pilotswarm-horizon-store 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.
Files changed (71) hide show
  1. package/README.md +154 -0
  2. package/dist/src/agent-tools.d.ts +32 -0
  3. package/dist/src/agent-tools.d.ts.map +1 -0
  4. package/dist/src/agent-tools.js +263 -0
  5. package/dist/src/agent-tools.js.map +1 -0
  6. package/dist/src/config.d.ts +93 -0
  7. package/dist/src/config.d.ts.map +1 -0
  8. package/dist/src/config.js +120 -0
  9. package/dist/src/config.js.map +1 -0
  10. package/dist/src/db-retry.d.ts +21 -0
  11. package/dist/src/db-retry.d.ts.map +1 -0
  12. package/dist/src/db-retry.js +94 -0
  13. package/dist/src/db-retry.js.map +1 -0
  14. package/dist/src/embedding-client.d.ts +13 -0
  15. package/dist/src/embedding-client.d.ts.map +1 -0
  16. package/dist/src/embedding-client.js +79 -0
  17. package/dist/src/embedding-client.js.map +1 -0
  18. package/dist/src/graph-model.d.ts +49 -0
  19. package/dist/src/graph-model.d.ts.map +1 -0
  20. package/dist/src/graph-model.js +112 -0
  21. package/dist/src/graph-model.js.map +1 -0
  22. package/dist/src/graph-queries.d.ts +83 -0
  23. package/dist/src/graph-queries.d.ts.map +1 -0
  24. package/dist/src/graph-queries.js +727 -0
  25. package/dist/src/graph-queries.js.map +1 -0
  26. package/dist/src/graph-store.d.ts +57 -0
  27. package/dist/src/graph-store.d.ts.map +1 -0
  28. package/dist/src/graph-store.js +327 -0
  29. package/dist/src/graph-store.js.map +1 -0
  30. package/dist/src/horizon-migrator.d.ts +49 -0
  31. package/dist/src/horizon-migrator.d.ts.map +1 -0
  32. package/dist/src/horizon-migrator.js +231 -0
  33. package/dist/src/horizon-migrator.js.map +1 -0
  34. package/dist/src/horizon-store.d.ts +116 -0
  35. package/dist/src/horizon-store.d.ts.map +1 -0
  36. package/dist/src/horizon-store.js +563 -0
  37. package/dist/src/horizon-store.js.map +1 -0
  38. package/dist/src/index.d.ts +13 -0
  39. package/dist/src/index.d.ts.map +1 -0
  40. package/dist/src/index.js +21 -0
  41. package/dist/src/index.js.map +1 -0
  42. package/dist/src/preconditions.d.ts +16 -0
  43. package/dist/src/preconditions.d.ts.map +1 -0
  44. package/dist/src/preconditions.js +99 -0
  45. package/dist/src/preconditions.js.map +1 -0
  46. package/dist/src/query-builder.d.ts +37 -0
  47. package/dist/src/query-builder.d.ts.map +1 -0
  48. package/dist/src/query-builder.js +82 -0
  49. package/dist/src/query-builder.js.map +1 -0
  50. package/dist/src/sql-util.d.ts +20 -0
  51. package/dist/src/sql-util.d.ts.map +1 -0
  52. package/dist/src/sql-util.js +40 -0
  53. package/dist/src/sql-util.js.map +1 -0
  54. package/dist/src/types.d.ts +16 -0
  55. package/dist/src/types.d.ts.map +1 -0
  56. package/dist/src/types.js +35 -0
  57. package/dist/src/types.js.map +1 -0
  58. package/migrations/0001_facts_table.sql +77 -0
  59. package/migrations/0002_indexes.sql +27 -0
  60. package/migrations/0003_age_bootstrap.sql +13 -0
  61. package/migrations/0004_facts_procs.sql +253 -0
  62. package/migrations/0005_embedder_workflow.sql +119 -0
  63. package/migrations/0006_facts_read_uncrawled_embedded_gate.sql +33 -0
  64. package/migrations/0007_embed_key_value_text.sql +122 -0
  65. package/migrations/0008_embedder_failure_recovery.sql +409 -0
  66. package/migrations/0009_search_text_full_value.sql +52 -0
  67. package/migrations/0010_minimal_two_loop_embedder.sql +392 -0
  68. package/migrations/0011_unified_api_embedder_workflow.sql +273 -0
  69. package/migrations/0012_facts_soft_delete.sql +587 -0
  70. package/migrations/0013_graph_namespaces.sql +60 -0
  71. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # `pilotswarm-horizon-store`
