fanout-cli 0.8.0 → 0.9.1

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.
Binary file
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../adapters/fake/src/cli.ts", "../../core/src/schema/common.ts", "../../core/src/schema/scope.ts", "../../core/src/schema/plan.ts", "../../core/src/schema/events.ts", "../../core/src/schema/manifest.ts", "../../core/src/schema/policy.ts", "../../core/src/ledger/ledger.ts", "../../adapters/fake/src/protocol.ts", "../../adapters/fake/src/scenario.ts"],
4
- "sourcesContent": ["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { parseArgs } from \"node:util\";\nimport { EXIT, type OutputLine } from \"./protocol.ts\";\nimport { Scenario } from \"./scenario.ts\";\n\n/*\n * A deterministic stand-in for an agent CLI:\n * node src/cli.ts --scenario-json '<json>' --report <path> -- <prompt>\n * It plays the scenario: prints its stream (protocol.ts), really writes files inside its working directory, writes\n * the report (the daemon chooses that path, usually outside the worktree), and exits. Same scenario, same stdout.\n */\n\nclass CliError extends Error {\n readonly code: number;\n\n constructor(code: number, message: string) {\n super(message);\n this.code = code;\n }\n}\n\nfunction readArgs(args: string[]): { scenario: Scenario; reportPath: string } {\n let parsed;\n try {\n parsed = parseArgs({\n args,\n options: { \"scenario-json\": { type: \"string\" }, report: { type: \"string\" } },\n allowPositionals: true,\n strict: true,\n });\n } catch (error) {\n throw new CliError(EXIT.usage, error instanceof Error ? error.message : String(error));\n }\n const { values, positionals } = parsed;\n if (values[\"scenario-json\"] === undefined) throw new CliError(EXIT.usage, \"missing --scenario-json\");\n if (values.report === undefined || values.report === \"\") throw new CliError(EXIT.usage, \"missing --report\");\n if (positionals.join(\" \").trim() === \"\") throw new CliError(EXIT.usage, \"missing prompt\");\n\n let json: unknown;\n try {\n json = JSON.parse(values[\"scenario-json\"]);\n } catch {\n throw new CliError(EXIT.usage, \"--scenario-json is not valid JSON\");\n }\n const scenario = Scenario.safeParse(json);\n if (!scenario.success) throw new CliError(EXIT.usage, `invalid scenario: ${scenario.error.message}`);\n return { scenario: scenario.data, reportPath: values.report };\n}\n\n/** Resolves a scenario write path, refusing anything outside the working directory. */\nfunction insideCwd(cwd: string, path: string): string {\n const target = resolve(cwd, path);\n const fromCwd = relative(cwd, target);\n if (isAbsolute(path) || fromCwd === \"\" || fromCwd === \"..\" || fromCwd.startsWith(`..${sep}`)) {\n throw new CliError(EXIT.unsafeWrite, `refusing to write outside the working directory: ${path}`);\n }\n return target;\n}\n\nfunction writeFile(path: string, content: string): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content, \"utf8\");\n}\n\nfunction emit(line: OutputLine): void {\n process.stdout.write(`${JSON.stringify(line)}\\n`);\n}\n\nasync function play(scenario: Scenario, cwd: string, reportPath: string): Promise<number> {\n const wait = async (ms: number | undefined): Promise<void> => {\n if (ms !== undefined && ms > 0) await sleep(ms * scenario.timeScale);\n };\n\n for (const step of scenario.steps) {\n if (\"sleep\" in step) {\n await wait(step.sleep);\n emit({ kind: \"sleep\", ms: step.sleep });\n continue;\n }\n await wait(step.delayMs);\n if (\"phase\" in step) {\n emit({\n kind: \"phase\",\n phase: step.phase,\n ...(step.detail === undefined ? {} : { detail: step.detail }),\n });\n } else if (\"tool\" in step) {\n const writes = Object.entries(step.write ?? {});\n const targets = writes.map(([path]) => insideCwd(cwd, path));\n writes.forEach(([, content], index) => {\n const target = targets[index];\n if (target !== undefined) writeFile(target, content);\n });\n emit({\n kind: \"tool\",\n tool: step.tool,\n ...(step.summary === undefined ? {} : { summary: step.summary }),\n files: writes.map(([path]) => path),\n });\n } else if (\"usage\" in step) {\n emit({ kind: \"usage\", amount: step.usage, unit: \"messages\" });\n } else {\n emit({ kind: \"limit\", message: step.limit });\n writeFile(reportPath, scenario.report);\n return EXIT.limit;\n }\n }\n\n writeFile(reportPath, scenario.report);\n emit({ kind: \"report\", text: scenario.report });\n if (scenario.hang) {\n // A pending promise alone lets Node exit; a timer keeps the process alive until it is killed.\n await new Promise<never>(() => setInterval(() => undefined, 60_000));\n }\n return scenario.exitCode;\n}\n\nasync function main(args: string[]): Promise<number> {\n const { scenario, reportPath } = readArgs(args);\n const cwd = process.cwd();\n return play(scenario, cwd, resolve(cwd, reportPath));\n}\n\nmain(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n const code = error instanceof CliError ? error.code : EXIT.internal;\n process.stderr.write(`fake seat: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = code;\n },\n);\n", "import { z } from \"zod\";\n\n/** Identifiers people read: mission, line, run and seat ids (\"csv-export\", \"api-builder-1\", \"codex\"). */\nexport const Slug = z\n .string()\n .regex(/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/, \"use lowercase letters, digits and inner dashes (max 64)\");\n\nexport const MissionId = Slug;\nexport const LineId = Slug;\nexport const RunId = Slug;\nexport const SeatId = Slug;\n\n/** A full commit id: 40 hex characters (SHA-1 repositories) or 64 (SHA-256 repositories). */\nexport const GitSha = z.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, \"a full commit sha\");\n\n/**\n * The identity of one piece of work: a hash of exactly what a run changed, at the moment we looked.\n *\n * An agent never commits, so its work has no commit id to name it by, and \"the diff in that worktree\" is not an\n * identity \u2014 it is a thing that can change between the moment it is reviewed and the moment it is merged. Every\n * step of the gate records the revision it judged, and a merge applies only a revision that every step agreed on.\n * Without that, \"reviewed and checked\" means \"reviewed and checked something, once\".\n */\nexport const WorkRevision = z.string().regex(/^[0-9a-f]{64}$/, \"a work revision (sha-256 of the diff)\");\n\n/** Which seat runs a line, and optionally with which model and effort. */\nexport const SeatRef = z.strictObject({\n id: SeatId,\n model: z.string().min(1).max(100).optional(),\n effort: z.string().min(1).max(40).optional(),\n});\nexport type SeatRef = z.infer<typeof SeatRef>;\n\n/** What the detector knows about an installed agent CLI. Unknown facts say \"unknown\" or null, never a guess. */\nexport const SeatInfo = z.strictObject({\n id: SeatId,\n displayName: z.string().min(1).max(80),\n binary: z.string().min(1).max(200),\n version: z.string().min(1).max(100).nullable(),\n supported: z.boolean(),\n signedIn: z.enum([\"yes\", \"no\", \"unknown\"]),\n models: z.array(z.string().min(1).max(100)).max(100),\n efforts: z.array(z.string().min(1).max(40)).max(20),\n billing: z.enum([\"subscription\", \"credit\", \"api\", \"unknown\"]),\n /**\n * The subscription tier this seat is on, and where that answer came from \u2014 `null` when the CLI does not report\n * one, which is most of them. The source travels with the value on purpose: \"detected\" is a fact the CLI told us\n * this run, \"declared\" is something the owner typed once and may since have outgrown, and a reader deciding how\n * much to trust a routing decision deserves to know which it is looking at.\n */\n plan: z\n .strictObject({\n name: z.string().min(1).max(100),\n source: z.enum([\"detected\", \"declared\"]),\n })\n .nullable(),\n});\nexport type SeatInfo = z.infer<typeof SeatInfo>;\n\nexport const DiffStat = z.strictObject({\n files: z.int().nonnegative(),\n insertions: z.int().nonnegative(),\n deletions: z.int().nonnegative(),\n});\nexport type DiffStat = z.infer<typeof DiffStat>;\n\nexport const MissionLimits = z.strictObject({\n maxParallel: z.int().min(1).max(32),\n timeoutMinutes: z\n .int()\n .min(1)\n .max(24 * 60),\n});\nexport type MissionLimits = z.infer<typeof MissionLimits>;\n\n/** One line of the safety report. A failed \"block\" check stops the launch; a failed \"warn\" check is shown. */\nexport const SafetyCheck = z.strictObject({\n id: z.string().min(1).max(64),\n ok: z.boolean(),\n severity: z.enum([\"block\", \"warn\"]),\n message: z.string().min(1).max(2000),\n lineIds: z.array(LineId).max(32).optional(),\n});\nexport type SafetyCheck = z.infer<typeof SafetyCheck>;\n", "import { z } from \"zod\";\n\n/*\n * Write scopes are repo-relative POSIX globs with a deliberately small syntax:\n * `*` and `?` match within one path segment, `**` (a whole segment) matches zero or more segments.\n * Every pattern also covers everything below what it matches, so `src/api` and `src/api/**` are the same scope.\n * Braces, character classes and negation are not supported: a scope must be obvious to the person approving it.\n *\n * Every other character is literal, so ordinary filenames work: spaces, parentheses, accents, CJK, emoji.\n * Only `*` and `?` are special, and there is no escape for them; a file whose name really contains one is covered\n * by a scope that ends in `**`. Separators, empty segments, `.` and `..` are refused, as are control characters.\n *\n * Matching is deliberately hand-written rather than translated to regular expressions: a pattern like `a*a*a*\u2026z`\n * makes a backtracking engine take exponential time, and scopes come from plans we must be able to check quickly.\n */\n\n// eslint-disable-next-line no-control-regex -- control characters are exactly what a path segment must not contain\nconst SEGMENT = /^(?:\\*\\*|[^/\u0000-\u001F\u007F]+)$/;\nconst WILDCARD = /[*?]/;\n\nexport function isValidScopeGlob(glob: string): boolean {\n if (glob.length === 0 || glob.startsWith(\"/\") || glob.endsWith(\"/\")) return false;\n return glob\n .split(\"/\")\n .every(\n (segment) =>\n SEGMENT.test(segment) &&\n segment !== \".\" &&\n segment !== \"..\" &&\n (segment === \"**\" || !segment.includes(\"**\")),\n );\n}\n\nexport const ScopeGlob = z.string().max(300).refine(isValidScopeGlob, {\n message: \"use a repo-relative path or glob (`*`, `?`, `**`), without `..`, leading or trailing `/`\",\n});\n\n/**\n * A plain repo-relative path: no leading slash, no `.` or `..`, no empty segments. Anything else (a path that still\n * needs resolving, or one from outside the repository) is not inside any scope, whatever it looks like.\n */\nexport function isRepoPath(path: string): boolean {\n if (path.length === 0 || path.startsWith(\"/\") || path.endsWith(\"/\")) return false;\n return path.split(\"/\").every((segment) => segment !== \"\" && segment !== \".\" && segment !== \"..\");\n}\n\n/** Segments of a pattern, with the implicit \"and everything below\" made explicit. */\nfunction scopeSegments(glob: string): string[] {\n const segments = glob.split(\"/\");\n return segments.at(-1) === \"**\" ? segments : [...segments, \"**\"];\n}\n\n/**\n * Matches one segment pattern (`*`, `?`) against one name, in linear time: on a mismatch it returns to the last `*`\n * and gives it one more character, so no input can make it backtrack exponentially.\n */\nfunction segmentMatches(pattern: string, text: string): boolean {\n let p = 0;\n let t = 0;\n let starAt = -1;\n let matchedAt = 0;\n\n while (t < text.length) {\n const token = pattern[p];\n if (token === \"?\" || (token !== undefined && token !== \"*\" && token === text[t])) {\n p += 1;\n t += 1;\n } else if (token === \"*\") {\n starAt = p;\n matchedAt = t;\n p += 1;\n } else if (starAt >= 0) {\n matchedAt += 1;\n p = starAt + 1;\n t = matchedAt;\n } else {\n return false;\n }\n }\n while (pattern[p] === \"*\") p += 1;\n return p === pattern.length;\n}\n\n/** The literal text before the first wildcard and after the last one. */\nfunction literalEnds(segment: string): [prefix: string, suffix: string] {\n const first = segment.search(WILDCARD);\n let last = segment.length - 1;\n while (last >= 0 && !WILDCARD.test(segment.charAt(last))) last -= 1;\n return [segment.slice(0, first), segment.slice(last + 1)];\n}\n\n/**\n * Whether two single-segment patterns can match a common name. Exact when at least one side is literal; when both\n * have wildcards it compares their literal ends, which can only err towards \"yes\" (the safe side for scopes).\n */\nfunction segmentsMayOverlap(a: string, b: string): boolean {\n const aWild = WILDCARD.test(a);\n const bWild = WILDCARD.test(b);\n if (!aWild && !bWild) return a === b;\n if (!aWild) return segmentMatches(b, a);\n if (!bWild) return segmentMatches(a, b);\n const [aPrefix, aSuffix] = literalEnds(a);\n const [bPrefix, bSuffix] = literalEnds(b);\n return (\n (aPrefix.startsWith(bPrefix) || bPrefix.startsWith(aPrefix)) &&\n (aSuffix.endsWith(bSuffix) || bSuffix.endsWith(aSuffix))\n );\n}\n\n/**\n * Whether some file path could fall inside both scopes. Sound: it never answers \"no\" when a common path exists.\n * It may answer \"yes\" for exotic wildcard pairs that cannot actually meet; the plan then asks for narrower scopes.\n */\nexport function scopesMayOverlap(a: string, b: string): boolean {\n const left = scopeSegments(a);\n const right = scopeSegments(b);\n const memo = new Map<number, boolean>();\n const width = right.length + 1;\n\n const from = (i: number, j: number): boolean => {\n const key = i * width + j;\n const known = memo.get(key);\n if (known !== undefined) return known;\n let result: boolean;\n const l = left[i];\n const r = right[j];\n if (l === undefined && r === undefined) result = true;\n else if (l === \"**\") result = from(i + 1, j) || (r !== undefined && from(i, j + 1));\n else if (r === \"**\") result = from(i, j + 1) || (l !== undefined && from(i + 1, j));\n else if (l === undefined || r === undefined) result = false;\n else result = segmentsMayOverlap(l, r) && from(i + 1, j + 1);\n memo.set(key, result);\n return result;\n };\n\n return from(0, 0);\n}\n\n/** Whether a repo-relative file path falls inside a scope. */\nexport function pathInScope(path: string, glob: string): boolean {\n if (!isRepoPath(path)) return false;\n const parts = path.split(\"/\");\n const pattern = scopeSegments(glob);\n const memo = new Map<number, boolean>();\n const width = pattern.length + 1;\n\n const from = (i: number, j: number): boolean => {\n const key = i * width + j;\n const known = memo.get(key);\n if (known !== undefined) return known;\n const segment = pattern[j];\n const part = parts[i];\n let result: boolean;\n if (segment === undefined) result = i === parts.length;\n else if (segment === \"**\") result = from(i, j + 1) || (i < parts.length && from(i + 1, j));\n else result = part !== undefined && segmentMatches(segment, part) && from(i + 1, j + 1);\n memo.set(key, result);\n return result;\n };\n\n return from(0, 0);\n}\n", "import { z } from \"zod\";\nimport { LineId, SeatRef } from \"./common.ts\";\nimport { ScopeGlob, scopesMayOverlap } from \"./scope.ts\";\n\nexport const LineRole = z.enum([\"auditor\", \"builder\", \"tester\"]);\nexport type LineRole = z.infer<typeof LineRole>;\n\n/** One task in a mission. Auditors are read-only; builders and testers declare where they may write. */\nexport const PlanLine = z.strictObject({\n id: LineId,\n title: z.string().trim().min(1).max(120),\n role: LineRole,\n prompt: z.string().min(1).max(100_000),\n seat: SeatRef,\n scope: z.strictObject({ write: z.array(ScopeGlob).max(64) }),\n dependsOn: z.array(LineId).max(32).default([]),\n checks: z.array(z.string().min(1).max(500)).max(16).default([]),\n timeoutMinutes: z.int().min(1).max(240).optional(),\n /**\n * This line fixes a bug, so the gate will not merge it without a test proven to fail on the old code.\n *\n * Declared when the mission is planned rather than judged afterwards, because the moment to decide whether\n * something is a fix is before an agent has written a persuasive explanation of why its change is fine.\n */\n fixesBug: z.boolean().default(false),\n});\nexport type PlanLine = z.infer<typeof PlanLine>;\n\nexport const PlanGraph = z.strictObject({\n lines: z.array(PlanLine).min(1).max(32),\n});\nexport type PlanGraph = z.infer<typeof PlanGraph>;\n\nexport type PlanIssueCode =\n | \"duplicate_line\"\n | \"unknown_dependency\"\n | \"self_dependency\"\n | \"dependency_cycle\"\n | \"auditor_writes\"\n | \"missing_write_scope\"\n | \"scope_overlap\";\n\nexport interface PlanIssue {\n code: PlanIssueCode;\n message: string;\n lineIds: string[];\n}\n\n/**\n * Checks the rules a schema can't express: the dependency graph and the write scopes of lines that may run at the\n * same time. Returns every issue found, in a stable order; an empty list means the plan is launchable.\n */\nexport function validatePlan(plan: PlanGraph): PlanIssue[] {\n const issues: PlanIssue[] = [];\n const { lines } = plan;\n\n const indexById = new Map<string, number>();\n lines.forEach((line, index) => {\n if (indexById.has(line.id)) {\n issues.push({\n code: \"duplicate_line\",\n message: `Line id \"${line.id}\" is used more than once.`,\n lineIds: [line.id],\n });\n } else {\n indexById.set(line.id, index);\n }\n });\n\n const edges: number[][] = lines.map((line) => {\n const targets: number[] = [];\n for (const dependency of line.dependsOn) {\n if (dependency === line.id) {\n issues.push({\n code: \"self_dependency\",\n message: `Line \"${line.id}\" depends on itself.`,\n lineIds: [line.id],\n });\n continue;\n }\n const target = indexById.get(dependency);\n if (target === undefined) {\n issues.push({\n code: \"unknown_dependency\",\n message: `Line \"${line.id}\" depends on \"${dependency}\", which is not in the plan.`,\n lineIds: [line.id],\n });\n } else {\n targets.push(target);\n }\n }\n return targets;\n });\n\n for (const cycle of findCycles(edges)) {\n const ids = cycle.map((index) => lines[index]?.id ?? \"?\");\n issues.push({\n code: \"dependency_cycle\",\n message: `Dependencies form a cycle: ${[...ids, ids[0]].join(\" \u2192 \")}.`,\n lineIds: ids,\n });\n }\n\n for (const line of lines) {\n if (line.role === \"auditor\" && line.scope.write.length > 0) {\n issues.push({\n code: \"auditor_writes\",\n message: `Line \"${line.id}\" is an auditor, so it is read-only; remove its write scope or make it a builder.`,\n lineIds: [line.id],\n });\n }\n if (line.role !== \"auditor\" && line.scope.write.length === 0) {\n issues.push({\n code: \"missing_write_scope\",\n message: `Line \"${line.id}\" is a ${line.role} but declares no write scope.`,\n lineIds: [line.id],\n });\n }\n }\n\n const reaches = reachability(edges);\n for (let i = 0; i < lines.length; i += 1) {\n for (let j = i + 1; j < lines.length; j += 1) {\n const a = lines[i];\n const b = lines[j];\n if (a === undefined || b === undefined) continue;\n if (reaches[i]?.has(j) === true || reaches[j]?.has(i) === true) continue;\n const clash = firstOverlap(a.scope.write, b.scope.write);\n if (clash !== undefined) {\n issues.push({\n code: \"scope_overlap\",\n message:\n `Lines \"${a.id}\" and \"${b.id}\" can run at the same time and may both write ` +\n `\"${clash[0]}\" / \"${clash[1]}\". Make one depend on the other, or narrow the scopes.`,\n lineIds: [a.id, b.id],\n });\n }\n }\n }\n\n return issues;\n}\n\nfunction firstOverlap(left: string[], right: string[]): [string, string] | undefined {\n for (const a of left) {\n for (const b of right) {\n if (scopesMayOverlap(a, b)) return [a, b];\n }\n }\n return undefined;\n}\n\n/** For each node, every node it can reach by following edges (its transitive dependencies). */\nfunction reachability(edges: number[][]): Set<number>[] {\n return edges.map((_, start) => {\n const seen = new Set<number>();\n const stack = [...(edges[start] ?? [])];\n for (let next = stack.pop(); next !== undefined; next = stack.pop()) {\n if (seen.has(next)) continue;\n seen.add(next);\n stack.push(...(edges[next] ?? []));\n }\n return seen;\n });\n}\n\n/** One cycle per back edge found by a depth-first search, each listed from its first node in plan order. */\nfunction findCycles(edges: number[][]): number[][] {\n const state = new Array<\"new\" | \"open\" | \"done\">(edges.length).fill(\"new\");\n const path: number[] = [];\n const cycles: number[][] = [];\n\n const visit = (node: number): void => {\n state[node] = \"open\";\n path.push(node);\n for (const next of edges[node] ?? []) {\n if (state[next] === \"open\") {\n cycles.push(path.slice(path.indexOf(next)));\n } else if (state[next] === \"new\") {\n visit(next);\n }\n }\n path.pop();\n state[node] = \"done\";\n };\n\n edges.forEach((_, node) => {\n if (state[node] === \"new\") visit(node);\n });\n return cycles;\n}\n", "import { z } from \"zod\";\nimport {\n DiffStat,\n GitSha,\n LineId,\n MissionId,\n MissionLimits,\n RunId,\n SafetyCheck,\n SeatId,\n SeatInfo,\n SeatRef,\n WorkRevision,\n} from \"./common.ts\";\nimport { PlanGraph } from \"./plan.ts\";\n\n/*\n * Every fact the daemon records is one of these events. Rules for changing this file:\n * - Adding a new event type is additive: old ledgers stay valid.\n * - Changing the shape of an existing type needs a new EVENT_VERSION and an upgrade path for stored events.\n * - Events carry no secrets and no raw logs: summaries, paths and numbers only.\n */\n\nexport const EVENT_VERSION = 1 as const;\n\nconst mission = { missionId: MissionId };\nconst run = { missionId: MissionId, runId: RunId };\n\nconst PlanAuthor = z.enum([\"lead\", \"user\"]);\nconst Phase = z.enum([\"reading\", \"coding\", \"testing\", \"reporting\"]);\nconst RepoPaths = z.array(z.string().min(1).max(1000)).max(1000);\n\nexport const SeatDetected = z.strictObject({\n type: z.literal(\"seat.detected\"),\n seat: SeatInfo,\n});\n\nexport const MissionCreated = z.strictObject({\n type: z.literal(\"mission.created\"),\n ...mission,\n goal: z.string().trim().min(1).max(4000),\n repo: z.strictObject({ root: z.string().min(1).max(1000), baseCommit: GitSha }),\n limits: MissionLimits,\n});\n\nexport const PlanProposed = z.strictObject({\n type: z.literal(\"plan.proposed\"),\n ...mission,\n plan: PlanGraph,\n by: PlanAuthor,\n note: z.string().max(2000).optional(),\n});\n\nexport const PlanRevised = z.strictObject({\n type: z.literal(\"plan.revised\"),\n ...mission,\n plan: PlanGraph,\n by: PlanAuthor,\n note: z.string().max(2000).optional(),\n});\n\nexport const SafetyReported = z\n .strictObject({\n type: z.literal(\"safety.report\"),\n ...mission,\n /** Which plan revision this report describes. A newer plan makes it stale, never current. */\n planRevision: z.int().positive(),\n ok: z.boolean(),\n checks: z.array(SafetyCheck).max(200),\n })\n .refine((report) => report.ok === report.checks.every((check) => check.ok || check.severity === \"warn\"), {\n message: \"ok must be true exactly when no blocking check failed\",\n path: [\"ok\"],\n });\n\nexport const RunQueued = z.strictObject({\n type: z.literal(\"run.queued\"),\n ...run,\n lineId: LineId,\n seat: SeatRef,\n attempt: z.int().min(1).max(3),\n});\n\nexport const RunStarted = z.strictObject({\n type: z.literal(\"run.started\"),\n ...run,\n workdir: z.string().min(1).max(1000),\n argv: z.array(z.string().max(200_000)).min(1).max(200),\n});\n\n/**\n * The agent's own name for this conversation, learned as soon as we have it.\n *\n * Recorded because rework depends on it: replying into the session that wrote a diff is worth far more than\n * re-explaining the work to a stranger who happens to share its model. Some CLIs let us choose the id before\n * launch and some announce it in their stream (ADR 0018); either way it is written down the moment it is known,\n * because the run most likely to need rework is the one that ended badly.\n */\nexport const RunSession = z.strictObject({\n type: z.literal(\"run.session\"),\n ...run,\n sessionId: z.string().trim().min(1).max(200),\n});\n\nexport const RunProgress = z.strictObject({\n type: z.literal(\"run.progress\"),\n ...run,\n phase: Phase,\n detail: z.string().max(500).optional(),\n});\n\nexport const RunTool = z.strictObject({\n type: z.literal(\"run.tool\"),\n ...run,\n tool: z.string().min(1).max(100),\n summary: z.string().max(500).optional(),\n files: RepoPaths.default([]),\n});\n\nexport const RunUsage = z.strictObject({\n type: z.literal(\"run.usage\"),\n ...run,\n seat: SeatId,\n amount: z.number().nonnegative(),\n unit: z.enum([\"messages\", \"tokens\", \"minutes\"]),\n estimated: z.boolean(),\n});\n\nexport const RunFinished = z.strictObject({\n type: z.literal(\"run.finished\"),\n ...run,\n status: z.enum([\"done\", \"failed\", \"killed\", \"timeout\"]),\n exitCode: z.int().nullable(),\n reportPath: z.string().min(1).max(1000).optional(),\n diffStat: DiffStat.optional(),\n});\n\n/*\n * The merge gate, in events. Every one of them names the `revision` it judged, because each is a statement about a\n * specific diff and not about a worktree that may since have moved. A merge applies a revision only when review,\n * checks, proof and approval all named that same one; anything else is a claim about work nobody looked at.\n */\n\nexport const ReviewDone = z.strictObject({\n type: z.literal(\"review.done\"),\n ...run,\n revision: WorkRevision,\n verdict: z.enum([\"accept\", \"rework\", \"reject\"]),\n notes: z.string().max(20_000),\n by: SeatRef,\n});\n\nexport const ChecksDone = z.strictObject({\n type: z.literal(\"checks.done\"),\n ...run,\n revision: WorkRevision,\n ok: z.boolean(),\n summary: z.string().max(4000),\n /** What actually ran, so \"checks pass\" can be read as a claim about specific commands. */\n commands: z.array(z.string().min(1).max(500)).max(50),\n});\n\n/** Proof of a fix: the new tests that fail on the old code (and pass on the new). */\nexport const ProofDone = z\n .strictObject({\n type: z.literal(\"proof.done\"),\n ...run,\n revision: WorkRevision,\n ok: z.boolean(),\n failedOnOld: z.array(z.string().min(1).max(500)).max(500),\n })\n .refine((proof) => !proof.ok || proof.failedOnOld.length > 0, {\n message: \"a passing proof names at least one test that failed on the old code\",\n path: [\"failedOnOld\"],\n });\n\n/**\n * Someone said yes. Recorded separately from the merge itself so a replay can answer \"who authorised this?\" \u2014\n * a question a diff in the history cannot answer on its own.\n */\nexport const MergeApproved = z.strictObject({\n type: z.literal(\"merge.approved\"),\n ...run,\n revision: WorkRevision,\n /**\n * A person, or a policy the person wrote down in advance. A policy must name itself: \"it was pre-approved\" is\n * not an answer anyone can audit, and \"which rule, written when\" is.\n */\n by: z.discriminatedUnion(\"kind\", [\n z.strictObject({\n kind: z.literal(\"user\"),\n /**\n * How we know. This is the difference between a fact and an agent's account of one.\n *\n * `direct` \u2014 the daemon received the click itself, from the mission view, on this machine. Nothing in\n * between could have invented it.\n *\n * `relayed` \u2014 the lead says it asked and quoted the answer in `note`. That is a claim by a language model\n * about a conversation, and an agent that skipped the asking writes a byte-identical event. It is worth\n * recording and it is not worth confusing with the first one.\n *\n * Absent on events written before Fanout drew the distinction; read those as `relayed`.\n */\n via: z.enum([\"direct\", \"relayed\"]).optional(),\n }),\n z.strictObject({ kind: z.literal(\"policy\"), name: z.string().trim().min(1).max(200) }),\n ]),\n note: z.string().max(2000).optional(),\n});\n\nexport const MergeApplied = z.strictObject({\n type: z.literal(\"merge.applied\"),\n ...run,\n revision: WorkRevision,\n files: RepoPaths.min(1),\n /** Where the work landed, so a dependent line can start from it rather than from a guess. */\n commit: GitSha,\n});\n\nexport const MergeConflict = z.strictObject({\n type: z.literal(\"merge.conflict\"),\n ...run,\n revision: WorkRevision,\n files: RepoPaths.min(1),\n});\n\nexport const RunDropped = z.strictObject({\n type: z.literal(\"run.dropped\"),\n ...run,\n reason: z.string().trim().min(1).max(2000),\n});\n\n/**\n * A second vendor read the lead's own uncommitted work.\n *\n * Not a mission and not a run: no agent worked in a worktree, and forcing this into the mission machinery would\n * put a fake mission in front of the user for every review. It carries no `missionId` for the same reason\n * `seat.detected` does not \u2014 it is a fact about this machine at a moment, not about a mission.\n *\n * This is the event that answers the product's only real question: was anything other than the author's own\n * judgement applied to this code before it was called done?\n */\nexport const BuddyReviewed = z.strictObject({\n type: z.literal(\"buddy.reviewed\"),\n /** Which working tree, so a review of one repository is never read as covering another. */\n repoRoot: z.string().min(1).max(1000),\n revision: WorkRevision,\n by: SeatRef,\n /** What it said, verbatim. A second opinion summarised by the author is not a second opinion. */\n findings: z.string().max(100_000),\n /** False when the reviewer could not be run at all, so \"no findings\" never stands in for \"never asked\". */\n ran: z.boolean(),\n files: RepoPaths.max(1000),\n});\n\n/**\n * The lead wrote down what it believes, and a cold reader checked each belief against the code.\n *\n * This is the sharpest thing a second vendor can do, and the cheapest. The lead carries the whole session \u2014 the\n * plan, the reasoning, the justification \u2014 and that context is precisely what makes its own mistakes invisible to\n * it: it knows why the code is right, so the code looks right. A reader arriving with only the diff is not\n * smarter, it is differently placed, which is why even a small model reading cold can refute a large one reading\n * warm. Asking it to review everything spends tokens on that asymmetry. Asking it to falsify three specific\n * claims spends almost none.\n *\n * Recording the claims, not only the verdicts, is the point. A replay shows what the lead asserted as well as\n * what turned out to be true, and an author who must write down falsifiable claims notices the weak ones while\n * writing them.\n */\nexport const ClaimsChecked = z.strictObject({\n type: z.literal(\"claims.checked\"),\n repoRoot: z.string().min(1).max(1000),\n revision: WorkRevision,\n by: SeatRef,\n claims: z\n .array(\n z.strictObject({\n /** What the lead asserted, in its own words. */\n claim: z.string().trim().min(1).max(500),\n /**\n * `unclear` is the default and the only safe absence. A verdict we could not read is not a pass, and a\n * claim the reader ignored has not been checked \u2014 treating either as confirmed would make this theatre.\n */\n verdict: z.enum([\"confirmed\", \"refuted\", \"unclear\"]),\n /** Why, in the reader's own words. Required for a refusal; a bare \"no\" helps nobody. */\n evidence: z.string().max(4000),\n }),\n )\n .min(1)\n .max(20),\n /** False when the reader could not be run at all, so \"nothing refuted\" never stands in for \"never asked\". */\n ran: z.boolean(),\n /**\n * True when these verdicts were written by us rather than read by anyone \u2014 the offline demo, and nothing else.\n *\n * It exists so that the one thing the demo cannot do honestly is labelled everywhere it appears instead of\n * being quietly indistinguishable from a real answer. Inventing a second opinion and presenting it as read\n * would be faking the only claim this product makes.\n */\n simulated: z.boolean().default(false),\n});\n\n/**\n * A seat said it has run out, in its own words.\n *\n * Not mission-scoped: a limit belongs to the account, not to whatever happened to be running when it was hit.\n * The message is kept verbatim because \"you have reached your usage limit\" and \"rate limited, retry in 30s\" are\n * different problems and only the vendor knows which one this is.\n */\nexport const SeatLimited = z.strictObject({\n type: z.literal(\"seat.limited\"),\n seat: SeatId,\n message: z.string().trim().min(1).max(500),\n /** When the seat says it will work again. Absent when it did not say, which is usually. */\n resetsAt: z.iso.datetime().optional(),\n});\n\n/**\n * How full one of a seat's quota windows is, when the CLI reports it rather than us guessing.\n *\n * Claude Code is the only seat that says this today, per turn, for its five-hour and seven-day windows. It is the\n * difference between routing on facts and routing on arithmetic we made up, so it is recorded as what it is \u2014\n * real, and belonging to a named window \u2014 rather than flattened into a token count that would read as estimated.\n */\nexport const SeatQuota = z.strictObject({\n type: z.literal(\"seat.quota\"),\n seat: SeatId,\n window: z.string().min(1).max(50),\n /** 0.28 means 28% of that window is used. */\n utilization: z.number().min(0).max(1),\n resetsAt: z.iso.datetime().optional(),\n});\n\nexport const RouteChanged = z.strictObject({\n type: z.literal(\"route.changed\"),\n ...mission,\n lineId: LineId,\n from: SeatRef,\n to: SeatRef,\n reason: z.string().trim().min(1).max(500),\n});\n\nexport const PolicyBreach = z.strictObject({\n type: z.literal(\"policy.breach\"),\n ...run,\n limit: z.string().min(1).max(100),\n action: z.enum([\"killed\", \"paused\", \"asked\"]),\n});\n\nexport const MissionFinished = z.strictObject({\n type: z.literal(\"mission.finished\"),\n ...mission,\n outcome: z.enum([\"completed\", \"aborted\"]),\n summary: z.string().max(8000),\n});\n\nexport const FanoutEvent = z.discriminatedUnion(\"type\", [\n SeatDetected,\n MissionCreated,\n PlanProposed,\n PlanRevised,\n SafetyReported,\n RunQueued,\n RunStarted,\n RunSession,\n RunProgress,\n RunTool,\n RunUsage,\n RunFinished,\n ReviewDone,\n ChecksDone,\n ProofDone,\n BuddyReviewed,\n ClaimsChecked,\n MergeApproved,\n MergeApplied,\n MergeConflict,\n RunDropped,\n SeatLimited,\n SeatQuota,\n RouteChanged,\n PolicyBreach,\n MissionFinished,\n]);\n\n/** An event as validated (defaults applied). */\nexport type FanoutEvent = z.infer<typeof FanoutEvent>;\n/** An event as written by a producer (defaults may be omitted). */\nexport type FanoutEventInput = z.input<typeof FanoutEvent>;\nexport type EventType = FanoutEvent[\"type\"];\nexport type EventOf<T extends EventType> = Extract<FanoutEvent, { type: T }>;\n\n/** What the ledger adds when it records an event. */\nexport const EventStamp = z.strictObject({\n v: z.literal(EVENT_VERSION),\n id: z.uuid(),\n seq: z.int().positive(),\n ts: z.iso.datetime(),\n});\nexport type EventStamp = z.infer<typeof EventStamp>;\n\n/** An event as stored in and read from the ledger. */\nexport type StoredEvent = FanoutEvent & EventStamp;\n", "import { z } from \"zod\";\nimport { SeatId } from \"./common.ts\";\n\n/*\n * What an adapter declares about its CLI, as data rather than code: the versions it was verified against, the exact\n * non-interactive invocation, how to read its output, the safest modes it offers, how to ask it whether it is signed\n * in, and when its vendor's terms were last reviewed.\n *\n * A manifest is a promise we can check. A CLI outside `supportedVersions` is reported as an unsupported version\n * rather than driven on a guess, because a stream we have not seen is a stream we cannot parse honestly.\n */\n\n/** A placeholder the supervisor fills in: {workdir}, {prompt}, {report}, {sandbox}, {model}, {effort}, {session}. */\nconst ArgTemplate = z.string().min(1).max(500);\n\n/**\n * A regular expression a manifest asks us to run, checked at parse time rather than at the moment we need it.\n *\n * A pattern that does not compile throws from `new RegExp`, and that throw would happen deep inside detection,\n * where it takes down the whole crew's result and not just the seat that declared it. Refusing the manifest is\n * both earlier and louder. (This does not make a pattern *fast*: see `matches` in the detector for that half.)\n */\nconst SafePattern = z.string().max(200).refine(compiles, { message: \"must be a valid regular expression\" });\n\nfunction compiles(pattern: string): boolean {\n try {\n new RegExp(pattern, \"i\");\n return true;\n } catch {\n return false;\n }\n}\n\nexport const AdapterManifest = z.strictObject({\n id: SeatId,\n displayName: z.string().min(1).max(80),\n binary: z.string().min(1).max(200),\n /** A semver range, e.g. \">=0.150 <1.0\". Outside it, the seat is unsupported, never guessed at. */\n supportedVersions: z.string().min(1).max(100),\n /** What we promise about this seat, never a judgment of the CLI's quality. */\n tier: z.enum([\"supported\", \"community\", \"reference\"]),\n\n /** Null means no such mode or none verified: the merge gate must never act on a guess. */\n capabilities: z.strictObject({\n resume: z.strictObject({ args: z.array(ArgTemplate).min(1).max(50) }).nullable(),\n fork: z.strictObject({ args: z.array(ArgTemplate).min(1).max(50) }).nullable(),\n review: z.strictObject({ args: z.array(ArgTemplate).min(1).max(50) }).nullable(),\n /**\n * An allowlist keeps account identity out of storage. A privacy promise that is data can be reviewed in a pull\n * request; a promise in adapter code has to be re-read every time.\n */\n plan: z\n .strictObject({\n probe: z.array(z.string().min(1).max(100)).min(1).max(10),\n format: z.literal(\"json\"),\n keep: z.array(z.string().min(1).max(100)).min(1).max(5),\n planField: z.string().min(1).max(100),\n })\n .refine((plan) => plan.keep.includes(plan.planField), {\n message: \"planField must be one of keep\",\n path: [\"planField\"],\n })\n .nullable(),\n }),\n\n headless: z.strictObject({\n args: z.array(ArgTemplate).min(1).max(50),\n /** Always closed: a CLI waiting on stdin is the most common way a run hangs forever. */\n stdin: z.literal(\"closed\"),\n }),\n\n stream: z.strictObject({\n /** The flag that turns on machine-readable output, or null when the CLI has none. */\n flag: z.string().max(100).nullable(),\n format: z.enum([\"jsonl\", \"text\"]),\n }),\n\n models: z.array(z.string().min(1).max(100)).max(100),\n efforts: z.array(z.string().min(1).max(40)).max(20),\n\n /** The flag value for each mode we use. Auditors get the read-only one; nothing else is ever passed. */\n permissionModes: z.strictObject({\n readOnly: z.string().min(1).max(100),\n edit: z.string().min(1).max(100),\n }),\n\n network: z.strictObject({\n canDisable: z.boolean(),\n flag: z.string().max(100).nullable(),\n }),\n\n /**\n * How to ask the CLI itself whether it is signed in. We never read credential files.\n *\n * Both answers are named, because only one of them can be inferred from the other's absence and neither\n * actually is: a probe that fails, times out or answers something unforeseen has told us nothing, and\n * \"nothing\" must stay \"unknown\" rather than becoming a \"no\" that quietly reroutes someone's work.\n */\n signIn: z.strictObject({\n probe: z.array(z.string().min(1).max(100)).max(10).nullable(),\n /** A pattern the probe's output must match to count as signed in. */\n okPattern: SafePattern.nullable(),\n /** A pattern that positively means signed out. Checked first, so \"Not logged in\" cannot match \"Logged in\". */\n noPattern: SafePattern.nullable(),\n }),\n\n /** Real usage when the CLI reports it; otherwise we estimate and say so. */\n usage: z.strictObject({\n probe: z.array(z.string().min(1).max(100)).max(10).nullable(),\n window: z.string().max(100),\n }),\n\n /** Which pool this seat's headless use bills against, so a vendor's policy change is a manifest change. */\n billing: z.enum([\"subscription\", \"credit\", \"api\", \"unknown\"]),\n\n terms: z.strictObject({\n reviewedAt: z.iso.date().nullable(),\n notes: z.string().max(2000),\n }),\n\n status: z.enum([\"planned\", \"research\", \"alpha\", \"stable\"]),\n});\nexport type AdapterManifest = z.infer<typeof AdapterManifest>;\n", "import { z } from \"zod\";\nimport { SeatId, type SeatInfo } from \"./common.ts\";\n\n/*\n * What the owner wants done with each seat, kept apart from what is true of it today.\n *\n * Posture is a preference and availability is a fact, and mixing them produces a interface that lies in both\n * directions: a seat you rely on looks disabled the morning its CLI fails to start, and a seat you asked us never\n * to touch looks ready the moment it signs in. They are resolved together only at the point of use, in `stanceFor`.\n *\n * This file holds only what the owner declared. It is never a cache of anything detected: a subscription tier\n * written down in June and read back in September is a stale answer presented as a current fact, and the whole\n * point of the `source` on a plan is that a reader can tell those apart.\n */\n\n/**\n * How willingly Fanout should spend a seat.\n *\n * - `preferred` \u2014 reach for this first when several seats could do the line.\n * - `normal` \u2014 use it when the plan calls for it.\n * - `sparing` \u2014 only when nothing else fits, and say so before launching. For the subscription you pay least for.\n * - `off` \u2014 never, until the owner says otherwise.\n */\nexport const SeatPosture = z.enum([\"preferred\", \"normal\", \"sparing\", \"off\"]);\nexport type SeatPosture = z.infer<typeof SeatPosture>;\n\nexport const SeatPolicy = z.strictObject({\n version: z.literal(1),\n seats: z.record(\n SeatId,\n z.strictObject({\n posture: SeatPosture,\n /** The owner's own words about why, shown back to them so a past decision explains itself. */\n note: z.string().max(200).optional(),\n }),\n ),\n});\nexport type SeatPolicy = z.infer<typeof SeatPolicy>;\n\nexport const EMPTY_POLICY: SeatPolicy = { version: 1, seats: {} };\n\n/**\n * Claude is the only seat that is off until asked for.\n *\n * The lead already runs on this subscription, so a Claude worker spends the same window the session you are sitting\n * in is spending. That is a decision about someone's money, and it is theirs to make deliberately rather than to\n * discover afterwards (DECISIONS 0009).\n */\nconst OPT_IN_SEATS: ReadonlySet<string> = new Set([\"claude\"]);\n\nexport interface SeatStance {\n posture: SeatPosture;\n /** `declared` when the owner set it; `default` when nobody has, and `reason` says why that default. */\n source: \"declared\" | \"default\";\n reason: string;\n /** Willing *and* able: the posture allows it and the CLI is actually there and signed in. */\n usable: boolean;\n note?: string;\n}\n\n/**\n * What we should do with one seat right now, given what the owner declared and what detection found.\n *\n * Deliberately not clever. We know a plan's *name*, never its price, so nothing here infers that \"pro\" is cheaper\n * than \"max\" or that an unknown plan is a small one \u2014 the one fact only the owner has is which subscription they\n * would rather not spend, and the only honest way to learn it is to be told.\n */\nexport function stanceFor(seat: SeatInfo, policy: SeatPolicy): SeatStance {\n const declared = Object.hasOwn(policy.seats, seat.id) ? policy.seats[seat.id] : undefined;\n const posture: SeatPosture = declared?.posture ?? (OPT_IN_SEATS.has(seat.id) ? \"off\" : \"normal\");\n\n const reason =\n declared !== undefined\n ? \"you set this\"\n : OPT_IN_SEATS.has(seat.id)\n ? \"opt-in: a worker here spends the same subscription your session is running on\"\n : \"nobody has said otherwise\";\n\n return {\n posture,\n source: declared === undefined ? \"default\" : \"declared\",\n reason,\n usable: posture !== \"off\" && seat.supported && seat.signedIn === \"yes\",\n ...(declared?.note === undefined ? {} : { note: declared.note }),\n };\n}\n\n/** The seats a mission may draw on, most willing first, so a planner can take the head of the list. */\nexport function usableSeats(seats: readonly SeatInfo[], policy: SeatPolicy): SeatInfo[] {\n const rank: Record<SeatPosture, number> = { preferred: 0, normal: 1, sparing: 2, off: 3 };\n return seats\n .filter((seat) => stanceFor(seat, policy).usable)\n .sort((a, b) => rank[stanceFor(a, policy).posture] - rank[stanceFor(b, policy).posture]);\n}\n", "import { randomUUID } from \"node:crypto\";\nimport { chmodSync, closeSync, mkdirSync, openSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { DatabaseSync, type StatementSync } from \"node:sqlite\";\nimport { z } from \"zod\";\nimport {\n EVENT_VERSION,\n EventStamp,\n FanoutEvent,\n type FanoutEventInput,\n type StoredEvent,\n} from \"../schema/events.ts\";\n\n/*\n * The ledger is the single source of truth: an append-only SQLite table of validated events.\n * Append-only is enforced by the database, not just by this API: triggers abort any UPDATE, any DELETE, and any\n * INSERT that would replace an existing row (INSERT OR REPLACE deletes the old row without firing DELETE triggers).\n * This guards against rewriting history with ordinary SQL. It is not a defense against someone with raw access to\n * the file: they own it, and `DROP TABLE` or replacing a trigger would still succeed. Opening checks the guards exist.\n * Every row is validated on the way in and again on the way out, so a damaged ledger fails loudly.\n */\n\nconst SCHEMA_VERSION = 1;\n\nconst SCHEMA_V1 = `\nCREATE TABLE IF NOT EXISTS events (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n ts TEXT NOT NULL,\n v INTEGER NOT NULL,\n type TEXT NOT NULL,\n mission_id TEXT,\n run_id TEXT,\n body TEXT NOT NULL CHECK (json_valid(body))\n) STRICT;\nCREATE INDEX IF NOT EXISTS events_by_mission ON events (mission_id, seq);\nCREATE TRIGGER IF NOT EXISTS events_no_update BEFORE UPDATE ON events\n BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;\nCREATE TRIGGER IF NOT EXISTS events_no_delete BEFORE DELETE ON events\n BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;\nCREATE TRIGGER IF NOT EXISTS events_no_replace BEFORE INSERT ON events\n WHEN EXISTS (SELECT 1 FROM events WHERE seq = NEW.seq OR id = NEW.id)\n BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;\n`;\n\nconst GUARD_TRIGGERS = [\"events_no_update\", \"events_no_delete\", \"events_no_replace\"] as const;\n\nexport class LedgerError extends Error {\n override name = \"LedgerError\";\n}\n\n/** An event that does not match the schema. Nothing was written. */\nexport class InvalidEventError extends LedgerError {\n override name = \"InvalidEventError\";\n readonly index: number;\n\n constructor(index: number, detail: string) {\n super(`Event ${index} is invalid, nothing was written:\\n${detail}`);\n this.index = index;\n }\n}\n\n/** A ledger or an event written by a newer Fanout. We refuse to guess at it. */\nexport class UnsupportedLedgerError extends LedgerError {\n override name = \"UnsupportedLedgerError\";\n}\n\nexport interface LedgerOptions {\n /** Clock for event timestamps (tests inject a fixed one). */\n now?: () => Date;\n /** Event id generator; must return UUIDs. */\n newId?: () => string;\n /**\n * Called once per event, after it is committed, so a live feed never shows something the ledger might roll back.\n * Whatever it throws is ignored: a listener must not be able to break the record.\n */\n onAppend?: (event: StoredEvent) => void;\n}\n\nexport interface ReadOptions {\n /** Only events with a larger sequence number. */\n afterSeq?: number;\n /** Only events of this mission. */\n missionId?: string;\n /** At most this many events. */\n limit?: number;\n}\n\nconst Row = z.object({\n seq: z.number(),\n id: z.string(),\n ts: z.string(),\n v: z.number(),\n type: z.string(),\n mission_id: z.string().nullable(),\n run_id: z.string().nullable(),\n body: z.string(),\n});\n\nexport class Ledger {\n readonly #db: DatabaseSync;\n readonly #now: () => Date;\n readonly #newId: () => string;\n readonly #onAppend: ((event: StoredEvent) => void) | undefined;\n readonly #insert: StatementSync;\n readonly #readAll: StatementSync;\n readonly #readMission: StatementSync;\n readonly #lastSeq: StatementSync;\n\n private constructor(db: DatabaseSync, options: LedgerOptions) {\n this.#db = db;\n this.#now = options.now ?? (() => new Date());\n this.#newId = options.newId ?? randomUUID;\n this.#onAppend = options.onAppend;\n this.#insert = db.prepare(\n \"INSERT INTO events (id, ts, v, type, mission_id, run_id, body) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n );\n this.#readAll = db.prepare(\n \"SELECT seq, id, ts, v, type, mission_id, run_id, body FROM events WHERE seq > ? ORDER BY seq LIMIT ?\",\n );\n this.#readMission = db.prepare(\n \"SELECT seq, id, ts, v, type, mission_id, run_id, body FROM events \" +\n \"WHERE seq > ? AND mission_id = ? ORDER BY seq LIMIT ?\",\n );\n this.#lastSeq = db.prepare(\"SELECT COALESCE(MAX(seq), 0) AS seq FROM events\");\n }\n\n /**\n * Opens (or creates) a ledger. Use \":memory:\" for a throwaway one. On disk, the file is private to the user\n * (mode 600, directory 700).\n */\n static open(path: string, options: LedgerOptions = {}): Ledger {\n const onDisk = path !== \":memory:\";\n if (onDisk) {\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n closeSync(openSync(path, \"a\", 0o600));\n chmodSync(path, 0o600);\n }\n const db = new DatabaseSync(path);\n try {\n db.exec(\"PRAGMA busy_timeout = 5000\");\n if (onDisk) db.exec(\"PRAGMA journal_mode = WAL\");\n db.exec(\"PRAGMA synchronous = FULL\");\n migrate(db);\n return new Ledger(db, options);\n } catch (error) {\n db.close();\n throw error;\n }\n }\n\n /** Validates and records one event; returns it with its stamp. */\n append(input: FanoutEventInput): StoredEvent {\n const [stored] = this.appendAll([input]);\n if (stored === undefined) throw new LedgerError(\"append recorded nothing\");\n return stored;\n }\n\n /** Validates every event first, then records them all in one transaction, or none of them. */\n appendAll(inputs: readonly FanoutEventInput[]): StoredEvent[] {\n const events = inputs.map((input, index) => {\n const result = FanoutEvent.safeParse(input);\n if (!result.success) throw new InvalidEventError(index, z.prettifyError(result.error));\n return result.data;\n });\n\n this.#db.exec(\"BEGIN IMMEDIATE\");\n try {\n const stored = events.map((event): StoredEvent => {\n const stamp = EventStamp.omit({ seq: true }).parse({\n v: EVENT_VERSION,\n id: this.#newId(),\n ts: this.#now().toISOString(),\n });\n const result = this.#insert.run(\n stamp.id,\n stamp.ts,\n stamp.v,\n event.type,\n \"missionId\" in event ? event.missionId : null,\n \"runId\" in event ? event.runId : null,\n JSON.stringify(event),\n );\n return { ...event, ...stamp, seq: Number(result.lastInsertRowid) };\n });\n this.#db.exec(\"COMMIT\");\n for (const event of stored) {\n try {\n this.#onAppend?.(event);\n } catch {\n // A listener that throws has a problem of its own; the record is already safe.\n }\n }\n return stored;\n } catch (error) {\n this.#db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n /** Events in sequence order. */\n read(options: ReadOptions = {}): StoredEvent[] {\n const afterSeq = options.afterSeq ?? 0;\n const limit = options.limit ?? -1;\n const rows =\n options.missionId === undefined\n ? this.#readAll.all(afterSeq, limit)\n : this.#readMission.all(afterSeq, options.missionId, limit);\n return rows.map(decode);\n }\n\n /** The sequence number of the last event, or 0 for an empty ledger. */\n lastSeq(): number {\n const row = this.#lastSeq.get();\n return Number(row?.[\"seq\"] ?? 0);\n }\n\n /**\n * Whether this ledger can still be written to.\n *\n * A daemon shutting down closes the ledger while runs may still be in flight, and a process that exits a moment\n * later tries to record how it ended. That is expected, not exceptional, and a caller needs to be able to tell\n * it apart from a ledger that has actually broken \u2014 one means \"we are going away\", the other means \"stop the\n * run, we can no longer record what it is doing\".\n */\n get isOpen(): boolean {\n return this.#db.isOpen;\n }\n\n /** Idempotent: closing twice is what happens when shutdown and a test's cleanup both do the right thing. */\n close(): void {\n if (this.#db.isOpen) this.#db.close();\n }\n}\n\nfunction migrate(db: DatabaseSync): void {\n const version = userVersion(db);\n if (version > SCHEMA_VERSION) {\n throw new UnsupportedLedgerError(\n `This ledger was written by a newer Fanout (schema ${version}; this one reads ${SCHEMA_VERSION}). ` +\n \"Update Fanout to open it.\",\n );\n }\n if (version < SCHEMA_VERSION) {\n db.exec(\"BEGIN IMMEDIATE\");\n try {\n if (userVersion(db) < SCHEMA_VERSION) {\n db.exec(SCHEMA_V1);\n db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);\n }\n db.exec(\"COMMIT\");\n } catch (error) {\n db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n const triggers = new Set(\n db\n .prepare(\"SELECT name FROM sqlite_master WHERE type = 'trigger' AND tbl_name = 'events'\")\n .all()\n .map((row) => String(row[\"name\"])),\n );\n const missing = GUARD_TRIGGERS.filter((name) => !triggers.has(name));\n if (missing.length > 0) {\n throw new LedgerError(\n `This ledger lost its append-only guard (${missing.join(\", \")}); refusing to use it.`,\n );\n }\n}\n\nfunction userVersion(db: DatabaseSync): number {\n return Number(db.prepare(\"PRAGMA user_version\").get()?.[\"user_version\"] ?? 0);\n}\n\nfunction decode(raw: unknown): StoredEvent {\n const row = Row.parse(raw);\n if (row.v !== EVENT_VERSION) {\n throw new UnsupportedLedgerError(\n `Event ${row.seq} has version ${row.v}; this Fanout reads version ${EVENT_VERSION}. Update Fanout to read it.`,\n );\n }\n let body: unknown;\n try {\n body = JSON.parse(row.body);\n } catch {\n throw new LedgerError(`Event ${row.seq} is not valid JSON; the ledger is damaged.`);\n }\n const event = FanoutEvent.safeParse(body);\n const stamp = EventStamp.safeParse({ v: row.v, id: row.id, seq: row.seq, ts: row.ts });\n if (!event.success || !stamp.success) {\n const detail = event.error ?? stamp.error;\n throw new LedgerError(\n `Event ${row.seq} does not match the schema; the ledger is damaged.` +\n (detail === undefined ? \"\" : `\\n${z.prettifyError(detail)}`),\n );\n }\n\n // The indexed columns are how events are found; if they disagree with the body, queries would silently lie.\n const routed =\n row.type === event.data.type &&\n row.mission_id === (\"missionId\" in event.data ? event.data.missionId : null) &&\n row.run_id === (\"runId\" in event.data ? event.data.runId : null);\n if (!routed) {\n throw new LedgerError(\n `Event ${row.seq} is indexed as ${row.type} (mission ${row.mission_id ?? \"none\"}, ` +\n `run ${row.run_id ?? \"none\"}) but its body says otherwise; the ledger is damaged.`,\n );\n }\n\n return { ...event.data, ...stamp.data };\n}\n", "import { RunProgress } from \"fanout-core\";\nimport { z } from \"zod\";\n\n/*\n * The fake agent's stdout: one JSON object per line. It stands in for a vendor CLI's stream, so the adapter parses it\n * the same way a real adapter parses Codex's or Kimi's output. The CLI writes it and the adapter reads it with this\n * one schema.\n */\nexport const OutputLine = z.discriminatedUnion(\"kind\", [\n z.strictObject({\n kind: z.literal(\"phase\"),\n phase: RunProgress.shape.phase,\n detail: z.string().max(500).optional(),\n }),\n z.strictObject({\n kind: z.literal(\"tool\"),\n tool: z.string().min(1).max(100),\n summary: z.string().max(500).optional(),\n files: z.array(z.string().min(1).max(1000)).max(200),\n }),\n z.strictObject({ kind: z.literal(\"usage\"), amount: z.int().nonnegative(), unit: z.literal(\"messages\") }),\n z.strictObject({ kind: z.literal(\"limit\"), message: z.string().min(1).max(500) }),\n z.strictObject({ kind: z.literal(\"sleep\"), ms: z.int().nonnegative() }),\n z.strictObject({ kind: z.literal(\"report\"), text: z.string().max(20_000) }),\n]);\nexport type OutputLine = z.infer<typeof OutputLine>;\n\n/** Exit codes besides the scenario's own. */\nexport const EXIT = {\n /** A limit step was played: the simulated seat ran out of usage. */\n limit: 2,\n /** Bad arguments or an invalid scenario (EX_USAGE). */\n usage: 64,\n /** The scenario tried to write outside the working directory (EX_DATAERR). */\n unsafeWrite: 65,\n /** Anything unexpected (EX_SOFTWARE). */\n internal: 70,\n} as const;\n", "import { RunProgress } from \"fanout-core\";\nimport { z } from \"zod\";\n\n/*\n * What the fake agent does, step by step. Deterministic by design: no randomness, no clock in the output.\n * Example:\n * { \"steps\": [ { \"phase\": \"reading\", \"delayMs\": 300 },\n * { \"tool\": \"edit\", \"summary\": \"add csv writer\", \"write\": { \"src/api/csv.ts\": \"export \u2026\" } },\n * { \"usage\": 2 }, { \"limit\": \"usage limit reached\" } ],\n * \"report\": \"Added the endpoint.\", \"exitCode\": 0, \"timeScale\": 0.2 }\n */\n\nconst Delay = z.int().nonnegative().max(60_000).optional();\n\nconst PhaseStep = z.strictObject({\n phase: RunProgress.shape.phase,\n detail: z.string().max(500).optional(),\n delayMs: Delay,\n});\n\nconst ToolStep = z.strictObject({\n tool: z.string().min(1).max(100),\n summary: z.string().max(500).optional(),\n /** Files to really write, relative to the working directory, with their content. */\n write: z.record(z.string().min(1).max(1000), z.string().max(1_000_000)).optional(),\n delayMs: Delay,\n});\n\nconst UsageStep = z.strictObject({ usage: z.int().nonnegative().max(1_000_000), delayMs: Delay });\n\n/** The seat runs out of usage: the agent prints the message, writes its report and exits with code 2. */\nconst LimitStep = z.strictObject({ limit: z.string().min(1).max(500), delayMs: Delay });\n\nconst SleepStep = z.strictObject({ sleep: z.int().nonnegative().max(60_000) });\n\nexport const ScenarioStep = z.union([PhaseStep, ToolStep, UsageStep, LimitStep, SleepStep]);\nexport type ScenarioStep = z.infer<typeof ScenarioStep>;\n\nexport const Scenario = z.strictObject({\n steps: z.array(ScenarioStep).max(1000),\n report: z.string().max(20_000),\n exitCode: z.int().min(0).max(255).default(0),\n /** Keep running after the last step, until killed (to exercise timeouts). */\n hang: z.boolean().default(false),\n /** Multiplies every delay: 0.2 plays five times faster (the demo), 0 plays instantly (tests). */\n timeScale: z.number().nonnegative().max(100).default(1),\n});\nexport type Scenario = z.infer<typeof Scenario>;\nexport type ScenarioInput = z.input<typeof Scenario>;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { parseArgs } from \"node:util\";\nimport { EXIT, type OutputLine } from \"./protocol.ts\";\nimport { Scenario } from \"./scenario.ts\";\n\n/*\n * A deterministic stand-in for an agent CLI:\n * node src/cli.ts --scenario-json '<json>' --report <path> -- <prompt>\n * It plays the scenario: prints its stream (protocol.ts), really writes files inside its working directory, writes\n * the report (the daemon chooses that path, usually outside the worktree), and exits. Same scenario, same stdout.\n */\n\nclass CliError extends Error {\n readonly code: number;\n\n constructor(code: number, message: string) {\n super(message);\n this.code = code;\n }\n}\n\nfunction readArgs(args: string[]): { scenario: Scenario; reportPath: string } {\n let parsed;\n try {\n parsed = parseArgs({\n args,\n options: { \"scenario-json\": { type: \"string\" }, report: { type: \"string\" } },\n allowPositionals: true,\n strict: true,\n });\n } catch (error) {\n throw new CliError(EXIT.usage, error instanceof Error ? error.message : String(error));\n }\n const { values, positionals } = parsed;\n if (values[\"scenario-json\"] === undefined) throw new CliError(EXIT.usage, \"missing --scenario-json\");\n if (values.report === undefined || values.report === \"\") throw new CliError(EXIT.usage, \"missing --report\");\n if (positionals.join(\" \").trim() === \"\") throw new CliError(EXIT.usage, \"missing prompt\");\n\n let json: unknown;\n try {\n json = JSON.parse(values[\"scenario-json\"]);\n } catch {\n throw new CliError(EXIT.usage, \"--scenario-json is not valid JSON\");\n }\n const scenario = Scenario.safeParse(json);\n if (!scenario.success) throw new CliError(EXIT.usage, `invalid scenario: ${scenario.error.message}`);\n return { scenario: scenario.data, reportPath: values.report };\n}\n\n/** Resolves a scenario write path, refusing anything outside the working directory. */\nfunction insideCwd(cwd: string, path: string): string {\n const target = resolve(cwd, path);\n const fromCwd = relative(cwd, target);\n if (isAbsolute(path) || fromCwd === \"\" || fromCwd === \"..\" || fromCwd.startsWith(`..${sep}`)) {\n throw new CliError(EXIT.unsafeWrite, `refusing to write outside the working directory: ${path}`);\n }\n return target;\n}\n\nfunction writeFile(path: string, content: string): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content, \"utf8\");\n}\n\nfunction emit(line: OutputLine): void {\n process.stdout.write(`${JSON.stringify(line)}\\n`);\n}\n\nasync function play(scenario: Scenario, cwd: string, reportPath: string): Promise<number> {\n const wait = async (ms: number | undefined): Promise<void> => {\n if (ms !== undefined && ms > 0) await sleep(ms * scenario.timeScale);\n };\n\n for (const step of scenario.steps) {\n if (\"sleep\" in step) {\n await wait(step.sleep);\n emit({ kind: \"sleep\", ms: step.sleep });\n continue;\n }\n await wait(step.delayMs);\n if (\"phase\" in step) {\n emit({\n kind: \"phase\",\n phase: step.phase,\n ...(step.detail === undefined ? {} : { detail: step.detail }),\n });\n } else if (\"tool\" in step) {\n const writes = Object.entries(step.write ?? {});\n const targets = writes.map(([path]) => insideCwd(cwd, path));\n writes.forEach(([, content], index) => {\n const target = targets[index];\n if (target !== undefined) writeFile(target, content);\n });\n emit({\n kind: \"tool\",\n tool: step.tool,\n ...(step.summary === undefined ? {} : { summary: step.summary }),\n files: writes.map(([path]) => path),\n });\n } else if (\"usage\" in step) {\n emit({ kind: \"usage\", amount: step.usage, unit: \"messages\" });\n } else {\n emit({ kind: \"limit\", message: step.limit });\n writeFile(reportPath, scenario.report);\n return EXIT.limit;\n }\n }\n\n writeFile(reportPath, scenario.report);\n emit({ kind: \"report\", text: scenario.report });\n if (scenario.hang) {\n // A pending promise alone lets Node exit; a timer keeps the process alive until it is killed.\n await new Promise<never>(() => setInterval(() => undefined, 60_000));\n }\n return scenario.exitCode;\n}\n\nasync function main(args: string[]): Promise<number> {\n const { scenario, reportPath } = readArgs(args);\n const cwd = process.cwd();\n return play(scenario, cwd, resolve(cwd, reportPath));\n}\n\nmain(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n const code = error instanceof CliError ? error.code : EXIT.internal;\n process.stderr.write(`fake seat: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = code;\n },\n);\n", "import { z } from \"zod\";\n\n/** Identifiers people read: mission, line, run and seat ids (\"csv-export\", \"api-builder-1\", \"codex\"). */\nexport const Slug = z\n .string()\n .regex(/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/, \"use lowercase letters, digits and inner dashes (max 64)\");\n\nexport const MissionId = Slug;\nexport const LineId = Slug;\nexport const RunId = Slug;\nexport const SeatId = Slug;\n\n/** A full commit id: 40 hex characters (SHA-1 repositories) or 64 (SHA-256 repositories). */\nexport const GitSha = z.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, \"a full commit sha\");\n\n/**\n * The identity of one piece of work: a hash of exactly what a run changed, at the moment we looked.\n *\n * An agent never commits, so its work has no commit id to name it by, and \"the diff in that worktree\" is not an\n * identity \u2014 it is a thing that can change between the moment it is reviewed and the moment it is merged. Every\n * step of the gate records the revision it judged, and a merge applies only a revision that every step agreed on.\n * Without that, \"reviewed and checked\" means \"reviewed and checked something, once\".\n */\nexport const WorkRevision = z.string().regex(/^[0-9a-f]{64}$/, \"a work revision (sha-256 of the diff)\");\n\n/** Which seat runs a line, and optionally with which model and effort. */\nexport const SeatRef = z.strictObject({\n id: SeatId,\n model: z.string().min(1).max(100).optional(),\n effort: z.string().min(1).max(40).optional(),\n});\nexport type SeatRef = z.infer<typeof SeatRef>;\n\n/** What the detector knows about an installed agent CLI. Unknown facts say \"unknown\" or null, never a guess. */\nexport const SeatInfo = z.strictObject({\n id: SeatId,\n displayName: z.string().min(1).max(80),\n binary: z.string().min(1).max(200),\n version: z.string().min(1).max(100).nullable(),\n supported: z.boolean(),\n signedIn: z.enum([\"yes\", \"no\", \"unknown\"]),\n models: z.array(z.string().min(1).max(100)).max(100),\n efforts: z.array(z.string().min(1).max(40)).max(20),\n billing: z.enum([\"subscription\", \"credit\", \"api\", \"unknown\"]),\n /**\n * The subscription tier this seat is on, and where that answer came from \u2014 `null` when the CLI does not report\n * one, which is most of them. The source travels with the value on purpose: \"detected\" is a fact the CLI told us\n * this run, \"declared\" is something the owner typed once and may since have outgrown, and a reader deciding how\n * much to trust a routing decision deserves to know which it is looking at.\n */\n plan: z\n .strictObject({\n name: z.string().min(1).max(100),\n source: z.enum([\"detected\", \"declared\"]),\n })\n .nullable(),\n});\nexport type SeatInfo = z.infer<typeof SeatInfo>;\n\nexport const DiffStat = z.strictObject({\n files: z.int().nonnegative(),\n insertions: z.int().nonnegative(),\n deletions: z.int().nonnegative(),\n});\nexport type DiffStat = z.infer<typeof DiffStat>;\n\nexport const MissionLimits = z.strictObject({\n maxParallel: z.int().min(1).max(32),\n timeoutMinutes: z\n .int()\n .min(1)\n .max(24 * 60),\n});\nexport type MissionLimits = z.infer<typeof MissionLimits>;\n\n/** One line of the safety report. A failed \"block\" check stops the launch; a failed \"warn\" check is shown. */\nexport const SafetyCheck = z.strictObject({\n id: z.string().min(1).max(64),\n ok: z.boolean(),\n severity: z.enum([\"block\", \"warn\"]),\n message: z.string().min(1).max(2000),\n lineIds: z.array(LineId).max(32).optional(),\n});\nexport type SafetyCheck = z.infer<typeof SafetyCheck>;\n", "import { z } from \"zod\";\n\n/*\n * Write scopes are repo-relative POSIX globs with a deliberately small syntax:\n * `*` and `?` match within one path segment, `**` (a whole segment) matches zero or more segments.\n * Every pattern also covers everything below what it matches, so `src/api` and `src/api/**` are the same scope.\n * Braces, character classes and negation are not supported: a scope must be obvious to the person approving it.\n *\n * Every other character is literal, so ordinary filenames work: spaces, parentheses, accents, CJK, emoji.\n * Only `*` and `?` are special, and there is no escape for them; a file whose name really contains one is covered\n * by a scope that ends in `**`. Separators, empty segments, `.` and `..` are refused, as are control characters.\n *\n * Matching is deliberately hand-written rather than translated to regular expressions: a pattern like `a*a*a*\u2026z`\n * makes a backtracking engine take exponential time, and scopes come from plans we must be able to check quickly.\n */\n\n// eslint-disable-next-line no-control-regex -- control characters are exactly what a path segment must not contain\nconst SEGMENT = /^(?:\\*\\*|[^/\u0000-\u001F\u007F]+)$/;\nconst WILDCARD = /[*?]/;\n\nexport function isValidScopeGlob(glob: string): boolean {\n if (glob.length === 0 || glob.startsWith(\"/\") || glob.endsWith(\"/\")) return false;\n return glob\n .split(\"/\")\n .every(\n (segment) =>\n SEGMENT.test(segment) &&\n segment !== \".\" &&\n segment !== \"..\" &&\n (segment === \"**\" || !segment.includes(\"**\")),\n );\n}\n\nexport const ScopeGlob = z.string().max(300).refine(isValidScopeGlob, {\n message: \"use a repo-relative path or glob (`*`, `?`, `**`), without `..`, leading or trailing `/`\",\n});\n\n/**\n * A plain repo-relative path: no leading slash, no `.` or `..`, no empty segments. Anything else (a path that still\n * needs resolving, or one from outside the repository) is not inside any scope, whatever it looks like.\n */\nexport function isRepoPath(path: string): boolean {\n if (path.length === 0 || path.startsWith(\"/\") || path.endsWith(\"/\")) return false;\n return path.split(\"/\").every((segment) => segment !== \"\" && segment !== \".\" && segment !== \"..\");\n}\n\n/** Segments of a pattern, with the implicit \"and everything below\" made explicit. */\nfunction scopeSegments(glob: string): string[] {\n const segments = glob.split(\"/\");\n return segments.at(-1) === \"**\" ? segments : [...segments, \"**\"];\n}\n\n/**\n * Matches one segment pattern (`*`, `?`) against one name, in linear time: on a mismatch it returns to the last `*`\n * and gives it one more character, so no input can make it backtrack exponentially.\n */\nfunction segmentMatches(pattern: string, text: string): boolean {\n let p = 0;\n let t = 0;\n let starAt = -1;\n let matchedAt = 0;\n\n while (t < text.length) {\n const token = pattern[p];\n if (token === \"?\" || (token !== undefined && token !== \"*\" && token === text[t])) {\n p += 1;\n t += 1;\n } else if (token === \"*\") {\n starAt = p;\n matchedAt = t;\n p += 1;\n } else if (starAt >= 0) {\n matchedAt += 1;\n p = starAt + 1;\n t = matchedAt;\n } else {\n return false;\n }\n }\n while (pattern[p] === \"*\") p += 1;\n return p === pattern.length;\n}\n\n/** The literal text before the first wildcard and after the last one. */\nfunction literalEnds(segment: string): [prefix: string, suffix: string] {\n const first = segment.search(WILDCARD);\n let last = segment.length - 1;\n while (last >= 0 && !WILDCARD.test(segment.charAt(last))) last -= 1;\n return [segment.slice(0, first), segment.slice(last + 1)];\n}\n\n/**\n * Whether two single-segment patterns can match a common name. Exact when at least one side is literal; when both\n * have wildcards it compares their literal ends, which can only err towards \"yes\" (the safe side for scopes).\n */\nfunction segmentsMayOverlap(a: string, b: string): boolean {\n const aWild = WILDCARD.test(a);\n const bWild = WILDCARD.test(b);\n if (!aWild && !bWild) return a === b;\n if (!aWild) return segmentMatches(b, a);\n if (!bWild) return segmentMatches(a, b);\n const [aPrefix, aSuffix] = literalEnds(a);\n const [bPrefix, bSuffix] = literalEnds(b);\n return (\n (aPrefix.startsWith(bPrefix) || bPrefix.startsWith(aPrefix)) &&\n (aSuffix.endsWith(bSuffix) || bSuffix.endsWith(aSuffix))\n );\n}\n\n/**\n * Whether some file path could fall inside both scopes. Sound: it never answers \"no\" when a common path exists.\n * It may answer \"yes\" for exotic wildcard pairs that cannot actually meet; the plan then asks for narrower scopes.\n */\nexport function scopesMayOverlap(a: string, b: string): boolean {\n const left = scopeSegments(a);\n const right = scopeSegments(b);\n const memo = new Map<number, boolean>();\n const width = right.length + 1;\n\n const from = (i: number, j: number): boolean => {\n const key = i * width + j;\n const known = memo.get(key);\n if (known !== undefined) return known;\n let result: boolean;\n const l = left[i];\n const r = right[j];\n if (l === undefined && r === undefined) result = true;\n else if (l === \"**\") result = from(i + 1, j) || (r !== undefined && from(i, j + 1));\n else if (r === \"**\") result = from(i, j + 1) || (l !== undefined && from(i + 1, j));\n else if (l === undefined || r === undefined) result = false;\n else result = segmentsMayOverlap(l, r) && from(i + 1, j + 1);\n memo.set(key, result);\n return result;\n };\n\n return from(0, 0);\n}\n\n/** Whether a repo-relative file path falls inside a scope. */\nexport function pathInScope(path: string, glob: string): boolean {\n if (!isRepoPath(path)) return false;\n const parts = path.split(\"/\");\n const pattern = scopeSegments(glob);\n const memo = new Map<number, boolean>();\n const width = pattern.length + 1;\n\n const from = (i: number, j: number): boolean => {\n const key = i * width + j;\n const known = memo.get(key);\n if (known !== undefined) return known;\n const segment = pattern[j];\n const part = parts[i];\n let result: boolean;\n if (segment === undefined) result = i === parts.length;\n else if (segment === \"**\") result = from(i, j + 1) || (i < parts.length && from(i + 1, j));\n else result = part !== undefined && segmentMatches(segment, part) && from(i + 1, j + 1);\n memo.set(key, result);\n return result;\n };\n\n return from(0, 0);\n}\n", "import { z } from \"zod\";\nimport { LineId, SeatRef } from \"./common.ts\";\nimport { ScopeGlob, scopesMayOverlap } from \"./scope.ts\";\n\nexport const LineRole = z.enum([\"auditor\", \"builder\", \"tester\"]);\nexport type LineRole = z.infer<typeof LineRole>;\n\n/** One task in a mission. Auditors are read-only; builders and testers declare where they may write. */\nexport const PlanLine = z.strictObject({\n id: LineId,\n title: z.string().trim().min(1).max(120),\n role: LineRole,\n prompt: z.string().min(1).max(100_000),\n seat: SeatRef,\n scope: z.strictObject({ write: z.array(ScopeGlob).max(64) }),\n dependsOn: z.array(LineId).max(32).default([]),\n checks: z.array(z.string().min(1).max(500)).max(16).default([]),\n timeoutMinutes: z.int().min(1).max(240).optional(),\n /**\n * This line fixes a bug, so the gate will not merge it without a test proven to fail on the old code.\n *\n * Declared when the mission is planned rather than judged afterwards, because the moment to decide whether\n * something is a fix is before an agent has written a persuasive explanation of why its change is fine.\n */\n fixesBug: z.boolean().default(false),\n});\nexport type PlanLine = z.infer<typeof PlanLine>;\n\nexport const PlanGraph = z.strictObject({\n lines: z.array(PlanLine).min(1).max(32),\n});\nexport type PlanGraph = z.infer<typeof PlanGraph>;\n\nexport type PlanIssueCode =\n | \"duplicate_line\"\n | \"unknown_dependency\"\n | \"self_dependency\"\n | \"dependency_cycle\"\n | \"auditor_writes\"\n | \"missing_write_scope\"\n | \"scope_overlap\";\n\nexport interface PlanIssue {\n code: PlanIssueCode;\n message: string;\n lineIds: string[];\n}\n\n/**\n * Checks the rules a schema can't express: the dependency graph and the write scopes of lines that may run at the\n * same time. Returns every issue found, in a stable order; an empty list means the plan is launchable.\n */\nexport function validatePlan(plan: PlanGraph): PlanIssue[] {\n const issues: PlanIssue[] = [];\n const { lines } = plan;\n\n const indexById = new Map<string, number>();\n lines.forEach((line, index) => {\n if (indexById.has(line.id)) {\n issues.push({\n code: \"duplicate_line\",\n message: `Line id \"${line.id}\" is used more than once.`,\n lineIds: [line.id],\n });\n } else {\n indexById.set(line.id, index);\n }\n });\n\n const edges: number[][] = lines.map((line) => {\n const targets: number[] = [];\n for (const dependency of line.dependsOn) {\n if (dependency === line.id) {\n issues.push({\n code: \"self_dependency\",\n message: `Line \"${line.id}\" depends on itself.`,\n lineIds: [line.id],\n });\n continue;\n }\n const target = indexById.get(dependency);\n if (target === undefined) {\n issues.push({\n code: \"unknown_dependency\",\n message: `Line \"${line.id}\" depends on \"${dependency}\", which is not in the plan.`,\n lineIds: [line.id],\n });\n } else {\n targets.push(target);\n }\n }\n return targets;\n });\n\n for (const cycle of findCycles(edges)) {\n const ids = cycle.map((index) => lines[index]?.id ?? \"?\");\n issues.push({\n code: \"dependency_cycle\",\n message: `Dependencies form a cycle: ${[...ids, ids[0]].join(\" \u2192 \")}.`,\n lineIds: ids,\n });\n }\n\n for (const line of lines) {\n if (line.role === \"auditor\" && line.scope.write.length > 0) {\n issues.push({\n code: \"auditor_writes\",\n message: `Line \"${line.id}\" is an auditor, so it is read-only; remove its write scope or make it a builder.`,\n lineIds: [line.id],\n });\n }\n if (line.role !== \"auditor\" && line.scope.write.length === 0) {\n issues.push({\n code: \"missing_write_scope\",\n message: `Line \"${line.id}\" is a ${line.role} but declares no write scope.`,\n lineIds: [line.id],\n });\n }\n }\n\n const reaches = reachability(edges);\n for (let i = 0; i < lines.length; i += 1) {\n for (let j = i + 1; j < lines.length; j += 1) {\n const a = lines[i];\n const b = lines[j];\n if (a === undefined || b === undefined) continue;\n if (reaches[i]?.has(j) === true || reaches[j]?.has(i) === true) continue;\n const clash = firstOverlap(a.scope.write, b.scope.write);\n if (clash !== undefined) {\n issues.push({\n code: \"scope_overlap\",\n message:\n `Lines \"${a.id}\" and \"${b.id}\" can run at the same time and may both write ` +\n `\"${clash[0]}\" / \"${clash[1]}\". Make one depend on the other, or narrow the scopes.`,\n lineIds: [a.id, b.id],\n });\n }\n }\n }\n\n return issues;\n}\n\nfunction firstOverlap(left: string[], right: string[]): [string, string] | undefined {\n for (const a of left) {\n for (const b of right) {\n if (scopesMayOverlap(a, b)) return [a, b];\n }\n }\n return undefined;\n}\n\n/** For each node, every node it can reach by following edges (its transitive dependencies). */\nfunction reachability(edges: number[][]): Set<number>[] {\n return edges.map((_, start) => {\n const seen = new Set<number>();\n const stack = [...(edges[start] ?? [])];\n for (let next = stack.pop(); next !== undefined; next = stack.pop()) {\n if (seen.has(next)) continue;\n seen.add(next);\n stack.push(...(edges[next] ?? []));\n }\n return seen;\n });\n}\n\n/** One cycle per back edge found by a depth-first search, each listed from its first node in plan order. */\nfunction findCycles(edges: number[][]): number[][] {\n const state = new Array<\"new\" | \"open\" | \"done\">(edges.length).fill(\"new\");\n const path: number[] = [];\n const cycles: number[][] = [];\n\n const visit = (node: number): void => {\n state[node] = \"open\";\n path.push(node);\n for (const next of edges[node] ?? []) {\n if (state[next] === \"open\") {\n cycles.push(path.slice(path.indexOf(next)));\n } else if (state[next] === \"new\") {\n visit(next);\n }\n }\n path.pop();\n state[node] = \"done\";\n };\n\n edges.forEach((_, node) => {\n if (state[node] === \"new\") visit(node);\n });\n return cycles;\n}\n", "import { z } from \"zod\";\nimport {\n DiffStat,\n GitSha,\n LineId,\n MissionId,\n MissionLimits,\n RunId,\n SafetyCheck,\n SeatId,\n SeatInfo,\n SeatRef,\n WorkRevision,\n} from \"./common.ts\";\nimport { PlanGraph } from \"./plan.ts\";\n\n/*\n * Every fact the daemon records is one of these events. Rules for changing this file:\n * - Adding a new event type is additive: old ledgers stay valid.\n * - Changing the shape of an existing type needs a new EVENT_VERSION and an upgrade path for stored events.\n * - Events carry no secrets and no raw logs: summaries, paths and numbers only.\n */\n\nexport const EVENT_VERSION = 1 as const;\n\nconst mission = { missionId: MissionId };\nconst run = { missionId: MissionId, runId: RunId };\n\nconst PlanAuthor = z.enum([\"lead\", \"user\"]);\nconst Phase = z.enum([\"reading\", \"coding\", \"testing\", \"reporting\"]);\nconst RepoPaths = z.array(z.string().min(1).max(1000)).max(1000);\n\nexport const SeatDetected = z.strictObject({\n type: z.literal(\"seat.detected\"),\n seat: SeatInfo,\n});\n\nexport const MissionCreated = z.strictObject({\n type: z.literal(\"mission.created\"),\n ...mission,\n goal: z.string().trim().min(1).max(4000),\n repo: z.strictObject({ root: z.string().min(1).max(1000), baseCommit: GitSha }),\n limits: MissionLimits,\n});\n\nexport const PlanProposed = z.strictObject({\n type: z.literal(\"plan.proposed\"),\n ...mission,\n plan: PlanGraph,\n by: PlanAuthor,\n note: z.string().max(2000).optional(),\n});\n\nexport const PlanRevised = z.strictObject({\n type: z.literal(\"plan.revised\"),\n ...mission,\n plan: PlanGraph,\n by: PlanAuthor,\n note: z.string().max(2000).optional(),\n});\n\nexport const SafetyReported = z\n .strictObject({\n type: z.literal(\"safety.report\"),\n ...mission,\n /** Which plan revision this report describes. A newer plan makes it stale, never current. */\n planRevision: z.int().positive(),\n ok: z.boolean(),\n checks: z.array(SafetyCheck).max(200),\n })\n .refine((report) => report.ok === report.checks.every((check) => check.ok || check.severity === \"warn\"), {\n message: \"ok must be true exactly when no blocking check failed\",\n path: [\"ok\"],\n });\n\nexport const RunQueued = z.strictObject({\n type: z.literal(\"run.queued\"),\n ...run,\n lineId: LineId,\n seat: SeatRef,\n attempt: z.int().min(1).max(3),\n});\n\nexport const RunStarted = z.strictObject({\n type: z.literal(\"run.started\"),\n ...run,\n workdir: z.string().min(1).max(1000),\n argv: z.array(z.string().max(200_000)).min(1).max(200),\n /**\n * The process id of the daemon supervising this run \u2014 not the agent's own.\n *\n * It exists to answer one question later: is anybody still watching this? A run whose supervisor is gone\n * cannot still be running, however the ledger last left it. Without this a session that ends while a mission\n * is in flight leaves a run recorded as running forever, and nothing can tell that apart from one that\n * genuinely is.\n *\n * Absent on runs recorded before this existed; read those as unknown rather than as dead.\n */\n owner: z.int().positive().optional(),\n});\n\n/**\n * The agent's own name for this conversation, learned as soon as we have it.\n *\n * Recorded because rework depends on it: replying into the session that wrote a diff is worth far more than\n * re-explaining the work to a stranger who happens to share its model. Some CLIs let us choose the id before\n * launch and some announce it in their stream (ADR 0018); either way it is written down the moment it is known,\n * because the run most likely to need rework is the one that ended badly.\n */\nexport const RunSession = z.strictObject({\n type: z.literal(\"run.session\"),\n ...run,\n sessionId: z.string().trim().min(1).max(200),\n});\n\nexport const RunProgress = z.strictObject({\n type: z.literal(\"run.progress\"),\n ...run,\n phase: Phase,\n detail: z.string().max(500).optional(),\n});\n\nexport const RunTool = z.strictObject({\n type: z.literal(\"run.tool\"),\n ...run,\n tool: z.string().min(1).max(100),\n summary: z.string().max(500).optional(),\n files: RepoPaths.default([]),\n});\n\nexport const RunUsage = z.strictObject({\n type: z.literal(\"run.usage\"),\n ...run,\n seat: SeatId,\n amount: z.number().nonnegative(),\n unit: z.enum([\"messages\", \"tokens\", \"minutes\"]),\n estimated: z.boolean(),\n});\n\nexport const RunFinished = z.strictObject({\n type: z.literal(\"run.finished\"),\n ...run,\n status: z.enum([\"done\", \"failed\", \"killed\", \"timeout\"]),\n exitCode: z.int().nullable(),\n reportPath: z.string().min(1).max(1000).optional(),\n diffStat: DiffStat.optional(),\n});\n\n/*\n * The merge gate, in events. Every one of them names the `revision` it judged, because each is a statement about a\n * specific diff and not about a worktree that may since have moved. A merge applies a revision only when review,\n * checks, proof and approval all named that same one; anything else is a claim about work nobody looked at.\n */\n\nexport const ReviewDone = z.strictObject({\n type: z.literal(\"review.done\"),\n ...run,\n revision: WorkRevision,\n verdict: z.enum([\"accept\", \"rework\", \"reject\"]),\n notes: z.string().max(20_000),\n by: SeatRef,\n});\n\nexport const ChecksDone = z.strictObject({\n type: z.literal(\"checks.done\"),\n ...run,\n revision: WorkRevision,\n ok: z.boolean(),\n summary: z.string().max(4000),\n /** What actually ran, so \"checks pass\" can be read as a claim about specific commands. */\n commands: z.array(z.string().min(1).max(500)).max(50),\n});\n\n/** Proof of a fix: the new tests that fail on the old code (and pass on the new). */\nexport const ProofDone = z\n .strictObject({\n type: z.literal(\"proof.done\"),\n ...run,\n revision: WorkRevision,\n ok: z.boolean(),\n failedOnOld: z.array(z.string().min(1).max(500)).max(500),\n })\n .refine((proof) => !proof.ok || proof.failedOnOld.length > 0, {\n message: \"a passing proof names at least one test that failed on the old code\",\n path: [\"failedOnOld\"],\n });\n\n/**\n * Someone said yes. Recorded separately from the merge itself so a replay can answer \"who authorised this?\" \u2014\n * a question a diff in the history cannot answer on its own.\n */\nexport const MergeApproved = z.strictObject({\n type: z.literal(\"merge.approved\"),\n ...run,\n revision: WorkRevision,\n /**\n * A person, or a policy the person wrote down in advance. A policy must name itself: \"it was pre-approved\" is\n * not an answer anyone can audit, and \"which rule, written when\" is.\n */\n by: z.discriminatedUnion(\"kind\", [\n z.strictObject({\n kind: z.literal(\"user\"),\n /**\n * How we know. This is the difference between a fact and an agent's account of one.\n *\n * `direct` \u2014 the daemon received the click itself, from the mission view, on this machine. Nothing in\n * between could have invented it.\n *\n * `relayed` \u2014 the lead says it asked and quoted the answer in `note`. That is a claim by a language model\n * about a conversation, and an agent that skipped the asking writes a byte-identical event. It is worth\n * recording and it is not worth confusing with the first one.\n *\n * Absent on events written before Fanout drew the distinction; read those as `relayed`.\n */\n via: z.enum([\"direct\", \"relayed\"]).optional(),\n }),\n z.strictObject({ kind: z.literal(\"policy\"), name: z.string().trim().min(1).max(200) }),\n ]),\n note: z.string().max(2000).optional(),\n});\n\nexport const MergeApplied = z.strictObject({\n type: z.literal(\"merge.applied\"),\n ...run,\n revision: WorkRevision,\n files: RepoPaths.min(1),\n /** Where the work landed, so a dependent line can start from it rather than from a guess. */\n commit: GitSha,\n});\n\nexport const MergeConflict = z.strictObject({\n type: z.literal(\"merge.conflict\"),\n ...run,\n revision: WorkRevision,\n files: RepoPaths.min(1),\n});\n\nexport const RunDropped = z.strictObject({\n type: z.literal(\"run.dropped\"),\n ...run,\n reason: z.string().trim().min(1).max(2000),\n});\n\n/**\n * A second vendor read the lead's own uncommitted work.\n *\n * Not a mission and not a run: no agent worked in a worktree, and forcing this into the mission machinery would\n * put a fake mission in front of the user for every review. It carries no `missionId` for the same reason\n * `seat.detected` does not \u2014 it is a fact about this machine at a moment, not about a mission.\n *\n * This is the event that answers the product's only real question: was anything other than the author's own\n * judgement applied to this code before it was called done?\n */\nexport const BuddyReviewed = z.strictObject({\n type: z.literal(\"buddy.reviewed\"),\n /** Which working tree, so a review of one repository is never read as covering another. */\n repoRoot: z.string().min(1).max(1000),\n revision: WorkRevision,\n by: SeatRef,\n /** What it said, verbatim. A second opinion summarised by the author is not a second opinion. */\n findings: z.string().max(100_000),\n /** False when the reviewer could not be run at all, so \"no findings\" never stands in for \"never asked\". */\n ran: z.boolean(),\n files: RepoPaths.max(1000),\n});\n\n/**\n * The lead wrote down what it believes, and a cold reader checked each belief against the code.\n *\n * This is the sharpest thing a second vendor can do, and the cheapest. The lead carries the whole session \u2014 the\n * plan, the reasoning, the justification \u2014 and that context is precisely what makes its own mistakes invisible to\n * it: it knows why the code is right, so the code looks right. A reader arriving with only the diff is not\n * smarter, it is differently placed, which is why even a small model reading cold can refute a large one reading\n * warm. Asking it to review everything spends tokens on that asymmetry. Asking it to falsify three specific\n * claims spends almost none.\n *\n * Recording the claims, not only the verdicts, is the point. A replay shows what the lead asserted as well as\n * what turned out to be true, and an author who must write down falsifiable claims notices the weak ones while\n * writing them.\n */\nexport const ClaimsChecked = z.strictObject({\n type: z.literal(\"claims.checked\"),\n repoRoot: z.string().min(1).max(1000),\n revision: WorkRevision,\n by: SeatRef,\n claims: z\n .array(\n z.strictObject({\n /** What the lead asserted, in its own words. */\n claim: z.string().trim().min(1).max(500),\n /**\n * `unclear` is the default and the only safe absence. A verdict we could not read is not a pass, and a\n * claim the reader ignored has not been checked \u2014 treating either as confirmed would make this theatre.\n */\n verdict: z.enum([\"confirmed\", \"refuted\", \"unclear\"]),\n /** Why, in the reader's own words. Required for a refusal; a bare \"no\" helps nobody. */\n evidence: z.string().max(4000),\n }),\n )\n .min(1)\n .max(20),\n /** False when the reader could not be run at all, so \"nothing refuted\" never stands in for \"never asked\". */\n ran: z.boolean(),\n /**\n * True when these verdicts were written by us rather than read by anyone \u2014 the offline demo, and nothing else.\n *\n * It exists so that the one thing the demo cannot do honestly is labelled everywhere it appears instead of\n * being quietly indistinguishable from a real answer. Inventing a second opinion and presenting it as read\n * would be faking the only claim this product makes.\n */\n simulated: z.boolean().default(false),\n});\n\n/**\n * A seat said it has run out, in its own words.\n *\n * Not mission-scoped: a limit belongs to the account, not to whatever happened to be running when it was hit.\n * The message is kept verbatim because \"you have reached your usage limit\" and \"rate limited, retry in 30s\" are\n * different problems and only the vendor knows which one this is.\n */\nexport const SeatLimited = z.strictObject({\n type: z.literal(\"seat.limited\"),\n seat: SeatId,\n message: z.string().trim().min(1).max(500),\n /** When the seat says it will work again. Absent when it did not say, which is usually. */\n resetsAt: z.iso.datetime().optional(),\n});\n\n/**\n * How full one of a seat's quota windows is, when the CLI reports it rather than us guessing.\n *\n * Claude Code is the only seat that says this today, per turn, for its five-hour and seven-day windows. It is the\n * difference between routing on facts and routing on arithmetic we made up, so it is recorded as what it is \u2014\n * real, and belonging to a named window \u2014 rather than flattened into a token count that would read as estimated.\n */\nexport const SeatQuota = z.strictObject({\n type: z.literal(\"seat.quota\"),\n seat: SeatId,\n window: z.string().min(1).max(50),\n /** 0.28 means 28% of that window is used. */\n utilization: z.number().min(0).max(1),\n resetsAt: z.iso.datetime().optional(),\n});\n\nexport const RouteChanged = z.strictObject({\n type: z.literal(\"route.changed\"),\n ...mission,\n lineId: LineId,\n from: SeatRef,\n to: SeatRef,\n reason: z.string().trim().min(1).max(500),\n});\n\nexport const PolicyBreach = z.strictObject({\n type: z.literal(\"policy.breach\"),\n ...run,\n limit: z.string().min(1).max(100),\n action: z.enum([\"killed\", \"paused\", \"asked\"]),\n});\n\nexport const MissionFinished = z.strictObject({\n type: z.literal(\"mission.finished\"),\n ...mission,\n outcome: z.enum([\"completed\", \"aborted\"]),\n summary: z.string().max(8000),\n});\n\nexport const FanoutEvent = z.discriminatedUnion(\"type\", [\n SeatDetected,\n MissionCreated,\n PlanProposed,\n PlanRevised,\n SafetyReported,\n RunQueued,\n RunStarted,\n RunSession,\n RunProgress,\n RunTool,\n RunUsage,\n RunFinished,\n ReviewDone,\n ChecksDone,\n ProofDone,\n BuddyReviewed,\n ClaimsChecked,\n MergeApproved,\n MergeApplied,\n MergeConflict,\n RunDropped,\n SeatLimited,\n SeatQuota,\n RouteChanged,\n PolicyBreach,\n MissionFinished,\n]);\n\n/** An event as validated (defaults applied). */\nexport type FanoutEvent = z.infer<typeof FanoutEvent>;\n/** An event as written by a producer (defaults may be omitted). */\nexport type FanoutEventInput = z.input<typeof FanoutEvent>;\nexport type EventType = FanoutEvent[\"type\"];\nexport type EventOf<T extends EventType> = Extract<FanoutEvent, { type: T }>;\n\n/** What the ledger adds when it records an event. */\nexport const EventStamp = z.strictObject({\n v: z.literal(EVENT_VERSION),\n id: z.uuid(),\n seq: z.int().positive(),\n ts: z.iso.datetime(),\n});\nexport type EventStamp = z.infer<typeof EventStamp>;\n\n/** An event as stored in and read from the ledger. */\nexport type StoredEvent = FanoutEvent & EventStamp;\n", "import { z } from \"zod\";\nimport { SeatId } from \"./common.ts\";\n\n/*\n * What an adapter declares about its CLI, as data rather than code: the versions it was verified against, the exact\n * non-interactive invocation, how to read its output, the safest modes it offers, how to ask it whether it is signed\n * in, and when its vendor's terms were last reviewed.\n *\n * A manifest is a promise we can check. A CLI outside `supportedVersions` is reported as an unsupported version\n * rather than driven on a guess, because a stream we have not seen is a stream we cannot parse honestly.\n */\n\n/** A placeholder the supervisor fills in: {workdir}, {prompt}, {report}, {sandbox}, {model}, {effort}, {session}. */\nconst ArgTemplate = z.string().min(1).max(500);\n\n/**\n * A regular expression a manifest asks us to run, checked at parse time rather than at the moment we need it.\n *\n * A pattern that does not compile throws from `new RegExp`, and that throw would happen deep inside detection,\n * where it takes down the whole crew's result and not just the seat that declared it. Refusing the manifest is\n * both earlier and louder. (This does not make a pattern *fast*: see `matches` in the detector for that half.)\n */\nconst SafePattern = z.string().max(200).refine(compiles, { message: \"must be a valid regular expression\" });\n\nfunction compiles(pattern: string): boolean {\n try {\n new RegExp(pattern, \"i\");\n return true;\n } catch {\n return false;\n }\n}\n\nexport const AdapterManifest = z.strictObject({\n id: SeatId,\n displayName: z.string().min(1).max(80),\n binary: z.string().min(1).max(200),\n /** A semver range, e.g. \">=0.150 <1.0\". Outside it, the seat is unsupported, never guessed at. */\n supportedVersions: z.string().min(1).max(100),\n /** What we promise about this seat, never a judgment of the CLI's quality. */\n tier: z.enum([\"supported\", \"community\", \"reference\"]),\n\n /** Null means no such mode or none verified: the merge gate must never act on a guess. */\n capabilities: z.strictObject({\n resume: z.strictObject({ args: z.array(ArgTemplate).min(1).max(50) }).nullable(),\n fork: z.strictObject({ args: z.array(ArgTemplate).min(1).max(50) }).nullable(),\n review: z.strictObject({ args: z.array(ArgTemplate).min(1).max(50) }).nullable(),\n /**\n * An allowlist keeps account identity out of storage. A privacy promise that is data can be reviewed in a pull\n * request; a promise in adapter code has to be re-read every time.\n */\n plan: z\n .strictObject({\n probe: z.array(z.string().min(1).max(100)).min(1).max(10),\n format: z.literal(\"json\"),\n keep: z.array(z.string().min(1).max(100)).min(1).max(5),\n planField: z.string().min(1).max(100),\n })\n .refine((plan) => plan.keep.includes(plan.planField), {\n message: \"planField must be one of keep\",\n path: [\"planField\"],\n })\n .nullable(),\n }),\n\n headless: z.strictObject({\n args: z.array(ArgTemplate).min(1).max(50),\n /** Always closed: a CLI waiting on stdin is the most common way a run hangs forever. */\n stdin: z.literal(\"closed\"),\n }),\n\n stream: z.strictObject({\n /** The flag that turns on machine-readable output, or null when the CLI has none. */\n flag: z.string().max(100).nullable(),\n format: z.enum([\"jsonl\", \"text\"]),\n }),\n\n models: z.array(z.string().min(1).max(100)).max(100),\n efforts: z.array(z.string().min(1).max(40)).max(20),\n\n /** The flag value for each mode we use. Auditors get the read-only one; nothing else is ever passed. */\n permissionModes: z.strictObject({\n readOnly: z.string().min(1).max(100),\n edit: z.string().min(1).max(100),\n }),\n\n network: z.strictObject({\n canDisable: z.boolean(),\n flag: z.string().max(100).nullable(),\n }),\n\n /**\n * How to ask the CLI itself whether it is signed in. We never read credential files.\n *\n * Both answers are named, because only one of them can be inferred from the other's absence and neither\n * actually is: a probe that fails, times out or answers something unforeseen has told us nothing, and\n * \"nothing\" must stay \"unknown\" rather than becoming a \"no\" that quietly reroutes someone's work.\n */\n signIn: z.strictObject({\n probe: z.array(z.string().min(1).max(100)).max(10).nullable(),\n /** A pattern the probe's output must match to count as signed in. */\n okPattern: SafePattern.nullable(),\n /** A pattern that positively means signed out. Checked first, so \"Not logged in\" cannot match \"Logged in\". */\n noPattern: SafePattern.nullable(),\n }),\n\n /** Real usage when the CLI reports it; otherwise we estimate and say so. */\n usage: z.strictObject({\n probe: z.array(z.string().min(1).max(100)).max(10).nullable(),\n window: z.string().max(100),\n }),\n\n /** Which pool this seat's headless use bills against, so a vendor's policy change is a manifest change. */\n billing: z.enum([\"subscription\", \"credit\", \"api\", \"unknown\"]),\n\n terms: z.strictObject({\n reviewedAt: z.iso.date().nullable(),\n notes: z.string().max(2000),\n }),\n\n status: z.enum([\"planned\", \"research\", \"alpha\", \"stable\"]),\n});\nexport type AdapterManifest = z.infer<typeof AdapterManifest>;\n", "import { z } from \"zod\";\nimport { SeatId, type SeatInfo } from \"./common.ts\";\n\n/*\n * What the owner wants done with each seat, kept apart from what is true of it today.\n *\n * Posture is a preference and availability is a fact, and mixing them produces a interface that lies in both\n * directions: a seat you rely on looks disabled the morning its CLI fails to start, and a seat you asked us never\n * to touch looks ready the moment it signs in. They are resolved together only at the point of use, in `stanceFor`.\n *\n * This file holds only what the owner declared. It is never a cache of anything detected: a subscription tier\n * written down in June and read back in September is a stale answer presented as a current fact, and the whole\n * point of the `source` on a plan is that a reader can tell those apart.\n */\n\n/**\n * How willingly Fanout should spend a seat.\n *\n * - `preferred` \u2014 reach for this first when several seats could do the line.\n * - `normal` \u2014 use it when the plan calls for it.\n * - `sparing` \u2014 only when nothing else fits, and say so before launching. For the subscription you pay least for.\n * - `off` \u2014 never, until the owner says otherwise.\n */\nexport const SeatPosture = z.enum([\"preferred\", \"normal\", \"sparing\", \"off\"]);\nexport type SeatPosture = z.infer<typeof SeatPosture>;\n\nexport const SeatPolicy = z.strictObject({\n version: z.literal(1),\n seats: z.record(\n SeatId,\n z.strictObject({\n posture: SeatPosture,\n /** The owner's own words about why, shown back to them so a past decision explains itself. */\n note: z.string().max(200).optional(),\n }),\n ),\n});\nexport type SeatPolicy = z.infer<typeof SeatPolicy>;\n\nexport const EMPTY_POLICY: SeatPolicy = { version: 1, seats: {} };\n\n/**\n * Claude is the only seat that is off until asked for.\n *\n * The lead already runs on this subscription, so a Claude worker spends the same window the session you are sitting\n * in is spending. That is a decision about someone's money, and it is theirs to make deliberately rather than to\n * discover afterwards (DECISIONS 0009).\n */\nconst OPT_IN_SEATS: ReadonlySet<string> = new Set([\"claude\"]);\n\nexport interface SeatStance {\n posture: SeatPosture;\n /** `declared` when the owner set it; `default` when nobody has, and `reason` says why that default. */\n source: \"declared\" | \"default\";\n reason: string;\n /** Willing *and* able: the posture allows it and the CLI is actually there and signed in. */\n usable: boolean;\n note?: string;\n}\n\n/**\n * What we should do with one seat right now, given what the owner declared and what detection found.\n *\n * Deliberately not clever. We know a plan's *name*, never its price, so nothing here infers that \"pro\" is cheaper\n * than \"max\" or that an unknown plan is a small one \u2014 the one fact only the owner has is which subscription they\n * would rather not spend, and the only honest way to learn it is to be told.\n */\nexport function stanceFor(seat: SeatInfo, policy: SeatPolicy): SeatStance {\n const declared = Object.hasOwn(policy.seats, seat.id) ? policy.seats[seat.id] : undefined;\n const posture: SeatPosture = declared?.posture ?? (OPT_IN_SEATS.has(seat.id) ? \"off\" : \"normal\");\n\n const reason =\n declared !== undefined\n ? \"you set this\"\n : OPT_IN_SEATS.has(seat.id)\n ? \"opt-in: a worker here spends the same subscription your session is running on\"\n : \"nobody has said otherwise\";\n\n return {\n posture,\n source: declared === undefined ? \"default\" : \"declared\",\n reason,\n usable: posture !== \"off\" && seat.supported && seat.signedIn === \"yes\",\n ...(declared?.note === undefined ? {} : { note: declared.note }),\n };\n}\n\n/** The seats a mission may draw on, most willing first, so a planner can take the head of the list. */\nexport function usableSeats(seats: readonly SeatInfo[], policy: SeatPolicy): SeatInfo[] {\n const rank: Record<SeatPosture, number> = { preferred: 0, normal: 1, sparing: 2, off: 3 };\n return seats\n .filter((seat) => stanceFor(seat, policy).usable)\n .sort((a, b) => rank[stanceFor(a, policy).posture] - rank[stanceFor(b, policy).posture]);\n}\n", "import { randomUUID } from \"node:crypto\";\nimport { chmodSync, closeSync, mkdirSync, openSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname } from \"node:path\";\nimport type * as Sqlite from \"node:sqlite\";\ntype Database = Sqlite.DatabaseSync;\ntype StatementSync = Sqlite.StatementSync;\nimport { z } from \"zod\";\n/*\n * Required at runtime rather than imported, for one reason: Node prints `ExperimentalWarning: SQLite is an\n * experimental feature` the moment this module is loaded, and ESM resolves every static import before any module\n * body runs. A static import here fires that warning before the CLI has executed a single line, so nothing the\n * CLI does could ever suppress it \u2014 and every `fanout` command opened with two lines of noise about a decision\n * the user did not make and cannot act on.\n *\n * A runtime require happens during evaluation instead, by which time `cli.ts` has installed its filter. The\n * types are the real ones; only the moment of loading changes. `packages/cli/test/quiet.test.ts` fails if this\n * becomes a static import again.\n */\nconst { DatabaseSync } = createRequire(import.meta.url)(\"node:sqlite\") as typeof Sqlite;\n\nimport {\n EVENT_VERSION,\n EventStamp,\n FanoutEvent,\n type FanoutEventInput,\n type StoredEvent,\n} from \"../schema/events.ts\";\n\n/*\n * The ledger is the single source of truth: an append-only SQLite table of validated events.\n * Append-only is enforced by the database, not just by this API: triggers abort any UPDATE, any DELETE, and any\n * INSERT that would replace an existing row (INSERT OR REPLACE deletes the old row without firing DELETE triggers).\n * This guards against rewriting history with ordinary SQL. It is not a defense against someone with raw access to\n * the file: they own it, and `DROP TABLE` or replacing a trigger would still succeed. Opening checks the guards exist.\n * Every row is validated on the way in and again on the way out, so a damaged ledger fails loudly.\n */\n\nconst SCHEMA_VERSION = 1;\n\nconst SCHEMA_V1 = `\nCREATE TABLE IF NOT EXISTS events (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n ts TEXT NOT NULL,\n v INTEGER NOT NULL,\n type TEXT NOT NULL,\n mission_id TEXT,\n run_id TEXT,\n body TEXT NOT NULL CHECK (json_valid(body))\n) STRICT;\nCREATE INDEX IF NOT EXISTS events_by_mission ON events (mission_id, seq);\nCREATE TRIGGER IF NOT EXISTS events_no_update BEFORE UPDATE ON events\n BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;\nCREATE TRIGGER IF NOT EXISTS events_no_delete BEFORE DELETE ON events\n BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;\nCREATE TRIGGER IF NOT EXISTS events_no_replace BEFORE INSERT ON events\n WHEN EXISTS (SELECT 1 FROM events WHERE seq = NEW.seq OR id = NEW.id)\n BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;\n`;\n\nconst GUARD_TRIGGERS = [\"events_no_update\", \"events_no_delete\", \"events_no_replace\"] as const;\n\nexport class LedgerError extends Error {\n override name = \"LedgerError\";\n}\n\n/** An event that does not match the schema. Nothing was written. */\nexport class InvalidEventError extends LedgerError {\n override name = \"InvalidEventError\";\n readonly index: number;\n\n constructor(index: number, detail: string) {\n super(`Event ${index} is invalid, nothing was written:\\n${detail}`);\n this.index = index;\n }\n}\n\n/** A ledger or an event written by a newer Fanout. We refuse to guess at it. */\nexport class UnsupportedLedgerError extends LedgerError {\n override name = \"UnsupportedLedgerError\";\n}\n\nexport interface LedgerOptions {\n /** Clock for event timestamps (tests inject a fixed one). */\n now?: () => Date;\n /** Event id generator; must return UUIDs. */\n newId?: () => string;\n /**\n * Called once per event, after it is committed, so a live feed never shows something the ledger might roll back.\n * Whatever it throws is ignored: a listener must not be able to break the record.\n */\n onAppend?: (event: StoredEvent) => void;\n}\n\nexport interface ReadOptions {\n /** Only events with a larger sequence number. */\n afterSeq?: number;\n /** Only events of this mission. */\n missionId?: string;\n /** At most this many events. */\n limit?: number;\n}\n\nconst Row = z.object({\n seq: z.number(),\n id: z.string(),\n ts: z.string(),\n v: z.number(),\n type: z.string(),\n mission_id: z.string().nullable(),\n run_id: z.string().nullable(),\n body: z.string(),\n});\n\nexport class Ledger {\n readonly #db: Database;\n readonly #now: () => Date;\n readonly #newId: () => string;\n readonly #onAppend: ((event: StoredEvent) => void) | undefined;\n readonly #insert: StatementSync;\n readonly #readAll: StatementSync;\n readonly #readMission: StatementSync;\n readonly #lastSeq: StatementSync;\n\n private constructor(db: Database, options: LedgerOptions) {\n this.#db = db;\n this.#now = options.now ?? (() => new Date());\n this.#newId = options.newId ?? randomUUID;\n this.#onAppend = options.onAppend;\n this.#insert = db.prepare(\n \"INSERT INTO events (id, ts, v, type, mission_id, run_id, body) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n );\n this.#readAll = db.prepare(\n \"SELECT seq, id, ts, v, type, mission_id, run_id, body FROM events WHERE seq > ? ORDER BY seq LIMIT ?\",\n );\n this.#readMission = db.prepare(\n \"SELECT seq, id, ts, v, type, mission_id, run_id, body FROM events \" +\n \"WHERE seq > ? AND mission_id = ? ORDER BY seq LIMIT ?\",\n );\n this.#lastSeq = db.prepare(\"SELECT COALESCE(MAX(seq), 0) AS seq FROM events\");\n }\n\n /**\n * Opens (or creates) a ledger. Use \":memory:\" for a throwaway one. On disk, the file is private to the user\n * (mode 600, directory 700).\n */\n static open(path: string, options: LedgerOptions = {}): Ledger {\n const onDisk = path !== \":memory:\";\n if (onDisk) {\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n closeSync(openSync(path, \"a\", 0o600));\n chmodSync(path, 0o600);\n }\n const db = new DatabaseSync(path);\n try {\n db.exec(\"PRAGMA busy_timeout = 5000\");\n if (onDisk) db.exec(\"PRAGMA journal_mode = WAL\");\n db.exec(\"PRAGMA synchronous = FULL\");\n migrate(db);\n return new Ledger(db, options);\n } catch (error) {\n db.close();\n throw error;\n }\n }\n\n /** Validates and records one event; returns it with its stamp. */\n append(input: FanoutEventInput): StoredEvent {\n const [stored] = this.appendAll([input]);\n if (stored === undefined) throw new LedgerError(\"append recorded nothing\");\n return stored;\n }\n\n /** Validates every event first, then records them all in one transaction, or none of them. */\n appendAll(inputs: readonly FanoutEventInput[]): StoredEvent[] {\n const events = inputs.map((input, index) => {\n const result = FanoutEvent.safeParse(input);\n if (!result.success) throw new InvalidEventError(index, z.prettifyError(result.error));\n return result.data;\n });\n\n this.#db.exec(\"BEGIN IMMEDIATE\");\n try {\n const stored = events.map((event): StoredEvent => {\n const stamp = EventStamp.omit({ seq: true }).parse({\n v: EVENT_VERSION,\n id: this.#newId(),\n ts: this.#now().toISOString(),\n });\n const result = this.#insert.run(\n stamp.id,\n stamp.ts,\n stamp.v,\n event.type,\n \"missionId\" in event ? event.missionId : null,\n \"runId\" in event ? event.runId : null,\n JSON.stringify(event),\n );\n return { ...event, ...stamp, seq: Number(result.lastInsertRowid) };\n });\n this.#db.exec(\"COMMIT\");\n for (const event of stored) {\n try {\n this.#onAppend?.(event);\n } catch {\n // A listener that throws has a problem of its own; the record is already safe.\n }\n }\n return stored;\n } catch (error) {\n this.#db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n /** Events in sequence order. */\n read(options: ReadOptions = {}): StoredEvent[] {\n const afterSeq = options.afterSeq ?? 0;\n const limit = options.limit ?? -1;\n const rows =\n options.missionId === undefined\n ? this.#readAll.all(afterSeq, limit)\n : this.#readMission.all(afterSeq, options.missionId, limit);\n return rows.map(decode);\n }\n\n /** The sequence number of the last event, or 0 for an empty ledger. */\n lastSeq(): number {\n const row = this.#lastSeq.get();\n return Number(row?.[\"seq\"] ?? 0);\n }\n\n /**\n * Whether this ledger can still be written to.\n *\n * A daemon shutting down closes the ledger while runs may still be in flight, and a process that exits a moment\n * later tries to record how it ended. That is expected, not exceptional, and a caller needs to be able to tell\n * it apart from a ledger that has actually broken \u2014 one means \"we are going away\", the other means \"stop the\n * run, we can no longer record what it is doing\".\n */\n get isOpen(): boolean {\n return this.#db.isOpen;\n }\n\n /** Idempotent: closing twice is what happens when shutdown and a test's cleanup both do the right thing. */\n close(): void {\n if (this.#db.isOpen) this.#db.close();\n }\n}\n\nfunction migrate(db: Database): void {\n const version = userVersion(db);\n if (version > SCHEMA_VERSION) {\n throw new UnsupportedLedgerError(\n `This ledger was written by a newer Fanout (schema ${version}; this one reads ${SCHEMA_VERSION}). ` +\n \"Update Fanout to open it.\",\n );\n }\n if (version < SCHEMA_VERSION) {\n db.exec(\"BEGIN IMMEDIATE\");\n try {\n if (userVersion(db) < SCHEMA_VERSION) {\n db.exec(SCHEMA_V1);\n db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);\n }\n db.exec(\"COMMIT\");\n } catch (error) {\n db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n const triggers = new Set(\n db\n .prepare(\"SELECT name FROM sqlite_master WHERE type = 'trigger' AND tbl_name = 'events'\")\n .all()\n .map((row: Record<string, unknown>) => String(row[\"name\"])),\n );\n const missing = GUARD_TRIGGERS.filter((name) => !triggers.has(name));\n if (missing.length > 0) {\n throw new LedgerError(\n `This ledger lost its append-only guard (${missing.join(\", \")}); refusing to use it.`,\n );\n }\n}\n\nfunction userVersion(db: Database): number {\n return Number(db.prepare(\"PRAGMA user_version\").get()?.[\"user_version\"] ?? 0);\n}\n\nfunction decode(raw: unknown): StoredEvent {\n const row = Row.parse(raw);\n if (row.v !== EVENT_VERSION) {\n throw new UnsupportedLedgerError(\n `Event ${row.seq} has version ${row.v}; this Fanout reads version ${EVENT_VERSION}. Update Fanout to read it.`,\n );\n }\n let body: unknown;\n try {\n body = JSON.parse(row.body);\n } catch {\n throw new LedgerError(`Event ${row.seq} is not valid JSON; the ledger is damaged.`);\n }\n const event = FanoutEvent.safeParse(body);\n const stamp = EventStamp.safeParse({ v: row.v, id: row.id, seq: row.seq, ts: row.ts });\n if (!event.success || !stamp.success) {\n const detail = event.error ?? stamp.error;\n throw new LedgerError(\n `Event ${row.seq} does not match the schema; the ledger is damaged.` +\n (detail === undefined ? \"\" : `\\n${z.prettifyError(detail)}`),\n );\n }\n\n // The indexed columns are how events are found; if they disagree with the body, queries would silently lie.\n const routed =\n row.type === event.data.type &&\n row.mission_id === (\"missionId\" in event.data ? event.data.missionId : null) &&\n row.run_id === (\"runId\" in event.data ? event.data.runId : null);\n if (!routed) {\n throw new LedgerError(\n `Event ${row.seq} is indexed as ${row.type} (mission ${row.mission_id ?? \"none\"}, ` +\n `run ${row.run_id ?? \"none\"}) but its body says otherwise; the ledger is damaged.`,\n );\n }\n\n return { ...event.data, ...stamp.data };\n}\n", "import { RunProgress } from \"fanout-core\";\nimport { z } from \"zod\";\n\n/*\n * The fake agent's stdout: one JSON object per line. It stands in for a vendor CLI's stream, so the adapter parses it\n * the same way a real adapter parses Codex's or Kimi's output. The CLI writes it and the adapter reads it with this\n * one schema.\n */\nexport const OutputLine = z.discriminatedUnion(\"kind\", [\n z.strictObject({\n kind: z.literal(\"phase\"),\n phase: RunProgress.shape.phase,\n detail: z.string().max(500).optional(),\n }),\n z.strictObject({\n kind: z.literal(\"tool\"),\n tool: z.string().min(1).max(100),\n summary: z.string().max(500).optional(),\n files: z.array(z.string().min(1).max(1000)).max(200),\n }),\n z.strictObject({ kind: z.literal(\"usage\"), amount: z.int().nonnegative(), unit: z.literal(\"messages\") }),\n z.strictObject({ kind: z.literal(\"limit\"), message: z.string().min(1).max(500) }),\n z.strictObject({ kind: z.literal(\"sleep\"), ms: z.int().nonnegative() }),\n z.strictObject({ kind: z.literal(\"report\"), text: z.string().max(20_000) }),\n]);\nexport type OutputLine = z.infer<typeof OutputLine>;\n\n/** Exit codes besides the scenario's own. */\nexport const EXIT = {\n /** A limit step was played: the simulated seat ran out of usage. */\n limit: 2,\n /** Bad arguments or an invalid scenario (EX_USAGE). */\n usage: 64,\n /** The scenario tried to write outside the working directory (EX_DATAERR). */\n unsafeWrite: 65,\n /** Anything unexpected (EX_SOFTWARE). */\n internal: 70,\n} as const;\n", "import { RunProgress } from \"fanout-core\";\nimport { z } from \"zod\";\n\n/*\n * What the fake agent does, step by step. Deterministic by design: no randomness, no clock in the output.\n * Example:\n * { \"steps\": [ { \"phase\": \"reading\", \"delayMs\": 300 },\n * { \"tool\": \"edit\", \"summary\": \"add csv writer\", \"write\": { \"src/api/csv.ts\": \"export \u2026\" } },\n * { \"usage\": 2 }, { \"limit\": \"usage limit reached\" } ],\n * \"report\": \"Added the endpoint.\", \"exitCode\": 0, \"timeScale\": 0.2 }\n */\n\nconst Delay = z.int().nonnegative().max(60_000).optional();\n\nconst PhaseStep = z.strictObject({\n phase: RunProgress.shape.phase,\n detail: z.string().max(500).optional(),\n delayMs: Delay,\n});\n\nconst ToolStep = z.strictObject({\n tool: z.string().min(1).max(100),\n summary: z.string().max(500).optional(),\n /** Files to really write, relative to the working directory, with their content. */\n write: z.record(z.string().min(1).max(1000), z.string().max(1_000_000)).optional(),\n delayMs: Delay,\n});\n\nconst UsageStep = z.strictObject({ usage: z.int().nonnegative().max(1_000_000), delayMs: Delay });\n\n/** The seat runs out of usage: the agent prints the message, writes its report and exits with code 2. */\nconst LimitStep = z.strictObject({ limit: z.string().min(1).max(500), delayMs: Delay });\n\nconst SleepStep = z.strictObject({ sleep: z.int().nonnegative().max(60_000) });\n\nexport const ScenarioStep = z.union([PhaseStep, ToolStep, UsageStep, LimitStep, SleepStep]);\nexport type ScenarioStep = z.infer<typeof ScenarioStep>;\n\nexport const Scenario = z.strictObject({\n steps: z.array(ScenarioStep).max(1000),\n report: z.string().max(20_000),\n exitCode: z.int().min(0).max(255).default(0),\n /** Keep running after the last step, until killed (to exercise timeouts). */\n hang: z.boolean().default(false),\n /** Multiplies every delay: 0.2 plays five times faster (the demo), 0 plays instantly (tests). */\n timeScale: z.number().nonnegative().max(100).default(1),\n});\nexport type Scenario = z.infer<typeof Scenario>;\nexport type ScenarioInput = z.input<typeof Scenario>;\n"],
5
+ "mappings": ";AAAA,SAAS,WAAW,qBAAqB;AACzC,SAAS,SAAS,YAAY,UAAU,SAAS,WAAW;AAC5D,SAAS,cAAc,aAAa;AACpC,SAAS,iBAAiB;;;ACH1B,SAAS,SAAS;AAGX,IAAM,OAAO,EACjB,OAAO,EACP,MAAM,0CAA0C,yDAAyD;AAErG,IAAM,YAAY;AAClB,IAAM,SAAS;AACf,IAAM,QAAQ;AACd,IAAM,SAAS;AAGf,IAAM,SAAS,EAAE,OAAO,EAAE,MAAM,mCAAmC,mBAAmB;AAUtF,IAAM,eAAe,EAAE,OAAO,EAAE,MAAM,kBAAkB,uCAAuC;AAG/F,IAAM,UAAU,EAAE,aAAa;AAAA,EACpC,IAAI;AAAA,EACJ,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC3C,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAC7C,CAAC;AAIM,IAAM,WAAW,EAAE,aAAa;AAAA,EACrC,IAAI;AAAA,EACJ,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,EACrC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACjC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC7C,WAAW,EAAE,QAAQ;AAAA,EACrB,UAAU,EAAE,KAAK,CAAC,OAAO,MAAM,SAAS,CAAC;AAAA,EACzC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG;AAAA,EACnD,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE;AAAA,EAClD,SAAS,EAAE,KAAK,CAAC,gBAAgB,UAAU,OAAO,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO5D,MAAM,EACH,aAAa;AAAA,IACZ,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,IAC/B,QAAQ,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC;AAAA,EACzC,CAAC,EACA,SAAS;AACd,CAAC;AAGM,IAAM,WAAW,EAAE,aAAa;AAAA,EACrC,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EAC3B,YAAY,EAAE,IAAI,EAAE,YAAY;AAAA,EAChC,WAAW,EAAE,IAAI,EAAE,YAAY;AACjC,CAAC;AAGM,IAAM,gBAAgB,EAAE,aAAa;AAAA,EAC1C,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,EAClC,gBAAgB,EACb,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,KAAK,EAAE;AAChB,CAAC;AAIM,IAAM,cAAc,EAAE,aAAa;AAAA,EACxC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,EAC5B,IAAI,EAAE,QAAQ;AAAA,EACd,UAAU,EAAE,KAAK,CAAC,SAAS,MAAM,CAAC;AAAA,EAClC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EACnC,SAAS,EAAE,MAAM,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS;AAC5C,CAAC;;;AClFD,SAAS,KAAAA,UAAS;AAiBlB,IAAM,UAAU;AAGT,SAAS,iBAAiB,MAAuB;AACtD,MAAI,KAAK,WAAW,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,EAAG,QAAO;AAC5E,SAAO,KACJ,MAAM,GAAG,EACT;AAAA,IACC,CAAC,YACC,QAAQ,KAAK,OAAO,KACpB,YAAY,OACZ,YAAY,SACX,YAAY,QAAQ,CAAC,QAAQ,SAAS,IAAI;AAAA,EAC/C;AACJ;AAEO,IAAM,YAAYC,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,OAAO,kBAAkB;AAAA,EACpE,SAAS;AACX,CAAC;;;ACnCD,SAAS,KAAAC,UAAS;AAIX,IAAM,WAAWC,GAAE,KAAK,CAAC,WAAW,WAAW,QAAQ,CAAC;AAIxD,IAAM,WAAWA,GAAE,aAAa;AAAA,EACrC,IAAI;AAAA,EACJ,OAAOA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACvC,MAAM;AAAA,EACN,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAO;AAAA,EACrC,MAAM;AAAA,EACN,OAAOA,GAAE,aAAa,EAAE,OAAOA,GAAE,MAAM,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;AAAA,EAC3D,WAAWA,GAAE,MAAM,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC7C,QAAQA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC9D,gBAAgBA,GAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjD,UAAUA,GAAE,QAAQ,EAAE,QAAQ,KAAK;AACrC,CAAC;AAGM,IAAM,YAAYA,GAAE,aAAa;AAAA,EACtC,OAAOA,GAAE,MAAM,QAAQ,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AACxC,CAAC;;;AC9BD,SAAS,KAAAC,UAAS;AAuBX,IAAM,gBAAgB;AAE7B,IAAM,UAAU,EAAE,WAAW,UAAU;AACvC,IAAM,MAAM,EAAE,WAAW,WAAW,OAAO,MAAM;AAEjD,IAAM,aAAaC,GAAE,KAAK,CAAC,QAAQ,MAAM,CAAC;AAC1C,IAAM,QAAQA,GAAE,KAAK,CAAC,WAAW,UAAU,WAAW,WAAW,CAAC;AAClE,IAAM,YAAYA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,CAAC,EAAE,IAAI,GAAI;AAExD,IAAM,eAAeA,GAAE,aAAa;AAAA,EACzC,MAAMA,GAAE,QAAQ,eAAe;AAAA,EAC/B,MAAM;AACR,CAAC;AAEM,IAAM,iBAAiBA,GAAE,aAAa;AAAA,EAC3C,MAAMA,GAAE,QAAQ,iBAAiB;AAAA,EACjC,GAAG;AAAA,EACH,MAAMA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EACvC,MAAMA,GAAE,aAAa,EAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,GAAG,YAAY,OAAO,CAAC;AAAA,EAC9E,QAAQ;AACV,CAAC;AAEM,IAAM,eAAeA,GAAE,aAAa;AAAA,EACzC,MAAMA,GAAE,QAAQ,eAAe;AAAA,EAC/B,GAAG;AAAA,EACH,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAI,EAAE,SAAS;AACtC,CAAC;AAEM,IAAM,cAAcA,GAAE,aAAa;AAAA,EACxC,MAAMA,GAAE,QAAQ,cAAc;AAAA,EAC9B,GAAG;AAAA,EACH,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAI,EAAE,SAAS;AACtC,CAAC;AAEM,IAAM,iBAAiBA,GAC3B,aAAa;AAAA,EACZ,MAAMA,GAAE,QAAQ,eAAe;AAAA,EAC/B,GAAG;AAAA;AAAA,EAEH,cAAcA,GAAE,IAAI,EAAE,SAAS;AAAA,EAC/B,IAAIA,GAAE,QAAQ;AAAA,EACd,QAAQA,GAAE,MAAM,WAAW,EAAE,IAAI,GAAG;AACtC,CAAC,EACA,OAAO,CAAC,WAAW,OAAO,OAAO,OAAO,OAAO,MAAM,CAAC,UAAU,MAAM,MAAM,MAAM,aAAa,MAAM,GAAG;AAAA,EACvG,SAAS;AAAA,EACT,MAAM,CAAC,IAAI;AACb,CAAC;AAEI,IAAM,YAAYA,GAAE,aAAa;AAAA,EACtC,MAAMA,GAAE,QAAQ,YAAY;AAAA,EAC5B,GAAG;AAAA,EACH,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAASA,GAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAC/B,CAAC;AAEM,IAAM,aAAaA,GAAE,aAAa;AAAA,EACvC,MAAMA,GAAE,QAAQ,aAAa;AAAA,EAC7B,GAAG;AAAA,EACH,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EACnC,MAAMA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAO,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWrD,OAAOA,GAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AACrC,CAAC;AAUM,IAAM,aAAaA,GAAE,aAAa;AAAA,EACvC,MAAMA,GAAE,QAAQ,aAAa;AAAA,EAC7B,GAAG;AAAA,EACH,WAAWA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAC7C,CAAC;AAEM,IAAM,cAAcA,GAAE,aAAa;AAAA,EACxC,MAAMA,GAAE,QAAQ,cAAc;AAAA,EAC9B,GAAG;AAAA,EACH,OAAO;AAAA,EACP,QAAQA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AACvC,CAAC;AAEM,IAAM,UAAUA,GAAE,aAAa;AAAA,EACpC,MAAMA,GAAE,QAAQ,UAAU;AAAA,EAC1B,GAAG;AAAA,EACH,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC/B,SAASA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACtC,OAAO,UAAU,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAEM,IAAM,WAAWA,GAAE,aAAa;AAAA,EACrC,MAAMA,GAAE,QAAQ,WAAW;AAAA,EAC3B,GAAG;AAAA,EACH,MAAM;AAAA,EACN,QAAQA,GAAE,OAAO,EAAE,YAAY;AAAA,EAC/B,MAAMA,GAAE,KAAK,CAAC,YAAY,UAAU,SAAS,CAAC;AAAA,EAC9C,WAAWA,GAAE,QAAQ;AACvB,CAAC;AAEM,IAAM,cAAcA,GAAE,aAAa;AAAA,EACxC,MAAMA,GAAE,QAAQ,cAAc;AAAA,EAC9B,GAAG;AAAA,EACH,QAAQA,GAAE,KAAK,CAAC,QAAQ,UAAU,UAAU,SAAS,CAAC;AAAA,EACtD,UAAUA,GAAE,IAAI,EAAE,SAAS;AAAA,EAC3B,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA,EACjD,UAAU,SAAS,SAAS;AAC9B,CAAC;AAQM,IAAM,aAAaA,GAAE,aAAa;AAAA,EACvC,MAAMA,GAAE,QAAQ,aAAa;AAAA,EAC7B,GAAG;AAAA,EACH,UAAU;AAAA,EACV,SAASA,GAAE,KAAK,CAAC,UAAU,UAAU,QAAQ,CAAC;AAAA,EAC9C,OAAOA,GAAE,OAAO,EAAE,IAAI,GAAM;AAAA,EAC5B,IAAI;AACN,CAAC;AAEM,IAAM,aAAaA,GAAE,aAAa;AAAA,EACvC,MAAMA,GAAE,QAAQ,aAAa;AAAA,EAC7B,GAAG;AAAA,EACH,UAAU;AAAA,EACV,IAAIA,GAAE,QAAQ;AAAA,EACd,SAASA,GAAE,OAAO,EAAE,IAAI,GAAI;AAAA;AAAA,EAE5B,UAAUA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE;AACtD,CAAC;AAGM,IAAM,YAAYA,GACtB,aAAa;AAAA,EACZ,MAAMA,GAAE,QAAQ,YAAY;AAAA,EAC5B,GAAG;AAAA,EACH,UAAU;AAAA,EACV,IAAIA,GAAE,QAAQ;AAAA,EACd,aAAaA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG;AAC1D,CAAC,EACA,OAAO,CAAC,UAAU,CAAC,MAAM,MAAM,MAAM,YAAY,SAAS,GAAG;AAAA,EAC5D,SAAS;AAAA,EACT,MAAM,CAAC,aAAa;AACtB,CAAC;AAMI,IAAM,gBAAgBA,GAAE,aAAa;AAAA,EAC1C,MAAMA,GAAE,QAAQ,gBAAgB;AAAA,EAChC,GAAG;AAAA,EACH,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKV,IAAIA,GAAE,mBAAmB,QAAQ;AAAA,IAC/BA,GAAE,aAAa;AAAA,MACb,MAAMA,GAAE,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAatB,KAAKA,GAAE,KAAK,CAAC,UAAU,SAAS,CAAC,EAAE,SAAS;AAAA,IAC9C,CAAC;AAAA,IACDA,GAAE,aAAa,EAAE,MAAMA,GAAE,QAAQ,QAAQ,GAAG,MAAMA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,CAAC;AAAA,EACvF,CAAC;AAAA,EACD,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAI,EAAE,SAAS;AACtC,CAAC;AAEM,IAAM,eAAeA,GAAE,aAAa;AAAA,EACzC,MAAMA,GAAE,QAAQ,eAAe;AAAA,EAC/B,GAAG;AAAA,EACH,UAAU;AAAA,EACV,OAAO,UAAU,IAAI,CAAC;AAAA;AAAA,EAEtB,QAAQ;AACV,CAAC;AAEM,IAAM,gBAAgBA,GAAE,aAAa;AAAA,EAC1C,MAAMA,GAAE,QAAQ,gBAAgB;AAAA,EAChC,GAAG;AAAA,EACH,UAAU;AAAA,EACV,OAAO,UAAU,IAAI,CAAC;AACxB,CAAC;AAEM,IAAM,aAAaA,GAAE,aAAa;AAAA,EACvC,MAAMA,GAAE,QAAQ,aAAa;AAAA,EAC7B,GAAG;AAAA,EACH,QAAQA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAC3C,CAAC;AAYM,IAAM,gBAAgBA,GAAE,aAAa;AAAA,EAC1C,MAAMA,GAAE,QAAQ,gBAAgB;AAAA;AAAA,EAEhC,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EACpC,UAAU;AAAA,EACV,IAAI;AAAA;AAAA,EAEJ,UAAUA,GAAE,OAAO,EAAE,IAAI,GAAO;AAAA;AAAA,EAEhC,KAAKA,GAAE,QAAQ;AAAA,EACf,OAAO,UAAU,IAAI,GAAI;AAC3B,CAAC;AAgBM,IAAM,gBAAgBA,GAAE,aAAa;AAAA,EAC1C,MAAMA,GAAE,QAAQ,gBAAgB;AAAA,EAChC,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EACpC,UAAU;AAAA,EACV,IAAI;AAAA,EACJ,QAAQA,GACL;AAAA,IACCA,GAAE,aAAa;AAAA;AAAA,MAEb,OAAOA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,MAKvC,SAASA,GAAE,KAAK,CAAC,aAAa,WAAW,SAAS,CAAC;AAAA;AAAA,MAEnD,UAAUA,GAAE,OAAO,EAAE,IAAI,GAAI;AAAA,IAC/B,CAAC;AAAA,EACH,EACC,IAAI,CAAC,EACL,IAAI,EAAE;AAAA;AAAA,EAET,KAAKA,GAAE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQf,WAAWA,GAAE,QAAQ,EAAE,QAAQ,KAAK;AACtC,CAAC;AASM,IAAM,cAAcA,GAAE,aAAa;AAAA,EACxC,MAAMA,GAAE,QAAQ,cAAc;AAAA,EAC9B,MAAM;AAAA,EACN,SAASA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA;AAAA,EAEzC,UAAUA,GAAE,IAAI,SAAS,EAAE,SAAS;AACtC,CAAC;AASM,IAAM,YAAYA,GAAE,aAAa;AAAA,EACtC,MAAMA,GAAE,QAAQ,YAAY;AAAA,EAC5B,MAAM;AAAA,EACN,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA;AAAA,EAEhC,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACpC,UAAUA,GAAE,IAAI,SAAS,EAAE,SAAS;AACtC,CAAC;AAEM,IAAM,eAAeA,GAAE,aAAa;AAAA,EACzC,MAAMA,GAAE,QAAQ,eAAe;AAAA,EAC/B,GAAG;AAAA,EACH,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,QAAQA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAC1C,CAAC;AAEM,IAAM,eAAeA,GAAE,aAAa;AAAA,EACzC,MAAMA,GAAE,QAAQ,eAAe;AAAA,EAC/B,GAAG;AAAA,EACH,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAChC,QAAQA,GAAE,KAAK,CAAC,UAAU,UAAU,OAAO,CAAC;AAC9C,CAAC;AAEM,IAAM,kBAAkBA,GAAE,aAAa;AAAA,EAC5C,MAAMA,GAAE,QAAQ,kBAAkB;AAAA,EAClC,GAAG;AAAA,EACH,SAASA,GAAE,KAAK,CAAC,aAAa,SAAS,CAAC;AAAA,EACxC,SAASA,GAAE,OAAO,EAAE,IAAI,GAAI;AAC9B,CAAC;AAEM,IAAM,cAAcA,GAAE,mBAAmB,QAAQ;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAUM,IAAM,aAAaA,GAAE,aAAa;AAAA,EACvC,GAAGA,GAAE,QAAQ,aAAa;AAAA,EAC1B,IAAIA,GAAE,KAAK;AAAA,EACX,KAAKA,GAAE,IAAI,EAAE,SAAS;AAAA,EACtB,IAAIA,GAAE,IAAI,SAAS;AACrB,CAAC;;;ACzZD,SAAS,KAAAC,UAAS;AAalB,IAAM,cAAcC,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAS7C,IAAM,cAAcA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,OAAO,UAAU,EAAE,SAAS,qCAAqC,CAAC;AAE1G,SAAS,SAAS,SAA0B;AAC1C,MAAI;AACF,QAAI,OAAO,SAAS,GAAG;AACvB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,kBAAkBA,GAAE,aAAa;AAAA,EAC5C,IAAI;AAAA,EACJ,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,EACrC,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA;AAAA,EAEjC,mBAAmBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA;AAAA,EAE5C,MAAMA,GAAE,KAAK,CAAC,aAAa,aAAa,WAAW,CAAC;AAAA;AAAA,EAGpD,cAAcA,GAAE,aAAa;AAAA,IAC3B,QAAQA,GAAE,aAAa,EAAE,MAAMA,GAAE,MAAM,WAAW,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,SAAS;AAAA,IAC/E,MAAMA,GAAE,aAAa,EAAE,MAAMA,GAAE,MAAM,WAAW,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,SAAS;AAAA,IAC7E,QAAQA,GAAE,aAAa,EAAE,MAAMA,GAAE,MAAM,WAAW,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,IAK/E,MAAMA,GACH,aAAa;AAAA,MACZ,OAAOA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,MACxD,QAAQA,GAAE,QAAQ,MAAM;AAAA,MACxB,MAAMA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,MACtD,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,IACtC,CAAC,EACA,OAAO,CAAC,SAAS,KAAK,KAAK,SAAS,KAAK,SAAS,GAAG;AAAA,MACpD,SAAS;AAAA,MACT,MAAM,CAAC,WAAW;AAAA,IACpB,CAAC,EACA,SAAS;AAAA,EACd,CAAC;AAAA,EAED,UAAUA,GAAE,aAAa;AAAA,IACvB,MAAMA,GAAE,MAAM,WAAW,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA;AAAA,IAExC,OAAOA,GAAE,QAAQ,QAAQ;AAAA,EAC3B,CAAC;AAAA,EAED,QAAQA,GAAE,aAAa;AAAA;AAAA,IAErB,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,IACnC,QAAQA,GAAE,KAAK,CAAC,SAAS,MAAM,CAAC;AAAA,EAClC,CAAC;AAAA,EAED,QAAQA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG;AAAA,EACnD,SAASA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE;AAAA;AAAA,EAGlD,iBAAiBA,GAAE,aAAa;AAAA,IAC9B,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,IACnC,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACjC,CAAC;AAAA,EAED,SAASA,GAAE,aAAa;AAAA,IACtB,YAAYA,GAAE,QAAQ;AAAA,IACtB,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASD,QAAQA,GAAE,aAAa;AAAA,IACrB,OAAOA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA;AAAA,IAE5D,WAAW,YAAY,SAAS;AAAA;AAAA,IAEhC,WAAW,YAAY,SAAS;AAAA,EAClC,CAAC;AAAA;AAAA,EAGD,OAAOA,GAAE,aAAa;AAAA,IACpB,OAAOA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA,IAC5D,QAAQA,GAAE,OAAO,EAAE,IAAI,GAAG;AAAA,EAC5B,CAAC;AAAA;AAAA,EAGD,SAASA,GAAE,KAAK,CAAC,gBAAgB,UAAU,OAAO,SAAS,CAAC;AAAA,EAE5D,OAAOA,GAAE,aAAa;AAAA,IACpB,YAAYA,GAAE,IAAI,KAAK,EAAE,SAAS;AAAA,IAClC,OAAOA,GAAE,OAAO,EAAE,IAAI,GAAI;AAAA,EAC5B,CAAC;AAAA,EAED,QAAQA,GAAE,KAAK,CAAC,WAAW,YAAY,SAAS,QAAQ,CAAC;AAC3D,CAAC;;;ACzHD,SAAS,KAAAC,UAAS;AAuBX,IAAM,cAAcC,GAAE,KAAK,CAAC,aAAa,UAAU,WAAW,KAAK,CAAC;AAGpE,IAAM,aAAaA,GAAE,aAAa;AAAA,EACvC,SAASA,GAAE,QAAQ,CAAC;AAAA,EACpB,OAAOA,GAAE;AAAA,IACP;AAAA,IACAA,GAAE,aAAa;AAAA,MACb,SAAS;AAAA;AAAA,MAET,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,IACrC,CAAC;AAAA,EACH;AACF,CAAC;;;AClCD,SAAS,qBAAqB;AAK9B,SAAS,KAAAC,UAAS;AAYlB,IAAM,EAAE,aAAa,IAAI,cAAc,YAAY,GAAG,EAAE,aAAa;AAqFrE,IAAM,MAAMC,GAAE,OAAO;AAAA,EACnB,KAAKA,GAAE,OAAO;AAAA,EACd,IAAIA,GAAE,OAAO;AAAA,EACb,IAAIA,GAAE,OAAO;AAAA,EACb,GAAGA,GAAE,OAAO;AAAA,EACZ,MAAMA,GAAE,OAAO;AAAA,EACf,YAAYA,GAAE,OAAO,EAAE,SAAS;AAAA,EAChC,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,MAAMA,GAAE,OAAO;AACjB,CAAC;;;AChHD,SAAS,KAAAC,UAAS;AAOX,IAAM,aAAaA,GAAE,mBAAmB,QAAQ;AAAA,EACrDA,GAAE,aAAa;AAAA,IACb,MAAMA,GAAE,QAAQ,OAAO;AAAA,IACvB,OAAO,YAAY,MAAM;AAAA,IACzB,QAAQA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACvC,CAAC;AAAA,EACDA,GAAE,aAAa;AAAA,IACb,MAAMA,GAAE,QAAQ,MAAM;AAAA,IACtB,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,IAC/B,SAASA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,IACtC,OAAOA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACrD,CAAC;AAAA,EACDA,GAAE,aAAa,EAAE,MAAMA,GAAE,QAAQ,OAAO,GAAG,QAAQA,GAAE,IAAI,EAAE,YAAY,GAAG,MAAMA,GAAE,QAAQ,UAAU,EAAE,CAAC;AAAA,EACvGA,GAAE,aAAa,EAAE,MAAMA,GAAE,QAAQ,OAAO,GAAG,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,CAAC;AAAA,EAChFA,GAAE,aAAa,EAAE,MAAMA,GAAE,QAAQ,OAAO,GAAG,IAAIA,GAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAAA,EACtEA,GAAE,aAAa,EAAE,MAAMA,GAAE,QAAQ,QAAQ,GAAG,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAM,EAAE,CAAC;AAC5E,CAAC;AAIM,IAAM,OAAO;AAAA;AAAA,EAElB,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,aAAa;AAAA;AAAA,EAEb,UAAU;AACZ;;;ACpCA,SAAS,KAAAC,UAAS;AAWlB,IAAM,QAAQA,GAAE,IAAI,EAAE,YAAY,EAAE,IAAI,GAAM,EAAE,SAAS;AAEzD,IAAM,YAAYA,GAAE,aAAa;AAAA,EAC/B,OAAO,YAAY,MAAM;AAAA,EACzB,QAAQA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrC,SAAS;AACX,CAAC;AAED,IAAM,WAAWA,GAAE,aAAa;AAAA,EAC9B,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC/B,SAASA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA;AAAA,EAEtC,OAAOA,GAAE,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,GAAGA,GAAE,OAAO,EAAE,IAAI,GAAS,CAAC,EAAE,SAAS;AAAA,EACjF,SAAS;AACX,CAAC;AAED,IAAM,YAAYA,GAAE,aAAa,EAAE,OAAOA,GAAE,IAAI,EAAE,YAAY,EAAE,IAAI,GAAS,GAAG,SAAS,MAAM,CAAC;AAGhG,IAAM,YAAYA,GAAE,aAAa,EAAE,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,GAAG,SAAS,MAAM,CAAC;AAEtF,IAAM,YAAYA,GAAE,aAAa,EAAE,OAAOA,GAAE,IAAI,EAAE,YAAY,EAAE,IAAI,GAAM,EAAE,CAAC;AAEtE,IAAM,eAAeA,GAAE,MAAM,CAAC,WAAW,UAAU,WAAW,WAAW,SAAS,CAAC;AAGnF,IAAM,WAAWA,GAAE,aAAa;AAAA,EACrC,OAAOA,GAAE,MAAM,YAAY,EAAE,IAAI,GAAI;AAAA,EACrC,QAAQA,GAAE,OAAO,EAAE,IAAI,GAAM;AAAA,EAC7B,UAAUA,GAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,CAAC;AAAA;AAAA,EAE3C,MAAMA,GAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA,EAE/B,WAAWA,GAAE,OAAO,EAAE,YAAY,EAAE,IAAI,GAAG,EAAE,QAAQ,CAAC;AACxD,CAAC;;;AThCD,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClB;AAAA,EAET,YAAY,MAAc,SAAiB;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAAS,SAAS,MAA4D;AAC5E,MAAI;AACJ,MAAI;AACF,aAAS,UAAU;AAAA,MACjB;AAAA,MACA,SAAS,EAAE,iBAAiB,EAAE,MAAM,SAAS,GAAG,QAAQ,EAAE,MAAM,SAAS,EAAE;AAAA,MAC3E,kBAAkB;AAAA,MAClB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,IAAI,SAAS,KAAK,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,EACvF;AACA,QAAM,EAAE,QAAQ,YAAY,IAAI;AAChC,MAAI,OAAO,eAAe,MAAM,OAAW,OAAM,IAAI,SAAS,KAAK,OAAO,yBAAyB;AACnG,MAAI,OAAO,WAAW,UAAa,OAAO,WAAW,GAAI,OAAM,IAAI,SAAS,KAAK,OAAO,kBAAkB;AAC1G,MAAI,YAAY,KAAK,GAAG,EAAE,KAAK,MAAM,GAAI,OAAM,IAAI,SAAS,KAAK,OAAO,gBAAgB;AAExF,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,OAAO,eAAe,CAAC;AAAA,EAC3C,QAAQ;AACN,UAAM,IAAI,SAAS,KAAK,OAAO,mCAAmC;AAAA,EACpE;AACA,QAAM,WAAW,SAAS,UAAU,IAAI;AACxC,MAAI,CAAC,SAAS,QAAS,OAAM,IAAI,SAAS,KAAK,OAAO,qBAAqB,SAAS,MAAM,OAAO,EAAE;AACnG,SAAO,EAAE,UAAU,SAAS,MAAM,YAAY,OAAO,OAAO;AAC9D;AAGA,SAAS,UAAU,KAAa,MAAsB;AACpD,QAAM,SAAS,QAAQ,KAAK,IAAI;AAChC,QAAM,UAAU,SAAS,KAAK,MAAM;AACpC,MAAI,WAAW,IAAI,KAAK,YAAY,MAAM,YAAY,QAAQ,QAAQ,WAAW,KAAK,GAAG,EAAE,GAAG;AAC5F,UAAM,IAAI,SAAS,KAAK,aAAa,oDAAoD,IAAI,EAAE;AAAA,EACjG;AACA,SAAO;AACT;AAEA,SAAS,UAAU,MAAc,SAAuB;AACtD,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,SAAS,MAAM;AACrC;AAEA,SAAS,KAAK,MAAwB;AACpC,UAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,IAAI,CAAC;AAAA,CAAI;AAClD;AAEA,eAAe,KAAK,UAAoB,KAAa,YAAqC;AACxF,QAAM,OAAO,OAAO,OAA0C;AAC5D,QAAI,OAAO,UAAa,KAAK,EAAG,OAAM,MAAM,KAAK,SAAS,SAAS;AAAA,EACrE;AAEA,aAAW,QAAQ,SAAS,OAAO;AACjC,QAAI,WAAW,MAAM;AACnB,YAAM,KAAK,KAAK,KAAK;AACrB,WAAK,EAAE,MAAM,SAAS,IAAI,KAAK,MAAM,CAAC;AACtC;AAAA,IACF;AACA,UAAM,KAAK,KAAK,OAAO;AACvB,QAAI,WAAW,MAAM;AACnB,WAAK;AAAA,QACH,MAAM;AAAA,QACN,OAAO,KAAK;AAAA,QACZ,GAAI,KAAK,WAAW,SAAY,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO;AAAA,MAC7D,CAAC;AAAA,IACH,WAAW,UAAU,MAAM;AACzB,YAAM,SAAS,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC;AAC9C,YAAM,UAAU,OAAO,IAAI,CAAC,CAAC,IAAI,MAAM,UAAU,KAAK,IAAI,CAAC;AAC3D,aAAO,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,UAAU;AACrC,cAAM,SAAS,QAAQ,KAAK;AAC5B,YAAI,WAAW,OAAW,WAAU,QAAQ,OAAO;AAAA,MACrD,CAAC;AACD,WAAK;AAAA,QACH,MAAM;AAAA,QACN,MAAM,KAAK;AAAA,QACX,GAAI,KAAK,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,KAAK,QAAQ;AAAA,QAC9D,OAAO,OAAO,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AAAA,MACpC,CAAC;AAAA,IACH,WAAW,WAAW,MAAM;AAC1B,WAAK,EAAE,MAAM,SAAS,QAAQ,KAAK,OAAO,MAAM,WAAW,CAAC;AAAA,IAC9D,OAAO;AACL,WAAK,EAAE,MAAM,SAAS,SAAS,KAAK,MAAM,CAAC;AAC3C,gBAAU,YAAY,SAAS,MAAM;AACrC,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AAEA,YAAU,YAAY,SAAS,MAAM;AACrC,OAAK,EAAE,MAAM,UAAU,MAAM,SAAS,OAAO,CAAC;AAC9C,MAAI,SAAS,MAAM;AAEjB,UAAM,IAAI,QAAe,MAAM,YAAY,MAAM,QAAW,GAAM,CAAC;AAAA,EACrE;AACA,SAAO,SAAS;AAClB;AAEA,eAAe,KAAK,MAAiC;AACnD,QAAM,EAAE,UAAU,WAAW,IAAI,SAAS,IAAI;AAC9C,QAAM,MAAM,QAAQ,IAAI;AACxB,SAAO,KAAK,UAAU,KAAK,QAAQ,KAAK,UAAU,CAAC;AACrD;AAEA,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,EAAE;AAAA,EAC1B,CAAC,SAAS;AACR,YAAQ,WAAW;AAAA,EACrB;AAAA,EACA,CAAC,UAAmB;AAClB,UAAM,OAAO,iBAAiB,WAAW,MAAM,OAAO,KAAK;AAC3D,YAAQ,OAAO,MAAM,cAAc,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,CAAI;AAC7F,YAAQ,WAAW;AAAA,EACrB;AACF;",
6
6
  "names": ["z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z"]
7
7
  }
package/dist/view.html CHANGED
@@ -327,6 +327,11 @@
327
327
  .waiting .who {
328
328
  color: var(--fg);
329
329
  }
330
+ /* The other project's name, said quietly: it is context, not the subject of the row. */
331
+ .from {
332
+ color: var(--fg-faint);
333
+ font-size: var(--text-xs);
334
+ }
330
335
  .waiting .what {
331
336
  color: var(--fg-dim);
332
337
  }
@@ -615,7 +620,7 @@
615
620
  * of it; a page that worked out its own answer would eventually tell someone a run was ready while the
616
621
  * merge tool refused it.
617
622
  */
618
- function waitingSection(waiting) {
623
+ function waitingSection(waiting, here) {
619
624
  const section = el("section");
620
625
  const head = el("h2");
621
626
  head.append(document.createTextNode("Waiting on you"));
@@ -639,6 +644,15 @@
639
644
  // The work, not the run id — the id is for the tools, and is on the row for anyone who needs it.
640
645
  row.title = item.runId;
641
646
  row.append(el("span", "who", item.task ?? item.runId));
647
+ /*
648
+ * Where it came from, when it did not come from here. One daemon serves the whole machine, so a row
649
+ * with no repository asks somebody to review work they may never have seen the project for.
650
+ */
651
+ if (item.repo && item.repo !== here) {
652
+ const from = el("span", "from", item.repo.split("/").pop());
653
+ from.title = item.repo;
654
+ row.append(from);
655
+ }
642
656
  row.append(
643
657
  el(
644
658
  "span",
@@ -801,7 +815,12 @@
801
815
  main.append(crew);
802
816
 
803
817
  // Above the runs: it is the only thing on this page that asks something of whoever is reading it.
804
- main.append(waitingSection(snapshot.waiting ?? []));
818
+ /*
819
+ * The repository this page is about: the one the mission on screen belongs to. Rows from anywhere else
820
+ * are labelled, because one daemon serves the whole machine and an unlabelled row is a request to review
821
+ * work from a project the reader may not even have open.
822
+ */
823
+ main.append(waitingSection(snapshot.waiting ?? [], mission?.repo?.root));
805
824
 
806
825
  const runs = el("section");
807
826
  const runsHead = el("h2");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fanout-cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Let Claude Code lead the other coding-agent CLIs you already pay for.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,34 +14,44 @@
14
14
  },
15
15
  "homepage": "https://github.com/elberacasa/fanout#readme",
16
16
  "bin": {
17
- "fanout": "./dist/cli.js"
17
+ "fanout": "./src/cli.ts"
18
18
  },
19
19
  "exports": {
20
- ".": {
21
- "default": "./dist/cli.js"
22
- }
20
+ ".": "./src/main.ts"
23
21
  },
24
22
  "files": [
25
23
  "dist",
24
+ "plugin",
26
25
  "README.md",
27
26
  "LICENSE"
28
27
  ],
28
+ "publishConfig": {
29
+ "exports": {
30
+ ".": {
31
+ "default": "./dist/cli.js"
32
+ }
33
+ },
34
+ "bin": {
35
+ "fanout": "./dist/cli.js"
36
+ }
37
+ },
38
+ "scripts": {
39
+ "typecheck": "tsc -p tsconfig.json",
40
+ "build": "node ../../scripts/bundle-cli.mjs",
41
+ "prepack": "node ../../scripts/bundle-cli.mjs"
42
+ },
29
43
  "dependencies": {
30
44
  "@modelcontextprotocol/sdk": "1.30.0",
31
45
  "ws": "8.21.3",
32
46
  "zod": "4.6.2"
33
47
  },
34
48
  "devDependencies": {
35
- "fanout-adapter-claude": "0.8.0",
36
- "fanout-adapter-fake": "0.8.0",
37
- "fanout-adapter-grok": "0.8.0",
38
- "fanout-adapter-codex": "0.8.0",
39
- "fanout-core": "0.8.0",
40
- "fanout-mcp": "0.8.0",
41
- "fanout-daemon": "0.8.0"
42
- },
43
- "scripts": {
44
- "typecheck": "tsc -p tsconfig.json",
45
- "build": "node ../../scripts/bundle-cli.mjs"
49
+ "fanout-adapter-claude": "workspace:*",
50
+ "fanout-adapter-codex": "workspace:*",
51
+ "fanout-adapter-fake": "workspace:*",
52
+ "fanout-adapter-grok": "workspace:*",
53
+ "fanout-core": "workspace:*",
54
+ "fanout-daemon": "workspace:*",
55
+ "fanout-mcp": "workspace:*"
46
56
  }
47
- }
57
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "fanout",
3
+ "description": "Lead a crew of the coding-agent CLIs you already pay for: plan, fan out into isolated git worktrees, watch, and review before anything merges.",
4
+ "version": "0.9.1",
5
+ "author": {
6
+ "name": "elberacasa"
7
+ },
8
+ "homepage": "https://github.com/elberacasa/fanout",
9
+ "repository": "https://github.com/elberacasa/fanout",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "agents",
13
+ "orchestration",
14
+ "codex",
15
+ "grok",
16
+ "worktrees",
17
+ "local-first"
18
+ ]
19
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "mcpServers": {
3
+ "fanout": {
4
+ "type": "stdio",
5
+ "command": "node",
6
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/fanout", "mcp"],
7
+ "env": {}
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,44 @@
1
+ # The Fanout plugin for Claude Code
2
+
3
+ Claude Code becomes the lead: it plans a mission, fans it out to the other agent CLIs on your machine, watches them
4
+ work in isolated git worktrees, and reviews every diff. Nothing merges without you.
5
+
6
+ ## What you get
7
+
8
+ | | |
9
+ |---|---|
10
+ | `/fanout <goal>` | Plan a mission, check it against the safety gate, launch it, and watch it |
11
+ | `/fanout:crew` | Which CLIs are installed, signed in, and ready |
12
+ | `/fanout:watch [mission]` | Subscribe to the live feed and review each run as it finishes |
13
+ | The `fanout` skill | The lead's judgment: when to fan out, how to write a prompt an agent can follow, how to review what comes back |
14
+ | Hooks | The crew at the start of a session; a warning if runs are still going when you stop |
15
+ | Seven MCP tools | `seats`, `repo_overview`, `plan_check`, `launch`, `mission_status`, `run_diff`, `cancel_mission` |
16
+
17
+ ## Installing
18
+
19
+ The plugin runs `fanout mcp`, so the `fanout` command must be on your `PATH`.
20
+
21
+ **From a clone**, while the package is not yet published:
22
+
23
+ ```sh
24
+ git clone https://github.com/elberacasa/fanout.git && cd fanout
25
+ corepack enable && pnpm install
26
+ pnpm --filter fanout-cli link --global # puts `fanout` on your PATH
27
+ claude --plugin-dir "$PWD/plugin" # try it in one session
28
+ ```
29
+
30
+ Check it before you rely on it:
31
+
32
+ ```sh
33
+ fanout status # your crew
34
+ claude plugin validate plugin
35
+ ```
36
+
37
+ ## What it will not do
38
+
39
+ - **It will not merge.** It reads a run's diff from the workspace and hands you the decision. The merge gate, with
40
+ review, your project's checks and proof that a fix fails on the old code, is the next milestone.
41
+ - **It will not work around a usage limit.** A seat that is out of quota is out; the lead says so and uses another.
42
+ - **It will not hand an agent your secrets.** Workspaces exclude ignored and deny-listed files, and a run gets an
43
+ allowlisted environment only — never your shell's.
44
+ - **It sends nothing anywhere.** The daemon listens on `127.0.0.1` with a token only you can read.