chapterhouse 0.9.2 → 0.11.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.
Files changed (121) hide show
  1. package/README.md +1 -1
  2. package/dist/api/auth.js +11 -1
  3. package/dist/api/auth.test.js +29 -0
  4. package/dist/api/errors.js +23 -0
  5. package/dist/api/route-coverage.test.js +61 -21
  6. package/dist/api/routes/agents.js +472 -0
  7. package/dist/api/routes/memory.js +299 -0
  8. package/dist/api/routes/projects.js +170 -0
  9. package/dist/api/routes/sessions.js +347 -0
  10. package/dist/api/routes/system.js +82 -0
  11. package/dist/api/routes/wiki.js +455 -0
  12. package/dist/api/routes/wiki.test.js +49 -0
  13. package/dist/api/send-json.js +16 -0
  14. package/dist/api/send-json.test.js +18 -0
  15. package/dist/api/server-runtime.js +45 -3
  16. package/dist/api/server.js +34 -1764
  17. package/dist/api/server.test.js +239 -8
  18. package/dist/api/sse-hub.js +37 -0
  19. package/dist/cli.js +1 -1
  20. package/dist/config.js +151 -58
  21. package/dist/config.test.js +29 -0
  22. package/dist/copilot/okr-mapper.js +2 -11
  23. package/dist/copilot/orchestrator.js +358 -352
  24. package/dist/copilot/orchestrator.test.js +139 -4
  25. package/dist/copilot/prompt-date.js +2 -1
  26. package/dist/copilot/session-manager.js +25 -23
  27. package/dist/copilot/session-manager.test.js +35 -1
  28. package/dist/copilot/standup.js +2 -2
  29. package/dist/copilot/task-event-log.js +7 -1
  30. package/dist/copilot/task-event-log.test.js +13 -0
  31. package/dist/copilot/tools/agent.js +608 -0
  32. package/dist/copilot/tools/index.js +19 -0
  33. package/dist/copilot/tools/memory.js +678 -0
  34. package/dist/copilot/tools/models.js +2 -0
  35. package/dist/copilot/tools/okr.js +171 -0
  36. package/dist/copilot/tools/wiki.js +333 -0
  37. package/dist/copilot/tools-deps.js +4 -0
  38. package/dist/copilot/tools.agent.test.js +10 -8
  39. package/dist/copilot/tools.inventory.test.js +76 -0
  40. package/dist/copilot/tools.js +1 -1780
  41. package/dist/copilot/tools.okr.test.js +31 -0
  42. package/dist/copilot/tools.wiki.test.js +6 -3
  43. package/dist/copilot/turn-event-log.js +31 -4
  44. package/dist/copilot/turn-event-log.test.js +24 -2
  45. package/dist/copilot/workiq-installer.test.js +2 -2
  46. package/dist/daemon-install.js +3 -2
  47. package/dist/daemon.js +9 -17
  48. package/dist/integrations/ado-client.js +90 -9
  49. package/dist/integrations/ado-client.test.js +56 -0
  50. package/dist/integrations/team-push.js +1 -0
  51. package/dist/integrations/team-push.test.js +6 -0
  52. package/dist/integrations/teams-notify.js +1 -0
  53. package/dist/integrations/teams-notify.test.js +5 -0
  54. package/dist/memory/active-scope.test.js +0 -1
  55. package/dist/memory/checkpoint.js +89 -72
  56. package/dist/memory/checkpoint.test.js +23 -3
  57. package/dist/memory/eot.js +87 -85
  58. package/dist/memory/eot.test.js +71 -3
  59. package/dist/memory/hooks.js +2 -4
  60. package/dist/memory/housekeeping-scheduler.js +1 -1
  61. package/dist/memory/housekeeping-scheduler.test.js +1 -2
  62. package/dist/memory/housekeeping.js +100 -3
  63. package/dist/memory/housekeeping.test.js +33 -2
  64. package/dist/memory/reflect.test.js +2 -0
  65. package/dist/memory/scope-lock.js +26 -0
  66. package/dist/memory/scope-lock.test.js +118 -0
  67. package/dist/memory/scopes.test.js +0 -1
  68. package/dist/mode-context.js +58 -5
  69. package/dist/mode-context.test.js +68 -0
  70. package/dist/paths.js +1 -0
  71. package/dist/setup.js +3 -2
  72. package/dist/shared/api-schemas.js +48 -5
  73. package/dist/store/connection.js +96 -0
  74. package/dist/store/db.js +5 -1498
  75. package/dist/store/db.test.js +182 -1
  76. package/dist/store/migrations.js +460 -0
  77. package/dist/store/repositories/memory.js +281 -0
  78. package/dist/store/repositories/okr.js +3 -0
  79. package/dist/store/repositories/projects.js +5 -0
  80. package/dist/store/repositories/sessions.js +284 -0
  81. package/dist/store/repositories/wiki.js +60 -0
  82. package/dist/store/schema.js +501 -0
  83. package/dist/util/logger.js +3 -2
  84. package/dist/wiki/consolidation.js +50 -9
  85. package/dist/wiki/consolidation.test.js +45 -0
  86. package/dist/wiki/frontmatter.js +43 -13
  87. package/dist/wiki/frontmatter.test.js +24 -0
  88. package/dist/wiki/fs.js +16 -4
  89. package/dist/wiki/fs.test.js +84 -0
  90. package/dist/wiki/index-manager.js +30 -2
  91. package/dist/wiki/index-manager.test.js +43 -12
  92. package/dist/wiki/ingest.js +1 -1
  93. package/dist/wiki/lock.js +11 -1
  94. package/dist/wiki/log-manager.js +2 -7
  95. package/dist/wiki/migrate.js +44 -17
  96. package/dist/wiki/project-registry.js +10 -5
  97. package/dist/wiki/project-registry.test.js +14 -0
  98. package/dist/wiki/scheduler.js +1 -1
  99. package/dist/wiki/seed-team-wiki.js +2 -1
  100. package/dist/wiki/team-sync.js +31 -6
  101. package/dist/wiki/team-sync.test.js +81 -0
  102. package/package.json +1 -1
  103. package/web/dist/assets/WikiEdit-EBVoY1Pk.js +30 -0
  104. package/web/dist/assets/WikiEdit-EBVoY1Pk.js.map +1 -0
  105. package/web/dist/assets/WikiGraph-BUbbABq-.js +2 -0
  106. package/web/dist/assets/WikiGraph-BUbbABq-.js.map +1 -0
  107. package/web/dist/assets/icon-acolyte-cream.svg +10 -0
  108. package/web/dist/assets/icon-acolyte-dark.svg +10 -0
  109. package/web/dist/assets/icon-acolyte-gold.svg +10 -0
  110. package/web/dist/assets/icon-acolyte-ibad.svg +10 -0
  111. package/web/dist/assets/icon-acolyte-lit.svg +10 -0
  112. package/web/dist/assets/icon-acolyte-mono.svg +10 -0
  113. package/web/dist/assets/icon-acolyte.png +0 -0
  114. package/web/dist/assets/icon-acolyte.svg +10 -0
  115. package/web/dist/assets/index-BGLL9pgM.css +10 -0
  116. package/web/dist/assets/index-KFX8UmOb.js +250 -0
  117. package/web/dist/assets/index-KFX8UmOb.js.map +1 -0
  118. package/web/dist/index.html +6 -4
  119. package/web/dist/assets/index-5kz9aRU9.css +0 -10
  120. package/web/dist/assets/index-iQrv3lQN.js +0 -286
  121. 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