@brainst0rm/cli 0.14.1 → 0.14.3

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 (103) hide show
  1. package/dist/{App-DSD2B5RV.js → App-4GFCMEPX.js} +10 -10
  2. package/dist/{App-6WBAUX35.js → App-KTUPXXQM.js} +10 -10
  3. package/dist/{agent-D5GTWGPD.js → agent-EYT6BPVT.js} +2 -2
  4. package/dist/{agent-DOR4KPUH.js → agent-KTWFCMY5.js} +2 -2
  5. package/dist/brainstorm.js +56 -56
  6. package/dist/{chunk-RV3CJQGC.js → chunk-2AXI2OPK.js} +43 -7
  7. package/dist/chunk-2AXI2OPK.js.map +1 -0
  8. package/dist/{chunk-6G2HA63H.js → chunk-3AYD5ONW.js} +43 -7
  9. package/dist/chunk-3AYD5ONW.js.map +1 -0
  10. package/dist/{chunk-UPP3TOCP.js → chunk-3RH5MKZF.js} +5 -5
  11. package/dist/{chunk-GLWCTGWG.js → chunk-4NNXZFPX.js} +2 -2
  12. package/dist/{chunk-Z5QQ5OGV.js → chunk-52Q6CE4Y.js} +19 -2
  13. package/dist/chunk-52Q6CE4Y.js.map +1 -0
  14. package/dist/{chunk-FGYEICUI.js → chunk-6UFDBLUX.js} +36 -4
  15. package/dist/chunk-6UFDBLUX.js.map +1 -0
  16. package/dist/{chunk-NVA62I52.js → chunk-6WGHIUWX.js} +2 -2
  17. package/dist/{chunk-4LR7LQPG.js → chunk-CZILJ33T.js} +2 -2
  18. package/dist/{chunk-TLQVDPEQ.js → chunk-DJ7WG6GZ.js} +19 -2
  19. package/dist/chunk-DJ7WG6GZ.js.map +1 -0
  20. package/dist/{chunk-N7JKT44Y.js → chunk-GUHXB5DX.js} +2 -2
  21. package/dist/{chunk-SCGV333Z.js → chunk-HKRUCBMI.js} +4 -4
  22. package/dist/{chunk-PIT7VD46.js → chunk-N5V2PGPN.js} +4 -4
  23. package/dist/{chunk-GF5TKYDA.js → chunk-RVQOLZR6.js} +14 -14
  24. package/dist/{chunk-AH5SFL5J.js → chunk-RVXUVX5W.js} +14 -14
  25. package/dist/{chunk-4PCWPRRN.js → chunk-WRO5TVID.js} +5 -5
  26. package/dist/{chunk-ZYGUHAHM.js → chunk-Z5RDHTWQ.js} +36 -4
  27. package/dist/chunk-Z5RDHTWQ.js.map +1 -0
  28. package/dist/{dist-QUYR4VH7.js → dist-5NJP3JHL.js} +23 -3
  29. package/dist/{dist-QUYR4VH7.js.map → dist-5NJP3JHL.js.map} +1 -1
  30. package/dist/{dist-P6IZYZBM.js → dist-7AIEUUFF.js} +2 -2
  31. package/dist/{dist-RVTIEEXC.js → dist-DCJYPRUZ.js} +3 -3
  32. package/dist/{dist-PGQ4UIM4.js → dist-DXQQF55Y.js} +3 -3
  33. package/dist/{dist-JQXY4E6A.js → dist-KSUHKNET.js} +88 -15
  34. package/dist/dist-KSUHKNET.js.map +1 -0
  35. package/dist/{dist-TS5U3BDK.js → dist-LCPM5BXN.js} +5 -5
  36. package/dist/{dist-VB7CXEYB.js → dist-MKAADC4H.js} +5 -5
  37. package/dist/{dist-MKWOTCNR.js → dist-NQQPQGZU.js} +6 -6
  38. package/dist/{dist-VECPW2NV.js → dist-NTQ7LFRW.js} +5 -5
  39. package/dist/{dist-ESUVKHL4.js → dist-QFVAD45U.js} +6 -6
  40. package/dist/{dist-7IRVYQYG.js → dist-RUBJT7FI.js} +2 -2
  41. package/dist/{dist-P2J4GXPC.js → dist-S7FLVLXS.js} +2 -2
  42. package/dist/{dist-4JONNOLT.js → dist-U3G5HT5D.js} +6 -6
  43. package/dist/{dist-IUVHFJV2.js → dist-UETKBS6A.js} +6 -6
  44. package/dist/{dist-ODBEXWTS.js → dist-WLMQD6I5.js} +5 -5
  45. package/dist/{dist-GNYSGXLR.js → dist-XFJ337R7.js} +23 -3
  46. package/dist/{dist-GNYSGXLR.js.map → dist-XFJ337R7.js.map} +1 -1
  47. package/dist/{dist-T6BIJZSD.js → dist-Y5FZXOVL.js} +2 -2
  48. package/dist/{dist-2DSARR2V.js → dist-ZPASHTYZ.js} +88 -15
  49. package/dist/dist-ZPASHTYZ.js.map +1 -0
  50. package/dist/{handler-VOVQRV5B.js → handler-CPXQZBSW.js} +6 -6
  51. package/dist/{handler-MHEFUP32.js → handler-FIBSROM4.js} +6 -6
  52. package/dist/index.js +56 -56
  53. package/dist/{mcp-server-C732TVIQ.js → mcp-server-56FVMXTC.js} +2 -2
  54. package/dist/{mcp-server-JUYR37EX.js → mcp-server-XXUZDYW6.js} +2 -2
  55. package/dist/{roles-QTZ54BOF.js → roles-CJTZSFFW.js} +6 -6
  56. package/dist/{roles-LDNPU3NI.js → roles-MVBHE5QW.js} +6 -6
  57. package/dist/{slash-VYIMEVPU.js → slash-WFDKT67A.js} +8 -8
  58. package/dist/{slash-VAUFJQBQ.js → slash-Y3E5KBOJ.js} +8 -8
  59. package/package.json +28 -28
  60. package/dist/chunk-6G2HA63H.js.map +0 -1
  61. package/dist/chunk-FGYEICUI.js.map +0 -1
  62. package/dist/chunk-RV3CJQGC.js.map +0 -1
  63. package/dist/chunk-TLQVDPEQ.js.map +0 -1
  64. package/dist/chunk-Z5QQ5OGV.js.map +0 -1
  65. package/dist/chunk-ZYGUHAHM.js.map +0 -1
  66. package/dist/dist-2DSARR2V.js.map +0 -1
  67. package/dist/dist-JQXY4E6A.js.map +0 -1
  68. /package/dist/{App-DSD2B5RV.js.map → App-4GFCMEPX.js.map} +0 -0
  69. /package/dist/{App-6WBAUX35.js.map → App-KTUPXXQM.js.map} +0 -0
  70. /package/dist/{agent-D5GTWGPD.js.map → agent-EYT6BPVT.js.map} +0 -0
  71. /package/dist/{agent-DOR4KPUH.js.map → agent-KTWFCMY5.js.map} +0 -0
  72. /package/dist/{chunk-UPP3TOCP.js.map → chunk-3RH5MKZF.js.map} +0 -0
  73. /package/dist/{chunk-GLWCTGWG.js.map → chunk-4NNXZFPX.js.map} +0 -0
  74. /package/dist/{chunk-NVA62I52.js.map → chunk-6WGHIUWX.js.map} +0 -0
  75. /package/dist/{chunk-4LR7LQPG.js.map → chunk-CZILJ33T.js.map} +0 -0
  76. /package/dist/{chunk-N7JKT44Y.js.map → chunk-GUHXB5DX.js.map} +0 -0
  77. /package/dist/{chunk-SCGV333Z.js.map → chunk-HKRUCBMI.js.map} +0 -0
  78. /package/dist/{chunk-PIT7VD46.js.map → chunk-N5V2PGPN.js.map} +0 -0
  79. /package/dist/{chunk-GF5TKYDA.js.map → chunk-RVQOLZR6.js.map} +0 -0
  80. /package/dist/{chunk-AH5SFL5J.js.map → chunk-RVXUVX5W.js.map} +0 -0
  81. /package/dist/{chunk-4PCWPRRN.js.map → chunk-WRO5TVID.js.map} +0 -0
  82. /package/dist/{dist-4JONNOLT.js.map → dist-7AIEUUFF.js.map} +0 -0
  83. /package/dist/{dist-7IRVYQYG.js.map → dist-DCJYPRUZ.js.map} +0 -0
  84. /package/dist/{dist-ESUVKHL4.js.map → dist-DXQQF55Y.js.map} +0 -0
  85. /package/dist/{dist-IUVHFJV2.js.map → dist-LCPM5BXN.js.map} +0 -0
  86. /package/dist/{dist-VB7CXEYB.js.map → dist-MKAADC4H.js.map} +0 -0
  87. /package/dist/{dist-MKWOTCNR.js.map → dist-NQQPQGZU.js.map} +0 -0
  88. /package/dist/{dist-P2J4GXPC.js.map → dist-NTQ7LFRW.js.map} +0 -0
  89. /package/dist/{dist-P6IZYZBM.js.map → dist-QFVAD45U.js.map} +0 -0
  90. /package/dist/{dist-PGQ4UIM4.js.map → dist-RUBJT7FI.js.map} +0 -0
  91. /package/dist/{dist-RVTIEEXC.js.map → dist-S7FLVLXS.js.map} +0 -0
  92. /package/dist/{dist-T6BIJZSD.js.map → dist-U3G5HT5D.js.map} +0 -0
  93. /package/dist/{dist-TS5U3BDK.js.map → dist-UETKBS6A.js.map} +0 -0
  94. /package/dist/{dist-ODBEXWTS.js.map → dist-WLMQD6I5.js.map} +0 -0
  95. /package/dist/{dist-VECPW2NV.js.map → dist-Y5FZXOVL.js.map} +0 -0
  96. /package/dist/{handler-VOVQRV5B.js.map → handler-CPXQZBSW.js.map} +0 -0
  97. /package/dist/{handler-MHEFUP32.js.map → handler-FIBSROM4.js.map} +0 -0
  98. /package/dist/{mcp-server-C732TVIQ.js.map → mcp-server-56FVMXTC.js.map} +0 -0
  99. /package/dist/{mcp-server-JUYR37EX.js.map → mcp-server-XXUZDYW6.js.map} +0 -0
  100. /package/dist/{roles-LDNPU3NI.js.map → roles-CJTZSFFW.js.map} +0 -0
  101. /package/dist/{roles-QTZ54BOF.js.map → roles-MVBHE5QW.js.map} +0 -0
  102. /package/dist/{slash-VAUFJQBQ.js.map → slash-WFDKT67A.js.map} +0 -0
  103. /package/dist/{slash-VYIMEVPU.js.map → slash-Y3E5KBOJ.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../db/src/client.ts","../../db/src/repositories.ts","../../db/src/team-repository.ts","../../db/src/compliance-repository.ts","../../db/src/routing-audit-repository.ts","../../db/src/routing-audit-writer.ts"],"sourcesContent":["import Database from \"better-sqlite3\";\nimport { mkdirSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createLogger } from \"@brainst0rm/shared\";\n\nconst log = createLogger(\"db.client\");\n\n/**\n * Where the brainstorm data directory lives. Defaults to `~/.brainstorm`,\n * but honors `BRAINSTORM_HOME` for test isolation — the live-backend\n * harness points each spec at its own tmpdir so tests can't collide\n * on the shared user DB. Production users never set this; the default\n * is the right one.\n */\nconst DB_DIR = process.env.BRAINSTORM_HOME\n ? join(process.env.BRAINSTORM_HOME)\n : join(homedir(), \".brainstorm\");\nconst DB_PATH = join(DB_DIR, \"brainstorm.db\");\n\nlet _db: Database.Database | null = null;\n\nexport function getDb(): Database.Database {\n if (_db) return _db;\n\n if (!existsSync(DB_DIR)) {\n mkdirSync(DB_DIR, { recursive: true });\n }\n\n _db = new Database(DB_PATH);\n _db.pragma(\"journal_mode = WAL\");\n _db.pragma(\"foreign_keys = ON\");\n // Retry for up to 5s on SQLITE_BUSY before returning an error.\n // v11 Chaos Monkey finding: pre-fix, no busy_timeout meant the\n // first concurrent write from a second window (desktop + CLI both\n // open, or two desktop windows) produced SQLITE_BUSY immediately\n // with no backoff. WAL mode serializes writes; busy_timeout lets\n // the loser of the race wait for the winner instead of erroring.\n _db.pragma(\"busy_timeout = 5000\");\n\n runMigrations(_db);\n cleanupOldRecords(_db);\n _db.pragma(\"optimize\");\n return _db;\n}\n\nexport function closeDb(): void {\n if (_db) {\n _db.close();\n _db = null;\n }\n}\n\nexport function getTestDb(): Database.Database {\n const db = new Database(\":memory:\");\n db.pragma(\"foreign_keys = ON\");\n runMigrations(db);\n return db;\n}\n\n/** Delete cost records and model performance data older than 90 days. */\nexport function cleanupOldRecords(db: Database.Database): void {\n const cutoff = Math.floor(Date.now() / 1000) - 90 * 24 * 60 * 60;\n // Each DELETE runs independently so a missing table (first run, before its\n // migration) does not stop later statements from running. Errors other\n // than \"no such table\" are logged so typos in column names surface\n // instead of silently disabling cleanup.\n const statements: Array<[string, string]> = [\n [\"cost_records\", \"DELETE FROM cost_records WHERE timestamp < ?\"],\n [\"model_performance\", \"DELETE FROM model_performance WHERE timestamp < ?\"],\n [\n \"model_performance_v2\",\n \"DELETE FROM model_performance_v2 WHERE timestamp < ?\",\n ],\n [\n \"messages\",\n \"DELETE FROM messages WHERE session_id IN (SELECT id FROM sessions WHERE created_at < ?)\",\n ],\n [\"sessions\", \"DELETE FROM sessions WHERE created_at < ?\"],\n [\"audit_log\", \"DELETE FROM audit_log WHERE created_at < ?\"],\n [\"compliance_events\", \"DELETE FROM compliance_events WHERE created_at < ?\"],\n ];\n for (const [table, sql] of statements) {\n try {\n db.prepare(sql).run(cutoff);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes(\"no such table\")) continue; // Pre-migration; expected.\n log.warn({ table, err: msg }, \"cleanupOldRecords statement failed\");\n }\n }\n}\n\n/**\n * Detects the \"newer DB + older CLI\" downgrade scenario.\n *\n * Migrations are forward-only by design. If a user runs 0.14 (which\n * applies new migrations to ~/.brainstorm/brainstorm.db), then\n * downgrades to 0.13, the older code sees a DB with rows in\n * _migrations it doesn't recognise. Older code reading the new\n * schema can silently mishandle NOT NULL columns it doesn't populate.\n *\n * Fail-fast at startup. Pointer to the rollback runbook.\n * Escape hatch via BRAINSTORM_DB_ALLOW_UNKNOWN_MIGRATIONS=1 for\n * operators restoring from a backup who know what they're doing.\n */\n/** @internal — exported for testing. Production code should not call this\n * directly; runMigrations() invokes it at the right moment. */\nexport function assertNoUnknownMigrations(\n db: Database.Database,\n knownNames: Set<string>,\n): void {\n const rows = db.prepare(\"SELECT name FROM _migrations\").all() as Array<{\n name: string;\n }>;\n const unknown = rows.map((r) => r.name).filter((n) => !knownNames.has(n));\n if (unknown.length === 0) return;\n\n if (process.env.BRAINSTORM_DB_ALLOW_UNKNOWN_MIGRATIONS === \"1\") {\n // eslint-disable-next-line no-console\n console.warn(\n `[db] BRAINSTORM_DB_ALLOW_UNKNOWN_MIGRATIONS=1 — proceeding with unknown migrations: ${unknown.join(\", \")}`,\n );\n return;\n }\n\n throw new Error(\n [\n \"Database has migrations this CLI doesn't recognise:\",\n ` ${unknown.join(\", \")}`,\n \"\",\n \"This usually means you downgraded the CLI from a newer version that\",\n \"wrote schema changes to ~/.brainstorm/brainstorm.db. Older code\",\n \"reading those tables can silently corrupt rows.\",\n \"\",\n \"Options:\",\n \" 1. Upgrade back to the version that wrote the migration\",\n \" (or any newer version that has it).\",\n \" 2. Restore the pre-upgrade DB backup; see\",\n \" docs/runbooks/rollback-published-version.md.\",\n \" 3. Set BRAINSTORM_DB_ALLOW_UNKNOWN_MIGRATIONS=1 to proceed anyway\",\n \" (safe only when you've verified the migration was additive and\",\n \" your workflow doesn't touch the affected tables).\",\n ].join(\"\\n\"),\n );\n}\n\nfunction runMigrations(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS _migrations (\n id INTEGER PRIMARY KEY,\n name TEXT NOT NULL,\n applied_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n `);\n\n const knownNames = new Set(MIGRATIONS.map((m) => m.name));\n assertNoUnknownMigrations(db, knownNames);\n\n const applied = new Set(\n db\n .prepare(\"SELECT name FROM _migrations\")\n .all()\n .map((r: any) => r.name),\n );\n\n for (const migration of MIGRATIONS) {\n if (applied.has(migration.name)) continue;\n try {\n db.exec(\"BEGIN\");\n db.exec(migration.sql);\n db.prepare(\"INSERT INTO _migrations (name) VALUES (?)\").run(\n migration.name,\n );\n db.exec(\"COMMIT\");\n } catch (err) {\n db.exec(\"ROLLBACK\");\n throw new Error(\n `Migration \"${migration.name}\" failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n}\n\nconst MIGRATIONS = [\n {\n name: \"001_sessions\",\n sql: `\n CREATE TABLE sessions (\n id TEXT PRIMARY KEY,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n project_path TEXT NOT NULL,\n total_cost REAL NOT NULL DEFAULT 0,\n message_count INTEGER NOT NULL DEFAULT 0\n );\n `,\n },\n {\n name: \"002_messages\",\n sql: `\n CREATE TABLE messages (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system', 'tool')),\n content TEXT NOT NULL,\n model_id TEXT,\n token_count INTEGER,\n timestamp INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX idx_messages_session ON messages(session_id);\n `,\n },\n {\n name: \"003_cost_records\",\n sql: `\n CREATE TABLE cost_records (\n id TEXT PRIMARY KEY,\n timestamp INTEGER NOT NULL DEFAULT (unixepoch()),\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n model_id TEXT NOT NULL,\n provider TEXT NOT NULL,\n input_tokens INTEGER NOT NULL,\n output_tokens INTEGER NOT NULL,\n cached_tokens INTEGER NOT NULL DEFAULT 0,\n cost REAL NOT NULL,\n task_type TEXT NOT NULL,\n project_path TEXT\n );\n CREATE INDEX idx_cost_session ON cost_records(session_id);\n CREATE INDEX idx_cost_timestamp ON cost_records(timestamp);\n `,\n },\n {\n name: \"004_model_performance\",\n sql: `\n CREATE TABLE model_performance (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n model_id TEXT NOT NULL,\n task_type TEXT NOT NULL,\n success INTEGER NOT NULL DEFAULT 1,\n latency_ms INTEGER,\n user_accepted INTEGER,\n timestamp INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX idx_perf_model ON model_performance(model_id);\n `,\n },\n {\n name: \"005_agent_profiles\",\n sql: `\n CREATE TABLE agent_profiles (\n id TEXT PRIMARY KEY,\n display_name TEXT NOT NULL,\n role TEXT NOT NULL CHECK (role IN ('architect', 'coder', 'reviewer', 'debugger', 'analyst', 'orchestrator', 'product-manager', 'security-reviewer', 'code-reviewer', 'style-reviewer', 'qa', 'compliance', 'devops', 'custom')),\n description TEXT NOT NULL DEFAULT '',\n model_id TEXT NOT NULL,\n system_prompt TEXT,\n allowed_tools TEXT NOT NULL DEFAULT '\"all\"',\n output_format TEXT,\n budget_per_workflow REAL,\n budget_daily REAL,\n exhaustion_action TEXT NOT NULL DEFAULT 'downgrade',\n downgrade_model_id TEXT,\n confidence_threshold REAL NOT NULL DEFAULT 0.7,\n max_steps INTEGER NOT NULL DEFAULT 10,\n fallback_chain TEXT NOT NULL DEFAULT '[]',\n guardrails TEXT NOT NULL DEFAULT '{}',\n lifecycle TEXT NOT NULL DEFAULT 'active' CHECK (lifecycle IN ('active', 'suspended')),\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n `,\n },\n {\n name: \"006_workflow_definitions\",\n sql: `\n CREATE TABLE workflow_definitions (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n steps_json TEXT NOT NULL,\n communication_mode TEXT NOT NULL DEFAULT 'handoff' CHECK (communication_mode IN ('handoff', 'shared', 'parallel')),\n max_iterations INTEGER NOT NULL DEFAULT 3,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n `,\n },\n {\n name: \"007_workflow_runs\",\n sql: `\n CREATE TABLE workflow_runs (\n id TEXT PRIMARY KEY,\n workflow_id TEXT NOT NULL,\n session_id TEXT REFERENCES sessions(id),\n description TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'paused', 'completed', 'failed', 'cancelled')),\n total_cost REAL NOT NULL DEFAULT 0,\n estimated_cost REAL NOT NULL DEFAULT 0,\n iteration INTEGER NOT NULL DEFAULT 0,\n max_iterations INTEGER NOT NULL DEFAULT 3,\n communication_mode TEXT NOT NULL DEFAULT 'handoff',\n continue_from_run_id TEXT,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX idx_workflow_runs_status ON workflow_runs(status);\n `,\n },\n {\n name: \"008_workflow_step_runs\",\n sql: `\n CREATE TABLE workflow_step_runs (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE,\n step_def_id TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'skipped')),\n artifact_json TEXT,\n error TEXT,\n cost REAL NOT NULL DEFAULT 0,\n iteration INTEGER NOT NULL DEFAULT 0,\n started_at INTEGER,\n completed_at INTEGER\n );\n CREATE INDEX idx_step_runs_run ON workflow_step_runs(run_id);\n `,\n },\n {\n name: \"009_model_performance_v2\",\n sql: `\n CREATE TABLE model_performance_v2 (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n model_id TEXT NOT NULL,\n task_type TEXT NOT NULL,\n shape_key TEXT,\n success INTEGER NOT NULL DEFAULT 1,\n latency_ms INTEGER,\n cost_usd REAL,\n validity_score REAL,\n quality_score REAL,\n input_tokens INTEGER,\n output_tokens INTEGER,\n user_accepted INTEGER,\n timestamp INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX idx_perf_v2_model_task ON model_performance_v2(model_id, task_type);\n CREATE INDEX idx_perf_v2_shape ON model_performance_v2(shape_key);\n CREATE INDEX idx_perf_v2_timestamp ON model_performance_v2(timestamp);\n `,\n },\n {\n name: \"010_session_patterns\",\n sql: `\n CREATE TABLE session_patterns (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_path TEXT NOT NULL,\n pattern_type TEXT NOT NULL CHECK (pattern_type IN ('tool_success', 'command_timing', 'user_preference', 'model_choice')),\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.5,\n occurrences INTEGER NOT NULL DEFAULT 1,\n last_seen INTEGER NOT NULL DEFAULT (unixepoch()),\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX idx_patterns_project ON session_patterns(project_path);\n CREATE INDEX idx_patterns_type ON session_patterns(pattern_type, key);\n CREATE UNIQUE INDEX idx_patterns_unique ON session_patterns(project_path, pattern_type, key);\n `,\n },\n {\n name: \"011_session_checkpoints\",\n sql: `\n CREATE TABLE session_checkpoints (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n turn_number INTEGER NOT NULL,\n state_json TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX idx_checkpoint_session ON session_checkpoints(session_id);\n CREATE INDEX idx_checkpoint_created ON session_checkpoints(created_at);\n `,\n },\n {\n name: \"012_session_locks\",\n sql: `\n CREATE TABLE session_locks (\n session_id TEXT PRIMARY KEY,\n holder TEXT NOT NULL,\n acquired_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n `,\n },\n {\n name: \"013_message_timestamp_index\",\n sql: `\n CREATE INDEX IF NOT EXISTS idx_messages_session_ts\n ON messages(session_id, timestamp DESC);\n `,\n },\n {\n name: \"014_audit_log\",\n sql: `\n CREATE TABLE IF NOT EXISTS audit_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n tool_name TEXT NOT NULL,\n args_json TEXT,\n result_ok INTEGER NOT NULL DEFAULT 1,\n duration_ms INTEGER,\n model_id TEXT,\n cost REAL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_audit_session ON audit_log(session_id);\n CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at);\n `,\n },\n {\n name: \"015_code_embeddings\",\n sql: `\n CREATE TABLE IF NOT EXISTS code_embeddings (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_path TEXT NOT NULL,\n file_path TEXT NOT NULL,\n symbol_name TEXT,\n content_snippet TEXT NOT NULL,\n tfidf_vector TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_embeddings_project ON code_embeddings(project_path);\n CREATE INDEX IF NOT EXISTS idx_embeddings_file ON code_embeddings(file_path);\n `,\n },\n {\n name: \"016_projects\",\n sql: `\n CREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n path TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n custom_instructions TEXT,\n knowledge_files TEXT NOT NULL DEFAULT '[]',\n budget_daily REAL,\n budget_monthly REAL,\n is_active INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_projects_name ON projects(name);\n CREATE INDEX IF NOT EXISTS idx_projects_path ON projects(path);\n `,\n },\n {\n name: \"017_project_memory\",\n sql: `\n CREATE TABLE IF NOT EXISTS project_memory (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n category TEXT NOT NULL DEFAULT 'general',\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n UNIQUE(project_id, key)\n );\n CREATE INDEX IF NOT EXISTS idx_project_memory_project ON project_memory(project_id);\n `,\n },\n {\n name: \"018_sessions_project_id\",\n sql: `\n ALTER TABLE sessions ADD COLUMN project_id TEXT REFERENCES projects(id);\n CREATE INDEX IF NOT EXISTS idx_sessions_project_id ON sessions(project_id);\n `,\n },\n {\n name: \"019_scheduled_tasks\",\n sql: `\n CREATE TABLE IF NOT EXISTS scheduled_tasks (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n prompt TEXT NOT NULL,\n cron_expression TEXT,\n execution_mode TEXT NOT NULL DEFAULT 'trigger',\n allow_mutations INTEGER NOT NULL DEFAULT 0,\n budget_limit REAL,\n max_turns INTEGER NOT NULL DEFAULT 20,\n timeout_ms INTEGER NOT NULL DEFAULT 600000,\n model_id TEXT,\n status TEXT NOT NULL DEFAULT 'active',\n expires_at INTEGER,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_project ON scheduled_tasks(project_id);\n CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_status ON scheduled_tasks(status);\n `,\n },\n {\n name: \"020_scheduled_task_runs\",\n sql: `\n CREATE TABLE IF NOT EXISTS scheduled_task_runs (\n id TEXT PRIMARY KEY,\n task_id TEXT NOT NULL REFERENCES scheduled_tasks(id) ON DELETE CASCADE,\n session_id TEXT REFERENCES sessions(id),\n status TEXT NOT NULL DEFAULT 'pending',\n trigger_type TEXT NOT NULL DEFAULT 'cron',\n output_summary TEXT,\n cost REAL NOT NULL DEFAULT 0,\n turns_used INTEGER NOT NULL DEFAULT 0,\n error TEXT,\n trajectory_path TEXT,\n started_at INTEGER,\n completed_at INTEGER,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_task_runs_task ON scheduled_task_runs(task_id);\n CREATE INDEX IF NOT EXISTS idx_task_runs_status ON scheduled_task_runs(status);\n CREATE INDEX IF NOT EXISTS idx_task_runs_created ON scheduled_task_runs(created_at);\n `,\n },\n {\n name: \"021_orchestration\",\n sql: `\n CREATE TABLE IF NOT EXISTS orchestration_runs (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n description TEXT NOT NULL,\n lead_session_id TEXT REFERENCES sessions(id),\n status TEXT NOT NULL DEFAULT 'pending',\n project_ids TEXT NOT NULL DEFAULT '[]',\n budget_limit REAL,\n total_cost REAL NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE TABLE IF NOT EXISTS orchestration_tasks (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES orchestration_runs(id) ON DELETE CASCADE,\n project_id TEXT NOT NULL REFERENCES projects(id),\n prompt TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n subagent_type TEXT NOT NULL DEFAULT 'code',\n result_summary TEXT,\n cost REAL NOT NULL DEFAULT 0,\n session_id TEXT REFERENCES sessions(id),\n depends_on TEXT NOT NULL DEFAULT '[]',\n started_at INTEGER,\n completed_at INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_orch_tasks_run ON orchestration_tasks(run_id);\n `,\n },\n {\n name: \"022_plan_runs\",\n sql: `\n CREATE TABLE IF NOT EXISTS plan_runs (\n id TEXT PRIMARY KEY,\n plan_file_path TEXT NOT NULL,\n plan_name TEXT NOT NULL,\n project_id TEXT REFERENCES projects(id),\n status TEXT NOT NULL DEFAULT 'pending',\n total_tasks INTEGER NOT NULL DEFAULT 0,\n completed_tasks INTEGER NOT NULL DEFAULT 0,\n total_cost REAL NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_plan_runs_project ON plan_runs(project_id);\n CREATE TABLE IF NOT EXISTS plan_task_runs (\n id TEXT PRIMARY KEY,\n plan_run_id TEXT NOT NULL REFERENCES plan_runs(id) ON DELETE CASCADE,\n task_path TEXT NOT NULL,\n description TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n assigned_skill TEXT,\n subagent_type TEXT,\n model_used TEXT,\n cost REAL NOT NULL DEFAULT 0,\n tool_calls_json TEXT NOT NULL DEFAULT '[]',\n started_at INTEGER,\n completed_at INTEGER,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_plan_task_runs_plan ON plan_task_runs(plan_run_id);\n CREATE INDEX IF NOT EXISTS idx_plan_task_runs_status ON plan_task_runs(status);\n `,\n },\n {\n name: \"023_routing_outcome_index\",\n sql: `\n CREATE INDEX IF NOT EXISTS idx_perf_v2_routing\n ON model_performance_v2(task_type, model_id, success);\n `,\n },\n {\n name: \"024_compaction_commits\",\n sql: `\n CREATE TABLE IF NOT EXISTS compaction_commits (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n timestamp INTEGER NOT NULL DEFAULT (unixepoch()),\n summary TEXT NOT NULL,\n original_message_ids TEXT NOT NULL,\n kept_count INTEGER NOT NULL DEFAULT 0,\n summarized_count INTEGER NOT NULL DEFAULT 0,\n dropped_count INTEGER NOT NULL DEFAULT 0,\n tokens_before INTEGER,\n tokens_after INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_compaction_session ON compaction_commits(session_id);\n `,\n },\n {\n name: \"025_daemon_sessions\",\n sql: `\n ALTER TABLE sessions ADD COLUMN is_daemon INTEGER NOT NULL DEFAULT 0;\n ALTER TABLE sessions ADD COLUMN tick_count INTEGER NOT NULL DEFAULT 0;\n ALTER TABLE sessions ADD COLUMN last_tick_at INTEGER;\n ALTER TABLE sessions ADD COLUMN is_paused INTEGER NOT NULL DEFAULT 0;\n ALTER TABLE sessions ADD COLUMN tick_interval_ms INTEGER;\n\n CREATE TABLE IF NOT EXISTS daemon_daily_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT,\n log_date TEXT NOT NULL,\n entry_time INTEGER NOT NULL DEFAULT (unixepoch()),\n tick_number INTEGER,\n event_type TEXT NOT NULL DEFAULT 'tick',\n content TEXT NOT NULL,\n cost REAL NOT NULL DEFAULT 0,\n model_id TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_daemon_log_date ON daemon_daily_log(log_date);\n CREATE INDEX IF NOT EXISTS idx_daemon_log_session ON daemon_daily_log(session_id);\n `,\n },\n {\n name: \"026_godmode_changeset_log\",\n sql: `\n CREATE TABLE IF NOT EXISTS godmode_changeset_log (\n changeset_id TEXT PRIMARY KEY,\n connector TEXT NOT NULL,\n action TEXT NOT NULL,\n description TEXT NOT NULL,\n risk_score INTEGER NOT NULL,\n status TEXT NOT NULL,\n changes_json TEXT,\n simulation_json TEXT,\n rollback_json TEXT,\n created_at INTEGER NOT NULL,\n executed_at INTEGER,\n session_id TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_gm_changeset_connector ON godmode_changeset_log(connector);\n CREATE INDEX IF NOT EXISTS idx_gm_changeset_status ON godmode_changeset_log(status);\n CREATE INDEX IF NOT EXISTS idx_gm_changeset_created ON godmode_changeset_log(created_at);\n `,\n },\n {\n name: \"027_widen_role_and_comm_constraints\",\n sql: `\n -- SQLite cannot ALTER CHECK constraints, so we drop and recreate.\n -- agent_profiles: widen role to include all AgentRole values\n CREATE TABLE IF NOT EXISTS agent_profiles_new (\n id TEXT PRIMARY KEY,\n display_name TEXT NOT NULL,\n role TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n model_id TEXT NOT NULL,\n system_prompt TEXT,\n allowed_tools TEXT NOT NULL DEFAULT '\"all\"',\n output_format TEXT,\n budget_per_workflow REAL,\n budget_daily REAL,\n exhaustion_action TEXT NOT NULL DEFAULT 'downgrade',\n downgrade_model_id TEXT,\n confidence_threshold REAL NOT NULL DEFAULT 0.7,\n max_steps INTEGER NOT NULL DEFAULT 10,\n fallback_chain TEXT NOT NULL DEFAULT '[]',\n guardrails TEXT NOT NULL DEFAULT '{}',\n lifecycle TEXT NOT NULL DEFAULT 'active' CHECK (lifecycle IN ('active', 'suspended')),\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n INSERT OR IGNORE INTO agent_profiles_new SELECT * FROM agent_profiles;\n DROP TABLE IF EXISTS agent_profiles;\n ALTER TABLE agent_profiles_new RENAME TO agent_profiles;\n\n -- workflow_definitions: widen communication_mode to include 'parallel'\n CREATE TABLE IF NOT EXISTS workflow_definitions_new (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n steps_json TEXT NOT NULL,\n communication_mode TEXT NOT NULL DEFAULT 'handoff',\n max_iterations INTEGER NOT NULL DEFAULT 3,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n INSERT OR IGNORE INTO workflow_definitions_new SELECT * FROM workflow_definitions;\n DROP TABLE IF EXISTS workflow_definitions;\n ALTER TABLE workflow_definitions_new RENAME TO workflow_definitions;\n `,\n },\n {\n name: \"028_conversations\",\n sql: `\n CREATE TABLE IF NOT EXISTS conversations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL DEFAULT 'Untitled',\n description TEXT NOT NULL DEFAULT '',\n project_path TEXT NOT NULL,\n tags TEXT NOT NULL DEFAULT '[]',\n model_override TEXT,\n memory_overrides TEXT NOT NULL DEFAULT '{}',\n metadata TEXT NOT NULL DEFAULT '{}',\n is_archived INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n last_message_at INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_conversations_project ON conversations(project_path);\n CREATE INDEX IF NOT EXISTS idx_conversations_updated ON conversations(updated_at DESC);\n\n ALTER TABLE sessions ADD COLUMN conversation_id TEXT REFERENCES conversations(id) ON DELETE SET NULL;\n CREATE INDEX IF NOT EXISTS idx_sessions_conversation ON sessions(conversation_id);\n `,\n },\n {\n name: \"029_orchestration_workers\",\n sql: `\n ALTER TABLE orchestration_tasks ADD COLUMN assigned_worker TEXT;\n ALTER TABLE orchestration_tasks ADD COLUMN worktree_path TEXT;\n ALTER TABLE orchestration_tasks ADD COLUMN files_touched TEXT NOT NULL DEFAULT '[]';\n ALTER TABLE orchestration_tasks ADD COLUMN error TEXT;\n CREATE INDEX IF NOT EXISTS idx_orch_tasks_status ON orchestration_tasks(status);\n CREATE INDEX IF NOT EXISTS idx_orch_tasks_worker ON orchestration_tasks(assigned_worker);\n `,\n },\n {\n name: \"030_sync_queue\",\n sql: `\n CREATE TABLE IF NOT EXISTS sync_queue (\n id TEXT PRIMARY KEY,\n -- Kind categorizes the resource being synced. Used for metrics\n -- and for selective drain (e.g., \"memory entries only\").\n kind TEXT NOT NULL,\n -- HTTP method + path this item will POST/PUT/DELETE against on BR.\n -- Full path including /v1/ prefix, so the sync worker can call\n -- arbitrary endpoints without wrapper code knowing about each one.\n method TEXT NOT NULL,\n path TEXT NOT NULL,\n -- JSON-encoded request body (null for GET/DELETE).\n body TEXT,\n -- JSON-encoded idempotency metadata — prevents duplicate pushes\n -- when the worker retries after a network blip that actually\n -- succeeded upstream. Includes a client-generated idempotency key.\n idempotency_key TEXT NOT NULL,\n -- State machine: pending → in_flight → completed | failed\n -- Only pending rows are eligible for the next drain cycle.\n status TEXT NOT NULL DEFAULT 'pending',\n -- Retry tracking with exponential backoff. next_attempt_at is\n -- updated on failure; the worker skips rows where that's in the future.\n attempt_count INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 10,\n next_attempt_at INTEGER NOT NULL DEFAULT (unixepoch()),\n last_error TEXT,\n -- Timestamps for observability\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n completed_at INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_sync_queue_status_next\n ON sync_queue(status, next_attempt_at);\n CREATE INDEX IF NOT EXISTS idx_sync_queue_kind\n ON sync_queue(kind);\n CREATE INDEX IF NOT EXISTS idx_sync_queue_idempotency\n ON sync_queue(idempotency_key);\n `,\n },\n {\n name: \"031_org_teams\",\n sql: `\n CREATE TABLE IF NOT EXISTS orgs (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n github_owner TEXT,\n github_repo TEXT,\n settings_json TEXT NOT NULL DEFAULT '{}',\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n CREATE TABLE IF NOT EXISTS team_members (\n id TEXT PRIMARY KEY,\n org_id TEXT NOT NULL,\n email TEXT NOT NULL,\n display_name TEXT NOT NULL,\n role TEXT NOT NULL DEFAULT 'engineer'\n CHECK (role IN ('admin', 'engineer', 'qa', 'designer', 'devops', 'compliance')),\n github_username TEXT,\n api_key_hash TEXT,\n budget_daily REAL,\n budget_monthly REAL,\n is_active INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n UNIQUE(org_id, email)\n );\n CREATE INDEX IF NOT EXISTS idx_members_org ON team_members(org_id);\n CREATE INDEX IF NOT EXISTS idx_members_email ON team_members(email);\n `,\n },\n {\n name: \"032_tenant_columns\",\n sql: `\n ALTER TABLE sessions ADD COLUMN org_id TEXT;\n ALTER TABLE sessions ADD COLUMN user_id TEXT;\n ALTER TABLE cost_records ADD COLUMN org_id TEXT;\n ALTER TABLE cost_records ADD COLUMN user_id TEXT;\n ALTER TABLE audit_log ADD COLUMN org_id TEXT;\n ALTER TABLE audit_log ADD COLUMN user_id TEXT;\n CREATE INDEX IF NOT EXISTS idx_cost_org ON cost_records(org_id);\n CREATE INDEX IF NOT EXISTS idx_cost_user ON cost_records(user_id);\n CREATE INDEX IF NOT EXISTS idx_audit_org ON audit_log(org_id);\n CREATE INDEX IF NOT EXISTS idx_sessions_org ON sessions(org_id);\n `,\n },\n {\n name: \"033_compliance_events\",\n sql: `\n CREATE TABLE IF NOT EXISTS compliance_events (\n id TEXT PRIMARY KEY,\n org_id TEXT NOT NULL,\n user_id TEXT,\n event_type TEXT NOT NULL,\n severity TEXT NOT NULL DEFAULT 'info'\n CHECK (severity IN ('info', 'warning', 'critical')),\n description TEXT NOT NULL,\n metadata_json TEXT NOT NULL DEFAULT '{}',\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_compliance_org ON compliance_events(org_id);\n CREATE INDEX IF NOT EXISTS idx_compliance_type ON compliance_events(event_type);\n CREATE INDEX IF NOT EXISTS idx_compliance_severity ON compliance_events(severity);\n CREATE INDEX IF NOT EXISTS idx_compliance_created ON compliance_events(created_at);\n `,\n },\n {\n name: \"034_routing_audit\",\n // Per-request audit trail captured from BR's x-br-envelope headers.\n // Complementary to model_performance_v2 (which holds Thompson-sampling\n // aggregates): this table is the verifiable per-turn chain — every BR\n // chat completion records its x-br-audit-hash, x-br-routed-model, cost,\n // confidence, quality tier, and routing reasoning. Lets us answer\n // \"what did BR decide for request X, and is its audit-hash chain\n // continuous across this session?\" without re-querying BR.\n //\n // Drives Path-to-90 D5 (operability — auditable history), D6 (security\n // — actual provenance chain), D8 (failure traceability).\n sql: `\n CREATE TABLE IF NOT EXISTS routing_audit (\n request_id TEXT PRIMARY KEY,\n audit_hash TEXT,\n envelope_mode TEXT,\n routed_model TEXT,\n route_reason TEXT,\n route_confidence REAL,\n selection_method TEXT,\n selection_confidence REAL,\n quality_tier TEXT,\n quality_score REAL,\n models_considered INTEGER,\n actual_cost_usd REAL,\n estimated_cost_usd REAL,\n routing_savings_usd REAL,\n budget_remaining_usd REAL,\n total_latency_ms REAL,\n provider_latency_ms REAL,\n routing_overhead_ms REAL,\n guardian_overhead_ms REAL,\n guardian_status TEXT,\n guardrail_status TEXT,\n reputation_tier TEXT,\n tier TEXT,\n degradation_level INTEGER,\n deprecation_notice TEXT,\n cache_state TEXT,\n cache_age_ms REAL,\n cold_start_ms REAL,\n br_build TEXT,\n routing_reasoning_json TEXT,\n context_json TEXT,\n captured_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n CREATE INDEX IF NOT EXISTS idx_routing_audit_captured_at\n ON routing_audit(captured_at);\n CREATE INDEX IF NOT EXISTS idx_routing_audit_audit_hash\n ON routing_audit(audit_hash);\n CREATE INDEX IF NOT EXISTS idx_routing_audit_routed_model\n ON routing_audit(routed_model);\n `,\n },\n];\n","import type Database from \"better-sqlite3\";\nimport { randomUUID } from \"node:crypto\";\nimport type {\n Session,\n Message,\n CostRecord,\n TaskType,\n} from \"@brainst0rm/shared\";\n\n/**\n * Parse a JSON column, falling back to the provided default on any error.\n * A single corrupt row (power-loss truncation, bug in an old migration,\n * manual edit) would otherwise crash session/conversation hydration and\n * take down every caller that doesn't wrap the repository call.\n */\nfunction safeParseJson<T>(raw: unknown, fallback: T): T {\n if (typeof raw !== \"string\" || raw.length === 0) return fallback;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n}\n\n// ── Sessions ─────────────────────────────────────────────────────────\n\nexport class SessionRepository {\n constructor(private db: Database.Database) {}\n\n create(projectPath: string): Session {\n const id = randomUUID();\n const now = Math.floor(Date.now() / 1000);\n this.db\n .prepare(\n \"INSERT INTO sessions (id, created_at, updated_at, project_path) VALUES (?, ?, ?, ?)\",\n )\n .run(id, now, now, projectPath);\n return {\n id,\n createdAt: now,\n updatedAt: now,\n projectPath,\n totalCost: 0,\n messageCount: 0,\n };\n }\n\n get(id: string): Session | null {\n const row = this.db\n .prepare(\"SELECT * FROM sessions WHERE id = ?\")\n .get(id) as any;\n if (!row) return null;\n return {\n id: row.id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n projectPath: row.project_path,\n totalCost: row.total_cost,\n messageCount: row.message_count,\n };\n }\n\n updateCost(id: string, cost: number): void {\n this.db\n .prepare(\n \"UPDATE sessions SET total_cost = total_cost + ?, updated_at = unixepoch() WHERE id = ?\",\n )\n .run(cost, id);\n }\n\n incrementMessages(id: string): void {\n this.db\n .prepare(\n \"UPDATE sessions SET message_count = message_count + 1, updated_at = unixepoch() WHERE id = ?\",\n )\n .run(id);\n }\n\n listRecent(limit = 10): Session[] {\n const rows = this.db\n .prepare(\"SELECT * FROM sessions ORDER BY updated_at DESC LIMIT ?\")\n .all(limit) as any[];\n return rows.map((row) => ({\n id: row.id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n projectPath: row.project_path,\n totalCost: row.total_cost,\n messageCount: row.message_count,\n }));\n }\n\n /**\n * List the most-recent sessions for a specific project. Used by\n * resumeLatest(projectPath) which previously filtered the top-1\n * global session — and returned null whenever the user's latest\n * session was in a different project, even though a relevant\n * session existed elsewhere in the history.\n */\n listRecentByProject(projectPath: string, limit = 10): Session[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM sessions WHERE project_path = ? ORDER BY updated_at DESC LIMIT ?\",\n )\n .all(projectPath, limit) as any[];\n return rows.map((row) => ({\n id: row.id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n projectPath: row.project_path,\n totalCost: row.total_cost,\n messageCount: row.message_count,\n }));\n }\n\n /** Mark a session as daemon-mode and set initial tick interval. */\n markDaemon(id: string, tickIntervalMs: number): void {\n this.db\n .prepare(\n \"UPDATE sessions SET is_daemon = 1, tick_interval_ms = ?, updated_at = unixepoch() WHERE id = ?\",\n )\n .run(tickIntervalMs, id);\n }\n\n /** Update daemon state after each tick. */\n updateDaemonState(\n id: string,\n state: {\n tickCount: number;\n lastTickAt: number;\n isPaused?: boolean;\n totalCost?: number;\n },\n ): void {\n this.db\n .prepare(\n `UPDATE sessions SET\n tick_count = ?, last_tick_at = ?, is_paused = ?,\n total_cost = COALESCE(?, total_cost), updated_at = unixepoch()\n WHERE id = ?`,\n )\n .run(\n state.tickCount,\n state.lastTickAt,\n state.isPaused ? 1 : 0,\n state.totalCost ?? null,\n id,\n );\n }\n\n /** Get the most recent daemon session for a project (for --continue). */\n getLastDaemon(projectPath: string): Session | null {\n const row = this.db\n .prepare(\n \"SELECT * FROM sessions WHERE project_path = ? AND is_daemon = 1 ORDER BY updated_at DESC LIMIT 1\",\n )\n .get(projectPath) as any;\n if (!row) return null;\n return {\n id: row.id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n projectPath: row.project_path,\n totalCost: row.total_cost,\n messageCount: row.message_count,\n isDaemon: true,\n tickCount: row.tick_count,\n lastTickAt: row.last_tick_at,\n isPaused: !!row.is_paused,\n tickIntervalMs: row.tick_interval_ms,\n };\n }\n}\n\n// ── Messages ─────────────────────────────────────────────────────────\n\nexport class MessageRepository {\n constructor(private db: Database.Database) {}\n\n create(\n sessionId: string,\n role: Message[\"role\"],\n content: string,\n modelId?: string,\n tokenCount?: number,\n ): Message {\n const id = randomUUID();\n const timestamp = Math.floor(Date.now() / 1000);\n this.db\n .prepare(\n \"INSERT INTO messages (id, session_id, role, content, model_id, token_count, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n )\n .run(\n id,\n sessionId,\n role,\n content,\n modelId ?? null,\n tokenCount ?? null,\n timestamp,\n );\n return { id, sessionId, role, content, modelId, tokenCount, timestamp };\n }\n\n listBySession(sessionId: string): Message[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM messages WHERE session_id = ? ORDER BY timestamp ASC\",\n )\n .all(sessionId) as any[];\n return rows.map((row) => ({\n id: row.id,\n sessionId: row.session_id,\n role: row.role,\n content: row.content,\n modelId: row.model_id ?? undefined,\n tokenCount: row.token_count ?? undefined,\n timestamp: row.timestamp,\n }));\n }\n\n /** Load only the most recent N messages for a session. Used for lazy loading. */\n listBySessionRecent(sessionId: string, limit = 50): Message[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM (SELECT * FROM messages WHERE session_id = ? ORDER BY timestamp DESC LIMIT ?) ORDER BY timestamp ASC\",\n )\n .all(sessionId, limit) as any[];\n return rows.map((row) => ({\n id: row.id,\n sessionId: row.session_id,\n role: row.role,\n content: row.content,\n modelId: row.model_id ?? undefined,\n tokenCount: row.token_count ?? undefined,\n timestamp: row.timestamp,\n }));\n }\n\n /** Count total messages in a session. */\n countBySession(sessionId: string): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as count FROM messages WHERE session_id = ?\")\n .get(sessionId) as any;\n return row?.count ?? 0;\n }\n}\n\n// ── Cost Records ─────────────────────────────────────────────────────\n\nexport class CostRepository {\n constructor(private db: Database.Database) {}\n\n record(entry: Omit<CostRecord, \"id\">): CostRecord {\n const id = randomUUID();\n this.db\n .prepare(\n `INSERT INTO cost_records (id, timestamp, session_id, model_id, provider, input_tokens, output_tokens, cached_tokens, cost, task_type, project_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n id,\n entry.timestamp,\n entry.sessionId,\n entry.modelId,\n entry.provider,\n entry.inputTokens,\n entry.outputTokens,\n entry.cachedTokens,\n entry.cost,\n entry.taskType,\n entry.projectPath ?? null,\n );\n return { ...entry, id };\n }\n\n totalCostToday(): number {\n const startOfDay = new Date();\n startOfDay.setHours(0, 0, 0, 0);\n const row = this.db\n .prepare(\n \"SELECT COALESCE(SUM(cost), 0) as total FROM cost_records WHERE timestamp >= ?\",\n )\n .get(Math.floor(startOfDay.getTime() / 1000)) as any;\n return row.total;\n }\n\n totalCostThisMonth(): number {\n const startOfMonth = new Date();\n startOfMonth.setDate(1);\n startOfMonth.setHours(0, 0, 0, 0);\n const row = this.db\n .prepare(\n \"SELECT COALESCE(SUM(cost), 0) as total FROM cost_records WHERE timestamp >= ?\",\n )\n .get(Math.floor(startOfMonth.getTime() / 1000)) as any;\n return row.total;\n }\n\n totalCostForSession(sessionId: string): number {\n const row = this.db\n .prepare(\n \"SELECT COALESCE(SUM(cost), 0) as total FROM cost_records WHERE session_id = ?\",\n )\n .get(sessionId) as any;\n return row.total;\n }\n\n lastForSession(sessionId: string): CostRecord | null {\n const row = this.db\n .prepare(\n \"SELECT * FROM cost_records WHERE session_id = ? ORDER BY timestamp DESC LIMIT 1\",\n )\n .get(sessionId) as any;\n if (!row) return null;\n return {\n id: row.id,\n timestamp: row.timestamp,\n sessionId: row.session_id,\n modelId: row.model_id,\n provider: row.provider,\n inputTokens: row.input_tokens,\n outputTokens: row.output_tokens,\n cachedTokens: row.cached_tokens,\n cost: row.cost,\n taskType: row.task_type,\n projectPath: row.project_path,\n };\n }\n\n updateCost(id: string, cost: number): void {\n this.db\n .prepare(\"UPDATE cost_records SET cost = ? WHERE id = ?\")\n .run(cost, id);\n }\n\n recentByModel(\n limit = 20,\n ): Array<{ modelId: string; totalCost: number; requestCount: number }> {\n const rows = this.db\n .prepare(\n `SELECT model_id, SUM(cost) as total_cost, COUNT(*) as request_count FROM cost_records GROUP BY model_id ORDER BY total_cost DESC LIMIT ?`,\n )\n .all(limit) as any[];\n return rows.map((r) => ({\n modelId: r.model_id,\n totalCost: r.total_cost,\n requestCount: r.request_count,\n }));\n }\n\n /** Aggregate cost by task type. */\n byTaskType(): Array<{\n taskType: string;\n totalCost: number;\n requestCount: number;\n avgCost: number;\n }> {\n const rows = this.db\n .prepare(\n `SELECT task_type, SUM(cost) as total_cost, COUNT(*) as request_count, AVG(cost) as avg_cost FROM cost_records GROUP BY task_type ORDER BY total_cost DESC`,\n )\n .all() as any[];\n return rows.map((r) => ({\n taskType: r.task_type,\n totalCost: r.total_cost,\n requestCount: r.request_count,\n avgCost: r.avg_cost,\n }));\n }\n\n /** Get cost for a specific task type within a time range. */\n costForTaskType(\n taskType: string,\n sinceTimestamp?: number,\n ): { totalCost: number; requestCount: number } {\n const since = sinceTimestamp ?? 0;\n const row = this.db\n .prepare(\n `SELECT COALESCE(SUM(cost), 0) as total_cost, COUNT(*) as request_count FROM cost_records WHERE task_type = ? AND timestamp >= ?`,\n )\n .get(taskType, since) as any;\n return { totalCost: row.total_cost, requestCount: row.request_count };\n }\n\n /** Aggregate cost by project path with 7-day trend. */\n byProject(): Array<{\n projectPath: string;\n totalCost: number;\n requestCount: number;\n last7DaysCost: number;\n }> {\n const weekAgo = Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60;\n const rows = this.db\n .prepare(\n `SELECT project_path,\n SUM(cost) as total_cost,\n COUNT(*) as request_count,\n SUM(CASE WHEN timestamp >= ? THEN cost ELSE 0 END) as last_7_days_cost\n FROM cost_records\n WHERE project_path IS NOT NULL\n GROUP BY project_path\n ORDER BY total_cost DESC`,\n )\n .all(weekAgo) as any[];\n return rows.map((r: any) => ({\n projectPath: r.project_path,\n totalCost: r.total_cost,\n requestCount: r.request_count,\n last7DaysCost: r.last_7_days_cost,\n }));\n }\n}\n\n// ── Routing Outcomes (Thompson Sampling Persistence) ────────────────\n\nexport interface AggregatedRoutingStats {\n taskType: string;\n modelId: string;\n successes: number;\n failures: number;\n avgLatencyMs: number;\n avgCost: number;\n samples: number;\n}\n\nexport class RoutingOutcomeRepository {\n constructor(private db: Database.Database) {}\n\n /** Record a single routing outcome (writes to model_performance_v2). */\n record(\n modelId: string,\n taskType: string,\n success: boolean,\n latencyMs: number,\n costUsd: number,\n ): void {\n this.db\n .prepare(\n `INSERT INTO model_performance_v2 (model_id, task_type, success, latency_ms, cost_usd)\n VALUES (?, ?, ?, ?, ?)`,\n )\n .run(modelId, taskType, success ? 1 : 0, latencyMs, costUsd);\n }\n\n /** Load aggregated stats for Thompson sampling — grouped by (task_type, model_id). */\n loadAggregated(): AggregatedRoutingStats[] {\n const rows = this.db\n .prepare(\n `SELECT\n task_type,\n model_id,\n SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successes,\n SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failures,\n AVG(latency_ms) as avg_latency_ms,\n AVG(cost_usd) as avg_cost,\n COUNT(*) as samples\n FROM model_performance_v2\n GROUP BY task_type, model_id\n HAVING samples >= 1`,\n )\n .all() as any[];\n\n return rows.map((r) => ({\n taskType: r.task_type,\n modelId: r.model_id,\n successes: r.successes,\n failures: r.failures,\n avgLatencyMs: r.avg_latency_ms ?? 0,\n avgCost: r.avg_cost ?? 0,\n samples: r.samples,\n }));\n }\n}\n\n// ── Compaction Commits (Reversible Context Collapse) ────────────────\n\nexport interface CompactionCommit {\n id: string;\n sessionId: string;\n timestamp: number;\n summary: string;\n originalMessageIds: string[];\n keptCount: number;\n summarizedCount: number;\n droppedCount: number;\n tokensBefore?: number;\n tokensAfter?: number;\n}\n\nexport class CompactionCommitRepository {\n constructor(private db: Database.Database) {}\n\n /** Persist a compaction commit before replacing messages. */\n create(commit: CompactionCommit): void {\n this.db\n .prepare(\n `INSERT INTO compaction_commits (id, session_id, timestamp, summary, original_message_ids, kept_count, summarized_count, dropped_count, tokens_before, tokens_after)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n commit.id,\n commit.sessionId,\n commit.timestamp,\n commit.summary,\n JSON.stringify(commit.originalMessageIds),\n commit.keptCount,\n commit.summarizedCount,\n commit.droppedCount,\n commit.tokensBefore ?? null,\n commit.tokensAfter ?? null,\n );\n }\n\n /** Get a compaction commit by ID. */\n get(id: string): CompactionCommit | null {\n const row = this.db\n .prepare(\"SELECT * FROM compaction_commits WHERE id = ?\")\n .get(id) as any;\n if (!row) return null;\n return {\n id: row.id,\n sessionId: row.session_id,\n timestamp: row.timestamp,\n summary: row.summary,\n originalMessageIds: safeParseJson<string[]>(row.original_message_ids, []),\n keptCount: row.kept_count,\n summarizedCount: row.summarized_count,\n droppedCount: row.dropped_count,\n tokensBefore: row.tokens_before,\n tokensAfter: row.tokens_after,\n };\n }\n\n /** List all compaction commits for a session. */\n listForSession(sessionId: string): CompactionCommit[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM compaction_commits WHERE session_id = ? ORDER BY timestamp DESC\",\n )\n .all(sessionId) as any[];\n return rows.map((row) => ({\n id: row.id,\n sessionId: row.session_id,\n timestamp: row.timestamp,\n summary: row.summary,\n originalMessageIds: safeParseJson<string[]>(row.original_message_ids, []),\n keptCount: row.kept_count,\n summarizedCount: row.summarized_count,\n droppedCount: row.dropped_count,\n tokensBefore: row.tokens_before,\n tokensAfter: row.tokens_after,\n }));\n }\n}\n\n// ── Session Patterns ────────────────────────────────────────────────\n\nexport interface SessionPattern {\n id: number;\n projectPath: string;\n patternType:\n | \"tool_success\"\n | \"command_timing\"\n | \"user_preference\"\n | \"model_choice\";\n key: string;\n value: string;\n confidence: number;\n occurrences: number;\n lastSeen: number;\n}\n\nexport class PatternRepository {\n constructor(private db: Database.Database) {}\n\n /** Upsert a pattern — increments occurrences if exists, creates if not. */\n record(\n projectPath: string,\n patternType: SessionPattern[\"patternType\"],\n key: string,\n value: string,\n confidence = 0.5,\n ): void {\n this.db\n .prepare(\n `\n INSERT INTO session_patterns (project_path, pattern_type, key, value, confidence, occurrences, last_seen)\n VALUES (?, ?, ?, ?, ?, 1, unixepoch())\n ON CONFLICT(project_path, pattern_type, key)\n DO UPDATE SET\n value = excluded.value,\n confidence = MIN(1.0, confidence + 0.1),\n occurrences = occurrences + 1,\n last_seen = unixepoch()\n `,\n )\n .run(projectPath, patternType, key, value, confidence);\n }\n\n /** Get all patterns for a project, optionally filtered by type. */\n getForProject(\n projectPath: string,\n patternType?: SessionPattern[\"patternType\"],\n ): SessionPattern[] {\n const query = patternType\n ? \"SELECT * FROM session_patterns WHERE project_path = ? AND pattern_type = ? ORDER BY confidence DESC\"\n : \"SELECT * FROM session_patterns WHERE project_path = ? ORDER BY confidence DESC\";\n const rows = (\n patternType\n ? this.db.prepare(query).all(projectPath, patternType)\n : this.db.prepare(query).all(projectPath)\n ) as any[];\n\n return rows.map((r) => ({\n id: r.id,\n projectPath: r.project_path,\n patternType: r.pattern_type,\n key: r.key,\n value: r.value,\n confidence: r.confidence,\n occurrences: r.occurrences,\n lastSeen: r.last_seen,\n }));\n }\n\n /** Decay old patterns — reduce confidence for patterns not seen in N days. */\n decayOld(maxAgeDays = 30): number {\n const cutoff = Math.floor(Date.now() / 1000) - maxAgeDays * 86400;\n const result = this.db\n .prepare(\n `\n DELETE FROM session_patterns WHERE last_seen < ? AND confidence < 0.3\n `,\n )\n .run(cutoff);\n // Decay confidence for old but still-relevant patterns\n this.db\n .prepare(\n `\n UPDATE session_patterns SET confidence = MAX(0.1, confidence - 0.2)\n WHERE last_seen < ?\n `,\n )\n .run(cutoff);\n return result.changes;\n }\n\n /** Format patterns as a context string for system prompt injection. */\n formatForPrompt(projectPath: string): string {\n const patterns = this.getForProject(projectPath);\n if (patterns.length === 0) return \"\";\n\n const lines: string[] = [\"[Project patterns from previous sessions]\"];\n for (const p of patterns.slice(0, 10)) {\n lines.push(\n `- ${p.patternType}: ${p.key} → ${p.value} (${p.occurrences}x, confidence ${p.confidence.toFixed(1)})`,\n );\n }\n return lines.join(\"\\n\");\n }\n}\n\n// ── Session Locks ───────────────────────────────────────────────────\n\nconst STALE_LOCK_SECONDS = 5 * 60; // 5 minutes\n\nexport class SessionLockManager {\n constructor(private db: Database.Database) {}\n\n /** Acquire a lock for a session. Returns true if acquired, false if held by another. */\n acquire(sessionId: string, holder: string): boolean {\n this.cleanStale();\n\n const result = this.db\n .transaction(() => {\n const existing = this.db\n .prepare(\"SELECT holder FROM session_locks WHERE session_id = ?\")\n .get(sessionId) as any;\n\n if (existing) {\n if (existing.holder === holder) {\n // Renew the lease\n this.db\n .prepare(\n \"UPDATE session_locks SET acquired_at = unixepoch() WHERE session_id = ?\",\n )\n .run(sessionId);\n return true;\n }\n return false;\n }\n\n this.db\n .prepare(\n \"INSERT INTO session_locks (session_id, holder) VALUES (?, ?)\",\n )\n .run(sessionId, holder);\n return true;\n })\n .immediate();\n\n return result;\n }\n\n /** Renew the lease on a held lock. Call this periodically during long operations. */\n renew(sessionId: string, holder: string): boolean {\n const result = this.db\n .prepare(\n \"UPDATE session_locks SET acquired_at = unixepoch() WHERE session_id = ? AND holder = ?\",\n )\n .run(sessionId, holder);\n return result.changes > 0;\n }\n\n /** Release a lock. Only the holder can release. */\n release(sessionId: string, holder: string): boolean {\n const result = this.db\n .prepare(\"DELETE FROM session_locks WHERE session_id = ? AND holder = ?\")\n .run(sessionId, holder);\n return result.changes > 0;\n }\n\n /** Check if a session is locked. */\n isLocked(sessionId: string): boolean {\n this.cleanStale();\n const row = this.db\n .prepare(\"SELECT 1 FROM session_locks WHERE session_id = ?\")\n .get(sessionId);\n return !!row;\n }\n\n /** Remove locks older than STALE_LOCK_SECONDS (stale/crashed processes). */\n private cleanStale(): void {\n const cutoff = Math.floor(Date.now() / 1000) - STALE_LOCK_SECONDS;\n this.db\n .prepare(\"DELETE FROM session_locks WHERE acquired_at < ?\")\n .run(cutoff);\n }\n}\n\n// ── Daemon Daily Log ───────────────────────────────────────────────\n\nexport interface DailyLogEntry {\n id: number;\n sessionId?: string;\n logDate: string;\n entryTime: number;\n tickNumber?: number;\n eventType: string;\n content: string;\n cost: number;\n modelId?: string;\n}\n\nexport class DailyLogRepository {\n constructor(private db: Database.Database) {}\n\n /** Append a log entry. */\n append(entry: Omit<DailyLogEntry, \"id\">): void {\n this.db\n .prepare(\n `INSERT INTO daemon_daily_log (session_id, log_date, entry_time, tick_number, event_type, content, cost, model_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n entry.sessionId ?? null,\n entry.logDate,\n entry.entryTime,\n entry.tickNumber ?? null,\n entry.eventType,\n entry.content,\n entry.cost,\n entry.modelId ?? null,\n );\n }\n\n /** Read all entries for today. */\n readToday(): DailyLogEntry[] {\n const today = new Date().toISOString().slice(0, 10);\n return this.readDate(today);\n }\n\n /** Read entries for a specific date (YYYY-MM-DD). */\n readDate(date: string): DailyLogEntry[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM daemon_daily_log WHERE log_date = ? ORDER BY entry_time ASC\",\n )\n .all(date) as any[];\n return rows.map(this.mapRow);\n }\n\n /** Read entries for a date range (inclusive). */\n readRange(startDate: string, endDate: string): DailyLogEntry[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM daemon_daily_log WHERE log_date >= ? AND log_date <= ? ORDER BY entry_time ASC\",\n )\n .all(startDate, endDate) as any[];\n return rows.map(this.mapRow);\n }\n\n /** Read the last N entries across all dates. */\n readRecent(limit = 50): DailyLogEntry[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM daemon_daily_log ORDER BY entry_time DESC LIMIT ?\",\n )\n .all(limit) as any[];\n return rows.map(this.mapRow).reverse();\n }\n\n private mapRow(row: any): DailyLogEntry {\n return {\n id: row.id,\n sessionId: row.session_id ?? undefined,\n logDate: row.log_date,\n entryTime: row.entry_time,\n tickNumber: row.tick_number ?? undefined,\n eventType: row.event_type,\n content: row.content,\n cost: row.cost,\n modelId: row.model_id ?? undefined,\n };\n }\n}\n\n// ── God Mode Audit Repository ──────────────────────────────────────\n\nexport interface ChangeSetLogEntry {\n changesetId: string;\n connector: string;\n action: string;\n description: string;\n riskScore: number;\n status: string;\n changesJson: string | null;\n simulationJson: string | null;\n rollbackJson: string | null;\n createdAt: number;\n executedAt: number | null;\n sessionId: string | null;\n}\n\nexport class ChangeSetLogRepository {\n constructor(private db: Database.Database) {}\n\n /** Persist a changeset execution to the audit log. */\n log(entry: ChangeSetLogEntry): void {\n this.db\n .prepare(\n `INSERT OR REPLACE INTO godmode_changeset_log\n (changeset_id, connector, action, description, risk_score, status,\n changes_json, simulation_json, rollback_json, created_at, executed_at, session_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n entry.changesetId,\n entry.connector,\n entry.action,\n entry.description,\n entry.riskScore,\n entry.status,\n entry.changesJson,\n entry.simulationJson,\n entry.rollbackJson,\n entry.createdAt,\n entry.executedAt,\n entry.sessionId,\n );\n }\n\n /** Get recent changeset audit entries. */\n recent(limit = 50, offset = 0): ChangeSetLogEntry[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM godmode_changeset_log ORDER BY created_at DESC LIMIT ? OFFSET ?\",\n )\n .all(limit, offset) as any[];\n return rows.map(this.mapRow);\n }\n\n /** Get entries for a specific connector. */\n byConnector(connector: string, limit = 50): ChangeSetLogEntry[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM godmode_changeset_log WHERE connector = ? ORDER BY created_at DESC LIMIT ?\",\n )\n .all(connector, limit) as any[];\n return rows.map(this.mapRow);\n }\n\n /** Count total entries. */\n count(): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as cnt FROM godmode_changeset_log\")\n .get() as any;\n return row?.cnt ?? 0;\n }\n\n private mapRow(row: any): ChangeSetLogEntry {\n return {\n changesetId: row.changeset_id,\n connector: row.connector,\n action: row.action,\n description: row.description,\n riskScore: row.risk_score,\n status: row.status,\n changesJson: row.changes_json,\n simulationJson: row.simulation_json,\n rollbackJson: row.rollback_json,\n createdAt: row.created_at,\n executedAt: row.executed_at,\n sessionId: row.session_id,\n };\n }\n}\n\n// ── Conversations ─────────────────────────────────────────────────────\n\nexport interface Conversation {\n id: string;\n name: string;\n description: string;\n projectPath: string;\n tags: string[];\n modelOverride: string | null;\n /** Per-conversation memory overrides: { memoryId: content | null (suppress) } */\n memoryOverrides: Record<string, string | null>;\n metadata: Record<string, unknown>;\n isArchived: boolean;\n createdAt: number;\n updatedAt: number;\n lastMessageAt: number | null;\n}\n\nexport class ConversationRepository {\n constructor(private db: Database.Database) {}\n\n create(\n projectPath: string,\n opts?: {\n name?: string;\n description?: string;\n tags?: string[];\n modelOverride?: string | null;\n memoryOverrides?: Record<string, string | null>;\n metadata?: Record<string, unknown>;\n },\n ): Conversation {\n const id = randomUUID();\n const now = Math.floor(Date.now() / 1000);\n this.db\n .prepare(\n `INSERT INTO conversations (id, name, description, project_path, tags, model_override, memory_overrides, metadata, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n id,\n opts?.name ?? \"Untitled\",\n opts?.description ?? \"\",\n projectPath,\n JSON.stringify(opts?.tags ?? []),\n opts?.modelOverride ?? null,\n JSON.stringify(opts?.memoryOverrides ?? {}),\n JSON.stringify(opts?.metadata ?? {}),\n now,\n now,\n );\n return {\n id,\n name: opts?.name ?? \"Untitled\",\n description: opts?.description ?? \"\",\n projectPath,\n tags: opts?.tags ?? [],\n modelOverride: opts?.modelOverride ?? null,\n memoryOverrides: opts?.memoryOverrides ?? {},\n metadata: opts?.metadata ?? {},\n isArchived: false,\n createdAt: now,\n updatedAt: now,\n lastMessageAt: null,\n };\n }\n\n get(id: string): Conversation | null {\n const row = this.db\n .prepare(\"SELECT * FROM conversations WHERE id = ?\")\n .get(id) as any;\n if (!row) return null;\n return this.mapRow(row);\n }\n\n list(\n projectPath?: string,\n opts?: { includeArchived?: boolean; limit?: number },\n ): Conversation[] {\n const limit = opts?.limit ?? 50;\n const includeArchived = opts?.includeArchived ?? false;\n let query: string;\n let params: any[];\n\n if (projectPath) {\n query = includeArchived\n ? \"SELECT * FROM conversations WHERE project_path = ? ORDER BY updated_at DESC LIMIT ?\"\n : \"SELECT * FROM conversations WHERE project_path = ? AND is_archived = 0 ORDER BY updated_at DESC LIMIT ?\";\n params = [projectPath, limit];\n } else {\n query = includeArchived\n ? \"SELECT * FROM conversations ORDER BY updated_at DESC LIMIT ?\"\n : \"SELECT * FROM conversations WHERE is_archived = 0 ORDER BY updated_at DESC LIMIT ?\";\n params = [limit];\n }\n\n const rows = this.db.prepare(query).all(...params) as any[];\n return rows.map(this.mapRow);\n }\n\n update(\n id: string,\n updates: Partial<\n Pick<\n Conversation,\n | \"name\"\n | \"description\"\n | \"tags\"\n | \"modelOverride\"\n | \"memoryOverrides\"\n | \"metadata\"\n | \"isArchived\"\n >\n >,\n ): Conversation | null {\n const existing = this.get(id);\n if (!existing) return null;\n\n const merged = { ...existing, ...updates };\n this.db\n .prepare(\n `UPDATE conversations SET\n name = ?, description = ?, tags = ?, model_override = ?,\n memory_overrides = ?, metadata = ?, is_archived = ?,\n updated_at = unixepoch()\n WHERE id = ?`,\n )\n .run(\n merged.name,\n merged.description,\n JSON.stringify(merged.tags),\n merged.modelOverride,\n JSON.stringify(merged.memoryOverrides),\n JSON.stringify(merged.metadata),\n merged.isArchived ? 1 : 0,\n id,\n );\n return this.get(id);\n }\n\n delete(id: string): boolean {\n const result = this.db\n .prepare(\"DELETE FROM conversations WHERE id = ?\")\n .run(id);\n return result.changes > 0;\n }\n\n /** Touch the last_message_at timestamp for a conversation. */\n touchLastMessage(id: string): void {\n this.db\n .prepare(\n \"UPDATE conversations SET last_message_at = unixepoch(), updated_at = unixepoch() WHERE id = ?\",\n )\n .run(id);\n }\n\n /** Link a session to a conversation. */\n linkSession(sessionId: string, conversationId: string): void {\n this.db\n .prepare(\"UPDATE sessions SET conversation_id = ? WHERE id = ?\")\n .run(conversationId, sessionId);\n }\n\n /** Get all sessions belonging to a conversation. */\n getSessions(conversationId: string): Session[] {\n const rows = this.db\n .prepare(\n \"SELECT * FROM sessions WHERE conversation_id = ? ORDER BY created_at DESC\",\n )\n .all(conversationId) as any[];\n return rows.map((row) => ({\n id: row.id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n projectPath: row.project_path,\n totalCost: row.total_cost,\n messageCount: row.message_count,\n isDaemon: !!row.is_daemon,\n tickCount: row.tick_count,\n lastTickAt: row.last_tick_at,\n isPaused: !!row.is_paused,\n tickIntervalMs: row.tick_interval_ms,\n }));\n }\n\n /** Fork a conversation — copies metadata but not sessions. */\n fork(id: string, newName?: string): Conversation | null {\n const original = this.get(id);\n if (!original) return null;\n return this.create(original.projectPath, {\n name: newName ?? `${original.name} (fork)`,\n description: original.description,\n tags: [...original.tags],\n modelOverride: original.modelOverride,\n memoryOverrides: { ...original.memoryOverrides },\n metadata: { ...original.metadata, forkedFrom: id },\n });\n }\n\n private mapRow(row: any): Conversation {\n return {\n id: row.id,\n name: row.name,\n description: row.description,\n projectPath: row.project_path,\n tags: safeParseJson<string[]>(row.tags, []),\n modelOverride: row.model_override,\n memoryOverrides: safeParseJson<Record<string, string | null>>(\n row.memory_overrides,\n {},\n ),\n metadata: safeParseJson<Record<string, unknown>>(row.metadata, {}),\n isArchived: !!row.is_archived,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n lastMessageAt: row.last_message_at,\n };\n }\n}\n\n// ── Sync Queue Repository ──────────────────────────────────────────────\n//\n// Persistent retry queue for fire-and-forget BR pushes. When the gateway\n// client fails to deliver a request, it enqueues the request here and a\n// worker drains the queue with exponential backoff. Survives crashes and\n// restarts — the state machine lives in SQLite.\n//\n// Kind taxonomy (used for metrics + selective drain):\n// \"memory-entry\" — POST/PUT /v1/memory/entries\n// \"memory-shared\" — POST /v1/memory/shared/store\n// \"memory-approval\" — POST /v1/memory/pending/*/approve|reject\n// \"project\" — POST/PUT /v1/projects\n// \"trajectory\" — POST /v1/agent/trajectory\n// \"capability\" — POST /v1/models/{id}/capabilities\n// \"generic\" — anything else\n//\n// Idempotency key: clients generate a stable key (e.g., hash of memory\n// entry id + updated_at). The sync worker sends it in the X-Idempotency-Key\n// header so BR can deduplicate server-side if a retry actually succeeded\n// on a previous attempt that failed before the response arrived.\n\nexport interface SyncQueueRow {\n id: string;\n kind: string;\n method: string;\n path: string;\n body: string | null;\n idempotencyKey: string;\n status: \"pending\" | \"in_flight\" | \"completed\" | \"failed\";\n attemptCount: number;\n maxAttempts: number;\n nextAttemptAt: number;\n lastError: string | null;\n createdAt: number;\n updatedAt: number;\n completedAt: number | null;\n}\n\nexport interface EnqueueOptions {\n kind: string;\n method: \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n path: string;\n body?: unknown;\n /** Stable idempotency key. If provided, duplicate enqueues with the\n * same key are skipped (the existing pending row is returned). */\n idempotencyKey?: string;\n maxAttempts?: number;\n}\n\nexport class SyncQueueRepository {\n constructor(private db: Database.Database) {}\n\n /**\n * Enqueue a new sync item. If an item with the same idempotencyKey\n * already exists in a non-terminal state, return that row instead of\n * creating a duplicate.\n */\n enqueue(opts: EnqueueOptions): SyncQueueRow {\n const idempotencyKey = opts.idempotencyKey ?? randomUUID();\n\n // Dedupe: return existing non-terminal row if idempotencyKey matches\n const existing = this.db\n .prepare(\n `SELECT * FROM sync_queue\n WHERE idempotency_key = ? AND status IN ('pending', 'in_flight')\n LIMIT 1`,\n )\n .get(idempotencyKey) as any;\n if (existing) return mapSyncQueueRow(existing);\n\n const id = randomUUID();\n const body = opts.body != null ? JSON.stringify(opts.body) : null;\n const maxAttempts = opts.maxAttempts ?? 10;\n\n this.db\n .prepare(\n `INSERT INTO sync_queue\n (id, kind, method, path, body, idempotency_key, status, max_attempts)\n VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)`,\n )\n .run(\n id,\n opts.kind,\n opts.method,\n opts.path,\n body,\n idempotencyKey,\n maxAttempts,\n );\n\n return this.getById(id)!;\n }\n\n getById(id: string): SyncQueueRow | null {\n const row = this.db\n .prepare(\"SELECT * FROM sync_queue WHERE id = ?\")\n .get(id) as any;\n return row ? mapSyncQueueRow(row) : null;\n }\n\n /**\n * Claim a batch of pending items whose next_attempt_at is in the past.\n * Returns them in creation order, oldest first. Marks them as in_flight\n * so a concurrent worker doesn't pick up the same items.\n */\n claimBatch(limit: number = 10): SyncQueueRow[] {\n const now = Math.floor(Date.now() / 1000);\n // The SELECT-then-UPDATE must run in a single transaction. better-sqlite3\n // is single-writer, but a crash between the two statements would leave\n // rows claimed-but-not-marked, and on restart another worker would\n // re-claim and double-process them.\n return this.db.transaction((): SyncQueueRow[] => {\n const rows = this.db\n .prepare(\n `SELECT * FROM sync_queue\n WHERE status = 'pending' AND next_attempt_at <= ?\n ORDER BY created_at ASC\n LIMIT ?`,\n )\n .all(now, limit) as any[];\n\n if (rows.length === 0) return [];\n\n const ids = rows.map((r) => r.id);\n const placeholders = ids.map(() => \"?\").join(\",\");\n this.db\n .prepare(\n `UPDATE sync_queue SET status = 'in_flight', updated_at = unixepoch()\n WHERE id IN (${placeholders})`,\n )\n .run(...ids);\n\n return rows.map((r) => ({\n ...mapSyncQueueRow(r),\n status: \"in_flight\" as const,\n }));\n })();\n }\n\n /** Mark a successfully-sent item as completed. */\n markCompleted(id: string): void {\n this.db\n .prepare(\n `UPDATE sync_queue\n SET status = 'completed',\n completed_at = unixepoch(),\n updated_at = unixepoch()\n WHERE id = ?`,\n )\n .run(id);\n }\n\n /**\n * Mark a send failure. Increments attempt count, schedules the next\n * retry using exponential backoff (base 5s, cap 1 hour). If max\n * attempts exceeded, marks the row as permanently failed.\n */\n markFailed(id: string, error: string): void {\n const row = this.getById(id);\n if (!row) return;\n\n const nextCount = row.attemptCount + 1;\n if (nextCount >= row.maxAttempts) {\n this.db\n .prepare(\n `UPDATE sync_queue\n SET status = 'failed',\n attempt_count = ?,\n last_error = ?,\n completed_at = unixepoch(),\n updated_at = unixepoch()\n WHERE id = ?`,\n )\n .run(nextCount, error.slice(0, 1000), id);\n return;\n }\n\n // Exp backoff: 5s, 10s, 20s, 40s, 80s, 160s, 320s, 640s, 1280s, 2560s\n // Capped at 3600s (1 hour). Adds up to 20% jitter to prevent thundering herd.\n const baseDelay = Math.min(5 * Math.pow(2, nextCount - 1), 3600);\n const jitter = Math.floor(baseDelay * (Math.random() * 0.2));\n const nextAttemptAt = Math.floor(Date.now() / 1000) + baseDelay + jitter;\n\n this.db\n .prepare(\n `UPDATE sync_queue\n SET status = 'pending',\n attempt_count = ?,\n next_attempt_at = ?,\n last_error = ?,\n updated_at = unixepoch()\n WHERE id = ?`,\n )\n .run(nextCount, nextAttemptAt, error.slice(0, 1000), id);\n }\n\n /** Stats for the `brainstorm sync status` command. */\n getStats(): {\n pending: number;\n inFlight: number;\n completed: number;\n failed: number;\n oldestPending: number | null;\n latestFailure: { id: string; error: string; attemptCount: number } | null;\n } {\n const counts = this.db\n .prepare(\n `SELECT status, COUNT(*) as count FROM sync_queue GROUP BY status`,\n )\n .all() as Array<{ status: string; count: number }>;\n\n const result = {\n pending: 0,\n inFlight: 0,\n completed: 0,\n failed: 0,\n oldestPending: null as number | null,\n latestFailure: null as {\n id: string;\n error: string;\n attemptCount: number;\n } | null,\n };\n for (const row of counts) {\n if (row.status === \"pending\") result.pending = row.count;\n else if (row.status === \"in_flight\") result.inFlight = row.count;\n else if (row.status === \"completed\") result.completed = row.count;\n else if (row.status === \"failed\") result.failed = row.count;\n }\n\n const oldest = this.db\n .prepare(\n `SELECT MIN(created_at) as ts FROM sync_queue WHERE status = 'pending'`,\n )\n .get() as { ts: number | null };\n result.oldestPending = oldest?.ts ?? null;\n\n const fail = this.db\n .prepare(\n `SELECT id, last_error, attempt_count FROM sync_queue\n WHERE status = 'failed'\n ORDER BY updated_at DESC LIMIT 1`,\n )\n .get() as any;\n if (fail) {\n result.latestFailure = {\n id: fail.id,\n error: fail.last_error ?? \"(unknown)\",\n attemptCount: fail.attempt_count,\n };\n }\n\n return result;\n }\n\n /** Drop completed rows older than the given age (for cleanup). */\n pruneCompleted(olderThanSeconds: number): number {\n const cutoff = Math.floor(Date.now() / 1000) - olderThanSeconds;\n const result = this.db\n .prepare(\n `DELETE FROM sync_queue\n WHERE status = 'completed' AND completed_at < ?`,\n )\n .run(cutoff);\n return result.changes;\n }\n}\n\nfunction mapSyncQueueRow(row: any): SyncQueueRow {\n return {\n id: row.id,\n kind: row.kind,\n method: row.method,\n path: row.path,\n body: row.body,\n idempotencyKey: row.idempotency_key,\n status: row.status,\n attemptCount: row.attempt_count,\n maxAttempts: row.max_attempts,\n nextAttemptAt: row.next_attempt_at,\n lastError: row.last_error,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n completedAt: row.completed_at,\n };\n}\n","/**\n * Team Repository — CRUD for orgs and team members.\n *\n * Manages org-level data: team membership, roles, per-user budgets.\n */\n\nimport type Database from \"better-sqlite3\";\nimport { randomUUID } from \"node:crypto\";\n\nexport type TeamRole =\n | \"admin\"\n | \"engineer\"\n | \"qa\"\n | \"designer\"\n | \"devops\"\n | \"compliance\";\n\nexport interface Org {\n id: string;\n name: string;\n githubOwner?: string;\n githubRepo?: string;\n settings: Record<string, unknown>;\n createdAt: number;\n}\n\nexport interface TeamMember {\n id: string;\n orgId: string;\n email: string;\n displayName: string;\n role: TeamRole;\n githubUsername?: string;\n budgetDaily?: number;\n budgetMonthly?: number;\n isActive: boolean;\n createdAt: number;\n updatedAt: number;\n}\n\nexport class OrgRepository {\n constructor(private db: Database.Database) {}\n\n create(\n name: string,\n opts?: {\n githubOwner?: string;\n githubRepo?: string;\n settings?: Record<string, unknown>;\n },\n ): Org {\n const id = randomUUID().slice(0, 12);\n const now = Math.floor(Date.now() / 1000);\n this.db\n .prepare(\n \"INSERT INTO orgs (id, name, github_owner, github_repo, settings_json, created_at) VALUES (?, ?, ?, ?, ?, ?)\",\n )\n .run(\n id,\n name,\n opts?.githubOwner ?? null,\n opts?.githubRepo ?? null,\n JSON.stringify(opts?.settings ?? {}),\n now,\n );\n return {\n id,\n name,\n githubOwner: opts?.githubOwner,\n githubRepo: opts?.githubRepo,\n settings: opts?.settings ?? {},\n createdAt: now,\n };\n }\n\n get(id: string): Org | null {\n const row = this.db\n .prepare(\"SELECT * FROM orgs WHERE id = ?\")\n .get(id) as any;\n if (!row) return null;\n return {\n id: row.id,\n name: row.name,\n githubOwner: row.github_owner,\n githubRepo: row.github_repo,\n settings: JSON.parse(row.settings_json ?? \"{}\"),\n createdAt: row.created_at,\n };\n }\n\n list(): Org[] {\n return (\n this.db\n .prepare(\"SELECT * FROM orgs ORDER BY created_at DESC\")\n .all() as any[]\n ).map((row) => ({\n id: row.id,\n name: row.name,\n githubOwner: row.github_owner,\n githubRepo: row.github_repo,\n settings: JSON.parse(row.settings_json ?? \"{}\"),\n createdAt: row.created_at,\n }));\n }\n\n updateSettings(id: string, settings: Record<string, unknown>): void {\n this.db\n .prepare(\"UPDATE orgs SET settings_json = ? WHERE id = ?\")\n .run(JSON.stringify(settings), id);\n }\n}\n\nexport class TeamMemberRepository {\n constructor(private db: Database.Database) {}\n\n add(\n orgId: string,\n member: {\n email: string;\n displayName: string;\n role?: TeamRole;\n githubUsername?: string;\n budgetDaily?: number;\n budgetMonthly?: number;\n },\n ): TeamMember {\n const id = randomUUID().slice(0, 12);\n const now = Math.floor(Date.now() / 1000);\n const role = member.role ?? \"engineer\";\n\n this.db\n .prepare(\n \"INSERT INTO team_members (id, org_id, email, display_name, role, github_username, budget_daily, budget_monthly, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n )\n .run(\n id,\n orgId,\n member.email,\n member.displayName,\n role,\n member.githubUsername ?? null,\n member.budgetDaily ?? null,\n member.budgetMonthly ?? null,\n now,\n now,\n );\n\n return {\n id,\n orgId,\n email: member.email,\n displayName: member.displayName,\n role,\n githubUsername: member.githubUsername,\n budgetDaily: member.budgetDaily,\n budgetMonthly: member.budgetMonthly,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n list(orgId: string): TeamMember[] {\n return (\n this.db\n .prepare(\n \"SELECT * FROM team_members WHERE org_id = ? AND is_active = 1 ORDER BY role, display_name\",\n )\n .all(orgId) as any[]\n ).map(toMember);\n }\n\n getByEmail(orgId: string, email: string): TeamMember | null {\n const row = this.db\n .prepare(\"SELECT * FROM team_members WHERE org_id = ? AND email = ?\")\n .get(orgId, email) as any;\n return row ? toMember(row) : null;\n }\n\n getById(id: string): TeamMember | null {\n const row = this.db\n .prepare(\"SELECT * FROM team_members WHERE id = ?\")\n .get(id) as any;\n return row ? toMember(row) : null;\n }\n\n updateRole(id: string, role: TeamRole): void {\n this.db\n .prepare(\n \"UPDATE team_members SET role = ?, updated_at = unixepoch() WHERE id = ?\",\n )\n .run(role, id);\n }\n\n updateBudget(id: string, daily?: number, monthly?: number): void {\n this.db\n .prepare(\n \"UPDATE team_members SET budget_daily = ?, budget_monthly = ?, updated_at = unixepoch() WHERE id = ?\",\n )\n .run(daily ?? null, monthly ?? null, id);\n }\n\n deactivate(id: string): void {\n this.db\n .prepare(\n \"UPDATE team_members SET is_active = 0, updated_at = unixepoch() WHERE id = ?\",\n )\n .run(id);\n }\n\n /** Get cost breakdown per user for an org in the current month. */\n costByUser(\n orgId: string,\n ): Array<{\n userId: string;\n email: string;\n displayName: string;\n role: string;\n totalCost: number;\n sessionCount: number;\n }> {\n const monthStart = getMonthStart();\n return this.db\n .prepare(\n `\n SELECT\n tm.id AS userId,\n tm.email,\n tm.display_name AS displayName,\n tm.role,\n COALESCE(SUM(cr.cost), 0) AS totalCost,\n COUNT(DISTINCT cr.session_id) AS sessionCount\n FROM team_members tm\n LEFT JOIN cost_records cr ON cr.user_id = tm.id AND cr.timestamp > ?\n WHERE tm.org_id = ? AND tm.is_active = 1\n GROUP BY tm.id\n ORDER BY totalCost DESC\n `,\n )\n .all(monthStart, orgId) as any[];\n }\n\n /** Get org-level budget totals for the current period. */\n orgBudgetSummary(orgId: string): {\n dailyCost: number;\n monthlyCost: number;\n sessionCount: number;\n } {\n const dayStart = getDayStart();\n const monthStart = getMonthStart();\n\n const daily =\n (\n this.db\n .prepare(\n \"SELECT COALESCE(SUM(cost), 0) AS c FROM cost_records WHERE org_id = ? AND timestamp > ?\",\n )\n .get(orgId, dayStart) as any\n )?.c ?? 0;\n\n const monthly =\n (\n this.db\n .prepare(\n \"SELECT COALESCE(SUM(cost), 0) AS c FROM cost_records WHERE org_id = ? AND timestamp > ?\",\n )\n .get(orgId, monthStart) as any\n )?.c ?? 0;\n\n const sessions =\n (\n this.db\n .prepare(\n \"SELECT COUNT(DISTINCT session_id) AS c FROM cost_records WHERE org_id = ? AND timestamp > ?\",\n )\n .get(orgId, monthStart) as any\n )?.c ?? 0;\n\n return { dailyCost: daily, monthlyCost: monthly, sessionCount: sessions };\n }\n}\n\nfunction toMember(row: any): TeamMember {\n return {\n id: row.id,\n orgId: row.org_id,\n email: row.email,\n displayName: row.display_name,\n role: row.role,\n githubUsername: row.github_username,\n budgetDaily: row.budget_daily,\n budgetMonthly: row.budget_monthly,\n isActive: !!row.is_active,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction getDayStart(): number {\n const now = new Date();\n return Math.floor(\n new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime() / 1000,\n );\n}\n\nfunction getMonthStart(): number {\n const now = new Date();\n return Math.floor(\n new Date(now.getFullYear(), now.getMonth(), 1).getTime() / 1000,\n );\n}\n","/**\n * Compliance Event Repository — logs and queries compliance events.\n *\n * Auto-logged on: budget exceedance, high-risk changeset execution,\n * permission escalation, model failover to non-approved provider.\n */\n\nimport type Database from \"better-sqlite3\";\nimport { randomUUID } from \"node:crypto\";\n\nexport type ComplianceSeverity = \"info\" | \"warning\" | \"critical\";\n\nexport interface ComplianceEvent {\n id: string;\n orgId: string;\n userId?: string;\n eventType: string;\n severity: ComplianceSeverity;\n description: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n}\n\nexport class ComplianceEventRepository {\n constructor(private db: Database.Database) {}\n\n log(\n orgId: string,\n event: {\n userId?: string;\n eventType: string;\n severity?: ComplianceSeverity;\n description: string;\n metadata?: Record<string, unknown>;\n },\n ): string {\n const id = randomUUID().slice(0, 12);\n this.db\n .prepare(\n \"INSERT INTO compliance_events (id, org_id, user_id, event_type, severity, description, metadata_json) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n )\n .run(\n id,\n orgId,\n event.userId ?? null,\n event.eventType,\n event.severity ?? \"info\",\n event.description,\n JSON.stringify(event.metadata ?? {}),\n );\n return id;\n }\n\n list(\n orgId: string,\n opts?: {\n severity?: ComplianceSeverity;\n eventType?: string;\n since?: number;\n limit?: number;\n },\n ): ComplianceEvent[] {\n let sql = \"SELECT * FROM compliance_events WHERE org_id = ?\";\n const params: any[] = [orgId];\n\n if (opts?.severity) {\n sql += \" AND severity = ?\";\n params.push(opts.severity);\n }\n if (opts?.eventType) {\n sql += \" AND event_type = ?\";\n params.push(opts.eventType);\n }\n if (opts?.since) {\n sql += \" AND created_at > ?\";\n params.push(opts.since);\n }\n\n sql += \" ORDER BY created_at DESC\";\n if (opts?.limit) {\n sql += \" LIMIT ?\";\n params.push(opts.limit);\n }\n\n return (this.db.prepare(sql).all(...params) as any[]).map(toEvent);\n }\n\n summary(\n orgId: string,\n since: number,\n ): {\n total: number;\n bySeverity: Record<ComplianceSeverity, number>;\n byType: Record<string, number>;\n } {\n const events = this.list(orgId, { since });\n const bySeverity: Record<string, number> = {\n info: 0,\n warning: 0,\n critical: 0,\n };\n const byType: Record<string, number> = {};\n\n for (const e of events) {\n bySeverity[e.severity] = (bySeverity[e.severity] ?? 0) + 1;\n byType[e.eventType] = (byType[e.eventType] ?? 0) + 1;\n }\n\n return { total: events.length, bySeverity: bySeverity as any, byType };\n }\n\n /** Generate a full compliance report for export. */\n generateReport(\n orgId: string,\n since: number,\n ): {\n period: { since: number; until: number };\n events: ComplianceEvent[];\n summary: ReturnType<ComplianceEventRepository[\"summary\"]>;\n } {\n const events = this.list(orgId, { since });\n return {\n period: { since, until: Math.floor(Date.now() / 1000) },\n events,\n summary: this.summary(orgId, since),\n };\n }\n}\n\nfunction toEvent(row: any): ComplianceEvent {\n return {\n id: row.id,\n orgId: row.org_id,\n userId: row.user_id,\n eventType: row.event_type,\n severity: row.severity,\n description: row.description,\n metadata: JSON.parse(row.metadata_json ?? \"{}\"),\n createdAt: row.created_at,\n };\n}\n","/**\n * RoutingAuditRepository — per-request BR envelope persistence.\n *\n * Stores the full BR `x-br-*` response envelope per chat completion, keyed\n * on `x-request-id` (from the response). Enables:\n *\n * 1. Verifiable audit chain — given an audit-hash, look up the full\n * routing context (model picked, why, cost, quality tier, confidence,\n * guardrail state).\n * 2. Trajectory enrichment — when submitting trajectories to BR via\n * /v1/agent/trajectory, attach the per-turn audit-hashes so the\n * submitted trail is continuous with BR's own evidence ledger.\n * 3. Operator observability — `storm dashboard` queries this table to\n * show \"last 10 routes\" with model/cost/confidence/savings.\n *\n * Complementary to `model_performance_v2` (Thompson-sampling aggregates).\n * This is the per-turn detail; that is the rolled-up summary.\n *\n * Drives Path-to-90 D5/D6/D8.\n */\n\nimport type Database from \"better-sqlite3\";\n\nexport interface RoutingAuditEntry {\n /** x-request-id — primary key. */\n requestId: string;\n /** x-br-audit-hash (64-hex chain pointer to BR's evidence ledger). */\n auditHash?: string;\n /** x-br-envelope mode (\"audit\" / \"enforce\" / etc.). */\n envelopeMode?: string;\n /** x-br-routed-model (the actual model BR routed to). */\n routedModel?: string;\n /** x-br-route-reason. */\n routeReason?: string;\n /** x-br-route-confidence (0-1). */\n routeConfidence?: number;\n /** x-br-selection-method. */\n selectionMethod?: string;\n /** x-br-selection-confidence (0-1). */\n selectionConfidence?: number;\n /** x-br-quality-tier (\"heuristic\"/\"learned\"/\"verified\"). */\n qualityTier?: string;\n /** x-br-quality-score (0-1). */\n qualityScore?: number;\n /** x-br-models-considered. */\n modelsConsidered?: number;\n /** x-br-actual-cost (USD). */\n actualCostUsd?: number;\n /** x-br-estimated-cost (USD). */\n estimatedCostUsd?: number;\n /** x-br-routing-savings (USD vs naive routing). */\n routingSavingsUsd?: number;\n /** x-br-budget-remaining (USD). */\n budgetRemainingUsd?: number;\n /** x-br-total-latency-ms (end-to-end). */\n totalLatencyMs?: number;\n /** x-br-provider-latency-ms. */\n providerLatencyMs?: number;\n /** x-br-routing-overhead-ms. */\n routingOverheadMs?: number;\n /** x-br-guardian-overhead-ms. */\n guardianOverheadMs?: number;\n /** x-br-guardian-status. */\n guardianStatus?: string;\n /** x-br-guardrail-status (\"warn\"/\"enforce\"/\"off\"). */\n guardrailStatus?: string;\n /** x-br-reputation-tier (caller). */\n reputationTier?: string;\n /** x-br-tier (subscription tier). */\n tier?: string;\n /** x-br-degradation-level (0 nominal, >0 degraded). */\n degradationLevel?: number;\n /** x-br-deprecation (sunset notice). */\n deprecationNotice?: string;\n /** x-br-cache state (\"hit\"/\"miss\"/\"skip\"). */\n cacheState?: string;\n /** x-br-cache-age (ms when cache=hit). */\n cacheAgeMs?: number;\n /** x-br-cold-start-ms. */\n coldStartMs?: number;\n /** x-br-build (BR commit SHA short). */\n brBuild?: string;\n /** x-br-routing-reasoning (parsed JSON or raw string). */\n routingReasoning?: unknown;\n /** x-br-context (parsed JSON or raw string). */\n context?: unknown;\n /** Unix seconds; defaults to now if omitted. */\n capturedAt?: number;\n}\n\n/** Per-row shape returned from listRecent — flat for ergonomic UI rendering. */\nexport interface RoutingAuditRow extends Omit<RoutingAuditEntry, \"capturedAt\"> {\n capturedAt: number;\n}\n\nexport class RoutingAuditRepository {\n constructor(private db: Database.Database) {}\n\n /**\n * Insert a routing audit entry. Idempotent on `requestId` — re-inserting\n * the same request_id is treated as an update (BR may emit a corrected\n * envelope on retry). `audit_hash` is what changes; we keep the latest.\n */\n insert(entry: RoutingAuditEntry): void {\n if (!entry.requestId || entry.requestId.length === 0) {\n throw new Error(\"RoutingAuditRepository.insert: requestId required\");\n }\n const capturedAt = entry.capturedAt ?? Math.floor(Date.now() / 1000);\n this.db\n .prepare(\n `INSERT OR REPLACE INTO routing_audit (\n request_id, audit_hash, envelope_mode, routed_model, route_reason,\n route_confidence, selection_method, selection_confidence,\n quality_tier, quality_score, models_considered,\n actual_cost_usd, estimated_cost_usd, routing_savings_usd,\n budget_remaining_usd, total_latency_ms, provider_latency_ms,\n routing_overhead_ms, guardian_overhead_ms, guardian_status,\n guardrail_status, reputation_tier, tier, degradation_level,\n deprecation_notice, cache_state, cache_age_ms, cold_start_ms,\n br_build, routing_reasoning_json, context_json, captured_at\n ) VALUES (\n @requestId, @auditHash, @envelopeMode, @routedModel, @routeReason,\n @routeConfidence, @selectionMethod, @selectionConfidence,\n @qualityTier, @qualityScore, @modelsConsidered,\n @actualCostUsd, @estimatedCostUsd, @routingSavingsUsd,\n @budgetRemainingUsd, @totalLatencyMs, @providerLatencyMs,\n @routingOverheadMs, @guardianOverheadMs, @guardianStatus,\n @guardrailStatus, @reputationTier, @tier, @degradationLevel,\n @deprecationNotice, @cacheState, @cacheAgeMs, @coldStartMs,\n @brBuild, @routingReasoningJson, @contextJson, @capturedAt\n )`,\n )\n .run({\n requestId: entry.requestId,\n auditHash: entry.auditHash ?? null,\n envelopeMode: entry.envelopeMode ?? null,\n routedModel: entry.routedModel ?? null,\n routeReason: entry.routeReason ?? null,\n routeConfidence: entry.routeConfidence ?? null,\n selectionMethod: entry.selectionMethod ?? null,\n selectionConfidence: entry.selectionConfidence ?? null,\n qualityTier: entry.qualityTier ?? null,\n qualityScore: entry.qualityScore ?? null,\n modelsConsidered: entry.modelsConsidered ?? null,\n actualCostUsd: entry.actualCostUsd ?? null,\n estimatedCostUsd: entry.estimatedCostUsd ?? null,\n routingSavingsUsd: entry.routingSavingsUsd ?? null,\n budgetRemainingUsd: entry.budgetRemainingUsd ?? null,\n totalLatencyMs: entry.totalLatencyMs ?? null,\n providerLatencyMs: entry.providerLatencyMs ?? null,\n routingOverheadMs: entry.routingOverheadMs ?? null,\n guardianOverheadMs: entry.guardianOverheadMs ?? null,\n guardianStatus: entry.guardianStatus ?? null,\n guardrailStatus: entry.guardrailStatus ?? null,\n reputationTier: entry.reputationTier ?? null,\n tier: entry.tier ?? null,\n degradationLevel: entry.degradationLevel ?? null,\n deprecationNotice: entry.deprecationNotice ?? null,\n cacheState: entry.cacheState ?? null,\n cacheAgeMs: entry.cacheAgeMs ?? null,\n coldStartMs: entry.coldStartMs ?? null,\n brBuild: entry.brBuild ?? null,\n routingReasoningJson:\n entry.routingReasoning !== undefined\n ? JSON.stringify(entry.routingReasoning)\n : null,\n contextJson:\n entry.context !== undefined ? JSON.stringify(entry.context) : null,\n capturedAt,\n });\n }\n\n /** Look up a single audit entry by request id. */\n get(requestId: string): RoutingAuditRow | null {\n const row = this.db\n .prepare(\"SELECT * FROM routing_audit WHERE request_id = ?\")\n .get(requestId) as any;\n return row ? rowToEntry(row) : null;\n }\n\n /** Look up an entry by BR's audit_hash (the chain pointer). */\n lookupByAuditHash(auditHash: string): RoutingAuditRow | null {\n const row = this.db\n .prepare(\"SELECT * FROM routing_audit WHERE audit_hash = ?\")\n .get(auditHash) as any;\n return row ? rowToEntry(row) : null;\n }\n\n /**\n * Recent entries, newest first. Drives dashboard \"last N routes\" view.\n * Cap default at 50 to keep render cost bounded.\n */\n listRecent(limit = 50): RoutingAuditRow[] {\n const rows = this.db\n .prepare(\"SELECT * FROM routing_audit ORDER BY captured_at DESC LIMIT ?\")\n .all(limit) as any[];\n return rows.map(rowToEntry);\n }\n\n /**\n * Return all audit_hash values for a window. Used by trajectory submission\n * to attach BR's chain pointers to outgoing trajectory payloads — closes\n * the evidence loop documented in path-to-90 plan P2.\n */\n listAuditHashesSince(sinceUnixSec: number): string[] {\n const rows = this.db\n .prepare(\n `SELECT audit_hash FROM routing_audit\n WHERE captured_at >= ? AND audit_hash IS NOT NULL\n ORDER BY captured_at ASC`,\n )\n .all(sinceUnixSec) as Array<{ audit_hash: string }>;\n return rows.map((r) => r.audit_hash);\n }\n\n /** Count rows. Useful in tests + dashboard ops summary. */\n count(): number {\n const row = this.db\n .prepare(\"SELECT COUNT(*) as n FROM routing_audit\")\n .get() as { n: number };\n return row.n;\n }\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\nfunction rowToEntry(row: any): RoutingAuditRow {\n return {\n requestId: row.request_id,\n auditHash: row.audit_hash ?? undefined,\n envelopeMode: row.envelope_mode ?? undefined,\n routedModel: row.routed_model ?? undefined,\n routeReason: row.route_reason ?? undefined,\n routeConfidence: row.route_confidence ?? undefined,\n selectionMethod: row.selection_method ?? undefined,\n selectionConfidence: row.selection_confidence ?? undefined,\n qualityTier: row.quality_tier ?? undefined,\n qualityScore: row.quality_score ?? undefined,\n modelsConsidered: row.models_considered ?? undefined,\n actualCostUsd: row.actual_cost_usd ?? undefined,\n estimatedCostUsd: row.estimated_cost_usd ?? undefined,\n routingSavingsUsd: row.routing_savings_usd ?? undefined,\n budgetRemainingUsd: row.budget_remaining_usd ?? undefined,\n totalLatencyMs: row.total_latency_ms ?? undefined,\n providerLatencyMs: row.provider_latency_ms ?? undefined,\n routingOverheadMs: row.routing_overhead_ms ?? undefined,\n guardianOverheadMs: row.guardian_overhead_ms ?? undefined,\n guardianStatus: row.guardian_status ?? undefined,\n guardrailStatus: row.guardrail_status ?? undefined,\n reputationTier: row.reputation_tier ?? undefined,\n tier: row.tier ?? undefined,\n degradationLevel: row.degradation_level ?? undefined,\n deprecationNotice: row.deprecation_notice ?? undefined,\n cacheState: row.cache_state ?? undefined,\n cacheAgeMs: row.cache_age_ms ?? undefined,\n coldStartMs: row.cold_start_ms ?? undefined,\n brBuild: row.br_build ?? undefined,\n routingReasoning: safeJsonParse(row.routing_reasoning_json),\n context: safeJsonParse(row.context_json),\n capturedAt: row.captured_at,\n };\n}\n\nfunction safeJsonParse(raw: string | null): unknown {\n if (raw == null || raw.length === 0) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n","/**\n * P2b wiring helper — turns a parsed BrEnvelope into a row insert against\n * routing_audit. Returns a listener that's safe to hand to\n * `createProviderRegistry({ onEnvelope })`.\n *\n * The producer (providers package) doesn't know about the db; the consumer\n * (cli bootstrap) wires the two ends together via this helper. Crossing the\n * package boundary that way keeps providers DB-free.\n *\n * Failure semantics: per-turn persistence MUST NOT crash the request. Any\n * thrown error is swallowed with an opt-in `onError` hook (defaults to a\n * console.warn). The fetch path in brainstorm-saas.ts already wraps the\n * listener in a .catch(); this is belt-and-braces for synchronous throws\n * inside the listener.\n */\n\nimport type {\n RoutingAuditRepository,\n RoutingAuditEntry,\n} from \"./routing-audit-repository.js\";\n\n/** Shape of the envelope this helper consumes. Kept structural (not an\n * import of BrEnvelope) so @brainst0rm/db does not depend on\n * @brainst0rm/providers — the dependency must point one way only.\n *\n * Every field is optional because BR may omit any header on a degraded\n * response; the parser produces undefined rather than failing. We only\n * require `requestId` because it's the table's primary key.\n *\n * Field coverage matches BrEnvelope 1:1 by name. Not every field is\n * persisted to routing_audit (see PERSISTED_BR_ENVELOPE_FIELDS and the\n * IGNORED set below) — but accepting the field here means future\n * schema-extension PRs don't need to re-thread the type. The drift\n * test in __tests__/routing-audit-writer-drift.test.ts asserts every\n * parsed envelope field appears either in the persisted set or the\n * documented-ignored set.\n */\nexport interface BrEnvelopeLike {\n // identity\n requestId?: string;\n build?: string;\n envelope?: string;\n tier?: string;\n reputationTier?: string;\n modelContract?: string;\n // cost\n actualCost?: number;\n estimatedCost?: number;\n estimatedCostCents?: number;\n routingSavings?: number;\n // budget\n budgetRemaining?: number;\n tokensRemaining?: number;\n requestsRemaining?: number;\n // latency\n totalLatencyMs?: number;\n providerLatencyMs?: number;\n routingOverheadMs?: number;\n guardianOverheadMs?: number;\n // routing\n routedModel?: string;\n routeReason?: string;\n routeConfidence?: number;\n routingReasoning?: unknown;\n selectionMethod?: string;\n selectionConfidence?: number;\n modelsConsidered?: number;\n qualityTier?: string;\n qualityScore?: number;\n // task complexity\n complexityLevel?: string;\n complexityScore?: number;\n // audit\n auditHash?: string;\n context?: unknown;\n // guardian / guardrail\n guardianStatus?: string;\n guardrailStatus?: string;\n guardrailSummary?: string;\n guardrailActions?: unknown;\n // lifecycle\n degradationLevel?: number;\n deprecation?: string;\n // cache\n cache?: string;\n cacheAge?: number;\n cacheSimilarity?: number;\n coldStartMs?: number;\n // drift sentinel from parser; we explicitly do not persist this —\n // it's a parser-internal counter for fields outside the canonical set\n unknownHeaders?: string[];\n}\n\n/**\n * Envelope fields the routing_audit table currently persists. Updates\n * to this set MUST accompany a schema migration in client.ts.\n * The drift test asserts every persisted field has a column.\n */\nexport const PERSISTED_BR_ENVELOPE_FIELDS = [\n \"requestId\",\n \"build\",\n \"envelope\",\n \"tier\",\n \"reputationTier\",\n \"actualCost\",\n \"estimatedCost\",\n \"routingSavings\",\n \"budgetRemaining\",\n \"totalLatencyMs\",\n \"providerLatencyMs\",\n \"routingOverheadMs\",\n \"guardianOverheadMs\",\n \"routedModel\",\n \"routeReason\",\n \"routeConfidence\",\n \"routingReasoning\",\n \"selectionMethod\",\n \"selectionConfidence\",\n \"modelsConsidered\",\n \"qualityTier\",\n \"qualityScore\",\n \"auditHash\",\n \"context\",\n \"guardianStatus\",\n \"guardrailStatus\",\n \"degradationLevel\",\n \"deprecation\",\n \"cache\",\n \"cacheAge\",\n \"coldStartMs\",\n] as const;\n\n/**\n * Envelope fields the parser captures but we intentionally do NOT\n * persist. Each entry must have a one-line rationale. Adding a field\n * here is documented technical debt; removing one is a schema bump.\n */\nexport const IGNORED_BR_ENVELOPE_FIELDS: Readonly<Record<string, string>> = {\n // routing_audit is per-request; subscription tier metadata doesn't\n // vary per-row and adds no audit value vs. cost/route fields\n modelContract: \"static per subscription; carried in /v1/ops/status\",\n // cents column would duplicate `actualCost` (USD) with a rounding\n // hazard; downstream consumers should derive from actualCost\n estimatedCostCents: \"duplicates estimatedCost (USD) with rounding hazard\",\n // budget snapshots — drift fast, low audit value, surface via /budget\n tokensRemaining: \"drifts every request; surface via storm budget\",\n requestsRemaining: \"drifts every request; surface via storm budget\",\n // complexity is a routing input, captured in routing_reasoning JSON\n complexityLevel: \"captured inside routing_reasoning JSON blob\",\n complexityScore: \"captured inside routing_reasoning JSON blob\",\n // guardrail summary + actions are display-only; full structure\n // belongs in compliance_events table, not the routing per-row audit\n guardrailSummary: \"display-only; full structure goes in compliance_events\",\n guardrailActions: \"display-only; full structure goes in compliance_events\",\n // similarity score is internal to BR's cache implementation, not\n // a routing decision factor we audit on\n cacheSimilarity: \"BR-internal cache implementation detail\",\n // parser-internal drift sentinel — should always be [] in healthy state;\n // non-empty entries are alerted at parse time, not stored per-row\n unknownHeaders: \"parser-internal drift sentinel; alerts at parse time\",\n};\n\nexport interface WireRoutingAuditOptions {\n onError?: (err: unknown, envelope: BrEnvelopeLike) => void;\n}\n\nexport function envelopeToAuditEntry(\n env: BrEnvelopeLike,\n): RoutingAuditEntry | null {\n // Without a requestId we have no primary key; drop silently. This is the\n // expected path when a proxy strips x-request-id, NOT a bug.\n if (!env.requestId) return null;\n return {\n requestId: env.requestId,\n auditHash: env.auditHash,\n envelopeMode: env.envelope,\n routedModel: env.routedModel,\n routeReason: env.routeReason,\n routeConfidence: env.routeConfidence,\n selectionMethod: env.selectionMethod,\n selectionConfidence: env.selectionConfidence,\n qualityTier: env.qualityTier,\n qualityScore: env.qualityScore,\n modelsConsidered: env.modelsConsidered,\n actualCostUsd: env.actualCost,\n estimatedCostUsd: env.estimatedCost,\n routingSavingsUsd: env.routingSavings,\n budgetRemainingUsd: env.budgetRemaining,\n totalLatencyMs: env.totalLatencyMs,\n providerLatencyMs: env.providerLatencyMs,\n routingOverheadMs: env.routingOverheadMs,\n guardianOverheadMs: env.guardianOverheadMs,\n guardianStatus: env.guardianStatus,\n guardrailStatus: env.guardrailStatus,\n reputationTier: env.reputationTier,\n tier: env.tier,\n degradationLevel: env.degradationLevel,\n deprecationNotice: env.deprecation,\n cacheState: env.cache,\n cacheAgeMs: env.cacheAge,\n coldStartMs: env.coldStartMs,\n brBuild: env.build,\n routingReasoning: env.routingReasoning,\n context: env.context,\n };\n}\n\n/**\n * Build the listener. Pass the returned function as `onEnvelope` to\n * `createProviderRegistry`. Each BR response flows through here and lands\n * as a row in routing_audit, keyed on x-request-id.\n */\nexport function wireRoutingAudit(\n repo: RoutingAuditRepository,\n options: WireRoutingAuditOptions = {},\n): (env: BrEnvelopeLike) => void {\n const onError =\n options.onError ??\n ((err: unknown) => {\n // Best-effort write — don't crash the agent because the audit\n // table is unavailable. Surface the error so a human can see it.\n // eslint-disable-next-line no-console\n console.warn(\"[routing-audit] insert failed:\", err);\n });\n return (env: BrEnvelopeLike) => {\n try {\n const entry = envelopeToAuditEntry(env);\n if (entry) repo.insert(entry);\n } catch (err) {\n onError(err, env);\n }\n };\n}\n"],"mappings":"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","names":["randomUUID"]}