exec

package
v1.801.490 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 6, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package exec is the code interpreter: run a snippet in a sandbox, and move files in and out of the session that sandbox IS.

A session is a sandbox

That is the whole design, and it is why this subsystem now holds no store, no session table and no lifetime of its own. The upstream contract hands a `session_id` back on every reply and takes it back on later calls; a sandbox has an id, a lease and files. They are the same object, so `session_id` IS the sandbox id and everything else falls out:

POST /v1/exec            lease the session's sandbox → write the program →
                         run it → report what it printed and what it wrote
POST /v1/upload          write into the session's sandbox
GET  /v1/download/{sid}/{id}  read one file back out
GET  /v1/files/{sid}     list what the session holds

Nothing here ends a lease. apps/sandbox's reaper does, on the two clocks it already keeps — the ttl the lease was taken for, and an hour of nobody touching it. Deleting the sandbox at the end of a run was the obvious other design and it is wrong: the reply carries a session id, and the contract's next three calls address it. A sandbox torn down at the end of POST /v1/exec would answer 404 to every download of the plot that run had just made.

What was here before

A reverse proxy to code-exec.hanzo.svc.cluster.local:8000, and that Service has had ZERO endpoints for 33 days: /v1/exec has been answering 503 in production the whole time. The proxy was not failing to reach the executor — there was no executor. Pointing it somewhere better was never the fix, because the thing being pointed at did not exist; the fix is that cloud already runs the one compute primitive, and this subsystem composes over it.

The wire is NOT ours

hanzo.chat drives this through @hanzochat/agents' CodeExecutor and its own Files/Code client, so the shapes below are MEASURED from those two callers rather than designed here — including the details that are easy to get subtly wrong: download is addressed by TWO segments (`{session_id}/{fileId}`, crud.js), the file listing answers a BARE JSON ARRAY of {name, lastModified} whose `name` is that same two-segment identifier (process.js getSessionInfo), and upload answers {message:"success", session_id, files:[{fileId, filename}]} and is checked for `message === 'success'` before anything else is read.

Auth

The gateway bypasses these paths — the credential is an opaque service key on X-API-Key, not a JWT — so this subsystem enforces that key itself against CODE_EXEC_API_KEY, in constant time, failing CLOSED when none is configured.

The chat server ALSO forwards the end user's validated IAM bearer when it can resolve one (crud.js codeAuthHeaders), which is what lets a session be scoped to a real tenant. When it is there, the sandboxes are that org's. When it is not, they belong to the deployment's own brand org — one tenant for one deployment, which is what a shared service key with no tenant in it actually means.

Index

Constants

View Source
const Path = "/v1/exec"

Path is where the code interpreter answers. It lives HERE, in the app that serves it, because three consumers once disagreed about it in production and nothing could see the disagreement.

Variables

This section is empty.

Functions

func End added in v1.801.490

func End(ctx context.Context, org, session string) error

End drops a session's sandbox now instead of leaving it to the reaper. Nothing on the code-interpreter surface calls it — a session there outlives its run, because the reply hands back an id the next three calls address — but a FUNCTION invoke is over the moment it answers, and holding a pod for the reaper to notice would be fifteen idle minutes of somebody's node per call.

func Languages added in v1.801.490

func Languages() []string

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount registers the code-interpreter surface.

Types

type CodeFile added in v1.801.490

type CodeFile struct {
	ID               string `json:"id"`
	Name             string `json:"name"`
	StorageSessionID string `json:"storage_session_id,omitempty"`
	SessionID        string `json:"session_id,omitempty"`
}

CodeFile is one file in a session. ID is its path RELATIVE to the session's artifact directory, which is what makes a download a read and not a lookup: there is no id table to keep, because the id already says where the bytes are.

TWO SPELLINGS OF ONE FIELD, and both are read. @hanzochat/agents' FileRef calls it `storage_session_id` (tools.d.ts, and the split from the execution session is deliberate there); hanzo.chat's own primer sends `session_id` (Files/Code/process.js pushFile). Reading only the first meant every file a user attached arrived with an empty session, was skipped by the copy loop, and was skipped by the "not available" note as well — so the CSV was invisible and nothing said so. Answering with `storage_session_id` keeps the reply on the agents shape.

func (CodeFile) Session added in v1.801.490

func (f CodeFile) Session() string

Session is the session a file's bytes live in, whichever name the caller used.

type CodeResult added in v1.801.490

type CodeResult struct {
	SessionID string     `json:"session_id"`
	Stdout    string     `json:"stdout"`
	Stderr    string     `json:"stderr"`
	Files     []CodeFile `json:"files,omitempty"`
}

CodeResult is one run. A program that exited non-zero is a SUCCESSFUL call carrying a failed program — its diagnostics are on Stderr and the status stays 200, because "the code threw" and "the interpreter is down" are different facts and the caller renders them differently.

func Run added in v1.801.490

func Run(ctx context.Context, org string, in *CodeRun) (*CodeResult, error)

Run is the whole interpreter, and it is composition rather than implementation: lease a sandbox, put the program in it, run it, read back what changed.

It is EXPORTED because a second subsystem in this process runs snippets too — apps/functions invokes a customer function, which is this operation with a shorter lease — and the alternative was a second copy of the language table, the artifact sweep and the session rule. The org is a parameter and never read from the argument: a caller that could name the tenant could run in another one.

type CodeRun added in v1.801.490

type CodeRun struct {
	Lang string   `json:"lang" validate:"required"`
	Code string   `json:"code" validate:"required"`
	Args []string `json:"args,omitempty"`
	// Files are inputs the host already put in some session. Each names the session
	// its bytes live in, which is usually — and ideally — the session this run wants.
	Files     []CodeFile `json:"files,omitempty"`
	SessionID string     `json:"session_id,omitempty"`
	UserID    string     `json:"user_id,omitempty"`
	// RuntimeSessionHint is the stateful-session hint. It is carried so a client
	// that sends it is not silently misread, and it selects nothing here: every
	// session in this implementation is already a warm sandbox, so there is no
	// second kind of runtime for a hint to choose between.
	RuntimeSessionHint string `json:"runtime_session_hint,omitempty"`
}

CodeRun is what the code tool posts. Every field is one the client actually sends: lang/code/args from the model's tool call, files/session_id/user_id from the host's injection, runtime_session_hint from the stateful-session path.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL