@zuplo/cli 7.3.2 → 7.4.3

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 (105) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cmds/lint.d.ts +9 -0
  4. package/dist/cmds/lint.d.ts.map +1 -0
  5. package/dist/cmds/lint.js +67 -0
  6. package/dist/cmds/lint.js.map +1 -0
  7. package/dist/lint/handler.d.ts +10 -0
  8. package/dist/lint/handler.d.ts.map +1 -0
  9. package/dist/lint/handler.js +60 -0
  10. package/dist/lint/handler.js.map +1 -0
  11. package/dist/lint/resolve-binary.d.ts +20 -0
  12. package/dist/lint/resolve-binary.d.ts.map +1 -0
  13. package/dist/lint/resolve-binary.js +71 -0
  14. package/dist/lint/resolve-binary.js.map +1 -0
  15. package/dist/lint/resolve-binary.test.d.ts +2 -0
  16. package/dist/lint/resolve-binary.test.d.ts.map +1 -0
  17. package/dist/lint/resolve-binary.test.js +155 -0
  18. package/dist/lint/resolve-binary.test.js.map +1 -0
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/node_modules/@inquirer/checkbox/package.json +5 -4
  21. package/node_modules/@inquirer/confirm/dist/index.d.ts +1 -1
  22. package/node_modules/@inquirer/confirm/package.json +4 -3
  23. package/node_modules/@inquirer/core/README.md +9 -3
  24. package/node_modules/@inquirer/core/dist/lib/hook-engine.d.ts +2 -8
  25. package/node_modules/@inquirer/core/dist/lib/use-memo.js +1 -1
  26. package/node_modules/@inquirer/core/dist/lib/use-state.d.ts +5 -2
  27. package/node_modules/@inquirer/core/dist/lib/use-state.js +7 -2
  28. package/node_modules/@inquirer/core/package.json +5 -4
  29. package/node_modules/@inquirer/editor/dist/index.d.ts +1 -1
  30. package/node_modules/@inquirer/editor/package.json +5 -4
  31. package/node_modules/@inquirer/expand/README.md +1 -1
  32. package/node_modules/@inquirer/expand/package.json +4 -3
  33. package/node_modules/@inquirer/external-editor/README.md +13 -0
  34. package/node_modules/@inquirer/external-editor/package.json +4 -3
  35. package/node_modules/@inquirer/figures/package.json +4 -3
  36. package/node_modules/@inquirer/input/package.json +4 -3
  37. package/node_modules/@inquirer/number/dist/index.d.ts +1 -1
  38. package/node_modules/@inquirer/number/dist/index.js +1 -6
  39. package/node_modules/@inquirer/number/dist/is-step-of.d.ts +1 -0
  40. package/node_modules/@inquirer/number/dist/is-step-of.js +22 -0
  41. package/node_modules/@inquirer/number/package.json +4 -3
  42. package/node_modules/@inquirer/password/package.json +4 -3
  43. package/node_modules/@inquirer/rawlist/package.json +4 -3
  44. package/node_modules/@inquirer/search/README.md +9 -8
  45. package/node_modules/@inquirer/search/dist/index.d.ts +1 -0
  46. package/node_modules/@inquirer/search/dist/index.js +6 -1
  47. package/node_modules/@inquirer/search/package.json +5 -4
  48. package/node_modules/@inquirer/select/package.json +5 -4
  49. package/node_modules/@posthog/core/dist/error-tracking/coercers/error-coercer.d.ts.map +1 -1
  50. package/node_modules/@posthog/core/dist/error-tracking/coercers/error-coercer.js +4 -2
  51. package/node_modules/@posthog/core/dist/error-tracking/coercers/error-coercer.mjs +4 -2
  52. package/node_modules/@posthog/core/dist/error-tracking/coercers/error-event-coercer.d.ts.map +1 -1
  53. package/node_modules/@posthog/core/dist/error-tracking/coercers/error-event-coercer.js +5 -5
  54. package/node_modules/@posthog/core/dist/error-tracking/coercers/error-event-coercer.mjs +5 -5
  55. package/node_modules/@posthog/core/dist/error-tracking/parsers/base.d.ts.map +1 -1
  56. package/node_modules/@posthog/core/dist/error-tracking/parsers/base.js +2 -1
  57. package/node_modules/@posthog/core/dist/error-tracking/parsers/base.mjs +2 -1
  58. package/node_modules/@posthog/core/dist/posthog-core.d.ts.map +1 -1
  59. package/node_modules/@posthog/core/dist/posthog-core.js +1 -0
  60. package/node_modules/@posthog/core/dist/posthog-core.mjs +1 -0
  61. package/node_modules/@posthog/core/package.json +1 -1
  62. package/node_modules/@posthog/core/src/error-tracking/coercers/error-coercer.ts +8 -2
  63. package/node_modules/@posthog/core/src/error-tracking/coercers/error-event-coercer.spec.ts +33 -0
  64. package/node_modules/@posthog/core/src/error-tracking/coercers/error-event-coercer.ts +13 -6
  65. package/node_modules/@posthog/core/src/error-tracking/error-properties-builder.coerce.spec.ts +15 -0
  66. package/node_modules/@posthog/core/src/error-tracking/parsers/base.spec.ts +34 -0
  67. package/node_modules/@posthog/core/src/error-tracking/parsers/base.ts +10 -2
  68. package/node_modules/@posthog/core/src/posthog-core.ts +1 -0
  69. package/node_modules/@zuplo/core/customer.cli.minified.js +239 -239
  70. package/node_modules/@zuplo/core/index.minified.js +253 -253
  71. package/node_modules/@zuplo/core/package.json +1 -1
  72. package/node_modules/@zuplo/graphql/package.json +1 -1
  73. package/node_modules/@zuplo/openapi-tools/package.json +1 -1
  74. package/node_modules/@zuplo/otel/package.json +1 -1
  75. package/node_modules/@zuplo/runtime/out/esm/chunk-NHSMO7UN.js +409 -0
  76. package/node_modules/@zuplo/runtime/out/esm/chunk-NHSMO7UN.js.map +1 -0
  77. package/node_modules/@zuplo/runtime/out/esm/index.js +1 -1
  78. package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js +1 -1
  79. package/node_modules/@zuplo/runtime/out/types/index.d.ts +108 -69
  80. package/node_modules/@zuplo/runtime/package.json +1 -1
  81. package/node_modules/negotiator/lib/accept.js +54 -0
  82. package/node_modules/negotiator/lib/charset.js +10 -34
  83. package/node_modules/negotiator/lib/encoding.js +9 -30
  84. package/node_modules/negotiator/lib/language.js +15 -36
  85. package/node_modules/negotiator/lib/mediaType.js +16 -134
  86. package/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  87. package/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  88. package/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  89. package/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  90. package/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  91. package/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  92. package/node_modules/negotiator/package.json +16 -10
  93. package/package.json +13 -6
  94. package/node_modules/@inquirer/external-editor/dist/errors/CreateFileError.d.ts +0 -10
  95. package/node_modules/@inquirer/external-editor/dist/errors/CreateFileError.js +0 -13
  96. package/node_modules/@inquirer/external-editor/dist/errors/LaunchEditorError.d.ts +0 -10
  97. package/node_modules/@inquirer/external-editor/dist/errors/LaunchEditorError.js +0 -13
  98. package/node_modules/@inquirer/external-editor/dist/errors/ReadFileError.d.ts +0 -10
  99. package/node_modules/@inquirer/external-editor/dist/errors/ReadFileError.js +0 -13
  100. package/node_modules/@inquirer/external-editor/dist/errors/RemoveFileError.d.ts +0 -10
  101. package/node_modules/@inquirer/external-editor/dist/errors/RemoveFileError.js +0 -13
  102. package/node_modules/@zuplo/runtime/out/esm/chunk-YOMMW6SX.js +0 -409
  103. package/node_modules/@zuplo/runtime/out/esm/chunk-YOMMW6SX.js.map +0 -1
  104. package/node_modules/negotiator/HISTORY.md +0 -114
  105. /package/node_modules/@zuplo/runtime/out/esm/{chunk-YOMMW6SX.js.LEGAL.txt → chunk-NHSMO7UN.js.LEGAL.txt} +0 -0
