@adcp/sdk 13.0.0-rc.13 → 13.0.0-rc.14

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 (73) hide show
  1. package/dist/lib/index.d.mts +2 -2
  2. package/dist/lib/index.d.ts +2 -2
  3. package/dist/lib/index.d.ts.map +1 -1
  4. package/dist/lib/index.js +6 -0
  5. package/dist/lib/index.js.map +1 -1
  6. package/dist/lib/index.mjs +6 -0
  7. package/dist/lib/index.mjs.map +1 -1
  8. package/dist/lib/protocols/transportDiagnostics.js +2 -1
  9. package/dist/lib/protocols/transportDiagnostics.js.map +1 -1
  10. package/dist/lib/protocols/transportDiagnostics.mjs +2 -1
  11. package/dist/lib/protocols/transportDiagnostics.mjs.map +1 -1
  12. package/dist/lib/registry/index.d.mts +17 -0
  13. package/dist/lib/registry/index.d.ts +17 -0
  14. package/dist/lib/registry/index.d.ts.map +1 -1
  15. package/dist/lib/registry/index.js +81 -7
  16. package/dist/lib/registry/index.js.map +1 -1
  17. package/dist/lib/registry/index.mjs +79 -7
  18. package/dist/lib/registry/index.mjs.map +1 -1
  19. package/dist/lib/registry/types.generated.d.mts +157 -7
  20. package/dist/lib/registry/types.generated.d.ts +157 -7
  21. package/dist/lib/registry/types.generated.d.ts.map +1 -1
  22. package/dist/lib/registry/types.generated.js.map +1 -1
  23. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  24. package/dist/lib/server/errors.d.mts +10 -2
  25. package/dist/lib/server/errors.d.ts +10 -2
  26. package/dist/lib/server/errors.d.ts.map +1 -1
  27. package/dist/lib/server/errors.js +9 -2
  28. package/dist/lib/server/errors.js.map +1 -1
  29. package/dist/lib/server/errors.mjs +9 -2
  30. package/dist/lib/server/errors.mjs.map +1 -1
  31. package/dist/lib/testing/compliance/comply.d.mts +6 -0
  32. package/dist/lib/testing/compliance/comply.d.ts +6 -0
  33. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  34. package/dist/lib/testing/compliance/comply.js +3 -0
  35. package/dist/lib/testing/compliance/comply.js.map +1 -1
  36. package/dist/lib/testing/compliance/comply.mjs +3 -0
  37. package/dist/lib/testing/compliance/comply.mjs.map +1 -1
  38. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  39. package/dist/lib/testing/storyboard/runner.js +31 -5
  40. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  41. package/dist/lib/testing/storyboard/runner.mjs +36 -6
  42. package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
  43. package/dist/lib/testing/storyboard/types.d.mts +16 -5
  44. package/dist/lib/testing/storyboard/types.d.ts +16 -5
  45. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  46. package/dist/lib/testing/storyboard/types.js +1 -1
  47. package/dist/lib/testing/storyboard/types.js.map +1 -1
  48. package/dist/lib/testing/storyboard/types.mjs +1 -1
  49. package/dist/lib/testing/storyboard/types.mjs.map +1 -1
  50. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  51. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  52. package/dist/lib/v2/projection/augment-response.d.mts +16 -0
  53. package/dist/lib/v2/projection/augment-response.d.ts +16 -0
  54. package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
  55. package/dist/lib/v2/projection/augment-response.js +13 -4
  56. package/dist/lib/v2/projection/augment-response.js.map +1 -1
  57. package/dist/lib/v2/projection/augment-response.mjs +12 -4
  58. package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
  59. package/dist/lib/v2/projection/index.d.mts +3 -1
  60. package/dist/lib/v2/projection/index.d.ts +3 -1
  61. package/dist/lib/v2/projection/index.d.ts.map +1 -1
  62. package/dist/lib/v2/projection/index.js +2 -0
  63. package/dist/lib/v2/projection/index.js.map +1 -1
  64. package/dist/lib/v2/projection/index.mjs +2 -0
  65. package/dist/lib/v2/projection/index.mjs.map +1 -1
  66. package/dist/lib/version.d.mts +3 -3
  67. package/dist/lib/version.d.ts +3 -3
  68. package/dist/lib/version.js +3 -3
  69. package/dist/lib/version.js.map +1 -1
  70. package/dist/lib/version.mjs +3 -3
  71. package/dist/lib/version.mjs.map +1 -1
  72. package/examples/error-compliant-server.ts +9 -3
  73. package/package.json +1 -1
@@ -4,5 +4,5 @@
4
4
  "source_sha": "4e553ad955f83b49c7d221ab5c3ff78237ad02e3",
5
5
  "source_tarball_sha256": "580656d6466ef9f0d1119985e6726c2efea718dc671e2ad30957fcb2fd54af0f",
6
6
  "upstream_adcp_version": "2.5.3",
7
- "synced_at": "2026-08-10T05:48:43.171Z"
7
+ "synced_at": "2026-08-11T09:45:42.714Z"
8
8
  }
@@ -8,6 +8,11 @@ import { type StandardErrorCode, type ErrorRecovery } from '../types/error-codes
8
8
  import type { ValidationIssue } from '../validation/schema-validator.mjs';
