@tangle-network/agent-runtime 0.60.0 → 0.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/agent.d.ts +1 -1
  2. package/dist/agent.js +1 -2
  3. package/dist/agent.js.map +1 -1
  4. package/dist/analyst-loop.d.ts +1 -1
  5. package/dist/{chunk-IN7WHMGZ.js → chunk-5V343QPB.js} +47 -7
  6. package/dist/chunk-5V343QPB.js.map +1 -0
  7. package/dist/chunk-7IXF3VUJ.js +59 -0
  8. package/dist/chunk-7IXF3VUJ.js.map +1 -0
  9. package/dist/{chunk-ZWGEA722.js → chunk-AU5MCNHO.js} +2 -2
  10. package/dist/chunk-IBRJTG7O.js +475 -0
  11. package/dist/chunk-IBRJTG7O.js.map +1 -0
  12. package/dist/{chunk-MMDIORZY.js → chunk-Q5R33I7Y.js} +4 -23
  13. package/dist/chunk-Q5R33I7Y.js.map +1 -0
  14. package/dist/{chunk-45D64J7B.js → chunk-VCOT7XEQ.js} +190 -99
  15. package/dist/chunk-VCOT7XEQ.js.map +1 -0
  16. package/dist/coder-DD5J5Onk.d.ts +52 -0
  17. package/dist/coordination-k29badiX.d.ts +381 -0
  18. package/dist/{delegates-C94qchkz.d.ts → delegates-CWMv_rKL.d.ts} +649 -21
  19. package/dist/index.d.ts +9 -6
  20. package/dist/index.js +9 -9
  21. package/dist/intelligence.d.ts +1 -1
  22. package/dist/{loop-runner-bin-Noz7P-mS.d.ts → loop-runner-bin-Bqt0hiNY.d.ts} +46 -12
  23. package/dist/loop-runner-bin.d.ts +7 -4
  24. package/dist/loop-runner-bin.js +4 -4
  25. package/dist/loops.d.ts +7 -6
  26. package/dist/loops.js +17 -6
  27. package/dist/mcp/bin.js +10 -10
  28. package/dist/mcp/bin.js.map +1 -1
  29. package/dist/mcp/index.d.ts +12 -12
  30. package/dist/mcp/index.js +7 -7
  31. package/dist/{openai-tools-d4GKwgya.d.ts → openai-tools-CA2N3-Ak.d.ts} +1 -1
  32. package/dist/profiles.d.ts +7 -9
  33. package/dist/profiles.js +5 -7
  34. package/dist/profiles.js.map +1 -1
  35. package/dist/{run-loop-CcqfR_gy.d.ts → run-loop-D3PwlG7J.d.ts} +1 -1
  36. package/dist/runtime.d.ts +30 -1046
  37. package/dist/runtime.js +17 -6
  38. package/dist/{types-CUzjRFZ3.d.ts → types-Crxftafi.d.ts} +2 -2
  39. package/dist/workflow.d.ts +2 -2
  40. package/dist/workflow.js +1 -2
  41. package/dist/workflow.js.map +1 -1
  42. package/dist/worktree-fanout-CBULEoVe.d.ts +1098 -0
  43. package/package.json +1 -1
  44. package/dist/chunk-45D64J7B.js.map +0 -1
  45. package/dist/chunk-4FEUFYOY.js +0 -282
  46. package/dist/chunk-4FEUFYOY.js.map +0 -1
  47. package/dist/chunk-7QYOXFCD.js +0 -293
  48. package/dist/chunk-7QYOXFCD.js.map +0 -1
  49. package/dist/chunk-IN7WHMGZ.js.map +0 -1
  50. package/dist/chunk-MMDIORZY.js.map +0 -1
  51. package/dist/coder-CybltHEm.d.ts +0 -163
  52. package/dist/coordination-Biw19JzN.d.ts +0 -944
  53. /package/dist/{chunk-ZWGEA722.js.map → chunk-AU5MCNHO.js.map} +0 -0
package/dist/agent.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _tangle_network_agent_eval from '@tangle-network/agent-eval';
2
2
  import { TraceAnalystKindSpec, AnalystFinding } from '@tangle-network/agent-eval';
3
- import { R as RuntimeStreamEvent, S as SandboxClient, O as OutputAdapter, A as AgentRunSpec } from './types-CUzjRFZ3.js';
3
+ import { R as RuntimeStreamEvent, S as SandboxClient, O as OutputAdapter, A as AgentRunSpec } from './types-Crxftafi.js';
4
4
  import { A as AgentSurfaces } from './improvement-adapter-BC4HhuAR.js';
5
5
  export { C as CreateSurfaceImprovementAdapterOpts, D as DraftPatchInput, a as DraftPatchOutput, R as ResolvedSurface, S as SurfaceImprovementEdit, b as SurfaceValidationIssue, c as createSurfaceImprovementAdapter, r as renderSurfaceIssues, d as resolveSubjectPath, v as validateSurfaces } from './improvement-adapter-BC4HhuAR.js';
6
6
  import { K as KnowledgeAdapter, a as RunAnalystLoopResult } from './types-p8dWBIXL.js';
package/dist/agent.js CHANGED
@@ -3,8 +3,7 @@ import {
3
3
  } from "./chunk-XRYEZPR6.js";
4
4
  import {
5
5
  createSandboxForSpec
6
- } from "./chunk-45D64J7B.js";
7
- import "./chunk-7QYOXFCD.js";
6
+ } from "./chunk-VCOT7XEQ.js";
8
7
  import "./chunk-WIR4HOOJ.js";
9
8
  import {
10
9
  mapSandboxEvent
package/dist/agent.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent/surfaces.ts","../src/agent/define-agent.ts","../src/agent/improvement-adapter.ts","../src/agent/knowledge-adapter.ts","../src/agent/outcome.ts","../src/agent/sandbox-act.ts"],"sourcesContent":["/**\n * `AgentSurfaces` — declarative map of the mutable file/directory paths\n * the self-improvement loop can edit on behalf of an agent.\n *\n * The substrate uses this map to resolve every parsed `FindingSubject`\n * (from agent-eval) to a real on-disk path. No per-vertical glue;\n * no fabricated paths; no silent `existsSync(...)` skips that hide\n * misconfiguration from the operator.\n *\n * Surfaces are validated at `defineAgent` time — missing paths fail\n * loud with a list of every offender. A surface that's not needed\n * (e.g. an agent with no RAG corpora) is simply omitted; the loop\n * refuses to route those subjects rather than fabricating a target.\n */\n\nimport { existsSync } from 'node:fs'\nimport { isAbsolute, join } from 'node:path'\nimport type { FindingSubject } from '@tangle-network/agent-eval'\n\n/**\n * Surface declarations. Every path is repo-relative (or absolute) at\n * `defineAgent` time. At resolution time, paths are joined against the\n * agent's `repoRoot`.\n *\n * `systemPrompt`, `tools`, `personas` are DIRECTORIES; the loop appends\n * `<section>.md`, `<tool>/README.md`, `<persona-id>.yaml` etc.\n * `rubric`, `outputSchema` are SINGLE FILES; the loop edits them in\n * place.\n *\n * `knowledge` is the agent-knowledge root (typically `.agent-knowledge`);\n * `applyKnowledgeWriteBlocks` writes pages relative to it.\n *\n * Optional surfaces (`scaffolding`, `memory`, `rag`, `outputSchema`)\n * can be omitted — the loop will reject findings targeting them with a\n * clear log message instead of fabricating a path.\n */\nexport interface AgentSurfaces {\n /** Directory containing one markdown file per system-prompt section. */\n systemPrompt: string\n /** Directory containing one subdir per tool (`<tool>/README.md`). */\n tools: string\n /** Single file (TypeScript module) defining the rubric weights + dimensions. */\n rubric: string\n /** Knowledge-base root; typically `.agent-knowledge`. */\n knowledge: string\n /** Directory containing one YAML/JSON file per persona. */\n personas: string\n /** Optional: directory containing scaffolding rules (precondition checks, retry policies). */\n scaffolding?: string\n /** Optional: memory store path (JSONL / SQLite / DB). */\n memory?: string\n /** Optional: directory containing RAG corpora (`<corpus>/<doc-id>.md`). */\n rag?: string\n /** Optional: single file defining the output schema (Zod / JSON Schema). */\n outputSchema?: string\n}\n\nexport interface ResolvedSurface {\n /** Absolute filesystem path the operator can `cat` / `vim`. */\n absolutePath: string\n /** Repo-relative path for PR descriptions, diffs, audit logs. */\n repoRelativePath: string\n /** Whether the path currently exists on disk. */\n exists: boolean\n /** The substrate's intent: edit an existing file or create a new one. */\n intent: 'edit-existing' | 'create-new'\n}\n\n/**\n * Resolve a parsed `FindingSubject` to the file path the substrate\n * should edit (or create) on disk.\n *\n * Returns `null` when:\n * - the subject targets a surface the agent didn't declare\n * (e.g. `rag:*` when `surfaces.rag` is undefined), OR\n * - the subject is a `cluster` (failure-mode emits these as evidence,\n * not actionable mutations — they don't route to a file).\n *\n * Returns a `ResolvedSurface` with `intent: 'create-new'` when the\n * subject names a path that doesn't yet exist (e.g. a new wiki page).\n * The caller chooses whether to honour the create — for tightly-managed\n * surfaces like `systemPrompt` it's usually a contract violation\n * (the analyst named a section that doesn't exist); for `knowledge`\n * it's the whole point.\n */\nexport function resolveSubjectPath(\n subject: FindingSubject,\n surfaces: AgentSurfaces,\n repoRoot: string,\n): ResolvedSurface | null {\n const candidates = candidatePathsForSubject(subject, surfaces)\n if (candidates.length === 0) return null\n\n // Probe candidates in order, preferring the first one that exists on disk.\n // Lets the substrate accept both the flat `<section>.md` convention and\n // the skill-dir `<section>/SKILL.md` convention without forcing one layout.\n // When none exists, fall back to the first candidate (canonical create-new).\n for (const rel of candidates) {\n const abs = isAbsolute(rel) ? rel : join(repoRoot, rel)\n if (existsSync(abs)) {\n return { absolutePath: abs, repoRelativePath: rel, exists: true, intent: 'edit-existing' }\n }\n }\n const fallback = candidates[0]!\n const fallbackAbs = isAbsolute(fallback) ? fallback : join(repoRoot, fallback)\n return {\n absolutePath: fallbackAbs,\n repoRelativePath: fallback,\n exists: false,\n intent: 'create-new',\n }\n}\n\nfunction candidatePathsForSubject(\n subject: FindingSubject,\n surfaces: AgentSurfaces,\n): ReadonlyArray<string> {\n switch (subject.kind) {\n case 'knowledge.wiki':\n case 'knowledge.stale':\n return [join(surfaces.knowledge, `${subject.slug}.md`)]\n case 'knowledge.claim':\n // Claims land in a per-topic claims directory under the knowledge root.\n return [join(surfaces.knowledge, 'claims', `${slugify(subject.topic)}.md`)]\n case 'knowledge.raw':\n return [join(surfaces.knowledge, 'raw', `${subject.sourceId}.md`)]\n case 'system-prompt': {\n const slug = slugify(subject.section)\n // Prefer flat layout for create-new (canonical); probe skill-dir layout\n // in case the existing repo (tax/legal/gtm/creative) uses\n // `<section>/SKILL.md` already.\n return [\n join(surfaces.systemPrompt, `${slug}.md`),\n join(surfaces.systemPrompt, slug, 'SKILL.md'),\n join(surfaces.systemPrompt, slug, 'index.md'),\n ]\n }\n case 'tool-doc':\n if (subject.aspect) {\n return [join(surfaces.tools, subject.tool, `${slugify(subject.aspect)}.md`)]\n }\n // tool-doc default: `<tool>/README.md`; also probe `<tool>.md` for flat\n // tool-list repos.\n return [\n join(surfaces.tools, subject.tool, 'README.md'),\n join(surfaces.tools, `${subject.tool}.md`),\n ]\n case 'new-tool':\n return [join(surfaces.tools, subject.name, 'README.md')]\n case 'rag':\n if (!surfaces.rag) return []\n return [join(surfaces.rag, subject.corpus, `${subject.docId}.md`)]\n case 'memory':\n if (!surfaces.memory) return []\n return [join(surfaces.memory, `${slugify(subject.key)}.json`)]\n case 'scaffolding':\n if (!surfaces.scaffolding) return []\n return [join(surfaces.scaffolding, `${slugify(subject.concern)}.md`)]\n case 'output-schema':\n if (!surfaces.outputSchema) return []\n return [surfaces.outputSchema]\n case 'websearch.outdated':\n case 'prior-run-summary':\n // Stale signals don't map to a single file — handled by the knowledge\n // adapter as `agent-knowledge:stale:*` after operator review.\n return []\n case 'cluster':\n // failure-mode cluster labels are evidence, not mutations.\n return []\n }\n}\n\nfunction slugify(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 200) || 'untitled'\n )\n}\n\n/**\n * Validate that every declared surface exists on disk under `repoRoot`.\n *\n * Returns an array of `SurfaceValidationIssue` — empty when all required\n * surfaces resolve. `defineAgent` throws with the issues rendered, so\n * a misconfigured manifest fails at startup (not at the first finding\n * the loop produces 20 minutes later).\n */\nexport interface SurfaceValidationIssue {\n surface: keyof AgentSurfaces\n path: string\n reason: 'missing' | 'not-directory' | 'not-file'\n}\n\nexport function validateSurfaces(\n surfaces: AgentSurfaces,\n repoRoot: string,\n): ReadonlyArray<SurfaceValidationIssue> {\n const issues: SurfaceValidationIssue[] = []\n const dirSurfaces: ReadonlyArray<keyof AgentSurfaces> = [\n 'systemPrompt',\n 'tools',\n 'personas',\n 'knowledge',\n ]\n const fileSurfaces: ReadonlyArray<keyof AgentSurfaces> = ['rubric']\n const optionalDirSurfaces: ReadonlyArray<keyof AgentSurfaces> = ['scaffolding', 'memory', 'rag']\n const optionalFileSurfaces: ReadonlyArray<keyof AgentSurfaces> = ['outputSchema']\n\n for (const key of dirSurfaces) {\n const p = surfaces[key] as string | undefined\n if (!p) {\n issues.push({ surface: key, path: '', reason: 'missing' })\n continue\n }\n const abs = isAbsolute(p) ? p : join(repoRoot, p)\n if (!existsSync(abs)) {\n issues.push({ surface: key, path: p, reason: 'missing' })\n }\n }\n for (const key of fileSurfaces) {\n const p = surfaces[key] as string | undefined\n if (!p) {\n issues.push({ surface: key, path: '', reason: 'missing' })\n continue\n }\n const abs = isAbsolute(p) ? p : join(repoRoot, p)\n if (!existsSync(abs)) {\n issues.push({ surface: key, path: p, reason: 'missing' })\n }\n }\n for (const key of [...optionalDirSurfaces, ...optionalFileSurfaces]) {\n const p = surfaces[key] as string | undefined\n if (p === undefined) continue\n const abs = isAbsolute(p) ? p : join(repoRoot, p)\n if (!existsSync(abs)) {\n issues.push({ surface: key, path: p, reason: 'missing' })\n }\n }\n return issues\n}\n\nexport function renderSurfaceIssues(\n issues: ReadonlyArray<SurfaceValidationIssue>,\n repoRoot: string,\n): string {\n if (issues.length === 0) return ''\n const lines = issues.map(\n (i) => ` - ${i.surface}: ${i.path ? `\"${i.path}\"` : '<not set>'} (${i.reason})`,\n )\n return [\n `Agent surface validation failed against repoRoot=${repoRoot}:`,\n ...lines,\n '',\n 'Fix the manifest: every required surface must point at an existing',\n 'directory (systemPrompt / tools / personas / knowledge) or file',\n '(rubric). Optional surfaces (scaffolding / memory / rag / outputSchema)',\n 'may be omitted; the loop will reject findings targeting omitted',\n 'surfaces rather than fabricating a path.',\n ].join('\\n')\n}\n","/**\n * `defineAgent` — typed, validated manifest API for declarative agent\n * configuration. The substrate consumes this manifest to wire the\n * canonical eval pattern + analyst self-improvement loop without any\n * per-vertical glue.\n *\n * Design goal: scale to 1000s of vertical agents. Every agent declares\n * its surfaces, rubric, runtime, and analyst configuration in ~50 lines.\n * No per-vertical `ImprovementAdapter`. No per-vertical CLI. No\n * fabricated paths.\n *\n * Validation: `defineAgent` runs `validateSurfaces` synchronously and\n * throws a structured error if any required surface is missing on\n * disk. The cost is one filesystem stat per surface (cheap); the\n * benefit is a manifest that can't ship broken.\n */\n\nimport type { TraceAnalystKindSpec } from '@tangle-network/agent-eval'\nimport type { RuntimeStreamEvent } from '../types'\nimport { type AgentSurfaces, renderSurfaceIssues, validateSurfaces } from './surfaces'\n\n// ── manifest ─────────────────────────────────────────────────────────\n\n/**\n * The full agent manifest. Each agent ships ONE of these.\n *\n * Generics:\n * `TPersona` — the agent's persona shape (loaded from\n * `surfaces.personas`). Defaults to `unknown` so the substrate's\n * persona discovery (`loadPersonas`) can accept anything; per-agent\n * code re-narrows when it matters.\n * `TRunOutput` — the shape `runtime.act` returns. Used by the rubric\n * scorers and emitted into the trace.\n */\nexport interface AgentManifest<TPersona = unknown, TRunOutput = unknown> {\n /**\n * Stable identifier — used as `projectId` in traces, as the analyst\n * loop's `runId` prefix, and as the namespace under which findings\n * are persisted. MUST match the agent's repo name to keep\n * cross-repo telemetry joinable.\n */\n id: string\n\n /**\n * Filesystem root the substrate resolves surface paths against.\n * Typically `process.cwd()` or a fixed absolute path. Use an\n * absolute path when the agent's tests may run from subdirectories\n * (vitest sometimes shifts cwd).\n */\n repoRoot: string\n\n /**\n * Map of mutable surfaces the self-improvement loop can edit. See\n * `AgentSurfaces` — required: `systemPrompt`, `tools`, `rubric`,\n * `knowledge`, `personas`. Optional: `scaffolding`, `memory`, `rag`,\n * `outputSchema`.\n *\n * Every required path is validated at `defineAgent` time. Missing\n * paths throw with the full list of offenders.\n */\n surfaces: AgentSurfaces\n\n /**\n * Rubric the substrate uses to score each run. Dimensions × weights\n * × judges. The substrate computes the weighted composite and\n * stamps it into the RunRecord.\n */\n rubric: AgentRubric<TRunOutput>\n\n /**\n * Runtime adapter — how the substrate INVOKES the agent against a\n * persona. The `act` function takes a persona + a context (with the\n * tracer the substrate threads through for span emission) and\n * returns the run output the rubric will score.\n *\n * The agent's existing production runtime goes in here; the\n * substrate is intentionally thin around it.\n */\n runtime: AgentRuntime<TPersona, TRunOutput>\n\n /**\n * Persona discovery — the substrate loads personas via this function\n * at eval start. Can read from `surfaces.personas`, an API, or be\n * hardcoded. The substrate calls it once per `runAgentEval` call;\n * persona ordering is preserved.\n */\n personas: () => Promise<ReadonlyArray<TPersona>>\n\n /**\n * Analyst kinds the substrate runs against each persona's trace.\n * Defaults to `DEFAULT_TRACE_ANALYST_KINDS` from agent-eval. Per-agent\n * authors can prune (e.g. skip `knowledge-poisoning` when there's no\n * knowledge base) or extend (custom domain kinds).\n *\n * Empty array disables the loop — useful for `pnpm eval --no-analyst`.\n */\n analystKinds: ReadonlyArray<TraceAnalystKindSpec>\n\n /**\n * Analyst LLM configuration. The substrate uses these for all four\n * kinds (override per-kind via `analystKinds` if needed).\n */\n analyst: AnalystConfig\n\n /**\n * Auto-apply policy. Knowledge / improvement edits land only when\n * `enabled === true` AND the source finding's confidence meets the\n * threshold. `mode` controls how applies happen: `'write'` mutates\n * files in-place; `'open-pr'` writes to a branch and opens a PR.\n *\n * Default: knowledge auto-applies at confidence ≥0.85 in `'write'`\n * mode (wiki edits are git-reversible); improvement stays at\n * `enabled: false` until the agent author has measured precision.\n */\n autoApply?: AutoApplyPolicy\n}\n\nexport interface AgentRubric<TRunOutput> {\n /** Dimensions composing the weighted score. Weights sum to 1.0 by convention. */\n dimensions: ReadonlyArray<RubricDimension<TRunOutput>>\n /**\n * Optional judges layered on top of deterministic dimensions. Each\n * judge returns a score per dimension; the substrate averages judges\n * (mean by default) for the LLM contribution.\n */\n judges?: ReadonlyArray<JudgeConfig<TRunOutput>>\n}\n\nexport interface RubricDimension<TRunOutput> {\n /** Unique identifier — appears in finding subjects (`rubric:<id>`). */\n id: string\n /** 0..1 — weight in the composite. */\n weight: number\n /**\n * Deterministic scorer: given the persona + run output, returns a\n * 0..1 score. The substrate sums weight × score across dimensions\n * for the deterministic composite; judges supplement subjective dims.\n */\n score: (input: { persona: unknown; output: TRunOutput }) => number\n /** Optional human-readable label for reports. */\n label?: string\n}\n\nexport interface JudgeConfig<TRunOutput> {\n /** Judge identifier — appears in trace spans + manifest. */\n id: string\n /** Model snapshot to invoke. Pin the snapshot (`claude-sonnet-4-6@2025-04-15`); the validator rejects bare aliases. */\n model: string\n /** Dimensions this judge scores. */\n dimensions: ReadonlyArray<string>\n /**\n * Optional rubric anchors — text examples the judge sees as a\n * few-shot prompt to calibrate. STRONGLY recommended for subjective\n * dimensions; required by the calibration gate (Pearson ≥0.7).\n */\n anchors?: ReadonlyArray<{ input: string; output: TRunOutput; expected: Record<string, number> }>\n}\n\nexport interface AgentRuntime<TPersona, TRunOutput> {\n /**\n * Invoke the agent against one persona. Returns BOTH:\n * - `events`: an `AsyncIterable<RuntimeStreamEvent>` the chat-centric\n * product consumes verbatim (SSE / WebSocket / inline render).\n * **Streaming is mandatory — never collapse this to a single Promise.**\n * The agent's existing `runChatTurn` (or equivalent async generator)\n * plugs in here directly.\n * - `output`: a `Promise<TRunOutput>` resolved AFTER the event stream\n * drains. The eval substrate awaits this for rubric scoring; chat\n * products usually ignore it (they already rendered incrementally).\n *\n * Implementation contract:\n * 1. `act` MUST return immediately (synchronous construction of the\n * `events` iterator + the `output` promise).\n * 2. Iterating `events` drives the underlying LLM/tool calls — the\n * caller chooses when to consume.\n * 3. `output` resolves only after the iterator yields its terminal\n * event (typically `task_end`); see `collectAgentRun` helper.\n *\n * `ctx.emitter` is the substrate-threaded `TraceEmitter` — runtimes\n * SHOULD record LLM/tool spans through it for capture integrity.\n * `ctx.deadlineMs` is wall-clock; the runtime SHOULD honour for graceful\n * cancel. `ctx.signal` is the standard abort signal.\n */\n act: (persona: TPersona, ctx: AgentRunContext) => AgentRunInvocation<TRunOutput>\n}\n\nexport interface AgentRunInvocation<TRunOutput> {\n /** Live stream of typed runtime events. Consumed by chat UX directly. */\n events: AsyncIterable<RuntimeStreamEvent>\n /** Final structured output the rubric scores. Resolves after `events` drains. */\n output: Promise<TRunOutput>\n}\n\n/**\n * Stub for agents whose `runtime.act` is not yet wired to the substrate's\n * eval path. Preserves the streaming contract (empty event stream + a\n * rejected `output` promise that tells the caller exactly what to fix).\n *\n * Per-vertical manifests usually start with this stub and replace it with\n * the agent's real streaming runtime (`runChatTurn` or equivalent) once\n * the eval path consumes the manifest end-to-end.\n */\nexport function unimplementedAgentRun<TRunOutput = unknown>(\n reason = 'AgentRuntime.act is not yet wired for this manifest',\n): AgentRunInvocation<TRunOutput> {\n return {\n events: (async function* empty(): AsyncIterable<RuntimeStreamEvent> {})(),\n output: Promise.reject(new Error(reason)),\n }\n}\n\n/**\n * Drain `act`'s `events` into an array AND await its `output`. Useful for\n * eval / outcome-measurement code paths that don't care about live\n * rendering. The events array is preserved so the substrate can inspect\n * tool calls / readiness / questions retrospectively.\n *\n * IMPORTANT: chat-centric UX MUST NOT call this — it defeats streaming\n * (no incremental render). Use `for await (const ev of invocation.events)`\n * directly in the chat surface.\n */\nexport async function collectAgentRun<TRunOutput>(\n invocation: AgentRunInvocation<TRunOutput>,\n): Promise<{ events: ReadonlyArray<RuntimeStreamEvent>; output: TRunOutput }> {\n const events: RuntimeStreamEvent[] = []\n for await (const ev of invocation.events) events.push(ev)\n const output = await invocation.output\n return { events, output }\n}\n\nexport interface AgentRunContext {\n /** Substrate-managed trace emitter. */\n emitter: import('@tangle-network/agent-eval').TraceEmitter\n /** Stable run id for this persona × variant cell. */\n runId: string\n /** Variant the runtime is exercising (e.g. `'baseline'`, `'source-grounded'`). */\n variantId?: string\n /** Wall-clock deadline (epoch ms). The runtime SHOULD honour for graceful cancel. */\n deadlineMs?: number\n /** Optional abort signal. */\n signal?: AbortSignal\n}\n\nexport interface AnalystConfig {\n /** Model the analyst kinds use. Override per-kind via `analystKinds[i].cost.models`. */\n model: string\n /** Optional total budget across all kinds for one run. Substrate enforces via `BudgetGuard`. */\n budgetUsd?: number\n /** Backend hint for the AxAIService factory — same shape every kind uses. */\n backend?: {\n name?: 'openai' | 'router'\n apiKey?: string\n baseUrl?: string\n }\n}\n\nexport interface AutoApplyPolicy {\n knowledge?: {\n enabled: boolean\n confidenceThreshold?: number\n mode?: 'write' | 'open-pr'\n }\n improvement?: {\n enabled: boolean\n confidenceThreshold?: number\n mode?: 'write' | 'open-pr'\n }\n}\n\n// ── factory + validation ─────────────────────────────────────────────\n\nexport class AgentManifestError extends Error {\n constructor(\n message: string,\n public readonly agentId: string,\n public readonly issues: ReadonlyArray<unknown> = [],\n ) {\n super(message)\n this.name = 'AgentManifestError'\n }\n}\n\n/**\n * Construct a validated agent manifest. Throws `AgentManifestError`\n * if any required surface is missing on disk.\n *\n * Generics: pass your persona / output types if you want narrowed\n * `runtime.act` signatures:\n * `defineAgent<TaxPersona, TaxRunOutput>({ ... })`\n *\n * Most callers don't need the generics — the substrate operates on\n * `unknown` payloads internally and the manifest's `score` /\n * `runtime.act` see the typed shapes via TypeScript inference at\n * the call site.\n */\nexport function defineAgent<TPersona = unknown, TRunOutput = unknown>(\n manifest: AgentManifest<TPersona, TRunOutput>,\n): AgentManifest<TPersona, TRunOutput> {\n if (!manifest.id || manifest.id.trim().length === 0) {\n throw new AgentManifestError('defineAgent: `id` is required', manifest.id ?? '')\n }\n if (!manifest.repoRoot || manifest.repoRoot.trim().length === 0) {\n throw new AgentManifestError('defineAgent: `repoRoot` is required', manifest.id)\n }\n const issues = validateSurfaces(manifest.surfaces, manifest.repoRoot)\n if (issues.length > 0) {\n throw new AgentManifestError(\n renderSurfaceIssues(issues, manifest.repoRoot),\n manifest.id,\n issues,\n )\n }\n // Lightweight rubric sanity: weights sum to ~1.0 (no hard requirement —\n // the substrate normalizes — but flag wildly miscalibrated weights).\n const total = manifest.rubric.dimensions.reduce((acc, d) => acc + d.weight, 0)\n if (manifest.rubric.dimensions.length > 0 && (total < 0.5 || total > 1.5)) {\n throw new AgentManifestError(\n `defineAgent(${manifest.id}): rubric dimension weights sum to ${total.toFixed(3)} — should be ~1.0`,\n manifest.id,\n )\n }\n return manifest\n}\n","/**\n * Substrate-default `ImprovementAdapter` — surfaces-driven, LLM-drafted\n * patches, optional auto-apply or PR-open.\n *\n * This is the one ImprovementAdapter every vertical agent uses. The\n * substrate parses each finding's `subject` via\n * `parseFindingSubject` (agent-eval), resolves it to a real file path\n * via the agent's `AgentSurfaces`, reads the current content, and asks\n * an LLM to draft a unified-diff patch given the finding + current\n * content + per-kind editing-discipline rules.\n *\n * Auto-apply gates on the source-finding's confidence and the\n * autoApply.improvement policy. Two modes:\n * `write` — apply the patch in-place via `git apply -p0`. Operator\n * reviews via `git diff`.\n * `open-pr` — write to a branch, commit, push, open a PR via `gh`.\n * Operator reviews via the PR UI.\n *\n * Fail-loud rules:\n * - Findings whose subject doesn't parse → counted in `errors`.\n * - Findings whose subject targets an undeclared surface → counted in\n * `errors` with the offending kind in the message.\n * - Findings whose target path doesn't exist AND the kind isn't a\n * create-new variant (`new-tool`, `knowledge.wiki`) → counted in\n * `errors` with the resolved path in the message.\n * - LLM drafts that fail JSON-schema validation → counted in\n * `errors` with the schema issue.\n *\n * No silent skips. Every dropped finding has a recorded reason the\n * loop's report surfaces.\n */\n\nimport { spawnSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport type { AnalystFinding, FindingSubject } from '@tangle-network/agent-eval'\nimport { parseFindingSubject } from '@tangle-network/agent-eval/analyst'\nimport type { ImprovementAdapter } from '../analyst-loop/types'\nimport type { AgentSurfaces, ResolvedSurface } from './surfaces'\nimport { resolveSubjectPath } from './surfaces'\n\n// ── proposal shape ───────────────────────────────────────────────────\n\nexport interface SurfaceImprovementEdit {\n /** Stable id derived from the source finding so re-proposals are idempotent. */\n id: string\n /** The finding that produced this edit — for revert + audit trail. */\n sourceFindingId: string\n /** Parsed subject; included so the apply step doesn't re-parse. */\n subject: FindingSubject\n /** Resolved on-disk target. */\n target: ResolvedSurface\n /** SHA-256 of the current file content the patch was drafted against. */\n baseSha256: string\n /** Unified-diff patch the LLM drafted (relative to `target.absolutePath`). */\n patch: string\n /** One-line summary the operator sees in the report / PR title. */\n summary: string\n /** Multi-line rationale for the PR body — finding context + LLM reasoning. */\n rationale: string\n /** Carry-forward from the finding so the apply gate can check the threshold. */\n confidence: number\n /** Carry-forward severity for prioritization. */\n severity: AnalystFinding['severity']\n}\n\nexport interface CreateSurfaceImprovementAdapterOpts {\n surfaces: AgentSurfaces\n repoRoot: string\n /**\n * LLM-draft callback. Given a finding + current file content + the\n * resolved target, returns a unified-diff patch + summary + rationale.\n *\n * Required — the substrate doesn't ship a hardcoded prompt; the agent\n * author picks the model (Haiku for cheap routine drafts, Sonnet for\n * substantive prompt rewrites, etc.) via this callback.\n */\n draftPatch: (input: DraftPatchInput) => Promise<DraftPatchOutput>\n /**\n * Apply mode:\n * `write` — `git apply` in-place; operator reviews via `git diff`\n * `open-pr` — branch + commit + push + `gh pr create`\n * `none` — never apply; collect proposals for the report only\n *\n * The `apply` method honours this even when the loop calls it; the\n * effective behaviour is also gated on the per-finding confidence\n * threshold via `runAnalystLoop`'s `autoApply` policy.\n */\n mode?: 'write' | 'open-pr' | 'none'\n /** When `mode === 'open-pr'`, the base branch new PRs target. Default: `main`. */\n baseBranch?: string\n /** Required for `mode === 'open-pr'` — the GH owner/repo (`tangle-network/tax-agent`). */\n ghRepo?: string\n /**\n * When the resolved target doesn't exist, allow the substrate to\n * CREATE the file (for `knowledge.wiki`, `new-tool` subjects). Default\n * true for those kinds, false for `system-prompt` / `rubric` / etc.\n * (named sections that don't exist are a contract violation, not a\n * scaffolding opportunity).\n */\n allowCreateForKinds?: ReadonlyArray<FindingSubject['kind']>\n}\n\nexport interface DraftPatchInput {\n finding: AnalystFinding\n subject: FindingSubject\n target: ResolvedSurface\n /** Current file content (empty string when `intent === 'create-new'`). */\n currentContent: string\n}\n\nexport interface DraftPatchOutput {\n /** Unified diff against the current file content. Empty string skips this finding. */\n patch: string\n /** One-line summary for the operator. */\n summary: string\n /** Multi-line rationale for the PR body. */\n rationale: string\n}\n\n// ── factory ──────────────────────────────────────────────────────────\n\nconst DEFAULT_CREATE_KINDS: ReadonlyArray<FindingSubject['kind']> = [\n 'knowledge.wiki',\n 'knowledge.claim',\n 'knowledge.raw',\n 'new-tool',\n]\n\nexport function createSurfaceImprovementAdapter(\n opts: CreateSurfaceImprovementAdapterOpts,\n): ImprovementAdapter<SurfaceImprovementEdit> {\n const mode = opts.mode ?? 'none'\n const allowCreate = opts.allowCreateForKinds ?? DEFAULT_CREATE_KINDS\n\n return {\n async proposeFromFindings(findings) {\n const edits: SurfaceImprovementEdit[] = []\n const errors: Array<{ findingId: string; subject: string; message: string }> = []\n let skipped = 0\n\n for (const f of findings) {\n const subject = parseFindingSubject(f.subject)\n if (subject === null) {\n if (f.subject !== undefined) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject,\n message: 'subject does not parse against the finding-subject grammar',\n })\n } else {\n // Subject-less findings are descriptive, not actionable —\n // legitimate; count in `skipped` not `errors`.\n skipped += 1\n }\n continue\n }\n\n // `cluster` findings (failure-mode) are evidence, not mutations.\n if (subject.kind === 'cluster') {\n skipped += 1\n continue\n }\n\n // `agent-knowledge:*` findings flow to the KnowledgeAdapter;\n // the ImprovementAdapter skips them so subjects don't double-route.\n if (subject.kind.startsWith('knowledge.')) {\n skipped += 1\n continue\n }\n\n const target = resolveSubjectPath(subject, opts.surfaces, opts.repoRoot)\n if (target === null) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject ?? '',\n message: `subject kind \"${subject.kind}\" targets an undeclared surface; declare it in AgentSurfaces or stop emitting this subject`,\n })\n continue\n }\n\n if (target.intent === 'create-new' && !allowCreate.includes(subject.kind)) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject ?? '',\n message: `target ${target.repoRelativePath} does not exist; the kind \"${subject.kind}\" requires an existing target (analyst named a section that isn't in the codebase)`,\n })\n continue\n }\n\n const currentContent = target.exists ? readFileSync(target.absolutePath, 'utf-8') : ''\n\n let draft: DraftPatchOutput\n try {\n draft = await opts.draftPatch({ finding: f, subject, target, currentContent })\n } catch (err) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject ?? '',\n message: `draftPatch threw: ${err instanceof Error ? err.message : String(err)}`,\n })\n continue\n }\n\n if (draft.patch.trim().length === 0) {\n skipped += 1\n continue\n }\n\n edits.push({\n id: `imp-${f.finding_id}`,\n sourceFindingId: f.finding_id,\n subject,\n target,\n baseSha256: sha256(currentContent),\n patch: draft.patch,\n summary: draft.summary,\n rationale: draft.rationale,\n confidence: f.confidence,\n severity: f.severity,\n })\n }\n\n return { edits, skipped, errors }\n },\n\n async apply(edits) {\n const applied: string[] = []\n const warnings: string[] = []\n\n if (mode === 'none') {\n warnings.push(\n 'createSurfaceImprovementAdapter: mode=none; no edits applied — adjust manifest.autoApply.improvement.mode',\n )\n return { applied, warnings }\n }\n\n if (mode === 'open-pr' && !opts.ghRepo) {\n warnings.push(\n 'createSurfaceImprovementAdapter: mode=open-pr requires `ghRepo`; falling back to no-op',\n )\n return { applied, warnings }\n }\n\n for (const edit of edits) {\n // Race-detection: confirm the file content hasn't moved since the\n // patch was drafted. A diff applied against drifted content is a\n // recipe for silent corruption.\n const current = edit.target.exists ? readFileSync(edit.target.absolutePath, 'utf-8') : ''\n if (sha256(current) !== edit.baseSha256) {\n warnings.push(\n `${edit.target.repoRelativePath}: base SHA mismatch; file changed after draft. Skipping.`,\n )\n continue\n }\n\n const ok = applyPatchInPlace(edit, opts.repoRoot)\n if (!ok) {\n warnings.push(`${edit.target.repoRelativePath}: git apply failed`)\n continue\n }\n applied.push(edit.target.repoRelativePath)\n }\n\n if (mode === 'open-pr' && applied.length > 0 && opts.ghRepo) {\n const prUrl = openPullRequest(\n applied,\n edits.filter((e) => applied.includes(e.target.repoRelativePath)),\n opts.repoRoot,\n opts.ghRepo,\n opts.baseBranch ?? 'main',\n )\n if (prUrl) warnings.push(`opened PR: ${prUrl}`)\n else warnings.push('PR creation failed; edits are committed to a local branch only')\n }\n\n return { applied, warnings }\n },\n }\n}\n\n// ── apply helpers ────────────────────────────────────────────────────\n\nfunction applyPatchInPlace(edit: SurfaceImprovementEdit, repoRoot: string): boolean {\n const result = spawnSync('git', ['apply', '--whitespace=fix', '-p0', '-'], {\n cwd: repoRoot,\n input: edit.patch,\n encoding: 'utf-8',\n })\n return result.status === 0\n}\n\nfunction openPullRequest(\n paths: ReadonlyArray<string>,\n edits: ReadonlyArray<SurfaceImprovementEdit>,\n repoRoot: string,\n ghRepo: string,\n baseBranch: string,\n): string | null {\n const branch = `analyst-loop/${Date.now()}-${edits[0]?.sourceFindingId.slice(0, 12) ?? 'edits'}`\n // Create branch, stage, commit\n const checkout = spawnSync('git', ['checkout', '-b', branch], { cwd: repoRoot })\n if (checkout.status !== 0) return null\n const add = spawnSync('git', ['add', ...paths], { cwd: repoRoot })\n if (add.status !== 0) return null\n const title = `analyst-loop: ${edits[0]?.summary ?? `${edits.length} improvement edits`}`\n const body = [\n `Automated analyst-loop edits — review carefully before merge.`,\n '',\n `Source findings:`,\n ...edits.map(\n (e) =>\n ` - ${e.sourceFindingId} (confidence ${e.confidence.toFixed(2)}, severity ${e.severity})`,\n ),\n '',\n 'Rationales:',\n ...edits.map((e) => `\\n## ${e.target.repoRelativePath}\\n\\n${e.rationale}`),\n ].join('\\n')\n const commit = spawnSync('git', ['commit', '-m', title, '-m', body], { cwd: repoRoot })\n if (commit.status !== 0) return null\n const push = spawnSync('git', ['push', '-u', 'origin', branch], { cwd: repoRoot })\n if (push.status !== 0) return null\n const pr = spawnSync(\n 'gh',\n [\n 'pr',\n 'create',\n '--repo',\n ghRepo,\n '--title',\n title,\n '--body',\n body,\n '--base',\n baseBranch,\n '--head',\n branch,\n ],\n { cwd: repoRoot, encoding: 'utf-8' },\n )\n if (pr.status !== 0) return null\n return pr.stdout.trim()\n}\n\nfunction sha256(s: string): string {\n // node:crypto is dynamic-imported lazily so the adapter can be tested in\n // environments without crypto (browser tests, mocked envs).\n const crypto = require('node:crypto') as typeof import('node:crypto')\n return crypto.createHash('sha256').update(s, 'utf-8').digest('hex')\n}\n","/**\n * Substrate-default `KnowledgeAdapter` — wraps agent-knowledge's\n * `proposeFromFindings` + `applyKnowledgeWriteBlocks` with substrate\n * defaults (auto-lint after apply, source linkage via finding id).\n *\n * Every agent that ships a `.agent-knowledge/` tree uses this adapter\n * unmodified. Per-agent customization happens at the manifest level\n * (`autoApply.knowledge.confidenceThreshold`, etc.), not by writing a\n * new adapter.\n *\n * Lint discipline: after each apply we run agent-knowledge's\n * `lintKnowledgeIndex` to catch broken links / circular claims /\n * duplicate pages introduced by the new writes. Findings that fail the\n * post-apply lint are recorded in `warnings`; the apply itself is not\n * rolled back (lint failures are soft — humans review the wiki state).\n */\n\nimport type { AnalystFinding } from '@tangle-network/agent-eval'\nimport type { KnowledgeAdapter } from '../analyst-loop/types'\n\nexport interface CreateSurfaceKnowledgeAdapterOpts {\n /** `.agent-knowledge/` root (absolute path the substrate writes blocks against). */\n knowledgeRoot: string\n}\n\n/**\n * Build the adapter. We accept the agent-knowledge functions as DI so\n * the substrate stays decoupled from a specific agent-knowledge\n * version — the agent author imports them in their manifest module\n * and hands them to the factory.\n *\n * `proposeFromFindings(findings)` returns\n * `{ proposals: KnowledgeProposal[]; skipped: number; errors: ... }`.\n *\n * `applyKnowledgeWriteBlocks(root, content)` returns\n * `{ written: string[]; warnings: string[] }`.\n *\n * `lintKnowledgeIndex(index)` (optional) returns `KnowledgeLintFinding[]`.\n */\nexport interface KnowledgeAdapterDeps<TProposal> {\n proposeFromFindings: (findings: ReadonlyArray<AnalystFinding>) => {\n proposals: TProposal[]\n skipped: number\n errors: Array<{ findingId: string; subject: string; message: string }>\n }\n applyKnowledgeWriteBlocks: (\n root: string,\n proposalText: string,\n ) => Promise<{ written: string[]; warnings: string[] }>\n /**\n * Optional post-apply lint hook. The substrate runs it after each\n * batch of writes; failures land in `warnings` (the apply is not\n * rolled back — lint signals drift to review, not block).\n */\n lintAfterApply?: (root: string) => Promise<ReadonlyArray<string>>\n}\n\nexport function createSurfaceKnowledgeAdapter<TProposal>(\n opts: CreateSurfaceKnowledgeAdapterOpts,\n deps: KnowledgeAdapterDeps<TProposal>,\n): KnowledgeAdapter<TProposal> {\n return {\n proposeFromFindings(findings) {\n const batch = deps.proposeFromFindings(findings)\n return {\n proposals: batch.proposals,\n skipped: batch.skipped,\n errors: batch.errors,\n }\n },\n async apply(proposals) {\n const written: string[] = []\n const warnings: string[] = []\n for (const p of proposals) {\n const proposalText = renderProposalAsWriteBlock(p)\n if (proposalText === null) {\n warnings.push(\n `proposal has no writeBlocks/content; skipping (sourceFindingId=${getSourceFindingId(p)})`,\n )\n continue\n }\n try {\n const r = await deps.applyKnowledgeWriteBlocks(opts.knowledgeRoot, proposalText)\n written.push(...r.written)\n warnings.push(...r.warnings)\n } catch (err) {\n warnings.push(\n `applyKnowledgeWriteBlocks failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n if (deps.lintAfterApply && written.length > 0) {\n try {\n const lintIssues = await deps.lintAfterApply(opts.knowledgeRoot)\n for (const issue of lintIssues) warnings.push(`lint: ${issue}`)\n } catch (err) {\n warnings.push(\n `lintAfterApply failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n return { written, warnings }\n },\n }\n}\n\n/**\n * Pluck the canonical write-block text from a proposal regardless of\n * which exact agent-knowledge version produced it. We accept either:\n * - `{ writeBlocks: Array<{ path, content }> }` — the typed shape\n * 1.3.0+ emits\n * - `{ proposalText: string }` — legacy single-block shape\n * - `{ content: string }` — minimal raw form\n *\n * Returns `null` when nothing parseable is present (warned upstream).\n */\nfunction renderProposalAsWriteBlock(p: unknown): string | null {\n if (!p || typeof p !== 'object') return null\n const obj = p as Record<string, unknown>\n if (Array.isArray(obj.writeBlocks)) {\n const blocks = obj.writeBlocks as Array<{ path?: string; content?: string }>\n if (blocks.length === 0) return null\n return blocks\n .map((b) => (typeof b.content === 'string' ? b.content : ''))\n .filter((s) => s.length > 0)\n .join('\\n\\n')\n }\n if (typeof obj.proposalText === 'string') return obj.proposalText\n if (typeof obj.content === 'string') return obj.content\n return null\n}\n\nfunction getSourceFindingId(p: unknown): string {\n if (!p || typeof p !== 'object') return '<unknown>'\n const obj = p as Record<string, unknown>\n if (typeof obj.sourceFindingId === 'string') return obj.sourceFindingId\n if (typeof obj.id === 'string') return obj.id\n return '<unknown>'\n}\n","/**\n * `OutcomeMeasurement` — the missing metric that turns the analyst\n * loop from \"observability\" into \"self-improvement\".\n *\n * Without this hook, the loop reports process counts (`findings: 42`,\n * `applied: 7`) and never proves the applied edits actually improved\n * anything. With this hook, the substrate re-runs the cohort against\n * the same personas after each apply pass and reports a composite\n * score delta. A negative delta is the substrate's strongest signal\n * to either roll back or surface for review.\n *\n * Wiring is intentionally simple: pass the manifest + the `runAgentEval`\n * function and a list of `personaIds` to re-run. The wrapper:\n * 1. Captures the baseline composite from the just-finished run.\n * 2. After `runAnalystLoop` returns, re-invokes `runAgentEval` against\n * the same persona slice.\n * 3. Computes the delta and appends to `loop-report.json`.\n * 4. If `rollbackOnRegression` and delta < 0, reverts applied edits.\n */\n\nimport type { RunAnalystLoopResult } from '../analyst-loop/types'\n\nexport interface OutcomeMeasurement {\n /** Baseline composite before applies — captured from the most-recent eval run. */\n baselineComposite: number\n /** Composite after re-running the cohort with applied edits. */\n afterComposite: number\n /** `afterComposite - baselineComposite`. Positive = the loop improved the agent. */\n delta: number\n /** Per-persona deltas for finer-grained review. */\n perPersona: ReadonlyArray<{ personaId: string; before: number; after: number; delta: number }>\n /** When the substrate rolled back applies due to regression, the paths reverted. */\n rolledBackPaths: ReadonlyArray<string>\n}\n\nexport interface OutcomeMeasurementOpts {\n /** Composite scores from the run that produced the findings. */\n baseline: ReadonlyArray<{ personaId: string; composite: number }>\n /**\n * Re-run callback — the substrate invokes this after applies. The\n * agent author provides their `runAgentEval`-equivalent so the\n * substrate can ask \"score this persona slice now.\"\n *\n * The callback SHOULD reuse the same cohort + judges + variant as\n * the baseline run; only the agent's mutable surfaces have changed.\n */\n reRunCohort: (\n personaIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<{ personaId: string; composite: number }>>\n /** When `true`, applied edits are reverted on negative delta. Default `false`. */\n rollbackOnRegression?: boolean\n /** Callback to revert a list of paths (typically `git checkout HEAD --`). */\n revert?: (paths: ReadonlyArray<string>) => Promise<void>\n}\n\n/**\n * Run `runAnalystLoop` and stamp an `OutcomeMeasurement` onto the\n * result. The substrate calls this after each canonical eval; the\n * delta lands in `loop-report.json` for cross-run trend analysis.\n *\n * The function returns the original `RunAnalystLoopResult` enriched\n * with `outcome` so callers stay backwards-compatible (the field is\n * optional on the type; missing means no measurement was wired).\n */\nexport async function measureOutcome<TProposal, TEdit>(\n result: RunAnalystLoopResult<TProposal, TEdit>,\n opts: OutcomeMeasurementOpts,\n): Promise<RunAnalystLoopResult<TProposal, TEdit> & { outcome: OutcomeMeasurement }> {\n const applied = result.knowledge?.applied ?? []\n const improvementsApplied = result.improvement?.applied ?? []\n const allApplied = [...applied, ...improvementsApplied]\n\n // No applies → no outcome to measure. Return a zero-delta to keep the\n // shape stable for consumers; baseline / after equal.\n if (allApplied.length === 0) {\n return {\n ...result,\n outcome: {\n baselineComposite: meanComposite(opts.baseline),\n afterComposite: meanComposite(opts.baseline),\n delta: 0,\n perPersona: opts.baseline.map((b) => ({\n personaId: b.personaId,\n before: b.composite,\n after: b.composite,\n delta: 0,\n })),\n rolledBackPaths: [],\n },\n }\n }\n\n const personaIds = opts.baseline.map((b) => b.personaId)\n const after = await opts.reRunCohort(personaIds)\n const afterByPersona = new Map(after.map((r) => [r.personaId, r.composite]))\n\n const perPersona = opts.baseline.map((b) => {\n const a = afterByPersona.get(b.personaId) ?? b.composite\n return { personaId: b.personaId, before: b.composite, after: a, delta: a - b.composite }\n })\n const baselineComposite = meanComposite(opts.baseline)\n const afterComposite = meanComposite(after)\n const delta = afterComposite - baselineComposite\n\n let rolledBackPaths: string[] = []\n if (delta < 0 && opts.rollbackOnRegression && opts.revert) {\n await opts.revert(allApplied)\n rolledBackPaths = [...allApplied]\n }\n\n return {\n ...result,\n outcome: {\n baselineComposite,\n afterComposite,\n delta,\n perPersona,\n rolledBackPaths,\n },\n }\n}\n\nfunction meanComposite(rows: ReadonlyArray<{ composite: number }>): number {\n if (rows.length === 0) return 0\n return rows.reduce((acc, r) => acc + r.composite, 0) / rows.length\n}\n","/**\n * Sandbox bridge for `AgentRuntime.act` — prod-faithful eval execution.\n *\n * The point of this adapter is parity: the eval substrate must run the agent\n * through the SAME profile the production chat turn uses, or scorecard numbers\n * grade a profile that never ships. `createSandboxAct` composes the production\n * profile via {@link composeProductionAgentProfile}, boots a sandbox with it\n * through the loop kernel's own {@link createSandboxForSpec}, streams the\n * `streamPrompt` events mapped to the `RuntimeStreamEvent` vocabulary, and\n * resolves the `OutputAdapter`-parsed output for rubric scoring — satisfying\n * the `act` streaming contract with one code path shared by chat and eval.\n *\n * Agents with a bespoke streaming chat turn should wire THAT into `act`\n * directly (the contract is designed for it); this adapter is the default for\n * agents whose turn is a plain prod-profile sandbox dispatch — notably the\n * agents agent-builder generates.\n */\n\nimport type { AgentProfile, SandboxEvent } from '@tangle-network/sandbox'\nimport type { ComposeProductionAgentProfileOptions } from '../mcp/delegation-profile'\nimport { composeProductionAgentProfile } from '../mcp/delegation-profile'\nimport type { AgentRunSpec, OutputAdapter, SandboxClient } from '../runtime'\nimport { createSandboxForSpec, mapSandboxEvent } from '../runtime'\nimport type { RuntimeStreamEvent } from '../types'\nimport type { AgentRunContext, AgentRunInvocation } from './define-agent'\n\nexport interface CreateSandboxActOptions<TPersona, TRunOutput> {\n /** Canonical agent profile — the same one the prod chat turn composes from. */\n baseProfile: AgentProfile\n /** Sandbox client used to boot the per-run sandbox. */\n sandboxClient: SandboxClient\n /** Persona → prompt. Pure; the eval cell's input. */\n buildPrompt: (persona: TPersona) => string\n /** Sandbox event stream → typed output the rubric scores. */\n output: OutputAdapter<TRunOutput>\n /**\n * Per-persona composition overrides (workspace-augmented system prompt,\n * extra file mounts, sandbox key). Merged into\n * {@link composeProductionAgentProfile}; `env` here is overridden by the\n * top-level `env` option when both are set.\n */\n compose?: (persona: TPersona) => ComposeProductionAgentProfileOptions\n /** Sandbox-SDK overrides forwarded to `createSandboxForSpec`. */\n sandboxOverrides?: AgentRunSpec<unknown>['sandboxOverrides']\n /** Stable run name surfaced in mapped `llm_call` events. */\n name?: string\n /** Override the `SandboxEvent → RuntimeStreamEvent` mapper. */\n mapEvent?: (\n event: SandboxEvent,\n opts: { agentRunName?: string },\n ) => RuntimeStreamEvent | undefined\n /** Environment source for delegation-MCP composition. Defaults to `process.env`. */\n env?: Record<string, string | undefined>\n}\n\n/**\n * Build an `AgentRuntime.act` implementation backed by a single prod-profile\n * sandbox run. The returned function honours the `act` contract: it returns\n * synchronously with a live `events` iterator and an `output` promise that\n * resolves only after the iterator drains.\n */\nexport function createSandboxAct<TPersona, TRunOutput>(\n options: CreateSandboxActOptions<TPersona, TRunOutput>,\n): (persona: TPersona, ctx: AgentRunContext) => AgentRunInvocation<TRunOutput> {\n const mapEvent = options.mapEvent ?? mapSandboxEvent\n\n return (persona: TPersona, ctx: AgentRunContext): AgentRunInvocation<TRunOutput> => {\n const profile = composeProductionAgentProfile(options.baseProfile, {\n ...(options.compose?.(persona) ?? {}),\n ...(options.env ? { env: options.env } : {}),\n })\n const agentRunName = options.name ?? profile.name ?? 'agent'\n const message = options.buildPrompt(persona)\n const signal = ctx.signal ?? new AbortController().signal\n\n const raw: SandboxEvent[] = []\n let settle!: (value: TRunOutput) => void\n let fail!: (err: unknown) => void\n const output = new Promise<TRunOutput>((resolve, reject) => {\n settle = resolve\n fail = reject\n })\n // The output promise rejects when the stream errors; if the caller ignores\n // `output` (chat UX) the rejection is still observed by the events iterator\n // throwing. Attach a no-op catch so an ignored rejection is never \"unhandled\".\n output.catch(() => {})\n\n const spec: AgentRunSpec<unknown> = {\n profile,\n taskToPrompt: () => message,\n name: agentRunName,\n ...(options.sandboxOverrides ? { sandboxOverrides: options.sandboxOverrides } : {}),\n }\n\n async function* events(): AsyncIterable<RuntimeStreamEvent> {\n try {\n const box = await createSandboxForSpec(options.sandboxClient, spec, signal)\n for await (const event of box.streamPrompt(message, { signal })) {\n raw.push(event)\n const mapped = mapEvent(event, { agentRunName })\n if (mapped) yield mapped\n }\n settle(options.output.parse(raw))\n } catch (err) {\n fail(err)\n throw err\n }\n }\n\n return { events: events(), output }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/agent/surfaces.ts","../src/agent/define-agent.ts","../src/agent/improvement-adapter.ts","../src/agent/knowledge-adapter.ts","../src/agent/outcome.ts","../src/agent/sandbox-act.ts"],"sourcesContent":["/**\n * `AgentSurfaces` — declarative map of the mutable file/directory paths\n * the self-improvement loop can edit on behalf of an agent.\n *\n * The substrate uses this map to resolve every parsed `FindingSubject`\n * (from agent-eval) to a real on-disk path. No per-vertical glue;\n * no fabricated paths; no silent `existsSync(...)` skips that hide\n * misconfiguration from the operator.\n *\n * Surfaces are validated at `defineAgent` time — missing paths fail\n * loud with a list of every offender. A surface that's not needed\n * (e.g. an agent with no RAG corpora) is simply omitted; the loop\n * refuses to route those subjects rather than fabricating a target.\n */\n\nimport { existsSync } from 'node:fs'\nimport { isAbsolute, join } from 'node:path'\nimport type { FindingSubject } from '@tangle-network/agent-eval'\n\n/**\n * Surface declarations. Every path is repo-relative (or absolute) at\n * `defineAgent` time. At resolution time, paths are joined against the\n * agent's `repoRoot`.\n *\n * `systemPrompt`, `tools`, `personas` are DIRECTORIES; the loop appends\n * `<section>.md`, `<tool>/README.md`, `<persona-id>.yaml` etc.\n * `rubric`, `outputSchema` are SINGLE FILES; the loop edits them in\n * place.\n *\n * `knowledge` is the agent-knowledge root (typically `.agent-knowledge`);\n * `applyKnowledgeWriteBlocks` writes pages relative to it.\n *\n * Optional surfaces (`scaffolding`, `memory`, `rag`, `outputSchema`)\n * can be omitted — the loop will reject findings targeting them with a\n * clear log message instead of fabricating a path.\n */\nexport interface AgentSurfaces {\n /** Directory containing one markdown file per system-prompt section. */\n systemPrompt: string\n /** Directory containing one subdir per tool (`<tool>/README.md`). */\n tools: string\n /** Single file (TypeScript module) defining the rubric weights + dimensions. */\n rubric: string\n /** Knowledge-base root; typically `.agent-knowledge`. */\n knowledge: string\n /** Directory containing one YAML/JSON file per persona. */\n personas: string\n /** Optional: directory containing scaffolding rules (precondition checks, retry policies). */\n scaffolding?: string\n /** Optional: memory store path (JSONL / SQLite / DB). */\n memory?: string\n /** Optional: directory containing RAG corpora (`<corpus>/<doc-id>.md`). */\n rag?: string\n /** Optional: single file defining the output schema (Zod / JSON Schema). */\n outputSchema?: string\n}\n\nexport interface ResolvedSurface {\n /** Absolute filesystem path the operator can `cat` / `vim`. */\n absolutePath: string\n /** Repo-relative path for PR descriptions, diffs, audit logs. */\n repoRelativePath: string\n /** Whether the path currently exists on disk. */\n exists: boolean\n /** The substrate's intent: edit an existing file or create a new one. */\n intent: 'edit-existing' | 'create-new'\n}\n\n/**\n * Resolve a parsed `FindingSubject` to the file path the substrate\n * should edit (or create) on disk.\n *\n * Returns `null` when:\n * - the subject targets a surface the agent didn't declare\n * (e.g. `rag:*` when `surfaces.rag` is undefined), OR\n * - the subject is a `cluster` (failure-mode emits these as evidence,\n * not actionable mutations — they don't route to a file).\n *\n * Returns a `ResolvedSurface` with `intent: 'create-new'` when the\n * subject names a path that doesn't yet exist (e.g. a new wiki page).\n * The caller chooses whether to honour the create — for tightly-managed\n * surfaces like `systemPrompt` it's usually a contract violation\n * (the analyst named a section that doesn't exist); for `knowledge`\n * it's the whole point.\n */\nexport function resolveSubjectPath(\n subject: FindingSubject,\n surfaces: AgentSurfaces,\n repoRoot: string,\n): ResolvedSurface | null {\n const candidates = candidatePathsForSubject(subject, surfaces)\n if (candidates.length === 0) return null\n\n // Probe candidates in order, preferring the first one that exists on disk.\n // Lets the substrate accept both the flat `<section>.md` convention and\n // the skill-dir `<section>/SKILL.md` convention without forcing one layout.\n // When none exists, fall back to the first candidate (canonical create-new).\n for (const rel of candidates) {\n const abs = isAbsolute(rel) ? rel : join(repoRoot, rel)\n if (existsSync(abs)) {\n return { absolutePath: abs, repoRelativePath: rel, exists: true, intent: 'edit-existing' }\n }\n }\n const fallback = candidates[0]!\n const fallbackAbs = isAbsolute(fallback) ? fallback : join(repoRoot, fallback)\n return {\n absolutePath: fallbackAbs,\n repoRelativePath: fallback,\n exists: false,\n intent: 'create-new',\n }\n}\n\nfunction candidatePathsForSubject(\n subject: FindingSubject,\n surfaces: AgentSurfaces,\n): ReadonlyArray<string> {\n switch (subject.kind) {\n case 'knowledge.wiki':\n case 'knowledge.stale':\n return [join(surfaces.knowledge, `${subject.slug}.md`)]\n case 'knowledge.claim':\n // Claims land in a per-topic claims directory under the knowledge root.\n return [join(surfaces.knowledge, 'claims', `${slugify(subject.topic)}.md`)]\n case 'knowledge.raw':\n return [join(surfaces.knowledge, 'raw', `${subject.sourceId}.md`)]\n case 'system-prompt': {\n const slug = slugify(subject.section)\n // Prefer flat layout for create-new (canonical); probe skill-dir layout\n // in case the existing repo (tax/legal/gtm/creative) uses\n // `<section>/SKILL.md` already.\n return [\n join(surfaces.systemPrompt, `${slug}.md`),\n join(surfaces.systemPrompt, slug, 'SKILL.md'),\n join(surfaces.systemPrompt, slug, 'index.md'),\n ]\n }\n case 'tool-doc':\n if (subject.aspect) {\n return [join(surfaces.tools, subject.tool, `${slugify(subject.aspect)}.md`)]\n }\n // tool-doc default: `<tool>/README.md`; also probe `<tool>.md` for flat\n // tool-list repos.\n return [\n join(surfaces.tools, subject.tool, 'README.md'),\n join(surfaces.tools, `${subject.tool}.md`),\n ]\n case 'new-tool':\n return [join(surfaces.tools, subject.name, 'README.md')]\n case 'rag':\n if (!surfaces.rag) return []\n return [join(surfaces.rag, subject.corpus, `${subject.docId}.md`)]\n case 'memory':\n if (!surfaces.memory) return []\n return [join(surfaces.memory, `${slugify(subject.key)}.json`)]\n case 'scaffolding':\n if (!surfaces.scaffolding) return []\n return [join(surfaces.scaffolding, `${slugify(subject.concern)}.md`)]\n case 'output-schema':\n if (!surfaces.outputSchema) return []\n return [surfaces.outputSchema]\n case 'websearch.outdated':\n case 'prior-run-summary':\n // Stale signals don't map to a single file — handled by the knowledge\n // adapter as `agent-knowledge:stale:*` after operator review.\n return []\n case 'cluster':\n // failure-mode cluster labels are evidence, not mutations.\n return []\n }\n}\n\nfunction slugify(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 200) || 'untitled'\n )\n}\n\n/**\n * Validate that every declared surface exists on disk under `repoRoot`.\n *\n * Returns an array of `SurfaceValidationIssue` — empty when all required\n * surfaces resolve. `defineAgent` throws with the issues rendered, so\n * a misconfigured manifest fails at startup (not at the first finding\n * the loop produces 20 minutes later).\n */\nexport interface SurfaceValidationIssue {\n surface: keyof AgentSurfaces\n path: string\n reason: 'missing' | 'not-directory' | 'not-file'\n}\n\nexport function validateSurfaces(\n surfaces: AgentSurfaces,\n repoRoot: string,\n): ReadonlyArray<SurfaceValidationIssue> {\n const issues: SurfaceValidationIssue[] = []\n const dirSurfaces: ReadonlyArray<keyof AgentSurfaces> = [\n 'systemPrompt',\n 'tools',\n 'personas',\n 'knowledge',\n ]\n const fileSurfaces: ReadonlyArray<keyof AgentSurfaces> = ['rubric']\n const optionalDirSurfaces: ReadonlyArray<keyof AgentSurfaces> = ['scaffolding', 'memory', 'rag']\n const optionalFileSurfaces: ReadonlyArray<keyof AgentSurfaces> = ['outputSchema']\n\n for (const key of dirSurfaces) {\n const p = surfaces[key] as string | undefined\n if (!p) {\n issues.push({ surface: key, path: '', reason: 'missing' })\n continue\n }\n const abs = isAbsolute(p) ? p : join(repoRoot, p)\n if (!existsSync(abs)) {\n issues.push({ surface: key, path: p, reason: 'missing' })\n }\n }\n for (const key of fileSurfaces) {\n const p = surfaces[key] as string | undefined\n if (!p) {\n issues.push({ surface: key, path: '', reason: 'missing' })\n continue\n }\n const abs = isAbsolute(p) ? p : join(repoRoot, p)\n if (!existsSync(abs)) {\n issues.push({ surface: key, path: p, reason: 'missing' })\n }\n }\n for (const key of [...optionalDirSurfaces, ...optionalFileSurfaces]) {\n const p = surfaces[key] as string | undefined\n if (p === undefined) continue\n const abs = isAbsolute(p) ? p : join(repoRoot, p)\n if (!existsSync(abs)) {\n issues.push({ surface: key, path: p, reason: 'missing' })\n }\n }\n return issues\n}\n\nexport function renderSurfaceIssues(\n issues: ReadonlyArray<SurfaceValidationIssue>,\n repoRoot: string,\n): string {\n if (issues.length === 0) return ''\n const lines = issues.map(\n (i) => ` - ${i.surface}: ${i.path ? `\"${i.path}\"` : '<not set>'} (${i.reason})`,\n )\n return [\n `Agent surface validation failed against repoRoot=${repoRoot}:`,\n ...lines,\n '',\n 'Fix the manifest: every required surface must point at an existing',\n 'directory (systemPrompt / tools / personas / knowledge) or file',\n '(rubric). Optional surfaces (scaffolding / memory / rag / outputSchema)',\n 'may be omitted; the loop will reject findings targeting omitted',\n 'surfaces rather than fabricating a path.',\n ].join('\\n')\n}\n","/**\n * `defineAgent` — typed, validated manifest API for declarative agent\n * configuration. The substrate consumes this manifest to wire the\n * canonical eval pattern + analyst self-improvement loop without any\n * per-vertical glue.\n *\n * Design goal: scale to 1000s of vertical agents. Every agent declares\n * its surfaces, rubric, runtime, and analyst configuration in ~50 lines.\n * No per-vertical `ImprovementAdapter`. No per-vertical CLI. No\n * fabricated paths.\n *\n * Validation: `defineAgent` runs `validateSurfaces` synchronously and\n * throws a structured error if any required surface is missing on\n * disk. The cost is one filesystem stat per surface (cheap); the\n * benefit is a manifest that can't ship broken.\n */\n\nimport type { TraceAnalystKindSpec } from '@tangle-network/agent-eval'\nimport type { RuntimeStreamEvent } from '../types'\nimport { type AgentSurfaces, renderSurfaceIssues, validateSurfaces } from './surfaces'\n\n// ── manifest ─────────────────────────────────────────────────────────\n\n/**\n * The full agent manifest. Each agent ships ONE of these.\n *\n * Generics:\n * `TPersona` — the agent's persona shape (loaded from\n * `surfaces.personas`). Defaults to `unknown` so the substrate's\n * persona discovery (`loadPersonas`) can accept anything; per-agent\n * code re-narrows when it matters.\n * `TRunOutput` — the shape `runtime.act` returns. Used by the rubric\n * scorers and emitted into the trace.\n */\nexport interface AgentManifest<TPersona = unknown, TRunOutput = unknown> {\n /**\n * Stable identifier — used as `projectId` in traces, as the analyst\n * loop's `runId` prefix, and as the namespace under which findings\n * are persisted. MUST match the agent's repo name to keep\n * cross-repo telemetry joinable.\n */\n id: string\n\n /**\n * Filesystem root the substrate resolves surface paths against.\n * Typically `process.cwd()` or a fixed absolute path. Use an\n * absolute path when the agent's tests may run from subdirectories\n * (vitest sometimes shifts cwd).\n */\n repoRoot: string\n\n /**\n * Map of mutable surfaces the self-improvement loop can edit. See\n * `AgentSurfaces` — required: `systemPrompt`, `tools`, `rubric`,\n * `knowledge`, `personas`. Optional: `scaffolding`, `memory`, `rag`,\n * `outputSchema`.\n *\n * Every required path is validated at `defineAgent` time. Missing\n * paths throw with the full list of offenders.\n */\n surfaces: AgentSurfaces\n\n /**\n * Rubric the substrate uses to score each run. Dimensions × weights\n * × judges. The substrate computes the weighted composite and\n * stamps it into the RunRecord.\n */\n rubric: AgentRubric<TRunOutput>\n\n /**\n * Runtime adapter — how the substrate INVOKES the agent against a\n * persona. The `act` function takes a persona + a context (with the\n * tracer the substrate threads through for span emission) and\n * returns the run output the rubric will score.\n *\n * The agent's existing production runtime goes in here; the\n * substrate is intentionally thin around it.\n */\n runtime: AgentRuntime<TPersona, TRunOutput>\n\n /**\n * Persona discovery — the substrate loads personas via this function\n * at eval start. Can read from `surfaces.personas`, an API, or be\n * hardcoded. The substrate calls it once per `runAgentEval` call;\n * persona ordering is preserved.\n */\n personas: () => Promise<ReadonlyArray<TPersona>>\n\n /**\n * Analyst kinds the substrate runs against each persona's trace.\n * Defaults to `DEFAULT_TRACE_ANALYST_KINDS` from agent-eval. Per-agent\n * authors can prune (e.g. skip `knowledge-poisoning` when there's no\n * knowledge base) or extend (custom domain kinds).\n *\n * Empty array disables the loop — useful for `pnpm eval --no-analyst`.\n */\n analystKinds: ReadonlyArray<TraceAnalystKindSpec>\n\n /**\n * Analyst LLM configuration. The substrate uses these for all four\n * kinds (override per-kind via `analystKinds` if needed).\n */\n analyst: AnalystConfig\n\n /**\n * Auto-apply policy. Knowledge / improvement edits land only when\n * `enabled === true` AND the source finding's confidence meets the\n * threshold. `mode` controls how applies happen: `'write'` mutates\n * files in-place; `'open-pr'` writes to a branch and opens a PR.\n *\n * Default: knowledge auto-applies at confidence ≥0.85 in `'write'`\n * mode (wiki edits are git-reversible); improvement stays at\n * `enabled: false` until the agent author has measured precision.\n */\n autoApply?: AutoApplyPolicy\n}\n\nexport interface AgentRubric<TRunOutput> {\n /** Dimensions composing the weighted score. Weights sum to 1.0 by convention. */\n dimensions: ReadonlyArray<RubricDimension<TRunOutput>>\n /**\n * Optional judges layered on top of deterministic dimensions. Each\n * judge returns a score per dimension; the substrate averages judges\n * (mean by default) for the LLM contribution.\n */\n judges?: ReadonlyArray<JudgeConfig<TRunOutput>>\n}\n\nexport interface RubricDimension<TRunOutput> {\n /** Unique identifier — appears in finding subjects (`rubric:<id>`). */\n id: string\n /** 0..1 — weight in the composite. */\n weight: number\n /**\n * Deterministic scorer: given the persona + run output, returns a\n * 0..1 score. The substrate sums weight × score across dimensions\n * for the deterministic composite; judges supplement subjective dims.\n */\n score: (input: { persona: unknown; output: TRunOutput }) => number\n /** Optional human-readable label for reports. */\n label?: string\n}\n\nexport interface JudgeConfig<TRunOutput> {\n /** Judge identifier — appears in trace spans + manifest. */\n id: string\n /** Model snapshot to invoke. Pin the snapshot (`claude-sonnet-4-6@2025-04-15`); the validator rejects bare aliases. */\n model: string\n /** Dimensions this judge scores. */\n dimensions: ReadonlyArray<string>\n /**\n * Optional rubric anchors — text examples the judge sees as a\n * few-shot prompt to calibrate. STRONGLY recommended for subjective\n * dimensions; required by the calibration gate (Pearson ≥0.7).\n */\n anchors?: ReadonlyArray<{ input: string; output: TRunOutput; expected: Record<string, number> }>\n}\n\nexport interface AgentRuntime<TPersona, TRunOutput> {\n /**\n * Invoke the agent against one persona. Returns BOTH:\n * - `events`: an `AsyncIterable<RuntimeStreamEvent>` the chat-centric\n * product consumes verbatim (SSE / WebSocket / inline render).\n * **Streaming is mandatory — never collapse this to a single Promise.**\n * The agent's existing `runChatTurn` (or equivalent async generator)\n * plugs in here directly.\n * - `output`: a `Promise<TRunOutput>` resolved AFTER the event stream\n * drains. The eval substrate awaits this for rubric scoring; chat\n * products usually ignore it (they already rendered incrementally).\n *\n * Implementation contract:\n * 1. `act` MUST return immediately (synchronous construction of the\n * `events` iterator + the `output` promise).\n * 2. Iterating `events` drives the underlying LLM/tool calls — the\n * caller chooses when to consume.\n * 3. `output` resolves only after the iterator yields its terminal\n * event (typically `task_end`); see `collectAgentRun` helper.\n *\n * `ctx.emitter` is the substrate-threaded `TraceEmitter` — runtimes\n * SHOULD record LLM/tool spans through it for capture integrity.\n * `ctx.deadlineMs` is wall-clock; the runtime SHOULD honour for graceful\n * cancel. `ctx.signal` is the standard abort signal.\n */\n act: (persona: TPersona, ctx: AgentRunContext) => AgentRunInvocation<TRunOutput>\n}\n\nexport interface AgentRunInvocation<TRunOutput> {\n /** Live stream of typed runtime events. Consumed by chat UX directly. */\n events: AsyncIterable<RuntimeStreamEvent>\n /** Final structured output the rubric scores. Resolves after `events` drains. */\n output: Promise<TRunOutput>\n}\n\n/**\n * Stub for agents whose `runtime.act` is not yet wired to the substrate's\n * eval path. Preserves the streaming contract (empty event stream + a\n * rejected `output` promise that tells the caller exactly what to fix).\n *\n * Per-vertical manifests usually start with this stub and replace it with\n * the agent's real streaming runtime (`runChatTurn` or equivalent) once\n * the eval path consumes the manifest end-to-end.\n */\nexport function unimplementedAgentRun<TRunOutput = unknown>(\n reason = 'AgentRuntime.act is not yet wired for this manifest',\n): AgentRunInvocation<TRunOutput> {\n return {\n events: (async function* empty(): AsyncIterable<RuntimeStreamEvent> {})(),\n output: Promise.reject(new Error(reason)),\n }\n}\n\n/**\n * Drain `act`'s `events` into an array AND await its `output`. Useful for\n * eval / outcome-measurement code paths that don't care about live\n * rendering. The events array is preserved so the substrate can inspect\n * tool calls / readiness / questions retrospectively.\n *\n * IMPORTANT: chat-centric UX MUST NOT call this — it defeats streaming\n * (no incremental render). Use `for await (const ev of invocation.events)`\n * directly in the chat surface.\n */\nexport async function collectAgentRun<TRunOutput>(\n invocation: AgentRunInvocation<TRunOutput>,\n): Promise<{ events: ReadonlyArray<RuntimeStreamEvent>; output: TRunOutput }> {\n const events: RuntimeStreamEvent[] = []\n for await (const ev of invocation.events) events.push(ev)\n const output = await invocation.output\n return { events, output }\n}\n\nexport interface AgentRunContext {\n /** Substrate-managed trace emitter. */\n emitter: import('@tangle-network/agent-eval').TraceEmitter\n /** Stable run id for this persona × variant cell. */\n runId: string\n /** Variant the runtime is exercising (e.g. `'baseline'`, `'source-grounded'`). */\n variantId?: string\n /** Wall-clock deadline (epoch ms). The runtime SHOULD honour for graceful cancel. */\n deadlineMs?: number\n /** Optional abort signal. */\n signal?: AbortSignal\n}\n\nexport interface AnalystConfig {\n /** Model the analyst kinds use. Override per-kind via `analystKinds[i].cost.models`. */\n model: string\n /** Optional total budget across all kinds for one run. Substrate enforces via `BudgetGuard`. */\n budgetUsd?: number\n /** Backend hint for the AxAIService factory — same shape every kind uses. */\n backend?: {\n name?: 'openai' | 'router'\n apiKey?: string\n baseUrl?: string\n }\n}\n\nexport interface AutoApplyPolicy {\n knowledge?: {\n enabled: boolean\n confidenceThreshold?: number\n mode?: 'write' | 'open-pr'\n }\n improvement?: {\n enabled: boolean\n confidenceThreshold?: number\n mode?: 'write' | 'open-pr'\n }\n}\n\n// ── factory + validation ─────────────────────────────────────────────\n\nexport class AgentManifestError extends Error {\n constructor(\n message: string,\n public readonly agentId: string,\n public readonly issues: ReadonlyArray<unknown> = [],\n ) {\n super(message)\n this.name = 'AgentManifestError'\n }\n}\n\n/**\n * Construct a validated agent manifest. Throws `AgentManifestError`\n * if any required surface is missing on disk.\n *\n * Generics: pass your persona / output types if you want narrowed\n * `runtime.act` signatures:\n * `defineAgent<TaxPersona, TaxRunOutput>({ ... })`\n *\n * Most callers don't need the generics — the substrate operates on\n * `unknown` payloads internally and the manifest's `score` /\n * `runtime.act` see the typed shapes via TypeScript inference at\n * the call site.\n */\nexport function defineAgent<TPersona = unknown, TRunOutput = unknown>(\n manifest: AgentManifest<TPersona, TRunOutput>,\n): AgentManifest<TPersona, TRunOutput> {\n if (!manifest.id || manifest.id.trim().length === 0) {\n throw new AgentManifestError('defineAgent: `id` is required', manifest.id ?? '')\n }\n if (!manifest.repoRoot || manifest.repoRoot.trim().length === 0) {\n throw new AgentManifestError('defineAgent: `repoRoot` is required', manifest.id)\n }\n const issues = validateSurfaces(manifest.surfaces, manifest.repoRoot)\n if (issues.length > 0) {\n throw new AgentManifestError(\n renderSurfaceIssues(issues, manifest.repoRoot),\n manifest.id,\n issues,\n )\n }\n // Lightweight rubric sanity: weights sum to ~1.0 (no hard requirement —\n // the substrate normalizes — but flag wildly miscalibrated weights).\n const total = manifest.rubric.dimensions.reduce((acc, d) => acc + d.weight, 0)\n if (manifest.rubric.dimensions.length > 0 && (total < 0.5 || total > 1.5)) {\n throw new AgentManifestError(\n `defineAgent(${manifest.id}): rubric dimension weights sum to ${total.toFixed(3)} — should be ~1.0`,\n manifest.id,\n )\n }\n return manifest\n}\n","/**\n * Substrate-default `ImprovementAdapter` — surfaces-driven, LLM-drafted\n * patches, optional auto-apply or PR-open.\n *\n * This is the one ImprovementAdapter every vertical agent uses. The\n * substrate parses each finding's `subject` via\n * `parseFindingSubject` (agent-eval), resolves it to a real file path\n * via the agent's `AgentSurfaces`, reads the current content, and asks\n * an LLM to draft a unified-diff patch given the finding + current\n * content + per-kind editing-discipline rules.\n *\n * Auto-apply gates on the source-finding's confidence and the\n * autoApply.improvement policy. Two modes:\n * `write` — apply the patch in-place via `git apply -p0`. Operator\n * reviews via `git diff`.\n * `open-pr` — write to a branch, commit, push, open a PR via `gh`.\n * Operator reviews via the PR UI.\n *\n * Fail-loud rules:\n * - Findings whose subject doesn't parse → counted in `errors`.\n * - Findings whose subject targets an undeclared surface → counted in\n * `errors` with the offending kind in the message.\n * - Findings whose target path doesn't exist AND the kind isn't a\n * create-new variant (`new-tool`, `knowledge.wiki`) → counted in\n * `errors` with the resolved path in the message.\n * - LLM drafts that fail JSON-schema validation → counted in\n * `errors` with the schema issue.\n *\n * No silent skips. Every dropped finding has a recorded reason the\n * loop's report surfaces.\n */\n\nimport { spawnSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport type { AnalystFinding, FindingSubject } from '@tangle-network/agent-eval'\nimport { parseFindingSubject } from '@tangle-network/agent-eval/analyst'\nimport type { ImprovementAdapter } from '../analyst-loop/types'\nimport type { AgentSurfaces, ResolvedSurface } from './surfaces'\nimport { resolveSubjectPath } from './surfaces'\n\n// ── proposal shape ───────────────────────────────────────────────────\n\nexport interface SurfaceImprovementEdit {\n /** Stable id derived from the source finding so re-proposals are idempotent. */\n id: string\n /** The finding that produced this edit — for revert + audit trail. */\n sourceFindingId: string\n /** Parsed subject; included so the apply step doesn't re-parse. */\n subject: FindingSubject\n /** Resolved on-disk target. */\n target: ResolvedSurface\n /** SHA-256 of the current file content the patch was drafted against. */\n baseSha256: string\n /** Unified-diff patch the LLM drafted (relative to `target.absolutePath`). */\n patch: string\n /** One-line summary the operator sees in the report / PR title. */\n summary: string\n /** Multi-line rationale for the PR body — finding context + LLM reasoning. */\n rationale: string\n /** Carry-forward from the finding so the apply gate can check the threshold. */\n confidence: number\n /** Carry-forward severity for prioritization. */\n severity: AnalystFinding['severity']\n}\n\nexport interface CreateSurfaceImprovementAdapterOpts {\n surfaces: AgentSurfaces\n repoRoot: string\n /**\n * LLM-draft callback. Given a finding + current file content + the\n * resolved target, returns a unified-diff patch + summary + rationale.\n *\n * Required — the substrate doesn't ship a hardcoded prompt; the agent\n * author picks the model (Haiku for cheap routine drafts, Sonnet for\n * substantive prompt rewrites, etc.) via this callback.\n */\n draftPatch: (input: DraftPatchInput) => Promise<DraftPatchOutput>\n /**\n * Apply mode:\n * `write` — `git apply` in-place; operator reviews via `git diff`\n * `open-pr` — branch + commit + push + `gh pr create`\n * `none` — never apply; collect proposals for the report only\n *\n * The `apply` method honours this even when the loop calls it; the\n * effective behaviour is also gated on the per-finding confidence\n * threshold via `runAnalystLoop`'s `autoApply` policy.\n */\n mode?: 'write' | 'open-pr' | 'none'\n /** When `mode === 'open-pr'`, the base branch new PRs target. Default: `main`. */\n baseBranch?: string\n /** Required for `mode === 'open-pr'` — the GH owner/repo (`tangle-network/tax-agent`). */\n ghRepo?: string\n /**\n * When the resolved target doesn't exist, allow the substrate to\n * CREATE the file (for `knowledge.wiki`, `new-tool` subjects). Default\n * true for those kinds, false for `system-prompt` / `rubric` / etc.\n * (named sections that don't exist are a contract violation, not a\n * scaffolding opportunity).\n */\n allowCreateForKinds?: ReadonlyArray<FindingSubject['kind']>\n}\n\nexport interface DraftPatchInput {\n finding: AnalystFinding\n subject: FindingSubject\n target: ResolvedSurface\n /** Current file content (empty string when `intent === 'create-new'`). */\n currentContent: string\n}\n\nexport interface DraftPatchOutput {\n /** Unified diff against the current file content. Empty string skips this finding. */\n patch: string\n /** One-line summary for the operator. */\n summary: string\n /** Multi-line rationale for the PR body. */\n rationale: string\n}\n\n// ── factory ──────────────────────────────────────────────────────────\n\nconst DEFAULT_CREATE_KINDS: ReadonlyArray<FindingSubject['kind']> = [\n 'knowledge.wiki',\n 'knowledge.claim',\n 'knowledge.raw',\n 'new-tool',\n]\n\nexport function createSurfaceImprovementAdapter(\n opts: CreateSurfaceImprovementAdapterOpts,\n): ImprovementAdapter<SurfaceImprovementEdit> {\n const mode = opts.mode ?? 'none'\n const allowCreate = opts.allowCreateForKinds ?? DEFAULT_CREATE_KINDS\n\n return {\n async proposeFromFindings(findings) {\n const edits: SurfaceImprovementEdit[] = []\n const errors: Array<{ findingId: string; subject: string; message: string }> = []\n let skipped = 0\n\n for (const f of findings) {\n const subject = parseFindingSubject(f.subject)\n if (subject === null) {\n if (f.subject !== undefined) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject,\n message: 'subject does not parse against the finding-subject grammar',\n })\n } else {\n // Subject-less findings are descriptive, not actionable —\n // legitimate; count in `skipped` not `errors`.\n skipped += 1\n }\n continue\n }\n\n // `cluster` findings (failure-mode) are evidence, not mutations.\n if (subject.kind === 'cluster') {\n skipped += 1\n continue\n }\n\n // `agent-knowledge:*` findings flow to the KnowledgeAdapter;\n // the ImprovementAdapter skips them so subjects don't double-route.\n if (subject.kind.startsWith('knowledge.')) {\n skipped += 1\n continue\n }\n\n const target = resolveSubjectPath(subject, opts.surfaces, opts.repoRoot)\n if (target === null) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject ?? '',\n message: `subject kind \"${subject.kind}\" targets an undeclared surface; declare it in AgentSurfaces or stop emitting this subject`,\n })\n continue\n }\n\n if (target.intent === 'create-new' && !allowCreate.includes(subject.kind)) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject ?? '',\n message: `target ${target.repoRelativePath} does not exist; the kind \"${subject.kind}\" requires an existing target (analyst named a section that isn't in the codebase)`,\n })\n continue\n }\n\n const currentContent = target.exists ? readFileSync(target.absolutePath, 'utf-8') : ''\n\n let draft: DraftPatchOutput\n try {\n draft = await opts.draftPatch({ finding: f, subject, target, currentContent })\n } catch (err) {\n errors.push({\n findingId: f.finding_id,\n subject: f.subject ?? '',\n message: `draftPatch threw: ${err instanceof Error ? err.message : String(err)}`,\n })\n continue\n }\n\n if (draft.patch.trim().length === 0) {\n skipped += 1\n continue\n }\n\n edits.push({\n id: `imp-${f.finding_id}`,\n sourceFindingId: f.finding_id,\n subject,\n target,\n baseSha256: sha256(currentContent),\n patch: draft.patch,\n summary: draft.summary,\n rationale: draft.rationale,\n confidence: f.confidence,\n severity: f.severity,\n })\n }\n\n return { edits, skipped, errors }\n },\n\n async apply(edits) {\n const applied: string[] = []\n const warnings: string[] = []\n\n if (mode === 'none') {\n warnings.push(\n 'createSurfaceImprovementAdapter: mode=none; no edits applied — adjust manifest.autoApply.improvement.mode',\n )\n return { applied, warnings }\n }\n\n if (mode === 'open-pr' && !opts.ghRepo) {\n warnings.push(\n 'createSurfaceImprovementAdapter: mode=open-pr requires `ghRepo`; falling back to no-op',\n )\n return { applied, warnings }\n }\n\n for (const edit of edits) {\n // Race-detection: confirm the file content hasn't moved since the\n // patch was drafted. A diff applied against drifted content is a\n // recipe for silent corruption.\n const current = edit.target.exists ? readFileSync(edit.target.absolutePath, 'utf-8') : ''\n if (sha256(current) !== edit.baseSha256) {\n warnings.push(\n `${edit.target.repoRelativePath}: base SHA mismatch; file changed after draft. Skipping.`,\n )\n continue\n }\n\n const ok = applyPatchInPlace(edit, opts.repoRoot)\n if (!ok) {\n warnings.push(`${edit.target.repoRelativePath}: git apply failed`)\n continue\n }\n applied.push(edit.target.repoRelativePath)\n }\n\n if (mode === 'open-pr' && applied.length > 0 && opts.ghRepo) {\n const prUrl = openPullRequest(\n applied,\n edits.filter((e) => applied.includes(e.target.repoRelativePath)),\n opts.repoRoot,\n opts.ghRepo,\n opts.baseBranch ?? 'main',\n )\n if (prUrl) warnings.push(`opened PR: ${prUrl}`)\n else warnings.push('PR creation failed; edits are committed to a local branch only')\n }\n\n return { applied, warnings }\n },\n }\n}\n\n// ── apply helpers ────────────────────────────────────────────────────\n\nfunction applyPatchInPlace(edit: SurfaceImprovementEdit, repoRoot: string): boolean {\n const result = spawnSync('git', ['apply', '--whitespace=fix', '-p0', '-'], {\n cwd: repoRoot,\n input: edit.patch,\n encoding: 'utf-8',\n })\n return result.status === 0\n}\n\nfunction openPullRequest(\n paths: ReadonlyArray<string>,\n edits: ReadonlyArray<SurfaceImprovementEdit>,\n repoRoot: string,\n ghRepo: string,\n baseBranch: string,\n): string | null {\n const branch = `analyst-loop/${Date.now()}-${edits[0]?.sourceFindingId.slice(0, 12) ?? 'edits'}`\n // Create branch, stage, commit\n const checkout = spawnSync('git', ['checkout', '-b', branch], { cwd: repoRoot })\n if (checkout.status !== 0) return null\n const add = spawnSync('git', ['add', ...paths], { cwd: repoRoot })\n if (add.status !== 0) return null\n const title = `analyst-loop: ${edits[0]?.summary ?? `${edits.length} improvement edits`}`\n const body = [\n `Automated analyst-loop edits — review carefully before merge.`,\n '',\n `Source findings:`,\n ...edits.map(\n (e) =>\n ` - ${e.sourceFindingId} (confidence ${e.confidence.toFixed(2)}, severity ${e.severity})`,\n ),\n '',\n 'Rationales:',\n ...edits.map((e) => `\\n## ${e.target.repoRelativePath}\\n\\n${e.rationale}`),\n ].join('\\n')\n const commit = spawnSync('git', ['commit', '-m', title, '-m', body], { cwd: repoRoot })\n if (commit.status !== 0) return null\n const push = spawnSync('git', ['push', '-u', 'origin', branch], { cwd: repoRoot })\n if (push.status !== 0) return null\n const pr = spawnSync(\n 'gh',\n [\n 'pr',\n 'create',\n '--repo',\n ghRepo,\n '--title',\n title,\n '--body',\n body,\n '--base',\n baseBranch,\n '--head',\n branch,\n ],\n { cwd: repoRoot, encoding: 'utf-8' },\n )\n if (pr.status !== 0) return null\n return pr.stdout.trim()\n}\n\nfunction sha256(s: string): string {\n // node:crypto is dynamic-imported lazily so the adapter can be tested in\n // environments without crypto (browser tests, mocked envs).\n const crypto = require('node:crypto') as typeof import('node:crypto')\n return crypto.createHash('sha256').update(s, 'utf-8').digest('hex')\n}\n","/**\n * Substrate-default `KnowledgeAdapter` — wraps agent-knowledge's\n * `proposeFromFindings` + `applyKnowledgeWriteBlocks` with substrate\n * defaults (auto-lint after apply, source linkage via finding id).\n *\n * Every agent that ships a `.agent-knowledge/` tree uses this adapter\n * unmodified. Per-agent customization happens at the manifest level\n * (`autoApply.knowledge.confidenceThreshold`, etc.), not by writing a\n * new adapter.\n *\n * Lint discipline: after each apply we run agent-knowledge's\n * `lintKnowledgeIndex` to catch broken links / circular claims /\n * duplicate pages introduced by the new writes. Findings that fail the\n * post-apply lint are recorded in `warnings`; the apply itself is not\n * rolled back (lint failures are soft — humans review the wiki state).\n */\n\nimport type { AnalystFinding } from '@tangle-network/agent-eval'\nimport type { KnowledgeAdapter } from '../analyst-loop/types'\n\nexport interface CreateSurfaceKnowledgeAdapterOpts {\n /** `.agent-knowledge/` root (absolute path the substrate writes blocks against). */\n knowledgeRoot: string\n}\n\n/**\n * Build the adapter. We accept the agent-knowledge functions as DI so\n * the substrate stays decoupled from a specific agent-knowledge\n * version — the agent author imports them in their manifest module\n * and hands them to the factory.\n *\n * `proposeFromFindings(findings)` returns\n * `{ proposals: KnowledgeProposal[]; skipped: number; errors: ... }`.\n *\n * `applyKnowledgeWriteBlocks(root, content)` returns\n * `{ written: string[]; warnings: string[] }`.\n *\n * `lintKnowledgeIndex(index)` (optional) returns `KnowledgeLintFinding[]`.\n */\nexport interface KnowledgeAdapterDeps<TProposal> {\n proposeFromFindings: (findings: ReadonlyArray<AnalystFinding>) => {\n proposals: TProposal[]\n skipped: number\n errors: Array<{ findingId: string; subject: string; message: string }>\n }\n applyKnowledgeWriteBlocks: (\n root: string,\n proposalText: string,\n ) => Promise<{ written: string[]; warnings: string[] }>\n /**\n * Optional post-apply lint hook. The substrate runs it after each\n * batch of writes; failures land in `warnings` (the apply is not\n * rolled back — lint signals drift to review, not block).\n */\n lintAfterApply?: (root: string) => Promise<ReadonlyArray<string>>\n}\n\nexport function createSurfaceKnowledgeAdapter<TProposal>(\n opts: CreateSurfaceKnowledgeAdapterOpts,\n deps: KnowledgeAdapterDeps<TProposal>,\n): KnowledgeAdapter<TProposal> {\n return {\n proposeFromFindings(findings) {\n const batch = deps.proposeFromFindings(findings)\n return {\n proposals: batch.proposals,\n skipped: batch.skipped,\n errors: batch.errors,\n }\n },\n async apply(proposals) {\n const written: string[] = []\n const warnings: string[] = []\n for (const p of proposals) {\n const proposalText = renderProposalAsWriteBlock(p)\n if (proposalText === null) {\n warnings.push(\n `proposal has no writeBlocks/content; skipping (sourceFindingId=${getSourceFindingId(p)})`,\n )\n continue\n }\n try {\n const r = await deps.applyKnowledgeWriteBlocks(opts.knowledgeRoot, proposalText)\n written.push(...r.written)\n warnings.push(...r.warnings)\n } catch (err) {\n warnings.push(\n `applyKnowledgeWriteBlocks failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n if (deps.lintAfterApply && written.length > 0) {\n try {\n const lintIssues = await deps.lintAfterApply(opts.knowledgeRoot)\n for (const issue of lintIssues) warnings.push(`lint: ${issue}`)\n } catch (err) {\n warnings.push(\n `lintAfterApply failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n return { written, warnings }\n },\n }\n}\n\n/**\n * Pluck the canonical write-block text from a proposal regardless of\n * which exact agent-knowledge version produced it. We accept either:\n * - `{ writeBlocks: Array<{ path, content }> }` — the typed shape\n * 1.3.0+ emits\n * - `{ proposalText: string }` — legacy single-block shape\n * - `{ content: string }` — minimal raw form\n *\n * Returns `null` when nothing parseable is present (warned upstream).\n */\nfunction renderProposalAsWriteBlock(p: unknown): string | null {\n if (!p || typeof p !== 'object') return null\n const obj = p as Record<string, unknown>\n if (Array.isArray(obj.writeBlocks)) {\n const blocks = obj.writeBlocks as Array<{ path?: string; content?: string }>\n if (blocks.length === 0) return null\n return blocks\n .map((b) => (typeof b.content === 'string' ? b.content : ''))\n .filter((s) => s.length > 0)\n .join('\\n\\n')\n }\n if (typeof obj.proposalText === 'string') return obj.proposalText\n if (typeof obj.content === 'string') return obj.content\n return null\n}\n\nfunction getSourceFindingId(p: unknown): string {\n if (!p || typeof p !== 'object') return '<unknown>'\n const obj = p as Record<string, unknown>\n if (typeof obj.sourceFindingId === 'string') return obj.sourceFindingId\n if (typeof obj.id === 'string') return obj.id\n return '<unknown>'\n}\n","/**\n * `OutcomeMeasurement` — the missing metric that turns the analyst\n * loop from \"observability\" into \"self-improvement\".\n *\n * Without this hook, the loop reports process counts (`findings: 42`,\n * `applied: 7`) and never proves the applied edits actually improved\n * anything. With this hook, the substrate re-runs the cohort against\n * the same personas after each apply pass and reports a composite\n * score delta. A negative delta is the substrate's strongest signal\n * to either roll back or surface for review.\n *\n * Wiring is intentionally simple: pass the manifest + the `runAgentEval`\n * function and a list of `personaIds` to re-run. The wrapper:\n * 1. Captures the baseline composite from the just-finished run.\n * 2. After `runAnalystLoop` returns, re-invokes `runAgentEval` against\n * the same persona slice.\n * 3. Computes the delta and appends to `loop-report.json`.\n * 4. If `rollbackOnRegression` and delta < 0, reverts applied edits.\n */\n\nimport type { RunAnalystLoopResult } from '../analyst-loop/types'\n\nexport interface OutcomeMeasurement {\n /** Baseline composite before applies — captured from the most-recent eval run. */\n baselineComposite: number\n /** Composite after re-running the cohort with applied edits. */\n afterComposite: number\n /** `afterComposite - baselineComposite`. Positive = the loop improved the agent. */\n delta: number\n /** Per-persona deltas for finer-grained review. */\n perPersona: ReadonlyArray<{ personaId: string; before: number; after: number; delta: number }>\n /** When the substrate rolled back applies due to regression, the paths reverted. */\n rolledBackPaths: ReadonlyArray<string>\n}\n\nexport interface OutcomeMeasurementOpts {\n /** Composite scores from the run that produced the findings. */\n baseline: ReadonlyArray<{ personaId: string; composite: number }>\n /**\n * Re-run callback — the substrate invokes this after applies. The\n * agent author provides their `runAgentEval`-equivalent so the\n * substrate can ask \"score this persona slice now.\"\n *\n * The callback SHOULD reuse the same cohort + judges + variant as\n * the baseline run; only the agent's mutable surfaces have changed.\n */\n reRunCohort: (\n personaIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<{ personaId: string; composite: number }>>\n /** When `true`, applied edits are reverted on negative delta. Default `false`. */\n rollbackOnRegression?: boolean\n /** Callback to revert a list of paths (typically `git checkout HEAD --`). */\n revert?: (paths: ReadonlyArray<string>) => Promise<void>\n}\n\n/**\n * Run `runAnalystLoop` and stamp an `OutcomeMeasurement` onto the\n * result. The substrate calls this after each canonical eval; the\n * delta lands in `loop-report.json` for cross-run trend analysis.\n *\n * The function returns the original `RunAnalystLoopResult` enriched\n * with `outcome` so callers stay backwards-compatible (the field is\n * optional on the type; missing means no measurement was wired).\n */\nexport async function measureOutcome<TProposal, TEdit>(\n result: RunAnalystLoopResult<TProposal, TEdit>,\n opts: OutcomeMeasurementOpts,\n): Promise<RunAnalystLoopResult<TProposal, TEdit> & { outcome: OutcomeMeasurement }> {\n const applied = result.knowledge?.applied ?? []\n const improvementsApplied = result.improvement?.applied ?? []\n const allApplied = [...applied, ...improvementsApplied]\n\n // No applies → no outcome to measure. Return a zero-delta to keep the\n // shape stable for consumers; baseline / after equal.\n if (allApplied.length === 0) {\n return {\n ...result,\n outcome: {\n baselineComposite: meanComposite(opts.baseline),\n afterComposite: meanComposite(opts.baseline),\n delta: 0,\n perPersona: opts.baseline.map((b) => ({\n personaId: b.personaId,\n before: b.composite,\n after: b.composite,\n delta: 0,\n })),\n rolledBackPaths: [],\n },\n }\n }\n\n const personaIds = opts.baseline.map((b) => b.personaId)\n const after = await opts.reRunCohort(personaIds)\n const afterByPersona = new Map(after.map((r) => [r.personaId, r.composite]))\n\n const perPersona = opts.baseline.map((b) => {\n const a = afterByPersona.get(b.personaId) ?? b.composite\n return { personaId: b.personaId, before: b.composite, after: a, delta: a - b.composite }\n })\n const baselineComposite = meanComposite(opts.baseline)\n const afterComposite = meanComposite(after)\n const delta = afterComposite - baselineComposite\n\n let rolledBackPaths: string[] = []\n if (delta < 0 && opts.rollbackOnRegression && opts.revert) {\n await opts.revert(allApplied)\n rolledBackPaths = [...allApplied]\n }\n\n return {\n ...result,\n outcome: {\n baselineComposite,\n afterComposite,\n delta,\n perPersona,\n rolledBackPaths,\n },\n }\n}\n\nfunction meanComposite(rows: ReadonlyArray<{ composite: number }>): number {\n if (rows.length === 0) return 0\n return rows.reduce((acc, r) => acc + r.composite, 0) / rows.length\n}\n","/**\n * Sandbox bridge for `AgentRuntime.act` — prod-faithful eval execution.\n *\n * The point of this adapter is parity: the eval substrate must run the agent\n * through the SAME profile the production chat turn uses, or scorecard numbers\n * grade a profile that never ships. `createSandboxAct` composes the production\n * profile via {@link composeProductionAgentProfile}, boots a sandbox with it\n * through the loop kernel's own {@link createSandboxForSpec}, streams the\n * `streamPrompt` events mapped to the `RuntimeStreamEvent` vocabulary, and\n * resolves the `OutputAdapter`-parsed output for rubric scoring — satisfying\n * the `act` streaming contract with one code path shared by chat and eval.\n *\n * Agents with a bespoke streaming chat turn should wire THAT into `act`\n * directly (the contract is designed for it); this adapter is the default for\n * agents whose turn is a plain prod-profile sandbox dispatch — notably the\n * agents agent-builder generates.\n */\n\nimport type { AgentProfile, SandboxEvent } from '@tangle-network/sandbox'\nimport type { ComposeProductionAgentProfileOptions } from '../mcp/delegation-profile'\nimport { composeProductionAgentProfile } from '../mcp/delegation-profile'\nimport type { AgentRunSpec, OutputAdapter, SandboxClient } from '../runtime'\nimport { createSandboxForSpec, mapSandboxEvent } from '../runtime'\nimport type { RuntimeStreamEvent } from '../types'\nimport type { AgentRunContext, AgentRunInvocation } from './define-agent'\n\nexport interface CreateSandboxActOptions<TPersona, TRunOutput> {\n /** Canonical agent profile — the same one the prod chat turn composes from. */\n baseProfile: AgentProfile\n /** Sandbox client used to boot the per-run sandbox. */\n sandboxClient: SandboxClient\n /** Persona → prompt. Pure; the eval cell's input. */\n buildPrompt: (persona: TPersona) => string\n /** Sandbox event stream → typed output the rubric scores. */\n output: OutputAdapter<TRunOutput>\n /**\n * Per-persona composition overrides (workspace-augmented system prompt,\n * extra file mounts, sandbox key). Merged into\n * {@link composeProductionAgentProfile}; `env` here is overridden by the\n * top-level `env` option when both are set.\n */\n compose?: (persona: TPersona) => ComposeProductionAgentProfileOptions\n /** Sandbox-SDK overrides forwarded to `createSandboxForSpec`. */\n sandboxOverrides?: AgentRunSpec<unknown>['sandboxOverrides']\n /** Stable run name surfaced in mapped `llm_call` events. */\n name?: string\n /** Override the `SandboxEvent → RuntimeStreamEvent` mapper. */\n mapEvent?: (\n event: SandboxEvent,\n opts: { agentRunName?: string },\n ) => RuntimeStreamEvent | undefined\n /** Environment source for delegation-MCP composition. Defaults to `process.env`. */\n env?: Record<string, string | undefined>\n}\n\n/**\n * Build an `AgentRuntime.act` implementation backed by a single prod-profile\n * sandbox run. The returned function honours the `act` contract: it returns\n * synchronously with a live `events` iterator and an `output` promise that\n * resolves only after the iterator drains.\n */\nexport function createSandboxAct<TPersona, TRunOutput>(\n options: CreateSandboxActOptions<TPersona, TRunOutput>,\n): (persona: TPersona, ctx: AgentRunContext) => AgentRunInvocation<TRunOutput> {\n const mapEvent = options.mapEvent ?? mapSandboxEvent\n\n return (persona: TPersona, ctx: AgentRunContext): AgentRunInvocation<TRunOutput> => {\n const profile = composeProductionAgentProfile(options.baseProfile, {\n ...(options.compose?.(persona) ?? {}),\n ...(options.env ? { env: options.env } : {}),\n })\n const agentRunName = options.name ?? profile.name ?? 'agent'\n const message = options.buildPrompt(persona)\n const signal = ctx.signal ?? new AbortController().signal\n\n const raw: SandboxEvent[] = []\n let settle!: (value: TRunOutput) => void\n let fail!: (err: unknown) => void\n const output = new Promise<TRunOutput>((resolve, reject) => {\n settle = resolve\n fail = reject\n })\n // The output promise rejects when the stream errors; if the caller ignores\n // `output` (chat UX) the rejection is still observed by the events iterator\n // throwing. Attach a no-op catch so an ignored rejection is never \"unhandled\".\n output.catch(() => {})\n\n const spec: AgentRunSpec<unknown> = {\n profile,\n taskToPrompt: () => message,\n name: agentRunName,\n ...(options.sandboxOverrides ? { sandboxOverrides: options.sandboxOverrides } : {}),\n }\n\n async function* events(): AsyncIterable<RuntimeStreamEvent> {\n try {\n const box = await createSandboxForSpec(options.sandboxClient, spec, signal)\n for await (const event of box.streamPrompt(message, { signal })) {\n raw.push(event)\n const mapped = mapEvent(event, { agentRunName })\n if (mapped) yield mapped\n }\n settle(options.output.parse(raw))\n } catch (err) {\n fail(err)\n throw err\n }\n }\n\n return { events: events(), output }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  import { TraceAnalystByteBudgets, TraceAnalysisStore } from '@tangle-network/agent-eval';
2
- import { I as Iteration } from './types-CUzjRFZ3.js';
2
+ import { I as Iteration } from './types-Crxftafi.js';
3
3
  import { R as RunAnalystLoopOpts, a as RunAnalystLoopResult } from './types-p8dWBIXL.js';
4
4
  export { A as AnalystLoopEvent, b as AnalystRegistryLike, c as AnalystRegistryStreamingLike, d as AutoApplyPolicy, F as FindingsStoreLike, I as ImprovementAdapter, e as ImprovementEditBatch, f as ImprovementReport, K as KnowledgeAdapter, g as KnowledgeProposalBatch, h as KnowledgeReport } from './types-p8dWBIXL.js';
5
5
  import '@tangle-network/sandbox';
@@ -2,11 +2,17 @@ import {
2
2
  createKbGate
3
3
  } from "./chunk-FNMGYYSS.js";
4
4
  import {
5
- createDefaultCoderDelegate
6
- } from "./chunk-4FEUFYOY.js";
5
+ detachedSessionDelegate
6
+ } from "./chunk-IBRJTG7O.js";
7
7
  import {
8
8
  runAnalystLoop
9
9
  } from "./chunk-P5OKDSLB.js";
10
+ import {
11
+ createExecutorRegistry,
12
+ definePersona,
13
+ runPersonified,
14
+ worktreeFanout
15
+ } from "./chunk-VCOT7XEQ.js";
10
16
  import {
11
17
  ConfigError
12
18
  } from "./chunk-VLF5RHEQ.js";
@@ -42,7 +48,7 @@ async function runDelegatedLoop(mode, registry, options = {}) {
42
48
  }
43
49
  }
44
50
  function coderLoopRunner(options) {
45
- const delegate = createDefaultCoderDelegate({
51
+ const delegate = detachedSessionDelegate({
46
52
  sandboxClient: options.sandboxClient,
47
53
  ...options.reviewer ? { reviewer: options.reviewer } : {},
48
54
  ...options.winnerSelection ? { winnerSelection: options.winnerSelection } : {},
@@ -54,8 +60,42 @@ function coderLoopRunner(options) {
54
60
  return delegate(options.args, ctx);
55
61
  };
56
62
  }
57
- function reviewLoopRunner(options) {
58
- return coderLoopRunner(options);
63
+ function worktreeLoopRunner(options) {
64
+ const shape = worktreeFanout({
65
+ repoRoot: options.repoRoot,
66
+ taskPrompt: options.taskPrompt,
67
+ harnesses: options.harnesses,
68
+ ...options.testCmd !== void 0 ? { testCmd: options.testCmd } : {},
69
+ ...options.typecheckCmd !== void 0 ? { typecheckCmd: options.typecheckCmd } : {},
70
+ ...options.require !== void 0 ? { require: options.require } : {},
71
+ ...options.maxDiffLines !== void 0 ? { maxDiffLines: options.maxDiffLines } : {},
72
+ ...options.forbiddenPaths !== void 0 ? { forbiddenPaths: options.forbiddenPaths } : {},
73
+ ...options.winnerStrategy !== void 0 ? { winnerStrategy: options.winnerStrategy } : {},
74
+ ...options.runGit ? { runGit: options.runGit } : {},
75
+ ...options.runHarness ? { runHarness: options.runHarness } : {},
76
+ ...options.runCommand ? { runCommand: options.runCommand } : {}
77
+ });
78
+ const persona = definePersona({
79
+ name: "worktree-coder",
80
+ root: { profile: { name: "worktree-coder" }, harness: null },
81
+ directive: "deliver a minimal validated patch on a fresh worktree",
82
+ context: { role: "coder" },
83
+ executors: { registry: createExecutorRegistry() }
84
+ });
85
+ return async (signal) => {
86
+ const result = await runPersonified({
87
+ persona,
88
+ shape,
89
+ task: options.taskPrompt,
90
+ budget: options.budget,
91
+ signal
92
+ });
93
+ if (result.kind !== "winner" || result.out.kind !== "done") {
94
+ const blockers = result.kind === "winner" && result.out.kind === "blocked" ? result.out.blockers.join("; ") : `supervisor settled ${result.kind}`;
95
+ throw new Error(`worktreeLoopRunner: no delivered patch (${blockers})`);
96
+ }
97
+ return result.out.deliverable;
98
+ };
59
99
  }
60
100
  function researchLoopRunner(o) {
61
101
  const gate = createKbGate(o.gate);
@@ -164,11 +204,11 @@ export {
164
204
  isDelegatedLoopMode,
165
205
  runDelegatedLoop,
166
206
  coderLoopRunner,
167
- reviewLoopRunner,
207
+ worktreeLoopRunner,
168
208
  researchLoopRunner,
169
209
  selfImproveLoopRunner,
170
210
  auditLoopRunner,
171
211
  runLoopRunnerCli,
172
212
  parseLoopRunnerArgv
173
213
  };
174
- //# sourceMappingURL=chunk-IN7WHMGZ.js.map
214
+ //# sourceMappingURL=chunk-5V343QPB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/loop-runner.ts","../src/loop-runner-bin.ts"],"sourcesContent":["/**\n * @experimental\n *\n * `runDelegatedLoop` — the configured delegated loop-runner.\n *\n * One typed entrypoint a worker agent (or a scheduled routine) calls to run a\n * disciplined loop in a chosen MODE, over agent-runtime's hardened engines:\n *\n * code → build-in-a-loop via the coder delegate (no-op + secret floor,\n * optional reviewer gate, winner-selection)\n * review → code mode with a REQUIRED reviewer (the gate is the point)\n * research → research-in-a-loop with valid-only KB growth (createKbGate)\n * audit → analyze trace/run data → findings (runAnalystLoop, caller-wired)\n * self-improve → closed-loop text/config optimization (selfImprove, held-out gated)\n *\n * It is intentionally a thin façade: the value is that EVERY product reuses the\n * one hardened engine instead of forking delegation logic. The dispatcher owns\n * mode routing, timing, fail-loud on an unregistered mode, and a uniform result\n * shape; each mode's engine is a pre-configured runner in the registry (build it\n * with the factories below, or inject your own / a stub).\n */\n\nimport type { Scenario } from '@tangle-network/agent-eval/campaign'\nimport {\n type SelfImproveOptions,\n type SelfImproveResult,\n selfImprove,\n} from '@tangle-network/agent-eval/contract'\nimport { runAnalystLoop } from './analyst-loop'\nimport type { RunAnalystLoopOpts, RunAnalystLoopResult } from './analyst-loop/types'\nimport { ConfigError } from './errors'\nimport {\n type CoderReviewer,\n type DelegateRunCtx,\n type DetachedWinnerSelection,\n detachedSessionDelegate,\n} from './mcp/delegates'\nimport type { CoderOutput } from './mcp/detached-coder'\nimport { type CreateKbGateOptions, createKbGate, type FactCandidate } from './mcp/kb-gate'\nimport type { DelegateCodeArgs } from './mcp/types'\nimport {\n type AuthoredHarness,\n type Budget,\n createExecutorRegistry,\n definePersona,\n runPersonified,\n type SandboxClient,\n type WinnerStrategy,\n type WorktreeFanoutOptions,\n type WorktreePatchArtifact,\n worktreeFanout,\n} from './runtime'\n\n/** @experimental Every delegated-loop mode, for validation + CLI surfaces. */\nexport const DELEGATED_LOOP_MODES = ['code', 'review', 'research', 'audit', 'self-improve'] as const\n\n/** @experimental */\nexport type DelegatedLoopMode = (typeof DELEGATED_LOOP_MODES)[number]\n\n/** @experimental Type guard for an untrusted mode string (CLI / config input). */\nexport function isDelegatedLoopMode(value: unknown): value is DelegatedLoopMode {\n return typeof value === 'string' && (DELEGATED_LOOP_MODES as readonly string[]).includes(value)\n}\n\n/** @experimental A pre-configured loop for one mode. Returns the mode's raw\n * output; the dispatcher wraps it in a {@link DelegatedLoopResult}. */\nexport type DelegatedLoopRunner<T = unknown> = (signal: AbortSignal) => Promise<T>\n\n/** @experimental Mode → configured runner. Partial: only register the modes a\n * given product/routine actually uses. */\nexport type DelegatedLoopRegistry = Partial<Record<DelegatedLoopMode, DelegatedLoopRunner>>\n\n/** @experimental Uniform result — never throws from a registered runner; a\n * thrown engine becomes `{ ok: false, error }` so a routine can record + move on. */\nexport interface DelegatedLoopResult<T = unknown> {\n mode: DelegatedLoopMode\n ok: boolean\n output?: T\n error?: string\n durationMs: number\n}\n\n/** @experimental */\nexport interface RunDelegatedLoopOptions {\n signal?: AbortSignal\n /** Clock override for deterministic tests. */\n now?: () => number\n}\n\n/**\n * @experimental\n *\n * Dispatch a configured loop by mode. Fails loud (throws `ConfigError`) when no\n * runner is registered for the mode — a routine pointed at an unwired mode is a\n * config bug, not a silent no-op. A runner that throws is captured as\n * `{ ok: false }` so unattended runs record the failure rather than crash.\n */\nexport async function runDelegatedLoop<T = unknown>(\n mode: DelegatedLoopMode,\n registry: DelegatedLoopRegistry,\n options: RunDelegatedLoopOptions = {},\n): Promise<DelegatedLoopResult<T>> {\n const runner = registry[mode] as DelegatedLoopRunner<T> | undefined\n if (!runner) {\n throw new ConfigError(\n `runDelegatedLoop: no runner registered for mode '${mode}' (registered: ${\n Object.keys(registry).join(', ') || 'none'\n })`,\n )\n }\n const now = options.now ?? Date.now\n const signal = options.signal ?? new AbortController().signal\n const start = now()\n try {\n const output = await runner(signal)\n return { mode, ok: true, output, durationMs: now() - start }\n } catch (err) {\n return {\n mode,\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n durationMs: now() - start,\n }\n }\n}\n\n/** @experimental Options for the default `code`/`review` runner. */\nexport interface CoderLoopRunnerOptions {\n sandboxClient: SandboxClient\n /** What to build — the delegate args (goal, repoRoot, variants, config, …). */\n args: DelegateCodeArgs\n /** Adversarial reviewer. Pass one to run `review` mode (an approval gate over the candidate). */\n reviewer?: CoderReviewer\n /** Winner-selection strategy. Default `highest-score`. */\n winnerSelection?: DetachedWinnerSelection\n /** Harnesses for `variants > 1` fanout. */\n fanoutHarnesses?: string[]\n}\n\n/**\n * @experimental Build a `code`/`review`-mode runner over the sandbox-session coder delegate. Pass a\n * `reviewer` to run `review` mode — an approval gate over the validated candidate.\n */\nexport function coderLoopRunner(options: CoderLoopRunnerOptions): DelegatedLoopRunner<CoderOutput> {\n const delegate = detachedSessionDelegate({\n sandboxClient: options.sandboxClient,\n ...(options.reviewer ? { reviewer: options.reviewer } : {}),\n ...(options.winnerSelection ? { winnerSelection: options.winnerSelection } : {}),\n ...(options.fanoutHarnesses ? { fanoutHarnesses: options.fanoutHarnesses } : {}),\n })\n return async (signal) => {\n const ctx: DelegateRunCtx = { signal, report: () => {} }\n return delegate(options.args, ctx)\n }\n}\n\n/** @experimental Options for the local-repo `code` runner over the GENERIC recursive path. */\nexport interface WorktreeLoopRunnerOptions {\n /** Absolute path to the local git checkout each worktree is cut from. */\n repoRoot: string\n /** The instruction handed to every authored harness (composed under each profile's systemPrompt). */\n taskPrompt: string\n /** The supervisor-authored harness profiles — one fanout item (one worktree-CLI leaf) each. */\n harnesses: ReadonlyArray<AuthoredHarness>\n /** Conserved budget pool bounding the fanout (equal-k holds by construction). */\n budget: Budget\n /** Shell command run in each worktree to derive the tests-PASS signal. */\n testCmd?: string\n /** Shell command run in each worktree to derive the typecheck-PASS signal. */\n typecheckCmd?: string\n /** Which verification signals the deliverable REQUIRES present-and-passing (default none). */\n require?: ReadonlyArray<'tests' | 'typecheck'>\n /** Diff-size cap (lines). */\n maxDiffLines?: number\n /** Literal path prefixes the patch must not touch (the secret-floor is always on regardless). */\n forbiddenPaths?: string[]\n /** Winner-selection strategy among gated candidates. Default `highest-score`. */\n winnerStrategy?: WinnerStrategy\n /** Test seams forwarded to the worktree-CLI leaves so the runner drives offline. */\n runGit?: WorktreeFanoutOptions['runGit']\n runHarness?: WorktreeFanoutOptions['runHarness']\n runCommand?: WorktreeFanoutOptions['runCommand']\n}\n\n/**\n * @experimental\n *\n * `code` mode on the GENERIC recursive path: author one `AgentProfile` per harness, run them as a\n * `worktreeFanout` (N `createWorktreeCliExecutor` leaves, each `gateOnDeliverable`) through\n * `runPersonified` on the keystone Supervisor. This is the local-repo counterpart to\n * {@link coderLoopRunner} (which drives the in-box harness over a `SandboxClient`): no `runLoop`\n * driver, no role-coupled delegate — the harness list is the fanout, the gate is `patchDelivered`,\n * the winner is the shared valid-only selector (NOT `defaultSelectWinner`, whose non-valid fallback\n * would surface an ungated patch). Equal-k holds by the conserved budget pool. Returns the winning\n * patch artifact, or throws when no candidate is delivered (fail loud, never a vacuous done).\n */\nexport function worktreeLoopRunner(\n options: WorktreeLoopRunnerOptions,\n): DelegatedLoopRunner<WorktreePatchArtifact> {\n const shape = worktreeFanout<string>({\n repoRoot: options.repoRoot,\n taskPrompt: options.taskPrompt,\n harnesses: options.harnesses,\n ...(options.testCmd !== undefined ? { testCmd: options.testCmd } : {}),\n ...(options.typecheckCmd !== undefined ? { typecheckCmd: options.typecheckCmd } : {}),\n ...(options.require !== undefined ? { require: options.require } : {}),\n ...(options.maxDiffLines !== undefined ? { maxDiffLines: options.maxDiffLines } : {}),\n ...(options.forbiddenPaths !== undefined ? { forbiddenPaths: options.forbiddenPaths } : {}),\n ...(options.winnerStrategy !== undefined ? { winnerStrategy: options.winnerStrategy } : {}),\n ...(options.runGit ? { runGit: options.runGit } : {}),\n ...(options.runHarness ? { runHarness: options.runHarness } : {}),\n ...(options.runCommand ? { runCommand: options.runCommand } : {}),\n })\n // The persona's only role here is to carry the fanout shape onto the Supervisor; each item's\n // executor is BYO (the gated worktree-CLI leaf), so the registry only needs to pass BYO through.\n const persona = definePersona<WorktreePatchArtifact>({\n name: 'worktree-coder',\n root: { profile: { name: 'worktree-coder' }, harness: null },\n directive: 'deliver a minimal validated patch on a fresh worktree',\n context: { role: 'coder' },\n executors: { registry: createExecutorRegistry() },\n })\n return async (signal) => {\n const result = await runPersonified<string, WorktreePatchArtifact>({\n persona,\n shape,\n task: options.taskPrompt,\n budget: options.budget,\n signal,\n })\n if (result.kind !== 'winner' || result.out.kind !== 'done') {\n const blockers =\n result.kind === 'winner' && result.out.kind === 'blocked'\n ? result.out.blockers.join('; ')\n : `supervisor settled ${result.kind}`\n throw new Error(`worktreeLoopRunner: no delivered patch (${blockers})`)\n }\n return result.out.deliverable\n }\n}\n\n/** @experimental A fact rejected at the KB gate — surfaced, never dropped. */\nexport interface VetoedFact {\n candidate: FactCandidate\n vetoedBy?: string\n reason?: string\n}\n\n/** @experimental */\nexport interface ResearchLoopResult {\n /** Facts that passed the fail-closed gate — safe to write to the KB. */\n accepted: FactCandidate[]\n /** Facts the gate vetoed in the final round — escalate, do not silently drop. */\n vetoed: VetoedFact[]\n /** Research rounds actually run. */\n rounds: number\n}\n\n/** @experimental Options for the default `research` runner. */\nexport interface ResearchLoopRunnerOptions {\n /**\n * The research engine (the consumer's web/doc searcher + extractor). Called\n * each round with the prior round's vetoes so it can re-research the gaps.\n * Returns fact candidates carrying their grounding (`verbatimPassage` +\n * `sourceText`).\n */\n research: (round: number, vetoed: VetoedFact[]) => Promise<FactCandidate[]>\n /** Gate config (extra judges, self-artifact kinds, …). The floor is always on. */\n gate?: CreateKbGateOptions\n /** Max research rounds (correct-on-veto remediation). Default 1. */\n maxRounds?: number\n}\n\n/**\n * @experimental `research` mode — research-in-a-loop with valid-only KB growth.\n *\n * Each round: research → gate every candidate (fail-closed; passage MUST be in\n * the source) → accept the clean ones → re-research the vetoed ones next round,\n * up to `maxRounds`. Vetoed facts in the final round are RETURNED (escalate,\n * never silently dropped) so the caller audits vs retries.\n */\nexport function researchLoopRunner(\n o: ResearchLoopRunnerOptions,\n): DelegatedLoopRunner<ResearchLoopResult> {\n const gate = createKbGate(o.gate)\n const maxRounds = Math.max(1, Math.trunc(o.maxRounds ?? 1))\n return async (signal) => {\n const accepted: FactCandidate[] = []\n let vetoed: VetoedFact[] = []\n let rounds = 0\n for (let round = 0; round < maxRounds; round += 1) {\n if (signal.aborted) break\n rounds += 1\n const candidates = await o.research(round, vetoed)\n if (candidates.length === 0) break\n vetoed = []\n for (const c of candidates) {\n const v = await gate(c)\n if (v.accepted) accepted.push(c)\n else vetoed.push({ candidate: c, vetoedBy: v.vetoedBy, reason: v.reason })\n }\n if (vetoed.length === 0) break\n }\n return { accepted, vetoed, rounds }\n }\n}\n\n/** @experimental `self-improve` mode — agent-eval's one-call closed loop (held-out gated). */\nexport function selfImproveLoopRunner<TScenario extends Scenario, TArtifact>(\n options: SelfImproveOptions<TScenario, TArtifact>,\n): DelegatedLoopRunner<SelfImproveResult<TScenario, TArtifact>> {\n return async () => selfImprove<TScenario, TArtifact>(options)\n}\n\n/** @experimental `audit` mode — analyst loop over captured trace/run data. */\nexport function auditLoopRunner<TProposal = unknown, TEdit = unknown>(\n options: RunAnalystLoopOpts,\n): DelegatedLoopRunner<RunAnalystLoopResult<TProposal, TEdit>> {\n return async () => runAnalystLoop<TProposal, TEdit>(options)\n}\n","#!/usr/bin/env node\n/**\n * @experimental\n *\n * `agent-runtime-loop` — the schedulable entrypoint for the configured\n * delegated loop-runner. A cron job / routine / Makefile target invokes:\n *\n * agent-runtime-loop --mode research --config ./loops.config.js\n *\n * The config module wires the registry (with full access to env / creds —\n * which is why the deps live there, not in this generic bin). It must default-\n * export a `DelegatedLoopRegistry`, or a `() => DelegatedLoopRegistry | Promise<…>`.\n * The bin runs the selected mode, prints the `DelegatedLoopResult` as JSON, and\n * exits 0 on `ok`, 1 on a recorded failure, 2 on a usage/config error.\n */\n\nimport {\n DELEGATED_LOOP_MODES,\n type DelegatedLoopMode,\n type DelegatedLoopRegistry,\n type DelegatedLoopResult,\n isDelegatedLoopMode,\n runDelegatedLoop,\n} from './loop-runner'\n\n/** @experimental Parsed CLI invocation. */\nexport interface LoopRunnerCliArgs {\n mode: string\n /** Loads the registry — the bin wires this from `--config`; tests inject a stub. */\n loadRegistry: () => Promise<DelegatedLoopRegistry> | DelegatedLoopRegistry\n now?: () => number\n}\n\n/** @experimental */\nexport interface LoopRunnerCliResult {\n exitCode: number\n result?: DelegatedLoopResult\n error?: string\n}\n\n/**\n * @experimental\n *\n * Pure CLI core (no process / argv / IO) so it's unit-testable: validate the\n * mode, load the registry, dispatch, map to an exit code (0 ok / 1 failed /\n * 2 usage). Exported for embedding in custom runners + tests.\n */\nexport async function runLoopRunnerCli(args: LoopRunnerCliArgs): Promise<LoopRunnerCliResult> {\n if (!isDelegatedLoopMode(args.mode)) {\n return {\n exitCode: 2,\n error: `unknown mode '${args.mode}' (expected one of: ${DELEGATED_LOOP_MODES.join(', ')})`,\n }\n }\n let registry: DelegatedLoopRegistry\n try {\n registry = await args.loadRegistry()\n } catch (err) {\n return { exitCode: 2, error: `failed to load registry: ${errMsg(err)}` }\n }\n if (!registry[args.mode]) {\n return {\n exitCode: 2,\n error: `config registers no runner for mode '${args.mode}' (registered: ${\n Object.keys(registry).join(', ') || 'none'\n })`,\n }\n }\n // runDelegatedLoop throws only on a missing runner (guarded above); a failing\n // engine is captured as { ok: false } → exit 1, not a crash.\n const result = await runDelegatedLoop(args.mode as DelegatedLoopMode, registry, {\n ...(args.now ? { now: args.now } : {}),\n })\n return { exitCode: result.ok ? 0 : 1, result }\n}\n\n/** Parse `--mode X --config Y` from an argv tail (`process.argv.slice(2)`). */\nexport function parseLoopRunnerArgv(argv: string[]): { mode?: string; config?: string } {\n const out: { mode?: string; config?: string } = {}\n for (let i = 0; i < argv.length; i += 1) {\n const a = argv[i]\n if (a === '--mode') out.mode = argv[++i]\n else if (a === '--config') out.config = argv[++i]\n else if (a?.startsWith('--mode=')) out.mode = a.slice('--mode='.length)\n else if (a?.startsWith('--config=')) out.config = a.slice('--config='.length)\n }\n return out\n}\n\n/** Normalize a config module's default export → a registry. */\nfunction resolveRegistry(mod: unknown): DelegatedLoopRegistry {\n const def = (mod as { default?: unknown })?.default ?? mod\n const value = typeof def === 'function' ? (def as () => unknown)() : def\n return value as DelegatedLoopRegistry\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\n/** The argv → IO → exit shell. Kept thin; logic lives in `runLoopRunnerCli`. */\nasync function main(): Promise<void> {\n const { mode, config } = parseLoopRunnerArgv(process.argv.slice(2))\n if (!mode || !config) {\n process.stderr.write(\n 'usage: agent-runtime-loop --mode <mode> --config <module>\\n' +\n ` modes: ${DELEGATED_LOOP_MODES.join(' | ')}\\n` +\n ' config: a JS/TS module default-exporting a DelegatedLoopRegistry (or a factory)\\n',\n )\n process.exit(2)\n }\n const { pathToFileURL } = await import('node:url')\n const { resolve } = await import('node:path')\n const cli = await runLoopRunnerCli({\n mode,\n loadRegistry: async () => resolveRegistry(await import(pathToFileURL(resolve(config)).href)),\n })\n process.stdout.write(`${JSON.stringify(cli.result ?? { error: cli.error }, null, 2)}\\n`)\n if (cli.error) process.stderr.write(`${cli.error}\\n`)\n process.exit(cli.exitCode)\n}\n\n// Run only when executed as the bin — never when imported for the testable\n// core, and never when bundled into a runtime that has no `process.argv`\n// (e.g. Cloudflare Workers, where `process` is a shim without `argv`). Reading\n// `process.argv[1]` directly would throw at module load there; `process.argv?.`\n// keeps the guard a no-op instead of crashing the Worker on startup.\nconst invokedScript = typeof process !== 'undefined' ? process.argv?.[1] : undefined\nif (invokedScript && /loop-runner-bin\\.(js|ts|mjs)$/.test(invokedScript)) {\n void main()\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,59 @@
1
+ // src/profiles/coder.ts
2
+ var DEFAULT_MAX_DIFF_LINES = 400;
3
+ var DEFAULT_CODER_SYSTEM_PROMPT = [
4
+ "You are a coder agent operating inside an isolated sandbox workspace.",
5
+ "Your job is to deliver a minimal, correct patch for the user-supplied goal.",
6
+ "",
7
+ "Hard rules:",
8
+ " 1. Work on a fresh branch off the supplied base. Do not mutate the base branch.",
9
+ " 2. Never touch a forbidden path. The user will list them explicitly.",
10
+ " 3. Keep the diff under the max-diff cap. Prefer the smallest change that ships.",
11
+ " 4. Run the supplied test and typecheck commands before declaring done.",
12
+ " 5. If either command fails, fix the cause \u2014 do not weaken the test or hide the error.",
13
+ "",
14
+ "When you finish, emit a single final structured message of the shape:",
15
+ " ```json",
16
+ ' { "branch": "<branch-name>",',
17
+ ' "patch": "<unified-diff>",',
18
+ ' "testResult": { "passed": <bool>, "output": "<stdout/stderr>" },',
19
+ ' "typecheckResult": { "passed": <bool>, "output": "<stdout/stderr>" },',
20
+ ' "diffStats": { "filesChanged": <int>, "insertions": <int>, "deletions": <int> },',
21
+ ' "reviewerNotes": "<optional commentary>" }',
22
+ " ```"
23
+ ].join("\n");
24
+ var coderProfile = {
25
+ name: "coder",
26
+ description: "Code-modification agent. Minimal-diff worktree-based coder.",
27
+ prompt: { systemPrompt: DEFAULT_CODER_SYSTEM_PROMPT },
28
+ tools: { git: true, fs: true, shell: true, test_runner: true },
29
+ metadata: { role: "coder" }
30
+ };
31
+ function coderTaskToPrompt(task) {
32
+ const base = task.baseBranch ?? "main";
33
+ const testCmd = task.testCmd ?? "pnpm test --run";
34
+ const typecheckCmd = task.typecheckCmd ?? "pnpm typecheck";
35
+ const maxDiff = task.maxDiffLines ?? DEFAULT_MAX_DIFF_LINES;
36
+ const forbidden = task.forbiddenPaths?.length ? task.forbiddenPaths.join(", ") : "(none)";
37
+ const context = task.contextFiles?.length ? task.contextFiles.map((f) => ` - ${f}`).join("\n") : " (none)";
38
+ return [
39
+ `Goal: ${task.goal}`,
40
+ `Repo: ${task.repoRoot}`,
41
+ `Base branch: ${base}`,
42
+ `Run tests with: ${testCmd}`,
43
+ `Run typecheck with: ${typecheckCmd}`,
44
+ `Forbidden paths: ${forbidden}`,
45
+ `Max diff lines: ${maxDiff}`,
46
+ "Context files:",
47
+ context,
48
+ "",
49
+ "Produce a minimal patch on a fresh branch. Run tests and typecheck before",
50
+ "returning. Emit the final JSON result block exactly as instructed."
51
+ ].join("\n");
52
+ }
53
+
54
+ export {
55
+ DEFAULT_CODER_SYSTEM_PROMPT,
56
+ coderProfile,
57
+ coderTaskToPrompt
58
+ };
59
+ //# sourceMappingURL=chunk-7IXF3VUJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/profiles/coder.ts"],"sourcesContent":["/**\n * @experimental\n *\n * `coderProfile` — the §1.5 author-the-profile DATA for code-modification tasks: an `AgentProfile`\n * constant (the agent IS its profile) plus a pure `coderTaskToPrompt` formatter that renders a\n * `CoderTask` into the per-task instruction. There is no factory, output adapter, or validator\n * here — a domain customizes the worker by authoring a profile + handing it to a leaf executor\n * (`createWorktreeCliExecutor`) or a fanout (`worktreeFanout`), and \"is it delivered\" is a\n * `DeliverableSpec` (`patchDelivered`), not a bundled validator.\n *\n * The standing instruction tells the agent to work on a fresh branch, keep the patch minimal,\n * avoid forbidden paths, and run the test + typecheck commands before declaring done.\n */\n\nimport type { AgentProfile } from '@tangle-network/sandbox'\n\nconst DEFAULT_MAX_DIFF_LINES = 400\n\n/** @experimental The per-task inputs `coderTaskToPrompt` renders + the worktree gate enforces. */\nexport interface CoderTask {\n /** What the agent must accomplish. Free-form prose. */\n goal: string\n /** Absolute path inside the sandbox where the repo lives. */\n repoRoot: string\n /** Default `main`. The branch the agent diffs against. */\n baseBranch?: string\n /** Default `pnpm test --run`. */\n testCmd?: string\n /** Default `pnpm typecheck`. */\n typecheckCmd?: string\n /** Files the agent may inspect for context. Surfaced verbatim in the prompt. */\n contextFiles?: string[]\n /**\n * Paths the agent must not touch. The mechanical gate hard-fails on any match.\n * Use glob-free literal path prefixes for unambiguous enforcement.\n */\n forbiddenPaths?: string[]\n /** Default 400. Hard cap; the gate hard-fails when exceeded. */\n maxDiffLines?: number\n}\n\n/** @experimental The coder agent's standing instruction (its body lives in `coderProfile.prompt`). */\nexport const DEFAULT_CODER_SYSTEM_PROMPT = [\n 'You are a coder agent operating inside an isolated sandbox workspace.',\n 'Your job is to deliver a minimal, correct patch for the user-supplied goal.',\n '',\n 'Hard rules:',\n ' 1. Work on a fresh branch off the supplied base. Do not mutate the base branch.',\n ' 2. Never touch a forbidden path. The user will list them explicitly.',\n ' 3. Keep the diff under the max-diff cap. Prefer the smallest change that ships.',\n ' 4. Run the supplied test and typecheck commands before declaring done.',\n ' 5. If either command fails, fix the cause — do not weaken the test or hide the error.',\n '',\n 'When you finish, emit a single final structured message of the shape:',\n ' ```json',\n ' { \"branch\": \"<branch-name>\",',\n ' \"patch\": \"<unified-diff>\",',\n ' \"testResult\": { \"passed\": <bool>, \"output\": \"<stdout/stderr>\" },',\n ' \"typecheckResult\": { \"passed\": <bool>, \"output\": \"<stdout/stderr>\" },',\n ' \"diffStats\": { \"filesChanged\": <int>, \"insertions\": <int>, \"deletions\": <int> },',\n ' \"reviewerNotes\": \"<optional commentary>\" }',\n ' ```',\n].join('\\n')\n\n/**\n * @experimental\n *\n * The coder `AgentProfile` — the §1.5 DATA the substrate materializes into a harness invocation.\n * Stateless and harness-agnostic: a consumer overrides `model`/`metadata.backendType` by spreading\n * a copy, never by a factory. `worktreeFanout` authors one such profile per harness leaf.\n */\nexport const coderProfile: AgentProfile = {\n name: 'coder',\n description: 'Code-modification agent. Minimal-diff worktree-based coder.',\n prompt: { systemPrompt: DEFAULT_CODER_SYSTEM_PROMPT },\n tools: { git: true, fs: true, shell: true, test_runner: true },\n metadata: { role: 'coder' },\n}\n\n/** @experimental Render a `CoderTask` into the per-task instruction handed to the coder profile. */\nexport function coderTaskToPrompt(task: CoderTask): string {\n const base = task.baseBranch ?? 'main'\n const testCmd = task.testCmd ?? 'pnpm test --run'\n const typecheckCmd = task.typecheckCmd ?? 'pnpm typecheck'\n const maxDiff = task.maxDiffLines ?? DEFAULT_MAX_DIFF_LINES\n const forbidden = task.forbiddenPaths?.length ? task.forbiddenPaths.join(', ') : '(none)'\n const context = task.contextFiles?.length\n ? task.contextFiles.map((f) => ` - ${f}`).join('\\n')\n : ' (none)'\n return [\n `Goal: ${task.goal}`,\n `Repo: ${task.repoRoot}`,\n `Base branch: ${base}`,\n `Run tests with: ${testCmd}`,\n `Run typecheck with: ${typecheckCmd}`,\n `Forbidden paths: ${forbidden}`,\n `Max diff lines: ${maxDiff}`,\n 'Context files:',\n context,\n '',\n 'Produce a minimal patch on a fresh branch. Run tests and typecheck before',\n 'returning. Emit the final JSON result block exactly as instructed.',\n ].join('\\n')\n}\n"],"mappings":";AAgBA,IAAM,yBAAyB;AA0BxB,IAAM,8BAA8B;AAAA,EACzC;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,EAAE,KAAK,IAAI;AASJ,IAAM,eAA6B;AAAA,EACxC,MAAM;AAAA,EACN,aAAa;AAAA,EACb,QAAQ,EAAE,cAAc,4BAA4B;AAAA,EACpD,OAAO,EAAE,KAAK,MAAM,IAAI,MAAM,OAAO,MAAM,aAAa,KAAK;AAAA,EAC7D,UAAU,EAAE,MAAM,QAAQ;AAC5B;AAGO,SAAS,kBAAkB,MAAyB;AACzD,QAAM,OAAO,KAAK,cAAc;AAChC,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,eAAe,KAAK,gBAAgB;AAC1C,QAAM,UAAU,KAAK,gBAAgB;AACrC,QAAM,YAAY,KAAK,gBAAgB,SAAS,KAAK,eAAe,KAAK,IAAI,IAAI;AACjF,QAAM,UAAU,KAAK,cAAc,SAC/B,KAAK,aAAa,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IAClD;AACJ,SAAO;AAAA,IACL,SAAS,KAAK,IAAI;AAAA,IAClB,SAAS,KAAK,QAAQ;AAAA,IACtB,gBAAgB,IAAI;AAAA,IACpB,mBAAmB,OAAO;AAAA,IAC1B,uBAAuB,YAAY;AAAA,IACnC,oBAAoB,SAAS;AAAA,IAC7B,mBAAmB,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;","names":[]}
@@ -14,7 +14,7 @@ import {
14
14
  DELEGATION_STATUS_DESCRIPTION,
15
15
  DELEGATION_STATUS_INPUT_SCHEMA,
16
16
  DELEGATION_STATUS_TOOL_NAME
17
- } from "./chunk-45D64J7B.js";
17
+ } from "./chunk-VCOT7XEQ.js";
18
18
 
19
19
  // src/mcp/openai-tools.ts
20
20
  function buildTool(name, description, parameters) {
@@ -61,4 +61,4 @@ export {
61
61
  mcpToolsForRuntimeMcp,
62
62
  mcpToolsForRuntimeMcpSubset
63
63
  };
64
- //# sourceMappingURL=chunk-ZWGEA722.js.map
64
+ //# sourceMappingURL=chunk-AU5MCNHO.js.map