@@ -345,8 +345,9 @@ export declare interface AIGatewayConfigurationLoaderV2InboundPolicyOptions {
345
345
  * identifiers, API keys and other credentials) using the in-gateway DLP
346
346
  * engine, and applies per-rule actions: mask, block, or log. Detection runs
347
347
  * entirely inside the gateway; nothing is sent to a third-party scanning
348
- * service. Streaming responses are scanned as they stream, chunk by chunk
349
- * with a sliding boundary window, rather than being buffered to the end.
348
+ * service. Streaming responses are scanned as they stream by default (chunk
349
+ * by chunk with a sliding boundary window); `streaming.mode: "buffer"`
350
+ * instead collects the whole response and scans it once as one document.
350
351
  *
351
352
  * Options are validated at request time and invalid options fail closed with
352
353
  * a ConfigurationError. Content that cannot be inspected is denied by
@@ -367,13 +368,13 @@ export declare function AIGatewayDlpInboundPolicy(
367
368
  ): Promise<ZuploRequest | Response>;
368
369
 
369
370
  /**
370
- * The options for the Data Loss Prevention (DLP) policy. Scans the content of AI requests and responses — system prompts, messages, and tool-call arguments — for sensitive data such as credit cards, national identifiers, and API keys, applying a per-rule action (mask, block, or log). Detection runs entirely inside the gateway and streaming responses are scanned as they stream.
371
+ * The options for the Data Loss Prevention (DLP) policy. Scans the content of AI requests and responses — system prompts, messages, and tool-call arguments — for sensitive data such as credit cards, national identifiers, and API keys, applying a per-rule action (mask, block, or log). Detection runs entirely inside the gateway; streaming responses are scanned as they stream (or collected and scanned whole with `streaming.mode: "buffer"`).
371
372
  * @public
372
373
  */
