@tangle-network/agent-runtime 0.71.1 → 0.73.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 (66) hide show
  1. package/README.md +8 -8
  2. package/dist/agent.d.ts +27 -17
  3. package/dist/agent.js +21 -9
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/analyst-loop.js +3 -2
  7. package/dist/{chunk-4KGQHS7U.js → chunk-7ODB76J5.js} +2 -18
  8. package/dist/chunk-7ODB76J5.js.map +1 -0
  9. package/dist/chunk-HPYWEFVY.js +4039 -0
  10. package/dist/chunk-HPYWEFVY.js.map +1 -0
  11. package/dist/{chunk-P5OKDSLB.js → chunk-IODKUOBA.js} +5 -3
  12. package/dist/{chunk-P5OKDSLB.js.map → chunk-IODKUOBA.js.map} +1 -1
  13. package/dist/chunk-NBV35BR6.js +68 -0
  14. package/dist/chunk-NBV35BR6.js.map +1 -0
  15. package/dist/{chunk-5ISW5JUF.js → chunk-NCH4XUZ7.js} +2004 -6551
  16. package/dist/chunk-NCH4XUZ7.js.map +1 -0
  17. package/dist/chunk-PBE35ULD.js +52 -0
  18. package/dist/chunk-PBE35ULD.js.map +1 -0
  19. package/dist/{chunk-VLF5RHEQ.js → chunk-T2HVQVB4.js} +1 -66
  20. package/dist/chunk-T2HVQVB4.js.map +1 -0
  21. package/dist/{chunk-K3RM4MPM.js → chunk-U56XGKVY.js} +9 -24
  22. package/dist/chunk-U56XGKVY.js.map +1 -0
  23. package/dist/chunk-UZ5SODU7.js +29 -0
  24. package/dist/chunk-UZ5SODU7.js.map +1 -0
  25. package/dist/{delegates-CsXJPZDH.d.ts → coordination-DU0saWeg.d.ts} +875 -1099
  26. package/dist/index.d.ts +7 -8
  27. package/dist/index.js +10 -12
  28. package/dist/index.js.map +1 -1
  29. package/dist/intelligence.d.ts +1 -1
  30. package/dist/lifecycle.d.ts +281 -0
  31. package/dist/lifecycle.js +174 -0
  32. package/dist/lifecycle.js.map +1 -0
  33. package/dist/{loop-runner-bin-DLM_bVQO.d.ts → loop-runner-bin-eD3m0rHW.d.ts} +10 -27
  34. package/dist/loop-runner-bin.d.ts +5 -6
  35. package/dist/loop-runner-bin.js +6 -6
  36. package/dist/loops.d.ts +47 -10
  37. package/dist/loops.js +47 -42
  38. package/dist/mcp/bin.js +17 -329
  39. package/dist/mcp/bin.js.map +1 -1
  40. package/dist/mcp/index.d.ts +10 -190
  41. package/dist/mcp/index.js +949 -47
  42. package/dist/mcp/index.js.map +1 -1
  43. package/dist/{openai-tools-kdCS-T12.d.ts → openai-tools-CBurv8Cu.d.ts} +11 -13
  44. package/dist/otel-export-BKmNwiCb.d.ts +180 -0
  45. package/dist/profiles.d.ts +3 -3
  46. package/dist/profiles.js +1 -5
  47. package/dist/profiles.js.map +1 -1
  48. package/dist/{substrate-BoRXgvka.d.ts → substrate-rNj6TDc3.d.ts} +7 -23
  49. package/dist/{types-CdnEAE3U.d.ts → types-JufmXF2a.d.ts} +80 -1
  50. package/dist/worktree-DaxOvw-C.d.ts +702 -0
  51. package/dist/{worktree-fanout-CK2ypmEm.d.ts → worktree-fanout-DIffZohV.d.ts} +15 -5
  52. package/package.json +6 -1
  53. package/dist/chunk-4KGQHS7U.js.map +0 -1
  54. package/dist/chunk-5ISW5JUF.js.map +0 -1
  55. package/dist/chunk-74UAWZXE.js +0 -278
  56. package/dist/chunk-74UAWZXE.js.map +0 -1
  57. package/dist/chunk-DVQGYDN5.js +0 -59
  58. package/dist/chunk-DVQGYDN5.js.map +0 -1
  59. package/dist/chunk-INXDNX2W.js +0 -475
  60. package/dist/chunk-INXDNX2W.js.map +0 -1
  61. package/dist/chunk-K3RM4MPM.js.map +0 -1
  62. package/dist/chunk-VLF5RHEQ.js.map +0 -1
  63. package/dist/chunk-XRYEZPR6.js +0 -88
  64. package/dist/chunk-XRYEZPR6.js.map +0 -1
  65. package/dist/coordination-BPQmuwv8.d.ts +0 -666
  66. package/dist/delegation-profile-Bvfro2m1.d.ts +0 -99
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/audit/issue-writer.ts","../src/profiles/ui-auditor/slugify.ts","../src/profiles/ui-auditor/prompt.ts","../src/profiles/ui-auditor/in-process-client.ts","../src/profiles/ui-auditor/lens-prompts.ts","../src/profiles/ui-auditor/output-adapter.ts","../src/profiles/ui-auditor/validator.ts","../src/profiles/ui-auditor/profile.ts"],"sourcesContent":["/**\n * @experimental\n *\n * UI-audit issue writer — pure I/O. Takes a workspace dir + `UiFinding[]`\n * and emits:\n * - `<workspace>/issues/NNN--<lens>--<slug>.md` — one self-contained\n * GitHub-issue-ready Markdown per finding, with embedded screenshot\n * references.\n * - `<workspace>/registry.json` — finding index for dedup and audit\n * resume across iterations.\n * - `<workspace>/index.md` — human-readable rollup (severity / lens /\n * route counts plus a sorted finding list).\n *\n * The writer is deterministic, idempotent for `appendFindings()`, and\n * never invokes an LLM. It assigns the next monotonic id to a finding the\n * caller did not pre-id.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { slugify } from '../profiles/ui-auditor/slugify'\nimport {\n UI_FINDING_SEVERITIES,\n UI_LENSES,\n type UiFinding,\n type UiLens,\n} from '../profiles/ui-auditor/substrate'\n\n/** @experimental */\nexport interface AuditRegistry {\n schemaVersion: 1\n findings: UiFinding[]\n /** Route → URL + captures sidecar; preserved across runs. */\n routes: Record<string, { url?: string; captures: AuditRegistryCapture[] }>\n}\n\n/** @experimental */\nexport interface AuditRegistryCapture {\n file: string\n viewport?: string\n fullPage?: boolean\n elementSelector?: string\n capturedAt: string\n}\n\nconst SEVERITY_ORDER: Record<UiFinding['severity'], number> = {\n critical: 0,\n high: 1,\n med: 2,\n low: 3,\n}\n\n// Validate workspaceDir at every public entry point. The MCP tool checks\n// the same shape at the wire boundary, but the writer is independently\n// exported via `@tangle-network/agent-runtime/audit`, so direct callers\n// would otherwise bypass that defense. Absolute + no `..` segments matches\n// the MCP tool's contract — any path that the writer joins for I/O must be\n// rooted at a deterministic location chosen by the caller.\nfunction assertValidWorkspaceDir(dir: string): void {\n if (typeof dir !== 'string' || dir.length === 0) {\n throw new Error(\n `audit-writer: workspaceDir must be a non-empty string (got ${JSON.stringify(dir)})`,\n )\n }\n if (!path.isAbsolute(dir)) {\n throw new Error(\n `audit-writer: workspaceDir must be an absolute path (got ${JSON.stringify(dir)})`,\n )\n }\n if (dir.split(path.sep).includes('..')) {\n throw new Error(\n `audit-writer: workspaceDir must not contain '..' segments (got ${JSON.stringify(dir)})`,\n )\n }\n}\n\n/** @experimental */\nexport async function initAuditWorkspace(workspaceDir: string): Promise<void> {\n assertValidWorkspaceDir(workspaceDir)\n await fs.mkdir(path.join(workspaceDir, 'issues'), { recursive: true })\n await fs.mkdir(path.join(workspaceDir, 'screenshots'), { recursive: true })\n const regPath = path.join(workspaceDir, 'registry.json')\n try {\n await fs.access(regPath)\n } catch {\n const fresh: AuditRegistry = { schemaVersion: 1, findings: [], routes: {} }\n await fs.writeFile(regPath, JSON.stringify(fresh, null, 2))\n }\n}\n\n/** @experimental */\nexport async function readAuditRegistry(workspaceDir: string): Promise<AuditRegistry> {\n assertValidWorkspaceDir(workspaceDir)\n const regPath = path.join(workspaceDir, 'registry.json')\n const text = await fs.readFile(regPath, 'utf8')\n const parsed = JSON.parse(text) as unknown\n if (!parsed || typeof parsed !== 'object') {\n throw new Error(`audit-writer: registry.json at ${regPath} is not a JSON object`)\n }\n const reg = parsed as Partial<AuditRegistry>\n if (reg.schemaVersion !== 1 || !Array.isArray(reg.findings) || typeof reg.routes !== 'object') {\n throw new Error(`audit-writer: registry.json at ${regPath} has an unrecognized shape`)\n }\n return reg as AuditRegistry\n}\n\nasync function writeAuditRegistry(workspaceDir: string, reg: AuditRegistry): Promise<void> {\n const regPath = path.join(workspaceDir, 'registry.json')\n // Write to a sibling temp file and rename. POSIX rename is atomic, so a\n // reader at any instant sees either the pre-write OR post-write registry,\n // never a half-written file. Prevents corruption on crash mid-write.\n const tmpPath = `${regPath}.tmp-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n await fs.writeFile(tmpPath, JSON.stringify(reg, null, 2))\n await fs.rename(tmpPath, regPath)\n}\n\n// Per-workspace serialization. The writer reads registry.json, mutates it\n// in memory, then writes it back; concurrent calls would race on the\n// monotonic-id assignment and clobber each other's findings. Each\n// workspace key holds the Promise of the most-recently-enqueued operation;\n// new operations chain off it so they run strictly serially.\nconst workspaceLocks = new Map<string, Promise<unknown>>()\n\nasync function withWorkspaceLock<T>(workspaceDir: string, fn: () => Promise<T>): Promise<T> {\n const key = path.resolve(workspaceDir)\n const prev = workspaceLocks.get(key) ?? Promise.resolve()\n let signalDone!: () => void\n const done = new Promise<void>((resolve) => {\n signalDone = resolve\n })\n workspaceLocks.set(key, done)\n try {\n await prev\n return await fn()\n } finally {\n signalDone()\n // Only delete if no later caller has overwritten the slot — preserves\n // the chain for any concurrent callers still waiting.\n if (workspaceLocks.get(key) === done) workspaceLocks.delete(key)\n }\n}\n\nfunction assertFindingShape(f: UiFinding, index: number): void {\n const where = `audit-writer: findings[${index}]`\n if (!UI_LENSES.includes(f.lens)) {\n throw new Error(\n `${where}: invalid lens ${JSON.stringify(f.lens)}; one of ${UI_LENSES.join('|')}`,\n )\n }\n if (!UI_FINDING_SEVERITIES.includes(f.severity)) {\n throw new Error(\n `${where}: invalid severity ${JSON.stringify(f.severity)}; one of ${UI_FINDING_SEVERITIES.join('|')}`,\n )\n }\n for (const field of ['title', 'route', 'observation', 'impact', 'suggestedFix'] as const) {\n const v = f[field]\n if (typeof v !== 'string' || v.trim().length === 0) {\n throw new Error(`${where}: ${field} must be a non-empty string`)\n }\n }\n if (!Array.isArray(f.screenshots) || f.screenshots.length === 0) {\n throw new Error(`${where}: screenshots must be a non-empty array`)\n }\n for (let i = 0; i < f.screenshots.length; i += 1) {\n const s = f.screenshots[i]\n if (!s || typeof s.path !== 'string' || s.path.length === 0) {\n throw new Error(`${where}.screenshots[${i}].path must be a non-empty string`)\n }\n // Defense-in-depth: screenshot paths are written verbatim into Markdown\n // and the registry; a downstream consumer that joins workspaceDir with\n // them would otherwise be exposed to traversal-read. Reject `..`\n // segments at the validation boundary.\n if (s.path.split(/[/\\\\]/).includes('..')) {\n throw new Error(`${where}.screenshots[${i}].path must not contain '..' segments`)\n }\n }\n}\n\nfunction nextFindingId(reg: AuditRegistry): number {\n let max = 0\n for (const f of reg.findings) {\n if (typeof f.id === 'number' && f.id > max) max = f.id\n }\n return max + 1\n}\n\nfunction slugifyTitle(title: string): string {\n return slugify(title, 'title')\n}\n\nfunction renderFinding(finding: UiFinding): string {\n if (!finding.id) {\n throw new Error('audit-writer: cannot render a finding without an assigned id')\n }\n if (finding.screenshots.length === 0) {\n throw new Error(`audit-writer: finding #${finding.id} has no screenshots`)\n }\n const id = finding.id\n const numStr = String(id).padStart(3, '0')\n\n const metaLines: string[] = [\n `> **Issue #${numStr}** · **Severity:** ${finding.severity} · **Lens:** ${finding.lens}`,\n `> **Route:** \\`${finding.route}\\`${finding.url ? ` · **URL:** ${finding.url}` : ''}`,\n ]\n if (finding.viewport) metaLines.push(`> **Viewport:** ${finding.viewport}`)\n if (finding.selector) metaLines.push(`> **Selector:** \\`${finding.selector}\\``)\n if (finding.tags && finding.tags.length > 0) {\n metaLines.push(`> **Tags:** ${finding.tags.map((t) => `\\`${t}\\``).join(', ')}`)\n }\n if (finding.similarTo && finding.similarTo.length > 0) {\n metaLines.push(\n `> **Possible duplicates:** ${finding.similarTo.map((n) => `#${String(n).padStart(3, '0')}`).join(', ')}`,\n )\n }\n metaLines.push(`> _Generated: ${finding.createdAt ?? new Date().toISOString()}_`)\n\n const reproSteps =\n finding.reproSteps ??\n `1. Open \\`${finding.route}\\`${finding.url ? ` (${finding.url})` : ''}\\n2. Observe the area shown in the screenshot${finding.selector ? ` (selector: \\`${finding.selector}\\`)` : ''}.`\n\n // Screenshot paths are workspace-relative; the issue file sits in\n // `<workspace>/issues/`, so we render `../<path>`.\n const evidence = finding.screenshots\n .map((s) => `![${path.basename(s.path)}](../${s.path})`)\n .join('\\n\\n')\n\n return [\n `# [UI] ${finding.title}`,\n '',\n metaLines.join('\\n'),\n '',\n '## Observation',\n finding.observation.trim(),\n '',\n '## Why it matters',\n finding.impact.trim(),\n '',\n '## Steps to reproduce',\n reproSteps,\n '',\n '## Suggested fix',\n finding.suggestedFix.trim(),\n '',\n '## Evidence',\n evidence,\n '',\n '---',\n `<sub>Generated by agent-runtime/ui-auditor · lens=\\`${finding.lens}\\` · severity=\\`${finding.severity}\\`</sub>`,\n '',\n ].join('\\n')\n}\n\n/** @experimental */\nexport interface AppendFindingsResult {\n /** Findings with id + createdAt assigned, in input order. */\n written: UiFinding[]\n /** Workspace-relative path to each issue Markdown file, in input order. */\n files: string[]\n}\n\n/**\n * Append findings to a workspace, writing one Markdown file per finding\n * and updating registry.json. Assigns monotonically increasing ids to\n * findings that arrived without one.\n *\n * Findings already carrying an id that collides with the registry are\n * rejected — callers must either freshly mint findings (id undefined) or\n * use a separate update path. This protects against accidental overwrite.\n *\n * @experimental\n */\nexport async function appendFindings(\n workspaceDir: string,\n findings: readonly UiFinding[],\n): Promise<AppendFindingsResult> {\n assertValidWorkspaceDir(workspaceDir)\n // Validate every finding BEFORE acquiring the lock so callers see a fast\n // input-shape error without blocking concurrent writers. Validation is\n // the only defense against path traversal via crafted `lens` values\n // (the filename interpolates lens directly) and against malformed\n // Markdown from missing required fields — TypeScript types are erased\n // at runtime, so this boundary cannot trust its callers.\n for (let i = 0; i < findings.length; i += 1) {\n const f = findings[i]\n if (!f) throw new Error(`audit-writer: findings[${i}] is undefined`)\n assertFindingShape(f, i)\n }\n\n return withWorkspaceLock(workspaceDir, async () => {\n await initAuditWorkspace(workspaceDir)\n const reg = await readAuditRegistry(workspaceDir)\n const usedIds = new Set<number>()\n for (const f of reg.findings) {\n if (typeof f.id === 'number') usedIds.add(f.id)\n }\n\n const written: UiFinding[] = []\n const files: string[] = []\n let nextId = nextFindingId(reg)\n\n for (const incoming of findings) {\n let id = incoming.id\n if (id !== undefined) {\n if (usedIds.has(id)) {\n throw new Error(\n `audit-writer: incoming finding id ${id} (title=${JSON.stringify(incoming.title)}) collides with an existing registry entry`,\n )\n }\n } else {\n id = nextId\n while (usedIds.has(id)) id += 1\n nextId = id + 1\n }\n usedIds.add(id)\n\n const createdAt = incoming.createdAt ?? new Date().toISOString()\n const persisted: UiFinding = { ...incoming, id, createdAt }\n\n const slug = slugifyTitle(persisted.title)\n const fileName = `${String(id).padStart(3, '0')}--${persisted.lens}--${slug}.md`\n const filePathAbs = path.join(workspaceDir, 'issues', fileName)\n const filePathRel = `issues/${fileName}`\n\n await fs.writeFile(filePathAbs, renderFinding(persisted))\n reg.findings.push(persisted)\n written.push(persisted)\n files.push(filePathRel)\n }\n\n await writeAuditRegistry(workspaceDir, reg)\n return { written, files }\n })\n}\n\n/** @experimental */\nexport interface RegisterCapturesOptions {\n route: string\n url?: string\n captures: readonly AuditRegistryCapture[]\n}\n\n/**\n * Record screenshots taken for a route in the registry, without filing a\n * finding. Useful when the auditor wants to remember which captures\n * exist for resume / dedup purposes.\n *\n * @experimental\n */\nexport async function registerCaptures(\n workspaceDir: string,\n options: RegisterCapturesOptions,\n): Promise<void> {\n assertValidWorkspaceDir(workspaceDir)\n if (typeof options.route !== 'string' || options.route.trim().length === 0) {\n throw new Error('audit-writer: registerCaptures: route must be a non-empty string')\n }\n return withWorkspaceLock(workspaceDir, async () => {\n await initAuditWorkspace(workspaceDir)\n const reg = await readAuditRegistry(workspaceDir)\n const slot = reg.routes[options.route] ?? { captures: [] }\n if (options.url) slot.url = options.url\n slot.captures = [...slot.captures, ...options.captures]\n reg.routes[options.route] = slot\n await writeAuditRegistry(workspaceDir, reg)\n })\n}\n\n/** @experimental */\nexport interface AuditIndex {\n /** Total findings in the workspace. */\n total: number\n bySeverity: Record<UiFinding['severity'], number>\n byLens: Partial<Record<UiLens, number>>\n byRoute: Record<string, number>\n}\n\n/** @experimental */\nexport function summarizeRegistry(reg: AuditRegistry): AuditIndex {\n const bySeverity: Record<UiFinding['severity'], number> = {\n critical: 0,\n high: 0,\n med: 0,\n low: 0,\n }\n const byLens: Partial<Record<UiLens, number>> = {}\n const byRoute: Record<string, number> = {}\n for (const f of reg.findings) {\n bySeverity[f.severity] += 1\n byLens[f.lens] = (byLens[f.lens] ?? 0) + 1\n byRoute[f.route] = (byRoute[f.route] ?? 0) + 1\n }\n return { total: reg.findings.length, bySeverity, byLens, byRoute }\n}\n\n/**\n * Regenerate `<workspace>/index.md` from registry.json.\n *\n * @experimental\n */\nexport async function writeAuditIndex(workspaceDir: string): Promise<string> {\n assertValidWorkspaceDir(workspaceDir)\n const reg = await readAuditRegistry(workspaceDir)\n const summary = summarizeRegistry(reg)\n\n const sorted = [...reg.findings].sort((a, b) => {\n const sa = SEVERITY_ORDER[a.severity]\n const sb = SEVERITY_ORDER[b.severity]\n if (sa !== sb) return sa - sb\n return (a.id ?? 0) - (b.id ?? 0)\n })\n\n const lines: string[] = []\n lines.push('# UI audit index', '')\n lines.push(`_Generated: ${new Date().toISOString()}_`, '')\n lines.push(`**Total findings:** ${summary.total}`, '')\n lines.push('## By severity')\n for (const s of ['critical', 'high', 'med', 'low'] as const) {\n const n = summary.bySeverity[s] ?? 0\n if (n > 0) lines.push(`- **${s}** — ${n}`)\n }\n lines.push('', '## By lens')\n const lensEntries: [string, number][] = Object.entries(summary.byLens).map(([k, v]) => [\n k,\n v ?? 0,\n ])\n for (const [lens, n] of lensEntries.sort((a, b) => b[1] - a[1])) {\n lines.push(`- **${lens}** — ${n}`)\n }\n lines.push('', '## By route')\n const routeEntries: [string, number][] = Object.entries(summary.byRoute).map(([k, v]) => [\n k,\n v ?? 0,\n ])\n for (const [route, n] of routeEntries.sort((a, b) => b[1] - a[1])) {\n lines.push(`- \\`${route}\\` — ${n}`)\n }\n lines.push('', '## Findings', '')\n for (const f of sorted) {\n if (typeof f.id !== 'number') continue\n const num = String(f.id).padStart(3, '0')\n const slug = slugifyTitle(f.title)\n const file = `issues/${num}--${f.lens}--${slug}.md`\n lines.push(`- [#${num} \\`${f.severity}\\` \\`${f.lens}\\` \\`${f.route}\\` — ${f.title}](${file})`)\n }\n lines.push('')\n\n const out = path.join(workspaceDir, 'index.md')\n const body = lines.join('\\n')\n await fs.writeFile(out, body)\n return out\n}\n","/**\n * @experimental\n *\n * Shared slug helper used by both the in-process auditor client (for\n * screenshot filenames) and the issue writer (for issue Markdown\n * filenames). Lowercases, normalizes, strips non-alphanumeric, trims\n * dashes, caps at 80 chars. Throws when the result is empty so callers\n * never silently write to a name-less filename.\n */\n\n/** @experimental */\nexport function slugify(value: string, fieldName: string): string {\n const slug = value\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80)\n if (slug.length === 0) {\n throw new Error(\n `ui-auditor: ${fieldName} slugified to empty string. Provide a non-empty value (got ${JSON.stringify(value)}).`,\n )\n }\n return slug\n}\n","/**\n * @experimental\n *\n * Prompt formatter for the auditor profile. `formatAuditorPrompt` produces\n * the user message handed to the iteration — describes the captures to be\n * taken and the lens to apply. The system prompt comes from\n * `buildAuditorSystemPrompt(lens)` (lens-prompts.ts).\n *\n * The formatter prepends a machine-readable envelope (`<<UI_AUDIT_TASK>>`\n * … `<<UI_AUDIT_TASK_END>>`) carrying a JSON-serialised task. The\n * in-process auditor client recovers the task from this envelope so the\n * iteration is self-describing — robust to concurrent fanout, where any\n * per-client side state (e.g. a \"current task\" register) would race.\n *\n * The formatter is pure and deterministic — re-run on the same task\n * produces the same prompt. Tests and trace replays rely on this.\n */\n\nimport type { UiAuditTask } from './task'\n\nconst ENVELOPE_BEGIN = '<<UI_AUDIT_TASK>>'\nconst ENVELOPE_END = '<<UI_AUDIT_TASK_END>>'\n\n/** @experimental */\nexport function encodeAuditTaskEnvelope(task: UiAuditTask): string {\n return `${ENVELOPE_BEGIN}${JSON.stringify(task)}${ENVELOPE_END}`\n}\n\n/**\n * Parse a task envelope back out of a prompt string. Returns undefined if\n * the prompt does not contain a complete envelope OR if the payload is\n * not valid JSON.\n *\n * @experimental\n */\nexport function decodeAuditTaskEnvelope(prompt: string): UiAuditTask | undefined {\n const start = prompt.indexOf(ENVELOPE_BEGIN)\n if (start === -1) return undefined\n const payloadStart = start + ENVELOPE_BEGIN.length\n const end = prompt.indexOf(ENVELOPE_END, payloadStart)\n if (end === -1) return undefined\n const payload = prompt.slice(payloadStart, end)\n try {\n const parsed = JSON.parse(payload) as unknown\n if (!parsed || typeof parsed !== 'object') return undefined\n const t = parsed as Partial<UiAuditTask>\n if (typeof t.lens !== 'string' || !Array.isArray(t.captures)) return undefined\n return t as UiAuditTask\n } catch {\n return undefined\n }\n}\n\n/** @experimental */\nexport function formatAuditorPrompt(task: UiAuditTask): string {\n const lines: string[] = []\n lines.push(`# UI audit iteration — lens: ${task.lens}`)\n lines.push('')\n if (task.productContext && task.productContext.trim().length > 0) {\n lines.push('## Product context')\n lines.push(task.productContext.trim())\n lines.push('')\n }\n lines.push('## Captures to take')\n task.captures.forEach((cap, i) => {\n const vp = cap.viewport ? `${cap.viewport.width}x${cap.viewport.height}` : '1280x800 (default)'\n const detail = [\n `viewport=${vp}`,\n cap.fullPage ? 'fullPage=true' : null,\n cap.elementSelector ? `selector=\\`${cap.elementSelector}\\`` : null,\n cap.waitFor ? `waitFor=\\`${cap.waitFor}\\`` : null,\n cap.waitMs !== undefined ? `waitMs=${cap.waitMs}` : null,\n cap.label ? `label=${cap.label}` : null,\n ]\n .filter((s): s is string => s !== null)\n .join(' · ')\n lines.push(`${i + 1}. route=\\`${cap.route}\\` url=${cap.url} ${detail ? `(${detail})` : ''}`)\n })\n lines.push('')\n if (task.knownFindingIds && task.knownFindingIds.length > 0) {\n lines.push('## Known findings (link via similarTo, do not refile)')\n lines.push(task.knownFindingIds.map((n) => `#${String(n).padStart(3, '0')}`).join(', '))\n lines.push('')\n }\n lines.push('## Output format')\n lines.push(\n 'Emit a single JSON object with the shape `{ findings: UiFinding[], notes?: string }` where every finding has the fields enumerated in your system prompt. The screenshots field on each finding must reference the captures above by path. Do not emit findings outside the lens.',\n )\n return lines.join('\\n')\n}\n","/**\n * @experimental\n *\n * `createInProcessUiAuditClient` — a `SandboxClient` that drives a\n * Playwright browser in-process and delegates finding identification to a\n * consumer-supplied {@link UiJudge}.\n *\n * Why this exists: `runLoop` is built around a sandbox-SDK seam — each\n * iteration is `client.create() → box.streamPrompt() → box.delete()`.\n * For UI audit, spinning up a real container running a coding harness\n * per iteration is overkill: the work is one browser capture + one\n * vision LLM call. This client satisfies the kernel contract while\n * doing the audit in-process; no container, no sandbox-SDK backend.\n *\n * The client owns ONE browser for its lifetime and creates a fresh\n * context per iteration (isolated cookies/storage). Playwright is\n * dynamically imported so consumers who use a different `SandboxClient`\n * — e.g. a fleet executor that drives Playwright remotely — do not pay\n * the peer dep cost.\n *\n * Concurrency: each iteration's prompt carries a self-describing task\n * envelope (see `prompt.ts`), so concurrent fanout iterations do not race\n * over per-client side state.\n */\n\nimport type { CreateSandboxOptions, SandboxEvent, SandboxInstance } from '@tangle-network/sandbox'\nimport type { SandboxClient } from '../../runtime/types'\nimport type { UiJudge } from './judge'\nimport { decodeAuditTaskEnvelope } from './prompt'\nimport { slugify } from './slugify'\nimport type { UiAuditCapture, UiAuditCaptureRequest } from './task'\n\n// All synthetic events the auditor emits flow through this helper. Reason:\n// `SandboxEvent.data` is a sandbox-SDK shape (effectively `Record<string,\n// unknown>`) that our typed payloads (`UiAuditCapture`, `UiFinding`, …) do not\n// satisfy structurally. The cast moves the type-system smell into a single,\n// named, documented call site so the call sites in `runIteration` stay clean.\n// The runtime contract — `{ type, data }` — is what the output adapter reads;\n// the static type is what the kernel collects into `SandboxEvent[]`.\nfunction asSandboxEvent<T>(type: string, data: T): SandboxEvent {\n return { type, data } as unknown as SandboxEvent\n}\n\n/** @experimental */\nexport interface InProcessUiAuditClientOptions {\n /**\n * Absolute path under which screenshots are written. Each capture lands\n * at `<workspaceDir>/screenshots/<filename>`; finding screenshot paths\n * are workspace-relative (`screenshots/<filename>`).\n */\n workspaceDir: string\n /** The vision judge that turns captures into findings. */\n judge: UiJudge\n /**\n * Navigation policy.\n *\n * `'strict'` (default) waits for `networkidle` and fails the iteration\n * if the page does not settle. `'spa'` waits for `domcontentloaded` —\n * use for single-page apps that hold open long-poll/websocket\n * connections and never settle.\n */\n navPolicy?: 'strict' | 'spa'\n /**\n * Browser launch override. Default: chromium headless via Playwright.\n * Consumers pass a custom factory to target a remote browser, a\n * different channel, or a fleet adapter.\n */\n launchBrowser?: () => Promise<BrowserHandle>\n}\n\n/** @experimental */\nexport interface BrowserHandle {\n newContext(options?: {\n viewport?: { width: number; height: number }\n }): Promise<BrowserContextHandle>\n close(): Promise<void>\n}\n\n/** @experimental */\nexport interface BrowserContextHandle {\n newPage(): Promise<PageHandle>\n close(): Promise<void>\n}\n\n/** @experimental */\nexport interface PageHandle {\n setViewportSize(size: { width: number; height: number }): Promise<void>\n goto(url: string, options?: { waitUntil?: string; timeout?: number }): Promise<unknown>\n waitForSelector(selector: string, options?: { timeout?: number }): Promise<unknown>\n waitForTimeout(ms: number): Promise<void>\n screenshot(options: { path: string; fullPage?: boolean }): Promise<void>\n locator(selector: string): {\n first(): { screenshot(options: { path: string }): Promise<void> }\n }\n}\n\nconst DEFAULT_VIEWPORT = { width: 1280, height: 800 } as const\nconst NAV_TIMEOUT_MS = 30_000\n\nasync function defaultLaunch(): Promise<BrowserHandle> {\n const mod = (await import('playwright')) as unknown as {\n chromium?: { launch(options?: { headless?: boolean }): Promise<BrowserHandle> }\n }\n if (!mod?.chromium || typeof mod.chromium.launch !== 'function') {\n throw new Error(\n 'ui-auditor: playwright is not installed. Install `playwright` (and run `playwright install chromium`) or pass a custom `launchBrowser` to createInProcessUiAuditClient.',\n )\n }\n return mod.chromium.launch({ headless: true })\n}\n\nfunction nowStamp(): string {\n const d = new Date()\n const pad = (n: number) => String(n).padStart(2, '0')\n // Millisecond resolution so parallel fanout iterations capturing the same\n // route/viewport/label within the same second don't collide on filename and\n // silently overwrite each other.\n return (\n `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}-` +\n `${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}` +\n `-${String(d.getUTCMilliseconds()).padStart(3, '0')}`\n )\n}\n\nfunction viewportOf(req: UiAuditCaptureRequest): { width: number; height: number } {\n return req.viewport ?? DEFAULT_VIEWPORT\n}\n\nfunction captureFilename(req: UiAuditCaptureRequest): string {\n const vp = viewportOf(req)\n const labelPart = req.label ? `--${slugify(req.label, 'label')}` : ''\n return `${slugify(req.route, 'route')}--${vp.width}x${vp.height}${labelPart}--${nowStamp()}.png`\n}\n\nfunction assertHttpUrl(url: string): void {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n throw new Error(`ui-auditor: capture url is not parseable (got ${JSON.stringify(url)})`)\n }\n // SSRF defense at the client boundary. The MCP tool already restricts to\n // http(s), but `createInProcessUiAuditClient` is exported and can be wired\n // up directly by consumers (the example does this). A crafted task envelope\n // could otherwise navigate Playwright to `file://`, `data:`, `javascript:`\n // and read local files or execute inline content.\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(\n `ui-auditor: capture url must use http or https (got ${parsed.protocol} in ${JSON.stringify(url)})`,\n )\n }\n}\n\nasync function captureOne(\n page: PageHandle,\n req: UiAuditCaptureRequest,\n outAbsPath: string,\n signal: AbortSignal,\n navPolicy: 'strict' | 'spa',\n): Promise<void> {\n signal.throwIfAborted()\n assertHttpUrl(req.url)\n // Apply the per-capture viewport before navigation. The capture metadata\n // and filename both encode this viewport; the rendered page must match.\n await page.setViewportSize(viewportOf(req))\n const waitUntil = navPolicy === 'spa' ? 'domcontentloaded' : 'networkidle'\n await page.goto(req.url, { waitUntil, timeout: NAV_TIMEOUT_MS })\n if (req.waitFor) {\n await page.waitForSelector(req.waitFor, { timeout: 15_000 })\n }\n const extra = req.waitMs ?? 500\n if (extra > 0) await page.waitForTimeout(extra)\n signal.throwIfAborted()\n if (req.elementSelector) {\n await page.locator(req.elementSelector).first().screenshot({ path: outAbsPath })\n } else {\n await page.screenshot({ path: outAbsPath, fullPage: req.fullPage === true })\n }\n}\n\ninterface SyntheticSandbox extends SandboxInstance {}\n\nfunction makeSandboxId(): string {\n const rand = () => Math.random().toString(16).slice(2, 10)\n return `ui-audit-${rand()}${rand()}`\n}\n\n/** @experimental */\nexport function createInProcessUiAuditClient(\n options: InProcessUiAuditClientOptions,\n): SandboxClient & {\n /**\n * Close the underlying browser. Idempotent.\n *\n * Contract: callers MUST ensure no iterations are in flight when this is\n * called. The kernel respects this — `runLoop` awaits every iteration\n * before returning, so `await runLoop(...); await client.close()` is the\n * intended pattern (see `examples/ui-audit`). If `close()` is invoked\n * concurrently with a running iteration, the browser teardown will race\n * against in-flight page operations; the iteration will surface an\n * AggregateError carrying both the iteration error and the close error,\n * but no work is lost silently.\n */\n close(): Promise<void>\n} {\n const launch = options.launchBrowser ?? defaultLaunch\n const navPolicy = options.navPolicy ?? 'strict'\n let browserPromise: Promise<BrowserHandle> | undefined\n let closed = false\n\n async function getBrowser(): Promise<BrowserHandle> {\n if (closed) {\n throw new Error('ui-auditor: client is closed; create a new client to run another iteration')\n }\n if (!browserPromise) browserPromise = launch()\n return browserPromise\n }\n\n async function* runIteration(\n promptText: string,\n signal: AbortSignal,\n ): AsyncIterable<SandboxEvent> {\n const task = decodeAuditTaskEnvelope(promptText)\n if (!task) {\n throw new Error(\n 'ui-auditor: prompt is missing a UI_AUDIT_TASK envelope. Use uiAuditorProfile().taskToPrompt to format prompts, or pass an envelope-prefixed prompt manually.',\n )\n }\n if (task.captures.length === 0) {\n throw new Error('ui-auditor: task has zero captures; nothing to audit.')\n }\n\n yield asSandboxEvent('audit.lens', { lens: task.lens })\n\n const browser = await getBrowser()\n const context = await browser.newContext({ viewport: DEFAULT_VIEWPORT })\n // Track both the primary iteration error and any context-close failure so\n // the cleanup path never silently swallows a leaked-context bug AND a\n // close failure never shadows the real iteration error. After the\n // try/catch/finally settles, we rethrow the primary if there was one,\n // otherwise we rethrow the close error.\n let primaryError: unknown\n let closeError: unknown\n try {\n const page = await context.newPage()\n const captures: UiAuditCapture[] = []\n const fs = await import('node:fs/promises')\n const path = await import('node:path')\n const shotsDir = path.join(options.workspaceDir, 'screenshots')\n await fs.mkdir(shotsDir, { recursive: true })\n\n for (const req of task.captures) {\n signal.throwIfAborted()\n const filename = captureFilename(req)\n const absPath = path.join(shotsDir, filename)\n const relPath = `screenshots/${filename}`\n await captureOne(page, req, absPath, signal, navPolicy)\n const vp = viewportOf(req)\n const cap: UiAuditCapture = {\n path: relPath,\n viewport: `${vp.width}x${vp.height}`,\n fullPage: req.fullPage === true,\n route: req.route,\n url: req.url,\n capturedAt: new Date().toISOString(),\n }\n if (req.elementSelector) cap.elementSelector = req.elementSelector\n if (req.label) cap.label = req.label\n captures.push(cap)\n yield asSandboxEvent('audit.capture', cap)\n }\n\n const judgeOut = await options.judge({\n lens: task.lens,\n captures,\n productContext: task.productContext,\n knownFindingIds: task.knownFindingIds,\n promptText,\n signal,\n })\n\n for (const finding of judgeOut.findings) {\n yield asSandboxEvent('audit.finding', finding)\n }\n if (judgeOut.notes && judgeOut.notes.trim().length > 0) {\n yield asSandboxEvent('audit.notes', { notes: judgeOut.notes })\n }\n\n const usage = judgeOut.tokenUsage ?? { input: 0, output: 0 }\n yield asSandboxEvent('done', {\n tokenUsage: {\n inputTokens: usage.input,\n outputTokens: usage.output,\n },\n totalCostUsd: judgeOut.costUsd ?? 0,\n })\n } catch (err) {\n primaryError = err\n } finally {\n try {\n await context.close()\n } catch (err) {\n closeError = err\n }\n }\n // When both the iteration and the cleanup fail, surface both via\n // AggregateError so a leaked context bug is not silently masked by an\n // earlier iteration failure (per the fail-loud doctrine).\n if (primaryError !== undefined && closeError !== undefined) {\n throw new AggregateError(\n [primaryError, closeError],\n 'ui-auditor: iteration failed AND context.close() failed; both errors attached.',\n )\n }\n if (primaryError !== undefined) throw primaryError\n if (closeError !== undefined) throw closeError\n }\n\n function makeSyntheticSandbox(): SyntheticSandbox {\n const id = makeSandboxId()\n const instance = {\n id,\n streamPrompt(message: string, opts?: { signal?: AbortSignal }): AsyncIterable<SandboxEvent> {\n const signal = opts?.signal ?? new AbortController().signal\n return runIteration(message, signal)\n },\n async delete(): Promise<void> {\n // No per-sandbox resources to release; the browser is shared and\n // closed by `client.close()`. Intentionally a no-op so trace-time\n // `box.delete()` succeeds without doing surprising work.\n },\n }\n return instance as unknown as SyntheticSandbox\n }\n\n return {\n async create(_options?: CreateSandboxOptions) {\n return makeSyntheticSandbox()\n },\n describePlacement(box) {\n const id = (box as unknown as { id?: string }).id\n return { kind: 'sibling', sandboxId: typeof id === 'string' ? id : undefined }\n },\n async close() {\n closed = true\n const pending = browserPromise\n browserPromise = undefined\n if (pending) {\n const browser = await pending\n await browser.close()\n }\n },\n }\n}\n","/**\n * @experimental\n *\n * Per-lens guidance the auditor inlines into its system prompt for an\n * iteration. Each entry is a self-contained brief — the same content the\n * standalone ui-issue-finder skill ships, embedded as a string constant so\n * agent-runtime carries no runtime dep on that external workspace.\n *\n * Briefs are deliberately concrete: they enumerate the SIGNALS to look for\n * and the cross-lens distinctions to respect, so the judge files fewer\n * pile-on findings under generic labels.\n */\n\nimport type { UiLens } from './substrate'\n\n/** @experimental */\nexport const SHARED_AUDITOR_RULES = `\nYou are auditing a UI for a specific class of problems. Stay strictly in your assigned lens — do not file issues that belong to another lens (a separate iteration will catch those).\n\nA finding is only valid if a thoughtful product designer would agree the screenshot shows something that should change. Avoid:\n- Personal taste (\"I'd prefer brand blue\").\n- Hallucinated text or controls you cannot actually see in the screenshot.\n- Suggestions that depend on requirements you don't have access to.\n- Pile-on findings about the same root cause — file ONE finding and use \\`similarTo\\` to link the rest.\n\nRequired for every finding:\n- title: concrete, names the offending element AND what's wrong (NOT \"improve UX\").\n- severity: critical=blocks a core task or accessibility blocker; high=noticeable friction; med=visible polish issue; low=nitpick.\n- observation: 1–3 sentences describing exactly what you see that is wrong.\n- impact: who is affected and how (concrete).\n- suggestedFix: a specific change a developer could apply without asking you back.\n- screenshots: refer to the captures attached to this iteration by path.\n- selector: when you can pin the offending element with a CSS selector.\n\nMost findings are med or low. Reserve high/critical for genuine blockers.\n`.trim()\n\n/** @experimental */\nexport const LENS_BRIEFS: Record<UiLens, string> = {\n consistency: `\nLENS: consistency\nLook for inconsistencies in the design system — things that look like they came from different products glued together.\nSignals: multiple font families, inconsistent weights/sizes for the same role, two shades of \"primary\", arbitrary paddings/margins that don't snap to a scale (4/8/12/16/24), same control with different border-radius or shadow on different pages, mixed icon styles (filled vs outlined), inconsistent button heights/padding for the same variant, inconsistent capitalization (Title Case vs sentence case) for the same role.\nNOT this lens: layout misalignment (use \\`layout\\`), confusing user flow (use \\`ux-flow\\`), contrast/keyboard issues (use \\`accessibility\\`).\nTitle format: \\`Inconsistent <thing> between <A> and <B>\\`.\n`.trim(),\n hierarchy: `\nLENS: hierarchy\nLook for broken visual hierarchy — places where the eye does not land on what matters most.\nSignals: primary CTA same weight as secondary/tertiary controls, headings (H1/H2/H3) nearly the same size, important data buried (headline number smaller than its label), decoration outshining content, too many emphases competing, wrong scan order, missing or overly heavy section dividers.\nNOT this lens: same-role styled differently (\\`consistency\\`), grid/alignment (\\`layout\\`), contrast-failing text (\\`accessibility\\`).\nTitle format: \\`Weak hierarchy: <element> does not read as the <intended-role>\\`.\n`.trim(),\n layout: `\nLENS: layout\nLook for layout and organization problems — alignment, grouping, whitespace, structural choices that hurt scannability.\nSignals: misalignment within rows, inconsistent gutters in grids, orphan whitespace next to crammed regions, poor grouping (related fields separated, unrelated fields adjacent), no visual sections (long wall of content), container overflow (text/content punching out of card boundaries), cramped or oversized hit targets, sidebars/headers sized wrong relative to main content.\nNOT this lens: same-role styled differently (\\`consistency\\`), click-distance/friction (\\`ux-flow\\`), overflow specifically at small viewports (\\`responsive\\`).\nTitle format: \\`<Region> alignment/spacing problem\\` or \\`<Region> grouping unclear\\`.\n`.trim(),\n 'ux-flow': `\nLENS: ux-flow\nLook for interaction-flow friction — action sequences that are slower, more annoying, or more error-prone than necessary.\nSignals: sequential clicks far apart (e.g. Next top-right while user is bottom-left), destructive action adjacent to primary with same weight, confirmations that don't say what's being confirmed, primary CTA below the fold or hidden in a kebab menu, silent state changes (toggle gives no feedback), form ordering that fights real-world order, dead-end states after submit, lost inputs on back-navigation, hidden pre-selected options.\nNOT this lens: visual style only (\\`consistency\\`), component arrangement without a flow problem (\\`layout\\`), microcopy clarity (\\`content\\`).\nTitle format: \\`<Action A> → <Action B> friction: <root cause>\\`.\n`.trim(),\n duplication: `\nLENS: duplication\nLook for redundancy — the same control, link, or piece of content appearing more than once with no good reason.\nSignals: two ways to do the same action on the same screen with no difference, repeated nav (same links in sidebar AND top nav), drifted duplicates (two copies that have diverged), content repeated verbatim, icon + label saying the same thing twice in one row, per-row + bulk actions that overlap confusingly, multiple status indicators conveying the same status.\nNOT this lens: inconsistent styling of duplicates (\\`consistency\\`) — this lens is about the existence of duplicates.\nTitle format: \\`Duplicate <thing> in <location A> and <location B>\\`.\n`.trim(),\n accessibility: `\nLENS: accessibility\nLook for accessibility blockers and degradations. Be conservative — do not assume violations you cannot see.\nSignals: insufficient contrast on body text or controls, missing/invisible focus styles, tiny tap targets (<24px on mobile), color as sole signal (red border with no message), form labels missing or not associated (placeholders standing in for labels), broken heading order (H1 → H4), modals that don't trap focus, decorative elements that take focus, errors not announced, important text rendered inside images.\nNOT this lens: generic \"looks confusing\" (\\`hierarchy\\` or \\`content\\`), layout overflow at small viewports (\\`responsive\\`).\nTitle format: \\`Accessibility: <specific blocker> in <element>\\`.\n`.trim(),\n responsive: `\nLENS: responsive\nLook for layout breakage across viewport sizes — content that works at one width but degrades at another. This iteration's captures should include the same surface at >=2 viewports; compare across them.\nSignals: horizontal scroll where content should reflow, overlapping elements (header overlaps content, fixed footer covers inputs), desktop nav crammed into mobile without collapsing, table columns that don't truncate, tap targets too close at touch sizes, controls vanishing at certain widths, layout flips that break grouping order, modals exceeding viewport height (confirm button unreachable).\nNOT this lens: issues present at every viewport (\\`consistency\\` / \\`hierarchy\\` instead).\nTitle format: \\`<Element/Region> breaks at <viewport>\\`.\n`.trim(),\n states: `\nLENS: states\nLook for missing or broken UI states — the not-happy-paths that make a product feel finished or unfinished. The iteration's captures should depict at least one non-default state.\nSignals: empty lists with no guidance, skeletons that don't match final layout (CLS on settle), error states with no message or recovery action, disabled buttons with no explanation, toasts that disappear before being read, success states that don't confirm, missing hover/focus/active/disabled variants on primary controls, no long-content view, no-permission state broken.\nNOT this lens: generic polish on the happy path (other lenses), missing focus rings specifically (\\`accessibility\\`).\nTitle format: \\`Missing/broken <state> state on <surface>\\`.\n`.trim(),\n content: `\nLENS: content\nLook for microcopy and content problems — text that is unclear, inconsistent, condescending, jargon-heavy, or wrong.\nSignals: jargon/internal language leaking (\"Provisioning a Tenant\" instead of \"Setting up your account\"), inconsistent terminology (workspace vs team), verbose button labels, empty-state copy that's just \"No results\", error messages blaming the user, tone inconsistency, truncation without affordance, mixed date/number formats on one page, placeholder used as a label, \"Saved!\" toast appearing before save completes, typos and grammar errors.\nNOT this lens: visual treatment of text (\\`hierarchy\\` / \\`consistency\\`), missing labels for a11y (\\`accessibility\\`).\nTitle format: \\`Copy: \"<actual text>\" in <location>\\` or \\`Inconsistent term: \"<A>\" vs \"<B>\"\\`.\n`.trim(),\n interaction: `\nLENS: interaction\nLook for interaction quality problems — affordances, feedback, and micro-interactions.\nSignals: no affordance (clickable areas not looking clickable, non-clickable areas looking clickable), missing feedback (>100ms click with no progress), hover surprises (whole row highlights but only title clickable), cursor inconsistency, animations that block input, missing transitions where they're needed (accordion snaps open), drag-and-drop without indicators, scroll-jacking, click-through bugs (card click handler firing alongside button), hover-only revelations on touch.\nNOT this lens: position of controls (\\`layout\\` / \\`ux-flow\\`), missing focus styles (\\`accessibility\\`).\nTitle format: \\`<Action> on <element>: <missing/wrong> feedback\\`.\n`.trim(),\n 'performance-perceived': `\nLENS: performance-perceived\nLook for perceived-performance problems — visible jank a real user would notice, not benchmark numbers. This iteration's captures should include >=2 frames during load to show shift.\nSignals: layout shift (CLS) when late-arriving images/fonts/banners settle, FOUC (flash of unstyled content), font swap jumps, late-loading hero images that shift everything, skeletons that don't match final shape, spinners on instant local actions, loading state reappearing after content paints (refetch on focus), modal open animation longer than the operation it precedes.\nNOT this lens: slow API calls (file separately), stale data after navigation (\\`states\\`).\nTitle format: \\`Layout shift / late paint on <route>: <root cause>\\`.\n`.trim(),\n other: `\nLENS: other\nUse ONLY when a finding is clearly a UI quality issue but does not fit any other lens. Strongly prefer a specific lens — \\`other\\` should be rare. Title must still be concrete.\n`.trim(),\n}\n\n/**\n * Build a system prompt for a single auditor iteration.\n *\n * @experimental\n */\nexport function buildAuditorSystemPrompt(lens: UiLens): string {\n const brief = LENS_BRIEFS[lens]\n return `${SHARED_AUDITOR_RULES}\\n\\n${brief}`\n}\n","/**\n * @experimental\n *\n * Sandbox-event stream → UiAuditOutput decoder. The custom auditor\n * `SandboxClient` emits events of the form:\n *\n * { type: 'audit.capture', data: UiAuditCapture }\n * { type: 'audit.finding', data: UiFinding }\n * { type: 'audit.notes', data: { notes: string } }\n * { type: 'audit.lens', data: { lens: UiLens } }\n * { type: 'done', data: { tokenUsage: { ... }, totalCostUsd?: number } }\n *\n * Other event types are tolerated and ignored. The adapter is pure: it\n * folds an already-collected event array into a UiAuditOutput.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport { UI_LENSES, type UiFinding, type UiLens } from './substrate'\nimport type { UiAuditCapture, UiAuditOutput } from './task'\n\n// Build the lens-validation set from the canonical UI_LENSES tuple so adding\n// a lens to the substrate automatically extends the parser; otherwise a new\n// lens would silently fail isUiLens() and parseAuditorEvents would drop\n// every event using it.\nconst KNOWN_LENS_VALUES = new Set<UiLens>(UI_LENSES)\n\nfunction isUiLens(v: unknown): v is UiLens {\n return typeof v === 'string' && KNOWN_LENS_VALUES.has(v as UiLens)\n}\n\n/** @experimental */\nexport function parseAuditorEvents(events: SandboxEvent[]): UiAuditOutput {\n const findings: UiFinding[] = []\n const captures: UiAuditCapture[] = []\n let lens: UiLens | undefined\n let notes: string | undefined\n\n for (const evt of events) {\n if (!evt || typeof evt !== 'object') continue\n const type = String(evt.type ?? '')\n const data =\n evt.data && typeof evt.data === 'object' ? (evt.data as Record<string, unknown>) : undefined\n if (!data) continue\n\n switch (type) {\n case 'audit.lens': {\n const v = data.lens\n if (isUiLens(v)) lens = v\n break\n }\n case 'audit.capture': {\n const cap = data as unknown as Partial<UiAuditCapture>\n if (\n typeof cap.path === 'string' &&\n typeof cap.viewport === 'string' &&\n typeof cap.fullPage === 'boolean' &&\n typeof cap.route === 'string' &&\n typeof cap.url === 'string' &&\n typeof cap.capturedAt === 'string'\n ) {\n const out: UiAuditCapture = {\n path: cap.path,\n viewport: cap.viewport,\n fullPage: cap.fullPage,\n route: cap.route,\n url: cap.url,\n capturedAt: cap.capturedAt,\n }\n if (cap.elementSelector) out.elementSelector = cap.elementSelector\n if (cap.label) out.label = cap.label\n captures.push(out)\n }\n break\n }\n case 'audit.finding': {\n const f = data as unknown as Partial<UiFinding>\n // Hard requirement: all the actionable fields must be present and\n // non-empty for a finding to enter the output. The validator does the\n // softer scoring; the adapter only filters structural junk.\n if (\n typeof f.title === 'string' &&\n f.title.trim().length > 0 &&\n isUiLens(f.lens) &&\n typeof f.severity === 'string' &&\n ['low', 'med', 'high', 'critical'].includes(f.severity) &&\n typeof f.route === 'string' &&\n typeof f.observation === 'string' &&\n typeof f.impact === 'string' &&\n typeof f.suggestedFix === 'string' &&\n Array.isArray(f.screenshots)\n ) {\n findings.push(f as UiFinding)\n }\n break\n }\n case 'audit.notes': {\n const n = data.notes\n if (typeof n === 'string' && n.trim().length > 0) notes = n\n break\n }\n default:\n // Tolerate cost/usage events and other backend chatter — extractLlmCallEvent\n // in run-loop.ts handles cost accounting upstream from the adapter.\n break\n }\n }\n\n const out: UiAuditOutput = { lens: lens ?? 'other', findings, captures }\n if (notes) out.notes = notes\n return out\n}\n","/**\n * @experimental\n *\n * Auditor validator — scores a single iteration's findings for actionability\n * and gates the iteration result. The kernel uses `valid` + `score` for\n * winner selection across fanned-out iterations and to detect a degenerate\n * iteration (lens-violating findings, no screenshot evidence, no findings\n * at all on a route where we expected some).\n *\n * Hard fails (`valid = false`):\n * - A finding is filed under a lens that does not match the iteration's\n * lens. The whole iteration is bad — the judge isn't following the\n * lens discipline and the resulting Markdown would mislead reviewers.\n * - A finding has no screenshot reference.\n * - A finding's screenshot references a path that wasn't captured in\n * this iteration.\n *\n * Score (0..1, max two decimals stable):\n * - 0.4 * specificityRatio — proportion of findings with a selector\n * - 0.4 * evidenceRatio — proportion of findings whose screenshots resolve to captures\n * - 0.2 * (1 - genericTitleRatio) — proportion of findings whose titles\n * are concrete (not \"improve UX\", \"fix layout\", etc.)\n *\n * An iteration with zero findings scores 0.5 by convention — neither a\n * confident pass nor a hard failure (the judge might just have nothing to\n * say on this lens). The driver decides what to do with it.\n */\n\nimport type { DefaultVerdict } from '@tangle-network/agent-eval'\nimport type { Validator } from '../../runtime/types'\nimport type { UiAuditOutput, UiAuditTask } from './task'\n\nconst GENERIC_TITLE_PATTERNS = [\n /^improve\\s/i,\n /^fix\\s/i,\n /^update\\s/i,\n /^better\\s/i,\n /^bad\\s/i,\n /^make\\s.+\\sbetter/i,\n /\\bUX\\b\\s*$/i,\n /\\bUI\\b\\s*$/i,\n]\n\nfunction isGenericTitle(title: string): boolean {\n const t = title.trim()\n if (t.length < 16) return true\n return GENERIC_TITLE_PATTERNS.some((re) => re.test(t))\n}\n\n/** @experimental */\nexport function createUiAuditorValidator(task: UiAuditTask): Validator<UiAuditOutput> {\n return {\n async validate(output) {\n const findings = output.findings\n const captures = output.captures\n const capturePaths = new Set(captures.map((c) => c.path))\n\n const offLens = findings.filter((f) => f.lens !== task.lens)\n if (offLens.length > 0) {\n const verdict: DefaultVerdict = {\n valid: false,\n score: 0,\n notes: `${offLens.length} finding(s) filed under wrong lens (expected ${task.lens}; got ${offLens.map((f) => f.lens).join(', ')})`,\n scores: { offLens: 0 },\n }\n return verdict\n }\n\n const missingEvidence = findings.filter(\n (f) => !Array.isArray(f.screenshots) || f.screenshots.length === 0,\n )\n if (missingEvidence.length > 0) {\n const verdict: DefaultVerdict = {\n valid: false,\n score: 0,\n notes: `${missingEvidence.length} finding(s) have no screenshot evidence`,\n scores: { evidence: 0 },\n }\n return verdict\n }\n\n const unresolvedShot = findings.filter((f) =>\n f.screenshots.some((s) => !capturePaths.has(s.path)),\n )\n if (unresolvedShot.length > 0) {\n const verdict: DefaultVerdict = {\n valid: false,\n score: 0,\n notes: `${unresolvedShot.length} finding(s) reference screenshot paths not captured this iteration`,\n scores: { evidence: 0 },\n }\n return verdict\n }\n\n if (findings.length === 0) {\n const verdict: DefaultVerdict = {\n valid: true,\n score: 0.5,\n notes: 'No findings reported. Neither a confident pass nor a failure.',\n scores: { specificity: 0, evidence: 1, titles: 1 },\n }\n return verdict\n }\n\n const withSelector = findings.filter((f) => typeof f.selector === 'string').length\n const specificity = withSelector / findings.length\n const generic = findings.filter((f) => isGenericTitle(f.title)).length\n const titles = 1 - generic / findings.length\n // Compute evidence honestly from the data: proportion of findings whose\n // screenshots are all resolvable against this iteration's captures. The\n // guards above hard-fail when this would be < 1, so today the result is\n // always 1; if a future change relaxes those guards into a soft-fail\n // mode, this still produces a truthful evidence ratio rather than a\n // stale constant inflating the score.\n const withFullEvidence = findings.filter(\n (f) =>\n Array.isArray(f.screenshots) &&\n f.screenshots.length > 0 &&\n f.screenshots.every((s) => capturePaths.has(s.path)),\n ).length\n const evidence = withFullEvidence / findings.length\n const score = Number((0.4 * specificity + 0.4 * evidence + 0.2 * titles).toFixed(4))\n\n const verdict: DefaultVerdict = {\n valid: true,\n score,\n notes: `${findings.length} finding(s) — specificity=${specificity.toFixed(2)} evidence=${evidence.toFixed(2)} titles=${titles.toFixed(2)}`,\n scores: { specificity, evidence, titles },\n }\n return verdict\n },\n }\n}\n","/**\n * @experimental\n *\n * `uiAuditorProfile` — preset for vision-driven UI audit iterations.\n *\n * A `runLoop` bundle: it returns the `AgentRunSpec`, output adapter, validator, and prompt\n * formatter the loop kernel needs. The agent's \"harness\" is not a sandbox-SDK code-runner — it's a\n * vision-capable judge driving a browser. The loop kernel still iterates\n * `client.create() → box.streamPrompt() → box.delete()`; the client/box pair are provided by\n * `createInProcessUiAuditClient` (in `./in-process-client.ts`) or a consumer-supplied `SandboxClient`.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type { AgentRunSpec, OutputAdapter, Validator } from '../../runtime/types'\nimport { buildAuditorSystemPrompt } from './lens-prompts'\nimport { parseAuditorEvents } from './output-adapter'\nimport { encodeAuditTaskEnvelope, formatAuditorPrompt } from './prompt'\nimport type { UiAuditOutput, UiAuditTask } from './task'\nimport { createUiAuditorValidator } from './validator'\n\n/** @experimental */\nexport interface UiAuditorProfileOptions {\n /**\n * Stable name surfaced in trace events. Defaults to `ui-auditor`.\n */\n name?: string\n /**\n * Optional model identifier passed in `AgentProfile.model.default`.\n * The consumer's `SandboxClient` chooses how to interpret it.\n */\n model?: string\n /**\n * Task bound to the validator. Without it the validator uses the lens\n * embedded in the iteration output as its expectation — fine for one-off\n * use; less strict than passing the task explicitly.\n */\n task?: UiAuditTask\n}\n\n/** @experimental */\nexport function uiAuditorProfile(options: UiAuditorProfileOptions = {}): {\n profile: AgentProfile\n taskToPrompt: (task: UiAuditTask) => string\n output: OutputAdapter<UiAuditOutput>\n validator: Validator<UiAuditOutput>\n agentRunSpec: AgentRunSpec<UiAuditTask>\n} {\n const name = options.name ?? 'ui-auditor'\n\n // Lens is per-task; the profile's system prompt is filled in by the\n // taskToPrompt formatter at iteration time (prefixed to the user\n // message). Keeping the profile lens-agnostic lets one AgentRunSpec\n // serve every lens-iteration of the loop.\n const profile: AgentProfile = {\n name,\n description: 'Vision-driven UI auditor. One lens per iteration.',\n prompt: { systemPrompt: '' },\n model: options.model ? { default: options.model } : undefined,\n tools: { browser: true, vision: true },\n metadata: { role: 'ui-auditor' },\n }\n\n const output: OutputAdapter<UiAuditOutput> = { parse: parseAuditorEvents }\n const validator: Validator<UiAuditOutput> = options.task\n ? createUiAuditorValidator(options.task)\n : createUiAuditorValidator({ lens: 'other', captures: [] })\n\n // Prompt shape (consumed both by sandbox-SDK harnesses AND by the\n // in-process auditor client):\n // <<UI_AUDIT_TASK>>{json}<<UI_AUDIT_TASK_END>>\n // <system-prompt for the lens>\n // <human-readable iteration brief>\n // The envelope makes the iteration self-describing so concurrent fanout\n // does not race over per-client side state. Sandbox-SDK harnesses can\n // ignore the envelope; the in-process auditor client decodes it back\n // into a typed UiAuditTask via decodeAuditTaskEnvelope.\n const taskToPrompt = (task: UiAuditTask): string =>\n `${encodeAuditTaskEnvelope(task)}\\n${buildAuditorSystemPrompt(task.lens)}\\n\\n${formatAuditorPrompt(task)}`\n\n const agentRunSpec: AgentRunSpec<UiAuditTask> = {\n name,\n profile,\n taskToPrompt,\n }\n\n return { profile, taskToPrompt, output, validator, agentRunSpec }\n}\n"],"mappings":"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","names":["fs","path","out","verdict"]}
1
+ {"version":3,"sources":["../src/audit/issue-writer.ts","../src/profiles/ui-auditor/slugify.ts","../src/profiles/ui-auditor/prompt.ts","../src/profiles/ui-auditor/in-process-client.ts","../src/profiles/ui-auditor/lens-prompts.ts","../src/profiles/ui-auditor/output-adapter.ts","../src/profiles/ui-auditor/validator.ts","../src/profiles/ui-auditor/profile.ts"],"sourcesContent":["/**\n * @experimental\n *\n * UI-audit issue writer — pure I/O. Takes a workspace dir + `UiFinding[]`\n * and emits:\n * - `<workspace>/issues/NNN--<lens>--<slug>.md` — one self-contained\n * GitHub-issue-ready Markdown per finding, with embedded screenshot\n * references.\n * - `<workspace>/registry.json` — finding index for dedup and audit\n * resume across iterations.\n * - `<workspace>/index.md` — human-readable rollup (severity / lens /\n * route counts plus a sorted finding list).\n *\n * The writer is deterministic, idempotent for `appendFindings()`, and\n * never invokes an LLM. It assigns the next monotonic id to a finding the\n * caller did not pre-id.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { slugify } from '../profiles/ui-auditor/slugify'\nimport {\n UI_FINDING_SEVERITIES,\n UI_LENSES,\n type UiFinding,\n type UiLens,\n} from '../profiles/ui-auditor/substrate'\n\n/** @experimental */\nexport interface AuditRegistry {\n schemaVersion: 1\n findings: UiFinding[]\n /** Route → URL + captures sidecar; preserved across runs. */\n routes: Record<string, { url?: string; captures: AuditRegistryCapture[] }>\n}\n\n/** @experimental */\nexport interface AuditRegistryCapture {\n file: string\n viewport?: string\n fullPage?: boolean\n elementSelector?: string\n capturedAt: string\n}\n\nconst SEVERITY_ORDER: Record<UiFinding['severity'], number> = {\n critical: 0,\n high: 1,\n med: 2,\n low: 3,\n}\n\n// Validate workspaceDir at every public entry point. The MCP tool checks\n// the same shape at the wire boundary, but the writer is independently\n// exported via `@tangle-network/agent-runtime/audit`, so direct callers\n// would otherwise bypass that defense. Absolute + no `..` segments matches\n// the MCP tool's contract — any path that the writer joins for I/O must be\n// rooted at a deterministic location chosen by the caller.\nfunction assertValidWorkspaceDir(dir: string): void {\n if (typeof dir !== 'string' || dir.length === 0) {\n throw new Error(\n `audit-writer: workspaceDir must be a non-empty string (got ${JSON.stringify(dir)})`,\n )\n }\n if (!path.isAbsolute(dir)) {\n throw new Error(\n `audit-writer: workspaceDir must be an absolute path (got ${JSON.stringify(dir)})`,\n )\n }\n if (dir.split(path.sep).includes('..')) {\n throw new Error(\n `audit-writer: workspaceDir must not contain '..' segments (got ${JSON.stringify(dir)})`,\n )\n }\n}\n\n/** @experimental */\nexport async function initAuditWorkspace(workspaceDir: string): Promise<void> {\n assertValidWorkspaceDir(workspaceDir)\n await fs.mkdir(path.join(workspaceDir, 'issues'), { recursive: true })\n await fs.mkdir(path.join(workspaceDir, 'screenshots'), { recursive: true })\n const regPath = path.join(workspaceDir, 'registry.json')\n try {\n await fs.access(regPath)\n } catch {\n const fresh: AuditRegistry = { schemaVersion: 1, findings: [], routes: {} }\n await fs.writeFile(regPath, JSON.stringify(fresh, null, 2))\n }\n}\n\n/** @experimental */\nexport async function readAuditRegistry(workspaceDir: string): Promise<AuditRegistry> {\n assertValidWorkspaceDir(workspaceDir)\n const regPath = path.join(workspaceDir, 'registry.json')\n const text = await fs.readFile(regPath, 'utf8')\n const parsed = JSON.parse(text) as unknown\n if (!parsed || typeof parsed !== 'object') {\n throw new Error(`audit-writer: registry.json at ${regPath} is not a JSON object`)\n }\n const reg = parsed as Partial<AuditRegistry>\n if (reg.schemaVersion !== 1 || !Array.isArray(reg.findings) || typeof reg.routes !== 'object') {\n throw new Error(`audit-writer: registry.json at ${regPath} has an unrecognized shape`)\n }\n return reg as AuditRegistry\n}\n\nasync function writeAuditRegistry(workspaceDir: string, reg: AuditRegistry): Promise<void> {\n const regPath = path.join(workspaceDir, 'registry.json')\n // Write to a sibling temp file and rename. POSIX rename is atomic, so a\n // reader at any instant sees either the pre-write OR post-write registry,\n // never a half-written file. Prevents corruption on crash mid-write.\n const tmpPath = `${regPath}.tmp-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n await fs.writeFile(tmpPath, JSON.stringify(reg, null, 2))\n await fs.rename(tmpPath, regPath)\n}\n\n// Per-workspace serialization. The writer reads registry.json, mutates it\n// in memory, then writes it back; concurrent calls would race on the\n// monotonic-id assignment and clobber each other's findings. Each\n// workspace key holds the Promise of the most-recently-enqueued operation;\n// new operations chain off it so they run strictly serially.\nconst workspaceLocks = new Map<string, Promise<unknown>>()\n\nasync function withWorkspaceLock<T>(workspaceDir: string, fn: () => Promise<T>): Promise<T> {\n const key = path.resolve(workspaceDir)\n const prev = workspaceLocks.get(key) ?? Promise.resolve()\n let signalDone!: () => void\n const done = new Promise<void>((resolve) => {\n signalDone = resolve\n })\n workspaceLocks.set(key, done)\n try {\n await prev\n return await fn()\n } finally {\n signalDone()\n // Only delete if no later caller has overwritten the slot — preserves\n // the chain for any concurrent callers still waiting.\n if (workspaceLocks.get(key) === done) workspaceLocks.delete(key)\n }\n}\n\nfunction assertFindingShape(f: UiFinding, index: number): void {\n const where = `audit-writer: findings[${index}]`\n if (!UI_LENSES.includes(f.lens)) {\n throw new Error(\n `${where}: invalid lens ${JSON.stringify(f.lens)}; one of ${UI_LENSES.join('|')}`,\n )\n }\n if (!UI_FINDING_SEVERITIES.includes(f.severity)) {\n throw new Error(\n `${where}: invalid severity ${JSON.stringify(f.severity)}; one of ${UI_FINDING_SEVERITIES.join('|')}`,\n )\n }\n for (const field of ['title', 'route', 'observation', 'impact', 'suggestedFix'] as const) {\n const v = f[field]\n if (typeof v !== 'string' || v.trim().length === 0) {\n throw new Error(`${where}: ${field} must be a non-empty string`)\n }\n }\n if (!Array.isArray(f.screenshots) || f.screenshots.length === 0) {\n throw new Error(`${where}: screenshots must be a non-empty array`)\n }\n for (let i = 0; i < f.screenshots.length; i += 1) {\n const s = f.screenshots[i]\n if (!s || typeof s.path !== 'string' || s.path.length === 0) {\n throw new Error(`${where}.screenshots[${i}].path must be a non-empty string`)\n }\n // Defense-in-depth: screenshot paths are written verbatim into Markdown\n // and the registry; a downstream consumer that joins workspaceDir with\n // them would otherwise be exposed to traversal-read. Reject `..`\n // segments at the validation boundary.\n if (s.path.split(/[/\\\\]/).includes('..')) {\n throw new Error(`${where}.screenshots[${i}].path must not contain '..' segments`)\n }\n }\n}\n\nfunction nextFindingId(reg: AuditRegistry): number {\n let max = 0\n for (const f of reg.findings) {\n if (typeof f.id === 'number' && f.id > max) max = f.id\n }\n return max + 1\n}\n\nfunction slugifyTitle(title: string): string {\n return slugify(title, 'title')\n}\n\nfunction renderFinding(finding: UiFinding): string {\n if (!finding.id) {\n throw new Error('audit-writer: cannot render a finding without an assigned id')\n }\n if (finding.screenshots.length === 0) {\n throw new Error(`audit-writer: finding #${finding.id} has no screenshots`)\n }\n const id = finding.id\n const numStr = String(id).padStart(3, '0')\n\n const metaLines: string[] = [\n `> **Issue #${numStr}** · **Severity:** ${finding.severity} · **Lens:** ${finding.lens}`,\n `> **Route:** \\`${finding.route}\\`${finding.url ? ` · **URL:** ${finding.url}` : ''}`,\n ]\n if (finding.viewport) metaLines.push(`> **Viewport:** ${finding.viewport}`)\n if (finding.selector) metaLines.push(`> **Selector:** \\`${finding.selector}\\``)\n if (finding.tags && finding.tags.length > 0) {\n metaLines.push(`> **Tags:** ${finding.tags.map((t) => `\\`${t}\\``).join(', ')}`)\n }\n if (finding.similarTo && finding.similarTo.length > 0) {\n metaLines.push(\n `> **Possible duplicates:** ${finding.similarTo.map((n) => `#${String(n).padStart(3, '0')}`).join(', ')}`,\n )\n }\n metaLines.push(`> _Generated: ${finding.createdAt ?? new Date().toISOString()}_`)\n\n const reproSteps =\n finding.reproSteps ??\n `1. Open \\`${finding.route}\\`${finding.url ? ` (${finding.url})` : ''}\\n2. Observe the area shown in the screenshot${finding.selector ? ` (selector: \\`${finding.selector}\\`)` : ''}.`\n\n // Screenshot paths are workspace-relative; the issue file sits in\n // `<workspace>/issues/`, so we render `../<path>`.\n const evidence = finding.screenshots\n .map((s) => `![${path.basename(s.path)}](../${s.path})`)\n .join('\\n\\n')\n\n return [\n `# [UI] ${finding.title}`,\n '',\n metaLines.join('\\n'),\n '',\n '## Observation',\n finding.observation.trim(),\n '',\n '## Why it matters',\n finding.impact.trim(),\n '',\n '## Steps to reproduce',\n reproSteps,\n '',\n '## Suggested fix',\n finding.suggestedFix.trim(),\n '',\n '## Evidence',\n evidence,\n '',\n '---',\n `<sub>Generated by agent-runtime/ui-auditor · lens=\\`${finding.lens}\\` · severity=\\`${finding.severity}\\`</sub>`,\n '',\n ].join('\\n')\n}\n\n/** @experimental */\nexport interface AppendFindingsResult {\n /** Findings with id + createdAt assigned, in input order. */\n written: UiFinding[]\n /** Workspace-relative path to each issue Markdown file, in input order. */\n files: string[]\n}\n\n/**\n * Append findings to a workspace, writing one Markdown file per finding\n * and updating registry.json. Assigns monotonically increasing ids to\n * findings that arrived without one.\n *\n * Findings already carrying an id that collides with the registry are\n * rejected — callers must either freshly mint findings (id undefined) or\n * use a separate update path. This protects against accidental overwrite.\n *\n * @experimental\n */\nexport async function appendFindings(\n workspaceDir: string,\n findings: readonly UiFinding[],\n): Promise<AppendFindingsResult> {\n assertValidWorkspaceDir(workspaceDir)\n // Validate every finding BEFORE acquiring the lock so callers see a fast\n // input-shape error without blocking concurrent writers. Validation is\n // the only defense against path traversal via crafted `lens` values\n // (the filename interpolates lens directly) and against malformed\n // Markdown from missing required fields — TypeScript types are erased\n // at runtime, so this boundary cannot trust its callers.\n for (let i = 0; i < findings.length; i += 1) {\n const f = findings[i]\n if (!f) throw new Error(`audit-writer: findings[${i}] is undefined`)\n assertFindingShape(f, i)\n }\n\n return withWorkspaceLock(workspaceDir, async () => {\n await initAuditWorkspace(workspaceDir)\n const reg = await readAuditRegistry(workspaceDir)\n const usedIds = new Set<number>()\n for (const f of reg.findings) {\n if (typeof f.id === 'number') usedIds.add(f.id)\n }\n\n const written: UiFinding[] = []\n const files: string[] = []\n let nextId = nextFindingId(reg)\n\n for (const incoming of findings) {\n let id = incoming.id\n if (id !== undefined) {\n if (usedIds.has(id)) {\n throw new Error(\n `audit-writer: incoming finding id ${id} (title=${JSON.stringify(incoming.title)}) collides with an existing registry entry`,\n )\n }\n } else {\n id = nextId\n while (usedIds.has(id)) id += 1\n nextId = id + 1\n }\n usedIds.add(id)\n\n const createdAt = incoming.createdAt ?? new Date().toISOString()\n const persisted: UiFinding = { ...incoming, id, createdAt }\n\n const slug = slugifyTitle(persisted.title)\n const fileName = `${String(id).padStart(3, '0')}--${persisted.lens}--${slug}.md`\n const filePathAbs = path.join(workspaceDir, 'issues', fileName)\n const filePathRel = `issues/${fileName}`\n\n await fs.writeFile(filePathAbs, renderFinding(persisted))\n reg.findings.push(persisted)\n written.push(persisted)\n files.push(filePathRel)\n }\n\n await writeAuditRegistry(workspaceDir, reg)\n return { written, files }\n })\n}\n\n/** @experimental */\nexport interface RegisterCapturesOptions {\n route: string\n url?: string\n captures: readonly AuditRegistryCapture[]\n}\n\n/**\n * Record screenshots taken for a route in the registry, without filing a\n * finding. Useful when the auditor wants to remember which captures\n * exist for resume / dedup purposes.\n *\n * @experimental\n */\nexport async function registerCaptures(\n workspaceDir: string,\n options: RegisterCapturesOptions,\n): Promise<void> {\n assertValidWorkspaceDir(workspaceDir)\n if (typeof options.route !== 'string' || options.route.trim().length === 0) {\n throw new Error('audit-writer: registerCaptures: route must be a non-empty string')\n }\n return withWorkspaceLock(workspaceDir, async () => {\n await initAuditWorkspace(workspaceDir)\n const reg = await readAuditRegistry(workspaceDir)\n const slot = reg.routes[options.route] ?? { captures: [] }\n if (options.url) slot.url = options.url\n slot.captures = [...slot.captures, ...options.captures]\n reg.routes[options.route] = slot\n await writeAuditRegistry(workspaceDir, reg)\n })\n}\n\n/** @experimental */\nexport interface AuditIndex {\n /** Total findings in the workspace. */\n total: number\n bySeverity: Record<UiFinding['severity'], number>\n byLens: Partial<Record<UiLens, number>>\n byRoute: Record<string, number>\n}\n\n/** @experimental */\nexport function summarizeRegistry(reg: AuditRegistry): AuditIndex {\n const bySeverity: Record<UiFinding['severity'], number> = {\n critical: 0,\n high: 0,\n med: 0,\n low: 0,\n }\n const byLens: Partial<Record<UiLens, number>> = {}\n const byRoute: Record<string, number> = {}\n for (const f of reg.findings) {\n bySeverity[f.severity] += 1\n byLens[f.lens] = (byLens[f.lens] ?? 0) + 1\n byRoute[f.route] = (byRoute[f.route] ?? 0) + 1\n }\n return { total: reg.findings.length, bySeverity, byLens, byRoute }\n}\n\n/**\n * Regenerate `<workspace>/index.md` from registry.json.\n *\n * @experimental\n */\nexport async function writeAuditIndex(workspaceDir: string): Promise<string> {\n assertValidWorkspaceDir(workspaceDir)\n const reg = await readAuditRegistry(workspaceDir)\n const summary = summarizeRegistry(reg)\n\n const sorted = [...reg.findings].sort((a, b) => {\n const sa = SEVERITY_ORDER[a.severity]\n const sb = SEVERITY_ORDER[b.severity]\n if (sa !== sb) return sa - sb\n return (a.id ?? 0) - (b.id ?? 0)\n })\n\n const lines: string[] = []\n lines.push('# UI audit index', '')\n lines.push(`_Generated: ${new Date().toISOString()}_`, '')\n lines.push(`**Total findings:** ${summary.total}`, '')\n lines.push('## By severity')\n for (const s of ['critical', 'high', 'med', 'low'] as const) {\n const n = summary.bySeverity[s] ?? 0\n if (n > 0) lines.push(`- **${s}** — ${n}`)\n }\n lines.push('', '## By lens')\n const lensEntries: [string, number][] = Object.entries(summary.byLens).map(([k, v]) => [\n k,\n v ?? 0,\n ])\n for (const [lens, n] of lensEntries.sort((a, b) => b[1] - a[1])) {\n lines.push(`- **${lens}** — ${n}`)\n }\n lines.push('', '## By route')\n const routeEntries: [string, number][] = Object.entries(summary.byRoute).map(([k, v]) => [\n k,\n v ?? 0,\n ])\n for (const [route, n] of routeEntries.sort((a, b) => b[1] - a[1])) {\n lines.push(`- \\`${route}\\` — ${n}`)\n }\n lines.push('', '## Findings', '')\n for (const f of sorted) {\n if (typeof f.id !== 'number') continue\n const num = String(f.id).padStart(3, '0')\n const slug = slugifyTitle(f.title)\n const file = `issues/${num}--${f.lens}--${slug}.md`\n lines.push(`- [#${num} \\`${f.severity}\\` \\`${f.lens}\\` \\`${f.route}\\` — ${f.title}](${file})`)\n }\n lines.push('')\n\n const out = path.join(workspaceDir, 'index.md')\n const body = lines.join('\\n')\n await fs.writeFile(out, body)\n return out\n}\n","/**\n * @experimental\n *\n * Shared slug helper used by both the in-process auditor client (for\n * screenshot filenames) and the issue writer (for issue Markdown\n * filenames). Lowercases, normalizes, strips non-alphanumeric, trims\n * dashes, caps at 80 chars. Throws when the result is empty so callers\n * never silently write to a name-less filename.\n */\n\n/** @experimental */\nexport function slugify(value: string, fieldName: string): string {\n const slug = value\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80)\n if (slug.length === 0) {\n throw new Error(\n `ui-auditor: ${fieldName} slugified to empty string. Provide a non-empty value (got ${JSON.stringify(value)}).`,\n )\n }\n return slug\n}\n","/**\n * @experimental\n *\n * Prompt formatter for the auditor profile. `formatAuditorPrompt` produces\n * the user message handed to the iteration — describes the captures to be\n * taken and the lens to apply. The system prompt comes from\n * `buildAuditorSystemPrompt(lens)` (lens-prompts.ts).\n *\n * The formatter prepends a machine-readable envelope (`<<UI_AUDIT_TASK>>`\n * … `<<UI_AUDIT_TASK_END>>`) carrying a JSON-serialised task. The\n * in-process auditor client recovers the task from this envelope so the\n * iteration is self-describing — robust to concurrent fanout, where any\n * per-client side state (e.g. a \"current task\" register) would race.\n *\n * The formatter is pure and deterministic — re-run on the same task\n * produces the same prompt. Tests and trace replays rely on this.\n */\n\nimport type { UiAuditTask } from './task'\n\nconst ENVELOPE_BEGIN = '<<UI_AUDIT_TASK>>'\nconst ENVELOPE_END = '<<UI_AUDIT_TASK_END>>'\n\n/** @experimental */\nexport function encodeAuditTaskEnvelope(task: UiAuditTask): string {\n return `${ENVELOPE_BEGIN}${JSON.stringify(task)}${ENVELOPE_END}`\n}\n\n/**\n * Parse a task envelope back out of a prompt string. Returns undefined if\n * the prompt does not contain a complete envelope OR if the payload is\n * not valid JSON.\n *\n * @experimental\n */\nexport function decodeAuditTaskEnvelope(prompt: string): UiAuditTask | undefined {\n const start = prompt.indexOf(ENVELOPE_BEGIN)\n if (start === -1) return undefined\n const payloadStart = start + ENVELOPE_BEGIN.length\n const end = prompt.indexOf(ENVELOPE_END, payloadStart)\n if (end === -1) return undefined\n const payload = prompt.slice(payloadStart, end)\n try {\n const parsed = JSON.parse(payload) as unknown\n if (!parsed || typeof parsed !== 'object') return undefined\n const t = parsed as Partial<UiAuditTask>\n if (typeof t.lens !== 'string' || !Array.isArray(t.captures)) return undefined\n return t as UiAuditTask\n } catch {\n return undefined\n }\n}\n\n/** @experimental */\nexport function formatAuditorPrompt(task: UiAuditTask): string {\n const lines: string[] = []\n lines.push(`# UI audit iteration — lens: ${task.lens}`)\n lines.push('')\n if (task.productContext && task.productContext.trim().length > 0) {\n lines.push('## Product context')\n lines.push(task.productContext.trim())\n lines.push('')\n }\n lines.push('## Captures to take')\n task.captures.forEach((cap, i) => {\n const vp = cap.viewport ? `${cap.viewport.width}x${cap.viewport.height}` : '1280x800 (default)'\n const detail = [\n `viewport=${vp}`,\n cap.fullPage ? 'fullPage=true' : null,\n cap.elementSelector ? `selector=\\`${cap.elementSelector}\\`` : null,\n cap.waitFor ? `waitFor=\\`${cap.waitFor}\\`` : null,\n cap.waitMs !== undefined ? `waitMs=${cap.waitMs}` : null,\n cap.label ? `label=${cap.label}` : null,\n ]\n .filter((s): s is string => s !== null)\n .join(' · ')\n lines.push(`${i + 1}. route=\\`${cap.route}\\` url=${cap.url} ${detail ? `(${detail})` : ''}`)\n })\n lines.push('')\n if (task.knownFindingIds && task.knownFindingIds.length > 0) {\n lines.push('## Known findings (link via similarTo, do not refile)')\n lines.push(task.knownFindingIds.map((n) => `#${String(n).padStart(3, '0')}`).join(', '))\n lines.push('')\n }\n lines.push('## Output format')\n lines.push(\n 'Emit a single JSON object with the shape `{ findings: UiFinding[], notes?: string }` where every finding has the fields enumerated in your system prompt. The screenshots field on each finding must reference the captures above by path. Do not emit findings outside the lens.',\n )\n return lines.join('\\n')\n}\n","/**\n * @experimental\n *\n * `createInProcessUiAuditClient` — a `SandboxClient` that drives a\n * Playwright browser in-process and delegates finding identification to a\n * consumer-supplied {@link UiJudge}.\n *\n * Why this exists: `runLoop` is built around a sandbox-SDK seam — each\n * iteration is `client.create() → box.streamPrompt() → box.delete()`.\n * For UI audit, spinning up a real container running a coding harness\n * per iteration is overkill: the work is one browser capture + one\n * vision LLM call. This client satisfies the kernel contract while\n * doing the audit in-process; no container, no sandbox-SDK backend.\n *\n * The client owns ONE browser for its lifetime and creates a fresh\n * context per iteration (isolated cookies/storage). Playwright is\n * dynamically imported so consumers who use a different `SandboxClient`\n * — e.g. a fleet executor that drives Playwright remotely — do not pay\n * the peer dep cost.\n *\n * Concurrency: each iteration's prompt carries a self-describing task\n * envelope (see `prompt.ts`), so concurrent fanout iterations do not race\n * over per-client side state.\n */\n\nimport type { CreateSandboxOptions, SandboxEvent, SandboxInstance } from '@tangle-network/sandbox'\nimport type { SandboxClient } from '../../runtime/types'\nimport type { UiJudge } from './judge'\nimport { decodeAuditTaskEnvelope } from './prompt'\nimport { slugify } from './slugify'\nimport type { UiAuditCapture, UiAuditCaptureRequest } from './task'\n\n// All synthetic events the auditor emits flow through this helper. Reason:\n// `SandboxEvent.data` is a sandbox-SDK shape (effectively `Record<string,\n// unknown>`) that our typed payloads (`UiAuditCapture`, `UiFinding`, …) do not\n// satisfy structurally. The cast moves the type-system smell into a single,\n// named, documented call site so the call sites in `runIteration` stay clean.\n// The runtime contract — `{ type, data }` — is what the output adapter reads;\n// the static type is what the kernel collects into `SandboxEvent[]`.\nfunction asSandboxEvent<T>(type: string, data: T): SandboxEvent {\n return { type, data } as unknown as SandboxEvent\n}\n\n/** @experimental */\nexport interface InProcessUiAuditClientOptions {\n /**\n * Absolute path under which screenshots are written. Each capture lands\n * at `<workspaceDir>/screenshots/<filename>`; finding screenshot paths\n * are workspace-relative (`screenshots/<filename>`).\n */\n workspaceDir: string\n /** The vision judge that turns captures into findings. */\n judge: UiJudge\n /**\n * Navigation policy.\n *\n * `'strict'` (default) waits for `networkidle` and fails the iteration\n * if the page does not settle. `'spa'` waits for `domcontentloaded` —\n * use for single-page apps that hold open long-poll/websocket\n * connections and never settle.\n */\n navPolicy?: 'strict' | 'spa'\n /**\n * Browser launch override. Default: chromium headless via Playwright.\n * Consumers pass a custom factory to target a remote browser, a\n * different channel, or a fleet adapter.\n */\n launchBrowser?: () => Promise<BrowserHandle>\n}\n\n/** @experimental */\nexport interface BrowserHandle {\n newContext(options?: {\n viewport?: { width: number; height: number }\n }): Promise<BrowserContextHandle>\n close(): Promise<void>\n}\n\n/** @experimental */\nexport interface BrowserContextHandle {\n newPage(): Promise<PageHandle>\n close(): Promise<void>\n}\n\n/** @experimental */\nexport interface PageHandle {\n setViewportSize(size: { width: number; height: number }): Promise<void>\n goto(url: string, options?: { waitUntil?: string; timeout?: number }): Promise<unknown>\n waitForSelector(selector: string, options?: { timeout?: number }): Promise<unknown>\n waitForTimeout(ms: number): Promise<void>\n screenshot(options: { path: string; fullPage?: boolean }): Promise<void>\n locator(selector: string): {\n first(): { screenshot(options: { path: string }): Promise<void> }\n }\n}\n\nconst DEFAULT_VIEWPORT = { width: 1280, height: 800 } as const\nconst NAV_TIMEOUT_MS = 30_000\n\nasync function defaultLaunch(): Promise<BrowserHandle> {\n const mod = (await import('playwright')) as unknown as {\n chromium?: { launch(options?: { headless?: boolean }): Promise<BrowserHandle> }\n }\n if (!mod?.chromium || typeof mod.chromium.launch !== 'function') {\n throw new Error(\n 'ui-auditor: playwright is not installed. Install `playwright` (and run `playwright install chromium`) or pass a custom `launchBrowser` to createInProcessUiAuditClient.',\n )\n }\n return mod.chromium.launch({ headless: true })\n}\n\nfunction nowStamp(): string {\n const d = new Date()\n const pad = (n: number) => String(n).padStart(2, '0')\n // Millisecond resolution so parallel fanout iterations capturing the same\n // route/viewport/label within the same second don't collide on filename and\n // silently overwrite each other.\n return (\n `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}-` +\n `${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}` +\n `-${String(d.getUTCMilliseconds()).padStart(3, '0')}`\n )\n}\n\nfunction viewportOf(req: UiAuditCaptureRequest): { width: number; height: number } {\n return req.viewport ?? DEFAULT_VIEWPORT\n}\n\nfunction captureFilename(req: UiAuditCaptureRequest): string {\n const vp = viewportOf(req)\n const labelPart = req.label ? `--${slugify(req.label, 'label')}` : ''\n return `${slugify(req.route, 'route')}--${vp.width}x${vp.height}${labelPart}--${nowStamp()}.png`\n}\n\nfunction assertHttpUrl(url: string): void {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n throw new Error(`ui-auditor: capture url is not parseable (got ${JSON.stringify(url)})`)\n }\n // SSRF defense at the client boundary. The MCP tool already restricts to\n // http(s), but `createInProcessUiAuditClient` is exported and can be wired\n // up directly by consumers (the example does this). A crafted task envelope\n // could otherwise navigate Playwright to `file://`, `data:`, `javascript:`\n // and read local files or execute inline content.\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(\n `ui-auditor: capture url must use http or https (got ${parsed.protocol} in ${JSON.stringify(url)})`,\n )\n }\n}\n\nasync function captureOne(\n page: PageHandle,\n req: UiAuditCaptureRequest,\n outAbsPath: string,\n signal: AbortSignal,\n navPolicy: 'strict' | 'spa',\n): Promise<void> {\n signal.throwIfAborted()\n assertHttpUrl(req.url)\n // Apply the per-capture viewport before navigation. The capture metadata\n // and filename both encode this viewport; the rendered page must match.\n await page.setViewportSize(viewportOf(req))\n const waitUntil = navPolicy === 'spa' ? 'domcontentloaded' : 'networkidle'\n await page.goto(req.url, { waitUntil, timeout: NAV_TIMEOUT_MS })\n if (req.waitFor) {\n await page.waitForSelector(req.waitFor, { timeout: 15_000 })\n }\n const extra = req.waitMs ?? 500\n if (extra > 0) await page.waitForTimeout(extra)\n signal.throwIfAborted()\n if (req.elementSelector) {\n await page.locator(req.elementSelector).first().screenshot({ path: outAbsPath })\n } else {\n await page.screenshot({ path: outAbsPath, fullPage: req.fullPage === true })\n }\n}\n\ninterface SyntheticSandbox extends SandboxInstance {}\n\nfunction makeSandboxId(): string {\n const rand = () => Math.random().toString(16).slice(2, 10)\n return `ui-audit-${rand()}${rand()}`\n}\n\n/** @experimental */\nexport function createInProcessUiAuditClient(\n options: InProcessUiAuditClientOptions,\n): SandboxClient & {\n /**\n * Close the underlying browser. Idempotent.\n *\n * Contract: callers MUST ensure no iterations are in flight when this is\n * called. The kernel respects this — `runLoop` awaits every iteration\n * before returning, so `await runLoop(...); await client.close()` is the\n * intended pattern (see `examples/ui-audit`). If `close()` is invoked\n * concurrently with a running iteration, the browser teardown will race\n * against in-flight page operations; the iteration will surface an\n * AggregateError carrying both the iteration error and the close error,\n * but no work is lost silently.\n */\n close(): Promise<void>\n} {\n const launch = options.launchBrowser ?? defaultLaunch\n const navPolicy = options.navPolicy ?? 'strict'\n let browserPromise: Promise<BrowserHandle> | undefined\n let closed = false\n\n async function getBrowser(): Promise<BrowserHandle> {\n if (closed) {\n throw new Error('ui-auditor: client is closed; create a new client to run another iteration')\n }\n if (!browserPromise) browserPromise = launch()\n return browserPromise\n }\n\n async function* runIteration(\n promptText: string,\n signal: AbortSignal,\n ): AsyncIterable<SandboxEvent> {\n const task = decodeAuditTaskEnvelope(promptText)\n if (!task) {\n throw new Error(\n 'ui-auditor: prompt is missing a UI_AUDIT_TASK envelope. Use uiAuditorProfile().taskToPrompt to format prompts, or pass an envelope-prefixed prompt manually.',\n )\n }\n if (task.captures.length === 0) {\n throw new Error('ui-auditor: task has zero captures; nothing to audit.')\n }\n\n yield asSandboxEvent('audit.lens', { lens: task.lens })\n\n const browser = await getBrowser()\n const context = await browser.newContext({ viewport: DEFAULT_VIEWPORT })\n // Track both the primary iteration error and any context-close failure so\n // the cleanup path never silently swallows a leaked-context bug AND a\n // close failure never shadows the real iteration error. After the\n // try/catch/finally settles, we rethrow the primary if there was one,\n // otherwise we rethrow the close error.\n let primaryError: unknown\n let closeError: unknown\n try {\n const page = await context.newPage()\n const captures: UiAuditCapture[] = []\n const fs = await import('node:fs/promises')\n const path = await import('node:path')\n const shotsDir = path.join(options.workspaceDir, 'screenshots')\n await fs.mkdir(shotsDir, { recursive: true })\n\n for (const req of task.captures) {\n signal.throwIfAborted()\n const filename = captureFilename(req)\n const absPath = path.join(shotsDir, filename)\n const relPath = `screenshots/${filename}`\n await captureOne(page, req, absPath, signal, navPolicy)\n const vp = viewportOf(req)\n const cap: UiAuditCapture = {\n path: relPath,\n viewport: `${vp.width}x${vp.height}`,\n fullPage: req.fullPage === true,\n route: req.route,\n url: req.url,\n capturedAt: new Date().toISOString(),\n }\n if (req.elementSelector) cap.elementSelector = req.elementSelector\n if (req.label) cap.label = req.label\n captures.push(cap)\n yield asSandboxEvent('audit.capture', cap)\n }\n\n const judgeOut = await options.judge({\n lens: task.lens,\n captures,\n productContext: task.productContext,\n knownFindingIds: task.knownFindingIds,\n promptText,\n signal,\n })\n\n for (const finding of judgeOut.findings) {\n yield asSandboxEvent('audit.finding', finding)\n }\n if (judgeOut.notes && judgeOut.notes.trim().length > 0) {\n yield asSandboxEvent('audit.notes', { notes: judgeOut.notes })\n }\n\n const usage = judgeOut.tokenUsage ?? { input: 0, output: 0 }\n yield asSandboxEvent('done', {\n tokenUsage: {\n inputTokens: usage.input,\n outputTokens: usage.output,\n },\n totalCostUsd: judgeOut.costUsd ?? 0,\n })\n } catch (err) {\n primaryError = err\n } finally {\n try {\n await context.close()\n } catch (err) {\n closeError = err\n }\n }\n // When both the iteration and the cleanup fail, surface both via\n // AggregateError so a leaked context bug is not silently masked by an\n // earlier iteration failure (per the fail-loud doctrine).\n if (primaryError !== undefined && closeError !== undefined) {\n throw new AggregateError(\n [primaryError, closeError],\n 'ui-auditor: iteration failed AND context.close() failed; both errors attached.',\n )\n }\n if (primaryError !== undefined) throw primaryError\n if (closeError !== undefined) throw closeError\n }\n\n function makeSyntheticSandbox(): SyntheticSandbox {\n const id = makeSandboxId()\n const instance = {\n id,\n streamPrompt(message: string, opts?: { signal?: AbortSignal }): AsyncIterable<SandboxEvent> {\n const signal = opts?.signal ?? new AbortController().signal\n return runIteration(message, signal)\n },\n async delete(): Promise<void> {\n // No per-sandbox resources to release; the browser is shared and\n // closed by `client.close()`. Intentionally a no-op so trace-time\n // `box.delete()` succeeds without doing surprising work.\n },\n }\n return instance as unknown as SyntheticSandbox\n }\n\n return {\n async create(_options?: CreateSandboxOptions) {\n return makeSyntheticSandbox()\n },\n describePlacement(box) {\n const id = (box as unknown as { id?: string }).id\n return { kind: 'sibling', sandboxId: typeof id === 'string' ? id : undefined }\n },\n async close() {\n closed = true\n const pending = browserPromise\n browserPromise = undefined\n if (pending) {\n const browser = await pending\n await browser.close()\n }\n },\n }\n}\n","/**\n * @experimental\n *\n * Per-lens guidance the auditor inlines into its system prompt for an\n * iteration. Each entry is a self-contained brief — the same content the\n * standalone ui-issue-finder skill ships, embedded as a string constant so\n * agent-runtime carries no runtime dep on that external workspace.\n *\n * Briefs are deliberately concrete: they enumerate the SIGNALS to look for\n * and the cross-lens distinctions to respect, so the judge files fewer\n * pile-on findings under generic labels.\n */\n\nimport type { UiLens } from './substrate'\n\n/** @experimental */\nexport const SHARED_AUDITOR_RULES = `\nYou are auditing a UI for a specific class of problems. Stay strictly in your assigned lens — do not file issues that belong to another lens (a separate iteration will catch those).\n\nA finding is only valid if a thoughtful product designer would agree the screenshot shows something that should change. Avoid:\n- Personal taste (\"I'd prefer brand blue\").\n- Hallucinated text or controls you cannot actually see in the screenshot.\n- Suggestions that depend on requirements you don't have access to.\n- Pile-on findings about the same root cause — file ONE finding and use \\`similarTo\\` to link the rest.\n\nRequired for every finding:\n- title: concrete, names the offending element AND what's wrong (NOT \"improve UX\").\n- severity: critical=blocks a core task or accessibility blocker; high=noticeable friction; med=visible polish issue; low=nitpick.\n- observation: 1–3 sentences describing exactly what you see that is wrong.\n- impact: who is affected and how (concrete).\n- suggestedFix: a specific change a developer could apply without asking you back.\n- screenshots: refer to the captures attached to this iteration by path.\n- selector: when you can pin the offending element with a CSS selector.\n\nMost findings are med or low. Reserve high/critical for genuine blockers.\n`.trim()\n\n/** @experimental */\nexport const LENS_BRIEFS: Record<UiLens, string> = {\n consistency: `\nLENS: consistency\nLook for inconsistencies in the design system — things that look like they came from different products glued together.\nSignals: multiple font families, inconsistent weights/sizes for the same role, two shades of \"primary\", arbitrary paddings/margins that don't snap to a scale (4/8/12/16/24), same control with different border-radius or shadow on different pages, mixed icon styles (filled vs outlined), inconsistent button heights/padding for the same variant, inconsistent capitalization (Title Case vs sentence case) for the same role.\nNOT this lens: layout misalignment (use \\`layout\\`), confusing user flow (use \\`ux-flow\\`), contrast/keyboard issues (use \\`accessibility\\`).\nTitle format: \\`Inconsistent <thing> between <A> and <B>\\`.\n`.trim(),\n hierarchy: `\nLENS: hierarchy\nLook for broken visual hierarchy — places where the eye does not land on what matters most.\nSignals: primary CTA same weight as secondary/tertiary controls, headings (H1/H2/H3) nearly the same size, important data buried (headline number smaller than its label), decoration outshining content, too many emphases competing, wrong scan order, missing or overly heavy section dividers.\nNOT this lens: same-role styled differently (\\`consistency\\`), grid/alignment (\\`layout\\`), contrast-failing text (\\`accessibility\\`).\nTitle format: \\`Weak hierarchy: <element> does not read as the <intended-role>\\`.\n`.trim(),\n layout: `\nLENS: layout\nLook for layout and organization problems — alignment, grouping, whitespace, structural choices that hurt scannability.\nSignals: misalignment within rows, inconsistent gutters in grids, orphan whitespace next to crammed regions, poor grouping (related fields separated, unrelated fields adjacent), no visual sections (long wall of content), container overflow (text/content punching out of card boundaries), cramped or oversized hit targets, sidebars/headers sized wrong relative to main content.\nNOT this lens: same-role styled differently (\\`consistency\\`), click-distance/friction (\\`ux-flow\\`), overflow specifically at small viewports (\\`responsive\\`).\nTitle format: \\`<Region> alignment/spacing problem\\` or \\`<Region> grouping unclear\\`.\n`.trim(),\n 'ux-flow': `\nLENS: ux-flow\nLook for interaction-flow friction — action sequences that are slower, more annoying, or more error-prone than necessary.\nSignals: sequential clicks far apart (e.g. Next top-right while user is bottom-left), destructive action adjacent to primary with same weight, confirmations that don't say what's being confirmed, primary CTA below the fold or hidden in a kebab menu, silent state changes (toggle gives no feedback), form ordering that fights real-world order, dead-end states after submit, lost inputs on back-navigation, hidden pre-selected options.\nNOT this lens: visual style only (\\`consistency\\`), component arrangement without a flow problem (\\`layout\\`), microcopy clarity (\\`content\\`).\nTitle format: \\`<Action A> → <Action B> friction: <root cause>\\`.\n`.trim(),\n duplication: `\nLENS: duplication\nLook for redundancy — the same control, link, or piece of content appearing more than once with no good reason.\nSignals: two ways to do the same action on the same screen with no difference, repeated nav (same links in sidebar AND top nav), drifted duplicates (two copies that have diverged), content repeated verbatim, icon + label saying the same thing twice in one row, per-row + bulk actions that overlap confusingly, multiple status indicators conveying the same status.\nNOT this lens: inconsistent styling of duplicates (\\`consistency\\`) — this lens is about the existence of duplicates.\nTitle format: \\`Duplicate <thing> in <location A> and <location B>\\`.\n`.trim(),\n accessibility: `\nLENS: accessibility\nLook for accessibility blockers and degradations. Be conservative — do not assume violations you cannot see.\nSignals: insufficient contrast on body text or controls, missing/invisible focus styles, tiny tap targets (<24px on mobile), color as sole signal (red border with no message), form labels missing or not associated (placeholders standing in for labels), broken heading order (H1 → H4), modals that don't trap focus, decorative elements that take focus, errors not announced, important text rendered inside images.\nNOT this lens: generic \"looks confusing\" (\\`hierarchy\\` or \\`content\\`), layout overflow at small viewports (\\`responsive\\`).\nTitle format: \\`Accessibility: <specific blocker> in <element>\\`.\n`.trim(),\n responsive: `\nLENS: responsive\nLook for layout breakage across viewport sizes — content that works at one width but degrades at another. This iteration's captures should include the same surface at >=2 viewports; compare across them.\nSignals: horizontal scroll where content should reflow, overlapping elements (header overlaps content, fixed footer covers inputs), desktop nav crammed into mobile without collapsing, table columns that don't truncate, tap targets too close at touch sizes, controls vanishing at certain widths, layout flips that break grouping order, modals exceeding viewport height (confirm button unreachable).\nNOT this lens: issues present at every viewport (\\`consistency\\` / \\`hierarchy\\` instead).\nTitle format: \\`<Element/Region> breaks at <viewport>\\`.\n`.trim(),\n states: `\nLENS: states\nLook for missing or broken UI states — the not-happy-paths that make a product feel finished or unfinished. The iteration's captures should depict at least one non-default state.\nSignals: empty lists with no guidance, skeletons that don't match final layout (CLS on settle), error states with no message or recovery action, disabled buttons with no explanation, toasts that disappear before being read, success states that don't confirm, missing hover/focus/active/disabled variants on primary controls, no long-content view, no-permission state broken.\nNOT this lens: generic polish on the happy path (other lenses), missing focus rings specifically (\\`accessibility\\`).\nTitle format: \\`Missing/broken <state> state on <surface>\\`.\n`.trim(),\n content: `\nLENS: content\nLook for microcopy and content problems — text that is unclear, inconsistent, condescending, jargon-heavy, or wrong.\nSignals: jargon/internal language leaking (\"Provisioning a Tenant\" instead of \"Setting up your account\"), inconsistent terminology (workspace vs team), verbose button labels, empty-state copy that's just \"No results\", error messages blaming the user, tone inconsistency, truncation without affordance, mixed date/number formats on one page, placeholder used as a label, \"Saved!\" toast appearing before save completes, typos and grammar errors.\nNOT this lens: visual treatment of text (\\`hierarchy\\` / \\`consistency\\`), missing labels for a11y (\\`accessibility\\`).\nTitle format: \\`Copy: \"<actual text>\" in <location>\\` or \\`Inconsistent term: \"<A>\" vs \"<B>\"\\`.\n`.trim(),\n interaction: `\nLENS: interaction\nLook for interaction quality problems — affordances, feedback, and micro-interactions.\nSignals: no affordance (clickable areas not looking clickable, non-clickable areas looking clickable), missing feedback (>100ms click with no progress), hover surprises (whole row highlights but only title clickable), cursor inconsistency, animations that block input, missing transitions where they're needed (accordion snaps open), drag-and-drop without indicators, scroll-jacking, click-through bugs (card click handler firing alongside button), hover-only revelations on touch.\nNOT this lens: position of controls (\\`layout\\` / \\`ux-flow\\`), missing focus styles (\\`accessibility\\`).\nTitle format: \\`<Action> on <element>: <missing/wrong> feedback\\`.\n`.trim(),\n 'performance-perceived': `\nLENS: performance-perceived\nLook for perceived-performance problems — visible jank a real user would notice, not benchmark numbers. This iteration's captures should include >=2 frames during load to show shift.\nSignals: layout shift (CLS) when late-arriving images/fonts/banners settle, FOUC (flash of unstyled content), font swap jumps, late-loading hero images that shift everything, skeletons that don't match final shape, spinners on instant local actions, loading state reappearing after content paints (refetch on focus), modal open animation longer than the operation it precedes.\nNOT this lens: slow API calls (file separately), stale data after navigation (\\`states\\`).\nTitle format: \\`Layout shift / late paint on <route>: <root cause>\\`.\n`.trim(),\n other: `\nLENS: other\nUse ONLY when a finding is clearly a UI quality issue but does not fit any other lens. Strongly prefer a specific lens — \\`other\\` should be rare. Title must still be concrete.\n`.trim(),\n}\n\n/**\n * Build a system prompt for a single auditor iteration.\n *\n * @experimental\n */\nexport function buildAuditorSystemPrompt(lens: UiLens): string {\n const brief = LENS_BRIEFS[lens]\n return `${SHARED_AUDITOR_RULES}\\n\\n${brief}`\n}\n","/**\n * @experimental\n *\n * Sandbox-event stream → UiAuditOutput decoder. The custom auditor\n * `SandboxClient` emits events of the form:\n *\n * { type: 'audit.capture', data: UiAuditCapture }\n * { type: 'audit.finding', data: UiFinding }\n * { type: 'audit.notes', data: { notes: string } }\n * { type: 'audit.lens', data: { lens: UiLens } }\n * { type: 'done', data: { tokenUsage: { ... }, totalCostUsd?: number } }\n *\n * Other event types are tolerated and ignored. The adapter is pure: it\n * folds an already-collected event array into a UiAuditOutput.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport { UI_LENSES, type UiFinding, type UiLens } from './substrate'\nimport type { UiAuditCapture, UiAuditOutput } from './task'\n\n// Build the lens-validation set from the canonical UI_LENSES tuple so adding\n// a lens to the substrate automatically extends the parser; otherwise a new\n// lens would silently fail isUiLens() and parseAuditorEvents would drop\n// every event using it.\nconst KNOWN_LENS_VALUES = new Set<UiLens>(UI_LENSES)\n\nfunction isUiLens(v: unknown): v is UiLens {\n return typeof v === 'string' && KNOWN_LENS_VALUES.has(v as UiLens)\n}\n\n/** @experimental */\nexport function parseAuditorEvents(events: SandboxEvent[]): UiAuditOutput {\n const findings: UiFinding[] = []\n const captures: UiAuditCapture[] = []\n let lens: UiLens | undefined\n let notes: string | undefined\n\n for (const evt of events) {\n if (!evt || typeof evt !== 'object') continue\n const type = String(evt.type ?? '')\n const data =\n evt.data && typeof evt.data === 'object' ? (evt.data as Record<string, unknown>) : undefined\n if (!data) continue\n\n switch (type) {\n case 'audit.lens': {\n const v = data.lens\n if (isUiLens(v)) lens = v\n break\n }\n case 'audit.capture': {\n const cap = data as unknown as Partial<UiAuditCapture>\n if (\n typeof cap.path === 'string' &&\n typeof cap.viewport === 'string' &&\n typeof cap.fullPage === 'boolean' &&\n typeof cap.route === 'string' &&\n typeof cap.url === 'string' &&\n typeof cap.capturedAt === 'string'\n ) {\n const out: UiAuditCapture = {\n path: cap.path,\n viewport: cap.viewport,\n fullPage: cap.fullPage,\n route: cap.route,\n url: cap.url,\n capturedAt: cap.capturedAt,\n }\n if (cap.elementSelector) out.elementSelector = cap.elementSelector\n if (cap.label) out.label = cap.label\n captures.push(out)\n }\n break\n }\n case 'audit.finding': {\n const f = data as unknown as Partial<UiFinding>\n // Hard requirement: all the actionable fields must be present and\n // non-empty for a finding to enter the output. The validator does the\n // softer scoring; the adapter only filters structural junk.\n if (\n typeof f.title === 'string' &&\n f.title.trim().length > 0 &&\n isUiLens(f.lens) &&\n typeof f.severity === 'string' &&\n ['low', 'med', 'high', 'critical'].includes(f.severity) &&\n typeof f.route === 'string' &&\n typeof f.observation === 'string' &&\n typeof f.impact === 'string' &&\n typeof f.suggestedFix === 'string' &&\n Array.isArray(f.screenshots)\n ) {\n findings.push(f as UiFinding)\n }\n break\n }\n case 'audit.notes': {\n const n = data.notes\n if (typeof n === 'string' && n.trim().length > 0) notes = n\n break\n }\n default:\n // Tolerate cost/usage events and other backend chatter — extractLlmCallEvent\n // in run-loop.ts handles cost accounting upstream from the adapter.\n break\n }\n }\n\n const out: UiAuditOutput = { lens: lens ?? 'other', findings, captures }\n if (notes) out.notes = notes\n return out\n}\n","/**\n * @experimental\n *\n * Auditor validator — scores a single iteration's findings for actionability\n * and gates the iteration result. The kernel uses `valid` + `score` for\n * winner selection across fanned-out iterations and to detect a degenerate\n * iteration (lens-violating findings, no screenshot evidence, no findings\n * at all on a route where we expected some).\n *\n * Hard fails (`valid = false`):\n * - A finding is filed under a lens that does not match the iteration's\n * lens. The whole iteration is bad — the judge isn't following the\n * lens discipline and the resulting Markdown would mislead reviewers.\n * - A finding has no screenshot reference.\n * - A finding's screenshot references a path that wasn't captured in\n * this iteration.\n *\n * Score (0..1, max two decimals stable):\n * - 0.4 * specificityRatio — proportion of findings with a selector\n * - 0.4 * evidenceRatio — proportion of findings whose screenshots resolve to captures\n * - 0.2 * (1 - genericTitleRatio) — proportion of findings whose titles\n * are concrete (not \"improve UX\", \"fix layout\", etc.)\n *\n * An iteration with zero findings scores 0.5 by convention — neither a\n * confident pass nor a hard failure (the judge might just have nothing to\n * say on this lens). The driver decides what to do with it.\n */\n\nimport type { DefaultVerdict } from '@tangle-network/agent-eval'\nimport type { Validator } from '../../runtime/types'\nimport type { UiAuditOutput, UiAuditTask } from './task'\n\nconst GENERIC_TITLE_PATTERNS = [\n /^improve\\s/i,\n /^fix\\s/i,\n /^update\\s/i,\n /^better\\s/i,\n /^bad\\s/i,\n /^make\\s.+\\sbetter/i,\n /\\bUX\\b\\s*$/i,\n /\\bUI\\b\\s*$/i,\n]\n\nfunction isGenericTitle(title: string): boolean {\n const t = title.trim()\n if (t.length < 16) return true\n return GENERIC_TITLE_PATTERNS.some((re) => re.test(t))\n}\n\n/** @experimental */\nexport function createUiAuditorValidator(task: UiAuditTask): Validator<UiAuditOutput> {\n return {\n async validate(output) {\n const findings = output.findings\n const captures = output.captures\n const capturePaths = new Set(captures.map((c) => c.path))\n\n const offLens = findings.filter((f) => f.lens !== task.lens)\n if (offLens.length > 0) {\n const verdict: DefaultVerdict = {\n valid: false,\n score: 0,\n notes: `${offLens.length} finding(s) filed under wrong lens (expected ${task.lens}; got ${offLens.map((f) => f.lens).join(', ')})`,\n scores: { offLens: 0 },\n }\n return verdict\n }\n\n const missingEvidence = findings.filter(\n (f) => !Array.isArray(f.screenshots) || f.screenshots.length === 0,\n )\n if (missingEvidence.length > 0) {\n const verdict: DefaultVerdict = {\n valid: false,\n score: 0,\n notes: `${missingEvidence.length} finding(s) have no screenshot evidence`,\n scores: { evidence: 0 },\n }\n return verdict\n }\n\n const unresolvedShot = findings.filter((f) =>\n f.screenshots.some((s) => !capturePaths.has(s.path)),\n )\n if (unresolvedShot.length > 0) {\n const verdict: DefaultVerdict = {\n valid: false,\n score: 0,\n notes: `${unresolvedShot.length} finding(s) reference screenshot paths not captured this iteration`,\n scores: { evidence: 0 },\n }\n return verdict\n }\n\n if (findings.length === 0) {\n const verdict: DefaultVerdict = {\n valid: true,\n score: 0.5,\n notes: 'No findings reported. Neither a confident pass nor a failure.',\n scores: { specificity: 0, evidence: 1, titles: 1 },\n }\n return verdict\n }\n\n const withSelector = findings.filter((f) => typeof f.selector === 'string').length\n const specificity = withSelector / findings.length\n const generic = findings.filter((f) => isGenericTitle(f.title)).length\n const titles = 1 - generic / findings.length\n // Compute evidence honestly from the data: proportion of findings whose\n // screenshots are all resolvable against this iteration's captures. The\n // guards above hard-fail when this would be < 1, so today the result is\n // always 1; if a future change relaxes those guards into a soft-fail\n // mode, this still produces a truthful evidence ratio rather than a\n // stale constant inflating the score.\n const withFullEvidence = findings.filter(\n (f) =>\n Array.isArray(f.screenshots) &&\n f.screenshots.length > 0 &&\n f.screenshots.every((s) => capturePaths.has(s.path)),\n ).length\n const evidence = withFullEvidence / findings.length\n const score = Number((0.4 * specificity + 0.4 * evidence + 0.2 * titles).toFixed(4))\n\n const verdict: DefaultVerdict = {\n valid: true,\n score,\n notes: `${findings.length} finding(s) — specificity=${specificity.toFixed(2)} evidence=${evidence.toFixed(2)} titles=${titles.toFixed(2)}`,\n scores: { specificity, evidence, titles },\n }\n return verdict\n },\n }\n}\n","/**\n * @experimental\n *\n * `uiAuditorProfile` — preset for vision-driven UI audit iterations.\n *\n * A `runLoop` bundle: it returns the `AgentRunSpec`, output adapter, validator, and prompt\n * formatter the loop kernel needs. The agent's \"harness\" is not a sandbox-SDK code-runner — it's a\n * vision-capable judge driving a browser. The loop kernel still iterates\n * `client.create() → box.streamPrompt() → box.delete()`; the client/box pair are provided by\n * `createInProcessUiAuditClient` (in `./in-process-client.ts`) or a consumer-supplied `SandboxClient`.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type { AgentRunSpec, OutputAdapter, Validator } from '../../runtime/types'\nimport { buildAuditorSystemPrompt } from './lens-prompts'\nimport { parseAuditorEvents } from './output-adapter'\nimport { encodeAuditTaskEnvelope, formatAuditorPrompt } from './prompt'\nimport type { UiAuditOutput, UiAuditTask } from './task'\nimport { createUiAuditorValidator } from './validator'\n\n/** @experimental */\nexport interface UiAuditorProfileOptions {\n /**\n * Stable name surfaced in trace events. Defaults to `ui-auditor`.\n */\n name?: string\n /**\n * Optional model identifier passed in `AgentProfile.model.default`.\n * The consumer's `SandboxClient` chooses how to interpret it.\n */\n model?: string\n /**\n * Task bound to the validator. Without it the validator uses the lens\n * embedded in the iteration output as its expectation — fine for one-off\n * use; less strict than passing the task explicitly.\n */\n task?: UiAuditTask\n}\n\n/** @experimental */\nexport function uiAuditorProfile(options: UiAuditorProfileOptions = {}): {\n profile: AgentProfile\n taskToPrompt: (task: UiAuditTask) => string\n output: OutputAdapter<UiAuditOutput>\n validator: Validator<UiAuditOutput>\n agentRunSpec: AgentRunSpec<UiAuditTask>\n} {\n const name = options.name ?? 'ui-auditor'\n\n // Lens is per-task; the profile's system prompt is filled in by the\n // taskToPrompt formatter at iteration time (prefixed to the user\n // message). Keeping the profile lens-agnostic lets one AgentRunSpec\n // serve every lens-iteration of the loop.\n const profile: AgentProfile = {\n name,\n description: 'Vision-driven UI auditor. One lens per iteration.',\n prompt: { systemPrompt: '' },\n model: options.model ? { default: options.model } : undefined,\n tools: { browser: true, vision: true },\n metadata: { role: 'ui-auditor' },\n }\n\n const output: OutputAdapter<UiAuditOutput> = { parse: parseAuditorEvents }\n const validator: Validator<UiAuditOutput> = options.task\n ? createUiAuditorValidator(options.task)\n : createUiAuditorValidator({ lens: 'other', captures: [] })\n\n // Prompt shape (consumed both by sandbox-SDK harnesses AND by the\n // in-process auditor client):\n // <<UI_AUDIT_TASK>>{json}<<UI_AUDIT_TASK_END>>\n // <system-prompt for the lens>\n // <human-readable iteration brief>\n // The envelope makes the iteration self-describing so concurrent fanout\n // does not race over per-client side state. Sandbox-SDK harnesses can\n // ignore the envelope; the in-process auditor client decodes it back\n // into a typed UiAuditTask via decodeAuditTaskEnvelope.\n const taskToPrompt = (task: UiAuditTask): string =>\n `${encodeAuditTaskEnvelope(task)}\\n${buildAuditorSystemPrompt(task.lens)}\\n\\n${formatAuditorPrompt(task)}`\n\n const agentRunSpec: AgentRunSpec<UiAuditTask> = {\n name,\n profile,\n taskToPrompt,\n }\n\n return { profile, taskToPrompt, output, validator, agentRunSpec }\n}\n"],"mappings":"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","names":["fs","path","out","verdict"]}
@@ -1,19 +1,13 @@
1
- import { AgentProfile } from '@tangle-network/agent-interface';
2
-
3
1
  /**
4
2
  * @experimental
5
3
  *
6
- * `coderProfile` — the §1.5 author-the-profile DATA for code-modification tasks: an `AgentProfile`
7
- * constant (the agent IS its profile) plus a pure `coderTaskToPrompt` formatter that renders a
8
- * `CoderTask` into the per-task instruction. There is no factory, output adapter, or validator
9
- * here a domain customizes the worker by authoring a profile + handing it to a leaf executor
10
- * (`createWorktreeCliExecutor`) or a fanout (`worktreeFanout`), and "is it delivered" is a
11
- * `DeliverableSpec` (`patchDelivered`), not a bundled validator.
12
- *
13
- * The standing instruction tells the agent to work on a fresh branch, keep the patch minimal,
14
- * avoid forbidden paths, and run the test + typecheck commands before declaring done.
4
+ * `CoderTask` + `coderTaskToPrompt` — the per-task DATA + pure formatter for code-modification tasks
5
+ * (§1.5: the system authors profiles; there is no hardcoded coder profile constant). A domain
6
+ * customizes the worker by authoring its own `AgentProfile` and handing it to a leaf executor
7
+ * (`createWorktreeCliExecutor`) or a fanout (`worktreeFanout`); "is it delivered" is a
8
+ * `DeliverableSpec` (`patchDelivered`), not a bundled validator. This formatter renders a `CoderTask`
9
+ * into the per-task instruction that profile receives.
15
10
  */
16
-
17
11
  /** @experimental The per-task inputs `coderTaskToPrompt` renders + the worktree gate enforces. */
18
12
  interface CoderTask {
19
13
  /** What the agent must accomplish. Free-form prose. */
@@ -36,16 +30,6 @@ interface CoderTask {
36
30
  /** Default 400. Hard cap; the gate hard-fails when exceeded. */
37
31
  maxDiffLines?: number;
38
32
  }
39
- /** @experimental The coder agent's standing instruction (its body lives in `coderProfile.prompt`). */
40
- declare const DEFAULT_CODER_SYSTEM_PROMPT: string;
41
- /**
42
- * @experimental
43
- *
44
- * The coder `AgentProfile` — the §1.5 DATA the substrate materializes into a harness invocation.
45
- * Stateless and harness-agnostic: a consumer overrides `model`/`metadata.backendType` by spreading
46
- * a copy, never by a factory. `worktreeFanout` authors one such profile per harness leaf.
47
- */
48
- declare const coderProfile: AgentProfile;
49
33
  /** @experimental Render a `CoderTask` into the per-task instruction handed to the coder profile. */
50
34
  declare function coderTaskToPrompt(task: CoderTask): string;
51
35
 
@@ -125,4 +109,4 @@ interface UiFinding {
125
109
  createdAt?: string;
126
110
  }
127
111
 
128
- export { type CoderTask as C, DEFAULT_CODER_SYSTEM_PROMPT as D, type UiFinding as U, type UiLens as a, UI_FINDING_SEVERITIES as b, UI_LENSES as c, type UiFindingScreenshot as d, type UiFindingSeverity as e, coderProfile as f, coderTaskToPrompt as g };
112
+ export { type CoderTask as C, type UiLens as U, type UiFinding as a, UI_FINDING_SEVERITIES as b, UI_LENSES as c, type UiFindingScreenshot as d, type UiFindingSeverity as e, coderTaskToPrompt as f };
@@ -763,9 +763,15 @@ interface AgentRunSpec<Task> {
763
763
  * files, or seed datasets. The hook is part of the runtime surface so loop
764
764
  * consumers do not hand-roll Sandbox SDK orchestration just to prepare a
765
765
  * workspace before the agent sees it.
766
+ *
767
+ * `ctx.recordMount` records what was placed into the box so the run carries a
768
+ * provenance manifest (`LoopResult.provenance.mounts`). It is optional and
769
+ * provenance-only — the kernel never reads box contents and attaches no
770
+ * meaning to the entries; not calling it simply leaves the manifest empty.
766
771
  */
767
772
  prepareBox?: (box: SandboxInstance, ctx: {
768
773
  signal: AbortSignal;
774
+ recordMount: MountRecorder;
769
775
  }) => Promise<void> | void;
770
776
  /**
771
777
  * Per-spec stable name. Surfaced in trace events and the default winner
@@ -801,6 +807,75 @@ interface LoopTokenUsage {
801
807
  input: number;
802
808
  output: number;
803
809
  }
810
+ /**
811
+ * One mounted resource recorded during box preparation — a pure provenance
812
+ * record of what the caller placed into a box before the agent saw it. The
813
+ * kernel never reads box contents itself (it does not know what was mounted);
814
+ * the caller, which owns the bytes inside `prepareBox`, supplies each entry via
815
+ * `recordMount`. Carries no domain semantics — just where the resource landed,
816
+ * its content fingerprint, its size, and where it came from — so a run is
817
+ * auditable after the fact ("what exactly was this agent given?").
818
+ *
819
+ * @experimental
820
+ */
821
+ interface MountManifestEntry {
822
+ /** Destination path inside the box where the resource was placed. */
823
+ path: string;
824
+ /** Hex SHA-256 of the mounted bytes. The caller computes it from the bytes
825
+ * it wrote — the kernel does not hash box contents. */
826
+ sha256: string;
827
+ /** Size of the mounted resource in bytes. */
828
+ bytes: number;
829
+ /** Free-form origin of the resource (e.g. a repo ref, a corpus id, a local
830
+ * path, a URL). Provenance only — the kernel attaches no meaning to it. */
831
+ source: string;
832
+ }
833
+ /**
834
+ * A record of one candidate-selection decision: which iteration the selector
835
+ * picked (or rejected) and why. Pure audit trail of the SELECTOR role — it
836
+ * carries the selector's identity, the candidate's score, and an optional
837
+ * human-readable reason, with no domain semantics. The kernel emits one receipt
838
+ * per scored candidate at finalize so a run answers "why did THIS one win?".
839
+ *
840
+ * @experimental
841
+ */
842
+ interface SelectionReceipt {
843
+ /** Iteration index this receipt is about. */
844
+ candidateIndex: number;
845
+ /** True for the iteration the selector chose as winner; false otherwise. */
846
+ selected: boolean;
847
+ /** The candidate's verdict score, when it has one. */
848
+ score?: number;
849
+ /** Why this candidate was (or was not) selected, when the selector states it. */
850
+ reason?: string;
851
+ /** Identity of the selector that produced this receipt — `'caller'` (an
852
+ * explicit `selectWinner`), `'driver'` (a driver-authored winner), or
853
+ * `'default'` (the kernel's best-valid-score argmax). */
854
+ selector: 'caller' | 'driver' | 'default';
855
+ }
856
+ /**
857
+ * Domain-free run provenance: a manifest of what was mounted into the run's
858
+ * boxes and the receipts for how the winner was selected. Surfaced on
859
+ * `LoopResult` purely for run auditability — nothing in the kernel branches on
860
+ * it. Empty arrays when the caller recorded no mounts and there was no
861
+ * candidate to select.
862
+ *
863
+ * @experimental
864
+ */
865
+ interface RunProvenance {
866
+ /** Every resource recorded via `prepareBox`'s `recordMount`, in record order. */
867
+ mounts: MountManifestEntry[];
868
+ /** One receipt per scored candidate at finalize, in iteration order. */
869
+ selectionReceipts: SelectionReceipt[];
870
+ }
871
+ /**
872
+ * Records a mounted resource into the run's provenance manifest. Passed to
873
+ * `prepareBox` so the caller — which owns the bytes it writes into the box —
874
+ * declares what it mounted without the kernel having to inspect box contents.
875
+ *
876
+ * @experimental
877
+ */
878
+ type MountRecorder = (entry: MountManifestEntry) => void;
804
879
  /** @experimental */
805
880
  interface Iteration<Task, Output> {
806
881
  /** 0-based iteration index assigned by the kernel. */
@@ -892,6 +967,10 @@ interface LoopResult<Task, Output, Decision> {
892
967
  * `ctx.cost.observeTokens` in a `runProfileMatrix` dispatch so the
893
968
  * integrity guard sees real LLM activity. */
894
969
  tokenUsage: LoopTokenUsage;
970
+ /** Domain-free run provenance for auditability: the mount manifest recorded
971
+ * during `prepareBox` and the selection receipts for how the winner was
972
+ * chosen. Always present; empty arrays when nothing was recorded. */
973
+ provenance: RunProvenance;
895
974
  }
896
975
  /**
897
976
  * Minimal sandbox client surface the kernel calls. Satisfied structurally by
@@ -1178,4 +1257,4 @@ interface ExecCtx {
1178
1257
  parentSpanId?: string;
1179
1258
  }
1180
1259
 
1181
- export { type LoopIterationDispatchPayload as $, type AgentRunSpec as A, type BackendErrorDetail as B, type RuntimeHookEvent as C, type RuntimeHookPhase as D, type ExecCtx as E, type RuntimeHookTarget as F, type RuntimeRunHandle as G, type RuntimeRunPersistenceAdapter as H, type Iteration as I, type RuntimeRunRow as J, type KnowledgeReadinessDecision as K, type LoopTraceEvent as L, composeRuntimeHooks as M, defineRuntimeHooks as N, type OpenAIChatTool as O, notifyRuntimeDecisionPoint as P, notifyRuntimeHookEvent as Q, type RuntimeStreamEvent as R, type SandboxClient as S, startRuntimeRun as T, type Driver as U, type Validator as V, type LoopWinner as W, type LoopLineageOptions as X, type LoopResult as Y, type LoopDecisionPayload as Z, type LoopEndedPayload as _, type OutputAdapter as a, type LoopIterationEndedPayload as a0, type LoopIterationStartedPayload as a1, type LoopPlanDescription as a2, type LoopPlanPayload as a3, type LoopStartedPayload as a4, type LoopTeardownFailedPayload as a5, type ValidationCtx as a6, type RuntimeHooks as b, type LoopSandboxPlacement as c, type LoopTokenUsage as d, type LoopTraceEmitter as e, type AgentBackendInput as f, type AgentExecutionBackend as g, type OpenAIChatToolChoice as h, type AgentBackendContext as i, type RunAgentTaskOptions as j, type AgentTaskRunResult as k, type RunAgentTaskStreamOptions as l, type AgentRuntimeEvent as m, type AgentTaskStatus as n, type RuntimeSessionStore as o, type RuntimeSession as p, type AgentAdapter as q, type AgentKnowledgeProvider as r, type AgentRuntimeEventSink as s, type AgentTaskContext as t, type AgentTaskSpec as u, type RuntimeDecisionEvidenceRef as v, type RuntimeDecisionKind as w, type RuntimeDecisionPoint as x, type RuntimeHookContext as y, type RuntimeHookErrorContext as z };
1260
+ export { type LoopEndedPayload as $, type AgentRunSpec as A, type BackendErrorDetail as B, type RuntimeHookEvent as C, type RuntimeHookPhase as D, type ExecCtx as E, type RuntimeHookTarget as F, type RuntimeRunHandle as G, type RuntimeRunPersistenceAdapter as H, type Iteration as I, type RuntimeRunRow as J, type KnowledgeReadinessDecision as K, type LoopTokenUsage as L, composeRuntimeHooks as M, defineRuntimeHooks as N, type OpenAIChatTool as O, notifyRuntimeDecisionPoint as P, notifyRuntimeHookEvent as Q, type RuntimeStreamEvent as R, type SandboxClient as S, startRuntimeRun as T, type Driver as U, type Validator as V, type LoopWinner as W, type LoopLineageOptions as X, type LoopResult as Y, type MountRecorder as Z, type LoopDecisionPayload as _, type OutputAdapter as a, type LoopIterationDispatchPayload as a0, type LoopIterationEndedPayload as a1, type LoopIterationStartedPayload as a2, type LoopPlanDescription as a3, type LoopPlanPayload as a4, type LoopStartedPayload as a5, type LoopTeardownFailedPayload as a6, type MountManifestEntry as a7, type RunProvenance as a8, type SelectionReceipt as a9, type ValidationCtx as aa, type RuntimeHooks as b, type LoopTraceEvent as c, type LoopSandboxPlacement as d, type LoopTraceEmitter as e, type AgentBackendInput as f, type AgentExecutionBackend as g, type OpenAIChatToolChoice as h, type AgentBackendContext as i, type RunAgentTaskOptions as j, type AgentTaskRunResult as k, type RunAgentTaskStreamOptions as l, type AgentRuntimeEvent as m, type AgentTaskStatus as n, type RuntimeSessionStore as o, type RuntimeSession as p, type AgentAdapter as q, type AgentKnowledgeProvider as r, type AgentRuntimeEventSink as s, type AgentTaskContext as t, type AgentTaskSpec as u, type RuntimeDecisionEvidenceRef as v, type RuntimeDecisionKind as w, type RuntimeDecisionPoint as x, type RuntimeHookContext as y, type RuntimeHookErrorContext as z };