bb855d2ddc
* feat(branding): implement dynamic branding in service worker and reports * feat(auth): enhance API key scopes and add bookkeeping write scope - Updated transaction write scope description to include additional tools. - Enhanced reports read scope description to reflect new functionality. - Introduced bookkeeping write scope with relevant description. - Updated SCOPE_GROUPS to include bookkeeping domain. - Modified TOOL_SCOPE_MAP to include new bookkeeping operations. - Updated validateApiKey function to return api_key_id and api_key_name for better actor attribution. feat(tests): add unit tests for MCP resource registry - Created tests for data resources to ensure all required fields are present. - Added tests for resource query parsing and retrieval. feat(resources): implement MCP resources for company and accounting data - Added capabilities resource to expose API key capabilities based on granted scopes. - Implemented chart of accounts resource to retrieve active BAS chart. - Created company current resource to fetch active company details. - Developed active fiscal period resource to check posting eligibility. - Implemented recent activity resource to fetch latest journal entries, invoices, and transactions. - Added VAT treatments resource to provide available VAT rates per customer type. feat(pending-operations): introduce risk tiers for operations - Added risk level classification for pending operations to determine auto-commit eligibility. - Implemented functions to classify operation risk levels and identify high-risk operations. feat(migrations): add actor model and risk tier to pending operations - Updated pending_operations table to include actor type and risk level columns. - Enhanced audit_log to mirror actor information for compliance. - Modified validate_and_increment_api_key function to return actor details. - Expanded operation types in pending_operations to include new high-risk operations. * feat: add auto-commit functionality for low-risk pending operations - Implemented shouldAutoCommit function to determine eligibility for auto-commit based on operation type, actor type, and company settings. - Created commitPendingOperation function to handle execution of pending operations with consistent status updates. - Added tests for shouldAutoCommit to cover various scenarios including high-risk operations, user actors, company opt-in status, and monetary thresholds. - Introduced new columns in company_settings for agent_auto_commit_enabled and agent_auto_commit_max_amount to allow companies to opt-in for auto-commit functionality. - Added SQL migration to update the database schema for new auto-commit settings. * feat(idempotency): implement idempotency key handling for safe retries and cleanup * feat: expand API key scopes and pending operations for bookkeeping - Added 'suppliers:write' scope to API key scopes for supplier invoice management. - Updated SCOPE_GROUPS to include the new 'suppliers:write' scope. - Introduced new pending operation types for bookkeeping: close_period, lock_period, run_year_end, set_opening_balances, run_currency_revaluation, explain_voucher_gap, uncategorize_transaction, approve_supplier_invoice, credit_supplier_invoice, and convert_invoice. - Implemented corresponding commit functions for the new operations in the pending operations module. - Enhanced PendingOperation type to include actor model and risk level attributes. - Added tests for new functionality, ensuring proper behavior and constraints in the database. * feat: implement unlockPeriod functionality and related tests * feat: add agent auto-commit settings and related functionality * feat: add attention resource with comprehensive summary of outstanding tasks * feat: enhance pending operations with 'committing' status and immutability checks, improve idempotency handling, and add original voucher reference for credit notes
129 lines
5.0 KiB
PL/PgSQL
129 lines
5.0 KiB
PL/PgSQL
-- Migration: Actor model + risk tier on pending_operations + audit_log
|
|
--
|
|
-- Adds first-class actor attribution (user vs api_key vs mcp_oauth vs cron) and
|
|
-- a risk_level for risk-tiered approval policies. This is the foundation for
|
|
-- letting trusted agents auto-commit low-risk proposals while keeping high-risk
|
|
-- operations (period close, year-end, send_invoice, etc.) gated behind human
|
|
-- approval regardless of trust level.
|
|
--
|
|
-- Why this lives here, not in app code:
|
|
-- - actor_type and risk_level are filtered/queried from the UI ("show me
|
|
-- only auto-committed actions")
|
|
-- - the same actor info needs to live in audit_log for compliance review
|
|
-- - having the columns enforced by check constraints prevents drift between
|
|
-- producers (MCP, OAuth, web, cron)
|
|
|
|
-- =============================================================================
|
|
-- 1. pending_operations: actor + risk columns
|
|
-- =============================================================================
|
|
|
|
ALTER TABLE public.pending_operations
|
|
ADD COLUMN actor_type TEXT NOT NULL DEFAULT 'user' CHECK (actor_type IN (
|
|
'user', 'api_key', 'mcp_oauth', 'cron'
|
|
)),
|
|
ADD COLUMN actor_id UUID,
|
|
ADD COLUMN actor_label TEXT,
|
|
ADD COLUMN risk_level TEXT NOT NULL DEFAULT 'high' CHECK (risk_level IN (
|
|
'low', 'medium', 'high'
|
|
)),
|
|
ADD COLUMN auto_commit_eligible BOOLEAN NOT NULL DEFAULT false,
|
|
ADD COLUMN auto_committed_at TIMESTAMPTZ;
|
|
|
|
-- Index supporting the "auto-committed by Claude Desktop" filter tab on the
|
|
-- pending operations page.
|
|
CREATE INDEX idx_pending_ops_actor_type ON public.pending_operations (company_id, actor_type, status);
|
|
CREATE INDEX idx_pending_ops_auto_committed ON public.pending_operations (company_id, auto_committed_at)
|
|
WHERE auto_committed_at IS NOT NULL;
|
|
|
|
-- Sanity: auto_committed_at can only be set when status = 'committed'.
|
|
ALTER TABLE public.pending_operations
|
|
ADD CONSTRAINT pending_ops_auto_commit_status CHECK (
|
|
auto_committed_at IS NULL OR status = 'committed'
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- 2. audit_log: mirror actor columns
|
|
-- =============================================================================
|
|
-- audit_log already has an `actor_id` column (uuid). We add actor_type/label
|
|
-- alongside so the UI can show "Auto-committed by Claude Desktop" without
|
|
-- needing a join through api_keys.
|
|
|
|
ALTER TABLE public.audit_log
|
|
ADD COLUMN actor_type TEXT DEFAULT 'user' CHECK (actor_type IN (
|
|
'user', 'api_key', 'mcp_oauth', 'cron', 'system'
|
|
)),
|
|
ADD COLUMN actor_label TEXT;
|
|
|
|
CREATE INDEX idx_audit_log_actor_type ON public.audit_log (user_id, actor_type, created_at DESC);
|
|
|
|
-- =============================================================================
|
|
-- 3. validate_and_increment_api_key: surface api_key_id + name for actor model
|
|
-- =============================================================================
|
|
-- The RPC previously returned only (user_id, company_id, rate_limited, scopes).
|
|
-- We now also return (api_key_id, api_key_name) so the MCP server can record
|
|
-- the actor on pending_operations / audit_log without an extra round-trip.
|
|
|
|
DROP FUNCTION IF EXISTS public.validate_and_increment_api_key(text);
|
|
|
|
CREATE FUNCTION public.validate_and_increment_api_key(p_key_hash text)
|
|
RETURNS TABLE(
|
|
user_id uuid,
|
|
company_id uuid,
|
|
api_key_id uuid,
|
|
api_key_name text,
|
|
rate_limited boolean,
|
|
scopes text[]
|
|
)
|
|
LANGUAGE plpgsql SECURITY DEFINER AS $$
|
|
DECLARE
|
|
v_user_id uuid;
|
|
v_company_id uuid;
|
|
v_api_key_id uuid;
|
|
v_api_key_name text;
|
|
v_rate_limit_rpm integer;
|
|
v_request_count integer;
|
|
v_window_start timestamptz;
|
|
v_scopes text[];
|
|
BEGIN
|
|
SELECT ak.user_id, ak.company_id, ak.id, ak.name,
|
|
ak.rate_limit_rpm, ak.request_count, ak.rate_limit_window_start, ak.scopes
|
|
INTO v_user_id, v_company_id, v_api_key_id, v_api_key_name,
|
|
v_rate_limit_rpm, v_request_count, v_window_start, v_scopes
|
|
FROM public.api_keys ak
|
|
WHERE ak.key_hash = p_key_hash AND ak.revoked_at IS NULL
|
|
FOR UPDATE;
|
|
|
|
IF v_user_id IS NULL THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
IF v_window_start IS NULL OR v_window_start < now() - interval '1 minute' THEN
|
|
UPDATE public.api_keys
|
|
SET request_count = 1,
|
|
rate_limit_window_start = now(),
|
|
last_used_at = now()
|
|
WHERE key_hash = p_key_hash;
|
|
|
|
RETURN QUERY SELECT v_user_id, v_company_id, v_api_key_id, v_api_key_name, false, v_scopes;
|
|
RETURN;
|
|
END IF;
|
|
|
|
IF v_request_count >= v_rate_limit_rpm THEN
|
|
RETURN QUERY SELECT v_user_id, v_company_id, v_api_key_id, v_api_key_name, true, v_scopes;
|
|
RETURN;
|
|
END IF;
|
|
|
|
UPDATE public.api_keys
|
|
SET request_count = request_count + 1,
|
|
last_used_at = now()
|
|
WHERE key_hash = p_key_hash;
|
|
|
|
RETURN QUERY SELECT v_user_id, v_company_id, v_api_key_id, v_api_key_name, false, v_scopes;
|
|
END;
|
|
$$;
|
|
|
|
-- =============================================================================
|
|
-- 4. PostgREST schema reload
|
|
-- =============================================================================
|
|
NOTIFY pgrst, 'reload schema';
|