manifest 5.31.1 → 5.32.0

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.
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddProviderRegion1773600000000 = void 0;
4
+ class AddProviderRegion1773600000000 {
5
+ async up(queryRunner) {
6
+ await queryRunner.query(`ALTER TABLE "user_providers" ADD COLUMN IF NOT EXISTS "region" varchar DEFAULT NULL`);
7
+ }
8
+ async down(queryRunner) {
9
+ await queryRunner.query(`ALTER TABLE "user_providers" DROP COLUMN IF EXISTS "region"`);
10
+ }
11
+ }
12
+ exports.AddProviderRegion1773600000000 = AddProviderRegion1773600000000;
13
+ //# sourceMappingURL=1773600000000-AddProviderRegion.js.map
@@ -20,6 +20,7 @@ let UserProvider = class UserProvider {
20
20
  api_key_encrypted;
21
21
  key_prefix;
22
22
  auth_type;
23
+ region;
23
24
  is_active;
24
25
  connected_at;
25
26
  updated_at;
@@ -55,6 +56,10 @@ __decorate([
55
56
  (0, typeorm_1.Column)('varchar', { default: 'api_key' }),
56
57
  __metadata("design:type", String)
57
58
  ], UserProvider.prototype, "auth_type", void 0);
59
+ __decorate([
60
+ (0, typeorm_1.Column)('varchar', { nullable: true, default: null }),
61
+ __metadata("design:type", Object)
62
+ ], UserProvider.prototype, "region", void 0);
58
63
  __decorate([
59
64
  (0, typeorm_1.Column)('boolean', { default: true }),
60
65
  __metadata("design:type", Boolean)
@@ -43,6 +43,7 @@ class ConnectProviderDto {
43
43
  provider;
44
44
  apiKey;
45
45
  authType;
46
+ region;
46
47
  }
47
48
  exports.ConnectProviderDto = ConnectProviderDto;
48
49
  __decorate([
@@ -60,6 +61,11 @@ __decorate([
60
61
  (0, class_validator_1.IsIn)(manifest_shared_1.AUTH_TYPES),
61
62
  __metadata("design:type", String)
62
63
  ], ConnectProviderDto.prototype, "authType", void 0);
64
+ __decorate([
65
+ (0, class_validator_1.IsOptional)(),
66
+ (0, class_validator_1.IsString)(),
67
+ __metadata("design:type", String)
68
+ ], ConnectProviderDto.prototype, "region", void 0);
63
69
  class AgentProviderParamDto {
64
70
  agentName;
65
71
  provider;
@@ -14,13 +14,14 @@ const custom_provider_entity_1 = require("../../entities/custom-provider.entity"
14
14
  const model_prices_module_1 = require("../../model-prices/model-prices.module");
15
15
  const provider_model_fetcher_service_1 = require("./provider-model-fetcher.service");
16
16
  const model_discovery_service_1 = require("./model-discovery.service");
17
+ const copilot_token_service_1 = require("../proxy/copilot-token.service");
17
18
  let ModelDiscoveryModule = class ModelDiscoveryModule {
18
19
  };
19
20
  exports.ModelDiscoveryModule = ModelDiscoveryModule;
20
21
  exports.ModelDiscoveryModule = ModelDiscoveryModule = __decorate([
21
22
  (0, common_1.Module)({
22
23
  imports: [typeorm_1.TypeOrmModule.forFeature([user_provider_entity_1.UserProvider, custom_provider_entity_1.CustomProvider]), model_prices_module_1.ModelPricesModule],
23
- providers: [provider_model_fetcher_service_1.ProviderModelFetcherService, model_discovery_service_1.ModelDiscoveryService],
24
+ providers: [provider_model_fetcher_service_1.ProviderModelFetcherService, model_discovery_service_1.ModelDiscoveryService, copilot_token_service_1.CopilotTokenService],
24
25
  exports: [model_discovery_service_1.ModelDiscoveryService, provider_model_fetcher_service_1.ProviderModelFetcherService],
25
26
  })
26
27
  ], ModelDiscoveryModule);
@@ -25,26 +25,35 @@ const crypto_util_1 = require("../../common/utils/crypto.util");
25
25
  const quality_score_util_1 = require("../../database/quality-score.util");
26
26
  const pricing_sync_service_1 = require("../../database/pricing-sync.service");
27
27
  const openai_oauth_types_1 = require("../openai-oauth.types");
28
+ const qwen_region_1 = require("../qwen-region");
29
+ const copilot_token_service_1 = require("../proxy/copilot-token.service");
28
30
  const model_fallback_1 = require("./model-fallback");
29
31
  const customProviderKey = (id) => `custom:${id}`;
30
32
  const customModelKey = (id, modelName) => `custom:${id}/${modelName}`;
33
+ function isQwenProvider(providerId) {
34
+ const lower = providerId.toLowerCase();
35
+ return lower === 'qwen' || lower === 'alibaba';
36
+ }
31
37
  let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryService {
32
38
  providerRepo;
33
39
  customProviderRepo;
34
40
  fetcher;
35
41
  pricingSync;
36
42
  modelRegistry;
43
+ copilotTokenService;
37
44
  logger = new common_1.Logger(ModelDiscoveryService_1.name);
38
- constructor(providerRepo, customProviderRepo, fetcher, pricingSync, modelRegistry) {
45
+ constructor(providerRepo, customProviderRepo, fetcher, pricingSync, modelRegistry, copilotTokenService) {
39
46
  this.providerRepo = providerRepo;
40
47
  this.customProviderRepo = customProviderRepo;
41
48
  this.fetcher = fetcher;
42
49
  this.pricingSync = pricingSync;
43
50
  this.modelRegistry = modelRegistry;
51
+ this.copilotTokenService = copilotTokenService;
44
52
  }
45
53
  async discoverModels(provider) {
46
54
  let apiKey = '';
47
55
  let endpointOverride;
56
+ const lowerProvider = provider.provider.toLowerCase();
48
57
  if (provider.api_key_encrypted) {
49
58
  try {
50
59
  apiKey = (0, crypto_util_1.decrypt)(provider.api_key_encrypted, (0, crypto_util_1.getEncryptionSecret)());
@@ -55,7 +64,6 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
55
64
  }
56
65
  }
57
66
  if (provider.auth_type === 'subscription' && apiKey) {
58
- const lowerProvider = provider.provider.toLowerCase();
59
67
  if (lowerProvider === 'openai' || lowerProvider === 'minimax') {
60
68
  const blob = (0, openai_oauth_types_1.parseOAuthTokenBlob)(apiKey);
61
69
  if (blob?.t) {
@@ -65,6 +73,18 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
65
73
  }
66
74
  }
67
75
  }
76
+ else if (lowerProvider === 'copilot' && this.copilotTokenService) {
77
+ try {
78
+ apiKey = await this.copilotTokenService.getCopilotToken(apiKey);
79
+ }
80
+ catch {
81
+ this.logger.warn('Copilot token exchange failed for model discovery — falling back to known models');
82
+ apiKey = '';
83
+ }
84
+ }
85
+ }
86
+ if (isQwenProvider(provider.provider) && (0, qwen_region_1.isQwenResolvedRegion)(provider.region)) {
87
+ endpointOverride = (0, qwen_region_1.getQwenCompatibleBaseUrl)(provider.region);
68
88
  }
69
89
  let raw;
70
90
  if (provider.auth_type === 'subscription' && !apiKey) {
@@ -78,7 +98,7 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
78
98
  if (raw.length > 0 && this.modelRegistry) {
79
99
  this.modelRegistry.registerModels(provider.provider, raw.map((m) => m.id));
80
100
  }
81
- if (raw.length === 0) {
101
+ if (raw.length === 0 && !isQwenProvider(provider.provider)) {
82
102
  const confirmed = this.modelRegistry?.getConfirmedModels(provider.provider) ?? null;
83
103
  raw = (0, model_fallback_1.buildFallbackModels)(this.pricingSync, provider.provider, confirmed);
84
104
  if (raw.length > 0) {
@@ -224,8 +244,10 @@ exports.ModelDiscoveryService = ModelDiscoveryService = ModelDiscoveryService_1
224
244
  __param(3, (0, common_1.Inject)(pricing_sync_service_1.PricingSyncService)),
225
245
  __param(4, (0, common_1.Optional)()),
226
246
  __param(4, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)),
247
+ __param(5, (0, common_1.Optional)()),
248
+ __param(5, (0, common_1.Inject)(copilot_token_service_1.CopilotTokenService)),
227
249
  __metadata("design:paramtypes", [typeorm_2.Repository,
228
250
  typeorm_2.Repository,
229
- provider_model_fetcher_service_1.ProviderModelFetcherService, Object, Object])
251
+ provider_model_fetcher_service_1.ProviderModelFetcherService, Object, Object, Object])
230
252
  ], ModelDiscoveryService);
231
253
  //# sourceMappingURL=model-discovery.service.js.map
@@ -11,6 +11,7 @@ exports.ProviderModelFetcherService = exports.PROVIDER_CONFIGS = void 0;
11
11
  const common_1 = require("@nestjs/common");
12
12
  const ollama_1 = require("../../common/constants/ollama");
13
13
  const provider_base_url_1 = require("../provider-base-url");
14
+ const qwen_region_1 = require("../qwen-region");
14
15
  const FETCH_TIMEOUT_MS = 5000;
15
16
  const DEFAULT_CONTEXT_WINDOW = 128000;
16
17
  const ANTHROPIC_DEFAULT_CONTEXT = 200000;
@@ -173,6 +174,30 @@ function parseOpenaiSubscription(body, provider) {
173
174
  };
174
175
  });
175
176
  }
177
+ function parseCopilot(body, provider) {
178
+ const data = body?.data;
179
+ if (!Array.isArray(data))
180
+ return [];
181
+ return data
182
+ .filter((m) => {
183
+ const entry = m;
184
+ return typeof entry.id === 'string' && entry.id.length > 0;
185
+ })
186
+ .map((m) => {
187
+ const entry = m;
188
+ return {
189
+ id: `copilot/${entry.id}`,
190
+ displayName: entry.id,
191
+ provider,
192
+ contextWindow: DEFAULT_CONTEXT_WINDOW,
193
+ inputPricePerToken: 0,
194
+ outputPricePerToken: 0,
195
+ capabilityReasoning: false,
196
+ capabilityCode: false,
197
+ qualityScore: 3,
198
+ };
199
+ });
200
+ }
176
201
  exports.PROVIDER_CONFIGS = {
177
202
  openai: {
178
203
  endpoint: 'https://api.openai.com/v1/models',
@@ -223,7 +248,7 @@ exports.PROVIDER_CONFIGS = {
223
248
  parse: parseAnthropic,
224
249
  },
225
250
  qwen: {
226
- endpoint: 'https://dashscope.aliyuncs.com/compatible-mode/v1/models',
251
+ endpoint: `${(0, qwen_region_1.getQwenCompatibleBaseUrl)('beijing')}/v1/models`,
227
252
  buildHeaders: bearerHeaders,
228
253
  parse: parseOpenAI,
229
254
  },
@@ -264,6 +289,17 @@ exports.PROVIDER_CONFIGS = {
264
289
  buildHeaders: () => ({}),
265
290
  parse: parseOllama,
266
291
  },
292
+ copilot: {
293
+ endpoint: 'https://api.githubcopilot.com/models',
294
+ buildHeaders: (key) => ({
295
+ Authorization: `Bearer ${key}`,
296
+ Accept: 'application/json',
297
+ 'Editor-Version': 'vscode/1.100.0',
298
+ 'Editor-Plugin-Version': 'copilot/1.300.0',
299
+ 'Copilot-Integration-Id': 'vscode-chat',
300
+ }),
301
+ parse: parseCopilot,
302
+ },
267
303
  };
268
304
  let ProviderModelFetcherService = ProviderModelFetcherService_1 = class ProviderModelFetcherService {
269
305
  logger = new common_1.Logger(ProviderModelFetcherService_1.name);
@@ -290,6 +326,15 @@ let ProviderModelFetcherService = ProviderModelFetcherService_1 = class Provider
290
326
  this.logger.warn('Ignoring invalid MiniMax subscription endpoint override');
291
327
  }
292
328
  }
329
+ else if (endpointOverride && configKey === 'qwen') {
330
+ const qwenBaseUrl = (0, qwen_region_1.normalizeQwenCompatibleBaseUrl)(endpointOverride);
331
+ if (qwenBaseUrl) {
332
+ url = `${qwenBaseUrl}/v1/models`;
333
+ }
334
+ else {
335
+ this.logger.warn('Ignoring invalid Qwen endpoint override');
336
+ }
337
+ }
293
338
  const headers = config.buildHeaders(apiKey, authType);
294
339
  try {
295
340
  const controller = new AbortController();
@@ -7,6 +7,7 @@ exports.resolveEndpointKey = resolveEndpointKey;
7
7
  const ollama_1 = require("../../common/constants/ollama");
8
8
  const providers_1 = require("../../common/constants/providers");
9
9
  const provider_base_url_1 = require("../provider-base-url");
10
+ const qwen_region_1 = require("../qwen-region");
10
11
  const openaiHeaders = (apiKey) => ({
11
12
  Authorization: `Bearer ${apiKey}`,
12
13
  'Content-Type': 'application/json',
@@ -94,6 +95,12 @@ exports.PROVIDER_ENDPOINTS = {
94
95
  buildPath: openaiPath,
95
96
  format: 'openai',
96
97
  },
98
+ qwen: {
99
+ baseUrl: (0, qwen_region_1.getQwenCompatibleBaseUrl)('beijing'),
100
+ buildHeaders: openaiHeaders,
101
+ buildPath: openaiPath,
102
+ format: 'openai',
103
+ },
97
104
  zai: {
98
105
  baseUrl: 'https://api.z.ai',
99
106
  buildHeaders: openaiHeaders,
@@ -26,6 +26,7 @@ const limit_check_service_1 = require("../../notifications/services/limit-check.
26
26
  const fallback_status_codes_1 = require("./fallback-status-codes");
27
27
  const provider_aliases_1 = require("../provider-aliases");
28
28
  const provider_base_url_1 = require("../provider-base-url");
29
+ const qwen_region_1 = require("../qwen-region");
29
30
  const anthropic_model_id_1 = require("../../common/utils/anthropic-model-id");
30
31
  const SCORING_EXCLUDED_ROLES = new Set(['system', 'developer']);
31
32
  const SCORING_RECENT_MESSAGES = 10;
@@ -80,10 +81,11 @@ let ProxyService = ProxyService_1 = class ProxyService {
80
81
  throw new common_1.BadRequestException(`No API key found for provider: ${resolved.provider}. Re-connect the provider with an API key.`);
81
82
  }
82
83
  const resolvedCredentials = await this.resolveApiKey(resolved.provider, apiKey, resolved.auth_type, agentId, userId);
84
+ const providerRegion = await this.routingService.getProviderRegion(agentId, resolved.provider, resolved.auth_type);
83
85
  const primaryModel = this.normalizeProviderModel(resolved.provider, resolved.model);
84
86
  this.logger.log(`Proxy: tier=${resolved.tier} model=${primaryModel} provider=${resolved.provider} auth_type=${resolved.auth_type} confidence=${resolved.confidence}`);
85
87
  const stream = body.stream === true;
86
- const forward = await this.tryForwardToProvider(resolved.provider, resolvedCredentials.apiKey, primaryModel, body, stream, sessionKey, signal, resolved.auth_type, resolvedCredentials.resourceUrl);
88
+ const forward = await this.tryForwardToProvider(resolved.provider, resolvedCredentials.apiKey, primaryModel, body, stream, sessionKey, signal, resolved.auth_type, resolvedCredentials.resourceUrl, providerRegion);
87
89
  if (!forward.response.ok && (0, fallback_status_codes_1.shouldTriggerFallback)(forward.response.status)) {
88
90
  const tiers = await this.routingService.getTiers(agentId);
89
91
  const assignment = tiers.find((t) => t.tier === resolved.tier);
@@ -180,8 +182,9 @@ let ProxyService = ProxyService_1 = class ProxyService {
180
182
  continue;
181
183
  }
182
184
  const resolvedCredentials = await this.resolveApiKey(provider, apiKey, authType, agentId, userId);
185
+ const providerRegion = await this.routingService.getProviderRegion(agentId, provider, authType);
183
186
  this.logger.log(`Fallback ${i}: trying model=${model} provider=${provider} auth_type=${authType} (primary=${primaryModel})`);
184
- const forward = await this.tryForwardToProvider(provider, resolvedCredentials.apiKey, model, body, stream, sessionKey, signal, authType, resolvedCredentials.resourceUrl);
187
+ const forward = await this.tryForwardToProvider(provider, resolvedCredentials.apiKey, model, body, stream, sessionKey, signal, authType, resolvedCredentials.resourceUrl, providerRegion);
185
188
  if (forward.response.ok) {
186
189
  return { success: { forward, model, provider, fallbackIndex: i }, failures };
187
190
  }
@@ -214,9 +217,9 @@ let ProxyService = ProxyService_1 = class ProxyService {
214
217
  }
215
218
  return { apiKey };
216
219
  }
217
- async tryForwardToProvider(provider, apiKey, model, body, stream, sessionKey, signal, authType, resourceUrl) {
220
+ async tryForwardToProvider(provider, apiKey, model, body, stream, sessionKey, signal, authType, resourceUrl, providerRegion) {
218
221
  try {
219
- return await this.forwardToProvider(provider, apiKey, model, body, stream, sessionKey, signal, authType, resourceUrl);
222
+ return await this.forwardToProvider(provider, apiKey, model, body, stream, sessionKey, signal, authType, resourceUrl, providerRegion);
220
223
  }
221
224
  catch (error) {
222
225
  if (signal?.aborted)
@@ -345,7 +348,7 @@ let ProxyService = ProxyService_1 = class ProxyService {
345
348
  return undefined;
346
349
  return error.cause;
347
350
  }
348
- async forwardToProvider(provider, apiKey, model, body, stream, sessionKey, signal, authType, resourceUrl) {
351
+ async forwardToProvider(provider, apiKey, model, body, stream, sessionKey, signal, authType, resourceUrl, providerRegion) {
349
352
  const extraHeaders = {};
350
353
  if (provider === 'xai') {
351
354
  extraHeaders['x-grok-conv-id'] = sessionKey;
@@ -368,6 +371,9 @@ let ProxyService = ProxyService_1 = class ProxyService {
368
371
  forwardModel = custom_provider_service_1.CustomProviderService.rawModelName(model);
369
372
  }
370
373
  }
374
+ else if ((0, provider_endpoints_1.resolveEndpointKey)(provider) === 'qwen' && (0, qwen_region_1.isQwenResolvedRegion)(providerRegion)) {
375
+ customEndpoint = (0, provider_endpoints_1.buildEndpointOverride)((0, qwen_region_1.getQwenCompatibleBaseUrl)(providerRegion), 'qwen');
376
+ }
371
377
  else if (authType === 'subscription' && provider.toLowerCase() === 'minimax' && resourceUrl) {
372
378
  const minimaxBaseUrl = (0, provider_base_url_1.normalizeMinimaxSubscriptionBaseUrl)(resourceUrl);
373
379
  if (minimaxBaseUrl) {
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isQwenResolvedRegion = isQwenResolvedRegion;
4
+ exports.isQwenRegion = isQwenRegion;
5
+ exports.getQwenCompatibleBaseUrl = getQwenCompatibleBaseUrl;
6
+ exports.normalizeQwenCompatibleBaseUrl = normalizeQwenCompatibleBaseUrl;
7
+ exports.detectQwenRegion = detectQwenRegion;
8
+ const provider_base_url_1 = require("./provider-base-url");
9
+ const QWEN_REGION_BASE_URLS = {
10
+ singapore: 'https://dashscope-intl.aliyuncs.com/compatible-mode',
11
+ us: 'https://dashscope-us.aliyuncs.com/compatible-mode',
12
+ beijing: 'https://dashscope.aliyuncs.com/compatible-mode',
13
+ };
14
+ const QWEN_ALLOWED_BASE_URLS = new Set(Object.values(QWEN_REGION_BASE_URLS));
15
+ const QWEN_REGION_DETECTION_ORDER = ['singapore', 'us', 'beijing'];
16
+ const QWEN_DETECTION_TIMEOUT_MS = 3000;
17
+ function buildModelsUrl(region) {
18
+ return `${QWEN_REGION_BASE_URLS[region]}/v1/models`;
19
+ }
20
+ function isQwenResolvedRegion(value) {
21
+ return value === 'singapore' || value === 'us' || value === 'beijing';
22
+ }
23
+ function isQwenRegion(value) {
24
+ return value === 'auto' || isQwenResolvedRegion(value);
25
+ }
26
+ function getQwenCompatibleBaseUrl(region) {
27
+ if (isQwenResolvedRegion(region))
28
+ return QWEN_REGION_BASE_URLS[region];
29
+ return QWEN_REGION_BASE_URLS.beijing;
30
+ }
31
+ function normalizeQwenCompatibleBaseUrl(baseUrl) {
32
+ const normalized = (0, provider_base_url_1.normalizeProviderBaseUrl)(baseUrl);
33
+ return QWEN_ALLOWED_BASE_URLS.has(normalized) ? normalized : null;
34
+ }
35
+ async function detectQwenRegion(apiKey, fetchImpl = fetch) {
36
+ const headers = { Authorization: `Bearer ${apiKey}` };
37
+ for (const region of QWEN_REGION_DETECTION_ORDER) {
38
+ try {
39
+ const response = await fetchImpl(buildModelsUrl(region), {
40
+ headers,
41
+ signal: AbortSignal.timeout(QWEN_DETECTION_TIMEOUT_MS),
42
+ });
43
+ if (response.ok)
44
+ return region;
45
+ }
46
+ catch {
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+ //# sourceMappingURL=qwen-region.js.map
@@ -22,6 +22,7 @@ const model_discovery_service_1 = require("./model-discovery/model-discovery.ser
22
22
  const ollama_sync_service_1 = require("../database/ollama-sync.service");
23
23
  const copilot_device_auth_service_1 = require("./copilot-device-auth.service");
24
24
  const routing_dto_1 = require("./dto/routing.dto");
25
+ const qwen_region_1 = require("./qwen-region");
25
26
  let RoutingController = class RoutingController {
26
27
  routingService;
27
28
  customProviderService;
@@ -53,15 +54,26 @@ let RoutingController = class RoutingController {
53
54
  is_active: p.is_active,
54
55
  has_api_key: !!p.api_key_encrypted,
55
56
  key_prefix: p.key_prefix ?? null,
57
+ region: p.region ?? null,
56
58
  connected_at: p.connected_at,
57
59
  }));
58
60
  }
59
61
  async upsertProvider(user, params, body) {
60
62
  const agent = await this.resolveAgentService.resolve(user.id, params.agentName);
63
+ const lowerProvider = body.provider.toLowerCase();
64
+ const isQwenProvider = lowerProvider === 'qwen' || lowerProvider === 'alibaba';
65
+ if (body.region !== undefined) {
66
+ if (!isQwenProvider) {
67
+ throw new common_1.BadRequestException('region is only supported for Alibaba/Qwen providers');
68
+ }
69
+ if (!(0, qwen_region_1.isQwenRegion)(body.region)) {
70
+ throw new common_1.BadRequestException('region must be one of: auto, singapore, us, beijing');
71
+ }
72
+ }
61
73
  if (body.provider.toLowerCase() === 'ollama') {
62
74
  await this.ollamaSync.sync();
63
75
  }
64
- const { provider: result, isNew } = await this.routingService.upsertProvider(agent.id, user.id, body.provider, body.apiKey, body.authType);
76
+ const { provider: result, isNew } = await this.routingService.upsertProvider(agent.id, user.id, body.provider, body.apiKey, body.authType, body.region);
65
77
  try {
66
78
  await this.discoveryService.discoverModels(result);
67
79
  await this.routingService.recalculateTiers(agent.id);
@@ -73,6 +85,7 @@ let RoutingController = class RoutingController {
73
85
  provider: result.provider,
74
86
  auth_type: result.auth_type ?? 'api_key',
75
87
  is_active: result.is_active,
88
+ region: result.region ?? null,
76
89
  };
77
90
  }
78
91
  async deactivateAllProviders(user, params) {
@@ -28,6 +28,7 @@ const crypto_util_1 = require("../common/utils/crypto.util");
28
28
  const provider_aliases_1 = require("./provider-aliases");
29
29
  const manifest_shared_1 = require("manifest-shared");
30
30
  const subscription_support_1 = require("./subscription-support");
31
+ const qwen_region_1 = require("./qwen-region");
31
32
  let RoutingService = RoutingService_1 = class RoutingService {
32
33
  providerRepo;
33
34
  tierRepo;
@@ -57,18 +58,20 @@ let RoutingService = RoutingService_1 = class RoutingService {
57
58
  this.routingCache.setProviders(agentId, providers);
58
59
  return providers;
59
60
  }
60
- async upsertProvider(agentId, userId, provider, apiKey, authType) {
61
+ async upsertProvider(agentId, userId, provider, apiKey, authType, region) {
61
62
  const effectiveAuthType = authType ?? 'api_key';
62
- const apiKeyEncrypted = apiKey ? (0, crypto_util_1.encrypt)(apiKey, (0, crypto_util_1.getEncryptionSecret)()) : null;
63
- const keyPrefix = apiKey ? apiKey.substring(0, 8) : null;
64
63
  const existing = await this.providerRepo.findOne({
65
64
  where: { agent_id: agentId, provider, auth_type: effectiveAuthType },
66
65
  });
66
+ const resolvedRegion = await this.resolveProviderRegion(provider, effectiveAuthType, region, apiKey, existing);
67
+ const apiKeyEncrypted = apiKey ? (0, crypto_util_1.encrypt)(apiKey, (0, crypto_util_1.getEncryptionSecret)()) : null;
68
+ const keyPrefix = apiKey ? apiKey.substring(0, 8) : null;
67
69
  if (existing) {
68
70
  if (apiKeyEncrypted !== null) {
69
71
  existing.api_key_encrypted = apiKeyEncrypted;
70
72
  existing.key_prefix = keyPrefix;
71
73
  }
74
+ existing.region = resolvedRegion;
72
75
  existing.is_active = true;
73
76
  existing.updated_at = new Date().toISOString();
74
77
  await this.providerRepo.save(existing);
@@ -84,6 +87,7 @@ let RoutingService = RoutingService_1 = class RoutingService {
84
87
  auth_type: effectiveAuthType,
85
88
  api_key_encrypted: apiKeyEncrypted,
86
89
  key_prefix: keyPrefix,
90
+ region: resolvedRegion,
87
91
  is_active: true,
88
92
  connected_at: new Date().toISOString(),
89
93
  updated_at: new Date().toISOString(),
@@ -93,6 +97,47 @@ let RoutingService = RoutingService_1 = class RoutingService {
93
97
  this.routingCache.invalidateAgent(agentId);
94
98
  return { provider: record, isNew: true };
95
99
  }
100
+ async resolveProviderRegion(provider, authType, requestedRegion, apiKey, existing) {
101
+ const lower = provider.toLowerCase();
102
+ const isQwenProvider = lower === 'qwen' || lower === 'alibaba';
103
+ if (!isQwenProvider || authType !== 'api_key')
104
+ return null;
105
+ if (requestedRegion === undefined) {
106
+ if (apiKey) {
107
+ return this.detectQwenRegionOrThrow(apiKey);
108
+ }
109
+ return (0, qwen_region_1.isQwenResolvedRegion)(existing?.region) ? existing.region : null;
110
+ }
111
+ if (!(0, qwen_region_1.isQwenRegion)(requestedRegion)) {
112
+ throw new common_1.BadRequestException('Qwen region must be one of: auto, singapore, us, beijing');
113
+ }
114
+ if (requestedRegion !== 'auto')
115
+ return requestedRegion;
116
+ const keyToProbe = await this.getQwenDetectionKey(apiKey, existing);
117
+ if (!keyToProbe) {
118
+ return (0, qwen_region_1.isQwenResolvedRegion)(existing?.region) ? existing.region : null;
119
+ }
120
+ return this.detectQwenRegionOrThrow(keyToProbe);
121
+ }
122
+ async getQwenDetectionKey(apiKey, existing) {
123
+ if (apiKey)
124
+ return apiKey;
125
+ if (!existing?.api_key_encrypted)
126
+ return null;
127
+ try {
128
+ return (0, crypto_util_1.decrypt)(existing.api_key_encrypted, (0, crypto_util_1.getEncryptionSecret)());
129
+ }
130
+ catch {
131
+ this.logger.warn('Failed to decrypt API key while auto-detecting Alibaba region');
132
+ return null;
133
+ }
134
+ }
135
+ async detectQwenRegionOrThrow(apiKey) {
136
+ const detected = await (0, qwen_region_1.detectQwenRegion)(apiKey);
137
+ if (detected)
138
+ return detected;
139
+ throw new common_1.BadRequestException('Could not auto-detect Alibaba region from this API key. Verify the key and try again.');
140
+ }
96
141
  async registerSubscriptionProvider(agentId, userId, provider) {
97
142
  if (!(0, subscription_support_1.isSupportedSubscriptionProvider)(provider)) {
98
143
  this.logger.debug(`Ignoring unsupported subscription provider registration for ${provider}`);
@@ -423,6 +468,13 @@ let RoutingService = RoutingService_1 = class RoutingService {
423
468
  const withKey = matches.find((r) => r.api_key_encrypted);
424
469
  return withKey?.auth_type ?? matches[0]?.auth_type ?? 'api_key';
425
470
  }
471
+ async getProviderRegion(agentId, provider, authType) {
472
+ const names = (0, provider_aliases_1.expandProviderNames)([provider]);
473
+ const records = await this.getProviders(agentId);
474
+ const matches = records.filter((r) => r.is_active && names.has(r.provider.toLowerCase()));
475
+ const match = authType ? matches.find((r) => r.auth_type === authType) : matches[0];
476
+ return match?.region ?? null;
477
+ }
426
478
  async findProviderForModel(agentId, model) {
427
479
  const providers = await this.getProviders(agentId);
428
480
  for (const p of providers) {
@@ -451,10 +503,16 @@ let RoutingService = RoutingService_1 = class RoutingService {
451
503
  const discovered = await this.discoveryService.getModelForAgent(agentId, model);
452
504
  if (discovered)
453
505
  return true;
506
+ const pricing = this.pricingCache.getByModel(model);
507
+ const inferredPrefix = (0, provider_aliases_1.inferProviderFromModelName)(model);
508
+ const pricingNames = pricing ? (0, provider_aliases_1.expandProviderNames)([pricing.provider]) : null;
509
+ const inferredNames = inferredPrefix ? (0, provider_aliases_1.expandProviderNames)([inferredPrefix]) : null;
510
+ if (pricingNames?.has('qwen') || inferredNames?.has('qwen')) {
511
+ return false;
512
+ }
454
513
  const records = (await this.providerRepo.find({
455
514
  where: { agent_id: agentId, is_active: true },
456
515
  })).filter(subscription_support_1.isManifestUsableProvider);
457
- const pricing = this.pricingCache.getByModel(model);
458
516
  if (pricing) {
459
517
  const names = (0, provider_aliases_1.expandProviderNames)([pricing.provider]);
460
518
  if (records.find((r) => names.has(r.provider.toLowerCase())))
@@ -466,9 +524,8 @@ let RoutingService = RoutingService_1 = class RoutingService {
466
524
  return true;
467
525
  }
468
526
  }
469
- const prefix = (0, provider_aliases_1.inferProviderFromModelName)(model);
470
- if (prefix) {
471
- const prefixNames = (0, provider_aliases_1.expandProviderNames)([prefix]);
527
+ if (inferredPrefix) {
528
+ const prefixNames = (0, provider_aliases_1.expandProviderNames)([inferredPrefix]);
472
529
  if (records.find((r) => prefixNames.has(r.provider.toLowerCase())))
473
530
  return true;
474
531
  }
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ To resolve the conflict:`,(0,ni.getConflictResolutionRecipe)(u,r))),c=a):ZM.diag
16
16
  openclaw config set plugins.entries.manifest.config.endpoint https://app.manifest.build/otlp`:`Invalid apiKey format. Keys must start with '${Ie}'. Fix it via:
17
17
  openclaw config set manifest.apiKey ${Ie}YOUR_KEY`:`Missing apiKey. Set it via:
18
18
  openclaw config set manifest.apiKey ${Ie}YOUR_KEY
19
- or export MANIFEST_API_KEY=${Ie}YOUR_KEY`}var oa=ve(cM()),ia=ve(Ai()),BI=ve(II()),GI=ve(wI()),HI=ve(mo());m();var tn=null,rn=null,Lc=null,Ic=null;function kI(o,r){let i=new HI.Resource({"service.name":dr.SERVICE_NAME,"service.version":"5.31.1","manifest.plugin":"true"}),c=o.apiKey?{Authorization:`Bearer ${o.apiKey}`}:{},a=new BI.OTLPTraceExporter({url:`${o.endpoint}/v1/traces`,headers:c});tn=new oa.BasicTracerProvider({resource:i,spanProcessors:[new oa.BatchSpanProcessor(a,{scheduledDelayMillis:5e3,maxQueueSize:2048,maxExportBatchSize:512})]}),tn.register(),r.debug(`[manifest] Trace exporter -> ${o.endpoint}/v1/traces`);let u=new GI.OTLPMetricExporter({url:`${o.endpoint}/v1/metrics`,headers:c}),t=o.devMode||o.mode==="local"?Cc.METRICS_INTERVAL_MS:dr.METRICS_INTERVAL_MS;return rn=new ia.MeterProvider({resource:i,readers:[new ia.PeriodicExportingMetricReader({exporter:u,exportIntervalMillis:t})]}),gt.setGlobalMeterProvider(rn),r.debug(`[manifest] Metrics exporter -> ${o.endpoint}/v1/metrics (interval=${t}ms)`),Lc=Ge.getTracer("manifest-plugin","5.31.1"),Ic=gt.getMeter("manifest-plugin","5.31.1"),{tracer:Lc,meter:Ic}}async function YI(o){o.info("[manifest] Shutting down telemetry..."),tn&&(await tn.shutdown(),tn=null),rn&&(await rn.shutdown(),rn=null),Lc=null,Ic=null,o.info("[manifest] Telemetry shut down")}m();var L5=5,KI=30*60*1e3,I5=3e3,x5=5*60*1e3,C5=new Set(["system","developer"]),b5=10,nn=new Map,FI=!1;function U5(){if(FI)return;FI=!0;let o=setInterval(()=>{let r=Date.now();for(let[i,c]of nn)r-c.lastUpdated>KI&&nn.delete(i)},x5);typeof o=="object"&&"unref"in o&&o.unref()}async function jI(o,r,i,c){U5();let u=`${o.endpoint.replace(/\/otlp(\/v1)?\/?$/,"")}/api/v1/routing/resolve`;try{if(!r||!Array.isArray(r)||r.length===0)return c.debug("[manifest] Routing resolve: no messages, skipping"),null;let t=r.filter(C=>C&&typeof C=="object"&&"role"in C&&!C5.has(C.role)).slice(-b5).map(C=>({role:C.role,content:C.content}));if(t.length===0)return c.debug("[manifest] Routing resolve: no scorable messages, skipping"),null;let e=nn.get(i),n=e&&Date.now()-e.lastUpdated<KI?e.tiers:void 0,s={messages:t};n&&(s.recentTiers=n);let l={"Content-Type":"application/json"};o.apiKey&&(l.Authorization=`Bearer ${o.apiKey}`);let E=await fetch(u,{method:"POST",headers:l,body:JSON.stringify(s),signal:AbortSignal.timeout(I5)});if(!E.ok)return c.debug(`[manifest] Routing resolve returned ${E.status}`),null;let A=await E.json();if(!A.model)return c.debug(`[manifest] Routing resolve: no model for tier=${A.tier}`),null;let g=nn.get(i);return g?(g.tiers=[A.tier,...g.tiers].slice(0,L5),g.lastUpdated=Date.now()):nn.set(i,{tiers:[A.tier],lastUpdated:Date.now()}),c.debug(`[manifest] Routing resolved: tier=${A.tier} model=${A.model} provider=${A.provider}`),{tier:A.tier,model:A.model,provider:A.provider??"unknown",reason:A.reason??"",auth_type:A.auth_type??"api_key"}}catch(t){let e=t instanceof Error?t.message:String(t);return c.debug(`[manifest] Routing resolve failed (${e})`),null}}function qI(o,r,i){if(typeof o.registerProvider!="function"){i.debug("[manifest] registerProvider not available, skipping provider registration");return}let c=r.endpoint.replace(/\/otlp(\/v1)?\/?$/,"");try{o.registerProvider({id:"manifest",name:"Manifest Router",label:"Manifest Router",api:"openai-completions",baseUrl:c,apiKey:r.apiKey,models:["auto"]}),i.info("[manifest] Registered as OpenAI-compatible provider (proxy mode)")}catch(a){let u=a instanceof Error?a.message:String(a);i.debug(`[manifest] registerProvider failed (${u})`)}}var Er=new Map,WI,zI,JI,XI,D5,$I,QI,ZI,ex;function tx(o){WI=o.createCounter(Oe.LLM_REQUESTS,{description:"Total LLM inference requests"}),zI=o.createCounter(Oe.LLM_TOKENS_INPUT,{description:"Total input tokens sent to LLM"}),JI=o.createCounter(Oe.LLM_TOKENS_OUTPUT,{description:"Total output tokens from LLM"}),XI=o.createCounter(Oe.LLM_TOKENS_CACHE_READ,{description:"Total cache-read tokens"}),D5=o.createHistogram(Oe.LLM_DURATION,{description:"LLM request duration in ms",unit:"ms"}),$I=o.createCounter(Oe.TOOL_CALLS,{description:"Total tool invocations"}),QI=o.createCounter(Oe.TOOL_ERRORS,{description:"Total tool errors"}),ZI=o.createHistogram(Oe.TOOL_DURATION,{description:"Tool execution duration in ms",unit:"ms"}),ex=o.createCounter(Oe.MESSAGES_RECEIVED,{description:"Total messages received from users"})}function aa(o,r,i){typeof o.on=="function"?o.on(r,i):typeof o.registerHook=="function"&&o.registerHook(r,i)}function rx(o,r,i,c){aa(o,"message_received",a=>{let u=a.sessionKey||a.session?.key||`agent:${a.agent||"main"}:main`,t=a.channel||"unknown",e=r.startSpan(sn.REQUEST,{kind:we.SERVER,attributes:{[b.SESSION_KEY]:u,[b.CHANNEL]:t}});Er.set(u,{root:e}),ex.add(1,{[b.CHANNEL]:t}),c.debug(`[manifest] Root span started for session=${u}`)}),aa(o,"before_agent_start",a=>{let u=a.sessionKey||a.session?.key||`agent:${a.agent||"main"}:main`,t=a.agent||"main",e=Er.get(u),n=e?.root?Ge.setSpan(Be.active(),e.root):Be.active(),s=r.startSpan(sn.AGENT_TURN,{kind:we.INTERNAL,attributes:{[b.AGENT_NAME]:t,[b.SESSION_KEY]:u}},n);e?e.turn=s:Er.set(u,{root:s,turn:s}),c.debug(`[manifest] Agent turn started: agent=${t}, session=${u}`)}),aa(o,"tool_result_persist",a=>{let u=a.toolName||a.tool||"unknown",t=a.durationMs||0,e=a.error==null,n=a.sessionKey||"unknown",s=Er.get(n),l=s?.turn?Ge.setSpan(Be.active(),s.turn):Be.active(),E=r.startSpan(`${sn.TOOL_PREFIX}${u}`,{kind:we.INTERNAL,attributes:{[b.TOOL_NAME]:u,[b.TOOL_SUCCESS]:String(e),[b.SESSION_KEY]:n}},l);e||(E.setStatus({code:rt.ERROR,message:a.error?.message||"Tool execution failed"}),QI.add(1,{[b.TOOL_NAME]:u})),E.end(),$I.add(1,{[b.TOOL_NAME]:u}),ZI.record(t,{[b.TOOL_NAME]:u})}),aa(o,"agent_end",async a=>{let u=a.sessionKey||a.session?.key||`agent:${a.agent||"main"}:main`,t=a.messages||[],e=[...t].reverse().find(k=>k.role==="assistant"&&k.usage),n=e?.model||a.model||"unknown",s=e?.provider||a.provider||"unknown",l=e?.usage||a.usage||{},E=l.input||l.inputTokens||l.prompt_tokens||l.promptTokens||0,A=l.output||l.outputTokens||l.completion_tokens||l.completionTokens||0,g=l.prompt_tokens_details||{},C=l.cacheRead||l.cacheReadTokens||l.cache_read_tokens||g.cached_tokens||0,on=l.cacheWrite||l.cacheWriteTokens||l.cache_creation_tokens||0,Le=n,Sr=s,Qe=null,mt=null;if(Le==="auto"){let k=await jI(i,t,u,c);k&&(Le=k.model,Sr=k.provider,Qe=k.tier,mt=k.reason||null)}let ht=[...t].reverse().find(k=>k?.role==="user");(ht?typeof ht.content=="string"?ht.content.includes("HEARTBEAT_OK"):Array.isArray(ht.content)?ht.content.some(k=>k.type==="text"&&typeof k.text=="string"&&k.text.includes("HEARTBEAT_OK")):!1:!1)&&(mt="heartbeat",Qe="simple");let Ae=Er.get(u);if(Ae?.turn){if(Ae.turn.setAttributes({[b.MODEL]:Le,[b.PROVIDER]:Sr,[b.INPUT_TOKENS]:E,[b.OUTPUT_TOKENS]:A,[b.CACHE_READ_TOKENS]:C,[b.CACHE_WRITE_TOKENS]:on}),Qe&&Ae.turn.setAttribute(b.ROUTING_TIER,Qe),mt&&Ae.turn.setAttribute(b.ROUTING_REASON,mt),a.success===!1||a.error!=null){let k=a.error?.message||a.errorMessage||"Agent turn failed";Ae.turn.setStatus({code:rt.ERROR,message:typeof k=="string"?k.slice(0,500):String(k)})}Ae.turn.end()}Ae?.root&&Ae.root!==Ae.turn&&Ae.root.end(),Er.delete(u);let an={[b.MODEL]:Le,[b.PROVIDER]:Sr};WI.add(1,an),zI.add(E,an),JI.add(A,an),C>0&&XI.add(C,an),c.debug(`[manifest] agent_end tokens: in=${E}, out=${A}, cache=${C}`),c.debug(`[manifest] Trace completed for session=${u}`)}),c.debug("[manifest] All hooks registered")}async function Ot(o){let r=o.endpoint.replace(/\/otlp(\/v1)?\/?$/,""),i={endpointReachable:!1,authValid:!1,agentName:null,error:null};try{let c=await fetch(`${r}/api/v1/health`,{signal:AbortSignal.timeout(5e3)});if(!c.ok)return i.error=`Health endpoint returned ${c.status}`,i;i.endpointReachable=!0}catch(c){let a=c instanceof Error?c.message:String(c);return i.error=`Cannot reach endpoint: ${a}`,i}try{let c=o.apiKey?{Authorization:`Bearer ${o.apiKey}`}:{},a=await fetch(`${r}/api/v1/agent/usage?range=24h`,{headers:c,signal:AbortSignal.timeout(5e3)});if(a.status===401||a.status===403)return i.error="API key rejected \u2014 check your mnfst_ key is correct",i;if(!a.ok)return i.error=`Usage endpoint returned ${a.status}`,i;i.authValid=!0;let u=await a.json();u&&typeof u.agentName=="string"&&(i.agentName=u.agentName)}catch(c){let a=c instanceof Error?c.message:String(c);return i.error=`Auth check failed: ${a}`,i}return i}var ua={today:"24h",week:"7d",month:"30d"};async function sa(o,r,i,c){let a=`${o}${r}`;try{let u=i?{Authorization:`Bearer ${i}`}:{},t=await fetch(a,{headers:u});if(!t.ok)return{content:[{type:"text",text:JSON.stringify({error:`API returned ${t.status}`})}]};let e=await t.json();return{content:[{type:"text",text:JSON.stringify(e)}]}}catch(u){let t=u instanceof Error?u.message:String(u);return c.error(`[manifest] API call failed: ${t}`),{content:[{type:"text",text:JSON.stringify({error:t})}]}}}function nx(o){try{let r=JSON.parse(o.content[0].text);return r.error?{error:r.error}:{result:r}}catch{return{result:o.content[0].text}}}function ox(o,r,i){let c=r.endpoint.replace(/\/otlp(\/v1)?\/?$/,"");o.registerTool({name:"manifest_usage",description:"Get token consumption for this agent: total, input, output, cache-read tokens, and action count. Use when the user asks about token usage or consumption.",parameters:{type:"object",properties:{period:{type:"string",enum:["today","week","month"],default:"today",description:"Time period"}}},async handler(a){let u=ua[a.period||"today"]||"24h";return nx(await sa(c,`/api/v1/agent/usage?range=${u}`,r.apiKey,i))},async execute(a,u){let t=ua[u.period||"today"]||"24h";return sa(c,`/api/v1/agent/usage?range=${t}`,r.apiKey,i)}},{optional:!0}),o.registerTool({name:"manifest_costs",description:"Get cost breakdown for this agent in USD, grouped by model. Use when the user asks about costs, spending, or money burned.",parameters:{type:"object",properties:{period:{type:"string",enum:["today","week","month"],default:"week",description:"Time period"}}},async handler(a){let u=ua[a.period||"week"]||"7d";return nx(await sa(c,`/api/v1/agent/costs?range=${u}`,r.apiKey,i))},async execute(a,u){let t=ua[u.period||"week"]||"7d";return sa(c,`/api/v1/agent/costs?range=${t}`,r.apiKey,i)}},{optional:!0}),o.registerTool({name:"manifest_health",description:"Check whether Manifest observability is connected and working. Use when the user asks if monitoring is set up or wants a connectivity test.",parameters:{type:"object",properties:{}},async handler(){let a=await Ot(r);return a.error?{error:a.error}:{result:{endpointReachable:a.endpointReachable,authValid:a.authValid,agentName:a.agentName,status:"ok"}}},async execute(){let a=await Ot(r);return a.error?{content:[{type:"text",text:JSON.stringify({error:a.error})}]}:{content:[{type:"text",text:JSON.stringify({endpointReachable:a.endpointReachable,authValid:a.authValid,agentName:a.agentName,status:"ok"})}]}}},{optional:!0}),i.debug("[manifest] Registered agent tools: manifest_usage, manifest_costs, manifest_health")}function ix(o,r,i){if(typeof o.registerCommand!="function"){i.debug("[manifest] registerCommand not available, skipping /manifest command");return}let c=async()=>{try{let a=await Ot(r),u=[`Mode: ${r.mode}`,`Dev mode: ${r.devMode?"yes":"no"}`,`Endpoint reachable: ${a.endpointReachable?"yes":"no"}`,`Auth valid: ${a.authValid?"yes":"no"}`];return a.agentName&&u.push(`Agent: ${a.agentName}`),a.error&&u.push(`Error: ${a.error}`),u.join(`
19
+ or export MANIFEST_API_KEY=${Ie}YOUR_KEY`}var oa=ve(cM()),ia=ve(Ai()),BI=ve(II()),GI=ve(wI()),HI=ve(mo());m();var tn=null,rn=null,Lc=null,Ic=null;function kI(o,r){let i=new HI.Resource({"service.name":dr.SERVICE_NAME,"service.version":"5.32.0","manifest.plugin":"true"}),c=o.apiKey?{Authorization:`Bearer ${o.apiKey}`}:{},a=new BI.OTLPTraceExporter({url:`${o.endpoint}/v1/traces`,headers:c});tn=new oa.BasicTracerProvider({resource:i,spanProcessors:[new oa.BatchSpanProcessor(a,{scheduledDelayMillis:5e3,maxQueueSize:2048,maxExportBatchSize:512})]}),tn.register(),r.debug(`[manifest] Trace exporter -> ${o.endpoint}/v1/traces`);let u=new GI.OTLPMetricExporter({url:`${o.endpoint}/v1/metrics`,headers:c}),t=o.devMode||o.mode==="local"?Cc.METRICS_INTERVAL_MS:dr.METRICS_INTERVAL_MS;return rn=new ia.MeterProvider({resource:i,readers:[new ia.PeriodicExportingMetricReader({exporter:u,exportIntervalMillis:t})]}),gt.setGlobalMeterProvider(rn),r.debug(`[manifest] Metrics exporter -> ${o.endpoint}/v1/metrics (interval=${t}ms)`),Lc=Ge.getTracer("manifest-plugin","5.32.0"),Ic=gt.getMeter("manifest-plugin","5.32.0"),{tracer:Lc,meter:Ic}}async function YI(o){o.info("[manifest] Shutting down telemetry..."),tn&&(await tn.shutdown(),tn=null),rn&&(await rn.shutdown(),rn=null),Lc=null,Ic=null,o.info("[manifest] Telemetry shut down")}m();var L5=5,KI=30*60*1e3,I5=3e3,x5=5*60*1e3,C5=new Set(["system","developer"]),b5=10,nn=new Map,FI=!1;function U5(){if(FI)return;FI=!0;let o=setInterval(()=>{let r=Date.now();for(let[i,c]of nn)r-c.lastUpdated>KI&&nn.delete(i)},x5);typeof o=="object"&&"unref"in o&&o.unref()}async function jI(o,r,i,c){U5();let u=`${o.endpoint.replace(/\/otlp(\/v1)?\/?$/,"")}/api/v1/routing/resolve`;try{if(!r||!Array.isArray(r)||r.length===0)return c.debug("[manifest] Routing resolve: no messages, skipping"),null;let t=r.filter(C=>C&&typeof C=="object"&&"role"in C&&!C5.has(C.role)).slice(-b5).map(C=>({role:C.role,content:C.content}));if(t.length===0)return c.debug("[manifest] Routing resolve: no scorable messages, skipping"),null;let e=nn.get(i),n=e&&Date.now()-e.lastUpdated<KI?e.tiers:void 0,s={messages:t};n&&(s.recentTiers=n);let l={"Content-Type":"application/json"};o.apiKey&&(l.Authorization=`Bearer ${o.apiKey}`);let E=await fetch(u,{method:"POST",headers:l,body:JSON.stringify(s),signal:AbortSignal.timeout(I5)});if(!E.ok)return c.debug(`[manifest] Routing resolve returned ${E.status}`),null;let A=await E.json();if(!A.model)return c.debug(`[manifest] Routing resolve: no model for tier=${A.tier}`),null;let g=nn.get(i);return g?(g.tiers=[A.tier,...g.tiers].slice(0,L5),g.lastUpdated=Date.now()):nn.set(i,{tiers:[A.tier],lastUpdated:Date.now()}),c.debug(`[manifest] Routing resolved: tier=${A.tier} model=${A.model} provider=${A.provider}`),{tier:A.tier,model:A.model,provider:A.provider??"unknown",reason:A.reason??"",auth_type:A.auth_type??"api_key"}}catch(t){let e=t instanceof Error?t.message:String(t);return c.debug(`[manifest] Routing resolve failed (${e})`),null}}function qI(o,r,i){if(typeof o.registerProvider!="function"){i.debug("[manifest] registerProvider not available, skipping provider registration");return}let c=r.endpoint.replace(/\/otlp(\/v1)?\/?$/,"");try{o.registerProvider({id:"manifest",name:"Manifest Router",label:"Manifest Router",api:"openai-completions",baseUrl:c,apiKey:r.apiKey,models:["auto"]}),i.info("[manifest] Registered as OpenAI-compatible provider (proxy mode)")}catch(a){let u=a instanceof Error?a.message:String(a);i.debug(`[manifest] registerProvider failed (${u})`)}}var Er=new Map,WI,zI,JI,XI,D5,$I,QI,ZI,ex;function tx(o){WI=o.createCounter(Oe.LLM_REQUESTS,{description:"Total LLM inference requests"}),zI=o.createCounter(Oe.LLM_TOKENS_INPUT,{description:"Total input tokens sent to LLM"}),JI=o.createCounter(Oe.LLM_TOKENS_OUTPUT,{description:"Total output tokens from LLM"}),XI=o.createCounter(Oe.LLM_TOKENS_CACHE_READ,{description:"Total cache-read tokens"}),D5=o.createHistogram(Oe.LLM_DURATION,{description:"LLM request duration in ms",unit:"ms"}),$I=o.createCounter(Oe.TOOL_CALLS,{description:"Total tool invocations"}),QI=o.createCounter(Oe.TOOL_ERRORS,{description:"Total tool errors"}),ZI=o.createHistogram(Oe.TOOL_DURATION,{description:"Tool execution duration in ms",unit:"ms"}),ex=o.createCounter(Oe.MESSAGES_RECEIVED,{description:"Total messages received from users"})}function aa(o,r,i){typeof o.on=="function"?o.on(r,i):typeof o.registerHook=="function"&&o.registerHook(r,i)}function rx(o,r,i,c){aa(o,"message_received",a=>{let u=a.sessionKey||a.session?.key||`agent:${a.agent||"main"}:main`,t=a.channel||"unknown",e=r.startSpan(sn.REQUEST,{kind:we.SERVER,attributes:{[b.SESSION_KEY]:u,[b.CHANNEL]:t}});Er.set(u,{root:e}),ex.add(1,{[b.CHANNEL]:t}),c.debug(`[manifest] Root span started for session=${u}`)}),aa(o,"before_agent_start",a=>{let u=a.sessionKey||a.session?.key||`agent:${a.agent||"main"}:main`,t=a.agent||"main",e=Er.get(u),n=e?.root?Ge.setSpan(Be.active(),e.root):Be.active(),s=r.startSpan(sn.AGENT_TURN,{kind:we.INTERNAL,attributes:{[b.AGENT_NAME]:t,[b.SESSION_KEY]:u}},n);e?e.turn=s:Er.set(u,{root:s,turn:s}),c.debug(`[manifest] Agent turn started: agent=${t}, session=${u}`)}),aa(o,"tool_result_persist",a=>{let u=a.toolName||a.tool||"unknown",t=a.durationMs||0,e=a.error==null,n=a.sessionKey||"unknown",s=Er.get(n),l=s?.turn?Ge.setSpan(Be.active(),s.turn):Be.active(),E=r.startSpan(`${sn.TOOL_PREFIX}${u}`,{kind:we.INTERNAL,attributes:{[b.TOOL_NAME]:u,[b.TOOL_SUCCESS]:String(e),[b.SESSION_KEY]:n}},l);e||(E.setStatus({code:rt.ERROR,message:a.error?.message||"Tool execution failed"}),QI.add(1,{[b.TOOL_NAME]:u})),E.end(),$I.add(1,{[b.TOOL_NAME]:u}),ZI.record(t,{[b.TOOL_NAME]:u})}),aa(o,"agent_end",async a=>{let u=a.sessionKey||a.session?.key||`agent:${a.agent||"main"}:main`,t=a.messages||[],e=[...t].reverse().find(k=>k.role==="assistant"&&k.usage),n=e?.model||a.model||"unknown",s=e?.provider||a.provider||"unknown",l=e?.usage||a.usage||{},E=l.input||l.inputTokens||l.prompt_tokens||l.promptTokens||0,A=l.output||l.outputTokens||l.completion_tokens||l.completionTokens||0,g=l.prompt_tokens_details||{},C=l.cacheRead||l.cacheReadTokens||l.cache_read_tokens||g.cached_tokens||0,on=l.cacheWrite||l.cacheWriteTokens||l.cache_creation_tokens||0,Le=n,Sr=s,Qe=null,mt=null;if(Le==="auto"){let k=await jI(i,t,u,c);k&&(Le=k.model,Sr=k.provider,Qe=k.tier,mt=k.reason||null)}let ht=[...t].reverse().find(k=>k?.role==="user");(ht?typeof ht.content=="string"?ht.content.includes("HEARTBEAT_OK"):Array.isArray(ht.content)?ht.content.some(k=>k.type==="text"&&typeof k.text=="string"&&k.text.includes("HEARTBEAT_OK")):!1:!1)&&(mt="heartbeat",Qe="simple");let Ae=Er.get(u);if(Ae?.turn){if(Ae.turn.setAttributes({[b.MODEL]:Le,[b.PROVIDER]:Sr,[b.INPUT_TOKENS]:E,[b.OUTPUT_TOKENS]:A,[b.CACHE_READ_TOKENS]:C,[b.CACHE_WRITE_TOKENS]:on}),Qe&&Ae.turn.setAttribute(b.ROUTING_TIER,Qe),mt&&Ae.turn.setAttribute(b.ROUTING_REASON,mt),a.success===!1||a.error!=null){let k=a.error?.message||a.errorMessage||"Agent turn failed";Ae.turn.setStatus({code:rt.ERROR,message:typeof k=="string"?k.slice(0,500):String(k)})}Ae.turn.end()}Ae?.root&&Ae.root!==Ae.turn&&Ae.root.end(),Er.delete(u);let an={[b.MODEL]:Le,[b.PROVIDER]:Sr};WI.add(1,an),zI.add(E,an),JI.add(A,an),C>0&&XI.add(C,an),c.debug(`[manifest] agent_end tokens: in=${E}, out=${A}, cache=${C}`),c.debug(`[manifest] Trace completed for session=${u}`)}),c.debug("[manifest] All hooks registered")}async function Ot(o){let r=o.endpoint.replace(/\/otlp(\/v1)?\/?$/,""),i={endpointReachable:!1,authValid:!1,agentName:null,error:null};try{let c=await fetch(`${r}/api/v1/health`,{signal:AbortSignal.timeout(5e3)});if(!c.ok)return i.error=`Health endpoint returned ${c.status}`,i;i.endpointReachable=!0}catch(c){let a=c instanceof Error?c.message:String(c);return i.error=`Cannot reach endpoint: ${a}`,i}try{let c=o.apiKey?{Authorization:`Bearer ${o.apiKey}`}:{},a=await fetch(`${r}/api/v1/agent/usage?range=24h`,{headers:c,signal:AbortSignal.timeout(5e3)});if(a.status===401||a.status===403)return i.error="API key rejected \u2014 check your mnfst_ key is correct",i;if(!a.ok)return i.error=`Usage endpoint returned ${a.status}`,i;i.authValid=!0;let u=await a.json();u&&typeof u.agentName=="string"&&(i.agentName=u.agentName)}catch(c){let a=c instanceof Error?c.message:String(c);return i.error=`Auth check failed: ${a}`,i}return i}var ua={today:"24h",week:"7d",month:"30d"};async function sa(o,r,i,c){let a=`${o}${r}`;try{let u=i?{Authorization:`Bearer ${i}`}:{},t=await fetch(a,{headers:u});if(!t.ok)return{content:[{type:"text",text:JSON.stringify({error:`API returned ${t.status}`})}]};let e=await t.json();return{content:[{type:"text",text:JSON.stringify(e)}]}}catch(u){let t=u instanceof Error?u.message:String(u);return c.error(`[manifest] API call failed: ${t}`),{content:[{type:"text",text:JSON.stringify({error:t})}]}}}function nx(o){try{let r=JSON.parse(o.content[0].text);return r.error?{error:r.error}:{result:r}}catch{return{result:o.content[0].text}}}function ox(o,r,i){let c=r.endpoint.replace(/\/otlp(\/v1)?\/?$/,"");o.registerTool({name:"manifest_usage",description:"Get token consumption for this agent: total, input, output, cache-read tokens, and action count. Use when the user asks about token usage or consumption.",parameters:{type:"object",properties:{period:{type:"string",enum:["today","week","month"],default:"today",description:"Time period"}}},async handler(a){let u=ua[a.period||"today"]||"24h";return nx(await sa(c,`/api/v1/agent/usage?range=${u}`,r.apiKey,i))},async execute(a,u){let t=ua[u.period||"today"]||"24h";return sa(c,`/api/v1/agent/usage?range=${t}`,r.apiKey,i)}},{optional:!0}),o.registerTool({name:"manifest_costs",description:"Get cost breakdown for this agent in USD, grouped by model. Use when the user asks about costs, spending, or money burned.",parameters:{type:"object",properties:{period:{type:"string",enum:["today","week","month"],default:"week",description:"Time period"}}},async handler(a){let u=ua[a.period||"week"]||"7d";return nx(await sa(c,`/api/v1/agent/costs?range=${u}`,r.apiKey,i))},async execute(a,u){let t=ua[u.period||"week"]||"7d";return sa(c,`/api/v1/agent/costs?range=${t}`,r.apiKey,i)}},{optional:!0}),o.registerTool({name:"manifest_health",description:"Check whether Manifest observability is connected and working. Use when the user asks if monitoring is set up or wants a connectivity test.",parameters:{type:"object",properties:{}},async handler(){let a=await Ot(r);return a.error?{error:a.error}:{result:{endpointReachable:a.endpointReachable,authValid:a.authValid,agentName:a.agentName,status:"ok"}}},async execute(){let a=await Ot(r);return a.error?{content:[{type:"text",text:JSON.stringify({error:a.error})}]}:{content:[{type:"text",text:JSON.stringify({endpointReachable:a.endpointReachable,authValid:a.authValid,agentName:a.agentName,status:"ok"})}]}}},{optional:!0}),i.debug("[manifest] Registered agent tools: manifest_usage, manifest_costs, manifest_health")}function ix(o,r,i){if(typeof o.registerCommand!="function"){i.debug("[manifest] registerCommand not available, skipping /manifest command");return}let c=async()=>{try{let a=await Ot(r),u=[`Mode: ${r.mode}`,`Dev mode: ${r.devMode?"yes":"no"}`,`Endpoint reachable: ${a.endpointReachable?"yes":"no"}`,`Auth valid: ${a.authValid?"yes":"no"}`];return a.agentName&&u.push(`Agent: ${a.agentName}`),a.error&&u.push(`Error: ${a.error}`),u.join(`
20
20
  `)}catch(a){return`Manifest status check failed: ${a instanceof Error?a.message:String(a)}`}};o.registerCommand({name:"manifest",description:"Show Manifest plugin status and connection info",async handler(){return c()},async execute(){let a=await c();return{text:a,content:[{type:"text",text:a}]}}}),i.debug("[manifest] Registered /manifest command")}var Tr=require("./local-mode"),ca=require("./subscription");module.exports={id:"manifest",name:"Manifest \u2014 Agent Observability",register(o){let r=o.logger||{info:(...A)=>console.log(...A),debug:()=>{},error:(...A)=>console.error(...A),warn:(...A)=>console.warn(...A)},{config:i,_deprecatedDevMode:c}=bc(o.pluginConfig);if(c&&r.warn?.(`[manifest] mode: "dev" is deprecated. Use mode: "cloud" with devMode: true instead.
21
21
  openclaw config set plugins.entries.manifest.config.mode cloud
22
22
  openclaw config set plugins.entries.manifest.config.devMode true`),i.mode==="local"){(0,Tr.registerLocalMode)(o,i,r);return}let a=Uc(i);if(a){!i.devMode&&i.mode==="cloud"&&!i.apiKey?r.info(`[manifest] Cloud mode requires an API key: