@imenam/mcp-organizer 1.0.3 → 1.0.7

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 (45) hide show
  1. package/README.md +71 -20
  2. package/dist/shared/schemas/admin-api.schema.d.ts +101 -0
  3. package/dist/shared/schemas/admin-api.schema.js +39 -0
  4. package/dist/shared/schemas/agent-api.schema.d.ts +2 -0
  5. package/dist/shared/schemas/agent-api.schema.js +4 -0
  6. package/dist/shared/schemas/agent-tools.schema.d.ts +34 -0
  7. package/dist/shared/schemas/agent-tools.schema.js +2 -0
  8. package/dist/shared/schemas/api.schema.d.ts +2 -0
  9. package/dist/shared/schemas/api.schema.js +2 -0
  10. package/dist/shared/schemas/entities.schema.d.ts +27 -1
  11. package/dist/shared/schemas/entities.schema.js +21 -2
  12. package/dist/shared/schemas/notifications-api.schema.d.ts +31 -0
  13. package/dist/shared/schemas/notifications-api.schema.js +22 -1
  14. package/dist/src/agent-api/requests.js +2 -1
  15. package/dist/src/cli/set-admin.d.ts +1 -0
  16. package/dist/src/cli/set-admin.js +45 -0
  17. package/dist/src/core/accounts.js +2 -1
  18. package/dist/src/core/admin.d.ts +24 -0
  19. package/dist/src/core/admin.js +85 -0
  20. package/dist/src/core/agent-tokens.d.ts +9 -1
  21. package/dist/src/core/agent-tokens.js +25 -2
  22. package/dist/src/core/agent.d.ts +5 -0
  23. package/dist/src/core/agent.js +23 -0
  24. package/dist/src/core/migrations.js +369 -344
  25. package/dist/src/core/notifications.d.ts +7 -1
  26. package/dist/src/core/notifications.js +32 -1
  27. package/dist/src/core/requests.d.ts +1 -1
  28. package/dist/src/core/requests.js +4 -4
  29. package/dist/src/core/store.d.ts +38 -2
  30. package/dist/src/core/store.js +161 -38
  31. package/dist/src/gui-api.d.ts +6 -0
  32. package/dist/src/gui-api.js +25 -4
  33. package/dist/src/index.js +8 -1
  34. package/dist/src/mcp/start.d.ts +1 -1
  35. package/dist/src/mcp/start.js +1 -1
  36. package/dist/src/mcp/tools.js +1 -1
  37. package/dist/src/server/runner.d.ts +51 -0
  38. package/dist/src/server/runner.js +222 -0
  39. package/dist/src/server/start.js +8 -0
  40. package/gui/dist/assets/index-DuoSNpBr.js +70 -0
  41. package/gui/dist/assets/index-duxL0fvO.css +1 -0
  42. package/gui/dist/index.html +2 -2
  43. package/package.json +3 -2
  44. package/gui/dist/assets/index-BVN0agCc.css +0 -1
  45. package/gui/dist/assets/index-DI1_kr81.js +0 -70
@@ -4,376 +4,401 @@
4
4
  * plus : toute évolution du schéma s'ajoute en fin de liste.
5
5
  */
6
6
  export const MIGRATIONS = [
7
- `
8
- CREATE TABLE users (
9
- id TEXT PRIMARY KEY,
10
- email TEXT NOT NULL UNIQUE CHECK (email = lower(email) AND email <> ''),
11
- display_name TEXT,
12
- password_hash TEXT,
13
- status TEXT NOT NULL CHECK (status IN ('invited', 'active', 'disabled')),
14
- created_at TEXT NOT NULL,
15
- CHECK (status = 'invited' OR display_name IS NOT NULL)
16
- );
17
-
18
- CREATE TABLE access_links (
19
- id TEXT PRIMARY KEY,
20
- user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
21
- purpose TEXT NOT NULL CHECK (purpose IN ('invite', 'reset')),
22
- token_hash TEXT NOT NULL UNIQUE,
23
- expires_at TEXT NOT NULL,
24
- used_at TEXT,
25
- created_at TEXT NOT NULL
26
- );
27
- CREATE INDEX access_links_user ON access_links(user_id);
28
-
29
- CREATE TABLE sessions (
30
- id_hash TEXT PRIMARY KEY,
31
- user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
32
- created_at TEXT NOT NULL,
33
- expires_at TEXT NOT NULL
34
- );
35
- CREATE INDEX sessions_user ON sessions(user_id);
36
- CREATE INDEX sessions_expiry ON sessions(expires_at);
37
-
38
- CREATE TABLE user_settings (
39
- user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
40
- agent_scope TEXT NOT NULL DEFAULT 'personal' CHECK (agent_scope IN ('personal', 'current_space')),
41
- notify_assign_app INTEGER NOT NULL DEFAULT 1 CHECK (notify_assign_app IN (0, 1)),
42
- notify_assign_email INTEGER NOT NULL DEFAULT 1 CHECK (notify_assign_email IN (0, 1)),
43
- notify_comment_app INTEGER NOT NULL DEFAULT 1 CHECK (notify_comment_app IN (0, 1)),
44
- notify_comment_email INTEGER NOT NULL DEFAULT 1 CHECK (notify_comment_email IN (0, 1))
45
- );
46
-
47
- CREATE TABLE spaces (
48
- id TEXT PRIMARY KEY,
49
- name TEXT NOT NULL CHECK (name <> ''),
50
- kind TEXT NOT NULL CHECK (kind IN ('personal', 'shared')),
51
- owner_id TEXT NOT NULL REFERENCES users(id),
52
- key_prefix TEXT NOT NULL,
53
- next_task_number INTEGER NOT NULL DEFAULT 1 CHECK (next_task_number >= 1),
54
- created_at TEXT NOT NULL
55
- );
56
- CREATE UNIQUE INDEX spaces_one_personal_per_user ON spaces(owner_id) WHERE kind = 'personal';
57
-
58
- CREATE TABLE memberships (
59
- space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
60
- user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
61
- role TEXT NOT NULL CHECK (role IN ('owner', 'member')),
62
- joined_at TEXT NOT NULL,
63
- PRIMARY KEY (space_id, user_id)
64
- );
65
- CREATE INDEX memberships_user ON memberships(user_id);
66
-
67
- CREATE TABLE invitations (
68
- id TEXT PRIMARY KEY,
69
- space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
70
- email TEXT NOT NULL CHECK (email = lower(email)),
71
- token_hash TEXT NOT NULL UNIQUE,
72
- invited_by TEXT NOT NULL REFERENCES users(id),
73
- created_at TEXT NOT NULL,
74
- expires_at TEXT NOT NULL,
75
- accepted_at TEXT,
76
- revoked_at TEXT
77
- );
78
- CREATE INDEX invitations_space ON invitations(space_id);
79
- CREATE INDEX invitations_email ON invitations(email);
80
-
81
- CREATE TABLE tasks (
82
- id TEXT PRIMARY KEY,
83
- space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
84
- number INTEGER NOT NULL CHECK (number >= 1),
85
- parent_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
86
- depth INTEGER NOT NULL CHECK (depth IN (0, 1, 2)),
87
- type TEXT NOT NULL CHECK (type IN ('standard', 'test')),
88
- title TEXT NOT NULL CHECK (title <> ''),
89
- description TEXT NOT NULL DEFAULT '',
90
- status TEXT NOT NULL CHECK (status IN ('todo', 'doing', 'review', 'done')),
91
- priority TEXT NOT NULL CHECK (priority IN ('low', 'medium', 'high', 'critical')),
92
- position REAL NOT NULL,
93
- created_by TEXT NOT NULL REFERENCES users(id),
94
- created_at TEXT NOT NULL,
95
- updated_at TEXT NOT NULL,
96
- version INTEGER NOT NULL DEFAULT 1 CHECK (version >= 1),
97
- UNIQUE (space_id, number),
98
- CHECK ((parent_id IS NULL) = (depth = 0))
99
- );
100
- CREATE INDEX tasks_column ON tasks(space_id, parent_id, status, position);
101
- CREATE INDEX tasks_parent ON tasks(parent_id);
102
-
103
- CREATE TABLE task_assignees (
104
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
105
- user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
106
- PRIMARY KEY (task_id, user_id)
107
- );
108
- CREATE INDEX task_assignees_user ON task_assignees(user_id);
109
-
110
- CREATE TABLE tags (
111
- id TEXT PRIMARY KEY,
112
- space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
113
- name TEXT NOT NULL CHECK (name <> ''),
114
- UNIQUE (space_id, name)
115
- );
116
-
117
- CREATE TABLE task_tags (
118
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
119
- tag_id TEXT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
120
- PRIMARY KEY (task_id, tag_id)
121
- );
122
- CREATE INDEX task_tags_tag ON task_tags(tag_id);
123
-
124
- CREATE TABLE checklist_items (
125
- id TEXT PRIMARY KEY,
126
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
127
- text TEXT NOT NULL CHECK (text <> ''),
128
- done INTEGER NOT NULL DEFAULT 0 CHECK (done IN (0, 1)),
129
- position REAL NOT NULL
130
- );
131
- CREATE INDEX checklist_items_task ON checklist_items(task_id, position);
132
-
133
- CREATE TABLE test_checkpoints (
134
- id TEXT PRIMARY KEY,
135
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
136
- text TEXT NOT NULL CHECK (text <> ''),
137
- position REAL NOT NULL
138
- );
139
- CREATE INDEX test_checkpoints_task ON test_checkpoints(task_id, position);
140
-
141
- CREATE TABLE test_results (
142
- checkpoint_id TEXT NOT NULL REFERENCES test_checkpoints(id) ON DELETE CASCADE,
143
- tester_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
144
- result TEXT NOT NULL CHECK (result IN ('ok', 'ko', 'blocked', 'untested')),
145
- comment TEXT NOT NULL DEFAULT '',
146
- updated_at TEXT NOT NULL,
147
- PRIMARY KEY (checkpoint_id, tester_id)
148
- );
149
- CREATE INDEX test_results_tester ON test_results(tester_id);
150
-
151
- CREATE TABLE test_reviews (
152
- id TEXT PRIMARY KEY,
153
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
154
- tester_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
155
- text TEXT NOT NULL DEFAULT '',
156
- submitted_at TEXT,
157
- request_id TEXT REFERENCES agent_requests(id) ON DELETE SET NULL
158
- );
159
- CREATE INDEX test_reviews_task ON test_reviews(task_id, tester_id);
160
-
161
- CREATE TABLE comments (
162
- id TEXT PRIMARY KEY,
163
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
164
- author_id TEXT NOT NULL REFERENCES users(id),
165
- body TEXT NOT NULL CHECK (body <> ''),
166
- created_at TEXT NOT NULL,
167
- edited_at TEXT
168
- );
169
- CREATE INDEX comments_task ON comments(task_id, created_at);
170
-
171
- CREATE TABLE attachments (
172
- id TEXT PRIMARY KEY,
173
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
174
- uploader_id TEXT NOT NULL REFERENCES users(id),
175
- filename TEXT NOT NULL,
176
- mime TEXT NOT NULL,
177
- size INTEGER NOT NULL CHECK (size >= 0),
178
- storage_path TEXT NOT NULL,
179
- created_at TEXT NOT NULL
180
- );
181
- CREATE INDEX attachments_task ON attachments(task_id);
182
-
183
- CREATE TABLE ideas (
184
- id TEXT PRIMARY KEY,
185
- space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
186
- author_id TEXT NOT NULL REFERENCES users(id),
187
- text TEXT NOT NULL CHECK (text <> ''),
188
- origin TEXT NOT NULL CHECK (origin IN ('manual', 'agent')),
189
- request_id TEXT REFERENCES agent_requests(id) ON DELETE SET NULL,
190
- promoted_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
191
- archived_at TEXT,
192
- created_at TEXT NOT NULL
193
- );
194
- CREATE INDEX ideas_space ON ideas(space_id, created_at);
195
-
196
- CREATE TABLE agent_requests (
197
- id TEXT PRIMARY KEY,
198
- author_id TEXT NOT NULL REFERENCES users(id),
199
- source_space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
200
- target_space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
201
- kind TEXT NOT NULL CHECK (kind IN ('idea', 'remark', 'test_review', 'message', 'idea_promotion')),
202
- context_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
203
- context_idea_id TEXT REFERENCES ideas(id) ON DELETE SET NULL,
204
- text TEXT NOT NULL CHECK (text <> ''),
205
- status TEXT NOT NULL CHECK (status IN ('pending', 'processing', 'done', 'failed', 'cancelled')),
206
- reply TEXT,
207
- failure_reason TEXT,
208
- created_at TEXT NOT NULL,
209
- updated_at TEXT NOT NULL,
210
- claimed_at TEXT,
211
- completed_at TEXT,
212
- cancelled_by TEXT REFERENCES users(id)
213
- );
214
- CREATE INDEX agent_requests_status ON agent_requests(status, created_at);
215
- CREATE INDEX agent_requests_source ON agent_requests(source_space_id, created_at);
216
- CREATE INDEX agent_requests_target ON agent_requests(target_space_id);
217
-
218
- CREATE TABLE agent_actions (
219
- id TEXT PRIMARY KEY,
220
- request_id TEXT NOT NULL REFERENCES agent_requests(id) ON DELETE CASCADE,
221
- seq INTEGER NOT NULL CHECK (seq >= 1),
222
- entity_type TEXT NOT NULL,
223
- entity_id TEXT NOT NULL,
224
- operation TEXT NOT NULL,
225
- before TEXT,
226
- after TEXT,
227
- summary TEXT NOT NULL,
228
- created_at TEXT NOT NULL,
229
- undone_at TEXT,
230
- undone_by TEXT REFERENCES users(id),
231
- UNIQUE (request_id, seq)
232
- );
233
- CREATE INDEX agent_actions_entity ON agent_actions(entity_type, entity_id);
234
-
235
- CREATE TABLE entity_versions (
236
- entity_type TEXT NOT NULL CHECK (entity_type IN ('task', 'idea')),
237
- entity_id TEXT NOT NULL,
238
- version INTEGER NOT NULL CHECK (version >= 1),
239
- snapshot TEXT,
240
- actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'agent')),
241
- actor_id TEXT NOT NULL,
242
- request_id TEXT,
243
- created_at TEXT NOT NULL,
244
- PRIMARY KEY (entity_type, entity_id, version)
245
- );
246
-
247
- CREATE TABLE notifications (
248
- id TEXT PRIMARY KEY,
249
- user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
250
- type TEXT NOT NULL CHECK (type IN ('assigned', 'comment')),
251
- task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
252
- actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'agent')),
253
- actor_id TEXT,
254
- request_id TEXT,
255
- comment_id TEXT REFERENCES comments(id) ON DELETE CASCADE,
256
- created_at TEXT NOT NULL,
257
- read_at TEXT,
258
- emailed_at TEXT,
259
- email_error TEXT
260
- );
261
- CREATE INDEX notifications_user ON notifications(user_id, read_at, created_at);
262
-
263
- CREATE TABLE changes (
264
- seq INTEGER PRIMARY KEY AUTOINCREMENT,
265
- space_id TEXT,
266
- user_id TEXT,
267
- entity_type TEXT NOT NULL,
268
- entity_id TEXT NOT NULL,
269
- at TEXT NOT NULL
270
- );
271
- CREATE INDEX changes_space ON changes(space_id, seq);
272
- CREATE INDEX changes_user ON changes(user_id, seq);
7
+ `
8
+ CREATE TABLE users (
9
+ id TEXT PRIMARY KEY,
10
+ email TEXT NOT NULL UNIQUE CHECK (email = lower(email) AND email <> ''),
11
+ display_name TEXT,
12
+ password_hash TEXT,
13
+ status TEXT NOT NULL CHECK (status IN ('invited', 'active', 'disabled')),
14
+ created_at TEXT NOT NULL,
15
+ CHECK (status = 'invited' OR display_name IS NOT NULL)
16
+ );
17
+
18
+ CREATE TABLE access_links (
19
+ id TEXT PRIMARY KEY,
20
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
21
+ purpose TEXT NOT NULL CHECK (purpose IN ('invite', 'reset')),
22
+ token_hash TEXT NOT NULL UNIQUE,
23
+ expires_at TEXT NOT NULL,
24
+ used_at TEXT,
25
+ created_at TEXT NOT NULL
26
+ );
27
+ CREATE INDEX access_links_user ON access_links(user_id);
28
+
29
+ CREATE TABLE sessions (
30
+ id_hash TEXT PRIMARY KEY,
31
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
32
+ created_at TEXT NOT NULL,
33
+ expires_at TEXT NOT NULL
34
+ );
35
+ CREATE INDEX sessions_user ON sessions(user_id);
36
+ CREATE INDEX sessions_expiry ON sessions(expires_at);
37
+
38
+ CREATE TABLE user_settings (
39
+ user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
40
+ agent_scope TEXT NOT NULL DEFAULT 'personal' CHECK (agent_scope IN ('personal', 'current_space')),
41
+ notify_assign_app INTEGER NOT NULL DEFAULT 1 CHECK (notify_assign_app IN (0, 1)),
42
+ notify_assign_email INTEGER NOT NULL DEFAULT 1 CHECK (notify_assign_email IN (0, 1)),
43
+ notify_comment_app INTEGER NOT NULL DEFAULT 1 CHECK (notify_comment_app IN (0, 1)),
44
+ notify_comment_email INTEGER NOT NULL DEFAULT 1 CHECK (notify_comment_email IN (0, 1))
45
+ );
46
+
47
+ CREATE TABLE spaces (
48
+ id TEXT PRIMARY KEY,
49
+ name TEXT NOT NULL CHECK (name <> ''),
50
+ kind TEXT NOT NULL CHECK (kind IN ('personal', 'shared')),
51
+ owner_id TEXT NOT NULL REFERENCES users(id),
52
+ key_prefix TEXT NOT NULL,
53
+ next_task_number INTEGER NOT NULL DEFAULT 1 CHECK (next_task_number >= 1),
54
+ created_at TEXT NOT NULL
55
+ );
56
+ CREATE UNIQUE INDEX spaces_one_personal_per_user ON spaces(owner_id) WHERE kind = 'personal';
57
+
58
+ CREATE TABLE memberships (
59
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
60
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
61
+ role TEXT NOT NULL CHECK (role IN ('owner', 'member')),
62
+ joined_at TEXT NOT NULL,
63
+ PRIMARY KEY (space_id, user_id)
64
+ );
65
+ CREATE INDEX memberships_user ON memberships(user_id);
66
+
67
+ CREATE TABLE invitations (
68
+ id TEXT PRIMARY KEY,
69
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
70
+ email TEXT NOT NULL CHECK (email = lower(email)),
71
+ token_hash TEXT NOT NULL UNIQUE,
72
+ invited_by TEXT NOT NULL REFERENCES users(id),
73
+ created_at TEXT NOT NULL,
74
+ expires_at TEXT NOT NULL,
75
+ accepted_at TEXT,
76
+ revoked_at TEXT
77
+ );
78
+ CREATE INDEX invitations_space ON invitations(space_id);
79
+ CREATE INDEX invitations_email ON invitations(email);
80
+
81
+ CREATE TABLE tasks (
82
+ id TEXT PRIMARY KEY,
83
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
84
+ number INTEGER NOT NULL CHECK (number >= 1),
85
+ parent_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
86
+ depth INTEGER NOT NULL CHECK (depth IN (0, 1, 2)),
87
+ type TEXT NOT NULL CHECK (type IN ('standard', 'test')),
88
+ title TEXT NOT NULL CHECK (title <> ''),
89
+ description TEXT NOT NULL DEFAULT '',
90
+ status TEXT NOT NULL CHECK (status IN ('todo', 'doing', 'review', 'done')),
91
+ priority TEXT NOT NULL CHECK (priority IN ('low', 'medium', 'high', 'critical')),
92
+ position REAL NOT NULL,
93
+ created_by TEXT NOT NULL REFERENCES users(id),
94
+ created_at TEXT NOT NULL,
95
+ updated_at TEXT NOT NULL,
96
+ version INTEGER NOT NULL DEFAULT 1 CHECK (version >= 1),
97
+ UNIQUE (space_id, number),
98
+ CHECK ((parent_id IS NULL) = (depth = 0))
99
+ );
100
+ CREATE INDEX tasks_column ON tasks(space_id, parent_id, status, position);
101
+ CREATE INDEX tasks_parent ON tasks(parent_id);
102
+
103
+ CREATE TABLE task_assignees (
104
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
105
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
106
+ PRIMARY KEY (task_id, user_id)
107
+ );
108
+ CREATE INDEX task_assignees_user ON task_assignees(user_id);
109
+
110
+ CREATE TABLE tags (
111
+ id TEXT PRIMARY KEY,
112
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
113
+ name TEXT NOT NULL CHECK (name <> ''),
114
+ UNIQUE (space_id, name)
115
+ );
116
+
117
+ CREATE TABLE task_tags (
118
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
119
+ tag_id TEXT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
120
+ PRIMARY KEY (task_id, tag_id)
121
+ );
122
+ CREATE INDEX task_tags_tag ON task_tags(tag_id);
123
+
124
+ CREATE TABLE checklist_items (
125
+ id TEXT PRIMARY KEY,
126
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
127
+ text TEXT NOT NULL CHECK (text <> ''),
128
+ done INTEGER NOT NULL DEFAULT 0 CHECK (done IN (0, 1)),
129
+ position REAL NOT NULL
130
+ );
131
+ CREATE INDEX checklist_items_task ON checklist_items(task_id, position);
132
+
133
+ CREATE TABLE test_checkpoints (
134
+ id TEXT PRIMARY KEY,
135
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
136
+ text TEXT NOT NULL CHECK (text <> ''),
137
+ position REAL NOT NULL
138
+ );
139
+ CREATE INDEX test_checkpoints_task ON test_checkpoints(task_id, position);
140
+
141
+ CREATE TABLE test_results (
142
+ checkpoint_id TEXT NOT NULL REFERENCES test_checkpoints(id) ON DELETE CASCADE,
143
+ tester_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
144
+ result TEXT NOT NULL CHECK (result IN ('ok', 'ko', 'blocked', 'untested')),
145
+ comment TEXT NOT NULL DEFAULT '',
146
+ updated_at TEXT NOT NULL,
147
+ PRIMARY KEY (checkpoint_id, tester_id)
148
+ );
149
+ CREATE INDEX test_results_tester ON test_results(tester_id);
150
+
151
+ CREATE TABLE test_reviews (
152
+ id TEXT PRIMARY KEY,
153
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
154
+ tester_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
155
+ text TEXT NOT NULL DEFAULT '',
156
+ submitted_at TEXT,
157
+ request_id TEXT REFERENCES agent_requests(id) ON DELETE SET NULL
158
+ );
159
+ CREATE INDEX test_reviews_task ON test_reviews(task_id, tester_id);
160
+
161
+ CREATE TABLE comments (
162
+ id TEXT PRIMARY KEY,
163
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
164
+ author_id TEXT NOT NULL REFERENCES users(id),
165
+ body TEXT NOT NULL CHECK (body <> ''),
166
+ created_at TEXT NOT NULL,
167
+ edited_at TEXT
168
+ );
169
+ CREATE INDEX comments_task ON comments(task_id, created_at);
170
+
171
+ CREATE TABLE attachments (
172
+ id TEXT PRIMARY KEY,
173
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
174
+ uploader_id TEXT NOT NULL REFERENCES users(id),
175
+ filename TEXT NOT NULL,
176
+ mime TEXT NOT NULL,
177
+ size INTEGER NOT NULL CHECK (size >= 0),
178
+ storage_path TEXT NOT NULL,
179
+ created_at TEXT NOT NULL
180
+ );
181
+ CREATE INDEX attachments_task ON attachments(task_id);
182
+
183
+ CREATE TABLE ideas (
184
+ id TEXT PRIMARY KEY,
185
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
186
+ author_id TEXT NOT NULL REFERENCES users(id),
187
+ text TEXT NOT NULL CHECK (text <> ''),
188
+ origin TEXT NOT NULL CHECK (origin IN ('manual', 'agent')),
189
+ request_id TEXT REFERENCES agent_requests(id) ON DELETE SET NULL,
190
+ promoted_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
191
+ archived_at TEXT,
192
+ created_at TEXT NOT NULL
193
+ );
194
+ CREATE INDEX ideas_space ON ideas(space_id, created_at);
195
+
196
+ CREATE TABLE agent_requests (
197
+ id TEXT PRIMARY KEY,
198
+ author_id TEXT NOT NULL REFERENCES users(id),
199
+ source_space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
200
+ target_space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
201
+ kind TEXT NOT NULL CHECK (kind IN ('idea', 'remark', 'test_review', 'message', 'idea_promotion')),
202
+ context_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
203
+ context_idea_id TEXT REFERENCES ideas(id) ON DELETE SET NULL,
204
+ text TEXT NOT NULL CHECK (text <> ''),
205
+ status TEXT NOT NULL CHECK (status IN ('pending', 'processing', 'done', 'failed', 'cancelled')),
206
+ reply TEXT,
207
+ failure_reason TEXT,
208
+ created_at TEXT NOT NULL,
209
+ updated_at TEXT NOT NULL,
210
+ claimed_at TEXT,
211
+ completed_at TEXT,
212
+ cancelled_by TEXT REFERENCES users(id)
213
+ );
214
+ CREATE INDEX agent_requests_status ON agent_requests(status, created_at);
215
+ CREATE INDEX agent_requests_source ON agent_requests(source_space_id, created_at);
216
+ CREATE INDEX agent_requests_target ON agent_requests(target_space_id);
217
+
218
+ CREATE TABLE agent_actions (
219
+ id TEXT PRIMARY KEY,
220
+ request_id TEXT NOT NULL REFERENCES agent_requests(id) ON DELETE CASCADE,
221
+ seq INTEGER NOT NULL CHECK (seq >= 1),
222
+ entity_type TEXT NOT NULL,
223
+ entity_id TEXT NOT NULL,
224
+ operation TEXT NOT NULL,
225
+ before TEXT,
226
+ after TEXT,
227
+ summary TEXT NOT NULL,
228
+ created_at TEXT NOT NULL,
229
+ undone_at TEXT,
230
+ undone_by TEXT REFERENCES users(id),
231
+ UNIQUE (request_id, seq)
232
+ );
233
+ CREATE INDEX agent_actions_entity ON agent_actions(entity_type, entity_id);
234
+
235
+ CREATE TABLE entity_versions (
236
+ entity_type TEXT NOT NULL CHECK (entity_type IN ('task', 'idea')),
237
+ entity_id TEXT NOT NULL,
238
+ version INTEGER NOT NULL CHECK (version >= 1),
239
+ snapshot TEXT,
240
+ actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'agent')),
241
+ actor_id TEXT NOT NULL,
242
+ request_id TEXT,
243
+ created_at TEXT NOT NULL,
244
+ PRIMARY KEY (entity_type, entity_id, version)
245
+ );
246
+
247
+ CREATE TABLE notifications (
248
+ id TEXT PRIMARY KEY,
249
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
250
+ type TEXT NOT NULL CHECK (type IN ('assigned', 'comment')),
251
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
252
+ actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'agent')),
253
+ actor_id TEXT,
254
+ request_id TEXT,
255
+ comment_id TEXT REFERENCES comments(id) ON DELETE CASCADE,
256
+ created_at TEXT NOT NULL,
257
+ read_at TEXT,
258
+ emailed_at TEXT,
259
+ email_error TEXT
260
+ );
261
+ CREATE INDEX notifications_user ON notifications(user_id, read_at, created_at);
262
+
263
+ CREATE TABLE changes (
264
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
265
+ space_id TEXT,
266
+ user_id TEXT,
267
+ entity_type TEXT NOT NULL,
268
+ entity_id TEXT NOT NULL,
269
+ at TEXT NOT NULL
270
+ );
271
+ CREATE INDEX changes_space ON changes(space_id, seq);
272
+ CREATE INDEX changes_user ON changes(user_id, seq);
273
273
  `,
274
274
  // Une invitation n'a pas de jeton propre : le lien envoyé est l'AccessLink du compte invité.
275
- `
276
- CREATE TABLE invitations_v2 (
277
- id TEXT PRIMARY KEY,
278
- space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
279
- email TEXT NOT NULL CHECK (email = lower(email)),
280
- invited_by TEXT NOT NULL REFERENCES users(id),
281
- created_at TEXT NOT NULL,
282
- expires_at TEXT NOT NULL,
283
- accepted_at TEXT,
284
- revoked_at TEXT
285
- );
286
- INSERT INTO invitations_v2 (id, space_id, email, invited_by, created_at, expires_at, accepted_at, revoked_at)
287
- SELECT id, space_id, email, invited_by, created_at, expires_at, accepted_at, revoked_at FROM invitations;
288
- DROP TABLE invitations;
289
- ALTER TABLE invitations_v2 RENAME TO invitations;
290
- CREATE INDEX invitations_space ON invitations(space_id);
291
- CREATE INDEX invitations_email ON invitations(email);
292
- CREATE UNIQUE INDEX invitations_one_open_per_email ON invitations(space_id, email) WHERE accepted_at IS NULL AND revoked_at IS NULL;
275
+ `
276
+ CREATE TABLE invitations_v2 (
277
+ id TEXT PRIMARY KEY,
278
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
279
+ email TEXT NOT NULL CHECK (email = lower(email)),
280
+ invited_by TEXT NOT NULL REFERENCES users(id),
281
+ created_at TEXT NOT NULL,
282
+ expires_at TEXT NOT NULL,
283
+ accepted_at TEXT,
284
+ revoked_at TEXT
285
+ );
286
+ INSERT INTO invitations_v2 (id, space_id, email, invited_by, created_at, expires_at, accepted_at, revoked_at)
287
+ SELECT id, space_id, email, invited_by, created_at, expires_at, accepted_at, revoked_at FROM invitations;
288
+ DROP TABLE invitations;
289
+ ALTER TABLE invitations_v2 RENAME TO invitations;
290
+ CREATE INDEX invitations_space ON invitations(space_id);
291
+ CREATE INDEX invitations_email ON invitations(email);
292
+ CREATE UNIQUE INDEX invitations_one_open_per_email ON invitations(space_id, email) WHERE accepted_at IS NULL AND revoked_at IS NULL;
293
293
  `,