2
+
3
+ Optional HorizonDB-backed providers for PilotSwarm enhanced facts/search and the
4
+ open knowledge graph. The package is published alongside `pilotswarm-sdk` and is
5
+ intended for apps that keep stock PostgreSQL as the PilotSwarm runtime store while
6
+ opting into HorizonDB for knowledge retrieval and graph-backed harvester workflows.
7
+
8
+ ## What this is
9
+
10
+ An **optional, enhanced read interface** over the PilotSwarm Facts Store, built
11
+ exclusively on **Azure HorizonDB** (preview) capabilities:
12
+
13
+ | HorizonDB capability | Role here |
14
+ | --- | --- |
15
+ | `pg_textsearch` | Ranked lexical recall (upgrade over today's `LIKE` / `key_pattern`) |
16
+ | HTTP embedding endpoint | In-DB embedding generation via a configurable endpoint + semantic (vector ANN) recall |
17
+ | Apache AGE | Relationship/lineage graph overlay (structure only) |
18
+ | `pg_durable` | Durable, idle-aware maintenance pipeline (embeddings only) |
19
+
20
+ > **Embeddings come from a configurable HTTP endpoint, not HorizonDB's built-in
21
+ > `aiModelManagement`.** You pass an OpenAI/Azure-OpenAI-compatible embeddings
22
+ > endpoint to the provider; the `pg_durable` loop calls it over HTTP from inside
23
+ > the database (`sql/006`), and a Node fallback (`embedPending()`) covers
24
+ > clusters without the `http` extension. See [CRAWLER-SPEC.md](./CRAWLER-SPEC.md) §3.
25
+
26
+ ## Hard design rules (carried from PilotSwarm)
27
+
28
+ 1. **The `facts` table stays authoritative.** tsvector, vector, and AGE are
29
+ *derived indexes/overlays* — never the source of truth. The graph stores
30
+ **ids and structure, never fact values or ACLs**.
31
+ 2. **Governance is unchanged.** Scope (`scope_key`), `shared` / `transient`,
32
+ namespace ACLs, and spawn-tree visibility are still enforced by stored
33
+ procedures. Search modes are extra `AND` clauses inside the existing
34
+ visibility filter — they can only narrow what a caller already sees.
35
+ 3. **Determinism boundary.** Anything LLM/IO (embedding, distillation,
36
+ relatedness) runs as a `pg_durable` **activity**, never inline orchestration.
37
+ 4. **Rebuildable.** Every derived artifact (tsvector, embeddings, AGE graph)
38
+ can be dropped and rebuilt from `facts` rows.
39
+
40
+ ## Layout
41
+
42
+ ```
43
+ packages/horizon-store/
44
+ SPEC.md ← the design: data model, compute/API/frequency, scenarios
45
+ CRAWLER.md ← open, ontology-free LLM graph crawler (entities + free-form relationships)
46
+ CRAWLER-SPEC.md ← implementation contract: API, schema, compute tiers, PG mailing-list example
47
+ src/
48
+ types.ts ← EnhancedFactStore + GraphCrawlerInterface contracts + DTOs
49
+ config.ts ← provider config incl. the embedding HTTP endpoint
50
+ embedding-client.ts ← Node-side embeddings client (query-time + test reference)
51
+ query-builder.ts ← DB-less hybrid ranking/fusion + SQL fragment builders (unit-tested)
52
+ graph-model.ts ← DB-less open-graph quality core: canonicalize, predicate, confidence (unit-tested)
53
+ migrations.ts ← Node-runnable schema + AGE setup (mirrors sql/001–005)
54
+ http-embedding.ts ← Node-runnable in-DB HTTP embedding pipeline (mirrors sql/006)
55
+ horizon-store.ts ← HorizonFactStore: drop-in EnhancedFactStore + open-graph crawler
56
+ agent-tools.ts ← optional agent tools (search / related / graph) for injection
57
+ index.ts ← public exports
58
+ sql/
59
+ 001_enrich_facts.sql ← tsvector + embedding columns + indexes
60
+ 002_age_graph.sql ← AGE node/edge model + structural backfill
61
+ 003_search_procs.sql ← facts_search_facts + related/lineage procs
62
+ 004_pipelines.sql ← pg_durable maintenance pipeline (embeddings only)
63
+ 005_open_graph.sql ← open Entity / REL / EVIDENCED_BY graph for the crawler
64
+ 006_embeddings_http.sql ← in-DB HTTP embedding pipeline (replaces aiModelManagement)
65
+ poc/ ← runnable harnesses (lexical/hybrid/crawler run DB-less today)
66
+ test/ ← DB-less unit tests (run in CI without HorizonDB)
67
+ integration/ ← live tests against a real HorizonDB (skip without HORIZON_DATABASE_URL)
68
+ ```
69
+
70
+ ## Drop-in replacement
71
+
72
+ `HorizonFactStore` implements the full PilotSwarm `FactStore` API
73
+ (`storeFact` / `readFacts` / `deleteFact` / stats / …) with identical semantics,
74
+ so it can replace `PgFactStore` anywhere. It **adds** retrieval methods
75
+ (`searchFacts` / `relatedFacts` / `lineageFacts`) and the open-graph crawler
76
+ (`upsertEntity` / `assertRelationship` / …). Apps opt into the extras; nothing
77
+ in the base behavior changes.
78
+
79
+ ```js
80
+ import { HorizonFactStore, createFactsTools } from "pilotswarm-horizon-store";
81
+
82
+ const store = await HorizonFactStore.create({
83
+ connectionString: process.env.HORIZON_DATABASE_URL,
84
+ embedding: { url: EMBED_URL, model: "text-embedding-3-small", dim: 1536, apiKey: KEY },
85
+ });
86
+ await store.initialize();
87
+
88
+ // ...use exactly like the existing FactStore, plus:
89
+ await store.searchFacts("jsonb subscripting", { mode: "hybrid" }, { unrestricted: true });
90
+
91
+ // Optionally inject tools into your agents:
92
+ const tools = createFactsTools(store, { graphWrite: true }); // spread into worker.registerTools([...])
93
+ ```
94
+
95
+ ## Running
96
+
97
+ ```bash
98
+ cd packages/horizon-store
99
+ npm install
100
+ npm run build
101
+ npm test # DB-less unit tests — no HorizonDB needed
102
+ npm run poc:crawler # open-graph harvest scenario — runs DB-less today
103
+
104
+ # Live integration tests against a real HorizonDB (auto-skip if unset):
105
+ export HORIZON_DATABASE_URL=postgres://user:pw@host/db
106
+ npm run test:integration # pg_durable HTTP embeddings, AGE Cypher, full provider
107
+ ```
108
+
109
+ ## Evaluation
110
+
111
+ The incubator ships a two-axis evaluation surface — **system** evals (is the
112
+ harvest correct, durable, fast?) and **quality** evals (does the graph actually
113
+ help an LLM answer better than parametric knowledge + live web?). The full system
114
+ overview — how the harvester builds the KB and how every eval tier fits together —
115
+ is in [docs/harvester-and-eval.md](./docs/harvester-and-eval.md).
116
+
117
+ | Tier | Where | What it proves |
118
+ | --- | --- | --- |
119
+ | Scenario (system) | [eval/README.md](./eval/README.md) | Cold/incremental harvest, replay determinism, scoped publication, reader fact-pivot |
120
+ | Quality (single model) | [eval/graph-quality.mjs](./eval/graph-quality.mjs) | Graph arm vs parametric+web baseline, blind-judged against corpus ground truth |
121
+ | **Quality (cross-model sweep)** | [eval/sweep/](./eval/sweep/) | A 3×3×3 **harvester × query × judge** tensor that isolates judge bias |
122
+
123
+ ### Cross-model sweep
124
+
125
+ The sweep answers "is the graph really better, or just a lucky model / biased
126
+ judge?" by sweeping three independent axes into a score tensor and generating a
127
+ bias-aware report grounded in a deterministic numeric summary. Latest run
128
+ (`pgsql-hackers-recent`, N=8 questions/cell, 216 graded rows):
129
+
130
+ > **graph 4.76 vs baseline 2.42** (Δ +2.34 on a 1–5 scale), graph winning 163/216
131
+ > head-to-head with no judge able to flip the verdict — and it holds with the
132
+ > graph answers being *shorter* than the baseline. Honestly deflated to a
133
+ > ~+1.8–2.0 *substantive* edge once baseline web-timeout failures are separated
134
+ > out (see the report's methodology + caveats).
135
+
136
+ - Results: [eval/sweep/REPORT.md](./eval/sweep/REPORT.md) (narrative) + [eval/sweep/summary.json](./eval/sweep/summary.json) (authoritative numbers).
137
+ - Run + interpret: skill [horizon-facts-eval-sweep/SKILL.md](../../.github/skills/horizon-facts-eval-sweep/SKILL.md) and agent [horizon-facts-eval-sweep.agent.md](../../.github/agents/horizon-facts-eval-sweep.agent.md) (commands + manual graph-validation queries).
138
+ - The bulky per-cell intermediates (`scores/`, `transcripts/`, `logs/`) are gitignored and regenerable; the driver, config, pinned `questions.json`, and final `REPORT.md` / `summary.json` are tracked, so any run reproduces.
139
+
140
+ ## Specs & docs
141
+
142
+ **Enhanced facts store (this incubator):**
143
+ [SPEC.md](./SPEC.md) (base design) ·
144
+ [CRAWLER.md](./CRAWLER.md) / [CRAWLER-SPEC.md](./CRAWLER-SPEC.md) (open-graph crawler) ·
145
+ [GAP-ANALYSIS.md](./GAP-ANALYSIS.md).
146
+ Upstream provider contract + tool/test specs:
147
+ [docs/proposals/enhancedfactstore/](../../docs/proposals/enhancedfactstore/)
148
+ (`01-functional-spec` · `02-api-reference` · `03-design` · `04-test-spec` ·
149
+ `05-tools-spec` · `06-provider-test-plan`).
150
+
151
+ **Harvester & evaluation:**
152
+ [docs/harvester-and-eval.md](./docs/harvester-and-eval.md) (full system overview) ·
153
+ [eval/README.md](./eval/README.md) (scenario tier) ·
154
+ [eval/sweep/REPORT.md](./eval/sweep/REPORT.md) (latest cross-model sweep).
@@ -0,0 +1,32 @@
1
+ import type { AccessContext, GraphStore } from "./types.js";
2
+ import type { HorizonDBFactStore } from "./horizon-store.js";
3
+ export interface AgentTool {
4
+ name: string;
5
+ description: string;
6
+ parameters: Record<string, unknown>;
7
+ handler: (args: any) => Promise<unknown>;
8
+ }
9
+ export type Role = "reader" | "harvester";
10
+ export interface FactsToolsOptions {
11
+ /** Which bundle to build. Default "reader". */
12
+ role?: Role;
13
+ /**
14
+ * Access context resolver for the READER-scoped tools. Default:
15
+ * unrestricted (single-tenant). Override for multi-tenant sessions.
16
+ * The crawl-queue tools never use it — they are privileged by contract.
17
+ */
18
+ resolveAccess?: (args: any) => AccessContext;
19
+ /** Agent identity recorded on graph writes. Default "harvester". */
20
+ agentId?: string;
21
+ /**
22
+ * When true, the privileged `facts_read_uncrawled` queue tool only returns
23
+ * facts that already have an embedding; un-embedded facts are skipped this
24
+ * turn and reappear once the in-DB embed loop catches up. This is a HARVEST
25
+ * POLICY set by the host (not an LLM-controlled argument): enable it when
26
+ * embeddings are configured so the harvester can similarity-refine the
27
+ * graph from each fact's stored vector.
28
+ */
29
+ embeddedOnly?: boolean;
30
+ }
31
+ export declare function createFactsTools(factStore: HorizonDBFactStore, graphStore: GraphStore | undefined, opts?: FactsToolsOptions): AgentTool[];
32
+ //# sourceMappingURL=agent-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/agent-tools.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACR,aAAa,EAAE,UAAU,EAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5C;AAED,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAC9B,+CAA+C;IAC/C,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,aAAa,CAAC;IAC7C,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,gBAAgB,CAC5B,SAAS,EAAE,kBAAkB,EAC7B,UAAU,EAAE,UAAU,GAAG,SAAS,EAClC,IAAI,GAAE,iBAAsB,GAC7B,SAAS,EAAE,CA0Rb"}
@@ -0,0 +1,263 @@
1
+ // pilotswarm-horizon-store — LLM-facing agent tools (05-tools-spec).
2
+ //
3
+ // Tool names and contracts follow docs/proposals/enhancedfactstore/05-tools-spec.md
4
+ // exactly: `facts_*` over the facts store, `graph_*` over the open graph. Two
5
+ // role bundles over the same corpus:
6
+ //
7
+ // READER — facts_search / facts_similar / facts_read + the graph READ tools.
8
+ // HARVESTER — everything the reader gets, plus the PRIVILEGED crawl-queue
9
+ // tools (facts_read_uncrawled / facts_mark_crawled) and the graph
10
+ // WRITE tools. Crawling sees ALL facts across scopes by design
11
+ // (01 §6.6); only grant this bundle to the trusted harvester role.
12
+ //
13
+ // Descriptors are host-agnostic (name + JSON-schema + handler), mapping onto
14
+ // PilotSwarm's defineTool() shape.
15
+ export function createFactsTools(factStore, graphStore, opts = {}) {
16
+ const role = opts.role ?? "reader";
17
+ const access = opts.resolveAccess ?? (() => ({ unrestricted: true }));
18
+ const agentId = opts.agentId ?? "harvester";
19
+ const embeddedOnly = opts.embeddedOnly ?? false;
20
+ const tools = [];
21
+ // ── §1 retrieval (reader + harvester) ────────────────────────────────────
22
+ tools.push({
23
+ name: "facts_search",
24
+ description: "Search facts by a query over the FACTS STORE ONLY. The query shape depends on mode: " +
25
+ "lexical = BM25 — pass KEYWORDS/terms, not a sentence; semantic = natural language (embedded); " +
26
+ "hybrid = a short keyword-rich phrase, used both ways. There is NO graph mode — use graph_search_nodes. " +
27
+ "Returned scopeKey values are the natural seeds for graph_search_nodes.",
28
+ parameters: {
29
+ type: "object",
30
+ properties: {
31
+ query: { type: "string", description: "Keywords for lexical, natural language for semantic, keyword-rich phrase for hybrid." },
32
+ mode: { type: "string", enum: ["lexical", "semantic", "hybrid"], description: "Default hybrid." },
33
+ namespace: { type: "string", description: "Key-prefix filter, e.g. 'archive/pgsql-hackers'." },
34
+ tags: { type: "array", items: { type: "string" }, description: "Restrict to facts carrying all these tags." },
35
+ limit: { type: "number", description: "Max results (default 20)." },
36
+ },
37
+ required: ["query"],
38
+ },
39
+ handler: (a) => {
40
+ const o = { mode: a.mode, namespace: a.namespace, tags: a.tags, limit: a.limit };
41
+ return factStore.searchFacts(a.query, o, access(a));
42
+ },
43
+ });
44
+ tools.push({
45
+ name: "facts_similar",
46
+ description: "Given a fact you already have, return the semantically nearest other facts " +
47
+ "(vector kNN over the fact's stored embedding — no query text, no re-embedding). Anchor excluded.",
48
+ parameters: {
49
+ type: "object",
50
+ properties: {
51
+ scopeKey: { type: "string", description: "The anchor fact's scope_key." },
52
+ k: { type: "number", description: "Top-k neighbours (default 8)." },
53
+ minScore: { type: "number", description: "Drop neighbours below this cosine score (0..1)." },
54
+ },
55
+ required: ["scopeKey"],
56
+ },
57
+ handler: (a) => factStore.similarFacts(a.scopeKey, { k: a.k, minScore: a.minScore }, access(a)),
58
+ });
59
+ tools.push({
60
+ name: "facts_read",
61
+ description: "Read facts directly by key/scopeKeys/tags/scope — no ranking. Use scopeKeys to resolve the " +
62
+ "evidence arrays returned by graph tools back into facts. scope: 'descendants' reads the spawn-tree (lineage).",
63
+ parameters: {
64
+ type: "object",
65
+ properties: {
66
+ keyPattern: { type: "string", description: "Key prefix/pattern filter." },
67
+ scopeKeys: { type: "array", items: { type: "string" }, description: "Explicit fact scope_keys (e.g. graph evidence). Inaccessible/unknown keys are silently omitted." },
68
+ tags: { type: "array", items: { type: "string" } },
69
+ scope: { type: "string", enum: ["accessible", "shared", "session", "descendants"], description: "Default accessible." },
70
+ limit: { type: "number" },
71
+ },
72
+ },
73
+ handler: (a) => factStore.readFacts({ keyPattern: a.keyPattern, scopeKeys: a.scopeKeys, tags: a.tags, scope: a.scope, limit: a.limit }, access(a)),
74
+ });
75
+ // ── §3 graph read (reader + harvester) — only when a graph is configured ──
76
+ if (graphStore) {
77
+ tools.push({
78
+ name: "graph_search_nodes",
79
+ description: "Find / expand graph nodes. RESOLVE step (does this entity exist? — use kind + nameLike before every " +
80
+ "create) and PIVOT step (seeds = fact scope_keys from facts_search pivot via EVIDENCED_BY; node keys " +
81
+ "expand directly; depth bounds the hops). Each hit carries its EVIDENCED_BY fact scope_keys — feed " +
82
+ "evidence straight into facts_read.",
83
+ parameters: {
84
+ type: "object",
85
+ properties: {
86
+ kind: { type: "string", description: "Free-text node kind filter ('person', 'patch', …)." },
87
+ nameLike: { type: "string", description: "Lexical match on node name or any alias. The resolve key." },
88
+ seeds: { type: "array", items: { type: "string" }, description: "Fact scope_keys OR node keys to anchor from." },
89
+ depth: { type: "number", description: "Hops to expand from seeds (1..5)." },
90
+ limit: { type: "number" },
91
+ },
92
+ },
93
+ handler: (a) => graphStore.searchGraphNodes({ kind: a.kind, nameLike: a.nameLike, seeds: a.seeds, depth: a.depth, limit: a.limit }, access(a)),
94
+ });
95
+ tools.push({
96
+ name: "graph_search_edges",
97
+ description: "Find edges, two ways only: anchor-and-explore (set fromKey and/or toKey) or exact-predicate " +
98
+ "(predicate/predicateKey, exact equality — no fuzzy match).",
99
+ parameters: {
100
+ type: "object",
101
+ properties: {
102
+ predicate: { type: "string", description: "EXACT predicate text." },
103
+ predicateKey: { type: "string", description: "EXACT normalized key (preferred, surface-stable)." },
104
+ fromKey: { type: "string" },
105
+ toKey: { type: "string" },
106
+ minConfidence: { type: "number" },
107
+ limit: { type: "number" },
108
+ },
109
+ },
110
+ handler: (a) => graphStore.searchGraphEdges(a, access(a)),
111
+ });
112
+ tools.push({
113
+ name: "graph_neighbourhood",
114
+ description: "Bounded subgraph around a node — 'show me everything connected to X'.",
115
+ parameters: {
116
+ type: "object",
117
+ properties: {
118
+ nodeKey: { type: "string" },
119
+ depth: { type: "number", description: "Hops (clamped 1..5)." },
120
+ },
121
+ required: ["nodeKey", "depth"],
122
+ },
123
+ handler: (a) => graphStore.graphNeighbourhood(a.nodeKey, a.depth, access(a)),
124
+ });
125
+ }
126
+ if (role !== "harvester")
127
+ return tools;
128
+ // Crawl-queue + graph-write are harvester tools that only make sense with a
129
+ // graph to harvest into (07 §1.5) — gate them on graphStore presence.
130
+ if (graphStore) {
131
+ // ── §2 crawl queue (HARVESTER ONLY — privileged, all scopes) ─────────────
132
+ tools.push({
133
+ name: "facts_read_uncrawled",
134
+ description: "PRIVILEGED work queue: facts not yet incorporated into the graph (new or edited since last crawl), " +
135
+ "across ALL scopes. Keep each fact's scopeKey and etag — both are the receipt facts_mark_crawled needs.",
136
+ parameters: {
137
+ type: "object",
138
+ properties: {
139
+ namespace: { type: "string", description: "Restrict the queue to a key prefix." },
140
+ limit: { type: "number", description: "Max facts this batch (default 20)." },
141
+ },
142
+ },
143
+ handler: (a) => factStore.readUncrawledFacts({ namespace: a.namespace, limit: a.limit, embeddedOnly }),
144
+ });
145
+ tools.push({
146
+ name: "facts_mark_crawled",
147
+ description: "Stamp facts as incorporated so they leave the queue. Pass each fact's scopeKey and etag from facts_read_uncrawled. " +
148
+ "A skipped stamp means the fact was already marked, changed since your read, or no longer exists; re-read if needed.",
149
+ parameters: {
150
+ type: "object",
151
+ properties: {
152
+ stamps: {
153
+ type: "array",
154
+ items: {
155
+ type: "object",
156
+ properties: {
157
+ scopeKey: { type: "string" },
158
+ etag: { type: "number" },
159
+ },
160
+ required: ["scopeKey", "etag"],
161
+ },
162
+ },
163
+ },
164
+ required: ["stamps"],
165
+ },
166
+ handler: (a) => factStore.markFactsCrawled(a.stamps),
167
+ });
168
+ tools.push({
169
+ name: "graph_remove_evidence",
170
+ description: "Reconcile a soft-deleted fact with the graph: remove this fact scopeKey from node EVIDENCED_BY anchors " +
171
+ "and edge evidence arrays, deleting graph nodes/edges that become evidence-less. Call this for " +
172
+ "facts_read_uncrawled rows where deletedAt/deleted_at is set, then mark the fact crawled with its scopeKey and etag.",
173
+ parameters: {
174
+ type: "object",
175
+ properties: {
176
+ scopeKey: { type: "string", description: "Deleted fact scopeKey from facts_read_uncrawled." },
177
+ namespace: { type: "string", description: "Optional graph namespace guard." },
178
+ },
179
+ required: ["scopeKey"],
180
+ },
181
+ handler: (a) => graphStore.removeGraphEvidence(a.scopeKey, { namespace: a.namespace }),
182
+ });
183
+ // ── §4 graph write (HARVESTER ONLY) ──────────────────────────────────────
184
+ tools.push({
185
+ name: "graph_upsert_node",
186
+ description: "Create a node, or merge into an existing one (idempotent; aliases and evidence union in). " +
187
+ "RESOLVE BEFORE YOU CREATE: graph_search_nodes by nameLike first. Evidence is optional in the " +
188
+ "contract, but pass the source fact's scope_key — it is the provenance, and what makes " +
189
+ "reinforcement dedup work. The graph is SHARED: anything you incorporate is visible to every reader.",
190
+ parameters: {
191
+ type: "object",
192
+ properties: {
193
+ kind: { type: "string", description: "Free text: person, patch, code_file, thread…" },
194
+ name: { type: "string", description: "Canonical surface form." },
195
+ aliases: { type: "array", items: { type: "string" }, description: "Other observed surface forms." },
196
+ evidence: { type: "array", items: { type: "string" }, description: "Fact scope_keys justifying this node. Pass the source fact." },
197
+ },
198
+ required: ["kind", "name"],
199
+ },
200
+ handler: (a) => graphStore.upsertGraphNode({ ...a, agentId }),
201
+ });
202
+ tools.push({
203
+ name: "graph_upsert_edge",
204
+ description: "Assert a free-text relationship, or reinforce an existing one. Re-stating the same " +
205
+ "(fromKey, predicate, toKey) does not duplicate — it bumps observations and combines confidence " +
206
+ "(noisy-OR) ONLY when you bring new evidence; same-evidence replays are harmless no-ops. " +
207
+ "Pass RESOLVED nodeKeys, not raw names. One verb per relationship.",
208
+ parameters: {
209
+ type: "object",
210
+ properties: {
211
+ fromKey: { type: "string", description: "Source node key (from graph_upsert_node / graph_search_nodes)." },
212
+ toKey: { type: "string", description: "Target node key." },
213
+ predicate: { type: "string", description: "Free-text verb, e.g. 'revives argument from'." },
214
+ confidence: { type: "number", description: "0..1 for THIS observation (default 1.0)." },
215
+ evidence: { type: "array", items: { type: "string" }, description: "Fact scope_keys justifying the edge. Pass the source fact." },
216
+ },
217
+ required: ["fromKey", "toKey", "predicate"],
218
+ },
219
+ handler: (a) => graphStore.upsertGraphEdge({ ...a, agentId }),
220
+ });
221
+ tools.push({
222
+ name: "graph_merge_nodes",
223
+ description: "Entity resolution: fold a duplicate node into the survivor (union aliases, repoint edges, delete " +
224
+ "the duplicate). Use when you discover two nodes are the same entity after the fact.",
225
+ parameters: {
226
+ type: "object",
227
+ properties: {
228
+ fromKey: { type: "string", description: "Duplicate to remove." },
229
+ intoKey: { type: "string", description: "Survivor to keep." },
230
+ reason: { type: "string", description: "Why they're the same (audit)." },
231
+ },
232
+ required: ["fromKey", "intoKey", "reason"],
233
+ },
234
+ handler: async (a) => { await graphStore.mergeGraphNodes(a.fromKey, a.intoKey, a.reason); return { merged: true }; },
235
+ });
236
+ tools.push({
237
+ name: "graph_delete_node",
238
+ description: "Remove a node and all its edges (DETACH DELETE). No cascade to facts.",
239
+ parameters: {
240
+ type: "object",
241
+ properties: { nodeKey: { type: "string" } },
242
+ required: ["nodeKey"],
243
+ },
244
+ handler: async (a) => ({ deleted: await graphStore.deleteGraphNode(a.nodeKey) }),
245
+ });
246
+ tools.push({
247
+ name: "graph_delete_edge",
248
+ description: "Remove one exact edge triple. Returns whether something matched.",
249
+ parameters: {
250
+ type: "object",
251
+ properties: {
252
+ fromKey: { type: "string" },
253
+ toKey: { type: "string" },
254
+ predicateKey: { type: "string" },
255
+ },
256
+ required: ["fromKey", "toKey", "predicateKey"],
257
+ },
258
+ handler: async (a) => ({ deleted: await graphStore.deleteGraphEdge(a.fromKey, a.toKey, a.predicateKey) }),
259
+ });
260
+ }
261
+ return tools;
262
+ }
263
+ //# sourceMappingURL=agent-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-tools.js","sourceRoot":"","sources":["../../src/agent-tools.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,oFAAoF;AACpF,8EAA8E;AAC9E,qCAAqC;AACrC,EAAE;AACF,kFAAkF;AAClF,4EAA4E;AAC5E,gFAAgF;AAChF,6EAA6E;AAC7E,iFAAiF;AACjF,EAAE;AACF,6EAA6E;AAC7E,mCAAmC;AAsCnC,MAAM,UAAU,gBAAgB,CAC5B,SAA6B,EAC7B,UAAkC,EAClC,OAA0B,EAAE;IAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,WAAW,CAAC;IAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC;IAChD,MAAM,KAAK,GAAgB,EAAE,CAAC;IAE9B,4EAA4E;IAE5E,KAAK,CAAC,IAAI,CAAC;QACP,IAAI,EAAE,cAAc;QACpB,WAAW,EACP,sFAAsF;YACtF,gGAAgG;YAChG,yGAAyG;YACzG,wEAAwE;QAC5E,UAAU,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACR,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sFAAsF,EAAE;gBAC9H,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE;gBACjG,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kDAAkD,EAAE;gBAC9F,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,4CAA4C,EAAE;gBAC7G,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;aACtE;YACD,QAAQ,EAAE,CAAC,OAAO,CAAC;SACtB;QACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACX,MAAM,CAAC,GAAe,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;YAC7F,OAAO,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC;KACJ,CAAC,CAAC;IAEH,KAAK,CAAC,IAAI,CAAC;QACP,IAAI,EAAE,eAAe;QACrB,WAAW,EACP,6EAA6E;YAC7E,kGAAkG;QACtG,UAAU,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACR,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8BAA8B,EAAE;gBACzE,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+BAA+B,EAAE;gBACnE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iDAAiD,EAAE;aAC/F;YACD,QAAQ,EAAE,CAAC,UAAU,CAAC;SACzB;QACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;KAClG,CAAC,CAAC;IAEH,KAAK,CAAC,IAAI,CAAC;QACP,IAAI,EAAE,YAAY;QAClB,WAAW,EACP,6FAA6F;YAC7F,+GAA+G;QACnH,UAAU,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACR,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;gBACzE,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,iGAAiG,EAAE;gBACvK,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;gBAClD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE;gBACvH,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;aAC5B;SACJ;QACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAC/B,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,EAClG,MAAM,CAAC,CAAC,CAAC,CAAC;KACjB,CAAC,CAAC;IAEH,6EAA6E;IAE7E,IAAI,UAAU,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACP,sGAAsG;gBACtG,sGAAsG;gBACtG,oGAAoG;gBACpG,oCAAoC;YACxC,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oDAAoD,EAAE;oBAC3F,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2DAA2D,EAAE;oBACtG,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,8CAA8C,EAAE;oBAChH,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mCAAmC,EAAE;oBAC3E,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC5B;aACJ;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAW,CAAC,gBAAgB,CACxC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,EACtF,MAAM,CAAC,CAAC,CAAC,CAAC;SACjB,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACP,8FAA8F;gBAC9F,4DAA4D;YAChE,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE;oBACnE,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mDAAmD,EAAE;oBAClG,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACjC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC5B;aACJ;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAW,CAAC,gBAAgB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;SAC7D,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,qBAAqB;YAC3B,WAAW,EAAE,uEAAuE;YACpF,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE;iBACjE;gBACD,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;aACjC;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;SAChF,CAAC,CAAC;IACH,CAAC;IAED,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAEvC,4EAA4E;IAC5E,sEAAsE;IACtE,IAAI,UAAU,EAAE,CAAC;QAEjB,4EAA4E;QAE5E,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,sBAAsB;YAC5B,WAAW,EACP,qGAAqG;gBACrG,wGAAwG;YAC5G,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qCAAqC,EAAE;oBACjF,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oCAAoC,EAAE;iBAC/E;aACJ;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC;SACzG,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACP,qHAAqH;gBACrH,qHAAqH;YACzH,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,MAAM,EAAE;wBACJ,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE;4BACH,IAAI,EAAE,QAAQ;4BACd,UAAU,EAAE;gCACR,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gCAC5B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;6BAC3B;4BACD,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;yBACjC;qBACJ;iBACJ;gBACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;aACvB;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC;SACvD,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,uBAAuB;YAC7B,WAAW,EACP,yGAAyG;gBACzG,gGAAgG;gBAChG,qHAAqH;YACzH,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kDAAkD,EAAE;oBAC7F,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iCAAiC,EAAE;iBAChF;gBACD,QAAQ,EAAE,CAAC,UAAU,CAAC;aACzB;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC;SACzF,CAAC,CAAC;QAEH,4EAA4E;QAE5E,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,mBAAmB;YACzB,WAAW,EACP,4FAA4F;gBAC5F,+FAA+F;gBAC/F,wFAAwF;gBACxF,qGAAqG;YACzG,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8CAA8C,EAAE;oBACrF,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE;oBAChE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,+BAA+B,EAAE;oBACnG,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,6DAA6D,EAAE;iBACrI;gBACD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;aAC7B;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAW,CAAC,eAAe,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC;SACjE,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,mBAAmB;YACzB,WAAW,EACP,qFAAqF;gBACrF,iGAAiG;gBACjG,0FAA0F;gBAC1F,mEAAmE;YACvE,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gEAAgE,EAAE;oBAC1G,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE;oBAC1D,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;oBAC3F,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0CAA0C,EAAE;oBACvF,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,4DAA4D,EAAE;iBACpI;gBACD,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC;aAC9C;YACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAW,CAAC,eAAe,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC;SACjE,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,mBAAmB;YACzB,WAAW,EACP,mGAAmG;gBACnG,qFAAqF;YACzF,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE;oBAChE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE;oBAC7D,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+BAA+B,EAAE;iBAC3E;gBACD,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC;aAC7C;YACD,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,GAAG,MAAM,UAAW,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;SACxH,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,mBAAmB;YACzB,WAAW,EAAE,uEAAuE;YACpF,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;gBAC3C,QAAQ,EAAE,CAAC,SAAS,CAAC;aACxB;YACD,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,UAAW,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;SACpF,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,mBAAmB;YACzB,WAAW,EAAE,kEAAkE;YAC/E,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBACnC;gBACD,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC;aACjD;YACD,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,UAAW,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;SAC7G,CAAC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC"}
@@ -0,0 +1,93 @@
1
+ import type { EmbeddingEndpointConfig } from "./types.js";
2
+ export type { EmbeddingEndpointConfig } from "./types.js";
3
+ import type { PoolConfig } from "pg";
4
+ export interface HorizonFactsConfig {
5
+ /** PostgreSQL/HorizonDB connection string. */
6
+ connectionString: string;
7
+ /** Relational schema for the facts table + procs. Default "horizon_facts". */
8
+ schema?: string;
9
+ /** AGE graph name. Default "horizon_facts". */
10
+ graphName?: string;
11
+ /**
12
+ * Embedding endpoint (provider-neutral, see EmbeddingEndpointConfig).
13
+ * Required for semantic/hybrid search and for this provider's in-DB embedding
14
+ * pipeline. Lexical + graph work without it. When provided, initialize()
15
+ * configures AND auto-starts the eternal in-DB embed loop (idempotent +
16
+ * advisory-locked, so repeated/concurrent instantiations never duplicate it).
17
+ */
18
+ embedding?: EmbeddingEndpointConfig;
19
+ /**
20
+ * Dimension of the vector(N) column, fixed at migration time. Defaults to
21
+ * embedding?.dim ?? 1536. configureEmbedder() rejects endpoints whose dim
22
+ * differs (a dim change requires a column migration + full re-embed).
23
+ */
24
+ embeddingDim?: number;
25
+ /**
26
+ * Vector ANN index method. "diskann" uses Azure's pg_diskann (must be
27
+ * allow-listed in the cluster's azure.extensions parameter group); "hnsw"
28
+ * uses pgvector's built-in HNSW; "auto" (default) prefers diskann and falls
29
+ * back to hnsw when pg_diskann is unavailable.
30
+ */
31
+ annIndex?: "diskann" | "hnsw" | "auto";
32
+ /**
33
+ * Max pool connections. Default 16. The graph layer issues several
34
+ * sequential Cypher statements per upsert, and the harvester fires graph
35
+ * tool calls in parallel; a small pool serializes those behind a few
36
+ * connections and connection-queue wait dominates latency (a pool of 3 was
37
+ * measured ~2-4x slower than 10 under concurrency=8). Override per-cluster
38
+ * with HORIZON_POOL_MAX, bearing in mind the cluster's max_connections.
39
+ */
40
+ poolMax?: number;
41
+ /** AAD / managed-identity auth (mirrors PilotSwarm's PgFactStore). */
42
+ useManagedIdentity?: boolean;
43
+ aadUser?: string;
44
+ /**
45
+ * Relational schema OWNED BY THE GRAPH PROVIDER for the namespace registry
46
+ * sidecar (graph-fact-search enhancements). MUST differ from the AGE graph
47
+ * name (`graphName`) — `create_graph()` creates a Postgres schema named after
48
+ * the graph, so reusing it would put the sidecar INSIDE the AGE-managed
49
+ * schema (droppable by drop_graph). Also distinct from the facts `schema`,
50
+ * since the graph store may run against a database with no facts schema at
51
+ * all. Defaults to `${graphName}_registry`. Override with
52
+ * HORIZON_GRAPH_REGISTRY_SCHEMA.
53
+ */
54
+ registrySchema?: string;
55
+ /**
56
+ * TTL (ms) for the in-provider namespace-list cache. The namespace set is
57
+ * small and changes rarely; the provider caches a single full snapshot and
58
+ * filters in memory. Writes invalidate the snapshot in-process; other
59
+ * workers converge within the TTL. Default 60000 (one minute). Set 0 to
60
+ * disable caching (always reload) — useful in tests.
61
+ */
62
+ namespaceCacheTtlMs?: number;
63
+ }
64
+ export declare const DEFAULT_SCHEMA = "horizon_facts";
65
+ export declare const DEFAULT_GRAPH = "horizon_facts";
66
+ export declare const DEFAULT_POOL_MAX = 16;
67
+ export declare const DEFAULT_CONNECTION_TIMEOUT_MS = 15000;
68
+ export declare const DEFAULT_NAMESPACE_CACHE_TTL_MS = 60000;
69
+ /** Resolve config from explicit values, falling back to HORIZON_* env vars. */
70
+ export declare function resolveConfig(partial?: Partial<HorizonFactsConfig>): HorizonFactsConfig;
71
+ /**
72
+ * Build a `pg.PoolConfig` from a HorizonDB connection string, normalizing TLS
73
+ * the same way the PilotSwarm SDK's `pg-pool-factory` does for the CMS / facts
74
+ * pools, so a HorizonDB URL behaves here exactly like `DATABASE_URL` does there.
75
+ *
76
+ * Why this is needed: HorizonDB requires SSL, but its certificate chain is not in
77
+ * Node's default trust store, and `pg` v8 treats `sslmode=require` (and
78
+ * `prefer` / `verify-ca` / `verify-full`) as `verify-full` — which rejects the
79
+ * chain with `self-signed certificate in certificate chain`. The SDK factory
80
+ * handles this for `DATABASE_URL` by stripping `sslmode` from the URL and setting
81
+ * `ssl: { rejectUnauthorized: false }` on the pool config. This provider builds
82
+ * its OWN raw pools (it keeps its runtime dep surface to `pg` only and does not
83
+ * import the SDK), so without this helper a natural `?sslmode=require` URL fails
84
+ * here while the identical URL works for `DATABASE_URL` — the asymmetry that
85
+ * previously forced callers to hand-append `uselibpqcompat=true`.
86
+ *
87
+ * Encrypt-but-don't-verify matches libpq's `sslmode=require` semantics and the
88
+ * SDK's existing posture (this is not a new weakening — it is the same choice the
89
+ * CMS/facts pools already make). For full CA verification against a non-preview
90
+ * cluster, present a trusted chain and pass a URL without an `sslmode` param.
91
+ */
92
+ export declare function buildPoolConfig(connectionString: string, max: number): PoolConfig;
93
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAErC,MAAM,WAAW,kBAAkB;IAC/B,8CAA8C;IAC9C,gBAAgB,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IACvC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,cAAc,kBAAkB,CAAC;AAC9C,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAC7C,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,8BAA8B,QAAS,CAAC;AAyBrD,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,OAAO,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAAG,kBAAkB,CAgC3F;AAKD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAwBjF"}