Skip to content

Python: Add agent-framework-tenki (Tenki-backed CodeAct provider)#7312

Open
Patricio-Filice-Luxor wants to merge 6 commits into
microsoft:mainfrom
Patricio-Filice-Luxor:feat/add-tenki-codeact-integration
Open

Python: Add agent-framework-tenki (Tenki-backed CodeAct provider)#7312
Patricio-Filice-Luxor wants to merge 6 commits into
microsoft:mainfrom
Patricio-Filice-Luxor:feat/add-tenki-codeact-integration

Conversation

@Patricio-Filice-Luxor

Copy link
Copy Markdown

Motivation & Context

Adds a third CodeAct backend, agent-framework-tenki, wrapping Tenki Sandbox (managed Linux micro-VMs) behind the same *CodeActProvider / *ExecuteCodeTool shape as agent-framework-hyperlight and agent-framework-monty. Unlike the existing in-process backends it provides a remote, isolated, real Linux environment: pip/apt installs, subprocesses, and a persistent filesystem across calls.

Full rationale, backend comparison, and scope discussion: #7311

Description & Review Guide

  • Major changes: new alpha package python/packages/tenki/ (1.0.0a260722, PEP 561 typed) with TenkiCodeActProvider (run-scoped: fresh execute_code tool + sandbox per agent run, terminated in after_run so state never leaks across runs) and TenkiExecuteCodeTool (standalone: one sandbox reused until close()); a runnable sample; 66 unit + 4 opt-in integration tests; CI wiring.
  • Impact: additive only — not part of agent-framework[all] and no agent_framework.tenki lazy-loading shim (both deferred to beta promotion, per the alpha package policy). No existing package modified beyond PACKAGE_STATUS.md, workspace registration, and the CI workflow env.
  • Review focus: the sandbox lifecycle reconciliation and the run-scoped provider state contract (session state stores only the sandbox name — a plain JSON-serializable string; live tool handles stay on the provider so close() can reap runs that never reached after_run).