294
294
  // Tâche concernée par un changement : la tâche elle-même, ou celle qui porte l'élément modifié.
295
- `
296
- ALTER TABLE changes ADD COLUMN task_id TEXT;
295
+ `
296
+ ALTER TABLE changes ADD COLUMN task_id TEXT;
297
297
  `,
298
298
  // Un brouillon de review au plus par testeur et par tâche.
299
- `
300
- CREATE UNIQUE INDEX test_reviews_one_draft ON test_reviews(task_id, tester_id) WHERE submitted_at IS NULL;
299
+ `
300
+ CREATE UNIQUE INDEX test_reviews_one_draft ON test_reviews(task_id, tester_id) WHERE submitted_at IS NULL;
301
301
  `,
302
302
  // Une notification absente de l'application (préférence désactivée) ne sert qu'à suivre l'envoi de son e-mail.
303
- `
304
- ALTER TABLE notifications ADD COLUMN in_app INTEGER NOT NULL DEFAULT 1 CHECK (in_app IN (0, 1));
305
- DROP INDEX notifications_user;
306
- CREATE INDEX notifications_user ON notifications(user_id, in_app, read_at, created_at);
307
- CREATE INDEX notifications_comment ON notifications(comment_id);
303
+ `
304
+ ALTER TABLE notifications ADD COLUMN in_app INTEGER NOT NULL DEFAULT 1 CHECK (in_app IN (0, 1));
305
+ DROP INDEX notifications_user;
306
+ CREATE INDEX notifications_user ON notifications(user_id, in_app, read_at, created_at);
307
+ CREATE INDEX notifications_comment ON notifications(comment_id);
308
308
  `,
309
309
  // Auteur de chaque changement, et demande de l'agent à l'origine d'un commentaire.
310
- `
311
- ALTER TABLE changes ADD COLUMN actor_type TEXT;
312
- ALTER TABLE changes ADD COLUMN actor_id TEXT;
313
- ALTER TABLE changes ADD COLUMN request_id TEXT;
314
- CREATE INDEX changes_entity ON changes(entity_type, entity_id, seq);
315
- ALTER TABLE comments ADD COLUMN request_id TEXT REFERENCES agent_requests(id) ON DELETE SET NULL;
310
+ `
311
+ ALTER TABLE changes ADD COLUMN actor_type TEXT;
312
+ ALTER TABLE changes ADD COLUMN actor_id TEXT;
313
+ ALTER TABLE changes ADD COLUMN request_id TEXT;
314
+ CREATE INDEX changes_entity ON changes(entity_type, entity_id, seq);
315
+ ALTER TABLE comments ADD COLUMN request_id TEXT REFERENCES agent_requests(id) ON DELETE SET NULL;
316
316
  `,
