@north-light/cli 0.3.532 → 0.3.533

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 (193) hide show
  1. package/dist/api/client.d.ts +6 -3
  2. package/dist/api/client.js +1 -1
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/canvas.d.ts +2 -0
  6. package/dist/api/dto/crons.d.ts +2 -0
  7. package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
  8. package/dist/builtin-memory/northlight-apps/providers/README.md +2 -0
  9. package/dist/builtin-memory/northlight-apps/providers/sms.md +2 -0
  10. package/dist/clients/attach/chrome/inbox-strip.d.ts +6 -1
  11. package/dist/clients/attach/chrome/inbox-strip.js +1 -1
  12. package/dist/clients/attach/chrome/roster.js +1 -1
  13. package/dist/clients/attach/session/chrome-refresh.d.ts +12 -3
  14. package/dist/clients/attach/session/chrome-refresh.js +1 -1
  15. package/dist/clients/attach/session/document-links.js +1 -1
  16. package/dist/clients/attach/viewer.js +586 -587
  17. package/dist/commands/api-client.js +3 -3
  18. package/dist/commands/node/inspect.js +6 -6
  19. package/dist/commands/sys/context/expose.js +2 -2
  20. package/dist/commands/sys/db.d.ts +1 -0
  21. package/dist/commands/sys/db.js +1 -0
  22. package/dist/commands/sys.js +1 -1
  23. package/dist/core/bash-guard.js +9 -3
  24. package/dist/core/canvas/canvas.d.ts +12 -2
  25. package/dist/core/canvas/canvas.js +23 -20
  26. package/dist/core/canvas/daemon-actions.d.ts +0 -3
  27. package/dist/core/canvas/daemon-actions.js +7 -24
  28. package/dist/core/canvas/daemon-owner.d.ts +3 -0
  29. package/dist/core/canvas/daemon-owner.js +1 -6
  30. package/dist/core/canvas/db.d.ts +28 -20
  31. package/dist/core/canvas/db.js +4 -3
  32. package/dist/core/canvas/deliveries.d.ts +5 -0
  33. package/dist/core/canvas/deliveries.js +25 -30
  34. package/dist/core/canvas/export-sqlite.d.ts +14 -0
  35. package/dist/core/canvas/export-sqlite.js +3 -0
  36. package/dist/core/canvas/import-sqlite.d.ts +44 -0
  37. package/dist/core/canvas/import-sqlite.js +7 -0
  38. package/dist/core/canvas/node-tokens.js +3 -3
  39. package/dist/core/canvas/outcome-actions.js +4 -4
  40. package/dist/core/canvas/recovery.js +1 -1
  41. package/dist/core/canvas/schema-pg.sql +1022 -0
  42. package/dist/core/canvas/schema-version.d.ts +4 -0
  43. package/dist/core/canvas/schema-version.js +1 -0
  44. package/dist/core/canvas/sessions.js +3 -3
  45. package/dist/core/canvas/structured-requests.js +2 -2
  46. package/dist/core/canvas/tree.js +6 -6
  47. package/dist/core/canvas/waits.js +1 -1
  48. package/dist/core/feed/inbox.js +1 -1
  49. package/dist/core/feed/messages.js +5 -5
  50. package/dist/core/feed/reports.js +2 -2
  51. package/dist/core/grants/mirror.js +2 -2
  52. package/dist/core/graph/access.js +7 -7
  53. package/dist/core/graph/custom.js +10 -10
  54. package/dist/core/graph/deletion.js +6 -6
  55. package/dist/core/graph/documents.d.ts +1 -1
  56. package/dist/core/graph/documents.js +9 -9
  57. package/dist/core/graph/edges.d.ts +3 -3
  58. package/dist/core/graph/edges.js +3 -1
  59. package/dist/core/graph/events.js +7 -7
  60. package/dist/core/graph/exposures.d.ts +8 -8
  61. package/dist/core/graph/jobs.d.ts +1 -1
  62. package/dist/core/graph/jobs.js +2 -2
  63. package/dist/core/graph/kinds/custom.js +1 -1
  64. package/dist/core/graph/kinds/document.js +4 -4
  65. package/dist/core/graph/kinds/dto.js +1 -1
  66. package/dist/core/graph/kinds/job.js +2 -2
  67. package/dist/core/graph/kinds/kind.d.ts +2 -2
  68. package/dist/core/graph/kinds/kind.js +1 -1
  69. package/dist/core/graph/kinds/live-read.d.ts +5 -0
  70. package/dist/core/graph/kinds/live-read.js +1 -0
  71. package/dist/core/graph/kinds/node.js +5 -5
  72. package/dist/core/graph/kinds/owner.js +2 -2
  73. package/dist/core/graph/names.js +3 -3
  74. package/dist/core/graph/objects.d.ts +5 -5
  75. package/dist/core/graph/objects.js +10 -10
  76. package/dist/core/graph/package-docs.js +6 -5
  77. package/dist/core/graph/reader.d.ts +1 -1
  78. package/dist/core/graph/reader.js +2 -2
  79. package/dist/core/graph/receivers.js +4 -5
  80. package/dist/core/graph/repo-sync/exchange.js +2 -2
  81. package/dist/core/graph/repo-sync/repos.js +3 -3
  82. package/dist/core/graph/repo-sync/sync.js +1 -1
  83. package/dist/core/graph/rules.d.ts +3 -4
  84. package/dist/core/graph/rules.js +3 -3
  85. package/dist/core/graph/spaces/app.js +1 -1
  86. package/dist/core/graph/spaces/index.d.ts +1 -1
  87. package/dist/core/graph/spaces/index.js +1 -1
  88. package/dist/core/graph/spaces/own-memory.js +1 -1
  89. package/dist/core/graph/spaces/person.js +3 -3
  90. package/dist/core/graph/spaces/repo.d.ts +1 -1
  91. package/dist/core/graph/spaces/repo.js +1 -1
  92. package/dist/core/graph/types.d.ts +4 -3
  93. package/dist/core/graph/watches.d.ts +2 -2
  94. package/dist/core/graph/watches.js +17 -17
  95. package/dist/core/human/page-schema.js +5 -4
  96. package/dist/core/human/requests.d.ts +1 -1
  97. package/dist/core/human/requests.js +20 -23
  98. package/dist/core/peer/ingest.js +1 -1
  99. package/dist/core/peer/receiver.js +1 -1
  100. package/dist/core/peer/stubs.js +1 -1
  101. package/dist/core/providers/receipts.js +3 -2
  102. package/dist/core/review/comments.js +5 -4
  103. package/dist/core/runs/activity.js +4 -5
  104. package/dist/core/runs/confinement.d.ts +1 -1
  105. package/dist/core/runs/confinement.js +2 -2
  106. package/dist/core/runs/events.d.ts +1 -1
  107. package/dist/core/runs/events.js +5 -5
  108. package/dist/core/runs/operations.js +6 -4
  109. package/dist/core/runtime/close.d.ts +3 -0
  110. package/dist/core/runtime/close.js +1 -1
  111. package/dist/core/runtime/host.d.ts +5 -1
  112. package/dist/core/runtime/host.js +3 -3
  113. package/dist/core/runtime/launch.js +2 -2
  114. package/dist/core/runtime/ledger.js +3 -3
  115. package/dist/core/runtime/lifecycle.d.ts +9 -0
  116. package/dist/core/runtime/lifecycle.js +2 -2
  117. package/dist/core/runtime/nodes.d.ts +1 -1
  118. package/dist/core/runtime/nodes.js +1 -1
  119. package/dist/core/runtime/roadmap.js +1 -1
  120. package/dist/core/spaces/stop.d.ts +1 -0
  121. package/dist/core/spaces/stop.js +2 -2
  122. package/dist/core/statements.d.ts +17 -0
  123. package/dist/core/statements.js +2 -0
  124. package/dist/core/substrate/delivery/corpus.js +3 -3
  125. package/dist/core/substrate/delivery/deliver.js +5 -5
  126. package/dist/core/substrate/delivery/listings.js +1 -1
  127. package/dist/core/substrate/delivery/match.d.ts +2 -2
  128. package/dist/core/substrate/delivery/match.js +1 -1
  129. package/dist/core/substrate/delivery/plan.js +3 -3
  130. package/dist/core/substrate/delivery/render-boot.js +5 -5
  131. package/dist/core/substrate/delivery/render-event.js +6 -6
  132. package/dist/core/usage/ledger.js +8 -8
  133. package/dist/daemon/api/bridge.js +4 -4
  134. package/dist/daemon/api/document-listener.js +1 -1
  135. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  136. package/dist/daemon/api/handlers/canvas.js +1 -1
  137. package/dist/daemon/api/handlers/crons.js +1 -1
  138. package/dist/daemon/api/handlers/custom-objects.js +3 -3
  139. package/dist/daemon/api/handlers/docs.js +1 -1
  140. package/dist/daemon/api/handlers/inbox.js +1 -1
  141. package/dist/daemon/api/handlers/memory.js +7 -7
  142. package/dist/daemon/api/handlers/messages.js +1 -1
  143. package/dist/daemon/api/handlers/nodes.js +1 -1
  144. package/dist/daemon/api/handlers/objects.js +3 -3
  145. package/dist/daemon/api/handlers/reviews.js +1 -1
  146. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  147. package/dist/daemon/api/reader.js +1 -1
  148. package/dist/daemon/broker-signals/bus.d.ts +14 -6
  149. package/dist/daemon/broker-signals/bus.js +1 -10
  150. package/dist/daemon/crtrd-cli.js +1 -1
  151. package/dist/daemon/crtrd.js +12 -11
  152. package/dist/daemon/due.js +1 -1
  153. package/dist/daemon/export-sqlite-run.d.ts +1 -0
  154. package/dist/daemon/export-sqlite-run.js +4 -0
  155. package/dist/daemon/fleet.d.ts +1 -0
  156. package/dist/daemon/fleet.js +3 -3
  157. package/dist/daemon/human/settle.js +1 -1
  158. package/dist/daemon/manage.d.ts +10 -0
  159. package/dist/daemon/manage.js +3 -2
  160. package/dist/daemon/postgres/binary.d.ts +17 -0
  161. package/dist/daemon/postgres/binary.js +2 -0
  162. package/dist/daemon/postgres/cluster.d.ts +37 -0
  163. package/dist/daemon/postgres/cluster.js +6 -0
  164. package/dist/daemon/postgres/index.d.ts +2 -0
  165. package/dist/daemon/postgres/index.js +1 -0
  166. package/dist/daemon/postgres/pins.d.ts +7 -0
  167. package/dist/daemon/postgres/pins.js +1 -0
  168. package/dist/daemon/postgres/supervisor.d.ts +95 -0
  169. package/dist/daemon/postgres/supervisor.js +2 -0
  170. package/dist/daemon/reconcilers/bash-deadline.js +2 -2
  171. package/dist/daemon/reconcilers/delivery-lane.js +2 -2
  172. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +7 -3
  173. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  174. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  175. package/dist/daemon/reconcilers/storage-maintenance.d.ts +0 -6
  176. package/dist/daemon/reconcilers/storage-maintenance.js +1 -8
  177. package/dist/daemon/runs/observer.js +5 -4
  178. package/dist/migrations/004-canvas-documents/index.js +1 -1
  179. package/dist/pi-extensions/canvas-doc-substrate.d.ts +2 -0
  180. package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
  181. package/dist/pi-extensions/envelope-merge.d.ts +3 -2
  182. package/dist/pi-extensions/envelope-merge.js +9 -8
  183. package/dist/shared/env.d.ts +3 -0
  184. package/dist/shared/env.js +2 -2
  185. package/docs/apps/providers/README.md +2 -0
  186. package/docs/apps/providers/sms.md +2 -0
  187. package/docs/public-api.md +11 -2
  188. package/docs/sdk/release-notes.md +8 -0
  189. package/package.json +6 -6
  190. package/packages/crouter-identity/package.json +1 -1
  191. package/runtime.lock.json +13 -26
  192. package/scripts/install-runtime.mjs +53 -3
  193. package/scripts/lib/isolation-env.mjs +3 -1