Sandbox lifecycle (highlights):

  • Lazy provision on the first execute_code call, reused per tool instance. Before each call the tool refreshes remote state: PAUSED / USER_SHUTDOWN → auto-resume, retried within a 120s poll budget (USER_SHUTDOWN links its pause snapshot asynchronously — verified live end-to-end); TERMINATED / TERMINATING → re-provision; refresh() failure → structured error, handle kept for retry.
  • Reconcile + sandbox.exec run under a single lock hold: close() serializes with in-flight execs, a failed terminate preserves the handle for retry, and the provider's close() attempts every leaked run tool and raises RuntimeError naming the survivors so a second close() retries exactly those.
  • Cost backstops: max_duration_seconds (default 900s) stops compute billing server-side even if the client process crashes; provider run-scoped sandboxes cap pause-snapshot retention at 1h (standalone keeps Tenki's 7-day default).

Implementation notes: sync SDK (tenki-sandbox>=0.4.0,<0.5) bridged via asyncio.to_thread + threading.Lock; typed CommandResult parsing (signal-killed processes correctly reported as failures); injected CodeAct instructions cover small-model footguns (print(...) requirement, subprocess for pip, no Jupyter magic); explicit constructor args — including empty string — always win over env fallbacks (TENKI_API_KEY, TENKI_PROJECT_ID, TENKI_WORKSPACE_ID); Tenki-specific options (snapshot_id, volumes, network policy, …) pass through extra_create_kwargs.

Tests: 66 hermetic unit tests through the public tool.invoke API (full reconcile matrix, kwargs forwarding, close semantics, provider run-scoping, CancelledError propagation); 4 opt-in integration tests against the live service, guarded on TENKI_API_KEY. Note for maintainers: the integration job needs secrets.TENKI_API_KEY and vars.TENKI_PROJECT_ID configured.

Out of scope for the alpha: host tool callbacks (the Tenki SDK has no bridge), dedicated constructor parameters for mounts/network/snapshots (reachable today via extra_create_kwargs), and a session-scoped sandbox mode.

Related Issue

Resolves #7311

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Patricio-Filice-Luxor and others added 4 commits July 20, 2026 22:31
Introduces agent-framework-tenki, a third code-executor backend that runs
Python inside a Tenki managed Linux microVM sandbox. Ships alongside the
existing hyperlight (WASM) and monty (in-process Rust) executors, and
mirrors their public API shape (TenkiCodeActProvider ContextProvider +
TenkiExecuteCodeTool FunctionTool).

Lifecycle: lazy provision, reuse-per-tool, and per-call reconciliation —
PAUSED sandboxes are transparently resumed, TERMINATED sandboxes are
replaced by a fresh provision.

Includes 32 hermetic unit tests plus one opt-in integration test guarded
on TENKI_API_KEY. Alpha-policy compliant: workspace uv sources only, no
core[all] extra, no lazy-loading shim.
- Retry sandbox resume within a 120s poll budget: USER_SHUTDOWN pause
  snapshots are linked asynchronously (~60s) and the server can revert
  an accepted resume, so single-shot resume failed live
- Keep run-scoped provider state JSON-serializable (store sandbox name
  in session state; live tool handles stay on the provider)
- Handle CommandResult diagnostics (ok/signal/reason/errno) directly
  and keep the sandbox handle when close/refresh fails so it can retry
- Run tool close under a single lock in a worker thread so a close
  during an in-flight reconcile cannot block the event loop
- Make provider.close() retryable: keep failed terminates in the
  live-tool set instead of dropping them up front
- Type __aexit__ signatures to match other resource-backed providers
- Pin tenki-sandbox>=0.4.0,<0.5, finite max_duration default (900s),
  document pause/terminate asymmetry on expiry
- Correct startup-latency docs to measured ~2s (was 10-30s) and
  clarify run-scoped vs standalone sandbox lifetime in the README
- Expand tests to 58 unit + 4 integration (resume retry, server revert,
  close/run serialization, retryable provider close, cancellation
  teardown, terminal-state re-provision); wire tenki into
  integration/merge CI workflows

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…-tenki

- Hold the sandbox lock across reconcile + exec so close() cannot
  terminate a sandbox while an exec is in flight
- Add pause_retention_seconds kwarg (tool + provider); run-scoped
  sandboxes default to 1h retention so orphans are GC'd server-side
- Provider close() raises RuntimeError after attempting all terminates,
  retaining failed handles for retry
- Harden the teardown integration test: assert provisioning succeeded
  and poll the live API until TERMINATED
- Clarify README max_duration None-semantics and pause retention

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`snapshot_id` restores from a snapshot prepared beforehand with the Tenki
CLI/SDK — it does not snapshot the (already terminated) sandbox, so the
previous wording suggested an impossible workflow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 24, 2026 19:56
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Jul 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Python alpha package, agent-framework-tenki, that implements a Tenki Sandbox–backed CodeAct backend. This extends the Agent Framework’s CodeAct provider/tool ecosystem with a remote, isolated Linux micro-VM execution environment (persistent filesystem across calls, subprocesses, package installs), alongside existing in-process backends.

Changes:

  • Introduces agent-framework-tenki package with TenkiCodeActProvider (run-scoped) and TenkiExecuteCodeTool (standalone, reusable sandbox).
  • Adds a runnable Tenki CodeAct sample and updates the CodeAct samples README to include the third backend.
  • Wires Tenki into the Python workspace/lockfile and includes its integration tests in CI workflows.

Reviewed changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
python/uv.lock Registers agent-framework-tenki workspace member and locks tenki-sandbox dependency.
python/pyproject.toml Adds agent-framework-tenki to the Python workspace packages.
python/PACKAGE_STATUS.md Marks the new package as alpha.
python/samples/02-agents/context_providers/code_act/tenki_code_act.py New sample demonstrating Tenki-backed provider usage.
python/samples/02-agents/context_providers/code_act/README.md Updates sample matrix + install/run instructions to include Tenki.
python/packages/tenki/README.md Package documentation covering configuration, lifecycle, and Tenki-specific passthrough options.
python/packages/tenki/pyproject.toml New package metadata, dependencies, typing, lint/test configuration.
python/packages/tenki/LICENSE Adds MIT license file for the new package.
python/packages/tenki/agent_framework_tenki/py.typed Marks the package as typed (PEP 561).
python/packages/tenki/agent_framework_tenki/init.py Public exports for provider/tool + version wiring.
python/packages/tenki/agent_framework_tenki/_provider.py Implements run-scoped provider lifecycle (before_run/after_run/close).
python/packages/tenki/agent_framework_tenki/_execute_code_tool.py Implements sandbox lifecycle reconciliation + execute_code tool behavior.
python/packages/tenki/tests/tenki/test_tenki_codeact.py Unit + opt-in integration tests for tool/provider lifecycle and result parsing.
.github/workflows/python-merge-tests.yml Adds Tenki package tests to the misc integration test job; exports Tenki env vars.
.github/workflows/python-integration-tests.yml Same as above for scheduled/manual integration workflow.
Comments suppressed due to low confidence (3)

python/packages/tenki/tests/tenki/test_tenki_codeact.py:1373

  • These integration tests pass project_id=os.environ.get("TENKI_PROJECT_ID") directly. In GitHub Actions, an unset vars.TENKI_PROJECT_ID expands to an empty string, which then gets forwarded as project_id="" and can cause Tenki provisioning to fail even though project_id is optional (single-project API keys). Consider only passing project_id when it’s a non-empty string.
    project_id = os.environ.get("TENKI_PROJECT_ID")
    async with TenkiExecuteCodeTool(
        sandbox_name=f"agent-framework-ci-fs-{os.getpid()}",
        project_id=project_id,
        max_duration_seconds=300,

python/packages/tenki/tests/tenki/test_tenki_codeact.py:1392

  • These integration tests pass project_id=os.environ.get("TENKI_PROJECT_ID") directly. In GitHub Actions, an unset vars.TENKI_PROJECT_ID expands to an empty string, which then gets forwarded as project_id="" and can cause Tenki provisioning to fail even though project_id is optional (single-project API keys). Consider only passing project_id when it’s a non-empty string.
    project_id = os.environ.get("TENKI_PROJECT_ID")
    async with TenkiExecuteCodeTool(
        sandbox_name=f"agent-framework-ci-fail-{os.getpid()}",
        project_id=project_id,
        max_duration_seconds=300,

python/packages/tenki/tests/tenki/test_tenki_codeact.py:1422

  • This integration test passes project_id=os.environ.get("TENKI_PROJECT_ID") directly. In GitHub Actions, an unset vars.TENKI_PROJECT_ID expands to an empty string, which then gets forwarded as project_id="" and can cause Tenki provisioning to fail even though project_id is optional (single-project API keys). Consider only passing project_id when it’s a non-empty string.
    project_id = os.environ.get("TENKI_PROJECT_ID")
    unique_name = f"agent-framework-ci-teardown-{os.getpid()}"

    async with TenkiExecuteCodeTool(
        sandbox_name=unique_name,
        project_id=project_id,
        max_duration_seconds=300,
    ) as tool:

Comment thread python/packages/tenki/agent_framework_tenki/_execute_code_tool.py Outdated
Comment thread python/packages/tenki/tests/tenki/test_tenki_codeact.py Outdated
…t-integration

# Conflicts:
#	python/PACKAGE_STATUS.md
#	python/samples/02-agents/context_providers/code_act/README.md
CI systems expand unconfigured secrets/vars to "" (e.g. GitHub Actions
vars.TENKI_PROJECT_ID), which was forwarded to the Tenki SDK as
project_id=""/auth_token="" and failed provisioning in non-obvious ways.
Env fallbacks now normalize "" to unset; explicit constructor args keep
their documented precedence. Integration tests no longer pass an empty
project_id as an explicit arg. Addresses Copilot review on microsoft#7312.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Patricio-Filice-Luxor

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Luxor Technology"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Feature]: Add a CodeAct backend for remote isolated Linux micro-VM sandboxes

2 participants