373
374
  export declare interface AIGatewayDlpInboundPolicyOptions {
374
375
  rules: DlpRules;
375
376
  /**
376
- * Your own detection rules alongside the built-ins: the same rule object plus a `name`, a regex `pattern`, and optional `context` words.
377
+ * Your own detectors: a `name`, a regex `pattern`, and optional `context` words. A detector does nothing by itself — activate it by name inside `rules.inbound` or `rules.outbound`, exactly like a built-in type, with its own action, detection level, and mask per direction.
377
378
  */
378
379
  customRules?: DlpCustomRule[];
379
380
  /**
@@ -4861,7 +4862,7 @@ declare interface DlpCustomPattern_2 {
4861
4862
 
4862
4863
  declare interface DlpCustomRule {
4863
4864
  /**
4864
- * Identifier reported in findings and block details for this rule.
4865
+ * The detector's name — the key you use to activate it inside `rules.inbound` or `rules.outbound`, exactly like a built-in type id. Must be distinct and must not reuse a built-in type id or group selector.
4865
4866
  */
4866
4867
  name: string;
4867
4868
  /**
@@ -4869,68 +4870,16 @@ declare interface DlpCustomRule {
4869
4870
  */
4870
4871
  pattern: string;
4871
4872
  /**
4872
- * Context words for this rule. With `detection: "corroborated"` the rule only fires when one of these words appears near the match.
4873
+ * Context words for this detector. An activation with `detection: "corroborated"` then only fires when one of these words appears near the match.
4873
4874
  */
4874
4875
  context?: string[];
4875
- /**
4876
- * What happens when this rule matches, in BOTH directions. `mask` replaces the matched text with a placeholder and the conversation continues; `block` rejects the request or terminates the response with an error naming only the rule's name (never the values); `log` records a finding and lets the traffic pass unchanged; `off` disables the rule. A rule states either `action` or the directional pair (`inboundAction`/`outboundAction`), never both.
4877
- */
4878
- action?: "mask" | "block" | "log" | "off";
4879
- /**
4880
- * What happens when this rule matches in what callers send the model. Use with `outboundAction` to give the rule different behavior per direction. Omitting it (or `off`) leaves the inbound direction unscanned for this rule. Cannot be combined with `action`.
4881
- */
4882
- inboundAction?: "mask" | "block" | "log" | "off";
4883
- /**
4884
- * What happens when this rule matches in what the model sends back. Omitting it (or `off`) leaves the outbound direction unscanned for this rule. Cannot be combined with `action`.
4885
- */
4886
- outboundAction?: "mask" | "block" | "log" | "off";
4887
- /**
4888
- * How much evidence a match needs before this rule acts. `corroborated` additionally requires one of the type's context words near the match (types that define no context words behave as `standard`); `standard` uses the catalog as tuned — distinctive shapes act on their own while ambiguous shapes need nearby context; `aggressive` counts every pattern match and is false-positive-prone — pair it with the `log` action for audits. Overrides the policy-level `detection` for this rule.
4889
- */
4890
- detection?: "corroborated" | "standard" | "aggressive";
4891
- /**
4892
- * Replacement text for this rule's masked matches. The `{type}` token inserts the detected type id in UPPER_SNAKE form, so the default turns a matched email into `[CONTACT_EMAIL]` — typed placeholders keep masked conversations legible to the model. Set a literal like `[REDACTED]` to hide the type.
4893
- */
4894
- mask?: string;
4895
- partialMask?: DlpPartialMask;
4896
- }
4897
-
4898
- declare interface DlpPartialMask {
4899
- /**
4900
- * Keep the last N characters of the matched value unmasked and replace the rest with `*`, preserving length (for example `**** **** **** 4242`). Values no longer than N characters are fully masked.
4901
- */
4902
- keepLast: number;
4903
- }
4904
-
4905
- declare interface DlpRule {
4906
- /**
4907
- * What happens when this type is found, in BOTH directions. `mask` replaces the matched text with a placeholder and the conversation continues; `block` rejects the request or terminates the response with an error naming only the detected types (never the values); `log` records a finding and lets the traffic pass unchanged — use it to trial a rule before enforcing it; `off` excludes this type from a group enabled by a broader key. A rule states either `action` or the directional pair (`inboundAction`/`outboundAction`), never both.
4908
- */
4909
- action?: "mask" | "block" | "log" | "off";
4910
- /**
4911
- * What happens when this type is found in what callers send the model (system prompt, message text, and tool-call arguments). Use with `outboundAction` to give one type different behavior per direction — for example mask PII on the way in and block it on the way out. Omitting it (or `off`) leaves the inbound direction unscanned for this type. Cannot be combined with `action`.
4912
- */
4913
- inboundAction?: "mask" | "block" | "log" | "off";
4914
- /**
4915
- * What happens when this type is found in what the model sends back (buffered responses whole, streaming responses as they stream). Omitting it (or `off`) leaves the outbound direction unscanned for this type. Cannot be combined with `action`.
4916
- */
4917
- outboundAction?: "mask" | "block" | "log" | "off";
4918
- /**
4919
- * How much evidence a match needs before this rule acts. `corroborated` additionally requires one of the type's context words near the match (types that define no context words behave as `standard`); `standard` uses the catalog as tuned — distinctive shapes act on their own while ambiguous shapes need nearby context; `aggressive` counts every pattern match and is false-positive-prone — pair it with the `log` action for audits. Overrides the policy-level `detection` for this rule.
4920
- */
4921
- detection?: "corroborated" | "standard" | "aggressive";
4922
- /**
4923
- * Replacement text for this rule's masked matches. The `{type}` token inserts the detected type id in UPPER_SNAKE form, so the default turns a matched email into `[CONTACT_EMAIL]` — typed placeholders keep masked conversations legible to the model. Set a literal like `[REDACTED]` to hide the type.
4924
- */
4925
- mask?: string;
4926
- partialMask?: DlpPartialMask;
4927
4876
  }
