@retinue/agentkit 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -277
- package/dist/adapters/audio/openai.d.ts +36 -0
- package/dist/adapters/audio/openai.js +136 -0
- package/dist/adapters/bullmq/lock.d.ts +26 -2
- package/dist/adapters/embeddings/openai.d.ts +45 -0
- package/dist/adapters/embeddings/openai.js +109 -0
- package/dist/adapters/memory/connections.d.ts +10 -0
- package/dist/adapters/memory/connections.js +87 -0
- package/dist/adapters/memory/graph.d.ts +17 -0
- package/dist/adapters/memory/graph.js +267 -0
- package/dist/adapters/memory/index.d.ts +3 -0
- package/dist/adapters/memory/index.js +3 -0
- package/dist/adapters/memory/rate-limit.d.ts +14 -0
- package/dist/adapters/memory/rate-limit.js +28 -0
- package/dist/adapters/memory/runtime.js +0 -0
- package/dist/adapters/postgres/connections.d.ts +15 -0
- package/dist/adapters/postgres/connections.js +134 -0
- package/dist/adapters/postgres/graph.d.ts +24 -0
- package/dist/adapters/postgres/graph.js +325 -0
- package/dist/adapters/postgres/index.d.ts +2 -0
- package/dist/adapters/postgres/index.js +2 -0
- package/dist/adapters/postgres/migrations.js +198 -0
- package/dist/adapters/postgres/run-store.js +26 -2
- package/dist/adapters/postgres/schema.d.ts +61 -2
- package/dist/adapters/postgres/schema.js +95 -10
- package/dist/adapters/redis/index.d.ts +1 -0
- package/dist/adapters/redis/index.js +1 -0
- package/dist/adapters/redis/rate-limit.d.ts +46 -0
- package/dist/adapters/redis/rate-limit.js +55 -0
- package/dist/adapters/supabase/index.d.ts +15 -0
- package/dist/adapters/supabase/index.js +15 -0
- package/dist/adapters/supabase/rls.js +33 -0
- package/dist/agents/agent.d.ts +99 -1
- package/dist/agents/agent.js +215 -16
- package/dist/agents/define.js +24 -4
- package/dist/agents/engine.d.ts +48 -0
- package/dist/agents/engine.js +370 -13
- package/dist/audio/index.d.ts +151 -0
- package/dist/audio/index.js +183 -0
- package/dist/capabilities/index.d.ts +5 -1
- package/dist/capabilities/index.js +23 -0
- package/dist/capabilities/runtime.d.ts +8 -0
- package/dist/connections/cipher.d.ts +103 -0
- package/dist/connections/cipher.js +141 -0
- package/dist/connections/index.d.ts +156 -0
- package/dist/connections/index.js +72 -0
- package/dist/connections/oauth/client.d.ts +84 -0
- package/dist/connections/oauth/client.js +141 -0
- package/dist/connections/oauth/index.d.ts +166 -0
- package/dist/connections/oauth/index.js +226 -0
- package/dist/connections/oauth/service.d.ts +90 -0
- package/dist/connections/oauth/service.js +132 -0
- package/dist/connections/pause.d.ts +68 -0
- package/dist/connections/pause.js +97 -0
- package/dist/connections/resolver.d.ts +59 -0
- package/dist/connections/resolver.js +138 -0
- package/dist/connections/resume.d.ts +64 -0
- package/dist/connections/resume.js +99 -0
- package/dist/connections/vault-cipher.d.ts +95 -0
- package/dist/connections/vault-cipher.js +206 -0
- package/dist/core/budget.d.ts +55 -0
- package/dist/core/budget.js +56 -0
- package/dist/core/content-parts.d.ts +26 -2
- package/dist/core/content-parts.js +1 -0
- package/dist/core/context.d.ts +25 -0
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.js +15 -0
- package/dist/core/events.d.ts +110 -2
- package/dist/core/events.js +33 -2
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/validation.js +19 -0
- package/dist/documents/index.d.ts +14 -0
- package/dist/documents/parsers/text.d.ts +16 -0
- package/dist/documents/parsers/text.js +54 -2
- package/dist/entries/connections.d.ts +10 -0
- package/dist/entries/connections.js +10 -0
- package/dist/entries/guardrails.d.ts +14 -0
- package/dist/entries/guardrails.js +14 -0
- package/dist/entries/knowledge.d.ts +17 -0
- package/dist/entries/knowledge.js +16 -0
- package/dist/entries/mcp-server.d.ts +9 -0
- package/dist/entries/mcp-server.js +9 -0
- package/dist/entries/runtime.d.ts +10 -0
- package/dist/entries/runtime.js +8 -0
- package/dist/entries/testing.d.ts +28 -0
- package/dist/entries/testing.js +28 -0
- package/dist/graphql/resolvers.d.ts +7 -1
- package/dist/graphql/resolvers.js +20 -0
- package/dist/graphql/schema.d.ts +1 -1
- package/dist/graphql/schema.js +44 -0
- package/dist/guardrails/index.d.ts +115 -0
- package/dist/guardrails/index.js +108 -0
- package/dist/guardrails/moderation.d.ts +53 -0
- package/dist/guardrails/moderation.js +75 -0
- package/dist/guardrails/pii.d.ts +75 -0
- package/dist/guardrails/pii.js +193 -0
- package/dist/knowledge/communities.d.ts +166 -0
- package/dist/knowledge/communities.js +377 -0
- package/dist/knowledge/graph-global.d.ts +153 -0
- package/dist/knowledge/graph-global.js +246 -0
- package/dist/knowledge/graph-retrieval.d.ts +150 -0
- package/dist/knowledge/graph-retrieval.js +303 -0
- package/dist/knowledge/graph.d.ts +223 -0
- package/dist/knowledge/graph.js +369 -0
- package/dist/knowledge/index.d.ts +25 -0
- package/dist/knowledge/index.js +37 -1
- package/dist/knowledge/navigate.d.ts +89 -0
- package/dist/knowledge/navigate.js +107 -0
- package/dist/knowledge/retrieval.d.ts +98 -5
- package/dist/knowledge/retrieval.js +158 -28
- package/dist/mcp-server/index.d.ts +168 -0
- package/dist/mcp-server/index.js +175 -0
- package/dist/models/extraction.d.ts +73 -0
- package/dist/models/extraction.js +118 -0
- package/dist/models/index.d.ts +40 -1
- package/dist/models/index.js +22 -1
- package/dist/models/pricing.d.ts +2 -0
- package/dist/models/pricing.js +17 -1
- package/dist/models/provider-factory.js +9 -7
- package/dist/models/streaming.d.ts +111 -1
- package/dist/models/streaming.js +184 -3
- package/dist/persistence/index.d.ts +230 -0
- package/dist/persistence/index.js +2 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +12 -0
- package/dist/runtime/worker.d.ts +36 -1
- package/dist/runtime/worker.js +37 -4
- package/dist/security/checklist.js +55 -0
- package/dist/security/findings.js +18 -9
- package/dist/server/bin.d.ts +19 -0
- package/dist/server/bin.js +226 -0
- package/dist/server/boot.d.ts +20 -2
- package/dist/server/boot.js +29 -3
- package/dist/server/cli.d.ts +13 -0
- package/dist/server/cli.js +7 -4
- package/dist/server/doctor.d.ts +74 -0
- package/dist/server/doctor.js +280 -0
- package/dist/skills/catalogue.d.ts +49 -0
- package/dist/skills/catalogue.js +61 -0
- package/dist/skills/context.d.ts +61 -0
- package/dist/skills/context.js +107 -0
- package/dist/skills/index.d.ts +2 -0
- package/dist/skills/index.js +2 -0
- package/dist/telemetry/spans.js +17 -0
- package/dist/testing/conformance/artifact-exports.d.ts +20 -0
- package/dist/testing/conformance/artifact-exports.js +172 -0
- package/dist/testing/conformance/artifacts.d.ts +11 -0
- package/dist/testing/conformance/artifacts.js +316 -0
- package/dist/testing/conformance/audio.d.ts +38 -0
- package/dist/testing/conformance/audio.js +145 -0
- package/dist/testing/conformance/capability.d.ts +23 -0
- package/dist/testing/conformance/capability.js +27 -0
- package/dist/testing/conformance/checkpoint-store.d.ts +15 -0
- package/dist/testing/conformance/checkpoint-store.js +72 -0
- package/dist/testing/conformance/connections.d.ts +14 -0
- package/dist/testing/conformance/connections.js +171 -0
- package/dist/testing/conformance/conversation-store.d.ts +10 -0
- package/dist/testing/conformance/conversation-store.js +60 -0
- package/dist/testing/conformance/evaluation.d.ts +10 -0
- package/dist/testing/conformance/evaluation.js +295 -0
- package/dist/testing/conformance/files.d.ts +19 -0
- package/dist/testing/conformance/files.js +454 -0
- package/dist/testing/conformance/flows.d.ts +16 -0
- package/dist/testing/conformance/flows.js +193 -0
- package/dist/testing/conformance/graph.d.ts +22 -0
- package/dist/testing/conformance/graph.js +500 -0
- package/dist/testing/conformance/hitl.d.ts +25 -0
- package/dist/testing/conformance/hitl.js +523 -0
- package/dist/testing/conformance/index.d.ts +163 -0
- package/dist/testing/conformance/index.js +321 -0
- package/dist/testing/conformance/invariants.d.ts +23 -0
- package/dist/testing/conformance/invariants.js +80 -0
- package/dist/testing/conformance/knowledge.d.ts +41 -0
- package/dist/testing/conformance/knowledge.js +592 -0
- package/dist/testing/conformance/parents.d.ts +50 -0
- package/dist/testing/conformance/parents.js +39 -0
- package/dist/testing/conformance/rate-limit.d.ts +25 -0
- package/dist/testing/conformance/rate-limit.js +71 -0
- package/dist/testing/conformance/records.d.ts +36 -0
- package/dist/testing/conformance/records.js +400 -0
- package/dist/testing/conformance/rollups.d.ts +30 -0
- package/dist/testing/conformance/rollups.js +425 -0
- package/dist/testing/conformance/run-coordinator.d.ts +27 -0
- package/dist/testing/conformance/run-coordinator.js +150 -0
- package/dist/testing/conformance/run-event-log.d.ts +9 -0
- package/dist/testing/conformance/run-event-log.js +138 -0
- package/dist/testing/conformance/run-store.d.ts +12 -0
- package/dist/testing/conformance/run-store.js +294 -0
- package/dist/testing/conformance/session-state.d.ts +38 -0
- package/dist/testing/conformance/session-state.js +198 -0
- package/dist/testing/conformance/usage-limits.d.ts +10 -0
- package/dist/testing/conformance/usage-limits.js +309 -0
- package/dist/testing/conformance.d.ts +8 -0
- package/dist/testing/conformance.js +8 -0
- package/dist/testing/memory-backend.d.ts +39 -0
- package/dist/testing/memory-backend.js +41 -0
- package/dist/testing/pglite.d.ts +55 -0
- package/dist/testing/pglite.js +78 -0
- package/dist/testing/stub-model.d.ts +58 -0
- package/dist/testing/stub-model.js +71 -0
- package/dist/testing/supabase-storage-double.d.ts +35 -0
- package/dist/testing/supabase-storage-double.js +101 -0
- package/dist/toolkit/files.d.ts +125 -0
- package/dist/toolkit/files.js +320 -0
- package/dist/toolkit/http.d.ts +27 -0
- package/dist/toolkit/http.js +48 -0
- package/dist/toolkit/index.d.ts +31 -1
- package/dist/toolkit/index.js +26 -1
- package/dist/toolkit/mime.d.ts +97 -0
- package/dist/toolkit/mime.js +233 -0
- package/dist/toolkit/sandbox.d.ts +119 -0
- package/dist/toolkit/sandbox.js +239 -0
- package/dist/toolkit/ssrf.d.ts +139 -0
- package/dist/toolkit/ssrf.js +349 -0
- package/dist/toolkit/vendor.d.ts +91 -0
- package/dist/toolkit/vendor.js +153 -0
- package/dist/toolkit/web.d.ts +13 -0
- package/dist/toolkit/web.js +7 -1
- package/dist/tools/budget.d.ts +28 -0
- package/dist/tools/budget.js +35 -0
- package/dist/tools/credentials.d.ts +316 -0
- package/dist/tools/credentials.js +369 -0
- package/dist/tools/define.d.ts +45 -0
- package/dist/tools/define.js +26 -0
- package/dist/tools/find.d.ts +109 -0
- package/dist/tools/find.js +210 -0
- package/dist/tools/index.d.ts +35 -2
- package/dist/tools/index.js +11 -0
- package/dist/tools/library/audio.d.ts +63 -0
- package/dist/tools/library/audio.js +148 -0
- package/dist/tools/library/fs.d.ts +24 -0
- package/dist/tools/library/fs.js +102 -0
- package/dist/tools/library/index.d.ts +37 -2
- package/dist/tools/library/index.js +47 -0
- package/dist/tools/library/shell.d.ts +45 -0
- package/dist/tools/library/shell.js +70 -0
- package/dist/tools/meta-tools.js +8 -0
- package/dist/tools/registry.d.ts +146 -0
- package/dist/tools/registry.js +250 -4
- package/dist/usage/index.d.ts +21 -0
- package/dist/usage/index.js +1 -0
- package/dist/usage/rate-limit.d.ts +122 -0
- package/dist/usage/rate-limit.js +131 -0
- package/dist/usage/recorder.js +3 -0
- package/package.json +33 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ConnectionStore` over Postgres — task #261.
|
|
3
|
+
*
|
|
4
|
+
* Pure SQL over `SqlExecutor`, like every other adapter here. The store never sees a plaintext secret: it
|
|
5
|
+
* writes the four columns of a `SealedSecret` and reads them back, so an adapter cannot leak something it does
|
|
6
|
+
* not have.
|
|
7
|
+
*
|
|
8
|
+
* Every query is tenant-scoped in its `WHERE`, and there is no `findById(id)` — governing principle 1. That is
|
|
9
|
+
* not belt-and-braces over RLS; it is the layer RLS is defence in depth *for*, and #91 is the reminder that a
|
|
10
|
+
* method accepting a tenant scope and not using it typechecks perfectly.
|
|
11
|
+
*/
|
|
12
|
+
import { AgentPlatformError } from "../../core/errors.js";
|
|
13
|
+
const iso = (value) => (value instanceof Date ? value.toISOString() : value);
|
|
14
|
+
const toConnection = (row) => ({
|
|
15
|
+
id: row.id,
|
|
16
|
+
kind: row.kind,
|
|
17
|
+
provider: row.provider,
|
|
18
|
+
...(row.label === null ? {} : { label: row.label }),
|
|
19
|
+
mode: row.mode,
|
|
20
|
+
scheme: row.scheme,
|
|
21
|
+
...(row.metadata === null || Object.keys(row.metadata).length === 0 ? {} : { metadata: row.metadata }),
|
|
22
|
+
...(row.granted_scopes === null ? {} : { grantedScopes: row.granted_scopes }),
|
|
23
|
+
sealed: {
|
|
24
|
+
keyId: row.secret_key_id,
|
|
25
|
+
algorithm: row.secret_algorithm,
|
|
26
|
+
nonce: row.secret_nonce,
|
|
27
|
+
ciphertext: row.secret_ciphertext,
|
|
28
|
+
},
|
|
29
|
+
...(row.expires_at === null ? {} : { expiresAt: iso(row.expires_at) }),
|
|
30
|
+
createdAt: iso(row.created_at),
|
|
31
|
+
updatedAt: iso(row.updated_at),
|
|
32
|
+
...(row.revoked_at === null ? {} : { revokedAt: iso(row.revoked_at) }),
|
|
33
|
+
});
|
|
34
|
+
const COLUMNS = `id, kind, provider, label, mode, scheme, metadata, granted_scopes,
|
|
35
|
+
secret_key_id, secret_algorithm, secret_nonce, secret_ciphertext,
|
|
36
|
+
expires_at, created_at, updated_at, revoked_at`;
|
|
37
|
+
const notFound = (id) => new AgentPlatformError({ code: "not_found", message: `no connection "${id}" for this tenant`, retryable: false });
|
|
38
|
+
export const createPostgresConnectionStore = (sql) => ({
|
|
39
|
+
async create({ tenantId, connection }) {
|
|
40
|
+
const rows = await sql.query(`INSERT INTO connections (
|
|
41
|
+
tenant_id, id, kind, provider, label, mode, scheme, metadata, granted_scopes,
|
|
42
|
+
secret_key_id, secret_algorithm, secret_nonce, secret_ciphertext, expires_at
|
|
43
|
+
) VALUES ($1,$2,$14,$3,$4,$5,$6,$7::jsonb,$8,$9,$10,$11,$12,$13)
|
|
44
|
+
-- No upsert: a second create for the same id is a caller bug, and silently overwriting a credential is
|
|
45
|
+
-- the worst possible resolution of it.
|
|
46
|
+
RETURNING ${COLUMNS}`, [
|
|
47
|
+
tenantId,
|
|
48
|
+
connection.id,
|
|
49
|
+
connection.provider,
|
|
50
|
+
connection.label ?? null,
|
|
51
|
+
connection.mode,
|
|
52
|
+
connection.scheme,
|
|
53
|
+
JSON.stringify(connection.metadata ?? {}),
|
|
54
|
+
connection.grantedScopes === undefined ? null : [...connection.grantedScopes],
|
|
55
|
+
connection.sealed.keyId,
|
|
56
|
+
connection.sealed.algorithm,
|
|
57
|
+
connection.sealed.nonce,
|
|
58
|
+
connection.sealed.ciphertext,
|
|
59
|
+
connection.expiresAt ?? null,
|
|
60
|
+
connection.kind ?? "connection",
|
|
61
|
+
]);
|
|
62
|
+
const row = rows[0];
|
|
63
|
+
if (row === undefined)
|
|
64
|
+
throw new AgentPlatformError({ code: "conflict", message: `connection "${connection.id}" already exists`, retryable: false });
|
|
65
|
+
return toConnection(row);
|
|
66
|
+
},
|
|
67
|
+
async get({ tenantId, id, kind }) {
|
|
68
|
+
// `revoked_at IS NULL` here rather than in the caller: a revoked connection reading as present is how a
|
|
69
|
+
// withdrawn credential gets used once more.
|
|
70
|
+
const rows = await sql.query(
|
|
71
|
+
// `kind` in the predicate, not filtered afterwards: a caller that did not ask for an app registration
|
|
72
|
+
// must not receive one, and a post-filter is a place somebody later removes.
|
|
73
|
+
`SELECT ${COLUMNS} FROM connections
|
|
74
|
+
WHERE tenant_id = $1 AND id = $2 AND revoked_at IS NULL AND kind = $3`, [tenantId, id, kind ?? "connection"]);
|
|
75
|
+
return rows[0] === undefined ? null : toConnection(rows[0]);
|
|
76
|
+
},
|
|
77
|
+
async list({ tenantId, provider, kind }) {
|
|
78
|
+
const rows = await sql.query(`SELECT ${COLUMNS} FROM connections
|
|
79
|
+
WHERE tenant_id = $1 AND revoked_at IS NULL AND ($2::text IS NULL OR provider = $2) AND kind = $3
|
|
80
|
+
ORDER BY created_at, id`, [tenantId, provider ?? null, kind ?? "connection"]);
|
|
81
|
+
return rows.map(toConnection);
|
|
82
|
+
},
|
|
83
|
+
async update({ tenantId, id, patch }) {
|
|
84
|
+
/**
|
|
85
|
+
* `COALESCE` per column, so an absent field means "leave it" rather than "set it to null".
|
|
86
|
+
*
|
|
87
|
+
* The alternative — building the SET list from the present keys — is how a patch with no fields becomes
|
|
88
|
+
* invalid SQL, and how a field named in one place and not another silently stops being updatable.
|
|
89
|
+
*/
|
|
90
|
+
const rows = await sql.query(`UPDATE connections SET
|
|
91
|
+
label = COALESCE($3, label),
|
|
92
|
+
secret_key_id = COALESCE($4, secret_key_id),
|
|
93
|
+
secret_algorithm = COALESCE($5, secret_algorithm),
|
|
94
|
+
secret_nonce = COALESCE($6, secret_nonce),
|
|
95
|
+
secret_ciphertext = COALESCE($7, secret_ciphertext),
|
|
96
|
+
metadata = COALESCE($8::jsonb, metadata),
|
|
97
|
+
granted_scopes = COALESCE($9, granted_scopes),
|
|
98
|
+
expires_at = COALESCE($10, expires_at),
|
|
99
|
+
updated_at = now()
|
|
100
|
+
WHERE tenant_id = $1 AND id = $2 AND revoked_at IS NULL
|
|
101
|
+
RETURNING ${COLUMNS}`, [
|
|
102
|
+
tenantId,
|
|
103
|
+
id,
|
|
104
|
+
patch.label ?? null,
|
|
105
|
+
patch.sealed?.keyId ?? null,
|
|
106
|
+
patch.sealed?.algorithm ?? null,
|
|
107
|
+
patch.sealed?.nonce ?? null,
|
|
108
|
+
patch.sealed?.ciphertext ?? null,
|
|
109
|
+
patch.metadata === undefined ? null : JSON.stringify(patch.metadata),
|
|
110
|
+
patch.grantedScopes === undefined ? null : [...patch.grantedScopes],
|
|
111
|
+
patch.expiresAt ?? null,
|
|
112
|
+
]);
|
|
113
|
+
if (rows[0] === undefined)
|
|
114
|
+
throw notFound(id);
|
|
115
|
+
return toConnection(rows[0]);
|
|
116
|
+
},
|
|
117
|
+
async revoke({ tenantId, id }) {
|
|
118
|
+
const rows = await sql.query(`UPDATE connections SET revoked_at = now(), updated_at = now()
|
|
119
|
+
WHERE tenant_id = $1 AND id = $2 AND revoked_at IS NULL
|
|
120
|
+
RETURNING id`, [tenantId, id]);
|
|
121
|
+
if (rows[0] !== undefined)
|
|
122
|
+
return;
|
|
123
|
+
// Idempotent: already revoked is success, so a retried disconnect need not distinguish it from a failure.
|
|
124
|
+
const existing = await sql.query(`SELECT id FROM connections WHERE tenant_id = $1 AND id = $2`, [tenantId, id]);
|
|
125
|
+
if (existing[0] === undefined)
|
|
126
|
+
throw notFound(id);
|
|
127
|
+
},
|
|
128
|
+
async purge({ tenantId }) {
|
|
129
|
+
// The one hard delete — `docs/18`. A soft-deleted credential is still a credential.
|
|
130
|
+
const rows = await sql.query(`DELETE FROM connections WHERE tenant_id = $1 RETURNING id`, [tenantId]);
|
|
131
|
+
return rows.length;
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=connections.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres `GraphStore` — REQ-064 (#270), task #271.
|
|
3
|
+
*
|
|
4
|
+
* **Contributions are the stored truth; the entity and relationship tables are a cache of their merge.**
|
|
5
|
+
*
|
|
6
|
+
* That split is the whole design, and it is worth being explicit about why the obvious alternative is wrong.
|
|
7
|
+
* Storing merged entities alone and mutating them per source needs reference counting — "how many sources still
|
|
8
|
+
* name this entity" — and reference counting is the thing everybody gets wrong under concurrency. Storing what
|
|
9
|
+
* each source asserted and rebuilding the merge makes pruning a consequence rather than a bookkeeping exercise:
|
|
10
|
+
* an entity exists precisely while some contribution names it, and nothing has to remember why.
|
|
11
|
+
*
|
|
12
|
+
* The merge is recomputed in TypeScript rather than SQL. A `jsonb_array_elements` aggregation could do it, but
|
|
13
|
+
* the merge rule — lexicographically-first canonical name, longest description, unioned surface forms, weight
|
|
14
|
+
* as the provenance count — would then exist twice, in two languages, and the reference adapter's copy is the
|
|
15
|
+
* one the conformance suite tests. One rule in one place is worth a round trip.
|
|
16
|
+
*
|
|
17
|
+
* Provenance is enforced by a `CHECK` constraint as well as here, deliberately. The application check gives a
|
|
18
|
+
* message that explains itself; the constraint is what still holds when somebody writes to these tables from a
|
|
19
|
+
* migration or a console.
|
|
20
|
+
*/
|
|
21
|
+
import type { GraphStore } from "../../persistence/index.js";
|
|
22
|
+
import type { SqlExecutor } from "./sql.js";
|
|
23
|
+
export declare const createPostgresGraphStore: (sql: SqlExecutor) => GraphStore;
|
|
24
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres `GraphStore` — REQ-064 (#270), task #271.
|
|
3
|
+
*
|
|
4
|
+
* **Contributions are the stored truth; the entity and relationship tables are a cache of their merge.**
|
|
5
|
+
*
|
|
6
|
+
* That split is the whole design, and it is worth being explicit about why the obvious alternative is wrong.
|
|
7
|
+
* Storing merged entities alone and mutating them per source needs reference counting — "how many sources still
|
|
8
|
+
* name this entity" — and reference counting is the thing everybody gets wrong under concurrency. Storing what
|
|
9
|
+
* each source asserted and rebuilding the merge makes pruning a consequence rather than a bookkeeping exercise:
|
|
10
|
+
* an entity exists precisely while some contribution names it, and nothing has to remember why.
|
|
11
|
+
*
|
|
12
|
+
* The merge is recomputed in TypeScript rather than SQL. A `jsonb_array_elements` aggregation could do it, but
|
|
13
|
+
* the merge rule — lexicographically-first canonical name, longest description, unioned surface forms, weight
|
|
14
|
+
* as the provenance count — would then exist twice, in two languages, and the reference adapter's copy is the
|
|
15
|
+
* one the conformance suite tests. One rule in one place is worth a round trip.
|
|
16
|
+
*
|
|
17
|
+
* Provenance is enforced by a `CHECK` constraint as well as here, deliberately. The application check gives a
|
|
18
|
+
* message that explains itself; the constraint is what still holds when somebody writes to these tables from a
|
|
19
|
+
* migration or a console.
|
|
20
|
+
*/
|
|
21
|
+
import { AgentPlatformError } from "../../core/errors.js";
|
|
22
|
+
const iso = (value) => (value instanceof Date ? value.toISOString() : value);
|
|
23
|
+
const toEntity = (row) => ({
|
|
24
|
+
id: row.id,
|
|
25
|
+
name: row.name,
|
|
26
|
+
type: row.type,
|
|
27
|
+
...(row.description === null ? {} : { description: row.description }),
|
|
28
|
+
surfaceForms: row.surface_forms,
|
|
29
|
+
provenance: row.provenance,
|
|
30
|
+
});
|
|
31
|
+
const toRelationship = (row) => ({
|
|
32
|
+
id: row.id,
|
|
33
|
+
fromId: row.from_id,
|
|
34
|
+
toId: row.to_id,
|
|
35
|
+
type: row.type,
|
|
36
|
+
...(row.description === null ? {} : { description: row.description }),
|
|
37
|
+
weight: Number(row.weight),
|
|
38
|
+
provenance: row.provenance,
|
|
39
|
+
});
|
|
40
|
+
const toCommunity = (row) => ({
|
|
41
|
+
id: row.id,
|
|
42
|
+
level: Number(row.level),
|
|
43
|
+
entityIds: row.entity_ids,
|
|
44
|
+
relationshipIds: row.relationship_ids,
|
|
45
|
+
chunkIds: row.chunk_ids,
|
|
46
|
+
fingerprint: row.fingerprint,
|
|
47
|
+
...(row.summary === null ? {} : { summary: row.summary }),
|
|
48
|
+
...(row.summary_fingerprint === null ? {} : { summaryFingerprint: row.summary_fingerprint }),
|
|
49
|
+
...(row.summarised_at === null ? {} : { summarisedAt: iso(row.summarised_at) }),
|
|
50
|
+
});
|
|
51
|
+
const uniqueSorted = (values) => [...new Set(values)].sort();
|
|
52
|
+
const byId = (a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
53
|
+
/** Rebuilds the merged graph from every contribution. Mirrors `mergeContributions` — see the header. */
|
|
54
|
+
const mergeAll = (contributions) => {
|
|
55
|
+
const entities = new Map();
|
|
56
|
+
const relationships = new Map();
|
|
57
|
+
for (const contribution of contributions) {
|
|
58
|
+
for (const entity of contribution.entities) {
|
|
59
|
+
const existing = entities.get(entity.id);
|
|
60
|
+
if (existing === undefined) {
|
|
61
|
+
entities.set(entity.id, entity);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const description = (entity.description ?? "").length > (existing.description ?? "").length ? entity.description : existing.description;
|
|
65
|
+
entities.set(entity.id, {
|
|
66
|
+
id: entity.id,
|
|
67
|
+
name: existing.name < entity.name ? existing.name : entity.name,
|
|
68
|
+
type: existing.type,
|
|
69
|
+
...(description === undefined ? {} : { description }),
|
|
70
|
+
surfaceForms: uniqueSorted([...existing.surfaceForms, ...entity.surfaceForms]),
|
|
71
|
+
provenance: uniqueSorted([...existing.provenance, ...entity.provenance]),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const contribution of contributions) {
|
|
76
|
+
for (const edge of contribution.relationships) {
|
|
77
|
+
const existing = relationships.get(edge.id);
|
|
78
|
+
if (existing === undefined) {
|
|
79
|
+
relationships.set(edge.id, edge);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const description = (edge.description ?? "").length > (existing.description ?? "").length ? edge.description : existing.description;
|
|
83
|
+
const provenance = uniqueSorted([...existing.provenance, ...edge.provenance]);
|
|
84
|
+
relationships.set(edge.id, {
|
|
85
|
+
...existing,
|
|
86
|
+
...(description === undefined ? {} : { description }),
|
|
87
|
+
weight: provenance.length,
|
|
88
|
+
provenance,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const kept = [...relationships.values()].filter((edge) => entities.has(edge.fromId) && entities.has(edge.toId));
|
|
93
|
+
return { entities: [...entities.values()].sort(byId), relationships: kept.sort(byId) };
|
|
94
|
+
};
|
|
95
|
+
const assertProvenance = (contribution) => {
|
|
96
|
+
for (const row of [...contribution.entities, ...contribution.relationships]) {
|
|
97
|
+
if (row.provenance.length === 0)
|
|
98
|
+
throw new AgentPlatformError({
|
|
99
|
+
code: "invalid_input",
|
|
100
|
+
message: `${row.id} has no provenance; a graph claim with no chunk behind it is one the model would cite`,
|
|
101
|
+
retryable: false,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
export const createPostgresGraphStore = (sql) => {
|
|
106
|
+
/** Every contribution for a tenant, which is what the merge is derived from. */
|
|
107
|
+
const contributionsOf = async (tenantId) => {
|
|
108
|
+
const rows = await sql.query(`SELECT entities, relationships FROM knowledge_graph_contributions
|
|
109
|
+
WHERE tenant_id = $1 ORDER BY source_type, source_id`, [tenantId]);
|
|
110
|
+
return rows.map((row) => ({
|
|
111
|
+
entities: (row.entities ?? []),
|
|
112
|
+
relationships: (row.relationships ?? []),
|
|
113
|
+
}));
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Rewrites the merged tables from the contributions.
|
|
117
|
+
*
|
|
118
|
+
* Delete-then-insert rather than an upsert-and-sweep, for the reason `replaceSource` is: a partial merge is a
|
|
119
|
+
* graph that is half old and half new, and no reader can tell. Callers run this inside the same unit of work
|
|
120
|
+
* as the contribution write.
|
|
121
|
+
*/
|
|
122
|
+
const rebuild = async (tenantId) => {
|
|
123
|
+
const merged = mergeAll(await contributionsOf(tenantId));
|
|
124
|
+
await sql.query(`DELETE FROM knowledge_graph_relationships WHERE tenant_id = $1`, [tenantId]);
|
|
125
|
+
await sql.query(`DELETE FROM knowledge_graph_entities WHERE tenant_id = $1`, [tenantId]);
|
|
126
|
+
for (const entity of merged.entities) {
|
|
127
|
+
await sql.query(`INSERT INTO knowledge_graph_entities
|
|
128
|
+
(tenant_id, id, name, type, description, surface_forms, provenance)
|
|
129
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`, [tenantId, entity.id, entity.name, entity.type, entity.description ?? null, entity.surfaceForms, entity.provenance]);
|
|
130
|
+
}
|
|
131
|
+
for (const edge of merged.relationships) {
|
|
132
|
+
await sql.query(`INSERT INTO knowledge_graph_relationships
|
|
133
|
+
(tenant_id, id, from_id, to_id, type, description, weight, provenance)
|
|
134
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [tenantId, edge.id, edge.fromId, edge.toId, edge.type, edge.description ?? null, edge.weight, edge.provenance]);
|
|
135
|
+
}
|
|
136
|
+
return { entities: merged.entities.length, relationships: merged.relationships.length };
|
|
137
|
+
};
|
|
138
|
+
const countEntities = async (tenantId) => {
|
|
139
|
+
const rows = await sql.query(`SELECT count(*)::text AS n FROM knowledge_graph_entities WHERE tenant_id = $1`, [tenantId]);
|
|
140
|
+
return Number(rows[0]?.n ?? 0);
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
async getSettings({ tenantId }) {
|
|
144
|
+
const rows = await sql.query(`SELECT enabled, updated_at FROM knowledge_graph_settings WHERE tenant_id = $1`, [tenantId]);
|
|
145
|
+
const row = rows[0];
|
|
146
|
+
// Absent means off. A tenant that has never been configured must not be able to start paying for
|
|
147
|
+
// extraction because a row was missing rather than false.
|
|
148
|
+
return row === undefined
|
|
149
|
+
? { enabled: false, updatedAt: "1970-01-01T00:00:00.000Z" }
|
|
150
|
+
: { enabled: row.enabled, updatedAt: iso(row.updated_at) };
|
|
151
|
+
},
|
|
152
|
+
async setEnabled({ tenantId, enabled, at }) {
|
|
153
|
+
await sql.query(`INSERT INTO knowledge_graph_settings (tenant_id, enabled, updated_at)
|
|
154
|
+
VALUES ($1, $2, $3)
|
|
155
|
+
ON CONFLICT (tenant_id) DO UPDATE SET enabled = EXCLUDED.enabled, updated_at = EXCLUDED.updated_at`, [tenantId, enabled, at]);
|
|
156
|
+
return { enabled, updatedAt: at };
|
|
157
|
+
},
|
|
158
|
+
async setSourceEnabled({ tenantId, sourceType, sourceId, enabled }) {
|
|
159
|
+
// Never consults the tenant switch — AC-2. The flag outlives it in both directions.
|
|
160
|
+
await sql.query(`INSERT INTO knowledge_graph_sources (tenant_id, source_type, source_id, enabled)
|
|
161
|
+
VALUES ($1, $2, $3, $4)
|
|
162
|
+
ON CONFLICT (tenant_id, source_type, source_id) DO UPDATE SET enabled = EXCLUDED.enabled`, [tenantId, sourceType, sourceId, enabled]);
|
|
163
|
+
},
|
|
164
|
+
async isSourceEnabled({ tenantId, sourceType, sourceId }) {
|
|
165
|
+
const rows = await sql.query(`SELECT enabled FROM knowledge_graph_sources
|
|
166
|
+
WHERE tenant_id = $1 AND source_type = $2 AND source_id = $3`, [tenantId, sourceType, sourceId]);
|
|
167
|
+
return rows[0]?.enabled === true;
|
|
168
|
+
},
|
|
169
|
+
async listEnabledSources({ tenantId, limit, cursor }) {
|
|
170
|
+
const offset = cursor === undefined ? 0 : Number.parseInt(cursor, 10) || 0;
|
|
171
|
+
const rows = await sql.query(`SELECT source_type, source_id FROM knowledge_graph_sources
|
|
172
|
+
WHERE tenant_id = $1 AND enabled
|
|
173
|
+
ORDER BY source_type, source_id
|
|
174
|
+
LIMIT $2 OFFSET $3`, [tenantId, limit + 1, offset]);
|
|
175
|
+
const items = rows.slice(0, limit).map((row) => ({
|
|
176
|
+
sourceType: row.source_type,
|
|
177
|
+
sourceId: row.source_id,
|
|
178
|
+
}));
|
|
179
|
+
return rows.length > limit ? { items, nextCursor: String(offset + limit) } : { items };
|
|
180
|
+
},
|
|
181
|
+
async replaceSourceGraph({ tenantId, sourceType, sourceId, contribution }) {
|
|
182
|
+
assertProvenance(contribution);
|
|
183
|
+
const before = await countEntities(tenantId);
|
|
184
|
+
if (contribution.entities.length === 0 && contribution.relationships.length === 0) {
|
|
185
|
+
// An empty contribution is a withdrawal, not an empty row: leaving `{"entities":[]}` behind would make
|
|
186
|
+
// every future merge read a row that says nothing.
|
|
187
|
+
await sql.query(`DELETE FROM knowledge_graph_contributions WHERE tenant_id = $1 AND source_type = $2 AND source_id = $3`, [tenantId, sourceType, sourceId]);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
await sql.query(`INSERT INTO knowledge_graph_contributions (tenant_id, source_type, source_id, entities, relationships)
|
|
191
|
+
VALUES ($1, $2, $3, $4::jsonb, $5::jsonb)
|
|
192
|
+
ON CONFLICT (tenant_id, source_type, source_id)
|
|
193
|
+
DO UPDATE SET entities = EXCLUDED.entities,
|
|
194
|
+
relationships = EXCLUDED.relationships,
|
|
195
|
+
updated_at = now()`, [tenantId, sourceType, sourceId, JSON.stringify(contribution.entities), JSON.stringify(contribution.relationships)]);
|
|
196
|
+
}
|
|
197
|
+
const rebuilt = await rebuild(tenantId);
|
|
198
|
+
return { ...rebuilt, pruned: Math.max(0, before - rebuilt.entities) };
|
|
199
|
+
},
|
|
200
|
+
async deleteSourceGraph({ tenantId, sourceType, sourceId }) {
|
|
201
|
+
const before = await countEntities(tenantId);
|
|
202
|
+
await sql.query(`DELETE FROM knowledge_graph_contributions WHERE tenant_id = $1 AND source_type = $2 AND source_id = $3`, [tenantId, sourceType, sourceId]);
|
|
203
|
+
const rebuilt = await rebuild(tenantId);
|
|
204
|
+
return { pruned: Math.max(0, before - rebuilt.entities) };
|
|
205
|
+
},
|
|
206
|
+
async getEntity({ tenantId, id }) {
|
|
207
|
+
const rows = await sql.query(`SELECT id, name, type, description, surface_forms, provenance
|
|
208
|
+
FROM knowledge_graph_entities WHERE tenant_id = $1 AND id = $2`, [tenantId, id]);
|
|
209
|
+
const row = rows[0];
|
|
210
|
+
return row === undefined ? null : toEntity(row);
|
|
211
|
+
},
|
|
212
|
+
async getEntities({ tenantId, ids }) {
|
|
213
|
+
if (ids.length === 0)
|
|
214
|
+
return [];
|
|
215
|
+
const rows = await sql.query(`SELECT id, name, type, description, surface_forms, provenance
|
|
216
|
+
FROM knowledge_graph_entities WHERE tenant_id = $1 AND id = ANY($2) ORDER BY id`, [tenantId, [...ids]]);
|
|
217
|
+
return rows.map(toEntity);
|
|
218
|
+
},
|
|
219
|
+
async resolveEntities({ tenantId, normalisedNames }) {
|
|
220
|
+
if (normalisedNames.length === 0)
|
|
221
|
+
return [];
|
|
222
|
+
const rows = await sql.query(`SELECT id, name, type, description, surface_forms, provenance
|
|
223
|
+
FROM knowledge_graph_entities
|
|
224
|
+
-- The id is \`type:normalisedName\`, and \`normaliseName\` cannot produce a colon, so everything after
|
|
225
|
+
-- the first one is the name. Matching on that keeps query-side and index-side resolution identical
|
|
226
|
+
-- without a second stored column to drift.
|
|
227
|
+
WHERE tenant_id = $1 AND substring(id from position(':' in id) + 1) = ANY($2)
|
|
228
|
+
ORDER BY id`, [tenantId, [...normalisedNames]]);
|
|
229
|
+
return rows.map(toEntity);
|
|
230
|
+
},
|
|
231
|
+
async listEntities({ tenantId, limit, cursor, type }) {
|
|
232
|
+
const offset = cursor === undefined ? 0 : Number.parseInt(cursor, 10) || 0;
|
|
233
|
+
const rows = await sql.query(`SELECT id, name, type, description, surface_forms, provenance
|
|
234
|
+
FROM knowledge_graph_entities
|
|
235
|
+
WHERE tenant_id = $1 AND ($2::text IS NULL OR type = $2)
|
|
236
|
+
ORDER BY id
|
|
237
|
+
LIMIT $3 OFFSET $4`, [tenantId, type ?? null, limit + 1, offset]);
|
|
238
|
+
const items = rows.slice(0, limit).map(toEntity);
|
|
239
|
+
return rows.length > limit ? { items, nextCursor: String(offset + limit) } : { items };
|
|
240
|
+
},
|
|
241
|
+
async neighbours({ tenantId, entityIds, limit }) {
|
|
242
|
+
if (entityIds.length === 0 || limit <= 0)
|
|
243
|
+
return [];
|
|
244
|
+
const rows = await sql.query(`SELECT id, from_id, to_id, type, description, weight, provenance
|
|
245
|
+
FROM knowledge_graph_relationships
|
|
246
|
+
WHERE tenant_id = $1 AND (from_id = ANY($2) OR to_id = ANY($2))
|
|
247
|
+
-- Heaviest first, then by id: a total order, so a truncated traversal is reproducible.
|
|
248
|
+
ORDER BY weight DESC, id
|
|
249
|
+
LIMIT $3`, [tenantId, [...entityIds], limit]);
|
|
250
|
+
return rows.map(toRelationship);
|
|
251
|
+
},
|
|
252
|
+
async replaceCommunities({ tenantId, communities }) {
|
|
253
|
+
// Read before the delete, so summaries can be carried over. One query rather than per-community lookups.
|
|
254
|
+
const previous = new Map((await sql.query(`SELECT id, summary, summary_fingerprint, summarised_at
|
|
255
|
+
FROM knowledge_graph_communities WHERE tenant_id = $1`, [tenantId])).map((row) => [row.id, row]));
|
|
256
|
+
await sql.query(`DELETE FROM knowledge_graph_communities WHERE tenant_id = $1`, [tenantId]);
|
|
257
|
+
let summariesKept = 0;
|
|
258
|
+
for (const community of communities) {
|
|
259
|
+
const before = previous.get(community.id);
|
|
260
|
+
// Identical membership only — see the memory adapter for why an id match is not enough.
|
|
261
|
+
const keep = before?.summary != null && before.summary_fingerprint === community.fingerprint;
|
|
262
|
+
if (keep)
|
|
263
|
+
summariesKept += 1;
|
|
264
|
+
await sql.query(`INSERT INTO knowledge_graph_communities
|
|
265
|
+
(tenant_id, id, level, entity_ids, relationship_ids, chunk_ids, fingerprint,
|
|
266
|
+
summary, summary_fingerprint, summarised_at)
|
|
267
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
|
|
268
|
+
tenantId,
|
|
269
|
+
community.id,
|
|
270
|
+
community.level,
|
|
271
|
+
community.entityIds,
|
|
272
|
+
community.relationshipIds,
|
|
273
|
+
community.chunkIds,
|
|
274
|
+
community.fingerprint,
|
|
275
|
+
keep ? before?.summary ?? null : null,
|
|
276
|
+
keep ? before?.summary_fingerprint ?? null : null,
|
|
277
|
+
keep ? (before?.summarised_at === null || before?.summarised_at === undefined ? null : iso(before.summarised_at)) : null,
|
|
278
|
+
]);
|
|
279
|
+
}
|
|
280
|
+
return { written: communities.length, summariesKept };
|
|
281
|
+
},
|
|
282
|
+
async listCommunities({ tenantId, limit, cursor, level }) {
|
|
283
|
+
const offset = cursor === undefined ? 0 : Number.parseInt(cursor, 10) || 0;
|
|
284
|
+
const rows = await sql.query(`SELECT id, level, entity_ids, relationship_ids, chunk_ids, fingerprint, summary, summary_fingerprint, summarised_at
|
|
285
|
+
FROM knowledge_graph_communities
|
|
286
|
+
WHERE tenant_id = $1 AND ($2::int IS NULL OR level = $2)
|
|
287
|
+
ORDER BY level, id
|
|
288
|
+
LIMIT $3 OFFSET $4`, [tenantId, level ?? null, limit + 1, offset]);
|
|
289
|
+
const items = rows.slice(0, limit).map(toCommunity);
|
|
290
|
+
return rows.length > limit ? { items, nextCursor: String(offset + limit) } : { items };
|
|
291
|
+
},
|
|
292
|
+
async getCommunity({ tenantId, id }) {
|
|
293
|
+
const rows = await sql.query(`SELECT id, level, entity_ids, relationship_ids, chunk_ids, fingerprint, summary, summary_fingerprint, summarised_at
|
|
294
|
+
FROM knowledge_graph_communities WHERE tenant_id = $1 AND id = $2`, [tenantId, id]);
|
|
295
|
+
const row = rows[0];
|
|
296
|
+
return row === undefined ? null : toCommunity(row);
|
|
297
|
+
},
|
|
298
|
+
async setCommunitySummary({ tenantId, id, summary, fingerprint, at }) {
|
|
299
|
+
await sql.query(`UPDATE knowledge_graph_communities
|
|
300
|
+
SET summary = $3, summary_fingerprint = $4, summarised_at = $5
|
|
301
|
+
WHERE tenant_id = $1 AND id = $2`, [tenantId, id, summary, fingerprint, at]);
|
|
302
|
+
},
|
|
303
|
+
async fingerprint({ tenantId }) {
|
|
304
|
+
// Read from the merged tables, not recomputed — the point is to prove *this adapter's stored state*
|
|
305
|
+
// matches, and recomputing would fingerprint the merge function instead of the database.
|
|
306
|
+
const entities = await sql.query(`SELECT id, name, type, description, surface_forms, provenance
|
|
307
|
+
FROM knowledge_graph_entities WHERE tenant_id = $1 ORDER BY id`, [tenantId]);
|
|
308
|
+
const relationships = await sql.query(`SELECT id, from_id, to_id, type, description, weight, provenance
|
|
309
|
+
FROM knowledge_graph_relationships WHERE tenant_id = $1 ORDER BY id`, [tenantId]);
|
|
310
|
+
return [
|
|
311
|
+
...entities.map((row) => [
|
|
312
|
+
"E",
|
|
313
|
+
row.id,
|
|
314
|
+
row.name,
|
|
315
|
+
row.type,
|
|
316
|
+
row.description ?? "",
|
|
317
|
+
[...row.surface_forms].sort().join(","),
|
|
318
|
+
[...row.provenance].sort().join(","),
|
|
319
|
+
].join("")),
|
|
320
|
+
...relationships.map((row) => ["R", row.id, row.type, String(Number(row.weight)), row.description ?? "", [...row.provenance].sort().join(",")].join("")),
|
|
321
|
+
].join("\n");
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -27,9 +27,11 @@ export * from "./files.js";
|
|
|
27
27
|
export * from "./artifacts.js";
|
|
28
28
|
export * from "./artifact-exports.js";
|
|
29
29
|
export * from "./knowledge.js";
|
|
30
|
+
export * from "./graph.js";
|
|
30
31
|
export * from "./rollups.js";
|
|
31
32
|
export * from "./evaluation.js";
|
|
32
33
|
export * from "./retention.js";
|
|
33
34
|
export * from "./file-content.js";
|
|
34
35
|
export * from "./flows.js";
|
|
36
|
+
export * from "./connections.js";
|
|
35
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -27,9 +27,11 @@ export * from "./files.js";
|
|
|
27
27
|
export * from "./artifacts.js";
|
|
28
28
|
export * from "./artifact-exports.js";
|
|
29
29
|
export * from "./knowledge.js";
|
|
30
|
+
export * from "./graph.js";
|
|
30
31
|
export * from "./rollups.js";
|
|
31
32
|
export * from "./evaluation.js";
|
|
32
33
|
export * from "./retention.js";
|
|
33
34
|
export * from "./file-content.js";
|
|
34
35
|
export * from "./flows.js";
|
|
36
|
+
export * from "./connections.js";
|
|
35
37
|
//# sourceMappingURL=index.js.map
|