@odla-ai/kg 0.3.0 → 0.4.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.
- package/dist/chunk-T6SYIVST.js +376 -0
- package/dist/chunk-T6SYIVST.js.map +1 -0
- package/dist/index.cjs +410 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +330 -5
- package/dist/index.d.ts +330 -5
- package/dist/index.js +249 -38
- package/dist/index.js.map +1 -1
- package/dist/lenses-C7Q7Gvnx.d.cts +123 -0
- package/dist/lenses-C7Q7Gvnx.d.ts +123 -0
- package/dist/temporal/index.cjs +134 -617
- package/dist/temporal/index.cjs.map +1 -1
- package/dist/temporal/index.d.cts +23 -447
- package/dist/temporal/index.d.ts +23 -447
- package/dist/temporal/index.js +28 -780
- package/dist/temporal/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-V3N2GRYU.js +0 -73
- package/dist/chunk-V3N2GRYU.js.map +0 -1
- package/dist/sources-nJyD0JUP.d.cts +0 -256
- package/dist/sources-nJyD0JUP.d.ts +0 -256
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/ontology.ts","../src/graph/merge.ts","../src/graph/writer.ts","../src/graph/export.ts","../src/graph/resolve.ts","../src/llm/provider.ts","../src/llm/json.ts","../src/util/content.ts","../src/connectors/rss.ts","../src/connectors/url.ts","../src/connectors/gdelt.ts","../src/util/identity.ts","../src/connectors/fred.ts","../src/connectors/edgar.ts","../src/connectors/govdata.ts","../src/connectors/web-search.ts","../src/connectors/registry.ts","../src/sources.ts","../src/state/kv.ts","../src/workflow/helpers.ts","../src/pipeline/run-items.ts"],"sourcesContent":["import type { Value } from \"@odla-ai/db\";\n\n/**\n * The output of a connector: one acquired item. Exactly one of `text` or\n * `fragment` is set — `text` goes through LLM extraction, `fragment` is already\n * ontology-typed and goes straight to the graph writer (structured feeds).\n */\nexport interface RawItem {\n url: string;\n title: string;\n date?: string;\n sourceName: string;\n sourceKind: string; // connector id: \"url\" | \"rss\" | \"edgar\" | ...\n watch?: string | null;\n text?: string;\n fragment?: GraphFragment;\n}\n\n/** A graph node = an odla-db entity in a namespace, keyed by a natural key. */\nexport interface GraphNode {\n type: string; // namespace, e.g. \"company\"\n key: string; // natural-key value (slug, urlHash, ...) — used for Lookup upsert\n attrs: Record<string, Value>; // node properties (the natural-key attr is added by the writer)\n}\n\n/** A graph edge = an odla-db `link` from one node to another by forward label. */\nexport interface GraphEdge {\n from: { type: string; key: string };\n to: { type: string; key: string };\n label: string; // forward link label, e.g. \"mentions\"\n}\n\n/** The single shape the graph writer consumes, whatever produced it. */\nexport interface GraphFragment {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\n/** What an extractor turns a text RawItem into: a fragment plus a prose summary. */\nexport interface ExtractResult {\n fragment: GraphFragment;\n summary: string;\n}\n\n/** Provider-agnostic LLM usage accounting. */\nexport interface Usage {\n calls: number;\n inputTokens: number;\n outputTokens: number;\n}\n\n/** Create zeroed provider-agnostic usage totals for one pipeline run. */\nexport function emptyUsage(): Usage {\n return { calls: 0, inputTokens: 0, outputTokens: 0 };\n}\n\n/** Mutate an accumulator by adding call, input-token, and output-token totals. */\nexport function addUsage(into: Usage, more: Usage): void {\n into.calls += more.calls;\n into.inputTokens += more.inputTokens;\n into.outputTokens += more.outputTokens;\n}\n","import type { SerializedAttr, SerializedEntity, SerializedLink, SerializedSchema, Cardinality } from \"@odla-ai/db\";\nimport type { NaturalKeys } from \"./graph/writer\";\n\n/** How a scalar attribute is reconciled when a node is re-discovered. */\nexport type MergeRule =\n | \"replace\" // last write wins (default)\n | \"prefer\" // first non-empty value sticks (stable facts like a name)\n | \"earliest\" // keep the smaller value (first_seen)\n | \"latest\"; // keep the larger value (last_seen)\n\nexport type AttrType = SerializedAttr[\"type\"]; // \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\"\n\nexport interface AttrSpec {\n type: AttrType;\n unique?: boolean;\n indexed?: boolean;\n optional?: boolean;\n /** The pipeline NEVER writes this attr (hand-edited in a UI); omitted from every update. */\n human?: boolean;\n /** Provenance rule for re-discovery. Default \"replace\". */\n merge?: MergeRule;\n /** Shown to the LLM when this attr appears in an extraction schema. */\n desc?: string;\n}\n\nexport interface EntitySpec {\n /** Unique attr used for Lookup upsert (MERGE key). Must be `unique` in attrs. */\n naturalKey: string;\n attrs: Record<string, AttrSpec>;\n /** FTS fields for fuzzy resolution (P5). */\n searchable?: string[];\n}\n\nexport interface EdgeSpec {\n from: string; // entity type\n to: string; // entity type\n forwardLabel: string; // link label on the `from` side (what the `link` op carries)\n reverseLabel: string;\n forwardHas?: Cardinality; // default \"many\"\n reverseHas?: Cardinality; // default \"many\"\n}\n\nexport interface Ontology {\n entities: Record<string, EntitySpec>;\n edges: Record<string, EdgeSpec>; // keyed by edge name\n}\n\n/** node type → natural-key attr name (for the graph writer's Lookup refs). */\nexport function naturalKeysOf(o: Ontology): NaturalKeys {\n const m: NaturalKeys = {};\n for (const [type, e] of Object.entries(o.entities)) m[type] = e.naturalKey;\n return m;\n}\n\n/** Attr name → merge rule, for every entity type (used by the provenance writer). */\nexport function mergeRulesOf(o: Ontology): Record<string, Record<string, MergeRule>> {\n const out: Record<string, Record<string, MergeRule>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = {};\n for (const [attr, spec] of Object.entries(e.attrs)) out[type][attr] = spec.merge ?? \"replace\";\n }\n return out;\n}\n\n/** Attr names the pipeline must never write, per entity type. */\nexport function humanAttrsOf(o: Ontology): Record<string, Set<string>> {\n const out: Record<string, Set<string>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = new Set(Object.entries(e.attrs).filter(([, a]) => a.human).map(([n]) => n));\n }\n return out;\n}\n\n/** Compile the ontology to an odla-db SerializedSchema to POST at /app/:id/schema. */\nexport function toSerializedSchema(o: Ontology): SerializedSchema {\n const entities: Record<string, SerializedEntity> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n const attrs: Record<string, SerializedAttr> = {};\n for (const [name, a] of Object.entries(e.attrs)) {\n attrs[name] = {\n type: a.type,\n unique: !!a.unique,\n indexed: !!a.indexed,\n optional: !!a.optional,\n };\n }\n entities[type] = { attrs };\n }\n const links: Record<string, SerializedLink> = {};\n for (const [name, ed] of Object.entries(o.edges)) {\n links[name] = {\n forward: { on: ed.from, has: ed.forwardHas ?? \"many\", label: ed.forwardLabel },\n reverse: { on: ed.to, has: ed.reverseHas ?? \"many\", label: ed.reverseLabel },\n };\n }\n return { entities, links };\n}\n","import type { Value } from \"@odla-ai/db\";\nimport type { MergeRule } from \"../ontology\";\n\nfunction isEmpty(v: Value | undefined): boolean {\n return v === undefined || v === null || v === \"\";\n}\n\n/**\n * Reconcile one scalar attribute when a node is re-discovered. Port of agnacl's\n * provenance rules (utils/markdown.ts). Empty incoming never clobbers; empty\n * existing is filled. Ordering rules compare stringwise (ISO dates sort right).\n */\nexport function mergeScalar(\n rule: MergeRule,\n existing: Value | undefined,\n incoming: Value | undefined,\n): Value | undefined {\n if (isEmpty(incoming)) return existing;\n if (isEmpty(existing)) return incoming;\n switch (rule) {\n case \"prefer\":\n return existing; // first non-empty value sticks\n case \"earliest\":\n return String(incoming) < String(existing) ? incoming : existing;\n case \"latest\":\n return String(incoming) > String(existing) ? incoming : existing;\n case \"replace\":\n default:\n return incoming;\n }\n}\n\n/**\n * Merge an incoming attr set into an existing one per the type's rules. `human`\n * attrs are excluded upstream (never in `incoming`); this only reconciles what\n * the pipeline owns. Returns the attrs to write (update leaves unlisted attrs intact).\n */\nexport function mergeAttrs(\n rules: Record<string, MergeRule>,\n existing: Record<string, Value>,\n incoming: Record<string, Value>,\n): Record<string, Value> {\n const out: Record<string, Value> = {};\n for (const [attr, val] of Object.entries(incoming)) {\n const merged = mergeScalar(rules[attr] ?? \"replace\", existing[attr], val);\n if (merged !== undefined) out[attr] = merged;\n }\n return out;\n}\n","import type {\n AdminDb,\n Op,\n EntityRef,\n Value,\n InstaQLQuery,\n QueryResult,\n} from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport {\n naturalKeysOf,\n mergeRulesOf,\n humanAttrsOf,\n type Ontology,\n} from \"../ontology\";\nimport { mergeAttrs } from \"./merge\";\n\n/** node type → unique natural-key attribute name. */\nexport type NaturalKeys = Record<string, string>;\n\n/**\n * Pure fragment → ops mapping (no read/merge). Node upserts by Lookup natural\n * key, then links — nodes always before links so `link` targets resolve.\n * Used by tests and as the building block for the provenance path below.\n */\nexport function fragmentToOps(fragment: GraphFragment, keys: NaturalKeys): Op[] {\n const ref = (type: string, key: string): EntityRef => {\n const attr = keys[type];\n if (!attr) throw new Error(`No natural key configured for node type \"${type}\"`);\n return { ns: type, attr, value: key };\n };\n const ops: Op[] = [];\n for (const n of fragment.nodes) {\n const attr = keys[n.type];\n if (!attr) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n ops.push({ t: \"update\", ns: n.type, id: { ns: n.type, attr, value: n.key }, attrs: { ...n.attrs, [attr]: n.key } });\n }\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n return ops;\n}\n\n/**\n * Provenance-preserving write. Reads existing nodes by natural key (one batched\n * query), merges scalar attrs per the ontology's rules, drops human-owned\n * attrs, then writes merged node updates + all links in one atomic, idempotent\n * transaction. `mutationId` must be deterministic so odla-db can deduplicate\n * the write on retry; this does not make preceding reads or external effects\n * exactly-once.\n */\nexport async function writeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n mutationId: string,\n): Promise<{ txId: number; ops: number }> {\n const keys = naturalKeysOf(ontology);\n const rules = mergeRulesOf(ontology);\n const humans = humanAttrsOf(ontology);\n\n // 1. Collapse duplicate nodes within the fragment (same type+key), dropping human attrs.\n const byType = new Map<string, Map<string, Record<string, Value>>>();\n for (const n of fragment.nodes) {\n if (!keys[n.type]) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n const human = humans[n.type] ?? new Set<string>();\n const clean: Record<string, Value> = {};\n for (const [k, v] of Object.entries(n.attrs)) if (!human.has(k)) clean[k] = v;\n const typeMap = byType.get(n.type) ?? new Map<string, Record<string, Value>>();\n const prev = typeMap.get(n.key);\n typeMap.set(n.key, prev ? mergeAttrs(rules[n.type]!, prev, clean) : clean);\n byType.set(n.type, typeMap);\n }\n\n // 2. Batch-read existing rows per type by natural key (one query, filtered per level).\n const query: InstaQLQuery = {};\n for (const [type, m] of byType) {\n query[type] = { $: { where: { [keys[type]!]: { $in: [...m.keys()] } } } };\n }\n const existing: QueryResult = Object.keys(query).length ? await db.query(query) : {};\n const priorOf = (type: string, key: string): Record<string, Value> => {\n const attr = keys[type]!;\n for (const row of existing[type] ?? []) {\n if (row[attr] === key) return row as unknown as Record<string, Value>;\n }\n return {};\n };\n\n // 3. Merged node updates first, then links.\n const ops: Op[] = [];\n for (const [type, m] of byType) {\n const attr = keys[type]!;\n for (const [key, incoming] of m) {\n const merged = mergeAttrs(rules[type]!, priorOf(type, key), incoming);\n ops.push({ t: \"update\", ns: type, id: { ns: type, attr, value: key }, attrs: { ...merged, [attr]: key } });\n }\n }\n const ref = (type: string, key: string): EntityRef => ({ ns: type, attr: keys[type]!, value: key });\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n\n if (ops.length === 0) return { txId: 0, ops: 0 };\n const { txId } = await db.transact(ops, { mutationId });\n return { txId, ops: ops.length };\n}\n","// Read a GraphFragment back OUT of odla-db — the inverse of the writer, and\n// the bridge from the live graph to the pure @odla-ai/kg/temporal analytics.\n// Reads are per-type offset walks with link labels as nested InstaQL keys\n// (odla-db has no cross-relation where; filter per level). Reverse labels are\n// normalized to FORWARD edges so the fragment satisfies resolvePath's\n// forward-only traversal.\nimport type { AdminDb } from \"@odla-ai/db\";\nimport type { GraphFragment, GraphNode, GraphEdge } from \"../types\";\nimport type { Ontology } from \"../ontology\";\nimport { naturalKeysOf } from \"../ontology\";\nimport { parseDateMs } from \"../temporal/time\";\n\n/** One entity type to export. */\nexport interface ExportEntitySpec {\n /** Link labels (forward or reverse, as queried in InstaQL) to pull as nested rows. */\n links?: readonly string[];\n /** Attr allowlist kept on nodes. Default: every scalar attr except `id` and links. */\n attrs?: readonly string[];\n /**\n * Date window. Filtering is client-side by default; set `indexed: true`\n * (only when the attr really is indexed) to also push a\n * `where { [dateAttr]: { $gte: from } }` to the server — odla-db silently\n * returns zero rows for `where` on unindexed attrs.\n */\n window?: { dateAttr: string; from: string; indexed?: boolean };\n /** Row cap for this type across the offset walk. Default 1000. */\n limit?: number;\n}\n\n/** What {@link exportFragment} should read. */\nexport interface ExportSpec {\n entities: Record<string, ExportEntitySpec>;\n /** Rows per page in the offset walk (1–1000). Default 200. */\n pageSize?: number;\n}\n\ninterface LinkPlan {\n label: string;\n counterpart: string;\n /** Forward: root --forwardLabel--> nested. Reverse: nested --forwardLabel--> root. */\n direction: \"forward\" | \"reverse\";\n forwardLabel: string;\n}\n\nfunction planLinks(ontology: Ontology, type: string, labels: readonly string[]): LinkPlan[] {\n return labels.map((label) => {\n for (const edge of Object.values(ontology.edges)) {\n if (edge.forwardLabel === label && edge.from === type) {\n return { label, counterpart: edge.to, direction: \"forward\" as const, forwardLabel: edge.forwardLabel };\n }\n if (edge.reverseLabel === label && edge.to === type) {\n return { label, counterpart: edge.from, direction: \"reverse\" as const, forwardLabel: edge.forwardLabel };\n }\n }\n throw new Error(`exportFragment: no ontology edge matches link \"${label}\" on \"${type}\"`);\n });\n}\n\nconst isScalar = (v: unknown) => v === null || (typeof v !== \"object\" && typeof v !== \"function\");\n\nfunction pickAttrs(row: Record<string, unknown>, spec: ExportEntitySpec, links: readonly string[]) {\n const out: GraphNode[\"attrs\"] = {};\n for (const [k, v] of Object.entries(row)) {\n if (spec.attrs ? !spec.attrs.includes(k) : k === \"id\" || links.includes(k) || !isScalar(v)) continue;\n if (isScalar(v) && v !== null) out[k] = v as GraphNode[\"attrs\"][string];\n }\n return out;\n}\n\nconst RETRYABLE_WITHOUT_WHERE = /query_scan_limit|query_too_complex/;\n\n/**\n * Export (a window of) the graph as a `GraphFragment` — nodes and forward\n * edges — ready for `extractEvents` and the rest of `@odla-ai/kg/temporal`.\n *\n * Each entity type is read with a stable offset walk (ordered by its natural\n * key) carrying its requested link labels as nested relations. Date windows\n * filter client-side; for attrs declared `indexed` the filter also runs\n * server-side, and if that indexed filter ever exceeds the scan budget\n * (`query_scan_limit`) the walk restarts without `where`.\n * Nodes dedupe by `type:key` keeping the richer attr set; output ordering is\n * deterministic (nodes by type/key, edges by from/label/to).\n */\nexport async function exportFragment(db: AdminDb, ontology: Ontology, spec: ExportSpec): Promise<GraphFragment> {\n const keys = naturalKeysOf(ontology);\n const pageSize = Math.min(1000, Math.max(1, spec.pageSize ?? 200));\n const nodes = new Map<string, GraphNode>();\n const edges = new Map<string, GraphEdge>();\n\n const putNode = (node: GraphNode) => {\n const id = `${node.type}:${node.key}`;\n const existing = nodes.get(id);\n if (!existing || Object.keys(node.attrs).length > Object.keys(existing.attrs).length) nodes.set(id, node);\n };\n\n for (const [type, entity] of Object.entries(spec.entities)) {\n const naturalKey = keys[type];\n if (!naturalKey) throw new Error(`exportFragment: no ontology entity \"${type}\"`);\n const links = planLinks(ontology, type, entity.links ?? []);\n const limit = entity.limit ?? 1000;\n const fromMs = entity.window ? parseDateMs(entity.window.from) : null;\n let useWhere = entity.window?.indexed === true;\n let offset = 0;\n let taken = 0;\n\n while (taken < limit) {\n const opts: Record<string, unknown> = {\n limit: Math.min(pageSize, limit - taken),\n offset,\n order: { [naturalKey]: \"asc\" },\n };\n if (useWhere && entity.window) opts.where = { [entity.window.dateAttr]: { $gte: entity.window.from } };\n const query = {\n [type]: { $: opts, ...Object.fromEntries(links.map((l) => [l.label, {}])) },\n };\n\n let rows: Array<Record<string, unknown>>;\n try {\n rows = ((await db.query(query)) as Record<string, unknown[]>)[type] as Array<Record<string, unknown>> ?? [];\n } catch (err) {\n if (useWhere && RETRYABLE_WITHOUT_WHERE.test(String((err as Error).message ?? err))) {\n // Unindexed attr blew the scan budget — restart this type's walk\n // without `where` (offsets aren't comparable across filters; the\n // dedupe maps make re-reads harmless) and filter client-side.\n useWhere = false;\n offset = 0;\n taken = 0;\n continue;\n }\n throw err;\n }\n\n for (const row of rows) {\n const key = row[naturalKey];\n if (key === undefined || key === null) continue; // defensive: unkeyed row\n if (entity.window && fromMs !== null) {\n const t = parseDateMs(row[entity.window.dateAttr]);\n if (t !== null && t < fromMs) continue;\n }\n const rootRef = { type, key: String(key) };\n putNode({ ...rootRef, attrs: pickAttrs(row, entity, entity.links ?? []) });\n\n for (const plan of links) {\n const nestedKeyAttr = keys[plan.counterpart];\n if (!nestedKeyAttr) throw new Error(`exportFragment: no ontology entity \"${plan.counterpart}\"`);\n // has-many relations come back as arrays, has-one as a bare object.\n const raw = row[plan.label];\n const nestedRows = Array.isArray(raw) ? raw : raw ? [raw] : [];\n for (const nested of nestedRows as Array<Record<string, unknown>>) {\n const nestedKey = nested[nestedKeyAttr];\n if (nestedKey === undefined || nestedKey === null) continue;\n const nestedRef = { type: plan.counterpart, key: String(nestedKey) };\n putNode({ ...nestedRef, attrs: pickAttrs(nested, {}, []) });\n const edge: GraphEdge =\n plan.direction === \"forward\"\n ? { from: rootRef, to: nestedRef, label: plan.forwardLabel }\n : { from: nestedRef, to: rootRef, label: plan.forwardLabel };\n edges.set(`${edge.from.type}:${edge.from.key}|${edge.label}|${edge.to.type}:${edge.to.key}`, edge);\n }\n }\n taken++;\n if (taken >= limit) break;\n }\n\n if (rows.length < pageSize) break;\n offset += rows.length;\n }\n }\n\n const byRef = (a: { type: string; key: string }, b: { type: string; key: string }) =>\n a.type < b.type ? -1 : a.type > b.type ? 1 : a.key < b.key ? -1 : a.key > b.key ? 1 : 0;\n return {\n nodes: [...nodes.values()].sort(byRef),\n edges: [...edges.values()].sort(\n (a, b) => byRef(a.from, b.from) || (a.label < b.label ? -1 : a.label > b.label ? 1 : 0) || byRef(a.to, b.to),\n ),\n };\n}\n","import type { AdminDb, Entity } from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport type { Ontology } from \"../ontology\";\n\n/** FTS5 search over a namespace's text fields (odla-db QueryOpts.search). */\nexport async function searchEntities(db: AdminDb, type: string, q: string, limit = 5): Promise<Entity[]> {\n if (!q.trim()) return [];\n const res = await db.query({ [type]: { $: { search: q, limit } } });\n return (res[type] ?? []) as Entity[];\n}\n\nconst norm = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]+/g, \"\");\n\n/**\n * Resolve a name to an existing entity's natural key via FTS — for merging a\n * re-discovery that slugified differently. Conservative: only returns a hit when\n * the top match's name/key normalizes identically to the query.\n */\nexport async function resolveName(\n db: AdminDb,\n type: string,\n keyAttr: string,\n name: string,\n): Promise<string | null> {\n const target = norm(name);\n if (!target) return null;\n for (const h of await searchEntities(db, type, name, 3)) {\n const key = h[keyAttr];\n const hname = h[\"name\"];\n if (typeof key !== \"string\") continue;\n if ((typeof hname === \"string\" && norm(hname) === target) || norm(key) === target) return key;\n }\n return null;\n}\n\n/**\n * Canonicalize a fragment's searchable nodes against existing entities via FTS,\n * remapping node keys (and the edges that reference them) to the canonical key.\n * Used on the lower-volume discovery/enrich paths, not high-volume ingest.\n */\nexport async function canonicalizeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n): Promise<GraphFragment> {\n const remap = new Map<string, string>(); // `${type}:${key}` -> canonical key\n for (const n of fragment.nodes) {\n const spec = ontology.entities[n.type];\n if (!spec?.searchable?.length) continue;\n const name = typeof n.attrs.name === \"string\" ? n.attrs.name : \"\";\n if (!name) continue;\n const canon = await resolveName(db, n.type, spec.naturalKey, name);\n if (canon && canon !== n.key) remap.set(`${n.type}:${n.key}`, canon);\n }\n if (remap.size === 0) return fragment;\n\n const rk = (type: string, key: string): string => remap.get(`${type}:${key}`) ?? key;\n return {\n nodes: fragment.nodes.map((n) => ({ ...n, key: rk(n.type, n.key) })),\n edges: fragment.edges.map((e) => ({\n from: { type: e.from.type, key: rk(e.from.type, e.from.key) },\n to: { type: e.to.type, key: rk(e.to.type, e.to.key) },\n label: e.label,\n })),\n };\n}\n","import { init, extractText, type Ai } from \"@odla-ai/ai\";\nimport type { Usage } from \"../types\";\n\n/**\n * The LLM seam, now a thin adapter over @odla-ai/ai (which was itself\n * generalized from this package's original Provider). Three verbs stay:\n * extract (forced structured tool call), search (web-search-enabled text),\n * write (plain generation). Callers never see vendor or oracle types.\n */\n\nexport type ProviderFamily = \"claude\" | \"openai\";\n\n/** Role → model tiers per family — config as data. `cheap` is for gate-style\n * calls, `extract` for structured extraction (and prose writing), `search`\n * for web-search-enabled calls. */\nexport const MODEL_TIERS: Record<ProviderFamily, { cheap: string; extract: string; search: string }> = {\n claude: { cheap: \"claude-haiku-4-5\", extract: \"claude-sonnet-5\", search: \"claude-sonnet-5\" },\n openai: { cheap: \"gpt-5-nano\", extract: \"gpt-5-mini\", search: \"gpt-5-search-api\" },\n};\n\n/** A forced structured tool call: schema in, validated object out. */\nexport interface ExtractCall {\n system: string;\n user: string;\n tool: { name: string; description?: string; parameters: Record<string, unknown> };\n model?: string;\n maxTokens?: number;\n}\n\n/** A web-search-enabled call: the model searches and returns text (caller tolerant-parses). */\nexport interface SearchCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\n/** A plain text-generation call (no tools, no search) — e.g. digest prose. */\nexport interface WriteCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\nexport interface Provider {\n id: ProviderFamily;\n /** The underlying @odla-ai/ai client — escape hatch for advanced use. */\n ai: Ai;\n extract<T = unknown>(c: ExtractCall): Promise<{ value: T; usage: Usage }>;\n search(c: SearchCall): Promise<{ text: string; usage: Usage }>;\n write(c: WriteCall): Promise<{ text: string; usage: Usage }>;\n}\n\nexport interface LlmConfig {\n provider: ProviderFamily;\n anthropicKey?: string;\n openaiKey?: string;\n /** Inject a pre-built Ai (tests, custom catalog); keys are ignored when set. */\n ai?: Ai;\n}\n\nfunction toUsage(u: { inputTokens: number; outputTokens: number }): Usage {\n return { calls: 1, inputTokens: u.inputTokens, outputTokens: u.outputTokens };\n}\n\n/** The library primitive: an explicit config in, the three-verb seam out. */\nexport function createProvider(cfg: LlmConfig): Provider {\n const tiers = MODEL_TIERS[cfg.provider];\n const ai = cfg.ai ?? init({ keys: { anthropic: cfg.anthropicKey, openai: cfg.openaiKey } });\n return {\n id: cfg.provider,\n ai,\n async extract<T = unknown>(c: ExtractCall) {\n const { value, usage } = await ai.extract<T>({\n model: c.model ?? tiers.extract,\n system: c.system,\n user: c.user,\n tool: c.tool,\n maxTokens: c.maxTokens,\n });\n return { value, usage: toUsage(usage) };\n },\n async search(c: SearchCall) {\n const { text, usage } = await ai.search({\n model: c.model ?? tiers.search,\n system: c.system,\n user: c.user,\n maxTokens: c.maxTokens,\n });\n return { text, usage: toUsage(usage) };\n },\n async write(c: WriteCall) {\n const res = await ai.chat({\n model: c.model ?? tiers.extract,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n maxTokens: c.maxTokens ?? 2048,\n });\n return { text: extractText(res.content), usage: toUsage(res.usage) };\n },\n };\n}\n\n/** The env vars the provider factory reads — a structural slice, not the\n * worker Env, so any host (worker, script, test) can supply plain strings. */\nexport interface LlmEnv {\n LLM_PROVIDER?: string; // \"claude\" | \"openai\"\n ANTHROPIC_API_KEY?: string;\n OPENAI_API_KEY?: string;\n}\n\n/** Build the configured provider from host env, failing before use when its key is missing. */\nexport function getProvider(env: LlmEnv): Provider {\n const which: ProviderFamily = (env.LLM_PROVIDER || \"claude\").toLowerCase() === \"openai\" ? \"openai\" : \"claude\";\n if (which === \"openai\" && !env.OPENAI_API_KEY) throw new Error(\"LLM_PROVIDER=openai but OPENAI_API_KEY is unset\");\n if (which === \"claude\" && !env.ANTHROPIC_API_KEY) throw new Error(\"LLM_PROVIDER=claude but ANTHROPIC_API_KEY is unset\");\n return createProvider({ provider: which, anthropicKey: env.ANTHROPIC_API_KEY, openaiKey: env.OPENAI_API_KEY });\n}\n","/**\n * Tolerant JSON extraction from LLM prose — web-search models return JSON inside\n * explanatory text. Ported from agnacl services/enrichment.ts.\n */\n\nexport function extractJson<T = unknown>(text: string): T | null {\n const m = text.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]) as T;\n } catch {\n return null;\n }\n}\n\n/** Tolerantly parse the first JSON-array-shaped span in model prose, or return an empty array. */\nexport function extractJsonArray<T = unknown>(text: string): T[] {\n const m = text.match(/\\[[\\s\\S]*\\]/);\n if (!m) return [];\n try {\n const v = JSON.parse(m[0]);\n return Array.isArray(v) ? (v as T[]) : [];\n } catch {\n return [];\n }\n}\n","/**\n * Dependency-free HTML → text extraction (Workers-isolate friendly: no DOM/jsdom).\n * Ported from agnacl services/content-extractor.ts.\n */\n\nconst MAX_CHARS = 8000;\n\n/** Strip scripts/styles/tags and collapse whitespace; cap length. */\nexport function htmlToText(html: string, maxChars = MAX_CHARS): string {\n let t = html;\n t = t.replace(/<script[\\s\\S]*?<\\/script>/gi, \" \");\n t = t.replace(/<style[\\s\\S]*?<\\/style>/gi, \" \");\n t = t.replace(/<!--[\\s\\S]*?-->/g, \" \");\n t = t.replace(/<\\/(p|div|section|article|li|h[1-6]|br)>/gi, \"\\n\");\n t = t.replace(/<[^>]+>/g, \" \");\n t = decodeEntities(t);\n t = t.replace(/[ \\t\\f\\r]+/g, \" \").replace(/\\n\\s*\\n\\s*/g, \"\\n\\n\").trim();\n return t.slice(0, maxChars);\n}\n\nfunction decodeEntities(s: string): string {\n return s\n .replace(/ /gi, \" \")\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\")\n .replace(/&#(\\d+);/g, (_, d) => String.fromCharCode(Number(d)))\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => String.fromCharCode(parseInt(h, 16)));\n}\n\n/** Fetch a URL and return readable text. Best-effort: returns \"\" on any failure. */\nexport async function fetchText(url: string, maxChars = MAX_CHARS): Promise<string> {\n try {\n const res = await fetch(url, {\n headers: {\n \"user-agent\": \"odla-kg/0.1 (+https://github.com/cory/odla-kg)\",\n accept: \"text/html,application/xhtml+xml\",\n },\n });\n if (!res.ok) return \"\";\n const ct = res.headers.get(\"content-type\") || \"\";\n const body = await res.text();\n if (ct.includes(\"html\") || /<\\w+[\\s>]/.test(body.slice(0, 200))) {\n return htmlToText(body, maxChars);\n }\n return body.slice(0, maxChars);\n } catch {\n return \"\";\n }\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { htmlToText } from \"../util/content\";\n\n/** Dependency-free RSS/Atom parsing, ported from agnacl services/rss.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\n\nfunction extractTag(xml: string, tag: string): string {\n const cdata = xml.match(new RegExp(`<${tag}[^>]*>\\\\s*<!\\\\[CDATA\\\\[([\\\\s\\\\S]*?)\\\\]\\\\]>\\\\s*</${tag}>`, \"i\"));\n if (cdata) return cdata[1]!.trim();\n const m = xml.match(new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction extractAttr(xml: string, tag: string, attr: string): string {\n const m = xml.match(new RegExp(`<${tag}[^>]*\\\\s${attr}=[\"']([^\"']*)[\"']`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction splitItems(xml: string): string[] {\n return xml.match(/<item[\\s>][\\s\\S]*?<\\/item>/gi) || xml.match(/<entry[\\s>][\\s\\S]*?<\\/entry>/gi) || [];\n}\n\ninterface RssCfg {\n url: string;\n name: string;\n watch?: string | null;\n}\n\nexport const rssConnector: SourceConnector<RssCfg> = {\n id: \"rss\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): RssCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"rss connector requires a `url`\");\n return { url: r.url, name: String(r.name ?? r.url), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n let xml: string;\n try {\n const res = await fetch(cfg.url, { headers: { \"User-Agent\": UA } });\n if (!res.ok) {\n if (res.status === 429 || res.status === 403 || res.status >= 500) return [];\n throw new Error(`feed ${cfg.url} → ${res.status}`);\n }\n xml = await res.text();\n } catch (e) {\n console.log(`rss fetch failed (non-blocking): ${cfg.url}: ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n for (const raw of splitItems(xml)) {\n const title = extractTag(raw, \"title\");\n let url = extractTag(raw, \"link\") || extractAttr(raw, \"link\", \"href\");\n if (!title || !url) continue;\n url = url.trim();\n const date =\n extractTag(raw, \"pubDate\") || extractTag(raw, \"published\") || extractTag(raw, \"updated\") || undefined;\n const content =\n extractTag(raw, \"content:encoded\") ||\n extractTag(raw, \"content\") ||\n extractTag(raw, \"description\") ||\n extractTag(raw, \"summary\") ||\n \"\";\n let text = htmlToText(content);\n // Thin feed body → fetch the article page.\n if (text.length < 200) {\n const page = await ctx.fetchText(url);\n if (page.length > text.length) text = page;\n }\n out.push({ url, title, date, sourceName: cfg.name, sourceKind: \"rss\", watch: cfg.watch, text });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector } from \"./types\";\nimport { fetchText } from \"../util/content\";\n\n/** Fetch one URL → a text-bearing RawItem. Used both directly (/trigger) and as a connector. */\nexport async function fetchUrl(url: string): Promise<RawItem> {\n const text = await fetchText(url);\n const firstLine = text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\";\n let host = url;\n try {\n host = new URL(url).hostname;\n } catch {\n /* keep raw url as the source label */\n }\n return { url, title: (firstLine || url).slice(0, 200).trim(), sourceName: host, sourceKind: \"url\", text };\n}\n\ninterface UrlCfg {\n url: string;\n watch?: string | null;\n}\n\nexport const urlConnector: SourceConnector<UrlCfg> = {\n id: \"url\",\n capabilities: { fetch: true, structured: false },\n parseConfig(raw): UrlCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"url connector requires a `url`\");\n return { url: r.url, watch: (r.watch as string | null) ?? null };\n },\n async fetch(cfg): Promise<RawItem[]> {\n const item = await fetchUrl(cfg.url);\n if (cfg.watch !== undefined) item.watch = cfg.watch;\n return [item];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/** GDELT DOC 2.0 standing-query connector (text). Ported from agnacl services/gdelt.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\nconst MAX_ITEMS = 15;\n\ninterface GdeltCfg {\n query: string;\n name: string;\n watch?: string | null;\n}\n\nfunction isoFromSeendate(s: string): string | undefined {\n const m = s.match(/^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})Z?$/);\n return m ? `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}Z` : undefined;\n}\n\nexport const gdeltConnector: SourceConnector<GdeltCfg> = {\n id: \"gdelt\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): GdeltCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.query !== \"string\" || !r.query) throw new Error(\"gdelt connector requires a `query`\");\n return { query: r.query, name: String(r.name ?? `GDELT: ${r.query}`), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const api =\n `https://api.gdeltproject.org/api/v2/doc/doc?query=${encodeURIComponent(cfg.query)}` +\n `&mode=artlist&format=json&maxrecords=40×pan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n // GDELT is a best-effort external source; without a deadline a slow or wedged endpoint hangs\n // the whole workflow run. Bound it and let the catch below treat a timeout like any other miss.\n const res = await fetch(api, { headers: { \"User-Agent\": UA }, signal: AbortSignal.timeout(15_000) });\n if (!res.ok) return [];\n const text = await res.text(); // GDELT sometimes returns text errors with 200\n try {\n articles = (JSON.parse(text) as { articles?: typeof articles }).articles ?? [];\n } catch {\n return [];\n }\n } catch (e) {\n console.log(`gdelt fetch failed (non-blocking): ${e}`);\n return [];\n }\n\n const seen = new Set<string>();\n const picked = articles\n .filter((a) => a?.url && a?.title && a.language === \"English\" && !seen.has(a.url!) && (seen.add(a.url!), true))\n .slice(0, MAX_ITEMS);\n\n const out: RawItem[] = [];\n for (const a of picked) {\n const body = await ctx.fetchText(a.url!); // GDELT gives no body; fetch the article\n if (body.length < 200) continue;\n out.push({\n url: a.url!,\n title: a.title!,\n date: isoFromSeendate(a.seendate ?? \"\"),\n sourceName: cfg.name,\n sourceKind: \"gdelt\",\n watch: cfg.watch,\n text: body,\n });\n }\n return out;\n },\n};\n","/**\n * Identity + dedup helpers. The slug / urlHash a node resolves to IS its\n * natural key for Lookup-based upserts, so these must be stable and pure.\n * Ported from agnacl (utils/markdown.ts slugify, services/kv.ts normalizeUrl).\n */\n\n/** NFKD-normalize, strip accents, lowercase, non-alphanumerics → \"-\", cap 60. */\nexport function slugify(input: string): string {\n return input\n .normalize(\"NFKD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60)\n .replace(/-+$/g, \"\");\n}\n\n/** Drop tracking params, hash, and trailing slash so the same page maps to one key. */\nexport function normalizeUrl(raw: string): string {\n try {\n const u = new URL(raw);\n u.hash = \"\";\n const drop = [/^utm_/i, /^ref$/i, /^ref_/i, /^fbclid$/i, /^gclid$/i, /^mc_/i];\n for (const key of [...u.searchParams.keys()]) {\n if (drop.some((re) => re.test(key))) u.searchParams.delete(key);\n }\n let s = u.toString();\n s = s.replace(/\\/$/, \"\");\n return s;\n } catch {\n return raw.trim().replace(/#.*$/, \"\").replace(/\\/$/, \"\");\n }\n}\n\n/** Stable short key for a URL (FNV-1a 32-bit, base36). Used as the `signal` natural key. */\nexport function urlHash(raw: string): string {\n const s = normalizeUrl(raw);\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(36);\n}\n\n/**\n * Reject descriptive phrases masquerading as people (\"three craftsmen\",\n * \"the founders\"). A real name is 2-4 capitalized tokens, no digits.\n * Ported from agnacl utils/names.ts (isLikelyPersonName).\n */\nexport function isLikelyPersonName(name: string): boolean {\n const n = name.trim();\n if (!n || /\\d/.test(n)) return false;\n const parts = n.split(/\\s+/);\n if (parts.length < 2 || parts.length > 4) return false;\n return parts.every((p) => /^[A-Z][a-zA-Z'’.-]*$/.test(p));\n}\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify } from \"../util/identity\";\n\n/**\n * FRED economic indicators — a STRUCTURED connector: it maps series directly to\n * graph nodes (indicator + region) and emits RawItem.fragment, so the ingest\n * workflow writes it without any LLM extraction. Ported from agnacl services/fred.ts.\n */\ninterface FredCfg {\n series: Array<{ id: string; label: string; unit?: string }>;\n region?: string;\n regionName?: string;\n}\n\nasync function fetchSeries(\n seriesId: string,\n apiKey: string,\n start: string,\n): Promise<Array<{ date: string; value: number }>> {\n const url =\n `https://api.stlouisfed.org/fred/series/observations?series_id=${encodeURIComponent(seriesId)}` +\n `&api_key=${encodeURIComponent(apiKey)}&file_type=json&observation_start=${encodeURIComponent(start)}`;\n try {\n const res = await fetch(url);\n if (!res.ok) return [];\n const data = (await res.json()) as { observations?: Array<{ date?: string; value?: string }> };\n const out: Array<{ date: string; value: number }> = [];\n for (const o of data.observations ?? []) {\n if (!o?.date || o.value === undefined || o.value === \".\") continue;\n const v = Number(o.value);\n if (Number.isFinite(v)) out.push({ date: o.date, value: v });\n }\n return out;\n } catch (e) {\n console.log(`fred fetch failed for ${seriesId} (non-blocking): ${e}`);\n return [];\n }\n}\n\nexport const fredConnector: SourceConnector<FredCfg> = {\n id: \"fred\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): FredCfg {\n const r = raw as Record<string, unknown>;\n const series = Array.isArray(r.series) ? (r.series as FredCfg[\"series\"]) : [];\n if (!series.length) throw new Error(\"fred connector requires a non-empty `series`\");\n return { series, region: r.region as string | undefined, regionName: r.regionName as string | undefined };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const apiKey = ctx.secret(\"FRED_API_KEY\");\n if (!apiKey) {\n console.log(\"fred: FRED_API_KEY unset — skipping\");\n return [];\n }\n const start = new Date(ctx.now.getTime() - 400 * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const out: RawItem[] = [];\n\n for (const s of cfg.series) {\n const obs = await fetchSeries(s.id, apiKey, start);\n if (!obs.length) continue;\n const latest = obs[obs.length - 1]!;\n const prior = obs.length > 1 ? obs[obs.length - 2] : undefined;\n\n const attrs: Record<string, Value> = { label: s.label, latest: String(latest.value), as_of: latest.date };\n if (prior) attrs.prior = String(prior.value);\n if (s.unit) attrs.unit = s.unit;\n\n const fragment: GraphFragment = { nodes: [{ type: \"indicator\", key: s.id, attrs }], edges: [] };\n if (cfg.region) {\n const rslug = slugify(cfg.region);\n if (rslug) {\n fragment.nodes.push({ type: \"region\", key: rslug, attrs: { name: cfg.regionName ?? cfg.region } });\n fragment.edges.push({ from: { type: \"region\", key: rslug }, to: { type: \"indicator\", key: s.id }, label: \"has_indicator\" });\n }\n }\n\n out.push({\n url: `https://fred.stlouisfed.org/series/${s.id}#${latest.date}`,\n title: `${s.label}: ${latest.value} (${latest.date})`,\n date: latest.date,\n sourceName: \"FRED\",\n sourceKind: \"fred\",\n fragment,\n });\n }\n return out;\n },\n};\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify, isLikelyPersonName } from \"../util/identity\";\n\n/**\n * SEC EDGAR Form D — a STRUCTURED connector (RawItem.fragment, no LLM). A lean\n * port of agnacl services/edgar.ts: first page of Form D filings for the given\n * states, each mapped to company + funding_round (+ people, region). SEC\n * requires an identifying User-Agent (env.EDGAR_USER_AGENT) on every request.\n */\nconst SEARCH_BASE = \"https://efts.sec.gov/LATEST/search-index\";\nconst MAX_FILINGS = 10;\n\ninterface EdgarCfg {\n locationCodes: string[];\n cityFilter?: string[];\n days?: number;\n}\n\nconst cikTrim = (cik: string): string => cik.replace(/^0+/, \"\") || cik;\nconst indexUrl = (cik: string, adsh: string): string =>\n `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/${adsh}-index.htm`;\n\nfunction tagText(scope: string, tag: string): string {\n const m = scope.match(new RegExp(`<${tag}[^>]*>\\\\s*([\\\\s\\\\S]*?)\\\\s*</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\ninterface EdgarHit {\n _source?: {\n adsh?: string;\n ciks?: string[];\n display_names?: string[];\n file_date?: string;\n biz_states?: string[];\n biz_locations?: string[];\n };\n}\n\nexport const edgarConnector: SourceConnector<EdgarCfg> = {\n id: \"edgar\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): EdgarCfg {\n const r = raw as Record<string, unknown>;\n const codes = Array.isArray(r.locationCodes) ? (r.locationCodes as string[]) : [];\n if (!codes.length) throw new Error('edgar connector requires `locationCodes` (e.g. [\"UT\"])');\n return {\n locationCodes: codes,\n cityFilter: Array.isArray(r.cityFilter) ? (r.cityFilter as string[]) : undefined,\n days: typeof r.days === \"number\" ? r.days : 7,\n };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const ua = ctx.secret(\"EDGAR_USER_AGENT\");\n if (!ua) {\n console.log(\"edgar: EDGAR_USER_AGENT unset — skipping\");\n return [];\n }\n const enddt = ctx.now.toISOString().slice(0, 10);\n const startdt = new Date(ctx.now.getTime() - (cfg.days ?? 7) * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const searchUrl =\n `${SEARCH_BASE}?q=&forms=D&locationCodes=${encodeURIComponent(cfg.locationCodes.join(\",\"))}` +\n `&startdt=${startdt}&enddt=${enddt}`;\n\n let hits: EdgarHit[] = [];\n try {\n const res = await fetch(searchUrl, { headers: { \"User-Agent\": ua } });\n if (!res.ok) return [];\n hits = ((await res.json()) as { hits?: { hits?: EdgarHit[] } }).hits?.hits ?? [];\n } catch (e) {\n console.log(`edgar search failed (non-blocking): ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n const seen = new Set<string>();\n for (const hit of hits.slice(0, MAX_FILINGS)) {\n const src = hit._source ?? {};\n const adsh = src.adsh ?? \"\";\n if (!adsh || seen.has(adsh)) continue;\n seen.add(adsh);\n const cik = src.ciks?.[0] ?? \"\";\n const name = (src.display_names?.[0] ?? \"\").replace(/\\s*\\(CIK\\s*\\d+\\)\\s*$/i, \"\").trim();\n if (!name || !cik) continue;\n\n const loc = src.biz_locations?.[0] ?? \"\";\n let city = loc.trim();\n let state = src.biz_states?.[0] ?? \"\";\n const comma = loc.lastIndexOf(\",\");\n if (comma >= 0) {\n city = loc.slice(0, comma).trim();\n state = loc.slice(comma + 1).trim() || state;\n }\n if (cfg.cityFilter?.length && !cfg.cityFilter.some((c) => c.toLowerCase() === city.toLowerCase())) continue;\n\n const url = indexUrl(cik, adsh);\n const fileDate = src.file_date ?? \"\";\n\n // Filing detail (offering amount, industry, related persons)\n let industry = \"\";\n let offering = \"\";\n const persons: Array<{ name: string; roles: string[] }> = [];\n try {\n const detailUrl = `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/primary_doc.xml`;\n const dres = await fetch(detailUrl, { headers: { \"User-Agent\": ua } });\n if (dres.ok) {\n const xml = await dres.text();\n industry = tagText(xml, \"industryGroupType\");\n offering = tagText(xml, \"totalOfferingAmount\");\n for (const block of xml.match(/<relatedPersonInfo>[\\s\\S]*?<\\/relatedPersonInfo>/gi) ?? []) {\n const pname = [tagText(block, \"firstName\"), tagText(block, \"lastName\")]\n .filter((p) => p && p !== \"-\")\n .join(\" \")\n .trim();\n if (!pname) continue;\n const roles: string[] = [];\n const relRe = /<relationship>\\s*([\\s\\S]*?)\\s*<\\/relationship>/gi;\n let m: RegExpExecArray | null;\n while ((m = relRe.exec(block)) !== null) {\n const r = m[1]!.trim();\n if (r && !roles.includes(r)) roles.push(r);\n }\n persons.push({ name: pname, roles });\n }\n }\n } catch {\n /* fall back to search-hit metadata */\n }\n\n const cslug = slugify(name);\n if (!cslug) continue;\n const f: GraphFragment = { nodes: [], edges: [] };\n const cattrs: Record<string, Value> = { name };\n if (city) cattrs.city = city;\n if (state) cattrs.region = state;\n if (industry) cattrs.sector = industry;\n if (fileDate) {\n cattrs.first_seen = fileDate;\n cattrs.last_seen = fileDate;\n }\n f.nodes.push({ type: \"company\", key: cslug, attrs: cattrs });\n\n if (state) {\n const rslug = slugify(state);\n if (rslug) {\n f.nodes.push({ type: \"region\", key: rslug, attrs: { name: state } });\n f.edges.push({ from: { type: \"company\", key: cslug }, to: { type: \"region\", key: rslug }, label: \"located_in\" });\n }\n }\n\n const rkey = `${cslug}:form-d:${fileDate}`;\n const rattrs: Record<string, Value> = { round: \"Form D\", source_url: url };\n if (offering) rattrs.amount = offering;\n if (fileDate) rattrs.date = fileDate;\n f.nodes.push({ type: \"funding_round\", key: rkey, attrs: rattrs });\n f.edges.push({ from: { type: \"funding_round\", key: rkey }, to: { type: \"company\", key: cslug }, label: \"round_of\" });\n\n for (const p of persons) {\n if (!isLikelyPersonName(p.name)) continue;\n const pslug = slugify(p.name);\n if (!pslug) continue;\n const pattrs: Record<string, Value> = { name: p.name };\n if (p.roles.length) pattrs.role = p.roles.join(\", \");\n f.nodes.push({ type: \"person\", key: pslug, attrs: pattrs });\n f.edges.push({ from: { type: \"person\", key: pslug }, to: { type: \"company\", key: cslug }, label: \"works_at\" });\n }\n\n out.push({\n url,\n title: `Form D: ${name}${offering ? ` — ${offering}` : \"\"}`,\n date: fileDate || undefined,\n sourceName: \"SEC EDGAR\",\n sourceKind: \"edgar\",\n fragment: f,\n });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/**\n * TEMPLATE — a government / open-data connector.\n *\n * This exists to demonstrate the extensibility contract: adding a new source is\n * a self-contained module implementing SourceConnector + ONE line in the\n * registry + config entries in config/sources.ts. The cron, ingest workflow,\n * extractor, and graph writer are untouched — they only ever speak RawItem /\n * GraphFragment.\n *\n * To make it real, fetch your dataset and return either:\n * - text-bearing RawItems ({ ...meta, text }) → the LLM extracts a graph\n * - fragment-bearing RawItems ({ ...meta, fragment }) → straight to the writer\n * (map records to ontology nodes/edges yourself — see fred.ts / edgar.ts).\n * Returns [] until implemented, so wiring it up is harmless.\n */\ninterface GovDataCfg {\n dataset: string;\n watch?: string | null;\n}\n\nexport const govDataConnector: SourceConnector<GovDataCfg> = {\n id: \"gov-data\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): GovDataCfg {\n const r = raw as Record<string, unknown>;\n return { dataset: String(r.dataset ?? \"\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, _ctx: ConnectorContext): Promise<RawItem[]> {\n console.log(`gov-data: template connector for dataset \"${cfg.dataset}\" — implement fetch() to emit RawItems`);\n return [];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { urlHash } from \"../util/identity\";\n\n/**\n * LLM-native web search — a DISCOVERY connector. Each standing query is run\n * through the provider's web-search tool; the prose result becomes a text\n * RawItem that flows through the normal extractor (so discovered companies land\n * in the graph the same way feed items do). Discovery-only: fetch() is a no-op.\n */\ninterface WebSearchCfg {\n queries: string[];\n name: string;\n watch?: string | null;\n}\n\nconst SWEEP_SYSTEM =\n \"You are a research scout building a knowledge graph of entrepreneurs, startups, and business trends. Search the web and report concrete, real companies and the people leading them, with specifics (what they do, where, who founded them). Tie every claim to something you actually found — never invent names, numbers, or companies. If a search turns up nothing solid, say so plainly.\";\n\nexport const webSearchConnector: SourceConnector<WebSearchCfg> = {\n id: \"web-search\",\n capabilities: { fetch: false, discover: true, structured: false },\n\n parseConfig(raw): WebSearchCfg {\n const r = raw as Record<string, unknown>;\n const queries = Array.isArray(r.queries)\n ? (r.queries.filter((q) => typeof q === \"string\") as string[])\n : typeof r.query === \"string\"\n ? [r.query]\n : [];\n if (!queries.length) throw new Error(\"web-search connector requires `queries` (or a `query`)\");\n return { queries, name: String(r.name ?? \"web-search\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(): Promise<RawItem[]> {\n return []; // discovery-only\n },\n\n async discover(cfg, ctx: ConnectorContext): Promise<RawItem[]> {\n const provider = ctx.provider();\n const today = ctx.now.toISOString().slice(0, 10);\n const out: RawItem[] = [];\n for (const q of cfg.queries) {\n const { text } = await provider.search({\n system: SWEEP_SYSTEM,\n user: `Search task: ${q}\\nToday's date: ${today}\\n\\nReport what you find, naming specific companies and the real people leading them.`,\n });\n if (text.trim().length < 80) continue;\n out.push({\n url: `search://web-search/${urlHash(q)}#${today}`, // synthetic; changes daily so re-sweeps aren't deduped away\n title: q,\n date: today,\n sourceName: cfg.name,\n sourceKind: \"web-search\",\n watch: cfg.watch,\n text,\n });\n }\n return out;\n },\n};\n","import type { SourceConnector, ConnectorContext } from \"./types\";\nimport type { Ontology } from \"../ontology\";\nimport type { Provider } from \"../llm/provider\";\nimport { fetchText } from \"../util/content\";\nimport { rssConnector } from \"./rss\";\nimport { urlConnector } from \"./url\";\nimport { gdeltConnector } from \"./gdelt\";\nimport { fredConnector } from \"./fred\";\nimport { edgarConnector } from \"./edgar\";\nimport { govDataConnector } from \"./govdata\";\nimport { webSearchConnector } from \"./web-search\";\n\n/**\n * The connector registry. Adding a source (RSS, a government feed, a new API)\n * is: implement SourceConnector in a module, add one line here, add config\n * entries to config/sources.ts. The cron, workflow, extractor, and writer are\n * untouched — they only speak RawItem / GraphFragment.\n */\nconst REGISTRY = new Map<string, SourceConnector>([\n [rssConnector.id, rssConnector as SourceConnector],\n [urlConnector.id, urlConnector as SourceConnector],\n [gdeltConnector.id, gdeltConnector as SourceConnector],\n [fredConnector.id, fredConnector as SourceConnector], // structured (RawItem.fragment)\n [edgarConnector.id, edgarConnector as SourceConnector], // structured (RawItem.fragment)\n [webSearchConnector.id, webSearchConnector as SourceConnector], // discovery (web search)\n [govDataConnector.id, govDataConnector as SourceConnector], // template — zero core change to add\n]);\n\n/** Resolve a built-in source connector by its stable registry id. */\nexport function getConnector(id: string): SourceConnector | undefined {\n return REGISTRY.get(id);\n}\n\n/** List registered connector ids in deterministic insertion order. */\nexport function connectorIds(): string[] {\n return [...REGISTRY.keys()];\n}\n\n/** Everything a ConnectorContext needs, supplied by the host (worker, script, test). */\nexport interface ContextConfig {\n ontology: Ontology;\n provider: () => Provider;\n /** Named secrets (API keys, user agents) — e.g. the worker Env, or a plain object. */\n secrets?: Record<string, string | undefined>;\n now?: Date;\n}\n\n/** Create a host-supplied connector context with lazy provider construction. */\nexport function makeContext(cfg: ContextConfig): ConnectorContext {\n let provider: Provider | undefined; // lazy: only discovery connectors need it\n return {\n now: cfg.now ?? new Date(),\n fetchText: (u) => fetchText(u),\n secret: (name) => cfg.secrets?.[name],\n ontology: cfg.ontology,\n provider: () => (provider ??= cfg.provider()),\n };\n}\n","import type { ConnectorJob } from \"./connectors/types\";\n\n/**\n * Sources are DATA, not code. Each entry names a connector (`kind`) + its config\n * + scheduling. Add a feed → add an entry; no redeploy of the pipeline. P5 moves\n * this into KV behind a /config admin endpoint so edits don't need a deploy at all.\n */\nexport interface SourceEntry {\n kind: string; // connector id (registry key)\n name: string;\n enabled?: boolean; // default true\n cadenceHours?: number; // default 1 — runs when UTC hour % cadence === 0\n watch?: string | null;\n // connector-specific config (rss: url; more in P3):\n url?: string;\n query?: string;\n [key: string]: unknown;\n}\n\n/** Return source entries not explicitly disabled, preserving configured order. */\nexport function enabledSources(sources: SourceEntry[]): SourceEntry[] {\n return sources.filter((s) => s.enabled !== false);\n}\n\nfunction toJob(s: SourceEntry): ConnectorJob {\n return { kind: s.kind, name: s.name, watch: s.watch ?? null, cfg: s };\n}\n\n/** Enabled sources whose cadence lands on this UTC hour. */\nexport function jobsForHour(sources: SourceEntry[], hour: number): ConnectorJob[] {\n return enabledSources(sources)\n .filter((s) => hour % Math.max(1, Math.floor(s.cadenceHours ?? 1)) === 0)\n .map(toJob);\n}\n\n/** Every enabled source, ignoring cadence (manual /trigger?kind=sources). */\nexport function allJobs(sources: SourceEntry[]): ConnectorJob[] {\n return enabledSources(sources).map(toJob);\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceEntry } from \"../sources\";\nimport { urlHash } from \"../util/identity\";\n\nconst SEEN_TTL_S = 120 * 24 * 60 * 60; // 120 days\nconst ENRICHED_TTL_S = 30 * 24 * 60 * 60; // 30-day enrichment cooldown\nconst TEMPORAL_TTL_S = 120 * 24 * 60 * 60; // dated temporal-report history copies\n\n/**\n * The slice of a KV store the pipeline needs. Cloudflare's KVNamespace\n * satisfies it structurally; tests pass an in-memory map. Keeping the library\n * off Workers types is what lets it publish as a plain isomorphic package.\n */\nexport interface KVLike {\n get(key: string): Promise<string | null>;\n put(key: string, value: string, opts?: { expirationTtl?: number }): Promise<void>;\n}\n\n/**\n * Operational state in KV (never the graph itself — that lives in odla-db).\n * Ported/trimmed from agnacl services/kv.ts: seen-URL dedup + lastrun stamps.\n */\nexport class Store {\n constructor(private kv: KVLike) {}\n\n private seenKey(url: string): string {\n return `seen:${urlHash(url)}`;\n }\n\n async hasSeen(url: string): Promise<boolean> {\n return (await this.kv.get(this.seenKey(url))) !== null;\n }\n\n async markSeen(url: string): Promise<void> {\n await this.kv.put(this.seenKey(url), \"1\", { expirationTtl: SEEN_TTL_S });\n }\n\n /** Drop items whose URL we've already processed (checked BEFORE any LLM work). */\n async filterNew(items: RawItem[]): Promise<RawItem[]> {\n const flags = await Promise.all(items.map((it) => this.hasSeen(it.url)));\n return items.filter((_, i) => !flags[i]);\n }\n\n async setLastRun(kind: string, at = new Date().toISOString()): Promise<void> {\n await this.kv.put(`lastrun:${kind}`, at);\n }\n\n async getLastRun(kind: string): Promise<string | null> {\n return this.kv.get(`lastrun:${kind}`);\n }\n\n /** Per-entity enrichment cooldown so we don't re-search the same node constantly. */\n async isEnrichCooling(key: string): Promise<boolean> {\n return (await this.kv.get(`enriched:${key}`)) !== null;\n }\n\n async markEnriched(key: string): Promise<void> {\n await this.kv.put(`enriched:${key}`, new Date().toISOString(), { expirationTtl: ENRICHED_TTL_S });\n }\n\n /** Runtime source config (edit without redeploy). null = fall back to the static SOURCES. */\n async getConfigSources(): Promise<SourceEntry[] | null> {\n const raw = await this.kv.get(\"config:sources\");\n if (!raw) return null;\n try {\n const v = JSON.parse(raw);\n return Array.isArray(v) ? (v as SourceEntry[]) : null;\n } catch {\n return null;\n }\n }\n\n async setConfigSources(list: SourceEntry[]): Promise<void> {\n await this.kv.put(\"config:sources\", JSON.stringify(list));\n }\n\n /** Cache the latest temporal report (`temporal:latest`) plus a dated history copy. */\n async setTemporalReport(report: unknown, date = new Date().toISOString().slice(0, 10)): Promise<void> {\n const raw = JSON.stringify(report);\n await this.kv.put(\"temporal:latest\", raw);\n await this.kv.put(`temporal:${date}`, raw, { expirationTtl: TEMPORAL_TTL_S });\n }\n\n /** The cached latest temporal report, or null when absent/unparseable. */\n async getTemporalReport<T = unknown>(): Promise<T | null> {\n const raw = await this.kv.get(\"temporal:latest\");\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n }\n}\n","/**\n * Cloudflare Workflow hardening — ported verbatim from agnacl\n * (utils/workflow-helpers.ts). resilientStep adds structured error logging\n * without changing retry semantics; the step configs replace the engine's slow\n * default retry with fast exponential backoff.\n */\n\nexport async function resilientStep<T>(stepName: string, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const stack = err instanceof Error ? err.stack : undefined;\n console.error(`[STEP_ERROR] ${stepName}: ${msg}`);\n if (stack) console.error(`[STEP_STACK] ${stepName}: ${stack}`);\n throw err;\n }\n}\n\n/** I/O-bound steps: KV reads, feed fetches. */\nexport const IO_STEP_CONFIG = {\n retries: { limit: 3, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"2 minutes\" as const,\n};\n\n/** Graph write steps. */\nexport const COMMIT_STEP_CONFIG = {\n retries: { limit: 3, delay: \"3 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"90 seconds\" as const,\n};\n\n/** LLM generation steps (can be slow). */\nexport const LLM_STEP_CONFIG = {\n retries: { limit: 2, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"3 minutes\" as const,\n};\n\n/** Retry/timeout shape of the step configs above. */\nexport interface StepConfig {\n retries?: { limit: number; delay: string; backoff?: string };\n timeout?: string;\n}\n\n/**\n * Structural view of Cloudflare's WorkflowStep. WorkflowStep.do constrains its\n * result to Rpc.Serializable<T>, which cannot terminate (TS2589) on the\n * recursive Value type now that the @odla/* workspace packages give\n * @odla-ai/db's re-exports real definitions. A live WorkflowStep satisfies\n * this shape at runtime; widen through asStepLike at the workflow boundary.\n */\nexport interface StepLike {\n do<T>(name: string, fn: () => Promise<T>): Promise<T>;\n do<T>(name: string, config: StepConfig, fn: () => Promise<T>): Promise<T>;\n}\n\n/** Widen a WorkflowStep to StepLike (type-level only, no runtime cost). */\nexport function asStepLike(step: unknown): StepLike {\n return step as StepLike;\n}\n","import { LLM_STEP_CONFIG, type StepLike, resilientStep } from \"../workflow/helpers\";\nimport type { ExtractResult, RawItem } from \"../types\";\nimport type { AdminDb } from \"@odla-ai/db\";\nimport type { Store } from \"../state/kv\";\nimport type { Provider } from \"../llm/provider\";\nimport type { Ontology } from \"../ontology\";\nimport { writeFragment } from \"../graph/writer\";\nimport { canonicalizeFragment } from \"../graph/resolve\";\nimport { urlHash } from \"../util/identity\";\n\n/** Everything the shared ingest loop needs, supplied by the host. The\n * extractor is the ontology's domain craft (e.g. the OSINT extractor); the\n * library stays ontology-agnostic. */\nexport interface PipelineDeps {\n db: AdminDb;\n provider: Provider;\n ontology: Ontology;\n extract: (provider: Provider, item: RawItem) => Promise<ExtractResult | null>;\n store: Store;\n}\n\n/**\n * Shared per-item processing: each RawItem is its own step (non-blocking,\n * replay-cached). Structured items (fragment) skip the LLM; text items extract.\n * `resolve` FTS-canonicalizes new nodes against existing ones (discovery path).\n * Used by both the ingest (fetch) and discover (web-search) workflows.\n */\nexport async function ingestFresh(\n step: StepLike,\n deps: PipelineDeps,\n fresh: RawItem[],\n kind: string,\n resolve = false,\n): Promise<void> {\n const { db, provider, ontology, extract, store } = deps;\n if (fresh.length === 0) {\n await store.setLastRun(kind);\n return;\n }\n\n for (const item of fresh) {\n const id = urlHash(item.url);\n await step.do(`item-${id}`, LLM_STEP_CONFIG, () =>\n resilientStep(`item-${id}`, async () => {\n try {\n let fragment = item.fragment;\n if (!fragment) {\n const r = await extract(provider, item);\n if (!r) {\n await store.markSeen(item.url);\n return { skipped: true };\n }\n fragment = r.fragment;\n }\n if (resolve) fragment = await canonicalizeFragment(db, fragment, ontology);\n const { txId, ops } = await writeFragment(db, fragment, ontology, `ingest:${id}:v1`);\n await store.markSeen(item.url);\n return { txId, ops };\n } catch (e) {\n console.log(`ingest item failed (non-blocking): ${item.url}: ${e instanceof Error ? e.message : e}`);\n return { error: true };\n }\n }),\n );\n }\n\n await step.do(`stamp-${kind}`, () =>\n resilientStep(`stamp-${kind}`, async () => {\n await store.setLastRun(kind);\n return { ok: true };\n }),\n );\n}\n"],"mappings":"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","names":["UA"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/ontology.ts","../src/graph/merge.ts","../src/graph/writer.ts","../src/graph/export.ts","../src/graph/resolve.ts","../src/graph/connectors.ts","../src/util/identity.ts","../src/enrich/facts.ts","../src/enrich/run.ts","../src/llm/provider.ts","../src/llm/json.ts","../src/util/content.ts","../src/connectors/rss.ts","../src/connectors/url.ts","../src/connectors/gdelt.ts","../src/connectors/fred.ts","../src/connectors/edgar.ts","../src/connectors/govdata.ts","../src/connectors/web-search.ts","../src/connectors/registry.ts","../src/sources.ts","../src/state/kv.ts","../src/workflow/helpers.ts","../src/pipeline/run-items.ts"],"sourcesContent":["import type { Value } from \"@odla-ai/db\";\n\n/**\n * The output of a connector: one acquired item. Exactly one of `text` or\n * `fragment` is set — `text` goes through LLM extraction, `fragment` is already\n * ontology-typed and goes straight to the graph writer (structured feeds).\n */\nexport interface RawItem {\n url: string;\n title: string;\n date?: string;\n sourceName: string;\n sourceKind: string; // connector id: \"url\" | \"rss\" | \"edgar\" | ...\n watch?: string | null;\n text?: string;\n fragment?: GraphFragment;\n}\n\n/** A graph node = an odla-db entity in a namespace, keyed by a natural key. */\nexport interface GraphNode {\n type: string; // namespace, e.g. \"company\"\n key: string; // natural-key value (slug, urlHash, ...) — used for Lookup upsert\n attrs: Record<string, Value>; // node properties (the natural-key attr is added by the writer)\n}\n\n/** A graph edge = an odla-db `link` from one node to another by forward label. */\nexport interface GraphEdge {\n from: { type: string; key: string };\n to: { type: string; key: string };\n label: string; // forward link label, e.g. \"mentions\"\n}\n\n/** The single shape the graph writer consumes, whatever produced it. */\nexport interface GraphFragment {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\n/** What an extractor turns a text RawItem into: a fragment plus a prose summary. */\nexport interface ExtractResult {\n fragment: GraphFragment;\n summary: string;\n}\n\n/** Provider-agnostic LLM usage accounting. */\nexport interface Usage {\n calls: number;\n inputTokens: number;\n outputTokens: number;\n}\n\n/** Create zeroed provider-agnostic usage totals for one pipeline run. */\nexport function emptyUsage(): Usage {\n return { calls: 0, inputTokens: 0, outputTokens: 0 };\n}\n\n/** Mutate an accumulator by adding call, input-token, and output-token totals. */\nexport function addUsage(into: Usage, more: Usage): void {\n into.calls += more.calls;\n into.inputTokens += more.inputTokens;\n into.outputTokens += more.outputTokens;\n}\n","import type { SerializedAttr, SerializedEntity, SerializedLink, SerializedSchema, Cardinality } from \"@odla-ai/db\";\nimport type { NaturalKeys } from \"./graph/writer\";\n\n/** How a scalar attribute is reconciled when a node is re-discovered. */\nexport type MergeRule =\n | \"replace\" // last write wins (default)\n | \"prefer\" // first non-empty value sticks (stable facts like a name)\n | \"earliest\" // keep the smaller value (first_seen)\n | \"latest\"; // keep the larger value (last_seen)\n\nexport type AttrType = SerializedAttr[\"type\"]; // \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\"\n\nexport interface AttrSpec {\n type: AttrType;\n unique?: boolean;\n indexed?: boolean;\n optional?: boolean;\n /** The pipeline NEVER writes this attr (hand-edited in a UI); omitted from every update. */\n human?: boolean;\n /** Provenance rule for re-discovery. Default \"replace\". */\n merge?: MergeRule;\n /** Shown to the LLM when this attr appears in an extraction schema. */\n desc?: string;\n}\n\nexport interface EntitySpec {\n /** Unique attr used for Lookup upsert (MERGE key). Must be `unique` in attrs. */\n naturalKey: string;\n attrs: Record<string, AttrSpec>;\n /** FTS fields for fuzzy resolution (P5). */\n searchable?: string[];\n}\n\nexport interface EdgeSpec {\n from: string; // entity type\n to: string; // entity type\n forwardLabel: string; // link label on the `from` side (what the `link` op carries)\n reverseLabel: string;\n forwardHas?: Cardinality; // default \"many\"\n reverseHas?: Cardinality; // default \"many\"\n}\n\nexport interface Ontology {\n entities: Record<string, EntitySpec>;\n edges: Record<string, EdgeSpec>; // keyed by edge name\n}\n\n/** node type → natural-key attr name (for the graph writer's Lookup refs). */\nexport function naturalKeysOf(o: Ontology): NaturalKeys {\n const m: NaturalKeys = {};\n for (const [type, e] of Object.entries(o.entities)) m[type] = e.naturalKey;\n return m;\n}\n\n/** Attr name → merge rule, for every entity type (used by the provenance writer). */\nexport function mergeRulesOf(o: Ontology): Record<string, Record<string, MergeRule>> {\n const out: Record<string, Record<string, MergeRule>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = {};\n for (const [attr, spec] of Object.entries(e.attrs)) out[type][attr] = spec.merge ?? \"replace\";\n }\n return out;\n}\n\n/** Attr names the pipeline must never write, per entity type. */\nexport function humanAttrsOf(o: Ontology): Record<string, Set<string>> {\n const out: Record<string, Set<string>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = new Set(Object.entries(e.attrs).filter(([, a]) => a.human).map(([n]) => n));\n }\n return out;\n}\n\n/** Compile the ontology to an odla-db SerializedSchema to POST at /app/:id/schema. */\nexport function toSerializedSchema(o: Ontology): SerializedSchema {\n const entities: Record<string, SerializedEntity> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n const attrs: Record<string, SerializedAttr> = {};\n for (const [name, a] of Object.entries(e.attrs)) {\n attrs[name] = {\n type: a.type,\n unique: !!a.unique,\n indexed: !!a.indexed,\n optional: !!a.optional,\n };\n }\n entities[type] = { attrs };\n }\n const links: Record<string, SerializedLink> = {};\n for (const [name, ed] of Object.entries(o.edges)) {\n links[name] = {\n forward: { on: ed.from, has: ed.forwardHas ?? \"many\", label: ed.forwardLabel },\n reverse: { on: ed.to, has: ed.reverseHas ?? \"many\", label: ed.reverseLabel },\n };\n }\n return { entities, links };\n}\n","import type { Value } from \"@odla-ai/db\";\nimport type { MergeRule } from \"../ontology\";\n\nfunction isEmpty(v: Value | undefined): boolean {\n return v === undefined || v === null || v === \"\";\n}\n\n/**\n * Reconcile one scalar attribute when a node is re-discovered. Port of agnacl's\n * provenance rules (utils/markdown.ts). Empty incoming never clobbers; empty\n * existing is filled. Ordering rules compare stringwise (ISO dates sort right).\n */\nexport function mergeScalar(\n rule: MergeRule,\n existing: Value | undefined,\n incoming: Value | undefined,\n): Value | undefined {\n if (isEmpty(incoming)) return existing;\n if (isEmpty(existing)) return incoming;\n switch (rule) {\n case \"prefer\":\n return existing; // first non-empty value sticks\n case \"earliest\":\n return String(incoming) < String(existing) ? incoming : existing;\n case \"latest\":\n return String(incoming) > String(existing) ? incoming : existing;\n case \"replace\":\n default:\n return incoming;\n }\n}\n\n/**\n * Merge an incoming attr set into an existing one per the type's rules. `human`\n * attrs are excluded upstream (never in `incoming`); this only reconciles what\n * the pipeline owns. Returns the attrs to write (update leaves unlisted attrs intact).\n */\nexport function mergeAttrs(\n rules: Record<string, MergeRule>,\n existing: Record<string, Value>,\n incoming: Record<string, Value>,\n): Record<string, Value> {\n const out: Record<string, Value> = {};\n for (const [attr, val] of Object.entries(incoming)) {\n const merged = mergeScalar(rules[attr] ?? \"replace\", existing[attr], val);\n if (merged !== undefined) out[attr] = merged;\n }\n return out;\n}\n","import type {\n AdminDb,\n Op,\n EntityRef,\n Value,\n InstaQLQuery,\n QueryResult,\n} from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport {\n naturalKeysOf,\n mergeRulesOf,\n humanAttrsOf,\n type Ontology,\n} from \"../ontology\";\nimport { mergeAttrs } from \"./merge\";\n\n/** node type → unique natural-key attribute name. */\nexport type NaturalKeys = Record<string, string>;\n\n/**\n * Pure fragment → ops mapping (no read/merge). Node upserts by Lookup natural\n * key, then links — nodes always before links so `link` targets resolve.\n * Used by tests and as the building block for the provenance path below.\n */\nexport function fragmentToOps(fragment: GraphFragment, keys: NaturalKeys): Op[] {\n const ref = (type: string, key: string): EntityRef => {\n const attr = keys[type];\n if (!attr) throw new Error(`No natural key configured for node type \"${type}\"`);\n return { ns: type, attr, value: key };\n };\n const ops: Op[] = [];\n for (const n of fragment.nodes) {\n const attr = keys[n.type];\n if (!attr) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n ops.push({ t: \"update\", ns: n.type, id: { ns: n.type, attr, value: n.key }, attrs: { ...n.attrs, [attr]: n.key } });\n }\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n return ops;\n}\n\n/**\n * Provenance-preserving write. Reads existing nodes by natural key (one batched\n * query), merges scalar attrs per the ontology's rules, drops human-owned\n * attrs, then writes merged node updates + all links in one atomic, idempotent\n * transaction. `mutationId` must be deterministic so odla-db can deduplicate\n * the write on retry; this does not make preceding reads or external effects\n * exactly-once.\n */\nexport async function writeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n mutationId: string,\n): Promise<{ txId: number; ops: number }> {\n const keys = naturalKeysOf(ontology);\n const rules = mergeRulesOf(ontology);\n const humans = humanAttrsOf(ontology);\n\n // 1. Collapse duplicate nodes within the fragment (same type+key), dropping human attrs.\n const byType = new Map<string, Map<string, Record<string, Value>>>();\n for (const n of fragment.nodes) {\n if (!keys[n.type]) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n const human = humans[n.type] ?? new Set<string>();\n const clean: Record<string, Value> = {};\n for (const [k, v] of Object.entries(n.attrs)) if (!human.has(k)) clean[k] = v;\n const typeMap = byType.get(n.type) ?? new Map<string, Record<string, Value>>();\n const prev = typeMap.get(n.key);\n typeMap.set(n.key, prev ? mergeAttrs(rules[n.type]!, prev, clean) : clean);\n byType.set(n.type, typeMap);\n }\n\n // 2. Batch-read existing rows per type by natural key (one query, filtered per level).\n const query: InstaQLQuery = {};\n for (const [type, m] of byType) {\n query[type] = { $: { where: { [keys[type]!]: { $in: [...m.keys()] } } } };\n }\n const existing: QueryResult = Object.keys(query).length ? await db.query(query) : {};\n const priorOf = (type: string, key: string): Record<string, Value> => {\n const attr = keys[type]!;\n for (const row of existing[type] ?? []) {\n if (row[attr] === key) return row as unknown as Record<string, Value>;\n }\n return {};\n };\n\n // 3. Merged node updates first, then links.\n const ops: Op[] = [];\n for (const [type, m] of byType) {\n const attr = keys[type]!;\n for (const [key, incoming] of m) {\n const merged = mergeAttrs(rules[type]!, priorOf(type, key), incoming);\n ops.push({ t: \"update\", ns: type, id: { ns: type, attr, value: key }, attrs: { ...merged, [attr]: key } });\n }\n }\n const ref = (type: string, key: string): EntityRef => ({ ns: type, attr: keys[type]!, value: key });\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n\n if (ops.length === 0) return { txId: 0, ops: 0 };\n const { txId } = await db.transact(ops, { mutationId });\n return { txId, ops: ops.length };\n}\n","// Read a GraphFragment back OUT of odla-db — the inverse of the writer, and\n// the bridge from the live graph to the pure @odla-ai/kg/temporal analytics.\n// Reads are per-type offset walks with link labels as nested InstaQL keys\n// (odla-db has no cross-relation where; filter per level). Reverse labels are\n// normalized to FORWARD edges so the fragment satisfies resolvePath's\n// forward-only traversal.\nimport type { AdminDb } from \"@odla-ai/db\";\nimport type { GraphFragment, GraphNode, GraphEdge } from \"../types\";\nimport type { Ontology } from \"../ontology\";\nimport { naturalKeysOf } from \"../ontology\";\nimport { parseDateMs } from \"../temporal/time\";\n\n/** One entity type to export. */\nexport interface ExportEntitySpec {\n /** Link labels (forward or reverse, as queried in InstaQL) to pull as nested rows. */\n links?: readonly string[];\n /** Attr allowlist kept on nodes. Default: every scalar attr except `id` and links. */\n attrs?: readonly string[];\n /**\n * Date window. Filtering is client-side by default; set `indexed: true`\n * (only when the attr really is indexed) to also push a\n * `where { [dateAttr]: { $gte: from } }` to the server — odla-db silently\n * returns zero rows for `where` on unindexed attrs.\n */\n window?: { dateAttr: string; from: string; indexed?: boolean };\n /** Row cap for this type across the offset walk. Default 1000. */\n limit?: number;\n}\n\n/** What {@link exportFragment} should read. */\nexport interface ExportSpec {\n entities: Record<string, ExportEntitySpec>;\n /** Rows per page in the offset walk (1–1000). Default 200. */\n pageSize?: number;\n}\n\ninterface LinkPlan {\n label: string;\n counterpart: string;\n /** Forward: root --forwardLabel--> nested. Reverse: nested --forwardLabel--> root. */\n direction: \"forward\" | \"reverse\";\n forwardLabel: string;\n}\n\nfunction planLinks(ontology: Ontology, type: string, labels: readonly string[]): LinkPlan[] {\n return labels.map((label) => {\n for (const edge of Object.values(ontology.edges)) {\n if (edge.forwardLabel === label && edge.from === type) {\n return { label, counterpart: edge.to, direction: \"forward\" as const, forwardLabel: edge.forwardLabel };\n }\n if (edge.reverseLabel === label && edge.to === type) {\n return { label, counterpart: edge.from, direction: \"reverse\" as const, forwardLabel: edge.forwardLabel };\n }\n }\n throw new Error(`exportFragment: no ontology edge matches link \"${label}\" on \"${type}\"`);\n });\n}\n\nconst isScalar = (v: unknown) => v === null || (typeof v !== \"object\" && typeof v !== \"function\");\n\nfunction pickAttrs(row: Record<string, unknown>, spec: ExportEntitySpec, links: readonly string[]) {\n const out: GraphNode[\"attrs\"] = {};\n for (const [k, v] of Object.entries(row)) {\n if (spec.attrs ? !spec.attrs.includes(k) : k === \"id\" || links.includes(k) || !isScalar(v)) continue;\n if (isScalar(v) && v !== null) out[k] = v as GraphNode[\"attrs\"][string];\n }\n return out;\n}\n\nconst RETRYABLE_WITHOUT_WHERE = /query_scan_limit|query_too_complex/;\n\n/**\n * Export (a window of) the graph as a `GraphFragment` — nodes and forward\n * edges — ready for `extractEvents` and the rest of `@odla-ai/kg/temporal`.\n *\n * Each entity type is read with a stable offset walk (ordered by its natural\n * key) carrying its requested link labels as nested relations. Date windows\n * filter client-side; for attrs declared `indexed` the filter also runs\n * server-side, and if that indexed filter ever exceeds the scan budget\n * (`query_scan_limit`) the walk restarts without `where`.\n * Nodes dedupe by `type:key` keeping the richer attr set; output ordering is\n * deterministic (nodes by type/key, edges by from/label/to).\n */\nexport async function exportFragment(db: AdminDb, ontology: Ontology, spec: ExportSpec): Promise<GraphFragment> {\n const keys = naturalKeysOf(ontology);\n const pageSize = Math.min(1000, Math.max(1, spec.pageSize ?? 200));\n const nodes = new Map<string, GraphNode>();\n const edges = new Map<string, GraphEdge>();\n\n const putNode = (node: GraphNode) => {\n const id = `${node.type}:${node.key}`;\n const existing = nodes.get(id);\n if (!existing || Object.keys(node.attrs).length > Object.keys(existing.attrs).length) nodes.set(id, node);\n };\n\n for (const [type, entity] of Object.entries(spec.entities)) {\n const naturalKey = keys[type];\n if (!naturalKey) throw new Error(`exportFragment: no ontology entity \"${type}\"`);\n const links = planLinks(ontology, type, entity.links ?? []);\n const limit = entity.limit ?? 1000;\n const fromMs = entity.window ? parseDateMs(entity.window.from) : null;\n let useWhere = entity.window?.indexed === true;\n let offset = 0;\n let taken = 0;\n\n while (taken < limit) {\n const opts: Record<string, unknown> = {\n limit: Math.min(pageSize, limit - taken),\n offset,\n order: { [naturalKey]: \"asc\" },\n };\n if (useWhere && entity.window) opts.where = { [entity.window.dateAttr]: { $gte: entity.window.from } };\n const query = {\n [type]: { $: opts, ...Object.fromEntries(links.map((l) => [l.label, {}])) },\n };\n\n let rows: Array<Record<string, unknown>>;\n try {\n rows = ((await db.query(query)) as Record<string, unknown[]>)[type] as Array<Record<string, unknown>> ?? [];\n } catch (err) {\n if (useWhere && RETRYABLE_WITHOUT_WHERE.test(String((err as Error).message ?? err))) {\n // Unindexed attr blew the scan budget — restart this type's walk\n // without `where` (offsets aren't comparable across filters; the\n // dedupe maps make re-reads harmless) and filter client-side.\n useWhere = false;\n offset = 0;\n taken = 0;\n continue;\n }\n throw err;\n }\n\n for (const row of rows) {\n const key = row[naturalKey];\n if (key === undefined || key === null) continue; // defensive: unkeyed row\n if (entity.window && fromMs !== null) {\n const t = parseDateMs(row[entity.window.dateAttr]);\n if (t !== null && t < fromMs) continue;\n }\n const rootRef = { type, key: String(key) };\n putNode({ ...rootRef, attrs: pickAttrs(row, entity, entity.links ?? []) });\n\n for (const plan of links) {\n const nestedKeyAttr = keys[plan.counterpart];\n if (!nestedKeyAttr) throw new Error(`exportFragment: no ontology entity \"${plan.counterpart}\"`);\n // has-many relations come back as arrays, has-one as a bare object.\n const raw = row[plan.label];\n const nestedRows = Array.isArray(raw) ? raw : raw ? [raw] : [];\n for (const nested of nestedRows as Array<Record<string, unknown>>) {\n const nestedKey = nested[nestedKeyAttr];\n if (nestedKey === undefined || nestedKey === null) continue;\n const nestedRef = { type: plan.counterpart, key: String(nestedKey) };\n putNode({ ...nestedRef, attrs: pickAttrs(nested, {}, []) });\n const edge: GraphEdge =\n plan.direction === \"forward\"\n ? { from: rootRef, to: nestedRef, label: plan.forwardLabel }\n : { from: nestedRef, to: rootRef, label: plan.forwardLabel };\n edges.set(`${edge.from.type}:${edge.from.key}|${edge.label}|${edge.to.type}:${edge.to.key}`, edge);\n }\n }\n taken++;\n if (taken >= limit) break;\n }\n\n if (rows.length < pageSize) break;\n offset += rows.length;\n }\n }\n\n const byRef = (a: { type: string; key: string }, b: { type: string; key: string }) =>\n a.type < b.type ? -1 : a.type > b.type ? 1 : a.key < b.key ? -1 : a.key > b.key ? 1 : 0;\n return {\n nodes: [...nodes.values()].sort(byRef),\n edges: [...edges.values()].sort(\n (a, b) => byRef(a.from, b.from) || (a.label < b.label ? -1 : a.label > b.label ? 1 : 0) || byRef(a.to, b.to),\n ),\n };\n}\n","import type { AdminDb, Entity } from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport type { Ontology } from \"../ontology\";\n\n/** FTS5 search over a namespace's text fields (odla-db QueryOpts.search). */\nexport async function searchEntities(db: AdminDb, type: string, q: string, limit = 5): Promise<Entity[]> {\n if (!q.trim()) return [];\n const res = await db.query({ [type]: { $: { search: q, limit } } });\n return (res[type] ?? []) as Entity[];\n}\n\nconst norm = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]+/g, \"\");\n\n/**\n * Resolve a name to an existing entity's natural key via FTS — for merging a\n * re-discovery that slugified differently. Conservative: only returns a hit when\n * the top match's name/key normalizes identically to the query.\n */\nexport async function resolveName(\n db: AdminDb,\n type: string,\n keyAttr: string,\n name: string,\n): Promise<string | null> {\n const target = norm(name);\n if (!target) return null;\n for (const h of await searchEntities(db, type, name, 3)) {\n const key = h[keyAttr];\n const hname = h[\"name\"];\n if (typeof key !== \"string\") continue;\n if ((typeof hname === \"string\" && norm(hname) === target) || norm(key) === target) return key;\n }\n return null;\n}\n\n/**\n * Canonicalize a fragment's searchable nodes against existing entities via FTS,\n * remapping node keys (and the edges that reference them) to the canonical key.\n * Used on the lower-volume discovery/enrich paths, not high-volume ingest.\n */\nexport async function canonicalizeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n): Promise<GraphFragment> {\n const remap = new Map<string, string>(); // `${type}:${key}` -> canonical key\n for (const n of fragment.nodes) {\n const spec = ontology.entities[n.type];\n if (!spec?.searchable?.length) continue;\n const name = typeof n.attrs.name === \"string\" ? n.attrs.name : \"\";\n if (!name) continue;\n const canon = await resolveName(db, n.type, spec.naturalKey, name);\n if (canon && canon !== n.key) remap.set(`${n.type}:${n.key}`, canon);\n }\n if (remap.size === 0) return fragment;\n\n const rk = (type: string, key: string): string => remap.get(`${type}:${key}`) ?? key;\n return {\n nodes: fragment.nodes.map((n) => ({ ...n, key: rk(n.type, n.key) })),\n edges: fragment.edges.map((e) => ({\n from: { type: e.from.type, key: rk(e.from.type, e.from.key) },\n to: { type: e.to.type, key: rk(e.to.type, e.to.key) },\n label: e.label,\n })),\n };\n}\n","// Connector discovery: given a GraphFragment and a set of \"lenses\" (which\n// shared entity connects two nodes of the universe type), find the nodes that\n// bridge otherwise-distinct groups — the brokers/connectors. Pure and\n// deterministic; the headless form of what the retired galaxy viewer showed.\n//\n// A lens says \"two <universe> nodes are related when they share an <X>, where\n// X links to the universe node through reified node R via labels (bridge, of)\".\n// e.g. two companies share a PERSON via `affiliation` (of_person → of_company),\n// or share an INVESTOR via `funding_round` (raised_from → round_of).\nimport type { GraphFragment } from \"../types\";\nimport {\n blendPairs,\n bridgingScores,\n labelPropagation,\n projectBridges,\n reifiedBridgeTargets,\n topBridges,\n type WeightedPair,\n} from \"../temporal\";\n\n/** One relatedness lens over the universe nodes. */\nexport interface ConnectorLens {\n /** Reified relationship node type carrying both edges (e.g. `\"affiliation\"`). */\n reified: string;\n /** Edge label to the SHARED entity that bridges (e.g. `\"of_person\"`). */\n bridge: string;\n /** Edge label to the UNIVERSE node the shared entity connects (e.g. `\"of_company\"`). */\n of: string;\n /** Blend weight (each lens is normalized to max 1 first). Default 1. */\n weight?: number;\n}\n\n/** How to find connectors: the lenses to blend, plus limits. */\nexport interface ConnectorConfig {\n lenses: ConnectorLens[];\n /** Extra precomputed pairs to fold in (e.g. geo/time affinity). */\n extraPairs?: Array<{ pairs: WeightedPair[]; weight: number }>;\n /** How many connectors to return. Default 12. */\n top?: number;\n /** Skip promiscuous shared entities touching more than this many nodes. Default 12. */\n maxPerBridge?: number;\n}\n\n/** One bridging node: the groups it joins and the shared entities that link it out. */\nexport interface Connector {\n /** `type:key` of the connector node. */\n ref: string;\n name: string;\n /** 0..1 brokerage — 1 is the strongest connector found. */\n score: number;\n /** Representative names of the distinct groups it bridges. */\n groups: string[];\n /** Names of the shared entities (people/investors/…) linking it to other groups. */\n via: string[];\n}\n\n/** The ranked connectors plus the size of the graph they were found in. */\nexport interface ConnectorReport {\n connectors: Connector[];\n /** Distinct communities in the blended graph. */\n communities: number;\n /** Universe nodes with at least one relationship. */\n nodes: number;\n}\n\n/**\n * Rank the connectors of a graph. Builds the blended relationship graph from\n * the lenses, detects communities, scores each node's cross-community\n * brokerage, and returns the top bridges with the groups they join and the\n * shared entities behind each link. Empty/relationshipless input → empty report.\n */\nexport function findConnectors(fragment: GraphFragment, config: ConnectorConfig): ConnectorReport {\n const nameOf = new Map<string, string>();\n for (const n of fragment.nodes) {\n const label = n.attrs.name ?? n.attrs.title;\n if (typeof label === \"string\" && label) nameOf.set(`${n.type}:${n.key}`, label);\n }\n\n const sets = config.lenses.map((l) => ({\n pairs: projectBridges(reifiedBridgeTargets(fragment, { reified: l.reified, bridgeLabel: l.bridge, targetLabel: l.of }), {\n maxPerBridge: config.maxPerBridge ?? 12,\n }),\n weight: l.weight ?? 1,\n }));\n const pairs = blendPairs([...sets, ...(config.extraPairs ?? [])]);\n if (pairs.length === 0) return { connectors: [], communities: 0, nodes: 0 };\n\n const nodes = [...new Set(pairs.flatMap((p) => [p.a, p.b]))].sort();\n const edges = pairs.map((p) => ({ a: p.a, b: p.b, weight: p.w }));\n const labels = labelPropagation(nodes, edges, { seed: 1 });\n\n // Representative display name per community: an anchor member's name.\n const groupName = new Map<string, string>();\n for (const id of nodes) {\n const g = labels.get(id)!;\n if (!groupName.has(g)) groupName.set(g, nameOf.get(id) ?? id);\n }\n\n const bridging = bridgingScores(edges, labels);\n const viaOf = (id: string) => {\n const names = new Set<string>();\n for (const p of pairs) {\n if (p.a !== id && p.b !== id) continue;\n // via ids are always non-empty `type:key`, so pop() is a string.\n for (const v of p.via) names.add(nameOf.get(v) ?? v.split(\":\").pop()!);\n }\n return [...names].slice(0, 6);\n };\n\n const connectors = topBridges(bridging, config.top ?? 12).map((b): Connector => ({\n ref: b.id,\n name: nameOf.get(b.id) ?? b.id,\n score: b.score,\n groups: b.groups.map((g) => groupName.get(g)!), // every label has an anchor name\n\n via: viaOf(b.id),\n }));\n\n return { connectors, communities: new Set(labels.values()).size, nodes: nodes.length };\n}\n","/**\n * Identity + dedup helpers. The slug / urlHash a node resolves to IS its\n * natural key for Lookup-based upserts, so these must be stable and pure.\n * Ported from agnacl (utils/markdown.ts slugify, services/kv.ts normalizeUrl).\n */\n\n/** NFKD-normalize, strip accents, lowercase, non-alphanumerics → \"-\", cap 60. */\nexport function slugify(input: string): string {\n return input\n .normalize(\"NFKD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60)\n .replace(/-+$/g, \"\");\n}\n\n/** Drop tracking params, hash, and trailing slash so the same page maps to one key. */\nexport function normalizeUrl(raw: string): string {\n try {\n const u = new URL(raw);\n u.hash = \"\";\n const drop = [/^utm_/i, /^ref$/i, /^ref_/i, /^fbclid$/i, /^gclid$/i, /^mc_/i];\n for (const key of [...u.searchParams.keys()]) {\n if (drop.some((re) => re.test(key))) u.searchParams.delete(key);\n }\n let s = u.toString();\n s = s.replace(/\\/$/, \"\");\n return s;\n } catch {\n return raw.trim().replace(/#.*$/, \"\").replace(/\\/$/, \"\");\n }\n}\n\n/** Stable short key for a URL (FNV-1a 32-bit, base36). Used as the `signal` natural key. */\nexport function urlHash(raw: string): string {\n const s = normalizeUrl(raw);\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(36);\n}\n\n/**\n * Reject descriptive phrases masquerading as people (\"three craftsmen\",\n * \"the founders\"). A real name is 2-4 capitalized tokens, no digits.\n * Ported from agnacl utils/names.ts (isLikelyPersonName).\n */\nexport function isLikelyPersonName(name: string): boolean {\n const n = name.trim();\n if (!n || /\\d/.test(n)) return false;\n const parts = n.split(/\\s+/);\n if (parts.length < 2 || parts.length > 4) return false;\n return parts.every((p) => /^[A-Z][a-zA-Z'’.-]*$/.test(p));\n}\n","// Company enrichment: prompts, forced-tool schemas, and the pure fragment\n// builders that turn a model's research/structuring output into an idempotent\n// GraphFragment (company attrs + person/affiliation/funding_round/investor\n// nodes). The model's output is a proposal — everything here is capped and\n// validated before it becomes graph. This module is data + pure functions; the\n// LLM runners live in ./run.\nimport type { ExtractTool } from \"@odla-ai/ai\";\nimport { slugify } from \"../util/identity\";\nimport type { GraphFragment, GraphNode } from \"../types\";\n\n/** The minimal company row enrichment reads (attrs only — never hydrates). */\nexport interface CompanyRow {\n slug: string;\n name: string;\n city?: string;\n region?: string;\n first_seen?: string;\n founded?: string;\n fips?: string;\n}\n\n// ------------------------------------------------------------------ locate\n\n/** System prompt for the county-resolution extraction. */\nexport const LOCATE_SYSTEM =\n \"You resolve US business locations to counties. For each company you are given a city and/or a free-text region. Name the US county containing that city (for independent cities, use the city's own county-equivalent name) plus its 2-letter state. Use nulls when the location is ambiguous, outside the US, or unknown.\";\n\n/** Forced-tool schema pairing each company slug with a county name + state. */\nexport const LOCATE_TOOL: ExtractTool = {\n name: \"resolve_counties\",\n description: \"County name + state per company\",\n parameters: {\n type: \"object\",\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n slug: { type: \"string\" },\n county: { type: [\"string\", \"null\"], description: 'county name WITHOUT the word \"County\", e.g. \"Santa Clara\"' },\n state: { type: [\"string\", \"null\"], description: \"2-letter USPS state\" },\n },\n required: [\"slug\", \"county\", \"state\"],\n },\n },\n },\n required: [\"items\"],\n },\n};\n\n/** The user prompt for a LOCATE batch. */\nexport function locatePrompt(batch: CompanyRow[]): string {\n return \"Companies:\\n\" + batch.map((c) => `- slug ${c.slug}: city=${c.city ?? \"?\"} region=${c.region ?? \"?\"}`).join(\"\\n\");\n}\n\n// ------------------------------------------------------------------- facts\n\n/** System prompt for the grounded web-search research pass. */\nexport const FACTS_SEARCH_SYSTEM =\n \"You are a research assistant with web search. Find concrete facts about the company: founding year/date, headquarters city and state, founders and key executives (names, roles), and funding (rounds, dates, amounts, investor names). Prefer official/reputable sources. Report dense factual notes; say 'not found' for anything you cannot establish. Do not speculate.\";\n\n/** System prompt for structuring the research notes into {@link FactsDraft}. */\nexport const FACTS_EXTRACT_SYSTEM =\n \"Extract structured facts from the research notes. Use null for anything the notes do not establish.\";\n\n/** Forced-tool schema for the structured company facts (founding, HQ, people, funding). */\nexport const FACTS_EXTRACT_TOOL: ExtractTool = {\n name: \"company_facts\",\n description: \"Structured company facts from research notes\",\n parameters: {\n type: \"object\",\n properties: {\n founded: { type: [\"string\", \"null\"], description: \"YYYY or YYYY-MM or YYYY-MM-DD\" },\n city: { type: [\"string\", \"null\"] },\n state: { type: [\"string\", \"null\"], description: \"2-letter USPS\" },\n people: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: { name: { type: \"string\" }, role: { type: \"string\" } },\n required: [\"name\", \"role\"],\n },\n },\n rounds: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n round: { type: [\"string\", \"null\"] },\n date: { type: [\"string\", \"null\"] },\n amount: { type: [\"string\", \"null\"] },\n investors: { type: \"array\", items: { type: \"string\" } },\n },\n required: [\"investors\"],\n },\n },\n },\n required: [\"people\", \"rounds\"],\n },\n};\n\n/** The model's structured company facts, before validation into a fragment. */\nexport interface FactsDraft {\n founded?: string | null;\n city?: string | null;\n state?: string | null;\n people: Array<{ name: string; role: string }>;\n rounds: Array<{ round?: string | null; date?: string | null; amount?: string | null; investors: string[] }>;\n}\n\nconst DATE_RE = /^\\d{4}(-\\d{2}){0,2}$/;\n\n/** Pad a year/month-precision date to a full ISO date (odla-db date attrs reject bare years). */\nconst isoDate = (v: string): string | null =>\n !DATE_RE.test(v) ? null : v.length === 4 ? `${v}-01-01` : v.length === 7 ? `${v}-01` : v;\n\n/** Loose county-name key for joining model output (\"Santa Clara\",\"CA\") to a gazetteer. */\nexport const countyKey = (name: string, st: string): string =>\n `${String(name).toLowerCase().replace(/\\bcounty\\b|\\bparish\\b|\\bborough\\b/g, \"\").replace(/[^a-z]/g, \"\")}|${String(st).toUpperCase()}`;\n\n/**\n * Turn one company's structured facts into an idempotent fragment: founded/\n * city attrs, person + dated affiliation nodes, funding_round nodes (keyed\n * `slug~e<i>` so they never collide with seed rounds), and investors. Capped\n * and validated — the draft is a proposal, not a command.\n */\nexport function factsToFragment(company: CompanyRow, draft: FactsDraft): GraphFragment {\n const nodes: GraphNode[] = [];\n const edges: GraphFragment[\"edges\"] = [];\n const attrs: Record<string, string> = {};\n const founded = draft.founded ? isoDate(draft.founded) : null;\n if (founded) attrs.founded = founded;\n if (draft.city && !company.city) attrs.city = String(draft.city).slice(0, 60);\n if (Object.keys(attrs).length > 0) {\n nodes.push({ type: \"company\", key: company.slug, attrs: { name: company.name, ...attrs } });\n }\n\n const anchorDate = attrs.founded ?? company.founded ?? company.first_seen ?? \"\";\n for (const p of (draft.people ?? []).slice(0, 5)) {\n const personSlug = slugify(p.name);\n if (!personSlug || personSlug === company.slug) continue;\n nodes.push({ type: \"person\", key: personSlug, attrs: { name: p.name } });\n const key = `${personSlug}~${company.slug}`;\n nodes.push({ type: \"affiliation\", key, attrs: { date: anchorDate, role: String(p.role).slice(0, 60) } });\n edges.push({ from: { type: \"affiliation\", key }, to: { type: \"person\", key: personSlug }, label: \"of_person\" });\n edges.push({ from: { type: \"affiliation\", key }, to: { type: \"company\", key: company.slug }, label: \"of_company\" });\n }\n\n (draft.rounds ?? []).slice(0, 4).forEach((r, i) => {\n const investors = (r.investors ?? []).map(slugify).filter(Boolean).slice(0, 6);\n const date = r.date ? isoDate(r.date) : null;\n if (investors.length === 0 && !date) return; // an undated, investor-less round says nothing\n const key = `${company.slug}~e${i}`;\n const rAttrs: Record<string, string> = { date: date ?? anchorDate };\n if (r.round) rAttrs.round = String(r.round).slice(0, 40);\n if (r.amount) rAttrs.amount = String(r.amount).slice(0, 40);\n nodes.push({ type: \"funding_round\", key, attrs: rAttrs });\n edges.push({ from: { type: \"funding_round\", key }, to: { type: \"company\", key: company.slug }, label: \"round_of\" });\n for (const [j, invSlug] of investors.entries()) {\n nodes.push({ type: \"investor\", key: invSlug, attrs: { name: (r.investors ?? [])[j]! } });\n edges.push({ from: { type: \"funding_round\", key }, to: { type: \"investor\", key: invSlug }, label: \"raised_from\" });\n }\n });\n\n return { nodes, edges };\n}\n\n/** The `located_in` write linking a company to its resolved county. */\nexport function locationFragment(slug: string, name: string, fips: string): GraphFragment {\n return {\n nodes: [{ type: \"company\", key: slug, attrs: { name, fips } }],\n edges: [{ from: { type: \"company\", key: slug }, to: { type: \"county\", key: fips }, label: \"located_in\" }],\n };\n}\n","// The enrichment runners: Env-agnostic functions that take an @odla-ai/ai\n// `Ai` (bring-your-own provider/keys — the caller decides which lane, e.g. a\n// cheap vaulted key vs the platform provider) and produce graph fragments.\n// Callers persist the fragments (via writeFragment) and own batching,\n// concurrency, and cooldowns.\nimport type { Ai } from \"@odla-ai/ai\";\nimport type { GraphFragment } from \"../types\";\nimport {\n FACTS_EXTRACT_SYSTEM,\n FACTS_EXTRACT_TOOL,\n FACTS_SEARCH_SYSTEM,\n LOCATE_SYSTEM,\n LOCATE_TOOL,\n countyKey,\n factsToFragment,\n locatePrompt,\n type CompanyRow,\n type FactsDraft,\n} from \"./facts\";\n\n/** Which model serves each verb (search must be a web-grounded model). */\nexport interface EnrichModels {\n /** Web-search-grounded research model. */\n search: string;\n /** Structured-extraction (forced tool-call) model. */\n extract: string;\n}\n\n/**\n * Enrich one company: a grounded web search for its facts, then a structured\n * extraction into a validated {@link factsToFragment}. Retries the extraction\n * once (reasoning models sometimes skip a forced tool call). Returns the\n * fragment (possibly empty — nothing established); the caller writes it.\n */\nexport async function enrichCompany(ai: Ai, models: EnrichModels, company: CompanyRow): Promise<GraphFragment> {\n const where = `${company.city ? `, ${company.city}` : \"\"}${company.region ? ` (${company.region})` : \"\"}`;\n const { text } = await ai.search({\n model: models.search,\n system: FACTS_SEARCH_SYSTEM,\n user: `Company: ${company.name}${where}. Research its founding, headquarters, key people, and funding.`,\n maxTokens: 900,\n });\n const extractOnce = () =>\n ai.extract<FactsDraft>({\n model: models.extract,\n system: FACTS_EXTRACT_SYSTEM,\n user: `Company: ${company.name}\\n\\nNotes:\\n${text}`,\n tool: FACTS_EXTRACT_TOOL,\n maxTokens: 4000, // reasoning headroom before the forced tool call\n });\n const { value } = await extractOnce().catch(extractOnce);\n return factsToFragment(company, value);\n}\n\n/**\n * Resolve a batch of companies' free-text locations to county FIPS codes via a\n * single structured extraction, joined against a caller-supplied gazetteer\n * (`countyKey(name, state)` → FIPS). Only confident, gazetteer-matched\n * resolutions are returned.\n */\nexport async function resolveCounties(\n ai: Ai,\n model: string,\n rows: CompanyRow[],\n gazetteer: Map<string, string>,\n): Promise<Array<{ slug: string; fips: string }>> {\n const { value } = await ai.extract<{ items: Array<{ slug: string; county: string | null; state: string | null }> }>({\n model,\n system: LOCATE_SYSTEM,\n user: locatePrompt(rows),\n tool: LOCATE_TOOL,\n maxTokens: 6000, // reasoning models spend tokens before the tool call\n });\n const out: Array<{ slug: string; fips: string }> = [];\n for (const item of value.items ?? []) {\n const fips = item.county && item.state ? gazetteer.get(countyKey(item.county, item.state)) : undefined;\n if (fips) out.push({ slug: item.slug, fips });\n }\n return out;\n}\n","import { init, extractText, type Ai } from \"@odla-ai/ai\";\nimport type { Usage } from \"../types\";\n\n/**\n * The LLM seam, now a thin adapter over @odla-ai/ai (which was itself\n * generalized from this package's original Provider). Three verbs stay:\n * extract (forced structured tool call), search (web-search-enabled text),\n * write (plain generation). Callers never see vendor or oracle types.\n */\n\nexport type ProviderFamily = \"claude\" | \"openai\";\n\n/** Role → model tiers per family — config as data. `cheap` is for gate-style\n * calls, `extract` for structured extraction (and prose writing), `search`\n * for web-search-enabled calls. */\nexport const MODEL_TIERS: Record<ProviderFamily, { cheap: string; extract: string; search: string }> = {\n claude: { cheap: \"claude-haiku-4-5\", extract: \"claude-sonnet-5\", search: \"claude-sonnet-5\" },\n openai: { cheap: \"gpt-5-nano\", extract: \"gpt-5-mini\", search: \"gpt-5-search-api\" },\n};\n\n/** A forced structured tool call: schema in, validated object out. */\nexport interface ExtractCall {\n system: string;\n user: string;\n tool: { name: string; description?: string; parameters: Record<string, unknown> };\n model?: string;\n maxTokens?: number;\n}\n\n/** A web-search-enabled call: the model searches and returns text (caller tolerant-parses). */\nexport interface SearchCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\n/** A plain text-generation call (no tools, no search) — e.g. digest prose. */\nexport interface WriteCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\nexport interface Provider {\n id: ProviderFamily;\n /** The underlying @odla-ai/ai client — escape hatch for advanced use. */\n ai: Ai;\n extract<T = unknown>(c: ExtractCall): Promise<{ value: T; usage: Usage }>;\n search(c: SearchCall): Promise<{ text: string; usage: Usage }>;\n write(c: WriteCall): Promise<{ text: string; usage: Usage }>;\n}\n\nexport interface LlmConfig {\n provider: ProviderFamily;\n anthropicKey?: string;\n openaiKey?: string;\n /** Inject a pre-built Ai (tests, custom catalog); keys are ignored when set. */\n ai?: Ai;\n}\n\nfunction toUsage(u: { inputTokens: number; outputTokens: number }): Usage {\n return { calls: 1, inputTokens: u.inputTokens, outputTokens: u.outputTokens };\n}\n\n/** The library primitive: an explicit config in, the three-verb seam out. */\nexport function createProvider(cfg: LlmConfig): Provider {\n const tiers = MODEL_TIERS[cfg.provider];\n const ai = cfg.ai ?? init({ keys: { anthropic: cfg.anthropicKey, openai: cfg.openaiKey } });\n return {\n id: cfg.provider,\n ai,\n async extract<T = unknown>(c: ExtractCall) {\n const { value, usage } = await ai.extract<T>({\n model: c.model ?? tiers.extract,\n system: c.system,\n user: c.user,\n tool: c.tool,\n maxTokens: c.maxTokens,\n });\n return { value, usage: toUsage(usage) };\n },\n async search(c: SearchCall) {\n const { text, usage } = await ai.search({\n model: c.model ?? tiers.search,\n system: c.system,\n user: c.user,\n maxTokens: c.maxTokens,\n });\n return { text, usage: toUsage(usage) };\n },\n async write(c: WriteCall) {\n const res = await ai.chat({\n model: c.model ?? tiers.extract,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n maxTokens: c.maxTokens ?? 2048,\n });\n return { text: extractText(res.content), usage: toUsage(res.usage) };\n },\n };\n}\n\n/** The env vars the provider factory reads — a structural slice, not the\n * worker Env, so any host (worker, script, test) can supply plain strings. */\nexport interface LlmEnv {\n LLM_PROVIDER?: string; // \"claude\" | \"openai\"\n ANTHROPIC_API_KEY?: string;\n OPENAI_API_KEY?: string;\n}\n\n/** Build the configured provider from host env, failing before use when its key is missing. */\nexport function getProvider(env: LlmEnv): Provider {\n const which: ProviderFamily = (env.LLM_PROVIDER || \"claude\").toLowerCase() === \"openai\" ? \"openai\" : \"claude\";\n if (which === \"openai\" && !env.OPENAI_API_KEY) throw new Error(\"LLM_PROVIDER=openai but OPENAI_API_KEY is unset\");\n if (which === \"claude\" && !env.ANTHROPIC_API_KEY) throw new Error(\"LLM_PROVIDER=claude but ANTHROPIC_API_KEY is unset\");\n return createProvider({ provider: which, anthropicKey: env.ANTHROPIC_API_KEY, openaiKey: env.OPENAI_API_KEY });\n}\n","/**\n * Tolerant JSON extraction from LLM prose — web-search models return JSON inside\n * explanatory text. Ported from agnacl services/enrichment.ts.\n */\n\nexport function extractJson<T = unknown>(text: string): T | null {\n const m = text.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]) as T;\n } catch {\n return null;\n }\n}\n\n/** Tolerantly parse the first JSON-array-shaped span in model prose, or return an empty array. */\nexport function extractJsonArray<T = unknown>(text: string): T[] {\n const m = text.match(/\\[[\\s\\S]*\\]/);\n if (!m) return [];\n try {\n const v = JSON.parse(m[0]);\n return Array.isArray(v) ? (v as T[]) : [];\n } catch {\n return [];\n }\n}\n","/**\n * Dependency-free HTML → text extraction (Workers-isolate friendly: no DOM/jsdom).\n * Ported from agnacl services/content-extractor.ts.\n */\n\nconst MAX_CHARS = 8000;\n\n/** Strip scripts/styles/tags and collapse whitespace; cap length. */\nexport function htmlToText(html: string, maxChars = MAX_CHARS): string {\n let t = html;\n t = t.replace(/<script[\\s\\S]*?<\\/script>/gi, \" \");\n t = t.replace(/<style[\\s\\S]*?<\\/style>/gi, \" \");\n t = t.replace(/<!--[\\s\\S]*?-->/g, \" \");\n t = t.replace(/<\\/(p|div|section|article|li|h[1-6]|br)>/gi, \"\\n\");\n t = t.replace(/<[^>]+>/g, \" \");\n t = decodeEntities(t);\n t = t.replace(/[ \\t\\f\\r]+/g, \" \").replace(/\\n\\s*\\n\\s*/g, \"\\n\\n\").trim();\n return t.slice(0, maxChars);\n}\n\nfunction decodeEntities(s: string): string {\n return s\n .replace(/ /gi, \" \")\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\")\n .replace(/&#(\\d+);/g, (_, d) => String.fromCharCode(Number(d)))\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => String.fromCharCode(parseInt(h, 16)));\n}\n\n/** Fetch a URL and return readable text. Best-effort: returns \"\" on any failure. */\nexport async function fetchText(url: string, maxChars = MAX_CHARS): Promise<string> {\n try {\n const res = await fetch(url, {\n headers: {\n \"user-agent\": \"odla-kg/0.1 (+https://github.com/cory/odla-kg)\",\n accept: \"text/html,application/xhtml+xml\",\n },\n });\n if (!res.ok) return \"\";\n const ct = res.headers.get(\"content-type\") || \"\";\n const body = await res.text();\n if (ct.includes(\"html\") || /<\\w+[\\s>]/.test(body.slice(0, 200))) {\n return htmlToText(body, maxChars);\n }\n return body.slice(0, maxChars);\n } catch {\n return \"\";\n }\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { htmlToText } from \"../util/content\";\n\n/** Dependency-free RSS/Atom parsing, ported from agnacl services/rss.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\n\nfunction extractTag(xml: string, tag: string): string {\n const cdata = xml.match(new RegExp(`<${tag}[^>]*>\\\\s*<!\\\\[CDATA\\\\[([\\\\s\\\\S]*?)\\\\]\\\\]>\\\\s*</${tag}>`, \"i\"));\n if (cdata) return cdata[1]!.trim();\n const m = xml.match(new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction extractAttr(xml: string, tag: string, attr: string): string {\n const m = xml.match(new RegExp(`<${tag}[^>]*\\\\s${attr}=[\"']([^\"']*)[\"']`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction splitItems(xml: string): string[] {\n return xml.match(/<item[\\s>][\\s\\S]*?<\\/item>/gi) || xml.match(/<entry[\\s>][\\s\\S]*?<\\/entry>/gi) || [];\n}\n\ninterface RssCfg {\n url: string;\n name: string;\n watch?: string | null;\n}\n\nexport const rssConnector: SourceConnector<RssCfg> = {\n id: \"rss\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): RssCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"rss connector requires a `url`\");\n return { url: r.url, name: String(r.name ?? r.url), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n let xml: string;\n try {\n const res = await fetch(cfg.url, { headers: { \"User-Agent\": UA } });\n if (!res.ok) {\n if (res.status === 429 || res.status === 403 || res.status >= 500) return [];\n throw new Error(`feed ${cfg.url} → ${res.status}`);\n }\n xml = await res.text();\n } catch (e) {\n console.log(`rss fetch failed (non-blocking): ${cfg.url}: ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n for (const raw of splitItems(xml)) {\n const title = extractTag(raw, \"title\");\n let url = extractTag(raw, \"link\") || extractAttr(raw, \"link\", \"href\");\n if (!title || !url) continue;\n url = url.trim();\n const date =\n extractTag(raw, \"pubDate\") || extractTag(raw, \"published\") || extractTag(raw, \"updated\") || undefined;\n const content =\n extractTag(raw, \"content:encoded\") ||\n extractTag(raw, \"content\") ||\n extractTag(raw, \"description\") ||\n extractTag(raw, \"summary\") ||\n \"\";\n let text = htmlToText(content);\n // Thin feed body → fetch the article page.\n if (text.length < 200) {\n const page = await ctx.fetchText(url);\n if (page.length > text.length) text = page;\n }\n out.push({ url, title, date, sourceName: cfg.name, sourceKind: \"rss\", watch: cfg.watch, text });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector } from \"./types\";\nimport { fetchText } from \"../util/content\";\n\n/** Fetch one URL → a text-bearing RawItem. Used both directly (/trigger) and as a connector. */\nexport async function fetchUrl(url: string): Promise<RawItem> {\n const text = await fetchText(url);\n const firstLine = text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\";\n let host = url;\n try {\n host = new URL(url).hostname;\n } catch {\n /* keep raw url as the source label */\n }\n return { url, title: (firstLine || url).slice(0, 200).trim(), sourceName: host, sourceKind: \"url\", text };\n}\n\ninterface UrlCfg {\n url: string;\n watch?: string | null;\n}\n\nexport const urlConnector: SourceConnector<UrlCfg> = {\n id: \"url\",\n capabilities: { fetch: true, structured: false },\n parseConfig(raw): UrlCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"url connector requires a `url`\");\n return { url: r.url, watch: (r.watch as string | null) ?? null };\n },\n async fetch(cfg): Promise<RawItem[]> {\n const item = await fetchUrl(cfg.url);\n if (cfg.watch !== undefined) item.watch = cfg.watch;\n return [item];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/** GDELT DOC 2.0 standing-query connector (text). Ported from agnacl services/gdelt.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\nconst MAX_ITEMS = 15;\n\ninterface GdeltCfg {\n query: string;\n name: string;\n watch?: string | null;\n}\n\nfunction isoFromSeendate(s: string): string | undefined {\n const m = s.match(/^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})Z?$/);\n return m ? `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}Z` : undefined;\n}\n\nexport const gdeltConnector: SourceConnector<GdeltCfg> = {\n id: \"gdelt\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): GdeltCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.query !== \"string\" || !r.query) throw new Error(\"gdelt connector requires a `query`\");\n return { query: r.query, name: String(r.name ?? `GDELT: ${r.query}`), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const api =\n `https://api.gdeltproject.org/api/v2/doc/doc?query=${encodeURIComponent(cfg.query)}` +\n `&mode=artlist&format=json&maxrecords=40×pan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n // GDELT is a best-effort external source; without a deadline a slow or wedged endpoint hangs\n // the whole workflow run. Bound it and let the catch below treat a timeout like any other miss.\n const res = await fetch(api, { headers: { \"User-Agent\": UA }, signal: AbortSignal.timeout(15_000) });\n if (!res.ok) return [];\n const text = await res.text(); // GDELT sometimes returns text errors with 200\n try {\n articles = (JSON.parse(text) as { articles?: typeof articles }).articles ?? [];\n } catch {\n return [];\n }\n } catch (e) {\n console.log(`gdelt fetch failed (non-blocking): ${e}`);\n return [];\n }\n\n const seen = new Set<string>();\n const picked = articles\n .filter((a) => a?.url && a?.title && a.language === \"English\" && !seen.has(a.url!) && (seen.add(a.url!), true))\n .slice(0, MAX_ITEMS);\n\n const out: RawItem[] = [];\n for (const a of picked) {\n const body = await ctx.fetchText(a.url!); // GDELT gives no body; fetch the article\n if (body.length < 200) continue;\n out.push({\n url: a.url!,\n title: a.title!,\n date: isoFromSeendate(a.seendate ?? \"\"),\n sourceName: cfg.name,\n sourceKind: \"gdelt\",\n watch: cfg.watch,\n text: body,\n });\n }\n return out;\n },\n};\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify } from \"../util/identity\";\n\n/**\n * FRED economic indicators — a STRUCTURED connector: it maps series directly to\n * graph nodes (indicator + region) and emits RawItem.fragment, so the ingest\n * workflow writes it without any LLM extraction. Ported from agnacl services/fred.ts.\n */\ninterface FredCfg {\n series: Array<{ id: string; label: string; unit?: string }>;\n region?: string;\n regionName?: string;\n}\n\nasync function fetchSeries(\n seriesId: string,\n apiKey: string,\n start: string,\n): Promise<Array<{ date: string; value: number }>> {\n const url =\n `https://api.stlouisfed.org/fred/series/observations?series_id=${encodeURIComponent(seriesId)}` +\n `&api_key=${encodeURIComponent(apiKey)}&file_type=json&observation_start=${encodeURIComponent(start)}`;\n try {\n const res = await fetch(url);\n if (!res.ok) return [];\n const data = (await res.json()) as { observations?: Array<{ date?: string; value?: string }> };\n const out: Array<{ date: string; value: number }> = [];\n for (const o of data.observations ?? []) {\n if (!o?.date || o.value === undefined || o.value === \".\") continue;\n const v = Number(o.value);\n if (Number.isFinite(v)) out.push({ date: o.date, value: v });\n }\n return out;\n } catch (e) {\n console.log(`fred fetch failed for ${seriesId} (non-blocking): ${e}`);\n return [];\n }\n}\n\nexport const fredConnector: SourceConnector<FredCfg> = {\n id: \"fred\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): FredCfg {\n const r = raw as Record<string, unknown>;\n const series = Array.isArray(r.series) ? (r.series as FredCfg[\"series\"]) : [];\n if (!series.length) throw new Error(\"fred connector requires a non-empty `series`\");\n return { series, region: r.region as string | undefined, regionName: r.regionName as string | undefined };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const apiKey = ctx.secret(\"FRED_API_KEY\");\n if (!apiKey) {\n console.log(\"fred: FRED_API_KEY unset — skipping\");\n return [];\n }\n const start = new Date(ctx.now.getTime() - 400 * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const out: RawItem[] = [];\n\n for (const s of cfg.series) {\n const obs = await fetchSeries(s.id, apiKey, start);\n if (!obs.length) continue;\n const latest = obs[obs.length - 1]!;\n const prior = obs.length > 1 ? obs[obs.length - 2] : undefined;\n\n const attrs: Record<string, Value> = { label: s.label, latest: String(latest.value), as_of: latest.date };\n if (prior) attrs.prior = String(prior.value);\n if (s.unit) attrs.unit = s.unit;\n\n const fragment: GraphFragment = { nodes: [{ type: \"indicator\", key: s.id, attrs }], edges: [] };\n if (cfg.region) {\n const rslug = slugify(cfg.region);\n if (rslug) {\n fragment.nodes.push({ type: \"region\", key: rslug, attrs: { name: cfg.regionName ?? cfg.region } });\n fragment.edges.push({ from: { type: \"region\", key: rslug }, to: { type: \"indicator\", key: s.id }, label: \"has_indicator\" });\n }\n }\n\n out.push({\n url: `https://fred.stlouisfed.org/series/${s.id}#${latest.date}`,\n title: `${s.label}: ${latest.value} (${latest.date})`,\n date: latest.date,\n sourceName: \"FRED\",\n sourceKind: \"fred\",\n fragment,\n });\n }\n return out;\n },\n};\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify, isLikelyPersonName } from \"../util/identity\";\n\n/**\n * SEC EDGAR Form D — a STRUCTURED connector (RawItem.fragment, no LLM). A lean\n * port of agnacl services/edgar.ts: first page of Form D filings for the given\n * states, each mapped to company + funding_round (+ people, region). SEC\n * requires an identifying User-Agent (env.EDGAR_USER_AGENT) on every request.\n */\nconst SEARCH_BASE = \"https://efts.sec.gov/LATEST/search-index\";\nconst MAX_FILINGS = 10;\n\ninterface EdgarCfg {\n locationCodes: string[];\n cityFilter?: string[];\n days?: number;\n}\n\nconst cikTrim = (cik: string): string => cik.replace(/^0+/, \"\") || cik;\nconst indexUrl = (cik: string, adsh: string): string =>\n `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/${adsh}-index.htm`;\n\nfunction tagText(scope: string, tag: string): string {\n const m = scope.match(new RegExp(`<${tag}[^>]*>\\\\s*([\\\\s\\\\S]*?)\\\\s*</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\ninterface EdgarHit {\n _source?: {\n adsh?: string;\n ciks?: string[];\n display_names?: string[];\n file_date?: string;\n biz_states?: string[];\n biz_locations?: string[];\n };\n}\n\nexport const edgarConnector: SourceConnector<EdgarCfg> = {\n id: \"edgar\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): EdgarCfg {\n const r = raw as Record<string, unknown>;\n const codes = Array.isArray(r.locationCodes) ? (r.locationCodes as string[]) : [];\n if (!codes.length) throw new Error('edgar connector requires `locationCodes` (e.g. [\"UT\"])');\n return {\n locationCodes: codes,\n cityFilter: Array.isArray(r.cityFilter) ? (r.cityFilter as string[]) : undefined,\n days: typeof r.days === \"number\" ? r.days : 7,\n };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const ua = ctx.secret(\"EDGAR_USER_AGENT\");\n if (!ua) {\n console.log(\"edgar: EDGAR_USER_AGENT unset — skipping\");\n return [];\n }\n const enddt = ctx.now.toISOString().slice(0, 10);\n const startdt = new Date(ctx.now.getTime() - (cfg.days ?? 7) * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const searchUrl =\n `${SEARCH_BASE}?q=&forms=D&locationCodes=${encodeURIComponent(cfg.locationCodes.join(\",\"))}` +\n `&startdt=${startdt}&enddt=${enddt}`;\n\n let hits: EdgarHit[] = [];\n try {\n const res = await fetch(searchUrl, { headers: { \"User-Agent\": ua } });\n if (!res.ok) return [];\n hits = ((await res.json()) as { hits?: { hits?: EdgarHit[] } }).hits?.hits ?? [];\n } catch (e) {\n console.log(`edgar search failed (non-blocking): ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n const seen = new Set<string>();\n for (const hit of hits.slice(0, MAX_FILINGS)) {\n const src = hit._source ?? {};\n const adsh = src.adsh ?? \"\";\n if (!adsh || seen.has(adsh)) continue;\n seen.add(adsh);\n const cik = src.ciks?.[0] ?? \"\";\n const name = (src.display_names?.[0] ?? \"\").replace(/\\s*\\(CIK\\s*\\d+\\)\\s*$/i, \"\").trim();\n if (!name || !cik) continue;\n\n const loc = src.biz_locations?.[0] ?? \"\";\n let city = loc.trim();\n let state = src.biz_states?.[0] ?? \"\";\n const comma = loc.lastIndexOf(\",\");\n if (comma >= 0) {\n city = loc.slice(0, comma).trim();\n state = loc.slice(comma + 1).trim() || state;\n }\n if (cfg.cityFilter?.length && !cfg.cityFilter.some((c) => c.toLowerCase() === city.toLowerCase())) continue;\n\n const url = indexUrl(cik, adsh);\n const fileDate = src.file_date ?? \"\";\n\n // Filing detail (offering amount, industry, related persons)\n let industry = \"\";\n let offering = \"\";\n const persons: Array<{ name: string; roles: string[] }> = [];\n try {\n const detailUrl = `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/primary_doc.xml`;\n const dres = await fetch(detailUrl, { headers: { \"User-Agent\": ua } });\n if (dres.ok) {\n const xml = await dres.text();\n industry = tagText(xml, \"industryGroupType\");\n offering = tagText(xml, \"totalOfferingAmount\");\n for (const block of xml.match(/<relatedPersonInfo>[\\s\\S]*?<\\/relatedPersonInfo>/gi) ?? []) {\n const pname = [tagText(block, \"firstName\"), tagText(block, \"lastName\")]\n .filter((p) => p && p !== \"-\")\n .join(\" \")\n .trim();\n if (!pname) continue;\n const roles: string[] = [];\n const relRe = /<relationship>\\s*([\\s\\S]*?)\\s*<\\/relationship>/gi;\n let m: RegExpExecArray | null;\n while ((m = relRe.exec(block)) !== null) {\n const r = m[1]!.trim();\n if (r && !roles.includes(r)) roles.push(r);\n }\n persons.push({ name: pname, roles });\n }\n }\n } catch {\n /* fall back to search-hit metadata */\n }\n\n const cslug = slugify(name);\n if (!cslug) continue;\n const f: GraphFragment = { nodes: [], edges: [] };\n const cattrs: Record<string, Value> = { name };\n if (city) cattrs.city = city;\n if (state) cattrs.region = state;\n if (industry) cattrs.sector = industry;\n if (fileDate) {\n cattrs.first_seen = fileDate;\n cattrs.last_seen = fileDate;\n }\n f.nodes.push({ type: \"company\", key: cslug, attrs: cattrs });\n\n if (state) {\n const rslug = slugify(state);\n if (rslug) {\n f.nodes.push({ type: \"region\", key: rslug, attrs: { name: state } });\n f.edges.push({ from: { type: \"company\", key: cslug }, to: { type: \"region\", key: rslug }, label: \"located_in\" });\n }\n }\n\n const rkey = `${cslug}:form-d:${fileDate}`;\n const rattrs: Record<string, Value> = { round: \"Form D\", source_url: url };\n if (offering) rattrs.amount = offering;\n if (fileDate) rattrs.date = fileDate;\n f.nodes.push({ type: \"funding_round\", key: rkey, attrs: rattrs });\n f.edges.push({ from: { type: \"funding_round\", key: rkey }, to: { type: \"company\", key: cslug }, label: \"round_of\" });\n\n for (const p of persons) {\n if (!isLikelyPersonName(p.name)) continue;\n const pslug = slugify(p.name);\n if (!pslug) continue;\n const pattrs: Record<string, Value> = { name: p.name };\n if (p.roles.length) pattrs.role = p.roles.join(\", \");\n f.nodes.push({ type: \"person\", key: pslug, attrs: pattrs });\n f.edges.push({ from: { type: \"person\", key: pslug }, to: { type: \"company\", key: cslug }, label: \"works_at\" });\n }\n\n out.push({\n url,\n title: `Form D: ${name}${offering ? ` — ${offering}` : \"\"}`,\n date: fileDate || undefined,\n sourceName: \"SEC EDGAR\",\n sourceKind: \"edgar\",\n fragment: f,\n });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/**\n * TEMPLATE — a government / open-data connector.\n *\n * This exists to demonstrate the extensibility contract: adding a new source is\n * a self-contained module implementing SourceConnector + ONE line in the\n * registry + config entries in config/sources.ts. The cron, ingest workflow,\n * extractor, and graph writer are untouched — they only ever speak RawItem /\n * GraphFragment.\n *\n * To make it real, fetch your dataset and return either:\n * - text-bearing RawItems ({ ...meta, text }) → the LLM extracts a graph\n * - fragment-bearing RawItems ({ ...meta, fragment }) → straight to the writer\n * (map records to ontology nodes/edges yourself — see fred.ts / edgar.ts).\n * Returns [] until implemented, so wiring it up is harmless.\n */\ninterface GovDataCfg {\n dataset: string;\n watch?: string | null;\n}\n\nexport const govDataConnector: SourceConnector<GovDataCfg> = {\n id: \"gov-data\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): GovDataCfg {\n const r = raw as Record<string, unknown>;\n return { dataset: String(r.dataset ?? \"\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, _ctx: ConnectorContext): Promise<RawItem[]> {\n console.log(`gov-data: template connector for dataset \"${cfg.dataset}\" — implement fetch() to emit RawItems`);\n return [];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { urlHash } from \"../util/identity\";\n\n/**\n * LLM-native web search — a DISCOVERY connector. Each standing query is run\n * through the provider's web-search tool; the prose result becomes a text\n * RawItem that flows through the normal extractor (so discovered companies land\n * in the graph the same way feed items do). Discovery-only: fetch() is a no-op.\n */\ninterface WebSearchCfg {\n queries: string[];\n name: string;\n watch?: string | null;\n}\n\nconst SWEEP_SYSTEM =\n \"You are a research scout building a knowledge graph of entrepreneurs, startups, and business trends. Search the web and report concrete, real companies and the people leading them, with specifics (what they do, where, who founded them). Tie every claim to something you actually found — never invent names, numbers, or companies. If a search turns up nothing solid, say so plainly.\";\n\nexport const webSearchConnector: SourceConnector<WebSearchCfg> = {\n id: \"web-search\",\n capabilities: { fetch: false, discover: true, structured: false },\n\n parseConfig(raw): WebSearchCfg {\n const r = raw as Record<string, unknown>;\n const queries = Array.isArray(r.queries)\n ? (r.queries.filter((q) => typeof q === \"string\") as string[])\n : typeof r.query === \"string\"\n ? [r.query]\n : [];\n if (!queries.length) throw new Error(\"web-search connector requires `queries` (or a `query`)\");\n return { queries, name: String(r.name ?? \"web-search\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(): Promise<RawItem[]> {\n return []; // discovery-only\n },\n\n async discover(cfg, ctx: ConnectorContext): Promise<RawItem[]> {\n const provider = ctx.provider();\n const today = ctx.now.toISOString().slice(0, 10);\n const out: RawItem[] = [];\n for (const q of cfg.queries) {\n const { text } = await provider.search({\n system: SWEEP_SYSTEM,\n user: `Search task: ${q}\\nToday's date: ${today}\\n\\nReport what you find, naming specific companies and the real people leading them.`,\n });\n if (text.trim().length < 80) continue;\n out.push({\n url: `search://web-search/${urlHash(q)}#${today}`, // synthetic; changes daily so re-sweeps aren't deduped away\n title: q,\n date: today,\n sourceName: cfg.name,\n sourceKind: \"web-search\",\n watch: cfg.watch,\n text,\n });\n }\n return out;\n },\n};\n","import type { SourceConnector, ConnectorContext } from \"./types\";\nimport type { Ontology } from \"../ontology\";\nimport type { Provider } from \"../llm/provider\";\nimport { fetchText } from \"../util/content\";\nimport { rssConnector } from \"./rss\";\nimport { urlConnector } from \"./url\";\nimport { gdeltConnector } from \"./gdelt\";\nimport { fredConnector } from \"./fred\";\nimport { edgarConnector } from \"./edgar\";\nimport { govDataConnector } from \"./govdata\";\nimport { webSearchConnector } from \"./web-search\";\n\n/**\n * The connector registry. Adding a source (RSS, a government feed, a new API)\n * is: implement SourceConnector in a module, add one line here, add config\n * entries to config/sources.ts. The cron, workflow, extractor, and writer are\n * untouched — they only speak RawItem / GraphFragment.\n */\nconst REGISTRY = new Map<string, SourceConnector>([\n [rssConnector.id, rssConnector as SourceConnector],\n [urlConnector.id, urlConnector as SourceConnector],\n [gdeltConnector.id, gdeltConnector as SourceConnector],\n [fredConnector.id, fredConnector as SourceConnector], // structured (RawItem.fragment)\n [edgarConnector.id, edgarConnector as SourceConnector], // structured (RawItem.fragment)\n [webSearchConnector.id, webSearchConnector as SourceConnector], // discovery (web search)\n [govDataConnector.id, govDataConnector as SourceConnector], // template — zero core change to add\n]);\n\n/** Resolve a built-in source connector by its stable registry id. */\nexport function getConnector(id: string): SourceConnector | undefined {\n return REGISTRY.get(id);\n}\n\n/** List registered connector ids in deterministic insertion order. */\nexport function connectorIds(): string[] {\n return [...REGISTRY.keys()];\n}\n\n/** Everything a ConnectorContext needs, supplied by the host (worker, script, test). */\nexport interface ContextConfig {\n ontology: Ontology;\n provider: () => Provider;\n /** Named secrets (API keys, user agents) — e.g. the worker Env, or a plain object. */\n secrets?: Record<string, string | undefined>;\n now?: Date;\n}\n\n/** Create a host-supplied connector context with lazy provider construction. */\nexport function makeContext(cfg: ContextConfig): ConnectorContext {\n let provider: Provider | undefined; // lazy: only discovery connectors need it\n return {\n now: cfg.now ?? new Date(),\n fetchText: (u) => fetchText(u),\n secret: (name) => cfg.secrets?.[name],\n ontology: cfg.ontology,\n provider: () => (provider ??= cfg.provider()),\n };\n}\n","import type { ConnectorJob } from \"./connectors/types\";\n\n/**\n * Sources are DATA, not code. Each entry names a connector (`kind`) + its config\n * + scheduling. Add a feed → add an entry; no redeploy of the pipeline. P5 moves\n * this into KV behind a /config admin endpoint so edits don't need a deploy at all.\n */\nexport interface SourceEntry {\n kind: string; // connector id (registry key)\n name: string;\n enabled?: boolean; // default true\n cadenceHours?: number; // default 1 — runs when UTC hour % cadence === 0\n watch?: string | null;\n // connector-specific config (rss: url; more in P3):\n url?: string;\n query?: string;\n [key: string]: unknown;\n}\n\n/** Return source entries not explicitly disabled, preserving configured order. */\nexport function enabledSources(sources: SourceEntry[]): SourceEntry[] {\n return sources.filter((s) => s.enabled !== false);\n}\n\nfunction toJob(s: SourceEntry): ConnectorJob {\n return { kind: s.kind, name: s.name, watch: s.watch ?? null, cfg: s };\n}\n\n/** Enabled sources whose cadence lands on this UTC hour. */\nexport function jobsForHour(sources: SourceEntry[], hour: number): ConnectorJob[] {\n return enabledSources(sources)\n .filter((s) => hour % Math.max(1, Math.floor(s.cadenceHours ?? 1)) === 0)\n .map(toJob);\n}\n\n/** Every enabled source, ignoring cadence (manual /trigger?kind=sources). */\nexport function allJobs(sources: SourceEntry[]): ConnectorJob[] {\n return enabledSources(sources).map(toJob);\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceEntry } from \"../sources\";\nimport { urlHash } from \"../util/identity\";\n\nconst SEEN_TTL_S = 120 * 24 * 60 * 60; // 120 days\nconst ENRICHED_TTL_S = 30 * 24 * 60 * 60; // 30-day enrichment cooldown\nconst TEMPORAL_TTL_S = 120 * 24 * 60 * 60; // dated temporal-report history copies\n\n/**\n * The slice of a KV store the pipeline needs. Cloudflare's KVNamespace\n * satisfies it structurally; tests pass an in-memory map. Keeping the library\n * off Workers types is what lets it publish as a plain isomorphic package.\n */\nexport interface KVLike {\n get(key: string): Promise<string | null>;\n put(key: string, value: string, opts?: { expirationTtl?: number }): Promise<void>;\n}\n\n/**\n * Operational state in KV (never the graph itself — that lives in odla-db).\n * Ported/trimmed from agnacl services/kv.ts: seen-URL dedup + lastrun stamps.\n */\nexport class Store {\n constructor(private kv: KVLike) {}\n\n private seenKey(url: string): string {\n return `seen:${urlHash(url)}`;\n }\n\n async hasSeen(url: string): Promise<boolean> {\n return (await this.kv.get(this.seenKey(url))) !== null;\n }\n\n async markSeen(url: string): Promise<void> {\n await this.kv.put(this.seenKey(url), \"1\", { expirationTtl: SEEN_TTL_S });\n }\n\n /** Drop items whose URL we've already processed (checked BEFORE any LLM work). */\n async filterNew(items: RawItem[]): Promise<RawItem[]> {\n const flags = await Promise.all(items.map((it) => this.hasSeen(it.url)));\n return items.filter((_, i) => !flags[i]);\n }\n\n async setLastRun(kind: string, at = new Date().toISOString()): Promise<void> {\n await this.kv.put(`lastrun:${kind}`, at);\n }\n\n async getLastRun(kind: string): Promise<string | null> {\n return this.kv.get(`lastrun:${kind}`);\n }\n\n /** Per-entity enrichment cooldown so we don't re-search the same node constantly. */\n async isEnrichCooling(key: string): Promise<boolean> {\n return (await this.kv.get(`enriched:${key}`)) !== null;\n }\n\n async markEnriched(key: string): Promise<void> {\n await this.kv.put(`enriched:${key}`, new Date().toISOString(), { expirationTtl: ENRICHED_TTL_S });\n }\n\n /** Runtime source config (edit without redeploy). null = fall back to the static SOURCES. */\n async getConfigSources(): Promise<SourceEntry[] | null> {\n const raw = await this.kv.get(\"config:sources\");\n if (!raw) return null;\n try {\n const v = JSON.parse(raw);\n return Array.isArray(v) ? (v as SourceEntry[]) : null;\n } catch {\n return null;\n }\n }\n\n async setConfigSources(list: SourceEntry[]): Promise<void> {\n await this.kv.put(\"config:sources\", JSON.stringify(list));\n }\n\n /** Cache the latest temporal report (`temporal:latest`) plus a dated history copy. */\n async setTemporalReport(report: unknown, date = new Date().toISOString().slice(0, 10)): Promise<void> {\n const raw = JSON.stringify(report);\n await this.kv.put(\"temporal:latest\", raw);\n await this.kv.put(`temporal:${date}`, raw, { expirationTtl: TEMPORAL_TTL_S });\n }\n\n /** The cached latest temporal report, or null when absent/unparseable. */\n async getTemporalReport<T = unknown>(): Promise<T | null> {\n const raw = await this.kv.get(\"temporal:latest\");\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n }\n}\n","/**\n * Cloudflare Workflow hardening — ported verbatim from agnacl\n * (utils/workflow-helpers.ts). resilientStep adds structured error logging\n * without changing retry semantics; the step configs replace the engine's slow\n * default retry with fast exponential backoff.\n */\n\nexport async function resilientStep<T>(stepName: string, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const stack = err instanceof Error ? err.stack : undefined;\n console.error(`[STEP_ERROR] ${stepName}: ${msg}`);\n if (stack) console.error(`[STEP_STACK] ${stepName}: ${stack}`);\n throw err;\n }\n}\n\n/** I/O-bound steps: KV reads, feed fetches. */\nexport const IO_STEP_CONFIG = {\n retries: { limit: 3, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"2 minutes\" as const,\n};\n\n/** Graph write steps. */\nexport const COMMIT_STEP_CONFIG = {\n retries: { limit: 3, delay: \"3 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"90 seconds\" as const,\n};\n\n/** LLM generation steps (can be slow). */\nexport const LLM_STEP_CONFIG = {\n retries: { limit: 2, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"3 minutes\" as const,\n};\n\n/** Retry/timeout shape of the step configs above. */\nexport interface StepConfig {\n retries?: { limit: number; delay: string; backoff?: string };\n timeout?: string;\n}\n\n/**\n * Structural view of Cloudflare's WorkflowStep. WorkflowStep.do constrains its\n * result to Rpc.Serializable<T>, which cannot terminate (TS2589) on the\n * recursive Value type now that the @odla/* workspace packages give\n * @odla-ai/db's re-exports real definitions. A live WorkflowStep satisfies\n * this shape at runtime; widen through asStepLike at the workflow boundary.\n */\nexport interface StepLike {\n do<T>(name: string, fn: () => Promise<T>): Promise<T>;\n do<T>(name: string, config: StepConfig, fn: () => Promise<T>): Promise<T>;\n}\n\n/** Widen a WorkflowStep to StepLike (type-level only, no runtime cost). */\nexport function asStepLike(step: unknown): StepLike {\n return step as StepLike;\n}\n","import { LLM_STEP_CONFIG, type StepLike, resilientStep } from \"../workflow/helpers\";\nimport type { ExtractResult, RawItem } from \"../types\";\nimport type { AdminDb } from \"@odla-ai/db\";\nimport type { Store } from \"../state/kv\";\nimport type { Provider } from \"../llm/provider\";\nimport type { Ontology } from \"../ontology\";\nimport { writeFragment } from \"../graph/writer\";\nimport { canonicalizeFragment } from \"../graph/resolve\";\nimport { urlHash } from \"../util/identity\";\n\n/** Everything the shared ingest loop needs, supplied by the host. The\n * extractor is the ontology's domain craft (e.g. the OSINT extractor); the\n * library stays ontology-agnostic. */\nexport interface PipelineDeps {\n db: AdminDb;\n provider: Provider;\n ontology: Ontology;\n extract: (provider: Provider, item: RawItem) => Promise<ExtractResult | null>;\n store: Store;\n}\n\n/**\n * Shared per-item processing: each RawItem is its own step (non-blocking,\n * replay-cached). Structured items (fragment) skip the LLM; text items extract.\n * `resolve` FTS-canonicalizes new nodes against existing ones (discovery path).\n * Used by both the ingest (fetch) and discover (web-search) workflows.\n */\nexport async function ingestFresh(\n step: StepLike,\n deps: PipelineDeps,\n fresh: RawItem[],\n kind: string,\n resolve = false,\n): Promise<void> {\n const { db, provider, ontology, extract, store } = deps;\n if (fresh.length === 0) {\n await store.setLastRun(kind);\n return;\n }\n\n for (const item of fresh) {\n const id = urlHash(item.url);\n await step.do(`item-${id}`, LLM_STEP_CONFIG, () =>\n resilientStep(`item-${id}`, async () => {\n try {\n let fragment = item.fragment;\n if (!fragment) {\n const r = await extract(provider, item);\n if (!r) {\n await store.markSeen(item.url);\n return { skipped: true };\n }\n fragment = r.fragment;\n }\n if (resolve) fragment = await canonicalizeFragment(db, fragment, ontology);\n const { txId, ops } = await writeFragment(db, fragment, ontology, `ingest:${id}:v1`);\n await store.markSeen(item.url);\n return { txId, ops };\n } catch (e) {\n console.log(`ingest item failed (non-blocking): ${item.url}: ${e instanceof Error ? e.message : e}`);\n return { error: true };\n }\n }),\n );\n }\n\n await step.do(`stamp-${kind}`, () =>\n resilientStep(`stamp-${kind}`, async () => {\n await store.setLastRun(kind);\n return { ok: true };\n }),\n );\n}\n"],"mappings":"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","names":["UA"]}
|