@runweave/server-linux-arm64 0.0.0-stage → 0.1.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/manifest.json +1993 -0
- package/native.tar.gz +0 -0
- package/package.json +17 -4
- package/payload/app-server/build-info.json +8 -0
- package/payload/app-server/configuration-compatibility.json +1 -0
- package/payload/app-server/index.cjs +43560 -0
- package/payload/app-server/index.cjs.map +1 -0
- package/payload/backend/activity-sqlite-worker.cjs +6730 -0
- package/payload/backend/build-info.json +8 -0
- package/payload/backend/dev-resources/control.cjs +14727 -0
- package/payload/backend/evolution-sqlite-worker.cjs +2073 -0
- package/payload/backend/index.cjs +135608 -0
- package/payload/backend/ios-simulators/device_shutdown.py +22 -0
- package/payload/backend/ios-simulators/monitor.py +204 -0
- package/payload/backend/ios-simulators/pool.py +600 -0
- package/payload/backend/scheduled-tasks-sqlite-worker.cjs +29203 -0
- package/payload/bin/runtime.cjs +95 -0
- package/payload/frontend/dist/assets/_baseUniq-BJ9V9bG-.js +1 -0
- package/payload/frontend/dist/assets/access-DYFqfdy3.js +1 -0
- package/payload/frontend/dist/assets/advanced-C4Sx575S.js +1 -0
- package/payload/frontend/dist/assets/appearance-Bhq5haQK.js +1 -0
- package/payload/frontend/dist/assets/arc-Bihx3tKA.js +1 -0
- package/payload/frontend/dist/assets/architectureDiagram-Q4EWVU46-63rNkvm4.js +36 -0
- package/payload/frontend/dist/assets/blockDiagram-DXYQGD6D-BlolQExt.js +132 -0
- package/payload/frontend/dist/assets/browser-CIEzmzuD.js +8 -0
- package/payload/frontend/dist/assets/c4Diagram-AHTNJAMY-DJtJ952u.js +10 -0
- package/payload/frontend/dist/assets/channel-Mm4Vb-2P.js +1 -0
- package/payload/frontend/dist/assets/chunk-4BX2VUAB-ChncjhEG.js +1 -0
- package/payload/frontend/dist/assets/chunk-4TB4RGXK-C6ipd8Rb.js +206 -0
- package/payload/frontend/dist/assets/chunk-55IACEB6-D9P1TLAl.js +1 -0
- package/payload/frontend/dist/assets/chunk-EDXVE4YY-NqBFXrAb.js +1 -0
- package/payload/frontend/dist/assets/chunk-FMBD7UC4-DDP02KGl.js +15 -0
- package/payload/frontend/dist/assets/chunk-OYMX7WX6-BH7IIwJs.js +231 -0
- package/payload/frontend/dist/assets/chunk-QZHKN3VN-BQs-0fGA.js +1 -0
- package/payload/frontend/dist/assets/chunk-YZCP3GAM-BjN5B1FZ.js +1 -0
- package/payload/frontend/dist/assets/classDiagram-6PBFFD2Q-BiLH0Poy.js +1 -0
- package/payload/frontend/dist/assets/classDiagram-v2-HSJHXN6E-BiLH0Poy.js +1 -0
- package/payload/frontend/dist/assets/clone-qIEEEAzl.js +1 -0
- package/payload/frontend/dist/assets/companion-BLgWW1ka.css +1 -0
- package/payload/frontend/dist/assets/companion-CRS3Tmyt.js +2 -0
- package/payload/frontend/dist/assets/connection-BK8OyohU.js +12 -0
- package/payload/frontend/dist/assets/connections-BMlDGuxr.js +1 -0
- package/payload/frontend/dist/assets/cose-bilkent-S5V4N54A-NPOCJBiA.js +1 -0
- package/payload/frontend/dist/assets/css.worker-B4z49cGk.js +93 -0
- package/payload/frontend/dist/assets/cytoscape.esm-DxGcaOPV.js +331 -0
- package/payload/frontend/dist/assets/dagre-KV5264BT-COIvyDWn.js +4 -0
- package/payload/frontend/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/payload/frontend/dist/assets/desktop-ByVdibdg.js +1 -0
- package/payload/frontend/dist/assets/diagram-5BDNPKRD-S_8jRdqp.js +10 -0
- package/payload/frontend/dist/assets/diagram-G4DWMVQ6-C42mxsNM.js +24 -0
- package/payload/frontend/dist/assets/diagram-MMDJMWI5-BJM6otf0.js +43 -0
- package/payload/frontend/dist/assets/diagram-TYMM5635-By5p6aCA.js +24 -0
- package/payload/frontend/dist/assets/editor.worker-DyuDvpVp.js +26 -0
- package/payload/frontend/dist/assets/erDiagram-SMLLAGMA-U0Ye_786.js +85 -0
- package/payload/frontend/dist/assets/flowDiagram-DWJPFMVM-CUkOR4uD.js +162 -0
- package/payload/frontend/dist/assets/ganttDiagram-T4ZO3ILL-CRlDiioi.js +292 -0
- package/payload/frontend/dist/assets/gitGraphDiagram-UUTBAWPF-DV4Ea1j2.js +106 -0
- package/payload/frontend/dist/assets/graph-Bn4s-NIu.js +1 -0
- package/payload/frontend/dist/assets/html.worker-DtiGdgqp.js +470 -0
- package/payload/frontend/dist/assets/image-0wCxVgo2.js +1 -0
- package/payload/frontend/dist/assets/index-BJDOs8Sk.css +1 -0
- package/payload/frontend/dist/assets/index-D9-cSGu-.js +49 -0
- package/payload/frontend/dist/assets/index-De2lxGIa.js +455 -0
- package/payload/frontend/dist/assets/infoDiagram-42DDH7IO-ut--ZHxq.js +2 -0
- package/payload/frontend/dist/assets/init-Gi6I4Gst.js +1 -0
- package/payload/frontend/dist/assets/ishikawaDiagram-UXIWVN3A-DbJ9qouY.js +70 -0
- package/payload/frontend/dist/assets/journeyDiagram-VCZTEJTY-CZ5a4nqq.js +139 -0
- package/payload/frontend/dist/assets/json.worker-leyajbqV.js +58 -0
- package/payload/frontend/dist/assets/kanban-definition-6JOO6SKY-CagFKide.js +89 -0
- package/payload/frontend/dist/assets/katex-DkKDou_j.js +257 -0
- package/payload/frontend/dist/assets/layout-DJ8E_yHX.js +1 -0
- package/payload/frontend/dist/assets/linear-Dn6DMk6q.js +1 -0
- package/payload/frontend/dist/assets/main-C-ZLrzMh.css +33 -0
- package/payload/frontend/dist/assets/main-T426svZw.js +693 -0
- package/payload/frontend/dist/assets/min-DnGEZSmi.js +1 -0
- package/payload/frontend/dist/assets/mindmap-definition-QFDTVHPH-Dy5F9V5f.js +96 -0
- package/payload/frontend/dist/assets/monitoring-BdNkTyog.js +1 -0
- package/payload/frontend/dist/assets/notifications-incoktxl.js +1 -0
- package/payload/frontend/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/payload/frontend/dist/assets/pieDiagram-DEJITSTG-yXPGxeUi.js +30 -0
- package/payload/frontend/dist/assets/quadrantDiagram-34T5L4WZ-D4cCRjRq.js +7 -0
- package/payload/frontend/dist/assets/record-content-D1WJhPcw.js +1 -0
- package/payload/frontend/dist/assets/requirementDiagram-MS252O5E-mq9zSUQY.js +84 -0
- package/payload/frontend/dist/assets/sankeyDiagram-XADWPNL6-CLpI-tDz.js +10 -0
- package/payload/frontend/dist/assets/sequenceDiagram-FGHM5R23-B-T8p6AJ.js +157 -0
- package/payload/frontend/dist/assets/shortcuts-BNrV46nh.js +1 -0
- package/payload/frontend/dist/assets/stateDiagram-FHFEXIEX-l2_t6o5K.js +1 -0
- package/payload/frontend/dist/assets/stateDiagram-v2-QKLJ7IA2-BXh3ccaZ.js +1 -0
- package/payload/frontend/dist/assets/status-CP5f7C2Y.js +1 -0
- package/payload/frontend/dist/assets/suiji-BAD1Bzfb.js +1 -0
- package/payload/frontend/dist/assets/svg-G8v0TLQu.js +21 -0
- package/payload/frontend/dist/assets/tabs-CuKPIDlT.js +1 -0
- package/payload/frontend/dist/assets/timeline-definition-GMOUNBTQ-CSaExDd9.js +120 -0
- package/payload/frontend/dist/assets/ts.worker-59MjiAqk.js +67731 -0
- package/payload/frontend/dist/assets/vennDiagram-DHZGUBPP-x-fCoh_Q.js +34 -0
- package/payload/frontend/dist/assets/wardley-RL74JXVD-B4O75ZAB.js +162 -0
- package/payload/frontend/dist/assets/wardleyDiagram-NUSXRM2D-CdXssvxH.js +20 -0
- package/payload/frontend/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +2 -0
- package/payload/frontend/dist/assets/xychartDiagram-5P7HB3ND-DwTtw7p0.js +7 -0
- package/payload/frontend/dist/build-info.json +8 -0
- package/payload/frontend/dist/companion.html +15 -0
- package/payload/frontend/dist/icons/apple-touch-icon.png +0 -0
- package/payload/frontend/dist/icons/icon-192.png +0 -0
- package/payload/frontend/dist/icons/icon-512.png +0 -0
- package/payload/frontend/dist/icons/maskable-icon-512.png +0 -0
- package/payload/frontend/dist/index.html +19 -0
- package/payload/frontend/dist/manifest.webmanifest +28 -0
- package/payload/frontend/dist/sw.js +1 -0
- package/payload/frontend/dist/workbox-632bafe5.js +1 -0
- package/payload/server/index.cjs +8620 -0
- package/payload/start.mjs +50 -0
- package/README.md +0 -3
|
@@ -0,0 +1,2073 @@
|
|
|
1
|
+
const __IMPORT_META_URL__ = require('url').pathToFileURL(__filename).href;
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// backend/src/evolution/storage/sqlite-worker.ts
|
|
27
|
+
var import_node_worker_threads = require("node:worker_threads");
|
|
28
|
+
|
|
29
|
+
// backend/src/evolution/storage/artifact-reads.ts
|
|
30
|
+
var PAGE_BYTES = 512 * 1024;
|
|
31
|
+
var EvolutionArtifactReads = class {
|
|
32
|
+
constructor(database2) {
|
|
33
|
+
this.database = database2;
|
|
34
|
+
}
|
|
35
|
+
summaries = /* @__PURE__ */ new Map();
|
|
36
|
+
overview(runId) {
|
|
37
|
+
const cacheAllowed = !this.database.inTransaction;
|
|
38
|
+
return this.database.transaction(() => ({
|
|
39
|
+
contextPack: this.summary(runId, cacheAllowed),
|
|
40
|
+
segmentCount: this.count("trace_segments", runId),
|
|
41
|
+
episodeCount: this.count("episodes", runId)
|
|
42
|
+
}))();
|
|
43
|
+
}
|
|
44
|
+
count(table, runId, boundary) {
|
|
45
|
+
return this.database.prepare(
|
|
46
|
+
`SELECT COUNT(*) AS n FROM ${table} WHERE run_id = ?
|
|
47
|
+
${boundary === void 0 ? "" : "AND rowid <= ?"}`
|
|
48
|
+
).get(...boundary === void 0 ? [runId] : [runId, boundary]).n;
|
|
49
|
+
}
|
|
50
|
+
summary(runId, cacheAllowed) {
|
|
51
|
+
const identity = this.database.prepare(
|
|
52
|
+
"SELECT context_pack_id, digest FROM context_packs WHERE run_id = ?"
|
|
53
|
+
).get(runId);
|
|
54
|
+
if (!identity) return null;
|
|
55
|
+
const cached = this.summaries.get(identity.context_pack_id);
|
|
56
|
+
if (cacheAllowed && cached?.digest === identity.digest) return cached.value;
|
|
57
|
+
const row = this.database.prepare(
|
|
58
|
+
`SELECT
|
|
59
|
+
json_remove(manifest_json, '$.evidence', '$.dataQualityIssues') AS metadata,
|
|
60
|
+
json_array_length(manifest_json, '$.evidence') AS evidenceCount,
|
|
61
|
+
json_array_length(manifest_json, '$.dataQualityIssues') AS dataQualityIssueCount
|
|
62
|
+
FROM context_packs WHERE run_id = ?`
|
|
63
|
+
).get(runId);
|
|
64
|
+
const workspaceCount = this.database.prepare(
|
|
65
|
+
`SELECT COUNT(DISTINCT json_extract(item.value, '$.origin.projectId')) AS n
|
|
66
|
+
FROM context_packs pack, json_each(pack.manifest_json, '$.evidence') item
|
|
67
|
+
WHERE pack.run_id = ? AND json_extract(item.value, '$.source') = 'activity'
|
|
68
|
+
AND json_extract(item.value, '$.origin.projectId') != ''`
|
|
69
|
+
).get(runId).n;
|
|
70
|
+
const dataQualitySummary = this.database.prepare(
|
|
71
|
+
`SELECT json_extract(item.value, '$.code') AS code,
|
|
72
|
+
COUNT(*) AS count, json_extract(item.value, '$.detail') AS detail, MIN(CAST(item.key AS INTEGER)) AS firstIndex
|
|
73
|
+
FROM context_packs pack, json_each(pack.manifest_json, '$.dataQualityIssues') item
|
|
74
|
+
WHERE pack.run_id = ? GROUP BY code`
|
|
75
|
+
).all(runId);
|
|
76
|
+
dataQualitySummary.sort(
|
|
77
|
+
(a, b) => b.count - a.count || a.code.localeCompare(b.code)
|
|
78
|
+
);
|
|
79
|
+
const value = {
|
|
80
|
+
...JSON.parse(row.metadata),
|
|
81
|
+
evidenceCount: row.evidenceCount,
|
|
82
|
+
dataQualityIssueCount: row.dataQualityIssueCount,
|
|
83
|
+
workspaceCount,
|
|
84
|
+
dataQualitySummary: dataQualitySummary.map(({ code, count, detail }) => ({
|
|
85
|
+
code,
|
|
86
|
+
count,
|
|
87
|
+
detail
|
|
88
|
+
}))
|
|
89
|
+
};
|
|
90
|
+
if (cacheAllowed) {
|
|
91
|
+
if (this.summaries.size >= 32)
|
|
92
|
+
this.summaries.delete(this.summaries.keys().next().value);
|
|
93
|
+
this.summaries.set(identity.context_pack_id, {
|
|
94
|
+
digest: identity.digest,
|
|
95
|
+
value
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
page(runId, query) {
|
|
101
|
+
const { collection } = query;
|
|
102
|
+
if (!["evidence", "data-quality", "segments", "episodes"].includes(collection))
|
|
103
|
+
throw new Error("evolution_artifact_query_invalid");
|
|
104
|
+
const limit = query.limit ?? 25;
|
|
105
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
|
|
106
|
+
throw new Error("evolution_artifact_query_invalid");
|
|
107
|
+
const cursor = query.cursor ? decodeCursor(query.cursor, runId, collection) : void 0;
|
|
108
|
+
return this.database.transaction(() => {
|
|
109
|
+
const offset = cursor?.offset ?? 0;
|
|
110
|
+
let boundary;
|
|
111
|
+
let total;
|
|
112
|
+
let rows;
|
|
113
|
+
if (collection === "evidence" || collection === "data-quality") {
|
|
114
|
+
const pack = this.database.prepare(
|
|
115
|
+
"SELECT context_pack_id, digest FROM context_packs WHERE run_id = ?"
|
|
116
|
+
).get(runId);
|
|
117
|
+
boundary = pack ? `${pack.context_pack_id}:${pack.digest}` : "missing";
|
|
118
|
+
if (cursor && cursor.boundary !== boundary)
|
|
119
|
+
throw new Error("evolution_artifact_cursor_stale");
|
|
120
|
+
const field = collection === "evidence" ? "evidence" : "dataQualityIssues";
|
|
121
|
+
total = pack ? this.database.prepare(
|
|
122
|
+
`SELECT json_array_length(manifest_json, '$.${field}') AS n
|
|
123
|
+
FROM context_packs WHERE run_id = ?`
|
|
124
|
+
).get(runId).n : 0;
|
|
125
|
+
rows = this.database.prepare(
|
|
126
|
+
`SELECT item.value AS payload FROM context_packs pack,
|
|
127
|
+
json_each(pack.manifest_json, '$.${field}') item WHERE pack.run_id = ? AND CAST(item.key AS INTEGER) >= ?
|
|
128
|
+
ORDER BY CAST(item.key AS INTEGER) LIMIT ?`
|
|
129
|
+
).iterate(runId, offset, limit);
|
|
130
|
+
} else {
|
|
131
|
+
const table = collection === "segments" ? "trace_segments" : "episodes";
|
|
132
|
+
const order = collection === "segments" ? "sequence, segment_id" : "created_at, episode_id";
|
|
133
|
+
boundary = cursor?.boundary ?? this.database.prepare(
|
|
134
|
+
`SELECT COALESCE(MAX(rowid), 0) AS n
|
|
135
|
+
FROM ${table} WHERE run_id = ?`
|
|
136
|
+
).get(runId).n;
|
|
137
|
+
if (typeof boundary !== "number")
|
|
138
|
+
throw new Error("evolution_artifact_query_invalid");
|
|
139
|
+
total = this.count(table, runId, boundary);
|
|
140
|
+
rows = this.database.prepare(
|
|
141
|
+
`SELECT payload_json AS payload FROM ${table}
|
|
142
|
+
WHERE run_id = ? AND rowid <= ? ORDER BY ${order} LIMIT ? OFFSET ?`
|
|
143
|
+
).iterate(runId, boundary, limit, offset);
|
|
144
|
+
}
|
|
145
|
+
const items = [];
|
|
146
|
+
let bytes = 0;
|
|
147
|
+
for (const row of rows) {
|
|
148
|
+
const size = Buffer.byteLength(row.payload);
|
|
149
|
+
if (items.length > 0 && bytes + size > PAGE_BYTES) break;
|
|
150
|
+
items.push(JSON.parse(row.payload));
|
|
151
|
+
bytes += size;
|
|
152
|
+
}
|
|
153
|
+
const nextOffset = offset + items.length;
|
|
154
|
+
return {
|
|
155
|
+
collection,
|
|
156
|
+
items,
|
|
157
|
+
total,
|
|
158
|
+
nextCursor: nextOffset < total ? Buffer.from(
|
|
159
|
+
JSON.stringify({
|
|
160
|
+
runId,
|
|
161
|
+
collection,
|
|
162
|
+
offset: nextOffset,
|
|
163
|
+
boundary
|
|
164
|
+
})
|
|
165
|
+
).toString("base64url") : null
|
|
166
|
+
};
|
|
167
|
+
})();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
function decodeCursor(value, runId, collection) {
|
|
171
|
+
try {
|
|
172
|
+
const cursor = JSON.parse(
|
|
173
|
+
Buffer.from(value, "base64url").toString("utf8")
|
|
174
|
+
);
|
|
175
|
+
if (cursor.runId === runId && cursor.collection === collection && Number.isSafeInteger(cursor.offset) && cursor.offset >= 0 && (typeof cursor.boundary === "string" && cursor.boundary.length <= 512 || Number.isSafeInteger(cursor.boundary) && Number(cursor.boundary) >= 0))
|
|
176
|
+
return cursor;
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
throw new Error("evolution_artifact_query_invalid");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// backend/src/evolution/storage/repository-database.ts
|
|
183
|
+
function initializeEvolutionRepositories(database2) {
|
|
184
|
+
database2.exec(`
|
|
185
|
+
CREATE TABLE IF NOT EXISTS evolution_repositories(repository_id TEXT PRIMARY KEY, payload_json TEXT NOT NULL);
|
|
186
|
+
CREATE TABLE IF NOT EXISTS evolution_repository_bindings(
|
|
187
|
+
kind TEXT NOT NULL, id TEXT NOT NULL, payload_json TEXT NOT NULL, PRIMARY KEY(kind,id));
|
|
188
|
+
CREATE TABLE IF NOT EXISTS evolution_topic_lineage(
|
|
189
|
+
canonical_insight_id TEXT NOT NULL, legacy_insight_id TEXT PRIMARY KEY, status TEXT NOT NULL);
|
|
190
|
+
CREATE TABLE IF NOT EXISTS evolution_reflection_batches(
|
|
191
|
+
batch_id TEXT PRIMARY KEY, idempotency_key TEXT NOT NULL UNIQUE, payload_json TEXT NOT NULL);
|
|
192
|
+
CREATE TABLE IF NOT EXISTS evolution_repository_migrations(
|
|
193
|
+
migration_id TEXT PRIMARY KEY, payload_json TEXT NOT NULL);
|
|
194
|
+
`);
|
|
195
|
+
const columns = database2.prepare("PRAGMA table_info(evolution_runs)").all();
|
|
196
|
+
if (!columns.some((column) => column.name === "repository_json"))
|
|
197
|
+
database2.exec("ALTER TABLE evolution_runs ADD COLUMN repository_json TEXT");
|
|
198
|
+
}
|
|
199
|
+
function readRepositoryAttribution(database2, kind, id) {
|
|
200
|
+
const row = database2.prepare(
|
|
201
|
+
"SELECT payload_json FROM evolution_repository_bindings WHERE kind = ? AND id = ?"
|
|
202
|
+
).get(kind, id);
|
|
203
|
+
return row ? JSON.parse(row.payload_json) : void 0;
|
|
204
|
+
}
|
|
205
|
+
function executeRepositoryCommand(database2, foundation, command) {
|
|
206
|
+
switch (command.op) {
|
|
207
|
+
case "list":
|
|
208
|
+
return database2.prepare(
|
|
209
|
+
"SELECT payload_json FROM evolution_repositories ORDER BY repository_id"
|
|
210
|
+
).all().map((row) => JSON.parse(row.payload_json));
|
|
211
|
+
case "put": {
|
|
212
|
+
return database2.transaction(() => {
|
|
213
|
+
const row = database2.prepare(
|
|
214
|
+
"SELECT payload_json FROM evolution_repositories WHERE repository_id = ?"
|
|
215
|
+
).get(command.repository.repositoryId);
|
|
216
|
+
const previous = row ? JSON.parse(row.payload_json) : null;
|
|
217
|
+
if (previous && previous.commonDirectory !== command.repository.commonDirectory)
|
|
218
|
+
throw new Error("evolution_repository_identity_conflict");
|
|
219
|
+
const repository = {
|
|
220
|
+
...command.repository,
|
|
221
|
+
paths: [
|
|
222
|
+
.../* @__PURE__ */ new Set([
|
|
223
|
+
...previous?.paths ?? [],
|
|
224
|
+
...command.repository.paths
|
|
225
|
+
])
|
|
226
|
+
],
|
|
227
|
+
projectIds: [
|
|
228
|
+
.../* @__PURE__ */ new Set([
|
|
229
|
+
...previous?.projectIds ?? [],
|
|
230
|
+
...command.repository.projectIds
|
|
231
|
+
])
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
database2.prepare(
|
|
235
|
+
"INSERT INTO evolution_repositories VALUES (?,?) ON CONFLICT(repository_id) DO UPDATE SET payload_json=excluded.payload_json"
|
|
236
|
+
).run(repository.repositoryId, JSON.stringify(repository));
|
|
237
|
+
return true;
|
|
238
|
+
}).immediate();
|
|
239
|
+
}
|
|
240
|
+
case "attribution":
|
|
241
|
+
return readRepositoryAttribution(database2, command.kind, command.id) ?? null;
|
|
242
|
+
case "batch-get": {
|
|
243
|
+
const row = database2.prepare(
|
|
244
|
+
"SELECT payload_json FROM evolution_reflection_batches WHERE idempotency_key = ?"
|
|
245
|
+
).get(command.key);
|
|
246
|
+
return row ? JSON.parse(row.payload_json) : null;
|
|
247
|
+
}
|
|
248
|
+
case "batch-create":
|
|
249
|
+
return database2.transaction(() => {
|
|
250
|
+
const previous = executeRepositoryCommand(database2, foundation, {
|
|
251
|
+
op: "batch-get",
|
|
252
|
+
key: command.batch.idempotencyKey
|
|
253
|
+
});
|
|
254
|
+
if (previous) return previous;
|
|
255
|
+
for (const run of command.runs) foundation.createRun(run);
|
|
256
|
+
database2.prepare("INSERT INTO evolution_reflection_batches VALUES (?,?,?)").run(
|
|
257
|
+
command.batch.batchId,
|
|
258
|
+
command.batch.idempotencyKey,
|
|
259
|
+
JSON.stringify(command.batch)
|
|
260
|
+
);
|
|
261
|
+
return command.batch;
|
|
262
|
+
}).immediate();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// backend/src/evolution/storage/database.ts
|
|
267
|
+
var import_node_fs = require("node:fs");
|
|
268
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
269
|
+
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
270
|
+
|
|
271
|
+
// backend/src/evolution/storage/database-helpers.ts
|
|
272
|
+
function insertImmutable(database2, params) {
|
|
273
|
+
const existing = database2.prepare(
|
|
274
|
+
`SELECT payload_json FROM ${params.table} WHERE ${params.idColumn} = ?`
|
|
275
|
+
).get(params.id);
|
|
276
|
+
if (existing) {
|
|
277
|
+
if (existing.payload_json !== params.payload) {
|
|
278
|
+
throw new Error("evolution_immutable_artifact_conflict");
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
params.insert();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// backend/src/evolution/storage/artifact-database.ts
|
|
286
|
+
var EvolutionArtifactDatabase = class {
|
|
287
|
+
constructor(database2) {
|
|
288
|
+
this.database = database2;
|
|
289
|
+
}
|
|
290
|
+
putRuntimeTrace(trace) {
|
|
291
|
+
this.database.transaction(() => {
|
|
292
|
+
this.database.prepare(
|
|
293
|
+
`INSERT OR IGNORE INTO runtime_traces
|
|
294
|
+
(trace_id, run_id, created_at, payload_json)
|
|
295
|
+
VALUES (?, ?, ?, ?)`
|
|
296
|
+
).run(
|
|
297
|
+
trace.traceId,
|
|
298
|
+
trace.runId,
|
|
299
|
+
trace.createdAt,
|
|
300
|
+
JSON.stringify({ ...trace, events: [] })
|
|
301
|
+
);
|
|
302
|
+
for (const event of trace.events) {
|
|
303
|
+
this.appendRuntimeTraceEvent(event);
|
|
304
|
+
}
|
|
305
|
+
})();
|
|
306
|
+
}
|
|
307
|
+
appendRuntimeTraceEvent(event) {
|
|
308
|
+
this.database.prepare(
|
|
309
|
+
`INSERT OR IGNORE INTO runtime_trace_events
|
|
310
|
+
(event_id, trace_id, at, payload_json)
|
|
311
|
+
VALUES (?, ?, ?, ?)`
|
|
312
|
+
).run(event.eventId, event.traceId, event.at, JSON.stringify(event));
|
|
313
|
+
}
|
|
314
|
+
getRuntimeTrace(traceId) {
|
|
315
|
+
const row = this.database.prepare("SELECT payload_json FROM runtime_traces WHERE trace_id = ?").get(traceId);
|
|
316
|
+
if (!row) return null;
|
|
317
|
+
const trace = JSON.parse(row.payload_json);
|
|
318
|
+
return {
|
|
319
|
+
...trace,
|
|
320
|
+
attribution: readRepositoryAttribution(this.database, "trace", traceId),
|
|
321
|
+
events: this.listEvents(traceId)
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
listRuntimeTraces(runId) {
|
|
325
|
+
const rows = this.database.prepare(
|
|
326
|
+
"SELECT trace_id FROM runtime_traces WHERE run_id = ? ORDER BY created_at"
|
|
327
|
+
).all(runId);
|
|
328
|
+
return rows.flatMap((row) => {
|
|
329
|
+
const trace = this.getRuntimeTrace(row.trace_id);
|
|
330
|
+
return trace ? [trace] : [];
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
listRecentRuntimeTraces(learningScopeId, limit) {
|
|
334
|
+
const rows = learningScopeId ? this.database.prepare(
|
|
335
|
+
`SELECT trace_id FROM runtime_traces
|
|
336
|
+
WHERE json_extract(payload_json, '$.learningScopeId') = ? OR EXISTS (
|
|
337
|
+
SELECT 1 FROM evolution_repository_bindings binding, json_each(binding.payload_json, '$.repositoryIds') repo
|
|
338
|
+
WHERE binding.kind='trace' AND binding.id=runtime_traces.trace_id AND repo.value=?
|
|
339
|
+
)
|
|
340
|
+
ORDER BY created_at DESC, trace_id
|
|
341
|
+
LIMIT ?`
|
|
342
|
+
).all(learningScopeId, learningScopeId, limit) : this.database.prepare(
|
|
343
|
+
`SELECT trace_id FROM runtime_traces
|
|
344
|
+
ORDER BY created_at DESC, trace_id
|
|
345
|
+
LIMIT ?`
|
|
346
|
+
).all(limit);
|
|
347
|
+
return rows.flatMap((row) => {
|
|
348
|
+
const trace = this.getRuntimeTrace(row.trace_id);
|
|
349
|
+
return trace ? [trace] : [];
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
listEvents(traceId) {
|
|
353
|
+
const rows = this.database.prepare(
|
|
354
|
+
`SELECT payload_json FROM runtime_trace_events
|
|
355
|
+
WHERE trace_id = ? ORDER BY at, rowid`
|
|
356
|
+
).all(traceId);
|
|
357
|
+
return rows.map((row) => JSON.parse(row.payload_json));
|
|
358
|
+
}
|
|
359
|
+
putContextPack(manifest) {
|
|
360
|
+
const manifestJson = JSON.stringify(manifest);
|
|
361
|
+
this.database.transaction(() => {
|
|
362
|
+
const existing = this.database.prepare(
|
|
363
|
+
`SELECT manifest_json FROM context_packs
|
|
364
|
+
WHERE context_pack_id = ? OR run_id = ?
|
|
365
|
+
LIMIT 1`
|
|
366
|
+
).get(manifest.contextPackId, manifest.runId);
|
|
367
|
+
if (existing) {
|
|
368
|
+
if (existing.manifest_json !== manifestJson) {
|
|
369
|
+
throw new Error("evolution_context_pack_identity_conflict");
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
this.database.prepare(
|
|
374
|
+
`INSERT INTO context_packs (
|
|
375
|
+
context_pack_id, run_id, learning_scope_id, digest,
|
|
376
|
+
created_at_ms, manifest_json
|
|
377
|
+
) VALUES (?, ?, ?, ?, ?, ?)`
|
|
378
|
+
).run(
|
|
379
|
+
manifest.contextPackId,
|
|
380
|
+
manifest.runId,
|
|
381
|
+
manifest.learningScope.learningScopeId,
|
|
382
|
+
manifest.digest,
|
|
383
|
+
Date.parse(manifest.createdAt),
|
|
384
|
+
manifestJson
|
|
385
|
+
);
|
|
386
|
+
const insertSource = this.database.prepare(
|
|
387
|
+
`INSERT INTO context_pack_sources (
|
|
388
|
+
context_pack_id, source_id, source, boundary_json
|
|
389
|
+
) VALUES (?, ?, ?, ?)`
|
|
390
|
+
);
|
|
391
|
+
for (const source of manifest.sources) {
|
|
392
|
+
insertSource.run(
|
|
393
|
+
manifest.contextPackId,
|
|
394
|
+
source.sourceId,
|
|
395
|
+
source.source,
|
|
396
|
+
JSON.stringify(source)
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
}).immediate();
|
|
400
|
+
}
|
|
401
|
+
getContextPack(contextPackId) {
|
|
402
|
+
return this.readContextPack("context_pack_id", contextPackId);
|
|
403
|
+
}
|
|
404
|
+
getContextPackByRun(runId) {
|
|
405
|
+
return this.readContextPack("run_id", runId);
|
|
406
|
+
}
|
|
407
|
+
readContextPack(column, value) {
|
|
408
|
+
const row = this.database.prepare(`SELECT manifest_json FROM context_packs WHERE ${column} = ?`).get(value);
|
|
409
|
+
return row ? JSON.parse(row.manifest_json) : null;
|
|
410
|
+
}
|
|
411
|
+
putTraceSegments(segments) {
|
|
412
|
+
const insert = this.database.prepare(
|
|
413
|
+
`INSERT INTO trace_segments (
|
|
414
|
+
segment_id, run_id, learning_scope_id, sequence, payload_json
|
|
415
|
+
) VALUES (?, ?, ?, ?, ?)`
|
|
416
|
+
);
|
|
417
|
+
this.database.transaction(() => {
|
|
418
|
+
for (const segment of segments) {
|
|
419
|
+
insertImmutable(this.database, {
|
|
420
|
+
table: "trace_segments",
|
|
421
|
+
idColumn: "segment_id",
|
|
422
|
+
id: segment.segmentId,
|
|
423
|
+
payload: JSON.stringify(segment),
|
|
424
|
+
insert: () => insert.run(
|
|
425
|
+
segment.segmentId,
|
|
426
|
+
segment.runId,
|
|
427
|
+
segment.learningScopeId,
|
|
428
|
+
segment.sequence,
|
|
429
|
+
JSON.stringify(segment)
|
|
430
|
+
)
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}).immediate();
|
|
434
|
+
}
|
|
435
|
+
listTraceSegments(runId) {
|
|
436
|
+
return this.readPayloadRows(
|
|
437
|
+
`SELECT payload_json FROM trace_segments
|
|
438
|
+
WHERE run_id = ? ORDER BY sequence, segment_id`,
|
|
439
|
+
runId
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
putEpisodes(episodes) {
|
|
443
|
+
const insert = this.database.prepare(
|
|
444
|
+
`INSERT INTO episodes (
|
|
445
|
+
episode_id, run_id, learning_scope_id, created_at, payload_json
|
|
446
|
+
) VALUES (?, ?, ?, ?, ?)`
|
|
447
|
+
);
|
|
448
|
+
this.database.transaction(() => {
|
|
449
|
+
for (const episode of episodes) {
|
|
450
|
+
insertImmutable(this.database, {
|
|
451
|
+
table: "episodes",
|
|
452
|
+
idColumn: "episode_id",
|
|
453
|
+
id: episode.episodeId,
|
|
454
|
+
payload: JSON.stringify(episode),
|
|
455
|
+
insert: () => insert.run(
|
|
456
|
+
episode.episodeId,
|
|
457
|
+
episode.runId,
|
|
458
|
+
episode.learningScopeId,
|
|
459
|
+
episode.createdAt,
|
|
460
|
+
JSON.stringify(episode)
|
|
461
|
+
)
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}).immediate();
|
|
465
|
+
}
|
|
466
|
+
listEpisodes(runId) {
|
|
467
|
+
return this.readPayloadRows(
|
|
468
|
+
`SELECT payload_json FROM episodes
|
|
469
|
+
WHERE run_id = ? ORDER BY created_at, episode_id`,
|
|
470
|
+
runId
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
putAnalysisReport(report) {
|
|
474
|
+
const payload = JSON.stringify(report);
|
|
475
|
+
insertImmutable(this.database, {
|
|
476
|
+
table: "analysis_reports",
|
|
477
|
+
idColumn: "report_id",
|
|
478
|
+
id: report.reportId,
|
|
479
|
+
payload,
|
|
480
|
+
insert: () => this.database.prepare(
|
|
481
|
+
`INSERT INTO analysis_reports (
|
|
482
|
+
report_id, run_id, attempt_number, role, created_at, payload_json
|
|
483
|
+
) VALUES (?, ?, ?, ?, ?, ?)`
|
|
484
|
+
).run(
|
|
485
|
+
report.reportId,
|
|
486
|
+
report.runId,
|
|
487
|
+
report.attemptNumber,
|
|
488
|
+
report.role,
|
|
489
|
+
report.createdAt,
|
|
490
|
+
payload
|
|
491
|
+
)
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
listAnalysisReports(runId) {
|
|
495
|
+
return this.readPayloadRows(
|
|
496
|
+
`SELECT payload_json FROM analysis_reports
|
|
497
|
+
WHERE run_id = ? ORDER BY attempt_number, created_at, report_id`,
|
|
498
|
+
runId
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
putRunAttempt(attempt) {
|
|
502
|
+
const existing = this.database.prepare(
|
|
503
|
+
"SELECT payload_json FROM evolution_run_attempts WHERE attempt_id = ?"
|
|
504
|
+
).get(attempt.attemptId);
|
|
505
|
+
if (!existing) {
|
|
506
|
+
this.database.prepare(
|
|
507
|
+
`INSERT INTO evolution_run_attempts (
|
|
508
|
+
attempt_id, run_id, attempt_number, role, provider, status,
|
|
509
|
+
started_at, completed_at, payload_json
|
|
510
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
511
|
+
).run(
|
|
512
|
+
attempt.attemptId,
|
|
513
|
+
attempt.runId,
|
|
514
|
+
attempt.attemptNumber,
|
|
515
|
+
attempt.role,
|
|
516
|
+
attempt.provider,
|
|
517
|
+
attempt.status,
|
|
518
|
+
attempt.startedAt,
|
|
519
|
+
attempt.completedAt,
|
|
520
|
+
JSON.stringify(attempt)
|
|
521
|
+
);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const current = JSON.parse(existing.payload_json);
|
|
525
|
+
if (current.runId !== attempt.runId || current.attemptNumber !== attempt.attemptNumber || current.role !== attempt.role || current.provider !== attempt.provider || current.selectionReason !== attempt.selectionReason || current.startedAt !== attempt.startedAt) {
|
|
526
|
+
throw new Error("evolution_run_attempt_identity_conflict");
|
|
527
|
+
}
|
|
528
|
+
if (current.status !== "running" && current.status !== attempt.status) {
|
|
529
|
+
throw new Error("evolution_run_attempt_terminal");
|
|
530
|
+
}
|
|
531
|
+
this.database.prepare(
|
|
532
|
+
`UPDATE evolution_run_attempts
|
|
533
|
+
SET status = ?, completed_at = ?, payload_json = ?
|
|
534
|
+
WHERE attempt_id = ?`
|
|
535
|
+
).run(
|
|
536
|
+
attempt.status,
|
|
537
|
+
attempt.completedAt,
|
|
538
|
+
JSON.stringify(attempt),
|
|
539
|
+
attempt.attemptId
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
listRunAttempts(runId) {
|
|
543
|
+
return this.readPayloadRows(
|
|
544
|
+
`SELECT payload_json FROM evolution_run_attempts
|
|
545
|
+
WHERE run_id = ? ORDER BY attempt_number, started_at, attempt_id`,
|
|
546
|
+
runId
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
putClaims(claims) {
|
|
550
|
+
const insert = this.database.prepare(
|
|
551
|
+
`INSERT INTO claims (
|
|
552
|
+
claim_id, run_id, learning_scope_id, topic_key, created_at, payload_json
|
|
553
|
+
) VALUES (?, ?, ?, ?, ?, ?)`
|
|
554
|
+
);
|
|
555
|
+
this.database.transaction(() => {
|
|
556
|
+
for (const claim of claims) {
|
|
557
|
+
insertImmutable(this.database, {
|
|
558
|
+
table: "claims",
|
|
559
|
+
idColumn: "claim_id",
|
|
560
|
+
id: claim.claimId,
|
|
561
|
+
payload: JSON.stringify(claim),
|
|
562
|
+
insert: () => insert.run(
|
|
563
|
+
claim.claimId,
|
|
564
|
+
claim.runId,
|
|
565
|
+
claim.learningScopeId,
|
|
566
|
+
claim.topicKey,
|
|
567
|
+
claim.createdAt,
|
|
568
|
+
JSON.stringify(claim)
|
|
569
|
+
)
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}).immediate();
|
|
573
|
+
}
|
|
574
|
+
listClaims(runId) {
|
|
575
|
+
return this.readPayloadRows(
|
|
576
|
+
`SELECT payload_json FROM claims
|
|
577
|
+
WHERE run_id = ? ORDER BY topic_key, claim_id`,
|
|
578
|
+
runId
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
putClaimNovelty(items) {
|
|
582
|
+
const insert = this.database.prepare(
|
|
583
|
+
`INSERT INTO claim_novelty (
|
|
584
|
+
claim_id, run_id, novelty, payload_json
|
|
585
|
+
) VALUES (?, ?, ?, ?)`
|
|
586
|
+
);
|
|
587
|
+
this.database.transaction(() => {
|
|
588
|
+
for (const item of items) {
|
|
589
|
+
const claim = this.database.prepare("SELECT run_id FROM claims WHERE claim_id = ?").get(item.claimId);
|
|
590
|
+
if (!claim) throw new Error("evolution_claim_not_found");
|
|
591
|
+
insertImmutable(this.database, {
|
|
592
|
+
table: "claim_novelty",
|
|
593
|
+
idColumn: "claim_id",
|
|
594
|
+
id: item.claimId,
|
|
595
|
+
payload: JSON.stringify(item),
|
|
596
|
+
insert: () => insert.run(
|
|
597
|
+
item.claimId,
|
|
598
|
+
claim.run_id,
|
|
599
|
+
item.novelty,
|
|
600
|
+
JSON.stringify(item)
|
|
601
|
+
)
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
}).immediate();
|
|
605
|
+
}
|
|
606
|
+
listClaimNovelty(runId) {
|
|
607
|
+
return this.readPayloadRows(
|
|
608
|
+
`SELECT payload_json FROM claim_novelty
|
|
609
|
+
WHERE run_id = ? ORDER BY claim_id`,
|
|
610
|
+
runId
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
readPayloadRows(sql, value) {
|
|
614
|
+
const rows = this.database.prepare(sql).all(value);
|
|
615
|
+
return rows.map((row) => JSON.parse(row.payload_json));
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// backend/src/evolution/storage/foundation-rules.ts
|
|
620
|
+
var EVOLUTION_ACTIVE_STAGES = [
|
|
621
|
+
"snapshotting",
|
|
622
|
+
"segmenting",
|
|
623
|
+
"independent_analysis",
|
|
624
|
+
"cross_questioning",
|
|
625
|
+
"adjudicating",
|
|
626
|
+
"novelty_check",
|
|
627
|
+
"validating"
|
|
628
|
+
];
|
|
629
|
+
var EVOLUTION_TERMINAL_STAGES = [
|
|
630
|
+
"completed",
|
|
631
|
+
"no_material_novelty",
|
|
632
|
+
"partial",
|
|
633
|
+
"failed",
|
|
634
|
+
"cancelled",
|
|
635
|
+
"blocked"
|
|
636
|
+
];
|
|
637
|
+
function evolutionTriggerPriority(type) {
|
|
638
|
+
if (type === "manual") return 0;
|
|
639
|
+
if (type === "event") return 1;
|
|
640
|
+
return 2;
|
|
641
|
+
}
|
|
642
|
+
function parseEvolutionTimestamp(value) {
|
|
643
|
+
const parsed = Date.parse(value);
|
|
644
|
+
if (!Number.isFinite(parsed)) throw new Error("evolution_invalid_timestamp");
|
|
645
|
+
return parsed;
|
|
646
|
+
}
|
|
647
|
+
function isAllowedEvolutionRunTransition(current, next) {
|
|
648
|
+
if (next === "partial" || next === "failed" || next === "blocked") {
|
|
649
|
+
return EVOLUTION_ACTIVE_STAGES.includes(current);
|
|
650
|
+
}
|
|
651
|
+
if (current === "snapshotting") return next === "segmenting";
|
|
652
|
+
if (current === "segmenting") return next === "independent_analysis";
|
|
653
|
+
if (current === "independent_analysis") {
|
|
654
|
+
return next === "cross_questioning";
|
|
655
|
+
}
|
|
656
|
+
if (current === "cross_questioning") {
|
|
657
|
+
return next === "adjudicating" || next === "novelty_check";
|
|
658
|
+
}
|
|
659
|
+
if (current === "adjudicating") return next === "novelty_check";
|
|
660
|
+
if (current === "novelty_check") return next === "validating";
|
|
661
|
+
if (current === "validating") {
|
|
662
|
+
return next === "completed" || next === "no_material_novelty";
|
|
663
|
+
}
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
function assertScheduleMaterialization(current, params) {
|
|
667
|
+
const trigger = params.run.trigger;
|
|
668
|
+
if (trigger.type !== "schedule" || trigger.scheduleId !== current.scheduleId || parseEvolutionTimestamp(trigger.dueAt) < parseEvolutionTimestamp(params.expectedNextDueAt) || parseEvolutionTimestamp(trigger.dueAt) > parseEvolutionTimestamp(params.now) || params.run.learningScopeId !== current.learningScopeId || params.nextSchedule.scheduleId !== current.scheduleId || params.nextSchedule.learningScopeId !== current.learningScopeId || params.nextSchedule.lastDueAt !== trigger.dueAt || params.nextSchedule.lastRunId !== params.run.runId) {
|
|
669
|
+
throw new Error("evolution_schedule_materialization_invalid");
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// backend/src/evolution/storage/foundation-database-rows.ts
|
|
674
|
+
function toRun(row) {
|
|
675
|
+
return {
|
|
676
|
+
runId: row.run_id,
|
|
677
|
+
...row.repository_json ? {
|
|
678
|
+
repository: JSON.parse(
|
|
679
|
+
row.repository_json
|
|
680
|
+
)
|
|
681
|
+
} : {},
|
|
682
|
+
learningScopeId: row.learning_scope_id,
|
|
683
|
+
trigger: JSON.parse(row.trigger_json),
|
|
684
|
+
profile: row.profile,
|
|
685
|
+
providerPolicy: row.provider_policy,
|
|
686
|
+
budget: JSON.parse(row.budget_json),
|
|
687
|
+
dataRange: JSON.parse(row.data_range_json),
|
|
688
|
+
stage: row.stage,
|
|
689
|
+
outcome: row.outcome,
|
|
690
|
+
createdAt: toIso(row.created_at_ms),
|
|
691
|
+
updatedAt: toIso(row.updated_at_ms),
|
|
692
|
+
startedAt: row.started_at_ms === null ? null : toIso(row.started_at_ms),
|
|
693
|
+
completedAt: row.completed_at_ms === null ? null : toIso(row.completed_at_ms),
|
|
694
|
+
attempt: row.attempt
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function nullableTimestamp(value) {
|
|
698
|
+
return value === null ? null : parseEvolutionTimestamp(value);
|
|
699
|
+
}
|
|
700
|
+
function toIso(value) {
|
|
701
|
+
return new Date(value).toISOString();
|
|
702
|
+
}
|
|
703
|
+
function placeholders(count) {
|
|
704
|
+
return Array.from({ length: count }, () => "?").join(", ");
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// backend/src/evolution/storage/foundation-database.ts
|
|
708
|
+
var GLOBAL_LEASE_KEY = "global-evolution-runner-v1";
|
|
709
|
+
var EvolutionFoundationDatabase = class {
|
|
710
|
+
constructor(database2) {
|
|
711
|
+
this.database = database2;
|
|
712
|
+
}
|
|
713
|
+
createRun(run) {
|
|
714
|
+
this.database.prepare(
|
|
715
|
+
`INSERT INTO evolution_runs (
|
|
716
|
+
run_id, learning_scope_id, trigger_type, priority, trigger_json,
|
|
717
|
+
profile, provider_policy, budget_json, data_range_json, stage,
|
|
718
|
+
outcome, created_at_ms, updated_at_ms, started_at_ms,
|
|
719
|
+
completed_at_ms, attempt, owner_id, fencing_token, repository_json
|
|
720
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?)`
|
|
721
|
+
).run(
|
|
722
|
+
run.runId,
|
|
723
|
+
run.learningScopeId,
|
|
724
|
+
run.trigger.type,
|
|
725
|
+
evolutionTriggerPriority(run.trigger.type),
|
|
726
|
+
JSON.stringify(run.trigger),
|
|
727
|
+
run.profile,
|
|
728
|
+
run.providerPolicy,
|
|
729
|
+
JSON.stringify(run.budget),
|
|
730
|
+
JSON.stringify(run.dataRange),
|
|
731
|
+
run.stage,
|
|
732
|
+
run.outcome,
|
|
733
|
+
parseEvolutionTimestamp(run.createdAt),
|
|
734
|
+
parseEvolutionTimestamp(run.updatedAt),
|
|
735
|
+
nullableTimestamp(run.startedAt),
|
|
736
|
+
nullableTimestamp(run.completedAt),
|
|
737
|
+
run.attempt,
|
|
738
|
+
run.repository ? JSON.stringify(run.repository) : null
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
getRun(runId) {
|
|
742
|
+
const row = this.database.prepare("SELECT * FROM evolution_runs WHERE run_id = ?").get(runId);
|
|
743
|
+
return row ? this.presentRun(row) : null;
|
|
744
|
+
}
|
|
745
|
+
listRuns(query = {}) {
|
|
746
|
+
const conditions = [];
|
|
747
|
+
const params = [];
|
|
748
|
+
if (query.learningScopeId) {
|
|
749
|
+
conditions.push(
|
|
750
|
+
`(learning_scope_id = ? OR EXISTS (SELECT 1 FROM evolution_repository_bindings binding, json_each(binding.payload_json, '$.repositoryIds') item WHERE binding.kind = 'run' AND binding.id = evolution_runs.run_id AND item.value = ?))`
|
|
751
|
+
);
|
|
752
|
+
params.push(query.learningScopeId, query.learningScopeId);
|
|
753
|
+
}
|
|
754
|
+
if (query.stage) {
|
|
755
|
+
conditions.push("stage = ?");
|
|
756
|
+
params.push(query.stage);
|
|
757
|
+
}
|
|
758
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
759
|
+
const limit = Math.min(Math.max(query.limit ?? 100, 1), 200);
|
|
760
|
+
const rows = this.database.prepare(
|
|
761
|
+
`SELECT * FROM evolution_runs ${where}
|
|
762
|
+
ORDER BY created_at_ms DESC, run_id DESC LIMIT ?`
|
|
763
|
+
).all(...params, limit);
|
|
764
|
+
return rows.map((row) => this.presentRun(row));
|
|
765
|
+
}
|
|
766
|
+
presentRun(row) {
|
|
767
|
+
const attribution = readRepositoryAttribution(
|
|
768
|
+
this.database,
|
|
769
|
+
"run",
|
|
770
|
+
row.run_id
|
|
771
|
+
);
|
|
772
|
+
return { ...toRun(row), ...attribution ? { attribution } : {} };
|
|
773
|
+
}
|
|
774
|
+
claimNextRun(params) {
|
|
775
|
+
const nowMs = parseEvolutionTimestamp(params.now);
|
|
776
|
+
return this.database.transaction(() => {
|
|
777
|
+
this.recoverExpiredRunsAt(nowMs);
|
|
778
|
+
const active = this.database.prepare(
|
|
779
|
+
`SELECT 1 FROM evolution_runs
|
|
780
|
+
WHERE stage IN (${placeholders(EVOLUTION_ACTIVE_STAGES.length)})
|
|
781
|
+
LIMIT 1`
|
|
782
|
+
).get(...EVOLUTION_ACTIVE_STAGES);
|
|
783
|
+
if (active) return null;
|
|
784
|
+
const lease = this.getLease();
|
|
785
|
+
if (lease && lease.expires_at_ms > nowMs) return null;
|
|
786
|
+
const row = this.database.prepare(
|
|
787
|
+
`SELECT * FROM evolution_runs
|
|
788
|
+
WHERE stage = 'queued'
|
|
789
|
+
ORDER BY priority, created_at_ms, run_id
|
|
790
|
+
LIMIT 1`
|
|
791
|
+
).get();
|
|
792
|
+
if (!row) return null;
|
|
793
|
+
const fencingToken = (lease?.fencing_token ?? 0) + 1;
|
|
794
|
+
const expiresAtMs = nowMs + params.leaseTtlMs;
|
|
795
|
+
this.database.prepare(
|
|
796
|
+
`INSERT INTO evolution_leases (
|
|
797
|
+
lease_key, owner_id, fencing_token, acquired_at_ms, expires_at_ms
|
|
798
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
799
|
+
ON CONFLICT(lease_key) DO UPDATE SET
|
|
800
|
+
owner_id = excluded.owner_id,
|
|
801
|
+
fencing_token = excluded.fencing_token,
|
|
802
|
+
acquired_at_ms = excluded.acquired_at_ms,
|
|
803
|
+
expires_at_ms = excluded.expires_at_ms`
|
|
804
|
+
).run(
|
|
805
|
+
GLOBAL_LEASE_KEY,
|
|
806
|
+
params.ownerId,
|
|
807
|
+
fencingToken,
|
|
808
|
+
nowMs,
|
|
809
|
+
expiresAtMs
|
|
810
|
+
);
|
|
811
|
+
const result = this.database.prepare(
|
|
812
|
+
`UPDATE evolution_runs
|
|
813
|
+
SET stage = 'snapshotting', updated_at_ms = ?,
|
|
814
|
+
started_at_ms = COALESCE(started_at_ms, ?),
|
|
815
|
+
attempt = attempt + 1, owner_id = ?, fencing_token = ?
|
|
816
|
+
WHERE run_id = ? AND stage = 'queued'`
|
|
817
|
+
).run(nowMs, nowMs, params.ownerId, fencingToken, row.run_id);
|
|
818
|
+
if (result.changes !== 1)
|
|
819
|
+
throw new Error("evolution_run_claim_conflict");
|
|
820
|
+
return {
|
|
821
|
+
run: this.requireRun(row.run_id),
|
|
822
|
+
ownerId: params.ownerId,
|
|
823
|
+
fencingToken,
|
|
824
|
+
leaseExpiresAt: toIso(expiresAtMs)
|
|
825
|
+
};
|
|
826
|
+
}).immediate();
|
|
827
|
+
}
|
|
828
|
+
heartbeatRunClaim(params) {
|
|
829
|
+
const nowMs = parseEvolutionTimestamp(params.now);
|
|
830
|
+
const expiresAtMs = nowMs + params.leaseTtlMs;
|
|
831
|
+
const result = this.database.prepare(
|
|
832
|
+
`UPDATE evolution_leases SET expires_at_ms = ?
|
|
833
|
+
WHERE lease_key = ? AND owner_id = ? AND fencing_token = ?
|
|
834
|
+
AND expires_at_ms > ?`
|
|
835
|
+
).run(
|
|
836
|
+
expiresAtMs,
|
|
837
|
+
GLOBAL_LEASE_KEY,
|
|
838
|
+
params.ownerId,
|
|
839
|
+
params.fencingToken,
|
|
840
|
+
nowMs
|
|
841
|
+
);
|
|
842
|
+
if (result.changes !== 1) throw new Error("evolution_lease_lost");
|
|
843
|
+
return toIso(expiresAtMs);
|
|
844
|
+
}
|
|
845
|
+
transitionRun(transition) {
|
|
846
|
+
if (!isAllowedEvolutionRunTransition(
|
|
847
|
+
transition.expectedStage,
|
|
848
|
+
transition.nextStage
|
|
849
|
+
)) {
|
|
850
|
+
throw new Error("evolution_run_transition_invalid");
|
|
851
|
+
}
|
|
852
|
+
const nowMs = parseEvolutionTimestamp(transition.now);
|
|
853
|
+
return this.database.transaction(() => {
|
|
854
|
+
this.assertLease(transition.ownerId, transition.fencingToken, nowMs);
|
|
855
|
+
const current = this.requireRunRow(transition.runId);
|
|
856
|
+
if (current.stage !== transition.expectedStage || current.owner_id !== transition.ownerId || current.fencing_token !== transition.fencingToken) {
|
|
857
|
+
throw new Error("evolution_run_transition_conflict");
|
|
858
|
+
}
|
|
859
|
+
const terminal = EVOLUTION_TERMINAL_STAGES.includes(
|
|
860
|
+
transition.nextStage
|
|
861
|
+
);
|
|
862
|
+
const outcome = terminal ? transition.outcome ?? transition.nextStage : null;
|
|
863
|
+
this.database.prepare(
|
|
864
|
+
`UPDATE evolution_runs
|
|
865
|
+
SET stage = ?, outcome = ?, updated_at_ms = ?,
|
|
866
|
+
completed_at_ms = ?, owner_id = ?, fencing_token = ?
|
|
867
|
+
WHERE run_id = ?`
|
|
868
|
+
).run(
|
|
869
|
+
transition.nextStage,
|
|
870
|
+
outcome,
|
|
871
|
+
nowMs,
|
|
872
|
+
terminal ? nowMs : null,
|
|
873
|
+
terminal ? null : transition.ownerId,
|
|
874
|
+
terminal ? null : transition.fencingToken,
|
|
875
|
+
transition.runId
|
|
876
|
+
);
|
|
877
|
+
if (terminal)
|
|
878
|
+
this.expireLease(transition.ownerId, transition.fencingToken, nowMs);
|
|
879
|
+
return this.requireRun(transition.runId);
|
|
880
|
+
}).immediate();
|
|
881
|
+
}
|
|
882
|
+
cancelRun(runId, now) {
|
|
883
|
+
const nowMs = parseEvolutionTimestamp(now);
|
|
884
|
+
return this.database.transaction(() => {
|
|
885
|
+
const current = this.requireRunRow(runId);
|
|
886
|
+
if (EVOLUTION_TERMINAL_STAGES.includes(current.stage)) {
|
|
887
|
+
return toRun(current);
|
|
888
|
+
}
|
|
889
|
+
this.database.prepare(
|
|
890
|
+
`UPDATE evolution_runs
|
|
891
|
+
SET stage = 'cancelled', outcome = 'cancelled',
|
|
892
|
+
updated_at_ms = ?, completed_at_ms = ?,
|
|
893
|
+
owner_id = NULL, fencing_token = NULL
|
|
894
|
+
WHERE run_id = ?`
|
|
895
|
+
).run(nowMs, nowMs, runId);
|
|
896
|
+
if (current.owner_id && current.fencing_token) {
|
|
897
|
+
this.expireLease(current.owner_id, current.fencing_token, nowMs);
|
|
898
|
+
}
|
|
899
|
+
return this.requireRun(runId);
|
|
900
|
+
}).immediate();
|
|
901
|
+
}
|
|
902
|
+
recoverExpiredRuns(now) {
|
|
903
|
+
return this.database.transaction(
|
|
904
|
+
() => this.recoverExpiredRunsAt(parseEvolutionTimestamp(now))
|
|
905
|
+
).immediate();
|
|
906
|
+
}
|
|
907
|
+
putSchedule(schedule) {
|
|
908
|
+
this.database.prepare(
|
|
909
|
+
`INSERT INTO evolution_schedules (
|
|
910
|
+
schedule_id, learning_scope_id, enabled, updated_at_ms, payload_json
|
|
911
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
912
|
+
ON CONFLICT(schedule_id) DO UPDATE SET
|
|
913
|
+
learning_scope_id = excluded.learning_scope_id,
|
|
914
|
+
enabled = excluded.enabled,
|
|
915
|
+
updated_at_ms = excluded.updated_at_ms,
|
|
916
|
+
payload_json = excluded.payload_json
|
|
917
|
+
WHERE excluded.updated_at_ms >= evolution_schedules.updated_at_ms`
|
|
918
|
+
).run(
|
|
919
|
+
schedule.scheduleId,
|
|
920
|
+
schedule.learningScopeId,
|
|
921
|
+
schedule.enabled ? 1 : 0,
|
|
922
|
+
parseEvolutionTimestamp(schedule.updatedAt),
|
|
923
|
+
JSON.stringify(schedule)
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
getSchedule(scheduleId) {
|
|
927
|
+
const row = this.database.prepare(
|
|
928
|
+
"SELECT payload_json FROM evolution_schedules WHERE schedule_id = ?"
|
|
929
|
+
).get(scheduleId);
|
|
930
|
+
return row ? JSON.parse(row.payload_json) : null;
|
|
931
|
+
}
|
|
932
|
+
listSchedules(learningScopeId) {
|
|
933
|
+
const rows = learningScopeId ? this.database.prepare(
|
|
934
|
+
`SELECT payload_json FROM evolution_schedules
|
|
935
|
+
WHERE learning_scope_id = ? ORDER BY updated_at_ms DESC`
|
|
936
|
+
).all(learningScopeId) : this.database.prepare(
|
|
937
|
+
"SELECT payload_json FROM evolution_schedules ORDER BY updated_at_ms DESC"
|
|
938
|
+
).all();
|
|
939
|
+
return rows.map((row) => JSON.parse(row.payload_json));
|
|
940
|
+
}
|
|
941
|
+
materializeDueSchedule(params) {
|
|
942
|
+
const nowMs = parseEvolutionTimestamp(params.now);
|
|
943
|
+
return this.database.transaction(() => {
|
|
944
|
+
const row = this.database.prepare(
|
|
945
|
+
"SELECT payload_json FROM evolution_schedules WHERE schedule_id = ?"
|
|
946
|
+
).get(params.scheduleId);
|
|
947
|
+
if (!row) return false;
|
|
948
|
+
const current = JSON.parse(row.payload_json);
|
|
949
|
+
if (!current.enabled || current.nextDueAt !== params.expectedNextDueAt || parseEvolutionTimestamp(params.expectedNextDueAt) > nowMs) {
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
assertScheduleMaterialization(current, params);
|
|
953
|
+
this.createRun(params.run);
|
|
954
|
+
this.database.prepare(
|
|
955
|
+
`UPDATE evolution_schedules
|
|
956
|
+
SET learning_scope_id = ?, enabled = ?,
|
|
957
|
+
updated_at_ms = ?, payload_json = ?
|
|
958
|
+
WHERE schedule_id = ?`
|
|
959
|
+
).run(
|
|
960
|
+
params.nextSchedule.learningScopeId,
|
|
961
|
+
params.nextSchedule.enabled ? 1 : 0,
|
|
962
|
+
parseEvolutionTimestamp(params.nextSchedule.updatedAt),
|
|
963
|
+
JSON.stringify(params.nextSchedule),
|
|
964
|
+
params.scheduleId
|
|
965
|
+
);
|
|
966
|
+
return true;
|
|
967
|
+
}).immediate();
|
|
968
|
+
}
|
|
969
|
+
deleteSchedule(scheduleId) {
|
|
970
|
+
return this.database.prepare("DELETE FROM evolution_schedules WHERE schedule_id = ?").run(scheduleId).changes === 1;
|
|
971
|
+
}
|
|
972
|
+
getWatermark(learningScopeId, source) {
|
|
973
|
+
const row = this.database.prepare(
|
|
974
|
+
`SELECT value, run_id, updated_at_ms FROM evolution_watermarks
|
|
975
|
+
WHERE learning_scope_id = ? AND source = ?`
|
|
976
|
+
).get(learningScopeId, source);
|
|
977
|
+
return row ? {
|
|
978
|
+
learningScopeId,
|
|
979
|
+
source,
|
|
980
|
+
value: row.value,
|
|
981
|
+
runId: row.run_id,
|
|
982
|
+
updatedAt: toIso(row.updated_at_ms)
|
|
983
|
+
} : null;
|
|
984
|
+
}
|
|
985
|
+
putWatermark(params) {
|
|
986
|
+
const nowMs = parseEvolutionTimestamp(params.now);
|
|
987
|
+
this.database.transaction(() => {
|
|
988
|
+
this.assertLease(params.ownerId, params.fencingToken, nowMs);
|
|
989
|
+
const run = this.requireRunRow(params.watermark.runId);
|
|
990
|
+
if (run.stage !== "validating" || run.owner_id !== params.ownerId || run.fencing_token !== params.fencingToken || run.learning_scope_id !== params.watermark.learningScopeId) {
|
|
991
|
+
throw new Error("evolution_watermark_fence_mismatch");
|
|
992
|
+
}
|
|
993
|
+
this.database.prepare(
|
|
994
|
+
`INSERT INTO evolution_watermarks (
|
|
995
|
+
learning_scope_id, source, value, run_id, updated_at_ms
|
|
996
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
997
|
+
ON CONFLICT(learning_scope_id, source) DO UPDATE SET
|
|
998
|
+
value = excluded.value,
|
|
999
|
+
run_id = excluded.run_id,
|
|
1000
|
+
updated_at_ms = excluded.updated_at_ms`
|
|
1001
|
+
).run(
|
|
1002
|
+
params.watermark.learningScopeId,
|
|
1003
|
+
params.watermark.source,
|
|
1004
|
+
params.watermark.value,
|
|
1005
|
+
params.watermark.runId,
|
|
1006
|
+
parseEvolutionTimestamp(params.watermark.updatedAt)
|
|
1007
|
+
);
|
|
1008
|
+
}).immediate();
|
|
1009
|
+
}
|
|
1010
|
+
finalizeRun(params, commitKnowledge) {
|
|
1011
|
+
const nowMs = parseEvolutionTimestamp(params.now);
|
|
1012
|
+
return this.database.transaction(() => {
|
|
1013
|
+
this.assertLease(params.ownerId, params.fencingToken, nowMs);
|
|
1014
|
+
const current = this.requireRunRow(params.runId);
|
|
1015
|
+
if (current.stage !== "validating" || current.owner_id !== params.ownerId || current.fencing_token !== params.fencingToken) {
|
|
1016
|
+
throw new Error("evolution_run_finalize_fence_mismatch");
|
|
1017
|
+
}
|
|
1018
|
+
commitKnowledge();
|
|
1019
|
+
this.database.prepare(
|
|
1020
|
+
`UPDATE evolution_runs
|
|
1021
|
+
SET stage = ?, outcome = ?, updated_at_ms = ?,
|
|
1022
|
+
completed_at_ms = ?, owner_id = NULL, fencing_token = NULL
|
|
1023
|
+
WHERE run_id = ?`
|
|
1024
|
+
).run(params.outcome, params.outcome, nowMs, nowMs, params.runId);
|
|
1025
|
+
this.expireLease(params.ownerId, params.fencingToken, nowMs);
|
|
1026
|
+
return this.requireRun(params.runId);
|
|
1027
|
+
}).immediate();
|
|
1028
|
+
}
|
|
1029
|
+
recoverExpiredRunsAt(nowMs) {
|
|
1030
|
+
const result = this.database.prepare(
|
|
1031
|
+
`UPDATE evolution_runs
|
|
1032
|
+
SET stage = 'queued', updated_at_ms = ?,
|
|
1033
|
+
owner_id = NULL, fencing_token = NULL
|
|
1034
|
+
WHERE stage IN (${placeholders(EVOLUTION_ACTIVE_STAGES.length)})
|
|
1035
|
+
AND (
|
|
1036
|
+
owner_id IS NULL OR fencing_token IS NULL OR NOT EXISTS (
|
|
1037
|
+
SELECT 1 FROM evolution_leases
|
|
1038
|
+
WHERE lease_key = ? AND owner_id = evolution_runs.owner_id
|
|
1039
|
+
AND fencing_token = evolution_runs.fencing_token
|
|
1040
|
+
AND expires_at_ms > ?
|
|
1041
|
+
)
|
|
1042
|
+
)`
|
|
1043
|
+
).run(nowMs, ...EVOLUTION_ACTIVE_STAGES, GLOBAL_LEASE_KEY, nowMs);
|
|
1044
|
+
return result.changes;
|
|
1045
|
+
}
|
|
1046
|
+
getLease() {
|
|
1047
|
+
return this.database.prepare(
|
|
1048
|
+
`SELECT owner_id, fencing_token, expires_at_ms
|
|
1049
|
+
FROM evolution_leases WHERE lease_key = ?`
|
|
1050
|
+
).get(GLOBAL_LEASE_KEY) ?? null;
|
|
1051
|
+
}
|
|
1052
|
+
assertLease(ownerId, fencingToken, nowMs) {
|
|
1053
|
+
const lease = this.getLease();
|
|
1054
|
+
if (!lease || lease.owner_id !== ownerId || lease.fencing_token !== fencingToken || lease.expires_at_ms <= nowMs) {
|
|
1055
|
+
throw new Error("evolution_lease_lost");
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
expireLease(ownerId, fencingToken, nowMs) {
|
|
1059
|
+
this.database.prepare(
|
|
1060
|
+
`UPDATE evolution_leases SET expires_at_ms = ?
|
|
1061
|
+
WHERE lease_key = ? AND owner_id = ? AND fencing_token = ?`
|
|
1062
|
+
).run(nowMs, GLOBAL_LEASE_KEY, ownerId, fencingToken);
|
|
1063
|
+
}
|
|
1064
|
+
requireRun(runId) {
|
|
1065
|
+
return toRun(this.requireRunRow(runId));
|
|
1066
|
+
}
|
|
1067
|
+
requireRunRow(runId) {
|
|
1068
|
+
const row = this.database.prepare("SELECT * FROM evolution_runs WHERE run_id = ?").get(runId);
|
|
1069
|
+
if (!row) throw new Error("evolution_run_not_found");
|
|
1070
|
+
return row;
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
// backend/src/evolution/storage/knowledge-database.ts
|
|
1075
|
+
var EvolutionKnowledgeDatabase = class {
|
|
1076
|
+
constructor(database2, foundation) {
|
|
1077
|
+
this.database = database2;
|
|
1078
|
+
this.foundation = foundation;
|
|
1079
|
+
}
|
|
1080
|
+
listCandidates() {
|
|
1081
|
+
const rows = this.database.prepare(
|
|
1082
|
+
`SELECT payload_json
|
|
1083
|
+
FROM (
|
|
1084
|
+
SELECT payload_json,
|
|
1085
|
+
ROW_NUMBER() OVER (
|
|
1086
|
+
PARTITION BY asset_id
|
|
1087
|
+
ORDER BY updated_at DESC, rowid DESC
|
|
1088
|
+
) AS current_revision
|
|
1089
|
+
FROM candidate_asset_revisions
|
|
1090
|
+
)
|
|
1091
|
+
WHERE current_revision = 1`
|
|
1092
|
+
).all();
|
|
1093
|
+
const candidates = rows.map(
|
|
1094
|
+
(row) => JSON.parse(row.payload_json)
|
|
1095
|
+
);
|
|
1096
|
+
if (candidates.length === 0) return [];
|
|
1097
|
+
const bindings = this.database.prepare(
|
|
1098
|
+
`SELECT id, payload_json FROM evolution_repository_bindings
|
|
1099
|
+
WHERE kind = 'candidate' AND id IN (SELECT value FROM json_each(?))`
|
|
1100
|
+
).all(
|
|
1101
|
+
JSON.stringify(candidates.map((candidate) => candidate.assetId))
|
|
1102
|
+
);
|
|
1103
|
+
const attributions = new Map(
|
|
1104
|
+
bindings.map((row) => [
|
|
1105
|
+
row.id,
|
|
1106
|
+
JSON.parse(row.payload_json)
|
|
1107
|
+
])
|
|
1108
|
+
);
|
|
1109
|
+
return candidates.map((candidate) => {
|
|
1110
|
+
const attribution = attributions.get(candidate.assetId);
|
|
1111
|
+
return attribution ? { ...candidate, attribution } : candidate;
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
putCandidate(candidate) {
|
|
1115
|
+
this.database.prepare(
|
|
1116
|
+
`INSERT OR IGNORE INTO candidate_asset_revisions
|
|
1117
|
+
(revision_id, asset_id, learning_scope_id, updated_at, payload_json)
|
|
1118
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
1119
|
+
).run(
|
|
1120
|
+
candidate.revisionId,
|
|
1121
|
+
candidate.assetId,
|
|
1122
|
+
candidate.learningScopeId,
|
|
1123
|
+
candidate.updatedAt,
|
|
1124
|
+
JSON.stringify(candidate)
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
putInsightRevision(params) {
|
|
1128
|
+
this.database.transaction(() => this.insertInsightRevision(params)).immediate();
|
|
1129
|
+
}
|
|
1130
|
+
insertInsightRevision(params) {
|
|
1131
|
+
const current = this.database.prepare(
|
|
1132
|
+
`SELECT insight_id FROM insights
|
|
1133
|
+
WHERE learning_scope_id = ? AND topic_key = ?`
|
|
1134
|
+
).get(params.insight.learningScopeId, params.insight.topicKey);
|
|
1135
|
+
if (current && current.insight_id !== params.insight.insightId) {
|
|
1136
|
+
throw new Error("evolution_insight_identity_conflict");
|
|
1137
|
+
}
|
|
1138
|
+
if (!current) {
|
|
1139
|
+
this.database.prepare(
|
|
1140
|
+
`INSERT INTO insights (
|
|
1141
|
+
insight_id, learning_scope_id, topic_key, current_revision_id,
|
|
1142
|
+
created_at, updated_at
|
|
1143
|
+
) VALUES (?, ?, ?, ?, ?, ?)`
|
|
1144
|
+
).run(
|
|
1145
|
+
params.insight.insightId,
|
|
1146
|
+
params.insight.learningScopeId,
|
|
1147
|
+
params.insight.topicKey,
|
|
1148
|
+
params.revision.revisionId,
|
|
1149
|
+
params.insight.createdAt,
|
|
1150
|
+
params.insight.updatedAt
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
const revisionPayload = JSON.stringify(params.revision);
|
|
1154
|
+
insertImmutable(this.database, {
|
|
1155
|
+
table: "insight_revisions",
|
|
1156
|
+
idColumn: "revision_id",
|
|
1157
|
+
id: params.revision.revisionId,
|
|
1158
|
+
payload: revisionPayload,
|
|
1159
|
+
insert: () => this.database.prepare(
|
|
1160
|
+
`INSERT INTO insight_revisions (
|
|
1161
|
+
revision_id, insight_id, run_id, created_at, payload_json
|
|
1162
|
+
) VALUES (?, ?, ?, ?, ?)`
|
|
1163
|
+
).run(
|
|
1164
|
+
params.revision.revisionId,
|
|
1165
|
+
params.revision.insightId,
|
|
1166
|
+
params.revision.runId,
|
|
1167
|
+
params.revision.createdAt,
|
|
1168
|
+
revisionPayload
|
|
1169
|
+
)
|
|
1170
|
+
});
|
|
1171
|
+
for (const edge of params.contributionEdges) {
|
|
1172
|
+
const payload = JSON.stringify(edge);
|
|
1173
|
+
insertImmutable(this.database, {
|
|
1174
|
+
table: "contribution_edges",
|
|
1175
|
+
idColumn: "edge_id",
|
|
1176
|
+
id: edge.edgeId,
|
|
1177
|
+
payload,
|
|
1178
|
+
insert: () => this.database.prepare(
|
|
1179
|
+
`INSERT INTO contribution_edges (
|
|
1180
|
+
edge_id, insight_revision_id, evidence_id,
|
|
1181
|
+
availability, created_at, payload_json
|
|
1182
|
+
) VALUES (?, ?, ?, ?, ?, ?)`
|
|
1183
|
+
).run(
|
|
1184
|
+
edge.edgeId,
|
|
1185
|
+
edge.insightRevisionId,
|
|
1186
|
+
edge.evidenceId,
|
|
1187
|
+
edge.availability,
|
|
1188
|
+
edge.createdAt,
|
|
1189
|
+
payload
|
|
1190
|
+
)
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
this.database.prepare(
|
|
1194
|
+
`UPDATE insights
|
|
1195
|
+
SET current_revision_id = ?, updated_at = ?
|
|
1196
|
+
WHERE insight_id = ? AND updated_at <= ?
|
|
1197
|
+
AND (? IS NULL OR current_revision_id = ?)`
|
|
1198
|
+
).run(
|
|
1199
|
+
params.revision.revisionId,
|
|
1200
|
+
params.insight.updatedAt,
|
|
1201
|
+
params.insight.insightId,
|
|
1202
|
+
params.insight.updatedAt,
|
|
1203
|
+
params.previousRevisionId ?? null,
|
|
1204
|
+
params.previousRevisionId ?? null
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
listInsights(learningScopeId) {
|
|
1208
|
+
const rows = learningScopeId ? this.database.prepare(
|
|
1209
|
+
`SELECT * FROM insights
|
|
1210
|
+
WHERE learning_scope_id = ? ORDER BY updated_at DESC`
|
|
1211
|
+
).all(learningScopeId) : this.database.prepare("SELECT * FROM insights ORDER BY updated_at DESC").all();
|
|
1212
|
+
return rows.map((row) => this.toInsight(row));
|
|
1213
|
+
}
|
|
1214
|
+
getInsight(insightId) {
|
|
1215
|
+
const row = this.database.prepare("SELECT * FROM insights WHERE insight_id = ?").get(insightId);
|
|
1216
|
+
return row ? this.toInsight(row) : null;
|
|
1217
|
+
}
|
|
1218
|
+
listInsightRevisionsByRun(runId) {
|
|
1219
|
+
return this.readPayloadRows(
|
|
1220
|
+
`SELECT payload_json FROM insight_revisions
|
|
1221
|
+
WHERE run_id = ? ORDER BY created_at, revision_id`,
|
|
1222
|
+
runId
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
listEvidenceDependencies() {
|
|
1226
|
+
const candidates = this.listCandidates();
|
|
1227
|
+
const candidatesByRevision = /* @__PURE__ */ new Map();
|
|
1228
|
+
for (const candidate of candidates) {
|
|
1229
|
+
const group = candidatesByRevision.get(candidate.insightRevisionId) ?? [];
|
|
1230
|
+
group.push(candidate);
|
|
1231
|
+
candidatesByRevision.set(candidate.insightRevisionId, group);
|
|
1232
|
+
}
|
|
1233
|
+
const insights = this.database.prepare("SELECT * FROM insights ORDER BY updated_at DESC").all();
|
|
1234
|
+
if (insights.length === 0) return [];
|
|
1235
|
+
const revisionIds = [
|
|
1236
|
+
.../* @__PURE__ */ new Set([
|
|
1237
|
+
...insights.map((insight) => insight.current_revision_id),
|
|
1238
|
+
...candidatesByRevision.keys()
|
|
1239
|
+
])
|
|
1240
|
+
];
|
|
1241
|
+
const rows = this.database.prepare(
|
|
1242
|
+
`SELECT insight_id, payload_json FROM insight_revisions
|
|
1243
|
+
WHERE revision_id IN (SELECT value FROM json_each(?))
|
|
1244
|
+
ORDER BY created_at, revision_id`
|
|
1245
|
+
).all(JSON.stringify(revisionIds));
|
|
1246
|
+
const revisionsByInsight = /* @__PURE__ */ new Map();
|
|
1247
|
+
for (const row of rows) {
|
|
1248
|
+
const group = revisionsByInsight.get(row.insight_id) ?? [];
|
|
1249
|
+
group.push(JSON.parse(row.payload_json));
|
|
1250
|
+
revisionsByInsight.set(row.insight_id, group);
|
|
1251
|
+
}
|
|
1252
|
+
const dependencies = insights.flatMap(
|
|
1253
|
+
(insight) => (revisionsByInsight.get(insight.insight_id) ?? []).filter(
|
|
1254
|
+
(revision) => revision.insightId === insight.insight_id && (revision.revisionId === insight.current_revision_id || candidatesByRevision.has(revision.revisionId))
|
|
1255
|
+
).map((revision) => ({
|
|
1256
|
+
insight: {
|
|
1257
|
+
insightId: insight.insight_id,
|
|
1258
|
+
learningScopeId: insight.learning_scope_id,
|
|
1259
|
+
topicKey: insight.topic_key,
|
|
1260
|
+
currentRevisionId: insight.current_revision_id,
|
|
1261
|
+
createdAt: insight.created_at,
|
|
1262
|
+
updatedAt: insight.updated_at
|
|
1263
|
+
},
|
|
1264
|
+
revision,
|
|
1265
|
+
candidates: candidatesByRevision.get(revision.revisionId) ?? []
|
|
1266
|
+
}))
|
|
1267
|
+
);
|
|
1268
|
+
if (dependencies.length === 0) return [];
|
|
1269
|
+
const edges = this.database.prepare(
|
|
1270
|
+
`SELECT insight_revision_id, payload_json FROM contribution_edges
|
|
1271
|
+
WHERE insight_revision_id IN (SELECT value FROM json_each(?)) ORDER BY edge_id`
|
|
1272
|
+
).all(
|
|
1273
|
+
JSON.stringify(dependencies.map((item) => item.revision.revisionId))
|
|
1274
|
+
);
|
|
1275
|
+
const edgesByRevision = /* @__PURE__ */ new Map();
|
|
1276
|
+
for (const row of edges) {
|
|
1277
|
+
const group = edgesByRevision.get(row.insight_revision_id) ?? [];
|
|
1278
|
+
group.push(JSON.parse(row.payload_json));
|
|
1279
|
+
edgesByRevision.set(row.insight_revision_id, group);
|
|
1280
|
+
}
|
|
1281
|
+
return dependencies.map((dependency) => ({
|
|
1282
|
+
...dependency,
|
|
1283
|
+
contributionEdges: edgesByRevision.get(dependency.revision.revisionId) ?? []
|
|
1284
|
+
}));
|
|
1285
|
+
}
|
|
1286
|
+
applyEvidenceReconciliation(reconciliation) {
|
|
1287
|
+
this.database.transaction(() => {
|
|
1288
|
+
for (const insight of reconciliation.insights) {
|
|
1289
|
+
this.insertInsightRevision(insight);
|
|
1290
|
+
}
|
|
1291
|
+
const revisionIds = new Set(
|
|
1292
|
+
reconciliation.insights.map((item) => item.revision.revisionId)
|
|
1293
|
+
);
|
|
1294
|
+
for (const candidate of reconciliation.candidates) {
|
|
1295
|
+
if (!revisionIds.has(candidate.insightRevisionId)) {
|
|
1296
|
+
throw new Error("evolution_reconciled_candidate_revision_mismatch");
|
|
1297
|
+
}
|
|
1298
|
+
this.putCandidate(candidate);
|
|
1299
|
+
}
|
|
1300
|
+
}).immediate();
|
|
1301
|
+
}
|
|
1302
|
+
commitRunKnowledge(params) {
|
|
1303
|
+
if (params.outcome === "no_material_novelty" && (params.insights.length > 0 || params.candidates.length > 0)) {
|
|
1304
|
+
throw new Error("evolution_no_material_novelty_has_output");
|
|
1305
|
+
}
|
|
1306
|
+
return this.foundation.finalizeRun(params, () => {
|
|
1307
|
+
for (const insight of params.insights) {
|
|
1308
|
+
if (insight.revision.runId !== params.runId) {
|
|
1309
|
+
throw new Error("evolution_insight_run_mismatch");
|
|
1310
|
+
}
|
|
1311
|
+
this.insertInsightRevision(insight);
|
|
1312
|
+
}
|
|
1313
|
+
const committedRevisionIds = new Set(
|
|
1314
|
+
params.insights.map((item) => item.revision.revisionId)
|
|
1315
|
+
);
|
|
1316
|
+
for (const candidate of params.candidates) {
|
|
1317
|
+
if (!committedRevisionIds.has(candidate.insightRevisionId)) {
|
|
1318
|
+
throw new Error("evolution_candidate_revision_mismatch");
|
|
1319
|
+
}
|
|
1320
|
+
this.putCandidate(candidate);
|
|
1321
|
+
}
|
|
1322
|
+
if (params.watermark) {
|
|
1323
|
+
if (params.watermark.runId !== params.runId) {
|
|
1324
|
+
throw new Error("evolution_watermark_run_mismatch");
|
|
1325
|
+
}
|
|
1326
|
+
this.database.prepare(
|
|
1327
|
+
`INSERT INTO evolution_watermarks (
|
|
1328
|
+
learning_scope_id, source, value, run_id, updated_at_ms
|
|
1329
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
1330
|
+
ON CONFLICT(learning_scope_id, source) DO UPDATE SET
|
|
1331
|
+
value = excluded.value,
|
|
1332
|
+
run_id = excluded.run_id,
|
|
1333
|
+
updated_at_ms = excluded.updated_at_ms`
|
|
1334
|
+
).run(
|
|
1335
|
+
params.watermark.learningScopeId,
|
|
1336
|
+
params.watermark.source,
|
|
1337
|
+
params.watermark.value,
|
|
1338
|
+
params.watermark.runId,
|
|
1339
|
+
Date.parse(params.watermark.updatedAt)
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
toInsight(row) {
|
|
1345
|
+
const membership = this.database.prepare(
|
|
1346
|
+
"SELECT canonical_insight_id FROM evolution_topic_lineage WHERE legacy_insight_id=?"
|
|
1347
|
+
).get(row.insight_id);
|
|
1348
|
+
const members = membership ? this.database.prepare(
|
|
1349
|
+
"SELECT legacy_insight_id, status FROM evolution_topic_lineage WHERE canonical_insight_id=? ORDER BY legacy_insight_id"
|
|
1350
|
+
).all(membership.canonical_insight_id) : [];
|
|
1351
|
+
const attribution = readRepositoryAttribution(
|
|
1352
|
+
this.database,
|
|
1353
|
+
"insight",
|
|
1354
|
+
row.insight_id
|
|
1355
|
+
);
|
|
1356
|
+
return {
|
|
1357
|
+
...attribution ? { attribution } : {},
|
|
1358
|
+
...membership ? {
|
|
1359
|
+
lineage: {
|
|
1360
|
+
canonicalInsightId: membership.canonical_insight_id,
|
|
1361
|
+
memberIds: members.map((member) => member.legacy_insight_id),
|
|
1362
|
+
status: members.some((member) => member.status === "contested") ? "contested" : "resolved"
|
|
1363
|
+
}
|
|
1364
|
+
} : {},
|
|
1365
|
+
insightId: row.insight_id,
|
|
1366
|
+
learningScopeId: row.learning_scope_id,
|
|
1367
|
+
.../^[a-f0-9]{64}$/u.test(row.learning_scope_id) ? { repositoryId: row.learning_scope_id } : {},
|
|
1368
|
+
topicKey: row.topic_key,
|
|
1369
|
+
currentRevisionId: row.current_revision_id,
|
|
1370
|
+
createdAt: row.created_at,
|
|
1371
|
+
updatedAt: row.updated_at,
|
|
1372
|
+
revisions: this.readPayloadRows(
|
|
1373
|
+
`SELECT payload_json FROM insight_revisions
|
|
1374
|
+
WHERE insight_id = ? OR insight_id IN (
|
|
1375
|
+
SELECT legacy_insight_id FROM evolution_topic_lineage WHERE canonical_insight_id = ?
|
|
1376
|
+
) ORDER BY created_at, revision_id`,
|
|
1377
|
+
row.insight_id,
|
|
1378
|
+
row.insight_id
|
|
1379
|
+
)
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
readPayloadRows(sql, ...values) {
|
|
1383
|
+
const rows = this.database.prepare(sql).all(...values);
|
|
1384
|
+
return rows.map((row) => JSON.parse(row.payload_json));
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
// backend/src/evolution/storage/migrations.ts
|
|
1389
|
+
var SCHEMA_VERSION = 6;
|
|
1390
|
+
var MINIMUM_WRITER_VERSION = 2;
|
|
1391
|
+
function migrateEvolutionDatabase(database2) {
|
|
1392
|
+
database2.exec(`
|
|
1393
|
+
CREATE TABLE IF NOT EXISTS evolution_metadata (
|
|
1394
|
+
key TEXT PRIMARY KEY,
|
|
1395
|
+
value TEXT NOT NULL
|
|
1396
|
+
)
|
|
1397
|
+
`);
|
|
1398
|
+
assertCompatibleWriter(database2);
|
|
1399
|
+
const version = database2.prepare("SELECT value FROM evolution_metadata WHERE key = ?").get("schemaVersion");
|
|
1400
|
+
if (version && Number(version.value) < 6) {
|
|
1401
|
+
for (const table of [
|
|
1402
|
+
"evolution_runs",
|
|
1403
|
+
"evolution_schedules",
|
|
1404
|
+
"insights",
|
|
1405
|
+
"candidate_asset_revisions",
|
|
1406
|
+
"evolution_policies",
|
|
1407
|
+
"evolution_watermarks"
|
|
1408
|
+
]) {
|
|
1409
|
+
if (!database2.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?").get(table))
|
|
1410
|
+
continue;
|
|
1411
|
+
if (database2.prepare(`SELECT 1 FROM ${table} LIMIT 1`).get())
|
|
1412
|
+
throw new Error("evolution_repository_migration_required");
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
const state = database2.prepare("SELECT value FROM evolution_metadata WHERE key = ?").get("repositoryMigrationState");
|
|
1416
|
+
if (state && state.value !== "complete" || version && Number(version.value) >= 6 && !state)
|
|
1417
|
+
throw new Error("evolution_repository_migration_incomplete");
|
|
1418
|
+
database2.transaction(() => {
|
|
1419
|
+
database2.exec(`
|
|
1420
|
+
CREATE TABLE IF NOT EXISTS candidate_asset_revisions (
|
|
1421
|
+
revision_id TEXT PRIMARY KEY,
|
|
1422
|
+
asset_id TEXT NOT NULL,
|
|
1423
|
+
learning_scope_id TEXT NOT NULL,
|
|
1424
|
+
updated_at TEXT NOT NULL,
|
|
1425
|
+
payload_json TEXT NOT NULL
|
|
1426
|
+
);
|
|
1427
|
+
CREATE INDEX IF NOT EXISTS candidate_asset_scope_idx
|
|
1428
|
+
ON candidate_asset_revisions (learning_scope_id, asset_id, updated_at);
|
|
1429
|
+
|
|
1430
|
+
CREATE TABLE IF NOT EXISTS evolution_policies (
|
|
1431
|
+
learning_scope_id TEXT PRIMARY KEY,
|
|
1432
|
+
revision INTEGER NOT NULL,
|
|
1433
|
+
payload_json TEXT NOT NULL
|
|
1434
|
+
);
|
|
1435
|
+
|
|
1436
|
+
CREATE TABLE IF NOT EXISTS runtime_traces (
|
|
1437
|
+
trace_id TEXT PRIMARY KEY,
|
|
1438
|
+
run_id TEXT NOT NULL,
|
|
1439
|
+
created_at TEXT NOT NULL,
|
|
1440
|
+
payload_json TEXT NOT NULL
|
|
1441
|
+
);
|
|
1442
|
+
CREATE INDEX IF NOT EXISTS runtime_trace_run_idx
|
|
1443
|
+
ON runtime_traces (run_id, created_at);
|
|
1444
|
+
|
|
1445
|
+
CREATE TABLE IF NOT EXISTS runtime_trace_events (
|
|
1446
|
+
event_id TEXT PRIMARY KEY,
|
|
1447
|
+
trace_id TEXT NOT NULL
|
|
1448
|
+
REFERENCES runtime_traces(trace_id) ON DELETE CASCADE,
|
|
1449
|
+
at TEXT NOT NULL,
|
|
1450
|
+
payload_json TEXT NOT NULL
|
|
1451
|
+
);
|
|
1452
|
+
CREATE INDEX IF NOT EXISTS runtime_trace_event_trace_idx
|
|
1453
|
+
ON runtime_trace_events (trace_id, at);
|
|
1454
|
+
|
|
1455
|
+
CREATE TABLE IF NOT EXISTS evolution_runs (
|
|
1456
|
+
run_id TEXT PRIMARY KEY,
|
|
1457
|
+
learning_scope_id TEXT NOT NULL,
|
|
1458
|
+
trigger_type TEXT NOT NULL,
|
|
1459
|
+
priority INTEGER NOT NULL,
|
|
1460
|
+
trigger_json TEXT NOT NULL,
|
|
1461
|
+
profile TEXT NOT NULL,
|
|
1462
|
+
provider_policy TEXT NOT NULL,
|
|
1463
|
+
budget_json TEXT NOT NULL,
|
|
1464
|
+
data_range_json TEXT NOT NULL,
|
|
1465
|
+
stage TEXT NOT NULL,
|
|
1466
|
+
outcome TEXT,
|
|
1467
|
+
created_at_ms INTEGER NOT NULL,
|
|
1468
|
+
updated_at_ms INTEGER NOT NULL,
|
|
1469
|
+
started_at_ms INTEGER,
|
|
1470
|
+
completed_at_ms INTEGER,
|
|
1471
|
+
attempt INTEGER NOT NULL,
|
|
1472
|
+
owner_id TEXT,
|
|
1473
|
+
fencing_token INTEGER
|
|
1474
|
+
);
|
|
1475
|
+
CREATE INDEX IF NOT EXISTS evolution_run_queue_idx
|
|
1476
|
+
ON evolution_runs (stage, priority, created_at_ms);
|
|
1477
|
+
CREATE INDEX IF NOT EXISTS evolution_run_scope_idx
|
|
1478
|
+
ON evolution_runs (learning_scope_id, created_at_ms DESC);
|
|
1479
|
+
|
|
1480
|
+
CREATE TABLE IF NOT EXISTS evolution_leases (
|
|
1481
|
+
lease_key TEXT PRIMARY KEY,
|
|
1482
|
+
owner_id TEXT NOT NULL,
|
|
1483
|
+
fencing_token INTEGER NOT NULL,
|
|
1484
|
+
acquired_at_ms INTEGER NOT NULL,
|
|
1485
|
+
expires_at_ms INTEGER NOT NULL
|
|
1486
|
+
);
|
|
1487
|
+
|
|
1488
|
+
CREATE TABLE IF NOT EXISTS evolution_schedules (
|
|
1489
|
+
schedule_id TEXT PRIMARY KEY,
|
|
1490
|
+
learning_scope_id TEXT NOT NULL,
|
|
1491
|
+
enabled INTEGER NOT NULL,
|
|
1492
|
+
updated_at_ms INTEGER NOT NULL,
|
|
1493
|
+
payload_json TEXT NOT NULL
|
|
1494
|
+
);
|
|
1495
|
+
CREATE INDEX IF NOT EXISTS evolution_schedule_scope_idx
|
|
1496
|
+
ON evolution_schedules (learning_scope_id, updated_at_ms DESC);
|
|
1497
|
+
|
|
1498
|
+
CREATE TABLE IF NOT EXISTS evolution_watermarks (
|
|
1499
|
+
learning_scope_id TEXT NOT NULL,
|
|
1500
|
+
source TEXT NOT NULL,
|
|
1501
|
+
value TEXT NOT NULL,
|
|
1502
|
+
run_id TEXT NOT NULL,
|
|
1503
|
+
updated_at_ms INTEGER NOT NULL,
|
|
1504
|
+
PRIMARY KEY (learning_scope_id, source)
|
|
1505
|
+
);
|
|
1506
|
+
|
|
1507
|
+
CREATE TABLE IF NOT EXISTS context_packs (
|
|
1508
|
+
context_pack_id TEXT PRIMARY KEY,
|
|
1509
|
+
run_id TEXT NOT NULL UNIQUE,
|
|
1510
|
+
learning_scope_id TEXT NOT NULL,
|
|
1511
|
+
digest TEXT NOT NULL,
|
|
1512
|
+
created_at_ms INTEGER NOT NULL,
|
|
1513
|
+
manifest_json TEXT NOT NULL
|
|
1514
|
+
);
|
|
1515
|
+
CREATE INDEX IF NOT EXISTS context_pack_scope_idx
|
|
1516
|
+
ON context_packs (learning_scope_id, created_at_ms DESC);
|
|
1517
|
+
CREATE INDEX IF NOT EXISTS context_pack_digest_idx
|
|
1518
|
+
ON context_packs (digest);
|
|
1519
|
+
|
|
1520
|
+
CREATE TABLE IF NOT EXISTS context_pack_sources (
|
|
1521
|
+
context_pack_id TEXT NOT NULL
|
|
1522
|
+
REFERENCES context_packs(context_pack_id) ON DELETE CASCADE,
|
|
1523
|
+
source_id TEXT NOT NULL,
|
|
1524
|
+
source TEXT NOT NULL,
|
|
1525
|
+
boundary_json TEXT NOT NULL,
|
|
1526
|
+
PRIMARY KEY (context_pack_id, source_id)
|
|
1527
|
+
);
|
|
1528
|
+
CREATE TRIGGER IF NOT EXISTS context_packs_immutable_update
|
|
1529
|
+
BEFORE UPDATE ON context_packs
|
|
1530
|
+
BEGIN
|
|
1531
|
+
SELECT RAISE(ABORT, 'evolution_context_pack_immutable');
|
|
1532
|
+
END;
|
|
1533
|
+
CREATE TRIGGER IF NOT EXISTS context_pack_sources_immutable_update
|
|
1534
|
+
BEFORE UPDATE ON context_pack_sources
|
|
1535
|
+
BEGIN
|
|
1536
|
+
SELECT RAISE(ABORT, 'evolution_context_pack_source_immutable');
|
|
1537
|
+
END;
|
|
1538
|
+
|
|
1539
|
+
CREATE TABLE IF NOT EXISTS trace_segments (
|
|
1540
|
+
segment_id TEXT PRIMARY KEY,
|
|
1541
|
+
run_id TEXT NOT NULL,
|
|
1542
|
+
learning_scope_id TEXT NOT NULL,
|
|
1543
|
+
sequence INTEGER NOT NULL,
|
|
1544
|
+
payload_json TEXT NOT NULL
|
|
1545
|
+
);
|
|
1546
|
+
CREATE INDEX IF NOT EXISTS trace_segment_run_idx
|
|
1547
|
+
ON trace_segments (run_id, sequence);
|
|
1548
|
+
|
|
1549
|
+
CREATE TABLE IF NOT EXISTS episodes (
|
|
1550
|
+
episode_id TEXT PRIMARY KEY,
|
|
1551
|
+
run_id TEXT NOT NULL,
|
|
1552
|
+
learning_scope_id TEXT NOT NULL,
|
|
1553
|
+
created_at TEXT NOT NULL,
|
|
1554
|
+
payload_json TEXT NOT NULL
|
|
1555
|
+
);
|
|
1556
|
+
CREATE INDEX IF NOT EXISTS episode_run_idx
|
|
1557
|
+
ON episodes (run_id, created_at);
|
|
1558
|
+
|
|
1559
|
+
CREATE TABLE IF NOT EXISTS analysis_reports (
|
|
1560
|
+
report_id TEXT PRIMARY KEY,
|
|
1561
|
+
run_id TEXT NOT NULL,
|
|
1562
|
+
attempt_number INTEGER NOT NULL,
|
|
1563
|
+
role TEXT NOT NULL,
|
|
1564
|
+
created_at TEXT NOT NULL,
|
|
1565
|
+
payload_json TEXT NOT NULL
|
|
1566
|
+
);
|
|
1567
|
+
CREATE INDEX IF NOT EXISTS analysis_report_run_idx
|
|
1568
|
+
ON analysis_reports (run_id, attempt_number, created_at);
|
|
1569
|
+
|
|
1570
|
+
CREATE TABLE IF NOT EXISTS evolution_run_attempts (
|
|
1571
|
+
attempt_id TEXT PRIMARY KEY,
|
|
1572
|
+
run_id TEXT NOT NULL,
|
|
1573
|
+
attempt_number INTEGER NOT NULL,
|
|
1574
|
+
role TEXT NOT NULL,
|
|
1575
|
+
provider TEXT NOT NULL,
|
|
1576
|
+
status TEXT NOT NULL,
|
|
1577
|
+
started_at TEXT NOT NULL,
|
|
1578
|
+
completed_at TEXT,
|
|
1579
|
+
payload_json TEXT NOT NULL
|
|
1580
|
+
);
|
|
1581
|
+
CREATE INDEX IF NOT EXISTS evolution_run_attempt_run_idx
|
|
1582
|
+
ON evolution_run_attempts (run_id, attempt_number, started_at);
|
|
1583
|
+
|
|
1584
|
+
CREATE TABLE IF NOT EXISTS claims (
|
|
1585
|
+
claim_id TEXT PRIMARY KEY,
|
|
1586
|
+
run_id TEXT NOT NULL,
|
|
1587
|
+
learning_scope_id TEXT NOT NULL,
|
|
1588
|
+
topic_key TEXT NOT NULL,
|
|
1589
|
+
created_at TEXT NOT NULL,
|
|
1590
|
+
payload_json TEXT NOT NULL
|
|
1591
|
+
);
|
|
1592
|
+
CREATE INDEX IF NOT EXISTS claim_run_idx
|
|
1593
|
+
ON claims (run_id, topic_key);
|
|
1594
|
+
|
|
1595
|
+
CREATE TABLE IF NOT EXISTS claim_novelty (
|
|
1596
|
+
claim_id TEXT PRIMARY KEY
|
|
1597
|
+
REFERENCES claims(claim_id) ON DELETE CASCADE,
|
|
1598
|
+
run_id TEXT NOT NULL,
|
|
1599
|
+
novelty TEXT NOT NULL,
|
|
1600
|
+
payload_json TEXT NOT NULL
|
|
1601
|
+
);
|
|
1602
|
+
CREATE INDEX IF NOT EXISTS claim_novelty_run_idx
|
|
1603
|
+
ON claim_novelty (run_id, novelty);
|
|
1604
|
+
|
|
1605
|
+
CREATE TABLE IF NOT EXISTS insights (
|
|
1606
|
+
insight_id TEXT PRIMARY KEY,
|
|
1607
|
+
learning_scope_id TEXT NOT NULL,
|
|
1608
|
+
topic_key TEXT NOT NULL,
|
|
1609
|
+
current_revision_id TEXT NOT NULL,
|
|
1610
|
+
created_at TEXT NOT NULL,
|
|
1611
|
+
updated_at TEXT NOT NULL,
|
|
1612
|
+
UNIQUE (learning_scope_id, topic_key)
|
|
1613
|
+
);
|
|
1614
|
+
CREATE INDEX IF NOT EXISTS insight_scope_idx
|
|
1615
|
+
ON insights (learning_scope_id, updated_at DESC);
|
|
1616
|
+
|
|
1617
|
+
CREATE TABLE IF NOT EXISTS insight_revisions (
|
|
1618
|
+
revision_id TEXT PRIMARY KEY,
|
|
1619
|
+
insight_id TEXT NOT NULL
|
|
1620
|
+
REFERENCES insights(insight_id) ON DELETE CASCADE,
|
|
1621
|
+
run_id TEXT NOT NULL,
|
|
1622
|
+
created_at TEXT NOT NULL,
|
|
1623
|
+
payload_json TEXT NOT NULL
|
|
1624
|
+
);
|
|
1625
|
+
CREATE INDEX IF NOT EXISTS insight_revision_insight_idx
|
|
1626
|
+
ON insight_revisions (insight_id, created_at);
|
|
1627
|
+
CREATE INDEX IF NOT EXISTS insight_revision_run_idx
|
|
1628
|
+
ON insight_revisions (run_id, created_at);
|
|
1629
|
+
|
|
1630
|
+
CREATE TABLE IF NOT EXISTS contribution_edges (
|
|
1631
|
+
edge_id TEXT PRIMARY KEY,
|
|
1632
|
+
insight_revision_id TEXT NOT NULL
|
|
1633
|
+
REFERENCES insight_revisions(revision_id) ON DELETE CASCADE,
|
|
1634
|
+
evidence_id TEXT NOT NULL,
|
|
1635
|
+
availability TEXT NOT NULL,
|
|
1636
|
+
created_at TEXT NOT NULL,
|
|
1637
|
+
payload_json TEXT NOT NULL
|
|
1638
|
+
);
|
|
1639
|
+
CREATE INDEX IF NOT EXISTS contribution_edge_revision_idx
|
|
1640
|
+
ON contribution_edges (insight_revision_id, evidence_id);
|
|
1641
|
+
|
|
1642
|
+
CREATE TRIGGER IF NOT EXISTS trace_segments_immutable_update
|
|
1643
|
+
BEFORE UPDATE ON trace_segments
|
|
1644
|
+
BEGIN
|
|
1645
|
+
SELECT RAISE(ABORT, 'evolution_trace_segment_immutable');
|
|
1646
|
+
END;
|
|
1647
|
+
CREATE TRIGGER IF NOT EXISTS episodes_immutable_update
|
|
1648
|
+
BEFORE UPDATE ON episodes
|
|
1649
|
+
BEGIN
|
|
1650
|
+
SELECT RAISE(ABORT, 'evolution_episode_immutable');
|
|
1651
|
+
END;
|
|
1652
|
+
CREATE TRIGGER IF NOT EXISTS analysis_reports_immutable_update
|
|
1653
|
+
BEFORE UPDATE ON analysis_reports
|
|
1654
|
+
BEGIN
|
|
1655
|
+
SELECT RAISE(ABORT, 'evolution_analysis_report_immutable');
|
|
1656
|
+
END;
|
|
1657
|
+
CREATE TRIGGER IF NOT EXISTS claims_immutable_update
|
|
1658
|
+
BEFORE UPDATE ON claims
|
|
1659
|
+
BEGIN
|
|
1660
|
+
SELECT RAISE(ABORT, 'evolution_claim_immutable');
|
|
1661
|
+
END;
|
|
1662
|
+
CREATE TRIGGER IF NOT EXISTS claim_novelty_immutable_update
|
|
1663
|
+
BEFORE UPDATE ON claim_novelty
|
|
1664
|
+
BEGIN
|
|
1665
|
+
SELECT RAISE(ABORT, 'evolution_claim_novelty_immutable');
|
|
1666
|
+
END;
|
|
1667
|
+
CREATE TRIGGER IF NOT EXISTS insight_revisions_immutable_update
|
|
1668
|
+
BEFORE UPDATE ON insight_revisions
|
|
1669
|
+
BEGIN
|
|
1670
|
+
SELECT RAISE(ABORT, 'evolution_insight_revision_immutable');
|
|
1671
|
+
END;
|
|
1672
|
+
CREATE TRIGGER IF NOT EXISTS contribution_edges_immutable_update
|
|
1673
|
+
BEFORE UPDATE ON contribution_edges
|
|
1674
|
+
BEGIN
|
|
1675
|
+
SELECT RAISE(ABORT, 'evolution_contribution_edge_immutable');
|
|
1676
|
+
END;
|
|
1677
|
+
`);
|
|
1678
|
+
initializeEvolutionRepositories(database2);
|
|
1679
|
+
database2.prepare(
|
|
1680
|
+
"INSERT INTO evolution_metadata(key,value) VALUES (?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value"
|
|
1681
|
+
).run("repositoryMigrationState", "complete");
|
|
1682
|
+
database2.prepare(
|
|
1683
|
+
`INSERT INTO evolution_metadata (key, value) VALUES (?, ?)
|
|
1684
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
1685
|
+
).run("schemaVersion", String(SCHEMA_VERSION));
|
|
1686
|
+
database2.prepare(
|
|
1687
|
+
"INSERT INTO evolution_metadata (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
1688
|
+
).run("minimumWriterVersion", String(MINIMUM_WRITER_VERSION));
|
|
1689
|
+
}).immediate();
|
|
1690
|
+
}
|
|
1691
|
+
function assertCompatibleWriter(database2) {
|
|
1692
|
+
const minimumWriterVersion = database2.prepare("SELECT value FROM evolution_metadata WHERE key = ?").get("minimumWriterVersion");
|
|
1693
|
+
if (!minimumWriterVersion) return;
|
|
1694
|
+
const parsed = Number(minimumWriterVersion.value);
|
|
1695
|
+
if (!Number.isInteger(parsed) || parsed > MINIMUM_WRITER_VERSION) {
|
|
1696
|
+
throw new Error("evolution_schema_incompatible");
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
// backend/src/evolution/storage/database.ts
|
|
1701
|
+
var EvolutionActivationDatabase = class {
|
|
1702
|
+
database;
|
|
1703
|
+
foundation;
|
|
1704
|
+
artifactReads;
|
|
1705
|
+
artifacts;
|
|
1706
|
+
knowledge;
|
|
1707
|
+
constructor(databasePath) {
|
|
1708
|
+
(0, import_node_fs.mkdirSync)(import_node_path.default.dirname(databasePath), { recursive: true, mode: 448 });
|
|
1709
|
+
this.database = new import_better_sqlite3.default(databasePath);
|
|
1710
|
+
this.database.pragma("journal_mode = WAL");
|
|
1711
|
+
this.database.pragma("busy_timeout = 5000");
|
|
1712
|
+
this.database.pragma("foreign_keys = ON");
|
|
1713
|
+
try {
|
|
1714
|
+
migrateEvolutionDatabase(this.database);
|
|
1715
|
+
} catch (error) {
|
|
1716
|
+
this.database.close();
|
|
1717
|
+
throw error;
|
|
1718
|
+
}
|
|
1719
|
+
this.foundation = new EvolutionFoundationDatabase(this.database);
|
|
1720
|
+
this.artifacts = new EvolutionArtifactDatabase(this.database);
|
|
1721
|
+
this.artifactReads = new EvolutionArtifactReads(this.database);
|
|
1722
|
+
this.knowledge = new EvolutionKnowledgeDatabase(
|
|
1723
|
+
this.database,
|
|
1724
|
+
this.foundation
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
repository(command) {
|
|
1728
|
+
return executeRepositoryCommand(this.database, this.foundation, command);
|
|
1729
|
+
}
|
|
1730
|
+
getPolicy(learningScopeId) {
|
|
1731
|
+
const row = this.database.prepare(
|
|
1732
|
+
"SELECT payload_json FROM evolution_policies WHERE learning_scope_id = ?"
|
|
1733
|
+
).get(learningScopeId);
|
|
1734
|
+
return row ? JSON.parse(row.payload_json) : null;
|
|
1735
|
+
}
|
|
1736
|
+
putPolicy(policy) {
|
|
1737
|
+
const result = this.database.prepare(
|
|
1738
|
+
`INSERT INTO evolution_policies (learning_scope_id, revision, payload_json)
|
|
1739
|
+
VALUES (?, ?, ?)
|
|
1740
|
+
ON CONFLICT(learning_scope_id) DO UPDATE SET
|
|
1741
|
+
revision = excluded.revision,
|
|
1742
|
+
payload_json = excluded.payload_json
|
|
1743
|
+
WHERE excluded.revision > evolution_policies.revision`
|
|
1744
|
+
).run(policy.learningScopeId, policy.revision, JSON.stringify(policy));
|
|
1745
|
+
if (result.changes !== 1) {
|
|
1746
|
+
throw new Error("evolution_policy_revision_conflict");
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
listCandidates() {
|
|
1750
|
+
return this.knowledge.listCandidates();
|
|
1751
|
+
}
|
|
1752
|
+
putCandidate(candidate) {
|
|
1753
|
+
this.knowledge.putCandidate(candidate);
|
|
1754
|
+
}
|
|
1755
|
+
putRuntimeTrace(trace) {
|
|
1756
|
+
this.artifacts.putRuntimeTrace(trace);
|
|
1757
|
+
}
|
|
1758
|
+
appendRuntimeTraceEvent(event) {
|
|
1759
|
+
this.artifacts.appendRuntimeTraceEvent(event);
|
|
1760
|
+
}
|
|
1761
|
+
getRuntimeTrace(traceId) {
|
|
1762
|
+
return this.artifacts.getRuntimeTrace(traceId);
|
|
1763
|
+
}
|
|
1764
|
+
listRuntimeTraces(runId) {
|
|
1765
|
+
return this.artifacts.listRuntimeTraces(runId);
|
|
1766
|
+
}
|
|
1767
|
+
listRecentRuntimeTraces(learningScopeId, limit) {
|
|
1768
|
+
return this.artifacts.listRecentRuntimeTraces(learningScopeId, limit);
|
|
1769
|
+
}
|
|
1770
|
+
getArtifactOverview(runId) {
|
|
1771
|
+
return this.artifactReads.overview(runId);
|
|
1772
|
+
}
|
|
1773
|
+
getArtifactPage(runId, query) {
|
|
1774
|
+
return this.artifactReads.page(runId, query);
|
|
1775
|
+
}
|
|
1776
|
+
putContextPack(manifest) {
|
|
1777
|
+
this.artifacts.putContextPack(manifest);
|
|
1778
|
+
}
|
|
1779
|
+
getContextPack(contextPackId) {
|
|
1780
|
+
return this.artifacts.getContextPack(contextPackId);
|
|
1781
|
+
}
|
|
1782
|
+
getContextPackByRun(runId) {
|
|
1783
|
+
return this.artifacts.getContextPackByRun(runId);
|
|
1784
|
+
}
|
|
1785
|
+
putTraceSegments(segments) {
|
|
1786
|
+
this.artifacts.putTraceSegments(segments);
|
|
1787
|
+
}
|
|
1788
|
+
listTraceSegments(runId) {
|
|
1789
|
+
return this.artifacts.listTraceSegments(runId);
|
|
1790
|
+
}
|
|
1791
|
+
putEpisodes(episodes) {
|
|
1792
|
+
this.artifacts.putEpisodes(episodes);
|
|
1793
|
+
}
|
|
1794
|
+
listEpisodes(runId) {
|
|
1795
|
+
return this.artifacts.listEpisodes(runId);
|
|
1796
|
+
}
|
|
1797
|
+
putAnalysisReport(report) {
|
|
1798
|
+
this.artifacts.putAnalysisReport(report);
|
|
1799
|
+
}
|
|
1800
|
+
listAnalysisReports(runId) {
|
|
1801
|
+
return this.artifacts.listAnalysisReports(runId);
|
|
1802
|
+
}
|
|
1803
|
+
putRunAttempt(attempt) {
|
|
1804
|
+
this.artifacts.putRunAttempt(attempt);
|
|
1805
|
+
}
|
|
1806
|
+
listRunAttempts(runId) {
|
|
1807
|
+
return this.artifacts.listRunAttempts(runId);
|
|
1808
|
+
}
|
|
1809
|
+
putClaims(claims) {
|
|
1810
|
+
this.artifacts.putClaims(claims);
|
|
1811
|
+
}
|
|
1812
|
+
listClaims(runId) {
|
|
1813
|
+
return this.artifacts.listClaims(runId);
|
|
1814
|
+
}
|
|
1815
|
+
putClaimNovelty(items) {
|
|
1816
|
+
this.artifacts.putClaimNovelty(items);
|
|
1817
|
+
}
|
|
1818
|
+
listClaimNovelty(runId) {
|
|
1819
|
+
return this.artifacts.listClaimNovelty(runId);
|
|
1820
|
+
}
|
|
1821
|
+
putInsightRevision(params) {
|
|
1822
|
+
this.knowledge.putInsightRevision(params);
|
|
1823
|
+
}
|
|
1824
|
+
listInsights(learningScopeId) {
|
|
1825
|
+
return this.knowledge.listInsights(learningScopeId);
|
|
1826
|
+
}
|
|
1827
|
+
getInsight(insightId) {
|
|
1828
|
+
return this.knowledge.getInsight(insightId);
|
|
1829
|
+
}
|
|
1830
|
+
listInsightRevisionsByRun(runId) {
|
|
1831
|
+
return this.knowledge.listInsightRevisionsByRun(runId);
|
|
1832
|
+
}
|
|
1833
|
+
listEvidenceDependencies() {
|
|
1834
|
+
return this.knowledge.listEvidenceDependencies();
|
|
1835
|
+
}
|
|
1836
|
+
applyEvidenceReconciliation(reconciliation) {
|
|
1837
|
+
this.knowledge.applyEvidenceReconciliation(reconciliation);
|
|
1838
|
+
}
|
|
1839
|
+
commitRunKnowledge(params) {
|
|
1840
|
+
return this.knowledge.commitRunKnowledge(params);
|
|
1841
|
+
}
|
|
1842
|
+
finishRunningAttempts(runId, status, completedAt) {
|
|
1843
|
+
this.database.prepare(
|
|
1844
|
+
`UPDATE evolution_run_attempts
|
|
1845
|
+
SET status = ?, completed_at = ?,
|
|
1846
|
+
payload_json = json_set(
|
|
1847
|
+
payload_json,
|
|
1848
|
+
'$.status', ?,
|
|
1849
|
+
'$.completedAt', ?,
|
|
1850
|
+
'$.errorCode', ?
|
|
1851
|
+
)
|
|
1852
|
+
WHERE run_id = ? AND status = 'running'`
|
|
1853
|
+
).run(status, completedAt, status, completedAt, "run_cancelled", runId);
|
|
1854
|
+
}
|
|
1855
|
+
createRun(run) {
|
|
1856
|
+
this.foundation.createRun(run);
|
|
1857
|
+
}
|
|
1858
|
+
getRun(runId) {
|
|
1859
|
+
return this.foundation.getRun(runId);
|
|
1860
|
+
}
|
|
1861
|
+
listRuns(query) {
|
|
1862
|
+
return this.foundation.listRuns(query);
|
|
1863
|
+
}
|
|
1864
|
+
claimNextRun(params) {
|
|
1865
|
+
return this.foundation.claimNextRun(params);
|
|
1866
|
+
}
|
|
1867
|
+
heartbeatRunClaim(params) {
|
|
1868
|
+
return this.foundation.heartbeatRunClaim(params);
|
|
1869
|
+
}
|
|
1870
|
+
transitionRun(transition) {
|
|
1871
|
+
return this.foundation.transitionRun(transition);
|
|
1872
|
+
}
|
|
1873
|
+
cancelRun(runId, now) {
|
|
1874
|
+
const run = this.foundation.cancelRun(runId, now);
|
|
1875
|
+
this.finishRunningAttempts(runId, "cancelled", now);
|
|
1876
|
+
return run;
|
|
1877
|
+
}
|
|
1878
|
+
recoverExpiredRuns(now) {
|
|
1879
|
+
const recovered = this.foundation.recoverExpiredRuns(now);
|
|
1880
|
+
if (recovered > 0) {
|
|
1881
|
+
this.database.prepare(
|
|
1882
|
+
`UPDATE evolution_run_attempts
|
|
1883
|
+
SET status = 'abandoned', completed_at = ?,
|
|
1884
|
+
payload_json = json_set(
|
|
1885
|
+
payload_json,
|
|
1886
|
+
'$.status', 'abandoned',
|
|
1887
|
+
'$.completedAt', ?,
|
|
1888
|
+
'$.errorCode', 'backend_recovered_unknown_call'
|
|
1889
|
+
)
|
|
1890
|
+
WHERE status = 'running'
|
|
1891
|
+
AND run_id IN (
|
|
1892
|
+
SELECT run_id FROM evolution_runs WHERE stage = 'queued'
|
|
1893
|
+
)`
|
|
1894
|
+
).run(now, now);
|
|
1895
|
+
}
|
|
1896
|
+
return recovered;
|
|
1897
|
+
}
|
|
1898
|
+
putSchedule(schedule) {
|
|
1899
|
+
this.foundation.putSchedule(schedule);
|
|
1900
|
+
}
|
|
1901
|
+
getSchedule(scheduleId) {
|
|
1902
|
+
return this.foundation.getSchedule(scheduleId);
|
|
1903
|
+
}
|
|
1904
|
+
listSchedules(learningScopeId) {
|
|
1905
|
+
return this.foundation.listSchedules(learningScopeId);
|
|
1906
|
+
}
|
|
1907
|
+
materializeDueSchedule(params) {
|
|
1908
|
+
return this.foundation.materializeDueSchedule(params);
|
|
1909
|
+
}
|
|
1910
|
+
deleteSchedule(scheduleId) {
|
|
1911
|
+
return this.foundation.deleteSchedule(scheduleId);
|
|
1912
|
+
}
|
|
1913
|
+
getWatermark(learningScopeId, source) {
|
|
1914
|
+
return this.foundation.getWatermark(learningScopeId, source);
|
|
1915
|
+
}
|
|
1916
|
+
putWatermark(params) {
|
|
1917
|
+
this.foundation.putWatermark(params);
|
|
1918
|
+
}
|
|
1919
|
+
integrity() {
|
|
1920
|
+
const result = this.database.pragma("quick_check");
|
|
1921
|
+
return result.every((row) => row.quick_check === "ok");
|
|
1922
|
+
}
|
|
1923
|
+
close() {
|
|
1924
|
+
this.database.close();
|
|
1925
|
+
}
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
// backend/src/evolution/storage/sqlite-worker.ts
|
|
1929
|
+
if (!import_node_worker_threads.parentPort) throw new Error("evolution_worker_parent_port_required");
|
|
1930
|
+
var port = import_node_worker_threads.parentPort;
|
|
1931
|
+
var database = new EvolutionActivationDatabase(
|
|
1932
|
+
import_node_worker_threads.workerData.databasePath
|
|
1933
|
+
);
|
|
1934
|
+
function execute(command) {
|
|
1935
|
+
switch (command.op) {
|
|
1936
|
+
case "repository":
|
|
1937
|
+
return database.repository(command.command);
|
|
1938
|
+
case "list-candidates":
|
|
1939
|
+
return database.listCandidates();
|
|
1940
|
+
case "put-candidate":
|
|
1941
|
+
database.putCandidate(command.candidate);
|
|
1942
|
+
return true;
|
|
1943
|
+
case "get-policy":
|
|
1944
|
+
return database.getPolicy(command.learningScopeId);
|
|
1945
|
+
case "put-policy":
|
|
1946
|
+
database.putPolicy(command.policy);
|
|
1947
|
+
return true;
|
|
1948
|
+
case "put-trace":
|
|
1949
|
+
database.putRuntimeTrace(command.trace);
|
|
1950
|
+
return true;
|
|
1951
|
+
case "append-trace-event":
|
|
1952
|
+
database.appendRuntimeTraceEvent(command.event);
|
|
1953
|
+
return true;
|
|
1954
|
+
case "get-trace":
|
|
1955
|
+
return database.getRuntimeTrace(command.traceId);
|
|
1956
|
+
case "list-traces":
|
|
1957
|
+
return database.listRuntimeTraces(command.runId);
|
|
1958
|
+
case "list-recent-traces":
|
|
1959
|
+
return database.listRecentRuntimeTraces(
|
|
1960
|
+
command.learningScopeId,
|
|
1961
|
+
command.limit
|
|
1962
|
+
);
|
|
1963
|
+
case "artifact-overview":
|
|
1964
|
+
return database.getArtifactOverview(command.runId);
|
|
1965
|
+
case "artifact-page":
|
|
1966
|
+
return database.getArtifactPage(command.runId, command.query);
|
|
1967
|
+
case "put-context-pack":
|
|
1968
|
+
database.putContextPack(command.manifest);
|
|
1969
|
+
return true;
|
|
1970
|
+
case "get-context-pack":
|
|
1971
|
+
return database.getContextPack(command.contextPackId);
|
|
1972
|
+
case "get-context-pack-by-run":
|
|
1973
|
+
return database.getContextPackByRun(command.runId);
|
|
1974
|
+
case "put-trace-segments":
|
|
1975
|
+
database.putTraceSegments(command.segments);
|
|
1976
|
+
return true;
|
|
1977
|
+
case "list-trace-segments":
|
|
1978
|
+
return database.listTraceSegments(command.runId);
|
|
1979
|
+
case "put-episodes":
|
|
1980
|
+
database.putEpisodes(command.episodes);
|
|
1981
|
+
return true;
|
|
1982
|
+
case "list-episodes":
|
|
1983
|
+
return database.listEpisodes(command.runId);
|
|
1984
|
+
case "put-analysis-report":
|
|
1985
|
+
database.putAnalysisReport(command.report);
|
|
1986
|
+
return true;
|
|
1987
|
+
case "list-analysis-reports":
|
|
1988
|
+
return database.listAnalysisReports(command.runId);
|
|
1989
|
+
case "put-run-attempt":
|
|
1990
|
+
database.putRunAttempt(command.attempt);
|
|
1991
|
+
return true;
|
|
1992
|
+
case "list-run-attempts":
|
|
1993
|
+
return database.listRunAttempts(command.runId);
|
|
1994
|
+
case "put-claims":
|
|
1995
|
+
database.putClaims(command.claims);
|
|
1996
|
+
return true;
|
|
1997
|
+
case "list-claims":
|
|
1998
|
+
return database.listClaims(command.runId);
|
|
1999
|
+
case "put-claim-novelty":
|
|
2000
|
+
database.putClaimNovelty(command.items);
|
|
2001
|
+
return true;
|
|
2002
|
+
case "list-claim-novelty":
|
|
2003
|
+
return database.listClaimNovelty(command.runId);
|
|
2004
|
+
case "put-insight-revision":
|
|
2005
|
+
database.putInsightRevision(command);
|
|
2006
|
+
return true;
|
|
2007
|
+
case "list-insights":
|
|
2008
|
+
return database.listInsights(command.learningScopeId);
|
|
2009
|
+
case "get-insight":
|
|
2010
|
+
return database.getInsight(command.insightId);
|
|
2011
|
+
case "list-insight-revisions-by-run":
|
|
2012
|
+
return database.listInsightRevisionsByRun(command.runId);
|
|
2013
|
+
case "list-evidence-dependencies":
|
|
2014
|
+
return database.listEvidenceDependencies();
|
|
2015
|
+
case "apply-evidence-reconciliation":
|
|
2016
|
+
database.applyEvidenceReconciliation(command.reconciliation);
|
|
2017
|
+
return true;
|
|
2018
|
+
case "commit-run-knowledge":
|
|
2019
|
+
return database.commitRunKnowledge(command.params);
|
|
2020
|
+
case "create-run":
|
|
2021
|
+
database.createRun(command.run);
|
|
2022
|
+
return true;
|
|
2023
|
+
case "get-run":
|
|
2024
|
+
return database.getRun(command.runId);
|
|
2025
|
+
case "list-runs":
|
|
2026
|
+
return database.listRuns(command.query);
|
|
2027
|
+
case "claim-next-run":
|
|
2028
|
+
return database.claimNextRun(command);
|
|
2029
|
+
case "heartbeat-run-claim":
|
|
2030
|
+
return database.heartbeatRunClaim(command);
|
|
2031
|
+
case "transition-run":
|
|
2032
|
+
return database.transitionRun(command.transition);
|
|
2033
|
+
case "cancel-run":
|
|
2034
|
+
return database.cancelRun(command.runId, command.now);
|
|
2035
|
+
case "recover-expired-runs":
|
|
2036
|
+
return database.recoverExpiredRuns(command.now);
|
|
2037
|
+
case "put-schedule":
|
|
2038
|
+
database.putSchedule(command.schedule);
|
|
2039
|
+
return true;
|
|
2040
|
+
case "get-schedule":
|
|
2041
|
+
return database.getSchedule(command.scheduleId);
|
|
2042
|
+
case "list-schedules":
|
|
2043
|
+
return database.listSchedules(command.learningScopeId);
|
|
2044
|
+
case "materialize-due-schedule":
|
|
2045
|
+
return database.materializeDueSchedule(command.params);
|
|
2046
|
+
case "delete-schedule":
|
|
2047
|
+
return database.deleteSchedule(command.scheduleId);
|
|
2048
|
+
case "get-watermark":
|
|
2049
|
+
return database.getWatermark(command.learningScopeId, command.source);
|
|
2050
|
+
case "put-watermark":
|
|
2051
|
+
database.putWatermark(command);
|
|
2052
|
+
return true;
|
|
2053
|
+
case "integrity":
|
|
2054
|
+
return database.integrity();
|
|
2055
|
+
case "close":
|
|
2056
|
+
database.close();
|
|
2057
|
+
return true;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
port.on("message", (command) => {
|
|
2061
|
+
let response;
|
|
2062
|
+
try {
|
|
2063
|
+
response = { id: command.id, ok: true, result: execute(command) };
|
|
2064
|
+
} catch (error) {
|
|
2065
|
+
response = {
|
|
2066
|
+
id: command.id,
|
|
2067
|
+
ok: false,
|
|
2068
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
port.postMessage(response);
|
|
2072
|
+
if (command.op === "close") port.close();
|
|
2073
|
+
});
|