@deeplake/hivemind 0.7.46 → 0.7.47
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +64 -0
- package/bundle/cli.js +17230 -4177
- package/codex/bundle/capture.js +185 -0
- package/codex/bundle/commands/auth-login.js +185 -0
- package/codex/bundle/graph-pull-worker.js +185 -0
- package/codex/bundle/pre-tool-use.js +185 -0
- package/codex/bundle/session-start-setup.js +185 -0
- package/codex/bundle/session-start.js +185 -0
- package/codex/bundle/shell/deeplake-shell.js +451 -3
- package/codex/bundle/skillify-worker.js +64 -0
- package/codex/bundle/stop.js +185 -0
- package/codex/skills/hivemind-goals/SKILL.md +157 -0
- package/cursor/bundle/capture.js +185 -0
- package/cursor/bundle/commands/auth-login.js +185 -0
- package/cursor/bundle/graph-pull-worker.js +185 -0
- package/cursor/bundle/pre-tool-use.js +185 -0
- package/cursor/bundle/session-end.js +16 -0
- package/cursor/bundle/session-start.js +596 -6
- package/cursor/bundle/shell/deeplake-shell.js +451 -3
- package/cursor/bundle/skillify-worker.js +64 -0
- package/hermes/bundle/capture.js +185 -0
- package/hermes/bundle/commands/auth-login.js +185 -0
- package/hermes/bundle/graph-pull-worker.js +185 -0
- package/hermes/bundle/pre-tool-use.js +185 -0
- package/hermes/bundle/session-end.js +16 -0
- package/hermes/bundle/session-start.js +596 -6
- package/hermes/bundle/shell/deeplake-shell.js +451 -3
- package/hermes/bundle/skillify-worker.js +64 -0
- package/mcp/bundle/server.js +185 -0
- package/openclaw/dist/chunks/{config-O5PDJQ7Y.js → config-FH6JYSJW.js} +16 -0
- package/openclaw/dist/index.js +262 -2
- package/openclaw/dist/skillify-worker.js +64 -0
- package/openclaw/openclaw.plugin.json +4 -2
- package/openclaw/package.json +1 -1
- package/openclaw/skills/hivemind-goals/SKILL.md +30 -0
- package/package.json +2 -1
|
@@ -104,6 +104,22 @@ function loadConfig() {
|
|
|
104
104
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
105
105
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
106
106
|
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
107
|
+
// Defaults match the table name written into the SQL — keep aligned
|
|
108
|
+
// with RULES_COLUMNS / TASKS_COLUMNS / TASK_EVENTS_COLUMNS in
|
|
109
|
+
// deeplake-schema.ts and with the e2e test-org override convention
|
|
110
|
+
// (memory_test / sessions_test → goals_test, etc.) documented in
|
|
111
|
+
// CLAUDE.md.
|
|
112
|
+
rulesTableName: process.env.HIVEMIND_RULES_TABLE ?? "hivemind_rules",
|
|
113
|
+
tasksTableName: process.env.HIVEMIND_TASKS_TABLE ?? "hivemind_tasks",
|
|
114
|
+
taskEventsTableName: process.env.HIVEMIND_TASK_EVENTS_TABLE ?? "hivemind_task_events",
|
|
115
|
+
// Goals + KPIs (refined design — VFS path classifier maps
|
|
116
|
+
// memory/goal/<user>/<status>/<uuid>.md → hivemind_goals row
|
|
117
|
+
// memory/kpi/<uuid>/<kpi_id>.md → hivemind_kpis row
|
|
118
|
+
// See src/shell/deeplake-fs.ts for the translation logic and
|
|
119
|
+
// GOALS_COLUMNS / KPIS_COLUMNS in deeplake-schema.ts for the
|
|
120
|
+
// table shape.
|
|
121
|
+
goalsTableName: process.env.HIVEMIND_GOALS_TABLE ?? "hivemind_goals",
|
|
122
|
+
kpisTableName: process.env.HIVEMIND_KPIS_TABLE ?? "hivemind_kpis",
|
|
107
123
|
codebaseTableName: process.env.HIVEMIND_CODEBASE_TABLE ?? "codebase",
|
|
108
124
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join(home, ".deeplake", "memory")
|
|
109
125
|
};
|
|
@@ -205,6 +221,65 @@ var SKILLS_COLUMNS = Object.freeze([
|
|
|
205
221
|
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
206
222
|
{ name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
207
223
|
]);
|
|
224
|
+
var RULES_COLUMNS = Object.freeze([
|
|
225
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
226
|
+
{ name: "rule_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
227
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
228
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'team'" },
|
|
229
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
230
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
231
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
232
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
233
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
234
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
235
|
+
]);
|
|
236
|
+
var TASKS_COLUMNS = Object.freeze([
|
|
237
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
238
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
239
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
240
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
|
|
241
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
242
|
+
{ name: "assigned_to", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
243
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
244
|
+
{ name: "kpis", sql: "JSONB" },
|
|
245
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
246
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
247
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
248
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
249
|
+
]);
|
|
250
|
+
var TASK_EVENTS_COLUMNS = Object.freeze([
|
|
251
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
252
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
253
|
+
{ name: "task_version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
254
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
255
|
+
{ name: "value", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
256
|
+
{ name: "note", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
257
|
+
{ name: "source", sql: "TEXT NOT NULL DEFAULT 'user'" },
|
|
258
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
259
|
+
{ name: "ts", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
260
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
261
|
+
]);
|
|
262
|
+
var GOALS_COLUMNS = Object.freeze([
|
|
263
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
264
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
265
|
+
{ name: "owner", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
266
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'opened'" },
|
|
267
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
268
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
269
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
270
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
271
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
272
|
+
]);
|
|
273
|
+
var KPIS_COLUMNS = Object.freeze([
|
|
274
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
275
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
276
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
277
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
278
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
279
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
280
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
281
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
282
|
+
]);
|
|
208
283
|
function validateSchema(label, cols) {
|
|
209
284
|
const seen = /* @__PURE__ */ new Set();
|
|
210
285
|
for (const col of cols) {
|
|
@@ -248,6 +323,11 @@ var CODEBASE_COLUMNS = Object.freeze([
|
|
|
248
323
|
validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
|
|
249
324
|
validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
|
|
250
325
|
validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
|
|
326
|
+
validateSchema("RULES_COLUMNS", RULES_COLUMNS);
|
|
327
|
+
validateSchema("TASKS_COLUMNS", TASKS_COLUMNS);
|
|
328
|
+
validateSchema("TASK_EVENTS_COLUMNS", TASK_EVENTS_COLUMNS);
|
|
329
|
+
validateSchema("GOALS_COLUMNS", GOALS_COLUMNS);
|
|
330
|
+
validateSchema("KPIS_COLUMNS", KPIS_COLUMNS);
|
|
251
331
|
validateSchema("CODEBASE_COLUMNS", CODEBASE_COLUMNS);
|
|
252
332
|
function buildCreateTableSql(tableName, cols) {
|
|
253
333
|
const safe = sqlIdent(tableName);
|
|
@@ -844,6 +924,111 @@ var DeeplakeApi = class {
|
|
|
844
924
|
await this.healSchema(safe, SKILLS_COLUMNS);
|
|
845
925
|
await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
|
|
846
926
|
}
|
|
927
|
+
/**
|
|
928
|
+
* Create the rules table.
|
|
929
|
+
*
|
|
930
|
+
* One row per rule version (same write pattern as skills): edits INSERT
|
|
931
|
+
* a fresh row with version+1, reads pick latest per rule_id via
|
|
932
|
+
* `ORDER BY version DESC LIMIT 1`. Sidesteps the Deeplake
|
|
933
|
+
* UPDATE-coalescing quirk by never UPDATEing.
|
|
934
|
+
*/
|
|
935
|
+
async ensureRulesTable(name) {
|
|
936
|
+
const safe = sqlIdent(name);
|
|
937
|
+
const tables = await this.listTables();
|
|
938
|
+
if (!tables.includes(safe)) {
|
|
939
|
+
log3(`table "${safe}" not found, creating`);
|
|
940
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, RULES_COLUMNS), safe);
|
|
941
|
+
log3(`table "${safe}" created`);
|
|
942
|
+
if (!tables.includes(safe))
|
|
943
|
+
this._tablesCache = [...tables, safe];
|
|
944
|
+
}
|
|
945
|
+
await this.healSchema(safe, RULES_COLUMNS);
|
|
946
|
+
await this.ensureLookupIndex(safe, "rule_id_version", `("rule_id", "version")`);
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Create the tasks table.
|
|
950
|
+
*
|
|
951
|
+
* Same write pattern as rules + skills. `kpis` is a nullable JSONB
|
|
952
|
+
* column with the agent's KPI metadata; KPI current values come from
|
|
953
|
+
* `task_events` (SUM(value)), not this snapshot.
|
|
954
|
+
*/
|
|
955
|
+
async ensureTasksTable(name) {
|
|
956
|
+
const safe = sqlIdent(name);
|
|
957
|
+
const tables = await this.listTables();
|
|
958
|
+
if (!tables.includes(safe)) {
|
|
959
|
+
log3(`table "${safe}" not found, creating`);
|
|
960
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASKS_COLUMNS), safe);
|
|
961
|
+
log3(`table "${safe}" created`);
|
|
962
|
+
if (!tables.includes(safe))
|
|
963
|
+
this._tablesCache = [...tables, safe];
|
|
964
|
+
}
|
|
965
|
+
await this.healSchema(safe, TASKS_COLUMNS);
|
|
966
|
+
await this.ensureLookupIndex(safe, "task_id_version", `("task_id", "version")`);
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Create the task-events table.
|
|
970
|
+
*
|
|
971
|
+
* Append-only. Every INSERT is a fresh row; never UPDATE. KPI current
|
|
972
|
+
* value is `SUM(value) WHERE task_id=? AND kpi_id=?`. Index on
|
|
973
|
+
* (task_id, kpi_id) is the canonical aggregation key.
|
|
974
|
+
*/
|
|
975
|
+
async ensureTaskEventsTable(name) {
|
|
976
|
+
const safe = sqlIdent(name);
|
|
977
|
+
const tables = await this.listTables();
|
|
978
|
+
if (!tables.includes(safe)) {
|
|
979
|
+
log3(`table "${safe}" not found, creating`);
|
|
980
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASK_EVENTS_COLUMNS), safe);
|
|
981
|
+
log3(`table "${safe}" created`);
|
|
982
|
+
if (!tables.includes(safe))
|
|
983
|
+
this._tablesCache = [...tables, safe];
|
|
984
|
+
}
|
|
985
|
+
await this.healSchema(safe, TASK_EVENTS_COLUMNS);
|
|
986
|
+
await this.ensureLookupIndex(safe, "task_id_kpi_id", `("task_id", "kpi_id")`);
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Create the goals table.
|
|
990
|
+
*
|
|
991
|
+
* Backed by the VFS path convention memory/goal/<owner>/<status>/<goal_id>.md.
|
|
992
|
+
* INSERT-only version-bumped: rm and mv operations translate to fresh
|
|
993
|
+
* v=N+1 rows (status flips for mv → closed; rm is the same soft-close).
|
|
994
|
+
* The (goal_id, version) index lets the VFS dispatch a cheap latest-row
|
|
995
|
+
* read on cat / Read of a single goal.
|
|
996
|
+
*/
|
|
997
|
+
async ensureGoalsTable(name) {
|
|
998
|
+
const safe = sqlIdent(name);
|
|
999
|
+
const tables = await this.listTables();
|
|
1000
|
+
if (!tables.includes(safe)) {
|
|
1001
|
+
log3(`table "${safe}" not found, creating`);
|
|
1002
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, GOALS_COLUMNS), safe);
|
|
1003
|
+
log3(`table "${safe}" created`);
|
|
1004
|
+
if (!tables.includes(safe))
|
|
1005
|
+
this._tablesCache = [...tables, safe];
|
|
1006
|
+
}
|
|
1007
|
+
await this.healSchema(safe, GOALS_COLUMNS);
|
|
1008
|
+
await this.ensureLookupIndex(safe, "goal_id_version", `("goal_id", "version")`);
|
|
1009
|
+
await this.ensureLookupIndex(safe, "owner_status", `("owner", "status")`);
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Create the kpis table.
|
|
1013
|
+
*
|
|
1014
|
+
* Backed by memory/kpi/<goal_id>/<kpi_id>.md. KPI rows do NOT carry
|
|
1015
|
+
* owner — ownership derives from the parent goal via logical join on
|
|
1016
|
+
* goal_id. INSERT-only version-bumped. (goal_id, kpi_id) index is the
|
|
1017
|
+
* canonical lookup the VFS uses on Read and Write.
|
|
1018
|
+
*/
|
|
1019
|
+
async ensureKpisTable(name) {
|
|
1020
|
+
const safe = sqlIdent(name);
|
|
1021
|
+
const tables = await this.listTables();
|
|
1022
|
+
if (!tables.includes(safe)) {
|
|
1023
|
+
log3(`table "${safe}" not found, creating`);
|
|
1024
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, KPIS_COLUMNS), safe);
|
|
1025
|
+
log3(`table "${safe}" created`);
|
|
1026
|
+
if (!tables.includes(safe))
|
|
1027
|
+
this._tablesCache = [...tables, safe];
|
|
1028
|
+
}
|
|
1029
|
+
await this.healSchema(safe, KPIS_COLUMNS);
|
|
1030
|
+
await this.ensureLookupIndex(safe, "goal_id_kpi_id", `("goal_id", "kpi_id")`);
|
|
1031
|
+
}
|
|
847
1032
|
};
|
|
848
1033
|
|
|
849
1034
|
// dist/src/shell/grep-core.js
|
|
@@ -126,6 +126,22 @@ function loadConfig() {
|
|
|
126
126
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
127
127
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
128
128
|
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
129
|
+
// Defaults match the table name written into the SQL — keep aligned
|
|
130
|
+
// with RULES_COLUMNS / TASKS_COLUMNS / TASK_EVENTS_COLUMNS in
|
|
131
|
+
// deeplake-schema.ts and with the e2e test-org override convention
|
|
132
|
+
// (memory_test / sessions_test → goals_test, etc.) documented in
|
|
133
|
+
// CLAUDE.md.
|
|
134
|
+
rulesTableName: process.env.HIVEMIND_RULES_TABLE ?? "hivemind_rules",
|
|
135
|
+
tasksTableName: process.env.HIVEMIND_TASKS_TABLE ?? "hivemind_tasks",
|
|
136
|
+
taskEventsTableName: process.env.HIVEMIND_TASK_EVENTS_TABLE ?? "hivemind_task_events",
|
|
137
|
+
// Goals + KPIs (refined design — VFS path classifier maps
|
|
138
|
+
// memory/goal/<user>/<status>/<uuid>.md → hivemind_goals row
|
|
139
|
+
// memory/kpi/<uuid>/<kpi_id>.md → hivemind_kpis row
|
|
140
|
+
// See src/shell/deeplake-fs.ts for the translation logic and
|
|
141
|
+
// GOALS_COLUMNS / KPIS_COLUMNS in deeplake-schema.ts for the
|
|
142
|
+
// table shape.
|
|
143
|
+
goalsTableName: process.env.HIVEMIND_GOALS_TABLE ?? "hivemind_goals",
|
|
144
|
+
kpisTableName: process.env.HIVEMIND_KPIS_TABLE ?? "hivemind_kpis",
|
|
129
145
|
codebaseTableName: process.env.HIVEMIND_CODEBASE_TABLE ?? "codebase",
|
|
130
146
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join3(home, ".deeplake", "memory")
|
|
131
147
|
};
|
|
@@ -218,6 +234,65 @@ var SKILLS_COLUMNS = Object.freeze([
|
|
|
218
234
|
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
219
235
|
{ name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
220
236
|
]);
|
|
237
|
+
var RULES_COLUMNS = Object.freeze([
|
|
238
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
239
|
+
{ name: "rule_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
240
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
241
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'team'" },
|
|
242
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
243
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
244
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
245
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
246
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
247
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
248
|
+
]);
|
|
249
|
+
var TASKS_COLUMNS = Object.freeze([
|
|
250
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
251
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
252
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
253
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
|
|
254
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
255
|
+
{ name: "assigned_to", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
256
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
257
|
+
{ name: "kpis", sql: "JSONB" },
|
|
258
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
259
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
260
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
261
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
262
|
+
]);
|
|
263
|
+
var TASK_EVENTS_COLUMNS = Object.freeze([
|
|
264
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
265
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
266
|
+
{ name: "task_version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
267
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
268
|
+
{ name: "value", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
269
|
+
{ name: "note", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
270
|
+
{ name: "source", sql: "TEXT NOT NULL DEFAULT 'user'" },
|
|
271
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
272
|
+
{ name: "ts", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
273
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
274
|
+
]);
|
|
275
|
+
var GOALS_COLUMNS = Object.freeze([
|
|
276
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
277
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
278
|
+
{ name: "owner", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
279
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'opened'" },
|
|
280
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
281
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
282
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
283
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
284
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
285
|
+
]);
|
|
286
|
+
var KPIS_COLUMNS = Object.freeze([
|
|
287
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
288
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
289
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
290
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
291
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
292
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
293
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
294
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
295
|
+
]);
|
|
221
296
|
function validateSchema(label, cols) {
|
|
222
297
|
const seen = /* @__PURE__ */ new Set();
|
|
223
298
|
for (const col of cols) {
|
|
@@ -261,6 +336,11 @@ var CODEBASE_COLUMNS = Object.freeze([
|
|
|
261
336
|
validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
|
|
262
337
|
validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
|
|
263
338
|
validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
|
|
339
|
+
validateSchema("RULES_COLUMNS", RULES_COLUMNS);
|
|
340
|
+
validateSchema("TASKS_COLUMNS", TASKS_COLUMNS);
|
|
341
|
+
validateSchema("TASK_EVENTS_COLUMNS", TASK_EVENTS_COLUMNS);
|
|
342
|
+
validateSchema("GOALS_COLUMNS", GOALS_COLUMNS);
|
|
343
|
+
validateSchema("KPIS_COLUMNS", KPIS_COLUMNS);
|
|
264
344
|
validateSchema("CODEBASE_COLUMNS", CODEBASE_COLUMNS);
|
|
265
345
|
function buildCreateTableSql(tableName, cols) {
|
|
266
346
|
const safe = sqlIdent(tableName);
|
|
@@ -839,6 +919,111 @@ var DeeplakeApi = class {
|
|
|
839
919
|
await this.healSchema(safe, SKILLS_COLUMNS);
|
|
840
920
|
await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
|
|
841
921
|
}
|
|
922
|
+
/**
|
|
923
|
+
* Create the rules table.
|
|
924
|
+
*
|
|
925
|
+
* One row per rule version (same write pattern as skills): edits INSERT
|
|
926
|
+
* a fresh row with version+1, reads pick latest per rule_id via
|
|
927
|
+
* `ORDER BY version DESC LIMIT 1`. Sidesteps the Deeplake
|
|
928
|
+
* UPDATE-coalescing quirk by never UPDATEing.
|
|
929
|
+
*/
|
|
930
|
+
async ensureRulesTable(name) {
|
|
931
|
+
const safe = sqlIdent(name);
|
|
932
|
+
const tables = await this.listTables();
|
|
933
|
+
if (!tables.includes(safe)) {
|
|
934
|
+
log3(`table "${safe}" not found, creating`);
|
|
935
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, RULES_COLUMNS), safe);
|
|
936
|
+
log3(`table "${safe}" created`);
|
|
937
|
+
if (!tables.includes(safe))
|
|
938
|
+
this._tablesCache = [...tables, safe];
|
|
939
|
+
}
|
|
940
|
+
await this.healSchema(safe, RULES_COLUMNS);
|
|
941
|
+
await this.ensureLookupIndex(safe, "rule_id_version", `("rule_id", "version")`);
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Create the tasks table.
|
|
945
|
+
*
|
|
946
|
+
* Same write pattern as rules + skills. `kpis` is a nullable JSONB
|
|
947
|
+
* column with the agent's KPI metadata; KPI current values come from
|
|
948
|
+
* `task_events` (SUM(value)), not this snapshot.
|
|
949
|
+
*/
|
|
950
|
+
async ensureTasksTable(name) {
|
|
951
|
+
const safe = sqlIdent(name);
|
|
952
|
+
const tables = await this.listTables();
|
|
953
|
+
if (!tables.includes(safe)) {
|
|
954
|
+
log3(`table "${safe}" not found, creating`);
|
|
955
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASKS_COLUMNS), safe);
|
|
956
|
+
log3(`table "${safe}" created`);
|
|
957
|
+
if (!tables.includes(safe))
|
|
958
|
+
this._tablesCache = [...tables, safe];
|
|
959
|
+
}
|
|
960
|
+
await this.healSchema(safe, TASKS_COLUMNS);
|
|
961
|
+
await this.ensureLookupIndex(safe, "task_id_version", `("task_id", "version")`);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Create the task-events table.
|
|
965
|
+
*
|
|
966
|
+
* Append-only. Every INSERT is a fresh row; never UPDATE. KPI current
|
|
967
|
+
* value is `SUM(value) WHERE task_id=? AND kpi_id=?`. Index on
|
|
968
|
+
* (task_id, kpi_id) is the canonical aggregation key.
|
|
969
|
+
*/
|
|
970
|
+
async ensureTaskEventsTable(name) {
|
|
971
|
+
const safe = sqlIdent(name);
|
|
972
|
+
const tables = await this.listTables();
|
|
973
|
+
if (!tables.includes(safe)) {
|
|
974
|
+
log3(`table "${safe}" not found, creating`);
|
|
975
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASK_EVENTS_COLUMNS), safe);
|
|
976
|
+
log3(`table "${safe}" created`);
|
|
977
|
+
if (!tables.includes(safe))
|
|
978
|
+
this._tablesCache = [...tables, safe];
|
|
979
|
+
}
|
|
980
|
+
await this.healSchema(safe, TASK_EVENTS_COLUMNS);
|
|
981
|
+
await this.ensureLookupIndex(safe, "task_id_kpi_id", `("task_id", "kpi_id")`);
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Create the goals table.
|
|
985
|
+
*
|
|
986
|
+
* Backed by the VFS path convention memory/goal/<owner>/<status>/<goal_id>.md.
|
|
987
|
+
* INSERT-only version-bumped: rm and mv operations translate to fresh
|
|
988
|
+
* v=N+1 rows (status flips for mv → closed; rm is the same soft-close).
|
|
989
|
+
* The (goal_id, version) index lets the VFS dispatch a cheap latest-row
|
|
990
|
+
* read on cat / Read of a single goal.
|
|
991
|
+
*/
|
|
992
|
+
async ensureGoalsTable(name) {
|
|
993
|
+
const safe = sqlIdent(name);
|
|
994
|
+
const tables = await this.listTables();
|
|
995
|
+
if (!tables.includes(safe)) {
|
|
996
|
+
log3(`table "${safe}" not found, creating`);
|
|
997
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, GOALS_COLUMNS), safe);
|
|
998
|
+
log3(`table "${safe}" created`);
|
|
999
|
+
if (!tables.includes(safe))
|
|
1000
|
+
this._tablesCache = [...tables, safe];
|
|
1001
|
+
}
|
|
1002
|
+
await this.healSchema(safe, GOALS_COLUMNS);
|
|
1003
|
+
await this.ensureLookupIndex(safe, "goal_id_version", `("goal_id", "version")`);
|
|
1004
|
+
await this.ensureLookupIndex(safe, "owner_status", `("owner", "status")`);
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Create the kpis table.
|
|
1008
|
+
*
|
|
1009
|
+
* Backed by memory/kpi/<goal_id>/<kpi_id>.md. KPI rows do NOT carry
|
|
1010
|
+
* owner — ownership derives from the parent goal via logical join on
|
|
1011
|
+
* goal_id. INSERT-only version-bumped. (goal_id, kpi_id) index is the
|
|
1012
|
+
* canonical lookup the VFS uses on Read and Write.
|
|
1013
|
+
*/
|
|
1014
|
+
async ensureKpisTable(name) {
|
|
1015
|
+
const safe = sqlIdent(name);
|
|
1016
|
+
const tables = await this.listTables();
|
|
1017
|
+
if (!tables.includes(safe)) {
|
|
1018
|
+
log3(`table "${safe}" not found, creating`);
|
|
1019
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, KPIS_COLUMNS), safe);
|
|
1020
|
+
log3(`table "${safe}" created`);
|
|
1021
|
+
if (!tables.includes(safe))
|
|
1022
|
+
this._tablesCache = [...tables, safe];
|
|
1023
|
+
}
|
|
1024
|
+
await this.healSchema(safe, KPIS_COLUMNS);
|
|
1025
|
+
await this.ensureLookupIndex(safe, "goal_id_kpi_id", `("goal_id", "kpi_id")`);
|
|
1026
|
+
}
|
|
842
1027
|
};
|
|
843
1028
|
|
|
844
1029
|
// dist/src/utils/stdin.js
|
|
@@ -324,6 +324,22 @@ function loadConfig() {
|
|
|
324
324
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
325
325
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
326
326
|
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
327
|
+
// Defaults match the table name written into the SQL — keep aligned
|
|
328
|
+
// with RULES_COLUMNS / TASKS_COLUMNS / TASK_EVENTS_COLUMNS in
|
|
329
|
+
// deeplake-schema.ts and with the e2e test-org override convention
|
|
330
|
+
// (memory_test / sessions_test → goals_test, etc.) documented in
|
|
331
|
+
// CLAUDE.md.
|
|
332
|
+
rulesTableName: process.env.HIVEMIND_RULES_TABLE ?? "hivemind_rules",
|
|
333
|
+
tasksTableName: process.env.HIVEMIND_TASKS_TABLE ?? "hivemind_tasks",
|
|
334
|
+
taskEventsTableName: process.env.HIVEMIND_TASK_EVENTS_TABLE ?? "hivemind_task_events",
|
|
335
|
+
// Goals + KPIs (refined design — VFS path classifier maps
|
|
336
|
+
// memory/goal/<user>/<status>/<uuid>.md → hivemind_goals row
|
|
337
|
+
// memory/kpi/<uuid>/<kpi_id>.md → hivemind_kpis row
|
|
338
|
+
// See src/shell/deeplake-fs.ts for the translation logic and
|
|
339
|
+
// GOALS_COLUMNS / KPIS_COLUMNS in deeplake-schema.ts for the
|
|
340
|
+
// table shape.
|
|
341
|
+
goalsTableName: process.env.HIVEMIND_GOALS_TABLE ?? "hivemind_goals",
|
|
342
|
+
kpisTableName: process.env.HIVEMIND_KPIS_TABLE ?? "hivemind_kpis",
|
|
327
343
|
codebaseTableName: process.env.HIVEMIND_CODEBASE_TABLE ?? "codebase",
|
|
328
344
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join7(home, ".deeplake", "memory")
|
|
329
345
|
};
|
|
@@ -398,6 +414,65 @@ var SKILLS_COLUMNS = Object.freeze([
|
|
|
398
414
|
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
399
415
|
{ name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
400
416
|
]);
|
|
417
|
+
var RULES_COLUMNS = Object.freeze([
|
|
418
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
419
|
+
{ name: "rule_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
420
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
421
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'team'" },
|
|
422
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
423
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
424
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
425
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
426
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
427
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
428
|
+
]);
|
|
429
|
+
var TASKS_COLUMNS = Object.freeze([
|
|
430
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
431
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
432
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
433
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
|
|
434
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
435
|
+
{ name: "assigned_to", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
436
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
437
|
+
{ name: "kpis", sql: "JSONB" },
|
|
438
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
439
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
440
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
441
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
442
|
+
]);
|
|
443
|
+
var TASK_EVENTS_COLUMNS = Object.freeze([
|
|
444
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
445
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
446
|
+
{ name: "task_version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
447
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
448
|
+
{ name: "value", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
449
|
+
{ name: "note", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
450
|
+
{ name: "source", sql: "TEXT NOT NULL DEFAULT 'user'" },
|
|
451
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
452
|
+
{ name: "ts", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
453
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
454
|
+
]);
|
|
455
|
+
var GOALS_COLUMNS = Object.freeze([
|
|
456
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
457
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
458
|
+
{ name: "owner", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
459
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'opened'" },
|
|
460
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
461
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
462
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
463
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
464
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
465
|
+
]);
|
|
466
|
+
var KPIS_COLUMNS = Object.freeze([
|
|
467
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
468
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
469
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
470
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
471
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
472
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
473
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
474
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
475
|
+
]);
|
|
401
476
|
function validateSchema(label, cols) {
|
|
402
477
|
const seen = /* @__PURE__ */ new Set();
|
|
403
478
|
for (const col of cols) {
|
|
@@ -441,6 +516,11 @@ var CODEBASE_COLUMNS = Object.freeze([
|
|
|
441
516
|
validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
|
|
442
517
|
validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
|
|
443
518
|
validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
|
|
519
|
+
validateSchema("RULES_COLUMNS", RULES_COLUMNS);
|
|
520
|
+
validateSchema("TASKS_COLUMNS", TASKS_COLUMNS);
|
|
521
|
+
validateSchema("TASK_EVENTS_COLUMNS", TASK_EVENTS_COLUMNS);
|
|
522
|
+
validateSchema("GOALS_COLUMNS", GOALS_COLUMNS);
|
|
523
|
+
validateSchema("KPIS_COLUMNS", KPIS_COLUMNS);
|
|
444
524
|
validateSchema("CODEBASE_COLUMNS", CODEBASE_COLUMNS);
|
|
445
525
|
function buildCreateTableSql(tableName, cols) {
|
|
446
526
|
const safe = sqlIdent(tableName);
|
|
@@ -1019,6 +1099,111 @@ var DeeplakeApi = class {
|
|
|
1019
1099
|
await this.healSchema(safe, SKILLS_COLUMNS);
|
|
1020
1100
|
await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
|
|
1021
1101
|
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Create the rules table.
|
|
1104
|
+
*
|
|
1105
|
+
* One row per rule version (same write pattern as skills): edits INSERT
|
|
1106
|
+
* a fresh row with version+1, reads pick latest per rule_id via
|
|
1107
|
+
* `ORDER BY version DESC LIMIT 1`. Sidesteps the Deeplake
|
|
1108
|
+
* UPDATE-coalescing quirk by never UPDATEing.
|
|
1109
|
+
*/
|
|
1110
|
+
async ensureRulesTable(name) {
|
|
1111
|
+
const safe = sqlIdent(name);
|
|
1112
|
+
const tables = await this.listTables();
|
|
1113
|
+
if (!tables.includes(safe)) {
|
|
1114
|
+
log3(`table "${safe}" not found, creating`);
|
|
1115
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, RULES_COLUMNS), safe);
|
|
1116
|
+
log3(`table "${safe}" created`);
|
|
1117
|
+
if (!tables.includes(safe))
|
|
1118
|
+
this._tablesCache = [...tables, safe];
|
|
1119
|
+
}
|
|
1120
|
+
await this.healSchema(safe, RULES_COLUMNS);
|
|
1121
|
+
await this.ensureLookupIndex(safe, "rule_id_version", `("rule_id", "version")`);
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Create the tasks table.
|
|
1125
|
+
*
|
|
1126
|
+
* Same write pattern as rules + skills. `kpis` is a nullable JSONB
|
|
1127
|
+
* column with the agent's KPI metadata; KPI current values come from
|
|
1128
|
+
* `task_events` (SUM(value)), not this snapshot.
|
|
1129
|
+
*/
|
|
1130
|
+
async ensureTasksTable(name) {
|
|
1131
|
+
const safe = sqlIdent(name);
|
|
1132
|
+
const tables = await this.listTables();
|
|
1133
|
+
if (!tables.includes(safe)) {
|
|
1134
|
+
log3(`table "${safe}" not found, creating`);
|
|
1135
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASKS_COLUMNS), safe);
|
|
1136
|
+
log3(`table "${safe}" created`);
|
|
1137
|
+
if (!tables.includes(safe))
|
|
1138
|
+
this._tablesCache = [...tables, safe];
|
|
1139
|
+
}
|
|
1140
|
+
await this.healSchema(safe, TASKS_COLUMNS);
|
|
1141
|
+
await this.ensureLookupIndex(safe, "task_id_version", `("task_id", "version")`);
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Create the task-events table.
|
|
1145
|
+
*
|
|
1146
|
+
* Append-only. Every INSERT is a fresh row; never UPDATE. KPI current
|
|
1147
|
+
* value is `SUM(value) WHERE task_id=? AND kpi_id=?`. Index on
|
|
1148
|
+
* (task_id, kpi_id) is the canonical aggregation key.
|
|
1149
|
+
*/
|
|
1150
|
+
async ensureTaskEventsTable(name) {
|
|
1151
|
+
const safe = sqlIdent(name);
|
|
1152
|
+
const tables = await this.listTables();
|
|
1153
|
+
if (!tables.includes(safe)) {
|
|
1154
|
+
log3(`table "${safe}" not found, creating`);
|
|
1155
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASK_EVENTS_COLUMNS), safe);
|
|
1156
|
+
log3(`table "${safe}" created`);
|
|
1157
|
+
if (!tables.includes(safe))
|
|
1158
|
+
this._tablesCache = [...tables, safe];
|
|
1159
|
+
}
|
|
1160
|
+
await this.healSchema(safe, TASK_EVENTS_COLUMNS);
|
|
1161
|
+
await this.ensureLookupIndex(safe, "task_id_kpi_id", `("task_id", "kpi_id")`);
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Create the goals table.
|
|
1165
|
+
*
|
|
1166
|
+
* Backed by the VFS path convention memory/goal/<owner>/<status>/<goal_id>.md.
|
|
1167
|
+
* INSERT-only version-bumped: rm and mv operations translate to fresh
|
|
1168
|
+
* v=N+1 rows (status flips for mv → closed; rm is the same soft-close).
|
|
1169
|
+
* The (goal_id, version) index lets the VFS dispatch a cheap latest-row
|
|
1170
|
+
* read on cat / Read of a single goal.
|
|
1171
|
+
*/
|
|
1172
|
+
async ensureGoalsTable(name) {
|
|
1173
|
+
const safe = sqlIdent(name);
|
|
1174
|
+
const tables = await this.listTables();
|
|
1175
|
+
if (!tables.includes(safe)) {
|
|
1176
|
+
log3(`table "${safe}" not found, creating`);
|
|
1177
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, GOALS_COLUMNS), safe);
|
|
1178
|
+
log3(`table "${safe}" created`);
|
|
1179
|
+
if (!tables.includes(safe))
|
|
1180
|
+
this._tablesCache = [...tables, safe];
|
|
1181
|
+
}
|
|
1182
|
+
await this.healSchema(safe, GOALS_COLUMNS);
|
|
1183
|
+
await this.ensureLookupIndex(safe, "goal_id_version", `("goal_id", "version")`);
|
|
1184
|
+
await this.ensureLookupIndex(safe, "owner_status", `("owner", "status")`);
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Create the kpis table.
|
|
1188
|
+
*
|
|
1189
|
+
* Backed by memory/kpi/<goal_id>/<kpi_id>.md. KPI rows do NOT carry
|
|
1190
|
+
* owner — ownership derives from the parent goal via logical join on
|
|
1191
|
+
* goal_id. INSERT-only version-bumped. (goal_id, kpi_id) index is the
|
|
1192
|
+
* canonical lookup the VFS uses on Read and Write.
|
|
1193
|
+
*/
|
|
1194
|
+
async ensureKpisTable(name) {
|
|
1195
|
+
const safe = sqlIdent(name);
|
|
1196
|
+
const tables = await this.listTables();
|
|
1197
|
+
if (!tables.includes(safe)) {
|
|
1198
|
+
log3(`table "${safe}" not found, creating`);
|
|
1199
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, KPIS_COLUMNS), safe);
|
|
1200
|
+
log3(`table "${safe}" created`);
|
|
1201
|
+
if (!tables.includes(safe))
|
|
1202
|
+
this._tablesCache = [...tables, safe];
|
|
1203
|
+
}
|
|
1204
|
+
await this.healSchema(safe, KPIS_COLUMNS);
|
|
1205
|
+
await this.ensureLookupIndex(safe, "goal_id_kpi_id", `("goal_id", "kpi_id")`);
|
|
1206
|
+
}
|
|
1022
1207
|
};
|
|
1023
1208
|
|
|
1024
1209
|
// dist/src/skillify/pull.js
|