4928
4877
 
4929
4878
  /**
4930
- * What to find and what to do about it. Each key is a built-in data type id (like `id-us-ssn`) or a group selector (like `pii` or `secret`, plus any dash-aligned id prefix such as `secret-aws`); each value is a rule object stating either one `action` for both directions or a directional pair (`inboundAction`/`outboundAction`) — for example mask PII inbound and block it outbound. Types you do not mention are not scanned. The most specific key wins — an exact id beats a prefix beats a group — and the winning entry decides everything for its type. When one stretch of text matches multiple rules, the severest action wins (block > mask > log). At least one rule must scan something.
4879
+ * What to find in this direction and what to do about it. Each key is a built-in data type id (like `id-us-ssn`), a group selector (like `pii` or `secret`, plus any dash-aligned id prefix such as `secret-aws`), or one of your custom detectors by name; each value is a rule object whose required `action` says what happens on a match. Types you do not mention are not scanned in this direction. The most specific key wins — an exact id beats a prefix beats a group — and the winning entry decides everything for its type in this direction. When one stretch of text matches multiple rules, the severest action wins (block > mask > log).
4931
4880
  * @public
4932
4881
  */
4933
- declare interface DlpRules {
4882
+ declare interface DlpDirectionRules {
4934
4883
  contact?: DlpRule;
4935
4884
  finance?: DlpRule;
4936
4885
  "finance-us"?: DlpRule;
@@ -5014,23 +4963,56 @@ declare interface DlpRules {
5014
4963
  "secret-terraform"?: DlpRule;
5015
4964
  "secret-twilio"?: DlpRule;
5016
4965
  "secret-zuplo"?: DlpRule;
4966
+ [k: string]: unknown;
4967
+ }
4968
+
4969
+ declare interface DlpPartialMask {
4970
+ /**
4971
+ * Keep the last N characters of the matched value unmasked and replace the rest with `*`, preserving length (for example `**** **** **** 4242`). Values no longer than N characters are fully masked.
4972
+ */
4973
+ keepLast: number;
4974
+ }
4975
+
4976
+ declare interface DlpRule {
4977
+ /**
4978
+ * What happens when this rule matches in this direction. `mask` replaces the matched text with a placeholder and the conversation continues; `block` rejects the request or terminates the response with an error naming only the detected types (never the values); `log` records a finding and lets the traffic pass unchanged — use it to trial a rule before enforcing it; `off` excludes this type from a group enabled by a broader key in the same section.
4979
+ */
4980
+ action: "mask" | "block" | "log" | "off";
4981
+ /**
4982
+ * How much evidence a match needs before this rule acts. `corroborated` additionally requires one of the type's context words near the match (types that define no context words behave as `standard`); `standard` uses the catalog as tuned — distinctive shapes act on their own while ambiguous shapes need nearby context; `aggressive` counts every pattern match and is false-positive-prone — pair it with the `log` action for audits. Overrides the policy-level `detection` for this rule.
4983
+ */
4984
+ detection?: "corroborated" | "standard" | "aggressive";
4985
+ /**
4986
+ * Replacement text for this rule's masked matches. The `{type}` token inserts the detected type id in UPPER_SNAKE form, so the default turns a matched email into `[CONTACT_EMAIL]` — typed placeholders keep masked conversations legible to the model. Set a literal like `[REDACTED]` to hide the type.
4987
+ */
4988
+ mask?: string;
4989
+ partialMask?: DlpPartialMask;
5017
4990
  }
5018
4991
 
5019
4992
  /**
5020
- * Tuning for streaming (SSE) response scanning. Each chunk is held until the boundary scan that includes its successor passes, then released so values split across chunks are always fully maskable, at the cost of one chunk of latency.
4993
+ * The scan configuration, split by direction: `inbound` covers what callers send the model (system prompt, message text, and tool-call arguments); `outbound` covers what the model sends back (buffered responses whole, streaming responses as they stream). Each direction you include is scanned; each you omit is left untouched there is no "both" shorthand and no mirroring, so wanting the same behavior on both sides means stating it in both sections. At least one section must enable at least one rule.
4994
+ * @public
4995
+ */
4996
+ declare interface DlpRules {
4997
+ inbound?: DlpDirectionRules;
4998
+ outbound?: DlpDirectionRules;
4999
+ }
5000
+
5001
+ /**
5002
+ * How streaming (SSE) responses are inspected. `stream` (the default) scans deltas as they flow, holding each chunk until the boundary scan that includes its successor passes — values split across chunks stay maskable at the cost of one chunk of latency. `buffer` collects the entire response and scans it once as one document — exact detection for values of any length, and a blocked response is a clean 400 because nothing has been delivered yet — at the cost of the client waiting for the full generation. `off` passes streaming responses through unscanned.
5021
5003
  * @public
5022
5004
  */
5023
5005
  declare interface DlpStreamingSettings {
5024
5006
  /**
5025
- * Scan streaming responses. When false, streaming responses pass through unscanned even when rules cover the outbound direction.
5007
+ * `stream` scans deltas in flight with the windowed scanner. `buffer` holds the entire response and scans it once, exactly like a non-streaming response: values of any length are caught (no carry window) and a block is a native 400 error instead of a mid-stream termination, but the client receives nothing until the model finishes. `off` skips scanning of streaming responses entirely, even when rules cover the outbound direction.
5026
5008
  */
5027
- enabled?: boolean;
5009
+ mode?: "stream" | "buffer" | "off";
5028
5010
  /**
5029
- * Scan across chunk boundaries using a carried tail of already-scanned text. When false, each chunk is scanned alone and released immediately — lower cost, but values split across two chunks are missed.
5011
+ * Scan across chunk boundaries using a carried tail of already-scanned text. When false, each chunk is scanned alone and released immediately — lower cost, but values split across two chunks are missed. Applies to `stream` mode only.
5030
5012
  */
5031
5013
  boundaryScan?: boolean;
5032
5014
  /**
5033
- * How many trailing characters are carried from one chunk into the next boundary scan. Bounds memory and re-scan cost; must be larger than the longest value you need to catch.
5015
+ * How many trailing characters are carried from one chunk into the next boundary scan. Bounds memory and re-scan cost; must be larger than the longest value you need to catch. Applies to `stream` mode only — `buffer` mode always scans the complete text.
5034
5016
  */
5035
5017
  maxCarryChars?: number;
5036
5018
  }
@@ -15679,12 +15661,69 @@ export declare class ZuploMcpSdk {
15679
15661
  */
15680
15662
  getRawCallToolRequest(): CallToolRequest | null;
15681
15663
  /**
15682
- * Sets the MCP request/response context object.
15683
- * Use this to set `_meta` that should be included in the tool result.
15664
+ * Overrides fields of the MCP tool result the gateway is about to send.
15665
+ *
15666
+ * Every field is independently optional; an absent field keeps the value
15667
+ * the gateway derives from the downstream response. The most useful
15668
+ * combination is `content` alone: the human-readable summary replaces the
15669
+ * raw serialized body while `structuredContent` is still auto-derived from
15670
+ * it — one call, compact model context, full data preserved.
15671
+ *
15672
+ * @example A compact summary for the model, full JSON in structuredContent
15673
+ * ```ts
15674
+ * const response = await context.invokeRoute("/todos");
15675
+ * new ZuploMcpSdk(context).setRawCallToolResult({
15676
+ * content: [{ type: "text", text: "Fetched the todo list" }],
15677
+ * });
15678
+ * return response;
15679
+ * ```
15680
+ *
15681
+ * Notes:
15682
+ *
15683
+ * - The stored value is *consumed* when the gateway builds the tool result
15684
+ * (asymmetric with {@link getRawCallToolRequest}, which can be read any
15685
+ * number of times).
15686
+ * - The value is written to the context that carried the matching
15687
+ * `tools/call` request, which is looked up exactly one `invokeRoute`
15688
+ * generation up. Calling this from a grandchild invocation (a route that
15689
+ * itself invokes another route) silently stores the override where the
15690
+ * gateway never reads it.
15691
+ * - `isError` is deliberately not overridable: it follows the status of the
15692
+ * response your handler returns, so upstream failures cannot be masked.
15693
+ * - `structuredContent` must be a JSON object. The MCP 2025-11-25 wire
15694
+ * format rejects arrays and scalars here.
15695
+ * - Declining the spec's backwards-compatibility SHOULD (mirroring
15696
+ * serialized JSON into a text block) has a concrete cost: clients that
15697
+ * predate `structuredContent` see only your summary in `content`.
15698
+ * - MCP reserves `_meta` keys with the `modelcontextprotocol.io/` prefix
15699
+ * for the protocol itself; use your own prefix for application metadata.
15684
15700
  *
15685
- * @param req - The object containing metadata (e.g. `{ _meta: { ... } }`)
15701
+ * @param result - The fields to override on the tool result
15702
+ */
15703
+ setRawCallToolResult(result: ZuploMcpToolResultOverride): void;
15704
+ }
15705
+
15706
+ /**
15707
+ * The subset of a `tools/call` result a module author may override via
15708
+ * {@link ZuploMcpSdk.setRawCallToolResult}. Every field is independently
15709
+ * optional; an absent field keeps the value the gateway derives from the
15710
+ * downstream response.
15711
+ */
15712
+ export declare interface ZuploMcpToolResultOverride {
15713
+ /**
15714
+ * Content blocks for the model to read. Replaces the gateway's default
15715
+ * (the serialized downstream body in a single text block).
15716
+ */
15717
+ content?: CallToolResult["content"];
15718
+ /**
15719
+ * The structured form of the tool's output. MUST be a JSON object — the
15720
+ * MCP 2025-11-25 wire format rejects arrays/strings/numbers here.
15721
+ */
15722
+ structuredContent?: Record<string, unknown>;
15723
+ /**
15724
+ * Per-call metadata. Replaces the gateway's default of `{}`.
15686
15725
  */
15687
- setRawCallToolResult(req: CallToolResult): void;
15726
+ _meta?: Record<string, unknown>;
15688
15727
  }
15689
15728
 
15690
15729
  /**
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zuplo/runtime",
3
3
  "type": "module",
4
- "version": "7.3.2",
4
+ "version": "7.4.3",
5
5
  "repository": "https://github.com/zuplo/zuplo",
6
6
  "author": "Zuplo, Inc.",
7
7
  "exports": {
@@ -0,0 +1,54 @@
1
+ /*!
2
+ * negotiator
3
+ * Copyright(c) 2026 Blake Embrey
4
+ * MIT Licensed
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ var contentType = require('content-type');
10
+
11
+ /**
12
+ * Module exports.
13
+ * @private
14
+ */
15
+
16
+ module.exports = parseAccept;
17
+
18
+ /**
19
+ * Parse an Accept-style header.
20
+ * @private
21
+ */
22
+
23
+ function parseAccept(header) {
24
+ var values = [];
25
+ var index = 0;
26
+
27
+ while (index < header.length) {
28
+ var start = skipOptionalWhitespace(header, index);
29
+ var parsed = contentType.parse(header, { comma: true, start: start });
30
+
31
+ // `content-type` normalizes the type, but accept methods return original casing.
32
+ parsed.type = header.slice(start, start + parsed.type.length);
33
+ values.push(parsed);
34
+
35
+ index = parsed.index + 1;
36
+ }
37
+
38
+ return values;
39
+ }
40
+
41
+ /**
42
+ * Skip optional whitespace.
43
+ * @private
44
+ */
45
+
46
+ function skipOptionalWhitespace(header, index) {
47
+ var cursor = index;
48
+
49
+ while (header.charCodeAt(cursor) === 0x20 || header.charCodeAt(cursor) === 0x09) {
50
+ cursor++;
51
+ }
52
+
53
+ return cursor;
54
+ }
@@ -8,6 +8,8 @@
8
8
 
9
9
  'use strict';
10
10
 
11
+ var parseAccept = require('./accept');
12
+
11
13
  /**
12
14
  * Module exports.
13
15
  * @public
@@ -21,55 +23,29 @@ module.exports.preferredCharsets = preferredCharsets;
21
23
  * @private
22
24
  */
23
25
 
24
- var simpleCharsetRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/;
25
-
26
- /**
27
- * Parse the Accept-Charset header.
28
- * @private
29
- */
30
-
31
26
  function parseAcceptCharset(accept) {
32
- var accepts = accept.split(',');
27
+ var accepts = parseAccept(accept);
33
28
 
34
29
  for (var i = 0, j = 0; i < accepts.length; i++) {
35
- var charset = parseCharset(accepts[i].trim(), i);
36
-
37
- if (charset) {
38
- accepts[j++] = charset;
39
- }
30
+ var charset = formatCharset(accepts[i], i);
31
+ if (charset) accepts[j++] = charset;
40
32
  }
41
33
 
42
- // trim accepts
43
34
  accepts.length = j;
44
-
45
35
  return accepts;
46
36
  }
47
37
 
48
38
  /**
49
- * Parse a charset from the Accept-Charset header.
39
+ * Format a parsed charset for negotiation.
50
40
  * @private
51
41
  */
52
42
 
53
- function parseCharset(str, i) {
54
- var match = simpleCharsetRegExp.exec(str);
55
- if (!match) return null;
56
-
57
- var charset = match[1];
58
- var q = 1;
59
- if (match[2]) {
60
- var params = match[2].split(';')
61
- for (var j = 0; j < params.length; j++) {
62
- var p = params[j].trim().split('=');
63
- if (p[0] === 'q') {
64
- q = parseFloat(p[1]);
65
- break;
66
- }
67
- }
68
- }
43
+ function formatCharset(parsed, i) {
44
+ if (!parsed.type) return null;
69
45
 
70
46
  return {
71
- charset: charset,
72
- q: q,
47
+ charset: parsed.type,
48
+ q: parsed.parameters.q ? parseFloat(parsed.parameters.q) : 1,
73
49
  i: i
74
50
  };
75
51
  }
@@ -8,6 +8,8 @@
8
8
 
9
9
  'use strict';
10
10
 
11
+ var parseAccept = require('./accept');
12
+
11
13
  /**
12
14
  * Module exports.
13
15
  * @public
@@ -21,20 +23,13 @@ module.exports.preferredEncodings = preferredEncodings;
21
23
  * @private
22
24
  */
23
25
 
24
- var simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/;
25
-
26
- /**
27
- * Parse the Accept-Encoding header.
28
- * @private
29
- */
30
-
31
26
  function parseAcceptEncoding(accept) {
32
- var accepts = accept.split(',');
27
+ var accepts = parseAccept(accept);
33
28
  var hasIdentity = false;
34
29
  var minQuality = 1;
35
30
 
36
31
  for (var i = 0, j = 0; i < accepts.length; i++) {
37
- var encoding = parseEncoding(accepts[i].trim(), i);
32
+ var encoding = formatEncoding(accepts[i], i);
38
33
 
39
34
  if (encoding) {
40
35
  accepts[j++] = encoding;
@@ -55,37 +50,21 @@ function parseAcceptEncoding(accept) {
55
50
  };
56
51
  }
57
52
 
58
- // trim accepts
59
53
  accepts.length = j;
60
-
61
54
  return accepts;
62
55
  }
63
56
 
64
57
  /**
65
- * Parse an encoding from the Accept-Encoding header.
58
+ * Format a parsed encoding for negotiation.
66
59
  * @private
67
60
  */
68
61
 
69
- function parseEncoding(str, i) {
70
- var match = simpleEncodingRegExp.exec(str);
71
- if (!match) return null;
72
-
73
- var encoding = match[1];
74
- var q = 1;
75
- if (match[2]) {
76
- var params = match[2].split(';');
77
- for (var j = 0; j < params.length; j++) {
78
- var p = params[j].trim().split('=');
79
- if (p[0] === 'q') {
80
- q = parseFloat(p[1]);
81
- break;
82
- }
83
- }
84
- }
62
+ function formatEncoding(parsed, i) {
63
+ if (!parsed.type) return null;
85
64
 
86
65
  return {
87
- encoding: encoding,
88
- q: q,
66
+ encoding: parsed.type,
67
+ q: parsed.parameters.q ? parseFloat(parsed.parameters.q) : 1,
89
68
  i: i
90
69
  };
91
70
  }
@@ -8,6 +8,9 @@
8
8
 
9
9
  'use strict';
10
10
 
11
+ var contentType = require('content-type');
12
+ var parseAccept = require('./accept');
13
+
11
14
  /**
12
15
  * Module exports.
13
16
  * @public
@@ -21,60 +24,36 @@ module.exports.preferredLanguages = preferredLanguages;
21
24
  * @private
22
25
  */
23
26
 
24
- var simpleLanguageRegExp = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
25
-
26
- /**
27
- * Parse the Accept-Language header.
28
- * @private
29
- */
30
-
31
27
  function parseAcceptLanguage(accept) {
32
- var accepts = accept.split(',');
28
+ var accepts = parseAccept(accept);
33
29
 
34
30
  for (var i = 0, j = 0; i < accepts.length; i++) {
35
- var language = parseLanguage(accepts[i].trim(), i);
36
-
37
- if (language) {
38
- accepts[j++] = language;
39
- }
31
+ var language = formatLanguage(accepts[i], i);
32
+ if (language) accepts[j++] = language;
40
33
  }
41
34
 
42
- // trim accepts
43
35
  accepts.length = j;
44
-
45
36
  return accepts;
46
37
  }
47
38
 
48
39
  /**
49
- * Parse a language from the Accept-Language header.
40
+ * Format a parsed language for negotiation.
50
41
  * @private
51
42
  */
52
43
 
53
- function parseLanguage(str, i) {
54
- var match = simpleLanguageRegExp.exec(str);
55
- if (!match) return null;
56
-
57
- var prefix = match[1]
58
- var suffix = match[2]
59
- var full = prefix
44
+ function formatLanguage(parsed, i) {
45
+ if (!parsed.type) return null;
60
46
 
61
- if (suffix) full += "-" + suffix;
62
-
63
- var q = 1;
64
- if (match[3]) {
65
- var params = match[3].split(';')
66
- for (var j = 0; j < params.length; j++) {
67
- var p = params[j].split('=');
68
- if (p[0] === 'q') q = parseFloat(p[1]);
69
- }
70
- }
47
+ var hyphen = parsed.type.indexOf('-');
48
+ var prefix = hyphen === -1 ? parsed.type : parsed.type.slice(0, hyphen);
49
+ var suffix = hyphen === -1 ? undefined : parsed.type.slice(hyphen + 1);
71
50
 
72
51
  return {
73
52
  prefix: prefix,
74
53
  suffix: suffix,
75
- q: q,
54
+ q: parsed.parameters.q ? parseFloat(parsed.parameters.q) : 1,
76
55
  i: i,
77
- full: full
56
+ full: parsed.type
78
57
  };
79
58
  }
80
59
 
@@ -103,7 +82,7 @@ function getLanguagePriority(language, accepted, index) {
103
82
  */
104
83
 
105
84
  function specify(language, spec, index) {
106
- var p = parseLanguage(language)
85
+ var p = formatLanguage(contentType.parse(language), 0)
107
86
  if (!p) return null;
108
87
  var s = 0;
109
88
  if(spec.full.toLowerCase() === p.full.toLowerCase()){