9
9
  export interface AdcpErrorOptions {
10
10
  message: string;
11
+ /**
12
+ * Opaque request context to echo beside `adcp_error`. Only a non-null,
13
+ * non-array object is echoable, matching the framework envelope behavior.
14
+ */
15
+ context?: unknown;
11
16
  /**
12
17
  * Override the recovery classification. Defaults to
13
18
  * `STANDARD_ERROR_CODES[code].recovery` for known codes, `'terminal'`
@@ -89,6 +94,7 @@ export interface AdcpErrorResponse {
89
94
  isError: true;
90
95
  structuredContent: {
91
96
  adcp_error: AdcpErrorPayload;
97
+ context?: object;
92
98
  };
93
99
  }
94
100
  /**
@@ -123,15 +129,17 @@ export interface AdcpErrorResponse {
123
129
  * ```typescript
124
130
  * import { adcpError } from '@adcp/sdk';
125
131
  *
126
- * server.registerTool("get_products", { inputSchema: schema }, async ({ query }) => {
132
+ * server.registerTool("get_products", { inputSchema: schema }, async request => {
133
+ * const { query, context } = request;
127
134
  * if (!products.length) {
128
135
  * return adcpError('PRODUCT_NOT_FOUND', {
129
136
  * message: 'No products match query',
130
137
  * field: 'query',
131
138
  * suggestion: 'Try a broader search term',
139
+ * context,
132
140
  * });
133
141
  * }
134
- * return { content: [...], structuredContent: { products } };
142
+ * return { content: [...], structuredContent: { products, context } };
135
143
  * });
136
144
  * ```
137
145
  */
@@ -8,6 +8,11 @@ import { type StandardErrorCode, type ErrorRecovery } from '../types/error-codes
8
8
  import type { ValidationIssue } from '../validation/schema-validator';
9
9
  export interface AdcpErrorOptions {
10
10
  message: string;
11
+ /**
12
+ * Opaque request context to echo beside `adcp_error`. Only a non-null,
13
+ * non-array object is echoable, matching the framework envelope behavior.
14
+ */
15
+ context?: unknown;
11
16
  /**
12
17
  * Override the recovery classification. Defaults to
13
18
  * `STANDARD_ERROR_CODES[code].recovery` for known codes, `'terminal'`
@@ -89,6 +94,7 @@ export interface AdcpErrorResponse {
89
94
  isError: true;
90
95
  structuredContent: {
91
96
  adcp_error: AdcpErrorPayload;
97
+ context?: object;
92
98
  };
93
99
  }
94
100
  /**
@@ -123,15 +129,17 @@ export interface AdcpErrorResponse {
123
129
  * ```typescript
124
130
  * import { adcpError } from '@adcp/sdk';
125
131
  *
126
- * server.registerTool("get_products", { inputSchema: schema }, async ({ query }) => {
132
+ * server.registerTool("get_products", { inputSchema: schema }, async request => {
133
+ * const { query, context } = request;
127
134
  * if (!products.length) {
128
135
  * return adcpError('PRODUCT_NOT_FOUND', {
129
136
  * message: 'No products match query',
130
137
  * field: 'query',
131
138
  * suggestion: 'Try a broader search term',
139
+ * context,
132
140
  * });
133
141
  * }
134
- * return { content: [...], structuredContent: { products } };
142
+ * return { content: [...], structuredContent: { products, context } };
135
143
  * });
136
144
  * ```
137
145
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/lib/server/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,IAAI,CAAC;IACd,iBAAiB,EAAE;QACjB,UAAU,EAAE,gBAAgB,CAAC;KAC9B,CAAC;CACH;AAwBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,iBAAiB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAqB/G;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAUxG;AAED,wBAAgB,2BAA2B,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAEpG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/lib/server/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,IAAI,CAAC;IACd,iBAAiB,EAAE;QACjB,UAAU,EAAE,gBAAgB,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,iBAAiB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAyB/G;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAUxG;AAED,wBAAgB,2BAA2B,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAEpG"}
@@ -26,6 +26,9 @@ module.exports = __toCommonJS(errors_exports);
26
26
  var import_error_codes = require('../types/error-codes.js');
27
27
  var import_envelope_allowlist = require('./envelope-allowlist.js');
28
28
  var import_pick_safe_details = require('./pick-safe-details.js');
29
+ function isEchoableContext(value) {
30
+ return value !== null && typeof value === "object" && !Array.isArray(value);
31
+ }
29
32
  const AUTHORIZATION_REQUIRED_DETAIL_KEYS = [
30
33
  "required_connections",
31
34
  "missing_connections",
@@ -60,10 +63,14 @@ function adcpError(code, options) {
60
63
  ...options.details != null && { details: options.details }
61
64
  };
62
65
  const filtered = applyAdcpErrorAllowlist(code, adcp_error);
66
+ const structuredContent = {
67
+ adcp_error: filtered,
68
+ ...isEchoableContext(options.context) ? { context: options.context } : {}
69
+ };
63
70
  return {
64
- content: [{ type: "text", text: JSON.stringify({ adcp_error: filtered }) }],
71
+ content: [{ type: "text", text: JSON.stringify(structuredContent) }],
65
72
  isError: true,
66
- structuredContent: { adcp_error: filtered }
73
+ structuredContent
67
74
  };
68
75
  }
69
76
  function applyAdcpErrorAllowlist(code, payload) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/server/errors.ts"],"sourcesContent":["/**\n * Server-side helpers for producing L3-compliant AdCP error responses.\n *\n * Use `adcpError()` in MCP tool handlers to return structured errors\n * that clients can automatically detect, classify, and act on.\n */\n\nimport {\n STANDARD_ERROR_CODES,\n isStandardErrorCode,\n type StandardErrorCode,\n type ErrorRecovery,\n} from '../types/error-codes';\nimport type { ValidationIssue } from '../validation/schema-validator';\nimport { ADCP_ERROR_FIELD_ALLOWLIST } from './envelope-allowlist';\nimport { pickSafeDetails } from './pick-safe-details';\n\nexport interface AdcpErrorOptions {\n message: string;\n /**\n * Override the recovery classification. Defaults to\n * `STANDARD_ERROR_CODES[code].recovery` for known codes, `'terminal'`\n * otherwise. Dropped from the wire shape for codes whose entry in\n * `ADCP_ERROR_FIELD_ALLOWLIST` excludes it; normalized back to the\n * standard table value for allowlisted standard-code envelopes.\n */\n recovery?: ErrorRecovery;\n /**\n * Name of the request field the error applies to (validation /\n * constraint errors). Dropped from the wire shape for codes whose\n * `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g. `IDEMPOTENCY_CONFLICT`\n * — a conflict response MUST NOT echo prior payload state).\n */\n field?: string;\n /**\n * Human-readable remediation hint. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g.\n * `IDEMPOTENCY_CONFLICT`).\n */\n suggestion?: string;\n /**\n * Seconds to wait before retrying a transient error. Only meaningful\n * on retryable codes (`RATE_LIMITED`, `SERVICE_UNAVAILABLE`); dropped\n * on terminal codes whose allowlist excludes it (`IDEMPOTENCY_CONFLICT`\n * — a computed `retry_after` on conflict would leak cached-entry age).\n */\n retry_after?: number;\n /**\n * Code-specific diagnostic payload. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it\n * (`IDEMPOTENCY_CONFLICT` — a conflict response MUST NOT echo the\n * prior request payload or cached response body).\n */\n details?: Record<string, unknown>;\n /**\n * Schema validation issues surfaced at the top level of `adcp_error`\n * so operators see JSON Pointers on the first render. Primary use is\n * `VALIDATION_ERROR`; framework validation hooks populate this\n * automatically and also mirror the same array to `details.issues`\n * for buyers that already index into `details` per AdCP spec\n * convention.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorPayload {\n code: string;\n message: string;\n /**\n * Closed-enum classifier. Populated by `adcpError()` from\n * `STANDARD_ERROR_CODES[code].recovery` unless the caller provides an\n * override. Marked optional because per-code inside-`adcp_error`\n * allowlists may deliberately drop it from the wire shape — consumers\n * reading a payload parsed off the wire MUST tolerate `undefined`.\n */\n recovery?: ErrorRecovery;\n field?: string;\n suggestion?: string;\n retry_after?: number;\n details?: Record<string, unknown>;\n /**\n * Schema validation issues (`VALIDATION_ERROR`) exposed at the top\n * level so the list is the first thing a reader sees when inspecting\n * the envelope. Also mirrored at `details.issues` for spec-convention\n * compatibility.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorResponse {\n [key: string]: unknown;\n content: Array<{ type: 'text'; text: string }>;\n isError: true;\n structuredContent: {\n adcp_error: AdcpErrorPayload;\n };\n}\n\nconst AUTHORIZATION_REQUIRED_DETAIL_KEYS = [\n 'required_connections',\n 'missing_connections',\n 'provider',\n 'connection_type',\n 'required_for',\n 'scope',\n 'status',\n 'resource_ref',\n 'platform_account_id',\n 'identity_id',\n 'handle',\n 'profile_url',\n 'post_id',\n 'post_url',\n 'authorization_url',\n 'authorization_instructions',\n 'checked_at',\n 'expires_at',\n 'reference_authorization',\n] as const;\n\n/**\n * Build an L3-compliant MCP tool error response with all three transport layers:\n *\n * 1. `structuredContent.adcp_error` — programmatic extraction (L3)\n * 2. `content[0].text` — JSON text fallback (L2)\n * 3. `isError: true` — MCP error signal\n *\n * Recovery is auto-populated from the standard error code table when not provided.\n *\n * Before returning, any field NOT allowlisted for the given code in\n * {@link ADCP_ERROR_FIELD_ALLOWLIST} is dropped — sellers get the builder's\n * ergonomics for every code AND the strict wire shape for codes that have\n * a registered allowlist. `IDEMPOTENCY_CONFLICT` is the canonical case:\n * payload-shaped diagnostics like `field`, `suggestion`, and `details`\n * silently drop while standard `recovery` metadata is preserved as the\n * canonical standard-table value. Codes without a registered allowlist\n * pass through unchanged.\n *\n * **Two-layer wire shape.** This builder emits the envelope layer\n * (`structuredContent.adcp_error`) only. For tools whose response\n * schema declares a typed Error arm (`errors[]` required at the top\n * level), the framework dispatcher synthesises the payload-layer\n * `errors[]` from the same data at finalize time, so the wire carries\n * both the envelope marker and the typed Error arm together — no\n * adopter code change required. The list of affected tools is derived\n * at server build from the bundled schema cache. RFC:\n * `docs/proposals/adcperror-two-layer-emission.md`.\n *\n * @example\n * ```typescript\n * import { adcpError } from '@adcp/sdk';\n *\n * server.registerTool(\"get_products\", { inputSchema: schema }, async ({ query }) => {\n * if (!products.length) {\n * return adcpError('PRODUCT_NOT_FOUND', {\n * message: 'No products match query',\n * field: 'query',\n * suggestion: 'Try a broader search term',\n * });\n * }\n * return { content: [...], structuredContent: { products } };\n * });\n * ```\n */\nexport function adcpError(code: StandardErrorCode | (string & {}), options: AdcpErrorOptions): AdcpErrorResponse {\n const recovery = normalizeRecoveryForCode(code, options.recovery);\n\n const adcp_error: AdcpErrorPayload = {\n code,\n message: options.message,\n recovery,\n ...(options.field != null && { field: options.field }),\n ...(options.suggestion != null && { suggestion: options.suggestion }),\n ...(options.retry_after != null && { retry_after: options.retry_after }),\n ...(options.issues != null && { issues: options.issues }),\n ...(options.details != null && { details: options.details }),\n };\n\n const filtered = applyAdcpErrorAllowlist(code, adcp_error as unknown as Record<string, unknown>);\n\n return {\n content: [{ type: 'text', text: JSON.stringify({ adcp_error: filtered }) }],\n isError: true,\n structuredContent: { adcp_error: filtered },\n };\n}\n\n/**\n * Drop every field not in {@link ADCP_ERROR_FIELD_ALLOWLIST} for `code`.\n * When an allowlisted standard code carries `recovery`, normalize it to\n * the fixed classifier from `STANDARD_ERROR_CODES` instead of trusting a\n * caller-supplied value.\n * Codes without an entry pass through unchanged — the allowlist is\n * opt-in per code, not a global filter. The returned object is re-typed\n * as `AdcpErrorPayload` on the assumption that `code` and `message`\n * (the only required fields) are in every registered allowlist; that\n * invariant is re-asserted at runtime by the module-load check in\n * `envelope-allowlist.ts`.\n */\nexport function applyAdcpErrorAllowlist(code: string, payload: Record<string, unknown>): AdcpErrorPayload {\n payload = sanitizeAdcpErrorDetails(code, payload);\n const allowlist = ADCP_ERROR_FIELD_ALLOWLIST[code];\n if (!allowlist) return payload as unknown as AdcpErrorPayload;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(payload)) {\n if (!allowlist.has(key)) continue;\n out[key] = key === 'recovery' ? normalizeAllowlistedRecoveryForCode(code, value) : value;\n }\n return out as unknown as AdcpErrorPayload;\n}\n\nexport function sanitizeStructuredAdcpError<T extends { code: string; message: string }>(error: T): T {\n return applyAdcpErrorAllowlist(error.code, error as unknown as Record<string, unknown>) as unknown as T;\n}\n\nfunction sanitizeAdcpErrorDetails(code: string, payload: Record<string, unknown>): Record<string, unknown> {\n if (code !== 'AUTHORIZATION_REQUIRED' || payload.details === undefined) return payload;\n\n const sanitizedDetails = pickSafeDetails(payload.details, AUTHORIZATION_REQUIRED_DETAIL_KEYS, {\n maxDepth: 4,\n maxSizeBytes: 4096,\n });\n const { details: _details, ...rest } = payload;\n return sanitizedDetails === undefined ? rest : { ...rest, details: sanitizedDetails };\n}\n\nfunction isErrorRecovery(value: unknown): value is ErrorRecovery {\n return value === 'transient' || value === 'correctable' || value === 'terminal';\n}\n\nfunction normalizeRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n return isErrorRecovery(value) ? value : isStandardErrorCode(code) ? STANDARD_ERROR_CODES[code].recovery : 'terminal';\n}\n\nfunction normalizeAllowlistedRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n if (isStandardErrorCode(code)) {\n return STANDARD_ERROR_CODES[code].recovery;\n }\n return normalizeRecoveryForCode(code, value);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,yBAKO;AAEP,gCAA2C;AAC3C,+BAAgC;AAmFhC,MAAM,qCAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA8CO,SAAS,UAAU,MAAyC,SAA8C;AAC/G,QAAM,WAAW,yBAAyB,MAAM,QAAQ,QAAQ;AAEhE,QAAM,aAA+B;AAAA,IACnC;AAAA,IACA,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA,GAAI,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM;AAAA,IACpD,GAAI,QAAQ,cAAc,QAAQ,EAAE,YAAY,QAAQ,WAAW;AAAA,IACnE,GAAI,QAAQ,eAAe,QAAQ,EAAE,aAAa,QAAQ,YAAY;AAAA,IACtE,GAAI,QAAQ,UAAU,QAAQ,EAAE,QAAQ,QAAQ,OAAO;AAAA,IACvD,GAAI,QAAQ,WAAW,QAAQ,EAAE,SAAS,QAAQ,QAAQ;AAAA,EAC5D;AAEA,QAAM,WAAW,wBAAwB,MAAM,UAAgD;AAE/F,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,EAAE,YAAY,SAAS,CAAC,EAAE,CAAC;AAAA,IAC1E,SAAS;AAAA,IACT,mBAAmB,EAAE,YAAY,SAAS;AAAA,EAC5C;AACF;AAcO,SAAS,wBAAwB,MAAc,SAAoD;AACxG,YAAU,yBAAyB,MAAM,OAAO;AAChD,QAAM,YAAY,qDAA2B,IAAI;AACjD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,CAAC,UAAU,IAAI,GAAG,EAAG;AACzB,QAAI,GAAG,IAAI,QAAQ,aAAa,oCAAoC,MAAM,KAAK,IAAI;AAAA,EACrF;AACA,SAAO;AACT;AAEO,SAAS,4BAAyE,OAAa;AACpG,SAAO,wBAAwB,MAAM,MAAM,KAA2C;AACxF;AAEA,SAAS,yBAAyB,MAAc,SAA2D;AACzG,MAAI,SAAS,4BAA4B,QAAQ,YAAY,OAAW,QAAO;AAE/E,QAAM,uBAAmB,0CAAgB,QAAQ,SAAS,oCAAoC;AAAA,IAC5F,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AACD,QAAM,EAAE,SAAS,UAAU,GAAG,KAAK,IAAI;AACvC,SAAO,qBAAqB,SAAY,OAAO,EAAE,GAAG,MAAM,SAAS,iBAAiB;AACtF;AAEA,SAAS,gBAAgB,OAAwC;AAC/D,SAAO,UAAU,eAAe,UAAU,iBAAiB,UAAU;AACvE;AAEA,SAAS,yBAAyB,MAAc,OAA+B;AAC7E,SAAO,gBAAgB,KAAK,IAAI,YAAQ,wCAAoB,IAAI,IAAI,wCAAqB,IAAI,EAAE,WAAW;AAC5G;AAEA,SAAS,oCAAoC,MAAc,OAA+B;AACxF,UAAI,wCAAoB,IAAI,GAAG;AAC7B,WAAO,wCAAqB,IAAI,EAAE;AAAA,EACpC;AACA,SAAO,yBAAyB,MAAM,KAAK;AAC7C;","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/server/errors.ts"],"sourcesContent":["/**\n * Server-side helpers for producing L3-compliant AdCP error responses.\n *\n * Use `adcpError()` in MCP tool handlers to return structured errors\n * that clients can automatically detect, classify, and act on.\n */\n\nimport {\n STANDARD_ERROR_CODES,\n isStandardErrorCode,\n type StandardErrorCode,\n type ErrorRecovery,\n} from '../types/error-codes';\nimport type { ValidationIssue } from '../validation/schema-validator';\nimport { ADCP_ERROR_FIELD_ALLOWLIST } from './envelope-allowlist';\nimport { pickSafeDetails } from './pick-safe-details';\n\nexport interface AdcpErrorOptions {\n message: string;\n /**\n * Opaque request context to echo beside `adcp_error`. Only a non-null,\n * non-array object is echoable, matching the framework envelope behavior.\n */\n context?: unknown;\n /**\n * Override the recovery classification. Defaults to\n * `STANDARD_ERROR_CODES[code].recovery` for known codes, `'terminal'`\n * otherwise. Dropped from the wire shape for codes whose entry in\n * `ADCP_ERROR_FIELD_ALLOWLIST` excludes it; normalized back to the\n * standard table value for allowlisted standard-code envelopes.\n */\n recovery?: ErrorRecovery;\n /**\n * Name of the request field the error applies to (validation /\n * constraint errors). Dropped from the wire shape for codes whose\n * `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g. `IDEMPOTENCY_CONFLICT`\n * — a conflict response MUST NOT echo prior payload state).\n */\n field?: string;\n /**\n * Human-readable remediation hint. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g.\n * `IDEMPOTENCY_CONFLICT`).\n */\n suggestion?: string;\n /**\n * Seconds to wait before retrying a transient error. Only meaningful\n * on retryable codes (`RATE_LIMITED`, `SERVICE_UNAVAILABLE`); dropped\n * on terminal codes whose allowlist excludes it (`IDEMPOTENCY_CONFLICT`\n * — a computed `retry_after` on conflict would leak cached-entry age).\n */\n retry_after?: number;\n /**\n * Code-specific diagnostic payload. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it\n * (`IDEMPOTENCY_CONFLICT` — a conflict response MUST NOT echo the\n * prior request payload or cached response body).\n */\n details?: Record<string, unknown>;\n /**\n * Schema validation issues surfaced at the top level of `adcp_error`\n * so operators see JSON Pointers on the first render. Primary use is\n * `VALIDATION_ERROR`; framework validation hooks populate this\n * automatically and also mirror the same array to `details.issues`\n * for buyers that already index into `details` per AdCP spec\n * convention.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorPayload {\n code: string;\n message: string;\n /**\n * Closed-enum classifier. Populated by `adcpError()` from\n * `STANDARD_ERROR_CODES[code].recovery` unless the caller provides an\n * override. Marked optional because per-code inside-`adcp_error`\n * allowlists may deliberately drop it from the wire shape — consumers\n * reading a payload parsed off the wire MUST tolerate `undefined`.\n */\n recovery?: ErrorRecovery;\n field?: string;\n suggestion?: string;\n retry_after?: number;\n details?: Record<string, unknown>;\n /**\n * Schema validation issues (`VALIDATION_ERROR`) exposed at the top\n * level so the list is the first thing a reader sees when inspecting\n * the envelope. Also mirrored at `details.issues` for spec-convention\n * compatibility.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorResponse {\n [key: string]: unknown;\n content: Array<{ type: 'text'; text: string }>;\n isError: true;\n structuredContent: {\n adcp_error: AdcpErrorPayload;\n context?: object;\n };\n}\n\nfunction isEchoableContext(value: unknown): value is object {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nconst AUTHORIZATION_REQUIRED_DETAIL_KEYS = [\n 'required_connections',\n 'missing_connections',\n 'provider',\n 'connection_type',\n 'required_for',\n 'scope',\n 'status',\n 'resource_ref',\n 'platform_account_id',\n 'identity_id',\n 'handle',\n 'profile_url',\n 'post_id',\n 'post_url',\n 'authorization_url',\n 'authorization_instructions',\n 'checked_at',\n 'expires_at',\n 'reference_authorization',\n] as const;\n\n/**\n * Build an L3-compliant MCP tool error response with all three transport layers:\n *\n * 1. `structuredContent.adcp_error` — programmatic extraction (L3)\n * 2. `content[0].text` — JSON text fallback (L2)\n * 3. `isError: true` — MCP error signal\n *\n * Recovery is auto-populated from the standard error code table when not provided.\n *\n * Before returning, any field NOT allowlisted for the given code in\n * {@link ADCP_ERROR_FIELD_ALLOWLIST} is dropped — sellers get the builder's\n * ergonomics for every code AND the strict wire shape for codes that have\n * a registered allowlist. `IDEMPOTENCY_CONFLICT` is the canonical case:\n * payload-shaped diagnostics like `field`, `suggestion`, and `details`\n * silently drop while standard `recovery` metadata is preserved as the\n * canonical standard-table value. Codes without a registered allowlist\n * pass through unchanged.\n *\n * **Two-layer wire shape.** This builder emits the envelope layer\n * (`structuredContent.adcp_error`) only. For tools whose response\n * schema declares a typed Error arm (`errors[]` required at the top\n * level), the framework dispatcher synthesises the payload-layer\n * `errors[]` from the same data at finalize time, so the wire carries\n * both the envelope marker and the typed Error arm together — no\n * adopter code change required. The list of affected tools is derived\n * at server build from the bundled schema cache. RFC:\n * `docs/proposals/adcperror-two-layer-emission.md`.\n *\n * @example\n * ```typescript\n * import { adcpError } from '@adcp/sdk';\n *\n * server.registerTool(\"get_products\", { inputSchema: schema }, async request => {\n * const { query, context } = request;\n * if (!products.length) {\n * return adcpError('PRODUCT_NOT_FOUND', {\n * message: 'No products match query',\n * field: 'query',\n * suggestion: 'Try a broader search term',\n * context,\n * });\n * }\n * return { content: [...], structuredContent: { products, context } };\n * });\n * ```\n */\nexport function adcpError(code: StandardErrorCode | (string & {}), options: AdcpErrorOptions): AdcpErrorResponse {\n const recovery = normalizeRecoveryForCode(code, options.recovery);\n\n const adcp_error: AdcpErrorPayload = {\n code,\n message: options.message,\n recovery,\n ...(options.field != null && { field: options.field }),\n ...(options.suggestion != null && { suggestion: options.suggestion }),\n ...(options.retry_after != null && { retry_after: options.retry_after }),\n ...(options.issues != null && { issues: options.issues }),\n ...(options.details != null && { details: options.details }),\n };\n\n const filtered = applyAdcpErrorAllowlist(code, adcp_error as unknown as Record<string, unknown>);\n const structuredContent = {\n adcp_error: filtered,\n ...(isEchoableContext(options.context) ? { context: options.context } : {}),\n };\n\n return {\n content: [{ type: 'text', text: JSON.stringify(structuredContent) }],\n isError: true,\n structuredContent,\n };\n}\n\n/**\n * Drop every field not in {@link ADCP_ERROR_FIELD_ALLOWLIST} for `code`.\n * When an allowlisted standard code carries `recovery`, normalize it to\n * the fixed classifier from `STANDARD_ERROR_CODES` instead of trusting a\n * caller-supplied value.\n * Codes without an entry pass through unchanged — the allowlist is\n * opt-in per code, not a global filter. The returned object is re-typed\n * as `AdcpErrorPayload` on the assumption that `code` and `message`\n * (the only required fields) are in every registered allowlist; that\n * invariant is re-asserted at runtime by the module-load check in\n * `envelope-allowlist.ts`.\n */\nexport function applyAdcpErrorAllowlist(code: string, payload: Record<string, unknown>): AdcpErrorPayload {\n payload = sanitizeAdcpErrorDetails(code, payload);\n const allowlist = ADCP_ERROR_FIELD_ALLOWLIST[code];\n if (!allowlist) return payload as unknown as AdcpErrorPayload;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(payload)) {\n if (!allowlist.has(key)) continue;\n out[key] = key === 'recovery' ? normalizeAllowlistedRecoveryForCode(code, value) : value;\n }\n return out as unknown as AdcpErrorPayload;\n}\n\nexport function sanitizeStructuredAdcpError<T extends { code: string; message: string }>(error: T): T {\n return applyAdcpErrorAllowlist(error.code, error as unknown as Record<string, unknown>) as unknown as T;\n}\n\nfunction sanitizeAdcpErrorDetails(code: string, payload: Record<string, unknown>): Record<string, unknown> {\n if (code !== 'AUTHORIZATION_REQUIRED' || payload.details === undefined) return payload;\n\n const sanitizedDetails = pickSafeDetails(payload.details, AUTHORIZATION_REQUIRED_DETAIL_KEYS, {\n maxDepth: 4,\n maxSizeBytes: 4096,\n });\n const { details: _details, ...rest } = payload;\n return sanitizedDetails === undefined ? rest : { ...rest, details: sanitizedDetails };\n}\n\nfunction isErrorRecovery(value: unknown): value is ErrorRecovery {\n return value === 'transient' || value === 'correctable' || value === 'terminal';\n}\n\nfunction normalizeRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n return isErrorRecovery(value) ? value : isStandardErrorCode(code) ? STANDARD_ERROR_CODES[code].recovery : 'terminal';\n}\n\nfunction normalizeAllowlistedRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n if (isStandardErrorCode(code)) {\n return STANDARD_ERROR_CODES[code].recovery;\n }\n return normalizeRecoveryForCode(code, value);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,yBAKO;AAEP,gCAA2C;AAC3C,+BAAgC;AAyFhC,SAAS,kBAAkB,OAAiC;AAC1D,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,MAAM,qCAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAgDO,SAAS,UAAU,MAAyC,SAA8C;AAC/G,QAAM,WAAW,yBAAyB,MAAM,QAAQ,QAAQ;AAEhE,QAAM,aAA+B;AAAA,IACnC;AAAA,IACA,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA,GAAI,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM;AAAA,IACpD,GAAI,QAAQ,cAAc,QAAQ,EAAE,YAAY,QAAQ,WAAW;AAAA,IACnE,GAAI,QAAQ,eAAe,QAAQ,EAAE,aAAa,QAAQ,YAAY;AAAA,IACtE,GAAI,QAAQ,UAAU,QAAQ,EAAE,QAAQ,QAAQ,OAAO;AAAA,IACvD,GAAI,QAAQ,WAAW,QAAQ,EAAE,SAAS,QAAQ,QAAQ;AAAA,EAC5D;AAEA,QAAM,WAAW,wBAAwB,MAAM,UAAgD;AAC/F,QAAM,oBAAoB;AAAA,IACxB,YAAY;AAAA,IACZ,GAAI,kBAAkB,QAAQ,OAAO,IAAI,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,EAC3E;AAEA,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,iBAAiB,EAAE,CAAC;AAAA,IACnE,SAAS;AAAA,IACT;AAAA,EACF;AACF;AAcO,SAAS,wBAAwB,MAAc,SAAoD;AACxG,YAAU,yBAAyB,MAAM,OAAO;AAChD,QAAM,YAAY,qDAA2B,IAAI;AACjD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,CAAC,UAAU,IAAI,GAAG,EAAG;AACzB,QAAI,GAAG,IAAI,QAAQ,aAAa,oCAAoC,MAAM,KAAK,IAAI;AAAA,EACrF;AACA,SAAO;AACT;AAEO,SAAS,4BAAyE,OAAa;AACpG,SAAO,wBAAwB,MAAM,MAAM,KAA2C;AACxF;AAEA,SAAS,yBAAyB,MAAc,SAA2D;AACzG,MAAI,SAAS,4BAA4B,QAAQ,YAAY,OAAW,QAAO;AAE/E,QAAM,uBAAmB,0CAAgB,QAAQ,SAAS,oCAAoC;AAAA,IAC5F,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AACD,QAAM,EAAE,SAAS,UAAU,GAAG,KAAK,IAAI;AACvC,SAAO,qBAAqB,SAAY,OAAO,EAAE,GAAG,MAAM,SAAS,iBAAiB;AACtF;AAEA,SAAS,gBAAgB,OAAwC;AAC/D,SAAO,UAAU,eAAe,UAAU,iBAAiB,UAAU;AACvE;AAEA,SAAS,yBAAyB,MAAc,OAA+B;AAC7E,SAAO,gBAAgB,KAAK,IAAI,YAAQ,wCAAoB,IAAI,IAAI,wCAAqB,IAAI,EAAE,WAAW;AAC5G;AAEA,SAAS,oCAAoC,MAAc,OAA+B;AACxF,UAAI,wCAAoB,IAAI,GAAG;AAC7B,WAAO,wCAAqB,IAAI,EAAE;AAAA,EACpC;AACA,SAAO,yBAAyB,MAAM,KAAK;AAC7C;","names":[]}
@@ -4,6 +4,9 @@ import {
4
4
  } from "../types/error-codes.mjs";
5
5
  import { ADCP_ERROR_FIELD_ALLOWLIST } from "./envelope-allowlist.mjs";
6
6
  import { pickSafeDetails } from "./pick-safe-details.mjs";
7
+ function isEchoableContext(value) {
8
+ return value !== null && typeof value === "object" && !Array.isArray(value);
9
+ }
7
10
  const AUTHORIZATION_REQUIRED_DETAIL_KEYS = [
8
11
  "required_connections",
9
12
  "missing_connections",
@@ -38,10 +41,14 @@ function adcpError(code, options) {
38
41
  ...options.details != null && { details: options.details }
39
42
  };
40
43
  const filtered = applyAdcpErrorAllowlist(code, adcp_error);
44
+ const structuredContent = {
45
+ adcp_error: filtered,
46
+ ...isEchoableContext(options.context) ? { context: options.context } : {}
47
+ };
41
48
  return {
42
- content: [{ type: "text", text: JSON.stringify({ adcp_error: filtered }) }],
49
+ content: [{ type: "text", text: JSON.stringify(structuredContent) }],
43
50
  isError: true,
44
- structuredContent: { adcp_error: filtered }
51
+ structuredContent
45
52
  };
46
53
  }
47
54
  function applyAdcpErrorAllowlist(code, payload) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/server/errors.ts"],"sourcesContent":["/**\n * Server-side helpers for producing L3-compliant AdCP error responses.\n *\n * Use `adcpError()` in MCP tool handlers to return structured errors\n * that clients can automatically detect, classify, and act on.\n */\n\nimport {\n STANDARD_ERROR_CODES,\n isStandardErrorCode,\n type StandardErrorCode,\n type ErrorRecovery,\n} from '../types/error-codes';\nimport type { ValidationIssue } from '../validation/schema-validator';\nimport { ADCP_ERROR_FIELD_ALLOWLIST } from './envelope-allowlist';\nimport { pickSafeDetails } from './pick-safe-details';\n\nexport interface AdcpErrorOptions {\n message: string;\n /**\n * Override the recovery classification. Defaults to\n * `STANDARD_ERROR_CODES[code].recovery` for known codes, `'terminal'`\n * otherwise. Dropped from the wire shape for codes whose entry in\n * `ADCP_ERROR_FIELD_ALLOWLIST` excludes it; normalized back to the\n * standard table value for allowlisted standard-code envelopes.\n */\n recovery?: ErrorRecovery;\n /**\n * Name of the request field the error applies to (validation /\n * constraint errors). Dropped from the wire shape for codes whose\n * `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g. `IDEMPOTENCY_CONFLICT`\n * — a conflict response MUST NOT echo prior payload state).\n */\n field?: string;\n /**\n * Human-readable remediation hint. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g.\n * `IDEMPOTENCY_CONFLICT`).\n */\n suggestion?: string;\n /**\n * Seconds to wait before retrying a transient error. Only meaningful\n * on retryable codes (`RATE_LIMITED`, `SERVICE_UNAVAILABLE`); dropped\n * on terminal codes whose allowlist excludes it (`IDEMPOTENCY_CONFLICT`\n * — a computed `retry_after` on conflict would leak cached-entry age).\n */\n retry_after?: number;\n /**\n * Code-specific diagnostic payload. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it\n * (`IDEMPOTENCY_CONFLICT` — a conflict response MUST NOT echo the\n * prior request payload or cached response body).\n */\n details?: Record<string, unknown>;\n /**\n * Schema validation issues surfaced at the top level of `adcp_error`\n * so operators see JSON Pointers on the first render. Primary use is\n * `VALIDATION_ERROR`; framework validation hooks populate this\n * automatically and also mirror the same array to `details.issues`\n * for buyers that already index into `details` per AdCP spec\n * convention.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorPayload {\n code: string;\n message: string;\n /**\n * Closed-enum classifier. Populated by `adcpError()` from\n * `STANDARD_ERROR_CODES[code].recovery` unless the caller provides an\n * override. Marked optional because per-code inside-`adcp_error`\n * allowlists may deliberately drop it from the wire shape — consumers\n * reading a payload parsed off the wire MUST tolerate `undefined`.\n */\n recovery?: ErrorRecovery;\n field?: string;\n suggestion?: string;\n retry_after?: number;\n details?: Record<string, unknown>;\n /**\n * Schema validation issues (`VALIDATION_ERROR`) exposed at the top\n * level so the list is the first thing a reader sees when inspecting\n * the envelope. Also mirrored at `details.issues` for spec-convention\n * compatibility.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorResponse {\n [key: string]: unknown;\n content: Array<{ type: 'text'; text: string }>;\n isError: true;\n structuredContent: {\n adcp_error: AdcpErrorPayload;\n };\n}\n\nconst AUTHORIZATION_REQUIRED_DETAIL_KEYS = [\n 'required_connections',\n 'missing_connections',\n 'provider',\n 'connection_type',\n 'required_for',\n 'scope',\n 'status',\n 'resource_ref',\n 'platform_account_id',\n 'identity_id',\n 'handle',\n 'profile_url',\n 'post_id',\n 'post_url',\n 'authorization_url',\n 'authorization_instructions',\n 'checked_at',\n 'expires_at',\n 'reference_authorization',\n] as const;\n\n/**\n * Build an L3-compliant MCP tool error response with all three transport layers:\n *\n * 1. `structuredContent.adcp_error` — programmatic extraction (L3)\n * 2. `content[0].text` — JSON text fallback (L2)\n * 3. `isError: true` — MCP error signal\n *\n * Recovery is auto-populated from the standard error code table when not provided.\n *\n * Before returning, any field NOT allowlisted for the given code in\n * {@link ADCP_ERROR_FIELD_ALLOWLIST} is dropped — sellers get the builder's\n * ergonomics for every code AND the strict wire shape for codes that have\n * a registered allowlist. `IDEMPOTENCY_CONFLICT` is the canonical case:\n * payload-shaped diagnostics like `field`, `suggestion`, and `details`\n * silently drop while standard `recovery` metadata is preserved as the\n * canonical standard-table value. Codes without a registered allowlist\n * pass through unchanged.\n *\n * **Two-layer wire shape.** This builder emits the envelope layer\n * (`structuredContent.adcp_error`) only. For tools whose response\n * schema declares a typed Error arm (`errors[]` required at the top\n * level), the framework dispatcher synthesises the payload-layer\n * `errors[]` from the same data at finalize time, so the wire carries\n * both the envelope marker and the typed Error arm together — no\n * adopter code change required. The list of affected tools is derived\n * at server build from the bundled schema cache. RFC:\n * `docs/proposals/adcperror-two-layer-emission.md`.\n *\n * @example\n * ```typescript\n * import { adcpError } from '@adcp/sdk';\n *\n * server.registerTool(\"get_products\", { inputSchema: schema }, async ({ query }) => {\n * if (!products.length) {\n * return adcpError('PRODUCT_NOT_FOUND', {\n * message: 'No products match query',\n * field: 'query',\n * suggestion: 'Try a broader search term',\n * });\n * }\n * return { content: [...], structuredContent: { products } };\n * });\n * ```\n */\nexport function adcpError(code: StandardErrorCode | (string & {}), options: AdcpErrorOptions): AdcpErrorResponse {\n const recovery = normalizeRecoveryForCode(code, options.recovery);\n\n const adcp_error: AdcpErrorPayload = {\n code,\n message: options.message,\n recovery,\n ...(options.field != null && { field: options.field }),\n ...(options.suggestion != null && { suggestion: options.suggestion }),\n ...(options.retry_after != null && { retry_after: options.retry_after }),\n ...(options.issues != null && { issues: options.issues }),\n ...(options.details != null && { details: options.details }),\n };\n\n const filtered = applyAdcpErrorAllowlist(code, adcp_error as unknown as Record<string, unknown>);\n\n return {\n content: [{ type: 'text', text: JSON.stringify({ adcp_error: filtered }) }],\n isError: true,\n structuredContent: { adcp_error: filtered },\n };\n}\n\n/**\n * Drop every field not in {@link ADCP_ERROR_FIELD_ALLOWLIST} for `code`.\n * When an allowlisted standard code carries `recovery`, normalize it to\n * the fixed classifier from `STANDARD_ERROR_CODES` instead of trusting a\n * caller-supplied value.\n * Codes without an entry pass through unchanged — the allowlist is\n * opt-in per code, not a global filter. The returned object is re-typed\n * as `AdcpErrorPayload` on the assumption that `code` and `message`\n * (the only required fields) are in every registered allowlist; that\n * invariant is re-asserted at runtime by the module-load check in\n * `envelope-allowlist.ts`.\n */\nexport function applyAdcpErrorAllowlist(code: string, payload: Record<string, unknown>): AdcpErrorPayload {\n payload = sanitizeAdcpErrorDetails(code, payload);\n const allowlist = ADCP_ERROR_FIELD_ALLOWLIST[code];\n if (!allowlist) return payload as unknown as AdcpErrorPayload;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(payload)) {\n if (!allowlist.has(key)) continue;\n out[key] = key === 'recovery' ? normalizeAllowlistedRecoveryForCode(code, value) : value;\n }\n return out as unknown as AdcpErrorPayload;\n}\n\nexport function sanitizeStructuredAdcpError<T extends { code: string; message: string }>(error: T): T {\n return applyAdcpErrorAllowlist(error.code, error as unknown as Record<string, unknown>) as unknown as T;\n}\n\nfunction sanitizeAdcpErrorDetails(code: string, payload: Record<string, unknown>): Record<string, unknown> {\n if (code !== 'AUTHORIZATION_REQUIRED' || payload.details === undefined) return payload;\n\n const sanitizedDetails = pickSafeDetails(payload.details, AUTHORIZATION_REQUIRED_DETAIL_KEYS, {\n maxDepth: 4,\n maxSizeBytes: 4096,\n });\n const { details: _details, ...rest } = payload;\n return sanitizedDetails === undefined ? rest : { ...rest, details: sanitizedDetails };\n}\n\nfunction isErrorRecovery(value: unknown): value is ErrorRecovery {\n return value === 'transient' || value === 'correctable' || value === 'terminal';\n}\n\nfunction normalizeRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n return isErrorRecovery(value) ? value : isStandardErrorCode(code) ? STANDARD_ERROR_CODES[code].recovery : 'terminal';\n}\n\nfunction normalizeAllowlistedRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n if (isStandardErrorCode(code)) {\n return STANDARD_ERROR_CODES[code].recovery;\n }\n return normalizeRecoveryForCode(code, value);\n}\n"],"mappings":"AAOA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,kCAAkC;AAC3C,SAAS,uBAAuB;AAmFhC,MAAM,qCAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA8CO,SAAS,UAAU,MAAyC,SAA8C;AAC/G,QAAM,WAAW,yBAAyB,MAAM,QAAQ,QAAQ;AAEhE,QAAM,aAA+B;AAAA,IACnC;AAAA,IACA,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA,GAAI,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM;AAAA,IACpD,GAAI,QAAQ,cAAc,QAAQ,EAAE,YAAY,QAAQ,WAAW;AAAA,IACnE,GAAI,QAAQ,eAAe,QAAQ,EAAE,aAAa,QAAQ,YAAY;AAAA,IACtE,GAAI,QAAQ,UAAU,QAAQ,EAAE,QAAQ,QAAQ,OAAO;AAAA,IACvD,GAAI,QAAQ,WAAW,QAAQ,EAAE,SAAS,QAAQ,QAAQ;AAAA,EAC5D;AAEA,QAAM,WAAW,wBAAwB,MAAM,UAAgD;AAE/F,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,EAAE,YAAY,SAAS,CAAC,EAAE,CAAC;AAAA,IAC1E,SAAS;AAAA,IACT,mBAAmB,EAAE,YAAY,SAAS;AAAA,EAC5C;AACF;AAcO,SAAS,wBAAwB,MAAc,SAAoD;AACxG,YAAU,yBAAyB,MAAM,OAAO;AAChD,QAAM,YAAY,2BAA2B,IAAI;AACjD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,CAAC,UAAU,IAAI,GAAG,EAAG;AACzB,QAAI,GAAG,IAAI,QAAQ,aAAa,oCAAoC,MAAM,KAAK,IAAI;AAAA,EACrF;AACA,SAAO;AACT;AAEO,SAAS,4BAAyE,OAAa;AACpG,SAAO,wBAAwB,MAAM,MAAM,KAA2C;AACxF;AAEA,SAAS,yBAAyB,MAAc,SAA2D;AACzG,MAAI,SAAS,4BAA4B,QAAQ,YAAY,OAAW,QAAO;AAE/E,QAAM,mBAAmB,gBAAgB,QAAQ,SAAS,oCAAoC;AAAA,IAC5F,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AACD,QAAM,EAAE,SAAS,UAAU,GAAG,KAAK,IAAI;AACvC,SAAO,qBAAqB,SAAY,OAAO,EAAE,GAAG,MAAM,SAAS,iBAAiB;AACtF;AAEA,SAAS,gBAAgB,OAAwC;AAC/D,SAAO,UAAU,eAAe,UAAU,iBAAiB,UAAU;AACvE;AAEA,SAAS,yBAAyB,MAAc,OAA+B;AAC7E,SAAO,gBAAgB,KAAK,IAAI,QAAQ,oBAAoB,IAAI,IAAI,qBAAqB,IAAI,EAAE,WAAW;AAC5G;AAEA,SAAS,oCAAoC,MAAc,OAA+B;AACxF,MAAI,oBAAoB,IAAI,GAAG;AAC7B,WAAO,qBAAqB,IAAI,EAAE;AAAA,EACpC;AACA,SAAO,yBAAyB,MAAM,KAAK;AAC7C;","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/server/errors.ts"],"sourcesContent":["/**\n * Server-side helpers for producing L3-compliant AdCP error responses.\n *\n * Use `adcpError()` in MCP tool handlers to return structured errors\n * that clients can automatically detect, classify, and act on.\n */\n\nimport {\n STANDARD_ERROR_CODES,\n isStandardErrorCode,\n type StandardErrorCode,\n type ErrorRecovery,\n} from '../types/error-codes';\nimport type { ValidationIssue } from '../validation/schema-validator';\nimport { ADCP_ERROR_FIELD_ALLOWLIST } from './envelope-allowlist';\nimport { pickSafeDetails } from './pick-safe-details';\n\nexport interface AdcpErrorOptions {\n message: string;\n /**\n * Opaque request context to echo beside `adcp_error`. Only a non-null,\n * non-array object is echoable, matching the framework envelope behavior.\n */\n context?: unknown;\n /**\n * Override the recovery classification. Defaults to\n * `STANDARD_ERROR_CODES[code].recovery` for known codes, `'terminal'`\n * otherwise. Dropped from the wire shape for codes whose entry in\n * `ADCP_ERROR_FIELD_ALLOWLIST` excludes it; normalized back to the\n * standard table value for allowlisted standard-code envelopes.\n */\n recovery?: ErrorRecovery;\n /**\n * Name of the request field the error applies to (validation /\n * constraint errors). Dropped from the wire shape for codes whose\n * `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g. `IDEMPOTENCY_CONFLICT`\n * — a conflict response MUST NOT echo prior payload state).\n */\n field?: string;\n /**\n * Human-readable remediation hint. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it (e.g.\n * `IDEMPOTENCY_CONFLICT`).\n */\n suggestion?: string;\n /**\n * Seconds to wait before retrying a transient error. Only meaningful\n * on retryable codes (`RATE_LIMITED`, `SERVICE_UNAVAILABLE`); dropped\n * on terminal codes whose allowlist excludes it (`IDEMPOTENCY_CONFLICT`\n * — a computed `retry_after` on conflict would leak cached-entry age).\n */\n retry_after?: number;\n /**\n * Code-specific diagnostic payload. Dropped from the wire shape for\n * codes whose `ADCP_ERROR_FIELD_ALLOWLIST` entry excludes it\n * (`IDEMPOTENCY_CONFLICT` — a conflict response MUST NOT echo the\n * prior request payload or cached response body).\n */\n details?: Record<string, unknown>;\n /**\n * Schema validation issues surfaced at the top level of `adcp_error`\n * so operators see JSON Pointers on the first render. Primary use is\n * `VALIDATION_ERROR`; framework validation hooks populate this\n * automatically and also mirror the same array to `details.issues`\n * for buyers that already index into `details` per AdCP spec\n * convention.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorPayload {\n code: string;\n message: string;\n /**\n * Closed-enum classifier. Populated by `adcpError()` from\n * `STANDARD_ERROR_CODES[code].recovery` unless the caller provides an\n * override. Marked optional because per-code inside-`adcp_error`\n * allowlists may deliberately drop it from the wire shape — consumers\n * reading a payload parsed off the wire MUST tolerate `undefined`.\n */\n recovery?: ErrorRecovery;\n field?: string;\n suggestion?: string;\n retry_after?: number;\n details?: Record<string, unknown>;\n /**\n * Schema validation issues (`VALIDATION_ERROR`) exposed at the top\n * level so the list is the first thing a reader sees when inspecting\n * the envelope. Also mirrored at `details.issues` for spec-convention\n * compatibility.\n */\n issues?: Array<Omit<ValidationIssue, 'schemaPath'> & { schemaPath?: string }>;\n}\n\nexport interface AdcpErrorResponse {\n [key: string]: unknown;\n content: Array<{ type: 'text'; text: string }>;\n isError: true;\n structuredContent: {\n adcp_error: AdcpErrorPayload;\n context?: object;\n };\n}\n\nfunction isEchoableContext(value: unknown): value is object {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nconst AUTHORIZATION_REQUIRED_DETAIL_KEYS = [\n 'required_connections',\n 'missing_connections',\n 'provider',\n 'connection_type',\n 'required_for',\n 'scope',\n 'status',\n 'resource_ref',\n 'platform_account_id',\n 'identity_id',\n 'handle',\n 'profile_url',\n 'post_id',\n 'post_url',\n 'authorization_url',\n 'authorization_instructions',\n 'checked_at',\n 'expires_at',\n 'reference_authorization',\n] as const;\n\n/**\n * Build an L3-compliant MCP tool error response with all three transport layers:\n *\n * 1. `structuredContent.adcp_error` — programmatic extraction (L3)\n * 2. `content[0].text` — JSON text fallback (L2)\n * 3. `isError: true` — MCP error signal\n *\n * Recovery is auto-populated from the standard error code table when not provided.\n *\n * Before returning, any field NOT allowlisted for the given code in\n * {@link ADCP_ERROR_FIELD_ALLOWLIST} is dropped — sellers get the builder's\n * ergonomics for every code AND the strict wire shape for codes that have\n * a registered allowlist. `IDEMPOTENCY_CONFLICT` is the canonical case:\n * payload-shaped diagnostics like `field`, `suggestion`, and `details`\n * silently drop while standard `recovery` metadata is preserved as the\n * canonical standard-table value. Codes without a registered allowlist\n * pass through unchanged.\n *\n * **Two-layer wire shape.** This builder emits the envelope layer\n * (`structuredContent.adcp_error`) only. For tools whose response\n * schema declares a typed Error arm (`errors[]` required at the top\n * level), the framework dispatcher synthesises the payload-layer\n * `errors[]` from the same data at finalize time, so the wire carries\n * both the envelope marker and the typed Error arm together — no\n * adopter code change required. The list of affected tools is derived\n * at server build from the bundled schema cache. RFC:\n * `docs/proposals/adcperror-two-layer-emission.md`.\n *\n * @example\n * ```typescript\n * import { adcpError } from '@adcp/sdk';\n *\n * server.registerTool(\"get_products\", { inputSchema: schema }, async request => {\n * const { query, context } = request;\n * if (!products.length) {\n * return adcpError('PRODUCT_NOT_FOUND', {\n * message: 'No products match query',\n * field: 'query',\n * suggestion: 'Try a broader search term',\n * context,\n * });\n * }\n * return { content: [...], structuredContent: { products, context } };\n * });\n * ```\n */\nexport function adcpError(code: StandardErrorCode | (string & {}), options: AdcpErrorOptions): AdcpErrorResponse {\n const recovery = normalizeRecoveryForCode(code, options.recovery);\n\n const adcp_error: AdcpErrorPayload = {\n code,\n message: options.message,\n recovery,\n ...(options.field != null && { field: options.field }),\n ...(options.suggestion != null && { suggestion: options.suggestion }),\n ...(options.retry_after != null && { retry_after: options.retry_after }),\n ...(options.issues != null && { issues: options.issues }),\n ...(options.details != null && { details: options.details }),\n };\n\n const filtered = applyAdcpErrorAllowlist(code, adcp_error as unknown as Record<string, unknown>);\n const structuredContent = {\n adcp_error: filtered,\n ...(isEchoableContext(options.context) ? { context: options.context } : {}),\n };\n\n return {\n content: [{ type: 'text', text: JSON.stringify(structuredContent) }],\n isError: true,\n structuredContent,\n };\n}\n\n/**\n * Drop every field not in {@link ADCP_ERROR_FIELD_ALLOWLIST} for `code`.\n * When an allowlisted standard code carries `recovery`, normalize it to\n * the fixed classifier from `STANDARD_ERROR_CODES` instead of trusting a\n * caller-supplied value.\n * Codes without an entry pass through unchanged — the allowlist is\n * opt-in per code, not a global filter. The returned object is re-typed\n * as `AdcpErrorPayload` on the assumption that `code` and `message`\n * (the only required fields) are in every registered allowlist; that\n * invariant is re-asserted at runtime by the module-load check in\n * `envelope-allowlist.ts`.\n */\nexport function applyAdcpErrorAllowlist(code: string, payload: Record<string, unknown>): AdcpErrorPayload {\n payload = sanitizeAdcpErrorDetails(code, payload);\n const allowlist = ADCP_ERROR_FIELD_ALLOWLIST[code];\n if (!allowlist) return payload as unknown as AdcpErrorPayload;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(payload)) {\n if (!allowlist.has(key)) continue;\n out[key] = key === 'recovery' ? normalizeAllowlistedRecoveryForCode(code, value) : value;\n }\n return out as unknown as AdcpErrorPayload;\n}\n\nexport function sanitizeStructuredAdcpError<T extends { code: string; message: string }>(error: T): T {\n return applyAdcpErrorAllowlist(error.code, error as unknown as Record<string, unknown>) as unknown as T;\n}\n\nfunction sanitizeAdcpErrorDetails(code: string, payload: Record<string, unknown>): Record<string, unknown> {\n if (code !== 'AUTHORIZATION_REQUIRED' || payload.details === undefined) return payload;\n\n const sanitizedDetails = pickSafeDetails(payload.details, AUTHORIZATION_REQUIRED_DETAIL_KEYS, {\n maxDepth: 4,\n maxSizeBytes: 4096,\n });\n const { details: _details, ...rest } = payload;\n return sanitizedDetails === undefined ? rest : { ...rest, details: sanitizedDetails };\n}\n\nfunction isErrorRecovery(value: unknown): value is ErrorRecovery {\n return value === 'transient' || value === 'correctable' || value === 'terminal';\n}\n\nfunction normalizeRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n return isErrorRecovery(value) ? value : isStandardErrorCode(code) ? STANDARD_ERROR_CODES[code].recovery : 'terminal';\n}\n\nfunction normalizeAllowlistedRecoveryForCode(code: string, value: unknown): ErrorRecovery {\n if (isStandardErrorCode(code)) {\n return STANDARD_ERROR_CODES[code].recovery;\n }\n return normalizeRecoveryForCode(code, value);\n}\n"],"mappings":"AAOA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,kCAAkC;AAC3C,SAAS,uBAAuB;AAyFhC,SAAS,kBAAkB,OAAiC;AAC1D,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,MAAM,qCAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAgDO,SAAS,UAAU,MAAyC,SAA8C;AAC/G,QAAM,WAAW,yBAAyB,MAAM,QAAQ,QAAQ;AAEhE,QAAM,aAA+B;AAAA,IACnC;AAAA,IACA,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA,GAAI,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM;AAAA,IACpD,GAAI,QAAQ,cAAc,QAAQ,EAAE,YAAY,QAAQ,WAAW;AAAA,IACnE,GAAI,QAAQ,eAAe,QAAQ,EAAE,aAAa,QAAQ,YAAY;AAAA,IACtE,GAAI,QAAQ,UAAU,QAAQ,EAAE,QAAQ,QAAQ,OAAO;AAAA,IACvD,GAAI,QAAQ,WAAW,QAAQ,EAAE,SAAS,QAAQ,QAAQ;AAAA,EAC5D;AAEA,QAAM,WAAW,wBAAwB,MAAM,UAAgD;AAC/F,QAAM,oBAAoB;AAAA,IACxB,YAAY;AAAA,IACZ,GAAI,kBAAkB,QAAQ,OAAO,IAAI,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,EAC3E;AAEA,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,iBAAiB,EAAE,CAAC;AAAA,IACnE,SAAS;AAAA,IACT;AAAA,EACF;AACF;AAcO,SAAS,wBAAwB,MAAc,SAAoD;AACxG,YAAU,yBAAyB,MAAM,OAAO;AAChD,QAAM,YAAY,2BAA2B,IAAI;AACjD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,CAAC,UAAU,IAAI,GAAG,EAAG;AACzB,QAAI,GAAG,IAAI,QAAQ,aAAa,oCAAoC,MAAM,KAAK,IAAI;AAAA,EACrF;AACA,SAAO;AACT;AAEO,SAAS,4BAAyE,OAAa;AACpG,SAAO,wBAAwB,MAAM,MAAM,KAA2C;AACxF;AAEA,SAAS,yBAAyB,MAAc,SAA2D;AACzG,MAAI,SAAS,4BAA4B,QAAQ,YAAY,OAAW,QAAO;AAE/E,QAAM,mBAAmB,gBAAgB,QAAQ,SAAS,oCAAoC;AAAA,IAC5F,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AACD,QAAM,EAAE,SAAS,UAAU,GAAG,KAAK,IAAI;AACvC,SAAO,qBAAqB,SAAY,OAAO,EAAE,GAAG,MAAM,SAAS,iBAAiB;AACtF;AAEA,SAAS,gBAAgB,OAAwC;AAC/D,SAAO,UAAU,eAAe,UAAU,iBAAiB,UAAU;AACvE;AAEA,SAAS,yBAAyB,MAAc,OAA+B;AAC7E,SAAO,gBAAgB,KAAK,IAAI,QAAQ,oBAAoB,IAAI,IAAI,qBAAqB,IAAI,EAAE,WAAW;AAC5G;AAEA,SAAS,oCAAoC,MAAc,OAA+B;AACxF,MAAI,oBAAoB,IAAI,GAAG;AAC7B,WAAO,qBAAqB,IAAI,EAAE;AAAA,EACpC;AACA,SAAO,yBAAyB,MAAM,KAAK;AAC7C;","names":[]}
