langwatch 1.19.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +27 -13
  2. package/dist/agent/index.d.mts +122 -79
  3. package/dist/agent/index.d.ts +122 -79
  4. package/dist/agent/index.js +541 -301
  5. package/dist/agent/index.js.map +1 -1
  6. package/dist/agent/index.mjs +540 -302
  7. package/dist/agent/index.mjs.map +1 -1
  8. package/dist/{chunk-NPEK4RSD.mjs → chunk-2GKEMCKT.mjs} +7757 -2409
  9. package/dist/chunk-2GKEMCKT.mjs.map +1 -0
  10. package/dist/chunk-5VYEG3RS.js +117 -0
  11. package/dist/chunk-5VYEG3RS.js.map +1 -0
  12. package/dist/chunk-AYPQNLCC.mjs +852 -0
  13. package/dist/chunk-AYPQNLCC.mjs.map +1 -0
  14. package/dist/chunk-BL3GHEKF.js +59 -0
  15. package/dist/chunk-BL3GHEKF.js.map +1 -0
  16. package/dist/chunk-C7IWJ2GJ.mjs +59 -0
  17. package/dist/chunk-C7IWJ2GJ.mjs.map +1 -0
  18. package/dist/chunk-DLZCOUXC.js +852 -0
  19. package/dist/chunk-DLZCOUXC.js.map +1 -0
  20. package/dist/{chunk-O4J3ZPZY.js → chunk-F7BVJW3S.js} +7912 -2564
  21. package/dist/chunk-F7BVJW3S.js.map +1 -0
  22. package/dist/{chunk-SY5HSL5G.mjs → chunk-M3DSPR5S.mjs} +124 -112
  23. package/dist/chunk-M3DSPR5S.mjs.map +1 -0
  24. package/dist/{chunk-CSV7B7B5.js → chunk-MAO3SIBB.js} +141 -129
  25. package/dist/chunk-MAO3SIBB.js.map +1 -0
  26. package/dist/{chunk-LCG5Y2EG.js → chunk-N76BG37W.js} +15 -101
  27. package/dist/chunk-N76BG37W.js.map +1 -0
  28. package/dist/chunk-SERULD4Z.mjs +117 -0
  29. package/dist/chunk-SERULD4Z.mjs.map +1 -0
  30. package/dist/{chunk-73QOHSHU.js → chunk-WDUUAIG3.js} +2 -2
  31. package/dist/chunk-WDUUAIG3.js.map +1 -0
  32. package/dist/{chunk-WMDQVWA6.mjs → chunk-WSGVZ3J2.mjs} +22 -108
  33. package/dist/chunk-WSGVZ3J2.mjs.map +1 -0
  34. package/dist/{chunk-57L2K66L.mjs → chunk-XQ4CP2JK.mjs} +2 -2
  35. package/dist/chunk-XQ4CP2JK.mjs.map +1 -0
  36. package/dist/cli/bundle.js +853 -552
  37. package/dist/implementation-BXVPzwOt.d.mts +261 -0
  38. package/dist/implementation-DJ-JveZl.d.ts +261 -0
  39. package/dist/index.d.mts +479 -1130
  40. package/dist/index.d.ts +479 -1130
  41. package/dist/index.js +7908 -3525
  42. package/dist/index.js.map +1 -1
  43. package/dist/index.mjs +7883 -3500
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/observability-sdk/index.d.mts +16 -76
  46. package/dist/observability-sdk/index.d.ts +16 -76
  47. package/dist/observability-sdk/index.js +8 -5
  48. package/dist/observability-sdk/index.js.map +1 -1
  49. package/dist/observability-sdk/index.mjs +13 -10
  50. package/dist/observability-sdk/instrumentation/langchain/index.d.mts +3 -5
  51. package/dist/observability-sdk/instrumentation/langchain/index.d.ts +3 -5
  52. package/dist/observability-sdk/instrumentation/langchain/index.js +160 -112
  53. package/dist/observability-sdk/instrumentation/langchain/index.js.map +1 -1
  54. package/dist/observability-sdk/instrumentation/langchain/index.mjs +148 -100
  55. package/dist/observability-sdk/instrumentation/langchain/index.mjs.map +1 -1
  56. package/dist/observability-sdk/semconv/index.d.mts +37 -0
  57. package/dist/observability-sdk/semconv/index.d.ts +37 -0
  58. package/dist/observability-sdk/semconv/index.js +121 -0
  59. package/dist/observability-sdk/semconv/index.js.map +1 -0
  60. package/dist/observability-sdk/semconv/index.mjs +121 -0
  61. package/dist/observability-sdk/semconv/index.mjs.map +1 -0
  62. package/dist/observability-sdk/setup/node/index.d.mts +73 -313
  63. package/dist/observability-sdk/setup/node/index.d.ts +73 -313
  64. package/dist/observability-sdk/setup/node/index.js +6 -4
  65. package/dist/observability-sdk/setup/node/index.js.map +1 -1
  66. package/dist/observability-sdk/setup/node/index.mjs +5 -3
  67. package/dist/types--ExXvd26.d.mts +16583 -0
  68. package/dist/{types-VOZv9LYO.d.ts → types-CoS5PmeU.d.mts} +22 -59
  69. package/dist/{types-VOZv9LYO.d.mts → types-CoS5PmeU.d.ts} +22 -59
  70. package/dist/types-CpT3iEIj.d.ts +16583 -0
  71. package/dist/{types-DLfhJb38.d.mts → types-RkiBQZ9j.d.mts} +1 -6
  72. package/dist/{types-DLfhJb38.d.ts → types-RkiBQZ9j.d.ts} +1 -6
  73. package/dist/values-1yj9l0Sa.d.mts +112 -0
  74. package/dist/values-1yj9l0Sa.d.ts +112 -0
  75. package/package.json +94 -64
  76. package/dist/chunk-57L2K66L.mjs.map +0 -1
  77. package/dist/chunk-73QOHSHU.js.map +0 -1
  78. package/dist/chunk-CSV7B7B5.js.map +0 -1
  79. package/dist/chunk-LCG5Y2EG.js.map +0 -1
  80. package/dist/chunk-NPEK4RSD.mjs.map +0 -1
  81. package/dist/chunk-O4J3ZPZY.js.map +0 -1
  82. package/dist/chunk-PIYOJF6V.mjs +0 -566
  83. package/dist/chunk-PIYOJF6V.mjs.map +0 -1
  84. package/dist/chunk-SY5HSL5G.mjs.map +0 -1
  85. package/dist/chunk-WGLVIV4J.js +0 -566
  86. package/dist/chunk-WGLVIV4J.js.map +0 -1
  87. package/dist/chunk-WMDQVWA6.mjs.map +0 -1
  88. package/dist/implementation-CeTvnq6e.d.mts +0 -716
  89. package/dist/implementation-CsQrexpY.d.ts +0 -716
  90. package/dist/types-ofe1fAYQ.d.mts +0 -38143
  91. package/dist/types-wuYJPoFb.d.ts +0 -38143
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/langwatch/langwatch/sdks/typescript/dist/index.js","../src/internal/credentialContext.ts","../src/cli/utils/responseFormat.ts","../src/cli/utils/promptConverter.ts","../src/client-sdk/services/prompts/tracing/prompt-tracing.decorator.ts","../src/client-sdk/services/prompts/tracing/prompt-service-tracing.decorator.ts","../src/client-sdk/services/prompts/tracing/tracer.ts","../src/client-sdk/tracing/create-tracing-proxy.ts","../src/internal/api/client.ts","../../../packages/langy/src/cards/handled-error.ts","../src/internal/api/errors.ts","../src/internal/http/langwatchFetch.ts","../src/client-sdk/services/prompts/errors/prompt-compilation.error.ts","../src/client-sdk/services/prompts/errors/prompt-validation.error.ts","../src/client-sdk/services/prompts/errors/prompts.error.ts","../src/client-sdk/services/prompts/errors/prompts-api.error.ts","../src/client-sdk/services/_shared/error-code-copy.ts","../src/client-sdk/services/_shared/format-api-error.ts","../src/client-sdk/services/prompts/prompts-api.service.ts","../src/client-sdk/services/prompts/prompt.ts","../src/client-sdk/services/prompts/constants.ts","../src/client-sdk/services/prompts/schema/prompt.schema.ts","../src/client-sdk/services/prompts/types.ts","../src/cli/utils/fileManager.ts","../src/cli/types-prompt.ts","../src/cli/utils/errors/prompt-not-found.error.ts","../src/client-sdk/services/prompts/local-prompts.service.ts","../src/client-sdk/services/prompts/prompts.facade.ts","../src/client-sdk/services/datasets/errors.ts","../src/client-sdk/services/datasets/tracing/tracer.ts","../src/client-sdk/services/datasets/dataset.service.ts","../src/client-sdk/services/datasets/datasets.facade.ts","../src/client-sdk/services/experiments/experiment.ts","../src/client-sdk/services/experiments/humanReadableId.ts","../src/client-sdk/services/experiments/errors/experiment.error.ts","../src/client-sdk/services/experiments/comparison.ts","../src/client-sdk/services/experiments/printSummary.ts","../src/client-sdk/services/experiments/experiments-api.service.ts","../src/client-sdk/services/experiments/platformErrors.ts","../src/client-sdk/services/experiments/run-status.ts","../src/client-sdk/services/experiments/mapResults.ts","../src/client-sdk/services/experiments/experiments.facade.ts","../src/client-sdk/services/evaluations/evaluations.facade.ts","../src/client-sdk/services/evaluations/errors.ts","../src/client-sdk/services/evaluators/errors.ts","../src/client-sdk/services/_shared/throw-handled-error.ts","../src/client-sdk/services/evaluators/evaluators-api.service.ts","../src/client-sdk/services/scenarios/errors.ts","../src/client-sdk/services/scenarios/scenarios-api.service.ts","../src/client-sdk/services/suites/suites-api.service.ts","../src/client-sdk/services/run-plans/run-plans-api.service.ts","../src/client-sdk/services/instant-evals/instant-evals-api.service.ts","../src/client-sdk/services/test-suites/test-suites-api.service.ts","../src/client-sdk/services/workflows/workflows-api.service.ts","../src/client-sdk/services/agents/agents-api.service.ts","../src/client-sdk/services/annotations/annotations-api.service.ts","../src/client-sdk/services/dashboards/dashboards-api.service.ts","../src/client-sdk/services/model-providers/model-providers-api.service.ts","../src/client-sdk/services/analytics/analytics-api.service.ts","../src/client-sdk/services/query/query-api.service.ts","../src/client-sdk/services/triggers/triggers-api.service.ts","../src/client-sdk/services/graphs/graphs-api.service.ts","../src/client-sdk/services/simulation-runs/simulation-runs-api.service.ts","../src/client-sdk/services/monitors/monitors-api.service.ts","../src/client-sdk/services/secrets/secrets-api.service.ts","../src/client-sdk/services/_shared/collect-cursor-pages.ts","../src/client-sdk/services/_shared/mutation-options.ts","../src/client-sdk/services/virtual-keys/virtual-keys-api.service.ts","../src/client-sdk/services/gateway-budgets/gateway-budgets-api.service.ts","../src/client-sdk/services/spend-events/spend-events-api.service.ts","../src/client-sdk/services/webhooks/webhooks-api.service.ts","../src/client-sdk/services/_shared/management-request.ts","../src/client-sdk/services/teams/teams-api.service.ts","../src/client-sdk/services/projects/projects-api.service.ts","../src/client-sdk/services/traces/types.ts","../src/client-sdk/services/traces/tracing/tracer.ts","../src/client-sdk/services/traces/service.ts","../src/client-sdk/services/traces/facade.ts","../src/client-sdk/index.ts","../src/client-sdk/services/webhooks/verify-signature.ts","../src/index.ts"],"names":["target","logger","path","FetchPolicy","tracer","_a","DEFAULT_POLL_INTERVAL"],"mappings":"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;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWI,EAAA;AAnRN,IAAA;AAoRI,IAAA;AACA,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAMI,EAAA;AA5RN,IAAA;AA6RI,IAAA;AACA,IAAA;AACF,EAAA;AA8BF;AAvHE;AACA;AAvEK;AAAA;AAAA;AAAA;AAAA;AAAA;AAsKL;AACS,EAAA;AAEL,IAAA;AACF,EAAA;AACF;AAEA;AACE,EAAA;AACA,EAAA;AACA,EAAA;AAKwD;AArT5D,EAAA;AAsTW,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;A9E8iPO;AACA;A+E32PA;AA2BI;AASA;AAQA;AAMA;AAwBA;AAGX,EAAA;AACE,IAAA;AACK,IAAA;AACA,IAAA;AACP,EAAA;AACF;AAmCS;AAIH,EAAA;AACE,EAAA;AACN,EAAA;AACE,IAAA;AACI,IAAA;AACJ,IAAA;AACA,IAAA;AAGI,IAAA;AAAmC,IAAA;AAEzC,EAAA;AACO,EAAA;AACT;AAGS;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AAEC,EAAA;AACT;AAmCgB;AAvLhB,EAAA;AA0LQ,EAAA;AAGF,EAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AAEA,EAAA;AAEA,EAAA;AACF,EAAA;AACF,IAAA;AACE,MAAA;AACG,MAAA;AACL,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACE,MAAA;AACG,MAAA;AACL,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACJ,EAAA;AACE,IAAA;AACA,IAAA;AAGE,MAAA;AACF,IAAA;AACF,EAAA;AAEK,EAAA;AACH,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;A/E8sPS;AACA;AgF1sPI;AACX,EAAA;AACA,EAAA;AACF;AhF4sPS;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/langwatch/langwatch/sdks/typescript/dist/index.js","sourcesContent":[null,"/**\n * Request-scoped resolved credentials.\n *\n * The CLI daemon is one long-lived process that admits multiple requests\n * concurrently when they share a `(cwd, env, colorLevel)` execution window\n * (see cli/daemon/execution.ts). Device-mode requests do exactly that: the\n * caller environment carries NO API key, so the window fingerprint is\n * identical across two different logged-in users and they run at the same\n * time. Writing the resolved per-user key into the single shared\n * `process.env.LANGWATCH_API_KEY` would let one in-flight request build a\n * service with another request's key the instant a concurrent resolution (or\n * a login/logout) overwrote the global: the cross-identity leak the daemon\n * design says must be structurally impossible.\n *\n * The fix keeps the resolved key out of the global entirely. Each request runs\n * inside a credential HOLDER scope established at the request boundary\n * (`runWithCredentialHolder`, wrapped around the in-process command dispatch\n * and around every daemon request in cli/daemon/execution.ts). The resolver,\n * running later inside that scope, mutates the holder; the API-client factory\n * reads it. Because the holder object identity is fixed per request by\n * `AsyncLocalStorage.run` at the top, and `run` (unlike `enterWith`)\n * propagates the store to every continuation of the wrapped callback, a\n * mutation made mid-command is visible to the service constructed afterward,\n * while a concurrent request in its own holder never observes it. Outside any\n * scope (a plain SDK embed, or a direct unit call) a process-local fallback\n * holder is used, which is safe because those paths are single-request.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\ninterface CredentialHolder {\n apiKey?: string;\n projectId?: string;\n requestedProject?: string;\n warnedProjectEnvIgnored?: boolean;\n}\n\nconst storage = new AsyncLocalStorage<CredentialHolder>();\n\n/**\n * Process-local fallback for callers not wrapped in a holder scope: a plain\n * SDK embed, or a cold-CLI path that skipped the wrapper. Never used inside\n * the daemon, where every request runs inside its own `run`-established\n * holder, so it can never carry one request's key into another.\n */\nconst fallbackHolder: CredentialHolder = {};\n\nfunction currentHolder(): CredentialHolder {\n return storage.getStore() ?? fallbackHolder;\n}\n\n/**\n * Run `fn` inside a fresh, empty credential holder. Called once at each request\n * boundary (in-process dispatch, and per daemon request). The resolver fills\n * the holder later; everything the request constructs afterward reads it.\n */\nexport function runWithCredentialHolder<T>(fn: () => T): T {\n return storage.run({}, fn);\n}\n\n/**\n * Publish the resolved API key for the current request into its holder. No\n * process-global state is touched, so concurrent requests never collide.\n */\nexport function setResolvedApiKey(apiKey: string): void {\n currentHolder().apiKey = apiKey;\n}\n\n/**\n * The API key resolved for the current request, or undefined when nothing has\n * resolved in this context. Client factories fall back to the environment\n * then, exactly as a plain SDK embed does.\n */\nexport function scopedApiKey(): string | undefined {\n return currentHolder().apiKey;\n}\n\n/**\n * Publish the project the current request targets. A user-scoped API key\n * (`sk-lw-{lookupId}_{secret}`) carries no project identity of its own, so the\n * server resolves the role binding from the project the request names: the\n * resolver decides which project that is (the personal one by default,\n * `--project <id|slug>` otherwise) and every client built afterwards reads it\n * from here. Same request scoping as the key, for the same reason — two\n * concurrent daemon requests can target different projects.\n */\nexport function setResolvedProjectId(projectId: string | undefined): void {\n currentHolder().projectId = projectId;\n}\n\n/**\n * The project id resolved for the current request, or undefined when nothing\n * has resolved in this context. Client factories fall back to\n * `LANGWATCH_PROJECT_ID` then, exactly as a plain SDK embed does.\n */\nexport function scopedProjectId(): string | undefined {\n return currentHolder().projectId;\n}\n\n/**\n * Publish the project the command line pointed this request at, BEFORE any\n * credential resolves: the id or slug as the user typed it, not a resolved id.\n *\n * Set from the `preAction` hook rather than passed down through the commands,\n * so a command inherits `--project` without its action having to accept the\n * value and hand it on (cli/utils/projectOption.ts). The resolver reads it\n * with `requestedProject()` and turns it into the project the request names.\n */\nexport function setRequestedProject(selector: string | undefined): void {\n currentHolder().requestedProject = selector;\n}\n\n/**\n * The project selector the command line asked for in this request, or\n * undefined when it named none. Unresolved on purpose: the resolver decides\n * what a selector means, and it is the only thing that can.\n */\nexport function requestedProject(): string | undefined {\n return currentHolder().requestedProject;\n}\n\n/**\n * Take the right to warn that `LANGWATCH_PROJECT_ID` was ignored, once per\n * request. True the first time it is asked in this holder, false afterwards.\n *\n * Request-scoped rather than process-scoped because the daemon serves many\n * requests from one process: a module-level flag would warn the first caller\n * and leave every later one running against the key's own project with\n * nothing on screen saying so, which is the silence the warning exists to end.\n */\nexport function claimProjectEnvIgnoredWarning(): boolean {\n const holder = currentHolder();\n if (holder.warnedProjectEnvIgnored) return false;\n holder.warnedProjectEnvIgnored = true;\n return true;\n}\n\n/**\n * Clear the process-local fallback holder. Test-only: a unit test that sets a\n * key outside any scope would otherwise leak it into the next test.\n */\nexport function resetFallbackCredentialHolder(): void {\n fallbackHolder.apiKey = undefined;\n fallbackHolder.projectId = undefined;\n fallbackHolder.requestedProject = undefined;\n fallbackHolder.warnedProjectEnvIgnored = undefined;\n}\n","/**\n * Bidirectional mapping between the platform's `outputs` array and the\n * local YAML `response_format` block.\n *\n * The platform is the single source of truth and stores structured output as\n * an `outputs` array. The local `.prompt.yaml` follows the GitHub Prompts\n * convention and carries a `response_format` block. Push and pull must be\n * exact inverses, so both directions live here and share one definition of\n * what \"flat structured fields\" vs. \"an opaque JSON schema\" means.\n *\n * Two shapes round-trip losslessly:\n *\n * - Flat platform fields (e.g. l1, l2, l3, reasoning) ⇆ a single-level\n * JSON-schema object whose properties are those fields. The platform keeps\n * showing them as individual fields after a pull → edit → push cycle.\n *\n * - A rich JSON schema (enums, nested objects, arrays, descriptions, …) ⇆ a\n * single `json_schema` output that preserves the schema verbatim.\n */\n\nexport type CliOutputType = \"str\" | \"float\" | \"bool\" | \"json_schema\";\n\nexport interface CliOutput {\n identifier: string;\n type: CliOutputType;\n json_schema?: Record<string, unknown>;\n}\n\nexport interface LocalResponseFormat {\n name?: string;\n schema: Record<string, unknown>;\n}\n\n/**\n * The CLI default output for an unstructured (plain text) prompt. A prompt\n * with exactly this single output has no structured response and therefore no\n * `response_format` block.\n */\nexport const DEFAULT_TEXT_OUTPUT: CliOutput = {\n identifier: \"output\",\n type: \"str\",\n};\n\nconst SCALAR_OUTPUT_TO_JSON_TYPE: Record<\n Exclude<CliOutputType, \"json_schema\">,\n string\n> = {\n str: \"string\",\n float: \"number\",\n bool: \"boolean\",\n};\n\nconst JSON_TYPE_TO_SCALAR_OUTPUT: Record<\n string,\n Exclude<CliOutputType, \"json_schema\">\n> = {\n string: \"str\",\n number: \"float\",\n integer: \"float\",\n boolean: \"bool\",\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/**\n * Accepts either the canonical local form `{ name?, schema }` or the\n * OpenAI-standard wrapper `{ type: \"json_schema\", json_schema: { name, schema } }`\n * and normalizes to `{ name?, schema }`. Returns undefined when there is no\n * usable schema.\n */\nexport const normalizeResponseFormat = (\n raw: unknown,\n): LocalResponseFormat | undefined => {\n if (!isPlainObject(raw)) return undefined;\n\n // OpenAI-standard wrapper\n if (raw.type === \"json_schema\" && isPlainObject(raw.json_schema)) {\n const inner = raw.json_schema;\n if (isPlainObject(inner.schema)) {\n return {\n name: typeof inner.name === \"string\" ? inner.name : undefined,\n schema: inner.schema,\n };\n }\n return undefined;\n }\n\n // Canonical local form\n if (isPlainObject(raw.schema)) {\n return {\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n schema: raw.schema,\n };\n }\n\n return undefined;\n};\n\n/**\n * A \"simple flat object schema\" has only scalar properties (string / number /\n * integer / boolean), no extra JSON-schema keywords on the schema or on any\n * property, and (when present) `required` listing exactly the properties.\n * These map losslessly to flat platform fields. Anything richer (enums,\n * arrays, nested objects, descriptions, …) is opaque and must be preserved\n * verbatim as a json_schema output.\n */\nexport const asFlatFields = (\n schema: Record<string, unknown>,\n): { identifier: string; type: Exclude<CliOutputType, \"json_schema\"> }[] | null => {\n if (schema.type !== \"object\") return null;\n if (!isPlainObject(schema.properties)) return null;\n\n const allowedSchemaKeys = new Set([\n \"type\",\n \"properties\",\n \"required\",\n \"additionalProperties\",\n ]);\n if (Object.keys(schema).some((k) => !allowedSchemaKeys.has(k))) return null;\n\n if (\n schema.additionalProperties !== undefined &&\n schema.additionalProperties !== false\n ) {\n return null;\n }\n\n const properties = schema.properties;\n const propertyNames = Object.keys(properties);\n if (propertyNames.length === 0) return null;\n\n const fields: {\n identifier: string;\n type: Exclude<CliOutputType, \"json_schema\">;\n }[] = [];\n\n for (const name of propertyNames) {\n const prop = properties[name];\n if (!isPlainObject(prop)) return null;\n if (Object.keys(prop).some((k) => k !== \"type\")) return null;\n const jsonType = prop.type;\n if (typeof jsonType !== \"string\") return null;\n const scalar = JSON_TYPE_TO_SCALAR_OUTPUT[jsonType];\n if (!scalar) return null;\n fields.push({ identifier: name, type: scalar });\n }\n\n if (schema.required !== undefined) {\n if (!Array.isArray(schema.required)) return null;\n const required = [...(schema.required as unknown[])].sort();\n const names = [...propertyNames].sort();\n if (\n required.length !== names.length ||\n required.some((r, i) => r !== names[i])\n ) {\n return null;\n }\n }\n\n return fields;\n};\n\n/**\n * Pull direction: reconstruct the local `response_format` block from the\n * platform `outputs` array. Returns undefined for a plain-text prompt so we\n * never invent a schema that wasn't there.\n */\nexport const outputsToResponseFormat = (\n outputs: CliOutput[] | undefined | null,\n fallbackName?: string,\n): LocalResponseFormat | undefined => {\n if (!outputs || outputs.length === 0) return undefined;\n\n const jsonSchemaOutput = outputs.find(\n (o) => o.type === \"json_schema\" && o.json_schema,\n );\n if (jsonSchemaOutput?.json_schema) {\n return {\n name: jsonSchemaOutput.identifier,\n schema: jsonSchemaOutput.json_schema,\n };\n }\n\n // Exactly the CLI default single text output → unstructured, no schema.\n if (\n outputs.length === 1 &&\n outputs[0]!.identifier === DEFAULT_TEXT_OUTPUT.identifier &&\n outputs[0]!.type === DEFAULT_TEXT_OUTPUT.type\n ) {\n return undefined;\n }\n\n // Flat structured fields → a single-level JSON schema object.\n const properties: Record<string, { type: string }> = {};\n for (const output of outputs) {\n if (output.type === \"json_schema\") continue;\n properties[output.identifier] = {\n type: SCALAR_OUTPUT_TO_JSON_TYPE[output.type],\n };\n }\n if (Object.keys(properties).length === 0) return undefined;\n\n return {\n name: fallbackName ?? \"output\",\n schema: {\n type: \"object\",\n properties,\n required: Object.keys(properties),\n additionalProperties: false,\n },\n };\n};\n\n/**\n * Push direction: turn the local `response_format` block back into the\n * platform `outputs` array. The exact inverse of {@link outputsToResponseFormat}:\n * a flat object schema expands to flat fields (so the platform keeps showing\n * them individually), anything richer becomes one json_schema output.\n */\nexport const responseFormatToOutputs = (\n raw: unknown,\n): CliOutput[] | undefined => {\n const rf = normalizeResponseFormat(raw);\n if (!rf) return undefined;\n\n const flat = asFlatFields(rf.schema);\n if (flat) {\n return flat.map((f) => ({ identifier: f.identifier, type: f.type }));\n }\n\n return [\n {\n identifier: rf.name ?? \"output\",\n type: \"json_schema\",\n json_schema: rf.schema,\n },\n ];\n};\n","import type {\n LocalPromptConfig,\n MaterializedPrompt,\n RuntimeParameters,\n} from \"../types\";\nimport { type PromptResponse, type UpdatePromptBody } from \"@/client-sdk/services/prompts/types\";\nimport {\n type CliOutput,\n type LocalResponseFormat,\n outputsToResponseFormat,\n} from \"./responseFormat\";\n\n/**\n * Converter utility for transforming between YAML prompt format and API service format.\n *\n * The YAML format follows the GitHub .prompt.yaml file format standard,\n * while the API format is our internal prompt service schema tied to the database.\n *\n * This separation allows us to maintain and evolve both formats independently\n * while keeping the conversion logic centralized and well-tested.\n */\nexport class PromptConverter {\n /**\n * Converts a Prompt instance from the API service to the MaterializedPrompt format\n * used for saving to the .materialized directory.\n */\n static fromApiToMaterialized(prompt: PromptResponse): MaterializedPrompt {\n return {\n id: prompt.id,\n name: prompt.name,\n version: prompt.version,\n versionId: prompt.versionId,\n model: prompt.model,\n messages: prompt.messages,\n prompt: prompt.prompt,\n temperature: prompt.temperature,\n maxTokens: prompt.maxTokens,\n inputs: prompt.inputs,\n outputs: prompt.outputs,\n parameters: prompt.parameters ?? {},\n updatedAt: prompt.updatedAt,\n };\n }\n\n /**\n * Converts a MaterializedPrompt to the YAML content structure\n * for saving to .prompt.yaml files.\n */\n static fromMaterializedToYaml(prompt: MaterializedPrompt): {\n model: string;\n modelParameters?: {\n temperature?: number;\n maxTokens?: number;\n };\n messages: Array<{\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n }>;\n response_format?: LocalResponseFormat;\n parameters?: RuntimeParameters;\n } {\n const result: any = {\n model: prompt.model,\n messages: prompt.messages,\n };\n\n if (Object.keys(prompt.parameters ?? {}).length > 0) {\n result.parameters = prompt.parameters;\n }\n\n // Add modelParameters if temperature or maxTokens exist. The server is the\n // source of truth for which sampling parameters a model accepts and omits\n // the ones it would reject (e.g. temperature on the gpt-5 family), so we\n // simply mirror what the API returned — never inject a default here.\n if (prompt.temperature !== undefined || prompt.maxTokens !== undefined) {\n result.modelParameters = {};\n if (prompt.temperature !== undefined) {\n result.modelParameters.temperature = prompt.temperature;\n }\n if (prompt.maxTokens !== undefined) {\n result.modelParameters.maxTokens = prompt.maxTokens;\n }\n }\n\n // Reconstruct the structured-output block from the platform outputs so a\n // pull never drops a response_format the user configured. Inverse of the\n // push direction in pushPrompts.\n const responseFormat = outputsToResponseFormat(\n prompt.outputs as CliOutput[] | undefined,\n prompt.name,\n );\n if (responseFormat) {\n result.response_format = responseFormat;\n }\n\n return result;\n }\n\n /**\n * Converts a LocalPromptConfig (loaded from YAML) to the format\n * expected by the API service for upserting.\n */\n static fromLocalToApiFormat(\n config: LocalPromptConfig,\n ): Omit<UpdatePromptBody, \"commitMessage\"> & { parameters?: RuntimeParameters }\n {\n return {\n model: config.model,\n temperature: config.modelParameters?.temperature,\n maxTokens: config.modelParameters?.max_tokens,\n messages: config.messages,\n parameters: config.parameters ?? {},\n };\n }\n\n /**\n * Extracts the system prompt from messages array.\n * Used when converting to API format that separates system prompt from messages.\n */\n static extractSystemPrompt(\n messages: Array<{ role: string; content: string }>,\n ): string {\n return messages.find((m) => m.role === \"system\")?.content ?? \"\";\n }\n\n /**\n * Filters out system messages from the messages array.\n * Used when converting to API format that handles system prompt separately.\n */\n static filterNonSystemMessages(\n messages: Array<{\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n }>,\n ): Array<{ role: \"user\" | \"assistant\"; content: string }> {\n return messages.filter((m) => m.role !== \"system\") as Array<{\n role: \"user\" | \"assistant\";\n content: string;\n }>;\n }\n}\n","import { type Prompt, type TemplateVariables, type CompiledPrompt } from \"../prompt\";\nimport { shouldCaptureInput, shouldCaptureOutput } from \"@/observability-sdk\";\nimport type { LangWatchSpan } from \"@/observability-sdk\";\n\n/**\n * Class that decorates the target prompt,\n * adding tracing to specific methods.\n */\nexport class PromptTracingDecorator {\n constructor(private readonly target: Prompt) {}\n\n private traceCompilation(\n span: LangWatchSpan,\n variables: TemplateVariables,\n compileFn: () => CompiledPrompt\n ): CompiledPrompt {\n span.setType(\"prompt\");\n\n if (shouldCaptureInput()) {\n span.setInput(this.target);\n\n if (variables) {\n span.setAttribute(\n 'langwatch.prompt.variables',\n JSON.stringify({\n type: \"json\",\n value: variables,\n }),\n );\n }\n }\n\n const result = compileFn();\n\n // Only emit combined handle:version format when both are available\n if (result.handle != null && result.version != null) {\n span.setAttribute(\n 'langwatch.prompt.id',\n `${result.handle}:${result.version}`,\n );\n }\n\n if (shouldCaptureOutput()) {\n span.setOutput({\n ...result,\n raw: void 0, // TODO(afr): Figure out a better way to do this.\n });\n }\n\n return result;\n }\n\n compile(span: LangWatchSpan, variables: TemplateVariables = {}): CompiledPrompt {\n return this.traceCompilation(\n span,\n variables,\n () => this.target.compile(variables),\n );\n }\n\n compileStrict(span: LangWatchSpan, variables: TemplateVariables): CompiledPrompt {\n return this.traceCompilation(\n span,\n variables,\n () => this.target.compileStrict(variables),\n );\n }\n}\n","import { type PromptsApiService } from \"../prompts-api.service\";\nimport type { LangWatchSpan } from \"@/observability-sdk\";\nimport { shouldCaptureInput, shouldCaptureOutput } from \"@/observability-sdk\";\nimport type { CreatePromptBody, UpdatePromptBody, PromptResponse } from \"../types\";\n\n/**\n * Class that decorates the target prompt service,\n * adding tracing to key methods.\n */\nexport class PromptServiceTracingDecorator {\n constructor(private readonly target: PromptsApiService) {}\n\n async get(\n span: LangWatchSpan,\n id: string,\n options?: { version?: string }\n ): Promise<PromptResponse> {\n span.setType(\"prompt\");\n\n const result = await this.target.get(id, options);\n\n if (result) {\n // Only emit combined format when both handle and version are available\n if (result.handle != null && result.version != null) {\n span.setAttribute(\n 'langwatch.prompt.id',\n `${result.handle}:${result.version}`,\n );\n }\n }\n\n if (result && shouldCaptureOutput()) {\n span.setOutput(\"json\", result);\n }\n\n return result;\n }\n\n async create(\n span: LangWatchSpan,\n params: CreatePromptBody\n ): Promise<PromptResponse> {\n span.setType(\"prompt\");\n\n if (shouldCaptureInput()) {\n span.setInput(params);\n }\n\n const result = await this.target.create(params);\n\n span.setAttributes({\n 'langwatch.prompt.id': result.id,\n 'langwatch.prompt.handle': result.handle ?? undefined,\n 'langwatch.prompt.scope': result.scope,\n 'langwatch.prompt.version.id': result.versionId,\n 'langwatch.prompt.version.number': result.version,\n });\n\n return result;\n }\n\n async update(\n span: LangWatchSpan,\n id: string,\n params: UpdatePromptBody\n ): Promise<PromptResponse> {\n\n if (shouldCaptureInput()) {\n span.setInput(params);\n }\n\n const result = await this.target.update(id, params);\n\n span.setType(\"prompt\");\n span.setAttributes({\n 'langwatch.prompt.id': id,\n 'langwatch.prompt.handle': result.handle ?? undefined,\n 'langwatch.prompt.scope': result.scope,\n 'langwatch.prompt.version.id': result.versionId,\n 'langwatch.prompt.version.number': result.version,\n });\n\n return result;\n }\n\n async delete(\n span: LangWatchSpan,\n id: string\n ): Promise<{ success: boolean }> {\n const result = await this.target.delete(id);\n\n span.setType(\"prompt\");\n span.setAttribute('langwatch.prompt.id', id);\n span.setAttribute('langwatch.prompt.deleted', 'true');\n\n return result;\n }\n\n async upsert(\n span: LangWatchSpan,\n handle: string,\n config: any\n ): Promise<{ created: boolean; prompt: PromptResponse }> {\n if (shouldCaptureInput()) {\n span.setInput(config);\n }\n\n const result = await this.target.upsert(handle, config);\n\n span.setType(\"prompt\");\n span.setAttributes({\n 'langwatch.prompt.handle': handle,\n 'langwatch.prompt.created': result.created.toString(),\n 'langwatch.prompt.id': result.prompt.id,\n 'langwatch.prompt.version.id': result.prompt.versionId,\n 'langwatch.prompt.version.number': result.prompt.version,\n });\n\n return result;\n }\n\n async sync(\n span: LangWatchSpan,\n params: any\n ): Promise<any> {\n if (shouldCaptureInput()) {\n span.setInput(params);\n }\n\n const result = await this.target.sync(params);\n\n span.setType(\"prompt\");\n span.setAttribute('langwatch.prompt.name', params.name);\n span.setAttribute('langwatch.prompt.sync.action', result.action);\n\n if (result.conflictInfo) {\n span.setAttributes({\n 'langwatch.prompt.sync.has_conflict': 'true',\n 'langwatch.prompt.sync.local_version': result.conflictInfo.localVersion.toString(),\n 'langwatch.prompt.sync.remote_version': result.conflictInfo.remoteVersion.toString(),\n });\n }\n\n return result;\n }\n}\n","import { getLangWatchTracer } from \"@/observability-sdk/tracer\";\nimport { LANGWATCH_SDK_NAME_CLIENT, LANGWATCH_SDK_VERSION } from \"@/internal/constants\";\n\nexport const tracer = getLangWatchTracer(`${LANGWATCH_SDK_NAME_CLIENT}.prompts`, LANGWATCH_SDK_VERSION);\n","import { type LangWatchTracer } from \"@/observability-sdk\";\nimport { SpanKind } from \"@opentelemetry/api\";\n\n// Type for decorator methods that receive span as first parameter\ntype DecoratorMethodWithSpan<T extends (...args: any[]) => any> =\n (span: any, ...args: Parameters<T>) => ReturnType<T>;\n\n// Type for decorator class that maps original methods to span-aware versions\n// Only requires methods that are actually implemented in the decorator\ntype DecoratorClass<T> = new (target: T) => Partial<{\n [K in keyof T]: T[K] extends (...args: any[]) => any\n ? DecoratorMethodWithSpan<T[K]>\n : T[K];\n }>;\n\n/**\n * Creates a proxy that always creates spans for public methods.\n * Decorators can access the span as the first parameter to add additional attributes.\n *\n * @param target - The target to wrap\n * @param tracer - The tracer instance to use\n * @param DecoratorClass - Optional decorator class for custom logic\n * @returns A proxy that wraps the target with consistent tracing\n */\nexport function createTracingProxy<\n T extends object,\n D extends DecoratorClass<T> | undefined = undefined\n>(\n target: T,\n tracer: LangWatchTracer,\n DecoratorClass?: D,\n): T {\n const decorator = DecoratorClass ? new DecoratorClass(target) : null;\n\n return new Proxy(target, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n // If it's not a function, return as-is\n if (typeof value !== \"function\") {\n return value;\n }\n\n // Only trace public methods\n if (\n typeof prop === \"string\" &&\n !prop.startsWith(\"_\") && // Skip private methods\n !isGetterOrSetter(target, prop) && // Skip actual getters/setters\n !isBuiltInMethod(prop) // Skip built-in methods\n ) {\n return (...args: any[]) => {\n const spanName = `${target.constructor.name}.${prop}`;\n\n return tracer.withActiveSpan(spanName, {\n kind: SpanKind.CLIENT,\n attributes: {\n 'code.function': prop,\n 'code.namespace': target.constructor.name,\n },\n }, (span) => {\n // If decorator has this method, call it with span as first parameter\n if (decorator && prop in decorator) {\n const decoratorMethod = decorator[prop as keyof typeof decorator];\n if (typeof decoratorMethod === \"function\") {\n return decoratorMethod.apply(decorator, [span, ...args]);\n }\n }\n\n // Default: just call the original method\n return value.apply(target, args);\n });\n };\n }\n\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n}\n\n// Helper function to check if a property is a getter or setter\nconst isGetterOrSetter = (target: any, prop: string | symbol): boolean => {\n // First check own properties\n let descriptor = Object.getOwnPropertyDescriptor(target, prop);\n\n // If not found on own properties, check prototype chain\n if (!descriptor) {\n const prototype = Object.getPrototypeOf(target);\n if (prototype) {\n descriptor = Object.getOwnPropertyDescriptor(prototype, prop);\n }\n }\n\n // Return true if it's a getter or setter\n return !!(descriptor?.get ?? descriptor?.set);\n};\n\n// Helper function to check if a method is a built-in method that should not be traced\nconst isBuiltInMethod = (prop: string | symbol): boolean => {\n if (typeof prop !== \"string\") {\n return false;\n }\n\n // List of built-in methods that should not be traced\n const builtInMethods = [\n 'toString',\n 'valueOf',\n 'toJSON',\n 'toLocaleString',\n 'hasOwnProperty',\n 'isPrototypeOf',\n 'propertyIsEnumerable',\n 'constructor'\n ];\n\n return builtInMethods.includes(prop);\n};\n","import openApiCreateClient, { type Middleware } from \"openapi-fetch\";\nimport type { paths } from \"../generated/openapi/api-client\";\nimport { version } from \"../../../package.json\";\nimport {\n LANGWATCH_SDK_LANGUAGE,\n LANGWATCH_SDK_NAME_OBSERVABILITY,\n LANGWATCH_SDK_RUNTIME,\n LANGWATCH_SDK_VERSION,\n} from \"../constants\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { scopedApiKey, scopedProjectId } from \"@/internal/credentialContext\";\nimport { buildAuthHeaders } from \"./auth\";\nimport { handledErrorFrom } from \"./errors\";\nimport { langwatchFetch } from \"../http/langwatchFetch\";\n\n/**\n * Turns a NAMED failure into a typed throw, once, for every call that goes\n * through this client.\n *\n * This lives in the transport rather than in each service because it is a\n * property of the WIRE, not of any one resource: the platform answers a declined\n * request with a `HandledError` — a `kind`, a status, a `meta` bag — and that is\n * true of `/api/traces` and `/api/prompts` alike. Reading it here means no\n * service has to remember to, and a service added tomorrow gets it for free.\n *\n * WHAT IT DOES NOT DO is just as load-bearing: a response whose body is not a\n * domain error — a 5xx, a proxy's HTML error page, a truncated body, anything at\n * all it cannot read — is left completely alone. `onResponse` returns nothing,\n * openapi-fetch carries on and hands the service the `{ error }` it always did,\n * and the service throws the same generic error it always threw. This is a\n * strict superset of the old behaviour: it only ever ADDS a type where there was\n * a string.\n */\nconst handledErrorMiddleware: Middleware = {\n async onResponse({ request, response }) {\n if (response.ok) return;\n\n // openapi-fetch reads the body itself further down the pipeline, and a body\n // can only be read once. Clone, or the non-domain path gets an empty error.\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"json\")) return;\n\n let body: unknown;\n try {\n body = await response.clone().json();\n } catch {\n // A body that claims JSON and isn't is exactly the case this must not\n // crash on. Leave it to the generic path.\n return;\n }\n\n const handledError = handledErrorFrom({\n body,\n status: response.status,\n // The platform's own sentence is the message; there is no operation to\n // prefix it with down here, and the CLI adds \"Failed to <action>\" itself.\n operation: `${request.method} ${new URL(request.url).pathname}`,\n message: undefined,\n });\n\n if (handledError) throw handledError;\n },\n};\n\n\n/**\n * Creates a new LangWatch API client.\n * @param apiKey - The API key or Personal Access Token used for authentication.\n * Defaults to `LANGWATCH_API_KEY`.\n * @param endpoint - The endpoint to use for the API client. Defaults to `LANGWATCH_ENDPOINT`\n * or the internal `DEFAULT_ENDPOINT`.\n * @param projectId - Project identifier. Required when `apiKey` is a PAT\n * (`pat-lw-*`). Defaults to `LANGWATCH_PROJECT_ID`.\n * @returns A new LangWatch API client.\n */\nexport const createLangWatchApiClient = (\n // The request-scoped key (the CLI resolver's output) wins over the global\n // env: in the daemon the resolved device-session key lives ONLY in the\n // async-scoped store, never in the shared process.env, so concurrent\n // requests can't read each other's credential. A plain SDK embed sets no\n // scope and falls back to the environment unchanged.\n apiKey: string = scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\",\n endpoint?: string,\n // Same precedence as the key: the request-scoped target project (the CLI\n // resolver's output, personal by default and `--project` when given) wins\n // over the global env, and a plain SDK embed that scopes nothing falls back\n // to the environment unchanged.\n projectId: string | undefined = scopedProjectId() ??\n process.env.LANGWATCH_PROJECT_ID,\n) => {\n const client = openApiCreateClient<paths>({\n baseUrl: resolveEndpoint(endpoint),\n fetch: langwatchFetch,\n headers: {\n ...buildAuthHeaders({ apiKey, projectId }),\n \"content-type\": \"application/json\",\n \"user-agent\": `langwatch-sdk-node/${version}`,\n \"x-langwatch-sdk-name\": LANGWATCH_SDK_NAME_OBSERVABILITY,\n \"x-langwatch-sdk-language\": LANGWATCH_SDK_LANGUAGE,\n \"x-langwatch-sdk-version\": LANGWATCH_SDK_VERSION,\n \"x-langwatch-sdk-platform\": LANGWATCH_SDK_RUNTIME(),\n },\n });\n\n client.use(handledErrorMiddleware);\n\n return client;\n};\n\n\nexport type LangwatchApiClient = ReturnType<typeof createLangWatchApiClient>;\n","/**\n * The failure contract between the platform, the CLI and the Langy panel.\n *\n * The API already speaks a precise language of failure: server-side, a\n * `HandledError` carries a `code` (\"dataset_not_found\", \"validation_error\"), an\n * HTTP status and a `meta` bag, and `handleError` flattens it onto the wire as:\n *\n * HTTP <httpStatus> { \"error\": <code>, \"message\": <message>, ...meta }\n *\n * By the time that reaches the CLI it has usually been mashed back down into a\n * single English string, and the panel can only print it. That is a real loss: a\n * 404 \"dataset not found\" is a fact the UI can act on (offer to list what does\n * exist); a 500 is not. So the CLI reads the wire shape back into the structured\n * error it always was and puts THAT on the event — code and status as their own\n * attributes — rather than flattening a structured failure into prose twice.\n *\n * The split that matters is domain vs infrastructure:\n * - a domain error is the platform saying \"no, and here is precisely why\" — the\n * user's problem, actionable, safe to show, worth a card;\n * - anything else (a dead socket, a 500, a proxy's HTML error page) is OUR\n * problem, and the panel must say so rather than blame the user.\n *\n * DELIBERATELY ZOD-FREE, and importable on its own (`@langwatch/langy/cards/handled-error`).\n * This module sits on the CLI's hot path: every instrumented command imports it,\n * including the overwhelming majority of runs where telemetry is switched off.\n * Importing zod here costs ~28ms on EVERY `langwatch` invocation — measured — and\n * a shape check this small does not need a schema engine to do it. The card\n * schemas, which are zod and which nothing on the hot path imports, live next\n * door.\n */\n\n/**\n * One link in the platform's `reasons` chain — the failure BEHIND the failure.\n * Recursive, and named by kind only: the platform masks anything it did not\n * raise itself as `{ code: \"unknown\" }` rather than leaking an internal message.\n */\nexport interface CliHandledErrorReason {\n kind: string;\n meta?: Record<string, unknown>;\n reasons?: CliHandledErrorReason[];\n}\n\n/** A failure, read back into the structure the platform originally gave it. */\nexport interface CliHandledError {\n /** The platform's serialisable discriminant, e.g. `dataset_not_found`. */\n code: string;\n /**\n * @deprecated Back-compat alias of `code`, emitted while the platform's\n * `DomainError` → `HandledError` rename rolls out (the serialised error\n * carries the same pair — see `SerializedHandledError.kind`). Read `code` in\n * new code; this alias is removed once no consumer reads `kind`.\n */\n kind: string;\n /** The human sentence. Safe to show: the platform wrote it for a user. */\n message: string;\n /** The status the platform answered with; 0 when the request never landed. */\n httpStatus: number;\n /** Whatever context the platform attached to the failure (ids, field errors). */\n meta: Record<string, unknown>;\n /**\n * True when the platform answered with a structured domain failure — i.e. it\n * understood the request and declined it. False for infrastructure failures,\n * where the CLI is guessing and the panel must not present it as the user's\n * fault.\n */\n isHandled: boolean;\n /**\n * The OTel trace the failure happened on, when the route sent it.\n *\n * Read when offered and simply absent otherwise — never fabricated. It now\n * survives all three REST dialects: the shared Hono handler nests it under\n * `trace: { traceId, … }`, and the routes that forward `serialize()` (or the\n * new framework envelope) carry it top-level.\n */\n traceId?: string;\n /** A clickable link to that trace, when the route sent one. */\n traceUrl?: string;\n /** A clickable link to the logs for that trace, when the route sent one. */\n logsUrl?: string;\n /** The reason chain, when the route sent it. Same availability as `traceId`. */\n reasons?: CliHandledErrorReason[];\n /**\n * What the user can DO about it — the platform's own next steps.\n *\n * The platform spells this `tips` (`HandledError.tips`, authored centrally in\n * `server/app-layer/error-remediation.ts` and keyed by code, with its docs\n * paths verified by CI). It is read here under both names: `tips` is what the\n * wire actually carries, `suggestions` is kept because the CLI's own error\n * document has always written that name and older documents must keep\n * parsing.\n *\n * This is the remediation channel ADR-045 added for exactly this consumer —\n * `specs/features/domain-error-contract.feature` requires that \"consumers\n * without a client-side explainer (CLI, API, MCP) can self-diagnose\". When\n * the server sends these they WIN over the CLI's own code-keyed fallback,\n * which only knows a handful of generic codes and cannot know, say, that\n * traces are deleted after the retention window.\n */\n suggestions?: string[];\n /** The docs page that explains the failure (`docsUrl` on the wire). */\n docUrl?: string;\n}\n\n/** Anything at or above this is the platform failing, not the caller. */\nconst SERVER_ERROR_STATUS = 500;\n\n/**\n * Codes the platform uses when it has NOT actually named a domain failure — the\n * generic 500 lane of `handleError`. They matter because the status is not always\n * recoverable: some of the SDK's service wrappers surface the error body without\n * the `Response` it came on. When that happens the code is the only thing left to\n * judge by, and these are the codes that mean \"we fell over\", not \"you asked for\n * something that cannot be done\".\n */\nconst GENERIC_CODES = new Set([\n \"internal server error\",\n \"internal_error\",\n \"unknown\",\n \"unknown error\",\n]);\n\nconst isGenericCode = (code: string): boolean =>\n GENERIC_CODES.has(code.trim().toLowerCase());\n\nconst asRecord = (value: unknown): Record<string, unknown> | null =>\n value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n\n/** The reason chain, defensively: anything without a code is not a reason. */\nconst asReasons = (value: unknown): CliHandledErrorReason[] | undefined => {\n if (!Array.isArray(value)) return undefined;\n\n const reasons = value\n .map(asRecord)\n .filter(\n (r): r is Record<string, unknown> =>\n !!r && (typeof r.code === \"string\" || typeof r.kind === \"string\"),\n )\n .map((r) => ({\n kind: (typeof r.code === \"string\" ? r.code : r.kind) as string,\n ...(asRecord(r.meta) ? { meta: asRecord(r.meta)! } : {}),\n ...(asReasons(r.reasons) ? { reasons: asReasons(r.reasons) } : {}),\n }));\n\n return reasons.length > 0 ? reasons : undefined;\n};\n\n/**\n * A libuv/Node system error, not a platform error body.\n *\n * `fetch` reports a transport failure by throwing a `TypeError(\"fetch failed\")`\n * whose `cause` is the system error, and `handledErrorFromThrown` unwraps to that\n * cause. Its own enumerable keys are `{ errno, code, syscall, address, port }` —\n * so its `code` is `ECONNREFUSED`/`ENOTFOUND`/`ETIMEDOUT`/a TLS cert code, NOT a\n * discriminant the platform ever chose. Reading one as a domain error is the\n * worst kind of wrong: it blames the user for the network being down, and it\n * lifts the local `address`/`port` into `meta` as though they were domain\n * context. `errno`/`syscall` are the tell, and no platform envelope carries\n * them, so their presence disqualifies the record outright — a transport\n * failure is infrastructure, always.\n */\nconst isSystemError = (record: Record<string, unknown>): boolean =>\n \"errno\" in record || \"syscall\" in record;\n\n/**\n * Does this record look like the platform's envelope at all?\n *\n * Only asked of a BARE top-level `code` — the one field a system error shares\n * with dialect 3. The platform never sends a lone `code`: its envelope always\n * carries the sentence, the meta bag, or the deprecated `kind` alongside it. So\n * a `code` with none of them is not the platform speaking, and is not trusted to\n * name a domain failure.\n */\nconst looksLikeErrorEnvelope = (record: Record<string, unknown>): boolean =>\n \"message\" in record || \"meta\" in record || \"kind\" in record;\n\n/** Next steps, defensively: only real strings survive. */\nconst asSuggestions = (value: unknown): string[] | undefined => {\n if (!Array.isArray(value)) return undefined;\n const suggestions = value.filter(\n (s): s is string => typeof s === \"string\" && s.trim().length > 0,\n );\n return suggestions.length > 0 ? suggestions : undefined;\n};\n\ninterface ErrorBody {\n code: string;\n message?: string;\n meta: Record<string, unknown>;\n traceId?: string;\n traceUrl?: string;\n logsUrl?: string;\n reasons?: CliHandledErrorReason[];\n suggestions?: string[];\n docUrl?: string;\n}\n\n/**\n * Read the platform's error body. The REST surface speaks this in FOUR\n * dialects, and all are real — verified against the routes, not assumed:\n *\n * 1. THE COMMON ONE, from the shared Hono error handler every `SecuredApp`\n * mounts (`app/api/middleware/error-handler.ts`): the HandledError is\n * FLATTENED to `{ error: <code>, message: <sentence>, ...meta }` — meta\n * spread across the top level, and `reasons`/`traceUrl` dropped entirely.\n * So `error` is the CODE here, not the sentence. The trace ids survive,\n * but nested: `trace: { traceId, spanId, traceUrl?, logsUrl? }`.\n *\n * 2. THE VERBATIM ONE, from routes that forward `HandledError.serialize()`\n * whole (e.g. `routes/scenario-generate.ts`):\n * `{ error: <sentence>, domainError: { code, kind, meta, traceId, reasons } }`.\n *\n * 3. THE FRAMEWORK ONE, from the new API framework\n * (`packages/api/src/errors.ts`): the envelope top-level —\n * `{ code, type, kind, message, meta, reasons, traceId, spanId, traceUrl }`,\n * with `error` carrying the HTTP status text on unversioned routes. `meta`\n * is a NESTED object here rather than spread, and `message` is usually the\n * code rather than a sentence: a handled error's own message is server copy\n * and never crosses the boundary (ADR-045). Prose, when the server\n * deliberately authored some, arrives as `meta.message` and wins.\n *\n * 4. THE CANONICAL ONE, from every `SecuredApp` that publishes the canonical\n * envelope (`app/api/shared/schemas.ts`): the failure NESTED under `error`\n * — `{ error: { type, code, message, meta?, trace_id?, span_id? } }`, with\n * the trace ids in snake_case. Unreadable until this dialect existed, so\n * an agent got no code and a customer's card printed the whole envelope\n * verbatim under \"this step couldn't be completed\", with the one sentence\n * that explained the failure buried in the middle of it.\n *\n * `code` is the name TypeScript uses, `type` the OpenAI-compatible name Go\n * emits; the framework sets all three to the same value (`errors.ts` assigns\n * `body.kind = body.code` and `body.type = body.code`), so which one answers\n * first only decides anything when a writer sent just one of them.\n *\n * A route that names the code explicitly under the deprecated `kind` only (an\n * older server, mid-rename) is read correctly too: `code ?? kind` always wins,\n * which means `error` is free to be the sentence.\n */\nconst asErrorBody = (value: unknown): ErrorBody | null => {\n const record = asRecord(value);\n if (!record) return null;\n\n // A libuv system error is a transport failure wearing a `code`. It is never\n // any of the three dialects, whatever else it carries, so it never gets to\n // name a domain failure — it falls through to the status-derived reading,\n // which calls it `network_error` and means it.\n if (isSystemError(record)) return null;\n\n // Dialect 2: the serialised HandledError, carried whole under `domainError`.\n const serialized = asRecord(record.domainError);\n if (\n serialized &&\n (typeof serialized.code === \"string\" || typeof serialized.kind === \"string\")\n ) {\n const telemetry = asRecord(serialized.telemetry);\n return {\n code:\n typeof serialized.code === \"string\"\n ? serialized.code\n : (serialized.kind as string),\n message:\n typeof record.error === \"string\"\n ? record.error\n : typeof record.message === \"string\"\n ? record.message\n : undefined,\n meta: asRecord(serialized.meta) ?? {},\n traceId:\n typeof serialized.traceId === \"string\"\n ? serialized.traceId\n : typeof telemetry?.traceId === \"string\"\n ? telemetry.traceId\n : undefined,\n traceUrl:\n typeof serialized.traceUrl === \"string\" ? serialized.traceUrl : undefined,\n logsUrl:\n typeof serialized.logsUrl === \"string\" ? serialized.logsUrl : undefined,\n reasons: asReasons(serialized.reasons),\n suggestions:\n asSuggestions(serialized.tips) ?? asSuggestions(serialized.suggestions),\n docUrl:\n typeof serialized.docsUrl === \"string\"\n ? serialized.docsUrl\n : typeof serialized.docUrl === \"string\"\n ? serialized.docUrl\n : undefined,\n };\n }\n\n // Dialect 4: THE CANONICAL ONE, from the shared REST envelope\n // (`app/api/shared/schemas.ts`) that the analytics-sql families and every\n // new canonical-envelope route answer with:\n // `{ error: { type, code, message, meta?, trace_id?, span_id? } }` — the\n // whole failure NESTED under `error` as an object, so none of the flat\n // readings below can see it. `code` is the discriminant, `type` the\n // status-class alias the Go plane also emits; reasons ride inside\n // `meta.reasons` and are lifted out. The Go plane's 402 additionally\n // carries `tips` / `docs_url` at the same level.\n const canonical = asRecord(record.error);\n if (\n canonical &&\n !isSystemError(canonical) &&\n (typeof canonical.code === \"string\" || typeof canonical.type === \"string\")\n ) {\n const code =\n typeof canonical.code === \"string\"\n ? canonical.code\n : (canonical.type as string);\n const { reasons: metaReasons, ...meta } = asRecord(canonical.meta) ?? {};\n return {\n code,\n message:\n typeof canonical.message === \"string\" && canonical.message !== code\n ? canonical.message\n : undefined,\n meta,\n traceId:\n typeof canonical.trace_id === \"string\" ? canonical.trace_id : undefined,\n reasons: asReasons(metaReasons),\n suggestions:\n asSuggestions(canonical.tips) ?? asSuggestions(canonical.suggestions),\n docUrl:\n typeof canonical.docs_url === \"string\" ? canonical.docs_url : undefined,\n };\n }\n\n // Dialects 1 and 3 (and the deprecated `kind`-only variant). One of them must\n // name the failure; without any, this is not the platform's shape at all.\n //\n // The ordering guards a hijack: dialect 1 spreads meta FLAT, so a meta bag\n // holding a literal `code` (or `type`) key would shadow the real discriminant\n // on `error` if it were read first. Dialect 3 always emits `kind` alongside\n // `code`, so a `kind` present means the envelope named itself and wins; with\n // no `kind`, `error` is the dialect-1 discriminant and a bare `code`/`type` is\n // only trusted when nothing else named the failure — and only then if the\n // record looks like the platform's envelope at all, so a stray `code`/`type`\n // on some other object cannot pass itself off as a discriminant the platform\n // chose.\n //\n // `type` sits at the same trust tier as `code` throughout: it is the same\n // value under the OpenAI-compatible name Go writes, so a writer that sent only\n // `type` must still resolve to the same failure — and must clear the same\n // envelope check, since `type` is a far more common field on ordinary objects\n // than `code` is.\n const named =\n typeof record.kind === \"string\"\n ? typeof record.code === \"string\"\n ? record.code\n : typeof record.type === \"string\"\n ? record.type\n : record.kind\n : typeof record.error === \"string\"\n ? record.error\n : looksLikeErrorEnvelope(record)\n ? typeof record.code === \"string\"\n ? record.code\n : typeof record.type === \"string\"\n ? record.type\n : null\n : null;\n if (named === null) return null;\n\n // Everything the platform did NOT put in meta gets lifted out, so the flat\n // spread does not smuggle the envelope's own fields back in as domain context.\n // `code`/`type` are discriminants, not context — they belong here too.\n const {\n error,\n message,\n kind,\n code,\n type,\n meta: explicitMeta,\n telemetry,\n trace,\n reasons,\n traceId,\n spanId,\n traceUrl,\n logsUrl,\n suggestions,\n docUrl,\n // The platform's own spelling of the two above. Lifted out for the same\n // reason as the rest: dialect 1 spreads meta flat, so leaving them in would\n // report the remediation copy back as domain context.\n tips,\n docsUrl,\n // `fault` is the envelope's own (\"customer\" vs \"platform\"), not something\n // the platform attached to this failure.\n fault,\n ...rest\n } = record;\n\n const nestedMeta = asRecord(explicitMeta);\n\n // The sentence, in order of how much the server meant it. Dialect 3's own\n // `message` is usually just the code echoed back — server copy never crosses\n // the boundary (ADR-045) — so prose the server deliberately authored under\n // `meta.message` wins outright. Failing that, `message` then `error` are the\n // sentence, but only when they are not simply repeating the discriminant:\n // a value equal to `named` carries no information a caller does not already\n // have, and passing it through would print the code where prose belongs.\n const authored = nestedMeta?.message;\n const sentence =\n typeof authored === \"string\" && authored.length > 0\n ? authored\n : typeof message === \"string\" && message !== named\n ? message\n : typeof error === \"string\" && error !== named\n ? error\n : undefined;\n\n const telemetryRecord = asRecord(telemetry);\n const traceRecord = asRecord(trace);\n\n return {\n code: named,\n message: sentence,\n // Dialect 3 carries meta as its own key; dialect 1 spreads it flat.\n meta: nestedMeta ?? rest,\n traceId:\n typeof traceRecord?.traceId === \"string\"\n ? traceRecord.traceId\n : typeof telemetryRecord?.traceId === \"string\"\n ? telemetryRecord.traceId\n : typeof traceId === \"string\"\n ? traceId\n : undefined,\n traceUrl:\n typeof traceUrl === \"string\"\n ? traceUrl\n : typeof traceRecord?.traceUrl === \"string\"\n ? traceRecord.traceUrl\n : undefined,\n logsUrl:\n typeof logsUrl === \"string\"\n ? logsUrl\n : typeof traceRecord?.logsUrl === \"string\"\n ? traceRecord.logsUrl\n : undefined,\n reasons: asReasons(reasons),\n suggestions: asSuggestions(tips) ?? asSuggestions(suggestions),\n docUrl:\n typeof docsUrl === \"string\"\n ? docsUrl\n : typeof docUrl === \"string\"\n ? docUrl\n : undefined,\n };\n};\n\n/**\n * Statuses where nothing the caller can SEND will change the answer.\n *\n * A machine caller — Langy runs the CLI in a shell — has exactly three moves\n * after a failure: retry unchanged, retry with different arguments, or stop.\n * Everything above tells it what the failure IS; this tells it which move is\n * left, and for these statuses the answer is \"stop\". Not because the request was\n * malformed (a 400/422 names the field to fix) and not because we fell over (a\n * 429/5xx is worth one retry), but because the platform understood the request\n * perfectly and the answer is no: the credential does not carry it (401/403),\n * the plan does not include it (402/403), it does not exist (404), it is gone\n * (410).\n *\n * Emitting this is the difference between an agent reporting a plan limit and an\n * agent quietly re-running a create with different flags until something sticks\n * — which is exactly what happened, and produced a second card for a thing that\n * never existed.\n */\nconst TERMINAL_STATUSES = new Set([401, 402, 403, 404, 410]);\n\n/**\n * True when retrying is pointless — see {@link TERMINAL_STATUSES}.\n *\n * Only ever said of a failure the platform NAMED. An infrastructure failure is\n * ours and transient by default, and a status we read off a proxy's error page\n * is not a verdict the platform reached.\n */\nexport const isTerminalFailure = (error: CliHandledError): boolean =>\n error.isHandled && TERMINAL_STATUSES.has(error.httpStatus);\n\n/**\n * The error constructors JavaScript raises for a fault in the PROGRAM, never\n * for a network that did not answer.\n *\n * `codeForStatus` calls anything with no HTTP status `network_error`, which is\n * right for a socket that never connected and wrong for a TypeError thrown\n * while rendering a response that had already arrived. `langwatch chart\n * schema` did exactly that against a payload shape it did not expect, and the\n * user was told \"Cannot read properties of undefined (reading 'length')\" under\n * the code `network_error` with the advice to check their connection: a fix\n * they cannot make, for a failure that was not theirs, and a crash filed as\n * something transient that a retry would clear.\n *\n * `SyntaxError` is deliberately absent. With no HTTP status it is almost\n * always `response.json()` over a body that was not JSON — a proxy's HTML\n * error page, a truncated answer — which `parseHandledError` already treats as\n * infrastructure, and which a retry genuinely can clear.\n */\nconst PROGRAM_FAULT_NAMES = new Set([\n \"TypeError\",\n \"RangeError\",\n \"ReferenceError\",\n]);\n\n/**\n * The codes a transport failure arrives with, as libuv and OpenSSL spell them.\n *\n * A named set rather than a SCREAMING_SNAKE shape, because Node writes its own\n * PROGRAM-fault codes in that same style: `new URL(\"/api/x\", endpoint)` with a\n * scheme-less endpoint throws `TypeError [ERR_INVALID_URL]`, and a shape match\n * would file that bug as a dead socket with \"check your connection\" as the way\n * out.\n */\nconst TRANSPORT_CODES = new Set([\n // libuv, the codes a socket, a DNS lookup or a connection carries.\n \"EADDRNOTAVAIL\",\n \"EAI_AGAIN\",\n \"ECANCELED\",\n \"ECONNABORTED\",\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"EHOSTDOWN\",\n \"EHOSTUNREACH\",\n \"ENETDOWN\",\n \"ENETUNREACH\",\n \"ENOTFOUND\",\n \"EPIPE\",\n \"EPROTO\",\n \"ETIMEDOUT\",\n // OpenSSL, verifying the certificate the other end presented.\n \"CERT_HAS_EXPIRED\",\n \"CERT_NOT_YET_VALID\",\n \"DEPTH_ZERO_SELF_SIGNED_CERT\",\n \"ERR_TLS_CERT_ALTNAME_INVALID\",\n \"SELF_SIGNED_CERT_IN_CHAIN\",\n \"UNABLE_TO_GET_ISSUER_CERT\",\n \"UNABLE_TO_GET_ISSUER_CERT_LOCALLY\",\n \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\",\n]);\n\n/**\n * True for a code the transport put there. `UND_ERR_` is undici's own prefix\n * (`UND_ERR_SOCKET`, `UND_ERR_CONNECT_TIMEOUT`), and every code under it names\n * something that happened to the request rather than to our code.\n */\nconst isTransportCode = (code: unknown): boolean =>\n typeof code === \"string\" &&\n (TRANSPORT_CODES.has(code) || code.startsWith(\"UND_ERR_\"));\n\n/**\n * Evidence that the throw came from the TRANSPORT rather than from our code.\n *\n * `fetch` reports a dead socket as `TypeError(\"fetch failed\")` whose `cause` is\n * the libuv system error, so the constructor alone cannot tell a refused\n * connection from a bug: both are a TypeError with no HTTP status. The cause is\n * what distinguishes them — it carries `syscall`/`errno` for a socket, and a\n * code of its own for a TLS failure, which has neither. The message is not\n * evidence: `TypeError(\"fetch failed\")` is also what a program fault in a\n * dependency reads like, and a substring match would file it as something a\n * retry clears.\n *\n * `originalError` is scanned beside `cause` because the SDK's HTTP layer wraps\n * a throw under that name, and `handledErrorFromThrown` reads it as the body:\n * an expired certificate arriving there carries `CERT_HAS_EXPIRED` and no\n * `errno`, so without this it would be read as a code the platform had chosen.\n */\nconst hasTransportEvidence = (error: unknown): boolean => {\n const outer = asRecord(error);\n const original = asRecord(outer?.originalError);\n return [outer, asRecord(outer?.cause), original, asRecord(original?.cause)].some(\n (candidate) =>\n typeof candidate?.syscall === \"string\" ||\n typeof candidate?.errno === \"number\" ||\n isTransportCode(candidate?.code),\n );\n};\n\n/** True when the throw is a fault in our own code rather than a failed request. */\nconst isProgramFault = (error: unknown): boolean =>\n error instanceof Error &&\n PROGRAM_FAULT_NAMES.has(error.name) &&\n !hasTransportEvidence(error);\n\n/** A stable code for a failure the platform did not name itself. */\nconst codeForStatus = (status: number): string => {\n if (status === 401 || status === 403) return \"unauthorized\";\n if (status === 404) return \"not_found\";\n if (status === 429) return \"rate_limited\";\n if (status >= SERVER_ERROR_STATUS) return \"internal_error\";\n if (status > 0) return \"request_failed\";\n return \"network_error\";\n};\n\nconst fallbackMessage = ({\n status,\n body,\n}: {\n status: number;\n body: unknown;\n}): string => {\n if (typeof body === \"string\" && body.trim()) return body.trim().slice(0, 300);\n return status > 0 ? `Request failed with status ${status}` : \"Request failed\";\n};\n\n/**\n * Read an HTTP failure back into a {@link CliHandledError}.\n *\n * A body in the platform's error shape that came back BELOW 500 is a true domain\n * error and keeps its code. A 5xx, or a body that is not the platform's shape at\n * all (a gateway's HTML, a truncated response), is infrastructure: it still\n * yields a usable error, but `isHandled` is false and the code degrades to a\n * status-derived one rather than a fabricated domain code.\n */\nexport const parseHandledError = ({\n status,\n body,\n}: {\n status: number;\n body: unknown;\n}): CliHandledError => {\n const parsed = asErrorBody(body);\n\n if (!parsed) {\n return {\n code: codeForStatus(status),\n kind: codeForStatus(status),\n message: fallbackMessage({ status, body }),\n httpStatus: status,\n meta: {},\n isHandled: false,\n };\n }\n\n return {\n code: parsed.code,\n // Deprecated back-compat alias — see CliHandledError.kind.\n kind: parsed.code,\n message: parsed.message ?? parsed.code,\n httpStatus: status,\n meta: parsed.meta,\n ...(parsed.traceId ? { traceId: parsed.traceId } : {}),\n ...(parsed.traceUrl ? { traceUrl: parsed.traceUrl } : {}),\n ...(parsed.logsUrl ? { logsUrl: parsed.logsUrl } : {}),\n ...(parsed.reasons ? { reasons: parsed.reasons } : {}),\n ...(parsed.suggestions ? { suggestions: parsed.suggestions } : {}),\n ...(parsed.docUrl ? { docUrl: parsed.docUrl } : {}),\n // The platform names its own failures; a 5xx names ours, whatever the body\n // says. Trusting a 500's \"code\" would let an infrastructure outage present\n // itself to the user as though they had done something wrong.\n //\n // With no status to go on, the code decides: the platform only emits a\n // generic code when it fell over, so anything more specific than that is a\n // failure it chose to name — which is exactly what a domain error is.\n isHandled:\n status > 0\n ? status < SERVER_ERROR_STATUS\n : !isGenericCode(parsed.code),\n };\n};\n\n/**\n * The document the CLI prints on stdout when a command fails under\n * `--format json`, and the one the panel reads back.\n *\n * A machine caller — Langy runs the CLI in a shell and parses its stdout — got\n * nothing but prose on the error path before this: the failure was a red line on\n * stderr and an exit code, so an agent could see THAT a command failed and never\n * WHY. It would then guess, and usually guess \"retry\". A `code` it can match on\n * is the difference between retrying a transient failure and stopping dead on a\n * terminal one.\n *\n * `ok: false` is the discriminant. A success document is the bare card object\n * (see `parseCliResult`), so nothing else on stdout carries it.\n */\nexport interface CliErrorDocument {\n ok: false;\n error: CliHandledError & {\n /**\n * Whether retrying — with the same arguments or with different ones — can\n * possibly change the answer. See {@link isTerminalFailure}.\n *\n * Written for the agent, which otherwise has to infer it from the code and\n * gets it wrong in the expensive direction: it re-ran a create that had\n * been refused on a plan limit with a different set of flags, which could\n * never have worked and put a second card in the user's transcript for a\n * resource that was never made. Derived, never authored by a caller.\n */\n terminal: boolean;\n };\n}\n\n/** Build the `--format json` failure document. */\nexport const toCliErrorDocument = (error: CliHandledError): CliErrorDocument => ({\n ok: false,\n error: { ...error, terminal: isTerminalFailure(error) },\n});\n\n/**\n * Read a CLI failure document back, or null when the output is not one.\n *\n * Null-on-miss rather than throw: stdout may hold a card, a human table, or\n * nothing at all, and none of those is an error document.\n */\nexport const readCliErrorDocument = (\n output: unknown,\n): CliHandledError | null => {\n const document =\n typeof output === \"string\" ? safeParseJson(output) : asRecord(output);\n\n const record = asRecord(document);\n if (!record || record.ok !== false) return null;\n\n const error = asRecord(record.error);\n const code =\n typeof error?.code === \"string\"\n ? error.code\n : typeof error?.kind === \"string\"\n ? error.kind\n : null;\n if (!error || code === null) return null;\n\n return {\n code,\n // Deprecated back-compat alias — see CliHandledError.kind.\n kind: code,\n message: typeof error.message === \"string\" ? error.message : code,\n httpStatus: typeof error.httpStatus === \"number\" ? error.httpStatus : 0,\n meta: asRecord(error.meta) ?? {},\n isHandled: error.isHandled === true,\n ...(typeof error.traceId === \"string\" ? { traceId: error.traceId } : {}),\n ...(typeof error.traceUrl === \"string\" ? { traceUrl: error.traceUrl } : {}),\n ...(typeof error.logsUrl === \"string\" ? { logsUrl: error.logsUrl } : {}),\n ...(asReasons(error.reasons) ? { reasons: asReasons(error.reasons) } : {}),\n ...(asSuggestions(error.suggestions)\n ? { suggestions: asSuggestions(error.suggestions) }\n : {}),\n ...(typeof error.docUrl === \"string\" ? { docUrl: error.docUrl } : {}),\n };\n};\n\nconst safeParseJson = (value: string): unknown => {\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"{\")) return null;\n try {\n return JSON.parse(trimmed) as unknown;\n } catch {\n return null;\n }\n};\n\n/**\n * A `LangWatchHandledError` — an error the SDK's transport ALREADY read the wire\n * into. It brands itself with a boolean rather than relying on `instanceof`,\n * which does not survive a bundled CLI meeting a consumer's own copy of the SDK.\n */\nconst asAlreadyReadHandledError = (\n outer: Record<string, unknown> | null,\n): CliHandledError | null => {\n if (!outer || outer.isLangWatchHandledError !== true) return null;\n const code =\n typeof outer.code === \"string\"\n ? outer.code\n : typeof outer.kind === \"string\"\n ? outer.kind\n : null;\n if (code === null) return null;\n\n return {\n code,\n // Deprecated back-compat alias — see CliHandledError.kind.\n kind: code,\n message: typeof outer.message === \"string\" ? outer.message : code,\n httpStatus: typeof outer.httpStatus === \"number\" ? outer.httpStatus : 0,\n meta: asRecord(outer.meta) ?? {},\n isHandled: true,\n ...(typeof outer.traceId === \"string\" ? { traceId: outer.traceId } : {}),\n ...(typeof outer.traceUrl === \"string\" ? { traceUrl: outer.traceUrl } : {}),\n ...(typeof outer.logsUrl === \"string\" ? { logsUrl: outer.logsUrl } : {}),\n ...(asReasons(outer.reasons) ? { reasons: asReasons(outer.reasons) } : {}),\n ...(asSuggestions(outer.suggestions)\n ? { suggestions: asSuggestions(outer.suggestions) }\n : {}),\n ...(typeof outer.docUrl === \"string\" ? { docUrl: outer.docUrl } : {}),\n };\n};\n\n/**\n * The status an error carries, wherever it chose to put it. The SDK's HTTP layer,\n * its service wrappers and `fetch` each spell this differently.\n */\nconst statusOf = (value: Record<string, unknown> | null): number => {\n if (!value) return 0;\n if (typeof value.status === \"number\") return value.status;\n if (typeof value.statusCode === \"number\") return value.statusCode;\n if (typeof value.httpStatus === \"number\") return value.httpStatus;\n\n const response = asRecord(value.response);\n if (response && typeof response.status === \"number\") return response.status;\n\n return 0;\n};\n\n/**\n * The same reading, for an error that was THROWN rather than returned — the shape\n * the SDK's service layer raises.\n *\n * Those wrappers (`TracesApiError` and friends) flatten the API's error body into\n * an English sentence for the console but keep the original alongside it, so the\n * structure is recoverable: unwrap to the innermost cause and read the wire shape\n * off that. Without this the CLI would report every failure as a generic string\n * and the panel would lose the one thing it can act on — the code.\n *\n * Anything unrecognisable becomes a non-domain error carrying its own message,\n * which is the honest answer: we do not know what went wrong, so we do not claim\n * a code we cannot substantiate.\n */\nexport const handledErrorFromThrown = (error: unknown): CliHandledError => {\n const outer = asRecord(error);\n\n // The SDK's HTTP layer may have read this already, into a richer structure than\n // the wire body it came from: the flattened body carries no trace id and no\n // reason chain, so re-deriving from it would THROW AWAY the fields the typed\n // error exists to preserve. Trust the reading that already happened.\n const alreadyRead = asAlreadyReadHandledError(outer);\n if (alreadyRead) return alreadyRead;\n\n // The SDK wraps the API's error body; the body is the thing worth reading.\n const cause =\n asRecord(outer?.originalError) ??\n asRecord(outer?.cause) ??\n asRecord(outer?.body) ??\n outer;\n\n const status = statusOf(cause) || statusOf(outer);\n const parsed = parseHandledError({ status, body: cause });\n\n const message =\n error instanceof Error && error.message ? error.message : parsed.message;\n\n // A transport failure is infrastructure whatever its system error happens to\n // carry. `isSystemError` disqualifies the record by `errno`/`syscall`, which\n // catches the socket cases but not a TLS failure: that one carries neither,\n // so its `code` (CERT_HAS_EXPIRED, UNABLE_TO_VERIFY_LEAF_SIGNATURE) was read\n // as a discriminant the platform had chosen and rendered as the user's\n // fault. The throw fetch makes is the tell, and it is enough on its own.\n if (status === 0 && hasTransportEvidence(error)) {\n return {\n code: \"network_error\",\n kind: \"network_error\",\n message,\n httpStatus: 0,\n meta: {},\n isHandled: false,\n };\n }\n\n // No status and a program fault: the request is not what failed, so do not\n // send the reader to their network. Read BEFORE the envelope, because the\n // runtime hangs codes of its own on these throws — `new URL(path, endpoint)`\n // with a scheme-less endpoint raises `TypeError [ERR_INVALID_URL]` — and a\n // code Node chose is not a discriminant the platform chose. Still\n // `isHandled: false`, and with no meta, because both are ours.\n if (status === 0 && isProgramFault(error)) {\n return {\n ...parsed,\n code: \"internal_error\",\n kind: \"internal_error\",\n message,\n meta: {},\n isHandled: false,\n };\n }\n\n if (parsed.isHandled) return parsed;\n\n return { ...parsed, message };\n};\n","import {\n parseHandledError,\n type CliHandledError,\n type CliHandledErrorReason,\n} from \"@langwatch/langy/cards/handled-error\";\n\n/**\n * A failure the platform NAMED.\n *\n * The API does not merely fail — when it declines a request it says precisely\n * why, as a `HandledError` with a serialisable `kind` (`dataset_not_found`,\n * `validation_error`), the HTTP status it answers with, and a `meta` bag of the\n * context that makes the failure actionable. Until now the SDK collapsed all of\n * that into an English sentence on a generic error, so a caller could read the\n * failure but never react to it: a 404 \"dataset not found\" — a fact you can\n * offer to fix — was indistinguishable from a 500.\n *\n * This is that structure, kept. Narrow with {@link isLangWatchHandledError} (or\n * `instanceof`) and switch on `code`; `body` is the escape hatch to whatever the\n * platform sent that this class did not model.\n *\n * ```ts\n * try {\n * await langwatch.datasets.get(\"nope\");\n * } catch (error) {\n * if (isLangWatchHandledError(error) && error.code === \"dataset_not_found\") {\n * // Actionable: we know WHAT was not found, and can offer the list.\n * console.error(error.meta.id, error.traceId);\n * }\n * throw error;\n * }\n * ```\n *\n * ONLY raised for a failure the platform named. An infrastructure failure — a\n * 5xx, a dead socket, a proxy's HTML error page — still raises the generic\n * service error it always did, because presenting one of those as a domain\n * error would blame the user for our outage.\n */\nexport class LangWatchHandledError extends Error {\n /**\n * The discriminant. A boolean brand rather than `instanceof` alone because a\n * bundled CLI and a consumer's `node_modules` copy of the SDK can hold two\n * different copies of this class, and `instanceof` is false across that seam —\n * the same reason the platform's own error handler tests for `code` rather\n * than class identity.\n */\n readonly isLangWatchHandledError = true as const;\n\n /** The platform's serialisable discriminant, e.g. `dataset_not_found`. */\n readonly code: string;\n /**\n * @deprecated Back-compat alias of `code`, kept while the platform's\n * `DomainError` → `HandledError` rename rolls out. Read `code` in new code.\n */\n readonly kind: string;\n /** The status the platform answered with. */\n readonly httpStatus: number;\n /** The context the platform attached: ids, field errors — whatever makes it actionable. */\n readonly meta: Record<string, unknown>;\n /** The OTel trace to quote at support. Absent unless the route sent one. */\n readonly traceId: string | undefined;\n /** A clickable link to that trace, when the route sent one. */\n readonly traceUrl: string | undefined;\n /** A clickable link to the logs for that trace, when the route sent one. */\n readonly logsUrl: string | undefined;\n /** The failure behind the failure, when the route sent the chain. */\n readonly reasons: CliHandledErrorReason[] | undefined;\n /** What the user can DO about it, when the platform sent next steps. */\n readonly suggestions: string[] | undefined;\n /** The docs page that explains the failure, when the platform sent one. */\n readonly docUrl: string | undefined;\n /** The raw response body, verbatim — the escape hatch for anything unmodelled. */\n readonly body: unknown;\n /** What the SDK was doing, e.g. `get dataset \"abc\"`. */\n readonly operation: string | undefined;\n\n /**\n * Alias of {@link httpStatus}, and the raw body again under the name the\n * per-service `*ApiError` classes use.\n *\n * Not redundancy for its own sake: this class is thrown from the SAME code\n * path that used to throw `TracesApiError` & friends, so anything that reads\n * `.status` or `.originalError` off a caught SDK error — including the CLI's\n * own telemetry reader — keeps working unchanged.\n */\n readonly status: number;\n readonly originalError: unknown;\n\n constructor({\n handled,\n body,\n operation,\n message,\n }: {\n handled: CliHandledError;\n body: unknown;\n operation?: string;\n message: string;\n }) {\n super(message);\n this.name = \"LangWatchHandledError\";\n this.code = handled.code;\n this.kind = handled.code;\n this.httpStatus = handled.httpStatus;\n this.meta = handled.meta;\n this.traceId = handled.traceId;\n this.traceUrl = handled.traceUrl;\n this.logsUrl = handled.logsUrl;\n this.reasons = handled.reasons;\n this.suggestions = handled.suggestions;\n this.docUrl = handled.docUrl;\n this.body = body;\n this.operation = operation;\n this.status = handled.httpStatus;\n this.originalError = body;\n }\n}\n\n/** Narrows any caught value to a {@link LangWatchHandledError}. */\nexport const isLangWatchHandledError = (\n error: unknown,\n): error is LangWatchHandledError =>\n error instanceof LangWatchHandledError ||\n (typeof error === \"object\" &&\n error !== null &&\n (error as { isLangWatchHandledError?: unknown }).isLangWatchHandledError ===\n true);\n\n/**\n * Read a non-2xx response into a {@link LangWatchHandledError}, or `null` when the\n * platform did NOT name the failure.\n *\n * `null` is the important half of the contract: it means \"this was not a domain\n * error\", and every caller answers it by throwing exactly the generic error it\n * threw before. A malformed body, an HTML error page from a proxy, a bare 500 —\n * all take the old path untouched. This is strictly additive.\n */\nexport const handledErrorFrom = ({\n operation,\n body,\n status,\n message,\n}: {\n /** What was being attempted — `GET /api/traces/{id}`, or a service's phrasing. */\n operation?: string;\n /** The parsed error body, as the HTTP client handed it back. */\n body: unknown;\n /** The status the response carried, when it is known. */\n status?: number;\n /**\n * The message the generic path WOULD have produced. Passed by callers that\n * already built one (so nothing regresses); omitted by the transport, which\n * has no operation worth prefixing and lets the platform's own sentence stand.\n */\n message?: string;\n}): LangWatchHandledError | null => {\n const handled = parseHandledError({ status: status ?? 0, body });\n if (!handled.isHandled) return null;\n\n return new LangWatchHandledError({\n handled,\n body,\n operation,\n // The platform wrote its message for a user to read. Use it as-is unless a\n // caller hands us the sentence it had already composed.\n message: message ?? handled.message,\n });\n};\n\nexport class LangWatchApiError extends Error {\n public readonly httpStatus: number;\n public readonly httpStatusText: string;\n public apiError: string | undefined;\n public body: unknown;\n\n constructor(message: string, response: Response) {\n super(message);\n this.httpStatus = response.status;\n this.httpStatusText = response.statusText;\n }\n\n async safeParseBody(response: Response): Promise<void> {\n try {\n if (response.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n const json = await response.json();\n\n this.body = json;\n\n if (json.error && typeof json.error === \"string\") {\n this.apiError = json.error;\n }\n\n return;\n }\n\n this.body = await response.text();\n } catch {\n this.body = null;\n }\n }\n}\n","/**\n * The one HTTP client every request to the LangWatch API goes through.\n *\n * A LangWatch endpoint configured as `http://app.langwatch.ai` answers with a\n * redirect to https. The global `fetch` follows it on its own and, for a 301\n * or 302, turns the POST into a GET and drops the body, so the event is lost\n * without an error. This client sends with `redirect: \"manual\"` and applies a\n * rule per method to the 3xx it gets back.\n *\n * GET and HEAD follow a 301, 302, 303, 307 or 308 with the same method, up to\n * five hops. A hop that keeps the origin, or only upgrades http to https on\n * the same host and port, keeps every header; any other hop drops the\n * credential headers first. A hop from https to http and a hop without a\n * Location are refused.\n *\n * Every other method follows exactly one redirect, and only when the target is\n * the same URL with the scheme changed from http to https (same host, port,\n * path and query). The replay uses the same method, headers and body bytes.\n *\n * Every refused redirect throws `LangWatchRedirectError`.\n *\n * This module depends on the SDK logger only, so the CLI boot graph and the\n * `agent` entry can import it without pulling anything else in.\n */\nimport { ConsoleLogger, type Logger } from \"../../logger\";\n\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n\n/** Methods that follow a redirect to any http or https URL. */\nconst FOLLOWING_METHODS = new Set([\"GET\", \"HEAD\"]);\n\n/** The most redirects a GET or HEAD follows before the next one is refused. */\nexport const MAX_FOLLOW_HOPS = 5;\n\n/**\n * Headers dropped when a GET or HEAD hop leaves the origin: the three the Fetch\n * standard strips on a cross-origin redirect, plus the names LangWatch keys on.\n */\nconst CREDENTIAL_HEADERS = [\n \"authorization\",\n \"cookie\",\n \"proxy-authorization\",\n \"x-api-key\",\n \"x-auth-token\",\n \"x-project-id\",\n];\n\nexport class LangWatchRedirectError extends Error {\n readonly url: string;\n readonly location: string | null;\n readonly status: number;\n\n constructor({\n url,\n location,\n status,\n }: {\n url: string;\n location: string | null;\n status: number;\n }) {\n super(\n `LangWatch refused to follow a redirect from ${url} to ${location ?? \"an unknown location\"} (HTTP ${status}). Set the endpoint to the final URL.`,\n );\n this.name = \"LangWatchRedirectError\";\n this.url = url;\n this.location = location;\n this.status = status;\n }\n}\n\nexport type LangWatchFetch = typeof globalThis.fetch;\n\nexport interface CreateLangWatchFetchOptions {\n /** The transport to send with. Defaults to the global `fetch` at call time. */\n fetch?: LangWatchFetch;\n /** Receives the one warning per process about an http endpoint. */\n logger?: Logger;\n}\n\n/** Origins that already produced the http-to-https warning in this process. */\nconst warnedOrigins = new Set<string>();\n\n/** Forgets which origins were warned about. For tests only. */\nexport const resetSchemeUpgradeWarnings = (): void => {\n warnedOrigins.clear();\n};\n\nconst isRedirect = (response: Response): boolean =>\n response.type === \"opaqueredirect\" || REDIRECT_STATUSES.has(response.status);\n\nconst isStream = (body: unknown): boolean =>\n typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream;\n\nconst abortError = (signal: AbortSignal): Error => {\n const reason: unknown = signal.reason;\n if (reason instanceof Error) return reason;\n if (typeof DOMException !== \"undefined\") {\n return new DOMException(\"This operation was aborted\", \"AbortError\");\n }\n return Object.assign(new Error(\"This operation was aborted\"), { name: \"AbortError\" });\n};\n\n/**\n * Releases the unread copy of a request body.\n *\n * `Request.clone` tees the body stream, and a branch nobody reads holds every\n * chunk the other branch consumes in memory. Cancelling the copy the replay\n * never needs keeps a streamed upload from being buffered whole.\n *\n * The cancellation is never awaited: a tee only settles the promise its\n * `cancel` returns once both branches are cancelled, so waiting on the copy\n * while the sent branch is still live would never return.\n */\nconst discard = (spare: Request | null): void => {\n void spare?.body?.cancel().catch(() => undefined);\n};\n\n/**\n * The body bytes to replay, read under the caller's signal.\n *\n * A `Request` built from a stream hands its copy over as a stream too, and\n * reading one that never ends would leave the call pending for good, past an\n * abort the caller already made. The read races the signal and cancels the\n * copy it loses to, so an aborted call settles.\n */\nconst replayBody = async ({\n spare,\n signal,\n}: {\n spare: Request;\n signal: AbortSignal | null | undefined;\n}): Promise<ArrayBuffer> => {\n if (!signal) return spare.arrayBuffer();\n if (signal.aborted) throw abortError(signal);\n\n const aborted = new Promise<never>((_, reject) => {\n signal.addEventListener(\"abort\", () => reject(abortError(signal)), { once: true });\n });\n // An abort that arrives after the read already won still rejects this one,\n // and nothing would be waiting on it by then.\n void aborted.catch(() => undefined);\n try {\n return await Promise.race([spare.arrayBuffer(), aborted]);\n } catch (error) {\n discard(spare);\n throw error;\n }\n};\n\nconst requestUrl = (input: RequestInfo | URL): string => {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return input.url;\n};\n\nconst parseHop = ({\n url,\n location,\n}: {\n url: string;\n location: string;\n}): { from: URL; to: URL } | null => {\n try {\n const from = new URL(url);\n return { from, to: new URL(location, from) };\n } catch {\n return null;\n }\n};\n\n/** Same host and port, with the scheme changed from http to https. */\nconst isSchemeUpgrade = ({ from, to }: { from: URL; to: URL }): boolean =>\n from.protocol === \"http:\" &&\n to.protocol === \"https:\" &&\n from.hostname === to.hostname &&\n from.port === to.port;\n\n/**\n * The https URL to replay against when `location` only upgrades the scheme of\n * `url`, and null for any other target. The URL parser drops a default port,\n * so `http://host:80` and `https://host:443` both read as no port.\n */\nexport const schemeUpgradeTarget = ({\n url,\n location,\n}: {\n url: string;\n location: string;\n}): string | null => {\n const hop = parseHop({ url, location });\n if (hop === null || !isSchemeUpgrade(hop)) return null;\n const { from, to } = hop;\n if (from.pathname !== to.pathname) return null;\n if (from.search !== to.search) return null;\n to.hash = \"\";\n return to.href;\n};\n\n/**\n * The URL a GET or HEAD follows to, and null when the hop is refused: a\n * target that is not http or https, or a downgrade from https to http.\n */\nexport const followTarget = ({\n url,\n location,\n}: {\n url: string;\n location: string;\n}): string | null => {\n const hop = parseHop({ url, location });\n if (hop === null) return null;\n const { from, to } = hop;\n if (to.protocol !== \"http:\" && to.protocol !== \"https:\") return null;\n if (from.protocol === \"https:\" && to.protocol === \"http:\") return null;\n to.hash = \"\";\n return to.href;\n};\n\n/** A hop keeps its credential headers on the same origin and on an https upgrade of the same host. */\nconst keepsCredentials = ({ from, to }: { from: URL; to: URL }): boolean =>\n from.origin === to.origin || isSchemeUpgrade({ from, to });\n\nconst withoutCredentials = (headers: Headers): Headers => {\n const stripped = new Headers(headers);\n for (const name of CREDENTIAL_HEADERS) stripped.delete(name);\n return stripped;\n};\n\nconst warnOnce = ({ url, logger }: { url: string; logger: Logger }): void => {\n const { origin, host } = new URL(url);\n if (warnedOrigins.has(origin)) return;\n warnedOrigins.add(origin);\n logger.warn(\n `LangWatch endpoint ${origin} redirected to https. Set the endpoint to https://${host} to skip the extra round trip.`,\n );\n};\n\nconst refusalOf = ({\n url,\n response,\n}: {\n url: string;\n response: Response;\n}): LangWatchRedirectError =>\n new LangWatchRedirectError({\n url,\n location: response.headers.get(\"location\"),\n status: response.status,\n });\n\n/**\n * What `fetch(input, init)` would send, as one request both sends read from.\n * `init` wins over a `Request` input field by field, so reading the raw input\n * for the replay would resend a method, headers or body the caller overrode.\n * A plain URL input stays null: the non-Request path keeps `init` as it is, so\n * a stream body reaches the transport untouched.\n */\nconst effectiveRequest = ({\n input,\n init,\n}: {\n input: RequestInfo | URL;\n init: RequestInit | undefined;\n}): Request | null =>\n typeof Request !== \"undefined\" && input instanceof Request\n ? new Request(input, { ...init, redirect: \"manual\" })\n : null;\n\ninterface Hop {\n send: LangWatchFetch;\n log: Logger;\n effective: Request | null;\n init: RequestInit | undefined;\n url: string;\n first: Response;\n}\n\n/** The GET and HEAD rule: follow with the same method, up to MAX_FOLLOW_HOPS. */\nconst follow = async ({\n send,\n log,\n effective,\n init,\n url,\n method,\n first,\n}: Hop & { method: string }): Promise<Response> => {\n let headers = new Headers(effective?.headers ?? init?.headers);\n const signal = effective?.signal ?? init?.signal;\n let current = url;\n let response = first;\n\n for (let hop = 0; hop < MAX_FOLLOW_HOPS; hop++) {\n const location = response.headers.get(\"location\");\n const target = location === null ? null : followTarget({ url: current, location });\n if (target === null) throw refusalOf({ url: current, response });\n\n const from = new URL(current);\n const to = new URL(target);\n if (!keepsCredentials({ from, to })) headers = withoutCredentials(headers);\n if (isSchemeUpgrade({ from, to })) warnOnce({ url: current, logger: log });\n\n current = target;\n response = effective\n ? await send(new Request(target, { method, headers, signal, redirect: \"manual\" }))\n : await send(target, { ...init, method, headers, body: undefined, redirect: \"manual\" });\n if (!isRedirect(response)) return response;\n }\n\n throw refusalOf({ url: current, response });\n};\n\n/** The rule for every other method: one hop, and only an https upgrade of the same URL. */\nconst upgrade = async ({\n send,\n log,\n effective,\n init,\n url,\n first,\n spare,\n}: Hop & { spare: Request | null }): Promise<Response> => {\n const location = first.headers.get(\"location\");\n const refused = refusalOf({ url, response: first });\n const target = location === null ? null : schemeUpgradeTarget({ url, location });\n if (location === null || first.status === 303 || target === null || isStream(init?.body)) {\n discard(spare);\n throw refused;\n }\n\n warnOnce({ url, logger: log });\n\n const second = effective\n ? await send(\n new Request(target, {\n method: effective.method,\n headers: effective.headers,\n body: spare ? await replayBody({ spare, signal: effective.signal }) : null,\n signal: effective.signal,\n redirect: \"manual\",\n }),\n )\n : await send(target, { ...init, redirect: \"manual\" });\n if (!isRedirect(second)) return second;\n\n throw refusalOf({ url: target, response: second });\n};\n\n/**\n * Builds a `fetch` that applies the redirect rule. Pass `fetch` to send through\n * another transport (a test double, a proxying client) and `logger` to route\n * the http endpoint warning.\n */\nexport const createLangWatchFetch = ({\n fetch: fetchImpl,\n logger,\n}: CreateLangWatchFetchOptions = {}): LangWatchFetch => {\n const send: LangWatchFetch =\n fetchImpl ?? ((input, init) => globalThis.fetch(input, init));\n const log = logger ?? new ConsoleLogger({ level: \"warn\", prefix: \"LangWatch\" });\n\n return async (input, init) => {\n const url = requestUrl(input);\n const effective = effectiveRequest({ input, init });\n const method = (effective?.method ?? init?.method ?? \"GET\").toUpperCase();\n // A Request carries its body as a stream that one send consumes, so a copy\n // is taken before the first send and read only if the replay happens.\n const spare = effective !== null && effective.body !== null ? effective.clone() : null;\n\n const first = effective\n ? await send(effective)\n : await send(input, { ...init, redirect: \"manual\" });\n if (!isRedirect(first)) {\n discard(spare);\n return first;\n }\n\n const hop = { send, log, effective, init, url, first };\n if (!FOLLOWING_METHODS.has(method)) return upgrade({ ...hop, spare });\n\n discard(spare);\n return follow({ ...hop, method });\n };\n};\n\n/** The shared client, bound to the global `fetch` and the SDK console logger. */\nexport const langwatchFetch: LangWatchFetch = createLangWatchFetch();\n","/**\n * Error class for template compilation issues\n */\nexport class PromptCompilationError extends Error {\n constructor(\n message: string,\n public readonly template: string,\n public readonly originalError?: any,\n ) {\n super(message);\n this.name = \"PromptCompilationError\";\n }\n}\n","import { type z } from \"zod\";\n\n/**\n * Error class for prompt validation issues\n */\nexport class PromptValidationError extends Error {\n constructor(message: string, public readonly validationErrors: z.ZodError) {\n super(message);\n this.name = \"PromptValidationError\";\n }\n}\n","/**\n * Base error class for the Prompts domain.\n * All prompt-related errors should extend this class.\n */\nexport class PromptsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PromptsError\";\n }\n}\n","import { PromptsError } from \"./prompts.error\";\n\n/**\n * Error class for Prompts API operations.\n * Provides context about the failed operation and the original error.\n */\nexport class PromptsApiError extends PromptsError {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"PromptsApiError\";\n }\n}\n","/**\n * A sentence for a failure the API named but did not write prose for.\n *\n * The REST error envelope sends `message` equal to `code` on purpose: a\n * handled error's message is server copy, and the framework will not forward\n * it to an external caller. That leaves the code as the only thing describing\n * what happened, and printing it verbatim gives the user a line like\n * \"Failed to fetch the organization: enterprise_plan_required\".\n *\n * So the client renders its own copy, keyed on the code, the same way the web\n * app does. Two layers, in order:\n *\n * 1. `SENTENCE_BY_CODE`, for codes whose slug reads wrong on its own or\n * leaves out the one fact the reader needs.\n * 2. Otherwise the slug, humanized. `organization_slug_taken` becomes\n * \"Organization slug taken\", which is plain and never misleading, so an\n * unlisted code degrades into something readable rather than into\n * punctuation.\n *\n * What to DO about a failure lives in the CLI's `errorSuggestions.ts`; this is\n * only what happened. Keys are exact codes, never prefixes: a new code that\n * lands nowhere gets humanized, not filed under a neighbour.\n */\n\nconst SENTENCE_BY_CODE: Record<string, string> = {\n enterprise_plan_required: \"This capability needs the Enterprise plan\",\n insufficient_permissions:\n \"This credential does not carry the permission this call needs\",\n missing_credentials: \"This request carried no API key\",\n invalid_credentials: \"That API key is not valid\",\n organization_not_found:\n \"That API key does not resolve to an organization on this instance\",\n validation_error: \"The request did not pass validation\",\n custom_role_in_use: \"That role is still held by a member or a role binding\",\n custom_role_id_required:\n \"A CUSTOM role binding has to name which custom role it grants\",\n custom_role_not_assignable: \"That custom role cannot be granted here\",\n custom_role_name_reserved: \"That role name is reserved\",\n custom_role_name_taken: \"A role with that name already exists here\",\n role_binding_principal_invalid:\n \"A role binding names exactly one principal: a user, a group, or an API key\",\n role_binding_already_exists:\n \"That principal already holds that role at that scope\",\n org_exclusive_permission_scope:\n \"That role carries an organization-only permission, so it cannot be bound to a team or a project\",\n member_seat_limit_reached: \"The plan's member seats are all in use\",\n personal_workspace_not_managed_here:\n \"Personal workspaces are not managed through this API\",\n already_organization_member: \"They are already in this organization\",\n user_not_in_organization: \"That user is not a member of this organization\",\n group_not_in_organization: \"That group does not belong to this organization\",\n api_key_not_in_organization:\n \"That API key does not belong to this organization\",\n team_not_in_organization: \"That team does not belong to this organization\",\n scope_not_in_organization: \"That scope does not belong to this organization\",\n // Humanizing turns an acronym into a word: \"Api key not found\", \"Scim token\n // not found\". Spelled out here instead.\n api_key_not_found: \"API key not found\",\n api_key_not_owned: \"That API key belongs to somebody else\",\n api_key_already_revoked: \"That API key is already revoked\",\n scim_token_not_found: \"SCIM token not found\",\n duplicate_invite: \"They already have a pending invite\",\n cannot_remove_self: \"A credential cannot remove its own member\",\n cannot_disable_self: \"A credential cannot disable its own member\",\n cannot_remove_last_admin:\n \"That is the organization's last administrator, so it has to keep them\",\n api_key_scope_violation: \"That API key cannot be given that access\",\n};\n\n/** `organization_slug_taken` -> `Organization slug taken`. */\nconst humanizeCode = (code: string): string => {\n const words = code.replace(/[_-]+/g, \" \").trim();\n if (!words) return code;\n return words.charAt(0).toUpperCase() + words.slice(1);\n};\n\n/**\n * True when the body's `message` says nothing the `code` did not: the envelope\n * forwarded the code, so there is no server prose to prefer over our copy.\n */\nexport const isCodeAsMessage = ({\n code,\n message,\n}: {\n code: unknown;\n message: unknown;\n}): boolean =>\n typeof code === \"string\" &&\n code.length > 0 &&\n typeof message === \"string\" &&\n message.trim() === code;\n\n/** The sentence to show for a code the API named. */\nexport const sentenceForCode = (code: string): string =>\n SENTENCE_BY_CODE[code] ?? humanizeCode(code);\n\n/**\n * True for a value shaped like one of our error codes: lower case throughout,\n * words joined by underscores. That is what separates `invalid_credentials`\n * and `unauthorized` from the reason phrases, class names and free text that\n * share the same envelope fields (\"Conflict\", \"NotFoundError\", \"UNEXPECTED\",\n * \"connection refused\"), which are prose, and prose is not a code.\n */\nexport const looksLikeErrorCode = (value: unknown): value is string =>\n typeof value === \"string\" && /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/.test(value);\n","/**\n * Extracts the most informative, user-facing message from an API error body.\n *\n * Errors from the LangWatch API follow the shape `{ error: string, message?: string }`\n * per `errorSchema` in the server. In production the middleware may return a\n * generic `{ error: \"Internal server error\", message: \"Internal server error\" }`\n * which is useless to a user — this helper at least falls back to stringifying\n * the raw body so no diagnostic information is lost.\n *\n * Priority (first non-generic, non-empty wins):\n * 1. `body.message` (descriptive sentence from the server)\n * 2. `body.error` (error kind — \"NotFoundError\", \"Conflict\", …)\n * 3. Any other string fields on the body object (e.g. `detail`, `reason`)\n * 4. JSON stringification of the entire body\n * 5. `Error#message` if the input is a thrown Error\n * 6. A status-code-derived fallback, if available\n */\nimport {\n isCodeAsMessage,\n looksLikeErrorCode,\n sentenceForCode,\n} from \"./error-code-copy\";\n\nconst GENERIC_MESSAGES = new Set([\n \"\",\n // The bare class name some envelopes carry in their `error` field; as a\n // prefix it can only turn \"enterprise_plan_required\" into\n // \"Error: enterprise_plan_required\".\n \"error\",\n \"internal server error\",\n \"unknown error\",\n \"unknown error occurred\",\n]);\n\nfunction isGeneric(s: string): boolean {\n return GENERIC_MESSAGES.has(s.trim().toLowerCase());\n}\n\nfunction firstMeaningful(...candidates: Array<unknown>): string | undefined {\n for (const c of candidates) {\n if (typeof c === \"string\" && !isGeneric(c)) return c;\n }\n return undefined;\n}\n\nfunction collectAllOwnPropertyNames(value: unknown, seen = new Set<unknown>()): string[] {\n if (!value || typeof value !== \"object\" || seen.has(value)) return [];\n seen.add(value);\n const names = new Set<string>();\n for (const name of Object.getOwnPropertyNames(value)) {\n names.add(name);\n try {\n const child = (value as Record<string, unknown>)[name];\n for (const nested of collectAllOwnPropertyNames(child, seen)) {\n names.add(nested);\n }\n } catch {\n // Ignore getter side effects.\n }\n }\n return Array.from(names);\n}\n\ninterface ZodIssue {\n path?: unknown;\n message?: unknown;\n}\n\n/**\n * Renders a Zod validation error body into a user-readable string. Returns\n * undefined if `body` is not a Zod error shape.\n *\n * Examples:\n * { name: \"ZodError\", issues: [{ path: [\"format\"], message: \"Invalid enum value...\" }] }\n * → \"Validation failed: format — Invalid enum value...\"\n * { name: \"ZodError\", issues: [<issue1>, <issue2>] }\n * → \"Validation failed: a.b — msg1; c — msg2\"\n */\nfunction formatZodIssues(body: Record<string, unknown>): string | undefined {\n const isZod =\n body.name === \"ZodError\" ||\n (Array.isArray(body.issues) && body.issues.length > 0);\n if (!isZod || !Array.isArray(body.issues)) return undefined;\n\n const rendered = (body.issues as ZodIssue[])\n .map((issue) => {\n const pathArr = Array.isArray(issue.path) ? issue.path : [];\n const path = pathArr\n .filter((p) => typeof p === \"string\" || typeof p === \"number\")\n .join(\".\");\n const msg = typeof issue.message === \"string\" ? issue.message : \"\";\n if (path && msg) return `${path} — ${msg}`;\n if (msg) return msg;\n if (path) return path;\n return undefined;\n })\n .filter((s): s is string => typeof s === \"string\" && s.length > 0);\n\n if (rendered.length === 0) return undefined;\n return `Validation failed: ${rendered.join(\"; \")}`;\n}\n\nfunction stringifyBody(body: unknown): string {\n try {\n // Use all own property names (including non-enumerable ones, common on\n // native Error instances and fetch errors) so we preserve every field the\n // server sent, at any nesting depth.\n if (body && typeof body === \"object\") {\n return JSON.stringify(body, collectAllOwnPropertyNames(body));\n }\n return JSON.stringify(body);\n } catch {\n return String(body);\n }\n}\n\nexport interface FormatApiErrorOptions {\n /**\n * HTTP status code from the response, when known. Used as part of the\n * fallback output so that the user has at least some actionable signal.\n */\n status?: number;\n}\n\nexport interface FormatApiErrorMessageParams {\n error: unknown;\n /** Currently only `status` — kept as an object for forward extension. */\n options?: FormatApiErrorOptions;\n}\n\nexport function formatApiErrorMessage({\n error,\n options = {},\n}: FormatApiErrorMessageParams): string {\n if (error == null) {\n return options.status\n ? `Request failed with status ${options.status}`\n : \"Unknown error occurred\";\n }\n\n if (typeof error === \"string\") {\n return isGeneric(error) && options.status\n ? `${error} (status ${options.status})`\n : error;\n }\n\n if (error instanceof Error) {\n // Node's fetch wraps transport failures as `TypeError: fetch failed` with\n // the real reason (ECONNREFUSED, ENOTFOUND, timeout, etc.) on `.cause`.\n // Surface that so the user can tell whether the endpoint is wrong, the\n // server is down, or DNS can't resolve the host.\n const base = error.message || \"Unknown error occurred\";\n const cause = (error as { cause?: unknown }).cause;\n const causeMsg =\n cause instanceof Error\n ? cause.message\n : cause && typeof cause === \"object\" &&\n typeof (cause as { message?: unknown }).message === \"string\"\n ? (cause as { message: string }).message\n : undefined;\n const causeCode =\n cause && typeof cause === \"object\" &&\n typeof (cause as { code?: unknown }).code === \"string\"\n ? (cause as { code: string }).code\n : undefined;\n\n const detail = [causeCode, causeMsg && causeMsg !== base ? causeMsg : undefined]\n .filter((s): s is string => typeof s === \"string\" && s.length > 0)\n .join(\": \");\n const formatted = detail ? `${base} (${detail})` : base;\n\n // Node fetch emits \"TypeError: fetch failed\" with `cause.message =\n // \"unknown scheme\"` when the URL has no/invalid scheme (e.g. the user\n // set LANGWATCH_ENDPOINT=localhost:5570 instead of http://localhost:5570).\n // Add a hint — the raw phrase tells the user nothing actionable.\n const combined = `${base} ${causeMsg ?? \"\"} ${causeCode ?? \"\"}`.toLowerCase();\n if (\n combined.includes(\"unknown scheme\") ||\n combined.includes(\"err_invalid_url\") ||\n combined.includes(\"failed to parse url\")\n ) {\n return `${formatted} — check your LANGWATCH_ENDPOINT (must start with http:// or https://)`;\n }\n return formatted;\n }\n\n if (typeof error === \"object\") {\n const body = error as Record<string, unknown>;\n\n // Zod validation errors: `{ name: \"ZodError\", issues: [{ path, message }] }`.\n // Without this they render as unreadable raw JSON to the user.\n const zod = formatZodIssues(body);\n if (zod) return zod;\n\n // Most specific fields first.\n const fromMessage = typeof body.message === \"string\" ? body.message : undefined;\n const fromError = typeof body.error === \"string\" ? body.error : undefined;\n\n // Two envelopes name the failure in different fields: the framework one\n // puts the code in `code`, the older one puts it in `error`. Either way the\n // code is an identifier, not a sentence — it belongs in the detail lines\n // the caller prints under the message, never in front of the prose.\n const codeField = looksLikeErrorCode(body.code)\n ? body.code\n : looksLikeErrorCode(fromError)\n ? fromError\n : undefined;\n\n if (codeField) {\n // The framework envelope forwards the code as the message rather than\n // the server's own prose (see `error-code-copy.ts`), so `message` there\n // says nothing `code` did not. Our own sentence for the code is the\n // difference between \"This capability needs the Enterprise plan\" and the\n // slug itself. When the server did write prose, it wins.\n if (\n !isCodeAsMessage({ code: codeField, message: fromMessage }) &&\n fromMessage &&\n !isGeneric(fromMessage)\n ) {\n return fromMessage;\n }\n return sentenceForCode(codeField);\n }\n\n const fromDetail = typeof body.detail === \"string\" ? body.detail : undefined;\n const fromReason = typeof body.reason === \"string\" ? body.reason : undefined;\n\n // 1. Top-level meaningful fields take priority. If the server gave us a\n // descriptive `message`/`error`/`detail`/`reason`, use that — even if\n // `body.error` is an object with its own (potentially generic) message.\n const meaningful = firstMeaningful(fromMessage, fromError, fromDetail, fromReason);\n if (meaningful) {\n if (\n fromError &&\n fromMessage &&\n fromMessage !== fromError &&\n !isGeneric(fromError) &&\n !isGeneric(fromMessage)\n ) {\n return `${fromError}: ${fromMessage}`;\n }\n return meaningful;\n }\n\n // 2. Nested `body.error` — only used as a fallback when no top-level\n // field carried a useful message. Native Error instances and\n // tRPC-style envelopes both fit here.\n if (body.error && typeof body.error === \"object\") {\n const nested = body.error as Record<string, unknown>;\n\n if (body.error instanceof Error && body.error.message) {\n return body.error.message;\n }\n\n // Zod validation envelopes: `{ success: false, error: { name: \"ZodError\", issues: [...] } }`\n const nestedZod = formatZodIssues(nested);\n if (nestedZod) return nestedZod;\n\n const fromNestedMsg = typeof nested.message === \"string\" ? nested.message : undefined;\n const fromNestedErr = typeof nested.error === \"string\" ? nested.error : undefined;\n const nestedMeaningful = firstMeaningful(fromNestedMsg, fromNestedErr);\n if (nestedMeaningful) {\n return nestedMeaningful;\n }\n\n // Stringify the nested object so identifiers like `{ code, status }`\n // still reach the user.\n const nestedRaw = stringifyBody(nested);\n if (nestedRaw && nestedRaw !== \"{}\") {\n return nestedRaw;\n }\n }\n\n // 3. No meaningful top-level or nested fields — dump the raw JSON so\n // the user at least sees the server payload. Attach status for\n // context.\n const raw = stringifyBody(body);\n\n // Collapse empty / near-empty payloads to a friendlier message — there's\n // nothing for the user to see in `{}` anyway.\n if (!raw || raw === \"{}\" || raw === '\"\"' || raw === \"null\") {\n return options.status\n ? `Request failed with status ${options.status}`\n : \"Unknown error occurred\";\n }\n\n const withStatus = options.status ? `status ${options.status} ${raw}` : raw;\n return `server returned ${withStatus}`;\n }\n\n // Primitive types (number, boolean, bigint, symbol) — coerce safely.\n // We've already handled string, null/undefined, Error, and object above.\n try {\n return String(error as number | boolean | bigint);\n } catch {\n return \"Unknown error occurred\";\n }\n}\n\nexport interface FormatApiErrorForOperationParams {\n operation: string;\n error: unknown;\n options?: FormatApiErrorOptions;\n}\n\n/**\n * Builds a fully-qualified error message for a specific operation, including\n * the operation name and the extracted server-side message.\n */\nexport function formatApiErrorForOperation({\n operation,\n error,\n options = {},\n}: FormatApiErrorForOperationParams): string {\n const message = formatApiErrorMessage({ error, options });\n return `Failed to ${operation}: ${message}`;\n}\n\n/**\n * Attempts to read a status code from common response-shaped wrappers without\n * assuming a particular SDK. Returns undefined if none is found.\n */\nexport function extractStatusFromResponse(value: unknown): number | undefined {\n if (!value || typeof value !== \"object\") return undefined;\n const obj = value as Record<string, unknown>;\n if (typeof obj.status === \"number\") return obj.status;\n if (typeof obj.statusCode === \"number\") return obj.statusCode;\n if (obj.response && typeof obj.response === \"object\") {\n const resp = obj.response as Record<string, unknown>;\n if (typeof resp.status === \"number\") return resp.status;\n }\n return undefined;\n}\n","import { z } from \"zod\";\nimport type { paths, operations } from \"@/internal/generated/openapi/api-client\";\nimport { type PromptResponse, type TagDefinition, type CreatedTag } from \"./types\";\nimport { PromptConverter } from \"@/cli/utils/promptConverter\";\nimport { PromptServiceTracingDecorator, tracer } from \"./tracing\";\nimport { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { type CreatePromptBody, type UpdatePromptBody } from \"./types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport { isLangWatchHandledError } from \"@/internal/api/errors\";\nimport { PromptsApiError } from \"./errors\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n formatApiErrorMessage,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport type { RuntimeParameters } from \"@/cli/types\";\n\nconst syncActionSchema = z.enum([\n \"created\",\n \"updated\",\n \"conflict\",\n \"up_to_date\",\n]);\n\nexport type SyncAction = z.infer<typeof syncActionSchema>;\n\nconst syncResultSchema = z.object({\n action: syncActionSchema,\n // `prompt` and `conflictInfo` are passed through untyped — they come from\n // the OpenAPI-derived shape which is already validated on the server side.\n prompt: z.unknown().optional(),\n conflictInfo: z\n .object({\n localVersion: z.number(),\n remoteVersion: z.number(),\n differences: z.array(z.string()),\n remoteConfigData: z.unknown(),\n })\n .passthrough()\n .optional(),\n});\n\nexport type AssignTagResult = NonNullable<\n operations[\"putApiPromptsByIdTagsByTag\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>;\n\nexport type ConfigData = NonNullable<\n paths[\"/api/prompts/{id}/sync\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"][\"configData\"];\n\nexport interface SyncResult {\n action: SyncAction;\n prompt?: PromptResponse;\n conflictInfo?: {\n localVersion: number;\n remoteVersion: number;\n differences: string[];\n remoteConfigData: ConfigData;\n remoteParameters?: RuntimeParameters;\n };\n}\n\n/**\n * Service for managing prompt resources via the Langwatch API.\n * Constructor creates a proxy that wraps the service and traces all methods.\n *\n * Responsibilities:\n * - CRUD operations for prompts\n * - Creating prompt versions\n * - Error handling with contextual information\n *\n * All methods return raw PromptResponse data from the API.\n */\nexport class PromptsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n\n /**\n * Wraps the service in a tracing proxy via the decorator.\n */\n return createTracingProxy(\n this as PromptsApiService,\n tracer,\n PromptServiceTracingDecorator,\n );\n }\n\n /**\n * Handles API errors by throwing a PromptsApiError with operation context.\n * @param operation Description of the operation being performed.\n * @param error The error object returned from the API client.\n * @throws {PromptsApiError}\n */\n private handleApiError(operation: string, error: any, status?: number): never {\n const resolvedStatus = status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: resolvedStatus,\n } });\n\n throw new PromptsApiError(message, operation, error);\n }\n\n /**\n * Fetches all prompts from the API.\n * @returns Array of raw PromptResponse data.\n * @throws {PromptsApiError} If the API call fails.\n */\n async getAll(): Promise<PromptResponse[]> {\n const { data, error } =\n await this.apiClient.GET(\"/api/prompts\");\n if (error) this.handleApiError(\"fetch all prompts\", error);\n return data;\n }\n\n /**\n * Fetches a single prompt by its ID.\n * @param id The prompt's unique identifier.\n * @param options Optional parameters for the request.\n * @param options.version Specific version to fetch (numeric string or \"latest\").\n * @param options.tag Tag to fetch (e.g., \"production\", \"staging\", or a custom tag).\n * @returns Raw PromptResponse data.\n * @throws {PromptsApiError} If the API call fails.\n */\n get = async (id: string, options?: { version?: string; tag?: string }): Promise<PromptResponse> => {\n // Parse version to number, skip for \"latest\" or invalid values\n const versionNumber = options?.version && options.version !== \"latest\"\n ? parseInt(options.version, 10)\n : undefined;\n\n const { data, error } = await this.apiClient.GET(\n \"/api/prompts/{id}\",\n {\n params: {\n path: { id },\n query: {\n version: Number.isNaN(versionNumber) ? undefined : versionNumber,\n tag: options?.tag,\n },\n },\n },\n );\n\n if (error) {\n this.handleApiError(`fetch prompt with ID \"${id}\"`, error);\n }\n\n return data;\n }\n\n /**\n * Validates if a prompt exists.\n * @param id The prompt's unique identifier.\n * @returns True if prompt exists, false otherwise.\n * @throws {PromptsApiError} If the API call fails (not 404).\n */\n async exists(id: string): Promise<boolean> {\n try {\n await this.get(id);\n return true;\n } catch (error) {\n // A named 404 from the platform (\"prompt_not_found\") now arrives typed, so\n // read the status off the domain error first. Without this, the very thing\n // that makes the failure legible — the platform naming it — would stop\n // `exists()` recognising a missing prompt and turn a `false` into a throw.\n if (isLangWatchHandledError(error) && error.httpStatus === 404) {\n return false;\n }\n\n const originalError = error instanceof PromptsApiError ? error.originalError : null;\n const statusCode = originalError != null && typeof originalError === \"object\" && \"statusCode\" in originalError\n ? (originalError as { statusCode: unknown }).statusCode\n : null;\n\n if (statusCode === 404) {\n return false;\n }\n\n throw error;\n }\n }\n\n /**\n * Creates a new prompt.\n * @param params The prompt creation payload, matching the OpenAPI schema.\n * @returns Raw PromptResponse data of the created prompt.\n * @throws {PromptsApiError} If the API call fails.\n */\n async create(params: CreatePromptBody): Promise<PromptResponse> {\n const { data, error } = await this.apiClient.POST(\n \"/api/prompts\",\n {\n body: params,\n },\n );\n if (error) this.handleApiError(\"create prompt\", error);\n return data;\n }\n\n /**\n * Updates an existing prompt.\n * @param id The prompt's unique identifier.\n * @param params The update payload, matching the OpenAPI schema.\n * @returns Raw PromptResponse data of the updated prompt.\n * @throws {PromptsApiError} If the API call fails.\n */\n async update(id: string, params: UpdatePromptBody): Promise<PromptResponse> {\n const { error, data: updatedPrompt } =\n await this.apiClient.PUT(\"/api/prompts/{id}\", {\n params: { path: { id } },\n body: params,\n });\n if (error) this.handleApiError(`update prompt with ID \"${id}\"`, error);\n return updatedPrompt;\n }\n\n /**\n * Lists all prompt tags (built-in and custom) for the organization.\n * @returns Array of tag definitions.\n * @throws {PromptsApiError} If the API call fails.\n */\n async listTags(): Promise<TagDefinition[]> {\n const { data, error } = await this.apiClient.GET(\"/api/prompts/tags\");\n if (error) this.handleApiError(\"list tags\", error);\n return data;\n }\n\n /**\n * Creates a custom prompt tag for the organization.\n * @param params.name The tag name (must match /^[a-z][a-z0-9_-]*$/).\n * @returns The created tag.\n * @throws {PromptsApiError} If the API call fails.\n */\n async createTag({ name }: { name: string }): Promise<CreatedTag> {\n const { data, error } = await this.apiClient.POST(\"/api/prompts/tags\", {\n body: { name },\n });\n if (error) this.handleApiError(\"create tag\", error);\n return data;\n }\n\n /**\n * Deletes a custom prompt tag by name.\n * @param tagName The tag name to delete.\n * @throws {PromptsApiError} If the API call fails.\n */\n async deleteTag(tagName: string): Promise<void> {\n const { error } = await this.apiClient.DELETE(\n \"/api/prompts/tags/{tag}\" as any,\n { params: { path: { tag: tagName } } } as any,\n );\n if (error) this.handleApiError(`delete tag \"${tagName}\"`, error);\n }\n\n /**\n * Renames an existing prompt tag.\n * @param tag The current tag name.\n * @param name The new tag name.\n * @throws {PromptsApiError} If the API call fails.\n */\n async renameTag({ tag, name }: { tag: string; name: string }): Promise<void> {\n const { error } = await this.apiClient.PUT(\n \"/api/prompts/tags/{tag}\",\n { params: { path: { tag } }, body: { name } },\n );\n if (error) this.handleApiError(`rename tag \"${tag}\"`, error);\n }\n\n async assignTag({\n id,\n tag,\n versionId,\n }: {\n id: string;\n tag: string;\n versionId: string;\n }): Promise<AssignTagResult> {\n const { data, error } = await this.apiClient.PUT(\n \"/api/prompts/{id}/tags/{tag}\",\n {\n params: { path: { id, tag } },\n body: { versionId },\n },\n );\n if (error) this.handleApiError(`assign tag \"${tag}\" to prompt \"${id}\"`, error);\n return data;\n }\n\n /**\n * Deletes a prompt by its ID.\n * @param id The prompt's unique identifier.\n * @throws {PromptsApiError} If the API call fails.\n */\n async delete(id: string): Promise<{ success: boolean }> {\n const { data, error } = await this.apiClient.DELETE(\n \"/api/prompts/{id}\",\n {\n params: { path: { id } },\n },\n );\n if (error) this.handleApiError(`delete prompt with ID \"${id}\"`, error);\n\n return data;\n }\n\n /**\n * Fetches all versions for a given prompt.\n * @param id The prompt's unique identifier.\n * @returns Array of raw PromptResponse data for each version.\n * @throws {PromptsApiError} If the API call fails.\n */\n async getVersions(id: string): Promise<PromptResponse[]> {\n const { data, error } = await this.apiClient.GET(\n \"/api/prompts/{id}/versions\",\n {\n params: { path: { id } },\n },\n );\n if (error)\n this.handleApiError(`fetch versions for prompt with ID \"${id}\"`, error);\n\n return data;\n }\n\n /**\n * Upserts a prompt with local configuration - creates if doesn't exist, updates version if exists.\n * @param handle The prompt's handle/identifier.\n * @param config Local prompt configuration.\n * @returns Object with created flag and raw PromptResponse data.\n * @throws {PromptsApiError} If the API call fails.\n */\n async upsert(\n handle: string,\n config: {\n model: string;\n modelParameters?: {\n temperature?: number;\n max_tokens?: number;\n };\n messages: Array<{\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n }>;\n parameters?: RuntimeParameters;\n },\n ): Promise<{ created: boolean; prompt: PromptResponse }> {\n const payload = {\n handle,\n model: config.model,\n prompt: PromptConverter.extractSystemPrompt(config.messages),\n messages: PromptConverter.filterNonSystemMessages(config.messages),\n temperature: config.modelParameters?.temperature,\n maxTokens: config.modelParameters?.max_tokens,\n inputs: [{ identifier: \"input\", type: \"str\" as const }],\n outputs: [{ identifier: \"output\", type: \"str\" as const }],\n parameters: config.parameters ?? {},\n commitMessage: `Updated via CLI sync`,\n schemaVersion: \"1.0\" as const,\n };\n\n // Creating a prompt with the same handle will fail, so we try to update instead\n try {\n const prompt = await this.create(payload);\n return {\n created: true,\n prompt,\n };\n } catch {\n const prompt = await this.update(handle, payload);\n\n return {\n created: false,\n prompt,\n };\n }\n }\n\n /**\n * Sync a prompt with local content, handling conflicts and version management\n * You probably don't need to use this method directly.\n */\n async sync(params: {\n name: string;\n configData: ConfigData;\n parameters?: RuntimeParameters;\n localVersion?: number;\n commitMessage?: string;\n }): Promise<SyncResult> {\n // openapi-fetch returns `{ data?, error?, response }`; we only need\n // these fields from the response so an explicit shape keeps the\n // no-redundant-type-constituents lint happy (the generic POST return is\n // widened to `any` by the generated types).\n interface SyncApiResponse {\n data?: unknown;\n error?: unknown;\n response?: { status?: number };\n }\n let response: SyncApiResponse | undefined;\n try {\n response = await this.apiClient.POST(\n \"/api/prompts/{id}/sync\",\n {\n params: { path: { id: params.name } },\n body: {\n configData: params.configData,\n parameters: params.parameters ?? {},\n localVersion: params.localVersion,\n commitMessage: params.commitMessage,\n },\n },\n );\n } catch (error) {\n // Transport-level failures (network errors, timeouts, unresolved DNS)\n // surface here. Preserve the underlying message so the user knows\n // whether the API is reachable.\n const message = formatApiErrorForOperation({ operation: \"sync prompt\", error: error });\n throw new PromptsApiError(message, \"sync\", error);\n }\n\n if (response?.error) {\n const err: unknown = response.error;\n const status =\n response.response?.status ?? extractStatusFromResponse(err);\n const message = formatApiErrorMessage({ error: err, options: { status } });\n throw new PromptsApiError(\n `Failed to sync prompt: ${message}`,\n \"sync\",\n err,\n );\n }\n\n // Validate the shape at the boundary so a malformed 2xx payload\n // (e.g. `{ action: undefined }`) surfaces as a PromptsApiError here\n // instead of crashing downstream code with a confusing stack trace.\n const parsed = syncResultSchema.safeParse(response?.data);\n if (!parsed.success) {\n throw new PromptsApiError(\n \"Failed to sync prompt: server returned an invalid response body\",\n \"sync\",\n response?.data ?? response,\n );\n }\n return {\n action: parsed.data.action,\n prompt: parsed.data.prompt as SyncResult[\"prompt\"],\n conflictInfo: parsed.data.conflictInfo as SyncResult[\"conflictInfo\"],\n };\n }\n}\n","import { Liquid } from \"liquidjs\";\nimport { PromptTracingDecorator, tracer } from \"./tracing\";\nimport { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\nimport { promptDataSchema } from \"./schema\";\nimport { type TemplateVariables, type PromptData, type CorePromptData, type PromptScope } from \"./types\";\nimport { PromptCompilationError, PromptValidationError } from \"./errors\";\n\n// Re-export types and errors for convenience\nexport type { TemplateVariables, PromptData, CorePromptData, PromptMetadata } from \"./types\";\nexport { PromptCompilationError, PromptValidationError } from \"./errors\";\n\n// Global Liquid instance - shared across all prompts for efficiency\nconst liquid = new Liquid({\n strictFilters: true,\n});\n\n/**\n * The Prompt class provides a standardized interface for working with prompt objects\n * within the SDK, focusing on core functionality needed for template compilation and execution.\n * Keeps only essential fields while maintaining compatibility with tracing and observability.\n */\nexport class Prompt {\n // === Core functionality (required) ===\n public readonly model!: string;\n public readonly messages!: Array<{\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n }>;\n\n // === Optional core fields ===\n public readonly prompt?: string;\n public readonly temperature?: number;\n public readonly maxTokens?: number;\n public readonly responseFormat?: CorePromptData[\"responseFormat\"];\n\n // === Runtime parameters ===\n public readonly parameters: Record<string, unknown> = {};\n\n // === Optional identification (for tracing) ===\n public readonly id?: string;\n public readonly handle?: string | null;\n public readonly version?: number;\n public readonly versionId?: string;\n public readonly scope?: PromptScope;\n\n constructor(data: PromptData) {\n // Validate input using Zod\n const validationResult = promptDataSchema.strip().safeParse(data);\n\n if (!validationResult.success) {\n throw new PromptValidationError(\n \"Invalid prompt data provided\",\n validationResult.error\n );\n }\n\n // Assign validated data\n Object.assign(this, validationResult.data);\n\n // Set default for prompt if not provided\n this.prompt ??= this.extractSystemPrompt();\n\n // Return a proxy that wraps specific methods for tracing\n return createTracingProxy(this as Prompt, tracer, PromptTracingDecorator);\n }\n\n private extractSystemPrompt(): string {\n return this.messages.find(m => m.role === \"system\")?.content ?? \"\";\n }\n\n /**\n * Compile the prompt template with provided variables (lenient - missing variables become empty)\n * @param variables - Object containing variable values for template compilation\n * @returns CompiledPrompt instance with compiled content\n */\n private _compile(\n variables: TemplateVariables,\n strict: boolean,\n ): CompiledPrompt {\n try {\n // Compile main prompt\n const compiledPrompt = this.prompt\n ? liquid.parseAndRenderSync(this.prompt, variables, {\n strictVariables: strict,\n })\n : \"\";\n\n // Compile messages\n const compiledMessages = (this.messages || []).map((message) => ({\n ...message,\n content: message.content\n ? liquid.parseAndRenderSync(message.content, variables, {\n strictVariables: strict,\n })\n : message.content,\n }));\n\n // Create new prompt data with compiled content\n const compiledData: PromptData = {\n ...this,\n prompt: compiledPrompt,\n messages: compiledMessages,\n };\n\n return new CompiledPrompt(compiledData, this);\n } catch (error) {\n const templateStr = this.prompt ?? JSON.stringify(this.messages);\n throw new PromptCompilationError(\n `Failed to compile prompt template: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n templateStr,\n error,\n );\n }\n }\n\n compile(variables: TemplateVariables = {}): CompiledPrompt {\n return this._compile(variables, false);\n }\n\n /**\n * Compile with validation - throws error if required variables are missing\n * @param variables - Template variables\n * @returns CompiledPrompt instance with compiled content\n */\n compileStrict(variables: TemplateVariables): CompiledPrompt {\n return this._compile(variables, true);\n }\n}\n\n\n/**\n * Represents a compiled prompt that extends Prompt with reference to the original template\n */\nexport class CompiledPrompt extends Prompt {\n constructor(\n compiledData: PromptData,\n public readonly original: Prompt,\n ) {\n super(compiledData);\n }\n}\n","import { type PromptScope as PromptScopeType } from \"./types\";\n\nexport const PromptScope = {\n ORGANIZATION: \"ORGANIZATION\" as const,\n PROJECT: \"PROJECT\" as const,\n} satisfies Record<string, PromptScopeType>;\n","// TODO: Move these to their own files\nimport { z } from \"zod\";\nimport { PromptScope } from \"../constants\";\n\n/**\n * Zod schema for message objects in prompts\n */\nexport const messageSchema = z.object({\n role: z.enum([\"user\", \"assistant\", \"system\"]),\n content: z.string(),\n});\n\n/**\n * Zod schema for response format configuration\n */\nexport const responseFormatSchema = z.object({\n type: z.literal(\"json_schema\"),\n json_schema: z.object({\n name: z.string(),\n schema: z.record(z.string(), z.unknown()),\n }).nullable(),\n}).optional();\n\n/**\n * Zod schema for core prompt data - the essential fields needed for functionality\n */\nexport const corePromptDataSchema = z.object({\n model: z.string().min(1, \"Model cannot be empty\"),\n messages: z.array(messageSchema).min(1, \"At least one message is required\"),\n prompt: z.string().optional(),\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().positive().optional(),\n responseFormat: responseFormatSchema,\n});\n\n/**\n * Zod schema for prompt metadata - optional fields for identification and tracing\n */\nexport const promptMetadataSchema = z.object({\n id: z.string().optional(),\n handle: z.string().nullable().optional(),\n version: z.number().min(0).optional(),\n versionId: z.string().optional(),\n scope: z.enum(PromptScope).optional(),\n});\n\n/**\n * Combined schema for complete prompt data\n */\nexport const promptDataSchema = z.object({\n ...corePromptDataSchema.shape,\n ...promptMetadataSchema.shape,\n parameters: z.record(z.string(), z.unknown()).optional(),\n});\n","import { type z } from \"zod\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n type corePromptDataSchema,\n type promptMetadataSchema,\n type promptDataSchema\n} from \"./schema\";\n\n/**\n * Type for template variables - supporting common data types\n */\nexport type TemplateVariables = Record<\n string,\n string | number | boolean | object | null\n>;\n\n/**\n * Core data needed for prompt functionality\n */\nexport type CorePromptData = z.infer<typeof corePromptDataSchema>;\n\n/**\n * Optional metadata for identification and tracing\n */\nexport type PromptMetadata = z.infer<typeof promptMetadataSchema>;\n\n/**\n * Combined type for creating prompts\n */\nexport type PromptData = z.infer<typeof promptDataSchema>;\n\n// Extract API types from OpenAPI schema for backwards compatibility\nexport type CreatePromptBody = NonNullable<\n paths[\"/api/prompts\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdatePromptBody = NonNullable<\n paths[\"/api/prompts/{id}\"][\"put\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type PromptResponse = NonNullable<\n paths[\"/api/prompts/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>;\n\n// Extract the PromptScope type from the API client\nexport type PromptScope = paths[\"/api/prompts\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"][\"scope\"];\n\n// Extract tag definition types from the OpenAPI schema\nexport type TagDefinition = NonNullable<\n paths[\"/api/prompts/tags\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type CreatedTag = NonNullable<\n paths[\"/api/prompts/tags\"][\"post\"][\"responses\"][\"201\"][\"content\"][\"application/json\"]\n>;\n\n/**\n * Fetch policy for prompt retrieval.\n * Controls how prompts are fetched and cached.\n */\nexport enum FetchPolicy {\n /** Use local file if available, otherwise fetch from API (default) */\n MATERIALIZED_FIRST = \"MATERIALIZED_FIRST\",\n /** Always try API first, fall back to materialized */\n ALWAYS_FETCH = \"ALWAYS_FETCH\",\n /** Fetch every X minutes, use materialized between fetches */\n CACHE_TTL = \"CACHE_TTL\",\n /** Never fetch, use materialized files only */\n MATERIALIZED_ONLY = \"MATERIALIZED_ONLY\",\n}\n\n","import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as yaml from \"js-yaml\";\nimport chalk from \"chalk\";\nimport type { PromptsConfig, LocalPromptConfig, MaterializedPrompt, PromptsLock } from \"../types\";\nimport { localPromptConfigSchema } from \"../types-prompt\";\nimport { PromptConverter } from \"@/cli/utils/promptConverter\";\nimport { PromptFileNotFoundError } from \"./errors/prompt-not-found.error\";\nimport { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\n\nexport class FileManager {\n private static readonly PROMPTS_CONFIG_FILE = \"prompts.json\";\n private static readonly PROMPTS_LOCK_FILE = \"prompts-lock.json\";\n private static readonly PROMPTS_DIR = \"prompts\";\n private static readonly MATERIALIZED_DIR = \".materialized\";\n\n private static _projectRoot: string | undefined;\n /**\n * The cwd `_projectRoot` was derived from.\n *\n * The cache MUST be keyed by it. Memoising a cwd-derived path without that key\n * is correct only in a process that serves exactly one command and then dies;\n * in a process that serves many from different directories (the CLI daemon, a\n * test runner, an embedding host), the second caller silently inherits the\n * first caller's project root and reads or writes the wrong prompts.json.\n */\n private static _projectRootCwd: string | undefined;\n\n /** Reset the cached project root. Tests use this to exercise different cwds. */\n static _resetProjectRootCache(): void {\n this._projectRoot = undefined;\n this._projectRootCwd = undefined;\n }\n\n private static cacheProjectRoot(cwd: string, root: string): string {\n this._projectRoot = root;\n this._projectRootCwd = cwd;\n return root;\n }\n\n private static readonly PROJECT_MARKERS = [\n \".git\",\n \"package.json\",\n \"pyproject.toml\",\n ];\n\n private static hasProjectMarker(dir: string): boolean {\n return this.PROJECT_MARKERS.some((m) => fs.existsSync(path.join(dir, m)));\n }\n\n private static findProjectRoot(): string {\n const cwd = process.cwd();\n\n if (this._projectRoot && this._projectRootCwd === cwd) {\n return this._projectRoot;\n }\n\n const fsRoot = path.parse(cwd).root;\n\n // Find the highest ancestor that still looks like part of \"this project\"\n // (has package.json / pyproject.toml / .git). If cwd itself has no marker,\n // we don't walk up at all — prevents grabbing a stray prompts.json from\n // /tmp or other shared scratch dirs.\n let topProjectDir: string | null = null;\n let scan = cwd;\n while (scan !== fsRoot) {\n if (this.hasProjectMarker(scan)) {\n topProjectDir = scan;\n }\n scan = path.dirname(scan);\n }\n\n if (!topProjectDir) {\n return this.cacheProjectRoot(cwd, cwd);\n }\n\n // Walk up from cwd looking for prompts.json, capped at the project boundary.\n let currentDir = cwd;\n while (true) {\n const configPath = path.join(currentDir, this.PROMPTS_CONFIG_FILE);\n if (fs.existsSync(configPath)) {\n return this.cacheProjectRoot(cwd, currentDir);\n }\n if (currentDir === topProjectDir) break;\n currentDir = path.dirname(currentDir);\n }\n\n // No prompts.json anywhere within the project — default to cwd so `init`\n // creates it where the user actually ran the command. This also keeps\n // concurrent test workdirs isolated: each tempdir gets its own prompts.json\n // instead of all pointing at a shared repo-root file.\n return this.cacheProjectRoot(cwd, cwd);\n }\n\n static getPromptsConfigPath(): string {\n return path.join(this.findProjectRoot(), this.PROMPTS_CONFIG_FILE);\n }\n\n static getPromptsLockPath(): string {\n return path.join(this.findProjectRoot(), this.PROMPTS_LOCK_FILE);\n }\n\n static getPromptsDir(): string {\n return path.join(this.findProjectRoot(), this.PROMPTS_DIR);\n }\n\n static getMaterializedDir(): string {\n return path.join(this.getPromptsDir(), this.MATERIALIZED_DIR);\n }\n\n static ensureDirectories(): void {\n const promptsDir = this.getPromptsDir();\n const materializedDir = this.getMaterializedDir();\n\n if (!fs.existsSync(promptsDir)) {\n fs.mkdirSync(promptsDir, { recursive: true });\n }\n\n if (!fs.existsSync(materializedDir)) {\n fs.mkdirSync(materializedDir, { recursive: true });\n }\n }\n\n static loadPromptsConfig(): PromptsConfig {\n const configPath = this.getPromptsConfigPath();\n\n if (!fs.existsSync(configPath)) {\n return { prompts: {} };\n }\n\n try {\n const content = fs.readFileSync(configPath, \"utf-8\");\n return JSON.parse(content) as PromptsConfig;\n } catch (error) {\n throw new Error(`Failed to parse prompts.json: ${formatApiErrorMessage({ error })}`);\n }\n }\n\n static savePromptsConfig(config: PromptsConfig): void {\n const configPath = this.getPromptsConfigPath();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n\n static initializePromptsConfig(): { created: boolean; path: string } {\n const configPath = this.getPromptsConfigPath();\n const existed = fs.existsSync(configPath);\n\n if (!existed) {\n const emptyConfig: PromptsConfig = { prompts: {} };\n this.savePromptsConfig(emptyConfig);\n return { created: true, path: configPath };\n }\n\n return { created: false, path: configPath };\n }\n\n static loadPromptsLock = (): PromptsLock => {\n const lockPath = this.getPromptsLockPath();\n\n if (!fs.existsSync(lockPath)) {\n return {\n lockfileVersion: 1,\n prompts: {}\n };\n }\n\n try {\n const content = fs.readFileSync(lockPath, \"utf-8\");\n return JSON.parse(content) as PromptsLock;\n } catch (error) {\n throw new Error(`Failed to parse prompts-lock.json: ${formatApiErrorMessage({ error })}`);\n }\n }\n\n static savePromptsLock(lock: PromptsLock): void {\n const lockPath = this.getPromptsLockPath();\n fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + \"\\n\");\n }\n\n static initializePromptsLock(): { created: boolean; path: string } {\n const lockPath = this.getPromptsLockPath();\n const existed = fs.existsSync(lockPath);\n\n if (!existed) {\n const emptyLock: PromptsLock = {\n lockfileVersion: 1,\n prompts: {}\n };\n this.savePromptsLock(emptyLock);\n return { created: true, path: lockPath };\n }\n\n return { created: false, path: lockPath };\n }\n\n static loadLocalPrompt = (filePath: string): LocalPromptConfig => {\n const fullPath = path.resolve(filePath);\n\n if (!fs.existsSync(fullPath)) {\n throw new PromptFileNotFoundError(filePath);\n }\n\n try {\n const content = fs.readFileSync(fullPath, \"utf-8\");\n const rawData = yaml.load(content);\n\n // Validate with zod and provide nice error messages\n const result = localPromptConfigSchema.safeParse(rawData);\n\n if (!result.success) {\n // Format zod errors nicely (manually since z.prettifyError might not be available)\n const prettyError = result.error.issues\n .map(issue => `✖ ${issue.message}${issue.path.length > 0 ? `\\n → at ${issue.path.join('.')}` : ''}`)\n .join('\\n');\n\n throw new Error(\n `Invalid prompt configuration in ${chalk.yellow(filePath)}:\\n${prettyError}`\n );\n }\n\n return result.data;\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"Invalid prompt configuration\")) {\n throw error; // Re-throw zod validation errors as-is\n }\n throw new Error(`Failed to parse local prompt file ${filePath}: ${formatApiErrorMessage({ error })}`);\n }\n }\n\n static saveMaterializedPrompt(name: string, prompt: MaterializedPrompt): string {\n const materializedDir = this.getMaterializedDir();\n const parts = name.split(\"/\");\n const fileName = `${parts[parts.length - 1]}.prompt.yaml`;\n\n // Create nested directories if needed\n if (parts.length > 1) {\n const subDir = path.join(materializedDir, ...parts.slice(0, -1));\n if (!fs.existsSync(subDir)) {\n fs.mkdirSync(subDir, { recursive: true });\n }\n }\n\n const filePath = path.join(materializedDir, ...parts.slice(0, -1), fileName);\n\n // Convert to YAML format using the converter\n const yamlContent = PromptConverter.fromMaterializedToYaml(prompt);\n\n const yamlString = yaml.dump(yamlContent, {\n lineWidth: -1,\n noRefs: true,\n sortKeys: false\n });\n\n fs.writeFileSync(filePath, yamlString);\n return filePath;\n }\n\n static getLocalPromptFiles = (): string[] => {\n const promptsDir = this.getPromptsDir();\n const materializedDir = this.getMaterializedDir();\n\n if (!fs.existsSync(promptsDir)) {\n return [];\n }\n\n const files: string[] = [];\n\n const walkDir = (dir: string, relativePath = \"\"): void => {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativeFilePath = path.join(relativePath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip the .materialized directory\n if (fullPath === materializedDir) {\n continue;\n }\n walkDir(fullPath, relativeFilePath);\n } else if (entry.isFile() && entry.name.endsWith(\".prompt.yaml\")) {\n files.push(path.join(promptsDir, relativeFilePath));\n }\n }\n };\n\n walkDir(promptsDir);\n return files;\n }\n\n static promptNameFromPath(filePath: string): string {\n const promptsDir = this.getPromptsDir();\n const relativePath = path.relative(promptsDir, filePath);\n return relativePath.replace(/\\.prompt\\.yaml$/, \"\");\n }\n\n static cleanupOrphanedMaterializedFiles(currentDependencies: Set<string>): string[] {\n const materializedDir = this.getMaterializedDir();\n\n if (!fs.existsSync(materializedDir)) {\n return [];\n }\n\n const cleaned: string[] = [];\n\n const cleanupDir = (dir: string, relativePath = \"\"): void => {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativeFilePath = path.join(relativePath, entry.name);\n\n if (entry.isDirectory()) {\n cleanupDir(fullPath, relativeFilePath);\n\n // Remove empty directories\n try {\n const dirEntries = fs.readdirSync(fullPath);\n if (dirEntries.length === 0) {\n fs.rmdirSync(fullPath);\n }\n } catch {\n // Directory not empty or other error, ignore\n }\n } else if (entry.isFile() && entry.name.endsWith(\".prompt.yaml\")) {\n // Extract prompt name from materialized file path\n const promptName = relativeFilePath.replace(/\\.prompt\\.yaml$/, \"\");\n\n if (!currentDependencies.has(promptName)) {\n fs.unlinkSync(fullPath);\n cleaned.push(promptName);\n }\n }\n }\n };\n\n cleanupDir(materializedDir);\n return cleaned;\n }\n\n static updateLockEntry(lock: PromptsLock, name: string, prompt: MaterializedPrompt, materializedPath: string): void {\n const relativePath = path.relative(this.findProjectRoot(), materializedPath);\n\n lock.prompts[name] = {\n version: prompt.version,\n versionId: prompt.versionId,\n materialized: relativePath,\n };\n }\n\n static removeFromLock(lock: PromptsLock, names: string[]): void {\n for (const name of names) {\n delete lock.prompts[name];\n }\n }\n\n static addToGitignore(entry: string): { added: boolean; existed: boolean } {\n const gitignorePath = path.join(this.findProjectRoot(), \".gitignore\");\n\n // Check if .gitignore exists\n if (!fs.existsSync(gitignorePath)) {\n // Create new .gitignore with the entry\n fs.writeFileSync(gitignorePath, `${entry}\\n`);\n return { added: true, existed: false };\n }\n\n // Read existing .gitignore\n const content = fs.readFileSync(gitignorePath, \"utf-8\");\n const lines = content.split(\"\\n\").map(line => line.trim());\n\n // Check if entry already exists\n if (lines.includes(entry)) {\n return { added: false, existed: true };\n }\n\n // Add entry to .gitignore\n const newContent = content.endsWith(\"\\n\") ? `${content}${entry}\\n` : `${content}\\n${entry}\\n`;\n fs.writeFileSync(gitignorePath, newContent);\n\n return { added: true, existed: false };\n }\n}\n","import { z } from \"zod\";\n\nimport type { JsonValue } from \"./types\";\n\nconst jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(z.string(), jsonValueSchema),\n ]),\n);\n\n// Kept OUT of `types.ts` on purpose: `types.ts` is imported by `program.ts`,\n// which is on the always-loaded cold-start path of every CLI invocation, and\n// zod costs ~39ms to load. Only the prompt commands need this schema, and\n// they are lazy-loaded behind dynamic imports, so the cost is paid only when\n// a prompt command actually runs.\nexport const localPromptConfigSchema = z\n .object({\n model: z.string().min(1, \"Model is required\"),\n modelParameters: z\n .object({\n temperature: z.number().optional(),\n max_tokens: z.number().optional(),\n })\n .loose()\n .optional(),\n messages: z\n .array(\n z\n .object({\n role: z.enum([\"system\", \"user\", \"assistant\"]),\n content: z.string().min(1, \"Message content cannot be empty\"),\n })\n .loose(),\n )\n .min(1, \"At least one message is required\"),\n // Parameters persist as JSON: parse them AS json so the value's type\n // matches the API contract (the spec's recursive JsonValue) instead of\n // laundering unknowns into a typed pipeline.\n parameters: z\n .record(z.string(), jsonValueSchema)\n .optional()\n .default({}),\n })\n .loose();\n\nexport type LocalPromptConfig = z.infer<typeof localPromptConfigSchema>;\n","export class PromptFileNotFoundError extends Error {\n constructor(filePath: string) {\n super(`Local prompt file not found: ${filePath}`);\n this.name = 'PromptFileNotFoundError';\n }\n}\n","import type { LocalPromptConfig, PromptDependency } from \"@/cli/types\";\nimport { FileManager } from \"@/cli/utils/fileManager\";\nimport { type Logger, NoOpLogger } from \"@/logger\";\nimport { type PromptData } from \"./types\";\nimport { PromptFileNotFoundError } from \"@/cli/utils/errors/prompt-not-found.error\";\n\nexport interface LocalPromptsServiceConfig {\n fileManager?: typeof FileManager;\n logger?: Logger;\n}\n\n/**\n * Service for retrieving prompts from local filesystem sources.\n *\n * Searches for prompts in the following priority order:\n * 1. Explicit file mapping in prompts.json config\n * 2. Materialized path from prompts-lock.json\n * 3. Direct file scanning in prompts directory\n */\nexport class LocalPromptsService {\n private readonly fileManager: typeof FileManager;\n private readonly logger: Logger;\n\n constructor(config?: LocalPromptsServiceConfig) {\n this.fileManager = config?.fileManager ?? FileManager;\n this.logger = config?.logger ?? new NoOpLogger();\n }\n\n /**\n * Retrieves a prompt using the configured search strategy.\n * Tries each source in priority order until found or all sources exhausted.\n */\n async get(handleOrId: string): Promise<PromptData | null> {\n try {\n const dependency = await this.getDependencyFromConfig(handleOrId);\n\n // If no dependency is found, it means it's not a local prompt\n if (!dependency) {\n return null;\n }\n\n // Try each source in priority order until found or all sources exhausted\n // We catch errors and return null if any of the sources fail so we\n // can continue to the next source and return null if all sources fail\n const localPromptConfig = (\n (await this.getFromConfig(dependency).catch((e) => {\n if (e instanceof PromptFileNotFoundError) return null;\n throw e;\n })) ??\n (await this.getFromLockFile(handleOrId).catch((e) => {\n if (e instanceof PromptFileNotFoundError) return null;\n throw e;\n })) ??\n (await this.getFromLocalFiles(handleOrId).catch((e) => {\n if (e instanceof PromptFileNotFoundError) return null;\n throw e;\n }))\n );\n\n return localPromptConfig ? this.convertToPromptData({\n ...localPromptConfig,\n handle: handleOrId,\n }) : null;\n } catch (error) {\n this.logger.warn(`Failed to get prompt \"${handleOrId}\": ${error instanceof Error ? error.message : String(error)}`);\n return null;\n }\n }\n\n\n /**\n * Searches for prompt using explicit file mapping in prompts.json.\n * Looks for dependencies with a 'file' property pointing to a specific path.\n */\n private async getFromConfig(dependency: PromptDependency): Promise<LocalPromptConfig | null> {\n if (typeof dependency === 'string' && dependency.startsWith('file:')) {\n return this.fileManager.loadLocalPrompt(dependency.slice(5));\n }\n\n return null;\n }\n\n /**\n * Searches for prompt using materialized path from lock file.\n * Lock file contains resolved paths for prompts that have been synced/materialized.\n */\n private async getFromLockFile(handleOrId: string): Promise<LocalPromptConfig | null> {\n const lock = this.fileManager.loadPromptsLock();\n const lockEntry = lock.prompts[handleOrId];\n\n if (lockEntry?.materialized) {\n return this.fileManager.loadLocalPrompt(lockEntry.materialized);\n }\n\n return null;\n }\n\n /**\n * Searches for prompt by scanning all .prompt.yaml files in prompts directory.\n * Extracts prompt name from file path and matches against the requested handle.\n * This is the fallback method when explicit mappings don't exist.\n */\n private async getFromLocalFiles(handleOrId: string): Promise<LocalPromptConfig | null> {\n const localFiles = this.fileManager.getLocalPromptFiles();\n\n for (const filePath of localFiles) {\n const promptName = this.fileManager.promptNameFromPath(filePath);\n if (promptName === handleOrId) {\n return this.fileManager.loadLocalPrompt(filePath);\n }\n }\n\n return null;\n }\n\n /**\n * Get dependency from config\n */\n private async getDependencyFromConfig(handleOrId: string): Promise<PromptDependency | null> {\n const config = this.fileManager.loadPromptsConfig();\n const dependency = config.prompts[handleOrId];\n\n return dependency ?? null;\n }\n\n /**\n * Converts LocalPromptConfig to PromptData format\n */\n private convertToPromptData(config: LocalPromptConfig & { handle: string; }): PromptData {\n const { modelParameters, ...rest } = config;\n return {\n maxTokens: modelParameters?.max_tokens,\n temperature: modelParameters?.temperature,\n ...rest,\n };\n }\n}\n","import { PromptsApiService, type AssignTagResult } from \"./prompts-api.service\";\nimport { Prompt } from \"./prompt\";\nimport type { CreatePromptBody, UpdatePromptBody, PromptData, TagDefinition, CreatedTag } from \"./types\";\nimport { FetchPolicy } from \"./types\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { LocalPromptsService } from \"./local-prompts.service\";\nimport { PromptsError } from \"./errors\";\n\n/**\n * Options for fetching a prompt.\n */\nexport interface GetPromptOptions {\n /** Specific version to fetch */\n version?: string;\n /** Tag to fetch (e.g., \"production\", \"staging\", or a custom tag) */\n tag?: string;\n /** Fetch policy to use */\n fetchPolicy?: FetchPolicy;\n /** Cache TTL in minutes (only used with CACHE_TTL policy) */\n cacheTtlMinutes?: number;\n}\n\ninterface CacheEntry {\n data: PromptData;\n timestamp: number;\n}\n\ninterface PromptsFacadeDependencies {\n promptsApiService: PromptsApiService;\n localPromptsService: LocalPromptsService;\n}\n\n/**\n * Facade for prompt operations in the LangWatch SDK.\n * Provides a simplified interface for common prompt management tasks.\n */\nexport class PromptsFacade implements Pick<PromptsApiService, \"sync\" | \"delete\">{\n private readonly promptsApiService: PromptsApiService;\n private readonly localPromptsService: LocalPromptsService;\n private readonly cache = new Map<string, CacheEntry>();\n readonly tags: {\n assign(id: string, params: { tag: string; versionId: string }): Promise<AssignTagResult>;\n list(): Promise<TagDefinition[]>;\n create(params: { name: string }): Promise<CreatedTag>;\n delete(tagName: string): Promise<void>;\n rename(oldName: string, newName: string): Promise<void>;\n };\n\n constructor(config: InternalConfig & PromptsFacadeDependencies) {\n this.promptsApiService = config.promptsApiService ?? new PromptsApiService(config);\n this.localPromptsService = config.localPromptsService ?? new LocalPromptsService();\n this.tags = {\n assign: (id, { tag, versionId }) =>\n this.promptsApiService.assignTag({ id, tag, versionId }),\n list: () => this.promptsApiService.listTags(),\n create: ({ name }) => this.promptsApiService.createTag({ name }),\n delete: (tagName) => this.promptsApiService.deleteTag(tagName),\n rename: (oldName, newName) =>\n this.promptsApiService.renameTag({ tag: oldName, name: newName }),\n };\n }\n\n /**\n * Creates a new prompt.\n * @param data The prompt creation payload.\n * @returns The created Prompt instance.\n * @throws {PromptsError} If the API call fails.\n */\n async create(data: CreatePromptBody): Promise<Prompt> {\n const serverPrompt = await this.promptsApiService.create(data);\n return new Prompt(serverPrompt);\n }\n\n /**\n * Retrieves a prompt by handle or ID.\n *\n * Supports shorthand `handle:tag` syntax — e.g. `get(\"pizza-prompt:production\")`.\n * Shorthand is parsed server-side; the SDK passes the string through as-is.\n *\n * @param handleOrId The prompt's handle, unique identifier, or `handle:tag` shorthand.\n * @param options Optional parameters for the request.\n * @returns The Prompt instance.\n * @throws {PromptsError} If the prompt is not found or the API call fails.\n */\n async get(\n handleOrId: string,\n options?: GetPromptOptions,\n ): Promise<Prompt> {\n const fetchPolicy = options?.fetchPolicy ?? FetchPolicy.MATERIALIZED_FIRST;\n\n switch (fetchPolicy) {\n case FetchPolicy.MATERIALIZED_ONLY:\n return this.getMaterializedOnly(handleOrId);\n\n case FetchPolicy.ALWAYS_FETCH:\n return this.getAlwaysFetch(handleOrId, options);\n\n case FetchPolicy.CACHE_TTL:\n return this.getCacheTtl(handleOrId, options);\n\n case FetchPolicy.MATERIALIZED_FIRST:\n default:\n return this.getMaterializedFirst(handleOrId, options);\n }\n }\n\n private async getMaterializedFirst(\n handleOrId: string,\n options?: GetPromptOptions,\n ): Promise<Prompt> {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n const serverPrompt = await this.promptsApiService.get(handleOrId, options);\n return new Prompt(serverPrompt);\n }\n\n private async getAlwaysFetch(\n handleOrId: string,\n options?: GetPromptOptions,\n ): Promise<Prompt> {\n try {\n const serverPrompt = await this.promptsApiService.get(handleOrId, options);\n return new Prompt(serverPrompt);\n } catch {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n throw new PromptsError(`Prompt \"${handleOrId}\" not found locally or on server`);\n }\n }\n\n private async getMaterializedOnly(handleOrId: string): Promise<Prompt> {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n throw new PromptsError(`Prompt \"${handleOrId}\" not found in materialized files`);\n }\n\n private buildCacheKey(handleOrId: string, options?: GetPromptOptions): string {\n const tagSegment = options?.tag != null ? `::tag:${options.tag}` : '';\n return `${handleOrId}::version:${options?.version ?? ''}${tagSegment}`;\n }\n\n private async getCacheTtl(\n handleOrId: string,\n options?: GetPromptOptions,\n ): Promise<Prompt> {\n const cacheKey = this.buildCacheKey(handleOrId, options);\n const ttlMs = (options?.cacheTtlMinutes ?? 5) * 60 * 1000;\n const cached = this.cache.get(cacheKey);\n const now = Date.now();\n\n if (cached && now - cached.timestamp < ttlMs) {\n return new Prompt(cached.data);\n }\n\n try {\n const serverPrompt = await this.promptsApiService.get(handleOrId, options);\n this.cache.set(cacheKey, { data: serverPrompt, timestamp: now });\n return new Prompt(serverPrompt);\n } catch {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n throw new PromptsError(`Prompt \"${handleOrId}\" not found locally or on server`);\n }\n }\n\n /**\n * Retrieves all prompts.\n * @returns Array of Prompt instances.\n * @throws {PromptsError} If the API call fails.\n */\n async getAll(): Promise<Prompt[]> {\n const serverPrompts = await this.promptsApiService.getAll();\n return serverPrompts.map((prompt) => new Prompt(prompt));\n }\n\n /**\n * Updates an existing prompt.\n * @param handleOrId The prompt's handle or unique identifier.\n * @param newData The update payload.\n * @returns The updated Prompt instance.\n * @throws {PromptsError} If the API call fails.\n */\n async update(handleOrId: string, newData: UpdatePromptBody): Promise<Prompt> {\n const serverPrompt = await this.promptsApiService.update(handleOrId, newData);\n return new Prompt(serverPrompt);\n }\n\n get delete() {\n return this.promptsApiService.delete.bind(this.promptsApiService);\n }\n\n /**\n * Delegated method to the prompts API service.\n */\n get sync() {\n return this.promptsApiService.sync.bind(this.promptsApiService);\n }\n}\n","/**\n * Errors for the Dataset API\n */\n\n/**\n * Base error for all dataset operations.\n */\nexport class DatasetError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DatasetError\";\n }\n}\n\n/**\n * Thrown when a dataset is not found (404).\n */\nexport class DatasetNotFoundError extends DatasetError {\n constructor(slugOrId: string) {\n super(`Dataset not found: ${slugOrId}`);\n this.name = \"DatasetNotFoundError\";\n }\n}\n\n/**\n * Error from the Dataset API with operation context.\n * Aligns with PromptsApiError pattern: includes operation and originalError fields.\n */\nexport class DatasetApiError extends DatasetError {\n readonly status: number;\n readonly operation: string;\n readonly originalError?: unknown;\n\n constructor(message: string, status: number, operation: string, originalError?: unknown) {\n super(message);\n this.name = \"DatasetApiError\";\n this.status = status;\n this.operation = operation;\n this.originalError = originalError;\n }\n}\n\n/**\n * Thrown for client-side validation failures (e.g. empty name, no update fields, empty entries).\n * Distinguished from DatasetApiError which represents server-side errors.\n */\nexport class DatasetValidationError extends DatasetError {\n constructor(message: string) {\n super(message);\n this.name = \"DatasetValidationError\";\n }\n}\n\n/**\n * Thrown when a dataset operation exceeds the plan limit (403).\n * The message includes the upgrade/subscription URL from the server.\n */\nexport class DatasetPlanLimitError extends DatasetError {\n readonly limitType: string;\n readonly current?: number;\n readonly max?: number;\n\n constructor(message: string, originalError?: unknown) {\n super(message);\n this.name = \"DatasetPlanLimitError\";\n\n if (originalError != null && typeof originalError === \"object\") {\n const err = originalError as Record<string, unknown>;\n this.limitType = typeof err.limitType === \"string\" ? err.limitType : \"datasets\";\n this.current = typeof err.current === \"number\" ? err.current : undefined;\n this.max = typeof err.max === \"number\" ? err.max : undefined;\n } else {\n this.limitType = \"datasets\";\n }\n }\n}\n","import { LANGWATCH_SDK_NAME_CLIENT, LANGWATCH_SDK_VERSION } from \"@/internal/constants\";\nimport { getLangWatchTracer } from \"@/observability-sdk/tracer\";\n\nexport const tracer = getLangWatchTracer(`${LANGWATCH_SDK_NAME_CLIENT}.datasets`, LANGWATCH_SDK_VERSION);\n","import { type LangwatchApiClient } from \"@/internal/api/client\";\nimport { isLangWatchHandledError } from \"@/internal/api/errors\";\nimport { type Logger } from \"@/logger\";\nimport {\n type Dataset,\n type DatasetEntry,\n type DatasetMetadata,\n type GetDatasetApiResponse,\n type GetDatasetOptions,\n type ListDatasetsOptions,\n type ListDatasetsApiResponse,\n type ListRecordsOptions,\n type ListRecordsApiResponse,\n type CreateDatasetOptions,\n type UpdateDatasetOptions,\n type CreateFromUploadOptions,\n type CreateFromUploadResponse,\n type BatchCreateRecordsResponse,\n type DeleteRecordsResponse,\n type UploadResponse,\n type DatasetRecordResponse,\n} from \"./types\";\nimport { DatasetApiError, DatasetNotFoundError, DatasetPlanLimitError } from \"./errors\";\nimport { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\nimport { tracer } from \"./tracing\";\nimport { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { buildAuthHeaders } from \"@/internal/api/auth\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\ntype DatasetServiceConfig = {\n langwatchApiClient: LangwatchApiClient;\n logger: Logger;\n endpoint: string;\n apiKey: string;\n};\n\n/**\n * Service for managing dataset resources via the LangWatch API.\n *\n * Responsibilities:\n * - CRUD operations for datasets\n * - Record management (create, update, delete)\n * - File upload\n * - Error handling with contextual information\n */\nexport class DatasetService {\n private readonly config: DatasetServiceConfig;\n\n constructor(config: DatasetServiceConfig) {\n this.config = { ...config, endpoint: resolveEndpoint(config.endpoint) };\n\n /**\n * Wraps the service in a tracing proxy that automatically creates\n * OpenTelemetry spans for all public methods.\n */\n return createTracingProxy(this as DatasetService, tracer);\n }\n\n /**\n * Handles API errors by mapping status codes to appropriate error types.\n * @param operation - Description of the operation being performed\n * @param error - The error object from the API response\n * @param status - The HTTP status code\n * @param slugOrId - The dataset identifier (only passed for operations targeting an existing resource)\n */\n private handleApiError(operation: string, error: unknown, status: number, slugOrId?: string): never {\n if (status === 404 && slugOrId) {\n throw new DatasetNotFoundError(slugOrId);\n }\n\n if (status === 403) {\n const errorMessage = this.extractErrorMessage(error, status);\n throw new DatasetPlanLimitError(errorMessage, error);\n }\n\n const errorMessage = this.extractErrorMessage(error, status);\n\n // NO handled-error throw here, deliberately.\n //\n // Datasets is the one service that already HAS a typed error taxonomy —\n // `DatasetNotFoundError`, `DatasetPlanLimitError`, and a `DatasetApiError`\n // that carries the status — and all three are public API that callers catch\n // by class. Raising a `LangWatchHandledError` in their place would be a\n // breaking change dressed up as an improvement (a 409 would stop being a\n // `DatasetApiError`), so this service keeps its own classes and the\n // transport's throw is folded back into them by `asResponseEnvelope` above.\n //\n // Nothing is lost at the surface that matters: `DatasetApiError` keeps the\n // raw body on `originalError`, so the CLI still reads the platform's `kind`\n // back off it and still prints a typed `--format json` document.\n throw new DatasetApiError(\n `Failed to ${operation}: ${errorMessage}`,\n status,\n operation,\n error,\n );\n }\n\n /**\n * Extracts a human-readable error message from an API error response.\n */\n private extractErrorMessage(error: unknown, status: number): string {\n return formatApiErrorMessage({ error: error, options: { status } });\n }\n\n /**\n * Wrapper for API calls to endpoints not yet in the generated OpenAPI types.\n * Quarantines `as any` casts to a single location.\n */\n private async untypedRequest<M extends 'GET' | 'POST' | 'PATCH' | 'DELETE'>(\n method: M,\n path: string,\n options?: Record<string, unknown>,\n ) {\n return this.asResponseEnvelope(\n (this.config.langwatchApiClient[method] as any)(path, options),\n );\n }\n\n /**\n * Puts a transport-thrown domain error back into the `{ error, response }`\n * envelope this service reads.\n *\n * The HTTP client now THROWS a typed domain error on a named failure, before\n * any service sees the response. Datasets is the one service that must not\n * receive it: it maps failures onto error classes of its own —\n * `DatasetNotFoundError`, `DatasetPlanLimitError`, `DatasetApiError` — and all\n * three are public API that callers catch by class. A typed throw arriving\n * first would silently take their place, which is a breaking change however\n * good the intention.\n *\n * So the throw is caught here and handed back as the envelope it would have\n * been, `unwrapResponse` runs exactly as it always has, and every dataset\n * error class survives untouched. This service is deliberately EXACTLY as it\n * was; the transport change is invisible to it.\n */\n private async asResponseEnvelope(\n request: Promise<{ data?: unknown; error?: unknown; response: { status: number } }>,\n ): Promise<{ data?: unknown; error?: unknown; response: { status: number } }> {\n try {\n return await request;\n } catch (error) {\n if (isLangWatchHandledError(error)) {\n return { error: error.body, response: { status: error.httpStatus } };\n }\n throw error;\n }\n }\n\n /**\n * Unwraps an API response, throwing a mapped error if the response contains an error.\n * Centralizes the repeated `if (error) handleApiError; return data` pattern.\n */\n private unwrapResponse<T>(\n response: { data?: unknown; error?: unknown; response: { status: number } },\n operation: string,\n slugOrId?: string,\n ): T {\n if (response.error) {\n this.handleApiError(operation, response.error, response.response.status, slugOrId);\n }\n return response.data as T;\n }\n\n /**\n * Fetches a dataset by its slug or ID, returning metadata and entries.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param _options - Optional configuration\n * @returns The dataset with metadata and entries\n */\n async getDataset<T extends Record<string, unknown> = Record<string, unknown>>(\n slugOrId: string,\n _options?: GetDatasetOptions\n ): Promise<Dataset<T>> {\n this.config.logger.debug(`Fetching dataset: ${slugOrId}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.GET(\"/api/dataset/{slugOrId}\", {\n params: {\n path: {\n slugOrId,\n },\n },\n }) as Promise<{ data?: unknown; error?: unknown; response: { status: number } }>,\n );\n\n const data = this.unwrapResponse<GetDatasetApiResponse>(\n response,\n `fetch dataset \"${slugOrId}\"`,\n slugOrId,\n );\n\n const entries: DatasetEntry<T>[] = data.data.map((item) => ({\n id: item.id,\n datasetId: item.datasetId,\n projectId: item.projectId,\n entry: item.entry as T,\n createdAt: item.createdAt,\n updatedAt: item.updatedAt,\n }));\n\n this.config.logger.debug(\n `Fetched dataset ${slugOrId} with ${entries.length} entries`\n );\n\n return {\n id: data.id,\n name: data.name,\n slug: data.slug,\n columnTypes: data.columnTypes,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n entries,\n };\n }\n\n /**\n * Lists all datasets for the project, with optional pagination.\n */\n async listDatasets(options?: ListDatasetsOptions): Promise<ListDatasetsApiResponse> {\n this.config.logger.debug(\"Listing datasets\");\n\n const response = await this.untypedRequest('GET', '/api/dataset', {\n params: {\n query: {\n page: options?.page,\n limit: options?.limit,\n },\n },\n });\n\n return this.unwrapResponse<ListDatasetsApiResponse>(\n response,\n \"list datasets\",\n );\n }\n\n /**\n * Creates a new dataset.\n */\n async createDataset(options: CreateDatasetOptions): Promise<DatasetMetadata> {\n this.config.logger.debug(`Creating dataset: ${options.name}`);\n\n const response = await this.untypedRequest('POST', '/api/dataset', {\n body: {\n name: options.name,\n columnTypes: options.columnTypes ?? [],\n },\n });\n\n return this.unwrapResponse<DatasetMetadata>(\n response,\n `create dataset \"${options.name}\"`,\n );\n }\n\n /**\n * Updates a dataset by its slug or ID.\n */\n async updateDataset(slugOrId: string, options: UpdateDatasetOptions): Promise<DatasetMetadata> {\n this.config.logger.debug(`Updating dataset: ${slugOrId}`);\n\n const response = await this.untypedRequest('PATCH', '/api/dataset/{slugOrId}', {\n params: {\n path: { slugOrId },\n },\n body: options,\n });\n\n return this.unwrapResponse<DatasetMetadata>(\n response,\n `update dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Deletes (archives) a dataset by its slug or ID.\n */\n async deleteDataset(slugOrId: string): Promise<DatasetMetadata> {\n this.config.logger.debug(`Deleting dataset: ${slugOrId}`);\n\n const response = await this.untypedRequest('DELETE', '/api/dataset/{slugOrId}', {\n params: {\n path: { slugOrId },\n },\n });\n\n return this.unwrapResponse<DatasetMetadata>(\n response,\n `delete dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Creates records in a dataset in batch.\n */\n async createRecords(\n slugOrId: string,\n entries: Record<string, unknown>[],\n ): Promise<BatchCreateRecordsResponse> {\n this.config.logger.debug(`Creating ${entries.length} records in dataset: ${slugOrId}`);\n\n const response = await this.untypedRequest('POST', '/api/dataset/{slugOrId}/records', {\n params: {\n path: { slugOrId },\n },\n body: { entries },\n });\n\n return this.unwrapResponse<BatchCreateRecordsResponse>(\n response,\n `create records in dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Updates a single record in a dataset.\n */\n async updateRecord(\n slugOrId: string,\n recordId: string,\n entry: Record<string, unknown>,\n ): Promise<DatasetRecordResponse> {\n this.config.logger.debug(`Updating record ${recordId} in dataset: ${slugOrId}`);\n\n const response = await this.untypedRequest('PATCH', '/api/dataset/{slugOrId}/records/{recordId}', {\n params: {\n path: { slugOrId, recordId },\n },\n body: { entry },\n });\n\n return this.unwrapResponse<DatasetRecordResponse>(\n response,\n `update record \"${recordId}\" in dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Deletes records from a dataset by IDs.\n */\n async deleteRecords(\n slugOrId: string,\n recordIds: string[],\n ): Promise<DeleteRecordsResponse> {\n this.config.logger.debug(`Deleting ${recordIds.length} records from dataset: ${slugOrId}`);\n\n const response = await this.untypedRequest('DELETE', '/api/dataset/{slugOrId}/records', {\n params: {\n path: { slugOrId },\n },\n body: { recordIds },\n });\n\n return this.unwrapResponse<DeleteRecordsResponse>(\n response,\n `delete records from dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Lists records in a dataset with optional pagination.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param options - Pagination options (page, limit)\n * @returns Paginated list of records\n */\n async listRecords(\n slugOrId: string,\n options?: ListRecordsOptions,\n ): Promise<ListRecordsApiResponse> {\n this.config.logger.debug(`Listing records for dataset: ${slugOrId}`);\n\n const response = await this.untypedRequest('GET', '/api/dataset/{slugOrId}/records', {\n params: {\n path: { slugOrId },\n query: {\n page: options?.page,\n limit: options?.limit,\n },\n },\n });\n\n return this.unwrapResponse<ListRecordsApiResponse>(\n response,\n `list records in dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Sends a multipart/form-data request using raw fetch.\n * openapi-fetch hardcodes content-type: application/json, so file uploads\n * must bypass it. This helper centralizes URL building, auth headers,\n * error parsing, and response unwrapping.\n *\n * @param path - The API path (appended to the endpoint)\n * @param formData - The FormData payload\n * @param operation - Human-readable operation name for error messages\n * @param slugOrId - Optional dataset identifier (passed to handleApiError for 404 mapping)\n */\n private async fetchMultipart<T>(\n path: string,\n formData: FormData,\n operation: string,\n slugOrId?: string,\n ): Promise<T> {\n const { endpoint, apiKey } = this.config;\n const url = `${endpoint}${path}`;\n\n const response = await langwatchFetch(url, {\n method: \"POST\",\n headers: buildAuthHeaders({ apiKey }),\n body: formData,\n });\n\n if (!response.ok) {\n const rawBody = await response.text();\n let errorBody: unknown = rawBody;\n if (rawBody) {\n try {\n errorBody = JSON.parse(rawBody);\n } catch {\n // Keep the plain-text body.\n }\n }\n\n this.handleApiError(operation, errorBody, response.status, slugOrId);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * Creates a new dataset from a file upload.\n *\n * @param options - The dataset name and file to upload\n * @returns The created dataset metadata with record count\n */\n async createDatasetFromUpload(\n options: CreateFromUploadOptions,\n ): Promise<CreateFromUploadResponse> {\n this.config.logger.debug(`Creating dataset from upload: ${options.name}`);\n\n const formData = new FormData();\n formData.append(\"name\", options.name);\n formData.append(\"file\", options.file);\n\n return this.fetchMultipart<CreateFromUploadResponse>(\n \"/api/dataset/upload\",\n formData,\n `create dataset from upload \"${options.name}\"`,\n );\n }\n\n /**\n * Uploads a file with a strategy for handling existing datasets.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param file - The file to upload (File or Blob)\n * @param ifExists - Strategy when dataset exists: \"append\" (default), \"replace\", or \"error\"\n * @returns The upload result\n */\n async uploadWithStrategy(\n slugOrId: string,\n file: File | Blob,\n ifExists: \"append\" | \"replace\" | \"error\" = \"append\",\n ): Promise<UploadResponse> {\n switch (ifExists) {\n case \"append\":\n return this._uploadAppend(slugOrId, file);\n case \"replace\":\n return this._uploadReplace(slugOrId, file);\n case \"error\":\n return this._uploadError(slugOrId, file);\n }\n }\n\n /**\n * Converts a CreateFromUploadResponse to the unified UploadResponse shape.\n */\n private toUploadResponse(result: CreateFromUploadResponse): UploadResponse {\n return {\n dataset: {\n id: result.id,\n name: result.name,\n slug: result.slug,\n columnTypes: result.columnTypes,\n createdAt: result.createdAt,\n updatedAt: result.updatedAt,\n },\n recordsCreated: result.recordsCreated,\n datasetId: result.id,\n };\n }\n\n /**\n * Append strategy: try uploading to existing dataset; if not found, create from file.\n */\n private async _uploadAppend(slugOrId: string, file: File | Blob): Promise<UploadResponse> {\n try {\n return await this.uploadFile(slugOrId, file);\n } catch (error) {\n if (error instanceof DatasetNotFoundError) {\n return this.toUploadResponse(await this.createDatasetFromUpload({ name: slugOrId, file }));\n }\n throw error;\n }\n }\n\n /**\n * Replace strategy: if dataset exists, delete all records then upload; if not found, create from file.\n */\n private async _uploadReplace(slugOrId: string, file: File | Blob): Promise<UploadResponse> {\n try {\n await this.getDataset(slugOrId);\n await this._deleteAllRecords(slugOrId);\n return await this.uploadFile(slugOrId, file);\n } catch (error) {\n if (error instanceof DatasetNotFoundError) {\n return this.toUploadResponse(await this.createDatasetFromUpload({ name: slugOrId, file }));\n }\n throw error;\n }\n }\n\n /**\n * Error strategy: if dataset exists, throw 409; if not found, create from file.\n */\n private async _uploadError(slugOrId: string, file: File | Blob): Promise<UploadResponse> {\n let datasetExists = false;\n try {\n await this.getDataset(slugOrId);\n datasetExists = true;\n } catch (error) {\n if (!(error instanceof DatasetNotFoundError)) {\n throw error;\n }\n }\n\n if (datasetExists) {\n throw new DatasetApiError(\n `Dataset already exists: ${slugOrId}`,\n 409,\n \"upload\",\n );\n }\n\n return this.toUploadResponse(await this.createDatasetFromUpload({ name: slugOrId, file }));\n }\n\n /**\n * Deletes all records from a dataset by iterating through pages.\n * Always fetches page 1 since records shift after deletion.\n * Includes a safety valve to prevent infinite loops.\n */\n private async _deleteAllRecords(slugOrId: string): Promise<void> {\n const BATCH_SIZE = 1000;\n const MAX_DELETE_ITERATIONS = 100;\n\n let iteration = 0;\n while (iteration < MAX_DELETE_ITERATIONS) {\n const page = await this.listRecords(slugOrId, { page: 1, limit: BATCH_SIZE });\n if (page.data.length === 0) {\n return;\n }\n\n const ids = page.data.map((record) => record.id);\n await this.deleteRecords(slugOrId, ids);\n iteration++;\n }\n\n throw new DatasetApiError(\n `Failed to delete all records from dataset \"${slugOrId}\": exceeded ${MAX_DELETE_ITERATIONS} iterations`,\n 0,\n `delete all records from dataset \"${slugOrId}\"`,\n );\n }\n\n /**\n * Uploads a file to an existing dataset.\n */\n async uploadFile(\n slugOrId: string,\n file: File | Blob,\n ): Promise<UploadResponse> {\n this.config.logger.debug(`Uploading file to dataset: ${slugOrId}`);\n\n const formData = new FormData();\n formData.append(\"file\", file);\n\n return this.fetchMultipart<UploadResponse>(\n `/api/dataset/${encodeURIComponent(slugOrId)}/upload`,\n formData,\n `upload file to dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n}\n","import { type LangwatchApiClient } from \"@/internal/api/client\";\nimport { type Logger } from \"@/logger\";\nimport { DatasetService } from \"./dataset.service\";\nimport { DatasetValidationError } from \"./errors\";\nimport {\n type Dataset,\n type DatasetMetadata,\n type GetDatasetOptions,\n type ListDatasetsOptions,\n type ListDatasetsApiResponse,\n type ListRecordsOptions,\n type ListRecordsApiResponse,\n type CreateDatasetOptions,\n type UpdateDatasetOptions,\n type BatchCreateRecordsResponse,\n type DeleteRecordsResponse,\n type UploadResponse,\n type DatasetRecordResponse,\n} from \"./types\";\n\ntype DatasetsFacadeConfig = {\n langwatchApiClient: LangwatchApiClient;\n logger: Logger;\n endpoint: string;\n apiKey: string;\n};\n\n/**\n * Facade for dataset operations in the LangWatch SDK.\n * Provides a simplified interface for managing datasets, records, and file uploads.\n *\n * @example\n * ```typescript\n * const langwatch = new LangWatch({ apiKey: \"your-api-key\" });\n *\n * // List all datasets\n * const datasets = await langwatch.datasets.list();\n *\n * // Get a dataset by slug or ID\n * const dataset = await langwatch.datasets.get(\"my-dataset\");\n *\n * // Create a new dataset\n * const newDataset = await langwatch.datasets.create({\n * name: \"my-dataset\",\n * columnTypes: [{ name: \"input\", type: \"string\" }],\n * });\n *\n * // Update a dataset\n * const updated = await langwatch.datasets.update(\"my-dataset\", { name: \"new-name\" });\n *\n * // Delete a dataset\n * const archived = await langwatch.datasets.delete(\"my-dataset\");\n *\n * // Create records\n * const records = await langwatch.datasets.createRecords(\"my-dataset\", [\n * { input: \"hello\", output: \"world\" },\n * ]);\n *\n * // Update a record\n * const record = await langwatch.datasets.updateRecord(\"my-dataset\", \"rec-1\", { input: \"updated\" });\n *\n * // Delete records\n * const result = await langwatch.datasets.deleteRecords(\"my-dataset\", [\"rec-1\", \"rec-2\"]);\n *\n * // Upload a file (append to existing or create new)\n * const uploadResult = await langwatch.datasets.upload(\"my-dataset\", file);\n *\n * // Upload with replace strategy (delete all records first)\n * await langwatch.datasets.upload(\"my-dataset\", file, { ifExists: \"replace\" });\n * ```\n */\nexport class DatasetsFacade {\n readonly #datasetService: DatasetService;\n\n constructor(config: DatasetsFacadeConfig) {\n this.#datasetService = new DatasetService(config);\n }\n\n /**\n * Lists all datasets for the project, with optional pagination.\n *\n * @param options - Pagination options (page, limit)\n * @returns Paginated list of datasets with metadata\n */\n list = (options?: ListDatasetsOptions): Promise<ListDatasetsApiResponse> => {\n return this.#datasetService.listDatasets(options);\n };\n\n /**\n * Creates a new dataset.\n *\n * @param options - Dataset creation options (name, columnTypes)\n * @returns The created dataset metadata\n */\n create = (options: CreateDatasetOptions): Promise<DatasetMetadata> => {\n if (!options.name || options.name.trim().length === 0) {\n throw new DatasetValidationError(\"Dataset name must not be empty\");\n }\n return this.#datasetService.createDataset(options);\n };\n\n /**\n * Fetches a dataset by its slug or ID, returning metadata and entries.\n *\n * @param slugOrId - The slug or ID of the dataset to fetch\n * @param options - Optional configuration\n * @returns The dataset with metadata and entries\n *\n * @example\n * ```typescript\n * // Get dataset by slug\n * const dataset = await langwatch.datasets.get(\"product-qa\");\n *\n * // Typed dataset\n * type MyDatasetEntry = { input: string; expected_output: string; };\n * const dataset = await langwatch.datasets.get<MyDatasetEntry>(\"my-dataset\");\n *\n * // Iterate over entries\n * for (const entry of dataset.entries) {\n * console.log(entry.entry.input);\n * }\n * ```\n */\n get = <T extends Record<string, unknown> = Record<string, unknown>>(\n slugOrId: string,\n options?: GetDatasetOptions\n ): Promise<Dataset<T>> => {\n return this.#datasetService.getDataset<T>(slugOrId, options);\n };\n\n /**\n * Updates a dataset by its slug or ID.\n *\n * @param slugOrId - The slug or ID of the dataset to update\n * @param options - Fields to update (name, columnTypes)\n * @returns The updated dataset metadata\n */\n update = (slugOrId: string, options: UpdateDatasetOptions): Promise<DatasetMetadata> => {\n if (options.name == null && options.columnTypes == null) {\n throw new DatasetValidationError(\n \"At least one field (name or columnTypes) must be provided for update\",\n );\n }\n return this.#datasetService.updateDataset(slugOrId, options);\n };\n\n /**\n * Deletes (archives) a dataset by its slug or ID.\n *\n * @param slugOrId - The slug or ID of the dataset to delete\n * @returns The archived dataset metadata\n */\n delete = (slugOrId: string): Promise<DatasetMetadata> => {\n return this.#datasetService.deleteDataset(slugOrId);\n };\n\n /**\n * Creates records in a dataset in batch.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param entries - Array of record entries to create\n * @returns The created records with IDs\n */\n createRecords = (\n slugOrId: string,\n entries: Record<string, unknown>[],\n ): Promise<BatchCreateRecordsResponse> => {\n if (!entries || entries.length === 0) {\n throw new DatasetValidationError(\"Entries must not be empty\");\n }\n return this.#datasetService.createRecords(slugOrId, entries);\n };\n\n /**\n * Updates a single record in a dataset.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param recordId - The ID of the record to update\n * @param entry - The updated entry data\n * @returns The updated record\n */\n updateRecord = (\n slugOrId: string,\n recordId: string,\n entry: Record<string, unknown>,\n ): Promise<DatasetRecordResponse> => {\n return this.#datasetService.updateRecord(slugOrId, recordId, entry);\n };\n\n /**\n * Deletes records from a dataset by IDs.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param recordIds - Array of record IDs to delete\n * @returns Object with the count of deleted records\n */\n deleteRecords = (\n slugOrId: string,\n recordIds: string[],\n ): Promise<DeleteRecordsResponse> => {\n return this.#datasetService.deleteRecords(slugOrId, recordIds);\n };\n\n /**\n * Lists records in a dataset with optional pagination.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param options - Pagination options (page, limit)\n * @returns Paginated list of records\n *\n * @example\n * ```typescript\n * // List first page of records\n * const result = await langwatch.datasets.listRecords(\"my-dataset\");\n *\n * // With pagination\n * const page2 = await langwatch.datasets.listRecords(\"my-dataset\", { page: 2, limit: 25 });\n * ```\n */\n listRecords = (\n slugOrId: string,\n options?: ListRecordsOptions,\n ): Promise<ListRecordsApiResponse> => {\n return this.#datasetService.listRecords(slugOrId, options);\n };\n\n /**\n * Uploads a file to a dataset with a configurable strategy for handling existing datasets.\n *\n * Strategies:\n * - `\"append\"` (default): Upload to existing dataset; if not found, create a new one.\n * - `\"replace\"`: Delete all existing records, then upload; if not found, create a new one.\n * - `\"error\"`: Throw a 409 error if the dataset already exists; if not found, create a new one.\n *\n * @param slugOrId - The slug or ID of the dataset\n * @param file - The file to upload (File or Blob)\n * @param options - Upload options including the ifExists strategy\n * @returns The upload result\n *\n * @example\n * ```typescript\n * const file = new File([\"input,output\\nhello,world\"], \"data.csv\", { type: \"text/csv\" });\n *\n * // Append to existing or create new\n * await langwatch.datasets.upload(\"my-dataset\", file);\n *\n * // Replace all records\n * await langwatch.datasets.upload(\"my-dataset\", file, { ifExists: \"replace\" });\n *\n * // Fail if dataset already exists\n * await langwatch.datasets.upload(\"my-dataset\", file, { ifExists: \"error\" });\n * ```\n */\n upload = (\n slugOrId: string,\n file: File | Blob,\n options?: {\n ifExists?: \"append\" | \"replace\" | \"error\";\n },\n ): Promise<UploadResponse> => {\n if (!file) {\n throw new DatasetValidationError(\"File must be provided for upload\");\n }\n return this.#datasetService.uploadWithStrategy(\n slugOrId,\n file,\n options?.ifExists ?? \"append\",\n );\n };\n}\n","/**\n * Experiment - Main class for running batch experiments\n *\n * Provides a clean API for running experiments over datasets with:\n * - Automatic tracing per iteration\n * - Parallel execution with concurrency control\n * - Batched result sending\n * - Built-in evaluator support\n * - Multi-target comparison with withTarget() context isolation\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { trace, SpanStatusCode, ROOT_CONTEXT } from \"@opentelemetry/api\";\nimport { createLangWatchSpan } from \"@/observability-sdk/span/implementation\";\nimport type { LangWatchSpan } from \"@/observability-sdk/span/types\";\nimport type { LangwatchApiClient } from \"@/internal/api/client\";\nimport type { Logger } from \"@/logger\";\nimport { ensureSetup } from \"@/observability-sdk/setup/node\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { generateHumanReadableId } from \"./humanReadableId\";\nimport {\n ExperimentInitError,\n TargetMetadataConflictError,\n ComparisonError,\n EvaluatorError,\n} from \"./errors\";\nimport type {\n Batch,\n BatchEntry,\n ComparisonOptions,\n ComparisonVerdict,\n EvaluationResult,\n TargetInfo,\n TargetMetadata,\n ExperimentInitOptions,\n LogOptions,\n EvaluateOptions,\n RunOptions,\n RunCallback,\n RunContext,\n ExperimentInitResponse,\n LogResultsRequest,\n RunEvaluatorResponse,\n TargetCallback,\n TargetResult,\n TargetExecutionContext,\n TargetContext,\n} from \"./types\";\nimport type { CapturedTargetOutput } from \"./comparison\";\nimport {\n COMPARISON_EVALUATOR_SLUG,\n DEFAULT_COMPARISON_NAME,\n buildComparisonCandidates,\n buildComparisonData,\n buildComparisonSettings,\n comparisonEntryLabel,\n comparisonEntryStatus,\n describeSkippedComparison,\n renderTargetOutput,\n toComparisonVerdict,\n} from \"./comparison\";\nimport { printSummary } from \"./printSummary\";\nimport { buildAuthHeaders } from \"@/internal/api/auth\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nconst DEFAULT_CONCURRENCY = 4;\nconst DEBOUNCE_INTERVAL_MS = 1000;\n\n/**\n * How many rows of captured target outputs compare() keeps around.\n *\n * A comparison runs while its row is still being processed, and at most\n * `concurrency` rows are ever in flight, so this bound sits orders of\n * magnitude above what any run needs while keeping memory flat over a dataset\n * of any size.\n */\nconst MAX_COMPARISON_ROWS_RETAINED = 1000;\n\n/**\n * How long an evaluator call may take before the socket is given up on.\n *\n * The ceiling has to clear the slowest legitimate judge: a comparison with\n * swap-and-reconcile on makes two sequential LLM calls over every candidate\n * the row produced, and a large reasoning model can spend minutes on each. It\n * exists because the alternative is worse: a judge that accepts the socket and\n * never answers would hold its slot in the concurrency window for the life of\n * the process, so a single dead connection stalls the whole run rather than\n * costing it one row. This is the ceiling the Python SDK already applies to\n * the same endpoint.\n */\nexport const EVALUATOR_TIMEOUT_MS = 900_000;\n\n// Slim projections retained across the lifetime of an Experiment for\n// printSummary() — deliberately excludes large fields (inputs, tracebacks,\n// outputs) so running thousands of items doesn't unbound memory.\ntype SummaryEvaluation = Pick<\n EvaluationResult,\n \"name\" | \"evaluator\" | \"status\" | \"passed\" | \"score\" | \"cost\" | \"target_id\"\n>;\ntype SummaryEntry = Pick<BatchEntry, \"duration\" | \"error\" | \"cost\" | \"target_id\">;\n\n/**\n * AsyncLocalStorage for iteration context isolation.\n * This stores the current item, index and trace for each iteration,\n * preventing race conditions in concurrent execution.\n *\n * `traceId` is the row's own iteration trace, and is null for a row whose\n * targets each opened a trace of their own: there is no single trace such a\n * row belongs to, and naming one would attribute its comparison to a trace\n * that judged something else.\n */\ntype IterationContext = {\n index: number;\n item: unknown;\n traceId: string | null;\n};\nconst iterationContextStorage = new AsyncLocalStorage<IterationContext>();\n\n/**\n * AsyncLocalStorage for target context isolation.\n * This allows log() calls inside withTarget() to automatically\n * infer the target without explicit specification.\n */\nconst targetContextStorage = new AsyncLocalStorage<TargetExecutionContext>();\n\n/**\n * Experiment session for running batch experiments\n */\nexport class Experiment {\n readonly name: string;\n readonly runId: string;\n readonly experimentSlug: string;\n\n private readonly apiClient: LangwatchApiClient;\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly logger: Logger;\n private readonly concurrency: number;\n\n private initialized = false;\n private createdAtMs: number;\n private total = 0;\n private progress = 0;\n\n // Batching state\n private batch: Batch = { dataset: [], evaluations: [], targets: [] };\n private lastSentMs = 0;\n private pendingFlush: Promise<void> | null = null;\n private flushTimeout: ReturnType<typeof setTimeout> | null = null;\n\n // Cumulative record for printSummary() — never cleared, mirrors batch.\n // Store only the fields printSummary() needs, not the full payloads —\n // inputs/tracebacks/outputs can be large and don't belong in retained summary state.\n private cumulativeEvaluations: SummaryEvaluation[] = [];\n private cumulativeEntries: SummaryEntry[] = [];\n private runUrl = \"\";\n\n // Target registry\n private targets = new Map<string, TargetInfo>();\n\n // Per-row target outputs for compare(), keyed by row then by target name.\n // Captured when a withTarget() callback resolves and kept independently of\n // the batch, which is flushed on a timer and may be long gone by the time\n // the row is compared. Insertion-ordered, and reaped oldest-first past\n // MAX_COMPARISON_ROWS_RETAINED.\n private capturedOutputs = new Map<number, Map<string, CapturedTargetOutput>>();\n\n // Track whether withTarget() was used in the current iteration\n // If so, we don't create dataset entries in executeItem()\n // Note: This is now checked via iterationContextStorage to be thread-safe\n private iterationUsedWithTarget = new Map<number, boolean>();\n\n // Track whether withTarget() has EVER been used in this evaluation\n // Once set to true, we stop creating iteration-level traces\n private evaluationUsesTargets = false;\n\n private constructor(\n name: string,\n options: {\n apiClient: LangwatchApiClient;\n endpoint: string;\n apiKey: string;\n logger: Logger;\n runId?: string;\n concurrency?: number;\n }\n ) {\n this.name = name;\n this.experimentSlug = name;\n this.runId = options.runId ?? generateHumanReadableId();\n this.apiClient = options.apiClient;\n this.endpoint = resolveEndpoint(options.endpoint);\n this.apiKey = options.apiKey;\n this.logger = options.logger;\n this.concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;\n this.createdAtMs = Date.now();\n }\n\n /**\n * Initialize an evaluation session\n */\n static async init(\n name: string,\n options: {\n apiClient: LangwatchApiClient;\n endpoint: string;\n apiKey: string;\n logger: Logger;\n } & ExperimentInitOptions\n ): Promise<Experiment> {\n // Ensure observability is set up for proper tracing\n ensureSetup();\n\n const experiment = new Experiment(name, options);\n await experiment.initialize();\n return experiment;\n }\n\n /**\n * Initialize the evaluation by creating/getting the experiment\n */\n private async initialize(): Promise<void> {\n if (!this.apiKey) {\n throw new ExperimentInitError(\n \"API key is required. Set LANGWATCH_API_KEY or pass apiKey to LangWatch constructor.\"\n );\n }\n\n try {\n const response = await langwatchFetch(`${this.endpoint}/api/experiment/init`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildAuthHeaders({ apiKey: this.apiKey }),\n },\n body: JSON.stringify({\n experiment_name: this.name,\n experiment_slug: this.experimentSlug,\n experiment_type: \"BATCH_EVALUATION_V2\",\n }),\n });\n\n if (response.status === 401) {\n throw new ExperimentInitError(\"Invalid API key\");\n }\n\n if (!response.ok) {\n const text = await response.text();\n throw new ExperimentInitError(`Failed to initialize experiment: ${text}`);\n }\n\n const data = (await response.json()) as ExperimentInitResponse;\n (this as { experimentSlug: string }).experimentSlug = data.slug;\n\n const encodedRunId = encodeURIComponent(this.runId);\n this.runUrl = `${this.endpoint}${data.path}?runId=${encodedRunId}`;\n console.log(`Follow results at: ${this.runUrl}`);\n\n this.initialized = true;\n } catch (error) {\n if (error instanceof ExperimentInitError) {\n throw error;\n }\n throw new ExperimentInitError(\n `Failed to initialize evaluation: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Run evaluation over a dataset with a callback\n *\n * @param dataset - Array of items to evaluate\n * @param callback - Function called for each item with { item, index, span }\n * @param options - Concurrency options\n *\n * @example\n * ```typescript\n * await evaluation.run(dataset, async ({ item, index, span }) => {\n * const response = await myAgent(item.question);\n * evaluation.log('accuracy', { index, score: 0.95 });\n * }, { concurrency: 4 });\n * ```\n */\n async run<T>(\n dataset: T[],\n callback: RunCallback<T>,\n options?: RunOptions\n ): Promise<void> {\n if (!this.initialized) {\n await this.initialize();\n }\n\n const concurrency = options?.concurrency ?? this.concurrency;\n this.total = dataset.length;\n this.progress = 0;\n\n const tracer = trace.getTracer(\"langwatch\");\n\n // Process items with concurrency control\n const executing = new Set<Promise<void>>();\n\n for (let index = 0; index < dataset.length; index++) {\n const item = dataset[index] as T;\n\n const itemPromise = this.executeItem(tracer, item, index, callback);\n\n executing.add(itemPromise);\n void itemPromise.finally(() => executing.delete(itemPromise));\n\n // Wait if we've hit concurrency limit\n if (executing.size >= concurrency) {\n await Promise.race(executing);\n }\n }\n\n // Wait for all remaining items\n await Promise.all(executing);\n\n // Flush OTEL spans so all child spans created inside callbacks are exported\n const provider = trace.getTracerProvider();\n const delegate = \"getDelegate\" in provider && typeof provider.getDelegate === \"function\"\n ? provider.getDelegate()\n : provider;\n if (delegate && \"forceFlush\" in delegate && typeof delegate.forceFlush === \"function\") {\n await delegate.forceFlush();\n }\n\n // Send final batch with finished timestamp\n await this.flush(true);\n }\n\n /**\n * Execute a single item in the dataset\n */\n private async executeItem<T>(\n tracer: ReturnType<typeof trace.getTracer>,\n item: T,\n index: number,\n callback: RunCallback<T>\n ): Promise<void> {\n const startTime = Date.now();\n let error: Error | undefined;\n let capturedTraceId: string | null = null;\n\n // Reset withTarget tracking for this iteration\n this.iterationUsedWithTarget.set(index, false);\n\n // Set up iteration context (thread-safe via AsyncLocalStorage)\n const iterationContext: IterationContext = { index, item, traceId: null };\n\n // If evaluation uses targets, skip creating iteration-level traces\n // Each withTarget() call will create its own independent trace\n if (this.evaluationUsesTargets) {\n await iterationContextStorage.run(iterationContext, async () => {\n try {\n // Create a minimal span context for the callback\n const span = {\n setStatus: () => { /* no-op */ },\n recordException: () => { /* no-op */ },\n end: () => { /* no-op */ },\n } as unknown as LangWatchSpan;\n\n const ctx: RunContext<T> = { item, index, span };\n const result = callback(ctx);\n\n if (result && typeof result.then === \"function\") {\n await result;\n }\n } catch (err) {\n error = err instanceof Error ? err : new Error(String(err));\n this.logger.error(`Evaluation error at index ${index}:`, error);\n }\n });\n } else {\n await iterationContextStorage.run(iterationContext, async () => {\n await tracer.startActiveSpan(\n \"evaluation.iteration\",\n {\n attributes: {\n \"langwatch.origin\": \"evaluation\",\n \"evaluation.run_id\": this.runId,\n \"evaluation.index\": index,\n },\n },\n async (otelSpan) => {\n const span = createLangWatchSpan(otelSpan);\n const spanContext = otelSpan.spanContext();\n const traceId = spanContext.traceId;\n\n // The row's own trace, for the log/evaluate/compare calls made\n // inside it. It rides the iteration context rather than the\n // instance, so a row reads its own trace and never a neighbour's.\n iterationContext.traceId = traceId;\n capturedTraceId = traceId;\n\n try {\n const ctx: RunContext<T> = { item, index, span };\n const result = callback(ctx);\n\n if (result && typeof result.then === \"function\") {\n await result;\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n } catch (err) {\n error = err instanceof Error ? err : new Error(String(err));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n span.recordException(error);\n this.logger.error(`Evaluation error at index ${index}:`, error);\n } finally {\n span.end();\n }\n }\n );\n });\n }\n\n // Only add a dataset entry if withTarget() was NOT used\n // When withTarget() is used, it creates its own dataset entries per target\n if (!this.iterationUsedWithTarget.get(index)) {\n const duration = Date.now() - startTime;\n const entry: BatchEntry = {\n index,\n entry: this.serializeItem(item),\n duration,\n error: error?.message ?? null,\n trace_id: capturedTraceId ?? this.getTraceIdFromContext(),\n };\n\n this.batch.dataset.push(entry);\n this.cumulativeEntries.push({\n duration: entry.duration,\n error: entry.error,\n cost: entry.cost,\n target_id: entry.target_id,\n });\n }\n\n // Clean up\n this.iterationUsedWithTarget.delete(index);\n\n this.progress++;\n\n // Debounced send\n this.scheduleSend();\n }\n\n /**\n * Log a custom metric result\n *\n * @param metric - Name of the metric\n * @param options - Metric options including index, score, passed, etc.\n *\n * If called inside a withTarget() block, the target and index are automatically\n * inferred from the context and don't need to be specified.\n *\n * @example\n * ```typescript\n * // Explicit target (outside withTarget)\n * evaluation.log('accuracy', { index, score: 0.95, target: 'gpt-4' });\n *\n * // Implicit target (inside withTarget)\n * await evaluation.withTarget('gpt-4', { model: 'openai/gpt-4' }, async () => {\n * evaluation.log('accuracy', { score: 0.95 }); // target and index auto-inferred\n * });\n * ```\n */\n log(metric: string, options: LogOptions): void {\n // Get context from AsyncLocalStorage (if inside withTarget)\n const targetContext = targetContextStorage.getStore();\n\n const {\n data = {},\n score,\n passed,\n label,\n details,\n status = options.error ? \"error\" : \"processed\",\n duration,\n cost,\n error,\n // Use context values as defaults, allow explicit override\n target = targetContext?.targetId,\n metadata,\n index = targetContext?.index ?? options.index,\n } = options;\n\n // Register target if provided (explicit or from context)\n let targetId: string | undefined;\n if (target) {\n targetId = this.registerTarget(target, metadata);\n }\n\n // Use trace ID from the target, then the row being iterated, then OTEL context\n const traceId =\n targetContext?.traceId ??\n iterationContextStorage.getStore()?.traceId ??\n this.getTraceIdFromContext();\n\n const result: EvaluationResult = {\n name: metric,\n evaluator: metric,\n trace_id: traceId,\n status,\n data,\n score: score ?? null,\n passed: passed ?? null,\n details: details ?? (error ? error.message : null),\n index,\n label: label ?? null,\n cost: cost ?? null,\n duration: duration ?? null,\n error_type: error ? error.name : null,\n traceback: error ? [error.stack ?? error.message] : null,\n target_id: targetId ?? null,\n };\n\n this.pushEvaluation(result);\n }\n\n /**\n * Queue an evaluation result for the next batch\n */\n private pushEvaluation(result: EvaluationResult): void {\n this.batch.evaluations.push(result);\n this.cumulativeEvaluations.push({\n name: result.name,\n evaluator: result.evaluator,\n status: result.status,\n passed: result.passed,\n score: result.score,\n cost: result.cost,\n target_id: result.target_id,\n });\n this.scheduleSend();\n }\n\n /**\n * Run a built-in evaluator\n *\n * @param evaluatorSlug - The evaluator identifier (e.g., 'ragas/faithfulness')\n * @param options - Evaluator options including data and settings\n *\n * If called inside a withTarget() block, the target and index are automatically\n * inferred from the context and don't need to be specified.\n *\n * @example\n * ```typescript\n * // Inside withTarget() - target and index auto-inferred\n * await evaluation.withTarget('gpt-4', { model: 'openai/gpt-4' }, async () => {\n * await evaluation.evaluate('ragas/faithfulness', {\n * data: { input, output, contexts },\n * });\n * });\n *\n * // Or explicit index/target\n * await evaluation.evaluate('ragas/faithfulness', {\n * index,\n * data: { input, output, contexts },\n * target: 'gpt-4',\n * });\n * ```\n */\n async evaluate(evaluatorSlug: string, options: EvaluateOptions): Promise<void> {\n // Get context from AsyncLocalStorage (if inside withTarget)\n const targetContext = targetContextStorage.getStore();\n\n const {\n data,\n settings,\n name,\n asGuardrail = false,\n // Use context values as defaults, allow explicit override\n target = targetContext?.targetId,\n metadata,\n index = targetContext?.index ?? options.index,\n } = options;\n\n const startTime = Date.now();\n // Use trace ID from the target, then the row being iterated, then OTEL context\n const traceId =\n targetContext?.traceId ??\n iterationContextStorage.getStore()?.traceId ??\n this.getTraceIdFromContext();\n const spanId = targetContext?.spanId ?? this.getSpanIdFromContext();\n\n try {\n const result = await this.callEvaluator({\n evaluatorSlug,\n data,\n settings,\n name,\n traceId,\n spanId,\n asGuardrail,\n });\n const duration = Date.now() - startTime;\n\n // Log the result\n this.log(name ?? evaluatorSlug, {\n index,\n data,\n status: result.status,\n score: result.score ?? undefined,\n passed: result.passed ?? undefined,\n details: result.details ?? undefined,\n label: result.label ?? undefined,\n duration,\n cost: result.cost?.amount,\n target,\n metadata,\n });\n } catch (error) {\n const duration = Date.now() - startTime;\n\n if (error instanceof EvaluatorError) {\n this.log(name ?? evaluatorSlug, {\n index,\n data,\n status: \"error\",\n duration,\n error: error,\n target,\n metadata,\n });\n throw error;\n }\n\n const wrappedError = new EvaluatorError(\n evaluatorSlug,\n error instanceof Error ? error.message : String(error),\n error instanceof Error ? error : undefined\n );\n\n this.log(name ?? evaluatorSlug, {\n index,\n data,\n status: \"error\",\n duration,\n error: wrappedError,\n target,\n metadata,\n });\n\n throw wrappedError;\n }\n }\n\n /**\n * Call an evaluator and return its raw result\n */\n private async callEvaluator({\n evaluatorSlug,\n data,\n settings,\n name,\n traceId,\n spanId,\n asGuardrail = false,\n }: {\n evaluatorSlug: string;\n data: Record<string, unknown>;\n settings?: Record<string, unknown>;\n name?: string;\n traceId?: string | null;\n spanId?: string | null;\n asGuardrail?: boolean;\n }): Promise<RunEvaluatorResponse> {\n const response = await langwatchFetch(\n `${this.endpoint}/api/evaluations/${evaluatorSlug}/evaluate`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildAuthHeaders({ apiKey: this.apiKey }),\n },\n body: JSON.stringify({\n trace_id: traceId ?? null,\n span_id: spanId ?? null,\n name: name ?? evaluatorSlug,\n data,\n settings,\n as_guardrail: asGuardrail,\n }),\n signal: AbortSignal.timeout(EVALUATOR_TIMEOUT_MS),\n }\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new EvaluatorError(evaluatorSlug, text);\n }\n\n return (await response.json()) as RunEvaluatorResponse;\n }\n\n /**\n * Compare a row's targets and pick the best one\n *\n * Every target that recorded an output for the row is judged together in a\n * single call, and the winner comes back named with the target name you\n * registered. Call it once the row's withTarget() calls have all settled,\n * which under the usual Promise.all is right after the await.\n *\n * The verdict grades no single target, so it is recorded against the row.\n *\n * The row is named the way log() names it: inside a run() callback it is\n * inferred from the row being processed, and outside one it is required.\n *\n * A row with fewer than two outputs cannot be compared: it is recorded as\n * skipped, naming the targets that produced nothing, and the run carries on.\n * A judge that fails or cannot be reached comes back as an `error` verdict\n * for the same reason, and stays distinct from `inconclusive`, which is the\n * judge telling you something about your candidates. Naming a target that\n * produced no output is a different thing again, and throws, because a\n * two-way verdict read as the three-way one you asked for is worse than a\n * failure.\n *\n * @param options - How the judge should read the row, and which row when\n * it cannot be inferred\n * @returns The verdict, including the candidates it actually saw\n *\n * @example\n * ```typescript\n * await experiment.run(dataset, async ({ item }) => {\n * await Promise.all([\n * experiment.withTarget('gpt-5-mini', () => askGpt(item.question)),\n * experiment.withTarget('claude-sonnet-5', () => askClaude(item.question)),\n * ]);\n *\n * const verdict = await experiment.compare({ input: item.question });\n * console.log(verdict.winner);\n * });\n * ```\n */\n async compare(options: ComparisonOptions = {}): Promise<ComparisonVerdict> {\n const {\n name = DEFAULT_COMPARISON_NAME,\n targets,\n input,\n golden,\n } = options;\n\n const index = this.resolveComparisonRow(options.index);\n\n const captured =\n this.capturedOutputs.get(index) ?? new Map<string, CapturedTargetOutput>();\n\n if (targets) {\n const missing = targets.filter((target) => !captured.has(target));\n if (missing.length > 0) {\n throw new ComparisonError(\n `Cannot compare row ${index}: no output was recorded for ${missing\n .map((target) => `'${target}'`)\n .join(\", \")}. Compare a row once every withTarget() call for it has settled.`,\n missing\n );\n }\n }\n\n // Registration order, not the order the outputs happened to land in:\n // targets usually run concurrently, so completion order varies per row and\n // would reshuffle what the judge sees from one row to the next.\n const requested = targets ? new Set(targets) : null;\n const candidates = Array.from(this.targets.keys()).filter(\n (target) =>\n captured.has(target) && (requested === null || requested.has(target))\n );\n\n if (candidates.length < 2) {\n const missing = Array.from(this.targets.keys()).filter(\n (target) =>\n !captured.has(target) && (requested === null || requested.has(target))\n );\n const verdict: ComparisonVerdict = {\n status: \"skipped\",\n winner: null,\n reasoning: describeSkippedComparison({ candidates, missing }),\n candidates,\n };\n\n this.recordComparison({ name, index, verdict });\n\n return verdict;\n }\n\n const data = buildComparisonData({\n input,\n golden,\n candidates: buildComparisonCandidates(candidates, captured),\n index,\n });\n const settings = buildComparisonSettings(options);\n\n const startTime = Date.now();\n\n // A judge that could not be reached measured nothing about the\n // candidates, so it is an errored row rather than a finding about them.\n // One unreachable judge must not end a run over a thousand rows either,\n // so the failure is reported to the caller instead of thrown.\n let response: RunEvaluatorResponse;\n try {\n response = await this.callEvaluator({\n evaluatorSlug: COMPARISON_EVALUATOR_SLUG,\n data,\n settings,\n name,\n traceId:\n iterationContextStorage.getStore()?.traceId ??\n this.getTraceIdFromContext(),\n spanId: this.getSpanIdFromContext(),\n });\n } catch (error) {\n response = {\n status: \"error\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n\n const verdict = toComparisonVerdict({ response, candidates });\n\n this.recordComparison({\n name,\n index,\n verdict,\n score: response.score ?? null,\n cost: response.cost?.amount ?? null,\n duration: Date.now() - startTime,\n });\n\n return verdict;\n }\n\n /**\n * The row a comparison is about\n *\n * Inferred from the row being processed, the same way withTarget() and\n * log() infer theirs, so a comparison inside a run() callback never repeats\n * an index the SDK already has. It reads that row from the iteration's own\n * context, which is what keeps a run of many rows at once from handing one\n * row's comparison the row a neighbour happens to be on.\n *\n * What it deliberately does not do is fall back to a row: judging row 0\n * because no row was named would hand back a confident verdict about\n * candidates the caller never asked about.\n */\n private resolveComparisonRow(index?: number): number {\n const resolved = index ?? iterationContextStorage.getStore()?.index;\n\n if (resolved === undefined) {\n throw new ComparisonError(\n \"Cannot compare: no row was given and none could be inferred. Pass index explicitly when comparing outside a run() iteration.\"\n );\n }\n\n return resolved;\n }\n\n /**\n * Record a comparison verdict against the row\n *\n * The verdict the caller is handed is the only input, so the row can never\n * say one thing while the return value says another.\n *\n * Deliberately not routed through log(): a comparison grades no single\n * target, so it must not pick one up from an ambient withTarget() context,\n * and it is recorded under the judge's own evaluator id so the results page\n * can tell it apart from a hand-logged metric.\n */\n private recordComparison({\n name,\n index,\n verdict,\n score = null,\n cost = null,\n duration = null,\n }: {\n name: string;\n index: number;\n verdict: ComparisonVerdict;\n score?: number | null;\n cost?: number | null;\n duration?: number | null;\n }): void {\n const status = comparisonEntryStatus(verdict.status);\n\n this.pushEvaluation({\n name,\n evaluator: COMPARISON_EVALUATOR_SLUG,\n trace_id:\n iterationContextStorage.getStore()?.traceId ??\n this.getTraceIdFromContext(),\n status,\n // The results page keys a comparison column off this candidate list.\n data: {\n candidates: verdict.candidates.map((candidate) => ({ id: candidate })),\n },\n // A row that established nothing carries no score: 0.5 would read as the\n // tie it is not, and 1.0 as a win for a winner it does not have.\n score: status === \"processed\" ? score : null,\n passed: null,\n details: verdict.reasoning,\n index,\n label: comparisonEntryLabel(verdict),\n cost,\n duration,\n error_type: null,\n traceback: null,\n target_id: null,\n });\n }\n\n /**\n * Execute code within a target context with automatic tracing\n *\n * Creates a new span for this target execution and sets up context\n * so that log() calls inside the callback automatically use this target.\n * Duration and output are captured automatically.\n *\n * This creates a dataset entry per target (like Evaluations V3), enabling\n * proper per-target latency and cost tracking.\n *\n * @param targetName - Unique identifier for the target\n * @param metadata - Optional metadata for comparison (e.g., { model: 'gpt-4' })\n * @param callback - Function to execute within the target context\n * @returns The callback result along with captured metrics\n *\n * @example\n * ```typescript\n * await evaluation.run(dataset, async ({ item, index }) => {\n * // Compare GPT-4 and Claude on the same input\n * const [gpt4Result, claudeResult] = await Promise.all([\n * evaluation.withTarget('gpt-4', { model: 'openai/gpt-4' }, async () => {\n * const response = await openai.chat(item.question);\n * evaluation.log('quality', { score: 0.95 }); // target auto-inferred\n * return response;\n * }),\n * evaluation.withTarget('claude-3', { model: 'anthropic/claude-3' }, async () => {\n * const response = await anthropic.messages(item.question);\n * evaluation.log('quality', { score: 0.85 }); // target auto-inferred\n * return response;\n * }),\n * ]);\n * });\n * ```\n */\n async withTarget<R>(\n targetName: string,\n metadata: TargetMetadata | null,\n callback: TargetCallback<R>\n ): Promise<TargetResult<R>>;\n async withTarget<R>(\n targetName: string,\n callback: TargetCallback<R>\n ): Promise<TargetResult<R>>;\n async withTarget<R>(\n targetName: string,\n metadataOrCallback: TargetMetadata | null | TargetCallback<R>,\n maybeCallback?: TargetCallback<R>\n ): Promise<TargetResult<R>> {\n // Handle overloads\n const metadata =\n typeof metadataOrCallback === \"function\" ? null : metadataOrCallback;\n const callback =\n typeof metadataOrCallback === \"function\" ? metadataOrCallback : maybeCallback!;\n\n // On FIRST withTarget() call ever in this evaluation:\n // - Set flag to skip creating iteration-level traces going forward\n if (!this.evaluationUsesTargets) {\n this.evaluationUsesTargets = true;\n }\n\n // Get iteration context (thread-safe via AsyncLocalStorage)\n const iterationContext = iterationContextStorage.getStore();\n const index = iterationContext?.index ?? 0;\n const currentItem = iterationContext?.item;\n\n // Mark that withTarget() was used - prevents executeItem from creating a dataset entry\n this.iterationUsedWithTarget.set(index, true);\n\n // Register target\n this.registerTarget(targetName, metadata ?? undefined);\n\n const tracer = trace.getTracer(\"langwatch\");\n const startTime = Date.now();\n let result: R | undefined;\n let traceId = \"\";\n let spanId = \"\";\n let callbackError: Error | undefined;\n\n // Create an INDEPENDENT root span using ROOT_CONTEXT\n // This ensures each target gets a unique trace_id, not shared with iteration\n await tracer.startActiveSpan(\n `evaluation.target.${targetName}`,\n {\n attributes: {\n \"langwatch.origin\": \"evaluation\",\n \"evaluation.run_id\": this.runId,\n \"evaluation.target\": targetName,\n \"evaluation.index\": index,\n },\n },\n ROOT_CONTEXT,\n async (otelSpan) => {\n const span = createLangWatchSpan(otelSpan);\n const spanContext = otelSpan.spanContext();\n const rawTraceId = spanContext.traceId;\n spanId = spanContext.spanId;\n\n // Check if this is a no-op trace (all zeros = no tracer configured)\n const isNoOpTrace = rawTraceId === \"00000000000000000000000000000000\";\n traceId = isNoOpTrace ? \"\" : rawTraceId;\n\n // Set up the target execution context\n const executionContext: TargetExecutionContext = {\n targetId: targetName,\n traceId,\n spanId,\n index,\n };\n\n try {\n // Run callback within AsyncLocalStorage context\n result = await targetContextStorage.run(executionContext, async () => {\n const ctx: TargetContext = { span, traceId, spanId };\n const callbackResult = callback(ctx);\n\n if (callbackResult && typeof (callbackResult as Promise<R>).then === \"function\") {\n return await callbackResult;\n }\n return callbackResult as R;\n });\n\n span.setStatus({ code: SpanStatusCode.OK });\n } catch (err) {\n callbackError = err instanceof Error ? err : new Error(String(err));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: callbackError.message,\n });\n span.recordException(callbackError);\n throw err;\n } finally {\n span.end();\n }\n }\n );\n\n const duration = Date.now() - startTime;\n\n // Serialize the result as \"predicted\" output (similar to Evaluations V3)\n let predicted: Record<string, unknown> | null = null;\n if (result !== undefined && result !== null) {\n predicted = typeof result === \"object\"\n ? (result as Record<string, unknown>)\n : { output: result };\n }\n\n // Create a dataset entry for this target execution (like Evaluations V3)\n // This captures per-target duration/latency properly\n // Use currentItem from iteration context (thread-safe), NOT shared instance variable\n const entry: BatchEntry = {\n index,\n entry: this.serializeItem(currentItem),\n duration,\n error: callbackError?.message ?? null,\n trace_id: traceId || null, // null if no tracer configured (no-op)\n target_id: targetName,\n predicted,\n };\n\n this.batch.dataset.push(entry);\n this.cumulativeEntries.push({\n duration: entry.duration,\n error: entry.error,\n cost: entry.cost,\n target_id: entry.target_id,\n });\n this.captureTargetOutput({\n index,\n targetName,\n output: renderTargetOutput(result),\n durationMs: duration,\n });\n this.scheduleSend();\n\n return {\n result: result!,\n duration,\n traceId,\n spanId,\n };\n }\n\n /**\n * Keep a target's output for the row so compare() can reach it later\n *\n * The batch this output also went into is flushed on a timer and cleared, so\n * a comparison that read its candidates from there would find them missing\n * whenever the flush landed first. A target that produced nothing is not\n * captured at all: it has no output to judge.\n */\n private captureTargetOutput({\n index,\n targetName,\n output,\n durationMs,\n }: {\n index: number;\n targetName: string;\n output: string;\n durationMs?: number;\n }): void {\n if (output === \"\") {\n return;\n }\n\n let row = this.capturedOutputs.get(index);\n if (!row) {\n row = new Map<string, CapturedTargetOutput>();\n this.capturedOutputs.set(index, row);\n }\n row.set(targetName, { output, durationMs });\n\n while (this.capturedOutputs.size > MAX_COMPARISON_ROWS_RETAINED) {\n const oldest = this.capturedOutputs.keys().next();\n if (oldest.done) break;\n this.capturedOutputs.delete(oldest.value);\n }\n }\n\n /**\n * Register a target for multi-target comparison\n */\n private registerTarget(name: string, metadata?: TargetMetadata): string {\n const existing = this.targets.get(name);\n\n if (existing) {\n // Check for metadata conflict\n if (metadata) {\n const existingMeta = existing.metadata ?? {};\n if (JSON.stringify(existingMeta) !== JSON.stringify(metadata)) {\n throw new TargetMetadataConflictError(name, existingMeta, metadata);\n }\n }\n return name;\n }\n\n // Register new target\n const targetInfo: TargetInfo = {\n id: name,\n name,\n type: \"custom\",\n metadata: metadata ?? null,\n };\n\n this.targets.set(name, targetInfo);\n this.batch.targets.push(targetInfo);\n\n return name;\n }\n\n /**\n * Schedule a debounced send\n */\n private scheduleSend(): void {\n const now = Date.now();\n\n if (now - this.lastSentMs >= DEBOUNCE_INTERVAL_MS) {\n this.sendBatch();\n } else {\n this.flushTimeout ??= setTimeout(() => {\n this.flushTimeout = null;\n this.sendBatch();\n }, DEBOUNCE_INTERVAL_MS - (now - this.lastSentMs));\n }\n }\n\n /**\n * Print a CI-friendly summary and optionally exit with code 1 on failure.\n *\n * Mirrors `ExperimentRunResult.printSummary` for parity — SDK-driven experiments\n * can now fail CI builds the same way platform experiments do.\n *\n * @param exitOnFailure - If true (default), calls `process.exit(1)` when any evaluation failed.\n *\n * @example\n * ```typescript\n * const experiment = await langwatch.experiments.init(\"ci-quality-check\");\n * await experiment.run(dataset, async ({ item, index }) => {\n * const response = await myLLM(item.input);\n * await experiment.evaluate(\"ragas/faithfulness\", { index, data: { input: item.input, output: response } });\n * });\n * experiment.printSummary();\n * ```\n */\n printSummary(exitOnFailure = true): void {\n const evaluators = new Map<\n string,\n { passed: number; failed: number; scoreSum: number; scoreCount: number }\n >();\n let totalPassed = 0;\n let totalFailed = 0;\n let totalCost = 0;\n\n for (const e of this.cumulativeEvaluations) {\n const name = e.name ?? e.evaluator ?? \"unknown\";\n if (!evaluators.has(name)) {\n evaluators.set(name, { passed: 0, failed: 0, scoreSum: 0, scoreCount: 0 });\n }\n const stats = evaluators.get(name)!;\n // Evaluators that crashed come back as status:\"error\" with passed often null;\n // treat them as failures so CI doesn't silently succeed on evaluator crashes.\n if (e.status === \"error\" || e.passed === false) {\n stats.failed += 1;\n totalFailed += 1;\n } else if (e.passed === true) {\n stats.passed += 1;\n totalPassed += 1;\n }\n if (typeof e.score === \"number\") {\n stats.scoreSum += e.score;\n stats.scoreCount += 1;\n }\n if (typeof e.cost === \"number\") {\n totalCost += e.cost;\n }\n }\n\n const targets = new Map<\n string,\n { passed: number; failed: number; latencySum: number; latencyCount: number; cost: number }\n >();\n for (const entry of this.cumulativeEntries) {\n const tid = entry.target_id;\n if (!tid) continue;\n if (!targets.has(tid)) {\n targets.set(tid, { passed: 0, failed: 0, latencySum: 0, latencyCount: 0, cost: 0 });\n }\n const stats = targets.get(tid)!;\n if (typeof entry.duration === \"number\") {\n stats.latencySum += entry.duration;\n stats.latencyCount += 1;\n }\n if (typeof entry.cost === \"number\") {\n stats.cost += entry.cost;\n }\n }\n // Lazily seed target stats from evaluations too — a user can log an\n // evaluation with an explicit target_id without ever going through\n // withTarget(), in which case the per-target entry row may not carry\n // target_id. Without this, such a target would be silently dropped from\n // the per-target summary.\n for (const e of this.cumulativeEvaluations) {\n const tid = e.target_id;\n if (!tid) continue;\n if (!targets.has(tid)) {\n targets.set(tid, { passed: 0, failed: 0, latencySum: 0, latencyCount: 0, cost: 0 });\n }\n const stats = targets.get(tid)!;\n if (e.status === \"error\" || e.passed === false) stats.failed += 1;\n else if (e.passed === true) stats.passed += 1;\n if (typeof e.cost === \"number\") stats.cost += e.cost;\n }\n\n const total = totalPassed + totalFailed;\n const passRate = total > 0 ? (totalPassed / total) * 100 : 0;\n\n // Wall-clock duration from init — summing entry durations over-counts\n // under concurrent withTarget() calls (each target produces its own entry).\n const duration = Math.max(0, Date.now() - this.createdAtMs);\n\n // Count entries whose target/loop execution errored out — distinct from\n // evaluator failures but must also trigger CI exit.\n let failedCells = 0;\n for (const entry of this.cumulativeEntries) {\n if (typeof entry.cost === \"number\") totalCost += entry.cost;\n if (entry.error) failedCells += 1;\n }\n\n const hasFailures = totalFailed > 0 || failedCells > 0;\n\n printSummary({\n runId: this.runId,\n status: hasFailures ? \"failed\" : \"completed\",\n passed: totalPassed,\n failed: totalFailed,\n passRate,\n duration,\n runUrl: this.runUrl,\n summary: {\n runId: this.runId,\n totalCells: this.cumulativeEntries.length || total,\n completedCells: Math.max(0, (this.cumulativeEntries.length || total) - failedCells),\n failedCells,\n duration,\n runUrl: this.runUrl,\n totalPassed,\n totalFailed,\n passRate,\n totalCost,\n targets: Array.from(targets.entries()).map(([targetId, s]) => ({\n targetId,\n name: targetId,\n passed: s.passed,\n failed: s.failed,\n avgLatency: s.latencyCount > 0 ? s.latencySum / s.latencyCount : 0,\n totalCost: s.cost,\n })),\n evaluators: Array.from(evaluators.entries()).map(([name, s]) => ({\n evaluatorId: name,\n name,\n passed: s.passed,\n failed: s.failed,\n passRate: s.passed + s.failed > 0 ? (s.passed / (s.passed + s.failed)) * 100 : 0,\n avgScore: s.scoreCount > 0 ? s.scoreSum / s.scoreCount : undefined,\n })),\n },\n });\n\n if (exitOnFailure && hasFailures) {\n process.exit(1);\n }\n }\n\n /**\n * Send current batch to the API\n */\n private sendBatch(finished = false): void {\n if (\n this.batch.dataset.length === 0 &&\n this.batch.evaluations.length === 0 &&\n this.batch.targets.length === 0 &&\n !finished\n ) {\n return;\n }\n\n const body: LogResultsRequest = {\n experiment_slug: this.experimentSlug,\n name: this.name,\n run_id: this.runId,\n dataset: this.batch.dataset.map((entry) => ({\n index: entry.index,\n entry: entry.entry,\n duration: entry.duration,\n error: entry.error,\n trace_id: entry.trace_id,\n target_id: entry.target_id ?? null,\n cost: entry.cost ?? null,\n // Only include predicted if it's defined (omit entirely when null/undefined)\n ...(entry.predicted !== undefined && entry.predicted !== null\n ? { predicted: entry.predicted }\n : {}),\n })),\n evaluations: this.batch.evaluations.map((e) => ({\n name: e.name,\n evaluator: e.evaluator,\n trace_id: e.trace_id,\n status: e.status,\n inputs: e.data,\n score: e.score,\n passed: e.passed,\n details: e.details,\n index: e.index,\n label: e.label,\n cost: e.cost,\n duration: e.duration,\n target_id: e.target_id,\n })),\n targets: this.batch.targets,\n progress: this.progress,\n total: this.total,\n timestamps: {\n created_at: this.createdAtMs,\n finished_at: finished ? Date.now() : null,\n },\n };\n\n // Fire and forget (with error logging)\n this.pendingFlush = langwatchFetch(`${this.endpoint}/api/evaluations/batch/log_results`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildAuthHeaders({ apiKey: this.apiKey }),\n },\n body: JSON.stringify(body),\n })\n .then((response) => {\n if (!response.ok) {\n this.logger.error(`Failed to send batch: ${response.status}`);\n }\n })\n .catch((error) => {\n this.logger.error(\"Failed to send batch:\", error);\n });\n\n // Clear batch\n this.batch = { dataset: [], evaluations: [], targets: [] };\n this.lastSentMs = Date.now();\n }\n\n /**\n * Flush all pending data\n */\n private async flush(finished = false): Promise<void> {\n if (this.flushTimeout) {\n clearTimeout(this.flushTimeout);\n this.flushTimeout = null;\n }\n\n this.sendBatch(finished);\n\n if (this.pendingFlush) {\n await this.pendingFlush;\n }\n }\n\n /**\n * Serialize a dataset item for the API\n */\n private serializeItem(item: unknown): unknown {\n if (item === null || item === undefined) {\n return item;\n }\n\n if (typeof item === \"object\") {\n // Handle objects with toJSON method\n if (\"toJSON\" in item && typeof (item as { toJSON: unknown }).toJSON === \"function\") {\n return (item as { toJSON: () => unknown }).toJSON();\n }\n // Return as-is, JSON.stringify will handle it\n return item;\n }\n\n return item;\n }\n\n /**\n * Get trace ID from current OpenTelemetry context\n */\n private getTraceIdFromContext(): string {\n const span = trace.getActiveSpan();\n if (span) {\n return span.spanContext().traceId;\n }\n return \"\";\n }\n\n /**\n * Get span ID from current OpenTelemetry context\n */\n private getSpanIdFromContext(): string | null {\n const span = trace.getActiveSpan();\n if (span) {\n return span.spanContext().spanId;\n }\n return null;\n }\n}\n","/**\n * Human-readable ID generator\n *\n * Generates memorable, human-readable identifiers using adjective-adjective-noun combinations.\n * Similar to Docker container names, Heroku app names, etc.\n */\n\nconst ADJECTIVES = [\n \"swift\", \"bright\", \"calm\", \"eager\", \"bold\", \"keen\", \"warm\", \"cool\", \"wise\", \"fair\",\n \"glad\", \"kind\", \"neat\", \"pure\", \"safe\", \"true\", \"vast\", \"wild\", \"zesty\", \"agile\",\n \"brave\", \"crisp\", \"dense\", \"epic\", \"fresh\", \"grand\", \"happy\", \"ideal\", \"jolly\", \"lively\",\n \"merry\", \"noble\", \"proud\", \"quick\", \"rapid\", \"sharp\", \"smart\", \"solid\", \"sunny\", \"vivid\",\n \"gentle\", \"silent\", \"cosmic\", \"golden\", \"silver\", \"ancient\", \"modern\", \"mighty\", \"humble\",\n];\n\nconst NOUNS = [\n \"fox\", \"owl\", \"bee\", \"elk\", \"hawk\", \"lynx\", \"wolf\", \"bear\", \"deer\", \"dove\",\n \"eagle\", \"finch\", \"heron\", \"koala\", \"lemur\", \"moose\", \"otter\", \"panda\", \"raven\", \"robin\",\n \"seal\", \"swan\", \"tiger\", \"whale\", \"zebra\", \"atlas\", \"bloom\", \"cloud\", \"delta\", \"ember\",\n \"flame\", \"grove\", \"haven\", \"iris\", \"jade\", \"leaf\", \"moon\", \"nova\", \"ocean\", \"peak\",\n \"river\", \"spark\", \"storm\", \"tide\", \"wave\", \"comet\", \"prism\", \"coral\",\n];\n\n/**\n * Generate a human-readable ID with 3 words like \"swift-bright-fox\"\n * Uses adjective-adjective-noun pattern for ~125,000 combinations.\n */\nexport const generateHumanReadableId = (separator = \"-\"): string => {\n // Pick two different adjectives\n const adj1Index = Math.floor(Math.random() * ADJECTIVES.length);\n let adj2Index = Math.floor(Math.random() * ADJECTIVES.length);\n if (adj2Index === adj1Index) {\n adj2Index = (adj2Index + 1) % ADJECTIVES.length;\n }\n\n const adjective1 = ADJECTIVES[adj1Index]!;\n const adjective2 = ADJECTIVES[adj2Index]!;\n const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)]!;\n\n return `${adjective1}${separator}${adjective2}${separator}${noun}`;\n};\n","/**\n * Errors for the Experiments API\n */\n\n/**\n * Base error for experiment-related issues\n */\nexport class ExperimentError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ExperimentError\";\n }\n}\n\n/**\n * Thrown when initialization fails\n */\nexport class ExperimentInitError extends ExperimentError {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = \"ExperimentInitError\";\n }\n}\n\n/**\n * Thrown when API calls fail\n */\nexport class ExperimentApiError extends ExperimentError {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly cause?: Error\n ) {\n super(message);\n this.name = \"ExperimentApiError\";\n }\n}\n\n/**\n * Thrown when target metadata conflicts\n */\nexport class TargetMetadataConflictError extends ExperimentError {\n constructor(\n public readonly targetName: string,\n public readonly existingMetadata: Record<string, unknown>,\n public readonly newMetadata: Record<string, unknown>\n ) {\n super(\n `Target '${targetName}' was previously registered with different metadata.\\n` +\n `Original: ${JSON.stringify(existingMetadata)}\\n` +\n `New: ${JSON.stringify(newMetadata)}\\n` +\n `If you want to use different metadata, please use a different target name.`\n );\n this.name = \"TargetMetadataConflictError\";\n }\n}\n\n/**\n * Thrown when a comparison cannot produce a verdict the caller asked for\n *\n * A row that is simply too thin to judge is skipped instead, so this is\n * reserved for a mismatch between what the caller named and what the run\n * actually recorded.\n */\nexport class ComparisonError extends ExperimentError {\n constructor(\n message: string,\n public readonly missingTargets: string[] = []\n ) {\n super(message);\n this.name = \"ComparisonError\";\n }\n}\n\n/**\n * Thrown when an evaluator call fails\n */\nexport class EvaluatorError extends ExperimentError {\n constructor(\n public readonly evaluatorSlug: string,\n message: string,\n public readonly cause?: Error\n ) {\n super(`Evaluator '${evaluatorSlug}' failed: ${message}`);\n this.name = \"EvaluatorError\";\n }\n}\n","/**\n * Comparison (n-way judging) for code-first experiments.\n *\n * Every target that recorded an output for a row is handed to\n * `langevals/select_best_compare` in a single call, and the winner comes back\n * named with the target name the caller registered.\n *\n * The judge owns its defaults. An option the caller did not set is absent from\n * the request, which is what lets the judge apply the default that fits the\n * row: it picks one of four prompts depending on whether the row carries a\n * reference answer and task context, so a copy of any of them shipped here\n * would both drift and silently disable that adaptation.\n */\n\nimport type {\n ComparisonOptions,\n ComparisonStatus,\n ComparisonVerdict,\n EvaluationStatus,\n RunEvaluatorResponse,\n} from \"./types\";\nimport { ComparisonError } from \"./errors\";\n\n/** The evaluator behind every comparison, in the workbench and in the SDK. */\nexport const COMPARISON_EVALUATOR_SLUG = \"langevals/select_best_compare\";\n\n/** Name a comparison is recorded under when the caller does not pick one. */\nexport const DEFAULT_COMPARISON_NAME = \"comparison\";\n\n/**\n * One target's output for one row, captured when its withTarget() callback\n * resolves so a comparison never has to read it back out of a batch that may\n * already have been flushed.\n */\nexport type CapturedTargetOutput = {\n /** The callback result, rendered as the text the judge will read. */\n output: string;\n /** Wall-clock duration of the target execution, in milliseconds. */\n durationMs?: number;\n};\n\n/** One candidate as the judge receives it. */\nexport type ComparisonCandidatePayload = {\n id: string;\n output: string;\n /** Seconds, which is the unit the judge renders. */\n duration?: number;\n};\n\n/**\n * Render a target's callback result as the text a judge can read.\n *\n * A lone `output` field is unwrapped to its own value, because that is the\n * shape a plain response is already recorded under, so returning the response\n * and returning `{ output: response }` reach the judge as the same text. A\n * response with several fields is presented whole, as JSON.\n *\n * An empty string means the target produced nothing for this row, and a target\n * with nothing to show is not a candidate: asking a judge to rank silence\n * produces a verdict about the wrong thing. A result that cannot be rendered\n * as text at all is treated the same way, since \"[object Object]\" tells a\n * judge even less than silence does.\n */\nexport const renderTargetOutput = (result: unknown): string => {\n if (result === undefined || result === null) {\n return \"\";\n }\n if (typeof result === \"string\") {\n return result;\n }\n if (\n typeof result === \"number\" ||\n typeof result === \"boolean\" ||\n typeof result === \"bigint\"\n ) {\n return String(result);\n }\n if (typeof result === \"object\" && !Array.isArray(result)) {\n const fields = Object.keys(result);\n if (fields.length === 1 && fields[0] === \"output\") {\n return renderTargetOutput((result as { output: unknown }).output);\n }\n }\n try {\n return JSON.stringify(result) ?? \"\";\n } catch {\n return \"\";\n }\n};\n\n/**\n * The judge settings for a comparison, carrying only what the caller set.\n *\n * `golden` is the single knob for reference-answer judging: passing one turns\n * `has_golden_answer` on, so the two can never disagree.\n */\nexport const buildComparisonSettings = (\n options: Pick<\n ComparisonOptions,\n | \"golden\"\n | \"prompt\"\n | \"model\"\n | \"allowTie\"\n | \"randomizeOrder\"\n | \"swapAndReconcile\"\n | \"includeMetrics\"\n | \"temperature\"\n >\n): Record<string, unknown> => {\n const settings: Record<string, unknown> = {};\n\n if (options.prompt !== undefined) settings.prompt = options.prompt;\n if (options.model !== undefined) settings.model = options.model;\n if (options.golden !== undefined) settings.has_golden_answer = true;\n if (options.allowTie !== undefined) settings.allow_tie = options.allowTie;\n if (options.randomizeOrder !== undefined) {\n settings.randomize_order = options.randomizeOrder;\n }\n if (options.swapAndReconcile !== undefined) {\n settings.swap_and_reconcile = options.swapAndReconcile;\n }\n if (options.includeMetrics !== undefined) {\n settings.include_metrics = options.includeMetrics;\n }\n if (options.temperature !== undefined) {\n settings.temperature = options.temperature;\n }\n\n return settings;\n};\n\n/**\n * The candidates the judge will see, in the order given.\n *\n * A name with nothing recorded against it never reaches here, and if one ever\n * does it is a bug in candidate selection rather than a thin row, so it says\n * which target it was instead of judging a silent candidate.\n */\nexport const buildComparisonCandidates = (\n names: string[],\n captured: Map<string, CapturedTargetOutput>\n): ComparisonCandidatePayload[] =>\n names.map((name) => {\n const output = captured.get(name);\n if (!output) {\n throw new ComparisonError(\n `Cannot compare: '${name}' was selected as a candidate but recorded no output.`,\n [name]\n );\n }\n return {\n id: name,\n output: output.output,\n ...(output.durationMs !== undefined\n ? { duration: output.durationMs / 1000 }\n : {}),\n };\n });\n\n/**\n * The judge entry for a row.\n *\n * `row_index` seeds the judge's deterministic candidate shuffle, so it comes\n * from the row the caller is already naming rather than from a second argument\n * they would have to remember to keep in step.\n */\nexport const buildComparisonData = ({\n input,\n golden,\n candidates,\n index,\n}: {\n input?: string;\n golden?: string;\n candidates: ComparisonCandidatePayload[];\n index: number;\n}): Record<string, unknown> => ({\n ...(input !== undefined ? { input } : {}),\n ...(golden !== undefined ? { golden } : {}),\n candidates,\n row_index: index,\n});\n\n/**\n * Translate the judge's result into a verdict.\n *\n * - a judged row with a winner is `decided`\n * - a judged row the judge called even is a `tie`\n * - a judged row the judge would not call, which under swap-and-reconcile\n * means its two passes disagreed, is `inconclusive`: a finding about the\n * candidates, and not a tie, which would claim a measurement never made\n * - a judge that failed or could not be reached is `error`, because nothing\n * was measured about the candidates at all. Reporting that as\n * `inconclusive` would dress a broken call up as a finding\n */\nexport const toComparisonVerdict = ({\n response,\n candidates,\n}: {\n response: RunEvaluatorResponse;\n candidates: string[];\n}): ComparisonVerdict => {\n const reasoning = response.details ?? null;\n\n if (response.status === \"error\") {\n return { status: \"error\", winner: null, reasoning, candidates };\n }\n\n if (response.status !== \"processed\" || response.label == null) {\n return { status: \"inconclusive\", winner: null, reasoning, candidates };\n }\n\n if (response.label === \"tie\") {\n return { status: \"tie\", winner: null, reasoning, candidates };\n }\n\n return { status: \"decided\", winner: response.label, reasoning, candidates };\n};\n\n/**\n * The batch status a verdict is recorded under.\n *\n * The batch protocol carries three statuses to the verdict's five, so the\n * mapping lives here and nowhere else: what the row records and what the\n * caller is handed are then two readings of one decision rather than two\n * decisions that can drift apart.\n */\nexport const comparisonEntryStatus = (\n status: ComparisonStatus\n): EvaluationStatus => {\n switch (status) {\n case \"decided\":\n case \"tie\":\n return \"processed\";\n case \"error\":\n return \"error\";\n case \"inconclusive\":\n case \"skipped\":\n return \"skipped\";\n }\n};\n\n/**\n * The label a verdict is recorded under: the winner, \"tie\", or nothing.\n */\nexport const comparisonEntryLabel = (verdict: ComparisonVerdict): string | null => {\n if (verdict.status === \"decided\") return verdict.winner;\n if (verdict.status === \"tie\") return \"tie\";\n return null;\n};\n\n/**\n * Why a row could not be compared, naming the targets that had nothing to\n * contribute so the reader can tell a thin dataset from a broken target.\n */\nexport const describeSkippedComparison = ({\n candidates,\n missing,\n}: {\n candidates: string[];\n missing: string[];\n}): string => {\n const head = `A comparison needs at least two candidate outputs, this row has ${candidates.length}.`;\n if (missing.length === 0) {\n return head;\n }\n return `${head} No output was recorded for: ${missing.join(\", \")}.`;\n};\n","import type { ExperimentRunResult } from \"./platformTypes\";\n\n/**\n * Print a CI-friendly summary of experiment results to stdout.\n *\n * Shared between platform runs (`langwatch.experiments.run`) and SDK-driven\n * experiments (`langwatch.experiments.init` → `experiment.printSummary()`).\n */\nexport function printSummary(result: Omit<ExperimentRunResult, \"printSummary\" | \"toString\">): void {\n const { runId, status, passed, failed, passRate, duration, runUrl, summary } = result;\n\n console.log(\"\\n\" + \"═\".repeat(60));\n console.log(\" EXPERIMENT RESULTS\");\n console.log(\"═\".repeat(60));\n console.log(` Run ID: ${runId}`);\n console.log(` Status: ${status.toUpperCase()}`);\n console.log(` Duration: ${(duration / 1000).toFixed(1)}s`);\n console.log(\"─\".repeat(60));\n console.log(` Passed: ${passed}`);\n console.log(` Failed: ${failed}`);\n console.log(` Pass Rate: ${passRate.toFixed(1)}%`);\n if (summary.totalCost) {\n console.log(` Total Cost: $${summary.totalCost.toFixed(4)}`);\n }\n\n if (summary.targets && summary.targets.length > 0) {\n console.log(\"─\".repeat(60));\n console.log(\" TARGETS:\");\n for (const target of summary.targets) {\n console.log(` ${target.name}: ${target.passed} passed, ${target.failed} failed`);\n if (target.avgLatency) {\n console.log(` Avg latency: ${target.avgLatency.toFixed(0)}ms`);\n }\n if (target.totalCost) {\n console.log(` Total cost: $${target.totalCost.toFixed(4)}`);\n }\n }\n }\n\n if (summary.evaluators && summary.evaluators.length > 0) {\n console.log(\"─\".repeat(60));\n console.log(\" EVALUATORS:\");\n for (const evaluator of summary.evaluators) {\n console.log(` ${evaluator.name}: ${evaluator.passRate.toFixed(1)}% pass rate`);\n if (evaluator.avgScore !== undefined) {\n console.log(` Avg score: ${evaluator.avgScore.toFixed(2)}`);\n }\n }\n }\n\n console.log(\"─\".repeat(60));\n console.log(` View details: ${runUrl}`);\n console.log(\"═\".repeat(60) + \"\\n\");\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\n\nexport interface ExperimentRunStartResponse {\n runId: string;\n status: \"running\";\n total: number;\n runUrl?: string;\n}\n\n/**\n * Optional body for `POST /api/evaluations/v3/{slug}/run`. `data` and\n * `dataset_id` are mutually exclusive on the server (400 if both are sent).\n */\nexport interface ExperimentRunStartRequest {\n data?: Array<Record<string, unknown>>;\n dataset_id?: string;\n parameters?: Record<string, string | number | boolean>;\n row_indices?: number[];\n}\n\n/**\n * Build the snake_case run-start request body from camelCase options.\n *\n * Returns `undefined` when no overrides are provided so the caller can send a\n * body-less request (the server then uses the configured inputs).\n */\nexport const toRunStartRequest = ({\n data,\n datasetId,\n parameters,\n rowIndices,\n}: {\n data?: Array<Record<string, unknown>>;\n datasetId?: string;\n parameters?: Record<string, string | number | boolean>;\n rowIndices?: number[];\n}): ExperimentRunStartRequest | undefined => {\n const body: ExperimentRunStartRequest = {};\n if (data !== undefined) body.data = data;\n if (datasetId !== undefined) body.dataset_id = datasetId;\n if (parameters !== undefined) body.parameters = parameters;\n if (rowIndices !== undefined) body.row_indices = rowIndices;\n return Object.keys(body).length > 0 ? body : undefined;\n};\n\nexport type ExperimentRunStatusResponse =\n paths[\"/api/experiments/runs/{runId}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * Status payload for `GET /api/evaluations/v3/runs/{runId}` (polling).\n *\n * Hand-written because the v3 path is served via a legacy-alias that rewrites\n * to `/api/experiments/...`, so only the legacy path is declared in the\n * generated OpenAPI types. Kept structurally aligned with that legacy schema.\n */\nexport interface ExperimentV3RunStatusResponse {\n runId: string;\n status: \"pending\" | \"running\" | \"completed\" | \"failed\" | \"stopped\";\n /** Number of cells completed */\n progress: number;\n /** Total number of cells */\n total: number;\n /** Unix timestamp when run started */\n startedAt?: number;\n /** Unix timestamp when run finished (completed/failed/stopped only) */\n finishedAt?: number;\n /** Execution summary (present when completed) */\n summary?: {\n runId?: string;\n totalCells?: number;\n completedCells?: number;\n failedCells?: number;\n /** Total execution time in milliseconds */\n duration?: number;\n /** URL to view the run in LangWatch */\n runUrl?: string;\n };\n /** Error message (present when failed) */\n error?: string;\n}\n\n/**\n * Summary entry returned by `GET /api/experiments`. Mirrors\n * `experimentSummarySchema` from the control-plane Hono route. Hand-written\n * because the route is not yet exposed via the generated OpenAPI types.\n */\nexport interface ExperimentSummary {\n id: string;\n slug: string;\n name: string | null;\n type: string;\n workflowId: string | null;\n createdAt: string;\n updatedAt: string;\n runsCount: number;\n lastRunAt: string | null;\n}\n\nexport interface ExperimentListPagination {\n page: number;\n pageSize: number;\n totalHits: number;\n hasMore: boolean;\n}\n\nexport interface ExperimentListResponse {\n experiments: ExperimentSummary[];\n pagination: ExperimentListPagination;\n}\n\n/**\n * Per-run entry returned by `GET /api/experiments/runs?experimentSlug=...`.\n * Mirrors `ExperimentRun` from the control plane.\n */\nexport interface ExperimentRunSummaryEntry {\n experimentId: string;\n runId: string;\n workflowVersion: {\n id: string;\n version: string;\n commitMessage: string;\n author: { name: string | null; image: string | null } | null;\n } | null;\n timestamps: {\n createdAt: number;\n updatedAt: number;\n finishedAt?: number | null;\n stoppedAt?: number | null;\n };\n progress?: number | null;\n total?: number | null;\n summary: {\n datasetCost?: number;\n evaluationsCost?: number;\n datasetAverageCost?: number;\n datasetAverageDuration?: number;\n evaluationsAverageCost?: number;\n evaluationsAverageDuration?: number;\n evaluations: Record<\n string,\n { name: string; averageScore: number | null; averagePassed?: number }\n >;\n };\n}\n\nexport interface ExperimentRunsListResponse {\n experimentId: string;\n experimentSlug: string;\n runs: ExperimentRunSummaryEntry[];\n pagination: ExperimentListPagination;\n}\n\n/**\n * Per-row results for a completed experiment run.\n *\n * Mirrors `ExperimentRunWithItems` from the control plane\n * (`platform/app/src/server/experiments-v3/services/types.ts`). Hand-written\n * because the `/runs/{runId}/results` route is not yet exposed via the\n * generated OpenAPI types.\n */\nexport interface ExperimentRunDatasetEntry {\n index: number;\n targetId?: string | null;\n entry: Record<string, unknown>;\n predicted?: Record<string, unknown>;\n cost?: number | null;\n duration?: number | null;\n error?: string | null;\n traceId?: string | null;\n}\n\nexport interface ExperimentRunEvaluation {\n evaluator: string;\n name?: string | null;\n targetId?: string | null;\n status: \"processed\" | \"skipped\" | \"error\";\n index: number;\n score?: number | null;\n label?: string | null;\n passed?: boolean | null;\n details?: string | null;\n cost?: number | null;\n duration?: number | null;\n inputs?: Record<string, unknown> | null;\n}\n\nexport interface ExperimentRunResultsResponse {\n experimentId: string;\n runId: string;\n projectId: string;\n workflowVersionId?: string | null;\n progress?: number | null;\n total?: number | null;\n dataset: ExperimentRunDatasetEntry[];\n evaluations: ExperimentRunEvaluation[];\n timestamps: {\n createdAt: number;\n updatedAt: number;\n finishedAt?: number | null;\n stoppedAt?: number | null;\n };\n}\n\n/**\n * The workbench types below are projections of the generated OpenAPI `paths`,\n * never hand-written copies of them. The control plane owns these shapes, so a\n * restated interface can only drift out of date, and a cast onto it would hide\n * the drift instead of failing the build.\n */\n\n/**\n * The experiment setup as the API carries it: datasets, targets and\n * evaluators. Read it, change it, send it back whole. The canonical shape is\n * the control plane's `persistedEvaluationsV3StateSchema`, which validates\n * every write, so this stays open rather than restating it here.\n */\nexport type ExperimentWorkbenchState =\n paths[\"/api/experiments/{slug}/workbench-state\"][\"put\"][\"requestBody\"][\"content\"][\"application/json\"][\"state\"];\n\nexport type ExperimentCreateResponse =\n paths[\"/api/experiments\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * The read answers one of two documents, chosen by the `fields` query. The\n * full setup is the one carrying `state`, so that field is what splits the\n * union into the two shapes the overloads promise.\n */\ntype WorkbenchStateReadResponse =\n paths[\"/api/experiments/{slug}/workbench-state\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type ExperimentWorkbenchStateResponse = Extract<\n WorkbenchStateReadResponse,\n { state: unknown }\n>;\n\n/** What `fields: \"version\"` answers: the staleness probe without the setup. */\nexport type ExperimentWorkbenchVersionProbe = Exclude<\n WorkbenchStateReadResponse,\n { state: unknown }\n>;\n\nexport type ExperimentSaveWorkbenchStateResponse =\n paths[\"/api/experiments/{slug}/workbench-state\"][\"put\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type ExperimentRestoreVersionResponse =\n paths[\"/api/experiments/{slug}/versions/{version}/restore\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type ExperimentVersionsResponse =\n paths[\"/api/experiments/{slug}/versions\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type ExperimentVersionSummary =\n ExperimentVersionsResponse[\"versions\"][number];\n\nexport class ExperimentsApiServiceError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"ExperimentsApiServiceError\";\n }\n}\n\nexport class ExperimentsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throw new ExperimentsApiServiceError(message, operation, error);\n }\n\n private async getUndeclaredEndpoint<T>({\n path,\n operation,\n }: {\n path: string;\n operation: string;\n }): Promise<T> {\n type UntypedClient = {\n GET: (\n path: string,\n init?: { parseAs?: \"json\" },\n ) => Promise<{ data?: unknown; error?: unknown; response: Response }>;\n };\n\n let result: { data?: unknown; error?: unknown; response: Response };\n try {\n result = await (this.apiClient as unknown as UntypedClient).GET(path, {\n parseAs: \"json\",\n });\n } catch (error) {\n this.handleApiError(operation, error);\n }\n\n if (result.error) this.handleApiError(operation, result.error);\n return result.data as T;\n }\n\n private async postUndeclaredEndpoint<T>({\n path,\n body,\n operation,\n }: {\n path: string;\n body?: unknown;\n operation: string;\n }): Promise<T> {\n type UntypedClient = {\n POST: (\n path: string,\n init?: { body?: unknown; parseAs?: \"json\" },\n ) => Promise<{ data?: unknown; error?: unknown; response: Response }>;\n };\n\n let result: { data?: unknown; error?: unknown; response: Response };\n try {\n result = await (this.apiClient as unknown as UntypedClient).POST(path, {\n ...(body !== undefined ? { body } : {}),\n parseAs: \"json\",\n });\n } catch (error) {\n this.handleApiError(operation, error);\n }\n\n if (result.error) this.handleApiError(operation, result.error);\n return result.data as T;\n }\n\n /**\n * Start a saved experiment by slug.\n *\n * The body stays off the wire entirely when no overrides are given, so the\n * run uses the inputs the experiment is configured with.\n */\n async startRun(\n slug: string,\n options: {\n parameters?: Record<string, string | number | boolean>;\n } = {},\n ): Promise<ExperimentRunStartResponse> {\n const body = toRunStartRequest({ parameters: options.parameters });\n const { data, error } = await this.apiClient.POST(\n \"/api/experiments/{slug}/run\",\n {\n params: { path: { slug } },\n ...(body !== undefined ? { body } : {}),\n },\n );\n if (error) this.handleApiError(`start experiment run for \"${slug}\"`, error);\n return data as unknown as ExperimentRunStartResponse;\n }\n\n /**\n * Create an experiment.\n *\n * Sending no state creates a blank workbench with one inline dataset, so a\n * caller that only wants somewhere to put its setup does not have to build\n * one first.\n */\n async create({\n name,\n state,\n }: {\n name?: string;\n state?: ExperimentWorkbenchState;\n } = {}): Promise<ExperimentCreateResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/experiments\", {\n body: {\n ...(name !== undefined ? { name } : {}),\n ...(state !== undefined ? { state } : {}),\n },\n });\n if (error) this.handleApiError(\"create experiment\", error);\n return data;\n }\n\n /**\n * Read an experiment's setup, with the version to send back when saving.\n *\n * `fields: \"version\"` answers with the version and timestamp only, which is\n * what a poller checking for changes wants: it costs the same round trip and\n * none of the payload.\n */\n async getWorkbenchState(options: {\n slug: string;\n fields: \"version\";\n }): Promise<ExperimentWorkbenchVersionProbe>;\n async getWorkbenchState(options: {\n slug: string;\n fields?: undefined;\n }): Promise<ExperimentWorkbenchStateResponse>;\n async getWorkbenchState({\n slug,\n fields,\n }: {\n slug: string;\n fields?: \"version\";\n }): Promise<ExperimentWorkbenchStateResponse | ExperimentWorkbenchVersionProbe> {\n const { data, error } = await this.apiClient.GET(\n \"/api/experiments/{slug}/workbench-state\",\n {\n params: {\n path: { slug },\n ...(fields !== undefined ? { query: { fields } } : {}),\n },\n },\n );\n if (error) {\n this.handleApiError(`get workbench state for \"${slug}\"`, error);\n }\n return data;\n }\n\n /**\n * Save an experiment's setup.\n *\n * Send `expectedVersion` with the version you read and the platform refuses\n * the save with a 409 when someone else wrote first, instead of overwriting\n * their work.\n */\n async setWorkbenchState({\n slug,\n state,\n expectedVersion,\n commitMessage,\n }: {\n slug: string;\n state: ExperimentWorkbenchState;\n expectedVersion?: number;\n commitMessage?: string;\n }): Promise<ExperimentSaveWorkbenchStateResponse> {\n const { data, error } = await this.apiClient.PUT(\n \"/api/experiments/{slug}/workbench-state\",\n {\n params: { path: { slug } },\n body: {\n state,\n ...(expectedVersion !== undefined ? { expectedVersion } : {}),\n ...(commitMessage !== undefined ? { commitMessage } : {}),\n },\n },\n );\n if (error) {\n this.handleApiError(`save workbench state for \"${slug}\"`, error);\n }\n return data;\n }\n\n /** The experiment's saved versions, newest first. */\n async listVersions({\n slug,\n limit,\n cursor,\n }: {\n slug: string;\n limit?: number;\n cursor?: number;\n }): Promise<ExperimentVersionsResponse> {\n const { data, error } = await this.apiClient.GET(\n \"/api/experiments/{slug}/versions\",\n {\n params: {\n path: { slug },\n query: {\n ...(limit !== undefined ? { limit } : {}),\n ...(cursor !== undefined ? { cursor } : {}),\n },\n },\n },\n );\n if (error) {\n this.handleApiError(`list versions for experiment \"${slug}\"`, error);\n }\n return data;\n }\n\n /**\n * Bring an old version back by writing it forward as a new save. History is\n * never rewritten: the version restored from stays in the list.\n */\n async restoreVersion({\n slug,\n version,\n }: {\n slug: string;\n version: number;\n }): Promise<ExperimentRestoreVersionResponse> {\n const { data, error } = await this.apiClient.POST(\n \"/api/experiments/{slug}/versions/{version}/restore\",\n { params: { path: { slug, version } } },\n );\n if (error) {\n this.handleApiError(\n `restore version ${version} of experiment \"${slug}\"`,\n error,\n );\n }\n return data;\n }\n\n async getRunStatus(runId: string): Promise<ExperimentRunStatusResponse> {\n const { data, error } = await this.apiClient.GET(\n \"/api/experiments/runs/{runId}\",\n {\n params: { path: { runId } },\n },\n );\n if (error) this.handleApiError(`get run status for \"${runId}\"`, error);\n return data;\n }\n\n /**\n * List experiments for the current project.\n *\n * Hits `GET /api/experiments` through the configured API client transport.\n * The route is not yet declared in generated OpenAPI types, so the path is\n * dispatched through a narrow untyped helper.\n */\n async listExperiments({\n pageSize,\n page,\n }: {\n pageSize?: number;\n page?: number;\n } = {}): Promise<ExperimentListResponse> {\n const search = new URLSearchParams();\n if (pageSize !== undefined) search.set(\"pageSize\", String(pageSize));\n if (page !== undefined) search.set(\"page\", String(page));\n const qs = search.toString();\n return this.getUndeclaredEndpoint<ExperimentListResponse>({\n path: `/api/experiments${qs ? `?${qs}` : \"\"}`,\n operation: \"list experiments\",\n });\n }\n\n /**\n * List experiment runs for an experiment slug.\n *\n * Hits `GET /api/experiments/runs?experimentSlug=...` through the\n * configured API client transport because the route is not yet declared in\n * the generated OpenAPI.\n */\n async listRuns({\n experimentSlug,\n pageSize,\n page,\n }: {\n experimentSlug: string;\n pageSize?: number;\n page?: number;\n }): Promise<ExperimentRunsListResponse> {\n const search = new URLSearchParams();\n search.set(\"experimentSlug\", experimentSlug);\n if (pageSize !== undefined) search.set(\"pageSize\", String(pageSize));\n if (page !== undefined) search.set(\"page\", String(page));\n return this.getUndeclaredEndpoint<ExperimentRunsListResponse>({\n path: `/api/experiments/runs?${search.toString()}`,\n operation: `list runs for experiment \"${experimentSlug}\"`,\n });\n }\n\n /**\n * Fetch per-row results for a completed experiment run.\n *\n * Hits `GET /api/experiments/runs/{runId}/results` through the\n * configured API client transport because the route is not yet declared in\n * the generated OpenAPI `paths`.\n */\n async getRunResults({\n runId,\n experimentSlug,\n }: {\n runId: string;\n experimentSlug?: string;\n }): Promise<ExperimentRunResultsResponse> {\n const search = new URLSearchParams();\n if (experimentSlug) search.set(\"experimentSlug\", experimentSlug);\n const qs = search.toString() ? `?${search.toString()}` : \"\";\n const body = await this.getUndeclaredEndpoint<\n ExperimentRunResultsResponse | null\n >({\n path: `/api/experiments/runs/${encodeURIComponent(runId)}/results${qs}`,\n operation: `get run results for \"${runId}\"`,\n });\n if (body === null) {\n this.handleApiError(`get run results for \"${runId}\"`, {\n response: { status: 404 },\n data: { error: `Run not found: ${runId}` },\n });\n }\n return body;\n }\n\n /**\n * Start a saved Evaluations V3 experiment by slug through the unified\n * evaluations-v3 backend.\n *\n * Hits `POST /api/evaluations/v3/{slug}/run`. The optional body overrides\n * the configured inputs (`data` / `dataset_id` are mutually exclusive on the\n * server). The route accepts a body, but the generated OpenAPI types declare\n * it body-less, so the call is dispatched through a narrow untyped helper.\n */\n async startV3Run({\n slug,\n body,\n }: {\n slug: string;\n body?: ExperimentRunStartRequest;\n }): Promise<ExperimentRunStartResponse> {\n return this.postUndeclaredEndpoint<ExperimentRunStartResponse>({\n path: `/api/evaluations/v3/${encodeURIComponent(slug)}/run`,\n body,\n operation: `start evaluation run for \"${slug}\"`,\n });\n }\n\n /**\n * Poll the status of an Evaluations V3 run.\n *\n * Hits `GET /api/evaluations/v3/runs/{runId}`.\n */\n async getV3RunStatus(runId: string): Promise<ExperimentV3RunStatusResponse> {\n return this.getUndeclaredEndpoint<ExperimentV3RunStatusResponse>({\n path: `/api/evaluations/v3/runs/${encodeURIComponent(runId)}`,\n operation: `get run status for \"${runId}\"`,\n });\n }\n\n /**\n * Fetch per-row results for an Evaluations V3 run.\n *\n * Hits `GET /api/evaluations/v3/runs/{runId}/results`. `experimentSlug` is\n * optional for runs created in the last 24h and required afterwards.\n */\n async getV3RunResults({\n runId,\n experimentSlug,\n }: {\n runId: string;\n experimentSlug?: string;\n }): Promise<ExperimentRunResultsResponse> {\n const search = new URLSearchParams();\n if (experimentSlug) search.set(\"experimentSlug\", experimentSlug);\n const qs = search.toString() ? `?${search.toString()}` : \"\";\n const body = await this.getUndeclaredEndpoint<\n ExperimentRunResultsResponse | null\n >({\n path: `/api/evaluations/v3/runs/${encodeURIComponent(runId)}/results${qs}`,\n operation: `get run results for \"${runId}\"`,\n });\n if (body === null) {\n this.handleApiError(`get run results for \"${runId}\"`, {\n response: { status: 404 },\n data: { error: `Run not found: ${runId}` },\n });\n }\n return body;\n }\n}\n","/**\n * Error classes for platform experiments API (Experiments Workbench)\n */\n\n/**\n * Base error for experiment operations\n */\nexport class ExperimentsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ExperimentsError\";\n }\n}\n\n/**\n * Error when experiment is not found\n */\nexport class ExperimentNotFoundError extends ExperimentsError {\n constructor(slug: string) {\n super(`Experiment not found: ${slug}`);\n this.name = \"ExperimentNotFoundError\";\n }\n}\n\n/**\n * Error when experiment run times out\n */\nexport class ExperimentTimeoutError extends ExperimentsError {\n readonly runId: string;\n readonly progress: number;\n readonly total: number;\n\n constructor(runId: string, progress: number, total: number) {\n super(`Experiment run timed out: ${runId} (${progress}/${total} completed)`);\n this.name = \"ExperimentTimeoutError\";\n this.runId = runId;\n this.progress = progress;\n this.total = total;\n }\n}\n\n/**\n * Error when experiment run fails\n */\nexport class ExperimentRunFailedError extends ExperimentsError {\n readonly runId: string;\n readonly errorMessage: string;\n\n constructor(runId: string, errorMessage: string) {\n super(`Experiment run failed: ${errorMessage}`);\n this.name = \"ExperimentRunFailedError\";\n this.runId = runId;\n this.errorMessage = errorMessage;\n }\n}\n\n/**\n * Error from the experiments API\n */\nexport class ExperimentsApiError extends ExperimentsError {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = \"ExperimentsApiError\";\n this.statusCode = statusCode;\n }\n}\n","// Experiment runs are stored as a timestamp-driven state in ClickHouse: a run\n// is \"completed\" once finishedAt is set, \"stopped\" once stoppedAt is set, and\n// otherwise still \"running\". A run that stopped emitting updates a while ago\n// without ever finishing is reported as \"interrupted\": the SDK process likely\n// died before sending finished_at/stopped_at.\n\nimport type { ExperimentRunSummary } from \"./platformTypes\";\nimport {\n ExperimentTimeoutError,\n ExperimentRunFailedError,\n} from \"./platformErrors\";\n\nexport const INTERRUPTED_THRESHOLD_MS = 5 * 60 * 1000;\n\nexport type RunStatus = \"completed\" | \"stopped\" | \"running\" | \"interrupted\";\n\nexport interface RunTimestamps {\n createdAt?: number | null;\n updatedAt?: number | null;\n finishedAt?: number | null;\n stoppedAt?: number | null;\n}\n\nexport const deriveRunStatus = (\n timestamps: RunTimestamps,\n now: number = Date.now(),\n): RunStatus => {\n if (timestamps.stoppedAt != null) return \"stopped\";\n if (timestamps.finishedAt != null) return \"completed\";\n if (\n timestamps.updatedAt != null &&\n now - timestamps.updatedAt > INTERRUPTED_THRESHOLD_MS\n ) {\n return \"interrupted\";\n }\n return \"running\";\n};\n\nexport const isTerminalStatus = (status: RunStatus): boolean =>\n status === \"completed\" || status === \"stopped\";\n\nexport const DEFAULT_POLL_INTERVAL = 2000;\nexport const DEFAULT_POLL_TIMEOUT = 600000; // 10 minutes\n\n/**\n * Status payload returned by the polling endpoint\n * (`GET /api/evaluations/v3/runs/{runId}`).\n */\nexport interface PollRunStatus {\n status: string;\n progress: number;\n total: number;\n summary?: ExperimentRunSummary;\n error?: string;\n}\n\nexport interface PollExperimentRunResult {\n status: \"completed\" | \"stopped\";\n summary: ExperimentRunSummary;\n}\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Poll a run to completion.\n *\n * Calls `getStatus(runId)` every `pollInterval` ms until the run reports a\n * terminal status, mirroring the python SDK poll loop. Resolves on `completed`\n * or `stopped`, throws `ExperimentRunFailedError` on `failed`, and throws\n * `ExperimentTimeoutError` if `timeout` ms elapse first.\n *\n * The status fetcher is injected so both the experiment and workflow paths can\n * reuse the same loop.\n */\nexport const pollExperimentRun = async ({\n runId,\n getStatus,\n pollInterval = DEFAULT_POLL_INTERVAL,\n timeout = DEFAULT_POLL_TIMEOUT,\n onProgress,\n now = Date.now,\n}: {\n runId: string;\n getStatus: (runId: string) => Promise<PollRunStatus>;\n pollInterval?: number;\n timeout?: number;\n onProgress?: (progress: number, total: number) => void;\n now?: () => number;\n}): Promise<PollExperimentRunResult> => {\n const startTime = now();\n\n while (true) {\n if (now() - startTime > timeout) {\n const finalStatus = await getStatus(runId);\n throw new ExperimentTimeoutError(\n runId,\n finalStatus.progress,\n finalStatus.total,\n );\n }\n\n await sleep(pollInterval);\n\n const status = await getStatus(runId);\n onProgress?.(status.progress, status.total);\n\n if (status.status === \"completed\") {\n return { status: \"completed\", summary: status.summary ?? {} };\n }\n\n if (status.status === \"failed\") {\n throw new ExperimentRunFailedError(runId, status.error ?? \"Unknown error\");\n }\n\n if (status.status === \"stopped\") {\n return {\n status: \"stopped\",\n summary: status.summary ?? {\n runId,\n totalCells: status.total,\n completedCells: status.progress,\n failedCells: 0,\n duration: now() - startTime,\n },\n };\n }\n }\n};\n\n/**\n * Replace the domain of a URL with a new base, preserving path/query/fragment.\n *\n * The platform returns its own (cloud) URL for a run; rebasing it onto the\n * configured endpoint keeps a self-hosted run pointing at the local instance\n * instead of app.langwatch.ai. Returns the original string if either URL is\n * unparseable.\n */\nexport const rebaseUrlToEndpoint = (url: string, newBase: string): string => {\n if (!url) return url;\n try {\n const parsedUrl = new URL(url);\n const parsedNewBase = new URL(newBase);\n return `${parsedNewBase.origin}${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;\n } catch {\n return url;\n }\n};\n\n/**\n * Fetch a run's per-row results, retrying through the brief post-completion\n * window where the results endpoint 404s (\"not yet available\") or returns an\n * empty dataset because the ClickHouse projection has not caught up yet.\n *\n * Generic over the result shape: `getResults` performs one fetch and `isEmpty`\n * reports whether it came back without rows. When the run reported rows\n * (`expectsRows`), an empty or failed read is retried up to `maxAttempts` with a\n * fixed `delay`; otherwise the first read is returned. The same loop backs both\n * the experiment and workflow SDK paths and mirrors the python SDK.\n */\nexport const fetchResultsWithRetry = async <T>({\n getResults,\n isEmpty,\n expectsRows,\n delay = DEFAULT_POLL_INTERVAL,\n maxAttempts = 6,\n}: {\n getResults: () => Promise<T>;\n isEmpty: (results: T) => boolean;\n expectsRows: boolean;\n delay?: number;\n maxAttempts?: number;\n}): Promise<T> => {\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const results = await getResults();\n if (expectsRows && isEmpty(results) && attempt < maxAttempts) {\n await sleep(delay);\n continue;\n }\n return results;\n } catch (error) {\n lastError = error;\n // Only the rows-expected path waits out the post-completion projection\n // lag (a 404 / \"not yet available\" right after the run finishes). With no\n // rows expected a thrown error is terminal, so surface it immediately\n // instead of delaying through every attempt.\n if (expectsRows && attempt < maxAttempts) {\n await sleep(delay);\n continue;\n }\n throw error;\n }\n }\n throw lastError;\n};\n","/**\n * Maps the platform `/runs/{runId}/results` response (ExperimentRunWithItems)\n * into per-row results.\n *\n * Mirrors the python SDK's `_build_df_from_platform`: dataset entries become the\n * base rows (one per entry, i.e. one per target in multi-target runs), and\n * evaluations are joined onto those rows on `(index, targetId)`.\n */\n\nimport type { ExperimentRunResultsResponse } from \"./experiments-api.service\";\nimport type { ExperimentRowResult } from \"./platformTypes\";\n\nconst matchesRow = ({\n row,\n index,\n targetId,\n}: {\n row: ExperimentRowResult;\n index: number;\n targetId?: string | null;\n}): boolean => {\n if (row.index !== index) return false;\n // Only constrain on target when the evaluation carries one and the rows are\n // multi-target (rows carry a `target`). Single-target rows have no `target`,\n // so an evaluation targetId should not exclude them.\n if (targetId && row.target !== undefined) {\n return row.target === targetId;\n }\n return true;\n};\n\nexport const mapRunResultsToRows = (\n response: ExperimentRunResultsResponse,\n): ExperimentRowResult[] => {\n const datasetEntries = response.dataset ?? [];\n const evaluations = response.evaluations ?? [];\n\n const rows: ExperimentRowResult[] = datasetEntries.map((entry) => {\n const entryData =\n entry.entry && typeof entry.entry === \"object\" ? entry.entry : {};\n\n const predicted = entry.predicted;\n const output =\n predicted && typeof predicted === \"object\" && \"output\" in predicted\n ? (predicted).output\n : predicted;\n\n const row: ExperimentRowResult = {\n index: entry.index ?? 0,\n input: { ...entryData },\n output,\n traceId: entry.traceId ?? \"\",\n evaluations: {},\n };\n\n if (entry.cost != null) row.cost = entry.cost;\n if (entry.duration != null) row.duration = entry.duration;\n if (entry.error) row.error = entry.error;\n if (entry.targetId) row.target = entry.targetId;\n\n return row;\n });\n\n // Index rows by their dataset index so each evaluation joins against just the\n // rows for that index (one per target) instead of scanning every row, which\n // would be O(evaluations x rows) on large result sets.\n const rowsByIndex = new Map<number, ExperimentRowResult[]>();\n for (const row of rows) {\n const bucket = rowsByIndex.get(row.index);\n if (bucket) bucket.push(row);\n else rowsByIndex.set(row.index, [row]);\n }\n\n for (const evaluation of evaluations) {\n const index = evaluation.index;\n // A null, undefined, or empty name falls through to the evaluator id\n // (matching the python builder); the length check keeps the empty-string\n // case from being collapsed by nullish coalescing.\n const name =\n evaluation.name && evaluation.name.length > 0\n ? evaluation.name\n : evaluation.evaluator;\n if (index == null || !name) continue;\n\n for (const row of rowsByIndex.get(index) ?? []) {\n if (!matchesRow({ row, index, targetId: evaluation.targetId })) continue;\n\n const metric = row.evaluations[name] ?? {};\n if (evaluation.score != null) metric.score = evaluation.score;\n if (evaluation.passed != null) metric.passed = evaluation.passed;\n row.evaluations[name] = metric;\n }\n }\n\n return rows;\n};\n","/**\n * ExperimentsFacade - Entry point for the experiments API\n *\n * Provides:\n * - `init()` method to create experiment sessions (SDK-defined experiments)\n * - `run()` method to execute platform-configured experiments (Experiments Workbench)\n */\n\nimport type { LangwatchApiClient } from \"@/internal/api/client\";\nimport { isLangWatchHandledError } from \"@/internal/api/errors\";\nimport type { Logger } from \"@/logger\";\nimport { Experiment } from \"./experiment\";\nimport {\n ExperimentsApiService,\n toRunStartRequest,\n} from \"./experiments-api.service\";\nimport type { ExperimentInitOptions } from \"./types\";\nimport type {\n ExperimentRunResult,\n RunExperimentOptions,\n RunWithResultsOptions,\n ExperimentRunWithResults,\n ExperimentRunSummary,\n} from \"./platformTypes\";\nimport {\n ExperimentsApiError,\n ExperimentNotFoundError,\n ExperimentTimeoutError,\n ExperimentRunFailedError,\n} from \"./platformErrors\";\nimport {\n pollExperimentRun,\n rebaseUrlToEndpoint,\n fetchResultsWithRetry,\n} from \"./run-status\";\nimport { mapRunResultsToRows } from \"./mapResults\";\nimport { printSummary } from \"./printSummary\";\n\nconst DEFAULT_POLL_INTERVAL = 2000;\nconst DEFAULT_TIMEOUT = 600000; // 10 minutes\n\ntype ExperimentsFacadeConfig = {\n langwatchApiClient: LangwatchApiClient;\n endpoint: string;\n apiKey: string;\n logger: Logger;\n};\n\n/**\n * Facade for creating experiment sessions and running platform-configured experiments\n */\nexport class ExperimentsFacade {\n private readonly config: ExperimentsFacadeConfig;\n private readonly apiService: ExperimentsApiService;\n\n constructor(config: ExperimentsFacadeConfig) {\n this.config = config;\n this.apiService = new ExperimentsApiService({\n langwatchApiClient: config.langwatchApiClient,\n });\n }\n\n /**\n * Initialize a new experiment session (SDK-defined)\n *\n * @param name - Name of the experiment (used as slug)\n * @param options - Optional configuration\n * @returns An initialized Experiment instance\n *\n * @example\n * ```typescript\n * const experiment = await langwatch.experiments.init('my-experiment');\n *\n * await experiment.run(dataset, async ({ item, index }) => {\n * const response = await myAgent(item.question);\n * experiment.log('accuracy', { index, score: 0.95 });\n * });\n * ```\n */\n async init(\n name: string,\n options?: ExperimentInitOptions,\n ): Promise<Experiment> {\n return Experiment.init(name, {\n apiClient: this.config.langwatchApiClient,\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n logger: this.config.logger,\n ...options,\n });\n }\n\n /**\n * Run a platform-configured experiment (Experiments Workbench)\n *\n * This runs an experiment that was configured in the LangWatch platform.\n * The method automatically prints a summary and exits with code 1 on failure\n * (unless `exitOnFailure: false` is passed).\n *\n * @param slug - The slug of the experiment (found in the experiment URL)\n * @param options - Optional configuration\n * @returns The experiment results including pass rate and summary\n *\n * @example\n * ```typescript\n * import { LangWatch } from \"langwatch\";\n *\n * const langwatch = new LangWatch();\n *\n * const result = await langwatch.experiments.run(\"my-experiment-slug\");\n * result.printSummary();\n * ```\n */\n async run(\n slug: string,\n options?: RunExperimentOptions,\n ): Promise<ExperimentRunResult> {\n this.config.logger.info(`Running platform experiment: ${slug}`);\n const result = await this.runWithPolling(slug, options);\n return result;\n }\n\n /**\n * Run a platform experiment and return per-row structured results.\n *\n * Starts the run through the unified evaluations-v3 backend (optionally\n * overriding the configured inputs via `data` / `datasetId` / `parameters` /\n * `rowIndices`), polls to completion, fetches the per-row results, and maps\n * them to the same row structure as the python SDK's results DataFrame.\n *\n * @param slug - The slug of the experiment (found in the experiment URL)\n * @param options - Optional inputs and polling configuration\n * @returns The run id, results URL, status, summary, and per-row results\n *\n * @example\n * ```typescript\n * const langwatch = new LangWatch();\n * const { rows, runUrl } = await langwatch.experiments.runWithResults(\n * \"my-experiment-slug\",\n * { data: [{ question: \"What is 2 + 2?\" }] },\n * );\n * for (const row of rows) {\n * console.log(row.output, row.evaluations);\n * }\n * ```\n */\n async runWithResults(\n slug: string,\n options: RunWithResultsOptions = {},\n ): Promise<ExperimentRunWithResults> {\n this.config.logger.info(\n `Running platform experiment with results: ${slug}`,\n );\n\n const body = toRunStartRequest({\n data: options.data,\n datasetId: options.datasetId,\n parameters: options.parameters,\n rowIndices: options.rowIndices,\n });\n\n const startResponse = await this.apiService.startV3Run({ slug, body });\n const { runId } = startResponse;\n\n const { status, summary } = await pollExperimentRun({\n runId,\n getStatus: (id) => this.apiService.getV3RunStatus(id),\n pollInterval: options.pollInterval,\n timeout: options.timeout,\n onProgress: options.onProgress,\n });\n\n // ClickHouse can lag right after completion: the results endpoint may 404\n // (\"not yet available\") or return 200 with an empty dataset before the rows\n // materialize. Retry both cases when the run reported rows, mirroring the\n // python SDK, instead of failing or returning an empty result.\n const results = await fetchResultsWithRetry({\n getResults: () =>\n this.apiService.getV3RunResults({ runId, experimentSlug: slug }),\n isEmpty: (r) => (r.dataset?.length ?? 0) === 0,\n expectsRows: (summary.totalCells ?? 0) > 0,\n delay: options.pollInterval ?? DEFAULT_POLL_INTERVAL,\n });\n\n // Always return the URL rebased onto the configured endpoint, so a\n // self-hosted run does not surface a cloud (app.langwatch.ai) link. Both\n // the start response and the summary carry the platform's own URL; whichever\n // is present gets its domain replaced. Mirrors the python SDK.\n const rawRunUrl = startResponse.runUrl ?? summary.runUrl;\n const runUrl = rawRunUrl\n ? rebaseUrlToEndpoint(rawRunUrl, this.config.endpoint)\n : \"\";\n\n return {\n runId,\n runUrl,\n status,\n summary,\n rows: mapRunResultsToRows(results),\n };\n }\n\n /**\n * Run an experiment and wait for completion using polling\n */\n private async runWithPolling(\n slug: string,\n options: RunExperimentOptions = {},\n ): Promise<ExperimentRunResult> {\n const pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n // Start the run\n const startResponse = await this.startRun(slug);\n const { runId } = startResponse;\n\n // Use the run URL from API but replace domain with configured endpoint\n const apiRunUrl = startResponse.runUrl ?? \"\";\n const runUrl = apiRunUrl\n ? rebaseUrlToEndpoint(apiRunUrl, this.config.endpoint)\n : \"\";\n\n console.log(`Started experiment run: ${runId}`);\n if (runUrl) {\n console.log(`Follow live: ${runUrl}`);\n }\n\n const total = startResponse.total;\n let lastProgress = 0;\n\n // Print initial progress\n if (total > 0) {\n process.stdout.write(`Progress: 0/${total} (0%)`);\n }\n options.onProgress?.(0, total);\n\n const startTime = Date.now();\n\n // Poll until complete\n while (true) {\n if (Date.now() - startTime > timeout) {\n console.log(); // Newline after progress\n const finalStatus = await this.getRunStatus(runId);\n throw new ExperimentTimeoutError(\n runId,\n finalStatus.progress,\n finalStatus.total,\n );\n }\n\n await this.sleep(pollInterval);\n\n const status = await this.getRunStatus(runId);\n const progress = status.progress;\n\n // Update progress display if changed\n if (progress !== lastProgress && status.total > 0) {\n const percentage = Math.round((progress / status.total) * 100);\n process.stdout.write(\n `\\rProgress: ${progress}/${status.total} (${percentage}%)`,\n );\n lastProgress = progress;\n }\n\n options.onProgress?.(status.progress, status.total);\n\n if (status.status === \"completed\") {\n console.log(); // Newline after progress\n const summary = status.summary ?? {};\n return this.buildResult(runId, \"completed\", summary, runUrl ?? \"\");\n }\n\n if (status.status === \"failed\") {\n console.log(); // Newline after progress\n throw new ExperimentRunFailedError(\n runId,\n status.error ?? \"Unknown error\",\n );\n }\n\n if (status.status === \"stopped\") {\n console.log(); // Newline after progress\n return this.buildResult(\n runId,\n \"stopped\",\n status.summary ?? {\n runId,\n totalCells: status.total,\n completedCells: status.progress,\n failedCells: 0,\n duration: Date.now() - startTime,\n },\n runUrl ?? \"\",\n );\n }\n }\n }\n\n /**\n * Start an experiment run\n */\n private async startRun(\n slug: string,\n ): Promise<{ runId: string; total: number; runUrl?: string }> {\n let response;\n try {\n response = await this.config.langwatchApiClient.POST(\n \"/api/experiments/{slug}/run\",\n {\n params: {\n path: { slug },\n },\n },\n );\n } catch (error) {\n if (isLangWatchHandledError(error)) {\n this.handleStartRunError(slug, error.body, error.httpStatus);\n }\n throw error;\n }\n\n if (response.error) {\n this.handleStartRunError(slug, response.error, response.response.status);\n }\n\n return response.data as { runId: string; total: number; runUrl?: string };\n }\n\n /**\n * Get the status of a run\n */\n private async getRunStatus(runId: string): Promise<{\n status: string;\n progress: number;\n total: number;\n summary?: ExperimentRunSummary;\n error?: string;\n }> {\n let response;\n try {\n response = await this.config.langwatchApiClient.GET(\n \"/api/experiments/runs/{runId}\",\n {\n params: {\n path: { runId },\n },\n },\n );\n } catch (error) {\n if (isLangWatchHandledError(error)) {\n this.handleRunStatusError(runId, error.body, error.httpStatus);\n }\n throw error;\n }\n\n if (response.error) {\n this.handleRunStatusError(\n runId,\n response.error,\n response.response.status,\n );\n }\n\n return response.data as {\n status: string;\n progress: number;\n total: number;\n summary?: ExperimentRunSummary;\n error?: string;\n };\n }\n\n private handleStartRunError(\n slug: string,\n error: unknown,\n status: number,\n ): never {\n if (status === 404) {\n throw new ExperimentNotFoundError(slug);\n }\n\n if (status === 401) {\n throw new ExperimentsApiError(\"Unauthorized - check your API key\", 401);\n }\n\n const errorMessage =\n typeof error === \"object\" &&\n error !== null &&\n \"error\" in error &&\n typeof error.error === \"string\"\n ? error.error\n : `Failed to start experiment: ${slug}`;\n throw new ExperimentsApiError(errorMessage, status);\n }\n\n private handleRunStatusError(\n runId: string,\n error: unknown,\n status: number,\n ): never {\n if (status === 404) {\n throw new ExperimentsApiError(`Run not found: ${runId}`, 404);\n }\n\n if (status === 401) {\n throw new ExperimentsApiError(\"Unauthorized - check your API key\", 401);\n }\n\n const errorMessage =\n typeof error === \"object\" &&\n error !== null &&\n \"error\" in error &&\n typeof error.error === \"string\"\n ? error.error\n : `Failed to get run status: ${runId}`;\n throw new ExperimentsApiError(errorMessage, status);\n }\n\n /**\n * Build the result object from API response\n */\n private buildResult(\n runId: string,\n status: \"completed\" | \"failed\" | \"stopped\",\n summary: ExperimentRunSummary,\n runUrl: string,\n ): ExperimentRunResult {\n const completedCells = summary.completedCells ?? 0;\n const failedCells = summary.failedCells ?? 0;\n const duration = summary.duration ?? 0;\n\n const totalPassed = summary.totalPassed ?? completedCells - failedCells;\n const totalFailed = summary.totalFailed ?? failedCells;\n const passRate =\n summary.passRate ??\n (completedCells > 0 ? (totalPassed / completedCells) * 100 : 0);\n\n const result: ExperimentRunResult = {\n runId,\n status,\n passed: totalPassed,\n failed: totalFailed,\n passRate,\n duration,\n runUrl,\n summary,\n printSummary: (exitOnFailure = true) => {\n printSummary({\n runId,\n status,\n passed: totalPassed,\n failed: totalFailed,\n passRate,\n duration,\n runUrl: summary.runUrl ?? runUrl,\n summary,\n });\n\n if (exitOnFailure && totalFailed > 0) {\n process.exit(1);\n }\n },\n toString: () =>\n `ExperimentRunResult { status: ${status.toUpperCase()}, passed: ${totalPassed}, failed: ${totalFailed}, passRate: ${passRate.toFixed(1)}%, duration: ${(duration / 1000).toFixed(1)}s }`,\n };\n\n // Custom Node.js inspect for console.log\n (result as Record<string | symbol, unknown>)[\n Symbol.for(\"nodejs.util.inspect.custom\")\n ] = () => result.toString();\n\n return result;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * EvaluationsFacade - Entry point for the Evaluations API (Online Evaluations / Guardrails)\n *\n * Provides an API for running evaluators and guardrails in real-time against LLM inputs/outputs.\n *\n * @example\n * ```typescript\n * const langwatch = new LangWatch({ apiKey: \"your-api-key\" });\n *\n * // Run a guardrail\n * const guardrail = await langwatch.evaluations.evaluate(\"presidio/pii_detection\", {\n * data: { input: userInput, output: generatedResponse },\n * name: \"PII Detection\",\n * asGuardrail: true,\n * settings: {},\n * });\n *\n * if (!guardrail.passed) {\n * return \"I'm sorry, I can't do that.\";\n * }\n * ```\n */\n\nimport { trace, SpanStatusCode, context as otelContext } from \"@opentelemetry/api\";\nimport { createLangWatchSpan } from \"@/observability-sdk/span/implementation\";\nimport type {\n EvaluationResult,\n EvaluateOptions,\n EvaluateRequest,\n EvaluateResponse,\n} from \"./types\";\nimport {\n EvaluatorCallError,\n EvaluatorNotFoundError,\n EvaluationsApiError,\n} from \"./errors\";\nimport type { Logger } from \"@/logger\";\nimport { buildAuthHeaders } from \"@/internal/api/auth\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\ntype EvaluationsFacadeConfig = {\n endpoint: string;\n apiKey: string;\n logger: Logger;\n};\n\nexport class EvaluationsFacade {\n readonly #endpoint: string;\n readonly #apiKey: string;\n readonly #logger: Logger;\n\n constructor(config: EvaluationsFacadeConfig) {\n this.#endpoint = config.endpoint;\n this.#apiKey = config.apiKey;\n this.#logger = config.logger;\n }\n\n /**\n * Run an evaluator or guardrail against provided data\n *\n * Creates an OpenTelemetry span attached to the current trace context,\n * calls the LangWatch evaluation API, and returns the result.\n *\n * @param slug - The evaluator slug (e.g., \"presidio/pii_detection\", \"langevals/llm_boolean\")\n * @param options - Evaluation options including data, name, settings, and asGuardrail flag\n * @returns The evaluation result with status, passed, score, details, label, and cost\n *\n * @example\n * ```typescript\n * // Run as a guardrail (synchronous evaluation that can block responses)\n * const guardrail = await langwatch.evaluations.evaluate(\"presidio/pii_detection\", {\n * data: { input: userInput, output: generatedResponse },\n * name: \"PII Detection Guardrail\",\n * asGuardrail: true,\n * });\n *\n * if (!guardrail.passed) {\n * console.log(\"PII detected:\", guardrail.details);\n * return \"Sorry, I cannot process that request.\";\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Run as an online evaluation (async scoring for monitoring)\n * const result = await langwatch.evaluations.evaluate(\"langevals/llm_boolean\", {\n * data: { input: question, output: response },\n * name: \"Quality Check\",\n * settings: { prompt: \"Check if the response answers the question.\" },\n * });\n *\n * console.log(\"Score:\", result.score);\n * console.log(\"Details:\", result.details);\n * ```\n */\n evaluate = async (\n slug: string,\n options: EvaluateOptions\n ): Promise<EvaluationResult> => {\n const { data, name, settings, asGuardrail } = options;\n const spanName = name ?? slug;\n const spanType = asGuardrail ? \"guardrail\" : \"evaluation\";\n\n // Get tracer and create a span attached to the current context\n const tracer = trace.getTracer(\"langwatch-evaluations\");\n\n // Get current trace/span IDs from active context\n const activeSpan = trace.getActiveSpan();\n const traceId = activeSpan\n ? activeSpan.spanContext().traceId\n : undefined;\n const parentSpanId = activeSpan\n ? activeSpan.spanContext().spanId\n : undefined;\n\n // Start the evaluation span\n const otelSpan = tracer.startSpan(\n spanName,\n {\n attributes: {\n \"langwatch.span.type\": spanType,\n },\n },\n otelContext.active()\n );\n\n const langwatchSpan = createLangWatchSpan(otelSpan);\n\n // Set span input\n langwatchSpan.setType(spanType);\n langwatchSpan.setInput({\n data,\n ...(settings && { settings }),\n });\n\n try {\n // Build request payload\n const requestBody: EvaluateRequest = {\n trace_id: traceId ?? null,\n span_id: parentSpanId ?? null,\n name: name ?? null,\n data,\n settings,\n as_guardrail: asGuardrail,\n };\n\n // Call the evaluation API\n const url = `${this.#endpoint}/api/evaluations/${slug}/evaluate`;\n\n this.#logger.debug(`Calling evaluation API: ${url}`);\n\n const response = await langwatchFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildAuthHeaders({ apiKey: this.#apiKey }),\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n\n if (response.status === 404) {\n throw new EvaluatorNotFoundError(slug);\n }\n\n throw new EvaluationsApiError(\n `Evaluation API returned ${response.status}: ${errorText}`,\n response.status\n );\n }\n\n const responseData = (await response.json()) as EvaluateResponse;\n\n // Map response to result\n const result: EvaluationResult = {\n status: responseData.status,\n ...(responseData.passed !== null && responseData.passed !== undefined && { passed: responseData.passed }),\n ...(responseData.score !== null && responseData.score !== undefined && { score: responseData.score }),\n ...(responseData.details !== null && responseData.details !== undefined && { details: responseData.details }),\n ...(responseData.label !== null && responseData.label !== undefined && { label: responseData.label }),\n ...(responseData.cost !== null && responseData.cost !== undefined && { cost: responseData.cost }),\n };\n\n // Update span with output\n langwatchSpan.setOutput({\n type: asGuardrail ? \"guardrail_result\" : \"evaluation_result\",\n value: result,\n });\n\n // Set span status based on result\n if (result.status === \"error\") {\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: result.details ?? \"Evaluation failed\",\n });\n } else {\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n }\n\n return result;\n } catch (error) {\n // Handle errors\n const errorResult: EvaluationResult = {\n status: \"error\",\n details: error instanceof Error ? error.message : String(error),\n };\n\n // For guardrails, default to passed=true on error to avoid blocking\n if (asGuardrail) {\n errorResult.passed = true;\n }\n\n // Update span with error\n langwatchSpan.setOutput({\n type: asGuardrail ? \"guardrail_result\" : \"evaluation_result\",\n value: errorResult,\n });\n\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: errorResult.details,\n });\n\n if (error instanceof Error) {\n otelSpan.recordException(error);\n }\n\n // Re-throw known errors\n if (\n error instanceof EvaluatorNotFoundError ||\n error instanceof EvaluationsApiError ||\n error instanceof EvaluatorCallError\n ) {\n throw error;\n }\n\n // Wrap unknown errors\n throw new EvaluatorCallError(\n slug,\n error instanceof Error ? error.message : String(error)\n );\n } finally {\n // Always end the span\n otelSpan.end();\n }\n };\n}\n","/**\n * Error classes for the Evaluations API\n */\n\n/**\n * Base error for evaluation operations\n */\nexport class EvaluationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EvaluationError\";\n }\n}\n\n/**\n * Error when an evaluator call fails\n */\nexport class EvaluatorCallError extends EvaluationError {\n readonly evaluatorSlug: string;\n readonly statusCode?: number;\n\n constructor(evaluatorSlug: string, message: string, statusCode?: number) {\n super(`Evaluator '${evaluatorSlug}' failed: ${message}`);\n this.name = \"EvaluatorCallError\";\n this.evaluatorSlug = evaluatorSlug;\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Error when evaluator is not found\n */\nexport class EvaluatorNotFoundError extends EvaluationError {\n readonly evaluatorSlug: string;\n\n constructor(evaluatorSlug: string) {\n super(`Evaluator not found: ${evaluatorSlug}`);\n this.name = \"EvaluatorNotFoundError\";\n this.evaluatorSlug = evaluatorSlug;\n }\n}\n\n/**\n * Error from the evaluations API\n */\nexport class EvaluationsApiError extends EvaluationError {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = \"EvaluationsApiError\";\n this.statusCode = statusCode;\n }\n}\n","export class EvaluatorsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"EvaluatorsApiError\";\n }\n}\n","/**\n * The one place the SDK decides \"did the platform NAME this failure?\".\n *\n * Every `*-api.service.ts` funnels its non-2xx responses through a private\n * `handleApiError`, which builds an English sentence and throws that service's\n * own error class. This slots in immediately before the throw: if the body is a\n * domain error, it raises the typed one instead; if it is anything else, it\n * returns and the service throws exactly what it always threw.\n *\n * That ordering is the whole design. Domain errors become typed; everything else\n * — a 5xx, a proxy's HTML, a truncated body, a dead socket — keeps its existing\n * behaviour byte for byte, including the message, because the sentence the\n * generic path already built is handed in and reused rather than rebuilt.\n */\nimport {\n handledErrorFrom,\n type LangWatchHandledError,\n} from \"@/internal/api/errors\";\nimport { extractStatusFromResponse } from \"./format-api-error\";\n\nexport interface ThrowIfHandledErrorParams {\n /** What was being attempted, e.g. `get trace \"abc\"`. */\n operation: string;\n /** The error body the HTTP client handed back. */\n error: unknown;\n /** The response it came on, when the service kept hold of it. */\n response?: Response;\n /** The status, when the service resolved it already. */\n status?: number;\n /** The sentence the generic path built — reused verbatim so nothing regresses. */\n message: string;\n}\n\n/**\n * Throws a {@link LangWatchHandledError} when the platform named the failure.\n * Returns — deliberately, so the caller throws its own error — when it did not.\n */\nexport function throwIfHandledError({\n operation,\n error,\n response,\n status,\n message,\n}: ThrowIfHandledErrorParams): void {\n const resolved =\n status ?? response?.status ?? extractStatusFromResponse(error);\n\n const handledError: LangWatchHandledError | null = handledErrorFrom({\n operation,\n body: error,\n status: resolved,\n message,\n });\n\n if (handledError) throw handledError;\n}\n","import type {\n CreateEvaluatorBody,\n DeleteEvaluatorResponse,\n EvaluatorResponse,\n UpdateEvaluatorBody,\n} from \"./types\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { EvaluatorsApiError } from \"./errors\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\n/**\n * Service for retrieving evaluator resources via the LangWatch API.\n *\n * Provides read-only access to project evaluators with computed fields.\n */\nexport class EvaluatorsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new EvaluatorsApiError(message, operation, error);\n }\n\n /**\n * Fetches all evaluators for the project.\n */\n async getAll(): Promise<EvaluatorResponse[]> {\n const { data, error } = await this.apiClient.GET(\"/api/evaluators\");\n if (error) this.handleApiError(\"fetch all evaluators\", error);\n return data;\n }\n\n /**\n * Fetches a single evaluator by its ID or slug.\n */\n async get(idOrSlug: string): Promise<EvaluatorResponse> {\n const { data, error } = await this.apiClient.GET(\n \"/api/evaluators/{idOrSlug}\",\n {\n params: { path: { idOrSlug } },\n },\n );\n if (error)\n this.handleApiError(\n `fetch evaluator with ID or slug \"${idOrSlug}\"`,\n error,\n );\n return data;\n }\n\n /**\n * Creates a new evaluator.\n */\n async create(params: CreateEvaluatorBody): Promise<EvaluatorResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/evaluators\", {\n body: params,\n });\n if (error) this.handleApiError(\"create evaluator\", error);\n return data;\n }\n\n /**\n * Updates an evaluator by its ID.\n */\n async update(id: string, params: UpdateEvaluatorBody): Promise<EvaluatorResponse> {\n const { data, error } = await this.apiClient.PUT(\n \"/api/evaluators/{id}\",\n {\n params: { path: { id } },\n body: params,\n },\n );\n if (error)\n this.handleApiError(`update evaluator with ID \"${id}\"`, error);\n return data;\n }\n\n /**\n * Deletes (archives) an evaluator by its ID.\n */\n async delete(id: string): Promise<DeleteEvaluatorResponse> {\n const { data, error } = await this.apiClient.DELETE(\n \"/api/evaluators/{id}\",\n {\n params: { path: { id } },\n },\n );\n if (error)\n this.handleApiError(`delete evaluator with ID \"${id}\"`, error);\n return data;\n }\n}\n","export class ScenariosApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"ScenariosApiError\";\n }\n}\n","import type {\n CreateScenarioBody,\n DeleteScenarioResponse,\n ScenarioResponse,\n ScenarioVersionDetail,\n ScenarioVersionListResponse,\n UpdateScenarioBody,\n} from \"./types\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { ScenariosApiError } from \"./errors\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport class ScenariosApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new ScenariosApiError(message, operation, error);\n }\n\n async getAll(): Promise<ScenarioResponse[]> {\n const { data, error } = await this.apiClient.GET(\"/api/scenarios\");\n if (error) this.handleApiError(\"fetch all scenarios\", error);\n return data;\n }\n\n async get(id: string): Promise<ScenarioResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/scenarios/{id}\", {\n params: { path: { id } },\n });\n if (error)\n this.handleApiError(`fetch scenario with ID \"${id}\"`, error);\n return data;\n }\n\n async create(params: CreateScenarioBody): Promise<ScenarioResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/scenarios\", {\n body: params,\n });\n if (error) this.handleApiError(\"create scenario\", error);\n return data;\n }\n\n async update(id: string, params: UpdateScenarioBody): Promise<ScenarioResponse> {\n const { data, error } = await this.apiClient.PUT(\"/api/scenarios/{id}\", {\n params: { path: { id } },\n body: params,\n });\n if (error)\n this.handleApiError(`update scenario with ID \"${id}\"`, error);\n return data;\n }\n\n /** The saved versions of a scenario, newest first. */\n async listVersions(\n id: string,\n options?: { limit?: number; cursor?: number },\n ): Promise<ScenarioVersionListResponse> {\n const query = {\n ...(options?.limit !== undefined && { limit: options.limit }),\n ...(options?.cursor !== undefined && { cursor: options.cursor }),\n };\n const { data, error } = await this.apiClient.GET(\n \"/api/scenarios/{id}/versions\",\n {\n params: { path: { id }, query },\n },\n );\n if (error)\n this.handleApiError(`list versions of scenario \"${id}\"`, error);\n return data;\n }\n\n /** One saved version of a scenario, with the content it saved. */\n async getVersion(\n id: string,\n version: number,\n ): Promise<ScenarioVersionDetail> {\n const { data, error } = await this.apiClient.GET(\n \"/api/scenarios/{id}/versions/{version}\",\n {\n params: { path: { id, version } },\n },\n );\n if (error)\n this.handleApiError(\n `get version ${version} of scenario \"${id}\"`,\n error,\n );\n return data;\n }\n\n async delete(id: string): Promise<DeleteScenarioResponse> {\n const { data, error } = await this.apiClient.DELETE(\n \"/api/scenarios/{id}\",\n {\n params: { path: { id } },\n },\n );\n if (error)\n this.handleApiError(`delete scenario with ID \"${id}\"`, error);\n return data;\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport type SuiteResponse = NonNullable<\n paths[\"/api/suites\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number] & {\n /** URL to view this suite on the LangWatch platform */\n platformUrl?: string;\n};\n\nexport type CreateSuiteBody = NonNullable<\n paths[\"/api/suites\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdateSuiteBody = NonNullable<\n paths[\"/api/suites/{id}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type SuiteRunResult =\n paths[\"/api/suites/{id}/run\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport interface SuiteTarget {\n type: \"prompt\" | \"http\" | \"code\" | \"workflow\";\n referenceId: string;\n}\n\n/** Options for `POST /api/suites/{id}/run`. */\nexport interface SuiteRunOptions {\n /**\n * Key that makes the request safe to retry. Generated per call when omitted,\n * so two retries of the same command schedule two runs unless the caller\n * pins one.\n */\n idempotencyKey?: string;\n /**\n * Constant values applied to every scenario in the run, e.g. a fixture id or\n * a tenant. A value supplied here overrides the scenario's own default for\n * that name.\n */\n parameters?: Record<string, string | number | boolean>;\n /**\n * One short line saying why this batch was run, e.g. a commit hash or what\n * changed. Every run of the batch carries it. Spaces around it are removed,\n * and a note of only spaces is sent as no note at all.\n */\n note?: string;\n}\n\n/** Which kind of suite `getAll` returns. Defaults to run plans. */\nexport type SuiteKind = \"custom\" | \"folder\";\n\nexport class SuitesApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SuitesApiError\";\n }\n}\n\n/**\n * @deprecated Use runPlans and testSuites; /api/suites is a frozen alias.\n */\nexport class SuitesApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new SuitesApiError(message, operation, error);\n }\n\n async getAll(options?: { kind?: SuiteKind }): Promise<SuiteResponse[]> {\n const { data, error } = await this.apiClient.GET(\"/api/suites\", {\n ...(options?.kind !== undefined && {\n params: { query: { kind: options.kind } },\n }),\n });\n if (error) this.handleApiError(\"list suites\", error);\n return data;\n }\n\n async get(id: string): Promise<SuiteResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/suites/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`get suite \"${id}\"`, error);\n return data;\n }\n\n async create(params: CreateSuiteBody): Promise<SuiteResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/suites\", {\n body: params,\n });\n if (error) this.handleApiError(\"create suite\", error);\n return data;\n }\n\n async update(id: string, params: UpdateSuiteBody): Promise<SuiteResponse> {\n const { data, error } = await this.apiClient.PATCH(\"/api/suites/{id}\", {\n params: { path: { id } },\n body: params,\n });\n if (error) this.handleApiError(`update suite \"${id}\"`, error);\n return data;\n }\n\n async duplicate(id: string): Promise<SuiteResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/suites/{id}/duplicate\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`duplicate suite \"${id}\"`, error);\n return data;\n }\n\n async run(id: string, options?: SuiteRunOptions): Promise<SuiteRunResult>;\n /**\n * @deprecated Pass `{ idempotencyKey }` instead. The options object is what\n * carries run parameters, and a positional key cannot reach them.\n */\n async run(id: string, idempotencyKey: string): Promise<SuiteRunResult>;\n async run(\n id: string,\n optionsOrIdempotencyKey?: SuiteRunOptions | string,\n ): Promise<SuiteRunResult> {\n const options: SuiteRunOptions =\n typeof optionsOrIdempotencyKey === \"string\"\n ? { idempotencyKey: optionsOrIdempotencyKey }\n : optionsOrIdempotencyKey ?? {};\n\n const body: {\n idempotencyKey: string;\n parameters?: Record<string, string | number | boolean>;\n note?: string;\n } = {\n idempotencyKey:\n options.idempotencyKey ??\n `cli-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n };\n if (options.parameters !== undefined) body.parameters = options.parameters;\n // A note of only spaces is no note: sending \"\" would store an empty string\n // every reader then has to filter out.\n const note = options.note?.trim();\n if (note) body.note = note;\n\n const { data, error } = await this.apiClient.POST(\"/api/suites/{id}/run\", {\n params: { path: { id } },\n body,\n });\n if (error) this.handleApiError(`run suite \"${id}\"`, error);\n return data;\n }\n\n async delete(id: string): Promise<{ id: string; archived: boolean }> {\n const { data, error } = await this.apiClient.DELETE(\"/api/suites/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`delete suite \"${id}\"`, error);\n return data as unknown as { id: string; archived: boolean };\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\n/** One run plan, exactly as the REST surface answers it. */\nexport type RunPlan =\n paths[\"/api/v1/run-plans/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** What a run plan covers. A dynamic scope is resolved again at every run. */\nexport type RunPlanScope = RunPlan[\"scope\"];\n\n/** What a run goes against. */\nexport type RunPlanTarget = RunPlan[\"targets\"][number];\n\n/** The body `POST /api/v1/run-plans/run` takes. */\nexport type RunPlanRunBody = NonNullable<\n paths[\"/api/v1/run-plans/run\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** The configuration half of a run request. */\nexport type RunPlanConfig = RunPlanRunBody[\"config\"];\n\n/**\n * One evaluator a plan runs beside the ones its test suites attach. It reads\n * the conversation and the trace, never a scenario field.\n */\nexport type RunPlanEvaluator = NonNullable<RunPlanConfig[\"evaluators\"]>[number];\n\n/** What a scheduled run answers with. */\nexport type RunPlanRunResult =\n paths[\"/api/v1/run-plans/run\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** The body `POST /api/v1/run-plans/{id}/run` takes. */\nexport type RunPlanRerunBody = NonNullable<\n paths[\"/api/v1/run-plans/{id}/run\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class RunPlansApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The HTTP status the platform answered with, when the response was kept,\n * so the CLI's error reader does not degrade a named failure to\n * `network_error`.\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"RunPlansApiError\";\n }\n}\n\n/**\n * Typed client for the run plan family (`/api/v1/run-plans`).\n *\n * A run plan is identified by its NAME. Posting a configuration under a name\n * already in use replaces that plan's configuration and joins its history;\n * posting under a new name creates the plan; posting no name lets the platform\n * derive one from what the run covers and what it runs against.\n *\n * @see specs/typescript-sdk/run-plans-and-test-suites.feature\n */\nexport class RunPlansApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(\n operation: string,\n error: unknown,\n response?: Response,\n ): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation,\n error,\n options: { status },\n });\n throwIfHandledError({ operation, error, response, message });\n throw new RunPlansApiError(message, operation, error, status);\n }\n\n /** The project's run plans. Archived plans are left out unless asked for. */\n async list(options?: { includeArchived?: boolean }): Promise<RunPlan[]> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/run-plans\",\n {\n ...(options?.includeArchived\n ? { params: { query: { includeArchived: \"true\" } } }\n : {}),\n },\n );\n if (error) this.handleApiError(\"list run plans\", error, response);\n return data as unknown as RunPlan[];\n }\n\n async get(id: string): Promise<RunPlan> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/run-plans/{id}\",\n { params: { path: { id } } },\n );\n if (error) this.handleApiError(`get run plan \"${id}\"`, error, response);\n return data as unknown as RunPlan;\n }\n\n /**\n * Runs a configuration under a name.\n *\n * A note of only spaces is no note: sending an empty string would store a\n * value every reader then has to filter out.\n */\n async run(body: RunPlanRunBody): Promise<RunPlanRunResult> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/run-plans/run\",\n { body: withTrimmedNote(body) },\n );\n if (error) this.handleApiError(\"run a run plan\", error, response);\n return data as unknown as RunPlanRunResult;\n }\n\n /** Runs a plan again with the configuration it already holds. */\n async rerun(\n id: string,\n body: RunPlanRerunBody = {},\n ): Promise<RunPlanRunResult> {\n const { data, error, response} = await this.apiClient.POST(\n \"/api/v1/run-plans/{id}/run\",\n { params: { path: { id } }, body: withTrimmedNote(body) },\n );\n if (error) this.handleApiError(`rerun run plan \"${id}\"`, error, response);\n return data as unknown as RunPlanRunResult;\n }\n\n async archive(id: string): Promise<{ id: string; archived: true }> {\n const { data, error, response } = await this.apiClient.DELETE(\n \"/api/v1/run-plans/{id}\",\n { params: { path: { id } } },\n );\n if (error) this.handleApiError(`archive run plan \"${id}\"`, error, response);\n return data as unknown as { id: string; archived: true };\n }\n}\n\n/** Drops a note that holds only spaces, and trims the rest. */\nfunction withTrimmedNote<T extends { note?: string }>(body: T): T {\n const note = body.note?.trim();\n if (note) return { ...body, note };\n const { note: _dropped, ...rest } = body;\n return rest as T;\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\n/** One Instant Eval run, exactly as the REST surface answers it. */\nexport type InstantEvalRun =\n paths[\"/api/v1/instant-evals/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** One question a run asks, named by the output column it lands in. */\nexport type InstantEvalQuestion = InstantEvalRun[\"questions\"][number];\n\n/** The body that starts a run, and the same body that prices one. */\nexport type InstantEvalRunBody = NonNullable<\n paths[\"/api/v1/instant-evals\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** What a run would read and what judging it would cost. */\nexport type InstantEvalEstimate =\n paths[\"/api/v1/instant-evals/estimate\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** One page of a run's judgements, and where the next one starts. */\nexport type InstantEvalResultsPage =\n paths[\"/api/v1/instant-evals/{id}/results\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** One judgement the run recorded. */\nexport type InstantEvalJudgment = InstantEvalResultsPage[\"judgments\"][number];\n\n/** A few of a run's rows, with the judged text beside the verdict. */\nexport type InstantEvalSample =\n paths[\"/api/v1/instant-evals/{id}/sample\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class InstantEvalsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The HTTP status the platform answered with, when the response was kept,\n * so the CLI's error reader does not degrade a named failure to\n * `network_error`.\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"InstantEvalsApiError\";\n }\n}\n\n/**\n * Typed client for the Instant Evals family (`/api/v1/instant-evals`).\n *\n * A run judges one LangWatchQL statement across the project's history. The\n * statement is the same one the query door runs, so a working query is a\n * working run once it projects `TraceId` and at least one eval function\n * column. Starting one answers the queued run, and its progress is polled.\n *\n * The project comes from the credential, so no method takes a project id.\n *\n * @see specs/instant-evals/instant-eval-api.feature\n */\nexport class InstantEvalsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(\n operation: string,\n error: unknown,\n response?: Response,\n ): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation,\n error,\n options: { status },\n });\n throwIfHandledError({ operation, error, response, message });\n throw new InstantEvalsApiError(message, operation, error, status);\n }\n\n /**\n * The body of a successful answer. A failed response can arrive with no body\n * at all (a proxy answering 502 while the platform restarts), which leaves\n * `error` empty, so the response status decides and not the error alone.\n */\n private unwrap<T>({\n operation,\n data,\n error,\n response,\n }: {\n operation: string;\n data: unknown;\n error: unknown;\n response?: Response;\n }): T {\n const failed = response !== undefined && !response.ok;\n if (error || failed || data === undefined) {\n this.handleApiError(operation, error ?? undefined, response);\n }\n return data as T;\n }\n\n /** Starts a run. The judging happens on the queue. */\n async create(body: InstantEvalRunBody): Promise<InstantEvalRun> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/instant-evals\",\n { body },\n );\n return this.unwrap<InstantEvalRun>({\n operation: \"start an instant eval run\",\n data,\n error,\n response,\n });\n }\n\n /** Prices a run without starting it. Nothing is judged and nothing is charged. */\n async estimate(body: InstantEvalRunBody): Promise<InstantEvalEstimate> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/instant-evals/estimate\",\n { body },\n );\n return this.unwrap<InstantEvalEstimate>({\n operation: \"estimate an instant eval run\",\n data,\n error,\n response,\n });\n }\n\n /** The project's runs, newest first. */\n async list(options?: {\n limit?: number;\n before?: string;\n beforeId?: string;\n }): Promise<InstantEvalRun[]> {\n const query = {\n ...(options?.limit === undefined ? {} : { limit: options.limit }),\n ...(options?.before === undefined ? {} : { before: options.before }),\n ...(options?.beforeId === undefined\n ? {}\n : { beforeId: options.beforeId }),\n };\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/instant-evals\",\n { params: { query } as never },\n );\n return this.unwrap<{ runs: InstantEvalRun[] }>({\n operation: \"list instant eval runs\",\n data,\n error,\n response,\n }).runs;\n }\n\n async get(id: string): Promise<InstantEvalRun> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/instant-evals/{id}\",\n { params: { path: { id } } },\n );\n return this.unwrap<InstantEvalRun>({\n operation: `get instant eval run \"${id}\"`,\n data,\n error,\n response,\n });\n }\n\n /** Asks a run to stop. It stops before its next page. */\n async cancel(id: string): Promise<InstantEvalRun> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/instant-evals/{id}/cancel\",\n { params: { path: { id } } },\n );\n return this.unwrap<InstantEvalRun>({\n operation: `cancel instant eval run \"${id}\"`,\n data,\n error,\n response,\n });\n }\n\n /**\n * One page of a run's judgements.\n *\n * Pass the `nextCursor` a page answers with to read the page after it. The\n * last page carries none.\n */\n async results(\n id: string,\n options?: {\n questionId?: string;\n isMatched?: boolean;\n status?: InstantEvalJudgment[\"status\"];\n limit?: number;\n cursor?: string;\n },\n ): Promise<InstantEvalResultsPage> {\n const query = {\n ...(options?.questionId === undefined\n ? {}\n : { questionId: options.questionId }),\n // The query key stays `matched`, which is what the endpoint reads; the\n // option is named for what it is, which is a boolean.\n ...(options?.isMatched === undefined\n ? {}\n : { matched: options.isMatched ? \"true\" : \"false\" }),\n ...(options?.status === undefined ? {} : { status: options.status }),\n ...(options?.limit === undefined ? {} : { limit: options.limit }),\n ...(options?.cursor === undefined ? {} : { cursor: options.cursor }),\n };\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/instant-evals/{id}/results\",\n { params: { path: { id }, query } as never },\n );\n return this.unwrap<InstantEvalResultsPage>({\n operation: `read instant eval run \"${id}\" results`,\n data,\n error,\n response,\n });\n }\n\n /** A few of the run's rows, with the judged text beside the verdict. */\n async sample(id: string, options?: { n?: number }): Promise<InstantEvalSample> {\n const query = options?.n === undefined ? {} : { n: options.n };\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/instant-evals/{id}/sample\",\n { params: { path: { id }, query } as never },\n );\n return this.unwrap<InstantEvalSample>({\n operation: `sample instant eval run \"${id}\"`,\n data,\n error,\n response,\n });\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport type { RunPlanRunResult } from \"@/client-sdk/services/run-plans/run-plans-api.service\";\n\n/** One test suite as the list answers it: a name and the scenarios filed in it. */\nexport type TestSuite = NonNullable<\n paths[\"/api/v1/test-suites\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\n/** One test suite read on its own, with its scenarios named. */\nexport type TestSuiteDetail =\n paths[\"/api/v1/test-suites/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** The body `POST /api/v1/test-suites` takes. */\nexport type CreateTestSuiteBody = NonNullable<\n paths[\"/api/v1/test-suites\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/**\n * The body `PATCH /api/v1/test-suites/{id}` takes: any of the name, the full\n * field list and the full evaluator list.\n */\nexport type UpdateTestSuiteBody = NonNullable<\n paths[\"/api/v1/test-suites/{id}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** The body `rename` sends: the name alone. */\nexport type RenameTestSuiteBody = Pick<UpdateTestSuiteBody, \"name\"> &\n Required<Pick<UpdateTestSuiteBody, \"name\">>;\n\n/** One field a test suite declares: an identifier and a type. */\nexport type SuiteFieldDefinition = NonNullable<\n UpdateTestSuiteBody[\"fields\"]\n>[number];\n\n/** One evaluator attached to a test suite or a run plan, with its mappings. */\nexport type EvaluatorAttachment = NonNullable<\n UpdateTestSuiteBody[\"evaluators\"]\n>[number];\n\n/** Where one evaluator input reads its value: a source path or a literal. */\nexport type ScenarioMapping = EvaluatorAttachment[\"mappings\"][string];\n\n/** The body `POST /api/v1/test-suites/{id}/run` takes. */\nexport type RunTestSuiteBody = NonNullable<\n paths[\"/api/v1/test-suites/{id}/run\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** What a scheduled suite run answers with. It is a run plan run. */\nexport type TestSuiteRunResult = RunPlanRunResult;\n\nexport class TestSuitesApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /** The HTTP status the platform answered with, when the response was kept. */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"TestSuitesApiError\";\n }\n}\n\n/**\n * Typed client for the test suite family (`/api/v1/test-suites`).\n *\n * A test suite is a group of scenarios: a name, and the scenarios filed in it.\n * It holds no targets and no configuration, so running one sends its targets with\n * the request and the platform files the run under a run plan.\n *\n * @see specs/typescript-sdk/run-plans-and-test-suites.feature\n */\nexport class TestSuitesApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(\n operation: string,\n error: unknown,\n response?: Response,\n ): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation,\n error,\n options: { status },\n });\n throwIfHandledError({ operation, error, response, message });\n throw new TestSuitesApiError(message, operation, error, status);\n }\n\n /** The project's test suites. Archived suites are left out unless asked for. */\n async list(options?: { includeArchived?: boolean }): Promise<TestSuite[]> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/test-suites\",\n {\n ...(options?.includeArchived\n ? { params: { query: { includeArchived: \"true\" } } }\n : {}),\n },\n );\n if (error) this.handleApiError(\"list test suites\", error, response);\n return data as unknown as TestSuite[];\n }\n\n async create(params: CreateTestSuiteBody): Promise<TestSuite> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/test-suites\",\n { body: params },\n );\n if (error) this.handleApiError(\"create test suite\", error, response);\n return data as unknown as TestSuite;\n }\n\n async get(id: string): Promise<TestSuiteDetail> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/test-suites/{id}\",\n { params: { path: { id } } },\n );\n if (error) this.handleApiError(`get test suite \"${id}\"`, error, response);\n return data as unknown as TestSuiteDetail;\n }\n\n /**\n * Edits a suite: any of its name, its fields and its evaluators. A field\n * list or an evaluator list replaces the one the suite holds; a key left\n * out keeps what the suite has.\n */\n async update(id: string, params: UpdateTestSuiteBody): Promise<TestSuite> {\n const { data, error, response } = await this.apiClient.PATCH(\n \"/api/v1/test-suites/{id}\",\n { params: { path: { id } }, body: params },\n );\n if (error) this.handleApiError(`update test suite \"${id}\"`, error, response);\n return data as unknown as TestSuite;\n }\n\n /** Renames a suite. The slug is kept, so links and run history stay put. */\n async rename(id: string, params: RenameTestSuiteBody): Promise<TestSuite> {\n const { data, error, response } = await this.apiClient.PATCH(\n \"/api/v1/test-suites/{id}\",\n { params: { path: { id } }, body: { name: params.name } },\n );\n if (error) this.handleApiError(`rename test suite \"${id}\"`, error, response);\n return data as unknown as TestSuite;\n }\n\n /** Archives a suite. The scenarios filed in it are archived with it. */\n async archive(id: string): Promise<{ id: string; archived: true }> {\n const { data, error, response } = await this.apiClient.DELETE(\n \"/api/v1/test-suites/{id}\",\n { params: { path: { id } } },\n );\n if (error)\n this.handleApiError(`archive test suite \"${id}\"`, error, response);\n return data as unknown as { id: string; archived: true };\n }\n\n /**\n * Runs every scenario filed in the suite against the targets sent with the\n * request. A note of only spaces is no note.\n */\n async run(id: string, body: RunTestSuiteBody): Promise<TestSuiteRunResult> {\n const note = body.note?.trim();\n const { note: _dropped, ...rest } = body;\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/test-suites/{id}/run\",\n {\n params: { path: { id } },\n body: note ? { ...rest, note } : rest,\n },\n );\n if (error) this.handleApiError(`run test suite \"${id}\"`, error, response);\n return data as unknown as TestSuiteRunResult;\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { ExperimentsApiService } from \"@/client-sdk/services/experiments/experiments-api.service\";\nimport {\n pollExperimentRun,\n rebaseUrlToEndpoint,\n fetchResultsWithRetry,\n} from \"@/client-sdk/services/experiments/run-status\";\nimport { mapRunResultsToRows } from \"@/client-sdk/services/experiments/mapResults\";\nimport type {\n RunWithResultsOptions,\n ExperimentRunWithResults,\n} from \"@/client-sdk/services/experiments/platformTypes\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\n\nexport type WorkflowResponse = NonNullable<\n paths[\"/api/workflows\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type WorkflowDeleteResponse =\n paths[\"/api/workflows/{id}\"][\"delete\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * Body for `POST /api/workflows/{workflowId}/evaluate`. `data` and `dataset_id`\n * are mutually exclusive on the server (400 if both are sent).\n */\ninterface WorkflowEvaluateRequest {\n version_id?: string;\n data?: Array<Record<string, unknown>>;\n dataset_id?: string;\n parameters?: Record<string, string | number | boolean>;\n row_indices?: number[];\n}\n\n/**\n * Response from `POST /api/workflows/{workflowId}/evaluate`. Hand-written\n * because the route is not yet exposed via the generated OpenAPI types.\n */\ninterface WorkflowEvaluateResponse {\n run_id: string;\n run_url: string;\n workflow_version_id: string;\n version: string;\n}\n\nexport class WorkflowsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"WorkflowsApiError\";\n }\n}\n\nexport class WorkflowsApiService {\n private readonly apiClient: LangwatchApiClient;\n private readonly experimentsApiService: ExperimentsApiService;\n private readonly endpoint: string;\n\n constructor(\n config?: Pick<InternalConfig, \"langwatchApiClient\"> & { endpoint?: string },\n ) {\n // The run URLs this service rebases and the requests it issues have to\n // name the same host, so a caller that supplies only an endpoint gets a\n // client built on that endpoint rather than on the environment.\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiClient =\n config?.langwatchApiClient ??\n createLangWatchApiClient(undefined, this.endpoint);\n this.experimentsApiService = new ExperimentsApiService({\n langwatchApiClient: this.apiClient,\n });\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throw new WorkflowsApiError(message, operation, error);\n }\n\n private async postUndeclaredEndpoint<T>({\n path,\n body,\n operation,\n }: {\n path: string;\n body?: unknown;\n operation: string;\n }): Promise<T> {\n type UntypedClient = {\n POST: (\n path: string,\n init?: { body?: unknown; parseAs?: \"json\" },\n ) => Promise<{ data?: unknown; error?: unknown; response: Response }>;\n };\n\n let result: { data?: unknown; error?: unknown; response: Response };\n try {\n result = await (this.apiClient as unknown as UntypedClient).POST(path, {\n ...(body !== undefined ? { body } : {}),\n parseAs: \"json\",\n });\n } catch (error) {\n this.handleApiError(operation, error);\n }\n\n if (result.error) this.handleApiError(operation, result.error);\n return result.data as T;\n }\n\n async getAll(): Promise<WorkflowResponse[]> {\n const { data, error } = await this.apiClient.GET(\"/api/workflows\");\n if (error) this.handleApiError(\"list workflows\", error);\n return data;\n }\n\n async get(id: string): Promise<WorkflowResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/workflows/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`get workflow \"${id}\"`, error);\n return data;\n }\n\n async delete(id: string): Promise<WorkflowDeleteResponse> {\n const { data, error } = await this.apiClient.DELETE(\"/api/workflows/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`delete workflow \"${id}\"`, error);\n return data;\n }\n\n /**\n * Run a studio workflow evaluation and return per-row structured results.\n *\n * Starts the evaluation through the unified evaluations-v3 backend\n * (`POST /api/workflows/{workflowId}/evaluate`), polls to completion, fetches\n * the per-row results, and maps them to the same row structure as the python\n * SDK's results DataFrame.\n *\n * @param workflowId - The studio workflow id\n * @param options - Optional inputs, committed version override, and polling\n * configuration. `data` and `datasetId` are mutually\n * exclusive.\n * @returns The run id, results URL, status, summary, and per-row results\n *\n * @example\n * ```typescript\n * const langwatch = new LangWatch();\n * const { rows, runUrl } = await langwatch.workflows.run(\"workflow_123\", {\n * data: [{ question: \"What is 2 + 2?\" }],\n * });\n * ```\n */\n async run(\n workflowId: string,\n options: RunWithResultsOptions & { versionId?: string } = {},\n ): Promise<ExperimentRunWithResults> {\n const body: WorkflowEvaluateRequest = {};\n if (options.versionId !== undefined) body.version_id = options.versionId;\n if (options.data !== undefined) body.data = options.data;\n if (options.datasetId !== undefined) body.dataset_id = options.datasetId;\n if (options.parameters !== undefined) body.parameters = options.parameters;\n if (options.rowIndices !== undefined) body.row_indices = options.rowIndices;\n\n const startResponse = await this.postUndeclaredEndpoint<WorkflowEvaluateResponse>(\n {\n path: `/api/workflows/${encodeURIComponent(workflowId)}/evaluate`,\n body,\n operation: `run workflow evaluation for \"${workflowId}\"`,\n },\n );\n\n const runId = startResponse.run_id;\n\n const { status, summary } = await pollExperimentRun({\n runId,\n getStatus: (id) => this.experimentsApiService.getV3RunStatus(id),\n pollInterval: options.pollInterval,\n timeout: options.timeout,\n onProgress: options.onProgress,\n });\n\n // ClickHouse can lag right after completion: retry the results read through\n // the brief 404 / empty-dataset window when the run reported rows. Mirrors\n // the experiment path and the python SDK.\n const results = await fetchResultsWithRetry({\n getResults: () => this.experimentsApiService.getV3RunResults({ runId }),\n isEmpty: (r) => (r.dataset?.length ?? 0) === 0,\n expectsRows: (summary.totalCells ?? 0) > 0,\n delay: options.pollInterval,\n });\n\n // Rebase the run URL onto the configured endpoint so a self-hosted run does\n // not surface a cloud (app.langwatch.ai) link.\n const rawRunUrl = startResponse.run_url ?? summary.runUrl;\n const runUrl = rawRunUrl ? rebaseUrlToEndpoint(rawRunUrl, this.endpoint) : \"\";\n\n return {\n runId,\n runUrl,\n status,\n summary,\n rows: mapRunResultsToRows(results),\n };\n }\n}\n","import {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\n/** One run parameter an agent declares, as the platform lists it. */\nexport interface AgentParameterSpec {\n name: string;\n type: \"string\" | \"number\" | \"boolean\";\n options?: string[];\n default?: string | number | boolean;\n description?: string;\n required?: boolean;\n secret?: boolean;\n}\n\n/** One process connected as an instance of a connected agent. */\nexport interface AgentInstance {\n id: string;\n hostname: string;\n label?: string | null;\n connectedAt: string;\n}\n\nexport interface AgentResponse {\n id: string;\n name: string;\n type: string;\n config: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n platformUrl?: string;\n /** Connected agents: the environment the SDK registered under. */\n environment?: string | null;\n /** Connected agents: online while at least one instance is connected. */\n status?: \"online\" | \"offline\" | null;\n instances?: AgentInstance[];\n lastSeenAt?: string | null;\n /** The owner of a personal agent. */\n owner?: { userId: string; name: string } | null;\n /** The machine a host-scoped development agent belongs to. */\n hostLabel?: string | null;\n /**\n * Whether this key can run the agent. A personal development agent of\n * another person is listed all the same, so two agents of one name can be\n * told apart, but it cannot be run with this key.\n */\n selectable?: boolean;\n /** Why the agent cannot be run with this key; null when it can. */\n notSelectableReason?: \"owned_by_another_person\" | null;\n parameters?: AgentParameterSpec[];\n}\n\nexport interface AgentListResponse {\n data: AgentResponse[];\n pagination: {\n page: number;\n limit: number;\n total: number;\n totalPages: number;\n };\n}\n\n/** The body `POST /api/v1/agents/{id}/call` takes. */\nexport type AgentCallBody = NonNullable<\n paths[\"/api/v1/agents/{id}/call\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** One conversation message as the relay carries it, OpenAI style. */\nexport type AgentCallMessage = AgentCallBody[\"messages\"][number];\n\n/** The reply of the relay: the function's output and the instance that ran it. */\nexport type AgentCallResponse =\n paths[\"/api/v1/agents/{id}/call\"][\"post\"][\"responses\"][200][\"content\"][\"application/json\"];\n\n/** The ids `POST /api/v1/agents/{id}/test` answers with. */\nexport type AgentTestRunResponse =\n paths[\"/api/v1/agents/{id}/test\"][\"post\"][\"responses\"][200][\"content\"][\"application/json\"];\n\nexport class AgentsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"AgentsApiError\";\n }\n}\n\nexport class AgentsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new AgentsApiError(message, operation, error);\n }\n\n async list(params?: { page?: number; limit?: number }): Promise<AgentListResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/v1/agents\", {\n params: { query: params },\n });\n if (error) this.handleApiError(\"list agents\", error);\n return data as unknown as AgentListResponse;\n }\n\n async get(id: string): Promise<AgentResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/v1/agents/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`get agent \"${id}\"`, error);\n return data as unknown as AgentResponse;\n }\n\n async create(params: {\n name: string;\n type: string;\n config: Record<string, unknown>;\n workflowId?: string;\n }): Promise<AgentResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/v1/agents\", {\n body: params as never,\n });\n if (error) this.handleApiError(\"create agent\", error);\n return data as unknown as AgentResponse;\n }\n\n async update(id: string, params: {\n name?: string;\n type?: string;\n config?: Record<string, unknown>;\n }): Promise<AgentResponse> {\n const { data, error } = await this.apiClient.PATCH(\"/api/v1/agents/{id}\", {\n params: { path: { id } },\n body: params as never,\n });\n if (error) this.handleApiError(`update agent \"${id}\"`, error);\n return data as unknown as AgentResponse;\n }\n\n async delete(id: string): Promise<{ id: string; name: string }> {\n const { data, error } = await this.apiClient.DELETE(\"/api/v1/agents/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`delete agent \"${id}\"`, error);\n return data as unknown as { id: string; name: string };\n }\n\n /**\n * Runs one turn of a connected agent through the relay: the platform\n * dispatches it to a live instance and answers with the function's output.\n */\n async call(id: string, body: AgentCallBody): Promise<AgentCallResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/v1/agents/{id}/call\", {\n params: { path: { id } },\n body,\n });\n if (error) this.handleApiError(`call agent \"${id}\"`, error);\n return data as AgentCallResponse;\n }\n\n /**\n * Runs one scripted scenario against an agent: the user sends \"ping\", the\n * agent answers, and the run succeeds when the answer arrives. The project\n * gains no scenario, run plan or test suite, and the answer carries the run\n * ids to follow.\n */\n async test(id: string): Promise<AgentTestRunResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/v1/agents/{id}/test\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`test agent \"${id}\"`, error);\n return data as AgentTestRunResponse;\n }\n}\n","import type { paths, components } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport type AnnotationResponse = components[\"schemas\"][\"Annotation\"];\n\nexport type CreateAnnotationBody = NonNullable<\n paths[\"/api/annotations/trace/{id}\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class AnnotationsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"AnnotationsApiError\";\n }\n}\n\nexport class AnnotationsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new AnnotationsApiError(message, operation, error);\n }\n\n async getAll(): Promise<AnnotationResponse[]> {\n const { data, error } = await this.apiClient.GET(\"/api/annotations\");\n if (error) this.handleApiError(\"fetch all annotations\", error);\n return data.data;\n }\n\n async get(id: string): Promise<AnnotationResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/annotations/{id}\", {\n params: { path: { id } },\n });\n if (error)\n this.handleApiError(`fetch annotation with ID \"${id}\"`, error);\n return data.data;\n }\n\n async getByTrace(traceId: string): Promise<AnnotationResponse[]> {\n const { data, error } = await this.apiClient.GET(\n \"/api/annotations/trace/{id}\",\n {\n params: { path: { id: traceId } },\n },\n );\n if (error)\n this.handleApiError(`fetch annotations for trace \"${traceId}\"`, error);\n return data.data;\n }\n\n async create(traceId: string, params: CreateAnnotationBody): Promise<AnnotationResponse> {\n const { data, error } = await this.apiClient.POST(\n \"/api/annotations/trace/{id}\",\n {\n params: { path: { id: traceId } },\n body: params,\n },\n );\n if (error) this.handleApiError(\"create annotation\", error);\n return data.data;\n }\n\n async delete(id: string): Promise<{ status?: string; message?: string }> {\n const { data, error } = await this.apiClient.DELETE(\n \"/api/annotations/{id}\",\n {\n params: { path: { id } },\n },\n );\n if (error)\n this.handleApiError(`delete annotation with ID \"${id}\"`, error);\n return data;\n }\n}\n","import {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport interface DashboardSummary {\n id: string;\n name: string;\n order: number;\n graphCount: number;\n createdAt: string;\n updatedAt: string;\n platformUrl?: string;\n}\n\nexport interface DashboardDetail {\n id: string;\n name: string;\n order: number;\n graphs: unknown[];\n createdAt: string;\n updatedAt: string;\n platformUrl?: string;\n}\n\nexport class DashboardsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"DashboardsApiError\";\n }\n}\n\nexport class DashboardsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new DashboardsApiError(message, operation, error);\n }\n\n async list(): Promise<{ data: DashboardSummary[] }> {\n const { data, error } = await this.apiClient.GET(\"/api/dashboards\");\n if (error) this.handleApiError(\"list dashboards\", error);\n return data as unknown as { data: DashboardSummary[] };\n }\n\n async get(id: string): Promise<DashboardDetail> {\n const { data, error } = await this.apiClient.GET(\"/api/dashboards/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`get dashboard \"${id}\"`, error);\n return data as unknown as DashboardDetail;\n }\n\n async create(params: { name: string }): Promise<DashboardDetail> {\n const { data, error } = await this.apiClient.POST(\"/api/dashboards\", {\n body: params,\n });\n if (error) this.handleApiError(\"create dashboard\", error);\n return data as unknown as DashboardDetail;\n }\n\n async rename(id: string, params: { name: string }): Promise<DashboardDetail> {\n const { data, error } = await this.apiClient.PATCH(\"/api/dashboards/{id}\", {\n params: { path: { id } },\n body: params,\n });\n if (error) this.handleApiError(`rename dashboard \"${id}\"`, error);\n return data as unknown as DashboardDetail;\n }\n\n async delete(id: string): Promise<{ id: string; name: string }> {\n const { data, error } = await this.apiClient.DELETE(\"/api/dashboards/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`delete dashboard \"${id}\"`, error);\n return data as unknown as { id: string; name: string };\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport type ModelProvidersListResponse =\n paths[\"/api/model-providers\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type UpdateModelProviderBody = NonNullable<\n paths[\"/api/model-providers/{provider}\"][\"put\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class ModelProvidersApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"ModelProvidersApiError\";\n }\n}\n\nexport class ModelProvidersApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new ModelProvidersApiError(message, operation, error);\n }\n\n async list(): Promise<ModelProvidersListResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/model-providers\");\n if (error) this.handleApiError(\"list model providers\", error);\n return data;\n }\n\n async set(provider: string, params: UpdateModelProviderBody): Promise<ModelProvidersListResponse> {\n const { data, error } = await this.apiClient.PUT(\n \"/api/model-providers/{provider}\",\n {\n params: { path: { provider } },\n body: params,\n },\n );\n if (error)\n this.handleApiError(`set model provider \"${provider}\"`, error);\n return data;\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport type AnalyticsTimeseriesBody = NonNullable<\n paths[\"/api/analytics/timeseries\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type AnalyticsTimeseriesResponse =\n paths[\"/api/analytics/timeseries\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class AnalyticsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"AnalyticsApiError\";\n }\n}\n\nexport class AnalyticsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new AnalyticsApiError(message, operation, error);\n }\n\n async timeseries(params: AnalyticsTimeseriesBody): Promise<AnalyticsTimeseriesResponse> {\n const { data, error } = await this.apiClient.POST(\n \"/api/analytics/timeseries\",\n { body: params },\n );\n if (error) this.handleApiError(\"query analytics\", error);\n return data;\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\n/** The typed columns/rows/statistics payload `POST /api/v1/query` answers with. */\nexport type QueryRunResult =\n paths[\"/api/v1/query\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** The queryable dataset/column catalog `GET /api/v1/query/schema` answers with. */\nexport type QuerySchemaResult =\n paths[\"/api/v1/query/schema\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * Both query languages, as `GET /api/v1/query/reference` describes them.\n *\n * A superset of {@link QuerySchemaResult} in one direction only: it embeds the\n * schema and adds the trace filter language, worked examples and the decision\n * table. Nothing here is tenant data — the values a field holds come from\n * `GET /api/traces/facets`.\n */\nexport type QueryReferenceResult =\n paths[\"/api/v1/query/reference\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * The body a query request sends.\n *\n * Derived from the generated request body so a contract change to what the\n * endpoint accepts breaks this at compile time rather than drifting silently.\n */\nexport type QueryRunParams = NonNullable<\n paths[\"/api/v1/query\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class QueryApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The HTTP status the platform answered with, when the response was kept.\n * Without it the CLI's error reader can only guess `network_error` for a\n * failure the platform named precisely — same rationale as `TracesApiError`.\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"QueryApiError\";\n }\n}\n\n/**\n * Typed client for the query doors (`POST /api/v1/query`,\n * `GET /api/v1/query/schema` and `GET /api/v1/query/reference`) — the same\n * governed query surface the workbench\n * and saved charts run through, exposed directly rather than only via a saved\n * chart's statement.\n *\n * Unlike the chart family, these routes carry no `projectId` in their path or\n * body — the generated operations declare `path?: never`. Project scope is\n * resolved once, when the underlying `LangwatchApiClient` is built\n * (`createLangWatchApiClient` bakes `scopedProjectId() ?? LANGWATCH_PROJECT_ID`\n * into that client's `Authorization`/`Basic` header at construction time —\n * see `src/internal/api/client.ts` and `src/internal/api/auth.ts`). A\n * per-call `projectId()` resolution the way `ChartsApiService` has one would\n * therefore be dead code here: there is no URL slot to put it in, and it\n * cannot retroactively change a header already baked into an existing\n * client. It would also wrongly force every caller to have a project in\n * scope — legacy `sk-lw-*` project keys carry their own project identity and\n * need none (see `buildAuthHeaders`). So this service takes the plain,\n * project-implicit config `AnalyticsApiService` does, and lets the api\n * client resolve the credential itself.\n */\nexport class QueryApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(\n operation: string,\n error: unknown,\n response?: Response,\n ): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation: operation,\n error,\n options: { status },\n });\n // A failure the platform NAMED (a code, a status, a meta bag) is raised as\n // the typed `LangWatchHandledError`, so the CLI's error output carries the\n // real code instead of degrading everything to `network_error`. The\n // canonical envelope arrives at the top level of the body here, exactly\n // where the shared reader looks — this family publishes the same error\n // shape as every other one.\n throwIfHandledError({ operation, error, response, message });\n throw new QueryApiError(message, operation, error, status);\n }\n\n /**\n * Runs one read-only LangWatchQL `SELECT` over the analytics views and\n * returns typed columns, rows, execution statistics, truncation state and\n * diagnostics, scoped to the caller's project.\n */\n async query(params: QueryRunParams): Promise<QueryRunResult> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/query\",\n { body: params },\n );\n if (error) this.handleApiError(\"run query\", error, response);\n return data;\n }\n\n /**\n * Lists the LangWatchQL analytics views this key may query, with each\n * column's type, description, the permissions that unlock it, and whether\n * this caller holds them — plus each dataset's grain, join keys,\n * partition-pruning time column, freshness and a runnable example query.\n */\n async schema(): Promise<QuerySchemaResult> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/query/schema\",\n {},\n );\n if (error) this.handleApiError(\"discover query schema\", error, response);\n return data;\n }\n\n /**\n * Describes both query languages in one payload: the LangWatchQL schema,\n * limits and endpoints, the trace filter's syntax and fields, worked examples\n * validated in both, and which language answers which kind of\n * question.\n */\n async reference(): Promise<QueryReferenceResult> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/query/reference\",\n {},\n );\n if (error) this.handleApiError(\"read query reference\", error, response);\n return data;\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport type TriggerResponse = NonNullable<\n paths[\"/api/triggers\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type CreateTriggerBody = NonNullable<\n paths[\"/api/triggers\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdateTriggerBody = NonNullable<\n paths[\"/api/triggers/{id}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type TriggerDeleteResponse =\n paths[\"/api/triggers/{id}\"][\"delete\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class TriggersApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"TriggersApiError\";\n }\n}\n\nexport class TriggersApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new TriggersApiError(message, operation, error);\n }\n\n async getAll(): Promise<TriggerResponse[]> {\n const { data, error } = await this.apiClient.GET(\"/api/triggers\");\n if (error) this.handleApiError(\"list triggers\", error);\n return data;\n }\n\n async get(id: string): Promise<TriggerResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/triggers/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`get trigger \"${id}\"`, error);\n return data;\n }\n\n async create(params: CreateTriggerBody): Promise<TriggerResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/triggers\", {\n body: params,\n });\n if (error) this.handleApiError(\"create trigger\", error);\n return data;\n }\n\n async update(id: string, params: UpdateTriggerBody): Promise<TriggerResponse> {\n const { data, error } = await this.apiClient.PATCH(\"/api/triggers/{id}\", {\n params: { path: { id } },\n body: params,\n });\n if (error) this.handleApiError(`update trigger \"${id}\"`, error);\n return data;\n }\n\n async delete(id: string): Promise<TriggerDeleteResponse> {\n const { data, error } = await this.apiClient.DELETE(\"/api/triggers/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`delete trigger \"${id}\"`, error);\n return data;\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport type GraphResponse = NonNullable<\n paths[\"/api/graphs\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type CreateGraphBody = NonNullable<\n paths[\"/api/graphs\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdateGraphBody = NonNullable<\n paths[\"/api/graphs/{id}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type GraphDeleteResponse =\n paths[\"/api/graphs/{id}\"][\"delete\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class GraphsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"GraphsApiError\";\n }\n}\n\nexport class GraphsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new GraphsApiError(message, operation, error);\n }\n\n async getAll(dashboardId?: string): Promise<GraphResponse[]> {\n const { data, error } = await this.apiClient.GET(\"/api/graphs\", {\n params: { query: { dashboardId } },\n });\n if (error) this.handleApiError(\"list graphs\", error);\n return data;\n }\n\n async get(id: string): Promise<GraphResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/graphs/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`get graph \"${id}\"`, error);\n return data;\n }\n\n async create(params: CreateGraphBody): Promise<GraphResponse> {\n const { data, error } = await this.apiClient.POST(\"/api/graphs\", {\n body: params,\n });\n if (error) this.handleApiError(\"create graph\", error);\n return data;\n }\n\n async update(id: string, params: UpdateGraphBody): Promise<GraphResponse> {\n const { data, error } = await this.apiClient.PATCH(\"/api/graphs/{id}\", {\n params: { path: { id } },\n body: params,\n });\n if (error) this.handleApiError(`update graph \"${id}\"`, error);\n return data;\n }\n\n async delete(id: string): Promise<GraphDeleteResponse> {\n const { data, error } = await this.apiClient.DELETE(\"/api/graphs/{id}\", {\n params: { path: { id } },\n });\n if (error) this.handleApiError(`delete graph \"${id}\"`, error);\n return data;\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\nimport {\n createLangWatchApiClient,\n type LangwatchApiClient,\n} from \"@/internal/api/client\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\n\nexport type SimulationRunsListResponse =\n paths[\"/api/simulation-runs\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type SimulationRunResponse =\n paths[\"/api/simulation-runs/{scenarioRunId}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * One evaluator's result on a finished run: its status, whether it was\n * required, and the verdict, score or reason it produced.\n */\nexport type SimulationRunEvaluation = NonNullable<\n NonNullable<SimulationRunResponse[\"results\"]>[\"evaluations\"]\n>[number];\n\nexport type SimulationRunsBatchesListResponse =\n paths[\"/api/simulation-runs/batches/list\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type SimulationRunsListParams = NonNullable<\n paths[\"/api/simulation-runs\"][\"get\"][\"parameters\"][\"query\"]\n>;\n\nexport type SimulationRunsBatchesListParams =\n paths[\"/api/simulation-runs/batches/list\"][\"get\"][\"parameters\"][\"query\"];\n\nexport class SimulationRunsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SimulationRunsApiError\";\n }\n}\n\nexport class SimulationRunsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown): never {\n const message = formatApiErrorForOperation({ operation: operation, error: error, options: {\n status: extractStatusFromResponse(error),\n } });\n throwIfHandledError({ operation, error, message });\n throw new SimulationRunsApiError(message, operation, error);\n }\n\n async getAll(params?: SimulationRunsListParams): Promise<SimulationRunsListResponse> {\n const { data, error } = await this.apiClient.GET(\"/api/simulation-runs\", {\n params: { query: params },\n });\n if (error) this.handleApiError(\"list simulation runs\", error);\n return data;\n }\n\n async get(scenarioRunId: string): Promise<SimulationRunResponse> {\n const { data, error } = await this.apiClient.GET(\n \"/api/simulation-runs/{scenarioRunId}\",\n {\n params: { path: { scenarioRunId } },\n },\n );\n if (error)\n this.handleApiError(`get simulation run \"${scenarioRunId}\"`, error);\n return data;\n }\n\n async listBatches(\n params: SimulationRunsBatchesListParams,\n ): Promise<SimulationRunsBatchesListResponse> {\n const { data, error } = await this.apiClient.GET(\n \"/api/simulation-runs/batches/list\",\n {\n params: { query: params },\n },\n );\n if (error) this.handleApiError(\"list simulation run batches\", error);\n return data;\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { buildAuthHeaders } from \"@/internal/api/auth\";\nimport { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport interface MonitorResponse {\n id: string;\n name: string;\n slug: string;\n checkType: string;\n enabled: boolean;\n executionMode: string;\n sample: number;\n level: string;\n evaluatorId: string | null;\n preconditions: unknown[];\n parameters: Record<string, unknown>;\n mappings: Record<string, unknown>;\n threadIdleTimeout: number | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateMonitorBody {\n name: string;\n checkType: string;\n executionMode?: string;\n sample?: number;\n level?: string;\n preconditions?: unknown[];\n parameters?: Record<string, unknown>;\n mappings?: Record<string, unknown>;\n}\n\nexport interface UpdateMonitorBody {\n name?: string;\n enabled?: boolean;\n executionMode?: string;\n sample?: number;\n preconditions?: unknown[];\n parameters?: Record<string, unknown>;\n mappings?: Record<string, unknown>;\n}\n\nexport interface MonitorDeleteResponse {\n id: string;\n deleted: boolean;\n}\n\nexport class MonitorsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"MonitorsApiError\";\n }\n}\n\nexport class MonitorsApiService {\n private readonly apiKey: string;\n private readonly endpoint: string;\n\n constructor(config?: { apiKey?: string; endpoint?: string }) {\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n this.endpoint = resolveEndpoint(config?.endpoint);\n }\n\n private async request<T>(path: string, options?: RequestInit): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...options,\n headers: {\n ...buildAuthHeaders({ apiKey: this.apiKey }),\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let parsed: unknown = errorText;\n try {\n parsed = JSON.parse(errorText);\n } catch {\n // leave as raw text\n }\n const message = formatApiErrorMessage({ error: parsed, options: { status: response.status } });\n throwIfHandledError({\n operation: options?.method ?? \"GET\",\n error: parsed,\n status: response.status,\n message: `HTTP ${response.status}: ${message}`,\n });\n throw new MonitorsApiError(\n `HTTP ${response.status}: ${message}`,\n options?.method ?? \"GET\",\n parsed,\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n async getAll(): Promise<MonitorResponse[]> {\n return this.request<MonitorResponse[]>(\"/api/monitors\");\n }\n\n async get(id: string): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(`/api/monitors/${encodeURIComponent(id)}`);\n }\n\n async create(body: CreateMonitorBody): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(\"/api/monitors\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n }\n\n async update(id: string, body: UpdateMonitorBody): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(`/api/monitors/${encodeURIComponent(id)}`, {\n method: \"PATCH\",\n body: JSON.stringify(body),\n });\n }\n\n async toggle(id: string, enabled: boolean): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(`/api/monitors/${encodeURIComponent(id)}/toggle`, {\n method: \"POST\",\n body: JSON.stringify({ enabled }),\n });\n }\n\n async delete(id: string): Promise<MonitorDeleteResponse> {\n return this.request<MonitorDeleteResponse>(`/api/monitors/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n });\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { buildAuthHeaders } from \"@/internal/api/auth\";\nimport { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport interface SecretResponse {\n id: string;\n projectId: string;\n name: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SecretDeleteResponse {\n id: string;\n deleted: boolean;\n}\n\nexport class SecretsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SecretsApiError\";\n }\n}\n\nexport class SecretsApiService {\n private readonly apiKey: string;\n private readonly endpoint: string;\n\n constructor(config?: { apiKey?: string; endpoint?: string }) {\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n this.endpoint = resolveEndpoint(config?.endpoint);\n }\n\n private async request<T>(path: string, options?: RequestInit): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...options,\n headers: {\n ...buildAuthHeaders({ apiKey: this.apiKey }),\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let parsed: unknown = errorText;\n try {\n parsed = JSON.parse(errorText);\n } catch {\n // leave as raw text\n }\n const message = formatApiErrorMessage({ error: parsed, options: { status: response.status } });\n throwIfHandledError({\n operation: options?.method ?? \"GET\",\n error: parsed,\n status: response.status,\n message: `HTTP ${response.status}: ${message}`,\n });\n throw new SecretsApiError(\n `HTTP ${response.status}: ${message}`,\n options?.method ?? \"GET\",\n parsed,\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n async getAll(): Promise<SecretResponse[]> {\n return this.request<SecretResponse[]>(\"/api/secrets\");\n }\n\n async get(id: string): Promise<SecretResponse> {\n return this.request<SecretResponse>(`/api/secrets/${encodeURIComponent(id)}`);\n }\n\n async create(body: { name: string; value: string }): Promise<SecretResponse> {\n return this.request<SecretResponse>(\"/api/secrets\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n }\n\n async update(id: string, body: { value: string }): Promise<SecretResponse> {\n return this.request<SecretResponse>(`/api/secrets/${encodeURIComponent(id)}`, {\n method: \"PUT\",\n body: JSON.stringify(body),\n });\n }\n\n async delete(id: string): Promise<SecretDeleteResponse> {\n return this.request<SecretDeleteResponse>(`/api/secrets/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n });\n }\n}\n","/**\n * The walk that turns a cursor-paginated endpoint back into a whole listing.\n *\n * The gateway platform lists serve one page plus a `next_cursor` that is null\n * exactly when the walk is exhausted. Two things about that contract are easy\n * to get wrong in a hand-rolled loop, and both lose rows silently:\n *\n * - A FULL page does not mean there is more, and a SHORT page does not mean\n * there is no more. `/virtual-keys` filters each page for visibility after\n * reading it, so a page of 3 can still be followed by a page of 200. The\n * null cursor is the only end-of-walk signal.\n * - A cursor the endpoint did not issue answers 400 `invalid_cursor` rather\n * than restarting the walk, so cursors must be passed back verbatim.\n *\n * THE PAGINATION VOCABULARY every cursor-paged service on this SDK uses, so a\n * method name tells you how much it reads before it answers:\n *\n * - `listPage()` / `<noun>Page()` returns EXACTLY ONE page plus `next_cursor`.\n * - `list()` returns the COMPLETE collection, walking internally. Offered only\n * for collections that are bounded in practice.\n * - `iterate()` / `iter<Noun>()` is a lazy async iterator over every row.\n * Offered for every cursor-paged collection.\n *\n * A service with one listable collection names its iterator `iterate()`; a\n * service with more than one names each after the rows it yields.\n */\n\n/**\n * The page size a walk asks for. The wire caps `limit` at 200 and defaults to\n * 50; a complete walk is four times fewer round trips at the cap.\n */\nexport const CURSOR_WALK_PAGE_SIZE = 200;\n\n/**\n * More pages than any real listing has, and therefore evidence of a cursor\n * chain that never ends. Raising beats stopping here: a silent stop is the\n * truncation the walk exists to prevent.\n */\nexport const MAX_CURSOR_WALK_PAGES = 1000;\n\nexport interface CursorWalkOptions<TPage> {\n /** Reads one page. `undefined` asks for the first. */\n fetchPage: (cursor: string | undefined) => Promise<TPage>;\n /** Absent and null both mean the walk is exhausted. */\n nextCursorOf: (page: TPage) => string | null | undefined;\n /** Resumes an interrupted walk instead of starting from the first row. */\n startCursor?: string;\n /** Raised when the endpoint's cursor chain never reaches an end. */\n onEndlessWalk: (reason: string) => Error;\n}\n\n/**\n * The one walk: yields each page as it arrives, so a caller can stop early\n * without paying for the rest, and raises rather than looping forever on a\n * cursor chain that never ends.\n *\n * Every eager `list()` and every lazy `iterate()` in the SDK is built on this,\n * so the end-of-walk contract and the endless-walk guards are written once.\n */\nexport async function* walkCursorPages<TPage>(\n options: CursorWalkOptions<TPage>,\n): AsyncGenerator<TPage> {\n const served = new Set<string>();\n let cursor = options.startCursor;\n let pagesRead = 0;\n\n for (;;) {\n const page = await options.fetchPage(cursor);\n pagesRead += 1;\n yield page;\n\n // A server that predates cursor pagination on this route sends no\n // `next_cursor` at all; absent reads the same as exhausted.\n const next = options.nextCursorOf(page) ?? null;\n if (next === null) return;\n\n // A cursor served twice walks the same page forever.\n if (served.has(next)) {\n throw options.onEndlessWalk(\n \"the endpoint served the same page cursor twice, so the walk cannot advance\",\n );\n }\n if (pagesRead >= MAX_CURSOR_WALK_PAGES) {\n throw options.onEndlessWalk(\n `the walk passed ${MAX_CURSOR_WALK_PAGES} pages without the cursor coming back null`,\n );\n }\n\n served.add(next);\n cursor = next;\n }\n}\n\n/**\n * Read every page of a cursor-paginated list, in order.\n *\n * Returns the pages rather than the rows so the caller can fold whatever else\n * rides on each page (a `spend_available` flag, say) instead of losing it.\n */\nexport async function collectCursorPages<TPage>(\n options: CursorWalkOptions<TPage>,\n): Promise<TPage[]> {\n const pages: TPage[] = [];\n for await (const page of walkCursorPages(options)) {\n pages.push(page);\n }\n return pages;\n}\n","/**\n * The per-call options every mutating call on the billing surfaces takes.\n *\n * WHERE THEY GO. A call whose arguments already ride in an options bag\n * (`disable(id, { reason })`, `reset(id, { endUserId })`) gains these fields in\n * that same bag: one bag per call is what a caller expects. A call that takes a\n * request BODY (`create(input)`, `update(id, input)`) gets a separate trailing\n * parameter instead, because those body types mirror the wire verbatim and must\n * not grow keys the wire has never heard of.\n *\n * They are an interface rather than a bare `signal` so that the next thing a\n * call needs per invocation is an added field rather than an added parameter.\n */\n\n/** The request header the control plane deduplicates creates on. */\nexport const IDEMPOTENCY_KEY_HEADER = \"Idempotency-Key\";\n\n/**\n * The response header a replayed create carries. Only ever `\"true\"`, and\n * ABSENT rather than false on a first execution, so its presence is the whole\n * signal.\n */\nexport const IDEMPOTENT_REPLAY_HEADER = \"X-Idempotent-Replay\";\n\nexport interface MutationOptions {\n /**\n * Cancel the call. Without one, the request is bounded by the SDK's own\n * 30 second timeout so a hung control plane fails rather than freezes.\n */\n signal?: AbortSignal;\n}\n\nexport interface IdempotentCreateOptions extends MutationOptions {\n /**\n * Makes the create safe to retry. A dropped connection after the write but\n * before the response looks exactly like a dropped request, and sending it\n * again without a key mints a SECOND resource. Send the same key on the\n * retry and the server answers with the first response instead, byte for\n * byte, including the one-time secret a create hands back.\n *\n * Any string of 8 to 255 characters; a UUID minted per logical create is the\n * usual choice. Receipts answer for 24 hours, and only successful creates\n * leave one, so a create that failed is safe to run again either way.\n *\n * Reusing a key with a DIFFERENT body is refused with `idempotency_error`\n * rather than quietly answering for the wrong request.\n */\n idempotencyKey?: string;\n /**\n * Called when the response came from a receipt rather than a fresh write,\n * i.e. this exact create had already succeeded.\n *\n * A hook rather than a field on the returned resource: the resource is\n * identical either way, so nothing about handling it changes, and the\n * distinction is something a caller logs rather than branches on. Keeping it\n * off the return type also keeps the wire-shaped entities free of fields the\n * wire does not have.\n */\n onIdempotentReplay?: () => void;\n}\n\n/** What a service's `request()` takes beyond a plain `RequestInit`. */\nexport interface ObservedRequestInit extends RequestInit {\n /**\n * Reads the raw response before its body is parsed. Only headers that the\n * caller asked about are read here; a service never keeps the response.\n */\n onResponse?: (response: Response) => void;\n}\n\n/** Per-call plumbing for a mutating call that is not an idempotent create. */\nexport function mutationInit(options?: MutationOptions): ObservedRequestInit {\n return options?.signal ? { signal: options.signal } : {};\n}\n\n/**\n * Per-call plumbing for a create the server will deduplicate: the key on the\n * way out, and the replay verdict on the way back.\n */\nexport function idempotentCreateInit(\n options?: IdempotentCreateOptions,\n): ObservedRequestInit {\n const onIdempotentReplay = options?.onIdempotentReplay;\n return {\n ...mutationInit(options),\n ...(options?.idempotencyKey !== undefined\n ? { headers: { [IDEMPOTENCY_KEY_HEADER]: options.idempotencyKey } }\n : {}),\n ...(onIdempotentReplay\n ? {\n onResponse: (response: Response) => {\n if (response.headers.get(IDEMPOTENT_REPLAY_HEADER) === \"true\") {\n onIdempotentReplay();\n }\n },\n }\n : {}),\n };\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport {\n CURSOR_WALK_PAGE_SIZE,\n collectCursorPages,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport {\n idempotentCreateInit,\n mutationInit,\n type IdempotentCreateOptions,\n type MutationOptions,\n type ObservedRequestInit,\n} from \"@/client-sdk/services/_shared/mutation-options\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport type VirtualKeyScopeType = \"organization\" | \"team\" | \"project\";\n\nexport interface VirtualKeyScope {\n scope_type: VirtualKeyScopeType;\n scope_id: string;\n}\n\nexport type VirtualKeyRoutingMode = \"none\" | \"fallback_all\" | \"policy\";\n\n/**\n * The snake DTO the server returns (`toVirtualKeySnakeDto`). The token\n * format is `vk-lw-<ulid>` with no live/test discriminator; the gateway\n * never branches on environment, so there is no env field.\n */\nexport interface VirtualKey {\n id: string;\n organization_id: string;\n name: string;\n description: string | null;\n /** `disabled` is the reversible stop; `revoked` is terminal. */\n status: \"active\" | \"disabled\" | \"revoked\";\n /** \"langy\" marks a product-managed key; customers can only mint \"user\". */\n purpose: \"user\" | \"langy\";\n /** e.g. \"vk-lw-01HZX9\" — the only secret material kept after creation. */\n display_prefix: string;\n principal_user_id: string | null;\n /**\n * Where this key's traces and costs land. Not a scope: it grants no\n * access to the key. Decided when the key is written and stored on it,\n * so editing what the key is scoped to never moves it. Null only on a\n * key created before this was stored, in an organization that had no\n * governance project to fall back to.\n */\n trace_project_id: string | null;\n /**\n * True when the project in `trace_project_id` has been deleted. The key\n * goes on sending its traces there, so the data stays whole and\n * reappears if the project is restored, and traffic is never refused.\n */\n trace_project_archived: boolean;\n scopes: VirtualKeyScope[];\n routing_policy_id: string | null;\n routing_mode: VirtualKeyRoutingMode;\n config: Record<string, unknown>;\n revision: string;\n created_at: string;\n updated_at: string;\n last_used_at: string | null;\n revoked_at: string | null;\n /**\n * When the key stops serving, or null for a key that never expires.\n * Requests presented after this moment are refused with\n * `virtual_key_expired`. `status` stays \"active\" past the date, so read\n * this field rather than the status to tell an expired key apart.\n */\n expires_at: string | null;\n}\n\n/**\n * The cap a key carries on itself, created atomically with the key. Only\n * the calendar windows a person reasons about; string amounts survive\n * JSON round-trips without float drift (numbers are stringified).\n */\nexport interface VirtualKeyBudgetInput {\n limit_usd: string | number;\n window: \"day\" | \"week\" | \"month\";\n on_breach?: \"block\" | \"warn\";\n name?: string;\n}\n\nexport interface CreateVirtualKeyInput {\n name: string;\n description?: string;\n principal_user_id?: string | null;\n /** Defaults to the caller's project when omitted. */\n scopes?: VirtualKeyScope[];\n /**\n * Explicit trace destination for org- and team-owned keys; requires\n * `virtualKeys:manage` on that project. NOT a scope.\n */\n trace_project_id?: string | null;\n routing_policy_id?: string | null;\n routing_mode?: VirtualKeyRoutingMode;\n /**\n * When the key stops serving, as an ISO 8601 instant. Omit it and the key\n * never expires. A date already in the past is refused.\n */\n expires_at?: string;\n /** Optional cap created atomically with the key. */\n budget?: VirtualKeyBudgetInput | null;\n config?: Record<string, unknown>;\n /**\n * Your own identifier for this key, unique within the organization. Lets\n * you look the key up by the id your system already has instead of storing\n * ours alongside it.\n */\n external_id?: string | null;\n /**\n * Free-form string labels, up to 40 of them. Sent WHOLE on an update: the\n * map you pass replaces the stored one rather than merging into it, and\n * `{}` clears it.\n */\n metadata?: Record<string, string>;\n /**\n * Withhold the secret from the response and get a one-time reveal id\n * instead. The secret is parked for 24 hours and served once, to the\n * person the key is for, through the LangWatch app; the caller never\n * holds it.\n */\n reveal_once?: boolean;\n}\n\nexport interface UpdateVirtualKeyInput {\n name?: string;\n description?: string | null;\n scopes?: VirtualKeyScope[];\n trace_project_id?: string | null;\n routing_policy_id?: string | null;\n routing_mode?: VirtualKeyRoutingMode;\n /**\n * Undefined leaves the expiration alone; null clears it, so the key never\n * expires; an ISO 8601 instant moves it. An expired key accepts this edit\n * like any other, which is how it goes back into service.\n */\n expires_at?: string | null;\n /** Undefined leaves the cap alone; a value upserts it; null archives it. */\n budget?: VirtualKeyBudgetInput | null;\n config?: Record<string, unknown>;\n /**\n * Your own identifier for this key, unique within the organization. Lets\n * you look the key up by the id your system already has instead of storing\n * ours alongside it.\n */\n external_id?: string | null;\n /**\n * Free-form string labels, up to 40 of them. Sent WHOLE on an update: the\n * map you pass replaces the stored one rather than merging into it, and\n * `{}` clears it.\n */\n metadata?: Record<string, string>;\n}\n\nexport interface VirtualKeyWithSecret {\n virtual_key: VirtualKey;\n secret: string;\n}\n\n/** What a create with `reveal_once` answers: the id that reads the secret once. */\nexport interface VirtualKeyWithReveal {\n virtual_key: VirtualKey;\n reveal_id: string;\n /** The display prefix, safe to show in place of the secret. */\n preview: string;\n}\n\n/** One page of the virtual-key listing, exactly as the wire serves it. */\nexport interface VirtualKeyPage {\n data: VirtualKey[];\n /**\n * Pass back as `cursor` for the next page. Null means the walk is\n * exhausted. Neither page length tells you anything here: visibility is\n * applied to each page AFTER it is read, so a page can hold fewer rows\n * than `limit` with more still to come.\n */\n next_cursor: string | null;\n}\n\n/** Aggregate spend for one key over a window, from the cost path. */\nexport interface VirtualKeySpendSummary {\n virtual_key_id: string;\n spent_usd: string;\n requests: number;\n /** Epoch milliseconds, the unit every spend surface takes and returns. */\n window: { from: number; to: number };\n}\n\nexport class VirtualKeysApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"VirtualKeysApiError\";\n }\n}\n\n/**\n * Client for the gateway virtual-key surface (/api/gateway/v1).\n *\n * Entity types and the create/update bodies mirror the wire verbatim, so\n * their fields are lowercase snake_case. Call options this SDK invents (query\n * filters, per-call behaviour, action arguments) are camelCase like the rest\n * of the SDK.\n */\nexport class VirtualKeysApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly projectId: string | undefined;\n\n constructor(config?: { endpoint?: string; apiKey?: string; projectId?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n this.projectId = config?.projectId ?? process.env.LANGWATCH_PROJECT_ID;\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n // Org-anchored API keys carry no project of their own; the surface\n // scopes on this header. Absent for project keys, which self-scope.\n ...(this.projectId ? { \"X-Project-Id\": this.projectId } : {}),\n };\n }\n\n private async request<T>(\n operation: string,\n path: string,\n init?: ObservedRequestInit,\n ): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: { ...this.headers(), ...(init?.headers ?? {}) },\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new VirtualKeysApiError(message, operation, parsedBody);\n }\n init?.onResponse?.(response);\n return (await response.json()) as T;\n }\n\n /**\n * ONE page of the virtual keys visible to the caller, newest first. Pass\n * `next_cursor` back as `cursor` for the next page, verbatim: a cursor this\n * endpoint did not issue answers 400 rather than restarting the walk.\n *\n * `limit` is the page size (server default 50, capped at 200), and it caps\n * the rows READ, not the rows returned: the visibility filter runs on the\n * page afterwards. Prefer `list()` unless you mean to page deliberately.\n */\n async listPage(options?: {\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<VirtualKeyPage> {\n const params = new URLSearchParams();\n if (options?.externalId) params.set(\"external_id\", options.externalId);\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const query = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, next_cursor } = await this.request<{\n data: VirtualKey[];\n next_cursor?: string | null;\n }>(\"list virtual keys\", `/api/gateway/v1/virtual-keys${query}`);\n return { data, next_cursor: next_cursor ?? null };\n }\n\n /**\n * Every virtual key visible to the caller: keys scoped to this project, to\n * its team, or to the whole organization.\n *\n * The endpoint pages; this follows `next_cursor` until it comes back null.\n * Stopping on a short page would be wrong here specifically, because the\n * server filters each page for visibility after reading it, so a page can\n * hold fewer rows than the limit with more still to come.\n *\n * `limit` sizes each request in the walk, it does NOT cap what comes back.\n * `cursor` resumes an interrupted walk. Take a single page with\n * `listPage()`, or stream the walk with `iterate()`.\n */\n async list(options?: {\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<VirtualKey[]> {\n const pages = await collectCursorPages<VirtualKeyPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new VirtualKeysApiError(\n `Failed to list virtual keys: ${reason}.`,\n \"list virtual keys\",\n ),\n fetchPage: (cursor) =>\n this.listPage({\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n return pages.flatMap((page) => page.data);\n }\n\n /**\n * Every visible virtual key, one row at a time, fetching each page only\n * when the consumer reaches it. Stop early and the rest is never read,\n * which `list()` cannot offer because it materialises the whole listing\n * first. Raises rather than looping forever on a cursor chain that never\n * ends, exactly like `list()`.\n */\n async *iterate(options?: {\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): AsyncGenerator<VirtualKey> {\n const pages = walkCursorPages<VirtualKeyPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new VirtualKeysApiError(\n `Failed to list virtual keys: ${reason}.`,\n \"list virtual keys\",\n ),\n fetchPage: (cursor) =>\n this.listPage({\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n async get(id: string): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `get virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}`,\n );\n return virtual_key;\n }\n\n /**\n * Mint a key. The response carries the secret ONCE; nothing ever serves it\n * again, so a create that times out is recovered with `idempotencyKey`\n * rather than by listing. With `reveal_once` the response carries a reveal\n * id in place of the secret.\n */\n async create(\n input: CreateVirtualKeyInput & { reveal_once: true },\n options?: IdempotentCreateOptions,\n ): Promise<VirtualKeyWithReveal>;\n async create(\n input: CreateVirtualKeyInput,\n options?: IdempotentCreateOptions,\n ): Promise<VirtualKeyWithSecret>;\n async create(\n input: CreateVirtualKeyInput,\n options?: IdempotentCreateOptions,\n ): Promise<VirtualKeyWithSecret | VirtualKeyWithReveal> {\n return this.request<VirtualKeyWithSecret | VirtualKeyWithReveal>(\n \"create virtual key\",\n \"/api/gateway/v1/virtual-keys\",\n {\n method: \"POST\",\n body: JSON.stringify(input),\n ...idempotentCreateInit(options),\n },\n );\n }\n\n async update(\n id: string,\n input: UpdateVirtualKeyInput,\n options?: MutationOptions,\n ): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `update virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input), ...mutationInit(options) },\n );\n return virtual_key;\n }\n\n async rotate(\n id: string,\n options?: MutationOptions,\n ): Promise<VirtualKeyWithSecret> {\n return this.request<VirtualKeyWithSecret>(\n `rotate virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/rotate`,\n { method: \"POST\", ...mutationInit(options) },\n );\n }\n\n async revoke(id: string, options?: MutationOptions): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `revoke virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/revoke`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return virtual_key;\n }\n\n /** Reversible stop; enable() restores the key exactly as it was. */\n async disable(\n id: string,\n options: { reason?: string } & MutationOptions = {},\n ): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `disable virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/disable`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(options.reason ? { reason: options.reason } : {}),\n ...mutationInit(options),\n },\n );\n return virtual_key;\n }\n\n async enable(id: string, options?: MutationOptions): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `enable virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/enable`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return virtual_key;\n }\n\n /**\n * Aggregate spend for one key over a window in epoch milliseconds.\n * Defaults to the current UTC calendar month server-side. Reads the same\n * cost path the dashboard reads, so this number and the UI agree by\n * construction.\n */\n async spend(\n id: string,\n options?: { from?: number; to?: number },\n ): Promise<VirtualKeySpendSummary> {\n const params = new URLSearchParams();\n if (options?.from !== undefined) params.set(\"from\", String(options.from));\n if (options?.to !== undefined) params.set(\"to\", String(options.to));\n const query = params.size > 0 ? `?${params.toString()}` : \"\";\n return this.request<VirtualKeySpendSummary>(\n `read virtual key spend \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/spend${query}`,\n );\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport {\n CURSOR_WALK_PAGE_SIZE,\n collectCursorPages,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport {\n idempotentCreateInit,\n mutationInit,\n type IdempotentCreateOptions,\n type MutationOptions,\n type ObservedRequestInit,\n} from \"@/client-sdk/services/_shared/mutation-options\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport type BudgetScopeKind =\n | \"organization\"\n | \"team\"\n | \"project\"\n | \"virtual_key\"\n | \"principal\"\n | \"group\"\n | \"attributed_user\";\n\nexport type BudgetWindow = \"minute\" | \"hour\" | \"day\" | \"week\" | \"month\" | \"total\" | \"manual\";\nexport type BudgetOnBreach = \"block\" | \"warn\";\n\nexport interface GatewayBudget {\n id: string;\n organization_id: string;\n scope_type: BudgetScopeKind;\n scope_id: string;\n name: string;\n description: string | null;\n window: BudgetWindow;\n on_breach: BudgetOnBreach;\n /**\n * For `group` rows this is the PER-MEMBER allowance, not a group total;\n * `spent_usd` sums the whole group and `member_count` says how many\n * members the allowance currently covers. For `attributed_user` rows it is\n * the PER-PERSON cap, and `end_users_seen` / `end_users_over` carry the\n * standing instead of `spent_usd`.\n */\n limit_usd: string;\n /** Canonical integer limit, nano-USD. Null past the safe integer range. */\n limit_nano_usd: number | null;\n /**\n * Display value. NULL when `spend_available` is false: spend could not be\n * totalled, so there is no figure, and the API sends null rather than a\n * stale one a caller could mistake for real money.\n */\n spent_usd: string | null;\n /** Canonical integer spend, nano-USD. Null whenever `spent_usd` is. */\n spent_nano_usd: number | null;\n timezone: string | null;\n /** ModelProvider id the budget counts; null counts every provider. */\n provider_key: string | null;\n current_period_started_at: string;\n resets_at: string;\n /** Instant the cycle is phased from; null means calendar aligned. */\n cycle_anchor_at: string | null;\n last_reset_at: string | null;\n archived_at: string | null;\n created_at: string;\n /** `group` rows only. */\n member_count?: number;\n /** `attributed_user` rows only: end users with spend this period. */\n end_users_seen?: number;\n /** `attributed_user` rows only: how many of those are at or over the cap. */\n end_users_over?: number;\n}\n\n/**\n * One page of the budget listing, exactly as the wire serves it.\n *\n * Budgets come back in an envelope where virtual keys come back as a bare\n * array because `spend_available` is a correctness flag about the whole page,\n * and an array cannot carry it.\n */\nexport interface GatewayBudgetPage {\n data: GatewayBudget[];\n /**\n * False when spend could not be totalled: render \"unavailable\" rather\n * than trusting `spent_usd` as real spend.\n */\n spend_available: boolean;\n /**\n * Pass back as `cursor` for the next page. Null means the walk is\n * exhausted; a FULL page does not by itself mean there is more.\n */\n next_cursor: string | null;\n}\n\nexport type CreateGatewayBudgetScope =\n | { kind: \"organization\"; organization_id: string }\n | { kind: \"team\"; team_id: string }\n | { kind: \"project\"; project_id: string }\n | { kind: \"virtual_key\"; virtual_key_id: string }\n | { kind: \"principal\"; principal_user_id: string }\n | { kind: \"group\"; group_id: string }\n // Template: each distinct external end user on the anchor gets the\n // budget's limit per window. Exactly one anchor id.\n | {\n kind: \"attributed_user\";\n anchor_virtual_key_id?: string;\n anchor_project_id?: string;\n };\n\nexport interface CreateGatewayBudgetInput {\n scope: CreateGatewayBudgetScope;\n name: string;\n description?: string;\n window: BudgetWindow;\n limit_usd: number | string;\n on_breach?: BudgetOnBreach;\n timezone?: string | null;\n /** ModelProvider id to pin the budget to one provider. */\n provider_key?: string | null;\n /**\n * RFC3339 instant that phases the budget's cycle instead of the calendar:\n * a `month` budget anchored `2026-01-17T09:00:00Z` rolls every 17th at\n * 09:00 UTC. Omit for calendar alignment. Immutable once created, and\n * rejected on the windows that never cycle (`total`, `manual`).\n */\n cycle_anchor_at?: string;\n /**\n * Your own identifier for this budget. Lets you look it up by the id your\n * system already has instead of storing ours alongside it.\n */\n external_id?: string | null;\n /**\n * Free-form string labels, up to 40 of them. Sent WHOLE on an update: the\n * map you pass replaces the stored one rather than merging into it, and\n * `{}` clears it.\n */\n metadata?: Record<string, string>;\n}\n\nexport interface UpdateGatewayBudgetInput {\n name?: string;\n description?: string | null;\n limit_usd?: number | string;\n on_breach?: BudgetOnBreach;\n timezone?: string | null;\n}\n\nexport class GatewayBudgetsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"GatewayBudgetsApiError\";\n }\n}\n\n/**\n * Client for the gateway budget surface (/api/gateway/v1).\n *\n * Entity types and the create/update bodies mirror the wire verbatim, so\n * their fields are lowercase snake_case. Call options this SDK invents (query\n * filters, per-call behaviour, action arguments) are camelCase like the rest\n * of the SDK.\n */\nexport class GatewayBudgetsApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly projectId: string | undefined;\n\n constructor(config?: { endpoint?: string; apiKey?: string; projectId?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n this.projectId = config?.projectId ?? process.env.LANGWATCH_PROJECT_ID;\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n // Org-anchored API keys carry no project of their own; the surface\n // scopes on this header. Absent for project keys, which self-scope.\n ...(this.projectId ? { \"X-Project-Id\": this.projectId } : {}),\n };\n }\n\n private async request<T>(\n operation: string,\n path: string,\n init?: ObservedRequestInit,\n ): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: { ...this.headers(), ...(init?.headers ?? {}) },\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new GatewayBudgetsApiError(message, operation, parsedBody);\n }\n init?.onResponse?.(response);\n return (await response.json()) as T;\n }\n\n /**\n * ONE page of non-archived budgets, exactly as the wire serves it. Pass\n * `next_cursor` back as `cursor` for the next page, verbatim: a cursor this\n * endpoint did not issue answers 400 rather than restarting the walk.\n *\n * `limit` is the page size (server default 50, capped at 200). Prefer\n * `list()` unless you mean to page deliberately: a full page is not a\n * promise of more, and a null `next_cursor` is the only end of the walk.\n */\n async listPage(options?: {\n scopeTypes?: BudgetScopeKind[];\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<GatewayBudgetPage> {\n const params = new URLSearchParams();\n if (options?.externalId) params.set(\"external_id\", options.externalId);\n if (options?.scopeTypes?.length) {\n params.set(\"scope_type\", options.scopeTypes.join(\",\"));\n }\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const query = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, spend_available, next_cursor } = await this.request<{\n data: GatewayBudget[];\n spend_available: boolean;\n next_cursor?: string | null;\n }>(\"list gateway budgets\", `/api/gateway/v1/budgets${query}`);\n return {\n data,\n spend_available,\n next_cursor: next_cursor ?? null,\n };\n }\n\n /**\n * Every non-archived budget in the organization across all seven scope\n * types, optionally filtered by `scopeTypes`.\n *\n * The endpoint pages; this follows `next_cursor` until it comes back null,\n * so the result is the complete listing and carries no cursor of its own.\n * Callers that count, total, or decide an all-clear on this list need that\n * completeness for correctness, not just for display.\n *\n * `limit` sizes each request in the walk, it does NOT cap what comes back.\n * `cursor` resumes an interrupted walk. Take a single page with\n * `listPage()`, or stream the walk with `iterate()`.\n *\n * A plain array, like every other exhaustive `list()` in the SDK: a walk\n * that ran to the end has no cursor left to report. Null `spent_usd` /\n * `spent_nano_usd` on a row is not by itself \"spend unavailable\": an\n * `attributed_user` template row serves null deliberately, because one\n * allowance per person has no single total. Use `listPage()` when you need\n * `spend_available` stated outright.\n */\n async list(options?: {\n scopeTypes?: BudgetScopeKind[];\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<GatewayBudget[]> {\n const pages = await collectCursorPages<GatewayBudgetPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new GatewayBudgetsApiError(\n `Failed to list gateway budgets: ${reason}.`,\n \"list gateway budgets\",\n ),\n fetchPage: (cursor) =>\n this.listPage({\n scopeTypes: options?.scopeTypes,\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n return pages.flatMap((page) => page.data);\n }\n\n /**\n * Every non-archived budget, one row at a time, fetching each page only\n * when the consumer reaches it.\n *\n * A null `spent_usd` means spend could not be totalled rather than that\n * nothing was spent.\n */\n async *iterate(options?: {\n scopeTypes?: BudgetScopeKind[];\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): AsyncGenerator<GatewayBudget> {\n const pages = walkCursorPages<GatewayBudgetPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new GatewayBudgetsApiError(\n `Failed to list gateway budgets: ${reason}.`,\n \"list gateway budgets\",\n ),\n fetchPage: (cursor) =>\n this.listPage({\n scopeTypes: options?.scopeTypes,\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /**\n * One budget by id, in the same row shape the listing serves.\n *\n * Archived budgets are not served, so a budget that existed yesterday can\n * answer 404 today. A null `spent_usd` means spend could not be totalled\n * rather than that nothing was spent, which is the same signal the listing\n * carries as `spend_available`.\n */\n async get(id: string): Promise<GatewayBudget> {\n const { budget } = await this.request<{\n budget: GatewayBudget;\n spend_available: boolean;\n }>(\n `get gateway budget \"${id}\"`,\n `/api/gateway/v1/budgets/${encodeURIComponent(id)}`,\n );\n return budget;\n }\n\n async create(\n input: CreateGatewayBudgetInput,\n options?: IdempotentCreateOptions,\n ): Promise<GatewayBudget> {\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n \"create gateway budget\",\n \"/api/gateway/v1/budgets\",\n {\n method: \"POST\",\n body: JSON.stringify(input),\n ...idempotentCreateInit(options),\n },\n );\n return budget;\n }\n\n async update(\n id: string,\n input: UpdateGatewayBudgetInput,\n options?: MutationOptions,\n ): Promise<GatewayBudget> {\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n `update gateway budget \"${id}\"`,\n `/api/gateway/v1/budgets/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input), ...mutationInit(options) },\n );\n return budget;\n }\n\n async archive(\n id: string,\n options?: MutationOptions,\n ): Promise<GatewayBudget> {\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n `archive gateway budget \"${id}\"`,\n `/api/gateway/v1/budgets/${encodeURIComponent(id)}`,\n { method: \"DELETE\", ...mutationInit(options) },\n );\n return budget;\n }\n\n /**\n * Move the budget's period boundary to now. Recorded spend is never\n * mutated; with `endUserId` only that end-user bucket's boundary moves.\n */\n async reset(\n id: string,\n options: { endUserId?: string; reason?: string } & MutationOptions = {},\n ): Promise<GatewayBudget> {\n const query = options.endUserId\n ? `?end_user_id=${encodeURIComponent(options.endUserId)}`\n : \"\";\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n `reset gateway budget \"${id}\"`,\n `/api/gateway/v1/budgets/${encodeURIComponent(id)}/reset${query}`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(options.reason ? { reason: options.reason } : {}),\n ...mutationInit(options),\n },\n );\n return budget;\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport {\n CURSOR_WALK_PAGE_SIZE,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport {\n mutationInit,\n type MutationOptions,\n} from \"@/client-sdk/services/_shared/mutation-options\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\n/**\n * The quantities one priced request or rollup carries. Every field is always\n * present; a bucket the request never used reports 0.\n *\n * The token buckets are disjoint, not nested. An image generation reports its\n * render under `output_image_tokens` with `output_tokens` at 0, so reading\n * `output_tokens` alone sees none of the image traffic.\n *\n * Every priced quantity is charged once at its own rate. `reasoning_tokens`\n * is a subset of `output_tokens` and `image_count` is a count of images, so\n * no rate prices either and neither belongs in a cost sum.\n */\nexport interface SpendUsage {\n input_tokens: number;\n output_tokens: number;\n cache_read_input_tokens: number;\n cache_creation_input_tokens: number;\n reasoning_tokens: number;\n input_image_tokens: number;\n output_image_tokens: number;\n image_count: number;\n}\n\n/**\n * Fill the image quantities a server older than the release that added them\n * does not send.\n *\n * `SpendUsage` declares the three because a current server always sends them.\n * The cast is what a response from a deployment one release behind actually\n * looks like: three integers that are genuinely zero there, worth defaulting\n * rather than handing a caller `undefined` in the middle of a spend page.\n */\nfunction spendUsageFromWire(usage: SpendUsage): SpendUsage {\n const wire = usage as Partial<SpendUsage>;\n return {\n ...usage,\n input_image_tokens: wire.input_image_tokens ?? 0,\n output_image_tokens: wire.output_image_tokens ?? 0,\n image_count: wire.image_count ?? 0,\n };\n}\n\nexport interface SpendEvent {\n id: string;\n type: string;\n created: string;\n schema_version: string;\n data: {\n /** Type-suffixed, unique per (request, event type): a settled and a\n * completed event for one request never share an id. */\n event_id: string;\n /** \"gateway.request.completed\" (confirmed and failed outcomes) or\n * \"gateway.request.settled\" (confirmation never arrived). */\n event_type: string;\n /** The join key across a settled/completed pair: a completed event\n * SUPERSEDES an earlier settled one for the same request; replace the\n * figure, never sum the pair. */\n gateway_request_id: string;\n occurred_at: string;\n organization_id: string;\n project_id: string;\n virtual_key_id: string;\n principal_user_id: string | null;\n end_user_id: string | null;\n trace_id: string;\n model: string | null;\n model_provider_id: string | null;\n request_type: string | null;\n /** Null on settled events: unknown is not zero. */\n usage: SpendUsage | null;\n /** Null on settled events: unknown is not zero. */\n cost: {\n total_usd: string;\n nano_usd: number;\n rate_version: string | null;\n } | null;\n /** \"success\" | \"error\" on completed events, \"settled\" on settled ones. */\n status: string;\n needs_reconciliation: boolean | null;\n settle_reason: string | null;\n error: { class: string; http_status: number | null } | null;\n duration_ms: number | null;\n labels: string[];\n metadata: Record<string, unknown>;\n };\n}\n\nexport interface SpendSummaryRow {\n /**\n * The FIRST grouping dimension's value. Unchanged from when a rollup could\n * only be grouped one way, so existing code keeps reading what it did. With\n * two dimensions, two rows can share a key: read `group` to tell them apart.\n */\n key: string;\n /** Every grouping dimension by name, e.g. `{ model: \"gpt-5-mini\" }`. */\n group: Record<string, string>;\n /** Start of the time bucket in the requested zone, null when unbucketed. */\n bucket_start: string | null;\n /** Priced outcomes (confirmed and failed). */\n event_count: number;\n /** Unpriced settled requests, counted separately: never in cost sums. */\n settled_count: number;\n usage: SpendUsage;\n cost: { total_usd: string; nano_usd: number };\n}\n\n/**\n * The states a request can be filtered by, which is more than the two a\n * caller usually thinks in: a request is `admitted` when it starts,\n * `confirmed` or `failed` when it ends, and `settled` once its cost is final.\n * `success` and `error` are the coarse outcome pair over those.\n */\nexport type SpendEventStatus =\n | \"success\"\n | \"error\"\n | \"admitted\"\n | \"confirmed\"\n | \"failed\"\n | \"settled\";\n\n/**\n * The states a ROLLUP can be filtered by. A rollup sums the cost of requests\n * past admission, so `admitted` is refused there rather than answered with a\n * zero; list the events to see those. Derived by exclusion so the two stay one\n * vocabulary.\n */\nexport type SpendSummaryStatus = Exclude<SpendEventStatus, \"admitted\">;\n\n/** A dimension a rollup can be grouped by. */\nexport type SpendGroupBy =\n | \"virtual_key\"\n | \"end_user\"\n | \"project\"\n | \"model\"\n | \"provider\"\n | \"principal\"\n | \"request_type\";\n\n/**\n * The filters BOTH spend reads accept. A reconciliation checksums the rollups\n * and diffs the events when a checksum disagrees, so the two take the same\n * vocabulary and a divergence can be walked on exactly the narrowing that\n * produced it.\n *\n * Every field takes one value or many; many means \"any of these\". Naming two\n * different fields narrows.\n */\nexport interface SpendFilterOptions {\n projectId?: string | string[];\n /** Resolved to the projects the team owns. A team with none matches nothing. */\n teamId?: string | string[];\n /** Your own id for a virtual key. One nobody minted matches nothing. */\n externalId?: string | string[];\n virtualKeyId?: string | string[];\n endUserId?: string | string[];\n principalUserId?: string | string[];\n model?: string | string[];\n providerKey?: string | string[];\n requestType?: string | string[];\n label?: string | string[];\n /**\n * Your own request metadata, e.g. `{ customer_tier: \"gold\" }`. Several\n * values for one key widen it; several keys narrow.\n */\n metadata?: Record<string, string | string[]>;\n status?: SpendEventStatus;\n}\n\nconst FILTER_PARAMS: ReadonlyArray<[keyof SpendFilterOptions, string]> = [\n [\"projectId\", \"project_id\"],\n [\"teamId\", \"team_id\"],\n [\"externalId\", \"external_id\"],\n [\"virtualKeyId\", \"virtual_key_id\"],\n [\"endUserId\", \"end_user_id\"],\n [\"principalUserId\", \"principal_user_id\"],\n [\"model\", \"model\"],\n [\"providerKey\", \"provider_key\"],\n [\"requestType\", \"request_type\"],\n [\"label\", \"label\"],\n];\n\n/** Repeat the parameter once per value: that is how the API widens a filter. */\nfunction appendSpendFilters({\n params,\n filters,\n}: {\n params: URLSearchParams;\n filters: SpendFilterOptions;\n}): void {\n for (const [field, name] of FILTER_PARAMS) {\n const value = filters[field] as string | string[] | undefined;\n if (value === undefined) continue;\n for (const one of Array.isArray(value) ? value : [value]) {\n params.append(name, one);\n }\n }\n for (const [key, value] of Object.entries(filters.metadata ?? {})) {\n // The API splits a pair on its FIRST colon, so a key carrying one would\n // silently address a different key and report spend for a filter nobody\n // wrote. Refused here rather than sent and misread.\n if (key.includes(\":\")) {\n throw new SpendEventsApiError(\n `A metadata key cannot contain a colon: ${key}`,\n \"build spend filters\",\n );\n }\n for (const one of Array.isArray(value) ? value : [value]) {\n params.append(\"metadata\", `${key}:${one}`);\n }\n }\n if (filters.status) params.set(\"status\", filters.status);\n}\n\n/**\n * What a rollup is grouped by, and over what window.\n *\n * Grouping by `model` or `provider`, or into time buckets, is REFUSED with\n * `gateway_spend_group_by_unstable` over a window recent enough that outcomes\n * can still arrive: until a request settles, the model and provider recorded\n * against it are the ones that were asked for, and they are replaced by the\n * ones that actually served it. A page walk over a group that can move counts\n * some requests twice and misses others.\n *\n * Reconcile closed periods and this never fires. For a live view where an\n * approximate shape is enough, send `allowUnstable`.\n */\nexport interface SpendSummariesOptions\n extends Omit<SpendFilterOptions, \"status\"> {\n /**\n * One lifecycle status, minus `admitted`: a rollup sums the cost of requests\n * past admission, and an admitted request has none yet. List the events for\n * those.\n */\n status?: SpendSummaryStatus;\n /** One or two dimensions. Two rows can share `key`; read `group`. */\n groupBy: SpendGroupBy | SpendGroupBy[];\n from: number;\n to: number;\n /** Adds a time column. Counts as movable, so the same refusal applies. */\n bucket?: \"none\" | \"hour\" | \"day\";\n /** IANA zone the bucket boundary falls on, e.g. \"Europe/Amsterdam\". */\n timezone?: string;\n /** Serve a movable grouping anyway, accepting an inexact walk. */\n allowUnstable?: boolean;\n}\n\nexport interface SpendSummariesPage {\n data: SpendSummaryRow[];\n /**\n * Pass back as `cursor` for the next page; null means the walk is done.\n * A full page does NOT mean there is more, so follow this until null\n * rather than stopping when a page comes back short.\n */\n next_cursor: string | null;\n}\n\nexport interface SpendEventsPage {\n data: SpendEvent[];\n next_cursor: string | null;\n}\n\nexport interface SpendReplayResult {\n endpoint_id: string;\n replay_id: string;\n replayed: number;\n window: { from: string; to: string };\n}\n\nexport interface EndUserSpend {\n end_user_id: string;\n window: string;\n from: string;\n to: string;\n cost: { total_usd: string; nano_usd?: number };\n request_count: number;\n usage: SpendUsage;\n /**\n * The attributed-user template caps that apply to this end user, each\n * with its boundary-aware current-period spend. Empty when the\n * organization runs no templates; never null.\n */\n caps: EndUserCap[];\n}\n\nexport interface EndUserCap {\n budget_id: string;\n anchor_id: string;\n window: string;\n on_breach: \"block\" | \"warn\";\n limit_usd: string;\n spent_usd: string;\n period_started_at: string;\n}\n\nexport class SpendEventsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SpendEventsApiError\";\n }\n}\n\n/**\n * Client for the gateway spend reconciliation surface (/api/gateway/v1).\n * Authenticates with an ORGANIZATION API key (sk-lw-*).\n *\n * Entity types mirror the wire verbatim, so their fields are lowercase\n * snake_case. Call options this SDK invents (query filters, per-call\n * behaviour, action arguments) are camelCase like the rest of the SDK.\n *\n * There is no project id here: `/spend-summaries` takes `project_id` as a\n * query filter rather than scoping on a header, so the project belongs to the\n * call, not to the client.\n *\n * The key MUST be an organization API key (`sk-lw-{id}_{secret}`, from\n * Settings > API Keys). A project API key is refused before any permission is\n * consulted, with `credential_class_mismatch`, and no header makes it work:\n * these are organization-scoped routes and a project key names one project.\n * The same organization key also reaches the project-scoped surfaces when\n * given `X-Project-Id`, so one key covers both families and a project key\n * covers only one.\n *\n * Neither collection on this service offers an eager whole-set read. The\n * ledger is unbounded, and materialising a window of it is the very\n * under-counting and out-of-memory footgun the page docstrings warn about:\n * take pages, or stream with `iterate()` / `iterSummaries()`.\n */\nexport class SpendEventsApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n }\n\n private async request<T>(\n operation: string,\n path: string,\n init?: RequestInit,\n ): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n ...(init?.headers ?? {}),\n },\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new SpendEventsApiError(message, operation, parsedBody);\n }\n return (await response.json()) as T;\n }\n\n /**\n * ONE page of the per-request spend ledger for a window. Pass `next_cursor`\n * back as `cursor` for the next page, verbatim.\n *\n * A full page does NOT mean there is more and a short page does NOT mean\n * there is no more: only a null cursor ends the walk. A reconciler that\n * stops on the first page silently under-counts the window, so read every\n * page or stream them with `iterate()`.\n */\n async listPage(\n options: SpendFilterOptions & {\n /** Required: the pull is a ranged read by contract. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n },\n ): Promise<SpendEventsPage> {\n const params = new URLSearchParams();\n params.set(\"from\", String(options.from));\n params.set(\"to\", String(options.to));\n if (options.cursor) params.set(\"cursor\", options.cursor);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n appendSpendFilters({ params, filters: options });\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const page = await this.request<SpendEventsPage>(\n \"list spend events\",\n `/api/gateway/v1/spend-events${qs}`,\n );\n return {\n ...page,\n data: page.data.map((event) => ({\n ...event,\n data: {\n ...event.data,\n usage:\n event.data.usage === null\n ? null\n : spendUsageFromWire(event.data.usage),\n },\n })),\n };\n }\n\n /**\n * Every spend event in the window, one row at a time, fetching each page\n * only when the consumer reaches it.\n *\n * This is how a reconciler reads a whole window without holding it: the\n * ledger is unbounded, so there is deliberately no eager `list()` to\n * collect it into an array. Raises rather than looping forever on a cursor\n * chain that never ends.\n */\n async *iterate(\n options: SpendFilterOptions & {\n /** Required: the pull is a ranged read by contract. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n },\n ): AsyncGenerator<SpendEvent> {\n const pages = walkCursorPages<SpendEventsPage>({\n startCursor: options.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new SpendEventsApiError(\n `Failed to list spend events: ${reason}.`,\n \"list spend events\",\n ),\n fetchPage: (cursor) =>\n this.listPage({\n ...options,\n cursor,\n limit: options.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /**\n * ONE page of per-key spend rollups for a window, paged by group key\n * ascending.\n *\n * The page is a step of a walk, not a whole answer: follow `next_cursor`\n * until it comes back null, or stream the walk with `iterSummaries()`. A\n * reconciler that reads only the first page silently under-counts every\n * tenant past the limit.\n */\n async summariesPage(\n options: SpendSummariesOptions & { cursor?: string; limit?: number },\n ): Promise<SpendSummariesPage> {\n const params = new URLSearchParams();\n const groupBy = Array.isArray(options.groupBy)\n ? options.groupBy\n : [options.groupBy];\n params.set(\"group_by\", groupBy.join(\",\"));\n params.set(\"from\", String(options.from));\n params.set(\"to\", String(options.to));\n if (options.bucket) params.set(\"bucket\", options.bucket);\n if (options.timezone) params.set(\"timezone\", options.timezone);\n if (options.allowUnstable) params.set(\"allow_unstable\", \"true\");\n appendSpendFilters({ params, filters: options });\n if (options.cursor) params.set(\"cursor\", options.cursor);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n const page = await this.request<SpendSummariesPage>(\n \"read spend summaries\",\n `/api/gateway/v1/spend-summaries?${params.toString()}`,\n );\n return {\n ...page,\n data: page.data.map((row) => ({\n ...row,\n usage: spendUsageFromWire(row.usage),\n })),\n };\n }\n\n /**\n * Every rollup row for the window, one at a time, fetching each page only\n * when the consumer reaches it.\n *\n * The rollup has one row per tenant seen in the window, which no bound\n * covers, so there is deliberately no eager whole-set read here either: a\n * checksum that quietly covers part of the window is worse than none.\n */\n async *iterSummaries(\n options: SpendSummariesOptions & { cursor?: string; limit?: number },\n ): AsyncGenerator<SpendSummaryRow> {\n const pages = walkCursorPages<SpendSummariesPage>({\n startCursor: options.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new SpendEventsApiError(\n `Failed to read spend summaries: ${reason}.`,\n \"read spend summaries\",\n ),\n fetchPage: (cursor) =>\n this.summariesPage({\n ...options,\n cursor,\n limit: options.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /**\n * Re-deliver a window's spend envelopes to ONE endpoint through the\n * normal delivery path. Envelope ids are unchanged (your consumer's\n * dedup key); mind your downstream billing system's finite dedup\n * window before replaying old ranges. The window is capped server-side\n * at 7 days per call.\n */\n async replay(\n options: {\n from: number;\n to: number;\n endpointId: string;\n } & MutationOptions,\n ): Promise<SpendReplayResult> {\n const response = await this.request<{ data: SpendReplayResult }>(\n \"replay spend events\",\n \"/api/gateway/v1/spend-events/replay\",\n {\n method: \"POST\",\n body: JSON.stringify({\n from: options.from,\n to: options.to,\n endpoint_id: options.endpointId,\n }),\n ...mutationInit(options),\n },\n );\n return response.data;\n }\n\n async endUserSpend(\n endUserId: string,\n options?: {\n window?: \"day\" | \"week\" | \"month\";\n from?: number;\n to?: number;\n virtualKeyId?: string;\n },\n ): Promise<EndUserSpend> {\n const params = new URLSearchParams();\n if (options?.window) params.set(\"window\", options.window);\n if (options?.from !== undefined) params.set(\"from\", String(options.from));\n if (options?.to !== undefined) params.set(\"to\", String(options.to));\n if (options?.virtualKeyId) params.set(\"virtual_key_id\", options.virtualKeyId);\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const res = await this.request<{ data: EndUserSpend }>(\n \"read end-user spend\",\n `/api/gateway/v1/end-users/${encodeURIComponent(endUserId)}/spend${qs}`,\n );\n return { ...res.data, usage: spendUsageFromWire(res.data.usage) };\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport {\n CURSOR_WALK_PAGE_SIZE,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport {\n idempotentCreateInit,\n mutationInit,\n type IdempotentCreateOptions,\n type MutationOptions,\n type ObservedRequestInit,\n} from \"@/client-sdk/services/_shared/mutation-options\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\n/** Where an endpoint delivers. */\nexport type WebhookDestinationKind = \"http\" | \"sqs\";\n\n/** The queue an `sqs` endpoint delivers to, as any read surface sees it: the\n * secret half of a static key pair is never returned. */\nexport interface WebhookSqsDestination {\n queue_url: string;\n /** Read off the queue URL, never configured beside it. */\n region: string;\n /** Whose queue it is. */\n account_id: string;\n queue_name: string;\n credential_mode: \"assume_role\" | \"static\" | \"ambient\";\n role_arn: string | null;\n /** Generated at save time, to paste into the role's trust policy. */\n external_id: string | null;\n access_key_id: string | null;\n}\n\n/** The queue half of a create or update body. */\nexport interface WebhookSqsDestinationInput {\n queue_url: string;\n /** The role to assume, with an external id we generate. The recommended\n * way to grant access: nothing long-lived is stored, and the customer\n * revokes by editing their own trust policy. */\n role_arn?: string;\n external_id?: string;\n /** A static key pair instead. The secret is stored encrypted and never\n * returned. */\n access_key_id?: string;\n secret_access_key?: string;\n}\n\nexport interface WebhookEndpointSummary {\n id: string;\n destination_kind: WebhookDestinationKind;\n /** The receiver URL on an `http` endpoint, null on every other kind. */\n url: string | null;\n /** The queue on an `sqs` endpoint, null on every other kind. */\n sqs: WebhookSqsDestination | null;\n max_batch_size: number;\n max_batch_delay_ms: number;\n max_in_flight: number;\n enabled_events: string[];\n status: \"active\" | \"disabled\";\n disabled_reason: string | null;\n disabled_at: string | null;\n failing_since: string | null;\n last_success_at: string | null;\n last_failure_at: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface WebhookEndpointWithSecret extends WebhookEndpointSummary {\n /** Present only on create and roll-secret responses; never again. */\n secret: string;\n}\n\n/** Everything a create body carries that is not the destination. */\ninterface CreateWebhookEndpointBase {\n enabled_events: string[];\n /** Envelopes per delivery. The receiver always gets an array. */\n max_batch_size?: number;\n /** How long a partial batch waits for company before it is sent. */\n max_batch_delay_ms?: number;\n /** Concurrent in-flight deliveries to this endpoint. */\n max_in_flight?: number;\n}\n\n/**\n * The POST body, exactly as the wire takes it.\n *\n * A destination is one kind and one address, so the two are a union rather\n * than independent optional fields: the type refuses `{destination_kind:\n * \"sqs\"}` with no queue, and `{url, sqs}` together, which is what the server\n * refuses too. `destination_kind` is optional only on the http branch, where\n * absent has always meant http.\n */\nexport type WebhookDestinationInput =\n | { destination_kind?: \"http\"; url: string; sqs?: never }\n | { destination_kind: \"sqs\"; sqs: WebhookSqsDestinationInput; url?: never };\n\nexport type CreateWebhookEndpointInput = CreateWebhookEndpointBase &\n WebhookDestinationInput;\n\n/** The PATCH body, exactly as the wire takes it. Omitted fields are left alone. */\nexport interface UpdateWebhookEndpointInput {\n url?: string;\n /** Only the queue's own fields; the destination kind cannot change, because\n * batches already planned against the old transport are in flight. Create\n * a new endpoint and archive this one once it has drained. A credential\n * field sent as null is CLEARED; omitted keeps what is stored. */\n sqs?: Partial<{\n queue_url: string;\n role_arn: string | null;\n external_id: string | null;\n access_key_id: string | null;\n secret_access_key: string | null;\n }>;\n enabled_events?: string[];\n status?: \"active\" | \"disabled\";\n max_batch_size?: number;\n max_batch_delay_ms?: number;\n max_in_flight?: number;\n}\n\nexport interface WebhookDeliveryRecord {\n id: string;\n dispatch_id: string;\n attempt: number;\n event_count: number;\n outcome: string;\n response_status: number | null;\n latency_ms: number | null;\n error: string | null;\n fired_at: string;\n}\n\nexport interface WebhookTestResult {\n delivered: boolean;\n response_status: number | null;\n response_body?: string;\n error?: string;\n}\n\nexport interface WebhookEndpointHealth {\n status: \"active\" | \"disabled\";\n disabled_reason: string | null;\n failing_since: string | null;\n last_success_at: string | null;\n last_failure_at: string | null;\n /** The headline: age of the oldest envelope still buffered or retrying;\n * null when the feed is fully delivered. */\n oldest_undelivered_age_ms: number | null;\n dlq_depth: number;\n sends_per_minute: number;\n success_rate: number | null;\n p95_latency_ms: number | null;\n}\n\nexport interface WebhookEventType {\n type: string;\n family: string;\n schema_version: string;\n is_emitting: boolean;\n description: string;\n}\n\nexport interface EmittedEvent {\n id: string;\n type: string;\n created: string;\n schema_version: string;\n data: Record<string, unknown>;\n}\n\n/** One page of the organization's emitted-events log. */\nexport interface EmittedEventsPage {\n data: EmittedEvent[];\n /** Pass back as `cursor` for the next page; null ends the walk. */\n next_cursor: string | null;\n}\n\n/** One page of an endpoint's delivery log, newest first. */\nexport interface WebhookDeliveryPage {\n data: WebhookDeliveryRecord[];\n /** Pass back as `cursor` for the next page; null ends the walk. */\n next_cursor: string | null;\n}\n\nexport class WebhooksApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"WebhooksApiError\";\n }\n}\n\n/**\n * Client for the org-anchored webhook platform surface (/api/webhooks/v1).\n * Authenticates with an ORGANIZATION API key (sk-lw-*); project keys are\n * rejected by the server. The surface is anchored on the organization alone,\n * so there is no project id to give this client.\n *\n * The key MUST be an organization API key (`sk-lw-{id}_{secret}`, from\n * Settings > API Keys). A project API key is refused with\n * `credential_class_mismatch` before any permission is consulted, and no\n * header makes it work. The same organization key also reaches the\n * project-scoped surfaces when given `X-Project-Id`, so one key covers both\n * families and a project key covers only one.\n *\n * The endpoint entity and the create/update bodies mirror the wire verbatim,\n * so their fields are lowercase snake_case: virtual keys and gateway budgets\n * already take the wire body as it is, and translating field by field here\n * only made the request bodies of the four billing surfaces disagree. Call\n * options this SDK invents (query filters, per-call behaviour, action\n * arguments) stay camelCase like the rest of the SDK.\n */\nexport class WebhooksApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n }\n\n private async request<T>(\n operation: string,\n path: string,\n init?: ObservedRequestInit,\n ): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n ...(init?.headers ?? {}),\n },\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new WebhooksApiError(message, operation, parsedBody);\n }\n init?.onResponse?.(response);\n return (await response.json()) as T;\n }\n\n async list(): Promise<WebhookEndpointSummary[]> {\n const res = await this.request<{ data: WebhookEndpointSummary[] }>(\n \"list webhook endpoints\",\n \"/api/webhooks/v1/endpoints\",\n );\n return res.data;\n }\n\n async get(id: string): Promise<WebhookEndpointSummary> {\n const res = await this.request<{ data: WebhookEndpointSummary }>(\n \"get webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}`,\n );\n return res.data;\n }\n\n /**\n * The signing secret comes back on this response and never again, so a\n * create that times out is recovered with `idempotencyKey`: the replay\n * carries the same secret, and nothing else ever will.\n */\n async create(\n input: CreateWebhookEndpointInput,\n options?: IdempotentCreateOptions,\n ): Promise<WebhookEndpointWithSecret> {\n const res = await this.request<{ data: WebhookEndpointWithSecret }>(\n \"create webhook endpoint\",\n \"/api/webhooks/v1/endpoints\",\n {\n method: \"POST\",\n body: JSON.stringify(input),\n ...idempotentCreateInit(options),\n },\n );\n return res.data;\n }\n\n async update(\n id: string,\n input: UpdateWebhookEndpointInput,\n options?: MutationOptions,\n ): Promise<WebhookEndpointSummary> {\n const res = await this.request<{ data: WebhookEndpointSummary }>(\n \"update webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input), ...mutationInit(options) },\n );\n return res.data;\n }\n\n /**\n * Retire an endpoint: the server soft-archives the row, stamping\n * `archived_at` and dropping the status to disabled, so the delivery\n * history stays readable for audit while nothing more is ever sent. The\n * row is archived, not removed, and `gatewayBudgets.archive()` already\n * names that operation, so the billing surfaces agree on the verb.\n *\n * Nothing comes back: the response body carries only an `archived: true`\n * acknowledgement, and a non-2xx already raises.\n */\n async archive(id: string, options?: MutationOptions): Promise<void> {\n await this.request<unknown>(\n \"archive webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}`,\n { method: \"DELETE\", ...mutationInit(options) },\n );\n }\n\n async rollSecret(\n id: string,\n options?: MutationOptions,\n ): Promise<WebhookEndpointWithSecret> {\n const res = await this.request<{ data: WebhookEndpointWithSecret }>(\n \"roll webhook endpoint secret\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/roll-secret`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return res.data;\n }\n\n async test(\n id: string,\n options?: MutationOptions,\n ): Promise<WebhookTestResult> {\n const res = await this.request<{ data: WebhookTestResult }>(\n \"test webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/test`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return res.data;\n }\n\n /**\n * ONE page of the endpoint's delivery attempts, newest first.\n *\n * The cursor is why this is a page: the route has always served one, and\n * dropping it truncated the delivery log at whatever the first page held,\n * with nothing in the result to say the rest existed. Pass `next_cursor`\n * back as `cursor`, or walk the whole log with `iterDeliveries()`.\n */\n async deliveriesPage(\n id: string,\n options?: { cursor?: string; limit?: number },\n ): Promise<WebhookDeliveryPage> {\n const params = new URLSearchParams();\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, next_cursor } = await this.request<{\n data: WebhookDeliveryRecord[];\n next_cursor?: string | null;\n }>(\n \"list webhook deliveries\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/deliveries${qs}`,\n );\n return { data, next_cursor: next_cursor ?? null };\n }\n\n /**\n * Every recorded delivery attempt for the endpoint, one at a time,\n * fetching each page only when the consumer reaches it.\n */\n async *iterDeliveries(\n id: string,\n options?: { cursor?: string; limit?: number },\n ): AsyncGenerator<WebhookDeliveryRecord> {\n const pages = walkCursorPages<WebhookDeliveryPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new WebhooksApiError(\n `Failed to list webhook deliveries: ${reason}.`,\n \"list webhook deliveries\",\n ),\n fetchPage: (cursor) =>\n this.deliveriesPage(id, {\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n async health(id: string): Promise<WebhookEndpointHealth> {\n const res = await this.request<{ data: WebhookEndpointHealth }>(\n \"read webhook endpoint health\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/health`,\n );\n return res.data;\n }\n\n async eventTypes(): Promise<WebhookEventType[]> {\n const res = await this.request<{ data: WebhookEventType[] }>(\n \"list webhook event types\",\n \"/api/webhooks/v1/event-types\",\n );\n return res.data;\n }\n\n /**\n * ONE page of the organization's emitted-events log, newest first.\n *\n * Webhooks are a push over this log, never the only copy of it: a consumer\n * that missed a delivery reads the window back from here. Walk the whole\n * window with `iterEvents()`.\n */\n async eventsPage(options: {\n type?: string;\n /** Required: the log is a ranged read by contract. Epoch milliseconds. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n }): Promise<EmittedEventsPage> {\n const params = new URLSearchParams();\n if (options.type) params.set(\"type\", options.type);\n params.set(\"from\", String(options.from));\n params.set(\"to\", String(options.to));\n if (options.cursor) params.set(\"cursor\", options.cursor);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, next_cursor } = await this.request<{\n data: EmittedEvent[];\n next_cursor?: string | null;\n }>(\"list emitted events\", `/api/webhooks/v1/events${qs}`);\n return { data, next_cursor: next_cursor ?? null };\n }\n\n /**\n * Every emitted event matching the filter, one at a time, fetching each\n * page only when the consumer reaches it.\n */\n async *iterEvents(options: {\n type?: string;\n /** Required: the log is a ranged read by contract. Epoch milliseconds. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n }): AsyncGenerator<EmittedEvent> {\n const pages = walkCursorPages<EmittedEventsPage>({\n startCursor: options.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new WebhooksApiError(\n `Failed to list emitted events: ${reason}.`,\n \"list emitted events\",\n ),\n fetchPage: (cursor) =>\n this.eventsPage({\n ...options,\n cursor,\n limit: options.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /**\n * One emitted event by id, the envelope exactly as it was delivered.\n *\n * A 404 covers every reason the log cannot answer: never emitted, past the\n * retention horizon, or belonging to another organization.\n */\n async getEvent(id: string): Promise<EmittedEvent> {\n const res = await this.request<{ data: EmittedEvent }>(\n \"get emitted event\",\n `/api/webhooks/v1/events/${encodeURIComponent(id)}`,\n );\n return res.data;\n }\n}\n","/**\n * The raw-fetch request path the management API services share.\n *\n * Every one of them talks to an organization-scoped REST family with the same\n * three-step failure handling the api-keys service established: read the body,\n * build the English sentence, raise the typed `LangWatchHandledError` when the\n * platform NAMED the failure, and otherwise throw the family's own error class\n * with that same sentence. Nine copies of that would drift; this is the one\n * copy, parameterised by the family's error constructor.\n *\n * The families answer two error envelopes and both land here unchanged:\n * `@langwatch/api` services send `{code, message, meta, tips?, docsUrl?}`,\n * while api-keys, teams, groups and the instance-admin family send the legacy\n * `{error, message}`. `handledErrorFrom` reads either, so the caller never has\n * to know which family it is holding.\n */\nimport { scopedApiKey } from \"@/internal/credentialContext\";\nimport { formatApiErrorForOperation } from \"./format-api-error\";\nimport { throwIfHandledError } from \"./throw-handled-error\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\n/** Builds the family's own error for a failure the platform did not name. */\nexport type ManagementErrorFactory = (params: {\n message: string;\n operation: string;\n body: unknown;\n}) => Error;\n\n/**\n * How long one management call may hang before it is abandoned. A control\n * plane that accepts the socket and never answers would otherwise wedge the\n * command forever, with a spinner and no way out.\n */\nexport const MANAGEMENT_REQUEST_TIMEOUT_MS = 30_000;\n\n/**\n * The organization credential every management family except the\n * instance-provisioning one runs with, resolved once rather than in each\n * constructor. An empty token is refused here so the caller reads what is\n * missing instead of a 401 from the platform.\n */\nexport const resolveManagementToken = ({\n apiKey,\n}: {\n apiKey?: string;\n}): string => {\n const token = apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY;\n if (!token) {\n throw new Error(\n \"No API key configured. Set LANGWATCH_API_KEY, run `langwatch login`, or pass apiKey.\",\n );\n }\n return token;\n};\n\nexport interface ManagementRequestConfig {\n endpoint: string;\n /** The bearer credential; an organization key, or the instance-admin key. */\n token: string;\n /** Raised when the response carries no named platform failure. */\n errorFactory: ManagementErrorFactory;\n}\n\nexport interface ManagementRequestParams {\n /** What was being attempted, e.g. `list custom roles`. */\n operation: string;\n /** Path from the endpoint root, e.g. `/api/roles`. */\n path: string;\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n /** JSON request body; omitted for reads. */\n body?: unknown;\n /** Query parameters. Undefined values are left off the URL entirely. */\n query?: Record<string, string | number | boolean | undefined>;\n /** Caller's own deadline; without one the default timeout applies. */\n signal?: AbortSignal;\n}\n\n/** `?a=1&b=2` for the values that are actually set, or \"\" when none are. */\nexport const buildQueryString = (\n query: Record<string, string | number | boolean | undefined> | undefined,\n): string => {\n if (!query) return \"\";\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n params.set(key, String(value));\n }\n const rendered = params.toString();\n return rendered ? `?${rendered}` : \"\";\n};\n\n/**\n * A bound request function for one service: same semantics as the api-keys\n * service's private `request`, with the family's error class supplied once.\n */\nexport const createManagementRequest = ({\n endpoint,\n token,\n errorFactory,\n}: ManagementRequestConfig) => {\n return async <T>({\n operation,\n path,\n method,\n body,\n query,\n signal,\n }: ManagementRequestParams): Promise<T> => {\n const response = await langwatchFetch(\n `${endpoint}${path}${buildQueryString(query)}`,\n {\n ...(method ? { method } : {}),\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n signal: signal ?? AbortSignal.timeout(MANAGEMENT_REQUEST_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n // One read: a body consumed by a failed `json()` cannot be read again,\n // and the second read would throw past the error handling below.\n const rawBody = await response.text();\n let parsedBody: unknown;\n try {\n parsedBody = JSON.parse(rawBody);\n } catch {\n parsedBody = rawBody;\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw errorFactory({ message, operation, body: parsedBody });\n }\n\n // A 204 carries no body, and `json()` on an empty one throws a bare\n // SyntaxError that would escape as a parse failure rather than the success\n // it is. A family that answers 204 to a delete gets an undefined result.\n if (response.status === 204) return undefined as T;\n\n return (await response.json()) as T;\n };\n};\n\nexport type ManagementRequest = ReturnType<typeof createManagementRequest>;\n","/**\n * The `/api/teams` REST family, which shipped without a CLI.\n *\n * Teams group projects and members; membership is a team-scoped role binding,\n * which is why adding a member takes the role they get on the team.\n */\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport {\n createManagementRequest,\n resolveManagementToken,\n type ManagementRequest,\n} from \"../_shared/management-request\";\nimport type { ManagementRole } from \"../_shared/management-types\";\n\nexport interface Team {\n id: string;\n name: string;\n slug: string;\n organizationId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface TeamPagination {\n page: number;\n limit: number;\n total: number;\n}\n\nexport interface ListTeamsResponse {\n data: Team[];\n pagination: TeamPagination;\n}\n\nexport interface ArchivedTeam {\n id: string;\n name: string;\n archivedAt: string | null;\n}\n\nexport interface TeamMember {\n userId: string | null;\n name: string | null;\n email: string | null;\n role: ManagementRole;\n}\n\nexport class TeamsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"TeamsApiError\";\n }\n}\n\nexport class TeamsApiService {\n readonly #request: ManagementRequest;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.#request = createManagementRequest({\n endpoint: resolveEndpoint(config?.endpoint),\n token: resolveManagementToken({ apiKey: config?.apiKey }),\n errorFactory: ({ message, operation, body }) =>\n new TeamsApiError(message, operation, body),\n });\n }\n\n async list(\n options: { page?: number; limit?: number } = {},\n ): Promise<ListTeamsResponse> {\n return this.#request({\n operation: \"list teams\",\n path: \"/api/teams\",\n query: { ...options },\n });\n }\n\n async get(id: string): Promise<Team> {\n return this.#request({\n operation: `fetch team \"${id}\"`,\n path: `/api/teams/${encodeURIComponent(id)}`,\n });\n }\n\n async create(input: { name: string }): Promise<Team> {\n return this.#request({\n operation: \"create team\",\n path: \"/api/teams\",\n method: \"POST\",\n body: input,\n });\n }\n\n async update({\n id,\n input,\n }: {\n id: string;\n input: { name?: string };\n }): Promise<Team> {\n return this.#request({\n operation: `update team \"${id}\"`,\n path: `/api/teams/${encodeURIComponent(id)}`,\n method: \"PATCH\",\n body: input,\n });\n }\n\n async archive(id: string): Promise<ArchivedTeam> {\n return this.#request({\n operation: `archive team \"${id}\"`,\n path: `/api/teams/${encodeURIComponent(id)}`,\n method: \"DELETE\",\n });\n }\n\n async listMembers(teamId: string): Promise<{ data: TeamMember[] }> {\n return this.#request({\n operation: `list members of team \"${teamId}\"`,\n path: `/api/teams/${encodeURIComponent(teamId)}/members`,\n });\n }\n\n async addMember({\n teamId,\n input,\n }: {\n teamId: string;\n input: { userId: string; role?: ManagementRole };\n }): Promise<{ success: boolean }> {\n return this.#request({\n operation: `add a member to team \"${teamId}\"`,\n path: `/api/teams/${encodeURIComponent(teamId)}/members`,\n method: \"POST\",\n body: input,\n });\n }\n\n async removeMember({\n teamId,\n userId,\n }: {\n teamId: string;\n userId: string;\n }): Promise<{ success: boolean }> {\n return this.#request({\n operation: `remove member \"${userId}\" from team \"${teamId}\"`,\n path: `/api/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`,\n method: \"DELETE\",\n });\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport interface Project {\n id: string;\n name: string;\n slug: string;\n language: string;\n framework: string;\n teamId: string;\n piiRedactionLevel: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateProjectInput {\n name: string;\n language: string;\n framework: string;\n teamId?: string;\n newTeamName?: string;\n}\n\nexport interface UpdateProjectInput {\n name?: string;\n language?: string;\n framework?: string;\n piiRedactionLevel?: \"STRICT\" | \"ESSENTIAL\" | \"DISABLED\";\n}\n\nexport interface ProjectWithServiceKey extends Project {\n serviceApiKey: string;\n serviceApiKeyId: string;\n}\n\nexport interface PaginatedProjects {\n data: Project[];\n pagination: {\n page: number;\n limit: number;\n total: number;\n totalPages: number;\n };\n}\n\nexport interface ArchivedProject {\n id: string;\n name: string;\n archivedAt: string;\n}\n\nexport class ProjectsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The status the platform answered with. Callers that resolve a\n * `--project` selector through the listing branch on it: a 401/403 means\n * the credential cannot see the listing at all, which is a different\n * answer to the user than \"no project of yours matches this name\".\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"ProjectsApiError\";\n }\n}\n\nexport class ProjectsApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n }\n\n /**\n * Bearer, never the project-pinned Basic shape the data routes use. The\n * listing is the question \"which projects can this credential see?\", so\n * naming one project in the header would scope the answer to that project\n * and defeat the call — including the `--project <slug>` lookup, which reads\n * the listing precisely because it does not know the id yet.\n */\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n private async request<T>(operation: string, path: string, init?: RequestInit): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n headers: { ...this.headers(), ...(init?.headers ?? {}) },\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new ProjectsApiError(\n message,\n operation,\n parsedBody,\n response.status,\n );\n }\n return (await response.json()) as T;\n }\n\n async list(options?: { page?: number; limit?: number }): Promise<PaginatedProjects> {\n const params = new URLSearchParams();\n if (options?.page) params.set(\"page\", String(options.page));\n if (options?.limit) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.request<PaginatedProjects>(\n \"list projects\",\n `/api/projects${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n async get(id: string): Promise<Project> {\n return this.request<Project>(\n `get project \"${id}\"`,\n `/api/projects/${encodeURIComponent(id)}`,\n );\n }\n\n /**\n * The project's ingest key. The platform hands it out only to a credential\n * with `project:update` on the project, so a 403 here is the answer \"this\n * login may not\", not a transport failure.\n */\n async getApiKey(id: string): Promise<string> {\n const answer = await this.request<{ apiKey: string }>(\n `get api key of project \"${id}\"`,\n `/api/projects/${encodeURIComponent(id)}/api-key`,\n );\n return answer.apiKey;\n }\n\n async create(input: CreateProjectInput): Promise<ProjectWithServiceKey> {\n return this.request<ProjectWithServiceKey>(\n \"create project\",\n \"/api/projects\",\n { method: \"POST\", body: JSON.stringify(input) },\n );\n }\n\n async update(id: string, input: UpdateProjectInput): Promise<Project> {\n return this.request<Project>(\n `update project \"${id}\"`,\n `/api/projects/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input) },\n );\n }\n\n async archive(id: string): Promise<ArchivedProject> {\n return this.request<ArchivedProject>(\n `archive project \"${id}\"`,\n `/api/projects/${encodeURIComponent(id)}`,\n { method: \"DELETE\" },\n );\n }\n}\n","import type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport interface GetTraceParams {\n includeSpans?: boolean;\n}\n\nexport type GetTraceResponse = NonNullable<\n paths[\"/api/trace/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>;\n\n/**\n * Custom error class for Traces API operations.\n * Provides context about the failed operation and the original error.\n */\nexport class TracesError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: any,\n ) {\n super(message);\n this.name = \"TracesError\";\n }\n}\n","import { LANGWATCH_SDK_NAME_CLIENT, LANGWATCH_SDK_VERSION } from \"@/internal/constants\";\nimport { getLangWatchTracer } from \"@/observability-sdk/tracer\";\n\nexport const tracer = getLangWatchTracer(`${LANGWATCH_SDK_NAME_CLIENT}.traces`, LANGWATCH_SDK_VERSION);\n","import { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { type GetTraceParams, TracesError, type GetTraceResponse } from \"./types\";\nimport { tracer } from \"./tracing\";\n\n/**\n * Service for managing trace resources via the Langwatch API.\n * Constructor creates a proxy that wraps the service and traces all methods.\n *\n * Responsibilities:\n * - Retrieving trace data\n * - Error handling with contextual information\n *\n * All methods return trace response objects directly.\n */\nexport class TracesService {\n private config: InternalConfig;\n\n constructor(config: InternalConfig) {\n this.config = config;\n\n /**\n * Wraps the service in a tracing proxy via the decorator.\n */\n return createTracingProxy(\n this as TracesService,\n tracer,\n );\n }\n\n /**\n * Handles API errors by throwing a TracesError with operation context.\n * @param operation Description of the operation being performed.\n * @param error The error object returned from the API client.\n * @throws {TracesError}\n */\n private handleApiError(operation: string, error: any): never {\n const errorMessage =\n typeof error === \"string\"\n ? error\n : error?.error ?? error?.message ?? \"Unknown error occurred\";\n const message = `Failed to ${operation}: ${errorMessage}`;\n\n throw new TracesError(\n message,\n operation,\n error,\n );\n }\n\n /**\n * Retrieves a trace by its ID.\n * @param traceId The trace's unique identifier.\n * @param params Optional parameters for the request.\n * @returns The trace response object.\n * @throws {TracesError} If the API call fails.\n */\n async get(\n traceId: string,\n params?: GetTraceParams,\n ): Promise<GetTraceResponse> {\n const { data, error } = await this.config.langwatchApiClient.GET(\"/api/trace/{id}\", {\n params: {\n path: {\n id: traceId,\n },\n },\n query: params,\n });\n\n if (error) {\n this.handleApiError(\"get trace\", error);\n }\n\n return data;\n }\n}\n","import { type InternalConfig } from \"../../types\";\nimport { TracesService } from \"./service\";\nimport { type GetTraceParams, type GetTraceResponse } from \"./types\";\n\nexport class TracesFacade {\n readonly #service: TracesService;\n\n constructor(config: InternalConfig) {\n this.#service = new TracesService(config);\n }\n\n async get(traceId: string, params?: GetTraceParams): Promise<GetTraceResponse> {\n return this.#service.get(traceId, params);\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\nimport { PromptsFacade, PromptsApiService } from \"./services/prompts\";\nexport { FetchPolicy, type GetPromptOptions } from \"./services/prompts\";\nexport type {\n Dataset,\n DatasetEntry,\n DatasetMetadata,\n DatasetColumnType,\n DatasetListItem,\n Pagination,\n PaginatedResponse,\n GetDatasetOptions,\n ListDatasetsOptions,\n ListDatasetsApiResponse,\n ListRecordsOptions,\n ListRecordsApiResponse,\n CreateDatasetOptions,\n UpdateDatasetOptions,\n CreateFromUploadResponse,\n BatchCreateRecordsResponse,\n DeleteRecordsResponse,\n UploadResponse,\n DatasetRecordResponse,\n} from \"./services/datasets\";\nexport { DatasetError, DatasetNotFoundError, DatasetApiError, DatasetValidationError, DatasetPlanLimitError } from \"./services/datasets\";\nexport type {\n ExperimentRunResult,\n RunExperimentOptions,\n RunWithResultsOptions,\n ExperimentRowResult,\n ExperimentRunWithResults,\n} from \"./services/experiments\";\nexport {\n ExperimentsError,\n ExperimentNotFoundError,\n ExperimentTimeoutError,\n ExperimentRunFailedError,\n ExperimentsApiError,\n} from \"./services/experiments\";\nexport type { EvaluationResult, EvaluateOptions, EvaluationStatus, EvaluationCost } from \"./services/evaluations\";\nexport {\n EvaluationError,\n EvaluatorCallError,\n EvaluatorNotFoundError,\n EvaluationsApiError,\n} from \"./services/evaluations\";\nexport { EvaluatorsApiService, EvaluatorsApiError } from \"./services/evaluators\";\nexport { ScenariosApiService, ScenariosApiError } from \"./services/scenarios\";\nexport { SuitesApiService, SuitesApiError } from \"./services/suites\";\nexport { RunPlansApiService, RunPlansApiError } from \"./services/run-plans\";\nexport { InstantEvalsApiService, InstantEvalsApiError } from \"./services/instant-evals\";\nexport { TestSuitesApiService, TestSuitesApiError } from \"./services/test-suites\";\nexport { WorkflowsApiService, WorkflowsApiError } from \"./services/workflows/workflows-api.service\";\nexport { AgentsApiService, AgentsApiError } from \"./services/agents/agents-api.service\";\nexport { AnnotationsApiService, AnnotationsApiError } from \"./services/annotations/annotations-api.service\";\nexport { DashboardsApiService, DashboardsApiError } from \"./services/dashboards/dashboards-api.service\";\nexport { ModelProvidersApiService, ModelProvidersApiError } from \"./services/model-providers/model-providers-api.service\";\nexport { AnalyticsApiService, AnalyticsApiError } from \"./services/analytics/analytics-api.service\";\nexport { QueryApiService, QueryApiError } from \"./services/query/query-api.service\";\nexport { TriggersApiService, TriggersApiError } from \"./services/triggers\";\nexport { GraphsApiService, GraphsApiError } from \"./services/graphs\";\nexport { SimulationRunsApiService, SimulationRunsApiError } from \"./services/simulation-runs\";\nexport { TracesApiService, TracesApiError } from \"./services/traces/traces-api.service\";\nexport { MonitorsApiService, MonitorsApiError } from \"./services/monitors\";\nexport { SecretsApiService, SecretsApiError } from \"./services/secrets\";\nexport { VirtualKeysApiService, VirtualKeysApiError } from \"./services/virtual-keys/virtual-keys-api.service\";\nexport { GatewayBudgetsApiService, GatewayBudgetsApiError } from \"./services/gateway-budgets/gateway-budgets-api.service\";\nexport { SpendEventsApiService, SpendEventsApiError } from \"./services/spend-events/spend-events-api.service\";\nexport { WebhooksApiService, WebhooksApiError } from \"./services/webhooks/webhooks-api.service\";\nexport { TeamsApiService, TeamsApiError } from \"./services/teams/teams-api.service\";\nexport type {\n Team,\n TeamPagination,\n TeamMember,\n ListTeamsResponse,\n ArchivedTeam,\n} from \"./services/teams/teams-api.service\";\nexport { ProjectsApiService, ProjectsApiError } from \"./services/projects/projects-api.service\";\nexport type {\n Project,\n PaginatedProjects,\n ProjectWithServiceKey,\n ArchivedProject,\n CreateProjectInput,\n UpdateProjectInput,\n} from \"./services/projects/projects-api.service\";\nimport { LocalPromptsService } from \"./services/prompts/local-prompts.service\";\nimport { ExperimentsFacade } from \"./services/experiments\";\nimport { DatasetsFacade } from \"./services/datasets\";\nimport { EvaluationsFacade } from \"./services/evaluations\";\nimport { EvaluatorsApiService } from \"./services/evaluators\";\nimport { ScenariosApiService } from \"./services/scenarios\";\nimport { SuitesApiService } from \"./services/suites\";\nimport { RunPlansApiService } from \"./services/run-plans\";\nimport { InstantEvalsApiService } from \"./services/instant-evals\";\nimport { TestSuitesApiService } from \"./services/test-suites\";\nimport { WorkflowsApiService } from \"./services/workflows/workflows-api.service\";\nimport { AgentsApiService } from \"./services/agents/agents-api.service\";\nimport { AnnotationsApiService } from \"./services/annotations/annotations-api.service\";\nimport { DashboardsApiService } from \"./services/dashboards/dashboards-api.service\";\nimport { ModelProvidersApiService } from \"./services/model-providers/model-providers-api.service\";\nimport { AnalyticsApiService } from \"./services/analytics/analytics-api.service\";\nimport { QueryApiService } from \"./services/query/query-api.service\";\nimport { TriggersApiService } from \"./services/triggers\";\nimport { GraphsApiService } from \"./services/graphs\";\nimport { SimulationRunsApiService } from \"./services/simulation-runs\";\nimport { MonitorsApiService } from \"./services/monitors\";\nimport { SecretsApiService } from \"./services/secrets\";\nimport { VirtualKeysApiService } from \"./services/virtual-keys/virtual-keys-api.service\";\nimport { GatewayBudgetsApiService } from \"./services/gateway-budgets/gateway-budgets-api.service\";\nimport { SpendEventsApiService } from \"./services/spend-events/spend-events-api.service\";\nimport { WebhooksApiService } from \"./services/webhooks/webhooks-api.service\";\nimport { TeamsApiService } from \"./services/teams/teams-api.service\";\nimport { ProjectsApiService } from \"./services/projects/projects-api.service\";\nimport { type InternalConfig } from \"./types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"../internal/api/client\";\nimport { type Logger, NoOpLogger } from \"../logger\";\nimport { TracesFacade } from \"./services/traces/facade\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\n\nexport interface LangWatchConstructorOptions {\n apiKey?: string;\n endpoint?: string;\n options?: {\n logger?: Logger;\n };\n}\n\nexport class LangWatch {\n private readonly config: InternalConfig & { endpoint: string; apiKey: string };\n\n readonly prompts: PromptsFacade;\n readonly traces: TracesFacade;\n readonly datasets: DatasetsFacade;\n\n /**\n * Run experiments on LangWatch platform or via SDK.\n *\n * Platform experiments (CI/CD):\n * ```typescript\n * const result = await langwatch.experiments.run(\"my-experiment-slug\");\n * result.printSummary();\n * ```\n *\n * SDK-defined experiments:\n * ```typescript\n * const experiment = await langwatch.experiments.init(\"my-experiment\");\n * // ... run evaluators using experiment.evaluate()\n * ```\n */\n readonly experiments: ExperimentsFacade;\n\n /**\n * Run evaluators and guardrails in real-time (Online Evaluations).\n *\n * @example\n * ```typescript\n * const guardrail = await langwatch.evaluations.evaluate(\"presidio/pii_detection\", {\n * data: { input: userInput, output: generatedResponse },\n * name: \"PII Detection\",\n * asGuardrail: true,\n * });\n *\n * if (!guardrail.passed) {\n * return \"I'm sorry, I can't do that.\";\n * }\n * ```\n */\n readonly evaluations: EvaluationsFacade;\n\n readonly evaluators: EvaluatorsApiService;\n readonly scenarios: ScenariosApiService;\n /**\n * @deprecated Use runPlans and testSuites; /api/suites is a frozen alias.\n */\n readonly suites: SuitesApiService;\n readonly runPlans: RunPlansApiService;\n /** Judge one LangWatchQL statement across the project's history as a job. */\n readonly instantEvals: InstantEvalsApiService;\n readonly testSuites: TestSuitesApiService;\n readonly workflows: WorkflowsApiService;\n readonly agents: AgentsApiService;\n readonly annotations: AnnotationsApiService;\n readonly dashboards: DashboardsApiService;\n readonly modelProviders: ModelProvidersApiService;\n readonly analytics: AnalyticsApiService;\n /** The raw LangWatchQL door — run a governed SELECT or discover the analytics schema directly, outside a saved chart. */\n readonly query: QueryApiService;\n readonly triggers: TriggersApiService;\n readonly graphs: GraphsApiService;\n readonly simulationRuns: SimulationRunsApiService;\n readonly monitors: MonitorsApiService;\n readonly secrets: SecretsApiService;\n readonly virtualKeys: VirtualKeysApiService;\n readonly gatewayBudgets: GatewayBudgetsApiService;\n readonly spendEvents: SpendEventsApiService;\n readonly webhooks: WebhooksApiService;\n\n #teams?: TeamsApiService;\n #projects?: ProjectsApiService;\n\n constructor(options: LangWatchConstructorOptions = {}) {\n const apiKey = options.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n const endpoint = resolveEndpoint(options.endpoint);\n\n this.config = this.#createInternalConfig({\n apiKey,\n endpoint,\n options: options.options,\n });\n\n this.prompts = new PromptsFacade({\n promptsApiService: new PromptsApiService(this.config),\n localPromptsService: new LocalPromptsService(),\n ...this.config,\n });\n this.traces = new TracesFacade(this.config);\n\n this.experiments = new ExperimentsFacade({\n langwatchApiClient: this.config.langwatchApiClient,\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n logger: this.config.logger,\n });\n\n this.datasets = new DatasetsFacade({\n langwatchApiClient: this.config.langwatchApiClient,\n logger: this.config.logger,\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n });\n\n this.evaluations = new EvaluationsFacade({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n logger: this.config.logger,\n });\n\n this.evaluators = new EvaluatorsApiService(this.config);\n this.scenarios = new ScenariosApiService(this.config);\n this.suites = new SuitesApiService(this.config);\n this.runPlans = new RunPlansApiService(this.config);\n this.instantEvals = new InstantEvalsApiService(this.config);\n this.testSuites = new TestSuitesApiService(this.config);\n this.workflows = new WorkflowsApiService(this.config);\n this.agents = new AgentsApiService(this.config);\n this.annotations = new AnnotationsApiService(this.config);\n this.dashboards = new DashboardsApiService(this.config);\n this.modelProviders = new ModelProvidersApiService(this.config);\n this.analytics = new AnalyticsApiService(this.config);\n this.query = new QueryApiService(this.config);\n this.triggers = new TriggersApiService(this.config);\n this.graphs = new GraphsApiService(this.config);\n this.simulationRuns = new SimulationRunsApiService(this.config);\n this.monitors = new MonitorsApiService({ apiKey, endpoint });\n this.secrets = new SecretsApiService({ apiKey, endpoint });\n this.virtualKeys = new VirtualKeysApiService({ apiKey, endpoint });\n this.gatewayBudgets = new GatewayBudgetsApiService({ apiKey, endpoint });\n this.spendEvents = new SpendEventsApiService({ apiKey, endpoint });\n this.webhooks = new WebhooksApiService({ apiKey, endpoint });\n }\n\n get apiClient(): LangwatchApiClient {\n return this.config.langwatchApiClient;\n }\n\n /**\n * Teams, which group projects and the members who can reach them. These\n * routes want an organization API key.\n *\n * Built on first use rather than in the constructor: the management\n * families resolve their credential when constructed and refuse an empty\n * one, so building this eagerly would make `new LangWatch()` throw for\n * every caller that never touches a team.\n */\n get teams(): TeamsApiService {\n this.#teams ??= new TeamsApiService(this.#managementConfig());\n return this.#teams;\n }\n\n /**\n * Projects, including provisioning one with its own service API key. These\n * routes want an organization API key.\n */\n get projects(): ProjectsApiService {\n this.#projects ??= new ProjectsApiService(this.#managementConfig());\n return this.#projects;\n }\n\n /**\n * What the management families are constructed with. An empty key is left\n * off entirely so they resolve the ambient credential instead of\n * authenticating with a blank bearer token.\n */\n #managementConfig(): { apiKey?: string; endpoint: string } {\n return {\n ...(this.config.apiKey ? { apiKey: this.config.apiKey } : {}),\n endpoint: this.config.endpoint,\n };\n }\n\n #createInternalConfig({\n apiKey,\n endpoint,\n options,\n }: {\n apiKey: string;\n endpoint: string;\n options?: LangWatchConstructorOptions[\"options\"];\n }): InternalConfig & { endpoint: string; apiKey: string } {\n return {\n logger: options?.logger ?? new NoOpLogger(),\n langwatchApiClient: createLangWatchApiClient(apiKey, endpoint),\n endpoint,\n apiKey,\n };\n }\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Receiver-side verification of a LangWatch webhook delivery.\n *\n * Every delivery carries\n *\n * X-LangWatch-Signature: t=<unix seconds>,v1=<hex hmac-sha256>[,v1=<hex>]\n *\n * where each `v1` is HMAC-SHA256 over `\"<t>.<raw body>\"` under one currently\n * valid signing secret. `v1` REPEATS during a secret rotation, newest first,\n * which is what lets a receiver swap secrets on its own schedule instead of\n * dropping deliveries mid-swap.\n *\n * That repetition is the reason this helper exists. A hand-rolled parser that\n * keeps the LAST `v1` it sees, or splits the header into a flat key/value map,\n * rejects every delivery to a receiver that has already moved to the new\n * secret: the signature it kept is the one computed from the OLD secret. The\n * bug only appears during a rotation, which is exactly when a receiver can\n * least afford to be dropping deliveries.\n *\n * The algorithm here is pinned to the sender's by the vectors in\n * `specs/webhooks/signature-vectors.json`, generated from the server's own\n * signing code and asserted by the suite next to this file.\n */\n\n/** The header a delivery carries its signature in. */\nexport const WEBHOOK_SIGNATURE_HEADER = \"X-LangWatch-Signature\";\n\n/**\n * Identifies one delivery ATTEMPT on the webhook platform's endpoints.\n *\n * The natural idempotency key for a receiver: retries of the same batch repeat\n * it, so a receiver that has already processed this id can acknowledge and\n * stop rather than applying the batch twice.\n */\nexport const WEBHOOK_DELIVERY_ID_HEADER = \"X-LangWatch-Delivery-Id\";\n\n/**\n * The same role on automation deliveries (graph alerts and friends), which\n * group their attempts by the logical fire rather than by the batch.\n *\n * Two names because they are two senders: read whichever the delivery carries.\n */\nexport const WEBHOOK_EVENT_ID_HEADER = \"X-LangWatch-Event-Id\";\n\n/**\n * How far a delivery's timestamp may sit from the receiver's clock, in\n * seconds. Matches the sender's documented window.\n */\nexport const WEBHOOK_SIGNATURE_DEFAULT_TOLERANCE_SECONDS = 300;\n\n/**\n * Why a delivery was refused. Switch on this rather than on the message: the\n * message is written for a human reading a log and will change, the code is\n * the contract.\n *\n * The three mean genuinely different things to an operator. `stale_timestamp`\n * is a clock or a replay and is worth alerting on; `invalid_signature` is a\n * wrong secret or a tampered body; `malformed_header` is almost always\n * something other than LangWatch posting to the URL.\n */\nexport type WebhookSignatureFailureCode =\n | \"malformed_header\"\n | \"stale_timestamp\"\n | \"invalid_signature\";\n\n/**\n * A delivery that did not verify.\n *\n * One class carrying a `code` rather than three classes, because the SDK and\n * the platform both ask callers to branch on a stable code instead of on the\n * error's identity, which does not survive a serialization boundary.\n */\nexport class WebhookSignatureVerificationError extends Error {\n readonly code: WebhookSignatureFailureCode;\n\n constructor(code: WebhookSignatureFailureCode, message: string) {\n super(message);\n this.name = \"WebhookSignatureVerificationError\";\n this.code = code;\n }\n}\n\nexport interface VerifyWebhookSignatureOptions {\n /**\n * The EXACT bytes of the request body, as received.\n *\n * Not a parsed object, and not the result of re-serializing one: the digest\n * is over the bytes the sender hashed, and `JSON.parse` followed by\n * `JSON.stringify` reorders keys, drops insignificant whitespace and\n * re-escapes non-ASCII, any of which changes the digest. Read the raw body\n * before your framework's JSON middleware does.\n */\n body: string | Uint8Array;\n /** The `X-LangWatch-Signature` header value, verbatim. */\n header: string;\n /**\n * The signing secret, or every secret this receiver currently accepts.\n *\n * Pass both values during a rotation and the delivery verifies under\n * either, so there is no window where deliveries are refused.\n */\n secret: string | readonly string[];\n /**\n * Freshness window in seconds, defaulting to the sender's five minutes.\n * Tighten it only if your clocks are disciplined.\n */\n toleranceSeconds?: number;\n /**\n * The current time in unix SECONDS. Defaults to the system clock; pass it\n * to verify a delivery captured earlier, or from a test.\n */\n nowSeconds?: number;\n}\n\n/** The timestamp and EVERY `v1` the header carries, in the order sent. */\nfunction parseSignatureHeader(header: string): {\n timestamp: number | undefined;\n candidates: string[];\n} {\n let timestamp: number | undefined;\n const candidates: string[] = [];\n for (const piece of header.split(\",\")) {\n const eq = piece.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = piece.slice(0, eq).trim();\n const value = piece.slice(eq + 1).trim();\n // Every `v1` is kept. Keeping only one is the rotation bug this helper\n // exists to make impossible.\n if (key === \"v1\") candidates.push(value);\n else if (key === \"t\" && value.length > 0) timestamp = Number(value);\n }\n return { timestamp, candidates };\n}\n\n/** Constant-time equality over two hex digests, length-safe. */\nfunction digestsMatch(expected: string, candidate: string): boolean {\n const a = Buffer.from(expected, \"utf8\");\n const b = Buffer.from(candidate, \"utf8\");\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction signedPayload(timestamp: number, body: string | Uint8Array): Buffer {\n const prefix = Buffer.from(`${timestamp}.`, \"utf8\");\n const bytes =\n typeof body === \"string\" ? Buffer.from(body, \"utf8\") : Buffer.from(body);\n return Buffer.concat([prefix, bytes]);\n}\n\n/**\n * Verify a webhook delivery, or throw explaining which check failed.\n *\n * ```ts\n * app.post(\"/langwatch\", express.raw({ type: \"application/json\" }), (req, res) => {\n * try {\n * verifyWebhookSignature({\n * body: req.body, // the raw Buffer, before JSON parsing\n * header: req.header(\"X-LangWatch-Signature\") ?? \"\",\n * secret: [process.env.WEBHOOK_SECRET_NEW, process.env.WEBHOOK_SECRET_OLD],\n * });\n * } catch (error) {\n * return res.status(400).send((error as WebhookSignatureVerificationError).code);\n * }\n * // Trusted from here.\n * });\n * ```\n *\n * Throws rather than returning false so that a delivery cannot be trusted by\n * forgetting to check a return value. The thrown\n * {@link WebhookSignatureVerificationError} carries a\n * {@link WebhookSignatureFailureCode} saying which check failed.\n *\n * Checks run in a fixed order, so a delivery that is both stale and wrongly\n * signed reports the staleness: a header that did not parse has no\n * trustworthy timestamp to judge, and a timestamp outside the window makes\n * the digest moot.\n *\n * A missing or empty secret is a configuration mistake rather than a bad\n * delivery, and raises `TypeError`. Reporting it as a failed verification\n * would let a receiver that lost its secret quietly refuse every delivery as\n * if the sender were at fault.\n */\nexport function verifyWebhookSignature(\n options: VerifyWebhookSignatureOptions,\n): void {\n const secrets = (\n typeof options.secret === \"string\" ? [options.secret] : options.secret\n ).filter((secret) => typeof secret === \"string\" && secret.length > 0);\n if (secrets.length === 0) {\n throw new TypeError(\n \"verifyWebhookSignature needs at least one non-empty signing secret\",\n );\n }\n\n const tolerance =\n options.toleranceSeconds ?? WEBHOOK_SIGNATURE_DEFAULT_TOLERANCE_SECONDS;\n const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);\n\n const { timestamp, candidates } = parseSignatureHeader(options.header);\n if (timestamp === undefined || !Number.isFinite(timestamp)) {\n throw new WebhookSignatureVerificationError(\n \"malformed_header\",\n `${WEBHOOK_SIGNATURE_HEADER} carries no readable t= timestamp, so the delivery cannot be checked for freshness`,\n );\n }\n if (candidates.length === 0) {\n throw new WebhookSignatureVerificationError(\n \"malformed_header\",\n `${WEBHOOK_SIGNATURE_HEADER} carries no v1= signature, so there is nothing to compare`,\n );\n }\n\n if (Math.abs(now - timestamp) > tolerance) {\n throw new WebhookSignatureVerificationError(\n \"stale_timestamp\",\n `the delivery was signed ${Math.abs(now - timestamp)}s from now, outside the ${tolerance}s tolerance`,\n );\n }\n\n const payload = signedPayload(timestamp, options.body);\n let matched = false;\n for (const secret of secrets) {\n const expected = createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\n for (const candidate of candidates) {\n // Every pair is compared even once one has matched, so the work does\n // not depend on WHICH secret or which v1 was the right one.\n if (digestsMatch(expected, candidate)) matched = true;\n }\n }\n\n if (!matched) {\n throw new WebhookSignatureVerificationError(\n \"invalid_signature\",\n `no v1 signature in ${WEBHOOK_SIGNATURE_HEADER} matched the ${secrets.length === 1 ? \"secret\" : \"secrets\"} held, so the body was signed with something else or changed in transit`,\n );\n }\n}\n","import { ConsoleLogger, NoOpLogger } from \"./logger\";\n\nexport {\n getLangWatchTracer,\n getLangWatchLogger,\n attributes,\n} from \"./observability-sdk\";\n\nexport {\n FilterableBatchSpanProcessor,\n type SpanProcessingExcludeRule,\n} from \"./observability-sdk/processors\";\nexport { LangWatchExporter } from \"./observability-sdk/exporters\";\nexport { LangWatch, FetchPolicy, type GetPromptOptions } from \"./client-sdk\";\n\n/**\n * Typed API failures.\n *\n * When the platform DECLINES a request it says why, in a structure: a `code`\n * you can switch on, the `meta` that makes it actionable, and the trace id to\n * quote at support. Narrow with `isLangWatchHandledError` and match the `code`\n * rather than the message — the message is written for humans and may change;\n * the code is the contract.\n *\n * ```ts\n * try {\n * await langwatch.prompts.get(\"nope\");\n * } catch (error) {\n * if (isLangWatchHandledError(error) && error.code === \"prompt_not_found\") {\n * // ...\n * }\n * throw error;\n * }\n * ```\n *\n * Failures the platform did NOT name — a 5xx, a dead socket, a proxy's HTML —\n * still arrive as the generic errors they always did. A domain error means the\n * platform understood you and said no; anything else means it fell over, and\n * the two must not look alike.\n */\nexport {\n LangWatchHandledError,\n isLangWatchHandledError,\n LangWatchApiError,\n} from \"./internal/api/errors\";\nexport type {\n CliHandledError as LangWatchHandledErrorShape,\n CliHandledErrorReason as LangWatchHandledErrorReason,\n} from \"@langwatch/langy/cards/handled-error\";\n\n// Experiments API exports\nexport {\n Experiment,\n ExperimentsFacade,\n type EvaluationStatus as ExperimentEvaluationStatus,\n type TargetType,\n type TargetMetadata,\n type TargetInfo,\n type EvaluationResult as ExperimentEvaluationResult,\n type ComparisonMetric,\n type ComparisonOptions,\n type ComparisonStatus,\n type ComparisonVerdict,\n type ExperimentInitOptions,\n type LogOptions,\n type EvaluateOptions as ExperimentEvaluateOptions,\n type RunOptions,\n type RunCallback,\n type RunContext,\n type RunWithResultsOptions,\n type ExperimentRowResult,\n type ExperimentRunWithResults,\n ExperimentError,\n ExperimentInitError,\n ExperimentApiError,\n TargetMetadataConflictError,\n ComparisonError,\n EvaluatorError,\n} from \"./client-sdk/services/experiments\";\n\n// Evaluators API exports\nexport {\n EvaluatorsApiService,\n type EvaluatorResponse,\n type EvaluatorField,\n type CreateEvaluatorBody,\n EvaluatorsApiError,\n} from \"./client-sdk/services/evaluators\";\n\n// Dataset API exports\nexport {\n DatasetsFacade,\n DatasetError,\n DatasetNotFoundError,\n DatasetApiError,\n DatasetValidationError,\n DatasetPlanLimitError,\n type Dataset,\n type DatasetEntry,\n type DatasetMetadata,\n type DatasetColumnType,\n type DatasetListItem,\n type Pagination,\n type PaginatedResponse,\n type GetDatasetOptions,\n type ListDatasetsOptions,\n type ListDatasetsApiResponse,\n type ListRecordsOptions,\n type ListRecordsApiResponse,\n type CreateDatasetOptions,\n type UpdateDatasetOptions,\n type CreateFromUploadResponse,\n type BatchCreateRecordsResponse,\n type DeleteRecordsResponse,\n type UploadResponse,\n type DatasetRecordResponse,\n} from \"./client-sdk/services/datasets\";\n\n// Evaluations API exports (Online Evaluations / Guardrails)\nexport {\n EvaluationsFacade,\n type EvaluationResult,\n type EvaluateOptions,\n type EvaluationStatus,\n type EvaluationCost,\n EvaluationError,\n EvaluatorCallError,\n EvaluatorNotFoundError,\n EvaluationsApiError,\n} from \"./client-sdk/services/evaluations\";\n\n/**\n * The per-call options every mutating call on the gateway and webhook\n * surfaces accepts, including the idempotency key that makes a create safe to\n * retry after a timeout.\n */\nexport {\n IDEMPOTENCY_KEY_HEADER,\n IDEMPOTENT_REPLAY_HEADER,\n type MutationOptions,\n type IdempotentCreateOptions,\n} from \"./client-sdk/services/_shared/mutation-options\";\n\n// AI Gateway management API exports (virtual keys + budgets)\nexport {\n VirtualKeysApiService,\n VirtualKeysApiError,\n type VirtualKey,\n type VirtualKeyScope,\n type VirtualKeyScopeType,\n type VirtualKeyRoutingMode,\n type VirtualKeyBudgetInput,\n type VirtualKeyWithSecret,\n type VirtualKeyPage,\n type VirtualKeySpendSummary,\n type CreateVirtualKeyInput,\n type UpdateVirtualKeyInput,\n} from \"./client-sdk/services/virtual-keys/virtual-keys-api.service\";\nexport {\n WebhooksApiService,\n WebhooksApiError,\n type WebhookEndpointSummary,\n type WebhookEndpointWithSecret,\n type WebhookDeliveryRecord,\n type WebhookDeliveryPage,\n type WebhookTestResult,\n type WebhookEndpointHealth,\n type WebhookEventType,\n type EmittedEvent,\n type EmittedEventsPage,\n type CreateWebhookEndpointInput,\n type UpdateWebhookEndpointInput,\n} from \"./client-sdk/services/webhooks/webhooks-api.service\";\n/**\n * Receiving webhooks, which needs no API client at all: a receiver holds a\n * signing secret and the raw request, and has to decide whether to trust it.\n */\nexport {\n verifyWebhookSignature,\n WebhookSignatureVerificationError,\n WEBHOOK_SIGNATURE_HEADER,\n WEBHOOK_SIGNATURE_DEFAULT_TOLERANCE_SECONDS,\n WEBHOOK_DELIVERY_ID_HEADER,\n WEBHOOK_EVENT_ID_HEADER,\n type WebhookSignatureFailureCode,\n type VerifyWebhookSignatureOptions,\n} from \"./client-sdk/services/webhooks/verify-signature\";\nexport {\n SpendEventsApiService,\n SpendEventsApiError,\n type SpendEvent,\n type SpendEventsPage,\n type SpendSummaryRow,\n type SpendSummariesPage,\n type SpendEventStatus,\n type SpendSummaryStatus,\n type SpendGroupBy,\n type SpendFilterOptions,\n type SpendSummariesOptions,\n type SpendReplayResult,\n type EndUserSpend,\n type EndUserCap,\n} from \"./client-sdk/services/spend-events/spend-events-api.service\";\nexport {\n GatewayBudgetsApiService,\n GatewayBudgetsApiError,\n type GatewayBudget,\n type GatewayBudgetPage,\n type BudgetScopeKind,\n type BudgetWindow,\n type BudgetOnBreach,\n type CreateGatewayBudgetScope,\n type CreateGatewayBudgetInput,\n type UpdateGatewayBudgetInput,\n} from \"./client-sdk/services/gateway-budgets/gateway-budgets-api.service\";\n\n/**\n * Provisioning teams and projects, the two things an integration has to\n * create before anything else exists to write to. Both families want an\n * organization API key; creating a project also mints that project's own\n * service API key, served once in the create response.\n */\nexport {\n TeamsApiService,\n TeamsApiError,\n type Team,\n type TeamPagination,\n type TeamMember,\n type ListTeamsResponse,\n type ArchivedTeam,\n} from \"./client-sdk/services/teams/teams-api.service\";\nexport {\n ProjectsApiService,\n ProjectsApiError,\n type Project,\n type PaginatedProjects,\n type ProjectWithServiceKey,\n type ArchivedProject,\n type CreateProjectInput,\n type UpdateProjectInput,\n} from \"./client-sdk/services/projects/projects-api.service\";\n\nexport const logger = {\n ConsoleLogger,\n NoOpLogger,\n};\n\n/**\n * The HTTP client every SDK request to the LangWatch API goes through. It\n * follows a redirect only when it upgrades http to https on the same URL and\n * refuses every other one with `LangWatchRedirectError`. `createLangWatchFetch`\n * builds one over another transport or logger.\n */\nexport {\n langwatchFetch,\n createLangWatchFetch,\n LangWatchRedirectError,\n type LangWatchFetch,\n type CreateLangWatchFetchOptions,\n} from \"./internal/http/langwatchFetch\";\n"]}
1
+ {"version":3,"sources":["/home/runner/work/langwatch/langwatch/sdks/typescript/dist/index.js","../src/internal/credentialContext.ts","../src/client-sdk/services/prompts/errors/prompt-compilation.error.ts","../src/client-sdk/services/prompts/errors/prompt-validation.error.ts","../src/client-sdk/services/prompts/errors/prompts.error.ts","../src/client-sdk/services/prompts/errors/prompts-api.error.ts","../src/cli/utils/errors/prompt-not-found.error.ts","../src/cli/utils/fileManager.ts","../../../node_modules/.pnpm/chalk@6.0.0/node_modules/chalk/source/utilities.js","../../../node_modules/.pnpm/chalk@6.0.0/node_modules/chalk/source/vendor/ansi-styles/index.js","../../../node_modules/.pnpm/chalk@6.0.0/node_modules/chalk/source/vendor/supports-color/index.js","../../../node_modules/.pnpm/chalk@6.0.0/node_modules/chalk/source/index.js","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/str.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/null_core.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/null_json.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/null_yaml11.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/bool_core.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/bool_json.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/bool_yaml11.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/int_core.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/int_json.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/int_yaml11.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/float_core.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/float_json.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/float_yaml11.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/merge.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/binary.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/scalar/timestamp.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/sequence/seq.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/common/object.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/sequence/omap.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/sequence/pairs.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/mapping/map.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/mapping/set.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/schema.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/mapping/real_map.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/tag/mapping/legacy_map.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/common/snippet.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/common/exception.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/parser/events.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/parser/parser_scalar.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/common/tagname.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/parser/constructor.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/parser/parser.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/load.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/ast/from_js.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/ast/visit.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/ast/styler_defaults.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/ast/scalar_styler.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/ast/presenter.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/dump.ts","../../../node_modules/.pnpm/js-yaml@5.4.3/node_modules/js-yaml/src/index.ts","../src/cli/utils/responseFormat.ts","../src/cli/utils/promptConverter.ts","../src/client-sdk/services/_shared/error-code-copy.ts","../src/client-sdk/services/_shared/format-api-error.ts","../src/cli/types-prompt.ts","../src/client-sdk/services/prompts/local-prompts.service.ts","../src/client-sdk/services/prompts/prompt.ts","../src/client-sdk/tracing/create-tracing-proxy.ts","../src/client-sdk/services/prompts/prompt-data.ts","../src/client-sdk/services/prompts/constants.ts","../src/client-sdk/services/prompts/schema/prompt.schema.ts","../src/client-sdk/services/prompts/tracing/prompt-tracing.decorator.ts","../src/client-sdk/services/prompts/tracing/prompt-service-tracing.decorator.ts","../src/client-sdk/services/prompts/tracing/tracer.ts","../src/client-sdk/services/_shared/unwrap-api-result.ts","../src/internal/api/client.ts","../src/internal/http/langwatchFetch.ts","../../../packages/handled-error/src/langwatch-handled-error.ts","../src/internal/api/errors.ts","../src/internal/surface.ts","../src/internal/api/request-headers.ts","../src/client-sdk/services/prompts/prompts-api.service.ts","../src/client-sdk/services/prompts/types.ts","../src/client-sdk/services/prompts/prompts.facade.ts","../src/client-sdk/services/datasets/errors.ts","../src/client-sdk/services/datasets/paged-records.ts","../src/client-sdk/services/datasets/tracing/tracer.ts","../src/client-sdk/services/datasets/dataset.service.ts","../src/client-sdk/services/datasets/datasets.facade.ts","../src/client-sdk/services/experiments/experiment.ts","../src/client-sdk/services/experiments/errors/experiment.error.ts","../src/client-sdk/services/experiments/comparison.ts","../src/client-sdk/services/experiments/printSummary.ts","../src/client-sdk/services/experiments/experiment-summary.ts","../src/client-sdk/services/experiments/humanReadableId.ts","../src/client-sdk/services/experiments/log-results-batching.ts","../src/client-sdk/services/experiments/experiments-api.service.ts","../src/client-sdk/services/experiments/mapResults.ts","../src/client-sdk/services/experiments/platformErrors.ts","../src/client-sdk/services/experiments/run-status.ts","../src/client-sdk/services/experiments/experiments.facade.ts","../src/client-sdk/services/evaluations/evaluations.facade.ts","../src/client-sdk/services/evaluations/errors.ts","../src/client-sdk/services/_shared/throw-handled-error.ts","../src/client-sdk/services/evaluators/errors.ts","../src/client-sdk/services/evaluators/evaluators-api.service.ts","../src/client-sdk/services/scenarios/errors.ts","../src/client-sdk/services/scenarios/scenarios-api.service.ts","../src/client-sdk/services/suites/suites-api.service.ts","../src/client-sdk/services/run-plans/run-plans-api.service.ts","../src/client-sdk/services/instant-evals/instant-evals-api.service.ts","../src/client-sdk/services/test-suites/test-suites-api.service.ts","../src/client-sdk/services/workflows/workflows-api.service.ts","../src/client-sdk/services/agents/agents-api.service.ts","../src/client-sdk/services/annotations/annotations-api.service.ts","../src/client-sdk/services/dashboards/dashboards-api.service.ts","../src/client-sdk/services/analytics-reports/analytics-reports-api.service.ts","../src/client-sdk/services/model-providers/model-providers-api.service.ts","../src/client-sdk/services/analytics/analytics-api.service.ts","../src/client-sdk/services/query/query-api.service.ts","../src/client-sdk/services/triggers/triggers-api.service.ts","../src/client-sdk/services/graphs/graphs-api.service.ts","../src/client-sdk/services/simulation-runs/simulation-runs-api.service.ts","../src/client-sdk/services/traces/traces-api.service.ts","../src/client-sdk/services/_shared/merge-headers.ts","../src/client-sdk/services/monitors/monitors-api.service.ts","../src/client-sdk/services/secrets/secrets-api.service.ts","../src/client-sdk/services/_shared/collect-cursor-pages.ts","../src/client-sdk/services/_shared/mutation-options.ts","../src/client-sdk/services/virtual-keys/virtual-keys-api.service.ts","../src/client-sdk/services/gateway-budgets/gateway-budgets-api.service.ts","../src/client-sdk/services/spend-events/spend-events-api.service.ts","../src/client-sdk/services/webhooks/webhooks-api.service.ts","../src/client-sdk/services/_shared/management-request.ts","../src/client-sdk/services/teams/teams-api.service.ts","../src/client-sdk/services/projects/projects-api.service.ts","../src/client-sdk/services/traces/tracing/tracer.ts","../src/client-sdk/services/traces/facade.ts","../src/client-sdk/index.ts","../src/client-sdk/services/webhooks/verify-signature.ts","../src/index.ts"],"names":["process","chalk","date","tag","strTag","node","path","logger","trace","FetchPolicy","errorMessage","AsyncLocalStorage","tracer","verdict","_b","_a","resolve","SpanStatusCode"],"mappings":"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C/B,EAAA;AACI,EAAA;AACZ,EAAA;AACtC;AAE6B;AAC3B,EAAA;AACA,EAAA;AACA,EAAA;AAKU;AACI,EAAA;AAEgB,EAAA;AACkB,IAAA;AACV,IAAA;AAGK,MAAA;AACzC,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAOqF;AA7IrF,EAAA;AA8I6C,EAAA;AACC,IAAA;AAC5C,EAAA;AAC0B,EAAA;AACJ,IAAA;AACtB,EAAA;AAE0B,EAAA;AACd,EAAA;AAEsB,EAAA;AACc,EAAA;AACpC,IAAA;AACR,MAAA;AAC2B,MAAA;AAC7B,IAAA;AACF,EAAA;AAC6B,EAAA;AACjB,IAAA;AACR,MAAA;AAC2B,MAAA;AAC7B,IAAA;AACF,EAAA;AAE2C,EAAA;AAC/B,IAAA;AACR,MAAA;AAC0C,MAAA;AAC5C,IAAA;AACF,EAAA;AAEiD,EAAA;AACF,EAAA;AAEjC,EAAA;AACF,IAAA;AACR,MAAA;AACsB,MAAA;AACxB,IAAA;AACF,EAAA;AACF;ApI+hYmD;AACA;AqI7/X7B;AACpB,EAAA;AACA,EAAA;AACF;ArI+/XmD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/langwatch/langwatch/sdks/typescript/dist/index.js","sourcesContent":[null,"/**\n * Request-scoped resolved credentials: the CLI daemon serves concurrent\n * requests from one process, so a resolved key in `process.env` would leak\n * across requests -- this holder makes that structurally impossible.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\ninterface CredentialHolder {\n apiKey?: string;\n projectId?: string;\n requestedProject?: string;\n runsOutsideProject?: boolean;\n warnedProjectEnvIgnored?: boolean;\n /**\n * Set to \"cli\" at the CLI's request boundaries (runWithCliCredentialHolder,\n * below). A plain SDK embed never sets this, so its holder — scoped or\n * fallback — always reads undefined here.\n */\n surface?: \"cli\";\n}\n\nconst storage = new AsyncLocalStorage<CredentialHolder>();\n\n/**\n * Process-local fallback for callers not wrapped in a holder scope: a plain\n * SDK embed, or a cold-CLI path that skipped the wrapper. Never used inside\n * the daemon, where every request runs in its own holder.\n */\nconst fallbackHolder: CredentialHolder = {};\n\nfunction currentHolder(): CredentialHolder {\n return storage.getStore() ?? fallbackHolder;\n}\n\n/**\n * Run `fn` inside a fresh, empty credential holder. Called once at each request\n * boundary (in-process dispatch, and per daemon request). The resolver fills\n * the holder later; everything the request constructs afterward reads it.\n */\nexport function runWithCredentialHolder<T>(fn: () => T): T {\n return storage.run({}, fn);\n}\n\n/**\n * `runWithCredentialHolder` that marks the holder as a CLI request, so the shared\n * request-header builders attach `x-langwatch-surface: cli` (cli/daemon/dispatch.ts,\n * cli/daemon/execution.ts). Spec: specs/observability/traffic-attribution.feature\n */\nexport function runWithCliCredentialHolder<T>({ fn }: { fn: () => T }): T {\n return runWithCredentialHolder(() => {\n setScopedSurface({ surface: \"cli\" });\n return fn();\n });\n}\n\n/**\n * Publish the resolved API key for the current request into its holder. No\n * process-global state is touched, so concurrent requests never collide.\n */\nexport function setResolvedApiKey(apiKey: string): void {\n currentHolder().apiKey = apiKey;\n}\n\n/**\n * The API key resolved for the current request, or undefined when nothing has\n * resolved in this context. Client factories fall back to the environment\n * then, exactly as a plain SDK embed does.\n */\nexport function scopedApiKey(): string | undefined {\n return currentHolder().apiKey;\n}\n\n/**\n * Publishes the project the current request targets. A user-scoped API key\n * carries no project identity, so the resolver decides which project the\n * request names, and every client built afterwards reads it from here.\n */\nexport function setResolvedProjectId(projectId: string | undefined): void {\n currentHolder().projectId = projectId;\n}\n\n/**\n * The project id resolved for the current request, or undefined when nothing\n * has resolved in this context. Client factories fall back to\n * `LANGWATCH_PROJECT_ID` then, exactly as a plain SDK embed does.\n */\nexport function scopedProjectId(): string | undefined {\n return currentHolder().projectId;\n}\n\n/**\n * Publish the project the command line named (as typed) before any credential resolves. Set from\n * `preAction` so commands inherit `--project`; the resolver reads it with `requestedProject()`.\n */\nexport function setRequestedProject(selector: string | undefined): void {\n currentHolder().requestedProject = selector;\n}\n\n/**\n * The project selector the command line asked for in this request, or\n * undefined when it named none. Unresolved on purpose: the resolver decides\n * what a selector means, and it is the only thing that can.\n */\nexport function requestedProject(): string | undefined {\n return currentHolder().requestedProject;\n}\n\n/**\n * Publish whether the command about to run answers outside any project (machine-local or\n * organization-scoped), so nothing tells its user which project it reads.\n */\nexport function setRunsOutsideProject(outsideProject: boolean): void {\n currentHolder().runsOutsideProject = outsideProject;\n}\n\n/** Whether the current request's command answers outside any project; false when nothing said. */\nexport function runsOutsideProject(): boolean {\n return currentHolder().runsOutsideProject === true;\n}\n\n/**\n * Take the once-per-request right to warn that `LANGWATCH_PROJECT_ID` was ignored. Request-scoped\n * because the daemon serves many requests from one process; a module flag would warn only the\n * first.\n */\nexport function claimProjectEnvIgnoredWarning(): boolean {\n const holder = currentHolder();\n if (holder.warnedProjectEnvIgnored) return false;\n holder.warnedProjectEnvIgnored = true;\n return true;\n}\n\n/**\n * Mark the current request's holder as a CLI request. Read by\n * the shared request-header builders when attaching the surface header.\n */\nexport function setScopedSurface({ surface }: { surface: \"cli\" }): void {\n currentHolder().surface = surface;\n}\n\n/**\n * The surface recorded for the current request, or undefined outside a CLI\n * request boundary (a plain SDK embed, or a request scope that never called\n * `runWithCliCredentialHolder`).\n */\nexport function scopedSurface(): \"cli\" | undefined {\n return currentHolder().surface;\n}\n\n/**\n * Clear the process-local fallback holder. Test-only: a unit test that sets a\n * key outside any scope would otherwise leak it into the next test.\n */\nexport function resetFallbackCredentialHolder(): void {\n fallbackHolder.apiKey = undefined;\n fallbackHolder.projectId = undefined;\n fallbackHolder.requestedProject = undefined;\n fallbackHolder.warnedProjectEnvIgnored = undefined;\n fallbackHolder.surface = undefined;\n}\n","/**\n * Error class for template compilation issues\n */\nexport class PromptCompilationError extends Error {\n constructor(\n message: string,\n public readonly template: string,\n public readonly originalError?: any,\n ) {\n super(message);\n this.name = \"PromptCompilationError\";\n }\n}\n","import { type z } from \"zod\";\n\n/**\n * Error class for prompt validation issues\n */\nexport class PromptValidationError extends Error {\n constructor(\n message: string,\n public readonly validationErrors: z.ZodError,\n ) {\n super(message);\n this.name = \"PromptValidationError\";\n }\n}\n","/**\n * Base error class for the Prompts domain.\n * All prompt-related errors should extend this class.\n */\nexport class PromptsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PromptsError\";\n }\n}\n","import { PromptsError } from \"./prompts.error\";\n\n/**\n * Error class for Prompts API operations.\n * Provides context about the failed operation and the original error.\n */\nexport class PromptsApiError extends PromptsError {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"PromptsApiError\";\n }\n}\n","export class PromptFileNotFoundError extends Error {\n constructor(filePath: string) {\n super(`Local prompt file not found: ${filePath}`);\n this.name = \"PromptFileNotFoundError\";\n }\n}\n","import * as fs from \"fs\";\nimport * as path from \"path\";\n\nimport chalk from \"chalk\";\nimport * as yaml from \"js-yaml\";\n\nimport { PromptConverter } from \"@/cli/utils/promptConverter\";\nimport { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\n\nimport type { PromptsConfig, LocalPromptConfig, MaterializedPrompt, PromptsLock } from \"../types\";\nimport { localPromptConfigSchema } from \"../types-prompt\";\nimport { PromptFileNotFoundError } from \"./errors/prompt-not-found.error\";\n\nexport class FileManager {\n private static readonly PROMPTS_CONFIG_FILE = \"prompts.json\";\n private static readonly PROMPTS_LOCK_FILE = \"prompts-lock.json\";\n private static readonly PROMPTS_DIR = \"prompts\";\n private static readonly MATERIALIZED_DIR = \".materialized\";\n\n private static _projectRoot: string | undefined;\n /** The cwd `_projectRoot` was derived from. Cache must be keyed by it or\n * multi-directory processes like daemons will use the wrong root. */\n private static _projectRootCwd: string | undefined;\n\n /** Reset the cached project root. Tests use this to exercise different cwds. */\n static _resetProjectRootCache(): void {\n this._projectRoot = undefined;\n this._projectRootCwd = undefined;\n }\n\n private static cacheProjectRoot(cwd: string, root: string): string {\n this._projectRoot = root;\n this._projectRootCwd = cwd;\n return root;\n }\n\n private static readonly PROJECT_MARKERS = [\".git\", \"package.json\", \"pyproject.toml\"];\n\n private static hasProjectMarker(dir: string): boolean {\n return this.PROJECT_MARKERS.some((m) => fs.existsSync(path.join(dir, m)));\n }\n\n private static findProjectRoot(): string {\n const cwd = process.cwd();\n\n if (this._projectRoot && this._projectRootCwd === cwd) {\n return this._projectRoot;\n }\n\n const fsRoot = path.parse(cwd).root;\n\n // Find the highest ancestor that still looks like part of \"this project\"\n // (has package.json / pyproject.toml / .git). If cwd itself has no marker,\n // we don't walk up at all — prevents grabbing a stray prompts.json from\n // /tmp or other shared scratch dirs.\n let topProjectDir: string | null = null;\n let scan = cwd;\n while (scan !== fsRoot) {\n if (this.hasProjectMarker(scan)) {\n topProjectDir = scan;\n }\n scan = path.dirname(scan);\n }\n\n if (!topProjectDir) {\n return this.cacheProjectRoot(cwd, cwd);\n }\n\n // Walk up from cwd looking for prompts.json, capped at the project boundary.\n let currentDir = cwd;\n while (true) {\n const configPath = path.join(currentDir, this.PROMPTS_CONFIG_FILE);\n if (fs.existsSync(configPath)) {\n return this.cacheProjectRoot(cwd, currentDir);\n }\n if (currentDir === topProjectDir) break;\n currentDir = path.dirname(currentDir);\n }\n\n // No prompts.json anywhere within the project — default to cwd so `init`\n // creates it where the user actually ran the command. This also keeps\n // concurrent test workdirs isolated: each tempdir gets its own prompts.json\n // instead of all pointing at a shared repo-root file.\n return this.cacheProjectRoot(cwd, cwd);\n }\n\n static getPromptsConfigPath(): string {\n return path.join(this.findProjectRoot(), this.PROMPTS_CONFIG_FILE);\n }\n\n static getPromptsLockPath(): string {\n return path.join(this.findProjectRoot(), this.PROMPTS_LOCK_FILE);\n }\n\n static getPromptsDir(): string {\n return path.join(this.findProjectRoot(), this.PROMPTS_DIR);\n }\n\n static getMaterializedDir(): string {\n return path.join(this.getPromptsDir(), this.MATERIALIZED_DIR);\n }\n\n static ensureDirectories(): void {\n const promptsDir = this.getPromptsDir();\n const materializedDir = this.getMaterializedDir();\n\n if (!fs.existsSync(promptsDir)) {\n fs.mkdirSync(promptsDir, { recursive: true });\n }\n\n if (!fs.existsSync(materializedDir)) {\n fs.mkdirSync(materializedDir, { recursive: true });\n }\n }\n\n static loadPromptsConfig(): PromptsConfig {\n const configPath = this.getPromptsConfigPath();\n\n if (!fs.existsSync(configPath)) {\n return { prompts: {} };\n }\n\n try {\n const content = fs.readFileSync(configPath, \"utf-8\");\n return JSON.parse(content) as PromptsConfig;\n } catch (error) {\n throw new Error(`Failed to parse prompts.json: ${formatApiErrorMessage({ error })}`);\n }\n }\n\n static savePromptsConfig(config: PromptsConfig): void {\n const configPath = this.getPromptsConfigPath();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n\n static initializePromptsConfig(): { created: boolean; path: string } {\n const configPath = this.getPromptsConfigPath();\n const existed = fs.existsSync(configPath);\n\n if (!existed) {\n const emptyConfig: PromptsConfig = { prompts: {} };\n this.savePromptsConfig(emptyConfig);\n return { created: true, path: configPath };\n }\n\n return { created: false, path: configPath };\n }\n\n static loadPromptsLock = (): PromptsLock => {\n const lockPath = this.getPromptsLockPath();\n\n if (!fs.existsSync(lockPath)) {\n return {\n lockfileVersion: 1,\n prompts: {},\n };\n }\n\n try {\n const content = fs.readFileSync(lockPath, \"utf-8\");\n return JSON.parse(content) as PromptsLock;\n } catch (error) {\n throw new Error(`Failed to parse prompts-lock.json: ${formatApiErrorMessage({ error })}`);\n }\n };\n\n static savePromptsLock(lock: PromptsLock): void {\n const lockPath = this.getPromptsLockPath();\n fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + \"\\n\");\n }\n\n static initializePromptsLock(): { created: boolean; path: string } {\n const lockPath = this.getPromptsLockPath();\n const existed = fs.existsSync(lockPath);\n\n if (!existed) {\n const emptyLock: PromptsLock = {\n lockfileVersion: 1,\n prompts: {},\n };\n this.savePromptsLock(emptyLock);\n return { created: true, path: lockPath };\n }\n\n return { created: false, path: lockPath };\n }\n\n static loadLocalPrompt = (filePath: string): LocalPromptConfig => {\n const fullPath = path.resolve(filePath);\n\n if (!fs.existsSync(fullPath)) {\n throw new PromptFileNotFoundError(filePath);\n }\n\n try {\n const content = fs.readFileSync(fullPath, \"utf-8\");\n const rawData = yaml.load(content);\n\n // Validate with zod and provide nice error messages\n const result = localPromptConfigSchema.safeParse(rawData);\n\n if (!result.success) {\n // Format zod errors nicely (manually since z.prettifyError might not be available)\n const prettyError = result.error.issues\n .map(\n (issue) =>\n `✖ ${issue.message}${issue.path.length > 0 ? `\\n → at ${issue.path.join(\".\")}` : \"\"}`,\n )\n .join(\"\\n\");\n\n throw new Error(\n `Invalid prompt configuration in ${chalk.yellow(filePath)}:\\n${prettyError}`,\n );\n }\n\n return result.data;\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"Invalid prompt configuration\")) {\n throw error; // Re-throw zod validation errors as-is\n }\n throw new Error(\n `Failed to parse local prompt file ${filePath}: ${formatApiErrorMessage({ error })}`,\n );\n }\n };\n\n static saveMaterializedPrompt(name: string, prompt: MaterializedPrompt): string {\n const materializedDir = this.getMaterializedDir();\n const parts = name.split(\"/\");\n const fileName = `${parts[parts.length - 1]}.prompt.yaml`;\n\n // Create nested directories if needed\n if (parts.length > 1) {\n const subDir = path.join(materializedDir, ...parts.slice(0, -1));\n if (!fs.existsSync(subDir)) {\n fs.mkdirSync(subDir, { recursive: true });\n }\n }\n\n const filePath = path.join(materializedDir, ...parts.slice(0, -1), fileName);\n\n // Convert to YAML format using the converter\n const yamlContent = PromptConverter.fromMaterializedToYaml(prompt);\n\n const yamlString = yaml.dump(yamlContent, {\n lineWidth: -1,\n noRefs: true,\n sortKeys: false,\n });\n\n fs.writeFileSync(filePath, yamlString);\n return filePath;\n }\n\n static getLocalPromptFiles = (): string[] => {\n const promptsDir = this.getPromptsDir();\n const materializedDir = this.getMaterializedDir();\n\n if (!fs.existsSync(promptsDir)) {\n return [];\n }\n\n const files: string[] = [];\n\n const walkDir = (dir: string, relativePath = \"\"): void => {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativeFilePath = path.join(relativePath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip the .materialized directory\n if (fullPath === materializedDir) {\n continue;\n }\n walkDir(fullPath, relativeFilePath);\n } else if (entry.isFile() && entry.name.endsWith(\".prompt.yaml\")) {\n files.push(path.join(promptsDir, relativeFilePath));\n }\n }\n };\n\n walkDir(promptsDir);\n return files;\n };\n\n static promptNameFromPath(filePath: string): string {\n const promptsDir = this.getPromptsDir();\n const relativePath = path.relative(promptsDir, filePath);\n return relativePath.replace(/\\.prompt\\.yaml$/, \"\");\n }\n\n static cleanupOrphanedMaterializedFiles(currentDependencies: Set<string>): string[] {\n const materializedDir = this.getMaterializedDir();\n\n if (!fs.existsSync(materializedDir)) {\n return [];\n }\n\n const cleaned: string[] = [];\n\n const cleanupDir = (dir: string, relativePath = \"\"): void => {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativeFilePath = path.join(relativePath, entry.name);\n\n if (entry.isDirectory()) {\n cleanupDir(fullPath, relativeFilePath);\n\n removeEmptyDirectory(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".prompt.yaml\")) {\n // Extract prompt name from materialized file path\n const promptName = relativeFilePath.replace(/\\.prompt\\.yaml$/, \"\");\n\n if (!currentDependencies.has(promptName)) {\n fs.unlinkSync(fullPath);\n cleaned.push(promptName);\n }\n }\n }\n };\n\n cleanupDir(materializedDir);\n return cleaned;\n }\n\n static updateLockEntry(\n lock: PromptsLock,\n name: string,\n prompt: MaterializedPrompt,\n materializedPath: string,\n ): void {\n const relativePath = path.relative(this.findProjectRoot(), materializedPath);\n\n lock.prompts[name] = {\n version: prompt.version,\n versionId: prompt.versionId,\n materialized: relativePath,\n };\n }\n\n static removeFromLock(lock: PromptsLock, names: string[]): void {\n for (const name of names) {\n delete lock.prompts[name];\n }\n }\n\n static addToGitignore(entry: string): { added: boolean; existed: boolean } {\n const gitignorePath = path.join(this.findProjectRoot(), \".gitignore\");\n\n // Check if .gitignore exists\n if (!fs.existsSync(gitignorePath)) {\n // Create new .gitignore with the entry\n fs.writeFileSync(gitignorePath, `${entry}\\n`);\n return { added: true, existed: false };\n }\n\n // Read existing .gitignore\n const content = fs.readFileSync(gitignorePath, \"utf-8\");\n const lines = content.split(\"\\n\").map((line) => line.trim());\n\n // Check if entry already exists\n if (lines.includes(entry)) {\n return { added: false, existed: true };\n }\n\n // Add entry to .gitignore\n const newContent = content.endsWith(\"\\n\") ? `${content}${entry}\\n` : `${content}\\n${entry}\\n`;\n fs.writeFileSync(gitignorePath, newContent);\n\n return { added: true, existed: false };\n }\n}\n\nfunction removeEmptyDirectory(fullPath: string): void {\n // Remove empty directories\n try {\n const dirEntries = fs.readdirSync(fullPath);\n if (dirEntries.length === 0) {\n fs.rmdirSync(fullPath);\n }\n } catch {\n // Directory not empty or other error, ignore\n void 0;\n }\n}\n","// Note: Each match is kept and `postfix` is inserted after it. `String#replaceAll(substring, substring + postfix)` does the same, but it has to scan the replacement for `$` patterns and it is several times slower on the no-match path that most calls take.\nexport function stringReplaceAll(string, substring, postfix) {\n\tlet index = string.indexOf(substring);\n\tif (index === -1) {\n\t\treturn string;\n\t}\n\n\tconst substringLength = substring.length;\n\tlet endIndex = 0;\n\tlet returnValue = '';\n\tdo {\n\t\treturnValue += string.slice(endIndex, index) + substring + postfix;\n\t\tendIndex = index + substringLength;\n\t\tindex = string.indexOf(substring, endIndex);\n\t} while (index !== -1);\n\n\treturnValue += string.slice(endIndex);\n\treturn returnValue;\n}\n\nexport function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {\n\tlet endIndex = 0;\n\tlet returnValue = '';\n\tdo {\n\t\tconst isGotCR = string[index - 1] === '\\r';\n\t\treturnValue += string.slice(endIndex, (isGotCR ? index - 1 : index)) + prefix + (isGotCR ? '\\r\\n' : '\\n') + postfix;\n\t\tendIndex = index + 1;\n\t\tindex = string.indexOf('\\n', endIndex);\n\t} while (index !== -1);\n\n\treturnValue += string.slice(endIndex);\n\treturn returnValue;\n}\n","const ANSI_BACKGROUND_OFFSET = 10;\nconst ANSI_UNDERLINE_OFFSET = 20;\n\nconst wrapAnsi16 = (offset = 0) => code => `\\u{1B}[${code + offset}m`;\n\nconst wrapAnsi256 = (offset = 0) => code => `\\u{1B}[${38 + offset};5;${code}m`;\n\nconst wrapAnsi16m = (offset = 0) => (red, green, blue) => `\\u{1B}[${38 + offset};2;${red};${green};${blue}m`;\n\n// `SGR 58` has no basic 16-color form, so the basic color code is mapped to its palette index instead.\nconst wrapUnderlineAnsi = code => `\\u{1B}[58;5;${code < 90 ? code - 30 : code - 90 + 8}m`;\n\nconst styles = {\n\tmodifier: {\n\t\treset: [0, 0],\n\t\t// 21 isn't widely supported and 22 does the same thing\n\t\tbold: [1, 22],\n\t\tdim: [2, 22],\n\t\titalic: [3, 23],\n\t\tunderline: [4, 24],\n\t\t// Extended underline styles (`SGR 4:x` sub-parameters). Not in upstream `ansi-styles`.\n\t\tunderlineDouble: ['4:2', 24],\n\t\tunderlineCurly: ['4:3', 24],\n\t\tunderlineDotted: ['4:4', 24],\n\t\tunderlineDashed: ['4:5', 24],\n\t\toverline: [53, 55],\n\t\tinverse: [7, 27],\n\t\thidden: [8, 28],\n\t\tstrikethrough: [9, 29],\n\t},\n\tcolor: {\n\t\tblack: [30, 39],\n\t\tred: [31, 39],\n\t\tgreen: [32, 39],\n\t\tyellow: [33, 39],\n\t\tblue: [34, 39],\n\t\tmagenta: [35, 39],\n\t\tcyan: [36, 39],\n\t\twhite: [37, 39],\n\n\t\t// Bright color\n\t\tblackBright: [90, 39],\n\t\tgray: [90, 39], // Alias of `blackBright`\n\t\tgrey: [90, 39], // Alias of `blackBright`\n\t\tredBright: [91, 39],\n\t\tgreenBright: [92, 39],\n\t\tyellowBright: [93, 39],\n\t\tblueBright: [94, 39],\n\t\tmagentaBright: [95, 39],\n\t\tcyanBright: [96, 39],\n\t\twhiteBright: [97, 39],\n\t},\n\tbgColor: {\n\t\tbgBlack: [40, 49],\n\t\tbgRed: [41, 49],\n\t\tbgGreen: [42, 49],\n\t\tbgYellow: [43, 49],\n\t\tbgBlue: [44, 49],\n\t\tbgMagenta: [45, 49],\n\t\tbgCyan: [46, 49],\n\t\tbgWhite: [47, 49],\n\n\t\t// Bright color\n\t\tbgBlackBright: [100, 49],\n\t\tbgGray: [100, 49], // Alias of `bgBlackBright`\n\t\tbgGrey: [100, 49], // Alias of `bgBlackBright`\n\t\tbgRedBright: [101, 49],\n\t\tbgGreenBright: [102, 49],\n\t\tbgYellowBright: [103, 49],\n\t\tbgBlueBright: [104, 49],\n\t\tbgMagentaBright: [105, 49],\n\t\tbgCyanBright: [106, 49],\n\t\tbgWhiteBright: [107, 49],\n\t},\n\t// Underline color (`SGR 58`/`59`). Not in upstream `ansi-styles`.\n\tunderlineColor: {\n\t\tunderlineBlack: ['58;5;0', 59],\n\t\tunderlineRed: ['58;5;1', 59],\n\t\tunderlineGreen: ['58;5;2', 59],\n\t\tunderlineYellow: ['58;5;3', 59],\n\t\tunderlineBlue: ['58;5;4', 59],\n\t\tunderlineMagenta: ['58;5;5', 59],\n\t\tunderlineCyan: ['58;5;6', 59],\n\t\tunderlineWhite: ['58;5;7', 59],\n\n\t\t// Bright color\n\t\tunderlineBlackBright: ['58;5;8', 59],\n\t\tunderlineGray: ['58;5;8', 59], // Alias of `underlineBlackBright`\n\t\tunderlineGrey: ['58;5;8', 59], // Alias of `underlineBlackBright`\n\t\tunderlineRedBright: ['58;5;9', 59],\n\t\tunderlineGreenBright: ['58;5;10', 59],\n\t\tunderlineYellowBright: ['58;5;11', 59],\n\t\tunderlineBlueBright: ['58;5;12', 59],\n\t\tunderlineMagentaBright: ['58;5;13', 59],\n\t\tunderlineCyanBright: ['58;5;14', 59],\n\t\tunderlineWhiteBright: ['58;5;15', 59],\n\t},\n};\n\nexport const modifierNames = Object.keys(styles.modifier);\nexport const foregroundColorNames = Object.keys(styles.color);\nexport const backgroundColorNames = Object.keys(styles.bgColor);\nexport const underlineColorNames = Object.keys(styles.underlineColor);\nexport const colorNames = [...foregroundColorNames, ...backgroundColorNames];\n\nfunction assembleStyles() {\n\tconst codes = new Map();\n\n\tfor (const [groupName, group] of Object.entries(styles)) {\n\t\tfor (const [styleName, style] of Object.entries(group)) {\n\t\t\tstyles[styleName] = {\n\t\t\t\topen: `\\u{1B}[${style[0]}m`,\n\t\t\t\tclose: `\\u{1B}[${style[1]}m`,\n\t\t\t};\n\n\t\t\tgroup[styleName] = styles[styleName];\n\n\t\t\t// Only the leading SGR parameter identifies a style, so `4:3` and `58;5;1` are keyed as `4` and `58`.\n\t\t\tcodes.set(Number.parseInt(style[0], 10), style[1]);\n\t\t}\n\n\t\tObject.defineProperty(styles, groupName, {\n\t\t\tvalue: group,\n\t\t\tenumerable: false,\n\t\t});\n\t}\n\n\tObject.defineProperty(styles, 'codes', {\n\t\tvalue: codes,\n\t\tenumerable: false,\n\t});\n\n\tstyles.color.close = '\\u{1B}[39m';\n\tstyles.bgColor.close = '\\u{1B}[49m';\n\tstyles.underlineColor.close = '\\u{1B}[59m';\n\n\tstyles.color.ansi = wrapAnsi16();\n\tstyles.color.ansi256 = wrapAnsi256();\n\tstyles.color.ansi16m = wrapAnsi16m();\n\tstyles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);\n\tstyles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);\n\tstyles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);\n\tstyles.underlineColor.ansi = wrapUnderlineAnsi;\n\tstyles.underlineColor.ansi256 = wrapAnsi256(ANSI_UNDERLINE_OFFSET);\n\tstyles.underlineColor.ansi16m = wrapAnsi16m(ANSI_UNDERLINE_OFFSET);\n\n\t// From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js\n\tObject.defineProperties(styles, {\n\t\trgbToAnsi256: {\n\t\t\tvalue(red, green, blue) {\n\t\t\t\t// We use the extended greyscale palette here, with the exception of\n\t\t\t\t// black and white. normal palette only has 4 greyscale shades.\n\t\t\t\tif (red === green && green === blue) {\n\t\t\t\t\tif (red < 8) {\n\t\t\t\t\t\treturn 16;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (red > 248) {\n\t\t\t\t\t\treturn 231;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Math.round(((red - 8) / 247) * 24) + 232;\n\t\t\t\t}\n\n\t\t\t\treturn 16\n\t\t\t\t\t+ (36 * Math.round(red / 255 * 5))\n\t\t\t\t\t+ (6 * Math.round(green / 255 * 5))\n\t\t\t\t\t+ Math.round(blue / 255 * 5);\n\t\t\t},\n\t\t\tenumerable: false,\n\t\t},\n\t\thexToRgb: {\n\t\t\tvalue(hex) {\n\t\t\t\tconst matches = /[\\da-f]{6}|[\\da-f]{3}/i.exec(hex.toString(16));\n\t\t\t\tif (!matches) {\n\t\t\t\t\treturn [0, 0, 0];\n\t\t\t\t}\n\n\t\t\t\tlet [colorString] = matches;\n\n\t\t\t\tif (colorString.length === 3) {\n\t\t\t\t\tcolorString = [...colorString].map(character => character + character).join('');\n\t\t\t\t}\n\n\t\t\t\tconst integer = Number.parseInt(colorString, 16);\n\n\t\t\t\treturn [\n\t\t\t\t\t/* eslint-disable no-bitwise -- We need the speed */\n\t\t\t\t\t(integer >> 16) & 0xFF,\n\t\t\t\t\t(integer >> 8) & 0xFF,\n\t\t\t\t\tinteger & 0xFF,\n\t\t\t\t\t/* eslint-enable no-bitwise */\n\t\t\t\t];\n\t\t\t},\n\t\t\tenumerable: false,\n\t\t},\n\t\thexToAnsi256: {\n\t\t\tvalue: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)),\n\t\t\tenumerable: false,\n\t\t},\n\t\tansi256ToAnsi: {\n\t\t\tvalue(code) {\n\t\t\t\tif (code < 8) {\n\t\t\t\t\treturn 30 + code;\n\t\t\t\t}\n\n\t\t\t\tif (code < 16) {\n\t\t\t\t\treturn 90 + (code - 8);\n\t\t\t\t}\n\n\t\t\t\tlet red;\n\t\t\t\tlet green;\n\t\t\t\tlet blue;\n\n\t\t\t\tif (code >= 232) {\n\t\t\t\t\tred = (((code - 232) * 10) + 8) / 255;\n\t\t\t\t\tgreen = red;\n\t\t\t\t\tblue = red;\n\t\t\t\t} else {\n\t\t\t\t\tcode -= 16;\n\n\t\t\t\t\tconst remainder = code % 36;\n\n\t\t\t\t\tred = Math.floor(code / 36) / 5;\n\t\t\t\t\tgreen = Math.floor(remainder / 6) / 5;\n\t\t\t\t\tblue = (remainder % 6) / 5;\n\t\t\t\t}\n\n\t\t\t\tconst value = Math.max(red, green, blue) * 2;\n\n\t\t\t\tif (value === 0) {\n\t\t\t\t\treturn 30;\n\t\t\t\t}\n\n\t\t\t\t// eslint-disable-next-line no-bitwise\n\t\t\t\tlet result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red));\n\n\t\t\t\tif (value === 2) {\n\t\t\t\t\tresult += 60;\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t},\n\t\t\tenumerable: false,\n\t\t},\n\t\trgbToAnsi: {\n\t\t\tvalue: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),\n\t\t\tenumerable: false,\n\t\t},\n\t\thexToAnsi: {\n\t\t\tvalue: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),\n\t\t\tenumerable: false,\n\t\t},\n\t});\n\n\treturn styles;\n}\n\nconst ansiStyles = assembleStyles();\n\nexport default ansiStyles;\n","import process from 'node:process';\nimport os from 'node:os';\nimport tty from 'node:tty';\n\n// From: https://github.com/sindresorhus/has-flag/blob/main/index.js\n/// function hasFlag(flag, argv = globalThis.Deno?.args ?? process.argv) {\nfunction hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process.argv) {\n\tconst prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');\n\tconst position = argv.indexOf(prefix + flag);\n\tconst terminatorPosition = argv.indexOf('--');\n\treturn position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);\n}\n\nconst {env} = process;\n\nlet flagForceColor;\nif (\n\thasFlag('no-color')\n\t|| hasFlag('no-colors')\n\t|| hasFlag('color=false')\n\t|| hasFlag('color=never')\n) {\n\tflagForceColor = 0;\n} else if (\n\thasFlag('color')\n\t|| hasFlag('colors')\n\t|| hasFlag('color=true')\n\t|| hasFlag('color=always')\n) {\n\tflagForceColor = 1;\n}\n\n// Whether `FORCE_COLOR` names a level. Shared with the exact-level check below so that the two cannot disagree on what counts as numeric.\nfunction hasNumericForceColor() {\n\treturn /^\\d+$/.test(env.FORCE_COLOR);\n}\n\nfunction envForceColor() {\n\tif (!('FORCE_COLOR' in env)) {\n\t\treturn;\n\t}\n\n\tif (env.FORCE_COLOR === 'false') {\n\t\treturn 0;\n\t}\n\n\tif (env.FORCE_COLOR === 'true' || env.FORCE_COLOR.length === 0) {\n\t\treturn 1;\n\t}\n\n\tif (!hasNumericForceColor()) {\n\t\treturn;\n\t}\n\n\treturn Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);\n}\n\nfunction translateLevel(level) {\n\tif (level === 0) {\n\t\treturn false;\n\t}\n\n\treturn {\n\t\tlevel,\n\t\thasBasic: true,\n\t\thas256: level >= 2,\n\t\thas16m: level >= 3,\n\t};\n}\n\nfunction _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) {\n\tconst noFlagForceColor = envForceColor();\n\tif (noFlagForceColor !== undefined) {\n\t\tflagForceColor = noFlagForceColor;\n\t}\n\n\tconst forceColor = sniffFlags ? flagForceColor : noFlagForceColor;\n\n\tif (forceColor === 0) {\n\t\treturn 0;\n\t}\n\n\tif (sniffFlags) {\n\t\tif (hasFlag('color=16m')\n\t\t\t|| hasFlag('color=full')\n\t\t\t|| hasFlag('color=truecolor')) {\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (hasFlag('color=256')) {\n\t\t\treturn 2;\n\t\t}\n\t}\n\n\t// A numeric `FORCE_COLOR` requests an exact level, while `FORCE_COLOR=true` and `FORCE_COLOR=` only enable color and let the level be detected.\n\tif (forceColor !== undefined && hasNumericForceColor()) {\n\t\treturn forceColor;\n\t}\n\n\t// Check for Azure DevOps pipelines.\n\t// Has to be above the `!streamIsTTY` check.\n\tif ('TF_BUILD' in env && 'AGENT_NAME' in env) {\n\t\treturn 1;\n\t}\n\n\tif (haveStream && !streamIsTTY && forceColor === undefined) {\n\t\treturn 0;\n\t}\n\n\tconst min = forceColor || 0;\n\n\tif (env.TERM === 'dumb') {\n\t\treturn min;\n\t}\n\n\tif (process.platform === 'win32') {\n\t\t// Windows 10 build 10586 is the first Windows release that supports 256 colors.\n\t\t// Windows 10 build 14931 is the first release that supports 16m/TrueColor.\n\t\tconst osRelease = os.release().split('.');\n\t\tif (\n\t\t\tNumber(osRelease[0]) >= 10\n\t\t\t&& Number(osRelease[2]) >= 10_586\n\t\t) {\n\t\t\treturn Number(osRelease[2]) >= 14_931 ? 3 : 2;\n\t\t}\n\n\t\treturn 1;\n\t}\n\n\tif ('CI' in env) {\n\t\tif (['GITHUB_ACTIONS', 'GITEA_ACTIONS', 'CIRCLECI'].some(key => key in env)) {\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (['TRAVIS', 'APPVEYOR', 'GITLAB_CI', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {\n\t\t\treturn 1;\n\t\t}\n\n\t\treturn min;\n\t}\n\n\tif ('TEAMCITY_VERSION' in env) {\n\t\treturn /^(?:9\\.0*[1-9]\\d*\\.|\\d{2,}\\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;\n\t}\n\n\tif (env.COLORTERM === 'truecolor') {\n\t\treturn 3;\n\t}\n\n\tif (env.TERM === 'xterm-kitty') {\n\t\treturn 3;\n\t}\n\n\tif (env.TERM === 'xterm-ghostty') {\n\t\treturn 3;\n\t}\n\n\tif (env.TERM === 'wezterm') {\n\t\treturn 3;\n\t}\n\n\tif ('TERM_PROGRAM' in env) {\n\t\tconst version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.', 1)[0], 10);\n\n\t\tswitch (env.TERM_PROGRAM) {\n\t\t\tcase 'iTerm.app': {\n\t\t\t\treturn version >= 3 ? 3 : 2;\n\t\t\t}\n\n\t\t\tcase 'Apple_Terminal': {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\t// No default\n\t\t}\n\t}\n\n\tif (/-256(?:color)?$/i.test(env.TERM)) {\n\t\treturn 2;\n\t}\n\n\tif (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {\n\t\treturn 1;\n\t}\n\n\tif ('COLORTERM' in env) {\n\t\treturn 1;\n\t}\n\n\treturn min;\n}\n\nexport function createSupportsColor(stream, options = {}) {\n\tconst level = _supportsColor(stream, {\n\t\tstreamIsTTY: stream && stream.isTTY,\n\t\t...options,\n\t});\n\n\treturn translateLevel(level);\n}\n\nconst supportsColor = {\n\tstdout: createSupportsColor({isTTY: tty.isatty(1)}),\n\tstderr: createSupportsColor({isTTY: tty.isatty(2)}),\n};\n\nexport default supportsColor;\n","import {\n\tstringReplaceAll,\n\tstringEncaseCRLFWithFirstIndex,\n} from './utilities.js';\nimport ansiStyles from '#ansi-styles';\nimport supportsColor from '#supports-color';\n\nconst {stdout: stdoutColor, stderr: stderrColor} = supportsColor;\n\nconst GENERATOR = Symbol('GENERATOR');\nconst STYLER = Symbol('STYLER');\nconst IS_EMPTY = Symbol('IS_EMPTY');\nconst LEVEL = Symbol('LEVEL');\n\nconst styles = Object.create(null);\n\nconst assertValidLevel = level => {\n\tif (!Number.isSafeInteger(level) || level < 0 || level > 3) {\n\t\tthrow new Error('The `level` should be an integer from 0 to 3');\n\t}\n};\n\n// The level is stored under a symbol so the hot path can read it as a plain property, while `level` itself is an accessor that rejects values the rest of the code could not handle.\nconst levelDescriptor = {\n\tenumerable: true,\n\tget() {\n\t\treturn this[LEVEL];\n\t},\n\tset(level) {\n\t\tassertValidLevel(level);\n\t\tthis[LEVEL] = level;\n\t},\n};\n\nconst applyOptions = (object, options = {}) => {\n\tif (options.level !== undefined) {\n\t\tassertValidLevel(options.level);\n\t}\n\n\t// Detect level if not set manually. Written under the symbol rather than through `level`, as the prototype carrying that accessor is not installed until after this runs.\n\tconst colorLevel = stdoutColor ? stdoutColor.level : 0;\n\tobject[LEVEL] = options.level === undefined ? colorLevel : options.level;\n};\n\nexport class Chalk {\n\tconstructor(options) {\n\t\t// eslint-disable-next-line no-constructor-return\n\t\treturn chalkFactory(options);\n\t}\n}\n\nconst chalkFactory = options => {\n\tconst chalk = (...strings) => strings.join(' ');\n\tapplyOptions(chalk, options);\n\n\tObject.setPrototypeOf(chalk, createChalk.prototype);\n\n\treturn chalk;\n};\n\nfunction createChalk(options) {\n\treturn chalkFactory(options);\n}\n\n// eslint-disable-next-line unicorn/no-top-level-side-effects -- The prototype chain must be set up at module load.\nObject.setPrototypeOf(createChalk.prototype, Function.prototype);\n\nfor (const [styleName, style] of Object.entries(ansiStyles)) {\n\tstyles[styleName] = {\n\t\tget() {\n\t\t\tconst builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);\n\t\t\tObject.defineProperty(this, styleName, {value: builder});\n\t\t\treturn builder;\n\t\t},\n\t};\n}\n\nstyles.visible = {\n\tget() {\n\t\tconst builder = createBuilder(this, this[STYLER], true);\n\t\tObject.defineProperty(this, 'visible', {value: builder});\n\t\treturn builder;\n\t},\n};\n\n// Resolve a color model to one converter per `level`, so that a call only has to look up the converter for the current level instead of re-deciding the model and level every time.\nconst createModelConverters = (model, type) => {\n\tconst style = ansiStyles[type];\n\n\tif (model === 'rgb') {\n\t\tconst ansi = (red, green, blue) => style.ansi(ansiStyles.rgbToAnsi(red, green, blue));\n\t\tconst ansi256 = (red, green, blue) => style.ansi256(ansiStyles.rgbToAnsi256(red, green, blue));\n\t\treturn [ansi, ansi, ansi256, style.ansi16m];\n\t}\n\n\tif (model === 'hex') {\n\t\tconst ansi = hex => style.ansi(ansiStyles.hexToAnsi(hex));\n\t\tconst ansi256 = hex => style.ansi256(ansiStyles.hexToAnsi256(hex));\n\t\treturn [ansi, ansi, ansi256, hex => style.ansi16m(...ansiStyles.hexToRgb(hex))];\n\t}\n\n\t// `ansi256` is already the native form, so only the 16-color levels need converting.\n\tconst ansi = code => style.ansi(ansiStyles.ansi256ToAnsi(code));\n\treturn [ansi, ansi, style.ansi256, style.ansi256];\n};\n\nconst usedModels = ['rgb', 'hex', 'ansi256'];\n\nfor (const model of usedModels) {\n\tconst capitalizedModel = model[0].toUpperCase() + model.slice(1);\n\n\tfor (const [styleName, type] of [\n\t\t[model, 'color'],\n\t\t['bg' + capitalizedModel, 'bgColor'],\n\t\t['underline' + capitalizedModel, 'underlineColor'],\n\t]) {\n\t\tconst {close} = ansiStyles[type];\n\t\tconst converters = createModelConverters(model, type);\n\n\t\tstyles[styleName] = {\n\t\t\tget() {\n\t\t\t\t// The level is read on call rather than captured here so the function can be cached on the instance instead of being reallocated on every property access.\n\t\t\t\t// `rgb` is the widest model, so naming the three parameters avoids a rest array.\n\t\t\t\tconst styleFunction = function (first, second, third) {\n\t\t\t\t\tconst open = converters[this.level](first, second, third);\n\t\t\t\t\treturn createBuilder(this, createStyler(open, close, this[STYLER]), this[IS_EMPTY]);\n\t\t\t\t};\n\n\t\t\t\tObject.defineProperty(this, styleName, {value: styleFunction});\n\t\t\t\treturn styleFunction;\n\t\t\t},\n\t\t};\n\t}\n}\n\nconst proto = Object.defineProperties(\n\t() => {},\n\t{\n\t\t...styles,\n\t\tlevel: {\n\t\t\tenumerable: true,\n\t\t\tget() {\n\t\t\t\treturn this[GENERATOR].level;\n\t\t\t},\n\t\t\tset(level) {\n\t\t\t\tthis[GENERATOR].level = level;\n\t\t\t},\n\t\t},\n\t},\n);\n\nconst createStyler = (open, close, parent) => {\n\tlet openAll;\n\tlet closeAll;\n\tif (parent === undefined) {\n\t\topenAll = open;\n\t\tcloseAll = close;\n\t} else {\n\t\topenAll = parent.openAll + open;\n\t\tcloseAll = close + parent.closeAll;\n\t}\n\n\treturn {\n\t\topen,\n\t\tclose,\n\t\topenAll,\n\t\tcloseAll,\n\t\tparent,\n\t};\n};\n\nconst createBuilder = (self, _styler, _isEmpty) => {\n\t// Single argument is hot path, implicit coercion is faster than anything\n\tconst builder = (...arguments_) => {\n\t\tif (arguments_.length === 1) {\n\t\t\t// eslint-disable-next-line no-implicit-coercion\n\t\t\treturn applyStyle(builder, '' + arguments_[0]);\n\t\t}\n\n\t\tif (arguments_.length === 2) {\n\t\t\treturn applyStyle(builder, arguments_[0] + ' ' + arguments_[1]);\n\t\t}\n\n\t\treturn applyStyle(builder, arguments_.join(' '));\n\t};\n\n\t// We alter the prototype because we must return a function, but there is\n\t// no way to create a function with a different prototype\n\tObject.setPrototypeOf(builder, proto);\n\n\t// Point every builder at the root generator instead of its immediate parent, so reading the level costs one property load rather than walking a `level` getter per link of the chain.\n\tbuilder[GENERATOR] = self[GENERATOR] ?? self;\n\tbuilder[STYLER] = _styler;\n\tbuilder[IS_EMPTY] = _isEmpty;\n\n\treturn builder;\n};\n\nconst applyStyle = (self, string) => {\n\t// Read the level directly off the generator to skip the `level` getter dispatch on this hot path\n\tif (self[GENERATOR][LEVEL] <= 0 || !string) {\n\t\t// eslint-disable-next-line unicorn/no-computed-property-existence-check -- Reads the boolean value, not a property existence check.\n\t\treturn self[IS_EMPTY] ? '' : string;\n\t}\n\n\tlet styler = self[STYLER];\n\n\tif (styler === undefined) {\n\t\treturn string;\n\t}\n\n\tconst {openAll, closeAll} = styler;\n\tif (string.includes('\\u{1B}')) {\n\t\twhile (styler !== undefined) {\n\t\t\t// Replace any instances already present with a re-opening code\n\t\t\t// otherwise only the part of the string until said closing code\n\t\t\t// will be colored, and the rest will simply be 'plain'.\n\t\t\tstring = stringReplaceAll(string, styler.close, styler.open);\n\n\t\t\tstyler = styler.parent;\n\t\t}\n\t}\n\n\t// We can move both next actions out of loop, because remaining actions in loop won't have\n\t// any/visible effect on parts we add here. Close the styling before a linebreak and reopen\n\t// after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92\n\tconst lfIndex = string.indexOf('\\n');\n\tif (lfIndex !== -1) {\n\t\tstring = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);\n\t}\n\n\treturn openAll + string + closeAll;\n};\n\n// `level` lives on the prototype rather than on each instance, so it costs nothing to construct an instance and matches how builders already expose it. It is inherited rather than own, so it does not show up in `Object.keys()`, same as for a builder.\n// eslint-disable-next-line unicorn/no-top-level-side-effects -- The style getters must be installed at module load.\nObject.defineProperties(createChalk.prototype, {...styles, level: levelDescriptor});\n\nconst chalk = createChalk();\nexport const chalkStderr = createChalk({level: stderrColor ? stderrColor.level : 0});\n\nexport {\n\tmodifierNames,\n\tforegroundColorNames,\n\tbackgroundColorNames,\n\tunderlineColorNames,\n\tcolorNames,\n\n\t// TODO: Remove these aliases in the next major version\n\tmodifierNames as modifiers,\n\tforegroundColorNames as foregroundColors,\n\tbackgroundColorNames as backgroundColors,\n\tcolorNames as colors,\n} from './vendor/ansi-styles/index.js';\n\nexport {\n\tstdoutColor as supportsColor,\n\tstderrColor as supportsColorStderr,\n};\n\nexport default chalk;\n","/**\n * Returned by a scalar resolver when the source does not match its tag.\n *\n * @category Tags\n */\nconst NOT_RESOLVED: unique symbol = Symbol('NOT_RESOLVED')\n\n/**\n * Options for {@link defineScalarTag}.\n *\n * @category Tags\n */\ninterface ScalarTagOptions<Result> {\n /**\n * Whether this tag participates in resolving plain scalars without an\n * explicit tag. Default: `false`.\n */\n implicit?: boolean\n\n /**\n * Whether explicit tag names are matched by prefix instead of exact equality.\n * Default: `false`.\n */\n matchByTagPrefix?: boolean\n\n /**\n * Set of `source.charAt(0)` keys for which `resolve` may succeed (a superset\n * of what it really matches). A key is either a single character or '' (empty\n * source). `null` means \"no constraint, always try\". Used by the composer to\n * dispatch implicit scalars by first character without running every resolver.\n */\n implicitFirstChars?: readonly string[] | null\n\n /**\n * Construct a value from scalar text, or return {@link NOT_RESOLVED} when it\n * is invalid for this tag. `isExplicit` is true for an explicit tag and\n * `tagName` is the actual matched name.\n */\n resolve: (source: string, isExplicit: boolean, tagName: string) => Result | typeof NOT_RESOLVED\n\n /**\n * Selects this tag for a JavaScript value when dumping. Use `() => false`\n * for load-only tags.\n */\n identify: (data: any) => boolean\n\n /**\n * A scalar's printed form is text, so `represent` always yields a string.\n * The factory supplies a `String(data)` default when a tag omits it.\n */\n represent?: (data: any) => string\n\n /** Return the tag name to emit for a prefix-matching tag. Defaults to `tagName`. */\n representTagName?: (data: any) => string\n}\n\n/**\n * Normalized scalar tag returned by {@link defineScalarTag}.\n *\n * @category Tags\n */\ninterface ScalarTagDefinition<Result = unknown> extends Required<ScalarTagOptions<Result>> {\n /** Tag name used for schema lookup. */\n tagName: string\n\n /** YAML node kind handled by this tag. */\n nodeKind: 'scalar'\n}\n\n/**\n * Options for {@link defineSequenceTag}.\n *\n * @category Tags\n */\ninterface SequenceTagOptions<Carrier, Result = Carrier> {\n /**\n * Whether explicit tag names are matched by prefix instead of exact equality.\n * Default: `false`.\n */\n matchByTagPrefix?: boolean\n\n /** Create the carrier used while constructing a sequence. */\n create: (tagName: string) => Carrier\n\n /** Add an item to the carrier. Return a non-empty error message to reject it. */\n addItem: (carrier: Carrier, item: unknown, index: number) => void | string\n\n /** Convert the completed carrier to the result. Defaults to the identity function. */\n finalize?: (carrier: Carrier) => Result\n\n /**\n * Selects this tag for a JavaScript value when dumping. Use `() => false`\n * for load-only tags.\n */\n identify: (data: any) => boolean\n\n /** Return the array-like contents to dump. Defaults to the identity function. */\n represent?: (data: any) => ArrayLike<unknown>\n\n /** Return the tag name to emit for a prefix-matching tag. Defaults to `tagName`. */\n representTagName?: (data: any) => string\n}\n\n/**\n * Normalized sequence tag returned by {@link defineSequenceTag}.\n *\n * @category Tags\n */\ninterface SequenceTagDefinition<Carrier = unknown, Result = Carrier> extends Required<SequenceTagOptions<Carrier, Result>> {\n /** Tag name used for schema lookup. */\n tagName: string\n\n /** YAML node kind handled by this tag. */\n nodeKind: 'sequence'\n\n /** Sequence tags do not participate in implicit scalar resolution. */\n implicit: false\n\n /** Whether the carrier is also the final result (`finalize` was omitted). */\n carrierIsResult: boolean\n}\n\n/**\n * Options for {@link defineMappingTag}.\n *\n * @category Tags\n */\ninterface MappingTagOptions<Carrier, Result = Carrier> {\n /**\n * Whether explicit tag names are matched by prefix instead of exact equality.\n * Default: `false`.\n */\n matchByTagPrefix?: boolean\n\n /** Create the carrier used while constructing a mapping. */\n create: (tagName: string) => Carrier\n\n /**\n * Writes a pair. Returns '' on success, a non-empty error message otherwise\n * (key does not fit the representation, value rejected, ...). Always a string\n * so the hot path never allocates an exception wrapper.\n */\n addPair: (carrier: Carrier, key: unknown, value: unknown) => string\n\n /** Return whether the carrier contains a key, for duplicate and merge checks. */\n has: (carrier: Carrier, key: unknown) => boolean\n\n /** Return the keys of a completed result for YAML merge processing. */\n keys: (result: Result) => Iterable<unknown>\n\n /** Return a value from a completed result for YAML merge processing. */\n get: (result: Result, key: unknown) => unknown\n\n /** Convert the completed carrier to the result. Defaults to the identity function. */\n finalize?: (carrier: Carrier) => Result\n\n /**\n * Selects this tag for a JavaScript value when dumping. Use `() => false`\n * for load-only tags.\n */\n identify: (data: any) => boolean\n\n /** Return the mapping entries to dump. Defaults to the identity function. */\n represent?: (data: any) => Map<unknown, unknown>\n\n /** Return the tag name to emit for a prefix-matching tag. Defaults to `tagName`. */\n representTagName?: (data: any) => string\n}\n\n/**\n * Normalized mapping tag returned by {@link defineMappingTag}.\n *\n * @category Tags\n */\ninterface MappingTagDefinition<Carrier = unknown, Result = Carrier> extends Required<MappingTagOptions<Carrier, Result>> {\n /** Tag name used for schema lookup. */\n tagName: string\n\n /** YAML node kind handled by this tag. */\n nodeKind: 'mapping'\n\n /** Mapping tags do not participate in implicit scalar resolution. */\n implicit: false\n\n /** Whether the carrier is also the final result (`finalize` was omitted). */\n carrierIsResult: boolean\n}\n\n/**\n * Any normalized tag definition accepted by {@link Schema}.\n *\n * @category Tags\n */\ntype TagDefinition =\n | ScalarTagDefinition<any>\n | SequenceTagDefinition<any, any>\n | MappingTagDefinition<any, any>\n\n/**\n * Create a normalized scalar tag definition.\n *\n * @category Tags\n */\nfunction defineScalarTag<Result> (tagName: string, options: ScalarTagOptions<Result>): ScalarTagDefinition<Result> {\n return {\n tagName,\n nodeKind: 'scalar',\n implicit: options.implicit ?? false,\n matchByTagPrefix: options.matchByTagPrefix ?? false,\n implicitFirstChars: options.implicitFirstChars ?? null,\n resolve: options.resolve,\n identify: options.identify,\n represent: options.represent ?? (data => String(data)),\n representTagName: options.representTagName ?? (() => tagName)\n }\n}\n\n/**\n * Create a normalized sequence tag definition.\n *\n * @category Tags\n */\nfunction defineSequenceTag<Carrier, Result = Carrier> (tagName: string, options: SequenceTagOptions<Carrier, Result>): SequenceTagDefinition<Carrier, Result> {\n const carrierIsResult = options.finalize === undefined\n\n return {\n tagName,\n nodeKind: 'sequence',\n implicit: false,\n matchByTagPrefix: options.matchByTagPrefix ?? false,\n create: options.create,\n addItem: options.addItem,\n finalize: options.finalize ?? (carrier => carrier as unknown as Result),\n carrierIsResult,\n identify: options.identify,\n represent: options.represent ?? (data => data as ArrayLike<unknown>),\n representTagName: options.representTagName ?? (() => tagName)\n }\n}\n\n/**\n * Create a normalized mapping tag definition.\n *\n * @category Tags\n */\nfunction defineMappingTag<Carrier, Result = Carrier> (tagName: string, options: MappingTagOptions<Carrier, Result>): MappingTagDefinition<Carrier, Result> {\n const carrierIsResult = options.finalize === undefined\n\n return {\n tagName,\n nodeKind: 'mapping',\n implicit: false,\n matchByTagPrefix: options.matchByTagPrefix ?? false,\n create: options.create,\n addPair: options.addPair,\n has: options.has,\n keys: options.keys,\n get: options.get,\n finalize: options.finalize ?? (carrier => carrier as unknown as Result),\n carrierIsResult,\n identify: options.identify,\n represent: options.represent ?? (data => data as Map<unknown, unknown>),\n representTagName: options.representTagName ?? (() => tagName)\n }\n}\n\nexport {\n NOT_RESOLVED,\n defineScalarTag,\n defineSequenceTag,\n defineMappingTag,\n\n type ScalarTagDefinition,\n type SequenceTagDefinition,\n type MappingTagDefinition,\n type TagDefinition,\n type ScalarTagOptions,\n type SequenceTagOptions,\n type MappingTagOptions\n}\n","import { defineScalarTag } from '../../tag.ts'\n\n/** @category Tags */\nconst strTag = defineScalarTag('tag:yaml.org,2002:str', {\n resolve: (source) => source,\n identify: (data) => typeof data === 'string'\n})\n\nexport { strTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst NULL_VALUES = ['', '~', 'null', 'Null', 'NULL']\n\n/** @category Tags */\nconst nullCoreTag = defineScalarTag('tag:yaml.org,2002:null', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: '' (empty), '~', 'null'/'Null'/'NULL'.\n implicitFirstChars: ['', '~', 'n', 'N'],\n resolve: (source) => {\n if (NULL_VALUES.indexOf(source) !== -1) return null\n\n return NOT_RESOLVED\n },\n identify: (object) => object === null,\n represent: () => 'null'\n})\n\nexport { nullCoreTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\n/** @category Tags */\nconst nullJsonTag = defineScalarTag('tag:yaml.org,2002:null', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: null.\n implicitFirstChars: ['n'],\n resolve: (source, isExplicit) => {\n if (source === 'null' || (isExplicit && source === '')) return null\n\n return NOT_RESOLVED\n },\n identify: (object) => object === null,\n represent: () => 'null'\n})\n\nexport { nullJsonTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst NULL_VALUES = ['', '~', 'null', 'Null', 'NULL']\n\n/** @category Tags */\nconst nullYaml11Tag = defineScalarTag('tag:yaml.org,2002:null', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: '' (empty), '~', 'null'/'Null'/'NULL'.\n implicitFirstChars: ['', '~', 'n', 'N'],\n resolve: (source) => {\n if (NULL_VALUES.indexOf(source) !== -1) return null\n\n return NOT_RESOLVED\n },\n identify: (object) => object === null,\n represent: () => 'null'\n})\n\nexport { nullYaml11Tag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst TRUE_VALUES = ['true', 'True', 'TRUE']\nconst FALSE_VALUES = ['false', 'False', 'FALSE']\n\n/** @category Tags */\nconst boolCoreTag = defineScalarTag('tag:yaml.org,2002:bool', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: true/True/TRUE, false/False/FALSE.\n implicitFirstChars: ['t', 'T', 'f', 'F'],\n resolve: (source) => {\n if (TRUE_VALUES.indexOf(source) !== -1) return true\n if (FALSE_VALUES.indexOf(source) !== -1) return false\n\n return NOT_RESOLVED\n },\n identify: (object) => Object.prototype.toString.call(object) === '[object Boolean]',\n represent: (object) => object ? 'true' : 'false'\n})\n\nexport { boolCoreTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst TRUE_VALUES = ['true']\nconst FALSE_VALUES = ['false']\n\n/** @category Tags */\nconst boolJsonTag = defineScalarTag('tag:yaml.org,2002:bool', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: true, false.\n implicitFirstChars: ['t', 'f'],\n resolve: (source) => {\n if (TRUE_VALUES.indexOf(source) !== -1) return true\n if (FALSE_VALUES.indexOf(source) !== -1) return false\n\n return NOT_RESOLVED\n },\n identify: (object) => Object.prototype.toString.call(object) === '[object Boolean]',\n represent: (object) => object ? 'true' : 'false'\n})\n\nexport { boolJsonTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst TRUE_VALUES = ['true', 'True', 'TRUE', 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON']\nconst FALSE_VALUES = ['false', 'False', 'FALSE', 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF']\n\n/** @category Tags */\nconst boolYaml11Tag = defineScalarTag('tag:yaml.org,2002:bool', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs.\n implicitFirstChars: ['y', 'Y', 'n', 'N', 't', 'T', 'f', 'F', 'o', 'O'],\n resolve: (source) => {\n if (TRUE_VALUES.indexOf(source) !== -1) return true\n if (FALSE_VALUES.indexOf(source) !== -1) return false\n\n return NOT_RESOLVED\n },\n identify: (object) => Object.prototype.toString.call(object) === '[object Boolean]',\n represent: (object) => object ? 'true' : 'false'\n})\n\nexport { boolYaml11Tag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\n// YAML 1.2 Core schema implicit resolution:\n// [-+]? [0-9]+ | 0o [0-7]+ | 0x [0-9a-fA-F]+\nconst YAML_INTEGER_IMPLICIT_PATTERN = new RegExp(\n // 0o123\n '^(?:0o[0-7]+' +\n // 0x1A\n '|0x[0-9a-fA-F]+' +\n // 12345\n '|[-+]?[0-9]+)$')\n\n// Explicit `!!int` validation is separate from Core implicit resolution.\nconst YAML_INTEGER_EXPLICIT_PATTERN = new RegExp(\n // 0b1010\n '^(?:[-+]?0b[0-1]+' +\n // 0o123\n '|[-+]?0o[0-7]+' +\n // 0x1A\n '|[-+]?0x[0-9a-fA-F]+' +\n // 12345\n '|[-+]?[0-9]+)$')\n\nfunction parseYamlInteger (source: string) {\n let value = source\n let sign = 1\n\n if (value[0] === '-' || value[0] === '+') {\n if (value[0] === '-') sign = -1\n value = value.slice(1)\n }\n\n if (value.startsWith('0b')) return sign * parseInt(value.slice(2), 2)\n if (value.startsWith('0o')) return sign * parseInt(value.slice(2), 8)\n if (value.startsWith('0x')) return sign * parseInt(value.slice(2), 16)\n\n return sign * parseInt(value, 10)\n}\n\nfunction resolveYamlInteger (source: string, isExplicit: boolean) {\n if (isExplicit) {\n if (!YAML_INTEGER_EXPLICIT_PATTERN.test(source)) return NOT_RESOLVED\n } else if (!YAML_INTEGER_IMPLICIT_PATTERN.test(source)) {\n return NOT_RESOLVED\n }\n\n const result = parseYamlInteger(source)\n return Number.isFinite(result) ? result : NOT_RESOLVED\n}\n\n/** @category Tags */\nconst intCoreTag = defineScalarTag('tag:yaml.org,2002:int', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: optional sign + decimal digit.\n implicitFirstChars: ['-', '+', ...'0123456789'],\n resolve: resolveYamlInteger,\n identify: (object) =>\n // No ancient boxed numbers support\n Number.isInteger(object) &&\n // Negative zero => !!float\n !Object.is(object, -0) &&\n // Exponential form => !!float, round-trip for !!int 1e21 will be broken\n object.toString(10).indexOf('e') < 0,\n represent: (object: number) => object.toString(10)\n})\n\nexport { intCoreTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\n// YAML 1.2 JSON schema implicit resolution:\n// -? ( 0 | [1-9] [0-9]* )\nconst YAML_INTEGER_IMPLICIT_PATTERN = new RegExp(\n '^-?(?:0|[1-9][0-9]*)$')\n\n// Explicit `!!int` validation is separate from JSON implicit resolution.\nconst YAML_INTEGER_EXPLICIT_PATTERN = new RegExp(\n // 0b1010\n '^(?:[-+]?0b[0-1]+' +\n // 0o123\n '|[-+]?0o[0-7]+' +\n // 0x1A\n '|[-+]?0x[0-9a-fA-F]+' +\n // 12345\n '|[-+]?[0-9]+)$')\n\nfunction parseYamlInteger (source: string) {\n let value = source\n let sign = 1\n\n if (value[0] === '-' || value[0] === '+') {\n if (value[0] === '-') sign = -1\n value = value.slice(1)\n }\n\n if (value.startsWith('0b')) return sign * parseInt(value.slice(2), 2)\n if (value.startsWith('0o')) return sign * parseInt(value.slice(2), 8)\n if (value.startsWith('0x')) return sign * parseInt(value.slice(2), 16)\n\n return sign * parseInt(value, 10)\n}\n\nfunction resolveYamlInteger (source: string, isExplicit: boolean) {\n if (isExplicit) {\n if (!YAML_INTEGER_EXPLICIT_PATTERN.test(source)) return NOT_RESOLVED\n } else if (!YAML_INTEGER_IMPLICIT_PATTERN.test(source)) {\n return NOT_RESOLVED\n }\n\n const result = parseYamlInteger(source)\n return Number.isFinite(result) ? result : NOT_RESOLVED\n}\n\n/** @category Tags */\nconst intJsonTag = defineScalarTag('tag:yaml.org,2002:int', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: optional '-' or digit.\n implicitFirstChars: ['-', ...'0123456789'],\n resolve: resolveYamlInteger,\n identify: (object) =>\n // No ancient boxed numbers support\n Number.isInteger(object) &&\n // Negative zero => !!float\n !Object.is(object, -0) &&\n // Exponential form => !!float, round-trip for !!int 1e21 will be broken\n object.toString(10).indexOf('e') < 0,\n represent: (object: number) => object.toString(10)\n})\n\nexport { intJsonTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst YAML_INTEGER_PATTERN = new RegExp(\n // 0b1010\n '^(?:[-+]?0b[0-1_]+' +\n // 0123\n '|[-+]?0[0-7_]+' +\n // 0x1A\n '|[-+]?0x[0-9a-fA-F_]+' +\n // 1:23\n '|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+' +\n // 12345\n '|[-+]?(?:0|[1-9][0-9_]*))$')\n\nfunction parseYamlInteger (source: string) {\n let value = source.replace(/_/g, '')\n let sign = 1\n\n if (value[0] === '-' || value[0] === '+') {\n if (value[0] === '-') sign = -1\n value = value.slice(1)\n }\n\n if (value.startsWith('0b')) return sign * parseInt(value.slice(2), 2)\n if (value.startsWith('0x')) return sign * parseInt(value.slice(2), 16)\n\n if (value.includes(':')) {\n let result = 0\n for (const part of value.split(':')) result = result * 60 + Number(part)\n return sign * result\n }\n\n if (value !== '0' && value[0] === '0') return sign * parseInt(value, 8)\n\n return sign * parseInt(value, 10)\n}\n\nfunction resolveYamlInteger (source: string) {\n if (!YAML_INTEGER_PATTERN.test(source)) return NOT_RESOLVED\n\n const result = parseYamlInteger(source)\n return Number.isFinite(result) ? result : NOT_RESOLVED\n}\n\n/** @category Tags */\nconst intYaml11Tag = defineScalarTag('tag:yaml.org,2002:int', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: optional sign + decimal digit.\n implicitFirstChars: ['-', '+', ...'0123456789'],\n resolve: resolveYamlInteger,\n identify: (object) =>\n // No ancient boxed numbers support\n Number.isInteger(object) &&\n // Negative zero => !!float\n !Object.is(object, -0) &&\n // Exponential form => !!float, round-trip for !!int 1e21 will be broken\n object.toString(10).indexOf('e') < 0,\n represent: (object: number) => object.toString(10)\n})\n\nexport { intYaml11Tag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst YAML_FLOAT_PATTERN = new RegExp(\n // 2.5e4, 2.5 and integers\n '^(?:[-+]?[0-9]+(?:\\\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?' +\n // .2e4, .2\n '|[-+]?\\\\.[0-9]+(?:[eE][-+]?[0-9]+)?' +\n // .inf\n '|[-+]?\\\\.(?:inf|Inf|INF)' +\n // .nan\n '|\\\\.(?:nan|NaN|NAN))$')\n\nconst YAML_FLOAT_SPECIAL_PATTERN = new RegExp(\n '^(?:' +\n // .inf\n '[-+]?\\\\.(?:inf|Inf|INF)' +\n // .nan\n '|\\\\.(?:nan|NaN|NAN))$')\n\nfunction resolveYamlFloat (source: string) {\n if (!YAML_FLOAT_PATTERN.test(source)) return NOT_RESOLVED\n\n let value = source.toLowerCase()\n const sign = value[0] === '-' ? -1 : 1\n\n if ('+-'.includes(value[0])) value = value.slice(1)\n\n if (value === '.inf') return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY\n if (value === '.nan') return NaN\n\n const result = sign * parseFloat(value)\n\n if (Number.isFinite(result) || YAML_FLOAT_SPECIAL_PATTERN.test(source)) return result\n return NOT_RESOLVED\n}\n\nfunction representYamlFloat (object: number) {\n if (isNaN(object)) return '.nan'\n if (object === Number.POSITIVE_INFINITY) return '.inf'\n if (object === Number.NEGATIVE_INFINITY) return '-.inf'\n if (Object.is(object, -0)) return '-0.0'\n\n const result = object.toString(10)\n return /^[-+]?[0-9]+e/.test(result) ? result.replace('e', '.e') : result\n}\n\n/** @category Tags */\nconst floatCoreTag = defineScalarTag('tag:yaml.org,2002:float', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: optional sign, '.', or digit\n // ('.inf'/'.nan' start with '.').\n implicitFirstChars: ['-', '+', '.', ...'0123456789'],\n resolve: resolveYamlFloat,\n identify: (object) =>\n // No ancient boxed numbers support\n typeof object === 'number' &&\n (\n // We land here all numbers, not handled (declined) by !!int `.identify`\n // The same condition as for !!int, but reversed.\n\n // Filter out integers...\n !Number.isInteger(object) ||\n // but allow negative zero\n Object.is(object, -0) ||\n // and integers with exponential form\n object.toString(10).indexOf('e') >= 0\n ),\n represent: representYamlFloat\n})\n\nexport { floatCoreTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\n// YAML 1.2 JSON schema implicit resolution:\n// -? ( 0 | [1-9] [0-9]* ) ( \\. [0-9]* )? ( [eE] [-+]? [0-9]+ )?\nconst YAML_FLOAT_IMPLICIT_PATTERN = new RegExp(\n // 2.5e4, 2.5 and integers\n '^-?(?:0|[1-9][0-9]*)(?:\\\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$')\n\n// Explicit `!!float` validation is separate from JSON implicit resolution.\nconst YAML_FLOAT_EXPLICIT_PATTERN = new RegExp(\n // 2.5e4, 2.5 and integers\n '^(?:[-+]?[0-9]+(?:\\\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?' +\n // .2e4, .2\n '|[-+]?\\\\.[0-9]+(?:[eE][-+]?[0-9]+)?' +\n // .inf\n '|[-+]?\\\\.(?:inf|Inf|INF)' +\n // .nan\n '|\\\\.(?:nan|NaN|NAN))$')\n\nfunction resolveYamlFloat (source: string, isExplicit: boolean) {\n if (isExplicit) {\n if (!YAML_FLOAT_EXPLICIT_PATTERN.test(source)) return NOT_RESOLVED\n\n let value = source.toLowerCase()\n const sign = value[0] === '-' ? -1 : 1\n\n if ('+-'.includes(value[0])) value = value.slice(1)\n\n if (value === '.inf') return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY\n if (value === '.nan') return NaN\n\n const result = sign * parseFloat(value)\n return Number.isFinite(result) ? result : NOT_RESOLVED\n }\n\n if (!YAML_FLOAT_IMPLICIT_PATTERN.test(source)) return NOT_RESOLVED\n\n const result = Number(source)\n\n if (Number.isFinite(result)) return result\n return NOT_RESOLVED\n}\n\nfunction representYamlFloat (object: number) {\n if (isNaN(object)) return '.nan'\n if (object === Number.POSITIVE_INFINITY) return '.inf'\n if (object === Number.NEGATIVE_INFINITY) return '-.inf'\n if (Object.is(object, -0)) return '-0.0'\n\n const result = object.toString(10)\n return /^[-+]?[0-9]+e/.test(result) ? result.replace('e', '.e') : result\n}\n\n/** @category Tags */\nconst floatJsonTag = defineScalarTag('tag:yaml.org,2002:float', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: optional '-' or digit.\n implicitFirstChars: ['-', ...'0123456789'],\n resolve: resolveYamlFloat,\n identify: (object) =>\n // No ancient boxed numbers support\n typeof object === 'number' &&\n (\n // We land here all numbers, not handled (declined) by !!int `.identify`\n // The same condition as for !!int, but reversed.\n\n // Filter out integers...\n !Number.isInteger(object) ||\n // but allow negative zero\n Object.is(object, -0) ||\n // and integers with exponential form\n object.toString(10).indexOf('e') >= 0\n ),\n represent: representYamlFloat\n})\n\nexport { floatJsonTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst YAML_FLOAT_PATTERN = new RegExp(\n // 2.5e4, 2.5 and integers\n '^(?:[-+]?(?:(?:[0-9][0-9_]*)?\\\\.[0-9_]*)(?:[eE][-+][0-9]+)?' +\n // 190:20:30.15\n '|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\\\.[0-9_]*' +\n // .inf\n '|[-+]?\\\\.(?:inf|Inf|INF)' +\n // .nan\n '|\\\\.(?:nan|NaN|NAN))$')\n\nconst YAML_FLOAT_SPECIAL_PATTERN = new RegExp(\n '^(?:' +\n // .inf\n '[-+]?\\\\.(?:inf|Inf|INF)' +\n // .nan\n '|\\\\.(?:nan|NaN|NAN))$')\n\nfunction resolveYamlFloat (source: string) {\n if (!YAML_FLOAT_PATTERN.test(source)) return NOT_RESOLVED\n\n let value = source.toLowerCase().replace(/_/g, '')\n const sign = value[0] === '-' ? -1 : 1\n\n if ('+-'.includes(value[0])) value = value.slice(1)\n\n if (value === '.inf') return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY\n if (value === '.nan') return NaN\n\n let result = 0\n\n if (value.includes(':')) {\n for (const part of value.split(':')) result = result * 60 + Number(part)\n result *= sign\n } else {\n result = sign * parseFloat(value)\n }\n\n if (Number.isFinite(result) || YAML_FLOAT_SPECIAL_PATTERN.test(source)) return result\n return NOT_RESOLVED\n}\n\nfunction representYamlFloat (object: number) {\n if (isNaN(object)) return '.nan'\n if (object === Number.POSITIVE_INFINITY) return '.inf'\n if (object === Number.NEGATIVE_INFINITY) return '-.inf'\n if (Object.is(object, -0)) return '-0.0'\n\n const result = object.toString(10)\n return /^[-+]?[0-9]+e/.test(result) ? result.replace('e', '.e') : result\n}\n\n/** @category Tags */\nconst floatYaml11Tag = defineScalarTag('tag:yaml.org,2002:float', {\n implicit: true,\n // Superset of source.charAt(0) over all matched inputs: optional sign, '.', or digit\n // ('.inf'/'.nan' start with '.').\n implicitFirstChars: ['-', '+', '.', ...'0123456789'],\n resolve: resolveYamlFloat,\n identify: (object) =>\n // No ancient boxed numbers support\n typeof object === 'number' &&\n (\n // We land here all numbers, not handled (declined) by !!int `.identify`\n // The same condition as for !!int, but reversed.\n\n // Filter out integers...\n !Number.isInteger(object) ||\n // but allow negative zero\n Object.is(object, -0) ||\n // and integers with exponential form\n object.toString(10).indexOf('e') >= 0\n ),\n represent: representYamlFloat\n})\n\nexport { floatYaml11Tag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\n/**\n * Enables merge keys in {@link CORE_SCHEMA} when added with\n * {@link Schema.withTags}.\n *\n * @category Tags\n */\nconst mergeTag = defineScalarTag('tag:yaml.org,2002:merge', {\n implicit: true,\n // source.charAt(0) over matched implicit inputs: '<' ('<<').\n implicitFirstChars: ['<'],\n // Merge semantics live in the tag, not in the value: the constructor acts on\n // a key tagged `!!merge`, so `<<` anywhere else is just this string.\n resolve: (source, isExplicit) => {\n if (source === '<<' || (isExplicit && source === '')) return '<<'\n return NOT_RESOLVED\n },\n identify: () => false\n})\n\nexport { mergeTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/\n\nfunction resolveYamlBinary (source: string) {\n // Strip allowed whitespace first, so validation stays a plain base64 check.\n const input = source.replace(/\\s/g, '')\n if (input.length % 4 !== 0 || !BASE64_PATTERN.test(input)) return NOT_RESOLVED\n\n const binary = atob(input)\n const result = new Uint8Array(binary.length)\n for (let index = 0; index < binary.length; index++) {\n result[index] = binary.charCodeAt(index)\n }\n return result\n}\n\nfunction representYamlBinary (object: Uint8Array) {\n let binary = ''\n for (let index = 0; index < object.length; index++) {\n binary += String.fromCharCode(object[index])\n }\n return btoa(binary)\n}\n\n/**\n * The `!!binary` tag, represented as a `Uint8Array`.\n *\n * @category Tags\n */\nconst binaryTag = defineScalarTag('tag:yaml.org,2002:binary', {\n resolve: resolveYamlBinary,\n identify: (object) => Object.prototype.toString.call(object) === '[object Uint8Array]',\n represent: representYamlBinary\n})\n\nexport { binaryTag }\n","import { defineScalarTag, NOT_RESOLVED } from '../../tag.ts'\n\nconst YAML_DATE_REGEXP = new RegExp(\n '^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$')\n\nconst YAML_TIMESTAMP_REGEXP = new RegExp(\n '^([0-9][0-9][0-9][0-9])' +\n '-([0-9][0-9]?)' +\n '-([0-9][0-9]?)' +\n '(?:[Tt]|[ \\\\t]+)' +\n '([0-9][0-9]?)' +\n ':([0-9][0-9])' +\n ':([0-9][0-9])' +\n '(?:\\\\.([0-9]*))?' +\n '(?:[ \\\\t]*(Z|([-+])([0-9][0-9]?)' +\n '(?::([0-9][0-9]))?))?$')\n\nfunction makeUtcDate (\n year: number,\n month: number,\n day: number,\n hour = 0,\n minute = 0,\n second = 0,\n fraction = 0\n) {\n const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction))\n\n // Date.UTC() treats years 0..99 as 1900..1999. Restore the parsed YAML year\n // before validating calendar normalization, e.g. reject 0001-02-29.\n date.setUTCFullYear(year, month, day)\n\n return date\n}\n\nfunction resolveYamlTimestamp (source: string) {\n let match = YAML_DATE_REGEXP.exec(source)\n if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(source)\n if (match === null) return NOT_RESOLVED\n\n const year = +(match[1])\n const month = +(match[2]) - 1\n const day = +(match[3])\n\n // Date-only form (`YYYY-MM-DD`) has no time captures.\n if (!match[4]) {\n const date = makeUtcDate(year, month, day)\n // Reject dates that JS would normalize, e.g. 2023-02-29 -> 2023-03-01.\n if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) {\n return NOT_RESOLVED\n }\n return date\n }\n\n const hour = +(match[4])\n const minute = +(match[5])\n const second = +(match[6])\n let fraction = 0\n\n // Reject times that JS would normalize into the next minute/hour/day.\n if (hour > 23 || minute > 59 || second > 59) return NOT_RESOLVED\n\n if (match[7]) {\n let value = match[7].slice(0, 3)\n while (value.length < 3) value += '0'\n fraction = +value\n }\n\n const date = makeUtcDate(year, month, day, hour, minute, second, fraction)\n\n // Reject invalid calendar dates before applying timezone offset.\n if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) {\n return NOT_RESOLVED\n }\n\n if (match[9]) {\n const offsetHour = +(match[10])\n const offsetMinute = +(match[11] || 0)\n // Reject timezone offsets that JS date arithmetic would otherwise accept.\n if (offsetHour > 23 || offsetMinute > 59) return NOT_RESOLVED\n\n const offset = (offsetHour * 60 + offsetMinute) * 60000\n date.setTime(date.getTime() - (match[9] === '-' ? -offset : offset))\n }\n\n return date\n}\n\n/**\n * The YAML 1.1 `!!timestamp` tag, represented as a JavaScript `Date`.\n *\n * @category Tags\n */\nconst timestampTag = defineScalarTag('tag:yaml.org,2002:timestamp', {\n implicit: true,\n // Both patterns start with a 4-digit year, so source.charAt(0) is always a digit.\n implicitFirstChars: [...'0123456789'],\n resolve: resolveYamlTimestamp,\n identify: (object) => object instanceof Date,\n represent: (object: Date) => object.toISOString()\n})\n\nexport { timestampTag }\n","import { defineSequenceTag } from '../../tag.ts'\n\n/** @category Tags */\nconst seqTag = defineSequenceTag('tag:yaml.org,2002:seq', {\n create: () => [] as unknown[],\n addItem: (container, item) => {\n container.push(item)\n },\n identify: Array.isArray\n})\n\nexport { seqTag }\n","function isPlainObject (data: unknown): boolean {\n if (data === null || typeof data !== 'object' || Array.isArray(data)) return false\n const prototype = Object.getPrototypeOf(data)\n return prototype === null || prototype === Object.prototype\n}\n\n// Project `object` onto `keys`. Absent keys are skipped (so the result can be\n// safely spread over defaults without clobbering them with `undefined`), hence\n// the `Partial` return.\nfunction pick<T extends object, K extends keyof T> (object: T, keys: readonly K[]): Partial<Pick<T, K>> {\n const result: Partial<Pick<T, K>> = {}\n for (const key of keys) {\n if (object[key] !== undefined) result[key] = object[key]\n }\n return result\n}\n\nexport {\n isPlainObject,\n pick\n}\n","import { defineSequenceTag } from '../../tag.ts'\nimport { isPlainObject } from '../../common/object.ts'\n\n/**\n * Provided only for YAML 1.1 compatibility and supported by the loader only.\n * JavaScript has no dedicated class to represent this type, so it cannot be\n * identified and dumped.\n *\n * ```yaml\n * !!omap\n * - one: 1\n * - two: 2\n * ```\n *\n * is loaded as\n *\n * ```javascript\n * [\n * { one: 1 },\n * { two: 2 }\n * ]\n * ```\n *\n * @category Tags\n */\nconst omapTag = defineSequenceTag('tag:yaml.org,2002:omap', {\n create: (): { list: unknown[]; seen: Set<unknown> } => ({ list: [], seen: new Set() }),\n addItem: (carrier, item) => {\n let key: unknown\n\n if (item instanceof Map) {\n if (item.size !== 1) return 'cannot resolve an ordered map item'\n key = item.keys().next().value\n } else if (isPlainObject(item)) {\n const itemKeys = Object.keys(item as Record<string, unknown>)\n if (itemKeys.length !== 1) return 'cannot resolve an ordered map item'\n key = itemKeys[0]\n } else {\n return 'cannot resolve an ordered map item'\n }\n\n if (carrier.seen.has(key)) return 'duplicate key in ordered map'\n carrier.seen.add(key)\n carrier.list.push(item)\n return ''\n },\n finalize: (carrier): unknown[] => carrier.list,\n identify: () => false\n})\n\nexport { omapTag }\n","import { defineSequenceTag } from '../../tag.ts'\n\n/**\n * Provided only for YAML 1.1 compatibility and supported by the loader only.\n * JavaScript has no dedicated class to represent this type, so it cannot be\n * identified and dumped.\n *\n * ```yaml\n * !!pairs\n * - one: 1\n * - two: 2\n * ```\n *\n * is loaded as\n *\n * ```javascript\n * [\n * ['one', 1],\n * ['two', 2]\n * ]\n * ```\n *\n * @category Tags\n */\nconst pairsTag = defineSequenceTag('tag:yaml.org,2002:pairs', {\n create: () => [] as [unknown, unknown][],\n addItem: (container, item) => {\n if (item instanceof Map) {\n if (item.size !== 1) return 'cannot resolve a pairs item'\n\n container.push(item.entries().next().value!)\n return ''\n }\n\n if (Object.prototype.toString.call(item) !== '[object Object]') {\n return 'cannot resolve a pairs item'\n }\n\n const object = item as Record<string, unknown>\n const keys = Object.keys(object)\n\n if (keys.length !== 1) return 'cannot resolve a pairs item'\n\n container.push([keys[0], object[keys[0]]])\n return ''\n },\n identify: () => false\n})\n\nexport { pairsTag }\n","import { defineMappingTag } from '../../tag.ts'\nimport { isPlainObject } from '../../common/object.ts'\n\n/**\n * This is the default mapping implementation. It uses `{}` objects and has only\n * partial functionality due to language limitations. This choice was made\n * because users expect to get JavaScript objects, and it was left unchanged to\n * avoid too many breaking changes in the v5 release.\n *\n * Side effects:\n *\n * - `Object.hasOwn()` checks or `for...of` loops are required for safe use (to\n * avoid falling through to prototypes).\n * - Only scalar string keys are supported properly.\n * - Other scalar keys, such as `null` and numbers, are converted to strings.\n * This is historical behaviour, and it can cause side effects such as\n * problems with `!!merge`.\n *\n * Note that non-string scalar keys may be deprecated in future versions.\n *\n * Ideally, use {@link realMapTag} instead.\n *\n * @category Tags\n */\nconst mapTag = defineMappingTag('tag:yaml.org,2002:map', {\n create: (): Record<string, unknown> => ({}),\n identify: isPlainObject,\n // Dump side: wrap the plain object into the canonical `Map` form the writer\n // walks. Shallow — keys/values stay references to the originals.\n represent: (o: Record<string, unknown>) => {\n const map = new Map<string, unknown>()\n for (const key of Object.keys(o)) map.set(key, o[key])\n return map\n },\n addPair: (container, key, value) => {\n if (key !== null && typeof key === 'object') {\n return 'object-based map does not support complex keys'\n }\n const normalizedKey = String(key)\n if (normalizedKey === '__proto__') {\n // Define as an own data property so a literal `__proto__` key stays data\n // and never invokes the prototype setter.\n Object.defineProperty(container, normalizedKey, {\n value, enumerable: true, configurable: true, writable: true\n })\n } else {\n container[normalizedKey] = value\n }\n return ''\n },\n // hasOwn, not `in`: a plain object inherits `toString` and friends.\n has: (container, key) => {\n if (key !== null && typeof key === 'object') return false\n return Object.prototype.hasOwnProperty.call(container, String(key))\n },\n keys: (container) => Object.keys(container),\n get: (container, key) => {\n const normalizedKey = String(key)\n // key is from `keys()` only. Strong check is not needed, but leave for sure.\n if (!Object.prototype.hasOwnProperty.call(container, normalizedKey)) return null\n return container[normalizedKey]\n }\n})\n\nexport { mapTag, isPlainObject }\n","import { defineMappingTag } from '../../tag.ts'\n\n/**\n * The YAML 1.1 `!!set` tag, represented as a JavaScript `Set`.\n *\n * @category Tags\n */\nconst setTag = defineMappingTag('tag:yaml.org,2002:set', {\n create: () => new Set<unknown>(),\n identify: (data) => data instanceof Set,\n represent: (data: Set<unknown>) => {\n const map = new Map<unknown, null>()\n for (const key of data) map.set(key, null)\n return map\n },\n addPair: (container, key, value) => {\n if (value !== null) return 'cannot resolve a set item'\n container.add(key)\n return ''\n },\n has: (container, key) => container.has(key),\n keys: (container) => container.keys(),\n get: () => null\n})\n\nexport { setTag }\n","import {\n NOT_RESOLVED,\n type MappingTagDefinition,\n type ScalarTagDefinition,\n type SequenceTagDefinition,\n type TagDefinition\n} from './tag.ts'\nimport { strTag } from './tag/scalar/str.ts'\nimport { nullCoreTag } from './tag/scalar/null_core.ts'\nimport { nullJsonTag } from './tag/scalar/null_json.ts'\nimport { nullYaml11Tag } from './tag/scalar/null_yaml11.ts'\nimport { boolCoreTag } from './tag/scalar/bool_core.ts'\nimport { boolJsonTag } from './tag/scalar/bool_json.ts'\nimport { boolYaml11Tag } from './tag/scalar/bool_yaml11.ts'\nimport { intCoreTag } from './tag/scalar/int_core.ts'\nimport { intJsonTag } from './tag/scalar/int_json.ts'\nimport { intYaml11Tag } from './tag/scalar/int_yaml11.ts'\nimport { floatCoreTag } from './tag/scalar/float_core.ts'\nimport { floatJsonTag } from './tag/scalar/float_json.ts'\nimport { floatYaml11Tag } from './tag/scalar/float_yaml11.ts'\nimport { mergeTag } from './tag/scalar/merge.ts'\nimport { binaryTag } from './tag/scalar/binary.ts'\nimport { timestampTag } from './tag/scalar/timestamp.ts'\nimport { seqTag } from './tag/sequence/seq.ts'\nimport { omapTag } from './tag/sequence/omap.ts'\nimport { pairsTag } from './tag/sequence/pairs.ts'\nimport { mapTag } from './tag/mapping/map.ts'\nimport { setTag } from './tag/mapping/set.ts'\n\ninterface TagDefinitionMap {\n scalar: Record<string, ScalarTagDefinition>\n sequence: Record<string, SequenceTagDefinition>\n mapping: Record<string, MappingTagDefinition>\n}\n\ninterface TagDefinitionListMap {\n scalar: ScalarTagDefinition[]\n sequence: SequenceTagDefinition[]\n mapping: MappingTagDefinition[]\n}\n\nfunction createTagDefinitionMap (): TagDefinitionMap {\n return {\n scalar: Object.create(null),\n sequence: Object.create(null),\n mapping: Object.create(null)\n }\n}\n\nfunction createTagDefinitionListMap (): TagDefinitionListMap {\n return {\n scalar: [],\n sequence: [],\n mapping: []\n }\n}\n\nfunction compileTags (tags: readonly TagDefinition[]) {\n const result: TagDefinition[] = []\n\n for (const tag of tags) {\n let index = result.length\n\n for (let previousIndex = 0; previousIndex < result.length; previousIndex++) {\n const previous = result[previousIndex]\n\n if (previous.nodeKind === tag.nodeKind &&\n previous.tagName === tag.tagName &&\n previous.matchByTagPrefix === tag.matchByTagPrefix) {\n index = previousIndex\n break\n }\n }\n\n result[index] = tag\n }\n\n return result\n}\n\n/**\n * Controls tag resolution when loading and type selection when dumping.\n *\n * @category Schemas\n */\nclass Schema {\n readonly tags: readonly TagDefinition[]\n /** @internal */\n readonly implicitScalarTags: readonly ScalarTagDefinition[]\n\n /**\n * Dispatch implicit scalar resolvers by `source.charAt(0)`. Each bucket holds\n * the resolvers that may match that key, in schema order; a key absent from\n * the map uses\n * {@link Schema.implicitScalarAnyFirstChar}\n * (resolvers that declared no first-char constraint, so they apply to any\n * first character).\n */\n private readonly implicitScalarByFirstChar: ReadonlyMap<string, readonly ScalarTagDefinition[]>\n private readonly implicitScalarAnyFirstChar: readonly ScalarTagDefinition[]\n\n /**\n * The default scalar tag (`!!str`), resolved once so the composer's fallback\n * for unresolved plain scalars avoids a keyed lookup per scalar.\n *\n * @internal\n */\n readonly defaultScalarTag: ScalarTagDefinition\n\n /**\n * The default container tags (`!!seq` / `!!map`), used by the dumper: when a\n * value is identified by its default tag, the tag is implicit and not\n * printed. Undefined if the schema does not define them (then such values\n * can't be dumped).\n *\n * @internal\n */\n readonly defaultSequenceTag: SequenceTagDefinition | undefined\n /** @internal */\n readonly defaultMappingTag: MappingTagDefinition | undefined\n private readonly exact: TagDefinitionMap\n private readonly prefix: TagDefinitionListMap\n\n constructor (tags: readonly TagDefinition[]) {\n const compiledTags = compileTags(tags)\n const implicitScalarTags: ScalarTagDefinition[] = []\n const exact = createTagDefinitionMap()\n const prefix = createTagDefinitionListMap()\n\n for (const tag of compiledTags) {\n if (tag.nodeKind === 'scalar' && tag.implicit) {\n if (tag.matchByTagPrefix) {\n throw new Error('Implicit scalar tags cannot match by tag prefix')\n }\n\n implicitScalarTags.push(tag)\n }\n\n switch (tag.nodeKind) {\n case 'scalar':\n if (tag.matchByTagPrefix) prefix.scalar.push(tag)\n else exact.scalar[tag.tagName] = tag\n break\n case 'sequence':\n if (tag.matchByTagPrefix) prefix.sequence.push(tag)\n else exact.sequence[tag.tagName] = tag\n break\n case 'mapping':\n if (tag.matchByTagPrefix) prefix.mapping.push(tag)\n else exact.mapping[tag.tagName] = tag\n break\n }\n }\n\n const implicitScalarAnyFirstChar = implicitScalarTags.filter(tag => tag.implicitFirstChars === null)\n\n const keys = new Set<string>()\n for (const tag of implicitScalarTags) {\n if (tag.implicitFirstChars !== null) {\n for (const key of tag.implicitFirstChars) keys.add(key)\n }\n }\n\n const implicitScalarByFirstChar = new Map<string, ScalarTagDefinition[]>()\n for (const key of keys) {\n implicitScalarByFirstChar.set(key, implicitScalarTags.filter(tag =>\n tag.implicitFirstChars === null || tag.implicitFirstChars.indexOf(key) !== -1))\n }\n\n const defaultScalarTag = exact.scalar['tag:yaml.org,2002:str']\n if (!defaultScalarTag) throw new Error('schema does not define the default scalar tag (tag:yaml.org,2002:str)')\n\n this.tags = compiledTags\n this.implicitScalarTags = implicitScalarTags\n this.implicitScalarByFirstChar = implicitScalarByFirstChar\n this.implicitScalarAnyFirstChar = implicitScalarAnyFirstChar\n this.defaultScalarTag = defaultScalarTag\n this.defaultSequenceTag = exact.sequence['tag:yaml.org,2002:seq']\n this.defaultMappingTag = exact.mapping['tag:yaml.org,2002:map']\n this.exact = exact\n this.prefix = prefix\n }\n\n /** @internal */\n lookupScalarTag (tagName: string): ScalarTagDefinition | undefined {\n const exactTag = this.exact.scalar[tagName]\n if (exactTag) return exactTag\n\n for (const tag of this.prefix.scalar) {\n if (tagName.startsWith(tag.tagName)) return tag\n }\n\n return undefined\n }\n\n /** @internal */\n lookupSequenceTag (tagName: string): SequenceTagDefinition | undefined {\n const exactTag = this.exact.sequence[tagName]\n if (exactTag) return exactTag\n\n for (const tag of this.prefix.sequence) {\n if (tagName.startsWith(tag.tagName)) return tag\n }\n\n return undefined\n }\n\n /** @internal */\n lookupMappingTag (tagName: string): MappingTagDefinition | undefined {\n const exactTag = this.exact.mapping[tagName]\n if (exactTag) return exactTag\n\n for (const tag of this.prefix.mapping) {\n if (tagName.startsWith(tag.tagName)) return tag\n }\n\n return undefined\n }\n\n /** @internal */\n resolveImplicitScalarTag (source: string): { value: unknown, tag: ScalarTagDefinition } {\n const candidates = this.implicitScalarByFirstChar.get(source.charAt(0)) ??\n this.implicitScalarAnyFirstChar\n\n for (const tag of candidates) {\n const value = tag.resolve(source, false, tag.tagName)\n if (value !== NOT_RESOLVED) return { value, tag }\n }\n\n const tag = this.defaultScalarTag\n return { value: tag.resolve(source, false, tag.tagName), tag }\n }\n\n /**\n * Creates a new schema with the specified tags added. If a tag already\n * exists, it is replaced by the specified tag.\n *\n * @example\n *\n * ```javascript\n * import { CORE_SCHEMA, mergeTag, realMapTag } from 'js-yaml'\n *\n * const schema = CORE_SCHEMA.withTags(mergeTag, realMapTag)\n * ```\n */\n withTags (...tags: Array<TagDefinition | readonly TagDefinition[]>): Schema {\n let flatTags: TagDefinition[] = []\n for (const tag of tags) flatTags = flatTags.concat(tag)\n\n return new Schema([...this.tags, ...flatTags])\n }\n}\n\n/**\n * The YAML 1.2 Failsafe Schema: strings, sequences, and mappings.\n *\n * @category Schemas\n */\nconst FAILSAFE_SCHEMA = new Schema([\n strTag,\n seqTag,\n mapTag\n])\n\n/**\n * The YAML 1.2 JSON Schema. It uses JSON scalar forms while retaining YAML\n * collection syntax.\n *\n * @category Schemas\n */\nconst JSON_SCHEMA = new Schema([\n ...FAILSAFE_SCHEMA.tags,\n nullJsonTag,\n boolJsonTag,\n intJsonTag,\n floatJsonTag\n])\n\n/**\n * The default schema for the loaders. Note, {@link CORE_SCHEMA} comes\n * without the `!!merge` tag. You can easily enable it if needed.\n *\n * @example\n * Enable {@link mergeTag}:\n *\n * ```javascript\n * import { load, CORE_SCHEMA, mergeTag } from 'js-yaml'\n *\n * try {\n * load(data, { schema: CORE_SCHEMA.withTags(mergeTag) })\n * } catch (e) {\n * console.error(e)\n * }\n * ```\n *\n * @category Schemas\n */\nconst CORE_SCHEMA = new Schema([\n ...FAILSAFE_SCHEMA.tags,\n nullCoreTag,\n boolCoreTag,\n intCoreTag,\n floatCoreTag\n])\n\n/**\n * YAML 1.1-compatible schema.\n *\n * @category Schemas\n */\nconst YAML11_SCHEMA = new Schema([\n ...FAILSAFE_SCHEMA.tags,\n nullYaml11Tag,\n boolYaml11Tag,\n intYaml11Tag,\n floatYaml11Tag,\n timestampTag,\n mergeTag,\n binaryTag,\n omapTag,\n pairsTag,\n setTag\n])\n\n/**\n * The dumper schema for maximum compatibility. It combines all supported type\n * variants from YAML 1.1 and YAML 1.2 so strings matching any of them are\n * quoted. This makes the generated YAML more compatible with other parsers.\n *\n * The schema is based on YAML 1.1, but extends `!!int` and `!!float` to accept\n * both YAML 1.1 and Core Schema forms, since Core Schema supports some forms\n * that YAML 1.1 does not.\n *\n * @category Schemas\n */\nconst DUMP_SCHEMA = YAML11_SCHEMA.withTags(\n {\n ...intYaml11Tag,\n resolve: (source, isExplicit, tagName) => {\n const result = intYaml11Tag.resolve(source, isExplicit, tagName)\n return result === NOT_RESOLVED ? intCoreTag.resolve(source, isExplicit, tagName) : result\n }\n },\n {\n ...floatYaml11Tag,\n resolve: (source, isExplicit, tagName) => {\n const result = floatYaml11Tag.resolve(source, isExplicit, tagName)\n return result === NOT_RESOLVED ? floatCoreTag.resolve(source, isExplicit, tagName) : result\n }\n }\n)\n\nexport {\n Schema,\n FAILSAFE_SCHEMA,\n JSON_SCHEMA,\n CORE_SCHEMA,\n YAML11_SCHEMA,\n DUMP_SCHEMA\n}\n","import { defineMappingTag } from '../../tag.ts'\nimport { isPlainObject } from '../../common/object.ts'\n\n/**\n * Recommended when non-string keys are actually needed. It uses native\n * JavaScript `Map` objects, so keys keep their constructed types instead of\n * being converted to strings.\n *\n * It is not the default to avoid widespread breaking changes in existing\n * projects. `Map` has a different access API and does not pass deep equality\n * checks against `{}`-based fixtures. Alongside the other changes in v5,\n * making it the default was considered too disruptive.\n *\n * If these differences are acceptable for your project, we recommend using\n * {@link realMapTag} to guarantee the absence of problems and side effects.\n *\n * @example\n * Enable {@link realMapTag}:\n *\n * ```javascript\n * import { load, CORE_SCHEMA, realMapTag } from 'js-yaml'\n *\n * try {\n * load(data, { schema: CORE_SCHEMA.withTags(realMapTag) })\n * } catch (e) {\n * console.error(e)\n * }\n * ```\n *\n * @category Tags\n */\nconst realMapTag = defineMappingTag('tag:yaml.org,2002:map', {\n create: () => new Map<unknown, unknown>(),\n addPair: (container: Map<unknown, unknown>, key, value) => {\n container.set(key, value)\n return ''\n },\n has: (container: Map<unknown, unknown>, key) => container.has(key),\n keys: (container: Map<unknown, unknown>) => container.keys(),\n get: (container: Map<unknown, unknown>, key) => container.get(key),\n // Dump side: handle both a real `Map` and a plain object, so this tag fully\n // replaces the default map representation when dumping too.\n identify: (data) => data instanceof Map || isPlainObject(data),\n // Dump side: the canonical mapping form is a `Map`. A real `Map` passes\n // through untouched (keys keep their type); a plain object is wrapped\n // shallowly. Lossless — nothing is stringified.\n represent: (data) => {\n if (data instanceof Map) return data\n const map = new Map<unknown, unknown>()\n const obj = data as Record<string, unknown>\n for (const key of Object.keys(obj)) map.set(key, obj[key])\n return map\n }\n})\n\nexport { realMapTag }\n","import { defineMappingTag } from '../../tag.ts'\nimport { isPlainObject } from '../../common/object.ts'\n\n// Coerce a constructed key into the string identity a `{}` representation uses.\n// Returns null for a nested array key (an array element that is itself an\n// array), which would otherwise blow up exponentially when stringified via\n// aliases.\nfunction normalizeKey (key: unknown): string | null {\n if (Array.isArray(key)) {\n const array = Array.prototype.slice.call(key) as unknown[]\n\n for (let index = 0; index < array.length; index++) {\n if (Array.isArray(array[index])) return null\n\n if (typeof array[index] === 'object' &&\n Object.prototype.toString.call(array[index]) === '[object Object]') {\n array[index] = '[object Object]'\n }\n }\n\n return String(array)\n }\n\n if (typeof key === 'object' &&\n Object.prototype.toString.call(key) === '[object Object]') {\n return '[object Object]'\n }\n\n return String(key)\n}\n\n/**\n * This implementation exists solely to reproduce v4 behavior exactly. Its use\n * is strongly discouraged. If complex or non-string keys are needed, use\n * {@link realMapTag} instead.\n *\n * @category Tags\n */\nconst legacyMapTag = defineMappingTag('tag:yaml.org,2002:map', {\n create: (): Record<string, unknown> => ({}),\n identify: isPlainObject,\n // Dump side: wrap the plain object into the canonical `Map` form the writer\n // walks. Shallow — keys/values stay references to the originals.\n represent: (o: Record<string, unknown>) => {\n const map = new Map<string, unknown>()\n for (const key of Object.keys(o)) map.set(key, o[key])\n return map\n },\n addPair: (container, key, value) => {\n const normalizedKey = normalizeKey(key)\n if (normalizedKey === null) return 'nested arrays are not supported inside keys'\n if (normalizedKey === '__proto__') {\n // Define as an own data property so a literal `__proto__` key stays data\n // and never invokes the prototype setter.\n Object.defineProperty(container, normalizedKey, {\n value, enumerable: true, configurable: true, writable: true\n })\n } else {\n container[normalizedKey] = value\n }\n return ''\n },\n // hasOwn, not `in`: a plain object inherits `toString` and friends.\n has: (container, key) => {\n const normalizedKey = normalizeKey(key)\n return normalizedKey !== null && Object.prototype.hasOwnProperty.call(container, normalizedKey)\n },\n keys: (container) => Object.keys(container),\n get: (container, key) => {\n const normalizedKey = String(key)\n // key is from `keys()` only. Strong check is not needed, but leave for sure.\n if (!Object.prototype.hasOwnProperty.call(container, normalizedKey)) return null\n return container[normalizedKey]\n }\n})\n\nexport { legacyMapTag, isPlainObject }\n","export interface SnippetMark {\n name?: string | null\n buffer: string\n position: number\n line: number\n column: number\n snippet?: string | null\n}\n\ninterface SnippetOptions {\n maxLength?: number\n indent?: number\n linesBefore?: number\n linesAfter?: number\n}\n\nconst DEFAULT_SNIPPET_OPTIONS: Required<SnippetOptions> = {\n maxLength: 79,\n indent: 1,\n linesBefore: 3,\n linesAfter: 2\n}\n\n// get snippet for a single line, respecting maxLength\nfunction getLine (buffer: string, lineStart: number, lineEnd: number, position: number, maxLineLength: number) {\n let head = ''\n let tail = ''\n const maxHalfLength = Math.floor(maxLineLength / 2) - 1\n\n if (position - lineStart > maxHalfLength) {\n head = ' ... '\n lineStart = position - maxHalfLength + head.length\n }\n\n if (lineEnd - position > maxHalfLength) {\n tail = ' ...'\n lineEnd = position + maxHalfLength - tail.length\n }\n\n return {\n str: head + buffer.slice(lineStart, lineEnd).replace(/\\t/g, '→') + tail,\n pos: position - lineStart + head.length // relative position\n }\n}\n\nfunction padStart (string: string, max: number) {\n // max() protects from negativa value, to avoid exception.\n return ' '.repeat(Math.max(max - string.length, 0)) + string\n}\n\nfunction makeSnippet (mark: SnippetMark, options?: SnippetOptions) {\n if (!mark.buffer) return null\n\n const opts = { ...DEFAULT_SNIPPET_OPTIONS, ...options }\n\n const re = /\\r?\\n|\\r|\\0/g\n const lineStarts = [0]\n const lineEnds: number[] = []\n let match: RegExpExecArray | null\n let foundLineNo = -1\n\n while ((match = re.exec(mark.buffer))) {\n lineEnds.push(match.index)\n lineStarts.push(match.index + match[0].length)\n\n if (mark.position <= match.index && foundLineNo < 0) {\n foundLineNo = lineStarts.length - 2\n }\n }\n\n if (foundLineNo < 0) foundLineNo = lineStarts.length - 1\n\n let result = ''\n const lineNoLength = Math.min(mark.line + opts.linesAfter, lineEnds.length).toString().length\n const maxLineLength = opts.maxLength - (opts.indent + lineNoLength + 3)\n\n for (let i = 1; i <= opts.linesBefore; i++) {\n if (foundLineNo - i < 0) break\n const line = getLine(\n mark.buffer,\n lineStarts[foundLineNo - i],\n lineEnds[foundLineNo - i],\n mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]),\n maxLineLength\n )\n result = `${' '.repeat(opts.indent)}${padStart((mark.line - i + 1).toString(), lineNoLength)} | ${line.str}\\n${result}`\n }\n\n const line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength)\n result += `${' '.repeat(opts.indent)}${padStart((mark.line + 1).toString(), lineNoLength)} | ${line.str}\\n`\n result += `${'-'.repeat(opts.indent + lineNoLength + 3 + line.pos)}^\\n`\n\n for (let i = 1; i <= opts.linesAfter; i++) {\n if (foundLineNo + i >= lineEnds.length) break\n const line = getLine(\n mark.buffer,\n lineStarts[foundLineNo + i],\n lineEnds[foundLineNo + i],\n mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]),\n maxLineLength\n )\n result += `${' '.repeat(opts.indent)}${padStart((mark.line + i + 1).toString(), lineNoLength)} | ${line.str}\\n`\n }\n\n return result.replace(/\\n$/, '')\n}\n\nexport default makeSnippet\n","import makeSnippet, { type SnippetMark } from './snippet.ts'\n\n// YAML error class. http://stackoverflow.com/questions/8458984\n//\nfunction formatError (exception: YAMLException, compact?: boolean) {\n let where = ''\n\n if (!exception.mark) return exception.reason\n\n if (exception.mark.name) {\n where += `in \"${exception.mark.name}\" `\n }\n\n where += `(${exception.mark.line + 1}:${exception.mark.column + 1})`\n\n if (!compact && exception.mark.snippet) {\n where += `\\n\\n${exception.mark.snippet}`\n }\n\n return `${exception.reason} ${where}`\n}\n\n/**\n * A YAML error. Unlike an ordinary `Error`, it adds a source snippet showing\n * the location of the problem to the error message, when available.\n *\n * @category Main\n */\nclass YAMLException extends Error {\n reason: string\n mark?: SnippetMark\n\n /**\n * Optional `mark` contains source snippet data. Usually, use\n * {@link YAMLException.throwAt} instead of passing it directly.\n */\n constructor (reason: string, mark?: SnippetMark) {\n super()\n\n this.name = 'YAMLException'\n this.reason = reason\n this.mark = mark\n this.message = formatError(this, false)\n\n // Guard for ancient browsers\n if (Error.captureStackTrace) {\n // Include stack trace in error object,\n Error.captureStackTrace(this, this.constructor)\n }\n }\n\n /**\n * Returns the formatted error, omitting the source snippet in compact mode.\n */\n toString (compact?: boolean) {\n return `${this.name}: ${formatError(this, compact)}`\n }\n\n /**\n * Builds a YAMLException with a source snippet and throws it. `source` is\n * the raw input text; `position` is an offset into it.\n */\n static throwAt (source: string, position: number, message: string, filename = ''): never {\n let line = 0\n let lineStart = 0\n\n for (let index = 0; index < position; index++) {\n const ch = source.charCodeAt(index)\n\n if (ch === 0x0A/* LF */) {\n line++\n lineStart = index + 1\n } else if (ch === 0x0D/* CR */) {\n line++\n if (source.charCodeAt(index + 1) === 0x0A/* LF */) index++\n lineStart = index + 1\n }\n }\n\n const mark: SnippetMark = {\n name: filename,\n buffer: source,\n position,\n line,\n column: position - lineStart\n }\n\n mark.snippet = makeSnippet(mark)\n throw new YAMLException(message, mark)\n }\n}\n\nexport { YAMLException }\n","/** @category Events */\nconst EVENT_ID = {\n DOCUMENT: 1,\n SEQUENCE: 2,\n MAPPING: 3,\n SCALAR: 4,\n ALIAS: 5,\n POP: 6\n} as const\n\n/** @category Events */\ntype EventId = typeof EVENT_ID[keyof typeof EVENT_ID]\n\n/** @category Nodes */\nconst SCALAR_STYLE = {\n PLAIN: 1,\n SINGLE_QUOTED: 2,\n DOUBLE_QUOTED: 3,\n LITERAL_BLOCK: 4,\n FOLDED_BLOCK: 5\n} as const\n\n/** @category Nodes */\ntype ScalarStyle = typeof SCALAR_STYLE[keyof typeof SCALAR_STYLE]\n\n/** @category Nodes */\nconst COLLECTION_STYLE = {\n BLOCK: 1,\n FLOW: 2\n} as const\n\n/** @category Nodes */\ntype CollectionStyle = typeof COLLECTION_STYLE[keyof typeof COLLECTION_STYLE]\n\n/** @category Nodes */\nconst CHOMPING_MODE = {\n CLIP: 1,\n STRIP: 2,\n KEEP: 3\n} as const\n\n/** @category Nodes */\ntype ChompingMode = typeof CHOMPING_MODE[keyof typeof CHOMPING_MODE]\n\n/** @category Events */\ntype DocumentDirective =\n { kind: 'yaml', version: string } |\n { kind: 'tag', handle: string, prefix: string }\n\ntype TagHandlers = Record<string, string>\n\n/** @category Events */\ninterface DocumentEvent {\n type: typeof EVENT_ID.DOCUMENT\n explicitStart: boolean\n explicitEnd: boolean\n directives: DocumentDirective[]\n}\n\n/** @category Events */\ninterface SequenceEvent {\n type: typeof EVENT_ID.SEQUENCE\n start: number\n anchorStart: number\n anchorEnd: number\n tagStart: number\n tagEnd: number\n style: CollectionStyle\n}\n\n/** @category Events */\ninterface MappingEvent {\n type: typeof EVENT_ID.MAPPING\n start: number\n anchorStart: number\n anchorEnd: number\n tagStart: number\n tagEnd: number\n style: CollectionStyle\n}\n\n/**\n * A scalar whose decoded value can be read with {@link getScalarValue}.\n *\n * @category Events\n */\ninterface ScalarEvent {\n type: typeof EVENT_ID.SCALAR\n valueStart: number\n valueEnd: number\n anchorStart: number\n anchorEnd: number\n tagStart: number\n tagEnd: number\n style: ScalarStyle\n chomping: ChompingMode\n indent: number\n fast: boolean\n}\n\n/** @category Events */\ninterface AliasEvent {\n type: typeof EVENT_ID.ALIAS\n anchorStart: number\n anchorEnd: number\n}\n\n/**\n * Closes the most recently opened document, sequence, or mapping.\n *\n * @category Events\n */\ninterface PopEvent {\n type: typeof EVENT_ID.POP\n}\n\n/**\n * Source ranges are zero-based and end-exclusive; `-1` means absent.\n *\n * @category Events\n */\ntype Event =\n DocumentEvent |\n SequenceEvent |\n MappingEvent |\n ScalarEvent |\n AliasEvent |\n PopEvent\n\nexport {\n EVENT_ID,\n SCALAR_STYLE,\n COLLECTION_STYLE,\n CHOMPING_MODE,\n\n type EventId,\n type ScalarStyle,\n type CollectionStyle,\n type ChompingMode,\n\n type DocumentDirective,\n type TagHandlers,\n type DocumentEvent,\n type SequenceEvent,\n type MappingEvent,\n type ScalarEvent,\n type AliasEvent,\n type PopEvent,\n type Event\n}\n","import {\n SCALAR_STYLE,\n CHOMPING_MODE,\n type ScalarEvent\n} from './events.ts'\n\nconst NO_RANGE = -1\n\n// --- character helpers (mirrors src/loader.ts, kept self-contained here) ---\n\nfunction simpleEscapeSequence (c: number) {\n switch (c) {\n case 0x30/* 0 */: return '\\x00'\n case 0x61/* a */: return '\\x07'\n case 0x62/* b */: return '\\x08'\n case 0x74/* t */: return '\\x09'\n case 0x09/* Tab */: return '\\x09'\n case 0x6E/* n */: return '\\x0A'\n case 0x76/* v */: return '\\x0B'\n case 0x66/* f */: return '\\x0C'\n case 0x72/* r */: return '\\x0D'\n case 0x65/* e */: return '\\x1B'\n case 0x20/* Space */: return ' '\n case 0x22/* \" */: return '\\x22'\n case 0x2F/* / */: return '/'\n case 0x5C/* \\ */: return '\\x5C'\n case 0x4E/* N */: return '\\x85'\n case 0x5F/* _ */: return '\\xA0'\n case 0x4C/* L */: return '\\u2028'\n case 0x50/* P */: return '\\u2029'\n default: return ''\n }\n}\n\nconst simpleEscapeCheck = new Array(256)\nconst simpleEscapeMap = new Array(256)\nfor (let i = 0; i < 256; i++) {\n simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0\n simpleEscapeMap[i] = simpleEscapeSequence(i)\n}\n\nfunction charFromCodepoint (c: number) {\n if (c <= 0xFFFF) {\n return String.fromCharCode(c)\n }\n return String.fromCharCode(\n ((c - 0x010000) >> 10) + 0xD800,\n ((c - 0x010000) & 0x03FF) + 0xDC00\n )\n}\n\nfunction fromHexCode (c: number) {\n if (c >= 0x30/* 0 */ && c <= 0x39/* 9 */) return c - 0x30\n const lc = c | 0x20\n // Double-quoted scalar ranges are validated by parser.ts before cooking.\n return lc - 0x61 + 10\n}\n\nfunction escapedHexLen (c: number) {\n if (c === 0x78/* x */) return 2\n if (c === 0x75/* u */) return 4\n // Double-quoted scalar ranges are validated by parser.ts before cooking.\n return 8\n}\n\n// --- line folding helpers ---\n\n// Skip a run of line breaks plus the leading whitespace of the following\n// lines, returning the number of line breaks consumed and the new position.\nfunction skipFoldedBreaks (input: string, position: number, end: number) {\n let breaks = 0\n\n while (position < end) {\n const ch = input.charCodeAt(position)\n\n if (ch === 0x0A/* LF */) {\n breaks++\n position++\n } else if (ch === 0x0D/* CR */) {\n breaks++\n position++\n if (input.charCodeAt(position) === 0x0A/* LF */) position++\n } else if (ch === 0x20/* Space */ || ch === 0x09/* Tab */) {\n position++\n } else {\n break\n }\n }\n\n return { position, breaks }\n}\n\n// Folding of line breaks between content chunks: a single break becomes a\n// space, several breaks become (count - 1) newlines.\nfunction foldedBreaks (count: number) {\n if (count === 1) return ' '\n // Called only after skipFoldedBreaks() consumed at least one line break.\n return '\\n'.repeat(count - 1)\n}\n\n// --- per-style extractors ---\n\nfunction getPlainValue (input: string, start: number, end: number) {\n let result = ''\n let position = start\n let captureStart = start\n let captureEnd = start\n\n while (position < end) {\n const ch = input.charCodeAt(position)\n\n if (ch === 0x0A/* LF */ || ch === 0x0D/* CR */) {\n result += input.slice(captureStart, captureEnd)\n const fold = skipFoldedBreaks(input, position, end)\n result += foldedBreaks(fold.breaks)\n position = captureStart = captureEnd = fold.position\n } else {\n position++\n if (ch !== 0x20/* Space */ && ch !== 0x09/* Tab */) captureEnd = position\n }\n }\n\n return result + input.slice(captureStart, captureEnd)\n}\n\nfunction getSingleQuotedValue (input: string, start: number, end: number) {\n let result = ''\n let position = start\n let captureStart = start\n let captureEnd = start\n\n while (position < end) {\n const ch = input.charCodeAt(position)\n\n if (ch === 0x27/* ' */) {\n // Within the stored range every quote is part of an escaped '' pair.\n result += input.slice(captureStart, position) + \"'\"\n position += 2\n captureStart = captureEnd = position\n } else if (ch === 0x0A/* LF */ || ch === 0x0D/* CR */) {\n result += input.slice(captureStart, captureEnd)\n const fold = skipFoldedBreaks(input, position, end)\n result += foldedBreaks(fold.breaks)\n position = captureStart = captureEnd = fold.position\n } else {\n position++\n if (ch !== 0x20/* Space */ && ch !== 0x09/* Tab */) captureEnd = position\n }\n }\n\n // Whitespace right before the closing quote is significant (it is only\n // stripped when followed by a line break).\n return result + input.slice(captureStart, end)\n}\n\nfunction getDoubleQuotedValue (input: string, start: number, end: number) {\n let result = ''\n let position = start\n let captureStart = start\n let captureEnd = start\n\n while (position < end) {\n const ch = input.charCodeAt(position)\n\n if (ch === 0x5C/* \\ */) {\n result += input.slice(captureStart, position)\n position++\n const escaped = input.charCodeAt(position)\n\n if (escaped === 0x0A/* LF */ || escaped === 0x0D/* CR */) {\n // Escaped line break: a line continuation that joins with nothing.\n position = skipFoldedBreaks(input, position, end).position\n } else if (escaped < 256 && simpleEscapeCheck[escaped]) {\n result += simpleEscapeMap[escaped]\n position++\n } else {\n // parser.ts has already rejected unknown escapes and invalid hex digits.\n let hexLength = escapedHexLen(escaped)\n let hexResult = 0\n\n for (; hexLength > 0; hexLength--) {\n position++\n const digit = fromHexCode(input.charCodeAt(position))\n hexResult = (hexResult << 4) + digit\n }\n\n result += charFromCodepoint(hexResult)\n position++\n }\n\n captureStart = captureEnd = position\n } else if (ch === 0x0A/* LF */ || ch === 0x0D/* CR */) {\n result += input.slice(captureStart, captureEnd)\n const fold = skipFoldedBreaks(input, position, end)\n result += foldedBreaks(fold.breaks)\n position = captureStart = captureEnd = fold.position\n } else {\n position++\n if (ch !== 0x20/* Space */ && ch !== 0x09/* Tab */) captureEnd = position\n }\n }\n\n return result + input.slice(captureStart, end)\n}\n\nfunction getBlockValue (\n input: string,\n start: number,\n end: number,\n indent: number,\n chomping: number,\n folded: boolean\n) {\n const textIndent = indent < 0 ? 0 : indent\n // The range starts at column 0 of the first line and includes every line\n // break, including those of trailing blank lines.\n const region = input.slice(start, end).replace(/\\r\\n?/g, '\\n')\n // An empty range is a block with no lines at all (e.g. an empty `|+`) and\n // must stay empty; a naive split would invent a phantom blank line. Otherwise\n // a trailing line break leaves a trailing '' from split() that is not a real\n // line (just the terminator of the last one), so drop it. Interior blank\n // lines are kept.\n const lines = region === ''\n ? []\n : (region.endsWith('\\n') ? region.slice(0, -1) : region).split('\\n')\n\n let result = ''\n let didReadContent = false\n let emptyLines = 0\n let atMoreIndented = false\n\n for (const line of lines) {\n // Whitespace beyond the content indentation is part of the content, so the\n // indentation scan stops at textIndent. A line is empty only when nothing\n // remains after the (capped) indentation.\n // indent < 0 means no content line was detected (a wholly blank block), so\n // every line is an empty line.\n let column = 0\n while (column < textIndent && line.charCodeAt(column) === 0x20/* Space */) column++\n\n if (indent < 0 || column >= line.length) {\n emptyLines++\n continue\n }\n\n const content = line.slice(textIndent)\n const first = content.charCodeAt(0)\n\n if (folded) {\n if (first === 0x20/* Space */ || first === 0x09/* Tab */) {\n // More-indented lines are not folded.\n atMoreIndented = true\n result += '\\n'.repeat(didReadContent ? 1 + emptyLines : emptyLines)\n } else if (atMoreIndented) {\n atMoreIndented = false\n result += '\\n'.repeat(emptyLines + 1)\n } else if (emptyLines === 0) {\n if (didReadContent) result += ' '\n } else {\n result += '\\n'.repeat(emptyLines)\n }\n } else {\n result += '\\n'.repeat(didReadContent ? 1 + emptyLines : emptyLines)\n }\n\n result += content\n didReadContent = true\n emptyLines = 0\n }\n\n if (chomping === CHOMPING_MODE.KEEP) {\n result += '\\n'.repeat(didReadContent ? 1 + emptyLines : emptyLines)\n } else if (chomping !== CHOMPING_MODE.STRIP) {\n if (didReadContent) result += '\\n'\n }\n\n return result\n}\n\n/**\n * Decodes the scalar referenced by event offsets in `input`.\n *\n * @category Events\n */\nfunction getScalarValue (input: string, scalar: ScalarEvent): string {\n if (scalar.valueStart === NO_RANGE) return ''\n\n const { valueStart, valueEnd } = scalar\n\n // Fast path: the parser marked this scalar as a verbatim slice of the input\n // (single-line plain / quoted with no escapes or folded breaks), so the\n // per-style char loop below would just reproduce the slice.\n if (scalar.fast) return input.slice(valueStart, valueEnd)\n\n switch (scalar.style) {\n case SCALAR_STYLE.SINGLE_QUOTED:\n return getSingleQuotedValue(input, valueStart, valueEnd)\n case SCALAR_STYLE.DOUBLE_QUOTED:\n return getDoubleQuotedValue(input, valueStart, valueEnd)\n case SCALAR_STYLE.LITERAL_BLOCK:\n return getBlockValue(input, valueStart, valueEnd, scalar.indent, scalar.chomping, false)\n case SCALAR_STYLE.FOLDED_BLOCK:\n return getBlockValue(input, valueStart, valueEnd, scalar.indent, scalar.chomping, true)\n default:\n return getPlainValue(input, valueStart, valueEnd)\n }\n}\n\nexport {\n getScalarValue\n}\n","const DEFAULT_TAG_HANDLERS: Readonly<Record<string, string>> = Object.assign(\n Object.create(null),\n {\n '!': '!',\n '!!': 'tag:yaml.org,2002:'\n }\n)\n\nfunction tagPercentEncode (source: string) {\n return encodeURI(source).replace(/!/g, '%21')\n}\n\nfunction tagNameFull (rawTag: string, tagHandlers?: Readonly<Record<string, string>>) {\n if (rawTag.startsWith('!<') && rawTag.endsWith('>')) {\n return decodeURIComponent(rawTag.slice(2, -1))\n }\n\n const handleEnd = rawTag.indexOf('!', 1)\n const handle = handleEnd === -1 ? '!' : rawTag.slice(0, handleEnd + 1)\n const prefix = tagHandlers?.[handle] ?? DEFAULT_TAG_HANDLERS[handle] ?? handle\n\n return decodeURIComponent(prefix) + decodeURIComponent(rawTag.slice(handle.length))\n}\n\nfunction tagNameShort (fullTag: string) {\n let tag = fullTag\n\n if (tag.charCodeAt(0) === 0x21) {\n tag = tag.slice(1)\n return `!${tagPercentEncode(tag)}`\n }\n\n if (tag.slice(0, 18) === 'tag:yaml.org,2002:') {\n return `!!${tagPercentEncode(tag.slice(18))}`\n }\n\n return `!<${tagPercentEncode(tag)}>`\n}\n\nexport {\n tagNameFull,\n tagNameShort\n}\n","import {\n EVENT_ID,\n SCALAR_STYLE,\n type Event,\n type TagHandlers,\n type MappingEvent,\n type ScalarEvent,\n type SequenceEvent\n} from './events.ts'\nimport { getScalarValue } from './parser_scalar.ts'\nimport { CORE_SCHEMA, type Schema } from '../schema.ts'\nimport {\n NOT_RESOLVED,\n type MappingTagDefinition,\n type ScalarTagDefinition,\n type SequenceTagDefinition\n} from '../tag.ts'\nimport { YAMLException } from '../common/exception.ts'\nimport { tagNameFull } from '../common/tagname.ts'\n\nconst NO_RANGE = -1\n\nconst MERGE_TAG_NAME = 'tag:yaml.org,2002:merge'\n\ninterface DocumentFrame {\n kind: 'document'\n position: number\n value: unknown\n hasValue: boolean\n}\n\ninterface SequenceFrame {\n kind: 'sequence'\n position: number\n value: any\n tag: SequenceTagDefinition<any, any>\n anchor: Anchor | null\n index: number\n}\n\ninterface MappingFrame {\n kind: 'mapping'\n position: number\n value: any\n tag: MappingTagDefinition<any, any>\n anchor: Anchor | null\n key: unknown\n keyPosition: number\n hasKey: boolean\n // The key slot drops its tag, but `<<` is recognized by tag, not by value.\n keyIsMerge: boolean\n // Keys brought in by a merge that an explicit pair is still allowed to\n // override. Lazily allocated: stays null for mappings without `<<`.\n overridable: Set<unknown> | null\n}\n\ntype Frame = DocumentFrame | SequenceFrame | MappingFrame\n\ntype AnyTag = ScalarTagDefinition | SequenceTagDefinition<any, any> | MappingTagDefinition<any, any>\n\ninterface ValueAndTag {\n value: unknown\n tag: AnyTag\n}\n\ninterface Anchor {\n value: unknown\n tag: AnyTag\n isValueFinal: boolean\n}\n\n/** @category Events */\ninterface ConstructorOptions {\n /** Source text referenced by offsets in `events`. */\n source: string\n filename?: string\n\n /**\n * Schema to use.\n *\n * @defaultValue {@link CORE_SCHEMA}\n */\n schema?: Schema\n\n /**\n * Enables compatibility with `JSON.parse` behavior. Duplicate keys in a\n * mapping override values instead of throwing an error.\n *\n * @defaultValue `false`\n */\n json?: boolean\n\n /**\n * Maximum total number of keys processed by merge (`<<`) across one load\n * call. Each member of a merge sequence also counts as one key. Set to `-1`\n * to disable the limit.\n *\n * @defaultValue `10000`\n */\n maxTotalMergeKeys?: number\n\n /**\n * Maximum number of alias nodes (`*ref`) per document. Set to `0` to reject\n * all aliases, or to `-1` for no limit.\n *\n * @defaultValue `-1`\n */\n maxAliases?: number\n}\n\n// `source` is input data, not config — so it has no default here.\nconst DEFAULT_CONSTRUCTOR_OPTIONS: Required<Omit<ConstructorOptions, 'source'>> = {\n filename: '',\n schema: CORE_SCHEMA,\n json: false,\n maxTotalMergeKeys: 10000,\n maxAliases: -1\n}\n\ninterface ConstructorState extends Required<ConstructorOptions> {\n events: Event[]\n documents: unknown[]\n eventIndex: number\n position: number\n frames: Frame[]\n anchors: Map<string, Anchor>\n // Mapping tag each sequence element was built with, keyed by the element\n // itself. Needed by `<<` merge, which sees only the finished element values.\n nodeTags: Map<unknown, MappingTagDefinition<any, any>>\n tagHandlers: TagHandlers\n totalMergeKeys: number\n aliasCount: number\n}\n\nfunction eventPosition (event: Event) {\n if ('tagStart' in event && event.tagStart !== NO_RANGE) return event.tagStart\n if ('anchorStart' in event && event.anchorStart !== NO_RANGE) return event.anchorStart\n if ('valueStart' in event && event.valueStart !== NO_RANGE) return event.valueStart\n if ('start' in event) return event.start\n return 0\n}\n\nfunction throwError (state: ConstructorState, message: string): never {\n YAMLException.throwAt(state.source, state.position, message, state.filename)\n}\n\nfunction finalizeCollection (\n state: ConstructorState,\n position: number,\n tag: SequenceTagDefinition<any, any> | MappingTagDefinition<any, any>,\n carrier: unknown\n) {\n try {\n return tag.finalize(carrier)\n } catch (error) {\n if (error instanceof YAMLException) throw error\n YAMLException.throwAt(\n state.source,\n position,\n error instanceof Error ? error.message : String(error),\n state.filename\n )\n }\n}\n\nfunction constructScalar (\n state: ConstructorState,\n event: ScalarEvent\n): ValueAndTag {\n const source = getScalarValue(state.source, event)\n const rawTag = event.tagStart === NO_RANGE\n ? ''\n : state.source.slice(event.tagStart, event.tagEnd)\n const strTag = state.schema.defaultScalarTag\n\n if (rawTag !== '') {\n if (rawTag === '!') return { value: source, tag: strTag }\n\n const tagName = tagNameFull(rawTag, state.tagHandlers)\n const scalarTag = state.schema.lookupScalarTag(tagName)\n\n if (scalarTag) {\n const result = scalarTag.resolve(source, true, tagName)\n\n if (result === NOT_RESOLVED) {\n throwError(state, `cannot resolve a node with !<${tagName}> explicit tag`)\n }\n\n return { value: result, tag: scalarTag }\n }\n\n // An empty node carrying a collection tag (e.g. `!!map`, `!!seq`) is emitted\n // by the parser as a scalar event, since there is no collection syntax to key\n // off. Resolve it here by the explicit tag's kind into an empty collection.\n const collectionTagDef =\n state.schema.lookupMappingTag(tagName) ??\n state.schema.lookupSequenceTag(tagName)\n\n if (collectionTagDef) {\n if (source !== '') {\n throwError(state, `cannot resolve a node with !<${tagName}> explicit tag`)\n }\n\n const carrier = collectionTagDef.create(tagName)\n const value = collectionTagDef.carrierIsResult\n ? carrier\n : finalizeCollection(state, state.position, collectionTagDef, carrier)\n return { value, tag: collectionTagDef }\n }\n\n throwError(state, `unknown scalar tag !<${tagName}>`)\n }\n\n if (event.style === SCALAR_STYLE.PLAIN) {\n return state.schema.resolveImplicitScalarTag(source)\n }\n\n return { value: strTag.resolve(source, false, strTag.tagName), tag: strTag }\n}\n\nfunction collectionTagName (\n state: ConstructorState,\n event: SequenceEvent | MappingEvent,\n defaultTagName: string\n) {\n const rawTag = event.tagStart === NO_RANGE\n ? ''\n : state.source.slice(event.tagStart, event.tagEnd)\n const tagName = rawTag === '' || rawTag === '!'\n ? defaultTagName\n : tagNameFull(rawTag, state.tagHandlers)\n\n return tagName\n}\n\n// A merge source must be a mapping; every mapping tag exposes the read side.\nfunction isMappingTag (tag: AnyTag): tag is MappingTagDefinition<any, any> {\n return tag.nodeKind === 'mapping'\n}\n\nfunction chargeMergeWork (state: ConstructorState) {\n state.totalMergeKeys++\n\n if (state.maxTotalMergeKeys !== -1 && state.totalMergeKeys > state.maxTotalMergeKeys) {\n throwError(state, `merge keys exceeded maxTotalMergeKeys (${state.maxTotalMergeKeys})`)\n }\n}\n\n// Fold the keys of one mapping source into the target frame, honoring merge\n// precedence: an already-present key (explicit or from an earlier source) wins.\nfunction mergeKeys (state: ConstructorState, frame: MappingFrame, source: unknown, sourceTag: MappingTagDefinition<any, any>) {\n // Count the source mapping itself to bound sequences of empty mappings.\n chargeMergeWork(state)\n\n for (const sourceKey of sourceTag.keys(source)) {\n chargeMergeWork(state)\n\n if (frame.tag.has(frame.value, sourceKey)) continue\n\n const err = frame.tag.addPair(frame.value, sourceKey, sourceTag.get(source, sourceKey))\n if (err) throwError(state, err)\n\n frame.overridable ??= new Set()\n frame.overridable.add(sourceKey)\n }\n}\n\n// The value of a `<<` key: either a mapping (fold its keys) or a sequence of\n// mappings (fold each). Sequence elements arrive as bare values, so the tag each\n// was built with comes from `nodeTags`; a miss means it is not a mapping (a\n// scalar, a nested sequence, or a value some sequence tag synthesized itself).\nfunction mergeSource (state: ConstructorState, frame: MappingFrame, source: unknown, sourceTag: AnyTag) {\n state.position = frame.keyPosition\n\n if (isMappingTag(sourceTag)) {\n mergeKeys(state, frame, source, sourceTag)\n } else if (sourceTag.nodeKind === 'sequence' && Array.isArray(source)) {\n // The current merge budget is sufficient; this hard cap only further limits\n // the attack vector, so there is no reason to expose it as a public option.\n if (source.length > 100) {\n throwError(state, 'abnormal merge sequence size')\n }\n\n for (const element of source) {\n const elementTag = state.nodeTags.get(element)\n if (!elementTag) {\n throwError(state, 'cannot merge mappings; the provided source object is unacceptable')\n }\n mergeKeys(state, frame, element, elementTag)\n }\n } else {\n throwError(state, 'cannot merge mappings; the provided source object is unacceptable')\n }\n}\n\nfunction addMappingValue (state: ConstructorState, frame: MappingFrame, key: unknown, value: unknown, tag: AnyTag) {\n state.position = frame.keyPosition\n\n // `<<` is intercepted before dedup, so a repeated merge key is allowed.\n if (frame.keyIsMerge) {\n mergeSource(state, frame, value, tag)\n return\n }\n\n if (!state.json && frame.tag.has(frame.value, key) && !frame.overridable?.has(key)) {\n throwError(state, 'duplicated mapping key')\n }\n\n const err = frame.tag.addPair(frame.value, key, value)\n if (err) throwError(state, err)\n frame.overridable?.delete(key)\n}\n\nfunction addValue (state: ConstructorState, value: unknown, tag: AnyTag) {\n const frame = state.frames[state.frames.length - 1]!\n\n if (frame.kind === 'document') {\n frame.value = value\n frame.hasValue = true\n } else if (frame.kind === 'sequence') {\n // Any element may later be folded in by a `<<` merge, which by then has no\n // way to tell what built it.\n if (isMappingTag(tag)) state.nodeTags.set(value, tag)\n const err = frame.tag.addItem(frame.value, value, frame.index++)\n if (err) throwError(state, err)\n } else if (frame.hasKey) {\n const key = frame.key\n frame.key = undefined\n frame.hasKey = false\n addMappingValue(state, frame, key, value, tag)\n } else {\n frame.key = value\n frame.keyPosition = state.position\n frame.hasKey = true\n frame.keyIsMerge = tag.tagName === MERGE_TAG_NAME\n }\n}\n\nfunction storeAnchor (\n state: ConstructorState,\n event: ScalarEvent | SequenceEvent | MappingEvent,\n value: unknown,\n tag: AnyTag,\n isValueFinal: boolean\n): Anchor | null {\n if (event.anchorStart !== NO_RANGE) {\n const anchor = {\n value,\n tag,\n isValueFinal\n }\n state.anchors.set(state.source.slice(event.anchorStart, event.anchorEnd), anchor)\n return anchor\n }\n\n return null\n}\n\n/**\n * Constructs JavaScript documents directly from parser events, without an\n * intermediate AST.\n *\n * @category Events\n */\nfunction constructFromEvents (events: Event[], options: ConstructorOptions): unknown[] {\n const state: ConstructorState = {\n ...DEFAULT_CONSTRUCTOR_OPTIONS,\n ...options,\n events,\n documents: [],\n eventIndex: 0,\n position: 0,\n frames: [],\n anchors: new Map(),\n nodeTags: new Map(),\n tagHandlers: Object.create(null),\n totalMergeKeys: 0,\n aliasCount: 0\n }\n\n while (state.eventIndex < state.events.length) {\n const event = state.events[state.eventIndex++]\n state.position = eventPosition(event)\n\n switch (event.type) {\n case EVENT_ID.DOCUMENT:\n state.anchors = new Map()\n state.nodeTags = new Map()\n state.aliasCount = 0\n state.tagHandlers = Object.create(null)\n for (const directive of event.directives) {\n if (directive.kind === 'tag') state.tagHandlers[directive.handle] = directive.prefix\n }\n state.frames.push({ kind: 'document', position: state.position, value: undefined, hasValue: false })\n break\n\n case EVENT_ID.SCALAR: {\n const { value, tag } = constructScalar(state, event)\n storeAnchor(state, event, value, tag, true)\n addValue(state, value, tag)\n break\n }\n\n case EVENT_ID.SEQUENCE: {\n const tagName = collectionTagName(state, event, 'tag:yaml.org,2002:seq')\n const tag = state.schema.lookupSequenceTag(tagName)\n if (!tag) throwError(state, `unknown sequence tag !<${tagName}>`)\n\n const value = tag.create(tagName)\n const anchor = storeAnchor(state, event, value, tag, tag.carrierIsResult)\n\n state.frames.push({\n kind: 'sequence', position: state.position, value, tag, anchor, index: 0\n })\n break\n }\n\n case EVENT_ID.MAPPING: {\n const tagName = collectionTagName(state, event, 'tag:yaml.org,2002:map')\n const tag = state.schema.lookupMappingTag(tagName)\n if (!tag) throwError(state, `unknown mapping tag !<${tagName}>`)\n\n const value = tag.create(tagName)\n const anchor = storeAnchor(state, event, value, tag, tag.carrierIsResult)\n state.frames.push({\n kind: 'mapping',\n position: state.position,\n value,\n tag,\n anchor,\n key: undefined,\n keyPosition: state.position,\n hasKey: false,\n keyIsMerge: false,\n overridable: null\n })\n break\n }\n\n case EVENT_ID.ALIAS: {\n if (state.maxAliases !== -1 && ++state.aliasCount > state.maxAliases) {\n throwError(state, `aliases exceeded maxAliases (${state.maxAliases})`)\n }\n\n const name = state.source.slice(event.anchorStart, event.anchorEnd)\n const anchor = state.anchors.get(name)\n if (!anchor) {\n throwError(state, `unidentified alias \"${name}\"`)\n }\n if (!anchor.isValueFinal) {\n throwError(state, `recursive alias \"${name}\" is not supported for tag ${anchor.tag.tagName} because it uses finalize()`)\n }\n addValue(state, anchor.value, anchor.tag)\n break\n }\n\n case EVENT_ID.POP: {\n const frame = state.frames.pop()!\n\n if (frame.kind === 'mapping' && frame.hasKey) {\n state.position = frame.keyPosition\n throwError(state, 'incomplete mapping pair in event stream')\n }\n\n if (frame.kind === 'document') {\n state.documents.push(frame.value)\n } else {\n const value = frame.tag.carrierIsResult\n ? frame.value\n : finalizeCollection(state, frame.position, frame.tag, frame.value)\n if (frame.anchor) {\n frame.anchor.value = value\n frame.anchor.isValueFinal = true\n }\n addValue(state, value, frame.tag)\n }\n break\n }\n }\n }\n\n return state.documents\n}\n\nexport {\n constructFromEvents,\n DEFAULT_CONSTRUCTOR_OPTIONS,\n type ConstructorOptions\n}\n","import {\n EVENT_ID,\n SCALAR_STYLE,\n COLLECTION_STYLE,\n CHOMPING_MODE,\n type Event,\n type ScalarStyle,\n type CollectionStyle,\n type ChompingMode,\n type DocumentDirective,\n type TagHandlers\n} from './events.ts'\nimport { YAMLException } from '../common/exception.ts'\n\nconst NO_RANGE = -1\nconst HAS_OWN = Object.prototype.hasOwnProperty\n\nconst CONTEXT_FLOW_IN = 1\nconst CONTEXT_FLOW_OUT = 2\nconst CONTEXT_BLOCK_IN = 3\nconst CONTEXT_BLOCK_OUT = 4\n\n// eslint-disable-next-line no-control-regex\nconst PATTERN_NON_PRINTABLE = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F-\\x84\\x86-\\x9F\\uFFFE\\uFFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]/\n// eslint-disable-next-line no-useless-escape\nconst PATTERN_FLOW_INDICATORS = /[,\\[\\]{}]/\n// YAML 1.2.2, [91] c-tag-handle.\n// eslint-disable-next-line no-useless-escape\nconst PATTERN_TAG_HANDLE = /^(?:!|!!|![0-9A-Za-z-]+!)$/\n// YAML 1.2.2, [39] ns-uri-char.\n// eslint-disable-next-line no-useless-escape\nconst NS_URI_CHAR = String.raw`(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]])`\n// YAML 1.2.2, [40] ns-tag-char = ns-uri-char - \"!\" - c-flow-indicator.\n// eslint-disable-next-line no-useless-escape\nconst NS_TAG_CHAR = String.raw`(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\\-#;/?:@&=+$.~*'()_])`\nconst PATTERN_TAG_URI = new RegExp(`^(?:${NS_URI_CHAR})*$`)\n// YAML 1.2.2, [99] c-ns-shorthand-tag suffix part.\nconst PATTERN_TAG_SUFFIX = new RegExp(`^(?:${NS_TAG_CHAR})+$`)\n// YAML 1.2.2, [93] ns-tag-prefix.\nconst PATTERN_TAG_PREFIX = new RegExp(`^(?:!(?:${NS_URI_CHAR})*|${NS_TAG_CHAR}(?:${NS_URI_CHAR})*)$`)\n\ntype NodeContext =\n typeof CONTEXT_FLOW_IN | typeof CONTEXT_FLOW_OUT |\n typeof CONTEXT_BLOCK_IN | typeof CONTEXT_BLOCK_OUT\n\ninterface NodeProperties {\n anchorStart: number\n anchorEnd: number\n tagStart: number\n tagEnd: number\n}\n\ninterface ParserSnapshot {\n position: number\n line: number\n lineStart: number\n lineIndent: number\n firstTabInLine: number\n eventsLength: number\n}\n\n/** @category Events */\ninterface ParserOptions {\n /**\n * File path used in error messages.\n *\n * @defaultValue `null`\n */\n filename?: string\n\n /**\n * Maximum nesting depth for collections. Aliases are not taken into account.\n *\n * @defaultValue `100`\n */\n maxDepth?: number\n}\n\nconst DEFAULT_PARSER_OPTIONS: Required<ParserOptions> = {\n filename: '',\n maxDepth: 100\n}\n\ninterface ParserState extends Required<ParserOptions> {\n input: string\n length: number\n position: number\n line: number\n lineStart: number\n lineIndent: number\n firstTabInLine: number\n depth: number\n directives: DocumentDirective[]\n tagHandlers: TagHandlers\n events: Event[]\n}\n\nfunction addDocumentEvent (\n state: ParserState,\n explicitStart: boolean,\n explicitEnd: boolean\n) {\n state.events.push({\n type: EVENT_ID.DOCUMENT,\n explicitStart,\n explicitEnd,\n directives: state.directives\n })\n}\n\nfunction addSequenceEvent (\n state: ParserState,\n start: number,\n anchorStart: number,\n anchorEnd: number,\n tagStart: number,\n tagEnd: number,\n style: CollectionStyle\n) {\n state.events.push({\n type: EVENT_ID.SEQUENCE,\n start,\n anchorStart,\n anchorEnd,\n tagStart,\n tagEnd,\n style\n })\n}\n\nfunction addMappingEvent (\n state: ParserState,\n start: number,\n anchorStart: number,\n anchorEnd: number,\n tagStart: number,\n tagEnd: number,\n style: CollectionStyle\n) {\n state.events.push({\n type: EVENT_ID.MAPPING,\n start,\n anchorStart,\n anchorEnd,\n tagStart,\n tagEnd,\n style\n })\n}\n\nfunction insertFlowPairMappingEvent (state: ParserState, snapshot: ParserSnapshot) {\n state.events.splice(snapshot.eventsLength, 0, {\n type: EVENT_ID.MAPPING,\n start: snapshot.position,\n anchorStart: NO_RANGE,\n anchorEnd: NO_RANGE,\n tagStart: NO_RANGE,\n tagEnd: NO_RANGE,\n style: COLLECTION_STYLE.FLOW\n })\n}\n\nfunction addScalarEvent (\n state: ParserState,\n valueStart: number,\n valueEnd: number,\n anchorStart: number,\n anchorEnd: number,\n tagStart: number,\n tagEnd: number,\n style: ScalarStyle,\n chomping: ChompingMode = CHOMPING_MODE.CLIP,\n indent = -1,\n fast = false\n) {\n state.events.push({\n type: EVENT_ID.SCALAR,\n valueStart,\n valueEnd,\n anchorStart,\n anchorEnd,\n tagStart,\n tagEnd,\n style,\n chomping,\n indent,\n fast\n })\n}\n\nfunction addAliasEvent (\n state: ParserState,\n anchorStart: number,\n anchorEnd: number\n) {\n state.events.push({\n type: EVENT_ID.ALIAS,\n anchorStart,\n anchorEnd\n })\n}\n\nfunction addPopEvent (state: ParserState) {\n state.events.push({ type: EVENT_ID.POP })\n}\n\nfunction addEmptyScalarEvent (state: ParserState) {\n addScalarEvent(\n state,\n NO_RANGE,\n NO_RANGE,\n NO_RANGE,\n NO_RANGE,\n NO_RANGE,\n NO_RANGE,\n SCALAR_STYLE.PLAIN\n )\n}\n\nfunction emptyProperties (): NodeProperties {\n return {\n anchorStart: NO_RANGE,\n anchorEnd: NO_RANGE,\n tagStart: NO_RANGE,\n tagEnd: NO_RANGE\n }\n}\n\nfunction snapshotState (state: ParserState): ParserSnapshot {\n return {\n position: state.position,\n line: state.line,\n lineStart: state.lineStart,\n lineIndent: state.lineIndent,\n firstTabInLine: state.firstTabInLine,\n eventsLength: state.events.length\n }\n}\n\nfunction restoreState (state: ParserState, snapshot: ParserSnapshot) {\n state.position = snapshot.position\n state.line = snapshot.line\n state.lineStart = snapshot.lineStart\n state.lineIndent = snapshot.lineIndent\n state.firstTabInLine = snapshot.firstTabInLine\n state.events.length = snapshot.eventsLength\n}\n\nfunction throwError (state: ParserState, message: string): never {\n YAMLException.throwAt(state.input.slice(0, state.length), state.position, message, state.filename)\n}\n\nfunction isEol (c: number) {\n return c === 0x0A/* LF */ || c === 0x0D/* CR */\n}\n\nfunction isWhiteSpace (c: number) {\n return c === 0x09/* Tab */ || c === 0x20/* Space */\n}\n\nfunction isWsOrEol (c: number) {\n return isWhiteSpace(c) || isEol(c)\n}\n\nfunction isWsOrEolOrEnd (c: number) {\n return c === 0 || isWsOrEol(c)\n}\n\nfunction isFlowIndicator (c: number) {\n return c === 0x2C/* , */ ||\n c === 0x5B/* [ */ ||\n c === 0x5D/* ] */ ||\n c === 0x7B/* { */ ||\n c === 0x7D/* } */\n}\n\nfunction fromDecimalCode (c: number) {\n return c >= 0x30/* 0 */ && c <= 0x39/* 9 */ ? c - 0x30 : -1\n}\n\nfunction fromHexCode (c: number) {\n if (c >= 0x30/* 0 */ && c <= 0x39/* 9 */) return c - 0x30\n const lc = c | 0x20\n if (lc >= 0x61/* a */ && lc <= 0x66/* f */) return lc - 0x61 + 10\n return -1\n}\n\nfunction escapedHexLen (c: number) {\n if (c === 0x78/* x */) return 2\n if (c === 0x75/* u */) return 4\n if (c === 0x55/* U */) return 8\n return 0\n}\n\nfunction isSimpleEscape (c: number) {\n return c === 0x30/* 0 */ ||\n c === 0x61/* a */ ||\n c === 0x62/* b */ ||\n c === 0x74/* t */ ||\n c === 0x09/* Tab */ ||\n c === 0x6E/* n */ ||\n c === 0x76/* v */ ||\n c === 0x66/* f */ ||\n c === 0x72/* r */ ||\n c === 0x65/* e */ ||\n c === 0x20/* Space */ ||\n c === 0x22/* \" */ ||\n c === 0x2F/* / */ ||\n c === 0x5C/* \\ */ ||\n c === 0x4E/* N */ ||\n c === 0x5F/* _ */ ||\n c === 0x4C/* L */ ||\n c === 0x50/* P */\n}\n\n// Precondition: state.position points at LF or CR.\nfunction consumeLineBreak (state: ParserState) {\n const ch = state.input.charCodeAt(state.position)\n\n if (ch === 0x0A/* LF */) {\n state.position++\n } else {\n state.position++\n if (state.input.charCodeAt(state.position) === 0x0A/* LF */) state.position++\n }\n\n state.line++\n state.lineStart = state.position\n state.lineIndent = 0\n state.firstTabInLine = -1\n}\n\nfunction skipSeparationSpace (state: ParserState, allowComments: boolean) {\n let lineBreaks = 0\n let ch = state.input.charCodeAt(state.position)\n let hasSeparation = state.position === state.lineStart ||\n isWsOrEol(state.input.charCodeAt(state.position - 1))\n\n while (ch !== 0) {\n while (isWhiteSpace(ch)) {\n hasSeparation = true\n if (ch === 0x09/* Tab */ && state.firstTabInLine === -1) {\n state.firstTabInLine = state.position\n }\n ch = state.input.charCodeAt(++state.position)\n }\n\n if (allowComments && hasSeparation && ch === 0x23/* # */) {\n do { ch = state.input.charCodeAt(++state.position) }\n while (!isEol(ch) && ch !== 0)\n }\n\n if (!isEol(ch)) break\n\n consumeLineBreak(state)\n lineBreaks++\n hasSeparation = true\n ch = state.input.charCodeAt(state.position)\n\n while (ch === 0x20/* Space */) {\n state.lineIndent++\n ch = state.input.charCodeAt(++state.position)\n }\n }\n\n return lineBreaks\n}\n\nfunction testDocumentSeparator (state: ParserState, position = state.position) {\n const ch = state.input.charCodeAt(position)\n\n if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) &&\n ch === state.input.charCodeAt(position + 1) &&\n ch === state.input.charCodeAt(position + 2)) {\n const following = state.input.charCodeAt(position + 3)\n return following === 0 || isWsOrEol(following)\n }\n\n return false\n}\n\nfunction skipByteOrderMark (state: ParserState) {\n if (state.position === state.lineStart &&\n state.input.charCodeAt(state.position) === 0xFEFF) {\n state.position++\n state.lineStart = state.position\n }\n}\n\nfunction testDocumentBoundary (state: ParserState) {\n if (state.position !== state.lineStart) return false\n\n // Fast path: most document boundaries do not have a BOM.\n if (testDocumentSeparator(state)) return true\n if (state.input.charCodeAt(state.position) !== 0xFEFF) return false\n\n // BOM-prefixed boundaries are rare, so snapshot/restore overhead is negligible.\n const snapshot = snapshotState(state)\n\n skipByteOrderMark(state)\n skipSeparationSpace(state, true)\n\n const ch = state.input.charCodeAt(state.position)\n const result = state.position === state.lineStart &&\n (ch === 0x25/* % */ || (ch === 0x2D/* - */ && testDocumentSeparator(state)))\n\n restoreState(state, snapshot)\n return result\n}\n\nfunction skipUntilLineEnd (state: ParserState) {\n let ch = state.input.charCodeAt(state.position)\n\n while (ch !== 0 && !isEol(ch)) {\n ch = state.input.charCodeAt(++state.position)\n }\n}\n\nfunction checkPrintable (state: ParserState, start: number, end: number) {\n if (PATTERN_NON_PRINTABLE.test(state.input.slice(start, end))) {\n throwError(state, 'the stream contains non-printable characters')\n }\n}\n\nfunction readTagProperty (state: ParserState, props: NodeProperties, inFlow: boolean) {\n if (state.input.charCodeAt(state.position) !== 0x21/* ! */) return false\n if (props.tagStart !== NO_RANGE) throwError(state, 'duplication of a tag property')\n\n const start = state.position\n let isVerbatim = false\n let isNamed = false\n let tagHandle = '!'\n let ch = state.input.charCodeAt(++state.position)\n\n if (ch === 0x3C/* < */) {\n isVerbatim = true\n ch = state.input.charCodeAt(++state.position)\n } else if (ch === 0x21/* ! */) {\n isNamed = true\n tagHandle = '!!'\n ch = state.input.charCodeAt(++state.position)\n }\n\n let suffixStart = state.position\n let tagName\n\n if (isVerbatim) {\n while (ch !== 0 && ch !== 0x3E/* > */) ch = state.input.charCodeAt(++state.position)\n if (ch !== 0x3E/* > */) throwError(state, 'unexpected end of the stream within a verbatim tag')\n tagName = state.input.slice(suffixStart, state.position)\n state.position++\n } else {\n while (ch !== 0 && !isWsOrEol(ch) && !(inFlow && isFlowIndicator(ch))) {\n if (ch === 0x21/* ! */) {\n if (!isNamed) {\n tagHandle = state.input.slice(suffixStart - 1, state.position + 1)\n if (!PATTERN_TAG_HANDLE.test(tagHandle)) throwError(state, 'named tag handle cannot contain such characters')\n isNamed = true\n suffixStart = state.position + 1\n } else {\n throwError(state, 'tag suffix cannot contain exclamation marks')\n }\n }\n\n ch = state.input.charCodeAt(++state.position)\n }\n\n tagName = state.input.slice(suffixStart, state.position)\n if (PATTERN_FLOW_INDICATORS.test(tagName)) throwError(state, 'tag suffix cannot contain flow indicator characters')\n }\n\n if (tagName && !(isVerbatim ? PATTERN_TAG_URI.test(tagName) : PATTERN_TAG_SUFFIX.test(tagName))) {\n throwError(state, `tag name cannot contain such characters: ${tagName}`)\n }\n try {\n decodeURIComponent(tagName)\n } catch {\n throwError(state, `tag name is malformed: ${tagName}`)\n }\n\n if (!isVerbatim && tagHandle !== '!' && tagHandle !== '!!' && !HAS_OWN.call(state.tagHandlers, tagHandle)) {\n throwError(state, `undeclared tag handle \"${tagHandle}\"`)\n }\n\n props.tagStart = start\n props.tagEnd = state.position\n return true\n}\n\nfunction readAnchorProperty (state: ParserState, props: NodeProperties) {\n if (state.input.charCodeAt(state.position) !== 0x26/* & */) return false\n if (props.anchorStart !== NO_RANGE) throwError(state, 'duplication of an anchor property')\n\n state.position++\n const start = state.position\n\n while (state.input.charCodeAt(state.position) !== 0 && !isWsOrEol(state.input.charCodeAt(state.position)) && !isFlowIndicator(state.input.charCodeAt(state.position))) {\n state.position++\n }\n\n if (state.position === start) throwError(state, 'name of an anchor node must contain at least one character')\n\n props.anchorStart = start\n props.anchorEnd = state.position\n return true\n}\n\nfunction readAlias (state: ParserState, props: NodeProperties) {\n if (state.input.charCodeAt(state.position) !== 0x2A/* * */) return false\n if (props.anchorStart !== NO_RANGE || props.tagStart !== NO_RANGE) {\n throwError(state, 'alias node should not have any properties')\n }\n\n state.position++\n const start = state.position\n\n while (state.input.charCodeAt(state.position) !== 0 && !isWsOrEol(state.input.charCodeAt(state.position)) && !isFlowIndicator(state.input.charCodeAt(state.position))) {\n state.position++\n }\n\n if (state.position === start) throwError(state, 'name of an alias node must contain at least one character')\n\n addAliasEvent(state, start, state.position)\n return true\n}\n\nfunction readFlowScalarBreak (state: ParserState, nodeIndent: number) {\n skipSeparationSpace(state, false)\n\n if (state.lineIndent < nodeIndent) {\n throwError(state, 'deficient indentation')\n }\n}\n\nfunction readSingleQuotedScalar (state: ParserState, nodeIndent: number, props: NodeProperties) {\n if (state.input.charCodeAt(state.position) !== 0x27/* ' */) return false\n\n state.position++\n const start = state.position\n // A single-quoted scalar is sliceable verbatim when it has no '' escape pairs\n // and no folded line breaks (see getScalarValue fast path).\n let simple = true\n\n while (state.input.charCodeAt(state.position) !== 0) {\n const ch = state.input.charCodeAt(state.position)\n\n if (ch === 0x27/* ' */) {\n if (state.input.charCodeAt(state.position + 1) === 0x27/* ' */) {\n simple = false\n state.position += 2\n continue\n }\n\n const end = state.position\n state.position++\n addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.SINGLE_QUOTED, CHOMPING_MODE.CLIP, -1, simple)\n return true\n }\n\n if (isEol(ch)) {\n simple = false\n readFlowScalarBreak(state, nodeIndent)\n } else if (state.position === state.lineStart && testDocumentSeparator(state)) {\n throwError(state, 'unexpected end of the document within a single quoted scalar')\n } else if (ch !== 0x09/* Tab */ && ch < 0x20) {\n throwError(state, 'expected valid JSON character')\n } else {\n state.position++\n }\n }\n\n throwError(state, 'unexpected end of the stream within a single quoted scalar')\n}\n\nfunction readDoubleQuotedScalar (state: ParserState, nodeIndent: number, props: NodeProperties) {\n if (state.input.charCodeAt(state.position) !== 0x22/* \" */) return false\n\n state.position++\n const start = state.position\n // A double-quoted scalar is sliceable verbatim when it has no \\ escapes and\n // no folded line breaks (see getScalarValue fast path).\n let simple = true\n\n while (state.input.charCodeAt(state.position) !== 0) {\n const ch = state.input.charCodeAt(state.position)\n\n if (ch === 0x22/* \" */) {\n const end = state.position\n state.position++\n addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.DOUBLE_QUOTED, CHOMPING_MODE.CLIP, -1, simple)\n return true\n }\n\n if (ch === 0x5C/* \\ */) {\n simple = false\n const escaped = state.input.charCodeAt(++state.position)\n\n if (isEol(escaped)) {\n readFlowScalarBreak(state, nodeIndent)\n } else if (isSimpleEscape(escaped)) {\n state.position++\n } else {\n let hexLength = escapedHexLen(escaped)\n\n if (hexLength === 0) throwError(state, 'unknown escape sequence')\n\n while (hexLength-- > 0) {\n state.position++\n if (fromHexCode(state.input.charCodeAt(state.position)) < 0) {\n throwError(state, 'expected hexadecimal character')\n }\n }\n state.position++\n }\n } else if (isEol(ch)) {\n simple = false\n readFlowScalarBreak(state, nodeIndent)\n } else if (state.position === state.lineStart && testDocumentSeparator(state)) {\n throwError(state, 'unexpected end of the document within a double quoted scalar')\n } else if (ch !== 0x09/* Tab */ && ch < 0x20) {\n throwError(state, 'expected valid JSON character')\n } else {\n state.position++\n }\n }\n\n throwError(state, 'unexpected end of the stream within a double quoted scalar')\n}\n\nfunction readBlockScalar (state: ParserState, parentIndent: number, props: NodeProperties) {\n const ch = state.input.charCodeAt(state.position)\n let chomping: ChompingMode = CHOMPING_MODE.CLIP\n let indent = -1\n let detectedIndent = false\n\n if (ch !== 0x7C/* | */ && ch !== 0x3E/* > */) return false\n\n const style = ch === 0x7C/* | */ ? SCALAR_STYLE.LITERAL_BLOCK : SCALAR_STYLE.FOLDED_BLOCK\n state.position++\n\n while (state.input.charCodeAt(state.position) !== 0) {\n const current = state.input.charCodeAt(state.position)\n const digit = fromDecimalCode(current)\n\n if (current === 0x2B/* + */ || current === 0x2D/* - */) {\n if (chomping !== CHOMPING_MODE.CLIP) throwError(state, 'repeat of a chomping mode identifier')\n chomping = current === 0x2B/* + */ ? CHOMPING_MODE.KEEP : CHOMPING_MODE.STRIP\n state.position++\n } else if (digit >= 0) {\n if (digit === 0) {\n throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one')\n }\n if (detectedIndent) throwError(state, 'repeat of an indentation width identifier')\n indent = parentIndent + digit - 1\n detectedIndent = true\n state.position++\n } else {\n break\n }\n }\n\n let hadWhitespace = false\n while (isWhiteSpace(state.input.charCodeAt(state.position))) {\n hadWhitespace = true\n state.position++\n }\n if (hadWhitespace && state.input.charCodeAt(state.position) === 0x23/* # */) skipUntilLineEnd(state)\n\n if (isEol(state.input.charCodeAt(state.position))) {\n consumeLineBreak(state)\n } else if (state.input.charCodeAt(state.position) !== 0) {\n throwError(state, 'a line break is expected')\n }\n\n let contentIndent = detectedIndent ? indent : -1\n let maxLeadingIndent = 0\n const valueStart = state.position\n let valueEnd = state.position\n\n while (state.input.charCodeAt(state.position) !== 0) {\n const linePosition = state.position\n let column = 0\n\n while (state.input.charCodeAt(linePosition + column) === 0x20/* Space */) column++\n\n const first = state.input.charCodeAt(linePosition + column)\n if (first === 0) {\n // End of input acts as a line terminator, but there is no line break to\n // include here. A final all-spaces line still counts: when the block has a\n // content indent, the spaces beyond it are real content; in a wholly blank\n // block (contentIndent < 0) the spaces form a blank line that chomping must\n // see, exactly as it would if the line ended with a break. Capture the line\n // in both cases; otherwise the block ends at the start of this empty line.\n if (contentIndent >= 0) {\n if (column > contentIndent) valueEnd = linePosition + column\n } else if (column > 0) {\n valueEnd = linePosition + column\n }\n break\n }\n if (testDocumentBoundary(state)) break\n\n if (!detectedIndent && contentIndent === -1 && isEol(first)) {\n maxLeadingIndent = Math.max(maxLeadingIndent, column)\n }\n\n if (!detectedIndent && contentIndent === -1 && !isEol(first)) {\n if (first === 0x09/* Tab */ && column < parentIndent) {\n state.position = linePosition + column\n throwError(state, 'tab characters must not be used in indentation')\n }\n if (column >= parentIndent && column < maxLeadingIndent) {\n state.position = linePosition + column\n throwError(state, 'bad indentation of a mapping entry')\n }\n }\n\n if (contentIndent === -1 && first !== 0 && !isEol(first) && column < parentIndent) {\n state.lineIndent = column\n state.position = linePosition + column\n break\n }\n\n if (!detectedIndent && first !== 0 && !isEol(first) && contentIndent === -1) {\n contentIndent = column\n }\n\n const requiredIndent = contentIndent === -1 ? parentIndent + 1 : contentIndent\n if (first !== 0 && !isEol(first) && column < requiredIndent) {\n state.lineIndent = column\n state.position = linePosition + column\n break\n }\n\n skipUntilLineEnd(state)\n valueEnd = state.position\n if (isEol(state.input.charCodeAt(state.position))) {\n consumeLineBreak(state)\n // Include the line break in the range so trailing blank lines are\n // preserved. This is what lets cook tell apart an empty `|+` (range \"\",\n // value \"\") from a `|+` with one blank line (range \"\\n\", value \"\\n\").\n // De-indent and chomping are applied later in getScalarValue.\n valueEnd = state.position\n }\n }\n\n checkPrintable(state, valueStart, valueEnd)\n addScalarEvent(\n state,\n valueStart,\n valueEnd,\n props.anchorStart,\n props.anchorEnd,\n props.tagStart,\n props.tagEnd,\n style,\n chomping,\n contentIndent\n )\n return true\n}\n\nfunction canStartPlainScalar (state: ParserState, nodeContext: NodeContext) {\n const ch = state.input.charCodeAt(state.position)\n const inFlow = nodeContext === CONTEXT_FLOW_IN\n\n if (ch === 0 ||\n isWsOrEol(ch) ||\n ch === 0x23/* # */ ||\n ch === 0x26/* & */ ||\n ch === 0x2A/* * */ ||\n ch === 0x21/* ! */ ||\n ch === 0x7C/* | */ ||\n ch === 0x3E/* > */ ||\n ch === 0x27/* ' */ ||\n ch === 0x22/* \" */ ||\n ch === 0x25/* % */ ||\n ch === 0x40/* @ */ ||\n ch === 0x60/* ` */ ||\n (inFlow && isFlowIndicator(ch))) {\n return false\n }\n\n if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) {\n const following = state.input.charCodeAt(state.position + 1)\n if (isWsOrEolOrEnd(following) || (inFlow && isFlowIndicator(following))) return false\n }\n\n return true\n}\n\nfunction readPlainScalar (state: ParserState, nodeIndent: number, nodeContext: NodeContext, props: NodeProperties) {\n if (!canStartPlainScalar(state, nodeContext)) return false\n\n const start = state.position\n let end = state.position\n let ch = state.input.charCodeAt(state.position)\n const inFlow = nodeContext === CONTEXT_FLOW_IN\n // A single-line plain scalar is sliceable verbatim: the parser already trims\n // trailing whitespace from the range, so no folding is needed (see\n // getScalarValue fast path). Folded line breaks make it non-simple.\n let multiline = false\n\n while (ch !== 0) {\n if (testDocumentBoundary(state)) break\n\n if (ch === 0x3A/* : */) {\n const following = state.input.charCodeAt(state.position + 1)\n if (isWsOrEolOrEnd(following) || (inFlow && isFlowIndicator(following))) break\n } else if (ch === 0x23/* # */) {\n const preceding = state.input.charCodeAt(state.position - 1)\n if (isWsOrEol(preceding)) break\n } else if (inFlow && isFlowIndicator(ch)) {\n break\n } else if (isEol(ch)) {\n const savedPosition = state.position\n const savedLine = state.line\n const savedLineStart = state.lineStart\n const savedLineIndent = state.lineIndent\n\n skipSeparationSpace(state, false)\n\n if (state.lineIndent >= nodeIndent) {\n multiline = true\n ch = state.input.charCodeAt(state.position)\n continue\n }\n\n state.position = savedPosition\n state.line = savedLine\n state.lineStart = savedLineStart\n state.lineIndent = savedLineIndent\n break\n }\n\n if (!isWhiteSpace(ch)) end = state.position + 1\n ch = state.input.charCodeAt(++state.position)\n }\n\n if (end === start) return false\n\n checkPrintable(state, start, end)\n addScalarEvent(state, start, end, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, SCALAR_STYLE.PLAIN, CHOMPING_MODE.CLIP, -1, !multiline)\n return true\n}\n\nfunction findBlockMappingColon (state: ParserState) {\n let position = state.position\n let flowLevel = 0\n\n while (position < state.length) {\n const ch = state.input.charCodeAt(position)\n\n if (isEol(ch)) return -1\n if (ch === 0x23/* # */ && isWsOrEol(state.input.charCodeAt(position - 1))) return -1\n\n if ((ch === 0x2A/* * */ || ch === 0x26/* & */) && position === state.position) {\n do { position++ }\n while (state.input.charCodeAt(position) !== 0 &&\n !isWsOrEol(state.input.charCodeAt(position)) &&\n !isFlowIndicator(state.input.charCodeAt(position)))\n continue\n }\n\n if (ch === 0x5B/* [ */ || ch === 0x7B/* { */) {\n flowLevel++\n } else if (ch === 0x5D/* ] */ || ch === 0x7D/* } */) {\n if (flowLevel > 0) flowLevel--\n } else if (flowLevel === 0 && ch === 0x3A/* : */ && isWsOrEol(state.input.charCodeAt(position + 1))) {\n return position\n }\n\n if ((flowLevel > 0 || position === state.position) &&\n (ch === 0x27/* ' */ || ch === 0x22/* \" */)) {\n const quote = ch\n position++\n\n while (position < state.length && state.input.charCodeAt(position) !== quote) {\n if (state.input.charCodeAt(position) === 0x5C/* \\ */ && quote === 0x22/* \" */) position++\n position++\n }\n }\n\n position++\n }\n\n return -1\n}\n\nfunction skipFlowSeparationSpace (state: ParserState, nodeIndent: number) {\n const startLine = state.line\n skipSeparationSpace(state, true)\n\n if ((state.line > startLine && state.lineIndent < nodeIndent) ||\n (state.firstTabInLine !== -1 && state.lineIndent < nodeIndent)) {\n throwError(state, 'deficient indentation')\n }\n}\n\nfunction readFlowCollection (state: ParserState, nodeIndent: number, props: NodeProperties) {\n const ch = state.input.charCodeAt(state.position)\n const isMapping = ch === 0x7B/* { */\n const start = state.position\n let readNext = true\n\n if (ch !== 0x5B/* [ */ && ch !== 0x7B/* { */) return false\n\n const terminator = isMapping ? 0x7D/* } */ : 0x5D/* ] */\n\n if (isMapping) {\n addMappingEvent(state, start, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.FLOW)\n } else {\n addSequenceEvent(state, start, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.FLOW)\n }\n\n state.position++\n\n while (state.input.charCodeAt(state.position) !== 0) {\n skipFlowSeparationSpace(state, nodeIndent)\n\n let ch = state.input.charCodeAt(state.position)\n\n if (ch === terminator) {\n state.position++\n addPopEvent(state)\n return true\n } else if (!readNext) {\n throwError(state, 'missed comma between flow collection entries')\n } else if (ch === 0x2C/* , */) {\n throwError(state, \"expected the node content, but found ','\")\n }\n\n let isPair = false\n let isExplicitPair = false\n\n if (ch === 0x3F/* ? */ && isWsOrEol(state.input.charCodeAt(state.position + 1))) {\n isPair = isExplicitPair = true\n state.position += 1\n skipFlowSeparationSpace(state, nodeIndent)\n }\n\n const entryLine = state.line\n const entryStart = snapshotState(state)\n\n const keyWasRead = parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)\n skipFlowSeparationSpace(state, nodeIndent)\n\n ch = state.input.charCodeAt(state.position)\n\n if ((isMapping || isExplicitPair || state.line === entryLine) && ch === 0x3A/* : */) {\n isPair = true\n state.position++\n skipFlowSeparationSpace(state, nodeIndent)\n if (!isMapping) {\n insertFlowPairMappingEvent(state, entryStart)\n if (!keyWasRead) addEmptyScalarEvent(state)\n } else if (!keyWasRead) {\n addEmptyScalarEvent(state)\n }\n if (!parseNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)) {\n addEmptyScalarEvent(state)\n }\n skipFlowSeparationSpace(state, nodeIndent)\n if (!isMapping) addPopEvent(state)\n } else if (isMapping && isPair) {\n if (!keyWasRead) addEmptyScalarEvent(state)\n addEmptyScalarEvent(state)\n } else if (isMapping) {\n addEmptyScalarEvent(state)\n } else if (isPair) {\n insertFlowPairMappingEvent(state, entryStart)\n if (!keyWasRead) addEmptyScalarEvent(state)\n addEmptyScalarEvent(state)\n addPopEvent(state)\n }\n\n ch = state.input.charCodeAt(state.position)\n\n if (ch === 0x2C/* , */) {\n readNext = true\n state.position++\n } else {\n readNext = false\n }\n }\n\n throwError(state, 'unexpected end of the stream within a flow collection')\n}\n\nfunction readBlockSequence (state: ParserState, nodeIndent: number, props: NodeProperties) {\n if (state.firstTabInLine !== -1 || state.input.charCodeAt(state.position) !== 0x2D/* - */ || !isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) {\n return false\n }\n\n addSequenceEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK)\n\n while (state.input.charCodeAt(state.position) === 0x2D/* - */ && isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) {\n if (state.firstTabInLine !== -1) {\n state.position = state.firstTabInLine\n throwError(state, 'tab characters must not be used in indentation')\n }\n\n const entryLine = state.line\n state.position++\n\n const hadBreak = skipSeparationSpace(state, true) > 0\n if (state.firstTabInLine !== -1 &&\n state.input.charCodeAt(state.position) === 0x2D/* - */ &&\n isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) {\n throwError(state, 'bad indentation of a sequence entry')\n }\n\n if (hadBreak && state.lineIndent <= nodeIndent) {\n addEmptyScalarEvent(state)\n } else {\n parseNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true)\n }\n\n skipSeparationSpace(state, true)\n\n if (state.lineIndent < nodeIndent || state.position >= state.length) break\n if (state.lineIndent > nodeIndent) throwError(state, 'bad indentation of a sequence entry')\n if (state.line === entryLine &&\n state.input.charCodeAt(state.position) === 0x2D/* - */ &&\n isWsOrEolOrEnd(state.input.charCodeAt(state.position + 1))) {\n throwError(state, 'bad indentation of a sequence entry')\n }\n }\n\n addPopEvent(state)\n return true\n}\n\nfunction readBlockMapping (state: ParserState, nodeIndent: number, flowIndent: number, props: NodeProperties) {\n let atExplicitKey = false\n let detected = false\n let mappingOpened = false\n let pendingExplicitKey = false\n\n if (state.firstTabInLine !== -1) return false\n\n let ch = state.input.charCodeAt(state.position)\n\n while (ch !== 0) {\n if (!atExplicitKey && state.firstTabInLine !== -1) {\n state.position = state.firstTabInLine\n throwError(state, 'tab characters must not be used in indentation')\n }\n\n const following = state.input.charCodeAt(state.position + 1)\n const entryLine = state.line\n\n if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && isWsOrEolOrEnd(following)) {\n if (!mappingOpened) {\n addMappingEvent(state, state.position, props.anchorStart, props.anchorEnd, props.tagStart, props.tagEnd, COLLECTION_STYLE.BLOCK)\n mappingOpened = true\n }\n\n if (ch === 0x3F/* ? */) {\n if (atExplicitKey) addEmptyScalarEvent(state)\n detected = true\n atExplicitKey = true\n } else if (atExplicitKey) {\n atExplicitKey = false\n } else {\n addEmptyScalarEvent(state)\n detected = true\n atExplicitKey = false\n }\n\n state.position += 1\n pendingExplicitKey = true\n } else {\n // An explicit key awaiting its value, followed by an implicit key, means\n // the explicit key's value is empty. Emit it now (append-only) so it is\n // ordered before the implicit key node read just below.\n if (atExplicitKey) {\n addEmptyScalarEvent(state)\n atExplicitKey = false\n }\n\n const beforeKey = snapshotState(state)\n\n if (!parseNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {\n break\n }\n\n if (state.line === entryLine) {\n ch = state.input.charCodeAt(state.position)\n\n while (isWhiteSpace(ch)) {\n ch = state.input.charCodeAt(++state.position)\n }\n\n if (ch === 0x3A/* : */) {\n ch = state.input.charCodeAt(++state.position)\n\n if (!isWsOrEolOrEnd(ch)) {\n throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping')\n }\n\n if (!mappingOpened) {\n // The key is already parsed; insert the MAPPING event before its\n // events instead of rolling back and re-reading the key.\n state.events.splice(beforeKey.eventsLength, 0, {\n type: EVENT_ID.MAPPING,\n start: beforeKey.position,\n anchorStart: props.anchorStart,\n anchorEnd: props.anchorEnd,\n tagStart: props.tagStart,\n tagEnd: props.tagEnd,\n style: COLLECTION_STYLE.BLOCK\n })\n mappingOpened = true\n }\n\n detected = true\n atExplicitKey = false\n pendingExplicitKey = false\n } else if (detected) {\n throwError(state, \"expected ':' after a mapping key\")\n } else {\n // Not a mapping. If outer properties are pending, roll back so the\n // caller re-reads this node with them attached (events are append-only).\n if (props.anchorStart !== NO_RANGE || props.tagStart !== NO_RANGE) {\n restoreState(state, beforeKey)\n return false\n }\n return true\n }\n } else if (detected) {\n throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key')\n } else {\n if (props.anchorStart !== NO_RANGE || props.tagStart !== NO_RANGE) {\n restoreState(state, beforeKey)\n return false\n }\n return true\n }\n }\n\n if (parseNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, pendingExplicitKey)) {\n pendingExplicitKey = false\n }\n\n if (!atExplicitKey) {\n if (pendingExplicitKey) {\n addEmptyScalarEvent(state)\n pendingExplicitKey = false\n }\n }\n\n skipSeparationSpace(state, true)\n ch = state.input.charCodeAt(state.position)\n\n if ((state.line === entryLine || state.lineIndent > nodeIndent) && ch !== 0) {\n throwError(state, 'bad indentation of a mapping entry')\n } else if (state.lineIndent < nodeIndent) {\n break\n }\n }\n\n if (!detected) return false\n if (atExplicitKey) addEmptyScalarEvent(state)\n if (mappingOpened) addPopEvent(state)\n return true\n}\n\nfunction parseNode (\n state: ParserState,\n parentIndent: number,\n nodeContext: NodeContext,\n allowToSeek: boolean,\n allowCompact: boolean,\n allowPropertyMapping = true\n): boolean {\n if (state.depth >= state.maxDepth) {\n throwError(state, `nesting exceeded maxDepth (${state.maxDepth})`)\n }\n\n state.depth++\n\n let indentStatus = 1\n let atNewLine = false\n let hasContent = false\n let propertyStart: ParserSnapshot | null = null\n const props = emptyProperties()\n\n let allowBlockScalars = nodeContext === CONTEXT_BLOCK_OUT || nodeContext === CONTEXT_BLOCK_IN\n let allowBlockCollections = allowBlockScalars\n const allowBlockStyles = allowBlockScalars\n\n if (allowToSeek && skipSeparationSpace(state, true)) {\n atNewLine = true\n\n if (state.lineIndent > parentIndent) {\n indentStatus = 1\n } else if (state.lineIndent === parentIndent) {\n indentStatus = 0\n } else {\n indentStatus = -1\n }\n }\n\n if (indentStatus === 1) {\n while (true) {\n const ch = state.input.charCodeAt(state.position)\n const propertyState = snapshotState(state)\n\n if (atNewLine &&\n indentStatus !== 1 &&\n (ch === 0x21/* ! */ || ch === 0x26/* & */)) {\n break\n }\n\n if (atNewLine &&\n allowBlockStyles &&\n (props.tagStart !== NO_RANGE || props.anchorStart !== NO_RANGE) &&\n (ch === 0x21/* ! */ || ch === 0x26/* & */)) {\n const fallbackState = snapshotState(state)\n const flowIndent = parentIndent + 1\n const mappingIndent = state.position - state.lineStart\n\n if (readBlockMapping(state, mappingIndent, flowIndent, props) &&\n state.events[fallbackState.eventsLength]?.type === EVENT_ID.MAPPING) {\n state.depth--\n return true\n }\n\n restoreState(state, fallbackState)\n }\n\n if (atNewLine &&\n ((ch === 0x21/* ! */ && props.tagStart !== NO_RANGE) ||\n (ch === 0x26/* & */ && props.anchorStart !== NO_RANGE))) {\n break\n }\n\n if (!readTagProperty(state, props, nodeContext === CONTEXT_FLOW_IN) && !readAnchorProperty(state, props)) {\n break\n }\n\n if (propertyStart === null) propertyStart = propertyState\n\n if (skipSeparationSpace(state, true)) {\n atNewLine = true\n allowBlockCollections = allowBlockStyles\n\n if (state.lineIndent > parentIndent) {\n indentStatus = 1\n } else if (state.lineIndent === parentIndent) {\n indentStatus = 0\n } else {\n indentStatus = -1\n }\n } else {\n allowBlockCollections = false\n }\n }\n }\n\n if (allowBlockCollections) {\n allowBlockCollections = atNewLine || allowCompact\n }\n\n if (indentStatus === 1 || nodeContext === CONTEXT_BLOCK_OUT) {\n const flowIndent = nodeContext === CONTEXT_FLOW_IN || nodeContext === CONTEXT_FLOW_OUT\n ? parentIndent\n : parentIndent + 1\n const blockIndent = state.position - state.lineStart\n\n if (indentStatus === 1) {\n if ((allowBlockCollections &&\n (readBlockSequence(state, blockIndent, props) ||\n readBlockMapping(state, blockIndent, flowIndent, props))) ||\n readFlowCollection(state, flowIndent, props)) {\n hasContent = true\n } else {\n const ch = state.input.charCodeAt(state.position)\n\n if (propertyStart !== null && allowPropertyMapping && allowBlockStyles && !allowBlockCollections &&\n ch !== 0x7C/* | */ && ch !== 0x3E/* > */) {\n const fallbackState = snapshotState(state)\n const propertyIndent = propertyStart.position - propertyStart.lineStart\n\n restoreState(state, propertyStart)\n\n if (readBlockMapping(state, propertyIndent, flowIndent, emptyProperties()) &&\n state.events[fallbackState.eventsLength]?.type === EVENT_ID.MAPPING) {\n hasContent = true\n } else {\n restoreState(state, fallbackState)\n }\n }\n\n if (!hasContent &&\n ((allowBlockScalars && readBlockScalar(state, flowIndent, props)) ||\n readSingleQuotedScalar(state, flowIndent, props) ||\n readDoubleQuotedScalar(state, flowIndent, props) ||\n readAlias(state, props) ||\n readPlainScalar(state, flowIndent, nodeContext, props))) {\n hasContent = true\n }\n }\n } else if (indentStatus === 0) {\n hasContent = allowBlockCollections && readBlockSequence(state, blockIndent, props)\n }\n }\n\n allowBlockScalars = allowBlockScalars && !hasContent\n\n if (!hasContent && (props.anchorStart !== NO_RANGE || props.tagStart !== NO_RANGE || allowBlockScalars)) {\n addScalarEvent(\n state,\n NO_RANGE,\n NO_RANGE,\n props.anchorStart,\n props.anchorEnd,\n props.tagStart,\n props.tagEnd,\n SCALAR_STYLE.PLAIN\n )\n hasContent = true\n }\n\n state.depth--\n return hasContent || props.anchorStart !== NO_RANGE || props.tagStart !== NO_RANGE\n}\n\nfunction readDirective (state: ParserState) {\n if (state.lineIndent > 0 || state.input.charCodeAt(state.position) !== 0x25/* % */) return false\n\n state.position++\n const nameStart = state.position\n\n while (state.input.charCodeAt(state.position) !== 0 && !isWsOrEol(state.input.charCodeAt(state.position))) state.position++\n\n const name = state.input.slice(nameStart, state.position)\n const args: string[] = []\n\n if (name.length === 0) throwError(state, 'directive name must not be less than one character in length')\n\n while (state.input.charCodeAt(state.position) !== 0 && !isEol(state.input.charCodeAt(state.position))) {\n while (isWhiteSpace(state.input.charCodeAt(state.position))) state.position++\n if (state.input.charCodeAt(state.position) === 0x23/* # */ || isEol(state.input.charCodeAt(state.position)) || state.input.charCodeAt(state.position) === 0) break\n\n const start = state.position\n while (state.input.charCodeAt(state.position) !== 0 && !isWsOrEol(state.input.charCodeAt(state.position))) state.position++\n args.push(state.input.slice(start, state.position))\n }\n\n if (isEol(state.input.charCodeAt(state.position))) consumeLineBreak(state)\n\n if (name === 'YAML') {\n if (state.directives.some(directive => directive.kind === 'yaml')) throwError(state, 'duplication of %YAML directive')\n if (args.length !== 1) throwError(state, 'YAML directive accepts exactly one argument')\n\n const match = /^([0-9]+)\\.([0-9]+)$/.exec(args[0])\n if (match === null) throwError(state, 'ill-formed argument of the YAML directive')\n if (parseInt(match[1], 10) !== 1) throwError(state, 'unacceptable YAML version of the document')\n\n state.directives.push({ kind: 'yaml', version: args[0] })\n } else if (name === 'TAG') {\n if (args.length !== 2) throwError(state, 'TAG directive accepts exactly two arguments')\n\n const [handle, prefix] = args\n if (!PATTERN_TAG_HANDLE.test(handle)) throwError(state, 'ill-formed tag handle (first argument) of the TAG directive')\n if (HAS_OWN.call(state.tagHandlers, handle)) throwError(state, `there is a previously declared suffix for \"${handle}\" tag handle`)\n if (!PATTERN_TAG_PREFIX.test(prefix)) throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive')\n try {\n decodeURIComponent(prefix)\n } catch {\n throwError(state, `tag prefix is malformed: ${prefix}`)\n }\n\n state.tagHandlers[handle] = prefix\n state.directives.push({ kind: 'tag', handle, prefix })\n }\n\n return true\n}\n\nfunction readDocument (state: ParserState) {\n state.directives = []\n state.tagHandlers = Object.create(null)\n let hasDirectives = false\n\n skipSeparationSpace(state, true)\n\n while (readDirective(state)) {\n hasDirectives = true\n skipSeparationSpace(state, true)\n }\n\n let explicitStart = false\n let explicitEnd = false\n let allowCompact = true\n\n if (state.lineIndent === 0 &&\n state.input.charCodeAt(state.position) === 0x2D/* - */ &&\n state.input.charCodeAt(state.position + 1) === 0x2D/* - */ &&\n state.input.charCodeAt(state.position + 2) === 0x2D/* - */ &&\n isWsOrEolOrEnd(state.input.charCodeAt(state.position + 3))) {\n explicitStart = true\n const markerLine = state.line\n state.position += 3\n skipSeparationSpace(state, true)\n allowCompact = state.line > markerLine\n } else if (hasDirectives) {\n throwError(state, 'directives end mark is expected')\n }\n\n const documentEventIndex = state.events.length\n if (!explicitStart &&\n state.position === state.lineStart &&\n state.input.charCodeAt(state.position) === 0x2E/* . */ &&\n testDocumentSeparator(state)) {\n state.position += 3\n skipSeparationSpace(state, true)\n return\n }\n\n addDocumentEvent(state, explicitStart, false)\n if (!parseNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, allowCompact, allowCompact)) {\n addEmptyScalarEvent(state)\n }\n skipSeparationSpace(state, true)\n\n if (state.position === state.lineStart && testDocumentSeparator(state)) {\n explicitEnd = state.input.charCodeAt(state.position) === 0x2E/* . */\n if (explicitEnd) {\n const markerLine = state.line\n state.position += 3\n skipSeparationSpace(state, true)\n if (state.line === markerLine && state.position < state.length) {\n throwError(state, 'end of the stream or a document separator is expected')\n }\n }\n }\n\n const documentEvent = state.events[documentEventIndex]\n if (documentEvent?.type === EVENT_ID.DOCUMENT) documentEvent.explicitEnd = explicitEnd\n\n addPopEvent(state)\n\n if (!explicitEnd &&\n state.position < state.length &&\n !testDocumentBoundary(state)) {\n throwError(state, 'end of the stream or a document separator is expected')\n }\n}\n\n/**\n * Parses YAML into a flat event stream referencing source text by offsets.\n *\n * @category Events\n */\nfunction parseEvents (input: string, options: ParserOptions): Event[] {\n const length = input.length\n const state: ParserState = {\n ...DEFAULT_PARSER_OPTIONS,\n ...options,\n input: `${input}\\0`,\n length,\n position: 0,\n line: 0,\n lineStart: 0,\n lineIndent: 0,\n firstTabInLine: -1,\n depth: 0,\n directives: [],\n tagHandlers: Object.create(null),\n events: []\n }\n\n const nullpos = input.indexOf('\\0')\n if (nullpos !== -1) YAMLException.throwAt(input, nullpos, 'null byte is not allowed in input', state.filename)\n\n while (state.position < state.length) {\n skipByteOrderMark(state)\n skipSeparationSpace(state, true)\n if (state.position >= state.length) break\n const documentStart = state.position\n readDocument(state)\n if (state.position === documentStart) {\n // Internal progress guard: if readDocument() ever returns without\n // consuming input, stop here instead of looping forever.\n /* c8 ignore next */\n throwError(state, 'can not read a document')\n }\n }\n\n return state.events\n}\n\nexport {\n parseEvents,\n DEFAULT_PARSER_OPTIONS,\n type ParserOptions\n}\n","import { YAMLException } from './common/exception.ts'\nimport { pick } from './common/object.ts'\nimport {\n constructFromEvents,\n DEFAULT_CONSTRUCTOR_OPTIONS,\n type ConstructorOptions\n} from './parser/constructor.ts'\nimport {\n parseEvents,\n DEFAULT_PARSER_OPTIONS,\n type ParserOptions\n} from './parser/parser.ts'\n\n// `source` is supplied by `loadDocuments` itself, not by the public caller.\n/** @category Main */\ninterface LoadOptions extends ParserOptions, Omit<ConstructorOptions, 'source'> {}\n\n/** @inline */\ntype LoadAllIterator = (document: unknown) => void\n\nconst DEFAULT_LOAD_OPTIONS: Required<LoadOptions> = {\n ...DEFAULT_PARSER_OPTIONS,\n ...DEFAULT_CONSTRUCTOR_OPTIONS\n}\n\nfunction loadDocuments (input: string, options: LoadOptions = {}) {\n const opts = { ...DEFAULT_LOAD_OPTIONS, ...options }\n const source = String(input)\n\n const PARSER_OPT_KEYS = Object.keys(DEFAULT_PARSER_OPTIONS) as\n (keyof typeof DEFAULT_PARSER_OPTIONS)[]\n const CONSTRUCTOR_OPT_KEYS = Object.keys(DEFAULT_CONSTRUCTOR_OPTIONS) as\n (keyof typeof DEFAULT_CONSTRUCTOR_OPTIONS)[]\n\n const events = parseEvents(source, pick(opts, PARSER_OPT_KEYS))\n return constructFromEvents(events, { ...pick(opts, CONSTRUCTOR_OPT_KEYS), source })\n}\n\n/**\n * Same as {@link load}, but understands multi-document sources.\n * Returns an array of documents.\n *\n * @category Main\n */\nfunction loadAll (input: string, options?: LoadOptions): unknown[]\n\n/**\n * @deprecated Iterator is not supported.\n */\nfunction loadAll (input: string, iterator: null, options?: LoadOptions): unknown[]\n\n/**\n * @deprecated Iterator is not supported.\n */\nfunction loadAll (input: string, iterator: LoadAllIterator, options?: LoadOptions): void\nfunction loadAll (\n input: string,\n iteratorOrOptions?: LoadAllIterator | LoadOptions | null,\n options?: LoadOptions\n) {\n let iterator: LoadAllIterator | null = null\n\n if (typeof iteratorOrOptions === 'function') {\n iterator = iteratorOrOptions\n } else if (iteratorOrOptions !== null && typeof iteratorOrOptions === 'object') {\n options = iteratorOrOptions\n }\n\n const documents = loadDocuments(input, options)\n\n if (iterator === null) return documents\n for (const document of documents) iterator(document)\n}\n\n/**\n * Parses `string` as a single YAML document. Throws {@link YAMLException} on\n * error. This function does not understand multi-document or empty sources; it\n * throws an exception on those.\n *\n * > [!NOTE]\n * > 1. When processing untrusted input, see the\n * > [security considerations](../docs/safety.md).\n * > 2. All exceptions MUST be caught, not just {@link YAMLException}.\n * > 3. The default {@link CORE_SCHEMA} comes without the `!!merge` tag. You can\n * > easily enable it if needed.\n * > 4. The default {@link mapTag} is `{}`-object based, with known limitations\n * > (see description). For full compatibility use {@link realMapTag}\n * > instead (it uses native JS `Map`).\n *\n * @example\n * Enable {@link mergeTag} and {@link realMapTag}:\n *\n * ```javascript\n * import { load, CORE_SCHEMA, mergeTag, realMapTag } from 'js-yaml'\n *\n * try {\n * load(data, { schema: CORE_SCHEMA.withTags(mergeTag, realMapTag) })\n * } catch (e) {\n * console.error(e)\n * }\n * ```\n *\n * @category Main\n */\nfunction load (input: string, options?: LoadOptions) {\n const documents = loadDocuments(input, options)\n\n if (documents.length === 0) throw new YAMLException('expected a document, but the input is empty')\n if (documents.length === 1) return documents[0]\n\n throw new YAMLException('expected a single document in the stream, but found more')\n}\n\nexport {\n load,\n loadAll,\n type LoadOptions\n}\n","// JS value graph → AST. Knows tags (`identify` / `represent`). A single\n// identity-`Map` walk handles dedup: a repeat occurrence of an object (including\n// a cycle) becomes an `alias`, and the first occurrence gets an `anchor`.\n\nimport { YAMLException } from '../common/exception.ts'\nimport { type Schema } from '../schema.ts'\nimport { type TagDefinition } from '../tag.ts'\nimport { tagNameShort } from '../common/tagname.ts'\nimport { COLLECTION_STYLE, SCALAR_STYLE } from '../parser/events.ts'\nimport {\n type Document,\n type Node,\n type ScalarNode,\n type SequenceNode,\n type MappingNode\n} from './nodes.ts'\n\n/** @category AST */\ninterface FromJsOptions {\n /** Inlines duplicate objects instead of converting them into references. */\n noRefs?: boolean\n\n /**\n * Skips unrepresentable values instead of throwing. Invalid mapping pairs\n * and sequence items are skipped; `undefined` sequence items become `null`.\n */\n skipInvalid?: boolean\n}\n\n// A match candidate. `implicitTag` means the tag is not printed (implicit\n// scalars and the default str/seq/map tags).\ninterface RepresentType {\n tag: TagDefinition\n implicitTag: boolean\n}\n\n// Returned by `build` when no tag matched.\nconst INVALID = Symbol('INVALID')\n\ninterface FromJsState {\n representTypes: RepresentType[]\n noRefs: boolean\n skipInvalid: boolean\n\n // Already-built collection values → their node, for anchor/alias dedup.\n refs: Map<unknown, Node>\n refCounter: number\n}\n\nfunction buildRepresentTypes (schema: Schema): RepresentType[] {\n const defaultTags = new Set<TagDefinition>([\n schema.defaultScalarTag,\n schema.defaultSequenceTag,\n schema.defaultMappingTag\n ].filter((t): t is TagDefinition => t !== undefined))\n\n // Default container/str tags go last so a more specific tag identifying the\n // same JS value (e.g. a custom tag on a plain object) wins.\n const implicitScalars = schema.implicitScalarTags\n const explicitTags = schema.tags.filter(t =>\n !(t.nodeKind === 'scalar' && t.implicit) && !defaultTags.has(t))\n const defaultTagsLast = schema.tags.filter(t => defaultTags.has(t))\n\n return [\n ...implicitScalars.map(tag => ({ tag, implicitTag: true })),\n ...explicitTags.map(tag => ({ tag, implicitTag: false })),\n ...defaultTagsLast.map(tag => ({ tag, implicitTag: true }))\n ]\n}\n\n// First tag whose `identify` accepts `object`.\nfunction matchTag (state: FromJsState, object: unknown): { tag: TagDefinition, tagName: string, implicitTag: boolean } | null {\n for (let index = 0, length = state.representTypes.length; index < length; index += 1) {\n const { tag, implicitTag } = state.representTypes[index]\n\n if (tag.identify(object)) {\n let tagName: string\n if (tag.matchByTagPrefix) {\n tagName = tag.representTagName(object)\n } else {\n tagName = tag.tagName\n }\n return { tag, tagName, implicitTag }\n }\n }\n\n return null\n}\n\n// Build a node for `object`, or INVALID when no tag matches. `undefined` never\n// throws (caller decides: null in a sequence, skip in a mapping, '' at root);\n// any other unrepresentable value throws unless `skipInvalid`.\nfunction build (state: FromJsState, object: unknown): Node | typeof INVALID {\n if (!state.noRefs && object !== null && typeof object === 'object') {\n const existing = state.refs.get(object)\n if (existing) {\n if (existing.anchor === undefined) existing.anchor = `ref_${state.refCounter++}`\n return { kind: 'alias', anchor: existing.anchor }\n }\n }\n\n const matched = matchTag(state, object)\n\n if (!matched) {\n if (object === undefined) return INVALID\n if (state.skipInvalid) return INVALID\n throw new YAMLException(`unacceptable kind of an object to dump ${Object.prototype.toString.call(object)}`)\n }\n\n const { tag, tagName, implicitTag } = matched\n const nodeTagName = implicitTag ? tagName : tagNameShort(tagName)\n\n if (tag.nodeKind === 'scalar') {\n const node: ScalarNode = {\n kind: 'scalar',\n tag: nodeTagName,\n tagged: !implicitTag,\n style: SCALAR_STYLE.PLAIN,\n value: tag.represent(object)\n }\n return node\n }\n\n if (tag.nodeKind === 'sequence') {\n const container = tag.represent(object)\n const node: SequenceNode = {\n kind: 'sequence',\n tag: nodeTagName,\n tagged: !implicitTag,\n style: COLLECTION_STYLE.BLOCK,\n items: []\n }\n if (!state.noRefs) state.refs.set(object, node)\n\n for (let index = 0, length = container.length; index < length; index += 1) {\n let item = build(state, container[index])\n // An invalid element becomes null; a still-invalid null then skips/throws.\n if (item === INVALID && container[index] === undefined) item = build(state, null)\n if (item === INVALID) continue\n node.items.push(item)\n }\n return node\n }\n\n // mapping — the canonical form is always a `Map`.\n const map = tag.represent(object)\n const node: MappingNode = {\n kind: 'mapping',\n tag: nodeTagName,\n tagged: !implicitTag,\n style: COLLECTION_STYLE.BLOCK,\n items: []\n }\n if (!state.noRefs) state.refs.set(object, node)\n\n for (const [objectKey, objectValue] of map) {\n const key = build(state, objectKey)\n if (key === INVALID) continue // invalid key skips the pair\n const value = build(state, objectValue)\n if (value === INVALID) continue // invalid value skips the pair\n node.items.push({ key, value })\n }\n return node\n}\n\n/**\n * Convert JS object to AST. A JS value is one YAML document. An unrepresentable\n * root becomes an empty document, which the presenter renders as an empty\n * string.\n *\n * @category AST\n */\nfunction jsToAst (input: unknown, schema: Schema, options: FromJsOptions = {}): Document[] {\n const state: FromJsState = {\n representTypes: buildRepresentTypes(schema),\n noRefs: options.noRefs ?? false,\n skipInvalid: options.skipInvalid ?? false,\n refs: new Map(),\n refCounter: 0\n }\n\n const root = build(state, input)\n return [{ contents: root === INVALID ? null : root, directives: [] }]\n}\n\nexport {\n jsToAst,\n type FromJsOptions\n}\n","// Depth-first AST traversal. Mirrors the `kind` walk of the presenter and the\n// `from_*` builders, but stays read-oriented: nodes are plain objects, so a\n// visitor mutates them in place. Control signals let it prune or stop the walk.\n\nimport {\n type Node,\n type Document\n} from './nodes.ts'\n\n// Returned by a visitor to control the walk; anything else (incl. `undefined`)\n// descends as usual.\n/**\n * Return from a visitor to stop the whole traversal.\n *\n * @category AST\n */\nconst VISIT_BREAK = Symbol('visit:break')\n\n/**\n * Return from a visitor to skip the current node's children.\n *\n * @category AST\n */\nconst VISIT_SKIP = Symbol('visit:skip')\n\n/** @inline */\ntype VisitControl = typeof VISIT_BREAK | typeof VISIT_SKIP | undefined | void\n\n/**\n * Traversal-derived position of the current node. Kept off the node itself: a\n * node may sit in several places (alias/dedup reuse), so depth/role belong to\n * the walk, not the node. {@link VisitContext.parent} `kind` +\n * {@link VisitContext.isKey} pin the exact slot.\n *\n * @category AST\n */\ninterface VisitContext {\n /** 0 = document content root */\n depth: number\n\n /** Enclosing sequence/mapping, null at the root */\n parent: Node | null\n\n /** Node sits in a mapping key position */\n isKey: boolean\n}\n\n/** @category AST */\ntype Visitor = (node: Node, ctx: VisitContext) => VisitControl\n\n// Returns `true` once `VISIT_BREAK` was seen, so callers can unwind the walk.\nfunction visitNode (node: Node, visitor: Visitor, ctx: VisitContext): boolean {\n const control = visitor(node, ctx)\n if (control === VISIT_BREAK) return true\n if (control === VISIT_SKIP) return false\n\n const depth = ctx.depth + 1\n\n switch (node.kind) {\n case 'sequence':\n for (const item of node.items) {\n if (visitNode(item, visitor, { depth, parent: node, isKey: false })) return true\n }\n break\n case 'mapping':\n for (const { key, value } of node.items) {\n if (visitNode(key, visitor, { depth, parent: node, isKey: true })) return true\n if (visitNode(value, visitor, { depth, parent: node, isKey: false })) return true\n }\n break\n }\n\n return false\n}\n\n/**\n * Walk every node in the documents, calling {@link Visitor} once per\n * node (pre-order).\n *\n * @category AST\n */\nfunction visit (documents: Document[], visitor: Visitor): void {\n for (const doc of documents) {\n if (doc.contents && visitNode(doc.contents, visitor, { depth: 0, parent: null, isKey: false })) return\n }\n}\n\nexport {\n visit,\n VISIT_BREAK,\n VISIT_SKIP,\n type Visitor,\n type VisitContext\n}\n","import { SCALAR_STYLE, type ScalarStyle } from '../parser/events.ts'\nimport { type ScalarLayout } from './scalar_styler.ts'\n\nfunction hasBit (mask: number, bit: number): boolean { return (mask & (1 << bit)) !== 0 }\n\n// This should eventually be a presenter option, but collection styling,\n// especially key layout, must be designed first to decide whether scalar and\n// collection width limits should share an option or be configured separately.\nconst MIN_SCALAR_CONTENT_WIDTH = 40\n\n/**\n * Default scalar styling rules in application order.\n * See [Scalar styling](../../docs/scalar_styling.md) for usage details.\n *\n * @category AST\n */\nconst DEFAULT_SCALAR_STYLE_RULES = {\n applyQuoteFlowKeysOption,\n doubleQuoteForInvisibles,\n doubleQuoteWhitespaceOnly,\n applyForceQuotesOption,\n tryLongOrMultilineAsBlock,\n quoteInvalidPlain,\n fallbackToDoubleQuoted\n} as const\n\nfunction _preferredQuotedStyle (layout: ScalarLayout): ScalarStyle {\n if (layout.presenterOptions.quoteStyle === 'single' &&\n hasBit(layout.allowedStylesMask, SCALAR_STYLE.SINGLE_QUOTED)) {\n return SCALAR_STYLE.SINGLE_QUOTED\n }\n\n return SCALAR_STYLE.DOUBLE_QUOTED\n}\n\nfunction applyQuoteFlowKeysOption (layout: ScalarLayout): void {\n if (!layout.presenterOptions.quoteFlowKeys) return\n\n // quoteFlowKeys applies only to plain scalar keys in flow mappings.\n if (!layout.isKey || !layout.flowOnly || layout.style !== SCALAR_STYLE.PLAIN) return\n\n layout.style = SCALAR_STYLE.DOUBLE_QUOTED\n}\n\nfunction doubleQuoteForInvisibles (layout: ScalarLayout): void {\n if (layout.style === SCALAR_STYLE.PLAIN &&\n /[\\t\\x7F-\\xA0\\u2028\\u2029\\uFEFF\\uFFFE\\uFFFF]/.test(layout.node.value)) {\n layout.style = SCALAR_STYLE.DOUBLE_QUOTED\n }\n}\n\nfunction doubleQuoteWhitespaceOnly (layout: ScalarLayout): void {\n // Block styles normally make multiline structure easier to see, but\n // whitespace-only content turns that structure into visually empty lines,\n // so force double quotes for this special case.\n if (layout.style === SCALAR_STYLE.PLAIN && /^\\s+$/.test(layout.node.value)) {\n layout.style = SCALAR_STYLE.DOUBLE_QUOTED\n }\n}\n\nfunction applyForceQuotesOption (layout: ScalarLayout): void {\n if (!layout.presenterOptions.forceQuotes) return\n\n // forceQuotes applies only to plain string values, not to mapping keys\n // or other types.\n if (layout.isKey || layout.style !== SCALAR_STYLE.PLAIN) return\n if (layout.node.tag !== layout.presenterOptions.schema.defaultScalarTag.tagName) return\n\n layout.style = layout.node.value.includes('\\n')\n ? SCALAR_STYLE.DOUBLE_QUOTED\n : _preferredQuotedStyle(layout)\n}\n\nfunction tryLongOrMultilineAsBlock (layout: ScalarLayout): void {\n if (layout.style !== SCALAR_STYLE.PLAIN || layout.isKey) return\n\n const value = layout.node.value\n const multiline = value.indexOf('\\n') !== -1\n\n // Literal and folded block style bits are always set together, so checking\n // either one is sufficient.\n if (!hasBit(layout.allowedStylesMask, SCALAR_STYLE.LITERAL_BLOCK)) {\n if (multiline) layout.style = SCALAR_STYLE.DOUBLE_QUOTED\n return\n }\n\n const w = layout.presenterOptions.lineWidth\n\n if (w === -1) {\n if (multiline) layout.style = SCALAR_STYLE.LITERAL_BLOCK\n return\n }\n\n const availableWidth = Math.max(\n Math.min(w, MIN_SCALAR_CONTENT_WIDTH),\n w - layout.shiftOfContent\n )\n\n let position = 0\n let shouldFold = false\n\n // Check whether at least one line exceeds the width budget\n // and can be split at a space.\n while (position <= value.length) {\n let lineEnd = value.length\n\n const nextLineBreak = value.indexOf('\\n', position)\n\n if (nextLineBreak !== -1) lineEnd = nextLineBreak\n\n const line = value.slice(position, lineEnd)\n\n if (line.length > availableWidth &&\n line[0] !== ' ' &&\n / [^ \\t]/.test(line)) {\n shouldFold = true\n }\n\n if (nextLineBreak === -1) break\n position = nextLineBreak + 1\n }\n\n if (shouldFold) {\n layout.style = SCALAR_STYLE.FOLDED_BLOCK\n } else if (multiline) {\n layout.style = SCALAR_STYLE.LITERAL_BLOCK\n }\n}\n\nfunction quoteInvalidPlain (layout: ScalarLayout): void {\n if (layout.style === SCALAR_STYLE.PLAIN &&\n !hasBit(layout.allowedStylesMask, SCALAR_STYLE.PLAIN)) {\n layout.style = _preferredQuotedStyle(layout)\n }\n}\n\nfunction fallbackToDoubleQuoted (layout: ScalarLayout): void {\n if (!hasBit(layout.allowedStylesMask, layout.style)) {\n layout.style = SCALAR_STYLE.DOUBLE_QUOTED\n }\n}\n\nexport { MIN_SCALAR_CONTENT_WIDTH, DEFAULT_SCALAR_STYLE_RULES }\n","import { SCALAR_STYLE, type ScalarStyle } from '../parser/events.ts'\nimport { type Node, type ScalarNode } from './nodes.ts'\nimport { type PresenterOptions } from './presenter.ts'\nimport { MIN_SCALAR_CONTENT_WIDTH } from './styler_defaults.ts'\n\n/** Scalar presentation state passed to styling rules. @category AST */\ninterface ScalarLayout {\n readonly node: Readonly<ScalarNode>\n readonly parent: Readonly<Node> | null\n readonly level: number\n readonly isKey: boolean\n readonly flowOnly: boolean\n readonly shiftOfParent: number\n readonly shiftOfContent: number\n readonly shiftOfFirstLine: number\n readonly presenterOptions: Readonly<Required<PresenterOptions>>\n /**\n * Bit mask of allowed styles; each bit corresponds to a {@link SCALAR_STYLE}\n * value.\n */\n allowedStylesMask: number\n /**\n * Selected output style, which styling rules may modify. To avoid overriding\n * earlier decisions, a rule should normally modify it only while it is\n * {@link SCALAR_STYLE.PLAIN}.\n */\n style: ScalarStyle\n}\n\n/** Function signature for scalar styling rules. @category AST */\ntype ScalarStyleRule = (layout: ScalarLayout) => void\n\nfunction setBit (mask: number, bit: number): number { return mask | (1 << bit) }\n\n// YAML 1.2.2 character productions.\n// https://yaml.org/spec/1.2.2/#51-character-set\nconst SRC_C_PRINTABLE = '[\\\\x09\\\\x0A\\\\x0D\\\\x20-\\\\x7E\\\\x85\\\\xA0-\\\\uD7FF\\\\uE000-\\\\uFFFD\\\\u{10000}-\\\\u{10FFFF}]'\nconst SRC_B_CHAR = '[\\\\n\\\\r]'\nconst SRC_C_BYTE_ORDER_MARK = '\\\\uFEFF'\nconst SRC_S_WHITE = '[ \\\\t]'\nconst SRC_NB_CHAR = `(?:(?!(?:${SRC_B_CHAR}|${SRC_C_BYTE_ORDER_MARK}))${SRC_C_PRINTABLE})`\nconst SRC_NS_CHAR = `(?:(?!${SRC_S_WHITE})${SRC_NB_CHAR})`\n\n// YAML 1.2.2 [2] nb-json.\nconst SRC_NB_JSON = '[\\\\x09\\\\x20-\\\\uD7FF\\\\uE000-\\\\uFFFF\\\\u{10000}-\\\\u{10FFFF}]'\n\n// YAML 1.2.2 indicator productions.\n// https://yaml.org/spec/1.2.2/#54-indicator-characters\nconst SRC_C_INDICATOR = '[-?:,\\\\[\\\\]{}#&*!|>\\'\"%@`]'\nconst SRC_C_FLOW_INDICATOR = '[,\\\\[\\\\]{}]'\n\n// YAML 1.2.2 [127]-[129] ns-plain-safe(c).\n// https://yaml.org/spec/1.2.2/#733-plain-style\nconst SRC_NS_PLAIN_SAFE_FLOW_OUT = SRC_NS_CHAR\nconst SRC_NS_PLAIN_SAFE_FLOW_IN = `(?:(?!${SRC_C_FLOW_INDICATOR})${SRC_NS_CHAR})`\n\n// YAML 1.2.2 [126] ns-plain-first(c).\nconst SRC_NS_PLAIN_FIRST_FLOW_OUT =\n `(?:(?:(?!${SRC_C_INDICATOR})${SRC_NS_CHAR})|[?:-](?=${SRC_NS_PLAIN_SAFE_FLOW_OUT}))`\nconst SRC_NS_PLAIN_FIRST_FLOW_IN =\n `(?:(?:(?!${SRC_C_INDICATOR})${SRC_NS_CHAR})|[?:-](?=${SRC_NS_PLAIN_SAFE_FLOW_IN}))`\n\n// YAML 1.2.2 [130] ns-plain-char(c).\n// The production itself requires lookbehind, so these regexps require ES2018 lookbehind support.\n// const SRC_NS_PLAIN_CHAR_FLOW_OUT =\n// `(?:(?:(?![:#])${SRC_NS_PLAIN_SAFE_FLOW_OUT})|(?<=${SRC_NS_CHAR})#|:(?=${SRC_NS_PLAIN_SAFE_FLOW_OUT}))`\n// const SRC_NS_PLAIN_CHAR_FLOW_IN =\n// `(?:(?:(?![:#])${SRC_NS_PLAIN_SAFE_FLOW_IN})|(?<=${SRC_NS_CHAR})#|:(?=${SRC_NS_PLAIN_SAFE_FLOW_IN}))`\n\n// ES2015-compatible alternative without lookbehind: consume each run of hashes\n// together with the preceding non-hash ns-plain-char.\nconst SRC_NS_PLAIN_CHAR_FLOW_OUT =\n `(?:(?:(?![:#])${SRC_NS_PLAIN_SAFE_FLOW_OUT})|:(?=${SRC_NS_PLAIN_SAFE_FLOW_OUT}))#*`\nconst SRC_NS_PLAIN_CHAR_FLOW_IN =\n `(?:(?:(?![:#])${SRC_NS_PLAIN_SAFE_FLOW_IN})|:(?=${SRC_NS_PLAIN_SAFE_FLOW_IN}))#*`\n\n// YAML 1.2.2 [132] nb-ns-plain-in-line(c).\nconst SRC_NB_NS_PLAIN_IN_LINE_FLOW_OUT = `(?:${SRC_S_WHITE}*${SRC_NS_PLAIN_CHAR_FLOW_OUT})*`\nconst SRC_NB_NS_PLAIN_IN_LINE_FLOW_IN = `(?:${SRC_S_WHITE}*${SRC_NS_PLAIN_CHAR_FLOW_IN})*`\n\n// YAML 1.2.2 [133] ns-plain-one-line(c).\nconst SRC_NS_PLAIN_ONE_LINE_FLOW_OUT =\n `${SRC_NS_PLAIN_FIRST_FLOW_OUT}#*${SRC_NB_NS_PLAIN_IN_LINE_FLOW_OUT}`\nconst SRC_NS_PLAIN_ONE_LINE_FLOW_IN =\n `${SRC_NS_PLAIN_FIRST_FLOW_IN}#*${SRC_NB_NS_PLAIN_IN_LINE_FLOW_IN}`\nconst SRC_NS_PLAIN_ONE_LINE_BLOCK_KEY = SRC_NS_PLAIN_ONE_LINE_FLOW_OUT\nconst SRC_NS_PLAIN_ONE_LINE_FLOW_KEY = SRC_NS_PLAIN_ONE_LINE_FLOW_IN\n\n// YAML 1.2.2 [134] s-ns-plain-next-line(n,c).\n// ScalarNode.value contains the folded value, not the source text: one source\n// line break became a space, while k > 1 breaks became k - 1 LF characters.\n// The space case is already accepted by [132]; each remaining run of LFs\n// therefore represents a transition to the next non-empty content line in [134].\nconst SRC_S_NS_PLAIN_NEXT_LINE_FLOW_OUT =\n `\\\\n+${SRC_NS_PLAIN_CHAR_FLOW_OUT}${SRC_NB_NS_PLAIN_IN_LINE_FLOW_OUT}`\nconst SRC_S_NS_PLAIN_NEXT_LINE_FLOW_IN =\n `\\\\n+${SRC_NS_PLAIN_CHAR_FLOW_IN}${SRC_NB_NS_PLAIN_IN_LINE_FLOW_IN}`\n\n// YAML 1.2.2 [135] ns-plain-multi-line(n,c).\nconst SRC_NS_PLAIN_MULTI_LINE_FLOW_OUT =\n `${SRC_NS_PLAIN_ONE_LINE_FLOW_OUT}(?:${SRC_S_NS_PLAIN_NEXT_LINE_FLOW_OUT})*`\nconst SRC_NS_PLAIN_MULTI_LINE_FLOW_IN =\n `${SRC_NS_PLAIN_ONE_LINE_FLOW_IN}(?:${SRC_S_NS_PLAIN_NEXT_LINE_FLOW_IN})*`\n\n// YAML 1.2.2 [131] ns-plain(n,c).\nconst NS_PLAIN_FLOW_OUT = new RegExp(`^(?:${SRC_NS_PLAIN_MULTI_LINE_FLOW_OUT})$`, 'u')\nconst NS_PLAIN_FLOW_IN = new RegExp(`^(?:${SRC_NS_PLAIN_MULTI_LINE_FLOW_IN})$`, 'u')\nconst NS_PLAIN_BLOCK_KEY = new RegExp(`^(?:${SRC_NS_PLAIN_ONE_LINE_BLOCK_KEY})$`, 'u')\nconst NS_PLAIN_FLOW_KEY = new RegExp(`^(?:${SRC_NS_PLAIN_ONE_LINE_FLOW_KEY})$`, 'u')\n\n// YAML 1.2.2 [118]-[125], projected to ScalarNode.value: doubled quotes\n// are already decoded, and flow folding represents content line feeds as LF.\nconst NB_SINGLE_ONE_LINE = new RegExp(`^(?:${SRC_NB_JSON})*$`, 'u')\nconst NB_SINGLE_MULTI_LINE = new RegExp(`^(?:${SRC_NB_JSON}|\\\\n)*$`, 'u')\n\n// YAML 1.2.2 [170]-[182], projected to ScalarNode.value: source line breaks\n// are normalized to LF; every other content character must be nb-char.\nconst BLOCK_SCALAR_CONTENT = new RegExp(`^(?:${SRC_NB_CHAR}|\\\\n)*$`, 'u')\n\n// YAML 1.2.2 [206] c-forbidden.\n// https://yaml.org/spec/1.2.2/#912-document-markers\nconst C_FORBIDDEN_FIRST_LINE = /^(?:---|\\.\\.\\.)(?=$|[ \\t\\n\\r])/\nconst C_FORBIDDEN_CONTENT = /^(?:---|\\.\\.\\.)(?=$|[ \\t\\n\\r])/m\n\nfunction canUsePlain (layout: ScalarLayout): boolean {\n const str = layout.node.value\n\n // Allow null to be rendered as an empty scalar; its tag is checked below.\n if (str !== '') {\n const nsPlain = layout.isKey\n ? (layout.flowOnly ? NS_PLAIN_FLOW_KEY : NS_PLAIN_BLOCK_KEY)\n : (layout.flowOnly ? NS_PLAIN_FLOW_IN : NS_PLAIN_FLOW_OUT)\n\n if (!nsPlain.test(str)) return false\n if (layout.shiftOfFirstLine === 0 && C_FORBIDDEN_FIRST_LINE.test(str)) return false\n\n if (layout.shiftOfContent === 0) {\n const firstLineBreak = str.indexOf('\\n')\n\n if (firstLineBreak !== -1) {\n const content = str.slice(firstLineBreak + 1)\n\n if (C_FORBIDDEN_CONTENT.test(content)) return false\n }\n }\n }\n\n // Outside the plain-syntax BNF: preserve the node tag under implicit resolution.\n const resolvedTag = layout.presenterOptions.schema.resolveImplicitScalarTag(str).tag.tagName\n\n if (!layout.node.tagged && resolvedTag !== layout.node.tag) return false\n\n // Outside YAML 1.2.2: preserve YAML 1.1 !!value semantics.\n // https://yaml.org/type/value.html\n if (!layout.node.tagged && str === '=' &&\n resolvedTag === layout.presenterOptions.schema.defaultScalarTag.tagName) return false\n\n return true\n}\n\nfunction canUseSingleQuoted (layout: ScalarLayout): boolean {\n const str = layout.node.value\n const nbSingleText = layout.isKey ? NB_SINGLE_ONE_LINE : NB_SINGLE_MULTI_LINE\n\n if (!nbSingleText.test(str)) return false\n\n // [123]-[125] exclude trailing and leading s-white around a folded break;\n // single-quoted style has no escape that could preserve it.\n if (/[ \\t]\\n|\\n[ \\t]/.test(str)) return false\n\n // A decoded LF is rendered through flow folding. At zero continuation\n // indentation, c-forbidden would terminate the quoted scalar.\n if (!layout.isKey && layout.shiftOfContent === 0) {\n const firstLineBreak = str.indexOf('\\n')\n\n if (firstLineBreak !== -1 &&\n C_FORBIDDEN_CONTENT.test(str.slice(firstLineBreak + 1))) return false\n }\n\n return true\n}\n\nfunction canUseBlock (layout: ScalarLayout): boolean {\n if (layout.flowOnly || !BLOCK_SCALAR_CONTENT.test(layout.node.value)) return false\n\n const contentIndent = layout.shiftOfContent - layout.shiftOfParent\n\n if (contentIndent < 1) return false\n\n // A leading space requires [163] c-indentation-indicator. Its value is 1-9.\n if (contentIndent > 9 && /^\\n* /.test(layout.node.value)) return false\n\n // Block content starts on its own line, so every zero-indented content line\n // is subject to [206] c-forbidden.\n if (layout.shiftOfContent === 0 && C_FORBIDDEN_CONTENT.test(layout.node.value)) return false\n\n return true\n}\n\nfunction detectAllowedStyles (layout: ScalarLayout): void {\n // [107] Double-quoted style can express arbitrary strings through escape sequences.\n let mask = setBit(0, SCALAR_STYLE.DOUBLE_QUOTED)\n\n if (canUsePlain(layout)) mask = setBit(mask, SCALAR_STYLE.PLAIN)\n if (canUseSingleQuoted(layout)) mask = setBit(mask, SCALAR_STYLE.SINGLE_QUOTED)\n\n if (canUseBlock(layout)) {\n mask = setBit(setBit(mask, SCALAR_STYLE.LITERAL_BLOCK), SCALAR_STYLE.FOLDED_BLOCK)\n }\n\n layout.allowedStylesMask = mask\n}\n\nfunction renderScalar (layout: ScalarLayout): string {\n switch (layout.style) {\n case SCALAR_STYLE.PLAIN:\n return renderPlain(layout)\n case SCALAR_STYLE.SINGLE_QUOTED:\n return renderSingleQuoted(layout)\n case SCALAR_STYLE.LITERAL_BLOCK:\n return renderLiteralBlock(layout)\n case SCALAR_STYLE.FOLDED_BLOCK:\n return renderFoldedBlock(layout)\n case SCALAR_STYLE.DOUBLE_QUOTED:\n return renderDoubleQuoted(layout)\n }\n}\n\nfunction renderPlain (layout: ScalarLayout): string {\n return encodeFlowBreaks(layout.node.value, layout.shiftOfContent)\n}\n\nfunction renderSingleQuoted (layout: ScalarLayout): string {\n const value = encodeFlowBreaks(layout.node.value, layout.shiftOfContent)\n return `'${value.replace(/'/g, \"''\")}'`\n}\n\nfunction renderLiteralBlock (layout: ScalarLayout): string {\n const value = layout.node.value\n\n return '|' + blockHeader(value, layout.shiftOfParent, layout.shiftOfContent) +\n dropEndingNewline(indentString(value, layout.shiftOfContent))\n}\n\nfunction renderFoldedBlock (layout: ScalarLayout): string {\n const value = layout.node.value\n const w = layout.presenterOptions.lineWidth\n let availableWidth = Infinity\n\n if (w !== -1) {\n availableWidth = Math.max(\n Math.min(w, MIN_SCALAR_CONTENT_WIDTH),\n w - layout.shiftOfContent\n )\n }\n\n return '>' + blockHeader(value, layout.shiftOfParent, layout.shiftOfContent) +\n dropEndingNewline(indentString(\n foldBlockScalar(value, availableWidth),\n layout.shiftOfContent\n ))\n}\n\nfunction renderDoubleQuoted (layout: ScalarLayout): string {\n return `\"${escapeString(layout.node.value)}\"`\n}\n\n// Flow scalars fold line breaks: a run of k source line breaks reparses to k-1\n// literal LF characters. Encode each run of p literal LF characters as p+1\n// breaks and indent the following content line.\nfunction encodeFlowBreaks (string: string, shiftOfContent: number): string {\n let nextLF = string.indexOf('\\n')\n if (nextLF === -1) return string\n\n const pad = ' '.repeat(shiftOfContent)\n let result = string.slice(0, nextLF)\n\n const lineRe = /(\\n+)([^\\n]*)/g\n lineRe.lastIndex = nextLF\n let match\n\n while ((match = lineRe.exec(string))) {\n const breaks = match[1].length\n const line = match[2]\n result += '\\n'.repeat(breaks + 1) + pad + line\n }\n\n return result\n}\n\n// Indents every line in a string. Empty lines (\\n only) are not indented.\nfunction indentString (string: string, spaces: number): string {\n const indent = ' '.repeat(spaces)\n let position = 0\n let result = ''\n const length = string.length\n\n while (position < length) {\n let line\n const next = string.indexOf('\\n', position)\n\n if (next === -1) {\n line = string.slice(position)\n position = length\n } else {\n line = string.slice(position, next + 1)\n position = next + 1\n }\n\n if (line.length && line !== '\\n') result += indent\n\n result += line\n }\n\n return result\n}\n\nfunction needIndentIndicator (string: string): boolean {\n return /^\\n* /.test(string)\n}\n\nfunction blockHeader (string: string, shiftOfParent: number, shiftOfContent: number): string {\n const indentIndicator = needIndentIndicator(string)\n ? String(shiftOfContent - shiftOfParent)\n : ''\n\n // The string '\\n' counts as a trailing empty line.\n const clip = string[string.length - 1] === '\\n'\n const keep = clip && (string[string.length - 2] === '\\n' || string === '\\n')\n const chomp = keep ? '+' : (clip ? '' : '-')\n\n return `${indentIndicator}${chomp}\\n`\n}\n\n// The presenter adds its own trailing newline.\nfunction dropEndingNewline (string: string): string {\n return string[string.length - 1] === '\\n' ? string.slice(0, -1) : string\n}\n\nfunction isMoreIndented (char: string): boolean {\n return char === ' ' || char === '\\t'\n}\n\nfunction foldLine (line: string, width: number): string {\n if (line === '' || isMoreIndented(line[0])) return line\n\n const breakRe = / [^ \\t]/g\n let match\n let start = 0\n let end\n let curr = 0\n let next = 0\n let result = ''\n\n while ((match = breakRe.exec(line))) {\n next = match.index\n\n if (next - start > width) {\n end = (curr > start) ? curr : next\n result += `\\n${line.slice(start, end)}`\n start = end + 1\n }\n\n curr = next\n }\n\n result += '\\n'\n\n if (line.length - start > width && curr > start) {\n result += `${line.slice(start, curr)}\\n${line.slice(curr + 1)}`\n } else {\n result += line.slice(start)\n }\n\n return result.slice(1)\n}\n\nfunction foldBlockScalar (string: string, width: number): string {\n const lineRe = /(\\n+)([^\\n]*)/g\n\n let nextLF = string.indexOf('\\n')\n if (nextLF === -1) nextLF = string.length\n lineRe.lastIndex = nextLF\n\n let result = foldLine(string.slice(0, nextLF), width)\n let prevMoreIndented = string[0] === '\\n' || isMoreIndented(string[0])\n let moreIndented\n let match\n\n while ((match = lineRe.exec(string))) {\n const prefix = match[1]\n const line = match[2]\n\n moreIndented = line !== '' && isMoreIndented(line[0])\n result += prefix +\n ((!prevMoreIndented && !moreIndented && line !== '') ? '\\n' : '') +\n foldLine(line, width)\n prevMoreIndented = moreIndented\n }\n\n return result\n}\n\n// Characters escaped in Double-Quoted Style:\n//\n// - the inverse of YAML 1.2.2 [1] c-printable;\n// - TAB, LF and CR, which c-printable includes;\n// - NEL, NBSP, LS, PS and BOM, which are escaped to make invisible content\n// visible;\n// - double quote and backslash, which must be escaped because they have\n// syntactic meaning.\n//\n// In Unicode mode, the surrogate range matches unpaired surrogates but does not\n// match either code unit of a valid surrogate pair.\n// https://yaml.org/spec/1.2.2/#rule-c-printable\nconst CHARACTERS_TO_ESCAPE =\n /[\"\\\\\\x00-\\x1F\\x7F-\\xA0\\u2028\\u2029\\uD800-\\uDFFF\\uFEFF\\uFFFE\\uFFFF]/gu\n\nfunction escapeCharacter (character: string): string {\n switch (character) {\n case '\\x00': return '\\\\0'\n case '\\x07': return '\\\\a'\n case '\\x08': return '\\\\b'\n case '\\x09': return '\\\\t'\n case '\\x0A': return '\\\\n'\n case '\\x0B': return '\\\\v'\n case '\\x0C': return '\\\\f'\n case '\\x0D': return '\\\\r'\n case '\\x1B': return '\\\\e'\n case '\"': return '\\\\\"'\n case '\\\\': return '\\\\\\\\'\n case '\\x85': return '\\\\N'\n case '\\xA0': return '\\\\_'\n case '\\u2028': return '\\\\L'\n case '\\u2029': return '\\\\P'\n }\n\n const code = character.charCodeAt(0)\n const hex = code.toString(16).toUpperCase()\n\n if (code <= 0xFF) return `\\\\x${'0'.repeat(2 - hex.length)}${hex}`\n\n return `\\\\u${'0'.repeat(4 - hex.length)}${hex}`\n}\n\nfunction escapeString (string: string): string {\n return string.replace(CHARACTERS_TO_ESCAPE, escapeCharacter)\n}\n\nexport {\n detectAllowedStyles,\n renderScalar,\n type ScalarLayout,\n type ScalarStyleRule\n}\n","// AST → text. Walks the node `kind`; the scalar machinery (style selection,\n// quoting, folding) is driven by node text, not by sniffing a JS value.\n\nimport { YAMLException } from '../common/exception.ts'\nimport { tagNameShort } from '../common/tagname.ts'\nimport { COLLECTION_STYLE, SCALAR_STYLE } from '../parser/events.ts'\nimport { type Schema } from '../schema.ts'\nimport {\n type Node,\n type Document,\n type ScalarNode,\n type SequenceNode,\n type MappingNode\n} from './nodes.ts'\nimport {\n detectAllowedStyles,\n renderScalar,\n type ScalarLayout,\n type ScalarStyleRule\n} from './scalar_styler.ts'\nimport { DEFAULT_SCALAR_STYLE_RULES } from './styler_defaults.ts'\n\nconst CHAR_LINE_FEED = 0x0A /* LF */\n\n/** @category AST */\ninterface PresenterOptions {\n /** Schema used when selecting a safe scalar style. */\n schema: Schema\n\n /**\n * Indentation width in spaces.\n *\n * @defaultValue `2`\n */\n indent?: number\n\n /**\n * Does not add an indentation level to array elements when enabled.\n *\n * @defaultValue `false`\n */\n seqNoIndent?: boolean\n\n /**\n * Allows a nested collection to start on the same line after `-`.\n *\n * @defaultValue `true`\n */\n seqInlineFirst?: boolean\n\n /**\n * Preferred line width for folding. Unbreakable and more-indented lines may\n * exceed it. Set to `-1` for unlimited width.\n *\n * @defaultValue `80`\n */\n lineWidth?: number\n\n /**\n * Adds spaces inside flow collection brackets: `{a: 1}` becomes `{ a: 1 }`.\n *\n * @defaultValue `false`\n */\n flowBracketPadding?: boolean\n\n /**\n * Omits the space after commas in flow collections: `[1, 2]` becomes\n * `[1,2]`.\n *\n * @defaultValue `false`\n */\n flowSkipCommaSpace?: boolean\n\n /**\n * Omits the space after `:` in flow mappings: `{\"a\": 1}` becomes `{\"a\":1}`.\n *\n * This forces `quoteFlowKeys`; otherwise `a:1` would be parsed as a single\n * plain scalar instead of a mapping entry.\n *\n * @defaultValue `false`\n */\n flowSkipColonSpace?: boolean\n\n /**\n * Quotes flow mapping keys: `{a: 1}` becomes `{\"a\": 1}`.\n *\n * @defaultValue `false`\n */\n quoteFlowKeys?: boolean\n\n /**\n * Quoting style to use when a string needs quotes.\n *\n * @defaultValue `'single'`\n */\n quoteStyle?: 'single' | 'double'\n\n /**\n * Quotes all non-key strings using {@link quoteStyle}.\n *\n * @defaultValue `false`\n */\n forceQuotes?: boolean\n\n /**\n * Customizes how strings are rendered as plain, quoted, literal, or folded\n * scalars. Rules are applied in array order; providing this option replaces\n * the {@link DEFAULT_SCALAR_STYLE_RULES default rules}.\n *\n * @defaultValue `Object.values(DEFAULT_SCALAR_STYLE_RULES)`\n */\n scalarStyleRules?: readonly ScalarStyleRule[]\n\n /**\n * Prints an explicit tag before an anchor: `&ref_0 !!set` becomes\n * `!!set &ref_0`.\n *\n * @defaultValue `false`\n */\n tagBeforeAnchor?: boolean\n}\n\nconst DEFAULT_PRESENTER_OPTIONS: Required<Omit<PresenterOptions, 'schema'>> = {\n indent: 2,\n seqNoIndent: false,\n seqInlineFirst: true,\n lineWidth: 80,\n flowBracketPadding: false,\n flowSkipCommaSpace: false,\n flowSkipColonSpace: false,\n quoteFlowKeys: false,\n quoteStyle: 'single',\n forceQuotes: false,\n scalarStyleRules: Object.keys(DEFAULT_SCALAR_STYLE_RULES)\n .map(name => Reflect.get(DEFAULT_SCALAR_STYLE_RULES, name)),\n tagBeforeAnchor: false\n}\n\ninterface PresenterState extends Required<PresenterOptions> {\n defaultScalarTagName: string\n openEnded: boolean\n}\n\nfunction nodeTagShort (node: ScalarNode | SequenceNode | MappingNode) {\n return node.tagged ? node.tag : tagNameShort(node.tag)\n}\n\nfunction createPresenterState (options: PresenterOptions): PresenterState {\n const opts = {\n ...DEFAULT_PRESENTER_OPTIONS,\n ...options\n }\n\n if (opts.flowSkipColonSpace) {\n opts.quoteFlowKeys = true\n }\n\n return {\n ...opts,\n defaultScalarTagName: opts.schema.defaultScalarTag.tagName,\n openEnded: false\n }\n}\n\nfunction generateNextLine (state: PresenterState, level: number) {\n return `\\n${' '.repeat(state.indent * level)}`\n}\n\nfunction scalarLayout (state: PresenterState, node: ScalarNode, parent: Readonly<Node> | null, level: number,\n isKey: boolean, flowOnly: boolean): ScalarLayout {\n const shiftOfParent = level === 0 ? -1 : state.indent * (level - 1)\n const shiftOfContent = state.indent * Math.max(1, level)\n\n return {\n node,\n parent,\n level,\n isKey,\n flowOnly,\n shiftOfParent,\n shiftOfContent,\n shiftOfFirstLine: level === 0 ? 0 : state.indent * level,\n presenterOptions: state,\n allowedStylesMask: 0,\n style: node.style\n }\n}\n\nfunction writeFlowSequence (state: PresenterState, level: number, node: SequenceNode) {\n let result = ''\n\n for (let index = 0, length = node.items.length; index < length; index += 1) {\n const item = writeNode(state, level, node.items[index], node, {}).text\n if (index > 0) result += `,${!state.flowSkipCommaSpace ? ' ' : ''}`\n result += item\n }\n\n const pad = state.flowBracketPadding && node.items.length > 0 ? ' ' : ''\n return `[${pad}${result}${pad}]`\n}\n\nfunction writeBlockSequence (state: PresenterState, level: number, node: SequenceNode, compact: boolean) {\n let result = ''\n\n for (let index = 0, length = node.items.length; index < length; index += 1) {\n const item = writeNode(state, level + 1, node.items[index], node,\n { block: true, compact: state.seqInlineFirst, isblockseq: true }).text\n\n if (!compact || result !== '') {\n result += generateNextLine(state, level)\n }\n\n // No trailing space when the value renders empty (e.g. null → '').\n if (item === '' || CHAR_LINE_FEED === item.charCodeAt(0)) {\n result += '-'\n } else {\n result += '- '\n }\n\n result += item\n }\n\n return result\n}\n\nfunction writeFlowMapping (state: PresenterState, level: number, node: MappingNode) {\n let result = ''\n\n for (const { key, value } of node.items) {\n let pairBuffer = ''\n if (result !== '') pairBuffer += `,${!state.flowSkipCommaSpace ? ' ' : ''}`\n\n const keyRender = writeNode(state, level, key, node, { iskey: true })\n const keyText = keyRender.text\n\n const valueText = writeNode(state, level, value, node, {}).text\n // No separating space when the value renders empty (e.g. null → '').\n const sep = state.flowSkipColonSpace || valueText === '' ? '' : ' '\n\n // An alias or a property-only scalar must be separated from `:` because the\n // colon can otherwise be consumed as part of the alias/anchor/tag name.\n const keyIsBareProps = key.kind === 'scalar' && keyRender.noBody &&\n (key.tagged || key.anchor !== undefined)\n const keyColonSep = key.kind === 'alias' || keyIsBareProps ? ' ' : ''\n\n pairBuffer += `${keyText}${keyColonSep}:${sep}${valueText}`\n\n result += pairBuffer\n }\n\n const pad = state.flowBracketPadding && result !== '' ? ' ' : ''\n return `{${pad}${result}${pad}}`\n}\n\nfunction writeBlockMapping (state: PresenterState, level: number, node: MappingNode, compact: boolean) {\n let result = ''\n\n for (let index = 0, length = node.items.length; index < length; index += 1) {\n let pairBuffer = ''\n\n if (!compact || result !== '') {\n pairBuffer += generateNextLine(state, level)\n }\n\n const { key, value } = node.items[index]\n\n // A block key — a block collection (mapping/sequence) or a block scalar\n // (literal/folded) — can't sit on a `key:` line, so it's written with block\n // context and the pair takes the explicit `? key / : value` form. A simple\n // scalar key stays inline (flow-vs-block is invisible there).\n const keyIsBlock =\n ((key.kind === 'mapping' || key.kind === 'sequence') &&\n key.style === COLLECTION_STYLE.BLOCK && key.items.length !== 0) ||\n (key.kind === 'scalar' &&\n (key.style === SCALAR_STYLE.LITERAL_BLOCK || key.style === SCALAR_STYLE.FOLDED_BLOCK))\n\n // The `?`/`:` indicators shift content right like a `-`, so a block key or\n // value that stays on the indicator line keeps its indentation under\n // seqNoIndent (`isblockseq`). One that drops to its own line (tag/anchor)\n // collapses to the parent indent, so leave the flag off there.\n const keyRender = keyIsBlock\n ? writeNode(state, level + 1, key, node,\n { block: true, compact: true, isblockseq: !cannotBeCompact(state, key, level + 1) })\n : writeNode(state, level + 1, key, node, { block: true, compact: true, iskey: true })\n const keyText = keyRender.text\n\n // Block key, over-long key, or multiline scalar key forces explicit form.\n // Multiline isn't a spec requirement — just matches pyyaml's simple-key rule.\n const keyHasLineBreak = key.kind === 'scalar' && key.value.indexOf('\\n') !== -1\n\n // YAML limits an implicit key to 1024 Unicode code points. `length` counts\n // UTF-16 code units, never fewer than code points, so it is a cheap safe\n // precheck. The `u` regexp then counts each [\\s\\S] match as one code point.\n const keyIsTooLong = keyText.length > 1024 && /^[\\s\\S]{1025}/u.test(keyText)\n const explicitPair = keyIsBlock || keyHasLineBreak || keyIsTooLong\n\n if (explicitPair) {\n if (keyText && CHAR_LINE_FEED === keyText.charCodeAt(0)) {\n pairBuffer += '?'\n } else {\n pairBuffer += '? '\n }\n }\n\n pairBuffer += keyText\n\n if (explicitPair) {\n pairBuffer += generateNextLine(state, level)\n }\n\n const valueText = writeNode(state, level + 1, value, node,\n { block: true, compact: explicitPair, isblockseq: explicitPair && !cannotBeCompact(state, value, level + 1) }).text\n\n // Keep a space before the colon when the key text ends in a leading\n // property rather than scalar content, so the colon can't be read as part\n // of it. Two cases: an inline alias key (`*b : v`), and an empty scalar key\n // whose whole text is its anchor/tag (`&a :`, `!!str :`) — without the\n // space `&a:` reparses as an anchored value, dropping the null key.\n const keyIsBareProps = key.kind === 'scalar' && keyRender.noBody &&\n (key.tagged || key.anchor !== undefined)\n const keyColonSep = !explicitPair && (key.kind === 'alias' || keyIsBareProps) ? ' ' : ''\n\n // No trailing space when the value renders empty (e.g. null → '').\n if (valueText === '' || CHAR_LINE_FEED === valueText.charCodeAt(0)) {\n pairBuffer += `${keyColonSep}:`\n } else {\n pairBuffer += `${keyColonSep}: `\n }\n\n pairBuffer += valueText\n\n result += pairBuffer\n }\n\n return result\n}\n\n// Where a node sits relative to its parent — drives layout/style decisions.\n// All flags default to false (the flow-context, non-key, non-compact case).\ninterface NodeContext {\n block?: boolean // block context (vs flow); propagates downward\n compact?: boolean // may start on the current line (no leading newline)\n iskey?: boolean // node is a mapping key\n isblockseq?: boolean // content follows an indicator (`-`, or `?`/`:` in an\n // explicit pair) that already shifted it right; keeps\n // its indentation under seqNoIndent\n}\n\ninterface NodeRender {\n text: string\n noBody: boolean\n}\n\n// A node can't sit compact on its parent's indicator (`-`/`?`/`:`) line when it\n// carries leading props (tag/anchor) that would collide with the indicator, or\n// when the indent step is too narrow for the 2-char indicator. Such a node drops\n// to its own line; a block collection that does so also collapses its seqNoIndent\n// indentation back to the parent.\nfunction cannotBeCompact (state: PresenterState, node: Node, level: number) {\n if (node.kind === 'alias') return true\n return node.tagged || node.anchor !== undefined || (state.indent < 2 && level > 0)\n}\n\nfunction writeNode (state: PresenterState, level: number, node: Node,\n parent: Readonly<Node> | null, ctx: NodeContext): NodeRender {\n if (node.kind === 'alias') {\n state.openEnded = false\n return { text: `*${node.anchor}`, noBody: false }\n }\n\n const { block = false, iskey = false, isblockseq = false } = ctx\n let compact = ctx.compact ?? false\n\n const hasAnchor = node.anchor !== undefined\n\n if (cannotBeCompact(state, node, level)) {\n compact = false\n }\n\n let body: string\n let shouldPrintTag = node.tagged\n const useBlockCollection = block &&\n (node.kind === 'mapping' || node.kind === 'sequence') &&\n node.style === COLLECTION_STYLE.BLOCK && node.items.length !== 0\n\n if (node.kind === 'mapping') {\n if (useBlockCollection) {\n body = writeBlockMapping(state, level, node, compact)\n } else {\n body = writeFlowMapping(state, level, node)\n }\n } else if (node.kind === 'sequence') {\n if (useBlockCollection) {\n if (state.seqNoIndent && !isblockseq && level > 0) {\n body = writeBlockSequence(state, level - 1, node, compact)\n } else {\n body = writeBlockSequence(state, level, node, compact)\n }\n } else {\n body = writeFlowSequence(state, level, node)\n }\n } else {\n const layout = scalarLayout(state, node, parent, level, iskey, !block)\n\n detectAllowedStyles(layout)\n for (const rule of state.scalarStyleRules) rule(layout)\n\n body = renderScalar(layout)\n state.openEnded =\n (layout.style === SCALAR_STYLE.LITERAL_BLOCK || layout.style === SCALAR_STYLE.FOLDED_BLOCK) &&\n (node.value === '\\n' || node.value.endsWith('\\n\\n'))\n\n // A flow sequence entry cannot be completely empty. Print the semantic tag\n // as the node property that explicitly indicates the entry's existence.\n shouldPrintTag = node.tagged ||\n (body === '' && layout.flowOnly && parent?.kind === 'sequence' && !hasAnchor) ||\n (layout.style !== SCALAR_STYLE.PLAIN && node.tag !== state.defaultScalarTagName)\n }\n\n // A flow collection ends with its closing indicator, not with its last child.\n if ((node.kind === 'mapping' || node.kind === 'sequence') && !useBlockCollection) {\n state.openEnded = false\n }\n\n // An indicator plus its mandatory separator occupies 2 columns. For wider\n // indentation, pad a compact block collection so its first item starts at\n // the same column as the following items.\n if (useBlockCollection && compact && level > 0 && state.indent > 2) {\n body = `${' '.repeat(state.indent - 2)}${body}`\n }\n\n const noBody = body === ''\n let text = body\n\n if (shouldPrintTag || hasAnchor) {\n const props: string[] = []\n const tag = shouldPrintTag ? nodeTagShort(node) : null\n const anchor = hasAnchor ? `&${node.anchor}` : null\n\n if (state.tagBeforeAnchor) {\n if (tag !== null) props.push(tag)\n if (anchor !== null) props.push(anchor)\n } else {\n if (anchor !== null) props.push(anchor)\n if (tag !== null) props.push(tag)\n }\n\n // No separator when the body is empty (e.g. `&anchor` on a null node) or\n // already starts on its own line.\n const sep = body === '' || body.charCodeAt(0) === CHAR_LINE_FEED ? '' : ' '\n text = `${props.join(' ')}${sep}${body}`\n }\n\n return { text, noBody }\n}\n\n// A bare (untagged, unanchored) non-empty block collection: writeNode renders it\n// in compact form with its first item on the opening line. That works mid-stream,\n// but right after a `---` the first item must drop to the next line. A tag/anchor\n// already forces the body onto its own line, so those stay on the `---` line.\nfunction rootStartsOwnLine (node: Node) {\n return (node.kind === 'sequence' || node.kind === 'mapping') &&\n node.style === COLLECTION_STYLE.BLOCK &&\n node.items.length !== 0 &&\n !node.tagged &&\n node.anchor === undefined\n}\n\nfunction writeDocumentDirectives (doc: Document) {\n let result = ''\n\n for (const directive of doc.directives) {\n if (directive.kind === 'yaml') {\n result += `%YAML ${directive.version}\\n`\n continue\n }\n\n const { handle, prefix } = directive\n result += `%TAG ${handle} ${prefix}\\n`\n }\n\n return result\n}\n\n/**\n * Build YAML from AST.\n *\n * @category AST\n */\nfunction present (documents: Document[], options: PresenterOptions): string {\n const state = createPresenterState(options)\n let result = ''\n let previousEnded = false\n\n for (let index = 0; index < documents.length; index += 1) {\n const doc = documents[index]\n state.openEnded = false\n const directives = writeDocumentDirectives(doc)\n const hasDirectives = directives !== ''\n const marker = doc.explicitStart || hasDirectives || (index > 0 && !previousEnded)\n\n result += directives\n\n if (doc.contents === null) {\n if (marker) result += '---\\n'\n } else if (marker) {\n const body = writeNode(state, 0, doc.contents, null, { block: true, compact: true }).text\n // Content shares the `---` line, except: an empty render (no separator at\n // all), a bare block collection (wraps to the next line), or directives\n // forcing `---` onto its own line.\n const sep = body === '' ? '' : (hasDirectives || rootStartsOwnLine(doc.contents) ? '\\n' : ' ')\n result += `---${sep}${body}\\n`\n } else {\n result += writeNode(state, 0, doc.contents, null, { block: true, compact: true }).text + '\\n'\n }\n\n previousEnded = doc.explicitEnd || state.openEnded\n if (previousEnded) {\n result += '...\\n'\n }\n }\n\n return result\n}\n\nexport {\n DEFAULT_PRESENTER_OPTIONS,\n present,\n type PresenterOptions\n}\n","import { DUMP_SCHEMA, type Schema } from './schema.ts'\nimport { COLLECTION_STYLE } from './parser/events.ts'\nimport { jsToAst } from './ast/from_js.ts'\nimport { visit, VISIT_SKIP } from './ast/visit.ts'\nimport { type Document } from './ast/nodes.ts'\nimport {\n DEFAULT_PRESENTER_OPTIONS,\n present,\n type PresenterOptions\n} from './ast/presenter.ts'\nimport { pick } from './common/object.ts'\n\n/** @category Main */\ninterface DumpOptions extends Omit<PresenterOptions, 'schema'> {\n /**\n * Schema to use.\n *\n * @defaultValue {@link DUMP_SCHEMA}\n */\n schema?: Schema\n\n /**\n * Skips invalid types instead of throwing. Invalid mapping pairs and sequence\n * items are skipped; `undefined` sequence items are serialized as `null`.\n *\n * @defaultValue `false`\n */\n skipInvalid?: boolean\n\n /**\n * Inlines duplicate objects instead of converting them into references.\n *\n * @defaultValue `false`\n */\n noRefs?: boolean\n\n /**\n * Nesting level at which collections switch from block to flow style. Set to\n * `-1` to never switch automatically.\n *\n * @defaultValue `-1`\n */\n flowLevel?: number\n\n /**\n * Sorts mapping keys when `true`. A function can be provided to define the\n * sort order.\n *\n * @defaultValue `false`\n * @deprecated Use {@link transform} to reorder mapping items.\n */\n sortKeys?: boolean | ((a: any, b: any) => number)\n\n /**\n * Mutates the generated AST before it is rendered.\n *\n * @example Sort mapping keys:\n *\n * ```typescript\n * import { dump, visit } from 'js-yaml'\n *\n * dump(value, {\n * transform: documents => visit(documents, node => {\n * if (node.kind === 'mapping') node.items.sort((a, b) => {\n * const x = a.key.kind === 'scalar' ? a.key.value : ''\n * const y = b.key.kind === 'scalar' ? b.key.value : ''\n * return x.localeCompare(y)\n * })\n * })\n * })\n * ```\n */\n transform?: (documents: Document[]) => void\n}\n\nconst DEFAULT_DUMP_OPTIONS: Required<DumpOptions> = {\n ...DEFAULT_PRESENTER_OPTIONS,\n schema: DUMP_SCHEMA,\n skipInvalid: false,\n noRefs: false,\n flowLevel: -1,\n sortKeys: false,\n transform: () => {}\n}\n\nfunction defaultCompareFn (a: any, b: any) {\n const x = String(a)\n const y = String(b)\n\n if (x < y) return -1\n if (x > y) return 1\n return 0\n}\n\n/**\n * Serializes JS object as a YAML document. By default it can dump every\n * supported YAML type, so it throws an exception if you try to dump regexps or\n * functions. However, you can disable exceptions by setting the\n * {@link DumpOptions.skipInvalid} option to `true`.\n *\n * @category Main\n */\nfunction dump (input: any, options: DumpOptions = {}) {\n const opts = { ...DEFAULT_DUMP_OPTIONS, ...options }\n\n const documents = jsToAst(input, opts.schema, {\n noRefs: opts.noRefs,\n skipInvalid: opts.skipInvalid\n })\n\n // flowLevel: every node at this depth switches to flow; the presenter forces\n // everything below into flow too, so the walk stops there.\n if (opts.flowLevel >= 0) {\n visit(documents, (node, ctx) => {\n if (ctx.depth < opts.flowLevel) return\n if (node.kind === 'sequence' || node.kind === 'mapping') {\n node.style = COLLECTION_STYLE.FLOW\n }\n return VISIT_SKIP\n })\n }\n\n if (opts.sortKeys) {\n const compareFn = opts.sortKeys === true ? defaultCompareFn : opts.sortKeys\n\n visit(documents, node => {\n if (node.kind !== 'mapping') return\n\n node.items.sort((a, b) => compareFn(\n a.key.kind === 'scalar' ? a.key.value : '',\n b.key.kind === 'scalar' ? b.key.value : ''\n ))\n })\n }\n\n opts.transform(documents)\n\n const PRESENTER_OPT_KEYS = Object.keys(DEFAULT_PRESENTER_OPTIONS) as\n (keyof typeof DEFAULT_PRESENTER_OPTIONS)[]\n\n return present(documents, { ...pick(opts, PRESENTER_OPT_KEYS), schema: opts.schema })\n}\n\nexport {\n dump,\n\n type DumpOptions\n}\n","export {\n Schema,\n FAILSAFE_SCHEMA,\n JSON_SCHEMA,\n CORE_SCHEMA,\n YAML11_SCHEMA,\n DUMP_SCHEMA\n} from './schema.ts'\n\nexport {\n NOT_RESOLVED,\n defineScalarTag,\n defineSequenceTag,\n defineMappingTag,\n type ScalarTagDefinition,\n type SequenceTagDefinition,\n type MappingTagDefinition,\n type TagDefinition,\n type ScalarTagOptions,\n type SequenceTagOptions,\n type MappingTagOptions\n} from './tag.ts'\n\nexport { strTag } from './tag/scalar/str.ts'\nexport { nullCoreTag } from './tag/scalar/null_core.ts'\nexport { nullJsonTag } from './tag/scalar/null_json.ts'\nexport { nullYaml11Tag } from './tag/scalar/null_yaml11.ts'\nexport { boolCoreTag } from './tag/scalar/bool_core.ts'\nexport { boolJsonTag } from './tag/scalar/bool_json.ts'\nexport { boolYaml11Tag } from './tag/scalar/bool_yaml11.ts'\nexport { intCoreTag } from './tag/scalar/int_core.ts'\nexport { intJsonTag } from './tag/scalar/int_json.ts'\nexport { intYaml11Tag } from './tag/scalar/int_yaml11.ts'\nexport { floatCoreTag } from './tag/scalar/float_core.ts'\nexport { floatJsonTag } from './tag/scalar/float_json.ts'\nexport { floatYaml11Tag } from './tag/scalar/float_yaml11.ts'\nexport { mergeTag } from './tag/scalar/merge.ts'\nexport { binaryTag } from './tag/scalar/binary.ts'\nexport { timestampTag } from './tag/scalar/timestamp.ts'\n\nexport { seqTag } from './tag/sequence/seq.ts'\nexport { omapTag } from './tag/sequence/omap.ts'\nexport { pairsTag } from './tag/sequence/pairs.ts'\n\nexport { mapTag } from './tag/mapping/map.ts'\nexport { realMapTag } from './tag/mapping/real_map.ts'\nexport { legacyMapTag } from './tag/mapping/legacy_map.ts'\nexport { setTag } from './tag/mapping/set.ts'\n\nexport { load, loadAll, type LoadOptions } from './load.ts'\nexport { dump, type DumpOptions } from './dump.ts'\nexport { YAMLException } from './common/exception.ts'\n\nexport {\n EVENT_ID,\n SCALAR_STYLE,\n COLLECTION_STYLE,\n CHOMPING_MODE,\n type EventId,\n type ScalarStyle,\n type CollectionStyle,\n type ChompingMode,\n\n type DocumentDirective,\n type DocumentEvent,\n type SequenceEvent,\n type MappingEvent,\n type ScalarEvent,\n type AliasEvent,\n type PopEvent,\n type Event\n} from './parser/events.ts'\n\nexport {\n parseEvents,\n type ParserOptions\n} from './parser/parser.ts'\n\nexport { getScalarValue } from './parser/parser_scalar.ts'\n\nexport {\n constructFromEvents,\n type ConstructorOptions\n} from './parser/constructor.ts'\n\nexport { eventsToAst, type FromEventsOptions } from './ast/from_events.ts'\nexport { jsToAst, type FromJsOptions } from './ast/from_js.ts'\nexport { present, type PresenterOptions } from './ast/presenter.ts'\nexport { type ScalarLayout, type ScalarStyleRule } from './ast/scalar_styler.ts'\nexport { DEFAULT_SCALAR_STYLE_RULES } from './ast/styler_defaults.ts'\n\nexport {\n visit,\n VISIT_BREAK,\n VISIT_SKIP,\n type Visitor,\n type VisitContext\n} from './ast/visit.ts'\n\nexport {\n type Node,\n type Document,\n type NodeBase,\n type ScalarNode,\n type SequenceNode,\n type MappingNode,\n type AliasNode\n} from './ast/nodes.ts'\n\n// Deprecated compatibility exports\n\nimport { EVENT_ID, SCALAR_STYLE, COLLECTION_STYLE, CHOMPING_MODE } from './parser/events.ts'\n\n/** @deprecated Use `EVENT_ID.DOCUMENT` instead. @internal */\nexport const EVENT_DOCUMENT = EVENT_ID.DOCUMENT\n/** @deprecated Use `EVENT_ID.SEQUENCE` instead. @internal */\nexport const EVENT_SEQUENCE = EVENT_ID.SEQUENCE\n/** @deprecated Use `EVENT_ID.MAPPING` instead. @internal */\nexport const EVENT_MAPPING = EVENT_ID.MAPPING\n/** @deprecated Use `EVENT_ID.SCALAR` instead. @internal */\nexport const EVENT_SCALAR = EVENT_ID.SCALAR\n/** @deprecated Use `EVENT_ID.ALIAS` instead. @internal */\nexport const EVENT_ALIAS = EVENT_ID.ALIAS\n/** @deprecated Use `EVENT_ID.POP` instead. @internal */\nexport const EVENT_POP = EVENT_ID.POP\n/** @deprecated Use `SCALAR_STYLE.PLAIN` instead. @internal */\nexport const SCALAR_STYLE_PLAIN = SCALAR_STYLE.PLAIN\n/** @deprecated Use `SCALAR_STYLE.SINGLE_QUOTED` instead. @internal */\nexport const SCALAR_STYLE_SINGLE_QUOTED = SCALAR_STYLE.SINGLE_QUOTED\n/** @deprecated Use `SCALAR_STYLE.DOUBLE_QUOTED` instead. @internal */\nexport const SCALAR_STYLE_DOUBLE_QUOTED = SCALAR_STYLE.DOUBLE_QUOTED\n/** @deprecated Use `SCALAR_STYLE.LITERAL_BLOCK` instead. @internal */\nexport const SCALAR_STYLE_LITERAL_BLOCK = SCALAR_STYLE.LITERAL_BLOCK\n/** @deprecated Use `SCALAR_STYLE.FOLDED_BLOCK` instead. @internal */\nexport const SCALAR_STYLE_FOLDED_BLOCK = SCALAR_STYLE.FOLDED_BLOCK\n/** @deprecated Use `COLLECTION_STYLE.BLOCK` instead. @internal */\nexport const COLLECTION_STYLE_BLOCK = COLLECTION_STYLE.BLOCK\n/** @deprecated Use `COLLECTION_STYLE.FLOW` instead. @internal */\nexport const COLLECTION_STYLE_FLOW = COLLECTION_STYLE.FLOW\n/** @deprecated Use `CHOMPING_MODE.CLIP` instead. @internal */\nexport const CHOMPING_CLIP = CHOMPING_MODE.CLIP\n/** @deprecated Use `CHOMPING_MODE.STRIP` instead. @internal */\nexport const CHOMPING_STRIP = CHOMPING_MODE.STRIP\n/** @deprecated Use `CHOMPING_MODE.KEEP` instead. @internal */\nexport const CHOMPING_KEEP = CHOMPING_MODE.KEEP\n","/**\n * Bidirectional mapping between the platform's `outputs` array and the local\n * YAML `response_format` block; push and pull must be exact inverses. Flat\n * fields round-trip losslessly; a rich schema becomes one `json_schema` output.\n */\n\nexport type CliOutputType = \"str\" | \"float\" | \"bool\" | \"json_schema\";\n\nexport interface CliOutput {\n identifier: string;\n type: CliOutputType;\n json_schema?: Record<string, unknown>;\n}\n\nexport interface LocalResponseFormat {\n name?: string;\n schema: Record<string, unknown>;\n}\n\n/**\n * The CLI default output for an unstructured (plain text) prompt. A prompt\n * with exactly this single output has no structured response and therefore no\n * `response_format` block.\n */\nexport const DEFAULT_TEXT_OUTPUT: CliOutput = {\n identifier: \"output\",\n type: \"str\",\n};\n\nconst SCALAR_OUTPUT_TO_JSON_TYPE: Record<Exclude<CliOutputType, \"json_schema\">, string> = {\n str: \"string\",\n float: \"number\",\n bool: \"boolean\",\n};\n\nconst JSON_TYPE_TO_SCALAR_OUTPUT: Record<string, Exclude<CliOutputType, \"json_schema\">> = {\n string: \"str\",\n number: \"float\",\n integer: \"float\",\n boolean: \"bool\",\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/**\n * Accepts either the canonical local form `{ name?, schema }` or the\n * OpenAI-standard wrapper, normalizing to `{ name?, schema }`. Returns\n * undefined when there is no usable schema.\n */\nexport const normalizeResponseFormat = (raw: unknown): LocalResponseFormat | undefined => {\n if (!isPlainObject(raw)) return undefined;\n\n // OpenAI-standard wrapper\n if (raw.type === \"json_schema\" && isPlainObject(raw.json_schema)) {\n const inner = raw.json_schema;\n if (isPlainObject(inner.schema)) {\n return {\n name: typeof inner.name === \"string\" ? inner.name : undefined,\n schema: inner.schema,\n };\n }\n return undefined;\n }\n\n // Canonical local form\n if (isPlainObject(raw.schema)) {\n return {\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n schema: raw.schema,\n };\n }\n\n return undefined;\n};\n\n/**\n * A \"simple flat object schema\" has only scalar properties, no extra\n * JSON-schema keywords, and (if present) `required` listing exactly the\n * properties -- these map losslessly to flat platform fields.\n */\nexport const asFlatFields = (\n schema: Record<string, unknown>,\n): { identifier: string; type: Exclude<CliOutputType, \"json_schema\"> }[] | null => {\n if (schema.type !== \"object\") return null;\n if (!isPlainObject(schema.properties)) return null;\n\n const allowedSchemaKeys = new Set([\"type\", \"properties\", \"required\", \"additionalProperties\"]);\n for (const k of Object.keys(schema)) {\n if (!allowedSchemaKeys.has(k)) return null;\n }\n\n if (schema.additionalProperties !== undefined && schema.additionalProperties !== false) {\n return null;\n }\n\n const properties = schema.properties;\n const propertyNames = Object.keys(properties);\n if (propertyNames.length === 0) return null;\n\n const fields = scalarPropertyFields(properties, propertyNames);\n if (!fields) {\n return null;\n }\n\n if (schema.required !== undefined) {\n if (!Array.isArray(schema.required)) return null;\n const required = [...(schema.required as unknown[])].toSorted((a, b) =>\n String(a).localeCompare(String(b)),\n );\n const names = [...propertyNames].toSorted();\n if (required.length !== names.length || required.some((r, i) => r !== names[i])) {\n return null;\n }\n }\n\n return fields;\n};\n\n/**\n * Pull direction: reconstruct the local `response_format` block from the\n * platform `outputs` array. Returns undefined for a plain-text prompt so we\n * never invent a schema that wasn't there.\n */\nexport const outputsToResponseFormat = (\n outputs: CliOutput[] | undefined | null,\n fallbackName?: string,\n): LocalResponseFormat | undefined => {\n if (!outputs || outputs.length === 0) return undefined;\n\n const jsonSchemaOutput = outputs.find((o) => o.type === \"json_schema\" && o.json_schema);\n if (jsonSchemaOutput?.json_schema) {\n return {\n name: jsonSchemaOutput.identifier,\n schema: jsonSchemaOutput.json_schema,\n };\n }\n\n // Exactly the CLI default single text output → unstructured, no schema.\n if (\n outputs.length === 1 &&\n outputs[0]!.identifier === DEFAULT_TEXT_OUTPUT.identifier &&\n outputs[0]!.type === DEFAULT_TEXT_OUTPUT.type\n ) {\n return undefined;\n }\n\n // Flat structured fields → a single-level JSON schema object.\n const properties: Record<string, { type: string }> = {};\n for (const output of outputs) {\n if (output.type === \"json_schema\") continue;\n properties[output.identifier] = {\n type: SCALAR_OUTPUT_TO_JSON_TYPE[output.type],\n };\n }\n if (Object.keys(properties).length === 0) return undefined;\n\n return {\n name: fallbackName ?? \"output\",\n schema: {\n type: \"object\",\n properties,\n required: Object.keys(properties),\n additionalProperties: false,\n },\n };\n};\n\n/**\n * Push direction: turns the local `response_format` block back into the\n * platform `outputs` array, the exact inverse of {@link outputsToResponseFormat}.\n * A flat schema expands to flat fields; anything richer becomes one json_schema output.\n */\nexport const responseFormatToOutputs = (raw: unknown): CliOutput[] | undefined => {\n const rf = normalizeResponseFormat(raw);\n if (!rf) return undefined;\n\n const flat = asFlatFields(rf.schema);\n if (flat) {\n return flat.map((f) => ({ identifier: f.identifier, type: f.type }));\n }\n\n return [\n {\n identifier: rf.name ?? \"output\",\n type: \"json_schema\",\n json_schema: rf.schema,\n },\n ];\n};\n\nfunction scalarPropertyFields(\n properties: Record<string, unknown>,\n propertyNames: string[],\n): { identifier: string; type: Exclude<CliOutputType, \"json_schema\"> }[] | null {\n const fields: {\n identifier: string;\n type: Exclude<CliOutputType, \"json_schema\">;\n }[] = [];\n\n for (const name of propertyNames) {\n const prop = properties[name];\n if (!isPlainObject(prop)) return null;\n for (const k of Object.keys(prop)) {\n if (k !== \"type\") return null;\n }\n const jsonType = prop.type;\n if (typeof jsonType !== \"string\") return null;\n const scalar = JSON_TYPE_TO_SCALAR_OUTPUT[jsonType];\n if (!scalar) return null;\n fields.push({ identifier: name, type: scalar });\n }\n\n return fields;\n}\n","import { type PromptResponse, type UpdatePromptBody } from \"@/client-sdk/services/prompts/types\";\n\nimport type { LocalPromptConfig, MaterializedPrompt, RuntimeParameters } from \"../types\";\nimport {\n type CliOutput,\n type LocalResponseFormat,\n outputsToResponseFormat,\n} from \"./responseFormat\";\n\ntype PromptYamlContent = {\n model: string;\n modelParameters?: {\n temperature?: number;\n maxTokens?: number;\n };\n messages: {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n }[];\n response_format?: LocalResponseFormat;\n parameters?: RuntimeParameters;\n};\n\n/**\n * Converts between the YAML `.prompt.yaml` format (GitHub's standard) and\n * this service's internal API schema, keeping the two evolvable independently.\n */\nexport class PromptConverter {\n /**\n * Converts a Prompt instance from the API service to the MaterializedPrompt format\n * used for saving to the .materialized directory.\n */\n static fromApiToMaterialized(prompt: PromptResponse): MaterializedPrompt {\n return {\n id: prompt.id,\n name: prompt.name,\n version: prompt.version,\n versionId: prompt.versionId,\n model: prompt.model,\n messages: prompt.messages,\n prompt: prompt.prompt,\n temperature: prompt.temperature,\n maxTokens: prompt.maxTokens,\n inputs: prompt.inputs,\n outputs: prompt.outputs,\n parameters: prompt.parameters ?? {},\n updatedAt: prompt.updatedAt,\n };\n }\n\n /**\n * Converts a MaterializedPrompt to the YAML content structure\n * for saving to .prompt.yaml files.\n */\n static fromMaterializedToYaml(prompt: MaterializedPrompt): PromptYamlContent {\n const result: PromptYamlContent = {\n model: prompt.model,\n messages: prompt.messages,\n };\n\n if (Object.keys(prompt.parameters ?? {}).length > 0) {\n result.parameters = prompt.parameters;\n }\n\n // Add modelParameters if temperature or maxTokens exist. The server is the\n // source of truth for which sampling parameters a model accepts and omits\n // the ones it would reject (e.g. temperature on the gpt-5 family), so we\n // simply mirror what the API returned — never inject a default here.\n if (prompt.temperature !== undefined || prompt.maxTokens !== undefined) {\n result.modelParameters = {};\n if (prompt.temperature !== undefined) {\n result.modelParameters.temperature = prompt.temperature;\n }\n if (prompt.maxTokens !== undefined) {\n result.modelParameters.maxTokens = prompt.maxTokens;\n }\n }\n\n // Reconstruct the structured-output block from the platform outputs so a\n // pull never drops a response_format the user configured. Inverse of the\n // push direction in pushPrompts.\n const responseFormat = outputsToResponseFormat(\n prompt.outputs as CliOutput[] | undefined,\n prompt.name,\n );\n if (responseFormat) {\n result.response_format = responseFormat;\n }\n\n return result;\n }\n\n /**\n * Converts a LocalPromptConfig (loaded from YAML) to the format\n * expected by the API service for upserting.\n */\n static fromLocalToApiFormat(\n config: LocalPromptConfig,\n ): Omit<UpdatePromptBody, \"commitMessage\"> & { parameters?: RuntimeParameters } {\n return {\n model: config.model,\n temperature: config.modelParameters?.temperature,\n maxTokens: config.modelParameters?.max_tokens,\n messages: config.messages,\n parameters: config.parameters ?? {},\n };\n }\n\n /**\n * Extracts the system prompt from messages array.\n * Used when converting to API format that separates system prompt from messages.\n */\n static extractSystemPrompt(messages: { role: string; content: string }[]): string {\n return messages.find((m) => m.role === \"system\")?.content ?? \"\";\n }\n\n /**\n * Filters out system messages from the messages array.\n * Used when converting to API format that handles system prompt separately.\n */\n static filterNonSystemMessages(\n messages: {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n }[],\n ): { role: \"user\" | \"assistant\"; content: string }[] {\n return messages.filter((m) => m.role !== \"system\") as {\n role: \"user\" | \"assistant\";\n content: string;\n }[];\n }\n}\n","/**\n * A sentence for a failure the API named but wrote no prose for: the REST\n * envelope sends `message` equal to `code`, so this renders copy keyed on\n * `code` -- `SENTENCE_BY_CODE` overrides, else the humanized slug.\n */\n\nconst SENTENCE_BY_CODE: Record<string, string> = {\n enterprise_plan_required: \"This capability needs the Enterprise plan\",\n insufficient_permissions: \"This credential does not carry the permission this call needs\",\n missing_credentials: \"This request carried no API key\",\n invalid_credentials: \"That API key is not valid\",\n project_required:\n \"This API key reaches more than one project, so the request has to name one (CLI: --project <id|slug>)\",\n organization_not_found: \"That API key does not resolve to an organization on this instance\",\n validation_error: \"The request did not pass validation\",\n custom_role_in_use: \"That role is still held by a member or a role binding\",\n custom_role_id_required: \"A CUSTOM role binding has to name which custom role it grants\",\n custom_role_not_assignable: \"That custom role cannot be granted here\",\n custom_role_name_reserved: \"That role name is reserved\",\n custom_role_name_taken: \"A role with that name already exists here\",\n role_binding_principal_invalid:\n \"A role binding names exactly one principal: a user, a group, or an API key\",\n role_binding_already_exists: \"That principal already holds that role at that scope\",\n org_exclusive_permission_scope:\n \"That role carries an organization-only permission, so it cannot be bound to a team or a project\",\n member_seat_limit_reached: \"The plan's member seats are all in use\",\n personal_workspace_not_managed_here: \"Personal workspaces are not managed through this API\",\n already_organization_member: \"They are already in this organization\",\n user_not_in_organization: \"That user is not a member of this organization\",\n group_not_in_organization: \"That group does not belong to this organization\",\n api_key_not_in_organization: \"That API key does not belong to this organization\",\n team_not_in_organization: \"That team does not belong to this organization\",\n scope_not_in_organization: \"That scope does not belong to this organization\",\n // Humanizing turns an acronym into a word: \"Api key not found\", \"Scim token\n // not found\". Spelled out here instead.\n api_key_not_found: \"API key not found\",\n api_key_not_owned: \"That API key belongs to somebody else\",\n api_key_already_revoked: \"That API key is already revoked\",\n scim_token_not_found: \"SCIM token not found\",\n duplicate_invite: \"They already have a pending invite\",\n cannot_remove_self: \"A credential cannot remove its own member\",\n cannot_disable_self: \"A credential cannot disable its own member\",\n cannot_remove_last_admin: \"That is the organization's last administrator, so it has to keep them\",\n api_key_scope_violation: \"That API key cannot be given that access\",\n};\n\n/** `organization_slug_taken` -> `Organization slug taken`. */\nconst humanizeCode = (code: string): string => {\n const words = code.replace(/[_-]+/g, \" \").trim();\n if (!words) return code;\n return words.charAt(0).toUpperCase() + words.slice(1);\n};\n\n/**\n * True when the body's `message` says nothing the `code` did not: the envelope\n * forwarded the code, so there is no server prose to prefer over our copy.\n */\nexport const isCodeAsMessage = ({ code, message }: { code: unknown; message: unknown }): boolean =>\n typeof code === \"string\" &&\n code.length > 0 &&\n typeof message === \"string\" &&\n message.trim() === code;\n\n/** The sentence to show for a code the API named. */\nexport const sentenceForCode = (code: string): string =>\n SENTENCE_BY_CODE[code] ?? humanizeCode(code);\n\n/**\n * True for a value shaped like one of our error codes: lower case, words\n * joined by underscores. Separates `invalid_credentials` from reason\n * phrases and free text sharing the same envelope fields.\n */\nexport const looksLikeErrorCode = (value: unknown): value is string =>\n typeof value === \"string\" && /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/.test(value);\n","/**\n * Extracts the most informative, user-facing message from an API error\n * body: tries `body.message`, `body.error`, other string fields, then\n * falls back to stringifying the body or the Error's own message.\n */\nimport { isCodeAsMessage, looksLikeErrorCode, sentenceForCode } from \"./error-code-copy\";\n\nconst GENERIC_MESSAGES = new Set([\n \"\",\n // The bare class name some envelopes carry in their `error` field; as a\n // prefix it can only turn \"enterprise_plan_required\" into\n // \"Error: enterprise_plan_required\".\n \"error\",\n \"internal server error\",\n \"unknown error\",\n \"unknown error occurred\",\n]);\n\nfunction isGeneric(s: string): boolean {\n return GENERIC_MESSAGES.has(s.trim().toLowerCase());\n}\n\nfunction firstMeaningful(...candidates: unknown[]): string | undefined {\n for (const c of candidates) {\n if (typeof c === \"string\" && !isGeneric(c)) return c;\n }\n return undefined;\n}\n\nfunction collectAllOwnPropertyNames(value: unknown, seen = new Set<unknown>()): string[] {\n if (!value || typeof value !== \"object\" || seen.has(value)) return [];\n seen.add(value);\n const names = new Set<string>();\n for (const name of Object.getOwnPropertyNames(value)) {\n names.add(name);\n try {\n const child = (value as Record<string, unknown>)[name];\n for (const nested of collectAllOwnPropertyNames(child, seen)) {\n names.add(nested);\n }\n } catch {\n // Ignore getter side effects.\n void 0;\n }\n }\n return Array.from(names);\n}\n\ninterface ZodIssue {\n path?: unknown;\n message?: unknown;\n}\n\n/**\n * Renders a Zod validation error body into a user-readable string, e.g.\n * `\"Validation failed: format — Invalid enum value...\"` for one issue, joined by `;` for more.\n * Returns undefined if `body` is not a Zod error shape.\n */\nfunction formatZodIssues(body: Record<string, unknown>): string | undefined {\n const isZod = body.name === \"ZodError\" || (Array.isArray(body.issues) && body.issues.length > 0);\n if (!isZod || !Array.isArray(body.issues)) return undefined;\n\n const rendered = (body.issues as ZodIssue[])\n .map(formatZodIssue)\n .filter((s): s is string => typeof s === \"string\" && s.length > 0);\n\n if (rendered.length === 0) return undefined;\n return `Validation failed: ${rendered.join(\"; \")}`;\n}\n\nfunction stringifyBody(body: unknown): string {\n try {\n // Use all own property names (including non-enumerable ones, common on\n // native Error instances and fetch errors) so we preserve every field the\n // server sent, at any nesting depth.\n if (body && typeof body === \"object\") {\n return JSON.stringify(body, collectAllOwnPropertyNames(body));\n }\n return JSON.stringify(body);\n } catch {\n return String(body);\n }\n}\n\nexport interface FormatApiErrorOptions {\n /**\n * HTTP status code from the response, when known. Used as part of the\n * fallback output so that the user has at least some actionable signal.\n */\n status?: number;\n}\n\nexport interface FormatApiErrorMessageParams {\n error: unknown;\n /** Currently only `status` — kept as an object for forward extension. */\n options?: FormatApiErrorOptions;\n}\n\nexport function formatApiErrorMessage({\n error,\n options = {},\n}: FormatApiErrorMessageParams): string {\n if (error == null) {\n return options.status\n ? `Request failed with status ${options.status}`\n : \"Unknown error occurred\";\n }\n\n if (typeof error === \"string\") {\n return isGeneric(error) && options.status ? `${error} (status ${options.status})` : error;\n }\n\n if (error instanceof Error) {\n return formatNativeError(error);\n }\n\n if (typeof error === \"object\") {\n const body = error as Record<string, unknown>;\n\n return formatErrorBody(body, options);\n }\n\n // Primitive types (number, boolean, bigint, symbol) — coerce safely.\n // We've already handled string, null/undefined, Error, and object above.\n try {\n return String(error as number | boolean | bigint);\n } catch {\n return \"Unknown error occurred\";\n }\n}\n\nexport interface FormatApiErrorForOperationParams {\n operation: string;\n error: unknown;\n options?: FormatApiErrorOptions;\n}\n\n/**\n * Builds a fully-qualified error message for a specific operation, including\n * the operation name and the extracted server-side message.\n */\nexport function formatApiErrorForOperation({\n operation,\n error,\n options = {},\n}: FormatApiErrorForOperationParams): string {\n const message = formatApiErrorMessage({ error, options });\n return `Failed to ${operation}: ${message}`;\n}\n\n/**\n * Attempts to read a status code from common response-shaped wrappers without\n * assuming a particular SDK. Returns undefined if none is found.\n */\nexport function extractStatusFromResponse(value: unknown): number | undefined {\n if (!value || typeof value !== \"object\") return undefined;\n const obj = value as Record<string, unknown>;\n if (typeof obj.status === \"number\") return obj.status;\n if (typeof obj.statusCode === \"number\") return obj.statusCode;\n if (obj.response && typeof obj.response === \"object\") {\n const resp = obj.response as Record<string, unknown>;\n if (typeof resp.status === \"number\") return resp.status;\n }\n return undefined;\n}\n\nfunction formatZodIssue(issue: ZodIssue): string | undefined {\n const pathArr = Array.isArray(issue.path) ? issue.path : [];\n const path = pathArr.filter((p) => typeof p === \"string\" || typeof p === \"number\").join(\".\");\n const msg = typeof issue.message === \"string\" ? issue.message : \"\";\n if (path && msg) return `${path} — ${msg}`;\n if (msg) return msg;\n if (path) return path;\n return undefined;\n}\n\nfunction formatNativeError(error: Error): string {\n // Node's fetch wraps transport failures as `TypeError: fetch failed` with\n // the real reason (ECONNREFUSED, ENOTFOUND, timeout, etc.) on `.cause`.\n // Surface that so the user can tell whether the endpoint is wrong, the\n // server is down, or DNS can't resolve the host.\n const base = error.message || \"Unknown error occurred\";\n const cause = \"cause\" in error ? error.cause : void 0;\n const causeMsg = readCauseMessage(cause);\n const causeCode = readCauseCode(cause);\n\n const detail = [causeCode, causeMsg && causeMsg !== base ? causeMsg : undefined]\n .filter((s): s is string => typeof s === \"string\" && s.length > 0)\n .join(\": \");\n const formatted = detail ? `${base} (${detail})` : base;\n\n // Node fetch emits \"TypeError: fetch failed\" with `cause.message =\n // \"unknown scheme\"` when the URL has no/invalid scheme (e.g. the user\n // set LANGWATCH_ENDPOINT=localhost:5570 instead of http://localhost:5570).\n // Add a hint — the raw phrase tells the user nothing actionable.\n const combined = `${base} ${causeMsg ?? \"\"} ${causeCode ?? \"\"}`.toLowerCase();\n if (combined.includes(\"unknown scheme\")) {\n return `${formatted} — check your LANGWATCH_ENDPOINT (must start with http:// or https://)`;\n }\n if (combined.includes(\"err_invalid_url\")) {\n return `${formatted} — check your LANGWATCH_ENDPOINT (must start with http:// or https://)`;\n }\n if (combined.includes(\"failed to parse url\")) {\n return `${formatted} — check your LANGWATCH_ENDPOINT (must start with http:// or https://)`;\n }\n return formatted;\n}\n\nfunction formatErrorBody(body: Record<string, unknown>, options: FormatApiErrorOptions): string {\n // Zod validation errors: `{ name: \"ZodError\", issues: [{ path, message }] }`.\n // Without this they render as unreadable raw JSON to the user.\n const zod = formatZodIssues(body);\n if (zod) return zod;\n\n // Most specific fields first.\n const fromMessage = typeof body.message === \"string\" ? body.message : undefined;\n const fromError = typeof body.error === \"string\" ? body.error : undefined;\n\n // Two envelopes name the failure in different fields: the framework one\n // puts the code in `code`, the older one puts it in `error`. Either way the\n // code is an identifier, not a sentence — it belongs in the detail lines\n // the caller prints under the message, never in front of the prose.\n const codeField = errorCodeField(body.code, fromError);\n\n if (codeField) {\n return formatCodedMessage(codeField, fromMessage);\n }\n\n const meaningful = formatTopLevelMessage(body, fromMessage, fromError);\n if (meaningful) {\n return meaningful;\n }\n\n // 2. Nested `body.error` — only used as a fallback when no top-level\n // field carried a useful message. Native Error instances and\n // tRPC-style envelopes both fit here.\n if (body.error && typeof body.error === \"object\") {\n const nestedMessage = formatNestedError(body.error);\n if (nestedMessage) {\n return nestedMessage;\n }\n }\n\n return formatRawBody(body, options);\n}\n\nfunction formatNestedError(error: object): string | undefined {\n const nested = error as Record<string, unknown>;\n\n if (error instanceof Error && error.message) {\n return error.message;\n }\n\n // Zod validation envelopes: `{ success: false, error: { name: \"ZodError\", issues: [...] } }`\n const nestedZod = formatZodIssues(nested);\n if (nestedZod) return nestedZod;\n\n const fromNestedMsg = typeof nested.message === \"string\" ? nested.message : undefined;\n const fromNestedErr = typeof nested.error === \"string\" ? nested.error : undefined;\n const nestedMeaningful = firstMeaningful(fromNestedMsg, fromNestedErr);\n if (nestedMeaningful) {\n return nestedMeaningful;\n }\n\n // Stringify the nested object so identifiers like `{ code, status }`\n // still reach the user.\n const nestedRaw = stringifyBody(nested);\n if (nestedRaw && nestedRaw !== \"{}\") {\n return nestedRaw;\n }\n return void 0;\n}\n\nfunction formatRawBody(body: Record<string, unknown>, options: FormatApiErrorOptions): string {\n // 3. No meaningful top-level or nested fields — dump the raw JSON so\n // the user at least sees the server payload. Attach status for\n // context.\n const raw = stringifyBody(body);\n\n // Collapse empty / near-empty payloads to a friendlier message — there's\n // nothing for the user to see in `{}` anyway.\n if (!raw) {\n return options.status\n ? `Request failed with status ${options.status}`\n : \"Unknown error occurred\";\n }\n if (raw === \"{}\") {\n return options.status\n ? `Request failed with status ${options.status}`\n : \"Unknown error occurred\";\n }\n if (raw === '\"\"') {\n return options.status\n ? `Request failed with status ${options.status}`\n : \"Unknown error occurred\";\n }\n if (raw === \"null\") {\n return options.status\n ? `Request failed with status ${options.status}`\n : \"Unknown error occurred\";\n }\n\n const withStatus = options.status ? `status ${options.status} ${raw}` : raw;\n return `server returned ${withStatus}`;\n}\n\nfunction readCauseMessage(cause: unknown): string | undefined {\n if (cause instanceof Error) {\n return cause.message;\n }\n if (!cause || typeof cause !== \"object\") {\n return void 0;\n }\n if (\"message\" in cause && typeof cause.message === \"string\") {\n return cause.message;\n }\n return void 0;\n}\n\nfunction readCauseCode(cause: unknown): string | undefined {\n if (!cause || typeof cause !== \"object\") {\n return void 0;\n }\n if (\"code\" in cause && typeof cause.code === \"string\") {\n return cause.code;\n }\n return void 0;\n}\n\nfunction errorCodeField(code: unknown, error: string | undefined): string | undefined {\n if (looksLikeErrorCode(code)) {\n return code;\n }\n return looksLikeErrorCode(error) ? error : void 0;\n}\n\nfunction formatCodedMessage(codeField: string, fromMessage: string | undefined): string {\n // The framework envelope forwards the code as the message rather than\n // the server's own prose (see `error-code-copy.ts`), so `message` there\n // says nothing `code` did not. Our own sentence for the code is the\n // difference between \"This capability needs the Enterprise plan\" and the\n // slug itself. When the server did write prose, it wins.\n if (!isCodeAsMessage({ code: codeField, message: fromMessage })) {\n const meaningfulMessage = firstMeaningful(fromMessage);\n if (meaningfulMessage) {\n return meaningfulMessage;\n }\n }\n return sentenceForCode(codeField);\n}\n\nfunction formatTopLevelMessage(\n body: Record<string, unknown>,\n fromMessage: string | undefined,\n fromError: string | undefined,\n): string | undefined {\n const fromDetail = typeof body.detail === \"string\" ? body.detail : undefined;\n const fromReason = typeof body.reason === \"string\" ? body.reason : undefined;\n\n // 1. Top-level meaningful fields take priority. If the server gave us a\n // descriptive `message`/`error`/`detail`/`reason`, use that — even if\n // `body.error` is an object with its own (potentially generic) message.\n const meaningful = firstMeaningful(fromMessage, fromError, fromDetail, fromReason);\n if (meaningful) {\n if (fromError && fromMessage && fromMessage !== fromError) {\n if (!isGeneric(fromError) && !isGeneric(fromMessage)) {\n return `${fromError}: ${fromMessage}`;\n }\n }\n return meaningful;\n }\n\n return void 0;\n}\n","import { z } from \"zod\";\n\n// zod's own JSON schema: the same lazy union, already typed, so no recursive annotation to check.\nconst jsonValueSchema = z.json();\n\n// Kept OUT of `types.ts` on purpose: `types.ts` is imported by `program.ts`,\n// which is on the always-loaded cold-start path of every CLI invocation, and\n// zod costs ~39ms to load. Only the prompt commands need this schema, and\n// they are lazy-loaded behind dynamic imports, so the cost is paid only when\n// a prompt command actually runs.\nexport const localPromptConfigSchema = z\n .object({\n model: z.string().min(1, \"Model is required\"),\n modelParameters: z\n .object({\n temperature: z.number().optional(),\n max_tokens: z.number().optional(),\n })\n .loose()\n .optional(),\n messages: z\n .array(\n z\n .object({\n role: z.enum([\"system\", \"user\", \"assistant\"]),\n content: z.string().min(1, \"Message content cannot be empty\"),\n })\n .loose(),\n )\n .min(1, \"At least one message is required\"),\n // Parameters persist as JSON: parse them AS json so the value's type\n // matches the API contract (the spec's recursive JsonValue) instead of\n // laundering unknowns into a typed pipeline.\n parameters: z.record(z.string(), jsonValueSchema).optional().default({}),\n })\n .loose();\n\nexport type LocalPromptConfig = z.infer<typeof localPromptConfigSchema>;\n","import type { LocalPromptConfig, PromptDependency } from \"@/cli/types\";\nimport { PromptFileNotFoundError } from \"@/cli/utils/errors/prompt-not-found.error\";\nimport { FileManager } from \"@/cli/utils/fileManager\";\nimport { type Logger, NoOpLogger } from \"@/logger\";\n\nimport { type PromptData } from \"./types\";\n\nexport interface LocalPromptsServiceConfig {\n fileManager?: typeof FileManager;\n logger?: Logger;\n}\n\n/**\n * Retrieves prompts from local filesystem sources, in priority order:\n * explicit prompts.json mapping, materialized prompts-lock.json path,\n * then direct scanning of the prompts directory.\n */\nexport class LocalPromptsService {\n private readonly fileManager: typeof FileManager;\n private readonly logger: Logger;\n\n constructor(config?: LocalPromptsServiceConfig) {\n this.fileManager = config?.fileManager ?? FileManager;\n this.logger = config?.logger ?? new NoOpLogger();\n }\n\n /**\n * Retrieves a prompt using the configured search strategy.\n * Tries each source in priority order until found or all sources exhausted.\n */\n async get(handleOrId: string): Promise<PromptData | null> {\n try {\n const dependency = await this.getDependencyFromConfig(handleOrId);\n\n // If no dependency is found, it means it's not a local prompt\n if (!dependency) {\n return null;\n }\n\n // Try each source in priority order until found or all sources exhausted\n // We catch errors and return null if any of the sources fail so we\n // can continue to the next source and return null if all sources fail\n const localPromptConfig =\n (await this.getFromConfig(dependency).catch((e) => {\n if (e instanceof PromptFileNotFoundError) return null;\n throw e;\n })) ??\n (await this.getFromLockFile(handleOrId).catch((e) => {\n if (e instanceof PromptFileNotFoundError) return null;\n throw e;\n })) ??\n (await this.getFromLocalFiles(handleOrId).catch((e) => {\n if (e instanceof PromptFileNotFoundError) return null;\n throw e;\n }));\n\n return localPromptConfig\n ? this.convertToPromptData({\n ...localPromptConfig,\n handle: handleOrId,\n })\n : null;\n } catch (error) {\n this.logger.warn(\n `Failed to get prompt \"${handleOrId}\": ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n }\n\n /**\n * Searches for prompt using explicit file mapping in prompts.json.\n * Looks for dependencies with a 'file' property pointing to a specific path.\n */\n private async getFromConfig(dependency: PromptDependency): Promise<LocalPromptConfig | null> {\n if (typeof dependency === \"string\" && dependency.startsWith(\"file:\")) {\n return this.fileManager.loadLocalPrompt(dependency.slice(5));\n }\n\n return null;\n }\n\n /**\n * Searches for prompt using materialized path from lock file.\n * Lock file contains resolved paths for prompts that have been synced/materialized.\n */\n private async getFromLockFile(handleOrId: string): Promise<LocalPromptConfig | null> {\n const lock = this.fileManager.loadPromptsLock();\n const lockEntry = lock.prompts[handleOrId];\n\n if (lockEntry?.materialized) {\n return this.fileManager.loadLocalPrompt(lockEntry.materialized);\n }\n\n return null;\n }\n\n /**\n * Searches for prompt by scanning all .prompt.yaml files in prompts directory.\n * Extracts prompt name from file path and matches against the requested handle.\n * This is the fallback method when explicit mappings don't exist.\n */\n private async getFromLocalFiles(handleOrId: string): Promise<LocalPromptConfig | null> {\n const localFiles = this.fileManager.getLocalPromptFiles();\n\n for (const filePath of localFiles) {\n const promptName = this.fileManager.promptNameFromPath(filePath);\n if (promptName === handleOrId) {\n return this.fileManager.loadLocalPrompt(filePath);\n }\n }\n\n return null;\n }\n\n /**\n * Get dependency from config\n */\n private async getDependencyFromConfig(handleOrId: string): Promise<PromptDependency | null> {\n const config = this.fileManager.loadPromptsConfig();\n const dependency = config.prompts[handleOrId];\n\n return dependency ?? null;\n }\n\n /**\n * Converts LocalPromptConfig to PromptData format\n */\n private convertToPromptData(config: LocalPromptConfig & { handle: string }): PromptData {\n const { modelParameters, ...rest } = config;\n return {\n maxTokens: modelParameters?.max_tokens,\n temperature: modelParameters?.temperature,\n ...rest,\n };\n }\n}\n","import { Liquid } from \"liquidjs\";\n\nimport { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\n\nimport { PromptCompilationError, PromptValidationError } from \"./errors\";\nimport { promptDataOf } from \"./prompt-data\";\nimport { promptDataSchema } from \"./schema\";\nimport { PromptTracingDecorator, tracer } from \"./tracing\";\nimport {\n type TemplateVariables,\n type PromptData,\n type CorePromptData,\n type PromptScope,\n} from \"./types\";\n\n// Re-export types and errors for convenience\nexport type { TemplateVariables, PromptData, CorePromptData, PromptMetadata } from \"./types\";\nexport { PromptCompilationError, PromptValidationError } from \"./errors\";\n\n// Global Liquid instance - shared across all prompts for efficiency\nconst liquid = new Liquid({\n strictFilters: true,\n});\n\n/**\n * The Prompt class provides a standardized interface for working with prompt objects\n * within the SDK, focusing on core functionality needed for template compilation and execution.\n * Keeps only essential fields while maintaining compatibility with tracing and observability.\n */\nexport class Prompt {\n // === Core functionality (required) ===\n public readonly model!: string;\n public readonly messages!: {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n }[];\n\n // === Optional core fields ===\n public readonly prompt?: string;\n public readonly temperature?: number;\n public readonly maxTokens?: number;\n public readonly responseFormat?: CorePromptData[\"responseFormat\"];\n\n // === Runtime parameters ===\n public readonly parameters: Record<string, unknown> = {};\n\n // === Optional identification (for tracing) ===\n public readonly id?: string;\n public readonly handle?: string | null;\n public readonly version?: number;\n public readonly versionId?: string;\n public readonly scope?: PromptScope;\n\n constructor(data: PromptData) {\n // Validate input using Zod\n const validationResult = promptDataSchema.strip().safeParse(data);\n\n if (!validationResult.success) {\n throw new PromptValidationError(\"Invalid prompt data provided\", validationResult.error);\n }\n\n // Assign validated data\n Object.assign(this, validationResult.data);\n\n // Set default for prompt if not provided\n this.prompt ??= this.extractSystemPrompt();\n\n // Return a proxy that wraps specific methods for tracing\n return createTracingProxy(this as Prompt, tracer, PromptTracingDecorator);\n }\n\n private extractSystemPrompt(): string {\n return this.messages.find((m) => m.role === \"system\")?.content ?? \"\";\n }\n\n /**\n * Compile the prompt template with provided variables (lenient - missing variables become empty)\n * @param variables - Object containing variable values for template compilation\n * @returns CompiledPrompt instance with compiled content\n */\n private _compile(variables: TemplateVariables, strict: boolean): CompiledPrompt {\n try {\n // Compile main prompt\n const compiledPrompt = this.prompt\n ? liquid.parseAndRenderSync(this.prompt, variables, {\n strictVariables: strict,\n })\n : \"\";\n\n // Compile messages\n const compiledMessages = (this.messages || []).map((message) => ({\n ...message,\n content: message.content\n ? liquid.parseAndRenderSync(message.content, variables, {\n strictVariables: strict,\n })\n : message.content,\n }));\n\n // Create new prompt data with compiled content\n const compiledData: PromptData = {\n ...promptDataOf(this),\n prompt: compiledPrompt,\n messages: compiledMessages,\n };\n\n return new CompiledPrompt(compiledData, this);\n } catch (error) {\n const templateStr = this.prompt ?? JSON.stringify(this.messages);\n throw new PromptCompilationError(\n `Failed to compile prompt template: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n templateStr,\n error,\n );\n }\n }\n\n compile(variables: TemplateVariables = {}): CompiledPrompt {\n return this._compile(variables, false);\n }\n\n /**\n * Compile with validation - throws error if required variables are missing\n * @param variables - Template variables\n * @returns CompiledPrompt instance with compiled content\n */\n compileStrict(variables: TemplateVariables): CompiledPrompt {\n return this._compile(variables, true);\n }\n}\n\n/**\n * Represents a compiled prompt that extends Prompt with reference to the original template\n */\nexport class CompiledPrompt extends Prompt {\n constructor(\n compiledData: PromptData,\n public readonly original: Prompt,\n ) {\n super(compiledData);\n }\n}\n","import { SpanKind } from \"@opentelemetry/api\";\n\nimport { type LangWatchSpan, type LangWatchTracer } from \"@/observability-sdk\";\n\n// Type for decorator methods that receive span as first parameter\ntype DecoratorMethodWithSpan<T extends (...args: never[]) => unknown> = (\n span: LangWatchSpan,\n ...args: Parameters<T>\n) => ReturnType<T>;\n\n// Type for decorator class that maps original methods to span-aware versions\n// Only requires methods that are actually implemented in the decorator\ntype DecoratorClass<T> = new (target: T) => Partial<{\n [K in keyof T]: T[K] extends (...args: never[]) => unknown ? DecoratorMethodWithSpan<T[K]> : T[K];\n}>;\n\n/**\n * Creates a proxy that always creates spans for public methods.\n * @param target - The target to wrap\n * @param tracer - The tracer instance to use\n * @param DecoratorClass - Optional decorator class for custom logic\n * @returns A proxy that wraps the target with consistent tracing\n */\nexport function createTracingProxy<\n T extends object,\n D extends DecoratorClass<T> | undefined = undefined,\n>(target: T, tracer: LangWatchTracer, DecoratorClass?: D): T {\n const decorator = DecoratorClass ? new DecoratorClass(target) : null;\n\n return new Proxy(target, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n // If it's not a function, return as-is\n if (typeof value !== \"function\") {\n return value;\n }\n\n if (typeof prop !== \"string\" || prop.startsWith(\"_\")) {\n return value.bind(target);\n }\n if (isGetterOrSetter(target, prop)) {\n return value.bind(target);\n }\n if (isBuiltInMethod(prop)) {\n return value.bind(target);\n }\n\n return (...args: unknown[]) => {\n const spanName = `${target.constructor.name}.${prop}`;\n\n return tracer.withActiveSpan(\n spanName,\n {\n kind: SpanKind.CLIENT,\n attributes: {\n \"code.function\": prop,\n \"code.namespace\": target.constructor.name,\n },\n },\n (span) => {\n const decoratorMethod = findDecoratorMethod(decorator, prop);\n if (decoratorMethod) {\n return decoratorMethod.apply(decorator, [span, ...args]);\n }\n\n return value.apply(target, args);\n },\n );\n };\n },\n });\n}\n\n// Helper function to check if a property is a getter or setter\nconst isGetterOrSetter = (target: object, prop: string | symbol): boolean => {\n // First check own properties\n let descriptor = Object.getOwnPropertyDescriptor(target, prop);\n\n // If not found on own properties, check prototype chain\n if (!descriptor) {\n const prototype = Object.getPrototypeOf(target);\n if (prototype) {\n descriptor = Object.getOwnPropertyDescriptor(prototype, prop);\n }\n }\n\n // Return true if it's a getter or setter\n return descriptor?.get !== undefined || descriptor?.set !== undefined;\n};\n\n// Helper function to check if a method is a built-in method that should not be traced\nconst isBuiltInMethod = (prop: string | symbol): boolean => {\n if (typeof prop !== \"string\") {\n return false;\n }\n\n // List of built-in methods that should not be traced\n const builtInMethods = [\n \"toString\",\n \"valueOf\",\n \"toJSON\",\n \"toLocaleString\",\n \"hasOwnProperty\",\n \"isPrototypeOf\",\n \"propertyIsEnumerable\",\n \"constructor\",\n ];\n\n return builtInMethods.includes(prop);\n};\n\nfunction findDecoratorMethod(decorator: object | null, prop: string) {\n if (!decorator || !(prop in decorator)) return void 0;\n const method = Reflect.get(decorator, prop);\n return typeof method === \"function\" ? method : void 0;\n}\n","import type { PromptData } from \"./types\";\n\n/** Picks the schema fields of a prompt, leaving its methods and extra fields behind. */\nexport function promptDataOf(prompt: PromptData): PromptData {\n return {\n model: prompt.model,\n messages: prompt.messages,\n prompt: prompt.prompt,\n temperature: prompt.temperature,\n maxTokens: prompt.maxTokens,\n responseFormat: prompt.responseFormat,\n id: prompt.id,\n handle: prompt.handle,\n version: prompt.version,\n versionId: prompt.versionId,\n scope: prompt.scope,\n parameters: prompt.parameters,\n };\n}\n","import { type PromptScope as PromptScopeType } from \"./types\";\n\nexport const PromptScope = {\n ORGANIZATION: \"ORGANIZATION\" as const,\n PROJECT: \"PROJECT\" as const,\n} satisfies Record<string, PromptScopeType>;\n","// TODO: Move these to their own files\nimport { z } from \"zod\";\n\nimport { PromptScope } from \"../constants\";\n\n/**\n * Zod schema for message objects in prompts\n */\nexport const messageSchema = z.object({\n role: z.enum([\"user\", \"assistant\", \"system\"]),\n content: z.string(),\n});\n\n/**\n * Zod schema for response format configuration\n */\nexport const responseFormatSchema = z\n .object({\n type: z.literal(\"json_schema\"),\n json_schema: z\n .object({\n name: z.string(),\n schema: z.record(z.string(), z.unknown()),\n })\n .nullable(),\n })\n .optional();\n\n/**\n * Zod schema for core prompt data - the essential fields needed for functionality\n */\nexport const corePromptDataSchema = z.object({\n model: z.string().min(1, \"Model cannot be empty\"),\n messages: z.array(messageSchema).min(1, \"At least one message is required\"),\n prompt: z.string().optional(),\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().positive().optional(),\n responseFormat: responseFormatSchema,\n});\n\n/**\n * Zod schema for prompt metadata - optional fields for identification and tracing\n */\nexport const promptMetadataSchema = z.object({\n id: z.string().optional(),\n handle: z.string().nullable().optional(),\n version: z.number().min(0).optional(),\n versionId: z.string().optional(),\n scope: z.enum(PromptScope).optional(),\n});\n\n/**\n * Combined schema for complete prompt data\n */\nexport const promptDataSchema = z.object({\n ...corePromptDataSchema.shape,\n ...promptMetadataSchema.shape,\n parameters: z.record(z.string(), z.unknown()).optional(),\n});\n","import { shouldCaptureInput, shouldCaptureOutput } from \"@/observability-sdk\";\nimport type { LangWatchSpan } from \"@/observability-sdk\";\n\nimport { type Prompt, type TemplateVariables, type CompiledPrompt } from \"../prompt\";\nimport { promptDataOf } from \"../prompt-data\";\n\n/**\n * Class that decorates the target prompt,\n * adding tracing to specific methods.\n */\nexport class PromptTracingDecorator {\n constructor(private readonly target: Prompt) {}\n\n private traceCompilation(\n span: LangWatchSpan,\n variables: TemplateVariables,\n compileFn: () => CompiledPrompt,\n ): CompiledPrompt {\n span.setType(\"prompt\");\n\n if (shouldCaptureInput()) {\n span.setInput(this.target);\n\n if (variables) {\n span.setAttribute(\n \"langwatch.prompt.variables\",\n JSON.stringify({\n type: \"json\",\n value: variables,\n }),\n );\n }\n }\n\n const result = compileFn();\n\n // Only emit combined handle:version format when both are available\n if (result.handle != null && result.version != null) {\n span.setAttribute(\"langwatch.prompt.id\", `${result.handle}:${result.version}`);\n }\n\n if (shouldCaptureOutput()) {\n span.setOutput({ ...promptDataOf(result), original: promptDataOf(result.original) });\n }\n\n return result;\n }\n\n compile(span: LangWatchSpan, variables: TemplateVariables = {}): CompiledPrompt {\n return this.traceCompilation(span, variables, () => this.target.compile(variables));\n }\n\n compileStrict(span: LangWatchSpan, variables: TemplateVariables): CompiledPrompt {\n return this.traceCompilation(span, variables, () => this.target.compileStrict(variables));\n }\n}\n","import type { LangWatchSpan } from \"@/observability-sdk\";\nimport { shouldCaptureInput, shouldCaptureOutput } from \"@/observability-sdk\";\n\nimport { type PromptsApiService, type SyncResult } from \"../prompts-api.service\";\nimport type { CreatePromptBody, UpdatePromptBody, PromptResponse } from \"../types\";\n\n/**\n * Class that decorates the target prompt service,\n * adding tracing to key methods.\n */\nexport class PromptServiceTracingDecorator {\n constructor(private readonly target: PromptsApiService) {}\n\n async get(\n span: LangWatchSpan,\n id: string,\n options?: { version?: string },\n ): Promise<PromptResponse> {\n span.setType(\"prompt\");\n\n const result = await this.target.get(id, options);\n\n if (result) {\n // Only emit combined format when both handle and version are available\n if (result.handle != null && result.version != null) {\n span.setAttribute(\"langwatch.prompt.id\", `${result.handle}:${result.version}`);\n }\n }\n\n if (result && shouldCaptureOutput()) {\n span.setOutput(\"json\", result);\n }\n\n return result;\n }\n\n async create(span: LangWatchSpan, params: CreatePromptBody): Promise<PromptResponse> {\n span.setType(\"prompt\");\n\n if (shouldCaptureInput()) {\n span.setInput(params);\n }\n\n const result = await this.target.create(params);\n\n span.setAttributes({\n \"langwatch.prompt.id\": result.id,\n \"langwatch.prompt.handle\": result.handle ?? undefined,\n \"langwatch.prompt.scope\": result.scope,\n \"langwatch.prompt.version.id\": result.versionId,\n \"langwatch.prompt.version.number\": result.version,\n });\n\n return result;\n }\n\n async update(span: LangWatchSpan, id: string, params: UpdatePromptBody): Promise<PromptResponse> {\n if (shouldCaptureInput()) {\n span.setInput(params);\n }\n\n const result = await this.target.update(id, params);\n\n span.setType(\"prompt\");\n span.setAttributes({\n \"langwatch.prompt.id\": id,\n \"langwatch.prompt.handle\": result.handle ?? undefined,\n \"langwatch.prompt.scope\": result.scope,\n \"langwatch.prompt.version.id\": result.versionId,\n \"langwatch.prompt.version.number\": result.version,\n });\n\n return result;\n }\n\n async delete(span: LangWatchSpan, id: string): Promise<{ success: boolean }> {\n const result = await this.target.delete(id);\n\n span.setType(\"prompt\");\n span.setAttribute(\"langwatch.prompt.id\", id);\n span.setAttribute(\"langwatch.prompt.deleted\", \"true\");\n\n return result;\n }\n\n async upsert(\n span: LangWatchSpan,\n handle: string,\n config: Parameters<PromptsApiService[\"upsert\"]>[1],\n ): Promise<{ created: boolean; prompt: PromptResponse }> {\n if (shouldCaptureInput()) {\n span.setInput(config);\n }\n\n const result = await this.target.upsert(handle, config);\n\n span.setType(\"prompt\");\n span.setAttributes({\n \"langwatch.prompt.handle\": handle,\n \"langwatch.prompt.created\": result.created.toString(),\n \"langwatch.prompt.id\": result.prompt.id,\n \"langwatch.prompt.version.id\": result.prompt.versionId,\n \"langwatch.prompt.version.number\": result.prompt.version,\n });\n\n return result;\n }\n\n async sync(\n span: LangWatchSpan,\n params: Parameters<PromptsApiService[\"sync\"]>[0],\n ): Promise<SyncResult> {\n if (shouldCaptureInput()) {\n span.setInput(params);\n }\n\n const result = await this.target.sync(params);\n\n span.setType(\"prompt\");\n span.setAttribute(\"langwatch.prompt.name\", params.name);\n span.setAttribute(\"langwatch.prompt.sync.action\", result.action);\n\n if (result.conflictInfo) {\n span.setAttributes({\n \"langwatch.prompt.sync.has_conflict\": \"true\",\n \"langwatch.prompt.sync.local_version\": result.conflictInfo.localVersion.toString(),\n \"langwatch.prompt.sync.remote_version\": result.conflictInfo.remoteVersion.toString(),\n });\n }\n\n return result;\n }\n}\n","import { LANGWATCH_SDK_NAME_CLIENT, LANGWATCH_SDK_VERSION } from \"@/internal/constants\";\nimport { getLangWatchTracer } from \"@/observability-sdk/tracer\";\n\nexport const tracer = getLangWatchTracer(\n `${LANGWATCH_SDK_NAME_CLIENT}.prompts`,\n LANGWATCH_SDK_VERSION,\n);\n","/**\n * The shared guard every apiClient-backed service method funnels its `{ data, error,\n * response }` triplet through before returning.\n */\nexport interface UnwrapApiResultParams<T> {\n /** What was being attempted, e.g. `create evaluator`. */\n operation: string;\n /** The `data` half of the client's response triplet. */\n data: T | undefined;\n /** The `error` half of the client's response triplet. */\n error: unknown;\n /** The `response` half, when the client kept hold of it. */\n response?: Response;\n /** The service's own `handleApiError`, which throws its typed error. */\n onError: (operation: string, error: unknown, response?: Response) => never;\n /**\n * True for endpoints that legitimately answer with no body (204, HEAD).\n * `data` stays `undefined` in that case without tripping the guard.\n */\n allowEmpty?: boolean;\n}\n\nexport function unwrapApiResult<T>({\n operation,\n data,\n error,\n response,\n onError,\n allowEmpty = false,\n}: UnwrapApiResultParams<T>): T {\n if (error) onError(operation, error, response);\n if (data === undefined && !allowEmpty) onError(operation, error, response);\n return data as T;\n}\n","import openApiCreateClient, { type Middleware } from \"openapi-fetch\";\n\nimport { scopedApiKey, scopedProjectId } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\n\nimport type { paths } from \"../generated/openapi/api-client\";\nimport { langwatchFetch } from \"../http/langwatchFetch\";\nimport { handledErrorFrom } from \"./errors\";\nimport { buildRequestHeaders } from \"./request-headers\";\n\n/**\n * Turns a NAMED failure into a typed throw, once, for every call that goes through this\n * client.\n */\nconst handledErrorMiddleware: Middleware = {\n async onResponse({ request, response }) {\n if (response.ok) return;\n\n // openapi-fetch reads the body itself further down the pipeline, and a body\n // can only be read once. Clone, or the non-domain path gets an empty error.\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"json\")) return;\n\n let body: unknown;\n try {\n body = await response.clone().json();\n } catch {\n // A body that claims JSON and isn't is exactly the case this must not\n // crash on. Leave it to the generic path.\n return;\n }\n\n const handledError = handledErrorFrom({\n body,\n status: response.status,\n // The platform's own sentence is the message; there is no operation to\n // prefix it with down here, and the CLI adds \"Failed to <action>\" itself.\n operation: `${request.method} ${new URL(request.url).pathname}`,\n message: undefined,\n });\n\n if (handledError) throw handledError;\n },\n};\n\n/**\n * Creates a new LangWatch API client.\n * @param apiKey - The API key or Personal Access Token. Defaults to `LANGWATCH_API_KEY`.\n * @param endpoint - The API endpoint. Defaults to `LANGWATCH_ENDPOINT` or `DEFAULT_ENDPOINT`.\n */\nexport const createLangWatchApiClient = (\n // The request-scoped key (the CLI resolver's output) wins over the global\n // env: in the daemon the resolved device-session key lives ONLY in the\n // async-scoped store, never in the shared process.env, so concurrent\n // requests can't read each other's credential. A plain SDK embed sets no\n // scope and falls back to the environment unchanged.\n apiKey: string = scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\",\n endpoint?: string,\n // Same precedence as the key: the request-scoped target project (the CLI\n // resolver's output, personal by default and `--project` when given) wins\n // over the global env, and a plain SDK embed that scopes nothing falls back\n // to the environment unchanged.\n projectId: string | undefined = scopedProjectId() ?? process.env.LANGWATCH_PROJECT_ID,\n) => {\n const client = openApiCreateClient<paths>({\n baseUrl: resolveEndpoint(endpoint),\n fetch: langwatchFetch,\n headers: {\n ...buildRequestHeaders({ apiKey, projectId }),\n \"content-type\": \"application/json\",\n },\n });\n\n client.use(handledErrorMiddleware);\n\n return client;\n};\n\nexport type LangwatchApiClient = ReturnType<typeof createLangWatchApiClient>;\n","/**\n * The one HTTP client every LangWatch API request goes through, with\n * `redirect: \"manual\"` since the global `fetch` turns a POST into a GET on\n * a redirect. GET/HEAD follow up to `MAX_FOLLOW_HOPS`; refusal throws.\n */\nimport { ConsoleLogger, type Logger } from \"../../logger\";\nimport { API_VERSION_HEADER, LANGWATCH_API_VERSION } from \"../constants\";\n\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n\n/** Methods that follow a redirect to any http or https URL. */\nconst FOLLOWING_METHODS = new Set([\"GET\", \"HEAD\"]);\n\n/** The most redirects a GET or HEAD follows before the next one is refused. */\nexport const MAX_FOLLOW_HOPS = 5;\n\n/**\n * Headers dropped when a GET or HEAD hop leaves the origin: the three the Fetch\n * standard strips on a cross-origin redirect, plus the names LangWatch keys on.\n */\nconst CREDENTIAL_HEADERS = [\n \"authorization\",\n \"cookie\",\n \"proxy-authorization\",\n \"x-api-key\",\n \"x-auth-token\",\n \"x-project-id\",\n];\n\nexport class LangWatchRedirectError extends Error {\n readonly url: string;\n readonly location: string | null;\n readonly status: number;\n\n constructor({ url, location, status }: { url: string; location: string | null; status: number }) {\n super(\n `LangWatch refused to follow a redirect from ${url} to ${location ?? \"an unknown location\"} (HTTP ${status}). Set the endpoint to the final URL.`,\n );\n this.name = \"LangWatchRedirectError\";\n this.url = url;\n this.location = location;\n this.status = status;\n }\n}\n\nexport type LangWatchFetch = typeof globalThis.fetch;\n\nexport interface CreateLangWatchFetchOptions {\n /** The transport to send with. Defaults to the global `fetch` at call time. */\n fetch?: LangWatchFetch;\n /** Receives the one warning per process about an http endpoint. */\n logger?: Logger;\n}\n\nconst isPlainHeaders = (headers: HeadersInit): headers is Record<string, string> =>\n !(headers instanceof Headers) && !Array.isArray(headers);\n\n/**\n * `init` naming the client's API version, unless the caller already names one. A plain\n * headers object stays plain, so a caller reading the headers back sees the shape it sent.\n */\nconst withApiVersion = ({\n input,\n init,\n}: {\n input: RequestInfo | URL;\n init: RequestInit | undefined;\n}): RequestInit => {\n const sent =\n init?.headers ??\n (typeof Request !== \"undefined\" && input instanceof Request ? input.headers : undefined);\n if (new Headers(sent).has(API_VERSION_HEADER)) return { ...init };\n if (sent === undefined || isPlainHeaders(sent)) {\n return { ...init, headers: { ...sent, [API_VERSION_HEADER]: LANGWATCH_API_VERSION } };\n }\n const headers = new Headers(sent);\n headers.set(API_VERSION_HEADER, LANGWATCH_API_VERSION);\n return { ...init, headers };\n};\n\n/** Origins that already produced the http-to-https warning in this process. */\nconst warnedOrigins = new Set<string>();\n\n/** Forgets which origins were warned about. For tests only. */\nexport const resetSchemeUpgradeWarnings = (): void => {\n warnedOrigins.clear();\n};\n\nconst isRedirect = (response: Response): boolean =>\n response.type === \"opaqueredirect\" || REDIRECT_STATUSES.has(response.status);\n\nconst isStream = (body: unknown): boolean =>\n typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream;\n\nconst abortError = (signal: AbortSignal): Error => {\n const reason: unknown = signal.reason;\n if (reason instanceof Error) return reason;\n if (typeof DOMException !== \"undefined\") {\n return new DOMException(\"This operation was aborted\", \"AbortError\");\n }\n return Object.assign(new Error(\"This operation was aborted\"), { name: \"AbortError\" });\n};\n\n/**\n * Releases the unread copy of a request body. `Request.clone` tees the\n * stream, so the unused copy is cancelled without buffering a streamed\n * upload whole. Never awaited -- `cancel` settles once both branches are done.\n */\nconst discard = (spare: Request | null): void => {\n void spare?.body?.cancel().catch(() => undefined);\n};\n\n/**\n * The body bytes to replay, read under the caller's signal. A stream-backed\n * `Request`'s copy is a stream too, so the read races the signal and\n * cancels the copy it loses to, letting an aborted call settle.\n */\nconst replayBody = async ({\n spare,\n signal,\n}: {\n spare: Request;\n signal: AbortSignal | null | undefined;\n}): Promise<ArrayBuffer> => {\n if (!signal) return spare.arrayBuffer();\n if (signal.aborted) throw abortError(signal);\n\n const aborted = new Promise<never>((_, reject) => {\n signal.addEventListener(\"abort\", () => reject(abortError(signal)), { once: true });\n });\n // An abort that arrives after the read already won still rejects this one,\n // and nothing would be waiting on it by then.\n void aborted.catch(() => undefined);\n try {\n return await Promise.race([spare.arrayBuffer(), aborted]);\n } catch (error) {\n discard(spare);\n throw error;\n }\n};\n\nconst requestUrl = (input: RequestInfo | URL): string => {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return input.url;\n};\n\nconst parseHop = ({\n url,\n location,\n}: {\n url: string;\n location: string;\n}): { from: URL; to: URL } | null => {\n try {\n const from = new URL(url);\n return { from, to: new URL(location, from) };\n } catch {\n return null;\n }\n};\n\n/** Same host and port, with the scheme changed from http to https. */\nconst isSchemeUpgrade = ({ from, to }: { from: URL; to: URL }): boolean =>\n from.protocol === \"http:\" &&\n to.protocol === \"https:\" &&\n from.hostname === to.hostname &&\n from.port === to.port;\n\n/**\n * The https URL to replay against when `location` only upgrades the scheme of\n * `url`, and null for any other target. The URL parser drops a default port,\n * so `http://host:80` and `https://host:443` both read as no port.\n */\nexport const schemeUpgradeTarget = ({\n url,\n location,\n}: {\n url: string;\n location: string;\n}): string | null => {\n const hop = parseHop({ url, location });\n if (hop === null || !isSchemeUpgrade(hop)) return null;\n const { from, to } = hop;\n if (from.pathname !== to.pathname) return null;\n if (from.search !== to.search) return null;\n to.hash = \"\";\n return to.href;\n};\n\n/**\n * The URL a GET or HEAD follows to, and null when the hop is refused: a\n * target that is not http or https, or a downgrade from https to http.\n */\nexport const followTarget = ({\n url,\n location,\n}: {\n url: string;\n location: string;\n}): string | null => {\n const hop = parseHop({ url, location });\n if (hop === null) return null;\n const { from, to } = hop;\n if (to.protocol !== \"http:\" && to.protocol !== \"https:\") return null;\n if (from.protocol === \"https:\" && to.protocol === \"http:\") return null;\n to.hash = \"\";\n return to.href;\n};\n\n/** A hop keeps credential headers on the same origin, or an https upgrade of the same host. */\nconst keepsCredentials = ({ from, to }: { from: URL; to: URL }): boolean =>\n from.origin === to.origin || isSchemeUpgrade({ from, to });\n\nconst withoutCredentials = (headers: Headers): Headers => {\n const stripped = new Headers(headers);\n for (const name of CREDENTIAL_HEADERS) stripped.delete(name);\n return stripped;\n};\n\nconst warnOnce = ({ url, logger }: { url: string; logger: Logger }): void => {\n const { origin, host } = new URL(url);\n if (warnedOrigins.has(origin)) return;\n warnedOrigins.add(origin);\n logger.warn(\n `LangWatch endpoint ${origin} redirected to https. Set the endpoint to https://${host} to skip the extra round trip.`,\n );\n};\n\nconst refusalOf = ({\n url,\n response,\n}: {\n url: string;\n response: Response;\n}): LangWatchRedirectError =>\n new LangWatchRedirectError({\n url,\n location: response.headers.get(\"location\"),\n status: response.status,\n });\n\n/**\n * What `fetch(input, init)` would send, as one request both sends read\n * from. `init` wins field-by-field over a `Request` input, so reading raw\n * input would resend an overridden field. A plain URL input stays null.\n */\nconst effectiveRequest = ({\n input,\n init,\n}: {\n input: RequestInfo | URL;\n init: RequestInit | undefined;\n}): Request | null =>\n typeof Request !== \"undefined\" && input instanceof Request\n ? new Request(input, { ...init, redirect: \"manual\" })\n : null;\n\ninterface Hop {\n send: LangWatchFetch;\n log: Logger;\n effective: Request | null;\n init: RequestInit | undefined;\n url: string;\n first: Response;\n}\n\n/** The GET and HEAD rule: follow with the same method, up to MAX_FOLLOW_HOPS. */\nconst follow = async ({\n send,\n log,\n effective,\n init,\n url,\n method,\n first,\n}: Hop & { method: string }): Promise<Response> => {\n let headers = new Headers(effective?.headers ?? init?.headers);\n const signal = effective?.signal ?? init?.signal;\n let current = url;\n let response = first;\n\n for (let hop = 0; hop < MAX_FOLLOW_HOPS; hop++) {\n const location = response.headers.get(\"location\");\n const target = location === null ? null : followTarget({ url: current, location });\n if (target === null) throw refusalOf({ url: current, response });\n\n const from = new URL(current);\n const to = new URL(target);\n if (!keepsCredentials({ from, to })) headers = withoutCredentials(headers);\n if (isSchemeUpgrade({ from, to })) warnOnce({ url: current, logger: log });\n\n current = target;\n response = effective\n ? await send(new Request(target, { method, headers, signal, redirect: \"manual\" }))\n : await send(target, { ...init, method, headers, body: undefined, redirect: \"manual\" });\n if (!isRedirect(response)) return response;\n }\n\n throw refusalOf({ url: current, response });\n};\n\n/** The rule for every other method: one hop, and only an https upgrade of the same URL. */\nconst upgrade = async ({\n send,\n log,\n effective,\n init,\n url,\n first,\n spare,\n}: Hop & { spare: Request | null }): Promise<Response> => {\n const location = first.headers.get(\"location\");\n const refused = refusalOf({ url, response: first });\n const target = location === null ? null : schemeUpgradeTarget({ url, location });\n if (location === null) {\n discard(spare);\n throw refused;\n }\n if (first.status === 303) {\n discard(spare);\n throw refused;\n }\n if (target === null) {\n discard(spare);\n throw refused;\n }\n if (isStream(init?.body)) {\n discard(spare);\n throw refused;\n }\n\n warnOnce({ url, logger: log });\n\n const second = effective\n ? await send(\n new Request(target, {\n method: effective.method,\n headers: effective.headers,\n body: spare ? await replayBody({ spare, signal: effective.signal }) : null,\n signal: effective.signal,\n redirect: \"manual\",\n }),\n )\n : await send(target, { ...init, redirect: \"manual\" });\n if (!isRedirect(second)) return second;\n\n throw refusalOf({ url: target, response: second });\n};\n\n/**\n * Builds a `fetch` that applies the redirect rule. Pass `fetch` to send through\n * another transport (a test double, a proxying client) and `logger` to route\n * the http endpoint warning.\n */\nexport const createLangWatchFetch = ({\n fetch: fetchImpl,\n logger,\n}: CreateLangWatchFetchOptions = {}): LangWatchFetch => {\n const send: LangWatchFetch = fetchImpl ?? ((input, init) => globalThis.fetch(input, init));\n const log = logger ?? new ConsoleLogger({ level: \"warn\", prefix: \"LangWatch\" });\n\n return async (input, given) => {\n const init = withApiVersion({ input, init: given });\n const url = requestUrl(input);\n const effective = effectiveRequest({ input, init });\n const method = (effective?.method ?? init?.method ?? \"GET\").toUpperCase();\n // A Request carries its body as a stream that one send consumes, so a copy\n // is taken before the first send and read only if the replay happens.\n const spare = effective !== null && effective.body !== null ? effective.clone() : null;\n\n const first = effective\n ? await send(effective)\n : await send(input, { ...init, redirect: \"manual\" });\n if (!isRedirect(first)) {\n discard(spare);\n return first;\n }\n\n const hop = { send, log, effective, init, url, first };\n if (!FOLLOWING_METHODS.has(method)) return upgrade({ ...hop, spare });\n\n discard(spare);\n return follow({ ...hop, method });\n };\n};\n\n/** The shared client, bound to the global `fetch` and the SDK console logger. */\nexport const langwatchFetch: LangWatchFetch = createLangWatchFetch();\n","/**\n * The failure contract between the platform, the CLI and the Langy panel.\n */\n\n/**\n * One link in the platform's `reasons` chain — the failure BEHIND the failure.\n * Recursive, and named by kind only: the platform masks anything it did not\n * raise itself as `{ code: \"unknown\" }` rather than leaking an internal message.\n */\nexport interface LangWatchHandledErrorReason {\n kind: string;\n /** Whether retrying this underlying failure unchanged may succeed. */\n retryable: boolean;\n meta?: Record<string, unknown>;\n reasons?: LangWatchHandledErrorReason[];\n}\n\n/** A failure, read back into the structure the platform originally gave it. */\nexport interface LangWatchHandledErrorShape {\n /** The platform's serialisable discriminant, e.g. `dataset_not_found`. */\n code: string;\n /**\n * @deprecated Back-compat alias of `code`, emitted while the platform's\n */\n kind: string;\n /** The human sentence. Safe to show: the platform wrote it for a user. */\n message: string;\n /** The status the platform answered with; 0 when the request never landed. */\n httpStatus: number;\n /** Whatever context the platform attached to the failure (ids, field errors). */\n meta: Record<string, unknown>;\n /**\n * True when the platform answered with a structured domain failure — i.e. it understood the\n * request and declined it. False for members failures, where the CLI is guessing and\n * the panel must not present it as the user's fault.\n */\n isHandled: boolean;\n /** Whether the platform explicitly permits retrying the same request. */\n retryable: boolean;\n /**\n * The OTel trace the failure happened on, when the route sent it. Read when offered and\n * simply absent otherwise — never fabricated.\n */\n traceId?: string;\n /** A clickable link to that trace, when the route sent one. */\n traceUrl?: string;\n /** A clickable link to the logs for that trace, when the route sent one. */\n logsUrl?: string;\n /** The reason chain, when the route sent it. Same availability as `traceId`. */\n reasons?: LangWatchHandledErrorReason[];\n /**\n * What the user can DO about it — the platform's own next steps.\n * This is the remediation channel ADR-045 added for exactly this consumer —\n */\n suggestions?: string[];\n /** The docs page that explains the failure (`docsUrl` on the wire). */\n docUrl?: string;\n}\n\n/** Anything at or above this is the platform failing, not the caller. */\nconst SERVER_ERROR_STATUS = 500;\n\n/**\n * Codes the platform uses when it has NOT actually named a domain failure — the generic 500\n * lane of `handleError`. They matter because the status is not always recoverable: some of the\n * SDK's service wrappers surface the error body without the `Response` it came on.\n */\nconst GENERIC_CODES = new Set([\n \"internal server error\",\n \"internal_error\",\n \"unknown\",\n \"unknown error\",\n]);\n\nconst isGenericCode = (code: string): boolean => GENERIC_CODES.has(code.trim().toLowerCase());\n\nconst asRecord = (value: unknown): Record<string, unknown> | null =>\n value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n\nconst isString = (value: unknown): value is string => typeof value === \"string\";\n\n/** The reason chain, defensively: anything without a code is not a reason. */\nconst asReasons = (value: unknown): LangWatchHandledErrorReason[] | undefined => {\n if (!Array.isArray(value)) return undefined;\n\n const reasons = value\n .map(asRecord)\n .filter(\n (r): r is Record<string, unknown> =>\n !!r && (typeof r.code === \"string\" || typeof r.kind === \"string\"),\n )\n .flatMap((r) => {\n const kind = isString(r.code) ? r.code : r.kind;\n if (!isString(kind)) return [];\n const meta = asRecord(r.meta);\n return [\n {\n kind,\n retryable: r.retryable === true,\n ...(meta ? { meta } : {}),\n ...(asReasons(r.reasons) ? { reasons: asReasons(r.reasons) } : {}),\n },\n ];\n });\n\n return reasons.length > 0 ? reasons : undefined;\n};\n\n/**\n * A libuv/Node system error, not a platform error body. `fetch` reports a transport failure by\n * throwing a `TypeError(\"fetch failed\")` whose `cause` is the system error, and\n * `handledErrorFromThrown` unwraps to that cause.\n */\nconst isSystemError = (record: Record<string, unknown>): boolean =>\n \"errno\" in record || \"syscall\" in record;\n\n/**\n * Does this record look like the platform's envelope at all? Only asked of a BARE top-level\n * `code` — the one field a system error shares with dialect 3.\n */\nconst looksLikeErrorEnvelope = (record: Record<string, unknown>): boolean =>\n \"message\" in record || \"meta\" in record || \"kind\" in record;\n\n/** Next steps, defensively: only real strings survive. */\nconst asSuggestions = (value: unknown): string[] | undefined => {\n if (!Array.isArray(value)) return undefined;\n const suggestions = value.filter(\n (s): s is string => typeof s === \"string\" && s.trim().length > 0,\n );\n return suggestions.length > 0 ? suggestions : undefined;\n};\n\ninterface ErrorBody {\n code: string;\n message?: string;\n meta: Record<string, unknown>;\n retryable: boolean;\n traceId?: string;\n traceUrl?: string;\n logsUrl?: string;\n reasons?: LangWatchHandledErrorReason[];\n suggestions?: string[];\n docUrl?: string;\n}\n\n/** True when a dialect's record carries a string `code`, or the given fallback discriminant. */\nconst namesFailure = ({\n record,\n fallbackKey,\n}: {\n record: Record<string, unknown>;\n fallbackKey: \"kind\" | \"type\";\n}): boolean => typeof record.code === \"string\" || typeof record[fallbackKey] === \"string\";\n\n/** Dialect 2: the serialised HandledError, carried whole under `domainError`. */\nconst serializedErrorBody = ({\n record,\n serialized,\n}: {\n record: Record<string, unknown>;\n serialized: Record<string, unknown>;\n}): ErrorBody => {\n const telemetry = asRecord(serialized.telemetry);\n return {\n code: typeof serialized.code === \"string\" ? serialized.code : String(serialized.kind),\n message: [record.error, record.message].find(isString),\n meta: asRecord(serialized.meta) ?? {},\n retryable: serialized.retryable === true,\n traceId: [serialized.traceId, telemetry?.traceId].find(isString),\n traceUrl: typeof serialized.traceUrl === \"string\" ? serialized.traceUrl : undefined,\n logsUrl: typeof serialized.logsUrl === \"string\" ? serialized.logsUrl : undefined,\n reasons: asReasons(serialized.reasons),\n suggestions: asSuggestions(serialized.tips) ?? asSuggestions(serialized.suggestions),\n docUrl: [serialized.docsUrl, serialized.docUrl].find(isString),\n };\n};\n\n/**\n * Dialect 4: `{ error: { type, code, message, meta?, trace_id?, span_id? } }`, which the Go\n * plane still answers with. None of the flat readings can see it, so it is read first.\n */\nconst canonicalErrorBody = (canonical: Record<string, unknown>): ErrorBody => {\n const code = typeof canonical.code === \"string\" ? canonical.code : String(canonical.type);\n const { reasons: metaReasons, ...meta } = asRecord(canonical.meta) ?? {};\n return {\n code,\n retryable: canonical.retryable === true,\n message:\n typeof canonical.message === \"string\" && canonical.message !== code\n ? canonical.message\n : undefined,\n meta,\n traceId: typeof canonical.trace_id === \"string\" ? canonical.trace_id : undefined,\n reasons: asReasons(metaReasons),\n suggestions: asSuggestions(canonical.tips) ?? asSuggestions(canonical.suggestions),\n docUrl: typeof canonical.docs_url === \"string\" ? canonical.docs_url : undefined,\n };\n};\n\n/**\n * Dialects 1 and 3 (and the deprecated `kind`-only variant): the code that names the failure,\n * or unnamed when this is not the platform's shape. Dialect 1 spreads meta FLAT, so a meta\n * key named `code`/`type` must not shadow the real discriminant on `error`.\n */\nconst flatDialectCode = (record: Record<string, unknown>): FlatDialectRead => {\n const envelopeCode = [record.code, record.type].find(isString);\n if (typeof record.kind === \"string\") return { kind: \"named\", code: envelopeCode ?? record.kind };\n if (typeof record.error === \"string\") return { kind: \"named\", code: record.error };\n if (envelopeCode !== undefined && looksLikeErrorEnvelope(record)) {\n return { kind: \"named\", code: envelopeCode };\n }\n return { kind: \"unnamed\" };\n};\n\ntype FlatDialectRead = { kind: \"named\"; code: string } | { kind: \"unnamed\" };\n\n/**\n * Read the platform's error body. The REST surface speaks this in FOUR dialects, and all are\n * real — verified against the routes, not assumed: 1.\n * and never crosses the boundary (ADR-045). Prose, when the server\n */\nconst asErrorBody = (value: unknown): ErrorBody | null => {\n const record = asRecord(value);\n if (!record) return null;\n\n // A libuv system error is a transport failure wearing a `code`. It is never\n // any of the three dialects, whatever else it carries, so it never gets to\n // name a domain failure — it falls through to the status-derived reading,\n // which calls it `network_error` and means it.\n if (isSystemError(record)) return null;\n\n // Dialect 2: the serialised HandledError, carried whole under `domainError`.\n const serialized = asRecord(record.domainError);\n if (serialized && namesFailure({ record: serialized, fallbackKey: \"kind\" })) {\n return serializedErrorBody({ record, serialized });\n }\n\n // Dialect 4: the NESTED canonical envelope — see {@link canonicalErrorBody}. The `/api/` REST\n // surface writes those fields at the ROOT instead, and falls through to the flat reading below.\n const canonical = asRecord(record.error);\n if (\n canonical &&\n !isSystemError(canonical) &&\n namesFailure({ record: canonical, fallbackKey: \"type\" })\n ) {\n return canonicalErrorBody(canonical);\n }\n\n const flat = flatDialectCode(record);\n if (flat.kind === \"unnamed\") return null;\n const named = flat.code;\n\n // Everything the platform did NOT put in meta gets lifted out, so the flat\n // spread does not smuggle the envelope's own fields back in as domain context.\n // `code`/`type` are discriminants, not context — they belong here too.\n const {\n error,\n message,\n kind: _kind,\n code: _code,\n type: _type,\n meta: explicitMeta,\n telemetry,\n trace,\n reasons,\n traceId,\n spanId: _spanId,\n traceUrl,\n logsUrl,\n suggestions,\n docUrl,\n // The platform's own spelling of the two above. Lifted out for the same\n // reason as the rest: dialect 1 spreads meta flat, so leaving them in would\n // report the remediation copy back as domain context.\n tips,\n docsUrl,\n // `fault` is the envelope's own (\"customer\" vs \"platform\"), not something\n // the platform attached to this failure.\n fault: _fault,\n retryable,\n ...rest\n } = record;\n\n const nestedMeta = asRecord(explicitMeta);\n\n // The sentence, in order of how much the server meant it. Dialect 3's own `message` is\n // usually just the code echoed back — server copy never crosses\n // the boundary (ADR-045) — so prose the server deliberately authored under\n const authored = nestedMeta?.message;\n const authoredSentence =\n typeof authored === \"string\" && authored.length > 0 ? authored : undefined;\n const sentence =\n authoredSentence ??\n [message, error].find((text): text is string => typeof text === \"string\" && text !== named);\n\n const telemetryRecord = asRecord(telemetry);\n const traceRecord = asRecord(trace);\n\n return {\n code: named,\n message: sentence,\n // Dialect 3 carries meta as its own key; dialect 1 spreads it flat.\n meta: nestedMeta ?? rest,\n retryable: retryable === true,\n traceId: [traceRecord?.traceId, telemetryRecord?.traceId, traceId].find(isString),\n traceUrl: [traceUrl, traceRecord?.traceUrl].find(isString),\n logsUrl: [logsUrl, traceRecord?.logsUrl].find(isString),\n reasons: asReasons(reasons),\n suggestions: asSuggestions(tips) ?? asSuggestions(suggestions),\n docUrl: [docsUrl, docUrl].find(isString),\n };\n};\n\n/**\n * Statuses where nothing the caller can SEND will change the answer. A machine caller — Langy\n * runs the CLI in a shell — has exactly three moves after a failure: retry unchanged, retry\n * with different arguments, or stop.\n */\nconst TERMINAL_STATUSES = new Set([401, 402, 403, 404, 410]);\n\n/**\n * True when retrying is pointless — see {@link TERMINAL_STATUSES}. Only ever said of a failure\n * the platform NAMED. An members failure is ours and transient by default, and a status\n * we read off a proxy's error page is not a verdict the platform reached.\n */\nexport const isTerminalFailure = (error: LangWatchHandledErrorShape): boolean =>\n error.isHandled && TERMINAL_STATUSES.has(error.httpStatus);\n\n/**\n * The constructors raised for a fault in the PROGRAM, not a request that did\n * not answer. `SyntaxError` is absent: with no status it is almost always\n * `response.json()` over a body that was not JSON, which a retry can clear.\n */\nconst PROGRAM_FAULT_NAMES = new Set([\"TypeError\", \"RangeError\", \"ReferenceError\"]);\n\n/**\n * The codes a transport failure arrives with, as libuv and OpenSSL spell them.\n * A named set rather than a SCREAMING_SNAKE shape, because Node writes its own\n * program-fault codes that way too (`TypeError [ERR_INVALID_URL]`).\n */\nconst TRANSPORT_CODES = new Set([\n // libuv, the codes a socket, a DNS lookup or a connection carries.\n \"EADDRNOTAVAIL\",\n \"EAI_AGAIN\",\n \"ECANCELED\",\n \"ECONNABORTED\",\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"EHOSTDOWN\",\n \"EHOSTUNREACH\",\n \"ENETDOWN\",\n \"ENETUNREACH\",\n \"ENOTFOUND\",\n \"EPIPE\",\n \"EPROTO\",\n \"ETIMEDOUT\",\n // OpenSSL, verifying the certificate the other end presented.\n \"CERT_HAS_EXPIRED\",\n \"CERT_NOT_YET_VALID\",\n \"DEPTH_ZERO_SELF_SIGNED_CERT\",\n \"ERR_TLS_CERT_ALTNAME_INVALID\",\n \"SELF_SIGNED_CERT_IN_CHAIN\",\n \"UNABLE_TO_GET_ISSUER_CERT\",\n \"UNABLE_TO_GET_ISSUER_CERT_LOCALLY\",\n \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\",\n]);\n\n/** True for a code the transport put there; `UND_ERR_` is undici's own prefix. */\nconst isTransportCode = (code: unknown): boolean =>\n typeof code === \"string\" && (TRANSPORT_CODES.has(code) || code.startsWith(\"UND_ERR_\"));\n\n/**\n * Evidence the throw came from the TRANSPORT: the cause carries\n * `syscall`/`errno`, or a transport code of its own for a TLS failure.\n * `originalError` is scanned too, because the SDK wraps a throw under it.\n */\nconst hasTransportEvidence = (error: unknown): boolean => {\n const outer = asRecord(error);\n const original = asRecord(outer?.originalError);\n return [outer, asRecord(outer?.cause), original, asRecord(original?.cause)].some(\n (candidate) =>\n typeof candidate?.syscall === \"string\" ||\n typeof candidate?.errno === \"number\" ||\n isTransportCode(candidate?.code),\n );\n};\n\n/** True when the throw is a fault in our own code rather than a failed request. */\nconst isProgramFault = (error: unknown): boolean =>\n error instanceof Error && PROGRAM_FAULT_NAMES.has(error.name) && !hasTransportEvidence(error);\n\n/** A stable code for a failure the platform did not name itself. */\nconst codeForStatus = (status: number): string => {\n if (status === 401 || status === 403) return \"unauthorized\";\n if (status === 404) return \"not_found\";\n if (status === 429) return \"rate_limited\";\n if (status >= SERVER_ERROR_STATUS) return \"internal_error\";\n if (status > 0) return \"request_failed\";\n return \"network_error\";\n};\n\nconst fallbackMessage = ({ status, body }: { status: number; body: unknown }): string => {\n if (typeof body === \"string\" && body.trim()) return body.trim().slice(0, 300);\n return status > 0 ? `Request failed with status ${status}` : \"Request failed\";\n};\n\n/**\n * Read an HTTP failure back into a {@link LangWatchHandledErrorShape}. A body in the platform's\n * error shape that came back BELOW 500 is a true domain error and keeps its code.\n */\nexport const parseHandledError = ({\n status,\n body,\n}: {\n status: number;\n body: unknown;\n}): LangWatchHandledErrorShape => {\n const parsed = asErrorBody(body);\n\n if (!parsed) {\n return {\n code: codeForStatus(status),\n kind: codeForStatus(status),\n message: fallbackMessage({ status, body }),\n httpStatus: status,\n meta: {},\n isHandled: false,\n retryable: false,\n };\n }\n\n return {\n code: parsed.code,\n // Deprecated back-compat alias — see LangWatchHandledErrorShape.kind.\n kind: parsed.code,\n message: parsed.message ?? parsed.code,\n httpStatus: status,\n meta: parsed.meta,\n retryable: parsed.retryable,\n ...(parsed.traceId ? { traceId: parsed.traceId } : {}),\n ...(parsed.traceUrl ? { traceUrl: parsed.traceUrl } : {}),\n ...(parsed.logsUrl ? { logsUrl: parsed.logsUrl } : {}),\n ...(parsed.reasons ? { reasons: parsed.reasons } : {}),\n ...(parsed.suggestions ? { suggestions: parsed.suggestions } : {}),\n ...(parsed.docUrl ? { docUrl: parsed.docUrl } : {}),\n // The platform names its own failures; a 5xx names ours, whatever the body says. Trusting a\n // 500's \"code\" would let an members outage present itself to the user as though they\n // had done something wrong. With no status to go on, the code decides: the platform only\n // emits a generic code when it fell over, so anything more specific than that is a failure\n // it chose to name — which is exactly what a domain error is.\n isHandled: status > 0 ? status < SERVER_ERROR_STATUS : !isGenericCode(parsed.code),\n };\n};\n\n/**\n * The document the CLI prints on stdout when a command fails under `--format json`, and the one\n * the panel reads back.\n */\nexport interface CliErrorDocument {\n ok: false;\n error: LangWatchHandledErrorShape & {\n /**\n * Whether retrying — with the same arguments or with different ones — can possibly change\n * the answer. See {@link isTerminalFailure}.\n */\n terminal: boolean;\n };\n}\n\n/** Build the `--format json` failure document. */\nexport const toCliErrorDocument = (error: LangWatchHandledErrorShape): CliErrorDocument => ({\n ok: false,\n error: { ...error, terminal: isTerminalFailure(error) },\n});\n\n/**\n * Read a CLI failure document back; `other` when the output is not one, rather than a throw:\n * stdout may hold a card, a human table, or nothing at all, and none of those is an error document.\n */\nexport type CliErrorDocumentRead =\n | { kind: \"error\"; error: LangWatchHandledErrorShape }\n | { kind: \"other\" };\n\nexport const readCliErrorDocument = (output: unknown): CliErrorDocumentRead => {\n const document = typeof output === \"string\" ? safeParseJson(output) : asRecord(output);\n\n const record = asRecord(document);\n if (!record || record.ok !== false) return { kind: \"other\" };\n\n const error = asRecord(record.error);\n const code = [error?.code, error?.kind].find(isString) ?? null;\n if (!error || code === null) return { kind: \"other\" };\n\n return {\n kind: \"error\",\n error: {\n code,\n // Deprecated back-compat alias — see LangWatchHandledErrorShape.kind.\n kind: code,\n message: typeof error.message === \"string\" ? error.message : code,\n httpStatus: typeof error.httpStatus === \"number\" ? error.httpStatus : 0,\n meta: asRecord(error.meta) ?? {},\n isHandled: error.isHandled === true,\n retryable: error.retryable === true,\n ...(typeof error.traceId === \"string\" ? { traceId: error.traceId } : {}),\n ...(typeof error.traceUrl === \"string\" ? { traceUrl: error.traceUrl } : {}),\n ...(typeof error.logsUrl === \"string\" ? { logsUrl: error.logsUrl } : {}),\n ...(asReasons(error.reasons) ? { reasons: asReasons(error.reasons) } : {}),\n ...(asSuggestions(error.suggestions)\n ? { suggestions: asSuggestions(error.suggestions) }\n : {}),\n ...(typeof error.docUrl === \"string\" ? { docUrl: error.docUrl } : {}),\n },\n };\n};\n\nconst safeParseJson = (value: string): unknown => {\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"{\")) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n};\n\n/**\n * A `LangWatchHandledError` — an error the SDK's transport ALREADY read the wire\n * into. It brands itself with a boolean rather than relying on `instanceof`,\n * which does not survive a bundled CLI meeting a consumer's own copy of the SDK.\n */\nconst asAlreadyReadHandledError = (\n outer: Record<string, unknown> | null,\n): LangWatchHandledErrorShape | null => {\n if (!outer || outer.isLangWatchHandledError !== true) return null;\n const code = [outer.code, outer.kind].find(isString) ?? null;\n if (code === null) return null;\n\n return {\n code,\n // Deprecated back-compat alias — see LangWatchHandledErrorShape.kind.\n kind: code,\n message: typeof outer.message === \"string\" ? outer.message : code,\n httpStatus: typeof outer.httpStatus === \"number\" ? outer.httpStatus : 0,\n meta: asRecord(outer.meta) ?? {},\n isHandled: true,\n retryable: outer.retryable === true,\n ...(typeof outer.traceId === \"string\" ? { traceId: outer.traceId } : {}),\n ...(typeof outer.traceUrl === \"string\" ? { traceUrl: outer.traceUrl } : {}),\n ...(typeof outer.logsUrl === \"string\" ? { logsUrl: outer.logsUrl } : {}),\n ...(asReasons(outer.reasons) ? { reasons: asReasons(outer.reasons) } : {}),\n ...(asSuggestions(outer.suggestions) ? { suggestions: asSuggestions(outer.suggestions) } : {}),\n ...(typeof outer.docUrl === \"string\" ? { docUrl: outer.docUrl } : {}),\n };\n};\n\n/**\n * The status an error carries, wherever it chose to put it. The SDK's HTTP layer,\n * its service wrappers and `fetch` each spell this differently.\n */\nconst statusOf = (value: Record<string, unknown> | null): number => {\n if (!value) return 0;\n if (typeof value.status === \"number\") return value.status;\n if (typeof value.statusCode === \"number\") return value.statusCode;\n if (typeof value.httpStatus === \"number\") return value.httpStatus;\n\n const response = asRecord(value.response);\n if (response && typeof response.status === \"number\") return response.status;\n\n return 0;\n};\n\n/**\n * The same reading, for an error that was THROWN rather than returned — the shape the SDK's\n * service layer raises.\n */\nexport const handledErrorFromThrown = (error: unknown): LangWatchHandledErrorShape => {\n const outer = asRecord(error);\n\n // The SDK's HTTP layer may have read this already, into a richer structure than\n // the wire body it came from: the flattened body carries no trace id and no\n // reason chain, so re-deriving from it would THROW AWAY the fields the typed\n // error exists to preserve. Trust the reading that already happened.\n const alreadyRead = asAlreadyReadHandledError(outer);\n if (alreadyRead) return alreadyRead;\n\n // The SDK wraps the API's error body; the body is the thing worth reading.\n const cause =\n asRecord(outer?.originalError) ?? asRecord(outer?.cause) ?? asRecord(outer?.body) ?? outer;\n\n const status = statusOf(cause) || statusOf(outer);\n const parsed = parseHandledError({ status, body: cause });\n\n const message = error instanceof Error && error.message ? error.message : parsed.message;\n\n // A transport failure is infrastructure whatever system error it carries.\n // `isSystemError` disqualifies by `errno`/`syscall`, which misses a TLS\n // failure: that one carries neither, so its code read as a discriminant the\n // platform had chosen. The throw fetch makes is the tell.\n if (status === 0 && hasTransportEvidence(error)) {\n return {\n code: \"network_error\",\n kind: \"network_error\",\n message,\n httpStatus: 0,\n meta: {},\n isHandled: false,\n retryable: false,\n };\n }\n\n // No status and a program fault: the request is not what failed, so do not\n // send the reader to their network. Read BEFORE the envelope, because the\n // runtime hangs codes of its own on these throws and a code Node chose is\n // not a discriminant the platform chose.\n if (status === 0 && isProgramFault(error)) {\n return {\n ...parsed,\n code: \"internal_error\",\n kind: \"internal_error\",\n message,\n meta: {},\n isHandled: false,\n };\n }\n\n if (parsed.isHandled) return parsed;\n\n return { ...parsed, message };\n};\n","import {\n parseHandledError,\n type LangWatchHandledErrorShape,\n type LangWatchHandledErrorReason,\n} from \"@langwatch/handled-error/langwatch-handled-error\";\n\n/**\n * A failure the platform NAMED.\n */\nexport class LangWatchHandledError extends Error {\n /**\n * The discriminant.\n */\n readonly isLangWatchHandledError = true as const;\n\n /** The platform's serialisable discriminant, e.g. `dataset_not_found`. */\n readonly code: string;\n /**\n * @deprecated Back-compat alias of `code`, kept while the platform's\n * `DomainError` → `HandledError` rename rolls out. Read `code` in new code.\n */\n readonly kind: string;\n /** The status the platform answered with. */\n readonly httpStatus: number;\n /** The context the platform attached: ids, field errors — whatever makes it actionable. */\n readonly meta: Record<string, unknown>;\n /** The OTel trace to quote at support. Absent unless the route sent one. */\n readonly traceId: string | undefined;\n /** A clickable link to that trace, when the route sent one. */\n readonly traceUrl: string | undefined;\n /** A clickable link to the logs for that trace, when the route sent one. */\n readonly logsUrl: string | undefined;\n /** The failure behind the failure, when the route sent the chain. */\n readonly reasons: LangWatchHandledErrorReason[] | undefined;\n /** Whether the platform explicitly marked this failure safe to retry. */\n readonly retryable: boolean;\n /** What the user can DO about it, when the platform sent next steps. */\n readonly suggestions: string[] | undefined;\n /** The docs page that explains the failure, when the platform sent one. */\n readonly docUrl: string | undefined;\n /** The raw response body, verbatim — the escape hatch for anything unmodelled. */\n readonly body: unknown;\n /** What the SDK was doing, e.g. `get dataset \"abc\"`. */\n readonly operation: string | undefined;\n\n /**\n * Alias of {@link httpStatus}, and the raw body again under the name the per-service\n * `*ApiError` classes use.\n */\n readonly status: number;\n readonly originalError: unknown;\n\n constructor({\n handled,\n body,\n operation,\n message,\n }: {\n handled: LangWatchHandledErrorShape;\n body: unknown;\n operation?: string;\n message: string;\n }) {\n super(message);\n this.name = \"LangWatchHandledError\";\n this.code = handled.code;\n this.kind = handled.code;\n this.httpStatus = handled.httpStatus;\n this.meta = handled.meta;\n this.traceId = handled.traceId;\n this.traceUrl = handled.traceUrl;\n this.logsUrl = handled.logsUrl;\n this.reasons = handled.reasons;\n this.retryable = handled.retryable;\n this.suggestions = handled.suggestions;\n this.docUrl = handled.docUrl;\n this.body = body;\n this.operation = operation;\n this.status = handled.httpStatus;\n this.originalError = body;\n }\n}\n\n/** Narrows any caught value to a {@link LangWatchHandledError}. */\nexport const isLangWatchHandledError = (error: unknown): error is LangWatchHandledError =>\n error instanceof LangWatchHandledError ||\n (typeof error === \"object\" &&\n error !== null &&\n (error as { isLangWatchHandledError?: unknown }).isLangWatchHandledError === true);\n\n/**\n * Read a non-2xx response into a {@link LangWatchHandledError}, or `null` when the\n * platform did NOT name the failure.\n */\nexport const handledErrorFrom = ({\n operation,\n body,\n status,\n message,\n}: {\n /** What was being attempted — `GET /api/v1/traces/{id}`, or a service's phrasing. */\n operation?: string;\n /** The parsed error body, as the HTTP client handed it back. */\n body: unknown;\n /** The status the response carried, when it is known. */\n status?: number;\n /**\n * The message the generic path WOULD have produced. Passed by callers that\n * already built one (so nothing regresses); omitted by the transport, which\n * has no operation worth prefixing and lets the platform's own sentence stand.\n */\n message?: string;\n}): LangWatchHandledError | null => {\n const handled = parseHandledError({ status: status ?? 0, body });\n if (!handled.isHandled) return null;\n\n return new LangWatchHandledError({\n handled,\n body,\n operation,\n // The platform wrote its message for a user to read. Use it as-is unless a\n // caller hands us the sentence it had already composed.\n message: message ?? handled.message,\n });\n};\n\nexport class LangWatchApiError extends Error {\n public readonly httpStatus: number;\n public readonly httpStatusText: string;\n public apiError: string | undefined;\n public body: unknown;\n\n constructor(message: string, response: Response) {\n super(message);\n this.httpStatus = response.status;\n this.httpStatusText = response.statusText;\n }\n\n async safeParseBody(response: Response): Promise<void> {\n try {\n const contentType = response.headers.get(\"Content-Type\");\n if (contentType?.includes(\"application/json\")) {\n const json = await response.json();\n\n this.body = json;\n\n if (json.error && typeof json.error === \"string\") {\n this.apiError = json.error;\n }\n\n return;\n }\n\n this.body = await response.text();\n } catch {\n this.body = null;\n }\n }\n}\n","/** Hand-written mirror of GovernanceCallSurface from the app (CLI doesn't\n * import from the source tree). CLI sends only \"cli\"; others documented for\n * future SDK regen. */\nexport type GovernanceCallSurface = \"trpc\" | \"hono\" | \"cli\" | \"mcp\";\n\nexport const CLI_SURFACE_HEADER = \"X-LangWatch-Surface\" as const;\nexport const CLI_SURFACE_VALUE: GovernanceCallSurface = \"cli\";\n","import {\n LANGWATCH_SDK_LANGUAGE,\n LANGWATCH_SDK_NAME_OBSERVABILITY,\n LANGWATCH_SDK_RUNTIME,\n LANGWATCH_SDK_VERSION,\n} from \"../constants\";\nimport { scopedProjectId, scopedSurface } from \"../credentialContext\";\nimport { CLI_SURFACE_HEADER, CLI_SURFACE_VALUE } from \"../surface\";\nimport { buildAuthHeaders, type LangWatchAuthHeadersInput } from \"./auth\";\n\nexport function buildSdkIdentityHeaders({\n surface = scopedSurface(),\n}: { surface?: \"cli\" } = {}): Record<string, string> {\n return {\n \"user-agent\": `langwatch-sdk-node/${LANGWATCH_SDK_VERSION}`,\n \"x-langwatch-sdk-name\": LANGWATCH_SDK_NAME_OBSERVABILITY,\n \"x-langwatch-sdk-language\": LANGWATCH_SDK_LANGUAGE,\n \"x-langwatch-sdk-version\": LANGWATCH_SDK_VERSION,\n \"x-langwatch-sdk-platform\": LANGWATCH_SDK_RUNTIME(),\n ...(surface === \"cli\" ? { [CLI_SURFACE_HEADER]: CLI_SURFACE_VALUE } : {}),\n };\n}\n\n/** A command building its own request still names the project the resolver chose (`--project`). */\nexport function buildRequestHeaders(\n credentials: LangWatchAuthHeadersInput,\n): Record<string, string> {\n return {\n ...buildAuthHeaders({\n ...credentials,\n projectId: credentials.projectId ?? scopedProjectId(),\n }),\n ...buildSdkIdentityHeaders(),\n };\n}\n","import { z } from \"zod\";\n\nimport type { RuntimeParameters } from \"@/cli/types\";\nimport { PromptConverter } from \"@/cli/utils/promptConverter\";\nimport {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n formatApiErrorMessage,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport { isLangWatchHandledError } from \"@/internal/api/errors\";\nimport type { paths, operations } from \"@/internal/generated/openapi/api-client\";\n\nimport { PromptsApiError } from \"./errors\";\nimport { PromptServiceTracingDecorator, tracer } from \"./tracing\";\nimport {\n type PromptResponse,\n type TagDefinition,\n type CreatedTag,\n type CreatePromptBody,\n type UpdatePromptBody,\n} from \"./types\";\n\nconst syncActionSchema = z.enum([\"created\", \"updated\", \"conflict\", \"up_to_date\"]);\n\nexport type SyncAction = z.infer<typeof syncActionSchema>;\n\nconst syncResultSchema = z.object({\n action: syncActionSchema,\n // `prompt` and `conflictInfo` are passed through untyped — they come from\n // the OpenAPI-derived shape which is already validated on the server side.\n prompt: z.unknown().optional(),\n conflictInfo: z\n .object({\n localVersion: z.number(),\n remoteVersion: z.number(),\n differences: z.array(z.string()),\n remoteConfigData: z.unknown(),\n })\n .passthrough()\n .optional(),\n});\n\nexport type AssignTagResult = NonNullable<\n operations[\"putApiPromptsByIdTagsByTag\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>;\n\nexport type ConfigData = NonNullable<\n paths[\"/api/v1/prompts/{id}/sync\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"][\"configData\"];\n\nexport interface SyncResult {\n action: SyncAction;\n prompt?: PromptResponse;\n conflictInfo?: {\n localVersion: number;\n remoteVersion: number;\n differences: string[];\n remoteConfigData: ConfigData;\n remoteParameters?: RuntimeParameters;\n };\n}\n\n/**\n * Service for managing prompt resources via the Langwatch API. Constructor creates a proxy\n * that wraps the service and traces all methods.\n */\nexport class PromptsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n\n /**\n * Wraps the service in a tracing proxy via the decorator.\n */\n return createTracingProxy(this as PromptsApiService, tracer, PromptServiceTracingDecorator);\n }\n\n /**\n * Handles API errors by throwing a PromptsApiError with operation context. @throws\n * @param operation Description of the operation being performed.\n * @param error The error object returned from the API client.\n */\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const resolvedStatus = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: resolvedStatus,\n },\n });\n\n throw new PromptsApiError(message, operation, error);\n }\n\n /**\n * Fetches all prompts from the API.\n * @returns Array of raw PromptResponse data.\n * @throws {PromptsApiError} If the API call fails.\n */\n async getAll(): Promise<PromptResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/prompts\");\n return unwrapApiResult({\n operation: \"fetch all prompts\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Fetches a single prompt by its ID.\n * @param id The prompt's unique identifier.\n * @param options Optional version or tag to fetch.\n */\n get = async (\n id: string,\n options?: { version?: string; tag?: string },\n ): Promise<PromptResponse> => {\n // Parse version to number, skip for \"latest\" or invalid values\n const versionNumber =\n options?.version && options.version !== \"latest\" ? parseInt(options.version, 10) : undefined;\n\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/prompts/{id}\", {\n params: {\n path: { id },\n query: {\n version: Number.isNaN(versionNumber) ? undefined : versionNumber,\n tag: options?.tag,\n },\n },\n });\n\n return unwrapApiResult({\n operation: `fetch prompt with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n };\n\n /**\n * Validates if a prompt exists. @throws {PromptsApiError} If the API call fails (not 404).\n * @param id The prompt's unique identifier.\n * @returns True if prompt exists, false otherwise.\n */\n async exists(id: string): Promise<boolean> {\n try {\n await this.get(id);\n return true;\n } catch (error) {\n // A named 404 from the platform (\"prompt_not_found\") now arrives typed, so\n // read the status off the domain error first. Without this, the very thing\n // that makes the failure legible — the platform naming it — would stop\n // `exists()` recognising a missing prompt and turn a `false` into a throw.\n if (isLangWatchHandledError(error) && error.httpStatus === 404) {\n return false;\n }\n\n const originalError = error instanceof PromptsApiError ? error.originalError : null;\n const statusCode =\n originalError != null && typeof originalError === \"object\" && \"statusCode\" in originalError\n ? (originalError as { statusCode: unknown }).statusCode\n : null;\n\n if (statusCode === 404) {\n return false;\n }\n\n throw error;\n }\n }\n\n /**\n * Creates a new prompt. @throws {PromptsApiError} If the API call fails.\n * @param params The prompt creation payload, matching the OpenAPI schema.\n * @returns Raw PromptResponse data of the created prompt.\n */\n async create(params: CreatePromptBody): Promise<PromptResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/prompts\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create prompt\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * @param id The prompt's unique identifier.\n * @param params The update payload, matching the OpenAPI schema.\n * @returns Raw PromptResponse data of the updated prompt.\n */\n async update(id: string, params: UpdatePromptBody): Promise<PromptResponse> {\n const { error, data, response } = await this.apiClient.PUT(\"/api/v1/prompts/{id}\", {\n params: { path: { id } },\n body: params,\n });\n return unwrapApiResult({\n operation: `update prompt with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Lists all prompt tags (built-in and custom) for the organization.\n * @returns Array of tag definitions.\n * @throws {PromptsApiError} If the API call fails.\n */\n async listTags(): Promise<TagDefinition[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/prompts/tags\");\n return unwrapApiResult({\n operation: \"list tags\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Creates a custom prompt tag for the organization. @throws {PromptsApiError} If the API\n * @param params.name The tag name (must match /^[a-z][a-z0-9_-]*$/).\n * @returns The created tag.\n */\n async createTag({ name }: { name: string }): Promise<CreatedTag> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/prompts/tags\", {\n body: { name },\n });\n return unwrapApiResult({\n operation: \"create tag\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Deletes a custom prompt tag by name.\n * @param tagName The tag name to delete.\n * @throws {PromptsApiError} If the API call fails.\n */\n async deleteTag(tagName: string): Promise<void> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/prompts/tags/{tag}\", {\n params: { path: { tag: tagName } },\n });\n unwrapApiResult({\n operation: `delete tag \"${tagName}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n allowEmpty: true,\n });\n }\n\n /**\n * Renames an existing prompt tag. @throws {PromptsApiError} If the API call fails.\n * @param tag The current tag name.\n * @param name The new tag name.\n */\n async renameTag({ tag, name }: { tag: string; name: string }): Promise<void> {\n const { data, error, response } = await this.apiClient.PUT(\"/api/v1/prompts/tags/{tag}\", {\n params: { path: { tag } },\n body: { name },\n });\n unwrapApiResult({\n operation: `rename tag \"${tag}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n allowEmpty: true,\n });\n }\n\n async assignTag({\n id,\n tag,\n versionId,\n }: {\n id: string;\n tag: string;\n versionId: string;\n }): Promise<AssignTagResult> {\n const { data, error, response } = await this.apiClient.PUT(\"/api/v1/prompts/{id}/tags/{tag}\", {\n params: { path: { id, tag } },\n body: { versionId },\n });\n return unwrapApiResult({\n operation: `assign tag \"${tag}\" to prompt \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Deletes a prompt by its ID.\n * @param id The prompt's unique identifier.\n * @throws {PromptsApiError} If the API call fails.\n */\n async delete(id: string): Promise<{ success: boolean }> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/prompts/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `delete prompt with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Fetches all versions for a given prompt. @throws {PromptsApiError} If the API call\n * @param id The prompt's unique identifier.\n * @returns Array of raw PromptResponse data for each version.\n */\n async getVersions(id: string): Promise<PromptResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/prompts/{id}/versions\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `fetch versions for prompt with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * @param handle The prompt's handle/identifier.\n * @param config Local prompt configuration.\n * @returns Object with created flag and raw PromptResponse data.\n */\n async upsert(\n handle: string,\n config: {\n model: string;\n modelParameters?: {\n temperature?: number;\n max_tokens?: number;\n };\n messages: {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n }[];\n parameters?: RuntimeParameters;\n },\n ): Promise<{ created: boolean; prompt: PromptResponse }> {\n const payload = {\n handle,\n model: config.model,\n prompt: PromptConverter.extractSystemPrompt(config.messages),\n messages: PromptConverter.filterNonSystemMessages(config.messages),\n temperature: config.modelParameters?.temperature,\n maxTokens: config.modelParameters?.max_tokens,\n inputs: [{ identifier: \"input\", type: \"str\" as const }],\n outputs: [{ identifier: \"output\", type: \"str\" as const }],\n parameters: config.parameters ?? {},\n commitMessage: `Updated via CLI sync`,\n schemaVersion: \"1.0\" as const,\n };\n\n // Creating a prompt with the same handle will fail, so we try to update instead\n try {\n const prompt = await this.create(payload);\n return {\n created: true,\n prompt,\n };\n } catch {\n const prompt = await this.update(handle, payload);\n\n return {\n created: false,\n prompt,\n };\n }\n }\n\n /**\n * Sync a prompt with local content, handling conflicts and version management\n * You probably don't need to use this method directly.\n */\n async sync(params: {\n name: string;\n configData: ConfigData;\n parameters?: RuntimeParameters;\n localVersion?: number;\n commitMessage?: string;\n }): Promise<SyncResult> {\n // openapi-fetch returns `{ data?, error?, response }`; we only need\n // these fields from the response so an explicit shape keeps the\n // no-redundant-type-constituents lint happy (the generic POST return is\n // widened to `any` by the generated types).\n interface SyncApiResponse {\n data?: unknown;\n error?: unknown;\n response?: { status?: number };\n }\n let response: SyncApiResponse | undefined;\n try {\n response = await this.apiClient.POST(\"/api/v1/prompts/{id}/sync\", {\n params: { path: { id: params.name } },\n body: {\n configData: params.configData,\n parameters: params.parameters ?? {},\n localVersion: params.localVersion,\n commitMessage: params.commitMessage,\n },\n });\n } catch (error) {\n // Transport-level failures (network errors, timeouts, unresolved DNS)\n // surface here. Preserve the underlying message so the user knows\n // whether the API is reachable.\n const message = formatApiErrorForOperation({\n operation: \"sync prompt\",\n error: error,\n });\n throw new PromptsApiError(message, \"sync\", error);\n }\n\n if (response?.error) {\n const err: unknown = response.error;\n const status = response.response?.status ?? extractStatusFromResponse(err);\n const message = formatApiErrorMessage({ error: err, options: { status } });\n throw new PromptsApiError(`Failed to sync prompt: ${message}`, \"sync\", err);\n }\n\n // Validate the shape at the boundary so a malformed 2xx payload\n // (e.g. `{ action: undefined }`) surfaces as a PromptsApiError here\n // instead of crashing downstream code with a confusing stack trace.\n const parsed = syncResultSchema.safeParse(response?.data);\n if (!parsed.success) {\n throw new PromptsApiError(\n \"Failed to sync prompt: server returned an invalid response body\",\n \"sync\",\n response?.data ?? response,\n );\n }\n return {\n action: parsed.data.action,\n prompt: parsed.data.prompt as SyncResult[\"prompt\"],\n conflictInfo: parsed.data.conflictInfo as SyncResult[\"conflictInfo\"],\n };\n }\n}\n","import { type z } from \"zod\";\n\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nimport {\n type corePromptDataSchema,\n type promptMetadataSchema,\n type promptDataSchema,\n} from \"./schema\";\n\n/**\n * Type for template variables - supporting common data types\n */\nexport type TemplateVariables = Record<string, string | number | boolean | object | null>;\n\n/**\n * Core data needed for prompt functionality\n */\nexport type CorePromptData = z.infer<typeof corePromptDataSchema>;\n\n/**\n * Optional metadata for identification and tracing\n */\nexport type PromptMetadata = z.infer<typeof promptMetadataSchema>;\n\n/**\n * Combined type for creating prompts\n */\nexport type PromptData = z.infer<typeof promptDataSchema>;\n\n// Extract API types from OpenAPI schema for backwards compatibility\nexport type CreatePromptBody = NonNullable<\n paths[\"/api/v1/prompts\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdatePromptBody = NonNullable<\n paths[\"/api/v1/prompts/{id}\"][\"put\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type PromptResponse = NonNullable<\n paths[\"/api/v1/prompts/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>;\n\n// Extract the PromptScope type from the API client\nexport type PromptScope =\n paths[\"/api/v1/prompts\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"][\"scope\"];\n\n// Extract tag definition types from the OpenAPI schema\nexport type TagDefinition = NonNullable<\n paths[\"/api/v1/prompts/tags\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type CreatedTag = NonNullable<\n paths[\"/api/v1/prompts/tags\"][\"post\"][\"responses\"][\"201\"][\"content\"][\"application/json\"]\n>;\n\n/**\n * Fetch policy for prompt retrieval.\n * Controls how prompts are fetched and cached.\n */\nexport enum FetchPolicy {\n /** Use local file if available, otherwise fetch from API (default) */\n MATERIALIZED_FIRST = \"MATERIALIZED_FIRST\",\n /** Always try API first, fall back to materialized */\n ALWAYS_FETCH = \"ALWAYS_FETCH\",\n /** Fetch every X minutes, use materialized between fetches */\n CACHE_TTL = \"CACHE_TTL\",\n /** Never fetch, use materialized files only */\n MATERIALIZED_ONLY = \"MATERIALIZED_ONLY\",\n}\n","import { type InternalConfig } from \"@/client-sdk/types\";\n\nimport { PromptsError } from \"./errors\";\nimport { LocalPromptsService } from \"./local-prompts.service\";\nimport { Prompt } from \"./prompt\";\nimport { PromptsApiService, type AssignTagResult } from \"./prompts-api.service\";\nimport type {\n CreatePromptBody,\n UpdatePromptBody,\n PromptData,\n TagDefinition,\n CreatedTag,\n} from \"./types\";\nimport { FetchPolicy } from \"./types\";\n\n/**\n * Options for fetching a prompt.\n */\nexport interface GetPromptOptions {\n /** Specific version to fetch */\n version?: string;\n /** Tag to fetch (e.g., \"production\", \"staging\", or a custom tag) */\n tag?: string;\n /** Fetch policy to use */\n fetchPolicy?: FetchPolicy;\n /** Cache TTL in minutes (only used with CACHE_TTL policy) */\n cacheTtlMinutes?: number;\n}\n\ninterface CacheEntry {\n data: PromptData;\n timestamp: number;\n}\n\ninterface PromptsFacadeDependencies {\n promptsApiService: PromptsApiService;\n localPromptsService: LocalPromptsService;\n}\n\n/**\n * Facade for prompt operations in the LangWatch SDK.\n * Provides a simplified interface for common prompt management tasks.\n */\nexport class PromptsFacade implements Pick<PromptsApiService, \"sync\" | \"delete\"> {\n private readonly promptsApiService: PromptsApiService;\n private readonly localPromptsService: LocalPromptsService;\n private readonly cache = new Map<string, CacheEntry>();\n readonly tags: {\n assign(id: string, params: { tag: string; versionId: string }): Promise<AssignTagResult>;\n list(): Promise<TagDefinition[]>;\n create(params: { name: string }): Promise<CreatedTag>;\n delete(tagName: string): Promise<void>;\n rename(oldName: string, newName: string): Promise<void>;\n };\n\n constructor(config: InternalConfig & PromptsFacadeDependencies) {\n this.promptsApiService = config.promptsApiService ?? new PromptsApiService(config);\n this.localPromptsService = config.localPromptsService ?? new LocalPromptsService();\n this.tags = {\n assign: (id, { tag, versionId }) => this.promptsApiService.assignTag({ id, tag, versionId }),\n list: () => this.promptsApiService.listTags(),\n create: ({ name }) => this.promptsApiService.createTag({ name }),\n delete: (tagName) => this.promptsApiService.deleteTag(tagName),\n rename: (oldName, newName) =>\n this.promptsApiService.renameTag({ tag: oldName, name: newName }),\n };\n }\n\n /**\n * Creates a new prompt.\n * @param data The prompt creation payload.\n * @returns The created Prompt instance.\n * @throws {PromptsError} If the API call fails.\n */\n async create(data: CreatePromptBody): Promise<Prompt> {\n const serverPrompt = await this.promptsApiService.create(data);\n return new Prompt(serverPrompt);\n }\n\n /**\n * Retrieves a prompt by handle or ID (supports `handle:tag` shorthand, parsed server-side).\n * @param handleOrId The prompt's handle, unique identifier, or `handle:tag` shorthand.\n * @param options Optional parameters for the request.\n * @returns The Prompt instance.\n * @throws {PromptsError} If the prompt is not found or the API call fails.\n */\n async get(handleOrId: string, options?: GetPromptOptions): Promise<Prompt> {\n const fetchPolicy = options?.fetchPolicy ?? FetchPolicy.MATERIALIZED_FIRST;\n\n switch (fetchPolicy) {\n case FetchPolicy.MATERIALIZED_ONLY:\n return this.getMaterializedOnly(handleOrId);\n\n case FetchPolicy.ALWAYS_FETCH:\n return this.getAlwaysFetch(handleOrId, options);\n\n case FetchPolicy.CACHE_TTL:\n return this.getCacheTtl(handleOrId, options);\n\n case FetchPolicy.MATERIALIZED_FIRST:\n default:\n return this.getMaterializedFirst(handleOrId, options);\n }\n }\n\n private async getMaterializedFirst(\n handleOrId: string,\n options?: GetPromptOptions,\n ): Promise<Prompt> {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n const serverPrompt = await this.promptsApiService.get(handleOrId, options);\n return new Prompt(serverPrompt);\n }\n\n private async getAlwaysFetch(handleOrId: string, options?: GetPromptOptions): Promise<Prompt> {\n try {\n const serverPrompt = await this.promptsApiService.get(handleOrId, options);\n return new Prompt(serverPrompt);\n } catch {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n throw new PromptsError(`Prompt \"${handleOrId}\" not found locally or on server`);\n }\n }\n\n private async getMaterializedOnly(handleOrId: string): Promise<Prompt> {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n throw new PromptsError(`Prompt \"${handleOrId}\" not found in materialized files`);\n }\n\n private buildCacheKey(handleOrId: string, options?: GetPromptOptions): string {\n const tagSegment = options?.tag != null ? `::tag:${options.tag}` : \"\";\n return `${handleOrId}::version:${options?.version ?? \"\"}${tagSegment}`;\n }\n\n private async getCacheTtl(handleOrId: string, options?: GetPromptOptions): Promise<Prompt> {\n const cacheKey = this.buildCacheKey(handleOrId, options);\n const ttlMs = (options?.cacheTtlMinutes ?? 5) * 60 * 1000;\n const cached = this.cache.get(cacheKey);\n const now = Date.now();\n\n if (cached && now - cached.timestamp < ttlMs) {\n return new Prompt(cached.data);\n }\n\n try {\n const serverPrompt = await this.promptsApiService.get(handleOrId, options);\n this.cache.set(cacheKey, { data: serverPrompt, timestamp: now });\n return new Prompt(serverPrompt);\n } catch {\n const localPrompt = await this.localPromptsService.get(handleOrId);\n if (localPrompt) {\n return new Prompt(localPrompt);\n }\n throw new PromptsError(`Prompt \"${handleOrId}\" not found locally or on server`);\n }\n }\n\n /**\n * Retrieves all prompts.\n * @returns Array of Prompt instances.\n * @throws {PromptsError} If the API call fails.\n */\n async getAll(): Promise<Prompt[]> {\n const serverPrompts = await this.promptsApiService.getAll();\n return serverPrompts.map((prompt) => new Prompt(prompt));\n }\n\n /**\n * Updates an existing prompt.\n * @param handleOrId The prompt's handle or unique identifier.\n * @param newData The update payload.\n * @returns The updated Prompt instance.\n * @throws {PromptsError} If the API call fails.\n */\n async update(handleOrId: string, newData: UpdatePromptBody): Promise<Prompt> {\n const serverPrompt = await this.promptsApiService.update(handleOrId, newData);\n return new Prompt(serverPrompt);\n }\n\n get delete() {\n return this.promptsApiService.delete.bind(this.promptsApiService);\n }\n\n /**\n * Delegated method to the prompts API service.\n */\n get sync() {\n return this.promptsApiService.sync.bind(this.promptsApiService);\n }\n}\n","/**\n * Errors for the Dataset API\n */\n\n/**\n * Base error for all dataset operations.\n */\nexport class DatasetError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DatasetError\";\n }\n}\n\n/**\n * Thrown when a dataset is not found (404).\n */\nexport class DatasetNotFoundError extends DatasetError {\n constructor(slugOrId: string) {\n super(`Dataset not found: ${slugOrId}`);\n this.name = \"DatasetNotFoundError\";\n }\n}\n\n/**\n * Error from the Dataset API with operation context.\n * Aligns with PromptsApiError pattern: includes operation and originalError fields.\n */\nexport class DatasetApiError extends DatasetError {\n readonly status: number;\n readonly operation: string;\n readonly originalError?: unknown;\n\n constructor(message: string, status: number, operation: string, originalError?: unknown) {\n super(message);\n this.name = \"DatasetApiError\";\n this.status = status;\n this.operation = operation;\n this.originalError = originalError;\n }\n}\n\n/**\n * Thrown for client-side validation failures (e.g. empty name, no update fields, empty entries).\n * Distinguished from DatasetApiError which represents server-side errors.\n */\nexport class DatasetValidationError extends DatasetError {\n constructor(message: string) {\n super(message);\n this.name = \"DatasetValidationError\";\n }\n}\n\n/**\n * Thrown when a dataset operation exceeds the plan limit (403).\n * The message includes the upgrade/subscription URL from the server.\n */\nexport class DatasetPlanLimitError extends DatasetError {\n readonly limitType: string;\n readonly current?: number;\n readonly max?: number;\n\n constructor(message: string, originalError?: unknown) {\n super(message);\n this.name = \"DatasetPlanLimitError\";\n\n if (originalError != null && typeof originalError === \"object\") {\n const err = originalError as Record<string, unknown>;\n this.limitType = typeof err.limitType === \"string\" ? err.limitType : \"datasets\";\n this.current = typeof err.current === \"number\" ? err.current : undefined;\n this.max = typeof err.max === \"number\" ? err.max : undefined;\n } else {\n this.limitType = \"datasets\";\n }\n }\n}\n","/**\n * How a whole-dataset read sizes its pages: by bytes, since a row can hold inline images of\n * many megabytes. Page sizes are powers of two, so halving or doubling one keeps the rows\n * already read a whole number of pages and the next page number follows from that count.\n */\n\n/** Rows asked for on the first page: small, since the size of the rows is not known yet. */\nexport const RECORDS_PAGE_LIMIT_START = 16;\n\n/** The most rows asked for on any page. */\nexport const RECORDS_PAGE_LIMIT_MAX = 512;\n\n/** The page size, in bytes, a whole-dataset read steers toward. */\nexport const RECORDS_PAGE_TARGET_BYTES = 16 * 1024 * 1024;\n\n/** Datasets asked for per page when looking up one dataset's metadata. */\nexport const DATASETS_PAGE_LIMIT = 1000;\n\n/** The error envelope of a response body, which some routes nest under `error`. */\nfunction envelopeOf(error: unknown): Record<string, unknown> | undefined {\n if (error === null || typeof error !== \"object\") return undefined;\n const body = error as Record<string, unknown>;\n return body.error !== null && typeof body.error === \"object\"\n ? (body.error as Record<string, unknown>)\n : body;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n const envelope = envelopeOf(error);\n return [envelope?.code, envelope?.type, envelope?.kind].find(\n (candidate): candidate is string => typeof candidate === \"string\" && candidate.length > 0,\n );\n}\n\n/** The smaller page size a refusal names in `meta.suggestedLimit`, when it names one. */\nexport function suggestedLimitOf(error: unknown): number | undefined {\n const meta = envelopeOf(error)?.meta;\n if (meta === null || typeof meta !== \"object\") return undefined;\n const suggested = (meta as Record<string, unknown>).suggestedLimit;\n return typeof suggested === \"number\" && Number.isInteger(suggested) ? suggested : undefined;\n}\n\n/**\n * Whether the server refused a records page for its size. The platform answers 413 with the\n * code `dataset_page_too_large`. Any 413 is read the same way, and so is a 400 or 422 whose\n * code names a size refusal.\n */\nexport function isPageTooLarge({ status, error }: { status: number; error: unknown }): boolean {\n if (status === 413) return true;\n if (status !== 400 && status !== 422) return false;\n return errorCode(error)?.includes(\"too_large\") ?? false;\n}\n\n/**\n * The page size of a whole-dataset read, adjusted after every page. A refused page halves\n * it. An accepted page halves it while the next one would pass the byte target, and doubles\n * it while the next one would stay under the target and start where this one ended.\n */\nexport class PageSizer {\n limit = RECORDS_PAGE_LIMIT_START;\n\n // A grown page can be refused when the server's cap is under the byte target. Each such\n // refusal doubles the pages read before the next attempt to grow, so a dataset of evenly\n // large rows costs only a few refused requests.\n private grown = false;\n private growthBackoffPages = 1;\n private pagesUntilGrowth = 0;\n\n /** The 1-based page that starts right after the rows already read. */\n pageAfter(rowsRead: number): number {\n return rowsRead / this.limit + 1;\n }\n\n /**\n * Lowers the page size after a refusal: to the size the refusal suggests when the rows\n * already read are a whole number of pages of that size, and to half otherwise.\n */\n shrinkAfterRefusal({ suggested, rowsRead }: { suggested?: number; rowsRead: number }): void {\n const usable =\n suggested !== undefined &&\n suggested >= 1 &&\n suggested < this.limit &&\n rowsRead % suggested === 0;\n this.limit = usable ? suggested : Math.max(1, this.limit / 2);\n if (this.grown) {\n this.pagesUntilGrowth = this.growthBackoffPages;\n this.growthBackoffPages *= 2;\n this.grown = false;\n }\n }\n\n /** Picks the next page size from the size of the page just read. */\n accept({\n rowsRead,\n pageRows,\n pageBytes,\n }: {\n rowsRead: number;\n pageRows: number;\n pageBytes: number;\n }): void {\n if (this.grown) this.growthBackoffPages = 1;\n\n const rowBytes = Math.max(1, Math.floor(pageBytes / Math.max(1, pageRows)));\n const fits = (limit: number) => limit * rowBytes <= RECORDS_PAGE_TARGET_BYTES;\n let next = this.limit;\n while (next > 1 && !fits(next)) next /= 2;\n while (\n this.pagesUntilGrowth === 0 &&\n next < RECORDS_PAGE_LIMIT_MAX &&\n rowsRead % (next * 2) === 0 &&\n fits(next * 2)\n ) {\n next *= 2;\n }\n\n this.pagesUntilGrowth = Math.max(0, this.pagesUntilGrowth - 1);\n this.grown = next > this.limit;\n this.limit = next;\n }\n}\n","import { LANGWATCH_SDK_NAME_CLIENT, LANGWATCH_SDK_VERSION } from \"@/internal/constants\";\nimport { getLangWatchTracer } from \"@/observability-sdk/tracer\";\n\nexport const tracer = getLangWatchTracer(\n `${LANGWATCH_SDK_NAME_CLIENT}.datasets`,\n LANGWATCH_SDK_VERSION,\n);\n","import { z } from \"zod\";\n\nimport { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\nimport { type LangwatchApiClient } from \"@/internal/api/client\";\nimport { isLangWatchHandledError } from \"@/internal/api/errors\";\nimport { buildRequestHeaders } from \"@/internal/api/request-headers\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { type operations } from \"@/internal/generated/openapi/api-client\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\nimport { type Logger } from \"@/logger\";\n\nimport {\n DatasetApiError,\n DatasetNotFoundError,\n DatasetPlanLimitError,\n DatasetValidationError,\n} from \"./errors\";\nimport { DATASETS_PAGE_LIMIT, PageSizer, isPageTooLarge, suggestedLimitOf } from \"./paged-records\";\nimport { tracer } from \"./tracing\";\nimport {\n type Dataset,\n type DatasetColumnType,\n type DatasetEntry,\n type DatasetMetadata,\n type GetDatasetApiResponse,\n type GetDatasetOptions,\n type ListDatasetsOptions,\n type ListDatasetsApiResponse,\n type ListRecordsOptions,\n type ListRecordsApiResponse,\n type CreateDatasetOptions,\n type UpdateDatasetOptions,\n type CreateFromUploadOptions,\n type CreateFromUploadResponse,\n type BatchCreateRecordsResponse,\n type DeleteRecordsResponse,\n type UploadResponse,\n type DatasetRecordResponse,\n} from \"./types\";\n\ntype ApiColumnType = NonNullable<\n operations[\"postApiDataset\"][\"requestBody\"][\"content\"][\"application/json\"][\"columnTypes\"]\n>[number][\"type\"];\n\n/** Every column type the API accepts; `satisfies` keeps it in step with the generated contract. */\nconst API_COLUMN_TYPES = {\n string: \"string\",\n boolean: \"boolean\",\n number: \"number\",\n date: \"date\",\n list: \"list\",\n json: \"json\",\n spans: \"spans\",\n rag_contexts: \"rag_contexts\",\n chat_messages: \"chat_messages\",\n annotations: \"annotations\",\n evaluations: \"evaluations\",\n image: \"image\",\n file: \"file\",\n} as const satisfies { [Type in ApiColumnType]: Type };\n\nconst apiColumnTypeSchema = z.enum(API_COLUMN_TYPES);\n\n/** The public `type` is a plain string; the API takes one of its column types, checked here. */\nfunction toApiColumnTypes(\n columnTypes: DatasetColumnType[],\n): { name: string; type: ApiColumnType }[] {\n return columnTypes.map(({ name, type }) => {\n const parsed = apiColumnTypeSchema.safeParse(type);\n if (!parsed.success) {\n throw new DatasetValidationError(\n `Column \"${name}\" has type \"${type}\", which is not one of: ${apiColumnTypeSchema.options.join(\", \")}`,\n );\n }\n return { name, type: parsed.data };\n });\n}\n\ntype DatasetServiceConfig = {\n langwatchApiClient: LangwatchApiClient;\n logger: Logger;\n endpoint: string;\n apiKey: string;\n};\n\n/**\n * Service for managing dataset resources via the LangWatch API.\n */\nexport class DatasetService {\n private readonly config: DatasetServiceConfig;\n\n constructor(config: DatasetServiceConfig) {\n this.config = { ...config, endpoint: resolveEndpoint(config.endpoint) };\n\n /**\n * Wraps the service in a tracing proxy that automatically creates\n * OpenTelemetry spans for all public methods.\n */\n return createTracingProxy(this as DatasetService, tracer);\n }\n\n /** Handles API errors by mapping status codes to appropriate error types. */\n private handleApiError(\n operation: string,\n error: unknown,\n status: number,\n slugOrId?: string,\n ): never {\n if (status === 404 && slugOrId) {\n throw new DatasetNotFoundError(slugOrId);\n }\n\n if (status === 403) {\n const errorMessage = this.extractErrorMessage(error, status);\n throw new DatasetPlanLimitError(errorMessage, error);\n }\n\n const errorMessage = this.extractErrorMessage(error, status);\n\n // NO handled-error throw here, deliberately.\n throw new DatasetApiError(`Failed to ${operation}: ${errorMessage}`, status, operation, error);\n }\n\n /**\n * Extracts a human-readable error message from an API error response.\n */\n private extractErrorMessage(error: unknown, status: number): string {\n return formatApiErrorMessage({ error: error, options: { status } });\n }\n\n /**\n * Puts a transport-thrown domain error back into the `{ error, response }` envelope this\n * service reads.\n */\n private async asResponseEnvelope(\n request: Promise<{ data?: unknown; error?: unknown; response: { status: number } }>,\n ): Promise<{ data?: unknown; error?: unknown; response: { status: number } }> {\n try {\n return await request;\n } catch (error) {\n if (isLangWatchHandledError(error)) {\n return { error: error.body, response: { status: error.httpStatus } };\n }\n throw error;\n }\n }\n\n /**\n * Unwraps an API response, throwing a mapped error if the response contains an error.\n * Centralizes the repeated `if (error) handleApiError; return data` pattern.\n */\n private unwrapResponse<T>(\n response: { data?: unknown; error?: unknown; response: { status: number } },\n operation: string,\n slugOrId?: string,\n ): T {\n if (response.error) {\n this.handleApiError(operation, response.error, response.response.status, slugOrId);\n }\n if (response.data === undefined) {\n this.handleApiError(operation, response.error, response.response.status, slugOrId);\n }\n return response.data as T;\n }\n\n /**\n * Gets a dataset with all its entries, read page by page so its size is not bound by one\n * response. A server without the records endpoint is asked for it in one request instead.\n * @param slugOrId - The slug or ID of the dataset\n */\n async getDataset<T extends Record<string, unknown> = Record<string, unknown>>(\n slugOrId: string,\n _options?: GetDatasetOptions,\n ): Promise<Dataset<T>> {\n this.config.logger.debug(`Fetching dataset: ${slugOrId}`);\n\n // No records page means either the dataset does not exist or the server has no records\n // endpoint: the single request tells the two apart. A server that sends no dataset with\n // its pages is asked for it through the datasets list, then through the single request.\n const read = await this.readAllRecords(slugOrId);\n const metadata =\n read && (read.dataset ?? (await this.findDatasetMetadata(slugOrId, read.records)));\n const data = metadata\n ? { ...metadata, data: read.records }\n : await this.getDatasetInline(slugOrId);\n\n const entries: DatasetEntry<T>[] = data.data.map((item) => ({\n id: item.id,\n datasetId: item.datasetId,\n projectId: item.projectId,\n entry: item.entry as T,\n createdAt: item.createdAt,\n updatedAt: item.updatedAt,\n }));\n\n this.config.logger.debug(`Fetched dataset ${slugOrId} with ${entries.length} entries`);\n\n return {\n id: data.id,\n name: data.name,\n slug: data.slug,\n columnTypes: data.columnTypes,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n entries,\n };\n }\n\n /** The dataset and its entries in one response. */\n private async getDatasetInline(slugOrId: string): Promise<GetDatasetApiResponse> {\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.GET(\"/api/v1/dataset/{slugOrId}\", {\n params: {\n path: {\n slugOrId,\n },\n },\n }) as Promise<{ data?: unknown; error?: unknown; response: { status: number } }>,\n );\n\n return this.unwrapResponse<GetDatasetApiResponse>(\n response,\n `fetch dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Reads every record of a dataset in order, one page at a time, with the dataset a page\n * names. Answers `null` when the records endpoint answers 404. A page refused as too large\n * is asked for again with the size the refusal suggests, or half the rows, down to one.\n */\n private async readAllRecords(\n slugOrId: string,\n ): Promise<{ records: DatasetRecordResponse[]; dataset?: DatasetMetadata } | null> {\n const records: DatasetRecordResponse[] = [];\n let dataset: DatasetMetadata | undefined;\n const sizer = new PageSizer();\n\n for (;;) {\n const limit = sizer.limit;\n const page = await this.readRecordsPage(slugOrId, sizer.pageAfter(records.length), limit);\n if (page === \"missing\") return null;\n if (\"refused\" in page) {\n sizer.shrinkAfterRefusal({ suggested: page.suggestedLimit, rowsRead: records.length });\n continue;\n }\n\n const rows = page.data ?? [];\n for (const row of rows) records.push(row);\n dataset ??= page.dataset;\n\n const total = page.pagination?.total;\n const readAll = typeof total === \"number\" && records.length >= total;\n if (rows.length < limit || readAll) return { records, dataset };\n\n sizer.accept({\n rowsRead: records.length,\n pageRows: rows.length,\n pageBytes: JSON.stringify(rows).length,\n });\n }\n }\n\n /**\n * One page of records. Answers `\"missing\"` on a 404, and the refusal when the server\n * refuses the page for its size and it holds more than one row.\n */\n private async readRecordsPage(\n slugOrId: string,\n page: number,\n limit: number,\n ): Promise<ListRecordsApiResponse | \"missing\" | { refused: true; suggestedLimit?: number }> {\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.GET(\"/api/v1/dataset/{slugOrId}/records\", {\n params: { path: { slugOrId }, query: { page, limit } },\n }),\n );\n if (!response.error && response.data !== undefined) {\n return response.data as ListRecordsApiResponse;\n }\n\n const status = response.response.status;\n if (status === 404) return \"missing\";\n if (limit > 1 && isPageTooLarge({ status, error: response.error })) {\n return { refused: true, suggestedLimit: suggestedLimitOf(response.error) };\n }\n this.handleApiError(`fetch dataset \"${slugOrId}\"`, response.error, status, slugOrId);\n }\n\n /**\n * The dataset's metadata from the datasets list, or `undefined` when it is not listed. The\n * records name their dataset by id, which settles a slug that is another dataset's id.\n */\n private async findDatasetMetadata(\n slugOrId: string,\n records: DatasetRecordResponse[],\n ): Promise<DatasetMetadata | undefined> {\n const datasetId = records[0]?.datasetId;\n\n for (let page = 1; ; page++) {\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.GET(\"/api/v1/dataset\", {\n params: { query: { page, limit: DATASETS_PAGE_LIMIT } },\n }),\n );\n if (response.response.status === 404) return undefined;\n\n const list = this.unwrapResponse<ListDatasetsApiResponse>(\n response,\n `fetch dataset \"${slugOrId}\"`,\n );\n const datasets = list.data ?? [];\n const match = datasets.find((dataset) =>\n datasetId ? dataset.id === datasetId : dataset.id === slugOrId || dataset.slug === slugOrId,\n );\n if (match) return match;\n\n const totalPages = list.pagination?.totalPages;\n if (datasets.length === 0 || typeof totalPages !== \"number\" || page >= totalPages) {\n return undefined;\n }\n }\n }\n\n /** Whether a dataset exists, without reading its entries. */\n private async datasetExists(slugOrId: string): Promise<boolean> {\n try {\n await this.listRecords(slugOrId, { page: 1, limit: 1 });\n return true;\n } catch (error) {\n if (!(error instanceof DatasetNotFoundError)) throw error;\n }\n\n try {\n await this.getDatasetInline(slugOrId);\n return true;\n } catch (error) {\n if (error instanceof DatasetNotFoundError) return false;\n throw error;\n }\n }\n\n /**\n * Lists all datasets for the project, with optional pagination.\n */\n async listDatasets(options?: ListDatasetsOptions): Promise<ListDatasetsApiResponse> {\n this.config.logger.debug(\"Listing datasets\");\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.GET(\"/api/v1/dataset\", {\n params: {\n query: {\n page: options?.page,\n limit: options?.limit,\n },\n },\n }),\n );\n\n return this.unwrapResponse<ListDatasetsApiResponse>(response, \"list datasets\");\n }\n\n /**\n * Creates a new dataset.\n */\n async createDataset(options: CreateDatasetOptions): Promise<DatasetMetadata> {\n this.config.logger.debug(`Creating dataset: ${options.name}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.POST(\"/api/v1/dataset\", {\n body: {\n name: options.name,\n columnTypes: toApiColumnTypes(options.columnTypes ?? []),\n },\n }),\n );\n\n return this.unwrapResponse<DatasetMetadata>(response, `create dataset \"${options.name}\"`);\n }\n\n /**\n * Updates a dataset by its slug or ID.\n */\n async updateDataset(slugOrId: string, options: UpdateDatasetOptions): Promise<DatasetMetadata> {\n this.config.logger.debug(`Updating dataset: ${slugOrId}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.PATCH(\"/api/v1/dataset/{slugOrId}\", {\n params: {\n path: { slugOrId },\n },\n body: {\n name: options.name,\n columnTypes: options.columnTypes && toApiColumnTypes(options.columnTypes),\n },\n }),\n );\n\n return this.unwrapResponse<DatasetMetadata>(response, `update dataset \"${slugOrId}\"`, slugOrId);\n }\n\n /**\n * Deletes (archives) a dataset by its slug or ID.\n */\n async deleteDataset(slugOrId: string): Promise<DatasetMetadata> {\n this.config.logger.debug(`Deleting dataset: ${slugOrId}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.DELETE(\"/api/v1/dataset/{slugOrId}\", {\n params: {\n path: { slugOrId },\n },\n }),\n );\n\n return this.unwrapResponse<DatasetMetadata>(response, `delete dataset \"${slugOrId}\"`, slugOrId);\n }\n\n /**\n * Creates records in a dataset in batch.\n */\n async createRecords(\n slugOrId: string,\n entries: Record<string, unknown>[],\n ): Promise<BatchCreateRecordsResponse> {\n this.config.logger.debug(`Creating ${entries.length} records in dataset: ${slugOrId}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.POST(\"/api/v1/dataset/{slugOrId}/records\", {\n params: {\n path: { slugOrId },\n },\n body: { entries },\n }),\n );\n\n return this.unwrapResponse<BatchCreateRecordsResponse>(\n response,\n `create records in dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Updates a single record in a dataset.\n */\n async updateRecord(\n slugOrId: string,\n recordId: string,\n entry: Record<string, unknown>,\n ): Promise<DatasetRecordResponse> {\n this.config.logger.debug(`Updating record ${recordId} in dataset: ${slugOrId}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.PATCH(\"/api/v1/dataset/{slugOrId}/records/{recordId}\", {\n params: {\n path: { slugOrId, recordId },\n },\n body: { entry },\n }),\n );\n\n return this.unwrapResponse<DatasetRecordResponse>(\n response,\n `update record \"${recordId}\" in dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Deletes records from a dataset by IDs.\n */\n async deleteRecords(slugOrId: string, recordIds: string[]): Promise<DeleteRecordsResponse> {\n this.config.logger.debug(`Deleting ${recordIds.length} records from dataset: ${slugOrId}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.DELETE(\"/api/v1/dataset/{slugOrId}/records\", {\n params: {\n path: { slugOrId },\n },\n body: { recordIds },\n }),\n );\n\n return this.unwrapResponse<DeleteRecordsResponse>(\n response,\n `delete records from dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * @param slugOrId - The slug or ID of the dataset\n * @param options - Pagination options (page, limit)\n * @returns Paginated list of records\n */\n async listRecords(\n slugOrId: string,\n options?: ListRecordsOptions,\n ): Promise<ListRecordsApiResponse> {\n this.config.logger.debug(`Listing records for dataset: ${slugOrId}`);\n\n const response = await this.asResponseEnvelope(\n this.config.langwatchApiClient.GET(\"/api/v1/dataset/{slugOrId}/records\", {\n params: {\n path: { slugOrId },\n query: {\n page: options?.page,\n limit: options?.limit,\n },\n },\n }),\n );\n\n return this.unwrapResponse<ListRecordsApiResponse>(\n response,\n `list records in dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n\n /**\n * Sends a multipart/form-data request using raw fetch, since openapi-fetch\n * hardcodes content-type: application/json and file uploads must bypass it.\n */\n private async fetchMultipart<T>(\n path: string,\n formData: FormData,\n operation: string,\n slugOrId?: string,\n ): Promise<T> {\n const { endpoint, apiKey } = this.config;\n const url = `${endpoint}${path}`;\n\n const response = await langwatchFetch(url, {\n method: \"POST\",\n headers: buildRequestHeaders({ apiKey }),\n body: formData,\n });\n\n if (!response.ok) {\n const rawBody = await response.text();\n let errorBody: unknown = rawBody;\n if (rawBody) {\n try {\n errorBody = JSON.parse(rawBody);\n } catch {\n // Keep the plain-text body.\n void 0;\n }\n }\n\n this.handleApiError(operation, errorBody, response.status, slugOrId);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * Creates a new dataset from a file upload.\n * @param options - The dataset name and file to upload\n * @returns The created dataset metadata with record count\n */\n async createDatasetFromUpload(\n options: CreateFromUploadOptions,\n ): Promise<CreateFromUploadResponse> {\n this.config.logger.debug(`Creating dataset from upload: ${options.name}`);\n\n const formData = new FormData();\n formData.append(\"name\", options.name);\n formData.append(\"file\", options.file);\n\n return this.fetchMultipart<CreateFromUploadResponse>(\n \"/api/v1/dataset/upload\",\n formData,\n `create dataset from upload \"${options.name}\"`,\n );\n }\n\n /**\n * @param slugOrId - The slug or ID of the dataset\n * @param file - The file to upload (File or Blob)\n * @param ifExists - Strategy when dataset exists: \"append\" (default), \"replace\", or \"error\"\n */\n async uploadWithStrategy(\n slugOrId: string,\n file: File | Blob,\n ifExists: \"append\" | \"replace\" | \"error\" = \"append\",\n ): Promise<UploadResponse> {\n switch (ifExists) {\n case \"append\":\n return this._uploadAppend(slugOrId, file);\n case \"replace\":\n return this._uploadReplace(slugOrId, file);\n case \"error\":\n return this._uploadError(slugOrId, file);\n }\n }\n\n /**\n * Converts a CreateFromUploadResponse to the unified UploadResponse shape.\n */\n private toUploadResponse(result: CreateFromUploadResponse): UploadResponse {\n return {\n dataset: {\n id: result.id,\n name: result.name,\n slug: result.slug,\n columnTypes: result.columnTypes,\n createdAt: result.createdAt,\n updatedAt: result.updatedAt,\n },\n recordsCreated: result.recordsCreated,\n datasetId: result.id,\n };\n }\n\n /**\n * Append strategy: try uploading to existing dataset; if not found, create from file.\n */\n private async _uploadAppend(slugOrId: string, file: File | Blob): Promise<UploadResponse> {\n try {\n return await this.uploadFile(slugOrId, file);\n } catch (error) {\n if (error instanceof DatasetNotFoundError) {\n return this.toUploadResponse(await this.createDatasetFromUpload({ name: slugOrId, file }));\n }\n throw error;\n }\n }\n\n /**\n * Replace strategy: if the dataset exists, delete all records then upload;\n * if not found, create it from the file.\n */\n private async _uploadReplace(slugOrId: string, file: File | Blob): Promise<UploadResponse> {\n try {\n if (!(await this.datasetExists(slugOrId))) throw new DatasetNotFoundError(slugOrId);\n await this._deleteAllRecords(slugOrId);\n return await this.uploadFile(slugOrId, file);\n } catch (error) {\n if (error instanceof DatasetNotFoundError) {\n return this.toUploadResponse(await this.createDatasetFromUpload({ name: slugOrId, file }));\n }\n throw error;\n }\n }\n\n /**\n * Error strategy: if dataset exists, throw 409; if not found, create from file.\n */\n private async _uploadError(slugOrId: string, file: File | Blob): Promise<UploadResponse> {\n if (await this.datasetExists(slugOrId)) {\n throw new DatasetApiError(`Dataset already exists: ${slugOrId}`, 409, \"upload\");\n }\n\n return this.toUploadResponse(await this.createDatasetFromUpload({ name: slugOrId, file }));\n }\n\n /**\n * Deletes all records from a dataset by iterating through pages.\n * Always fetches page 1 since records shift after deletion.\n * Includes a safety valve to prevent infinite loops.\n */\n private async _deleteAllRecords(slugOrId: string): Promise<void> {\n const BATCH_SIZE = 1000;\n const MAX_DELETE_ITERATIONS = 100;\n\n let iteration = 0;\n while (iteration < MAX_DELETE_ITERATIONS) {\n const page = await this.listRecords(slugOrId, { page: 1, limit: BATCH_SIZE });\n if (page.data.length === 0) {\n return;\n }\n\n const ids = page.data.map((record) => record.id);\n await this.deleteRecords(slugOrId, ids);\n iteration++;\n }\n\n throw new DatasetApiError(\n `Failed to delete all records from dataset \"${slugOrId}\": exceeded ${MAX_DELETE_ITERATIONS} iterations`,\n 0,\n `delete all records from dataset \"${slugOrId}\"`,\n );\n }\n\n /**\n * Uploads a file to an existing dataset.\n */\n async uploadFile(slugOrId: string, file: File | Blob): Promise<UploadResponse> {\n this.config.logger.debug(`Uploading file to dataset: ${slugOrId}`);\n\n const formData = new FormData();\n formData.append(\"file\", file);\n\n return this.fetchMultipart<UploadResponse>(\n `/api/v1/dataset/${encodeURIComponent(slugOrId)}/upload`,\n formData,\n `upload file to dataset \"${slugOrId}\"`,\n slugOrId,\n );\n }\n}\n","import { type LangwatchApiClient } from \"@/internal/api/client\";\nimport { type Logger } from \"@/logger\";\n\nimport { DatasetService } from \"./dataset.service\";\nimport { DatasetValidationError } from \"./errors\";\nimport {\n type Dataset,\n type DatasetMetadata,\n type GetDatasetOptions,\n type ListDatasetsOptions,\n type ListDatasetsApiResponse,\n type ListRecordsOptions,\n type ListRecordsApiResponse,\n type CreateDatasetOptions,\n type UpdateDatasetOptions,\n type BatchCreateRecordsResponse,\n type DeleteRecordsResponse,\n type UploadResponse,\n type DatasetRecordResponse,\n} from \"./types\";\n\ntype DatasetsFacadeConfig = {\n langwatchApiClient: LangwatchApiClient;\n logger: Logger;\n endpoint: string;\n apiKey: string;\n};\n\n/**\n * Facade for dataset operations in the LangWatch SDK.\n * @example\n * const datasets = await langwatch.datasets.list();\n */\nexport class DatasetsFacade {\n readonly #datasetService: DatasetService;\n\n constructor(config: DatasetsFacadeConfig) {\n this.#datasetService = new DatasetService(config);\n }\n\n /**\n * Lists all datasets for the project, with optional pagination.\n * @param options - Pagination options (page, limit)\n * @returns Paginated list of datasets with metadata\n */\n list = (options?: ListDatasetsOptions): Promise<ListDatasetsApiResponse> => {\n return this.#datasetService.listDatasets(options);\n };\n\n /**\n * Creates a new dataset.\n * @param options - Dataset creation options (name, columnTypes)\n * @returns The created dataset metadata\n */\n create = (options: CreateDatasetOptions): Promise<DatasetMetadata> => {\n if (!options.name) {\n throw new DatasetValidationError(\"Dataset name must not be empty\");\n }\n const trimmedName = options.name.trim();\n if (trimmedName.length === 0) {\n throw new DatasetValidationError(\"Dataset name must not be empty\");\n }\n return this.#datasetService.createDataset(options);\n };\n\n /**\n * Fetches a dataset by its slug or ID, returning metadata and entries.\n * @param slugOrId - The slug or ID of the dataset to fetch\n * @param options - Optional configuration\n * @returns The dataset with metadata and entries\n */\n get = <T extends Record<string, unknown> = Record<string, unknown>>(\n slugOrId: string,\n options?: GetDatasetOptions,\n ): Promise<Dataset<T>> => {\n return this.#datasetService.getDataset<T>(slugOrId, options);\n };\n\n /**\n * Updates a dataset by its slug or ID.\n * @param slugOrId - The slug or ID of the dataset to update\n * @param options - Fields to update (name, columnTypes)\n * @returns The updated dataset metadata\n */\n update = (slugOrId: string, options: UpdateDatasetOptions): Promise<DatasetMetadata> => {\n if (options.name == null && options.columnTypes == null) {\n throw new DatasetValidationError(\n \"At least one field (name or columnTypes) must be provided for update\",\n );\n }\n return this.#datasetService.updateDataset(slugOrId, options);\n };\n\n /**\n * Deletes (archives) a dataset by its slug or ID.\n * @param slugOrId - The slug or ID of the dataset to delete\n * @returns The archived dataset metadata\n */\n delete = (slugOrId: string): Promise<DatasetMetadata> => {\n return this.#datasetService.deleteDataset(slugOrId);\n };\n\n /**\n * Creates records in a dataset in batch.\n * @param slugOrId - The slug or ID of the dataset\n * @param entries - Array of record entries to create\n * @returns The created records with IDs\n */\n createRecords = (\n slugOrId: string,\n entries: Record<string, unknown>[],\n ): Promise<BatchCreateRecordsResponse> => {\n if (!entries || entries.length === 0) {\n throw new DatasetValidationError(\"Entries must not be empty\");\n }\n return this.#datasetService.createRecords(slugOrId, entries);\n };\n\n /**\n * Updates a single record in a dataset.\n * @param slugOrId - The slug or ID of the dataset\n * @param recordId - The ID of the record to update\n * @param entry - The updated entry data\n * @returns The updated record\n */\n updateRecord = (\n slugOrId: string,\n recordId: string,\n entry: Record<string, unknown>,\n ): Promise<DatasetRecordResponse> => {\n return this.#datasetService.updateRecord(slugOrId, recordId, entry);\n };\n\n /**\n * Deletes records from a dataset by IDs.\n * @param slugOrId - The slug or ID of the dataset\n * @param recordIds - Array of record IDs to delete\n * @returns Object with the count of deleted records\n */\n deleteRecords = (slugOrId: string, recordIds: string[]): Promise<DeleteRecordsResponse> => {\n return this.#datasetService.deleteRecords(slugOrId, recordIds);\n };\n\n /**\n * Lists records in a dataset with optional pagination.\n * @param slugOrId - The slug or ID of the dataset\n * @param options - Pagination options (page, limit)\n * @returns Paginated list of records\n */\n listRecords = (\n slugOrId: string,\n options?: ListRecordsOptions,\n ): Promise<ListRecordsApiResponse> => {\n return this.#datasetService.listRecords(slugOrId, options);\n };\n\n /**\n * Uploads a file to a dataset with a strategy for existing datasets (default \"append\").\n * @param slugOrId - The slug or ID of the dataset\n * @param file - The file to upload (File or Blob)\n * @param options - Upload options including the ifExists strategy\n * @returns The upload result\n */\n upload = (\n slugOrId: string,\n file: File | Blob,\n options?: {\n ifExists?: \"append\" | \"replace\" | \"error\";\n },\n ): Promise<UploadResponse> => {\n if (!file) {\n throw new DatasetValidationError(\"File must be provided for upload\");\n }\n return this.#datasetService.uploadWithStrategy(slugOrId, file, options?.ifExists ?? \"append\");\n };\n}\n","/**\n * Experiment - Main class for running batch experiments\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport { trace, SpanStatusCode, ROOT_CONTEXT } from \"@opentelemetry/api\";\n\nimport type { LangwatchApiClient } from \"@/internal/api/client\";\nimport { buildRequestHeaders } from \"@/internal/api/request-headers\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\nimport type { Logger } from \"@/logger\";\nimport { ensureSetup } from \"@/observability-sdk/setup/node\";\nimport { createLangWatchSpan } from \"@/observability-sdk/span/implementation\";\nimport type { LangWatchSpan } from \"@/observability-sdk/span/types\";\n\nimport type { CapturedTargetOutput } from \"./comparison.ts\";\nimport {\n COMPARISON_EVALUATOR_SLUG,\n DEFAULT_COMPARISON_NAME,\n buildComparisonCandidates,\n buildComparisonData,\n buildComparisonSettings,\n comparisonEntryLabel,\n comparisonEntryStatus,\n describeSkippedComparison,\n renderTargetOutput,\n toComparisonVerdict,\n} from \"./comparison.ts\";\nimport {\n ExperimentInitError,\n TargetMetadataConflictError,\n ComparisonError,\n EvaluatorError,\n} from \"./errors/index.ts\";\nimport {\n printExperimentSummary,\n type SummaryEntry,\n type SummaryEvaluation,\n} from \"./experiment-summary.ts\";\nimport { generateHumanReadableId } from \"./humanReadableId.ts\";\nimport {\n describeRefusedEntry,\n endsTheRun,\n halveLogResultsBody,\n splitLogResultsBody,\n} from \"./log-results-batching.ts\";\nimport type {\n Batch,\n BatchEntry,\n ComparisonOptions,\n ComparisonVerdict,\n ExperimentEvaluationResult,\n TargetInfo,\n TargetMetadata,\n ExperimentInitOptions,\n LogOptions,\n ExperimentEvaluateOptions,\n RunOptions,\n RunCallback,\n RunContext,\n ExperimentInitResponse,\n LogResultsRequest,\n RunEvaluatorResponse,\n TargetCallback,\n TargetResult,\n TargetExecutionContext,\n TargetContext,\n} from \"./types.ts\";\n\nconst DEFAULT_CONCURRENCY = 4;\nconst DEBOUNCE_INTERVAL_MS = 1000;\n\n/**\n * How many rows of captured target outputs compare() keeps around.\n */\nconst MAX_COMPARISON_ROWS_RETAINED = 1000;\n\n/**\n * How long an evaluator call may take before the socket is given up on.\n */\nexport const EVALUATOR_TIMEOUT_MS = 900_000;\n\nfunction predictedOf(result: unknown): Record<string, unknown> | null {\n if (result === undefined || result === null) return null;\n return typeof result === \"object\" ? (result as Record<string, unknown>) : { output: result };\n}\n\n/**\n * AsyncLocalStorage for iteration context isolation. This stores the current item, index\n * and trace for each iteration, preventing race conditions in concurrent execution.\n */\ntype IterationContext = {\n index: number;\n item: unknown;\n traceId: string | null;\n};\nconst iterationContextStorage = new AsyncLocalStorage<IterationContext>();\n\n/**\n * AsyncLocalStorage for target context isolation.\n * This allows log() calls inside withTarget() to automatically\n * infer the target without explicit specification.\n */\nconst targetContextStorage = new AsyncLocalStorage<TargetExecutionContext>();\n\n/**\n * Experiment session for running batch experiments\n */\nexport class Experiment {\n readonly name: string;\n readonly runId: string;\n readonly experimentSlug: string;\n\n private readonly apiClient: LangwatchApiClient;\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly logger: Logger;\n private readonly concurrency: number;\n\n private initialized = false;\n private createdAtMs: number;\n private total = 0;\n private progress = 0;\n\n // Batching state\n private batch: Batch = { dataset: [], evaluations: [], targets: [] };\n private lastSentMs = 0;\n private pendingFlush: Promise<void> | null = null;\n /** Every row and verdict sent so far, by the identity the platform stores it under. */\n private readonly sentRows = new Set<string>();\n private readonly sentEvaluations = new Set<string>();\n private flushTimeout: ReturnType<typeof setTimeout> | null = null;\n\n // Cumulative record for printSummary() — never cleared, mirrors batch.\n // Store only the fields printSummary() needs, not the full payloads —\n // inputs/tracebacks/outputs can be large and don't belong in retained summary state.\n private cumulativeEvaluations: SummaryEvaluation[] = [];\n private cumulativeEntries: SummaryEntry[] = [];\n private runUrl = \"\";\n\n // Target registry\n private targets = new Map<string, TargetInfo>();\n\n // Per-row target outputs for compare(), keyed by row then by target name.\n // Captured when a withTarget() callback resolves and kept independently of\n // the batch, which is flushed on a timer and may be long gone by the time\n // the row is compared. Insertion-ordered, and reaped oldest-first past\n // MAX_COMPARISON_ROWS_RETAINED.\n private capturedOutputs = new Map<number, Map<string, CapturedTargetOutput>>();\n\n // Track whether withTarget() was used in the current iteration\n // If so, we don't create dataset entries in executeItem()\n // Note: This is now checked via iterationContextStorage to be thread-safe\n private iterationUsedWithTarget = new Map<number, boolean>();\n\n // Track whether withTarget() has EVER been used in this evaluation\n // Once set to true, we stop creating iteration-level traces\n private evaluationUsesTargets = false;\n\n private constructor(\n name: string,\n options: {\n apiClient: LangwatchApiClient;\n endpoint: string;\n apiKey: string;\n logger: Logger;\n runId?: string;\n concurrency?: number;\n },\n ) {\n this.name = name;\n this.experimentSlug = name;\n this.runId = options.runId ?? generateHumanReadableId();\n this.apiClient = options.apiClient;\n this.endpoint = resolveEndpoint(options.endpoint);\n this.apiKey = options.apiKey;\n this.logger = options.logger;\n this.concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;\n this.createdAtMs = Date.now();\n }\n\n /**\n * Initialize an evaluation session\n */\n static async init(\n name: string,\n options: {\n apiClient: LangwatchApiClient;\n endpoint: string;\n apiKey: string;\n logger: Logger;\n } & ExperimentInitOptions,\n ): Promise<Experiment> {\n // Ensure observability is set up for proper tracing\n ensureSetup();\n\n const experiment = new Experiment(name, options);\n await experiment.initialize();\n return experiment;\n }\n\n /**\n * Initialize the evaluation by creating/getting the experiment\n */\n private async initialize(): Promise<void> {\n if (!this.apiKey) {\n throw new ExperimentInitError(\n \"API key is required. Set LANGWATCH_API_KEY or pass apiKey to LangWatch constructor.\",\n );\n }\n\n try {\n const response = await langwatchFetch(`${this.endpoint}/api/v1/experiment/init`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildRequestHeaders({ apiKey: this.apiKey }),\n },\n body: JSON.stringify({\n experiment_name: this.name,\n experiment_slug: this.experimentSlug,\n experiment_type: \"BATCH_EVALUATION_V2\",\n }),\n });\n\n if (response.status === 401) {\n throw new ExperimentInitError(\"Invalid API key\");\n }\n\n if (!response.ok) {\n const text = await response.text();\n throw new ExperimentInitError(`Failed to initialize experiment: ${text}`);\n }\n\n const data = (await response.json()) as ExperimentInitResponse;\n (this as { experimentSlug: string }).experimentSlug = data.slug;\n\n const encodedRunId = encodeURIComponent(this.runId);\n this.runUrl = `${this.endpoint}${data.path}?runId=${encodedRunId}`;\n console.log(`Follow results at: ${this.runUrl}`);\n\n this.initialized = true;\n } catch (error) {\n if (error instanceof ExperimentInitError) {\n throw error;\n }\n throw new ExperimentInitError(\n `Failed to initialize evaluation: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Run evaluation over a dataset with a callback.\n * @param dataset - Array of items to evaluate\n * @param callback - Function called for each item with { item, index, span }\n */\n async run<T>(dataset: T[], callback: RunCallback<T>, options?: RunOptions): Promise<void> {\n if (!this.initialized) {\n await this.initialize();\n }\n\n const concurrency = options?.concurrency ?? this.concurrency;\n this.total = dataset.length;\n this.progress = 0;\n\n const tracer = trace.getTracer(\"langwatch\");\n\n // Process items with concurrency control\n const executing = new Set<Promise<void>>();\n\n for (let index = 0; index < dataset.length; index++) {\n const item = dataset[index] as T;\n\n const itemPromise = this.executeItem(tracer, item, index, callback);\n\n executing.add(itemPromise);\n void itemPromise.finally(() => executing.delete(itemPromise));\n\n // Wait if we've hit concurrency limit\n if (executing.size >= concurrency) {\n await Promise.race(executing);\n }\n }\n\n // Wait for all remaining items\n await Promise.all(executing);\n\n // Flush OTEL spans so all child spans created inside callbacks are exported\n const provider = trace.getTracerProvider();\n const delegate =\n \"getDelegate\" in provider && typeof provider.getDelegate === \"function\"\n ? provider.getDelegate()\n : provider;\n if (delegate && \"forceFlush\" in delegate && typeof delegate.forceFlush === \"function\") {\n await delegate.forceFlush();\n }\n\n // Send final batch with finished timestamp\n await this.flush(true);\n }\n\n /**\n * Execute a single item in the dataset\n */\n /** Runs one row without a trace of its own: each withTarget() call traces itself. */\n private async runItemUntraced<T>({\n iterationContext,\n item,\n callback,\n }: {\n iterationContext: IterationContext;\n item: T;\n callback: RunCallback<T>;\n }): Promise<{ error: Error | undefined; capturedTraceId: string | null }> {\n const index = iterationContext.index;\n let error: Error | undefined;\n await iterationContextStorage.run(iterationContext, async () => {\n try {\n // Create a minimal span context for the callback\n const span = {\n setStatus: () => {\n /* no-op */\n },\n recordException: () => {\n /* no-op */\n },\n end: () => {\n /* no-op */\n },\n } as unknown as LangWatchSpan;\n\n const ctx: RunContext<T> = { item, index, span };\n const result = callback(ctx);\n\n if (result && typeof result.then === \"function\") {\n await result;\n }\n } catch (err) {\n error = err instanceof Error ? err : new Error(String(err));\n this.logger.error(`Evaluation error at index ${index}:`, error);\n }\n });\n return { error, capturedTraceId: null };\n }\n\n /** Runs one row inside its own iteration span. */\n private async runItemTraced<T>({\n tracer,\n iterationContext,\n item,\n callback,\n }: {\n tracer: ReturnType<typeof trace.getTracer>;\n iterationContext: IterationContext;\n item: T;\n callback: RunCallback<T>;\n }): Promise<{ error: Error | undefined; capturedTraceId: string | null }> {\n const index = iterationContext.index;\n let error: Error | undefined;\n let capturedTraceId: string | null = null;\n await iterationContextStorage.run(iterationContext, async () => {\n await tracer.startActiveSpan(\n \"evaluation.iteration\",\n {\n attributes: {\n \"langwatch.origin\": \"evaluation\",\n \"evaluation.run_id\": this.runId,\n \"evaluation.index\": index,\n },\n },\n async (otelSpan) => {\n const span = createLangWatchSpan(otelSpan);\n const spanContext = otelSpan.spanContext();\n const traceId = spanContext.traceId;\n\n // The row's own trace, for the log/evaluate/compare calls made\n // inside it. It rides the iteration context rather than the\n // instance, so a row reads its own trace and never a neighbour's.\n iterationContext.traceId = traceId;\n capturedTraceId = traceId;\n\n try {\n const ctx: RunContext<T> = { item, index, span };\n const result = callback(ctx);\n\n if (result && typeof result.then === \"function\") {\n await result;\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n } catch (err) {\n error = err instanceof Error ? err : new Error(String(err));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n span.recordException(error);\n this.logger.error(`Evaluation error at index ${index}:`, error);\n } finally {\n span.end();\n }\n },\n );\n });\n return { error, capturedTraceId };\n }\n\n private async executeItem<T>(\n tracer: ReturnType<typeof trace.getTracer>,\n item: T,\n index: number,\n callback: RunCallback<T>,\n ): Promise<void> {\n const startTime = Date.now();\n\n // Reset withTarget tracking for this iteration\n this.iterationUsedWithTarget.set(index, false);\n\n // Set up iteration context (thread-safe via AsyncLocalStorage)\n const iterationContext: IterationContext = { index, item, traceId: null };\n\n // If evaluation uses targets, skip creating iteration-level traces\n // Each withTarget() call will create its own independent trace\n const { error, capturedTraceId } = this.evaluationUsesTargets\n ? await this.runItemUntraced({ iterationContext, item, callback })\n : await this.runItemTraced({ tracer, iterationContext, item, callback });\n\n // Only add a dataset entry if withTarget() was NOT used\n // When withTarget() is used, it creates its own dataset entries per target\n if (!this.iterationUsedWithTarget.get(index)) {\n const duration = Date.now() - startTime;\n const entry: BatchEntry = {\n index,\n entry: this.serializeItem(item),\n duration,\n error: error?.message ?? null,\n trace_id: capturedTraceId ?? this.getTraceIdFromContext(),\n };\n\n this.batch.dataset.push(entry);\n this.cumulativeEntries.push({\n duration: entry.duration,\n error: entry.error,\n cost: entry.cost,\n target_id: entry.target_id,\n });\n }\n\n // Clean up\n this.iterationUsedWithTarget.delete(index);\n\n this.progress++;\n\n // Debounced send\n this.scheduleSend();\n }\n\n /**\n * @param metric - Name of the metric\n * @param options - Metric options including index, score, passed, etc.\n * @example\n */\n log(metric: string, options: LogOptions): void {\n // Get context from AsyncLocalStorage (if inside withTarget)\n const targetContext = targetContextStorage.getStore();\n\n const {\n data = {},\n score,\n passed,\n label,\n details,\n status = options.error ? \"error\" : \"processed\",\n duration,\n cost,\n error,\n // Use context values as defaults, allow explicit override\n target = targetContext?.targetId,\n metadata,\n index = targetContext?.index ?? options.index,\n } = options;\n\n // Register target if provided (explicit or from context)\n let targetId: string | undefined;\n if (target) {\n targetId = this.registerTarget(target, metadata);\n }\n\n // Use trace ID from the target, then the row being iterated, then OTEL context\n const traceId =\n targetContext?.traceId ??\n iterationContextStorage.getStore()?.traceId ??\n this.getTraceIdFromContext();\n\n const result: ExperimentEvaluationResult = {\n name: metric,\n evaluator: metric,\n trace_id: traceId,\n status,\n data,\n score: score ?? null,\n passed: passed ?? null,\n details: details ?? (error ? error.message : null),\n index,\n label: label ?? null,\n cost: cost ?? null,\n duration: duration ?? null,\n error_type: error ? error.name : null,\n traceback: error ? [error.stack ?? error.message] : null,\n target_id: targetId ?? null,\n };\n\n this.pushEvaluation(result);\n }\n\n /**\n * Queue an evaluation result for the next batch\n */\n private pushEvaluation(result: ExperimentEvaluationResult): void {\n this.batch.evaluations.push(result);\n this.cumulativeEvaluations.push({\n name: result.name,\n evaluator: result.evaluator,\n status: result.status,\n passed: result.passed,\n score: result.score,\n cost: result.cost,\n target_id: result.target_id,\n });\n this.scheduleSend();\n }\n\n /**\n * @param evaluatorSlug - The evaluator identifier (e.g., 'ragas/faithfulness')\n * @param options - Evaluator options including data and settings\n * @example\n */\n async evaluate(evaluatorSlug: string, options: ExperimentEvaluateOptions): Promise<void> {\n // Get context from AsyncLocalStorage (if inside withTarget)\n const targetContext = targetContextStorage.getStore();\n\n const {\n data,\n settings,\n name,\n asGuardrail = false,\n // Use context values as defaults, allow explicit override\n target = targetContext?.targetId,\n metadata,\n index = targetContext?.index ?? options.index,\n } = options;\n\n const startTime = Date.now();\n // Use trace ID from the target, then the row being iterated, then OTEL context\n const traceId =\n targetContext?.traceId ??\n iterationContextStorage.getStore()?.traceId ??\n this.getTraceIdFromContext();\n const spanId = targetContext?.spanId ?? this.getSpanIdFromContext();\n\n try {\n const result = await this.callEvaluator({\n evaluatorSlug,\n data,\n settings,\n name,\n traceId,\n spanId,\n asGuardrail,\n });\n const duration = Date.now() - startTime;\n\n // Log the result\n this.log(name ?? evaluatorSlug, {\n index,\n data,\n status: result.status,\n score: result.score ?? undefined,\n passed: result.passed ?? undefined,\n details: result.details ?? undefined,\n label: result.label ?? undefined,\n duration,\n cost: result.cost?.amount,\n target,\n metadata,\n });\n } catch (error) {\n const duration = Date.now() - startTime;\n\n if (error instanceof EvaluatorError) {\n this.log(name ?? evaluatorSlug, {\n index,\n data,\n status: \"error\",\n duration,\n error: error,\n target,\n metadata,\n });\n throw error;\n }\n\n const wrappedError = new EvaluatorError(\n evaluatorSlug,\n error instanceof Error ? error.message : String(error),\n error instanceof Error ? error : undefined,\n );\n\n this.log(name ?? evaluatorSlug, {\n index,\n data,\n status: \"error\",\n duration,\n error: wrappedError,\n target,\n metadata,\n });\n\n throw wrappedError;\n }\n }\n\n /**\n * Call an evaluator and return its raw result\n */\n private async callEvaluator({\n evaluatorSlug,\n data,\n settings,\n name,\n traceId,\n spanId,\n asGuardrail = false,\n }: {\n evaluatorSlug: string;\n data: Record<string, unknown>;\n settings?: Record<string, unknown>;\n name?: string;\n traceId?: string | null;\n spanId?: string | null;\n asGuardrail?: boolean;\n }): Promise<RunEvaluatorResponse> {\n const response = await langwatchFetch(\n `${this.endpoint}/api/v1/evaluations/${evaluatorSlug}/evaluate`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildRequestHeaders({ apiKey: this.apiKey }),\n },\n body: JSON.stringify({\n trace_id: traceId ?? null,\n span_id: spanId ?? null,\n name: name ?? evaluatorSlug,\n data,\n settings,\n as_guardrail: asGuardrail,\n }),\n signal: AbortSignal.timeout(EVALUATOR_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new EvaluatorError(evaluatorSlug, text);\n }\n\n return (await response.json()) as RunEvaluatorResponse;\n }\n\n /**\n * @param options - How the judge should read the row, and which row when\n * @returns The verdict, including the candidates it actually saw\n * @example\n */\n async compare(options: ComparisonOptions = {}): Promise<ComparisonVerdict> {\n const { name = DEFAULT_COMPARISON_NAME, targets, input, golden } = options;\n\n const index = this.resolveComparisonRow(options.index);\n\n const captured = this.capturedOutputs.get(index) ?? new Map<string, CapturedTargetOutput>();\n\n if (targets) {\n const missing = targets.filter((target) => !captured.has(target));\n if (missing.length > 0) {\n throw new ComparisonError(\n `Cannot compare row ${index}: no output was recorded for ${missing\n .map((target) => `'${target}'`)\n .join(\", \")}. Compare a row once every withTarget() call for it has settled.`,\n missing,\n );\n }\n }\n\n // Registration order, not the order the outputs happened to land in:\n // targets usually run concurrently, so completion order varies per row and\n // would reshuffle what the judge sees from one row to the next.\n const requested = targets ? new Set(targets) : null;\n const candidates = Array.from(this.targets.keys()).filter(\n (target) => captured.has(target) && (requested === null || requested.has(target)),\n );\n\n if (candidates.length < 2) {\n const missing = Array.from(this.targets.keys()).filter(\n (target) => !captured.has(target) && (requested === null || requested.has(target)),\n );\n const verdict: ComparisonVerdict = {\n status: \"skipped\",\n winner: null,\n reasoning: describeSkippedComparison({ candidates, missing }),\n candidates,\n };\n\n this.recordComparison({ name, index, verdict });\n\n return verdict;\n }\n\n const data = buildComparisonData({\n input,\n golden,\n candidates: buildComparisonCandidates(candidates, captured),\n index,\n });\n const settings = buildComparisonSettings(options);\n\n const startTime = Date.now();\n\n // A judge that could not be reached measured nothing about the\n // candidates, so it is an errored row rather than a finding about them.\n // One unreachable judge must not end a run over a thousand rows either,\n // so the failure is reported to the caller instead of thrown.\n let response: RunEvaluatorResponse;\n try {\n response = await this.callEvaluator({\n evaluatorSlug: COMPARISON_EVALUATOR_SLUG,\n data,\n settings,\n name,\n traceId: iterationContextStorage.getStore()?.traceId ?? this.getTraceIdFromContext(),\n spanId: this.getSpanIdFromContext(),\n });\n } catch (error) {\n response = {\n status: \"error\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n\n const verdict = toComparisonVerdict({ response, candidates });\n\n this.recordComparison({\n name,\n index,\n verdict,\n score: response.score ?? null,\n cost: response.cost?.amount ?? null,\n duration: Date.now() - startTime,\n });\n\n return verdict;\n }\n\n /**\n * The row a comparison is about\n */\n private resolveComparisonRow(index?: number): number {\n const resolved = index ?? iterationContextStorage.getStore()?.index;\n\n if (resolved === undefined) {\n throw new ComparisonError(\n \"Cannot compare: no row was given and none could be inferred. Pass index explicitly when comparing outside a run() iteration.\",\n );\n }\n\n return resolved;\n }\n\n /**\n * Record a comparison verdict against the row\n */\n private recordComparison({\n name,\n index,\n verdict,\n score = null,\n cost = null,\n duration = null,\n }: {\n name: string;\n index: number;\n verdict: ComparisonVerdict;\n score?: number | null;\n cost?: number | null;\n duration?: number | null;\n }): void {\n const status = comparisonEntryStatus(verdict.status);\n\n this.pushEvaluation({\n name,\n evaluator: COMPARISON_EVALUATOR_SLUG,\n trace_id: iterationContextStorage.getStore()?.traceId ?? this.getTraceIdFromContext(),\n status,\n // The results page keys a comparison column off this candidate list.\n data: {\n candidates: verdict.candidates.map((candidate) => ({ id: candidate })),\n },\n // A row that established nothing carries no score: 0.5 would read as the\n // tie it is not, and 1.0 as a win for a winner it does not have.\n score: status === \"processed\" ? score : null,\n passed: null,\n details: verdict.reasoning,\n index,\n label: comparisonEntryLabel(verdict),\n cost,\n duration,\n error_type: null,\n traceback: null,\n target_id: null,\n });\n }\n\n /**\n * Execute code within a target context with automatic tracing.\n * @param targetName - Unique identifier for the target\n * @param callback - Function to execute within the target context\n */\n async withTarget<R>(\n targetName: string,\n metadata: TargetMetadata | null,\n callback: TargetCallback<R>,\n ): Promise<TargetResult<R>>;\n async withTarget<R>(targetName: string, callback: TargetCallback<R>): Promise<TargetResult<R>>;\n async withTarget<R>(\n targetName: string,\n metadataOrCallback: TargetMetadata | null | TargetCallback<R>,\n maybeCallback?: TargetCallback<R>,\n ): Promise<TargetResult<R>> {\n // Handle overloads\n const metadata = typeof metadataOrCallback === \"function\" ? null : metadataOrCallback;\n const callback = typeof metadataOrCallback === \"function\" ? metadataOrCallback : maybeCallback!;\n\n // On FIRST withTarget() call ever in this evaluation:\n // - Set flag to skip creating iteration-level traces going forward\n this.evaluationUsesTargets = true;\n\n // Get iteration context (thread-safe via AsyncLocalStorage)\n const iterationContext = iterationContextStorage.getStore();\n const index = iterationContext?.index ?? 0;\n const currentItem = iterationContext?.item;\n\n // Mark that withTarget() was used - prevents executeItem from creating a dataset entry\n this.iterationUsedWithTarget.set(index, true);\n\n // Register target\n this.registerTarget(targetName, metadata ?? undefined);\n\n const tracer = trace.getTracer(\"langwatch\");\n const startTime = Date.now();\n let result: R | undefined;\n let traceId = \"\";\n let spanId = \"\";\n let callbackError: Error | undefined;\n\n // Create an INDEPENDENT root span using ROOT_CONTEXT\n // This ensures each target gets a unique trace_id, not shared with iteration\n await tracer.startActiveSpan(\n `evaluation.target.${targetName}`,\n {\n attributes: {\n \"langwatch.origin\": \"evaluation\",\n \"evaluation.run_id\": this.runId,\n \"evaluation.target\": targetName,\n \"evaluation.index\": index,\n },\n },\n ROOT_CONTEXT,\n async (otelSpan) => {\n const span = createLangWatchSpan(otelSpan);\n const spanContext = otelSpan.spanContext();\n const rawTraceId = spanContext.traceId;\n spanId = spanContext.spanId;\n\n // Check if this is a no-op trace (all zeros = no tracer configured)\n const isNoOpTrace = rawTraceId === \"00000000000000000000000000000000\";\n traceId = isNoOpTrace ? \"\" : rawTraceId;\n\n // Set up the target execution context\n const executionContext: TargetExecutionContext = {\n targetId: targetName,\n traceId,\n spanId,\n index,\n };\n\n try {\n // Run callback within AsyncLocalStorage context\n result = await targetContextStorage.run(executionContext, async () => {\n const ctx: TargetContext = { span, traceId, spanId };\n const callbackResult = callback(ctx);\n\n if (callbackResult && typeof (callbackResult as Promise<R>).then === \"function\") {\n return callbackResult;\n }\n return callbackResult as R;\n });\n\n span.setStatus({ code: SpanStatusCode.OK });\n } catch (err) {\n callbackError = err instanceof Error ? err : new Error(String(err));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: callbackError.message,\n });\n span.recordException(callbackError);\n throw err;\n } finally {\n span.end();\n }\n },\n );\n\n const duration = Date.now() - startTime;\n\n // Serialize the result as \"predicted\" output (similar to Evaluations V3)\n const predicted = predictedOf(result);\n\n // Create a dataset entry for this target execution (like Evaluations V3)\n // This captures per-target duration/latency properly\n // Use currentItem from iteration context (thread-safe), NOT shared instance variable\n const entry: BatchEntry = {\n index,\n entry: this.serializeItem(currentItem),\n duration,\n error: callbackError?.message ?? null,\n trace_id: traceId || null, // null if no tracer configured (no-op)\n target_id: targetName,\n predicted,\n };\n\n this.batch.dataset.push(entry);\n this.cumulativeEntries.push({\n duration: entry.duration,\n error: entry.error,\n cost: entry.cost,\n target_id: entry.target_id,\n });\n this.captureTargetOutput({\n index,\n targetName,\n output: renderTargetOutput(result),\n durationMs: duration,\n });\n this.scheduleSend();\n\n return {\n result: result!,\n duration,\n traceId,\n spanId,\n };\n }\n\n /**\n * Keep a target's output for the row so compare() can reach it later\n */\n private captureTargetOutput({\n index,\n targetName,\n output,\n durationMs,\n }: {\n index: number;\n targetName: string;\n output: string;\n durationMs?: number;\n }): void {\n if (output === \"\") {\n return;\n }\n\n let row = this.capturedOutputs.get(index);\n if (!row) {\n row = new Map<string, CapturedTargetOutput>();\n this.capturedOutputs.set(index, row);\n }\n row.set(targetName, { output, durationMs });\n\n while (this.capturedOutputs.size > MAX_COMPARISON_ROWS_RETAINED) {\n const oldest = this.capturedOutputs.keys().next();\n if (oldest.done) break;\n this.capturedOutputs.delete(oldest.value);\n }\n }\n\n /**\n * Register a target for multi-target comparison\n */\n private registerTarget(name: string, metadata?: TargetMetadata): string {\n const existing = this.targets.get(name);\n\n if (existing) {\n // Check for metadata conflict\n if (metadata) {\n const existingMeta = existing.metadata ?? {};\n const existingMetaJson = JSON.stringify(existingMeta);\n const metadataJson = JSON.stringify(metadata);\n if (existingMetaJson !== metadataJson) {\n throw new TargetMetadataConflictError(name, existingMeta, metadata);\n }\n }\n return name;\n }\n\n // Register new target\n const targetInfo: TargetInfo = {\n id: name,\n name,\n type: \"custom\",\n metadata: metadata ?? null,\n };\n\n this.targets.set(name, targetInfo);\n this.batch.targets.push(targetInfo);\n\n return name;\n }\n\n /**\n * Schedule a debounced send\n */\n private scheduleSend(): void {\n const now = Date.now();\n\n if (now - this.lastSentMs >= DEBOUNCE_INTERVAL_MS) {\n this.sendBatch();\n } else {\n this.flushTimeout ??= setTimeout(\n () => {\n this.flushTimeout = null;\n this.sendBatch();\n },\n DEBOUNCE_INTERVAL_MS - (now - this.lastSentMs),\n );\n }\n }\n\n /**\n * Print a CI-friendly summary and optionally exit with code 1 on failure.\n * @param exitOnFailure - If true (default), calls `process.exit(1)` when any evaluation failed.\n * @example\n */\n printSummary(exitOnFailure = true): void {\n printExperimentSummary({\n runId: this.runId,\n runUrl: this.runUrl,\n createdAtMs: this.createdAtMs,\n evaluations: this.cumulativeEvaluations,\n entries: this.cumulativeEntries,\n exitOnFailure,\n });\n }\n\n /**\n * Send current batch to the API\n */\n private sendBatch(finished = false): void {\n if (this.batch.dataset.length === 0 && this.batch.evaluations.length === 0) {\n if (this.batch.targets.length === 0 && !finished) {\n return;\n }\n }\n\n for (const entry of this.batch.dataset) {\n this.sentRows.add(`${entry.index}:${entry.target_id ?? \"\"}`);\n }\n for (const evaluation of this.batch.evaluations) {\n this.sentEvaluations.add(\n `${evaluation.index ?? \"\"}:${evaluation.target_id ?? \"\"}:${evaluation.evaluator}`,\n );\n }\n\n const body: LogResultsRequest = {\n experiment_slug: this.experimentSlug,\n name: this.name,\n run_id: this.runId,\n dataset: this.batch.dataset.map((entry) => ({\n index: entry.index,\n entry: entry.entry,\n duration: entry.duration,\n error: entry.error,\n trace_id: entry.trace_id,\n target_id: entry.target_id ?? null,\n cost: entry.cost ?? null,\n // Only include predicted if it's defined (omit entirely when null/undefined)\n ...(entry.predicted !== undefined && entry.predicted !== null\n ? { predicted: entry.predicted }\n : {}),\n })),\n evaluations: this.batch.evaluations.map((e) => ({\n name: e.name,\n evaluator: e.evaluator,\n trace_id: e.trace_id,\n status: e.status,\n inputs: e.data,\n score: e.score,\n passed: e.passed,\n details: e.details,\n index: e.index,\n label: e.label,\n cost: e.cost,\n duration: e.duration,\n target_id: e.target_id,\n })),\n targets: this.batch.targets,\n progress: this.progress,\n total: this.total,\n timestamps: {\n created_at: this.createdAtMs,\n finished_at: finished ? Date.now() : null,\n },\n // What the run reported in total, so a reader can tell results that are still being\n // stored from a run that is whole.\n ...(finished\n ? { expected: { dataset: this.sentRows.size, evaluations: this.sentEvaluations.size } }\n : {}),\n };\n\n // Fire and forget (with error logging). Requests go out one after another, so the parts\n // of a batch arrive in order and the request that ends the run arrives last.\n const parts = splitLogResultsBody(body);\n this.pendingFlush = (this.pendingFlush ?? Promise.resolve()).then(async () => {\n for (const part of parts) {\n await this.postLogResults(part);\n }\n });\n\n // Clear batch\n this.batch = { dataset: [], evaluations: [], targets: [] };\n this.lastSentMs = Date.now();\n }\n\n /**\n * Posts one log_results request. A request the server refuses as too large is cut in two\n * and sent again. When a single entry is refused, the error names it and its size.\n */\n private async postLogResults(body: LogResultsRequest): Promise<void> {\n try {\n const response = await langwatchFetch(\n `${this.endpoint}/api/v1/evaluations/batch/log_results`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildRequestHeaders({ apiKey: this.apiKey }),\n },\n body: JSON.stringify(body),\n },\n );\n if (response.ok) return;\n\n if (response.status !== 413) {\n this.logger.error(`Failed to send batch: ${response.status}`);\n return;\n }\n\n const halves = halveLogResultsBody(body);\n if (halves) {\n for (const half of halves) {\n await this.postLogResults(half);\n }\n return;\n }\n\n this.logger.error(\n `Failed to send batch: ${describeRefusedEntry(body)}, and LangWatch refused it as too large for one request (HTTP 413). It was not logged. Make the row's entry, output or evaluation inputs smaller, for example by sending images as URLs.`,\n );\n if (endsTheRun(body) && body.dataset.length + body.evaluations.length > 0) {\n await this.postLogResults({ ...body, dataset: [], evaluations: [] });\n }\n } catch (error) {\n this.logger.error(\"Failed to send batch:\", error);\n }\n }\n\n /**\n * Flush all pending data\n */\n private async flush(finished = false): Promise<void> {\n if (this.flushTimeout) {\n clearTimeout(this.flushTimeout);\n this.flushTimeout = null;\n }\n\n this.sendBatch(finished);\n\n if (this.pendingFlush) {\n await this.pendingFlush;\n }\n }\n\n /**\n * Serialize a dataset item for the API\n */\n private serializeItem(item: unknown): unknown {\n if (item === null || item === undefined) {\n return item;\n }\n\n if (typeof item === \"object\") {\n // Handle objects with toJSON method\n if (\"toJSON\" in item && typeof (item as { toJSON: unknown }).toJSON === \"function\") {\n return (item as { toJSON: () => unknown }).toJSON();\n }\n // Return as-is, JSON.stringify will handle it\n return item;\n }\n\n return item;\n }\n\n /**\n * Get trace ID from current OpenTelemetry context\n */\n private getTraceIdFromContext(): string {\n const span = trace.getActiveSpan();\n if (span) {\n return span.spanContext().traceId;\n }\n return \"\";\n }\n\n /**\n * Get span ID from current OpenTelemetry context\n */\n private getSpanIdFromContext(): string | null {\n const span = trace.getActiveSpan();\n if (span) {\n return span.spanContext().spanId;\n }\n return null;\n }\n}\n","/**\n * Errors for the Experiments API\n */\n\n/**\n * Base error for experiment-related issues\n */\nexport class ExperimentError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ExperimentError\";\n }\n}\n\n/**\n * Thrown when initialization fails\n */\nexport class ExperimentInitError extends ExperimentError {\n constructor(\n message: string,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = \"ExperimentInitError\";\n }\n}\n\n/**\n * Thrown when API calls fail\n */\nexport class ExperimentApiError extends ExperimentError {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = \"ExperimentApiError\";\n }\n}\n\n/**\n * Thrown when target metadata conflicts\n */\nexport class TargetMetadataConflictError extends ExperimentError {\n constructor(\n public readonly targetName: string,\n public readonly existingMetadata: Record<string, unknown>,\n public readonly newMetadata: Record<string, unknown>,\n ) {\n super(\n `Target '${targetName}' was previously registered with different metadata.\\n` +\n `Original: ${JSON.stringify(existingMetadata)}\\n` +\n `New: ${JSON.stringify(newMetadata)}\\n` +\n `If you want to use different metadata, please use a different target name.`,\n );\n this.name = \"TargetMetadataConflictError\";\n }\n}\n\n/**\n * Thrown when a comparison cannot produce a verdict the caller asked for.\n * A too-thin-to-judge row is skipped instead -- this is reserved for a\n * mismatch between what the caller named and what the run recorded.\n */\nexport class ComparisonError extends ExperimentError {\n constructor(\n message: string,\n public readonly missingTargets: string[] = [],\n ) {\n super(message);\n this.name = \"ComparisonError\";\n }\n}\n\n/**\n * Thrown when an evaluator call fails\n */\nexport class EvaluatorError extends ExperimentError {\n constructor(\n public readonly evaluatorSlug: string,\n message: string,\n public readonly cause?: Error,\n ) {\n super(`Evaluator '${evaluatorSlug}' failed: ${message}`);\n this.name = \"EvaluatorError\";\n }\n}\n","/**\n * Comparison (n-way judging) via one call to `langevals/select_best_compare`.\n * The judge owns its defaults — an unset option stays absent from the request\n * so it can pick the prompt that fits the row; a copied default here would drift and disable that.\n */\n\nimport { ComparisonError } from \"./errors\";\nimport type {\n ComparisonOptions,\n ComparisonStatus,\n ComparisonVerdict,\n ExperimentEvaluationStatus,\n RunEvaluatorResponse,\n} from \"./types\";\n\n/** The evaluator behind every comparison, in the workbench and in the SDK. */\nexport const COMPARISON_EVALUATOR_SLUG = \"langevals/select_best_compare\";\n\n/** Name a comparison is recorded under when the caller does not pick one. */\nexport const DEFAULT_COMPARISON_NAME = \"comparison\";\n\n/**\n * One target's output for one row, captured when its withTarget() callback\n * resolves so a comparison never has to read it back out of a batch that may\n * already have been flushed.\n */\nexport type CapturedTargetOutput = {\n /** The callback result, rendered as the text the judge will read. */\n output: string;\n /** Wall-clock duration of the target execution, in milliseconds. */\n durationMs?: number;\n};\n\n/** One candidate as the judge receives it. */\nexport type ComparisonCandidatePayload = {\n id: string;\n output: string;\n /** Seconds, which is the unit the judge renders. */\n duration?: number;\n};\n\n/**\n * Render a target's callback result as text a judge can read. A lone `output`\n * field unwraps to its own value; other shapes render as JSON. Empty string\n * means nothing to show — an unparseable result is treated the same way.\n */\nexport const renderTargetOutput = (result: unknown): string => {\n if (result === undefined || result === null) {\n return \"\";\n }\n if (typeof result === \"string\") {\n return result;\n }\n if (typeof result === \"number\" || typeof result === \"boolean\" || typeof result === \"bigint\") {\n return String(result);\n }\n if (typeof result === \"object\" && !Array.isArray(result)) {\n const fields = Object.keys(result);\n if (fields.length === 1 && fields[0] === \"output\") {\n return renderTargetOutput((result as { output: unknown }).output);\n }\n }\n try {\n return JSON.stringify(result) ?? \"\";\n } catch {\n return \"\";\n }\n};\n\n/**\n * The judge settings for a comparison, carrying only what the caller set.\n * `golden` is the single knob for reference-answer judging: passing one\n * turns `has_golden_answer` on, so the two can never disagree.\n */\nexport const buildComparisonSettings = (\n options: Pick<\n ComparisonOptions,\n | \"golden\"\n | \"prompt\"\n | \"model\"\n | \"allowTie\"\n | \"randomizeOrder\"\n | \"swapAndReconcile\"\n | \"includeMetrics\"\n | \"temperature\"\n >,\n): Record<string, unknown> => {\n const settings: Record<string, unknown> = {};\n\n if (options.prompt !== undefined) settings.prompt = options.prompt;\n if (options.model !== undefined) settings.model = options.model;\n if (options.golden !== undefined) settings.has_golden_answer = true;\n if (options.allowTie !== undefined) settings.allow_tie = options.allowTie;\n if (options.randomizeOrder !== undefined) {\n settings.randomize_order = options.randomizeOrder;\n }\n if (options.swapAndReconcile !== undefined) {\n settings.swap_and_reconcile = options.swapAndReconcile;\n }\n if (options.includeMetrics !== undefined) {\n settings.include_metrics = options.includeMetrics;\n }\n if (options.temperature !== undefined) {\n settings.temperature = options.temperature;\n }\n\n return settings;\n};\n\n/**\n * The candidates the judge will see, in the order given. A name with\n * nothing recorded never reaches here; if one ever does, it's a bug in\n * candidate selection, so it names the target instead of judging silently.\n */\nexport const buildComparisonCandidates = (\n names: string[],\n captured: Map<string, CapturedTargetOutput>,\n): ComparisonCandidatePayload[] =>\n names.map((name) => {\n const output = captured.get(name);\n if (!output) {\n throw new ComparisonError(\n `Cannot compare: '${name}' was selected as a candidate but recorded no output.`,\n [name],\n );\n }\n return {\n id: name,\n output: output.output,\n ...(output.durationMs !== undefined ? { duration: output.durationMs / 1000 } : {}),\n };\n });\n\n/**\n * The judge entry for a row. `row_index` seeds the judge's deterministic\n * candidate shuffle, coming from the row the caller already names rather\n * than a second argument they'd have to keep in step.\n */\nexport const buildComparisonData = ({\n input,\n golden,\n candidates,\n index,\n}: {\n input?: string;\n golden?: string;\n candidates: ComparisonCandidatePayload[];\n index: number;\n}): Record<string, unknown> => ({\n ...(input !== undefined ? { input } : {}),\n ...(golden !== undefined ? { golden } : {}),\n candidates,\n row_index: index,\n});\n\n/**\n * Translate the judge's result into a verdict: `decided`, `tie`, or\n * `inconclusive` — the judge would not call it, a finding about the\n * candidates, not a claimed tie — or `error` when nothing was measured at all.\n */\nexport const toComparisonVerdict = ({\n response,\n candidates,\n}: {\n response: RunEvaluatorResponse;\n candidates: string[];\n}): ComparisonVerdict => {\n const reasoning = response.details ?? null;\n\n if (response.status === \"error\") {\n return { status: \"error\", winner: null, reasoning, candidates };\n }\n\n if (response.status !== \"processed\" || response.label == null) {\n return { status: \"inconclusive\", winner: null, reasoning, candidates };\n }\n\n if (response.label === \"tie\") {\n return { status: \"tie\", winner: null, reasoning, candidates };\n }\n\n return { status: \"decided\", winner: response.label, reasoning, candidates };\n};\n\n/**\n * The batch status a verdict is recorded under. The batch protocol carries\n * three statuses to the verdict's five, so this mapping lives here alone --\n * what the row records and what the caller reads are one decision, not two.\n */\nexport const comparisonEntryStatus = (status: ComparisonStatus): ExperimentEvaluationStatus => {\n switch (status) {\n case \"decided\":\n case \"tie\":\n return \"processed\";\n case \"error\":\n return \"error\";\n case \"inconclusive\":\n case \"skipped\":\n return \"skipped\";\n }\n};\n\n/**\n * The label a verdict is recorded under: the winner, \"tie\", or nothing.\n */\nexport const comparisonEntryLabel = (verdict: ComparisonVerdict): string | null => {\n if (verdict.status === \"decided\") return verdict.winner;\n if (verdict.status === \"tie\") return \"tie\";\n return null;\n};\n\n/**\n * Why a row could not be compared, naming the targets that had nothing to\n * contribute so the reader can tell a thin dataset from a broken target.\n */\nexport const describeSkippedComparison = ({\n candidates,\n missing,\n}: {\n candidates: string[];\n missing: string[];\n}): string => {\n const head = `A comparison needs at least two candidate outputs, this row has ${candidates.length}.`;\n if (missing.length === 0) {\n return head;\n }\n return `${head} No output was recorded for: ${missing.join(\", \")}.`;\n};\n","import type { ExperimentRunResult } from \"./platformTypes\";\n\ntype ExperimentSummary = ExperimentRunResult[\"summary\"];\n\nfunction printTargets(summary: ExperimentSummary): void {\n if (!summary.targets || summary.targets.length === 0) return;\n\n console.log(\"─\".repeat(60));\n console.log(\" TARGETS:\");\n for (const target of summary.targets) {\n console.log(` ${target.name}: ${target.passed} passed, ${target.failed} failed`);\n if (target.avgLatency) {\n console.log(` Avg latency: ${target.avgLatency.toFixed(0)}ms`);\n }\n if (target.totalCost) {\n console.log(` Total cost: $${target.totalCost.toFixed(4)}`);\n }\n }\n}\n\nfunction printEvaluators(summary: ExperimentSummary): void {\n if (!summary.evaluators || summary.evaluators.length === 0) return;\n\n console.log(\"─\".repeat(60));\n console.log(\" EVALUATORS:\");\n for (const evaluator of summary.evaluators) {\n console.log(` ${evaluator.name}: ${evaluator.passRate.toFixed(1)}% pass rate`);\n if (evaluator.avgScore !== undefined) {\n console.log(` Avg score: ${evaluator.avgScore.toFixed(2)}`);\n }\n }\n}\n\n/**\n * Prints a CI-friendly summary of experiment results to stdout. Shared\n * between platform runs (`experiments.run`) and SDK-driven experiments\n * (`experiments.init` → `experiment.printSummary()`).\n */\nexport function printSummary(result: Omit<ExperimentRunResult, \"printSummary\" | \"toString\">): void {\n const { runId, status, passed, failed, passRate, duration, runUrl, summary } = result;\n\n console.log(\"\\n\" + \"═\".repeat(60));\n console.log(\" EXPERIMENT RESULTS\");\n console.log(\"═\".repeat(60));\n console.log(` Run ID: ${runId}`);\n console.log(` Status: ${status.toUpperCase()}`);\n console.log(` Duration: ${(duration / 1000).toFixed(1)}s`);\n console.log(\"─\".repeat(60));\n console.log(` Passed: ${passed}`);\n console.log(` Failed: ${failed}`);\n console.log(` Pass Rate: ${passRate.toFixed(1)}%`);\n if (summary.totalCost) {\n console.log(` Total Cost: $${summary.totalCost.toFixed(4)}`);\n }\n\n printTargets(summary);\n printEvaluators(summary);\n\n console.log(\"─\".repeat(60));\n console.log(` View details: ${runUrl}`);\n console.log(\"═\".repeat(60) + \"\\n\");\n}\n","import { printSummary } from \"./printSummary.ts\";\nimport type { BatchEntry, ExperimentEvaluationResult } from \"./types.ts\";\n\n// Slim projections retained across the lifetime of an Experiment for\n// printSummary() — deliberately excludes large fields (inputs, tracebacks,\n// outputs) so running thousands of items doesn't unbound memory.\nexport type SummaryEvaluation = Pick<\n ExperimentEvaluationResult,\n \"name\" | \"evaluator\" | \"status\" | \"passed\" | \"score\" | \"cost\" | \"target_id\"\n>;\nexport type SummaryEntry = Pick<BatchEntry, \"duration\" | \"error\" | \"cost\" | \"target_id\">;\n\ntype EvaluatorTally = { passed: number; failed: number; scoreSum: number; scoreCount: number };\ntype TargetTally = {\n passed: number;\n failed: number;\n latencySum: number;\n latencyCount: number;\n cost: number;\n};\n\n/** Crashed evaluators (status \"error\") count as failures so CI never passes on a crash. */\nfunction tallyEvaluators(evaluations: readonly SummaryEvaluation[]): {\n evaluators: Map<string, EvaluatorTally>;\n totalPassed: number;\n totalFailed: number;\n evaluationCost: number;\n} {\n const evaluators = new Map<string, EvaluatorTally>();\n let totalPassed = 0;\n let totalFailed = 0;\n let evaluationCost = 0;\n for (const e of evaluations) {\n const name = e.name ?? e.evaluator ?? \"unknown\";\n const stats = evaluators.get(name) ?? { passed: 0, failed: 0, scoreSum: 0, scoreCount: 0 };\n evaluators.set(name, stats);\n if (e.status === \"error\" || e.passed === false) {\n stats.failed += 1;\n totalFailed += 1;\n } else if (e.passed === true) {\n stats.passed += 1;\n totalPassed += 1;\n }\n if (typeof e.score === \"number\") {\n stats.scoreSum += e.score;\n stats.scoreCount += 1;\n }\n if (typeof e.cost === \"number\") evaluationCost += e.cost;\n }\n return { evaluators, totalPassed, totalFailed, evaluationCost };\n}\n\nfunction targetTallyOf({\n targets,\n tid,\n}: {\n targets: Map<string, TargetTally>;\n tid: string;\n}): TargetTally {\n const stats = targets.get(tid) ?? {\n passed: 0,\n failed: 0,\n latencySum: 0,\n latencyCount: 0,\n cost: 0,\n };\n targets.set(tid, stats);\n return stats;\n}\n\nfunction targetSummaries(targets: Map<string, TargetTally>) {\n return Array.from(targets.entries()).map(([targetId, s]) => ({\n targetId,\n name: targetId,\n passed: s.passed,\n failed: s.failed,\n avgLatency: s.latencyCount > 0 ? s.latencySum / s.latencyCount : 0,\n totalCost: s.cost,\n }));\n}\n\nfunction evaluatorSummaries(evaluators: Map<string, EvaluatorTally>) {\n return Array.from(evaluators.entries()).map(([name, s]) => ({\n evaluatorId: name,\n name,\n passed: s.passed,\n failed: s.failed,\n passRate: s.passed + s.failed > 0 ? (s.passed / (s.passed + s.failed)) * 100 : 0,\n avgScore: s.scoreCount > 0 ? s.scoreSum / s.scoreCount : undefined,\n }));\n}\n\nfunction tallyEntryTargets({\n targets,\n entries,\n}: {\n targets: Map<string, TargetTally>;\n entries: readonly SummaryEntry[];\n}): void {\n for (const entry of entries) {\n if (!entry.target_id) continue;\n const stats = targetTallyOf({ targets, tid: entry.target_id });\n if (typeof entry.duration === \"number\") {\n stats.latencySum += entry.duration;\n stats.latencyCount += 1;\n }\n if (typeof entry.cost === \"number\") stats.cost += entry.cost;\n }\n}\n\nfunction tallyEvaluationTargets({\n targets,\n evaluations,\n}: {\n targets: Map<string, TargetTally>;\n evaluations: readonly SummaryEvaluation[];\n}): void {\n for (const e of evaluations) {\n if (!e.target_id) continue;\n const stats = targetTallyOf({ targets, tid: e.target_id });\n if (e.status === \"error\" || e.passed === false) stats.failed += 1;\n else if (e.passed === true) stats.passed += 1;\n if (typeof e.cost === \"number\") stats.cost += e.cost;\n }\n}\n\n/** Evaluations seed targets too: one logged with an explicit target_id may have no entry row. */\nfunction tallyTargets({\n entries,\n evaluations,\n}: {\n entries: readonly SummaryEntry[];\n evaluations: readonly SummaryEvaluation[];\n}): Map<string, TargetTally> {\n const targets = new Map<string, TargetTally>();\n tallyEntryTargets({ targets, entries });\n tallyEvaluationTargets({ targets, evaluations });\n return targets;\n}\n\n/** Prints the CI summary of an SDK-defined experiment run and exits 1 on failure when asked. */\nexport function printExperimentSummary({\n runId,\n runUrl,\n createdAtMs,\n evaluations,\n entries,\n exitOnFailure,\n}: {\n runId: string;\n runUrl: string;\n createdAtMs: number;\n evaluations: readonly SummaryEvaluation[];\n entries: readonly SummaryEntry[];\n exitOnFailure: boolean;\n}): void {\n const { evaluators, totalPassed, totalFailed, evaluationCost } = tallyEvaluators(evaluations);\n let totalCost = evaluationCost;\n const targets = tallyTargets({\n entries: entries,\n evaluations: evaluations,\n });\n\n const total = totalPassed + totalFailed;\n const passRate = total > 0 ? (totalPassed / total) * 100 : 0;\n\n // Wall-clock duration from init — summing entry durations over-counts\n // under concurrent withTarget() calls (each target produces its own entry).\n const duration = Math.max(0, Date.now() - createdAtMs);\n\n // Count entries whose target/loop execution errored out — distinct from\n // evaluator failures but must also trigger CI exit.\n let failedCells = 0;\n for (const entry of entries) {\n if (typeof entry.cost === \"number\") totalCost += entry.cost;\n if (entry.error) failedCells += 1;\n }\n\n const hasFailures = totalFailed > 0 || failedCells > 0;\n\n printSummary({\n runId: runId,\n status: hasFailures ? \"failed\" : \"completed\",\n passed: totalPassed,\n failed: totalFailed,\n passRate,\n duration,\n runUrl,\n summary: {\n runId: runId,\n totalCells: entries.length || total,\n completedCells: Math.max(0, (entries.length || total) - failedCells),\n failedCells,\n duration,\n runUrl: runUrl,\n totalPassed,\n totalFailed,\n passRate,\n totalCost,\n targets: targetSummaries(targets),\n evaluators: evaluatorSummaries(evaluators),\n },\n });\n\n if (exitOnFailure && hasFailures) {\n process.exit(1);\n }\n}\n","/** Generates memorable adjective-adjective-noun ids, like Docker/Heroku container names. */\n\nconst ADJECTIVES = [\n \"swift\",\n \"bright\",\n \"calm\",\n \"eager\",\n \"bold\",\n \"keen\",\n \"warm\",\n \"cool\",\n \"wise\",\n \"fair\",\n \"glad\",\n \"kind\",\n \"neat\",\n \"pure\",\n \"safe\",\n \"true\",\n \"vast\",\n \"wild\",\n \"zesty\",\n \"agile\",\n \"brave\",\n \"crisp\",\n \"dense\",\n \"epic\",\n \"fresh\",\n \"grand\",\n \"happy\",\n \"ideal\",\n \"jolly\",\n \"lively\",\n \"merry\",\n \"noble\",\n \"proud\",\n \"quick\",\n \"rapid\",\n \"sharp\",\n \"smart\",\n \"solid\",\n \"sunny\",\n \"vivid\",\n \"gentle\",\n \"silent\",\n \"cosmic\",\n \"golden\",\n \"silver\",\n \"ancient\",\n \"modern\",\n \"mighty\",\n \"humble\",\n];\n\nconst NOUNS = [\n \"fox\",\n \"owl\",\n \"bee\",\n \"elk\",\n \"hawk\",\n \"lynx\",\n \"wolf\",\n \"bear\",\n \"deer\",\n \"dove\",\n \"eagle\",\n \"finch\",\n \"heron\",\n \"koala\",\n \"lemur\",\n \"moose\",\n \"otter\",\n \"panda\",\n \"raven\",\n \"robin\",\n \"seal\",\n \"swan\",\n \"tiger\",\n \"whale\",\n \"zebra\",\n \"atlas\",\n \"bloom\",\n \"cloud\",\n \"delta\",\n \"ember\",\n \"flame\",\n \"grove\",\n \"haven\",\n \"iris\",\n \"jade\",\n \"leaf\",\n \"moon\",\n \"nova\",\n \"ocean\",\n \"peak\",\n \"river\",\n \"spark\",\n \"storm\",\n \"tide\",\n \"wave\",\n \"comet\",\n \"prism\",\n \"coral\",\n];\n\n/**\n * Generate a human-readable ID with 3 words like \"swift-bright-fox\"\n * Uses adjective-adjective-noun pattern for ~125,000 combinations.\n */\nexport const generateHumanReadableId = (separator = \"-\"): string => {\n // Pick two different adjectives\n const adj1Index = Math.floor(Math.random() * ADJECTIVES.length);\n let adj2Index = Math.floor(Math.random() * ADJECTIVES.length);\n if (adj2Index === adj1Index) {\n adj2Index = (adj2Index + 1) % ADJECTIVES.length;\n }\n\n const adjective1 = ADJECTIVES[adj1Index]!;\n const adjective2 = ADJECTIVES[adj2Index]!;\n const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)]!;\n\n return `${adjective1}${separator}${adjective2}${separator}${noun}`;\n};\n","/**\n * Sizes `log_results` requests by bytes. Results are batched by time and one row can carry\n * megabytes of inline images, so a batch above the request target is sent as several\n * requests, each holding whole entries in their original order.\n */\nimport { type LogResultsRequest } from \"./types.ts\";\n\n/**\n * The most bytes of entries one log_results request carries, unless a single entry is larger\n * than this on its own. It stays under the request limit of every server version an SDK can\n * talk to.\n */\nexport const LOG_RESULTS_TARGET_BYTES = 16 * 1024 * 1024;\n\ntype DatasetEntry = LogResultsRequest[\"dataset\"][number];\ntype EvaluationEntry = LogResultsRequest[\"evaluations\"][number];\n\ntype SizedEntry =\n | { kind: \"dataset\"; entry: DatasetEntry; bytes: number }\n | { kind: \"evaluation\"; entry: EvaluationEntry; bytes: number };\n\nconst encoder = new TextEncoder();\n\n/** The size of a value once it is serialized into a request body. */\nexport function serializedBytes(value: unknown): number {\n return encoder.encode(JSON.stringify(value) ?? \"\").length;\n}\n\nfunction entriesOf(body: LogResultsRequest): SizedEntry[] {\n return [\n ...body.dataset.map((entry): SizedEntry => ({\n kind: \"dataset\",\n entry,\n bytes: serializedBytes(entry),\n })),\n ...body.evaluations.map((entry): SizedEntry => ({\n kind: \"evaluation\",\n entry,\n bytes: serializedBytes(entry),\n })),\n ];\n}\n\n/**\n * One request body per group of entries. Every part repeats the run's own fields. The targets\n * go with the first part, and the timestamps that end the run go with the last one.\n */\nfunction partsOf(body: LogResultsRequest, groups: SizedEntry[][]): LogResultsRequest[] {\n return groups.map((group, position) => ({\n ...body,\n dataset: group.flatMap((sized) => (sized.kind === \"dataset\" ? [sized.entry] : [])),\n evaluations: group.flatMap((sized) => (sized.kind === \"evaluation\" ? [sized.entry] : [])),\n targets: position === 0 ? body.targets : [],\n timestamps:\n position === groups.length - 1\n ? body.timestamps\n : { created_at: body.timestamps.created_at, finished_at: null },\n }));\n}\n\n/**\n * Splits a log_results body into requests of at most `targetBytes` of entries. Entries keep\n * their order, and an entry larger than the target is sent alone. A body that already fits\n * is returned as it is.\n */\nexport function splitLogResultsBody(\n body: LogResultsRequest,\n targetBytes: number = LOG_RESULTS_TARGET_BYTES,\n): LogResultsRequest[] {\n const entries = entriesOf(body);\n const totalBytes = entries.reduce((sum, sized) => sum + sized.bytes, 0);\n if (totalBytes <= targetBytes) return [body];\n\n const groups: SizedEntry[][] = [];\n let groupBytes = 0;\n for (const sized of entries) {\n if (groups.length === 0 || groupBytes + sized.bytes > targetBytes) {\n groups.push([]);\n groupBytes = 0;\n }\n groups[groups.length - 1]!.push(sized);\n groupBytes += sized.bytes;\n }\n return partsOf(body, groups);\n}\n\n/**\n * Cuts a log_results body in two requests that each hold about half the bytes. Answers\n * `null` when the body holds fewer than two entries and so cannot be cut.\n */\nexport function halveLogResultsBody(body: LogResultsRequest): LogResultsRequest[] | null {\n const entries = entriesOf(body);\n if (entries.length < 2) return null;\n\n const half = entries.reduce((sum, sized) => sum + sized.bytes, 0) / 2;\n let read = 0;\n let cut = entries.length - 1;\n for (const [position, sized] of entries.entries()) {\n read += sized.bytes;\n if (read >= half) {\n cut = Math.min(Math.max(position + 1, 1), entries.length - 1);\n break;\n }\n }\n return partsOf(body, [entries.slice(0, cut), entries.slice(cut)]);\n}\n\n/** Names the single entry of a request the server refused, with its serialized size. */\nexport function describeRefusedEntry(body: LogResultsRequest): string {\n const [sized] = entriesOf(body);\n if (!sized) return \"The request\";\n\n const megabytes = (sized.bytes / (1024 * 1024)).toFixed(1);\n const kind = sized.kind === \"evaluation\" ? \"evaluation result\" : \"result\";\n const index = sized.entry.index;\n const subject =\n index === undefined || index === null ? `One ${kind}` : `The ${kind} for row index ${index}`;\n return `${subject} is ${megabytes} MB when serialized`;\n}\n\n/** Whether a request carries the timestamps that end the run. */\nexport function endsTheRun(body: LogResultsRequest): boolean {\n return (\n (body.timestamps.finished_at ?? null) !== null || (body.timestamps.stopped_at ?? null) !== null\n );\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nimport type { ExperimentRunSummary } from \"./platformTypes\";\n\nexport interface ExperimentRunStartResponse {\n runId: string;\n status: \"running\";\n total: number;\n runUrl?: string;\n}\n\n/**\n * Optional body for `POST /api/evaluations/v3/{slug}/run`. `data` and\n * `dataset_id` are mutually exclusive on the server (400 if both are sent).\n */\nexport interface ExperimentRunStartRequest {\n data?: Record<string, unknown>[];\n dataset_id?: string;\n parameters?: Record<string, string | number | boolean>;\n row_indices?: number[];\n}\n\n/**\n * Build the snake_case run-start request body from camelCase options.\n */\nexport const toRunStartRequest = ({\n data,\n datasetId,\n parameters,\n rowIndices,\n}: {\n data?: Record<string, unknown>[];\n datasetId?: string;\n parameters?: Record<string, string | number | boolean>;\n rowIndices?: number[];\n}): ExperimentRunStartRequest | undefined => {\n const body: ExperimentRunStartRequest = {};\n if (data !== undefined) body.data = data;\n if (datasetId !== undefined) body.dataset_id = datasetId;\n if (parameters !== undefined) body.parameters = parameters;\n if (rowIndices !== undefined) body.row_indices = rowIndices;\n return Object.keys(body).length > 0 ? body : undefined;\n};\n\n/**\n * `GET /api/v1/experiments/runs/{runId}` is `withRawResponse`, so no\n * document will ever type this body. Restored from the last document that\n * had it (`openapi-document.json` at `0a0f549cfd^`), not invented.\n */\nexport interface ExperimentRunStatusResponse {\n runId: string;\n status: \"pending\" | \"running\" | \"completed\" | \"failed\" | \"stopped\";\n /** Cells finished so far */\n progress: number;\n /** Cells in the run */\n total: number;\n /** Unix milliseconds */\n startedAt?: number;\n /** Unix milliseconds; set once the run is no longer running */\n finishedAt?: number;\n /** Present when completed */\n summary?: ExperimentRunSummary & {\n /** Non-zero means some rows may be missing from the stored results */\n chDispatchFailures?: number;\n };\n /** Stable failure code, present when failed. Not display copy. */\n error?: string;\n /** The full failure envelope, when the failure carried one */\n domainError?: {\n code: string;\n kind: string;\n message?: string;\n meta?: Record<string, unknown>;\n httpStatus?: number;\n fault?: string;\n traceId?: string;\n tips?: string[];\n docsUrl?: string;\n };\n /** Trace id for failures that carry no code, to quote in support */\n traceId?: string;\n}\n\n/**\n * Status payload for `GET /api/evaluations/v3/runs/{runId}` (polling).\n */\nexport interface ExperimentV3RunStatusResponse {\n runId: string;\n status: \"pending\" | \"running\" | \"completed\" | \"failed\" | \"stopped\";\n /** Number of cells completed */\n progress: number;\n /** Total number of cells */\n total: number;\n /** Unix timestamp when run started */\n startedAt?: number;\n /** Unix timestamp when run finished (completed/failed/stopped only) */\n finishedAt?: number;\n /** Execution summary (present when completed) */\n summary?: {\n runId?: string;\n totalCells?: number;\n completedCells?: number;\n failedCells?: number;\n /** Total execution time in milliseconds */\n duration?: number;\n /** URL to view the run in LangWatch */\n runUrl?: string;\n };\n /** Error message (present when failed) */\n error?: string;\n}\n\n/**\n * Summary entry returned by `GET /api/v1/experiments`. Mirrors\n * `experimentSummarySchema` from the control-plane Hono route. Hand-written\n * because the route is not yet exposed via the generated OpenAPI types.\n */\nexport interface ExperimentSummary {\n id: string;\n slug: string;\n name: string | null;\n type: string;\n workflowId: string | null;\n createdAt: string;\n updatedAt: string;\n runsCount: number;\n lastRunAt: string | null;\n}\n\nexport interface ExperimentListPagination {\n page: number;\n pageSize: number;\n totalHits: number;\n hasMore: boolean;\n}\n\nexport interface ExperimentListResponse {\n experiments: ExperimentSummary[];\n pagination: ExperimentListPagination;\n}\n\n/**\n * Per-run entry returned by `GET /api/v1/experiments/runs?experimentSlug=...`.\n * Mirrors `ExperimentRun` from the control plane.\n */\nexport interface ExperimentRunSummaryEntry {\n experimentId: string;\n runId: string;\n workflowVersion: {\n id: string;\n version: string;\n commitMessage: string;\n author: { name: string | null; image: string | null } | null;\n } | null;\n timestamps: {\n createdAt: number;\n updatedAt: number;\n finishedAt?: number | null;\n stoppedAt?: number | null;\n };\n progress?: number | null;\n total?: number | null;\n summary: {\n datasetCost?: number;\n evaluationsCost?: number;\n datasetAverageCost?: number;\n datasetAverageDuration?: number;\n evaluationsAverageCost?: number;\n evaluationsAverageDuration?: number;\n evaluations: Record<\n string,\n { name: string; averageScore: number | null; averagePassed?: number }\n >;\n };\n}\n\nexport interface ExperimentRunsListResponse {\n experimentId: string;\n experimentSlug: string;\n runs: ExperimentRunSummaryEntry[];\n pagination: ExperimentListPagination;\n}\n\n/**\n * Per-row results for a completed experiment run.\n */\nexport interface ExperimentRunDatasetEntry {\n index: number;\n targetId?: string | null;\n entry: Record<string, unknown>;\n predicted?: Record<string, unknown>;\n cost?: number | null;\n duration?: number | null;\n error?: string | null;\n traceId?: string | null;\n}\n\nexport interface ExperimentRunEvaluation {\n evaluator: string;\n name?: string | null;\n targetId?: string | null;\n status: \"processed\" | \"skipped\" | \"error\";\n index: number;\n score?: number | null;\n label?: string | null;\n passed?: boolean | null;\n details?: string | null;\n cost?: number | null;\n duration?: number | null;\n inputs?: Record<string, unknown> | null;\n}\n\nexport interface ExperimentRunResultsResponse {\n experimentId: string;\n runId: string;\n projectId: string;\n workflowVersionId?: string | null;\n progress?: number | null;\n total?: number | null;\n dataset: ExperimentRunDatasetEntry[];\n evaluations: ExperimentRunEvaluation[];\n timestamps: {\n createdAt: number;\n updatedAt: number;\n finishedAt?: number | null;\n stoppedAt?: number | null;\n };\n /** Absent on servers that predate it; the CLI then derives it from the timestamps. */\n completeness?: ExperimentRunCompleteness;\n}\n\n/** What is stored of a run against what the run reported; `expected` is null when unreported. */\nexport interface ExperimentRunCompleteness {\n complete: boolean;\n dataset: { received: number; expected: number | null };\n evaluations: { received: number; expected: number | null };\n}\n\n/**\n * The workbench types below are projections of the generated OpenAPI `paths`, never\n * hand-written copies of them.\n */\n\n/**\n * The experiment setup as the API carries it: datasets, targets and evaluators. Read it,\n * change it, send it back whole.\n */\nexport type ExperimentWorkbenchState =\n paths[\"/api/v1/experiments/{slug}/workbench-state\"][\"put\"][\"requestBody\"][\"content\"][\"application/json\"][\"state\"];\n\nexport type ExperimentCreateResponse =\n paths[\"/api/v1/experiments\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * These endpoints are all `withRawResponse`, restored from the last\n * document that had them. The read answers one of two shapes, chosen by\n * `fields`: `state` splits the union the overloads promise.\n */\nexport interface ExperimentWorkbenchStateResponse {\n id: string;\n slug: string;\n name: string | null;\n state: ExperimentWorkbenchState | null;\n /** Send this back as expectedVersion to save safely */\n version: number;\n /** ISO 8601 timestamp of the last save */\n updatedAt: string;\n}\n\n/** What `fields: \"version\"` answers: the staleness probe without the setup. */\nexport interface ExperimentWorkbenchVersionProbe {\n id: string;\n slug: string;\n version: number;\n updatedAt: string;\n}\n\nexport interface ExperimentSaveWorkbenchStateResponse {\n /** The version the save produced */\n version: number;\n}\n\nexport interface ExperimentRestoreVersionResponse {\n /** The new version the restore wrote. History is never rewritten, so the\n * restored version is still in the list. */\n version: number;\n}\n\nexport interface ExperimentVersionSummary {\n /** Restore this version by this number. Named versions run 1, 2, 3 with no\n * gaps. The autosave row also has a number, but it changes with every\n * save, so read it as a handle and not as a place in the history. */\n version: number;\n /** The setup version this row was written at. */\n counterVersion: number;\n /** True for the single autosave row, which every ordinary save rewrites in place */\n autoSaved: boolean;\n commitMessage: string | null;\n /** Who wrote it: user, langy or api */\n authorLabel: string;\n /** User id, when a person wrote it */\n authorId: string | null;\n /** ISO 8601 timestamp of the first write */\n createdAt: string;\n /** ISO 8601 timestamp of the last write */\n updatedAt: string;\n}\n\nexport interface ExperimentVersionsResponse {\n /** Newest first, by `counterVersion` */\n versions: ExperimentVersionSummary[];\n /** Pass as `cursor` to read the next page, null on the last one */\n nextCursor: number | null;\n}\n\nexport class ExperimentsApiServiceError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"ExperimentsApiServiceError\";\n }\n}\n\nexport class ExperimentsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throw new ExperimentsApiServiceError(message, operation, error);\n }\n\n private async getUndeclaredEndpoint<T>({\n path,\n operation,\n }: {\n path: string;\n operation: string;\n }): Promise<T> {\n type UntypedClient = {\n GET: (\n path: string,\n init?: { parseAs?: \"json\" },\n ) => Promise<{ data?: unknown; error?: unknown; response: Response }>;\n };\n\n let result: { data?: unknown; error?: unknown; response: Response };\n try {\n result = await (this.apiClient as unknown as UntypedClient).GET(path, {\n parseAs: \"json\",\n });\n } catch (error) {\n this.handleApiError(operation, error);\n }\n\n return unwrapApiResult({\n operation,\n data: result.data,\n error: result.error,\n response: result.response,\n onError: this.handleApiError.bind(this),\n }) as T;\n }\n\n private async postUndeclaredEndpoint<T>({\n path,\n body,\n operation,\n }: {\n path: string;\n body?: unknown;\n operation: string;\n }): Promise<T> {\n type UntypedClient = {\n POST: (\n path: string,\n init?: { body?: unknown; parseAs?: \"json\" },\n ) => Promise<{ data?: unknown; error?: unknown; response: Response }>;\n };\n\n let result: { data?: unknown; error?: unknown; response: Response };\n try {\n result = await (this.apiClient as unknown as UntypedClient).POST(path, {\n ...(body !== undefined ? { body } : {}),\n parseAs: \"json\",\n });\n } catch (error) {\n this.handleApiError(operation, error);\n }\n\n return unwrapApiResult({\n operation,\n data: result.data,\n error: result.error,\n response: result.response,\n onError: this.handleApiError.bind(this),\n }) as T;\n }\n\n /**\n * Start a saved experiment by slug.\n */\n async startRun(\n slug: string,\n options: {\n parameters?: Record<string, string | number | boolean>;\n } = {},\n ): Promise<ExperimentRunStartResponse> {\n const body = toRunStartRequest({ parameters: options.parameters });\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/experiments/{slug}/run\", {\n params: { path: { slug } },\n ...(body !== undefined ? { body } : {}),\n });\n return unwrapApiResult({\n operation: `start experiment run for \"${slug}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as ExperimentRunStartResponse;\n }\n\n /**\n * Create an experiment.\n */\n async create({\n name,\n state,\n }: {\n name?: string;\n state?: ExperimentWorkbenchState;\n } = {}): Promise<ExperimentCreateResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/experiments\", {\n body: {\n ...(name !== undefined ? { name } : {}),\n ...(state !== undefined ? { state } : {}),\n },\n });\n return unwrapApiResult({\n operation: \"create experiment\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Read an experiment's setup, with the version to send back when saving.\n */\n async getWorkbenchState(options: {\n slug: string;\n fields: \"version\";\n }): Promise<ExperimentWorkbenchVersionProbe>;\n async getWorkbenchState(options: {\n slug: string;\n fields?: undefined;\n }): Promise<ExperimentWorkbenchStateResponse>;\n async getWorkbenchState({\n slug,\n fields,\n }: {\n slug: string;\n fields?: \"version\";\n }): Promise<ExperimentWorkbenchStateResponse | ExperimentWorkbenchVersionProbe> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/experiments/{slug}/workbench-state\",\n {\n params: {\n path: { slug },\n ...(fields !== undefined ? { query: { fields } } : {}),\n },\n },\n );\n // The endpoint is declared `withRawResponse`, so the document cannot\n // describe `data`'s shape (it types as `undefined`); the response type\n // is asserted explicitly here rather than inferred, restored from the\n // last document that had it.\n return unwrapApiResult<ExperimentWorkbenchStateResponse | ExperimentWorkbenchVersionProbe>({\n operation: `get workbench state for \"${slug}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Save an experiment's setup.\n */\n async setWorkbenchState({\n slug,\n state,\n expectedVersion,\n commitMessage,\n }: {\n slug: string;\n state: ExperimentWorkbenchState;\n expectedVersion?: number;\n commitMessage?: string;\n }): Promise<ExperimentSaveWorkbenchStateResponse> {\n const { data, error, response } = await this.apiClient.PUT(\n \"/api/v1/experiments/{slug}/workbench-state\",\n {\n params: { path: { slug } },\n body: {\n state,\n ...(expectedVersion !== undefined ? { expectedVersion } : {}),\n ...(commitMessage !== undefined ? { commitMessage } : {}),\n },\n },\n );\n // Declared `withRawResponse`; see getWorkbenchState above.\n return unwrapApiResult<ExperimentSaveWorkbenchStateResponse>({\n operation: `save workbench state for \"${slug}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /** The experiment's saved versions, newest first. */\n async listVersions({\n slug,\n limit,\n cursor,\n }: {\n slug: string;\n limit?: number;\n cursor?: number;\n }): Promise<ExperimentVersionsResponse> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/experiments/{slug}/versions\",\n {\n params: {\n path: { slug },\n query: {\n ...(limit !== undefined ? { limit } : {}),\n ...(cursor !== undefined ? { cursor } : {}),\n },\n },\n },\n );\n // Declared `withRawResponse`; see getWorkbenchState above.\n return unwrapApiResult<ExperimentVersionsResponse>({\n operation: `list versions for experiment \"${slug}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Bring an old version back by writing it forward as a new save. History is\n * never rewritten: the version restored from stays in the list.\n */\n async restoreVersion({\n slug,\n version,\n }: {\n slug: string;\n version: number;\n }): Promise<ExperimentRestoreVersionResponse> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/experiments/{slug}/versions/{version}/restore\",\n { params: { path: { slug, version } } },\n );\n // Declared `withRawResponse`; see getWorkbenchState above.\n return unwrapApiResult<ExperimentRestoreVersionResponse>({\n operation: `restore version ${version} of experiment \"${slug}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async getRunStatus(runId: string): Promise<ExperimentRunStatusResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/experiments/runs/{runId}\", {\n params: { path: { runId } },\n });\n // Declared `withRawResponse`; see getWorkbenchState above.\n return unwrapApiResult<ExperimentRunStatusResponse>({\n operation: `get run status for \"${runId}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * List experiments for the current project.\n */\n async listExperiments({\n pageSize,\n page,\n }: {\n pageSize?: number;\n page?: number;\n } = {}): Promise<ExperimentListResponse> {\n const search = new URLSearchParams();\n if (pageSize !== undefined) search.set(\"pageSize\", String(pageSize));\n if (page !== undefined) search.set(\"page\", String(page));\n const qs = search.toString();\n return this.getUndeclaredEndpoint<ExperimentListResponse>({\n path: `/api/v1/experiments${qs ? `?${qs}` : \"\"}`,\n operation: \"list experiments\",\n });\n }\n\n /**\n * List experiment runs for an experiment slug.\n */\n async listRuns({\n experimentSlug,\n pageSize,\n page,\n }: {\n experimentSlug: string;\n pageSize?: number;\n page?: number;\n }): Promise<ExperimentRunsListResponse> {\n const search = new URLSearchParams();\n search.set(\"experimentSlug\", experimentSlug);\n if (pageSize !== undefined) search.set(\"pageSize\", String(pageSize));\n if (page !== undefined) search.set(\"page\", String(page));\n return this.getUndeclaredEndpoint<ExperimentRunsListResponse>({\n path: `/api/v1/experiments/runs?${search.toString()}`,\n operation: `list runs for experiment \"${experimentSlug}\"`,\n });\n }\n\n /**\n * Fetch per-row results for a completed experiment run.\n */\n async getRunResults({\n runId,\n experimentSlug,\n }: {\n runId: string;\n experimentSlug?: string;\n }): Promise<ExperimentRunResultsResponse> {\n const search = new URLSearchParams();\n if (experimentSlug) search.set(\"experimentSlug\", experimentSlug);\n const qs = search.toString() ? `?${search.toString()}` : \"\";\n const body = await this.getUndeclaredEndpoint<ExperimentRunResultsResponse | null>({\n path: `/api/v1/experiments/runs/${encodeURIComponent(runId)}/results${qs}`,\n operation: `get run results for \"${runId}\"`,\n });\n if (body === null) {\n this.handleApiError(`get run results for \"${runId}\"`, {\n response: { status: 404 },\n data: { error: `Run not found: ${runId}` },\n });\n }\n return body;\n }\n\n /**\n * Start a saved Evaluations V3 experiment by slug through the unified evaluations-v3\n * backend.\n */\n async startV3Run({\n slug,\n body,\n }: {\n slug: string;\n body?: ExperimentRunStartRequest;\n }): Promise<ExperimentRunStartResponse> {\n return this.postUndeclaredEndpoint<ExperimentRunStartResponse>({\n path: `/api/evaluations/v3/${encodeURIComponent(slug)}/run`,\n body,\n operation: `start evaluation run for \"${slug}\"`,\n });\n }\n\n /**\n * Poll the status of an Evaluations V3 run.\n *\n * Hits `GET /api/evaluations/v3/runs/{runId}`.\n */\n async getV3RunStatus(runId: string): Promise<ExperimentV3RunStatusResponse> {\n return this.getUndeclaredEndpoint<ExperimentV3RunStatusResponse>({\n path: `/api/evaluations/v3/runs/${encodeURIComponent(runId)}`,\n operation: `get run status for \"${runId}\"`,\n });\n }\n\n /**\n * Fetch per-row results for an Evaluations V3 run.\n */\n async getV3RunResults({\n runId,\n experimentSlug,\n }: {\n runId: string;\n experimentSlug?: string;\n }): Promise<ExperimentRunResultsResponse> {\n const search = new URLSearchParams();\n if (experimentSlug) search.set(\"experimentSlug\", experimentSlug);\n const qs = search.toString() ? `?${search.toString()}` : \"\";\n const body = await this.getUndeclaredEndpoint<ExperimentRunResultsResponse | null>({\n path: `/api/evaluations/v3/runs/${encodeURIComponent(runId)}/results${qs}`,\n operation: `get run results for \"${runId}\"`,\n });\n if (body === null) {\n this.handleApiError(`get run results for \"${runId}\"`, {\n response: { status: 404 },\n data: { error: `Run not found: ${runId}` },\n });\n }\n return body;\n }\n}\n","/**\n * Maps the platform `/runs/{runId}/results` response into per-row results,\n * mirroring the python SDK's `_build_df_from_platform`: dataset entries\n * become base rows, and evaluations join on `(index, targetId)`.\n */\n\nimport type { ExperimentRunResultsResponse } from \"./experiments-api.service\";\nimport type { ExperimentRowResult } from \"./platformTypes\";\n\nconst matchesRow = ({\n row,\n index,\n targetId,\n}: {\n row: ExperimentRowResult;\n index: number;\n targetId?: string | null;\n}): boolean => {\n if (row.index !== index) return false;\n // Only constrain on target when the evaluation carries one and the rows are\n // multi-target (rows carry a `target`). Single-target rows have no `target`,\n // so an evaluation targetId should not exclude them.\n if (targetId && row.target !== undefined) {\n return row.target === targetId;\n }\n return true;\n};\n\ntype DatasetEntry = NonNullable<ExperimentRunResultsResponse[\"dataset\"]>[number];\ntype RunEvaluation = NonNullable<ExperimentRunResultsResponse[\"evaluations\"]>[number];\n\nconst toRow = (entry: DatasetEntry): ExperimentRowResult => {\n const entryData = entry.entry && typeof entry.entry === \"object\" ? entry.entry : {};\n\n const predicted = entry.predicted;\n const output =\n predicted && typeof predicted === \"object\" && \"output\" in predicted\n ? predicted.output\n : predicted;\n\n const row: ExperimentRowResult = {\n index: entry.index ?? 0,\n input: { ...entryData },\n output,\n traceId: entry.traceId ?? \"\",\n evaluations: {},\n };\n\n if (entry.cost != null) row.cost = entry.cost;\n if (entry.duration != null) row.duration = entry.duration;\n if (entry.error) row.error = entry.error;\n if (entry.targetId) row.target = entry.targetId;\n\n return row;\n};\n\nconst recordEvaluation = ({\n evaluation,\n index,\n name,\n rows,\n}: {\n evaluation: RunEvaluation;\n index: number;\n name: string;\n rows: ExperimentRowResult[];\n}): void => {\n for (const row of rows) {\n if (!matchesRow({ row, index, targetId: evaluation.targetId })) continue;\n\n const metric = row.evaluations[name] ?? {};\n if (evaluation.score != null) metric.score = evaluation.score;\n if (evaluation.passed != null) metric.passed = evaluation.passed;\n row.evaluations[name] = metric;\n }\n};\n\nconst joinEvaluations = ({\n evaluations,\n rowsByIndex,\n}: {\n evaluations: RunEvaluation[];\n rowsByIndex: Map<number, ExperimentRowResult[]>;\n}): void => {\n for (const evaluation of evaluations) {\n const index = evaluation.index;\n // A null, undefined, or empty name falls through to the evaluator id\n // (matching the python builder); the length check keeps the empty-string\n // case from being collapsed by nullish coalescing.\n const name =\n evaluation.name && evaluation.name.length > 0 ? evaluation.name : evaluation.evaluator;\n if (index == null || !name) continue;\n\n recordEvaluation({ evaluation, index, name, rows: rowsByIndex.get(index) ?? [] });\n }\n};\n\nexport const mapRunResultsToRows = (\n response: ExperimentRunResultsResponse,\n): ExperimentRowResult[] => {\n const datasetEntries = response.dataset ?? [];\n const evaluations = response.evaluations ?? [];\n\n const rows: ExperimentRowResult[] = datasetEntries.map(toRow);\n\n // Index rows by their dataset index so each evaluation joins against just the\n // rows for that index (one per target) instead of scanning every row, which\n // would be O(evaluations x rows) on large result sets.\n const rowsByIndex = new Map<number, ExperimentRowResult[]>();\n for (const row of rows) {\n const bucket = rowsByIndex.get(row.index);\n if (bucket) bucket.push(row);\n else rowsByIndex.set(row.index, [row]);\n }\n\n joinEvaluations({ evaluations, rowsByIndex });\n\n return rows;\n};\n","/**\n * Error classes for platform experiments API (Experiments Workbench)\n */\n\n/**\n * Base error for experiment operations\n */\nexport class ExperimentsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ExperimentsError\";\n }\n}\n\n/**\n * Error when experiment is not found\n */\nexport class ExperimentNotFoundError extends ExperimentsError {\n constructor(slug: string) {\n super(`Experiment not found: ${slug}`);\n this.name = \"ExperimentNotFoundError\";\n }\n}\n\n/**\n * Error when experiment run times out\n */\nexport class ExperimentTimeoutError extends ExperimentsError {\n readonly runId: string;\n readonly progress: number;\n readonly total: number;\n\n constructor(runId: string, progress: number, total: number) {\n super(`Experiment run timed out: ${runId} (${progress}/${total} completed)`);\n this.name = \"ExperimentTimeoutError\";\n this.runId = runId;\n this.progress = progress;\n this.total = total;\n }\n}\n\n/**\n * Error when experiment run fails\n */\nexport class ExperimentRunFailedError extends ExperimentsError {\n readonly runId: string;\n readonly errorMessage: string;\n\n constructor(runId: string, errorMessage: string) {\n super(`Experiment run failed: ${errorMessage}`);\n this.name = \"ExperimentRunFailedError\";\n this.runId = runId;\n this.errorMessage = errorMessage;\n }\n}\n\n/**\n * Error from the experiments API\n */\nexport class ExperimentsApiError extends ExperimentsError {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = \"ExperimentsApiError\";\n this.statusCode = statusCode;\n }\n}\n","// Experiment runs are stored as a timestamp-driven state in ClickHouse: a run\n// is \"completed\" once finishedAt is set, \"stopped\" once stoppedAt is set, and\n// otherwise still \"running\". A run that stopped emitting updates a while ago\n// without ever finishing is reported as \"interrupted\": the SDK process likely\n// died before sending finished_at/stopped_at.\n\nimport { ExperimentTimeoutError, ExperimentRunFailedError } from \"./platformErrors\";\nimport type { ExperimentRunSummary } from \"./platformTypes\";\n\nexport const INTERRUPTED_THRESHOLD_MS = 5 * 60 * 1000;\n\nexport type RunStatus = \"completed\" | \"stopped\" | \"running\" | \"interrupted\";\n\nexport interface RunTimestamps {\n createdAt?: number | null;\n updatedAt?: number | null;\n finishedAt?: number | null;\n stoppedAt?: number | null;\n}\n\nexport const deriveRunStatus = (timestamps: RunTimestamps, now: number = Date.now()): RunStatus => {\n if (timestamps.stoppedAt != null) return \"stopped\";\n if (timestamps.finishedAt != null) return \"completed\";\n if (timestamps.updatedAt != null && now - timestamps.updatedAt > INTERRUPTED_THRESHOLD_MS) {\n return \"interrupted\";\n }\n return \"running\";\n};\n\nexport const isTerminalStatus = (status: RunStatus): boolean =>\n status === \"completed\" || status === \"stopped\";\n\nexport const DEFAULT_POLL_INTERVAL = 2000;\nexport const DEFAULT_POLL_TIMEOUT = 600000; // 10 minutes\n\n/**\n * Status payload returned by the polling endpoint\n * (`GET /api/evaluations/v3/runs/{runId}`).\n */\nexport interface PollRunStatus {\n status: string;\n progress: number;\n total: number;\n summary?: ExperimentRunSummary;\n error?: string;\n}\n\nexport interface PollExperimentRunResult {\n status: \"completed\" | \"stopped\";\n summary: ExperimentRunSummary;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Polls a run to completion via `getStatus`, mirroring the python SDK.\n * Resolves on `completed`/`stopped`, throws on `failed` or timeout. The\n * fetcher is injected so experiment and workflow paths share this one loop.\n */\nexport const pollExperimentRun = async ({\n runId,\n getStatus,\n pollInterval = DEFAULT_POLL_INTERVAL,\n timeout = DEFAULT_POLL_TIMEOUT,\n onProgress,\n now = Date.now,\n}: {\n runId: string;\n getStatus: (runId: string) => Promise<PollRunStatus>;\n pollInterval?: number;\n timeout?: number;\n onProgress?: (progress: number, total: number) => void;\n now?: () => number;\n}): Promise<PollExperimentRunResult> => {\n const startTime = now();\n\n while (true) {\n if (now() - startTime > timeout) {\n const finalStatus = await getStatus(runId);\n throw new ExperimentTimeoutError(runId, finalStatus.progress, finalStatus.total);\n }\n\n await sleep(pollInterval);\n\n const status = await getStatus(runId);\n onProgress?.(status.progress, status.total);\n\n if (status.status === \"completed\") {\n return { status: \"completed\", summary: status.summary ?? {} };\n }\n\n if (status.status === \"failed\") {\n throw new ExperimentRunFailedError(runId, status.error ?? \"Unknown error\");\n }\n\n if (status.status === \"stopped\") {\n return {\n status: \"stopped\",\n summary: status.summary ?? {\n runId,\n totalCells: status.total,\n completedCells: status.progress,\n failedCells: 0,\n duration: now() - startTime,\n },\n };\n }\n }\n};\n\n/**\n * Replaces a URL's domain with a new base, preserving path/query/fragment.\n * The platform returns its own cloud URL for a run; rebasing it keeps a\n * self-hosted run pointing local instead of app.langwatch.ai.\n */\nexport const rebaseUrlToEndpoint = (url: string, newBase: string): string => {\n if (!url) return url;\n try {\n const parsedUrl = new URL(url);\n const parsedNewBase = new URL(newBase);\n return `${parsedNewBase.origin}${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;\n } catch {\n return url;\n }\n};\n\n/**\n * Fetches a run's per-row results, retrying through the brief window where\n * ClickHouse projection lags (404 or empty). Retries up to `maxAttempts`\n * only when the run reported rows; otherwise the first read is returned.\n */\nexport const fetchResultsWithRetry = async <T>({\n getResults,\n isEmpty,\n expectsRows,\n delay = DEFAULT_POLL_INTERVAL,\n maxAttempts = 6,\n}: {\n getResults: () => Promise<T>;\n isEmpty: (results: T) => boolean;\n expectsRows: boolean;\n delay?: number;\n maxAttempts?: number;\n}): Promise<T> => {\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const results = await getResults();\n if (expectsRows && isEmpty(results) && attempt < maxAttempts) {\n await sleep(delay);\n continue;\n }\n return results;\n } catch (error) {\n lastError = error;\n // Only the rows-expected path waits out the post-completion projection\n // lag (a 404 / \"not yet available\" right after the run finishes). With no\n // rows expected a thrown error is terminal, so surface it immediately\n // instead of delaying through every attempt.\n if (expectsRows && attempt < maxAttempts) {\n await sleep(delay);\n continue;\n }\n throw error;\n }\n }\n throw lastError;\n};\n","/**\n * ExperimentsFacade - Entry point for the experiments API\n */\n\nimport type { LangwatchApiClient } from \"@/internal/api/client\";\nimport { isLangWatchHandledError } from \"@/internal/api/errors\";\nimport type { Logger } from \"@/logger\";\n\nimport { Experiment } from \"./experiment\";\nimport { ExperimentsApiService, toRunStartRequest } from \"./experiments-api.service\";\nimport type {\n ExperimentRunStartResponse,\n ExperimentRunStatusResponse,\n} from \"./experiments-api.service\";\nimport { mapRunResultsToRows } from \"./mapResults\";\nimport {\n ExperimentsApiError,\n ExperimentNotFoundError,\n ExperimentTimeoutError,\n ExperimentRunFailedError,\n} from \"./platformErrors\";\nimport type {\n ExperimentRunResult,\n RunExperimentOptions,\n RunWithResultsOptions,\n ExperimentRunWithResults,\n ExperimentRunSummary,\n} from \"./platformTypes\";\nimport { printSummary } from \"./printSummary\";\nimport { pollExperimentRun, rebaseUrlToEndpoint, fetchResultsWithRetry } from \"./run-status\";\nimport type { ExperimentInitOptions } from \"./types\";\n\nconst DEFAULT_POLL_INTERVAL = 2000;\nconst DEFAULT_TIMEOUT = 600000; // 10 minutes\n\n/**\n * Asserts the shape of a raw endpoint's body. `data` types `undefined` on a\n * `withRawResponse` op (undescribable), so an `as` cast has no overlap; the\n * type comes in through this generic, the same trust boundary the API service's helpers use.\n */\nfunction rawResponseData<T>(data: unknown): T {\n return data as T;\n}\n\ntype ExperimentsFacadeConfig = {\n langwatchApiClient: LangwatchApiClient;\n endpoint: string;\n apiKey: string;\n logger: Logger;\n};\n\n/**\n * Facade for creating experiment sessions and running platform-configured experiments\n */\nexport class ExperimentsFacade {\n private readonly config: ExperimentsFacadeConfig;\n private readonly apiService: ExperimentsApiService;\n\n constructor(config: ExperimentsFacadeConfig) {\n this.config = config;\n this.apiService = new ExperimentsApiService({\n langwatchApiClient: config.langwatchApiClient,\n });\n }\n\n /**\n * Initialize a new experiment session (SDK-defined).\n * @param name - Name of the experiment (used as slug)\n * @param options - Optional configuration\n */\n async init(name: string, options?: ExperimentInitOptions): Promise<Experiment> {\n return Experiment.init(name, {\n apiClient: this.config.langwatchApiClient,\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n logger: this.config.logger,\n ...options,\n });\n }\n\n /**\n * Run a platform-configured experiment (Experiments Workbench). Prints a\n * summary and exits with code 1 on failure, unless `exitOnFailure: false`.\n * @param slug - The slug of the experiment (found in the experiment URL)\n */\n async run(slug: string, options?: RunExperimentOptions): Promise<ExperimentRunResult> {\n this.config.logger.info(`Running platform experiment: ${slug}`);\n const result = await this.runWithPolling(slug, options);\n return result;\n }\n\n /**\n * Run a platform experiment and return per-row structured results.\n * @param slug - The slug of the experiment (found in the experiment URL)\n * @param options - Optional inputs and polling configuration\n */\n async runWithResults(\n slug: string,\n options: RunWithResultsOptions = {},\n ): Promise<ExperimentRunWithResults> {\n this.config.logger.info(`Running platform experiment with results: ${slug}`);\n\n const body = toRunStartRequest({\n data: options.data,\n datasetId: options.datasetId,\n parameters: options.parameters,\n rowIndices: options.rowIndices,\n });\n\n const startResponse = await this.apiService.startV3Run({ slug, body });\n const { runId } = startResponse;\n\n const { status, summary } = await pollExperimentRun({\n runId,\n getStatus: (id) => this.apiService.getV3RunStatus(id),\n pollInterval: options.pollInterval,\n timeout: options.timeout,\n onProgress: options.onProgress,\n });\n\n // ClickHouse can lag right after completion: the results endpoint may 404\n // (\"not yet available\") or return 200 with an empty dataset before the rows\n // materialize. Retry both cases when the run reported rows, mirroring the\n // python SDK, instead of failing or returning an empty result.\n const results = await fetchResultsWithRetry({\n getResults: () => this.apiService.getV3RunResults({ runId, experimentSlug: slug }),\n isEmpty: (r) => (r.dataset?.length ?? 0) === 0,\n expectsRows: (summary.totalCells ?? 0) > 0,\n delay: options.pollInterval ?? DEFAULT_POLL_INTERVAL,\n });\n\n // Always return the URL rebased onto the configured endpoint, so a\n // self-hosted run does not surface a cloud (app.langwatch.ai) link. Both\n // the start response and the summary carry the platform's own URL; whichever\n // is present gets its domain replaced. Mirrors the python SDK.\n const rawRunUrl = startResponse.runUrl ?? summary.runUrl;\n const runUrl = rawRunUrl ? rebaseUrlToEndpoint(rawRunUrl, this.config.endpoint) : \"\";\n\n return {\n runId,\n runUrl,\n status,\n summary,\n rows: mapRunResultsToRows(results),\n };\n }\n\n /**\n * Run an experiment and wait for completion using polling\n */\n private async runWithPolling(\n slug: string,\n options: RunExperimentOptions = {},\n ): Promise<ExperimentRunResult> {\n const pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n // Start the run\n const startResponse = await this.startRun(slug);\n const { runId } = startResponse;\n\n // Use the run URL from API but replace domain with configured endpoint\n const apiRunUrl = startResponse.runUrl ?? \"\";\n const runUrl = apiRunUrl ? rebaseUrlToEndpoint(apiRunUrl, this.config.endpoint) : \"\";\n\n console.log(`Started experiment run: ${runId}`);\n if (runUrl) {\n console.log(`Follow live: ${runUrl}`);\n }\n\n const total = startResponse.total;\n let lastProgress = 0;\n\n // Print initial progress\n if (total > 0) {\n process.stdout.write(`Progress: 0/${total} (0%)`);\n }\n options.onProgress?.(0, total);\n\n const startTime = Date.now();\n\n // Poll until complete\n while (true) {\n if (Date.now() - startTime > timeout) {\n console.log(); // Newline after progress\n const finalStatus = await this.getRunStatus(runId);\n throw new ExperimentTimeoutError(runId, finalStatus.progress, finalStatus.total);\n }\n\n await this.sleep(pollInterval);\n\n const status = await this.getRunStatus(runId);\n const progress = status.progress;\n\n // Update progress display if changed\n if (progress !== lastProgress && status.total > 0) {\n const percentage = Math.round((progress / status.total) * 100);\n process.stdout.write(`\\rProgress: ${progress}/${status.total} (${percentage}%)`);\n lastProgress = progress;\n }\n\n options.onProgress?.(status.progress, status.total);\n\n if (status.status === \"completed\") {\n console.log(); // Newline after progress\n const summary = status.summary ?? {};\n return this.buildResult(runId, \"completed\", summary, runUrl ?? \"\");\n }\n\n if (status.status === \"failed\") {\n console.log(); // Newline after progress\n throw new ExperimentRunFailedError(runId, status.error ?? \"Unknown error\");\n }\n\n if (status.status === \"stopped\") {\n console.log(); // Newline after progress\n return this.buildResult(\n runId,\n \"stopped\",\n status.summary ?? {\n runId,\n totalCells: status.total,\n completedCells: status.progress,\n failedCells: 0,\n duration: Date.now() - startTime,\n },\n runUrl ?? \"\",\n );\n }\n }\n }\n\n /**\n * Start an experiment run\n */\n private async startRun(slug: string): Promise<ExperimentRunStartResponse> {\n let response;\n try {\n response = await this.config.langwatchApiClient.POST(\"/api/v1/experiments/{slug}/run\", {\n params: {\n path: { slug },\n },\n });\n } catch (error) {\n if (isLangWatchHandledError(error)) {\n this.handleStartRunError(slug, error.body, error.httpStatus);\n }\n throw error;\n }\n\n if (response.error) {\n this.handleStartRunError(slug, response.error, response.response.status);\n }\n\n // `/api/v1/experiments/{slug}/run` is declared `withRawResponse`\n // (`experiment-v3.rest.ts:208`), so the typed client's `data` is\n // `undefined` here — the document cannot describe a raw response's body.\n // The real shape is restored from the last document that had it\n // (`openapi-document.json` at `0a0f549cfd^`).\n return rawResponseData<ExperimentRunStartResponse>(response.data);\n }\n\n /**\n * Get the status of a run\n */\n private async getRunStatus(runId: string): Promise<ExperimentRunStatusResponse> {\n let response;\n try {\n response = await this.config.langwatchApiClient.GET(\"/api/v1/experiments/runs/{runId}\", {\n params: {\n path: { runId },\n },\n });\n } catch (error) {\n if (isLangWatchHandledError(error)) {\n this.handleRunStatusError(runId, error.body, error.httpStatus);\n }\n throw error;\n }\n\n if (response.error) {\n this.handleRunStatusError(runId, response.error, response.response.status);\n }\n\n // Declared `withRawResponse`; see startRun above.\n return rawResponseData<ExperimentRunStatusResponse>(response.data);\n }\n\n private handleStartRunError(slug: string, error: unknown, status: number): never {\n if (status === 404) {\n throw new ExperimentNotFoundError(slug);\n }\n\n if (status === 401) {\n throw new ExperimentsApiError(\"Unauthorized - check your API key\", 401);\n }\n\n let errorMessage = `Failed to start experiment: ${slug}`;\n if (typeof error === \"object\" && error !== null && \"error\" in error) {\n if (typeof error.error === \"string\") {\n errorMessage = error.error;\n }\n }\n throw new ExperimentsApiError(errorMessage, status);\n }\n\n private handleRunStatusError(runId: string, error: unknown, status: number): never {\n if (status === 404) {\n throw new ExperimentsApiError(`Run not found: ${runId}`, 404);\n }\n\n if (status === 401) {\n throw new ExperimentsApiError(\"Unauthorized - check your API key\", 401);\n }\n\n let errorMessage = `Failed to get run status: ${runId}`;\n if (typeof error === \"object\" && error !== null && \"error\" in error) {\n if (typeof error.error === \"string\") {\n errorMessage = error.error;\n }\n }\n throw new ExperimentsApiError(errorMessage, status);\n }\n\n /**\n * Build the result object from API response\n */\n private buildResult(\n runId: string,\n status: \"completed\" | \"failed\" | \"stopped\",\n summary: ExperimentRunSummary,\n runUrl: string,\n ): ExperimentRunResult {\n const completedCells = summary.completedCells ?? 0;\n const failedCells = summary.failedCells ?? 0;\n const duration = summary.duration ?? 0;\n\n const totalPassed = summary.totalPassed ?? completedCells - failedCells;\n const totalFailed = summary.totalFailed ?? failedCells;\n const passRate =\n summary.passRate ?? (completedCells > 0 ? (totalPassed / completedCells) * 100 : 0);\n\n const result: ExperimentRunResult = {\n runId,\n status,\n passed: totalPassed,\n failed: totalFailed,\n passRate,\n duration,\n runUrl,\n summary,\n printSummary: (exitOnFailure = true) => {\n printSummary({\n runId,\n status,\n passed: totalPassed,\n failed: totalFailed,\n passRate,\n duration,\n runUrl: summary.runUrl ?? runUrl,\n summary,\n });\n\n if (exitOnFailure && totalFailed > 0) {\n process.exit(1);\n }\n },\n toString: () =>\n `ExperimentRunResult { status: ${status.toUpperCase()}, passed: ${totalPassed}, failed: ${totalFailed}, passRate: ${passRate.toFixed(1)}%, duration: ${(duration / 1000).toFixed(1)}s }`,\n };\n\n // Custom Node.js inspect for console.log\n (result as Record<string | symbol, unknown>)[Symbol.for(\"nodejs.util.inspect.custom\")] = () =>\n result.toString();\n\n return result;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * EvaluationsFacade - Entry point for the Evaluations API (Online Evaluations /\n * Guardrails)\n * @example\n */\n\nimport { trace, SpanStatusCode, context as otelContext, type Span } from \"@opentelemetry/api\";\n\nimport { buildRequestHeaders } from \"@/internal/api/request-headers\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\nimport type { Logger } from \"@/logger\";\nimport { createLangWatchSpan } from \"@/observability-sdk/span/implementation\";\n\nimport { EvaluatorCallError, EvaluatorNotFoundError, EvaluationsApiError } from \"./errors\";\nimport type { EvaluationResult, EvaluateOptions, EvaluateRequest, EvaluateResponse } from \"./types\";\n\ntype EvaluationsFacadeConfig = {\n endpoint: string;\n apiKey: string;\n logger: Logger;\n};\n\nfunction toEvaluationResult(responseData: EvaluateResponse): EvaluationResult {\n return {\n status: responseData.status,\n ...(responseData.passed !== null &&\n responseData.passed !== undefined && { passed: responseData.passed }),\n ...(responseData.score !== null &&\n responseData.score !== undefined && { score: responseData.score }),\n ...(responseData.details !== null &&\n responseData.details !== undefined && { details: responseData.details }),\n ...(responseData.label !== null &&\n responseData.label !== undefined && { label: responseData.label }),\n ...(responseData.cost !== null &&\n responseData.cost !== undefined && { cost: responseData.cost }),\n };\n}\n\nfunction throwEvaluationFailure({\n error,\n slug,\n asGuardrail,\n langwatchSpan,\n otelSpan,\n}: {\n error: unknown;\n slug: string;\n asGuardrail: boolean | undefined;\n langwatchSpan: ReturnType<typeof createLangWatchSpan>;\n otelSpan: Span;\n}): never {\n const errorResult: EvaluationResult = {\n status: \"error\",\n details: error instanceof Error ? error.message : String(error),\n };\n\n // For guardrails, default to passed=true on error to avoid blocking\n if (asGuardrail) {\n errorResult.passed = true;\n }\n\n langwatchSpan.setOutput({\n type: asGuardrail ? \"guardrail_result\" : \"evaluation_result\",\n value: errorResult,\n });\n\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: errorResult.details,\n });\n\n if (error instanceof Error) {\n otelSpan.recordException(error);\n }\n\n if (\n error instanceof EvaluatorNotFoundError ||\n error instanceof EvaluationsApiError ||\n error instanceof EvaluatorCallError\n ) {\n throw error;\n }\n\n throw new EvaluatorCallError(slug, error instanceof Error ? error.message : String(error));\n}\n\nexport class EvaluationsFacade {\n readonly #endpoint: string;\n readonly #apiKey: string;\n readonly #logger: Logger;\n\n constructor(config: EvaluationsFacadeConfig) {\n this.#endpoint = config.endpoint;\n this.#apiKey = config.apiKey;\n this.#logger = config.logger;\n }\n\n /**\n * Run an evaluator or guardrail against provided data.\n * @param slug - The evaluator slug (e.g., \"presidio/pii_detection\", \"langevals/llm_boolean\")\n * @param options - Evaluation options including data, name, settings, and asGuardrail flag\n */\n evaluate = async (slug: string, options: EvaluateOptions): Promise<EvaluationResult> => {\n const { data, name, settings, asGuardrail } = options;\n const spanName = name ?? slug;\n const spanType = asGuardrail ? \"guardrail\" : \"evaluation\";\n\n // Get tracer and create a span attached to the current context\n const tracer = trace.getTracer(\"langwatch-evaluations\");\n\n // Get current trace/span IDs from active context\n const activeSpan = trace.getActiveSpan();\n const traceId = activeSpan ? activeSpan.spanContext().traceId : undefined;\n const parentSpanId = activeSpan ? activeSpan.spanContext().spanId : undefined;\n\n // Start the evaluation span\n const otelSpan = tracer.startSpan(\n spanName,\n {\n attributes: {\n \"langwatch.span.type\": spanType,\n },\n },\n otelContext.active(),\n );\n\n const langwatchSpan = createLangWatchSpan(otelSpan);\n\n // Set span input\n langwatchSpan.setType(spanType);\n langwatchSpan.setInput({\n data,\n ...(settings && { settings }),\n });\n\n try {\n // Build request payload\n const requestBody: EvaluateRequest = {\n trace_id: traceId ?? null,\n span_id: parentSpanId ?? null,\n name: name ?? null,\n data,\n settings,\n as_guardrail: asGuardrail,\n };\n\n // Call the evaluation API\n const url = `${this.#endpoint}/api/v1/evaluations/${slug}/evaluate`;\n\n this.#logger.debug(`Calling evaluation API: ${url}`);\n\n const response = await langwatchFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...buildRequestHeaders({ apiKey: this.#apiKey }),\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n\n if (response.status === 404) {\n throw new EvaluatorNotFoundError(slug);\n }\n\n throw new EvaluationsApiError(\n `Evaluation API returned ${response.status}: ${errorText}`,\n response.status,\n );\n }\n\n const responseData = (await response.json()) as EvaluateResponse;\n\n // Map response to result\n const result = toEvaluationResult(responseData);\n\n // Update span with output\n langwatchSpan.setOutput({\n type: asGuardrail ? \"guardrail_result\" : \"evaluation_result\",\n value: result,\n });\n\n // Set span status based on result\n if (result.status === \"error\") {\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: result.details ?? \"Evaluation failed\",\n });\n } else {\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n }\n\n return result;\n } catch (error) {\n throwEvaluationFailure({ error, slug, asGuardrail, langwatchSpan, otelSpan });\n } finally {\n // Always end the span\n otelSpan.end();\n }\n };\n}\n","/**\n * Error classes for the Evaluations API\n */\n\n/**\n * Base error for evaluation operations\n */\nexport class EvaluationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EvaluationError\";\n }\n}\n\n/**\n * Error when an evaluator call fails\n */\nexport class EvaluatorCallError extends EvaluationError {\n readonly evaluatorSlug: string;\n readonly statusCode?: number;\n\n constructor(evaluatorSlug: string, message: string, statusCode?: number) {\n super(`Evaluator '${evaluatorSlug}' failed: ${message}`);\n this.name = \"EvaluatorCallError\";\n this.evaluatorSlug = evaluatorSlug;\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Error when evaluator is not found\n */\nexport class EvaluatorNotFoundError extends EvaluationError {\n readonly evaluatorSlug: string;\n\n constructor(evaluatorSlug: string) {\n super(`Evaluator not found: ${evaluatorSlug}`);\n this.name = \"EvaluatorNotFoundError\";\n this.evaluatorSlug = evaluatorSlug;\n }\n}\n\n/**\n * Error from the evaluations API\n */\nexport class EvaluationsApiError extends EvaluationError {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = \"EvaluationsApiError\";\n this.statusCode = statusCode;\n }\n}\n","/**\n * The one place the SDK decides \"did the platform NAME this failure?\",\n * slotted before each service's own throw: a domain-error body raises the\n * typed error; anything else keeps the generic path's behaviour byte for byte.\n */\nimport { handledErrorFrom, type LangWatchHandledError } from \"@/internal/api/errors\";\n\nimport { extractStatusFromResponse } from \"./format-api-error\";\n\nexport interface ThrowIfHandledErrorParams {\n /** What was being attempted, e.g. `get trace \"abc\"`. */\n operation: string;\n /** The error body the HTTP client handed back. */\n error: unknown;\n /** The response it came on, when the service kept hold of it. */\n response?: Response;\n /** The status, when the service resolved it already. */\n status?: number;\n /** The sentence the generic path built — reused verbatim so nothing regresses. */\n message: string;\n}\n\n/**\n * Throws a {@link LangWatchHandledError} when the platform named the failure.\n * Returns — deliberately, so the caller throws its own error — when it did not.\n */\nexport function throwIfHandledError({\n operation,\n error,\n response,\n status,\n message,\n}: ThrowIfHandledErrorParams): void {\n const resolved = status ?? response?.status ?? extractStatusFromResponse(error);\n\n const handledError: LangWatchHandledError | null = handledErrorFrom({\n operation,\n body: error,\n status: resolved,\n message,\n });\n\n if (handledError) throw handledError;\n}\n","export class EvaluatorsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"EvaluatorsApiError\";\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\n\nimport { EvaluatorsApiError } from \"./errors\";\nimport type {\n CreateEvaluatorBody,\n DeleteEvaluatorResponse,\n EvaluatorResponse,\n UpdateEvaluatorBody,\n} from \"./types\";\n\n/**\n * Service for retrieving evaluator resources via the LangWatch API.\n *\n * Provides read-only access to project evaluators with computed fields.\n */\nexport class EvaluatorsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new EvaluatorsApiError(message, operation, error);\n }\n\n /**\n * Fetches all evaluators for the project.\n */\n async getAll(): Promise<EvaluatorResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/evaluators\");\n return unwrapApiResult({\n operation: \"fetch all evaluators\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Fetches a single evaluator by its ID or slug.\n */\n async get(idOrSlug: string): Promise<EvaluatorResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/evaluators/{idOrSlug}\", {\n params: { path: { idOrSlug } },\n });\n return unwrapApiResult({\n operation: `fetch evaluator with ID or slug \"${idOrSlug}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Creates a new evaluator.\n */\n async create(params: CreateEvaluatorBody): Promise<EvaluatorResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/evaluators\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create evaluator\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Updates an evaluator by its ID.\n */\n async update(id: string, params: UpdateEvaluatorBody): Promise<EvaluatorResponse> {\n const { data, error, response } = await this.apiClient.PUT(\"/api/v1/evaluators/{id}\", {\n params: { path: { id } },\n body: params,\n });\n return unwrapApiResult({\n operation: `update evaluator with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Deletes (archives) an evaluator by its ID.\n */\n async delete(id: string): Promise<DeleteEvaluatorResponse> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/evaluators/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `delete evaluator with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","export class ScenariosApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"ScenariosApiError\";\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\n\nimport { ScenariosApiError } from \"./errors\";\nimport type {\n CreateScenarioBody,\n DeleteScenarioResponse,\n ScenarioResponse,\n ScenarioVersionDetail,\n ScenarioVersionListResponse,\n UpdateScenarioBody,\n} from \"./types\";\n\nexport class ScenariosApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new ScenariosApiError(message, operation, error);\n }\n\n async getAll(): Promise<ScenarioResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/scenarios\");\n return unwrapApiResult({\n operation: \"fetch all scenarios\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async get(id: string): Promise<ScenarioResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/scenarios/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `fetch scenario with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async create(params: CreateScenarioBody): Promise<ScenarioResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/scenarios\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create scenario\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async update(id: string, params: UpdateScenarioBody): Promise<ScenarioResponse> {\n const { data, error, response } = await this.apiClient.PUT(\"/api/v1/scenarios/{id}\", {\n params: { path: { id } },\n body: params,\n });\n return unwrapApiResult({\n operation: `update scenario with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /** The saved versions of a scenario, newest first. */\n async listVersions(\n id: string,\n options?: { limit?: number; cursor?: number },\n ): Promise<ScenarioVersionListResponse> {\n const query = {\n ...(options?.limit !== undefined && { limit: options.limit }),\n ...(options?.cursor !== undefined && { cursor: options.cursor }),\n };\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/scenarios/{id}/versions\", {\n params: { path: { id }, query },\n });\n return unwrapApiResult({\n operation: `list versions of scenario \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /** One saved version of a scenario, with the content it saved. */\n async getVersion(id: string, version: number): Promise<ScenarioVersionDetail> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/scenarios/{id}/versions/{version}\",\n {\n params: { path: { id, version } },\n },\n );\n return unwrapApiResult({\n operation: `get version ${version} of scenario \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async delete(id: string): Promise<DeleteScenarioResponse> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/scenarios/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `delete scenario with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type SuiteResponse = NonNullable<\n paths[\"/api/v1/suites\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number] & {\n /** URL to view this suite on the LangWatch platform */\n platformUrl?: string;\n};\n\nexport type CreateSuiteBody = NonNullable<\n paths[\"/api/v1/suites\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdateSuiteBody = NonNullable<\n paths[\"/api/v1/suites/{id}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type SuiteRunResult =\n paths[\"/api/v1/suites/{id}/run\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport interface SuiteTarget {\n type: \"prompt\" | \"http\" | \"code\" | \"workflow\";\n referenceId: string;\n}\n\n/** Options for `POST /api/v1/suites/{id}/run`. */\nexport interface SuiteRunOptions {\n /**\n * Makes the run safe to retry: the same key on a second call joins the batch\n * the first started. Omitted, no key is sent and every call schedules its own\n * run. Pin one per logical run, never per attempt.\n */\n idempotencyKey?: string;\n /**\n * Constant values applied to every scenario in the run, e.g. a fixture id or\n * a tenant. A value supplied here overrides the scenario's own default for\n * that name.\n */\n parameters?: Record<string, string | number | boolean>;\n /**\n * One short line saying why this batch was run, e.g. a commit hash or what\n * changed. Every run of the batch carries it. Spaces around it are removed,\n * and a note of only spaces is sent as no note at all.\n */\n note?: string;\n}\n\n/** Which kind of suite `getAll` returns. Defaults to run plans. */\nexport type SuiteKind = \"custom\" | \"folder\";\n\nexport class SuitesApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SuitesApiError\";\n }\n}\n\n/**\n * @deprecated Use runPlans and testSuites; /api/v1/suites is a frozen alias.\n */\nexport class SuitesApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new SuitesApiError(message, operation, error);\n }\n\n async getAll(options?: { kind?: SuiteKind }): Promise<SuiteResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/suites\", {\n ...(options?.kind !== undefined && {\n params: { query: { kind: options.kind } },\n }),\n });\n return unwrapApiResult({\n operation: \"list suites\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async get(id: string): Promise<SuiteResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/suites/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `get suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async create(params: CreateSuiteBody): Promise<SuiteResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/suites\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create suite\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async update(id: string, params: UpdateSuiteBody): Promise<SuiteResponse> {\n const { data, error, response } = await this.apiClient.PATCH(\"/api/v1/suites/{id}\", {\n params: { path: { id } },\n body: params,\n });\n return unwrapApiResult({\n operation: `update suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async duplicate(id: string): Promise<SuiteResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/suites/{id}/duplicate\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `duplicate suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async run(id: string, options?: SuiteRunOptions): Promise<SuiteRunResult>;\n /**\n * @deprecated Pass `{ idempotencyKey }` instead. The options object is what\n * carries run parameters, and a positional key cannot reach them.\n */\n async run(id: string, idempotencyKey: string): Promise<SuiteRunResult>;\n async run(\n id: string,\n optionsOrIdempotencyKey?: SuiteRunOptions | string,\n ): Promise<SuiteRunResult> {\n const options: SuiteRunOptions =\n typeof optionsOrIdempotencyKey === \"string\"\n ? { idempotencyKey: optionsOrIdempotencyKey }\n : (optionsOrIdempotencyKey ?? {});\n\n const body: {\n idempotencyKey?: string;\n parameters?: Record<string, string | number | boolean>;\n note?: string;\n } = {};\n // Only the caller knows what makes two calls the same run, so an absent key\n // stays absent. Minting one here says \"retry-safe\" and delivers nothing:\n // every attempt would carry a different key.\n if (options.idempotencyKey !== undefined) body.idempotencyKey = options.idempotencyKey;\n if (options.parameters !== undefined) body.parameters = options.parameters;\n // A note of only spaces is no note: sending \"\" would store an empty string\n // every reader then has to filter out.\n const note = options.note?.trim();\n if (note) body.note = note;\n\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/suites/{id}/run\", {\n params: { path: { id } },\n body,\n });\n return unwrapApiResult({\n operation: `run suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async delete(id: string): Promise<{ id: string; archived: boolean }> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/suites/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `delete suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as { id: string; archived: boolean };\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\n/** One run plan, exactly as the REST surface answers it. */\nexport type RunPlan =\n paths[\"/api/v1/run-plans/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** What a run plan covers. A dynamic scope is resolved again at every run. */\nexport type RunPlanScope = RunPlan[\"scope\"];\n\n/** What a run goes against. */\nexport type RunPlanTarget = RunPlan[\"targets\"][number];\n\n/** The body `POST /api/v1/run-plans/run` takes. */\nexport type RunPlanRunBody = NonNullable<\n paths[\"/api/v1/run-plans/run\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** The configuration half of a run request. */\nexport type RunPlanConfig = RunPlanRunBody[\"config\"];\n\n/**\n * One evaluator a plan runs beside the ones its test suites attach. It reads\n * the conversation and the trace, never a scenario field.\n */\nexport type RunPlanEvaluator = NonNullable<RunPlanConfig[\"evaluators\"]>[number];\n\n/** What a scheduled run answers with. */\nexport type RunPlanRunResult =\n paths[\"/api/v1/run-plans/run\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** The body `POST /api/v1/run-plans/{id}/run` takes. */\nexport type RunPlanRerunBody = NonNullable<\n paths[\"/api/v1/run-plans/{id}/run\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class RunPlansApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The HTTP status the platform answered with, when the response was kept,\n * so the CLI's error reader does not degrade a named failure to\n * `network_error`.\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"RunPlansApiError\";\n }\n}\n\n/**\n * Typed client for the run plan family (`/api/v1/run-plans`).\n * @see specs/typescript-sdk/run-plans-and-test-suites.feature\n */\nexport class RunPlansApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation,\n error,\n options: { status },\n });\n throwIfHandledError({ operation, error, response, message });\n throw new RunPlansApiError(message, operation, error, status);\n }\n\n /** The project's run plans. Archived plans are left out unless asked for. */\n async list(options?: { includeArchived?: boolean }): Promise<RunPlan[]> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/run-plans\",\n options?.includeArchived ? { params: { query: { includeArchived: \"true\" } } } : {},\n );\n return unwrapApiResult({\n operation: \"list run plans\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as RunPlan[];\n }\n\n async get(id: string): Promise<RunPlan> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/run-plans/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `get run plan \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as RunPlan;\n }\n\n /**\n * Runs a configuration under a name.\n */\n async run(body: RunPlanRunBody): Promise<RunPlanRunResult> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/run-plans/run\", {\n body: withTrimmedNote(body),\n });\n return unwrapApiResult({\n operation: \"run a run plan\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as RunPlanRunResult;\n }\n\n /** Runs a plan again with the configuration it already holds. */\n async rerun(id: string, body: RunPlanRerunBody = {}): Promise<RunPlanRunResult> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/run-plans/{id}/run\", {\n params: { path: { id } },\n body: withTrimmedNote(body),\n });\n return unwrapApiResult({\n operation: `rerun run plan \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as RunPlanRunResult;\n }\n\n async archive(id: string): Promise<{ id: string; archived: true }> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/run-plans/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `archive run plan \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as { id: string; archived: true };\n }\n}\n\n/** Drops a note that holds only spaces, and trims the rest. */\nfunction withTrimmedNote<T extends { note?: string }>(body: T): T {\n const note = body.note?.trim();\n if (note) return { ...body, note };\n const { note: _dropped, ...rest } = body;\n return rest as T;\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\n/** One Instant Eval run, exactly as the REST surface answers it. */\nexport type InstantEvalRun =\n paths[\"/api/v1/instant-evals/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** One question a run asks, named by the output column it lands in. */\nexport type InstantEvalQuestion = InstantEvalRun[\"questions\"][number];\n\n/** The body that starts a run, and the same body that prices one. */\nexport type InstantEvalRunBody = NonNullable<\n paths[\"/api/v1/instant-evals\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** What a run would read and what judging it would cost. */\nexport type InstantEvalEstimate =\n paths[\"/api/v1/instant-evals/estimate\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** One page of a run's judgements, and where the next one starts. */\nexport type InstantEvalResultsPage =\n paths[\"/api/v1/instant-evals/{id}/results\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** One judgement the run recorded. */\nexport type InstantEvalJudgment = InstantEvalResultsPage[\"judgments\"][number];\n\n/** A few of a run's rows, with the judged text beside the verdict. */\nexport type InstantEvalSample =\n paths[\"/api/v1/instant-evals/{id}/sample\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class InstantEvalsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The HTTP status the platform answered with, when the response was kept,\n * so the CLI's error reader does not degrade a named failure to\n * `network_error`.\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"InstantEvalsApiError\";\n }\n}\n\n/**\n * Typed client for Instant Evals (`/api/v1/instant-evals`): a run judges one LangWatchQL statement\n * that projects `TraceId` and an eval column. Progress is polled; the project comes from the\n * credential.\n * @see modules/instant-eval/specs/instant-eval-api.feature\n */\nexport class InstantEvalsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation,\n error,\n options: { status },\n });\n throwIfHandledError({ operation, error, response, message });\n throw new InstantEvalsApiError(message, operation, error, status);\n }\n\n /**\n * The body of a successful answer. A failed response can arrive with no body\n * at all (a proxy answering 502 while the platform restarts), which leaves\n * `error` empty, so the response status decides and not the error alone.\n */\n private unwrap<T>({\n operation,\n data,\n error,\n response,\n }: {\n operation: string;\n data: unknown;\n error: unknown;\n response?: Response;\n }): T {\n const failed = response !== undefined && !response.ok;\n if (error || failed || data === undefined) {\n this.handleApiError(operation, error ?? undefined, response);\n }\n return data as T;\n }\n\n /** Starts a run. The judging happens on the queue. */\n async create(body: InstantEvalRunBody): Promise<InstantEvalRun> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/instant-evals\", { body });\n return this.unwrap<InstantEvalRun>({\n operation: \"start an instant eval run\",\n data,\n error,\n response,\n });\n }\n\n /** Prices a run without starting it. Nothing is judged and nothing is charged. */\n async estimate(body: InstantEvalRunBody): Promise<InstantEvalEstimate> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/instant-evals/estimate\", {\n body,\n });\n return this.unwrap<InstantEvalEstimate>({\n operation: \"estimate an instant eval run\",\n data,\n error,\n response,\n });\n }\n\n /** The project's runs, newest first. */\n async list(options?: {\n limit?: number;\n before?: string;\n beforeId?: string;\n }): Promise<InstantEvalRun[]> {\n const query = {\n ...(options?.limit === undefined ? {} : { limit: options.limit }),\n ...(options?.before === undefined ? {} : { before: options.before }),\n ...(options?.beforeId === undefined ? {} : { beforeId: options.beforeId }),\n };\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/instant-evals\", {\n params: { query } as never,\n });\n return this.unwrap<{ runs: InstantEvalRun[] }>({\n operation: \"list instant eval runs\",\n data,\n error,\n response,\n }).runs;\n }\n\n async get(id: string): Promise<InstantEvalRun> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/instant-evals/{id}\", {\n params: { path: { id } },\n });\n return this.unwrap<InstantEvalRun>({\n operation: `get instant eval run \"${id}\"`,\n data,\n error,\n response,\n });\n }\n\n /** Asks a run to stop. It stops before its next page. */\n async cancel(id: string): Promise<InstantEvalRun> {\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/instant-evals/{id}/cancel\",\n { params: { path: { id } } },\n );\n return this.unwrap<InstantEvalRun>({\n operation: `cancel instant eval run \"${id}\"`,\n data,\n error,\n response,\n });\n }\n\n /**\n * One page of a run's judgements. Pass the `nextCursor` a page answers with to read the page\n * after it. The last page carries none.\n */\n async results(\n id: string,\n options?: {\n questionId?: string;\n isMatched?: boolean;\n status?: InstantEvalJudgment[\"status\"];\n limit?: number;\n cursor?: string;\n },\n ): Promise<InstantEvalResultsPage> {\n const query = {\n ...(options?.questionId === undefined ? {} : { questionId: options.questionId }),\n // The query key stays `matched`, which is what the endpoint reads; the\n // option is named for what it is, which is a boolean.\n ...(options?.isMatched === undefined\n ? {}\n : { matched: options.isMatched ? \"true\" : \"false\" }),\n ...(options?.status === undefined ? {} : { status: options.status }),\n ...(options?.limit === undefined ? {} : { limit: options.limit }),\n ...(options?.cursor === undefined ? {} : { cursor: options.cursor }),\n };\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/instant-evals/{id}/results\",\n { params: { path: { id }, query } as never },\n );\n return this.unwrap<InstantEvalResultsPage>({\n operation: `read instant eval run \"${id}\" results`,\n data,\n error,\n response,\n });\n }\n\n /** A few of the run's rows, with the judged text beside the verdict. */\n async sample(id: string, options?: { n?: number }): Promise<InstantEvalSample> {\n const query = options?.n === undefined ? {} : { n: options.n };\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/instant-evals/{id}/sample\",\n { params: { path: { id }, query } as never },\n );\n return this.unwrap<InstantEvalSample>({\n operation: `sample instant eval run \"${id}\"`,\n data,\n error,\n response,\n });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport type { RunPlanRunResult } from \"@/client-sdk/services/run-plans/run-plans-api.service\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\n/** One test suite as the list answers it: a name and the scenarios filed in it. */\nexport type TestSuite = NonNullable<\n paths[\"/api/v1/test-suites\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\n/** One test suite read on its own, with its scenarios named. */\nexport type TestSuiteDetail =\n paths[\"/api/v1/test-suites/{id}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** The body `POST /api/v1/test-suites` takes. */\nexport type CreateTestSuiteBody = NonNullable<\n paths[\"/api/v1/test-suites\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/**\n * The body `PATCH /api/v1/test-suites/{id}` takes: any of the name, the full\n * field list and the full evaluator list.\n */\nexport type UpdateTestSuiteBody = NonNullable<\n paths[\"/api/v1/test-suites/{id}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** The body `rename` sends: the name alone. */\nexport type RenameTestSuiteBody = Pick<UpdateTestSuiteBody, \"name\"> &\n Required<Pick<UpdateTestSuiteBody, \"name\">>;\n\n/** One field a test suite declares: an identifier and a type. */\nexport type SuiteFieldDefinition = NonNullable<UpdateTestSuiteBody[\"fields\"]>[number];\n\n/** One evaluator attached to a test suite or a run plan, with its mappings. */\nexport type EvaluatorAttachment = NonNullable<UpdateTestSuiteBody[\"evaluators\"]>[number];\n\n/** Where one evaluator input reads its value: a source path or a literal. */\nexport type ScenarioMapping = EvaluatorAttachment[\"mappings\"][string];\n\n/** The body `POST /api/v1/test-suites/{id}/run` takes. */\nexport type RunTestSuiteBody = NonNullable<\n paths[\"/api/v1/test-suites/{id}/run\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** What a scheduled suite run answers with. It is a run plan run. */\nexport type TestSuiteRunResult = RunPlanRunResult;\n\nexport class TestSuitesApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /** The HTTP status the platform answered with, when the response was kept. */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"TestSuitesApiError\";\n }\n}\n\n/**\n * Typed client for the test suite family (`/api/v1/test-suites`).\n * @see specs/typescript-sdk/run-plans-and-test-suites.feature\n */\nexport class TestSuitesApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation,\n error,\n options: { status },\n });\n throwIfHandledError({ operation, error, response, message });\n throw new TestSuitesApiError(message, operation, error, status);\n }\n\n /** The project's test suites. Archived suites are left out unless asked for. */\n async list(options?: { includeArchived?: boolean }): Promise<TestSuite[]> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/test-suites\",\n options?.includeArchived ? { params: { query: { includeArchived: \"true\" } } } : {},\n );\n return unwrapApiResult({\n operation: \"list test suites\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as TestSuite[];\n }\n\n async create(params: CreateTestSuiteBody): Promise<TestSuite> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/test-suites\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create test suite\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as TestSuite;\n }\n\n async get(id: string): Promise<TestSuiteDetail> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/test-suites/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `get test suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as TestSuiteDetail;\n }\n\n /**\n * Edits a suite: any of its name, its fields and its evaluators. A field\n * list or an evaluator list replaces the one the suite holds; a key left\n * out keeps what the suite has.\n */\n async update(id: string, params: UpdateTestSuiteBody): Promise<TestSuite> {\n const { data, error, response } = await this.apiClient.PATCH(\"/api/v1/test-suites/{id}\", {\n params: { path: { id } },\n body: params,\n });\n return unwrapApiResult({\n operation: `update test suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as TestSuite;\n }\n\n /** Renames a suite. The slug is kept, so links and run history stay put. */\n async rename(id: string, params: RenameTestSuiteBody): Promise<TestSuite> {\n const { data, error, response } = await this.apiClient.PATCH(\"/api/v1/test-suites/{id}\", {\n params: { path: { id } },\n body: { name: params.name },\n });\n return unwrapApiResult({\n operation: `rename test suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as TestSuite;\n }\n\n /** Archives a suite. The scenarios filed in it are archived with it. */\n async archive(id: string): Promise<{ id: string; archived: true }> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/test-suites/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `archive test suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as { id: string; archived: true };\n }\n\n /**\n * Runs every scenario filed in the suite against the targets sent with the\n * request. A note of only spaces is no note.\n */\n async run(id: string, body: RunTestSuiteBody): Promise<TestSuiteRunResult> {\n const note = body.note?.trim();\n const { note: _dropped, ...rest } = body;\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/test-suites/{id}/run\", {\n params: { path: { id } },\n body: note ? { ...rest, note } : rest,\n });\n return unwrapApiResult({\n operation: `run test suite \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as TestSuiteRunResult;\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { ExperimentsApiService } from \"@/client-sdk/services/experiments/experiments-api.service\";\nimport { mapRunResultsToRows } from \"@/client-sdk/services/experiments/mapResults\";\nimport type {\n RunWithResultsOptions,\n ExperimentRunWithResults,\n} from \"@/client-sdk/services/experiments/platformTypes\";\nimport {\n pollExperimentRun,\n rebaseUrlToEndpoint,\n fetchResultsWithRetry,\n} from \"@/client-sdk/services/experiments/run-status\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type WorkflowResponse = NonNullable<\n paths[\"/api/v1/workflows\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type WorkflowDeleteResponse =\n paths[\"/api/v1/workflows/{id}\"][\"delete\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * Body for `POST /api/v1/workflows/{workflowId}/evaluate`. `data` and `dataset_id`\n * are mutually exclusive on the server (400 if both are sent).\n */\ninterface WorkflowEvaluateRequest {\n version_id?: string;\n data?: Record<string, unknown>[];\n dataset_id?: string;\n parameters?: Record<string, string | number | boolean>;\n row_indices?: number[];\n}\n\n/**\n * Response from `POST /api/v1/workflows/{workflowId}/evaluate`. Hand-written\n * because the route is not yet exposed via the generated OpenAPI types.\n */\ninterface WorkflowEvaluateResponse {\n run_id: string;\n run_url: string;\n workflow_version_id: string;\n version: string;\n}\n\nexport class WorkflowsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"WorkflowsApiError\";\n }\n}\n\nexport class WorkflowsApiService {\n private readonly apiClient: LangwatchApiClient;\n private readonly experimentsApiService: ExperimentsApiService;\n private readonly endpoint: string;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\"> & { endpoint?: string }) {\n // The run URLs this service rebases and the requests it issues have to\n // name the same host, so a caller that supplies only an endpoint gets a\n // client built on that endpoint rather than on the environment.\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiClient =\n config?.langwatchApiClient ?? createLangWatchApiClient(undefined, this.endpoint);\n this.experimentsApiService = new ExperimentsApiService({\n langwatchApiClient: this.apiClient,\n });\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throw new WorkflowsApiError(message, operation, error);\n }\n\n private async postUndeclaredEndpoint<T>({\n path,\n body,\n operation,\n }: {\n path: string;\n body?: unknown;\n operation: string;\n }): Promise<T> {\n type UntypedClient = {\n POST: (\n path: string,\n init?: { body?: unknown; parseAs?: \"json\" },\n ) => Promise<{ data?: unknown; error?: unknown; response: Response }>;\n };\n\n let result: { data?: unknown; error?: unknown; response: Response };\n try {\n result = await (this.apiClient as unknown as UntypedClient).POST(path, {\n ...(body !== undefined ? { body } : {}),\n parseAs: \"json\",\n });\n } catch (error) {\n this.handleApiError(operation, error);\n }\n\n return unwrapApiResult({\n operation,\n data: result.data,\n error: result.error,\n response: result.response,\n onError: this.handleApiError.bind(this),\n }) as T;\n }\n\n async getAll(): Promise<WorkflowResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/workflows\");\n return unwrapApiResult({\n operation: \"list workflows\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async get(id: string): Promise<WorkflowResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/workflows/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `get workflow \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async delete(id: string): Promise<WorkflowDeleteResponse> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/workflows/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `delete workflow \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Run a studio workflow evaluation and return per-row structured results.\n * @param workflowId - The studio workflow id\n * @param options - Optional inputs and polling configuration\n */\n async run(\n workflowId: string,\n options: RunWithResultsOptions & { versionId?: string } = {},\n ): Promise<ExperimentRunWithResults> {\n const body: WorkflowEvaluateRequest = {};\n if (options.versionId !== undefined) body.version_id = options.versionId;\n if (options.data !== undefined) body.data = options.data;\n if (options.datasetId !== undefined) body.dataset_id = options.datasetId;\n if (options.parameters !== undefined) body.parameters = options.parameters;\n if (options.rowIndices !== undefined) body.row_indices = options.rowIndices;\n\n const startResponse = await this.postUndeclaredEndpoint<WorkflowEvaluateResponse>({\n path: `/api/v1/workflows/${encodeURIComponent(workflowId)}/evaluate`,\n body,\n operation: `run workflow evaluation for \"${workflowId}\"`,\n });\n\n const runId = startResponse.run_id;\n\n const { status, summary } = await pollExperimentRun({\n runId,\n getStatus: (id) => this.experimentsApiService.getV3RunStatus(id),\n pollInterval: options.pollInterval,\n timeout: options.timeout,\n onProgress: options.onProgress,\n });\n\n // ClickHouse can lag right after completion: retry the results read through\n // the brief 404 / empty-dataset window when the run reported rows. Mirrors\n // the experiment path and the python SDK.\n const results = await fetchResultsWithRetry({\n getResults: () => this.experimentsApiService.getV3RunResults({ runId }),\n isEmpty: (r) => (r.dataset?.length ?? 0) === 0,\n expectsRows: (summary.totalCells ?? 0) > 0,\n delay: options.pollInterval,\n });\n\n // Rebase the run URL onto the configured endpoint so a self-hosted run does\n // not surface a cloud (app.langwatch.ai) link.\n const rawRunUrl = startResponse.run_url ?? summary.runUrl;\n const runUrl = rawRunUrl ? rebaseUrlToEndpoint(rawRunUrl, this.endpoint) : \"\";\n\n return {\n runId,\n runUrl,\n status,\n summary,\n rows: mapRunResultsToRows(results),\n };\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\n/** One run parameter an agent declares, as the platform lists it. */\nexport interface AgentParameterSpec {\n name: string;\n type: \"string\" | \"number\" | \"boolean\";\n options?: string[];\n default?: string | number | boolean;\n description?: string;\n required?: boolean;\n secret?: boolean;\n}\n\n/** One process connected as an instance of a connected agent. */\nexport interface AgentInstance {\n id: string;\n hostname: string;\n label?: string | null;\n connectedAt: string;\n}\n\nexport interface AgentResponse {\n id: string;\n name: string;\n type: string;\n config: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n platformUrl?: string;\n /** Connected agents: the environment the SDK registered under. */\n environment?: string | null;\n /** Connected agents: online while at least one instance is connected. */\n status?: \"online\" | \"offline\" | null;\n instances?: AgentInstance[];\n lastSeenAt?: string | null;\n /** The owner of a personal agent. */\n owner?: { userId: string; name: string } | null;\n /** The machine a host-scoped development agent belongs to. */\n hostLabel?: string | null;\n /**\n * Whether this key can run the agent. A personal development agent of\n * another person is listed all the same, so two agents of one name can be\n * told apart, but it cannot be run with this key.\n */\n selectable?: boolean;\n /** Why the agent cannot be run with this key; null when it can. */\n notSelectableReason?: \"owned_by_another_person\" | null;\n parameters?: AgentParameterSpec[];\n}\n\nexport interface AgentListResponse {\n data: AgentResponse[];\n pagination: {\n page: number;\n limit: number;\n total: number;\n totalPages: number;\n };\n}\n\n/** The body `POST /api/v1/agents/{id}/call` takes. */\nexport type AgentCallBody = NonNullable<\n paths[\"/api/v1/agents/{id}/call\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** One conversation message as the relay carries it, OpenAI style. */\nexport type AgentCallMessage = AgentCallBody[\"messages\"][number];\n\n/** The reply of the relay: the function's output and the instance that ran it. */\nexport type AgentCallResponse =\n paths[\"/api/v1/agents/{id}/call\"][\"post\"][\"responses\"][200][\"content\"][\"application/json\"];\n\n/** The ids `POST /api/v1/agents/{id}/test` answers with. */\nexport type AgentTestRunResponse =\n paths[\"/api/v1/agents/{id}/test\"][\"post\"][\"responses\"][200][\"content\"][\"application/json\"];\n\nexport class AgentsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"AgentsApiError\";\n }\n}\n\nexport class AgentsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new AgentsApiError(message, operation, error);\n }\n\n async list(params?: { page?: number; limit?: number }): Promise<AgentListResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/agents\", {\n params: { query: params },\n });\n return unwrapApiResult({\n operation: \"list agents\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as AgentListResponse;\n }\n\n async get(id: string): Promise<AgentResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/agents/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `get agent \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as AgentResponse;\n }\n\n async create(params: {\n name: string;\n type: string;\n config: Record<string, unknown>;\n workflowId?: string;\n }): Promise<AgentResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/agents\", {\n body: params as never,\n });\n return unwrapApiResult({\n operation: \"create agent\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as AgentResponse;\n }\n\n async update(\n id: string,\n params: {\n name?: string;\n type?: string;\n config?: Record<string, unknown>;\n },\n ): Promise<AgentResponse> {\n const { data, error, response } = await this.apiClient.PATCH(\"/api/v1/agents/{id}\", {\n params: { path: { id } },\n body: params as never,\n });\n return unwrapApiResult({\n operation: `update agent \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as AgentResponse;\n }\n\n async delete(id: string): Promise<{ id: string; name: string }> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/agents/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `delete agent \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as { id: string; name: string };\n }\n\n /**\n * Runs one turn of a connected agent through the relay: the platform\n * dispatches it to a live instance and answers with the function's output.\n */\n async call(id: string, body: AgentCallBody): Promise<AgentCallResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/agents/{id}/call\", {\n params: { path: { id } },\n body,\n });\n return unwrapApiResult({\n operation: `call agent \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as AgentCallResponse;\n }\n\n /**\n * Runs one scripted scenario against an agent: the user sends \"ping\", the agent answers,\n * and the run succeeds when the answer arrives. The project gains no scenario, run plan or\n * test suite, and the answer carries the run ids to follow.\n */\n async test(id: string): Promise<AgentTestRunResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/agents/{id}/test\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `test agent \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as AgentTestRunResponse;\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { components, paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type AnnotationResponse = components[\"schemas\"][\"Annotation\"];\n\nexport type CreateAnnotationBody = NonNullable<\n paths[\"/api/v1/annotations/trace/{id}\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type DeleteAnnotationResponse = { status?: string; message?: string };\n\n/**\n * The delete endpoint's `application/json` content is `unknown` in the\n * document (the generator no longer names it as a component schema), so\n * the shape this service promises is verified at runtime, not assumed.\n */\nfunction isDeleteAnnotationResponse(value: unknown): value is DeleteAnnotationResponse {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n (record.status === undefined || typeof record.status === \"string\") &&\n (record.message === undefined || typeof record.message === \"string\")\n );\n}\n\nexport class AnnotationsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"AnnotationsApiError\";\n }\n}\n\nexport class AnnotationsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new AnnotationsApiError(message, operation, error);\n }\n\n async getAll(): Promise<AnnotationResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/annotations\");\n return unwrapApiResult({\n operation: \"fetch all annotations\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }).data;\n }\n\n async get(id: string): Promise<AnnotationResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/annotations/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `fetch annotation with ID \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }).data;\n }\n\n async getByTrace(traceId: string): Promise<AnnotationResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/annotations/trace/{id}\", {\n params: { path: { id: traceId } },\n });\n return unwrapApiResult({\n operation: `fetch annotations for trace \"${traceId}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }).data;\n }\n\n async create(traceId: string, params: CreateAnnotationBody): Promise<AnnotationResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/annotations/trace/{id}\", {\n params: { path: { id: traceId } },\n body: params,\n });\n return unwrapApiResult({\n operation: \"create annotation\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }).data;\n }\n\n async delete(id: string): Promise<DeleteAnnotationResponse> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/annotations/{id}\", {\n params: { path: { id } },\n });\n const operation = `delete annotation with ID \"${id}\"`;\n const result = unwrapApiResult({\n operation,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n if (!isDeleteAnnotationResponse(result)) {\n this.handleApiError(\n operation,\n new Error(\"Delete annotation response has an unexpected shape\"),\n );\n }\n return result;\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\ntype Json<T> = T extends { content: { \"application/json\": infer Body } } ? Body : never;\n\n/** A dashboard as `/api/v1/dashboards/{dashboardId}` answers it. */\nexport type Dashboard = Json<paths[\"/api/v1/dashboards/{dashboardId}\"][\"get\"][\"responses\"][\"200\"]>;\n\n/** One row of the dashboards list, with its widget count. */\nexport type DashboardListItem = Json<\n paths[\"/api/v1/dashboards\"][\"get\"][\"responses\"][\"200\"]\n>[\"data\"][number];\n\n/** A widget on a dashboard, as the dashboard's widget routes answer it. */\nexport type DashboardWidget = Json<\n paths[\"/api/v1/dashboards/{dashboardId}/widgets/{widgetId}\"][\"get\"][\"responses\"][\"200\"]\n>;\n\n/** What creating a widget on a dashboard submits. */\nexport type CreateDashboardWidgetInput = NonNullable<\n paths[\"/api/v1/dashboards/{dashboardId}/widgets\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\n/** What updating a widget on a dashboard submits; a field left out keeps its value. */\nexport type UpdateDashboardWidgetInput = NonNullable<\n paths[\"/api/v1/dashboards/{dashboardId}/widgets/{widgetId}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class DashboardsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"DashboardsApiError\";\n }\n}\n\n/**\n * Typed client for `/api/v1/dashboards`: the key's project's dashboards and the widgets on them.\n * The project comes from the API key; while Dashboards is off the routes answer 404.\n */\nexport class DashboardsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError({\n operation,\n error,\n response,\n }: {\n operation: string;\n error: unknown;\n response?: Response;\n }): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({ operation, error, options: { status } });\n throwIfHandledError({ operation, error, response, message });\n throw new DashboardsApiError(message, operation, error, status);\n }\n\n async list(): Promise<{ data: DashboardListItem[] }> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/dashboards\");\n if (error) this.handleApiError({ operation: \"list dashboards\", error, response });\n return data as { data: DashboardListItem[] };\n }\n\n async get(dashboardId: string): Promise<Dashboard> {\n const operation = `get dashboard \"${dashboardId}\"`;\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/dashboards/{dashboardId}\", {\n params: { path: { dashboardId } },\n });\n if (error) this.handleApiError({ operation, error, response });\n return data as Dashboard;\n }\n\n async create({ name }: { name: string }): Promise<Dashboard> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/dashboards\", {\n body: { name },\n });\n if (error) this.handleApiError({ operation: \"create dashboard\", error, response });\n return data as Dashboard;\n }\n\n /** `description: null` clears it; a field left out keeps its value. */\n async update({\n dashboardId,\n name,\n description,\n }: {\n dashboardId: string;\n name?: string;\n description?: string | null;\n }): Promise<Dashboard> {\n const operation = `update dashboard \"${dashboardId}\"`;\n const { data, error, response } = await this.apiClient.PATCH(\n \"/api/v1/dashboards/{dashboardId}\",\n {\n params: { path: { dashboardId } },\n body: {\n ...(name === undefined ? {} : { name }),\n ...(description === undefined ? {} : { description }),\n },\n },\n );\n if (error) this.handleApiError({ operation, error, response });\n return data as Dashboard;\n }\n\n /** Deletes the dashboard with the widgets on it; answers its id and name. */\n async delete(dashboardId: string): Promise<{ id: string; name: string }> {\n const operation = `delete dashboard \"${dashboardId}\"`;\n const { data, error, response } = await this.apiClient.DELETE(\n \"/api/v1/dashboards/{dashboardId}\",\n {\n params: { path: { dashboardId } },\n },\n );\n if (error) this.handleApiError({ operation, error, response });\n return data as { id: string; name: string };\n }\n\n async listWidgets(dashboardId: string): Promise<{ data: DashboardWidget[] }> {\n const operation = `list widgets on dashboard \"${dashboardId}\"`;\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/dashboards/{dashboardId}/widgets\",\n {\n params: { path: { dashboardId } },\n },\n );\n if (error) this.handleApiError({ operation, error, response });\n return data as { data: DashboardWidget[] };\n }\n\n /** Creates a widget placed on this dashboard. */\n async createWidget({\n dashboardId,\n widget,\n }: {\n dashboardId: string;\n widget: CreateDashboardWidgetInput;\n }): Promise<DashboardWidget> {\n const operation = `create widget on dashboard \"${dashboardId}\"`;\n const { data, error, response } = await this.apiClient.POST(\n \"/api/v1/dashboards/{dashboardId}/widgets\",\n { params: { path: { dashboardId } }, body: widget },\n );\n if (error) this.handleApiError({ operation, error, response });\n return data as DashboardWidget;\n }\n\n async getWidget({\n dashboardId,\n widgetId,\n }: {\n dashboardId: string;\n widgetId: string;\n }): Promise<DashboardWidget> {\n const operation = `get widget \"${widgetId}\" on dashboard \"${dashboardId}\"`;\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/dashboards/{dashboardId}/widgets/{widgetId}\",\n { params: { path: { dashboardId, widgetId } } },\n );\n if (error) this.handleApiError({ operation, error, response });\n return data as DashboardWidget;\n }\n\n async updateWidget({\n dashboardId,\n widgetId,\n changes,\n }: {\n dashboardId: string;\n widgetId: string;\n changes: UpdateDashboardWidgetInput;\n }): Promise<DashboardWidget> {\n const operation = `update widget \"${widgetId}\" on dashboard \"${dashboardId}\"`;\n const { data, error, response } = await this.apiClient.PATCH(\n \"/api/v1/dashboards/{dashboardId}/widgets/{widgetId}\",\n { params: { path: { dashboardId, widgetId } }, body: changes },\n );\n if (error) this.handleApiError({ operation, error, response });\n return data as DashboardWidget;\n }\n\n /** The route answers `204` with no body. */\n async deleteWidget({\n dashboardId,\n widgetId,\n }: {\n dashboardId: string;\n widgetId: string;\n }): Promise<void> {\n const operation = `delete widget \"${widgetId}\" on dashboard \"${dashboardId}\"`;\n const { error, response } = await this.apiClient.DELETE(\n \"/api/v1/dashboards/{dashboardId}/widgets/{widgetId}\",\n { params: { path: { dashboardId, widgetId } } },\n );\n if (error) this.handleApiError({ operation, error, response });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\n\n/** One Analytics Report in the list, with how many graphs it holds. */\nexport interface AnalyticsReportSummary {\n id: string;\n name: string;\n order: number;\n graphCount: number;\n createdAt: string;\n updatedAt: string;\n platformUrl?: string;\n}\n\n/** One Analytics Report with its graphs. */\nexport interface AnalyticsReportDetail {\n id: string;\n name: string;\n order: number;\n graphs: unknown[];\n createdAt: string;\n updatedAt: string;\n platformUrl?: string;\n}\n\nexport class AnalyticsReportsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /** The HTTP status the platform answered with, when the response was kept. */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"AnalyticsReportsApiError\";\n }\n}\n\n/**\n * Typed client for the Analytics Reports at `/api/v1/analytics/reports`, deprecated by the\n * platform in favour of the Dashboards API (`DashboardsApiService`, `/api/v1/dashboards`).\n */\nexport class AnalyticsReportsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: { status },\n });\n throwIfHandledError({ operation, error, response, message });\n throw new AnalyticsReportsApiError(message, operation, error, status);\n }\n\n async list(): Promise<{ data: AnalyticsReportSummary[] }> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/analytics/reports\");\n return unwrapApiResult({\n operation: \"list analytics reports\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as { data: AnalyticsReportSummary[] };\n }\n\n async get(id: string): Promise<AnalyticsReportDetail> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/analytics/reports/{reportId}\",\n {\n params: { path: { reportId: id } },\n },\n );\n return unwrapApiResult({\n operation: `get analytics report \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as AnalyticsReportDetail;\n }\n\n async create(params: { name: string }): Promise<AnalyticsReportDetail> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/analytics/reports\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create analytics report\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as AnalyticsReportDetail;\n }\n\n async rename(id: string, params: { name: string }): Promise<AnalyticsReportDetail> {\n const { data, error, response } = await this.apiClient.PATCH(\n \"/api/v1/analytics/reports/{reportId}\",\n {\n params: { path: { reportId: id } },\n body: params,\n },\n );\n return unwrapApiResult({\n operation: `rename analytics report \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as AnalyticsReportDetail;\n }\n\n async delete(id: string): Promise<{ id: string; name: string }> {\n const { data, error, response } = await this.apiClient.DELETE(\n \"/api/v1/analytics/reports/{reportId}\",\n {\n params: { path: { reportId: id } },\n },\n );\n return unwrapApiResult({\n operation: `delete analytics report \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n }) as unknown as { id: string; name: string };\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type ModelProvidersListResponse =\n paths[\"/api/v1/model-providers\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type UpdateModelProviderBody = NonNullable<\n paths[\"/api/v1/model-providers/{provider}\"][\"put\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class ModelProvidersApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"ModelProvidersApiError\";\n }\n}\n\nexport class ModelProvidersApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new ModelProvidersApiError(message, operation, error);\n }\n\n async list(): Promise<ModelProvidersListResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/model-providers\");\n return unwrapApiResult({\n operation: \"list model providers\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async set(\n provider: string,\n params: UpdateModelProviderBody,\n ): Promise<ModelProvidersListResponse> {\n const { data, error, response } = await this.apiClient.PUT(\n \"/api/v1/model-providers/{provider}\",\n {\n params: { path: { provider } },\n body: params,\n },\n );\n return unwrapApiResult({\n operation: `set model provider \"${provider}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type AnalyticsTimeseriesBody = NonNullable<\n paths[\"/api/v1/analytics/timeseries\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type AnalyticsTimeseriesResponse =\n paths[\"/api/v1/analytics/timeseries\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class AnalyticsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"AnalyticsApiError\";\n }\n}\n\nexport class AnalyticsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new AnalyticsApiError(message, operation, error);\n }\n\n async timeseries(params: AnalyticsTimeseriesBody): Promise<AnalyticsTimeseriesResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/analytics/timeseries\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"query analytics\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\n/** The typed columns/rows/statistics payload `POST /api/v1/query` answers with. */\nexport type QueryRunResult =\n paths[\"/api/v1/query\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/** The queryable dataset/column catalog `GET /api/v1/query/schema` answers with. */\nexport type QuerySchemaResult =\n paths[\"/api/v1/query/schema\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * Both query languages as `GET /api/v1/query/reference` describes them: {@link QuerySchemaResult}\n * plus the filter language, examples and decision table. No tenant data; values come from facets.\n */\nexport type QueryReferenceResult =\n paths[\"/api/v1/query/reference\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * The body a query request sends.\n */\nexport type QueryRunParams = NonNullable<\n paths[\"/api/v1/query\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport class QueryApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The HTTP status the platform answered with, when the response was kept.\n * Without it the CLI's error reader can only guess `network_error` for a\n * failure the platform named precisely — same rationale as `TracesApiError`.\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"QueryApiError\";\n }\n}\n\n/**\n * Typed client for the LangWatchQL query doors (`POST /api/v1/query` and `GET\n * /api/v1/query/schema`) — the same governed query surface the workbench and saved charts\n * run through, exposed directly rather than only via a saved chart's statement.\n */\nexport class QueryApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation: operation,\n error,\n options: { status },\n });\n // A failure the platform NAMED (a code, a status, a meta bag) is raised as the typed\n // `LangWatchHandledError`, so the CLI's error output carries the real code instead of\n // degrading everything to `network_error`. The canonical envelope arrives at the top level\n // of the body here, exactly where the shared reader looks — this family publishes the same\n // error shape as every other one.\n throwIfHandledError({ operation, error, response, message });\n throw new QueryApiError(message, operation, error, status);\n }\n\n /**\n * Runs one read-only LangWatchQL `SELECT` over the analytics views and\n * returns typed columns, rows, execution statistics, truncation state and\n * diagnostics, scoped to the caller's project.\n */\n async query(params: QueryRunParams): Promise<QueryRunResult> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/query\", { body: params });\n return unwrapApiResult({\n operation: \"run query\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Lists the LangWatchQL analytics datasets this key may query, with each column's type,\n * description, the permissions that unlock it, and whether this caller holds them — plus\n * each dataset's grain, join keys, partition-pruning time column, freshness and a runnable\n */\n async schema(): Promise<QuerySchemaResult> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/query/schema\", {});\n return unwrapApiResult({\n operation: \"discover query schema\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * Describes both query languages in one payload: the LangWatchQL schema, limits and endpoints,\n * the trace filter's syntax and fields, worked examples validated in both, and which language\n * answers which kind of question.\n */\n async reference(): Promise<QueryReferenceResult> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/query/reference\", {});\n if (error) this.handleApiError(\"read query reference\", error, response);\n return data;\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type TriggerResponse = NonNullable<\n paths[\"/api/v1/triggers\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type CreateTriggerBody = NonNullable<\n paths[\"/api/v1/triggers\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdateTriggerBody = NonNullable<\n paths[\"/api/v1/triggers/{triggerId}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type TriggerDeleteResponse =\n paths[\"/api/v1/triggers/{triggerId}\"][\"delete\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class TriggersApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"TriggersApiError\";\n }\n}\n\nexport class TriggersApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new TriggersApiError(message, operation, error);\n }\n\n async getAll(): Promise<TriggerResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/triggers\");\n return unwrapApiResult({\n operation: \"list triggers\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async get(id: string): Promise<TriggerResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/triggers/{triggerId}\", {\n params: { path: { triggerId: id } },\n });\n return unwrapApiResult({\n operation: `get trigger \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async create(params: CreateTriggerBody): Promise<TriggerResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/triggers\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create trigger\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async update(id: string, params: UpdateTriggerBody): Promise<TriggerResponse> {\n const { data, error, response } = await this.apiClient.PATCH(\"/api/v1/triggers/{triggerId}\", {\n params: { path: { triggerId: id } },\n body: params,\n });\n return unwrapApiResult({\n operation: `update trigger \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async delete(id: string): Promise<TriggerDeleteResponse> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/triggers/{triggerId}\", {\n params: { path: { triggerId: id } },\n });\n return unwrapApiResult({\n operation: `delete trigger \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type GraphResponse = NonNullable<\n paths[\"/api/v1/graphs\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n>[number];\n\nexport type CreateGraphBody = NonNullable<\n paths[\"/api/v1/graphs\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type UpdateGraphBody = NonNullable<\n paths[\"/api/v1/graphs/{id}\"][\"patch\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type GraphDeleteResponse =\n paths[\"/api/v1/graphs/{id}\"][\"delete\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport class GraphsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"GraphsApiError\";\n }\n}\n\nexport class GraphsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new GraphsApiError(message, operation, error);\n }\n\n async getAll(dashboardId?: string): Promise<GraphResponse[]> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/graphs\", {\n params: { query: { dashboardId } },\n });\n return unwrapApiResult({\n operation: \"list graphs\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async get(id: string): Promise<GraphResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/graphs/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `get graph \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async create(params: CreateGraphBody): Promise<GraphResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/graphs\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"create graph\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async update(id: string, params: UpdateGraphBody): Promise<GraphResponse> {\n const { data, error, response } = await this.apiClient.PATCH(\"/api/v1/graphs/{id}\", {\n params: { path: { id } },\n body: params,\n });\n return unwrapApiResult({\n operation: `update graph \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async delete(id: string): Promise<GraphDeleteResponse> {\n const { data, error, response } = await this.apiClient.DELETE(\"/api/v1/graphs/{id}\", {\n params: { path: { id } },\n });\n return unwrapApiResult({\n operation: `delete graph \"${id}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport type { InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type SimulationRunsListResponse =\n paths[\"/api/v1/simulation-runs\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type SimulationRunResponse =\n paths[\"/api/v1/simulation-runs/{scenarioRunId}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * One evaluator's result on a finished run: its status, whether it was\n * required, and the verdict, score or reason it produced.\n */\nexport type SimulationRunEvaluation = NonNullable<\n NonNullable<SimulationRunResponse[\"results\"]>[\"evaluations\"]\n>[number];\n\nexport type SimulationRunsBatchesListResponse =\n paths[\"/api/v1/simulation-runs/batches/list\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type SimulationRunsListParams = NonNullable<\n paths[\"/api/v1/simulation-runs\"][\"get\"][\"parameters\"][\"query\"]\n>;\n\nexport type SimulationRunsBatchesListParams =\n paths[\"/api/v1/simulation-runs/batches/list\"][\"get\"][\"parameters\"][\"query\"];\n\nexport class SimulationRunsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SimulationRunsApiError\";\n }\n}\n\nexport class SimulationRunsApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status: response?.status ?? extractStatusFromResponse(error),\n },\n });\n throwIfHandledError({ operation, error, message });\n throw new SimulationRunsApiError(message, operation, error);\n }\n\n async getAll(params?: SimulationRunsListParams): Promise<SimulationRunsListResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/simulation-runs\", {\n params: { query: params },\n });\n return unwrapApiResult({\n operation: \"list simulation runs\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async get(scenarioRunId: string): Promise<SimulationRunResponse> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/simulation-runs/{scenarioRunId}\",\n {\n params: { path: { scenarioRunId } },\n },\n );\n return unwrapApiResult({\n operation: `get simulation run \"${scenarioRunId}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n async listBatches(\n params: SimulationRunsBatchesListParams,\n ): Promise<SimulationRunsBatchesListResponse> {\n const { data, error, response } = await this.apiClient.GET(\n \"/api/v1/simulation-runs/batches/list\",\n {\n params: { query: params },\n },\n );\n return unwrapApiResult({\n operation: \"list simulation run batches\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","import {\n extractStatusFromResponse,\n formatApiErrorForOperation,\n} from \"@/client-sdk/services/_shared/format-api-error\";\nimport { unwrapApiResult } from \"@/client-sdk/services/_shared/unwrap-api-result\";\nimport { type InternalConfig } from \"@/client-sdk/types\";\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"@/internal/api/client\";\nimport type { paths } from \"@/internal/generated/openapi/api-client\";\n\nexport type TraceSearchBody = NonNullable<\n paths[\"/api/v1/traces/search\"][\"post\"][\"requestBody\"]\n>[\"content\"][\"application/json\"];\n\nexport type TraceSearchResponse =\n paths[\"/api/v1/traces/search\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\n/**\n * What `GET /api/v1/traces/facets` takes. `field` is what splits the two answers: without it the\n * endpoint describes every facet the project has, with it one field's values.\n */\nexport type TraceFacetsQuery = NonNullable<\n paths[\"/api/v1/traces/facets\"][\"get\"][\"parameters\"][\"query\"]\n>;\n\nexport type TraceFacetsResponse =\n paths[\"/api/v1/traces/facets\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\ntype TraceGetResponseRaw =\n paths[\"/api/v1/traces/{traceId}\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"];\n\nexport type TraceGetResponse = TraceGetResponseRaw extends string\n ? TraceGetResponseRaw\n : TraceGetResponseRaw extends object\n ? TraceGetResponseRaw & {\n /** URL to view this trace on the LangWatch platform */\n platformUrl?: string;\n }\n : TraceGetResponseRaw;\n\nexport class TracesApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The HTTP status the platform answered with.\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"TracesApiError\";\n }\n}\n\nexport class TracesApiService {\n private readonly apiClient: LangwatchApiClient;\n\n constructor(config?: Pick<InternalConfig, \"langwatchApiClient\">) {\n this.apiClient = config?.langwatchApiClient ?? createLangWatchApiClient();\n }\n\n private handleApiError(operation: string, error: unknown, response?: Response): never {\n const status = response?.status ?? extractStatusFromResponse(error);\n const message = formatApiErrorForOperation({\n operation: operation,\n error: error,\n options: {\n status,\n },\n });\n throw new TracesApiError(message, operation, error, status);\n }\n\n async search(params: TraceSearchBody): Promise<TraceSearchResponse> {\n const { data, error, response } = await this.apiClient.POST(\"/api/v1/traces/search\", {\n body: params,\n });\n return unwrapApiResult({\n operation: \"search traces\",\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n\n /**\n * What the trace filter fields actually hold in this project. The values the query reference\n * deliberately omits: they are tenant data that moves under the caller, so they live here and the\n * reference names this door instead of inlining a snapshot of it.\n */\n async facets(query?: TraceFacetsQuery): Promise<TraceFacetsResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/traces/facets\", {\n params: { query: query ?? {} },\n });\n if (error) this.handleApiError(\"read trace facets\", error, response);\n return data;\n }\n\n async get(traceId: string, options?: { format?: \"digest\" | \"json\" }): Promise<TraceGetResponse> {\n const { data, error, response } = await this.apiClient.GET(\"/api/v1/traces/{traceId}\", {\n params: {\n path: { traceId },\n query: options,\n },\n });\n return unwrapApiResult({\n operation: `get trace \"${traceId}\"`,\n data,\n error,\n response,\n onError: this.handleApiError.bind(this),\n });\n }\n}\n","/**\n * The service's default headers with the caller's laid over them, whichever\n * `HeadersInit` shape the caller passed.\n */\nexport function mergeHeaders(\n defaults: Record<string, string>,\n overrides: HeadersInit | undefined,\n): Headers {\n const merged = new Headers(defaults);\n new Headers(overrides).forEach((value, key) => merged.set(key, value));\n return merged;\n}\n","import { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { mergeHeaders } from \"@/client-sdk/services/_shared/merge-headers\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { buildRequestHeaders } from \"@/internal/api/request-headers\";\nimport { scopedApiKey } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport interface MonitorResponse {\n id: string;\n name: string;\n slug: string;\n checkType: string;\n enabled: boolean;\n executionMode: string;\n sample: number;\n level: string;\n evaluatorId: string | null;\n preconditions: unknown[];\n parameters: Record<string, unknown>;\n mappings: Record<string, unknown>;\n threadIdleTimeout: number | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateMonitorBody {\n name: string;\n checkType: string;\n executionMode?: string;\n sample?: number;\n level?: string;\n preconditions?: unknown[];\n parameters?: Record<string, unknown>;\n mappings?: Record<string, unknown>;\n}\n\nexport interface UpdateMonitorBody {\n name?: string;\n enabled?: boolean;\n executionMode?: string;\n sample?: number;\n preconditions?: unknown[];\n parameters?: Record<string, unknown>;\n mappings?: Record<string, unknown>;\n}\n\nexport interface MonitorDeleteResponse {\n id: string;\n deleted: boolean;\n}\n\nexport class MonitorsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"MonitorsApiError\";\n }\n}\n\nexport class MonitorsApiService {\n private readonly apiKey: string;\n private readonly endpoint: string;\n\n constructor(config?: { apiKey?: string; endpoint?: string }) {\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n this.endpoint = resolveEndpoint(config?.endpoint);\n }\n\n private async request<T>(path: string, options?: RequestInit): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...options,\n headers: mergeHeaders(\n { ...buildRequestHeaders({ apiKey: this.apiKey }), \"Content-Type\": \"application/json\" },\n options?.headers,\n ),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let parsed: unknown = errorText;\n try {\n parsed = JSON.parse(errorText);\n } catch {\n // leave as raw text\n void 0;\n }\n const message = formatApiErrorMessage({\n error: parsed,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation: options?.method ?? \"GET\",\n error: parsed,\n status: response.status,\n message: `HTTP ${response.status}: ${message}`,\n });\n throw new MonitorsApiError(\n `HTTP ${response.status}: ${message}`,\n options?.method ?? \"GET\",\n parsed,\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n async getAll(): Promise<MonitorResponse[]> {\n return this.request<MonitorResponse[]>(\"/api/v1/monitors\");\n }\n\n async get(id: string): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(`/api/v1/monitors/${encodeURIComponent(id)}`);\n }\n\n async create(body: CreateMonitorBody): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(\"/api/v1/monitors\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n }\n\n async update(id: string, body: UpdateMonitorBody): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(`/api/v1/monitors/${encodeURIComponent(id)}`, {\n method: \"PATCH\",\n body: JSON.stringify(body),\n });\n }\n\n async toggle(id: string, enabled: boolean): Promise<MonitorResponse> {\n return this.request<MonitorResponse>(`/api/v1/monitors/${encodeURIComponent(id)}/toggle`, {\n method: \"POST\",\n body: JSON.stringify({ enabled }),\n });\n }\n\n async delete(id: string): Promise<MonitorDeleteResponse> {\n return this.request<MonitorDeleteResponse>(`/api/v1/monitors/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n });\n }\n}\n","import { formatApiErrorMessage } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { mergeHeaders } from \"@/client-sdk/services/_shared/merge-headers\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { buildRequestHeaders } from \"@/internal/api/request-headers\";\nimport { scopedApiKey, scopedProjectId } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport interface SecretResponse {\n id: string;\n projectId: string;\n name: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SecretDeleteResponse {\n id: string;\n deleted: boolean;\n}\n\nexport class SecretsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SecretsApiError\";\n }\n}\n\nexport class SecretsApiService {\n private readonly apiKey: string;\n private readonly endpoint: string;\n private readonly configuredProjectId: string | undefined;\n\n constructor(config?: { apiKey?: string; endpoint?: string; projectId?: string }) {\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.configuredProjectId = config?.projectId;\n }\n\n private projectId(): string {\n const projectId =\n this.configuredProjectId ?? scopedProjectId() ?? process.env.LANGWATCH_PROJECT_ID;\n if (!projectId) {\n throw new SecretsApiError(\"A projectId is required for secret operations\", \"configuration\");\n }\n return projectId;\n }\n\n private async request<T>(path: string, options?: RequestInit): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...options,\n headers: mergeHeaders(\n {\n ...buildRequestHeaders({ apiKey: this.apiKey, projectId: this.projectId() }),\n \"Content-Type\": \"application/json\",\n },\n options?.headers,\n ),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let parsed: unknown = errorText;\n try {\n parsed = JSON.parse(errorText);\n } catch {\n // leave as raw text\n void 0;\n }\n const message = formatApiErrorMessage({\n error: parsed,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation: options?.method ?? \"GET\",\n error: parsed,\n status: response.status,\n message: `HTTP ${response.status}: ${message}`,\n });\n throw new SecretsApiError(\n `HTTP ${response.status}: ${message}`,\n options?.method ?? \"GET\",\n parsed,\n );\n }\n\n return response.json() as Promise<T>;\n }\n\n async getAll(): Promise<SecretResponse[]> {\n const projectId = this.projectId();\n return this.request<SecretResponse[]>(\n `/api/v1/secrets?projectId=${encodeURIComponent(projectId)}`,\n );\n }\n\n async get(id: string): Promise<SecretResponse> {\n const projectId = this.projectId();\n return this.request<SecretResponse>(\n `/api/v1/secrets/${encodeURIComponent(id)}?projectId=${encodeURIComponent(projectId)}`,\n );\n }\n\n async create(body: { name: string; value: string }): Promise<SecretResponse> {\n return this.request<SecretResponse>(\"/api/v1/secrets\", {\n method: \"POST\",\n body: JSON.stringify({ projectId: this.projectId(), ...body }),\n });\n }\n\n async update(id: string, body: { value: string }): Promise<SecretResponse> {\n return this.request<SecretResponse>(`/api/v1/secrets/${encodeURIComponent(id)}`, {\n method: \"PUT\",\n body: JSON.stringify({ projectId: this.projectId(), ...body }),\n });\n }\n\n async delete(id: string): Promise<SecretDeleteResponse> {\n return this.request<SecretDeleteResponse>(`/api/v1/secrets/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n body: JSON.stringify({ projectId: this.projectId() }),\n });\n }\n}\n","/**\n * The walk that turns a cursor-paginated endpoint back into a whole listing.\n * Only a null `next_cursor` ends it (page size says nothing); served cursors\n * must be replayed verbatim, or the endpoint answers 400 `invalid_cursor`.\n */\n\n/**\n * The page size a walk asks for. The wire caps `limit` at 200 and defaults to\n * 50; a complete walk is four times fewer round trips at the cap.\n */\nexport const CURSOR_WALK_PAGE_SIZE = 200;\n\n/**\n * More pages than any real listing has, and therefore evidence of a cursor\n * chain that never ends. Raising beats stopping here: a silent stop is the\n * truncation the walk exists to prevent.\n */\nexport const MAX_CURSOR_WALK_PAGES = 1000;\n\nexport interface CursorWalkOptions<TPage> {\n /** Reads one page. `undefined` asks for the first. */\n fetchPage: (cursor: string | undefined) => Promise<TPage>;\n /** Absent and null both mean the walk is exhausted. */\n nextCursorOf: (page: TPage) => string | null | undefined;\n /** Resumes an interrupted walk instead of starting from the first row. */\n startCursor?: string;\n /** Raised when the endpoint's cursor chain never reaches an end. */\n onEndlessWalk: (reason: string) => Error;\n}\n\n/**\n * The one walk: yields each page as it arrives so a caller can stop early,\n * and raises rather than looping forever on a cursor chain that never ends.\n * Every `list()`/`iterate()` in the SDK is built on this, written once.\n */\nexport async function* walkCursorPages<TPage>(\n options: CursorWalkOptions<TPage>,\n): AsyncGenerator<TPage> {\n const served = new Set<string>();\n let cursor = options.startCursor;\n let pagesRead = 0;\n\n for (;;) {\n const page = await options.fetchPage(cursor);\n pagesRead += 1;\n yield page;\n\n // A server that predates cursor pagination on this route sends no\n // `next_cursor` at all; absent reads the same as exhausted.\n const next = options.nextCursorOf(page) ?? null;\n if (next === null) return;\n\n // A cursor served twice walks the same page forever.\n if (served.has(next)) {\n throw options.onEndlessWalk(\n \"the endpoint served the same page cursor twice, so the walk cannot advance\",\n );\n }\n if (pagesRead >= MAX_CURSOR_WALK_PAGES) {\n throw options.onEndlessWalk(\n `the walk passed ${MAX_CURSOR_WALK_PAGES} pages without the cursor coming back null`,\n );\n }\n\n served.add(next);\n cursor = next;\n }\n}\n\n/**\n * Reads every page of a cursor-paginated list, in order. Returns the pages\n * rather than the rows so the caller can fold whatever else rides on each\n * page (a `spend_available` flag, say) instead of losing it.\n */\nexport async function collectCursorPages<TPage>(\n options: CursorWalkOptions<TPage>,\n): Promise<TPage[]> {\n const pages: TPage[] = [];\n for await (const page of walkCursorPages(options)) {\n pages.push(page);\n }\n return pages;\n}\n","/**\n * The per-call options every mutating billing call takes. An options-bag\n * call gains these fields there; a body call (`create(input)`) gets them as\n * a trailing param, since body types mirror the wire verbatim.\n */\n\n/** The request header the control plane deduplicates creates on. */\nexport const IDEMPOTENCY_KEY_HEADER = \"Idempotency-Key\";\n\n/**\n * The response header a replayed create carries. Only ever `\"true\"`, and\n * ABSENT rather than false on a first execution, so its presence is the whole\n * signal.\n */\nexport const IDEMPOTENT_REPLAY_HEADER = \"X-Idempotent-Replay\";\n\nexport interface MutationOptions {\n /**\n * Cancel the call. Without one, the request is bounded by the SDK's own\n * 30 second timeout so a hung control plane fails rather than freezes.\n */\n signal?: AbortSignal;\n}\n\nexport interface IdempotentCreateOptions extends MutationOptions {\n /**\n * Makes the create safe to retry: the same key replays the first\n * response verbatim, while a dropped response with no key would mint a\n * second resource. A DIFFERENT body under the same key is refused.\n */\n idempotencyKey?: string;\n /**\n * Called when the response came from a receipt, not a fresh write. A hook\n * rather than a resource field, since callers log this rather than branch\n * on it, keeping wire-shaped entities free of fields the wire lacks.\n */\n onIdempotentReplay?: () => void;\n}\n\n/** What a service's `request()` takes beyond a plain `RequestInit`. */\nexport interface ObservedRequestInit extends RequestInit {\n /**\n * Reads the raw response before its body is parsed. Only headers that the\n * caller asked about are read here; a service never keeps the response.\n */\n onResponse?: (response: Response) => void;\n}\n\n/** Per-call plumbing for a mutating call that is not an idempotent create. */\nexport function mutationInit(options?: MutationOptions): ObservedRequestInit {\n return options?.signal ? { signal: options.signal } : {};\n}\n\n/**\n * Per-call plumbing for a create the server will deduplicate: the key on the\n * way out, and the replay verdict on the way back.\n */\nexport function idempotentCreateInit(options?: IdempotentCreateOptions): ObservedRequestInit {\n const onIdempotentReplay = options?.onIdempotentReplay;\n return {\n ...mutationInit(options),\n ...(options?.idempotencyKey !== undefined\n ? { headers: { [IDEMPOTENCY_KEY_HEADER]: options.idempotencyKey } }\n : {}),\n ...(onIdempotentReplay\n ? {\n onResponse: (response: Response) => {\n if (response.headers.get(IDEMPOTENT_REPLAY_HEADER) === \"true\") {\n onIdempotentReplay();\n }\n },\n }\n : {}),\n };\n}\n","import {\n CURSOR_WALK_PAGE_SIZE,\n collectCursorPages,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport {\n idempotentCreateInit,\n mutationInit,\n type IdempotentCreateOptions,\n type MutationOptions,\n type ObservedRequestInit,\n} from \"@/client-sdk/services/_shared/mutation-options\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { buildSdkIdentityHeaders } from \"@/internal/api/request-headers\";\nimport { requestedProject, scopedApiKey, scopedProjectId } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport type VirtualKeyScopeType = \"organization\" | \"team\" | \"project\";\n\nexport interface VirtualKeyScope {\n scope_type: VirtualKeyScopeType;\n scope_id: string;\n}\n\nexport type VirtualKeyRoutingMode = \"none\" | \"fallback_all\" | \"policy\";\n\n/**\n * The snake DTO the server returns (`toVirtualKeySnakeDto`). The token\n * format is `vk-lw-<ulid>` with no live/test discriminator; the gateway\n * never branches on environment, so there is no env field.\n */\nexport interface VirtualKey {\n id: string;\n organization_id: string;\n name: string;\n description: string | null;\n /** `disabled` is the reversible stop; `revoked` is terminal. */\n status: \"active\" | \"disabled\" | \"revoked\";\n /** \"langy\" marks a product-managed key; customers can only mint \"user\". */\n purpose: \"user\" | \"langy\";\n /** e.g. \"vk-lw-01HZX9\" — the only secret material kept after creation. */\n display_prefix: string;\n principal_user_id: string | null;\n /**\n * Where this key's traces and costs land. Not a scope: it grants no access to the key.\n * Decided when the key is written and stored on it, so editing what the key is scoped to\n * never moves it. Null only on a pre-existing key with no governance project fallback.\n */\n trace_project_id: string | null;\n /**\n * True when the project in `trace_project_id` has been deleted. The key\n * goes on sending its traces there, so the data stays whole and\n * reappears if the project is restored, and traffic is never refused.\n */\n trace_project_archived: boolean;\n scopes: VirtualKeyScope[];\n routing_policy_id: string | null;\n routing_mode: VirtualKeyRoutingMode;\n config: Record<string, unknown>;\n revision: string;\n created_at: string;\n updated_at: string;\n last_used_at: string | null;\n revoked_at: string | null;\n /**\n * When the key stops serving, or null for a key that never expires. Requests presented\n * after this moment are refused with `virtual_key_expired`. `status` stays \"active\" past\n * the date, so read this field to tell an expired key apart, not status.\n */\n expires_at: string | null;\n}\n\n/**\n * The cap a key carries on itself, created atomically with the key. Only\n * the calendar windows a person reasons about; string amounts survive\n * JSON round-trips without float drift (numbers are stringified).\n */\nexport interface VirtualKeyBudgetInput {\n limit_usd: string | number;\n window: \"day\" | \"week\" | \"month\";\n on_breach?: \"block\" | \"warn\";\n name?: string;\n}\n\nexport interface CreateVirtualKeyInput {\n name: string;\n description?: string;\n principal_user_id?: string | null;\n /** Defaults to the caller's project when omitted. */\n scopes?: VirtualKeyScope[];\n /**\n * Explicit trace destination for org- and team-owned keys; requires\n * `virtualKeys:manage` on that project. NOT a scope.\n */\n trace_project_id?: string | null;\n routing_policy_id?: string | null;\n routing_mode?: VirtualKeyRoutingMode;\n /**\n * When the key stops serving, as an ISO 8601 instant. Omit it and the key\n * never expires. A date already in the past is refused.\n */\n expires_at?: string;\n /** Optional cap created atomically with the key. */\n budget?: VirtualKeyBudgetInput | null;\n config?: Record<string, unknown>;\n /**\n * Your own identifier for this key, unique within the organization. Lets\n * you look the key up by the id your system already has instead of storing\n * ours alongside it.\n */\n external_id?: string | null;\n /**\n * Free-form string labels, up to 40 of them. Sent WHOLE on an update: the\n * map you pass replaces the stored one rather than merging into it, and\n * `{}` clears it.\n */\n metadata?: Record<string, string>;\n /**\n * Withhold the secret from the response and get a one-time reveal id instead. The secret is\n * parked for 24 hours and served once, to the person the key is for, through the LangWatch app;\n * the caller never holds it.\n */\n reveal_once?: boolean;\n}\n\nexport interface UpdateVirtualKeyInput {\n name?: string;\n description?: string | null;\n scopes?: VirtualKeyScope[];\n trace_project_id?: string | null;\n routing_policy_id?: string | null;\n routing_mode?: VirtualKeyRoutingMode;\n /**\n * Undefined leaves the expiration alone; null clears it, so the key never\n * expires; an ISO 8601 instant moves it. An expired key accepts this edit\n * like any other, which is how it goes back into service.\n */\n expires_at?: string | null;\n /** Undefined leaves the cap alone; a value upserts it; null archives it. */\n budget?: VirtualKeyBudgetInput | null;\n config?: Record<string, unknown>;\n /**\n * Your own identifier for this key, unique within the organization. Lets\n * you look the key up by the id your system already has instead of storing\n * ours alongside it.\n */\n external_id?: string | null;\n /**\n * Free-form string labels, up to 40 of them. Sent WHOLE on an update: the\n * map you pass replaces the stored one rather than merging into it, and\n * `{}` clears it.\n */\n metadata?: Record<string, string>;\n}\n\nexport interface VirtualKeyWithSecret {\n virtual_key: VirtualKey;\n secret: string;\n}\n\n/** What a create with `reveal_once` answers: the id that reads the secret once. */\nexport interface VirtualKeyWithReveal {\n virtual_key: VirtualKey;\n reveal_id: string;\n /** The display prefix, safe to show in place of the secret. */\n preview: string;\n}\n\n/** One page of the virtual-key listing, exactly as the wire serves it. */\nexport interface VirtualKeyPage {\n data: VirtualKey[];\n /**\n * Pass back as `cursor` for the next page. Null means the walk is exhausted. Neither page\n * length tells you anything here: visibility is applied to each page AFTER it is read, so\n * a page can hold fewer rows than `limit` with more still to come.\n */\n next_cursor: string | null;\n}\n\n/** Aggregate spend for one key over a window, from the cost path. */\nexport interface VirtualKeySpendSummary {\n virtual_key_id: string;\n spent_usd: string;\n requests: number;\n /** Epoch milliseconds, the unit every spend surface takes and returns. */\n window: { from: number; to: number };\n}\n\nexport class VirtualKeysApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"VirtualKeysApiError\";\n }\n}\n\n/**\n * Client for the gateway virtual-key surface (/api/gateway/v1). Entity types and the\n * create/update bodies mirror the wire verbatim (lowercase snake_case); call options this\n * SDK invents (filters, per-call behaviour, action arguments) are camelCase like the rest.\n */\nexport class VirtualKeysApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n /** The project the caller asked for. Sent on every request, where it narrows what is read. */\n private readonly projectId: string | undefined;\n /** Where a key created with no scope lands: the project asked for, else the one resolved. */\n private readonly defaultProjectId: string | undefined;\n\n constructor(config?: { endpoint?: string; apiKey?: string; projectId?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n const resolved = scopedProjectId();\n this.projectId =\n config?.projectId ??\n (requestedProject() === undefined ? undefined : resolved) ??\n process.env.LANGWATCH_PROJECT_ID;\n this.defaultProjectId = this.projectId ?? resolved;\n }\n\n private mergedHeaders(extra: HeadersInit | undefined): Headers {\n const merged = new Headers(this.headers());\n new Headers(extra).forEach((value, key) => merged.set(key, value));\n return merged;\n }\n\n private headers(): Record<string, string> {\n return {\n ...buildSdkIdentityHeaders(),\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n // Only a project the caller asked for (`--project`, then the environment) is named here.\n // Without one, a key that reaches several projects answers for every virtual key it can\n // see, and a project key self-scopes.\n ...(this.projectId ? { \"X-Project-Id\": this.projectId } : {}),\n };\n }\n\n private async request<T>(\n operation: string,\n path: string,\n init?: ObservedRequestInit,\n ): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: this.mergedHeaders(init?.headers),\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new VirtualKeysApiError(message, operation, parsedBody);\n }\n init?.onResponse?.(response);\n return (await response.json()) as T;\n }\n\n /**\n * ONE page of the virtual keys visible to the caller, newest first. Pass `next_cursor`\n * back as `cursor` verbatim — an unrecognized cursor answers 400 rather than restarting.\n * `limit` caps rows READ, not returned. Prefer `list()` unless paging deliberately.\n */\n async listPage(options?: {\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<VirtualKeyPage> {\n const params = new URLSearchParams();\n if (options?.externalId) params.set(\"external_id\", options.externalId);\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const query = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, next_cursor } = await this.request<{\n data: VirtualKey[];\n next_cursor?: string | null;\n }>(\"list virtual keys\", `/api/gateway/v1/virtual-keys${query}`);\n return { data, next_cursor: next_cursor ?? null };\n }\n\n /**\n * Every virtual key visible to the caller (project, team, or org scoped). Follows\n * `next_cursor` until null — a short page does NOT mean done, since the visibility filter\n * runs after each page. `limit` sizes each request, it does not cap the total.\n */\n async list(options?: {\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<VirtualKey[]> {\n const pages = await collectCursorPages<VirtualKeyPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new VirtualKeysApiError(`Failed to list virtual keys: ${reason}.`, \"list virtual keys\"),\n fetchPage: (cursor) =>\n this.listPage({\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n return pages.flatMap((page) => page.data);\n }\n\n /**\n * Every visible virtual key, one row at a time, fetching each page only when the\n * consumer reaches it — stop early and the rest is never read, unlike `list()` which\n * materialises the whole listing first. Raises rather than looping forever, like `list()`.\n */\n async *iterate(options?: {\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): AsyncGenerator<VirtualKey> {\n const pages = walkCursorPages<VirtualKeyPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new VirtualKeysApiError(`Failed to list virtual keys: ${reason}.`, \"list virtual keys\"),\n fetchPage: (cursor) =>\n this.listPage({\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n async get(id: string): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `get virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}`,\n );\n return virtual_key;\n }\n\n /**\n * Mint a key. The response carries the secret ONCE; nothing ever serves it again, so a create\n * that times out is recovered with `idempotencyKey` rather than by listing. With `reveal_once`\n * the response carries a reveal id in place of the secret.\n */\n async create(\n input: CreateVirtualKeyInput & { reveal_once: true },\n options?: IdempotentCreateOptions,\n ): Promise<VirtualKeyWithReveal>;\n async create(\n input: CreateVirtualKeyInput,\n options?: IdempotentCreateOptions,\n ): Promise<VirtualKeyWithSecret>;\n async create(\n input: CreateVirtualKeyInput,\n options?: IdempotentCreateOptions,\n ): Promise<VirtualKeyWithSecret | VirtualKeyWithReveal> {\n const init = idempotentCreateInit(options);\n const headers = new Headers(init.headers);\n // A key with no scope defaults to its caller's project, so that project is named.\n if (input.scopes === undefined && this.defaultProjectId) {\n headers.set(\"X-Project-Id\", this.defaultProjectId);\n }\n return this.request<VirtualKeyWithSecret | VirtualKeyWithReveal>(\n \"create virtual key\",\n \"/api/gateway/v1/virtual-keys\",\n { method: \"POST\", body: JSON.stringify(input), ...init, headers },\n );\n }\n\n async update(\n id: string,\n input: UpdateVirtualKeyInput,\n options?: MutationOptions,\n ): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `update virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input), ...mutationInit(options) },\n );\n return virtual_key;\n }\n\n async rotate(id: string, options?: MutationOptions): Promise<VirtualKeyWithSecret> {\n return this.request<VirtualKeyWithSecret>(\n `rotate virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/rotate`,\n { method: \"POST\", ...mutationInit(options) },\n );\n }\n\n async revoke(id: string, options?: MutationOptions): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `revoke virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/revoke`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return virtual_key;\n }\n\n /** Reversible stop; enable() restores the key exactly as it was. */\n async disable(\n id: string,\n options: { reason?: string } & MutationOptions = {},\n ): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `disable virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/disable`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(options.reason ? { reason: options.reason } : {}),\n ...mutationInit(options),\n },\n );\n return virtual_key;\n }\n\n async enable(id: string, options?: MutationOptions): Promise<VirtualKey> {\n const { virtual_key } = await this.request<{ virtual_key: VirtualKey }>(\n `enable virtual key \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/enable`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return virtual_key;\n }\n\n /**\n * Aggregate spend for one key over a window in epoch milliseconds. Defaults to the\n * current UTC calendar month server-side. Reads the same cost path the dashboard reads,\n * so this number and the UI agree by construction.\n */\n async spend(\n id: string,\n options?: { from?: number; to?: number },\n ): Promise<VirtualKeySpendSummary> {\n const params = new URLSearchParams();\n if (options?.from !== undefined) params.set(\"from\", String(options.from));\n if (options?.to !== undefined) params.set(\"to\", String(options.to));\n const query = params.size > 0 ? `?${params.toString()}` : \"\";\n return this.request<VirtualKeySpendSummary>(\n `read virtual key spend \"${id}\"`,\n `/api/gateway/v1/virtual-keys/${encodeURIComponent(id)}/spend${query}`,\n );\n }\n}\n","import {\n CURSOR_WALK_PAGE_SIZE,\n collectCursorPages,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { mergeHeaders } from \"@/client-sdk/services/_shared/merge-headers\";\nimport {\n idempotentCreateInit,\n mutationInit,\n type IdempotentCreateOptions,\n type MutationOptions,\n type ObservedRequestInit,\n} from \"@/client-sdk/services/_shared/mutation-options\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { buildSdkIdentityHeaders } from \"@/internal/api/request-headers\";\nimport { scopedApiKey } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport type BudgetScopeKind =\n | \"organization\"\n | \"team\"\n | \"project\"\n | \"virtual_key\"\n | \"principal\"\n | \"group\"\n | \"attributed_user\";\n\nexport type BudgetWindow = \"minute\" | \"hour\" | \"day\" | \"week\" | \"month\" | \"total\" | \"manual\";\nexport type BudgetOnBreach = \"block\" | \"warn\";\n\nexport interface GatewayBudget {\n id: string;\n organization_id: string;\n scope_type: BudgetScopeKind;\n scope_id: string;\n name: string;\n description: string | null;\n window: BudgetWindow;\n on_breach: BudgetOnBreach;\n /**\n * For `group` rows, the PER-MEMBER allowance (`member_count` says how many\n * it covers); for `attributed_user` rows, the PER-PERSON cap, with\n * `end_users_seen`/`end_users_over` as the standing instead of `spent_usd`.\n */\n limit_usd: string;\n /** Canonical integer limit, nano-USD. Null past the safe integer range. */\n limit_nano_usd: number | null;\n /**\n * Display value. NULL when `spend_available` is false: spend could not be\n * totalled, so there is no figure, and the API sends null rather than a\n * stale one a caller could mistake for real money.\n */\n spent_usd: string | null;\n /** Canonical integer spend, nano-USD. Null whenever `spent_usd` is. */\n spent_nano_usd: number | null;\n timezone: string | null;\n /** ModelProvider id the budget counts; null counts every provider. */\n provider_key: string | null;\n current_period_started_at: string;\n resets_at: string;\n /** Instant the cycle is phased from; null means calendar aligned. */\n cycle_anchor_at: string | null;\n last_reset_at: string | null;\n archived_at: string | null;\n created_at: string;\n /** `group` rows only. */\n member_count?: number;\n /** `attributed_user` rows only: end users with spend this period. */\n end_users_seen?: number;\n /** `attributed_user` rows only: how many of those are at or over the cap. */\n end_users_over?: number;\n}\n\n/**\n * One page of the budget listing, exactly as the wire serves it. An envelope,\n * not a bare array, because `spend_available` is a correctness flag about the\n * whole page and an array cannot carry it.\n */\nexport interface GatewayBudgetPage {\n data: GatewayBudget[];\n /**\n * False when spend could not be totalled: render \"unavailable\" rather\n * than trusting `spent_usd` as real spend.\n */\n spend_available: boolean;\n /**\n * Pass back as `cursor` for the next page. Null means the walk is\n * exhausted; a FULL page does not by itself mean there is more.\n */\n next_cursor: string | null;\n}\n\nexport type CreateGatewayBudgetScope =\n | { kind: \"organization\"; organization_id: string }\n | { kind: \"team\"; team_id: string }\n | { kind: \"project\"; project_id: string }\n | { kind: \"virtual_key\"; virtual_key_id: string }\n | { kind: \"principal\"; principal_user_id: string }\n | { kind: \"group\"; group_id: string }\n // Template: each distinct external end user on the anchor gets the\n // budget's limit per window. Exactly one anchor id.\n | {\n kind: \"attributed_user\";\n anchor_virtual_key_id?: string;\n anchor_project_id?: string;\n };\n\nexport interface CreateGatewayBudgetInput {\n scope: CreateGatewayBudgetScope;\n name: string;\n description?: string;\n window: BudgetWindow;\n limit_usd: number | string;\n on_breach?: BudgetOnBreach;\n timezone?: string | null;\n /** ModelProvider id to pin the budget to one provider. */\n provider_key?: string | null;\n /**\n * RFC3339 instant phasing the cycle instead of the calendar (a `month`\n * anchored `2026-01-17T09:00:00Z` rolls every 17th at 09:00 UTC). Immutable\n * once created; rejected on windows that never cycle (`total`, `manual`).\n */\n cycle_anchor_at?: string;\n /**\n * Your own identifier for this budget. Lets you look it up by the id your\n * system already has instead of storing ours alongside it.\n */\n external_id?: string | null;\n /**\n * Free-form string labels, up to 40 of them. Sent WHOLE on an update: the\n * map you pass replaces the stored one rather than merging into it, and\n * `{}` clears it.\n */\n metadata?: Record<string, string>;\n}\n\nexport interface UpdateGatewayBudgetInput {\n name?: string;\n description?: string | null;\n limit_usd?: number | string;\n on_breach?: BudgetOnBreach;\n timezone?: string | null;\n}\n\nexport class GatewayBudgetsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"GatewayBudgetsApiError\";\n }\n}\n\n/**\n * Client for the gateway budget surface (/api/gateway/v1). Entity/body fields\n * mirror the wire verbatim (snake_case); options this SDK invents (filters,\n * per-call behaviour, actions) are camelCase like the rest.\n */\nexport class GatewayBudgetsApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly projectId: string | undefined;\n\n constructor(config?: { endpoint?: string; apiKey?: string; projectId?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n this.projectId = config?.projectId ?? process.env.LANGWATCH_PROJECT_ID;\n }\n\n private headers(): Record<string, string> {\n return {\n ...buildSdkIdentityHeaders(),\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n // Org-anchored API keys carry no project of their own; the surface\n // scopes on this header. Absent for project keys, which self-scope.\n ...(this.projectId ? { \"X-Project-Id\": this.projectId } : {}),\n };\n }\n\n private async request<T>(\n operation: string,\n path: string,\n init?: ObservedRequestInit,\n ): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: mergeHeaders(this.headers(), init?.headers),\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new GatewayBudgetsApiError(message, operation, parsedBody);\n }\n init?.onResponse?.(response);\n return (await response.json()) as T;\n }\n\n /**\n * ONE page of non-archived budgets. Pass `next_cursor` back as `cursor`\n * verbatim: an unissued cursor answers 400 rather than restarting the walk.\n * `limit` is the page size (default 50, capped 200) — prefer `list()`.\n */\n async listPage(options?: {\n scopeTypes?: BudgetScopeKind[];\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<GatewayBudgetPage> {\n const params = new URLSearchParams();\n if (options?.externalId) params.set(\"external_id\", options.externalId);\n if (options?.scopeTypes?.length) {\n params.set(\"scope_type\", options.scopeTypes.join(\",\"));\n }\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const query = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, spend_available, next_cursor } = await this.request<{\n data: GatewayBudget[];\n spend_available: boolean;\n next_cursor?: string | null;\n }>(\"list gateway budgets\", `/api/gateway/v1/budgets${query}`);\n return {\n data,\n spend_available,\n next_cursor: next_cursor ?? null,\n };\n }\n\n /**\n * Every non-archived budget across all scope types, following `next_cursor`\n * to completion — callers that count/total need that. Plain array, no\n * cursor; `limit` sizes requests but doesn't cap the result.\n */\n async list(options?: {\n scopeTypes?: BudgetScopeKind[];\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): Promise<GatewayBudget[]> {\n const pages = await collectCursorPages<GatewayBudgetPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new GatewayBudgetsApiError(\n `Failed to list gateway budgets: ${reason}.`,\n \"list gateway budgets\",\n ),\n fetchPage: (cursor) =>\n this.listPage({\n scopeTypes: options?.scopeTypes,\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n return pages.flatMap((page) => page.data);\n }\n\n /**\n * Every non-archived budget, one row at a time, fetching each page only\n * when the consumer reaches it. A null `spent_usd` means spend could not\n * be totalled, not that nothing was spent.\n */\n async *iterate(options?: {\n scopeTypes?: BudgetScopeKind[];\n cursor?: string;\n limit?: number;\n /** Exact match on your own identifier, not a prefix or a search. */\n externalId?: string;\n }): AsyncGenerator<GatewayBudget> {\n const pages = walkCursorPages<GatewayBudgetPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new GatewayBudgetsApiError(\n `Failed to list gateway budgets: ${reason}.`,\n \"list gateway budgets\",\n ),\n fetchPage: (cursor) =>\n this.listPage({\n scopeTypes: options?.scopeTypes,\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n externalId: options?.externalId,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /**\n * One budget by id, in the same row shape the listing serves. Archived\n * budgets are not served, so a budget that existed yesterday can answer\n * 404 today.\n */\n async get(id: string): Promise<GatewayBudget> {\n const { budget } = await this.request<{\n budget: GatewayBudget;\n spend_available: boolean;\n }>(`get gateway budget \"${id}\"`, `/api/gateway/v1/budgets/${encodeURIComponent(id)}`);\n return budget;\n }\n\n async create(\n input: CreateGatewayBudgetInput,\n options?: IdempotentCreateOptions,\n ): Promise<GatewayBudget> {\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n \"create gateway budget\",\n \"/api/gateway/v1/budgets\",\n {\n method: \"POST\",\n body: JSON.stringify(input),\n ...idempotentCreateInit(options),\n },\n );\n return budget;\n }\n\n async update(\n id: string,\n input: UpdateGatewayBudgetInput,\n options?: MutationOptions,\n ): Promise<GatewayBudget> {\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n `update gateway budget \"${id}\"`,\n `/api/gateway/v1/budgets/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input), ...mutationInit(options) },\n );\n return budget;\n }\n\n async archive(id: string, options?: MutationOptions): Promise<GatewayBudget> {\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n `archive gateway budget \"${id}\"`,\n `/api/gateway/v1/budgets/${encodeURIComponent(id)}`,\n { method: \"DELETE\", ...mutationInit(options) },\n );\n return budget;\n }\n\n /**\n * Move the budget's period boundary to now. Recorded spend is never\n * mutated; with `endUserId` only that end-user bucket's boundary moves.\n */\n async reset(\n id: string,\n options: { endUserId?: string; reason?: string } & MutationOptions = {},\n ): Promise<GatewayBudget> {\n const query = options.endUserId ? `?end_user_id=${encodeURIComponent(options.endUserId)}` : \"\";\n const { budget } = await this.request<{ budget: GatewayBudget }>(\n `reset gateway budget \"${id}\"`,\n `/api/gateway/v1/budgets/${encodeURIComponent(id)}/reset${query}`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(options.reason ? { reason: options.reason } : {}),\n ...mutationInit(options),\n },\n );\n return budget;\n }\n}\n","import {\n CURSOR_WALK_PAGE_SIZE,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { mergeHeaders } from \"@/client-sdk/services/_shared/merge-headers\";\nimport { mutationInit, type MutationOptions } from \"@/client-sdk/services/_shared/mutation-options\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { buildSdkIdentityHeaders } from \"@/internal/api/request-headers\";\nimport { scopedApiKey } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\n/**\n * The quantities one priced request or rollup carries; every field is\n * always present (0 when unused) and buckets are disjoint, not nested.\n * `reasoning_tokens` and `image_count` are subsets already priced elsewhere.\n */\nexport interface SpendUsage {\n input_tokens: number;\n output_tokens: number;\n cache_read_input_tokens: number;\n cache_creation_input_tokens: number;\n reasoning_tokens: number;\n input_image_tokens: number;\n output_image_tokens: number;\n image_count: number;\n}\n\n/**\n * Fills image quantities a server older than the release that added them\n * won't send — defaults them to 0 rather than handing a caller `undefined`\n * mid-spend-page.\n */\nfunction spendUsageFromWire(usage: SpendUsage): SpendUsage {\n const wire = usage as Partial<SpendUsage>;\n return {\n ...usage,\n input_image_tokens: wire.input_image_tokens ?? 0,\n output_image_tokens: wire.output_image_tokens ?? 0,\n image_count: wire.image_count ?? 0,\n };\n}\n\nexport interface SpendEvent {\n id: string;\n type: string;\n created: string;\n schema_version: string;\n data: {\n /** Type-suffixed, unique per (request, event type): a settled and a\n * completed event for one request never share an id. */\n event_id: string;\n /** \"gateway.request.completed\" (confirmed and failed outcomes) or\n * \"gateway.request.settled\" (confirmation never arrived). */\n event_type: string;\n /** The join key across a settled/completed pair: a completed event\n * SUPERSEDES an earlier settled one for the same request; replace the\n * figure, never sum the pair. */\n gateway_request_id: string;\n occurred_at: string;\n organization_id: string;\n project_id: string;\n virtual_key_id: string;\n principal_user_id: string | null;\n end_user_id: string | null;\n trace_id: string;\n model: string | null;\n model_provider_id: string | null;\n request_type: string | null;\n /** Null on settled events: unknown is not zero. */\n usage: SpendUsage | null;\n /** Null on settled events: unknown is not zero. */\n cost: {\n total_usd: string;\n nano_usd: number;\n rate_version: string | null;\n } | null;\n /** \"success\" | \"error\" on completed events, \"settled\" on settled ones. */\n status: string;\n needs_reconciliation: boolean | null;\n settle_reason: string | null;\n error: { class: string; http_status: number | null } | null;\n duration_ms: number | null;\n labels: string[];\n metadata: Record<string, unknown>;\n };\n}\n\nexport interface SpendSummaryRow {\n /**\n * The FIRST grouping dimension's value. Unchanged from when a rollup could\n * only be grouped one way, so existing code keeps reading what it did. With\n * two dimensions, two rows can share a key: read `group` to tell them apart.\n */\n key: string;\n /** Every grouping dimension by name, e.g. `{ model: \"gpt-6-luna\" }`. */\n group: Record<string, string>;\n /** Start of the time bucket in the requested zone, null when unbucketed. */\n bucket_start: string | null;\n /** Priced outcomes (confirmed and failed). */\n event_count: number;\n /** Unpriced settled requests, counted separately: never in cost sums. */\n settled_count: number;\n usage: SpendUsage;\n cost: { total_usd: string; nano_usd: number };\n}\n\n/** Request states: admitted at start, confirmed/failed/settled at end. */\nexport type SpendEventStatus =\n | \"success\"\n | \"error\"\n | \"admitted\"\n | \"confirmed\"\n | \"failed\"\n | \"settled\";\n\n/** Rollup states: excludes admitted (no cost yet). Use events for those. */\nexport type SpendSummaryStatus = Exclude<SpendEventStatus, \"admitted\">;\n\n/** A dimension a rollup can be grouped by. */\nexport type SpendGroupBy =\n | \"virtual_key\"\n | \"end_user\"\n | \"project\"\n | \"model\"\n | \"provider\"\n | \"principal\"\n | \"request_type\";\n\n/**\n * Shared filters for spend reads. Every field takes one value or many.\n * Multiple values widen; multiple fields narrow.\n */\nexport interface SpendFilterOptions {\n projectId?: string | string[];\n /** Resolved to the projects the team owns. A team with none matches nothing. */\n teamId?: string | string[];\n /** Your own id for a virtual key. One nobody minted matches nothing. */\n externalId?: string | string[];\n virtualKeyId?: string | string[];\n endUserId?: string | string[];\n principalUserId?: string | string[];\n model?: string | string[];\n providerKey?: string | string[];\n requestType?: string | string[];\n label?: string | string[];\n /**\n * Your own request metadata, e.g. `{ customer_tier: \"gold\" }`. Several\n * values for one key widen it; several keys narrow.\n */\n metadata?: Record<string, string | string[]>;\n status?: SpendEventStatus;\n}\n\nconst FILTER_PARAMS: readonly [keyof SpendFilterOptions, string][] = [\n [\"projectId\", \"project_id\"],\n [\"teamId\", \"team_id\"],\n [\"externalId\", \"external_id\"],\n [\"virtualKeyId\", \"virtual_key_id\"],\n [\"endUserId\", \"end_user_id\"],\n [\"principalUserId\", \"principal_user_id\"],\n [\"model\", \"model\"],\n [\"providerKey\", \"provider_key\"],\n [\"requestType\", \"request_type\"],\n [\"label\", \"label\"],\n];\n\n/** Repeat the parameter once per value: that is how the API widens a filter. */\nfunction appendSpendFilters({\n params,\n filters,\n}: {\n params: URLSearchParams;\n filters: SpendFilterOptions;\n}): void {\n for (const [field, name] of FILTER_PARAMS) {\n const value = filters[field] as string | string[] | undefined;\n if (value === undefined) continue;\n for (const one of Array.isArray(value) ? value : [value]) {\n params.append(name, one);\n }\n }\n for (const [key, value] of Object.entries(filters.metadata ?? {})) {\n // The API splits a pair on its FIRST colon, so a key carrying one would\n // silently address a different key and report spend for a filter nobody\n // wrote. Refused here rather than sent and misread.\n if (key.includes(\":\")) {\n throw new SpendEventsApiError(\n `A metadata key cannot contain a colon: ${key}`,\n \"build spend filters\",\n );\n }\n for (const one of Array.isArray(value) ? value : [value]) {\n params.append(\"metadata\", `${key}:${one}`);\n }\n }\n if (filters.status) params.set(\"status\", filters.status);\n}\n\n/**\n * Grouping by model/provider/time is unstable: outcomes may arrive and change\n * the recorded values. Use allowUnstable for approximate live views; reconcile\n * closed periods to avoid this.\n */\nexport interface SpendSummariesOptions extends Omit<SpendFilterOptions, \"status\"> {\n /** Lifecycle status; excludes admitted (no cost yet). */\n status?: SpendSummaryStatus;\n /** One or two dimensions. Two rows can share `key`; read `group`. */\n groupBy: SpendGroupBy | SpendGroupBy[];\n from: number;\n to: number;\n /** Adds a time column. Counts as movable, so the same refusal applies. */\n bucket?: \"none\" | \"hour\" | \"day\";\n /** IANA zone the bucket boundary falls on, e.g. \"Europe/Amsterdam\". */\n timezone?: string;\n /** Serve a movable grouping anyway, accepting an inexact walk. */\n allowUnstable?: boolean;\n}\n\nexport interface SpendSummariesPage {\n data: SpendSummaryRow[];\n /**\n * Pass back as `cursor` for the next page; null means the walk is done.\n * A full page does NOT mean there is more, so follow this until null\n * rather than stopping when a page comes back short.\n */\n next_cursor: string | null;\n}\n\nexport interface SpendEventsPage {\n data: SpendEvent[];\n next_cursor: string | null;\n}\n\nexport interface SpendReplayResult {\n endpoint_id: string;\n replay_id: string;\n replayed: number;\n window: { from: string; to: string };\n}\n\nexport interface EndUserSpend {\n end_user_id: string;\n window: string;\n from: string;\n to: string;\n cost: { total_usd: string; nano_usd?: number };\n request_count: number;\n usage: SpendUsage;\n /**\n * The attributed-user template caps that apply to this end user, each\n * with its boundary-aware current-period spend. Empty when the\n * organization runs no templates; never null.\n */\n caps: EndUserCap[];\n}\n\nexport interface EndUserCap {\n budget_id: string;\n anchor_id: string;\n window: string;\n on_breach: \"block\" | \"warn\";\n limit_usd: string;\n spent_usd: string;\n period_started_at: string;\n}\n\nexport class SpendEventsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"SpendEventsApiError\";\n }\n}\n\n/**\n * Spend reconciliation service (/api/gateway/v1). Requires ORGANIZATION API\n * key (sk-lw-*). Use iterate/iterSummaries for large windows (no eager read).\n */\nexport class SpendEventsApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n }\n\n private async request<T>(operation: string, path: string, init?: RequestInit): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: mergeHeaders(\n {\n ...buildSdkIdentityHeaders(),\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n init?.headers,\n ),\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new SpendEventsApiError(message, operation, parsedBody);\n }\n return (await response.json()) as T;\n }\n\n /** One page of events. Follow next_cursor or use iterate() for the full window. */\n async listPage(\n options: SpendFilterOptions & {\n /** Required: the pull is a ranged read by contract. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n },\n ): Promise<SpendEventsPage> {\n const params = new URLSearchParams();\n params.set(\"from\", String(options.from));\n params.set(\"to\", String(options.to));\n if (options.cursor) params.set(\"cursor\", options.cursor);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n appendSpendFilters({ params, filters: options });\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const page = await this.request<SpendEventsPage>(\n \"list spend events\",\n `/api/gateway/v1/spend-events${qs}`,\n );\n return {\n ...page,\n data: page.data.map((event) => ({\n ...event,\n data: {\n ...event.data,\n usage: event.data.usage === null ? null : spendUsageFromWire(event.data.usage),\n },\n })),\n };\n }\n\n /** Stream events without holding the whole window in memory. */\n async *iterate(\n options: SpendFilterOptions & {\n /** Required: the pull is a ranged read by contract. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n },\n ): AsyncGenerator<SpendEvent> {\n const pages = walkCursorPages<SpendEventsPage>({\n startCursor: options.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new SpendEventsApiError(`Failed to list spend events: ${reason}.`, \"list spend events\"),\n fetchPage: (cursor) =>\n this.listPage({\n ...options,\n cursor,\n limit: options.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /** One page of rollups by group key. Use iterSummaries for the full walk. */\n async summariesPage(\n options: SpendSummariesOptions & { cursor?: string; limit?: number },\n ): Promise<SpendSummariesPage> {\n const params = new URLSearchParams();\n const groupBy = Array.isArray(options.groupBy) ? options.groupBy : [options.groupBy];\n params.set(\"group_by\", groupBy.join(\",\"));\n params.set(\"from\", String(options.from));\n params.set(\"to\", String(options.to));\n if (options.bucket) params.set(\"bucket\", options.bucket);\n if (options.timezone) params.set(\"timezone\", options.timezone);\n if (options.allowUnstable) params.set(\"allow_unstable\", \"true\");\n appendSpendFilters({ params, filters: options });\n if (options.cursor) params.set(\"cursor\", options.cursor);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n const page = await this.request<SpendSummariesPage>(\n \"read spend summaries\",\n `/api/gateway/v1/spend-summaries?${params.toString()}`,\n );\n return {\n ...page,\n data: page.data.map((row) => ({\n ...row,\n usage: spendUsageFromWire(row.usage),\n })),\n };\n }\n\n /** Stream rollup rows without holding the whole window in memory. */\n async *iterSummaries(\n options: SpendSummariesOptions & { cursor?: string; limit?: number },\n ): AsyncGenerator<SpendSummaryRow> {\n const pages = walkCursorPages<SpendSummariesPage>({\n startCursor: options.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new SpendEventsApiError(\n `Failed to read spend summaries: ${reason}.`,\n \"read spend summaries\",\n ),\n fetchPage: (cursor) =>\n this.summariesPage({\n ...options,\n cursor,\n limit: options.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /** Re-deliver spend envelopes to an endpoint (max 7 days). */\n async replay(\n options: {\n from: number;\n to: number;\n endpointId: string;\n } & MutationOptions,\n ): Promise<SpendReplayResult> {\n const response = await this.request<{ data: SpendReplayResult }>(\n \"replay spend events\",\n \"/api/gateway/v1/spend-events/replay\",\n {\n method: \"POST\",\n body: JSON.stringify({\n from: options.from,\n to: options.to,\n endpoint_id: options.endpointId,\n }),\n ...mutationInit(options),\n },\n );\n return response.data;\n }\n\n async endUserSpend(\n endUserId: string,\n options?: {\n window?: \"day\" | \"week\" | \"month\";\n from?: number;\n to?: number;\n virtualKeyId?: string;\n },\n ): Promise<EndUserSpend> {\n const params = new URLSearchParams();\n if (options?.window) params.set(\"window\", options.window);\n if (options?.from !== undefined) params.set(\"from\", String(options.from));\n if (options?.to !== undefined) params.set(\"to\", String(options.to));\n if (options?.virtualKeyId) params.set(\"virtual_key_id\", options.virtualKeyId);\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const res = await this.request<{ data: EndUserSpend }>(\n \"read end-user spend\",\n `/api/gateway/v1/end-users/${encodeURIComponent(endUserId)}/spend${qs}`,\n );\n return { ...res.data, usage: spendUsageFromWire(res.data.usage) };\n }\n}\n","import {\n CURSOR_WALK_PAGE_SIZE,\n walkCursorPages,\n} from \"@/client-sdk/services/_shared/collect-cursor-pages\";\nimport { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { mergeHeaders } from \"@/client-sdk/services/_shared/merge-headers\";\nimport {\n idempotentCreateInit,\n mutationInit,\n type IdempotentCreateOptions,\n type MutationOptions,\n type ObservedRequestInit,\n} from \"@/client-sdk/services/_shared/mutation-options\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { buildSdkIdentityHeaders } from \"@/internal/api/request-headers\";\nimport { scopedApiKey } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\n/** Where an endpoint delivers. */\nexport type WebhookDestinationKind = \"http\" | \"sqs\";\n\n/** The queue an `sqs` endpoint delivers to, as any read surface sees it: the\n * secret half of a static key pair is never returned. */\nexport interface WebhookSqsDestination {\n queue_url: string;\n /** Read off the queue URL, never configured beside it. */\n region: string;\n /** Whose queue it is. */\n account_id: string;\n queue_name: string;\n credential_mode: \"assume_role\" | \"static\" | \"ambient\";\n role_arn: string | null;\n /** Generated at save time, to paste into the role's trust policy. */\n external_id: string | null;\n access_key_id: string | null;\n}\n\n/** The queue half of a create or update body. */\nexport interface WebhookSqsDestinationInput {\n queue_url: string;\n /** The role to assume, with an external id we generate. The recommended\n * way to grant access: nothing long-lived is stored, and the customer\n * revokes by editing their own trust policy. */\n role_arn?: string;\n external_id?: string;\n /** A static key pair instead. The secret is stored encrypted and never\n * returned. */\n access_key_id?: string;\n secret_access_key?: string;\n}\n\nexport interface WebhookEndpointSummary {\n id: string;\n destination_kind: WebhookDestinationKind;\n /** The receiver URL on an `http` endpoint, null on every other kind. */\n url: string | null;\n /** The queue on an `sqs` endpoint, null on every other kind. */\n sqs: WebhookSqsDestination | null;\n max_batch_size: number;\n max_batch_delay_ms: number;\n max_in_flight: number;\n enabled_events: string[];\n status: \"active\" | \"disabled\";\n disabled_reason: string | null;\n disabled_at: string | null;\n failing_since: string | null;\n last_success_at: string | null;\n last_failure_at: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface WebhookEndpointWithSecret extends WebhookEndpointSummary {\n /** Present only on create and roll-secret responses; never again. */\n secret: string;\n}\n\n/** Everything a create body carries that is not the destination. */\ninterface CreateWebhookEndpointBase {\n enabled_events: string[];\n /** Envelopes per delivery. The receiver always gets an array. */\n max_batch_size?: number;\n /** How long a partial batch waits for company before it is sent. */\n max_batch_delay_ms?: number;\n /** Concurrent in-flight deliveries to this endpoint. */\n max_in_flight?: number;\n}\n\n/**\n * The POST body, exactly as the wire takes it. A destination is one kind and\n * one address, so the two are a union, matching what the server refuses too.\n * `destination_kind` is optional only on the http branch (absent means http).\n */\nexport type WebhookDestinationInput =\n | { destination_kind?: \"http\"; url: string; sqs?: never }\n | { destination_kind: \"sqs\"; sqs: WebhookSqsDestinationInput; url?: never };\n\nexport type CreateWebhookEndpointInput = CreateWebhookEndpointBase & WebhookDestinationInput;\n\n/** The PATCH body, exactly as the wire takes it. Omitted fields are left alone. */\nexport interface UpdateWebhookEndpointInput {\n url?: string;\n /** Only the queue's own fields; the destination kind cannot change, because\n * batches already planned against the old transport are in flight. Create\n * a new endpoint and archive this one once it has drained. A credential\n * field sent as null is CLEARED; omitted keeps what is stored. */\n sqs?: Partial<{\n queue_url: string;\n role_arn: string | null;\n external_id: string | null;\n access_key_id: string | null;\n secret_access_key: string | null;\n }>;\n enabled_events?: string[];\n status?: \"active\" | \"disabled\";\n max_batch_size?: number;\n max_batch_delay_ms?: number;\n max_in_flight?: number;\n}\n\nexport interface WebhookDeliveryRecord {\n id: string;\n dispatch_id: string;\n attempt: number;\n event_count: number;\n outcome: string;\n response_status: number | null;\n latency_ms: number | null;\n error: string | null;\n fired_at: string;\n}\n\nexport interface WebhookTestResult {\n delivered: boolean;\n response_status: number | null;\n response_body?: string;\n error?: string;\n}\n\nexport interface WebhookEndpointHealth {\n status: \"active\" | \"disabled\";\n disabled_reason: string | null;\n failing_since: string | null;\n last_success_at: string | null;\n last_failure_at: string | null;\n /** The headline: age of the oldest envelope still buffered or retrying;\n * null when the feed is fully delivered. */\n oldest_undelivered_age_ms: number | null;\n dlq_depth: number;\n sends_per_minute: number;\n success_rate: number | null;\n p95_latency_ms: number | null;\n}\n\nexport interface WebhookEventType {\n type: string;\n family: string;\n schema_version: string;\n is_emitting: boolean;\n description: string;\n}\n\nexport interface EmittedEvent {\n id: string;\n type: string;\n created: string;\n schema_version: string;\n data: Record<string, unknown>;\n}\n\n/** One page of the organization's emitted-events log. */\nexport interface EmittedEventsPage {\n data: EmittedEvent[];\n /** Pass back as `cursor` for the next page; null ends the walk. */\n next_cursor: string | null;\n}\n\n/** One page of an endpoint's delivery log, newest first. */\nexport interface WebhookDeliveryPage {\n data: WebhookDeliveryRecord[];\n /** Pass back as `cursor` for the next page; null ends the walk. */\n next_cursor: string | null;\n}\n\nexport class WebhooksApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"WebhooksApiError\";\n }\n}\n\n/**\n * Client for the org-anchored webhook platform surface (/api/webhooks/v1) — an ORGANIZATION\n * API key only; a project key is refused with `credential_class_mismatch`. Fields mirror the\n * wire verbatim in snake_case, since translating field-by-field made billing surfaces disagree.\n */\nexport class WebhooksApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n }\n\n private async request<T>(\n operation: string,\n path: string,\n init?: ObservedRequestInit,\n ): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n // A hung control plane must fail the command, not freeze it.\n signal: init?.signal ?? AbortSignal.timeout(30_000),\n headers: mergeHeaders(\n {\n ...buildSdkIdentityHeaders(),\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n init?.headers,\n ),\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new WebhooksApiError(message, operation, parsedBody);\n }\n init?.onResponse?.(response);\n return (await response.json()) as T;\n }\n\n async list(): Promise<WebhookEndpointSummary[]> {\n const res = await this.request<{ data: WebhookEndpointSummary[] }>(\n \"list webhook endpoints\",\n \"/api/webhooks/v1/endpoints\",\n );\n return res.data;\n }\n\n async get(id: string): Promise<WebhookEndpointSummary> {\n const res = await this.request<{ data: WebhookEndpointSummary }>(\n \"get webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}`,\n );\n return res.data;\n }\n\n /**\n * The signing secret comes back on this response and never again, so a\n * create that times out is recovered with `idempotencyKey`: the replay\n * carries the same secret, and nothing else ever will.\n */\n async create(\n input: CreateWebhookEndpointInput,\n options?: IdempotentCreateOptions,\n ): Promise<WebhookEndpointWithSecret> {\n const res = await this.request<{ data: WebhookEndpointWithSecret }>(\n \"create webhook endpoint\",\n \"/api/webhooks/v1/endpoints\",\n {\n method: \"POST\",\n body: JSON.stringify(input),\n ...idempotentCreateInit(options),\n },\n );\n return res.data;\n }\n\n async update(\n id: string,\n input: UpdateWebhookEndpointInput,\n options?: MutationOptions,\n ): Promise<WebhookEndpointSummary> {\n const res = await this.request<{ data: WebhookEndpointSummary }>(\n \"update webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input), ...mutationInit(options) },\n );\n return res.data;\n }\n\n /**\n * Retire an endpoint: the server soft-archives the row (stamping `archived_at`, disabling\n * delivery) so history stays readable for audit. Archived, not removed — same verb as\n * `gatewayBudgets.archive()`, so the billing surfaces agree.\n */\n async archive(id: string, options?: MutationOptions): Promise<void> {\n await this.request<unknown>(\n \"archive webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}`,\n { method: \"DELETE\", ...mutationInit(options) },\n );\n }\n\n async rollSecret(id: string, options?: MutationOptions): Promise<WebhookEndpointWithSecret> {\n const res = await this.request<{ data: WebhookEndpointWithSecret }>(\n \"roll webhook endpoint secret\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/roll-secret`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return res.data;\n }\n\n async test(id: string, options?: MutationOptions): Promise<WebhookTestResult> {\n const res = await this.request<{ data: WebhookTestResult }>(\n \"test webhook endpoint\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/test`,\n { method: \"POST\", ...mutationInit(options) },\n );\n return res.data;\n }\n\n /**\n * ONE page of the endpoint's delivery attempts, newest first. Pass\n * `next_cursor` back as `cursor` for the next page, or walk the whole log\n * with `iterDeliveries()`.\n */\n async deliveriesPage(\n id: string,\n options?: { cursor?: string; limit?: number },\n ): Promise<WebhookDeliveryPage> {\n const params = new URLSearchParams();\n if (options?.cursor) params.set(\"cursor\", options.cursor);\n if (options?.limit !== undefined) params.set(\"limit\", String(options.limit));\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, next_cursor } = await this.request<{\n data: WebhookDeliveryRecord[];\n next_cursor?: string | null;\n }>(\n \"list webhook deliveries\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/deliveries${qs}`,\n );\n return { data, next_cursor: next_cursor ?? null };\n }\n\n /**\n * Every recorded delivery attempt for the endpoint, one at a time,\n * fetching each page only when the consumer reaches it.\n */\n async *iterDeliveries(\n id: string,\n options?: { cursor?: string; limit?: number },\n ): AsyncGenerator<WebhookDeliveryRecord> {\n const pages = walkCursorPages<WebhookDeliveryPage>({\n startCursor: options?.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new WebhooksApiError(\n `Failed to list webhook deliveries: ${reason}.`,\n \"list webhook deliveries\",\n ),\n fetchPage: (cursor) =>\n this.deliveriesPage(id, {\n cursor,\n limit: options?.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n async health(id: string): Promise<WebhookEndpointHealth> {\n const res = await this.request<{ data: WebhookEndpointHealth }>(\n \"read webhook endpoint health\",\n `/api/webhooks/v1/endpoints/${encodeURIComponent(id)}/health`,\n );\n return res.data;\n }\n\n async eventTypes(): Promise<WebhookEventType[]> {\n const res = await this.request<{ data: WebhookEventType[] }>(\n \"list webhook event types\",\n \"/api/webhooks/v1/event-types\",\n );\n return res.data;\n }\n\n /**\n * ONE page of the organization's emitted-events log, newest first.\n * Webhooks are a push over this log, never the only copy: a consumer that\n * missed a delivery reads the window back from here.\n */\n async eventsPage(options: {\n type?: string;\n /** Required: the log is a ranged read by contract. Epoch milliseconds. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n }): Promise<EmittedEventsPage> {\n const params = new URLSearchParams();\n if (options.type) params.set(\"type\", options.type);\n params.set(\"from\", String(options.from));\n params.set(\"to\", String(options.to));\n if (options.cursor) params.set(\"cursor\", options.cursor);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n const qs = params.toString() !== \"\" ? `?${params.toString()}` : \"\";\n const { data, next_cursor } = await this.request<{\n data: EmittedEvent[];\n next_cursor?: string | null;\n }>(\"list emitted events\", `/api/webhooks/v1/events${qs}`);\n return { data, next_cursor: next_cursor ?? null };\n }\n\n /**\n * Every emitted event matching the filter, one at a time, fetching each\n * page only when the consumer reaches it.\n */\n async *iterEvents(options: {\n type?: string;\n /** Required: the log is a ranged read by contract. Epoch milliseconds. */\n from: number;\n to: number;\n cursor?: string;\n limit?: number;\n }): AsyncGenerator<EmittedEvent> {\n const pages = walkCursorPages<EmittedEventsPage>({\n startCursor: options.cursor,\n nextCursorOf: (page) => page.next_cursor,\n onEndlessWalk: (reason) =>\n new WebhooksApiError(`Failed to list emitted events: ${reason}.`, \"list emitted events\"),\n fetchPage: (cursor) =>\n this.eventsPage({\n ...options,\n cursor,\n limit: options.limit ?? CURSOR_WALK_PAGE_SIZE,\n }),\n });\n for await (const page of pages) {\n yield* page.data;\n }\n }\n\n /**\n * One emitted event by id, the envelope exactly as it was delivered. A 404\n * covers every reason the log cannot answer: never emitted, past\n * retention, or belonging to another organization.\n */\n async getEvent(id: string): Promise<EmittedEvent> {\n const res = await this.request<{ data: EmittedEvent }>(\n \"get emitted event\",\n `/api/webhooks/v1/events/${encodeURIComponent(id)}`,\n );\n return res.data;\n }\n}\n","/**\n * The raw-fetch request path the management API services share.\n */\nimport { buildSdkIdentityHeaders } from \"@/internal/api/request-headers\";\nimport { scopedApiKey } from \"@/internal/credentialContext\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nimport { formatApiErrorForOperation } from \"./format-api-error.ts\";\nimport { throwIfHandledError } from \"./throw-handled-error.ts\";\n\n/** Builds the family's own error for a failure the platform did not name. */\nexport type ManagementErrorFactory = (params: {\n message: string;\n operation: string;\n body: unknown;\n}) => Error;\n\n/**\n * How long one management call may hang before it is abandoned. A control\n * plane that accepts the socket and never answers would otherwise wedge the\n * command forever, with a spinner and no way out.\n */\nexport const MANAGEMENT_REQUEST_TIMEOUT_MS = 30_000;\n\n/**\n * The organization credential every management family except the instance-provisioning one\n * runs with, resolved once rather than in each constructor. An empty token is refused here\n * so the caller reads what is missing instead of a 401 from the platform.\n */\nexport const resolveManagementToken = ({ apiKey }: { apiKey?: string }): string => {\n const token = apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY;\n if (!token) {\n throw new Error(\n \"No API key configured. Set LANGWATCH_API_KEY, run `langwatch login`, or pass apiKey.\",\n );\n }\n return token;\n};\n\nexport interface ManagementRequestConfig {\n endpoint: string;\n /** The bearer credential; an organization key, or the instance-admin key. */\n token: string;\n /** Raised when the response carries no named platform failure. */\n errorFactory: ManagementErrorFactory;\n}\n\nexport interface ManagementRequestParams {\n /** What was being attempted, e.g. `list custom roles`. */\n operation: string;\n /** Path from the endpoint root, e.g. `/api/v1/roles`. */\n path: string;\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n /** JSON request body; omitted for reads. */\n body?: unknown;\n /** Query parameters. Undefined values are left off the URL entirely. */\n query?: Record<string, string | number | boolean | undefined>;\n /** Extra request headers, e.g. `Idempotency-Key`. */\n headers?: Record<string, string>;\n /** Caller's own deadline; without one the default timeout applies. */\n signal?: AbortSignal;\n}\n\n/** `?a=1&b=2` for the values that are actually set, or \"\" when none are. */\nexport const buildQueryString = (\n query: Record<string, string | number | boolean | undefined> | undefined,\n): string => {\n if (!query) return \"\";\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n params.set(key, String(value));\n }\n const rendered = params.toString();\n return rendered ? `?${rendered}` : \"\";\n};\n\n/**\n * A bound request function for one service: same semantics as the api-keys\n * service's private `request`, with the family's error class supplied once.\n */\nexport const createManagementRequest = ({\n endpoint,\n token,\n errorFactory,\n}: ManagementRequestConfig) => {\n return async <T>({\n operation,\n path,\n method,\n body,\n query,\n headers,\n signal,\n }: ManagementRequestParams): Promise<T> => {\n const response = await langwatchFetch(`${endpoint}${path}${buildQueryString(query)}`, {\n ...(method ? { method } : {}),\n headers: {\n ...buildSdkIdentityHeaders(),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n signal: signal ?? AbortSignal.timeout(MANAGEMENT_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n // One read: a body consumed by a failed `json()` cannot be read again,\n // and the second read would throw past the error handling below.\n const rawBody = await response.text();\n let parsedBody: unknown;\n try {\n parsedBody = JSON.parse(rawBody);\n } catch {\n parsedBody = rawBody;\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw errorFactory({ message, operation, body: parsedBody });\n }\n\n // A 204 carries no body, and `json()` on an empty one throws a bare\n // SyntaxError that would escape as a parse failure rather than the success\n // it is. A family that answers 204 to a delete gets an undefined result.\n if (response.status === 204) return undefined as T;\n\n return (await response.json()) as T;\n };\n};\n\nexport type ManagementRequest = ReturnType<typeof createManagementRequest>;\n\n/**\n * The version namespace every management call addresses, spelled into the path, under the\n * canonical `/api/v1` prefix (packages/api/adrs/002 §1): the framework families serve\n * dated namespaces and `latest`, and the SDK tracks `latest`.\n */\nexport const managementPath = (path: string): string =>\n path.replace(\n /^(\\/api\\/v1\\/[^/]+)(\\/.*)?$/,\n (_match, base: string, rest?: string) => `${base}/latest${rest ?? \"\"}`,\n );\n","/**\n * The `/api/v1/teams` REST family, which shipped without a CLI.\n */\nimport { resolveEndpoint } from \"@/internal/endpoint\";\n\nimport {\n createManagementRequest,\n resolveManagementToken,\n type ManagementRequest,\n} from \"../_shared/management-request\";\nimport type { ManagementRole } from \"../_shared/management-types\";\n\nexport interface Team {\n id: string;\n name: string;\n slug: string;\n organizationId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface TeamPagination {\n page: number;\n limit: number;\n total: number;\n}\n\nexport interface ListTeamsResponse {\n data: Team[];\n pagination: TeamPagination;\n}\n\nexport interface ArchivedTeam {\n id: string;\n name: string;\n archivedAt: string | null;\n}\n\nexport interface TeamMember {\n userId: string | null;\n name: string | null;\n email: string | null;\n role: ManagementRole;\n}\n\nexport class TeamsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n ) {\n super(message);\n this.name = \"TeamsApiError\";\n }\n}\n\nexport class TeamsApiService {\n readonly #request: ManagementRequest;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.#request = createManagementRequest({\n endpoint: resolveEndpoint(config?.endpoint),\n token: resolveManagementToken({ apiKey: config?.apiKey }),\n errorFactory: ({ message, operation, body }) => new TeamsApiError(message, operation, body),\n });\n }\n\n async list(options: { page?: number; limit?: number } = {}): Promise<ListTeamsResponse> {\n return this.#request({\n operation: \"list teams\",\n path: \"/api/v1/teams\",\n query: { ...options },\n });\n }\n\n async get(id: string): Promise<Team> {\n return this.#request({\n operation: `fetch team \"${id}\"`,\n path: `/api/v1/teams/${encodeURIComponent(id)}`,\n });\n }\n\n async create(input: { name: string }): Promise<Team> {\n return this.#request({\n operation: \"create team\",\n path: \"/api/v1/teams\",\n method: \"POST\",\n body: input,\n });\n }\n\n async update({ id, input }: { id: string; input: { name?: string } }): Promise<Team> {\n return this.#request({\n operation: `update team \"${id}\"`,\n path: `/api/v1/teams/${encodeURIComponent(id)}`,\n method: \"PATCH\",\n body: input,\n });\n }\n\n async archive(id: string): Promise<ArchivedTeam> {\n return this.#request({\n operation: `archive team \"${id}\"`,\n path: `/api/v1/teams/${encodeURIComponent(id)}`,\n method: \"DELETE\",\n });\n }\n\n async listMembers(teamId: string): Promise<{ data: TeamMember[] }> {\n return this.#request({\n operation: `list members of team \"${teamId}\"`,\n path: `/api/v1/teams/${encodeURIComponent(teamId)}/members`,\n });\n }\n\n async addMember({\n teamId,\n input,\n }: {\n teamId: string;\n input: { userId: string; role?: ManagementRole };\n }): Promise<{ success: boolean }> {\n return this.#request({\n operation: `add a member to team \"${teamId}\"`,\n path: `/api/v1/teams/${encodeURIComponent(teamId)}/members`,\n method: \"POST\",\n body: input,\n });\n }\n\n async removeMember({\n teamId,\n userId,\n }: {\n teamId: string;\n userId: string;\n }): Promise<{ success: boolean }> {\n return this.#request({\n operation: `remove member \"${userId}\" from team \"${teamId}\"`,\n path: `/api/v1/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`,\n method: \"DELETE\",\n });\n }\n}\n","import { formatApiErrorForOperation } from \"@/client-sdk/services/_shared/format-api-error\";\nimport { mergeHeaders } from \"@/client-sdk/services/_shared/merge-headers\";\nimport { throwIfHandledError } from \"@/client-sdk/services/_shared/throw-handled-error\";\nimport { buildSdkIdentityHeaders } from \"@/internal/api/request-headers\";\nimport { scopedApiKey } from \"@/internal/credentialContext\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\nimport { langwatchFetch } from \"@/internal/http/langwatchFetch\";\n\nexport interface Project {\n id: string;\n name: string;\n slug: string;\n language: string;\n framework: string;\n teamId: string;\n piiRedactionLevel: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateProjectInput {\n name: string;\n language: string;\n framework: string;\n teamId?: string;\n newTeamName?: string;\n}\n\nexport interface UpdateProjectInput {\n name?: string;\n language?: string;\n framework?: string;\n piiRedactionLevel?: \"STRICT\" | \"ESSENTIAL\" | \"DISABLED\";\n /**\n * Moves the project to this team. The platform refuses a team outside the\n * organization, an archived one, and a move across the personal workspace\n * boundary.\n */\n teamId?: string;\n}\n\nexport interface ProjectWithServiceKey extends Project {\n serviceApiKey: string;\n serviceApiKeyId: string;\n}\n\nexport interface PaginatedProjects {\n data: Project[];\n pagination: {\n page: number;\n limit: number;\n total: number;\n totalPages: number;\n };\n}\n\nexport interface ArchivedProject {\n id: string;\n name: string;\n archivedAt: string;\n}\n\nexport class ProjectsApiError extends Error {\n constructor(\n message: string,\n public readonly operation: string,\n public readonly originalError?: unknown,\n /**\n * The status the platform answered with. A 401/403 means the credential\n * cannot see the listing at all -- a different answer than \"no project\n * of yours matches this name\".\n */\n public readonly status?: number,\n ) {\n super(message);\n this.name = \"ProjectsApiError\";\n }\n}\n\nexport class ProjectsApiService {\n private readonly endpoint: string;\n private readonly apiKey: string;\n\n constructor(config?: { endpoint?: string; apiKey?: string }) {\n this.endpoint = resolveEndpoint(config?.endpoint);\n this.apiKey = config?.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n }\n\n /**\n * Bearer, never the project-pinned Basic shape: the listing asks \"which\n * projects can this credential see?\", so naming one project in the header\n * would scope the answer and defeat the call, including `--project <slug>`.\n */\n private headers(): Record<string, string> {\n return {\n ...buildSdkIdentityHeaders(),\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n private async request<T>(operation: string, path: string, init?: RequestInit): Promise<T> {\n const response = await langwatchFetch(`${this.endpoint}${path}`, {\n ...init,\n headers: mergeHeaders(this.headers(), init?.headers),\n });\n if (!response.ok) {\n let parsedBody: unknown;\n try {\n parsedBody = await response.json();\n } catch {\n parsedBody = await response.text();\n }\n const message = formatApiErrorForOperation({\n operation,\n error: parsedBody,\n options: { status: response.status },\n });\n throwIfHandledError({\n operation,\n error: parsedBody,\n status: response.status,\n message,\n });\n throw new ProjectsApiError(message, operation, parsedBody, response.status);\n }\n return (await response.json()) as T;\n }\n\n async list(options?: { page?: number; limit?: number }): Promise<PaginatedProjects> {\n const params = new URLSearchParams();\n if (options?.page) params.set(\"page\", String(options.page));\n if (options?.limit) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.request<PaginatedProjects>(\n \"list projects\",\n `/api/v1/projects${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n async get(id: string): Promise<Project> {\n return this.request<Project>(\n `get project \"${id}\"`,\n `/api/v1/projects/${encodeURIComponent(id)}`,\n );\n }\n\n /**\n * The project's ingest key. The platform hands it out only to a credential\n * with `project:update` on the project, so a 403 here is the answer \"this\n * login may not\", not a transport failure.\n */\n async getApiKey(id: string): Promise<string> {\n const answer = await this.request<{ apiKey: string }>(\n `get api key of project \"${id}\"`,\n `/api/v1/projects/${encodeURIComponent(id)}/api-key`,\n );\n return answer.apiKey;\n }\n\n async create(input: CreateProjectInput): Promise<ProjectWithServiceKey> {\n return this.request<ProjectWithServiceKey>(\"create project\", \"/api/v1/projects\", {\n method: \"POST\",\n body: JSON.stringify(input),\n });\n }\n\n async update(id: string, input: UpdateProjectInput): Promise<Project> {\n return this.request<Project>(\n `update project \"${id}\"`,\n `/api/v1/projects/${encodeURIComponent(id)}`,\n { method: \"PATCH\", body: JSON.stringify(input) },\n );\n }\n\n async archive(id: string): Promise<ArchivedProject> {\n return this.request<ArchivedProject>(\n `archive project \"${id}\"`,\n `/api/v1/projects/${encodeURIComponent(id)}`,\n { method: \"DELETE\" },\n );\n }\n}\n","import { LANGWATCH_SDK_NAME_CLIENT, LANGWATCH_SDK_VERSION } from \"@/internal/constants\";\nimport { getLangWatchTracer } from \"@/observability-sdk/tracer\";\n\nexport const tracer = getLangWatchTracer(\n `${LANGWATCH_SDK_NAME_CLIENT}.traces`,\n LANGWATCH_SDK_VERSION,\n);\n","import { createTracingProxy } from \"@/client-sdk/tracing/create-tracing-proxy\";\n\nimport { type InternalConfig } from \"../../types\";\nimport { TracesApiService } from \"./traces-api.service\";\nimport { tracer } from \"./tracing\";\nimport { type GetTraceParams, type GetTraceResponse } from \"./types\";\n\n/** `langwatch.traces`: the CLI's trace read, traced. Failures throw `TracesApiError`. */\nexport class TracesFacade {\n readonly #service: TracesApiService;\n\n constructor(config: InternalConfig) {\n this.#service = createTracingProxy(new TracesApiService(config), tracer);\n }\n\n /** `params` stays for compatibility; the endpoint takes no such option and never got one. */\n async get(traceId: string, _params?: GetTraceParams): Promise<GetTraceResponse> {\n const trace: GetTraceResponse = {};\n // The served schema is free-form; the hand-declared shape is asserted here, as before.\n return Object.assign(trace, await this.#service.get(traceId));\n }\n}\n","import { scopedApiKey } from \"@/internal/credentialContext\";\n\nimport { PromptsFacade, PromptsApiService } from \"./services/prompts\";\nexport { FetchPolicy, type GetPromptOptions } from \"./services/prompts\";\nexport type {\n Dataset,\n DatasetEntry,\n DatasetMetadata,\n DatasetColumnType,\n DatasetListItem,\n Pagination,\n PaginatedResponse,\n GetDatasetOptions,\n ListDatasetsOptions,\n ListDatasetsApiResponse,\n ListRecordsOptions,\n ListRecordsApiResponse,\n CreateDatasetOptions,\n UpdateDatasetOptions,\n CreateFromUploadResponse,\n BatchCreateRecordsResponse,\n DeleteRecordsResponse,\n UploadResponse,\n DatasetRecordResponse,\n} from \"./services/datasets\";\nexport {\n DatasetError,\n DatasetNotFoundError,\n DatasetApiError,\n DatasetValidationError,\n DatasetPlanLimitError,\n} from \"./services/datasets\";\nexport type {\n ExperimentRunResult,\n RunExperimentOptions,\n RunWithResultsOptions,\n ExperimentRowResult,\n ExperimentRunWithResults,\n} from \"./services/experiments\";\nexport {\n ExperimentsError,\n ExperimentNotFoundError,\n ExperimentTimeoutError,\n ExperimentRunFailedError,\n ExperimentsApiError,\n} from \"./services/experiments\";\nexport type {\n EvaluationResult,\n EvaluateOptions,\n EvaluationStatus,\n EvaluationCost,\n} from \"./services/evaluations\";\nexport {\n EvaluationError,\n EvaluatorCallError,\n EvaluatorNotFoundError,\n EvaluationsApiError,\n} from \"./services/evaluations\";\nexport { EvaluatorsApiService, EvaluatorsApiError } from \"./services/evaluators\";\nexport { ScenariosApiService, ScenariosApiError } from \"./services/scenarios\";\nexport { SuitesApiService, SuitesApiError } from \"./services/suites\";\nexport { RunPlansApiService, RunPlansApiError } from \"./services/run-plans\";\nexport { InstantEvalsApiService, InstantEvalsApiError } from \"./services/instant-evals\";\nexport { TestSuitesApiService, TestSuitesApiError } from \"./services/test-suites\";\nexport { WorkflowsApiService, WorkflowsApiError } from \"./services/workflows/workflows-api.service\";\nexport { AgentsApiService, AgentsApiError } from \"./services/agents/agents-api.service\";\nexport {\n AnnotationsApiService,\n AnnotationsApiError,\n} from \"./services/annotations/annotations-api.service\";\nexport {\n DashboardsApiService,\n DashboardsApiError,\n} from \"./services/dashboards/dashboards-api.service\";\nexport type {\n Dashboard,\n DashboardListItem,\n DashboardWidget,\n CreateDashboardWidgetInput,\n UpdateDashboardWidgetInput,\n} from \"./services/dashboards/dashboards-api.service\";\nexport {\n AnalyticsReportsApiService,\n AnalyticsReportsApiError,\n} from \"./services/analytics-reports/analytics-reports-api.service\";\nexport type {\n AnalyticsReportSummary,\n AnalyticsReportDetail,\n} from \"./services/analytics-reports/analytics-reports-api.service\";\nexport {\n ModelProvidersApiService,\n ModelProvidersApiError,\n} from \"./services/model-providers/model-providers-api.service\";\nexport { AnalyticsApiService, AnalyticsApiError } from \"./services/analytics/analytics-api.service\";\nexport { QueryApiService, QueryApiError } from \"./services/query/query-api.service\";\nexport { TriggersApiService, TriggersApiError } from \"./services/triggers\";\nexport { GraphsApiService, GraphsApiError } from \"./services/graphs\";\nexport { SimulationRunsApiService, SimulationRunsApiError } from \"./services/simulation-runs\";\nexport { TracesApiService, TracesApiError } from \"./services/traces/traces-api.service\";\nexport { MonitorsApiService, MonitorsApiError } from \"./services/monitors\";\nexport { SecretsApiService, SecretsApiError } from \"./services/secrets\";\nexport {\n VirtualKeysApiService,\n VirtualKeysApiError,\n} from \"./services/virtual-keys/virtual-keys-api.service\";\nexport {\n GatewayBudgetsApiService,\n GatewayBudgetsApiError,\n} from \"./services/gateway-budgets/gateway-budgets-api.service\";\nexport {\n SpendEventsApiService,\n SpendEventsApiError,\n} from \"./services/spend-events/spend-events-api.service\";\nexport { WebhooksApiService, WebhooksApiError } from \"./services/webhooks/webhooks-api.service\";\nexport { TeamsApiService, TeamsApiError } from \"./services/teams/teams-api.service\";\nexport type {\n Team,\n TeamPagination,\n TeamMember,\n ListTeamsResponse,\n ArchivedTeam,\n} from \"./services/teams/teams-api.service\";\nexport { ProjectsApiService, ProjectsApiError } from \"./services/projects/projects-api.service\";\nexport type {\n Project,\n PaginatedProjects,\n ProjectWithServiceKey,\n ArchivedProject,\n CreateProjectInput,\n UpdateProjectInput,\n} from \"./services/projects/projects-api.service\";\nimport { resolveEndpoint } from \"@/internal/endpoint\";\n\nimport { createLangWatchApiClient, type LangwatchApiClient } from \"../internal/api/client\";\nimport { type Logger, NoOpLogger } from \"../logger\";\nimport { AgentsApiService } from \"./services/agents/agents-api.service\";\nimport { AnalyticsReportsApiService } from \"./services/analytics-reports/analytics-reports-api.service\";\nimport { AnalyticsApiService } from \"./services/analytics/analytics-api.service\";\nimport { AnnotationsApiService } from \"./services/annotations/annotations-api.service\";\nimport { DashboardsApiService } from \"./services/dashboards/dashboards-api.service\";\nimport { DatasetsFacade } from \"./services/datasets\";\nimport { EvaluationsFacade } from \"./services/evaluations\";\nimport { EvaluatorsApiService } from \"./services/evaluators\";\nimport { ExperimentsFacade } from \"./services/experiments\";\nimport { GatewayBudgetsApiService } from \"./services/gateway-budgets/gateway-budgets-api.service\";\nimport { GraphsApiService } from \"./services/graphs\";\nimport { InstantEvalsApiService } from \"./services/instant-evals\";\nimport { ModelProvidersApiService } from \"./services/model-providers/model-providers-api.service\";\nimport { MonitorsApiService } from \"./services/monitors\";\nimport { ProjectsApiService } from \"./services/projects/projects-api.service\";\nimport { LocalPromptsService } from \"./services/prompts/local-prompts.service\";\nimport { QueryApiService } from \"./services/query/query-api.service\";\nimport { RunPlansApiService } from \"./services/run-plans\";\nimport { ScenariosApiService } from \"./services/scenarios\";\nimport { SecretsApiService } from \"./services/secrets\";\nimport { SimulationRunsApiService } from \"./services/simulation-runs\";\nimport { SpendEventsApiService } from \"./services/spend-events/spend-events-api.service\";\nimport { SuitesApiService } from \"./services/suites\";\nimport { TeamsApiService } from \"./services/teams/teams-api.service\";\nimport { TestSuitesApiService } from \"./services/test-suites\";\nimport { TracesFacade } from \"./services/traces/facade\";\nimport { TriggersApiService } from \"./services/triggers\";\nimport { VirtualKeysApiService } from \"./services/virtual-keys/virtual-keys-api.service\";\nimport { WebhooksApiService } from \"./services/webhooks/webhooks-api.service\";\nimport { WorkflowsApiService } from \"./services/workflows/workflows-api.service\";\nimport { type InternalConfig } from \"./types\";\n\nexport interface LangWatchConstructorOptions {\n apiKey?: string;\n endpoint?: string;\n projectId?: string;\n options?: {\n logger?: Logger;\n };\n}\n\nexport class LangWatch {\n private readonly config: InternalConfig & { endpoint: string; apiKey: string };\n\n readonly prompts: PromptsFacade;\n readonly traces: TracesFacade;\n readonly datasets: DatasetsFacade;\n\n /**\n * Run experiments on LangWatch platform or via SDK.\n */\n readonly experiments: ExperimentsFacade;\n\n /**\n * Run evaluators and guardrails in real-time (Online Evaluations).\n * @example\n */\n readonly evaluations: EvaluationsFacade;\n\n readonly evaluators: EvaluatorsApiService;\n readonly scenarios: ScenariosApiService;\n /**\n * @deprecated Use runPlans and testSuites; /api/v1/suites is a frozen alias.\n */\n readonly suites: SuitesApiService;\n readonly runPlans: RunPlansApiService;\n /** Judge one LangWatchQL statement across the project's history as a job. */\n readonly instantEvals: InstantEvalsApiService;\n readonly testSuites: TestSuitesApiService;\n readonly workflows: WorkflowsApiService;\n readonly agents: AgentsApiService;\n readonly annotations: AnnotationsApiService;\n /** The project's dashboards and the widgets on them, over /api/v1/dashboards. */\n readonly dashboards: DashboardsApiService;\n /** The Analytics Reports, over /api/v1/analytics/reports; the platform deprecates them. */\n readonly analyticsReports: AnalyticsReportsApiService;\n readonly modelProviders: ModelProvidersApiService;\n readonly analytics: AnalyticsApiService;\n /** The raw LangWatchQL door: a governed SELECT or schema discovery outside a saved chart. */\n readonly query: QueryApiService;\n readonly triggers: TriggersApiService;\n readonly graphs: GraphsApiService;\n readonly simulationRuns: SimulationRunsApiService;\n readonly monitors: MonitorsApiService;\n readonly secrets: SecretsApiService;\n readonly virtualKeys: VirtualKeysApiService;\n readonly gatewayBudgets: GatewayBudgetsApiService;\n readonly spendEvents: SpendEventsApiService;\n readonly webhooks: WebhooksApiService;\n\n #teams?: TeamsApiService;\n #projects?: ProjectsApiService;\n\n constructor(options: LangWatchConstructorOptions = {}) {\n const apiKey = options.apiKey ?? scopedApiKey() ?? process.env.LANGWATCH_API_KEY ?? \"\";\n const endpoint = resolveEndpoint(options.endpoint);\n\n this.config = this.#createInternalConfig({\n apiKey,\n endpoint,\n options: options.options,\n });\n\n this.prompts = new PromptsFacade({\n promptsApiService: new PromptsApiService(this.config),\n localPromptsService: new LocalPromptsService(),\n ...this.config,\n });\n this.traces = new TracesFacade(this.config);\n\n this.experiments = new ExperimentsFacade({\n langwatchApiClient: this.config.langwatchApiClient,\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n logger: this.config.logger,\n });\n\n this.datasets = new DatasetsFacade({\n langwatchApiClient: this.config.langwatchApiClient,\n logger: this.config.logger,\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n });\n\n this.evaluations = new EvaluationsFacade({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n logger: this.config.logger,\n });\n\n this.evaluators = new EvaluatorsApiService(this.config);\n this.scenarios = new ScenariosApiService(this.config);\n this.suites = new SuitesApiService(this.config);\n this.runPlans = new RunPlansApiService(this.config);\n this.instantEvals = new InstantEvalsApiService(this.config);\n this.testSuites = new TestSuitesApiService(this.config);\n this.workflows = new WorkflowsApiService(this.config);\n this.agents = new AgentsApiService(this.config);\n this.annotations = new AnnotationsApiService(this.config);\n this.dashboards = new DashboardsApiService(this.config);\n this.analyticsReports = new AnalyticsReportsApiService(this.config);\n this.modelProviders = new ModelProvidersApiService(this.config);\n this.analytics = new AnalyticsApiService(this.config);\n this.query = new QueryApiService(this.config);\n this.triggers = new TriggersApiService(this.config);\n this.graphs = new GraphsApiService(this.config);\n this.simulationRuns = new SimulationRunsApiService(this.config);\n this.monitors = new MonitorsApiService({ apiKey, endpoint });\n this.secrets = new SecretsApiService({\n apiKey,\n endpoint,\n projectId: options.projectId,\n });\n this.virtualKeys = new VirtualKeysApiService({ apiKey, endpoint });\n this.gatewayBudgets = new GatewayBudgetsApiService({ apiKey, endpoint });\n this.spendEvents = new SpendEventsApiService({ apiKey, endpoint });\n this.webhooks = new WebhooksApiService({ apiKey, endpoint });\n }\n\n get apiClient(): LangwatchApiClient {\n return this.config.langwatchApiClient;\n }\n\n /**\n * Teams, which group projects and the members who can reach them. These routes want an\n * organization API key.\n */\n get teams(): TeamsApiService {\n this.#teams ??= new TeamsApiService(this.#managementConfig());\n return this.#teams;\n }\n\n /**\n * Projects, including provisioning one with its own service API key. These\n * routes want an organization API key.\n */\n get projects(): ProjectsApiService {\n this.#projects ??= new ProjectsApiService(this.#managementConfig());\n return this.#projects;\n }\n\n /**\n * What the management families are constructed with. An empty key is left\n * off entirely so they resolve the ambient credential instead of\n * authenticating with a blank bearer token.\n */\n #managementConfig(): { apiKey?: string; endpoint: string } {\n return {\n ...(this.config.apiKey ? { apiKey: this.config.apiKey } : {}),\n endpoint: this.config.endpoint,\n };\n }\n\n #createInternalConfig({\n apiKey,\n endpoint,\n options,\n }: {\n apiKey: string;\n endpoint: string;\n options?: LangWatchConstructorOptions[\"options\"];\n }): InternalConfig & { endpoint: string; apiKey: string } {\n return {\n logger: options?.logger ?? new NoOpLogger(),\n langwatchApiClient: createLangWatchApiClient(apiKey, endpoint),\n endpoint,\n apiKey,\n };\n }\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Verify webhook delivery signature in X-LangWatch-Signature header.\n * Handles secret rotation: v1 repeats with newest first.\n * Pinned to sender's algorithm by specs/webhooks/signature-vectors.json.\n */\n\n/** The header a delivery carries its signature in. */\nexport const WEBHOOK_SIGNATURE_HEADER = \"X-LangWatch-Signature\";\n\n/**\n * Identifies one delivery ATTEMPT on the webhook platform's endpoints. The\n * natural idempotency key: retries of the same batch repeat it, so a\n * receiver that already processed this id can stop, not apply it twice.\n */\nexport const WEBHOOK_DELIVERY_ID_HEADER = \"X-LangWatch-Delivery-Id\";\n\n/**\n * The same role on automation deliveries (graph alerts and friends), which\n * group attempts by the logical fire rather than the batch. Two names\n * because they are two senders: read whichever the delivery carries.\n */\nexport const WEBHOOK_EVENT_ID_HEADER = \"X-LangWatch-Event-Id\";\n\n/**\n * How far a delivery's timestamp may sit from the receiver's clock, in\n * seconds. Matches the sender's documented window.\n */\nexport const WEBHOOK_SIGNATURE_DEFAULT_TOLERANCE_SECONDS = 300;\n\n/**\n * Why a delivery was refused: malformed_header, stale_timestamp, invalid_signature.\n * Switch on code, not message; message is for logs and may change.\n */\nexport type WebhookSignatureFailureCode =\n | \"malformed_header\"\n | \"stale_timestamp\"\n | \"invalid_signature\";\n\n/**\n * A delivery that did not verify. Use code, not error type, to branch logic.\n */\nexport class WebhookSignatureVerificationError extends Error {\n readonly code: WebhookSignatureFailureCode;\n\n constructor(code: WebhookSignatureFailureCode, message: string) {\n super(message);\n this.name = \"WebhookSignatureVerificationError\";\n this.code = code;\n }\n}\n\nexport interface VerifyWebhookSignatureOptions {\n /**\n * The EXACT bytes of the request body, as received — not a parsed or\n * re-serialized object: `JSON.stringify` reorders keys and re-escapes\n * text, changing the digest. Read the raw body before your JSON middleware.\n */\n body: string | Uint8Array;\n /** The `X-LangWatch-Signature` header value, verbatim. */\n header: string;\n /**\n * The signing secret, or every secret this receiver currently accepts.\n * Pass both during a rotation and the delivery verifies under either, so\n * there is no window where deliveries are refused.\n */\n secret: string | readonly string[];\n /**\n * Freshness window in seconds, defaulting to the sender's five minutes.\n * Tighten it only if your clocks are disciplined.\n */\n toleranceSeconds?: number;\n /**\n * The current time in unix SECONDS. Defaults to the system clock; pass it\n * to verify a delivery captured earlier, or from a test.\n */\n nowSeconds?: number;\n}\n\n/** The timestamp and EVERY `v1` the header carries, in the order sent. */\nfunction parseSignatureHeader(header: string): {\n timestamp: number | undefined;\n candidates: string[];\n} {\n let timestamp: number | undefined;\n const candidates: string[] = [];\n for (const piece of header.split(\",\")) {\n const eq = piece.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = piece.slice(0, eq).trim();\n const value = piece.slice(eq + 1).trim();\n // Every `v1` is kept. Keeping only one is the rotation bug this helper\n // exists to make impossible.\n if (key === \"v1\") candidates.push(value);\n else if (key === \"t\" && value.length > 0) timestamp = Number(value);\n }\n return { timestamp, candidates };\n}\n\n/** Constant-time equality over two hex digests, length-safe. */\nfunction digestsMatch(expected: string, candidate: string): boolean {\n const a = Buffer.from(expected, \"utf8\");\n const b = Buffer.from(candidate, \"utf8\");\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction signedPayload(timestamp: number, body: string | Uint8Array): Buffer {\n const prefix = Buffer.from(`${timestamp}.`, \"utf8\");\n const bytes = typeof body === \"string\" ? Buffer.from(body, \"utf8\") : Buffer.from(body);\n return Buffer.concat([prefix, bytes]);\n}\n\nfunction matchesAnySignature({\n secrets,\n candidates,\n payload,\n}: {\n secrets: string[];\n candidates: string[];\n payload: Buffer;\n}): boolean {\n let matched = false;\n\n for (const secret of secrets) {\n const expected = createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\n for (const candidate of candidates) {\n // Every pair is compared even once one has matched, so the work does\n // not depend on WHICH secret or which v1 was the right one.\n if (digestsMatch(expected, candidate)) matched = true;\n }\n }\n\n return matched;\n}\n\n/**\n * Verify a webhook delivery, throwing (not returning false) so a check can't\n * be skipped by accident. A missing/empty secret throws a plain `TypeError`\n * instead of a verification failure, so it's never mistaken for sender fault.\n */\nexport function verifyWebhookSignature(options: VerifyWebhookSignatureOptions): void {\n const secrets = (typeof options.secret === \"string\" ? [options.secret] : options.secret).filter(\n (secret) => typeof secret === \"string\" && secret.length > 0,\n );\n if (secrets.length === 0) {\n throw new TypeError(\"verifyWebhookSignature needs at least one non-empty signing secret\");\n }\n\n const tolerance = options.toleranceSeconds ?? WEBHOOK_SIGNATURE_DEFAULT_TOLERANCE_SECONDS;\n const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);\n\n const { timestamp, candidates } = parseSignatureHeader(options.header);\n if (timestamp === undefined || !Number.isFinite(timestamp)) {\n throw new WebhookSignatureVerificationError(\n \"malformed_header\",\n `${WEBHOOK_SIGNATURE_HEADER} carries no readable t= timestamp, so the delivery cannot be checked for freshness`,\n );\n }\n if (candidates.length === 0) {\n throw new WebhookSignatureVerificationError(\n \"malformed_header\",\n `${WEBHOOK_SIGNATURE_HEADER} carries no v1= signature, so there is nothing to compare`,\n );\n }\n\n if (Math.abs(now - timestamp) > tolerance) {\n throw new WebhookSignatureVerificationError(\n \"stale_timestamp\",\n `the delivery was signed ${Math.abs(now - timestamp)}s from now, outside the ${tolerance}s tolerance`,\n );\n }\n\n const payload = signedPayload(timestamp, options.body);\n const matched = matchesAnySignature({ secrets, candidates, payload });\n\n if (!matched) {\n throw new WebhookSignatureVerificationError(\n \"invalid_signature\",\n `no v1 signature in ${WEBHOOK_SIGNATURE_HEADER} matched the ${secrets.length === 1 ? \"secret\" : \"secrets\"} held, so the body was signed with something else or changed in transit`,\n );\n }\n}\n","import { ConsoleLogger, NoOpLogger } from \"./logger\";\n\nexport { getLangWatchTracer, getLangWatchLogger, attributes } from \"./observability-sdk\";\n\nexport {\n FilterableBatchSpanProcessor,\n type SpanProcessingExcludeRule,\n} from \"./observability-sdk/processors\";\nexport { LangWatchExporter } from \"./observability-sdk/exporters\";\nexport { LangWatch, FetchPolicy, type GetPromptOptions } from \"./client-sdk\";\n\n/**\n * Typed API failures: `code` to switch on, `meta`, and a trace id. Narrow\n * with `isLangWatchHandledError` and match `code`, not the message — an\n * unnamed failure (5xx, dead socket) still arrives as a generic error.\n */\nexport {\n LangWatchHandledError,\n isLangWatchHandledError,\n LangWatchApiError,\n} from \"./internal/api/errors\";\nexport type {\n LangWatchHandledErrorShape,\n LangWatchHandledErrorReason,\n} from \"@langwatch/handled-error/langwatch-handled-error\";\n\n// Experiments API exports\nexport {\n Experiment,\n ExperimentsFacade,\n type ExperimentEvaluationStatus,\n type TargetType,\n type TargetMetadata,\n type TargetInfo,\n type ExperimentEvaluationResult,\n type ComparisonMetric,\n type ComparisonOptions,\n type ComparisonStatus,\n type ComparisonVerdict,\n type ExperimentInitOptions,\n type LogOptions,\n type ExperimentEvaluateOptions,\n type RunOptions,\n type RunCallback,\n type RunContext,\n type RunWithResultsOptions,\n type ExperimentRowResult,\n type ExperimentRunWithResults,\n ExperimentError,\n ExperimentInitError,\n ExperimentApiError,\n TargetMetadataConflictError,\n ComparisonError,\n EvaluatorError,\n} from \"./client-sdk/services/experiments\";\n\n// Evaluators API exports\nexport {\n EvaluatorsApiService,\n type EvaluatorResponse,\n type EvaluatorField,\n type CreateEvaluatorBody,\n EvaluatorsApiError,\n} from \"./client-sdk/services/evaluators\";\n\n// Dataset API exports\nexport {\n DatasetsFacade,\n DatasetError,\n DatasetNotFoundError,\n DatasetApiError,\n DatasetValidationError,\n DatasetPlanLimitError,\n type Dataset,\n type DatasetEntry,\n type DatasetMetadata,\n type DatasetColumnType,\n type DatasetListItem,\n type Pagination,\n type PaginatedResponse,\n type GetDatasetOptions,\n type ListDatasetsOptions,\n type ListDatasetsApiResponse,\n type ListRecordsOptions,\n type ListRecordsApiResponse,\n type CreateDatasetOptions,\n type UpdateDatasetOptions,\n type CreateFromUploadResponse,\n type BatchCreateRecordsResponse,\n type DeleteRecordsResponse,\n type UploadResponse,\n type DatasetRecordResponse,\n} from \"./client-sdk/services/datasets\";\n\n// Evaluations API exports (Online Evaluations / Guardrails)\nexport {\n EvaluationsFacade,\n type EvaluationResult,\n type EvaluateOptions,\n type EvaluationStatus,\n type EvaluationCost,\n EvaluationError,\n EvaluatorCallError,\n EvaluatorNotFoundError,\n EvaluationsApiError,\n} from \"./client-sdk/services/evaluations\";\n\n/**\n * The per-call options every mutating call on the gateway and webhook\n * surfaces accepts, including the idempotency key that makes a create safe to\n * retry after a timeout.\n */\nexport {\n IDEMPOTENCY_KEY_HEADER,\n IDEMPOTENT_REPLAY_HEADER,\n type MutationOptions,\n type IdempotentCreateOptions,\n} from \"./client-sdk/services/_shared/mutation-options\";\n\n// AI Gateway management API exports (virtual keys + budgets)\nexport {\n VirtualKeysApiService,\n VirtualKeysApiError,\n type VirtualKey,\n type VirtualKeyScope,\n type VirtualKeyScopeType,\n type VirtualKeyRoutingMode,\n type VirtualKeyBudgetInput,\n type VirtualKeyWithSecret,\n type VirtualKeyPage,\n type VirtualKeySpendSummary,\n type CreateVirtualKeyInput,\n type UpdateVirtualKeyInput,\n} from \"./client-sdk/services/virtual-keys/virtual-keys-api.service\";\nexport {\n WebhooksApiService,\n WebhooksApiError,\n type WebhookEndpointSummary,\n type WebhookEndpointWithSecret,\n type WebhookDeliveryRecord,\n type WebhookDeliveryPage,\n type WebhookTestResult,\n type WebhookEndpointHealth,\n type WebhookEventType,\n type EmittedEvent,\n type EmittedEventsPage,\n type CreateWebhookEndpointInput,\n type UpdateWebhookEndpointInput,\n} from \"./client-sdk/services/webhooks/webhooks-api.service\";\n/**\n * Receiving webhooks, which needs no API client at all: a receiver holds a\n * signing secret and the raw request, and has to decide whether to trust it.\n */\nexport {\n verifyWebhookSignature,\n WebhookSignatureVerificationError,\n WEBHOOK_SIGNATURE_HEADER,\n WEBHOOK_SIGNATURE_DEFAULT_TOLERANCE_SECONDS,\n WEBHOOK_DELIVERY_ID_HEADER,\n WEBHOOK_EVENT_ID_HEADER,\n type WebhookSignatureFailureCode,\n type VerifyWebhookSignatureOptions,\n} from \"./client-sdk/services/webhooks/verify-signature\";\nexport {\n SpendEventsApiService,\n SpendEventsApiError,\n type SpendEvent,\n type SpendEventsPage,\n type SpendSummaryRow,\n type SpendSummariesPage,\n type SpendEventStatus,\n type SpendSummaryStatus,\n type SpendGroupBy,\n type SpendFilterOptions,\n type SpendSummariesOptions,\n type SpendReplayResult,\n type EndUserSpend,\n type EndUserCap,\n} from \"./client-sdk/services/spend-events/spend-events-api.service\";\nexport {\n GatewayBudgetsApiService,\n GatewayBudgetsApiError,\n type GatewayBudget,\n type GatewayBudgetPage,\n type BudgetScopeKind,\n type BudgetWindow,\n type BudgetOnBreach,\n type CreateGatewayBudgetScope,\n type CreateGatewayBudgetInput,\n type UpdateGatewayBudgetInput,\n} from \"./client-sdk/services/gateway-budgets/gateway-budgets-api.service\";\n\n/**\n * Provisioning teams and projects, the two things an integration creates\n * before anything else exists to write to. Both want an organization API\n * key; creating a project also mints its own service API key, once.\n */\nexport {\n TeamsApiService,\n TeamsApiError,\n type Team,\n type TeamPagination,\n type TeamMember,\n type ListTeamsResponse,\n type ArchivedTeam,\n} from \"./client-sdk/services/teams/teams-api.service\";\nexport {\n ProjectsApiService,\n ProjectsApiError,\n type Project,\n type PaginatedProjects,\n type ProjectWithServiceKey,\n type ArchivedProject,\n type CreateProjectInput,\n type UpdateProjectInput,\n} from \"./client-sdk/services/projects/projects-api.service\";\n\nexport const logger = {\n ConsoleLogger,\n NoOpLogger,\n};\n\n/**\n * The HTTP client every SDK request goes through, following a redirect\n * only when it upgrades http to https on the same URL, refusing every\n * other one with `LangWatchRedirectError`.\n */\nexport {\n langwatchFetch,\n createLangWatchFetch,\n LangWatchRedirectError,\n type LangWatchFetch,\n type CreateLangWatchFetchOptions,\n} from \"./internal/http/langwatchFetch\";\n"]}