@consiliency/agent-board-schema 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +11 -0
  3. package/README.md +55 -0
  4. package/bin/agent-board-schema.js +97 -0
  5. package/fixtures/already-applied.json +181 -0
  6. package/fixtures/divergent-history.json +173 -0
  7. package/fixtures/production-history.json +173 -0
  8. package/migrations/20260501000001_agent_board_schema_version.sql +12 -0
  9. package/migrations/20260502000000_agent_board_schema.sql +180 -0
  10. package/migrations/20260503000000_agent_board_rpcs.sql +422 -0
  11. package/migrations/20260503000001_agent_board_coordination_rpcs.sql +229 -0
  12. package/migrations/20260504000000_agent_board_read_models.sql +206 -0
  13. package/migrations/20260505000000_agent_board_inbox_schema.sql +341 -0
  14. package/migrations/20260506000000_agent_board_inbox_delivery_rpcs.sql +1200 -0
  15. package/migrations/20260506000001_agent_board_inbox_callback_rpcs.sql +330 -0
  16. package/migrations/20260507000000_agent_board_inbox_reads.sql +334 -0
  17. package/migrations/20260508000000_agent_board_callback_runtime.sql +453 -0
  18. package/migrations/20260508000001_agent_board_inbox_service_role_reads.sql +10 -0
  19. package/migrations/20260510000000_agent_board_dblint_repairs.sql +413 -0
  20. package/migrations/20260511000000_agent_board_auth_schema.sql +146 -0
  21. package/migrations/20260511000001_agent_board_auth_credentials.sql +39 -0
  22. package/migrations/20260512000000_agent_board_auth_provisioning_rpcs.sql +622 -0
  23. package/migrations/20260512000001_agent_board_auth_v1_rpcs.sql +798 -0
  24. package/migrations/20260512000002_agent_board_auth_v2_rpcs.sql +1243 -0
  25. package/migrations/20260512000003_agent_board_auth_rls.sql +484 -0
  26. package/migrations/20260513000000_agent_board_backup_ops.sql +34 -0
  27. package/migrations/20260513000001_agent_board_retention.sql +160 -0
  28. package/migrations/20260513000002_agent_board_monitoring_views.sql +227 -0
  29. package/migrations/20260514000000_agent_board_auth_rls_id_qualification.sql +150 -0
  30. package/migrations/20260515000000_agent_board_boardopt.sql +493 -0
  31. package/migrations/20260516000000_agent_board_callbackcap.sql +113 -0
  32. package/migrations/20260517000000_agent_board_addressing.sql +542 -0
  33. package/migrations/20260628000000_agent_board_signing_store.sql +174 -0
  34. package/migrations/20260628000001_agent_board_signing_proof_rpcs.sql +215 -0
  35. package/migrations/20260628000002_agent_board_signature_rls_reads.sql +300 -0
  36. package/migrations/20260628000003_agent_board_signed_write_gateway.sql +288 -0
  37. package/migrations/20260628000004_agent_board_fleet_registry.sql +411 -0
  38. package/migrations/20260628000005_agent_board_fleet_policy.sql +509 -0
  39. package/migrations/20260628000006_agent_board_fleet_read_models.sql +170 -0
  40. package/migrations/20260629000100_agent_board_fleet_registry_repair.sql +411 -0
  41. package/migrations/20260629000101_agent_board_fleet_policy_refresh.sql +509 -0
  42. package/migrations/20260629000102_agent_board_fleet_read_models_refresh.sql +170 -0
  43. package/migrations/20260630000100_agent_board_harness_kind_open_vocab.sql +15 -0
  44. package/migrations/20260710000000_agent_board_authbound_boundary.sql +155 -0
  45. package/migrations/20260710000100_agent_board_schema_version_rpc.sql +14 -0
  46. package/migrations/20260710000400_agent_board_nonfleet_identity.sql +87 -0
  47. package/migrations/20260710000401_agent_board_workload_providers.sql +39 -0
  48. package/migrations/20260711000000_agent_board_provision_system_fail_closed.sql +242 -0
  49. package/migrations/20260711000100_canary_signed_fleet_enforcement.sql +234 -0
  50. package/migrations/20260712000000_agent_board_bundle_endpoint_compat.sql +97 -0
  51. package/migrations/20260713000000_agent_board_rls_reassert_board_scoping.sql +90 -0
  52. package/package.json +42 -0
  53. package/provenance.json +580 -0
  54. package/schema-manifest.json +458 -0
  55. package/src/check.js +23 -0
  56. package/src/index.js +50 -0
  57. package/src/materialize.js +73 -0
  58. package/src/plan.js +44 -0
  59. package/src/tooling.test.mjs +97 -0
  60. package/tests/agent_board_addressing.test.sql +166 -0
  61. package/tests/agent_board_auth_rls.test.sql +243 -0
  62. package/tests/agent_board_auth_rls_id_qualification.test.sql +225 -0
  63. package/tests/agent_board_auth_rpcs.test.sql +311 -0
  64. package/tests/agent_board_auth_schema.test.sql +411 -0
  65. package/tests/agent_board_authbound_boundary.test.sql +148 -0
  66. package/tests/agent_board_boardopt.test.sql +170 -0
  67. package/tests/agent_board_bundle_endpoint_compat.test.sql +49 -0
  68. package/tests/agent_board_callback_runtime.test.sql +425 -0
  69. package/tests/agent_board_callbackcap.test.sql +222 -0
  70. package/tests/agent_board_canary_signed_fleet_enforcement.test.sql +60 -0
  71. package/tests/agent_board_dblint_rpcs.test.sql +247 -0
  72. package/tests/agent_board_fleet_policy.test.sql +370 -0
  73. package/tests/agent_board_fleet_read_models.test.sql +158 -0
  74. package/tests/agent_board_fleet_registry.test.sql +149 -0
  75. package/tests/agent_board_inbox_reads.test.sql +471 -0
  76. package/tests/agent_board_inbox_rpcs.test.sql +452 -0
  77. package/tests/agent_board_inbox_schema.test.sql +355 -0
  78. package/tests/agent_board_nonfleet_identity.test.sql +26 -0
  79. package/tests/agent_board_rls_reassert_board_scoping.test.sql +39 -0
  80. package/tests/agent_board_schema_version.test.sql +24 -0
  81. package/tests/agent_board_schema_version_rpc.test.sql +40 -0
  82. package/tests/agent_board_signature_rls_reads.test.sql +133 -0
  83. package/tests/agent_board_signed_write_gateway.test.sql +219 -0
  84. package/tests/agent_board_signing_proof_rpcs.test.sql +143 -0
  85. package/tests/agent_board_signing_store.test.sql +183 -0
  86. package/tests/agent_board_smoke.test.sql +12 -0
  87. package/tests/agent_board_v1_invariants.test.sql +298 -0
  88. package/tests/agent_board_v2_invariants.test.sql +753 -0
  89. package/tests/agent_board_workload_providers.test.sql +15 -0
  90. package/tests/fixtures/adopter-base.sql +19 -0
  91. package/tests/fixtures/operator-hardening.sql +27 -0
  92. package/tests/fixtures/test-prereqs.sql +84 -0
