Skip to content

Environments: no snapshot/reuse — every provision re-installs from scratch; add capture-after-setup keyed by definition hash (docker commit, native cloud snapshots) + praisonai env rebuild/list/prune #3670

Description

@MervinPraison

Aim

Make provisioned agent environments fast to reuse: after the environment definition's packages install and setup: commands run once, capture that state and start subsequent runs from the capture instead of re-installing from scratch every time. Target: second-and-later runs start in seconds. This is the speed half of the environments work and depends on the definition sibling (#3669) — the capture is keyed by a hash of the definition, so any change to it invalidates cleanly.

Current behaviour (every run pays full setup; capture primitives unused)

Validated on current main:

  1. Zero environment snapshots anywhere. grep -rni "snapshot" across all packages returns only file/context/skill/config snapshots (paths.py:200-207, trace/context_events.py:155, skills/manager.py:865-871, …). praisonai_sandbox/ contains 0 occurrences of the word. No commit, pause, fork, or image-cache concept in either execution stack.
  2. Everything is ephemeral by design today: the docker sandbox backend's start() only pulls the base image and each execute() is a fresh docker run (praisonai_sandbox/docker.py:79-100, 278, 430-457); the daytona backend creates a sandbox per session and destroys it on cleanup (daytona.py:96-116). ComputeProviderProtocol has provision/execute/shutdownno snapshot method (praisonaiagents/managed/protocols.py:91+).
  3. The cloud SDKs already support capture and it's unused: the E2B and Daytona SDKs natively support sandbox pause/snapshot/resume; PraisonAI's adapters never call them. Docker supports commit for image capture; unused.
  4. The only related CLI verb is the opposite of a capture: praisonai sandbox recreate deletes containers so they rebuild from base (praisonai_code/cli/commands/sandbox.py:230-292).
  5. Consequence once Environments: no repo-committed execution-environment definition — add .praisonai/environment.yaml (image/packages/setup/resources) consumed by sandbox, compute, and kanban workers + praisonai env CLI #3669 lands: a definition with packages + setup: would re-run installs on every provision — kanban boards spawning many workers pay it per worker; iterative sandbox run pays it per invocation.

Desired behaviour

  1. Capture-after-setup, keyed by definition hash. When an environment is provisioned from .praisonai/environment.yaml (Environments: no repo-committed execution-environment definition — add .praisonai/environment.yaml (image/packages/setup/resources) consumed by sandbox, compute, and kanban workers + praisonai env CLI #3669), after packages+setup: succeed the backend captures the state:
    • dockerdocker commit to a local image praisonai-env:{hash12};
    • e2b / daytona → the SDK's native snapshot/pause primitive, id recorded;
    • subprocess/sandlock/ssh → no capture (documented no-op; nothing to capture or capture is meaningless).
  2. Reuse on match: next provision with the same definition hash starts from the capture (skip pull+install+setup). Any change to the definition file → new hash → fresh build then new capture. Manual refresh: praisonai env rebuild (capture invalidate + rebuild).
  3. Optional refresh hook: a refresh: command list in the definition runs when starting from a capture (e.g. pip install -e . to catch code changes) — cheap incremental step, never the full setup.
  4. Bookkeeping: captures recorded in ~/.praisonai/environments/registry.json {definition_hash: {backend, ref, created_at, last_used}}; praisonai env list shows them; age-based GC (default 14 days unused) with praisonai env prune.
  5. Safety/defaults: capture is on by default only for backends where it is free and local (docker commit); cloud snapshot capture is opt-in per definition (capture: true) since it may carry provider cost. A failed capture degrades to today's ephemeral behaviour with a log line — never blocks the run.

Layer placement

Implementation plan

  1. definition_hash(defn) -> str — stable sha256 over the normalised definition (image, packages, setup, env names, resources; exclude values marked secret).
  2. Protocol: SupportsCapture (optional) on ComputeProviderProtocol/sandbox backend protocol; provision flow becomes: registry lookup by hash → provision_from(ref) + refresh: if hit → else full provision + setup → capture() → registry record.
  3. Docker: docker commit {container} praisonai-env:{hash12} after setup (praisonai_sandbox/docker.py post-setup site; also the compute docker provider); provision-from = use that image instead of base.
  4. E2B/Daytona: wire the SDKs' snapshot/pause+resume (praisonai_sandbox/e2b.py, daytona.py, integrations/compute/{e2b,daytona}.py) behind capture: true.
  5. Registry + GC + CLI verbs (praisonai_code/cli/commands/): env list, env rebuild, env prune.
  6. Docs: the run-twice contract — first run builds and captures; subsequent runs start from capture; edit the file → automatic rebuild.

Tests (TDD)

  • test_definition_hash_stable_and_sensitive — reordering keys → same hash; changing a package → new hash.
  • test_docker_capture_and_reuse — second provision with same definition skips setup (setup command spy called once) and starts from praisonai-env:{hash12}.
  • test_definition_change_invalidates — edited file → full rebuild + new capture.
  • test_refresh_runs_on_capture_startrefresh: executes only on capture-based starts.
  • test_capture_failure_degrades_ephemeral — commit error → run proceeds as today, warning logged.
  • test_noncapturing_backend_noop — subprocess backend unchanged.
  • test_registry_gc_prune — unused entries pruned by age; env prune removes refs and (docker) images.

Severity

Medium-High — without reuse, the environment definition (#3669) makes every provision slower in exchange for reproducibility; with it, PraisonAI matches the fast-start property the major cloud coding agents converge on (setup-once-then-cached), and multi-worker kanban boards amortise setup across the fleet. The primitives (docker commit, cloud SDK snapshots) already exist under the adapters — this is wiring plus bookkeeping.

Validation (current main)

  • No env snapshots: repo-wide grep (only file/context/skill snapshots); 0 hits in praisonai_sandbox/; no commit/pause/fork in either stack.
  • Ephemeral flows: praisonai_sandbox/docker.py:79-100, 278, 430-457; daytona.py:96-116; ComputeProviderProtocol lacks capture (managed/protocols.py:91+).
  • sandbox recreate destroys rather than captures: praisonai_code/cli/commands/sandbox.py:230-292.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingclaudeAuto-trigger Claude analysisdocumentationImprovements or additions to documentationenhancementNew feature or requestperformance

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions