Files
synapbus/internal/storage/schema/016_agent_query_views.sql
T
Algis DumbrisandClaude Opus 4.6 bd1bccc692 feat(015): SQL query interface for agents + split read/write pools
Split Connection Pools:
- writeDB: MaxOpenConns=1, serializes all writes (no SQLITE_BUSY)
- readDB: MaxOpenConns=8, query_only=ON, for all SELECTs
- QueryDB() helper returns read pool when available

SQL Query Interface:
- New 'query' action via execute MCP tool
- Read-only enforcement (PRAGMA query_only=ON + SQL validation)
- Curated views: my_messages, my_channels, channel_messages
- Per-agent access control via CTE injection
- Auto LIMIT 100, 5s timeout, SELECT-only validation
- Blocks: INSERT, UPDATE, DELETE, DROP, PRAGMA, etc.
- 12 new tests (access control, validation, limits, CTEs)

Migration 016: agent query views (v_agent_messages, etc.)
Action registry: 30 actions (was 29, added 'query')
All 29 test packages pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:17:23 +02:00

59 lines
1.5 KiB
SQL

-- 016: Agent SQL query views
-- These views are used by the 'query' action to give agents read access
-- to messages they can see. The views expose a stable schema that agents
-- can query via SQL. Access control is enforced at the Go layer by
-- rewriting queries to filter by agent name.
-- Note: SQLite views cannot be parameterized. The Go query executor
-- wraps agent queries in a CTE that filters by the authenticated agent's
-- access (own DMs + joined channels). These views provide the base schema.
-- my_messages: All messages accessible to the calling agent
CREATE VIEW IF NOT EXISTS v_agent_messages AS
SELECT
m.id,
m.body,
m.from_agent,
m.to_agent,
m.priority,
m.status,
m.metadata,
m.created_at,
m.updated_at,
c.name AS channel_name,
m.channel_id,
m.reply_to,
m.conversation_id
FROM messages m
LEFT JOIN channels c ON c.id = m.channel_id;
-- my_channels: Channels the calling agent has joined
CREATE VIEW IF NOT EXISTS v_agent_channels AS
SELECT
c.id,
c.name,
c.description,
c.type,
c.topic,
c.is_private,
c.created_at,
cm.joined_at AS member_since
FROM channels c
JOIN channel_members cm ON cm.channel_id = c.id;
-- channel_messages: Messages in channels (filtered by membership at Go layer)
CREATE VIEW IF NOT EXISTS v_channel_messages AS
SELECT
m.id,
m.body,
m.from_agent,
m.priority,
m.status,
m.metadata,
m.created_at,
c.name AS channel_name,
m.channel_id,
m.reply_to
FROM messages m
JOIN channels c ON c.id = m.channel_id;