@cluesmith/codev 3.3.2 → 3.3.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 (157) hide show
  1. package/dashboard-dist/assets/{index-D3XaKpZW.js → index-CUIjS_Vb.js} +41 -41
  2. package/dashboard-dist/assets/index-CUIjS_Vb.js.map +1 -0
  3. package/dashboard-dist/assets/index-DnYLy5C_.css +32 -0
  4. package/dashboard-dist/index.html +2 -2
  5. package/dist/agent-farm/commands/inbox.d.ts.map +1 -1
  6. package/dist/agent-farm/commands/inbox.js +67 -10
  7. package/dist/agent-farm/commands/inbox.js.map +1 -1
  8. package/dist/agent-farm/commands/interrupt.d.ts +4 -1
  9. package/dist/agent-farm/commands/interrupt.d.ts.map +1 -1
  10. package/dist/agent-farm/commands/interrupt.js +6 -3
  11. package/dist/agent-farm/commands/interrupt.js.map +1 -1
  12. package/dist/agent-farm/commands/reset.d.ts +4 -1
  13. package/dist/agent-farm/commands/reset.d.ts.map +1 -1
  14. package/dist/agent-farm/commands/reset.js +6 -3
  15. package/dist/agent-farm/commands/reset.js.map +1 -1
  16. package/dist/agent-farm/commands/send.d.ts +28 -0
  17. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  18. package/dist/agent-farm/commands/send.js +64 -6
  19. package/dist/agent-farm/commands/send.js.map +1 -1
  20. package/dist/agent-farm/db/index.d.ts +3 -1
  21. package/dist/agent-farm/db/index.d.ts.map +1 -1
  22. package/dist/agent-farm/db/index.js +8 -443
  23. package/dist/agent-farm/db/index.js.map +1 -1
  24. package/dist/agent-farm/db/mailbox.d.ts +47 -7
  25. package/dist/agent-farm/db/mailbox.d.ts.map +1 -1
  26. package/dist/agent-farm/db/mailbox.js +56 -12
  27. package/dist/agent-farm/db/mailbox.js.map +1 -1
  28. package/dist/agent-farm/db/migrations.d.ts +57 -0
  29. package/dist/agent-farm/db/migrations.d.ts.map +1 -0
  30. package/dist/agent-farm/db/migrations.js +530 -0
  31. package/dist/agent-farm/db/migrations.js.map +1 -0
  32. package/dist/agent-farm/db/schema.d.ts +1 -1
  33. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  34. package/dist/agent-farm/db/schema.js +1 -0
  35. package/dist/agent-farm/db/schema.js.map +1 -1
  36. package/dist/agent-farm/db/types.d.ts +22 -0
  37. package/dist/agent-farm/db/types.d.ts.map +1 -1
  38. package/dist/agent-farm/db/types.js.map +1 -1
  39. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  40. package/dist/agent-farm/lib/tunnel-client.js +43 -14
  41. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  42. package/dist/agent-farm/servers/cron-delivery.d.ts +7 -1
  43. package/dist/agent-farm/servers/cron-delivery.d.ts.map +1 -1
  44. package/dist/agent-farm/servers/cron-delivery.js +2 -1
  45. package/dist/agent-farm/servers/cron-delivery.js.map +1 -1
  46. package/dist/agent-farm/servers/gate-profiles.d.ts +21 -0
  47. package/dist/agent-farm/servers/gate-profiles.d.ts.map +1 -1
  48. package/dist/agent-farm/servers/gate-profiles.js +36 -4
  49. package/dist/agent-farm/servers/gate-profiles.js.map +1 -1
  50. package/dist/agent-farm/servers/mailbox-delivery.d.ts +83 -8
  51. package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -1
  52. package/dist/agent-farm/servers/mailbox-delivery.js +102 -26
  53. package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -1
  54. package/dist/agent-farm/servers/mailbox-wiring.d.ts +48 -15
  55. package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -1
  56. package/dist/agent-farm/servers/mailbox-wiring.js +116 -22
  57. package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -1
  58. package/dist/agent-farm/servers/render-gate.d.ts +24 -1
  59. package/dist/agent-farm/servers/render-gate.d.ts.map +1 -1
  60. package/dist/agent-farm/servers/render-gate.js +50 -12
  61. package/dist/agent-farm/servers/render-gate.js.map +1 -1
  62. package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
  63. package/dist/agent-farm/servers/tower-cron.js +3 -2
  64. package/dist/agent-farm/servers/tower-cron.js.map +1 -1
  65. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  66. package/dist/agent-farm/servers/tower-instances.js +11 -3
  67. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  68. package/dist/agent-farm/servers/tower-routes.d.ts +32 -0
  69. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  70. package/dist/agent-farm/servers/tower-routes.js +133 -13
  71. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  72. package/dist/agent-farm/servers/tower-server.js +15 -2
  73. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  74. package/dist/agent-farm/servers/tower-terminals.d.ts +21 -0
  75. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  76. package/dist/agent-farm/servers/tower-terminals.js +53 -5
  77. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  78. package/dist/agent-farm/servers/tower-utils.d.ts +35 -0
  79. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
  80. package/dist/agent-farm/servers/tower-utils.js +39 -0
  81. package/dist/agent-farm/servers/tower-utils.js.map +1 -1
  82. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
  83. package/dist/agent-farm/servers/tower-websocket.js +8 -1
  84. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  85. package/dist/agent-farm/utils/message-format.d.ts +78 -2
  86. package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
  87. package/dist/agent-farm/utils/message-format.js +102 -5
  88. package/dist/agent-farm/utils/message-format.js.map +1 -1
  89. package/dist/cli.d.ts.map +1 -1
  90. package/dist/cli.js +3 -1
  91. package/dist/cli.js.map +1 -1
  92. package/dist/commands/doctor.js +2 -2
  93. package/dist/commands/generate-image.d.ts +20 -1
  94. package/dist/commands/generate-image.d.ts.map +1 -1
  95. package/dist/commands/generate-image.js +188 -1
  96. package/dist/commands/generate-image.js.map +1 -1
  97. package/dist/commands/porch/prompts.d.ts.map +1 -1
  98. package/dist/commands/porch/prompts.js +18 -2
  99. package/dist/commands/porch/prompts.js.map +1 -1
  100. package/dist/lib/forge-contracts.d.ts +66 -5
  101. package/dist/lib/forge-contracts.d.ts.map +1 -1
  102. package/dist/lib/forge-contracts.js.map +1 -1
  103. package/dist/lib/forge.d.ts +1 -1
  104. package/dist/lib/forge.d.ts.map +1 -1
  105. package/dist/lib/forge.js +33 -11
  106. package/dist/lib/forge.js.map +1 -1
  107. package/dist/terminal/pty-manager.d.ts +10 -1
  108. package/dist/terminal/pty-manager.d.ts.map +1 -1
  109. package/dist/terminal/pty-manager.js +38 -9
  110. package/dist/terminal/pty-manager.js.map +1 -1
  111. package/dist/terminal/pty-session.d.ts +52 -4
  112. package/dist/terminal/pty-session.d.ts.map +1 -1
  113. package/dist/terminal/pty-session.js +157 -18
  114. package/dist/terminal/pty-session.js.map +1 -1
  115. package/dist/terminal/shellper-client.d.ts +73 -0
  116. package/dist/terminal/shellper-client.d.ts.map +1 -1
  117. package/dist/terminal/shellper-client.js +151 -0
  118. package/dist/terminal/shellper-client.js.map +1 -1
  119. package/dist/terminal/shellper-main.js +20 -3
  120. package/dist/terminal/shellper-main.js.map +1 -1
  121. package/dist/terminal/shellper-process.d.ts +2 -0
  122. package/dist/terminal/shellper-process.d.ts.map +1 -1
  123. package/dist/terminal/shellper-process.js +17 -1
  124. package/dist/terminal/shellper-process.js.map +1 -1
  125. package/dist/terminal/shellper-protocol.d.ts +28 -0
  126. package/dist/terminal/shellper-protocol.d.ts.map +1 -1
  127. package/dist/terminal/shellper-protocol.js.map +1 -1
  128. package/package.json +4 -4
  129. package/scripts/forge/gitea/_lib.sh +214 -0
  130. package/scripts/forge/gitea/issue-comment.sh +8 -1
  131. package/scripts/forge/gitea/issue-view.sh +95 -4
  132. package/scripts/forge/gitea/pr-create.sh +155 -0
  133. package/scripts/forge/gitea/pr-exists.sh +32 -4
  134. package/scripts/forge/gitea/pr-list.sh +40 -31
  135. package/scripts/forge/gitea/pr-view.sh +65 -4
  136. package/scripts/forge/gitea/recently-merged.sh +91 -21
  137. package/scripts/forge/gitea/user-identity.sh +30 -1
  138. package/scripts/forge/github/issue-view.sh +5 -2
  139. package/scripts/forge/github/pr-create.sh +47 -0
  140. package/scripts/forge/github/pr-search.sh +3 -1
  141. package/scripts/forge/gitlab/issue-view.sh +24 -4
  142. package/scripts/forge/gitlab/pr-create.sh +53 -0
  143. package/scripts/forge/gitlab/pr-search.sh +3 -1
  144. package/scripts/forge/linear/issue-view.sh +13 -2
  145. package/skeleton/.claude/skills/generate-image/SKILL.md +22 -1
  146. package/skeleton/.codex/skills/generate-image/SKILL.md +22 -1
  147. package/skeleton/protocols/air/prompts/pr.md +6 -1
  148. package/skeleton/protocols/aspir/prompts/review.md +6 -1
  149. package/skeleton/protocols/bugfix/prompts/pr.md +6 -1
  150. package/skeleton/protocols/bugfix/protocol.md +2 -1
  151. package/skeleton/protocols/maintain/prompts/review.md +6 -1
  152. package/skeleton/protocols/pir/prompts/review.md +10 -7
  153. package/skeleton/protocols/spir/prompts/review.md +6 -1
  154. package/skeleton/resources/commands/agent-farm.md +1 -1
  155. package/templates/tower.html +16 -1
  156. package/dashboard-dist/assets/index-BZRTEMtH.css +0 -32
  157. package/dashboard-dist/assets/index-D3XaKpZW.js.map +0 -1