317
317
  // Tâche vérifiée par une tâche de test ; les instantanés de tâche déjà enregistrés reçoivent le champ, vide.
318
- `
319
- ALTER TABLE tasks ADD COLUMN tested_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL;
320
- CREATE INDEX tasks_tested ON tasks(tested_task_id);
321
- UPDATE entity_versions SET snapshot = json_set(snapshot, '$.testedTaskId', json('null')) WHERE entity_type = 'task' AND snapshot IS NOT NULL;
322
- UPDATE agent_actions SET before = json_set(before, '$.testedTaskId', json('null')) WHERE entity_type = 'task' AND before IS NOT NULL;
323
- UPDATE agent_actions SET after = json_set(after, '$.testedTaskId', json('null')) WHERE entity_type = 'task' AND after IS NOT NULL;
318
+ `
319
+ ALTER TABLE tasks ADD COLUMN tested_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL;
320
+ CREATE INDEX tasks_tested ON tasks(tested_task_id);
321
+ UPDATE entity_versions SET snapshot = json_set(snapshot, '$.testedTaskId', json('null')) WHERE entity_type = 'task' AND snapshot IS NOT NULL;
322
+ UPDATE agent_actions SET before = json_set(before, '$.testedTaskId', json('null')) WHERE entity_type = 'task' AND before IS NOT NULL;
323
+ UPDATE agent_actions SET after = json_set(after, '$.testedTaskId', json('null')) WHERE entity_type = 'task' AND after IS NOT NULL;
324
324
  `,
325
325
  // Une demande vise l'espace où elle est écrite : le réglage de portée disparaît et chaque demande appartient à l'espace qu'elle vise.
326
- `
327
- ALTER TABLE user_settings DROP COLUMN agent_scope;
328
- UPDATE agent_requests SET source_space_id = target_space_id WHERE source_space_id <> target_space_id;
326
+ `
327
+ ALTER TABLE user_settings DROP COLUMN agent_scope;
328
+ UPDATE agent_requests SET source_space_id = target_space_id WHERE source_space_id <> target_space_id;
329
329
  `,
