@tencent-ai/cloud-agent-sdk 0.2.13 → 0.2.14-next.17885cf.20260323

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");
@@ -1143,6 +1143,10 @@ var ArtifactManager = class {
1143
1143
  */
1144
1144
  const DEFAULT_INITIALIZE_TIMEOUT = 3e4;
1145
1145
  /**
1146
+ * Default timeout for session/new operation (waiting for sessionId via SSE)
1147
+ */
1148
+ const DEFAULT_SESSION_TIMEOUT = 3e4;
1149
+ /**
1146
1150
  * Default timeout for permission requests
1147
1151
  */
1148
1152
  const DEFAULT_PERMISSION_TIMEOUT = 3e5;
@@ -1198,8 +1202,14 @@ var InitializationError = class extends ACPClientError {
1198
1202
  * Error thrown for session-related failures
1199
1203
  */
1200
1204
  var SessionError = class extends ACPClientError {
1201
- constructor(message, sessionId, cause) {
1202
- super(message, "SESSION_ERROR", cause);
1205
+ constructor(message, sessionId, agentIdOrCause, cause) {
1206
+ if (agentIdOrCause instanceof Error) {
1207
+ super(message, "SESSION_ERROR", agentIdOrCause);
1208
+ this.agentId = void 0;
1209
+ } else {
1210
+ super(message, "SESSION_ERROR", cause);
1211
+ this.agentId = agentIdOrCause;
1212
+ }
1203
1213
  this.name = "SessionError";
1204
1214
  this.sessionId = sessionId;
1205
1215
  }
@@ -1874,6 +1884,9 @@ var StreamableHttpClient = class {
1874
1884
  headers: this.options.headers,
1875
1885
  reconnect: this.options.reconnect,
1876
1886
  fetch: this.options.fetch,
1887
+ heartbeatTimeout: this.options.heartbeatTimeout,
1888
+ postTimeout: this.options.postTimeout,
1889
+ connectionTimeout: this.options.connectionTimeout,
1877
1890
  onConnect: (connectionId) => {
1878
1891
  this.options.logger?.debug(`Transport connected: ${connectionId}`);
1879
1892
  },
@@ -1948,10 +1961,6 @@ var StreamableHttpClient = class {
1948
1961
  },
1949
1962
  requestPermission: async (params) => this.handleRequestPermission(params),
1950
1963
  extNotification: async (method, params) => {
1951
- console.log("[ACP-Client] extNotification callback invoked:", {
1952
- method,
1953
- paramsKeys: Object.keys(params)
1954
- });
1955
1964
  await this.handleExtNotification(method, params);
1956
1965
  },
1957
1966
  extMethod: async (method, params) => this.handleExtMethod(method, params)
@@ -1959,19 +1968,47 @@ var StreamableHttpClient = class {
1959
1968
  }
1960
1969
  /**
1961
1970
  * Create a new session
1971
+ *
1972
+ * Retries on transient network errors (e.g., proxy connection reset)
1973
+ * since session/new is idempotent and safe to retry.
1974
+ *
1975
+ * A timeout (`options.sessionTimeout`, default 30 s) guards against the
1976
+ * SSE response never arriving — the POST returns 202 immediately and the
1977
+ * sessionId comes back via GET SSE, which can hang indefinitely.
1978
+ * On timeout a `SessionError` is thrown.
1962
1979
  */
1963
1980
  async createSession(cwd) {
1964
1981
  this.ensureInitialized("createSession");
1965
- try {
1966
- const response = await this.connection.newSession({
1982
+ const maxRetries = 2;
1983
+ const timeout = this.options.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;
1984
+ let lastError;
1985
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
1986
+ const sessionPromise = this.connection.newSession({
1967
1987
  cwd,
1968
1988
  mcpServers: []
1969
1989
  });
1990
+ let response;
1991
+ if (timeout > 0) {
1992
+ const timeoutPromise = new Promise((_, reject) => {
1993
+ setTimeout(() => {
1994
+ reject(new SessionError(`session/new timed out after ${timeout}ms`));
1995
+ }, timeout);
1996
+ });
1997
+ response = await Promise.race([sessionPromise, timeoutPromise]);
1998
+ } else response = await sessionPromise;
1970
1999
  this.options.logger?.info(`Session created: ${response.sessionId}`);
1971
2000
  return response;
1972
2001
  } 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);
2002
+ lastError = err instanceof Error ? err : new Error(String(err));
2003
+ if (attempt < maxRetries && isRetryableNetworkError(err)) {
2004
+ const delay = 500 * Math.pow(2, attempt);
2005
+ this.options.logger?.warn(`session/new network error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries}): ${lastError.message}`);
2006
+ await new Promise((resolve) => setTimeout(resolve, delay));
2007
+ continue;
2008
+ }
2009
+ throw new SessionError(`Failed to create session: ${lastError.message}`, void 0, lastError);
1974
2010
  }
2011
+ throw new SessionError(`Failed to create session: ${lastError?.message}`, void 0, lastError);
1975
2012
  }
1976
2013
  /**
1977
2014
  * Load an existing session
@@ -2195,6 +2232,18 @@ var StreamableHttpClient = class {
2195
2232
  if (this.state !== "initialized") throw new InvalidStateError(operation, this.state, ["initialized"]);
2196
2233
  }
2197
2234
  };
2235
+ /**
2236
+ * Check if an error is a retryable network-level error.
2237
+ * Only network failures (TypeError from fetch) are retried, NOT HTTP errors (4xx/5xx).
2238
+ */
2239
+ function isRetryableNetworkError(error) {
2240
+ if (error instanceof TypeError) return true;
2241
+ if (error instanceof Error) {
2242
+ const msg = error.message.toLowerCase();
2243
+ 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");
2244
+ }
2245
+ return false;
2246
+ }
2198
2247
 
2199
2248
  //#endregion
2200
2249
  //#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-connection.ts
@@ -2230,7 +2279,7 @@ var CloudAgentConnection = class {
2230
2279
  fetch: config.fetch,
2231
2280
  clientCapabilities: config.clientCapabilities,
2232
2281
  onSessionUpdate: (update) => {
2233
- if (!this._isStreaming) this.emit("sessionUpdate", update);
2282
+ if (!this._isStreaming && this.isOwnSessionNotification(update)) this.emit("sessionUpdate", update);
2234
2283
  },
2235
2284
  onArtifact: (artifact, event) => {
2236
2285
  console.log("[CloudConnection] onArtifact callback:", {
@@ -2244,10 +2293,41 @@ var CloudAgentConnection = class {
2244
2293
  },
2245
2294
  onUsageUpdate: (usage) => {
2246
2295
  this.emit("usageUpdate", usage);
2296
+ },
2297
+ onExtNotification: (method, params) => {
2298
+ console.log("[CloudConnection] Received extNotification:", {
2299
+ method,
2300
+ paramsKeys: Object.keys(params)
2301
+ });
2302
+ if (method === ExtensionMethod.COMMAND) {
2303
+ const action = params.action;
2304
+ const commandParams = params.params;
2305
+ console.log("[CloudConnection] Emitting command event:", {
2306
+ action,
2307
+ paramsKeys: commandParams ? Object.keys(commandParams) : []
2308
+ });
2309
+ this.emit("command", {
2310
+ action,
2311
+ params: commandParams
2312
+ });
2313
+ }
2247
2314
  }
2248
2315
  });
2249
2316
  this.setupEventForwarding();
2250
2317
  }
2318
+ /**
2319
+ * Check whether a notification belongs to this connection's own session.
2320
+ *
2321
+ * CloudConnection.createSession() overrides sessionId to agentId, so the
2322
+ * rest of the client stack uses agentId as the canonical session
2323
+ * identifier. Notifications whose sessionId differs from agentId
2324
+ * originate from sub-agent sessions running inside the same sandbox and
2325
+ * should be silently ignored at this layer — the adapter layer handles
2326
+ * sub-agent messages independently via parentToolUseId in _meta.
2327
+ */
2328
+ isOwnSessionNotification(notification) {
2329
+ return notification.sessionId === this.agentId;
2330
+ }
2251
2331
  setupEventForwarding() {
2252
2332
  this.client.on("connecting", () => {
2253
2333
  this.emit("connecting", void 0);
@@ -2408,6 +2488,7 @@ var CloudAgentConnection = class {
2408
2488
  let resolveUpdate = null;
2409
2489
  let done = false;
2410
2490
  const listener = (update) => {
2491
+ if (!this.isOwnSessionNotification(update)) return;
2411
2492
  if (resolveUpdate) {
2412
2493
  resolveUpdate(update);
2413
2494
  resolveUpdate = null;
@@ -2490,6 +2571,16 @@ var CloudAgentConnection = class {
2490
2571
  get sessionConnectionInfo() {
2491
2572
  return this._sessionConnectionInfo;
2492
2573
  }
2574
+ async reportTelemetry(eventName, payload) {
2575
+ try {
2576
+ await this.client.extMethod("reportTelemetry", {
2577
+ eventName,
2578
+ payload
2579
+ });
2580
+ } catch (error) {
2581
+ console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
2582
+ }
2583
+ }
2493
2584
  async extMethod(method, params) {
2494
2585
  return this.client.extMethod(method, params);
2495
2586
  }
@@ -3092,7 +3183,7 @@ var utils_default = {
3092
3183
  *
3093
3184
  * @returns {Error} The created error.
3094
3185
  */
3095
- function AxiosError(message, code, config, request, response) {
3186
+ function AxiosError$1(message, code, config, request, response) {
3096
3187
  Error.call(this);
3097
3188
  if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
3098
3189
  else this.stack = (/* @__PURE__ */ new Error()).stack;
@@ -3106,7 +3197,7 @@ function AxiosError(message, code, config, request, response) {
3106
3197
  this.status = response.status ? response.status : null;
3107
3198
  }
3108
3199
  }
3109
- utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
3200
+ utils_default.inherits(AxiosError$1, Error, { toJSON: function toJSON() {
3110
3201
  return {
3111
3202
  message: this.message,
3112
3203
  name: this.name,
@@ -3121,7 +3212,7 @@ utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
3121
3212
  status: this.status
3122
3213
  };
3123
3214
  } });
3124
- const prototype$1 = AxiosError.prototype;
3215
+ const prototype$1 = AxiosError$1.prototype;
3125
3216
  const descriptors = {};
3126
3217
  [
3127
3218
  "ERR_BAD_OPTION_VALUE",
@@ -3139,22 +3230,22 @@ const descriptors = {};
3139
3230
  ].forEach((code) => {
3140
3231
  descriptors[code] = { value: code };
3141
3232
  });
3142
- Object.defineProperties(AxiosError, descriptors);
3233
+ Object.defineProperties(AxiosError$1, descriptors);
3143
3234
  Object.defineProperty(prototype$1, "isAxiosError", { value: true });
3144
- AxiosError.from = (error, code, config, request, response, customProps) => {
3235
+ AxiosError$1.from = (error, code, config, request, response, customProps) => {
3145
3236
  const axiosError = Object.create(prototype$1);
3146
3237
  utils_default.toFlatObject(error, axiosError, function filter(obj) {
3147
3238
  return obj !== Error.prototype;
3148
3239
  }, (prop) => {
3149
3240
  return prop !== "isAxiosError";
3150
3241
  });
3151
- AxiosError.call(axiosError, error.message, code, config, request, response);
3242
+ AxiosError$1.call(axiosError, error.message, code, config, request, response);
3152
3243
  axiosError.cause = error;
3153
3244
  axiosError.name = error.name;
3154
3245
  customProps && Object.assign(axiosError, customProps);
3155
3246
  return axiosError;
3156
3247
  };
3157
- var AxiosError_default = AxiosError;
3248
+ var AxiosError_default = AxiosError$1;
3158
3249
 
3159
3250
  //#endregion
3160
3251
  //#region ../agent-provider/node_modules/axios/lib/helpers/null.js
@@ -3233,7 +3324,7 @@ const predicates = utils_default.toFlatObject(utils_default, {}, null, function
3233
3324
  *
3234
3325
  * @returns
3235
3326
  */
3236
- function toFormData(obj, formData, options) {
3327
+ function toFormData$1(obj, formData, options) {
3237
3328
  if (!utils_default.isObject(obj)) throw new TypeError("target must be an object");
3238
3329
  formData = formData || new (null_default || FormData)();
3239
3330
  options = utils_default.toFlatObject(options, {
@@ -3304,7 +3395,7 @@ function toFormData(obj, formData, options) {
3304
3395
  build(obj);
3305
3396
  return formData;
3306
3397
  }
3307
- var toFormData_default = toFormData;
3398
+ var toFormData_default = toFormData$1;
3308
3399
 
3309
3400
  //#endregion
3310
3401
  //#region ../agent-provider/node_modules/axios/lib/helpers/AxiosURLSearchParams.js
@@ -3821,7 +3912,7 @@ function buildAccessors(obj, header) {
3821
3912
  });
3822
3913
  });
3823
3914
  }
3824
- var AxiosHeaders = class {
3915
+ var AxiosHeaders$1 = class {
3825
3916
  constructor(headers) {
3826
3917
  headers && this.set(headers);
3827
3918
  }
@@ -3959,7 +4050,7 @@ var AxiosHeaders = class {
3959
4050
  return this;
3960
4051
  }
3961
4052
  };
3962
- AxiosHeaders.accessor([
4053
+ AxiosHeaders$1.accessor([
3963
4054
  "Content-Type",
3964
4055
  "Content-Length",
3965
4056
  "Accept",
@@ -3967,7 +4058,7 @@ AxiosHeaders.accessor([
3967
4058
  "User-Agent",
3968
4059
  "Authorization"
3969
4060
  ]);
3970
- utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
4061
+ utils_default.reduceDescriptors(AxiosHeaders$1.prototype, ({ value }, key) => {
3971
4062
  let mapped = key[0].toUpperCase() + key.slice(1);
3972
4063
  return {
3973
4064
  get: () => value,
@@ -3976,8 +4067,8 @@ utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
3976
4067
  }
3977
4068
  };
3978
4069
  });
3979
- utils_default.freezeMethods(AxiosHeaders);
3980
- var AxiosHeaders_default = AxiosHeaders;
4070
+ utils_default.freezeMethods(AxiosHeaders$1);
4071
+ var AxiosHeaders_default = AxiosHeaders$1;
3981
4072
 
3982
4073
  //#endregion
3983
4074
  //#region ../agent-provider/node_modules/axios/lib/core/transformData.js
@@ -4003,7 +4094,7 @@ function transformData(fns, response) {
4003
4094
 
4004
4095
  //#endregion
4005
4096
  //#region ../agent-provider/node_modules/axios/lib/cancel/isCancel.js
4006
- function isCancel(value) {
4097
+ function isCancel$1(value) {
4007
4098
  return !!(value && value.__CANCEL__);
4008
4099
  }
4009
4100
 
@@ -4018,12 +4109,12 @@ function isCancel(value) {
4018
4109
  *
4019
4110
  * @returns {CanceledError} The created error.
4020
4111
  */
4021
- function CanceledError(message, config, request) {
4112
+ function CanceledError$1(message, config, request) {
4022
4113
  AxiosError_default.call(this, message == null ? "canceled" : message, AxiosError_default.ERR_CANCELED, config, request);
4023
4114
  this.name = "CanceledError";
4024
4115
  }
4025
- utils_default.inherits(CanceledError, AxiosError_default, { __CANCEL__: true });
4026
- var CanceledError_default = CanceledError;
4116
+ utils_default.inherits(CanceledError$1, AxiosError_default, { __CANCEL__: true });
4117
+ var CanceledError_default = CanceledError$1;
4027
4118
 
4028
4119
  //#endregion
4029
4120
  //#region ../agent-provider/node_modules/axios/lib/core/settle.js
@@ -4250,7 +4341,7 @@ const headersToObject = (thing) => thing instanceof AxiosHeaders_default ? { ...
4250
4341
  *
4251
4342
  * @returns {Object} New object resulting from merging config2 to config1
4252
4343
  */
4253
- function mergeConfig(config1, config2) {
4344
+ function mergeConfig$1(config1, config2) {
4254
4345
  config2 = config2 || {};
4255
4346
  const config = {};
4256
4347
  function getMergedValue(target, source, prop, caseless) {
@@ -4316,7 +4407,7 @@ function mergeConfig(config1, config2) {
4316
4407
  //#endregion
4317
4408
  //#region ../agent-provider/node_modules/axios/lib/helpers/resolveConfig.js
4318
4409
  var resolveConfig_default = (config) => {
4319
- const newConfig = mergeConfig({}, config);
4410
+ const newConfig = mergeConfig$1({}, config);
4320
4411
  let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
4321
4412
  newConfig.headers = headers = AxiosHeaders_default.from(headers);
4322
4413
  newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url, newConfig.allowAbsoluteUrls), config.params, config.paramsSerializer);
@@ -4747,7 +4838,7 @@ function dispatchRequest(config) {
4747
4838
  response.headers = AxiosHeaders_default.from(response.headers);
4748
4839
  return response;
4749
4840
  }, function onAdapterRejection(reason) {
4750
- if (!isCancel(reason)) {
4841
+ if (!isCancel$1(reason)) {
4751
4842
  throwIfCancellationRequested(config);
4752
4843
  if (reason && reason.response) {
4753
4844
  reason.response.data = transformData.call(config, config.transformResponse, reason.response);
@@ -4760,7 +4851,7 @@ function dispatchRequest(config) {
4760
4851
 
4761
4852
  //#endregion
4762
4853
  //#region ../agent-provider/node_modules/axios/lib/env/data.js
4763
- const VERSION = "1.10.0";
4854
+ const VERSION$1 = "1.10.0";
4764
4855
 
4765
4856
  //#endregion
4766
4857
  //#region ../agent-provider/node_modules/axios/lib/helpers/validator.js
@@ -4789,7 +4880,7 @@ const deprecatedWarnings = {};
4789
4880
  */
4790
4881
  validators$1.transitional = function transitional(validator, version, message) {
4791
4882
  function formatMessage(opt, desc) {
4792
- return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
4883
+ return "[Axios v" + VERSION$1 + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
4793
4884
  }
4794
4885
  return (value, opt, opts) => {
4795
4886
  if (validator === false) throw new AxiosError_default(formatMessage(opt, " has been removed" + (version ? " in " + version : "")), AxiosError_default.ERR_DEPRECATED);
@@ -4846,7 +4937,7 @@ const validators = validator_default.validators;
4846
4937
  *
4847
4938
  * @return {Axios} A new instance of Axios
4848
4939
  */
4849
- var Axios = class {
4940
+ var Axios$1 = class {
4850
4941
  constructor(instanceConfig) {
4851
4942
  this.defaults = instanceConfig || {};
4852
4943
  this.interceptors = {
@@ -4883,7 +4974,7 @@ var Axios = class {
4883
4974
  config = config || {};
4884
4975
  config.url = configOrUrl;
4885
4976
  } else config = configOrUrl || {};
4886
- config = mergeConfig(this.defaults, config);
4977
+ config = mergeConfig$1(this.defaults, config);
4887
4978
  const { transitional, paramsSerializer, headers } = config;
4888
4979
  if (transitional !== void 0) validator_default.assertOptions(transitional, {
4889
4980
  silentJSONParsing: validators.transitional(validators.boolean),
@@ -4962,7 +5053,7 @@ var Axios = class {
4962
5053
  return promise;
4963
5054
  }
4964
5055
  getUri(config) {
4965
- config = mergeConfig(this.defaults, config);
5056
+ config = mergeConfig$1(this.defaults, config);
4966
5057
  return buildURL(buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls), config.params, config.paramsSerializer);
4967
5058
  }
4968
5059
  };
@@ -4972,8 +5063,8 @@ utils_default.forEach([
4972
5063
  "head",
4973
5064
  "options"
4974
5065
  ], function forEachMethodNoData(method) {
4975
- Axios.prototype[method] = function(url, config) {
4976
- return this.request(mergeConfig(config || {}, {
5066
+ Axios$1.prototype[method] = function(url, config) {
5067
+ return this.request(mergeConfig$1(config || {}, {
4977
5068
  method,
4978
5069
  url,
4979
5070
  data: (config || {}).data
@@ -4987,7 +5078,7 @@ utils_default.forEach([
4987
5078
  ], function forEachMethodWithData(method) {
4988
5079
  function generateHTTPMethod(isForm) {
4989
5080
  return function httpMethod(url, data, config) {
4990
- return this.request(mergeConfig(config || {}, {
5081
+ return this.request(mergeConfig$1(config || {}, {
4991
5082
  method,
4992
5083
  headers: isForm ? { "Content-Type": "multipart/form-data" } : {},
4993
5084
  url,
@@ -4995,10 +5086,10 @@ utils_default.forEach([
4995
5086
  }));
4996
5087
  };
4997
5088
  }
4998
- Axios.prototype[method] = generateHTTPMethod();
4999
- Axios.prototype[method + "Form"] = generateHTTPMethod(true);
5089
+ Axios$1.prototype[method] = generateHTTPMethod();
5090
+ Axios$1.prototype[method + "Form"] = generateHTTPMethod(true);
5000
5091
  });
5001
- var Axios_default = Axios;
5092
+ var Axios_default = Axios$1;
5002
5093
 
5003
5094
  //#endregion
5004
5095
  //#region ../agent-provider/node_modules/axios/lib/cancel/CancelToken.js
@@ -5009,7 +5100,7 @@ var Axios_default = Axios;
5009
5100
  *
5010
5101
  * @returns {CancelToken}
5011
5102
  */
5012
- var CancelToken = class CancelToken {
5103
+ var CancelToken$1 = class CancelToken$1 {
5013
5104
  constructor(executor) {
5014
5105
  if (typeof executor !== "function") throw new TypeError("executor must be a function.");
5015
5106
  let resolvePromise;
@@ -5081,14 +5172,14 @@ var CancelToken = class CancelToken {
5081
5172
  static source() {
5082
5173
  let cancel;
5083
5174
  return {
5084
- token: new CancelToken(function executor(c) {
5175
+ token: new CancelToken$1(function executor(c) {
5085
5176
  cancel = c;
5086
5177
  }),
5087
5178
  cancel
5088
5179
  };
5089
5180
  }
5090
5181
  };
5091
- var CancelToken_default = CancelToken;
5182
+ var CancelToken_default = CancelToken$1;
5092
5183
 
5093
5184
  //#endregion
5094
5185
  //#region ../agent-provider/node_modules/axios/lib/helpers/spread.js
@@ -5113,7 +5204,7 @@ var CancelToken_default = CancelToken;
5113
5204
  *
5114
5205
  * @returns {Function}
5115
5206
  */
5116
- function spread(callback) {
5207
+ function spread$1(callback) {
5117
5208
  return function wrap(arr) {
5118
5209
  return callback.apply(null, arr);
5119
5210
  };
@@ -5128,13 +5219,13 @@ function spread(callback) {
5128
5219
  *
5129
5220
  * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
5130
5221
  */
5131
- function isAxiosError(payload) {
5222
+ function isAxiosError$1(payload) {
5132
5223
  return utils_default.isObject(payload) && payload.isAxiosError === true;
5133
5224
  }
5134
5225
 
5135
5226
  //#endregion
5136
5227
  //#region ../agent-provider/node_modules/axios/lib/helpers/HttpStatusCode.js
5137
- const HttpStatusCode = {
5228
+ const HttpStatusCode$1 = {
5138
5229
  Continue: 100,
5139
5230
  SwitchingProtocols: 101,
5140
5231
  Processing: 102,
@@ -5199,10 +5290,10 @@ const HttpStatusCode = {
5199
5290
  NotExtended: 510,
5200
5291
  NetworkAuthenticationRequired: 511
5201
5292
  };
5202
- Object.entries(HttpStatusCode).forEach(([key, value]) => {
5203
- HttpStatusCode[value] = key;
5293
+ Object.entries(HttpStatusCode$1).forEach(([key, value]) => {
5294
+ HttpStatusCode$1[value] = key;
5204
5295
  });
5205
- var HttpStatusCode_default = HttpStatusCode;
5296
+ var HttpStatusCode_default = HttpStatusCode$1;
5206
5297
 
5207
5298
  //#endregion
5208
5299
  //#region ../agent-provider/node_modules/axios/lib/axios.js
@@ -5219,7 +5310,7 @@ function createInstance(defaultConfig) {
5219
5310
  utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true });
5220
5311
  utils_default.extend(instance, context, null, { allOwnKeys: true });
5221
5312
  instance.create = function create(instanceConfig) {
5222
- return createInstance(mergeConfig(defaultConfig, instanceConfig));
5313
+ return createInstance(mergeConfig$1(defaultConfig, instanceConfig));
5223
5314
  };
5224
5315
  return instance;
5225
5316
  }
@@ -5227,17 +5318,17 @@ const axios = createInstance(defaults_default);
5227
5318
  axios.Axios = Axios_default;
5228
5319
  axios.CanceledError = CanceledError_default;
5229
5320
  axios.CancelToken = CancelToken_default;
5230
- axios.isCancel = isCancel;
5231
- axios.VERSION = VERSION;
5321
+ axios.isCancel = isCancel$1;
5322
+ axios.VERSION = VERSION$1;
5232
5323
  axios.toFormData = toFormData_default;
5233
5324
  axios.AxiosError = AxiosError_default;
5234
5325
  axios.Cancel = axios.CanceledError;
5235
5326
  axios.all = function all(promises) {
5236
5327
  return Promise.all(promises);
5237
5328
  };
5238
- axios.spread = spread;
5239
- axios.isAxiosError = isAxiosError;
5240
- axios.mergeConfig = mergeConfig;
5329
+ axios.spread = spread$1;
5330
+ axios.isAxiosError = isAxiosError$1;
5331
+ axios.mergeConfig = mergeConfig$1;
5241
5332
  axios.AxiosHeaders = AxiosHeaders_default;
5242
5333
  axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing);
5243
5334
  axios.getAdapter = adapters_default.getAdapter;
@@ -5245,6 +5336,10 @@ axios.HttpStatusCode = HttpStatusCode_default;
5245
5336
  axios.default = axios;
5246
5337
  var axios_default = axios;
5247
5338
 
5339
+ //#endregion
5340
+ //#region ../agent-provider/node_modules/axios/index.js
5341
+ const { Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig } = axios_default;
5342
+
5248
5343
  //#endregion
5249
5344
  //#region ../agent-provider/src/http/http-service.ts
5250
5345
  /**
@@ -5253,7 +5348,7 @@ var axios_default = axios;
5253
5348
  * 特性:
5254
5349
  * - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
5255
5350
  * - 支持拦截器注册(其他模块可注入 header)
5256
- * - 统一 401/403 处理
5351
+ * - 统一 401 处理(支持自动刷新 token 并重试)
5257
5352
  * - 自动携带凭证(withCredentials)
5258
5353
  * - 类型安全
5259
5354
  *
@@ -5293,6 +5388,8 @@ var HttpService = class HttpService {
5293
5388
  */
5294
5389
  constructor(config = {}) {
5295
5390
  this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
5391
+ this.isRefreshing = false;
5392
+ this.refreshSubscribers = [];
5296
5393
  this.config = config;
5297
5394
  this.axiosInstance = axios_default.create({
5298
5395
  baseURL: config.baseURL?.replace(/\/$/, "") || "",
@@ -5333,18 +5430,54 @@ var HttpService = class HttpService {
5333
5430
  }, (error) => Promise.reject(error));
5334
5431
  }
5335
5432
  /**
5336
- * 注册默认响应拦截器(处理 401/403)
5433
+ * 注册默认响应拦截器(处理 401,支持自动重试)
5337
5434
  */
5338
5435
  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();
5436
+ this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
5437
+ const originalRequest = error.config;
5438
+ if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
5439
+ if (originalRequest.url?.includes("/console/accounts")) {
5440
+ console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
5441
+ return Promise.reject(error);
5442
+ }
5443
+ console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
5444
+ originalRequest._retry = true;
5445
+ if (this.isRefreshing) return new Promise((resolve, reject) => {
5446
+ this.refreshSubscribers.push((success) => {
5447
+ if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
5448
+ else reject(error);
5449
+ });
5450
+ });
5451
+ this.isRefreshing = true;
5452
+ try {
5453
+ await this.triggerUnauthorizedCallbacks();
5454
+ this.onRefreshSuccess();
5455
+ return this.axiosInstance.request(originalRequest);
5456
+ } catch (refreshError) {
5457
+ this.onRefreshFailure();
5458
+ return Promise.reject(error);
5459
+ } finally {
5460
+ this.isRefreshing = false;
5461
+ }
5343
5462
  }
5344
5463
  return Promise.reject(error);
5345
5464
  });
5346
5465
  }
5347
5466
  /**
5467
+ * token 刷新成功,通知所有等待的请求
5468
+ */
5469
+ onRefreshSuccess() {
5470
+ this.refreshSubscribers.forEach((callback) => callback(true));
5471
+ this.refreshSubscribers = [];
5472
+ }
5473
+ /**
5474
+ * token 刷新失败,通知所有等待的请求
5475
+ */
5476
+ onRefreshFailure() {
5477
+ this.refreshSubscribers.forEach((callback) => callback(false));
5478
+ this.refreshSubscribers = [];
5479
+ }
5480
+ /**
5348
5481
  * 注册请求拦截器
5349
5482
  * @param onFulfilled 请求成功拦截器
5350
5483
  * @param onRejected 请求失败拦截器
@@ -5421,16 +5554,18 @@ var HttpService = class HttpService {
5421
5554
  this.unauthorizedCallbacks.delete(callback);
5422
5555
  }
5423
5556
  /**
5424
- * 触发所有 401 回调
5557
+ * 触发所有 401 回调并等待完成
5558
+ * @returns Promise,等待所有回调完成
5559
+ * @throws 如果任何回调失败,则抛出错误
5425
5560
  */
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
- });
5561
+ async triggerUnauthorizedCallbacks() {
5562
+ const callbacks = Array.from(this.unauthorizedCallbacks);
5563
+ const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
5564
+ if (failedResults.length > 0) {
5565
+ const errors = failedResults.map((r) => r.reason);
5566
+ console.error("[HttpService] Some unauthorized callbacks failed:", errors);
5567
+ throw errors[0];
5568
+ }
5434
5569
  }
5435
5570
  /**
5436
5571
  * 更新 authToken
@@ -5496,6 +5631,14 @@ var HttpService = class HttpService {
5496
5631
  return (await this.axiosInstance.put(url, data, config)).data;
5497
5632
  }
5498
5633
  /**
5634
+ * 通用请求方法
5635
+ * @param config axios 请求配置
5636
+ * @returns 原始 AxiosResponse
5637
+ */
5638
+ async request(config) {
5639
+ return this.axiosInstance.request(config);
5640
+ }
5641
+ /**
5499
5642
  * 获取原始 axios 实例(用于高级场景)
5500
5643
  */
5501
5644
  getAxiosInstance() {
@@ -5689,6 +5832,109 @@ const accountService = new AccountService();
5689
5832
  */
5690
5833
  if (typeof window !== "undefined") window.__genieAccountService = accountService;
5691
5834
 
5835
+ //#endregion
5836
+ //#region ../agent-provider/src/common/utils/lru-cache.ts
5837
+ /**
5838
+ * LRU (Least Recently Used) Cache
5839
+ * 当缓存达到容量上限时,自动淘汰最久未使用的数据
5840
+ *
5841
+ * @template K - Key 类型
5842
+ * @template V - Value 类型
5843
+ */
5844
+ var LRUCache = class {
5845
+ /**
5846
+ * 创建 LRU 缓存实例
5847
+ * @param capacity - 缓存容量上限
5848
+ */
5849
+ constructor(capacity) {
5850
+ if (capacity <= 0) throw new Error("Cache capacity must be greater than 0");
5851
+ this.capacity = capacity;
5852
+ this.cache = /* @__PURE__ */ new Map();
5853
+ }
5854
+ /**
5855
+ * 获取缓存值
5856
+ * @param key - 键
5857
+ * @returns 值,如果不存在返回 undefined
5858
+ */
5859
+ get(key) {
5860
+ if (!this.cache.has(key)) return;
5861
+ const value = this.cache.get(key);
5862
+ this.cache.delete(key);
5863
+ this.cache.set(key, value);
5864
+ return value;
5865
+ }
5866
+ /**
5867
+ * 设置缓存值
5868
+ * @param key - 键
5869
+ * @param value - 值
5870
+ */
5871
+ set(key, value) {
5872
+ if (this.cache.has(key)) this.cache.delete(key);
5873
+ else if (this.cache.size >= this.capacity) {
5874
+ const firstKey = this.cache.keys().next().value;
5875
+ this.cache.delete(firstKey);
5876
+ }
5877
+ this.cache.set(key, value);
5878
+ }
5879
+ /**
5880
+ * 检查 key 是否存在
5881
+ * @param key - 键
5882
+ * @returns 是否存在
5883
+ */
5884
+ has(key) {
5885
+ return this.cache.has(key);
5886
+ }
5887
+ /**
5888
+ * 删除指定 key
5889
+ * @param key - 键
5890
+ * @returns 是否删除成功
5891
+ */
5892
+ delete(key) {
5893
+ return this.cache.delete(key);
5894
+ }
5895
+ /**
5896
+ * 清空缓存
5897
+ */
5898
+ clear() {
5899
+ this.cache.clear();
5900
+ }
5901
+ /**
5902
+ * 获取当前缓存大小
5903
+ * @returns 当前缓存的元素数量
5904
+ */
5905
+ get size() {
5906
+ return this.cache.size;
5907
+ }
5908
+ /**
5909
+ * 获取缓存容量
5910
+ * @returns 缓存容量上限
5911
+ */
5912
+ get maxSize() {
5913
+ return this.capacity;
5914
+ }
5915
+ /**
5916
+ * 获取所有 key
5917
+ * @returns key 数组
5918
+ */
5919
+ keys() {
5920
+ return Array.from(this.cache.keys());
5921
+ }
5922
+ /**
5923
+ * 获取所有 value
5924
+ * @returns value 数组
5925
+ */
5926
+ values() {
5927
+ return Array.from(this.cache.values());
5928
+ }
5929
+ /**
5930
+ * 遍历缓存
5931
+ * @param callback - 回调函数
5932
+ */
5933
+ forEach(callback) {
5934
+ this.cache.forEach((value, key) => callback(value, key));
5935
+ }
5936
+ };
5937
+
5692
5938
  //#endregion
5693
5939
  //#region ../agent-provider/src/common/utils/concurrency.ts
5694
5940
  /**
@@ -5790,20 +6036,32 @@ var CosUploadService = class {
5790
6036
  * 上传单个文件到 COS
5791
6037
  *
5792
6038
  * @param file - 要上传的文件
6039
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
5793
6040
  * @returns 上传结果,包含访问 URL 或错误信息
5794
6041
  */
5795
- async uploadFile(file) {
6042
+ async uploadFile(file, abortSignal) {
5796
6043
  const filename = file.name;
5797
6044
  this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
5798
6045
  try {
6046
+ if (abortSignal?.aborted) return {
6047
+ success: false,
6048
+ error: "Upload cancelled",
6049
+ aborted: true
6050
+ };
5799
6051
  const objectKey = this.generateObjectKey(filename);
5800
6052
  this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
5801
6053
  const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
5802
6054
  if (!presignedItem) throw new Error("No presigned URL item returned");
6055
+ if (abortSignal?.aborted) return {
6056
+ success: false,
6057
+ error: "Upload cancelled",
6058
+ aborted: true
6059
+ };
5803
6060
  const uploadResponse = await fetch(presignedItem.upload_url, {
5804
6061
  method: "PUT",
5805
6062
  body: file,
5806
- headers: { "Content-Type": file.type || "application/octet-stream" }
6063
+ headers: { "Content-Type": file.type || "application/octet-stream" },
6064
+ signal: abortSignal
5807
6065
  });
5808
6066
  if (!uploadResponse.ok) {
5809
6067
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -5817,6 +6075,11 @@ var CosUploadService = class {
5817
6075
  objectKey
5818
6076
  };
5819
6077
  } catch (error) {
6078
+ if (error instanceof Error && error.name === "AbortError") return {
6079
+ success: false,
6080
+ error: "Upload cancelled",
6081
+ aborted: true
6082
+ };
5820
6083
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
5821
6084
  this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
5822
6085
  return {
@@ -5831,14 +6094,25 @@ var CosUploadService = class {
5831
6094
  * 使用并发控制,限制同时上传的文件数量
5832
6095
  *
5833
6096
  * @param files - 要上传的文件数组
6097
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
5834
6098
  * @returns 所有文件的上传结果
5835
6099
  */
5836
- async uploadFiles(files) {
6100
+ async uploadFiles(files, abortSignal) {
5837
6101
  if (files.length === 0) return {
5838
6102
  success: true,
5839
6103
  urls: [],
5840
6104
  results: []
5841
6105
  };
6106
+ if (abortSignal?.aborted) return {
6107
+ success: false,
6108
+ error: "Upload cancelled",
6109
+ aborted: true,
6110
+ results: files.map(() => ({
6111
+ success: false,
6112
+ error: "Upload cancelled",
6113
+ aborted: true
6114
+ }))
6115
+ };
5842
6116
  this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
5843
6117
  try {
5844
6118
  const fileInfos = files.map((file) => ({
@@ -5850,6 +6124,12 @@ var CosUploadService = class {
5850
6124
  this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
5851
6125
  if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
5852
6126
  const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
6127
+ if (abortSignal?.aborted) return {
6128
+ success: false,
6129
+ error: "Upload cancelled",
6130
+ aborted: true,
6131
+ objectKey: fileInfos[index].objectKey
6132
+ };
5853
6133
  const presignedItem = presignedResponse.items[index];
5854
6134
  if (!presignedItem) return {
5855
6135
  success: false,
@@ -5860,7 +6140,8 @@ var CosUploadService = class {
5860
6140
  const uploadResponse = await fetch(presignedItem.upload_url, {
5861
6141
  method: "PUT",
5862
6142
  body: file,
5863
- headers: { "Content-Type": file.type || "application/octet-stream" }
6143
+ headers: { "Content-Type": file.type || "application/octet-stream" },
6144
+ signal: abortSignal
5864
6145
  });
5865
6146
  if (!uploadResponse.ok) {
5866
6147
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -5877,6 +6158,12 @@ var CosUploadService = class {
5877
6158
  objectKey: presignedItem.object_key
5878
6159
  };
5879
6160
  } catch (error) {
6161
+ if (error instanceof Error && error.name === "AbortError") return {
6162
+ success: false,
6163
+ error: "Upload cancelled",
6164
+ aborted: true,
6165
+ objectKey: presignedItem.object_key
6166
+ };
5880
6167
  return {
5881
6168
  success: false,
5882
6169
  error: error instanceof Error ? error.message : "Unknown error",
@@ -5924,91 +6211,6 @@ var CosUploadService = class {
5924
6211
  }
5925
6212
  };
5926
6213
 
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
6214
  //#endregion
6013
6215
  //#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-provider.ts
6014
6216
  /**
@@ -6157,8 +6359,11 @@ var CloudAgentProvider = class CloudAgentProvider {
6157
6359
  this.filesystemCache = /* @__PURE__ */ new Map();
6158
6360
  this.connectionCache = /* @__PURE__ */ new Map();
6159
6361
  this.eventListeners = /* @__PURE__ */ new Map();
6362
+ this.productConfigCache = null;
6160
6363
  this.options = options;
6161
6364
  this.logger = options.logger;
6365
+ this.marketplaceCache = new LRUCache(200);
6366
+ this.pluginCache = new LRUCache(2e3);
6162
6367
  if (options.endpoint) httpService.setBaseURL(options.endpoint);
6163
6368
  if (options.authToken) httpService.setAuthToken(options.authToken);
6164
6369
  if (options.headers && Object.keys(options.headers).length > 0) this.requestInterceptorId = httpService.registerRequestInterceptor((config) => {
@@ -6209,7 +6414,19 @@ var CloudAgentProvider = class CloudAgentProvider {
6209
6414
  const cached = this.filesystemCache.get(agentId);
6210
6415
  if (cached) return cached;
6211
6416
  const info = await this.getSandboxInfo(agentId);
6212
- const filesystem = createAgentFilesystem(await E2BFilesystem.connect(info));
6417
+ let e2bFilesystem;
6418
+ if (this.options.filesystemFactory) e2bFilesystem = await this.options.filesystemFactory(info);
6419
+ else {
6420
+ const { E2BFilesystem } = await import("./e2b-filesystem-DWj9UkV8.mjs");
6421
+ e2bFilesystem = await E2BFilesystem.connect(info);
6422
+ }
6423
+ if (e2bFilesystem.setReconnectFn) e2bFilesystem.setReconnectFn(async () => {
6424
+ this.logger?.debug(`Reconnecting E2B sandbox for agent: ${agentId}`);
6425
+ const newInfo = await this.getSandboxInfo(agentId);
6426
+ this.logger?.debug(`E2B sandbox reconnected for agent: ${agentId}`);
6427
+ return newInfo;
6428
+ });
6429
+ const filesystem = createAgentFilesystem(e2bFilesystem);
6213
6430
  this.filesystemCache.set(agentId, filesystem);
6214
6431
  this.logger?.debug(`Created filesystem for agent: ${agentId}`);
6215
6432
  return filesystem;
@@ -6277,15 +6494,9 @@ var CloudAgentProvider = class CloudAgentProvider {
6277
6494
  const url = this.buildGetUrl("/console/as/conversations/", params);
6278
6495
  const apiResponse = await httpService.get(url);
6279
6496
  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
6497
  return {
6287
- agents,
6288
- pagination
6498
+ agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
6499
+ pagination: apiResponse.data.pagination
6289
6500
  };
6290
6501
  } catch (error) {
6291
6502
  this.logger?.error("Failed to list agents:", error);
@@ -6295,15 +6506,17 @@ var CloudAgentProvider = class CloudAgentProvider {
6295
6506
  /**
6296
6507
  * Create a new conversation
6297
6508
  * POST {endpoint}/console/as/conversations
6298
- * @param params - Optional session params containing _meta with tags
6509
+ * @param params - Session params containing cwd and optional configuration
6299
6510
  */
6300
6511
  async create(params) {
6301
6512
  try {
6302
- const tagsObj = (params?._meta?.["codebuddy.ai"])?.tags;
6513
+ const { options = {} } = params;
6514
+ const codebuddyMeta = options._meta?.["codebuddy.ai"];
6515
+ const tagsObj = options.tags || codebuddyMeta?.tags;
6303
6516
  const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
6304
6517
  const createPayload = {
6305
- prompt: "",
6306
- model: "deepseek-r1",
6518
+ prompt: (options.prompt || "").slice(0, 100),
6519
+ model: options.model || "deepseek-r1",
6307
6520
  ...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
6308
6521
  };
6309
6522
  console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
@@ -6357,7 +6570,14 @@ var CloudAgentProvider = class CloudAgentProvider {
6357
6570
  } catch (error) {
6358
6571
  this.logger?.debug(`Failed to fetch conversation details for ${agentId}:`, error);
6359
6572
  }
6360
- if (this.connectionCache.get(endpoint)) this.connectionCache.delete(endpoint);
6573
+ const existingConnection = this.connectionCache.get(endpoint);
6574
+ if (existingConnection) {
6575
+ this.connectionCache.delete(endpoint);
6576
+ existingConnection.removeAllListeners();
6577
+ existingConnection.disconnect().catch((err) => {
6578
+ this.logger?.debug("Failed to disconnect old connection:", err);
6579
+ });
6580
+ }
6361
6581
  const clientCapabilities = {
6362
6582
  ...this.options.clientCapabilities,
6363
6583
  _meta: {
@@ -6477,6 +6697,35 @@ var CloudAgentProvider = class CloudAgentProvider {
6477
6697
  }
6478
6698
  }
6479
6699
  /**
6700
+ * Update conversation status by ID
6701
+ * POST {endpoint}/console/as/conversations/{agentId}
6702
+ *
6703
+ * @param agentId - Conversation ID to update
6704
+ * @param status - New status for the conversation
6705
+ * @returns PatchConversationResponse containing the updated conversation ID
6706
+ *
6707
+ * @example
6708
+ * ```typescript
6709
+ * const result = await provider.updateStatus('agent-123', 'completed');
6710
+ * console.log('Updated conversation status:', result.id);
6711
+ * ```
6712
+ */
6713
+ async updateStatus(agentId, status) {
6714
+ try {
6715
+ const body = { status };
6716
+ const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
6717
+ if (!apiResponse.data) {
6718
+ this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
6719
+ return { id: agentId };
6720
+ }
6721
+ this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
6722
+ return apiResponse.data;
6723
+ } catch (error) {
6724
+ this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
6725
+ throw error;
6726
+ }
6727
+ }
6728
+ /**
6480
6729
  * Get available models from product configuration
6481
6730
  *
6482
6731
  * GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
@@ -6507,9 +6756,13 @@ var CloudAgentProvider = class CloudAgentProvider {
6507
6756
  this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
6508
6757
  return [];
6509
6758
  }
6510
- const models = apiResponse.data.models ?? [];
6511
- this.logger?.info(`[CloudAgentProvider] Retrieved ${models.length} models from /console/enterprises API`);
6512
- return models.map((model) => ({
6759
+ this.productConfigCache = apiResponse.data;
6760
+ const productConfig = apiResponse.data;
6761
+ const allModels = productConfig.models ?? [];
6762
+ const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
6763
+ const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
6764
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
6765
+ return filteredModels.map((model) => ({
6513
6766
  id: model.id,
6514
6767
  name: model.name ?? model.id,
6515
6768
  description: model.description,
@@ -6531,6 +6784,43 @@ var CloudAgentProvider = class CloudAgentProvider {
6531
6784
  }
6532
6785
  }
6533
6786
  /**
6787
+ * 获取产品部署类型(从缓存)
6788
+ * 需要先调用 getModels() 初始化缓存
6789
+ *
6790
+ * @returns 部署类型:'SaaS' | 'Cloud-Hosted' | 'Self-Hosted',默认为 'SaaS'
6791
+ */
6792
+ getDeploymentType() {
6793
+ return this.productConfigCache?.deploymentType ?? "SaaS";
6794
+ }
6795
+ /**
6796
+ * 获取 Credit 购买引导配置(从缓存)
6797
+ * 需要先调用 getModels() 初始化缓存
6798
+ *
6799
+ * @returns Credit 购买引导配置,key 为错误码。如果后端未返回,则使用默认配置
6800
+ */
6801
+ getCreditPurchaseActions() {
6802
+ return this.productConfigCache?.config?.creditPurchaseActions ?? {
6803
+ "14018": {
6804
+ labelZh: "获取 Credits",
6805
+ labelEn: "Get credits",
6806
+ url: "https://www.codebuddy.cn/profile/plan",
6807
+ showButton: true
6808
+ },
6809
+ "6004": {
6810
+ labelZh: "升级专业版",
6811
+ labelEn: "Upgrade to Pro",
6812
+ url: "https://www.codebuddy.cn/profile/plan",
6813
+ showButton: true
6814
+ },
6815
+ "6005": {
6816
+ labelZh: "升级专业版",
6817
+ labelEn: "Upgrade to Pro",
6818
+ url: "https://www.codebuddy.cn/profile/plan",
6819
+ showButton: true
6820
+ }
6821
+ };
6822
+ }
6823
+ /**
6534
6824
  * Generate a unique request ID
6535
6825
  */
6536
6826
  generateRequestId() {
@@ -6613,7 +6903,7 @@ var CloudAgentProvider = class CloudAgentProvider {
6613
6903
  /**
6614
6904
  * Upload files to cloud storage via COS presigned URL
6615
6905
  *
6616
- * @param params - files array (File objects in browser)
6906
+ * @param params - files array (File objects in browser), optional abortSignal
6617
6907
  * @returns Response with corresponding cloud URLs
6618
6908
  */
6619
6909
  async uploadFile(params) {
@@ -6623,12 +6913,13 @@ var CloudAgentProvider = class CloudAgentProvider {
6623
6913
  success: false,
6624
6914
  error: "No valid File objects provided"
6625
6915
  };
6626
- const result = await this.cosUploadService.uploadFiles(files);
6916
+ const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
6627
6917
  return {
6628
6918
  success: result.success,
6629
6919
  urls: result.urls,
6630
6920
  expireSeconds: result.expireSeconds,
6631
- error: result.error
6921
+ error: result.error,
6922
+ aborted: result.aborted
6632
6923
  };
6633
6924
  }
6634
6925
  /**
@@ -6670,20 +6961,22 @@ var CloudAgentProvider = class CloudAgentProvider {
6670
6961
  }
6671
6962
  /**
6672
6963
  * 获取支持的场景列表
6673
- * API 端点: GET /console/as/support/scenes
6964
+ * API 端点: GET /v2/as/support/scenes (不鉴权)
6674
6965
  * 用于 Welcome 页面的 QuickActions 快捷操作
6675
6966
  *
6967
+ * @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
6676
6968
  * @returns Promise<SupportScene[]> 支持的场景列表
6677
6969
  */
6678
- async getSupportScenes() {
6970
+ async getSupportScenes(locale) {
6679
6971
  try {
6680
- const apiResponse = await httpService.get("/console/as/support/scenes");
6972
+ const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
6973
+ const apiResponse = await httpService.get(url);
6681
6974
  if (!apiResponse.data) {
6682
6975
  this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
6683
6976
  return [];
6684
6977
  }
6685
6978
  const scenes = apiResponse.data.scenes || [];
6686
- this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
6979
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
6687
6980
  return scenes;
6688
6981
  } catch (error) {
6689
6982
  this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
@@ -6699,7 +6992,9 @@ var CloudAgentProvider = class CloudAgentProvider {
6699
6992
  type: "cloud",
6700
6993
  status,
6701
6994
  createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
6702
- capabilities: this.options.clientCapabilities
6995
+ updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0,
6996
+ capabilities: this.options.clientCapabilities,
6997
+ isUserDefinedTitle: data.isUserDefinedTitle
6703
6998
  };
6704
6999
  }
6705
7000
  /**
@@ -6715,58 +7010,501 @@ var CloudAgentProvider = class CloudAgentProvider {
6715
7010
  const queryString = searchParams.toString();
6716
7011
  return queryString ? `${path}?${queryString}` : path;
6717
7012
  }
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
7013
  /**
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
7014
+ * 获取已安装插件列表
7015
+ * GET /console/as/user/plugins/installed
6751
7016
  */
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();
7017
+ async getInstalledPlugins(forceRefresh) {
7018
+ try {
7019
+ const result = ((await httpService.get("/console/as/user/plugins/installed")).data?.plugins || []).map((p) => ({
7020
+ name: p.plugin_name,
7021
+ marketplaceName: p.marketplace_name,
7022
+ status: p.enabled ? "enabled" : "disabled",
7023
+ description: p.description,
7024
+ version: p.version,
7025
+ installScope: p.scope === "local" ? "project" : p.scope,
7026
+ installedScopes: [p.scope],
7027
+ installId: p.id
7028
+ }));
7029
+ result.forEach((plugin) => {
7030
+ this.pluginCache.set(plugin.name, plugin);
7031
+ });
7032
+ return result;
7033
+ } catch (error) {
7034
+ this.logger?.error("[CloudAgentProvider] getInstalledPlugins failed:", error);
7035
+ throw error;
7036
+ }
6767
7037
  }
6768
7038
  /**
6769
- * Session ID
7039
+ * 安装插件
7040
+ * POST /console/as/user/plugins/install
7041
+ *
7042
+ * @param pluginNames - 插件名称数组
7043
+ * @param marketplaceNameOrId - 市场名称或 ID
7044
+ */
7045
+ async installPlugins(pluginNames, marketplaceNameOrId, installScope, marketplaceSource, workspacePath) {
7046
+ try {
7047
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7048
+ if (!marketplaceId) return {
7049
+ success: false,
7050
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7051
+ };
7052
+ const failed = (await Promise.allSettled(pluginNames.map((pluginName) => httpService.post("/console/as/user/plugins/install", {
7053
+ plugin_name: pluginName,
7054
+ marketplace_id: marketplaceId,
7055
+ version: "latest"
7056
+ })))).filter((r) => r.status === "rejected");
7057
+ if (failed.length > 0) {
7058
+ const errors = failed.map((r) => r.reason?.message || "Unknown error");
7059
+ return {
7060
+ success: false,
7061
+ error: `安装失败 ${failed.length} 个插件: ${errors.join(", ")}`
7062
+ };
7063
+ }
7064
+ return { success: true };
7065
+ } catch (error) {
7066
+ return {
7067
+ success: false,
7068
+ error: this.extractErrorMessage(error)
7069
+ };
7070
+ }
7071
+ }
7072
+ /**
7073
+ * 卸载插件
7074
+ * POST /console/as/user/plugins/installed/:id/uninstall
7075
+ *
7076
+ * 完整链路:
7077
+ * CloudAgentProvider.uninstallPlugin()
7078
+ * -> HTTP POST /console/as/user/plugins/installed/:id/uninstall
7079
+ * -> agentserver: PluginInstallService.Uninstall()
7080
+ * -> 软删除 DB + 异步同步到活跃沙箱
7081
+ *
7082
+ * @param pluginName - 插件名称
7083
+ * @param marketplaceName - 市场名称(用于标识唯一插件)
7084
+ * @param scope - 卸载范围 ('user' | 'project' | 'project-local')
7085
+ */
7086
+ async uninstallPlugin(pluginName, marketplaceName, scope) {
7087
+ try {
7088
+ const installId = await this.findPluginInstallId(pluginName);
7089
+ if (!installId) return {
7090
+ success: false,
7091
+ error: `Plugin not found or not installed: ${pluginName}`
7092
+ };
7093
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/uninstall`);
7094
+ return { success: true };
7095
+ } catch (error) {
7096
+ return {
7097
+ success: false,
7098
+ error: this.extractErrorMessage(error)
7099
+ };
7100
+ }
7101
+ }
7102
+ /**
7103
+ * 获取插件市场列表
7104
+ * GET /console/as/marketplace/sources
7105
+ */
7106
+ async getPluginMarketplaces(forceRefresh) {
7107
+ try {
7108
+ const result = ((await httpService.get("/console/as/marketplace/sources")).data?.sources || []).map((src) => ({
7109
+ id: src.id,
7110
+ name: src.name,
7111
+ type: this.mapSourceType(src.source_type),
7112
+ source: { url: src.url },
7113
+ description: src.name,
7114
+ isBuiltin: src.is_default
7115
+ }));
7116
+ result.forEach((m) => {
7117
+ const marketplaceInfo = {
7118
+ id: m.id,
7119
+ name: m.name
7120
+ };
7121
+ this.marketplaceCache.set(m.name, marketplaceInfo);
7122
+ this.marketplaceCache.set(m.id, marketplaceInfo);
7123
+ });
7124
+ return result;
7125
+ } catch (error) {
7126
+ this.logger?.error("[CloudAgentProvider] getPluginMarketplaces failed:", error);
7127
+ throw error;
7128
+ }
7129
+ }
7130
+ /**
7131
+ * 获取市场下的插件列表
7132
+ * - 有 searchText: GET /console/as/marketplace/plugins/search (跨所有市场搜索)
7133
+ * - 无 searchText: GET /console/as/marketplace/plugins (指定市场)
7134
+ *
7135
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
7136
+ * @param forceRefresh - 是否强制刷新
7137
+ * @param searchText - 搜索关键字(如果提供,则跨所有市场搜索)
7138
+ */
7139
+ async getMarketplacePlugins(marketplaceNameOrId, forceRefresh, searchText) {
7140
+ try {
7141
+ if (searchText) return ((await httpService.get("/console/as/marketplace/plugins/search", { params: {
7142
+ q: searchText,
7143
+ page: 1,
7144
+ page_size: 100
7145
+ } })).data?.plugins || []).map((p) => this.mapPluginData(p));
7146
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
7147
+ if (!sourceId) {
7148
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
7149
+ return [];
7150
+ }
7151
+ const params = {
7152
+ source_id: sourceId,
7153
+ page: 1,
7154
+ page_size: 100
7155
+ };
7156
+ return ((await httpService.get("/console/as/marketplace/plugins", { params })).data?.plugins || []).map((p) => this.mapPluginData(p));
7157
+ } catch (error) {
7158
+ this.logger?.error("[CloudAgentProvider] getMarketplacePlugins failed:", error);
7159
+ throw error;
7160
+ }
7161
+ }
7162
+ /**
7163
+ * 获取插件详情
7164
+ * GET /console/as/marketplace/plugins/:name/detail
7165
+ *
7166
+ * @param pluginName - 插件名称
7167
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
7168
+ */
7169
+ async getPluginDetail(pluginName, marketplaceNameOrId) {
7170
+ try {
7171
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
7172
+ if (!sourceId) {
7173
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
7174
+ return null;
7175
+ }
7176
+ const p = (await httpService.get(`/console/as/marketplace/plugins/${pluginName}/detail`, { params: { source_id: sourceId } })).data?.plugin;
7177
+ if (!p) return null;
7178
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
7179
+ const tags = p.tags ? JSON.parse(p.tags) : [];
7180
+ return {
7181
+ name: p.name,
7182
+ marketplaceName: p.marketplace_name,
7183
+ description: p.description,
7184
+ version: p.version,
7185
+ iconUrl: p.icon_url,
7186
+ tags,
7187
+ installed: p.installed,
7188
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
7189
+ readme: p.readme,
7190
+ author: p.author,
7191
+ homepage: p.homepage,
7192
+ repositoryUrl: p.repository_url,
7193
+ license: p.license,
7194
+ ...capabilities
7195
+ };
7196
+ } catch (error) {
7197
+ this.logger?.error("[CloudAgentProvider] getPluginDetail failed:", error);
7198
+ throw error;
7199
+ }
7200
+ }
7201
+ /**
7202
+ * 添加插件市场
7203
+ * POST /console/as/marketplace/sources
7204
+ */
7205
+ async addPluginMarketplace(sourceUrl, name) {
7206
+ try {
7207
+ const body = {
7208
+ source_type: sourceUrl.startsWith("http") ? "url" : "github",
7209
+ url: sourceUrl,
7210
+ name: name || sourceUrl
7211
+ };
7212
+ const sourceData = (await httpService.post("/console/as/marketplace/sources", body)).data?.source;
7213
+ if (!sourceData) throw new Error("Invalid response from server");
7214
+ const marketplaceInfo = {
7215
+ id: sourceData.id,
7216
+ name: sourceData.name
7217
+ };
7218
+ this.marketplaceCache.set(sourceData.name, marketplaceInfo);
7219
+ this.marketplaceCache.set(sourceData.id, marketplaceInfo);
7220
+ return {
7221
+ success: true,
7222
+ marketplace: {
7223
+ id: sourceData.id,
7224
+ name: sourceData.name,
7225
+ type: this.mapSourceType(sourceData.source_type),
7226
+ source: { url: sourceData.url },
7227
+ isBuiltin: sourceData.is_default
7228
+ }
7229
+ };
7230
+ } catch (error) {
7231
+ return {
7232
+ success: false,
7233
+ error: this.extractErrorMessage(error)
7234
+ };
7235
+ }
7236
+ }
7237
+ /**
7238
+ * 删除插件市场
7239
+ * POST /console/as/marketplace/sources/:id/delete
7240
+ */
7241
+ async removePluginMarketplace(marketplaceNameOrId) {
7242
+ try {
7243
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7244
+ if (!marketplaceId) return {
7245
+ success: false,
7246
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7247
+ };
7248
+ await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/delete`, {});
7249
+ return { success: true };
7250
+ } catch (error) {
7251
+ return {
7252
+ success: false,
7253
+ error: this.extractErrorMessage(error)
7254
+ };
7255
+ }
7256
+ }
7257
+ /**
7258
+ * 刷新插件市场
7259
+ * POST /console/as/marketplace/sources/:id/check-updates
7260
+ */
7261
+ async refreshPluginMarketplace(marketplaceNameOrId) {
7262
+ try {
7263
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7264
+ if (!marketplaceId) return {
7265
+ success: false,
7266
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7267
+ };
7268
+ return {
7269
+ success: true,
7270
+ plugins: (await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/check-updates`, {})).data?.updated_plugins || []
7271
+ };
7272
+ } catch (error) {
7273
+ return {
7274
+ success: false,
7275
+ error: this.extractErrorMessage(error)
7276
+ };
7277
+ }
7278
+ }
7279
+ /**
7280
+ * 批量切换插件启用/禁用状态
7281
+ * POST /console/as/user/plugins/installed/:id/toggle
7282
+ */
7283
+ async batchTogglePlugins(request) {
7284
+ try {
7285
+ const results = await Promise.allSettled(request.items.map(async (item) => {
7286
+ const installId = await this.findPluginInstallId(item.pluginName);
7287
+ if (!installId) throw new Error(`Plugin not found or not installed: ${item.pluginName}`);
7288
+ const enabled = item.operation === "enable";
7289
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/toggle`, { enabled });
7290
+ return item;
7291
+ }));
7292
+ const succeededPlugins = [];
7293
+ const failedPlugins = [];
7294
+ results.forEach((r, i) => {
7295
+ const item = request.items[i];
7296
+ if (r.status === "fulfilled") succeededPlugins.push(item);
7297
+ else failedPlugins.push({
7298
+ ...item,
7299
+ error: r.reason?.message || "Unknown error"
7300
+ });
7301
+ });
7302
+ return {
7303
+ success: failedPlugins.length === 0,
7304
+ succeededPlugins,
7305
+ failedPlugins
7306
+ };
7307
+ } catch (error) {
7308
+ return {
7309
+ success: false,
7310
+ succeededPlugins: [],
7311
+ failedPlugins: request.items.map((item) => ({
7312
+ ...item,
7313
+ error: this.extractErrorMessage(error)
7314
+ }))
7315
+ };
7316
+ }
7317
+ }
7318
+ /**
7319
+ * 将后端插件数据映射为前端格式
7320
+ */
7321
+ mapPluginData(p) {
7322
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
7323
+ let tags = [];
7324
+ if (p.tags) {
7325
+ if (Array.isArray(p.tags)) tags = p.tags;
7326
+ else if (typeof p.tags === "string") try {
7327
+ const parsed = JSON.parse(p.tags);
7328
+ tags = Array.isArray(parsed) ? parsed : [parsed];
7329
+ } catch {
7330
+ tags = p.tags.split(",").map((t) => t.trim()).filter((t) => t);
7331
+ }
7332
+ }
7333
+ return {
7334
+ name: p.name,
7335
+ marketplaceName: p.marketplace_name,
7336
+ description: p.description,
7337
+ version: p.version,
7338
+ iconUrl: p.icon_url,
7339
+ tags,
7340
+ installed: p.installed,
7341
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
7342
+ installedScopes: p.installed ? [p.installed_scope] : [],
7343
+ ...capabilities
7344
+ };
7345
+ }
7346
+ /**
7347
+ * 从缓存中查找插件的 install_id
7348
+ * 如果缓存未命中,则调用 API 获取并缓存
7349
+ *
7350
+ * @param pluginName - 插件名称
7351
+ * @returns install_id 或 null
7352
+ */
7353
+ async findPluginInstallId(pluginName) {
7354
+ const cached = this.pluginCache.get(pluginName);
7355
+ if (cached) return cached.installId;
7356
+ await this.getInstalledPlugins();
7357
+ return this.pluginCache.get(pluginName)?.installId || null;
7358
+ }
7359
+ /**
7360
+ * 从缓存中查找 marketplace ID
7361
+ * 如果缓存未命中,则调用 API 获取并缓存
7362
+ */
7363
+ async findMarketplaceId(nameOrId) {
7364
+ const cached = this.marketplaceCache.get(nameOrId);
7365
+ if (cached) return cached.id;
7366
+ await this.getPluginMarketplaces();
7367
+ return this.marketplaceCache.get(nameOrId)?.id || null;
7368
+ }
7369
+ /**
7370
+ * 提取 API 错误信息
7371
+ * 从 AxiosError 中提取详细的错误信息,包括 HTTP 状态码、错误码和错误消息
7372
+ */
7373
+ extractErrorMessage(error) {
7374
+ if (error instanceof AxiosError) {
7375
+ const status = error.response?.status;
7376
+ const apiResponse = error.response?.data;
7377
+ const parts = [];
7378
+ if (status) parts.push(`HTTP ${status}`);
7379
+ if (apiResponse?.code) parts.push(`Code ${apiResponse.code}`);
7380
+ if (apiResponse?.msg) parts.push(apiResponse.msg);
7381
+ else if (error.message) parts.push(error.message);
7382
+ const errorMessage = parts.join(" - ");
7383
+ this.logger?.error("[CloudAgentProvider] API Error:", {
7384
+ status,
7385
+ code: apiResponse?.code,
7386
+ msg: apiResponse?.msg,
7387
+ requestId: apiResponse?.requestId,
7388
+ url: error.config?.url,
7389
+ method: error.config?.method
7390
+ });
7391
+ return errorMessage;
7392
+ }
7393
+ if (error instanceof Error) return error.message;
7394
+ return "Unknown error";
7395
+ }
7396
+ /**
7397
+ * 映射后端 source_type 到前端类型
7398
+ */
7399
+ mapSourceType(sourceType) {
7400
+ switch (sourceType) {
7401
+ case "github": return "github";
7402
+ case "official": return "custom";
7403
+ default: return "custom";
7404
+ }
7405
+ }
7406
+ /**
7407
+ * 解析 capabilities JSON 字符串
7408
+ */
7409
+ parseCapabilities(capabilitiesStr) {
7410
+ try {
7411
+ const cap = JSON.parse(capabilitiesStr);
7412
+ return {
7413
+ commands: cap.commands,
7414
+ skills: cap.skills,
7415
+ mcpServers: cap.mcp,
7416
+ agents: cap.agents,
7417
+ hooks: cap.hooks,
7418
+ rules: cap.rules
7419
+ };
7420
+ } catch {
7421
+ return {};
7422
+ }
7423
+ }
7424
+ /**
7425
+ * 上报 telemetry 事件(Cloud 模式)
7426
+ * 通过 HTTP POST 发送到 /v2/report
7427
+ * 注入用户信息和浏览器环境等公共字段
7428
+ */
7429
+ async reportTelemetry(eventName, payload) {
7430
+ try {
7431
+ const account = accountService.getAccount();
7432
+ const commonFields = {};
7433
+ if (account) {
7434
+ commonFields.userId = account.uid;
7435
+ commonFields.userNickname = account.nickname;
7436
+ if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
7437
+ if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
7438
+ }
7439
+ if (typeof navigator !== "undefined") {
7440
+ commonFields.userAgent = navigator.userAgent;
7441
+ commonFields.os = navigator.platform;
7442
+ }
7443
+ const events = [{
7444
+ eventCode: eventName,
7445
+ timestamp: Date.now(),
7446
+ reportDelay: 0,
7447
+ ...commonFields,
7448
+ ...payload
7449
+ }];
7450
+ await httpService.post("/v2/report", events);
7451
+ } catch (error) {
7452
+ this.logger?.warn("reportTelemetry() failed:", error);
7453
+ }
7454
+ }
7455
+ };
7456
+
7457
+ //#endregion
7458
+ //#region ../agent-provider/src/common/client/session.ts
7459
+ /**
7460
+ * ActiveSessionImpl - Implements the ActiveSession interface
7461
+ *
7462
+ * This class wraps an AgentConnection and provides the session-centric API.
7463
+ * It is created by SessionManager when creating or loading sessions.
7464
+ *
7465
+ * @example
7466
+ * ```typescript
7467
+ * // Created by client.sessions.new() or client.sessions.load()
7468
+ * const session = await client.sessions.new({ cwd: '/workspace' });
7469
+ *
7470
+ * // Access agent state
7471
+ * console.log(session.agentState.status);
7472
+ *
7473
+ * // Send prompt
7474
+ * const response = await session.prompts.send({ content: 'Hello!' });
7475
+ *
7476
+ * // Cleanup
7477
+ * session.disconnect();
7478
+ * ```
7479
+ */
7480
+ var ActiveSessionImpl = class {
7481
+ /**
7482
+ * Create an ActiveSessionImpl instance
7483
+ *
7484
+ * @param sessionId - Session ID
7485
+ * @param agentId - Agent ID
7486
+ * @param connection - Already connected AgentConnection
7487
+ * @param options - Additional options
7488
+ */
7489
+ constructor(sessionId, agentId, connection, options = {}) {
7490
+ this._availableCommands = [];
7491
+ this.listeners = /* @__PURE__ */ new Map();
7492
+ this.onceListeners = /* @__PURE__ */ new Map();
7493
+ this.connectionListeners = [];
7494
+ this._id = sessionId;
7495
+ this._agentId = agentId;
7496
+ this.connection = connection;
7497
+ this.logger = options.logger;
7498
+ this._getFilesystem = options.getFilesystem;
7499
+ this._connectionInfo = options.connectionInfo;
7500
+ this.setupConnectionEvents(connection);
7501
+ this.agent = this.createAgentOperations();
7502
+ this.prompts = this.createPromptsResource();
7503
+ this.artifacts = this.createArtifactsResource();
7504
+ this.files = this.createFilesResource();
7505
+ }
7506
+ /**
7507
+ * Session ID
6770
7508
  */
6771
7509
  get id() {
6772
7510
  return this._id;
@@ -6778,6 +7516,18 @@ var ActiveSessionImpl = class {
6778
7516
  return this._agentId;
6779
7517
  }
6780
7518
  /**
7519
+ * Actual workspace path (set from newSession response _meta)
7520
+ */
7521
+ get cwd() {
7522
+ return this._cwd;
7523
+ }
7524
+ /**
7525
+ * Set actual workspace path (called by SessionManager after createSession)
7526
+ */
7527
+ setCwd(cwd) {
7528
+ this._cwd = cwd;
7529
+ }
7530
+ /**
6781
7531
  * Agent state (live connection state)
6782
7532
  * Returns LocalAgentState or CloudAgentState based on transport type
6783
7533
  */
@@ -6971,10 +7721,10 @@ var ActiveSessionImpl = class {
6971
7721
  return this.connection.cancelQuestion(toolCallId, reason);
6972
7722
  }
6973
7723
  /**
6974
- * Callback for tool operations (skip or cancel)
7724
+ * Callback for tool operations (approve / skip / cancel)
6975
7725
  * @param toolCallId Tool call ID
6976
7726
  * @param toolName Tool name
6977
- * @param action Action to perform ('skip' or 'cancel')
7727
+ * @param action Action to perform ('approve' / 'skip' / 'cancel')
6978
7728
  */
6979
7729
  async toolCallback(toolCallId, toolName, action) {
6980
7730
  return await this.getConnectionOrThrow().toolCallback(this._id, toolCallId, toolName, action);
@@ -7018,6 +7768,7 @@ var ActiveSessionImpl = class {
7018
7768
  * ```
7019
7769
  */
7020
7770
  async setSessionModel(modelId) {
7771
+ this._currentModelId = modelId;
7021
7772
  await this.getConnectionOrThrow().setSessionModel(this._id, modelId);
7022
7773
  }
7023
7774
  /**
@@ -7095,11 +7846,23 @@ var ActiveSessionImpl = class {
7095
7846
  * Disconnect from the session/agent
7096
7847
  */
7097
7848
  disconnect() {
7849
+ this.removeConnectionListeners();
7098
7850
  this.connection.disconnect();
7099
7851
  this.removeAllListeners();
7100
7852
  this.logger?.info(`Session ${this._id}: Disconnected`);
7101
7853
  }
7102
7854
  /**
7855
+ * Detach the session from connection events without disconnecting the connection.
7856
+ * This should be called when the session is being replaced but the connection is shared.
7857
+ * Unlike disconnect(), this only removes event listeners without closing the connection.
7858
+ */
7859
+ detach() {
7860
+ this.logger?.info(`Session ${this._id}: Detaching from connection events`);
7861
+ this.removeConnectionListeners();
7862
+ this.removeAllListeners();
7863
+ this.logger?.info(`Session ${this._id}: Detached successfully`);
7864
+ }
7865
+ /**
7103
7866
  * Symbol.dispose for 'using' keyword support
7104
7867
  * Automatically disconnects and cleans up when session goes out of scope
7105
7868
  *
@@ -7118,60 +7881,85 @@ var ActiveSessionImpl = class {
7118
7881
  if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
7119
7882
  return this.connection;
7120
7883
  }
7884
+ /**
7885
+ * 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
7886
+ */
7887
+ addConnectionListener(connection, event, listener) {
7888
+ connection.on(event, listener);
7889
+ this.connectionListeners.push({
7890
+ event,
7891
+ listener
7892
+ });
7893
+ }
7894
+ /**
7895
+ * 从 connection 上移除所有本 session 注册的 listener
7896
+ */
7897
+ removeConnectionListeners() {
7898
+ for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
7899
+ this.connectionListeners = [];
7900
+ }
7121
7901
  setupConnectionEvents(connection) {
7122
- connection.on("connected", () => {
7902
+ this.addConnectionListener(connection, "connected", () => {
7123
7903
  this.emit("connected", void 0);
7124
7904
  });
7125
- connection.on("disconnected", () => {
7905
+ this.addConnectionListener(connection, "disconnected", () => {
7126
7906
  this.emit("disconnected", void 0);
7127
7907
  });
7128
- connection.on("error", (error) => {
7908
+ this.addConnectionListener(connection, "error", (error) => {
7129
7909
  this.emit("error", error);
7130
7910
  });
7131
- connection.on("sessionUpdate", (update) => {
7911
+ this.addConnectionListener(connection, "sessionUpdate", (update) => {
7912
+ const notificationSessionId = update?.sessionId;
7913
+ if (notificationSessionId && notificationSessionId !== this._id) {
7914
+ console.log(`[RT-DEBUG][AgentMgr:Session] sessionUpdate SKIPPED: notifSessionId mismatch, notif=${notificationSessionId?.substring(0, 8)}, my=${this._id?.substring(0, 8)}`);
7915
+ return;
7916
+ }
7132
7917
  this.emit("sessionUpdate", update);
7133
7918
  });
7134
- connection.on("artifactCreated", (artifact) => {
7135
- console.log("[Session] Forwarding artifactCreated:", {
7136
- artifactUri: artifact.uri,
7137
- artifactType: artifact.type
7138
- });
7919
+ this.addConnectionListener(connection, "artifactCreated", (artifact) => {
7920
+ if (!this.shouldForwardArtifact(artifact)) return;
7139
7921
  this.emit("artifactCreated", artifact);
7140
7922
  });
7141
- connection.on("artifactUpdated", (artifact) => {
7142
- console.log("[Session] Forwarding artifactUpdated:", {
7143
- artifactUri: artifact.uri,
7144
- artifactType: artifact.type
7145
- });
7923
+ this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
7924
+ if (!this.shouldForwardArtifact(artifact)) return;
7146
7925
  this.emit("artifactUpdated", artifact);
7147
7926
  });
7148
- connection.on("artifactDeleted", (artifact) => {
7149
- console.log("[Session] Forwarding artifactDeleted:", { artifactUri: artifact.uri });
7927
+ this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
7928
+ if (!this.shouldForwardArtifact(artifact)) return;
7150
7929
  this.emit("artifactDeleted", artifact);
7151
7930
  });
7152
- connection.on("permissionRequest", (request) => {
7931
+ this.addConnectionListener(connection, "permissionRequest", (request) => {
7153
7932
  this.emit("permissionRequest", request);
7154
7933
  });
7155
- connection.on("questionRequest", (request) => {
7934
+ this.addConnectionListener(connection, "questionRequest", (request) => {
7156
7935
  this.emit("questionRequest", request);
7157
7936
  });
7158
- connection.on("questionCancelled", () => {
7937
+ this.addConnectionListener(connection, "questionCancelled", () => {
7159
7938
  this.prompts.cancel();
7160
7939
  });
7161
- connection.on("usageUpdate", (usage) => {
7940
+ this.addConnectionListener(connection, "usageUpdate", (usage) => {
7162
7941
  this.emit("usageUpdate", usage);
7163
7942
  });
7164
- connection.on("checkpointCreated", (checkpoint) => {
7943
+ this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
7944
+ const originSessionId = checkpoint.__sessionId;
7945
+ if (originSessionId && originSessionId !== this._id) return;
7165
7946
  this.emit("checkpointCreated", checkpoint);
7166
7947
  });
7167
- connection.on("checkpointUpdated", (checkpoint) => {
7948
+ this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
7949
+ const originSessionId = checkpoint.__sessionId;
7950
+ if (originSessionId && originSessionId !== this._id) return;
7168
7951
  this.emit("checkpointUpdated", checkpoint);
7169
7952
  });
7170
- connection.on("command", (command) => {
7171
- console.log("[Session] Forwarding command:", {
7172
- action: command.action,
7173
- paramsKeys: command.params ? Object.keys(command.params) : []
7174
- });
7953
+ this.addConnectionListener(connection, "command", (command) => {
7954
+ const originSessionId = command.__sessionId;
7955
+ if (originSessionId && originSessionId !== this._id) {
7956
+ console.log("[Session] Command not forwarded:", {
7957
+ command,
7958
+ originSessionId,
7959
+ sessionId: this._id
7960
+ });
7961
+ return;
7962
+ }
7175
7963
  this.emit("command", command);
7176
7964
  });
7177
7965
  }
@@ -7181,6 +7969,15 @@ var ActiveSessionImpl = class {
7181
7969
  _meta: response._meta ?? void 0
7182
7970
  };
7183
7971
  }
7972
+ /**
7973
+ * 判断 artifact 是否应该转发给当前 session
7974
+ * 所有类型的 artifact 都按 __sessionId 严格隔离
7975
+ */
7976
+ shouldForwardArtifact(artifact) {
7977
+ const originSessionId = artifact.__sessionId;
7978
+ if (!originSessionId || originSessionId !== this._id) return false;
7979
+ return true;
7980
+ }
7184
7981
  };
7185
7982
 
7186
7983
  //#endregion
@@ -7213,6 +8010,7 @@ var ActiveSessionImpl = class {
7213
8010
  */
7214
8011
  var SessionManager = class {
7215
8012
  constructor(options) {
8013
+ this.pendingConnections = /* @__PURE__ */ new Map();
7216
8014
  this.provider = options.provider;
7217
8015
  this.logger = options.logger;
7218
8016
  }
@@ -7233,9 +8031,11 @@ var SessionManager = class {
7233
8031
  name: agent.name,
7234
8032
  status: agent.status,
7235
8033
  createdAt: agent.createdAt,
8034
+ updatedAt: agent.updatedAt,
7236
8035
  lastActivityAt: agent.updatedAt,
7237
8036
  cwd: agent.type === "local" ? agent.cwd : void 0,
7238
- isPlayground: agent.isPlayground
8037
+ isPlayground: agent.isPlayground,
8038
+ isUserDefinedTitle: agent.isUserDefinedTitle
7239
8039
  }));
7240
8040
  console.log("[SessionManager] Returning sessions:", {
7241
8041
  count: sessions.length,
@@ -7262,14 +8062,92 @@ var SessionManager = class {
7262
8062
  if (this.provider.create) {
7263
8063
  agentId = await this.provider.create(params);
7264
8064
  this.logger?.debug(`Created new agent: ${agentId}`);
8065
+ if (params.options?.onSessionPrepared) {
8066
+ const initialPrompt = params.options?.prompt;
8067
+ const initialTitle = initialPrompt?.slice(0, 50) || "";
8068
+ params.options.onSessionPrepared({
8069
+ id: agentId,
8070
+ agentId,
8071
+ name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
8072
+ status: "connecting",
8073
+ cwd: params.cwd || "",
8074
+ createdAt: /* @__PURE__ */ new Date()
8075
+ });
8076
+ this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
8077
+ }
7265
8078
  } else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
7266
8079
  const connection = await this.provider.connect(agentId);
7267
8080
  this.logger?.debug(`Connected to agent: ${agentId}`);
8081
+ let response;
8082
+ try {
8083
+ response = await connection.createSession({
8084
+ _meta: params.options?._meta,
8085
+ cwd: params.cwd,
8086
+ mcpServers: params.options?.mcpServers
8087
+ });
8088
+ } catch (err) {
8089
+ this.pendingConnections.set(agentId, connection);
8090
+ this.logger?.debug(`Cached pending connection for agent: ${agentId}`);
8091
+ if (err instanceof SessionError) throw new SessionError(err.message, err.sessionId, agentId, err.cause instanceof Error ? err.cause : void 0);
8092
+ const cause = err instanceof Error ? err : new Error(String(err));
8093
+ throw new SessionError(`Failed to create session: ${cause.message}`, void 0, agentId, cause);
8094
+ }
8095
+ return this.buildActiveSession(response, agentId, connection, params);
8096
+ }
8097
+ /**
8098
+ * Retry creating a session after initial session/new request failed.
8099
+ *
8100
+ * Use this when `sessions.create()` fails at the `session/new` step
8101
+ * (after agent creation and connection establishment succeeded).
8102
+ * The caller retrieves `agentId` from the thrown `SessionError.agentId`,
8103
+ * then calls this method to re-attempt only the `session/new` request
8104
+ * while reusing the already-initialized connection (no re-connect).
8105
+ *
8106
+ * @experimental This API is subject to change
8107
+ *
8108
+ * @param agentId - Agent ID from the failed SessionError
8109
+ * @param params - Original create session params (cwd, mcpServers, etc.)
8110
+ * @returns ActiveSession on success
8111
+ * @throws SessionError if session/new fails again
8112
+ *
8113
+ * @example
8114
+ * ```typescript
8115
+ * try {
8116
+ * const session = await client.sessions.create({ cwd: '/workspace' });
8117
+ * } catch (err) {
8118
+ * if (err instanceof SessionError && err.agentId) {
8119
+ * // Retry with custom logic
8120
+ * for (let i = 0; i < 3; i++) {
8121
+ * try {
8122
+ * const session = await client.sessions.retryNewSession(err.agentId, { cwd: '/workspace' });
8123
+ * break; // success
8124
+ * } catch (retryErr) {
8125
+ * await new Promise(r => setTimeout(r, 1000 * (i + 1)));
8126
+ * }
8127
+ * }
8128
+ * }
8129
+ * }
8130
+ * ```
8131
+ */
8132
+ async retryNewSession(agentId, params) {
8133
+ this.logger?.info(`Retrying session/new for agent: ${agentId}`);
8134
+ const connection = this.pendingConnections.get(agentId);
8135
+ if (!connection) throw new SessionError(`No pending connection found for agent: ${agentId}. retryNewSession() can only be called after a failed sessions.create().`, void 0, agentId);
8136
+ this.logger?.debug(`Reusing cached connection for agent: ${agentId}`);
7268
8137
  const response = await connection.createSession({
7269
- _meta: params._meta,
8138
+ _meta: params.options?._meta,
7270
8139
  cwd: params.cwd,
7271
- mcpServers: params.mcpServers
8140
+ mcpServers: params.options?.mcpServers
7272
8141
  });
8142
+ this.pendingConnections.delete(agentId);
8143
+ this.logger?.debug(`Cleared pending connection for agent: ${agentId}`);
8144
+ return this.buildActiveSession(response, agentId, connection, params);
8145
+ }
8146
+ /**
8147
+ * Build an ActiveSession from a NewSessionResponse.
8148
+ * Shared by createSession() and retryNewSession().
8149
+ */
8150
+ buildActiveSession(response, agentId, connection, params) {
7273
8151
  if (this.provider.registerSession) {
7274
8152
  this.provider.registerSession(response.sessionId, agentId);
7275
8153
  this.logger?.debug(`Registered session mapping: ${response.sessionId} → ${agentId}`);
@@ -7283,6 +8161,8 @@ var SessionManager = class {
7283
8161
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
7284
8162
  const availableModels = this.extractAvailableModels(response);
7285
8163
  if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
8164
+ const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
8165
+ if (responseCwd) session.setCwd(responseCwd);
7286
8166
  this.logger?.info(`Session created: ${response.sessionId}`);
7287
8167
  return session;
7288
8168
  }
@@ -7411,6 +8291,7 @@ var AgentClient = class {
7411
8291
  return {
7412
8292
  list: async (options) => this.sessionManager.listSessions(options),
7413
8293
  create: async (params) => this.sessionManager.createSession(params),
8294
+ retryNewSession: async (agentId, params) => this.sessionManager.retryNewSession(agentId, params),
7414
8295
  load: async (params) => {
7415
8296
  console.log("[AgentClient] sessions.load called:", params.sessionId);
7416
8297
  return this.sessionManager.loadSession(params);
@@ -7449,8 +8330,28 @@ var AgentClient = class {
7449
8330
  throw error;
7450
8331
  }
7451
8332
  },
7452
- move: async (sessionId) => {
7453
- this.logger?.debug("AgentClient.sessions.move called", { sessionId });
8333
+ updateStatus: async (sessionId, status) => {
8334
+ this.logger?.debug("AgentClient.sessions.updateStatus called", {
8335
+ sessionId,
8336
+ status
8337
+ });
8338
+ try {
8339
+ if (this.provider.updateStatus) {
8340
+ const result = await this.provider.updateStatus(sessionId, status);
8341
+ this.logger?.info("Session status updated successfully", {
8342
+ sessionId,
8343
+ status
8344
+ });
8345
+ return result;
8346
+ }
8347
+ throw new Error("Provider does not support updateStatus method");
8348
+ } catch (error) {
8349
+ this.logger?.error("Failed to update session status", error);
8350
+ throw error;
8351
+ }
8352
+ },
8353
+ move: async (sessionId) => {
8354
+ this.logger?.debug("AgentClient.sessions.move called", { sessionId });
7454
8355
  try {
7455
8356
  if (this.provider.move) {
7456
8357
  const result = await this.provider.move(sessionId);
@@ -7481,6 +8382,16 @@ var AgentClient = class {
7481
8382
  };
7482
8383
  }
7483
8384
  },
8385
+ requestYieldAfterCurrentStep: async (sessionId) => {
8386
+ try {
8387
+ if (this.provider?.requestYieldAfterCurrentStep) return await this.provider.requestYieldAfterCurrentStep(sessionId);
8388
+ this.logger?.warn("Provider does not support requestYieldAfterCurrentStep");
8389
+ return false;
8390
+ } catch (error) {
8391
+ this.logger?.error("Failed to request yield after current step", error);
8392
+ return false;
8393
+ }
8394
+ },
7484
8395
  getCurrentWorkspaces: async (filter) => {
7485
8396
  this.logger?.debug("AgentClient.sessions.getCurrentWorkspaces called", filter);
7486
8397
  try {
@@ -7496,6 +8407,100 @@ var AgentClient = class {
7496
8407
  return [];
7497
8408
  }
7498
8409
  },
8410
+ getAutomationSnapshot: async () => {
8411
+ try {
8412
+ if (this.provider?.getAutomationSnapshot) return await this.provider.getAutomationSnapshot();
8413
+ this.logger?.warn("Provider does not support getAutomationSnapshot");
8414
+ } catch (error) {
8415
+ this.logger?.error("Failed to get automation snapshot", error);
8416
+ }
8417
+ return {
8418
+ automations: [],
8419
+ inbox: [],
8420
+ runtimeState: {},
8421
+ updatedAt: Date.now()
8422
+ };
8423
+ },
8424
+ updateAutomation: async (payload) => {
8425
+ try {
8426
+ if (this.provider?.updateAutomation) return await this.provider.updateAutomation(payload);
8427
+ this.logger?.warn("Provider does not support updateAutomation");
8428
+ } catch (error) {
8429
+ this.logger?.error("Failed to update automation", error);
8430
+ return {
8431
+ success: false,
8432
+ message: error instanceof Error ? error.message : "Unknown error"
8433
+ };
8434
+ }
8435
+ return {
8436
+ success: false,
8437
+ message: "Provider does not support updateAutomation"
8438
+ };
8439
+ },
8440
+ deleteAutomation: async (id) => {
8441
+ try {
8442
+ if (this.provider?.deleteAutomation) return await this.provider.deleteAutomation(id);
8443
+ this.logger?.warn("Provider does not support deleteAutomation");
8444
+ } catch (error) {
8445
+ this.logger?.error("Failed to delete automation", error);
8446
+ return {
8447
+ success: false,
8448
+ message: error instanceof Error ? error.message : "Unknown error"
8449
+ };
8450
+ }
8451
+ return {
8452
+ success: false,
8453
+ message: "Provider does not support deleteAutomation"
8454
+ };
8455
+ },
8456
+ archiveAutomationInboxItem: async (itemId) => {
8457
+ try {
8458
+ if (this.provider?.archiveAutomationInboxItem) return await this.provider.archiveAutomationInboxItem(itemId);
8459
+ this.logger?.warn("Provider does not support archiveAutomationInboxItem");
8460
+ } catch (error) {
8461
+ this.logger?.error("Failed to archive automation inbox item", error);
8462
+ return {
8463
+ success: false,
8464
+ message: error instanceof Error ? error.message : "Unknown error"
8465
+ };
8466
+ }
8467
+ return {
8468
+ success: false,
8469
+ message: "Provider does not support archiveAutomationInboxItem"
8470
+ };
8471
+ },
8472
+ deleteAutomationInboxItem: async (itemId) => {
8473
+ try {
8474
+ if (this.provider?.deleteAutomationInboxItem) return await this.provider.deleteAutomationInboxItem(itemId);
8475
+ this.logger?.warn("Provider does not support deleteAutomationInboxItem");
8476
+ } catch (error) {
8477
+ this.logger?.error("Failed to delete automation inbox item", error);
8478
+ return {
8479
+ success: false,
8480
+ message: error instanceof Error ? error.message : "Unknown error"
8481
+ };
8482
+ }
8483
+ return {
8484
+ success: false,
8485
+ message: "Provider does not support deleteAutomationInboxItem"
8486
+ };
8487
+ },
8488
+ testAutomation: async (id) => {
8489
+ try {
8490
+ if (this.provider?.testAutomation) return await this.provider.testAutomation(id);
8491
+ this.logger?.warn("Provider does not support testAutomation");
8492
+ } catch (error) {
8493
+ this.logger?.error("Failed to test automation", error);
8494
+ return {
8495
+ success: false,
8496
+ message: error instanceof Error ? error.message : "Unknown error"
8497
+ };
8498
+ }
8499
+ return {
8500
+ success: false,
8501
+ message: "Provider does not support testAutomation"
8502
+ };
8503
+ },
7499
8504
  on: (event, handler) => {
7500
8505
  if (this.provider.on) this.provider.on(event, handler);
7501
8506
  else this.logger?.warn(`Provider does not support event registration: ${String(event)}`);
@@ -7637,6 +8642,167 @@ var AgentClient = class {
7637
8642
  };
7638
8643
  }
7639
8644
  },
8645
+ getSkillList: async (params) => {
8646
+ try {
8647
+ if (this.provider && this.provider.getSkillList) {
8648
+ const result = await this.provider.getSkillList(params);
8649
+ this.logger?.info("Skill list retrieved", {
8650
+ resultCount: result.results.length,
8651
+ hasError: !!result.error
8652
+ });
8653
+ return result;
8654
+ }
8655
+ return {
8656
+ results: [],
8657
+ error: "Provider does not support getSkillList"
8658
+ };
8659
+ } catch (error) {
8660
+ this.logger?.error("Failed to get skill list", error);
8661
+ return {
8662
+ results: [],
8663
+ error: error instanceof Error ? error.message : "Unknown error"
8664
+ };
8665
+ }
8666
+ },
8667
+ importSkill: async (params) => {
8668
+ try {
8669
+ if (this.provider && this.provider.importSkill) {
8670
+ const result = await this.provider.importSkill(params);
8671
+ this.logger?.info("Import skill completed", {
8672
+ success: result.success,
8673
+ hasError: !!result.error
8674
+ });
8675
+ return result;
8676
+ }
8677
+ return {
8678
+ success: false,
8679
+ error: "Provider does not support importSkill"
8680
+ };
8681
+ } catch (error) {
8682
+ this.logger?.error("Failed to import skill", error);
8683
+ return {
8684
+ success: false,
8685
+ error: error instanceof Error ? error.message : "Unknown error"
8686
+ };
8687
+ }
8688
+ },
8689
+ toggleSkill: async (params) => {
8690
+ try {
8691
+ if (this.provider && this.provider.toggleSkill) {
8692
+ const result = await this.provider.toggleSkill(params);
8693
+ this.logger?.info("Toggle skill completed", {
8694
+ success: result.success,
8695
+ hasError: !!result.error
8696
+ });
8697
+ return result;
8698
+ }
8699
+ return {
8700
+ success: false,
8701
+ error: "Provider does not support toggleSkill"
8702
+ };
8703
+ } catch (error) {
8704
+ this.logger?.error("Failed to toggle skill", error);
8705
+ return {
8706
+ success: false,
8707
+ error: error instanceof Error ? error.message : "Unknown error"
8708
+ };
8709
+ }
8710
+ },
8711
+ deleteSkill: async (params) => {
8712
+ try {
8713
+ if (this.provider && this.provider.deleteSkill) {
8714
+ const result = await this.provider.deleteSkill(params);
8715
+ this.logger?.info("Delete skill completed", {
8716
+ success: result.success,
8717
+ hasError: !!result.error
8718
+ });
8719
+ return result;
8720
+ }
8721
+ return {
8722
+ success: false,
8723
+ error: "Provider does not support deleteSkill"
8724
+ };
8725
+ } catch (error) {
8726
+ this.logger?.error("Failed to delete skill", error);
8727
+ return {
8728
+ success: false,
8729
+ error: error instanceof Error ? error.message : "Unknown error"
8730
+ };
8731
+ }
8732
+ },
8733
+ getSkillContent: async (params) => {
8734
+ try {
8735
+ if (this.provider && this.provider.getSkillContent) {
8736
+ const result = await this.provider.getSkillContent(params);
8737
+ this.logger?.info("Get skill content completed", { hasError: !!result.error });
8738
+ return result;
8739
+ }
8740
+ return {
8741
+ content: "",
8742
+ error: "Provider does not support getSkillContent"
8743
+ };
8744
+ } catch (error) {
8745
+ this.logger?.error("Failed to get skill content", error);
8746
+ return {
8747
+ content: "",
8748
+ error: error instanceof Error ? error.message : "Unknown error"
8749
+ };
8750
+ }
8751
+ },
8752
+ getMarketplaceSkills: async () => {
8753
+ try {
8754
+ if (this.provider && this.provider.getMarketplaceSkills) {
8755
+ const result = await this.provider.getMarketplaceSkills();
8756
+ this.logger?.info("Marketplace skills retrieved", {
8757
+ resultCount: result.results.length,
8758
+ hasError: !!result.error
8759
+ });
8760
+ return result;
8761
+ }
8762
+ return {
8763
+ results: [],
8764
+ error: "Provider does not support getMarketplaceSkills"
8765
+ };
8766
+ } catch (error) {
8767
+ this.logger?.error("Failed to get marketplace skills", error);
8768
+ return {
8769
+ results: [],
8770
+ error: error instanceof Error ? error.message : "Unknown error"
8771
+ };
8772
+ }
8773
+ },
8774
+ getMarketplaceSkillContent: async (params) => {
8775
+ try {
8776
+ if (this.provider && this.provider.getMarketplaceSkillContent) return await this.provider.getMarketplaceSkillContent(params);
8777
+ return {
8778
+ content: "",
8779
+ error: "Provider does not support getMarketplaceSkillContent"
8780
+ };
8781
+ } catch (error) {
8782
+ this.logger?.error("Failed to get marketplace skill content", error);
8783
+ return {
8784
+ content: "",
8785
+ error: error instanceof Error ? error.message : "Unknown error"
8786
+ };
8787
+ }
8788
+ },
8789
+ installMarketplaceSkill: async (params) => {
8790
+ try {
8791
+ if (this.provider && this.provider.installMarketplaceSkill) return await this.provider.installMarketplaceSkill(params);
8792
+ return {
8793
+ success: false,
8794
+ skillName: params.skillName,
8795
+ errorMessage: "Provider does not support installMarketplaceSkill"
8796
+ };
8797
+ } catch (error) {
8798
+ this.logger?.error("Failed to install marketplace skill", error);
8799
+ return {
8800
+ success: false,
8801
+ skillName: params.skillName,
8802
+ errorMessage: error instanceof Error ? error.message : "Unknown error"
8803
+ };
8804
+ }
8805
+ },
7640
8806
  batchTogglePlugins: async (request) => {
7641
8807
  try {
7642
8808
  if (this.provider && this.provider.batchTogglePlugins) {
@@ -7681,10 +8847,10 @@ var AgentClient = class {
7681
8847
  return [];
7682
8848
  }
7683
8849
  },
7684
- installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource) => {
8850
+ installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
7685
8851
  try {
7686
8852
  if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
7687
- const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource);
8853
+ const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
7688
8854
  this.logger?.info("Install plugins", {
7689
8855
  pluginNames,
7690
8856
  marketplaceName,
@@ -7705,37 +8871,399 @@ var AgentClient = class {
7705
8871
  };
7706
8872
  }
7707
8873
  },
7708
- getSupportScenes: async () => {
8874
+ uninstallPlugin: async (pluginName, marketplaceName, scope) => {
8875
+ try {
8876
+ if (this.provider && "uninstallPlugin" in this.provider && typeof this.provider.uninstallPlugin === "function") {
8877
+ const result = await this.provider.uninstallPlugin(pluginName, marketplaceName, scope);
8878
+ this.logger?.info("Uninstall plugin", {
8879
+ pluginName,
8880
+ marketplaceName,
8881
+ scope,
8882
+ success: result.success
8883
+ });
8884
+ return result;
8885
+ }
8886
+ this.logger?.warn("Provider does not support uninstallPlugin");
8887
+ return {
8888
+ success: false,
8889
+ error: "Provider does not support uninstallPlugin"
8890
+ };
8891
+ } catch (error) {
8892
+ this.logger?.error("Failed to uninstall plugin", error);
8893
+ return {
8894
+ success: false,
8895
+ error: error instanceof Error ? error.message : "Unknown error"
8896
+ };
8897
+ }
8898
+ },
8899
+ updatePlugin: async (pluginName, marketplaceName) => {
8900
+ try {
8901
+ if (this.provider && "updatePlugin" in this.provider && typeof this.provider.updatePlugin === "function") {
8902
+ const result = await this.provider.updatePlugin(pluginName, marketplaceName);
8903
+ this.logger?.info("Update plugin", {
8904
+ pluginName,
8905
+ marketplaceName,
8906
+ success: result.success
8907
+ });
8908
+ return result;
8909
+ }
8910
+ this.logger?.warn("Provider does not support updatePlugin");
8911
+ return {
8912
+ success: false,
8913
+ error: "Provider does not support updatePlugin"
8914
+ };
8915
+ } catch (error) {
8916
+ this.logger?.error("Failed to update plugin", error);
8917
+ return {
8918
+ success: false,
8919
+ error: error instanceof Error ? error.message : "Unknown error"
8920
+ };
8921
+ }
8922
+ },
8923
+ getPluginMarketplaces: async (forceRefresh) => {
8924
+ try {
8925
+ if (this.provider && "getPluginMarketplaces" in this.provider && typeof this.provider.getPluginMarketplaces === "function") {
8926
+ const result = await this.provider.getPluginMarketplaces(forceRefresh);
8927
+ this.logger?.info("Got plugin marketplaces", { count: result?.length ?? 0 });
8928
+ return result;
8929
+ }
8930
+ this.logger?.warn("Provider does not support getPluginMarketplaces");
8931
+ return [];
8932
+ } catch (error) {
8933
+ this.logger?.error("Failed to get plugin marketplaces", error);
8934
+ return [];
8935
+ }
8936
+ },
8937
+ getMarketplacePlugins: async (marketplaceName, forceRefresh, searchText) => {
8938
+ try {
8939
+ if (this.provider && "getMarketplacePlugins" in this.provider && typeof this.provider.getMarketplacePlugins === "function") {
8940
+ const result = await this.provider.getMarketplacePlugins(marketplaceName, forceRefresh, searchText);
8941
+ this.logger?.info("Got marketplace plugins", {
8942
+ marketplaceName,
8943
+ count: result?.length ?? 0
8944
+ });
8945
+ return result;
8946
+ }
8947
+ this.logger?.warn("Provider does not support getMarketplacePlugins");
8948
+ return [];
8949
+ } catch (error) {
8950
+ this.logger?.error("Failed to get marketplace plugins", error);
8951
+ return [];
8952
+ }
8953
+ },
8954
+ getPluginDetail: async (pluginName, marketplaceName) => {
8955
+ try {
8956
+ if (this.provider && "getPluginDetail" in this.provider && typeof this.provider.getPluginDetail === "function") {
8957
+ const result = await this.provider.getPluginDetail(pluginName, marketplaceName);
8958
+ this.logger?.info("Got plugin detail", {
8959
+ pluginName,
8960
+ marketplaceName
8961
+ });
8962
+ return result;
8963
+ }
8964
+ this.logger?.warn("Provider does not support getPluginDetail");
8965
+ return null;
8966
+ } catch (error) {
8967
+ this.logger?.error("Failed to get plugin detail", error);
8968
+ return null;
8969
+ }
8970
+ },
8971
+ addPluginMarketplace: async (source, name) => {
8972
+ try {
8973
+ if (this.provider && "addPluginMarketplace" in this.provider && typeof this.provider.addPluginMarketplace === "function") {
8974
+ const result = await this.provider.addPluginMarketplace(source, name);
8975
+ this.logger?.info("Add plugin marketplace", {
8976
+ source,
8977
+ name,
8978
+ success: result.success
8979
+ });
8980
+ return result;
8981
+ }
8982
+ this.logger?.warn("Provider does not support addPluginMarketplace");
8983
+ return {
8984
+ success: false,
8985
+ error: "Provider does not support addPluginMarketplace"
8986
+ };
8987
+ } catch (error) {
8988
+ this.logger?.error("Failed to add plugin marketplace", error);
8989
+ return {
8990
+ success: false,
8991
+ error: error instanceof Error ? error.message : "Unknown error"
8992
+ };
8993
+ }
8994
+ },
8995
+ removePluginMarketplace: async (marketplaceName) => {
8996
+ try {
8997
+ if (this.provider && "removePluginMarketplace" in this.provider && typeof this.provider.removePluginMarketplace === "function") {
8998
+ const result = await this.provider.removePluginMarketplace(marketplaceName);
8999
+ this.logger?.info("Remove plugin marketplace", {
9000
+ marketplaceName,
9001
+ success: result.success
9002
+ });
9003
+ return result;
9004
+ }
9005
+ this.logger?.warn("Provider does not support removePluginMarketplace");
9006
+ return {
9007
+ success: false,
9008
+ error: "Provider does not support removePluginMarketplace"
9009
+ };
9010
+ } catch (error) {
9011
+ this.logger?.error("Failed to remove plugin marketplace", error);
9012
+ return {
9013
+ success: false,
9014
+ error: error instanceof Error ? error.message : "Unknown error"
9015
+ };
9016
+ }
9017
+ },
9018
+ refreshPluginMarketplace: async (marketplaceName) => {
9019
+ try {
9020
+ if (this.provider && "refreshPluginMarketplace" in this.provider && typeof this.provider.refreshPluginMarketplace === "function") {
9021
+ const result = await this.provider.refreshPluginMarketplace(marketplaceName);
9022
+ this.logger?.info("Refresh plugin marketplace", {
9023
+ marketplaceName,
9024
+ success: result.success
9025
+ });
9026
+ return result;
9027
+ }
9028
+ this.logger?.warn("Provider does not support refreshPluginMarketplace");
9029
+ return {
9030
+ success: false,
9031
+ error: "Provider does not support refreshPluginMarketplace"
9032
+ };
9033
+ } catch (error) {
9034
+ this.logger?.error("Failed to refresh plugin marketplace", error);
9035
+ return {
9036
+ success: false,
9037
+ error: error instanceof Error ? error.message : "Unknown error"
9038
+ };
9039
+ }
9040
+ },
9041
+ openFolderInNewWindow: async (folderPath) => {
9042
+ try {
9043
+ if (this.provider && "openFolderInNewWindow" in this.provider && typeof this.provider.openFolderInNewWindow === "function") {
9044
+ await this.provider.openFolderInNewWindow(folderPath);
9045
+ this.logger?.info("Opened folder in new window", { folderPath });
9046
+ } else {
9047
+ this.logger?.warn("Provider does not support openFolderInNewWindow");
9048
+ throw new Error("Provider does not support openFolderInNewWindow");
9049
+ }
9050
+ } catch (error) {
9051
+ this.logger?.error("Failed to open folder in new window", error);
9052
+ throw error;
9053
+ }
9054
+ },
9055
+ openFolder: async (folderPath) => {
7709
9056
  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 });
9057
+ if (this.provider && "openFolder" in this.provider && typeof this.provider.openFolder === "function") {
9058
+ const result = await this.provider.openFolder(folderPath);
9059
+ this.logger?.info("Opened folder in system file manager", { folderPath });
7713
9060
  return result;
9061
+ } else {
9062
+ this.logger?.warn("Provider does not support openFolder");
9063
+ throw new Error("Provider does not support openFolder");
7714
9064
  }
9065
+ } catch (error) {
9066
+ this.logger?.error("Failed to open folder", error);
9067
+ throw error;
9068
+ }
9069
+ },
9070
+ getSupportScenes: async (locale) => {
9071
+ try {
9072
+ if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
7715
9073
  this.logger?.warn("Provider does not support getSupportScenes");
7716
9074
  return [];
7717
9075
  } catch (error) {
7718
- this.logger?.error("Failed to get support scenes", error);
9076
+ this.logger?.error("Failed to get support scenes", error);
9077
+ return [];
9078
+ }
9079
+ },
9080
+ getProductScenes: async (locale) => {
9081
+ try {
9082
+ if (this.provider?.getProductScenes) {
9083
+ const result = await this.provider.getProductScenes(locale);
9084
+ this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
9085
+ return result;
9086
+ }
9087
+ this.logger?.warn("Provider does not support getProductScenes");
9088
+ return [];
9089
+ } catch (error) {
9090
+ this.logger?.error("Failed to get product scenes", error);
7719
9091
  return [];
7720
9092
  }
7721
9093
  },
7722
- getAvailableCommands: async (sessionId) => {
9094
+ getAvailableCommands: async (params) => {
7723
9095
  try {
7724
9096
  if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
7725
- const result = await this.provider.getAvailableCommands(sessionId);
9097
+ const result = await this.provider.getAvailableCommands(params);
7726
9098
  this.logger?.info("Got available commands from provider", {
7727
- sessionId: sessionId ?? "(default)",
9099
+ sessionId: params?.sessionId ?? "(default)",
7728
9100
  count: result?.length ?? 0
7729
9101
  });
7730
9102
  return result;
7731
9103
  }
7732
- this.logger?.warn("Provider does not support getAvailableCommands", { sessionId });
9104
+ this.logger?.warn("Provider does not support getAvailableCommands", { params });
7733
9105
  return [];
7734
9106
  } catch (error) {
7735
9107
  this.logger?.error("Failed to get available commands", error);
7736
9108
  return [];
7737
9109
  }
7738
9110
  },
9111
+ reportTelemetry: async (eventName, payload) => {
9112
+ try {
9113
+ if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
9114
+ else this.logger?.warn("Provider does not support reportTelemetry");
9115
+ } catch (error) {
9116
+ this.logger?.error("Failed to report telemetry", error);
9117
+ }
9118
+ },
9119
+ getProductConfiguration: async () => {
9120
+ try {
9121
+ if (this.provider?.getProductConfiguration) return await this.provider.getProductConfiguration();
9122
+ this.logger?.warn("Provider does not support getProductConfiguration");
9123
+ return {};
9124
+ } catch (error) {
9125
+ this.logger?.error("Failed to get product configuration", error);
9126
+ return {};
9127
+ }
9128
+ },
9129
+ getUserInfo: async () => {
9130
+ this.logger?.info("[AgentClient.sessions] getUserInfo() called");
9131
+ try {
9132
+ if (this.provider?.getUserInfo) {
9133
+ const result = await this.provider.getUserInfo();
9134
+ this.logger?.info("[AgentClient.sessions] getUserInfo() result:", JSON.stringify(result));
9135
+ return result;
9136
+ }
9137
+ this.logger?.warn("Provider does not support getUserInfo");
9138
+ return {};
9139
+ } catch (error) {
9140
+ this.logger?.error("Failed to get user info", error);
9141
+ return {};
9142
+ }
9143
+ },
9144
+ respondToSampling: async (sessionId, response) => {
9145
+ try {
9146
+ if (this.provider?.respondToSampling) {
9147
+ await this.provider.respondToSampling(sessionId, response);
9148
+ this.logger?.info("Responded to sampling request", {
9149
+ sessionId,
9150
+ requestId: response.id,
9151
+ approved: response.approved
9152
+ });
9153
+ } else this.logger?.warn("Provider does not support respondToSampling");
9154
+ } catch (error) {
9155
+ this.logger?.error("Failed to respond to sampling request", error);
9156
+ throw error;
9157
+ }
9158
+ },
9159
+ respondToRoots: async (sessionId, response) => {
9160
+ try {
9161
+ if (this.provider?.respondToRoots) {
9162
+ await this.provider.respondToRoots(sessionId, response);
9163
+ this.logger?.info("Responded to roots request", {
9164
+ sessionId,
9165
+ requestId: response.id,
9166
+ approved: response.approved
9167
+ });
9168
+ } else this.logger?.warn("Provider does not support respondToRoots");
9169
+ } catch (error) {
9170
+ this.logger?.error("Failed to respond to roots request", error);
9171
+ throw error;
9172
+ }
9173
+ },
9174
+ subscribeSamplingRequests: (serverName, callback) => {
9175
+ if (this.provider?.subscribeSamplingRequests) {
9176
+ this.logger?.info("Subscribing to sampling requests", { serverName });
9177
+ return this.provider.subscribeSamplingRequests(serverName, callback);
9178
+ }
9179
+ this.logger?.warn("Provider does not support subscribeSamplingRequests");
9180
+ return () => {};
9181
+ },
9182
+ subscribeRootsRequests: (serverName, callback) => {
9183
+ if (this.provider?.subscribeRootsRequests) {
9184
+ this.logger?.info("Subscribing to roots requests", { serverName });
9185
+ return this.provider.subscribeRootsRequests(serverName, callback);
9186
+ }
9187
+ this.logger?.warn("Provider does not support subscribeRootsRequests");
9188
+ return () => {};
9189
+ },
9190
+ getMcpServers: async () => {
9191
+ if (this.provider?.getMcpServers) {
9192
+ this.logger?.info("Getting MCP servers list");
9193
+ return this.provider.getMcpServers();
9194
+ }
9195
+ this.logger?.warn("Provider does not support getMcpServers");
9196
+ return [];
9197
+ },
9198
+ toggleMcpServer: async (serverName, enabled) => {
9199
+ if (this.provider?.toggleMcpServer) {
9200
+ this.logger?.info("Toggling MCP server", {
9201
+ serverName,
9202
+ enabled
9203
+ });
9204
+ await this.provider.toggleMcpServer(serverName, enabled);
9205
+ } else {
9206
+ this.logger?.warn("Provider does not support toggleMcpServer");
9207
+ throw new Error("toggleMcpServer not supported by provider");
9208
+ }
9209
+ },
9210
+ reconnectMcpServer: async (serverName, forceHttpCallback) => {
9211
+ if (this.provider?.reconnectMcpServer) {
9212
+ this.logger?.info("Reconnecting MCP server", {
9213
+ serverName,
9214
+ forceHttpCallback
9215
+ });
9216
+ await this.provider.reconnectMcpServer(serverName, forceHttpCallback);
9217
+ } else {
9218
+ this.logger?.warn("Provider does not support reconnectMcpServer");
9219
+ throw new Error("reconnectMcpServer not supported by provider");
9220
+ }
9221
+ },
9222
+ deleteMcpServer: async (serverName) => {
9223
+ if (this.provider?.deleteMcpServer) {
9224
+ this.logger?.info("Deleting MCP server", { serverName });
9225
+ await this.provider.deleteMcpServer(serverName);
9226
+ } else {
9227
+ this.logger?.warn("Provider does not support deleteMcpServer");
9228
+ throw new Error("deleteMcpServer not supported by provider");
9229
+ }
9230
+ },
9231
+ openMcpConfig: async () => {
9232
+ if (this.provider?.openMcpConfig) {
9233
+ this.logger?.info("Opening MCP config");
9234
+ await this.provider.openMcpConfig();
9235
+ } else {
9236
+ this.logger?.warn("Provider does not support openMcpConfig");
9237
+ throw new Error("openMcpConfig not supported by provider");
9238
+ }
9239
+ },
9240
+ getMcpConfigContent: async () => {
9241
+ if (this.provider?.getMcpConfigContent) {
9242
+ this.logger?.info("Getting MCP config content");
9243
+ return await this.provider.getMcpConfigContent();
9244
+ } else {
9245
+ this.logger?.warn("Provider does not support getMcpConfigContent");
9246
+ throw new Error("getMcpConfigContent not supported by provider");
9247
+ }
9248
+ },
9249
+ saveMcpConfigContent: async (content) => {
9250
+ if (this.provider?.saveMcpConfigContent) {
9251
+ this.logger?.info("Saving MCP config content");
9252
+ await this.provider.saveMcpConfigContent(content);
9253
+ } else {
9254
+ this.logger?.warn("Provider does not support saveMcpConfigContent");
9255
+ throw new Error("saveMcpConfigContent not supported by provider");
9256
+ }
9257
+ },
9258
+ clipboardReadText: async () => {
9259
+ if (this.provider?.clipboardReadText) {
9260
+ this.logger?.info("Reading clipboard text");
9261
+ return await this.provider.clipboardReadText();
9262
+ } else {
9263
+ this.logger?.warn("Provider does not support clipboardReadText");
9264
+ throw new Error("clipboardReadText not supported by provider");
9265
+ }
9266
+ },
7739
9267
  models: this.createModelsResource()
7740
9268
  };
7741
9269
  }
@@ -7961,10 +9489,6 @@ const oauthRepositoryService = new OAuthRepositoryService();
7961
9489
  * 判断当前账号是否是 SSO 账号
7962
9490
  * 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
7963
9491
  */
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
9492
  /**
7969
9493
  * 根据路径获取完整 URL
7970
9494
  * - SSO 账号需要跳转到对应的预发/生产域名
@@ -7972,16 +9496,7 @@ const isSSODomain = () => {
7972
9496
  * @param path 路径,如 '/login'、'/logout'、'/home' 等
7973
9497
  * @returns 完整的 URL 地址
7974
9498
  */
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
- };
9499
+ const getFullUrl = (path) => `${window.location.origin}${path}`;
7985
9500
  /** 获取当前域名的账号选择页面 URL */
7986
9501
  const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
7987
9502
  /** localStorage 中存储选中账号 ID 的 key */
@@ -8014,16 +9529,34 @@ const getPackageName = (packageCode) => CommodityCodeText[packageCode] || "";
8014
9529
  * 注意:getAgents 和 getModels 方法已废弃并移除,
8015
9530
  * 请使用 IAgentAdapter 中的对应方法
8016
9531
  */
8017
- var BackendProvider = class {
9532
+ var BackendProvider = class BackendProvider {
8018
9533
  constructor(config) {
8019
9534
  httpService.setBaseURL(config.baseUrl);
8020
9535
  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);
9536
+ httpService.onUnauthorized(() => this.handleUnauthorized());
9537
+ }
9538
+ /**
9539
+ * 处理 401 未授权错误
9540
+ * 先尝试刷新 token,失败后再执行登出流程
9541
+ *
9542
+ * @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
9543
+ */
9544
+ async handleUnauthorized() {
9545
+ console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
9546
+ try {
9547
+ if (await this.refreshToken()) {
9548
+ console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
9549
+ return;
9550
+ }
9551
+ throw new Error("Token refresh returned null");
9552
+ } catch (error) {
9553
+ console.error("[BackendProvider] Token refresh failed after 401:", error);
9554
+ console.log("[BackendProvider] Token refresh failed, triggering logout");
9555
+ this.logout().catch((logoutError) => {
9556
+ console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
8025
9557
  });
8026
- });
9558
+ throw error;
9559
+ }
8027
9560
  }
8028
9561
  /**
8029
9562
  * 获取当前账号信息
@@ -8287,10 +9820,11 @@ var BackendProvider = class {
8287
9820
  if (!time) return 0;
8288
9821
  return new Date(time).getTime();
8289
9822
  };
8290
- const dailyCredits = [CommodityCode.free, CommodityCode.freeMon];
9823
+ const dailyCredits = [CommodityCode.free];
8291
9824
  const planResources = resources.map((r) => {
8292
9825
  const isDaily = dailyCredits.includes(r.PackageCode);
8293
9826
  const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
9827
+ const refreshAt = parseTime(r.CycleEndTime) + 1e3;
8294
9828
  return {
8295
9829
  id: r.ResourceId,
8296
9830
  name: isDaily ? "plan.addonCredits" : getPackageName(r.PackageCode),
@@ -8300,7 +9834,7 @@ var BackendProvider = class {
8300
9834
  used: Math.max(0, Number(r.CycleCapacitySizePrecise) - Number(r.CycleCapacityRemainPrecise)) || 0,
8301
9835
  left: Number(r.CycleCapacityRemainPrecise) || 0,
8302
9836
  expireAt: parseTime(endTime),
8303
- refreshAt: isDaily ? void 0 : parseTime(r.CycleEndTime)
9837
+ refreshAt: isDaily ? void 0 : refreshAt
8304
9838
  };
8305
9839
  }).sort((a, b) => {
8306
9840
  const getPriority = (code) => {
@@ -8308,10 +9842,11 @@ var BackendProvider = class {
8308
9842
  CommodityCode.proMon,
8309
9843
  CommodityCode.proMonPlus,
8310
9844
  CommodityCode.proYear,
9845
+ CommodityCode.freeMon,
8311
9846
  CommodityCode.extra
8312
9847
  ].includes(code)) return 1;
8313
9848
  if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
8314
- if ([CommodityCode.free, CommodityCode.freeMon].includes(code)) return 3;
9849
+ if ([CommodityCode.free].includes(code)) return 3;
8315
9850
  return 4;
8316
9851
  };
8317
9852
  return getPriority(a.packageCode) - getPriority(b.packageCode);
@@ -8436,34 +9971,65 @@ var BackendProvider = class {
8436
9971
  }
8437
9972
  /**
8438
9973
  * 登出账号
8439
- * Web 环境: 通过 iframe 访问登出 URL 清除 cookie
9974
+ *
9975
+ * 策略:
9976
+ * - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
9977
+ * - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
8440
9978
  */
8441
9979
  async logout() {
8442
- const url = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
9980
+ const account = accountService.getAccount();
9981
+ if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
9982
+ else await this.logoutViaHttp();
9983
+ localStorage.removeItem(SELECTED_ACCOUNT_KEY);
9984
+ accountService.clearAccount();
9985
+ }
9986
+ /**
9987
+ * IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
9988
+ * 轮询 iframe URL 变化检测完成,兜底超时 5 秒
9989
+ */
9990
+ async logoutViaIframe() {
9991
+ const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
8443
9992
  try {
8444
9993
  await new Promise((resolve) => {
8445
9994
  const iframe = document.createElement("iframe");
8446
9995
  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 = () => {
9996
+ iframe.src = logoutUrl;
9997
+ let pollTimer;
9998
+ let settled = false;
9999
+ const done = () => {
10000
+ if (settled) return;
10001
+ settled = true;
10002
+ clearInterval(pollTimer);
8453
10003
  clearTimeout(timeout);
8454
10004
  if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
8455
- };
8456
- iframe.onerror = () => {
8457
- cleanup();
8458
10005
  resolve();
8459
10006
  };
10007
+ let wasRedirecting = false;
10008
+ pollTimer = setInterval(() => {
10009
+ try {
10010
+ const href = iframe.contentWindow?.location?.href;
10011
+ if (wasRedirecting && href) done();
10012
+ } catch {
10013
+ wasRedirecting = true;
10014
+ }
10015
+ }, 100);
10016
+ const timeout = setTimeout(done, 5e3);
10017
+ iframe.onerror = done;
8460
10018
  document.body.appendChild(iframe);
8461
10019
  });
8462
10020
  } catch (error) {
8463
- console.error("[BackendProvider] logout failed:", error);
10021
+ console.error("[BackendProvider] logout via iframe failed:", error);
10022
+ }
10023
+ }
10024
+ /**
10025
+ * 非 IOA 企业登出:直接 HTTP 请求 /console/logout
10026
+ */
10027
+ async logoutViaHttp() {
10028
+ try {
10029
+ await httpService.get("/console/logout");
10030
+ } catch (error) {
10031
+ console.error("[BackendProvider] logout via http failed:", error);
8464
10032
  }
8465
- localStorage.removeItem(SELECTED_ACCOUNT_KEY);
8466
- accountService.clearAccount();
8467
10033
  }
8468
10034
  /**
8469
10035
  * 批量切换插件状态
@@ -8541,6 +10107,132 @@ var BackendProvider = class {
8541
10107
  async getRepositories(connector, page = 0, perPage = 100) {
8542
10108
  return oauthRepositoryService.getRepositories(connector, page, perPage);
8543
10109
  }
10110
+ /**
10111
+ * 保存待发送的输入内容到后端
10112
+ * API 端点: POST /api/v1/code-id
10113
+ */
10114
+ async savePendingInput(code) {
10115
+ try {
10116
+ const result = await httpService.post("/api/v1/code-id", { code });
10117
+ return result?.codeId || result?.data?.codeId || null;
10118
+ } catch (e) {
10119
+ console.warn("[BackendProvider] savePendingInput failed:", e);
10120
+ return null;
10121
+ }
10122
+ }
10123
+ /**
10124
+ * 从后端加载待发送的输入内容
10125
+ * API 端点: GET /api/v1/code?id=xxx
10126
+ */
10127
+ async loadPendingInput(codeId) {
10128
+ try {
10129
+ const result = await httpService.get(`/api/v1/code?id=${encodeURIComponent(codeId)}`);
10130
+ return result?.code || result?.data?.code || null;
10131
+ } catch (e) {
10132
+ console.warn("[BackendProvider] loadPendingInput failed:", e);
10133
+ return null;
10134
+ }
10135
+ }
10136
+ /**
10137
+ * 获取每日签到状态
10138
+ * API 端点: POST /billing/meter/checkin-status
10139
+ */
10140
+ async getCheckinStatus() {
10141
+ try {
10142
+ const result = await httpService.post("/billing/meter/checkin-status", {});
10143
+ if (result?.code === 0 && result?.data) return result.data;
10144
+ return null;
10145
+ } catch (error) {
10146
+ console.error("[BackendProvider] getCheckinStatus failed:", error);
10147
+ return null;
10148
+ }
10149
+ }
10150
+ /**
10151
+ * 执行每日签到
10152
+ * API 端点: POST /billing/meter/daily-checkin
10153
+ */
10154
+ async claimDailyCheckin() {
10155
+ const result = await httpService.post("/billing/meter/daily-checkin", {});
10156
+ if (result?.code === 0 && result?.data) return result.data;
10157
+ throw new Error(result?.msg || "Checkin failed");
10158
+ }
10159
+ static {
10160
+ this.SKILLHUB_BASE_URL = "https://lightmake.site";
10161
+ }
10162
+ static {
10163
+ this.SKILLHUB_FETCH_TIMEOUT = 1e4;
10164
+ }
10165
+ async skillHubFetch(url, init) {
10166
+ const controller = new AbortController();
10167
+ const timer = setTimeout(() => controller.abort(), BackendProvider.SKILLHUB_FETCH_TIMEOUT);
10168
+ try {
10169
+ return await fetch(url, {
10170
+ ...init,
10171
+ signal: controller.signal
10172
+ });
10173
+ } finally {
10174
+ clearTimeout(timer);
10175
+ }
10176
+ }
10177
+ async getSkillHubList(params = {}) {
10178
+ const qs = new URLSearchParams();
10179
+ if (params.page) qs.set("page", String(params.page));
10180
+ if (params.pageSize) qs.set("pageSize", String(params.pageSize));
10181
+ if (params.sortBy) qs.set("sortBy", params.sortBy);
10182
+ if (params.order) qs.set("order", params.order);
10183
+ if (params.keyword) qs.set("keyword", params.keyword);
10184
+ if (params.category) qs.set("category", params.category);
10185
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/skills?${qs.toString()}`);
10186
+ if (!res.ok) throw new Error(`SkillHub list: ${res.status}`);
10187
+ return res.json();
10188
+ }
10189
+ async getSkillHubCategories() {
10190
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/categories`);
10191
+ if (!res.ok) throw new Error(`SkillHub categories: ${res.status}`);
10192
+ return res.json();
10193
+ }
10194
+ async getSkillHubSearch(q, limit = 20) {
10195
+ const qs = new URLSearchParams({
10196
+ q,
10197
+ limit: String(limit)
10198
+ });
10199
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/search?${qs.toString()}`);
10200
+ if (!res.ok) throw new Error(`SkillHub search: ${res.status}`);
10201
+ return res.json();
10202
+ }
10203
+ async getSkillHubDetail(slug) {
10204
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/skills/${encodeURIComponent(slug)}`);
10205
+ if (!res.ok) throw new Error(`SkillHub detail: ${res.status}`);
10206
+ return res.json();
10207
+ }
10208
+ async getSkillHubExists(slugs) {
10209
+ const res = await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/skills/exists`, {
10210
+ method: "POST",
10211
+ headers: { "Content-Type": "application/json" },
10212
+ body: JSON.stringify({ slugs })
10213
+ });
10214
+ if (!res.ok) throw new Error(`SkillHub exists: ${res.status}`);
10215
+ return res.json();
10216
+ }
10217
+ async reportSkillHubStats(slug, inc) {
10218
+ try {
10219
+ await this.skillHubFetch(`${BackendProvider.SKILLHUB_BASE_URL}/api/v1/skills/${encodeURIComponent(slug)}/stats/inc`, {
10220
+ method: "POST",
10221
+ headers: { "Content-Type": "application/json" },
10222
+ body: JSON.stringify(inc)
10223
+ });
10224
+ } catch {}
10225
+ }
10226
+ async installSkillHubSkill(_slug, _version, _name) {
10227
+ return {
10228
+ success: false,
10229
+ skillName: _slug,
10230
+ errorMessage: "SkillHub install requires IDE mode (no IPC channel available)"
10231
+ };
10232
+ }
10233
+ async getSkillHubInstalledMetas() {
10234
+ return [];
10235
+ }
8544
10236
  };
8545
10237
  /**
8546
10238
  * 创建 BackendProvider 实例
@@ -8579,10 +10271,26 @@ const BACKEND_REQUEST_TYPES = {
8579
10271
  REVOKE_ALL: "backend:revoke-all",
8580
10272
  GET_FILE: "backend:get-file",
8581
10273
  RELOAD_WINDOW: "backend:reload-window",
10274
+ SAVE_LOCALE: "backend:save-locale",
8582
10275
  CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
8583
10276
  OPEN_EXTERNAL: "backend:open-external",
10277
+ OPEN_LOCAL_FILE: "backend:open-local-file",
10278
+ GET_LOCAL_CUSTOM_MODELS: "backend:get-local-custom-models",
10279
+ SAVE_LOCAL_CUSTOM_MODEL: "backend:save-local-custom-model",
10280
+ DELETE_LOCAL_CUSTOM_MODEL: "backend:delete-local-custom-model",
8584
10281
  BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
8585
- GET_SUPPORT_SCENES: "backend:get-support-scenes"
10282
+ GET_SUPPORT_SCENES: "backend:get-support-scenes",
10283
+ GET_ACCOUNT_USAGE: "backend:get-account-usage",
10284
+ GET_CHECKIN_STATUS: "backend:get-checkin-status",
10285
+ CLAIM_DAILY_CHECKIN: "backend:claim-daily-checkin",
10286
+ GET_ACTIVITY_BANNER: "backend:get-activity-banner",
10287
+ SKILLHUB_LIST: "backend:skillhub-list",
10288
+ SKILLHUB_CATEGORIES: "backend:skillhub-categories",
10289
+ SKILLHUB_SEARCH: "backend:skillhub-search",
10290
+ SKILLHUB_DETAIL: "backend:skillhub-detail",
10291
+ SKILLHUB_DOWNLOAD_URL: "backend:skillhub-download-url",
10292
+ SKILLHUB_EXISTS: "backend:skillhub-exists",
10293
+ SKILLHUB_REPORT_STATS: "backend:skillhub-report-stats"
8586
10294
  };
8587
10295
  /**
8588
10296
  * 生成唯一请求 ID
@@ -8601,6 +10309,25 @@ var IPCBackendProvider = class {
8601
10309
  this.debug = config.debug ?? false;
8602
10310
  this.timeoutMs = config.timeoutMs ?? 3e4;
8603
10311
  this.log("Initialized with IWidgetChannel");
10312
+ this.setupSessionChangeListener();
10313
+ }
10314
+ /**
10315
+ * 设置会话变化监听器
10316
+ * 监听来自 Extension Host (BackendBridgeService) 推送的 auth:session-changed 事件
10317
+ * 并更新本地 accountService
10318
+ */
10319
+ setupSessionChangeListener() {
10320
+ this.log("Setting up session change listener");
10321
+ this.channel.on("auth:session-changed", (data) => {
10322
+ this.log("Received auth:session-changed event from Extension Host:", data);
10323
+ const account = data?.account || null;
10324
+ accountService.setAccount(account);
10325
+ this.log("Updated accountService with new session", {
10326
+ hasAccount: !!account,
10327
+ accountNickname: account?.nickname
10328
+ });
10329
+ });
10330
+ this.log("Session change listener setup complete");
8604
10331
  }
8605
10332
  /**
8606
10333
  * 发送统一格式的后端请求
@@ -8629,12 +10356,14 @@ var IPCBackendProvider = class {
8629
10356
  */
8630
10357
  async getAccount() {
8631
10358
  this.log("Getting account via IPC");
10359
+ const startTime = performance.now();
8632
10360
  try {
8633
10361
  const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
10362
+ this.log(`getAccount IPC completed in ${(performance.now() - startTime).toFixed(0)}ms`);
8634
10363
  accountService.setAccount(account);
8635
10364
  return account;
8636
10365
  } catch (error) {
8637
- this.log("Get account failed:", error);
10366
+ this.log(`getAccount IPC failed after ${(performance.now() - startTime).toFixed(0)}ms:`, error);
8638
10367
  accountService.setAccount(null);
8639
10368
  return null;
8640
10369
  }
@@ -8863,6 +10592,20 @@ var IPCBackendProvider = class {
8863
10592
  }
8864
10593
  }
8865
10594
  /**
10595
+ * Save locale to argv.json without restarting the app.
10596
+ * The change takes effect on next manual restart.
10597
+ * @param params locale to save
10598
+ */
10599
+ async saveLocale(params) {
10600
+ this.log("Saving locale to argv.json via IPC", params);
10601
+ try {
10602
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SAVE_LOCALE, params);
10603
+ } catch (error) {
10604
+ this.log("Save locale request failed:", error);
10605
+ throw error;
10606
+ }
10607
+ }
10608
+ /**
8866
10609
  * 关闭 Agent Manager 面板
8867
10610
  * IDE 环境: 通过 IPC 通知 IDE 关闭 Agent Manager(用于返回 IDE)
8868
10611
  */
@@ -8890,6 +10633,41 @@ var IPCBackendProvider = class {
8890
10633
  }
8891
10634
  }
8892
10635
  /**
10636
+ * 打开本地文件
10637
+ * IDE 环境: 通过 IPC 通知 IDE 打开本地配置文件
10638
+ * @param filePath 要打开的文件路径
10639
+ */
10640
+ async openLocalFile(filePath) {
10641
+ this.log("Opening local file via IPC:", filePath);
10642
+ try {
10643
+ if (!await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_LOCAL_FILE, { filePath })) throw new Error(`Failed to open local file: ${filePath}`);
10644
+ } catch (error) {
10645
+ this.log("Open local file request failed:", error);
10646
+ throw error;
10647
+ }
10648
+ }
10649
+ /**
10650
+ * 获取用户级本地自定义模型
10651
+ */
10652
+ async getLocalCustomModels() {
10653
+ this.log("Getting local custom models via IPC");
10654
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_LOCAL_CUSTOM_MODELS);
10655
+ }
10656
+ /**
10657
+ * 保存用户级本地自定义模型
10658
+ */
10659
+ async saveLocalCustomModel(request) {
10660
+ this.log("Saving local custom model via IPC:", request);
10661
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SAVE_LOCAL_CUSTOM_MODEL, request);
10662
+ }
10663
+ /**
10664
+ * 删除用户级本地自定义模型
10665
+ */
10666
+ async deleteLocalCustomModel(id) {
10667
+ this.log("Deleting local custom model via IPC:", id);
10668
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.DELETE_LOCAL_CUSTOM_MODEL, { id });
10669
+ }
10670
+ /**
8893
10671
  * 批量切换插件状态
8894
10672
  * IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
8895
10673
  */
@@ -8923,6 +10701,187 @@ var IPCBackendProvider = class {
8923
10701
  }
8924
10702
  }
8925
10703
  /**
10704
+ * 获取账号用量信息(积分/Credits)
10705
+ * IDE 环境: 通过 IPC 实时获取用量信息,每次打开菜单时调用
10706
+ *
10707
+ * 调用链:
10708
+ * 1. agent-ui: IPCBackendProvider.getAccountUsage()
10709
+ * 2. Agent Manager renderer: BackendService.getAccountUsage()
10710
+ * 3. Main Process: codebuddy:getAccountUsage IPC handler
10711
+ * 4. 返回 { usageLeft, usageTotal, editionType, refreshAt } 等字段
10712
+ */
10713
+ async getAccountUsage() {
10714
+ this.log("Getting account usage via IPC");
10715
+ try {
10716
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT_USAGE);
10717
+ } catch (error) {
10718
+ this.log("Get account usage failed:", error);
10719
+ return null;
10720
+ }
10721
+ }
10722
+ /**
10723
+ * 获取每日签到状态
10724
+ * IDE 环境: 通过 IPC 获取签到状态
10725
+ */
10726
+ async getCheckinStatus() {
10727
+ this.log("Getting checkin status via IPC");
10728
+ try {
10729
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_CHECKIN_STATUS);
10730
+ } catch (error) {
10731
+ this.log("Get checkin status failed:", error);
10732
+ return null;
10733
+ }
10734
+ }
10735
+ /**
10736
+ * 执行每日签到
10737
+ * IDE 环境: 通过 IPC 执行签到
10738
+ */
10739
+ async claimDailyCheckin() {
10740
+ this.log("Claiming daily checkin via IPC");
10741
+ try {
10742
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.CLAIM_DAILY_CHECKIN);
10743
+ } catch (error) {
10744
+ this.log("Claim daily checkin failed:", error);
10745
+ throw error;
10746
+ }
10747
+ }
10748
+ /**
10749
+ * 获取活动 Banner
10750
+ * IDE 环境: 通过 IPC 获取活动 Banner
10751
+ */
10752
+ async getActivityBanner() {
10753
+ this.log("Getting activity banner via IPC");
10754
+ try {
10755
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACTIVITY_BANNER);
10756
+ } catch (error) {
10757
+ this.log("Get activity banner failed:", error);
10758
+ return null;
10759
+ }
10760
+ }
10761
+ /**
10762
+ * 获取 SkillHub 技能列表
10763
+ * IDE 环境: 通过 IPC 获取技能列表
10764
+ */
10765
+ async getSkillHubList(params) {
10766
+ this.log("Getting SkillHub list via IPC", params);
10767
+ try {
10768
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_LIST, params);
10769
+ } catch (error) {
10770
+ this.log("Get SkillHub list failed:", error);
10771
+ throw error;
10772
+ }
10773
+ }
10774
+ /**
10775
+ * 获取 SkillHub 分类列表
10776
+ * IDE 环境: 通过 IPC 获取分类列表
10777
+ */
10778
+ async getSkillHubCategories() {
10779
+ this.log("Getting SkillHub categories via IPC");
10780
+ try {
10781
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_CATEGORIES);
10782
+ } catch (error) {
10783
+ this.log("Get SkillHub categories failed:", error);
10784
+ throw error;
10785
+ }
10786
+ }
10787
+ /**
10788
+ * 搜索 SkillHub 技能
10789
+ * IDE 环境: 通过 IPC 搜索技能
10790
+ */
10791
+ async getSkillHubSearch(q, limit = 20) {
10792
+ this.log("Searching SkillHub via IPC", {
10793
+ q,
10794
+ limit
10795
+ });
10796
+ try {
10797
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_SEARCH, {
10798
+ q,
10799
+ limit
10800
+ });
10801
+ } catch (error) {
10802
+ this.log("SkillHub search failed:", error);
10803
+ throw error;
10804
+ }
10805
+ }
10806
+ /**
10807
+ * 获取 SkillHub 技能详情
10808
+ * IDE 环境: 通过 IPC 获取技能详情
10809
+ */
10810
+ async getSkillHubDetail(slug) {
10811
+ this.log("Getting SkillHub detail via IPC", { slug });
10812
+ try {
10813
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_DETAIL, { slug });
10814
+ } catch (error) {
10815
+ this.log("Get SkillHub detail failed:", error);
10816
+ throw error;
10817
+ }
10818
+ }
10819
+ /**
10820
+ * 批量检查 SkillHub 技能是否存在
10821
+ * IDE 环境: 通过 IPC 检查技能是否存在
10822
+ */
10823
+ async getSkillHubExists(slugs) {
10824
+ this.log("Checking SkillHub exists via IPC", { slugs });
10825
+ try {
10826
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_EXISTS, { slugs });
10827
+ } catch (error) {
10828
+ this.log("SkillHub exists check failed:", error);
10829
+ throw error;
10830
+ }
10831
+ }
10832
+ /**
10833
+ * 上报 SkillHub 技能统计
10834
+ * IDE 环境: 通过 IPC 上报统计
10835
+ */
10836
+ async reportSkillHubStats(slug, inc) {
10837
+ this.log("Reporting SkillHub stats via IPC", {
10838
+ slug,
10839
+ inc
10840
+ });
10841
+ try {
10842
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SKILLHUB_REPORT_STATS, {
10843
+ slug,
10844
+ inc
10845
+ });
10846
+ } catch (error) {
10847
+ this.log("Report SkillHub stats failed:", error);
10848
+ }
10849
+ }
10850
+ /**
10851
+ * 安装 SkillHub 技能
10852
+ * IDE 环境: 通过 IPC 安装技能(下载 zip → 解压到本地)
10853
+ */
10854
+ async installSkillHubSkill(slug, version, name) {
10855
+ this.log("Installing SkillHub skill via IPC", {
10856
+ slug,
10857
+ version,
10858
+ name
10859
+ });
10860
+ try {
10861
+ return await this.sendBackendRequest("backend:skillhub-install", {
10862
+ slug,
10863
+ version,
10864
+ name
10865
+ });
10866
+ } catch (error) {
10867
+ this.log("Install SkillHub skill failed:", error);
10868
+ throw error;
10869
+ }
10870
+ }
10871
+ /**
10872
+ * 获取本地已安装的 SkillHub 技能元信息
10873
+ * IDE 环境: 通过 IPC 获取元信息
10874
+ */
10875
+ async getSkillHubInstalledMetas() {
10876
+ this.log("Getting SkillHub installed metas via IPC");
10877
+ try {
10878
+ return await this.sendBackendRequest("backend:skillhub-installed-metas");
10879
+ } catch (error) {
10880
+ this.log("Get SkillHub installed metas failed:", error);
10881
+ return [];
10882
+ }
10883
+ }
10884
+ /**
8926
10885
  * 调试日志
8927
10886
  */
8928
10887
  log(...args) {
@@ -8937,5 +10896,5 @@ function createIPCBackendProvider(config) {
8937
10896
  }
8938
10897
 
8939
10898
  //#endregion
8940
- export { ActiveSessionImpl, AgentClient, BackendProvider, CloudAgentConnection, CloudAgentProvider, E2BFilesystem, HttpService, IPCBackendProvider, SessionManager, createBackendProvider, createIPCBackendProvider, httpService, isCloudAgentState };
10899
+ export { ActiveSessionImpl, AgentClient, BackendProvider, CloudAgentConnection, CloudAgentProvider, E2BFilesystem, HttpService, IPCBackendProvider, SessionError, SessionManager, createBackendProvider, createIPCBackendProvider, httpService, isCloudAgentState };
8941
10900
  //# sourceMappingURL=index.mjs.map