{"version":3,"file":"agent-refinements.d.ts","sourceRoot":"","sources":["../../../src/agents/agent-refinements.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { AgentToolResult } from \"@lpb-work/pi-agent-core\";\nimport { getProjectSubagentsDir } from \"../shared/artifacts.ts\";\nimport type { Details, JsonSchemaObject, SingleResult, SubagentState } from \"../shared/types.ts\";\nimport { type AgentConfig, discoverAgents, resolveAgentName } from \"./agents.ts\";\n\nconst REFINEMENT_FORMAT_VERSION = 1;\nconst REFINEMENT_DIR = \"refinements\";\nconst CURRENT_FENCE = \"pi-subagents-refinement-current\";\nconst SNAPSHOTS_FENCE = \"pi-subagents-refinement-snapshots-json\";\nconst MAX_EVIDENCE_ITEMS = 8;\nconst MAX_AGE_DAYS = 14;\nconst MAX_ITEM_BYTES = 2_048;\nconst MAX_PACKET_BYTES = 16_384;\nconst PROPOSAL_AGENT = \"reviewer\";\n\ntype RefinementAction = \"refine\" | \"refine.show\" | \"refine.rollback\";\ntype RefinementEvidenceSource = \"live-state\" | \"artifact-metadata\" | \"artifact-output\";\n\nexport interface RefinementEvidenceItem {\n\tid: string;\n\tsource: RefinementEvidenceSource;\n\trunId?: string;\n\tagent: string;\n\tat?: string;\n\tstatus?: string;\n\tmodel?: string;\n\tthinking?: string;\n\tacceptanceStatus?: string;\n\treviewFindings?: string[];\n\tresidualRisks?: string[];\n\terrors?: string[];\n\tcontrolSignals?: string[];\n\toutputTail?: string;\n\trefs: string[];\n}\n\nexport interface RefinementProposalEdit {\n\ttitle: string;\n\tguidance: string;\n\tevidenceIds: string[];\n\trationale: string;\n}\n\nexport interface RefinementProposal {\n\tsummary: string;\n\tedits: RefinementProposalEdit[];\n\trejectedIdeas?: string[];\n\tresidualRisks: string[];\n}\n\ninterface RefinementMetadata {\n\tagent: string;\n\trevision: number;\n\tupdatedAt: string;\n\tbase: {\n\t\tsource: AgentConfig[\"source\"];\n\t\tfilePath: string;\n\t\tsystemPromptSha256: string;\n\t};\n\tevidence: {\n\t\tmaxItems: number;\n\t\tmaxAgeDays: number;\n\t\titemBytes: number;\n\t\ttotalBytes: number;\n\t};\n}\n\ninterface RefinementSnapshot {\n\trevision: number;\n\tat: string;\n\taction: \"refine\" | \"rollback\";\n\tbefore: string;\n\tafter: string;\n\tevidenceIds: string[];\n\tproposalAgent?: string;\n}\n\ninterface ParsedRefinementFile {\n\tmetadata: RefinementMetadata;\n\tcurrent: string;\n\tsnapshots: RefinementSnapshot[];\n}\n\ninterface ExistingRefinementFile {\n\tpath: string;\n\tparsed?: ParsedRefinementFile;\n}\n\ninterface ProposalChildResult {\n\tisError?: boolean;\n\tcontent?: Array<{ type: string; text?: string }>;\n\tdetails?: {\n\t\tresults?: Array<Pick<SingleResult, \"structuredOutput\" | \"finalOutput\" | \"error\">>;\n\t};\n}\n\nexport type LaunchRefinementProposalChild = (\n\ttask: string,\n\toutputSchema: JsonSchemaObject,\n\tsignal: AbortSignal,\n) => Promise<ProposalChildResult>;\n\nexport interface RefinementActionContext {\n\tcwd: string;\n\tstate: SubagentState;\n\tsignal: AbortSignal;\n\tlaunchProposalChild: LaunchRefinementProposalChild;\n}\n\nfunction result(text: string, isError = false): AgentToolResult<Details> {\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\t...(isError ? { isError: true } : {}),\n\t\tdetails: { mode: \"management\", results: [] },\n\t};\n}\n\nfunction record(value: unknown): Record<string, unknown> | null {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: null;\n}\n\nfunction text(value: unknown): string | null {\n\treturn typeof value === \"string\" && value.trim().length > 0 ? value.trim() : null;\n}\n\nfunction textArray(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value.map(text).filter((entry): entry is string => entry !== null);\n}\n\nfunction isoTime(value: unknown): string | undefined {\n\tconst numberValue = typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n\tif (numberValue !== undefined) return new Date(numberValue).toISOString();\n\tconst stringValue = text(value);\n\tif (!stringValue) return undefined;\n\tconst parsed = Date.parse(stringValue);\n\treturn Number.isFinite(parsed) ? new Date(parsed).toISOString() : undefined;\n}\n\nfunction hashPrompt(systemPrompt: string): string {\n\treturn createHash(\"sha256\").update(systemPrompt).digest(\"hex\");\n}\n\nfunction safeAgentFileName(agentName: string): string {\n\tconst trimmed = agentName.trim();\n\tif (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed) || trimmed.includes(\"..\")) {\n\t\tthrow new Error(`Agent name '${agentName}' cannot be used as a refinement file name.`);\n\t}\n\treturn `${trimmed}.md`;\n}\n\nexport function getAgentRefinementPath(cwd: string, agentName: string): string {\n\tconst refinementRoot = path.join(getProjectSubagentsDir(cwd), REFINEMENT_DIR);\n\tconst resolvedRoot = path.resolve(refinementRoot);\n\tconst resolvedPath = path.resolve(refinementRoot, safeAgentFileName(agentName));\n\tif (resolvedPath !== path.join(resolvedRoot, path.basename(resolvedPath))) {\n\t\tthrow new Error(`Agent name '${agentName}' cannot be used as a refinement file name.`);\n\t}\n\treturn resolvedPath;\n}\n\nfunction readExisting(cwd: string, agentName: string): ExistingRefinementFile {\n\tconst filePath = getAgentRefinementPath(cwd, agentName);\n\tif (!fs.existsSync(filePath)) return { path: filePath };\n\treturn { path: filePath, parsed: parseRefinementFile(fs.readFileSync(filePath, \"utf-8\"), filePath) };\n}\n\nfunction extractFence(markdown: string, fence: string): string | null {\n\tconst escaped = fence.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\tconst match = markdown.match(new RegExp(`\\\\n\\`\\`\\`${escaped}\\\\n([\\\\s\\\\S]*?)\\\\n\\`\\`\\``, \"m\"));\n\treturn match?.[1] ?? null;\n}\n\nexport function parseRefinementFile(markdown: string, label = \"refinement file\"): ParsedRefinementFile {\n\tconst metadataMatch = markdown.match(/^<!-- pi-subagents-refinement:v1\\n([\\s\\S]*?)\\n-->\\n/);\n\tif (!metadataMatch?.[1]) throw new Error(`${label} is missing refinement metadata.`);\n\tconst metadataValue = JSON.parse(metadataMatch[1]) as unknown;\n\tconst metadataRecord = record(metadataValue);\n\tif (!metadataRecord) throw new Error(`${label} metadata must be an object.`);\n\tconst agent = text(metadataRecord.agent);\n\tconst revision = metadataRecord.revision;\n\tconst updatedAt = text(metadataRecord.updatedAt);\n\tconst base = record(metadataRecord.base);\n\tconst evidence = record(metadataRecord.evidence);\n\tif (!agent || typeof revision !== \"number\" || !Number.isInteger(revision) || !updatedAt || !base || !evidence) {\n\t\tthrow new Error(`${label} metadata is invalid.`);\n\t}\n\tconst source = text(base.source);\n\tconst filePath = text(base.filePath);\n\tconst systemPromptSha256 = text(base.systemPromptSha256);\n\tif (source !== \"builtin\" && source !== \"package\" && source !== \"user\" && source !== \"project\")\n\t\tthrow new Error(`${label} base source is invalid.`);\n\tif (!filePath || !systemPromptSha256) throw new Error(`${label} base metadata is invalid.`);\n\tconst current = extractFence(markdown, CURRENT_FENCE);\n\tif (current === null) throw new Error(`${label} is missing current refinement block.`);\n\tconst snapshotsRaw = extractFence(markdown, SNAPSHOTS_FENCE) ?? \"[]\";\n\tconst snapshotsValue = JSON.parse(snapshotsRaw) as unknown;\n\tif (!Array.isArray(snapshotsValue)) throw new Error(`${label} snapshots must be an array.`);\n\tconst snapshots = snapshotsValue.map((entry, index): RefinementSnapshot => {\n\t\tconst item = record(entry);\n\t\tif (!item) throw new Error(`${label} snapshot ${index} must be an object.`);\n\t\tconst action = text(item.action);\n\t\tconst before = typeof item.before === \"string\" ? item.before : null;\n\t\tconst after = typeof item.after === \"string\" ? item.after : null;\n\t\tconst at = text(item.at);\n\t\tif (\n\t\t\ttypeof item.revision !== \"number\" ||\n\t\t\t!Number.isInteger(item.revision) ||\n\t\t\t!at ||\n\t\t\t(action !== \"refine\" && action !== \"rollback\") ||\n\t\t\tbefore === null ||\n\t\t\tafter === null\n\t\t) {\n\t\t\tthrow new Error(`${label} snapshot ${index} is invalid.`);\n\t\t}\n\t\treturn {\n\t\t\trevision: item.revision,\n\t\t\tat,\n\t\t\taction,\n\t\t\tbefore,\n\t\t\tafter,\n\t\t\tevidenceIds: textArray(item.evidenceIds),\n\t\t\t...(text(item.proposalAgent) ? { proposalAgent: text(item.proposalAgent)! } : {}),\n\t\t};\n\t});\n\treturn {\n\t\tmetadata: {\n\t\t\tagent,\n\t\t\trevision,\n\t\t\tupdatedAt,\n\t\t\tbase: { source, filePath, systemPromptSha256 },\n\t\t\tevidence: {\n\t\t\t\tmaxItems: typeof evidence.maxItems === \"number\" ? evidence.maxItems : MAX_EVIDENCE_ITEMS,\n\t\t\t\tmaxAgeDays: typeof evidence.maxAgeDays === \"number\" ? evidence.maxAgeDays : MAX_AGE_DAYS,\n\t\t\t\titemBytes: typeof evidence.itemBytes === \"number\" ? evidence.itemBytes : MAX_ITEM_BYTES,\n\t\t\t\ttotalBytes: typeof evidence.totalBytes === \"number\" ? evidence.totalBytes : MAX_PACKET_BYTES,\n\t\t\t},\n\t\t},\n\t\tcurrent,\n\t\tsnapshots,\n\t};\n}\n\nfunction serializeRefinementFile(parsed: ParsedRefinementFile): string {\n\tconst metadata = JSON.stringify(parsed.metadata, null, 2);\n\tconst snapshots = JSON.stringify(parsed.snapshots, null, 2);\n\treturn [\n\t\t`<!-- pi-subagents-refinement:v${REFINEMENT_FORMAT_VERSION}`,\n\t\tmetadata,\n\t\t\"-->\",\n\t\t\"\",\n\t\t`# Current refinement for \\`${parsed.metadata.agent}\\``,\n\t\t\"\",\n\t\t`\\`\\`\\`${CURRENT_FENCE}`,\n\t\tparsed.current,\n\t\t\"```\",\n\t\t\"\",\n\t\t\"# Snapshots\",\n\t\t\"\",\n\t\t`\\`\\`\\`${SNAPSHOTS_FENCE}`,\n\t\tsnapshots,\n\t\t\"```\",\n\t\t\"\",\n\t].join(\"\\n\");\n}\n\nfunction writeRefinementFile(filePath: string, parsed: ParsedRefinementFile): void {\n\tfs.mkdirSync(path.dirname(filePath), { recursive: true });\n\tconst tmp = `${filePath}.${process.pid}.${randomUUID()}.tmp`;\n\tfs.writeFileSync(tmp, serializeRefinementFile(parsed), \"utf-8\");\n\tfs.renameSync(tmp, filePath);\n}\n\nfunction baseMetadata(agent: AgentConfig): RefinementMetadata[\"base\"] {\n\treturn {\n\t\tsource: agent.source,\n\t\tfilePath: agent.filePath,\n\t\tsystemPromptSha256: hashPrompt(agent.systemPrompt),\n\t};\n}\n\nfunction metadataFor(agent: AgentConfig, revision: number, now: string): RefinementMetadata {\n\treturn {\n\t\tagent: agent.name,\n\t\trevision,\n\t\tupdatedAt: now,\n\t\tbase: baseMetadata(agent),\n\t\tevidence: {\n\t\t\tmaxItems: MAX_EVIDENCE_ITEMS,\n\t\t\tmaxAgeDays: MAX_AGE_DAYS,\n\t\t\titemBytes: MAX_ITEM_BYTES,\n\t\t\ttotalBytes: MAX_PACKET_BYTES,\n\t\t},\n\t};\n}\n\nfunction withinAge(at: string | undefined, now = Date.now()): boolean {\n\tif (!at) return true;\n\tconst parsed = Date.parse(at);\n\treturn Number.isFinite(parsed) && now - parsed <= MAX_AGE_DAYS * 24 * 60 * 60 * 1_000;\n}\n\nfunction tailBytes(value: string, maxBytes: number): string {\n\tconst buffer = Buffer.from(value, \"utf-8\");\n\tif (buffer.byteLength <= maxBytes) return value;\n\treturn buffer.subarray(buffer.byteLength - maxBytes).toString(\"utf-8\");\n}\n\nfunction pushCapped(items: RefinementEvidenceItem[], item: RefinementEvidenceItem): void {\n\tif (items.length >= MAX_EVIDENCE_ITEMS) return;\n\titems.push({ ...item, outputTail: item.outputTail ? tailBytes(item.outputTail, MAX_ITEM_BYTES) : undefined });\n}\n\nfunction acceptanceFields(\n\tvalue: unknown,\n): Pick<RefinementEvidenceItem, \"acceptanceStatus\" | \"reviewFindings\" | \"residualRisks\"> {\n\tconst ledger = record(value);\n\tif (!ledger) return {};\n\tconst childReport = record(ledger.childReport);\n\tconst reviewResult = record(ledger.reviewResult);\n\tconst reviewFindings = [\n\t\t...textArray(childReport?.reviewFindings),\n\t\t...(Array.isArray(reviewResult?.findings)\n\t\t\t? reviewResult.findings\n\t\t\t\t\t.map((finding) => text(record(finding)?.issue))\n\t\t\t\t\t.filter((entry): entry is string => entry !== null)\n\t\t\t: []),\n\t];\n\tconst residualRisks = textArray(childReport?.residualRisks);\n\treturn {\n\t\t...(text(ledger.status) ? { acceptanceStatus: text(ledger.status)! } : {}),\n\t\t...(reviewFindings.length > 0 ? { reviewFindings } : {}),\n\t\t...(residualRisks.length > 0 ? { residualRisks } : {}),\n\t};\n}\n\nfunction controlSignals(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value\n\t\t.map((entry) => {\n\t\t\tconst item = record(entry);\n\t\t\tif (!item) return null;\n\t\t\tconst message = text(item.message);\n\t\t\tconst reason = text(item.reason);\n\t\t\treturn message ? (reason ? `${reason}: ${message}` : message) : null;\n\t\t})\n\t\t.filter((entry): entry is string => entry !== null);\n}\n\nfunction evidencePacket(items: RefinementEvidenceItem[]): RefinementEvidenceItem[] {\n\tconst packet: RefinementEvidenceItem[] = [];\n\tlet bytes = 2;\n\tfor (const item of items) {\n\t\tconst encoded = Buffer.byteLength(JSON.stringify(item), \"utf-8\") + 1;\n\t\tif (bytes + encoded > MAX_PACKET_BYTES) break;\n\t\tpacket.push(item);\n\t\tbytes += encoded;\n\t}\n\treturn packet;\n}\n\nexport function collectBoundedRefinementEvidence(\n\tcwd: string,\n\tagentName: string,\n\tstate: SubagentState,\n): RefinementEvidenceItem[] {\n\tconst items: RefinementEvidenceItem[] = [];\n\tconst jobs = [...state.asyncJobs.values()]\n\t\t.filter((job) => !job.cwd || path.resolve(job.cwd) === path.resolve(cwd))\n\t\t.filter((job) => job.agents?.includes(agentName) || job.steps?.some((step) => step.agent === agentName))\n\t\t.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));\n\tfor (const job of jobs) {\n\t\tconst at = isoTime(job.updatedAt ?? job.startedAt);\n\t\tif (!withinAge(at)) continue;\n\t\tconst matchingSteps = job.steps?.filter((step) => step.agent === agentName) ?? [];\n\t\tif (matchingSteps.length === 0) {\n\t\t\tpushCapped(items, {\n\t\t\t\tid: `live:${job.asyncId}`,\n\t\t\t\tsource: \"live-state\",\n\t\t\t\trunId: job.asyncId,\n\t\t\t\tagent: agentName,\n\t\t\t\t...(at ? { at } : {}),\n\t\t\t\tstatus: job.status,\n\t\t\t\trefs: [job.asyncDir],\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const [index, step] of matchingSteps.entries()) {\n\t\t\tconst stepRecord = step as unknown as Record<string, unknown>;\n\t\t\tpushCapped(items, {\n\t\t\t\tid: `live:${job.asyncId}:${index}`,\n\t\t\t\tsource: \"live-state\",\n\t\t\t\trunId: job.asyncId,\n\t\t\t\tagent: agentName,\n\t\t\t\t...(at ? { at } : {}),\n\t\t\t\tstatus: step.status,\n\t\t\t\t...(text(stepRecord.model) ? { model: text(stepRecord.model)! } : {}),\n\t\t\t\t...(text(stepRecord.thinking) ? { thinking: text(stepRecord.thinking)! } : {}),\n\t\t\t\t...acceptanceFields(stepRecord.acceptance),\n\t\t\t\t...(text(step.error) ? { errors: [text(step.error)!] } : {}),\n\t\t\t\t...(text(stepRecord.recentOutput) ? { outputTail: text(stepRecord.recentOutput)! } : {}),\n\t\t\t\trefs: [job.asyncDir],\n\t\t\t});\n\t\t}\n\t}\n\n\tconst artifactsDir = path.join(getProjectSubagentsDir(cwd), \"artifacts\");\n\tif (fs.existsSync(artifactsDir)) {\n\t\tconst files = fs\n\t\t\t.readdirSync(artifactsDir)\n\t\t\t.filter((file) => file.endsWith(\"_meta.json\"))\n\t\t\t.map((file) => path.join(artifactsDir, file))\n\t\t\t.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);\n\t\tfor (const file of files) {\n\t\t\tif (items.length >= MAX_EVIDENCE_ITEMS) break;\n\t\t\tlet metadata: Record<string, unknown> | null = null;\n\t\t\ttry {\n\t\t\t\tmetadata = record(JSON.parse(fs.readFileSync(file, \"utf-8\")) as unknown);\n\t\t\t} catch {\n\t\t\t\tmetadata = null;\n\t\t\t}\n\t\t\tif (!metadata || text(metadata.agent) !== agentName) continue;\n\t\t\tconst stat = fs.statSync(file);\n\t\t\tconst at = isoTime(metadata.timestamp ?? stat.mtimeMs);\n\t\t\tif (!withinAge(at)) continue;\n\t\t\tconst outputPath = file.replace(/_meta\\.json$/, \"_output.md\");\n\t\t\tconst outputTail = fs.existsSync(outputPath)\n\t\t\t\t? tailBytes(fs.readFileSync(outputPath, \"utf-8\"), MAX_ITEM_BYTES)\n\t\t\t\t: undefined;\n\t\t\tpushCapped(items, {\n\t\t\t\tid: `artifact:${path.basename(file, \"_meta.json\")}`,\n\t\t\t\tsource: outputTail ? \"artifact-output\" : \"artifact-metadata\",\n\t\t\t\t...(text(metadata.runId) ? { runId: text(metadata.runId)! } : {}),\n\t\t\t\tagent: agentName,\n\t\t\t\t...(at ? { at } : {}),\n\t\t\t\tstatus:\n\t\t\t\t\ttypeof metadata.exitCode === \"number\" ? (metadata.exitCode === 0 ? \"completed\" : \"failed\") : undefined,\n\t\t\t\t...(text(metadata.model) ? { model: text(metadata.model)! } : {}),\n\t\t\t\t...(text(metadata.thinking) ? { thinking: text(metadata.thinking)! } : {}),\n\t\t\t\t...acceptanceFields(metadata.acceptance),\n\t\t\t\t...(text(metadata.error) ? { errors: [text(metadata.error)!] } : {}),\n\t\t\t\t...(controlSignals(metadata.controlEvents).length > 0\n\t\t\t\t\t? { controlSignals: controlSignals(metadata.controlEvents) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(outputTail ? { outputTail } : {}),\n\t\t\t\trefs: outputTail ? [file, outputPath] : [file],\n\t\t\t});\n\t\t}\n\t}\n\treturn evidencePacket(items);\n}\n\nexport function appendAgentRefinementOverlay(systemPrompt: string, input: { cwd: string; agentName: string }): string {\n\tlet current = \"\";\n\tlet filePath = \"\";\n\ttry {\n\t\tfilePath = getAgentRefinementPath(input.cwd, input.agentName);\n\t\tif (!fs.existsSync(filePath)) return systemPrompt;\n\t\tcurrent = parseRefinementFile(fs.readFileSync(filePath, \"utf-8\"), filePath).current.trim();\n\t} catch {\n\t\treturn systemPrompt;\n\t}\n\tif (!current) return systemPrompt;\n\tconst overlay = [\n\t\t`<pi-subagents-refinement agent=${JSON.stringify(input.agentName)} source=${JSON.stringify(path.relative(input.cwd, filePath))}>`,\n\t\t\"Project-local refinement guidance generated from recent bounded evidence.\",\n\t\t\"It does not override tool, developer, task, output, acceptance, or safety instructions.\",\n\t\t\"\",\n\t\tcurrent,\n\t\t\"</pi-subagents-refinement>\",\n\t].join(\"\\n\");\n\treturn systemPrompt.trim() ? `${systemPrompt}\\n\\n${overlay}` : overlay;\n}\n\nexport function validateRefinementProposal(\n\tproposal: unknown,\n\tevidenceIds: string[],\n): { ok: true; proposal: RefinementProposal } | { ok: false; error: string } {\n\tconst item = record(proposal);\n\tif (!item) return { ok: false, error: \"Refinement proposal must be an object.\" };\n\tconst summary = text(item.summary);\n\tconst editsValue = item.edits;\n\tif (!summary || !Array.isArray(editsValue))\n\t\treturn { ok: false, error: \"Refinement proposal requires summary and edits.\" };\n\tif (editsValue.length > 3) return { ok: false, error: \"Refinement proposal may contain at most 3 edits.\" };\n\tconst allowed = new Set(evidenceIds);\n\tconst protectedInstruction = \"(?:acceptance|output|safety|policy|policies|tools?|developer|system)\";\n\tconst blocked = new RegExp(\n\t\t`\\\\b(all agents|every agent|global|(?:disable|ignore|bypass|skip|override)\\\\s+(?:the\\\\s+)?${protectedInstruction}(?:\\\\s+instructions?)?|${protectedInstruction}\\\\s+(?:instructions?|overrides?)|tool safety|review gates|rewrite base|base agent file|settings\\\\.json|\\\\.pi/agent|agents/.*\\\\.md)\\\\b`,\n\t\t\"i\",\n\t);\n\tconst edits: RefinementProposalEdit[] = [];\n\tfor (const [index, editValue] of editsValue.entries()) {\n\t\tconst edit = record(editValue);\n\t\tif (!edit) return { ok: false, error: `Refinement proposal edit ${index} must be an object.` };\n\t\tconst title = text(edit.title);\n\t\tconst guidance = text(edit.guidance);\n\t\tconst rationale = text(edit.rationale);\n\t\tconst cited = textArray(edit.evidenceIds);\n\t\tif (!title || !guidance || !rationale)\n\t\t\treturn { ok: false, error: `Refinement proposal edit ${index} is missing title, guidance, or rationale.` };\n\t\tif (cited.length === 0 || cited.some((id) => !allowed.has(id)))\n\t\t\treturn { ok: false, error: `Refinement proposal edit ${index} must cite known evidence ids.` };\n\t\tif (guidance.includes(\"```\") || guidance.includes(\"</pi-subagents-refinement>\") || blocked.test(guidance))\n\t\t\treturn { ok: false, error: `Refinement proposal edit ${index} contains disallowed guidance.` };\n\t\tedits.push({ title, guidance, rationale, evidenceIds: cited });\n\t}\n\treturn {\n\t\tok: true,\n\t\tproposal: {\n\t\t\tsummary,\n\t\t\tedits,\n\t\t\trejectedIdeas: textArray(item.rejectedIdeas),\n\t\t\tresidualRisks: textArray(item.residualRisks),\n\t\t},\n\t};\n}\n\nfunction proposalSchema(): JsonSchemaObject {\n\treturn {\n\t\ttype: \"object\",\n\t\tadditionalProperties: false,\n\t\trequired: [\"summary\", \"edits\", \"residualRisks\"],\n\t\tproperties: {\n\t\t\tsummary: { type: \"string\" },\n\t\t\tedits: {\n\t\t\t\ttype: \"array\",\n\t\t\t\tmaxItems: 3,\n\t\t\t\titems: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\trequired: [\"title\", \"guidance\", \"evidenceIds\", \"rationale\"],\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\ttitle: { type: \"string\" },\n\t\t\t\t\t\tguidance: { type: \"string\" },\n\t\t\t\t\t\tevidenceIds: { type: \"array\", items: { type: \"string\" }, minItems: 1 },\n\t\t\t\t\t\trationale: { type: \"string\" },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\trejectedIdeas: { type: \"array\", items: { type: \"string\" } },\n\t\t\tresidualRisks: { type: \"array\", items: { type: \"string\" } },\n\t\t},\n\t};\n}\n\nfunction proposalFromChild(child: ProposalChildResult): unknown {\n\tfor (const entry of child.details?.results ?? []) {\n\t\tif (entry.structuredOutput !== undefined) return entry.structuredOutput;\n\t}\n\tconst output =\n\t\tchild.details?.results?.map((entry) => entry.finalOutput ?? \"\").find((entry) => entry.trim().length > 0) ??\n\t\tchild.content?.map((entry) => entry.text ?? \"\").find((entry) => entry.trim().length > 0) ??\n\t\t\"\";\n\tconst jsonMatch = output.match(/```(?:json)?\\n([\\s\\S]*?)\\n```/) ?? output.match(/({[\\s\\S]*})/);\n\tif (!jsonMatch?.[1]) return null;\n\ttry {\n\t\treturn JSON.parse(jsonMatch[1]) as unknown;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction proposalTask(agent: AgentConfig, current: string, evidence: RefinementEvidenceItem[]): string {\n\treturn [\n\t\t`You are a fresh read-only proposal child for project-local refinement of one subagent: ${agent.name}.`,\n\t\t\"Do not read files. Do not use write, edit, or shell tools. Use only the evidence packet below.\",\n\t\t\"Propose at most 2-3 minimal overlay guidance edits.\",\n\t\t\"Each edit must cite one or more evidence ids from the packet.\",\n\t\t\"Do not propose base agent file, settings, global, automatic, tool, safety, output, acceptance, developer, or system instruction changes.\",\n\t\t\"Return only structured output that matches the schema.\",\n\t\t\"\",\n\t\t\"Agent metadata:\",\n\t\tJSON.stringify(\n\t\t\t{\n\t\t\t\tname: agent.name,\n\t\t\t\tsource: agent.source,\n\t\t\t\tfilePath: agent.filePath,\n\t\t\t\tbasePromptSha256: hashPrompt(agent.systemPrompt),\n\t\t\t},\n\t\t\tnull,\n\t\t\t2,\n\t\t),\n\t\t\"\",\n\t\t\"Current overlay:\",\n\t\tcurrent.trim() || \"(none)\",\n\t\t\"\",\n\t\t\"Evidence packet:\",\n\t\tJSON.stringify(evidence, null, 2),\n\t].join(\"\\n\");\n}\n\nfunction guidanceFromProposal(proposal: RefinementProposal): string {\n\treturn proposal.edits.map((edit) => `- ${edit.guidance.trim()}`).join(\"\\n\");\n}\n\nfunction resolveOneAgent(\n\tcwd: string,\n\tagentName: string,\n): { ok: true; agent: AgentConfig } | { ok: false; error: string } {\n\tconst agents = discoverAgents(cwd, \"both\").agents;\n\tconst resolved = resolveAgentName(agentName, agents);\n\tif (resolved.error) return { ok: false, error: resolved.error };\n\tif (!resolved.agent) return { ok: false, error: `Unknown agent: ${agentName}` };\n\treturn { ok: true, agent: resolved.agent };\n}\n\nexport async function handleRefinementAction(\n\taction: RefinementAction,\n\tparams: { agent?: string },\n\tctx: RefinementActionContext,\n): Promise<AgentToolResult<Details>> {\n\tconst requestedAgent = params.agent?.trim();\n\tif (!requestedAgent)\n\t\treturn result(\n\t\t\t`${action} requires agent. Use /subagents-refine <agent> or subagent({ action: \"${action}\", agent: \"<agent>\" }).`,\n\t\t\ttrue,\n\t\t);\n\tlet resolved: { ok: true; agent: AgentConfig } | { ok: false; error: string };\n\ttry {\n\t\tresolved = resolveOneAgent(ctx.cwd, requestedAgent);\n\t} catch (error) {\n\t\treturn result(error instanceof Error ? error.message : String(error), true);\n\t}\n\tif (!resolved.ok) return result(resolved.error, true);\n\tconst agent = resolved.agent;\n\tlet existing: ExistingRefinementFile;\n\ttry {\n\t\texisting = readExisting(ctx.cwd, agent.name);\n\t} catch (error) {\n\t\treturn result(error instanceof Error ? error.message : String(error), true);\n\t}\n\n\tif (action === \"refine.show\") {\n\t\tif (!existing.parsed) return result(`No refinement overlay exists for '${agent.name}'.`);\n\t\tconst currentDigest = hashPrompt(agent.systemPrompt);\n\t\tconst drift = existing.parsed.metadata.base.systemPromptSha256 === currentDigest ? \"no\" : \"yes\";\n\t\tconst history =\n\t\t\texisting.parsed.snapshots\n\t\t\t\t.slice(-5)\n\t\t\t\t.map(\n\t\t\t\t\t(snapshot) =>\n\t\t\t\t\t\t`- r${snapshot.revision} ${snapshot.action} at ${snapshot.at} (${snapshot.evidenceIds.length} evidence id${snapshot.evidenceIds.length === 1 ? \"\" : \"s\"})`,\n\t\t\t\t)\n\t\t\t\t.join(\"\\n\") || \"- none\";\n\t\treturn result(\n\t\t\t[\n\t\t\t\t`Refinement overlay for '${agent.name}'`,\n\t\t\t\t`Path: ${existing.path}`,\n\t\t\t\t`Revision: ${existing.parsed.metadata.revision}`,\n\t\t\t\t`Updated: ${existing.parsed.metadata.updatedAt}`,\n\t\t\t\t`Base: ${existing.parsed.metadata.base.source} ${existing.parsed.metadata.base.filePath}`,\n\t\t\t\t`Base prompt changed since overlay: ${drift}`,\n\t\t\t\t\"\",\n\t\t\t\t\"Current guidance:\",\n\t\t\t\texisting.parsed.current.trim() || \"(empty)\",\n\t\t\t\t\"\",\n\t\t\t\t\"Recent history:\",\n\t\t\t\thistory,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\tif (action === \"refine.rollback\") {\n\t\tif (!existing.parsed) return result(`No refinement overlay exists for '${agent.name}'.`, true);\n\t\tconst latest = existing.parsed.snapshots.at(-1);\n\t\tif (!latest) return result(`No refinement snapshot exists for '${agent.name}'.`, true);\n\t\tconst now = new Date().toISOString();\n\t\tconst nextRevision = existing.parsed.metadata.revision + 1;\n\t\tconst next: ParsedRefinementFile = {\n\t\t\tmetadata: metadataFor(agent, nextRevision, now),\n\t\t\tcurrent: latest.before,\n\t\t\tsnapshots: [\n\t\t\t\t...existing.parsed.snapshots,\n\t\t\t\t{\n\t\t\t\t\trevision: nextRevision,\n\t\t\t\t\tat: now,\n\t\t\t\t\taction: \"rollback\",\n\t\t\t\t\tbefore: existing.parsed.current,\n\t\t\t\t\tafter: latest.before,\n\t\t\t\t\tevidenceIds: latest.evidenceIds,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\ttry {\n\t\t\twriteRefinementFile(existing.path, next);\n\t\t} catch (error) {\n\t\t\treturn result(error instanceof Error ? error.message : String(error), true);\n\t\t}\n\t\treturn result(\n\t\t\t`Rolled back refinement overlay for '${agent.name}' to revision ${nextRevision}.\\nPath: ${existing.path}`,\n\t\t);\n\t}\n\n\tconst evidence = collectBoundedRefinementEvidence(ctx.cwd, agent.name, ctx.state);\n\tif (evidence.length === 0)\n\t\treturn result(\n\t\t\t`No bounded recent evidence was found for '${agent.name}'. No proposal child was launched and no overlay was written.`,\n\t\t);\n\tconst current = existing.parsed?.current ?? \"\";\n\tconst child = await ctx.launchProposalChild(proposalTask(agent, current, evidence), proposalSchema(), ctx.signal);\n\tif (child.isError) return result(`Refinement proposal child failed. No overlay was written.`, true);\n\tconst validation = validateRefinementProposal(\n\t\tproposalFromChild(child),\n\t\tevidence.map((item) => item.id),\n\t);\n\tif (!validation.ok) return result(`${validation.error} No overlay was written.`, true);\n\tif (validation.proposal.edits.length === 0)\n\t\treturn result(`The proposal child returned no edits for '${agent.name}'. No overlay was written.`);\n\tconst now = new Date().toISOString();\n\tconst nextRevision = (existing.parsed?.metadata.revision ?? 0) + 1;\n\tconst after = guidanceFromProposal(validation.proposal);\n\tconst snapshot: RefinementSnapshot = {\n\t\trevision: nextRevision,\n\t\tat: now,\n\t\taction: \"refine\",\n\t\tbefore: current,\n\t\tafter,\n\t\tevidenceIds: [...new Set(validation.proposal.edits.flatMap((edit) => edit.evidenceIds))],\n\t\tproposalAgent: PROPOSAL_AGENT,\n\t};\n\tconst next: ParsedRefinementFile = {\n\t\tmetadata: metadataFor(agent, nextRevision, now),\n\t\tcurrent: after,\n\t\tsnapshots: [...(existing.parsed?.snapshots ?? []), snapshot],\n\t};\n\ttry {\n\t\twriteRefinementFile(existing.path, next);\n\t} catch (error) {\n\t\treturn result(error instanceof Error ? error.message : String(error), true);\n\t}\n\treturn result(\n\t\t[\n\t\t\t`Wrote refinement overlay for '${agent.name}' at revision ${nextRevision}.`,\n\t\t\t`Path: ${existing.path}`,\n\t\t\t`Evidence items: ${evidence.length}`,\n\t\t\t`Edits: ${validation.proposal.edits.length}`,\n\t\t].join(\"\\n\"),\n\t);\n}\n"]}