2424from pathlib import Path
2525from typing import Any
2626
27- from .config import SecureAgentConfig , WrappedAgent
28- from .detection import (
29- DetectionResult ,
30- Framework ,
31- FrameworkDetector ,
32- UnsupportedFrameworkError ,
27+ from .adapters import (
28+ AdapterError ,
29+ AdapterResult ,
30+ create_adapter ,
31+ create_browser_use_adapter ,
32+ create_browser_use_runtime ,
33+ create_langchain_adapter ,
34+ create_playwright_adapter ,
35+ create_pydantic_ai_adapter ,
3336)
37+ from .config import SecureAgentConfig , WrappedAgent
38+ from .detection import DetectionResult , Framework , FrameworkDetector , UnsupportedFrameworkError
3439
3540__version__ = "0.1.0"
3641
4348 "Framework" ,
4449 "FrameworkDetector" ,
4550 "DetectionResult" ,
51+ # Adapters
52+ "AdapterResult" ,
53+ "AdapterError" ,
54+ "create_adapter" ,
55+ "create_browser_use_adapter" ,
56+ "create_browser_use_runtime" ,
57+ "create_playwright_adapter" ,
58+ "create_langchain_adapter" ,
59+ "create_pydantic_ai_adapter" ,
4660 # Modes
4761 "MODE_STRICT" ,
4862 "MODE_PERMISSIVE" ,
@@ -318,9 +332,7 @@ def run(self, task: str | None = None) -> Any:
318332 if self ._wrapped .framework == Framework .PYDANTIC_AI .value :
319333 return self ._run_pydantic_ai (task )
320334
321- raise NotImplementedError (
322- f"run() not implemented for framework: { self ._wrapped .framework } "
323- )
335+ raise NotImplementedError (f"run() not implemented for framework: { self ._wrapped .framework } " )
324336
325337 def _run_browser_use (self , task : str | None ) -> Any :
326338 """Run browser-use agent with authorization."""
@@ -331,8 +343,8 @@ def _run_browser_use(self, task: str | None) -> Any:
331343 agent = self ._wrapped .original
332344
333345 # Override task if provided
334- if task is not None :
335- agent . task = task
346+ if task is not None and hasattr ( agent , "task" ) :
347+ setattr ( agent , " task" , task )
336348
337349 # Check if agent has a run method
338350 if hasattr (agent , "run" ):
@@ -374,9 +386,7 @@ def _run_langchain(self, task: str | None) -> Any:
374386
375387 def _run_pydantic_ai (self , task : str | None ) -> Any :
376388 """Run PydanticAI agent with authorization."""
377- raise NotImplementedError (
378- "PydanticAI integration not yet implemented."
379- )
389+ raise NotImplementedError ("PydanticAI integration not yet implemented." )
380390
381391 @classmethod
382392 def attach (cls , agent : Any , ** kwargs : Any ) -> SecureAgent :
@@ -414,6 +424,195 @@ def get_pre_action_authorizer(self) -> Any:
414424 """
415425 return self ._create_pre_action_authorizer ()
416426
427+ def get_adapter (
428+ self ,
429+ tracer : Any | None = None ,
430+ snapshot_options : Any | None = None ,
431+ predicate_api_key : str | None = None ,
432+ ** kwargs : Any ,
433+ ) -> AdapterResult :
434+ """
435+ Get an adapter for the wrapped agent.
436+
437+ This creates the appropriate adapter based on the detected framework,
438+ wiring together BrowserUseAdapter, PredicateBrowserUsePlugin,
439+ SentienceLangChainCore, or AgentRuntime.from_playwright_page().
440+
441+ Args:
442+ tracer: Optional Tracer for event emission
443+ snapshot_options: Optional SnapshotOptions
444+ predicate_api_key: Optional API key for Predicate API
445+ **kwargs: Additional framework-specific options
446+
447+ Returns:
448+ AdapterResult with initialized components
449+
450+ Raises:
451+ AdapterError: If adapter initialization fails
452+
453+ Example:
454+ secure = SecureAgent(agent=my_browser_use_agent, policy="policy.yaml")
455+ adapter = secure.get_adapter()
456+
457+ # Use plugin lifecycle hooks
458+ result = await agent.run(
459+ on_step_start=adapter.plugin.on_step_start,
460+ on_step_end=adapter.plugin.on_step_end,
461+ )
462+ """
463+ return create_adapter (
464+ agent = self ._wrapped .original ,
465+ framework = self .framework ,
466+ tracer = tracer ,
467+ snapshot_options = snapshot_options ,
468+ predicate_api_key = predicate_api_key ,
469+ ** kwargs ,
470+ )
471+
472+ async def get_runtime_async (
473+ self ,
474+ tracer : Any | None = None ,
475+ snapshot_options : Any | None = None ,
476+ predicate_api_key : str | None = None ,
477+ ) -> Any :
478+ """
479+ Get an initialized AgentRuntime for the wrapped agent (async).
480+
481+ This is useful for browser-use agents where runtime initialization
482+ requires async operations.
483+
484+ Args:
485+ tracer: Optional Tracer for event emission
486+ snapshot_options: Optional SnapshotOptions
487+ predicate_api_key: Optional API key for Predicate API
488+
489+ Returns:
490+ AgentRuntime instance
491+
492+ Raises:
493+ AdapterError: If runtime initialization fails
494+
495+ Example:
496+ secure = SecureAgent(agent=my_browser_use_agent, policy="policy.yaml")
497+ runtime = await secure.get_runtime_async()
498+
499+ # Use with RuntimeAgent
500+ from predicate.runtime_agent import RuntimeAgent
501+ runtime_agent = RuntimeAgent(
502+ runtime=runtime,
503+ executor=my_llm,
504+ pre_action_authorizer=secure.get_pre_action_authorizer(),
505+ )
506+ """
507+ if self .framework == Framework .BROWSER_USE :
508+ result = await create_browser_use_runtime (
509+ agent = self ._wrapped .original ,
510+ tracer = tracer ,
511+ snapshot_options = snapshot_options ,
512+ predicate_api_key = predicate_api_key ,
513+ )
514+ # Cache the runtime
515+ self ._wrapped .agent_runtime = result .agent_runtime
516+ return result .agent_runtime
517+
518+ if self .framework == Framework .PLAYWRIGHT :
519+ adapter = create_playwright_adapter (
520+ page = self ._wrapped .original ,
521+ tracer = tracer ,
522+ snapshot_options = snapshot_options ,
523+ predicate_api_key = predicate_api_key ,
524+ )
525+ self ._wrapped .agent_runtime = adapter .agent_runtime
526+ return adapter .agent_runtime
527+
528+ raise AdapterError (
529+ f"get_runtime_async() not supported for framework: { self .framework .value } " ,
530+ self .framework ,
531+ )
532+
533+ def get_browser_use_plugin (
534+ self ,
535+ tracer : Any | None = None ,
536+ snapshot_options : Any | None = None ,
537+ predicate_api_key : str | None = None ,
538+ ) -> Any :
539+ """
540+ Get a PredicateBrowserUsePlugin for browser-use lifecycle hooks.
541+
542+ This is the recommended way to integrate with browser-use agents.
543+
544+ Args:
545+ tracer: Optional Tracer for event emission
546+ snapshot_options: Optional SnapshotOptions
547+ predicate_api_key: Optional API key for Predicate API
548+
549+ Returns:
550+ PredicateBrowserUsePlugin instance
551+
552+ Raises:
553+ AdapterError: If framework is not browser-use
554+
555+ Example:
556+ secure = SecureAgent(agent=my_agent, policy="policy.yaml")
557+ plugin = secure.get_browser_use_plugin()
558+
559+ # Run with lifecycle hooks
560+ result = await agent.run(
561+ on_step_start=plugin.on_step_start,
562+ on_step_end=plugin.on_step_end,
563+ )
564+ """
565+ if self .framework != Framework .BROWSER_USE :
566+ raise AdapterError (
567+ "get_browser_use_plugin() only available for browser-use agents" ,
568+ self .framework ,
569+ )
570+
571+ adapter = create_browser_use_adapter (
572+ agent = self ._wrapped .original ,
573+ tracer = tracer ,
574+ snapshot_options = snapshot_options ,
575+ predicate_api_key = predicate_api_key ,
576+ )
577+ return adapter .plugin
578+
579+ def get_langchain_core (
580+ self ,
581+ browser : Any | None = None ,
582+ tracer : Any | None = None ,
583+ snapshot_options : Any | None = None ,
584+ predicate_api_key : str | None = None ,
585+ ) -> Any :
586+ """
587+ Get a SentienceLangChainCore for LangChain tool interception.
588+
589+ Args:
590+ browser: Optional browser instance for browser tools
591+ tracer: Optional Tracer for event emission
592+ snapshot_options: Optional SnapshotOptions
593+ predicate_api_key: Optional API key for Predicate API
594+
595+ Returns:
596+ SentienceLangChainCore instance
597+
598+ Raises:
599+ AdapterError: If framework is not LangChain
600+ """
601+ if self .framework != Framework .LANGCHAIN :
602+ raise AdapterError (
603+ "get_langchain_core() only available for LangChain agents" ,
604+ self .framework ,
605+ )
606+
607+ adapter = create_langchain_adapter (
608+ agent = self ._wrapped .original ,
609+ browser = browser ,
610+ tracer = tracer ,
611+ snapshot_options = snapshot_options ,
612+ predicate_api_key = predicate_api_key ,
613+ )
614+ return adapter .plugin
615+
417616 def __repr__ (self ) -> str :
418617 return (
419618 f"SecureAgent("
0 commit comments