@@ -0,0 +1,330 @@
1
+ create or replace function agent_board._owned_callback_endpoint(
2
+ p_endpoint_id uuid,
3
+ p_owner_agent_id uuid
4
+ )
5
+ returns agent_board.callback_endpoints
6
+ language plpgsql
7
+ security definer
8
+ set search_path = agent_board, public
9
+ as $$
10
+ declare
11
+ v_endpoint agent_board.callback_endpoints%rowtype;
12
+ begin
13
+ select *
14
+ into v_endpoint
15
+ from agent_board.callback_endpoints ce
16
+ where ce.id = p_endpoint_id
17
+ and ce.owner_agent_id = p_owner_agent_id
18
+ for update;
19
+
20
+ if v_endpoint.id is null then
21
+ raise exception 'callback endpoint not found' using errcode = 'P0002';
22
+ end if;
23
+
24
+ return v_endpoint;
25
+ end;
26
+ $$;
27
+
28
+ create or replace function agent_board.register_callback_endpoint(
29
+ p_owner_agent_id uuid,
30
+ p_endpoint_url text,
31
+ p_metadata jsonb default '{}'::jsonb
32
+ )
33
+ returns table(endpoint_id uuid, owner_agent_id uuid, endpoint_status text, signing_key_version integer, verification_requested_at timestamptz, updated_at timestamptz)
34
+ language plpgsql
35
+ security definer
36
+ set search_path = agent_board, public
37
+ as $$
38
+ begin
39
+ if not exists (select 1 from agent_board.agents where id = p_owner_agent_id) then
40
+ raise exception 'owner agent not found' using errcode = 'P0002';
41
+ end if;
42
+ if nullif(trim(p_endpoint_url), '') is null or trim(p_endpoint_url) !~ '^https://.+$' then
43
+ raise exception 'callback endpoint URL must be https' using errcode = '22023';
44
+ end if;
45
+
46
+ return query
47
+ insert into agent_board.callback_endpoints as ce(
48
+ owner_agent_id,
49
+ endpoint_url,
50
+ endpoint_status,
51
+ verification_requested_at,
52
+ verified_at,
53
+ disabled_at,
54
+ disabled_reason,
55
+ metadata
56
+ )
57
+ values (
58
+ p_owner_agent_id,
59
+ trim(p_endpoint_url),
60
+ 'pending_verification',
61
+ now(),
62
+ null,
63
+ null,
64
+ null,
65
+ coalesce(p_metadata, '{}'::jsonb)
66
+ )
67
+ on conflict on constraint callback_endpoints_url_unique do update
68
+ set endpoint_status = 'pending_verification',
69
+ verification_requested_at = now(),
70
+ verified_at = null,
71
+ disabled_at = null,
72
+ disabled_reason = null,
73
+ metadata = coalesce(ce.metadata, '{}'::jsonb) || coalesce(excluded.metadata, '{}'::jsonb),
74
+ updated_at = now()
75
+ returning ce.id, ce.owner_agent_id, ce.endpoint_status, ce.signing_key_version, ce.verification_requested_at, ce.updated_at;
76
+ end;
77
+ $$;
78
+
79
+ create or replace function agent_board.verify_callback_endpoint(
80
+ p_endpoint_id uuid,
81
+ p_owner_agent_id uuid,
82
+ p_metadata jsonb default '{}'::jsonb
83
+ )
84
+ returns table(endpoint_id uuid, owner_agent_id uuid, endpoint_status text, verified_at timestamptz, updated_at timestamptz)
85
+ language plpgsql
86
+ security definer
87
+ set search_path = agent_board, public
88
+ as $$
89
+ declare
90
+ v_endpoint agent_board.callback_endpoints%rowtype;
91
+ begin
92
+ v_endpoint := agent_board._owned_callback_endpoint(p_endpoint_id, p_owner_agent_id);
93
+
94
+ update agent_board.callback_endpoints ce
95
+ set endpoint_status = 'verified',
96
+ verified_at = now(),
97
+ disabled_at = null,
98
+ disabled_reason = null,
99
+ metadata = coalesce(ce.metadata, '{}'::jsonb) || coalesce(p_metadata, '{}'::jsonb),
100
+ updated_at = now()
101
+ where ce.id = v_endpoint.id
102
+ returning * into v_endpoint;
103
+
104
+ return query
105
+ select v_endpoint.id, v_endpoint.owner_agent_id, v_endpoint.endpoint_status, v_endpoint.verified_at, v_endpoint.updated_at;
106
+ end;
107
+ $$;
108
+
109
+ create or replace function agent_board.disable_callback_endpoint(
110
+ p_endpoint_id uuid,
111
+ p_owner_agent_id uuid,
112
+ p_disabled_reason text default null,
113
+ p_metadata jsonb default '{}'::jsonb
114
+ )
115
+ returns table(endpoint_id uuid, owner_agent_id uuid, endpoint_status text, disabled_at timestamptz, updated_at timestamptz)
116
+ language plpgsql
117
+ security definer
118
+ set search_path = agent_board, public
119
+ as $$
120
+ declare
121
+ v_endpoint agent_board.callback_endpoints%rowtype;
122
+ begin
123
+ v_endpoint := agent_board._owned_callback_endpoint(p_endpoint_id, p_owner_agent_id);
124
+
125
+ update agent_board.callback_endpoints ce
126
+ set endpoint_status = 'disabled',
127
+ disabled_at = now(),
128
+ disabled_reason = nullif(trim(p_disabled_reason), ''),
129
+ metadata = coalesce(ce.metadata, '{}'::jsonb) || coalesce(p_metadata, '{}'::jsonb),
130
+ updated_at = now()
131
+ where ce.id = v_endpoint.id
132
+ returning * into v_endpoint;
133
+
134
+ update agent_board.callback_subscriptions cs
135
+ set subscription_status = 'disabled',
136
+ disabled_at = now(),
137
+ updated_at = now()
138
+ where cs.endpoint_id = v_endpoint.id
139
+ and cs.subscription_status = 'active';
140
+
141
+ return query
142
+ select v_endpoint.id, v_endpoint.owner_agent_id, v_endpoint.endpoint_status, v_endpoint.disabled_at, v_endpoint.updated_at;
143
+ end;
144
+ $$;
145
+
146
+ create or replace function agent_board.rotate_callback_signing_key(
147
+ p_endpoint_id uuid,
148
+ p_owner_agent_id uuid,
149
+ p_metadata jsonb default '{}'::jsonb
150
+ )
151
+ returns table(endpoint_id uuid, owner_agent_id uuid, signing_key_version integer, endpoint_status text, verification_requested_at timestamptz, updated_at timestamptz)
152
+ language plpgsql
153
+ security definer
154
+ set search_path = agent_board, public
155
+ as $$
156
+ declare
157
+ v_endpoint agent_board.callback_endpoints%rowtype;
158
+ begin
159
+ v_endpoint := agent_board._owned_callback_endpoint(p_endpoint_id, p_owner_agent_id);
160
+
161
+ update agent_board.callback_endpoints ce
162
+ set signing_key_version = ce.signing_key_version + 1,
163
+ endpoint_status = 'pending_verification',
164
+ verification_requested_at = now(),
165
+ verified_at = null,
166
+ disabled_at = null,
167
+ disabled_reason = null,
168
+ metadata = coalesce(ce.metadata, '{}'::jsonb) || coalesce(p_metadata, '{}'::jsonb),
169
+ updated_at = now()
170
+ where ce.id = v_endpoint.id
171
+ returning * into v_endpoint;
172
+
173
+ return query
174
+ select v_endpoint.id, v_endpoint.owner_agent_id, v_endpoint.signing_key_version, v_endpoint.endpoint_status, v_endpoint.verification_requested_at, v_endpoint.updated_at;
175
+ end;
176
+ $$;
177
+
178
+ create or replace function agent_board.delete_callback_endpoint(
179
+ p_endpoint_id uuid,
180
+ p_owner_agent_id uuid
181
+ )
182
+ returns table(endpoint_id uuid, owner_agent_id uuid, deleted boolean)
183
+ language plpgsql
184
+ security definer
185
+ set search_path = agent_board, public
186
+ as $$
187
+ declare
188
+ v_endpoint_id uuid;
189
+ begin
190
+ delete from agent_board.callback_endpoints ce
191
+ where ce.id = p_endpoint_id
192
+ and ce.owner_agent_id = p_owner_agent_id
193
+ returning id into v_endpoint_id;
194
+
195
+ if v_endpoint_id is null then
196
+ raise exception 'callback endpoint not found' using errcode = 'P0002';
197
+ end if;
198
+
199
+ return query
200
+ select v_endpoint_id, p_owner_agent_id, true;
201
+ end;
202
+ $$;
203
+
204
+ create or replace function agent_board.subscribe_callback_endpoint(
205
+ p_endpoint_id uuid,
206
+ p_agent_id uuid,
207
+ p_subscription_scope text,
208
+ p_event_category text,
209
+ p_board_id uuid default null,
210
+ p_metadata jsonb default '{}'::jsonb
211
+ )
212
+ returns table(subscription_id uuid, endpoint_id uuid, agent_id uuid, subscription_scope text, event_category text, board_id uuid, subscription_status text, updated_at timestamptz)
213
+ language plpgsql
214
+ security definer
215
+ set search_path = agent_board, public
216
+ as $$
217
+ declare
218
+ v_endpoint agent_board.callback_endpoints%rowtype;
219
+ v_subscription agent_board.callback_subscriptions%rowtype;
220
+ v_board_membership_id uuid;
221
+ begin
222
+ v_endpoint := agent_board._owned_callback_endpoint(p_endpoint_id, p_agent_id);
223
+
224
+ if p_subscription_scope = 'own_inbox' then
225
+ if p_event_category not in ('inbox.item.delivered', 'inbox.item.updated') or p_board_id is not null then
226
+ raise exception 'invalid own_inbox subscription shape' using errcode = '22023';
227
+ end if;
228
+ elsif p_subscription_scope = 'board' then
229
+ if p_event_category not in ('board.post.delivered', 'board.post.updated') or p_board_id is null then
230
+ raise exception 'invalid board subscription shape' using errcode = '22023';
231
+ end if;
232
+ v_board_membership_id := agent_board._active_board_membership_id(p_board_id, p_agent_id);
233
+ if v_board_membership_id is null then
234
+ raise exception 'board membership not found' using errcode = 'P0002';
235
+ end if;
236
+ else
237
+ raise exception 'invalid subscription scope' using errcode = '22023';
238
+ end if;
239
+
240
+ select *
241
+ into v_subscription
242
+ from agent_board.callback_subscriptions cs
243
+ where cs.endpoint_id = p_endpoint_id
244
+ and cs.agent_id = p_agent_id
245
+ and cs.subscription_scope = p_subscription_scope
246
+ and cs.event_category = p_event_category
247
+ and coalesce(cs.board_id, '00000000-0000-0000-0000-000000000000'::uuid) = coalesce(p_board_id, '00000000-0000-0000-0000-000000000000'::uuid)
248
+ order by cs.created_at desc
249
+ limit 1
250
+ for update;
251
+
252
+ if v_subscription.id is null then
253
+ insert into agent_board.callback_subscriptions(
254
+ endpoint_id,
255
+ agent_id,
256
+ subscription_scope,
257
+ event_category,
258
+ board_id,
259
+ board_membership_id,
260
+ metadata
261
+ )
262
+ values (
263
+ v_endpoint.id,
264
+ p_agent_id,
265
+ p_subscription_scope,
266
+ p_event_category,
267
+ p_board_id,
268
+ v_board_membership_id,
269
+ coalesce(p_metadata, '{}'::jsonb)
270
+ )
271
+ returning * into v_subscription;
272
+ else
273
+ update agent_board.callback_subscriptions
274
+ set subscription_status = 'active',
275
+ disabled_at = null,
276
+ board_membership_id = v_board_membership_id,
277
+ metadata = coalesce(callback_subscriptions.metadata, '{}'::jsonb) || coalesce(p_metadata, '{}'::jsonb),
278
+ updated_at = now()
279
+ where id = v_subscription.id
280
+ returning * into v_subscription;
281
+ end if;
282
+
283
+ return query
284
+ select v_subscription.id, v_subscription.endpoint_id, v_subscription.agent_id, v_subscription.subscription_scope, v_subscription.event_category, v_subscription.board_id, v_subscription.subscription_status, v_subscription.updated_at;
285
+ end;
286
+ $$;
287
+
288
+ create or replace function agent_board.unsubscribe_callback_endpoint(
289
+ p_endpoint_id uuid,
290
+ p_agent_id uuid,
291
+ p_subscription_scope text,
292
+ p_event_category text,
293
+ p_board_id uuid default null,
294
+ p_metadata jsonb default '{}'::jsonb
295
+ )
296
+ returns table(subscription_id uuid, endpoint_id uuid, agent_id uuid, subscription_scope text, event_category text, board_id uuid, subscription_status text, disabled_at timestamptz, updated_at timestamptz)
297
+ language plpgsql
298
+ security definer
299
+ set search_path = agent_board, public
300
+ as $$
301
+ declare
302
+ v_endpoint agent_board.callback_endpoints%rowtype;
303
+ v_subscription agent_board.callback_subscriptions%rowtype;
304
+ begin
305
+ v_endpoint := agent_board._owned_callback_endpoint(p_endpoint_id, p_agent_id);
306
+
307
+ update agent_board.callback_subscriptions cs
308
+ set subscription_status = 'disabled',
309
+ disabled_at = now(),
310
+ metadata = coalesce(cs.metadata, '{}'::jsonb) || coalesce(p_metadata, '{}'::jsonb),
311
+ updated_at = now()
312
+ where cs.endpoint_id = v_endpoint.id
313
+ and cs.agent_id = p_agent_id
314
+ and cs.subscription_scope = p_subscription_scope
315
+ and cs.event_category = p_event_category
316
+ and coalesce(cs.board_id, '00000000-0000-0000-0000-000000000000'::uuid) = coalesce(p_board_id, '00000000-0000-0000-0000-000000000000'::uuid)
317
+ and cs.subscription_status = 'active'
318
+ returning * into v_subscription;
319
+
320
+ if v_subscription.id is null then
321
+ raise exception 'callback subscription not found' using errcode = 'P0002';
322
+ end if;
323
+
324
+ return query
325
+ select v_subscription.id, v_subscription.endpoint_id, v_subscription.agent_id, v_subscription.subscription_scope, v_subscription.event_category, v_subscription.board_id, v_subscription.subscription_status, v_subscription.disabled_at, v_subscription.updated_at;
326
+ end;
327
+ $$;
328
+
329
+ revoke all on all functions in schema agent_board from public, anon, authenticated;
330
+ grant execute on all functions in schema agent_board to service_role;
@@ -0,0 +1,334 @@
1
+ create or replace view agent_board.v_agent_inbox
2
+ with (security_invoker = true)
3
+ as
4
+ select
5
+ ii.id as inbox_item_id,
6
+ ii.delivery_recipient_id,
7
+ ii.recipient_agent_id,
8
+ recipient.handle as recipient_handle,
9
+ recipient.display_name as recipient_display_name,
10
+ ii.board_id,
11
+ b.slug as board_slug,
12
+ b.name as board_name,
13
+ ii.delivery_mode,
14
+ ii.source_kind,
15
+ ii.source_message_id,
16
+ ii.source_task_id,
17
+ ii.source_delegation_id,
18
+ ii.subject,
19
+ ii.preview,
20
+ ii.priority,
21
+ ii.action_state,
22
+ ii.visible_in_board_feed,
23
+ ii.fetch_descriptor,
24
+ ii.created_at,
25
+ ii.updated_at
26
+ from agent_board.inbox_items ii
27
+ join agent_board.agents recipient on recipient.id = ii.recipient_agent_id
28
+ left join agent_board.boards b on b.id = ii.board_id
29
+ where ii.visible_in_inbox = true;
30
+
31
+ create or replace view agent_board.v_agent_inbox_counts
32
+ with (security_invoker = true)
33
+ as
34
+ select
35
+ ii.recipient_agent_id,
36
+ count(*) filter (where ii.action_state in ('new', 'restored'))::bigint as unread_count,
37
+ count(*) filter (where ii.action_state in ('new', 'restored', 'deferred'))::bigint as pending_action_count,
38
+ count(*)::bigint as total_visible_count,
39
+ max(ii.updated_at) as latest_inbox_updated_at
40
+ from agent_board.inbox_items ii
41
+ where ii.visible_in_inbox = true
42
+ group by ii.recipient_agent_id;
43
+
44
+ create or replace view agent_board.v_board_directory
45
+ with (security_invoker = true)
46
+ as
47
+ select
48
+ b.id as board_id,
49
+ b.slug as board_slug,
50
+ b.name as board_name,
51
+ b.description as board_description,
52
+ b.status as board_status,
53
+ b.created_by_agent_id,
54
+ creator.handle as created_by_handle,
55
+ coalesce(members.active_member_count, 0)::bigint as active_member_count,
56
+ coalesce(board_activity.latest_board_activity_at, b.updated_at) as latest_board_activity_at,
57
+ b.created_at,
58
+ b.updated_at
59
+ from agent_board.boards b
60
+ join agent_board.agents creator on creator.id = b.created_by_agent_id
61
+ left join lateral (
62
+ select count(*) as active_member_count
63
+ from agent_board.board_memberships bm
64
+ where bm.board_id = b.id
65
+ and bm.membership_state in ('joined', 'subscribed')
66
+ ) members on true
67
+ left join lateral (
68
+ select max(ii.created_at) as latest_board_activity_at
69
+ from agent_board.inbox_items ii
70
+ where ii.board_id = b.id
71
+ and ii.visible_in_board_feed = true
72
+ ) board_activity on true;
73
+
74
+ create or replace view agent_board.v_board_memberships
75
+ with (security_invoker = true)
76
+ as
77
+ select
78
+ bm.id as board_membership_id,
79
+ bm.board_id,
80
+ b.slug as board_slug,
81
+ b.name as board_name,
82
+ bm.agent_id,
83
+ a.handle as agent_handle,
84
+ a.display_name as agent_display_name,
85
+ bm.membership_state,
86
+ bm.created_by_agent_id,
87
+ creator.handle as created_by_handle,
88
+ bm.left_at,
89
+ bm.created_at,
90
+ bm.updated_at
91
+ from agent_board.board_memberships bm
92
+ join agent_board.boards b on b.id = bm.board_id
93
+ join agent_board.agents a on a.id = bm.agent_id
94
+ left join agent_board.agents creator on creator.id = bm.created_by_agent_id;
95
+
96
+ create or replace view agent_board.v_board_feed
97
+ with (security_invoker = true)
98
+ as
99
+ with board_posts as (
100
+ select distinct on (dr.board_id, dr.source_message_id)
101
+ dr.board_id,
102
+ dr.source_message_id as message_id,
103
+ min(ii.created_at) over (partition by dr.board_id, dr.source_message_id) as first_delivered_at,
104
+ ii.subject,
105
+ ii.preview,
106
+ ii.priority,
107
+ ii.fetch_descriptor
108
+ from agent_board.delivery_recipients dr
109
+ join agent_board.inbox_items ii on ii.delivery_recipient_id = dr.id
110
+ where dr.board_id is not null
111
+ and dr.source_kind = 'message'
112
+ and dr.source_message_id is not null
113
+ and ii.visible_in_board_feed = true
114
+ order by dr.board_id, dr.source_message_id, ii.created_at asc, ii.id asc
115
+ )
116
+ select
117
+ bp.board_id,
118
+ b.slug as board_slug,
119
+ b.name as board_name,
120
+ bp.message_id,
121
+ m.thread_id,
122
+ m.message_sequence,
123
+ m.created_at as message_created_at,
124
+ m.author_agent_id,
125
+ author.handle as author_handle,
126
+ author.display_name as author_display_name,
127
+ bp.subject,
128
+ bp.preview,
129
+ bp.priority,
130
+ bp.fetch_descriptor,
131
+ bp.first_delivered_at
132
+ from board_posts bp
133
+ join agent_board.boards b on b.id = bp.board_id
134
+ join agent_board.messages m on m.id = bp.message_id
135
+ join agent_board.agents author on author.id = m.author_agent_id;
136
+
137
+ create or replace view agent_board.v_callback_endpoint_status
138
+ with (security_invoker = true)
139
+ as
140
+ select
141
+ ce.id as endpoint_id,
142
+ ce.owner_agent_id,
143
+ owner.handle as owner_agent_handle,
144
+ owner.display_name as owner_agent_display_name,
145
+ ce.endpoint_status,
146
+ ce.signing_key_version,
147
+ ce.verification_requested_at,
148
+ ce.verified_at,
149
+ ce.disabled_at,
150
+ ce.disabled_reason,
151
+ ce.last_delivery_at,
152
+ ce.last_failure_at,
153
+ coalesce(active_subscriptions.active_subscription_count, 0)::bigint as active_subscription_count,
154
+ ce.created_at,
155
+ ce.updated_at
156
+ from agent_board.callback_endpoints ce
157
+ join agent_board.agents owner on owner.id = ce.owner_agent_id
158
+ left join lateral (
159
+ select count(*) as active_subscription_count
160
+ from agent_board.callback_subscriptions cs
161
+ where cs.endpoint_id = ce.id
162
+ and cs.subscription_status = 'active'
163
+ ) active_subscriptions on true;
164
+
165
+ create or replace view agent_board.v_callback_subscriptions
166
+ with (security_invoker = true)
167
+ as
168
+ select
169
+ cs.id as subscription_id,
170
+ cs.endpoint_id,
171
+ ce.owner_agent_id,
172
+ owner.handle as owner_agent_handle,
173
+ cs.agent_id,
174
+ agent.handle as agent_handle,
175
+ cs.subscription_scope,
176
+ cs.event_category,
177
+ cs.board_id,
178
+ b.slug as board_slug,
179
+ b.name as board_name,
180
+ cs.board_membership_id,
181
+ cs.subscription_status,
182
+ cs.disabled_at,
183
+ cs.created_at,
184
+ cs.updated_at
185
+ from agent_board.callback_subscriptions cs
186
+ join agent_board.callback_endpoints ce on ce.id = cs.endpoint_id
187
+ join agent_board.agents owner on owner.id = ce.owner_agent_id
188
+ join agent_board.agents agent on agent.id = cs.agent_id
189
+ left join agent_board.boards b on b.id = cs.board_id;
190
+
191
+ create or replace view agent_board.v_callback_delivery_health
192
+ with (security_invoker = true)
193
+ as
194
+ select
195
+ cs.id as subscription_id,
196
+ cs.endpoint_id,
197
+ ce.owner_agent_id,
198
+ owner.handle as owner_agent_handle,
199
+ cs.agent_id,
200
+ agent.handle as agent_handle,
201
+ cs.subscription_scope,
202
+ cs.event_category,
203
+ cs.board_id,
204
+ b.slug as board_slug,
205
+ cs.subscription_status,
206
+ latest_outbox.id as latest_outbox_event_id,
207
+ latest_outbox.outbox_status as latest_outbox_status,
208
+ latest_outbox.delivery_mode as latest_delivery_mode,
209
+ latest_outbox.source_kind as latest_source_kind,
210
+ latest_attempt.attempt_number as latest_attempt_number,
211
+ latest_attempt.attempt_outcome as latest_attempt_outcome,
212
+ latest_attempt.http_status as latest_http_status,
213
+ latest_attempt.error_summary as latest_error_summary,
214
+ latest_attempt.started_at as latest_attempt_started_at,
215
+ latest_attempt.finished_at as latest_attempt_finished_at,
216
+ latest_attempt.next_retry_at as latest_next_retry_at,
217
+ ce.last_delivery_at,
218
+ ce.last_failure_at,
219
+ cs.created_at,
220
+ cs.updated_at
221
+ from agent_board.callback_subscriptions cs
222
+ join agent_board.callback_endpoints ce on ce.id = cs.endpoint_id
223
+ join agent_board.agents owner on owner.id = ce.owner_agent_id
224
+ join agent_board.agents agent on agent.id = cs.agent_id
225
+ left join agent_board.boards b on b.id = cs.board_id
226
+ left join lateral (
227
+ select coe.id, coe.outbox_status, coe.delivery_mode, coe.source_kind, coe.created_at
228
+ from agent_board.callback_outbox_events coe
229
+ where coe.subscription_id = cs.id
230
+ order by coe.created_at desc, coe.id desc
231
+ limit 1
232
+ ) latest_outbox on true
233
+ left join lateral (
234
+ select cda.attempt_number, cda.attempt_outcome, cda.http_status, cda.error_summary, cda.started_at, cda.finished_at, cda.next_retry_at
235
+ from agent_board.callback_delivery_attempts cda
236
+ where cda.outbox_event_id = latest_outbox.id
237
+ order by cda.attempt_number desc
238
+ limit 1
239
+ ) latest_attempt on true;
240
+
241
+ create or replace view agent_board.v_agent_dashboard
242
+ with (security_invoker = true)
243
+ as
244
+ select
245
+ a.id as agent_id,
246
+ a.handle,
247
+ a.display_name,
248
+ a.status as participant_status,
249
+ p.availability,
250
+ p.thread_id as presence_thread_id,
251
+ p.last_heartbeat_at,
252
+ p.expires_at as presence_expires_at,
253
+ coalesce(open_tasks.open_task_count, 0)::bigint as open_task_count,
254
+ coalesce(active_claims.active_claim_count, 0)::bigint as active_claim_count,
255
+ coalesce(open_escalations.assigned_open_escalation_count, 0)::bigint as assigned_open_escalation_count,
256
+ latest.task_id as latest_task_id,
257
+ latest.event_sequence as latest_task_event_sequence,
258
+ latest.event_type as latest_task_event_type,
259
+ latest.payload->>'kind' as latest_task_event_kind,
260
+ latest.updated_at as latest_task_updated_at,
261
+ coalesce(inbox_counts.inbox_unread_count, 0)::bigint as inbox_unread_count,
262
+ coalesce(inbox_counts.inbox_pending_action_count, 0)::bigint as inbox_pending_action_count
263
+ from agent_board.agents a
264
+ left join agent_board.presence p on p.agent_id = a.id
265
+ left join lateral (
266
+ select count(*) as open_task_count
267
+ from agent_board.tasks t
268
+ where t.assigned_agent_id = a.id
269
+ and t.status in ('pending', 'in_progress', 'blocked')
270
+ ) open_tasks on true
271
+ left join lateral (
272
+ select count(*) as active_claim_count
273
+ from agent_board.claims c
274
+ where c.agent_id = a.id
275
+ and c.status = 'active'
276
+ and c.released_at is null
277
+ and c.lease_expires_at > now()
278
+ ) active_claims on true
279
+ left join lateral (
280
+ select count(*) as assigned_open_escalation_count
281
+ from agent_board.escalations e
282
+ where e.assigned_agent_id = a.id
283
+ and e.status in ('open', 'acknowledged')
284
+ ) open_escalations on true
285
+ left join lateral (
286
+ select
287
+ count(*) filter (where ii.action_state in ('new', 'restored')) as inbox_unread_count,
288
+ count(*) filter (where ii.action_state in ('new', 'restored', 'deferred')) as inbox_pending_action_count
289
+ from agent_board.inbox_items ii
290
+ where ii.recipient_agent_id = a.id
291
+ and ii.visible_in_inbox = true
292
+ ) inbox_counts on true
293
+ left join lateral (
294
+ select t.id as task_id, te.event_sequence, te.event_type, te.payload, t.updated_at
295
+ from agent_board.tasks t
296
+ left join agent_board.task_events te on te.task_id = t.id
297
+ where t.assigned_agent_id = a.id or t.created_by_agent_id = a.id
298
+ order by t.updated_at desc, te.event_sequence desc nulls last
299
+ limit 1
300
+ ) latest on true;
301
+
302
+ grant select on agent_board.v_agent_inbox,
303
+ agent_board.v_agent_inbox_counts,
304
+ agent_board.v_board_directory,
305
+ agent_board.v_board_memberships,
306
+ agent_board.v_board_feed,
307
+ agent_board.v_callback_endpoint_status,
308
+ agent_board.v_callback_subscriptions,
309
+ agent_board.v_callback_delivery_health,
310
+ agent_board.v_agent_dashboard
311
+ to authenticated;
312
+
313
+ do $$
314
+ declare
315
+ v_table text;
316
+ begin
317
+ foreach v_table in array array[
318
+ 'boards',
319
+ 'board_memberships',
320
+ 'inbox_items',
321
+ 'callback_endpoints',
322
+ 'callback_subscriptions',
323
+ 'callback_delivery_attempts'
324
+ ]
325
+ loop
326
+ begin
327
+ execute format('alter publication supabase_realtime add table agent_board.%I', v_table);
328
+ exception
329
+ when duplicate_object then null;
330
+ when undefined_object then null;
331
+ end;
332
+ end loop;
333
+ end;
334
+ $$;