330
330
  // Un lien d'invitation est rattaché à l'invitation qui l'a émis ; les liens d'invitation non rattachés ne servent plus.
331
331
  // Le destinataire d'une invitation peut la refuser.
332
- `
333
- ALTER TABLE access_links ADD COLUMN invitation_id TEXT REFERENCES invitations(id) ON DELETE CASCADE;
334
- CREATE INDEX access_links_invitation ON access_links(invitation_id);
335
- DELETE FROM access_links WHERE purpose = 'invite' AND used_at IS NULL;
336
- ALTER TABLE invitations ADD COLUMN declined_at TEXT;
337
- DROP INDEX invitations_one_open_per_email;
338
- CREATE UNIQUE INDEX invitations_one_open_per_email ON invitations(space_id, email) WHERE accepted_at IS NULL AND revoked_at IS NULL AND declined_at IS NULL;
332
+ `
333
+ ALTER TABLE access_links ADD COLUMN invitation_id TEXT REFERENCES invitations(id) ON DELETE CASCADE;
334
+ CREATE INDEX access_links_invitation ON access_links(invitation_id);
335
+ DELETE FROM access_links WHERE purpose = 'invite' AND used_at IS NULL;
336
+ ALTER TABLE invitations ADD COLUMN declined_at TEXT;
337
+ DROP INDEX invitations_one_open_per_email;
338
+ CREATE UNIQUE INDEX invitations_one_open_per_email ON invitations(space_id, email) WHERE accepted_at IS NULL AND revoked_at IS NULL AND declined_at IS NULL;
339
339
  `,
340
340
  // Jetons d'agent : un jeton couvre un espace pour un utilisateur ; seul le hash sha256 du jeton est stocké.
341
- `
342
- CREATE TABLE agent_tokens (
343
- id TEXT PRIMARY KEY,
344
- user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
345
- space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
346
- name TEXT NOT NULL CHECK (name <> ''),
347
- prefix TEXT NOT NULL,
348
- token_hash TEXT NOT NULL UNIQUE,
349
- created_at TEXT NOT NULL,
350
- last_used_at TEXT,
351
- expires_at TEXT,
352
- revoked_at TEXT
353
- );
354
- CREATE INDEX agent_tokens_user ON agent_tokens(user_id, created_at);
355
- CREATE INDEX agent_tokens_space ON agent_tokens(space_id);
341
+ `
342
+ CREATE TABLE agent_tokens (
343
+ id TEXT PRIMARY KEY,
344
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
345
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
346
+ name TEXT NOT NULL CHECK (name <> ''),
347
+ prefix TEXT NOT NULL,
348
+ token_hash TEXT NOT NULL UNIQUE,
349
+ created_at TEXT NOT NULL,
350
+ last_used_at TEXT,
351
+ expires_at TEXT,
352
+ revoked_at TEXT
353
+ );
354
+ CREATE INDEX agent_tokens_user ON agent_tokens(user_id, created_at);
355
+ CREATE INDEX agent_tokens_space ON agent_tokens(space_id);
356
356
  `,