@@ -0,0 +1,1022 @@
1
+ -- Canvas schema for Postgres 18. Equivalent to the SQLite canvas.db at user_version 98 with the
2
+ -- changes in the Postgres-swap design (Data model): text-id foreign keys (objects.id), `seq`
3
+ -- identity columns for insertion order, jsonb where JSON functions run, bytea blobs, PL/pgSQL
4
+ -- triggers, and pg_notify triggers for the broker-signal bus.
5
+ --
6
+ -- Applied once, in one transaction, by db.ts when canvas_meta has no `schema_version` row. Every
7
+ -- later change is a numbered delta file, never an edit of this file's tables once released.
8
+ -- Types: SQLite INTEGER (64-bit) is bigint everywhere, including 0/1 flags and epoch-ms columns.
9
+
10
+ -- ---------------------------------------------------------------- objects and their satellites
11
+
12
+ CREATE TABLE objects (
13
+ id text PRIMARY KEY,
14
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
15
+ type text NOT NULL,
16
+ kind text,
17
+ owner text REFERENCES objects(id),
18
+ privacy text NOT NULL DEFAULT 'public' CHECK (privacy IN ('public','friends','private')),
19
+ name text NOT NULL,
20
+ preview text NOT NULL DEFAULT '',
21
+ created_at text NOT NULL,
22
+ deleted_at text,
23
+ reception bigint NOT NULL DEFAULT 0,
24
+ listen_json text,
25
+ home_machine text
26
+ );
27
+ CREATE INDEX objects_owner_type ON objects(owner, type, kind);
28
+ CREATE INDEX objects_type_kind ON objects(type, kind);
29
+ CREATE INDEX objects_receiving ON objects(seq) WHERE reception = 2;
30
+ CREATE UNIQUE INDEX objects_named ON objects(owner, name)
31
+ WHERE deleted_at IS NULL AND home_machine IS NULL AND (type = 'document' OR strpos(type, '.') > 0);
32
+ CREATE UNIQUE INDEX objects_handle ON objects(type, name)
33
+ WHERE home_machine IS NULL AND type IN ('person','app','repo','profile');
34
+
35
+ CREATE TABLE documents (
36
+ object text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
37
+ summary text,
38
+ heads_key text NOT NULL,
39
+ revision bigint NOT NULL,
40
+ why_it_exists text,
41
+ unlisted bigint NOT NULL DEFAULT 0,
42
+ lint_ignore_json text,
43
+ extensions_json text,
44
+ source_path text
45
+ );
46
+
47
+ CREATE TABLE revisions (
48
+ rev_id text PRIMARY KEY,
49
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
50
+ document text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
51
+ number bigint NOT NULL,
52
+ body_hash text NOT NULL,
53
+ author text REFERENCES objects(id),
54
+ rationale text,
55
+ verbatim bigint NOT NULL DEFAULT 0,
56
+ created_at text NOT NULL
57
+ );
58
+ CREATE INDEX revisions_doc ON revisions(document, number);
59
+
60
+ CREATE TABLE revision_parents (
61
+ child text NOT NULL REFERENCES revisions(rev_id) ON DELETE CASCADE,
62
+ parent text NOT NULL REFERENCES revisions(rev_id) ON DELETE CASCADE,
63
+ PRIMARY KEY (child, parent)
64
+ );
65
+ CREATE INDEX revision_parents_parent ON revision_parents(parent);
66
+
67
+ CREATE TABLE jobs (
68
+ object text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
69
+ command text NOT NULL,
70
+ status text NOT NULL CHECK (status IN ('running','exited','killed')),
71
+ exit_code bigint,
72
+ job_dir text NOT NULL,
73
+ log_path text NOT NULL
74
+ );
75
+
76
+ CREATE TABLE identities (
77
+ object text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
78
+ scheme text NOT NULL,
79
+ external_id text NOT NULL,
80
+ UNIQUE (scheme, external_id)
81
+ );
82
+
83
+ CREATE TABLE custom_objects (
84
+ object text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
85
+ receiver_json text,
86
+ payload_json text,
87
+ description text,
88
+ summary text,
89
+ state_json text,
90
+ published_at text,
91
+ token_hash text,
92
+ dropped bigint NOT NULL DEFAULT 0,
93
+ registered_by text
94
+ );
95
+
96
+ CREATE TABLE context_sessions (
97
+ node text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
98
+ preference_snapshot text,
99
+ listed_json text NOT NULL DEFAULT '{}'
100
+ );
101
+
102
+ CREATE TABLE peer_stubs (
103
+ object text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
104
+ home_machine text NOT NULL,
105
+ envelope_json jsonb,
106
+ created_at text NOT NULL
107
+ );
108
+
109
+ CREATE TABLE object_kinds (
110
+ name text PRIMARY KEY,
111
+ owner text NOT NULL REFERENCES objects(id),
112
+ description text NOT NULL DEFAULT '',
113
+ events_json text NOT NULL,
114
+ created_at text NOT NULL,
115
+ updated_at text NOT NULL,
116
+ read_json text,
117
+ source text NOT NULL DEFAULT 'register' CHECK (source IN ('register','manifest','peer')),
118
+ declared bigint NOT NULL DEFAULT 1
119
+ );
120
+
121
+ CREATE TABLE watches (
122
+ watch_id text PRIMARY KEY,
123
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
124
+ watcher text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
125
+ object text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
126
+ in_tree bigint NOT NULL DEFAULT 0,
127
+ read_heads_key text,
128
+ created_at text NOT NULL,
129
+ link_app text REFERENCES objects(id) ON DELETE CASCADE,
130
+ UNIQUE (watcher, object)
131
+ );
132
+ CREATE INDEX watches_object ON watches(object, watcher);
133
+ CREATE INDEX watches_tree_down ON watches(watcher, object) WHERE in_tree = 1;
134
+ CREATE INDEX watches_tree_up ON watches(object, watcher) WHERE in_tree = 1;
135
+
136
+ CREATE TABLE watch_subscriptions (
137
+ subscription_id text PRIMARY KEY,
138
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
139
+ watch text NOT NULL REFERENCES watches(watch_id) ON DELETE CASCADE,
140
+ pattern text NOT NULL,
141
+ filter_json text,
142
+ config_json text,
143
+ started_by text NOT NULL CHECK (started_by IN ('spawn','job-start','ask','read','write','act','message','deliver','fork')),
144
+ ends_on text,
145
+ created_at text NOT NULL
146
+ );
147
+ CREATE INDEX watch_subscriptions_watch ON watch_subscriptions(watch);
148
+
149
+ CREATE TABLE exposures (
150
+ node text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
151
+ object text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
152
+ channel text NOT NULL CHECK (channel IN ('system','transcript')),
153
+ level text NOT NULL CHECK (level IN ('name','preview','content')),
154
+ heads_key text,
155
+ content_hash text,
156
+ PRIMARY KEY (node, object, channel)
157
+ );
158
+
159
+ CREATE TABLE rules (
160
+ object text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
161
+ position bigint NOT NULL,
162
+ owner text,
163
+ on_event text NOT NULL CHECK (on_event IN ('boot','workspace-open','file-read','document-read','command','pre-command','slash-command')),
164
+ match_json text,
165
+ match_frontmatter_json text,
166
+ if_json text,
167
+ deliver text NOT NULL CHECK (deliver IN ('name','preview','content')),
168
+ PRIMARY KEY (object, position)
169
+ );
170
+ CREATE INDEX rules_on_owner ON rules(on_event, owner);
171
+
172
+ CREATE TABLE edges (
173
+ type text NOT NULL CHECK (type IN ('acted-on','links-to')),
174
+ src text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
175
+ dst text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
176
+ written_as text,
177
+ created_at text NOT NULL,
178
+ PRIMARY KEY (type, src, dst)
179
+ );
180
+ CREATE INDEX edges_dst ON edges(type, dst, src);
181
+
182
+ CREATE TABLE unresolved_links (
183
+ src text NOT NULL REFERENCES objects(id) ON DELETE CASCADE,
184
+ name text NOT NULL,
185
+ PRIMARY KEY (src, name)
186
+ );
187
+
188
+ -- ---------------------------------------------------------------- nodes and runtime state
189
+
190
+ CREATE TABLE nodes (
191
+ node_id text PRIMARY KEY,
192
+ mode text NOT NULL DEFAULT 'base',
193
+ lifecycle text NOT NULL DEFAULT 'terminal',
194
+ status text NOT NULL DEFAULT 'active',
195
+ cwd text NOT NULL,
196
+ parent text,
197
+ created text NOT NULL,
198
+ intent text,
199
+ "window" text,
200
+ tmux_session text,
201
+ pane text,
202
+ host_kind text,
203
+ profile_id text,
204
+ final_report text,
205
+ finalized_at text,
206
+ frozen_at text,
207
+ respawn_failures bigint NOT NULL DEFAULT 0,
208
+ respawn_not_before text,
209
+ launched_at text,
210
+ last_settled_at text,
211
+ terminal_reason text,
212
+ description text,
213
+ cycles bigint,
214
+ outcome_kind text,
215
+ outcome_reason text,
216
+ outcome_at text,
217
+ outcome_revision bigint NOT NULL DEFAULT 0,
218
+ outcome_detail_json text,
219
+ deadline_at text,
220
+ title text,
221
+ icon text,
222
+ spawned_by text,
223
+ creator text,
224
+ fork_from text,
225
+ fork_source_file text,
226
+ passive_default bigint NOT NULL DEFAULT 0 CHECK (passive_default IN (0,1)),
227
+ cycle_pending bigint NOT NULL DEFAULT 0 CHECK (cycle_pending IN (0,1)),
228
+ model_override text,
229
+ persona_ack_json text,
230
+ review_binding_json text,
231
+ launch_config_json text,
232
+ scopes_json text,
233
+ telemetry_tokens_in bigint,
234
+ telemetry_context_tokens bigint,
235
+ telemetry_last_activity text,
236
+ telemetry_updated_at text,
237
+ sub text,
238
+ grant_id text,
239
+ client_id text,
240
+ metadata_json text,
241
+ run_id text,
242
+ event_cursor bigint,
243
+ usage_json text,
244
+ deleting_at text,
245
+ last_activity_at text,
246
+ isolated bigint NOT NULL DEFAULT 0 CHECK (isolated IN (0,1)),
247
+ last_turn_error_json text,
248
+ reply_at text,
249
+ read_at text
250
+ );
251
+ CREATE INDEX idx_nodes_status ON nodes(status);
252
+ CREATE INDEX idx_nodes_parent ON nodes(parent);
253
+ CREATE INDEX idx_nodes_created ON nodes(created);
254
+ CREATE INDEX idx_nodes_parent_created ON nodes(parent, created);
255
+ CREATE INDEX idx_nodes_run_id ON nodes(run_id);
256
+ CREATE INDEX idx_nodes_deadline ON nodes(deadline_at) WHERE deadline_at IS NOT NULL;
257
+ CREATE INDEX idx_nodes_cycle_pending ON nodes(cycle_pending) WHERE cycle_pending = 1;
258
+ CREATE INDEX idx_nodes_terminal_placement_created ON nodes(created)
259
+ WHERE status IN ('done', 'canceled', 'dead')
260
+ AND (pane IS NOT NULL OR "window" IS NOT NULL OR tmux_session IS NOT NULL);
261
+ CREATE INDEX idx_nodes_supervision_frozen ON nodes(node_id) WHERE frozen_at IS NOT NULL;
262
+
263
+ CREATE TABLE focuses (
264
+ focus_id text PRIMARY KEY,
265
+ pane text,
266
+ session text,
267
+ node_id text NOT NULL UNIQUE
268
+ );
269
+
270
+ CREATE TABLE canvas_meta (
271
+ key text PRIMARY KEY,
272
+ value text NOT NULL
273
+ );
274
+
275
+ CREATE TABLE runtime_state (
276
+ key text PRIMARY KEY,
277
+ value text NOT NULL
278
+ );
279
+
280
+ CREATE TABLE warm_pool (
281
+ node_id text PRIMARY KEY REFERENCES nodes(node_id) ON DELETE CASCADE,
282
+ recipe_key text NOT NULL,
283
+ created text NOT NULL
284
+ );
285
+ CREATE INDEX idx_warm_pool_recipe ON warm_pool(recipe_key, created);
286
+
287
+ CREATE TABLE sessions (
288
+ binding_id text PRIMARY KEY,
289
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
290
+ session_id text,
291
+ node_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
292
+ transcript_path text,
293
+ source_session_id text,
294
+ fork_entry_id text,
295
+ created_at text NOT NULL,
296
+ bound_at text NOT NULL,
297
+ unbound_at text,
298
+ event_offset bigint,
299
+ indexed_entry_id text,
300
+ input_ids_json text NOT NULL DEFAULT '[]'
301
+ );
302
+ CREATE INDEX idx_sessions_node ON sessions(node_id, created_at);
303
+ CREATE INDEX idx_sessions_session ON sessions(session_id);
304
+ CREATE UNIQUE INDEX idx_sessions_live_node ON sessions(node_id) WHERE unbound_at IS NULL;
305
+ CREATE UNIQUE INDEX idx_sessions_live_transcript ON sessions(transcript_path)
306
+ WHERE unbound_at IS NULL AND transcript_path IS NOT NULL;
307
+
308
+ CREATE TABLE executions (
309
+ execution_id text PRIMARY KEY,
310
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
311
+ node_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
312
+ daemon_epoch text,
313
+ pid bigint,
314
+ process_identity text,
315
+ state text NOT NULL CHECK (state IN ('launching','running','exited')),
316
+ started_at text NOT NULL,
317
+ ended_at text,
318
+ exit_reason text,
319
+ turn_open_at text,
320
+ turn_progress_at text,
321
+ turn_closed_at text,
322
+ turn_end_reason text CHECK (turn_end_reason IS NULL OR turn_end_reason IN ('settled', 'teardown-aborted')),
323
+ exit_diagnostic_json text,
324
+ CHECK ((state = 'exited') = (ended_at IS NOT NULL))
325
+ );
326
+ CREATE INDEX idx_executions_node ON executions(node_id, started_at);
327
+ CREATE UNIQUE INDEX idx_executions_live_node ON executions(node_id) WHERE state != 'exited';
328
+
329
+ CREATE TABLE worktrees (
330
+ node_id text PRIMARY KEY REFERENCES nodes(node_id) ON DELETE CASCADE,
331
+ state text NOT NULL CHECK (state IN ('open','closed','abandoned')),
332
+ cleanup_state text CHECK (cleanup_state IS NULL OR cleanup_state IN ('pending','complete')),
333
+ path text NOT NULL,
334
+ branch text NOT NULL,
335
+ repo_root text NOT NULL,
336
+ base_ref text NOT NULL,
337
+ base_sha text NOT NULL,
338
+ created_at text NOT NULL,
339
+ closed_at text,
340
+ next_attempt_at text,
341
+ attempts bigint NOT NULL DEFAULT 0 CHECK (attempts >= 0),
342
+ quarantined bigint NOT NULL DEFAULT 0 CHECK (quarantined IN (0,1)),
343
+ sweep_reason text,
344
+ sweep_detail text,
345
+ sweep_last_at text,
346
+ sweep_observed_json text,
347
+ abandoned_json text,
348
+ land_intent_json text,
349
+ disposal_json text
350
+ );
351
+ CREATE INDEX idx_worktrees_sweep ON worktrees(state, cleanup_state, next_attempt_at);
352
+
353
+ CREATE TABLE node_faults (
354
+ fault_id text PRIMARY KEY,
355
+ node_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
356
+ link text NOT NULL CHECK (link IN ('pi→provider', 'viewer↔broker', 'relay↔broker', 'viewer↔crtrd', 'daemon→node', 'crtr→pi')),
357
+ op text NOT NULL,
358
+ kind text NOT NULL CHECK (kind IN ('rate-limit', 'overloaded', 'connection', 'auth', 'protocol', 'context-overflow', 'other', 'wedged', 'model-not-found')),
359
+ message text NOT NULL,
360
+ since text NOT NULL,
361
+ operation_id text NOT NULL,
362
+ anchor_entry_id text,
363
+ retry_disposition text NOT NULL CHECK (retry_disposition IN ('auto', 'manual', 'fatal')),
364
+ retry_by text CHECK (retry_by IS NULL OR retry_by IN ('sdk', 'daemon', 'client')),
365
+ retry_attempt bigint,
366
+ retry_max bigint,
367
+ retry_next_at text,
368
+ provider_error_json text,
369
+ active bigint NOT NULL DEFAULT 0 CHECK (active IN (0, 1)),
370
+ episode_state text CHECK (episode_state IS NULL OR episode_state IN ('pending', 'admitted', 'invalidated')),
371
+ episode_binding_id text REFERENCES sessions(binding_id),
372
+ created_at text NOT NULL,
373
+ updated_at text NOT NULL,
374
+ notified_since text
375
+ );
376
+ CREATE UNIQUE INDEX idx_node_faults_active ON node_faults(node_id) WHERE active = 1;
377
+ CREATE UNIQUE INDEX idx_node_faults_episode ON node_faults(node_id) WHERE episode_state IS NOT NULL;
378
+
379
+ CREATE TABLE node_tokens (
380
+ broker_process_id text PRIMARY KEY REFERENCES executions(execution_id) ON DELETE CASCADE,
381
+ node_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
382
+ token_hash text NOT NULL UNIQUE,
383
+ grantee text NOT NULL,
384
+ uid bigint NOT NULL,
385
+ sub text NOT NULL,
386
+ "grant" text,
387
+ client_id text,
388
+ scopes_json text,
389
+ issued_at text NOT NULL,
390
+ revoked_at text
391
+ );
392
+
393
+ CREATE TABLE node_outcome_deliveries (
394
+ node_id text PRIMARY KEY REFERENCES nodes(node_id) ON DELETE CASCADE,
395
+ state text NOT NULL,
396
+ attempt bigint NOT NULL DEFAULT 0,
397
+ action_name text NOT NULL,
398
+ argv_json text NOT NULL,
399
+ cwd text NOT NULL,
400
+ config_origin text NOT NULL,
401
+ payload_json text,
402
+ document_json text,
403
+ next_attempt_at bigint NOT NULL DEFAULT 0,
404
+ claimed_at bigint,
405
+ claim_owner text,
406
+ accepted_at bigint,
407
+ permanent_failed_at bigint,
408
+ last_failure_json text,
409
+ created_at bigint NOT NULL,
410
+ updated_at bigint NOT NULL
411
+ );
412
+ CREATE INDEX idx_node_outcome_deliveries_due ON node_outcome_deliveries(state, next_attempt_at);
413
+
414
+ CREATE TABLE deleted_isolated_nodes (
415
+ node_id text PRIMARY KEY,
416
+ run_id text NOT NULL
417
+ );
418
+
419
+ CREATE TABLE create_requests (
420
+ request_key text PRIMARY KEY,
421
+ node_id text,
422
+ expires_at bigint NOT NULL,
423
+ state text NOT NULL DEFAULT 'reserved' CHECK (state IN ('reserved', 'warm_claimed', 'complete'))
424
+ );
425
+ CREATE INDEX idx_create_requests_expires ON create_requests(expires_at);
426
+
427
+ CREATE TABLE waits (
428
+ wait_id text PRIMARY KEY,
429
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
430
+ node_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
431
+ kind text NOT NULL CHECK (kind IN ('controller', 'object')),
432
+ target_id text NOT NULL,
433
+ generation bigint NOT NULL,
434
+ created_at bigint NOT NULL,
435
+ satisfied_at bigint
436
+ );
437
+ CREATE UNIQUE INDEX waits_live ON waits(node_id) WHERE satisfied_at IS NULL AND kind = 'controller';
438
+ CREATE UNIQUE INDEX waits_live_object ON waits(node_id, target_id) WHERE satisfied_at IS NULL AND kind = 'object';
439
+ CREATE INDEX waits_target ON waits(target_id) WHERE satisfied_at IS NULL;
440
+ CREATE INDEX waits_node ON waits(node_id);
441
+
442
+ -- ---------------------------------------------------------------- messaging
443
+
444
+ CREATE TABLE messages (
445
+ message_id text PRIMARY KEY,
446
+ seq bigint NOT NULL UNIQUE,
447
+ operation_id text NOT NULL,
448
+ created_at text NOT NULL,
449
+ sender_node_id text,
450
+ sender_name text,
451
+ kind text NOT NULL CHECK (kind IN ('update', 'urgent', 'final', 'message', 'completed')),
452
+ label text NOT NULL,
453
+ body_ref text,
454
+ body_json jsonb,
455
+ disposition text CHECK (disposition IS NULL OR disposition IN ('human-answer', 'human-canceled')),
456
+ event text,
457
+ object_id text,
458
+ level bigint,
459
+ actor_id text,
460
+ cause_message_id text,
461
+ depth bigint NOT NULL DEFAULT 0
462
+ );
463
+
464
+ CREATE TABLE message_sequence (
465
+ id bigint PRIMARY KEY CHECK (id = 1),
466
+ seq bigint NOT NULL
467
+ );
468
+ -- The single counter row feed/messages.ts increments; the SQLite importer sets seq to MAX(messages.seq).
469
+ INSERT INTO message_sequence (id, seq) VALUES (1, 0);
470
+
471
+ CREATE TABLE message_causes (
472
+ message_id text PRIMARY KEY,
473
+ depth bigint NOT NULL
474
+ );
475
+
476
+ CREATE TABLE deliveries (
477
+ delivery_id text PRIMARY KEY,
478
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
479
+ message_id text NOT NULL REFERENCES messages(message_id),
480
+ destination_id text NOT NULL,
481
+ urgency text NOT NULL CHECK (urgency IN ('critical', 'urgent', 'normal', 'deferred')),
482
+ wake bigint NOT NULL CHECK (wake IN (0, 1)),
483
+ state text NOT NULL CHECK (state IN ('pending', 'handed_off', 'acknowledged', 'permanent_failed')),
484
+ attempts bigint NOT NULL DEFAULT 0 CHECK (attempts >= 0),
485
+ next_attempt_at bigint,
486
+ claimed_at bigint,
487
+ claim_owner text,
488
+ acknowledged_at bigint,
489
+ failed_at bigint,
490
+ last_failure_json text,
491
+ idempotency_key text,
492
+ canceled_at bigint,
493
+ created_at bigint NOT NULL,
494
+ updated_at bigint NOT NULL
495
+ );
496
+ CREATE INDEX deliveries_destination ON deliveries(destination_id, wake, state, created_at);
497
+ CREATE INDEX deliveries_due ON deliveries(state, next_attempt_at, created_at, delivery_id)
498
+ WHERE next_attempt_at IS NOT NULL AND canceled_at IS NULL;
499
+ CREATE INDEX deliveries_handed_off ON deliveries(claim_owner) WHERE state = 'handed_off';
500
+ CREATE INDEX deliveries_message ON deliveries(message_id);
501
+ CREATE UNIQUE INDEX deliveries_idempotency ON deliveries(idempotency_key) WHERE idempotency_key IS NOT NULL;
502
+
503
+ CREATE TABLE structured_requests (
504
+ request_id text PRIMARY KEY,
505
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
506
+ node_id text REFERENCES nodes(node_id) ON DELETE CASCADE,
507
+ mode text NOT NULL,
508
+ submission text NOT NULL,
509
+ dedicated bigint NOT NULL,
510
+ state text NOT NULL,
511
+ schema_json text NOT NULL,
512
+ result_json text,
513
+ deadline_at bigint,
514
+ created_at bigint NOT NULL,
515
+ settled_at bigint,
516
+ effects_done_at bigint,
517
+ result_path text,
518
+ declined_reason text,
519
+ declined_code text,
520
+ declined_retryable bigint,
521
+ failure_reason text,
522
+ failure_detail_json text,
523
+ request_message_id text
524
+ );
525
+ CREATE UNIQUE INDEX idx_structured_requests_one_pending_node ON structured_requests(node_id) WHERE state = 'pending';
526
+ CREATE INDEX idx_structured_requests_expiry ON structured_requests(state, deadline_at);
527
+ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done_at) WHERE effects_done_at IS NULL;
528
+
529
+ CREATE TABLE daemon_actions (
530
+ action_id text PRIMARY KEY,
531
+ adapter_json text NOT NULL,
532
+ body_json text,
533
+ idempotency_key text NOT NULL UNIQUE,
534
+ state text NOT NULL CHECK (state IN ('pending', 'handed_off', 'acknowledged', 'permanent_failed')),
535
+ attempts bigint NOT NULL DEFAULT 0 CHECK (attempts >= 0),
536
+ next_attempt_at bigint,
537
+ claimed_at bigint,
538
+ claim_owner text,
539
+ acknowledged_at bigint,
540
+ failed_at bigint,
541
+ last_failure_json text,
542
+ canceled_at bigint,
543
+ created_at bigint NOT NULL,
544
+ updated_at bigint NOT NULL
545
+ );
546
+ CREATE INDEX daemon_actions_due ON daemon_actions(state, next_attempt_at, created_at, action_id) WHERE canceled_at IS NULL;
547
+
548
+ -- ---------------------------------------------------------------- human requests and reviews
549
+
550
+ CREATE TABLE human_requests (
551
+ request_id text PRIMARY KEY,
552
+ kind text NOT NULL CHECK (kind IN ('page','review')),
553
+ inbox_ticket_id text NOT NULL UNIQUE,
554
+ request_dir text NOT NULL,
555
+ presentation_dir text,
556
+ requester_node_id text,
557
+ companion_node_id text UNIQUE,
558
+ idempotency_key text UNIQUE,
559
+ state text NOT NULL CHECK (state IN ('binding','open','settled')),
560
+ outcome text CHECK (outcome IN ('approved','answered','canceled','dismissed')),
561
+ inbox_listed bigint NOT NULL,
562
+ settleable bigint NOT NULL,
563
+ title text NOT NULL,
564
+ subtitle text,
565
+ file text,
566
+ specification_json jsonb NOT NULL,
567
+ action_json jsonb,
568
+ progress_json jsonb,
569
+ feedback_json jsonb,
570
+ result_json jsonb,
571
+ anchor_base_sha256 text,
572
+ anchor_base_bytes bytea,
573
+ created_at text NOT NULL,
574
+ opened_at text,
575
+ submit_requested_at text,
576
+ settled_at text,
577
+ settlement_delivery_id text,
578
+ action_delivery_id text,
579
+ submit_note_delivery_id text,
580
+ projected_at text,
581
+ projection_error text,
582
+ companion_retired_at text,
583
+ updated_at text NOT NULL,
584
+ CHECK ((state = 'settled') = (outcome IS NOT NULL)),
585
+ CHECK ((state = 'settled') = (result_json IS NOT NULL)),
586
+ CHECK (kind <> 'page' OR presentation_dir IS NOT NULL)
587
+ );
588
+ CREATE INDEX idx_human_requests_inbox ON human_requests(state, inbox_listed);
589
+ CREATE INDEX idx_human_requests_kind_state ON human_requests(kind, state);
590
+ CREATE INDEX idx_human_requests_submit ON human_requests(state, submit_requested_at);
591
+ CREATE INDEX idx_human_requests_file ON human_requests(file);
592
+ CREATE INDEX idx_human_requests_requester ON human_requests(requester_node_id);
593
+ CREATE INDEX idx_human_requests_page_source ON human_requests((specification_json #>> '{source,nodeId}'))
594
+ WHERE kind = 'page';
595
+ CREATE INDEX idx_human_requests_page_invalid_spec ON human_requests(created_at, request_id)
596
+ WHERE kind = 'page' AND jsonb_typeof(specification_json) <> 'object';
597
+ CREATE INDEX idx_human_requests_page_result_exception ON human_requests(created_at, request_id)
598
+ WHERE kind = 'page' AND result_json IS NOT NULL
599
+ AND (result_json ->> 'kind') = 'page'
600
+ AND jsonb_typeof(result_json -> 'summary') IS DISTINCT FROM 'string';
601
+
602
+ CREATE TABLE human_action_deliveries (
603
+ request_id text PRIMARY KEY,
604
+ state text NOT NULL,
605
+ attempt bigint NOT NULL DEFAULT 0,
606
+ argv_json text NOT NULL,
607
+ cwd text NOT NULL,
608
+ completion_json text NOT NULL,
609
+ next_attempt_at bigint NOT NULL,
610
+ claimed_at bigint,
611
+ claim_owner text,
612
+ accepted_at bigint,
613
+ permanent_failed_at bigint,
614
+ last_failure_json text,
615
+ created_at bigint NOT NULL,
616
+ updated_at bigint NOT NULL
617
+ );
618
+ CREATE INDEX idx_human_action_deliveries_due ON human_action_deliveries(state, next_attempt_at);
619
+
620
+ CREATE TABLE review_comments (
621
+ comment_id text PRIMARY KEY,
622
+ review_id text NOT NULL,
623
+ status text NOT NULL CHECK (status IN ('open', 'resolved', 'deleted')),
624
+ revision bigint NOT NULL,
625
+ text text NOT NULL,
626
+ anchor_line bigint NOT NULL,
627
+ anchor_end_line bigint NOT NULL,
628
+ anchor_col_start bigint,
629
+ anchor_col_end bigint,
630
+ anchor_quote text,
631
+ anchor_line_text text NOT NULL,
632
+ anchor_doc_hash text NOT NULL,
633
+ author_kind text NOT NULL CHECK (author_kind IN ('human', 'node')),
634
+ author_node_id text,
635
+ updated_by_kind text NOT NULL CHECK (updated_by_kind IN ('human', 'node')),
636
+ updated_by_node_id text,
637
+ created_at text NOT NULL,
638
+ updated_at text NOT NULL,
639
+ resolved_at text,
640
+ deleted_at text,
641
+ created_seq bigint NOT NULL,
642
+ anchor_state text NOT NULL DEFAULT 'live' CHECK (anchor_state IN ('live', 'detached'))
643
+ );
644
+ CREATE INDEX idx_review_comments_review_line_seq ON review_comments(review_id, anchor_line, created_seq);
645
+ CREATE INDEX idx_review_comments_review_status ON review_comments(review_id, status);
646
+
647
+ CREATE TABLE review_comment_events (
648
+ seq bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
649
+ review_id text NOT NULL,
650
+ comment_id text NOT NULL,
651
+ kind text NOT NULL CHECK (kind IN ('created', 'edited', 'resolved', 'reopened', 'deleted', 'reanchored', 'notified', 'notify_failed')),
652
+ state_change bigint NOT NULL,
653
+ revision_after bigint,
654
+ actor_kind text NOT NULL CHECK (actor_kind IN ('human', 'node', 'system')),
655
+ actor_node_id text,
656
+ at text NOT NULL,
657
+ operation_id text NOT NULL,
658
+ payload text
659
+ );
660
+ CREATE INDEX idx_review_comment_events_review_seq ON review_comment_events(review_id, seq);
661
+ CREATE INDEX idx_review_comment_events_comment_seq ON review_comment_events(comment_id, seq);
662
+
663
+ -- ---------------------------------------------------------------- runs, grants, audit, usage
664
+
665
+ -- body_json stays text: 77 rows carry the JSON escape \u0000, which jsonb and json operators reject.
666
+ CREATE TABLE run_events (
667
+ run_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
668
+ sequence_number bigint NOT NULL,
669
+ seq bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,
670
+ node_id text NOT NULL,
671
+ type text NOT NULL,
672
+ ts text NOT NULL,
673
+ body_json text NOT NULL,
674
+ grantee text NOT NULL,
675
+ PRIMARY KEY (run_id, sequence_number)
676
+ );
677
+ CREATE INDEX idx_run_events_ts ON run_events(ts);
678
+ CREATE INDEX idx_run_events_node_type ON run_events(run_id, node_id, type);
679
+
680
+ CREATE TABLE idempotency_keys (
681
+ grantee text NOT NULL,
682
+ key text NOT NULL,
683
+ body_hash text NOT NULL,
684
+ run_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
685
+ response_json text NOT NULL,
686
+ created_at text NOT NULL,
687
+ PRIMARY KEY (grantee, key)
688
+ );
689
+ CREATE INDEX idx_idempotency_keys_created_at ON idempotency_keys(created_at);
690
+
691
+ CREATE TABLE grants (
692
+ id text PRIMARY KEY,
693
+ runtime text NOT NULL,
694
+ sub text NOT NULL,
695
+ grantee text NOT NULL,
696
+ rev bigint NOT NULL,
697
+ scopes_json text NOT NULL,
698
+ resolved_scopes_json text NOT NULL,
699
+ spend_cap double precision,
700
+ expires_at text,
701
+ status text NOT NULL,
702
+ revoked_at text,
703
+ approved_at text,
704
+ payer text,
705
+ synced_at text NOT NULL,
706
+ dependencies_json text NOT NULL DEFAULT '{}',
707
+ run_notice_url text,
708
+ inference_endpoint_json text
709
+ );
710
+ CREATE UNIQUE INDEX idx_grants_grantee_sub ON grants(grantee, sub);
711
+
712
+ CREATE TABLE memory_reads (
713
+ node_id text NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
714
+ name text NOT NULL,
715
+ scope text NOT NULL,
716
+ at text NOT NULL,
717
+ type text NOT NULL CHECK (type IN ('read','loaded'))
718
+ );
719
+ CREATE INDEX idx_memory_reads_at ON memory_reads(at);
720
+ CREATE INDEX idx_memory_reads_node ON memory_reads(node_id);
721
+
722
+ CREATE TABLE app_users (
723
+ uid bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
724
+ app text NOT NULL,
725
+ created text NOT NULL,
726
+ status text NOT NULL CHECK (status IN ('active', 'removed')),
727
+ removed_at text
728
+ );
729
+ CREATE UNIQUE INDEX idx_app_users_active_app ON app_users(app) WHERE status = 'active';
730
+
731
+ CREATE TABLE uploads (
732
+ upload_id text PRIMARY KEY,
733
+ grantee text NOT NULL,
734
+ name text NOT NULL,
735
+ media_type text NOT NULL,
736
+ size bigint NOT NULL,
737
+ created_at text NOT NULL,
738
+ delete_pending_since text
739
+ );
740
+ CREATE INDEX idx_uploads_grantee ON uploads(grantee);
741
+ CREATE INDEX idx_uploads_delete_pending ON uploads(delete_pending_since) WHERE delete_pending_since IS NOT NULL;
742
+
743
+ CREATE TABLE upload_uses (
744
+ upload_id text NOT NULL,
745
+ run_id text NOT NULL,
746
+ path text NOT NULL,
747
+ PRIMARY KEY (run_id, path)
748
+ );
749
+ CREATE INDEX idx_upload_uses_upload ON upload_uses(upload_id);
750
+
751
+ CREATE TABLE shares (
752
+ share_id text PRIMARY KEY,
753
+ source_space text NOT NULL,
754
+ source_path text NOT NULL,
755
+ storage_key text NOT NULL,
756
+ url text NOT NULL,
757
+ created_by text NOT NULL,
758
+ created_at text NOT NULL,
759
+ expires_at text,
760
+ status text NOT NULL CHECK (status IN ('on', 'off')),
761
+ object_deleted_at text
762
+ );
763
+ CREATE INDEX idx_shares_created_by ON shares(created_by);
764
+
765
+ CREATE TABLE audit_rows (
766
+ id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
767
+ request_id text NOT NULL,
768
+ time text NOT NULL,
769
+ grantee text,
770
+ sub text,
771
+ client_id text,
772
+ class text,
773
+ operation text NOT NULL,
774
+ scope_checked text,
775
+ outcome text NOT NULL,
776
+ node_id text,
777
+ run_id text,
778
+ payer text,
779
+ resource text,
780
+ initiator_chain text,
781
+ provider text,
782
+ tool text,
783
+ jti text,
784
+ receipt_id text,
785
+ responder text,
786
+ question_id text,
787
+ via text
788
+ );
789
+ CREATE INDEX audit_rows_grantee_time ON audit_rows(grantee, id DESC);
790
+ CREATE INDEX audit_rows_request ON audit_rows(request_id);
791
+ CREATE INDEX audit_rows_time ON audit_rows(id DESC);
792
+
793
+ CREATE TABLE grantee_usage (
794
+ grantee text NOT NULL,
795
+ month text NOT NULL,
796
+ usd_equivalent double precision NOT NULL DEFAULT 0,
797
+ PRIMARY KEY (grantee, month)
798
+ );
799
+
800
+ CREATE TABLE provider_receipts (
801
+ provider text NOT NULL,
802
+ receipt_id text NOT NULL,
803
+ grantee text NOT NULL,
804
+ run_id text,
805
+ usd_equivalent double precision NOT NULL,
806
+ received_at text NOT NULL,
807
+ PRIMARY KEY (provider, receipt_id)
808
+ );
809
+
810
+ CREATE TABLE provider_approvals (
811
+ question_id text PRIMARY KEY,
812
+ grantee text NOT NULL,
813
+ sub text NOT NULL,
814
+ provider text NOT NULL,
815
+ tool text NOT NULL,
816
+ version bigint,
817
+ args_hash text NOT NULL,
818
+ request_id text NOT NULL,
819
+ status text NOT NULL CHECK (status IN ('open','answered','consumed','canceled')),
820
+ option_id text,
821
+ call_shape text,
822
+ prepared_files text
823
+ );
824
+
825
+ CREATE TABLE usage (
826
+ grantee text NOT NULL,
827
+ month text NOT NULL,
828
+ payer text NOT NULL,
829
+ source text NOT NULL,
830
+ served_model text NOT NULL,
831
+ input_tokens bigint NOT NULL DEFAULT 0,
832
+ output_tokens bigint NOT NULL DEFAULT 0,
833
+ cached_tokens bigint NOT NULL DEFAULT 0,
834
+ usd_equivalent double precision,
835
+ PRIMARY KEY (grantee, month, payer, source, served_model)
836
+ );
837
+
838
+ -- ---------------------------------------------------------------- crons
839
+
840
+ CREATE TABLE crons (
841
+ cron_id text PRIMARY KEY,
842
+ name text NOT NULL,
843
+ created_by text,
844
+ command text NOT NULL,
845
+ fire_at text NOT NULL,
846
+ recur text,
847
+ tz text,
848
+ expires_at text,
849
+ anchor_node text REFERENCES nodes(node_id) ON DELETE CASCADE,
850
+ cancel_on_wake bigint NOT NULL DEFAULT 0,
851
+ cwd text NOT NULL,
852
+ env_json text,
853
+ profile text,
854
+ scope text NOT NULL DEFAULT 'profile',
855
+ run_timeout_s bigint NOT NULL DEFAULT 300,
856
+ overlap text NOT NULL DEFAULT 'skip',
857
+ on_output text NOT NULL DEFAULT 'on-failure',
858
+ sink text NOT NULL,
859
+ tier text NOT NULL DEFAULT 'normal',
860
+ last_output_hash text,
861
+ state text NOT NULL DEFAULT 'active',
862
+ run_state text NOT NULL DEFAULT 'idle',
863
+ run_pid bigint,
864
+ run_pid_identity text,
865
+ run_started_at text,
866
+ run_lease_owner text,
867
+ run_out_of_band bigint NOT NULL DEFAULT 0,
868
+ created text NOT NULL,
869
+ updated text NOT NULL,
870
+ held bigint NOT NULL DEFAULT 0,
871
+ last_poke_at text,
872
+ grantee text NOT NULL DEFAULT 'app:terminal',
873
+ isolated bigint NOT NULL DEFAULT 0 CHECK (isolated IN (0, 1)),
874
+ sub text,
875
+ grant_id text,
876
+ scopes_json text,
877
+ origin_run_id text,
878
+ run_execution_id text
879
+ );
880
+ CREATE UNIQUE INDEX idx_crons_deadline ON crons(anchor_node) WHERE cancel_on_wake = 1;
881
+ CREATE INDEX idx_crons_fire ON crons(fire_at);
882
+
883
+ CREATE TABLE cron_runs (
884
+ run_id text PRIMARY KEY,
885
+ cron_id text NOT NULL REFERENCES crons(cron_id) ON DELETE CASCADE,
886
+ started text NOT NULL,
887
+ finished text,
888
+ duration_ms bigint,
889
+ exit_code bigint,
890
+ stdout_head text,
891
+ stderr_head text,
892
+ delivered text
893
+ );
894
+ CREATE INDEX idx_cron_runs_cron ON cron_runs(cron_id, started);
895
+
896
+ -- ---------------------------------------------------------------- peer
897
+
898
+ CREATE TABLE peer_outbox (
899
+ id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
900
+ peer text NOT NULL,
901
+ kind text NOT NULL,
902
+ payload_json text NOT NULL,
903
+ idempotency_key text NOT NULL UNIQUE,
904
+ state text NOT NULL CHECK (state IN ('pending','waiting','claimed','done','expired')),
905
+ attempts bigint NOT NULL DEFAULT 0,
906
+ next_at bigint,
907
+ claimed_by text,
908
+ claimed_at bigint,
909
+ first_failed_at bigint,
910
+ created_at bigint NOT NULL
911
+ );
912
+ CREATE INDEX peer_outbox_due ON peer_outbox(next_at) WHERE state = 'pending';
913
+ CREATE INDEX peer_outbox_peer ON peer_outbox(peer, state);
914
+
915
+ CREATE TABLE peer_locations (
916
+ id text PRIMARY KEY,
917
+ machine_id text NOT NULL,
918
+ learned_at bigint NOT NULL
919
+ );
920
+
921
+ CREATE TABLE peer_ingested (
922
+ key text PRIMARY KEY,
923
+ ingested_at bigint NOT NULL
924
+ );
925
+
926
+ CREATE TABLE peer_ingested_destinations (
927
+ key text PRIMARY KEY,
928
+ ingested_at bigint NOT NULL
929
+ );
930
+
931
+ -- ---------------------------------------------------------------- view
932
+
933
+ -- Column list is fixed at creation (n.* expands here): a later nodes column needs a delta that
934
+ -- recreates this view.
935
+ CREATE VIEW node_rows AS
936
+ SELECT n.*,
937
+ o.name,
938
+ o.kind,
939
+ oi.external_id AS grantee,
940
+ CASE WHEN e.state = 'running' THEN e.pid ELSE NULL END AS pi_pid,
941
+ CASE WHEN e.state = 'running' THEN e.process_identity ELSE NULL END AS pi_pid_identity,
942
+ e.turn_open_at,
943
+ e.turn_closed_at,
944
+ s.session_id AS pi_session_id,
945
+ s.transcript_path AS pi_session_file,
946
+ f.fault_id,
947
+ f.link AS fault_link,
948
+ f.op AS fault_op,
949
+ f.kind AS fault_kind,
950
+ f.message AS fault_message,
951
+ f.since AS fault_since,
952
+ f.operation_id AS fault_operation_id,
953
+ f.anchor_entry_id AS fault_anchor_entry_id,
954
+ f.retry_disposition AS fault_retry_disposition,
955
+ f.retry_by AS fault_retry_by,
956
+ f.retry_attempt AS fault_retry_attempt,
957
+ f.retry_max AS fault_retry_max,
958
+ f.retry_next_at AS fault_retry_next_at,
959
+ f.provider_error_json AS fault_provider_error_json
960
+ FROM nodes n
961
+ JOIN objects o ON o.id = n.node_id
962
+ LEFT JOIN identities oi ON oi.object = o.owner
963
+ LEFT JOIN executions e ON e.node_id = n.node_id AND e.state != 'exited'
964
+ LEFT JOIN sessions s ON s.node_id = n.node_id AND s.unbound_at IS NULL
965
+ LEFT JOIN node_faults f ON f.node_id = n.node_id AND f.active = 1;
966
+
967
+ -- ---------------------------------------------------------------- triggers
968
+
969
+ CREATE FUNCTION nodes_reception_set() RETURNS trigger LANGUAGE plpgsql AS $$
970
+ BEGIN
971
+ UPDATE objects SET reception = CASE WHEN NEW.status IN ('active','idle') THEN 2 ELSE 1 END
972
+ WHERE id = NEW.node_id;
973
+ RETURN NULL;
974
+ END $$;
975
+ CREATE TRIGGER nodes_reception_insert AFTER INSERT ON nodes
976
+ FOR EACH ROW EXECUTE FUNCTION nodes_reception_set();
977
+ CREATE TRIGGER nodes_reception_status AFTER UPDATE OF status ON nodes
978
+ FOR EACH ROW EXECUTE FUNCTION nodes_reception_set();
979
+
980
+ CREATE FUNCTION preserve_message_cause() RETURNS trigger LANGUAGE plpgsql AS $$
981
+ BEGIN
982
+ INSERT INTO message_causes (message_id, depth) VALUES (OLD.message_id, OLD.depth)
983
+ ON CONFLICT DO NOTHING;
984
+ RETURN OLD;
985
+ END $$;
986
+ CREATE TRIGGER preserve_message_cause BEFORE DELETE ON messages
987
+ FOR EACH ROW EXECUTE FUNCTION preserve_message_cause();
988
+
989
+ -- Broker-signal bus: notifications arrive at commit as 'mail:<node id>' / 'node:<node id>' on
990
+ -- channel crtr_signal. These replace the four TEMP triggers in daemon/broker-signals/bus.ts.
991
+ -- The node trigger's column list must equal BROKER_SIGNAL_NODE_COLUMNS in bus.ts.
992
+ CREATE FUNCTION crtr_signal_mail() RETURNS trigger LANGUAGE plpgsql AS $$
993
+ BEGIN
994
+ IF TG_OP = 'DELETE' THEN
995
+ PERFORM pg_notify('crtr_signal', 'mail:' || OLD.destination_id);
996
+ RETURN OLD;
997
+ END IF;
998
+ PERFORM pg_notify('crtr_signal', 'mail:' || NEW.destination_id);
999
+ RETURN NEW;
1000
+ END $$;
1001
+ CREATE TRIGGER crtr_sig_deliv_ins AFTER INSERT ON deliveries
1002
+ FOR EACH ROW WHEN (NEW.wake = 1) EXECUTE FUNCTION crtr_signal_mail();
1003
+ CREATE TRIGGER crtr_sig_deliv_upd AFTER UPDATE OF state, canceled_at, claim_owner ON deliveries
1004
+ FOR EACH ROW WHEN (NEW.wake = 1) EXECUTE FUNCTION crtr_signal_mail();
1005
+ CREATE TRIGGER crtr_sig_deliv_del AFTER DELETE ON deliveries
1006
+ FOR EACH ROW WHEN (OLD.wake = 1) EXECUTE FUNCTION crtr_signal_mail();
1007
+
1008
+ CREATE FUNCTION crtr_signal_node() RETURNS trigger LANGUAGE plpgsql AS $$
1009
+ BEGIN
1010
+ PERFORM pg_notify('crtr_signal', 'node:' || NEW.node_id);
1011
+ RETURN NEW;
1012
+ END $$;
1013
+ CREATE TRIGGER crtr_sig_node_upd AFTER UPDATE OF
1014
+ description, title, icon, mode, lifecycle, status, cwd, parent, fork_from, profile_id,
1015
+ review_binding_json, persona_ack_json, intent
1016
+ ON nodes FOR EACH ROW EXECUTE FUNCTION crtr_signal_node();
1017
+
1018
+ -- ---------------------------------------------------------------- schema version
1019
+
1020
+ INSERT INTO canvas_meta (key, value) VALUES ('schema_version', '1');
1021
+ INSERT INTO canvas_meta (key, value)
1022
+ VALUES ('memory_reads.started_at', to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'));