aicodeswitch 5.2.12 → 5.2.13

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.
@@ -54,6 +54,7 @@ const model_rewrite_transform_1 = require("./transformers/model-rewrite-transfor
54
54
  const chunk_collector_1 = require("./transformers/chunk-collector");
55
55
  const stream_timing_transform_1 = require("./transformers/stream-timing-transform");
56
56
  const rules_status_service_1 = require("./rules-status-service");
57
+ const agent_map_1 = require("./agent-map");
57
58
  const index_1 = require("./conversions/index");
58
59
  const stream_converter_adapter_1 = require("./conversions/stream-converter-adapter");
59
60
  const types_1 = require("../types");
@@ -749,6 +750,7 @@ class ProxyServer {
749
750
  responseTime: Date.now() - requestStartAt,
750
751
  // 添加最后失败的服务信息
751
752
  ruleId: lastFailedRule === null || lastFailedRule === void 0 ? void 0 : lastFailedRule.id,
753
+ routeId: route === null || route === void 0 ? void 0 : route.id,
752
754
  targetServiceId: lastFailedService === null || lastFailedService === void 0 ? void 0 : lastFailedService.id,
753
755
  targetServiceName: lastFailedService === null || lastFailedService === void 0 ? void 0 : lastFailedService.name,
754
756
  targetModel: (lastFailedRule === null || lastFailedRule === void 0 ? void 0 : lastFailedRule.targetModel) || ((_e = req.body) === null || _e === void 0 ? void 0 : _e.model),
@@ -3415,7 +3417,7 @@ class ProxyServer {
3415
3417
  }
3416
3418
  proxyRequest(req, res, route, rule, service, options) {
3417
3419
  return __awaiter(this, void 0, void 0, function* () {
3418
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
3420
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
3419
3421
  res.locals.skipLog = true;
3420
3422
  const startTime = Date.now();
3421
3423
  const rawSourceType = service.sourceType || 'openai-chat';
@@ -3423,8 +3425,15 @@ class ProxyServer {
3423
3425
  const sourceType = (0, type_migration_1.normalizeSourceType)(rawSourceType);
3424
3426
  const targetType = this.inferToolFromRequest(req);
3425
3427
  const sessionId = this.defaultExtractSessionId(req, targetType) || '-';
3428
+ // Agent Map:在途请求注册(active 状态判定依据)
3429
+ const _accessKeyCtxAtStart = req._accessKeyCtx;
3430
+ agent_map_1.agentMapService.startRequest(sessionId, targetType, {
3431
+ source: _accessKeyCtxAtStart ? 'access-key' : 'global',
3432
+ keyId: (_a = _accessKeyCtxAtStart === null || _accessKeyCtxAtStart === void 0 ? void 0 : _accessKeyCtxAtStart.accessKey) === null || _a === void 0 ? void 0 : _a.id,
3433
+ keyName: (_b = _accessKeyCtxAtStart === null || _accessKeyCtxAtStart === void 0 ? void 0 : _accessKeyCtxAtStart.accessKey) === null || _b === void 0 ? void 0 : _b.name,
3434
+ });
3426
3435
  const vendor = this.dbManager.getVendorByServiceId(service.id);
3427
- console.log(`\x1b[32m[Request Start]\x1b[0m client=${targetType}, session=${sessionId}, rule=${rule.id}(${rule.contentType}), vendor=${(vendor === null || vendor === void 0 ? void 0 : vendor.name) || '-'}, service=${service.name}, model=${rule.targetModel || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.model) || '-'}`);
3436
+ console.log(`\x1b[32m[Request Start]\x1b[0m client=${targetType}, session=${sessionId}, rule=${rule.id}(${rule.contentType}), vendor=${(vendor === null || vendor === void 0 ? void 0 : vendor.name) || '-'}, service=${service.name}, model=${rule.targetModel || ((_c = req.body) === null || _c === void 0 ? void 0 : _c.model) || '-'}`);
3428
3437
  const failoverEnabled = (options === null || options === void 0 ? void 0 : options.failoverEnabled) === true;
3429
3438
  const forwardedToServiceName = options === null || options === void 0 ? void 0 : options.forwardedToServiceName;
3430
3439
  const useOriginalConfig = (options === null || options === void 0 ? void 0 : options.useOriginalConfig) === true;
@@ -3593,15 +3602,40 @@ class ProxyServer {
3593
3602
  // 标记规则正在使用
3594
3603
  rules_status_service_1.rulesStatusBroadcaster.markRuleInUse(route.id, rule.id);
3595
3604
  const finalizeLog = (statusCode, error) => __awaiter(this, void 0, void 0, function* () {
3596
- var _a, _b, _c, _d, _e, _f, _g, _h;
3605
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
3597
3606
  if (logged)
3598
3607
  return;
3599
3608
  // 服务性能数据点采集(全局,与 AUTH 无关;独立于 enableLogging 开关)
3600
3609
  this.emitPerformance({
3601
3610
  statusCode, startTime, usage: usageForLog, streamTiming,
3602
3611
  service, vendorId: vendor === null || vendor === void 0 ? void 0 : vendor.id, vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
3603
- model: rule.targetModel || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.model),
3612
+ model: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.model),
3604
3613
  });
3614
+ // Agent Map:在途请求注销 + 活动/状态采集广播(独立于 enableLogging,仅执行一次)
3615
+ if (!res.locals._agentMapRecorded) {
3616
+ res.locals._agentMapRecorded = true;
3617
+ agent_map_1.agentMapService.endRequest(sessionId);
3618
+ const _akCtx = req._accessKeyCtx;
3619
+ const _tokensDelta = (usageForLog === null || usageForLog === void 0 ? void 0 : usageForLog.totalTokens) ||
3620
+ (((usageForLog === null || usageForLog === void 0 ? void 0 : usageForLog.inputTokens) || 0) + ((usageForLog === null || usageForLog === void 0 ? void 0 : usageForLog.outputTokens) || 0));
3621
+ agent_map_1.agentMapService.onFinalized({
3622
+ sessionId,
3623
+ agent: targetType,
3624
+ source: _akCtx ? 'access-key' : 'global',
3625
+ keyId: (_b = _akCtx === null || _akCtx === void 0 ? void 0 : _akCtx.accessKey) === null || _b === void 0 ? void 0 : _b.id,
3626
+ keyName: (_c = _akCtx === null || _akCtx === void 0 ? void 0 : _akCtx.accessKey) === null || _c === void 0 ? void 0 : _c.name,
3627
+ title: this.defaultExtractSessionTitle(req, sessionId),
3628
+ timestamp: Date.now(),
3629
+ statusCode,
3630
+ // 采用真正转发给上游的模型(转换+覆盖后的 requestBody.model),而非 rule.targetModel 预测值,
3631
+ // 避免规则未配置 targetModel 时回退成编程工具提交的 req.body.model
3632
+ model: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_d = req.body) === null || _d === void 0 ? void 0 : _d.model),
3633
+ tokensDelta: _tokensDelta,
3634
+ body: req.body,
3635
+ downstreamResponseBody: downstreamResponseBodyForLog !== null && downstreamResponseBodyForLog !== void 0 ? downstreamResponseBodyForLog : responseBodyForLog,
3636
+ responseBody: responseBodyForLog,
3637
+ });
3638
+ }
3605
3639
  const isError = statusCode >= 400;
3606
3640
  if (isError) {
3607
3641
  console.log(`\x1b[31m[Request Error]\x1b[0m client=${targetType}, session=${sessionId}, rule=${rule.id}(${rule.contentType}), vendor=${(vendor === null || vendor === void 0 ? void 0 : vendor.name) || '-'}, service=${service.name}, status=${statusCode}, time=${Date.now() - startTime}ms${error ? `, error=${error}` : ''}`);
@@ -3610,7 +3644,7 @@ class ProxyServer {
3610
3644
  console.log(`\x1b[33m[Request End]\x1b[0m client=${targetType}, session=${sessionId}, rule=${rule.id}(${rule.contentType}), vendor=${(vendor === null || vendor === void 0 ? void 0 : vendor.name) || '-'}, service=${service.name}, status=${statusCode}, time=${Date.now() - startTime}ms`);
3611
3645
  }
3612
3646
  // 检查是否启用日志记录(默认启用)
3613
- const enableLogging = ((_b = this.config) === null || _b === void 0 ? void 0 : _b.enableLogging) !== false; // 默认为 true
3647
+ const enableLogging = ((_e = this.config) === null || _e === void 0 ? void 0 : _e.enableLogging) !== false; // 默认为 true
3614
3648
  if (!enableLogging) {
3615
3649
  return;
3616
3650
  }
@@ -3634,13 +3668,14 @@ class ProxyServer {
3634
3668
  error,
3635
3669
  contentType: rule.contentType,
3636
3670
  ruleId: rule.id,
3671
+ routeId: route === null || route === void 0 ? void 0 : route.id,
3637
3672
  targetType,
3638
3673
  targetServiceId: service.id,
3639
3674
  targetServiceName: service.name,
3640
- targetModel: rule.targetModel || ((_c = req.body) === null || _c === void 0 ? void 0 : _c.model),
3675
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_f = req.body) === null || _f === void 0 ? void 0 : _f.model),
3641
3676
  vendorId: service.vendorId,
3642
3677
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
3643
- requestModel: (_d = req.body) === null || _d === void 0 ? void 0 : _d.model,
3678
+ requestModel: (_g = req.body) === null || _g === void 0 ? void 0 : _g.model,
3644
3679
  tags: this.buildRelayTags(relayedForLog, useOriginalConfig),
3645
3680
  responseHeaders: responseHeadersForLog,
3646
3681
  responseBody: responseBodyForLog,
@@ -3674,7 +3709,7 @@ class ProxyServer {
3674
3709
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
3675
3710
  serviceId: service.id,
3676
3711
  serviceName: service.name,
3677
- model: rule.targetModel || ((_e = req.body) === null || _e === void 0 ? void 0 : _e.model),
3712
+ model: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_h = req.body) === null || _h === void 0 ? void 0 : _h.model),
3678
3713
  totalTokens: sessionTokens,
3679
3714
  }).catch(err => console.error('[KeySession] upsert error:', err));
3680
3715
  }
@@ -3698,13 +3733,14 @@ class ProxyServer {
3698
3733
  error,
3699
3734
  contentType: rule.contentType,
3700
3735
  ruleId: rule.id,
3736
+ routeId: route === null || route === void 0 ? void 0 : route.id,
3701
3737
  targetType,
3702
3738
  targetServiceId: service.id,
3703
3739
  targetServiceName: service.name,
3704
- targetModel: rule.targetModel || ((_f = req.body) === null || _f === void 0 ? void 0 : _f.model),
3740
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_j = req.body) === null || _j === void 0 ? void 0 : _j.model),
3705
3741
  vendorId: service.vendorId,
