@apifuse/provider-sdk 2.2.0-beta.48 → 2.2.0-beta.49

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.
Files changed (108) hide show
  1. package/AUTHORING.md +91 -36
  2. package/CHANGELOG.md +4 -0
  3. package/README.md +11 -9
  4. package/SUBMISSION.md +1 -1
  5. package/bin/apifuse-dev.ts +24 -13
  6. package/bin/apifuse-migrate-operation-declaration.ts +55 -0
  7. package/bin/apifuse-pack-smoke.ts +1 -1
  8. package/bin/apifuse-pack-types.ts +2 -1
  9. package/bin/apifuse-record.ts +30 -16
  10. package/bin/apifuse-submit-check.ts +20 -35
  11. package/dist/cli/commands.d.ts +1 -1
  12. package/dist/cli/commands.js +11 -0
  13. package/dist/cli/migrate-operation-declaration.d.ts +59 -0
  14. package/dist/cli/migrate-operation-declaration.js +1178 -0
  15. package/dist/cli/templates/provider/README.md.tpl +3 -3
  16. package/dist/cli/templates/provider/operations/ping.ts.tpl +2 -0
  17. package/dist/config/loader.d.ts +2 -0
  18. package/dist/config/loader.js +18 -7
  19. package/dist/contract-types.d.ts +11 -5
  20. package/dist/contract.js +21 -10
  21. package/dist/define.d.ts +25 -22
  22. package/dist/define.js +49 -75
  23. package/dist/dev.d.ts +3 -0
  24. package/dist/dev.js +1 -1
  25. package/dist/engine.d.ts +78 -0
  26. package/dist/engine.js +133 -0
  27. package/dist/index.d.ts +3 -2
  28. package/dist/index.js +2 -1
  29. package/dist/lint.d.ts +7 -15
  30. package/dist/lint.js +45 -70
  31. package/dist/provider.d.ts +3 -1
  32. package/dist/provider.js +1 -0
  33. package/dist/runtime/chrome149-header-order.d.ts +58 -0
  34. package/dist/runtime/chrome149-header-order.js +289 -0
  35. package/dist/runtime/env.js +12 -0
  36. package/dist/runtime/executor.d.ts +2 -1
  37. package/dist/runtime/executor.js +3 -36
  38. package/dist/runtime/insights.js +2 -2
  39. package/dist/runtime/otlp.d.ts +71 -2
  40. package/dist/runtime/otlp.js +397 -16
  41. package/dist/runtime/resolver-vendors/capsolver.js +3 -3
  42. package/dist/runtime/resolver.js +3 -3
  43. package/dist/runtime/stealth.d.ts +13 -4
  44. package/dist/runtime/stealth.js +362 -85
  45. package/dist/runtime/trace-config.js +2 -1
  46. package/dist/runtime/trace.d.ts +5 -0
  47. package/dist/runtime/trace.js +43 -10
  48. package/dist/server/self-test.d.ts +1 -3
  49. package/dist/server/self-test.js +2 -12
  50. package/dist/server/serve-implementation.d.ts +6 -1
  51. package/dist/server/serve-implementation.js +55 -40
  52. package/dist/server/trace-output.d.ts +3 -1
  53. package/dist/server/trace-output.js +61 -2
  54. package/dist/stealth/profiles.d.ts +9 -8
  55. package/dist/stealth/profiles.js +123 -286
  56. package/dist/types.d.ts +111 -108
  57. package/package.json +2 -1
  58. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +6 -0
  59. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +3 -0
  60. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +10 -0
  61. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +8 -0
  62. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +5 -0
  63. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +16 -0
  64. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +3 -0
  65. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +31 -0
  66. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +11 -0
  67. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +11 -0
  68. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +11 -0
  69. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +21 -0
  70. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +11 -0
  71. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +14 -0
  72. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +7 -0
  73. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +6 -0
  74. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +7 -0
  75. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +6 -0
  76. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +7 -0
  77. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +7 -0
  78. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +15 -0
  79. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +4 -0
  80. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +12 -0
  81. package/src/cli/commands.ts +13 -0
  82. package/src/cli/migrate-operation-declaration.ts +1654 -0
  83. package/src/cli/templates/provider/README.md.tpl +3 -3
  84. package/src/cli/templates/provider/operations/ping.ts.tpl +2 -0
  85. package/src/config/loader.ts +31 -6
  86. package/src/contract-types.ts +11 -5
  87. package/src/contract.ts +21 -10
  88. package/src/define.ts +107 -119
  89. package/src/dev.ts +4 -1
  90. package/src/engine.ts +279 -0
  91. package/src/index.ts +13 -5
  92. package/src/lint.ts +58 -92
  93. package/src/provider.ts +25 -3
  94. package/src/runtime/chrome149-header-order.ts +330 -0
  95. package/src/runtime/env.ts +13 -0
  96. package/src/runtime/executor.ts +7 -40
  97. package/src/runtime/insights.ts +2 -2
  98. package/src/runtime/otlp.ts +467 -21
  99. package/src/runtime/resolver-vendors/capsolver.ts +4 -3
  100. package/src/runtime/resolver.ts +3 -3
  101. package/src/runtime/stealth.ts +435 -103
  102. package/src/runtime/trace-config.ts +3 -2
  103. package/src/runtime/trace.ts +57 -17
  104. package/src/server/self-test.ts +2 -9
  105. package/src/server/serve-implementation.ts +89 -72
  106. package/src/server/trace-output.ts +99 -2
  107. package/src/stealth/profiles.ts +169 -327
  108. package/src/types.ts +109 -137
@@ -113,9 +113,9 @@ Structured errors return an `error` object with `code`, `message`,
113
113
  `connection.secrets`, and read them with `ctx.credential`.
114
114
  - Auth flow: call `/auth/start`, then `/auth/continue` with the same `flowId`;
115
115
  carry returned `contextPatch` values into the next request's `context`.
