circuschief 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/package.json +1 -1
  2. package/packages/server/src/agents/adapters/CodexAdapter.js +5 -4
  3. package/packages/server/src/api/canvas.js +22 -57
  4. package/packages/server/src/api/index.js +2 -0
  5. package/packages/server/src/api/kanban.js +4 -2
  6. package/packages/server/src/api/projects-helpers.js +20 -3
  7. package/packages/server/src/api/projects-session-helpers.js +35 -4
  8. package/packages/server/src/api/projects.js +11 -0
  9. package/packages/server/src/api/providers.js +11 -1
  10. package/packages/server/src/api/sessions-commands.js +35 -17
  11. package/packages/server/src/api/sessions-draft.js +1 -0
  12. package/packages/server/src/api/sessions-lifecycle.js +10 -10
  13. package/packages/server/src/api/sessions-patch.js +4 -0
  14. package/packages/server/src/api/sessions.js +6 -5
  15. package/packages/server/src/api/settings.js +52 -4
  16. package/packages/server/src/database.js +0 -2
  17. package/packages/server/src/db/ConversationRepository.js +16 -3
  18. package/packages/server/src/db/DatabaseManager.js +5 -1
  19. package/packages/server/src/db/ProjectDefaultsRepository.js +50 -40
  20. package/packages/server/src/db/ProviderRepository.js +87 -32
  21. package/packages/server/src/db/SessionRepository.js +13 -8
  22. package/packages/server/src/db/SettingsRepository.js +44 -16
  23. package/packages/server/src/db/conversation-helpers.js +1 -0
  24. package/packages/server/src/db/index.js +0 -3
  25. package/packages/server/src/db/migrations/index.js +36 -200
  26. package/packages/server/src/db/seedBaselineData.js +137 -0
  27. package/packages/server/src/db/session-helpers.js +9 -3
  28. package/packages/server/src/middleware/sessionLookup.js +81 -8
  29. package/packages/server/src/schema.sql +157 -132
  30. package/packages/server/src/scripts/backupDatabase.js +21 -0
  31. package/packages/server/src/scripts/dbUtils.js +81 -0
  32. package/packages/server/src/scripts/inspectDatabaseSchema.js +81 -0
  33. package/packages/server/src/scripts/validateDatabaseBaseline.js +212 -0
  34. package/packages/server/src/services/agentCallLogger.js +1 -1
  35. package/packages/server/src/services/codexSpawnHelper.js +9 -0
  36. package/packages/server/src/services/commandButtonPrompts.js +48 -0
  37. package/packages/server/src/services/commandRunner.js +7 -1
  38. package/packages/server/src/services/draftSessionService.js +2 -0
  39. package/packages/server/src/services/e2eSpawnCapture.js +147 -0
  40. package/packages/server/src/services/gitCommitAttribution.js +120 -0
  41. package/packages/server/src/services/gitService.js +11 -2
  42. package/packages/server/src/services/gitSessionSetup.js +11 -1
  43. package/packages/server/src/services/kanbanTriggers.js +6 -3
  44. package/packages/server/src/services/nodeSpawnHelper.js +9 -0
  45. package/packages/server/src/services/prUrlService.js +3 -3
  46. package/packages/server/src/services/queryParamBuilder.js +90 -0
  47. package/packages/server/src/services/sessionDuplicator.js +1 -5
  48. package/packages/server/src/services/sessionExecution.js +56 -106
  49. package/packages/server/src/services/sessionPrompts.js +16 -47
  50. package/packages/server/src/services/sessionProvider.js +16 -8
  51. package/packages/server/src/services/streamEventCallbacks.js +72 -40
  52. package/packages/server/src/services/streamEventHandler.js +13 -2
  53. package/packages/server/src/services/streamUsageHandler.js +6 -0
  54. package/packages/server/src/services/summaryClaudeClient.js +37 -12
  55. package/packages/server/src/services/summaryModelClient.js +154 -0
  56. package/packages/server/src/services/summaryModelResolver.js +148 -0
  57. package/packages/server/src/services/summaryService.js +11 -7
  58. package/packages/server/src/services/summaryStaleCheck.js +23 -4
  59. package/packages/server/src/services/templateTriggerService.js +3 -1
  60. package/packages/server/src/services/usageTracker.js +5 -1
  61. package/packages/server/src/services/visibleFinalErrorMessage.js +123 -0
  62. package/packages/shared/src/constants.js +4 -2
  63. package/packages/shared/src/contracts/commandButtons.js +16 -2
  64. package/packages/shared/src/contracts/projects.js +4 -2
  65. package/packages/shared/src/contracts/providers.js +60 -0
  66. package/packages/shared/src/contracts/sessions.js +2 -1
  67. package/packages/shared/src/contracts/templates.js +2 -2
  68. package/packages/shared/src/types.js +1 -9
  69. package/packages/shared/src/utils.js +11 -19
  70. package/packages/web/dist/assets/ActiveSessionsView-B0XHqLmv.js +1 -0
  71. package/packages/web/dist/assets/{AgentLogsView-Cdw4nmvd.js → AgentLogsView-DmsjUMlB.js} +2 -2
  72. package/packages/web/dist/assets/ApiClient-C3ztI9s9.js +1 -0
  73. package/packages/web/dist/assets/ArchiveConfirmModal-BlCyn5Vt.js +1 -0
  74. package/packages/web/dist/assets/ArchiveConfirmModal-DeoCVGXt.css +1 -0
  75. package/packages/web/dist/assets/{CommandButtonDetailView-DnFhJY5A.js → CommandButtonDetailView-CdSCPp78.js} +1 -1
  76. package/packages/web/dist/assets/EffortLevelSelector-hc2MNKg6.js +1 -0
  77. package/packages/web/dist/assets/{GeneralSettingsView-CQkmdczf.js → GeneralSettingsView-D1nI8_zk.js} +1 -1
  78. package/packages/web/dist/assets/InputWithButton-CAkttyqx.js +1 -0
  79. package/packages/web/dist/assets/{InputWithButton-cYdrEmTs.css → InputWithButton-D9HMvfR5.css} +1 -1
  80. package/packages/web/dist/assets/{InterpolationHelp-PfYR3KJo.js → InterpolationHelp-BO1j9Z3_.js} +1 -1
  81. package/packages/web/dist/assets/MarkdownEditor-ucRAP_UM.js +2 -0
  82. package/packages/web/dist/assets/{ModelSelector-BZOT1Jc6.css → ModelSelector-BSxKUSus.css} +1 -1
  83. package/packages/web/dist/assets/ModelSelector-CwTz8ZWO.js +1 -0
  84. package/packages/web/dist/assets/NewSessionView-BDPb-1qr.css +1 -0
  85. package/packages/web/dist/assets/NewSessionView-BsDrp8mj.js +3 -0
  86. package/packages/web/dist/assets/ProjectEditView-CwTOeSun.js +1 -0
  87. package/packages/web/dist/assets/ProjectEditView-J15mcsWz.css +1 -0
  88. package/packages/web/dist/assets/{ProjectListView-CuYMmd3O.js → ProjectListView-DcNyuINs.js} +1 -1
  89. package/packages/web/dist/assets/{ProjectNewView-CNaA4Maf.js → ProjectNewView-B5YV62hv.js} +1 -1
  90. package/packages/web/dist/assets/ProvidersView-bZemq_Rv.css +1 -0
  91. package/packages/web/dist/assets/ProvidersView-nY9GnDdO.js +1 -0
  92. package/packages/web/dist/assets/QuickResponseSettings-B352c75l.css +1 -0
  93. package/packages/web/dist/assets/QuickResponseSettings-BQwQXuL7.js +1 -0
  94. package/packages/web/dist/assets/{QuickResponsesPanel-DIBQFj0W.css → QuickResponsesPanel-BlFDvnZ2.css} +1 -1
  95. package/packages/web/dist/assets/{QuickResponsesPanel-BqMYSHb0.js → QuickResponsesPanel-BzSYcCSP.js} +1 -1
  96. package/packages/web/dist/assets/{ResizableTextarea-wYF3K2RO.js → ResizableTextarea-B3YIdIXv.js} +1 -1
  97. package/packages/web/dist/assets/{SessionCard-bLaQEWWX.js → SessionCard-CjE1tXiT.js} +1 -1
  98. package/packages/web/dist/assets/SessionDetailView-3cPZrbS3.js +36 -0
  99. package/packages/web/dist/assets/SessionDetailView-CZRZMrfM.css +1 -0
  100. package/packages/web/dist/assets/SessionFormOptions-B6AxyREh.js +1 -0
  101. package/packages/web/dist/assets/SessionFormOptions-BpUALRKn.css +1 -0
  102. package/packages/web/dist/assets/SessionListView-B5_6gW49.css +1 -0
  103. package/packages/web/dist/assets/SessionListView-CLXBfLcq.js +1 -0
  104. package/packages/web/dist/assets/{SessionLogStream-DTnDAF95.js → SessionLogStream-LlZ3z_Xj.js} +1 -1
  105. package/packages/web/dist/assets/{SettingsView-DNLUSsHV.js → SettingsView-CTGiGvR2.js} +1 -1
  106. package/packages/web/dist/assets/{SlashCommandWizard-CRGFaO8t.js → SlashCommandWizard-Cy04d7-o.js} +1 -1
  107. package/packages/web/dist/assets/{SlashCommandWizard-Dn7sNaBd.css → SlashCommandWizard-DJzw3LP5.css} +1 -1
  108. package/packages/web/dist/assets/SummarySettingsView-BR2ZjEa3.js +1 -0
  109. package/packages/web/dist/assets/SummarySettingsView-l2bxHmZZ.css +1 -0
  110. package/packages/web/dist/assets/TemplateDetailView-DH6Oswsp.js +1 -0
  111. package/packages/web/dist/assets/{commandButtons-Bbjf3fCt.js → commandButtons-BfqR-fqq.js} +1 -1
  112. package/packages/web/dist/assets/index-1zziPL6l.js +1 -0
  113. package/packages/web/dist/assets/index-7kzHPxSF.js +1 -0
  114. package/packages/web/dist/assets/index-B0N_obMc.js +1 -0
  115. package/packages/web/dist/assets/index-BNk_gdfI.js +1 -0
  116. package/packages/web/dist/assets/{index-gmCCsCQ1.css → index-BY174HVJ.css} +1 -1
  117. package/packages/web/dist/assets/index-CSqaAH-0.js +1 -0
  118. package/packages/web/dist/assets/index-C_q4WlK8.js +1 -0
  119. package/packages/web/dist/assets/index-D1wpU4y0.js +7 -0
  120. package/packages/web/dist/assets/index-D5zCA8sD.js +1 -0
  121. package/packages/web/dist/assets/index-DGR8ELWY.js +1 -0
  122. package/packages/web/dist/assets/index-DHga8pXo.js +1 -0
  123. package/packages/web/dist/assets/index-DSby02Wl.js +1 -0
  124. package/packages/web/dist/assets/{index-Cf6vdW-B.js → index-DgkC10TW.js} +3 -3
  125. package/packages/web/dist/assets/index-DqjXJTVI.js +1 -0
  126. package/packages/web/dist/assets/{index-Bs7Qf5D6.js → index-DtfUt785.js} +2 -2
  127. package/packages/web/dist/assets/index-_4S2uLDI.js +1 -0
  128. package/packages/web/dist/assets/{index-BQL_L4gL.js → index-fK8FIZgP.js} +15 -14
  129. package/packages/web/dist/assets/index-gmiZeFXN.js +1 -0
  130. package/packages/web/dist/assets/index-irD539ZM.js +3 -0
  131. package/packages/web/dist/assets/index-yq-E1Y00.js +1 -0
  132. package/packages/web/dist/assets/{projects-CPt3AB7U.js → projects-DXYQNJIi.js} +1 -1
  133. package/packages/web/dist/assets/{providers-ChfeMvUq.js → providers-1bnH-exJ.js} +1 -1
  134. package/packages/web/dist/assets/sessions-6zGUlFrt.js +1 -0
  135. package/packages/web/dist/assets/settings-MbfRir0d.js +1 -0
  136. package/packages/web/dist/index.html +2 -2
  137. package/packages/server/src/api/sessions-notes.js +0 -51
  138. package/packages/server/src/db/SessionNoteRepository.js +0 -60
  139. package/packages/server/src/db/migrations/canvasItemsMigrations.js +0 -109
  140. package/packages/server/src/db/migrations/conversationsMigrations.js +0 -183
  141. package/packages/server/src/db/migrations/kanbanMigrations.js +0 -99
  142. package/packages/server/src/db/migrations/miscMigrations.js +0 -369
  143. package/packages/server/src/db/migrations/projectsMigrations.js +0 -99
  144. package/packages/server/src/db/migrations/sessionsMigrations.js +0 -282
  145. package/packages/web/dist/assets/ActiveSessionsView-UCbQrF1b.js +0 -1
  146. package/packages/web/dist/assets/ApiClient-CWbXWDUY.js +0 -1
  147. package/packages/web/dist/assets/ArchiveConfirmModal-BQ-4gI0R.css +0 -1
  148. package/packages/web/dist/assets/ArchiveConfirmModal-J48eh3zw.js +0 -1
  149. package/packages/web/dist/assets/EffortLevelSelector-bXbPo4Zw.js +0 -1
  150. package/packages/web/dist/assets/InputWithButton-XyM3k6lN.js +0 -1
  151. package/packages/web/dist/assets/MarkdownEditor-P8F5kO-o.js +0 -2
  152. package/packages/web/dist/assets/ModelSelector-CowKfGMP.js +0 -1
  153. package/packages/web/dist/assets/NewSessionView-D_Hi7M9g.css +0 -1
  154. package/packages/web/dist/assets/NewSessionView-DkjFLvHU.js +0 -3
  155. package/packages/web/dist/assets/ProjectEditView-CpeKj-_w.css +0 -1
  156. package/packages/web/dist/assets/ProjectEditView-embVT7NC.js +0 -1
  157. package/packages/web/dist/assets/ProvidersView-C7rydtOd.js +0 -1
  158. package/packages/web/dist/assets/ProvidersView-DE82G_5W.css +0 -1
  159. package/packages/web/dist/assets/QuickResponseSettings-B8188A1D.css +0 -1
  160. package/packages/web/dist/assets/QuickResponseSettings-BTQEKhwJ.js +0 -1
  161. package/packages/web/dist/assets/SessionDetailView-Cv-xMzXp.css +0 -1
  162. package/packages/web/dist/assets/SessionDetailView-CvQOUsW2.js +0 -36
  163. package/packages/web/dist/assets/SessionFormOptions-3pzbgI2Q.js +0 -1
  164. package/packages/web/dist/assets/SessionFormOptions-DhhIkjIS.css +0 -1
  165. package/packages/web/dist/assets/SessionListView-Dranfb72.js +0 -1
  166. package/packages/web/dist/assets/SessionListView-fHlQyecX.css +0 -1
  167. package/packages/web/dist/assets/SummarySettingsView-C7G_suHp.js +0 -1
  168. package/packages/web/dist/assets/SummarySettingsView-DcsmSVJI.css +0 -1
  169. package/packages/web/dist/assets/TemplateDetailView-B78_DLMR.js +0 -1
  170. package/packages/web/dist/assets/index--V7c-VZf.js +0 -1
  171. package/packages/web/dist/assets/index-8Q04yd7H.js +0 -1
  172. package/packages/web/dist/assets/index-B47XRBDH.js +0 -1
  173. package/packages/web/dist/assets/index-BXbgZrhS.js +0 -1
  174. package/packages/web/dist/assets/index-CGhDVPen.js +0 -1
  175. package/packages/web/dist/assets/index-CKcRO1A6.js +0 -1
  176. package/packages/web/dist/assets/index-CTq-SLIW.js +0 -1
  177. package/packages/web/dist/assets/index-CYyos3iC.js +0 -1
  178. package/packages/web/dist/assets/index-CsCREAxF.js +0 -1
  179. package/packages/web/dist/assets/index-DJTTk_8T.js +0 -3
  180. package/packages/web/dist/assets/index-DPqUJ5JK.js +0 -1
  181. package/packages/web/dist/assets/index-EwAe1dKg.js +0 -1
  182. package/packages/web/dist/assets/index-JBA8axyA.js +0 -1
  183. package/packages/web/dist/assets/index-JkVHFtK5.js +0 -7
  184. package/packages/web/dist/assets/index-gMPUwT55.js +0 -1
  185. package/packages/web/dist/assets/index-wadc_0zT.js +0 -1
  186. package/packages/web/dist/assets/sessions-CwPsJOb1.js +0 -1
  187. package/packages/web/dist/assets/settings-BOj6wq6t.js +0 -1
