@apifuse/provider-sdk 2.2.0-beta.1 → 2.2.0-beta.10

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 (203) hide show
  1. package/AUTHORING.md +106 -0
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +5 -1
  4. package/SUBMISSION.md +1 -1
  5. package/bin/apifuse-check.ts +44 -59
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +27 -52
  8. package/bin/apifuse-pack-check.ts +14 -0
  9. package/bin/apifuse-pack-smoke.ts +22 -81
  10. package/bin/apifuse-pack-types.ts +266 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +53 -70
  13. package/bin/apifuse-submit-check.ts +510 -45
  14. package/bin/apifuse-sync-assets.ts +117 -0
  15. package/bin/apifuse.ts +1 -1
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/bin/submit-check-xml-semantics.ts +204 -0
  18. package/bin/submit-check-xml.ts +134 -0
  19. package/dist/auth-turn/index.d.ts +3 -3
  20. package/dist/auth.d.ts +2 -2
  21. package/dist/auth.js +9 -18
  22. package/dist/ceremonies/index.d.ts +1 -1
  23. package/dist/ceremonies/index.js +8 -18
  24. package/dist/cli/commands.d.ts +1 -1
  25. package/dist/cli/commands.js +8 -0
  26. package/dist/cli/create.d.ts +3 -0
  27. package/dist/cli/create.js +34 -35
  28. package/dist/cli/prompt-assets.d.ts +80 -0
  29. package/dist/cli/prompt-assets.js +743 -0
  30. package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
  31. package/dist/config/loader.d.ts +149 -8
  32. package/dist/config/loader.js +378 -89
  33. package/dist/contract-serialization.d.ts +2 -2
  34. package/dist/contract-serialization.js +3 -6
  35. package/dist/contract-types.d.ts +2 -2
  36. package/dist/contract.d.ts +3 -3
  37. package/dist/contract.js +4 -6
  38. package/dist/define.d.ts +8 -1
  39. package/dist/define.js +119 -122
  40. package/dist/dev.d.ts +1 -1
  41. package/dist/dev.js +1 -1
  42. package/dist/errors.d.ts +4 -1
  43. package/dist/errors.js +48 -0
  44. package/dist/i18n/catalog.d.ts +2 -2
  45. package/dist/i18n/catalog.js +4 -10
  46. package/dist/i18n/index.d.ts +2 -2
  47. package/dist/i18n/index.js +2 -2
  48. package/dist/i18n/keys.d.ts +2 -2
  49. package/dist/index.d.ts +44 -42
  50. package/dist/index.js +39 -37
  51. package/dist/lint.js +8 -15
  52. package/dist/provider.d.ts +11 -11
  53. package/dist/provider.js +9 -9
  54. package/dist/public-schema-field-lint.d.ts +1 -1
  55. package/dist/recipes/gov-api.js +1 -1
  56. package/dist/runtime/auth-flow.d.ts +1 -1
  57. package/dist/runtime/auth-flow.js +3 -3
  58. package/dist/runtime/browser.d.ts +1 -1
  59. package/dist/runtime/browser.js +15 -29
  60. package/dist/runtime/cache.d.ts +1 -1
  61. package/dist/runtime/cache.js +4 -8
  62. package/dist/runtime/choice.d.ts +1 -1
  63. package/dist/runtime/choice.js +31 -35
  64. package/dist/runtime/credential.d.ts +1 -1
  65. package/dist/runtime/credential.js +1 -1
  66. package/dist/runtime/env.d.ts +1 -1
  67. package/dist/runtime/executor.d.ts +1 -1
  68. package/dist/runtime/executor.js +15 -3
  69. package/dist/runtime/http.d.ts +2 -2
  70. package/dist/runtime/http.js +144 -348
  71. package/dist/runtime/insights.d.ts +1 -1
  72. package/dist/runtime/insights.js +6 -13
  73. package/dist/runtime/instrumentation.d.ts +2 -2
  74. package/dist/runtime/instrumentation.js +56 -19
  75. package/dist/runtime/keyring.js +1 -1
  76. package/dist/runtime/namespace.js +1 -1
  77. package/dist/runtime/otlp.d.ts +1 -1
  78. package/dist/runtime/perf.d.ts +1 -1
  79. package/dist/runtime/provider.d.ts +1 -1
  80. package/dist/runtime/provider.js +1 -2
  81. package/dist/runtime/proxy-errors.d.ts +1 -1
  82. package/dist/runtime/proxy-errors.js +9 -7
  83. package/dist/runtime/proxy-nodemaven.d.ts +35 -0
  84. package/dist/runtime/proxy-nodemaven.js +128 -0
  85. package/dist/runtime/proxy-retry-policy.d.ts +40 -0
  86. package/dist/runtime/proxy-retry-policy.js +326 -0
  87. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  88. package/dist/runtime/proxy-telemetry.js +55 -52
  89. package/dist/runtime/redis.d.ts +1 -1
  90. package/dist/runtime/redis.js +2 -4
  91. package/dist/runtime/request-options.d.ts +1 -1
  92. package/dist/runtime/secrets.d.ts +27 -0
  93. package/dist/runtime/secrets.js +51 -0
  94. package/dist/runtime/state.d.ts +2 -2
  95. package/dist/runtime/state.js +15 -4
  96. package/dist/runtime/stealth.d.ts +2 -2
  97. package/dist/runtime/stealth.js +108 -212
  98. package/dist/runtime/stt.d.ts +1 -1
  99. package/dist/runtime/stt.js +11 -15
  100. package/dist/runtime/trace.d.ts +2 -2
  101. package/dist/runtime/trace.js +2 -4
  102. package/dist/runtime/waterfall.d.ts +1 -1
  103. package/dist/schema.d.ts +1 -1
  104. package/dist/schema.js +7 -15
  105. package/dist/serve.d.ts +1 -1
  106. package/dist/serve.js +1 -1
  107. package/dist/server/index.d.ts +7 -7
  108. package/dist/server/index.js +6 -6
  109. package/dist/server/self-test-redaction.d.ts +1 -1
  110. package/dist/server/self-test-redaction.js +1 -1
  111. package/dist/server/self-test.d.ts +104 -3
  112. package/dist/server/self-test.js +673 -115
  113. package/dist/server/serve.d.ts +8 -3
  114. package/dist/server/serve.js +105 -59
  115. package/dist/server/types.d.ts +10 -9
  116. package/dist/server/types.js +3 -7
  117. package/dist/stealth/profiles.d.ts +1 -1
  118. package/dist/stealth/profiles.js +5 -14
  119. package/dist/stream.d.ts +1 -1
  120. package/dist/testing/index.d.ts +2 -2
  121. package/dist/testing/index.js +2 -2
  122. package/dist/testing/run.d.ts +1 -1
  123. package/dist/testing/run.js +7 -14
  124. package/dist/types.d.ts +87 -1
  125. package/dist/user-input.d.ts +30 -0
  126. package/dist/user-input.js +66 -0
  127. package/package.json +9 -5
  128. package/src/auth-turn/index.ts +1 -1
  129. package/src/auth.ts +28 -86
  130. package/src/ceremonies/index.ts +24 -78
  131. package/src/cli/commands.ts +10 -0
  132. package/src/cli/create.ts +42 -35
  133. package/src/cli/prompt-assets.ts +865 -0
  134. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  135. package/src/config/loader.ts +543 -208
  136. package/src/contract-serialization.ts +5 -11
  137. package/src/contract-types.ts +2 -2
  138. package/src/contract.ts +12 -28
  139. package/src/define.ts +247 -505
  140. package/src/dev.ts +4 -9
  141. package/src/errors.ts +58 -5
  142. package/src/i18n/catalog.ts +10 -32
  143. package/src/i18n/index.ts +2 -2
  144. package/src/i18n/keys.ts +5 -11
  145. package/src/index.ts +49 -42
  146. package/src/lint.ts +89 -159
  147. package/src/provider.ts +15 -11
  148. package/src/public-schema-field-lint.ts +7 -33
  149. package/src/recipes/gov-api.ts +2 -5
  150. package/src/runtime/auth-flow.ts +5 -7
  151. package/src/runtime/browser.ts +64 -187
  152. package/src/runtime/cache.ts +20 -67
  153. package/src/runtime/choice.ts +79 -132
  154. package/src/runtime/credential.ts +2 -2
  155. package/src/runtime/env.ts +1 -1
  156. package/src/runtime/executor.ts +23 -20
  157. package/src/runtime/http.ts +195 -559
  158. package/src/runtime/insights.ts +15 -53
  159. package/src/runtime/instrumentation.ts +78 -65
  160. package/src/runtime/keyring.ts +7 -19
  161. package/src/runtime/namespace.ts +2 -7
  162. package/src/runtime/otlp.ts +12 -23
  163. package/src/runtime/perf.ts +1 -1
  164. package/src/runtime/provider.ts +4 -9
  165. package/src/runtime/proxy-errors.ts +29 -42
  166. package/src/runtime/proxy-nodemaven.ts +178 -0
  167. package/src/runtime/proxy-retry-policy.ts +469 -0
  168. package/src/runtime/proxy-telemetry.ts +79 -77
  169. package/src/runtime/redis.ts +4 -12
  170. package/src/runtime/request-options.ts +4 -13
  171. package/src/runtime/secrets.ts +64 -0
  172. package/src/runtime/state.ts +41 -110
  173. package/src/runtime/stealth.ts +162 -377
  174. package/src/runtime/stt.ts +38 -94
  175. package/src/runtime/trace.ts +14 -44
  176. package/src/runtime/waterfall.ts +5 -18
  177. package/src/schema.ts +23 -84
  178. package/src/serve.ts +1 -1
  179. package/src/server/index.ts +12 -7
  180. package/src/server/self-test-redaction.ts +2 -2
  181. package/src/server/self-test.ts +857 -132
  182. package/src/server/serve.ts +172 -245
  183. package/src/server/types.ts +7 -19
  184. package/src/stealth/profiles.ts +10 -26
  185. package/src/stream.ts +8 -19
  186. package/src/testing/index.ts +2 -2
  187. package/src/testing/run.ts +19 -67
  188. package/src/types.ts +88 -1
  189. package/src/user-input.ts +118 -0
  190. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  191. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  192. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  193. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  194. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  195. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  196. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  197. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  198. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  199. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  200. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  201. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  202. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  203. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