357
357
  // Jeton qui a pris une demande, et jeton auteur de chaque action de l'agent et de chaque version qu'il crée.
358
- `
359
- ALTER TABLE agent_requests ADD COLUMN claimed_by_token_id TEXT REFERENCES agent_tokens(id) ON DELETE SET NULL;
360
- CREATE INDEX agent_requests_claimed_by ON agent_requests(claimed_by_token_id);
361
- ALTER TABLE agent_actions ADD COLUMN token_id TEXT REFERENCES agent_tokens(id) ON DELETE SET NULL;
362
- ALTER TABLE entity_versions ADD COLUMN token_id TEXT REFERENCES agent_tokens(id) ON DELETE SET NULL;
363
- CREATE INDEX agent_requests_target_status ON agent_requests(target_space_id, status, created_at);
358
+ `
359
+ ALTER TABLE agent_requests ADD COLUMN claimed_by_token_id TEXT REFERENCES agent_tokens(id) ON DELETE SET NULL;
360
+ CREATE INDEX agent_requests_claimed_by ON agent_requests(claimed_by_token_id);
361
+ ALTER TABLE agent_actions ADD COLUMN token_id TEXT REFERENCES agent_tokens(id) ON DELETE SET NULL;
362
+ ALTER TABLE entity_versions ADD COLUMN token_id TEXT REFERENCES agent_tokens(id) ON DELETE SET NULL;
363
+ CREATE INDEX agent_requests_target_status ON agent_requests(target_space_id, status, created_at);
364
364
  `,
365
365
  // Retire l'espace source des demandes, identique à l'espace visé : une demande n'a qu'un espace.
366
366
  // L'index (target_space_id, status, created_at) sert aussi les recherches par espace seul.
367
- `
368
- DROP INDEX agent_requests_source;
369
- DROP INDEX agent_requests_target;
370
- ALTER TABLE agent_requests DROP COLUMN source_space_id;
367
+ `
368
+ DROP INDEX agent_requests_source;
369
+ DROP INDEX agent_requests_target;
370
+ ALTER TABLE agent_requests DROP COLUMN source_space_id;
371
371
  `,
