{"version":3,"file":"dynamic-fanout.d.ts","sourceRoot":"","sources":["../../../../src/runs/shared/dynamic-fanout.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DynamicParallelStep, ParallelTaskItem } from \"../../shared/settings.ts\";\nimport type { ArtifactPaths, ChainOutputMap, JsonSchemaObject, SingleResult } from \"../../shared/types.ts\";\nimport { getSingleResultOutput } from \"../../shared/utils.ts\";\nimport { validateStructuredOutputValue } from \"./structured-output.ts\";\n\nexport class DynamicFanoutError extends Error {}\n\nexport interface DynamicFanoutConfig {\n\tmaxItems?: number;\n\tallowRunnerFields?: boolean;\n}\n\nexport interface DynamicMaterializedItem {\n\tindex: number;\n\tkey: string;\n\tidKey: string;\n\titem: unknown;\n}\n\nexport interface DynamicCollectedResult {\n\tkey: string;\n\tindex: number;\n\titem: unknown;\n\tagent: string;\n\texitCode: number | null;\n\ttext: string;\n\tstructured?: unknown;\n\terror?: string;\n\ttimedOut?: boolean;\n\tstopped?: boolean;\n\toutputPath?: string;\n\tartifactPaths?: ArtifactPaths;\n}\n\nexport interface DynamicMaterializedGroup {\n\titems: DynamicMaterializedItem[];\n\tparallel: ParallelTaskItem[];\n\tcollectedOnEmpty?: DynamicCollectedResult[];\n}\n\nconst SAFE_OUTPUT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst ITEM_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst ITEM_REF_PATTERN = /\\{([A-Za-z_][A-Za-z0-9_]*)(?:\\.([^{}]+))?\\}/g;\nconst RESERVED_TEMPLATE_NAMES = new Set([\"task\", \"previous\", \"chain_dir\", \"outputs\"]);\nconst DYNAMIC_STEP_KEYS = new Set([\n\t\"expand\",\n\t\"parallel\",\n\t\"collect\",\n\t\"concurrency\",\n\t\"failFast\",\n\t\"phase\",\n\t\"label\",\n\t\"acceptance\",\n\t\"agentContract\",\n\t\"gateOn\",\n]);\nconst RUNNER_DYNAMIC_STEP_KEYS = new Set([\n\t...DYNAMIC_STEP_KEYS,\n\t\"effectiveAcceptance\",\n\t\"acceptanceInput\",\n\t\"acceptanceRole\",\n\t\"sessionFiles\",\n\t\"thinkingOverrides\",\n]);\nconst DYNAMIC_EXPAND_KEYS = new Set([\"from\", \"item\", \"key\", \"maxItems\", \"onEmpty\"]);\nconst DYNAMIC_EXPAND_FROM_KEYS = new Set([\"output\", \"path\"]);\nconst DYNAMIC_PARALLEL_KEYS = new Set([\n\t\"agent\",\n\t\"task\",\n\t\"phase\",\n\t\"label\",\n\t\"outputSchema\",\n\t\"cwd\",\n\t\"output\",\n\t\"outputMode\",\n\t\"reads\",\n\t\"progress\",\n\t\"skill\",\n\t\"model\",\n\t\"toolBudget\",\n\t\"acceptance\",\n\t\"agentContract\",\n\t\"gateOn\",\n]);\nconst RUNNER_DYNAMIC_PARALLEL_KEYS = new Set([\n\t...DYNAMIC_PARALLEL_KEYS,\n\t\"outputName\",\n\t\"structured\",\n\t\"inheritProjectContext\",\n\t\"inheritSkills\",\n\t\"skills\",\n\t\"outputPath\",\n\t\"namespaceOutputPath\",\n\t\"maxSubagentDepth\",\n\t\"waitToolEnabled\",\n\t\"structuredOutput\",\n\t\"structuredOutputSchema\",\n\t\"tools\",\n\t\"extensions\",\n\t\"subagentOnlyExtensions\",\n\t\"mcpDirectTools\",\n\t\"capabilityCeiling\",\n\t\"completionGuard\",\n\t\"systemPrompt\",\n\t\"systemPromptMode\",\n\t\"thinking\",\n\t\"modelCandidates\",\n\t\"sessionFile\",\n\t\"effectiveAcceptance\",\n\t\"acceptanceInput\",\n\t\"acceptanceRole\",\n\t\"parentSessionId\",\n\t\"launchResolvedExtensions\",\n]);\nconst DYNAMIC_COLLECT_KEYS = new Set([\"as\", \"outputSchema\"]);\n\nexport function isSafeOutputName(name: string): boolean {\n\treturn SAFE_OUTPUT_NAME_PATTERN.test(name);\n}\n\nexport function assertJsonPointer(pointer: string, label: string): void {\n\tif (pointer === \"\") return;\n\tif (!pointer.startsWith(\"/\")) {\n\t\tthrow new DynamicFanoutError(`${label} must be a JSON Pointer starting with '/'.`);\n\t}\n\tfor (const segment of pointer.slice(1).split(\"/\")) {\n\t\tif (/~(?![01])/.test(segment)) {\n\t\t\tthrow new DynamicFanoutError(`${label} contains invalid JSON Pointer escape.`);\n\t\t}\n\t}\n}\n\nfunction decodePointerSegment(segment: string): string {\n\treturn segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\nexport function resolveJsonPointer(value: unknown, pointer: string, label: string): unknown {\n\tassertJsonPointer(pointer, label);\n\tif (pointer === \"\") return value;\n\tlet current = value;\n\tfor (const rawSegment of pointer.slice(1).split(\"/\")) {\n\t\tconst segment = decodePointerSegment(rawSegment);\n\t\tif (Array.isArray(current)) {\n\t\t\tif (!/^(0|[1-9][0-9]*)$/.test(segment)) {\n\t\t\t\tthrow new DynamicFanoutError(`${label} segment '${segment}' does not address an array index.`);\n\t\t\t}\n\t\t\tconst index = Number(segment);\n\t\t\tif (index >= current.length) throw new DynamicFanoutError(`${label} does not exist.`);\n\t\t\tcurrent = current[index];\n\t\t\tcontinue;\n\t\t}\n\t\tif (!current || typeof current !== \"object\") {\n\t\t\tthrow new DynamicFanoutError(`${label} does not exist.`);\n\t\t}\n\t\tconst record = current as Record<string, unknown>;\n\t\tif (!Object.hasOwn(record, segment)) {\n\t\t\tthrow new DynamicFanoutError(`${label} does not exist.`);\n\t\t}\n\t\tcurrent = record[segment];\n\t}\n\treturn current;\n}\n\nfunction scalarToKey(value: unknown, label: string): string {\n\tif (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n\t\tconst key = String(value);\n\t\tif (!key.trim()) throw new DynamicFanoutError(`${label} resolved to an empty key.`);\n\t\tif (/[\\u0000-\\u001F\\u007F]/.test(key)) throw new DynamicFanoutError(`${label} resolved to an unsafe key.`);\n\t\tif (key.length > 200) throw new DynamicFanoutError(`${label} resolved to a key longer than 200 characters.`);\n\t\treturn key;\n\t}\n\tthrow new DynamicFanoutError(`${label} must resolve to a string, number, or boolean.`);\n}\n\nexport function normalizeItemKeyForId(key: string): string {\n\tconst normalized = key\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\")\n\t\t.slice(0, 80);\n\treturn normalized || \"item\";\n}\n\nfunction valueToTemplateText(value: unknown, reference: string): string {\n\tif (value === undefined) throw new DynamicFanoutError(`Unresolved item reference '${reference}'.`);\n\tif (typeof value === \"string\") return value;\n\tif (typeof value === \"number\" || typeof value === \"boolean\" || value === null) return String(value);\n\treturn JSON.stringify(value);\n}\n\nfunction resolveItemPath(item: unknown, pathText: string | undefined, reference: string): unknown {\n\tif (!pathText) return item;\n\tconst pointer = `/${pathText\n\t\t.split(\".\")\n\t\t.map((segment) => segment.replace(/~/g, \"~0\").replace(/\\//g, \"~1\"))\n\t\t.join(\"/\")}`;\n\treturn resolveJsonPointer(item, pointer, reference);\n}\n\nexport function resolveItemTemplate(template: string, itemName: string, item: unknown): string {\n\treturn template.replace(ITEM_REF_PATTERN, (raw, name: string, pathText: string | undefined) => {\n\t\tif (name !== itemName) return raw;\n\t\tif (pathText !== undefined && (!pathText.trim() || pathText.includes(\"..\"))) {\n\t\t\tthrow new DynamicFanoutError(`Invalid item reference '${raw}'.`);\n\t\t}\n\t\treturn valueToTemplateText(resolveItemPath(item, pathText, raw), raw);\n\t});\n}\n\nfunction assertOnlyKeys(value: unknown, allowed: Set<string>, label: string): void {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new DynamicFanoutError(`${label} must be an object.`);\n\tfor (const key of Object.keys(value)) {\n\t\tif (!allowed.has(key)) throw new DynamicFanoutError(`${label} does not support field '${key}'.`);\n\t}\n}\n\nexport function assertNoUnresolvedItemReferences(template: string, itemName: string, label: string): void {\n\tfor (const match of template.matchAll(/\\{([^{}]*)\\}/g)) {\n\t\tconst raw = match[0]!;\n\t\tconst reference = match[1]!;\n\t\tif (reference === itemName || reference.startsWith(`${itemName}.`)) {\n\t\t\tif (!ITEM_REF_PATTERN.test(raw) || reference === `${itemName}.` || reference.includes(\"..\")) {\n\t\t\t\tthrow new DynamicFanoutError(`Invalid item reference '${raw}' in ${label}.`);\n\t\t\t}\n\t\t\tITEM_REF_PATTERN.lastIndex = 0;\n\t\t\tcontinue;\n\t\t}\n\t\tITEM_REF_PATTERN.lastIndex = 0;\n\t\tconst name = reference.match(/^[A-Za-z_][A-Za-z0-9_]*/)?.[0];\n\t\tif (name === itemName) throw new DynamicFanoutError(`Invalid item reference '${raw}' in ${label}.`);\n\t\tif (name && RESERVED_TEMPLATE_NAMES.has(name)) continue;\n\t\tif (name) throw new DynamicFanoutError(`Unsupported template reference '${raw}' in ${label}.`);\n\t}\n\tITEM_REF_PATTERN.lastIndex = 0;\n\tif (template.includes(`{${itemName}.}`) || new RegExp(`\\\\{${itemName}(?:\\\\.|$)[^}]*$`).test(template)) {\n\t\tthrow new DynamicFanoutError(`Invalid item reference in ${label}.`);\n\t}\n}\n\nexport function hasDynamicFanoutFields(step: unknown): boolean {\n\treturn (\n\t\t!!step &&\n\t\ttypeof step === \"object\" &&\n\t\t!Array.isArray(step) &&\n\t\t(Object.hasOwn(step, \"expand\") || Object.hasOwn(step, \"collect\"))\n\t);\n}\n\nexport function validateDynamicStepShape(\n\tstep: DynamicParallelStep,\n\tstepIndex: number,\n\tconfig: DynamicFanoutConfig = {},\n): void {\n\tconst prefix = `Dynamic chain step ${stepIndex + 1}`;\n\tassertOnlyKeys(step, config.allowRunnerFields ? RUNNER_DYNAMIC_STEP_KEYS : DYNAMIC_STEP_KEYS, prefix);\n\tif (!step.expand || !step.expand.from) throw new DynamicFanoutError(`${prefix} requires expand.from.`);\n\tassertOnlyKeys(step.expand, DYNAMIC_EXPAND_KEYS, `${prefix} expand`);\n\tassertOnlyKeys(step.expand.from, DYNAMIC_EXPAND_FROM_KEYS, `${prefix} expand.from`);\n\tif (!isSafeOutputName(step.expand.from.output))\n\t\tthrow new DynamicFanoutError(`${prefix} has invalid expand.from.output '${step.expand.from.output}'.`);\n\tassertJsonPointer(step.expand.from.path, `${prefix} expand.from.path`);\n\tif (step.expand.key !== undefined) assertJsonPointer(step.expand.key, `${prefix} expand.key`);\n\tconst itemName = step.expand.item ?? \"item\";\n\tif (!ITEM_NAME_PATTERN.test(itemName))\n\t\tthrow new DynamicFanoutError(`${prefix} has invalid expand.item '${itemName}'.`);\n\tif (step.expand.maxItems === undefined && config.maxItems === undefined) {\n\t\tthrow new DynamicFanoutError(`${prefix} requires expand.maxItems or config.chain.dynamicFanout.maxItems.`);\n\t}\n\tif (step.expand.maxItems !== undefined && (!Number.isInteger(step.expand.maxItems) || step.expand.maxItems < 0)) {\n\t\tthrow new DynamicFanoutError(`${prefix} expand.maxItems must be an integer >= 0.`);\n\t}\n\tif (config.maxItems !== undefined && (!Number.isInteger(config.maxItems) || config.maxItems < 0)) {\n\t\tthrow new DynamicFanoutError(\"config.chain.dynamicFanout.maxItems must be an integer >= 0.\");\n\t}\n\tif (!step.parallel || Array.isArray(step.parallel))\n\t\tthrow new DynamicFanoutError(\n\t\t\t`${prefix} requires a single parallel template object and cannot mix dynamic expand/collect with static parallel arrays.`,\n\t\t);\n\tassertOnlyKeys(\n\t\tstep.parallel,\n\t\tconfig.allowRunnerFields ? RUNNER_DYNAMIC_PARALLEL_KEYS : DYNAMIC_PARALLEL_KEYS,\n\t\t`${prefix} parallel`,\n\t);\n\tif (\"expand\" in (step.parallel as object))\n\t\tthrow new DynamicFanoutError(`${prefix} does not support nested dynamic fanout.`);\n\tif (!step.parallel.agent) throw new DynamicFanoutError(`${prefix} parallel.agent is required.`);\n\tif (!step.collect?.as || !isSafeOutputName(step.collect.as))\n\t\tthrow new DynamicFanoutError(`${prefix} requires collect.as with a safe output name.`);\n\tassertOnlyKeys(step.collect, DYNAMIC_COLLECT_KEYS, `${prefix} collect`);\n\tfor (const [label, template] of [\n\t\t[\"parallel.task\", step.parallel.task],\n\t\t[\"parallel.label\", step.parallel.label],\n\t] as const) {\n\t\tif (template) assertNoUnresolvedItemReferences(template, itemName, `${prefix} ${label}`);\n\t}\n}\n\nexport function resolveDynamicFanoutItems(\n\tstep: DynamicParallelStep,\n\toutputs: ChainOutputMap,\n\tstepIndex: number,\n\tconfig: DynamicFanoutConfig = {},\n): DynamicMaterializedItem[] {\n\tvalidateDynamicStepShape(step, stepIndex, config);\n\tconst sourceName = step.expand.from.output;\n\tconst source = outputs[sourceName];\n\tif (!source)\n\t\tthrow new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} references unknown output '${sourceName}'.`);\n\tif (source.structured === undefined)\n\t\tthrow new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} requires structured output '${sourceName}'.`);\n\tconst value = resolveJsonPointer(\n\t\tsource.structured,\n\t\tstep.expand.from.path,\n\t\t`Dynamic chain step ${stepIndex + 1} expand.from.path`,\n\t);\n\tif (!Array.isArray(value))\n\t\tthrow new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} expand.from.path must resolve to an array.`);\n\tconst maxItems = step.expand.maxItems ?? config.maxItems;\n\tif (maxItems === undefined)\n\t\tthrow new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} requires an effective maxItems.`);\n\tif (value.length > maxItems)\n\t\tthrow new DynamicFanoutError(\n\t\t\t`Dynamic chain step ${stepIndex + 1} resolved ${value.length} items, exceeding maxItems ${maxItems}.`,\n\t\t);\n\tconst seen = new Set<string>();\n\tconst seenIds = new Set<string>();\n\treturn value.map((item, index) => {\n\t\tconst key =\n\t\t\tstep.expand.key === undefined\n\t\t\t\t? String(index)\n\t\t\t\t: scalarToKey(\n\t\t\t\t\t\tresolveJsonPointer(item, step.expand.key, `Dynamic chain step ${stepIndex + 1} expand.key`),\n\t\t\t\t\t\t`Dynamic chain step ${stepIndex + 1} expand.key`,\n\t\t\t\t\t);\n\t\tif (seen.has(key))\n\t\t\tthrow new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} produced duplicate item key '${key}'.`);\n\t\tseen.add(key);\n\t\tconst idKey = normalizeItemKeyForId(key);\n\t\tif (seenIds.has(idKey))\n\t\t\tthrow new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} produced colliding item id '${idKey}'.`);\n\t\tseenIds.add(idKey);\n\t\treturn { index, key, idKey, item };\n\t});\n}\n\nexport function materializeDynamicParallelStep(\n\tstep: DynamicParallelStep,\n\toutputs: ChainOutputMap,\n\tstepIndex: number,\n\tconfig: DynamicFanoutConfig = {},\n): DynamicMaterializedGroup {\n\tconst items = resolveDynamicFanoutItems(step, outputs, stepIndex, config);\n\tif (items.length === 0) {\n\t\tif ((step.expand.onEmpty ?? \"skip\") === \"fail\") {\n\t\t\tthrow new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} source array is empty.`);\n\t\t}\n\t\treturn { items, parallel: [], collectedOnEmpty: [] };\n\t}\n\tconst itemName = step.expand.item ?? \"item\";\n\tconst parallel = items.map((entry) => {\n\t\tconst task = resolveItemTemplate(step.parallel.task ?? \"{previous}\", itemName, entry.item);\n\t\tconst label = step.parallel.label ? resolveItemTemplate(step.parallel.label, itemName, entry.item) : undefined;\n\t\treturn {\n\t\t\t...step.parallel,\n\t\t\ttask,\n\t\t\t...(label !== undefined ? { label } : {}),\n\t\t};\n\t});\n\treturn { items, parallel };\n}\n\nexport function collectDynamicResults(\n\tstep: DynamicParallelStep,\n\titems: DynamicMaterializedItem[],\n\tresults: Array<\n\t\tPick<\n\t\t\tSingleResult,\n\t\t\t\"agent\" | \"error\" | \"timedOut\" | \"stopped\" | \"structuredOutput\" | \"artifactPaths\" | \"savedOutputPath\"\n\t\t> & { exitCode: number | null; output?: string; finalOutput?: string }\n\t>,\n): DynamicCollectedResult[] {\n\treturn items.map((entry, index) => {\n\t\tconst result = results[index];\n\t\tconst text = result\n\t\t\t? \"output\" in result && typeof result.output === \"string\"\n\t\t\t\t? result.output\n\t\t\t\t: getSingleResultOutput(result as SingleResult)\n\t\t\t: \"\";\n\t\treturn {\n\t\t\tkey: entry.key,\n\t\t\tindex: entry.index,\n\t\t\titem: entry.item,\n\t\t\tagent: result?.agent ?? step.parallel.agent,\n\t\t\texitCode: result?.exitCode ?? null,\n\t\t\ttext,\n\t\t\t...(result?.structuredOutput !== undefined ? { structured: result.structuredOutput } : {}),\n\t\t\t...(result?.error ? { error: result.error } : {}),\n\t\t\t...(result?.timedOut ? { timedOut: true } : {}),\n\t\t\t...(result?.stopped ? { stopped: true } : {}),\n\t\t\t...(result?.savedOutputPath ? { outputPath: result.savedOutputPath } : {}),\n\t\t\t...(result?.artifactPaths ? { artifactPaths: result.artifactPaths } : {}),\n\t\t};\n\t});\n}\n\nexport async function validateDynamicCollection(\n\tschema: JsonSchemaObject | undefined,\n\tvalue: DynamicCollectedResult[],\n): Promise<void> {\n\tif (!schema) return;\n\tconst validation = await validateStructuredOutputValue(schema, value);\n\tif (validation.status === \"invalid\") {\n\t\tthrow new DynamicFanoutError(`Collected output validation failed: ${validation.message}`);\n\t}\n}\n"]}