@tencent-ai/cloud-agent-sdk 0.2.13 → 0.2.14-next.0a3c83c.20260321

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
@@ -1,9 +1,9 @@
1
+ import { t as E2BFilesystem } from "./e2b-filesystem-BNmEfpoW.mjs";
1
2
  import { z } from "zod";
2
3
  import { ClientSideConnection, PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
3
4
  import "@bufbuild/protobuf";
4
5
  import "@connectrpc/connect";
5
6
  import "@connectrpc/connect-web";
6
- import { Sandbox } from "e2b";
7
7
 
8
8
  //#region rolldown:runtime
9
9
  var __defProp = Object.defineProperty;
@@ -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);
@@ -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
@@ -5496,6 +5606,14 @@ var HttpService = class HttpService {
5496
5606
  return (await this.axiosInstance.put(url, data, config)).data;
5497
5607
  }
5498
5608
  /**
5609
+ * 通用请求方法
5610
+ * @param config axios 请求配置
5611
+ * @returns 原始 AxiosResponse
5612
+ */
5613
+ async request(config) {
5614
+ return this.axiosInstance.request(config);
5615
+ }
5616
+ /**
5499
5617
  * 获取原始 axios 实例(用于高级场景)
5500
5618
  */
5501
5619
  getAxiosInstance() {
@@ -5689,6 +5807,109 @@ const accountService = new AccountService();
5689
5807
  */
5690
5808
  if (typeof window !== "undefined") window.__genieAccountService = accountService;
5691
5809
 
5810
+ //#endregion
5811
+ //#region ../agent-provider/src/common/utils/lru-cache.ts
5812
+ /**
5813
+ * LRU (Least Recently Used) Cache
5814
+ * 当缓存达到容量上限时,自动淘汰最久未使用的数据
5815
+ *
5816
+ * @template K - Key 类型
5817
+ * @template V - Value 类型
5818
+ */
5819
+ var LRUCache = class {
5820
+ /**
5821
+ * 创建 LRU 缓存实例
5822
+ * @param capacity - 缓存容量上限
5823
+ */
5824
+ constructor(capacity) {
5825
+ if (capacity <= 0) throw new Error("Cache capacity must be greater than 0");
5826
+ this.capacity = capacity;
5827
+ this.cache = /* @__PURE__ */ new Map();
5828
+ }
5829
+ /**
5830
+ * 获取缓存值
5831
+ * @param key - 键
5832
+ * @returns 值,如果不存在返回 undefined
5833
+ */
5834
+ get(key) {
5835
+ if (!this.cache.has(key)) return;
5836
+ const value = this.cache.get(key);
5837
+ this.cache.delete(key);
5838
+ this.cache.set(key, value);
5839
+ return value;
5840
+ }
5841
+ /**
5842
+ * 设置缓存值
5843
+ * @param key - 键
5844
+ * @param value - 值
5845
+ */
5846
+ set(key, value) {
5847
+ if (this.cache.has(key)) this.cache.delete(key);
5848
+ else if (this.cache.size >= this.capacity) {
5849
+ const firstKey = this.cache.keys().next().value;
5850
+ this.cache.delete(firstKey);
5851
+ }
5852
+ this.cache.set(key, value);
5853
+ }
5854
+ /**
5855
+ * 检查 key 是否存在
5856
+ * @param key - 键
5857
+ * @returns 是否存在
5858
+ */
5859
+ has(key) {
5860
+ return this.cache.has(key);
5861
+ }
5862
+ /**
5863
+ * 删除指定 key
5864
+ * @param key - 键
5865
+ * @returns 是否删除成功
5866
+ */
5867
+ delete(key) {
5868
+ return this.cache.delete(key);
5869
+ }
5870
+ /**
5871
+ * 清空缓存
5872
+ */
5873
+ clear() {
5874
+ this.cache.clear();
5875
+ }
5876
+ /**
5877
+ * 获取当前缓存大小
5878
+ * @returns 当前缓存的元素数量
5879
+ */
5880
+ get size() {
5881
+ return this.cache.size;
5882
+ }
5883
+ /**
5884
+ * 获取缓存容量
5885
+ * @returns 缓存容量上限
5886
+ */
5887
+ get maxSize() {
5888
+ return this.capacity;
5889
+ }
5890
+ /**
5891
+ * 获取所有 key
5892
+ * @returns key 数组
5893
+ */
5894
+ keys() {
5895
+ return Array.from(this.cache.keys());
5896
+ }
5897
+ /**
5898
+ * 获取所有 value
5899
+ * @returns value 数组
5900
+ */
5901
+ values() {
5902
+ return Array.from(this.cache.values());
5903
+ }
5904
+ /**
5905
+ * 遍历缓存
5906
+ * @param callback - 回调函数
5907
+ */
5908
+ forEach(callback) {
5909
+ this.cache.forEach((value, key) => callback(value, key));
5910
+ }
5911
+ };
5912
+
5692
5913
  //#endregion
5693
5914
  //#region ../agent-provider/src/common/utils/concurrency.ts
5694
5915
  /**
@@ -5790,20 +6011,32 @@ var CosUploadService = class {
5790
6011
  * 上传单个文件到 COS
5791
6012
  *
5792
6013
  * @param file - 要上传的文件
6014
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
5793
6015
  * @returns 上传结果,包含访问 URL 或错误信息
5794
6016
  */
5795
- async uploadFile(file) {
6017
+ async uploadFile(file, abortSignal) {
5796
6018
  const filename = file.name;
5797
6019
  this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
5798
6020
  try {
6021
+ if (abortSignal?.aborted) return {
6022
+ success: false,
6023
+ error: "Upload cancelled",
6024
+ aborted: true
6025
+ };
5799
6026
  const objectKey = this.generateObjectKey(filename);
5800
6027
  this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
5801
6028
  const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
5802
6029
  if (!presignedItem) throw new Error("No presigned URL item returned");
6030
+ if (abortSignal?.aborted) return {
6031
+ success: false,
6032
+ error: "Upload cancelled",
6033
+ aborted: true
6034
+ };
5803
6035
  const uploadResponse = await fetch(presignedItem.upload_url, {
5804
6036
  method: "PUT",
5805
6037
  body: file,
5806
- headers: { "Content-Type": file.type || "application/octet-stream" }
6038
+ headers: { "Content-Type": file.type || "application/octet-stream" },
6039
+ signal: abortSignal
5807
6040
  });
5808
6041
  if (!uploadResponse.ok) {
5809
6042
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -5817,6 +6050,11 @@ var CosUploadService = class {
5817
6050
  objectKey
5818
6051
  };
5819
6052
  } catch (error) {
6053
+ if (error instanceof Error && error.name === "AbortError") return {
6054
+ success: false,
6055
+ error: "Upload cancelled",
6056
+ aborted: true
6057
+ };
5820
6058
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
5821
6059
  this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
5822
6060
  return {
@@ -5831,14 +6069,25 @@ var CosUploadService = class {
5831
6069
  * 使用并发控制,限制同时上传的文件数量
5832
6070
  *
5833
6071
  * @param files - 要上传的文件数组
6072
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
5834
6073
  * @returns 所有文件的上传结果
5835
6074
  */
5836
- async uploadFiles(files) {
6075
+ async uploadFiles(files, abortSignal) {
5837
6076
  if (files.length === 0) return {
5838
6077
  success: true,
5839
6078
  urls: [],
5840
6079
  results: []
5841
6080
  };
6081
+ if (abortSignal?.aborted) return {
6082
+ success: false,
6083
+ error: "Upload cancelled",
6084
+ aborted: true,
6085
+ results: files.map(() => ({
6086
+ success: false,
6087
+ error: "Upload cancelled",
6088
+ aborted: true
6089
+ }))
6090
+ };
5842
6091
  this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
5843
6092
  try {
5844
6093
  const fileInfos = files.map((file) => ({
@@ -5850,6 +6099,12 @@ var CosUploadService = class {
5850
6099
  this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
5851
6100
  if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
5852
6101
  const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
6102
+ if (abortSignal?.aborted) return {
6103
+ success: false,
6104
+ error: "Upload cancelled",
6105
+ aborted: true,
6106
+ objectKey: fileInfos[index].objectKey
6107
+ };
5853
6108
  const presignedItem = presignedResponse.items[index];
5854
6109
  if (!presignedItem) return {
5855
6110
  success: false,
@@ -5860,7 +6115,8 @@ var CosUploadService = class {
5860
6115
  const uploadResponse = await fetch(presignedItem.upload_url, {
5861
6116
  method: "PUT",
5862
6117
  body: file,
5863
- headers: { "Content-Type": file.type || "application/octet-stream" }
6118
+ headers: { "Content-Type": file.type || "application/octet-stream" },
6119
+ signal: abortSignal
5864
6120
  });
5865
6121
  if (!uploadResponse.ok) {
5866
6122
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -5877,6 +6133,12 @@ var CosUploadService = class {
5877
6133
  objectKey: presignedItem.object_key
5878
6134
  };
5879
6135
  } catch (error) {
6136
+ if (error instanceof Error && error.name === "AbortError") return {
6137
+ success: false,
6138
+ error: "Upload cancelled",
6139
+ aborted: true,
6140
+ objectKey: presignedItem.object_key
6141
+ };
5880
6142
  return {
5881
6143
  success: false,
5882
6144
  error: error instanceof Error ? error.message : "Unknown error",
@@ -5924,91 +6186,6 @@ var CosUploadService = class {
5924
6186
  }
5925
6187
  };
5926
6188
 
5927
- //#endregion
5928
- //#region ../agent-provider/src/common/providers/cloud-agent-provider/e2b-filesystem.ts
5929
- /**
5930
- * E2B Filesystem Implementation
5931
- *
5932
- * Provides FilesResource implementation using E2B Sandbox SDK.
5933
- * Directly uses e2b SDK types.
5934
- *
5935
- * @see https://e2b.dev/docs/filesystem/read-write
5936
- * @see https://e2b.dev/docs/filesystem/watch
5937
- */
5938
- /**
5939
- * E2B Filesystem Implementation
5940
- *
5941
- * Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource interface.
5942
- *
5943
- * @example
5944
- * ```typescript
5945
- * const fs = await E2BFilesystem.connect({
5946
- * sandboxId: 'sandbox-123',
5947
- * apiKey: 'e2b_xxx'
5948
- * });
5949
- *
5950
- * // Read/write files
5951
- * await fs.write('/test.txt', 'Hello World');
5952
- * const content = await fs.read('/test.txt');
5953
- *
5954
- * // Watch for changes
5955
- * const handle = await fs.watchDir('/workspace', (event) => {
5956
- * console.log('File changed:', event);
5957
- * });
5958
- * ```
5959
- */
5960
- var E2BFilesystem = class E2BFilesystem {
5961
- constructor(sandbox) {
5962
- this.sandbox = sandbox;
5963
- }
5964
- /**
5965
- * Connect to an E2B Sandbox and create filesystem instance
5966
- */
5967
- static async connect(info) {
5968
- return new E2BFilesystem(await Sandbox.connect(info.sandboxId, {
5969
- domain: info.domain,
5970
- apiUrl: info.apiUrl,
5971
- requestTimeoutMs: info.requestTimeoutMs,
5972
- debug: info.debug,
5973
- headers: info.headers
5974
- }));
5975
- }
5976
- /**
5977
- * Get the underlying E2B Sandbox instance
5978
- */
5979
- getSandbox() {
5980
- return this.sandbox;
5981
- }
5982
- read(path, opts) {
5983
- return this.sandbox.files.read(path, opts);
5984
- }
5985
- write(pathOrFiles, dataOrOpts, opts) {
5986
- if (Array.isArray(pathOrFiles)) return this.sandbox.files.write(pathOrFiles, dataOrOpts);
5987
- return this.sandbox.files.write(pathOrFiles, dataOrOpts, opts);
5988
- }
5989
- async list(path, opts) {
5990
- return this.sandbox.files.list(path, opts);
5991
- }
5992
- async exists(path, opts) {
5993
- return this.sandbox.files.exists(path, opts);
5994
- }
5995
- async makeDir(path, opts) {
5996
- return this.sandbox.files.makeDir(path, opts);
5997
- }
5998
- async remove(path, opts) {
5999
- return this.sandbox.files.remove(path, opts);
6000
- }
6001
- async rename(oldPath, newPath, opts) {
6002
- return this.sandbox.files.rename(oldPath, newPath, opts);
6003
- }
6004
- async getInfo(path, opts) {
6005
- return this.sandbox.files.getInfo(path, opts);
6006
- }
6007
- async watchDir(path, onEvent, opts) {
6008
- return this.sandbox.files.watchDir(path, onEvent, opts);
6009
- }
6010
- };
6011
-
6012
6189
  //#endregion
6013
6190
  //#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-provider.ts
6014
6191
  /**
@@ -6157,8 +6334,11 @@ var CloudAgentProvider = class CloudAgentProvider {
6157
6334
  this.filesystemCache = /* @__PURE__ */ new Map();
6158
6335
  this.connectionCache = /* @__PURE__ */ new Map();
6159
6336
  this.eventListeners = /* @__PURE__ */ new Map();
6337
+ this.productConfigCache = null;
6160
6338
  this.options = options;
6161
6339
  this.logger = options.logger;
6340
+ this.marketplaceCache = new LRUCache(200);
6341
+ this.pluginCache = new LRUCache(2e3);
6162
6342
  if (options.endpoint) httpService.setBaseURL(options.endpoint);
6163
6343
  if (options.authToken) httpService.setAuthToken(options.authToken);
6164
6344
  if (options.headers && Object.keys(options.headers).length > 0) this.requestInterceptorId = httpService.registerRequestInterceptor((config) => {
@@ -6209,7 +6389,19 @@ var CloudAgentProvider = class CloudAgentProvider {
6209
6389
  const cached = this.filesystemCache.get(agentId);
6210
6390
  if (cached) return cached;
6211
6391
  const info = await this.getSandboxInfo(agentId);
6212
- const filesystem = createAgentFilesystem(await E2BFilesystem.connect(info));
6392
+ let e2bFilesystem;
6393
+ if (this.options.filesystemFactory) e2bFilesystem = await this.options.filesystemFactory(info);
6394
+ else {
6395
+ const { E2BFilesystem } = await import("./e2b-filesystem-DWj9UkV8.mjs");
6396
+ e2bFilesystem = await E2BFilesystem.connect(info);
6397
+ }
6398
+ if (e2bFilesystem.setReconnectFn) e2bFilesystem.setReconnectFn(async () => {
6399
+ this.logger?.debug(`Reconnecting E2B sandbox for agent: ${agentId}`);
6400
+ const newInfo = await this.getSandboxInfo(agentId);
6401
+ this.logger?.debug(`E2B sandbox reconnected for agent: ${agentId}`);
6402
+ return newInfo;
6403
+ });
6404
+ const filesystem = createAgentFilesystem(e2bFilesystem);
6213
6405
  this.filesystemCache.set(agentId, filesystem);
6214
6406
  this.logger?.debug(`Created filesystem for agent: ${agentId}`);
6215
6407
  return filesystem;
@@ -6277,15 +6469,9 @@ var CloudAgentProvider = class CloudAgentProvider {
6277
6469
  const url = this.buildGetUrl("/console/as/conversations/", params);
6278
6470
  const apiResponse = await httpService.get(url);
6279
6471
  if (!apiResponse.data) throw new Error("No data in API response");
6280
- const agents = apiResponse.data.conversations.map((a) => this.toAgentState(a));
6281
- const pagination = apiResponse.data.pagination;
6282
- console.log("[CloudAgentProvider] API response:", {
6283
- agentsCount: agents.length,
6284
- pagination
6285
- });
6286
6472
  return {
6287
- agents,
6288
- pagination
6473
+ agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
6474
+ pagination: apiResponse.data.pagination
6289
6475
  };
6290
6476
  } catch (error) {
6291
6477
  this.logger?.error("Failed to list agents:", error);
@@ -6295,15 +6481,17 @@ var CloudAgentProvider = class CloudAgentProvider {
6295
6481
  /**
6296
6482
  * Create a new conversation
6297
6483
  * POST {endpoint}/console/as/conversations
6298
- * @param params - Optional session params containing _meta with tags
6484
+ * @param params - Session params containing cwd and optional configuration
6299
6485
  */
6300
6486
  async create(params) {
6301
6487
  try {
6302
- const tagsObj = (params?._meta?.["codebuddy.ai"])?.tags;
6488
+ const { options = {} } = params;
6489
+ const codebuddyMeta = options._meta?.["codebuddy.ai"];
6490
+ const tagsObj = options.tags || codebuddyMeta?.tags;
6303
6491
  const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
6304
6492
  const createPayload = {
6305
- prompt: "",
6306
- model: "deepseek-r1",
6493
+ prompt: (options.prompt || "").slice(0, 100),
6494
+ model: options.model || "deepseek-r1",
6307
6495
  ...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
6308
6496
  };
6309
6497
  console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
@@ -6357,7 +6545,14 @@ var CloudAgentProvider = class CloudAgentProvider {
6357
6545
  } catch (error) {
6358
6546
  this.logger?.debug(`Failed to fetch conversation details for ${agentId}:`, error);
6359
6547
  }
6360
- if (this.connectionCache.get(endpoint)) this.connectionCache.delete(endpoint);
6548
+ const existingConnection = this.connectionCache.get(endpoint);
6549
+ if (existingConnection) {
6550
+ this.connectionCache.delete(endpoint);
6551
+ existingConnection.removeAllListeners();
6552
+ existingConnection.disconnect().catch((err) => {
6553
+ this.logger?.debug("Failed to disconnect old connection:", err);
6554
+ });
6555
+ }
6361
6556
  const clientCapabilities = {
6362
6557
  ...this.options.clientCapabilities,
6363
6558
  _meta: {
@@ -6477,6 +6672,35 @@ var CloudAgentProvider = class CloudAgentProvider {
6477
6672
  }
6478
6673
  }
6479
6674
  /**
6675
+ * Update conversation status by ID
6676
+ * POST {endpoint}/console/as/conversations/{agentId}
6677
+ *
6678
+ * @param agentId - Conversation ID to update
6679
+ * @param status - New status for the conversation
6680
+ * @returns PatchConversationResponse containing the updated conversation ID
6681
+ *
6682
+ * @example
6683
+ * ```typescript
6684
+ * const result = await provider.updateStatus('agent-123', 'completed');
6685
+ * console.log('Updated conversation status:', result.id);
6686
+ * ```
6687
+ */
6688
+ async updateStatus(agentId, status) {
6689
+ try {
6690
+ const body = { status };
6691
+ const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
6692
+ if (!apiResponse.data) {
6693
+ this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
6694
+ return { id: agentId };
6695
+ }
6696
+ this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
6697
+ return apiResponse.data;
6698
+ } catch (error) {
6699
+ this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
6700
+ throw error;
6701
+ }
6702
+ }
6703
+ /**
6480
6704
  * Get available models from product configuration
6481
6705
  *
6482
6706
  * GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
@@ -6507,9 +6731,13 @@ var CloudAgentProvider = class CloudAgentProvider {
6507
6731
  this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
6508
6732
  return [];
6509
6733
  }
6510
- const models = apiResponse.data.models ?? [];
6511
- this.logger?.info(`[CloudAgentProvider] Retrieved ${models.length} models from /console/enterprises API`);
6512
- return models.map((model) => ({
6734
+ this.productConfigCache = apiResponse.data;
6735
+ const productConfig = apiResponse.data;
6736
+ const allModels = productConfig.models ?? [];
6737
+ const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
6738
+ const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
6739
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
6740
+ return filteredModels.map((model) => ({
6513
6741
  id: model.id,
6514
6742
  name: model.name ?? model.id,
6515
6743
  description: model.description,
@@ -6531,6 +6759,43 @@ var CloudAgentProvider = class CloudAgentProvider {
6531
6759
  }
6532
6760
  }
6533
6761
  /**
6762
+ * 获取产品部署类型(从缓存)
6763
+ * 需要先调用 getModels() 初始化缓存
6764
+ *
6765
+ * @returns 部署类型:'SaaS' | 'Cloud-Hosted' | 'Self-Hosted',默认为 'SaaS'
6766
+ */
6767
+ getDeploymentType() {
6768
+ return this.productConfigCache?.deploymentType ?? "SaaS";
6769
+ }
6770
+ /**
6771
+ * 获取 Credit 购买引导配置(从缓存)
6772
+ * 需要先调用 getModels() 初始化缓存
6773
+ *
6774
+ * @returns Credit 购买引导配置,key 为错误码。如果后端未返回,则使用默认配置
6775
+ */
6776
+ getCreditPurchaseActions() {
6777
+ return this.productConfigCache?.config?.creditPurchaseActions ?? {
6778
+ "14018": {
6779
+ labelZh: "获取 Credits",
6780
+ labelEn: "Get credits",
6781
+ url: "https://www.codebuddy.cn/profile/plan",
6782
+ showButton: true
6783
+ },
6784
+ "6004": {
6785
+ labelZh: "升级专业版",
6786
+ labelEn: "Upgrade to Pro",
6787
+ url: "https://www.codebuddy.cn/profile/plan",
6788
+ showButton: true
6789
+ },
6790
+ "6005": {
6791
+ labelZh: "升级专业版",
6792
+ labelEn: "Upgrade to Pro",
6793
+ url: "https://www.codebuddy.cn/profile/plan",
6794
+ showButton: true
6795
+ }
6796
+ };
6797
+ }
6798
+ /**
6534
6799
  * Generate a unique request ID
6535
6800
  */
6536
6801
  generateRequestId() {
@@ -6613,7 +6878,7 @@ var CloudAgentProvider = class CloudAgentProvider {
6613
6878
  /**
6614
6879
  * Upload files to cloud storage via COS presigned URL
6615
6880
  *
6616
- * @param params - files array (File objects in browser)
6881
+ * @param params - files array (File objects in browser), optional abortSignal
6617
6882
  * @returns Response with corresponding cloud URLs
6618
6883
  */
6619
6884
  async uploadFile(params) {
@@ -6623,12 +6888,13 @@ var CloudAgentProvider = class CloudAgentProvider {
6623
6888
  success: false,
6624
6889
  error: "No valid File objects provided"
6625
6890
  };
6626
- const result = await this.cosUploadService.uploadFiles(files);
6891
+ const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
6627
6892
  return {
6628
6893
  success: result.success,
6629
6894
  urls: result.urls,
6630
6895
  expireSeconds: result.expireSeconds,
6631
- error: result.error
6896
+ error: result.error,
6897
+ aborted: result.aborted
6632
6898
  };
6633
6899
  }
6634
6900
  /**
@@ -6670,20 +6936,22 @@ var CloudAgentProvider = class CloudAgentProvider {
6670
6936
  }
6671
6937
  /**
6672
6938
  * 获取支持的场景列表
6673
- * API 端点: GET /console/as/support/scenes
6939
+ * API 端点: GET /v2/as/support/scenes (不鉴权)
6674
6940
  * 用于 Welcome 页面的 QuickActions 快捷操作
6675
6941
  *
6942
+ * @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
6676
6943
  * @returns Promise<SupportScene[]> 支持的场景列表
6677
6944
  */
6678
- async getSupportScenes() {
6945
+ async getSupportScenes(locale) {
6679
6946
  try {
6680
- const apiResponse = await httpService.get("/console/as/support/scenes");
6947
+ const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
6948
+ const apiResponse = await httpService.get(url);
6681
6949
  if (!apiResponse.data) {
6682
6950
  this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
6683
6951
  return [];
6684
6952
  }
6685
6953
  const scenes = apiResponse.data.scenes || [];
6686
- this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
6954
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
6687
6955
  return scenes;
6688
6956
  } catch (error) {
6689
6957
  this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
@@ -6699,7 +6967,9 @@ var CloudAgentProvider = class CloudAgentProvider {
6699
6967
  type: "cloud",
6700
6968
  status,
6701
6969
  createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
6702
- capabilities: this.options.clientCapabilities
6970
+ updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0,
6971
+ capabilities: this.options.clientCapabilities,
6972
+ isUserDefinedTitle: data.isUserDefinedTitle
6703
6973
  };
6704
6974
  }
6705
6975
  /**
@@ -6715,69 +6985,524 @@ var CloudAgentProvider = class CloudAgentProvider {
6715
6985
  const queryString = searchParams.toString();
6716
6986
  return queryString ? `${path}?${queryString}` : path;
6717
6987
  }
6718
- };
6719
-
6720
- //#endregion
6721
- //#region ../agent-provider/src/common/client/session.ts
6722
- /**
6723
- * ActiveSessionImpl - Implements the ActiveSession interface
6724
- *
6725
- * This class wraps an AgentConnection and provides the session-centric API.
6726
- * It is created by SessionManager when creating or loading sessions.
6727
- *
6728
- * @example
6729
- * ```typescript
6730
- * // Created by client.sessions.new() or client.sessions.load()
6731
- * const session = await client.sessions.new({ cwd: '/workspace' });
6732
- *
6733
- * // Access agent state
6734
- * console.log(session.agentState.status);
6735
- *
6736
- * // Send prompt
6737
- * const response = await session.prompts.send({ content: 'Hello!' });
6738
- *
6739
- * // Cleanup
6740
- * session.disconnect();
6741
- * ```
6742
- */
6743
- var ActiveSessionImpl = class {
6744
6988
  /**
6745
- * Create an ActiveSessionImpl instance
6746
- *
6747
- * @param sessionId - Session ID
6748
- * @param agentId - Agent ID
6749
- * @param connection - Already connected AgentConnection
6750
- * @param options - Additional options
6989
+ * 获取已安装插件列表
6990
+ * GET /console/as/user/plugins/installed
6751
6991
  */
6752
- constructor(sessionId, agentId, connection, options = {}) {
6753
- this._availableCommands = [];
6754
- this.listeners = /* @__PURE__ */ new Map();
6755
- this.onceListeners = /* @__PURE__ */ new Map();
6756
- this._id = sessionId;
6757
- this._agentId = agentId;
6758
- this.connection = connection;
6759
- this.logger = options.logger;
6760
- this._getFilesystem = options.getFilesystem;
6761
- this._connectionInfo = options.connectionInfo;
6762
- this.setupConnectionEvents(connection);
6763
- this.agent = this.createAgentOperations();
6764
- this.prompts = this.createPromptsResource();
6765
- this.artifacts = this.createArtifactsResource();
6766
- this.files = this.createFilesResource();
6992
+ async getInstalledPlugins(forceRefresh) {
6993
+ try {
6994
+ const result = ((await httpService.get("/console/as/user/plugins/installed")).data?.plugins || []).map((p) => ({
6995
+ name: p.plugin_name,
6996
+ marketplaceName: p.marketplace_name,
6997
+ status: p.enabled ? "enabled" : "disabled",
6998
+ description: p.description,
6999
+ version: p.version,
7000
+ installScope: p.scope === "local" ? "project" : p.scope,
7001
+ installedScopes: [p.scope],
7002
+ installId: p.id
7003
+ }));
7004
+ result.forEach((plugin) => {
7005
+ this.pluginCache.set(plugin.name, plugin);
7006
+ });
7007
+ return result;
7008
+ } catch (error) {
7009
+ this.logger?.error("[CloudAgentProvider] getInstalledPlugins failed:", error);
7010
+ throw error;
7011
+ }
6767
7012
  }
6768
7013
  /**
6769
- * Session ID
7014
+ * 安装插件
7015
+ * POST /console/as/user/plugins/install
7016
+ *
7017
+ * @param pluginNames - 插件名称数组
7018
+ * @param marketplaceNameOrId - 市场名称或 ID
6770
7019
  */
6771
- get id() {
6772
- return this._id;
6773
- }
6774
- /**
6775
- * Agent ID
7020
+ async installPlugins(pluginNames, marketplaceNameOrId, installScope, marketplaceSource, workspacePath) {
7021
+ try {
7022
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7023
+ if (!marketplaceId) return {
7024
+ success: false,
7025
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7026
+ };
7027
+ const failed = (await Promise.allSettled(pluginNames.map((pluginName) => httpService.post("/console/as/user/plugins/install", {
7028
+ plugin_name: pluginName,
7029
+ marketplace_id: marketplaceId,
7030
+ version: "latest"
7031
+ })))).filter((r) => r.status === "rejected");
7032
+ if (failed.length > 0) {
7033
+ const errors = failed.map((r) => r.reason?.message || "Unknown error");
7034
+ return {
7035
+ success: false,
7036
+ error: `安装失败 ${failed.length} 个插件: ${errors.join(", ")}`
7037
+ };
7038
+ }
7039
+ return { success: true };
7040
+ } catch (error) {
7041
+ return {
7042
+ success: false,
7043
+ error: this.extractErrorMessage(error)
7044
+ };
7045
+ }
7046
+ }
7047
+ /**
7048
+ * 卸载插件
7049
+ * POST /console/as/user/plugins/installed/:id/uninstall
7050
+ *
7051
+ * 完整链路:
7052
+ * CloudAgentProvider.uninstallPlugin()
7053
+ * -> HTTP POST /console/as/user/plugins/installed/:id/uninstall
7054
+ * -> agentserver: PluginInstallService.Uninstall()
7055
+ * -> 软删除 DB + 异步同步到活跃沙箱
7056
+ *
7057
+ * @param pluginName - 插件名称
7058
+ * @param marketplaceName - 市场名称(用于标识唯一插件)
7059
+ * @param scope - 卸载范围 ('user' | 'project' | 'project-local')
7060
+ */
7061
+ async uninstallPlugin(pluginName, marketplaceName, scope) {
7062
+ try {
7063
+ const installId = await this.findPluginInstallId(pluginName);
7064
+ if (!installId) return {
7065
+ success: false,
7066
+ error: `Plugin not found or not installed: ${pluginName}`
7067
+ };
7068
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/uninstall`);
7069
+ return { success: true };
7070
+ } catch (error) {
7071
+ return {
7072
+ success: false,
7073
+ error: this.extractErrorMessage(error)
7074
+ };
7075
+ }
7076
+ }
7077
+ /**
7078
+ * 获取插件市场列表
7079
+ * GET /console/as/marketplace/sources
7080
+ */
7081
+ async getPluginMarketplaces(forceRefresh) {
7082
+ try {
7083
+ const result = ((await httpService.get("/console/as/marketplace/sources")).data?.sources || []).map((src) => ({
7084
+ id: src.id,
7085
+ name: src.name,
7086
+ type: this.mapSourceType(src.source_type),
7087
+ source: { url: src.url },
7088
+ description: src.name,
7089
+ isBuiltin: src.is_default
7090
+ }));
7091
+ result.forEach((m) => {
7092
+ const marketplaceInfo = {
7093
+ id: m.id,
7094
+ name: m.name
7095
+ };
7096
+ this.marketplaceCache.set(m.name, marketplaceInfo);
7097
+ this.marketplaceCache.set(m.id, marketplaceInfo);
7098
+ });
7099
+ return result;
7100
+ } catch (error) {
7101
+ this.logger?.error("[CloudAgentProvider] getPluginMarketplaces failed:", error);
7102
+ throw error;
7103
+ }
7104
+ }
7105
+ /**
7106
+ * 获取市场下的插件列表
7107
+ * - 有 searchText: GET /console/as/marketplace/plugins/search (跨所有市场搜索)
7108
+ * - 无 searchText: GET /console/as/marketplace/plugins (指定市场)
7109
+ *
7110
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
7111
+ * @param forceRefresh - 是否强制刷新
7112
+ * @param searchText - 搜索关键字(如果提供,则跨所有市场搜索)
7113
+ */
7114
+ async getMarketplacePlugins(marketplaceNameOrId, forceRefresh, searchText) {
7115
+ try {
7116
+ if (searchText) return ((await httpService.get("/console/as/marketplace/plugins/search", { params: {
7117
+ q: searchText,
7118
+ page: 1,
7119
+ page_size: 100
7120
+ } })).data?.plugins || []).map((p) => this.mapPluginData(p));
7121
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
7122
+ if (!sourceId) {
7123
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
7124
+ return [];
7125
+ }
7126
+ const params = {
7127
+ source_id: sourceId,
7128
+ page: 1,
7129
+ page_size: 100
7130
+ };
7131
+ return ((await httpService.get("/console/as/marketplace/plugins", { params })).data?.plugins || []).map((p) => this.mapPluginData(p));
7132
+ } catch (error) {
7133
+ this.logger?.error("[CloudAgentProvider] getMarketplacePlugins failed:", error);
7134
+ throw error;
7135
+ }
7136
+ }
7137
+ /**
7138
+ * 获取插件详情
7139
+ * GET /console/as/marketplace/plugins/:name/detail
7140
+ *
7141
+ * @param pluginName - 插件名称
7142
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
7143
+ */
7144
+ async getPluginDetail(pluginName, marketplaceNameOrId) {
7145
+ try {
7146
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
7147
+ if (!sourceId) {
7148
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
7149
+ return null;
7150
+ }
7151
+ const p = (await httpService.get(`/console/as/marketplace/plugins/${pluginName}/detail`, { params: { source_id: sourceId } })).data?.plugin;
7152
+ if (!p) return null;
7153
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
7154
+ const tags = p.tags ? JSON.parse(p.tags) : [];
7155
+ return {
7156
+ name: p.name,
7157
+ marketplaceName: p.marketplace_name,
7158
+ description: p.description,
7159
+ version: p.version,
7160
+ iconUrl: p.icon_url,
7161
+ tags,
7162
+ installed: p.installed,
7163
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
7164
+ readme: p.readme,
7165
+ author: p.author,
7166
+ homepage: p.homepage,
7167
+ repositoryUrl: p.repository_url,
7168
+ license: p.license,
7169
+ ...capabilities
7170
+ };
7171
+ } catch (error) {
7172
+ this.logger?.error("[CloudAgentProvider] getPluginDetail failed:", error);
7173
+ throw error;
7174
+ }
7175
+ }
7176
+ /**
7177
+ * 添加插件市场
7178
+ * POST /console/as/marketplace/sources
7179
+ */
7180
+ async addPluginMarketplace(sourceUrl, name) {
7181
+ try {
7182
+ const body = {
7183
+ source_type: sourceUrl.startsWith("http") ? "url" : "github",
7184
+ url: sourceUrl,
7185
+ name: name || sourceUrl
7186
+ };
7187
+ const sourceData = (await httpService.post("/console/as/marketplace/sources", body)).data?.source;
7188
+ if (!sourceData) throw new Error("Invalid response from server");
7189
+ const marketplaceInfo = {
7190
+ id: sourceData.id,
7191
+ name: sourceData.name
7192
+ };
7193
+ this.marketplaceCache.set(sourceData.name, marketplaceInfo);
7194
+ this.marketplaceCache.set(sourceData.id, marketplaceInfo);
7195
+ return {
7196
+ success: true,
7197
+ marketplace: {
7198
+ id: sourceData.id,
7199
+ name: sourceData.name,
7200
+ type: this.mapSourceType(sourceData.source_type),
7201
+ source: { url: sourceData.url },
7202
+ isBuiltin: sourceData.is_default
7203
+ }
7204
+ };
7205
+ } catch (error) {
7206
+ return {
7207
+ success: false,
7208
+ error: this.extractErrorMessage(error)
7209
+ };
7210
+ }
7211
+ }
7212
+ /**
7213
+ * 删除插件市场
7214
+ * POST /console/as/marketplace/sources/:id/delete
7215
+ */
7216
+ async removePluginMarketplace(marketplaceNameOrId) {
7217
+ try {
7218
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7219
+ if (!marketplaceId) return {
7220
+ success: false,
7221
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7222
+ };
7223
+ await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/delete`, {});
7224
+ return { success: true };
7225
+ } catch (error) {
7226
+ return {
7227
+ success: false,
7228
+ error: this.extractErrorMessage(error)
7229
+ };
7230
+ }
7231
+ }
7232
+ /**
7233
+ * 刷新插件市场
7234
+ * POST /console/as/marketplace/sources/:id/check-updates
7235
+ */
7236
+ async refreshPluginMarketplace(marketplaceNameOrId) {
7237
+ try {
7238
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7239
+ if (!marketplaceId) return {
7240
+ success: false,
7241
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7242
+ };
7243
+ return {
7244
+ success: true,
7245
+ plugins: (await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/check-updates`, {})).data?.updated_plugins || []
7246
+ };
7247
+ } catch (error) {
7248
+ return {
7249
+ success: false,
7250
+ error: this.extractErrorMessage(error)
7251
+ };
7252
+ }
7253
+ }
7254
+ /**
7255
+ * 批量切换插件启用/禁用状态
7256
+ * POST /console/as/user/plugins/installed/:id/toggle
7257
+ */
7258
+ async batchTogglePlugins(request) {
7259
+ try {
7260
+ const results = await Promise.allSettled(request.items.map(async (item) => {
7261
+ const installId = await this.findPluginInstallId(item.pluginName);
7262
+ if (!installId) throw new Error(`Plugin not found or not installed: ${item.pluginName}`);
7263
+ const enabled = item.operation === "enable";
7264
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/toggle`, { enabled });
7265
+ return item;
7266
+ }));
7267
+ const succeededPlugins = [];
7268
+ const failedPlugins = [];
7269
+ results.forEach((r, i) => {
7270
+ const item = request.items[i];
7271
+ if (r.status === "fulfilled") succeededPlugins.push(item);
7272
+ else failedPlugins.push({
7273
+ ...item,
7274
+ error: r.reason?.message || "Unknown error"
7275
+ });
7276
+ });
7277
+ return {
7278
+ success: failedPlugins.length === 0,
7279
+ succeededPlugins,
7280
+ failedPlugins
7281
+ };
7282
+ } catch (error) {
7283
+ return {
7284
+ success: false,
7285
+ succeededPlugins: [],
7286
+ failedPlugins: request.items.map((item) => ({
7287
+ ...item,
7288
+ error: this.extractErrorMessage(error)
7289
+ }))
7290
+ };
7291
+ }
7292
+ }
7293
+ /**
7294
+ * 将后端插件数据映射为前端格式
7295
+ */
7296
+ mapPluginData(p) {
7297
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
7298
+ let tags = [];
7299
+ if (p.tags) {
7300
+ if (Array.isArray(p.tags)) tags = p.tags;
7301
+ else if (typeof p.tags === "string") try {
7302
+ const parsed = JSON.parse(p.tags);
7303
+ tags = Array.isArray(parsed) ? parsed : [parsed];
7304
+ } catch {
7305
+ tags = p.tags.split(",").map((t) => t.trim()).filter((t) => t);
7306
+ }
7307
+ }
7308
+ return {
7309
+ name: p.name,
7310
+ marketplaceName: p.marketplace_name,
7311
+ description: p.description,
7312
+ version: p.version,
7313
+ iconUrl: p.icon_url,
7314
+ tags,
7315
+ installed: p.installed,
7316
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
7317
+ installedScopes: p.installed ? [p.installed_scope] : [],
7318
+ ...capabilities
7319
+ };
7320
+ }
7321
+ /**
7322
+ * 从缓存中查找插件的 install_id
7323
+ * 如果缓存未命中,则调用 API 获取并缓存
7324
+ *
7325
+ * @param pluginName - 插件名称
7326
+ * @returns install_id 或 null
7327
+ */
7328
+ async findPluginInstallId(pluginName) {
7329
+ const cached = this.pluginCache.get(pluginName);
7330
+ if (cached) return cached.installId;
7331
+ await this.getInstalledPlugins();
7332
+ return this.pluginCache.get(pluginName)?.installId || null;
7333
+ }
7334
+ /**
7335
+ * 从缓存中查找 marketplace ID
7336
+ * 如果缓存未命中,则调用 API 获取并缓存
7337
+ */
7338
+ async findMarketplaceId(nameOrId) {
7339
+ const cached = this.marketplaceCache.get(nameOrId);
7340
+ if (cached) return cached.id;
7341
+ await this.getPluginMarketplaces();
7342
+ return this.marketplaceCache.get(nameOrId)?.id || null;
7343
+ }
7344
+ /**
7345
+ * 提取 API 错误信息
7346
+ * 从 AxiosError 中提取详细的错误信息,包括 HTTP 状态码、错误码和错误消息
7347
+ */
7348
+ extractErrorMessage(error) {
7349
+ if (error instanceof AxiosError) {
7350
+ const status = error.response?.status;
7351
+ const apiResponse = error.response?.data;
7352
+ const parts = [];
7353
+ if (status) parts.push(`HTTP ${status}`);
7354
+ if (apiResponse?.code) parts.push(`Code ${apiResponse.code}`);
7355
+ if (apiResponse?.msg) parts.push(apiResponse.msg);
7356
+ else if (error.message) parts.push(error.message);
7357
+ const errorMessage = parts.join(" - ");
7358
+ this.logger?.error("[CloudAgentProvider] API Error:", {
7359
+ status,
7360
+ code: apiResponse?.code,
7361
+ msg: apiResponse?.msg,
7362
+ requestId: apiResponse?.requestId,
7363
+ url: error.config?.url,
7364
+ method: error.config?.method
7365
+ });
7366
+ return errorMessage;
7367
+ }
7368
+ if (error instanceof Error) return error.message;
7369
+ return "Unknown error";
7370
+ }
7371
+ /**
7372
+ * 映射后端 source_type 到前端类型
7373
+ */
7374
+ mapSourceType(sourceType) {
7375
+ switch (sourceType) {
7376
+ case "github": return "github";
7377
+ case "official": return "custom";
7378
+ default: return "custom";
7379
+ }
7380
+ }
7381
+ /**
7382
+ * 解析 capabilities JSON 字符串
7383
+ */
7384
+ parseCapabilities(capabilitiesStr) {
7385
+ try {
7386
+ const cap = JSON.parse(capabilitiesStr);
7387
+ return {
7388
+ commands: cap.commands,
7389
+ skills: cap.skills,
7390
+ mcpServers: cap.mcp,
7391
+ agents: cap.agents,
7392
+ hooks: cap.hooks,
7393
+ rules: cap.rules
7394
+ };
7395
+ } catch {
7396
+ return {};
7397
+ }
7398
+ }
7399
+ /**
7400
+ * 上报 telemetry 事件(Cloud 模式)
7401
+ * 通过 HTTP POST 发送到 /v2/report
7402
+ * 注入用户信息和浏览器环境等公共字段
7403
+ */
7404
+ async reportTelemetry(eventName, payload) {
7405
+ try {
7406
+ const account = accountService.getAccount();
7407
+ const commonFields = {};
7408
+ if (account) {
7409
+ commonFields.userId = account.uid;
7410
+ commonFields.userNickname = account.nickname;
7411
+ if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
7412
+ if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
7413
+ }
7414
+ if (typeof navigator !== "undefined") {
7415
+ commonFields.userAgent = navigator.userAgent;
7416
+ commonFields.os = navigator.platform;
7417
+ }
7418
+ const events = [{
7419
+ eventCode: eventName,
7420
+ timestamp: Date.now(),
7421
+ reportDelay: 0,
7422
+ ...commonFields,
7423
+ ...payload
7424
+ }];
7425
+ await httpService.post("/v2/report", events);
7426
+ } catch (error) {
7427
+ this.logger?.warn("reportTelemetry() failed:", error);
7428
+ }
7429
+ }
7430
+ };
7431
+
7432
+ //#endregion
7433
+ //#region ../agent-provider/src/common/client/session.ts
7434
+ /**
7435
+ * ActiveSessionImpl - Implements the ActiveSession interface
7436
+ *
7437
+ * This class wraps an AgentConnection and provides the session-centric API.
7438
+ * It is created by SessionManager when creating or loading sessions.
7439
+ *
7440
+ * @example
7441
+ * ```typescript
7442
+ * // Created by client.sessions.new() or client.sessions.load()
7443
+ * const session = await client.sessions.new({ cwd: '/workspace' });
7444
+ *
7445
+ * // Access agent state
7446
+ * console.log(session.agentState.status);
7447
+ *
7448
+ * // Send prompt
7449
+ * const response = await session.prompts.send({ content: 'Hello!' });
7450
+ *
7451
+ * // Cleanup
7452
+ * session.disconnect();
7453
+ * ```
7454
+ */
7455
+ var ActiveSessionImpl = class {
7456
+ /**
7457
+ * Create an ActiveSessionImpl instance
7458
+ *
7459
+ * @param sessionId - Session ID
7460
+ * @param agentId - Agent ID
7461
+ * @param connection - Already connected AgentConnection
7462
+ * @param options - Additional options
7463
+ */
7464
+ constructor(sessionId, agentId, connection, options = {}) {
7465
+ this._availableCommands = [];
7466
+ this.listeners = /* @__PURE__ */ new Map();
7467
+ this.onceListeners = /* @__PURE__ */ new Map();
7468
+ this.connectionListeners = [];
7469
+ this._id = sessionId;
7470
+ this._agentId = agentId;
7471
+ this.connection = connection;
7472
+ this.logger = options.logger;
7473
+ this._getFilesystem = options.getFilesystem;
7474
+ this._connectionInfo = options.connectionInfo;
7475
+ this.setupConnectionEvents(connection);
7476
+ this.agent = this.createAgentOperations();
7477
+ this.prompts = this.createPromptsResource();
7478
+ this.artifacts = this.createArtifactsResource();
7479
+ this.files = this.createFilesResource();
7480
+ }
7481
+ /**
7482
+ * Session ID
7483
+ */
7484
+ get id() {
7485
+ return this._id;
7486
+ }
7487
+ /**
7488
+ * Agent ID
6776
7489
  */
6777
7490
  get agentId() {
6778
7491
  return this._agentId;
6779
7492
  }
6780
7493
  /**
7494
+ * Actual workspace path (set from newSession response _meta)
7495
+ */
7496
+ get cwd() {
7497
+ return this._cwd;
7498
+ }
7499
+ /**
7500
+ * Set actual workspace path (called by SessionManager after createSession)
7501
+ */
7502
+ setCwd(cwd) {
7503
+ this._cwd = cwd;
7504
+ }
7505
+ /**
6781
7506
  * Agent state (live connection state)
6782
7507
  * Returns LocalAgentState or CloudAgentState based on transport type
6783
7508
  */
@@ -6971,10 +7696,10 @@ var ActiveSessionImpl = class {
6971
7696
  return this.connection.cancelQuestion(toolCallId, reason);
6972
7697
  }
6973
7698
  /**
6974
- * Callback for tool operations (skip or cancel)
7699
+ * Callback for tool operations (approve / skip / cancel)
6975
7700
  * @param toolCallId Tool call ID
6976
7701
  * @param toolName Tool name
6977
- * @param action Action to perform ('skip' or 'cancel')
7702
+ * @param action Action to perform ('approve' / 'skip' / 'cancel')
6978
7703
  */
6979
7704
  async toolCallback(toolCallId, toolName, action) {
6980
7705
  return await this.getConnectionOrThrow().toolCallback(this._id, toolCallId, toolName, action);
@@ -7018,6 +7743,7 @@ var ActiveSessionImpl = class {
7018
7743
  * ```
7019
7744
  */
7020
7745
  async setSessionModel(modelId) {
7746
+ this._currentModelId = modelId;
7021
7747
  await this.getConnectionOrThrow().setSessionModel(this._id, modelId);
7022
7748
  }
7023
7749
  /**
@@ -7095,11 +7821,23 @@ var ActiveSessionImpl = class {
7095
7821
  * Disconnect from the session/agent
7096
7822
  */
7097
7823
  disconnect() {
7824
+ this.removeConnectionListeners();
7098
7825
  this.connection.disconnect();
7099
7826
  this.removeAllListeners();
7100
7827
  this.logger?.info(`Session ${this._id}: Disconnected`);
7101
7828
  }
7102
7829
  /**
7830
+ * Detach the session from connection events without disconnecting the connection.
7831
+ * This should be called when the session is being replaced but the connection is shared.
7832
+ * Unlike disconnect(), this only removes event listeners without closing the connection.
7833
+ */
7834
+ detach() {
7835
+ this.logger?.info(`Session ${this._id}: Detaching from connection events`);
7836
+ this.removeConnectionListeners();
7837
+ this.removeAllListeners();
7838
+ this.logger?.info(`Session ${this._id}: Detached successfully`);
7839
+ }
7840
+ /**
7103
7841
  * Symbol.dispose for 'using' keyword support
7104
7842
  * Automatically disconnects and cleans up when session goes out of scope
7105
7843
  *
@@ -7118,60 +7856,85 @@ var ActiveSessionImpl = class {
7118
7856
  if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
7119
7857
  return this.connection;
7120
7858
  }
7859
+ /**
7860
+ * 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
7861
+ */
7862
+ addConnectionListener(connection, event, listener) {
7863
+ connection.on(event, listener);
7864
+ this.connectionListeners.push({
7865
+ event,
7866
+ listener
7867
+ });
7868
+ }
7869
+ /**
7870
+ * 从 connection 上移除所有本 session 注册的 listener
7871
+ */
7872
+ removeConnectionListeners() {
7873
+ for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
7874
+ this.connectionListeners = [];
7875
+ }
7121
7876
  setupConnectionEvents(connection) {
7122
- connection.on("connected", () => {
7877
+ this.addConnectionListener(connection, "connected", () => {
7123
7878
  this.emit("connected", void 0);
7124
7879
  });
7125
- connection.on("disconnected", () => {
7880
+ this.addConnectionListener(connection, "disconnected", () => {
7126
7881
  this.emit("disconnected", void 0);
7127
7882
  });
7128
- connection.on("error", (error) => {
7883
+ this.addConnectionListener(connection, "error", (error) => {
7129
7884
  this.emit("error", error);
7130
7885
  });
7131
- connection.on("sessionUpdate", (update) => {
7886
+ this.addConnectionListener(connection, "sessionUpdate", (update) => {
7887
+ const notificationSessionId = update?.sessionId;
7888
+ if (notificationSessionId && notificationSessionId !== this._id) {
7889
+ console.log(`[RT-DEBUG][AgentMgr:Session] sessionUpdate SKIPPED: notifSessionId mismatch, notif=${notificationSessionId?.substring(0, 8)}, my=${this._id?.substring(0, 8)}`);
7890
+ return;
7891
+ }
7132
7892
  this.emit("sessionUpdate", update);
7133
7893
  });
7134
- connection.on("artifactCreated", (artifact) => {
7135
- console.log("[Session] Forwarding artifactCreated:", {
7136
- artifactUri: artifact.uri,
7137
- artifactType: artifact.type
7138
- });
7894
+ this.addConnectionListener(connection, "artifactCreated", (artifact) => {
7895
+ if (!this.shouldForwardArtifact(artifact)) return;
7139
7896
  this.emit("artifactCreated", artifact);
7140
7897
  });
7141
- connection.on("artifactUpdated", (artifact) => {
7142
- console.log("[Session] Forwarding artifactUpdated:", {
7143
- artifactUri: artifact.uri,
7144
- artifactType: artifact.type
7145
- });
7898
+ this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
7899
+ if (!this.shouldForwardArtifact(artifact)) return;
7146
7900
  this.emit("artifactUpdated", artifact);
7147
7901
  });
7148
- connection.on("artifactDeleted", (artifact) => {
7149
- console.log("[Session] Forwarding artifactDeleted:", { artifactUri: artifact.uri });
7902
+ this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
7903
+ if (!this.shouldForwardArtifact(artifact)) return;
7150
7904
  this.emit("artifactDeleted", artifact);
7151
7905
  });
7152
- connection.on("permissionRequest", (request) => {
7906
+ this.addConnectionListener(connection, "permissionRequest", (request) => {
7153
7907
  this.emit("permissionRequest", request);
7154
7908
  });
7155
- connection.on("questionRequest", (request) => {
7909
+ this.addConnectionListener(connection, "questionRequest", (request) => {
7156
7910
  this.emit("questionRequest", request);
7157
7911
  });
7158
- connection.on("questionCancelled", () => {
7912
+ this.addConnectionListener(connection, "questionCancelled", () => {
7159
7913
  this.prompts.cancel();
7160
7914
  });
7161
- connection.on("usageUpdate", (usage) => {
7915
+ this.addConnectionListener(connection, "usageUpdate", (usage) => {
7162
7916
  this.emit("usageUpdate", usage);
7163
7917
  });
7164
- connection.on("checkpointCreated", (checkpoint) => {
7918
+ this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
7919
+ const originSessionId = checkpoint.__sessionId;
7920
+ if (originSessionId && originSessionId !== this._id) return;
7165
7921
  this.emit("checkpointCreated", checkpoint);
7166
7922
  });
7167
- connection.on("checkpointUpdated", (checkpoint) => {
7923
+ this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
7924
+ const originSessionId = checkpoint.__sessionId;
7925
+ if (originSessionId && originSessionId !== this._id) return;
7168
7926
  this.emit("checkpointUpdated", checkpoint);
7169
7927
  });
7170
- connection.on("command", (command) => {
7171
- console.log("[Session] Forwarding command:", {
7172
- action: command.action,
7173
- paramsKeys: command.params ? Object.keys(command.params) : []
7174
- });
7928
+ this.addConnectionListener(connection, "command", (command) => {
7929
+ const originSessionId = command.__sessionId;
7930
+ if (originSessionId && originSessionId !== this._id) {
7931
+ console.log("[Session] Command not forwarded:", {
7932
+ command,
7933
+ originSessionId,
7934
+ sessionId: this._id
7935
+ });
7936
+ return;
7937
+ }
7175
7938
  this.emit("command", command);
7176
7939
  });
7177
7940
  }
@@ -7181,6 +7944,15 @@ var ActiveSessionImpl = class {
7181
7944
  _meta: response._meta ?? void 0
7182
7945
  };
7183
7946
  }
7947
+ /**
7948
+ * 判断 artifact 是否应该转发给当前 session
7949
+ * 所有类型的 artifact 都按 __sessionId 严格隔离
7950
+ */
7951
+ shouldForwardArtifact(artifact) {
7952
+ const originSessionId = artifact.__sessionId;
7953
+ if (!originSessionId || originSessionId !== this._id) return false;
7954
+ return true;
7955
+ }
7184
7956
  };
7185
7957
 
7186
7958
  //#endregion
@@ -7233,9 +8005,11 @@ var SessionManager = class {
7233
8005
  name: agent.name,
7234
8006
  status: agent.status,
7235
8007
  createdAt: agent.createdAt,
8008
+ updatedAt: agent.updatedAt,
7236
8009
  lastActivityAt: agent.updatedAt,
7237
8010
  cwd: agent.type === "local" ? agent.cwd : void 0,
7238
- isPlayground: agent.isPlayground
8011
+ isPlayground: agent.isPlayground,
8012
+ isUserDefinedTitle: agent.isUserDefinedTitle
7239
8013
  }));
7240
8014
  console.log("[SessionManager] Returning sessions:", {
7241
8015
  count: sessions.length,
@@ -7262,13 +8036,26 @@ var SessionManager = class {
7262
8036
  if (this.provider.create) {
7263
8037
  agentId = await this.provider.create(params);
7264
8038
  this.logger?.debug(`Created new agent: ${agentId}`);
8039
+ if (params.options?.onSessionPrepared) {
8040
+ const initialPrompt = params.options?.prompt;
8041
+ const initialTitle = initialPrompt?.slice(0, 50) || "";
8042
+ params.options.onSessionPrepared({
8043
+ id: agentId,
8044
+ agentId,
8045
+ name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
8046
+ status: "connecting",
8047
+ cwd: params.cwd || "",
8048
+ createdAt: /* @__PURE__ */ new Date()
8049
+ });
8050
+ this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
8051
+ }
7265
8052
  } else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
7266
8053
  const connection = await this.provider.connect(agentId);
7267
8054
  this.logger?.debug(`Connected to agent: ${agentId}`);
7268
8055
  const response = await connection.createSession({
7269
- _meta: params._meta,
8056
+ _meta: params.options?._meta,
7270
8057
  cwd: params.cwd,
7271
- mcpServers: params.mcpServers
8058
+ mcpServers: params.options?.mcpServers
7272
8059
  });
7273
8060
  if (this.provider.registerSession) {
7274
8061
  this.provider.registerSession(response.sessionId, agentId);
@@ -7283,6 +8070,8 @@ var SessionManager = class {
7283
8070
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
7284
8071
  const availableModels = this.extractAvailableModels(response);
7285
8072
  if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
8073
+ const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
8074
+ if (responseCwd) session.setCwd(responseCwd);
7286
8075
  this.logger?.info(`Session created: ${response.sessionId}`);
7287
8076
  return session;
7288
8077
  }
@@ -7449,6 +8238,26 @@ var AgentClient = class {
7449
8238
  throw error;
7450
8239
  }
7451
8240
  },
8241
+ updateStatus: async (sessionId, status) => {
8242
+ this.logger?.debug("AgentClient.sessions.updateStatus called", {
8243
+ sessionId,
8244
+ status
8245
+ });
8246
+ try {
8247
+ if (this.provider.updateStatus) {
8248
+ const result = await this.provider.updateStatus(sessionId, status);
8249
+ this.logger?.info("Session status updated successfully", {
8250
+ sessionId,
8251
+ status
8252
+ });
8253
+ return result;
8254
+ }
8255
+ throw new Error("Provider does not support updateStatus method");
8256
+ } catch (error) {
8257
+ this.logger?.error("Failed to update session status", error);
8258
+ throw error;
8259
+ }
8260
+ },
7452
8261
  move: async (sessionId) => {
7453
8262
  this.logger?.debug("AgentClient.sessions.move called", { sessionId });
7454
8263
  try {
@@ -7481,6 +8290,16 @@ var AgentClient = class {
7481
8290
  };
7482
8291
  }
7483
8292
  },
8293
+ requestYieldAfterCurrentStep: async (sessionId) => {
8294
+ try {
8295
+ if (this.provider?.requestYieldAfterCurrentStep) return await this.provider.requestYieldAfterCurrentStep(sessionId);
8296
+ this.logger?.warn("Provider does not support requestYieldAfterCurrentStep");
8297
+ return false;
8298
+ } catch (error) {
8299
+ this.logger?.error("Failed to request yield after current step", error);
8300
+ return false;
8301
+ }
8302
+ },
7484
8303
  getCurrentWorkspaces: async (filter) => {
7485
8304
  this.logger?.debug("AgentClient.sessions.getCurrentWorkspaces called", filter);
7486
8305
  try {
@@ -7496,6 +8315,100 @@ var AgentClient = class {
7496
8315
  return [];
7497
8316
  }
7498
8317
  },
8318
+ getAutomationSnapshot: async () => {
8319
+ try {
8320
+ if (this.provider?.getAutomationSnapshot) return await this.provider.getAutomationSnapshot();
8321
+ this.logger?.warn("Provider does not support getAutomationSnapshot");
8322
+ } catch (error) {
8323
+ this.logger?.error("Failed to get automation snapshot", error);
8324
+ }
8325
+ return {
8326
+ automations: [],
8327
+ inbox: [],
8328
+ runtimeState: {},
8329
+ updatedAt: Date.now()
8330
+ };
8331
+ },
8332
+ updateAutomation: async (payload) => {
8333
+ try {
8334
+ if (this.provider?.updateAutomation) return await this.provider.updateAutomation(payload);
8335
+ this.logger?.warn("Provider does not support updateAutomation");
8336
+ } catch (error) {
8337
+ this.logger?.error("Failed to update automation", error);
8338
+ return {
8339
+ success: false,
8340
+ message: error instanceof Error ? error.message : "Unknown error"
8341
+ };
8342
+ }
8343
+ return {
8344
+ success: false,
8345
+ message: "Provider does not support updateAutomation"
8346
+ };
8347
+ },
8348
+ deleteAutomation: async (id) => {
8349
+ try {
8350
+ if (this.provider?.deleteAutomation) return await this.provider.deleteAutomation(id);
8351
+ this.logger?.warn("Provider does not support deleteAutomation");
8352
+ } catch (error) {
8353
+ this.logger?.error("Failed to delete automation", error);
8354
+ return {
8355
+ success: false,
8356
+ message: error instanceof Error ? error.message : "Unknown error"
8357
+ };
8358
+ }
8359
+ return {
8360
+ success: false,
8361
+ message: "Provider does not support deleteAutomation"
8362
+ };
8363
+ },
8364
+ archiveAutomationInboxItem: async (itemId) => {
8365
+ try {
8366
+ if (this.provider?.archiveAutomationInboxItem) return await this.provider.archiveAutomationInboxItem(itemId);
8367
+ this.logger?.warn("Provider does not support archiveAutomationInboxItem");
8368
+ } catch (error) {
8369
+ this.logger?.error("Failed to archive automation inbox item", error);
8370
+ return {
8371
+ success: false,
8372
+ message: error instanceof Error ? error.message : "Unknown error"
8373
+ };
8374
+ }
8375
+ return {
8376
+ success: false,
8377
+ message: "Provider does not support archiveAutomationInboxItem"
8378
+ };
8379
+ },
8380
+ deleteAutomationInboxItem: async (itemId) => {
8381
+ try {
8382
+ if (this.provider?.deleteAutomationInboxItem) return await this.provider.deleteAutomationInboxItem(itemId);
8383
+ this.logger?.warn("Provider does not support deleteAutomationInboxItem");
8384
+ } catch (error) {
8385
+ this.logger?.error("Failed to delete automation inbox item", error);
8386
+ return {
8387
+ success: false,
8388
+ message: error instanceof Error ? error.message : "Unknown error"
8389
+ };
8390
+ }
8391
+ return {
8392
+ success: false,
8393
+ message: "Provider does not support deleteAutomationInboxItem"
8394
+ };
8395
+ },
8396
+ testAutomation: async (id) => {
8397
+ try {
8398
+ if (this.provider?.testAutomation) return await this.provider.testAutomation(id);
8399
+ this.logger?.warn("Provider does not support testAutomation");
8400
+ } catch (error) {
8401
+ this.logger?.error("Failed to test automation", error);
8402
+ return {
8403
+ success: false,
8404
+ message: error instanceof Error ? error.message : "Unknown error"
8405
+ };
8406
+ }
8407
+ return {
8408
+ success: false,
8409
+ message: "Provider does not support testAutomation"
8410
+ };
8411
+ },
7499
8412
  on: (event, handler) => {
7500
8413
  if (this.provider.on) this.provider.on(event, handler);
7501
8414
  else this.logger?.warn(`Provider does not support event registration: ${String(event)}`);
@@ -7637,6 +8550,167 @@ var AgentClient = class {
7637
8550
  };
7638
8551
  }
7639
8552
  },
8553
+ getSkillList: async (params) => {
8554
+ try {
8555
+ if (this.provider && this.provider.getSkillList) {
8556
+ const result = await this.provider.getSkillList(params);
8557
+ this.logger?.info("Skill list retrieved", {
8558
+ resultCount: result.results.length,
8559
+ hasError: !!result.error
8560
+ });
8561
+ return result;
8562
+ }
8563
+ return {
8564
+ results: [],
8565
+ error: "Provider does not support getSkillList"
8566
+ };
8567
+ } catch (error) {
8568
+ this.logger?.error("Failed to get skill list", error);
8569
+ return {
8570
+ results: [],
8571
+ error: error instanceof Error ? error.message : "Unknown error"
8572
+ };
8573
+ }
8574
+ },
8575
+ importSkill: async (params) => {
8576
+ try {
8577
+ if (this.provider && this.provider.importSkill) {
8578
+ const result = await this.provider.importSkill(params);
8579
+ this.logger?.info("Import skill completed", {
8580
+ success: result.success,
8581
+ hasError: !!result.error
8582
+ });
8583
+ return result;
8584
+ }
8585
+ return {
8586
+ success: false,
8587
+ error: "Provider does not support importSkill"
8588
+ };
8589
+ } catch (error) {
8590
+ this.logger?.error("Failed to import skill", error);
8591
+ return {
8592
+ success: false,
8593
+ error: error instanceof Error ? error.message : "Unknown error"
8594
+ };
8595
+ }
8596
+ },
8597
+ toggleSkill: async (params) => {
8598
+ try {
8599
+ if (this.provider && this.provider.toggleSkill) {
8600
+ const result = await this.provider.toggleSkill(params);
8601
+ this.logger?.info("Toggle skill completed", {
8602
+ success: result.success,
8603
+ hasError: !!result.error
8604
+ });
8605
+ return result;
8606
+ }
8607
+ return {
8608
+ success: false,
8609
+ error: "Provider does not support toggleSkill"
8610
+ };
8611
+ } catch (error) {
8612
+ this.logger?.error("Failed to toggle skill", error);
8613
+ return {
8614
+ success: false,
8615
+ error: error instanceof Error ? error.message : "Unknown error"
8616
+ };
8617
+ }
8618
+ },
8619
+ deleteSkill: async (params) => {
8620
+ try {
8621
+ if (this.provider && this.provider.deleteSkill) {
8622
+ const result = await this.provider.deleteSkill(params);
8623
+ this.logger?.info("Delete skill completed", {
8624
+ success: result.success,
8625
+ hasError: !!result.error
8626
+ });
8627
+ return result;
8628
+ }
8629
+ return {
8630
+ success: false,
8631
+ error: "Provider does not support deleteSkill"
8632
+ };
8633
+ } catch (error) {
8634
+ this.logger?.error("Failed to delete skill", error);
8635
+ return {
8636
+ success: false,
8637
+ error: error instanceof Error ? error.message : "Unknown error"
8638
+ };
8639
+ }
8640
+ },
8641
+ getSkillContent: async (params) => {
8642
+ try {
8643
+ if (this.provider && this.provider.getSkillContent) {
8644
+ const result = await this.provider.getSkillContent(params);
8645
+ this.logger?.info("Get skill content completed", { hasError: !!result.error });
8646
+ return result;
8647
+ }
8648
+ return {
8649
+ content: "",
8650
+ error: "Provider does not support getSkillContent"
8651
+ };
8652
+ } catch (error) {
8653
+ this.logger?.error("Failed to get skill content", error);
8654
+ return {
8655
+ content: "",
8656
+ error: error instanceof Error ? error.message : "Unknown error"
8657
+ };
8658
+ }
8659
+ },
8660
+ getMarketplaceSkills: async () => {
8661
+ try {
8662
+ if (this.provider && this.provider.getMarketplaceSkills) {
8663
+ const result = await this.provider.getMarketplaceSkills();
8664
+ this.logger?.info("Marketplace skills retrieved", {
8665
+ resultCount: result.results.length,
8666
+ hasError: !!result.error
8667
+ });
8668
+ return result;
8669
+ }
8670
+ return {
8671
+ results: [],
8672
+ error: "Provider does not support getMarketplaceSkills"
8673
+ };
8674
+ } catch (error) {
8675
+ this.logger?.error("Failed to get marketplace skills", error);
8676
+ return {
8677
+ results: [],
8678
+ error: error instanceof Error ? error.message : "Unknown error"
8679
+ };
8680
+ }
8681
+ },
8682
+ getMarketplaceSkillContent: async (params) => {
8683
+ try {
8684
+ if (this.provider && this.provider.getMarketplaceSkillContent) return await this.provider.getMarketplaceSkillContent(params);
8685
+ return {
8686
+ content: "",
8687
+ error: "Provider does not support getMarketplaceSkillContent"
8688
+ };
8689
+ } catch (error) {
8690
+ this.logger?.error("Failed to get marketplace skill content", error);
8691
+ return {
8692
+ content: "",
8693
+ error: error instanceof Error ? error.message : "Unknown error"
8694
+ };
8695
+ }
8696
+ },
8697
+ installMarketplaceSkill: async (params) => {
8698
+ try {
8699
+ if (this.provider && this.provider.installMarketplaceSkill) return await this.provider.installMarketplaceSkill(params);
8700
+ return {
8701
+ success: false,
8702
+ skillName: params.skillName,
8703
+ errorMessage: "Provider does not support installMarketplaceSkill"
8704
+ };
8705
+ } catch (error) {
8706
+ this.logger?.error("Failed to install marketplace skill", error);
8707
+ return {
8708
+ success: false,
8709
+ skillName: params.skillName,
8710
+ errorMessage: error instanceof Error ? error.message : "Unknown error"
8711
+ };
8712
+ }
8713
+ },
7640
8714
  batchTogglePlugins: async (request) => {
7641
8715
  try {
7642
8716
  if (this.provider && this.provider.batchTogglePlugins) {
@@ -7681,37 +8755,229 @@ var AgentClient = class {
7681
8755
  return [];
7682
8756
  }
7683
8757
  },
7684
- installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource) => {
8758
+ installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
8759
+ try {
8760
+ if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
8761
+ const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
8762
+ this.logger?.info("Install plugins", {
8763
+ pluginNames,
8764
+ marketplaceName,
8765
+ success: result.success
8766
+ });
8767
+ return result;
8768
+ }
8769
+ this.logger?.warn("Provider does not support installPlugins");
8770
+ return {
8771
+ success: false,
8772
+ error: "Provider does not support installPlugins"
8773
+ };
8774
+ } catch (error) {
8775
+ this.logger?.error("Failed to install plugins", error);
8776
+ return {
8777
+ success: false,
8778
+ error: error instanceof Error ? error.message : "Unknown error"
8779
+ };
8780
+ }
8781
+ },
8782
+ uninstallPlugin: async (pluginName, marketplaceName, scope) => {
8783
+ try {
8784
+ if (this.provider && "uninstallPlugin" in this.provider && typeof this.provider.uninstallPlugin === "function") {
8785
+ const result = await this.provider.uninstallPlugin(pluginName, marketplaceName, scope);
8786
+ this.logger?.info("Uninstall plugin", {
8787
+ pluginName,
8788
+ marketplaceName,
8789
+ scope,
8790
+ success: result.success
8791
+ });
8792
+ return result;
8793
+ }
8794
+ this.logger?.warn("Provider does not support uninstallPlugin");
8795
+ return {
8796
+ success: false,
8797
+ error: "Provider does not support uninstallPlugin"
8798
+ };
8799
+ } catch (error) {
8800
+ this.logger?.error("Failed to uninstall plugin", error);
8801
+ return {
8802
+ success: false,
8803
+ error: error instanceof Error ? error.message : "Unknown error"
8804
+ };
8805
+ }
8806
+ },
8807
+ updatePlugin: async (pluginName, marketplaceName) => {
8808
+ try {
8809
+ if (this.provider && "updatePlugin" in this.provider && typeof this.provider.updatePlugin === "function") {
8810
+ const result = await this.provider.updatePlugin(pluginName, marketplaceName);
8811
+ this.logger?.info("Update plugin", {
8812
+ pluginName,
8813
+ marketplaceName,
8814
+ success: result.success
8815
+ });
8816
+ return result;
8817
+ }
8818
+ this.logger?.warn("Provider does not support updatePlugin");
8819
+ return {
8820
+ success: false,
8821
+ error: "Provider does not support updatePlugin"
8822
+ };
8823
+ } catch (error) {
8824
+ this.logger?.error("Failed to update plugin", error);
8825
+ return {
8826
+ success: false,
8827
+ error: error instanceof Error ? error.message : "Unknown error"
8828
+ };
8829
+ }
8830
+ },
8831
+ getPluginMarketplaces: async (forceRefresh) => {
8832
+ try {
8833
+ if (this.provider && "getPluginMarketplaces" in this.provider && typeof this.provider.getPluginMarketplaces === "function") {
8834
+ const result = await this.provider.getPluginMarketplaces(forceRefresh);
8835
+ this.logger?.info("Got plugin marketplaces", { count: result?.length ?? 0 });
8836
+ return result;
8837
+ }
8838
+ this.logger?.warn("Provider does not support getPluginMarketplaces");
8839
+ return [];
8840
+ } catch (error) {
8841
+ this.logger?.error("Failed to get plugin marketplaces", error);
8842
+ return [];
8843
+ }
8844
+ },
8845
+ getMarketplacePlugins: async (marketplaceName, forceRefresh, searchText) => {
8846
+ try {
8847
+ if (this.provider && "getMarketplacePlugins" in this.provider && typeof this.provider.getMarketplacePlugins === "function") {
8848
+ const result = await this.provider.getMarketplacePlugins(marketplaceName, forceRefresh, searchText);
8849
+ this.logger?.info("Got marketplace plugins", {
8850
+ marketplaceName,
8851
+ count: result?.length ?? 0
8852
+ });
8853
+ return result;
8854
+ }
8855
+ this.logger?.warn("Provider does not support getMarketplacePlugins");
8856
+ return [];
8857
+ } catch (error) {
8858
+ this.logger?.error("Failed to get marketplace plugins", error);
8859
+ return [];
8860
+ }
8861
+ },
8862
+ getPluginDetail: async (pluginName, marketplaceName) => {
8863
+ try {
8864
+ if (this.provider && "getPluginDetail" in this.provider && typeof this.provider.getPluginDetail === "function") {
8865
+ const result = await this.provider.getPluginDetail(pluginName, marketplaceName);
8866
+ this.logger?.info("Got plugin detail", {
8867
+ pluginName,
8868
+ marketplaceName
8869
+ });
8870
+ return result;
8871
+ }
8872
+ this.logger?.warn("Provider does not support getPluginDetail");
8873
+ return null;
8874
+ } catch (error) {
8875
+ this.logger?.error("Failed to get plugin detail", error);
8876
+ return null;
8877
+ }
8878
+ },
8879
+ addPluginMarketplace: async (source, name) => {
8880
+ try {
8881
+ if (this.provider && "addPluginMarketplace" in this.provider && typeof this.provider.addPluginMarketplace === "function") {
8882
+ const result = await this.provider.addPluginMarketplace(source, name);
8883
+ this.logger?.info("Add plugin marketplace", {
8884
+ source,
8885
+ name,
8886
+ success: result.success
8887
+ });
8888
+ return result;
8889
+ }
8890
+ this.logger?.warn("Provider does not support addPluginMarketplace");
8891
+ return {
8892
+ success: false,
8893
+ error: "Provider does not support addPluginMarketplace"
8894
+ };
8895
+ } catch (error) {
8896
+ this.logger?.error("Failed to add plugin marketplace", error);
8897
+ return {
8898
+ success: false,
8899
+ error: error instanceof Error ? error.message : "Unknown error"
8900
+ };
8901
+ }
8902
+ },
8903
+ removePluginMarketplace: async (marketplaceName) => {
8904
+ try {
8905
+ if (this.provider && "removePluginMarketplace" in this.provider && typeof this.provider.removePluginMarketplace === "function") {
8906
+ const result = await this.provider.removePluginMarketplace(marketplaceName);
8907
+ this.logger?.info("Remove plugin marketplace", {
8908
+ marketplaceName,
8909
+ success: result.success
8910
+ });
8911
+ return result;
8912
+ }
8913
+ this.logger?.warn("Provider does not support removePluginMarketplace");
8914
+ return {
8915
+ success: false,
8916
+ error: "Provider does not support removePluginMarketplace"
8917
+ };
8918
+ } catch (error) {
8919
+ this.logger?.error("Failed to remove plugin marketplace", error);
8920
+ return {
8921
+ success: false,
8922
+ error: error instanceof Error ? error.message : "Unknown error"
8923
+ };
8924
+ }
8925
+ },
8926
+ refreshPluginMarketplace: async (marketplaceName) => {
7685
8927
  try {
7686
- if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
7687
- const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource);
7688
- this.logger?.info("Install plugins", {
7689
- pluginNames,
8928
+ if (this.provider && "refreshPluginMarketplace" in this.provider && typeof this.provider.refreshPluginMarketplace === "function") {
8929
+ const result = await this.provider.refreshPluginMarketplace(marketplaceName);
8930
+ this.logger?.info("Refresh plugin marketplace", {
7690
8931
  marketplaceName,
7691
8932
  success: result.success
7692
8933
  });
7693
8934
  return result;
7694
8935
  }
7695
- this.logger?.warn("Provider does not support installPlugins");
8936
+ this.logger?.warn("Provider does not support refreshPluginMarketplace");
7696
8937
  return {
7697
8938
  success: false,
7698
- error: "Provider does not support installPlugins"
8939
+ error: "Provider does not support refreshPluginMarketplace"
7699
8940
  };
7700
8941
  } catch (error) {
7701
- this.logger?.error("Failed to install plugins", error);
8942
+ this.logger?.error("Failed to refresh plugin marketplace", error);
7702
8943
  return {
7703
8944
  success: false,
7704
8945
  error: error instanceof Error ? error.message : "Unknown error"
7705
8946
  };
7706
8947
  }
7707
8948
  },
7708
- getSupportScenes: async () => {
8949
+ openFolderInNewWindow: async (folderPath) => {
8950
+ try {
8951
+ if (this.provider && "openFolderInNewWindow" in this.provider && typeof this.provider.openFolderInNewWindow === "function") {
8952
+ await this.provider.openFolderInNewWindow(folderPath);
8953
+ this.logger?.info("Opened folder in new window", { folderPath });
8954
+ } else {
8955
+ this.logger?.warn("Provider does not support openFolderInNewWindow");
8956
+ throw new Error("Provider does not support openFolderInNewWindow");
8957
+ }
8958
+ } catch (error) {
8959
+ this.logger?.error("Failed to open folder in new window", error);
8960
+ throw error;
8961
+ }
8962
+ },
8963
+ openFolder: async (folderPath) => {
7709
8964
  try {
7710
- if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") {
7711
- const result = await this.provider.getSupportScenes();
7712
- this.logger?.info("Got support scenes", { count: result?.length ?? 0 });
8965
+ if (this.provider && "openFolder" in this.provider && typeof this.provider.openFolder === "function") {
8966
+ const result = await this.provider.openFolder(folderPath);
8967
+ this.logger?.info("Opened folder in system file manager", { folderPath });
7713
8968
  return result;
8969
+ } else {
8970
+ this.logger?.warn("Provider does not support openFolder");
8971
+ throw new Error("Provider does not support openFolder");
7714
8972
  }
8973
+ } catch (error) {
8974
+ this.logger?.error("Failed to open folder", error);
8975
+ throw error;
8976
+ }
8977
+ },
8978
+ getSupportScenes: async (locale) => {
8979
+ try {
8980
+ if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
7715
8981
  this.logger?.warn("Provider does not support getSupportScenes");
7716
8982
  return [];
7717
8983
  } catch (error) {
@@ -7719,23 +8985,193 @@ var AgentClient = class {
7719
8985
  return [];
7720
8986
  }
7721
8987
  },
7722
- getAvailableCommands: async (sessionId) => {
8988
+ getProductScenes: async (locale) => {
8989
+ try {
8990
+ if (this.provider?.getProductScenes) {
8991
+ const result = await this.provider.getProductScenes(locale);
8992
+ this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
8993
+ return result;
8994
+ }
8995
+ this.logger?.warn("Provider does not support getProductScenes");
8996
+ return [];
8997
+ } catch (error) {
8998
+ this.logger?.error("Failed to get product scenes", error);
8999
+ return [];
9000
+ }
9001
+ },
9002
+ getAvailableCommands: async (params) => {
7723
9003
  try {
7724
9004
  if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
7725
- const result = await this.provider.getAvailableCommands(sessionId);
9005
+ const result = await this.provider.getAvailableCommands(params);
7726
9006
  this.logger?.info("Got available commands from provider", {
7727
- sessionId: sessionId ?? "(default)",
9007
+ sessionId: params?.sessionId ?? "(default)",
7728
9008
  count: result?.length ?? 0
7729
9009
  });
7730
9010
  return result;
7731
9011
  }
7732
- this.logger?.warn("Provider does not support getAvailableCommands", { sessionId });
9012
+ this.logger?.warn("Provider does not support getAvailableCommands", { params });
7733
9013
  return [];
7734
9014
  } catch (error) {
7735
9015
  this.logger?.error("Failed to get available commands", error);
7736
9016
  return [];
7737
9017
  }
7738
9018
  },
9019
+ reportTelemetry: async (eventName, payload) => {
9020
+ try {
9021
+ if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
9022
+ else this.logger?.warn("Provider does not support reportTelemetry");
9023
+ } catch (error) {
9024
+ this.logger?.error("Failed to report telemetry", error);
9025
+ }
9026
+ },
9027
+ getProductConfiguration: async () => {
9028
+ try {
9029
+ if (this.provider?.getProductConfiguration) return await this.provider.getProductConfiguration();
9030
+ this.logger?.warn("Provider does not support getProductConfiguration");
9031
+ return {};
9032
+ } catch (error) {
9033
+ this.logger?.error("Failed to get product configuration", error);
9034
+ return {};
9035
+ }
9036
+ },
9037
+ getUserInfo: async () => {
9038
+ this.logger?.info("[AgentClient.sessions] getUserInfo() called");
9039
+ try {
9040
+ if (this.provider?.getUserInfo) {
9041
+ const result = await this.provider.getUserInfo();
9042
+ this.logger?.info("[AgentClient.sessions] getUserInfo() result:", JSON.stringify(result));
9043
+ return result;
9044
+ }
9045
+ this.logger?.warn("Provider does not support getUserInfo");
9046
+ return {};
9047
+ } catch (error) {
9048
+ this.logger?.error("Failed to get user info", error);
9049
+ return {};
9050
+ }
9051
+ },
9052
+ respondToSampling: async (sessionId, response) => {
9053
+ try {
9054
+ if (this.provider?.respondToSampling) {
9055
+ await this.provider.respondToSampling(sessionId, response);
9056
+ this.logger?.info("Responded to sampling request", {
9057
+ sessionId,
9058
+ requestId: response.id,
9059
+ approved: response.approved
9060
+ });
9061
+ } else this.logger?.warn("Provider does not support respondToSampling");
9062
+ } catch (error) {
9063
+ this.logger?.error("Failed to respond to sampling request", error);
9064
+ throw error;
9065
+ }
9066
+ },
9067
+ respondToRoots: async (sessionId, response) => {
9068
+ try {
9069
+ if (this.provider?.respondToRoots) {
9070
+ await this.provider.respondToRoots(sessionId, response);
9071
+ this.logger?.info("Responded to roots request", {
9072
+ sessionId,
9073
+ requestId: response.id,
9074
+ approved: response.approved
9075
+ });
9076
+ } else this.logger?.warn("Provider does not support respondToRoots");
9077
+ } catch (error) {
9078
+ this.logger?.error("Failed to respond to roots request", error);
9079
+ throw error;
9080
+ }
9081
+ },
9082
+ subscribeSamplingRequests: (serverName, callback) => {
9083
+ if (this.provider?.subscribeSamplingRequests) {
9084
+ this.logger?.info("Subscribing to sampling requests", { serverName });
9085
+ return this.provider.subscribeSamplingRequests(serverName, callback);
9086
+ }
9087
+ this.logger?.warn("Provider does not support subscribeSamplingRequests");
9088
+ return () => {};
9089
+ },
9090
+ subscribeRootsRequests: (serverName, callback) => {
9091
+ if (this.provider?.subscribeRootsRequests) {
9092
+ this.logger?.info("Subscribing to roots requests", { serverName });
9093
+ return this.provider.subscribeRootsRequests(serverName, callback);
9094
+ }
9095
+ this.logger?.warn("Provider does not support subscribeRootsRequests");
9096
+ return () => {};
9097
+ },
9098
+ getMcpServers: async () => {
9099
+ if (this.provider?.getMcpServers) {
9100
+ this.logger?.info("Getting MCP servers list");
9101
+ return this.provider.getMcpServers();
9102
+ }
9103
+ this.logger?.warn("Provider does not support getMcpServers");
9104
+ return [];
9105
+ },
9106
+ toggleMcpServer: async (serverName, enabled) => {
9107
+ if (this.provider?.toggleMcpServer) {
9108
+ this.logger?.info("Toggling MCP server", {
9109
+ serverName,
9110
+ enabled
9111
+ });
9112
+ await this.provider.toggleMcpServer(serverName, enabled);
9113
+ } else {
9114
+ this.logger?.warn("Provider does not support toggleMcpServer");
9115
+ throw new Error("toggleMcpServer not supported by provider");
9116
+ }
9117
+ },
9118
+ reconnectMcpServer: async (serverName, forceHttpCallback) => {
9119
+ if (this.provider?.reconnectMcpServer) {
9120
+ this.logger?.info("Reconnecting MCP server", {
9121
+ serverName,
9122
+ forceHttpCallback
9123
+ });
9124
+ await this.provider.reconnectMcpServer(serverName, forceHttpCallback);
9125
+ } else {
9126
+ this.logger?.warn("Provider does not support reconnectMcpServer");
9127
+ throw new Error("reconnectMcpServer not supported by provider");
9128
+ }
9129
+ },
9130
+ deleteMcpServer: async (serverName) => {
9131
+ if (this.provider?.deleteMcpServer) {
9132
+ this.logger?.info("Deleting MCP server", { serverName });
9133
+ await this.provider.deleteMcpServer(serverName);
9134
+ } else {
9135
+ this.logger?.warn("Provider does not support deleteMcpServer");
9136
+ throw new Error("deleteMcpServer not supported by provider");
9137
+ }
9138
+ },
9139
+ openMcpConfig: async () => {
9140
+ if (this.provider?.openMcpConfig) {
9141
+ this.logger?.info("Opening MCP config");
9142
+ await this.provider.openMcpConfig();
9143
+ } else {
9144
+ this.logger?.warn("Provider does not support openMcpConfig");
9145
+ throw new Error("openMcpConfig not supported by provider");
9146
+ }
9147
+ },
9148
+ getMcpConfigContent: async () => {
9149
+ if (this.provider?.getMcpConfigContent) {
9150
+ this.logger?.info("Getting MCP config content");
9151
+ return await this.provider.getMcpConfigContent();
9152
+ } else {
9153
+ this.logger?.warn("Provider does not support getMcpConfigContent");
9154
+ throw new Error("getMcpConfigContent not supported by provider");
9155
+ }
9156
+ },
9157
+ saveMcpConfigContent: async (content) => {
9158
+ if (this.provider?.saveMcpConfigContent) {
9159
+ this.logger?.info("Saving MCP config content");
9160
+ await this.provider.saveMcpConfigContent(content);
9161
+ } else {
9162
+ this.logger?.warn("Provider does not support saveMcpConfigContent");
9163
+ throw new Error("saveMcpConfigContent not supported by provider");
9164
+ }
9165
+ },
9166
+ clipboardReadText: async () => {
9167
+ if (this.provider?.clipboardReadText) {
9168
+ this.logger?.info("Reading clipboard text");
9169
+ return await this.provider.clipboardReadText();
9170
+ } else {
9171
+ this.logger?.warn("Provider does not support clipboardReadText");
9172
+ throw new Error("clipboardReadText not supported by provider");
9173
+ }
9174
+ },
7739
9175
  models: this.createModelsResource()
7740
9176
  };
7741
9177
  }
@@ -7961,10 +9397,6 @@ const oauthRepositoryService = new OAuthRepositoryService();
7961
9397
  * 判断当前账号是否是 SSO 账号
7962
9398
  * 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
7963
9399
  */
7964
- const isSSODomain = () => {
7965
- const { hostname } = window.location;
7966
- return hostname.includes(".sso.copilot") || hostname.includes("sso.codebuddy.cn") || hostname.includes(".sso.copilot-staging") || hostname.includes(".staging-sso.codebuddy.cn");
7967
- };
7968
9400
  /**
7969
9401
  * 根据路径获取完整 URL
7970
9402
  * - SSO 账号需要跳转到对应的预发/生产域名
@@ -7972,16 +9404,7 @@ const isSSODomain = () => {
7972
9404
  * @param path 路径,如 '/login'、'/logout'、'/home' 等
7973
9405
  * @returns 完整的 URL 地址
7974
9406
  */
7975
- const getFullUrl = (path) => {
7976
- const { hostname, protocol } = window.location;
7977
- if (isSSODomain()) {
7978
- const isCodebuddy = hostname.includes("codebuddy.cn");
7979
- const isStaging = hostname.includes("staging");
7980
- if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn${path}` : `${protocol}//www.codebuddy.cn${path}`;
7981
- else return isStaging ? `${protocol}//staging-copilot.tencent.com${path}` : `${protocol}//copilot.tencent.com${path}`;
7982
- }
7983
- return `${window.location.origin}${path}`;
7984
- };
9407
+ const getFullUrl = (path) => `${window.location.origin}${path}`;
7985
9408
  /** 获取当前域名的账号选择页面 URL */
7986
9409
  const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
7987
9410
  /** localStorage 中存储选中账号 ID 的 key */
@@ -8014,16 +9437,34 @@ const getPackageName = (packageCode) => CommodityCodeText[packageCode] || "";
8014
9437
  * 注意:getAgents 和 getModels 方法已废弃并移除,
8015
9438
  * 请使用 IAgentAdapter 中的对应方法
8016
9439
  */
8017
- var BackendProvider = class {
9440
+ var BackendProvider = class BackendProvider {
8018
9441
  constructor(config) {
8019
9442
  httpService.setBaseURL(config.baseUrl);
8020
9443
  if (config.authToken) httpService.setAuthToken(config.authToken);
8021
- httpService.onUnauthorized(() => {
8022
- console.log("[BackendProvider] User unauthorized (401/403), triggering logout");
8023
- this.logout().catch((error) => {
8024
- console.error("[BackendProvider] Logout failed in 401 handler:", error);
9444
+ httpService.onUnauthorized(() => this.handleUnauthorized());
9445
+ }
9446
+ /**
9447
+ * 处理 401 未授权错误
9448
+ * 先尝试刷新 token,失败后再执行登出流程
9449
+ *
9450
+ * @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
9451
+ */
9452
+ async handleUnauthorized() {
9453
+ console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
9454
+ try {
9455
+ if (await this.refreshToken()) {
9456
+ console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
9457
+ return;
9458
+ }
9459
+ throw new Error("Token refresh returned null");
9460
+ } catch (error) {
9461
+ console.error("[BackendProvider] Token refresh failed after 401:", error);
9462
+ console.log("[BackendProvider] Token refresh failed, triggering logout");
9463
+ this.logout().catch((logoutError) => {
9464
+ console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
8025
9465
  });
8026
- });
9466
+ throw error;
9467
+ }
8027
9468
  }
8028
9469
  /**
8029
9470
  * 获取当前账号信息
@@ -8287,10 +9728,11 @@ var BackendProvider = class {
8287
9728
  if (!time) return 0;
8288
9729
  return new Date(time).getTime();
8289
9730
  };
8290
- const dailyCredits = [CommodityCode.free, CommodityCode.freeMon];
9731
+ const dailyCredits = [CommodityCode.free];
8291
9732
  const planResources = resources.map((r) => {
8292
9733
  const isDaily = dailyCredits.includes(r.PackageCode);
8293
9734
  const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
9735
+ const refreshAt = parseTime(r.CycleEndTime) + 1e3;
8294
9736
  return {
8295
9737
  id: r.ResourceId,
8296
9738
  name: isDaily ? "plan.addonCredits" : getPackageName(r.PackageCode),
@@ -8300,7 +9742,7 @@ var BackendProvider = class {
8300
9742
  used: Math.max(0, Number(r.CycleCapacitySizePrecise) - Number(r.CycleCapacityRemainPrecise)) || 0,
8301
9743
  left: Number(r.CycleCapacityRemainPrecise) || 0,
8302
9744
  expireAt: parseTime(endTime),
8303
- refreshAt: isDaily ? void 0 : parseTime(r.CycleEndTime)
9745
+ refreshAt: isDaily ? void 0 : refreshAt
8304
9746
  };
8305
9747
  }).sort((a, b) => {
8306
9748
  const getPriority = (code) => {
@@ -8308,10 +9750,11 @@ var BackendProvider = class {
8308
9750
  CommodityCode.proMon,
8309
9751
  CommodityCode.proMonPlus,
8310
9752
  CommodityCode.proYear,
9753
+ CommodityCode.freeMon,
8311
9754
  CommodityCode.extra
8312
9755
  ].includes(code)) return 1;
8313
9756
  if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
8314
- if ([CommodityCode.free, CommodityCode.freeMon].includes(code)) return 3;
9757
+ if ([CommodityCode.free].includes(code)) return 3;
8315
9758
  return 4;
8316
9759
  };
8317
9760
  return getPriority(a.packageCode) - getPriority(b.packageCode);
@@ -8436,34 +9879,65 @@ var BackendProvider = class {
8436
9879
  }
8437
9880
  /**
8438
9881
  * 登出账号
8439
- * Web 环境: 通过 iframe 访问登出 URL 清除 cookie
9882
+ *
9883
+ * 策略:
9884
+ * - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
9885
+ * - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
8440
9886
  */
8441
9887
  async logout() {
8442
- const url = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
9888
+ const account = accountService.getAccount();
9889
+ if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
9890
+ else await this.logoutViaHttp();
9891
+ localStorage.removeItem(SELECTED_ACCOUNT_KEY);
9892
+ accountService.clearAccount();
9893
+ }
9894
+ /**
9895
+ * IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
9896
+ * 轮询 iframe URL 变化检测完成,兜底超时 5 秒
9897
+ */
9898
+ async logoutViaIframe() {
9899
+ const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
8443
9900
  try {
8444
9901
  await new Promise((resolve) => {
8445
9902
  const iframe = document.createElement("iframe");
8446
9903
  iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
8447
- iframe.src = url;
8448
- const timeout = setTimeout(() => {
8449
- cleanup();
8450
- resolve();
8451
- }, 5e3);
8452
- const cleanup = () => {
9904
+ iframe.src = logoutUrl;
9905
+ let pollTimer;
9906
+ let settled = false;
9907
+ const done = () => {
9908
+ if (settled) return;
9909
+ settled = true;
9910
+ clearInterval(pollTimer);
8453
9911
  clearTimeout(timeout);
8454
9912
  if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
8455
- };
8456
- iframe.onerror = () => {
8457
- cleanup();
8458
9913
  resolve();
8459
9914
  };
9915
+ let wasRedirecting = false;
9916
+ pollTimer = setInterval(() => {
9917
+ try {
9918
+ const href = iframe.contentWindow?.location?.href;
9919
+ if (wasRedirecting && href) done();
9920
+ } catch {
9921
+ wasRedirecting = true;
9922
+ }
9923
+ }, 100);
9924
+ const timeout = setTimeout(done, 5e3);
9925
+ iframe.onerror = done;
8460
9926
  document.body.appendChild(iframe);
8461
9927
  });
8462
9928
  } catch (error) {
8463
- console.error("[BackendProvider] logout failed:", error);
9929
+ console.error("[BackendProvider] logout via iframe failed:", error);
9930
+ }
9931
+ }
9932
+ /**
9933
+ * 非 IOA 企业登出:直接 HTTP 请求 /console/logout
9934
+ */
9935
+ async logoutViaHttp() {
9936
+ try {
9937
+ await httpService.get("/console/logout");
9938
+ } catch (error) {
9939
+ console.error("[BackendProvider] logout via http failed:", error);
8464
9940
  }
8465
- localStorage.removeItem(SELECTED_ACCOUNT_KEY);
8466
- accountService.clearAccount();
8467
9941
  }
8468
9942
  /**
8469
9943
  * 批量切换插件状态
@@ -8541,6 +10015,132 @@ var BackendProvider = class {
8541
10015
  async getRepositories(connector, page = 0, perPage = 100) {
8542
10016
  return oauthRepositoryService.getRepositories(connector, page, perPage);
8543
10017
  }
10018
+ /**
10019
+ * 保存待发送的输入内容到后端
10020
+ * API 端点: POST /api/v1/code-id
10021
+ */
10022
+ async savePendingInput(code) {
10023
+ try {
10024
+ const result = await httpService.post("/api/v1/code-id", { code });
10025
+ return result?.codeId || result?.data?.codeId || null;
10026
+ } catch (e) {
10027
+ console.warn("[BackendProvider] savePendingInput failed:", e);
10028
+ return null;
10029
+ }
10030
+ }
10031
+ /**
10032
+ * 从后端加载待发送的输入内容
10033
+ * API 端点: GET /api/v1/code?id=xxx
10034
+ */
10035
+ async loadPendingInput(codeId) {
10036
+ try {
10037
+ const result = await httpService.get(`/api/v1/code?id=${encodeURIComponent(codeId)}`);
10038
+ return result?.code || result?.data?.code || null;
10039
+ } catch (e) {
10040
+ console.warn("[BackendProvider] loadPendingInput failed:", e);
10041
+ return null;
10042
+ }
10043
+ }
10044
+ /**
10045
+ * 获取每日签到状态
10046
+ * API 端点: POST /billing/meter/checkin-status
10047
+ */
10048
+ async getCheckinStatus() {
10049
+ try {
10050
+ const result = await httpService.post("/billing/meter/checkin-status", {});
10051
+ if (result?.code === 0 && result?.data) return result.data;
10052
+ return null;
10053
+ } catch (error) {
10054
+ console.error("[BackendProvider] getCheckinStatus failed:", error);
10055
+ return null;
10056
+ }
10057
+ }
10058
+ /**
10059
+ * 执行每日签到
10060
+ * API 端点: POST /billing/meter/daily-checkin
10061
+ */
10062
+ async claimDailyCheckin() {
10063
+ const result = await httpService.post("/billing/meter/daily-checkin", {});
10064
+ if (result?.code === 0 && result?.data) return result.data;
10065
+ throw new Error(result?.msg || "Checkin failed");
10066
+ }
10067
+ static {
10068
+ this.SKILLHUB_BASE_URL = "https://lightmake.site";
10069
+ }
10070
+ static {
10071
+ this.SKILLHUB_FETCH_TIMEOUT = 1e4;
10072
+ }
10073
+ async skillHubFetch(url, init) {
10074
+ const controller = new AbortController();
10075
+ const timer = setTimeout(() => controller.abort(), BackendProvider.SKILLHUB_FETCH_TIMEOUT);
10076
+ try {
10077
+ return await fetch(url, {
10078
+ ...init,
10079
+ signal: controller.signal
10080
+ });
10081
+ } finally {
10082
+ clearTimeout(timer);
10083
+ }
10084
+ }
10085
+ async getSkillHubList(params = {}) {
10086
+ const qs = new URLSearchParams();
10087
+ if (params.page) qs.set("page", String(params.page));
10088
+ if (params.pageSize) qs.set("pageSize", String(params.pageSize));
10089
+ if (params.sortBy) qs.set("sortBy", params.sortBy);
10090
+ if (params.order) qs.set("order", params.order);
10091
+ if (params.keyword) qs.set("keyword", params.keyword);
10092
+ if (params.category) qs.set("category", params.category);
10093
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/skills?${qs.toString()}`);
10094
+ if (!res.ok) throw new Error(`SkillHub list: ${res.status}`);
10095
+ return res.json();
10096
+ }
10097
+ async getSkillHubCategories() {
10098
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/categories`);
10099
+ if (!res.ok) throw new Error(`SkillHub categories: ${res.status}`);
10100
+ return res.json();
10101
+ }
10102
+ async getSkillHubSearch(q, limit = 20) {
10103
+ const qs = new URLSearchParams({
10104
+ q,
10105
+ limit: String(limit)
10106
+ });
10107
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/search?${qs.toString()}`);
10108
+ if (!res.ok) throw new Error(`SkillHub search: ${res.status}`);
10109
+ return res.json();
10110
+ }
10111
+ async getSkillHubDetail(slug) {
10112
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/skills/${encodeURIComponent(slug)}`);
10113
+ if (!res.ok) throw new Error(`SkillHub detail: ${res.status}`);
10114
+ return res.json();
10115
+ }
10116
+ async getSkillHubExists(slugs) {
10117
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/skills/exists`, {
10118
+ method: "POST",
10119
+ headers: { "Content-Type": "application/json" },
10120
+ body: JSON.stringify({ slugs })
10121
+ });
10122
+ if (!res.ok) throw new Error(`SkillHub exists: ${res.status}`);
10123
+ return res.json();
10124
+ }
10125
+ async reportSkillHubStats(slug, inc) {
10126
+ try {
10127
+ await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/skills/${encodeURIComponent(slug)}/stats/inc`, {
10128
+ method: "POST",
10129
+ headers: { "Content-Type": "application/json" },
10130
+ body: JSON.stringify(inc)
10131
+ });
10132
+ } catch {}
10133
+ }
10134
+ async installSkillHubSkill(_slug, _version, _name) {
10135
+ return {
10136
+ success: false,
10137
+ skillName: _slug,
10138
+ errorMessage: "SkillHub install requires IDE mode (no IPC channel available)"
10139
+ };
10140
+ }
10141
+ async getSkillHubInstalledMetas() {
10142
+ return [];
10143
+ }
8544
10144
  };
8545
10145
  /**
8546
10146
  * 创建 BackendProvider 实例
@@ -8579,10 +10179,21 @@ const BACKEND_REQUEST_TYPES = {
8579
10179
  REVOKE_ALL: "backend:revoke-all",
8580
10180
  GET_FILE: "backend:get-file",
8581
10181
  RELOAD_WINDOW: "backend:reload-window",
10182
+ SAVE_LOCALE: "backend:save-locale",
8582
10183
  CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
8583
10184
  OPEN_EXTERNAL: "backend:open-external",
8584
10185
  BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
8585
- GET_SUPPORT_SCENES: "backend:get-support-scenes"
10186
+ GET_SUPPORT_SCENES: "backend:get-support-scenes",
10187
+ GET_ACCOUNT_USAGE: "backend:get-account-usage",
10188
+ GET_CHECKIN_STATUS: "backend:get-checkin-status",
10189
+ CLAIM_DAILY_CHECKIN: "backend:claim-daily-checkin",
10190
+ SKILLHUB_LIST: "backend:skillhub-list",
10191
+ SKILLHUB_CATEGORIES: "backend:skillhub-categories",
10192
+ SKILLHUB_SEARCH: "backend:skillhub-search",
10193
+ SKILLHUB_DETAIL: "backend:skillhub-detail",
10194
+ SKILLHUB_DOWNLOAD_URL: "backend:skillhub-download-url",
10195
+ SKILLHUB_EXISTS: "backend:skillhub-exists",
10196
+ SKILLHUB_REPORT_STATS: "backend:skillhub-report-stats"
8586
10197
  };
8587
10198
  /**
8588
10199
  * 生成唯一请求 ID
@@ -8629,12 +10240,14 @@ var IPCBackendProvider = class {
8629
10240
  */
8630
10241
  async getAccount() {
8631
10242
  this.log("Getting account via IPC");
10243
+ const startTime = performance.now();
8632
10244
  try {
8633
10245
  const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
10246
+ this.log(`getAccount IPC completed in ${(performance.now() - startTime).toFixed(0)}ms`);
8634
10247
  accountService.setAccount(account);
8635
10248
  return account;
8636
10249
  } catch (error) {
8637
- this.log("Get account failed:", error);
10250
+ this.log(`getAccount IPC failed after ${(performance.now() - startTime).toFixed(0)}ms:`, error);
8638
10251
  accountService.setAccount(null);
8639
10252
  return null;
8640
10253
  }
@@ -8863,6 +10476,20 @@ var IPCBackendProvider = class {
8863
10476
  }
8864
10477
  }
8865
10478
  /**
10479
+ * Save locale to argv.json without restarting the app.
10480
+ * The change takes effect on next manual restart.
10481
+ * @param params locale to save
10482
+ */
10483
+ async saveLocale(params) {
10484
+ this.log("Saving locale to argv.json via IPC", params);
10485
+ try {
10486
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SAVE_LOCALE, params);
10487
+ } catch (error) {
10488
+ this.log("Save locale request failed:", error);
10489
+ throw error;
10490
+ }
10491
+ }
10492
+ /**
8866
10493
  * 关闭 Agent Manager 面板
8867
10494
  * IDE 环境: 通过 IPC 通知 IDE 关闭 Agent Manager(用于返回 IDE)
8868
10495
  */
@@ -8923,6 +10550,174 @@ var IPCBackendProvider = class {
8923
10550
  }
8924
10551
  }
8925
10552
  /**
10553
+ * 获取账号用量信息(积分/Credits)
10554
+ * IDE 环境: 通过 IPC 实时获取用量信息,每次打开菜单时调用
10555
+ *
10556
+ * 调用链:
10557
+ * 1. agent-ui: IPCBackendProvider.getAccountUsage()
10558
+ * 2. Agent Manager renderer: BackendService.getAccountUsage()
10559
+ * 3. Main Process: codebuddy:getAccountUsage IPC handler
10560
+ * 4. 返回 { usageLeft, usageTotal, editionType, refreshAt } 等字段
10561
+ */
10562
+ async getAccountUsage() {
10563
+ this.log("Getting account usage via IPC");
10564
+ try {
10565
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT_USAGE);
10566
+ } catch (error) {
10567
+ this.log("Get account usage failed:", error);
10568
+ return null;
10569
+ }
10570
+ }
10571
+ /**
10572
+ * 获取每日签到状态
10573
+ * IDE 环境: 通过 IPC 获取签到状态
10574
+ */
10575
+ async getCheckinStatus() {
10576
+ this.log("Getting checkin status via IPC");
10577
+ try {
10578
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_CHECKIN_STATUS);
10579
+ } catch (error) {
10580
+ this.log("Get checkin status failed:", error);
10581
+ return null;
10582
+ }
10583
+ }
10584
+ /**
10585
+ * 执行每日签到
10586
+ * IDE 环境: 通过 IPC 执行签到
10587
+ */
10588
+ async claimDailyCheckin() {
10589
+ this.log("Claiming daily checkin via IPC");
10590
+ try {
10591
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.CLAIM_DAILY_CHECKIN);
10592
+ } catch (error) {
10593
+ this.log("Claim daily checkin failed:", error);
10594
+ throw error;
10595
+ }
10596
+ }
10597
+ /**
10598
+ * 获取 SkillHub 技能列表
10599
+ * IDE 环境: 通过 IPC 获取技能列表
10600
+ */
10601
+ async getSkillHubList(params) {
10602
+ this.log("Getting SkillHub list via IPC", params);
10603
+ try {
10604
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_LIST, params);
10605
+ } catch (error) {
10606
+ this.log("Get SkillHub list failed:", error);
10607
+ throw error;
10608
+ }
10609
+ }
10610
+ /**
10611
+ * 获取 SkillHub 分类列表
10612
+ * IDE 环境: 通过 IPC 获取分类列表
10613
+ */
10614
+ async getSkillHubCategories() {
10615
+ this.log("Getting SkillHub categories via IPC");
10616
+ try {
10617
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_CATEGORIES);
10618
+ } catch (error) {
10619
+ this.log("Get SkillHub categories failed:", error);
10620
+ throw error;
10621
+ }
10622
+ }
10623
+ /**
10624
+ * 搜索 SkillHub 技能
10625
+ * IDE 环境: 通过 IPC 搜索技能
10626
+ */
10627
+ async getSkillHubSearch(q, limit = 20) {
10628
+ this.log("Searching SkillHub via IPC", {
10629
+ q,
10630
+ limit
10631
+ });
10632
+ try {
10633
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_SEARCH, {
10634
+ q,
10635
+ limit
10636
+ });
10637
+ } catch (error) {
10638
+ this.log("SkillHub search failed:", error);
10639
+ throw error;
10640
+ }
10641
+ }
10642
+ /**
10643
+ * 获取 SkillHub 技能详情
10644
+ * IDE 环境: 通过 IPC 获取技能详情
10645
+ */
10646
+ async getSkillHubDetail(slug) {
10647
+ this.log("Getting SkillHub detail via IPC", { slug });
10648
+ try {
10649
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_DETAIL, { slug });
10650
+ } catch (error) {
10651
+ this.log("Get SkillHub detail failed:", error);
10652
+ throw error;
10653
+ }
10654
+ }
10655
+ /**
10656
+ * 批量检查 SkillHub 技能是否存在
10657
+ * IDE 环境: 通过 IPC 检查技能是否存在
10658
+ */
10659
+ async getSkillHubExists(slugs) {
10660
+ this.log("Checking SkillHub exists via IPC", { slugs });
10661
+ try {
10662
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_EXISTS, { slugs });
10663
+ } catch (error) {
10664
+ this.log("SkillHub exists check failed:", error);
10665
+ throw error;
10666
+ }
10667
+ }
10668
+ /**
10669
+ * 上报 SkillHub 技能统计
10670
+ * IDE 环境: 通过 IPC 上报统计
10671
+ */
10672
+ async reportSkillHubStats(slug, inc) {
10673
+ this.log("Reporting SkillHub stats via IPC", {
10674
+ slug,
10675
+ inc
10676
+ });
10677
+ try {
10678
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_REPORT_STATS, {
10679
+ slug,
10680
+ inc
10681
+ });
10682
+ } catch (error) {
10683
+ this.log("Report SkillHub stats failed:", error);
10684
+ }
10685
+ }
10686
+ /**
10687
+ * 安装 SkillHub 技能
10688
+ * IDE 环境: 通过 IPC 安装技能(下载 zip → 解压到本地)
10689
+ */
10690
+ async installSkillHubSkill(slug, version, name) {
10691
+ this.log("Installing SkillHub skill via IPC", {
10692
+ slug,
10693
+ version,
10694
+ name
10695
+ });
10696
+ try {
10697
+ return await this.sendBackendRequest("backend:skillhub-install", {
10698
+ slug,
10699
+ version,
10700
+ name
10701
+ });
10702
+ } catch (error) {
10703
+ this.log("Install SkillHub skill failed:", error);
10704
+ throw error;
10705
+ }
10706
+ }
10707
+ /**
10708
+ * 获取本地已安装的 SkillHub 技能元信息
10709
+ * IDE 环境: 通过 IPC 获取元信息
10710
+ */
10711
+ async getSkillHubInstalledMetas() {
10712
+ this.log("Getting SkillHub installed metas via IPC");
10713
+ try {
10714
+ return await this.sendBackendRequest("backend:skillhub-installed-metas");
10715
+ } catch (error) {
10716
+ this.log("Get SkillHub installed metas failed:", error);
10717
+ return [];
10718
+ }
10719
+ }
10720
+ /**
8926
10721
  * 调试日志
8927
10722
  */
8928
10723
  log(...args) {