amalfa 1.0.36 → 1.0.37
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/package.json +10 -7
- package/src/daemon/sonar-agent.ts +6 -124
- package/src/daemon/sonar-server.ts +133 -0
- package/src/resonance/DatabaseFactory.ts +1 -1
- package/src/resonance/drizzle/README.md +25 -0
- package/src/resonance/drizzle/migrations/0000_happy_thaddeus_ross.sql +30 -0
- package/src/resonance/drizzle/migrations/meta/0000_snapshot.json +199 -0
- package/src/resonance/drizzle/migrations/meta/_journal.json +13 -0
- package/src/resonance/drizzle/schema.ts +60 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amalfa",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.37",
|
|
4
4
|
"description": "Local-first knowledge graph engine for AI agents. Transforms markdown into searchable memory with MCP protocol.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/pjsvis/amalfa#readme",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@biomejs/biome": "2.3.8",
|
|
47
47
|
"@types/bun": "1.3.4",
|
|
48
|
-
"only-allow": "
|
|
49
|
-
"pino-pretty": "
|
|
50
|
-
"typescript": "
|
|
48
|
+
"only-allow": "1.2.2",
|
|
49
|
+
"pino-pretty": "13.1.3",
|
|
50
|
+
"typescript": "5.9.3"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"precommit": "bun run scripts/maintenance/pre-commit.ts",
|
|
@@ -64,9 +64,12 @@
|
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@modelcontextprotocol/sdk": "1.25.2",
|
|
67
|
+
"drizzle-kit": "0.31.8",
|
|
68
|
+
"drizzle-orm": "0.45.1",
|
|
67
69
|
"fastembed": "2.0.0",
|
|
68
|
-
"graphology": "
|
|
69
|
-
"graphology-library": "
|
|
70
|
-
"
|
|
70
|
+
"graphology": "0.26.0",
|
|
71
|
+
"graphology-library": "0.8.0",
|
|
72
|
+
"hono": "4.11.3",
|
|
73
|
+
"pino": "10.1.0"
|
|
71
74
|
}
|
|
72
75
|
}
|
|
@@ -24,27 +24,14 @@ import { ServiceLifecycle } from "@src/utils/ServiceLifecycle";
|
|
|
24
24
|
import { inferenceState } from "./sonar-inference";
|
|
25
25
|
import {
|
|
26
26
|
handleBatchEnhancement,
|
|
27
|
-
handleChat,
|
|
28
|
-
handleContextExtraction,
|
|
29
27
|
handleGardenTask,
|
|
30
|
-
handleMetadataEnhancement,
|
|
31
28
|
handleResearchTask,
|
|
32
|
-
handleResultReranking,
|
|
33
|
-
handleSearchAnalysis,
|
|
34
29
|
handleSynthesisTask,
|
|
35
30
|
handleTimelineTask,
|
|
36
31
|
type SonarContext,
|
|
37
32
|
} from "./sonar-logic";
|
|
38
33
|
import { getTaskModel } from "./sonar-strategies";
|
|
39
|
-
import type {
|
|
40
|
-
ChatRequest,
|
|
41
|
-
ChatSession,
|
|
42
|
-
MetadataEnhanceRequest,
|
|
43
|
-
SearchAnalyzeRequest,
|
|
44
|
-
SearchContextRequest,
|
|
45
|
-
SearchRerankRequest,
|
|
46
|
-
SonarTask,
|
|
47
|
-
} from "./sonar-types";
|
|
34
|
+
import type { ChatSession, SonarTask } from "./sonar-types";
|
|
48
35
|
|
|
49
36
|
const args = process.argv.slice(2);
|
|
50
37
|
const command = args[0] || "serve";
|
|
@@ -128,123 +115,18 @@ async function main() {
|
|
|
128
115
|
}
|
|
129
116
|
}
|
|
130
117
|
|
|
118
|
+
import { createSonarApp } from "./sonar-server";
|
|
119
|
+
|
|
131
120
|
/**
|
|
132
|
-
* Start Bun HTTP Server
|
|
121
|
+
* Start Bun HTTP Server via Hono
|
|
133
122
|
*/
|
|
134
123
|
function startServer(port: number) {
|
|
135
|
-
const corsHeaders = {
|
|
136
|
-
"Access-Control-Allow-Origin": "*",
|
|
137
|
-
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
138
|
-
"Access-Control-Allow-Headers": "Content-Type",
|
|
139
|
-
};
|
|
140
|
-
|
|
141
124
|
const context: SonarContext = { db, graphEngine, gardener, chatSessions };
|
|
125
|
+
const app = createSonarApp(context);
|
|
142
126
|
|
|
143
127
|
Bun.serve({
|
|
144
128
|
port,
|
|
145
|
-
|
|
146
|
-
if (req.method === "OPTIONS")
|
|
147
|
-
return new Response(null, { headers: corsHeaders });
|
|
148
|
-
const url = new URL(req.url);
|
|
149
|
-
|
|
150
|
-
// Health check
|
|
151
|
-
if (url.pathname === "/health") {
|
|
152
|
-
const cfg = await loadConfig();
|
|
153
|
-
const provider = cfg.sonar.cloud?.enabled ? "cloud" : "local";
|
|
154
|
-
const model = cfg.sonar.cloud?.enabled
|
|
155
|
-
? cfg.sonar.cloud.model
|
|
156
|
-
: inferenceState.ollamaModel || cfg.sonar.model;
|
|
157
|
-
return Response.json(
|
|
158
|
-
{
|
|
159
|
-
status: "ok",
|
|
160
|
-
ollama: inferenceState.ollamaAvailable,
|
|
161
|
-
provider,
|
|
162
|
-
model,
|
|
163
|
-
},
|
|
164
|
-
{ headers: corsHeaders },
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Chat endpoint
|
|
169
|
-
if (url.pathname === "/chat" && req.method === "POST") {
|
|
170
|
-
try {
|
|
171
|
-
const body = (await req.json()) as ChatRequest;
|
|
172
|
-
const { sessionId, message, model } = body;
|
|
173
|
-
const result = await handleChat(sessionId, message, context, model);
|
|
174
|
-
return Response.json(result, { headers: corsHeaders });
|
|
175
|
-
} catch (error) {
|
|
176
|
-
return Response.json(
|
|
177
|
-
{ error: String(error) },
|
|
178
|
-
{ status: 500, headers: corsHeaders },
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Metadata enhancement endpoint
|
|
184
|
-
if (url.pathname === "/metadata/enhance" && req.method === "POST") {
|
|
185
|
-
try {
|
|
186
|
-
const body = (await req.json()) as MetadataEnhanceRequest;
|
|
187
|
-
const { docId } = body;
|
|
188
|
-
await handleMetadataEnhancement(docId, context);
|
|
189
|
-
return Response.json({ status: "success" }, { headers: corsHeaders });
|
|
190
|
-
} catch (error) {
|
|
191
|
-
return Response.json(
|
|
192
|
-
{ error: String(error) },
|
|
193
|
-
{ status: 500, headers: corsHeaders },
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Graph Stats endpoint
|
|
199
|
-
if (url.pathname === "/graph/stats" && req.method === "GET") {
|
|
200
|
-
return Response.json(graphEngine.getStats(), { headers: corsHeaders });
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Search endpoints (analysis, rerank, context)
|
|
204
|
-
if (url.pathname === "/search/analyze" && req.method === "POST") {
|
|
205
|
-
try {
|
|
206
|
-
const body = (await req.json()) as SearchAnalyzeRequest;
|
|
207
|
-
const { query } = body;
|
|
208
|
-
const result = await handleSearchAnalysis(query, context);
|
|
209
|
-
return Response.json(result, { headers: corsHeaders });
|
|
210
|
-
} catch (error) {
|
|
211
|
-
return Response.json(
|
|
212
|
-
{ error: String(error) },
|
|
213
|
-
{ status: 500, headers: corsHeaders },
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (url.pathname === "/search/rerank" && req.method === "POST") {
|
|
219
|
-
try {
|
|
220
|
-
const body = (await req.json()) as SearchRerankRequest;
|
|
221
|
-
const { results, query, intent } = body;
|
|
222
|
-
const result = await handleResultReranking(results, query, intent);
|
|
223
|
-
return Response.json(result, { headers: corsHeaders });
|
|
224
|
-
} catch (error) {
|
|
225
|
-
return Response.json(
|
|
226
|
-
{ error: String(error) },
|
|
227
|
-
{ status: 500, headers: corsHeaders },
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (url.pathname === "/search/context" && req.method === "POST") {
|
|
233
|
-
try {
|
|
234
|
-
const body = (await req.json()) as SearchContextRequest;
|
|
235
|
-
const { result, query } = body;
|
|
236
|
-
const contextResult = await handleContextExtraction(result, query);
|
|
237
|
-
return Response.json(contextResult, { headers: corsHeaders });
|
|
238
|
-
} catch (error) {
|
|
239
|
-
return Response.json(
|
|
240
|
-
{ error: String(error) },
|
|
241
|
-
{ status: 500, headers: corsHeaders },
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return new Response("Not Found", { status: 404, headers: corsHeaders });
|
|
247
|
-
},
|
|
129
|
+
fetch: app.fetch,
|
|
248
130
|
});
|
|
249
131
|
|
|
250
132
|
log.info(`Server started on port ${port}`);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { loadConfig } from "@src/config/defaults";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { cors } from "hono/cors";
|
|
4
|
+
import { inferenceState } from "./sonar-inference";
|
|
5
|
+
import {
|
|
6
|
+
handleChat,
|
|
7
|
+
handleContextExtraction,
|
|
8
|
+
handleMetadataEnhancement,
|
|
9
|
+
handleResultReranking,
|
|
10
|
+
handleSearchAnalysis,
|
|
11
|
+
type SonarContext,
|
|
12
|
+
} from "./sonar-logic";
|
|
13
|
+
import type {
|
|
14
|
+
ChatRequest,
|
|
15
|
+
MetadataEnhanceRequest,
|
|
16
|
+
SearchAnalyzeRequest,
|
|
17
|
+
SearchContextRequest,
|
|
18
|
+
SearchRerankRequest,
|
|
19
|
+
} from "./sonar-types";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates the Hono application for the Sonar Agent
|
|
23
|
+
*/
|
|
24
|
+
export function createSonarApp(context: SonarContext) {
|
|
25
|
+
const app = new Hono();
|
|
26
|
+
|
|
27
|
+
// Global Middleware
|
|
28
|
+
app.use(
|
|
29
|
+
"*",
|
|
30
|
+
cors({
|
|
31
|
+
origin: "*",
|
|
32
|
+
allowMethods: ["GET", "POST", "OPTIONS"],
|
|
33
|
+
allowHeaders: ["Content-Type"],
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Health Check
|
|
39
|
+
*/
|
|
40
|
+
app.get("/health", async (c) => {
|
|
41
|
+
const cfg = await loadConfig();
|
|
42
|
+
const provider = cfg.sonar.cloud?.enabled ? "cloud" : "local";
|
|
43
|
+
const model = cfg.sonar.cloud?.enabled
|
|
44
|
+
? cfg.sonar.cloud.model
|
|
45
|
+
: inferenceState.ollamaModel || cfg.sonar.model;
|
|
46
|
+
|
|
47
|
+
return c.json({
|
|
48
|
+
status: "ok",
|
|
49
|
+
ollama: inferenceState.ollamaAvailable,
|
|
50
|
+
provider,
|
|
51
|
+
model,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Chat Interface
|
|
57
|
+
*/
|
|
58
|
+
app.post("/chat", async (c) => {
|
|
59
|
+
try {
|
|
60
|
+
const body = await c.req.json<ChatRequest>();
|
|
61
|
+
const { sessionId, message, model } = body;
|
|
62
|
+
const result = await handleChat(sessionId, message, context, model);
|
|
63
|
+
return c.json(result);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return c.json({ error: String(error) }, 500);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Metadata Enhancement
|
|
71
|
+
*/
|
|
72
|
+
app.post("/metadata/enhance", async (c) => {
|
|
73
|
+
try {
|
|
74
|
+
const body = await c.req.json<MetadataEnhanceRequest>();
|
|
75
|
+
const { docId } = body;
|
|
76
|
+
await handleMetadataEnhancement(docId, context);
|
|
77
|
+
return c.json({ status: "success" });
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return c.json({ error: String(error) }, 500);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Graph Stats
|
|
85
|
+
*/
|
|
86
|
+
app.get("/graph/stats", (c) => {
|
|
87
|
+
return c.json(context.graphEngine.getStats());
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Search: Query Analysis
|
|
92
|
+
*/
|
|
93
|
+
app.post("/search/analyze", async (c) => {
|
|
94
|
+
try {
|
|
95
|
+
const body = await c.req.json<SearchAnalyzeRequest>();
|
|
96
|
+
const { query } = body;
|
|
97
|
+
const result = await handleSearchAnalysis(query, context);
|
|
98
|
+
return c.json(result);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return c.json({ error: String(error) }, 500);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Search: Reranking
|
|
106
|
+
*/
|
|
107
|
+
app.post("/search/rerank", async (c) => {
|
|
108
|
+
try {
|
|
109
|
+
const body = await c.req.json<SearchRerankRequest>();
|
|
110
|
+
const { results, query, intent } = body;
|
|
111
|
+
const result = await handleResultReranking(results, query, intent);
|
|
112
|
+
return c.json(result);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return c.json({ error: String(error) }, 500);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Search: Context Extraction
|
|
120
|
+
*/
|
|
121
|
+
app.post("/search/context", async (c) => {
|
|
122
|
+
try {
|
|
123
|
+
const body = await c.req.json<SearchContextRequest>();
|
|
124
|
+
const { result, query } = body;
|
|
125
|
+
const contextResult = await handleContextExtraction(result, query);
|
|
126
|
+
return c.json(contextResult);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return c.json({ error: String(error) }, 500);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return app;
|
|
133
|
+
}
|
|
@@ -13,7 +13,7 @@ import { Database } from "bun:sqlite";
|
|
|
13
13
|
export const DatabaseFactory = {
|
|
14
14
|
/**
|
|
15
15
|
* Connects specifically to the main Resonance Graph database.
|
|
16
|
-
*
|
|
16
|
+
* Kept for convenience and backward compatibility.
|
|
17
17
|
*/
|
|
18
18
|
connectToResonance(
|
|
19
19
|
dbPath: string = ".amalfa/resonance.db",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Drizzle Schema Management
|
|
2
|
+
|
|
3
|
+
## ⚠️ CRITICAL ARCHITECTURE RULE
|
|
4
|
+
|
|
5
|
+
**Drizzle ORM is used HERE strictly for:**
|
|
6
|
+
1. **Schema Definition:** Single source of truth for table structures (`schema.ts`).
|
|
7
|
+
2. **Migrations:** Generating and running SQL migrations (`drizzle-kit`).
|
|
8
|
+
|
|
9
|
+
**Drizzle ORM is NOT used for:**
|
|
10
|
+
- **Runtime Queries:** Do NOT use Drizzle `db.select()` query builders in critical paths.
|
|
11
|
+
- **Data Access:** All runtime data access uses `bun:sqlite` with raw parameterized SQL (`db.prepare()`).
|
|
12
|
+
|
|
13
|
+
## Why?
|
|
14
|
+
Our **FAFCAS** (Fast, Autonomous, Fractionally-Connected Agent System) architecture demands raw IO performance. We treat SQLite as a raw storage engine. The overhead of an ORM query builder—even a light one—is unnecessary.
|
|
15
|
+
|
|
16
|
+
## ⛔ Agent Guardrail
|
|
17
|
+
If you are an AI Agent reading this:
|
|
18
|
+
- **DO NOT** refactor the existing `ResonanceDB` SQL queries to use Drizzle.
|
|
19
|
+
- **DO NOT** introduce Drizzle usage into `GraphEngine` or `VectorEngine`.
|
|
20
|
+
- **ONLY** modify this folder if the Database Schema (tables/columns) needs to change.
|
|
21
|
+
|
|
22
|
+
## Workflow for Schema Changes
|
|
23
|
+
1. Edit `schema.ts`
|
|
24
|
+
2. Run `bunx drizzle-kit generate`
|
|
25
|
+
3. Run migrations (automated via scripts)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
CREATE TABLE `edges` (
|
|
2
|
+
`source` text NOT NULL,
|
|
3
|
+
`target` text NOT NULL,
|
|
4
|
+
`type` text NOT NULL,
|
|
5
|
+
`confidence` real DEFAULT 1,
|
|
6
|
+
`veracity` real DEFAULT 1,
|
|
7
|
+
`context_source` text,
|
|
8
|
+
PRIMARY KEY(`source`, `target`, `type`)
|
|
9
|
+
);
|
|
10
|
+
--> statement-breakpoint
|
|
11
|
+
CREATE INDEX `idx_edges_source` ON `edges` (`source`);--> statement-breakpoint
|
|
12
|
+
CREATE INDEX `idx_edges_target` ON `edges` (`target`);--> statement-breakpoint
|
|
13
|
+
CREATE TABLE `ember_state` (
|
|
14
|
+
`file_path` text PRIMARY KEY NOT NULL,
|
|
15
|
+
`last_analyzed` text,
|
|
16
|
+
`sidecar_created` integer,
|
|
17
|
+
`confidence` real
|
|
18
|
+
);
|
|
19
|
+
--> statement-breakpoint
|
|
20
|
+
CREATE TABLE `nodes` (
|
|
21
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
22
|
+
`type` text,
|
|
23
|
+
`title` text,
|
|
24
|
+
`domain` text,
|
|
25
|
+
`layer` text,
|
|
26
|
+
`embedding` blob,
|
|
27
|
+
`hash` text,
|
|
28
|
+
`meta` text,
|
|
29
|
+
`date` text
|
|
30
|
+
);
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "6",
|
|
3
|
+
"dialect": "sqlite",
|
|
4
|
+
"id": "577c9f07-f198-49d2-9fa3-f222a6ecee23",
|
|
5
|
+
"prevId": "00000000-0000-0000-0000-000000000000",
|
|
6
|
+
"tables": {
|
|
7
|
+
"edges": {
|
|
8
|
+
"name": "edges",
|
|
9
|
+
"columns": {
|
|
10
|
+
"source": {
|
|
11
|
+
"name": "source",
|
|
12
|
+
"type": "text",
|
|
13
|
+
"primaryKey": false,
|
|
14
|
+
"notNull": true,
|
|
15
|
+
"autoincrement": false
|
|
16
|
+
},
|
|
17
|
+
"target": {
|
|
18
|
+
"name": "target",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"primaryKey": false,
|
|
21
|
+
"notNull": true,
|
|
22
|
+
"autoincrement": false
|
|
23
|
+
},
|
|
24
|
+
"type": {
|
|
25
|
+
"name": "type",
|
|
26
|
+
"type": "text",
|
|
27
|
+
"primaryKey": false,
|
|
28
|
+
"notNull": true,
|
|
29
|
+
"autoincrement": false
|
|
30
|
+
},
|
|
31
|
+
"confidence": {
|
|
32
|
+
"name": "confidence",
|
|
33
|
+
"type": "real",
|
|
34
|
+
"primaryKey": false,
|
|
35
|
+
"notNull": false,
|
|
36
|
+
"autoincrement": false,
|
|
37
|
+
"default": 1
|
|
38
|
+
},
|
|
39
|
+
"veracity": {
|
|
40
|
+
"name": "veracity",
|
|
41
|
+
"type": "real",
|
|
42
|
+
"primaryKey": false,
|
|
43
|
+
"notNull": false,
|
|
44
|
+
"autoincrement": false,
|
|
45
|
+
"default": 1
|
|
46
|
+
},
|
|
47
|
+
"context_source": {
|
|
48
|
+
"name": "context_source",
|
|
49
|
+
"type": "text",
|
|
50
|
+
"primaryKey": false,
|
|
51
|
+
"notNull": false,
|
|
52
|
+
"autoincrement": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"indexes": {
|
|
56
|
+
"idx_edges_source": {
|
|
57
|
+
"name": "idx_edges_source",
|
|
58
|
+
"columns": ["source"],
|
|
59
|
+
"isUnique": false
|
|
60
|
+
},
|
|
61
|
+
"idx_edges_target": {
|
|
62
|
+
"name": "idx_edges_target",
|
|
63
|
+
"columns": ["target"],
|
|
64
|
+
"isUnique": false
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"foreignKeys": {},
|
|
68
|
+
"compositePrimaryKeys": {
|
|
69
|
+
"edges_source_target_type_pk": {
|
|
70
|
+
"columns": ["source", "target", "type"],
|
|
71
|
+
"name": "edges_source_target_type_pk"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"uniqueConstraints": {},
|
|
75
|
+
"checkConstraints": {}
|
|
76
|
+
},
|
|
77
|
+
"ember_state": {
|
|
78
|
+
"name": "ember_state",
|
|
79
|
+
"columns": {
|
|
80
|
+
"file_path": {
|
|
81
|
+
"name": "file_path",
|
|
82
|
+
"type": "text",
|
|
83
|
+
"primaryKey": true,
|
|
84
|
+
"notNull": true,
|
|
85
|
+
"autoincrement": false
|
|
86
|
+
},
|
|
87
|
+
"last_analyzed": {
|
|
88
|
+
"name": "last_analyzed",
|
|
89
|
+
"type": "text",
|
|
90
|
+
"primaryKey": false,
|
|
91
|
+
"notNull": false,
|
|
92
|
+
"autoincrement": false
|
|
93
|
+
},
|
|
94
|
+
"sidecar_created": {
|
|
95
|
+
"name": "sidecar_created",
|
|
96
|
+
"type": "integer",
|
|
97
|
+
"primaryKey": false,
|
|
98
|
+
"notNull": false,
|
|
99
|
+
"autoincrement": false
|
|
100
|
+
},
|
|
101
|
+
"confidence": {
|
|
102
|
+
"name": "confidence",
|
|
103
|
+
"type": "real",
|
|
104
|
+
"primaryKey": false,
|
|
105
|
+
"notNull": false,
|
|
106
|
+
"autoincrement": false
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"indexes": {},
|
|
110
|
+
"foreignKeys": {},
|
|
111
|
+
"compositePrimaryKeys": {},
|
|
112
|
+
"uniqueConstraints": {},
|
|
113
|
+
"checkConstraints": {}
|
|
114
|
+
},
|
|
115
|
+
"nodes": {
|
|
116
|
+
"name": "nodes",
|
|
117
|
+
"columns": {
|
|
118
|
+
"id": {
|
|
119
|
+
"name": "id",
|
|
120
|
+
"type": "text",
|
|
121
|
+
"primaryKey": true,
|
|
122
|
+
"notNull": true,
|
|
123
|
+
"autoincrement": false
|
|
124
|
+
},
|
|
125
|
+
"type": {
|
|
126
|
+
"name": "type",
|
|
127
|
+
"type": "text",
|
|
128
|
+
"primaryKey": false,
|
|
129
|
+
"notNull": false,
|
|
130
|
+
"autoincrement": false
|
|
131
|
+
},
|
|
132
|
+
"title": {
|
|
133
|
+
"name": "title",
|
|
134
|
+
"type": "text",
|
|
135
|
+
"primaryKey": false,
|
|
136
|
+
"notNull": false,
|
|
137
|
+
"autoincrement": false
|
|
138
|
+
},
|
|
139
|
+
"domain": {
|
|
140
|
+
"name": "domain",
|
|
141
|
+
"type": "text",
|
|
142
|
+
"primaryKey": false,
|
|
143
|
+
"notNull": false,
|
|
144
|
+
"autoincrement": false
|
|
145
|
+
},
|
|
146
|
+
"layer": {
|
|
147
|
+
"name": "layer",
|
|
148
|
+
"type": "text",
|
|
149
|
+
"primaryKey": false,
|
|
150
|
+
"notNull": false,
|
|
151
|
+
"autoincrement": false
|
|
152
|
+
},
|
|
153
|
+
"embedding": {
|
|
154
|
+
"name": "embedding",
|
|
155
|
+
"type": "blob",
|
|
156
|
+
"primaryKey": false,
|
|
157
|
+
"notNull": false,
|
|
158
|
+
"autoincrement": false
|
|
159
|
+
},
|
|
160
|
+
"hash": {
|
|
161
|
+
"name": "hash",
|
|
162
|
+
"type": "text",
|
|
163
|
+
"primaryKey": false,
|
|
164
|
+
"notNull": false,
|
|
165
|
+
"autoincrement": false
|
|
166
|
+
},
|
|
167
|
+
"meta": {
|
|
168
|
+
"name": "meta",
|
|
169
|
+
"type": "text",
|
|
170
|
+
"primaryKey": false,
|
|
171
|
+
"notNull": false,
|
|
172
|
+
"autoincrement": false
|
|
173
|
+
},
|
|
174
|
+
"date": {
|
|
175
|
+
"name": "date",
|
|
176
|
+
"type": "text",
|
|
177
|
+
"primaryKey": false,
|
|
178
|
+
"notNull": false,
|
|
179
|
+
"autoincrement": false
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"indexes": {},
|
|
183
|
+
"foreignKeys": {},
|
|
184
|
+
"compositePrimaryKeys": {},
|
|
185
|
+
"uniqueConstraints": {},
|
|
186
|
+
"checkConstraints": {}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"views": {},
|
|
190
|
+
"enums": {},
|
|
191
|
+
"_meta": {
|
|
192
|
+
"schemas": {},
|
|
193
|
+
"tables": {},
|
|
194
|
+
"columns": {}
|
|
195
|
+
},
|
|
196
|
+
"internal": {
|
|
197
|
+
"indexes": {}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
blob,
|
|
3
|
+
index,
|
|
4
|
+
integer,
|
|
5
|
+
primaryKey,
|
|
6
|
+
real,
|
|
7
|
+
sqliteTable,
|
|
8
|
+
text,
|
|
9
|
+
} from "drizzle-orm/sqlite-core";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* NODES Table
|
|
13
|
+
* Core entity storage. Now "Hollow" (no content).
|
|
14
|
+
*/
|
|
15
|
+
export const nodes = sqliteTable("nodes", {
|
|
16
|
+
id: text("id").primaryKey(),
|
|
17
|
+
type: text("type"),
|
|
18
|
+
title: text("title"),
|
|
19
|
+
domain: text("domain"),
|
|
20
|
+
layer: text("layer"),
|
|
21
|
+
// Embeddings are stored as raw BLOBs (Float32Array bytes)
|
|
22
|
+
embedding: blob("embedding"),
|
|
23
|
+
hash: text("hash"),
|
|
24
|
+
meta: text("meta"), // JSON string
|
|
25
|
+
date: text("date"), // ISO string or YYYY-MM-DD
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* EDGES Table
|
|
30
|
+
* Defines relationships between nodes.
|
|
31
|
+
*/
|
|
32
|
+
export const edges = sqliteTable(
|
|
33
|
+
"edges",
|
|
34
|
+
{
|
|
35
|
+
source: text("source").notNull(),
|
|
36
|
+
target: text("target").notNull(),
|
|
37
|
+
type: text("type").notNull(),
|
|
38
|
+
confidence: real("confidence").default(1.0),
|
|
39
|
+
veracity: real("veracity").default(1.0),
|
|
40
|
+
contextSource: text("context_source"),
|
|
41
|
+
},
|
|
42
|
+
(table) => ({
|
|
43
|
+
// Composite Primary Key
|
|
44
|
+
pk: primaryKey({ columns: [table.source, table.target, table.type] }),
|
|
45
|
+
// Indices for traversal speed
|
|
46
|
+
sourceIdx: index("idx_edges_source").on(table.source),
|
|
47
|
+
targetIdx: index("idx_edges_target").on(table.target),
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* EMBER STATE Table (Pilot)
|
|
53
|
+
* Tracks the state of the Ember Service (automated enrichment).
|
|
54
|
+
*/
|
|
55
|
+
export const emberState = sqliteTable("ember_state", {
|
|
56
|
+
filePath: text("file_path").primaryKey(),
|
|
57
|
+
lastAnalyzed: text("last_analyzed"),
|
|
58
|
+
sidecarCreated: integer("sidecar_created", { mode: "boolean" }),
|
|
59
|
+
confidence: real("confidence"),
|
|
60
|
+
});
|