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.
- package/README.md +154 -0
- package/dist/src/agent-tools.d.ts +32 -0
- package/dist/src/agent-tools.d.ts.map +1 -0
- package/dist/src/agent-tools.js +263 -0
- package/dist/src/agent-tools.js.map +1 -0
- package/dist/src/config.d.ts +93 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +120 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/db-retry.d.ts +21 -0
- package/dist/src/db-retry.d.ts.map +1 -0
- package/dist/src/db-retry.js +94 -0
- package/dist/src/db-retry.js.map +1 -0
- package/dist/src/embedding-client.d.ts +13 -0
- package/dist/src/embedding-client.d.ts.map +1 -0
- package/dist/src/embedding-client.js +79 -0
- package/dist/src/embedding-client.js.map +1 -0
- package/dist/src/graph-model.d.ts +49 -0
- package/dist/src/graph-model.d.ts.map +1 -0
- package/dist/src/graph-model.js +112 -0
- package/dist/src/graph-model.js.map +1 -0
- package/dist/src/graph-queries.d.ts +83 -0
- package/dist/src/graph-queries.d.ts.map +1 -0
- package/dist/src/graph-queries.js +727 -0
- package/dist/src/graph-queries.js.map +1 -0
- package/dist/src/graph-store.d.ts +57 -0
- package/dist/src/graph-store.d.ts.map +1 -0
- package/dist/src/graph-store.js +327 -0
- package/dist/src/graph-store.js.map +1 -0
- package/dist/src/horizon-migrator.d.ts +49 -0
- package/dist/src/horizon-migrator.d.ts.map +1 -0
- package/dist/src/horizon-migrator.js +231 -0
- package/dist/src/horizon-migrator.js.map +1 -0
- package/dist/src/horizon-store.d.ts +116 -0
- package/dist/src/horizon-store.d.ts.map +1 -0
- package/dist/src/horizon-store.js +563 -0
- package/dist/src/horizon-store.js.map +1 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +21 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/preconditions.d.ts +16 -0
- package/dist/src/preconditions.d.ts.map +1 -0
- package/dist/src/preconditions.js +99 -0
- package/dist/src/preconditions.js.map +1 -0
- package/dist/src/query-builder.d.ts +37 -0
- package/dist/src/query-builder.d.ts.map +1 -0
- package/dist/src/query-builder.js +82 -0
- package/dist/src/query-builder.js.map +1 -0
- package/dist/src/sql-util.d.ts +20 -0
- package/dist/src/sql-util.d.ts.map +1 -0
- package/dist/src/sql-util.js +40 -0
- package/dist/src/sql-util.js.map +1 -0
- package/dist/src/types.d.ts +16 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +35 -0
- package/dist/src/types.js.map +1 -0
- package/migrations/0001_facts_table.sql +77 -0
- package/migrations/0002_indexes.sql +27 -0
- package/migrations/0003_age_bootstrap.sql +13 -0
- package/migrations/0004_facts_procs.sql +253 -0
- package/migrations/0005_embedder_workflow.sql +119 -0
- package/migrations/0006_facts_read_uncrawled_embedded_gate.sql +33 -0
- package/migrations/0007_embed_key_value_text.sql +122 -0
- package/migrations/0008_embedder_failure_recovery.sql +409 -0
- package/migrations/0009_search_text_full_value.sql +52 -0
- package/migrations/0010_minimal_two_loop_embedder.sql +392 -0
- package/migrations/0011_unified_api_embedder_workflow.sql +273 -0
- package/migrations/0012_facts_soft_delete.sql +587 -0
- package/migrations/0013_graph_namespaces.sql +60 -0
- 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":"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"}
|
|
@@ -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"}
|