116
- - Stealth/browser runtime: keep access-sensitive operations on `ctx.stealth.fetch()` with an
117
- SDK stealth `profile`; the TypeScript stealth runtime uses `wreq-js` internally
118
- and supports Chrome, Firefox, and Safari profiles. Use `ctx.browser` only when
116
+ - Stealth/browser runtime: keep access-sensitive operations on `ctx.stealth.fetch()` with
117
+ structured SDK `stealth: { browser, os }` selection; the TypeScript stealth runtime uses
118
+ `wreq-js` internally and supports Chrome, Firefox, and Safari. Use `ctx.browser` only when
119
119
  the provider needs browser execution; TypeScript browser Providers use
120
120
  `browser.engine: "playwright-stealth"` (`nodriver` is Python-runtime only). Install local Chromium with
121
121
  `bunx playwright install chromium` or set `APIFUSE__CDP_POOL__URL`.
@@ -4,6 +4,8 @@ import type { ProviderContext } from "../index";
4
4
  import { pingInputSchema, pingOutputSchema } from "../schemas/ping";
5
5
 
6
6
  export const pingOperation = defineOperation<ProviderContext>()({
7
+ connectionMode: "none",
8
+ riskClass: "read",
7
9
  descriptionKey: "operations.ping.description",
8
10
  input: pingInputSchema,
9
11
  output: pingOutputSchema,
@@ -58,6 +58,8 @@ export type ProxyResolutionOptions = {
58
58
  */
59
59
  proxyRefreshEpoch?: number;
60
60
  telemetry?: ProxyTelemetrySink;
61
+ /** Engine-owned credential snapshot. Provider code must never populate this field. */
62
+ engineCredentials?: Readonly<Record<string, string>>;
61
63
  };
62
64
  export type ProxyCacheStatus = "memory_hit" | "redis_hit" | "allocator" | "soft_stale_refresh" | "lock_wait" | "redis_error" | "redis_corrupt" | "disabled";
63
65
  export type SmartproxyAllocatorBodyClass = "network_error" | "http_error" | "empty" | "json_without_proxies" | "text_without_proxies" | "usable_proxy_endpoints";
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { existsSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
4
  import path from "node:path";
5
- import { NODEMAVEN_DEFAULT_PROTOCOL, NODEMAVEN_FILTER_ENV, NODEMAVEN_MAX_POOL_SIZE, NODEMAVEN_PASSWORD_ENV, NODEMAVEN_USERNAME_ENV, hasNodemavenCredentials, nodemavenPoolSize, synthesizeNodemavenProxy, } from "../runtime/proxy-nodemaven.js";
5
+ import { NODEMAVEN_DEFAULT_PROTOCOL, NODEMAVEN_FILTER_ENV, NODEMAVEN_MAX_POOL_SIZE, NODEMAVEN_PASSWORD_ENV, NODEMAVEN_USERNAME_ENV, nodemavenPoolSize, synthesizeNodemavenProxy, } from "../runtime/proxy-nodemaven.js";
6
6
  // "smartproxy" here is api.smartproxy.org — a residential proxy with an IP
7
7
  // extraction API (app_key → raw ip:port pool). It is NOT the company formerly
8
8
  // named Smartproxy (smartproxy.com), which rebranded to Decodo in 2025 and is
@@ -287,7 +287,7 @@ export async function resolveProxyConfigAsync(options = {}) {
287
287
  const nextVendor = chain[vendorIndex + 1];
288
288
  const poolIndex = vendorIndex === startVendorIndex ? startPoolIndex : 0;
289
289
  const protocol = options.protocol ?? VENDOR_DEFAULT_PROTOCOL[vendor];
290
- if (!vendorHasCredentials(vendor)) {
290
+ if (!vendorHasCredentials(vendor, options.engineCredentials)) {
291
291
  options.telemetry?.recordProxyVendorFailover?.({
292
292
  vendor,
293
293
  nextVendor,
@@ -313,6 +313,7 @@ export async function resolveProxyConfigAsync(options = {}) {
313
313
  protocol,
314
314
  poolIndex,
315
315
  refreshEpoch,
316
+ credentials: options.engineCredentials,
316
317
  });
317
318
  }
318
319
  catch (error) {
@@ -497,10 +498,18 @@ function envDefaultProvider() {
497
498
  const raw = process.env[DEFAULT_PROXY_PROVIDER_ENV]?.trim().toLowerCase();
498
499
  return raw ?? undefined;
499
500
  }
500
- function vendorHasCredentials(vendor) {
501
- if (vendor === "nodemaven")
502
- return hasNodemavenCredentials();
503
- return Boolean(process.env[SMARTPROXY_APP_KEY_ENV]?.trim());
501
+ function vendorHasCredentials(vendor, credentials) {
502
+ if (vendor === "nodemaven") {
503
+ return Boolean((credentials === undefined
504
+ ? process.env[NODEMAVEN_USERNAME_ENV]
505
+ : credentials[NODEMAVEN_USERNAME_ENV])?.trim() &&
506
+ (credentials === undefined
507
+ ? process.env[NODEMAVEN_PASSWORD_ENV]
508
+ : credentials[NODEMAVEN_PASSWORD_ENV])?.trim());
509
+ }
510
+ return Boolean((credentials === undefined
511
+ ? process.env[SMARTPROXY_APP_KEY_ENV]
512
+ : credentials[SMARTPROXY_APP_KEY_ENV])?.trim());
504
513
  }
505
514
  function missingCredentialEnv(vendor) {
506
515
  return vendor === "nodemaven" ? "APIFUSE__PROXY__NODEMAVEN_USERNAME" : SMARTPROXY_APP_KEY_ENV;
@@ -1149,7 +1158,9 @@ function markSmartproxyCacheInvalidated(options = {}) {
1149
1158
  return undefined;
1150
1159
  }
1151
1160
  const lifetimeMinutes = resolveSmartproxyLifetime(policy);
1152
- const appKey = process.env[SMARTPROXY_APP_KEY_ENV]?.trim();
1161
+ const appKey = (options.engineCredentials === undefined
1162
+ ? process.env[SMARTPROXY_APP_KEY_ENV]
1163
+ : options.engineCredentials[SMARTPROXY_APP_KEY_ENV])?.trim();
1153
1164
  if (!appKey)
1154
1165
  return undefined;
1155
1166
  const cacheKey = buildSmartproxyCacheKey(policy, appKey, options.affinityKey, lifetimeMinutes, options.protocol ?? VENDOR_DEFAULT_PROTOCOL.smartproxy);
@@ -27,17 +27,23 @@ export interface ProviderContractSnapshot {
27
27
  }
28
28
  export interface ProviderContractOperation {
29
29
  readonly id: string;
30
+ readonly connectionMode?: JsonValue;
31
+ readonly connectionExternalRefParam?: JsonValue;
32
+ readonly riskClass: JsonValue;
33
+ readonly approval?: JsonValue;
34
+ readonly timeoutMs?: JsonValue;
35
+ readonly titleKey?: JsonValue;
30
36
  readonly descriptionKey?: JsonValue;
31
- readonly docs?: JsonValue;
37
+ readonly summaryKey?: JsonValue;
38
+ readonly markdownKey?: JsonValue;
32
39
  readonly whenToUseKeys?: JsonValue;
33
40
  readonly whenNotToUseKeys?: JsonValue;
34
- readonly derivations?: JsonValue;
35
- readonly inputExamples?: JsonValue;
36
- readonly annotations?: JsonValue;
41
+ readonly normalizationNotesKeys?: JsonValue;
42
+ readonly errorCodes?: JsonValue;
43
+ readonly examples?: JsonValue;
37
44
  readonly contract?: JsonValue;
38
45
  readonly tags?: JsonValue;
39
46
  readonly relatedOperations?: JsonValue;
40
- readonly toolRouter?: JsonValue;
41
47
  readonly observability?: JsonValue;
42
48
  readonly transport?: JsonValue;
43
49
  readonly inputSchema: JsonValue;
package/dist/contract.js CHANGED
@@ -48,17 +48,22 @@ export function digestProviderContract(snapshot) {
48
48
  return createHash("sha256").update(canonicalJson(snapshot)).digest("hex");
49
49
  }
50
50
  function extractOperation(operationId, operation) {
51
+ const connectionMode = toJsonValue(operation.connectionMode);
52
+ const connectionExternalRefParam = toJsonValue(operation.connectionExternalRefParam);
53
+ const approval = toJsonValue(operation.approval);
54
+ const timeoutMs = toJsonValue(operation.timeoutMs);
55
+ const titleKey = toJsonValue(operation.titleKey);
51
56
  const descriptionKey = toJsonValue(operation.descriptionKey);
52
- const docs = toJsonValue(operation.docs);
57
+ const summaryKey = toJsonValue(operation.summaryKey);
58
+ const markdownKey = toJsonValue(operation.markdownKey);
53
59
  const whenToUseKeys = toJsonValue(operation.whenToUseKeys);
54
60
  const whenNotToUseKeys = toJsonValue(operation.whenNotToUseKeys);
55
- const derivations = toJsonValue(operation.derivations);
56
- const inputExamples = toJsonValue(operation.inputExamples);
57
- const annotations = toJsonValue(operation.annotations);
61
+ const normalizationNotesKeys = toJsonValue(operation.normalizationNotesKeys);
62
+ const errorCodes = toJsonValue(operation.errorCodes);
63
+ const examples = toJsonValue(operation.examples);
58
64
  const contract = toJsonValue(operation.contract);
59
65
  const tags = toJsonValue(operation.tags);
60
66
  const relatedOperations = toJsonValue(operation.relatedOperations);
61
- const toolRouter = toJsonValue(operation.toolRouter);
62
67
  const observability = toJsonValue(operation.observability);
63
68
  const transport = extractTransport(operation.transport);
64
69
  const fixtures = toJsonValue(operation.fixtures);
@@ -70,17 +75,23 @@ function extractOperation(operationId, operation) {
70
75
  id: operationId,
71
76
  inputSchema: describeSchema(operation.input),
72
77
  outputSchema: describeSchema(operation.output),
78
+ ...(connectionMode === undefined ? {} : { connectionMode }),
79
+ ...(connectionExternalRefParam === undefined ? {} : { connectionExternalRefParam }),
80
+ riskClass: operation.riskClass,
81
+ ...(approval === undefined ? {} : { approval }),
82
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
83
+ ...(titleKey === undefined ? {} : { titleKey }),
73
84
  ...(descriptionKey === undefined ? {} : { descriptionKey }),
74
- ...(docs === undefined ? {} : { docs }),
85
+ ...(summaryKey === undefined ? {} : { summaryKey }),
86
+ ...(markdownKey === undefined ? {} : { markdownKey }),
75
87
  ...(whenToUseKeys === undefined ? {} : { whenToUseKeys }),
76
88
  ...(whenNotToUseKeys === undefined ? {} : { whenNotToUseKeys }),
77
- ...(derivations === undefined ? {} : { derivations }),
78
- ...(inputExamples === undefined ? {} : { inputExamples }),
79
- ...(annotations === undefined ? {} : { annotations }),
89
+ ...(normalizationNotesKeys === undefined ? {} : { normalizationNotesKeys }),
90
+ ...(errorCodes === undefined ? {} : { errorCodes }),
91
+ ...(examples === undefined ? {} : { examples }),
80
92
  ...(contract === undefined ? {} : { contract }),
81
93
  ...(tags === undefined ? {} : { tags }),
82
94
  ...(relatedOperations === undefined ? {} : { relatedOperations }),
83
- ...(toolRouter === undefined ? {} : { toolRouter }),
84
95
  ...(observability === undefined ? {} : { observability }),
85
96
  ...(transport === undefined ? {} : { transport }),
86
97
  ...(fixtures === undefined ? {} : { fixtures }),
package/dist/define.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, NativeProviderConfig, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport, ProviderAccessConfig, ProviderContext, ProviderContextFor, ProviderDefinition, ProviderDeploymentOverrides, ProviderHealthMonitorConfig, ProviderOcrConfig, ProviderProxyConfig, ProviderPublicProfile, ProviderResolverConfig, ProviderReviewed, ProviderSecretDeclaration, ProviderStreamEvent, ProviderSttConfig, SchemaLike, SmsOtpMatcherDefinition, StealthPlatform } from "./types.js";
1
+ import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, NativeProviderConfig, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport, ProviderAccessConfig, ProviderContext, ProviderDefinition, ProviderDeploymentOverrides, ProviderHealthMonitorConfig, ProviderOcrConfig, ProviderProxyConfig, ProviderPublicProfile, ProviderResolverConfig, ProviderReviewed, ProviderRuntimeTarget, ProviderSecretDeclaration, ProviderStreamEvent, ProviderSttConfig, SchemaLike, SmsOtpMatcherDefinition, StealthProfileSelection } from "./types.js";
2
2
  type ProviderImplementationSourceAccess = "official_api" | "private_api" | "browser_flow" | "hybrid";
3
3
  type ProviderImplementationCredentialStrategy = "apifuse_managed" | "workspace_secret" | "user_oauth" | "user_session" | "none";
4
4
  interface ProviderImplementationProfile {
@@ -46,6 +46,8 @@ export interface ProviderDeclaration {
46
46
  id: string;
47
47
  version: string;
48
48
  runtime: "standard" | "shared" | "browser";
49
+ /** Provider business-logic target. Omit only while migrating a legacy provider. */
50
+ runtimeTarget?: ProviderRuntimeTarget;
49
51
  /**
50
52
  * Optional deployment overrides, passed through verbatim onto the returned
51
53
  * provider definition. The SDK types this field but does not deep-validate
@@ -53,14 +55,13 @@ export interface ProviderDeclaration {
53
55
  * resolves omitted fields against the runtime deployment profiles.
54
56
  */
55
57
  deployment?: ProviderDeploymentOverrides;
56
- /** Declares that provider operations use the SDK HTTP client. */
57
- http?: true;
58
+ /** Declares the HTTP capability binding. A bare object states use without configuration. */
59
+ http?: Record<string, never> | true;
60
+ /** Declares upstream host policy; this does not add a `ctx.allowedHosts` member. */
58
61
  allowedHosts?: string[];
59
62
  native?: NativeProviderConfig;
60
- stealth?: {
61
- profile: string;
62
- platform: StealthPlatform;
63
- };
63
+ stealth?: StealthProfileSelection;
64
+ /** Declares proxy policy; this is provider intent and does not add a `ctx.proxy` member. */
64
65
  proxy?: ProviderProxyConfig;
65
66
  ocr?: ProviderOcrConfig;
66
67
  stt?: ProviderSttConfig;
@@ -69,21 +70,23 @@ export interface ProviderDeclaration {
69
70
  engine: BrowserEngine;
70
71
  };
71
72
  auth?: AuthConfig;
72
- /** Declares that provider operations issue and consume SDK choice tokens. */
73
- choice?: true;
73
+ /** Declares the choice capability binding. A bare object states use without configuration. */
74
+ choice?: Record<string, never> | true;
74
75
  reviewed?: ProviderReviewed;
75
76
  access?: ProviderAccessConfig;
77
+ /** Declares secret requirements; this does not add a `ctx.secrets` member. */
76
78
  secrets?: ProviderSecretDeclaration[];
77
- /** Declares that provider operations read SDK-managed environment values. */
78
- env?: true;
79
+ /** Declares the environment capability binding. A bare object states use without configuration. */
80
+ env?: Record<string, never> | true;
79
81
  credential?: CredentialDeclaration;
82
+ /** Declares provider context metadata; this does not add a `ctx.context` member. */
80
83
  context?: ContextDeclaration;
81
- /** Declares that provider operations use SDK-managed persistent state. */
82
- state?: true;
83
- /** Declares that provider operations use the SDK provider cache. */
84
- cache?: true;
85
- /** Declares that provider operations access runtime-resolvable files. */
86
- files?: true;
84
+ /** Declares the state capability binding. A bare object states use without configuration. */
85
+ state?: Record<string, never> | true;
86
+ /** Declares the cache capability binding. A bare object states use without configuration. */
87
+ cache?: Record<string, never> | true;
88
+ /** Declares the files capability binding. A bare object states use without configuration. */
89
+ files?: Record<string, never> | true;
87
90
  meta: {
88
91
  displayName: string;
89
92
  displayNameKey?: string;
@@ -127,17 +130,17 @@ export declare function delayed(maxDelay: string): HealthScheduleRandomization;
127
130
  export declare function defineSmsOtpMatcher(config: Omit<SmsOtpMatcherDefinition, "extractOtp">): SmsOtpMatcherDefinition;
128
131
  export declare function defineHealthJourney(config: HealthJourneyDefinition): HealthJourneyDefinition;
129
132
  /** The second authoring phase for a declaration established by defineProvider. */
130
- export type ProviderBuilder<TDeclaration extends ProviderDeclaration> = <TOperations extends Record<string, ProviderOperation>>(implementation: {
131
- operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
133
+ export type ProviderBuilder<TConfig extends ProviderDeclaration> = <TOperations extends Record<string, ProviderOperation>>(implementation: {
134
+ operations: OperationMapConfig<TOperations, ProviderContext<TConfig>>;
132
135
  }) => Omit<ProviderDefinition, "operations"> & {
133
- operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
136
+ operations: OperationMapConfig<TOperations, ProviderContext<TConfig>>;
134
137
  };
135
138
  /** Extract the declaration-derived operation context from a provider builder. */
136
- export type ProviderContextOf<TBuilder> = TBuilder extends ProviderBuilder<infer TDeclaration> ? ProviderContextFor<TDeclaration> : never;
139
+ export type ProviderContextOf<TBuilder> = TBuilder extends ProviderBuilder<infer TConfig> ? ProviderContext<TConfig> : never;
137
140
  /** Annotate an operation while preserving the declaration-derived context. */
138
141
  export type OperationDefinitionFor<TBuilder, TInput extends SchemaLike = SchemaLike, TOutput extends SchemaLike = SchemaLike> = OperationDefinition<TInput, TOutput, ProviderContextOf<TBuilder>>;
139
142
  /** Annotate a built provider while preserving the declaration-derived context. */
140
143
  export type ProviderDefinitionFor<TBuilder> = ProviderDefinition<ProviderContextOf<TBuilder>>;
141
144
  /** Establish a provider declaration before its operations are contextually typed. */
142
- export declare function defineProvider<const TDeclaration extends ProviderDeclaration>(declaration: TDeclaration & Record<Exclude<keyof TDeclaration, keyof ProviderDeclaration>, never> & AuthStartNoInputGuard<TDeclaration>): ProviderBuilder<TDeclaration>;
145
+ export declare function defineProvider<const TConfig extends ProviderDeclaration>(declaration: TConfig & Record<Exclude<keyof TConfig, keyof ProviderDeclaration>, never> & AuthStartNoInputGuard<TConfig>): ProviderBuilder<TConfig>;
143
146
  export {};
package/dist/define.js CHANGED
@@ -2,6 +2,7 @@ import ms from "ms";
2
2
  import { validateFailClosedOperationDeclaration, validateFailClosedProviderDeclaration, } from "./declaration-validation.js";
3
3
  import { SDK_RUNTIME_OWNED_ERROR_CODES } from "./error-resolution.js";
4
4
  import { ProviderError, ValidationError } from "./errors.js";
5
+ import { isEngineOwnedProxyCredentialName, isEngineOwnedTelemetryEnvName } from "./engine.js";
5
6
  import { HealthScenarioSchema } from "./health-scenario.js";
6
7
  import { NativeEgressPolicyValidationError, validateNativeProviderConfig, } from "./native-egress-policy.js";
7
8
  import { safeParseSchemaSync } from "./schema.js";
@@ -10,6 +11,7 @@ import { HEALTH_CHECK_DEGRADED_THRESHOLD_MS_MAX, HEALTH_CHECK_DEGRADED_THRESHOLD
10
11
  const CONNECTOR_ID_REGEX = /^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)*$/;
11
12
  const OPERATION_ID_REGEX = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/;
12
13
  const VALID_RUNTIMES = ["standard", "shared", "browser"];
14
+ const VALID_RUNTIME_TARGETS = ["vanilla", "engine"];
13
15
  const VALID_AUTH_MODES = [
14
16
  "none",
15
17
  "platform-managed",
@@ -71,24 +73,10 @@ export const VALID_PROVIDER_CHALLENGE_KINDS = exhaustiveLiteralArray()([
71
73
  "akamai_sec_cpt",
72
74
  "akamai_sensor",
73
75
  ]);
74
- const SMARTPROXY_APP_KEY_SECRET = "APIFUSE__PROXY__SMARTPROXY_APP_KEY";
75
- const NODEMAVEN_USERNAME_SECRET = "APIFUSE__PROXY__NODEMAVEN_USERNAME";
76
- const NODEMAVEN_PASSWORD_SECRET = "APIFUSE__PROXY__NODEMAVEN_PASSWORD";
77
- // Per-vendor provider-declared credential secrets. A required-mode chain must
78
- // declare every secret of every credentialed vendor it names, so a missing
79
- // credential fails at build/validation time rather than during a live outage: a
80
- // declared-but-uncredentialed fallback leg is a silently dead SPOF, which is
81
- // exactly the failure class the multi-vendor chain exists to remove. Vendors
82
- // absent from this map (the deprecated `custom`/`decodo` values have no managed
83
- // adapter) impose no declaration requirement.
84
- const VENDOR_REQUIRED_SECRETS = {
85
- smartproxy: [SMARTPROXY_APP_KEY_SECRET],
86
- nodemaven: [NODEMAVEN_USERNAME_SECRET, NODEMAVEN_PASSWORD_SECRET],
87
- };
88
76
  const RESERVED_OPERATION_IDS = new Set(["auth", "health"]);
89
- const MCP_TOOL_NAME_REGEX = /^[A-Za-z][A-Za-z0-9_]{0,127}$/;
90
77
  const VALID_OPERATION_RISK_CLASSES = ["read", "write", "destructive", "external-send"];
91
78
  const VALID_OPERATION_APPROVAL_POLICIES = ["never", "risk-based", "always"];
79
+ const VALID_OPERATION_CONNECTION_MODES = ["none", "optional", "required"];
92
80
  const VALID_OPERATION_TRANSPORT_KINDS = ["json", "sse", "http-stream", "websocket"];
93
81
  const SSE_EVENT_NAME_REGEX = /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/;
94
82
  const WEBSOCKET_SUBPROTOCOL_REGEX = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
@@ -562,11 +550,21 @@ function validateProviderDeclarationShape(config) {
562
550
  assertRequiredField(config, "meta", String(config.id));
563
551
  if (typeof config.runtime === "string")
564
552
  assertLiteralField(config.runtime, "runtime", VALID_RUNTIMES, String(config.id));
553
+ if (config.runtimeTarget !== undefined && typeof config.runtimeTarget !== "string") {
554
+ throw new ValidationError(`Provider "${String(config.id)}" has invalid runtimeTarget: expected "vanilla" or "engine"`);
555
+ }
556
+ if (typeof config.runtimeTarget === "string")
557
+ assertLiteralField(config.runtimeTarget, "runtimeTarget", VALID_RUNTIME_TARGETS, String(config.id));
565
558
  if (config.native !== undefined && config.runtime === "browser") {
566
559
  throw new ValidationError(`Provider "${String(config.id)}" cannot declare capability "native" with runtime "browser"`, {
567
560
  fix: 'Use runtime: "standard" or runtime: "shared", or remove the native declaration.',
568
561
  });
569
562
  }
563
+ if (config.native !== undefined && config.runtimeTarget === "vanilla") {
564
+ throw new ValidationError(`Provider "${String(config.id)}" cannot declare capability "native" with runtime target "vanilla"; native requires an engine-resident runtime`, {
565
+ fix: 'Set runtimeTarget: "engine", or remove the native declaration.',
566
+ });
567
+ }
570
568
  const auth = config.auth;
571
569
  if (auth && typeof auth === "object" && "mode" in auth && typeof auth.mode === "string")
572
570
  assertLiteralField(auth.mode, "auth.mode", VALID_AUTH_MODES, String(config.id));
@@ -634,6 +632,18 @@ function validateProviderImplementationShape(config) {
634
632
  assertRequiredField(configRecord, "operations", String(config.id));
635
633
  }
636
634
  function validateProviderProxy(config) {
635
+ for (const secret of config.secrets ?? []) {
636
+ if (isEngineOwnedTelemetryEnvName(secret.name)) {
637
+ throw new ValidationError(`Provider "${config.id}" cannot declare engine-owned telemetry variable "${secret.name}"`, {
638
+ fix: `Remove "${secret.name}" from provider secrets; trace export is configured only on the provider engine.`,
639
+ });
640
+ }
641
+ if (!isEngineOwnedProxyCredentialName(secret.name))
642
+ continue;
643
+ throw new ValidationError(`Provider "${config.id}" cannot declare engine-owned proxy credential "${secret.name}"`, {
644
+ fix: `Remove "${secret.name}" from provider secrets; configure it only on the provider engine.`,
645
+ });
646
+ }
637
647
  const proxy = config.proxy;
638
648
  if (proxy === undefined || typeof proxy === "boolean") {
639
649
  return;
@@ -712,39 +722,11 @@ function validateProviderProxy(config) {
712
722
  });
713
723
  }
714
724
  }
715
- // Every credentialed vendor in a required-mode chain must declare its
716
- // provider secret(s) so a missing credential fails at build/validation time,
717
- // not during a live outage. This covers the fallback legs too (not just the
718
- // first vendor): a declared-but-uncredentialed nodemaven fallback would leave
719
- // the chain silently down to a single vendor, reintroducing the SPOF the chain
720
- // removes.
721
725
  const vendorChain = proxy.providers && proxy.providers.length > 0
722
726
  ? proxy.providers
723
727
  : proxy.provider
724
728
  ? [proxy.provider]
725
729
  : [];
726
- if (proxy.mode === "required") {
727
- for (const vendor of vendorChain) {
728
- const requiredSecrets = VENDOR_REQUIRED_SECRETS[vendor];
729
- if (!requiredSecrets)
730
- continue;
731
- for (const secretName of requiredSecrets) {
732
- // Match the canonical runtime gate (assertRequiredSecretsPresent /
733
- // listMissingRequiredSecrets), which enforces only `required === true`
734
- // declarations. A declaration that omits `required` (defaulting to
735
- // optional) is skipped at runtime, so accepting it here would pass
736
- // validation while leaving the credential unenforced until proxy
737
- // resolution during a live request — the fail-open gap this check exists
738
- // to close.
739
- const declared = config.secrets?.some((secret) => secret.name === secretName && secret.required === true);
740
- if (!declared) {
741
- throw new ValidationError(`Provider "${config.id}" requires ${vendor} egress but does not declare ${secretName}.`, {
742
- fix: `Add secrets: [{ name: "${secretName}", required: true }] to the provider (every vendor in a required proxy chain must declare its credential secrets).`,
743
- });
744
- }
745
- }
746
- }
747
- }
748
730
  // `decodo`/`custom` are deprecated vendor values (string-union members, so the
749
731
  // @deprecated symbol gate can't catch them — warn at validation time instead).
750
732
  const deprecatedVendors = vendorChain.filter((vendor) => vendor === "decodo" || vendor === "custom");
@@ -839,31 +821,19 @@ function assertNonEmptyString(value, field, providerId, operationName) {
839
821
  throw new ValidationError(`Provider "${providerId}" operation "${operationName}" has invalid ${field}: must be a non-empty string.`, { fix: `Set ${field} to a non-empty customer-facing value.` });
840
822
  }
841
823
  }
842
- function validateToolRouterMetadata(providerId, operations) {
824
+ function validateOperationMetadata(providerId, operations) {
843
825
  for (const [operationName, operation] of Object.entries(operations)) {
844
- const toolRouter = operation.toolRouter;
845
- if (toolRouter === undefined)
846
- continue;
847
- if (!toolRouter || typeof toolRouter !== "object") {
848
- throw new ValidationError(`Provider "${providerId}" operation "${operationName}" has invalid operations.${operationName}.toolRouter: must be an object.`, {
849
- fix: `Remove operations.${operationName}.toolRouter or provide MCP-safe metadata.`,
850
- });
851
- }
852
- if (toolRouter.name !== undefined && !MCP_TOOL_NAME_REGEX.test(toolRouter.name)) {
853
- throw new ValidationError(`Provider "${providerId}" operation "${operationName}" has invalid operations.${operationName}.toolRouter.name: expected an MCP-safe name.`, {
854
- fix: `Use letters, numbers, and underscores only, starting with a letter, for example "${providerId.replace(/[^A-Za-z0-9]+/g, "_")}__${operationName.replace(/[^A-Za-z0-9]+/g, "_")}".`,
855
- });
826
+ assertLiteralField(operation.riskClass, `operations.${operationName}.riskClass`, VALID_OPERATION_RISK_CLASSES, providerId);
827
+ if (operation.approval !== undefined) {
828
+ assertLiteralField(operation.approval, `operations.${operationName}.approval`, VALID_OPERATION_APPROVAL_POLICIES, providerId);
856
829
  }
857
- if (toolRouter.riskClass !== undefined) {
858
- assertLiteralField(toolRouter.riskClass, `operations.${operationName}.toolRouter.riskClass`, VALID_OPERATION_RISK_CLASSES, providerId);
830
+ if (operation.connectionMode !== undefined) {
831
+ assertLiteralField(operation.connectionMode, `operations.${operationName}.connectionMode`, VALID_OPERATION_CONNECTION_MODES, providerId);
859
832
  }
860
- if (toolRouter.approval !== undefined) {
861
- assertLiteralField(toolRouter.approval, `operations.${operationName}.toolRouter.approval`, VALID_OPERATION_APPROVAL_POLICIES, providerId);
862
- }
863
- if (toolRouter.connectionExternalRefParam !== undefined &&
864
- (typeof toolRouter.connectionExternalRefParam !== "string" ||
865
- toolRouter.connectionExternalRefParam.trim().length === 0)) {
866
- throw new ValidationError(`Provider "${providerId}" operation "${operationName}" has invalid operations.${operationName}.toolRouter.connectionExternalRefParam: must be a non-empty string.`, {
833
+ if (operation.connectionExternalRefParam !== undefined &&
834
+ (typeof operation.connectionExternalRefParam !== "string" ||
835
+ operation.connectionExternalRefParam.trim().length === 0)) {
836
+ throw new ValidationError(`Provider "${providerId}" operation "${operationName}" has invalid operations.${operationName}.connectionExternalRefParam: must be a non-empty string.`, {
867
837
  fix: `Use "externalRef" unless the operation has a documented public alias.`,
868
838
  });
869
839
  }
@@ -899,15 +869,12 @@ function validateOperationContracts(providerId, operations) {
899
869
  }
900
870
  }
901
871
  }
902
- function validateOperationAnnotations(providerId, operations) {
872
+ function validateOperationTimeouts(providerId, operations) {
903
873
  for (const [operationName, operation] of Object.entries(operations)) {
904
- const annotations = operation.annotations;
905
- if (!annotations)
906
- continue;
907
- const timeoutMs = annotations.timeoutMs;
874
+ const timeoutMs = operation.timeoutMs;
908
875
  if (timeoutMs === undefined)
909
876
  continue;
910
- const field = `operations.${operationName}.annotations.timeoutMs`;
877
+ const field = `operations.${operationName}.timeoutMs`;
911
878
  if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs))
912
879
  throw new ValidationError(`Provider "${providerId}" has invalid ${field}: must be an integer number of milliseconds.`, {
913
880
  fix: `Set ${field} to an integer in [${OPERATION_TIMEOUT_MS_MIN}, ${OPERATION_TIMEOUT_MS_MAX}] (milliseconds).`,
@@ -957,10 +924,10 @@ function validateOperationObservability(providerId, operations) {
957
924
  }
958
925
  function validateOperationErrorCodes(providerId, operations) {
959
926
  for (const [operationName, operation] of Object.entries(operations)) {
960
- for (const [index, errorCode] of (operation.docs?.errorCodes ?? []).entries()) {
927
+ for (const [index, errorCode] of (operation.errorCodes ?? []).entries()) {
961
928
  if (errorCode.status !== undefined &&
962
929
  !VALID_OPERATION_ERROR_STATUSES.some((status) => status === errorCode.status)) {
963
- const field = `operations.${operationName}.docs.errorCodes[${index}].status`;
930
+ const field = `operations.${operationName}.errorCodes[${index}].status`;
964
931
  throw new ValidationError(`Provider "${providerId}" has invalid ${field}: ${String(errorCode.status)} is not an emittable provider error status.`, {
965
932
  fix: `Set ${field} to one of ${VALID_OPERATION_ERROR_STATUSES.join(", ")}, or omit it.`,
966
933
  });
@@ -2022,12 +1989,12 @@ function finalizeProvider(config) {
2022
1989
  fix: "Add at least one operation to the operations object",
2023
1990
  });
2024
1991
  validateOperationIds(config.id, config.operations);
2025
- validateOperationAnnotations(config.id, config.operations);
1992
+ validateOperationTimeouts(config.id, config.operations);
2026
1993
  validateOperationObservability(config.id, config.operations);
2027
1994
  validateOperationErrorCodes(config.id, config.operations);
2028
1995
  validateOperationTransports(config.id, config.operations);
2029
1996
  validateOperationContracts(config.id, config.operations);
2030
- validateToolRouterMetadata(config.id, config.operations);
1997
+ validateOperationMetadata(config.id, config.operations);
2031
1998
  const journeyCoveredOperations = validateHealthJourneys(config.id, config.operations, config.healthJourneys);
2032
1999
  validateOperationHealthChecks(config.id, config.operations, journeyCoveredOperations);
2033
2000
  validateOperationFixtures(config.id, operations);
@@ -2035,9 +2002,11 @@ function finalizeProvider(config) {
2035
2002
  id: config.id,
2036
2003
  version: config.version,
2037
2004
  runtime: config.runtime,
2005
+ runtimeTarget: config.runtimeTarget,
2038
2006
  // Verbatim passthrough: deployment validation and profile resolution
2039
2007
  // are owned by the APIFuse registry builder, not the SDK.
2040
2008
  deployment: config.deployment,
2009
+ http: config.http,
2041
2010
  allowedHosts: config.allowedHosts,
2042
2011
  native: config.native,
2043
2012
  stealth: config.stealth,
@@ -2047,11 +2016,16 @@ function finalizeProvider(config) {
2047
2016
  resolver: config.resolver,
2048
2017
  browser: config.browser,
2049
2018
  auth: config.auth,
2019
+ choice: config.choice,
2050
2020
  reviewed: config.reviewed,
2051
2021
  access: config.access,
2052
2022
  secrets: config.secrets,
2023
+ env: config.env,
2053
2024
  credential: config.credential,
2054
2025
  context: config.context,
2026
+ state: config.state,
2027
+ cache: config.cache,
2028
+ files: config.files,
2055
2029
  meta: config.meta,
2056
2030
  operations,
2057
2031
  // Transitional healthMonitor → healthProbe alias: mirror whichever field
package/dist/dev.d.ts CHANGED
@@ -1,7 +1,10 @@
1
+ import type { ProviderEngine } from "./engine.js";
1
2
  import type { ProviderDefinition } from "./types.js";
2
3
  export interface DevServerOptions {
3
4
  port?: number;
4
5
  sessionDbPath?: string;
6
+ /** Override the default in-process engine attachment. */
7
+ engine?: ProviderEngine;
5
8
  }
6
9
  export declare function createDevServer(provider: ProviderDefinition, options?: DevServerOptions): {
7
10
  start: () => void;
package/dist/dev.js CHANGED
@@ -3,7 +3,7 @@ export function createDevServer(provider, options) {
3
3
  const port = options?.port ?? 3900;
4
4
  return {
5
5
  start: () => {
6
- void serve(provider, { port });
6
+ void serve(provider, { port, engine: options?.engine });
7
7
  console.log(`[apifuse dev] ${provider.id}@${provider.version} running at http://localhost:${port}`);
8
8
  console.log(`[apifuse dev] Operations: ${Object.keys(provider.operations).join(", ")}`);
9
9
  console.log(`[apifuse dev] Health: http://localhost:${port}/health`);
@@ -0,0 +1,78 @@
1
+ import type { AuthContext, BrowserClient, CredentialContext, EnvContext, HttpClient, NativeContext, OcrContext, ProviderCache, ProviderChoiceContext, ProviderContext, ProviderDefinition, ProviderFilesContext, ProviderRequestContext, ProviderRuntimeState, ResolverContext, StealthClient, SttContext, TraceContext } from "./types.js";
2
+ /** Versioned envelope protocol used by out-of-process engine transports. */
3
+ export declare const PROVIDER_ENGINE_PROTOCOL_VERSION: "provider-engine.v1";
4
+ /** Credential names owned by the engine and forbidden in provider declarations. */
5
+ export declare const ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES: readonly ["APIFUSE__PROXY__SMARTPROXY_APP_KEY", "APIFUSE__PROXY__NODEMAVEN_USERNAME", "APIFUSE__PROXY__NODEMAVEN_PASSWORD"];
6
+ export declare function isEngineOwnedProxyCredentialName(name: string): boolean;
7
+ /**
8
+ * Trace-export configuration owned by the engine and forbidden in provider
9
+ * declarations. The header variables carry collector credentials; the rest are
10
+ * engine deployment settings a provider has no reason to read.
11
+ */
12
+ export declare const ENGINE_OWNED_TELEMETRY_ENV_NAMES: readonly ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_EXPORTER_OTLP_TRACES_HEADERS", "OTEL_EXPORTER_OTLP_HEADERS", "OTEL_SERVICE_NAME", "OTEL_RESOURCE_ATTRIBUTES"];
13
+ export declare function isEngineOwnedTelemetryEnvName(name: string): boolean;
14
+ /** Every environment name the engine owns: rejected in declarations and filtered from provider projections. */
15
+ export declare function isEngineOwnedEnvName(name: string): boolean;
16
+ /** Capture credentials in the engine host before constructing provider bindings. */
17
+ export declare function readEngineProxyCredentials(environment?: Readonly<Record<string, string | undefined>>): Readonly<Record<string, string>>;
18
+ /** Build the exact environment projection permitted to enter a provider runtime. */
19
+ export declare function createProviderEnvironment(environment: Readonly<Record<string, string | undefined>>, declaredNames: readonly string[]): Readonly<Record<string, string>>;
20
+ /**
21
+ * Request/response is the first remote transport lane. Stream and session
22
+ * handles are separate lanes so lifecycle-bearing capabilities are never
23
+ * disguised as ordinary JSON calls.
24
+ */
25
+ export interface ProviderEngineTransport {
26
+ request<TResponse = unknown>(request: ProviderEngineRequest): Promise<TResponse>;
27
+ openStream?(request: ProviderEngineRequest): Promise<ReadableStream<Uint8Array>>;
28
+ openSession?(request: ProviderEngineRequest): Promise<ProviderEngineSession>;
29
+ }
30
+ export interface ProviderEngineRequest {
31
+ readonly version: typeof PROVIDER_ENGINE_PROTOCOL_VERSION;
32
+ readonly providerId: string;
33
+ readonly requestId: string;
34
+ readonly capability: ProviderCapabilityKey;
35
+ readonly method: string;
36
+ readonly payload: unknown;
37
+ }
38
+ export interface ProviderEngineSession {
39
+ request<TResponse = unknown>(method: string, payload: unknown): Promise<TResponse>;
40
+ close(): Promise<void>;
41
+ }
42
+ /** Capability implementations owned by the engine rather than provider code. */
43
+ export interface ProviderEngineCapabilitySurface {
44
+ readonly http: HttpClient;
45
+ readonly browser: BrowserClient;
46
+ readonly stealth: StealthClient;
47
+ readonly stt: SttContext;
48
+ readonly ocr: OcrContext;
49
+ readonly resolver: ResolverContext;
50
+ readonly cache: ProviderCache;
51
+ readonly state: ProviderRuntimeState;
52
+ }
53
+ /** Engine-resident-only capabilities, kept separate from the portable surface. */
54
+ export interface ProviderEngineResidentSurface {
55
+ readonly native: NativeContext;
56
+ }
57
+ export declare const PROVIDER_CAPABILITY_KEYS: readonly ["env", "credential", "http", "files", "native", "cache", "state", "stealth", "browser", "auth", "ocr", "stt", "resolver", "choice"];
58
+ export type ProviderCapabilityKey = (typeof PROVIDER_CAPABILITY_KEYS)[number];
59
+ export type ProviderEngineBindingCandidates = Partial<ProviderEngineCapabilitySurface & ProviderEngineResidentSurface & {
60
+ readonly env: EnvContext;
61
+ readonly credential: CredentialContext;
62
+ readonly files: ProviderFilesContext;
63
+ readonly auth: AuthContext;
64
+ readonly choice: ProviderChoiceContext;
65
+ }> & {
66
+ readonly request?: ProviderRequestContext;
67
+ readonly trace: TraceContext;
68
+ };
69
+ export interface ProviderEngineAttachmentInput {
70
+ readonly provider: ProviderDefinition;
71
+ readonly bindings: ProviderEngineBindingCandidates;
72
+ }
73
+ /** Attachment boundary shared by in-process development and remote RPC bridges. */
74
+ export interface ProviderEngine {
75
+ attach<TDeclaration extends object = Record<string, unknown>>(input: ProviderEngineAttachmentInput): ProviderContext<TDeclaration>;
76
+ }
77
+ /** Local engine attachment; deployed bridges implement the same interface with RPC clients. */
78
+ export declare function createInProcessProviderEngine(): ProviderEngine;