isolated-agents-sdk 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- isolated_agents_sdk/__init__.py +575 -0
- isolated_agents_sdk/adapters/__init__.py +112 -0
- isolated_agents_sdk/adapters/audit/__init__.py +42 -0
- isolated_agents_sdk/adapters/audit/base.py +192 -0
- isolated_agents_sdk/adapters/audit/composite.py +100 -0
- isolated_agents_sdk/adapters/audit/file.py +420 -0
- isolated_agents_sdk/adapters/audit/stderr.py +110 -0
- isolated_agents_sdk/adapters/audit/telemetry.py +161 -0
- isolated_agents_sdk/adapters/audit/types.py +124 -0
- isolated_agents_sdk/adapters/base.py +125 -0
- isolated_agents_sdk/adapters/config.py +392 -0
- isolated_agents_sdk/adapters/container/__init__.py +49 -0
- isolated_agents_sdk/adapters/container/base.py +299 -0
- isolated_agents_sdk/adapters/container/docker.py +288 -0
- isolated_agents_sdk/adapters/container/kubernetes.py +237 -0
- isolated_agents_sdk/adapters/container/podman.py +533 -0
- isolated_agents_sdk/adapters/container/types.py +118 -0
- isolated_agents_sdk/adapters/exceptions.py +95 -0
- isolated_agents_sdk/adapters/factory.py +374 -0
- isolated_agents_sdk/adapters/policy/__init__.py +39 -0
- isolated_agents_sdk/adapters/policy/base.py +95 -0
- isolated_agents_sdk/adapters/policy/default.py +215 -0
- isolated_agents_sdk/adapters/policy/types.py +110 -0
- isolated_agents_sdk/adapters/registry.py +529 -0
- isolated_agents_sdk/adapters/storage/__init__.py +41 -0
- isolated_agents_sdk/adapters/storage/base.py +218 -0
- isolated_agents_sdk/adapters/storage/local.py +334 -0
- isolated_agents_sdk/adapters/storage/types.py +89 -0
- isolated_agents_sdk/agent_runner.py +512 -0
- isolated_agents_sdk/audit_logger.py +146 -0
- isolated_agents_sdk/composability.py +108 -0
- isolated_agents_sdk/container_provisioner.py +166 -0
- isolated_agents_sdk/decorators.py +322 -0
- isolated_agents_sdk/exceptions.py +189 -0
- isolated_agents_sdk/models.py +622 -0
- isolated_agents_sdk/output_collector.py +242 -0
- isolated_agents_sdk/policy_validator.py +81 -0
- isolated_agents_sdk/session_manager.py +480 -0
- isolated_agents_sdk-0.2.0.dist-info/METADATA +19 -0
- isolated_agents_sdk-0.2.0.dist-info/RECORD +41 -0
- isolated_agents_sdk-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
"""Isolated Agents SDK — Public API.
|
|
2
|
+
|
|
3
|
+
Securely run AI agents in rootless containers with advanced telemetry,
|
|
4
|
+
structured output validation, and framework-agnostic support.
|
|
5
|
+
|
|
6
|
+
Main Functions:
|
|
7
|
+
- run_agent() — Synchronous execution, blocks until agent completion.
|
|
8
|
+
- async_run_agent() — Asynchronous execution using asyncio.
|
|
9
|
+
- list_sessions() — Retrieve all currently active agent sessions.
|
|
10
|
+
|
|
11
|
+
Decorators:
|
|
12
|
+
- @isolated_agent — Run a function as an isolated agent.
|
|
13
|
+
- @policy, @network — Configure environment and sandbox constraints.
|
|
14
|
+
- @resources, @retry — Manage resource limits and resilience.
|
|
15
|
+
- @langchain, @crewai — Pre-configure for specific agent frameworks.
|
|
16
|
+
- @structured_output — Enforce JSON Schema validation on return values.
|
|
17
|
+
|
|
18
|
+
Telemetry & Adapters:
|
|
19
|
+
- configure_adapters() — Configure custom container, storage, or audit backends.
|
|
20
|
+
- get_adapter_registry() — Access initialized adapter instances.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import asyncio
|
|
26
|
+
import tempfile
|
|
27
|
+
import uuid
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Callable, Optional, Dict, Any
|
|
30
|
+
|
|
31
|
+
from isolated_agents_sdk.models import (
|
|
32
|
+
NetworkPolicy,
|
|
33
|
+
Policy,
|
|
34
|
+
AgentResult,
|
|
35
|
+
SessionInfo,
|
|
36
|
+
SessionMetrics,
|
|
37
|
+
AuditEvent,
|
|
38
|
+
)
|
|
39
|
+
from isolated_agents_sdk.exceptions import (
|
|
40
|
+
PodmanNotFoundError,
|
|
41
|
+
PolicyValidationError,
|
|
42
|
+
WorkingDirectoryError,
|
|
43
|
+
OutputSizeLimitError,
|
|
44
|
+
ContainerError,
|
|
45
|
+
)
|
|
46
|
+
from isolated_agents_sdk.policy_validator import PolicyValidator
|
|
47
|
+
from isolated_agents_sdk.audit_logger import AuditLogger
|
|
48
|
+
from isolated_agents_sdk.container_provisioner import ContainerProvisioner, ContainerHandle
|
|
49
|
+
from isolated_agents_sdk.agent_runner import AgentRunner
|
|
50
|
+
from isolated_agents_sdk.output_collector import OutputCollector
|
|
51
|
+
from isolated_agents_sdk.session_manager import SessionManager, IsolatedSession
|
|
52
|
+
|
|
53
|
+
# Import decorators
|
|
54
|
+
from isolated_agents_sdk.decorators import (
|
|
55
|
+
isolated_agent,
|
|
56
|
+
policy,
|
|
57
|
+
network,
|
|
58
|
+
resources,
|
|
59
|
+
dependencies,
|
|
60
|
+
env_vars,
|
|
61
|
+
forward_env,
|
|
62
|
+
timeout,
|
|
63
|
+
telemetry,
|
|
64
|
+
interactive,
|
|
65
|
+
structured_output,
|
|
66
|
+
base_image,
|
|
67
|
+
entrypoint,
|
|
68
|
+
retry,
|
|
69
|
+
cache,
|
|
70
|
+
langchain,
|
|
71
|
+
crewai,
|
|
72
|
+
node,
|
|
73
|
+
llamaindex,
|
|
74
|
+
selenium,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Composability API
|
|
78
|
+
from isolated_agents_sdk.composability import chain, parallel
|
|
79
|
+
|
|
80
|
+
# Adapter support (optional, backward compatible)
|
|
81
|
+
try:
|
|
82
|
+
from isolated_agents_sdk.adapters import (
|
|
83
|
+
AdapterRegistry,
|
|
84
|
+
AdapterConfig,
|
|
85
|
+
get_registry,
|
|
86
|
+
)
|
|
87
|
+
_ADAPTERS_AVAILABLE = True
|
|
88
|
+
_get_registry = get_registry
|
|
89
|
+
_AdapterConfig = AdapterConfig
|
|
90
|
+
except ImportError:
|
|
91
|
+
_ADAPTERS_AVAILABLE = False
|
|
92
|
+
_get_registry = None # type: ignore
|
|
93
|
+
_AdapterConfig = None # type: ignore
|
|
94
|
+
AdapterRegistry = None # type: ignore
|
|
95
|
+
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
# Module-level singletons — shared across all calls in the same process
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
_session_manager = SessionManager()
|
|
101
|
+
_policy_validator = PolicyValidator()
|
|
102
|
+
_adapter_config_applied = False
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# Adapter Configuration API
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
def configure_adapters(
|
|
110
|
+
config: Optional[Dict[str, Any]] = None,
|
|
111
|
+
config_file: Optional[str | Path] = None,
|
|
112
|
+
from_env: bool = False,
|
|
113
|
+
) -> None:
|
|
114
|
+
"""Configure adapters for the SDK (optional, backward compatible).
|
|
115
|
+
|
|
116
|
+
This function allows you to configure custom adapters for container runtime,
|
|
117
|
+
storage, audit logging, and policy validation. If not called, the SDK uses
|
|
118
|
+
default implementations (Podman, local filesystem, file-based audit, schema validation).
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
config: Dictionary with adapter configuration. Example:
|
|
122
|
+
{
|
|
123
|
+
"container": {"type": "podman", "config": {...}},
|
|
124
|
+
"storage": {"type": "local", "config": {...}},
|
|
125
|
+
"audit": {"type": "file", "config": {...}},
|
|
126
|
+
"policy": {"type": "default", "config": {...}}
|
|
127
|
+
}
|
|
128
|
+
config_file: Path to YAML or JSON configuration file
|
|
129
|
+
from_env: Load configuration from environment variables (ISOLATED_AGENTS_*)
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
ImportError: If adapter support is not available
|
|
133
|
+
ValueError: If configuration is invalid
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
>>> configure_adapters(config={
|
|
137
|
+
... "container": {"type": "podman"},
|
|
138
|
+
... "storage": {"type": "local"}
|
|
139
|
+
... })
|
|
140
|
+
"""
|
|
141
|
+
global _adapter_config_applied
|
|
142
|
+
|
|
143
|
+
if not _ADAPTERS_AVAILABLE or _get_registry is None or _AdapterConfig is None:
|
|
144
|
+
raise ImportError(
|
|
145
|
+
"Adapter support is not available. "
|
|
146
|
+
"Ensure isolated_agents_sdk.adapters package is installed."
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
registry = _get_registry()
|
|
150
|
+
|
|
151
|
+
# Load configuration
|
|
152
|
+
if config_file:
|
|
153
|
+
adapter_config = _AdapterConfig.from_file(str(config_file))
|
|
154
|
+
elif from_env:
|
|
155
|
+
adapter_config = _AdapterConfig.from_env()
|
|
156
|
+
elif config:
|
|
157
|
+
adapter_config = _AdapterConfig.from_dict(config)
|
|
158
|
+
else:
|
|
159
|
+
raise ValueError("Must provide config, config_file, or from_env=True")
|
|
160
|
+
|
|
161
|
+
# Initialize adapters in registry
|
|
162
|
+
registry.initialize_from_config(adapter_config)
|
|
163
|
+
_adapter_config_applied = True
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_adapter_registry() -> Optional[Any]:
|
|
167
|
+
"""Get the global adapter registry (if adapters are available).
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
AdapterRegistry instance or None if adapters not available
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
>>> registry = get_adapter_registry()
|
|
174
|
+
>>> if registry:
|
|
175
|
+
... container_adapter = registry.get_container_adapter()
|
|
176
|
+
"""
|
|
177
|
+
if not _ADAPTERS_AVAILABLE or _get_registry is None:
|
|
178
|
+
return None
|
|
179
|
+
return _get_registry()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ---------------------------------------------------------------------------
|
|
183
|
+
# Public API
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
def run_agent(
|
|
187
|
+
agent: Optional[Callable],
|
|
188
|
+
working_dir: str | Path,
|
|
189
|
+
policy: Optional[Policy] = None,
|
|
190
|
+
host_output_path: Optional[str | Path] = None,
|
|
191
|
+
adapter_config: Optional[Dict[str, Any]] = None,
|
|
192
|
+
agent_args: tuple = (),
|
|
193
|
+
agent_kwargs: Optional[Dict[str, Any]] = None,
|
|
194
|
+
) -> AgentResult:
|
|
195
|
+
"""Launch *agent* in an isolated rootless Podman container and block until completion.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
agent: Any callable to execute inside the container (ignored if policy.entrypoint is set).
|
|
199
|
+
working_dir: Host path to the working directory to mount into the container.
|
|
200
|
+
policy: Optional :class:`Policy` describing resource/network/fs constraints.
|
|
201
|
+
Defaults are applied when ``None`` is passed.
|
|
202
|
+
host_output_path: Optional host directory where output artifacts are written
|
|
203
|
+
and persist after this call returns. When ``None``, a temporary directory
|
|
204
|
+
is used — artifacts are still returned as in-memory bytes in
|
|
205
|
+
``AgentResult.artifacts``, but no files are kept on disk.
|
|
206
|
+
adapter_config: Optional adapter configuration for this specific run.
|
|
207
|
+
If provided, temporarily configures adapters for this execution only.
|
|
208
|
+
agent_args: Positional arguments to pass to the *agent* callable.
|
|
209
|
+
agent_kwargs: Keyword arguments to pass to the *agent* callable.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
An :class:`AgentResult` with the agent's exit code and any output artifacts.
|
|
213
|
+
|
|
214
|
+
Raises:
|
|
215
|
+
PodmanNotFoundError: If Podman is not installed or not on PATH.
|
|
216
|
+
PolicyValidationError: If the policy contains unknown or invalid fields.
|
|
217
|
+
WorkingDirectoryError: If *working_dir* does not exist.
|
|
218
|
+
OutputSizeLimitError: If output exceeds the policy's ``max_output_bytes`` limit.
|
|
219
|
+
RuntimeError: If called from within a running asyncio event loop. Use
|
|
220
|
+
``await async_run_agent(...)`` instead in that context.
|
|
221
|
+
|
|
222
|
+
Example:
|
|
223
|
+
>>> # Basic usage (uses default adapters)
|
|
224
|
+
>>> result = run_agent(my_agent, "./workspace")
|
|
225
|
+
|
|
226
|
+
>>> # With custom adapter configuration
|
|
227
|
+
>>> result = run_agent(
|
|
228
|
+
... my_agent,
|
|
229
|
+
... "./workspace",
|
|
230
|
+
... adapter_config={
|
|
231
|
+
... "container": {"type": "podman"},
|
|
232
|
+
... "storage": {"type": "local"}
|
|
233
|
+
... }
|
|
234
|
+
... )
|
|
235
|
+
"""
|
|
236
|
+
try:
|
|
237
|
+
loop = asyncio.get_running_loop()
|
|
238
|
+
except RuntimeError:
|
|
239
|
+
loop = None
|
|
240
|
+
|
|
241
|
+
if loop is not None and loop.is_running():
|
|
242
|
+
raise RuntimeError(
|
|
243
|
+
"run_agent() cannot be called from within a running asyncio event loop "
|
|
244
|
+
"(e.g. inside an async function, Jupyter notebook, or async framework). "
|
|
245
|
+
"Use 'await async_run_agent(...)' instead."
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
return asyncio.run(
|
|
249
|
+
async_run_agent(
|
|
250
|
+
agent,
|
|
251
|
+
working_dir,
|
|
252
|
+
policy,
|
|
253
|
+
host_output_path,
|
|
254
|
+
adapter_config,
|
|
255
|
+
agent_args,
|
|
256
|
+
agent_kwargs,
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
async def async_run_agent(
|
|
262
|
+
agent: Optional[Callable],
|
|
263
|
+
working_dir: str | Path,
|
|
264
|
+
policy: Optional[Policy] = None,
|
|
265
|
+
host_output_path: Optional[str | Path] = None,
|
|
266
|
+
adapter_config: Optional[Dict[str, Any]] = None,
|
|
267
|
+
agent_args: tuple = (),
|
|
268
|
+
agent_kwargs: Optional[Dict[str, Any]] = None,
|
|
269
|
+
) -> AgentResult:
|
|
270
|
+
"""Launch *agent* in an isolated container asynchronously."""
|
|
271
|
+
# Apply adapter configuration if provided
|
|
272
|
+
if adapter_config and _ADAPTERS_AVAILABLE:
|
|
273
|
+
configure_adapters(config=adapter_config)
|
|
274
|
+
|
|
275
|
+
registry = get_adapter_registry() if _ADAPTERS_AVAILABLE else None
|
|
276
|
+
container_adapter = registry.get_container_adapter() if registry else None
|
|
277
|
+
# If host_output_path is provided, we'll let OutputCollector create a local adapter for it
|
|
278
|
+
storage_adapter = registry.get_storage_adapter() if (registry and not host_output_path) else None
|
|
279
|
+
|
|
280
|
+
validated_policy = await _policy_validator.validate(policy)
|
|
281
|
+
|
|
282
|
+
# Advanced Logging: Setup audit logger with telemetry if enabled
|
|
283
|
+
if validated_policy.enable_telemetry and _ADAPTERS_AVAILABLE:
|
|
284
|
+
from isolated_agents_sdk.adapters.audit.file import FileAuditAdapter
|
|
285
|
+
from isolated_agents_sdk.adapters.audit.telemetry import TelemetryAuditAdapter
|
|
286
|
+
from isolated_agents_sdk.adapters.audit.composite import CompositeAuditAdapter
|
|
287
|
+
|
|
288
|
+
adapters = [TelemetryAuditAdapter()]
|
|
289
|
+
if validated_policy.log_output_path:
|
|
290
|
+
adapters.append(FileAuditAdapter(log_file=validated_policy.log_output_path))
|
|
291
|
+
|
|
292
|
+
audit_logger = AuditLogger(adapter=CompositeAuditAdapter(adapters))
|
|
293
|
+
else:
|
|
294
|
+
audit_logger = AuditLogger(log_output_path=validated_policy.log_output_path)
|
|
295
|
+
|
|
296
|
+
session_id = str(uuid.uuid4())
|
|
297
|
+
if validated_policy.entrypoint:
|
|
298
|
+
agent_id = " ".join(validated_policy.entrypoint[:2])
|
|
299
|
+
else:
|
|
300
|
+
agent_id = getattr(agent, "__name__", "agent")
|
|
301
|
+
|
|
302
|
+
# Log session creation for telemetry
|
|
303
|
+
await audit_logger.log_event(
|
|
304
|
+
event_type="session_created",
|
|
305
|
+
session_id=session_id,
|
|
306
|
+
agent_id=agent_id,
|
|
307
|
+
payload={
|
|
308
|
+
"runtime": container_adapter.get_adapter_name() if container_adapter else "Podman",
|
|
309
|
+
"storage": "Local Filesystem",
|
|
310
|
+
"logger": "Composite" if validated_policy.enable_telemetry else "File"
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Log policy details for telemetry
|
|
315
|
+
await audit_logger.log_event(
|
|
316
|
+
event_type="policy_validated",
|
|
317
|
+
session_id=session_id,
|
|
318
|
+
agent_id=agent_id,
|
|
319
|
+
payload={
|
|
320
|
+
"cpu_cores": validated_policy.cpu_cores,
|
|
321
|
+
"memory_mb": validated_policy.memory_mb,
|
|
322
|
+
"network_enabled": not validated_policy.network.disabled,
|
|
323
|
+
"timeout_seconds": validated_policy.timeout_seconds
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
provisioner = ContainerProvisioner(
|
|
328
|
+
adapter=container_adapter,
|
|
329
|
+
audit_logger=audit_logger
|
|
330
|
+
)
|
|
331
|
+
handle = await provisioner.provision(
|
|
332
|
+
working_dir=working_dir,
|
|
333
|
+
policy=validated_policy,
|
|
334
|
+
session_id=session_id,
|
|
335
|
+
agent_id=agent_id,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
runner = AgentRunner(
|
|
339
|
+
handle=handle,
|
|
340
|
+
adapter=container_adapter,
|
|
341
|
+
audit_logger=audit_logger
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Update global session manager with current adapter if needed
|
|
345
|
+
if container_adapter:
|
|
346
|
+
_session_manager._adapter = container_adapter
|
|
347
|
+
|
|
348
|
+
# Register the session so cleanup handlers are in place before execution
|
|
349
|
+
_session_manager.register_session(
|
|
350
|
+
session_id=session_id,
|
|
351
|
+
container_id=handle.container_id,
|
|
352
|
+
agent_id=agent_id,
|
|
353
|
+
process=None,
|
|
354
|
+
policy=validated_policy,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
exit_code = 1
|
|
358
|
+
result: Optional[AgentResult] = None
|
|
359
|
+
try:
|
|
360
|
+
run_result = await runner.run(
|
|
361
|
+
agent=agent,
|
|
362
|
+
policy=validated_policy,
|
|
363
|
+
session_id=session_id,
|
|
364
|
+
agent_id=agent_id,
|
|
365
|
+
agent_args=agent_args,
|
|
366
|
+
agent_kwargs=agent_kwargs,
|
|
367
|
+
)
|
|
368
|
+
exit_code = run_result.exit_code
|
|
369
|
+
|
|
370
|
+
# Collect output artifacts from the container.
|
|
371
|
+
collector = OutputCollector(
|
|
372
|
+
container_adapter=container_adapter,
|
|
373
|
+
storage_adapter=storage_adapter,
|
|
374
|
+
audit_logger=audit_logger
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
# If host_output_path is not provided, create a temporary directory
|
|
378
|
+
effective_host_output_path = host_output_path
|
|
379
|
+
if effective_host_output_path is None:
|
|
380
|
+
effective_host_output_path = tempfile.mkdtemp(prefix="agent_output_")
|
|
381
|
+
|
|
382
|
+
result = await collector.collect(
|
|
383
|
+
container_id=handle.container_id,
|
|
384
|
+
policy=validated_policy,
|
|
385
|
+
host_output_path=effective_host_output_path,
|
|
386
|
+
exit_code=exit_code,
|
|
387
|
+
session_id=session_id,
|
|
388
|
+
agent_id=agent_id,
|
|
389
|
+
)
|
|
390
|
+
finally:
|
|
391
|
+
await _session_manager.complete_session(session_id, exit_code)
|
|
392
|
+
|
|
393
|
+
return result # type: ignore[return-value]
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def list_sessions() -> list[SessionInfo]:
|
|
397
|
+
"""Return all currently active sessions and their associated container identifiers.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
A list of :class:`SessionInfo` objects, one per active session.
|
|
401
|
+
"""
|
|
402
|
+
return _session_manager.list_sessions()
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
async def get_session_metrics(session_id: str) -> SessionMetrics:
|
|
406
|
+
"""Return CPU and memory usage metrics for an active session.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
session_id: The session to query.
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
A :class:`SessionMetrics` with ``cpu_percent`` and ``memory_mb``.
|
|
413
|
+
|
|
414
|
+
Raises:
|
|
415
|
+
KeyError: If *session_id* is not found in the active registry.
|
|
416
|
+
"""
|
|
417
|
+
return await _session_manager.get_session_metrics(session_id)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
async def exec_in_session(
|
|
421
|
+
session_id: str,
|
|
422
|
+
command: list[str],
|
|
423
|
+
) -> tuple[int, str, str]:
|
|
424
|
+
"""Execute a command inside an existing, isolated container.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
session_id: The active session ID.
|
|
428
|
+
command: The command list to execute.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
A tuple of (exit_code, stdout, stderr).
|
|
432
|
+
"""
|
|
433
|
+
return await _session_manager.exec_in_session(session_id, command)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
async def sync_artifact(
|
|
437
|
+
session_id: str,
|
|
438
|
+
container_path: str,
|
|
439
|
+
host_path: str | Path,
|
|
440
|
+
) -> None:
|
|
441
|
+
"""Copy a specific file from a running agent's workspace to the host.
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
session_id: The active session ID.
|
|
445
|
+
container_path: Path to the file inside the container.
|
|
446
|
+
host_path: Destination path on the host.
|
|
447
|
+
"""
|
|
448
|
+
await _session_manager.sync_artifact(session_id, container_path, host_path)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
async def start_agent_daemon(
|
|
452
|
+
agent: Optional[Callable],
|
|
453
|
+
working_dir: str | Path,
|
|
454
|
+
policy: Optional[Policy] = None,
|
|
455
|
+
) -> SessionInfo:
|
|
456
|
+
"""Start an agent in the background and return a handle immediately.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
agent: Any callable to execute inside the container (ignored if policy.entrypoint is set).
|
|
460
|
+
working_dir: Host path to the working directory.
|
|
461
|
+
policy: Optional :class:`Policy`.
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
A :class:`SessionInfo` object describing the newly created session.
|
|
465
|
+
"""
|
|
466
|
+
validated_policy = await _policy_validator.validate(policy)
|
|
467
|
+
|
|
468
|
+
# Advanced Logging: Setup audit logger with telemetry if enabled
|
|
469
|
+
if validated_policy.enable_telemetry and _ADAPTERS_AVAILABLE:
|
|
470
|
+
from isolated_agents_sdk.adapters.audit.file import FileAuditAdapter
|
|
471
|
+
from isolated_agents_sdk.adapters.audit.telemetry import TelemetryAuditAdapter
|
|
472
|
+
from isolated_agents_sdk.adapters.audit.composite import CompositeAuditAdapter
|
|
473
|
+
|
|
474
|
+
adapters = [TelemetryAuditAdapter()]
|
|
475
|
+
if validated_policy.log_output_path:
|
|
476
|
+
adapters.append(FileAuditAdapter(log_file=validated_policy.log_output_path))
|
|
477
|
+
|
|
478
|
+
audit_logger = AuditLogger(adapter=CompositeAuditAdapter(adapters))
|
|
479
|
+
else:
|
|
480
|
+
audit_logger = AuditLogger(log_output_path=validated_policy.log_output_path)
|
|
481
|
+
|
|
482
|
+
session_id = str(uuid.uuid4())
|
|
483
|
+
if validated_policy.entrypoint:
|
|
484
|
+
agent_id = " ".join(validated_policy.entrypoint[:2])
|
|
485
|
+
else:
|
|
486
|
+
agent_id = getattr(agent, "__name__", "agent")
|
|
487
|
+
|
|
488
|
+
provisioner = ContainerProvisioner(audit_logger=audit_logger)
|
|
489
|
+
handle = await provisioner.provision(
|
|
490
|
+
working_dir=working_dir,
|
|
491
|
+
policy=validated_policy,
|
|
492
|
+
session_id=session_id,
|
|
493
|
+
agent_id=agent_id,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# Register the session
|
|
497
|
+
_session_manager.register_session(
|
|
498
|
+
session_id=session_id,
|
|
499
|
+
container_id=handle.container_id,
|
|
500
|
+
agent_id=agent_id,
|
|
501
|
+
process=None,
|
|
502
|
+
policy=validated_policy,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
# Launch the agent in the background
|
|
506
|
+
async def _background_runner():
|
|
507
|
+
runner = AgentRunner(handle=handle, audit_logger=audit_logger)
|
|
508
|
+
exit_code = 1
|
|
509
|
+
try:
|
|
510
|
+
run_result = await runner.run(
|
|
511
|
+
agent=agent,
|
|
512
|
+
policy=validated_policy,
|
|
513
|
+
session_id=session_id,
|
|
514
|
+
agent_id=agent_id,
|
|
515
|
+
)
|
|
516
|
+
exit_code = run_result.exit_code
|
|
517
|
+
finally:
|
|
518
|
+
await _session_manager.complete_session(session_id, exit_code)
|
|
519
|
+
|
|
520
|
+
asyncio.create_task(_background_runner())
|
|
521
|
+
|
|
522
|
+
# Return the session info immediately
|
|
523
|
+
sessions = _session_manager.list_sessions()
|
|
524
|
+
for s in sessions:
|
|
525
|
+
if s.session_id == session_id:
|
|
526
|
+
return s
|
|
527
|
+
|
|
528
|
+
raise RuntimeError("Failed to retrieve session info after registration.")
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
__all__ = [
|
|
532
|
+
# Entry-point functions
|
|
533
|
+
"run_agent",
|
|
534
|
+
"async_run_agent",
|
|
535
|
+
"start_agent_daemon",
|
|
536
|
+
"isolated_agent",
|
|
537
|
+
"policy",
|
|
538
|
+
"network",
|
|
539
|
+
"resources",
|
|
540
|
+
"dependencies",
|
|
541
|
+
"env_vars",
|
|
542
|
+
"forward_env",
|
|
543
|
+
"timeout",
|
|
544
|
+
"telemetry",
|
|
545
|
+
"interactive",
|
|
546
|
+
"list_sessions",
|
|
547
|
+
"get_session_metrics",
|
|
548
|
+
"exec_in_session",
|
|
549
|
+
"sync_artifact",
|
|
550
|
+
# Adapter configuration (optional)
|
|
551
|
+
"configure_adapters",
|
|
552
|
+
"get_adapter_registry",
|
|
553
|
+
# Models
|
|
554
|
+
"NetworkPolicy",
|
|
555
|
+
"Policy",
|
|
556
|
+
"AgentResult",
|
|
557
|
+
"SessionInfo",
|
|
558
|
+
"SessionMetrics",
|
|
559
|
+
"AuditEvent",
|
|
560
|
+
# Exceptions
|
|
561
|
+
"PodmanNotFoundError",
|
|
562
|
+
"PolicyValidationError",
|
|
563
|
+
"WorkingDirectoryError",
|
|
564
|
+
"OutputSizeLimitError",
|
|
565
|
+
"ContainerError",
|
|
566
|
+
# Components (for advanced use)
|
|
567
|
+
"PolicyValidator",
|
|
568
|
+
"AuditLogger",
|
|
569
|
+
"ContainerProvisioner",
|
|
570
|
+
"ContainerHandle",
|
|
571
|
+
"AgentRunner",
|
|
572
|
+
"OutputCollector",
|
|
573
|
+
"SessionManager",
|
|
574
|
+
"IsolatedSession",
|
|
575
|
+
]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Adapter interfaces and implementations for the Isolated Agents SDK.
|
|
2
|
+
|
|
3
|
+
This package provides pluggable adapters for:
|
|
4
|
+
- Container runtimes (Podman, Docker, Kubernetes)
|
|
5
|
+
- Storage backends (Local, S3, Azure, GCS)
|
|
6
|
+
- Audit loggers (File, Database, CloudWatch, Datadog)
|
|
7
|
+
- Policy validators (Default, OPA, Custom)
|
|
8
|
+
|
|
9
|
+
Example usage:
|
|
10
|
+
from isolated_agents_sdk.adapters.container import PodmanAdapter
|
|
11
|
+
from isolated_agents_sdk.factory import AdapterFactory
|
|
12
|
+
|
|
13
|
+
# Register and create adapter
|
|
14
|
+
adapter = await AdapterFactory.create_container_adapter("podman")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from isolated_agents_sdk.adapters.audit.base import AuditAdapter
|
|
18
|
+
from isolated_agents_sdk.adapters.audit.file import FileAuditAdapter
|
|
19
|
+
from isolated_agents_sdk.adapters.audit.types import (
|
|
20
|
+
AuditEvent,
|
|
21
|
+
AuditQuery,
|
|
22
|
+
EventType,
|
|
23
|
+
)
|
|
24
|
+
from isolated_agents_sdk.adapters.base import BaseAdapter
|
|
25
|
+
from isolated_agents_sdk.adapters.config import AdapterConfig, load_config
|
|
26
|
+
from isolated_agents_sdk.adapters.container.base import ContainerRuntimeAdapter
|
|
27
|
+
from isolated_agents_sdk.adapters.container.podman import PodmanAdapter
|
|
28
|
+
from isolated_agents_sdk.adapters.container.types import (
|
|
29
|
+
ContainerHandle,
|
|
30
|
+
ContainerStats,
|
|
31
|
+
ExecResult,
|
|
32
|
+
Mount,
|
|
33
|
+
NetworkConfig,
|
|
34
|
+
ResourceLimits,
|
|
35
|
+
SecurityConfig,
|
|
36
|
+
)
|
|
37
|
+
from isolated_agents_sdk.adapters.exceptions import (
|
|
38
|
+
AdapterConfigurationError,
|
|
39
|
+
AdapterError,
|
|
40
|
+
AdapterInitializationError,
|
|
41
|
+
AdapterNotFoundError,
|
|
42
|
+
AdapterOperationError,
|
|
43
|
+
)
|
|
44
|
+
from isolated_agents_sdk.adapters.factory import AdapterFactory
|
|
45
|
+
from isolated_agents_sdk.adapters.policy.base import PolicyValidator
|
|
46
|
+
from isolated_agents_sdk.adapters.policy.default import DefaultPolicyValidator
|
|
47
|
+
from isolated_agents_sdk.adapters.policy.types import (
|
|
48
|
+
PolicyConstraints,
|
|
49
|
+
PolicyValidationResult,
|
|
50
|
+
ValidationError,
|
|
51
|
+
)
|
|
52
|
+
from isolated_agents_sdk.adapters.registry import AdapterRegistry, get_registry
|
|
53
|
+
from isolated_agents_sdk.adapters.storage.base import StorageAdapter
|
|
54
|
+
from isolated_agents_sdk.adapters.storage.local import LocalStorageAdapter
|
|
55
|
+
from isolated_agents_sdk.adapters.storage.types import (
|
|
56
|
+
ArtifactMetadata,
|
|
57
|
+
StorageLocation,
|
|
58
|
+
StorageStats,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
# Base classes
|
|
63
|
+
"BaseAdapter",
|
|
64
|
+
"ContainerRuntimeAdapter",
|
|
65
|
+
"StorageAdapter",
|
|
66
|
+
"AuditAdapter",
|
|
67
|
+
"PolicyValidator",
|
|
68
|
+
# Container adapters
|
|
69
|
+
"PodmanAdapter",
|
|
70
|
+
# Storage adapters
|
|
71
|
+
"LocalStorageAdapter",
|
|
72
|
+
# Audit adapters
|
|
73
|
+
"FileAuditAdapter",
|
|
74
|
+
# Policy adapters
|
|
75
|
+
"DefaultPolicyValidator",
|
|
76
|
+
# Factory
|
|
77
|
+
"AdapterFactory",
|
|
78
|
+
# Configuration
|
|
79
|
+
"AdapterConfig",
|
|
80
|
+
"load_config",
|
|
81
|
+
# Registry
|
|
82
|
+
"AdapterRegistry",
|
|
83
|
+
"get_registry",
|
|
84
|
+
# Container types
|
|
85
|
+
"ContainerHandle",
|
|
86
|
+
"ContainerStats",
|
|
87
|
+
"ExecResult",
|
|
88
|
+
"Mount",
|
|
89
|
+
"NetworkConfig",
|
|
90
|
+
"ResourceLimits",
|
|
91
|
+
"SecurityConfig",
|
|
92
|
+
# Storage types
|
|
93
|
+
"ArtifactMetadata",
|
|
94
|
+
"StorageLocation",
|
|
95
|
+
"StorageStats",
|
|
96
|
+
# Audit types
|
|
97
|
+
"AuditEvent",
|
|
98
|
+
"AuditQuery",
|
|
99
|
+
"EventType",
|
|
100
|
+
# Policy types
|
|
101
|
+
"PolicyConstraints",
|
|
102
|
+
"PolicyValidationResult",
|
|
103
|
+
"ValidationError",
|
|
104
|
+
# Exceptions
|
|
105
|
+
"AdapterError",
|
|
106
|
+
"AdapterNotFoundError",
|
|
107
|
+
"AdapterConfigurationError",
|
|
108
|
+
"AdapterInitializationError",
|
|
109
|
+
"AdapterOperationError",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
# Made with Bob
|