@@ -1,5 +1,9 @@
1
1
  import { sessions, projects } from '../database.js';
2
2
 
3
+ const SESSION_NOT_FOUND = 'Session not found';
4
+ const PROJECT_NOT_FOUND = 'Project not found';
5
+ const SESSION_NOT_IN_PROJECT = 'Session does not belong to this project';
6
+
3
7
  /**
4
8
  * Middleware: Look up a session by req.params.id and attach to req.session_.
5
9
  * Returns 404 if session not found.
@@ -8,17 +12,12 @@ import { sessions, projects } from '../database.js';
8
12
  export function requireSession(req, res, next) {
9
13
  const session = sessions.getById(req.params.id);
10
14
  if (!session) {
11
- return res.status(404).json({ error: 'Session not found' });
15
+ return res.status(404).json({ error: SESSION_NOT_FOUND });
12
16
  }
13
17
  req.session_ = session;
14
18
  next();
15
19
  }
16
20
 
17
- /**
18
- * Middleware: Look up session AND its project. Attaches req.session_ and req.project.
19
- * Returns 404 if either is not found.
20
- * Also resolves req.workingDirectory (gitWorktree || project.workingDirectory).
21
- */
22
21
  /**
23
22
  * Middleware factory: Validates that req.session_.status is one of the allowed statuses.
24
23
  * Must be used AFTER requireSession or requireSessionAndProject middleware.
@@ -39,15 +38,20 @@ export function requireSessionStatus(allowedStatuses, errorMessage) {
39
38
  };
40
39
  }
41
40
 
41
+ /**
42
+ * Middleware: Look up session AND its project. Attaches req.session_ and req.project.
43
+ * Returns 404 if either is not found.
44
+ * Also resolves req.workingDirectory (gitWorktree || project.workingDirectory).
45
+ */
42
46
  export function requireSessionAndProject(req, res, next) {
43
47
  const session = sessions.getById(req.params.id);
44
48
  if (!session) {
45
- return res.status(404).json({ error: 'Session not found' });
49
+ return res.status(404).json({ error: SESSION_NOT_FOUND });
46
50
  }
47
51
 
48
52
  const project = projects.getById(session.projectId);
49
53
  if (!project) {
50
- return res.status(404).json({ error: 'Project not found' });
54
+ return res.status(404).json({ error: PROJECT_NOT_FOUND });
51
55
  }
52
56
 
53
57
  req.session_ = session;
@@ -55,3 +59,72 @@ export function requireSessionAndProject(req, res, next) {
55
59
  req.workingDirectory = session.gitWorktree || project.workingDirectory;
56
60
  next();
57
61
  }
62
+
63
+ /**
64
+ * Middleware: Look up the provided session ID, then attach the workflow root
65
+ * session/project for resources shared across a session tree.
66
+ */
67
+ export function requireRootSessionAndProject(req, res, next) {
68
+ const providedSession = sessions.getById(req.params.id);
69
+ if (!providedSession) {
70
+ return res.status(404).json({ error: SESSION_NOT_FOUND });
71
+ }
72
+
73
+ const rootSessionId = sessions.getRootSessionId(providedSession.id) || providedSession.id;
74
+ const rootSession = sessions.getById(rootSessionId);
75
+ if (!rootSession) {
76
+ return res.status(404).json({ error: SESSION_NOT_FOUND });
77
+ }
78
+
79
+ if (providedSession.projectId !== rootSession.projectId) {
80
+ return res.status(400).json({ error: 'Session parent chain crosses projects' });
81
+ }
82
+
83
+ const rootProject = projects.getById(rootSession.projectId);
84
+ if (!rootProject) {
85
+ return res.status(404).json({ error: PROJECT_NOT_FOUND });
86
+ }
87
+
88
+ req.providedSession_ = providedSession;
89
+ req.rootSession_ = rootSession;
90
+ req.rootSessionId = rootSessionId;
91
+ req.rootProject = rootProject;
92
+ req.rootWorkingDirectory = rootSession.gitWorktree || rootProject.workingDirectory;
93
+ next();
94
+ }
95
+
96
+ /**
97
+ * Middleware factory for project-scoped endpoints that receive a sessionId in
98
+ * the JSON body and should operate on that session tree's root.
99
+ */
100
+ export function resolveBodyRootSessionForProject(projectParam = 'projectId') {
101
+ return (req, res, next) => {
102
+ const providedSessionId = req.body?.sessionId;
103
+ if (!providedSessionId) {
104
+ return res.status(400).json({ error: 'sessionId is required' });
105
+ }
106
+
107
+ const providedSession = sessions.getById(providedSessionId);
108
+ if (!providedSession) {
109
+ return res.status(404).json({ error: SESSION_NOT_FOUND });
110
+ }
111
+
112
+ if (providedSession.projectId !== req.params[projectParam]) {
113
+ return res.status(400).json({ error: SESSION_NOT_IN_PROJECT });
114
+ }
115
+
116
+ const rootSessionId = sessions.getRootSessionId(providedSession.id) || providedSession.id;
117
+ const rootSession = sessions.getById(rootSessionId);
118
+ if (!rootSession) {
119
+ return res.status(404).json({ error: SESSION_NOT_FOUND });
120
+ }
121
+
122
+ if (rootSession.projectId !== req.params[projectParam]) {
123
+ return res.status(400).json({ error: SESSION_NOT_IN_PROJECT });
124
+ }
125
+
126
+ req.bodyRootSession_ = rootSession;
127
+ req.bodyRootSessionId = rootSessionId;
128
+ next();
129
+ };
130
+ }
@@ -1,4 +1,3 @@
1
- -- Projects table
2
1
  CREATE TABLE IF NOT EXISTS projects (
3
2
  id TEXT PRIMARY KEY,
4
3
  name TEXT NOT NULL,
@@ -6,14 +5,14 @@ CREATE TABLE IF NOT EXISTS projects (
6
5
  system_prompt TEXT,
7
6
  on_session_created TEXT,
8
7
  on_session_deleted TEXT,
9
- pr_poll_interval INTEGER NOT NULL DEFAULT 60000, -- PR status poll interval in ms (default 1 minute)
10
- repo_url TEXT, -- GitHub repository URL for PR validation
8
+ pr_poll_interval INTEGER NOT NULL DEFAULT 60000,
9
+ repo_url TEXT,
10
+ worktree_path TEXT,
11
+ kanban_enabled INTEGER NOT NULL DEFAULT 1,
11
12
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
12
13
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
13
14
  );
14
15
 
15
- -- Session templates (reusable prompts that can chain sessions)
16
- -- Must be created before sessions due to foreign key reference
17
16
  CREATE TABLE IF NOT EXISTS session_templates (
18
17
  id TEXT PRIMARY KEY,
19
18
  project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
@@ -26,90 +25,132 @@ CREATE TABLE IF NOT EXISTS session_templates (
26
25
  model TEXT,
27
26
  mode TEXT DEFAULT 'yolo' CHECK(mode IN ('plan', 'standard', 'yolo')),
28
27
  effort_level TEXT CHECK(effort_level IN ('low', 'medium', 'high', 'max', 'auto')),
28
+ target_lane_id TEXT REFERENCES kanban_lanes(id) ON DELETE SET NULL,
29
29
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
30
30
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
31
31
  );
32
32
 
33
- -- Sessions table
33
+ CREATE TABLE IF NOT EXISTS providers (
34
+ id TEXT PRIMARY KEY,
35
+ name TEXT NOT NULL,
36
+ base_url TEXT,
37
+ auth_token TEXT,
38
+ api_timeout_ms INTEGER,
39
+ additional_env_vars TEXT,
40
+ commit_attribution_override TEXT,
41
+ is_built_in INTEGER NOT NULL DEFAULT 0,
42
+ kind TEXT NOT NULL DEFAULT 'anthropic' CHECK(kind IN ('anthropic','openai')),
43
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
44
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
45
+ );
46
+
47
+ CREATE TABLE IF NOT EXISTS provider_models (
48
+ id TEXT PRIMARY KEY,
49
+ provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
50
+ model_id TEXT NOT NULL,
51
+ display_name TEXT NOT NULL,
52
+ description TEXT,
53
+ tier TEXT CHECK(tier IN ('opus', 'sonnet', 'haiku', 'custom')),
54
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
55
+ );
56
+
34
57
  CREATE TABLE IF NOT EXISTS sessions (
35
58
  id TEXT PRIMARY KEY,
36
59
  project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
37
60
  name TEXT NOT NULL,
38
61
  status TEXT NOT NULL DEFAULT 'starting' CHECK (status IN ('starting', 'running', 'waiting', 'stopped', 'completed', 'error', 'scheduled')),
39
- mode TEXT NOT NULL DEFAULT 'standard' CHECK (mode IN ('plan', 'standard', 'yolo')),
40
- thinking_enabled INTEGER NOT NULL DEFAULT 0,
62
+ mode TEXT NOT NULL DEFAULT 'yolo' CHECK (mode IN ('plan', 'standard', 'yolo')),
63
+ thinking_enabled INTEGER NOT NULL DEFAULT 1,
41
64
  archived INTEGER NOT NULL DEFAULT 0,
42
65
  git_branch TEXT,
43
66
  git_worktree TEXT,
44
67
  pr_url TEXT,
68
+ pr_url_auto_link_disabled INTEGER NOT NULL DEFAULT 0,
45
69
  error TEXT,
46
70
  effort_level TEXT CHECK(effort_level IN ('low', 'medium', 'high', 'max', 'auto')),
47
71
  cost_usd REAL DEFAULT 0,
48
72
  claude_session_id TEXT,
73
+ model TEXT,
74
+ provider_id TEXT REFERENCES providers(id),
49
75
  next_template_id TEXT REFERENCES session_templates(id) ON DELETE SET NULL,
50
76
  parent_session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
77
+ input_tokens INTEGER DEFAULT 0,
78
+ output_tokens INTEGER DEFAULT 0,
79
+ thinking_tokens INTEGER DEFAULT 0,
80
+ cache_read_input_tokens INTEGER DEFAULT 0,
81
+ cache_creation_input_tokens INTEGER DEFAULT 0,
82
+ web_search_requests INTEGER DEFAULT 0,
83
+ context_window INTEGER DEFAULT 200000,
84
+ starred INTEGER NOT NULL DEFAULT 0,
85
+ manually_named INTEGER NOT NULL DEFAULT 0,
86
+ scheduled_at INTEGER DEFAULT NULL,
87
+ reschedule_delay_minutes INTEGER DEFAULT 60,
88
+ auto_reschedule_enabled INTEGER DEFAULT 0,
89
+ reschedule_on_token_limit INTEGER DEFAULT 1,
90
+ reschedule_on_service_error INTEGER DEFAULT 1,
91
+ max_reschedule_count INTEGER DEFAULT NULL,
92
+ max_total_tokens INTEGER DEFAULT NULL,
93
+ reschedule_count INTEGER DEFAULT 0,
94
+ reschedule_at_token_count INTEGER DEFAULT NULL,
95
+ pending_prompt TEXT,
96
+ slash_commands TEXT,
97
+ pending_model TEXT,
98
+ auto_send_pending_prompt INTEGER DEFAULT 0,
99
+ agent_type TEXT DEFAULT 'claude-code',
100
+ target_lane_id TEXT REFERENCES kanban_lanes(id) ON DELETE SET NULL,
101
+ lane_trigger_depth INTEGER NOT NULL DEFAULT 0,
51
102
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
52
103
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
53
104
  );
54
105
 
55
- -- Conversations (multiple conversation threads per session)
56
106
  CREATE TABLE IF NOT EXISTS conversations (
57
107
  id TEXT PRIMARY KEY,
58
108
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
59
- name TEXT, -- Auto from first message or user-defined
60
- summary TEXT, -- Per-conversation summary
61
- summary_generated_at INTEGER, -- When summary was last generated
62
- is_active INTEGER NOT NULL DEFAULT 0, -- Currently selected conversation (1 = active)
63
- claude_session_id TEXT, -- Claude SDK session ID for this conversation's context
64
- -- Token usage fields (per-conversation tracking)
109
+ name TEXT,
110
+ summary TEXT,
111
+ summary_generated_at INTEGER,
112
+ is_active INTEGER NOT NULL DEFAULT 0,
113
+ claude_session_id TEXT,
65
114
  input_tokens INTEGER DEFAULT 0,
66
115
  output_tokens INTEGER DEFAULT 0,
116
+ thinking_tokens INTEGER DEFAULT 0,
67
117
  cache_read_input_tokens INTEGER DEFAULT 0,
68
118
  cache_creation_input_tokens INTEGER DEFAULT 0,
69
119
  web_search_requests INTEGER DEFAULT 0,
70
120
  context_window INTEGER DEFAULT 200000,
121
+ model TEXT,
122
+ parent_conversation_id TEXT REFERENCES conversations(id) ON DELETE SET NULL,
123
+ branch_from_message_id TEXT REFERENCES conversation_messages(id) ON DELETE SET NULL,
71
124
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
72
125
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
73
126
  );
74
127
 
75
- -- Conversation messages
76
128
  CREATE TABLE IF NOT EXISTS conversation_messages (
77
129
  id TEXT PRIMARY KEY,
78
130
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
79
131
  conversation_id TEXT REFERENCES conversations(id) ON DELETE CASCADE,
80
132
  role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
81
133
  content TEXT NOT NULL,
82
- tool_use TEXT, -- JSON array of tool uses
83
- model TEXT, -- Model that generated this message (for assistant messages)
134
+ tool_use TEXT,
135
+ model TEXT,
84
136
  timestamp INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
85
137
  );
86
138
 
87
- -- Canvas items
88
139
  CREATE TABLE IF NOT EXISTS canvas_items (
89
140
  id TEXT PRIMARY KEY,
90
141
  session_id TEXT REFERENCES sessions(id) ON DELETE CASCADE,
91
142
  type TEXT NOT NULL CHECK (type IN ('image', 'markdown', 'text', 'json', 'pdf', 'code')),
92
- content TEXT, -- For markdown/text
93
- data TEXT, -- For json (stored as JSON string) or image (base64)
94
- mime_type TEXT, -- For images
143
+ content TEXT,
144
+ data TEXT,
145
+ mime_type TEXT,
95
146
  filename TEXT,
96
147
  width INTEGER,
97
148
  height INTEGER,
98
- deleted_at INTEGER, -- null = active, timestamp = soft deleted
149
+ deleted_at INTEGER,
99
150
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
100
- updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
151
+ updated_at INTEGER
101
152
  );
102
153
 
103
- -- Session notes
104
- CREATE TABLE IF NOT EXISTS session_notes (
105
- id TEXT PRIMARY KEY,
106
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
107
- content TEXT NOT NULL,
108
- created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
109
- updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
110
- );
111
-
112
- -- Global tool templates
113
154
  CREATE TABLE IF NOT EXISTS global_tool_templates (
114
155
  id TEXT PRIMARY KEY,
115
156
  name TEXT NOT NULL,
@@ -119,7 +160,6 @@ CREATE TABLE IF NOT EXISTS global_tool_templates (
119
160
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
120
161
  );
121
162
 
122
- -- Project tool templates
123
163
  CREATE TABLE IF NOT EXISTS project_tool_templates (
124
164
  id TEXT PRIMARY KEY,
125
165
  project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
@@ -130,50 +170,46 @@ CREATE TABLE IF NOT EXISTS project_tool_templates (
130
170
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
131
171
  );
132
172
 
133
- -- Session todos (Claude's task list, scoped to conversations)
134
173
  CREATE TABLE IF NOT EXISTS session_todos (
135
174
  id TEXT PRIMARY KEY,
136
175
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
137
176
  conversation_id TEXT REFERENCES conversations(id) ON DELETE CASCADE,
138
177
  content TEXT NOT NULL,
139
- status TEXT NOT NULL DEFAULT 'pending'
140
- CHECK (status IN ('pending', 'in_progress', 'completed')),
178
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed')),
141
179
  position INTEGER NOT NULL,
142
180
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
143
181
  );
144
182
 
145
- -- Work logs (thinking, command outputs, tool executions)
146
183
  CREATE TABLE IF NOT EXISTS work_logs (
147
184
  id TEXT PRIMARY KEY,
148
185
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
149
186
  message_id TEXT REFERENCES conversation_messages(id) ON DELETE CASCADE,
150
187
  type TEXT NOT NULL CHECK (type IN ('thinking', 'tool_input', 'tool_output')),
151
- tool_name TEXT, -- Tool name for tool_input/tool_output types
152
- content TEXT NOT NULL, -- The actual log content
188
+ tool_name TEXT,
189
+ content TEXT NOT NULL,
153
190
  timestamp INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
154
191
  );
155
192
 
156
- -- Session summaries (AI-generated)
157
193
  CREATE TABLE IF NOT EXISTS session_summaries (
158
194
  id TEXT PRIMARY KEY,
159
195
  session_id TEXT NOT NULL UNIQUE REFERENCES sessions(id) ON DELETE CASCADE,
160
- short_summary TEXT NOT NULL, -- 1-2 sentence preview for list view (~100 chars)
161
- full_summary TEXT NOT NULL, -- Detailed summary with key points (~500 chars)
162
- key_actions TEXT, -- JSON array of main actions taken
163
- files_modified TEXT, -- JSON array of files touched
164
- outcome TEXT, -- 'completed' | 'partial' | 'failed' | 'ongoing'
165
- message_count INTEGER, -- Track what was summarized (staleness detection)
166
- pr_merged INTEGER, -- 0/1 boolean - whether PR is merged
167
- pr_state TEXT, -- 'open' | 'closed' | 'merged' | 'draft'
168
- has_merge_conflicts INTEGER, -- 0/1 boolean - whether PR has merge conflicts
169
- ci_status TEXT, -- 'success' | 'failure' | 'pending' | null
170
- ci_failures TEXT, -- JSON array of failed check names
196
+ short_summary TEXT NOT NULL,
197
+ full_summary TEXT NOT NULL,
198
+ key_actions TEXT,
199
+ files_modified TEXT,
200
+ outcome TEXT,
201
+ message_count INTEGER,
202
+ pr_merged INTEGER,
203
+ pr_state TEXT,
204
+ has_merge_conflicts INTEGER,
205
+ ci_status TEXT,
206
+ ci_failures TEXT,
207
+ last_summarized_message_id TEXT,
171
208
  generated_at INTEGER NOT NULL,
172
209
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
173
210
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
174
211
  );
175
212
 
176
- -- Message attachments (files attached to user messages)
177
213
  CREATE TABLE IF NOT EXISTS message_attachments (
178
214
  id TEXT PRIMARY KEY,
179
215
  message_id TEXT REFERENCES conversation_messages(id) ON DELETE CASCADE,
@@ -182,12 +218,11 @@ CREATE TABLE IF NOT EXISTS message_attachments (
182
218
  mime_type TEXT NOT NULL,
183
219
  size_bytes INTEGER NOT NULL,
184
220
  storage_type TEXT NOT NULL DEFAULT 'base64' CHECK (storage_type IN ('base64', 'file_path', 'project_file')),
185
- content TEXT, -- base64 data or file reference depending on storage_type
186
- file_path TEXT, -- absolute path to file on disk (for Claude's Read tool)
221
+ content TEXT,
222
+ file_path TEXT,
187
223
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
188
224
  );
189
225
 
190
- -- Command buttons (configurable buttons per project)
191
226
  CREATE TABLE IF NOT EXISTS command_buttons (
192
227
  id TEXT PRIMARY KEY,
193
228
  project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
@@ -199,7 +234,6 @@ CREATE TABLE IF NOT EXISTS command_buttons (
199
234
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
200
235
  );
201
236
 
202
- -- Command runs (execution history for command buttons)
203
237
  CREATE TABLE IF NOT EXISTS command_runs (
204
238
  id TEXT PRIMARY KEY,
205
239
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
@@ -211,7 +245,6 @@ CREATE TABLE IF NOT EXISTS command_runs (
211
245
  completed_at INTEGER
212
246
  );
213
247
 
214
- -- Quick responses (reusable messages for conversation)
215
248
  CREATE TABLE IF NOT EXISTS quick_responses (
216
249
  id TEXT PRIMARY KEY,
217
250
  project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
@@ -224,102 +257,51 @@ CREATE TABLE IF NOT EXISTS quick_responses (
224
257
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
225
258
  );
226
259
 
227
- -- Providers (custom API endpoints for Claude; replaces the old model_providers table)
228
- CREATE TABLE IF NOT EXISTS providers (
260
+ CREATE TABLE IF NOT EXISTS project_session_defaults (
229
261
  id TEXT PRIMARY KEY,
230
- name TEXT NOT NULL,
231
- base_url TEXT,
232
- auth_token TEXT,
233
- api_timeout_ms INTEGER,
234
- additional_env_vars TEXT,
235
- is_built_in INTEGER NOT NULL DEFAULT 0,
236
- kind TEXT NOT NULL DEFAULT 'anthropic' CHECK(kind IN ('anthropic','openai')),
237
- created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
238
- updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
262
+ project_id TEXT NOT NULL UNIQUE,
263
+ mode TEXT CHECK(mode IN ('plan', 'standard', 'yolo')),
264
+ thinking_enabled INTEGER,
265
+ start_immediately INTEGER,
266
+ git_mode TEXT CHECK(git_mode IN ('branch', 'worktree', 'current')),
267
+ git_branch TEXT,
268
+ model TEXT,
269
+ provider_id TEXT REFERENCES providers(id),
270
+ effort_level TEXT CHECK(effort_level IN ('low', 'medium', 'high', 'max', 'auto')),
271
+ created_at INTEGER NOT NULL,
272
+ updated_at INTEGER NOT NULL,
273
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
239
274
  );
240
275
 
241
- -- Provider models (custom models available per provider)
242
- CREATE TABLE IF NOT EXISTS provider_models (
243
- id TEXT PRIMARY KEY,
244
- provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
245
- model_id TEXT NOT NULL,
246
- display_name TEXT NOT NULL,
247
- description TEXT,
248
- tier TEXT CHECK(tier IN ('opus', 'sonnet', 'haiku', 'custom')),
249
- created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
276
+ CREATE TABLE IF NOT EXISTS app_settings (
277
+ key TEXT PRIMARY KEY,
278
+ value TEXT NOT NULL,
279
+ updated_at INTEGER NOT NULL
250
280
  );
251
281
 
252
- -- Indexes
253
- CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_id);
254
- CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
255
- CREATE INDEX IF NOT EXISTS idx_sessions_archived ON sessions(archived);
256
- CREATE INDEX IF NOT EXISTS idx_conversations_session ON conversations(session_id);
257
- CREATE INDEX IF NOT EXISTS idx_messages_session ON conversation_messages(session_id);
258
- -- Note: idx_messages_conversation is created in migrations to handle existing databases
259
- -- that may not have the conversation_id column yet
260
- CREATE INDEX IF NOT EXISTS idx_canvas_session ON canvas_items(session_id);
261
- -- Note: idx_canvas_deleted is created in migrations to handle existing databases
262
- -- that may not have the deleted_at column yet
263
- CREATE INDEX IF NOT EXISTS idx_notes_session ON session_notes(session_id);
264
- CREATE INDEX IF NOT EXISTS idx_project_tools ON project_tool_templates(project_id);
265
- CREATE INDEX IF NOT EXISTS idx_session_templates_project ON session_templates(project_id);
266
- -- Note: idx_sessions_next_template and idx_sessions_parent are created in migrations
267
- -- to handle existing databases that may not have these columns yet
268
- CREATE INDEX IF NOT EXISTS idx_todos_session ON session_todos(session_id);
269
- -- Note: idx_todos_conversation is created in migrations to handle existing databases
270
- -- that may not have the conversation_id column yet
271
- CREATE INDEX IF NOT EXISTS idx_work_logs_session ON work_logs(session_id);
272
- CREATE INDEX IF NOT EXISTS idx_work_logs_message ON work_logs(message_id);
273
- CREATE INDEX IF NOT EXISTS idx_summaries_session ON session_summaries(session_id);
274
- CREATE INDEX IF NOT EXISTS idx_attachments_message ON message_attachments(message_id);
275
- CREATE INDEX IF NOT EXISTS idx_attachments_session ON message_attachments(session_id);
276
- CREATE INDEX IF NOT EXISTS idx_command_buttons_project ON command_buttons(project_id);
277
- CREATE INDEX IF NOT EXISTS idx_command_runs_session ON command_runs(session_id);
278
- CREATE INDEX IF NOT EXISTS idx_command_runs_button ON command_runs(button_id);
279
- CREATE INDEX IF NOT EXISTS idx_command_runs_status ON command_runs(status);
280
- CREATE INDEX IF NOT EXISTS idx_quick_responses_project ON quick_responses(project_id);
281
- CREATE INDEX IF NOT EXISTS idx_quick_responses_sort ON quick_responses(project_id, sort_order);
282
- CREATE INDEX IF NOT EXISTS idx_provider_models_provider ON provider_models(provider_id);
283
-
284
- -- Agent call logs (metrics and analytics for agent interactions)
285
282
  CREATE TABLE IF NOT EXISTS agent_call_logs (
286
283
  id TEXT PRIMARY KEY,
287
284
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
288
285
  conversation_id TEXT,
289
286
  agent_type TEXT NOT NULL,
290
287
  model TEXT,
291
-
292
- -- Request info
293
- call_type TEXT NOT NULL, -- 'runSession' | 'continueSession' | 'continueSessionWithExistingMessage'
288
+ call_type TEXT NOT NULL,
294
289
  prompt_length INTEGER,
295
-
296
- -- Response token info (updated during streaming and finalized at result)
297
290
  input_tokens INTEGER DEFAULT 0,
298
291
  output_tokens INTEGER DEFAULT 0,
299
292
  thinking_tokens INTEGER DEFAULT 0,
300
293
  cache_read_tokens INTEGER DEFAULT 0,
301
294
  cache_write_tokens INTEGER DEFAULT 0,
302
295
  total_tokens INTEGER DEFAULT 0,
303
-
304
- -- Timing
305
296
  started_at INTEGER NOT NULL,
306
297
  completed_at INTEGER,
307
298
  duration_ms INTEGER,
308
-
309
- -- Status
310
299
  status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'streaming', 'completed', 'error')),
311
300
  error_message TEXT,
312
-
313
- -- Metadata (JSON blob for extensibility)
314
301
  metadata TEXT,
315
-
316
302
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
317
303
  );
318
304
 
319
- CREATE INDEX IF NOT EXISTS idx_agent_call_logs_session ON agent_call_logs(session_id);
320
- CREATE INDEX IF NOT EXISTS idx_agent_call_logs_started ON agent_call_logs(started_at);
321
-
322
- -- Kanban boards (one per project)
323
305
  CREATE TABLE IF NOT EXISTS kanban_boards (
324
306
  id TEXT PRIMARY KEY,
325
307
  project_id TEXT NOT NULL UNIQUE REFERENCES projects(id) ON DELETE CASCADE,
@@ -327,7 +309,6 @@ CREATE TABLE IF NOT EXISTS kanban_boards (
327
309
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
328
310
  );
329
311
 
330
- -- Kanban lanes (columns on the board)
331
312
  CREATE TABLE IF NOT EXISTS kanban_lanes (
332
313
  id TEXT PRIMARY KEY,
333
314
  board_id TEXT NOT NULL REFERENCES kanban_boards(id) ON DELETE CASCADE,
@@ -335,11 +316,21 @@ CREATE TABLE IF NOT EXISTS kanban_lanes (
335
316
  sort_order INTEGER NOT NULL DEFAULT 0,
336
317
  on_enter_template_id TEXT REFERENCES session_templates(id) ON DELETE SET NULL,
337
318
  on_enter_prompt TEXT,
319
+ on_enter_mode TEXT,
320
+ on_enter_model TEXT,
321
+ on_enter_effort_level TEXT,
322
+ on_enter_thinking_enabled INTEGER,
323
+ on_enter_auto_reschedule_enabled INTEGER DEFAULT 0,
324
+ on_enter_reschedule_delay_minutes INTEGER DEFAULT 60,
325
+ on_enter_reschedule_on_token_limit INTEGER DEFAULT 1,
326
+ on_enter_reschedule_on_service_error INTEGER DEFAULT 1,
327
+ on_enter_max_reschedule_count INTEGER,
328
+ on_enter_max_total_tokens INTEGER,
329
+ on_enter_reschedule_at_token_count INTEGER,
338
330
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
339
331
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
340
332
  );
341
333
 
342
- -- Kanban cards (entries on the board, separate from sessions for future flexibility)
343
334
  CREATE TABLE IF NOT EXISTS kanban_cards (
344
335
  id TEXT PRIMARY KEY,
345
336
  lane_id TEXT NOT NULL REFERENCES kanban_lanes(id) ON DELETE CASCADE,
@@ -348,7 +339,6 @@ CREATE TABLE IF NOT EXISTS kanban_cards (
348
339
  updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
349
340
  );
350
341
 
351
- -- Kanban card sessions (links cards to sessions, currently 1:1 but allows future N:1)
352
342
  CREATE TABLE IF NOT EXISTS kanban_card_sessions (
353
343
  id TEXT PRIMARY KEY,
354
344
  card_id TEXT NOT NULL REFERENCES kanban_cards(id) ON DELETE CASCADE,
@@ -356,7 +346,42 @@ CREATE TABLE IF NOT EXISTS kanban_card_sessions (
356
346
  created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
357
347
  );
358
348
 
359
- -- Kanban indexes
349
+ CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_id);
350
+ CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
351
+ CREATE INDEX IF NOT EXISTS idx_sessions_archived ON sessions(archived);
352
+ CREATE INDEX IF NOT EXISTS idx_sessions_starred ON sessions(archived, starred);
353
+ CREATE INDEX IF NOT EXISTS idx_sessions_next_template ON sessions(next_template_id);
354
+ CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
355
+ CREATE INDEX IF NOT EXISTS idx_sessions_scheduled ON sessions(scheduled_at) WHERE scheduled_at IS NOT NULL;
356
+ CREATE INDEX IF NOT EXISTS idx_conversations_session ON conversations(session_id);
357
+ CREATE INDEX IF NOT EXISTS idx_conversations_parent ON conversations(parent_conversation_id);
358
+ CREATE INDEX IF NOT EXISTS idx_messages_session ON conversation_messages(session_id);
359
+ CREATE INDEX IF NOT EXISTS idx_messages_conversation ON conversation_messages(conversation_id);
360
+ CREATE INDEX IF NOT EXISTS idx_canvas_session ON canvas_items(session_id);
361
+ CREATE INDEX IF NOT EXISTS idx_canvas_deleted ON canvas_items(deleted_at);
362
+ CREATE INDEX IF NOT EXISTS idx_project_tools ON project_tool_templates(project_id);
363
+ CREATE INDEX IF NOT EXISTS idx_session_templates_project ON session_templates(project_id);
364
+ CREATE INDEX IF NOT EXISTS idx_todos_session ON session_todos(session_id);
365
+ CREATE INDEX IF NOT EXISTS idx_todos_conversation ON session_todos(conversation_id);
366
+ CREATE INDEX IF NOT EXISTS idx_work_logs_session ON work_logs(session_id);
367
+ CREATE INDEX IF NOT EXISTS idx_work_logs_message ON work_logs(message_id);
368
+ CREATE INDEX IF NOT EXISTS idx_summaries_session ON session_summaries(session_id);
369
+ CREATE INDEX IF NOT EXISTS idx_attachments_message ON message_attachments(message_id);
370
+ CREATE INDEX IF NOT EXISTS idx_attachments_session ON message_attachments(session_id);
371
+ CREATE INDEX IF NOT EXISTS idx_command_buttons_project ON command_buttons(project_id);
372
+ CREATE INDEX IF NOT EXISTS idx_command_runs_session ON command_runs(session_id);
373
+ CREATE INDEX IF NOT EXISTS idx_command_runs_button ON command_runs(button_id);
374
+ CREATE INDEX IF NOT EXISTS idx_command_runs_status ON command_runs(status);
375
+ CREATE INDEX IF NOT EXISTS idx_quick_responses_project ON quick_responses(project_id);
376
+ CREATE INDEX IF NOT EXISTS idx_quick_responses_sort ON quick_responses(project_id, sort_order);
377
+ CREATE INDEX IF NOT EXISTS idx_provider_models_provider ON provider_models(provider_id);
378
+ CREATE INDEX IF NOT EXISTS idx_project_defaults_projectId ON project_session_defaults(project_id);
379
+ CREATE INDEX IF NOT EXISTS idx_agent_call_logs_session ON agent_call_logs(session_id);
380
+ CREATE INDEX IF NOT EXISTS idx_agent_call_logs_started ON agent_call_logs(started_at);
381
+ CREATE INDEX IF NOT EXISTS idx_agent_call_logs_agent_type ON agent_call_logs(agent_type);
382
+ CREATE INDEX IF NOT EXISTS idx_agent_call_logs_call_type ON agent_call_logs(call_type);
383
+ CREATE INDEX IF NOT EXISTS idx_agent_call_logs_status ON agent_call_logs(status);
384
+ CREATE INDEX IF NOT EXISTS idx_agent_call_logs_model ON agent_call_logs(model);
360
385
  CREATE INDEX IF NOT EXISTS idx_kanban_boards_project ON kanban_boards(project_id);
361
386
  CREATE INDEX IF NOT EXISTS idx_kanban_lanes_board ON kanban_lanes(board_id, sort_order);
362
387
  CREATE INDEX IF NOT EXISTS idx_kanban_cards_lane ON kanban_cards(lane_id, sort_order);
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { copyDatabaseBackups } from './dbUtils.js';
3
+
4
+ export function main() {
5
+ const result = copyDatabaseBackups();
6
+
7
+ if (result.copied.length === 0) {
8
+ console.log(`No database found at ${result.dbPath}; nothing to back up.`);
9
+ return 0;
10
+ }
11
+
12
+ console.log(`Database backup directory: ${result.backupDir}`);
13
+ for (const file of result.copied) {
14
+ console.log(`${file.source} -> ${file.target}`);
15
+ }
16
+ return 0;
17
+ }
18
+
19
+ if (import.meta.url === `file://${process.argv[1]}`) {
20
+ process.exit(main());
21
+ }