Add/ai native supp (#385)
* 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
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { Loader2, Sparkles } from 'lucide-react'
|
||||
|
||||
export function AgentAutoCommitSettings() {
|
||||
const { toast } = useToast()
|
||||
const [enabled, setEnabled] = useState(false)
|
||||
const [maxAmount, setMaxAmount] = useState<string>('')
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
fetch('/api/settings')
|
||||
.then((r) => r.json())
|
||||
.then((body) => {
|
||||
if (cancelled) return
|
||||
const data = body?.data ?? {}
|
||||
setEnabled(Boolean(data.agent_auto_commit_enabled))
|
||||
setMaxAmount(
|
||||
data.agent_auto_commit_max_amount != null
|
||||
? String(data.agent_auto_commit_max_amount)
|
||||
: ''
|
||||
)
|
||||
setLoading(false)
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setLoading(false)
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [])
|
||||
|
||||
async function save() {
|
||||
setSaving(true)
|
||||
const parsedMax = maxAmount.trim() === '' ? null : Number(maxAmount)
|
||||
if (parsedMax !== null && (Number.isNaN(parsedMax) || parsedMax < 0)) {
|
||||
toast({ title: 'Ogiltigt belopp', description: 'Ange ett positivt tal eller lämna fältet tomt.', variant: 'destructive' })
|
||||
setSaving(false)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await fetch('/api/settings', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
agent_auto_commit_enabled: enabled,
|
||||
agent_auto_commit_max_amount: parsedMax,
|
||||
}),
|
||||
})
|
||||
|
||||
setSaving(false)
|
||||
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}))
|
||||
toast({
|
||||
title: 'Kunde inte spara',
|
||||
description: body?.error ?? 'Försök igen.',
|
||||
variant: 'destructive',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
toast({ title: 'Sparat', description: 'Inställningar för auto-godkännande uppdaterade.' })
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Sparkles className="h-4 w-4" />
|
||||
Auto-godkännande för agenter
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
När detta är aktivt får betrodda agenter (API-nycklar och Claude Desktop via OAuth)
|
||||
köra åtgärder med låg risk utan din godkännande. Hög-risk-åtgärder (periodlåsning,
|
||||
bokslut, fakturautskick m.m.) kräver alltid manuell granskning oavsett inställning.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{loading ? (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Loader2 className="h-4 w-4 animate-spin" /> Laddar…
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-start gap-3">
|
||||
<Switch
|
||||
id="agent_auto_commit_enabled"
|
||||
checked={enabled}
|
||||
onCheckedChange={setEnabled}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="agent_auto_commit_enabled">Aktivera auto-godkännande</Label>
|
||||
<p className="text-xs text-muted-foreground max-w-prose">
|
||||
Endast åtgärder klassificerade som <em>låg risk</em> (t.ex. skapa kund) körs
|
||||
automatiskt. Hög-risk är alltid stoppad och hamnar i kön för granskning.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 max-w-xs">
|
||||
<Label htmlFor="agent_auto_commit_max_amount">
|
||||
Maxbelopp per åtgärd (SEK)
|
||||
</Label>
|
||||
<Input
|
||||
id="agent_auto_commit_max_amount"
|
||||
type="number"
|
||||
inputMode="decimal"
|
||||
min={0}
|
||||
step={1}
|
||||
placeholder="Ingen gräns"
|
||||
value={maxAmount}
|
||||
onChange={(e) => setMaxAmount(e.target.value)}
|
||||
disabled={!enabled}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Lämna tomt för ingen gräns. Åtgärder över beloppet faller tillbaka till manuell
|
||||
granskning.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="pt-2">
|
||||
<Button onClick={save} disabled={saving}>
|
||||
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-2" /> : null}
|
||||
Spara
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user