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>
59 lines
1.5 KiB
SQL
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;
|