3706
3742
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
3707
- requestModel: (_g = req.body) === null || _g === void 0 ? void 0 : _g.model,
3743
+ requestModel: (_k = req.body) === null || _k === void 0 ? void 0 : _k.model,
3708
3744
  tags: this.buildRelayTags(relayedForLog, useOriginalConfig),
3709
3745
  });
3710
3746
  }
@@ -3717,7 +3753,7 @@ class ProxyServer {
3717
3753
  const vendors = this.dbManager.getVendors();
3718
3754
  const vendorForLog = vendors.find(v => v.id === service.vendorId);
3719
3755
  // 从请求体中提取模型信息
3720
- const requestModel = (_h = req.body) === null || _h === void 0 ? void 0 : _h.model;
3756
+ const requestModel = (_l = req.body) === null || _l === void 0 ? void 0 : _l.model;
3721
3757
  const tagsForLog = this.buildRelayTags(relayedForLog, useOriginalConfig);
3722
3758
  if (extraTagsForLog.length > 0) {
3723
3759
  tagsForLog.push(...extraTagsForLog);
@@ -3736,10 +3772,11 @@ class ProxyServer {
3736
3772
  // 新增字段
3737
3773
  contentType: rule.contentType,
3738
3774
  ruleId: rule.id,
3775
+ routeId: route === null || route === void 0 ? void 0 : route.id,
3739
3776
  targetType,
3740
3777
  targetServiceId: service.id,
3741
3778
  targetServiceName: service.name,
3742
- targetModel: rule.targetModel || requestModel,
3779
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_m = req.body) === null || _m === void 0 ? void 0 : _m.model),
3743
3780
  vendorId: service.vendorId,
3744
3781
  vendorName: vendorForLog === null || vendorForLog === void 0 ? void 0 : vendorForLog.name,
3745
3782
  requestModel,
@@ -3767,7 +3804,7 @@ class ProxyServer {
3767
3804
  vendorName: vendorForLog === null || vendorForLog === void 0 ? void 0 : vendorForLog.name,
3768
3805
  serviceId: service.id,
3769
3806
  serviceName: service.name,
3770
- model: requestModel || rule.targetModel,
3807
+ model: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_o = req.body) === null || _o === void 0 ? void 0 : _o.model),
3771
3808
  totalTokens,
3772
3809
  highIqMode: rule.contentType === 'high-iq' ? true : existingSession === null || existingSession === void 0 ? void 0 : existingSession.highIqMode,
3773
3810
  highIqRuleId: rule.contentType === 'high-iq' ? rule.id : existingSession === null || existingSession === void 0 ? void 0 : existingSession.highIqRuleId,
@@ -3868,7 +3905,7 @@ class ProxyServer {
3868
3905
  targetType,
3869
3906
  targetServiceId: service.id,
3870
3907
  targetServiceName: service.name,
3871
- targetModel: rule.targetModel || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.model),
3908
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.model),
3872
3909
  vendorId: service.vendorId,
3873
3910
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
3874
3911
  requestModel: (_b = req.body) === null || _b === void 0 ? void 0 : _b.model,
@@ -3934,7 +3971,7 @@ class ProxyServer {
3934
3971
  // responses→responses 直连非 OpenAI 官方端点时,需降级兼容(剥离 custom/namespace 等私有工具与非标准字段)
3935
3972
  const sanitizeBody = clientFormat === 'responses' && (0, source_type_mapping_1.sourceTypeToFormat)(sourceType) === 'responses' && !(0, index_1.isOfficialOpenAiApi)(effectiveApiUrl || '');
3936
3973
  const transformedRequestBody = this.transformRequestToUpstream(targetType, sourceType, payloadForTransform, rule.targetModel, providerConfig, serverToolConfig, sanitizeBody);
3937
- requestBody = (_b = transformedRequestBody !== null && transformedRequestBody !== void 0 ? transformedRequestBody : this.cloneRequestBody(originalToolRequestBody)) !== null && _b !== void 0 ? _b : {};
3974
+ requestBody = (_d = transformedRequestBody !== null && transformedRequestBody !== void 0 ? transformedRequestBody : this.cloneRequestBody(originalToolRequestBody)) !== null && _d !== void 0 ? _d : {};
3938
3975
  // 对最终即将发送到上游的 Claude compact 请求再做一次兜底清理,
3939
3976
  // 避免中间转换/覆盖步骤重新引入未配对的 tool_use。
3940
3977
  if (rule.contentType === 'compact' && targetType === 'claude-code' && Array.isArray(requestBody === null || requestBody === void 0 ? void 0 : requestBody.messages)) {
@@ -4040,7 +4077,7 @@ class ProxyServer {
4040
4077
  let errorResponseData = response.data;
4041
4078
  if (streamRequested && response.data && typeof response.data.on === 'function') {
4042
4079
  const raw = yield this.readStreamBody(response.data);
4043
- errorResponseData = (_c = this.safeJsonParse(raw)) !== null && _c !== void 0 ? _c : raw;
4080
+ errorResponseData = (_e = this.safeJsonParse(raw)) !== null && _e !== void 0 ? _e : raw;
4044
4081
  }
4045
4082
  responseHeadersForLog = this.normalizeResponseHeaders(responseHeaders);
4046
4083
  yield handleUpstreamHttpError(response.status, errorResponseData, responseHeaders, contentType);
@@ -4078,10 +4115,10 @@ class ProxyServer {
4078
4115
  targetType,
4079
4116
  targetServiceId: service.id,
4080
4117
  targetServiceName: service.name,
4081
- targetModel: rule.targetModel || ((_d = req.body) === null || _d === void 0 ? void 0 : _d.model),
4118
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_f = req.body) === null || _f === void 0 ? void 0 : _f.model),
4082
4119
  vendorId: service.vendorId,
4083
4120
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4084
- requestModel: (_e = req.body) === null || _e === void 0 ? void 0 : _e.model,
4121
+ requestModel: (_g = req.body) === null || _g === void 0 ? void 0 : _g.model,
4085
4122
  responseTime: Date.now() - startTime,
4086
4123
  });
4087
4124
  }
@@ -4119,7 +4156,7 @@ class ProxyServer {
4119
4156
  ? new ClaudeCompactResponseSanitizer()
4120
4157
  : null;
4121
4158
  // 流式 model 回写:将上游返回的 model 改写为客户端请求时的原始模型名
4122
- const originalModel = (_f = req.body) === null || _f === void 0 ? void 0 : _f.model;
4159
+ const originalModel = (_h = req.body) === null || _h === void 0 ? void 0 : _h.model;
4123
4160
  const modelRewriter = originalModel ? new model_rewrite_transform_1.ModelRewriteTransform(originalModel) : null;
4124
4161
  responseHeadersForLog = this.normalizeResponseHeaders(responseHeaders);
4125
4162
  // 使用 transformSSEToTool 方法选择转换器
@@ -4219,10 +4256,10 @@ class ProxyServer {
4219
4256
  targetType,
4220
4257
  targetServiceId: service.id,
4221
4258
  targetServiceName: service.name,
4222
- targetModel: rule.targetModel || ((_g = req.body) === null || _g === void 0 ? void 0 : _g.model),
4259
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_j = req.body) === null || _j === void 0 ? void 0 : _j.model),
4223
4260
  vendorId: service.vendorId,
4224
4261
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4225
- requestModel: (_h = req.body) === null || _h === void 0 ? void 0 : _h.model,
4262
+ requestModel: (_k = req.body) === null || _k === void 0 ? void 0 : _k.model,
4226
4263
  responseTime: Date.now() - startTime,
4227
4264
  });
4228
4265
  }
@@ -4266,10 +4303,10 @@ class ProxyServer {
4266
4303
  targetType,
4267
4304
  targetServiceId: service.id,
4268
4305
  targetServiceName: service.name,
4269
- targetModel: rule.targetModel || ((_j = req.body) === null || _j === void 0 ? void 0 : _j.model),
4306
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_l = req.body) === null || _l === void 0 ? void 0 : _l.model),
4270
4307
  vendorId: service.vendorId,
4271
4308
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4272
- requestModel: (_k = req.body) === null || _k === void 0 ? void 0 : _k.model,
4309
+ requestModel: (_m = req.body) === null || _m === void 0 ? void 0 : _m.model,
4273
4310
  responseTime: Date.now() - startTime,
4274
4311
  });
4275
4312
  }
@@ -4288,7 +4325,7 @@ class ProxyServer {
4288
4325
  let responseData = response.data;
4289
4326
  if (streamRequested && response.data && typeof response.data.on === 'function' && !isEventStream) {
4290
4327
  const raw = yield this.readStreamBody(response.data);
4291
- responseData = (_l = this.safeJsonParse(raw)) !== null && _l !== void 0 ? _l : raw;
4328
+ responseData = (_o = this.safeJsonParse(raw)) !== null && _o !== void 0 ? _o : raw;
4292
4329
  }
4293
4330
  // 收集响应头
4294
4331
  responseHeadersForLog = this.normalizeResponseHeaders(responseHeaders);
@@ -4310,7 +4347,7 @@ class ProxyServer {
4310
4347
  usageForLog = this.extractTokenUsageFromResponse(responseData, sourceType);
4311
4348
  console.log('[Proxy] Non-stream response: extracted usageForLog:', usageForLog);
4312
4349
  // 回写 model 字段:将上游返回的 model 改写为客户端请求时的原始模型名
4313
- const originalModel = (_m = req.body) === null || _m === void 0 ? void 0 : _m.model;
4350
+ const originalModel = (_p = req.body) === null || _p === void 0 ? void 0 : _p.model;
4314
4351
  (0, model_rewrite_transform_1.rewriteResponseModel)(normalizedConverted, originalModel);
4315
4352
  (0, model_rewrite_transform_1.rewriteResponseModel)(responseData, originalModel);
4316
4353
  this.copyResponseHeaders(responseHeaders, res);
@@ -4375,10 +4412,10 @@ class ProxyServer {
4375
4412
  targetType,
4376
4413
  targetServiceId: service.id,
4377
4414
  targetServiceName: service.name,
4378
- targetModel: rule.targetModel || ((_o = req.body) === null || _o === void 0 ? void 0 : _o.model),
4415
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_q = req.body) === null || _q === void 0 ? void 0 : _q.model),
4379
4416
  vendorId: service.vendorId,
4380
4417
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4381
- requestModel: (_p = req.body) === null || _p === void 0 ? void 0 : _p.model,
4418
+ requestModel: (_r = req.body) === null || _r === void 0 ? void 0 : _r.model,
4382
4419
  upstreamRequest: upstreamRequestForLog,
4383
4420
  responseTime: Date.now() - startTime,
4384
4421
  });
