{"version":3,"file":"yaml-BPBoiOjx.mjs","names":["#options","#resolveDocs","#docs"],"sources":["../src/batteries/artifacts/yaml/exceptions.ts","../src/batteries/artifacts/yaml/index.ts"],"sourcesContent":["import { createException } from '@nhtio/adk/factories'\n\n/**\n * Thrown when YAML parsing fails due to invalid syntax.\n *\n * @remarks\n * The exception message includes the parser's original error reason and line/mark information\n * when available, which enables the model to self-correct malformed YAML.\n */\nexport const E_YAML_PARSE_ERROR = createException<[string]>(\n  'E_YAML_PARSE_ERROR',\n  'YAML parse error: %s',\n  'E_YAML_PARSE_ERROR',\n  422\n)\n\n/**\n * Thrown when the js-yaml peer dependency is missing or fails to load.\n *\n * @remarks\n * This occurs when the battery's consumer has not installed the optional js-yaml peer\n * dependency. The message template includes the package name, a description of its purpose,\n * the underlying error, and the exact install command, so the consumer sees a complete,\n * actionable message regardless of call site.\n */\nexport const E_YAML_PEER_MISSING = createException<[string]>(\n  'E_YAML_PEER_MISSING',\n  'the yaml battery could not load its peer dependency \"js-yaml\" (needed for YAML format artifact queries): %s — install it (pnpm add js-yaml)',\n  'E_YAML_PEER_MISSING',\n  500\n)\n","/**\n * YAML artifact battery: {@link SpooledYamlArtifact} and bidirectional converters.\n *\n * @remarks\n * Provides structured query tools for YAML documents, both single-document and\n * multi-document streams. Includes converter tools to transform between YAML and JSON\n * representations without materialising the artifact contents.\n *\n * Requires the optional peer `js-yaml@^4.1.1`. Install with:\n * ```\n * pnpm add js-yaml\n * ```\n *\n * Note: `decode()` on `SpooledYamlArtifact` instances throws until\n * `registerArtifactEncodables()` has been called.\n *\n * @module @nhtio/adk/batteries/artifacts/yaml\n */\n\nimport { v6 as uuidv6 } from 'uuid'\nimport { JSONPath } from 'jsonpath-plus'\nimport { validator } from '@nhtio/validation'\nimport { resolveSpoolReader } from '@nhtio/adk/common'\nimport { isInstanceOf, isError, isObject } from '@nhtio/adk/guards'\nimport { E_YAML_PARSE_ERROR, E_YAML_PEER_MISSING } from './exceptions'\nimport {\n  Tool,\n  ArtifactTool,\n  ToolRegistry,\n  SpooledJsonArtifact,\n  ReaderDescriptor,\n} from '@nhtio/adk/common'\n\nimport {\n  collectArtifactCompatibleIds,\n  resolveArtifactById,\n  defaultSerialise,\n  SpooledArtifact,\n} from '@nhtio/adk/spooled_artifact'\nimport type { SpoolReader } from '@nhtio/adk/common'\nimport type { ToolMethodDescriptor, DispatchContext } from '@nhtio/adk/types'\n\n// Well-known @nhtio/encoder contract keys, resolved through the global symbol registry.\n// These are identical to the symbols core uses, with no import edge on the optional peer.\nconst ENCODE_METHOD: unique symbol = Symbol.for('@nhtio/encoder:toEncoded')\nconst DECODE_METHOD: unique symbol = Symbol.for('@nhtio/encoder:fromEncoded')\n\n/** Snapshot payload for the encoder contract; the encoder treats it as opaque. */\ntype AdkEncodableSnapshot = unknown\n\n/**\n * Lazy-loaded promise for the js-yaml module.\n *\n * @remarks\n * Caches the result to avoid repeated import attempts. If the import fails, the exception\n * is thrown on subsequent access rather than re-importing.\n */\nlet yamlPromise:\n  | Promise<{\n      load: (content: string) => unknown\n      loadAll: (content: string) => unknown[]\n      dump: (data: unknown, options?: Record<string, unknown>) => string\n    }>\n  | undefined\n\n/**\n * Lazy-loads and caches the js-yaml module.\n *\n * @returns The loaded `js-yaml` module.\n * @throws {@link E_YAML_PEER_MISSING} if the module fails to load.\n */\nasync function getYaml() {\n  yamlPromise ??= import('js-yaml').catch((err) => {\n    throw new E_YAML_PEER_MISSING([isError(err) ? err.message : String(err)])\n  })\n  return yamlPromise\n}\n\n/**\n * A {@link SpooledArtifact} specialisation that adds YAML-aware read operations.\n *\n * @remarks\n * Handles both single-document YAML and multi-document streams (delimited by `---`).\n * Parsed documents are cached in a private field for the lifetime of the instance.\n *\n * For multi-document streams:\n * - `yaml_length` reports the document count.\n * - `yaml_keys` returns the deduplicated union of keys across all documents.\n * - `yaml_get` / `yaml_filter` / `yaml_pluck` evaluate paths against all documents and return\n *   a flat array of all matches.\n *\n * Non-finite numbers (`.NaN`, `.inf`, `-.inf`) present in the YAML are preserved through the\n * `yaml_to_json` converter using a custom replacer.\n */\nexport class SpooledYamlArtifact extends SpooledArtifact {\n  #docs: unknown[] | undefined\n\n  /**\n   * @param reader - The backing store to read from.\n   * @param options - Optional configuration for parsing.\n   * @param options.multiDocument - Declares the source's document mode. `true` parses as a\n   *   stream and makes {@link SpooledYamlArtifact.yaml_type} report `'multi-document'` regardless\n   *   of the current count. `false` asserts exactly one document and parses with `load`, so a\n   *   `---` stream raises `E_YAML_PARSE_ERROR` instead of being silently accepted. When omitted,\n   *   mode is auto-detected\n   *   by parsing the content with `loadAll`.\n   */\n  constructor(reader: SpoolReader, options?: { multiDocument?: boolean }) {\n    super(reader)\n    this.#options = options\n  }\n\n  #options: { multiDocument?: boolean } | undefined\n\n  /**\n   * Returns `true` if `value` is a {@link SpooledYamlArtifact} instance.\n   *\n   * @remarks\n   * Uses the cross-realm-safe {@link @nhtio/adk!isInstanceOf} guard. Safe against the\n   * dual-module-copy case where two distinct `SpooledYamlArtifact` classes coexist in the\n   * same realm.\n   *\n   * @param value - The value to test.\n   * @returns `true` when `value` is a {@link SpooledYamlArtifact} instance.\n   */\n  public static isSpooledYamlArtifact(value: unknown): value is SpooledYamlArtifact {\n    return isInstanceOf(value, 'SpooledYamlArtifact', SpooledYamlArtifact)\n  }\n\n  /**\n   * The YAML-specific artifact-query descriptors this class adds on top of the base set.\n   *\n   * @remarks\n   * Lists `artifact_yaml_type`, `artifact_yaml_keys`, `artifact_yaml_length`,\n   * `artifact_yaml_get`, `artifact_yaml_filter`, `artifact_yaml_slice`, `artifact_yaml_pluck`.\n   * The base seven descriptors (`artifact_head`, etc.) are NOT included here — they are\n   * forged separately by {@link SpooledYamlArtifact.forgeTools}.\n   */\n  public static toolMethods: ReadonlyArray<ToolMethodDescriptor> = Object.freeze([\n    {\n      name: 'artifact_yaml_type',\n      method: 'yaml_type',\n      description:\n        'Return the YAML type (single-document or multi-document) of an artifact produced earlier in this turn.',\n      argsSchema: validator.object({}),\n    },\n    {\n      name: 'artifact_yaml_keys',\n      method: 'yaml_keys',\n      description:\n        'Return the top-level keys of a YAML artifact produced earlier in this turn; for multi-document streams, the deduplicated union across all documents.',\n      argsSchema: validator.object({}),\n    },\n    {\n      name: 'artifact_yaml_length',\n      method: 'yaml_length',\n      description:\n        'Return the document count of a YAML artifact produced earlier in this turn. The count comes from js-yaml parsing; for sources with no actual YAML content (empty, whitespace, or BOM), the result is typically 0 but may be 1 depending on whitespace arrangement. For real YAML documents or multi-document streams separated by ---, the count is exact.',\n      argsSchema: validator.object({}),\n    },\n    {\n      name: 'artifact_yaml_get',\n      method: 'yaml_get',\n      description:\n        'Evaluate a JSONPath expression against a YAML artifact produced earlier in this turn.',\n      argsSchema: validator.object({\n        path: validator.string().required().description(\"JSONPath expression, e.g. '$.user.name'.\"),\n      }),\n    },\n    {\n      name: 'artifact_yaml_filter',\n      method: 'yaml_filter',\n      description:\n        'Return records of a YAML artifact (produced earlier in this turn) matched by a JSONPath filter.',\n      argsSchema: validator.object({\n        path: validator\n          .string()\n          .required()\n          .description('JSONPath filter expression, e.g. \\'$[?(@.status === \"active\")]\\'.'),\n      }),\n    },\n    {\n      name: 'artifact_yaml_slice',\n      method: 'yaml_slice',\n      description:\n        'Return a slice of documents by index range from a YAML artifact produced earlier in this turn.',\n      argsSchema: validator.object({\n        start: validator\n          .number()\n          .integer()\n          .min(0)\n          .optional()\n          .description('Start index (inclusive).'),\n        end: validator.number().integer().min(0).optional().description('End index (exclusive).'),\n      }),\n    },\n    {\n      name: 'artifact_yaml_pluck',\n      method: 'yaml_pluck',\n      description:\n        'Return all values matched by a JSONPath expression across every document of a YAML artifact produced earlier in this turn.',\n      argsSchema: validator.object({\n        path: validator.string().required().description(\"JSONPath expression, e.g. '$..name'.\"),\n      }),\n    },\n  ])\n\n  /**\n   * Forges base-class tools plus YAML-specific tools narrowed to {@link SpooledYamlArtifact}.\n   *\n   * @remarks\n   * Standard subclass extension pattern: call `SpooledArtifact.forgeTools(ctx)` to produce\n   * the base seven `artifact_*` tools narrowed to any `SpooledArtifact` in the turn, then\n   * register one `ArtifactTool` per YAML-specific descriptor narrowed to YAML artifacts.\n   */\n  public static override forgeTools(ctx: DispatchContext): ToolRegistry {\n    const registry = SpooledArtifact.forgeTools(ctx)\n    const requires = SpooledYamlArtifact\n    const compatibleIds = collectArtifactCompatibleIds(ctx, requires)\n    if (compatibleIds.length === 0) return registry\n\n    for (const descriptor of this.toolMethods) {\n      const callIdSchema = validator\n        .string()\n        .valid(...compatibleIds)\n        .required()\n        .description('ToolCall id of the artifact to query.')\n\n      const argsSchema = (\n        descriptor.argsSchema ?? validator.object<Record<string, never>>({})\n      ).append({\n        callId: callIdSchema,\n      })\n\n      const tool = new ArtifactTool({\n        name: descriptor.name,\n        description: descriptor.description,\n        inputSchema: argsSchema,\n        ephemeral: true,\n        onCollision: 'replace',\n        handler: async (rawArgs, ctxInner) => {\n          const args = rawArgs as Record<string, unknown> & { callId: string }\n          const resolved = resolveArtifactById(ctxInner, args.callId, requires)\n          if (!resolved) return `Error: no artifact with id ${args.callId} in this turn`\n          const artifact = resolved.artifact\n          const methodArgs: unknown[] = []\n          if (\n            descriptor.method === 'yaml_get' ||\n            descriptor.method === 'yaml_filter' ||\n            descriptor.method === 'yaml_pluck'\n          ) {\n            methodArgs.push(args.path as string)\n          } else if (descriptor.method === 'yaml_slice') {\n            methodArgs.push(args.start as number | undefined, args.end as number | undefined)\n          }\n          const fn = (artifact as unknown as Record<string, (...a: unknown[]) => unknown>)[\n            descriptor.method\n          ]\n          if (typeof fn !== 'function') {\n            return `Error: artifact has no method ${descriptor.method}`\n          }\n          const result = await Promise.resolve(fn.apply(artifact, methodArgs))\n          const serialise = descriptor.serialise ?? defaultSerialise\n          return serialise(result)\n        },\n      })\n      registry.register(tool)\n    }\n    return registry\n  }\n\n  /**\n   * Parses and caches all documents from the artifact.\n   *\n   * @remarks\n   * Uses `js-yaml.loadAll` to extract all documents from the stream. A single-document source\n   * yields a one-element array; a multi-document stream yields one element per document. When\n   * the constructor declared `multiDocument: false`, `load` is used instead so that a\n   * multi-document source is rejected rather than quietly accepted. The parsed result is cached\n   * for the lifetime of the instance.\n   */\n  async #resolveDocs(): Promise<unknown[]> {\n    if (this.#docs !== undefined) {\n      return this.#docs\n    }\n\n    const yaml = await getYaml()\n    const content = await this.asString()\n    try {\n      if (this.#options?.multiDocument === false) {\n        // The caller asserted this source holds exactly one document. `load` enforces that: it\n        // throws \"expected a single document in the stream, but found more\" on a `---` stream,\n        // which surfaces below as E_YAML_PARSE_ERROR. Anything weaker would make the option a\n        // no-op, which is what it was before.\n        this.#docs = [yaml.load(content)]\n      } else {\n        const loaded = yaml.loadAll(content)\n        this.#docs = Array.isArray(loaded) ? loaded : [loaded]\n      }\n    } catch (err) {\n      let detail = isError(err) ? err.message : String(err)\n      // Append line and mark information if available for self-correction\n      if (isObject(err)) {\n        const mark = (err as Record<string, unknown>).mark as Record<string, unknown> | undefined\n        if (mark && (mark.line !== undefined || mark.column !== undefined)) {\n          const line = typeof mark.line === 'number' ? mark.line + 1 : 0\n          const col = typeof mark.column === 'number' ? mark.column + 1 : 0\n          detail += ` (line ${line}:${col})`\n        }\n      }\n      throw new E_YAML_PARSE_ERROR([detail])\n    }\n    return this.#docs\n  }\n\n  /**\n   * Returns whether this artifact contains a single document or multiple documents.\n   *\n   * @remarks\n   * When the constructor was given `multiDocument: true`, the source is treated as a stream and\n   * this reports `'multi-document'` even if that stream currently holds one document — a\n   * one-element stream is still a stream, and {@link SpooledYamlArtifact.yaml_length} reports the\n   * real count either way. `multiDocument: false` cannot disagree with the count, because parsing\n   * a `---` stream under it fails outright rather than silently reporting the wrong mode.\n   *\n   * @returns `'single-document'` or `'multi-document'`.\n   */\n  async yaml_type(): Promise<'single-document' | 'multi-document'> {\n    const docs = await this.#resolveDocs()\n    if (this.#options?.multiDocument === true) {\n      return 'multi-document'\n    }\n    return docs.length > 1 ? 'multi-document' : 'single-document'\n  }\n\n  /**\n   * Returns the top-level keys of the parsed content.\n   *\n   * @remarks\n   * For single-document: returns the keys of the root object, or `undefined` when the root\n   * is not a plain object.\n   * For multi-document: returns the union of keys across all documents that are plain objects.\n   * Duplicate keys are deduplicated.\n   *\n   * @returns Array of key strings, or `undefined` when no object keys are present.\n   */\n  async yaml_keys(): Promise<string[] | undefined> {\n    const docs = await this.#resolveDocs()\n    const keySet = new Set<string>()\n\n    for (const doc of docs) {\n      if (isObject(doc)) {\n        for (const key of Object.keys(doc as object)) {\n          keySet.add(key)\n        }\n      }\n    }\n\n    return keySet.size > 0 ? Array.from(keySet) : undefined\n  }\n\n  /**\n   * Returns the total number of documents in the artifact.\n   *\n   * @remarks\n   * The result comes directly from js-yaml.loadAll(). For sources with no actual YAML content\n   * (empty, whitespace-only, or BOM-only), the parser typically returns 0 documents, but certain\n   * whitespace arrangements (such as a bare double newline) may yield 1. Do not rely on the exact\n   * count to test for emptiness. For real documents, the count is reliable: a single-document\n   * YAML returns 1, and a `---`-separated stream returns its exact document count.\n   *\n   * @returns The document count.\n   */\n  async yaml_length(): Promise<number> {\n    const docs = await this.#resolveDocs()\n    return docs.length\n  }\n\n  /**\n   * Evaluates a JSONPath expression against the parsed documents.\n   *\n   * @remarks\n   * For single-document: evaluates the expression against the root value.\n   * For multi-document: evaluates the expression against each document and returns a flat\n   * array of all matches across all documents.\n   *\n   * Uses [JSONPath-Plus](https://github.com/JSONPath-Plus/JSONPath). Full JSONPath syntax is\n   * supported.\n   *\n   * @param path - A JSONPath expression (e.g. `'$.user.address.city'`, `'$..name'`).\n   * @returns Array of matched values. Empty array when no matches are found.\n   */\n  async yaml_get(path: string): Promise<unknown[]> {\n    const docs = await this.#resolveDocs()\n    return docs.flatMap((doc) => JSONPath({ path, json: doc as object }))\n  }\n\n  /**\n   * Returns documents matched by a JSONPath filter expression.\n   *\n   * @remarks\n   * Evaluates `path` against each document and returns those for which the expression\n   * produces at least one match.\n   *\n   * @param path - A JSONPath expression (e.g. `'$[?(@.status === \"active\")]'`).\n   * @returns Array of matching documents.\n   */\n  async yaml_filter(path: string): Promise<unknown[]> {\n    const docs = await this.#resolveDocs()\n    return docs.filter((doc) => {\n      const matches = JSONPath({ path, json: doc as object })\n      return Array.isArray(matches) && matches.length > 0\n    })\n  }\n\n  /**\n   * Returns a slice of documents by index range.\n   *\n   * @remarks\n   * Behaves like `Array.prototype.slice` over the document array.\n   *\n   * @param start - Start index (inclusive). Defaults to `0`.\n   * @param end - End index (exclusive). Defaults to the document count.\n   * @returns Array of sliced documents.\n   */\n  async yaml_slice(start?: number, end?: number): Promise<unknown[]> {\n    const docs = await this.#resolveDocs()\n    return docs.slice(start, end)\n  }\n\n  /**\n   * Returns all values matched by a JSONPath expression across every document.\n   *\n   * @remarks\n   * Convenience over {@link yaml_get} with an identical signature — use whichever name\n   * better communicates intent at the call site.\n   *\n   * @param path - A JSONPath expression (e.g. `'$..name'`).\n   * @returns Array of matched values.\n   */\n  async yaml_pluck(path: string): Promise<unknown[]> {\n    return this.yaml_get(path)\n  }\n\n  /**\n   * Serialise this SpooledYamlArtifact into an `@nhtio/encoder` snapshot.\n   *\n   * @remarks\n   * Overrides {@link SpooledArtifact.[ENCODE_METHOD]} to carry the constructor's `multiDocument`\n   * option. The parsed-document cache is derived and not encoded. Round-trips via\n   * {@link SpooledYamlArtifact.[DECODE_METHOD]}.\n   *\n   * @returns A snapshot consumed by {@link SpooledYamlArtifact.[DECODE_METHOD]}.\n   */\n  [ENCODE_METHOD](): AdkEncodableSnapshot {\n    return { reader: this.readerDescriptor(), multiDocument: this.#options?.multiDocument }\n  }\n\n  /**\n   * Reconstruct a {@link SpooledYamlArtifact} from a {@link SpooledYamlArtifact.[ENCODE_METHOD]}\n   * snapshot.\n   *\n   * @param data - The snapshot produced by {@link SpooledYamlArtifact.[ENCODE_METHOD]}.\n   * @returns A fresh {@link SpooledYamlArtifact}} backed by a freshly-resolved reader.\n   */\n  static [DECODE_METHOD](data: AdkEncodableSnapshot): SpooledYamlArtifact {\n    const snapshot = data as {\n      reader: ReaderDescriptor\n      multiDocument?: boolean\n    }\n    return new SpooledYamlArtifact(resolveSpoolReader(snapshot.reader), {\n      multiDocument: snapshot.multiDocument,\n    })\n  }\n}\n\n/**\n * Replacer for {@link JSON.stringify} that preserves non-finite numbers as YAML tokens.\n *\n * @remarks\n * YAML permits `.NaN`, `.inf`, and `-.inf`. {@link JSON.stringify} silently converts these\n * to `null`, losing the original value. This replacer renders non-finite numbers as their\n * YAML string representations so the information survives the JSON round-trip.\n *\n * @internal\n */\nfunction nonFiniteReplacer(_key: string, value: unknown): unknown {\n  if (typeof value === 'number' && !Number.isFinite(value)) {\n    return Number.isNaN(value) ? '.NaN' : value > 0 ? '.inf' : '-.inf'\n  }\n  return value\n}\n\n/**\n * Converter tool: YAML → JSON.\n *\n * @remarks\n * Takes either inline YAML text or a reference to a {@link SpooledYamlArtifact} produced\n * earlier in the turn. Converts to JSON and returns a fresh {@link SpooledJsonArtifact}\n * immediately queryable with JSON artifact tools.\n *\n * Non-finite numbers are preserved as their YAML token strings (`.NaN`, `.inf`, `-.inf`).\n * Undefined values are normalised to the JSON string `'null'`.\n */\nexport const yamlToJsonTool = new Tool({\n  name: 'yaml_to_json',\n  description:\n    'Convert a YAML document to JSON format. Returns a JSON artifact. Provide inline YAML text or a tool call id of a YAML artifact produced earlier in this turn — not both.',\n  inputSchema: validator.object({\n    text: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('Inline YAML text. Provide this or call_id, not both.'),\n    call_id: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('ToolCall id of a YAML artifact produced earlier in this turn.'),\n  }),\n  artifactConstructor: () => SpooledJsonArtifact,\n  handler: async (args, ctx) => {\n    const { text = '', call_id: callId = '' } = args as { text?: string; call_id?: string }\n    const hasText = text.trim().length > 0\n    const hasCallId = callId.trim().length > 0\n\n    if ((!hasText && !hasCallId) || (hasText && hasCallId)) {\n      return 'Error: provide either text or call_id, not both or neither'\n    }\n\n    let sourceDoc: unknown\n    let sourceId: string\n\n    if (hasCallId) {\n      const resolved = resolveArtifactById(ctx, callId, SpooledYamlArtifact)\n      if (!resolved) {\n        return `Error: no YAML artifact with id ${callId} in this turn`\n      }\n      const yaml = await getYaml()\n      try {\n        const yamlText = await resolved.artifact.asString()\n        const loaded = yaml.loadAll(yamlText)\n        sourceDoc = Array.isArray(loaded) ? (loaded.length === 1 ? loaded[0] : loaded) : loaded\n        sourceId = callId\n      } catch (err) {\n        let detail = isError(err) ? err.message : String(err)\n        if (isObject(err)) {\n          const mark = (err as Record<string, unknown>).mark as Record<string, unknown> | undefined\n          if (mark && (mark.line !== undefined || mark.column !== undefined)) {\n            const line = typeof mark.line === 'number' ? mark.line + 1 : 0\n            const col = typeof mark.column === 'number' ? mark.column + 1 : 0\n            detail += ` (line ${line}:${col})`\n          }\n        }\n        return `Error: Invalid YAML — ${detail}`\n      }\n    } else {\n      const yaml = await getYaml()\n      try {\n        const loaded = yaml.loadAll(text)\n        sourceDoc = Array.isArray(loaded) ? (loaded.length === 1 ? loaded[0] : loaded) : loaded\n        sourceId = uuidv6()\n      } catch (err) {\n        let detail = isError(err) ? err.message : String(err)\n        if (isObject(err)) {\n          const mark = (err as Record<string, unknown>).mark as Record<string, unknown> | undefined\n          if (mark && (mark.line !== undefined || mark.column !== undefined)) {\n            const line = typeof mark.line === 'number' ? mark.line + 1 : 0\n            const col = typeof mark.column === 'number' ? mark.column + 1 : 0\n            detail += ` (line ${line}:${col})`\n          }\n        }\n        return `Error: Invalid YAML — ${detail}`\n      }\n    }\n\n    // Normalise undefined to 'null' string\n    const jsonStr =\n      sourceDoc === undefined ? 'null' : JSON.stringify(sourceDoc, nonFiniteReplacer, 2)\n    const reader = await ctx.storeRetrievableBytes(`${ctx.id}:yaml_to_json:${sourceId}`, jsonStr)\n    return new SpooledJsonArtifact(reader)\n  },\n})\n\n/**\n * Converter tool: JSON → YAML.\n *\n * @remarks\n * Takes either inline JSON text or a reference to a {@link SpooledJsonArtifact} produced\n * earlier in the turn. Converts to YAML and returns a fresh {@link SpooledYamlArtifact}}\n * immediately queryable with YAML artifact tools.\n */\nexport const jsonToYamlTool = new Tool({\n  name: 'json_to_yaml',\n  description:\n    'Convert a JSON document to YAML format. Returns a YAML artifact. Provide inline JSON text or a tool call id of a JSON artifact produced earlier in this turn — not both.',\n  inputSchema: validator.object({\n    text: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('Inline JSON text. Provide this or call_id, not both.'),\n    call_id: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('ToolCall id of a JSON artifact produced earlier in this turn.'),\n  }),\n  artifactConstructor: () => SpooledYamlArtifact,\n  handler: async (args, ctx) => {\n    const { text = '', call_id: callId = '' } = args as { text?: string; call_id?: string }\n    const hasText = text.trim().length > 0\n    const hasCallId = callId.trim().length > 0\n\n    if ((!hasText && !hasCallId) || (hasText && hasCallId)) {\n      return 'Error: provide either text or call_id, not both or neither'\n    }\n\n    let sourceData: unknown\n    let sourceId: string\n\n    if (hasCallId) {\n      const resolved = resolveArtifactById(ctx, callId, SpooledJsonArtifact)\n      if (!resolved) {\n        return `Error: no JSON artifact with id ${callId} in this turn`\n      }\n      try {\n        const jsonText = await resolved.artifact.asString()\n        sourceData = JSON.parse(jsonText)\n        sourceId = callId\n      } catch (err) {\n        return `Error: Invalid JSON — ${isError(err) ? err.message : String(err)}`\n      }\n    } else {\n      try {\n        sourceData = JSON.parse(text)\n        sourceId = uuidv6()\n      } catch (err) {\n        return `Error: Invalid JSON — ${isError(err) ? err.message : String(err)}`\n      }\n    }\n\n    const yaml = await getYaml()\n    const yamlStr = yaml.dump(sourceData, { indent: 2 })\n    const reader = await ctx.storeRetrievableBytes(`${ctx.id}:json_to_yaml:${sourceId}`, yamlStr)\n    return new SpooledYamlArtifact(reader)\n  },\n})\n\n// Re-export exceptions\nexport { E_YAML_PARSE_ERROR, E_YAML_PEER_MISSING }\n"],"mappings":"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"}