{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../src/shared/settings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Chain behavior, template resolution, and directory management\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { AgentConfig } from \"../agents/agents.ts\";\nimport { normalizeSkillInput } from \"../agents/skills.ts\";\nimport {\n\ttype AcceptanceInput,\n\ttype AgentContract,\n\tCHAIN_RUNS_DIR,\n\ttype ChainGateLayer,\n\ttype JsonSchemaObject,\n\ttype OutputMode,\n\ttype ToolBudgetConfig,\n} from \"./types.ts\";\n\nconst CHAIN_DIR_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst INITIAL_PROGRESS_CONTENT = \"# Progress\\n\\n## Status\\nIn Progress\\n\\n## Tasks\\n\\n## Files Changed\\n\\n## Notes\\n\";\n\n// =============================================================================\n// Behavior Resolution Types\n// =============================================================================\n\nexport interface ResolvedStepBehavior {\n\toutput: string | false;\n\toutputMode: OutputMode;\n\treads: string[] | false;\n\tprogress: boolean;\n\tskills: string[] | false;\n\tmodel?: string;\n}\n\nexport interface StepOverrides {\n\toutput?: string | false;\n\toutputMode?: OutputMode;\n\treads?: string[] | false;\n\tprogress?: boolean;\n\tskills?: string[] | false;\n\tmodel?: string;\n}\n\nfunction normalizeOutputOverride(output: string | false | undefined): string | false | undefined {\n\treturn output === \"false\" ? false : output;\n}\n\n// =============================================================================\n// Chain Step Types\n// =============================================================================\n\n/** Sequential step: single agent execution */\nexport interface SequentialStep {\n\tagent: string;\n\ttask?: string;\n\tphase?: string;\n\tlabel?: string;\n\tas?: string;\n\toutputSchema?: JsonSchemaObject;\n\tcwd?: string;\n\toutput?: string | false;\n\toutputMode?: OutputMode;\n\treads?: string[] | false;\n\tprogress?: boolean;\n\tskill?: string | string[] | false;\n\tmodel?: string;\n\ttoolBudget?: ToolBudgetConfig;\n\tacceptance?: AcceptanceInput;\n\tagentContract?: AgentContract;\n\tgateOn?: ChainGateLayer;\n}\n\n/** Parallel task item within a parallel step */\nexport interface ParallelTaskItem {\n\tagent: string;\n\ttask?: string;\n\tphase?: string;\n\tlabel?: string;\n\tas?: string;\n\toutputSchema?: JsonSchemaObject;\n\tcwd?: string;\n\tcount?: number;\n\toutput?: string | false;\n\toutputMode?: OutputMode;\n\treads?: string[] | false;\n\tprogress?: boolean;\n\tskill?: string | string[] | false;\n\tmodel?: string;\n\ttoolBudget?: ToolBudgetConfig;\n\tacceptance?: AcceptanceInput;\n\tagentContract?: AgentContract;\n\tgateOn?: ChainGateLayer;\n}\n\nexport interface DynamicExpandSpec {\n\tfrom: {\n\t\toutput: string;\n\t\tpath: string;\n\t};\n\titem?: string;\n\tkey?: string;\n\tmaxItems?: number;\n\tonEmpty?: \"skip\" | \"fail\";\n}\n\nexport type DynamicParallelTemplate = Omit<ParallelTaskItem, \"as\" | \"count\">;\n\nexport interface DynamicCollectSpec {\n\tas: string;\n\toutputSchema?: JsonSchemaObject;\n}\n\nexport interface DynamicParallelStep {\n\texpand: DynamicExpandSpec;\n\tparallel: DynamicParallelTemplate;\n\tcollect: DynamicCollectSpec;\n\tconcurrency?: number;\n\tfailFast?: boolean;\n\tphase?: string;\n\tlabel?: string;\n\tacceptance?: AcceptanceInput;\n\tagentContract?: AgentContract;\n\tgateOn?: ChainGateLayer;\n}\n\n/** Approval checkpoint: pause before later chain steps until parent approval. */\nexport interface CheckpointStep {\n\tcheckpoint: string;\n\tmessage?: string;\n\tphase?: string;\n\tlabel?: string;\n\tagent?: string;\n\ttask?: string;\n\tas?: string;\n\toutput?: string | false;\n\toutputMode?: OutputMode;\n\treads?: string[] | false;\n\tprogress?: boolean;\n\tskill?: string | string[] | false;\n\tskills?: string[] | false;\n\tmodel?: string;\n}\n\n/** Parallel step: multiple agents running concurrently */\nexport interface ParallelStep {\n\tparallel: ParallelTaskItem[];\n\tconcurrency?: number;\n\tfailFast?: boolean;\n\tworktree?: boolean;\n\tcwd?: string;\n\tagentContract?: AgentContract;\n\tgateOn?: ChainGateLayer;\n}\n\n/** Union type for chain steps */\nexport type ChainStep = SequentialStep | ParallelStep | DynamicParallelStep | CheckpointStep;\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\nexport function isCheckpointStep(step: ChainStep): step is CheckpointStep {\n\treturn \"checkpoint\" in step;\n}\n\nexport function isParallelStep(step: ChainStep): step is ParallelStep {\n\treturn \"parallel\" in step && Array.isArray((step as ParallelStep).parallel);\n}\n\nexport function isDynamicParallelStep(step: ChainStep): step is DynamicParallelStep {\n\treturn (\n\t\t\"expand\" in step &&\n\t\t\"collect\" in step &&\n\t\t\"parallel\" in step &&\n\t\t!Array.isArray((step as { parallel?: unknown }).parallel)\n\t);\n}\n\n/** Get all agent names in a step (single for sequential, multiple for parallel) */\nexport function getStepAgents(step: ChainStep): string[] {\n\tif (isCheckpointStep(step)) {\n\t\treturn [];\n\t}\n\tif (isParallelStep(step)) {\n\t\treturn step.parallel.map((t) => t.agent);\n\t}\n\tif (isDynamicParallelStep(step)) {\n\t\treturn [step.parallel.agent];\n\t}\n\treturn [step.agent];\n}\n\n// =============================================================================\n// Chain Directory Management\n// =============================================================================\n\nexport function createChainDir(runId: string, baseDir?: string): string {\n\tconst chainDir = path.join(baseDir ? path.resolve(baseDir) : CHAIN_RUNS_DIR, runId);\n\tfs.mkdirSync(chainDir, { recursive: true });\n\treturn chainDir;\n}\n\nexport function removeChainDir(chainDir: string): void {\n\ttry {\n\t\tfs.rmSync(chainDir, { recursive: true });\n\t} catch {\n\t\t// Chain cleanup is best-effort. Runs can already have cleaned their temp dir.\n\t}\n}\n\nexport function cleanupOldChainDirs(): void {\n\tif (!fs.existsSync(CHAIN_RUNS_DIR)) return;\n\tconst now = Date.now();\n\tlet dirs: string[];\n\ttry {\n\t\tdirs = fs.readdirSync(CHAIN_RUNS_DIR);\n\t} catch {\n\t\t// Startup cleanup is best-effort. If the scoped temp root is unreadable,\n\t\t// skip cleanup instead of failing extension startup.\n\t\treturn;\n\t}\n\n\tfor (const dir of dirs) {\n\t\ttry {\n\t\t\tconst dirPath = path.join(CHAIN_RUNS_DIR, dir);\n\t\t\tconst stat = fs.statSync(dirPath);\n\t\t\tif (stat.isDirectory() && now - stat.mtimeMs > CHAIN_DIR_MAX_AGE_MS) {\n\t\t\t\tfs.rmSync(dirPath, { recursive: true });\n\t\t\t}\n\t\t} catch {\n\t\t\t// Skip directories that can't be processed; continue with others\n\t\t}\n\t}\n}\n\n// =============================================================================\n// Template Resolution\n// =============================================================================\n\n/** Resolved templates for a chain - string for sequential, string[] for parallel */\nexport type ResolvedTemplates = (string | string[])[];\n\n/**\n * Resolve templates for a chain with parallel step support.\n * Returns string for sequential steps, string[] for parallel steps.\n */\nexport function resolveChainTemplates(steps: ChainStep[]): ResolvedTemplates {\n\treturn steps.map((step, i) => {\n\t\tif (isCheckpointStep(step)) return \"\";\n\t\tif (isParallelStep(step)) {\n\t\t\t// Parallel step: resolve each task's template\n\t\t\treturn step.parallel.map((task) => {\n\t\t\t\tif (task.task) return task.task;\n\t\t\t\t// Default for parallel tasks is {previous}\n\t\t\t\treturn \"{previous}\";\n\t\t\t});\n\t\t}\n\t\tif (isDynamicParallelStep(step)) {\n\t\t\treturn step.parallel.task ?? \"{previous}\";\n\t\t}\n\t\t// Sequential step: existing logic\n\t\tconst seq = step as SequentialStep;\n\t\tif (seq.task) return seq.task;\n\t\t// Default: first step uses {task}, others use {previous}\n\t\treturn i === 0 ? \"{task}\" : \"{previous}\";\n\t});\n}\n\n// =============================================================================\n// Behavior Resolution\n// =============================================================================\n\n/**\n * Resolve effective chain behavior per step.\n * Priority: step override > agent frontmatter > false (disabled)\n */\nexport function resolveStepBehavior(\n\tagentConfig: AgentConfig,\n\tstepOverrides: StepOverrides,\n\tchainSkills?: string[],\n): ResolvedStepBehavior {\n\t// Output: step override > frontmatter > false (no output)\n\tconst stepOutput = normalizeOutputOverride(stepOverrides.output);\n\tconst output = stepOutput !== undefined ? stepOutput : (normalizeOutputOverride(agentConfig.output) ?? false);\n\n\t// Reads: step override > frontmatter defaultReads > false (no reads)\n\tconst reads = stepOverrides.reads !== undefined ? stepOverrides.reads : (agentConfig.defaultReads ?? false);\n\n\t// Progress: step override > frontmatter defaultProgress > false\n\tconst progress =\n\t\tstepOverrides.progress !== undefined ? stepOverrides.progress : (agentConfig.defaultProgress ?? false);\n\n\tlet skills: string[] | false;\n\tif (stepOverrides.skills === false) {\n\t\tskills = false;\n\t} else if (stepOverrides.skills !== undefined) {\n\t\tskills = [...stepOverrides.skills];\n\t\tif (chainSkills && chainSkills.length > 0) {\n\t\t\tskills = [...new Set([...skills, ...chainSkills])];\n\t\t}\n\t} else {\n\t\tskills = agentConfig.skills ? [...agentConfig.skills] : [];\n\t\tif (chainSkills && chainSkills.length > 0) {\n\t\t\tskills = [...new Set([...skills, ...chainSkills])];\n\t\t}\n\t}\n\n\tconst outputMode = stepOverrides.outputMode ?? \"inline\";\n\tconst model = stepOverrides.model ?? agentConfig.model;\n\treturn { output, outputMode, reads, progress, skills, model };\n}\n\nexport function resolveTaskTextForFileUpdatePolicy(\n\ttask: string | undefined,\n\toriginalTask?: string,\n): string | undefined {\n\tif (!task) return originalTask;\n\treturn originalTask ? task.replaceAll(\"{task}\", originalTask) : task;\n}\n\nexport function taskDisallowsFileUpdates(task: string | undefined): boolean {\n\tif (!task) return false;\n\treturn (\n\t\t/\\breview[- ]only\\b/i.test(task) ||\n\t\t/\\bread[- ]only\\s+(?:review|audit|inspection|pass)\\b/i.test(task) ||\n\t\t/\\b(?:no|without)\\s+(?:file\\s+)?edits?\\b/i.test(task) ||\n\t\t/\\b(?:do not|don't|must not)\\s+(?:edit|modify|write|touch)\\b/i.test(task) ||\n\t\t/\\bleave\\s+files?\\s+unchanged\\b/i.test(task)\n\t);\n}\n\nexport function suppressProgressForReadOnlyTask(\n\tbehavior: ResolvedStepBehavior,\n\ttask: string | undefined,\n\toriginalTask?: string,\n): ResolvedStepBehavior {\n\tconst policyTask = resolveTaskTextForFileUpdatePolicy(task, originalTask);\n\treturn behavior.progress && taskDisallowsFileUpdates(policyTask) ? { ...behavior, progress: false } : behavior;\n}\n\n// =============================================================================\n// Chain Instruction Injection\n// =============================================================================\n\n/**\n * Resolve a file path: absolute paths pass through, relative paths get chainDir prepended.\n */\nfunction resolveChainPath(filePath: string, chainDir: string): string {\n\treturn path.isAbsolute(filePath) ? filePath : path.join(chainDir, filePath);\n}\n\n/**\n * Build chain instructions from resolved behavior.\n * These are appended to the task to tell the agent what to read/write.\n */\nexport function writeInitialProgressFile(progressDir: string): void {\n\tfs.mkdirSync(progressDir, { recursive: true });\n\tfs.writeFileSync(path.join(progressDir, \"progress.md\"), INITIAL_PROGRESS_CONTENT);\n}\n\nexport function buildChainInstructions(\n\tbehavior: ResolvedStepBehavior,\n\tchainDir: string,\n\tisFirstProgressAgent: boolean,\n\tpreviousSummary?: string,\n): { prefix: string; suffix: string } {\n\tconst prefixParts: string[] = [];\n\tconst suffixParts: string[] = [];\n\n\t// READS - prepend to override any hardcoded filenames in task text\n\tif (behavior.reads && behavior.reads.length > 0) {\n\t\tconst files = behavior.reads.map((f) => resolveChainPath(f, chainDir));\n\t\tprefixParts.push(`[Read from: ${files.join(\", \")}]`);\n\t}\n\n\t// OUTPUT - prepend so agent knows where to write\n\tif (behavior.output) {\n\t\tconst outputPath = resolveChainPath(behavior.output, chainDir);\n\t\tprefixParts.push(`[Write to: ${outputPath}]`);\n\t}\n\n\t// Progress instructions in suffix (less critical)\n\tif (behavior.progress) {\n\t\tconst progressPath = path.join(chainDir, \"progress.md\");\n\t\tif (isFirstProgressAgent) {\n\t\t\tsuffixParts.push(`Create and maintain progress at: ${progressPath}`);\n\t\t} else {\n\t\t\tsuffixParts.push(`Update progress at: ${progressPath}`);\n\t\t}\n\t}\n\n\t// Include previous step's summary in suffix if available\n\tif (previousSummary?.trim()) {\n\t\tsuffixParts.push(`Previous step output:\\n${previousSummary.trim()}`);\n\t}\n\n\tconst prefix = prefixParts.length > 0 ? `${prefixParts.join(\"\\n\")}\\n\\n` : \"\";\n\n\tconst suffix = suffixParts.length > 0 ? `\\n\\n---\\n${suffixParts.join(\"\\n\")}` : \"\";\n\n\treturn { prefix, suffix };\n}\n\n// =============================================================================\n// Parallel Step Support\n// =============================================================================\n\n/**\n * Resolve behaviors for all tasks in a parallel step.\n * Creates namespaced output paths to avoid collisions.\n */\nexport function resolveParallelBehaviors(\n\ttasks: ParallelTaskItem[],\n\tagentConfigs: AgentConfig[],\n\tstepIndex: number,\n\tchainSkills?: string[],\n): ResolvedStepBehavior[] {\n\treturn tasks.map((task, taskIndex) => {\n\t\tconst config = agentConfigs.find((a) => a.name === task.agent);\n\t\tif (!config) {\n\t\t\tthrow new Error(`Unknown agent: ${task.agent}`);\n\t\t}\n\n\t\t// Build subdirectory path for this parallel task\n\t\tconst subdir = path.join(`parallel-${stepIndex}`, `${taskIndex}-${task.agent}`);\n\n\t\t// Output: task override > agent default (namespaced) > false\n\t\t// Absolute paths pass through unchanged; relative paths get namespaced under subdir\n\t\tlet output: string | false = false;\n\t\tconst taskOutput = normalizeOutputOverride(task.output);\n\t\tconst configOutput = normalizeOutputOverride(config.output);\n\t\tif (taskOutput !== undefined) {\n\t\t\tif (taskOutput === false) {\n\t\t\t\toutput = false;\n\t\t\t} else if (path.isAbsolute(taskOutput)) {\n\t\t\t\toutput = taskOutput; // Absolute path: use as-is\n\t\t\t} else {\n\t\t\t\toutput = path.join(subdir, taskOutput); // Relative: namespace under subdir\n\t\t\t}\n\t\t} else if (configOutput) {\n\t\t\t// Agent defaults are always relative, so namespace them\n\t\t\toutput = path.join(subdir, configOutput);\n\t\t}\n\n\t\t// Reads: task override > agent default > false\n\t\tconst reads = task.reads !== undefined ? task.reads : (config.defaultReads ?? false);\n\n\t\t// Progress: task override > agent default > false\n\t\tconst progress = task.progress !== undefined ? task.progress : (config.defaultProgress ?? false);\n\n\t\tconst taskSkillInput = normalizeSkillInput(task.skill);\n\t\tlet skills: string[] | false;\n\t\tif (taskSkillInput === false) {\n\t\t\tskills = false;\n\t\t} else if (taskSkillInput !== undefined) {\n\t\t\tskills = [...taskSkillInput];\n\t\t\tif (chainSkills && chainSkills.length > 0) {\n\t\t\t\tskills = [...new Set([...skills, ...chainSkills])];\n\t\t\t}\n\t\t} else {\n\t\t\tskills = config.skills ? [...config.skills] : [];\n\t\t\tif (chainSkills && chainSkills.length > 0) {\n\t\t\t\tskills = [...new Set([...skills, ...chainSkills])];\n\t\t\t}\n\t\t}\n\n\t\tconst outputMode = task.outputMode ?? \"inline\";\n\t\tconst model = task.model ?? config.model;\n\t\treturn { output, outputMode, reads, progress, skills, model };\n\t});\n}\n\n/**\n * Create subdirectories for parallel step outputs\n */\nexport function createParallelDirs(chainDir: string, stepIndex: number, taskCount: number, agentNames: string[]): void {\n\tfor (let i = 0; i < taskCount; i++) {\n\t\tconst subdir = path.join(chainDir, `parallel-${stepIndex}`, `${i}-${agentNames[i]}`);\n\t\tfs.mkdirSync(subdir, { recursive: true });\n\t}\n}\n\nexport type { ParallelTaskResult } from \"../runs/shared/parallel-utils.ts\";\nexport { aggregateParallelOutputs } from \"../runs/shared/parallel-utils.ts\";\n"]}