372
372
  // Révoque les jetons dont le propriétaire n'est plus membre de l'espace couvert : un départ révoque ses jetons.
373
- `
374
- UPDATE agent_tokens SET revoked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
375
- WHERE revoked_at IS NULL
376
- AND NOT EXISTS (SELECT 1 FROM memberships m WHERE m.space_id = agent_tokens.space_id AND m.user_id = agent_tokens.user_id);
373
+ `
374
+ UPDATE agent_tokens SET revoked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
375
+ WHERE revoked_at IS NULL
376
+ AND NOT EXISTS (SELECT 1 FROM memberships m WHERE m.space_id = agent_tokens.space_id AND m.user_id = agent_tokens.user_id);
377
+ `,
378
+ // Administrateurs du site, et espaces où le runner est autorisé (aucun par défaut).
379
+ `
380
+ ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0;
381
+ ALTER TABLE spaces ADD COLUMN runner_allowed INTEGER NOT NULL DEFAULT 0;
382
+ `,
383
+ // Jeton du runner : un par espace, au nom de son propriétaire, dont la valeur change à chaque session.
384
+ `
385
+ ALTER TABLE agent_tokens ADD COLUMN is_runner INTEGER NOT NULL DEFAULT 0;
386
+ `,
387
+ // Nombre d'échanges précédents de l'auteur transmis à l'agent avec un message.
388
+ `
389
+ ALTER TABLE user_settings ADD COLUMN assistant_history INTEGER NOT NULL DEFAULT 5 CHECK (assistant_history BETWEEN 5 AND 20);
390
+ `,
391
+ // Préférences de notification d'un membre propres à un espace.
392
+ `
393
+ CREATE TABLE space_notification_settings (
394
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
395
+ space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
396
+ notify_assign_app INTEGER NOT NULL CHECK (notify_assign_app IN (0, 1)),
397
+ notify_assign_email INTEGER NOT NULL CHECK (notify_assign_email IN (0, 1)),
398
+ notify_comment_app INTEGER NOT NULL CHECK (notify_comment_app IN (0, 1)),
399
+ notify_comment_email INTEGER NOT NULL CHECK (notify_comment_email IN (0, 1)),
400
+ PRIMARY KEY (user_id, space_id)
401
+ );
377
402
  `,
378
403
  ];
379
404
  /** Version du schéma attendue par ce code. */