Add inspected agent viewport export with policy-bound final release

This commit is contained in:
Karl committed 2026-10-13 21:39:47 +02:00
1 parent c8b6ff9cdd
commit e7d778aeac
24 files changed
+483 -32

No files matched your search

+9 -1
View File
@@ -417,13 +417,21 @@ async function ensureAgentAPI(){
const actor=agentActorStorage.getStore();await auditAgent({action:'click',outcome:'completed',sessionId:metadata.sessionId,...(actor?.principalId?{principalId:actor.principalId,generation:actor.generation}:{}),protectedInteraction:payload});
}});
Object.assign(actions,agentSessionControls());
actions.screenshot=require('./agent-visual-export.cjs').createAgentVisualExport({
getContext:id=>{const c=agentContext(id),actor=agentActorStorage.getStore();return {...c,ready:c.ready&&actorGrantAllowed(actor,'screenshot'),actor:c.actor||Object.freeze({agentId:'legacy-paired-agent',generation:1})};},contextCurrent:currentTabContext,
probeSensitive:require('./agent-visual-probe.cjs').probeVisualContext,capture:c=>capturePNG(c.webContents),
inspectLocal:async(bytes,p)=>{if(!localInspector)throw Error('Managed visual export refused');return diagnosticSpan('browser.inspect.local',{bytes:bytes.length},()=>localInspector.inspect(bytes,p));},
inspectEnterprise:(bytes,p)=>diagnosticSpan('browser.inspect.enterprise',{bytes:bytes.length},()=>scanEnterprise(bytes,p,'upload')),audit:auditAgent,
traceOperation:(operation,fn)=>diagnosticSpan('browser.visual',{operation},fn)
});
for(const action of ['click','type','select','check']){const original=actions[action];actions[action]=async(...args)=>{const tab=tabs.get(args[0]?.sessionId||activeTab().id),context=tabContext(tab.id);const checked=await checkVisual(tab);if(!currentTabContext(context)||checked.decision!=='allow')throw Error('Visual login protection has not approved this page');return original(...args);};}
for(const action of Object.keys(actions)){const original=actions[action];actions[action]=(args,context={})=>agentActorStorage.run({...context,action},()=>diagnosticSpan('browser.agent',{operation:action},async()=>{
if(context.principalId&&['navigate','back','forward','reload','history:go','vault:fill','vault:prepare-submit','click','type','select','check'].includes(action)){const tab=tabs.get(args.sessionId||activeTab().id);tab.security.agentAuthority={principalId:context.principalId,generation:context.generation,action};}
return original(args,context);
}));}
agentServiceMode=policy.agents.identityMode;agentService=await startAgentControl({actions,principalMode:agentServiceMode,principals:agentServiceMode==='individual'?effectiveAgentPolicy().agents.grants:[],
agentServiceMode=policy.agents.identityMode;agentService=await startAgentControl({actions,actionTimeouts:{screenshot:45000},principalMode:agentServiceMode,principals:agentServiceMode==='individual'?effectiveAgentPolicy().agents.grants:[],
traceOperation:(operation,fn)=>diagnosticSpan('browser.agent',{operation},fn),
traceRelease:(operation,fn)=>diagnostics.spanSync('browser.visual',diagnosticContext({operation}),fn),
onError:error=>{if(!app.isPackaged&&process.env.SAFETYNET_SMOKE_ARTIFACTS)fs.appendFileSync(path.join(process.env.SAFETYNET_SMOKE_ARTIFACTS,'smoke-entry.txt'),`Agent action diagnostic: ${error.reason}\n`);},
authorize:({action,args,scope,principalId,generation})=>{
const actor={principalId,generation,action};