@@ -59,6 +59,12 @@ export interface ComplyOptions extends TestOptions {
59
59
  * `runStoryboard`. See `StoryboardRunOptions.contracts`.
60
60
  */
61
61
  contracts?: StoryboardRunOptions['contracts'];
62
+ /**
63
+ * Explicitly authorize the `expect_rate_limit_not_replayed` storyboard
64
+ * probe. Independent from `request_signing.allowLiveSideEffects`. Passed
65
+ * through to `runStoryboard`; default false.
66
+ */
67
+ allowLiveSideEffects?: StoryboardRunOptions['allowLiveSideEffects'];
62
68
  /** Explicit compliance cache version override. */
63
69
  version?: string;
64
70
  /** Explicit compliance cache directory override. */
@@ -59,6 +59,12 @@ export interface ComplyOptions extends TestOptions {
59
59
  * `runStoryboard`. See `StoryboardRunOptions.contracts`.
60
60
  */
61
61
  contracts?: StoryboardRunOptions['contracts'];
62
+ /**
63
+ * Explicitly authorize the `expect_rate_limit_not_replayed` storyboard
64
+ * probe. Independent from `request_signing.allowLiveSideEffects`. Passed
65
+ * through to `runStoryboard`; default false.
66
+ */
67
+ allowLiveSideEffects?: StoryboardRunOptions['allowLiveSideEffects'];
62
68
  /** Explicit compliance cache version override. */
63
69
  version?: string;
64
70
  /** Explicit compliance cache directory override. */
@@ -1 +1 @@
1
- {"version":3,"file":"comply.d.ts","sourceRoot":"","sources":["../../../../src/lib/testing/compliance/comply.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAkB,MAAM,UAAU,CAAC;AAuBtF,OAAO,KAAK,EAGV,UAAU,EAEV,gBAAgB,EAChB,oBAAoB,EAGrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAEV,eAAe,EACf,iBAAiB,EAGjB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AA0C3D;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,UAAU,EAAE,EACrB,OAAO,EAAE,YAAY,GACpB,mBAAmB,EAAE,CAuavB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;IAC5D;;;OAGG;IACH,uBAAuB,CAAC,EAAE,oBAAoB,CAAC,yBAAyB,CAAC,CAAC;IAC1E,+DAA+D;IAC/D,mCAAmC,CAAC,EAAE,oBAAoB,CAAC,qCAAqC,CAAC,CAAC;IAClG;;;OAGG;IACH,SAAS,CAAC,EAAE,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAC9C,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0BrG;AA2FD,wBAAgB,uCAAuC,CACrD,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE;IACN,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;CAC7C,GACA,WAAW,CAuBb;AAuOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,WAAW,EAAE,UAAU,EAAE,EACzB,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE;IACV,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACL,iBAAiB,EAAE,CA6DrB;AA0cD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,mBAAmB,EAAE,CAAA;CAAE,CAAC,CAsFnE;AAoLD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,GAAG,aAAa,CAe9E;AAmJD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAqKxE;AAcD;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAa5E"}
1
+ {"version":3,"file":"comply.d.ts","sourceRoot":"","sources":["../../../../src/lib/testing/compliance/comply.ts"],"names":[],"mappings":"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"}
@@ -732,6 +732,7 @@ async function complyImpl(agentUrl, options) {
732
732
  webhook_replay_receiver,
733
733
  trusted_match_context_router_runner,
734
734
  contracts,
735
+ allowLiveSideEffects,
735
736
  version,
736
737
  complianceDir,
737
738
  schemaRoot,
@@ -894,6 +895,7 @@ async function complyImpl(agentUrl, options) {
894
895
  trusted_match_context_router_runner: complianceDir !== void 0 && trusted_match_context_router_runner.vectorsRoot === void 0 ? { ...trusted_match_context_router_runner, vectorsRoot: complianceDir } : trusted_match_context_router_runner
895
896
  },
896
897
  ...contracts !== void 0 && { contracts },
898
+ ...allowLiveSideEffects !== void 0 && { allowLiveSideEffects },
897
899
  ...signal !== void 0 && { signal }
898
900
  };
899
901
  let stoppedForTimeoutBudget = false;
@@ -1087,6 +1089,7 @@ async function runWithDegradedProfile(agentUrl, profile, storyboards, options, e
1087
1089
  trusted_match_context_router_runner: options.complianceDir !== void 0 && options.trusted_match_context_router_runner.vectorsRoot === void 0 ? { ...options.trusted_match_context_router_runner, vectorsRoot: options.complianceDir } : options.trusted_match_context_router_runner
1088
1090
  },
1089
1091
  ...options.contracts !== void 0 && { contracts: options.contracts },
1092
+ ...options.allowLiveSideEffects !== void 0 && { allowLiveSideEffects: options.allowLiveSideEffects },
1090
1093
  ...signal !== void 0 && { signal }
1091
1094
  };
1092
1095
  let stoppedForTimeoutBudget = false;