@shipfox/api-agent 9.2.0 → 10.0.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.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 52 files with swc (456.07ms)
2
+ Successfully compiled: 52 files with swc (366.13ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @shipfox/api-agent
2
2
 
3
+ ## 10.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 43ce975: Align Pi harness compatibility and provider catalog metadata with the current Pi SDK.
8
+ - Updated dependencies [74f9e31]
9
+ - Updated dependencies [43ce975]
10
+ - @shipfox/node-fastify@0.4.0
11
+ - @shipfox/api-agent-dto@10.0.0
12
+ - @shipfox/api-auth-context@10.0.0
13
+ - @shipfox/node-module@1.0.4
14
+ - @shipfox/api-secrets-dto@9.0.2
15
+ - @shipfox/config@1.2.4
16
+ - @shipfox/inter-module@0.2.2
17
+ - @shipfox/redact@0.2.5
18
+ - @shipfox/node-drizzle@0.3.4
19
+ - @shipfox/node-egress-guard@0.1.3
20
+ - @shipfox/node-error-monitoring@0.3.0
21
+ - @shipfox/node-opentelemetry@0.6.3
22
+ - @shipfox/node-postgres@0.4.4
23
+
24
+ ## 9.3.0
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [4425c6d]
29
+ - @shipfox/node-opentelemetry@0.6.3
30
+ - @shipfox/api-auth-context@9.3.0
31
+ - @shipfox/node-fastify@0.3.4
32
+ - @shipfox/node-module@1.0.3
33
+
3
34
  ## 9.2.0
4
35
 
5
36
  ### Patch Changes
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare const config: Readonly<{
2
- AGENT_DEFAULT_PROVIDER: "anthropic" | "ant-ling" | "azure-openai-responses" | "openai" | "deepseek" | "nvidia" | "google" | "mistral" | "groq" | "cerebras" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "xai" | "openrouter" | "vercel-ai-gateway" | "zai" | "zai-coding-cn" | "opencode" | "opencode-go" | "huggingface" | "fireworks" | "together" | "kimi-coding" | "minimax" | "minimax-cn" | "moonshotai" | "moonshotai-cn" | "xiaomi" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-sgp" | undefined;
2
+ AGENT_DEFAULT_PROVIDER: "anthropic" | "ant-ling" | "azure-openai-responses" | "openai" | "deepseek" | "nvidia" | "google" | "mistral" | "groq" | "cerebras" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "xai" | "openrouter" | "vercel-ai-gateway" | "zai" | "zai-coding-cn" | "opencode" | "opencode-go" | "huggingface" | "fireworks" | "together" | "kimi-coding" | "qwen-token-plan" | "qwen-token-plan-cn" | "minimax" | "minimax-cn" | "moonshotai" | "moonshotai-cn" | "xiaomi" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-sgp" | undefined;
3
3
  AGENT_DEFAULT_PROVIDER_MODEL: string | undefined;
4
4
  AGENT_DEFAULT_PROVIDER_THINKING: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
5
5
  AGENT_DEFAULT_PROVIDER_API_KEY: string | undefined;
@@ -1,4 +1,4 @@
1
- import { getModels } from '@earendil-works/pi-ai';
1
+ import { getModels } from '@earendil-works/pi-ai/compat';
2
2
  import { PI_HARNESS } from '@shipfox/api-agent-dto';
3
3
  import { probeModelProviderCredentials } from '../model-provider-validation.js';
4
4
  export { PI_HARNESS };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/harness/pi.ts"],"sourcesContent":["import {getModels, type KnownProvider} from '@earendil-works/pi-ai';\nimport {type AgentModelOptionDto, PI_HARNESS} from '@shipfox/api-agent-dto';\nimport {probeModelProviderCredentials} from '../model-provider-validation.js';\nimport type {HarnessProviderCatalog} from './registry.js';\n\nexport {PI_HARNESS};\n\nexport function listPiProviderModels(providerId: string): AgentModelOptionDto[] {\n return getModels(providerId as KnownProvider).map((model) => ({\n id: model.id,\n label: model.name,\n }));\n}\n\nexport const piHarnessCatalog: HarnessProviderCatalog = {\n listModels: listPiProviderModels,\n validateCredentials: probeModelProviderCredentials,\n};\n"],"names":["getModels","PI_HARNESS","probeModelProviderCredentials","listPiProviderModels","providerId","map","model","id","label","name","piHarnessCatalog","listModels","validateCredentials"],"mappings":"AAAA,SAAQA,SAAS,QAA2B,wBAAwB;AACpE,SAAkCC,UAAU,QAAO,yBAAyB;AAC5E,SAAQC,6BAA6B,QAAO,kCAAkC;AAG9E,SAAQD,UAAU,GAAE;AAEpB,OAAO,SAASE,qBAAqBC,UAAkB;IACrD,OAAOJ,UAAUI,YAA6BC,GAAG,CAAC,CAACC,QAAW,CAAA;YAC5DC,IAAID,MAAMC,EAAE;YACZC,OAAOF,MAAMG,IAAI;QACnB,CAAA;AACF;AAEA,OAAO,MAAMC,mBAA2C;IACtDC,YAAYR;IACZS,qBAAqBV;AACvB,EAAE"}
1
+ {"version":3,"sources":["../../../src/core/harness/pi.ts"],"sourcesContent":["import {getModels} from '@earendil-works/pi-ai/compat';\nimport {type AgentModelOptionDto, PI_HARNESS} from '@shipfox/api-agent-dto';\nimport {probeModelProviderCredentials} from '../model-provider-validation.js';\nimport type {HarnessProviderCatalog} from './registry.js';\n\nexport {PI_HARNESS};\n\nexport function listPiProviderModels(providerId: string): AgentModelOptionDto[] {\n return getModels(providerId as Parameters<typeof getModels>[0]).map((model) => ({\n id: model.id,\n label: model.name,\n }));\n}\n\nexport const piHarnessCatalog: HarnessProviderCatalog = {\n listModels: listPiProviderModels,\n validateCredentials: probeModelProviderCredentials,\n};\n"],"names":["getModels","PI_HARNESS","probeModelProviderCredentials","listPiProviderModels","providerId","map","model","id","label","name","piHarnessCatalog","listModels","validateCredentials"],"mappings":"AAAA,SAAQA,SAAS,QAAO,+BAA+B;AACvD,SAAkCC,UAAU,QAAO,yBAAyB;AAC5E,SAAQC,6BAA6B,QAAO,kCAAkC;AAG9E,SAAQD,UAAU,GAAE;AAEpB,OAAO,SAASE,qBAAqBC,UAAkB;IACrD,OAAOJ,UAAUI,YAA+CC,GAAG,CAAC,CAACC,QAAW,CAAA;YAC9EC,IAAID,MAAMC,EAAE;YACZC,OAAOF,MAAMG,IAAI;QACnB,CAAA;AACF;AAEA,OAAO,MAAMC,mBAA2C;IACtDC,YAAYR;IACZS,qBAAqBV;AACvB,EAAE"}
@@ -1,4 +1,4 @@
1
- import { complete, getModels } from '@earendil-works/pi-ai';
1
+ import { complete, getModels } from '@earendil-works/pi-ai/compat';
2
2
  import { parseEgressHostDenylist } from '@shipfox/node-egress-guard';
3
3
  import { redactSecrets, secretWireForms } from '@shipfox/redact';
4
4
  import { config } from '#config.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/model-provider-validation.ts"],"sourcesContent":["import {\n type AssistantMessage,\n type Context,\n complete,\n getModels,\n type ProviderStreamOptions,\n} from '@earendil-works/pi-ai';\nimport type {\n CustomAgentModelDto,\n ModelProviderApi,\n ModelProviderRef,\n SupportedModelProviderId,\n} from '@shipfox/api-agent-dto';\nimport {type EgressPolicy, parseEgressHostDenylist} from '@shipfox/node-egress-guard';\nimport {redactSecrets, secretWireForms} from '@shipfox/redact';\nimport {config} from '#config.js';\nimport {modelProviderValidationCount} from '#metrics/index.js';\nimport {appendCustomProviderPath} from './custom-provider-url.js';\nimport {\n InvalidAgentModelError,\n InvalidCredentialFieldsError,\n ModelProviderValidationError,\n ModelProviderValidationUnavailableError,\n UnsupportedModelProviderError,\n} from './errors.js';\nimport {getModelProviderEntry} from './model-provider-policy.js';\n\nconst PROBE_MAX_TOKENS = 64;\nconst MAX_SANITIZED_ERROR_LENGTH = 500;\nconst CUSTOM_PROBE_PROMPT = 'Reply with OK.';\n\nexport interface ProbeModelProviderCredentialsParams {\n providerId: SupportedModelProviderId;\n model: string;\n credentials: Record<string, string>;\n signal?: AbortSignal | undefined;\n}\n\nexport interface ProbeCustomModelProviderCredentialsParams {\n providerId: ModelProviderRef;\n api: ModelProviderApi;\n baseUrl: string;\n model: CustomAgentModelDto;\n apiKey?: string | undefined;\n headers?: Record<string, string> | undefined;\n signal?: AbortSignal | undefined;\n}\n\nexport interface RunProviderProbeParams<TArgs> {\n probe: (args: TArgs) => Promise<void>;\n args: TArgs;\n metricLabel: SupportedModelProviderId | 'custom';\n providerId: ModelProviderRef;\n secrets: string[];\n signal?: AbortSignal | undefined;\n}\n\nexport async function runProviderProbe<TArgs>(\n params: RunProviderProbeParams<TArgs>,\n): Promise<void> {\n try {\n await params.probe(params.args);\n } catch (error) {\n if (params.signal?.aborted) throw error;\n modelProviderValidationCount.add(1, {\n model_provider: params.metricLabel,\n outcome: 'failed',\n });\n if (error instanceof InvalidAgentModelError) throw error;\n\n const sanitizedMessage = sanitizeModelProviderError(error, params.secrets);\n throw new ModelProviderValidationError(params.providerId, sanitizedMessage);\n }\n\n modelProviderValidationCount.add(1, {\n model_provider: params.metricLabel,\n outcome: 'succeeded',\n });\n}\n\nexport async function probeModelProviderCredentials(\n params: ProbeModelProviderCredentialsParams,\n): Promise<void> {\n const entry = getModelProviderEntry(params.providerId);\n if (entry === undefined || entry.support_status !== 'supported') {\n throw new UnsupportedModelProviderError(params.providerId);\n }\n\n const model = getModels(params.providerId).find((candidate) => candidate.id === params.model);\n if (!model) throw new InvalidAgentModelError('pi', params.providerId, params.model);\n\n const secretField = entry.credential_fields.find((field) => field.secret);\n if (!secretField) throw new ModelProviderValidationUnavailableError(params.providerId);\n const apiKey = credentialValue(params, secretField.key);\n\n const context: Context = {\n messages: [\n {\n role: 'user',\n content: 'Reply with OK.',\n timestamp: Date.now(),\n },\n ],\n };\n const options: ProviderStreamOptions = {\n apiKey,\n maxTokens: PROBE_MAX_TOKENS,\n maxRetries: 0,\n timeoutMs: config.AGENT_PROVIDER_VALIDATION_TIMEOUT_MS,\n ...modelProviderCredentialOptions(params),\n ...(params.signal ? {signal: params.signal} : {}),\n };\n\n const result = await complete(model, context, options);\n rejectModelProviderErrorResult(result);\n}\n\nexport async function probeCustomModelProviderCredentials(\n params: ProbeCustomModelProviderCredentialsParams,\n): Promise<void> {\n const response = await fetch(customProbeUrl(params), {\n method: 'POST',\n headers: customProbeHeaders(params),\n body: JSON.stringify(customProbeBody(params)),\n redirect: 'error',\n signal: timeoutSignal(params.signal, config.AGENT_PROVIDER_VALIDATION_TIMEOUT_MS),\n });\n\n try {\n if (!response.ok) {\n throw new Error(`Provider returned HTTP ${response.status}.`);\n }\n } finally {\n await response.body?.cancel();\n }\n}\n\nexport function sanitizeModelProviderError(error: unknown, secrets: string[]): string {\n const message =\n error instanceof Error && error.message.length > 0\n ? error.message\n : 'Provider validation failed.';\n const secretForms = secrets.flatMap((secret) => secretWireForms(secret));\n const redacted = redactSecrets(message, secretForms);\n return redacted.slice(0, MAX_SANITIZED_ERROR_LENGTH);\n}\n\nexport function egressPolicy(): EgressPolicy {\n return {\n allowPrivateNetworks: config.AGENT_CUSTOM_PROVIDER_ALLOW_PRIVATE_NETWORKS,\n hostDenylist: parseEgressHostDenylist(config.AGENT_CUSTOM_PROVIDER_HOST_DENYLIST),\n };\n}\n\nfunction customProbeUrl(params: ProbeCustomModelProviderCredentialsParams): string {\n switch (params.api) {\n case 'anthropic-messages':\n return appendCustomProviderPath(params.baseUrl, 'messages').toString();\n case 'google-generative-ai':\n return appendCustomProviderPath(\n params.baseUrl,\n `models/${encodeURIComponent(params.model.id)}:generateContent`,\n ).toString();\n case 'openai-completions':\n return appendCustomProviderPath(params.baseUrl, 'chat/completions').toString();\n case 'openai-responses':\n return appendCustomProviderPath(params.baseUrl, 'responses').toString();\n default:\n return assertNever(params.api);\n }\n}\n\nfunction customProbeHeaders(params: ProbeCustomModelProviderCredentialsParams): Headers {\n const headers = new Headers(params.headers);\n headers.set('content-type', 'application/json');\n if (!params.apiKey) return headers;\n\n switch (params.api) {\n case 'anthropic-messages':\n headers.set('x-api-key', params.apiKey);\n headers.set('anthropic-version', '2023-06-01');\n break;\n case 'google-generative-ai':\n headers.set('x-goog-api-key', params.apiKey);\n break;\n case 'openai-completions':\n case 'openai-responses':\n headers.set('authorization', `Bearer ${params.apiKey}`);\n break;\n default:\n assertNever(params.api);\n }\n\n return headers;\n}\n\nfunction customProbeBody(params: ProbeCustomModelProviderCredentialsParams): unknown {\n switch (params.api) {\n case 'anthropic-messages':\n return {\n model: params.model.id,\n max_tokens: PROBE_MAX_TOKENS,\n messages: [{role: 'user', content: CUSTOM_PROBE_PROMPT}],\n };\n case 'google-generative-ai':\n return {\n contents: [{role: 'user', parts: [{text: CUSTOM_PROBE_PROMPT}]}],\n generationConfig: {maxOutputTokens: PROBE_MAX_TOKENS},\n };\n case 'openai-completions':\n return {\n model: params.model.id,\n messages: [{role: 'user', content: CUSTOM_PROBE_PROMPT}],\n max_tokens: PROBE_MAX_TOKENS,\n stream: false,\n };\n case 'openai-responses':\n return {\n model: params.model.id,\n input: CUSTOM_PROBE_PROMPT,\n max_output_tokens: PROBE_MAX_TOKENS,\n stream: false,\n store: false,\n };\n default:\n return assertNever(params.api);\n }\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unsupported custom model provider API: ${value}`);\n}\n\nfunction timeoutSignal(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal {\n return signal === undefined\n ? AbortSignal.timeout(timeoutMs)\n : AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]);\n}\n\nfunction modelProviderCredentialOptions(\n params: Pick<ProbeModelProviderCredentialsParams, 'providerId' | 'credentials'>,\n): ProviderStreamOptions {\n switch (params.providerId) {\n case 'azure-openai-responses':\n return {azureBaseUrl: credentialValue(params, 'endpoint')};\n case 'cloudflare-ai-gateway':\n return {\n env: {\n CLOUDFLARE_ACCOUNT_ID: credentialValue(params, 'account_id'),\n CLOUDFLARE_GATEWAY_ID: credentialValue(params, 'gateway_id'),\n },\n };\n case 'cloudflare-workers-ai':\n return {\n env: {\n CLOUDFLARE_ACCOUNT_ID: credentialValue(params, 'account_id'),\n },\n };\n default:\n return {};\n }\n}\n\nfunction credentialValue(\n params: Pick<ProbeModelProviderCredentialsParams, 'providerId' | 'credentials'>,\n key: string,\n): string {\n const value = params.credentials[key];\n if (value === undefined) throw new InvalidCredentialFieldsError(params.providerId);\n return value;\n}\n\nfunction rejectModelProviderErrorResult(result: AssistantMessage): void {\n if (result.stopReason !== 'error' && result.stopReason !== 'aborted') return;\n throw new Error(result.errorMessage ?? 'Provider validation failed.');\n}\n"],"names":["complete","getModels","parseEgressHostDenylist","redactSecrets","secretWireForms","config","modelProviderValidationCount","appendCustomProviderPath","InvalidAgentModelError","InvalidCredentialFieldsError","ModelProviderValidationError","ModelProviderValidationUnavailableError","UnsupportedModelProviderError","getModelProviderEntry","PROBE_MAX_TOKENS","MAX_SANITIZED_ERROR_LENGTH","CUSTOM_PROBE_PROMPT","runProviderProbe","params","probe","args","error","signal","aborted","add","model_provider","metricLabel","outcome","sanitizedMessage","sanitizeModelProviderError","secrets","providerId","probeModelProviderCredentials","entry","undefined","support_status","model","find","candidate","id","secretField","credential_fields","field","secret","apiKey","credentialValue","key","context","messages","role","content","timestamp","Date","now","options","maxTokens","maxRetries","timeoutMs","AGENT_PROVIDER_VALIDATION_TIMEOUT_MS","modelProviderCredentialOptions","result","rejectModelProviderErrorResult","probeCustomModelProviderCredentials","response","fetch","customProbeUrl","method","headers","customProbeHeaders","body","JSON","stringify","customProbeBody","redirect","timeoutSignal","ok","Error","status","cancel","message","length","secretForms","flatMap","redacted","slice","egressPolicy","allowPrivateNetworks","AGENT_CUSTOM_PROVIDER_ALLOW_PRIVATE_NETWORKS","hostDenylist","AGENT_CUSTOM_PROVIDER_HOST_DENYLIST","api","baseUrl","toString","encodeURIComponent","assertNever","Headers","set","max_tokens","contents","parts","text","generationConfig","maxOutputTokens","stream","input","max_output_tokens","store","value","AbortSignal","timeout","any","azureBaseUrl","env","CLOUDFLARE_ACCOUNT_ID","CLOUDFLARE_GATEWAY_ID","credentials","stopReason","errorMessage"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/model-provider-validation.ts"],"sourcesContent":["import {\n type AssistantMessage,\n type Context,\n complete,\n getModels,\n type ProviderStreamOptions,\n} from '@earendil-works/pi-ai/compat';\nimport type {\n CustomAgentModelDto,\n ModelProviderApi,\n ModelProviderRef,\n SupportedModelProviderId,\n} from '@shipfox/api-agent-dto';\nimport {type EgressPolicy, parseEgressHostDenylist} from '@shipfox/node-egress-guard';\nimport {redactSecrets, secretWireForms} from '@shipfox/redact';\nimport {config} from '#config.js';\nimport {modelProviderValidationCount} from '#metrics/index.js';\nimport {appendCustomProviderPath} from './custom-provider-url.js';\nimport {\n InvalidAgentModelError,\n InvalidCredentialFieldsError,\n ModelProviderValidationError,\n ModelProviderValidationUnavailableError,\n UnsupportedModelProviderError,\n} from './errors.js';\nimport {getModelProviderEntry} from './model-provider-policy.js';\n\nconst PROBE_MAX_TOKENS = 64;\nconst MAX_SANITIZED_ERROR_LENGTH = 500;\nconst CUSTOM_PROBE_PROMPT = 'Reply with OK.';\n\nexport interface ProbeModelProviderCredentialsParams {\n providerId: SupportedModelProviderId;\n model: string;\n credentials: Record<string, string>;\n signal?: AbortSignal | undefined;\n}\n\nexport interface ProbeCustomModelProviderCredentialsParams {\n providerId: ModelProviderRef;\n api: ModelProviderApi;\n baseUrl: string;\n model: CustomAgentModelDto;\n apiKey?: string | undefined;\n headers?: Record<string, string> | undefined;\n signal?: AbortSignal | undefined;\n}\n\nexport interface RunProviderProbeParams<TArgs> {\n probe: (args: TArgs) => Promise<void>;\n args: TArgs;\n metricLabel: SupportedModelProviderId | 'custom';\n providerId: ModelProviderRef;\n secrets: string[];\n signal?: AbortSignal | undefined;\n}\n\nexport async function runProviderProbe<TArgs>(\n params: RunProviderProbeParams<TArgs>,\n): Promise<void> {\n try {\n await params.probe(params.args);\n } catch (error) {\n if (params.signal?.aborted) throw error;\n modelProviderValidationCount.add(1, {\n model_provider: params.metricLabel,\n outcome: 'failed',\n });\n if (error instanceof InvalidAgentModelError) throw error;\n\n const sanitizedMessage = sanitizeModelProviderError(error, params.secrets);\n throw new ModelProviderValidationError(params.providerId, sanitizedMessage);\n }\n\n modelProviderValidationCount.add(1, {\n model_provider: params.metricLabel,\n outcome: 'succeeded',\n });\n}\n\nexport async function probeModelProviderCredentials(\n params: ProbeModelProviderCredentialsParams,\n): Promise<void> {\n const entry = getModelProviderEntry(params.providerId);\n if (entry === undefined || entry.support_status !== 'supported') {\n throw new UnsupportedModelProviderError(params.providerId);\n }\n\n const model = getModels(params.providerId).find((candidate) => candidate.id === params.model);\n if (!model) throw new InvalidAgentModelError('pi', params.providerId, params.model);\n\n const secretField = entry.credential_fields.find((field) => field.secret);\n if (!secretField) throw new ModelProviderValidationUnavailableError(params.providerId);\n const apiKey = credentialValue(params, secretField.key);\n\n const context: Context = {\n messages: [\n {\n role: 'user',\n content: 'Reply with OK.',\n timestamp: Date.now(),\n },\n ],\n };\n const options: ProviderStreamOptions = {\n apiKey,\n maxTokens: PROBE_MAX_TOKENS,\n maxRetries: 0,\n timeoutMs: config.AGENT_PROVIDER_VALIDATION_TIMEOUT_MS,\n ...modelProviderCredentialOptions(params),\n ...(params.signal ? {signal: params.signal} : {}),\n };\n\n const result = await complete(model, context, options);\n rejectModelProviderErrorResult(result);\n}\n\nexport async function probeCustomModelProviderCredentials(\n params: ProbeCustomModelProviderCredentialsParams,\n): Promise<void> {\n const response = await fetch(customProbeUrl(params), {\n method: 'POST',\n headers: customProbeHeaders(params),\n body: JSON.stringify(customProbeBody(params)),\n redirect: 'error',\n signal: timeoutSignal(params.signal, config.AGENT_PROVIDER_VALIDATION_TIMEOUT_MS),\n });\n\n try {\n if (!response.ok) {\n throw new Error(`Provider returned HTTP ${response.status}.`);\n }\n } finally {\n await response.body?.cancel();\n }\n}\n\nexport function sanitizeModelProviderError(error: unknown, secrets: string[]): string {\n const message =\n error instanceof Error && error.message.length > 0\n ? error.message\n : 'Provider validation failed.';\n const secretForms = secrets.flatMap((secret) => secretWireForms(secret));\n const redacted = redactSecrets(message, secretForms);\n return redacted.slice(0, MAX_SANITIZED_ERROR_LENGTH);\n}\n\nexport function egressPolicy(): EgressPolicy {\n return {\n allowPrivateNetworks: config.AGENT_CUSTOM_PROVIDER_ALLOW_PRIVATE_NETWORKS,\n hostDenylist: parseEgressHostDenylist(config.AGENT_CUSTOM_PROVIDER_HOST_DENYLIST),\n };\n}\n\nfunction customProbeUrl(params: ProbeCustomModelProviderCredentialsParams): string {\n switch (params.api) {\n case 'anthropic-messages':\n return appendCustomProviderPath(params.baseUrl, 'messages').toString();\n case 'google-generative-ai':\n return appendCustomProviderPath(\n params.baseUrl,\n `models/${encodeURIComponent(params.model.id)}:generateContent`,\n ).toString();\n case 'openai-completions':\n return appendCustomProviderPath(params.baseUrl, 'chat/completions').toString();\n case 'openai-responses':\n return appendCustomProviderPath(params.baseUrl, 'responses').toString();\n default:\n return assertNever(params.api);\n }\n}\n\nfunction customProbeHeaders(params: ProbeCustomModelProviderCredentialsParams): Headers {\n const headers = new Headers(params.headers);\n headers.set('content-type', 'application/json');\n if (!params.apiKey) return headers;\n\n switch (params.api) {\n case 'anthropic-messages':\n headers.set('x-api-key', params.apiKey);\n headers.set('anthropic-version', '2023-06-01');\n break;\n case 'google-generative-ai':\n headers.set('x-goog-api-key', params.apiKey);\n break;\n case 'openai-completions':\n case 'openai-responses':\n headers.set('authorization', `Bearer ${params.apiKey}`);\n break;\n default:\n assertNever(params.api);\n }\n\n return headers;\n}\n\nfunction customProbeBody(params: ProbeCustomModelProviderCredentialsParams): unknown {\n switch (params.api) {\n case 'anthropic-messages':\n return {\n model: params.model.id,\n max_tokens: PROBE_MAX_TOKENS,\n messages: [{role: 'user', content: CUSTOM_PROBE_PROMPT}],\n };\n case 'google-generative-ai':\n return {\n contents: [{role: 'user', parts: [{text: CUSTOM_PROBE_PROMPT}]}],\n generationConfig: {maxOutputTokens: PROBE_MAX_TOKENS},\n };\n case 'openai-completions':\n return {\n model: params.model.id,\n messages: [{role: 'user', content: CUSTOM_PROBE_PROMPT}],\n max_tokens: PROBE_MAX_TOKENS,\n stream: false,\n };\n case 'openai-responses':\n return {\n model: params.model.id,\n input: CUSTOM_PROBE_PROMPT,\n max_output_tokens: PROBE_MAX_TOKENS,\n stream: false,\n store: false,\n };\n default:\n return assertNever(params.api);\n }\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unsupported custom model provider API: ${value}`);\n}\n\nfunction timeoutSignal(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal {\n return signal === undefined\n ? AbortSignal.timeout(timeoutMs)\n : AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]);\n}\n\nfunction modelProviderCredentialOptions(\n params: Pick<ProbeModelProviderCredentialsParams, 'providerId' | 'credentials'>,\n): ProviderStreamOptions {\n switch (params.providerId) {\n case 'azure-openai-responses':\n return {azureBaseUrl: credentialValue(params, 'endpoint')};\n case 'cloudflare-ai-gateway':\n return {\n env: {\n CLOUDFLARE_ACCOUNT_ID: credentialValue(params, 'account_id'),\n CLOUDFLARE_GATEWAY_ID: credentialValue(params, 'gateway_id'),\n },\n };\n case 'cloudflare-workers-ai':\n return {\n env: {\n CLOUDFLARE_ACCOUNT_ID: credentialValue(params, 'account_id'),\n },\n };\n default:\n return {};\n }\n}\n\nfunction credentialValue(\n params: Pick<ProbeModelProviderCredentialsParams, 'providerId' | 'credentials'>,\n key: string,\n): string {\n const value = params.credentials[key];\n if (value === undefined) throw new InvalidCredentialFieldsError(params.providerId);\n return value;\n}\n\nfunction rejectModelProviderErrorResult(result: AssistantMessage): void {\n if (result.stopReason !== 'error' && result.stopReason !== 'aborted') return;\n throw new Error(result.errorMessage ?? 'Provider validation failed.');\n}\n"],"names":["complete","getModels","parseEgressHostDenylist","redactSecrets","secretWireForms","config","modelProviderValidationCount","appendCustomProviderPath","InvalidAgentModelError","InvalidCredentialFieldsError","ModelProviderValidationError","ModelProviderValidationUnavailableError","UnsupportedModelProviderError","getModelProviderEntry","PROBE_MAX_TOKENS","MAX_SANITIZED_ERROR_LENGTH","CUSTOM_PROBE_PROMPT","runProviderProbe","params","probe","args","error","signal","aborted","add","model_provider","metricLabel","outcome","sanitizedMessage","sanitizeModelProviderError","secrets","providerId","probeModelProviderCredentials","entry","undefined","support_status","model","find","candidate","id","secretField","credential_fields","field","secret","apiKey","credentialValue","key","context","messages","role","content","timestamp","Date","now","options","maxTokens","maxRetries","timeoutMs","AGENT_PROVIDER_VALIDATION_TIMEOUT_MS","modelProviderCredentialOptions","result","rejectModelProviderErrorResult","probeCustomModelProviderCredentials","response","fetch","customProbeUrl","method","headers","customProbeHeaders","body","JSON","stringify","customProbeBody","redirect","timeoutSignal","ok","Error","status","cancel","message","length","secretForms","flatMap","redacted","slice","egressPolicy","allowPrivateNetworks","AGENT_CUSTOM_PROVIDER_ALLOW_PRIVATE_NETWORKS","hostDenylist","AGENT_CUSTOM_PROVIDER_HOST_DENYLIST","api","baseUrl","toString","encodeURIComponent","assertNever","Headers","set","max_tokens","contents","parts","text","generationConfig","maxOutputTokens","stream","input","max_output_tokens","store","value","AbortSignal","timeout","any","azureBaseUrl","env","CLOUDFLARE_ACCOUNT_ID","CLOUDFLARE_GATEWAY_ID","credentials","stopReason","errorMessage"],"mappings":"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"}