- import { providerCacheRedisUrlFromEnv } from "../config/loader";
3
- import { createProviderRedisClient, ensureRedisReady, withRedisTimeout, } from "./redis";
2
+ import { providerCacheRedisUrlFromEnv } from "../config/loader.js";
3
+ import { createProviderRedisClient, ensureRedisReady, withRedisTimeout, } from "./redis.js";
4
4
  const DEFAULT_PREFIX = "apifuse:provider-cache:v1";
5
5
  const DEFAULT_MEMORY_MAX_ENTRIES = 1_000;
6
6
  const DEFAULT_REDIS_TIMEOUT_MS = 150;
@@ -71,10 +71,7 @@ function normalizeKeyPart(value, extra) {
71
71
  return value;
72
72
  }
73
73
  function stableHash(value) {
74
- return createHash("sha256")
75
- .update(JSON.stringify(value))
76
- .digest("hex")
77
- .slice(0, 32);
74
+ return createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 32);
78
75
  }
79
76
  function jitteredTtlMs(ttlMs, jitterPct) {
80
77
  if (!jitterPct || jitterPct <= 0)
@@ -282,8 +279,7 @@ export function createProviderCache(options) {
282
279
  async delete(key) {
283
280
  backend.memory.delete(key);
284
281
  const redis = backend.redis;
285
- if (!redis ||
286
- !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
282
+ if (!redis || !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
287
283
  return;
288
284
  }
289
285
  await withRedisFallback(() => redis.del(key));
@@ -1,4 +1,4 @@
1
- import type { CredentialContext, EnvContext, ProviderChoiceContext, ProviderRequestContext, ProviderRuntimeState } from "../types";
1
+ import type { CredentialContext, EnvContext, ProviderChoiceContext, ProviderRequestContext, ProviderRuntimeState } from "../types.js";
2
2
  export declare const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV = "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
3
3
  export type CreateProviderChoiceContextOptions = {
4
4
  readonly providerId: string;
@@ -1,6 +1,6 @@
1
1
  import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
2
- import { assertFreshProviderChoiceIssuedAt, ProviderChoiceTokenError, } from "../choice-token";
3
- import { ProviderError } from "../errors";
2
+ import { assertFreshProviderChoiceIssuedAt, ProviderChoiceTokenError, } from "../choice-token.js";
3
+ import { isProviderError, ProviderError } from "../errors.js";
4
4
  export const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV = "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
5
5
  const PRIMARY_CHOICE_TOKEN_KID = "v1";
6
6
  const MANAGED_CHOICE_TOKEN_VERSION = 1;
@@ -53,7 +53,7 @@ export function createProviderChoiceContext(options) {
53
53
  });
54
54
  }
55
55
  function parse(parseOptions) {
56
- const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature,] = parseManagedChoiceTokenParts(parseOptions.token);
56
+ const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature] = parseManagedChoiceTokenParts(parseOptions.token);
57
57
  if (actualPrefix !== parseOptions.prefix ||
58
58
  tokenKid !== kid ||
59
59
  !encodedIv ||
@@ -68,13 +68,7 @@ export function createProviderChoiceContext(options) {
68
68
  purpose: parseOptions.purpose,
69
69
  kid: tokenKid,
70
70
  });
71
- const signedBody = [
72
- parseOptions.prefix,
73
- tokenKid,
74
- encodedIv,
75
- encryptedPayload,
76
- authTag,
77
- ].join(".");
71
+ const signedBody = [parseOptions.prefix, tokenKid, encodedIv, encryptedPayload, authTag].join(".");
78
72
  assertManagedChoiceSignature({
79
73
  signedBody,
80
74
  signature,
@@ -117,13 +111,11 @@ export function createProviderChoiceContext(options) {
117
111
  export function createTestProviderChoiceContext(options) {
118
112
  return createProviderChoiceContext({
119
113
  ...options,
120
- masterSecret: options.masterSecret ??
121
- "apifuse-test-provider-runtime-choice-token-master-secret",
114
+ masterSecret: options.masterSecret ?? "apifuse-test-provider-runtime-choice-token-master-secret",
122
115
  });
123
116
  }
124
117
  function resolveChoiceMasterSecret(options) {
125
- const configured = options.masterSecret ??
126
- options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
118
+ const configured = options.masterSecret ?? options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
127
119
  const trimmed = configured?.trim();
128
120
  if (trimmed)
129
121
  return trimmed;
@@ -165,13 +157,7 @@ function encryptManagedChoiceToken(options) {
165
157
  ]).toString("base64url");
166
158
  const authTag = cipher.getAuthTag().toString("base64url");
167
159
  const encodedIv = iv.toString("base64url");
168
- const signedBody = [
169
- options.prefix,
170
- options.kid,
171
- encodedIv,
172
- encryptedPayload,
173
- authTag,
174
- ].join(".");
160
+ const signedBody = [options.prefix, options.kid, encodedIv, encryptedPayload, authTag].join(".");
175
161
  const signature = createHmac("sha256", options.keys.signing)
176
162
  .update(signedBody)
177
163
  .digest("base64url");
@@ -223,7 +209,25 @@ async function parseServerStoredChoice(options) {
223
209
  storage,
224
210
  contextState: options.contextState,
225
211
  });
226
- const record = await namespace.get(optionsStateKey(options.handle.state_id));
212
+ // Reading a server-stored choice back deserializes a persisted value. A
213
+ // corrupt/undecodable value would otherwise surface as a raw JSON.parse
214
+ // SyntaxError (or another unexpected throwable) that escapes the choice error
215
+ // taxonomy, gets masked as internal_error 500, and is treated as retryable by
216
+ // the hub -> reservation restart loop (2026-07-22 catchtable RCA, candidate A).
217
+ // Convert any non-branded throwable into a branded invalid_payload so it maps
218
+ // to a clean, non-retryable 400. Branded ProviderChoiceTokenError and genuine
219
+ // ProviderError (e.g. Redis-unavailable / state-unavailable) pass through so
220
+ // their category/retryable semantics are preserved.
221
+ let record;
222
+ try {
223
+ record = await namespace.get(optionsStateKey(options.handle.state_id));
224
+ }
225
+ catch (error) {
226
+ if (error instanceof ProviderChoiceTokenError || isProviderError(error)) {
227
+ throw error;
228
+ }
229
+ throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token state payload could not be decoded.");
230
+ }
227
231
  if (!record) {
228
232
  throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token state payload is missing.");
229
233
  }
@@ -345,9 +349,7 @@ function isManagedChoiceEnvelope(value) {
345
349
  typeof value.issued_at_ms === "number" &&
346
350
  "ttl_ms" in value &&
347
351
  typeof value.ttl_ms === "number" &&
348
- (!("binding" in value) ||
349
- value.binding === undefined ||
350
- isChoiceBinding(value.binding)));
352
+ (!("binding" in value) || value.binding === undefined || isChoiceBinding(value.binding)));
351
353
  }
352
354
  function isChoicePayload(value) {
353
355
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -355,29 +357,23 @@ function isChoicePayload(value) {
355
357
  function isChoiceBinding(value) {
356
358
  if (!value || typeof value !== "object" || Array.isArray(value))
357
359
  return false;
358
- return ((!("connection_hash" in value) ||
359
- typeof value.connection_hash === "string") &&
360
+ return ((!("connection_hash" in value) || typeof value.connection_hash === "string") &&
360
361
  (!("credential_hash" in value) || typeof value.credential_hash === "string"));
361
362
  }
362
363
  function assertManagedChoiceEnvelope(envelope, options) {
363
- if (envelope.provider_id !== options.providerId ||
364
- envelope.purpose !== options.purpose) {
364
+ if (envelope.provider_id !== options.providerId || envelope.purpose !== options.purpose) {
365
365
  throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token payload is invalid.");
366
366
  }
367
367
  assertFreshProviderChoiceIssuedAt(envelope.issued_at_ms, {
368
368
  // Clamp to the issuer's embedded TTL so a caller-supplied value cannot
369
369
  // silently extend token validity past the deadline the issuer intended.
370
- ttlMs: options.ttlMs != null
371
- ? Math.min(options.ttlMs, envelope.ttl_ms)
372
- : envelope.ttl_ms,
370
+ ttlMs: options.ttlMs != null ? Math.min(options.ttlMs, envelope.ttl_ms) : envelope.ttl_ms,
373
371
  nowMs: options.nowMs,
374
372
  futureToleranceMs: options.futureToleranceMs,
375
373
  });
376
374
  }
377
375
  function createChoiceBinding(options) {
378
- const connectionHash = options.options?.connection
379
- ? hashRequiredConnection(options)
380
- : undefined;
376
+ const connectionHash = options.options?.connection ? hashRequiredConnection(options) : undefined;
381
377
  const credentialHash = options.options?.credentialKeys?.length
382
378
  ? hashCredentialKeys(options)
383
379
  : undefined;
@@ -1,4 +1,4 @@
1
- import type { AuthMode, CredentialContext } from "../types";
1
+ import type { AuthMode, CredentialContext } from "../types.js";
2
2
  export interface CreateCredentialContextOptions {
3
3
  allowedKeys?: string[];
4
4
  mode?: AuthMode;
@@ -1,4 +1,4 @@
1
- import { CredentialModeError } from "../errors";
1
+ import { CredentialModeError } from "../errors.js";
2
2
  function getAllowedKeys(allowedKeys, values) {
3
3
  if (allowedKeys) {
4
4
  return allowedKeys;
@@ -1,2 +1,2 @@
1
- import type { EnvContext } from "../types";
1
+ import type { EnvContext } from "../types.js";
2
2
  export declare function createEnvContext(allowedKeys?: string[]): EnvContext;
@@ -1,4 +1,4 @@
1
- import type { ProviderContext, ProviderDefinition } from "../types";
1
+ import type { ProviderContext, ProviderDefinition } from "../types.js";
2
2
  export declare function isStreamingOperation(provider: ProviderDefinition, operationId: string): boolean;
3
3
  /**
4
4
  * Execute a provider operation by calling its handler.
@@ -1,5 +1,6 @@
1
- import { ProviderError, SessionExpiredError } from "../errors";
2
- import { parseSchema } from "../schema";
1
+ import { isSessionExpiredError, ProviderError, SessionExpiredError } from "../errors.js";
2
+ import { parseSchema } from "../schema.js";
3
+ import { assertRequiredSecretsPresent } from "./secrets.js";
3
4
  export function isStreamingOperation(provider, operationId) {
4
5
  const kind = provider.operations[operationId]?.transport?.kind ?? "json";
5
6
  return kind !== "json";
@@ -23,6 +24,12 @@ export async function executeOperation(provider, operationId, ctx, input, _optio
23
24
  fix: `Valid operations: ${Object.keys(provider.operations).join(", ")}`,
24
25
  });
25
26
  }
27
+ // SDK-owned secret presence gate (single source of truth): declared
28
+ // `required: true` secrets are validated here, before input parsing and the
29
+ // handler, so every invocation path (serve /v1, self-test probes, perf,
30
+ // record) fails with the same structured MISSING_SECRET error instead of a
31
+ // handler-specific crash. Providers must not re-check presence locally.
32
+ assertRequiredSecretsPresent(provider, ctx.env);
26
33
  const validatedInput = await parseSchema(operation.input, input, `operations.${operationId}.input`);
27
34
  const execute = () => ctx.trace.span(`handler:${operationId}`, () => Promise.resolve(operation.handler(ctx, validatedInput)));
28
35
  let result;
@@ -39,7 +46,12 @@ export async function executeOperation(provider, operationId, ctx, input, _optio
39
46
  // operation is safe to re-drive after refresh, which we signal by marking
40
47
  // the surfaced error retryable; non-idempotent operations (the default)
41
48
  // stay non-retryable so they are not auto-re-driven. See design.md §4.3 D3.
42
- if (error instanceof SessionExpiredError && operation.retryOnAuthRefresh) {
49
+ // Use the branded guard, not `instanceof`: a handler loaded through a
50
+ // duplicate/published SDK module can throw a correctly branded
51
+ // SessionExpiredError whose constructor identity differs from this
52
+ // executor's, which `instanceof` would miss — dropping the retryable
53
+ // upgrade and stranding an operation that opted into auth refresh.
54
+ if (isSessionExpiredError(error) && operation.retryOnAuthRefresh) {
43
55
  throw new SessionExpiredError(error.message, { retryable: true });
44
56
  }
45
57
  throw error;
@@ -1,5 +1,5 @@
1
- import type { ProxyResolutionOptions } from "../config/loader";
2
- import type { HttpClient, HttpRetrySummary } from "../types";
1
+ import type { ProxyResolutionOptions } from "../config/loader.js";
2
+ import type { HttpClient, HttpRetrySummary } from "../types.js";
3
3
  export type HttpClientOptions = ProxyResolutionOptions & {
4
4
  warn?: (message: string) => void;
5
5
  userAgent?: string;