@@ -4393,7 +4430,7 @@ class ProxyServer {
4393
4430
  console.error('Proxy error:', error);
4394
4431
  // 检测是否是 timeout 错误
4395
4432
  const isTimeout = error.code === 'ECONNABORTED' ||
4396
- ((_q = error.message) === null || _q === void 0 ? void 0 : _q.toLowerCase().includes('timeout')) ||
4433
+ ((_s = error.message) === null || _s === void 0 ? void 0 : _s.toLowerCase().includes('timeout')) ||
4397
4434
  (error.errno && error.errno === 'ETIMEDOUT');
4398
4435
  const statusCode = isTimeout ? 504 : this.getErrorStatusCode(error, 500);
4399
4436
  const baseErrorMessage = isTimeout
@@ -4419,10 +4456,10 @@ class ProxyServer {
4419
4456
  targetType,
4420
4457
  targetServiceId: service.id,
4421
4458
  targetServiceName: service.name,
4422
- targetModel: rule.targetModel || ((_r = req.body) === null || _r === void 0 ? void 0 : _r.model),
4459
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_t = req.body) === null || _t === void 0 ? void 0 : _t.model),
4423
4460
  vendorId: service.vendorId,
4424
4461
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4425
- requestModel: (_s = req.body) === null || _s === void 0 ? void 0 : _s.model,
4462
+ requestModel: (_u = req.body) === null || _u === void 0 ? void 0 : _u.model,
4426
4463
  upstreamRequest: upstreamRequestForLog,
4427
4464
  responseHeaders: responseHeadersForLog,
4428
4465
  responseTime: Date.now() - startTime,
@@ -4693,7 +4730,7 @@ class ProxyServer {
4693
4730
  this.emitPerformance({
4694
4731
  statusCode, startTime, usage: usageForLog, streamTiming,
4695
4732
  service, vendorId: vendor === null || vendor === void 0 ? void 0 : vendor.id, vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4696
- model: rule.targetModel || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.model),
4733
+ model: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.model),
4697
4734
  });
4698
4735
  // AccessKey 独立日志处理
4699
4736
  const accessKeyCtx = req._accessKeyCtx;
@@ -4711,9 +4748,10 @@ class ProxyServer {
4711
4748
  error,
4712
4749
  contentType: rule.contentType,
4713
4750
  ruleId: rule.id,
4751
+ routeId: route === null || route === void 0 ? void 0 : route.id,
4714
4752
  targetServiceId: service.id,
4715
4753
  targetServiceName: service.name,
4716
- targetModel: rule.targetModel || ((_b = req.body) === null || _b === void 0 ? void 0 : _b.model),
4754
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_b = req.body) === null || _b === void 0 ? void 0 : _b.model),
4717
4755
  vendorId: service.vendorId,
4718
4756
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4719
4757
  requestModel: (_c = req.body) === null || _c === void 0 ? void 0 : _c.model,
@@ -4742,7 +4780,7 @@ class ProxyServer {
4742
4780
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4743
4781
  serviceId: service.id,
4744
4782
  serviceName: service.name,
4745
- model: rule.targetModel || ((_d = req.body) === null || _d === void 0 ? void 0 : _d.model),
4783
+ model: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_d = req.body) === null || _d === void 0 ? void 0 : _d.model),
4746
4784
  totalTokens: sessionTokens,
4747
4785
  }).catch(err => console.error('[KeySession] upsert error:', err));
4748
4786
  }
