wyrm-mcp 7.2.0 → 7.2.2
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/LICENSE +26 -667
- package/NOTICE +14 -33
- package/dist/activation.d.ts.map +1 -1
- package/dist/activation.js +1 -44
- package/dist/activation.js.map +1 -1
- package/dist/agent-daemon.js +4 -281
- package/dist/agent-loop.js +7 -332
- package/dist/analytics.js +13 -236
- package/dist/attribution.js +1 -49
- package/dist/audit.js +2 -457
- package/dist/auto-capture.js +3 -138
- package/dist/auto-orchestrator.js +1 -325
- package/dist/autoconfig.js +39 -840
- package/dist/buddy-runner.js +1 -109
- package/dist/buddy.js +14 -564
- package/dist/build-flags.js +1 -17
- package/dist/capabilities.js +3 -183
- package/dist/capture.js +1 -56
- package/dist/causality.js +6 -107
- package/dist/cli.js +20 -281
- package/dist/cloud/cli.js +5 -541
- package/dist/cloud/client.js +1 -221
- package/dist/cloud/crypto.js +1 -85
- package/dist/cloud/machine-id.js +2 -113
- package/dist/cloud/recovery.js +1 -60
- package/dist/cloud/sync-engine.js +7 -543
- package/dist/cloud-backup.js +5 -579
- package/dist/cloud-profile.js +1 -138
- package/dist/cloud-sync-entrypoint.js +1 -47
- package/dist/cloud-sync.js +2 -309
- package/dist/constellation.js +12 -168
- package/dist/context-build-budgeted.js +4 -144
- package/dist/context-ranking.js +1 -69
- package/dist/crypto.js +1 -179
- package/dist/daemon-write-endpoint.js +1 -290
- package/dist/daemon-writer.js +2 -406
- package/dist/database.js +43 -1110
- package/dist/deprecations.js +2 -162
- package/dist/design.js +13 -141
- package/dist/event-replication.js +1 -112
- package/dist/events-sse.js +7 -43
- package/dist/events.js +6 -238
- package/dist/failure-patterns.js +42 -659
- package/dist/federation.js +12 -236
- package/dist/goals.js +13 -101
- package/dist/golden.js +3 -355
- package/dist/handlers/agent.js +4 -165
- package/dist/handlers/alias-adapters.js +1 -129
- package/dist/handlers/aliases.js +1 -171
- package/dist/handlers/audit.js +1 -87
- package/dist/handlers/boundary.js +1 -221
- package/dist/handlers/capture.js +73 -1109
- package/dist/handlers/causality.js +7 -114
- package/dist/handlers/cloud.js +85 -382
- package/dist/handlers/companion.js +28 -459
- package/dist/handlers/datalake.js +7 -187
- package/dist/handlers/dispatch-context.js +0 -22
- package/dist/handlers/entity.js +25 -256
- package/dist/handlers/events.js +16 -335
- package/dist/handlers/failure.js +13 -340
- package/dist/handlers/goals.js +4 -296
- package/dist/handlers/intelligence.js +126 -674
- package/dist/handlers/invoicing.js +1 -70
- package/dist/handlers/mcpclient.js +6 -137
- package/dist/handlers/orchestration.js +40 -125
- package/dist/handlers/output-schemas.js +1 -24
- package/dist/handlers/presence.js +3 -99
- package/dist/handlers/project.js +28 -182
- package/dist/handlers/prompts.js +6 -157
- package/dist/handlers/quest.js +4 -224
- package/dist/handlers/recall.js +11 -218
- package/dist/handlers/registry.js +1 -167
- package/dist/handlers/resources.js +1 -288
- package/dist/handlers/review.js +11 -74
- package/dist/handlers/run.js +17 -487
- package/dist/handlers/search.js +15 -326
- package/dist/handlers/session.js +28 -615
- package/dist/handlers/share.js +8 -184
- package/dist/handlers/shims.js +1 -464
- package/dist/handlers/skill.js +67 -449
- package/dist/handlers/survivors.js +1 -120
- package/dist/handlers/symbols.js +8 -109
- package/dist/handlers/syncops.js +4 -302
- package/dist/handlers/types.js +1 -27
- package/dist/harvest.js +5 -191
- package/dist/hours.js +7 -156
- package/dist/http-auth.js +3 -321
- package/dist/http-fast.js +21 -1137
- package/dist/icons.js +1 -47
- package/dist/index.js +2 -924
- package/dist/indexer.js +4 -145
- package/dist/intelligence.js +31 -261
- package/dist/internal-dispatch.js +3 -212
- package/dist/keyset.js +1 -110
- package/dist/knowledge-graph.js +12 -176
- package/dist/license.d.ts +11 -0
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +2 -414
- package/dist/license.js.map +1 -1
- package/dist/logger.js +2 -199
- package/dist/maintenance.js +2 -148
- package/dist/mcp-client.js +6 -262
- package/dist/memory-artifacts.js +30 -449
- package/dist/migrate-prompt.js +2 -124
- package/dist/migrations.js +40 -655
- package/dist/performance.js +1 -228
- package/dist/presence.js +11 -140
- package/dist/priority-embed.js +5 -164
- package/dist/providers/embedding-provider.js +1 -196
- package/dist/readonly-gate.js +1 -29
- package/dist/rehydration.js +9 -157
- package/dist/reindex.js +1 -88
- package/dist/render-target.js +21 -514
- package/dist/render.js +4 -280
- package/dist/repl-guard.js +1 -173
- package/dist/replication-daemon-entrypoint.js +1 -31
- package/dist/replication-daemon.js +2 -262
- package/dist/resilience.js +1 -591
- package/dist/reverse-bridge.js +5 -360
- package/dist/security.js +1 -244
- package/dist/session-seen.js +3 -51
- package/dist/setup.js +1 -260
- package/dist/skill-author.js +5 -168
- package/dist/spec-kit.js +1 -191
- package/dist/sqlite-busy.js +1 -154
- package/dist/statusline.js +11 -315
- package/dist/sub-agent.js +13 -262
- package/dist/summarizer.js +13 -139
- package/dist/symbols.js +7 -283
- package/dist/sync.js +5 -359
- package/dist/tasks-dispatch.js +1 -84
- package/dist/tasks.js +1 -282
- package/dist/token-budget.js +1 -143
- package/dist/tool-analytics.js +7 -129
- package/dist/tool-annotations.js +1 -365
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/tool-profiles.js +1 -75
- package/dist/trace-harvest.js +6 -244
- package/dist/types.js +1 -30
- package/dist/ui-dashboard.js +41 -50
- package/dist/ulid.js +1 -81
- package/dist/validate.js +1 -129
- package/dist/vault.js +1 -534
- package/dist/vectors.js +3 -184
- package/dist/version-check.js +4 -136
- package/dist/visibility.js +19 -155
- package/dist/wyrm-cli.js +98 -2451
- package/dist/wyrm-cli.js.map +1 -1
- package/dist/wyrm-guard.js +14 -424
- package/dist/wyrm-loop.js +3 -150
- package/dist/wyrm-manifest.json +1 -1
- package/dist/wyrm-statusline-daemon.js +1 -11
- package/dist/wyrm-statusline.js +4 -56
- package/dist/wyrm-ui.js +9 -77
- package/package.json +4 -2
package/dist/migrations.js
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Wyrm Database Migrations — Versioned schema management
|
|
3
|
-
*
|
|
4
|
-
* @copyright 2026 Ghost Protocol (Pvt) Ltd.
|
|
5
|
-
* @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
|
|
6
|
-
*
|
|
7
|
-
* Each migration is a function that receives a better-sqlite3 Database instance.
|
|
8
|
-
* Migrations run in order and are tracked in the schema_versions table.
|
|
9
|
-
* Once applied, a migration is never re-run.
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* All migrations in order. Append new migrations to the end — never modify existing ones.
|
|
13
|
-
*/
|
|
14
|
-
export const migrations = [
|
|
15
|
-
{
|
|
16
|
-
version: 1,
|
|
17
|
-
description: 'Baseline schema — projects, sessions, quests, context, data_lake, skills, FTS5',
|
|
18
|
-
up: (db) => {
|
|
19
|
-
db.exec(`
|
|
1
|
+
const n=[{version:1,description:"Baseline schema \u2014 projects, sessions, quests, context, data_lake, skills, FTS5",up:e=>{e.exec(`
|
|
20
2
|
-- Core tables
|
|
21
3
|
CREATE TABLE IF NOT EXISTS projects (
|
|
22
4
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -215,14 +197,7 @@ export const migrations = [
|
|
|
215
197
|
VALUES('delete', old.id, old.key, old.value);
|
|
216
198
|
INSERT INTO data_lake_fts(rowid, key, value) VALUES (new.id, new.key, new.value);
|
|
217
199
|
END;
|
|
218
|
-
`)
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
version: 2,
|
|
223
|
-
description: 'Add usage_events table for analytics tracking',
|
|
224
|
-
up: (db) => {
|
|
225
|
-
db.exec(`
|
|
200
|
+
`)}},{version:2,description:"Add usage_events table for analytics tracking",up:e=>{e.exec(`
|
|
226
201
|
CREATE TABLE IF NOT EXISTS usage_events (
|
|
227
202
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
228
203
|
tool_name TEXT NOT NULL,
|
|
@@ -235,34 +210,7 @@ export const migrations = [
|
|
|
235
210
|
timestamp TEXT DEFAULT (datetime('now'))
|
|
236
211
|
);
|
|
237
212
|
CREATE INDEX IF NOT EXISTS idx_usage_timestamp ON usage_events(timestamp);
|
|
238
|
-
`);
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
version: 3,
|
|
243
|
-
description: 'Add visibility and created_by columns for future multi-user support',
|
|
244
|
-
up: (db) => {
|
|
245
|
-
// Add visibility + created_by to tables that will need them.
|
|
246
|
-
// Using ALTER TABLE with defaults so existing rows get sensible values.
|
|
247
|
-
const tables = ['sessions', 'quests', 'context', 'data_lake'];
|
|
248
|
-
for (const table of tables) {
|
|
249
|
-
// Check if column already exists (idempotent)
|
|
250
|
-
const cols = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
251
|
-
const colNames = cols.map(c => c.name);
|
|
252
|
-
if (!colNames.includes('visibility')) {
|
|
253
|
-
db.exec(`ALTER TABLE ${table} ADD COLUMN visibility TEXT DEFAULT 'private'`);
|
|
254
|
-
}
|
|
255
|
-
if (!colNames.includes('created_by')) {
|
|
256
|
-
db.exec(`ALTER TABLE ${table} ADD COLUMN created_by TEXT DEFAULT 'local'`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
version: 4,
|
|
263
|
-
description: 'Knowledge graph — entities, aliases, relationships with FTS5',
|
|
264
|
-
up: (db) => {
|
|
265
|
-
db.exec(`
|
|
213
|
+
`)}},{version:3,description:"Add visibility and created_by columns for future multi-user support",up:e=>{const E=["sessions","quests","context","data_lake"];for(const t of E){const i=e.prepare(`PRAGMA table_info(${t})`).all().map(T=>T.name);i.includes("visibility")||e.exec(`ALTER TABLE ${t} ADD COLUMN visibility TEXT DEFAULT 'private'`),i.includes("created_by")||e.exec(`ALTER TABLE ${t} ADD COLUMN created_by TEXT DEFAULT 'local'`)}}},{version:4,description:"Knowledge graph \u2014 entities, aliases, relationships with FTS5",up:e=>{e.exec(`
|
|
266
214
|
-- Entities: named things that can be linked together
|
|
267
215
|
CREATE TABLE IF NOT EXISTS entities (
|
|
268
216
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -291,7 +239,7 @@ export const migrations = [
|
|
|
291
239
|
CREATE INDEX IF NOT EXISTS idx_entity_aliases_entity ON entity_aliases(entity_id);
|
|
292
240
|
CREATE INDEX IF NOT EXISTS idx_entity_aliases_alias ON entity_aliases(alias);
|
|
293
241
|
|
|
294
|
-
-- Relationships between entities
|
|
242
|
+
-- Relationships between entities \u2014 directed, typed, with provenance
|
|
295
243
|
CREATE TABLE IF NOT EXISTS relationships (
|
|
296
244
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
297
245
|
project_id INTEGER NOT NULL,
|
|
@@ -337,14 +285,7 @@ export const migrations = [
|
|
|
337
285
|
INSERT INTO entities_fts(rowid, name, type, metadata)
|
|
338
286
|
VALUES (new.id, new.name, new.type, new.metadata);
|
|
339
287
|
END;
|
|
340
|
-
`)
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
version: 5,
|
|
345
|
-
description: 'Memory artifacts — distilled knowledge for intelligence amplification',
|
|
346
|
-
up: (db) => {
|
|
347
|
-
db.exec(`
|
|
288
|
+
`)}},{version:5,description:"Memory artifacts \u2014 distilled knowledge for intelligence amplification",up:e=>{e.exec(`
|
|
348
289
|
-- Memory artifacts: distilled problem-solution records, lessons, patterns, anti-patterns.
|
|
349
290
|
-- Used by wyrm_context_build to assemble task-specific memory briefs for AI models.
|
|
350
291
|
CREATE TABLE IF NOT EXISTS memory_artifacts (
|
|
@@ -399,14 +340,7 @@ export const migrations = [
|
|
|
399
340
|
INSERT INTO memory_artifacts_fts(rowid, problem, constraints, validated_fix, why_it_worked, tags)
|
|
400
341
|
VALUES (new.id, new.problem, new.constraints, new.validated_fix, new.why_it_worked, new.tags);
|
|
401
342
|
END;
|
|
402
|
-
`)
|
|
403
|
-
},
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
version: 6,
|
|
407
|
-
description: 'Intelligence amplification — ground truths, reasoning scaffolds, distillation review queue',
|
|
408
|
-
up: (db) => {
|
|
409
|
-
db.exec(`
|
|
343
|
+
`)}},{version:6,description:"Intelligence amplification \u2014 ground truths, reasoning scaffolds, distillation review queue",up:e=>{e.exec(`
|
|
410
344
|
-- Ground truths: authoritative, versioned project facts.
|
|
411
345
|
-- Use is_current=1 for the live value. Superseded rows kept for history.
|
|
412
346
|
CREATE TABLE IF NOT EXISTS ground_truths (
|
|
@@ -476,33 +410,12 @@ export const migrations = [
|
|
|
476
410
|
-- needs_review=1 means it was auto-created by wyrm_distill and awaits approval
|
|
477
411
|
ALTER TABLE memory_artifacts ADD COLUMN needs_review INTEGER DEFAULT 0;
|
|
478
412
|
CREATE INDEX IF NOT EXISTS idx_artifacts_review ON memory_artifacts(project_id, needs_review);
|
|
479
|
-
`)
|
|
480
|
-
},
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
version: 7,
|
|
484
|
-
description: 'Ground truth TTL — temporal decay support for staleness detection',
|
|
485
|
-
up: (db) => {
|
|
486
|
-
db.exec(`
|
|
413
|
+
`)}},{version:7,description:"Ground truth TTL \u2014 temporal decay support for staleness detection",up:e=>{e.exec(`
|
|
487
414
|
ALTER TABLE ground_truths ADD COLUMN ttl_days INTEGER CHECK(ttl_days IS NULL OR ttl_days > 0);
|
|
488
|
-
`)
|
|
489
|
-
},
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
version: 8,
|
|
493
|
-
description: 'Memory artifact decay — access tracking for auto-prune',
|
|
494
|
-
up: (db) => {
|
|
495
|
-
db.exec(`
|
|
415
|
+
`)}},{version:8,description:"Memory artifact decay \u2014 access tracking for auto-prune",up:e=>{e.exec(`
|
|
496
416
|
ALTER TABLE memory_artifacts ADD COLUMN last_accessed_at TEXT DEFAULT (datetime('now'));
|
|
497
417
|
ALTER TABLE memory_artifacts ADD COLUMN access_count INTEGER NOT NULL DEFAULT 0;
|
|
498
|
-
`)
|
|
499
|
-
},
|
|
500
|
-
},
|
|
501
|
-
{
|
|
502
|
-
version: 9,
|
|
503
|
-
description: 'v3.9.0 — failure patterns, agent presence, causality, symbol index, tool analytics',
|
|
504
|
-
up: (db) => {
|
|
505
|
-
db.exec(`
|
|
418
|
+
`)}},{version:9,description:"v3.9.0 \u2014 failure patterns, agent presence, causality, symbol index, tool analytics",up:e=>{e.exec(`
|
|
506
419
|
-- ============================================================
|
|
507
420
|
-- Tier 1.1: Counter-pattern detection
|
|
508
421
|
-- Records edits/commands that failed so the predictive push can
|
|
@@ -649,16 +562,9 @@ export const migrations = [
|
|
|
649
562
|
CREATE INDEX IF NOT EXISTS idx_toolcall_name ON tool_call_log(tool_name, called_at DESC);
|
|
650
563
|
CREATE INDEX IF NOT EXISTS idx_toolcall_project ON tool_call_log(project_id, called_at DESC);
|
|
651
564
|
CREATE INDEX IF NOT EXISTS idx_toolcall_success ON tool_call_log(success, called_at DESC);
|
|
652
|
-
`)
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
version: 10,
|
|
657
|
-
description: 'v4.0.0 — federated sync, hash-chained audit, sub-agent embedding (wyrm_ask), session-rehydration helpers',
|
|
658
|
-
up: (db) => {
|
|
659
|
-
db.exec(`
|
|
565
|
+
`)}},{version:10,description:"v4.0.0 \u2014 federated sync, hash-chained audit, sub-agent embedding (wyrm_ask), session-rehydration helpers",up:e=>{e.exec(`
|
|
660
566
|
-- ============================================================
|
|
661
|
-
-- Tier 2.8: Federated team Wyrm
|
|
567
|
+
-- Tier 2.8: Federated team Wyrm \u2014 selective sharing
|
|
662
568
|
-- A single "is_shared" flag on the major user-facing tables.
|
|
663
569
|
-- A team server pulls only is_shared=1 rows during sync.
|
|
664
570
|
-- ============================================================
|
|
@@ -708,7 +614,7 @@ export const migrations = [
|
|
|
708
614
|
ON sync_log(remote_origin, direction, synced_at DESC);
|
|
709
615
|
|
|
710
616
|
-- ============================================================
|
|
711
|
-
-- Tier 3.9: Compliance audit trail
|
|
617
|
+
-- Tier 3.9: Compliance audit trail \u2014 hash-chained event log
|
|
712
618
|
-- Each row references the hash of the previous row. Tampering with
|
|
713
619
|
-- any historical entry breaks the chain. Combined with optional
|
|
714
620
|
-- Ed25519 signing this provides a SOC2/HIPAA-grade audit surface.
|
|
@@ -730,7 +636,7 @@ export const migrations = [
|
|
|
730
636
|
CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit_log(actor, logged_at DESC);
|
|
731
637
|
|
|
732
638
|
-- ============================================================
|
|
733
|
-
-- Tier 3.10: Sub-agent embedding
|
|
639
|
+
-- Tier 3.10: Sub-agent embedding \u2014 wyrm_ask query log
|
|
734
640
|
-- Tracks every wyrm_ask invocation: query, context window, model,
|
|
735
641
|
-- response, latency, tokens. Cost tracking + answer quality
|
|
736
642
|
-- analysis. Doubles as a public-facing audit if wyrm_ask is
|
|
@@ -753,16 +659,9 @@ export const migrations = [
|
|
|
753
659
|
);
|
|
754
660
|
CREATE INDEX IF NOT EXISTS idx_llm_project ON llm_query_log(project_id, asked_at DESC);
|
|
755
661
|
CREATE INDEX IF NOT EXISTS idx_llm_model ON llm_query_log(model, asked_at DESC);
|
|
756
|
-
`)
|
|
757
|
-
},
|
|
758
|
-
},
|
|
759
|
-
{
|
|
760
|
-
version: 11,
|
|
761
|
-
description: 'v5.0.0 — goals, agent loop, outbound MCP clients, action log',
|
|
762
|
-
up: (db) => {
|
|
763
|
-
db.exec(`
|
|
662
|
+
`)}},{version:11,description:"v5.0.0 \u2014 goals, agent loop, outbound MCP clients, action log",up:e=>{e.exec(`
|
|
764
663
|
-- ============================================================
|
|
765
|
-
-- v5.0.0: Goals
|
|
664
|
+
-- v5.0.0: Goals \u2014 persistent objectives Wyrm pursues
|
|
766
665
|
-- ============================================================
|
|
767
666
|
CREATE TABLE IF NOT EXISTS goals (
|
|
768
667
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -810,7 +709,7 @@ export const migrations = [
|
|
|
810
709
|
CREATE INDEX IF NOT EXISTS idx_goal_iter_goal ON goal_iterations(goal_id, iteration_num);
|
|
811
710
|
|
|
812
711
|
-- ============================================================
|
|
813
|
-
-- v5.0.0: Outbound MCP clients
|
|
712
|
+
-- v5.0.0: Outbound MCP clients \u2014 Wyrm calls other servers
|
|
814
713
|
-- Config rows for MCP servers Wyrm should connect to (stdio).
|
|
815
714
|
-- ============================================================
|
|
816
715
|
CREATE TABLE IF NOT EXISTS mcp_client_configs (
|
|
@@ -818,7 +717,7 @@ export const migrations = [
|
|
|
818
717
|
server_name TEXT UNIQUE NOT NULL, -- 'github', 'slack', 'phantomdragon', etc.
|
|
819
718
|
command TEXT NOT NULL, -- 'npx' or absolute path
|
|
820
719
|
args TEXT, -- JSON array of args
|
|
821
|
-
env TEXT, -- JSON object of env vars (NEVER store secrets here in plaintext for prod
|
|
720
|
+
env TEXT, -- JSON object of env vars (NEVER store secrets here in plaintext for prod \u2014 but local dev is fine)
|
|
822
721
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
823
722
|
added_at TEXT DEFAULT (datetime('now')),
|
|
824
723
|
last_used_at TEXT,
|
|
@@ -839,7 +738,7 @@ export const migrations = [
|
|
|
839
738
|
CREATE INDEX IF NOT EXISTS idx_ext_call_server ON external_call_log(server_name, called_at DESC);
|
|
840
739
|
|
|
841
740
|
-- ============================================================
|
|
842
|
-
-- v5.0.0: Agent actions
|
|
741
|
+
-- v5.0.0: Agent actions \u2014 the audit-trail of autonomous loop ticks
|
|
843
742
|
-- Higher-level than goal_iterations; one row per wyrm-loop tick.
|
|
844
743
|
-- ============================================================
|
|
845
744
|
CREATE TABLE IF NOT EXISTS agent_actions (
|
|
@@ -854,14 +753,7 @@ export const migrations = [
|
|
|
854
753
|
);
|
|
855
754
|
CREATE INDEX IF NOT EXISTS idx_agent_actions_goal ON agent_actions(goal_id, ran_at DESC);
|
|
856
755
|
CREATE INDEX IF NOT EXISTS idx_agent_actions_actor ON agent_actions(actor, ran_at DESC);
|
|
857
|
-
`)
|
|
858
|
-
},
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
version: 12,
|
|
862
|
-
description: 'session_seen_artifacts (spec 014) + token-budget telemetry columns',
|
|
863
|
-
up: (db) => {
|
|
864
|
-
db.exec(`
|
|
756
|
+
`)}},{version:12,description:"session_seen_artifacts (spec 014) + token-budget telemetry columns",up:e=>{e.exec(`
|
|
865
757
|
-- session_seen_artifacts: per-session dedup so wyrm_context_build
|
|
866
758
|
-- doesn't re-emit fully-rendered content the AI already saw this
|
|
867
759
|
-- session. Pruned by wyrm_maintenance after WYRM_SEEN_TTL_DAYS
|
|
@@ -878,14 +770,7 @@ export const migrations = [
|
|
|
878
770
|
ON session_seen_artifacts(session_id, shown_at DESC);
|
|
879
771
|
CREATE INDEX IF NOT EXISTS idx_session_seen_prune
|
|
880
772
|
ON session_seen_artifacts(shown_at);
|
|
881
|
-
`)
|
|
882
|
-
},
|
|
883
|
-
},
|
|
884
|
-
{
|
|
885
|
-
version: 13,
|
|
886
|
-
description: 'design_tokens + design_references — creative workflow tables (5.9.0)',
|
|
887
|
-
up: (db) => {
|
|
888
|
-
db.exec(`
|
|
773
|
+
`)}},{version:13,description:"design_tokens + design_references \u2014 creative workflow tables (5.9.0)",up:e=>{e.exec(`
|
|
889
774
|
CREATE TABLE IF NOT EXISTS design_tokens (
|
|
890
775
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
891
776
|
project_id INTEGER NOT NULL,
|
|
@@ -936,14 +821,7 @@ export const migrations = [
|
|
|
936
821
|
INSERT INTO design_references_fts(rowid, title, notes, tags)
|
|
937
822
|
VALUES (new.id, new.title, new.notes, new.tags);
|
|
938
823
|
END;
|
|
939
|
-
`)
|
|
940
|
-
},
|
|
941
|
-
},
|
|
942
|
-
{
|
|
943
|
-
version: 14,
|
|
944
|
-
description: 'token_savings_log + cross_project_visibility — Wyrm 6.0 substrate (spec 018)',
|
|
945
|
-
up: (db) => {
|
|
946
|
-
db.exec(`
|
|
824
|
+
`)}},{version:14,description:"token_savings_log + cross_project_visibility \u2014 Wyrm 6.0 substrate (spec 018)",up:e=>{e.exec(`
|
|
947
825
|
-- Token-savings telemetry: every Wyrm tool that has a counterfactual
|
|
948
826
|
-- logs an estimate so the statusline and digest can show real ROI.
|
|
949
827
|
CREATE TABLE IF NOT EXISTS token_savings_log (
|
|
@@ -966,36 +844,10 @@ export const migrations = [
|
|
|
966
844
|
ON token_savings_log(session_id, timestamp DESC);
|
|
967
845
|
CREATE INDEX IF NOT EXISTS idx_savings_category
|
|
968
846
|
ON token_savings_log(category, timestamp DESC);
|
|
969
|
-
`);
|
|
970
|
-
// Cross-project visibility flags on every searchable kind.
|
|
971
|
-
// Default 'within' — no existing data leaks across projects.
|
|
972
|
-
const tables = [
|
|
973
|
-
'ground_truths',
|
|
974
|
-
'memory_artifacts',
|
|
975
|
-
'quests',
|
|
976
|
-
'decision_edges', // was 'decisions' (a table that never existed) — fixed in v17; kept correct here for fresh installs
|
|
977
|
-
'design_references',
|
|
978
|
-
'design_tokens',
|
|
979
|
-
];
|
|
980
|
-
for (const t of tables) {
|
|
981
|
-
try {
|
|
982
|
-
const cols = db.prepare(`PRAGMA table_info(${t})`).all();
|
|
983
|
-
if (cols.some((c) => c.name === 'cross_project_visibility'))
|
|
984
|
-
continue;
|
|
985
|
-
db.exec(`
|
|
847
|
+
`);const E=["ground_truths","memory_artifacts","quests","decision_edges","design_references","design_tokens"];for(const t of E)try{if(e.prepare(`PRAGMA table_info(${t})`).all().some(i=>i.name==="cross_project_visibility"))continue;e.exec(`
|
|
986
848
|
ALTER TABLE ${t} ADD COLUMN cross_project_visibility TEXT NOT NULL DEFAULT 'within'
|
|
987
849
|
CHECK (cross_project_visibility IN ('within', 'org', 'public'));
|
|
988
|
-
`)
|
|
989
|
-
}
|
|
990
|
-
catch { /* table may not exist on older installs; safe to skip */ }
|
|
991
|
-
}
|
|
992
|
-
},
|
|
993
|
-
},
|
|
994
|
-
{
|
|
995
|
-
version: 15,
|
|
996
|
-
description: 'Live Memory v6.4 — append-only events log + wyrm_meta (device identity)',
|
|
997
|
-
up: (db) => {
|
|
998
|
-
db.exec(`
|
|
850
|
+
`)}catch{}}},{version:15,description:"Live Memory v6.4 \u2014 append-only events log + wyrm_meta (device identity)",up:e=>{e.exec(`
|
|
999
851
|
-- Node-local KV (stable device id for cross-device event identity, etc.)
|
|
1000
852
|
CREATE TABLE IF NOT EXISTS wyrm_meta (
|
|
1001
853
|
key TEXT PRIMARY KEY,
|
|
@@ -1004,7 +856,7 @@ export const migrations = [
|
|
|
1004
856
|
|
|
1005
857
|
-- Append-only event log (Live Memory v6.4). The existing tables stay the
|
|
1006
858
|
-- system of record; this is a derived stream for live propagation. Writes
|
|
1007
|
-
-- emit here as a FAILURE-ISOLATED side effect (see events.ts)
|
|
859
|
+
-- emit here as a FAILURE-ISOLATED side effect (see events.ts) \u2014 a broken
|
|
1008
860
|
-- events table can never regress the canonical write. 'cursor' is the
|
|
1009
861
|
-- LOCAL subscription cursor (monotonic per node); cross-device identity is
|
|
1010
862
|
-- (origin_device, origin_seq), with replication via INSERT OR IGNORE.
|
|
@@ -1025,19 +877,7 @@ export const migrations = [
|
|
|
1025
877
|
);
|
|
1026
878
|
CREATE INDEX IF NOT EXISTS idx_events_project_cursor ON events(project_id, cursor);
|
|
1027
879
|
CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind, created_at);
|
|
1028
|
-
`)
|
|
1029
|
-
},
|
|
1030
|
-
},
|
|
1031
|
-
{
|
|
1032
|
-
version: 16,
|
|
1033
|
-
description: 'Rebuild FTS tables with porter stemming (override≈overrides≈overriding) for better recall',
|
|
1034
|
-
up: (db) => {
|
|
1035
|
-
// The base-table triggers reference these FTS tables BY NAME, so recreating
|
|
1036
|
-
// them with the same name + the porter tokenizer keeps the triggers valid —
|
|
1037
|
-
// no need to drop/recreate triggers. `rebuild` repopulates from the
|
|
1038
|
-
// external-content base table. Migrations run single-threaded at startup,
|
|
1039
|
-
// so there's no concurrent write between DROP and CREATE.
|
|
1040
|
-
db.exec(`
|
|
880
|
+
`)}},{version:16,description:"Rebuild FTS tables with porter stemming (override\u2248overrides\u2248overriding) for better recall",up:e=>{e.exec(`
|
|
1041
881
|
DROP TABLE IF EXISTS quests_fts;
|
|
1042
882
|
CREATE VIRTUAL TABLE quests_fts USING fts5(
|
|
1043
883
|
title, description, content='quests', content_rowid='id',
|
|
@@ -1065,62 +905,13 @@ export const migrations = [
|
|
|
1065
905
|
tokenize='porter unicode61'
|
|
1066
906
|
);
|
|
1067
907
|
INSERT INTO skills_fts(skills_fts) VALUES('rebuild');
|
|
1068
|
-
`)
|
|
1069
|
-
},
|
|
1070
|
-
},
|
|
1071
|
-
{
|
|
1072
|
-
version: 17,
|
|
1073
|
-
description: 'Backfill cross_project_visibility on decision_edges (migration 14 named a non-existent "decisions" table)',
|
|
1074
|
-
up: (db) => {
|
|
1075
|
-
// Migration 14 looped over a table called 'decisions' that never existed —
|
|
1076
|
-
// the real table is 'decision_edges' — so it silently never got the
|
|
1077
|
-
// cross_project_visibility column. Add it here (guarded + idempotent) so
|
|
1078
|
-
// already-migrated DBs converge with fresh installs (which now get it in v14).
|
|
1079
|
-
try {
|
|
1080
|
-
const cols = db.prepare(`PRAGMA table_info(decision_edges)`).all();
|
|
1081
|
-
if (!cols.some((c) => c.name === 'cross_project_visibility')) {
|
|
1082
|
-
db.exec(`
|
|
908
|
+
`)}},{version:17,description:'Backfill cross_project_visibility on decision_edges (migration 14 named a non-existent "decisions" table)',up:e=>{try{e.prepare("PRAGMA table_info(decision_edges)").all().some(t=>t.name==="cross_project_visibility")||e.exec(`
|
|
1083
909
|
ALTER TABLE decision_edges ADD COLUMN cross_project_visibility TEXT NOT NULL DEFAULT 'within'
|
|
1084
910
|
CHECK (cross_project_visibility IN ('within', 'org', 'public'));
|
|
1085
|
-
`);
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
catch { /* table may not exist on a very old install; safe to skip */ }
|
|
1089
|
-
},
|
|
1090
|
-
},
|
|
1091
|
-
{
|
|
1092
|
-
version: 18,
|
|
1093
|
-
description: 'GOD-SKILL SPEC v2 — skill tier/governs/composes + spec-kit registry (specs table)',
|
|
1094
|
-
up: (db) => {
|
|
1095
|
-
// --- Skill governance columns (additive, safe defaults) ---
|
|
1096
|
-
// tier: atomic|mega|god — apex skill tier routing. Default 'atomic' so
|
|
1097
|
-
// every pre-existing skill keeps its current (leaf) behaviour.
|
|
1098
|
-
// governs: JSON string array of skill names this node routes DOWN to
|
|
1099
|
-
// (god → megas, mega → atomics). Default '[]'.
|
|
1100
|
-
// composes: JSON string array of skill names this node pulls in laterally.
|
|
1101
|
-
// Default '[]'.
|
|
1102
|
-
// All three are guarded so re-running against a partially-migrated DB
|
|
1103
|
-
// (or a fresh install that already has them) is a no-op.
|
|
1104
|
-
const skillCols = db.prepare(`PRAGMA table_info(skills)`).all();
|
|
1105
|
-
const hasSkillCol = (c) => skillCols.some((x) => x.name === c);
|
|
1106
|
-
if (!hasSkillCol('tier')) {
|
|
1107
|
-
db.exec(`
|
|
911
|
+
`)}catch{}}},{version:18,description:"GOD-SKILL SPEC v2 \u2014 skill tier/governs/composes + spec-kit registry (specs table)",up:e=>{const E=e.prepare("PRAGMA table_info(skills)").all(),t=s=>E.some(i=>i.name===s);t("tier")||e.exec(`
|
|
1108
912
|
ALTER TABLE skills ADD COLUMN tier TEXT NOT NULL DEFAULT 'atomic'
|
|
1109
913
|
CHECK (tier IN ('atomic', 'mega', 'god'));
|
|
1110
|
-
`);
|
|
1111
|
-
}
|
|
1112
|
-
if (!hasSkillCol('governs')) {
|
|
1113
|
-
db.exec(`ALTER TABLE skills ADD COLUMN governs TEXT NOT NULL DEFAULT '[]';`);
|
|
1114
|
-
}
|
|
1115
|
-
if (!hasSkillCol('composes')) {
|
|
1116
|
-
db.exec(`ALTER TABLE skills ADD COLUMN composes TEXT NOT NULL DEFAULT '[]';`);
|
|
1117
|
-
}
|
|
1118
|
-
db.exec(`CREATE INDEX IF NOT EXISTS idx_skills_tier ON skills(tier);`);
|
|
1119
|
-
// --- Spec-kit registry (GHOSTMESH spec-kit specs → Wyrm-native) ---
|
|
1120
|
-
// One row per registered spec directory, linked to a project. The
|
|
1121
|
-
// generated quests carry a deterministic tag signature so wyrm_spec_register
|
|
1122
|
-
// is idempotent (re-run updates the same quests instead of duplicating).
|
|
1123
|
-
db.exec(`
|
|
914
|
+
`),t("governs")||e.exec("ALTER TABLE skills ADD COLUMN governs TEXT NOT NULL DEFAULT '[]';"),t("composes")||e.exec("ALTER TABLE skills ADD COLUMN composes TEXT NOT NULL DEFAULT '[]';"),e.exec("CREATE INDEX IF NOT EXISTS idx_skills_tier ON skills(tier);"),e.exec(`
|
|
1124
915
|
CREATE TABLE IF NOT EXISTS specs (
|
|
1125
916
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1126
917
|
project_id INTEGER NOT NULL,
|
|
@@ -1135,86 +926,11 @@ export const migrations = [
|
|
|
1135
926
|
);
|
|
1136
927
|
CREATE INDEX IF NOT EXISTS idx_specs_project ON specs(project_id);
|
|
1137
928
|
CREATE INDEX IF NOT EXISTS idx_specs_dir ON specs(spec_dir);
|
|
1138
|
-
`)
|
|
1139
|
-
},
|
|
1140
|
-
},
|
|
1141
|
-
{
|
|
1142
|
-
version: 19,
|
|
1143
|
-
description: 'Grove sync policy: projects.sync_policy gate (private by default) for cloud + federation isolation',
|
|
1144
|
-
up: (db) => {
|
|
1145
|
-
// Grove-level replication gate. A project's rows may leave the local box
|
|
1146
|
-
// only if its grove opts in:
|
|
1147
|
-
// 'private' (default): never replicates by any channel.
|
|
1148
|
-
// 'cloud': may replicate to the operator's OWN cloud backup
|
|
1149
|
-
// (per-row cross_project_visibility still applies).
|
|
1150
|
-
// 'team': may also federate to a team Wyrm
|
|
1151
|
-
// (per-row is_shared still applies).
|
|
1152
|
-
// This is a SECOND, grove-level gate on top of the existing per-row flags,
|
|
1153
|
-
// so one accidental flag flip can never alone leak a private grove.
|
|
1154
|
-
const pcols = db.prepare(`PRAGMA table_info(projects)`).all();
|
|
1155
|
-
if (!pcols.some((c) => c.name === 'sync_policy')) {
|
|
1156
|
-
db.exec(`
|
|
929
|
+
`)}},{version:19,description:"Grove sync policy: projects.sync_policy gate (private by default) for cloud + federation isolation",up:e=>{e.prepare("PRAGMA table_info(projects)").all().some(o=>o.name==="sync_policy")||e.exec(`
|
|
1157
930
|
ALTER TABLE projects ADD COLUMN sync_policy TEXT NOT NULL DEFAULT 'private'
|
|
1158
931
|
CHECK (sync_policy IN ('private', 'cloud', 'team'));
|
|
1159
|
-
`);
|
|
1160
|
-
|
|
1161
|
-
// Backward-compatible backfill, CONSERVATIVE: a project that already had
|
|
1162
|
-
// cloud-eligible rows (cross_project_visibility org/public) keeps its cloud
|
|
1163
|
-
// backup ability (cloud is per-operator + encrypted, low risk, no silent
|
|
1164
|
-
// regression). We deliberately DO NOT auto-promote any grove to 'team':
|
|
1165
|
-
// federation is the actual team-exposure axis, so it is ALWAYS an explicit
|
|
1166
|
-
// opt-in (via `wyrm grove policy <proj> team`). This guarantees a grove that
|
|
1167
|
-
// merely had an is_shared row in the past is never silently moved off
|
|
1168
|
-
// 'private' on upgrade. Everything not cloud-eligible stays 'private'.
|
|
1169
|
-
const hasCol = (t, c) => {
|
|
1170
|
-
try {
|
|
1171
|
-
return db.prepare(`PRAGMA table_info(${t})`).all().some((x) => x.name === c);
|
|
1172
|
-
}
|
|
1173
|
-
catch {
|
|
1174
|
-
return false;
|
|
1175
|
-
}
|
|
1176
|
-
};
|
|
1177
|
-
const distinctPids = (sql) => {
|
|
1178
|
-
try {
|
|
1179
|
-
return db.prepare(sql).all().map((r) => r.pid);
|
|
1180
|
-
}
|
|
1181
|
-
catch {
|
|
1182
|
-
return [];
|
|
1183
|
-
}
|
|
1184
|
-
};
|
|
1185
|
-
const cloudIds = new Set();
|
|
1186
|
-
for (const t of ['ground_truths', 'memory_artifacts', 'quests', 'design_tokens', 'design_references']) {
|
|
1187
|
-
if (!hasCol(t, 'cross_project_visibility') || !hasCol(t, 'project_id'))
|
|
1188
|
-
continue;
|
|
1189
|
-
for (const pid of distinctPids(`SELECT DISTINCT project_id AS pid FROM ${t}
|
|
1190
|
-
WHERE cross_project_visibility IN ('org', 'public') AND project_id IS NOT NULL`))
|
|
1191
|
-
cloudIds.add(pid);
|
|
1192
|
-
}
|
|
1193
|
-
const setCloud = db.prepare(`UPDATE projects SET sync_policy = 'cloud' WHERE id = ? AND sync_policy = 'private'`);
|
|
1194
|
-
for (const id of cloudIds)
|
|
1195
|
-
setCloud.run(id);
|
|
1196
|
-
},
|
|
1197
|
-
},
|
|
1198
|
-
{
|
|
1199
|
-
version: 20,
|
|
1200
|
-
description: 'v7 F2 (T008) — run-native provenance: runs + run_agents tables, agent_id/run_id attribution on 9 tables, failure_patterns.quarantine_scope',
|
|
1201
|
-
up: (db) => {
|
|
1202
|
-
// ============================================================
|
|
1203
|
-
// v7 BROOD FR-1: run-native provenance core.
|
|
1204
|
-
// Additive + guarded (Article VI): new tables use IF NOT EXISTS, every
|
|
1205
|
-
// ALTER is PRAGMA-checked, and the only UPDATE is a one-time backfill
|
|
1206
|
-
// INSIDE its column guard. A v6.x DB opens under this migration with
|
|
1207
|
-
// 100% of its rows; nothing is renamed, dropped, or rewritten.
|
|
1208
|
-
// ============================================================
|
|
1209
|
-
// ---- runs: one row per orchestrated fleet run ----
|
|
1210
|
-
// run_id is a ULID (src/ulid.ts: 26-char Crockford base32, monotonic,
|
|
1211
|
-
// crypto-random) so run ids sort lexicographically in creation order.
|
|
1212
|
-
// parent_run_id nests runs (orchestrator spawning sub-orchestrators).
|
|
1213
|
-
// debrief_artifact_id links the run's debrief memory_artifact when the
|
|
1214
|
-
// orchestrator files one. No FK from run_agents.agent_id to
|
|
1215
|
-
// agent_presence: presence rows are TTL-pruned heartbeats, while run
|
|
1216
|
-
// membership is durable provenance that must outlive them.
|
|
1217
|
-
db.exec(`
|
|
932
|
+
`);const t=(o,a)=>{try{return e.prepare(`PRAGMA table_info(${o})`).all().some(r=>r.name===a)}catch{return!1}},s=o=>{try{return e.prepare(o).all().map(a=>a.pid)}catch{return[]}},i=new Set;for(const o of["ground_truths","memory_artifacts","quests","design_tokens","design_references"])if(!(!t(o,"cross_project_visibility")||!t(o,"project_id")))for(const a of s(`SELECT DISTINCT project_id AS pid FROM ${o}
|
|
933
|
+
WHERE cross_project_visibility IN ('org', 'public') AND project_id IS NOT NULL`))i.add(a);const T=e.prepare("UPDATE projects SET sync_policy = 'cloud' WHERE id = ? AND sync_policy = 'private'");for(const o of i)T.run(o)}},{version:20,description:"v7 F2 (T008) \u2014 run-native provenance: runs + run_agents tables, agent_id/run_id attribution on 9 tables, failure_patterns.quarantine_scope",up:e=>{e.exec(`
|
|
1218
934
|
CREATE TABLE IF NOT EXISTS runs (
|
|
1219
935
|
run_id TEXT PRIMARY KEY,
|
|
1220
936
|
parent_run_id TEXT,
|
|
@@ -1239,98 +955,14 @@ export const migrations = [
|
|
|
1239
955
|
FOREIGN KEY (run_id) REFERENCES runs(run_id) ON DELETE CASCADE
|
|
1240
956
|
);
|
|
1241
957
|
CREATE INDEX IF NOT EXISTS idx_run_agents_agent ON run_agents(agent_id);
|
|
1242
|
-
`);
|
|
1243
|
-
// ---- nullable agent_id/run_id attribution on the 9 provenance tables ----
|
|
1244
|
-
// Soft references on purpose (no FK to runs): attribution is provenance
|
|
1245
|
-
// metadata — pruning or never-creating a runs row must never block or
|
|
1246
|
-
// cascade-delete canonical memory rows.
|
|
1247
|
-
//
|
|
1248
|
-
// On `events`, attribution EXTENDS the existing `actor` column — NOT a
|
|
1249
|
-
// parallel concept. `actor` stays the human display identity exactly as
|
|
1250
|
-
// 6.x wrote it; `agent_id` (machine identity) and `run_id` land
|
|
1251
|
-
// alongside. Precedence at read sites that surface attribution
|
|
1252
|
-
// (src/attribution.ts owns this rule):
|
|
1253
|
-
// 1. agent_id — machine identity (v7 fleet writes)
|
|
1254
|
-
// 2. actor — display identity (6.x writes, human labels)
|
|
1255
|
-
// 3. 'legacy' — neither set (a pre-v7 row); read-time only, never
|
|
1256
|
-
// written back. audit_log.actor follows the same rule.
|
|
1257
|
-
//
|
|
1258
|
-
// quest_claims already has agent_id (NOT NULL, migration 9) — the guard
|
|
1259
|
-
// skips it and only run_id is added there. All other tables gain both.
|
|
1260
|
-
const hasCol = (t, c) => {
|
|
1261
|
-
try {
|
|
1262
|
-
return db.prepare(`PRAGMA table_info(${t})`).all()
|
|
1263
|
-
.some((x) => x.name === c);
|
|
1264
|
-
}
|
|
1265
|
-
catch {
|
|
1266
|
-
return false;
|
|
1267
|
-
}
|
|
1268
|
-
};
|
|
1269
|
-
const ATTRIBUTED_TABLES = [
|
|
1270
|
-
'memory_artifacts', 'failure_patterns', 'decision_edges',
|
|
1271
|
-
'ground_truths', 'quests', 'sessions', 'quest_claims',
|
|
1272
|
-
'events', 'audit_log',
|
|
1273
|
-
];
|
|
1274
|
-
for (const t of ATTRIBUTED_TABLES) {
|
|
1275
|
-
if (!hasCol(t, 'agent_id'))
|
|
1276
|
-
db.exec(`ALTER TABLE ${t} ADD COLUMN agent_id TEXT;`);
|
|
1277
|
-
if (!hasCol(t, 'run_id'))
|
|
1278
|
-
db.exec(`ALTER TABLE ${t} ADD COLUMN run_id TEXT;`);
|
|
1279
|
-
}
|
|
1280
|
-
// Partial indexes for the run-scoped hot paths (failure quarantine,
|
|
1281
|
-
// run-tagged event fan-out, debrief assembly). Partial so the 6.x
|
|
1282
|
-
// all-NULL bulk costs nothing.
|
|
1283
|
-
db.exec(`
|
|
958
|
+
`);const E=(s,i)=>{try{return e.prepare(`PRAGMA table_info(${s})`).all().some(T=>T.name===i)}catch{return!1}},t=["memory_artifacts","failure_patterns","decision_edges","ground_truths","quests","sessions","quest_claims","events","audit_log"];for(const s of t)E(s,"agent_id")||e.exec(`ALTER TABLE ${s} ADD COLUMN agent_id TEXT;`),E(s,"run_id")||e.exec(`ALTER TABLE ${s} ADD COLUMN run_id TEXT;`);e.exec(`
|
|
1284
959
|
CREATE INDEX IF NOT EXISTS idx_failure_run ON failure_patterns(run_id) WHERE run_id IS NOT NULL;
|
|
1285
960
|
CREATE INDEX IF NOT EXISTS idx_events_run ON events(run_id) WHERE run_id IS NOT NULL;
|
|
1286
961
|
CREATE INDEX IF NOT EXISTS idx_artifacts_run ON memory_artifacts(run_id) WHERE run_id IS NOT NULL;
|
|
1287
|
-
`)
|
|
1288
|
-
// ---- failure_patterns quarantine scope (run|project|global) ----
|
|
1289
|
-
// The v7 spec names this column `scope`, but failure_patterns.scope has
|
|
1290
|
-
// existed since migration 9 with DIFFERENT semantics: it is the signature
|
|
1291
|
-
// MATCH dimension ('file'|'symbol'|'command'|'prompt') baked into every
|
|
1292
|
-
// stored signature by FailurePatterns.signature(). Renaming or
|
|
1293
|
-
// repurposing it would be a destructive rewrite (Article VI) and corrupt
|
|
1294
|
-
// every existing signature, so the run|project|global QUARANTINE/
|
|
1295
|
-
// authority scope lands as `quarantine_scope`.
|
|
1296
|
-
//
|
|
1297
|
-
// Default 'project' preserves 6.x semantics: a 6.x failure recorded with
|
|
1298
|
-
// a project_id was authoritative for its whole project the moment it was
|
|
1299
|
-
// written (check() filters by project_id). The guarded one-time backfill
|
|
1300
|
-
// marks project_id-IS-NULL rows 'global' because 6.x check() matched
|
|
1301
|
-
// those rows from EVERY project (`OR project_id IS NULL`) — without it,
|
|
1302
|
-
// v7 quarantine enforcement would silently narrow their reach.
|
|
1303
|
-
if (!hasCol('failure_patterns', 'quarantine_scope')) {
|
|
1304
|
-
db.exec(`
|
|
962
|
+
`),E("failure_patterns","quarantine_scope")||(e.exec(`
|
|
1305
963
|
ALTER TABLE failure_patterns ADD COLUMN quarantine_scope TEXT NOT NULL DEFAULT 'project'
|
|
1306
964
|
CHECK (quarantine_scope IN ('run', 'project', 'global'));
|
|
1307
|
-
`);
|
|
1308
|
-
db.exec(`UPDATE failure_patterns SET quarantine_scope = 'global' WHERE project_id IS NULL;`);
|
|
1309
|
-
}
|
|
1310
|
-
db.exec(`CREATE INDEX IF NOT EXISTS idx_failure_quarantine ON failure_patterns(quarantine_scope, resolved);`);
|
|
1311
|
-
},
|
|
1312
|
-
},
|
|
1313
|
-
{
|
|
1314
|
-
version: 21,
|
|
1315
|
-
description: 'v7 F2 (T015) — failure_confirmations: distinct-agent confirmation ledger behind run-quarantine auto-promotion',
|
|
1316
|
-
up: (db) => {
|
|
1317
|
-
// ============================================================
|
|
1318
|
-
// v7 BROOD FR-2 (T015): run-scoped failure quarantine promotion.
|
|
1319
|
-
// One row per (failure, agent) that recorded/confirmed the same failure
|
|
1320
|
-
// signature — the evidence ledger behind the debrief-INDEPENDENT
|
|
1321
|
-
// "≥2 distinct agent_id confirmations → promote to project scope" rule
|
|
1322
|
-
// (src/failure-patterns.ts noteConfirmation()).
|
|
1323
|
-
//
|
|
1324
|
-
// Additive + guarded (Article VI): IF NOT EXISTS only — re-running this
|
|
1325
|
-
// up() is a no-op and a v6.x DB opens with 100% of its rows untouched.
|
|
1326
|
-
//
|
|
1327
|
-
// PK (failure_id, agent_id) makes COUNT(*) per failure_id the DISTINCT
|
|
1328
|
-
// agent count by construction: the same agent re-recording N times is
|
|
1329
|
-
// ONE confirmation. run_id records where a confirmation came from (T017
|
|
1330
|
-
// analytics input); it is NOT part of the identity. The FK cascades with
|
|
1331
|
-
// the failure row — confirmations are evidence ABOUT a canonical row,
|
|
1332
|
-
// never canonical memory themselves (a delete() must not orphan them).
|
|
1333
|
-
db.exec(`
|
|
965
|
+
`),e.exec("UPDATE failure_patterns SET quarantine_scope = 'global' WHERE project_id IS NULL;")),e.exec("CREATE INDEX IF NOT EXISTS idx_failure_quarantine ON failure_patterns(quarantine_scope, resolved);")}},{version:21,description:"v7 F2 (T015) \u2014 failure_confirmations: distinct-agent confirmation ledger behind run-quarantine auto-promotion",up:e=>{e.exec(`
|
|
1334
966
|
CREATE TABLE IF NOT EXISTS failure_confirmations (
|
|
1335
967
|
failure_id INTEGER NOT NULL,
|
|
1336
968
|
agent_id TEXT NOT NULL,
|
|
@@ -1339,42 +971,7 @@ export const migrations = [
|
|
|
1339
971
|
PRIMARY KEY (failure_id, agent_id),
|
|
1340
972
|
FOREIGN KEY (failure_id) REFERENCES failure_patterns(id) ON DELETE CASCADE
|
|
1341
973
|
);
|
|
1342
|
-
`)
|
|
1343
|
-
},
|
|
1344
|
-
},
|
|
1345
|
-
{
|
|
1346
|
-
version: 22,
|
|
1347
|
-
description: 'v7 F2 (T017) — failure_blocks: prevented-repeat ledger behind wyrm_stats view=failures',
|
|
1348
|
-
up: (db) => {
|
|
1349
|
-
// ============================================================
|
|
1350
|
-
// v7 BROOD FR-2 (T017): prevented-repeat analytics.
|
|
1351
|
-
// One row per BLOCKED failure_check verdict (blocked:true) — the unit of
|
|
1352
|
-
// "a repeat was prevented". Attribution columns name the CHECKING
|
|
1353
|
-
// agent/run (the agent that was about to repeat the failure), NOT the
|
|
1354
|
-
// recorder — failure_patterns.agent_id/run_id already name the recorder.
|
|
1355
|
-
// failure_id is the verdict's top blocker (matches[0]).
|
|
1356
|
-
//
|
|
1357
|
-
// STORAGE CHOICE, documented (the task offered counter-table vs
|
|
1358
|
-
// events-derived):
|
|
1359
|
-
// - NOT events-derived: emitEvent is failure-isolated best-effort AND
|
|
1360
|
-
// the events log is retention-pruned (WYRM_EVENT_RETAIN_DAYS /
|
|
1361
|
-
// maxPerProject in wyrm_maintenance) — analytics derived from it
|
|
1362
|
-
// would silently undercount over time (same rationale that kept the
|
|
1363
|
-
// T015 confirmation ledger off the events log). failure_check is
|
|
1364
|
-
// also a READ that emits no events today; emitting one per check
|
|
1365
|
-
// would bloat the private event stream for one integer of analytics.
|
|
1366
|
-
// - An APPEND-ONLY ledger (not an UPSERTed counter row): one INSERT
|
|
1367
|
-
// per blocked verdict is exactly correct under multi-process fleet
|
|
1368
|
-
// concurrency with no read-modify-write race, and read-time
|
|
1369
|
-
// COUNT/GROUP BY is deterministic (Article III: pure SQL, zero LLM).
|
|
1370
|
-
// - Private by construction: this is not an events surface — block
|
|
1371
|
-
// rows never ride SSE/replication (failures stay is_shared=0).
|
|
1372
|
-
//
|
|
1373
|
-
// Additive + guarded (Article VI): IF NOT EXISTS only — re-running this
|
|
1374
|
-
// up() is a no-op and a v6.x DB opens with 100% of its rows untouched.
|
|
1375
|
-
// FK cascades with the failure row (the migration-21 precedent): blocks
|
|
1376
|
-
// are analytics ABOUT a canonical row, never canonical memory.
|
|
1377
|
-
db.exec(`
|
|
974
|
+
`)}},{version:22,description:"v7 F2 (T017) \u2014 failure_blocks: prevented-repeat ledger behind wyrm_stats view=failures",up:e=>{e.exec(`
|
|
1378
975
|
CREATE TABLE IF NOT EXISTS failure_blocks (
|
|
1379
976
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1380
977
|
failure_id INTEGER NOT NULL,
|
|
@@ -1384,41 +981,7 @@ export const migrations = [
|
|
|
1384
981
|
blocked_at TEXT DEFAULT (datetime('now')),
|
|
1385
982
|
FOREIGN KEY (failure_id) REFERENCES failure_patterns(id) ON DELETE CASCADE
|
|
1386
983
|
);
|
|
1387
|
-
`);
|
|
1388
|
-
// "N repeats blocked this run" is the hot read — partial index in the
|
|
1389
|
-
// style of migration 20's idx_failure_run/idx_events_run.
|
|
1390
|
-
db.exec(`CREATE INDEX IF NOT EXISTS idx_failure_blocks_run ON failure_blocks(run_id) WHERE run_id IS NOT NULL;`);
|
|
1391
|
-
db.exec(`CREATE INDEX IF NOT EXISTS idx_failure_blocks_failure ON failure_blocks(failure_id);`);
|
|
1392
|
-
},
|
|
1393
|
-
},
|
|
1394
|
-
{
|
|
1395
|
-
version: 23,
|
|
1396
|
-
description: 'v7 F3 (T028) — run_briefs: byte-stable fleet session_prime brief cache, one row per (run_id, role)',
|
|
1397
|
-
up: (db) => {
|
|
1398
|
-
// ============================================================
|
|
1399
|
-
// v7 BROOD FR-5 (T028): fleet-mode session_prime.
|
|
1400
|
-
// The FIRST prime of a (run_id, role) compiles the role-sliced brief
|
|
1401
|
-
// and CAS-inserts it here (INSERT OR IGNORE on the PK, then every
|
|
1402
|
-
// caller — winner and losers alike — reads the row back), so 12
|
|
1403
|
-
// simultaneous primes across 12 PROCESSES return the byte-identical
|
|
1404
|
-
// cached prefix (spec §7 criterion 8). An in-memory cache cannot give
|
|
1405
|
-
// that guarantee: fleet agents are separate stdio server processes
|
|
1406
|
-
// sharing only this SQLite file.
|
|
1407
|
-
//
|
|
1408
|
-
// body_json is the full structured session_prime body (the T026
|
|
1409
|
-
// sections[] shape) — the text channel re-derives from it through the
|
|
1410
|
-
// T019 renderer, so text and structuredContent cannot drift.
|
|
1411
|
-
//
|
|
1412
|
-
// run_id is a SOFT reference to runs (no FK), the migration-20
|
|
1413
|
-
// precedent: a prime may legitimately race `wyrm_run action=start`
|
|
1414
|
-
// (the F2 "orchestrator already exported WYRM_RUN_ID" orphan shape),
|
|
1415
|
-
// and a cache row must never block or cascade with run lifecycle.
|
|
1416
|
-
// Retention: wyrm_maintenance prunes by created_at (T029, same
|
|
1417
|
-
// WYRM_EVENT_RETAIN_DAYS knob as the event log).
|
|
1418
|
-
//
|
|
1419
|
-
// Additive + guarded (Article VI): IF NOT EXISTS only — re-running
|
|
1420
|
-
// this up() is a no-op and a v6.x DB opens with 100% of its rows.
|
|
1421
|
-
db.exec(`
|
|
984
|
+
`),e.exec("CREATE INDEX IF NOT EXISTS idx_failure_blocks_run ON failure_blocks(run_id) WHERE run_id IS NOT NULL;"),e.exec("CREATE INDEX IF NOT EXISTS idx_failure_blocks_failure ON failure_blocks(failure_id);")}},{version:23,description:"v7 F3 (T028) \u2014 run_briefs: byte-stable fleet session_prime brief cache, one row per (run_id, role)",up:e=>{e.exec(`
|
|
1422
985
|
CREATE TABLE IF NOT EXISTS run_briefs (
|
|
1423
986
|
run_id TEXT NOT NULL,
|
|
1424
987
|
role TEXT NOT NULL DEFAULT '',
|
|
@@ -1427,198 +990,20 @@ export const migrations = [
|
|
|
1427
990
|
created_at TEXT DEFAULT (datetime('now')),
|
|
1428
991
|
PRIMARY KEY (run_id, role)
|
|
1429
992
|
);
|
|
1430
|
-
`);
|
|
1431
|
-
},
|
|
1432
|
-
},
|
|
1433
|
-
{
|
|
1434
|
-
version: 24,
|
|
1435
|
-
description: 'v7 F3 (T029) — claims/presence hardening: role on quest_claims, run_id/role on agent_presence, run-scoped partial indexes',
|
|
1436
|
-
up: (db) => {
|
|
1437
|
-
// ============================================================
|
|
1438
|
-
// v7 BROOD FR-5 (T029): claims/presence run attribution completed.
|
|
1439
|
-
// quest_claims got run_id in migration 20; `role` lands here so an
|
|
1440
|
-
// orchestrator auditing the claim board sees WHICH fleet role holds
|
|
1441
|
-
// each lease. agent_presence persists rows (TTL heartbeats, table
|
|
1442
|
-
// since migration 9) so it gains BOTH run_id and role — presence
|
|
1443
|
-
// dashboards become per-run. All three are stamped at write time from
|
|
1444
|
-
// the ambient actor envelope + run_agents membership (presence.ts);
|
|
1445
|
-
// NULL outside a run (the migration-20 soft-ref rule: never an FK —
|
|
1446
|
-
// attribution metadata must never block or cascade canonical rows).
|
|
1447
|
-
//
|
|
1448
|
-
// Claims stay atomic via the existing PK-conflict CAS — this migration
|
|
1449
|
-
// adds attribution columns only, no locking changes (the spec is
|
|
1450
|
-
// explicit: there is no race to fix).
|
|
1451
|
-
//
|
|
1452
|
-
// Additive + guarded (Article VI): PRAGMA-checked ALTERs, IF NOT
|
|
1453
|
-
// EXISTS indexes — a v6.x DB opens with 100% of its rows; pre-v7 rows
|
|
1454
|
-
// carry NULL and read as 'legacy' at display time only.
|
|
1455
|
-
const hasCol = (t, c) => {
|
|
1456
|
-
try {
|
|
1457
|
-
return db.prepare(`PRAGMA table_info(${t})`).all()
|
|
1458
|
-
.some((x) => x.name === c);
|
|
1459
|
-
}
|
|
1460
|
-
catch {
|
|
1461
|
-
return false;
|
|
1462
|
-
}
|
|
1463
|
-
};
|
|
1464
|
-
if (!hasCol('quest_claims', 'role'))
|
|
1465
|
-
db.exec(`ALTER TABLE quest_claims ADD COLUMN role TEXT;`);
|
|
1466
|
-
if (!hasCol('agent_presence', 'run_id'))
|
|
1467
|
-
db.exec(`ALTER TABLE agent_presence ADD COLUMN run_id TEXT;`);
|
|
1468
|
-
if (!hasCol('agent_presence', 'role'))
|
|
1469
|
-
db.exec(`ALTER TABLE agent_presence ADD COLUMN role TEXT;`);
|
|
1470
|
-
// Partial indexes in the migration-20 style: the all-NULL 6.x bulk
|
|
1471
|
-
// costs nothing; wyrm_run status + per-run presence views are the
|
|
1472
|
-
// hot reads.
|
|
1473
|
-
db.exec(`
|
|
993
|
+
`)}},{version:24,description:"v7 F3 (T029) \u2014 claims/presence hardening: role on quest_claims, run_id/role on agent_presence, run-scoped partial indexes",up:e=>{const E=(t,s)=>{try{return e.prepare(`PRAGMA table_info(${t})`).all().some(i=>i.name===s)}catch{return!1}};E("quest_claims","role")||e.exec("ALTER TABLE quest_claims ADD COLUMN role TEXT;"),E("agent_presence","run_id")||e.exec("ALTER TABLE agent_presence ADD COLUMN run_id TEXT;"),E("agent_presence","role")||e.exec("ALTER TABLE agent_presence ADD COLUMN role TEXT;"),e.exec(`
|
|
1474
994
|
CREATE INDEX IF NOT EXISTS idx_quest_claims_run ON quest_claims(run_id) WHERE run_id IS NOT NULL;
|
|
1475
995
|
CREATE INDEX IF NOT EXISTS idx_presence_run ON agent_presence(run_id) WHERE run_id IS NOT NULL;
|
|
1476
|
-
`);
|
|
1477
|
-
},
|
|
1478
|
-
},
|
|
1479
|
-
{
|
|
1480
|
-
version: 25,
|
|
1481
|
-
description: 'Skills portability — store SKILL.md content (+ sha/ts) in the registry and make skills cloud-sync-eligible behind the per-row visibility gate',
|
|
1482
|
-
up: (db) => {
|
|
1483
|
-
// ============================================================
|
|
1484
|
-
// SKILLS CLOUD SYNC: carry skill BODIES across machines.
|
|
1485
|
-
// The `skills` table has only ever held metadata + a `skill_path`
|
|
1486
|
-
// pointer into ~/.copilot/skills/<slug>/SKILL.md — a pointer that is
|
|
1487
|
-
// meaningless on another machine. To make a registered skill portable
|
|
1488
|
-
// (this Fedora box → a Mac mini) we store the SKILL.md text itself.
|
|
1489
|
-
//
|
|
1490
|
-
// Additive + guarded (Article VI), the migration 20/24 style: every
|
|
1491
|
-
// ALTER is PRAGMA-checked, nothing is renamed/dropped/rewritten, and a
|
|
1492
|
-
// v7.0.1 DB opens under this migration with 100% of its rows preserved.
|
|
1493
|
-
//
|
|
1494
|
-
// content — the full SKILL.md text (NULL until backfilled;
|
|
1495
|
-
// a skill whose file is unreadable stays NULL).
|
|
1496
|
-
// content_sha256 — sha256 of `content`, for idempotent backfill
|
|
1497
|
-
// (skip unchanged) and export dedup.
|
|
1498
|
-
// content_updated_at — when `content` last changed (sync/merge aid).
|
|
1499
|
-
//
|
|
1500
|
-
// CRITICAL — FTS: skills_fts is an EXTERNAL-CONTENT FTS5 over
|
|
1501
|
-
// (name, description, tags) ONLY (content='skills'). Adding plain base
|
|
1502
|
-
// columns does NOT change the FTS table's indexed columns, so the
|
|
1503
|
-
// external-content contract and its insert/delete/update triggers are
|
|
1504
|
-
// untouched. (Verified by the post-migration FTS search test.)
|
|
1505
|
-
const hasCol = (t, c) => {
|
|
1506
|
-
try {
|
|
1507
|
-
return db.prepare(`PRAGMA table_info(${t})`).all()
|
|
1508
|
-
.some((x) => x.name === c);
|
|
1509
|
-
}
|
|
1510
|
-
catch {
|
|
1511
|
-
return false;
|
|
1512
|
-
}
|
|
1513
|
-
};
|
|
1514
|
-
if (!hasCol('skills', 'content'))
|
|
1515
|
-
db.exec(`ALTER TABLE skills ADD COLUMN content TEXT;`);
|
|
1516
|
-
if (!hasCol('skills', 'content_sha256'))
|
|
1517
|
-
db.exec(`ALTER TABLE skills ADD COLUMN content_sha256 TEXT;`);
|
|
1518
|
-
if (!hasCol('skills', 'content_updated_at'))
|
|
1519
|
-
db.exec(`ALTER TABLE skills ADD COLUMN content_updated_at TEXT;`);
|
|
1520
|
-
// ---- cloud-sync eligibility, private-by-default (Article I/IX) ----
|
|
1521
|
-
// Skills are global (no project_id), so the grove (project) sync_policy
|
|
1522
|
-
// gate cannot apply per-skill. We instead make skills sync the SAME way
|
|
1523
|
-
// ground_truths/memory_artifacts do: a per-row `cross_project_visibility`
|
|
1524
|
-
// marker, DEFAULT 'within' (operator-private). The sync engine's visClause
|
|
1525
|
-
// (`cross_project_visibility IN ('org','public')`) is the egress gate —
|
|
1526
|
-
// a brand-new skills column defaulting to 'within' means NO skill leaves
|
|
1527
|
-
// this machine until the operator explicitly promotes it. `is_shared`
|
|
1528
|
-
// (default 0) lands too so the team-federation gate has its flag.
|
|
1529
|
-
// This adds skills to the existing privacy lattice without weakening the
|
|
1530
|
-
// default, exactly per the constitution's private-by-default rule.
|
|
1531
|
-
if (!hasCol('skills', 'cross_project_visibility')) {
|
|
1532
|
-
db.exec(`ALTER TABLE skills ADD COLUMN cross_project_visibility TEXT NOT NULL DEFAULT 'within';`);
|
|
1533
|
-
}
|
|
1534
|
-
if (!hasCol('skills', 'is_shared')) {
|
|
1535
|
-
db.exec(`ALTER TABLE skills ADD COLUMN is_shared INTEGER NOT NULL DEFAULT 0;`);
|
|
1536
|
-
}
|
|
1537
|
-
// Partial index for the egress hot path (collectChangedRows filters on
|
|
1538
|
-
// promoted rows), migration 20/24 style — the all-'within' default bulk
|
|
1539
|
-
// costs nothing.
|
|
1540
|
-
db.exec(`CREATE INDEX IF NOT EXISTS idx_skills_visibility ON skills(cross_project_visibility) WHERE cross_project_visibility != 'within';`);
|
|
1541
|
-
},
|
|
1542
|
-
},
|
|
1543
|
-
{
|
|
1544
|
-
version: 26,
|
|
1545
|
-
description: 'Reverse-bridge rejection tombstones — remember a once-rejected outside-prose sig so the next sweep never re-queues content the operator already deleted',
|
|
1546
|
-
up: (db) => {
|
|
1547
|
-
// ============================================================
|
|
1548
|
-
// REVERSE-BRIDGE REJECTION MEMORY (security pass #2, finding #1).
|
|
1549
|
-
//
|
|
1550
|
-
// detectEdits() always re-emits the operator prose OUTSIDE the Wyrm
|
|
1551
|
-
// markers as an 'outside' edit on EVERY sweep — there is no diff against
|
|
1552
|
-
// a prior state. Re-queueing is suppressed only by existsBySig() against
|
|
1553
|
-
// the live review queue. So if the operator REVIEWS and REJECTS (deletes)
|
|
1554
|
-
// a reverse-bridge candidate, the sig disappears from the DB and the next
|
|
1555
|
-
// sweep re-extracts + re-queues the identical prose — a review-queue loop
|
|
1556
|
-
// for content the operator already said no to.
|
|
1557
|
-
//
|
|
1558
|
-
// Fix: a tombstone of rejected `rb:` sigs. wyrm_review's reject path
|
|
1559
|
-
// records the sig here; makeBridgeDeps.existsBySig() additionally consults
|
|
1560
|
-
// it, so a once-rejected outside-prose edit is remembered as rejected and
|
|
1561
|
-
// never re-offered. Purely local, per-project, never replicated.
|
|
1562
|
-
//
|
|
1563
|
-
// Additive + guarded (Article VI), migration 20/24/25 style: a brand-new
|
|
1564
|
-
// table, nothing renamed/dropped, a v7.0.x DB opens with 100% of its rows.
|
|
1565
|
-
db.exec(`
|
|
996
|
+
`)}},{version:25,description:"Skills portability \u2014 store SKILL.md content (+ sha/ts) in the registry and make skills cloud-sync-eligible behind the per-row visibility gate",up:e=>{const E=(t,s)=>{try{return e.prepare(`PRAGMA table_info(${t})`).all().some(i=>i.name===s)}catch{return!1}};E("skills","content")||e.exec("ALTER TABLE skills ADD COLUMN content TEXT;"),E("skills","content_sha256")||e.exec("ALTER TABLE skills ADD COLUMN content_sha256 TEXT;"),E("skills","content_updated_at")||e.exec("ALTER TABLE skills ADD COLUMN content_updated_at TEXT;"),E("skills","cross_project_visibility")||e.exec("ALTER TABLE skills ADD COLUMN cross_project_visibility TEXT NOT NULL DEFAULT 'within';"),E("skills","is_shared")||e.exec("ALTER TABLE skills ADD COLUMN is_shared INTEGER NOT NULL DEFAULT 0;"),e.exec("CREATE INDEX IF NOT EXISTS idx_skills_visibility ON skills(cross_project_visibility) WHERE cross_project_visibility != 'within';")}},{version:26,description:"Reverse-bridge rejection tombstones \u2014 remember a once-rejected outside-prose sig so the next sweep never re-queues content the operator already deleted",up:e=>{e.exec(`
|
|
1566
997
|
CREATE TABLE IF NOT EXISTS reverse_bridge_tombstones (
|
|
1567
998
|
project_id INTEGER NOT NULL,
|
|
1568
999
|
sig TEXT NOT NULL,
|
|
1569
1000
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1570
1001
|
PRIMARY KEY (project_id, sig)
|
|
1571
1002
|
);
|
|
1572
|
-
`);
|
|
1573
|
-
},
|
|
1574
|
-
},
|
|
1575
|
-
];
|
|
1576
|
-
/**
|
|
1577
|
-
* Run all pending migrations on the database.
|
|
1578
|
-
* Creates the schema_versions table if it doesn't exist.
|
|
1579
|
-
* Returns the list of migrations that were applied.
|
|
1580
|
-
*/
|
|
1581
|
-
export function runMigrations(db) {
|
|
1582
|
-
// Create the version tracking table (always safe to re-run)
|
|
1583
|
-
db.exec(`
|
|
1003
|
+
`)}}];function d(e){e.exec(`
|
|
1584
1004
|
CREATE TABLE IF NOT EXISTS schema_versions (
|
|
1585
1005
|
version INTEGER PRIMARY KEY,
|
|
1586
1006
|
description TEXT NOT NULL,
|
|
1587
1007
|
applied_at TEXT DEFAULT (datetime('now'))
|
|
1588
1008
|
);
|
|
1589
|
-
`);
|
|
1590
|
-
const currentVersion = db.prepare('SELECT COALESCE(MAX(version), 0) as v FROM schema_versions').get().v;
|
|
1591
|
-
// Always apply in ascending version order, regardless of array order in
|
|
1592
|
-
// `migrations` (defensive — v3.9 added a migration that was authored out
|
|
1593
|
-
// of array order). Sorted copy so we don't mutate the exported array.
|
|
1594
|
-
const pending = migrations
|
|
1595
|
-
.filter(m => m.version > currentVersion)
|
|
1596
|
-
.slice()
|
|
1597
|
-
.sort((a, b) => a.version - b.version);
|
|
1598
|
-
if (pending.length === 0)
|
|
1599
|
-
return [];
|
|
1600
|
-
const applied = [];
|
|
1601
|
-
const insertVersion = db.prepare('INSERT INTO schema_versions (version, description) VALUES (?, ?)');
|
|
1602
|
-
for (const migration of pending) {
|
|
1603
|
-
// Each migration runs in its own transaction for atomicity
|
|
1604
|
-
const runOne = db.transaction(() => {
|
|
1605
|
-
migration.up(db);
|
|
1606
|
-
insertVersion.run(migration.version, migration.description);
|
|
1607
|
-
});
|
|
1608
|
-
runOne();
|
|
1609
|
-
applied.push(migration);
|
|
1610
|
-
}
|
|
1611
|
-
return applied;
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Get the current schema version.
|
|
1615
|
-
*/
|
|
1616
|
-
export function getSchemaVersion(db) {
|
|
1617
|
-
try {
|
|
1618
|
-
return db.prepare('SELECT COALESCE(MAX(version), 0) as v FROM schema_versions').get().v;
|
|
1619
|
-
}
|
|
1620
|
-
catch {
|
|
1621
|
-
return 0; // schema_versions table doesn't exist yet
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
//# sourceMappingURL=migrations.js.map
|
|
1009
|
+
`);const E=e.prepare("SELECT COALESCE(MAX(version), 0) as v FROM schema_versions").get().v,t=n.filter(T=>T.version>E).slice().sort((T,o)=>T.version-o.version);if(t.length===0)return[];const s=[],i=e.prepare("INSERT INTO schema_versions (version, description) VALUES (?, ?)");for(const T of t)e.transaction(()=>{T.up(e),i.run(T.version,T.description)})(),s.push(T);return s}function N(e){try{return e.prepare("SELECT COALESCE(MAX(version), 0) as v FROM schema_versions").get().v}catch{return 0}}export{N as getSchemaVersion,n as migrations,d as runMigrations};
|