Objetivo
Section titled “Objetivo”Extender el runtime agentic para escribir archivos exclusivamente dentro de /tmp/opencode/sandbox/ y /opt/ai-lab/sandbox/, con snapshots pre-mutación, rollback checksum-based, governance completo, y rastro de auditoría.
Filosofía
Section titled “Filosofía”28.2 → readonly executor (comandos seguros, shell=False)28.3 → sandbox write executor (Python I/O, governance, rollback) ← ESTAMOS AQUÍ28.4 → tool contracts (futuro)Principio clave: sandbox = filesystem capability runtime gobernado, NO shell unrestricted dentro de /tmp.
Archivos nuevos (8)
Section titled “Archivos nuevos (8)”runtime/agentic/sandbox_fs.py
Section titled “runtime/agentic/sandbox_fs.py”Filesystem foundation con boundary enforcement:
SANDBOX_ROOTS—/tmp/opencode/sandbox/y/opt/ai-lab/sandbox/resolve_sandbox_path()— canonical path resolutionis_within_sandbox()— boundary checkdetect_symlink_escape()— bloquea symlinks fuera del sandboxdetect_path_traversal()— bloquea../,~, etc.check_path_depth(max_depth=8)— evita nesting arbitrariois_extension_allowed()/is_extension_blocked()— extension filterBLOCKED_EXTENSIONS:.socket,.service,.mount,.timer,.path,.target
runtime/agentic/sandbox_registry.py
Section titled “runtime/agentic/sandbox_registry.py”Catálogo de 11 operaciones sandbox con SandboxOperationSpec:
| Operación | Extensiones | Max size | Risk | Governance |
|---|---|---|---|---|
create_file | All allowed | 1MB | medium | |
append_file | All allowed | 1MB | medium | |
replace_file | All allowed | 1MB | medium | |
create_directory | — | — | low | |
write_json | .json | 2MB | low | |
write_yaml | .yaml/.yml | 1MB | low | |
write_markdown | .md | 2MB | low | |
generate_report | .md | 5MB | low | |
generate_config | .json/.yaml/.toml/.ini | 512KB | low | |
generate_script | .py/.sh | 1MB | medium | ✅ siempre |
sandbox_transform | Todos | 5MB | low |
MutationClassenum:CREATE,APPEND,REPLACE,TRANSFORM,GENERATE,ROLLBACKis_allowed_operation()— valida extensión, tamaño, tipoop_for_intent()— mapeo intent string → operación
runtime/agentic/mutation_context.py
Section titled “runtime/agentic/mutation_context.py”MutationExecutionContext extiende RuntimeExecutionContext:
sandbox_root,mutation_class,mutation_typedry_run_only_writeflagcurrent_workflow_artifacts,current_workflow_bytes— budget countersis_executable()— permiteREADONLYySANDBOX_WRITE
runtime/agentic/sandbox_policies.py
Section titled “runtime/agentic/sandbox_policies.py”Governance sandbox:
check_sandbox_governance()— verifica: phase ≥ 28.3, intent es sandbox write, path dentro de boundary, sin symlink/traversal, extensión permitida, profundidad dentro de límitedetect_chmod_intent()— detectachmod +x,chmod 755,+x, etc. → siempre bloqueadoassess_sandbox_risk()—.py/.shsiempremediumcomo mínimoCHMOD_PATTERNS— 10 patrones de chmod prohibidos en sandbox
runtime/agentic/rollback_engine.py
Section titled “runtime/agentic/rollback_engine.py”Reemplaza rollback_placeholder.py con engine real:
Snapshotter.take_snapshot()— backup pre-mutación en{sandbox_root}/.rollback/{workflow_id}/{action_id}/- SHA-256 checksum antes de mutar
RollbackEngine.restore()— restore + validación checksum post-restoreRollbackEngine.rollback_workflow()— restore completo por workflowwrite_original_path_marker()— metadato para inferir path originalRollbackResultconchecksum_validatedyrestored_checksum
runtime/agentic/artifact_registry.py
Section titled “runtime/agentic/artifact_registry.py”Registro de outputs con lineage DAG:
ArtifactEntrycon:artifact_id,path,checksum_sha256,size_bytes,mutation_type,workflow_id,action_id,parent_workflow_id,parent_artifact_id,generated_by_actionArtifactRegistry.register()— JSONL appendArtifactRegistry.list(),get(),get_by_workflow()ArtifactRegistry.get_lineage()— DAG de ancestrosArtifactRegistry.count_by_workflow()/total_bytes_by_workflow()— budget queries
runtime/agentic/sandbox_audit.py
Section titled “runtime/agentic/sandbox_audit.py”Append-only JSONL con:
SandboxAuditEntry:timestamp,execution_id,workflow_id,action_id,mutation_class,mutation_type,target_path,before_checksum,after_checksum,rollback_available,rollback_path,status,errorwrite_sandbox_audit(),read_sandbox_audit(),get_sandbox_audit_stats()
runtime/agentic/sandbox_executor.py
Section titled “runtime/agentic/sandbox_executor.py”SandboxWriteExecutor — orquestador con:
- Feature flags:
ENABLE_SANDBOX_WRITE = False,DRY_RUN = True(secure by default) - Budget:
MAX_ARTIFACTS_PER_WORKFLOW = 100,MAX_WORKFLOW_BYTES = 25MB - Rate limiting: 10 writes/min por workflow (token bucket)
- Pipeline por acción:
governance → budget → rate limit → snapshot → mutate (Python I/O) → checksum → artifact registry → audit - Rollback automático en error de mutación
- NUNCA usa subprocess para writes — solo
open(),pathlib,shutil,json.dump()
Archivos modificados (9)
Section titled “Archivos modificados (9)”runtime/agentic/workflow_state.py
Section titled “runtime/agentic/workflow_state.py”- Nuevo estado:
MUTATING - Nuevas transiciones:
EXECUTING → MUTATING,MUTATING → DONE/FAILED,FAILED → ROLLED_BACK,DONE → ROLLED_BACK
runtime/agentic/permissions.py
Section titled “runtime/agentic/permissions.py”_SANDBOX_WRITE_INTENTS— 11 intents de escritura sandbox_SCOPE_ALLOWED_IN_PHASE["28.3"] = {"readonly", "workspace_write_reserved"}classify_permission_scope()— sandbox intents →WORKSPACE_WRITE_RESERVED"create_file"removido de_FORBIDDEN_INTENTS
runtime/agentic/execution_context.py
Section titled “runtime/agentic/execution_context.py”DryRunReason.SANDBOX_DISABLED = "sandbox_disabled"is_executable()— ahora permiteSANDBOX_WRITEyREADONLY
runtime/telemetry/prometheus_metrics.py
Section titled “runtime/telemetry/prometheus_metrics.py”7 métricas nuevas:
| Métrica | Tipo | Labels |
|---|---|---|
ailab_sandbox_mutations_total | Counter | type, result |
ailab_sandbox_rollbacks_total | Counter | reason |
ailab_sandbox_policy_denied_total | Counter | intent, reason |
ailab_sandbox_artifacts_total | Gauge | |
ailab_sandbox_escape_attempts_total | Counter | detection_method |
ailab_sandbox_checksum_mismatch_total | Counter | |
ailab_sandbox_mutation_duration_seconds | Histogram | type |
runtime/gateway/openai_gateway.py
Section titled “runtime/gateway/openai_gateway.py”- Feature flag
AI_LAB_ENABLE_SANDBOX_WRITE(defaultfalse) - Import condicional de
SandboxWriteExecutor - Pipeline bifurcación: si sandbox write enabled + intents sandbox →
SandboxWriteExecutor
runtime/llm/router_api.py
Section titled “runtime/llm/router_api.py”3 nuevos endpoints GET:
| Endpoint | Descripción |
|---|---|
GET /agentic/artifacts?limit=50 | Lista artifacts recientes |
GET /agentic/artifacts/{id} | Artifact específico + lineage |
GET /agentic/rollback/{workflow_id} | Información de rollback disponible |
Endpoints internos
Section titled “Endpoints internos”GET /agentic/state → execution_mode, phase, executor_enabledGET /agentic/executions → audit trail readonlyGET /agentic/executions/stats → estadísticas auditGET /agentic/artifacts → artifacts sandboxGET /agentic/artifacts/{id} → artifact + lineageGET /agentic/rollback/{id} → rollback infoFeature flags
Section titled “Feature flags”| Variable | Default | Propósito |
|---|---|---|
AI_LAB_ENABLE_SANDBOX_WRITE | false | Habilita executor sandbox write real |
AI_LAB_ENABLE_PLANNER | false | Habilita pipeline agentic completo |
Seguridad
Section titled “Seguridad”- NO subprocess para writes — solo
open(),pathlib,shutil - NO chmod — 10 patrones detectados y bloqueados
- Symlink escape detection —
os.path.realpath()en cada operación - Path depth limit — máximo 8 niveles de nesting
- Extensiones bloqueadas —
.socket,.service,.mount, etc. - Secure by default — flags
false/True, dry-run por defecto
122 tests en tests/test_sandbox_28_3.py:
| Categoría | Tests |
|---|---|
| SandboxFS | 21 |
| SandboxRegistry | 14 |
| SandboxPolicies | 13 |
| SandboxAudit | 5 |
| ArtifactRegistry | 10 |
| RollbackEngine | 9 |
| MutationContext | 10 |
| WorkflowState (MUTATING) | 9 |
| Permissions 28.3 | 9 |
| SandboxExecutor | 9 |
| Mutation helpers | 11 |
| Sandbox intent classification | 6 |