@odla-ai/kg 0.2.0 → 0.2.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cory Ondrejka
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,14 +1,19 @@
1
1
  # @odla-ai/kg
2
2
 
3
+ > ⚠️ **Early access — pre-1.0.** Agents work from bounded runbooks; humans
4
+ > approve credentials, production changes, releases, and merges. APIs and exact
5
+ > package availability can change. Review the documented guarantees and
6
+ > limitations; this software is MIT-licensed and provided without warranty.
7
+
3
8
  An ontology-driven **knowledge-graph builder**. Pluggable source connectors pull
4
9
  from the web and data feeds, an LLM extracts entities + relationships against a
5
10
  **config-as-data ontology**, and a provenance-preserving graph writer persists
6
11
  the result into [odla-db](https://www.npmjs.com/package/@odla-ai/db).
7
12
 
8
13
  The library is host-agnostic: it runs in Cloudflare Workers, Node, or tests —
9
- the host supplies the database client, LLM keys, and a KV-like store. For a
10
- complete deployable consumer (workflows, hourly cron, HTTP API, viewer UI), see
11
- `examples/kg-demo` in the [odla-ai monorepo](https://github.com/odla-ai/odla-ai).
14
+ the host supplies the database client, LLM keys, and a KV-like store. The
15
+ public platform and package manuals describe how to compose it with Workflows,
16
+ scheduled triggers, an HTTP API, and a viewer UI.
12
17
 
13
18
  ## Architecture
14
19
 
@@ -39,7 +44,7 @@ import {
39
44
  toSerializedSchema, type Ontology, type PipelineDeps,
40
45
  } from "@odla-ai/kg";
41
46
 
42
- // 1. Your ontology is data (see the OSINT example in examples/kg-demo).
47
+ // 1. Your ontology is ordinary application-owned data.
43
48
  const ontology: Ontology = { entities: { /* … */ }, edges: { /* … */ } };
44
49
 
45
50
  // 2. Wire the host services once. Env-var keys work standalone…
@@ -47,7 +52,7 @@ const db = init({ appId, adminToken, endpoint });
47
52
  const provider = getProvider({ LLM_PROVIDER: "claude", ANTHROPIC_API_KEY: key });
48
53
  // …but platform apps should bring a configured Ai instead (no LLM keys in
49
54
  // worker env): createProvider({ provider: "claude", ai }) with the ai from
50
- // @odla-ai/ai's initFromPlatform — see examples/kg-demo/src/deps.ts. The
55
+ // @odla-ai/ai's initFromPlatform. The
51
56
  // platform's default model is ignored here; kg's MODEL_TIERS govern verbs.
52
57
  const deps: PipelineDeps = { db, provider, ontology, extract: myExtractor, store: new Store(kv) };
53
58
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/ontology.ts","../src/graph/merge.ts","../src/graph/writer.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":["/**\n * @odla-ai/kg — ontology-driven knowledge-graph builder.\n *\n * Pipeline shape: Connector → RawItem[] → extract → GraphFragment →\n * writeFragment → Op[] → odla-db. The ontology is config-as-data; it compiles\n * to the odla-db schema, the writer's natural-key/merge-rule maps, and (by\n * convention) the extractor's tool schema. Consumers supply the ontology\n * instance, the extractor, the AdminDb, and a KV store — see PipelineDeps.\n */\n\n// Core shapes\nexport type { RawItem, GraphNode, GraphEdge, GraphFragment, Usage, ExtractResult } from \"./types\";\nexport { emptyUsage, addUsage } from \"./types\";\n\n// Ontology: types + compilers (ontology instances are consumer data)\nexport type { Ontology, EntitySpec, EdgeSpec, AttrSpec, AttrType, MergeRule } from \"./ontology\";\nexport { naturalKeysOf, mergeRulesOf, humanAttrsOf, toSerializedSchema } from \"./ontology\";\n\n// Graph writer + entity resolution\nexport { fragmentToOps, writeFragment, type NaturalKeys } from \"./graph/writer\";\nexport { mergeScalar, mergeAttrs } from \"./graph/merge\";\nexport { searchEntities, resolveName, canonicalizeFragment } from \"./graph/resolve\";\n\n// LLM seam (extract / search / write, adapted over @odla-ai/ai)\nexport type { Provider, ProviderFamily, ExtractCall, SearchCall, WriteCall, LlmConfig, LlmEnv } from \"./llm/provider\";\nexport { createProvider, getProvider, MODEL_TIERS } from \"./llm/provider\";\nexport { extractJson, extractJsonArray } from \"./llm/json\";\n\n// Connectors\nexport type { SourceConnector, ConnectorContext, ConnectorJob } from \"./connectors/types\";\nexport { getConnector, connectorIds, makeContext, type ContextConfig } from \"./connectors/registry\";\nexport { fetchUrl } from \"./connectors/url\";\n\n// Source scheduling (the source list itself lives with the consumer)\nexport type { SourceEntry } from \"./sources\";\nexport { enabledSources, jobsForHour, allJobs } from \"./sources\";\n\n// Operational state (seen-URL dedup, lastrun stamps, runtime source config)\nexport { Store, type KVLike } from \"./state/kv\";\n\n// Pipeline\nexport { ingestFresh, type PipelineDeps } from \"./pipeline/run-items\";\nexport {\n resilientStep,\n asStepLike,\n IO_STEP_CONFIG,\n COMMIT_STEP_CONFIG,\n LLM_STEP_CONFIG,\n type StepLike,\n type StepConfig,\n} from \"./workflow/helpers\";\n\n// Utilities\nexport { slugify, normalizeUrl, urlHash, isLikelyPersonName } from \"./util/identity\";\nexport { fetchText, htmlToText } from \"./util/content\";\n","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\nexport function emptyUsage(): Usage {\n return { calls: 0, inputTokens: 0, outputTokens: 0 };\n}\n\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 for 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","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\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\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(/&nbsp;/gi, \" \")\n .replace(/&amp;/gi, \"&\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/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&timespan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n const res = await fetch(api, { headers: { \"User-Agent\": UA } });\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\nexport function getConnector(id: string): SourceConnector | undefined {\n return REGISTRY.get(id);\n}\n\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\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\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\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","/**\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/index.ts","../src/types.ts","../src/ontology.ts","../src/graph/merge.ts","../src/graph/writer.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":["/**\n * @odla-ai/kg — ontology-driven knowledge-graph builder.\n *\n * Pipeline shape: Connector → RawItem[] → extract → GraphFragment →\n * writeFragment → Op[] → odla-db. The ontology is config-as-data; it compiles\n * to the odla-db schema, the writer's natural-key/merge-rule maps, and (by\n * convention) the extractor's tool schema. Consumers supply the ontology\n * instance, the extractor, the AdminDb, and a KV store — see PipelineDeps.\n */\n\n// Core shapes\nexport type { RawItem, GraphNode, GraphEdge, GraphFragment, Usage, ExtractResult } from \"./types\";\nexport { emptyUsage, addUsage } from \"./types\";\n\n// Ontology: types + compilers (ontology instances are consumer data)\nexport type { Ontology, EntitySpec, EdgeSpec, AttrSpec, AttrType, MergeRule } from \"./ontology\";\nexport { naturalKeysOf, mergeRulesOf, humanAttrsOf, toSerializedSchema } from \"./ontology\";\n\n// Graph writer + entity resolution\nexport { fragmentToOps, writeFragment, type NaturalKeys } from \"./graph/writer\";\nexport { mergeScalar, mergeAttrs } from \"./graph/merge\";\nexport { searchEntities, resolveName, canonicalizeFragment } from \"./graph/resolve\";\n\n// LLM seam (extract / search / write, adapted over @odla-ai/ai)\nexport type { Provider, ProviderFamily, ExtractCall, SearchCall, WriteCall, LlmConfig, LlmEnv } from \"./llm/provider\";\nexport { createProvider, getProvider, MODEL_TIERS } from \"./llm/provider\";\nexport { extractJson, extractJsonArray } from \"./llm/json\";\n\n// Connectors\nexport type { SourceConnector, ConnectorContext, ConnectorJob } from \"./connectors/types\";\nexport { getConnector, connectorIds, makeContext, type ContextConfig } from \"./connectors/registry\";\nexport { fetchUrl } from \"./connectors/url\";\n\n// Source scheduling (the source list itself lives with the consumer)\nexport type { SourceEntry } from \"./sources\";\nexport { enabledSources, jobsForHour, allJobs } from \"./sources\";\n\n// Operational state (seen-URL dedup, lastrun stamps, runtime source config)\nexport { Store, type KVLike } from \"./state/kv\";\n\n// Pipeline\nexport { ingestFresh, type PipelineDeps } from \"./pipeline/run-items\";\nexport {\n resilientStep,\n asStepLike,\n IO_STEP_CONFIG,\n COMMIT_STEP_CONFIG,\n LLM_STEP_CONFIG,\n type StepLike,\n type StepConfig,\n} from \"./workflow/helpers\";\n\n// Utilities\nexport { slugify, normalizeUrl, urlHash, isLikelyPersonName } from \"./util/identity\";\nexport { fetchText, htmlToText } from \"./util/content\";\n","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","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(/&nbsp;/gi, \" \")\n .replace(/&amp;/gi, \"&\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/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&timespan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n const res = await fetch(api, { headers: { \"User-Agent\": UA } });\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\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","/**\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"]}
package/dist/index.d.cts CHANGED
@@ -50,7 +50,9 @@ interface Usage {
50
50
  inputTokens: number;
51
51
  outputTokens: number;
52
52
  }
53
+ /** Create zeroed provider-agnostic usage totals for one pipeline run. */
53
54
  declare function emptyUsage(): Usage;
55
+ /** Mutate an accumulator by adding call, input-token, and output-token totals. */
54
56
  declare function addUsage(into: Usage, more: Usage): void;
55
57
 
56
58
  /** node type → unique natural-key attribute name. */
@@ -65,7 +67,9 @@ declare function fragmentToOps(fragment: GraphFragment, keys: NaturalKeys): Op[]
65
67
  * Provenance-preserving write. Reads existing nodes by natural key (one batched
66
68
  * query), merges scalar attrs per the ontology's rules, drops human-owned
67
69
  * attrs, then writes merged node updates + all links in one atomic, idempotent
68
- * transaction. `mutationId` must be deterministic for exactly-once.
70
+ * transaction. `mutationId` must be deterministic so odla-db can deduplicate
71
+ * the write on retry; this does not make preceding reads or external effects
72
+ * exactly-once.
69
73
  */
70
74
  declare function writeFragment(db: AdminDb, fragment: GraphFragment, ontology: Ontology, mutationId: string): Promise<{
71
75
  txId: number;
@@ -217,6 +221,7 @@ interface LlmEnv {
217
221
  ANTHROPIC_API_KEY?: string;
218
222
  OPENAI_API_KEY?: string;
219
223
  }
224
+ /** Build the configured provider from host env, failing before use when its key is missing. */
220
225
  declare function getProvider(env: LlmEnv): Provider;
221
226
 
222
227
  /**
@@ -224,6 +229,7 @@ declare function getProvider(env: LlmEnv): Provider;
224
229
  * explanatory text. Ported from agnacl services/enrichment.ts.
225
230
  */
226
231
  declare function extractJson<T = unknown>(text: string): T | null;
232
+ /** Tolerantly parse the first JSON-array-shaped span in model prose, or return an empty array. */
227
233
  declare function extractJsonArray<T = unknown>(text: string): T[];
228
234
 
229
235
  /** Ambient services a connector may use. Host-agnostic: secrets come through
@@ -261,7 +267,9 @@ interface ConnectorJob {
261
267
  cfg: unknown;
262
268
  }
263
269
 
270
+ /** Resolve a built-in source connector by its stable registry id. */
264
271
  declare function getConnector(id: string): SourceConnector | undefined;
272
+ /** List registered connector ids in deterministic insertion order. */
265
273
  declare function connectorIds(): string[];
266
274
  /** Everything a ConnectorContext needs, supplied by the host (worker, script, test). */
267
275
  interface ContextConfig {
@@ -271,6 +279,7 @@ interface ContextConfig {
271
279
  secrets?: Record<string, string | undefined>;
272
280
  now?: Date;
273
281
  }
282
+ /** Create a host-supplied connector context with lazy provider construction. */
274
283
  declare function makeContext(cfg: ContextConfig): ConnectorContext;
275
284
 
276
285
  /** Fetch one URL → a text-bearing RawItem. Used both directly (/trigger) and as a connector. */
@@ -291,6 +300,7 @@ interface SourceEntry {
291
300
  query?: string;
292
301
  [key: string]: unknown;
293
302
  }
303
+ /** Return source entries not explicitly disabled, preserving configured order. */
294
304
  declare function enabledSources(sources: SourceEntry[]): SourceEntry[];
295
305
  /** Enabled sources whose cadence lands on this UTC hour. */
296
306
  declare function jobsForHour(sources: SourceEntry[], hour: number): ConnectorJob[];
package/dist/index.d.ts CHANGED
@@ -50,7 +50,9 @@ interface Usage {
50
50
  inputTokens: number;
51
51
  outputTokens: number;
52
52
  }
53
+ /** Create zeroed provider-agnostic usage totals for one pipeline run. */
53
54
  declare function emptyUsage(): Usage;
55
+ /** Mutate an accumulator by adding call, input-token, and output-token totals. */
54
56
  declare function addUsage(into: Usage, more: Usage): void;
55
57
 
56
58
  /** node type → unique natural-key attribute name. */
@@ -65,7 +67,9 @@ declare function fragmentToOps(fragment: GraphFragment, keys: NaturalKeys): Op[]
65
67
  * Provenance-preserving write. Reads existing nodes by natural key (one batched
66
68
  * query), merges scalar attrs per the ontology's rules, drops human-owned
67
69
  * attrs, then writes merged node updates + all links in one atomic, idempotent
68
- * transaction. `mutationId` must be deterministic for exactly-once.
70
+ * transaction. `mutationId` must be deterministic so odla-db can deduplicate
71
+ * the write on retry; this does not make preceding reads or external effects
72
+ * exactly-once.
69
73
  */
70
74
  declare function writeFragment(db: AdminDb, fragment: GraphFragment, ontology: Ontology, mutationId: string): Promise<{
71
75
  txId: number;
@@ -217,6 +221,7 @@ interface LlmEnv {
217
221
  ANTHROPIC_API_KEY?: string;
218
222
  OPENAI_API_KEY?: string;
219
223
  }
224
+ /** Build the configured provider from host env, failing before use when its key is missing. */
220
225
  declare function getProvider(env: LlmEnv): Provider;
221
226
 
222
227
  /**
@@ -224,6 +229,7 @@ declare function getProvider(env: LlmEnv): Provider;
224
229
  * explanatory text. Ported from agnacl services/enrichment.ts.
225
230
  */
226
231
  declare function extractJson<T = unknown>(text: string): T | null;
232
+ /** Tolerantly parse the first JSON-array-shaped span in model prose, or return an empty array. */
227
233
  declare function extractJsonArray<T = unknown>(text: string): T[];
228
234
 
229
235
  /** Ambient services a connector may use. Host-agnostic: secrets come through
@@ -261,7 +267,9 @@ interface ConnectorJob {
261
267
  cfg: unknown;
262
268
  }
263
269
 
270
+ /** Resolve a built-in source connector by its stable registry id. */
264
271
  declare function getConnector(id: string): SourceConnector | undefined;
272
+ /** List registered connector ids in deterministic insertion order. */
265
273
  declare function connectorIds(): string[];
266
274
  /** Everything a ConnectorContext needs, supplied by the host (worker, script, test). */
267
275
  interface ContextConfig {
@@ -271,6 +279,7 @@ interface ContextConfig {
271
279
  secrets?: Record<string, string | undefined>;
272
280
  now?: Date;
273
281
  }
282
+ /** Create a host-supplied connector context with lazy provider construction. */
274
283
  declare function makeContext(cfg: ContextConfig): ConnectorContext;
275
284
 
276
285
  /** Fetch one URL → a text-bearing RawItem. Used both directly (/trigger) and as a connector. */
@@ -291,6 +300,7 @@ interface SourceEntry {
291
300
  query?: string;
292
301
  [key: string]: unknown;
293
302
  }
303
+ /** Return source entries not explicitly disabled, preserving configured order. */
294
304
  declare function enabledSources(sources: SourceEntry[]): SourceEntry[];
295
305
  /** Enabled sources whose cadence lands on this UTC hour. */
296
306
  declare function jobsForHour(sources: SourceEntry[], hour: number): ConnectorJob[];
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/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\nexport function emptyUsage(): Usage {\n return { calls: 0, inputTokens: 0, outputTokens: 0 };\n}\n\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 for 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","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\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\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(/&nbsp;/gi, \" \")\n .replace(/&amp;/gi, \"&\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/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&timespan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n const res = await fetch(api, { headers: { \"User-Agent\": UA } });\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\nexport function getConnector(id: string): SourceConnector | undefined {\n return REGISTRY.get(id);\n}\n\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\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\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\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","/**\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/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","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(/&nbsp;/gi, \" \")\n .replace(/&amp;/gi, \"&\")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#39;|&apos;/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&timespan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n const res = await fetch(api, { headers: { \"User-Agent\": UA } });\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\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","/**\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"]}
package/llms.txt CHANGED
@@ -2,15 +2,20 @@
2
2
 
3
3
  > Ontology-driven knowledge-graph builder: pluggable source connectors, LLM extraction against a config-as-data ontology, and a provenance-preserving graph writer persisting into odla-db.
4
4
 
5
+ > ⚠️ **Early access — pre-1.0.** Agents work from bounded runbooks; humans
6
+ > approve credentials, production changes, releases, and merges. APIs and exact
7
+ > package availability can change. Review the documented guarantees and
8
+ > limitations; this software is MIT-licensed and provided without warranty.
9
+
5
10
  An ontology-driven **knowledge-graph builder**. Pluggable source connectors pull
6
11
  from the web and data feeds, an LLM extracts entities + relationships against a
7
12
  **config-as-data ontology**, and a provenance-preserving graph writer persists
8
13
  the result into [odla-db](https://www.npmjs.com/package/@odla-ai/db).
9
14
 
10
15
  The library is host-agnostic: it runs in Cloudflare Workers, Node, or tests —
11
- the host supplies the database client, LLM keys, and a KV-like store. For a
12
- complete deployable consumer (workflows, hourly cron, HTTP API, viewer UI), see
13
- `examples/kg-demo` in the [odla-ai monorepo](https://github.com/odla-ai/odla-ai).
16
+ the host supplies the database client, LLM keys, and a KV-like store. The
17
+ public platform and package manuals describe how to compose it with Workflows,
18
+ scheduled triggers, an HTTP API, and a viewer UI.
14
19
 
15
20
  ## Architecture
16
21
 
@@ -41,7 +46,7 @@ import {
41
46
  toSerializedSchema, type Ontology, type PipelineDeps,
42
47
  } from "@odla-ai/kg";
43
48
 
44
- // 1. Your ontology is data (see the OSINT example in examples/kg-demo).
49
+ // 1. Your ontology is ordinary application-owned data.
45
50
  const ontology: Ontology = { entities: { /* … */ }, edges: { /* … */ } };
46
51
 
47
52
  // 2. Wire the host services once. Env-var keys work standalone…
@@ -49,7 +54,7 @@ const db = init({ appId, adminToken, endpoint });
49
54
  const provider = getProvider({ LLM_PROVIDER: "claude", ANTHROPIC_API_KEY: key });
50
55
  // …but platform apps should bring a configured Ai instead (no LLM keys in
51
56
  // worker env): createProvider({ provider: "claude", ai }) with the ai from
52
- // @odla-ai/ai's initFromPlatform — see examples/kg-demo/src/deps.ts. The
57
+ // @odla-ai/ai's initFromPlatform. The
53
58
  // platform's default model is ignored here; kg's MODEL_TIERS govern verbs.
54
59
  const deps: PipelineDeps = { db, provider, ontology, extract: myExtractor, store: new Store(kv) };
55
60
 
@@ -80,7 +85,7 @@ scalar attrs per the ontology's rules (`replace` / `prefer` / `earliest` /
80
85
  `latest`), never writes `human`-owned attrs, and commits nodes-then-links in a
81
86
  single idempotent transaction (deterministic `mutationId`).
82
87
 
83
- ## API reference (generated from dist/index.d.ts, v0.2.0)
88
+ ## API reference (generated from dist/index.d.ts, v0.2.2)
84
89
 
85
90
  ```ts
86
91
  import { Value, Op, AdminDb, SerializedAttr, Cardinality, SerializedSchema, Entity } from '@odla-ai/db';
@@ -135,7 +140,9 @@ interface Usage {
135
140
  inputTokens: number;
136
141
  outputTokens: number;
137
142
  }
143
+ /** Create zeroed provider-agnostic usage totals for one pipeline run. */
138
144
  declare function emptyUsage(): Usage;
145
+ /** Mutate an accumulator by adding call, input-token, and output-token totals. */
139
146
  declare function addUsage(into: Usage, more: Usage): void;
140
147
 
141
148
  /** node type → unique natural-key attribute name. */
@@ -150,7 +157,9 @@ declare function fragmentToOps(fragment: GraphFragment, keys: NaturalKeys): Op[]
150
157
  * Provenance-preserving write. Reads existing nodes by natural key (one batched
151
158
  * query), merges scalar attrs per the ontology's rules, drops human-owned
152
159
  * attrs, then writes merged node updates + all links in one atomic, idempotent
153
- * transaction. `mutationId` must be deterministic for exactly-once.
160
+ * transaction. `mutationId` must be deterministic so odla-db can deduplicate
161
+ * the write on retry; this does not make preceding reads or external effects
162
+ * exactly-once.
154
163
  */
155
164
  declare function writeFragment(db: AdminDb, fragment: GraphFragment, ontology: Ontology, mutationId: string): Promise<{
156
165
  txId: number;
@@ -302,6 +311,7 @@ interface LlmEnv {
302
311
  ANTHROPIC_API_KEY?: string;
303
312
  OPENAI_API_KEY?: string;
304
313
  }
314
+ /** Build the configured provider from host env, failing before use when its key is missing. */
305
315
  declare function getProvider(env: LlmEnv): Provider;
306
316
 
307
317
  /**
@@ -309,6 +319,7 @@ declare function getProvider(env: LlmEnv): Provider;
309
319
  * explanatory text. Ported from agnacl services/enrichment.ts.
310
320
  */
311
321
  declare function extractJson<T = unknown>(text: string): T | null;
322
+ /** Tolerantly parse the first JSON-array-shaped span in model prose, or return an empty array. */
312
323
  declare function extractJsonArray<T = unknown>(text: string): T[];
313
324
 
314
325
  /** Ambient services a connector may use. Host-agnostic: secrets come through
@@ -346,7 +357,9 @@ interface ConnectorJob {
346
357
  cfg: unknown;
347
358
  }
348
359
 
360
+ /** Resolve a built-in source connector by its stable registry id. */
349
361
  declare function getConnector(id: string): SourceConnector | undefined;
362
+ /** List registered connector ids in deterministic insertion order. */
350
363
  declare function connectorIds(): string[];
351
364
  /** Everything a ConnectorContext needs, supplied by the host (worker, script, test). */
352
365
  interface ContextConfig {
@@ -356,6 +369,7 @@ interface ContextConfig {
356
369
  secrets?: Record<string, string | undefined>;
357
370
  now?: Date;
358
371
  }
372
+ /** Create a host-supplied connector context with lazy provider construction. */
359
373
  declare function makeContext(cfg: ContextConfig): ConnectorContext;
360
374
 
361
375
  /** Fetch one URL → a text-bearing RawItem. Used both directly (/trigger) and as a connector. */
@@ -376,6 +390,7 @@ interface SourceEntry {
376
390
  query?: string;
377
391
  [key: string]: unknown;
378
392
  }
393
+ /** Return source entries not explicitly disabled, preserving configured order. */
379
394
  declare function enabledSources(sources: SourceEntry[]): SourceEntry[];
380
395
  /** Enabled sources whose cadence lands on this UTC hour. */
381
396
  declare function jobsForHour(sources: SourceEntry[], hour: number): ConnectorJob[];
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@odla-ai/kg",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Ontology-driven knowledge-graph builder: pluggable source connectors, LLM extraction against a config-as-data ontology, and a provenance-preserving graph writer persisting into odla-db.",
5
5
  "license": "MIT",
6
+ "homepage": "https://odla.ai/docs/packages/kg",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cory/odla-ai.git",
10
+ "directory": "packages/kg"
11
+ },
6
12
  "type": "module",
7
13
  "main": "./dist/index.cjs",
8
14
  "module": "./dist/index.js",
@@ -24,7 +30,8 @@
24
30
  "node": ">=20"
25
31
  },
26
32
  "publishConfig": {
27
- "access": "public"
33
+ "access": "public",
34
+ "provenance": false
28
35
  },
29
36
  "keywords": [
30
37
  "odla",
@@ -45,10 +52,10 @@
45
52
  "prepublishOnly": "npm run build && npm run gen:llms"
46
53
  },
47
54
  "dependencies": {
48
- "@odla-ai/ai": "^0.2.0"
55
+ "@odla-ai/ai": "^0.3.0"
49
56
  },
50
57
  "peerDependencies": {
51
- "@odla-ai/db": "^0.5.0"
58
+ "@odla-ai/db": "^0.6.0"
52
59
  },
53
60
  "devDependencies": {
54
61
  "@odla-ai/db": "*",