@@ -0,0 +1,530 @@
1
+ /**
2
+ * global.db forward-only migrations (Issue #1476).
3
+ *
4
+ * The migration sequence used to live inline inside the private
5
+ * `ensureGlobalDatabase()` path in `db/index.ts`, so migration tests could only
6
+ * drive hand-maintained *replicas* of it. This module is the single source of
7
+ * truth both production init and the tests call:
8
+ *
9
+ * - `ensureGlobalDatabase()` calls `runGlobalMigrations(db)` on an existing
10
+ * database (fresh installs get the final shape from GLOBAL_SCHEMA and stamp
11
+ * every marker up to `GLOBAL_CURRENT_VERSION` instead).
12
+ * - `__tests__/spec-1313-migration.test.ts` builds a database at a historical
13
+ * version and calls the very same function, so the SQL under test is the SQL
14
+ * that ships.
15
+ *
16
+ * The runner takes an explicit `db` handle (matching `db/mailbox.ts` /
17
+ * `db/consolidate.ts`) — it never opens a connection, reads a singleton, or sets
18
+ * pragmas. Every step is idempotent and gated on its `_migrations` marker, so
19
+ * calling it repeatedly on the same handle is a no-op. Migrations are
20
+ * forward-only by project convention; there is no reverse SQL.
21
+ */
22
+ import { existsSync, readdirSync, renameSync } from 'node:fs';
23
+ import { homedir } from 'node:os';
24
+ import { join } from 'node:path';
25
+ /** Current migration version — bump when adding new migrations. */
26
+ export const GLOBAL_CURRENT_VERSION = 18;
27
+ /**
28
+ * Run every outstanding global.db migration against `db`.
29
+ *
30
+ * Safe to call on any existing database that reached its recorded version through
31
+ * migrations: each step checks its own `_migrations` marker first, so applied steps
32
+ * are skipped and the call converges on the `GLOBAL_CURRENT_VERSION` shape.
33
+ *
34
+ * NOT for a fresh database. A GLOBAL_SCHEMA-shaped database with no markers would run
35
+ * v5 against the long-renamed `terminal_sessions.project_path` — which is why
36
+ * `ensureGlobalDatabase()` stamps every marker on the fresh path instead of running
37
+ * the chain. That shape is rejected at entry with a named error rather than an opaque
38
+ * mid-chain SQLite failure.
39
+ *
40
+ * @throws if the database carries the post-v9 `workspace_path` shape without the v9
41
+ * marker to match (a fresh schema that never walked the chain).
42
+ */
43
+ export function runGlobalMigrations(db, options = {}) {
44
+ const log = options.log ?? ((message) => console.log(message));
45
+ const runDir = options.runDir ?? join(homedir(), '.codev', 'run');
46
+ // Ensure _migrations table exists for tracking
47
+ db.exec(`CREATE TABLE IF NOT EXISTS _migrations (
48
+ version INTEGER PRIMARY KEY,
49
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
50
+ )`);
51
+ // Precondition. Steps v5/v7/v8/v9 read and rebuild the project_path-era tables, so
52
+ // the chain only makes sense on a database that reached its recorded version through
53
+ // migrations. A fresh GLOBAL_SCHEMA database with no markers would instead die at v5
54
+ // with an opaque `no such column: project_path` — so name that shape here. Production
55
+ // never reaches this (ensureGlobalDatabase stamps every marker on the fresh path); it
56
+ // exists because the runner is now callable from anywhere.
57
+ const terminalSessions = db
58
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='terminal_sessions'")
59
+ .get();
60
+ if (terminalSessions) {
61
+ const renamed = db.prepare(`PRAGMA table_info(terminal_sessions)`).all()
62
+ .some((c) => c.name === 'workspace_path');
63
+ const v9Marker = db.prepare('SELECT version FROM _migrations WHERE version = 9').get();
64
+ if (renamed && !v9Marker) {
65
+ throw new Error('runGlobalMigrations: refusing to run on a database whose terminal_sessions is already ' +
66
+ 'workspace_path-shaped but has no v9 migration marker. This is a fresh GLOBAL_SCHEMA ' +
67
+ 'database that never walked the chain — stamp markers 1..GLOBAL_CURRENT_VERSION instead ' +
68
+ '(see ensureGlobalDatabase), or the chain will fail mid-way on renamed columns.');
69
+ }
70
+ }
71
+ // Migration v2: No-op (previously added columns to port_allocations, now removed by Spec 0098)
72
+ const v2 = db.prepare('SELECT version FROM _migrations WHERE version = 2').get();
73
+ if (!v2) {
74
+ db.prepare('INSERT INTO _migrations (version) VALUES (2)').run();
75
+ }
76
+ // Migration v3: Add terminal_sessions table (Spec 0090 TICK-001)
77
+ const v3 = db.prepare('SELECT version FROM _migrations WHERE version = 3').get();
78
+ if (!v3) {
79
+ // Create terminal_sessions table if it doesn't exist
80
+ db.exec(`
81
+ CREATE TABLE IF NOT EXISTS terminal_sessions (
82
+ id TEXT PRIMARY KEY,
83
+ project_path TEXT NOT NULL,
84
+ type TEXT NOT NULL CHECK(type IN ('architect', 'builder', 'shell')),
85
+ role_id TEXT,
86
+ pid INTEGER,
87
+ tmux_session TEXT,
88
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
89
+ );
90
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_project ON terminal_sessions(project_path);
91
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_type ON terminal_sessions(type);
92
+ `);
93
+ db.prepare('INSERT INTO _migrations (version) VALUES (3)').run();
94
+ log('[info] Created terminal_sessions table (Spec 0090 TICK-001)');
95
+ }
96
+ // Migration v4: Add file_tabs table (Spec 0099 Phase 4)
97
+ const v4 = db.prepare('SELECT version FROM _migrations WHERE version = 4').get();
98
+ if (!v4) {
99
+ db.exec(`
100
+ CREATE TABLE IF NOT EXISTS file_tabs (
101
+ id TEXT PRIMARY KEY,
102
+ project_path TEXT NOT NULL,
103
+ file_path TEXT NOT NULL,
104
+ created_at INTEGER NOT NULL
105
+ );
106
+ CREATE INDEX IF NOT EXISTS idx_file_tabs_project ON file_tabs(project_path);
107
+ `);
108
+ db.prepare('INSERT INTO _migrations (version) VALUES (4)').run();
109
+ log('[info] Created file_tabs table (Spec 0099 Phase 4)');
110
+ }
111
+ // Migration v5: Add known_projects table for persistent project registry
112
+ const v5 = db.prepare('SELECT version FROM _migrations WHERE version = 5').get();
113
+ if (!v5) {
114
+ db.exec(`
115
+ CREATE TABLE IF NOT EXISTS known_projects (
116
+ project_path TEXT PRIMARY KEY,
117
+ name TEXT NOT NULL,
118
+ last_launched_at TEXT NOT NULL DEFAULT (datetime('now'))
119
+ );
120
+ `);
121
+ // Seed from existing terminal_sessions so current projects appear immediately
122
+ db.exec(`
123
+ INSERT OR IGNORE INTO known_projects (project_path, name, last_launched_at)
124
+ SELECT DISTINCT project_path, '', datetime('now') FROM terminal_sessions;
125
+ `);
126
+ db.prepare('INSERT INTO _migrations (version) VALUES (5)').run();
127
+ log('[info] Created known_projects table');
128
+ }
129
+ // Migration v6: Add shepherd columns to terminal_sessions (Spec 0104)
130
+ const v6 = db.prepare('SELECT version FROM _migrations WHERE version = 6').get();
131
+ if (!v6) {
132
+ const cols = ['shepherd_socket TEXT', 'shepherd_pid INTEGER', 'shepherd_start_time INTEGER'];
133
+ for (const col of cols) {
134
+ try {
135
+ db.exec(`ALTER TABLE terminal_sessions ADD COLUMN ${col}`);
136
+ }
137
+ catch {
138
+ // Column already exists (fresh install ran updated schema)
139
+ }
140
+ }
141
+ db.prepare('INSERT INTO _migrations (version) VALUES (6)').run();
142
+ log('[info] Added shepherd columns to terminal_sessions (Spec 0104)');
143
+ }
144
+ // Migration v7: Drop tmux_session column from terminal_sessions (Spec 0104 Phase 4)
145
+ const v7 = db.prepare('SELECT version FROM _migrations WHERE version = 7').get();
146
+ if (!v7) {
147
+ // SQLite table-rebuild pattern to drop the tmux_session column
148
+ try {
149
+ db.exec(`
150
+ CREATE TABLE IF NOT EXISTS terminal_sessions_new (
151
+ id TEXT PRIMARY KEY,
152
+ project_path TEXT NOT NULL,
153
+ type TEXT NOT NULL CHECK(type IN ('architect', 'builder', 'shell')),
154
+ role_id TEXT,
155
+ pid INTEGER,
156
+ shepherd_socket TEXT,
157
+ shepherd_pid INTEGER,
158
+ shepherd_start_time INTEGER,
159
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
160
+ );
161
+ INSERT OR IGNORE INTO terminal_sessions_new
162
+ SELECT id, project_path, type, role_id, pid, shepherd_socket, shepherd_pid, shepherd_start_time, created_at
163
+ FROM terminal_sessions;
164
+ DROP TABLE terminal_sessions;
165
+ ALTER TABLE terminal_sessions_new RENAME TO terminal_sessions;
166
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_project ON terminal_sessions(project_path);
167
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_type ON terminal_sessions(type);
168
+ `);
169
+ }
170
+ catch {
171
+ // Table may already be in the correct schema (fresh install)
172
+ }
173
+ db.prepare('INSERT INTO _migrations (version) VALUES (7)').run();
174
+ log('[info] Dropped tmux_session column from terminal_sessions (Spec 0104)');
175
+ }
176
+ // Migration v8: Rename shepherd_* columns to shellper_* (Spec 0106)
177
+ const v8 = db.prepare('SELECT version FROM _migrations WHERE version = 8').get();
178
+ if (!v8) {
179
+ try {
180
+ db.exec(`
181
+ CREATE TABLE IF NOT EXISTS terminal_sessions_new (
182
+ id TEXT PRIMARY KEY,
183
+ project_path TEXT NOT NULL,
184
+ type TEXT NOT NULL CHECK(type IN ('architect', 'builder', 'shell')),
185
+ role_id TEXT,
186
+ pid INTEGER,
187
+ shellper_socket TEXT,
188
+ shellper_pid INTEGER,
189
+ shellper_start_time INTEGER,
190
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
191
+ );
192
+ INSERT OR IGNORE INTO terminal_sessions_new
193
+ SELECT id, project_path, type, role_id, pid, shepherd_socket, shepherd_pid, shepherd_start_time, created_at
194
+ FROM terminal_sessions;
195
+ DROP TABLE terminal_sessions;
196
+ ALTER TABLE terminal_sessions_new RENAME TO terminal_sessions;
197
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_project ON terminal_sessions(project_path);
198
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_type ON terminal_sessions(type);
199
+ UPDATE terminal_sessions SET shellper_socket = REPLACE(shellper_socket, 'shepherd-', 'shellper-')
200
+ WHERE shellper_socket LIKE '%shepherd-%';
201
+ `);
202
+ }
203
+ catch {
204
+ // Table may already be in the correct schema (fresh install)
205
+ }
206
+ // Rename physical socket files on disk
207
+ try {
208
+ if (existsSync(runDir)) {
209
+ const files = readdirSync(runDir);
210
+ for (const file of files) {
211
+ if (file.startsWith('shepherd-') && file.endsWith('.sock')) {
212
+ const newName = file.replace('shepherd-', 'shellper-');
213
+ try {
214
+ renameSync(join(runDir, file), join(runDir, newName));
215
+ }
216
+ catch {
217
+ // Skip files that can't be renamed (missing, permissions, etc.)
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+ catch {
224
+ // Skip if run directory doesn't exist or can't be read
225
+ }
226
+ db.prepare('INSERT INTO _migrations (version) VALUES (8)').run();
227
+ log('[info] Renamed shepherd columns to shellper in terminal_sessions (Spec 0106)');
228
+ }
229
+ // Migration v9: Rename project_path → workspace_path in all tables (Spec 0112)
230
+ // Note: Fresh installs never reach here (handled by the caller), so old column
231
+ // names are guaranteed.
232
+ // Wrapped in a transaction for atomicity — all three renames succeed or none do.
233
+ const v9 = db.prepare('SELECT version FROM _migrations WHERE version = 9').get();
234
+ if (!v9) {
235
+ const migrate = db.transaction(() => {
236
+ // 1. Rename terminal_sessions.project_path → workspace_path
237
+ db.exec(`
238
+ CREATE TABLE IF NOT EXISTS terminal_sessions_new (
239
+ id TEXT PRIMARY KEY,
240
+ workspace_path TEXT NOT NULL,
241
+ type TEXT NOT NULL CHECK(type IN ('architect', 'builder', 'shell')),
242
+ role_id TEXT,
243
+ pid INTEGER,
244
+ shellper_socket TEXT,
245
+ shellper_pid INTEGER,
246
+ shellper_start_time INTEGER,
247
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
248
+ );
249
+ INSERT OR IGNORE INTO terminal_sessions_new
250
+ SELECT id, project_path, type, role_id, pid, shellper_socket, shellper_pid, shellper_start_time, created_at
251
+ FROM terminal_sessions;
252
+ DROP TABLE terminal_sessions;
253
+ ALTER TABLE terminal_sessions_new RENAME TO terminal_sessions;
254
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_workspace ON terminal_sessions(workspace_path);
255
+ CREATE INDEX IF NOT EXISTS idx_terminal_sessions_type ON terminal_sessions(type);
256
+ `);
257
+ // 2. Rename file_tabs.project_path → workspace_path
258
+ db.exec(`
259
+ CREATE TABLE IF NOT EXISTS file_tabs_new (
260
+ id TEXT PRIMARY KEY,
261
+ workspace_path TEXT NOT NULL,
262
+ file_path TEXT NOT NULL,
263
+ created_at INTEGER NOT NULL
264
+ );
265
+ INSERT OR IGNORE INTO file_tabs_new
266
+ SELECT id, project_path, file_path, created_at
267
+ FROM file_tabs;
268
+ DROP TABLE file_tabs;
269
+ ALTER TABLE file_tabs_new RENAME TO file_tabs;
270
+ CREATE INDEX IF NOT EXISTS idx_file_tabs_workspace ON file_tabs(workspace_path);
271
+ `);
272
+ // 3. Rename known_projects → known_workspaces with project_path → workspace_path
273
+ db.exec(`
274
+ CREATE TABLE IF NOT EXISTS known_workspaces (
275
+ workspace_path TEXT PRIMARY KEY,
276
+ name TEXT NOT NULL,
277
+ last_launched_at TEXT NOT NULL DEFAULT (datetime('now'))
278
+ );
279
+ INSERT OR IGNORE INTO known_workspaces (workspace_path, name, last_launched_at)
280
+ SELECT project_path, name, last_launched_at FROM known_projects;
281
+ DROP TABLE IF EXISTS known_projects;
282
+ `);
283
+ db.prepare('INSERT INTO _migrations (version) VALUES (9)').run();
284
+ });
285
+ migrate();
286
+ log('[info] Renamed project_path → workspace_path in global tables (Spec 0112)');
287
+ }
288
+ // Migration v10: Add cron_tasks table (Spec 399)
289
+ const v10 = db.prepare('SELECT version FROM _migrations WHERE version = 10').get();
290
+ if (!v10) {
291
+ db.exec(`
292
+ CREATE TABLE IF NOT EXISTS cron_tasks (
293
+ id TEXT PRIMARY KEY,
294
+ workspace_path TEXT NOT NULL,
295
+ task_name TEXT NOT NULL,
296
+ last_run INTEGER,
297
+ last_result TEXT,
298
+ last_output TEXT,
299
+ enabled INTEGER NOT NULL DEFAULT 1,
300
+ UNIQUE(workspace_path, task_name)
301
+ );
302
+ `);
303
+ db.prepare('INSERT INTO _migrations (version) VALUES (10)').run();
304
+ log('[info] Created cron_tasks table (Spec 399)');
305
+ }
306
+ // Migration v11: Add label column to terminal_sessions (Spec 468)
307
+ const v11 = db.prepare('SELECT version FROM _migrations WHERE version = 11').get();
308
+ if (!v11) {
309
+ try {
310
+ db.exec(`ALTER TABLE terminal_sessions ADD COLUMN label TEXT`);
311
+ }
312
+ catch {
313
+ // Column may already exist from a fresh install
314
+ }
315
+ db.prepare('INSERT INTO _migrations (version) VALUES (11)').run();
316
+ log('[info] Added label column to terminal_sessions (Spec 468)');
317
+ }
318
+ // Migration v12: Add cwd column to terminal_sessions (Bugfix #506)
319
+ const v12 = db.prepare('SELECT version FROM _migrations WHERE version = 12').get();
320
+ if (!v12) {
321
+ try {
322
+ db.exec(`ALTER TABLE terminal_sessions ADD COLUMN cwd TEXT`);
323
+ }
324
+ catch {
325
+ // Column may already exist from a fresh install
326
+ }
327
+ db.prepare('INSERT INTO _migrations (version) VALUES (12)').run();
328
+ log('[info] Added cwd column to terminal_sessions (Bugfix #506)');
329
+ }
330
+ // Migration v13: Backfill terminal_sessions.role_id for legacy architect rows (Spec 755)
331
+ // Pre-v13 rows for architects always stored role_id as NULL because there was only
332
+ // ever one architect per workspace. Multi-architect support requires the name to be
333
+ // present in role_id so reconnect can re-key the in-memory map. The idempotent
334
+ // backfill sets role_id = 'main' for legacy rows; subsequent architect rows write
335
+ // their explicit name and are unaffected.
336
+ const v13 = db.prepare('SELECT version FROM _migrations WHERE version = 13').get();
337
+ if (!v13) {
338
+ db.prepare(`
339
+ UPDATE terminal_sessions
340
+ SET role_id = 'main'
341
+ WHERE type = 'architect' AND role_id IS NULL
342
+ `).run();
343
+ db.prepare('INSERT INTO _migrations (version) VALUES (13)').run();
344
+ log('[info] Backfilled architect role_id with \'main\' (Spec 755)');
345
+ }
346
+ // Migration v14: Absorb the retired state.db tables (Issue #1118).
347
+ // Creates architect/builders/utils/annotations in global.db at their final
348
+ // shape. architect/utils/annotations move as-is; builders is RESHAPED with a
349
+ // workspace_path column + composite PK (workspace_path, id) so the same
350
+ // builder id can exist in multiple workspaces. Idempotent via
351
+ // `CREATE TABLE IF NOT EXISTS`. The one-time data migration of legacy
352
+ // state.db files is a separate, marker-gated step run at Tower boot
353
+ // (db/consolidate.ts) — NOT here — so opening global.db never moves data.
354
+ const v14 = db.prepare('SELECT version FROM _migrations WHERE version = 14').get();
355
+ if (!v14) {
356
+ db.exec(`
357
+ CREATE TABLE IF NOT EXISTS architect (
358
+ workspace_path TEXT NOT NULL,
359
+ id TEXT NOT NULL,
360
+ pid INTEGER NOT NULL,
361
+ port INTEGER NOT NULL,
362
+ cmd TEXT NOT NULL,
363
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
364
+ terminal_id TEXT,
365
+ session_id TEXT,
366
+ PRIMARY KEY (workspace_path, id)
367
+ );
368
+ CREATE INDEX IF NOT EXISTS idx_architect_workspace ON architect(workspace_path);
369
+
370
+ CREATE TABLE IF NOT EXISTS builders (
371
+ workspace_path TEXT NOT NULL,
372
+ id TEXT NOT NULL,
373
+ name TEXT NOT NULL,
374
+ port INTEGER NOT NULL DEFAULT 0,
375
+ pid INTEGER NOT NULL DEFAULT 0,
376
+ status TEXT NOT NULL DEFAULT 'spawning'
377
+ CHECK(status IN ('spawning', 'implementing', 'blocked', 'pr', 'complete')),
378
+ phase TEXT NOT NULL DEFAULT '',
379
+ worktree TEXT NOT NULL,
380
+ branch TEXT NOT NULL,
381
+ type TEXT NOT NULL DEFAULT 'spec'
382
+ CHECK(type IN ('spec', 'task', 'protocol', 'shell', 'worktree', 'bugfix', 'pir')),
383
+ task_text TEXT,
384
+ protocol_name TEXT,
385
+ issue_number TEXT,
386
+ terminal_id TEXT,
387
+ spawned_by_architect TEXT,
388
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
389
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
390
+ PRIMARY KEY (workspace_path, id)
391
+ );
392
+ CREATE INDEX IF NOT EXISTS idx_builders_status ON builders(status);
393
+ CREATE INDEX IF NOT EXISTS idx_builders_port ON builders(port);
394
+ CREATE TRIGGER IF NOT EXISTS builders_updated_at
395
+ AFTER UPDATE ON builders
396
+ FOR EACH ROW
397
+ BEGIN
398
+ UPDATE builders SET updated_at = datetime('now')
399
+ WHERE workspace_path = NEW.workspace_path AND id = NEW.id;
400
+ END;
401
+
402
+ CREATE TABLE IF NOT EXISTS utils (
403
+ id TEXT PRIMARY KEY,
404
+ name TEXT NOT NULL,
405
+ port INTEGER NOT NULL DEFAULT 0,
406
+ pid INTEGER NOT NULL DEFAULT 0,
407
+ terminal_id TEXT,
408
+ started_at TEXT NOT NULL DEFAULT (datetime('now'))
409
+ );
410
+
411
+ CREATE TABLE IF NOT EXISTS annotations (
412
+ id TEXT PRIMARY KEY,
413
+ file TEXT NOT NULL,
414
+ port INTEGER NOT NULL DEFAULT 0,
415
+ pid INTEGER NOT NULL DEFAULT 0,
416
+ parent_type TEXT NOT NULL CHECK(parent_type IN ('architect', 'builder', 'util')),
417
+ parent_id TEXT,
418
+ started_at TEXT NOT NULL DEFAULT (datetime('now'))
419
+ );
420
+ `);
421
+ db.prepare('INSERT INTO _migrations (version) VALUES (14)').run();
422
+ log('[info] Absorbed state.db tables into global.db (Issue #1118)');
423
+ }
424
+ // Migration v15: Add mailbox table (Spec 1313 — mailbox-first delivery).
425
+ // Additive new table: every `afx send` is persisted here before the send
426
+ // response returns, so nothing is lost to a Tower crash/restart/shutdown.
427
+ // Rows address AGENTS (to_agent), not PTYs, so a respawned terminal drains its
428
+ // predecessor's mail. No rows to migrate — the retired SendBuffer was in-memory.
429
+ // Idempotent via CREATE TABLE / CREATE INDEX IF NOT EXISTS (fresh installs
430
+ // already created it from GLOBAL_SCHEMA and reach the marker as a no-op).
431
+ const v15 = db.prepare('SELECT version FROM _migrations WHERE version = 15').get();
432
+ if (!v15) {
433
+ db.exec(`
434
+ CREATE TABLE IF NOT EXISTS mailbox (
435
+ id TEXT PRIMARY KEY,
436
+ workspace_path TEXT NOT NULL,
437
+ to_agent TEXT NOT NULL,
438
+ terminal_id TEXT,
439
+ from_agent TEXT,
440
+ from_workspace TEXT,
441
+ body TEXT NOT NULL,
442
+ formatted_message TEXT NOT NULL,
443
+ no_enter INTEGER NOT NULL DEFAULT 0,
444
+ status TEXT NOT NULL DEFAULT 'held'
445
+ CHECK(status IN ('held', 'delivered', 'superseded', 'dismissed')),
446
+ reason TEXT CHECK(reason IN ('busy', 'no-profile', 'no-live-pty')),
447
+ supersede_key TEXT,
448
+ escalated INTEGER NOT NULL DEFAULT 0,
449
+ created_at INTEGER NOT NULL,
450
+ updated_at INTEGER NOT NULL,
451
+ resolved_at INTEGER
452
+ );
453
+ CREATE INDEX IF NOT EXISTS idx_mailbox_workspace_status ON mailbox(workspace_path, status);
454
+ CREATE INDEX IF NOT EXISTS idx_mailbox_agent_drain ON mailbox(workspace_path, to_agent, status);
455
+ CREATE INDEX IF NOT EXISTS idx_mailbox_supersede ON mailbox(supersede_key);
456
+ `);
457
+ db.prepare('INSERT INTO _migrations (version) VALUES (15)').run();
458
+ log('[info] Created mailbox table (Spec 1313)');
459
+ }
460
+ // Migration v16: Add command column to terminal_sessions (Spec 1313).
461
+ // The render-gate resolves an agent's classifier profile from its launch
462
+ // command (PtySession.command). Shellper-backed sessions were created with
463
+ // command: '' and the profile fell back to reading `.builder-start.sh` —
464
+ // which only builder worktrees have. Architects run in the workspace root
465
+ // (no launch script), so they never resolved and every `afx send architect`
466
+ // held `no-profile`. Persisting the command lets the reconcile/reconnect
467
+ // paths restore identity after a Tower restart, so architects resolve
468
+ // directly and survive restart (builders keep the launch-script backstop).
469
+ // Mirrors the label (v11) / cwd (v12) column adds.
470
+ const v16 = db.prepare('SELECT version FROM _migrations WHERE version = 16').get();
471
+ if (!v16) {
472
+ // Only skip the ALTER when the column genuinely exists already (fresh install
473
+ // ran GLOBAL_SCHEMA). A blanket try/catch would let a REAL alter failure be
474
+ // recorded as "migrated" — and since saveTerminalSession's INSERT now names
475
+ // `command`, every future write would then fail against a table missing it.
476
+ const hasCommand = db.prepare(`PRAGMA table_info(terminal_sessions)`).all()
477
+ .some((c) => c.name === 'command');
478
+ if (!hasCommand) {
479
+ db.exec(`ALTER TABLE terminal_sessions ADD COLUMN command TEXT`);
480
+ }
481
+ db.prepare('INSERT INTO _migrations (version) VALUES (16)').run();
482
+ log('[info] Added command column to terminal_sessions (Spec 1313 restart-safe render-gate identity)');
483
+ }
484
+ // Migration v17: Add not_before column to mailbox (Spec 1313 round 3 — durable `--delay`).
485
+ // `afx send --delay` now persists its row at REQUEST time with not_before = now + delay*1000
486
+ // and defers delivery through the render gate, so a delayed send survives a Tower restart
487
+ // (the conscious reversal of Spec 1307's drop-on-restart semantics). A row is deliverable
488
+ // only when `not_before IS NULL OR not_before <= now`; null means deliver-ASAP (every
489
+ // pre-round-3 row). PRAGMA-gated ADD COLUMN mirroring v16 — a blanket try/catch would let a
490
+ // real ALTER failure be recorded as "migrated" and every subsequent mailbox insert (which
491
+ // now names not_before) would then fail against a table missing it. Do NOT edit v15 in place:
492
+ // dev machines on this branch already applied it, so the column must arrive as its own step.
493
+ const v17 = db.prepare('SELECT version FROM _migrations WHERE version = 17').get();
494
+ if (!v17) {
495
+ const hasNotBefore = db.prepare(`PRAGMA table_info(mailbox)`).all()
496
+ .some((c) => c.name === 'not_before');
497
+ if (!hasNotBefore) {
498
+ db.exec(`ALTER TABLE mailbox ADD COLUMN not_before INTEGER`);
499
+ }
500
+ db.prepare('INSERT INTO _migrations (version) VALUES (17)').run();
501
+ log('[info] Added not_before column to mailbox (Spec 1313 durable --delay)');
502
+ }
503
+ // Migration v18: Add detail column to mailbox (Issue #1482 — the gate verdict's detail).
504
+ // The render gate already distinguishes a legitimately-occupied composer (`user-text` — a
505
+ // human at the line, the safe and intended hold) from a composer it CANNOT verify
506
+ // (`no-region-end`/`no-composer-marker` — a drifted profile, a torn frame, or a dims
507
+ // divergence, which will never clear on its own). That distinction lived only in memory on
508
+ // `DeliveryOutcome.detail`, so every operator surface — `afx inbox`, the send response, the
509
+ // held/escalation logs, the owner starvation notice — showed a bare `busy` for both. The two
510
+ // cases have DIFFERENT remedies, so the row has to carry which one it is.
511
+ //
512
+ // Deliberately NO CHECK constraint, unlike the sibling `reason` column: SQLite cannot add one
513
+ // via ALTER TABLE, so a CHECK present in GLOBAL_SCHEMA and absent here would make a fresh
514
+ // install and an upgraded install structurally different — the exact convergence the
515
+ // migration suite asserts. The value set is enforced in TypeScript (`MailboxGateDetail`).
516
+ // Same PRAGMA-gated ADD COLUMN shape as v16/v17: a blanket try/catch would let a real ALTER
517
+ // failure be recorded as "migrated", and every subsequent mailbox write (which now names
518
+ // `detail`) would then fail against a table missing it.
519
+ const v18 = db.prepare('SELECT version FROM _migrations WHERE version = 18').get();
520
+ if (!v18) {
521
+ const hasDetail = db.prepare(`PRAGMA table_info(mailbox)`).all()
522
+ .some((c) => c.name === 'detail');
523
+ if (!hasDetail) {
524
+ db.exec(`ALTER TABLE mailbox ADD COLUMN detail TEXT`);
525
+ }
526
+ db.prepare('INSERT INTO _migrations (version) VALUES (18)').run();
527
+ log('[info] Added detail column to mailbox (Issue #1482 gate-verdict detail)');
528
+ }
529
+ }
530
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/agent-farm/db/migrations.ts"],"names":[],"mappings":"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"}
@@ -19,5 +19,5 @@ export declare const LOCAL_SCHEMA = "\n-- Schema versioning\nCREATE TABLE IF NOT
19
19
  * Global registry schema (global.db)
20
20
  * Stores terminal sessions and migrations across all workspaces
21
21
  */
22
- export declare const GLOBAL_SCHEMA = "\n-- Schema versioning\nCREATE TABLE IF NOT EXISTS _migrations (\n version INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Terminal sessions (Spec 0090 TICK-001)\n-- Tracks all terminal sessions across all workspaces for persistence and reconciliation\nCREATE TABLE IF NOT EXISTS terminal_sessions (\n id TEXT PRIMARY KEY, -- terminal UUID from PtyManager\n workspace_path TEXT NOT NULL, -- workspace this terminal belongs to\n type TEXT NOT NULL -- 'architect', 'builder', 'shell'\n CHECK(type IN ('architect', 'builder', 'shell')),\n role_id TEXT, -- builder ID or shell ID (null for architect)\n pid INTEGER, -- process ID of the terminal\n shellper_socket TEXT, -- Unix socket path for shellper process\n shellper_pid INTEGER, -- shellper process PID\n shellper_start_time INTEGER, -- shellper process start time (epoch ms)\n label TEXT, -- custom display label (Spec 468)\n cwd TEXT, -- working directory of the terminal (Bugfix #506)\n command TEXT, -- launch command; render-gate identity seam (Spec 1313)\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_terminal_sessions_workspace ON terminal_sessions(workspace_path);\nCREATE INDEX IF NOT EXISTS idx_terminal_sessions_type ON terminal_sessions(type);\n\n-- File tabs (Spec 0099 Phase 4)\nCREATE TABLE IF NOT EXISTS file_tabs (\n id TEXT PRIMARY KEY,\n workspace_path TEXT NOT NULL,\n file_path TEXT NOT NULL,\n created_at INTEGER NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_file_tabs_workspace ON file_tabs(workspace_path);\n\n-- Known workspaces (persistent workspace registry)\nCREATE TABLE IF NOT EXISTS known_workspaces (\n workspace_path TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n last_launched_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Cron tasks (Spec 399)\n-- Tracks scheduled task state across all workspaces\nCREATE TABLE IF NOT EXISTS cron_tasks (\n id TEXT PRIMARY KEY,\n workspace_path TEXT NOT NULL,\n task_name TEXT NOT NULL,\n last_run INTEGER,\n last_result TEXT,\n last_output TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n UNIQUE(workspace_path, task_name)\n);\n\n-- ===========================================================================\n-- Issue #1118: tables absorbed from the retired per-workspace state.db.\n-- architect/utils/annotations move as-is; builders is RESHAPED to be\n-- workspace-scoped (composite PK), mirroring architect (Bugfix #826) \u2014 builder\n-- ids are <protocol>-<issueNumber>, unique within a workspace but reused across\n-- repos, so a single shared table must disambiguate by workspace_path.\n-- ===========================================================================\n\n-- Architect sessions (Spec 755 multi-architect; Bugfix #826 workspace-scoped;\n-- Issue #832 session_id). id is the architect's name ('main', siblings).\nCREATE TABLE IF NOT EXISTS architect (\n workspace_path TEXT NOT NULL,\n id TEXT NOT NULL,\n pid INTEGER NOT NULL,\n port INTEGER NOT NULL,\n cmd TEXT NOT NULL,\n started_at TEXT NOT NULL DEFAULT (datetime('now')),\n terminal_id TEXT,\n session_id TEXT,\n PRIMARY KEY (workspace_path, id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_architect_workspace ON architect(workspace_path);\n\n-- Builder sessions. Issue #1118: workspace_path + composite PK so the same\n-- builder id can exist in multiple workspaces without collision.\nCREATE TABLE IF NOT EXISTS builders (\n workspace_path TEXT NOT NULL,\n id TEXT NOT NULL,\n name TEXT NOT NULL,\n port INTEGER NOT NULL DEFAULT 0,\n pid INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'spawning'\n CHECK(status IN ('spawning', 'implementing', 'blocked', 'pr', 'complete')),\n phase TEXT NOT NULL DEFAULT '',\n worktree TEXT NOT NULL,\n branch TEXT NOT NULL,\n type TEXT NOT NULL DEFAULT 'spec'\n CHECK(type IN ('spec', 'task', 'protocol', 'shell', 'worktree', 'bugfix', 'pir')),\n task_text TEXT,\n protocol_name TEXT,\n issue_number TEXT,\n terminal_id TEXT,\n spawned_by_architect TEXT,\n started_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (workspace_path, id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_builders_status ON builders(status);\nCREATE INDEX IF NOT EXISTS idx_builders_port ON builders(port);\n\nCREATE TRIGGER IF NOT EXISTS builders_updated_at\n AFTER UPDATE ON builders\n FOR EACH ROW\n BEGIN\n UPDATE builders SET updated_at = datetime('now')\n WHERE workspace_path = NEW.workspace_path AND id = NEW.id;\n END;\n\n-- Utility terminals (UUID-keyed; moved as-is).\nCREATE TABLE IF NOT EXISTS utils (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n port INTEGER NOT NULL DEFAULT 0,\n pid INTEGER NOT NULL DEFAULT 0,\n terminal_id TEXT,\n started_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Annotations / file viewers (UUID-keyed; moved as-is).\nCREATE TABLE IF NOT EXISTS annotations (\n id TEXT PRIMARY KEY,\n file TEXT NOT NULL,\n port INTEGER NOT NULL DEFAULT 0,\n pid INTEGER NOT NULL DEFAULT 0,\n parent_type TEXT NOT NULL CHECK(parent_type IN ('architect', 'builder', 'util')),\n parent_id TEXT,\n started_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Mailbox (Spec 1313): durable home for every 'afx send'.\n-- Persist-first delivery \u2014 a row is written before the send response returns, so\n-- nothing is lost to a Tower crash/restart/shutdown (the retired in-memory\n-- SendBuffer lost held messages on both). Rows address AGENTS (to_agent within\n-- workspace_path), not PTYs, so a respawned terminal drains its predecessor's\n-- mail. Delivery is authorized elsewhere by the render-gate (Phases 2/4); this\n-- table is pure durable state. Timestamps are epoch-ms integers (not SQLite\n-- datetime) so ordering and age math are trivial. Additive new table \u2014 fresh\n-- installs get it here; existing installs get it from migration v15.\nCREATE TABLE IF NOT EXISTS mailbox (\n id TEXT PRIMARY KEY, -- uuid\n workspace_path TEXT NOT NULL, -- addressing scope\n to_agent TEXT NOT NULL, -- recipient agent identity (drains across respawn)\n terminal_id TEXT, -- last-known PTY hint (nullable; not the identity)\n from_agent TEXT,\n from_workspace TEXT,\n body TEXT NOT NULL, -- raw message (never logged)\n formatted_message TEXT NOT NULL, -- what gets written to the PTY\n no_enter INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'held'\n CHECK(status IN ('held', 'delivered', 'superseded', 'dismissed')),\n reason TEXT CHECK(reason IN ('busy', 'no-profile', 'no-live-pty')), -- why-held; null once delivered\n supersede_key TEXT, -- cron-only; null for direct sends\n escalated INTEGER NOT NULL DEFAULT 0, -- set once escalation age crossed (visibility only)\n not_before INTEGER, -- epoch ms; delayed-send due time (Spec 1313 round 3, --delay). null = deliver ASAP; a row is deliverable only when not_before IS NULL OR not_before <= now\n created_at INTEGER NOT NULL, -- epoch ms (enqueue order per agent)\n updated_at INTEGER NOT NULL,\n resolved_at INTEGER -- delivered/superseded/dismissed timestamp\n);\n\nCREATE INDEX IF NOT EXISTS idx_mailbox_workspace_status ON mailbox(workspace_path, status);\nCREATE INDEX IF NOT EXISTS idx_mailbox_agent_drain ON mailbox(workspace_path, to_agent, status);\nCREATE INDEX IF NOT EXISTS idx_mailbox_supersede ON mailbox(supersede_key);\n";
22
+ export declare const GLOBAL_SCHEMA = "\n-- Schema versioning\nCREATE TABLE IF NOT EXISTS _migrations (\n version INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Terminal sessions (Spec 0090 TICK-001)\n-- Tracks all terminal sessions across all workspaces for persistence and reconciliation\nCREATE TABLE IF NOT EXISTS terminal_sessions (\n id TEXT PRIMARY KEY, -- terminal UUID from PtyManager\n workspace_path TEXT NOT NULL, -- workspace this terminal belongs to\n type TEXT NOT NULL -- 'architect', 'builder', 'shell'\n CHECK(type IN ('architect', 'builder', 'shell')),\n role_id TEXT, -- builder ID or shell ID (null for architect)\n pid INTEGER, -- process ID of the terminal\n shellper_socket TEXT, -- Unix socket path for shellper process\n shellper_pid INTEGER, -- shellper process PID\n shellper_start_time INTEGER, -- shellper process start time (epoch ms)\n label TEXT, -- custom display label (Spec 468)\n cwd TEXT, -- working directory of the terminal (Bugfix #506)\n command TEXT, -- launch command; render-gate identity seam (Spec 1313)\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_terminal_sessions_workspace ON terminal_sessions(workspace_path);\nCREATE INDEX IF NOT EXISTS idx_terminal_sessions_type ON terminal_sessions(type);\n\n-- File tabs (Spec 0099 Phase 4)\nCREATE TABLE IF NOT EXISTS file_tabs (\n id TEXT PRIMARY KEY,\n workspace_path TEXT NOT NULL,\n file_path TEXT NOT NULL,\n created_at INTEGER NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_file_tabs_workspace ON file_tabs(workspace_path);\n\n-- Known workspaces (persistent workspace registry)\nCREATE TABLE IF NOT EXISTS known_workspaces (\n workspace_path TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n last_launched_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Cron tasks (Spec 399)\n-- Tracks scheduled task state across all workspaces\nCREATE TABLE IF NOT EXISTS cron_tasks (\n id TEXT PRIMARY KEY,\n workspace_path TEXT NOT NULL,\n task_name TEXT NOT NULL,\n last_run INTEGER,\n last_result TEXT,\n last_output TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n UNIQUE(workspace_path, task_name)\n);\n\n-- ===========================================================================\n-- Issue #1118: tables absorbed from the retired per-workspace state.db.\n-- architect/utils/annotations move as-is; builders is RESHAPED to be\n-- workspace-scoped (composite PK), mirroring architect (Bugfix #826) \u2014 builder\n-- ids are <protocol>-<issueNumber>, unique within a workspace but reused across\n-- repos, so a single shared table must disambiguate by workspace_path.\n-- ===========================================================================\n\n-- Architect sessions (Spec 755 multi-architect; Bugfix #826 workspace-scoped;\n-- Issue #832 session_id). id is the architect's name ('main', siblings).\nCREATE TABLE IF NOT EXISTS architect (\n workspace_path TEXT NOT NULL,\n id TEXT NOT NULL,\n pid INTEGER NOT NULL,\n port INTEGER NOT NULL,\n cmd TEXT NOT NULL,\n started_at TEXT NOT NULL DEFAULT (datetime('now')),\n terminal_id TEXT,\n session_id TEXT,\n PRIMARY KEY (workspace_path, id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_architect_workspace ON architect(workspace_path);\n\n-- Builder sessions. Issue #1118: workspace_path + composite PK so the same\n-- builder id can exist in multiple workspaces without collision.\nCREATE TABLE IF NOT EXISTS builders (\n workspace_path TEXT NOT NULL,\n id TEXT NOT NULL,\n name TEXT NOT NULL,\n port INTEGER NOT NULL DEFAULT 0,\n pid INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'spawning'\n CHECK(status IN ('spawning', 'implementing', 'blocked', 'pr', 'complete')),\n phase TEXT NOT NULL DEFAULT '',\n worktree TEXT NOT NULL,\n branch TEXT NOT NULL,\n type TEXT NOT NULL DEFAULT 'spec'\n CHECK(type IN ('spec', 'task', 'protocol', 'shell', 'worktree', 'bugfix', 'pir')),\n task_text TEXT,\n protocol_name TEXT,\n issue_number TEXT,\n terminal_id TEXT,\n spawned_by_architect TEXT,\n started_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (workspace_path, id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_builders_status ON builders(status);\nCREATE INDEX IF NOT EXISTS idx_builders_port ON builders(port);\n\nCREATE TRIGGER IF NOT EXISTS builders_updated_at\n AFTER UPDATE ON builders\n FOR EACH ROW\n BEGIN\n UPDATE builders SET updated_at = datetime('now')\n WHERE workspace_path = NEW.workspace_path AND id = NEW.id;\n END;\n\n-- Utility terminals (UUID-keyed; moved as-is).\nCREATE TABLE IF NOT EXISTS utils (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n port INTEGER NOT NULL DEFAULT 0,\n pid INTEGER NOT NULL DEFAULT 0,\n terminal_id TEXT,\n started_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Annotations / file viewers (UUID-keyed; moved as-is).\nCREATE TABLE IF NOT EXISTS annotations (\n id TEXT PRIMARY KEY,\n file TEXT NOT NULL,\n port INTEGER NOT NULL DEFAULT 0,\n pid INTEGER NOT NULL DEFAULT 0,\n parent_type TEXT NOT NULL CHECK(parent_type IN ('architect', 'builder', 'util')),\n parent_id TEXT,\n started_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Mailbox (Spec 1313): durable home for every 'afx send'.\n-- Persist-first delivery \u2014 a row is written before the send response returns, so\n-- nothing is lost to a Tower crash/restart/shutdown (the retired in-memory\n-- SendBuffer lost held messages on both). Rows address AGENTS (to_agent within\n-- workspace_path), not PTYs, so a respawned terminal drains its predecessor's\n-- mail. Delivery is authorized elsewhere by the render-gate (Phases 2/4); this\n-- table is pure durable state. Timestamps are epoch-ms integers (not SQLite\n-- datetime) so ordering and age math are trivial. Additive new table \u2014 fresh\n-- installs get it here; existing installs get it from migration v15.\nCREATE TABLE IF NOT EXISTS mailbox (\n id TEXT PRIMARY KEY, -- uuid\n workspace_path TEXT NOT NULL, -- addressing scope\n to_agent TEXT NOT NULL, -- recipient agent identity (drains across respawn)\n terminal_id TEXT, -- last-known PTY hint (nullable; not the identity)\n from_agent TEXT,\n from_workspace TEXT,\n body TEXT NOT NULL, -- raw message (never logged)\n formatted_message TEXT NOT NULL, -- what gets written to the PTY\n no_enter INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'held'\n CHECK(status IN ('held', 'delivered', 'superseded', 'dismissed')),\n reason TEXT CHECK(reason IN ('busy', 'no-profile', 'no-live-pty')), -- why-held; null once delivered\n detail TEXT, -- Issue #1482: the gate verdict's detail behind a busy hold ('user-text' = a human at the line; 'no-region-end'/'no-composer-marker' = the classifier could not verify). Null for non-gate holds and once delivered. NO CHECK on purpose -- SQLite cannot ALTER one in, so migration v18 could not match it and a fresh install would diverge from an upgraded one; the value set is enforced in TypeScript (MailboxGateDetail)\n supersede_key TEXT, -- cron-only; null for direct sends\n escalated INTEGER NOT NULL DEFAULT 0, -- set once escalation age crossed (visibility only)\n not_before INTEGER, -- epoch ms; delayed-send due time (Spec 1313 round 3, --delay). null = deliver ASAP; a row is deliverable only when not_before IS NULL OR not_before <= now\n created_at INTEGER NOT NULL, -- epoch ms (enqueue order per agent)\n updated_at INTEGER NOT NULL,\n resolved_at INTEGER -- delivered/superseded/dismissed timestamp\n);\n\nCREATE INDEX IF NOT EXISTS idx_mailbox_workspace_status ON mailbox(workspace_path, status);\nCREATE INDEX IF NOT EXISTS idx_mailbox_agent_drain ON mailbox(workspace_path, to_agent, status);\nCREATE INDEX IF NOT EXISTS idx_mailbox_supersede ON mailbox(supersede_key);\n";
23
23
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/db/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,0gGAqFxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,4nPA4KzB,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/db/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,0gGAqFxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,wkQA6KzB,CAAC"}
@@ -265,6 +265,7 @@ CREATE TABLE IF NOT EXISTS mailbox (
265
265
  status TEXT NOT NULL DEFAULT 'held'
266
266
  CHECK(status IN ('held', 'delivered', 'superseded', 'dismissed')),
267
267
  reason TEXT CHECK(reason IN ('busy', 'no-profile', 'no-live-pty')), -- why-held; null once delivered
268
+ detail TEXT, -- Issue #1482: the gate verdict's detail behind a busy hold ('user-text' = a human at the line; 'no-region-end'/'no-composer-marker' = the classifier could not verify). Null for non-gate holds and once delivered. NO CHECK on purpose -- SQLite cannot ALTER one in, so migration v18 could not match it and a fresh install would diverge from an upgraded one; the value set is enforced in TypeScript (MailboxGateDetail)
268
269
  supersede_key TEXT, -- cron-only; null for direct sends
269
270
  escalated INTEGER NOT NULL DEFAULT 0, -- set once escalation age crossed (visibility only)
270
271
  not_before INTEGER, -- epoch ms; delayed-send due time (Spec 1313 round 3, --delay). null = deliver ASAP; a row is deliverable only when not_before IS NULL OR not_before <= now
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/agent-farm/db/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqF3B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4K5B,CAAC"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/agent-farm/db/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqF3B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6K5B,CAAC"}
@@ -84,6 +84,27 @@ export type MailboxStatus = 'held' | 'delivered' | 'superseded' | 'dismissed';
84
84
  * - `no-live-pty` — the recipient agent has no live terminal right now
85
85
  */
86
86
  export type MailboxReason = 'busy' | 'no-profile' | 'no-live-pty';
87
+ /**
88
+ * The render gate's classification detail behind a `busy` hold (Issue #1482). Persisted
89
+ * beside {@link MailboxReason} so every operator surface can tell the two apart:
90
+ * - `user-text` — a draft or menu occupies the composer. A human is at the line;
91
+ * this is the SAFE, intended hold and it clears when they finish.
92
+ * - `no-region-end` — a composer marker with no rule/status line beneath it to bound
93
+ * the region (a partial/mid-repaint frame, or a mirror rendered at
94
+ * dims the real TUI never adopted).
95
+ * - `no-composer-marker` — no recognized marker at all (a wrapper/boot screen, a drifted
96
+ * profile, or an unrenderable frame).
97
+ * The latter two are the DEFECT class: the classifier could not verify anything, so the mail
98
+ * will not deliver on its own. `GateVerdict.detail`'s fourth value, `empty`, is never persisted
99
+ * — a clean verdict delivers the row (and delivery nulls both columns).
100
+ *
101
+ * Null for every non-gate hold (`no-live-pty`, `no-profile`) and for the post-classify
102
+ * token/settle re-holds, so a stale detail can never outlive the verdict that produced it.
103
+ *
104
+ * The DB column carries NO CHECK constraint (see `GLOBAL_SCHEMA` / migration v18); this type
105
+ * is the enforcement.
106
+ */
107
+ export type MailboxGateDetail = 'user-text' | 'no-region-end' | 'no-composer-marker';
87
108
  /**
88
109
  * Database row type for the mailbox table (Spec 1313).
89
110
  *
@@ -105,6 +126,7 @@ export interface DbMailbox {
105
126
  no_enter: number;
106
127
  status: MailboxStatus;
107
128
  reason: MailboxReason | null;
129
+ detail: MailboxGateDetail | null;
108
130
  supersede_key: string | null;
109
131
  escalated: number;
110
132
  not_before: number | null;