Skip to content

feat(agents-api): add Tenki sandbox provider for function tool execution#3468

Open
buffgbob wants to merge 1 commit into
inkeep:mainfrom
buffgbob:feat/tenki-sandbox-provider
Open

feat(agents-api): add Tenki sandbox provider for function tool execution#3468
buffgbob wants to merge 1 commit into
inkeep:mainfrom
buffgbob:feat/tenki-sandbox-provider

Conversation

@buffgbob

Copy link
Copy Markdown

feat(agents-api): add Tenki sandbox provider for function tool execution

Summary

Adds Tenki Sandbox as a third sandbox provider for function tools, alongside the existing native and vercel providers. Tenki is a hosted microVM sandbox service, so this gives deployments strong isolation for function tool execution from any environment (including ones where child-process spawning is restricted) without operating their own sandbox infrastructure.

Changes

  • TenkiSandboxExecutor (agents-api/src/domains/run/tools/TenkiSandboxExecutor.ts) — new executor using @tenkicloud/sandbox, mirroring VercelSandboxExecutor: sessions are pooled and reused per dependency hash, honoring the existing FUNCTION_TOOL_SANDBOX_POOL_TTL_MS / FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT limits; each invocation writes its wrapper to an isolated /home/tenki/runs/<runId>/ directory and cleans it up afterwards
  • SandboxExecutorFactory — routes provider: 'tenki' to the new executor
  • TypesTenkiSandboxConfig added to the SandboxConfig union in agents-api/src/types/app.ts and domains/run/types/executionContext.ts (authToken, baseUrl, and projectId optional; the SDK falls back to TENKI_AUTH_TOKEN / TENKI_API_KEY env vars, and the project is auto-discovered via whoAmI() when not configured)
  • Env wiringSANDBOX_TENKI_API_KEY (+ optional SANDBOX_TENKI_BASE_URL, SANDBOX_TENKI_PROJECT_ID) selects the Tenki provider in agents-api/src/index.ts, after the existing Vercel env check so current behavior is unchanged
  • Capabilities'tenki' added to the provider enum in /capabilities and the manage-ui capabilities type
  • TestsTenkiSandboxExecutor.concurrency.test.ts mirrors the Vercel concurrency test (single create under concurrent invocations, per-run file isolation) and additionally asserts outbound networking, project auto-discovery, and dependency install
  • Docs — Tenki listed under Sandbox Providers in function-tools.mdx with a config example
  • Changeset — patch for @inkeep/agents-api

Implementation notes

A note on scope: the executor follows VercelSandboxExecutor's structure (dependency-hash pooling, per-run directories, the same TTL/max-use limits) but is deliberately stricter about session lifecycle — leases, deferred closes, and shutdown draining. Tenki sessions are billed cloud microVMs rather than local processes, so "a command was cut short at the TTL boundary" and "a session outlived cleanup()" are user-visible bugs here in a way they aren't for local providers; each behavior below exists because a test demonstrated the failure. Happy to split any of this out or simplify if you'd rather keep provider parity — and some of it (the mid-install pool-publish race, the 2-second reuse window) may be worth porting to the Vercel executor as a follow-up.

  • Tenki sandboxes have networking disabled by default; the executor creates sessions with allowOutbound: true so npm install of tool dependencies works (matching Vercel sandbox behavior, which always has egress).
  • Command timeouts are enforced client-side by killing the process on expiry and awaiting termination for a bounded grace period — the SDK's Session.exec() types a timeoutMs option but does not enforce it in 0.3.x, and kill() only enqueues the signal. A timed-out sandbox is retired from the pool rather than reused, since surviving child processes cannot be ruled out.
  • Session lifetime is decoupled from the per-command timeout: sessions are created with maxDurationMs = pool TTL + command timeout + buffer, and pool expiry is computed from the session's actual server-side deadline (session.timeoutAt — the clock starts at creation, so cold-start/install time is accounted for). The pool only hands out sessions that still have a full command timeout of life remaining, so a pooled call can never be cut short by session expiry.
  • Pool entries track active leases: TTL/max-use expiry retires a sandbox from new use but defers close() until its last in-flight execution finishes, and sandboxes only enter the pool after dependency install completes so a concurrent call can never lease a half-initialized session.
  • cleanup() is shutdown-safe: it awaits in-flight sandbox initializations before draining the pool, an initialization that completes after shutdown begins closes its session instead of publishing it, and deferred/asynchronous closes started by lease release or retirement are tracked and awaited — no cloud session can outlive the executor.
  • Timed-out sessions are retired by identity: if an old session was already replaced under the same dependency hash, its late timeout can never evict the healthy replacement.
  • Executors are cached per full configuration hash (credentials, baseUrl, projectId, runtime, resources), so distinct Tenki configurations never share a client.
  • Tenki requires a project id at session creation when authenticating with an API key. When projectId isn't configured, the executor resolves it once via whoAmI() (using the sole accessible project, erroring if there are several) — the same auto-selection the Tenki CLI performs at login.
  • Dependency version is ^0.3.6 rather than the just-released 0.4.0 to satisfy this repo's minimumReleaseAge (3-day supply-chain cooldown). The API surface used here is identical in both versions.

Testing

  • Unit: TenkiSandboxExecutor.concurrency.test.ts (SDK mocked) — concurrent invocations create one sandbox with unique per-run paths; sandbox created with allowOutbound, correct cpuCores/maxDurationMs, auto-discovered projectId, and dependency install. Existing factory + Vercel executor tests still pass (6/6).
  • pnpm typecheck, pnpm lint (biome), pnpm knip all clean.
  • Live end-to-end against a real Tenki workspace through the executor:
    • no-dependency tool: returned correct result on guest Node v24 (~3.6s cold, includes microVM create)
    • tool with npm dependency (left-pad): installed inside the sandbox over egress, correct result (~4s cold)
    • repeat call with same dependencies: pooled session reused, ~0.3s
    • executor.cleanup() closed all sessions; tenki sandbox list confirmed nothing leaked
  • Timeout enforcement covered by a unit test: a hanging command is killed and the call fails with a timeout error at the configured budget
  • OpenAPI snapshot regenerated (openapi:update-snapshot, 12/12 tests pass)

Adds @tenkicloud/sandbox-backed TenkiSandboxExecutor mirroring the
Vercel provider: pooled microVM sessions keyed by dependency hash,
per-run directory isolation, and cleanup on session end. Configure via
sandboxConfig { provider: 'tenki' } or SANDBOX_TENKI_API_KEY, with
optional SANDBOX_TENKI_BASE_URL / SANDBOX_TENKI_PROJECT_ID; the project
is auto-discovered via whoAmI() when not configured.

Command timeouts are enforced client-side (the SDK's exec timeoutMs is
not enforced in 0.3.x): on expiry the process is killed, termination is
awaited for a bounded grace period, and the timed-out session is
retired by identity. Pool entries track active leases so TTL/max-use
expiry defers close() until in-flight executions finish, sessions only
enter the pool after dependency install completes, pool expiry follows
the session's server-side timeoutAt deadline, deferred closes are
tracked and awaited by cleanup() alongside in-flight initializations so
no session can leak past shutdown, and executors are cached per full
configuration hash.
@changeset-bot

changeset-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 56d4e5f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@inkeep/agents-api Patch
@inkeep/agents-manage-ui Patch
@inkeep/agents-cli Patch
@inkeep/agents-core Patch
@inkeep/agents-email Patch
@inkeep/agents-mcp Patch
@inkeep/agents-sdk Patch
@inkeep/agents-work-apps Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant