@tangle-network/agent-eval 0.190.1 → 0.191.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 (157) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/adapters/http.d.ts +2 -2
  3. package/dist/{agent-profile-DNV_U6QR.d.ts → agent-profile-DDfIjdyS.d.ts} +3 -3
  4. package/dist/{agent-profile-DNV_U6QR.d.ts.map → agent-profile-DDfIjdyS.d.ts.map} +1 -1
  5. package/dist/analyst/index.d.ts +11 -11
  6. package/dist/analyst/index.js +5 -5
  7. package/dist/{insight-report-CvPkEIcC.d.ts → analyze-runs-CPiKLJFs.d.ts} +73 -3
  8. package/dist/analyze-runs-CPiKLJFs.d.ts.map +1 -0
  9. package/dist/{attribute-vocabulary-DLJ6303h.d.ts → attribute-vocabulary-CcXqHODT.d.ts} +23 -12
  10. package/dist/attribute-vocabulary-CcXqHODT.d.ts.map +1 -0
  11. package/dist/{benchmark-command-BXIr_k8D.js → benchmark-command-3tZJnD82.js} +8 -8
  12. package/dist/{benchmark-command-BXIr_k8D.js.map → benchmark-command-3tZJnD82.js.map} +1 -1
  13. package/dist/benchmarks/index.d.ts +3 -3
  14. package/dist/benchmarks/index.js +3 -3
  15. package/dist/campaign/index.d.ts +6 -6
  16. package/dist/campaign/index.js +9 -7
  17. package/dist/{campaign-B7q74uIh.js → campaign-C8iWEF0M.js} +10 -8
  18. package/dist/{campaign-B7q74uIh.js.map → campaign-C8iWEF0M.js.map} +1 -1
  19. package/dist/{campaign-evidence-D40-LHIL.js → campaign-evidence-BqjlTM3P.js} +10 -111
  20. package/dist/campaign-evidence-BqjlTM3P.js.map +1 -0
  21. package/dist/claim-V9Zqbq0J.js +54 -0
  22. package/dist/claim-V9Zqbq0J.js.map +1 -0
  23. package/dist/cli.js +99 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/client-wFyDisxi.d.ts +236 -0
  26. package/dist/client-wFyDisxi.d.ts.map +1 -0
  27. package/dist/contract/index.d.ts +10 -11
  28. package/dist/contract/index.js +9 -139
  29. package/dist/contract/index.js.map +1 -1
  30. package/dist/{default-registry-B5HrwrPM.d.ts → default-registry-DK_9aVJf.d.ts} +6 -6
  31. package/dist/{default-registry-B5HrwrPM.d.ts.map → default-registry-DK_9aVJf.d.ts.map} +1 -1
  32. package/dist/{default-registry-16I-Bxlf.js → default-registry-xMLX160E.js} +3 -3
  33. package/dist/{default-registry-16I-Bxlf.js.map → default-registry-xMLX160E.js.map} +1 -1
  34. package/dist/{define-agent-eval-DPizhHfy.js → define-agent-eval-BE43YTm4.js} +27 -109
  35. package/dist/define-agent-eval-BE43YTm4.js.map +1 -0
  36. package/dist/{define-agent-eval-DblGedQk.d.ts → define-agent-eval-P28y7mI1.d.ts} +16 -20
  37. package/dist/define-agent-eval-P28y7mI1.d.ts.map +1 -0
  38. package/dist/diagnosis/index.d.ts +2 -2
  39. package/dist/diagnosis/index.js +1 -1
  40. package/dist/{dspy-rlm-engine-Cs5LzJ-f.js → dspy-rlm-engine-MfpwoEaj.js} +3 -3
  41. package/dist/{dspy-rlm-engine-Cs5LzJ-f.js.map → dspy-rlm-engine-MfpwoEaj.js.map} +1 -1
  42. package/dist/{engine-BkLCnZQX.d.ts → engine-vNiM02Ub.d.ts} +4 -4
  43. package/dist/{engine-BkLCnZQX.d.ts.map → engine-vNiM02Ub.d.ts.map} +1 -1
  44. package/dist/evaluation.d.ts +2 -2
  45. package/dist/{exact-types-CfpP2A4Z.d.ts → exact-types-KGxBWH4b.d.ts} +2 -2
  46. package/dist/{exact-types-CfpP2A4Z.d.ts.map → exact-types-KGxBWH4b.d.ts.map} +1 -1
  47. package/dist/experiment/index.d.ts +1 -1
  48. package/dist/experiment/index.js +2 -1
  49. package/dist/experiment/index.js.map +1 -1
  50. package/dist/{external-optimizer-process-BnSmrkgL.js → external-optimizer-process-xFTliuiK.js} +2 -2
  51. package/dist/{external-optimizer-process-BnSmrkgL.js.map → external-optimizer-process-xFTliuiK.js.map} +1 -1
  52. package/dist/{external-optimizer-subprocess-kbcPu5po.js → external-optimizer-subprocess-B234V9V0.js} +4 -27
  53. package/dist/external-optimizer-subprocess-B234V9V0.js.map +1 -0
  54. package/dist/{extract-usage-BrQ8mCLX.js → extract-usage-DFATEuWW.js} +2 -2
  55. package/dist/{extract-usage-BrQ8mCLX.js.map → extract-usage-DFATEuWW.js.map} +1 -1
  56. package/dist/{feedback-trajectory-BYjh7NEM.d.ts → feedback-trajectory-BXCGTRkz.d.ts} +3 -3
  57. package/dist/{feedback-trajectory-BYjh7NEM.d.ts.map → feedback-trajectory-BXCGTRkz.d.ts.map} +1 -1
  58. package/dist/hosted/index.d.ts +66 -6
  59. package/dist/hosted/index.d.ts.map +1 -1
  60. package/dist/hosted/index.js +2 -2
  61. package/dist/{index-CJv56rhX.d.ts → index-CzfnASj_.d.ts} +18 -11
  62. package/dist/index-CzfnASj_.d.ts.map +1 -0
  63. package/dist/{index-2e_MTznj.d.ts → index-D-TN7ZRM.d.ts} +6 -6
  64. package/dist/{index-2e_MTznj.d.ts.map → index-D-TN7ZRM.d.ts.map} +1 -1
  65. package/dist/{index-CWoIJHmy.d.ts → index-DH-MWH5f.d.ts} +10 -104
  66. package/dist/index-DH-MWH5f.d.ts.map +1 -0
  67. package/dist/index.d.ts +16 -17
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +11 -11
  70. package/dist/jev.d.ts +2 -2
  71. package/dist/{kind-factory-ByZeXDqF.js → kind-factory-CdPPHtGZ.js} +10 -26
  72. package/dist/kind-factory-CdPPHtGZ.js.map +1 -0
  73. package/dist/{llm-judge-BnxAv7ed.js → llm-judge-NJOHM0iA.js} +9 -1426
  74. package/dist/llm-judge-NJOHM0iA.js.map +1 -0
  75. package/dist/{matrix-CNypzP-v.d.ts → matrix-CgqdFm5N.d.ts} +2 -2
  76. package/dist/{matrix-CNypzP-v.d.ts.map → matrix-CgqdFm5N.d.ts.map} +1 -1
  77. package/dist/multishot/golden/index.d.ts +1 -1
  78. package/dist/multishot/index.d.ts +2 -2
  79. package/dist/openapi.json +1 -1
  80. package/dist/{otel-spans-CQY1pNiN.js → otel-spans-DS7t1t1J.js} +8 -19
  81. package/dist/otel-spans-DS7t1t1J.js.map +1 -0
  82. package/dist/otlp-attributes-D9ptWaIJ.js +53 -0
  83. package/dist/otlp-attributes-D9ptWaIJ.js.map +1 -0
  84. package/dist/{prime-protocol-Fwvctmhm.d.ts → prime-protocol-T0i6xcAs.d.ts} +2 -2
  85. package/dist/{prime-protocol-Fwvctmhm.d.ts.map → prime-protocol-T0i6xcAs.d.ts.map} +1 -1
  86. package/dist/{produced-state-BXjXJHJT.js → produced-state-CY-L7CqS.js} +3 -3
  87. package/dist/{produced-state-BXjXJHJT.js.map → produced-state-CY-L7CqS.js.map} +1 -1
  88. package/dist/{registry-mAQVtuLx.d.ts → registry-Bu3lNole.d.ts} +3 -3
  89. package/dist/{registry-mAQVtuLx.d.ts.map → registry-Bu3lNole.d.ts.map} +1 -1
  90. package/dist/rl.d.ts +1 -1
  91. package/dist/rl.js +1 -1
  92. package/dist/{run-record-DnMFyg_g.js → run-record-tNnlQIrD.js} +2 -10
  93. package/dist/{run-record-DnMFyg_g.js.map → run-record-tNnlQIrD.js.map} +1 -1
  94. package/dist/search-ledger-CQ8Uy4FK.js +1451 -0
  95. package/dist/search-ledger-CQ8Uy4FK.js.map +1 -0
  96. package/dist/search-ledger-file-EvdDZRhv.js +27 -0
  97. package/dist/search-ledger-file-EvdDZRhv.js.map +1 -0
  98. package/dist/search-shipper-C55V7HE3.js +781 -0
  99. package/dist/search-shipper-C55V7HE3.js.map +1 -0
  100. package/dist/{semantic-concept-judge-ClB73cei.js → semantic-concept-judge-Bt7bXAAo.js} +3 -3
  101. package/dist/{semantic-concept-judge-ClB73cei.js.map → semantic-concept-judge-Bt7bXAAo.js.map} +1 -1
  102. package/dist/{skillopt-optimization-method-l2vDBtKp.js → skillopt-optimization-method-DwO3TOAV.js} +6 -5
  103. package/dist/{skillopt-optimization-method-l2vDBtKp.js.map → skillopt-optimization-method-DwO3TOAV.js.map} +1 -1
  104. package/dist/{statistical-heldout-CLpjYffJ.d.ts → statistical-heldout-BED3wBtX.d.ts} +2 -2
  105. package/dist/{statistical-heldout-CLpjYffJ.d.ts.map → statistical-heldout-BED3wBtX.d.ts.map} +1 -1
  106. package/dist/{store-otlp-84DH0OEH.js → store-otlp-BPowgPm6.js} +2 -2
  107. package/dist/{store-otlp-84DH0OEH.js.map → store-otlp-BPowgPm6.js.map} +1 -1
  108. package/dist/{store-tool-spans-DUiqyHvR.js → store-tool-spans-BcC_5ef-.js} +5 -5
  109. package/dist/{store-tool-spans-DUiqyHvR.js.map → store-tool-spans-BcC_5ef-.js.map} +1 -1
  110. package/dist/{store-tool-spans-BBXs8hDP.d.ts → store-tool-spans-Cfh9XUCm.d.ts} +5 -9
  111. package/dist/store-tool-spans-Cfh9XUCm.d.ts.map +1 -0
  112. package/dist/{task-failure-attributes-B34TiMBe.js → task-failure-attributes-CEqEfuIY.js} +17 -9
  113. package/dist/task-failure-attributes-CEqEfuIY.js.map +1 -0
  114. package/dist/{tool-groups-W7MJobX_.d.ts → tool-groups-D-mkeN0N.d.ts} +3 -3
  115. package/dist/tool-groups-D-mkeN0N.d.ts.map +1 -0
  116. package/dist/trace-attributes.d.ts +1 -1
  117. package/dist/trace-attributes.js +23 -75
  118. package/dist/trace-attributes.js.map +1 -1
  119. package/dist/trace-repair/index.d.ts +1 -1
  120. package/dist/traces.d.ts +11 -18
  121. package/dist/traces.d.ts.map +1 -1
  122. package/dist/traces.js +16 -16
  123. package/dist/traces.js.map +1 -1
  124. package/dist/{types-BvmWNjjs.d.ts → types-BmQRs661.d.ts} +2 -2
  125. package/dist/{types-BvmWNjjs.d.ts.map → types-BmQRs661.d.ts.map} +1 -1
  126. package/dist/{types-Bay1dh2o.d.ts → types-CeDfCRXp.d.ts} +11 -16
  127. package/dist/types-CeDfCRXp.d.ts.map +1 -0
  128. package/dist/{types-Cvqy7asY.d.ts → types-ClK-sHs8.d.ts} +2 -2
  129. package/dist/{types-Cvqy7asY.d.ts.map → types-ClK-sHs8.d.ts.map} +1 -1
  130. package/dist/wire/index.d.ts +1 -1
  131. package/docs/hosted-ingest-spec.md +92 -133
  132. package/docs/public-api.md +46 -35
  133. package/docs/search-ledger.md +17 -0
  134. package/package.json +2 -2
  135. package/dist/analyze-runs-BvK4-wSs.d.ts +0 -75
  136. package/dist/analyze-runs-BvK4-wSs.d.ts.map +0 -1
  137. package/dist/attribute-vocabulary-DLJ6303h.d.ts.map +0 -1
  138. package/dist/campaign-evidence-D40-LHIL.js.map +0 -1
  139. package/dist/client-BhEyAdfe.d.ts +0 -202
  140. package/dist/client-BhEyAdfe.d.ts.map +0 -1
  141. package/dist/client-QwJpsNYU.js +0 -655
  142. package/dist/client-QwJpsNYU.js.map +0 -1
  143. package/dist/define-agent-eval-DPizhHfy.js.map +0 -1
  144. package/dist/define-agent-eval-DblGedQk.d.ts.map +0 -1
  145. package/dist/external-optimizer-subprocess-kbcPu5po.js.map +0 -1
  146. package/dist/index-CJv56rhX.d.ts.map +0 -1
  147. package/dist/index-CWoIJHmy.d.ts.map +0 -1
  148. package/dist/insight-report-CvPkEIcC.d.ts.map +0 -1
  149. package/dist/kind-factory-ByZeXDqF.js.map +0 -1
  150. package/dist/llm-judge-BnxAv7ed.js.map +0 -1
  151. package/dist/otel-spans-CQY1pNiN.js.map +0 -1
  152. package/dist/otlp-attributes-BpC_blLF.js +0 -83
  153. package/dist/otlp-attributes-BpC_blLF.js.map +0 -1
  154. package/dist/store-tool-spans-BBXs8hDP.d.ts.map +0 -1
  155. package/dist/task-failure-attributes-B34TiMBe.js.map +0 -1
  156. package/dist/tool-groups-W7MJobX_.d.ts.map +0 -1
  157. package/dist/types-Bay1dh2o.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-shipper-C55V7HE3.js","names":[],"sources":["../src/hosted/types.ts","../src/hosted/schemas.ts","../src/hosted/search-ledger-wire.ts","../src/hosted/client.ts","../src/hosted/search-shipper.ts"],"sourcesContent":["/**\n * # Hosted-tier wire format: the shapes every orchestrator (ours, a partner's\n * self-hosted one, a future open implementation) must accept.\n *\n * This package implements exactly one wire version. Servers reject every\n * other version instead of translating old payloads.\n *\n * The wire carries two streams in one transport:\n *\n * 1. **Search ledgers** (`/v1/search-blobs/*`, `/v1/ingest/search-ledger`).\n * A producer ships a search's hash-chained ledger lines and the blobs\n * they name; `./search-ledger-wire.ts` owns those shapes.\n *\n * 2. **Trace spans** (`POST /v1/ingest/traces`). Standard OTLP-shaped\n * spans with a few additional attributes so the orchestrator can\n * pivot from a search cell to its underlying execution. Compatible\n * with any OTel collector.\n *\n * Every endpoint is authenticated with a bearer token and a tenant id\n * header. Tenants isolate everything downstream of ingest; no tenant\n * ever sees another tenant's data.\n */\n\nexport const HOSTED_WIRE_VERSION = '2026-07-24.v1' as const\nexport type HostedWireVersion = typeof HOSTED_WIRE_VERSION\n\n// ── Transport headers ───────────────────────────────────────────────\n\n/** Every ingest request carries these. */\nexport interface HostedIngestHeaders {\n /** Bearer token. The orchestrator validates against the tenant key. */\n authorization: `Bearer ${string}`\n /** Stable tenant id (the orchestrator-side primary key for the tenant). */\n 'x-tangle-tenant-id': string\n /** Wire-version pin so the server can reject incompatible payloads. */\n 'x-tangle-wire-version': HostedWireVersion\n /** Stable request key generated once and reused across retries. */\n 'idempotency-key': string\n}\n\n// ── Trace span event ────────────────────────────────────────────────\n\n/**\n * Canonical unsigned 64-bit integer encoded as a base-10 string.\n * JSON numbers cannot represent OTLP nanosecond timestamps exactly.\n */\nexport type UnixNanoTimestamp = string\n\n/**\n * OTel-shape span with a few additional attributes for pivoting.\n * Compatible with any OTLP collector — `name`, `traceId`, `spanId`,\n * `startTimeUnixNano`, `endTimeUnixNano`, `attributes` are stock OTel.\n */\nexport interface TraceSpanEvent {\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n startTimeUnixNano: UnixNanoTimestamp\n endTimeUnixNano: UnixNanoTimestamp\n attributes: Record<string, string | number | boolean>\n events?: Array<{\n timeUnixNano: UnixNanoTimestamp\n name: string\n attributes?: Record<string, string | number | boolean>\n }>\n status?: { code: 'OK' | 'ERROR' | 'UNSET'; message?: string }\n /** Pivot to the producing run. */\n 'tangle.runId'?: string\n /** Pivot to the specific generation. */\n 'tangle.generation'?: number\n /** Pivot to the specific cell. */\n 'tangle.cellId'?: string\n /** Pivot to the specific scenario. */\n 'tangle.scenarioId'?: string\n}\n\n// ── Ingest request bodies ───────────────────────────────────────────\n\nexport interface IngestTracesRequest {\n wireVersion: HostedWireVersion\n spans: TraceSpanEvent[]\n}\n\nexport interface IngestResponse {\n /** Accepted spans count. */\n accepted: number\n /** Rejected spans with reasons (validation failures, conflicting identity). */\n rejected: Array<{ index: number; reason: string }>\n}\n","import { z } from 'zod'\nimport type {\n IngestResponse,\n IngestTracesRequest,\n TraceSpanEvent,\n UnixNanoTimestamp,\n} from './types'\nimport { HOSTED_WIRE_VERSION } from './types'\n\nconst finiteNumber = z.number().finite()\nconst nonNegativeInteger = z.number().int().nonnegative()\nconst nonEmptyString = z\n .string()\n .min(1)\n .refine((value) => value.trim().length > 0, 'must not be blank')\nconst attributeValue = z.union([z.string(), finiteNumber, z.boolean()])\nconst attributes = z.record(z.string(), attributeValue)\nconst UINT64_MAX = 18_446_744_073_709_551_615n\n\nexport const UnixNanoTimestampSchema: z.ZodType<UnixNanoTimestamp> = z\n .string()\n .regex(/^(0|[1-9][0-9]*)$/, 'expected an unsigned base-10 integer string')\n .refine((value) => BigInt(value) <= UINT64_MAX, 'must fit in an unsigned 64-bit integer')\n\nconst TraceSpanEventEntrySchema = z\n .object({\n timeUnixNano: UnixNanoTimestampSchema,\n name: nonEmptyString,\n attributes: attributes.optional(),\n })\n .strict()\n\nexport const TraceSpanEventSchema: z.ZodType<TraceSpanEvent> = z\n .object({\n traceId: nonEmptyString,\n spanId: nonEmptyString,\n parentSpanId: nonEmptyString.optional(),\n name: nonEmptyString,\n startTimeUnixNano: UnixNanoTimestampSchema,\n endTimeUnixNano: UnixNanoTimestampSchema,\n attributes,\n events: z.array(TraceSpanEventEntrySchema).optional(),\n status: z\n .object({\n code: z.enum(['OK', 'ERROR', 'UNSET']),\n message: z.string().optional(),\n })\n .strict()\n .optional(),\n 'tangle.runId': nonEmptyString.optional(),\n 'tangle.generation': nonNegativeInteger.optional(),\n 'tangle.cellId': nonEmptyString.optional(),\n 'tangle.scenarioId': nonEmptyString.optional(),\n })\n .strict()\n .refine((span) => BigInt(span.endTimeUnixNano) >= BigInt(span.startTimeUnixNano), {\n path: ['endTimeUnixNano'],\n message: 'endTimeUnixNano must be greater than or equal to startTimeUnixNano',\n })\n\nexport const IngestTracesEnvelopeSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(z.unknown()),\n })\n .strict()\n\nexport const IngestTracesRequestSchema: z.ZodType<IngestTracesRequest> = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(TraceSpanEventSchema),\n })\n .strict()\n\nexport const IngestResponseSchema: z.ZodType<IngestResponse> = z\n .object({\n accepted: nonNegativeInteger,\n rejected: z.array(\n z\n .object({\n index: nonNegativeInteger,\n reason: nonEmptyString,\n })\n .strict(),\n ),\n })\n .strict()\n","/**\n * The search-ledger wire: how a producer ships a search ledger to a hosted\n * store, and the rule a store applies to each batch.\n *\n * A producer ships three things, in this order:\n *\n * 1. Blobs: `PUT /v1/search-blobs/<sha256 hex>` with the exact bytes an\n * entry's artifact ref names. The store re-hashes them; the call is\n * idempotent by content.\n * 2. Head: `GET /v1/ingest/search-ledger/<searchId>/head` tells a restarted\n * producer where the store's copy of the chain ends.\n * 3. Entries: `POST /v1/ingest/search-ledger` with canonical ledger lines,\n * starting at `fromSequence`. The store verifies every line, links it to\n * its stored head, and answers with its new head.\n *\n * The lines are the ledger's own bytes, so producer and store hash identical\n * input. A line below the store's head with an equal hash is a no-op; a\n * different hash is a fork (`409 chain_conflict`); a batch that starts past\n * the head is a gap (`409 sequence_gap`). Both 409s carry the store's head,\n * so a producer resends from there or stops on a fork.\n */\n\nimport { z } from 'zod'\nimport { parseSearchLedgerLine } from '../campaign/search-ledger'\nimport { SearchLedgerIntegrityError } from '../campaign/search-ledger-errors'\nimport type {\n SearchArtifactRef,\n SearchLedgerEntry,\n SearchLedgerEvent,\n SearchLedgerHash,\n} from '../campaign/search-ledger-types'\nimport { HOSTED_WIRE_VERSION } from './types'\n\n/** At most this many lines in one ingest request. */\nexport const SEARCH_LEDGER_BATCH_MAX_LINES = 1000\n/** At most this many UTF-8 bytes across one request's lines. A single line\n * larger than this cannot be shipped. */\nexport const SEARCH_LEDGER_BATCH_MAX_BYTES = 1_048_576\n\nexport const SEARCH_LEDGER_INGEST_PATH = '/v1/ingest/search-ledger'\n\nexport function searchLedgerHeadPath(searchId: string): string {\n return `${SEARCH_LEDGER_INGEST_PATH}/${encodeURIComponent(searchId)}/head`\n}\n\n/** Blobs are addressed by the hex digest alone. */\nexport function searchBlobPath(sha256: SearchLedgerHash): string {\n return `/v1/search-blobs/${sha256.slice('sha256:'.length)}`\n}\n\nconst HASH = z.custom<SearchLedgerHash>(\n (value) => typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value),\n 'expected sha256:<64 lowercase hex characters>',\n)\nconst SEARCH_ID = z\n .string()\n .min(1)\n .refine((value) => value.trim() === value, 'must not contain surrounding whitespace')\nconst SEQUENCE = z.number().int().nonnegative().safe()\n\n/** What the run is for: an optimization, or an eval (a one-node search). */\nexport const SearchRunKindSchema = z.enum(['optimization', 'eval'])\nexport type SearchRunKind = z.infer<typeof SearchRunKindSchema>\n\n/** Where a store's copy of one search's chain ends. `headHash` is null exactly\n * when the store holds no entry. */\nexport const SearchLedgerHeadSchema = z\n .object({ searchId: SEARCH_ID, nextSequence: SEQUENCE, headHash: HASH.nullable() })\n .strict()\n .refine((head) => (head.nextSequence === 0) === (head.headHash === null), {\n message: 'headHash must be null exactly when nextSequence is 0',\n path: ['headHash'],\n })\nexport type SearchLedgerHead = z.infer<typeof SearchLedgerHeadSchema>\n\nexport const IngestSearchLedgerRequestSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n searchId: SEARCH_ID,\n runKind: SearchRunKindSchema,\n fromSequence: SEQUENCE,\n lines: z.array(z.string().min(1)).min(1).max(SEARCH_LEDGER_BATCH_MAX_LINES),\n })\n .strict()\n .superRefine((request, ctx) => {\n const bytes = searchLedgerLinesByteLength(request.lines)\n if (bytes > SEARCH_LEDGER_BATCH_MAX_BYTES) {\n ctx.addIssue({\n code: 'custom',\n path: ['lines'],\n message: `lines hold ${bytes} bytes; at most ${SEARCH_LEDGER_BATCH_MAX_BYTES} per request`,\n })\n }\n })\nexport type IngestSearchLedgerRequest = z.infer<typeof IngestSearchLedgerRequestSchema>\n\n/** The body of a `409`: a fork or a gap, with the store's head. */\nexport const SearchLedgerConflictSchema = z\n .object({\n error: z.enum(['chain_conflict', 'sequence_gap']),\n message: z.string().min(1),\n head: SearchLedgerHeadSchema,\n })\n .strict()\nexport type SearchLedgerConflict = z.infer<typeof SearchLedgerConflictSchema>\n\n/** What a store kept of one blob. `masked` and `withheld` come from the\n * store's own redaction scan or the tenant's content posture. */\nexport const SearchBlobPutResponseSchema = z\n .object({\n sha256: HASH,\n byteLength: SEQUENCE,\n state: z.enum(['stored', 'masked', 'withheld']),\n })\n .strict()\nexport type SearchBlobPutResponse = z.infer<typeof SearchBlobPutResponseSchema>\n\nconst UTF8 = new TextEncoder()\n\nexport function searchLedgerLinesByteLength(lines: readonly string[]): number {\n let bytes = 0\n for (const line of lines) bytes += UTF8.encode(line).byteLength\n return bytes\n}\n\n/** Every blob an event names, once each. Ship these before the event. */\nexport function searchLedgerArtifactRefs(event: SearchLedgerEvent): SearchArtifactRef[] {\n const refs: SearchArtifactRef[] = [...event.artifacts]\n switch (event.kind) {\n case 'node-registered':\n refs.push(event.artifact, ...event.surfaces.map((surface) => surface.artifact))\n break\n case 'edge-recorded':\n for (const ref of [event.rationale, ...event.diffs]) if ('uri' in ref) refs.push(ref)\n break\n case 'cell-settled':\n for (const evidence of event.surfaceEvidence) refs.push(...evidence.evidence)\n break\n default:\n break\n }\n const unique = new Map<string, SearchArtifactRef>()\n for (const ref of refs) if (!unique.has(ref.sha256)) unique.set(ref.sha256, ref)\n return [...unique.values()]\n}\n\nexport type SearchLedgerBatchAdmission =\n | {\n status: 'accepted'\n /** Entries past the stored head, in order, with their exact lines. */\n entries: SearchLedgerEntry[]\n lines: string[]\n /** The head after storing `entries`. */\n head: SearchLedgerHead\n }\n | { status: 'conflict'; conflict: SearchLedgerConflict }\n\nexport interface AdmitSearchLedgerBatchInput {\n request: IngestSearchLedgerRequest\n /** The store's head for `request.searchId` before this batch. */\n head: SearchLedgerHead\n /** The stored hash at a sequence below the head. */\n storedEntryHash(sequence: number): SearchLedgerHash | undefined\n}\n\n/**\n * Apply the wire's chain rule to one batch. Each line is verified in isolation\n * (`parseSearchLedgerLine`) and linked to the stored head. A malformed line\n * throws `SearchLedgerIntegrityError`, which a store answers with 422; the\n * state machine (`SearchState`) runs after admission, on `entries`.\n */\nexport function admitSearchLedgerBatch(\n input: AdmitSearchLedgerBatchInput,\n): SearchLedgerBatchAdmission {\n const { request, head } = input\n if (request.searchId !== head.searchId) {\n throw new Error(`head is for search ${head.searchId}, request is for ${request.searchId}`)\n }\n if (request.fromSequence > head.nextSequence) {\n return conflict(\n 'sequence_gap',\n `batch starts at sequence ${request.fromSequence}; the store expects ${head.nextSequence}`,\n head,\n )\n }\n const entries: SearchLedgerEntry[] = []\n const lines: string[] = []\n let previousHash = head.headHash\n for (let index = 0; index < request.lines.length; index++) {\n const sequence = request.fromSequence + index\n const line = request.lines[index]!\n const entry = parseSearchLedgerLine(line, request.searchId, {\n path: `search ${request.searchId}`,\n line: sequence + 1,\n })\n if (entry.sequence !== sequence) {\n throw new SearchLedgerIntegrityError(\n `line ${index} of the batch holds sequence ${entry.sequence}, expected ${sequence}`,\n )\n }\n if (sequence < head.nextSequence) {\n if (input.storedEntryHash(sequence) !== entry.entryHash) {\n return conflict(\n 'chain_conflict',\n `sequence ${sequence} differs from the stored entry; the ledger forked`,\n head,\n )\n }\n continue\n }\n if (entry.previousHash !== previousHash) {\n if (sequence === head.nextSequence) {\n return conflict(\n 'chain_conflict',\n `sequence ${sequence} does not extend the stored head; the ledger forked`,\n head,\n )\n }\n throw new SearchLedgerIntegrityError(\n `sequence ${sequence} does not extend sequence ${sequence - 1} in the same batch`,\n )\n }\n entries.push(entry)\n lines.push(line)\n previousHash = entry.entryHash\n }\n const last = entries.at(-1)\n return {\n status: 'accepted',\n entries,\n lines,\n head: last\n ? { searchId: head.searchId, nextSequence: last.sequence + 1, headHash: last.entryHash }\n : head,\n }\n}\n\nfunction conflict(\n error: SearchLedgerConflict['error'],\n message: string,\n head: SearchLedgerHead,\n): SearchLedgerBatchAdmission {\n return { status: 'conflict', conflict: { error, message, head } }\n}\n","/**\n * # Hosted-tier ingest client.\n *\n * Ships search ledgers and trace spans to any orchestrator (ours, a\n * partner's self-hosted one, or a future open implementation) that speaks\n * the wire format in `./types.ts` and `./search-ledger-wire.ts`.\n *\n * Three modes:\n * - **Ours:** point at `https://orchestrator.tangle.tools` (the host root —\n * the client appends the versioned `/v1/...` path itself; a trailing\n * `/v1` on the endpoint is tolerated and normalized away).\n * - **Self-hosted:** point at whatever URL runs the reference receiver\n * from `examples/hosted-ingest-server/`.\n * - **Off (default):** when no tenant is configured, nothing is sent.\n * Everything stays local.\n *\n * Every call retries network errors, 408, 429 and 5xx. When the server sends\n * `Retry-After`, the client waits exactly that long before the next attempt.\n */\n\nimport type { z } from 'zod'\nimport { IngestResponseSchema, IngestTracesRequestSchema } from './schemas'\nimport {\n type IngestSearchLedgerRequest,\n IngestSearchLedgerRequestSchema,\n SEARCH_LEDGER_INGEST_PATH,\n type SearchBlobPutResponse,\n SearchBlobPutResponseSchema,\n type SearchLedgerConflict,\n SearchLedgerConflictSchema,\n type SearchLedgerHead,\n SearchLedgerHeadSchema,\n searchBlobPath,\n searchLedgerHeadPath,\n} from './search-ledger-wire'\nimport {\n HOSTED_WIRE_VERSION,\n type HostedWireVersion,\n type IngestResponse,\n type IngestTracesRequest,\n type TraceSpanEvent,\n} from './types'\n\nexport interface HostedTenant {\n /** Orchestrator endpoint base URL (no trailing slash). Required. */\n endpoint: string\n /** Bearer token issued by the orchestrator. Required. */\n apiKey: string\n /** Tenant id — the orchestrator's primary key for this consumer. Required. */\n tenantId: string\n /** Optional `fetch` override (auth wrappers, custom agent). */\n fetchImpl?: typeof fetch\n /** Per-attempt timeout in ms. Default 30s. */\n timeoutMs?: number\n /** Retries on network errors, 408, 429 and 5xx. Default 2. */\n retries?: number\n}\n\n/** A search-ledger batch the store accepted, or the fork or gap it refused. */\nexport type SearchLedgerIngestOutcome =\n | { status: 'accepted'; head: SearchLedgerHead }\n | { status: 'conflict'; conflict: SearchLedgerConflict }\n\nexport interface HostedClient {\n ingestTraces(spans: TraceSpanEvent[], idempotencyKey?: string): Promise<IngestResponse>\n /** The store's head for one search; `nextSequence` 0 when it holds nothing. */\n searchLedgerHead(searchId: string, signal?: AbortSignal): Promise<SearchLedgerHead>\n ingestSearchLedger(\n request: IngestSearchLedgerRequest,\n signal?: AbortSignal,\n ): Promise<SearchLedgerIngestOutcome>\n putSearchBlob(\n sha256: `sha256:${string}`,\n bytes: Uint8Array<ArrayBuffer>,\n contentType: string,\n signal?: AbortSignal,\n ): Promise<SearchBlobPutResponse>\n readonly tenant: HostedTenant\n readonly wireVersion: HostedWireVersion\n}\n\n/** A request the server refused, after any retries. */\nexport class HostedRequestError extends Error {\n readonly status: number\n readonly body: string\n constructor(url: string, status: number, body: string) {\n super(`hosted ${url} failed (${status}): ${body.slice(0, 500)}`)\n this.name = 'HostedRequestError'\n this.status = status\n this.body = body\n }\n}\n\ninterface HostedRequest {\n method: 'GET' | 'POST' | 'PUT'\n path: string\n body?: string | Uint8Array<ArrayBuffer>\n contentType?: string\n idempotencyKey?: string\n signal?: AbortSignal\n /** Statuses returned to the caller instead of thrown. */\n expected?: readonly number[]\n}\n\nconst MAX_IDEMPOTENCY_KEY_LENGTH = 256\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason)\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timer)\n reject(signal!.reason)\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/** `Retry-After` as milliseconds: delay-seconds or an HTTP date. Null when\n * absent or unreadable, so the caller falls back to its own backoff. */\nfunction retryAfterMs(value: string | null, now = Date.now()): number | null {\n if (value === null) return null\n const trimmed = value.trim()\n if (/^\\d+$/.test(trimmed)) return Number(trimmed) * 1000\n const at = Date.parse(trimmed)\n return Number.isFinite(at) ? Math.max(0, at - now) : null\n}\n\nfunction backoffMs(attempt: number): number {\n return 2 ** attempt * 200 + Math.random() * 200\n}\n\nfunction normalizeHostedBase(endpoint: string): string {\n return endpoint.trim().replace(/\\/+$/, '').replace(/\\/v1$/, '')\n}\n\nfunction resolveIdempotencyKey(key: string | undefined): string {\n const resolved = key ?? globalThis.crypto.randomUUID()\n if (resolved.trim().length === 0) throw new Error('idempotency key must not be blank')\n if (resolved.length > MAX_IDEMPOTENCY_KEY_LENGTH) {\n throw new Error(`idempotency key must be at most ${MAX_IDEMPOTENCY_KEY_LENGTH} characters`)\n }\n return resolved\n}\n\nfunction isRetryable(status: number): boolean {\n return status >= 500 || status === 408 || status === 429\n}\n\nasync function send(\n tenant: HostedTenant,\n request: HostedRequest,\n): Promise<{ status: number; json: unknown; url: string }> {\n const timeoutMs = tenant.timeoutMs ?? 30_000\n const maxRetries = tenant.retries ?? 2\n const f: typeof fetch = tenant.fetchImpl ?? ((...args) => fetch(...args))\n const url = `${normalizeHostedBase(tenant.endpoint)}${request.path}`\n const headers: Record<string, string> = {\n authorization: `Bearer ${tenant.apiKey}`,\n 'x-tangle-tenant-id': tenant.tenantId,\n 'x-tangle-wire-version': HOSTED_WIRE_VERSION,\n }\n if (request.method !== 'GET') {\n headers['idempotency-key'] = resolveIdempotencyKey(request.idempotencyKey)\n headers['content-type'] = request.contentType ?? 'application/json'\n }\n\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const timeout = AbortSignal.timeout(timeoutMs)\n const signal = request.signal ? AbortSignal.any([request.signal, timeout]) : timeout\n let res: Response\n try {\n res = await f(url, {\n method: request.method,\n headers,\n ...(request.body === undefined ? {} : { body: request.body }),\n signal,\n })\n } catch (err) {\n if (request.signal?.aborted) throw err\n lastError = err\n if (attempt === maxRetries) throw err\n await sleep(backoffMs(attempt), request.signal)\n continue\n }\n\n if (!res.ok && !request.expected?.includes(res.status)) {\n const text = await res.text().catch(() => '')\n const error = new HostedRequestError(url, res.status, text)\n if (!isRetryable(res.status) || attempt === maxRetries) throw error\n lastError = error\n await sleep(\n retryAfterMs(res.headers.get('retry-after')) ?? backoffMs(attempt),\n request.signal,\n )\n continue\n }\n\n try {\n return { status: res.status, json: await res.json(), url }\n } catch (error) {\n throw new Error(`hosted ${url} returned invalid JSON`, { cause: error })\n }\n }\n throw lastError ?? new Error('hosted request exhausted retries')\n}\n\nfunction parseResponse<T>(schema: z.ZodType<T>, value: unknown, url: string): T {\n const parsed = schema.safeParse(value)\n if (parsed.success) return parsed.data\n const reason = parsed.error.issues\n .map((issue) => `${issue.path.map(String).join('.') || 'value'}: ${issue.message}`)\n .join('; ')\n throw new Error(`hosted ${url} returned an invalid response: ${reason}`)\n}\n\nexport function createHostedClient(tenant: HostedTenant): HostedClient {\n if (normalizeHostedBase(tenant.endpoint).length === 0) throw new Error('endpoint is required')\n if (tenant.apiKey.trim().length === 0) throw new Error('apiKey is required')\n if (tenant.tenantId.trim().length === 0) throw new Error('tenantId is required')\n if (\n tenant.timeoutMs !== undefined &&\n (!Number.isFinite(tenant.timeoutMs) || tenant.timeoutMs <= 0)\n ) {\n throw new Error('timeoutMs must be greater than 0')\n }\n if (tenant.retries !== undefined && (!Number.isInteger(tenant.retries) || tenant.retries < 0)) {\n throw new Error('retries must be a non-negative integer')\n }\n\n return {\n tenant,\n wireVersion: HOSTED_WIRE_VERSION,\n\n async ingestTraces(spans, idempotencyKey) {\n const body: IngestTracesRequest = IngestTracesRequestSchema.parse({\n wireVersion: HOSTED_WIRE_VERSION,\n spans,\n })\n const res = await send(tenant, {\n method: 'POST',\n path: '/v1/ingest/traces',\n body: JSON.stringify(body),\n idempotencyKey,\n })\n return parseResponse(IngestResponseSchema, res.json, res.url)\n },\n\n async searchLedgerHead(searchId, signal) {\n const res = await send(tenant, {\n method: 'GET',\n path: searchLedgerHeadPath(searchId),\n signal,\n })\n const head = parseResponse(SearchLedgerHeadSchema, res.json, res.url)\n if (head.searchId !== searchId) {\n throw new Error(`hosted ${res.url} returned the head of search ${head.searchId}`)\n }\n return head\n },\n\n async ingestSearchLedger(request, signal) {\n const body = IngestSearchLedgerRequestSchema.parse(request)\n const last = body.lines.at(-1)!\n const res = await send(tenant, {\n method: 'POST',\n path: SEARCH_LEDGER_INGEST_PATH,\n body: JSON.stringify(body),\n // Equal key exactly when the batch is equal, so a cached response is\n // always the response to these lines.\n idempotencyKey: `search-ledger:${await sha256Hex(\n `${body.searchId}\\n${body.runKind}\\n${body.fromSequence}\\n${body.lines.length}\\n${last}`,\n )}`,\n signal,\n expected: [409],\n })\n if (res.status === 409) {\n return {\n status: 'conflict',\n conflict: parseResponse(SearchLedgerConflictSchema, res.json, res.url),\n }\n }\n return { status: 'accepted', head: parseResponse(SearchLedgerHeadSchema, res.json, res.url) }\n },\n\n async putSearchBlob(sha256, bytes, contentType, signal) {\n const res = await send(tenant, {\n method: 'PUT',\n path: searchBlobPath(sha256),\n body: bytes,\n contentType,\n idempotencyKey: `search-blob:${sha256}`,\n signal,\n })\n const stored = parseResponse(SearchBlobPutResponseSchema, res.json, res.url)\n if (stored.sha256 !== sha256) {\n throw new Error(`hosted ${res.url} acknowledged blob ${stored.sha256}, sent ${sha256}`)\n }\n return stored\n },\n }\n}\n\nasync function sha256Hex(text: string): Promise<string> {\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(text))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * Build a {@link HostedTenant} from env, or `undefined` when ingest is not\n * configured, so a product wires it unconditionally and it stays off until the\n * env is set. Env precedence:\n * - endpoint: `TANGLE_INGEST_URL` → `TANGLE_ORCHESTRATOR_URL`\n * - apiKey: `TANGLE_INGEST_API_KEY` → `TANGLE_API_KEY`\n * - tenantId: `TANGLE_TENANT_ID`\n * A trailing slash on the endpoint is stripped. `overrides` win over env.\n */\nexport function hostedTenantFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedTenant | undefined {\n const env = overrides.env ?? process.env\n const endpoint = (\n overrides.endpoint ??\n env.TANGLE_INGEST_URL ??\n env.TANGLE_ORCHESTRATOR_URL\n )?.trim()\n const apiKey = (overrides.apiKey ?? env.TANGLE_INGEST_API_KEY ?? env.TANGLE_API_KEY)?.trim()\n const tenantId = (overrides.tenantId ?? env.TANGLE_TENANT_ID)?.trim()\n if (!endpoint || !apiKey || !tenantId) return undefined\n const tenant: HostedTenant = { endpoint: endpoint.replace(/\\/+$/, ''), apiKey, tenantId }\n if (overrides.fetchImpl) tenant.fetchImpl = overrides.fetchImpl\n if (overrides.timeoutMs !== undefined) tenant.timeoutMs = overrides.timeoutMs\n if (overrides.retries !== undefined) tenant.retries = overrides.retries\n return tenant\n}\n\n/** {@link createHostedClient} over {@link hostedTenantFromEnv}; `undefined`\n * when ingest is not configured. */\nexport function hostedClientFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedClient | undefined {\n const tenant = hostedTenantFromEnv(overrides)\n return tenant ? createHostedClient(tenant) : undefined\n}\n","/**\n * Ship a search ledger to a hosted store while the search runs, or after.\n *\n * The local ledger is the source of truth and the search never waits on the\n * network: the shipper reads the ledger file the recorder already made\n * durable, uploads the blobs each entry names, then posts the entries. It\n * starts from the store's head, so a restarted process, a lost response, or a\n * second shipper continues where the store's chain ends instead of resending\n * the search. A store that holds a different chain for the same search stops\n * the shipper with `SearchShipConflictError`; nothing is overwritten.\n */\n\nimport { createHash } from 'node:crypto'\nimport { open, readFile } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport type { SearchLedgerEntry, SearchLedgerHash } from '../campaign/search-ledger-types'\nimport { createHostedClient, type HostedClient, type HostedTenant } from './client'\nimport {\n SEARCH_LEDGER_BATCH_MAX_BYTES,\n SEARCH_LEDGER_BATCH_MAX_LINES,\n type SearchLedgerConflict,\n type SearchLedgerHead,\n type SearchRunKind,\n searchLedgerArtifactRefs,\n} from './search-ledger-wire'\nimport { HOSTED_WIRE_VERSION } from './types'\n\nexport interface SearchShipOptions {\n tenant: HostedTenant\n ledger: { path: string; searchId: string }\n runKind: SearchRunKind\n /** `full` (default) uploads every blob an entry names; `digests` ships the\n * entries alone, so the store holds refs and digests without content. */\n content?: 'full' | 'digests'\n signal?: AbortSignal\n}\n\n/** A blob an entry names that was not uploaded, and why. */\nexport interface SearchShipMissingBlob {\n sha256: SearchLedgerHash\n uri: string\n reason: string\n}\n\nexport interface SearchShipResult {\n searchId: string\n /** The store's head after the last accepted batch. */\n head: SearchLedgerHead\n /** Local complete lines, which the store now holds when `head` reaches them. */\n localLines: number\n /** Ledger lines posted, counting resends after a gap. */\n linesPosted: number\n batches: number\n /** Resends after the store answered `sequence_gap`. */\n gapResends: number\n blobs: {\n content: 'full' | 'digests'\n uploaded: number\n stored: number\n masked: number\n withheld: number\n missing: SearchShipMissingBlob[]\n }\n}\n\n/** The store holds a different chain for this search. */\nexport class SearchShipConflictError extends Error {\n readonly conflict: SearchLedgerConflict\n constructor(conflict: SearchLedgerConflict) {\n super(\n `search ${conflict.head.searchId}: ${conflict.error}: ${conflict.message} (store head ${conflict.head.nextSequence} ${conflict.head.headHash ?? 'empty'})`,\n )\n this.name = 'SearchShipConflictError'\n this.conflict = conflict\n }\n}\n\n/** Ship every complete line of the ledger file now, then return. */\nexport async function shipSearchLedger(options: SearchShipOptions): Promise<SearchShipResult> {\n return new SearchLedgerTail(options).ship()\n}\n\nexport interface SearchShipper {\n /** Ship what is in the file now without waiting for the next tick. */\n flush(): Promise<SearchShipResult>\n /** Stop tailing, ship the rest of the file, and return the final result. */\n stop(): Promise<SearchShipResult>\n /** The last transient failure, cleared by the next successful tick. */\n readonly lastError: Error | null\n}\n\n/**\n * Tail the ledger: ship new lines every `intervalMs` (default 2 s) until\n * `stop()`. A transient failure is kept on `lastError` and retried on the next\n * tick; a conflict stops tailing and is thrown by `stop()`.\n */\nexport function startSearchShipper(\n options: SearchShipOptions & { intervalMs?: number },\n): SearchShipper {\n const tail = new SearchLedgerTail(options)\n const intervalMs = options.intervalMs ?? 2000\n const stopping = new AbortController()\n let lastError: Error | null = null\n let conflict: SearchShipConflictError | null = null\n let running: Promise<unknown> = Promise.resolve()\n\n const tick = (): Promise<unknown> => {\n running = running.then(async () => {\n if (conflict) return\n try {\n await tail.ship()\n lastError = null\n } catch (error) {\n if (error instanceof SearchShipConflictError) conflict = error\n else lastError = error instanceof Error ? error : new Error(String(error))\n }\n })\n return running\n }\n\n const loop = (async () => {\n while (!stopping.signal.aborted && !conflict) {\n await tick()\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, intervalMs)\n stopping.signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer)\n resolve()\n },\n { once: true },\n )\n })\n }\n })()\n\n return {\n get lastError() {\n return lastError\n },\n async flush() {\n await tick()\n if (conflict) throw conflict\n if (lastError) throw lastError\n return tail.result()\n },\n async stop() {\n stopping.abort()\n await loop\n await running\n if (conflict) throw conflict\n return tail.ship()\n },\n }\n}\n\n/** One full batch of lines with their newlines, plus a byte to see where a\n * maximal line ends. */\nconst READ_CHUNK_BYTES = SEARCH_LEDGER_BATCH_MAX_BYTES + SEARCH_LEDGER_BATCH_MAX_LINES + 1\n\ninterface LocalLine {\n text: string\n entry: SearchLedgerEntry\n bytes: number\n}\n\nclass SearchLedgerTail {\n private readonly client: HostedClient\n private readonly options: SearchShipOptions\n private readonly content: 'full' | 'digests'\n /** Where the store's chain ends, and the byte offset of that line locally. */\n private head: SearchLedgerHead | null = null\n private offset = 0\n private localLines = 0\n private readonly uploaded = new Set<string>()\n private readonly missing = new Map<string, SearchShipMissingBlob>()\n private readonly counts = {\n linesPosted: 0,\n batches: 0,\n gapResends: 0,\n uploaded: 0,\n stored: 0,\n masked: 0,\n withheld: 0,\n }\n\n constructor(options: SearchShipOptions) {\n this.options = options\n this.content = options.content ?? 'full'\n this.client = createHostedClient(options.tenant)\n }\n\n result(): SearchShipResult {\n const { linesPosted, batches, gapResends, uploaded, stored, masked, withheld } = this.counts\n return {\n searchId: this.options.ledger.searchId,\n head: this.head ?? {\n searchId: this.options.ledger.searchId,\n nextSequence: 0,\n headHash: null,\n },\n localLines: this.localLines,\n linesPosted,\n batches,\n gapResends,\n blobs: {\n content: this.content,\n uploaded,\n stored,\n masked,\n withheld,\n missing: [...this.missing.values()],\n },\n }\n }\n\n async ship(): Promise<SearchShipResult> {\n const { signal } = this.options\n if (this.head === null) {\n await this.syncTo(await this.client.searchLedgerHead(this.options.ledger.searchId, signal))\n }\n for (;;) {\n const lines = await this.readChunk(this.offset, this.head!)\n if (lines.length === 0) return this.result()\n const batch = takeBatch(lines)\n if (this.content === 'full') await this.uploadBlobs(batch)\n const fromSequence = this.head!.nextSequence\n const outcome = await this.client.ingestSearchLedger(\n {\n wireVersion: HOSTED_WIRE_VERSION,\n searchId: this.options.ledger.searchId,\n runKind: this.options.runKind,\n fromSequence,\n lines: batch.map((line) => line.text),\n },\n signal,\n )\n this.counts.batches++\n this.counts.linesPosted += batch.length\n if (outcome.status === 'conflict') {\n if (outcome.conflict.error === 'chain_conflict') {\n throw new SearchShipConflictError(outcome.conflict)\n }\n this.counts.gapResends++\n await this.syncTo(outcome.conflict.head)\n continue\n }\n const last = batch.at(-1)!.entry\n if (\n outcome.head.nextSequence < last.sequence + 1 ||\n (outcome.head.nextSequence === last.sequence + 1 &&\n outcome.head.headHash !== last.entryHash)\n ) {\n throw new Error(\n `store acknowledged sequence ${fromSequence}..${last.sequence} with head ${outcome.head.nextSequence} ${outcome.head.headHash}`,\n )\n }\n await this.syncTo(outcome.head)\n }\n }\n\n /** Move the cursor to the store's head, checking that the local ledger holds\n * the same entry there. A different entry is a fork; a store ahead of the\n * local file holds lines this ledger never wrote. */\n private async syncTo(head: SearchLedgerHead): Promise<void> {\n if (head.searchId !== this.options.ledger.searchId) {\n throw new Error(`store answered for search ${head.searchId}`)\n }\n const fork = (message: string) =>\n new SearchShipConflictError({ error: 'chain_conflict', message, head })\n const empty: SearchLedgerHead = { searchId: head.searchId, nextSequence: 0, headHash: null }\n const forward = this.head !== null && head.nextSequence >= this.head.nextSequence\n let offset = forward ? this.offset : 0\n let cursor = forward ? this.head! : empty\n while (cursor.nextSequence < head.nextSequence) {\n const lines = await this.readChunk(offset, cursor)\n if (lines.length === 0) {\n throw fork(\n `the store holds ${head.nextSequence} entries; the local ledger holds ${cursor.nextSequence}`,\n )\n }\n const take = Math.min(lines.length, head.nextSequence - cursor.nextSequence)\n for (let index = 0; index < take; index++) offset += lines[index]!.bytes\n const last = lines[take - 1]!.entry\n cursor = {\n searchId: head.searchId,\n nextSequence: last.sequence + 1,\n headHash: last.entryHash,\n }\n }\n if (cursor.headHash !== head.headHash) {\n throw fork(`the store's entry ${head.nextSequence - 1} differs from the local ledger's`)\n }\n this.offset = offset\n this.head = head\n }\n\n /** The complete lines in one read chunk from `offset`, checked to continue\n * the chain at `head`. A chunk holds at least one full batch. */\n private async readChunk(offset: number, head: SearchLedgerHead): Promise<LocalLine[]> {\n const bytes = await readRange(this.options.ledger.path, offset, READ_CHUNK_BYTES)\n const lines: LocalLine[] = []\n let start = 0\n let expectedSequence = head.nextSequence\n let previousHash = head.headHash\n for (;;) {\n const newline = bytes.indexOf(0x0a, start)\n // A line without its newline is still being written, or continues in\n // the next chunk.\n if (newline < 0) break\n const text = bytes.toString('utf8', start, newline)\n const entry = JSON.parse(text) as SearchLedgerEntry\n if (\n entry.searchId !== this.options.ledger.searchId ||\n entry.sequence !== expectedSequence ||\n entry.previousHash !== previousHash\n ) {\n throw new Error(\n `${this.options.ledger.path} line ${expectedSequence + 1} does not continue the chain at sequence ${expectedSequence}`,\n )\n }\n lines.push({ text, entry, bytes: newline + 1 - start })\n expectedSequence++\n previousHash = entry.entryHash\n start = newline + 1\n }\n if (lines.length === 0 && bytes.byteLength === READ_CHUNK_BYTES) {\n throw new Error(\n `${this.options.ledger.path} line ${expectedSequence + 1} is longer than ${SEARCH_LEDGER_BATCH_MAX_BYTES} bytes; one request carries at most that`,\n )\n }\n this.localLines = Math.max(this.localLines, expectedSequence)\n return lines\n }\n\n private async uploadBlobs(batch: LocalLine[]): Promise<void> {\n for (const line of batch) {\n for (const ref of searchLedgerArtifactRefs(line.entry.event)) {\n if (this.uploaded.has(ref.sha256) || this.missing.has(ref.sha256)) continue\n const bytes = await readBlob(ref.uri)\n if (typeof bytes === 'string') {\n this.missing.set(ref.sha256, { sha256: ref.sha256, uri: ref.uri, reason: bytes })\n continue\n }\n const digest = `sha256:${createHash('sha256').update(bytes).digest('hex')}`\n if (digest !== ref.sha256 || bytes.byteLength !== ref.byteLength) {\n this.missing.set(ref.sha256, {\n sha256: ref.sha256,\n uri: ref.uri,\n reason: `local bytes are ${digest} (${bytes.byteLength} bytes), not the ref's ${ref.sha256} (${ref.byteLength} bytes)`,\n })\n continue\n }\n const stored = await this.client.putSearchBlob(\n ref.sha256,\n new Uint8Array(bytes),\n ref.uri.endsWith('.json') ? 'application/json' : 'application/octet-stream',\n this.options.signal,\n )\n this.uploaded.add(ref.sha256)\n this.counts.uploaded++\n this.counts[stored.state]++\n }\n }\n }\n}\n\n/** The longest prefix of `lines` within one request's limits. */\nfunction takeBatch(lines: LocalLine[]): LocalLine[] {\n const batch: LocalLine[] = []\n let bytes = 0\n for (const line of lines) {\n const size = line.bytes - 1\n if (size > SEARCH_LEDGER_BATCH_MAX_BYTES) {\n throw new Error(\n `ledger line ${line.entry.sequence} holds ${size} bytes; one request carries at most ${SEARCH_LEDGER_BATCH_MAX_BYTES}`,\n )\n }\n if (\n batch.length === SEARCH_LEDGER_BATCH_MAX_LINES ||\n bytes + size > SEARCH_LEDGER_BATCH_MAX_BYTES\n )\n break\n batch.push(line)\n bytes += size\n }\n return batch\n}\n\n/** Up to `limit` bytes of the file from `offset`; empty before the recorder\n * created the file. */\nasync function readRange(path: string, offset: number, limit: number): Promise<Buffer> {\n let handle: Awaited<ReturnType<typeof open>>\n try {\n handle = await open(path, 'r')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT' && offset === 0) return Buffer.alloc(0)\n throw error\n }\n try {\n const { size } = await handle.stat()\n if (size < offset) throw new Error(`${path} shrank below byte ${offset}`)\n const bytes = Buffer.alloc(Math.min(limit, size - offset))\n let read = 0\n while (read < bytes.byteLength) {\n const { bytesRead } = await handle.read(bytes, read, bytes.byteLength - read, offset + read)\n if (bytesRead === 0) break\n read += bytesRead\n }\n return bytes.subarray(0, read)\n } finally {\n await handle.close()\n }\n}\n\n/** The bytes at a `file:` ref, or why there are none. */\nasync function readBlob(uri: string): Promise<Buffer | string> {\n if (!uri.startsWith('file:'))\n return `the shipper reads only file: refs, not ${uri.split(':')[0]}:`\n try {\n return await readFile(fileURLToPath(uri))\n } catch (error) {\n return `unreadable: ${(error as NodeJS.ErrnoException).code ?? String(error)}`\n }\n}\n\n/**\n * Run a ship that must never fail its caller, such as the one a loop runs at\n * its end. A failure, a store short of the local ledger, or a blob that did\n * not upload is logged with the command that resumes it.\n */\nexport async function shipBestEffort(\n ship: () => Promise<SearchShipResult>,\n ledgerPath: string,\n): Promise<SearchShipResult | undefined> {\n const resume = `agent-eval search ship ${ledgerPath}`\n try {\n const shipped = await ship()\n if (shipped.head.nextSequence < shipped.localLines) {\n console.warn(\n `[agent-eval] search ${shipped.searchId}: the store holds ${shipped.head.nextSequence} of ${shipped.localLines} ledger entries; resume with ${resume}`,\n )\n }\n if (shipped.blobs.missing.length > 0) {\n console.warn(\n `[agent-eval] search ${shipped.searchId}: ${shipped.blobs.missing.length} blob(s) were not uploaded, first: ${shipped.blobs.missing[0]!.reason}`,\n )\n }\n return shipped\n } catch (error) {\n console.warn(\n `[agent-eval] search ledger ship failed (continuing; resume with ${resume}): ${error instanceof Error ? error.message : String(error)}`,\n )\n return undefined\n }\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { i as CostLedger } from "./cost-ledger-_IbkejCF.js";
2
2
  import { f as Mutex } from "./journal-c3qY0SDP.js";
3
- import { N as RawAnalystFindingSchema } from "./kind-factory-ByZeXDqF.js";
3
+ import { N as RawAnalystFindingSchema } from "./kind-factory-CdPPHtGZ.js";
4
4
  import { c as extractJsonPayload } from "./llm-client-KYQBh9C8.js";
5
5
  import { n as paidJsonChat, t as paidChat } from "./chat-json-call-CXHBvUz8.js";
6
- import { n as decodeRawFindingArray } from "./dspy-rlm-engine-Cs5LzJ-f.js";
6
+ import { n as decodeRawFindingArray } from "./dspy-rlm-engine-MfpwoEaj.js";
7
7
  import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
8
8
  import { z } from "zod";
9
9
  import { dirname } from "node:path";
@@ -777,4 +777,4 @@ async function runSemanticConceptJudge(input, options) {
777
777
  //#endregion
778
778
  export { diffFindings as a, defaultIsMaterial as i, runSemanticConceptJudge as n, createChatTraceEngine as o, FindingsStore as r, SEMANTIC_CONCEPT_JUDGE_VERSION as t };
779
779
 
780
- //# sourceMappingURL=semantic-concept-judge-ClB73cei.js.map
780
+ //# sourceMappingURL=semantic-concept-judge-Bt7bXAAo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-concept-judge-ClB73cei.js","names":[],"sources":["../src/analyst/chat-trace-engine.ts","../src/locked-jsonl-appender.ts","../src/analyst/findings-store.ts","../src/semantic-concept-judge.ts"],"sourcesContent":["/**\n * A `TraceAnalysisEngine` that runs entirely inside Node against a\n * caller-owned `ChatClient`.\n *\n * The other engine in this package, `createDspyRlmTraceEngine`, reaches the\n * DSPy RLM through a Python subprocess. That made every model-backed analyst\n * unreachable for a consumer that already owns a model seam and no Python:\n * `createTraceAnalyst` requires an engine, the only exported constructor\n * required a Python runner, so `buildDefaultAnalystRegistry()` registered the\n * deterministic analyst alone and `analystsFromRegistry` refused the result.\n *\n * This engine closes that path. It drives the same investigation contract —\n * bounded trace tools, a prose answer, a strict findings array — with native\n * function calls over the transport the caller already bound with\n * `createChatClient`. A caller holding a bare\n * `(request: LlmCallRequest) => Promise<LlmCallResult>` adapts it in one line:\n *\n * createChatClient({ transport: 'custom', chat: call, defaultModel, maximumAttempts })\n *\n * agent-eval still executes no paid model and holds no provider credential.\n * Every call goes through `paidChat`, so the shared cost ledger reserves the\n * priced maximum before the call and settles the receipt after it.\n *\n * Shape of one investigation:\n * 1. Investigation turns. The model reads the trace store through the tools\n * its analyst kind was given. A turn that requests no tool ends the\n * phase; an exhausted iteration or tool budget also ends it.\n * 2. One report turn. The model returns `{ answer, findings }` as JSON,\n * decoded by the same `decodeRawFindingArray` the Python bridge uses, so\n * a row this engine accepts is a row that engine could report.\n */\n\nimport { z } from 'zod'\nimport { paidChat } from '../chat-json-call'\nimport type { CustomTokenPricing } from '../cost-ledger'\nimport {\n extractJsonPayload,\n type LlmCallRequest,\n type LlmMessage,\n type LlmThinkingMode,\n type LlmToolCall,\n type LlmToolDefinition,\n} from '../llm-client'\nimport type { TraceAnalysisToolDescriptor } from '../trace-analyst/tools'\nimport type { ChatClient } from './chat-client'\nimport {\n DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS,\n type TraceAnalysisEngine,\n type TraceAnalysisEngineRequest,\n type TraceAnalysisEngineResult,\n} from './engine'\nimport { decodeRawFindingArray } from './finding-codec'\nimport { RawAnalystFindingSchema } from './finding-signature'\n\n/** Bumped whenever this engine's execution behavior changes. */\nconst CHAT_TRACE_ENGINE_VERSION = '1.0.0'\nconst ENGINE_ID = 'chat-trace'\n\n/** Marker appended to a tool result cut down to the retained-output budget. */\nconst TRUNCATION_MARKER = '\\n…[truncated to the analyst maxOutputChars budget]'\n\nexport interface ChatTraceEngineOptions {\n /**\n * Caller-owned transport. Build it with `createChatClient`; agent-eval never\n * receives the provider credential.\n */\n chat: ChatClient\n /** Model for every call. Defaults to the client's `defaultModel`. */\n model?: string\n /** Endpoint rates used when the transport reports no billed amount. */\n pricing?: CustomTokenPricing\n /** Completion cap per turn. Default: {@link DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS}. */\n maxOutputTokens?: number\n /** Sampling temperature. Omitted from the request when absent. */\n temperature?: number\n /** Provider reasoning mode. Omitted when the provider default should apply. */\n thinking?: LlmThinkingMode\n /** Per-call deadline handed to the transport. */\n requestTimeoutMs?: number\n /**\n * How the report turn asks for structured output.\n *\n * `json-object` sends JSON mode, which every OpenAI-compatible endpoint\n * accepts, and relies on the finding grammar the analyst kind already puts\n * in its instructions. `json-schema` sends the generated response schema as\n * well, which stricter endpoints honour and older ones reject. Default:\n * `json-object`.\n */\n reportFormat?: 'json-object' | 'json-schema'\n}\n\n/**\n * The report envelope. Its `findings` rows are generated from\n * `RawAnalystFindingSchema`, so the schema offered to a provider and the\n * decoder that accepts the answer cannot drift apart.\n */\nfunction buildReportJsonSchema(): { name: string; schema: Record<string, unknown> } {\n const row = z.toJSONSchema(RawAnalystFindingSchema, { target: 'draft-7' }) as Record<\n string,\n unknown\n >\n delete row.$schema\n return {\n name: 'trace_analysis_report',\n schema: {\n type: 'object',\n additionalProperties: false,\n required: ['answer', 'findings'],\n properties: {\n answer: { type: 'string', description: 'Direct prose answer to the question.' },\n findings: { type: 'array', items: row },\n },\n },\n }\n}\n\nconst REPORT_JSON_SCHEMA = buildReportJsonSchema()\n\n/**\n * Run bounded recursive trace analysis in-process over a caller-owned chat\n * transport. No Python, no subprocess, no loopback proxy.\n */\nexport function createChatTraceEngine(options: ChatTraceEngineOptions): TraceAnalysisEngine {\n const model = resolveModel(options)\n const maxOutputTokens = options.maxOutputTokens ?? DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS\n if (!Number.isSafeInteger(maxOutputTokens) || maxOutputTokens <= 0) {\n throw new TypeError('chat trace engine maxOutputTokens must be a positive safe integer')\n }\n if (\n options.temperature !== undefined &&\n (!Number.isFinite(options.temperature) || options.temperature < 0)\n ) {\n throw new TypeError('chat trace engine temperature must be a non-negative finite number')\n }\n if (\n options.requestTimeoutMs !== undefined &&\n (!Number.isSafeInteger(options.requestTimeoutMs) || options.requestTimeoutMs <= 0)\n ) {\n throw new TypeError('chat trace engine requestTimeoutMs must be a positive safe integer')\n }\n const reportFormat = options.reportFormat ?? 'json-object'\n if (reportFormat !== 'json-object' && reportFormat !== 'json-schema') {\n throw new TypeError(`chat trace engine reportFormat must be json-object or json-schema`)\n }\n\n return {\n id: ENGINE_ID,\n description:\n 'In-process recursive trace analysis over a caller-owned ChatClient with native tool calls.',\n model,\n version: CHAT_TRACE_ENGINE_VERSION,\n executionConfig: {\n kind: ENGINE_ID,\n model,\n transport: options.chat.transport,\n maximum_attempts: options.chat.maximumAttempts ?? null,\n pricing: options.pricing ? { ...options.pricing } : null,\n max_output_tokens: maxOutputTokens,\n temperature: options.temperature ?? null,\n thinking: options.thinking ?? null,\n request_timeout_ms: options.requestTimeoutMs ?? null,\n report_format: reportFormat,\n report_schema_name: REPORT_JSON_SCHEMA.name,\n },\n analyze: (request) => analyze(request, { ...options, model, maxOutputTokens, reportFormat }),\n }\n}\n\ninterface ResolvedOptions extends ChatTraceEngineOptions {\n model: string\n maxOutputTokens: number\n reportFormat: 'json-object' | 'json-schema'\n}\n\nasync function analyze(\n request: TraceAnalysisEngineRequest,\n options: ResolvedOptions,\n): Promise<TraceAnalysisEngineResult> {\n // One model call is spent on the report, so a budget of one call would buy\n // an answer with no investigation behind it. Refuse instead of pretending.\n if (request.limits.maxLlmCalls < 2) {\n throw new Error(\n `chat trace engine reserves one model call for the report, so maxLlmCalls must be at least 2 (analyst '${request.analystId}' declared ${request.limits.maxLlmCalls})`,\n )\n }\n const toolsByName = new Map(request.tools.map((tool) => [tool.name, tool]))\n if (toolsByName.size !== request.tools.length) {\n throw new Error(`chat trace engine received duplicate tool names for '${request.analystId}'`)\n }\n const toolDefinitions: LlmToolDefinition[] = request.tools.map((tool) => ({\n type: 'function',\n function: { name: tool.name, description: tool.description, parameters: tool.parameters },\n }))\n const investigationTurns = Math.max(\n 1,\n Math.min(request.limits.maxIterations, request.limits.maxLlmCalls - 1),\n )\n\n const messages: LlmMessage[] = [\n { role: 'system', content: systemPrompt(request, investigationTurns) },\n ]\n if (request.taskInputs) {\n messages.push({ role: 'user', content: renderTaskInputs(request.taskInputs) })\n }\n messages.push({ role: 'user', content: request.question })\n\n request.log?.('trace analyst engine started', {\n engine: ENGINE_ID,\n model: options.model,\n transport: options.chat.transport,\n tools: request.tools.map((tool) => tool.name),\n limits: request.limits,\n })\n\n const trajectory: unknown[] = []\n const servedModels = new Set<string>()\n let modelCalls = 0\n let toolCalls = 0\n let truncatedToolResults = 0\n let toolBudgetExhausted = false\n let turnsUsed = 0\n let stoppedOnAnswer = false\n\n for (let turn = 0; turn < investigationTurns; turn++) {\n turnsUsed = turn + 1\n const response = await callModel(request, options, {\n messages,\n tools: toolDefinitions,\n toolChoice: 'auto',\n purpose: 'investigation',\n })\n modelCalls += 1\n servedModels.add(response.servedModel ?? 'unreported')\n const requested = response.toolCalls ?? []\n messages.push({\n role: 'assistant',\n content: response.content,\n ...(requested.length > 0 ? { toolCalls: requested } : {}),\n })\n trajectory.push({\n turn: turnsUsed,\n phase: 'investigation',\n content: response.content,\n tool_calls: requested.map((call) => ({ id: call.id, name: call.name })),\n finish_reason: response.finishReason ?? null,\n })\n if (requested.length === 0) {\n stoppedOnAnswer = true\n break\n }\n for (const call of requested) {\n if (toolCalls >= request.limits.maxToolCalls) {\n toolBudgetExhausted = true\n // Every requested id still needs an answer: a provider rejects the\n // next turn when one tool call is left unanswered.\n messages.push(toolMessage(call, exhaustedToolBudget(request.limits.maxToolCalls)))\n continue\n }\n toolCalls += 1\n const executed = await executeTool(call, toolsByName, request)\n if (executed.truncated) truncatedToolResults += 1\n messages.push(toolMessage(call, executed.payload))\n trajectory.push({\n turn: turnsUsed,\n phase: 'tool',\n name: call.name,\n ok: executed.ok,\n truncated: executed.truncated,\n result_chars: executed.payload.length,\n })\n }\n if (toolBudgetExhausted) break\n }\n\n messages.push({ role: 'user', content: reportPrompt(options.reportFormat) })\n const report = await callModel(request, options, {\n messages,\n purpose: 'report',\n json: options.reportFormat,\n })\n modelCalls += 1\n servedModels.add(report.servedModel ?? 'unreported')\n trajectory.push({\n turn: turnsUsed + 1,\n phase: 'report',\n content: report.content,\n finish_reason: report.finishReason ?? null,\n })\n\n const parsed = parseReport(report.content, request.analystId, (index, reason) => {\n request.log?.('finding rejected: report row failed schema validation', {\n engine: ENGINE_ID,\n index,\n reason,\n })\n })\n\n const result: TraceAnalysisEngineResult = {\n answer: parsed.answer,\n findings: parsed.findings,\n trajectory,\n modelCalls,\n toolCalls,\n runtime: {\n engine: ENGINE_ID,\n model: options.model,\n transport: options.chat.transport,\n report_format: options.reportFormat,\n investigation_turns: turnsUsed,\n investigation_stopped_on_answer: stoppedOnAnswer,\n tool_budget_exhausted: toolBudgetExhausted,\n truncated_tool_results: truncatedToolResults,\n rejected_findings: parsed.rejectedFindings,\n // A gateway can answer `model: X` from another model on HTTP 200, so the\n // ids the provider echoed travel with the result as evidence.\n served_models: [...servedModels].sort(),\n task_inputs: request.taskInputs ? 'prompt-delivered' : 'none',\n },\n }\n request.log?.('trace analyst engine completed', {\n engine: ENGINE_ID,\n model_calls: result.modelCalls,\n tool_calls: result.toolCalls,\n findings: result.findings.length,\n })\n return result\n}\n\ninterface ModelTurn {\n content: string\n toolCalls?: LlmToolCall[]\n servedModel?: string | null\n finishReason?: string | null\n}\n\nasync function callModel(\n request: TraceAnalysisEngineRequest,\n options: ResolvedOptions,\n turn: {\n messages: LlmMessage[]\n tools?: LlmToolDefinition[]\n toolChoice?: LlmCallRequest['toolChoice']\n purpose: 'investigation' | 'report'\n json?: 'json-object' | 'json-schema'\n },\n): Promise<ModelTurn> {\n const chatRequest: LlmCallRequest = {\n model: options.model,\n // The transport may mutate what it is handed; the conversation is ours.\n messages: turn.messages.map((message) => ({ ...message })),\n maxTokens: options.maxOutputTokens,\n ...(turn.tools && turn.tools.length > 0\n ? { tools: turn.tools, ...(turn.toolChoice ? { toolChoice: turn.toolChoice } : {}) }\n : {}),\n ...(turn.json ? { jsonMode: true } : {}),\n ...(turn.json === 'json-schema' ? { jsonSchema: REPORT_JSON_SCHEMA } : {}),\n ...(options.temperature === undefined ? {} : { temperature: options.temperature }),\n ...(options.thinking === undefined ? {} : { thinking: options.thinking }),\n ...(options.requestTimeoutMs === undefined ? {} : { timeoutMs: options.requestTimeoutMs }),\n }\n const paid = await paidChat({\n chat: options.chat,\n request: chatRequest,\n ledger: request.costLedger,\n channel: 'analyst',\n phase: request.costPhase,\n actor: request.analystId,\n ...(request.costTags ? { tags: request.costTags } : {}),\n ...(options.pricing ? { pricing: options.pricing } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n })\n if (!paid.succeeded) {\n // The transport already spent its own attempts. A failure here ends the\n // investigation: continuing would report an answer the model never gave.\n throw new Error(\n `chat trace engine ${turn.purpose} call failed for '${request.analystId}': ${paid.error.message}`,\n { cause: paid.error },\n )\n }\n return {\n content: paid.response.content,\n ...(paid.response.toolCalls ? { toolCalls: paid.response.toolCalls } : {}),\n ...(paid.response.servedModel === undefined ? {} : { servedModel: paid.response.servedModel }),\n ...(paid.response.finishReason === undefined\n ? {}\n : { finishReason: paid.response.finishReason }),\n }\n}\n\ninterface ExecutedTool {\n ok: boolean\n truncated: boolean\n payload: string\n}\n\nasync function executeTool(\n call: LlmToolCall,\n toolsByName: ReadonlyMap<string, TraceAnalysisToolDescriptor>,\n request: TraceAnalysisEngineRequest,\n): Promise<ExecutedTool> {\n const descriptor = toolsByName.get(call.name)\n if (!descriptor) {\n return {\n ok: false,\n truncated: false,\n payload: toolError(\n `unknown tool '${call.name}'; available tools are ${[...toolsByName.keys()].sort().join(', ')}`,\n ),\n }\n }\n let args: unknown\n try {\n args = call.argumentsJson.trim() === '' ? {} : JSON.parse(call.argumentsJson)\n } catch (error) {\n return {\n ok: false,\n truncated: false,\n payload: toolError(\n `arguments for '${call.name}' were not JSON: ${messageOf(error)}. Send valid JSON arguments.`,\n ),\n }\n }\n try {\n const value = await descriptor.handler(\n args,\n request.signal ? { signal: request.signal } : undefined,\n )\n return truncateToolPayload(JSON.stringify(value ?? null), request.limits.maxOutputChars)\n } catch (error) {\n // A bounded store refuses an oversized or malformed read by design. That\n // refusal is the model's next instruction, not the run's failure.\n if (request.signal?.aborted) throw error\n request.log?.('trace tool failed', {\n engine: ENGINE_ID,\n analyst_id: request.analystId,\n tool: call.name,\n reason: messageOf(error),\n })\n return { ok: false, truncated: false, payload: toolError(messageOf(error)) }\n }\n}\n\nfunction truncateToolPayload(payload: string, maxOutputChars: number): ExecutedTool {\n if (payload.length <= maxOutputChars) return { ok: true, truncated: false, payload }\n const keep = Math.max(0, maxOutputChars - TRUNCATION_MARKER.length)\n return { ok: true, truncated: true, payload: `${payload.slice(0, keep)}${TRUNCATION_MARKER}` }\n}\n\nfunction toolMessage(call: LlmToolCall, content: string): LlmMessage {\n return { role: 'tool', toolCallId: call.id, content }\n}\n\nfunction toolError(reason: string): string {\n return JSON.stringify({ error: reason })\n}\n\nfunction exhaustedToolBudget(maxToolCalls: number): string {\n return toolError(\n `the trace-tool budget of ${maxToolCalls} calls is spent; answer from the evidence already read`,\n )\n}\n\nfunction systemPrompt(request: TraceAnalysisEngineRequest, investigationTurns: number): string {\n return [\n request.instructions.trim(),\n [\n 'HOW THIS INVESTIGATION RUNS:',\n `- You have ${investigationTurns} investigation turns and at most ${request.limits.maxToolCalls} trace-tool calls.`,\n '- Call the trace tools to read the store. Never state a trace fact you did not read.',\n `- A tool result longer than ${request.limits.maxOutputChars} characters is truncated; narrow the query instead of asking again.`,\n '- A tool result carrying an \"error\" field is feedback: fix the call or take another route.',\n '- Answer with no tool call once you have the evidence. You are then asked for the final report.',\n ].join('\\n'),\n ].join('\\n\\n')\n}\n\nfunction reportPrompt(reportFormat: 'json-object' | 'json-schema'): string {\n return [\n 'Report now. Do not call any more tools.',\n 'Return one JSON object with exactly two fields:',\n ' \"answer\": a direct prose answer to the question.',\n ' \"findings\": an array of findings in the schema above. Emit [] when there is nothing to report.',\n reportFormat === 'json-object'\n ? 'Return the object alone, with no surrounding prose and no code fence.'\n : 'Return the object in the response schema you were given.',\n ].join('\\n')\n}\n\nfunction renderTaskInputs(taskInputs: Readonly<Record<string, unknown>>): string {\n // The engine contract forbids dropping structured inputs. This engine has no\n // code environment to bind them as variables, so they are delivered whole as\n // conversation material and the runtime record says which way they arrived.\n return [\n 'TASK INPUTS — structured material delivered with the question, not fetched through tools:',\n JSON.stringify(taskInputs, null, 2),\n ].join('\\n')\n}\n\ninterface ParsedReport {\n answer: string\n findings: TraceAnalysisEngineResult['findings']\n rejectedFindings: number\n}\n\nfunction parseReport(\n content: string,\n analystId: string,\n onRejectedFinding: (index: number, reason: string) => void,\n): ParsedReport {\n const envelope = coerceReportEnvelope(content, analystId)\n if (typeof envelope.answer !== 'string' || !envelope.answer.trim()) {\n throw new Error(`chat trace engine report for '${analystId}' carried no answer`)\n }\n // Findings are model output: one malformed row is model noise, not an engine\n // fault, and the rest of the paid investigation must survive it.\n const decoded = decodeRawFindingArray(envelope.findings)\n if (decoded.topLevelError !== undefined) {\n throw new Error(\n `chat trace engine report for '${analystId}' had a malformed findings array: ${decoded.topLevelError}`,\n )\n }\n for (const rejection of decoded.rejected) {\n onRejectedFinding(\n rejection.index,\n `${rejection.code}${rejection.path ? ` at ${rejection.path}` : ''}: ${rejection.message}`,\n )\n }\n return {\n answer: envelope.answer,\n findings: decoded.accepted,\n rejectedFindings: decoded.rejected.length,\n }\n}\n\nfunction coerceReportEnvelope(content: string, analystId: string): Record<string, unknown> {\n let value: unknown\n try {\n value = JSON.parse(extractJsonPayload(content))\n } catch (error) {\n throw new Error(`chat trace engine report for '${analystId}' was not JSON: ${messageOf(error)}`)\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error(`chat trace engine report for '${analystId}' was not a JSON object`)\n }\n return value as Record<string, unknown>\n}\n\nfunction resolveModel(options: ChatTraceEngineOptions): string {\n const model = options.model ?? options.chat.defaultModel\n if (typeof model !== 'string' || !model.trim() || model !== model.trim()) {\n throw new TypeError(\n 'chat trace engine needs a model: pass ChatTraceEngineOptions.model or bind defaultModel on the ChatClient',\n )\n }\n return model\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","/**\n * LockedJsonlAppender — mutex-serialized JSONL append helper for arbitrary\n * payloads. The reference-replay store does the same thing for typed\n * `ReferenceReplayRun` rows; this is the generic version used by\n * `MutationTelemetry`, `TrialTelemetry`, and any other consumer that wants\n * append-only durable telemetry without rolling its own lock.\n *\n * Locks are per absolute file path (process-local). Cross-process\n * concurrency is NOT addressed — that's an fcntl/flock problem.\n */\n\nimport { appendFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { Mutex } from './concurrency'\n\nconst mutexes = new Map<string, Mutex>()\n\nfunction getMutex(path: string): Mutex {\n let m = mutexes.get(path)\n if (!m) {\n m = new Mutex()\n mutexes.set(path, m)\n }\n return m\n}\n\nexport class LockedJsonlAppender {\n private readonly mutex: Mutex\n constructor(public readonly path: string) {\n this.mutex = getMutex(path)\n if (!existsSync(dirname(path))) {\n mkdirSync(dirname(path), { recursive: true })\n }\n }\n\n async append(entry: unknown): Promise<void> {\n const line = `${JSON.stringify(entry)}\\n`\n await this.mutex.runExclusive(() => {\n appendFileSync(this.path, line)\n })\n }\n}\n\n/** Reset all internal mutex state — tests only. */\nexport function resetLockedAppendersForTesting(): void {\n mutexes.clear()\n}\n","/**\n * FindingsStore — durable persistence for AnalystFinding rows + a diff\n * helper so we can answer \"what changed since the last run?\" without\n * recomputing analysts.\n *\n * On-disk shape is JSONL: one finding per line, append-only, locked via\n * LockedJsonlAppender. Operators get crash-safety (no partial JSON),\n * cheap reads (sequential parse), and trivial backup (rsync the file).\n *\n * Reads are non-locking: a reader sees a consistent snapshot of all\n * fully-written lines and skips an incomplete trailing line if the\n * writer is mid-append. Cross-process locking is intentionally out of\n * scope (see locked-jsonl-appender.ts).\n *\n * The store is run-scoped: callers pass `runId` on append and on load,\n * which keeps multi-run files cleanly partitioned. The `diffFindings`\n * helper compares two run-id sets using stable `finding_id` semantics —\n * the diff is the cross-run signal the regression dashboard renders.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { LockedJsonlAppender } from '../locked-jsonl-appender'\nimport type { AnalystFinding } from './types'\n\n/**\n * One persisted row. We attach `run_id` on disk so a single file can\n * hold multiple runs and the diff helper can query without re-walking\n * separate files.\n */\nexport interface PersistedFinding extends AnalystFinding {\n run_id: string\n}\n\nexport class FindingsStore {\n private readonly appender: LockedJsonlAppender\n\n constructor(public readonly path: string) {\n this.appender = new LockedJsonlAppender(path)\n }\n\n async append(runId: string, findings: AnalystFinding[]): Promise<void> {\n for (const f of findings) {\n const row: PersistedFinding = { ...f, run_id: runId }\n await this.appender.append(row)\n }\n }\n\n /** Load every persisted finding. Discards malformed trailing lines silently. */\n loadAll(): PersistedFinding[] {\n if (!existsSync(this.path)) return []\n const raw = readFileSync(this.path, 'utf8')\n if (!raw) return []\n const out: PersistedFinding[] = []\n for (const line of raw.split('\\n')) {\n if (!line) continue\n try {\n out.push(JSON.parse(line) as PersistedFinding)\n } catch {\n // Skip torn trailing line — the lock guarantees no torn lines\n // mid-file, only at EOF when a writer is in-flight.\n }\n }\n return out\n }\n\n /** Filter to a single run. */\n loadRun(runId: string): PersistedFinding[] {\n return this.loadAll().filter((r) => r.run_id === runId)\n }\n}\n\n// ── Cross-run diff ──────────────────────────────────────────────────\n\nexport interface FindingsDiff {\n /** New finding ids in `current` that weren't in `previous`. */\n appeared: PersistedFinding[]\n /** Finding ids in `previous` that aren't in `current`. */\n disappeared: PersistedFinding[]\n /** Same finding id present in both runs and unchanged per the materiality test. */\n persisted: PersistedFinding[]\n /**\n * Same finding id in both runs but at least one non-identity field\n * shifted per `DiffPolicy.isMaterial`. Reported as [previous, current].\n */\n changed: Array<{ previous: PersistedFinding; current: PersistedFinding }>\n}\n\nexport interface DiffPolicy {\n /**\n * Predicate that decides whether two findings (same finding_id) count\n * as a material change. Defaults to {@link defaultIsMaterial}: severity\n * shift, confidence Δ > 0.05, or evidence count change. Compliance /\n * perf consumers MAY supply a stricter predicate (e.g. rationale text\n * diff, metric Δ thresholds).\n */\n isMaterial?: (previous: AnalystFinding, current: AnalystFinding) => boolean\n}\n\n/**\n * Default materiality test. Deliberately narrow so LLM-reword churn\n * doesn't flood the diff. Stricter tests are opt-in via DiffPolicy.\n */\nexport function defaultIsMaterial(a: AnalystFinding, b: AnalystFinding): boolean {\n if (a.severity !== b.severity) return true\n if (Math.abs((a.confidence ?? 0) - (b.confidence ?? 0)) > 0.05) return true\n if (a.evidence_refs.length !== b.evidence_refs.length) return true\n return false\n}\n\n/**\n * Diff two findings sets by stable finding_id. Callers typically load\n * the two run-id slices from the same store and pass them in.\n */\nexport function diffFindings(\n previous: PersistedFinding[],\n current: PersistedFinding[],\n policy: DiffPolicy = {},\n): FindingsDiff {\n const isMaterial = policy.isMaterial ?? defaultIsMaterial\n const prevById = new Map(previous.map((f) => [f.finding_id, f]))\n const curById = new Map(current.map((f) => [f.finding_id, f]))\n\n const appeared: PersistedFinding[] = []\n const disappeared: PersistedFinding[] = []\n const persisted: PersistedFinding[] = []\n const changed: FindingsDiff['changed'] = []\n\n for (const [id, cur] of curById) {\n const prev = prevById.get(id)\n if (!prev) {\n appeared.push(cur)\n continue\n }\n if (isMaterial(prev, cur)) {\n changed.push({ previous: prev, current: cur })\n } else {\n persisted.push(cur)\n }\n }\n for (const [id, prev] of prevById) {\n if (!curById.has(id)) disappeared.push(prev)\n }\n return { appeared, disappeared, persisted, changed }\n}\n","/**\n * Semantic concept judge — \"does the built artifact actually implement\n * the features the user asked for?\"\n *\n * Distinct from the domain/code/coherence judges in `judges.ts`:\n * - those judges score free-form conversational agent outputs along\n * quality dimensions (accuracy, depth, etc.)\n * - this judge scores a *built artifact* (served HTML + source files)\n * against an explicit list of expected concepts, returning per-concept\n * {present, score 0-10, evidence, severity}.\n *\n * The judge is strict about distinguishing (a) a working implementation\n * from (b) a keyword-present stub. \"// TODO: mint button\" is NOT present.\n * Only real, functional, wired-up code counts.\n *\n * Use via {@link createSemanticConceptJudge} or directly via\n * {@link runSemanticConceptJudge}. Soft-fails (available=false) on LLM\n * or JSON-parse errors so the caller can treat that as \"layer skipped\"\n * rather than \"layer failed\" in a multi-layer pipeline.\n */\n\nimport type { ChatClient } from './analyst/chat-client'\nimport { paidJsonChat } from './chat-json-call'\nimport {\n CostLedger,\n type CostLedgerHandle,\n type CostReceipt,\n type CustomTokenPricing,\n} from './cost-ledger'\nimport type { LlmCallRequest } from './llm-client'\nimport type { Severity } from './multi-layer-verifier'\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\n/**\n * Implementation complexity class for weighted scoring.\n *\n * - `render` (default): the concept is a UI surface that displays static\n * data — render a list, show a counter, lay out a button. Single-file\n * work, no external integration.\n * - `integrate`: the concept requires wiring a real external system —\n * wallet connect (wagmi + RainbowKit + chain config), payment provider\n * (Stripe Elements + intent + webhook), an API client with auth.\n * Multi-file, library-knowledge, runtime correctness matters.\n * - `compute`: the concept requires algorithmic work — solver, simulator,\n * constraint propagation, ML inference. Correctness > UI polish.\n *\n * Default weights (when applied via `weightConcepts: 'complexity'`):\n * render=1.0, integrate=2.0, compute=2.5\n *\n * Cross-vertical scoring without complexity weighting silently inflates\n * the rate of UI-heavy verticals (healthcare, fintech dashboards) vs\n * integration-heavy verticals (DeFi, wallets) — all concepts treated\n * equally even though the agent does 2-3x the work for `integrate`.\n */\nexport type ConceptComplexity = 'render' | 'integrate' | 'compute'\n\nexport interface ConceptSpec {\n name: string\n /** Short hints that help the judge; not used for matching. */\n keywords?: string[]\n /** Optional explicit weight; default 1.0. Overrides complexity-derived weight. */\n weight?: number\n /** Implementation complexity class. Default `render`. */\n complexity?: ConceptComplexity\n}\n\nexport interface ConceptFinding {\n concept: string\n present: boolean\n /** 0..10. 10 = production-ready; 7 = functional thin; 4 = partial; 0 = absent. */\n score: number\n evidence: string\n severity: Severity\n}\n\nexport interface SemanticConceptJudgeInput {\n /** Full natural-language prompt the agent was handed. */\n userRequest: string\n /** Rendered HTML the preview returns (UI artifacts). Optional. */\n servedHtml?: string\n /** Top-level source files from the agent's workdir. */\n sourceFiles: Array<{ path: string; content: string }>\n /** The expected concept list. */\n expectedConcepts: ConceptSpec[]\n /** Free-form metadata (id, difficulty) to inject into the prompt. */\n artifactLabel?: string\n artifactDescription?: string\n}\n\nexport interface SemanticConceptJudgeResult {\n kind: 'semantic-concept'\n version: string\n /** Normalized 0..1 score — mean of per-concept scores / 10. */\n score: number\n presentCount: number\n totalCount: number\n findings: ConceptFinding[]\n summary: string\n durationMs: number\n costUsd: number | null\n /** False on LLM/JSON error — treat as \"skipped / unable to judge\" in pipelines. */\n available: boolean\n error?: string\n}\n\n/**\n * Score-aggregation strategy. `mean` averages 0-10 scores uniformly.\n * `complexity` applies the default weight table (render=1, integrate=2,\n * compute=2.5) unless a concept has an explicit `weight`. `explicit`\n * honors only `weight` (defaulting to 1 for unspecified).\n */\nexport type ConceptWeightStrategy = 'mean' | 'complexity' | 'explicit'\n\nexport const DEFAULT_COMPLEXITY_WEIGHTS: Record<ConceptComplexity, number> = {\n render: 1.0,\n integrate: 2.0,\n compute: 2.5,\n}\n\nexport interface SemanticConceptJudgeOptions {\n /** Model id to call. Default 'claude-sonnet-4-6' via agent-eval defaults. */\n model?: string\n /** Per-call timeout. Default 300s. */\n timeoutMs?: number\n /** Provider-enforced output limit. Default 16000. */\n maxTokens?: number\n /** Pipeline budget for the prompt (source blob truncation). Default 45000. */\n maxSourceChars?: number\n /** Per-file cap before inclusion. Default 20000. */\n maxPerFileChars?: number\n /** HTML cap. Default 30000. */\n maxHtmlChars?: number\n /** Caller-owned transport. Required: agent-eval executes no paid model. */\n chat: ChatClient\n /** Endpoint rates used when the transport reports no billed amount. */\n pricing?: CustomTokenPricing\n costLedger?: CostLedgerHandle\n costPhase?: string\n costTags?: Record<string, string>\n signal?: AbortSignal\n /**\n * Score aggregation strategy. Default `mean` — uniform average across\n * concepts. Cross-vertical comparisons should use `complexity` to\n * neutralize the integrate-vs-render asymmetry.\n */\n weightConcepts?: ConceptWeightStrategy\n /** Override the default complexity → weight table. */\n complexityWeights?: Partial<Record<ConceptComplexity, number>>\n}\n\n// ─── Prompt assembly ────────────────────────────────────────────────────\n\nexport const SEMANTIC_CONCEPT_JUDGE_VERSION = 'semantic-concept-judge-v1-2026-04-24'\n\nconst DEFAULT_MAX_SOURCE = 45_000\nconst DEFAULT_MAX_HTML = 30_000\nconst DEFAULT_MAX_PER_FILE = 20_000\nconst DEFAULT_TIMEOUT = 300_000\nconst DEFAULT_MAX_TOKENS = 16_000\nconst DEFAULT_MODEL = 'claude-sonnet-4-6'\n\nconst SEMANTIC_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['summary', 'concepts'],\n properties: {\n summary: { type: 'string', minLength: 20, maxLength: 600 },\n concepts: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['concept', 'present', 'score', 'evidence', 'severity'],\n properties: {\n concept: { type: 'string', minLength: 1, maxLength: 120 },\n present: { type: 'boolean' },\n score: { type: 'number', minimum: 0, maximum: 10 },\n evidence: { type: 'string', minLength: 5, maxLength: 400 },\n severity: { type: 'string', enum: ['critical', 'major', 'minor', 'info'] },\n },\n },\n },\n },\n}\n\nfunction truncate(body: string, cap: number, label: string): string {\n if (body.length <= cap) return body\n return `${body.slice(0, cap)}\\n… [truncated ${body.length - cap} chars of ${label}]`\n}\n\nfunction buildPrompt(\n input: SemanticConceptJudgeInput,\n opts: { maxPerFileChars: number; maxSourceChars: number; maxHtmlChars: number },\n): string {\n const sourceBlob = input.sourceFiles\n .filter((f) => f.content.length <= opts.maxPerFileChars)\n .map((f) => `--- FILE: ${f.path} ---\\n${f.content}`)\n .join('\\n\\n')\n\n const html = input.servedHtml ?? ''\n\n return `You are a strict code-review judge evaluating whether an agent's 0-to-1 build actually implements the features the user asked for.\n\nYou MUST distinguish:\n (a) WORKING code that implements the concept (rendered UI, wired handler, real API call),\n (b) KEYWORD-PRESENT stub (comments mentioning the concept, variable names, TODOs),\n (c) ABSENT (concept nowhere).\n\nA comment like \"// TODO: add mint button\" is NOT present — score 2-3. Only count a concept as present if there is real functional code: a rendered component, a call handler wired to state or a network call, a computed value actually used.\n\nUSER REQUEST (what the agent was asked to build):\n${input.userRequest}\n\n${input.artifactLabel ? `ARTIFACT METADATA:\\n name: ${input.artifactLabel}\\n description: ${input.artifactDescription ?? ''}\\n\\n` : ''}EXPECTED CONCEPTS (each must be graded independently):\n${input.expectedConcepts\n .map(\n (c, i) =>\n ` ${i + 1}. \"${c.name}\"${c.keywords?.length ? ` — hints: [${c.keywords.slice(0, 6).join(' | ')}]` : ''}`,\n )\n .join('\\n')}\n\n${html ? `SERVED HTML (what the preview returns when hit):\\n${truncate(html, opts.maxHtmlChars, 'HTML')}\\n\\n` : ''}SOURCE FILES (the agent's workdir):\n${truncate(sourceBlob, opts.maxSourceChars, 'source')}\n\nFor EACH concept, return:\n - concept: the concept name as given (match exactly)\n - present: boolean — does a working implementation exist?\n - score: 0-10 — 10 = production-ready; 7 = functional but thin; 4 = partial/stubbed; 2 = keyword-only comment; 0 = absent\n - evidence: cite \"<file>:<line>\" or \"served-html:<selector>\" pointing at the strongest supporting code. If the concept is absent or stubbed, explain what's missing.\n - severity:\n \"info\" when present: true AND score >= 7\n \"minor\" when present: true AND 4 <= score < 7\n \"major\" when present: false OR score < 4\n \"critical\" when the concept is not only absent but a core user flow depends on it\n\nAlso produce a \"summary\" (one sentence, 20-600 chars): overall verdict on whether this is a shippable implementation of the user request vs a keyword-dense placeholder.\n\nBE SKEPTICAL. Keyword matching already passed — your job is to catch what keyword matching misses. If the agent shipped a working build, say so. If it shipped a stub, say so. Don't grade on effort.\n\nReturn STRICT JSON. No prose outside the JSON.`\n}\n\n// ─── Runner ─────────────────────────────────────────────────────────────\n\n/**\n * Run the semantic concept judge. Soft-fails to available=false on\n * LLM/JSON errors — callers in a MultiLayerVerifier pipeline can treat\n * that as \"skip\" rather than \"fail.\"\n */\nexport async function runSemanticConceptJudge(\n input: SemanticConceptJudgeInput,\n options: SemanticConceptJudgeOptions,\n): Promise<SemanticConceptJudgeResult> {\n const start = Date.now()\n const totalCount = input.expectedConcepts.length\n\n if (totalCount === 0) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount: 0,\n findings: [],\n summary: 'no expected concepts declared',\n durationMs: 0,\n costUsd: null,\n available: false,\n error: 'no expected concepts declared',\n }\n }\n\n const opts = {\n chat: options.chat,\n model: options.model ?? options.chat.defaultModel ?? DEFAULT_MODEL,\n timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT,\n maxTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n maxSourceChars: options.maxSourceChars ?? DEFAULT_MAX_SOURCE,\n maxPerFileChars: options.maxPerFileChars ?? DEFAULT_MAX_PER_FILE,\n maxHtmlChars: options.maxHtmlChars ?? DEFAULT_MAX_HTML,\n ...(options.pricing ? { pricing: options.pricing } : {}),\n costLedger: options.costLedger ?? new CostLedger(),\n costPhase: options.costPhase ?? 'judge.semantic-concept',\n costTags: options.costTags ?? {},\n signal: options.signal ?? new AbortController().signal,\n weightConcepts: options.weightConcepts ?? 'mean',\n complexityWeights: { ...DEFAULT_COMPLEXITY_WEIGHTS, ...(options.complexityWeights ?? {}) },\n }\n\n // Build a name → weight map for aggregation. Mean strategy keeps every\n // weight at 1 (uniform average). Complexity strategy reads the table\n // and lets an explicit `weight` override. Explicit strategy uses ONLY\n // the spec's `weight` (defaulting to 1).\n const weightForConcept = (spec: ConceptSpec): number => {\n if (opts.weightConcepts === 'mean') return 1\n if (spec.weight != null) return spec.weight\n if (opts.weightConcepts === 'complexity') {\n return opts.complexityWeights[spec.complexity ?? 'render'] ?? 1\n }\n return 1\n }\n const weightByName = new Map<string, number>(\n input.expectedConcepts.map((c) => [c.name, weightForConcept(c)]),\n )\n\n let receipt: CostReceipt | undefined\n try {\n const request = {\n model: opts.model,\n messages: [\n {\n role: 'system' as const,\n content:\n 'You are a strict code-review judge. Return strict JSON only. No prose outside the JSON. A keyword in a comment is NOT a working implementation.',\n },\n { role: 'user' as const, content: buildPrompt(input, opts) },\n ],\n jsonSchema: { name: 'semantic_concept_judge', schema: SEMANTIC_SCHEMA },\n temperature: 0,\n maxTokens: opts.maxTokens,\n timeoutMs: opts.timeoutMs,\n } satisfies LlmCallRequest\n const paid = await paidJsonChat<{ summary: string; concepts: ConceptFinding[] }>({\n chat: opts.chat,\n request,\n ledger: opts.costLedger,\n channel: 'judge',\n phase: opts.costPhase,\n actor: 'semantic-concept',\n tags: opts.costTags,\n signal: opts.signal,\n ...(opts.pricing ? { pricing: opts.pricing } : {}),\n })\n receipt = paid.receipt\n if (!paid.succeeded) throw paid.error\n const { value } = paid\n\n if (!value?.concepts || !Array.isArray(value.concepts)) {\n throw new Error('judge returned malformed response — expected array under \"concepts\"')\n }\n\n const findings: ConceptFinding[] = value.concepts.map((c) => ({\n concept: String(c.concept),\n present: Boolean(c.present),\n score: Math.max(0, Math.min(10, Number(c.score ?? 0))),\n evidence: String(c.evidence ?? ''),\n severity: (['critical', 'major', 'minor', 'info'] as const).includes(c.severity)\n ? c.severity\n : 'info',\n }))\n\n const presentCount = findings.filter((f) => f.present && f.score >= 7).length\n let weightSum = 0\n let weightedScoreSum = 0\n for (const f of findings) {\n const w = weightByName.get(f.concept) ?? 1\n weightSum += w\n weightedScoreSum += w * f.score\n }\n const scoreAvg =\n weightSum > 0\n ? weightedScoreSum / weightSum\n : findings.reduce((a, f) => a + f.score, 0) / Math.max(1, findings.length)\n\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: Number((scoreAvg / 10).toFixed(3)),\n presentCount,\n totalCount,\n findings,\n summary: String(value.summary ?? ''),\n durationMs: Date.now() - start,\n costUsd: paid.receipt.costUnknown ? null : paid.receipt.costUsd,\n available: true,\n }\n } catch (err) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount,\n findings: [],\n summary: '',\n durationMs: Date.now() - start,\n costUsd: receipt && !receipt.costUnknown ? receipt.costUsd : null,\n available: false,\n error: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\n/**\n * Factory: pin LLM options once, return a closure that accepts inputs.\n * Convenient for pipelines that want to share a single LlmClient config.\n */\nexport function createSemanticConceptJudge(\n options: SemanticConceptJudgeOptions,\n): (input: SemanticConceptJudgeInput) => Promise<SemanticConceptJudgeResult> {\n return (input) => runSemanticConceptJudge(input, options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"semantic-concept-judge-Bt7bXAAo.js","names":[],"sources":["../src/analyst/chat-trace-engine.ts","../src/locked-jsonl-appender.ts","../src/analyst/findings-store.ts","../src/semantic-concept-judge.ts"],"sourcesContent":["/**\n * A `TraceAnalysisEngine` that runs entirely inside Node against a\n * caller-owned `ChatClient`.\n *\n * The other engine in this package, `createDspyRlmTraceEngine`, reaches the\n * DSPy RLM through a Python subprocess. That made every model-backed analyst\n * unreachable for a consumer that already owns a model seam and no Python:\n * `createTraceAnalyst` requires an engine, the only exported constructor\n * required a Python runner, so `buildDefaultAnalystRegistry()` registered the\n * deterministic analyst alone and `analystsFromRegistry` refused the result.\n *\n * This engine closes that path. It drives the same investigation contract —\n * bounded trace tools, a prose answer, a strict findings array — with native\n * function calls over the transport the caller already bound with\n * `createChatClient`. A caller holding a bare\n * `(request: LlmCallRequest) => Promise<LlmCallResult>` adapts it in one line:\n *\n * createChatClient({ transport: 'custom', chat: call, defaultModel, maximumAttempts })\n *\n * agent-eval still executes no paid model and holds no provider credential.\n * Every call goes through `paidChat`, so the shared cost ledger reserves the\n * priced maximum before the call and settles the receipt after it.\n *\n * Shape of one investigation:\n * 1. Investigation turns. The model reads the trace store through the tools\n * its analyst kind was given. A turn that requests no tool ends the\n * phase; an exhausted iteration or tool budget also ends it.\n * 2. One report turn. The model returns `{ answer, findings }` as JSON,\n * decoded by the same `decodeRawFindingArray` the Python bridge uses, so\n * a row this engine accepts is a row that engine could report.\n */\n\nimport { z } from 'zod'\nimport { paidChat } from '../chat-json-call'\nimport type { CustomTokenPricing } from '../cost-ledger'\nimport {\n extractJsonPayload,\n type LlmCallRequest,\n type LlmMessage,\n type LlmThinkingMode,\n type LlmToolCall,\n type LlmToolDefinition,\n} from '../llm-client'\nimport type { TraceAnalysisToolDescriptor } from '../trace-analyst/tools'\nimport type { ChatClient } from './chat-client'\nimport {\n DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS,\n type TraceAnalysisEngine,\n type TraceAnalysisEngineRequest,\n type TraceAnalysisEngineResult,\n} from './engine'\nimport { decodeRawFindingArray } from './finding-codec'\nimport { RawAnalystFindingSchema } from './finding-signature'\n\n/** Bumped whenever this engine's execution behavior changes. */\nconst CHAT_TRACE_ENGINE_VERSION = '1.0.0'\nconst ENGINE_ID = 'chat-trace'\n\n/** Marker appended to a tool result cut down to the retained-output budget. */\nconst TRUNCATION_MARKER = '\\n…[truncated to the analyst maxOutputChars budget]'\n\nexport interface ChatTraceEngineOptions {\n /**\n * Caller-owned transport. Build it with `createChatClient`; agent-eval never\n * receives the provider credential.\n */\n chat: ChatClient\n /** Model for every call. Defaults to the client's `defaultModel`. */\n model?: string\n /** Endpoint rates used when the transport reports no billed amount. */\n pricing?: CustomTokenPricing\n /** Completion cap per turn. Default: {@link DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS}. */\n maxOutputTokens?: number\n /** Sampling temperature. Omitted from the request when absent. */\n temperature?: number\n /** Provider reasoning mode. Omitted when the provider default should apply. */\n thinking?: LlmThinkingMode\n /** Per-call deadline handed to the transport. */\n requestTimeoutMs?: number\n /**\n * How the report turn asks for structured output.\n *\n * `json-object` sends JSON mode, which every OpenAI-compatible endpoint\n * accepts, and relies on the finding grammar the analyst kind already puts\n * in its instructions. `json-schema` sends the generated response schema as\n * well, which stricter endpoints honour and older ones reject. Default:\n * `json-object`.\n */\n reportFormat?: 'json-object' | 'json-schema'\n}\n\n/**\n * The report envelope. Its `findings` rows are generated from\n * `RawAnalystFindingSchema`, so the schema offered to a provider and the\n * decoder that accepts the answer cannot drift apart.\n */\nfunction buildReportJsonSchema(): { name: string; schema: Record<string, unknown> } {\n const row = z.toJSONSchema(RawAnalystFindingSchema, { target: 'draft-7' }) as Record<\n string,\n unknown\n >\n delete row.$schema\n return {\n name: 'trace_analysis_report',\n schema: {\n type: 'object',\n additionalProperties: false,\n required: ['answer', 'findings'],\n properties: {\n answer: { type: 'string', description: 'Direct prose answer to the question.' },\n findings: { type: 'array', items: row },\n },\n },\n }\n}\n\nconst REPORT_JSON_SCHEMA = buildReportJsonSchema()\n\n/**\n * Run bounded recursive trace analysis in-process over a caller-owned chat\n * transport. No Python, no subprocess, no loopback proxy.\n */\nexport function createChatTraceEngine(options: ChatTraceEngineOptions): TraceAnalysisEngine {\n const model = resolveModel(options)\n const maxOutputTokens = options.maxOutputTokens ?? DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS\n if (!Number.isSafeInteger(maxOutputTokens) || maxOutputTokens <= 0) {\n throw new TypeError('chat trace engine maxOutputTokens must be a positive safe integer')\n }\n if (\n options.temperature !== undefined &&\n (!Number.isFinite(options.temperature) || options.temperature < 0)\n ) {\n throw new TypeError('chat trace engine temperature must be a non-negative finite number')\n }\n if (\n options.requestTimeoutMs !== undefined &&\n (!Number.isSafeInteger(options.requestTimeoutMs) || options.requestTimeoutMs <= 0)\n ) {\n throw new TypeError('chat trace engine requestTimeoutMs must be a positive safe integer')\n }\n const reportFormat = options.reportFormat ?? 'json-object'\n if (reportFormat !== 'json-object' && reportFormat !== 'json-schema') {\n throw new TypeError(`chat trace engine reportFormat must be json-object or json-schema`)\n }\n\n return {\n id: ENGINE_ID,\n description:\n 'In-process recursive trace analysis over a caller-owned ChatClient with native tool calls.',\n model,\n version: CHAT_TRACE_ENGINE_VERSION,\n executionConfig: {\n kind: ENGINE_ID,\n model,\n transport: options.chat.transport,\n maximum_attempts: options.chat.maximumAttempts ?? null,\n pricing: options.pricing ? { ...options.pricing } : null,\n max_output_tokens: maxOutputTokens,\n temperature: options.temperature ?? null,\n thinking: options.thinking ?? null,\n request_timeout_ms: options.requestTimeoutMs ?? null,\n report_format: reportFormat,\n report_schema_name: REPORT_JSON_SCHEMA.name,\n },\n analyze: (request) => analyze(request, { ...options, model, maxOutputTokens, reportFormat }),\n }\n}\n\ninterface ResolvedOptions extends ChatTraceEngineOptions {\n model: string\n maxOutputTokens: number\n reportFormat: 'json-object' | 'json-schema'\n}\n\nasync function analyze(\n request: TraceAnalysisEngineRequest,\n options: ResolvedOptions,\n): Promise<TraceAnalysisEngineResult> {\n // One model call is spent on the report, so a budget of one call would buy\n // an answer with no investigation behind it. Refuse instead of pretending.\n if (request.limits.maxLlmCalls < 2) {\n throw new Error(\n `chat trace engine reserves one model call for the report, so maxLlmCalls must be at least 2 (analyst '${request.analystId}' declared ${request.limits.maxLlmCalls})`,\n )\n }\n const toolsByName = new Map(request.tools.map((tool) => [tool.name, tool]))\n if (toolsByName.size !== request.tools.length) {\n throw new Error(`chat trace engine received duplicate tool names for '${request.analystId}'`)\n }\n const toolDefinitions: LlmToolDefinition[] = request.tools.map((tool) => ({\n type: 'function',\n function: { name: tool.name, description: tool.description, parameters: tool.parameters },\n }))\n const investigationTurns = Math.max(\n 1,\n Math.min(request.limits.maxIterations, request.limits.maxLlmCalls - 1),\n )\n\n const messages: LlmMessage[] = [\n { role: 'system', content: systemPrompt(request, investigationTurns) },\n ]\n if (request.taskInputs) {\n messages.push({ role: 'user', content: renderTaskInputs(request.taskInputs) })\n }\n messages.push({ role: 'user', content: request.question })\n\n request.log?.('trace analyst engine started', {\n engine: ENGINE_ID,\n model: options.model,\n transport: options.chat.transport,\n tools: request.tools.map((tool) => tool.name),\n limits: request.limits,\n })\n\n const trajectory: unknown[] = []\n const servedModels = new Set<string>()\n let modelCalls = 0\n let toolCalls = 0\n let truncatedToolResults = 0\n let toolBudgetExhausted = false\n let turnsUsed = 0\n let stoppedOnAnswer = false\n\n for (let turn = 0; turn < investigationTurns; turn++) {\n turnsUsed = turn + 1\n const response = await callModel(request, options, {\n messages,\n tools: toolDefinitions,\n toolChoice: 'auto',\n purpose: 'investigation',\n })\n modelCalls += 1\n servedModels.add(response.servedModel ?? 'unreported')\n const requested = response.toolCalls ?? []\n messages.push({\n role: 'assistant',\n content: response.content,\n ...(requested.length > 0 ? { toolCalls: requested } : {}),\n })\n trajectory.push({\n turn: turnsUsed,\n phase: 'investigation',\n content: response.content,\n tool_calls: requested.map((call) => ({ id: call.id, name: call.name })),\n finish_reason: response.finishReason ?? null,\n })\n if (requested.length === 0) {\n stoppedOnAnswer = true\n break\n }\n for (const call of requested) {\n if (toolCalls >= request.limits.maxToolCalls) {\n toolBudgetExhausted = true\n // Every requested id still needs an answer: a provider rejects the\n // next turn when one tool call is left unanswered.\n messages.push(toolMessage(call, exhaustedToolBudget(request.limits.maxToolCalls)))\n continue\n }\n toolCalls += 1\n const executed = await executeTool(call, toolsByName, request)\n if (executed.truncated) truncatedToolResults += 1\n messages.push(toolMessage(call, executed.payload))\n trajectory.push({\n turn: turnsUsed,\n phase: 'tool',\n name: call.name,\n ok: executed.ok,\n truncated: executed.truncated,\n result_chars: executed.payload.length,\n })\n }\n if (toolBudgetExhausted) break\n }\n\n messages.push({ role: 'user', content: reportPrompt(options.reportFormat) })\n const report = await callModel(request, options, {\n messages,\n purpose: 'report',\n json: options.reportFormat,\n })\n modelCalls += 1\n servedModels.add(report.servedModel ?? 'unreported')\n trajectory.push({\n turn: turnsUsed + 1,\n phase: 'report',\n content: report.content,\n finish_reason: report.finishReason ?? null,\n })\n\n const parsed = parseReport(report.content, request.analystId, (index, reason) => {\n request.log?.('finding rejected: report row failed schema validation', {\n engine: ENGINE_ID,\n index,\n reason,\n })\n })\n\n const result: TraceAnalysisEngineResult = {\n answer: parsed.answer,\n findings: parsed.findings,\n trajectory,\n modelCalls,\n toolCalls,\n runtime: {\n engine: ENGINE_ID,\n model: options.model,\n transport: options.chat.transport,\n report_format: options.reportFormat,\n investigation_turns: turnsUsed,\n investigation_stopped_on_answer: stoppedOnAnswer,\n tool_budget_exhausted: toolBudgetExhausted,\n truncated_tool_results: truncatedToolResults,\n rejected_findings: parsed.rejectedFindings,\n // A gateway can answer `model: X` from another model on HTTP 200, so the\n // ids the provider echoed travel with the result as evidence.\n served_models: [...servedModels].sort(),\n task_inputs: request.taskInputs ? 'prompt-delivered' : 'none',\n },\n }\n request.log?.('trace analyst engine completed', {\n engine: ENGINE_ID,\n model_calls: result.modelCalls,\n tool_calls: result.toolCalls,\n findings: result.findings.length,\n })\n return result\n}\n\ninterface ModelTurn {\n content: string\n toolCalls?: LlmToolCall[]\n servedModel?: string | null\n finishReason?: string | null\n}\n\nasync function callModel(\n request: TraceAnalysisEngineRequest,\n options: ResolvedOptions,\n turn: {\n messages: LlmMessage[]\n tools?: LlmToolDefinition[]\n toolChoice?: LlmCallRequest['toolChoice']\n purpose: 'investigation' | 'report'\n json?: 'json-object' | 'json-schema'\n },\n): Promise<ModelTurn> {\n const chatRequest: LlmCallRequest = {\n model: options.model,\n // The transport may mutate what it is handed; the conversation is ours.\n messages: turn.messages.map((message) => ({ ...message })),\n maxTokens: options.maxOutputTokens,\n ...(turn.tools && turn.tools.length > 0\n ? { tools: turn.tools, ...(turn.toolChoice ? { toolChoice: turn.toolChoice } : {}) }\n : {}),\n ...(turn.json ? { jsonMode: true } : {}),\n ...(turn.json === 'json-schema' ? { jsonSchema: REPORT_JSON_SCHEMA } : {}),\n ...(options.temperature === undefined ? {} : { temperature: options.temperature }),\n ...(options.thinking === undefined ? {} : { thinking: options.thinking }),\n ...(options.requestTimeoutMs === undefined ? {} : { timeoutMs: options.requestTimeoutMs }),\n }\n const paid = await paidChat({\n chat: options.chat,\n request: chatRequest,\n ledger: request.costLedger,\n channel: 'analyst',\n phase: request.costPhase,\n actor: request.analystId,\n ...(request.costTags ? { tags: request.costTags } : {}),\n ...(options.pricing ? { pricing: options.pricing } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n })\n if (!paid.succeeded) {\n // The transport already spent its own attempts. A failure here ends the\n // investigation: continuing would report an answer the model never gave.\n throw new Error(\n `chat trace engine ${turn.purpose} call failed for '${request.analystId}': ${paid.error.message}`,\n { cause: paid.error },\n )\n }\n return {\n content: paid.response.content,\n ...(paid.response.toolCalls ? { toolCalls: paid.response.toolCalls } : {}),\n ...(paid.response.servedModel === undefined ? {} : { servedModel: paid.response.servedModel }),\n ...(paid.response.finishReason === undefined\n ? {}\n : { finishReason: paid.response.finishReason }),\n }\n}\n\ninterface ExecutedTool {\n ok: boolean\n truncated: boolean\n payload: string\n}\n\nasync function executeTool(\n call: LlmToolCall,\n toolsByName: ReadonlyMap<string, TraceAnalysisToolDescriptor>,\n request: TraceAnalysisEngineRequest,\n): Promise<ExecutedTool> {\n const descriptor = toolsByName.get(call.name)\n if (!descriptor) {\n return {\n ok: false,\n truncated: false,\n payload: toolError(\n `unknown tool '${call.name}'; available tools are ${[...toolsByName.keys()].sort().join(', ')}`,\n ),\n }\n }\n let args: unknown\n try {\n args = call.argumentsJson.trim() === '' ? {} : JSON.parse(call.argumentsJson)\n } catch (error) {\n return {\n ok: false,\n truncated: false,\n payload: toolError(\n `arguments for '${call.name}' were not JSON: ${messageOf(error)}. Send valid JSON arguments.`,\n ),\n }\n }\n try {\n const value = await descriptor.handler(\n args,\n request.signal ? { signal: request.signal } : undefined,\n )\n return truncateToolPayload(JSON.stringify(value ?? null), request.limits.maxOutputChars)\n } catch (error) {\n // A bounded store refuses an oversized or malformed read by design. That\n // refusal is the model's next instruction, not the run's failure.\n if (request.signal?.aborted) throw error\n request.log?.('trace tool failed', {\n engine: ENGINE_ID,\n analyst_id: request.analystId,\n tool: call.name,\n reason: messageOf(error),\n })\n return { ok: false, truncated: false, payload: toolError(messageOf(error)) }\n }\n}\n\nfunction truncateToolPayload(payload: string, maxOutputChars: number): ExecutedTool {\n if (payload.length <= maxOutputChars) return { ok: true, truncated: false, payload }\n const keep = Math.max(0, maxOutputChars - TRUNCATION_MARKER.length)\n return { ok: true, truncated: true, payload: `${payload.slice(0, keep)}${TRUNCATION_MARKER}` }\n}\n\nfunction toolMessage(call: LlmToolCall, content: string): LlmMessage {\n return { role: 'tool', toolCallId: call.id, content }\n}\n\nfunction toolError(reason: string): string {\n return JSON.stringify({ error: reason })\n}\n\nfunction exhaustedToolBudget(maxToolCalls: number): string {\n return toolError(\n `the trace-tool budget of ${maxToolCalls} calls is spent; answer from the evidence already read`,\n )\n}\n\nfunction systemPrompt(request: TraceAnalysisEngineRequest, investigationTurns: number): string {\n return [\n request.instructions.trim(),\n [\n 'HOW THIS INVESTIGATION RUNS:',\n `- You have ${investigationTurns} investigation turns and at most ${request.limits.maxToolCalls} trace-tool calls.`,\n '- Call the trace tools to read the store. Never state a trace fact you did not read.',\n `- A tool result longer than ${request.limits.maxOutputChars} characters is truncated; narrow the query instead of asking again.`,\n '- A tool result carrying an \"error\" field is feedback: fix the call or take another route.',\n '- Answer with no tool call once you have the evidence. You are then asked for the final report.',\n ].join('\\n'),\n ].join('\\n\\n')\n}\n\nfunction reportPrompt(reportFormat: 'json-object' | 'json-schema'): string {\n return [\n 'Report now. Do not call any more tools.',\n 'Return one JSON object with exactly two fields:',\n ' \"answer\": a direct prose answer to the question.',\n ' \"findings\": an array of findings in the schema above. Emit [] when there is nothing to report.',\n reportFormat === 'json-object'\n ? 'Return the object alone, with no surrounding prose and no code fence.'\n : 'Return the object in the response schema you were given.',\n ].join('\\n')\n}\n\nfunction renderTaskInputs(taskInputs: Readonly<Record<string, unknown>>): string {\n // The engine contract forbids dropping structured inputs. This engine has no\n // code environment to bind them as variables, so they are delivered whole as\n // conversation material and the runtime record says which way they arrived.\n return [\n 'TASK INPUTS — structured material delivered with the question, not fetched through tools:',\n JSON.stringify(taskInputs, null, 2),\n ].join('\\n')\n}\n\ninterface ParsedReport {\n answer: string\n findings: TraceAnalysisEngineResult['findings']\n rejectedFindings: number\n}\n\nfunction parseReport(\n content: string,\n analystId: string,\n onRejectedFinding: (index: number, reason: string) => void,\n): ParsedReport {\n const envelope = coerceReportEnvelope(content, analystId)\n if (typeof envelope.answer !== 'string' || !envelope.answer.trim()) {\n throw new Error(`chat trace engine report for '${analystId}' carried no answer`)\n }\n // Findings are model output: one malformed row is model noise, not an engine\n // fault, and the rest of the paid investigation must survive it.\n const decoded = decodeRawFindingArray(envelope.findings)\n if (decoded.topLevelError !== undefined) {\n throw new Error(\n `chat trace engine report for '${analystId}' had a malformed findings array: ${decoded.topLevelError}`,\n )\n }\n for (const rejection of decoded.rejected) {\n onRejectedFinding(\n rejection.index,\n `${rejection.code}${rejection.path ? ` at ${rejection.path}` : ''}: ${rejection.message}`,\n )\n }\n return {\n answer: envelope.answer,\n findings: decoded.accepted,\n rejectedFindings: decoded.rejected.length,\n }\n}\n\nfunction coerceReportEnvelope(content: string, analystId: string): Record<string, unknown> {\n let value: unknown\n try {\n value = JSON.parse(extractJsonPayload(content))\n } catch (error) {\n throw new Error(`chat trace engine report for '${analystId}' was not JSON: ${messageOf(error)}`)\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error(`chat trace engine report for '${analystId}' was not a JSON object`)\n }\n return value as Record<string, unknown>\n}\n\nfunction resolveModel(options: ChatTraceEngineOptions): string {\n const model = options.model ?? options.chat.defaultModel\n if (typeof model !== 'string' || !model.trim() || model !== model.trim()) {\n throw new TypeError(\n 'chat trace engine needs a model: pass ChatTraceEngineOptions.model or bind defaultModel on the ChatClient',\n )\n }\n return model\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","/**\n * LockedJsonlAppender — mutex-serialized JSONL append helper for arbitrary\n * payloads. The reference-replay store does the same thing for typed\n * `ReferenceReplayRun` rows; this is the generic version used by\n * `MutationTelemetry`, `TrialTelemetry`, and any other consumer that wants\n * append-only durable telemetry without rolling its own lock.\n *\n * Locks are per absolute file path (process-local). Cross-process\n * concurrency is NOT addressed — that's an fcntl/flock problem.\n */\n\nimport { appendFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { Mutex } from './concurrency'\n\nconst mutexes = new Map<string, Mutex>()\n\nfunction getMutex(path: string): Mutex {\n let m = mutexes.get(path)\n if (!m) {\n m = new Mutex()\n mutexes.set(path, m)\n }\n return m\n}\n\nexport class LockedJsonlAppender {\n private readonly mutex: Mutex\n constructor(public readonly path: string) {\n this.mutex = getMutex(path)\n if (!existsSync(dirname(path))) {\n mkdirSync(dirname(path), { recursive: true })\n }\n }\n\n async append(entry: unknown): Promise<void> {\n const line = `${JSON.stringify(entry)}\\n`\n await this.mutex.runExclusive(() => {\n appendFileSync(this.path, line)\n })\n }\n}\n\n/** Reset all internal mutex state — tests only. */\nexport function resetLockedAppendersForTesting(): void {\n mutexes.clear()\n}\n","/**\n * FindingsStore — durable persistence for AnalystFinding rows + a diff\n * helper so we can answer \"what changed since the last run?\" without\n * recomputing analysts.\n *\n * On-disk shape is JSONL: one finding per line, append-only, locked via\n * LockedJsonlAppender. Operators get crash-safety (no partial JSON),\n * cheap reads (sequential parse), and trivial backup (rsync the file).\n *\n * Reads are non-locking: a reader sees a consistent snapshot of all\n * fully-written lines and skips an incomplete trailing line if the\n * writer is mid-append. Cross-process locking is intentionally out of\n * scope (see locked-jsonl-appender.ts).\n *\n * The store is run-scoped: callers pass `runId` on append and on load,\n * which keeps multi-run files cleanly partitioned. The `diffFindings`\n * helper compares two run-id sets using stable `finding_id` semantics —\n * the diff is the cross-run signal the regression dashboard renders.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { LockedJsonlAppender } from '../locked-jsonl-appender'\nimport type { AnalystFinding } from './types'\n\n/**\n * One persisted row. We attach `run_id` on disk so a single file can\n * hold multiple runs and the diff helper can query without re-walking\n * separate files.\n */\nexport interface PersistedFinding extends AnalystFinding {\n run_id: string\n}\n\nexport class FindingsStore {\n private readonly appender: LockedJsonlAppender\n\n constructor(public readonly path: string) {\n this.appender = new LockedJsonlAppender(path)\n }\n\n async append(runId: string, findings: AnalystFinding[]): Promise<void> {\n for (const f of findings) {\n const row: PersistedFinding = { ...f, run_id: runId }\n await this.appender.append(row)\n }\n }\n\n /** Load every persisted finding. Discards malformed trailing lines silently. */\n loadAll(): PersistedFinding[] {\n if (!existsSync(this.path)) return []\n const raw = readFileSync(this.path, 'utf8')\n if (!raw) return []\n const out: PersistedFinding[] = []\n for (const line of raw.split('\\n')) {\n if (!line) continue\n try {\n out.push(JSON.parse(line) as PersistedFinding)\n } catch {\n // Skip torn trailing line — the lock guarantees no torn lines\n // mid-file, only at EOF when a writer is in-flight.\n }\n }\n return out\n }\n\n /** Filter to a single run. */\n loadRun(runId: string): PersistedFinding[] {\n return this.loadAll().filter((r) => r.run_id === runId)\n }\n}\n\n// ── Cross-run diff ──────────────────────────────────────────────────\n\nexport interface FindingsDiff {\n /** New finding ids in `current` that weren't in `previous`. */\n appeared: PersistedFinding[]\n /** Finding ids in `previous` that aren't in `current`. */\n disappeared: PersistedFinding[]\n /** Same finding id present in both runs and unchanged per the materiality test. */\n persisted: PersistedFinding[]\n /**\n * Same finding id in both runs but at least one non-identity field\n * shifted per `DiffPolicy.isMaterial`. Reported as [previous, current].\n */\n changed: Array<{ previous: PersistedFinding; current: PersistedFinding }>\n}\n\nexport interface DiffPolicy {\n /**\n * Predicate that decides whether two findings (same finding_id) count\n * as a material change. Defaults to {@link defaultIsMaterial}: severity\n * shift, confidence Δ > 0.05, or evidence count change. Compliance /\n * perf consumers MAY supply a stricter predicate (e.g. rationale text\n * diff, metric Δ thresholds).\n */\n isMaterial?: (previous: AnalystFinding, current: AnalystFinding) => boolean\n}\n\n/**\n * Default materiality test. Deliberately narrow so LLM-reword churn\n * doesn't flood the diff. Stricter tests are opt-in via DiffPolicy.\n */\nexport function defaultIsMaterial(a: AnalystFinding, b: AnalystFinding): boolean {\n if (a.severity !== b.severity) return true\n if (Math.abs((a.confidence ?? 0) - (b.confidence ?? 0)) > 0.05) return true\n if (a.evidence_refs.length !== b.evidence_refs.length) return true\n return false\n}\n\n/**\n * Diff two findings sets by stable finding_id. Callers typically load\n * the two run-id slices from the same store and pass them in.\n */\nexport function diffFindings(\n previous: PersistedFinding[],\n current: PersistedFinding[],\n policy: DiffPolicy = {},\n): FindingsDiff {\n const isMaterial = policy.isMaterial ?? defaultIsMaterial\n const prevById = new Map(previous.map((f) => [f.finding_id, f]))\n const curById = new Map(current.map((f) => [f.finding_id, f]))\n\n const appeared: PersistedFinding[] = []\n const disappeared: PersistedFinding[] = []\n const persisted: PersistedFinding[] = []\n const changed: FindingsDiff['changed'] = []\n\n for (const [id, cur] of curById) {\n const prev = prevById.get(id)\n if (!prev) {\n appeared.push(cur)\n continue\n }\n if (isMaterial(prev, cur)) {\n changed.push({ previous: prev, current: cur })\n } else {\n persisted.push(cur)\n }\n }\n for (const [id, prev] of prevById) {\n if (!curById.has(id)) disappeared.push(prev)\n }\n return { appeared, disappeared, persisted, changed }\n}\n","/**\n * Semantic concept judge — \"does the built artifact actually implement\n * the features the user asked for?\"\n *\n * Distinct from the domain/code/coherence judges in `judges.ts`:\n * - those judges score free-form conversational agent outputs along\n * quality dimensions (accuracy, depth, etc.)\n * - this judge scores a *built artifact* (served HTML + source files)\n * against an explicit list of expected concepts, returning per-concept\n * {present, score 0-10, evidence, severity}.\n *\n * The judge is strict about distinguishing (a) a working implementation\n * from (b) a keyword-present stub. \"// TODO: mint button\" is NOT present.\n * Only real, functional, wired-up code counts.\n *\n * Use via {@link createSemanticConceptJudge} or directly via\n * {@link runSemanticConceptJudge}. Soft-fails (available=false) on LLM\n * or JSON-parse errors so the caller can treat that as \"layer skipped\"\n * rather than \"layer failed\" in a multi-layer pipeline.\n */\n\nimport type { ChatClient } from './analyst/chat-client'\nimport { paidJsonChat } from './chat-json-call'\nimport {\n CostLedger,\n type CostLedgerHandle,\n type CostReceipt,\n type CustomTokenPricing,\n} from './cost-ledger'\nimport type { LlmCallRequest } from './llm-client'\nimport type { Severity } from './multi-layer-verifier'\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\n/**\n * Implementation complexity class for weighted scoring.\n *\n * - `render` (default): the concept is a UI surface that displays static\n * data — render a list, show a counter, lay out a button. Single-file\n * work, no external integration.\n * - `integrate`: the concept requires wiring a real external system —\n * wallet connect (wagmi + RainbowKit + chain config), payment provider\n * (Stripe Elements + intent + webhook), an API client with auth.\n * Multi-file, library-knowledge, runtime correctness matters.\n * - `compute`: the concept requires algorithmic work — solver, simulator,\n * constraint propagation, ML inference. Correctness > UI polish.\n *\n * Default weights (when applied via `weightConcepts: 'complexity'`):\n * render=1.0, integrate=2.0, compute=2.5\n *\n * Cross-vertical scoring without complexity weighting silently inflates\n * the rate of UI-heavy verticals (healthcare, fintech dashboards) vs\n * integration-heavy verticals (DeFi, wallets) — all concepts treated\n * equally even though the agent does 2-3x the work for `integrate`.\n */\nexport type ConceptComplexity = 'render' | 'integrate' | 'compute'\n\nexport interface ConceptSpec {\n name: string\n /** Short hints that help the judge; not used for matching. */\n keywords?: string[]\n /** Optional explicit weight; default 1.0. Overrides complexity-derived weight. */\n weight?: number\n /** Implementation complexity class. Default `render`. */\n complexity?: ConceptComplexity\n}\n\nexport interface ConceptFinding {\n concept: string\n present: boolean\n /** 0..10. 10 = production-ready; 7 = functional thin; 4 = partial; 0 = absent. */\n score: number\n evidence: string\n severity: Severity\n}\n\nexport interface SemanticConceptJudgeInput {\n /** Full natural-language prompt the agent was handed. */\n userRequest: string\n /** Rendered HTML the preview returns (UI artifacts). Optional. */\n servedHtml?: string\n /** Top-level source files from the agent's workdir. */\n sourceFiles: Array<{ path: string; content: string }>\n /** The expected concept list. */\n expectedConcepts: ConceptSpec[]\n /** Free-form metadata (id, difficulty) to inject into the prompt. */\n artifactLabel?: string\n artifactDescription?: string\n}\n\nexport interface SemanticConceptJudgeResult {\n kind: 'semantic-concept'\n version: string\n /** Normalized 0..1 score — mean of per-concept scores / 10. */\n score: number\n presentCount: number\n totalCount: number\n findings: ConceptFinding[]\n summary: string\n durationMs: number\n costUsd: number | null\n /** False on LLM/JSON error — treat as \"skipped / unable to judge\" in pipelines. */\n available: boolean\n error?: string\n}\n\n/**\n * Score-aggregation strategy. `mean` averages 0-10 scores uniformly.\n * `complexity` applies the default weight table (render=1, integrate=2,\n * compute=2.5) unless a concept has an explicit `weight`. `explicit`\n * honors only `weight` (defaulting to 1 for unspecified).\n */\nexport type ConceptWeightStrategy = 'mean' | 'complexity' | 'explicit'\n\nexport const DEFAULT_COMPLEXITY_WEIGHTS: Record<ConceptComplexity, number> = {\n render: 1.0,\n integrate: 2.0,\n compute: 2.5,\n}\n\nexport interface SemanticConceptJudgeOptions {\n /** Model id to call. Default 'claude-sonnet-4-6' via agent-eval defaults. */\n model?: string\n /** Per-call timeout. Default 300s. */\n timeoutMs?: number\n /** Provider-enforced output limit. Default 16000. */\n maxTokens?: number\n /** Pipeline budget for the prompt (source blob truncation). Default 45000. */\n maxSourceChars?: number\n /** Per-file cap before inclusion. Default 20000. */\n maxPerFileChars?: number\n /** HTML cap. Default 30000. */\n maxHtmlChars?: number\n /** Caller-owned transport. Required: agent-eval executes no paid model. */\n chat: ChatClient\n /** Endpoint rates used when the transport reports no billed amount. */\n pricing?: CustomTokenPricing\n costLedger?: CostLedgerHandle\n costPhase?: string\n costTags?: Record<string, string>\n signal?: AbortSignal\n /**\n * Score aggregation strategy. Default `mean` — uniform average across\n * concepts. Cross-vertical comparisons should use `complexity` to\n * neutralize the integrate-vs-render asymmetry.\n */\n weightConcepts?: ConceptWeightStrategy\n /** Override the default complexity → weight table. */\n complexityWeights?: Partial<Record<ConceptComplexity, number>>\n}\n\n// ─── Prompt assembly ────────────────────────────────────────────────────\n\nexport const SEMANTIC_CONCEPT_JUDGE_VERSION = 'semantic-concept-judge-v1-2026-04-24'\n\nconst DEFAULT_MAX_SOURCE = 45_000\nconst DEFAULT_MAX_HTML = 30_000\nconst DEFAULT_MAX_PER_FILE = 20_000\nconst DEFAULT_TIMEOUT = 300_000\nconst DEFAULT_MAX_TOKENS = 16_000\nconst DEFAULT_MODEL = 'claude-sonnet-4-6'\n\nconst SEMANTIC_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['summary', 'concepts'],\n properties: {\n summary: { type: 'string', minLength: 20, maxLength: 600 },\n concepts: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['concept', 'present', 'score', 'evidence', 'severity'],\n properties: {\n concept: { type: 'string', minLength: 1, maxLength: 120 },\n present: { type: 'boolean' },\n score: { type: 'number', minimum: 0, maximum: 10 },\n evidence: { type: 'string', minLength: 5, maxLength: 400 },\n severity: { type: 'string', enum: ['critical', 'major', 'minor', 'info'] },\n },\n },\n },\n },\n}\n\nfunction truncate(body: string, cap: number, label: string): string {\n if (body.length <= cap) return body\n return `${body.slice(0, cap)}\\n… [truncated ${body.length - cap} chars of ${label}]`\n}\n\nfunction buildPrompt(\n input: SemanticConceptJudgeInput,\n opts: { maxPerFileChars: number; maxSourceChars: number; maxHtmlChars: number },\n): string {\n const sourceBlob = input.sourceFiles\n .filter((f) => f.content.length <= opts.maxPerFileChars)\n .map((f) => `--- FILE: ${f.path} ---\\n${f.content}`)\n .join('\\n\\n')\n\n const html = input.servedHtml ?? ''\n\n return `You are a strict code-review judge evaluating whether an agent's 0-to-1 build actually implements the features the user asked for.\n\nYou MUST distinguish:\n (a) WORKING code that implements the concept (rendered UI, wired handler, real API call),\n (b) KEYWORD-PRESENT stub (comments mentioning the concept, variable names, TODOs),\n (c) ABSENT (concept nowhere).\n\nA comment like \"// TODO: add mint button\" is NOT present — score 2-3. Only count a concept as present if there is real functional code: a rendered component, a call handler wired to state or a network call, a computed value actually used.\n\nUSER REQUEST (what the agent was asked to build):\n${input.userRequest}\n\n${input.artifactLabel ? `ARTIFACT METADATA:\\n name: ${input.artifactLabel}\\n description: ${input.artifactDescription ?? ''}\\n\\n` : ''}EXPECTED CONCEPTS (each must be graded independently):\n${input.expectedConcepts\n .map(\n (c, i) =>\n ` ${i + 1}. \"${c.name}\"${c.keywords?.length ? ` — hints: [${c.keywords.slice(0, 6).join(' | ')}]` : ''}`,\n )\n .join('\\n')}\n\n${html ? `SERVED HTML (what the preview returns when hit):\\n${truncate(html, opts.maxHtmlChars, 'HTML')}\\n\\n` : ''}SOURCE FILES (the agent's workdir):\n${truncate(sourceBlob, opts.maxSourceChars, 'source')}\n\nFor EACH concept, return:\n - concept: the concept name as given (match exactly)\n - present: boolean — does a working implementation exist?\n - score: 0-10 — 10 = production-ready; 7 = functional but thin; 4 = partial/stubbed; 2 = keyword-only comment; 0 = absent\n - evidence: cite \"<file>:<line>\" or \"served-html:<selector>\" pointing at the strongest supporting code. If the concept is absent or stubbed, explain what's missing.\n - severity:\n \"info\" when present: true AND score >= 7\n \"minor\" when present: true AND 4 <= score < 7\n \"major\" when present: false OR score < 4\n \"critical\" when the concept is not only absent but a core user flow depends on it\n\nAlso produce a \"summary\" (one sentence, 20-600 chars): overall verdict on whether this is a shippable implementation of the user request vs a keyword-dense placeholder.\n\nBE SKEPTICAL. Keyword matching already passed — your job is to catch what keyword matching misses. If the agent shipped a working build, say so. If it shipped a stub, say so. Don't grade on effort.\n\nReturn STRICT JSON. No prose outside the JSON.`\n}\n\n// ─── Runner ─────────────────────────────────────────────────────────────\n\n/**\n * Run the semantic concept judge. Soft-fails to available=false on\n * LLM/JSON errors — callers in a MultiLayerVerifier pipeline can treat\n * that as \"skip\" rather than \"fail.\"\n */\nexport async function runSemanticConceptJudge(\n input: SemanticConceptJudgeInput,\n options: SemanticConceptJudgeOptions,\n): Promise<SemanticConceptJudgeResult> {\n const start = Date.now()\n const totalCount = input.expectedConcepts.length\n\n if (totalCount === 0) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount: 0,\n findings: [],\n summary: 'no expected concepts declared',\n durationMs: 0,\n costUsd: null,\n available: false,\n error: 'no expected concepts declared',\n }\n }\n\n const opts = {\n chat: options.chat,\n model: options.model ?? options.chat.defaultModel ?? DEFAULT_MODEL,\n timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT,\n maxTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n maxSourceChars: options.maxSourceChars ?? DEFAULT_MAX_SOURCE,\n maxPerFileChars: options.maxPerFileChars ?? DEFAULT_MAX_PER_FILE,\n maxHtmlChars: options.maxHtmlChars ?? DEFAULT_MAX_HTML,\n ...(options.pricing ? { pricing: options.pricing } : {}),\n costLedger: options.costLedger ?? new CostLedger(),\n costPhase: options.costPhase ?? 'judge.semantic-concept',\n costTags: options.costTags ?? {},\n signal: options.signal ?? new AbortController().signal,\n weightConcepts: options.weightConcepts ?? 'mean',\n complexityWeights: { ...DEFAULT_COMPLEXITY_WEIGHTS, ...(options.complexityWeights ?? {}) },\n }\n\n // Build a name → weight map for aggregation. Mean strategy keeps every\n // weight at 1 (uniform average). Complexity strategy reads the table\n // and lets an explicit `weight` override. Explicit strategy uses ONLY\n // the spec's `weight` (defaulting to 1).\n const weightForConcept = (spec: ConceptSpec): number => {\n if (opts.weightConcepts === 'mean') return 1\n if (spec.weight != null) return spec.weight\n if (opts.weightConcepts === 'complexity') {\n return opts.complexityWeights[spec.complexity ?? 'render'] ?? 1\n }\n return 1\n }\n const weightByName = new Map<string, number>(\n input.expectedConcepts.map((c) => [c.name, weightForConcept(c)]),\n )\n\n let receipt: CostReceipt | undefined\n try {\n const request = {\n model: opts.model,\n messages: [\n {\n role: 'system' as const,\n content:\n 'You are a strict code-review judge. Return strict JSON only. No prose outside the JSON. A keyword in a comment is NOT a working implementation.',\n },\n { role: 'user' as const, content: buildPrompt(input, opts) },\n ],\n jsonSchema: { name: 'semantic_concept_judge', schema: SEMANTIC_SCHEMA },\n temperature: 0,\n maxTokens: opts.maxTokens,\n timeoutMs: opts.timeoutMs,\n } satisfies LlmCallRequest\n const paid = await paidJsonChat<{ summary: string; concepts: ConceptFinding[] }>({\n chat: opts.chat,\n request,\n ledger: opts.costLedger,\n channel: 'judge',\n phase: opts.costPhase,\n actor: 'semantic-concept',\n tags: opts.costTags,\n signal: opts.signal,\n ...(opts.pricing ? { pricing: opts.pricing } : {}),\n })\n receipt = paid.receipt\n if (!paid.succeeded) throw paid.error\n const { value } = paid\n\n if (!value?.concepts || !Array.isArray(value.concepts)) {\n throw new Error('judge returned malformed response — expected array under \"concepts\"')\n }\n\n const findings: ConceptFinding[] = value.concepts.map((c) => ({\n concept: String(c.concept),\n present: Boolean(c.present),\n score: Math.max(0, Math.min(10, Number(c.score ?? 0))),\n evidence: String(c.evidence ?? ''),\n severity: (['critical', 'major', 'minor', 'info'] as const).includes(c.severity)\n ? c.severity\n : 'info',\n }))\n\n const presentCount = findings.filter((f) => f.present && f.score >= 7).length\n let weightSum = 0\n let weightedScoreSum = 0\n for (const f of findings) {\n const w = weightByName.get(f.concept) ?? 1\n weightSum += w\n weightedScoreSum += w * f.score\n }\n const scoreAvg =\n weightSum > 0\n ? weightedScoreSum / weightSum\n : findings.reduce((a, f) => a + f.score, 0) / Math.max(1, findings.length)\n\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: Number((scoreAvg / 10).toFixed(3)),\n presentCount,\n totalCount,\n findings,\n summary: String(value.summary ?? ''),\n durationMs: Date.now() - start,\n costUsd: paid.receipt.costUnknown ? null : paid.receipt.costUsd,\n available: true,\n }\n } catch (err) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount,\n findings: [],\n summary: '',\n durationMs: Date.now() - start,\n costUsd: receipt && !receipt.costUnknown ? receipt.costUsd : null,\n available: false,\n error: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\n/**\n * Factory: pin LLM options once, return a closure that accepts inputs.\n * Convenient for pipelines that want to share a single LlmClient config.\n */\nexport function createSemanticConceptJudge(\n options: SemanticConceptJudgeOptions,\n): (input: SemanticConceptJudgeInput) => Promise<SemanticConceptJudgeResult> {\n return (input) => runSemanticConceptJudge(input, options)\n}\n"],"mappings":"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"}
@@ -1,12 +1,13 @@
1
1
  import { a as hashCanonical } from "./canonical-BsZVgBhW.js";
2
2
  import { a as heldoutSignificance, o as pairHoldout } from "./statistical-heldout-GcLIPxk4.js";
3
3
  import { n as contentHash, t as canonicalJson } from "./verdict-cache-BQEsVy9n.js";
4
- import { _ as SearchRecorder, b as surfaceDiff, d as combineComparisonCosts, f as costFromLedgerSummary, j as openSearchLedger, l as optimizationTokenUsageFromSummary, m as readGepaCandidatePopulationArtifact, ot as runCampaign, p as assertGepaCandidatePopulationSummary, t as llmJudge, v as developmentClaim, x as surfaceNode } from "./llm-judge-BnxAv7ed.js";
5
- import { B as surfaceContentHash, x as campaignBreakdown } from "./campaign-evidence-D40-LHIL.js";
4
+ import { Y as runCampaign, _ as SearchRecorder, b as surfaceDiff, d as combineComparisonCosts, f as costFromLedgerSummary, l as optimizationTokenUsageFromSummary, m as readGepaCandidatePopulationArtifact, p as assertGepaCandidatePopulationSummary, t as llmJudge, v as developmentClaim, x as surfaceNode } from "./llm-judge-NJOHM0iA.js";
5
+ import { L as surfaceContentHash, x as campaignBreakdown } from "./campaign-evidence-BqjlTM3P.js";
6
6
  import { t as deepFreezeCanonicalJson } from "./deep-freeze-CurcOQg3.js";
7
- import { S as fsCampaignStorage, _ as removeCredentialEnvironment, b as safePathComponent, d as assertExternalOptimizerModelBudget, f as assertJsonValue, g as isRecord, h as isExternalTextCandidate, i as startExternalOptimizerModelProxy, m as isCandidateText, n as closeExternalOptimizerResources, p as assertNoCredentialValues, r as runWithCleanup, t as runExternalOptimizerProcess, v as resolveExternalOptimizerCallbackLimits, x as createRunCostLedger, y as resolveExternalOptimizerProcessLimits } from "./external-optimizer-subprocess-kbcPu5po.js";
7
+ import { S as fsCampaignStorage, _ as removeCredentialEnvironment, b as safePathComponent, d as assertExternalOptimizerModelBudget, f as assertJsonValue, g as isRecord, h as isExternalTextCandidate, i as startExternalOptimizerModelProxy, m as isCandidateText, n as closeExternalOptimizerResources, p as assertNoCredentialValues, r as runWithCleanup, t as runExternalOptimizerProcess, v as resolveExternalOptimizerCallbackLimits, x as createRunCostLedger, y as resolveExternalOptimizerProcessLimits } from "./external-optimizer-subprocess-B234V9V0.js";
8
+ import { n as openSearchLedger } from "./search-ledger-CQ8Uy4FK.js";
8
9
  import { g as assertServedModelPolicy } from "./llm-client-KYQBh9C8.js";
9
- import { n as acquireSingleRunLock, t as startExternalOptimizerCallback } from "./external-optimizer-process-BnSmrkgL.js";
10
+ import { n as acquireSingleRunLock, t as startExternalOptimizerCallback } from "./external-optimizer-process-xFTliuiK.js";
10
11
  import { createHash, randomBytes } from "node:crypto";
11
12
  import { z } from "zod";
12
13
  import { mkdir } from "node:fs/promises";
@@ -2486,4 +2487,4 @@ function skillOptOptimizationMethod(config) {
2486
2487
  //#endregion
2487
2488
  export { importGepaPopulation as a, composeGate as c, readExternalOptimizerObservationArtifact as d, REFERENCE_EQUIVALENCE_INPUT_LIMITS as f, runReferenceEquivalenceJudge as h, importExternalEvaluations as i, externalTextOptimizationMethod as l, createReferenceEquivalenceJudge as m, gepaOptimizationMethod as n, recordGepaSearch as o, REFERENCE_EQUIVALENCE_JUDGE_VERSION as p, externalSurface as r, heldOutGate as s, skillOptOptimizationMethod as t, decodeExternalTextCandidate as u };
2488
2489
 
2489
- //# sourceMappingURL=skillopt-optimization-method-l2vDBtKp.js.map
2490
+ //# sourceMappingURL=skillopt-optimization-method-DwO3TOAV.js.map