chapterhouse 0.9.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api/auth.js +11 -1
- package/dist/api/auth.test.js +29 -0
- package/dist/api/errors.js +23 -0
- package/dist/api/route-coverage.test.js +61 -21
- package/dist/api/routes/agents.js +472 -0
- package/dist/api/routes/memory.js +299 -0
- package/dist/api/routes/projects.js +170 -0
- package/dist/api/routes/sessions.js +347 -0
- package/dist/api/routes/system.js +82 -0
- package/dist/api/routes/wiki.js +455 -0
- package/dist/api/routes/wiki.test.js +49 -0
- package/dist/api/send-json.js +16 -0
- package/dist/api/send-json.test.js +18 -0
- package/dist/api/server-runtime.js +45 -3
- package/dist/api/server.js +34 -1764
- package/dist/api/server.test.js +239 -8
- package/dist/api/sse-hub.js +37 -0
- package/dist/cli.js +1 -1
- package/dist/config.js +151 -58
- package/dist/config.test.js +29 -0
- package/dist/copilot/okr-mapper.js +2 -11
- package/dist/copilot/orchestrator.js +358 -352
- package/dist/copilot/orchestrator.test.js +139 -4
- package/dist/copilot/prompt-date.js +2 -1
- package/dist/copilot/session-manager.js +25 -23
- package/dist/copilot/session-manager.test.js +35 -1
- package/dist/copilot/standup.js +2 -2
- package/dist/copilot/task-event-log.js +7 -1
- package/dist/copilot/task-event-log.test.js +13 -0
- package/dist/copilot/tools/agent.js +608 -0
- package/dist/copilot/tools/index.js +19 -0
- package/dist/copilot/tools/memory.js +678 -0
- package/dist/copilot/tools/models.js +2 -0
- package/dist/copilot/tools/okr.js +171 -0
- package/dist/copilot/tools/wiki.js +333 -0
- package/dist/copilot/tools-deps.js +4 -0
- package/dist/copilot/tools.agent.test.js +10 -8
- package/dist/copilot/tools.inventory.test.js +76 -0
- package/dist/copilot/tools.js +1 -1780
- package/dist/copilot/tools.okr.test.js +31 -0
- package/dist/copilot/tools.wiki.test.js +6 -3
- package/dist/copilot/turn-event-log.js +31 -4
- package/dist/copilot/turn-event-log.test.js +24 -2
- package/dist/copilot/workiq-installer.test.js +2 -2
- package/dist/daemon-install.js +3 -2
- package/dist/daemon.js +9 -17
- package/dist/integrations/ado-client.js +90 -9
- package/dist/integrations/ado-client.test.js +56 -0
- package/dist/integrations/team-push.js +1 -0
- package/dist/integrations/team-push.test.js +6 -0
- package/dist/integrations/teams-notify.js +1 -0
- package/dist/integrations/teams-notify.test.js +5 -0
- package/dist/memory/active-scope.test.js +0 -1
- package/dist/memory/checkpoint.js +89 -72
- package/dist/memory/checkpoint.test.js +23 -3
- package/dist/memory/eot.js +87 -85
- package/dist/memory/eot.test.js +71 -3
- package/dist/memory/hooks.js +2 -4
- package/dist/memory/housekeeping-scheduler.js +1 -1
- package/dist/memory/housekeeping-scheduler.test.js +1 -2
- package/dist/memory/housekeeping.js +100 -3
- package/dist/memory/housekeeping.test.js +33 -2
- package/dist/memory/reflect.test.js +2 -0
- package/dist/memory/scope-lock.js +26 -0
- package/dist/memory/scope-lock.test.js +118 -0
- package/dist/memory/scopes.test.js +0 -1
- package/dist/mode-context.js +58 -5
- package/dist/mode-context.test.js +68 -0
- package/dist/paths.js +1 -0
- package/dist/setup.js +3 -2
- package/dist/shared/api-schemas.js +48 -5
- package/dist/store/connection.js +96 -0
- package/dist/store/db.js +5 -1498
- package/dist/store/db.test.js +182 -1
- package/dist/store/migrations.js +460 -0
- package/dist/store/repositories/memory.js +281 -0
- package/dist/store/repositories/okr.js +3 -0
- package/dist/store/repositories/projects.js +5 -0
- package/dist/store/repositories/sessions.js +284 -0
- package/dist/store/repositories/wiki.js +60 -0
- package/dist/store/schema.js +501 -0
- package/dist/util/logger.js +3 -2
- package/dist/wiki/consolidation.js +50 -9
- package/dist/wiki/consolidation.test.js +45 -0
- package/dist/wiki/frontmatter.js +43 -13
- package/dist/wiki/frontmatter.test.js +24 -0
- package/dist/wiki/fs.js +16 -4
- package/dist/wiki/fs.test.js +84 -0
- package/dist/wiki/index-manager.js +30 -2
- package/dist/wiki/index-manager.test.js +43 -12
- package/dist/wiki/ingest.js +1 -1
- package/dist/wiki/lock.js +11 -1
- package/dist/wiki/log-manager.js +2 -7
- package/dist/wiki/migrate.js +44 -17
- package/dist/wiki/project-registry.js +10 -5
- package/dist/wiki/project-registry.test.js +14 -0
- package/dist/wiki/scheduler.js +1 -1
- package/dist/wiki/seed-team-wiki.js +2 -1
- package/dist/wiki/team-sync.js +31 -6
- package/dist/wiki/team-sync.test.js +81 -0
- package/package.json +1 -1
- package/web/dist/assets/WikiEdit-BZXAdarz.js +30 -0
- package/web/dist/assets/WikiEdit-BZXAdarz.js.map +1 -0
- package/web/dist/assets/WikiGraph-KrCYco4v.js +2 -0
- package/web/dist/assets/WikiGraph-KrCYco4v.js.map +1 -0
- package/web/dist/assets/index-CUm2Wbuh.js +250 -0
- package/web/dist/assets/index-CUm2Wbuh.js.map +1 -0
- package/web/dist/index.html +1 -1
- package/web/dist/assets/index-iQrv3lQN.js +0 -286
- package/web/dist/assets/index-iQrv3lQN.js.map +0 -1
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
function hasColumn(database, table, column) {
|
|
2
|
+
return database.prepare(`PRAGMA table_info(${table})`).all().some((entry) => entry.name === column);
|
|
3
|
+
}
|
|
4
|
+
function memoryTierCase(tableAlias = "") {
|
|
5
|
+
const prefix = tableAlias ? `${tableAlias}.` : "";
|
|
6
|
+
return `
|
|
7
|
+
CASE
|
|
8
|
+
WHEN ${prefix}archived_at IS NOT NULL OR ${prefix}superseded_by IS NOT NULL THEN 'cold'
|
|
9
|
+
WHEN ${prefix}tier = 'glacier' THEN 'cold'
|
|
10
|
+
WHEN ${prefix}tier IN ('hot', 'warm', 'cold') THEN ${prefix}tier
|
|
11
|
+
ELSE 'warm'
|
|
12
|
+
END
|
|
13
|
+
`;
|
|
14
|
+
}
|
|
15
|
+
function entityTierCase(tableAlias = "") {
|
|
16
|
+
const prefix = tableAlias ? `${tableAlias}.` : "";
|
|
17
|
+
return `
|
|
18
|
+
CASE
|
|
19
|
+
WHEN ${prefix}tier = 'glacier' THEN 'cold'
|
|
20
|
+
WHEN ${prefix}tier IN ('hot', 'warm', 'cold') THEN ${prefix}tier
|
|
21
|
+
WHEN ${prefix}confidence > 0.7 AND datetime(${prefix}updated_at) >= datetime('now', '-30 days') THEN 'hot'
|
|
22
|
+
ELSE 'warm'
|
|
23
|
+
END
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
26
|
+
function tableCreateSql(database, table) {
|
|
27
|
+
const row = database.prepare(`
|
|
28
|
+
SELECT sql
|
|
29
|
+
FROM sqlite_master
|
|
30
|
+
WHERE type = 'table' AND name = ?
|
|
31
|
+
`).get(table);
|
|
32
|
+
return row?.sql ?? "";
|
|
33
|
+
}
|
|
34
|
+
function migrateMemActionItemsCreatedAtType(database) {
|
|
35
|
+
const columns = database.prepare(`PRAGMA table_info(mem_action_items)`).all();
|
|
36
|
+
const createdAt = columns.find((column) => column.name === "created_at");
|
|
37
|
+
if (!createdAt || createdAt.type.toUpperCase() === "DATETIME") {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
database.pragma("foreign_keys = OFF");
|
|
41
|
+
try {
|
|
42
|
+
database.transaction(() => {
|
|
43
|
+
database.exec(`DROP TRIGGER IF EXISTS mem_action_items_ai`);
|
|
44
|
+
database.exec(`DROP TRIGGER IF EXISTS mem_action_items_ad`);
|
|
45
|
+
database.exec(`DROP TRIGGER IF EXISTS mem_action_items_au`);
|
|
46
|
+
database.exec(`
|
|
47
|
+
CREATE TABLE mem_action_items_new (
|
|
48
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
scope_id INTEGER NOT NULL REFERENCES mem_scopes(id),
|
|
50
|
+
entity_id INTEGER REFERENCES mem_entities(id),
|
|
51
|
+
title TEXT NOT NULL,
|
|
52
|
+
detail TEXT,
|
|
53
|
+
status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'done', 'dropped', 'snoozed')),
|
|
54
|
+
due_at TEXT,
|
|
55
|
+
snooze_until TEXT,
|
|
56
|
+
source TEXT,
|
|
57
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
58
|
+
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
59
|
+
resolved_at TEXT,
|
|
60
|
+
resolution_reason TEXT,
|
|
61
|
+
tier TEXT NOT NULL DEFAULT 'warm' CHECK(tier IN ('hot', 'warm', 'cold')),
|
|
62
|
+
tier_pinned_at TEXT,
|
|
63
|
+
tier_reason TEXT,
|
|
64
|
+
last_recalled_at TEXT
|
|
65
|
+
)
|
|
66
|
+
`);
|
|
67
|
+
database.exec(`
|
|
68
|
+
INSERT INTO mem_action_items_new (
|
|
69
|
+
id, scope_id, entity_id, title, detail, status, due_at, snooze_until, source,
|
|
70
|
+
created_at, updated_at, resolved_at, resolution_reason, tier, tier_pinned_at, tier_reason, last_recalled_at
|
|
71
|
+
)
|
|
72
|
+
SELECT
|
|
73
|
+
id, scope_id, entity_id, title, detail, status, due_at, snooze_until, source,
|
|
74
|
+
datetime(created_at), updated_at, resolved_at, resolution_reason, tier, tier_pinned_at, tier_reason, last_recalled_at
|
|
75
|
+
FROM mem_action_items
|
|
76
|
+
`);
|
|
77
|
+
database.exec(`DROP TABLE mem_action_items`);
|
|
78
|
+
database.exec(`ALTER TABLE mem_action_items_new RENAME TO mem_action_items`);
|
|
79
|
+
})();
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
database.pragma("foreign_keys = ON");
|
|
83
|
+
}
|
|
84
|
+
ensureMemoryIndexes(database);
|
|
85
|
+
}
|
|
86
|
+
function rebuildMemoryTierTables(database) {
|
|
87
|
+
const needsRebuild = ["mem_entities", "mem_observations", "mem_decisions"]
|
|
88
|
+
.some((table) => tableCreateSql(database, table).includes("'glacier'"));
|
|
89
|
+
if (!needsRebuild) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
database.pragma("foreign_keys = OFF");
|
|
93
|
+
try {
|
|
94
|
+
database.transaction(() => {
|
|
95
|
+
database.exec(`ALTER TABLE mem_entities RENAME TO mem_entities_legacy_tier`);
|
|
96
|
+
database.exec(`
|
|
97
|
+
CREATE TABLE mem_entities (
|
|
98
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
99
|
+
scope_id INTEGER NOT NULL REFERENCES mem_scopes(id),
|
|
100
|
+
slug TEXT,
|
|
101
|
+
kind TEXT NOT NULL,
|
|
102
|
+
name TEXT NOT NULL,
|
|
103
|
+
summary TEXT,
|
|
104
|
+
tier TEXT NOT NULL DEFAULT 'warm' CHECK(tier IN ('hot', 'warm', 'cold')),
|
|
105
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
106
|
+
tier_pinned_at DATETIME,
|
|
107
|
+
tier_reason TEXT,
|
|
108
|
+
last_recalled_at DATETIME,
|
|
109
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
110
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
111
|
+
)
|
|
112
|
+
`);
|
|
113
|
+
database.exec(`
|
|
114
|
+
INSERT INTO mem_entities (id, scope_id, slug, kind, name, summary, tier, confidence, created_at, updated_at)
|
|
115
|
+
SELECT id, scope_id, NULL, kind, name, summary, ${entityTierCase()}, confidence, created_at, updated_at
|
|
116
|
+
FROM mem_entities_legacy_tier
|
|
117
|
+
`);
|
|
118
|
+
database.exec(`DROP TABLE mem_entities_legacy_tier`);
|
|
119
|
+
database.exec(`ALTER TABLE mem_observations RENAME TO mem_observations_legacy_tier`);
|
|
120
|
+
database.exec(`
|
|
121
|
+
CREATE TABLE mem_observations (
|
|
122
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
123
|
+
scope_id INTEGER NOT NULL REFERENCES mem_scopes(id),
|
|
124
|
+
entity_id INTEGER REFERENCES mem_entities(id),
|
|
125
|
+
content TEXT NOT NULL,
|
|
126
|
+
source TEXT NOT NULL,
|
|
127
|
+
tier TEXT NOT NULL DEFAULT 'warm' CHECK(tier IN ('hot', 'warm', 'cold')),
|
|
128
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
129
|
+
embedding BLOB,
|
|
130
|
+
superseded_by INTEGER REFERENCES mem_observations(id) ON DELETE SET NULL,
|
|
131
|
+
archived_at DATETIME,
|
|
132
|
+
tier_pinned_at DATETIME,
|
|
133
|
+
tier_reason TEXT,
|
|
134
|
+
last_recalled_at DATETIME,
|
|
135
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
136
|
+
)
|
|
137
|
+
`);
|
|
138
|
+
database.exec(`
|
|
139
|
+
INSERT INTO mem_observations (
|
|
140
|
+
id, scope_id, entity_id, content, source, tier, confidence, embedding, superseded_by, archived_at, created_at
|
|
141
|
+
)
|
|
142
|
+
SELECT id, scope_id, entity_id, content, source, ${memoryTierCase()}, confidence, embedding, superseded_by, archived_at, created_at
|
|
143
|
+
FROM mem_observations_legacy_tier
|
|
144
|
+
`);
|
|
145
|
+
database.exec(`DROP TABLE mem_observations_legacy_tier`);
|
|
146
|
+
database.exec(`ALTER TABLE mem_decisions RENAME TO mem_decisions_legacy_tier`);
|
|
147
|
+
database.exec(`
|
|
148
|
+
CREATE TABLE mem_decisions (
|
|
149
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
150
|
+
scope_id INTEGER NOT NULL REFERENCES mem_scopes(id),
|
|
151
|
+
entity_id INTEGER REFERENCES mem_entities(id),
|
|
152
|
+
title TEXT NOT NULL,
|
|
153
|
+
rationale TEXT NOT NULL,
|
|
154
|
+
decided_at TEXT NOT NULL,
|
|
155
|
+
source TEXT,
|
|
156
|
+
tier TEXT NOT NULL DEFAULT 'warm' CHECK(tier IN ('hot', 'warm', 'cold')),
|
|
157
|
+
superseded_by INTEGER REFERENCES mem_decisions(id) ON DELETE SET NULL,
|
|
158
|
+
archived_at DATETIME,
|
|
159
|
+
tier_pinned_at DATETIME,
|
|
160
|
+
tier_reason TEXT,
|
|
161
|
+
last_recalled_at DATETIME,
|
|
162
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
163
|
+
)
|
|
164
|
+
`);
|
|
165
|
+
database.exec(`
|
|
166
|
+
INSERT INTO mem_decisions (
|
|
167
|
+
id, scope_id, entity_id, title, rationale, decided_at, source, tier, superseded_by, archived_at, created_at
|
|
168
|
+
)
|
|
169
|
+
SELECT id, scope_id, entity_id, title, rationale, decided_at, NULL, ${memoryTierCase()}, superseded_by, archived_at, created_at
|
|
170
|
+
FROM mem_decisions_legacy_tier
|
|
171
|
+
`);
|
|
172
|
+
database.exec(`DROP TABLE mem_decisions_legacy_tier`);
|
|
173
|
+
})();
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
database.pragma("foreign_keys = ON");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function ensureMemoryTierColumns(database) {
|
|
180
|
+
for (const table of ["mem_entities", "mem_observations", "mem_decisions", "mem_action_items"]) {
|
|
181
|
+
if (!hasColumn(database, table, "tier")) {
|
|
182
|
+
database.exec(`ALTER TABLE ${table} ADD COLUMN tier TEXT DEFAULT 'warm'`);
|
|
183
|
+
}
|
|
184
|
+
if (!hasColumn(database, table, "tier_pinned_at")) {
|
|
185
|
+
database.exec(`ALTER TABLE ${table} ADD COLUMN tier_pinned_at DATETIME`);
|
|
186
|
+
}
|
|
187
|
+
if (!hasColumn(database, table, "tier_reason")) {
|
|
188
|
+
database.exec(`ALTER TABLE ${table} ADD COLUMN tier_reason TEXT`);
|
|
189
|
+
}
|
|
190
|
+
if (!hasColumn(database, table, "last_recalled_at")) {
|
|
191
|
+
database.exec(`ALTER TABLE ${table} ADD COLUMN last_recalled_at DATETIME`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
database.exec(`
|
|
195
|
+
UPDATE mem_entities
|
|
196
|
+
SET tier = CASE
|
|
197
|
+
WHEN tier = 'glacier' THEN 'cold'
|
|
198
|
+
WHEN tier IN ('hot', 'warm', 'cold') THEN tier
|
|
199
|
+
WHEN confidence > 0.7 AND datetime(updated_at) >= datetime('now', '-30 days') THEN 'hot'
|
|
200
|
+
ELSE 'warm'
|
|
201
|
+
END
|
|
202
|
+
`);
|
|
203
|
+
database.exec(`
|
|
204
|
+
UPDATE mem_observations
|
|
205
|
+
SET tier = CASE
|
|
206
|
+
WHEN archived_at IS NOT NULL OR superseded_by IS NOT NULL THEN 'cold'
|
|
207
|
+
WHEN tier = 'glacier' THEN 'cold'
|
|
208
|
+
WHEN tier IN ('hot', 'warm', 'cold') THEN tier
|
|
209
|
+
WHEN confidence > 0.7 AND datetime(created_at) >= datetime('now', '-30 days') THEN 'hot'
|
|
210
|
+
ELSE 'warm'
|
|
211
|
+
END
|
|
212
|
+
`);
|
|
213
|
+
database.exec(`
|
|
214
|
+
UPDATE mem_decisions
|
|
215
|
+
SET tier = CASE
|
|
216
|
+
WHEN archived_at IS NOT NULL OR superseded_by IS NOT NULL THEN 'cold'
|
|
217
|
+
WHEN tier = 'glacier' THEN 'cold'
|
|
218
|
+
WHEN tier IN ('hot', 'warm', 'cold') THEN tier
|
|
219
|
+
WHEN datetime(COALESCE(decided_at, created_at)) >= datetime('now', '-30 days') THEN 'hot'
|
|
220
|
+
ELSE 'warm'
|
|
221
|
+
END
|
|
222
|
+
`);
|
|
223
|
+
database.exec(`
|
|
224
|
+
UPDATE mem_action_items
|
|
225
|
+
SET tier = CASE
|
|
226
|
+
WHEN status IN ('done', 'dropped') THEN 'cold'
|
|
227
|
+
WHEN tier = 'glacier' THEN 'cold'
|
|
228
|
+
WHEN tier IN ('hot', 'warm', 'cold') THEN tier
|
|
229
|
+
WHEN status = 'open' AND due_at IS NOT NULL AND datetime(due_at) <= datetime('now', '+7 days') THEN 'hot'
|
|
230
|
+
ELSE 'warm'
|
|
231
|
+
END
|
|
232
|
+
`);
|
|
233
|
+
}
|
|
234
|
+
function ensureMemoryIndexes(database) {
|
|
235
|
+
database.exec(`CREATE INDEX IF NOT EXISTS mem_entities_scope_kind_idx ON mem_entities(scope_id, kind)`);
|
|
236
|
+
database.exec(`CREATE UNIQUE INDEX IF NOT EXISTS mem_entities_scope_kind_name_idx ON mem_entities(scope_id, kind, name)`);
|
|
237
|
+
database.exec(`CREATE UNIQUE INDEX IF NOT EXISTS mem_entities_scope_slug_idx ON mem_entities(scope_id, slug) WHERE slug IS NOT NULL`);
|
|
238
|
+
database.exec(`CREATE INDEX IF NOT EXISTS mem_observations_scope_idx ON mem_observations(scope_id)`);
|
|
239
|
+
database.exec(`CREATE INDEX IF NOT EXISTS mem_observations_scope_superseded_by_idx ON mem_observations(scope_id, superseded_by)`);
|
|
240
|
+
database.exec(`CREATE INDEX IF NOT EXISTS mem_decisions_scope_idx ON mem_decisions(scope_id)`);
|
|
241
|
+
database.exec(`CREATE INDEX IF NOT EXISTS mem_decisions_scope_superseded_by_idx ON mem_decisions(scope_id, superseded_by)`);
|
|
242
|
+
database.exec(`CREATE INDEX IF NOT EXISTS idx_mem_action_items_scope_status ON mem_action_items(scope_id, status)`);
|
|
243
|
+
database.exec(`CREATE INDEX IF NOT EXISTS idx_mem_action_items_due ON mem_action_items(status, due_at)`);
|
|
244
|
+
}
|
|
245
|
+
export const DB_MIGRATIONS = [
|
|
246
|
+
{ version: 1, name: "conversation-log-agent-completion-role", up: migrateConversationLogAgentCompletionRole },
|
|
247
|
+
{ version: 2, name: "copilot-sessions-agent-mode", up: migrateCopilotSessionsAgentMode },
|
|
248
|
+
{ version: 3, name: "conversation-log-metadata-columns", up: migrateConversationLogMetadataColumns },
|
|
249
|
+
{ version: 4, name: "agent-tasks-session-source-prompt", up: migrateAgentTaskColumns },
|
|
250
|
+
{ version: 5, name: "agent-task-events-text-status", up: migrateAgentTaskEventsTextStatus },
|
|
251
|
+
{ version: 6, name: "agent-tasks-event-seq", up: migrateAgentTasksEventSeq },
|
|
252
|
+
{ version: 7, name: "memory-entity-slug", up: migrateMemoryEntitySlug },
|
|
253
|
+
{ version: 8, name: "wiki-sources-status-session", up: migrateWikiSourceColumns },
|
|
254
|
+
{ version: 9, name: "memory-decisions-source-archive", up: migrateMemoryDecisionColumns },
|
|
255
|
+
{ version: 10, name: "memory-tier-and-action-item-schema", up: migrateMemoryTierAndActionItems },
|
|
256
|
+
];
|
|
257
|
+
function ensureMigrationsTable(database) {
|
|
258
|
+
database.exec(`
|
|
259
|
+
CREATE TABLE IF NOT EXISTS __db_migrations (
|
|
260
|
+
version INTEGER PRIMARY KEY,
|
|
261
|
+
name TEXT NOT NULL UNIQUE,
|
|
262
|
+
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
263
|
+
)
|
|
264
|
+
`);
|
|
265
|
+
}
|
|
266
|
+
function hasMigration(database, migration) {
|
|
267
|
+
const row = database.prepare(`SELECT version FROM __db_migrations WHERE version = ?`).get(migration.version);
|
|
268
|
+
return row !== undefined;
|
|
269
|
+
}
|
|
270
|
+
function recordMigration(database, migration) {
|
|
271
|
+
database.prepare(`
|
|
272
|
+
INSERT OR IGNORE INTO __db_migrations (version, name)
|
|
273
|
+
VALUES (?, ?)
|
|
274
|
+
`).run(migration.version, migration.name);
|
|
275
|
+
}
|
|
276
|
+
export function applyMigrations(database, throughVersion = Number.POSITIVE_INFINITY) {
|
|
277
|
+
ensureMigrationsTable(database);
|
|
278
|
+
for (const migration of DB_MIGRATIONS) {
|
|
279
|
+
if (migration.version > throughVersion) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (hasMigration(database, migration)) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
migration.up(database);
|
|
286
|
+
recordMigration(database, migration);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function migrateConversationLogAgentCompletionRole(database) {
|
|
290
|
+
try {
|
|
291
|
+
database.prepare(`INSERT INTO conversation_log (role, content, source) VALUES ('agent_completion', '__migration_test__', 'test')`).run();
|
|
292
|
+
database.prepare(`DELETE FROM conversation_log WHERE content = '__migration_test__'`).run();
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
database.exec(`ALTER TABLE conversation_log RENAME TO conversation_log_old`);
|
|
296
|
+
const oldConvCols = database.prepare(`PRAGMA table_info(conversation_log_old)`).all();
|
|
297
|
+
const oldConvColNames = new Set(oldConvCols.map((column) => column.name));
|
|
298
|
+
const sessionKeySelect = oldConvColNames.has("session_key") ? "session_key" : "'default'";
|
|
299
|
+
const turnIdSelect = oldConvColNames.has("turn_id") ? "turn_id" : "NULL";
|
|
300
|
+
const agentSlugSelect = oldConvColNames.has("agent_slug") ? "agent_slug" : "NULL";
|
|
301
|
+
const agentDisplayNameSelect = oldConvColNames.has("agent_display_name") ? "agent_display_name" : "NULL";
|
|
302
|
+
const runIdSelect = oldConvColNames.has("run_id") ? "run_id" : "NULL";
|
|
303
|
+
database.exec(`
|
|
304
|
+
CREATE TABLE conversation_log (
|
|
305
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
306
|
+
role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system', 'agent_completion')),
|
|
307
|
+
content TEXT NOT NULL,
|
|
308
|
+
source TEXT NOT NULL DEFAULT 'unknown',
|
|
309
|
+
session_key TEXT NOT NULL DEFAULT 'default',
|
|
310
|
+
turn_id TEXT,
|
|
311
|
+
agent_slug TEXT,
|
|
312
|
+
agent_display_name TEXT,
|
|
313
|
+
run_id TEXT,
|
|
314
|
+
ts DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
315
|
+
)
|
|
316
|
+
`);
|
|
317
|
+
database.exec(`
|
|
318
|
+
INSERT INTO conversation_log (role, content, source, session_key, turn_id, agent_slug, agent_display_name, run_id, ts)
|
|
319
|
+
SELECT role, content, source, ${sessionKeySelect}, ${turnIdSelect}, ${agentSlugSelect}, ${agentDisplayNameSelect}, ${runIdSelect}, ts FROM conversation_log_old
|
|
320
|
+
`);
|
|
321
|
+
database.exec(`DROP TABLE conversation_log_old`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function migrateCopilotSessionsAgentMode(database) {
|
|
325
|
+
try {
|
|
326
|
+
database.prepare(`
|
|
327
|
+
INSERT INTO copilot_sessions (session_key, mode, copilot_session_id)
|
|
328
|
+
VALUES ('__mode_probe__', 'agent', '__probe__')
|
|
329
|
+
`).run();
|
|
330
|
+
database.prepare(`DELETE FROM copilot_sessions WHERE session_key = '__mode_probe__'`).run();
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
database.exec(`ALTER TABLE copilot_sessions RENAME TO copilot_sessions_old`);
|
|
334
|
+
database.exec(`
|
|
335
|
+
CREATE TABLE copilot_sessions (
|
|
336
|
+
session_key TEXT PRIMARY KEY,
|
|
337
|
+
mode TEXT NOT NULL CHECK(mode IN ('default', 'project', 'agent')),
|
|
338
|
+
project_root TEXT,
|
|
339
|
+
copilot_session_id TEXT NOT NULL,
|
|
340
|
+
model TEXT,
|
|
341
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
342
|
+
)
|
|
343
|
+
`);
|
|
344
|
+
database.exec(`
|
|
345
|
+
INSERT INTO copilot_sessions (session_key, mode, project_root, copilot_session_id, model, updated_at)
|
|
346
|
+
SELECT session_key, mode, project_root, copilot_session_id, model, updated_at
|
|
347
|
+
FROM copilot_sessions_old
|
|
348
|
+
`);
|
|
349
|
+
database.exec(`DROP TABLE copilot_sessions_old`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function migrateConversationLogMetadataColumns(database) {
|
|
353
|
+
const convCols = database.prepare(`PRAGMA table_info(conversation_log)`).all();
|
|
354
|
+
if (!convCols.some((c) => c.name === 'session_key')) {
|
|
355
|
+
database.exec(`ALTER TABLE conversation_log ADD COLUMN session_key TEXT NOT NULL DEFAULT 'default'`);
|
|
356
|
+
}
|
|
357
|
+
if (!convCols.some((c) => c.name === 'turn_id')) {
|
|
358
|
+
database.exec(`ALTER TABLE conversation_log ADD COLUMN turn_id TEXT`);
|
|
359
|
+
}
|
|
360
|
+
if (!convCols.some((c) => c.name === 'agent_slug')) {
|
|
361
|
+
database.exec(`ALTER TABLE conversation_log ADD COLUMN agent_slug TEXT`);
|
|
362
|
+
}
|
|
363
|
+
if (!convCols.some((c) => c.name === 'agent_display_name')) {
|
|
364
|
+
database.exec(`ALTER TABLE conversation_log ADD COLUMN agent_display_name TEXT`);
|
|
365
|
+
}
|
|
366
|
+
if (!convCols.some((c) => c.name === "run_id")) {
|
|
367
|
+
database.exec(`ALTER TABLE conversation_log ADD COLUMN run_id TEXT`);
|
|
368
|
+
}
|
|
369
|
+
database.exec(`CREATE INDEX IF NOT EXISTS idx_conversation_log_session_run ON conversation_log(session_key, run_id, id)`);
|
|
370
|
+
}
|
|
371
|
+
function migrateAgentTaskColumns(database) {
|
|
372
|
+
const taskCols = database.prepare(`PRAGMA table_info(agent_tasks)`).all();
|
|
373
|
+
if (!taskCols.some((c) => c.name === 'session_key')) {
|
|
374
|
+
database.exec(`ALTER TABLE agent_tasks ADD COLUMN session_key TEXT NOT NULL DEFAULT 'default'`);
|
|
375
|
+
}
|
|
376
|
+
if (!taskCols.some((c) => c.name === 'source')) {
|
|
377
|
+
database.exec(`ALTER TABLE agent_tasks ADD COLUMN source TEXT NOT NULL DEFAULT 'adhoc'`);
|
|
378
|
+
}
|
|
379
|
+
if (!taskCols.some((c) => c.name === "prompt")) {
|
|
380
|
+
database.exec(`ALTER TABLE agent_tasks ADD COLUMN prompt TEXT`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function migrateAgentTaskEventsTextStatus(database) {
|
|
384
|
+
const taskEventCols = database.prepare(`PRAGMA table_info(agent_task_events)`).all();
|
|
385
|
+
if (!taskEventCols.some((c) => c.name === "text") || !taskEventCols.some((c) => c.name === "status")) {
|
|
386
|
+
database.exec(`ALTER TABLE agent_task_events RENAME TO agent_task_events_old`);
|
|
387
|
+
database.exec(`
|
|
388
|
+
CREATE TABLE agent_task_events (
|
|
389
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
390
|
+
task_id TEXT NOT NULL REFERENCES agent_tasks(task_id) ON DELETE CASCADE,
|
|
391
|
+
seq INTEGER NOT NULL,
|
|
392
|
+
ts INTEGER NOT NULL,
|
|
393
|
+
kind TEXT NOT NULL CHECK(kind IN ('tool_start', 'tool_complete', 'output_delta', 'task_status')),
|
|
394
|
+
tool_name TEXT,
|
|
395
|
+
summary TEXT,
|
|
396
|
+
text TEXT,
|
|
397
|
+
status TEXT
|
|
398
|
+
)
|
|
399
|
+
`);
|
|
400
|
+
database.exec(`
|
|
401
|
+
INSERT INTO agent_task_events (id, task_id, seq, ts, kind, tool_name, summary)
|
|
402
|
+
SELECT id, task_id, seq, ts, kind, tool_name, summary FROM agent_task_events_old
|
|
403
|
+
`);
|
|
404
|
+
database.exec(`DROP TABLE agent_task_events_old`);
|
|
405
|
+
database.exec(`CREATE INDEX IF NOT EXISTS idx_agent_task_events_task_id ON agent_task_events(task_id, seq)`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function migrateAgentTasksEventSeq(database) {
|
|
409
|
+
const taskColsNow = database.prepare(`PRAGMA table_info(agent_tasks)`).all();
|
|
410
|
+
if (!taskColsNow.some((c) => c.name === 'event_seq')) {
|
|
411
|
+
database.exec(`ALTER TABLE agent_tasks ADD COLUMN event_seq INTEGER NOT NULL DEFAULT 0`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function migrateMemoryEntitySlug(database) {
|
|
415
|
+
const entityCols = database.prepare(`PRAGMA table_info(mem_entities)`).all();
|
|
416
|
+
if (!entityCols.some((column) => column.name === "slug")) {
|
|
417
|
+
database.exec(`ALTER TABLE mem_entities ADD COLUMN slug TEXT`);
|
|
418
|
+
}
|
|
419
|
+
database.exec(`
|
|
420
|
+
DELETE FROM mem_entities
|
|
421
|
+
WHERE id NOT IN (
|
|
422
|
+
SELECT MIN(id)
|
|
423
|
+
FROM mem_entities
|
|
424
|
+
GROUP BY scope_id, kind, name
|
|
425
|
+
)
|
|
426
|
+
`);
|
|
427
|
+
database.exec(`CREATE UNIQUE INDEX IF NOT EXISTS mem_entities_scope_kind_name_idx ON mem_entities(scope_id, kind, name)`);
|
|
428
|
+
database.exec(`CREATE UNIQUE INDEX IF NOT EXISTS mem_entities_scope_slug_idx ON mem_entities(scope_id, slug) WHERE slug IS NOT NULL`);
|
|
429
|
+
}
|
|
430
|
+
function migrateWikiSourceColumns(database) {
|
|
431
|
+
const wikiSourceCols = database.prepare(`PRAGMA table_info(wiki_sources)`).all();
|
|
432
|
+
if (!wikiSourceCols.some((column) => column.name === "status")) {
|
|
433
|
+
database.exec(`ALTER TABLE wiki_sources ADD COLUMN status TEXT NOT NULL DEFAULT 'active'`);
|
|
434
|
+
}
|
|
435
|
+
if (!wikiSourceCols.some((column) => column.name === "session_id")) {
|
|
436
|
+
database.exec(`ALTER TABLE wiki_sources ADD COLUMN session_id TEXT`);
|
|
437
|
+
}
|
|
438
|
+
if (!wikiSourceCols.some((column) => column.name === "session_name")) {
|
|
439
|
+
database.exec(`ALTER TABLE wiki_sources ADD COLUMN session_name TEXT`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function migrateMemoryDecisionColumns(database) {
|
|
443
|
+
const decisionCols = database.prepare(`PRAGMA table_info(mem_decisions)`).all();
|
|
444
|
+
if (!decisionCols.some((column) => column.name === "superseded_by")) {
|
|
445
|
+
database.exec(`ALTER TABLE mem_decisions ADD COLUMN superseded_by INTEGER REFERENCES mem_decisions(id) ON DELETE SET NULL`);
|
|
446
|
+
}
|
|
447
|
+
if (!decisionCols.some((column) => column.name === "archived_at")) {
|
|
448
|
+
database.exec(`ALTER TABLE mem_decisions ADD COLUMN archived_at DATETIME`);
|
|
449
|
+
}
|
|
450
|
+
if (!decisionCols.some((column) => column.name === "source")) {
|
|
451
|
+
database.exec(`ALTER TABLE mem_decisions ADD COLUMN source TEXT`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function migrateMemoryTierAndActionItems(database) {
|
|
455
|
+
rebuildMemoryTierTables(database);
|
|
456
|
+
ensureMemoryTierColumns(database);
|
|
457
|
+
migrateMemActionItemsCreatedAtType(database);
|
|
458
|
+
ensureMemoryIndexes(database);
|
|
459
|
+
}
|
|
460
|
+
//# sourceMappingURL=migrations.js.map
|