2026-10-13 16:58:15 +02:00
'use strict' ;
const { z } = require ( 'zod' );
2026-10-13 19:41:27 +02:00
const application = [ 'sessions:save' , 'sessions:list' , 'sessions:restore' , 'vault:status' , 'vault:list' , 'vault:fill' , 'history:list' , 'history:go' , 'window:state' , 'window:control' , 'application:state' , 'settings:get' , 'settings:update' , 'bookmarks:list' , 'bookmarks:mutate' , 'tabs:create' , 'tabs:activate' , 'tabs:close' , 'tabs:group' , 'library:organize-plan' , 'library:organize-apply' ];
2026-10-13 16:58:15 +02:00
const AGENT_SCOPES = Object . freeze ({ sessions : 'sessions:list' , navigate : 'browser:navigate' , back : 'browser:navigate' , forward : 'browser:navigate' , reload : 'browser:navigate' , read : 'browser:read' , inspect : 'browser:read' , scroll : 'browser:interact' , click : 'browser:interact' , type : 'browser:interact' , select : 'browser:interact' , check : 'browser:interact' , ai : 'ai:request' ,... Object . fromEntries ( application . map ( action =>[ action , action ])), 'agent-request' : 'agent:request' });
const AgentAuditSchema = z . object ({ action : z . enum ( Object . keys ( AGENT_SCOPES )), scope : z . enum ([... new Set ( Object . values ( AGENT_SCOPES ))]), outcome : z . enum ([ 'authorized' , 'completed' , 'denied' , 'failed' ]), sessionId : z . string (). uuid (). optional (), principalId : z . string (). regex ( /^[A-Za-z0-9][A-Za-z0-9._:-]{0,79}$/ ). optional (), credentialGeneration : z . number (). int (). min ( 1 ). max ( Number . MAX_SAFE_INTEGER ). optional ()}). strict (). superRefine (( value , ctx )=>{
if (( value . principalId !== undefined ) !== ( value . credentialGeneration !== undefined )) ctx . addIssue ({ code : 'custom' , message : 'Agent principal attribution requires both identity and generation' });
if ( AGENT_SCOPES [ value . action ] !== value . scope ) ctx . addIssue ({ code : 'custom' , message : 'Agent action scope mismatch' });
if ( value . action === 'agent-request' &&! [ 'denied' , 'failed' ]. includes ( value . outcome )) ctx . addIssue ({ code : 'custom' , message : 'Unvalidated request cannot be authorized' });
});
// Only fixed control-plane facts cross the audit boundary; action arguments never do.
function agentAuditFacts ( value ){ return AgentAuditSchema . parse ({... value , scope : value ? . scope ?? AGENT_SCOPES [ value ? . action ]});}
function agentFactsForEvent ( kind , value ){ if ( ! [ 'agent.action' , 'agent.denied' ]. includes ( kind )) return null ; const result = AgentAuditSchema . safeParse ( value ); return result . success && (( kind === 'agent.action' ) === [ 'authorized' , 'completed' ]. includes ( result . data . outcome )) ? result . data : null ;}
module . exports = { AGENT_SCOPES , AgentAuditSchema , agentAuditFacts , agentFactsForEvent };