@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.
- package/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +55 -0
- package/bin/agent-board-schema.js +97 -0
- package/fixtures/already-applied.json +181 -0
- package/fixtures/divergent-history.json +173 -0
- package/fixtures/production-history.json +173 -0
- package/migrations/20260501000001_agent_board_schema_version.sql +12 -0
- package/migrations/20260502000000_agent_board_schema.sql +180 -0
- package/migrations/20260503000000_agent_board_rpcs.sql +422 -0
- package/migrations/20260503000001_agent_board_coordination_rpcs.sql +229 -0
- package/migrations/20260504000000_agent_board_read_models.sql +206 -0
- package/migrations/20260505000000_agent_board_inbox_schema.sql +341 -0
- package/migrations/20260506000000_agent_board_inbox_delivery_rpcs.sql +1200 -0
- package/migrations/20260506000001_agent_board_inbox_callback_rpcs.sql +330 -0
- package/migrations/20260507000000_agent_board_inbox_reads.sql +334 -0
- package/migrations/20260508000000_agent_board_callback_runtime.sql +453 -0
- package/migrations/20260508000001_agent_board_inbox_service_role_reads.sql +10 -0
- package/migrations/20260510000000_agent_board_dblint_repairs.sql +413 -0
- package/migrations/20260511000000_agent_board_auth_schema.sql +146 -0
- package/migrations/20260511000001_agent_board_auth_credentials.sql +39 -0
- package/migrations/20260512000000_agent_board_auth_provisioning_rpcs.sql +622 -0
- package/migrations/20260512000001_agent_board_auth_v1_rpcs.sql +798 -0
- package/migrations/20260512000002_agent_board_auth_v2_rpcs.sql +1243 -0
- package/migrations/20260512000003_agent_board_auth_rls.sql +484 -0
- package/migrations/20260513000000_agent_board_backup_ops.sql +34 -0
- package/migrations/20260513000001_agent_board_retention.sql +160 -0
- package/migrations/20260513000002_agent_board_monitoring_views.sql +227 -0
- package/migrations/20260514000000_agent_board_auth_rls_id_qualification.sql +150 -0
- package/migrations/20260515000000_agent_board_boardopt.sql +493 -0
- package/migrations/20260516000000_agent_board_callbackcap.sql +113 -0
- package/migrations/20260517000000_agent_board_addressing.sql +542 -0
- package/migrations/20260628000000_agent_board_signing_store.sql +174 -0
- package/migrations/20260628000001_agent_board_signing_proof_rpcs.sql +215 -0
- package/migrations/20260628000002_agent_board_signature_rls_reads.sql +300 -0
- package/migrations/20260628000003_agent_board_signed_write_gateway.sql +288 -0
- package/migrations/20260628000004_agent_board_fleet_registry.sql +411 -0
- package/migrations/20260628000005_agent_board_fleet_policy.sql +509 -0
- package/migrations/20260628000006_agent_board_fleet_read_models.sql +170 -0
- package/migrations/20260629000100_agent_board_fleet_registry_repair.sql +411 -0
- package/migrations/20260629000101_agent_board_fleet_policy_refresh.sql +509 -0
- package/migrations/20260629000102_agent_board_fleet_read_models_refresh.sql +170 -0
- package/migrations/20260630000100_agent_board_harness_kind_open_vocab.sql +15 -0
- package/migrations/20260710000000_agent_board_authbound_boundary.sql +155 -0
- package/migrations/20260710000100_agent_board_schema_version_rpc.sql +14 -0
- package/migrations/20260710000400_agent_board_nonfleet_identity.sql +87 -0
- package/migrations/20260710000401_agent_board_workload_providers.sql +39 -0
- package/migrations/20260711000000_agent_board_provision_system_fail_closed.sql +242 -0
- package/migrations/20260711000100_canary_signed_fleet_enforcement.sql +234 -0
- package/migrations/20260712000000_agent_board_bundle_endpoint_compat.sql +97 -0
- package/migrations/20260713000000_agent_board_rls_reassert_board_scoping.sql +90 -0
- package/package.json +42 -0
- package/provenance.json +580 -0
- package/schema-manifest.json +458 -0
- package/src/check.js +23 -0
- package/src/index.js +50 -0
- package/src/materialize.js +73 -0
- package/src/plan.js +44 -0
- package/src/tooling.test.mjs +97 -0
- package/tests/agent_board_addressing.test.sql +166 -0
- package/tests/agent_board_auth_rls.test.sql +243 -0
- package/tests/agent_board_auth_rls_id_qualification.test.sql +225 -0
- package/tests/agent_board_auth_rpcs.test.sql +311 -0
- package/tests/agent_board_auth_schema.test.sql +411 -0
- package/tests/agent_board_authbound_boundary.test.sql +148 -0
- package/tests/agent_board_boardopt.test.sql +170 -0
- package/tests/agent_board_bundle_endpoint_compat.test.sql +49 -0
- package/tests/agent_board_callback_runtime.test.sql +425 -0
- package/tests/agent_board_callbackcap.test.sql +222 -0
- package/tests/agent_board_canary_signed_fleet_enforcement.test.sql +60 -0
- package/tests/agent_board_dblint_rpcs.test.sql +247 -0
- package/tests/agent_board_fleet_policy.test.sql +370 -0
- package/tests/agent_board_fleet_read_models.test.sql +158 -0
- package/tests/agent_board_fleet_registry.test.sql +149 -0
- package/tests/agent_board_inbox_reads.test.sql +471 -0
- package/tests/agent_board_inbox_rpcs.test.sql +452 -0
- package/tests/agent_board_inbox_schema.test.sql +355 -0
- package/tests/agent_board_nonfleet_identity.test.sql +26 -0
- package/tests/agent_board_rls_reassert_board_scoping.test.sql +39 -0
- package/tests/agent_board_schema_version.test.sql +24 -0
- package/tests/agent_board_schema_version_rpc.test.sql +40 -0
- package/tests/agent_board_signature_rls_reads.test.sql +133 -0
- package/tests/agent_board_signed_write_gateway.test.sql +219 -0
- package/tests/agent_board_signing_proof_rpcs.test.sql +143 -0
- package/tests/agent_board_signing_store.test.sql +183 -0
- package/tests/agent_board_smoke.test.sql +12 -0
- package/tests/agent_board_v1_invariants.test.sql +298 -0
- package/tests/agent_board_v2_invariants.test.sql +753 -0
- package/tests/agent_board_workload_providers.test.sql +15 -0
- package/tests/fixtures/adopter-base.sql +19 -0
- package/tests/fixtures/operator-hardening.sql +27 -0
- package/tests/fixtures/test-prereqs.sql +84 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
create or replace view agent_board.v_thread_feed
|
|
2
|
+
with (security_invoker = true)
|
|
3
|
+
as
|
|
4
|
+
select
|
|
5
|
+
m.thread_id,
|
|
6
|
+
m.id as message_id,
|
|
7
|
+
m.message_sequence,
|
|
8
|
+
m.created_at as message_created_at,
|
|
9
|
+
m.author_agent_id,
|
|
10
|
+
a.handle as author_handle,
|
|
11
|
+
a.display_name as author_display_name,
|
|
12
|
+
m.message_type,
|
|
13
|
+
m.body,
|
|
14
|
+
m.task_id
|
|
15
|
+
from agent_board.messages m
|
|
16
|
+
join agent_board.agents a on a.id = m.author_agent_id;
|
|
17
|
+
|
|
18
|
+
create or replace view agent_board.v_task_tree
|
|
19
|
+
with (security_invoker = true)
|
|
20
|
+
as
|
|
21
|
+
select
|
|
22
|
+
coalesce(t.root_task_id, t.id) as root_task_id,
|
|
23
|
+
t.id as task_id,
|
|
24
|
+
t.parent_task_id,
|
|
25
|
+
t.thread_id,
|
|
26
|
+
t.created_by_agent_id,
|
|
27
|
+
t.assigned_agent_id,
|
|
28
|
+
assigned.handle as assigned_agent_handle,
|
|
29
|
+
t.status,
|
|
30
|
+
t.priority,
|
|
31
|
+
t.orchestration_mode,
|
|
32
|
+
t.title,
|
|
33
|
+
t.details,
|
|
34
|
+
t.created_at,
|
|
35
|
+
t.updated_at,
|
|
36
|
+
t.completed_at,
|
|
37
|
+
latest.event_sequence as latest_event_sequence,
|
|
38
|
+
latest.event_type as latest_event_type,
|
|
39
|
+
latest.payload->>'kind' as latest_event_kind
|
|
40
|
+
from agent_board.tasks t
|
|
41
|
+
left join agent_board.agents assigned on assigned.id = t.assigned_agent_id
|
|
42
|
+
left join lateral (
|
|
43
|
+
select te.event_sequence, te.event_type, te.payload
|
|
44
|
+
from agent_board.task_events te
|
|
45
|
+
where te.task_id = t.id
|
|
46
|
+
order by te.event_sequence desc
|
|
47
|
+
limit 1
|
|
48
|
+
) latest on true;
|
|
49
|
+
|
|
50
|
+
create or replace view agent_board.v_active_claims
|
|
51
|
+
with (security_invoker = true)
|
|
52
|
+
as
|
|
53
|
+
select
|
|
54
|
+
c.id as claim_id,
|
|
55
|
+
c.task_id,
|
|
56
|
+
c.thread_id,
|
|
57
|
+
c.agent_id,
|
|
58
|
+
a.handle as agent_handle,
|
|
59
|
+
c.resource_kind,
|
|
60
|
+
c.resource_key,
|
|
61
|
+
c.lease_token,
|
|
62
|
+
c.lease_expires_at,
|
|
63
|
+
c.created_at,
|
|
64
|
+
c.updated_at,
|
|
65
|
+
t.status as task_status
|
|
66
|
+
from agent_board.claims c
|
|
67
|
+
join agent_board.agents a on a.id = c.agent_id
|
|
68
|
+
left join agent_board.tasks t on t.id = c.task_id
|
|
69
|
+
where c.status = 'active'
|
|
70
|
+
and c.released_at is null
|
|
71
|
+
and c.lease_expires_at > now();
|
|
72
|
+
|
|
73
|
+
create or replace view agent_board.v_open_escalations
|
|
74
|
+
with (security_invoker = true)
|
|
75
|
+
as
|
|
76
|
+
select
|
|
77
|
+
e.id as escalation_id,
|
|
78
|
+
e.thread_id,
|
|
79
|
+
e.task_id,
|
|
80
|
+
e.raised_by_agent_id,
|
|
81
|
+
raised.handle as raised_by_handle,
|
|
82
|
+
e.assigned_agent_id,
|
|
83
|
+
assigned.handle as assigned_agent_handle,
|
|
84
|
+
e.escalation_type,
|
|
85
|
+
e.severity,
|
|
86
|
+
e.status,
|
|
87
|
+
e.summary,
|
|
88
|
+
e.details,
|
|
89
|
+
e.created_at,
|
|
90
|
+
e.updated_at,
|
|
91
|
+
e.acknowledged_at,
|
|
92
|
+
t.status as task_status
|
|
93
|
+
from agent_board.escalations e
|
|
94
|
+
join agent_board.agents raised on raised.id = e.raised_by_agent_id
|
|
95
|
+
left join agent_board.agents assigned on assigned.id = e.assigned_agent_id
|
|
96
|
+
left join agent_board.tasks t on t.id = e.task_id
|
|
97
|
+
where e.status in ('open', 'acknowledged');
|
|
98
|
+
|
|
99
|
+
create or replace view agent_board.v_agent_dashboard
|
|
100
|
+
with (security_invoker = true)
|
|
101
|
+
as
|
|
102
|
+
select
|
|
103
|
+
a.id as agent_id,
|
|
104
|
+
a.handle,
|
|
105
|
+
a.display_name,
|
|
106
|
+
a.status as participant_status,
|
|
107
|
+
p.availability,
|
|
108
|
+
p.thread_id as presence_thread_id,
|
|
109
|
+
p.last_heartbeat_at,
|
|
110
|
+
p.expires_at as presence_expires_at,
|
|
111
|
+
coalesce(open_tasks.open_task_count, 0)::bigint as open_task_count,
|
|
112
|
+
coalesce(active_claims.active_claim_count, 0)::bigint as active_claim_count,
|
|
113
|
+
coalesce(open_escalations.assigned_open_escalation_count, 0)::bigint as assigned_open_escalation_count,
|
|
114
|
+
latest.task_id as latest_task_id,
|
|
115
|
+
latest.event_sequence as latest_task_event_sequence,
|
|
116
|
+
latest.event_type as latest_task_event_type,
|
|
117
|
+
latest.payload->>'kind' as latest_task_event_kind,
|
|
118
|
+
latest.updated_at as latest_task_updated_at
|
|
119
|
+
from agent_board.agents a
|
|
120
|
+
left join agent_board.presence p on p.agent_id = a.id
|
|
121
|
+
left join lateral (
|
|
122
|
+
select count(*) as open_task_count
|
|
123
|
+
from agent_board.tasks t
|
|
124
|
+
where t.assigned_agent_id = a.id
|
|
125
|
+
and t.status in ('pending', 'in_progress', 'blocked')
|
|
126
|
+
) open_tasks on true
|
|
127
|
+
left join lateral (
|
|
128
|
+
select count(*) as active_claim_count
|
|
129
|
+
from agent_board.claims c
|
|
130
|
+
where c.agent_id = a.id
|
|
131
|
+
and c.status = 'active'
|
|
132
|
+
and c.released_at is null
|
|
133
|
+
and c.lease_expires_at > now()
|
|
134
|
+
) active_claims on true
|
|
135
|
+
left join lateral (
|
|
136
|
+
select count(*) as assigned_open_escalation_count
|
|
137
|
+
from agent_board.escalations e
|
|
138
|
+
where e.assigned_agent_id = a.id
|
|
139
|
+
and e.status in ('open', 'acknowledged')
|
|
140
|
+
) open_escalations on true
|
|
141
|
+
left join lateral (
|
|
142
|
+
select t.id as task_id, te.event_sequence, te.event_type, te.payload, t.updated_at
|
|
143
|
+
from agent_board.tasks t
|
|
144
|
+
left join agent_board.task_events te on te.task_id = t.id
|
|
145
|
+
where t.assigned_agent_id = a.id or t.created_by_agent_id = a.id
|
|
146
|
+
order by t.updated_at desc, te.event_sequence desc nulls last
|
|
147
|
+
limit 1
|
|
148
|
+
) latest on true;
|
|
149
|
+
|
|
150
|
+
grant usage on schema agent_board to service_role;
|
|
151
|
+
grant select on all tables in schema agent_board to service_role;
|
|
152
|
+
grant select on agent_board.v_thread_feed, agent_board.v_task_tree, agent_board.v_active_claims, agent_board.v_open_escalations, agent_board.v_agent_dashboard to authenticated;
|
|
153
|
+
|
|
154
|
+
alter table agent_board.agents enable row level security;
|
|
155
|
+
alter table agent_board.threads enable row level security;
|
|
156
|
+
alter table agent_board.messages enable row level security;
|
|
157
|
+
alter table agent_board.tasks enable row level security;
|
|
158
|
+
alter table agent_board.task_events enable row level security;
|
|
159
|
+
alter table agent_board.claims enable row level security;
|
|
160
|
+
alter table agent_board.delegations enable row level security;
|
|
161
|
+
alter table agent_board.artifacts enable row level security;
|
|
162
|
+
alter table agent_board.conflicts enable row level security;
|
|
163
|
+
alter table agent_board.escalations enable row level security;
|
|
164
|
+
alter table agent_board.read_receipts enable row level security;
|
|
165
|
+
alter table agent_board.presence enable row level security;
|
|
166
|
+
|
|
167
|
+
do $$
|
|
168
|
+
declare
|
|
169
|
+
v_table text;
|
|
170
|
+
begin
|
|
171
|
+
foreach v_table in array array[
|
|
172
|
+
'agents',
|
|
173
|
+
'threads',
|
|
174
|
+
'messages',
|
|
175
|
+
'tasks',
|
|
176
|
+
'task_events',
|
|
177
|
+
'claims',
|
|
178
|
+
'delegations',
|
|
179
|
+
'artifacts',
|
|
180
|
+
'conflicts',
|
|
181
|
+
'escalations',
|
|
182
|
+
'read_receipts',
|
|
183
|
+
'presence'
|
|
184
|
+
]
|
|
185
|
+
loop
|
|
186
|
+
execute format('drop policy if exists %I on agent_board.%I', 'agent_board_service_all_' || v_table, v_table);
|
|
187
|
+
execute format('create policy %I on agent_board.%I as permissive for all to service_role using (true) with check (true)', 'agent_board_service_all_' || v_table, v_table);
|
|
188
|
+
end loop;
|
|
189
|
+
end;
|
|
190
|
+
$$;
|
|
191
|
+
|
|
192
|
+
do $$
|
|
193
|
+
declare
|
|
194
|
+
v_table text;
|
|
195
|
+
begin
|
|
196
|
+
foreach v_table in array array['messages', 'task_events', 'tasks', 'claims', 'delegations', 'conflicts', 'escalations', 'presence']
|
|
197
|
+
loop
|
|
198
|
+
begin
|
|
199
|
+
execute format('alter publication supabase_realtime add table agent_board.%I', v_table);
|
|
200
|
+
exception
|
|
201
|
+
when duplicate_object then null;
|
|
202
|
+
when undefined_object then null;
|
|
203
|
+
end;
|
|
204
|
+
end loop;
|
|
205
|
+
end;
|
|
206
|
+
$$;
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
create table if not exists agent_board.boards (
|
|
2
|
+
id uuid primary key default gen_random_uuid(),
|
|
3
|
+
slug text not null unique,
|
|
4
|
+
name text not null,
|
|
5
|
+
description text,
|
|
6
|
+
created_by_agent_id uuid not null references agent_board.agents(id),
|
|
7
|
+
status text not null default 'active' check (status in ('active', 'archived')),
|
|
8
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
9
|
+
created_at timestamptz not null default now(),
|
|
10
|
+
updated_at timestamptz not null default now()
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
comment on table agent_board.boards is 'Named opt-in coordination surfaces for shared board delivery and board feeds.';
|
|
14
|
+
|
|
15
|
+
create table if not exists agent_board.board_memberships (
|
|
16
|
+
id uuid primary key default gen_random_uuid(),
|
|
17
|
+
board_id uuid not null references agent_board.boards(id) on delete cascade,
|
|
18
|
+
agent_id uuid not null references agent_board.agents(id) on delete cascade,
|
|
19
|
+
membership_state text not null default 'joined' check (membership_state in ('joined', 'subscribed', 'left', 'removed')),
|
|
20
|
+
created_by_agent_id uuid references agent_board.agents(id),
|
|
21
|
+
left_at timestamptz,
|
|
22
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
23
|
+
created_at timestamptz not null default now(),
|
|
24
|
+
updated_at timestamptz not null default now(),
|
|
25
|
+
constraint board_memberships_state_left_at check (
|
|
26
|
+
(membership_state in ('joined', 'subscribed') and left_at is null)
|
|
27
|
+
or (membership_state in ('left', 'removed') and left_at is not null)
|
|
28
|
+
),
|
|
29
|
+
constraint board_memberships_identity_unique unique (id, board_id, agent_id)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
comment on table agent_board.board_memberships is 'One agent membership state per board; joined and subscribed are active delivery states.';
|
|
33
|
+
|
|
34
|
+
create unique index if not exists idx_agent_board_board_memberships_active_unique
|
|
35
|
+
on agent_board.board_memberships(board_id, agent_id)
|
|
36
|
+
where membership_state in ('joined', 'subscribed');
|
|
37
|
+
|
|
38
|
+
create index if not exists idx_agent_board_board_memberships_board_state
|
|
39
|
+
on agent_board.board_memberships(board_id, membership_state, created_at desc);
|
|
40
|
+
|
|
41
|
+
create table if not exists agent_board.delivery_recipients (
|
|
42
|
+
id uuid primary key default gen_random_uuid(),
|
|
43
|
+
delivery_mode text not null check (delivery_mode in ('direct', 'multicast', 'board', 'tasking')),
|
|
44
|
+
recipient_agent_id uuid not null references agent_board.agents(id) on delete cascade,
|
|
45
|
+
source_kind text not null check (source_kind in ('message', 'task', 'delegation')),
|
|
46
|
+
source_message_id uuid references agent_board.messages(id) on delete cascade,
|
|
47
|
+
source_task_id uuid references agent_board.tasks(id) on delete cascade,
|
|
48
|
+
source_delegation_id uuid references agent_board.delegations(id) on delete cascade,
|
|
49
|
+
board_id uuid references agent_board.boards(id) on delete cascade,
|
|
50
|
+
board_membership_id uuid,
|
|
51
|
+
canonical_source_key text generated always as (
|
|
52
|
+
case source_kind
|
|
53
|
+
when 'message' then 'message:' || source_message_id::text
|
|
54
|
+
when 'task' then 'task:' || source_task_id::text
|
|
55
|
+
when 'delegation' then 'delegation:' || source_delegation_id::text
|
|
56
|
+
end
|
|
57
|
+
) stored,
|
|
58
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
59
|
+
created_at timestamptz not null default now(),
|
|
60
|
+
constraint delivery_recipients_source_shape check (
|
|
61
|
+
(source_kind = 'message' and source_message_id is not null and source_task_id is null and source_delegation_id is null)
|
|
62
|
+
or (source_kind = 'task' and source_message_id is null and source_task_id is not null and source_delegation_id is null)
|
|
63
|
+
or (source_kind = 'delegation' and source_message_id is null and source_task_id is null and source_delegation_id is not null)
|
|
64
|
+
),
|
|
65
|
+
constraint delivery_recipients_board_scope check (
|
|
66
|
+
(
|
|
67
|
+
delivery_mode in ('direct', 'multicast')
|
|
68
|
+
and board_id is null
|
|
69
|
+
and board_membership_id is null
|
|
70
|
+
)
|
|
71
|
+
or (
|
|
72
|
+
delivery_mode in ('board', 'tasking')
|
|
73
|
+
and (
|
|
74
|
+
(board_id is null and board_membership_id is null)
|
|
75
|
+
or (board_id is not null and board_membership_id is not null)
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
),
|
|
79
|
+
constraint delivery_recipients_board_membership_fkey
|
|
80
|
+
foreign key (board_membership_id, board_id, recipient_agent_id)
|
|
81
|
+
references agent_board.board_memberships(id, board_id, agent_id)
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
comment on table agent_board.delivery_recipients is 'Append-only canonical routing rows for direct, multicast, board, and tasking recipients.';
|
|
85
|
+
|
|
86
|
+
create unique index if not exists idx_agent_board_delivery_recipients_unique_source_recipient
|
|
87
|
+
on agent_board.delivery_recipients(recipient_agent_id, canonical_source_key);
|
|
88
|
+
|
|
89
|
+
create index if not exists idx_agent_board_delivery_recipients_board_feed
|
|
90
|
+
on agent_board.delivery_recipients(board_id, created_at desc)
|
|
91
|
+
where board_id is not null;
|
|
92
|
+
|
|
93
|
+
create table if not exists agent_board.inbox_items (
|
|
94
|
+
id uuid primary key default gen_random_uuid(),
|
|
95
|
+
delivery_recipient_id uuid not null unique references agent_board.delivery_recipients(id) on delete cascade,
|
|
96
|
+
recipient_agent_id uuid not null references agent_board.agents(id) on delete cascade,
|
|
97
|
+
board_id uuid references agent_board.boards(id) on delete cascade,
|
|
98
|
+
delivery_mode text not null check (delivery_mode in ('direct', 'multicast', 'board', 'tasking')),
|
|
99
|
+
source_kind text not null check (source_kind in ('message', 'task', 'delegation')),
|
|
100
|
+
source_message_id uuid references agent_board.messages(id) on delete cascade,
|
|
101
|
+
source_task_id uuid references agent_board.tasks(id) on delete cascade,
|
|
102
|
+
source_delegation_id uuid references agent_board.delegations(id) on delete cascade,
|
|
103
|
+
subject text not null,
|
|
104
|
+
preview text,
|
|
105
|
+
priority text not null default 'medium' check (priority in ('low', 'medium', 'high', 'urgent')),
|
|
106
|
+
action_state text not null default 'new' check (action_state in ('new', 'read', 'acknowledged', 'deferred', 'dismissed', 'restored')),
|
|
107
|
+
visible_in_inbox boolean not null default true,
|
|
108
|
+
visible_in_board_feed boolean not null default false,
|
|
109
|
+
fetch_descriptor jsonb not null default '{}'::jsonb,
|
|
110
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
111
|
+
created_at timestamptz not null default now(),
|
|
112
|
+
updated_at timestamptz not null default now(),
|
|
113
|
+
constraint inbox_items_source_shape check (
|
|
114
|
+
(source_kind = 'message' and source_message_id is not null and source_task_id is null and source_delegation_id is null)
|
|
115
|
+
or (source_kind = 'task' and source_message_id is null and source_task_id is not null and source_delegation_id is null)
|
|
116
|
+
or (source_kind = 'delegation' and source_message_id is null and source_task_id is null and source_delegation_id is not null)
|
|
117
|
+
),
|
|
118
|
+
constraint inbox_items_visibility_shape check (
|
|
119
|
+
(visible_in_inbox or visible_in_board_feed)
|
|
120
|
+
and (not visible_in_board_feed or board_id is not null)
|
|
121
|
+
),
|
|
122
|
+
constraint inbox_items_board_scope check (
|
|
123
|
+
(
|
|
124
|
+
delivery_mode in ('direct', 'multicast')
|
|
125
|
+
and board_id is null
|
|
126
|
+
and visible_in_board_feed = false
|
|
127
|
+
)
|
|
128
|
+
or delivery_mode in ('board', 'tasking')
|
|
129
|
+
),
|
|
130
|
+
constraint inbox_items_fetch_descriptor_object check (jsonb_typeof(fetch_descriptor) = 'object')
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
comment on table agent_board.inbox_items is 'Compact per-recipient triage rows with bounded metadata and canonical fetch descriptors.';
|
|
134
|
+
|
|
135
|
+
create index if not exists idx_agent_board_inbox_items_agent_triage
|
|
136
|
+
on agent_board.inbox_items(recipient_agent_id, updated_at desc, created_at desc);
|
|
137
|
+
|
|
138
|
+
create index if not exists idx_agent_board_inbox_items_agent_pending
|
|
139
|
+
on agent_board.inbox_items(recipient_agent_id, updated_at desc)
|
|
140
|
+
where action_state in ('new', 'restored', 'deferred');
|
|
141
|
+
|
|
142
|
+
create index if not exists idx_agent_board_inbox_items_board_feed
|
|
143
|
+
on agent_board.inbox_items(board_id, created_at desc)
|
|
144
|
+
where visible_in_board_feed = true;
|
|
145
|
+
|
|
146
|
+
create table if not exists agent_board.callback_endpoints (
|
|
147
|
+
id uuid primary key default gen_random_uuid(),
|
|
148
|
+
owner_agent_id uuid not null references agent_board.agents(id) on delete cascade,
|
|
149
|
+
endpoint_url text not null,
|
|
150
|
+
endpoint_status text not null default 'pending_verification' check (endpoint_status in ('pending_verification', 'verified', 'disabled')),
|
|
151
|
+
signing_key_version integer not null default 1 check (signing_key_version > 0),
|
|
152
|
+
verification_requested_at timestamptz,
|
|
153
|
+
verified_at timestamptz,
|
|
154
|
+
disabled_at timestamptz,
|
|
155
|
+
disabled_reason text,
|
|
156
|
+
last_delivery_at timestamptz,
|
|
157
|
+
last_failure_at timestamptz,
|
|
158
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
159
|
+
created_at timestamptz not null default now(),
|
|
160
|
+
updated_at timestamptz not null default now(),
|
|
161
|
+
constraint callback_endpoints_status_disabled_shape check (
|
|
162
|
+
(endpoint_status = 'disabled' and disabled_at is not null)
|
|
163
|
+
or (endpoint_status <> 'disabled' and disabled_at is null)
|
|
164
|
+
),
|
|
165
|
+
constraint callback_endpoints_url_unique unique (owner_agent_id, endpoint_url)
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
comment on table agent_board.callback_endpoints is 'Durable callback endpoint registration with verification and disablement metadata.';
|
|
169
|
+
|
|
170
|
+
create table if not exists agent_board.callback_subscriptions (
|
|
171
|
+
id uuid primary key default gen_random_uuid(),
|
|
172
|
+
endpoint_id uuid not null references agent_board.callback_endpoints(id) on delete cascade,
|
|
173
|
+
agent_id uuid not null references agent_board.agents(id) on delete cascade,
|
|
174
|
+
subscription_scope text not null check (subscription_scope in ('own_inbox', 'board')),
|
|
175
|
+
event_category text not null check (event_category in ('inbox.item.delivered', 'inbox.item.updated', 'board.post.delivered', 'board.post.updated')),
|
|
176
|
+
board_id uuid references agent_board.boards(id) on delete cascade,
|
|
177
|
+
board_membership_id uuid,
|
|
178
|
+
subscription_status text not null default 'active' check (subscription_status in ('active', 'disabled')),
|
|
179
|
+
disabled_at timestamptz,
|
|
180
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
181
|
+
created_at timestamptz not null default now(),
|
|
182
|
+
updated_at timestamptz not null default now(),
|
|
183
|
+
constraint callback_subscriptions_scope_shape check (
|
|
184
|
+
(
|
|
185
|
+
subscription_scope = 'own_inbox'
|
|
186
|
+
and event_category in ('inbox.item.delivered', 'inbox.item.updated')
|
|
187
|
+
and board_id is null
|
|
188
|
+
and board_membership_id is null
|
|
189
|
+
)
|
|
190
|
+
or (
|
|
191
|
+
subscription_scope = 'board'
|
|
192
|
+
and event_category in ('board.post.delivered', 'board.post.updated')
|
|
193
|
+
and board_id is not null
|
|
194
|
+
and board_membership_id is not null
|
|
195
|
+
)
|
|
196
|
+
),
|
|
197
|
+
constraint callback_subscriptions_status_disabled_shape check (
|
|
198
|
+
(subscription_status = 'disabled' and disabled_at is not null)
|
|
199
|
+
or (subscription_status <> 'disabled' and disabled_at is null)
|
|
200
|
+
),
|
|
201
|
+
constraint callback_subscriptions_board_membership_fkey
|
|
202
|
+
foreign key (board_membership_id, board_id, agent_id)
|
|
203
|
+
references agent_board.board_memberships(id, board_id, agent_id)
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
comment on table agent_board.callback_subscriptions is 'Own-inbox and board-scoped callback intent for one endpoint.';
|
|
207
|
+
|
|
208
|
+
create unique index if not exists idx_agent_board_callback_subscriptions_active_unique
|
|
209
|
+
on agent_board.callback_subscriptions(endpoint_id, agent_id, event_category, coalesce(board_id, '00000000-0000-0000-0000-000000000000'::uuid))
|
|
210
|
+
where subscription_status = 'active';
|
|
211
|
+
|
|
212
|
+
create index if not exists idx_agent_board_callback_subscriptions_board
|
|
213
|
+
on agent_board.callback_subscriptions(board_id, subscription_status, created_at desc)
|
|
214
|
+
where board_id is not null;
|
|
215
|
+
|
|
216
|
+
create table if not exists agent_board.callback_outbox_events (
|
|
217
|
+
id uuid primary key default gen_random_uuid(),
|
|
218
|
+
endpoint_id uuid not null references agent_board.callback_endpoints(id) on delete cascade,
|
|
219
|
+
subscription_id uuid not null references agent_board.callback_subscriptions(id) on delete cascade,
|
|
220
|
+
recipient_agent_id uuid not null references agent_board.agents(id) on delete cascade,
|
|
221
|
+
inbox_item_id uuid references agent_board.inbox_items(id) on delete set null,
|
|
222
|
+
board_id uuid references agent_board.boards(id) on delete cascade,
|
|
223
|
+
event_category text not null check (event_category in ('inbox.item.delivered', 'inbox.item.updated', 'board.post.delivered', 'board.post.updated')),
|
|
224
|
+
delivery_mode text not null check (delivery_mode in ('direct', 'multicast', 'board', 'tasking')),
|
|
225
|
+
source_kind text not null check (source_kind in ('message', 'task', 'delegation')),
|
|
226
|
+
source_message_id uuid references agent_board.messages(id) on delete cascade,
|
|
227
|
+
source_task_id uuid references agent_board.tasks(id) on delete cascade,
|
|
228
|
+
source_delegation_id uuid references agent_board.delegations(id) on delete cascade,
|
|
229
|
+
idempotency_key text not null unique,
|
|
230
|
+
subject text,
|
|
231
|
+
preview text,
|
|
232
|
+
priority text not null default 'medium' check (priority in ('low', 'medium', 'high', 'urgent')),
|
|
233
|
+
fetch_descriptor jsonb not null default '{}'::jsonb,
|
|
234
|
+
outbox_status text not null default 'pending' check (outbox_status in ('pending', 'delivering', 'retryable', 'delivered', 'failed', 'disabled')),
|
|
235
|
+
available_at timestamptz not null default now(),
|
|
236
|
+
locked_at timestamptz,
|
|
237
|
+
delivered_at timestamptz,
|
|
238
|
+
last_error_at timestamptz,
|
|
239
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
240
|
+
created_at timestamptz not null default now(),
|
|
241
|
+
updated_at timestamptz not null default now(),
|
|
242
|
+
constraint callback_outbox_events_source_shape check (
|
|
243
|
+
(source_kind = 'message' and source_message_id is not null and source_task_id is null and source_delegation_id is null)
|
|
244
|
+
or (source_kind = 'task' and source_message_id is null and source_task_id is not null and source_delegation_id is null)
|
|
245
|
+
or (source_kind = 'delegation' and source_message_id is null and source_task_id is null and source_delegation_id is not null)
|
|
246
|
+
),
|
|
247
|
+
constraint callback_outbox_events_scope_shape check (
|
|
248
|
+
(
|
|
249
|
+
event_category in ('inbox.item.delivered', 'inbox.item.updated')
|
|
250
|
+
and board_id is null
|
|
251
|
+
)
|
|
252
|
+
or (
|
|
253
|
+
event_category in ('board.post.delivered', 'board.post.updated')
|
|
254
|
+
and board_id is not null
|
|
255
|
+
)
|
|
256
|
+
),
|
|
257
|
+
constraint callback_outbox_events_fetch_descriptor_object check (jsonb_typeof(fetch_descriptor) = 'object')
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
comment on table agent_board.callback_outbox_events is 'Durable callback notification intent written before any external delivery attempt.';
|
|
261
|
+
|
|
262
|
+
create index if not exists idx_agent_board_callback_outbox_events_poll
|
|
263
|
+
on agent_board.callback_outbox_events(outbox_status, available_at, created_at)
|
|
264
|
+
where outbox_status in ('pending', 'retryable');
|
|
265
|
+
|
|
266
|
+
create index if not exists idx_agent_board_callback_outbox_events_retry
|
|
267
|
+
on agent_board.callback_outbox_events(available_at, updated_at desc)
|
|
268
|
+
where outbox_status = 'retryable';
|
|
269
|
+
|
|
270
|
+
create table if not exists agent_board.callback_delivery_attempts (
|
|
271
|
+
id uuid primary key default gen_random_uuid(),
|
|
272
|
+
outbox_event_id uuid not null references agent_board.callback_outbox_events(id) on delete cascade,
|
|
273
|
+
endpoint_id uuid not null references agent_board.callback_endpoints(id) on delete cascade,
|
|
274
|
+
attempt_number integer not null check (attempt_number > 0),
|
|
275
|
+
attempt_outcome text not null check (attempt_outcome in ('delivered', 'retryable_failure', 'terminal_failure', 'endpoint_disabled')),
|
|
276
|
+
http_status integer check (http_status is null or (http_status >= 100 and http_status <= 599)),
|
|
277
|
+
error_summary text,
|
|
278
|
+
started_at timestamptz not null default now(),
|
|
279
|
+
finished_at timestamptz,
|
|
280
|
+
next_retry_at timestamptz,
|
|
281
|
+
metadata jsonb not null default '{}'::jsonb,
|
|
282
|
+
created_at timestamptz not null default now(),
|
|
283
|
+
constraint callback_delivery_attempts_retry_shape check (
|
|
284
|
+
(attempt_outcome = 'retryable_failure' and next_retry_at is not null)
|
|
285
|
+
or (attempt_outcome <> 'retryable_failure' and next_retry_at is null)
|
|
286
|
+
),
|
|
287
|
+
constraint callback_delivery_attempts_outbox_attempt_unique unique (outbox_event_id, attempt_number)
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
comment on table agent_board.callback_delivery_attempts is 'Append-only callback delivery attempts for retries, successes, and terminal failures.';
|
|
291
|
+
|
|
292
|
+
create index if not exists idx_agent_board_callback_delivery_attempts_retry_schedule
|
|
293
|
+
on agent_board.callback_delivery_attempts(next_retry_at, created_at)
|
|
294
|
+
where attempt_outcome = 'retryable_failure';
|
|
295
|
+
|
|
296
|
+
create index if not exists idx_agent_board_callback_delivery_attempts_outbox
|
|
297
|
+
on agent_board.callback_delivery_attempts(outbox_event_id, attempt_number desc);
|
|
298
|
+
|
|
299
|
+
grant select on agent_board.boards,
|
|
300
|
+
agent_board.board_memberships,
|
|
301
|
+
agent_board.delivery_recipients,
|
|
302
|
+
agent_board.inbox_items,
|
|
303
|
+
agent_board.callback_endpoints,
|
|
304
|
+
agent_board.callback_subscriptions,
|
|
305
|
+
agent_board.callback_outbox_events,
|
|
306
|
+
agent_board.callback_delivery_attempts
|
|
307
|
+
to service_role;
|
|
308
|
+
|
|
309
|
+
alter table agent_board.boards enable row level security;
|
|
310
|
+
alter table agent_board.board_memberships enable row level security;
|
|
311
|
+
alter table agent_board.delivery_recipients enable row level security;
|
|
312
|
+
alter table agent_board.inbox_items enable row level security;
|
|
313
|
+
alter table agent_board.callback_endpoints enable row level security;
|
|
314
|
+
alter table agent_board.callback_subscriptions enable row level security;
|
|
315
|
+
alter table agent_board.callback_outbox_events enable row level security;
|
|
316
|
+
alter table agent_board.callback_delivery_attempts enable row level security;
|
|
317
|
+
|
|
318
|
+
do $$
|
|
319
|
+
declare
|
|
320
|
+
v_table text;
|
|
321
|
+
begin
|
|
322
|
+
foreach v_table in array array[
|
|
323
|
+
'boards',
|
|
324
|
+
'board_memberships',
|
|
325
|
+
'delivery_recipients',
|
|
326
|
+
'inbox_items',
|
|
327
|
+
'callback_endpoints',
|
|
328
|
+
'callback_subscriptions',
|
|
329
|
+
'callback_outbox_events',
|
|
330
|
+
'callback_delivery_attempts'
|
|
331
|
+
]
|
|
332
|
+
loop
|
|
333
|
+
execute format('drop policy if exists %I on agent_board.%I', 'agent_board_service_all_' || v_table, v_table);
|
|
334
|
+
execute format(
|
|
335
|
+
'create policy %I on agent_board.%I as permissive for all to service_role using (true) with check (true)',
|
|
336
|
+
'agent_board_service_all_' || v_table,
|
|
337
|
+
v_table
|
|
338
|
+
);
|
|
339
|
+
end loop;
|
|
340
|
+
end;
|
|
341
|
+
$$;
|