@@ -4764,9 +4802,10 @@ class ProxyServer {
4764
4802
  error,
4765
4803
  contentType: rule.contentType,
4766
4804
  ruleId: rule.id,
4805
+ routeId: route === null || route === void 0 ? void 0 : route.id,
4767
4806
  targetServiceId: service.id,
4768
4807
  targetServiceName: service.name,
4769
- targetModel: rule.targetModel || ((_e = req.body) === null || _e === void 0 ? void 0 : _e.model),
4808
+ targetModel: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || ((_e = req.body) === null || _e === void 0 ? void 0 : _e.model),
4770
4809
  vendorId: service.vendorId,
4771
4810
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
4772
4811
  requestModel: (_f = req.body) === null || _f === void 0 ? void 0 : _f.model,
@@ -0,0 +1 @@
1
+ .nav-item-with-tooltip{position:relative;display:block}.nav-tooltip{position:fixed;transform:translateY(-50%);background:var(--bg-card);color:var(--text-primary);padding:8px 12px;border-radius:8px;font-size:14px;font-weight:500;white-space:nowrap;z-index:999999;pointer-events:none;box-shadow:0 4px 12px #00000026;border:1px solid var(--border-primary);animation:tooltipFadeIn .2s ease-out}.nav-tooltip:before{content:"";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:6px solid transparent;border-right-color:var(--border-primary)}.nav-tooltip:after{content:"";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:5px solid transparent;border-right-color:var(--bg-card);margin-right:-1px}@keyframes tooltipFadeIn{0%{opacity:0;transform:translateY(-50%) translate(-8px)}to{opacity:1;transform:translateY(-50%) translate(0)}}.app{display:flex;width:100%;height:100%;overflow:hidden}.sidebar{width:260px;background:var(--bg-sidebar);color:var(--text-primary);display:flex;flex-direction:column;padding:0 0 80px;border-radius:12px;box-shadow:0 4px 12px #00000026;margin:8px;position:relative;z-index:999999;border:1px solid rgba(255,255,255,.15);transition:width .1s ease,margin .1s ease}.sidebar.collapsed{width:80px;margin-right:8px;overflow:visible}.sidebar.collapsed .logo h2,.sidebar.collapsed .nav-menu .nav-text,.sidebar.collapsed .github-link,.sidebar.collapsed .version-info,.sidebar.collapsed .theme-mode-btn{display:none;opacity:0}.sidebar.collapsed .logo{padding:20px 10px}.sidebar.collapsed .nav-menu a{padding:14px;justify-content:center}.sidebar.collapsed .theme-toggle{flex-direction:column;gap:8px;align-items:center}.logo{padding:16px 20px;border-bottom:2px solid rgba(255,255,255,.2);text-align:center;position:relative;overflow:hidden;transition:padding .3s ease;display:flex;flex-direction:row;align-items:center;justify-content:center;gap:12px}.logo h2{font-size:18px;font-weight:800;background:linear-gradient(135deg,#fff,#f8f8ff,#e6e6fa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;text-shadow:0 2px 4px rgba(0,0,0,.1);position:relative;z-index:1;letter-spacing:1px;text-transform:uppercase;transition:all .3s ease;margin:0}.logo-image{width:36px;height:36px;object-fit:contain;transition:all .3s ease;filter:drop-shadow(0 2px 8px rgba(0,0,0,.2));flex-shrink:0}.sidebar.collapsed .logo{justify-content:center;padding:12px 10px}.sidebar.collapsed .logo-image{width:32px;height:32px}.nav-menu-wrap{position:relative;flex:1;min-height:0}.nav-menu{list-style:none;padding:16px 0;margin:0;height:100%;overflow-y:auto;overflow-x:visible;scrollbar-width:thin;scrollbar-color:var(--border-secondary) transparent}.nav-menu::-webkit-scrollbar{width:6px}.nav-menu::-webkit-scrollbar-track{background:transparent}.nav-menu::-webkit-scrollbar-thumb{background:var(--border-secondary);border-radius:4px}.nav-menu::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.nav-scroll-shadow{position:absolute;left:0;right:0;height:48px;pointer-events:none;z-index:10;transition:opacity .2s ease}.nav-scroll-shadow--top{top:0;background:linear-gradient(to bottom,#0F5132 0%,rgba(15,81,50,.85) 45%,transparent 100%)}.nav-scroll-shadow--bottom{bottom:0;display:flex;align-items:flex-end;justify-content:center;padding-bottom:4px;background:linear-gradient(to top,#065F46 0%,rgba(6,95,70,.85) 45%,transparent 100%)}.nav-scroll-shadow.is-hidden{opacity:0}.nav-scroll-wheel{display:inline-flex;align-items:center;justify-content:center;color:var(--text-sidebar);opacity:.55;animation:nav-wheel-hint 1.6s ease-in-out infinite}.nav-scroll-wheel svg{width:16px;height:16px}@keyframes nav-wheel-hint{0%,to{transform:translateY(0)}50%{transform:translateY(3px)}}.nav-menu li{margin:8px 12px;overflow:visible}.nav-menu a{display:flex;align-items:center;gap:12px;padding:14px 20px;color:var(--text-sidebar);text-decoration:none;border-radius:12px;transition:all .3s ease-out;font-weight:500;position:relative;overflow:hidden}.nav-menu .nav-icon{font-size:20px;flex-shrink:0;transition:all .3s ease}.nav-menu .nav-text{transition:opacity .3s ease,width .3s ease;white-space:nowrap}.nav-menu a:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.15),transparent);transition:left .5s ease-out}.nav-menu a:hover:before{left:100%}.nav-menu a:hover{background-color:#ffffff1a}.nav-menu a.active{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary))}.main-content{flex:1;padding:24px;overflow:auto;background:var(--bg-primary);position:relative;margin:16px 16px 16px 0;border-radius:20px;transition:margin .3s ease}.main-content>.routes-page,.main-content>.vendors-page,.main-content>.logs-page{min-width:1000px}.page-header{margin-bottom:24px;position:relative;z-index:1}.page-header-content{display:flex;justify-content:space-between;align-items:center;gap:20px}.page-header-text h1{font-family:Fredoka,sans-serif;font-size:28px;font-weight:700;color:var(--accent-primary);margin-bottom:8px;text-shadow:0 2px 4px var(--shadow-primary)}.page-header-text p{color:var(--text-muted);font-size:16px;font-weight:400}.card{background:var(--bg-card);border-radius:20px;padding:24px;box-shadow:0 2px 8px #00000014;margin-bottom:24px;border:1px solid var(--border-primary);position:relative;overflow:hidden;transition:all .3s ease-out}.card:before{content:"";position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,var(--accent-primary),var(--accent-secondary));border-radius:20px 20px 0 0}.card:hover{box-shadow:0 4px 12px #0000001f}.btn{padding:10px 20px;border:none;border-radius:12px;cursor:pointer;font-size:14px;font-weight:600;transition:all .3s ease-out;position:relative;overflow:hidden;white-space:nowrap}.btn:before{content:"";position:absolute;top:50%;left:50%;width:0;height:0;background:#ffffff4d;border-radius:50%;transform:translate(-50%,-50%);transition:width .6s,height .6s}.btn:hover:before{width:300px;height:300px}.btn:active{transform:translateY(0)}.btn:disabled{opacity:.4;cursor:not-allowed}.btn-primary{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;position:relative;overflow:hidden}.btn-primary:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.3),transparent);transition:left .5s ease}.btn-primary:hover:before{left:100%}.btn-danger{background:var(--accent-danger);color:#fff}.btn-danger:hover{background:#ea580c}.btn-success{background:var(--accent-success);color:#fff}.btn-success:hover{background:#059669}.btn-secondary{background:var(--accent-secondary);color:#fff}.btn-secondary:hover{background:var(--accent-primary)}.btn-sm{padding:4px 8px}table{width:100%;border-collapse:collapse;margin-top:16px;border-radius:12px;overflow:hidden}table th,table td{padding:16px;text-align:left;border-bottom:1px solid var(--border-secondary);color:var(--text-primary)}table th{background:var(--bg-table-header);font-weight:700;color:var(--text-secondary);font-family:Fredoka,sans-serif;font-size:14px;white-space:nowrap}@media (max-width: 1200px){.rules-table .col-priority{display:none}.rules-table .action-buttons{flex-wrap:wrap;justify-content:flex-end}}.col-priority-box{display:flex;align-items:center;gap:4px}.priority-arrow-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;background-color:var(--bg-card);border:1px solid var(--border-primary);border-radius:4px;color:var(--text-primary);font-size:12px;line-height:1;cursor:pointer;transition:all .2s ease;vertical-align:middle}.priority-arrow-btn:hover{background-color:var(--primary-color);border-color:var(--primary-color);color:#fff}.priority-arrow-btn:active{transform:scale(.95)}.form-group{margin-bottom:20px}.form-group label{display:block;margin-bottom:8px;color:var(--text-primary);font-size:14px;font-weight:600;font-family:Nunito,sans-serif}.form-group input,.form-group select,.form-group textarea{width:100%;padding:12px 16px;border:2px solid var(--border-primary);border-radius:12px;font-size:14px;font-family:Nunito,sans-serif;background:var(--bg-secondary);color:var(--text-primary);transition:all .3s ease-out}.form-group input:focus,.form-group select:focus,.form-group textarea:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 3px var(--shadow-secondary);background:var(--bg-secondary)}.form-group input:disabled,.form-group select:disabled,.form-group textarea:disabled{background:var(--bg-input-disabled);cursor:not-allowed;color:var(--text-input-disabled);opacity:.7}.form-group label small{font-size:.8em;color:var(--text-light)}@keyframes modalFadeIn{0%{opacity:0}to{opacity:1}}.modal{background:#fff;border-radius:24px;padding:32px 28px 32px 32px;min-width:500px;width:800px;max-width:90%;max-height:90vh;box-shadow:0 8px 24px #0003;border:1px solid var(--border-secondary);animation:modalSlideIn .4s ease-out;overflow:hidden;display:flex;flex-direction:column}.modal-container{overflow-y:auto;overflow-x:hidden;margin-right:-8px;padding-right:8px;scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--border-secondary) transparent}.modal-container::-webkit-scrollbar{width:8px}.modal-container::-webkit-scrollbar-track{background:transparent;border-radius:4px;margin:4px}.modal-container::-webkit-scrollbar-thumb{background:var(--border-secondary);border-radius:4px;border:2px solid transparent}.modal-container::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:#0009;display:flex;justify-content:center;align-items:center;z-index:1000000;animation:modalFadeIn .3s ease-out}.modal-close-btn{position:absolute;top:20px;right:20px;width:48px;height:48px;border:none;background:var(--accent-light);color:var(--text-primary);font-size:32px;line-height:1;cursor:pointer;border-radius:50%;transition:all .2s ease;display:flex;align-items:center;justify-content:center;padding:0;border:1px solid rgba(255,255,255,.3);z-index:1001}.modal-close-btn:hover{background:var(--accent-danger);color:#fff;transform:scale(1.05)}.modal-close-btn:active{transform:scale(.95)}@keyframes modalSlideIn{0%{opacity:0;transform:translateY(-20px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.modal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;position:relative}.modal-header h2{font-size:20px;color:var(--text-primary);flex:1}.modal-footer{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}.modal--sticky-layout .modal-header{flex-shrink:0;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid var(--border-color)}.modal--sticky-layout .modal-body-scrollable{flex:1;overflow-y:auto;overflow-x:hidden;min-height:0;scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--border-secondary) transparent;padding-right:8px;margin-right:-8px}.modal--sticky-layout .modal-body-scrollable::-webkit-scrollbar{width:8px}.modal--sticky-layout .modal-body-scrollable::-webkit-scrollbar-track{background:transparent}.modal--sticky-layout .modal-body-scrollable::-webkit-scrollbar-thumb{background-color:var(--border-secondary);border-radius:4px}.modal--sticky-layout .modal-footer{flex-shrink:0;margin-top:0;padding-top:16px;border-top:1px solid var(--border-color)}.session-refresh-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-primary-solid);color:var(--text-secondary);cursor:pointer;transition:all .2s ease}.session-refresh-btn:hover:not(:disabled){background:var(--bg-hover);color:var(--text-primary)}.session-refresh-btn:disabled{opacity:.5;cursor:not-allowed}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.spin-icon{animation:spin 1s linear infinite}.session-view-toggle{display:inline-flex;gap:0;border:1px solid var(--border-color);border-radius:8px;overflow:hidden;margin-left:12px}.session-view-toggle button{padding:4px 14px;font-size:13px;border:none;background:var(--bg-primary);color:var(--text-secondary);cursor:pointer;transition:all .2s ease;white-space:nowrap}.session-view-toggle button:not(:last-child){border-right:1px solid var(--border-color)}.session-view-toggle button.active{background:var(--accent-light);color:var(--text-primary);font-weight:600}.session-view-toggle button:hover:not(.active){background:var(--bg-hover)}.chat-view-container{display:flex;flex-direction:column;gap:16px;padding:4px 0;position:relative}.chat-scroll-bottom{position:sticky;bottom:0;left:50%;display:flex;justify-content:center;padding:8px 0;pointer-events:none}.chat-scroll-bottom-btn{pointer-events:auto;width:36px;height:36px;border-radius:50%;border:1px solid var(--border-color);background:var(--bg-secondary);color:var(--text-secondary);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;box-shadow:0 2px 8px #00000026}.chat-scroll-bottom-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.chat-message{display:flex;flex-direction:column}.chat-message--user{align-items:flex-end}.chat-message--assistant{align-items:flex-start}.chat-bubble{max-width:85%;padding:10px 14px;border-radius:12px;font-size:14px;line-height:1.6;word-break:break-word;white-space:pre-wrap}.chat-tool-header{font-size:12px;color:var(--text-tertiary);font-style:italic;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.55}.chat-collapse-top-bar{display:flex;align-items:center;justify-content:flex-end;gap:8px;position:sticky;top:0;z-index:1;margin-bottom:8px}.chat-message--user .chat-bubble{background:var(--accent-light);color:var(--text-primary);border-bottom-right-radius:4px}.chat-message--assistant .chat-bubble{background:var(--bg-primary-solid);color:var(--text-primary);border:1px solid var(--border-color);border-bottom-left-radius:4px}.chat-message--tool_result .chat-bubble,.chat-message--tool_use .chat-bubble{background:#f0f4f1;border:1px solid var(--border-color);opacity:.85}.chat-message--tool_result .chat-bubble{border-bottom-right-radius:4px}.chat-message--tool_use .chat-bubble{border-bottom-left-radius:4px}.chat-meta{font-size:12px;color:var(--text-tertiary);margin-top:4px;padding:0 4px}.chat-thinking{margin-bottom:8px;border:1px solid var(--border-thinking-box);border-radius:8px;padding:8px 10px;background-color:var(--bg-thinking-box)}.chat-thinking summary{cursor:pointer;font-weight:600;font-size:13px;color:var(--text-thinking-title);-webkit-user-select:none;user-select:none}.chat-thinking pre{margin-top:8px;white-space:pre-wrap;word-break:break-word;font-family:monospace;font-size:13px;color:var(--text-thinking-content);background-color:var(--bg-thinking-content);padding:8px;border-radius:4px;border:1px solid var(--border-thinking-content)}.chat-content-text{font-size:14px;line-height:1.6;white-space:pre-wrap;word-break:break-word}.chat-collapsible-wrapper{position:relative}.chat-collapse-btn-sticky{display:flex;justify-content:flex-end;flex-shrink:0;position:absolute;top:0;right:0}.chat-collapse-fade{position:relative;margin-top:-60px;height:60px;display:flex;align-items:center;justify-content:center;cursor:pointer}.chat-message--user .chat-collapse-fade{background:linear-gradient(to bottom,transparent,var(--accent-light))}.chat-message--assistant .chat-collapse-fade{background:linear-gradient(to bottom,transparent,var(--bg-primary-solid))}.chat-message--tool_result .chat-collapse-fade,.chat-message--tool_use .chat-collapse-fade{background:linear-gradient(to bottom,transparent,#f0f4f1)}[data-theme=dark] .chat-message--user .chat-bubble{background:#007b33}[data-theme=dark] .chat-message--assistant .chat-bubble{background:#064d00}[data-theme=dark] .chat-message--tool_result .chat-bubble,[data-theme=dark] .chat-message--tool_use .chat-bubble{background:#223227}[data-theme=dark] .chat-message--user .chat-collapse-fade{background:linear-gradient(to bottom,transparent,#007b33)}[data-theme=dark] .chat-message--assistant .chat-collapse-fade{background:linear-gradient(to bottom,transparent,#064d00)}[data-theme=dark] .chat-message--tool_result .chat-collapse-fade,[data-theme=dark] .chat-message--tool_use .chat-collapse-fade{background:linear-gradient(to bottom,transparent,#223227)}.chat-collapse-fade-btn{font-size:12px;color:var(--text-secondary);background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:4px;padding:1px 10px;cursor:pointer;transition:all .2s ease;margin-top:40px}.chat-collapse-fade-btn:hover{background:var(--bg-secondary)}.chat-collapse-btn{display:inline-block;padding:2px 10px;font-size:12px;color:var(--text-secondary);background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:4px;cursor:pointer;transition:all .2s ease}.chat-collapse-btn:hover{background:var(--bg-secondary)}.chat-separator{text-align:center;color:var(--text-tertiary);font-size:12px;padding:4px 0;position:relative}.chat-separator:before,.chat-separator:after{content:"";position:absolute;top:50%;width:calc(50% - 40px);height:1px;background:var(--border-color)}.chat-separator:before{left:0}.chat-separator:after{right:0}.action-buttons{display:flex;gap:8px}.badge{display:inline-block;padding:6px 12px;border-radius:20px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;white-space:nowrap}.badge-success{background:var(--accent-success);color:#fff}.badge-warning{background:var(--accent-warning);color:#fff}.badge-danger{background:var(--accent-danger);color:#fff}.badge-claude-code{background:#ce653c;color:#fff}.badge-codex{background:#1f2937;color:#fff}.empty-state{text-align:center;padding:60px 20px;color:var(--text-muted);font-family:Nunito,sans-serif}.empty-state p{margin-bottom:20px;font-size:16px;font-weight:500}.pagination{display:flex;justify-content:space-between;align-items:center;margin-top:20px;padding:16px;background:var(--bg-secondary);border-radius:12px;border:1px solid var(--border-primary);gap:20px;flex-wrap:wrap}.pagination-info{color:var(--text-muted);font-size:14px;font-weight:500}.pagination-controls{display:flex;align-items:center;gap:10px}.pagination-btn{padding:6px 14px;border:1px solid var(--border-primary);background:var(--bg-card);color:var(--text-primary);border-radius:8px;cursor:pointer;font-size:14px;font-weight:500;transition:all .2s ease}.pagination-btn:hover:not(:disabled){background:var(--accent-primary);color:#fff;border-color:var(--accent-primary)}.pagination-btn:disabled{opacity:.4;cursor:not-allowed;background:var(--bg-secondary)}.pagination-size{display:flex;align-items:center;gap:8px;color:var(--text-primary);font-size:14px;font-weight:500}.pagination-select{padding:6px 10px;border:1px solid var(--border-primary);background:var(--bg-card);color:var(--text-primary);border-radius:8px;cursor:pointer;font-size:14px;outline:none;transition:all .2s ease}.pagination-select:hover{border-color:var(--accent-primary)}.pagination-select:focus{border-color:var(--accent-primary);box-shadow:0 0 0 2px #3498db1a}.toolbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.theme-toggle{position:absolute;bottom:20px;left:20px;right:20px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.sidebar-toggle-btn{background:none;border:none;color:var(--text-sidebar);cursor:pointer;padding:8px;border-radius:8px;transition:all .3s ease-out;display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:700}.sidebar-toggle-btn:hover{background:#ffffff26;opacity:1}.github-link{background:none;border:none;color:var(--text-sidebar);cursor:pointer;padding:8px;border-radius:8px;transition:all .3s ease-out;display:flex;align-items:center;justify-content:center;opacity:.8;margin-left:auto}.github-link:hover{background:#ffffff26;opacity:1}.theme-toggle button{background:none;border:none;color:var(--text-sidebar);cursor:pointer;padding:8px;border-radius:8px;transition:all .3s ease-out;display:flex;align-items:center;justify-content:center}.version-info{font-size:12px;color:var(--text-sidebar);opacity:.7;font-family:Monaco,Menlo,monospace;font-weight:500;-webkit-user-select:none;user-select:none;transition:opacity .3s ease}.theme-toggle button:hover{background:#ffffff26}.theme-toggle button.active{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;animation:pulse 2s ease-in-out infinite}@keyframes pulse{0%,to{transform:scale(1)}50%{transform:scale(1.05)}}@property --border-angle{syntax: "<angle>"; initial-value: 0deg; inherits: false;}.route-active-glow{border-color:transparent!important}.route-active-glow:before{content:"";position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;border-radius:9px;padding:1.5px;background:conic-gradient(from var(--border-angle, 0deg),transparent 0%,transparent 55%,var(--color-primary, #2980b9) 75%,var(--color-primary, #2980b9) 82%,transparent 100%);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;animation:border-angle-rotate 2.5s linear infinite;pointer-events:none}@keyframes border-angle-rotate{to{--border-angle: 360deg}}.version-info-wrapper{position:relative;display:flex;align-items:center}.version-info{font-size:12px;color:var(--text-sidebar);opacity:.7;font-family:Monaco,Menlo,monospace;font-weight:500;-webkit-user-select:none;user-select:none;transition:opacity .3s ease;position:relative}.version-info:hover{opacity:1}.version-badge{position:absolute;top:-2px;right:-6px;width:8px;height:8px;background:linear-gradient(135deg,#ef4444,#dc2626);border-radius:50%;border:2px solid var(--bg-sidebar);animation:pulse-badge 2s ease-in-out infinite;box-shadow:0 0 8px #ef444499}@keyframes pulse-badge{0%,to{transform:scale(1);opacity:1}50%{transform:scale(1.2);opacity:.8}}.version-update-popup{position:absolute;bottom:calc(100% + 12px);left:50%;transform:translate(-50%);background:#10b98126;border:1px solid rgba(16,185,129,.4);border-radius:12px;padding:12px;min-width:220px;box-shadow:0 4px 12px #0003;opacity:0;visibility:hidden;transition:all .3s ease-out;pointer-events:none;z-index:10000;text-decoration:none;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.version-info-wrapper:hover .version-update-popup,.version-update-popup:hover{opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%) translateY(12px)}.version-update-popup:after{content:"";position:absolute;top:100%;left:50%;transform:translate(-50%);border:6px solid transparent;border-top-color:#10b98166}.update-popup-content{display:flex;align-items:flex-start;gap:10px}.update-icon{font-size:18px;animation:bounce 1s ease-in-out infinite;flex-shrink:0}@keyframes bounce{0%,to{transform:translateY(0)}50%{transform:translateY(-3px)}}.update-text{flex:1}.update-title{font-size:13px;font-weight:600;color:var(--text-sidebar);margin-bottom:4px}.update-versions{font-size:11px;color:#fffc;font-family:Monaco,Menlo,monospace;margin-bottom:6px}.update-message{font-size:10px;color:var(--text-sidebar);line-height:1.4}.update-message code{background:#ffffff26;padding:2px 4px;border-radius:4px;font-family:Monaco,Menlo,monospace;margin-top:2px;display:inline-block;color:#ffffffe6}@media (prefers-reduced-motion: reduce){.nav-menu a,.card,.btn,.form-group input,.form-group select,.form-group textarea,.modal-overlay,.modal,table tr{transition:none;animation:none}.nav-menu a:hover,.card:hover,.btn:hover,table tr:hover{transform:none}.btn:before{display:none}}.markdown-content{line-height:1.7;color:var(--text-primary);max-width:100%;overflow-x:auto}.markdown-content h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.8em;color:var(--text-primary);border-bottom:2px solid var(--border-color);padding-bottom:.3em}.markdown-content h2{font-size:1.6em;font-weight:600;margin-top:1.5em;margin-bottom:.6em;color:var(--text-primary);border-bottom:1px solid var(--border-color);padding-bottom:.25em}.markdown-content h3{font-size:1.3em;font-weight:600;margin-top:1.2em;margin-bottom:.5em;color:var(--text-primary)}.markdown-content h4{font-size:1.1em;font-weight:600;margin-top:1em;margin-bottom:.4em;color:var(--text-primary)}.markdown-content p{margin-bottom:1em;line-height:1.8}.markdown-content ul,.markdown-content ol{margin-bottom:1em;padding-left:2em}.markdown-content li{margin-bottom:.5em;line-height:1.7}.markdown-content code{background:var(--bg-secondary);padding:.2em .4em;border-radius:4px;font-size:.9em;font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace;color:var(--accent-secondary);border:1px solid var(--border-color)}.markdown-content pre{background:var(--bg-secondary);padding:1em;border-radius:8px;overflow-x:auto;margin-bottom:1em;border:1px solid var(--border-color)}.markdown-content pre code{background:none;padding:0;border:none;color:var(--text-primary);font-size:.95em}.markdown-content blockquote{border-left:4px solid var(--accent-primary);padding-left:1em;margin-left:0;margin-bottom:1em;color:var(--text-secondary);font-style:italic}.markdown-content a{color:var(--accent-primary);text-decoration:none;border-bottom:1px solid var(--accent-secondary);transition:all .2s ease}.markdown-content a:hover{color:var(--accent-secondary)}.markdown-content table{width:100%;border-collapse:collapse;margin-bottom:1em}.markdown-content table th,.markdown-content table td{padding:.75em;text-align:left;border:1px solid var(--border-color)}.markdown-content table th{background:var(--bg-secondary);font-weight:600}.markdown-content hr{border:none;border-top:2px solid var(--border-color);margin:2em 0}.markdown-content strong{font-weight:600;color:var(--text-primary)}.markdown-content em{font-style:italic}.markdown-content img{max-width:100%;height:auto;border-radius:8px;margin:1em 0}.config-status-indicator{display:inline-block}.status-badge{display:inline-block;padding:4px 10px;border-radius:12px;font-size:11px;font-weight:600}.status-active{background:linear-gradient(135deg,#4caf50,#45a049);color:#fff}.status-backup{background:linear-gradient(135deg,#ff9800,#f57c00);color:#fff}.status-inactive{background:linear-gradient(135deg,#9e9e9e,#757575);color:#fff}.skills-header-row{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}.skills-header-actions{margin-top:8px}.skills-discover-btn{padding:18px 36px;font-size:16px;font-weight:700;border-radius:14px;box-shadow:0 4px 12px #00000026;position:relative}.skills-discover-btn:after{content:"";position:absolute;top:-6px;right:-6px;bottom:-6px;left:-6px;border-radius:18px;border:2px solid rgba(16,185,129,.35);opacity:.6}.skills-section-header{display:flex;flex-direction:column;gap:6px;margin-bottom:20px}.skills-section-header h2{margin:0;font-size:22px;color:var(--text-primary)}.skills-section-header span{color:var(--text-muted);font-size:14px}.skills-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:16px}.skill-card{display:flex;flex-direction:column;gap:16px;min-height:180px}.skill-title{font-size:18px;font-weight:700;color:var(--text-primary);margin-bottom:8px}.skill-description{color:var(--text-muted);font-size:14px;line-height:1.6;flex:1}.skill-tags{display:flex;flex-wrap:wrap;gap:8px}.skills-discover{display:flex;flex-direction:column;gap:20px}.skills-search-card{display:flex;flex-direction:column;gap:16px}.skills-search-header h3{margin:0 0 6px;font-size:18px;color:var(--text-primary)}.skills-search-header p{margin:0;color:var(--text-muted);font-size:14px}.skills-search-input{width:100%;padding:12px 14px;border-radius:12px;border:1px solid var(--border-primary);background:var(--bg-secondary);color:var(--text-primary);resize:vertical;min-height:120px;font-size:14px;line-height:1.6}.skills-search-actions{display:flex;justify-content:flex-end}.skills-results{display:flex;flex-direction:column;gap:16px}.skills-result-actions{display:flex;justify-content:flex-end;gap:8px}.badge-secondary{background:#0f513226;color:var(--text-primary)}.switch{position:relative;width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;background-color:#4b5563;transition:background-color .2s}.switch.active{background-color:var(--primary-color)}.switch:disabled{cursor:not-allowed;opacity:.6}.switch:focus{outline:none;box-shadow:0 0 0 2px var(--primary-color)}.switch::-moz-focus-inner{border:0}.rules-table .vendor-sevices-col>div{white-space:nowrap}.api-binding-card{padding:20px 24px!important}.api-binding-header{display:flex;align-items:center;gap:10px;margin-bottom:6px}.api-binding-header-icon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:10px;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;font-size:16px;flex-shrink:0}.api-binding-header h3{margin:0!important;font-size:18px!important;font-weight:700;letter-spacing:.2px}.api-binding-desc{margin:0 0 18px!important;font-size:13px!important;color:var(--text-muted)!important;line-height:1.6}.api-binding-baseurl-row{display:flex;align-items:center;margin:20px;border:1px solid var(--border-primary);border-radius:6px;overflow:hidden;background:var(--bg-secondary)}.api-binding-baseurl-prefix{flex-shrink:0;padding:6px 12px;font-size:12px;font-weight:600;color:var(--text-muted);background:var(--border-primary);letter-spacing:.3px}.api-binding-baseurl-value{flex:1;padding:6px 12px;font-family:monospace;font-size:13px;color:var(--text-secondary);-webkit-user-select:all;user-select:all}.api-binding-list{display:flex;flex-direction:column;gap:10px;margin:20px}.api-binding-row{display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:12px;background:var(--bg-secondary);border:1px solid var(--border-primary);transition:all .25s ease}.api-binding-row:hover{border-color:var(--accent-secondary);box-shadow:0 2px 8px var(--shadow-secondary)}.api-binding-path{flex:0 0 300px;padding:6px 12px;border-radius:8px;font-family:Monaco,Menlo,Consolas,monospace;font-size:13px;font-weight:600;letter-spacing:.3px;background:var(--bg-code);color:var(--accent-secondary);border:1px solid var(--border-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:all;user-select:all;transition:all .2s ease}.api-binding-path:hover{border-color:var(--accent-secondary)}.api-binding-path--disabled{background:var(--bg-route-item);color:var(--text-route-muted);opacity:.7}.api-binding-arrow{flex-shrink:0;color:var(--border-primary);font-size:16px;transition:color .2s ease}.api-binding-row:hover .api-binding-arrow{color:var(--accent-secondary)}.api-binding-control{flex:1;min-width:0;position:relative}.api-binding-control select,.api-binding-control input{width:100%;padding:8px 12px;border:1.5px solid var(--border-primary);border-radius:10px;font-size:13px;font-family:Nunito,sans-serif;background:var(--bg-card);color:var(--text-primary);transition:all .25s ease;-moz-appearance:none;appearance:none;-webkit-appearance:none;cursor:pointer}.api-binding-control input{cursor:text}.api-binding-control select{padding-right:32px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23065F46' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;background-size:14px}[data-theme=dark] .api-binding-control select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236EE7B7' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E")}.api-binding-control select:focus,.api-binding-control input:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 3px var(--shadow-secondary)}.api-binding-control select:hover,.api-binding-control input:hover{border-color:var(--accent-secondary)}.api-binding-status{flex-shrink:0;width:8px;height:8px;border-radius:50%;background:var(--border-primary);transition:all .3s ease}.api-binding-status--bound{background:var(--accent-success);box-shadow:0 0 6px var(--shadow-secondary)}.api-binding-footer{margin-top:18px;display:flex;align-items:center;justify-content:flex-end;gap:12px}.api-binding-save-btn{padding:14px 36px!important;font-size:16px!important;border-radius:14px!important;font-weight:700;letter-spacing:.3px;display:inline-flex;align-items:center;gap:8px}@media (max-width: 900px){.api-binding-row{flex-wrap:wrap}.api-binding-path{flex:1 1 100%}.api-binding-arrow{display:none}.api-binding-control{flex:1 1 100%}}.tool-binding-block{margin-bottom:20px;padding:16px 18px;border-radius:12px;background:var(--bg-secondary);border:1px solid var(--border-primary);transition:all .25s ease}.tool-binding-block:hover{border-color:var(--accent-secondary);box-shadow:0 2px 8px var(--shadow-secondary)}.tool-binding-label{display:flex;align-items:center;gap:8px;font-weight:600;font-size:14px;margin-bottom:12px;color:var(--text-primary)}.tool-binding-label-icon{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;font-size:13px;flex-shrink:0}.tool-binding-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.tool-binding-row select{flex:1;min-width:200px;padding:9px 32px 9px 12px;border:1.5px solid var(--border-primary);border-radius:10px;font-size:13px;font-family:Nunito,sans-serif;background-color:var(--bg-card);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23065F46' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;background-size:14px;color:var(--text-primary);-moz-appearance:none;appearance:none;-webkit-appearance:none;cursor:pointer;transition:all .25s ease}[data-theme=dark] .tool-binding-row select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236EE7B7' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E")}.tool-binding-row select:hover{border-color:var(--accent-secondary)}.tool-binding-row select:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 3px var(--shadow-secondary)}.tool-binding-row select:disabled{opacity:.5;cursor:not-allowed}.tool-binding-deactivate-btn{padding:8px 18px!important;font-size:13px!important;border-radius:10px!important;font-weight:600;white-space:nowrap}.tool-binding-desc{font-size:12px;color:var(--text-muted);margin-top:10px;line-height:1.6}.migration-source-info{background:var(--bg-secondary, #f5f6fa);border-radius:8px;padding:14px 18px;border:1px solid var(--border-primary)}.migration-source-title{font-size:16px;font-weight:600;color:var(--text-primary);margin-bottom:8px}.migration-source-meta{display:flex;gap:12px;align-items:center;font-size:13px;color:var(--text-tertiary);flex-wrap:wrap}.migration-source-meta .badge{padding:2px 10px;border-radius:12px;font-size:12px;font-weight:500}.migration-source-badge{padding:3px 12px!important;border-radius:20px!important;font-size:11px!important;font-weight:600!important;text-transform:uppercase;letter-spacing:.5px}.migration-badge-claude{background:#e67e22!important;color:#fff!important}.migration-badge-codex{background:#2d3436!important;color:#fff!important}.migration-tool-cards{display:flex;align-items:stretch;justify-content:flex-start;gap:12px}.migration-tool-card{position:relative;flex:1;display:flex;flex-direction:column;align-items:center;gap:8px;padding:18px 20px 14px;border:2px solid var(--border-primary);border-radius:12px;background:var(--bg-primary);cursor:pointer;transition:all .15s ease;font-family:inherit;color:var(--text-primary)}.migration-tool-card:hover{border-color:var(--accent-color, #6c5ce7);background:var(--bg-secondary, #f5f6fa);transform:translateY(-1px);box-shadow:0 2px 8px #0000000a}.migration-tool-card.active{border-color:var(--accent-color, #6c5ce7);background:linear-gradient(135deg,#6c5ce70a,#00b8940a);box-shadow:0 0 0 3px #6c5ce71a}.migration-tool-card.is-source{cursor:default;border-style:dashed;border-color:var(--border-primary);background:var(--bg-secondary, #f5f6fa)}.migration-tool-card.is-source:hover{transform:none;box-shadow:none;border-color:var(--border-primary);background:var(--bg-secondary, #f5f6fa)}.migration-tool-card-icon{width:52px;height:52px;border-radius:14px;display:flex;align-items:center;justify-content:center;background:var(--bg-secondary, #f5f6fa);color:var(--accent-color, #6c5ce7);transition:all .15s ease}.migration-tool-card.active .migration-tool-card-icon{background:#6c5ce71a}.migration-tool-card.is-source .migration-tool-card-icon{opacity:.7}.migration-tool-card-name{font-size:16px;font-weight:600;white-space:nowrap}.migration-tool-card-desc{font-size:12px;color:var(--text-tertiary);text-align:center}.migration-tool-card-badge{position:absolute;top:-8px;right:-8px;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;color:#fff;background:#8e44ad;box-shadow:0 2px 4px #8e44ad4d}.migration-arrow{flex-shrink:0;display:flex;align-items:center;color:var(--accent-color, #6c5ce7);opacity:.5}.migration-empty-hint{margin-top:16px;padding:20px 24px;border:1px dashed var(--border-primary);border-radius:10px;background:var(--bg-secondary, #f5f6fa);display:flex;align-items:center;gap:16px;color:var(--text-tertiary)}.migration-empty-hint svg{flex-shrink:0;opacity:.4}.migration-options label{color:var(--text-secondary)}.migration-options input[type=checkbox]{accent-color:var(--accent-color, #6c5ce7)}.migration-prompt-textarea{width:100%;min-height:400px;padding:12px;border:1px solid var(--border-primary);border-radius:8px;font-size:13px;font-family:SF Mono,Menlo,Monaco,Consolas,monospace;background-color:var(--input-bg);color:var(--text-primary);resize:vertical;line-height:1.5}.migration-prompt-textarea:focus{outline:none;border-color:var(--accent-color, #6c5ce7);box-shadow:0 0 0 2px #6c5ce71a}.migration-preview-footer{font-size:13px;color:var(--text-tertiary)}.migration-launch-result{animation:fadeIn .3s ease}@keyframes fadeIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}[data-theme=dark] .migration-source-info{background:var(--bg-secondary, #1e1e2e)}[data-theme=dark] .migration-tool-card{border-color:var(--border-primary, #3a3a5c);background:var(--bg-primary, #1a1a2e)}[data-theme=dark] .migration-tool-card:hover{border-color:var(--accent-color, #a29bfe);background:var(--bg-secondary, #1e1e2e)}[data-theme=dark] .migration-tool-card.active{border-color:var(--accent-color, #a29bfe);background:linear-gradient(135deg,#a29bfe0f,#00cec90f)}[data-theme=dark] .migration-tool-card.is-source,[data-theme=dark] .migration-tool-card.is-source:hover{border-color:var(--border-primary, #3a3a5c);background:var(--bg-secondary, #1e1e2e)}[data-theme=dark] .migration-tool-card-icon{background:var(--bg-secondary, #1e1e2e);color:var(--accent-color, #a29bfe)}[data-theme=dark] .migration-tool-card.active .migration-tool-card-icon{background:#a29bfe1a}[data-theme=dark] .migration-arrow{color:var(--accent-color, #a29bfe)}[data-theme=dark] .migration-prompt-textarea{background:var(--input-bg, #1e1e2e);color:var(--text-primary, #e0e0e0);border-color:var(--border-primary, #3a3a5c)}[data-theme=dark] .migration-options label,[data-theme=dark] .migration-source-meta{color:var(--text-secondary, #a0a0b8)}[data-theme=dark] .modal{background:#0f2415}.route-icon-popover{position:absolute;bottom:calc(100% + 6px);right:0;background:var(--bg-tooltip, #333);color:#fff;font-size:11px;padding:4px 8px;border-radius:4px;white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .15s ease;z-index:100;box-shadow:0 2px 8px #00000026}.route-icon-popover:after{content:"";position:absolute;top:100%;right:4px;border:4px solid transparent;border-top-color:var(--bg-tooltip, #333)}div:hover>.route-icon-popover{opacity:1}.am-page{display:flex;flex-direction:column;height:100%;min-height:0;padding:12px;gap:12px;box-sizing:border-box;position:relative}.am-header{display:flex;align-items:center;gap:16px;padding:10px 16px;border:1px solid var(--border-color);border-radius:12px;flex-wrap:wrap;background:var(--bg-card)}.am-title{display:flex;align-items:center;gap:10px}.am-title h2{margin:0;font-size:18px}.am-conn{font-size:12px;color:var(--text-muted)}.am-conn--on{color:var(--accent-success)}.am-stats{display:flex;gap:14px;flex:1;flex-wrap:wrap;font-size:13px;color:var(--text-muted)}.am-stat b{color:var(--text-light);margin-left:4px}.am-stat--active b{color:var(--accent-primary)}.am-stat--error b{color:var(--accent-danger)}.am-stat--idle b{color:var(--accent-warning)}.am-stat--completed b{color:var(--text-muted)}.am-actions{display:flex;gap:8px}.am-btn{padding:5px 12px;border:1px solid var(--border-color);background:var(--bg-secondary);color:var(--text-light);border-radius:6px;cursor:pointer;font-size:12px}.am-btn:hover{background:var(--bg-hover)}.am-btn--ghost{background:transparent}.am-btn:disabled{opacity:.5;cursor:not-allowed}.am-main{flex:1;display:flex;gap:12px;min-height:0;overflow:hidden}.am-canvas-wrap{flex:1;position:relative;background:var(--bg-primary);overflow:hidden;border:1px solid var(--border-color);border-radius:12px}.am-canvas{width:100%;height:100%;display:block}.am-link{stroke-width:1;opacity:.25}.am-link--active{stroke:var(--accent-primary)}.am-node{transition:opacity .2s}.am-node-glow{fill:none;stroke-width:0;opacity:0}.am-node-circle{fill:var(--bg-card);stroke:var(--border-color);stroke-width:2;transition:stroke .2s,fill .2s}.am-node-emoji{font-size:20px;pointer-events:none;-webkit-user-select:none;user-select:none}.am-node-title{font-size:11px;fill:var(--text-light);pointer-events:none;-webkit-user-select:none;user-select:none}.am-node-activity{font-size:10px;fill:var(--text-muted);pointer-events:none;-webkit-user-select:none;user-select:none}.am-node-source{font-size:9px;fill:var(--accent-secondary);pointer-events:none;-webkit-user-select:none;user-select:none}.am-node-busy-dot{fill:var(--accent-warning);stroke:var(--bg-card);stroke-width:1.5}.am-node--active .am-node-circle{stroke:var(--accent-primary);fill:color-mix(in srgb,var(--accent-primary) 18%,var(--bg-card))}.am-node--active .am-node-glow{stroke:var(--accent-primary);stroke-width:3;opacity:.5;animation:am-pulse 2s ease-in-out infinite}.am-node--active .am-node-emoji{fill:var(--accent-primary)}.am-node--idle .am-node-circle{stroke:var(--accent-warning);fill:color-mix(in srgb,var(--accent-warning) 12%,var(--bg-card))}.am-node--error .am-node-circle{stroke:var(--accent-danger);fill:color-mix(in srgb,var(--accent-danger) 18%,var(--bg-card))}.am-node--error .am-node-glow{stroke:var(--accent-danger);stroke-width:3;opacity:.5;animation:am-pulse 1.2s ease-in-out infinite}.am-node--completed{opacity:.55}.am-node--completed .am-node-circle{stroke:var(--border-secondary);stroke-dasharray:3 3}.am-node--selected .am-node-circle{stroke-width:4}.am-node--busy .am-node-circle{animation:am-pulse 1s ease-in-out infinite}@keyframes am-pulse{0%,to{opacity:.35;transform:scale(1)}50%{opacity:.8;transform:scale(1.08)}}.am-legend{position:absolute;bottom:12px;left:12px;display:flex;gap:14px;font-size:12px;color:var(--text-muted);background:var(--bg-card);padding:6px 12px;border-radius:6px;border:1px solid var(--border-color)}.am-dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:4px;vertical-align:middle}.am-dot--active{background:var(--accent-primary)}.am-dot--idle{background:var(--accent-warning)}.am-dot--completed{background:var(--border-secondary)}.am-dot--error{background:var(--accent-danger)}.am-empty-canvas{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-muted);text-align:center}.am-empty-icon{font-size:64px;margin-bottom:12px;opacity:.6}.am-empty-hint{font-size:12px;opacity:.8}.am-detail{width:380px;border:1px solid var(--border-color);border-radius:12px;background:var(--bg-card);display:flex;flex-direction:column;min-height:0;overflow:hidden}.am-detail-head{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid var(--border-color)}.am-badge{display:inline-flex;align-items:center;gap:5px;font-size:12px;padding:3px 10px;border-radius:12px;font-weight:600}.am-badge svg{flex-shrink:0}.am-badge--active{background:color-mix(in srgb,var(--accent-primary) 20%,transparent);color:var(--accent-primary)}.am-badge--idle{background:color-mix(in srgb,var(--accent-warning) 20%,transparent);color:var(--accent-warning)}.am-badge--completed{background:var(--bg-secondary);color:var(--text-muted)}.am-badge--error{background:color-mix(in srgb,var(--accent-danger) 20%,transparent);color:var(--accent-danger)}.am-detail-close{background:none;border:none;font-size:20px;color:var(--text-muted);cursor:pointer;line-height:1}.am-detail-title{margin:0;padding:10px 14px 4px;font-size:14px}.am-detail-meta{padding:0 14px 10px;font-size:12px;color:var(--text-muted);display:grid;gap:4px}.am-detail-meta b{color:var(--text-light);font-weight:600}.am-detail-inflight{color:var(--accent-warning);margin-top:4px}.am-detail-section-title{padding:8px 14px;font-size:12px;color:var(--text-muted);border-top:1px solid var(--border-color);text-transform:uppercase;letter-spacing:.5px}.am-detail-footer{display:flex;gap:8px;padding:10px 14px;border-top:1px solid var(--border-color);margin-top:auto}.am-path{flex:1;overflow-y:auto;padding:8px 14px}.am-path-item{display:flex;gap:10px;margin-bottom:4px}.am-path-rail{display:flex;flex-direction:column;align-items:center;width:14px;flex-shrink:0;padding-top:4px}.am-path-dot{width:9px;height:9px;border-radius:50%;background:var(--accent-secondary);flex-shrink:0}.am-path-item--tool_use .am-path-dot{background:var(--accent-primary)}.am-path-item--error .am-path-dot{background:var(--accent-danger)}.am-path-item--cancelled .am-path-dot{background:var(--text-route-muted)}.am-path-item--prompt .am-path-dot{background:var(--accent-warning)}.am-path-line{width:2px;flex:1;background:var(--border-color);min-height:14px}.am-path-body{flex:1;min-width:0;padding-bottom:6px}.am-path-head{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-light)}.am-path-icon{flex-shrink:0}.am-path-summary{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.am-path-time{font-size:10px;color:var(--text-muted);flex-shrink:0}.am-path-count{flex-shrink:0;font-size:10px;line-height:1;padding:2px 5px;border-radius:8px;background:var(--bg-secondary);color:var(--text-muted)}.am-path-toggle{color:var(--text-muted);flex-shrink:0}.am-clickable{cursor:pointer}.am-path-detail{margin:4px 0 0;padding:6px 8px;background:var(--bg-secondary);border-radius:4px;font-size:11px;color:var(--text-muted);white-space:pre-wrap;word-break:break-all;max-height:200px;overflow:auto}.am-path-prompt{margin-top:2px;font-size:12px;color:var(--text-muted);line-height:1.5}.am-empty{padding:20px;text-align:center;color:var(--text-muted);font-size:13px}.am-feed-wrap{display:flex;align-items:stretch;flex-shrink:0}.am-feed-toggle{align-self:center;display:flex;align-items:center;justify-content:center;width:26px;height:26px;margin-right:6px;border:none;background:transparent;color:var(--text-muted);cursor:pointer;border-radius:50%;transition:color .15s,background .15s}.am-feed-toggle:hover{color:var(--accent-primary);background:var(--bg-hover)}.am-feed-sidebar{width:280px;margin:4px 0;border:1px solid var(--border-color);border-radius:12px;background:var(--bg-card);display:flex;flex-direction:column;min-height:0;overflow:hidden}.am-feed-head{display:flex;justify-content:space-between;padding:8px 14px;font-size:12px;color:var(--text-muted);border-bottom:1px solid var(--border-color);flex-shrink:0}.am-feed{flex:1;overflow-y:auto;padding:6px 14px;display:flex;flex-direction:column}.am-feed-empty{flex:1;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:12px}.am-feed-row{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:12px;border-bottom:1px solid var(--border-color)}.am-feed-time{color:var(--text-muted);font-size:10px;width:60px;flex-shrink:0}.am-feed-agent,.am-feed-icon{flex-shrink:0}.am-feed-summary{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-light)}.am-feed-row--tool_use .am-feed-summary{color:var(--accent-primary)}.am-feed-row--error .am-feed-summary{color:var(--accent-danger)}.am-feed-row--cancelled .am-feed-summary{color:var(--text-secondary)}.am-center-mark{fill:var(--accent-primary);opacity:.4}.am-zoom{position:absolute;right:12px;bottom:12px;display:flex;align-items:center;gap:4px;background:var(--bg-card);border:1px solid var(--border-color);border-radius:8px;padding:3px 6px;box-shadow:var(--shadow-secondary)}.am-zoom-btn{width:24px;height:24px;border:none;background:transparent;color:var(--text-light);border-radius:5px;cursor:pointer;font-size:14px;line-height:1;display:flex;align-items:center;justify-content:center}.am-zoom-btn:hover{background:var(--bg-hover);color:var(--accent-primary)}.am-zoom-pct{font-size:11px;color:var(--text-muted);min-width:38px;text-align:center}.am-timefilter{display:flex;flex-direction:column;align-items:stretch;gap:12px;padding:12px 16px}.am-timepresets{display:flex;gap:6px;flex-wrap:wrap;align-self:flex-start}.am-chip{padding:4px 11px;border:1px solid var(--border-color);background:var(--bg-secondary);color:var(--text-muted);border-radius:14px;cursor:pointer;font-size:12px;white-space:nowrap;transition:all .15s}.am-chip:hover{color:var(--text-light);border-color:var(--accent-primary)}.am-chip--active{background:color-mix(in srgb,var(--accent-primary) 16%,transparent);color:var(--accent-primary);border-color:var(--accent-primary)}.am-timefilter-hint{padding:10px 16px 12px;font-size:11px;color:var(--text-muted);border-top:1px solid var(--border-color);margin:0 16px}.am-toggle-btn{display:inline-flex;align-items:center;gap:5px;font-size:12px}.am-toggle-btn svg{opacity:.7}.am-trend{display:flex;flex-direction:column;gap:2px;flex:1;min-width:120px}.am-trend-label{font-size:10px;color:var(--text-muted)}.am-trend-svg{width:100%;height:44px;display:block}.am-trend-axis{font-size:9px;color:var(--text-muted);text-align:center}.am-icon-btn{width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;font-size:15px;line-height:1}.am-icon-btn--active{background:color-mix(in srgb,var(--accent-primary) 16%,transparent);color:var(--accent-primary);border-color:var(--accent-primary)}.am-help-btn{width:32px;height:32px;padding:0;border-radius:50%;font-weight:700;font-size:14px;line-height:1}.am-popover-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:50;background:transparent}.am-popover{position:absolute;top:64px;right:12px;z-index:51;width:360px;max-width:calc(100vw - 24px);max-height:calc(100vh - 80px);background:var(--bg-card);border:1px solid var(--border-color);border-radius:14px;box-shadow:0 8px 28px #0000002e;display:flex;flex-direction:column;overflow:hidden}.am-popover--wide{width:860px}.am-legend-grid{display:flex;gap:24px;align-items:flex-start}.am-legend-col{flex:1;min-width:0}.am-popover--time{width:440px}.am-popover-head{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border-color);font-weight:600;font-size:14px}.am-popover-close{background:none;border:none;font-size:20px;line-height:1;color:var(--text-muted);cursor:pointer;padding:0 4px}.am-popover-close:hover{color:var(--text-light)}.am-popover-body{padding:10px 16px 14px;overflow-y:auto}.am-legend-group{break-inside:avoid;margin-bottom:10px}.am-legend-group-title{font-size:11px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin:0 0 4px;padding-bottom:4px;border-bottom:1px solid var(--border-color)}.am-legend-row{display:flex;align-items:flex-start;gap:10px;padding:4px 0}.am-legend-ic{flex-shrink:0;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;font-size:14px}.am-legend-text{display:flex;flex-direction:column;gap:1px;min-width:0}.am-legend-label{font-size:13px;font-weight:600;color:var(--text-light)}.am-legend-desc{font-size:11px;color:var(--text-muted);line-height:1.45}.am-ic-star{color:var(--accent-primary);font-size:16px}.am-ic-bolt{color:var(--accent-warning);font-size:16px}.am-ic-busy{width:14px;height:14px;border-radius:50%;background:var(--accent-warning);border:2px solid var(--bg-card);box-shadow:0 0 0 1px var(--border-color)}.am-ic-center{width:8px;height:8px;border-radius:50%;background:var(--accent-primary);opacity:.6}.am-detail--popover{position:fixed;z-index:60;width:380px;max-width:calc(100vw - 24px);max-height:80vh;box-shadow:0 8px 28px #00000038}.am-detail--popover .am-path{overflow-y:auto}.am-feed-agent{flex-shrink:0}.am-feed-row--clickable{cursor:pointer}.am-feed-row--clickable:hover{background:var(--bg-hover)}.am-ring{pointer-events:none}.am-ring circle{stroke-width:1;stroke-dasharray:5 6;opacity:.55}.am-ring--t0 circle{stroke:var(--accent-success)}.am-ring--t1 circle{stroke:var(--accent-warning)}.am-ring--t2 circle{stroke:#6ba8e5}.am-ring--t3 circle{stroke:var(--accent-danger);opacity:.7}.am-ring-label{font-size:11px;fill:var(--text-muted);opacity:.7;pointer-events:none;-webkit-user-select:none;user-select:none}.am-ring--t0 .am-ring-label{fill:var(--accent-success)}.am-ring--t1 .am-ring-label{fill:var(--accent-warning)}.am-ring--t2 .am-ring-label{fill:#6ba8e5}.am-ring--t3 .am-ring-label{fill:var(--accent-danger)}.am-ring-swatch{display:inline-block;width:22px;height:0;border-top:2px dashed currentColor;opacity:.7}.am-detail-path{display:flex;align-items:flex-start;gap:4px;flex-wrap:wrap}.am-detail-path code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--text-light);background:var(--bg-secondary);padding:1px 6px;border-radius:4px;word-break:break-all}.am-detail-path-na{color:var(--text-muted);font-size:11px;font-style:normal}.am-notify-switch{display:inline-flex;align-items:center;gap:6px;padding-left:4px}.am-notify-switch .switch{width:42px;height:24px}.am-notify-help-link{background:none;border:none;padding:0 2px;font-size:11px;color:var(--accent-warning);cursor:pointer;white-space:nowrap;text-decoration:underline}.am-notify-help-link:hover{color:var(--accent-danger)}.am-notify-test{margin-left:4px;padding:2px 8px;border:1px solid var(--border-color);background:var(--bg-secondary);color:var(--text-muted);border-radius:10px;font-size:11px;cursor:pointer}.am-notify-test:hover{color:var(--accent-primary);border-color:var(--accent-primary)}*{margin:0;padding:0;box-sizing:border-box}:root{--primary-color: #14532D;--error-color: #DC2626;--bg-primary: linear-gradient(135deg, #F7FEE7 0%, #F0FDF4 100%);--bg-primary-solid: #F3FCEF;--bg-secondary: rgba(255, 255, 255, .95);--bg-sidebar: linear-gradient(135deg, #0F5132 0%, #065F46 100%);--bg-card: rgba(255, 255, 255, .98);--bg-code: #f4fff7;--bg-table-header: linear-gradient(135deg, #A7F3D0 0%, #6EE7B7 100%);--text-primary: #14532D;--text-secondary: #064E3B;--text-muted: #065F46;--text-sidebar: #FFFFFF;--text-on-dark: #FFFFFF;--border-primary: rgba(15, 81, 50, .2);--border-secondary: rgba(6, 95, 70, .2);--accent-primary: #0F5132;--accent-secondary: #047857;--accent-light: #a1e9c7;--accent-success: #047857;--accent-warning: #D97706;--accent-danger: #DC2626;--shadow-primary: rgba(15, 81, 50, .3);--shadow-secondary: rgba(15, 81, 50, .15);--bg-route-item: rgba(248, 249, 250, .9);--bg-route-item-hover: rgba(230, 244, 234, .95);--bg-route-item-selected: rgba(161, 233, 199, .25);--text-route-muted: #6c757d;--text-info: #2faeee;--text-light: #018038;--bg-info-box: #f8f9fa;--border-info-box: #e0e0e0;--text-info-box: #666;--bg-info-blue: #e3f2fd;--border-info-blue: #2196f3;--bg-info-green: #f1f8e9;--border-info-green: #8bc34a;--bg-info-orange: #fff3e0;--border-info-orange: #ff9800;--bg-info-yellow: #fff8e1;--border-info-yellow: #ffc107;--bg-assembled-text: #f8f9fa;--bg-thinking-box: #fff9e6;--border-thinking-box: #e0e0e0;--text-thinking-title: #f39c12;--bg-thinking-content: #fff;--border-thinking-content: #f0e6d3;--text-thinking-content: #555;--text-reply-title: #333;--bg-reply-content: #fff;--border-reply-content: #ddd;--text-reply-content: #333;--bg-input-disabled: #e5e7eb;--text-input-disabled: #9ca3af}[data-theme=dark]{--bg-primary: linear-gradient(135deg, #0A1A0F 0%, #0F2415 100%);--bg-primary-solid: #0C1F12;--bg-secondary: rgba(15, 36, 21, .9);--bg-sidebar: linear-gradient(135deg, #0F5132 0%, #065F46 100%);--bg-card: rgba(25, 51, 31, .95);--bg-code: #0f172a;--bg-table-header: linear-gradient(135deg, #0F5132 0%, #047857 100%);--text-primary: #ECFEF5;--text-secondary: #A7F3D0;--text-muted: #6EE7B7;--text-sidebar: #FFFFFF;--text-on-dark: #ECFEF5;--border-primary: rgba(15, 81, 50, .5);--border-secondary: rgba(6, 95, 70, .5);--accent-primary: #0F5132;--accent-secondary: #047857;--accent-light: #002d18;--accent-success: #10B981;--accent-warning: #F59E0B;--accent-danger: #EF4444;--shadow-primary: rgba(15, 81, 50, .4);--shadow-secondary: rgba(6, 95, 70, .3);--bg-route-item: rgba(20, 46, 28, .7);--bg-route-item-hover: rgba(30, 60, 38, .85);--bg-route-item-selected: rgba(16, 185, 129, .15);--text-route-muted: #A7F3D0;--text-light: #999999;--bg-info-box: rgba(20, 46, 28, .6);--border-info-box: rgba(161, 233, 199, .2);--text-info-box: #A7F3D0;--bg-info-blue: rgba(13, 71, 161, .25);--border-info-blue: #1976d2;--bg-info-green: rgba(27, 94, 32, .25);--border-info-green: #689f38;--bg-info-orange: rgba(230, 81, 0, .2);--border-info-orange: #f57c00;--bg-info-yellow: rgba(255, 193, 7, .15);--border-info-yellow: #ffb300;--bg-assembled-text: rgba(15, 23, 42, .5);--bg-thinking-box: rgba(230, 81, 0, .15);--border-thinking-box: rgba(245, 158, 11, .3);--text-thinking-title: #fbbf24;--bg-thinking-content: rgba(15, 23, 42, .4);--border-thinking-content: rgba(245, 158, 11, .2);--text-thinking-content: #d1d5db;--text-reply-title: #e5e7eb;--bg-reply-content: rgba(15, 23, 42, .4);--border-reply-content: rgba(161, 233, 199, .2);--text-reply-content: #e5e7eb;--bg-input-disabled: #2d3748;--text-input-disabled: #718096}body{font-family:Nunito,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:var(--bg-primary);color:var(--text-primary);transition:all .3s ease;min-height:100vh}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}#root{width:100vw;height:100vh;overflow:visible}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:var(--bg-secondary);border-radius:6px}::-webkit-scrollbar-thumb{background:var(--accent-secondary);border-radius:6px;border:2px solid var(--bg-secondary)}::-webkit-scrollbar-thumb:hover{background:var(--accent-primary)}::-webkit-scrollbar-corner{background:var(--bg-secondary)}*{scrollbar-width:thin;scrollbar-color:var(--accent-primary) var(--bg-secondary)}::-webkit-scrollbar-button{display:none}html{scroll-behavior:smooth}.datetime-picker-input{cursor:pointer}.datetime-picker-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:1;filter:brightness(0);padding:2px 4px}.datetime-picker-input::-webkit-calendar-picker-indicator:hover{filter:brightness(.3)}.datetime-picker-input::-moz-calendar-picker-indicator{cursor:pointer;opacity:1;filter:brightness(0);padding:2px 4px}.datetime-picker-input::-moz-calendar-picker-indicator:hover{filter:brightness(.3)}[data-theme=dark] .datetime-picker-input::-webkit-calendar-picker-indicator{filter:brightness(0) invert(1)}[data-theme=dark] .datetime-picker-input::-webkit-calendar-picker-indicator:hover{filter:brightness(.2) invert(1)}[data-theme=dark] .datetime-picker-input::-moz-calendar-picker-indicator{filter:brightness(0) invert(1)}[data-theme=dark] .datetime-picker-input::-moz-calendar-picker-indicator:hover{filter:brightness(.2) invert(1)}