agentsdk

package module
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: Apache-2.0 Imports: 43 Imported by: 0

README

agentsdk

Go SDK for building cyborg agents — programs that are half code, half AI — that run on airlock.

Cyborg agents are deterministic Go where it makes sense (HTTP routes, webhooks, cron jobs, structured tool execution) and AI-driven where it helps (LLM reasoning, conversation handling, open-ended decisions). agentsdk is the contract your code uses to participate in the airlock platform: register routes, tools, webhooks, crons, and chat surfaces; access scoped storage and per-agent Postgres; and call LLMs through the platform's credential-managing proxy.

If you're not building on airlock, you don't need this — agentsdk is the glue, not the runtime.

[!WARNING] Alpha software. Early-stage code with bugs we haven't found yet — please open an issue for anything that breaks. Note: even though the SDK is alpha, the public API surface is intended to stay relatively stable and backwards-compatible even pre-1.0, because agents built against an older agentsdk version need to keep working against newer ones. Internal/unexported code can change freely. See Stability below.

Install

go get github.com/airlockrun/agentsdk

Requires Go 1.26+.

Hello-world agent

package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/airlockrun/agentsdk"
)

func main() {
	agent := agentsdk.New(agentsdk.Config{
		Description: "Greets visitors. Replace once the agent does real work.",
	})

	agent.RegisterRoute("/", "GET", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "hello from a cyborg agent")
	}, agentsdk.RouteOpts{
		Access:      agentsdk.AccessPublic,
		Description: "Greet anyone who hits the agent's home route.",
	})

	agent.Serve()
}

In a real agent you'd also call RegisterTool, RegisterWebhook, RegisterCron, RegisterConnection, and so on — see the airlock docs for the full surface.

Stability

agentsdk's public API is treated as a stability commitment: changes to exported types, functions, or runtime behavior are kept backwards-compatible across minor versions. Older built agents must continue to work against newer agentsdk releases.

Internal/unexported code can change freely. Non-trivial API changes go through a Discussion before any PR — see CONTRIBUTING.md.

Companion projects

  • airlock (AGPL-3.0) — the self-hosted platform that runs agents built with this SDK
  • goai (Apache-2.0) — Go port of the Vercel AI SDK
  • sol (Apache-2.0) — agent runtime / CLI utility

License

Apache-2.0.

Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md. A CLA Assistant bot will prompt you to sign on your first PR (one signature covers all airlockrun projects).

Security

Email security@airlock.run. Do not open public issues for vulnerabilities.

Documentation

Overview

Package agentsdk provides the Go SDK for building Airlock agents.

This package will contain the client library that agents use to communicate with the Airlock platform — registering connections, receiving triggers, and streaming run results.

Index

Constants

View Source
const (
	// ErrorKindPlatform: failure upstream of the agent's own code. LLM
	// provider 4xx, sol/goai stream errors, request transport (body read).
	// The agent's code couldn't have prevented or fixed this — the "Fix
	// this error" workflow on the run page is hidden for these.
	ErrorKindPlatform = "platform"

	// ErrorKindAgent: failure from agent-defined code paths. Webhook/cron
	// handlers returning err, panics in user code recovered by the SDK,
	// post-LLM bookkeeping that hit something the agent owns. The Fix
	// workflow targets exactly these.
	ErrorKindAgent = "agent"
)

Error kinds passed in RunCompleteRequest.ErrorKind. The agentsdk side classifies structurally — by call-site, not by error string — so airlock can avoid pattern-matching at all.

View Source
const Version = "0.2.0" // pre-release; bump when shipping breaking protocol changes

Version is the agentsdk API version. Reported to Airlock during sync. Bump on breaking changes — see CLAUDE.md for versioning rules.

Variables

View Source
var ErrInvalidPath = errors.New("agentsdk: invalid path")

ErrInvalidPath is returned for paths that fail normalization (missing leading '/', empty segments, '..' segments, etc.).

View Source
var ErrNotFound = errors.New("agentsdk: file not found")

ErrNotFound is returned by CheckFileAccess and the storage methods for both "directory not registered" and "caller does not have access" — the two cases are deliberately indistinguishable at the public surface so path-guessing leaks no information about what exists.

Functions

func GetDeps

func GetDeps[T any](ctx context.Context) T

GetDeps retrieves the typed Deps struct from the Agent bound to ctx. Panics if no agent is bound (must be called from inside a handler), if Deps is nil, or if the type doesn't match. Used by handlers that need the builder's pre-registered application state (connection/MCP handles, config, etc.) — particularly VM functions defined in separate packages where the `agent` variable isn't in scope.

func IsLikelySecret added in v0.2.3

func IsLikelySecret(v string) bool

IsLikelySecret reports whether v looks like a real credential rather than a low-entropy placeholder or common phrase. Used to gate auto-redaction so values like "password", "true", or "hello world" don't end up in the redact set and nuke ordinary words from LLM input.

Heuristic, calibrated empirically against real API-key shapes (sk-…, bb_live_…, AKIA…, ghp_…, JWTs, telegram bot tokens):

  • Length ≥ 16 — every common credential format clears this; common phrases ("password1234", "hello world!") don't.
  • Reject all-digit strings and JSON literals regardless of length.
  • Reject very-low-entropy strings (< 2.0 bits/char) to filter out "aaaaaaaaaaaaaaaa"-style repeats. Real keys score 4–5 bits/char.

This is a heuristic, not a security boundary. Operators who paste a real key are auto-protected; operators who paste a placeholder don't poison their own logs.

func IsValidatingMigrations

func IsValidatingMigrations() bool

IsValidatingMigrations reports whether the agent is running in migration validation mode (AGENT_VALIDATE_MIGRATIONS=1). Go migrations that touch external services (S3, Airlock API, connection credentials) should return early when this is true — those services are not available during build-time validation.

func ResolveDisplayPart

func ResolveDisplayPart(p *DisplayPart)

resolveDisplayPart infers missing fields on a DisplayPart from available data. 1. If Data is set but MimeType is empty → detect from bytes. 2. If MimeType is set but Type is empty → infer from MIME prefix. 3. If Filename is empty and part has media → generate from type + mimeType.

func WithCaller added in v0.2.0

func WithCaller(ctx context.Context, c Caller) context.Context

WithCaller attaches a Caller to ctx. Used by the framework when dispatching into untrusted territory (LLM-driven VM, public HTTP).

Types

type Access

type Access string

Access defines who can reach a tool, connection, MCP, topic, or storage zone.

const (
	AccessAdmin  Access = "admin"
	AccessUser   Access = "user"
	AccessPublic Access = "public"
)

type Action

type Action struct {
	Type       string    `json:"type"`
	Timestamp  time.Time `json:"timestamp"`
	DurationMs int64     `json:"durationMs"`
	Request    any       `json:"request,omitempty"`
	Response   any       `json:"response,omitempty"`
	Error      string    `json:"error,omitempty"`
}

Action records a single operation performed during a Run.

type Agent

type Agent struct {

	// Deps holds application-level dependencies (connection handles, MCP handles, etc.).
	// The builder defines their own typed struct and assigns it here.
	// Access from handlers via GetDeps[T](run).
	Deps any
	// contains filtered or unexported fields
}

Agent is a long-lived singleton, one per container. Created once at startup via New(), lives for the lifetime of the process.

func AgentFromContext

func AgentFromContext(ctx context.Context) *Agent

AgentFromContext returns the *Agent associated with a handler's ctx. Returns nil if ctx wasn't produced by a handler (e.g. a plain context.Background() in test code).

func AgentFromMigrationContext

func AgentFromMigrationContext(ctx context.Context) *Agent

AgentFromMigrationContext returns the Agent associated with a migration context. Panics if called outside of a migration — migrations receive the context via goose, which propagates it from autoMigrate.

func New

func New(cfg Config) *Agent

New creates an Agent by reading required environment variables. Panics if AIRLOCK_AGENT_ID, AIRLOCK_API_URL, or AIRLOCK_AGENT_TOKEN is missing. Panics if Config.Description is empty.

func (*Agent) AddExtraPrompt

func (a *Agent) AddExtraPrompt(p *ExtraPrompt)

AddExtraPrompt appends a system-prompt fragment that Airlock will include for callers whose resolved access on this agent matches one of the fragment's Access levels. An empty Access slice means the fragment applies to every caller.

Fragments accumulate in registration order and are joined with "\n\n" by Airlock at run dispatch, then appended to the sync-rendered system prompt. Only /prompt-triggered runs (web + bridge) receive extras — webhook and cron handlers run arbitrary Go code and build their own prompts.

func (*Agent) AddSensitive

func (a *Agent) AddSensitive(values ...string)

AddSensitive registers values that should be redacted from LLM messages. Bypasses the IsLikelySecret filter — use only for values the framework knows are sensitive (e.g. the agent token); otherwise prefer maybeAddSensitive.

func (*Agent) CheckFileAccess added in v0.2.0

func (a *Agent) CheckFileAccess(ctx context.Context, path string, op FileOp) error

CheckFileAccess is the single gate for paths that arrived from untrusted territory: VM run_js code, HTTP requests, tool inputs from the LLM. Builder Go code that constructs paths itself bypasses this check by calling OpenFile/ReadFile/WriteFile/etc. directly.

Returns ErrInvalidPath for malformed paths, ErrNotFound for everything else (denied OR no covering directory). The two latter cases are indistinguishable on purpose so path-guessing reveals nothing.

func (*Agent) CopyFile added in v0.2.0

func (a *Agent) CopyFile(ctx context.Context, src, dst string) error

CopyFile server-side-copies a file from src to dst. Both paths are absolute and may live under different directories. Trusted: no access check.

func (*Agent) DB

func (a *Agent) DB() *AgentDB

DB returns a lazily-initialized *AgentDB from AIRLOCK_DB_URL. Returns nil if the env var is not set (DB is optional).

AgentDB implements the same DBTX interface that sqlc-generated New() takes, so `mygen.New(agent.DB())` works unchanged. The wrapper is the extension point through which the framework can later record query activity onto the run carried by ctx.

func (*Agent) DeleteFile added in v0.2.0

func (a *Agent) DeleteFile(ctx context.Context, path string) error

DeleteFile removes a file. Idempotent — missing files do not error. Trusted: no access check.

func (*Agent) EmbeddingModel

func (a *Agent) EmbeddingModel(ctx context.Context, slug string, opts ModelOpts) model.EmbeddingModel

EmbeddingModel returns an embedding model proxied through Airlock.

func (*Agent) ImageModel

func (a *Agent) ImageModel(ctx context.Context, slug string, opts ModelOpts) model.ImageModel

ImageModel returns an image generation model proxied through Airlock.

func (*Agent) LLM

func (a *Agent) LLM(ctx context.Context, slug string, opts ModelOpts) stream.Model

LLM returns a streaming language model. Capability defaults to CapText if ModelOpts.Capability is empty. Pass the returned model the same ctx when calling Stream.

func (*Agent) ListDir added in v0.2.0

func (a *Agent) ListDir(ctx context.Context, path string, opts ListOpts) ([]FileInfo, error)

ListDir enumerates files under `path`. Trusted: no access check. The empty string lists the agent root.

func (*Agent) Log

func (a *Agent) Log(ctx context.Context, level LogLevel, msg string)

Log records a message scoped to the current handler invocation at the given level. Visible in the Runs UI alongside the actions the handler performed; level controls how the UI surfaces it (color/filter).

Use LogLevelInfo for normal progress, LogLevelWarn for recoverable concerns, and LogLevelError for failures the handler chose not to raise. The argument shape is uniform — pick a level rather than reaching for severity-named methods.

func (*Agent) Logf

func (a *Agent) Logf(ctx context.Context, level LogLevel, format string, args ...any)

Logf is the printf-style sibling of Log — formats with fmt.Sprintf and records the result. Use Log for plain strings, Logf when you'd otherwise reach for fmt.Sprintf.

func (*Agent) OpenFile added in v0.2.0

func (a *Agent) OpenFile(ctx context.Context, path string) (io.ReadCloser, error)

OpenFile streams a file. The returned ReadCloser must be closed by the caller. Trusted: no access check. Used by builder Go code that constructs paths itself.

func (*Agent) ReadFile added in v0.2.0

func (a *Agent) ReadFile(ctx context.Context, path string) ([]byte, error)

ReadFile reads a file fully into memory. For very large files prefer OpenFile + io.Copy. Trusted: no access check.

func (*Agent) RegisterConnection

func (a *Agent) RegisterConnection(c *Connection) *ConnectionHandle

RegisterConnection registers an outgoing service connection and returns a handle for proxied requests. Synced to Airlock on Serve(). Use the returned handle for compile-time-bound proxy calls:

gmail := agent.RegisterConnection(&agentsdk.Connection{
    Slug: "gmail", Name: "Gmail", BaseURL: "https://gmail.googleapis.com", ...,
})
body, err := gmail.Request(ctx, "GET", "/messages", nil)

func (*Agent) RegisterCron

func (a *Agent) RegisterCron(c *Cron)

RegisterCron installs a cron job. Schedule is a standard cron expression (e.g. "0 9 * * *"). Synced to Airlock on Serve() so the scheduler can fire it.

func (*Agent) RegisterDirectory added in v0.2.0

func (a *Agent) RegisterDirectory(path string, opts DirectoryOpts)

RegisterDirectory declares an S3-backed directory at the given path, gated by independent Read / Write / List caps. Inside run_js the flat verbs (readFile, writeFile, listDir, deleteFile, statFile, readBytes, fileExists) check the calling run's access against the directory's caps via CheckFileAccess.

Path is S3-style: no leading '/', no trailing '/', e.g. "uploads" or "reports/q1". A leading slash is rejected — the LLM and builders share one canonical form. Files under the directory are addressed as "uploads/doc.pdf", never "/uploads/doc.pdf".

Builder Go code reads and writes the directory through the trusted file API (agent.OpenFile / ReadFile / WriteFile / StatFile / ListDir / DeleteFile) — these methods do NOT call CheckFileAccess, on the principle that builder code that constructs paths itself is trusted. When a builder tool accepts a path from the LLM (typed as `string` on an Input struct), the builder must call agent.CheckFileAccess explicitly before passing the path anywhere.

The framework reserves "tmp" for its own scratch (truncated tool output, generated media) at Read=Write=List=AccessUser. Builders may call RegisterDirectory("tmp", ...) to override the description; the access caps are kept at the framework's defaults.

agent.RegisterDirectory("uploads", agentsdk.DirectoryOpts{
    Read: agentsdk.AccessUser, Write: agentsdk.AccessUser, List: agentsdk.AccessUser,
    Description: "User uploads",
})
err := agent.WriteFile(ctx, "uploads/doc.pdf", reader, "application/pdf")

func (*Agent) RegisterEnvVar added in v0.2.3

func (a *Agent) RegisterEnvVar(e *EnvVar) *EnvVarHandle

RegisterEnvVar declares an operator-configured environment variable the agent will read at runtime. Returned handle's Get(ctx) fetches the value through Airlock; operators populate the value via the agent's "Environment" tab in the admin UI.

See the EnvVar type doc for the Secret flag's semantics (write-only UI + redaction).

bbKey := agent.RegisterEnvVar(&agentsdk.EnvVar{
    Slug:        "browserbase_api_key",
    Description: "Browserbase API key",
    Secret:      true,
})
// later, inside a tool:
key, err := bbKey.Get(ctx)

func (*Agent) RegisterMCP

func (a *Agent) RegisterMCP(m *MCP) *MCPHandle

RegisterMCP registers a remote MCP server dependency and returns a handle for calling its tools. Synced to Airlock on Serve(). Use the returned handle for compile-time-bound tool calls:

github := agent.RegisterMCP(&agentsdk.MCP{Slug: "github", URL: "https://api.github.com/mcp"})
result, err := github.CallTool(ctx, "search_repos", args)

func (*Agent) RegisterModel

func (a *Agent) RegisterModel(slot *ModelSlot)

RegisterModel declares a named model slot the agent will use at runtime via agent.LLM(ctx, slug, ...) / agent.ImageModel(...) / etc. The admin binds a specific model to each slot in the Airlock UI; if no model is bound, calls fall through to the agent's per-capability default and then to the system default. Call before Serve().

Registering a slot is optional — undeclared slugs still work, they just silently fall through to the capability default and won't appear in the admin UI's Models tab.

func (*Agent) RegisterRoute

func (a *Agent) RegisterRoute(r *Route)

RegisterRoute installs a custom HTTP route served by this agent and proxied via Airlock's subdomain routing.

func (*Agent) RegisterTool

func (a *Agent) RegisterTool(def AnyTool)

RegisterTool registers a typed, schema-bearing capability the LLM can invoke via run_js. The tool auto-binds as a global inside the goja VM; input from JS is JSON-marshaled and decoded into the author's In struct, Execute runs, and the Out struct is JSON-marshaled back to a native JS value. Input/output JSON schemas are rendered as a TypeScript declaration in the system prompt so the LLM sees typed signatures.

agent.RegisterTool(&agentsdk.Tool[SearchIn, SearchOut]{
    Name:        "search",
    Description: "Search the web.",
    Execute:     doSearch,
    Access:      agentsdk.AccessUser,
})

func (*Agent) RegisterTopic

func (a *Agent) RegisterTopic(t *Topic) *TopicHandle

RegisterTopic declares a topic the agent can publish notifications to. Synced to Airlock on Serve(). Use the returned *TopicHandle for compile-time-bound publishing:

alerts := agent.RegisterTopic(&agentsdk.Topic{Slug: "alerts", Description: "System alerts"})
alerts.Publish(ctx, []DisplayPart{{Type: "text", Text: "Server restarted"}})

func (*Agent) RegisterWebhook

func (a *Agent) RegisterWebhook(w *Webhook)

RegisterWebhook installs a webhook handler at /webhook/{Path}. Synced to Airlock on Serve() so external callers can reach it via the agent's webhook ingress endpoint.

func (*Agent) Serve

func (a *Agent) Serve()

Serve starts the agent HTTP server. Blocks until SIGINT/SIGTERM. Listens on AIRLOCK_ADDR env var or :8080. Before starting, syncs connections/webhooks/crons with Airlock.

func (*Agent) ShareFileURL added in v0.2.1

func (a *Agent) ShareFileURL(ctx context.Context, path string, ttl time.Duration) (*ShareFileResponse, error)

ShareFileURL returns a presigned, unauthenticated, time-limited URL pointing at the given storage path. ttl <= 0 picks the server default (1h); the server caps anything over 24h. The URL is signed for the public S3 endpoint when configured, so it works from outside the docker network (browsers, LLM providers, external tools). Trusted: no access check — the JS binding gates LLM-supplied paths via CheckFileAccess.

Use cases: embedding in markdown ([file](url)), sharing externally, cases where the agent's authenticated /__air/storage subdomain route isn't reachable for the recipient. For showing files in chat, prefer printToUser({type:"file", source:path}).

func (*Agent) SpeechModel

func (a *Agent) SpeechModel(ctx context.Context, slug string, opts ModelOpts) model.SpeechModel

SpeechModel returns a text-to-speech model proxied through Airlock.

func (*Agent) StatFile added in v0.2.0

func (a *Agent) StatFile(ctx context.Context, path string) (FileInfo, error)

StatFile returns metadata for a file. Trusted: no access check.

func (*Agent) SyncDown added in v0.2.1

func (a *Agent) SyncDown(ctx context.Context, prefix, localDir string) error

SyncDown copies every file under `prefix` from S3-backed storage into `localDir`. After the call, files that exist in S3 are present locally at the matching subpath (S3 → local mirror within the prefix). Files that exist *only* locally are left in place — this is sync, not a destructive mirror, so the runtime image's seeded /var/agent/bin survives the very first call when S3 is still empty.

Sync semantics: a file is overwritten locally iff the remote object's LastModified is newer than the local file's mtime, or the local file is missing / a different size. After overwriting, the local mtime is set to the remote's LastModified so subsequent SyncUp/SyncDown rounds don't churn the same files back and forth.

Files synced down are chmodded to 0755. The use case is binaries + data caches — both fine with the executable bit set. Set the mode explicitly after the call if you need finer control.

Trusted: no access check (builder code that constructs paths itself).

Use case: pair with SyncUp in a cron handler to persist self-updating binaries (e.g. `bun upgrade`, `freshclam`) across container restarts — the running container's local copy is the working copy, S3 is the durable record. See the agent-builder prompt for a full worked example.

func (*Agent) SyncUp added in v0.2.1

func (a *Agent) SyncUp(ctx context.Context, localDir, prefix string) error

SyncUp is the reverse of SyncDown: walks `localDir` and uploads every file to the matching subpath under `prefix`. A file is uploaded iff the remote is missing, a different size, or older than the local mtime. After upload, the local mtime is matched to the resulting S3 LastModified so subsequent rounds don't churn.

Last-writer-wins semantics on multi-replica: two replicas concurrently uploading the same path will end up with whichever finished last. That's correct for self-updates (both replicas converge to the same new version anyway). For shared mutable state with concurrent writers, use the agent's Postgres schema instead — files are for blobs, rows are for shared state.

Trusted: no access check (builder code that constructs paths itself).

func (*Agent) TranscriptionModel

func (a *Agent) TranscriptionModel(ctx context.Context, slug string, opts ModelOpts) model.TranscriptionModel

TranscriptionModel returns a speech-to-text model proxied through Airlock.

func (*Agent) WriteFile added in v0.2.0

func (a *Agent) WriteFile(ctx context.Context, path string, data io.Reader, contentType string) (FileInfo, error)

WriteFile writes data with the given content type. Returns the resulting FileInfo (path/filename/contentType/size/lastModified). Trusted: no access check.

type AgentDB

type AgentDB struct {
	// contains filtered or unexported fields
}

AgentDB wraps the agent's *sql.DB and is the type returned by Agent.DB(). It implements the same DBTX interface that sqlc-generated New() functions take, so builder code that does `mygen.New(agent.DB())` keeps compiling.

Today AgentDB is a thin pass-through. The reason it exists is so the framework can later intercept queries at this layer (record an action on the run carried by ctx, surface query timings in the Runs UI, redact sensitive arguments) without breaking builders or sqlc-generated code. Builders that need the underlying driver-level handle for advanced cases (Stats, Driver, custom drivers) can reach it via Underlying().

func (*AgentDB) BeginTx

func (a *AgentDB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*AgentTx, error)

BeginTx starts a transaction. The returned *AgentTx implements the same DBTX interface, so `mygen.New(tx)` and `q.WithTx(tx)` both work.

func (*AgentDB) ExecContext

func (a *AgentDB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)

ExecContext satisfies sqlc's DBTX. Forwards to the underlying *sql.DB.

func (*AgentDB) PingContext

func (a *AgentDB) PingContext(ctx context.Context) error

PingContext checks database connectivity.

func (*AgentDB) PrepareContext

func (a *AgentDB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

PrepareContext satisfies sqlc's DBTX. Forwards to the underlying *sql.DB.

func (*AgentDB) QueryContext

func (a *AgentDB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)

QueryContext satisfies sqlc's DBTX. Forwards to the underlying *sql.DB.

func (*AgentDB) QueryRowContext

func (a *AgentDB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row

QueryRowContext satisfies sqlc's DBTX. Forwards to the underlying *sql.DB.

func (*AgentDB) Underlying

func (a *AgentDB) Underlying() *sql.DB

Underlying returns the wrapped *sql.DB. Use this only when you need driver-level access that DBTX doesn't expose; otherwise stick to the AgentDB methods so future framework instrumentation applies to your code.

type AgentTx

type AgentTx struct {
	// contains filtered or unexported fields
}

AgentTx wraps a *sql.Tx with the same shape as AgentDB so sqlc's q.WithTx(tx) accepts it transparently.

func (*AgentTx) Commit

func (a *AgentTx) Commit() error

Commit commits the transaction.

func (*AgentTx) ExecContext

func (a *AgentTx) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)

ExecContext satisfies sqlc's DBTX.

func (*AgentTx) PrepareContext

func (a *AgentTx) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

PrepareContext satisfies sqlc's DBTX.

func (*AgentTx) QueryContext

func (a *AgentTx) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)

QueryContext satisfies sqlc's DBTX.

func (*AgentTx) QueryRowContext

func (a *AgentTx) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row

QueryRowContext satisfies sqlc's DBTX.

func (*AgentTx) Rollback

func (a *AgentTx) Rollback() error

Rollback aborts the transaction. Idempotent — safe to defer.

func (*AgentTx) Underlying

func (a *AgentTx) Underlying() *sql.Tx

Underlying returns the wrapped *sql.Tx.

type AnyTool

type AnyTool interface {
	// contains filtered or unexported methods
}

AnyTool is the sealed interface produced by *Tool[In, Out]. The unexported toRegistered method means only generic Tool values can satisfy it — authors cannot roll their own AnyTool implementations. Used as the argument type for RegisterTool so a heterogeneous set of generic Tool values can share one entry point.

type AuthInjection

type AuthInjection struct {
	Type AuthInjectionType `json:"type"`
	Name string            `json:"name,omitempty"`
}

AuthInjection defines how auth credentials are injected into proxied requests. Name carries the header or query-parameter name depending on Type:

  • api_key_header: header name (default "X-API-Key")
  • query_param: query-string key (default "token")
  • bearer / path_prefix: ignored

type AuthInjectionType added in v0.2.1

type AuthInjectionType string

AuthInjectionType selects how the proxy injects the stored credential into each upstream request.

const (
	// AuthInjectBearer sets `Authorization: Bearer {token}`.
	AuthInjectBearer AuthInjectionType = "bearer"
	// AuthInjectAPIKey sets a custom header `{Name}: {token}` (Name defaults
	// to "X-API-Key").
	AuthInjectAPIKey AuthInjectionType = "api_key_header"
	// AuthInjectPathPrefix prepends `/{token}` to the URL path. Used by
	// APIs that carry credentials in the path (e.g. Telegram bot API).
	AuthInjectPathPrefix AuthInjectionType = "path_prefix"
	// AuthInjectQueryParam appends `?{Name}={token}` (or merges into existing
	// query string). Name defaults to "token". Used by MCP servers and APIs
	// that auth via URL query strings.
	AuthInjectQueryParam AuthInjectionType = "query_param"
)

type AuthRequiredError

type AuthRequiredError struct {
	Slug     string `json:"slug"`
	ConnName string `json:"connName"`
	AuthURL  string `json:"authUrl"`
}

AuthRequiredError is returned by ConnectionHandle.Request when a connection needs authorization.

func IsAuthRequired

func IsAuthRequired(err error) (*AuthRequiredError, bool)

IsAuthRequired checks whether err is an *AuthRequiredError.

func (*AuthRequiredError) Error

func (e *AuthRequiredError) Error() string

type Caller added in v0.2.0

type Caller struct {
	Access Access
	UserID string // optional, for audit
	RunID  string // optional, for audit
}

Caller carries the access level of whoever triggered the current dispatch. Framework dispatch sites (tool Execute, VM bindings, cron, webhook, route, subdomain proxy) inject one onto ctx via WithCaller. Builder Go code that constructs paths itself does NOT need to set a caller — it calls the trusted file API directly (OpenFile/ReadFile/ WriteFile/StatFile/ListDir/DeleteFile) which bypasses CheckFileAccess.

func CallerFrom added in v0.2.0

func CallerFrom(ctx context.Context) Caller

CallerFrom returns the Caller attached to ctx, defaulting to AccessPublic when none is set. This is the fail-closed default: forgetting to tag ctx denies access to anything user-or-above.

type Config

type Config struct {
	Description string // required — shown to users in the Airlock UI
}

Config holds configuration for creating an Agent.

type Connection

type Connection struct {
	Slug              string // unique per agent; binds as conn_{slug} in run_js
	Name              string
	Description       string
	BaseURL           string
	AuthMode          ConnectionAuth
	AuthURL           string
	TokenURL          string
	Scopes            []string
	AuthInjection     AuthInjection
	SetupInstructions string
	LLMHint           string // appended to the connection block in the system prompt
	Access            Access // who may invoke conn_{slug}; default AccessUser
}

Connection is the self-contained declaration registered via agent.RegisterConnection — an outgoing service Airlock proxies for the agent with credentials it manages.

type ConnectionAuth

type ConnectionAuth string

ConnectionAuth enumerates the supported authentication strategies for an outgoing service Connection.

const (
	ConnectionAuthOAuth ConnectionAuth = "oauth"
	ConnectionAuthToken ConnectionAuth = "token"
	ConnectionAuthNone  ConnectionAuth = "none"
)

type ConnectionDef

type ConnectionDef struct {
	Name              string         `json:"name"`
	Description       string         `json:"description"`
	BaseURL           string         `json:"baseUrl,omitempty"`
	AuthMode          ConnectionAuth `json:"authMode"`
	AuthURL           string         `json:"authUrl,omitempty"`
	TokenURL          string         `json:"tokenUrl,omitempty"`
	Scopes            []string       `json:"scopes,omitempty"`
	AuthInjection     AuthInjection  `json:"authInjection"`
	SetupInstructions string         `json:"setupInstructions,omitempty"`
	LLMHint           string         `json:"llmHint,omitempty"`
	Access            Access         `json:"access,omitempty"`
}

ConnectionDef is the wire format used by PUT /api/agent/connections/{slug}. Slug is sent in the URL, not the body.

type ConnectionHandle

type ConnectionHandle struct {
	// contains filtered or unexported fields
}

ConnectionHandle is a compile-time binding to a registered connection. Returned by RegisterConnection, used to make proxied HTTP requests.

func (*ConnectionHandle) Request

func (h *ConnectionHandle) Request(ctx context.Context, method, path string, body any) ([]byte, error)

Request sends an HTTP request through Airlock's credential-injecting proxy and returns the raw response body. Body encoding is chosen from its type:

nil            — no body
[]byte, string — sent as-is
io.Reader      — fully read, sent as-is
anything else  — JSON-marshalled

Returns *AuthRequiredError if the connection needs authorization.

type ConversationVM

type ConversationVM struct {
	// contains filtered or unexported fields
}

ConversationVM holds persistent state for a single conversation. The goja VM is recreated per-run, but the store map survives across prompts.

type ConversationVMConfig

type ConversationVMConfig struct {
	IdleTimeout     time.Duration // default: 72h
	MaxStoreSize    int64         // default: 4MB
	GCSweepInterval time.Duration // default: 1h
}

ConversationVMConfig configures conversation VM behavior.

func DefaultConversationVMConfig

func DefaultConversationVMConfig() ConversationVMConfig

DefaultConversationVMConfig returns sensible defaults.

type CreateRunRequest

type CreateRunRequest struct {
	TriggerType string `json:"triggerType"`
	TriggerRef  string `json:"triggerRef"`
}

CreateRunRequest is the body for POST /api/agent/run/create.

type CreateRunResponse

type CreateRunResponse struct {
	RunID string `json:"runId"`
}

CreateRunResponse is the response from POST /api/agent/run/create.

type Cron

type Cron struct {
	Name        string          // unique per agent
	Schedule    string          // standard cron expression, e.g. "0 9 * * *"
	Handler     CronHandlerFunc // required
	Timeout     time.Duration   // max execution time (default: 2 min)
	Description string
}

Cron is the self-contained declaration registered via agent.RegisterCron. Crons fire by schedule, never by user action — no Access field.

type CronDef

type CronDef struct {
	Name        string `json:"name"`
	Schedule    string `json:"schedule"`
	TimeoutMs   int64  `json:"timeoutMs"`
	Description string `json:"description,omitempty"`
}

CronDef is a cron job definition sent during sync.

type CronHandlerFunc

type CronHandlerFunc func(ctx context.Context, ew *EventWriter) error

CronHandlerFunc handles cron-triggered requests.

type Directory added in v0.2.0

type Directory struct {
	Path        string // S3-style path with no leading '/', e.g. "reports"; no '..' or '//'; no trailing slash
	Read        Access // gates ReadFile / OpenFile / StatFile + the public read route
	Write       Access // gates WriteFile / DeleteFile + the public write route
	List        Access // gates ListDir
	Description string // shown in the system prompt's directories section

	// LLMHint is optional guidance shown to the LLM in the system prompt
	// alongside the directory entry, e.g. "internal cache; avoid listing
	// or modifying" or "user-uploaded reports; prefer summarizing over
	// quoting". Authorization stays with Read/Write/List — LLMHint only
	// steers the model. Empty by default.
	LLMHint string

	// RetentionHours, when > 0, opts the directory into Airlock's storage
	// sweeper: any file in the S3 prefix older than this many hours is
	// deleted on the next sweep tick (~6h cadence). Zero means files
	// stay forever — that's the default for normal builder directories.
	// The framework's /tmp registers with 72 to garbage-collect chat
	// uploads and generated media; tools that produce throwaway artifacts
	// (e.g. AI-generated images served via shareFileURL with a 1h URL
	// expiry) should set a matching short TTL so the bytes go away when
	// the URL does.
	RetentionHours int
}

Directory is the self-contained declaration registered via agent.RegisterDirectory. Each directory owns an S3 prefix ("agents/{agentID}/{Path}") and gates access through three independent caps.

The framework auto-registers a reserved directory "tmp" at Read=Write=List=AccessUser; builder calls with Path="tmp" silently keep the framework's caps (Description may still be supplied).

Read, Write, and List are independent. delete folds into Write (write on the parent governs unlink), so DeleteFile requires Write access.

type DirectoryDef added in v0.2.0

type DirectoryDef struct {
	Path           string `json:"path"`
	Read           Access `json:"read"`
	Write          Access `json:"write"`
	List           Access `json:"list"`
	Description    string `json:"description"`
	LLMHint        string `json:"llmHint,omitempty"`
	RetentionHours int    `json:"retentionHours,omitempty"`
}

DirectoryDef is the wire format sent in SyncRequest.

type DirectoryOpts added in v0.2.0

type DirectoryOpts struct {
	Read        Access // default AccessUser
	Write       Access // default AccessUser
	List        Access // default AccessUser
	Description string

	// LLMHint: see Directory.LLMHint. Optional model-facing guidance.
	LLMHint string

	// RetentionHours: see Directory.RetentionHours. Zero = no sweep.
	RetentionHours int
}

DirectoryOpts is the option struct accepted by RegisterDirectory.

type DisplayPart

type DisplayPart struct {
	Type     string  `json:"type"`             // "text", "image", "file", "audio", "video"
	Text     string  `json:"text,omitempty"`   // body text, or caption for media types
	Source   string  `json:"source,omitempty"` // S3 key
	URL      string  `json:"url,omitempty"`    // external URL
	Data     []byte  `json:"data,omitempty"`   // raw bytes (base64 in JSON)
	Filename string  `json:"filename,omitempty"`
	MimeType string  `json:"mimeType,omitempty"`
	Alt      string  `json:"alt,omitempty"`      // accessibility text for images
	Duration float64 `json:"duration,omitempty"` // seconds, audio/video
}

DisplayPart is a single piece of rich content for user-facing output. Used by both printToUser (VM) and TopicHandle.Publish (Go).

type EnvVar added in v0.2.3

type EnvVar struct {
	// Slug is the unique identifier per agent. Mirrored as the URL
	// segment in /api/v1/agents/{id}/env-vars/{slug}.
	Slug string

	// Description is shown to the operator in the editor UI. Never sent
	// to the LLM.
	Description string

	// Secret toggles the write-only UI affordance + redaction. See the
	// type doc for full semantics.
	Secret bool

	// Default is the value used when the operator hasn't configured the
	// slot. Lets an agent ship with sensible plain-config defaults
	// (region="us-east-1", timeout="30s") that the operator only
	// overrides when needed.
	//
	// Forbidden for Secret=true: there is no sensible default for a
	// credential, and a hardcoded one in agent source would defeat the
	// point of the secrets surface. RegisterEnvVar panics if both are
	// set.
	Default string

	// Pattern is an optional Go regex (RE2) the operator-supplied value
	// must match. Airlock rejects values that don't match at save time,
	// so typos in known-shape credentials (AWS keys, region codes,
	// hostnames) surface immediately rather than at first runtime use.
	// Empty string disables validation.
	//
	// Validated against agent's declaration (mirrors the Description
	// and Default fields), not against a per-set choice — operators
	// can't bypass the pattern.
	Pattern string
}

EnvVar declares an operator-configured environment variable the agent will read at runtime. Operators set the value via Airlock's UI; the agent fetches it through the returned EnvVarHandle.

Two flavours, distinguished by the Secret flag:

  • Secret=false: plain config value (regions, hostnames, feature flags). Operator sees and edits the current value in the UI. Not added to the agent's redact set.
  • Secret=true: credential. Operator can paste a value but cannot read it back — only rotate. Auto-added to the redact set on first Get() so substring matches are stripped from LLM input.

Bytes by convention: base64-encode and decode in agent code. Single string per slug — for compound credentials register multiple slugs.

type EnvVarDef added in v0.2.3

type EnvVarDef struct {
	Slug        string `json:"slug,omitempty"`
	Description string `json:"description"`
	Secret      bool   `json:"secret"`
	Default     string `json:"default,omitempty"`
	Pattern     string `json:"pattern,omitempty"`
}

EnvVarDef is the wire format used by PUT /api/agent/env-vars/{slug} and (with Slug populated) by SyncRequest.EnvVars. Mirrors the agentsdk.EnvVar struct one-to-one.

type EnvVarHandle added in v0.2.3

type EnvVarHandle struct {
	// contains filtered or unexported fields
}

EnvVarHandle is a compile-time binding to a registered EnvVar. Returned by RegisterEnvVar; the agent calls Get(ctx) at runtime to fetch the operator-supplied value. Values are cached on the handle for the lifetime of the agent process — call Refresh() to force a re-fetch (e.g. after the operator rotates the value).

func (*EnvVarHandle) Get added in v0.2.3

func (h *EnvVarHandle) Get(ctx context.Context) (string, error)

Get returns the operator-supplied value, falling back to the agent-declared Default when the operator hasn't set anything (always "" for secrets). For Secret=true vars, the value is registered with the agent's redact set on each fetch so it's stripped from outbound LLM input.

Return shape:

  • (s, nil) — the stored value (or Default if no value was set, or "" if neither). Empty string IS a valid successful return when no Pattern is declared.
  • ("", non-nil) — transport / decrypt error, or the value does not match the declared Pattern. Pattern is checked unconditionally, including against empty strings — declare Pattern="^.+$" to enforce non-empty, or any tighter regex for a known shape. Operators are blocked from saving a non-matching value at the UI, so a mismatch here usually means nothing has been configured yet (or the Pattern was tightened after a save).

Subsequent calls return the cached value until Refresh() is invoked.

func (*EnvVarHandle) IsSecret added in v0.2.3

func (h *EnvVarHandle) IsSecret() bool

IsSecret reports whether this var was registered as a secret.

func (*EnvVarHandle) Refresh added in v0.2.3

func (h *EnvVarHandle) Refresh()

Refresh discards the cached value so the next Get() re-fetches. Useful when the operator has rotated the value mid-run.

func (*EnvVarHandle) Slug added in v0.2.3

func (h *EnvVarHandle) Slug() string

Slug returns the registered slug for this handle.

type EventWriter

type EventWriter struct {
	// contains filtered or unexported fields
}

EventWriter streams NDJSON events to an HTTP response.

func (*EventWriter) WriteError

func (ew *EventWriter) WriteError(err error) error

WriteError writes an error event.

func (*EventWriter) WriteEvent

func (ew *EventWriter) WriteEvent(event stream.Event) error

WriteEvent serializes a GoAI stream.Event as an NDJSON line.

func (*EventWriter) WriteProgress

func (ew *EventWriter) WriteProgress(message string) error

WriteProgress writes a progress event (for webhook/cron handlers).

type ExtraPrompt

type ExtraPrompt struct {
	Text   string
	Access []Access
}

ExtraPrompt is the self-contained declaration passed to agent.AddExtraPrompt. The Text fragment is appended to the system prompt for runs whose caller access matches one of the listed Access levels. Empty Access slice means "applies to every access level."

type ExtraPromptDef

type ExtraPromptDef struct {
	Text   string   `json:"text"`
	Access []Access `json:"access,omitempty"`
}

ExtraPromptDef is the wire format sent in SyncRequest.

type FileInfo added in v0.2.0

type FileInfo struct {
	Path         string    `json:"path"`     // S3-style storage path, e.g. "uploads/foo.png"
	Filename     string    `json:"filename"` // original upload name; S3 metadata
	ContentType  string    `json:"contentType"`
	Size         int64     `json:"size"`
	LastModified time.Time `json:"lastModified"`
}

FileInfo describes a file in agent storage. Returned by StatFile, ListDir, WriteFile, and embedded in PromptInput.Files for chat uploads. Path is the canonical identifier; Filename is the original upload name preserved as S3 metadata so the LLM can refer to "Q1 Report.pdf" while the path uses a uuid-prefixed safe filename.

type FileOp added in v0.2.0

type FileOp string

FileOp tags an operation passed to CheckFileAccess. Delete folds into OpWrite (write on the parent governs unlink); there is no separate OpDelete.

const (
	OpRead  FileOp = "read"
	OpWrite FileOp = "write"
	OpList  FileOp = "list"
)

type HTTPRequest

type HTTPRequest struct {
	URL     string            `json:"url"`
	Method  string            `json:"method,omitempty"` // default: GET
	Headers map[string]string `json:"headers,omitempty"`
	Body    string            `json:"body,omitempty"`
	Timeout int               `json:"timeout,omitempty"` // seconds, default: 30, max: 120
	SaveAs  string            `json:"saveAs,omitempty"`  // save response body to S3 at this key (binary-safe)
	Raw     bool              `json:"raw,omitempty"`     // skip HTML→markdown conversion for HTML responses
}

HTTPRequest is the body for POST /api/agent/http.

type HTTPResponse

type HTTPResponse struct {
	Status      int               `json:"status"`
	Headers     map[string]string `json:"headers"`
	Body        string            `json:"body,omitempty"`
	ContentType string            `json:"contentType"` // original upstream Content-Type
	Size        int               `json:"size"`
	SavedTo     string            `json:"savedTo,omitempty"` // S3 key if body was auto-saved
	Note        string            `json:"note,omitempty"`    // human-readable note about transformations applied (e.g. HTML→markdown conversion)
}

HTTPResponse is returned from POST /api/agent/http.

type HTTPSessionStore

type HTTPSessionStore struct {
	// contains filtered or unexported fields
}

HTTPSessionStore implements session.SessionStore by calling Airlock's API. It is pre-scoped to a single conversation at construction time.

func NewHTTPSessionStore

func NewHTTPSessionStore(client *airlockClient, convID, runID, source string) *HTTPSessionStore

NewHTTPSessionStore creates a store scoped to the given conversation.

func (*HTTPSessionStore) Append

func (s *HTTPSessionStore) Append(ctx context.Context, msgs []session.Message) error

func (*HTTPSessionStore) Compact

func (s *HTTPSessionStore) Compact(ctx context.Context, summary []session.Message, tokensFreed int) error

func (*HTTPSessionStore) Load

type LLMProxyRequest

type LLMProxyRequest struct {
	Slug       string          `json:"slug,omitempty"`
	Capability string          `json:"capability,omitempty"`
	Options    json.RawMessage `json:"options"`
}

LLMProxyRequest is the body for POST /api/agent/llm/stream.

type ListOpts added in v0.2.0

type ListOpts struct {
	// Recursive walks the entire subtree. Zero value (false) lists only
	// files directly under the path (one level only, like `ls`).
	Recursive bool
}

ListOpts controls ListDir.

type LogEntry

type LogEntry struct {
	Level   LogLevel `json:"level"`
	Message string   `json:"message"`
}

LogEntry is one builder-emitted line: a level and a message. The wire format used by /api/agent/run/complete; also the in-memory shape on the run.

type LogLevel

type LogLevel string

LogLevel categorizes a builder-emitted log line. UI can color/filter on it; the wire format stores it explicitly so the level isn't lost in a flat string.

const (
	LogLevelInfo  LogLevel = "info"
	LogLevelWarn  LogLevel = "warn"
	LogLevelError LogLevel = "error"
)

type MCP

type MCP struct {
	Slug     string // unique per agent; binds as mcp_{slug} in run_js
	Name     string
	URL      string
	AuthMode MCPAuth
	AuthURL  string
	TokenURL string
	Scopes   []string
	// AuthInjection picks how the stored credential is added to each MCP
	// HTTP call: bearer header (default), custom header, query parameter,
	// or path prefix. Mirrors Connection.AuthInjection.
	AuthInjection AuthInjection
	Access        Access // who may invoke mcp_{slug}; default AccessUser
}

MCP is the self-contained declaration registered via agent.RegisterMCP. Slug binds as mcp_{slug} in run_js; the builder uses the returned *MCPHandle to call tools from Go.

type MCPAuth

type MCPAuth string

MCPAuth enumerates the supported authentication strategies for an MCP server. MCPAuthOAuthDiscovery is MCP-specific (RFC 9728 server-advertised OAuth endpoints) and not available on Connection.

const (
	MCPAuthOAuth          MCPAuth = "oauth"
	MCPAuthOAuthDiscovery MCPAuth = "oauth_discovery"
	MCPAuthToken          MCPAuth = "token"
	MCPAuthNone           MCPAuth = "none"
)

type MCPAuthStatus

type MCPAuthStatus struct {
	Slug       string  `json:"slug"`
	AuthMode   MCPAuth `json:"authMode"`
	Authorized bool    `json:"authorized"`
	AuthURL    string  `json:"authUrl,omitempty"`
}

MCPAuthStatus reports auth state for an MCP server.

type MCPContent

type MCPContent struct {
	Type string `json:"type"`
	Text string `json:"text,omitempty"`
}

MCPContent is a single content block in an MCP tool response.

type MCPDef

type MCPDef struct {
	Slug          string        `json:"slug,omitempty"`
	Name          string        `json:"name"`
	URL           string        `json:"url"`
	AuthMode      MCPAuth       `json:"authMode"`
	AuthURL       string        `json:"authUrl,omitempty"`
	TokenURL      string        `json:"tokenUrl,omitempty"`
	Scopes        []string      `json:"scopes,omitempty"`
	AuthInjection AuthInjection `json:"authInjection"`
	Access        Access        `json:"access,omitempty"`
}

MCPDef is the wire format used by PUT /api/agent/mcp-servers/{slug} and (with Slug populated) by SyncRequest.MCPServers. Slug is sent in the URL for the per-slug PUT and in the body for the bulk sync.

type MCPHandle

type MCPHandle struct {
	// contains filtered or unexported fields
}

MCPHandle is a compile-time binding to a registered MCP server. Returned by RegisterMCP, used to call tools and build tool sets.

func (*MCPHandle) CallTool

func (h *MCPHandle) CallTool(ctx context.Context, toolName string, args any) (*MCPToolCallResponse, error)

CallTool calls a tool on this MCP server via Airlock's proxy. Args encoding mirrors ConnectionHandle.Request:

nil                           — sent as {} (MCP requires a JSON object)
[]byte, string, json.RawMessage — assumed to be valid JSON, sent as-is
io.Reader                     — fully read, assumed JSON, sent as-is
anything else                 — JSON-marshalled

type MCPToolCallRequest

type MCPToolCallRequest struct {
	Tool      string          `json:"tool"`
	Arguments json.RawMessage `json:"arguments"`
}

MCPToolCallRequest is the body for POST /api/agent/mcp/{slug}/tools/call.

type MCPToolCallResponse

type MCPToolCallResponse struct {
	Content []MCPContent `json:"content"`
	IsError bool         `json:"isError"`
}

MCPToolCallResponse is returned from MCP tool call proxy.

type MCPToolSchema

type MCPToolSchema struct {
	ServerSlug  string          `json:"serverSlug"`
	Name        string          `json:"name"`
	Description string          `json:"description"`
	InputSchema json.RawMessage `json:"inputSchema"`
}

MCPToolSchema is a discovered MCP tool schema returned in SyncResponse.

type MockAirlock

type MockAirlock struct {
	Server *httptest.Server

	// LLMResponse is the NDJSON response returned by POST /api/agent/llm/stream.
	// Set this before making prompt requests.
	LLMResponse []byte
	// contains filtered or unexported fields
}

MockAirlock is an httptest server that implements the Airlock agent API. Use NewMockAirlock() to create one. Exported for agent developers' tests.

func NewMockAirlock

func NewMockAirlock() (*MockAirlock, string)

NewMockAirlock creates a mock Airlock server and returns it along with the base URL.

func (*MockAirlock) Close

func (m *MockAirlock) Close()

Close shuts down the mock server.

func (*MockAirlock) Requests

func (m *MockAirlock) Requests() []MockRequest

Requests returns all recorded requests.

func (*MockAirlock) RequestsByPath

func (m *MockAirlock) RequestsByPath(prefix string) []MockRequest

RequestsByPath returns requests matching the given path prefix.

func (*MockAirlock) Reset

func (m *MockAirlock) Reset()

Reset clears all recorded requests.

type MockRequest

type MockRequest struct {
	Method string
	Path   string
	Body   []byte
}

MockRequest records a request made to the mock Airlock server.

type ModelCapability

type ModelCapability string

ModelCapability describes what kind of model is needed.

const (
	CapText          ModelCapability = "text"          // any chat/language model
	CapVision        ModelCapability = "vision"        // chat model that accepts images
	CapEmbedding     ModelCapability = "embedding"     // vector embeddings
	CapImage         ModelCapability = "image"         // image generation
	CapSpeech        ModelCapability = "speech"        // text-to-speech
	CapTranscription ModelCapability = "transcription" // speech-to-text
)

type ModelOpts

type ModelOpts struct {
	// Capability selects the model sub-type. Only meaningful for run.LLM()
	// (distinguishes text vs vision). For other methods, the method name
	// determines the capability and this field is ignored.
	Capability ModelCapability `json:"capability,omitempty"`

	// Description is optional human-readable context for run logs/UI.
	Description string `json:"description,omitempty"`
}

ModelOpts configures a model request. Used with agent.LLM(), agent.ImageModel(), etc.

type ModelProxyRequest

type ModelProxyRequest struct {
	Slug       string          `json:"slug,omitempty"`
	Capability string          `json:"capability"`
	Options    json.RawMessage `json:"options"`
}

ModelProxyRequest is the body for non-streaming model endpoints (POST /api/agent/llm/{image,embedding,speech,transcription}).

type ModelSlot

type ModelSlot struct {
	Slug        string
	Capability  ModelCapability // required: CapText, CapVision, CapImage, CapSpeech, CapTranscription, CapEmbedding
	Description string          // human-readable hint shown in the admin UI
}

ModelSlot is the self-contained declaration registered via agent.RegisterModel.

type ModelSlotDef

type ModelSlotDef struct {
	Slug        string `json:"slug"`
	Capability  string `json:"capability"`
	Description string `json:"description,omitempty"`
}

ModelSlotDef is the wire format sent in SyncRequest. The agent uses Slug at runtime (e.g. `agent.LLM(ctx, slug, ...)`); the admin binds a specific model to the slug in the Airlock UI. When no model is bound, calls fall through to the agent's per-capability default and then to the system default for that capability.

type PrintRequest

type PrintRequest struct {
	Parts          []DisplayPart `json:"parts"`
	Topic          string        `json:"topic,omitempty"`          // empty = direct to conversation
	ConversationID string        `json:"conversationId,omitempty"` // set for direct prints
	RunID          string        `json:"runId,omitempty"`          // originating run, used to sort ephemerals after their run's assistant messages
}

PrintRequest is the body for POST /api/agent/print.

type PromptInput

type PromptInput struct {
	Messages            []message.Message `json:"messages"`
	Message             string            `json:"message,omitempty"` // New user message text (used with SessionStore)
	ConversationID      string            `json:"conversationId,omitempty"`
	ProviderID          string            `json:"providerId,omitempty"`
	ModelID             string            `json:"modelId,omitempty"`
	Temperature         *float64          `json:"temperature,omitempty"`
	MaxOutputTokens     *int              `json:"maxOutputTokens,omitempty"`
	ProviderOptions     json.RawMessage   `json:"providerOptions,omitempty"`
	Files               []FileInfo        `json:"files,omitempty"`
	ResumeRunID         string            `json:"resumeRunId,omitempty"`
	Approved            *bool             `json:"approved,omitempty"`
	SupportedModalities []string          `json:"supportedModalities,omitempty"` // e.g. ["text", "image", "pdf", "audio", "video"]
	Source              string            `json:"source,omitempty"`              // "user" (default), "system" (injected by Airlock)

	// ExtraSystemPrompt is an access-filtered concatenation of the agent's
	// registered AddExtraPrompt fragments, composed by Airlock at run
	// dispatch. The agent appends this to its sync-cached system prompt.
	ExtraSystemPrompt string `json:"extraSystemPrompt,omitempty"`

	// CallerAccess is the resolved per-(agent, user) access level for the
	// triggering caller. agentsdk uses it to gate which conn_/mcp_/topic_/
	// storage_ JS bindings (and registered tools) are exposed to the run.
	// Airlock sets this from trigger.ResolveAgentAccess. For trusted server
	// triggers (webhooks, crons) Airlock sends AccessAdmin.
	CallerAccess Access `json:"callerAccess,omitempty"`

	// ForceCompact tells the agent to skip the thinking loop and run a
	// user-triggered compaction instead. Message is ignored when set. The
	// agent loads conversation history, asks the model to summarize it,
	// persists the summary via the SessionStore's Compact method, and emits
	// a short text-delta describing the outcome.
	ForceCompact bool `json:"forceCompact,omitempty"`
}

PromptInput is the request body for POST /prompt.

type ProxyRequest

type ProxyRequest struct {
	Method string `json:"method"`
	Path   string `json:"path"`
	Body   string `json:"body,omitempty"`
}

ProxyRequest is the body for POST /api/agent/proxy/{slug}.

type Route

type Route struct {
	Method      string           // "GET", "POST", ...
	Path        string           // e.g. "/spotify"
	Handler     RouteHandlerFunc // required
	Access      Access           // required: AccessAdmin, AccessUser, or AccessPublic
	Description string
}

Route is the self-contained declaration registered via agent.RegisterRoute. Custom HTTP routes served by the agent and proxied by Airlock via subdomain routing. The (Method, Path) pair must be unique per agent.

type RouteDef

type RouteDef struct {
	Path        string `json:"path"`
	Method      string `json:"method"`
	Access      Access `json:"access"`
	Description string `json:"description,omitempty"`
}

RouteDef is a custom HTTP route definition sent during sync.

type RouteHandlerFunc

type RouteHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request)

RouteHandlerFunc handles custom HTTP routes registered via RegisterRoute.

type RunCompleteRequest

type RunCompleteRequest struct {
	RunID string `json:"runId"`
	// Status is "success" | "error" | "suspended" | "timeout" | "tool_errors".
	Status string `json:"status"`
	Error  string `json:"error,omitempty"`
	// ErrorKind is set when Status == "error" and disambiguates platform
	// vs agent failure for the UI. Empty otherwise.
	ErrorKind  string          `json:"errorKind,omitempty"`
	PanicTrace string          `json:"panicTrace,omitempty"`
	Actions    json.RawMessage `json:"actions"`
	Logs       []LogEntry      `json:"logs,omitempty"`
	Checkpoint json.RawMessage `json:"checkpoint,omitempty"`
}

RunCompleteRequest is the body for POST /api/agent/run/complete.

type ShareFileRequest added in v0.2.1

type ShareFileRequest struct {
	Path           string `json:"path"`
	ExpiresSeconds int64  `json:"expiresSeconds,omitempty"`
}

ShareFileRequest is the body for POST /api/agent/storage/share. Path is an S3-style storage path (no leading slash); ExpiresSeconds caps how long the returned URL is valid for. Server defaults to 1h if 0, caps at 24h.

type ShareFileResponse added in v0.2.1

type ShareFileResponse struct {
	URL         string `json:"url"`
	ExpiresAtMs int64  `json:"expiresAtMs"`
}

ShareFileResponse is returned by POST /api/agent/storage/share. URL is unauthenticated and valid until ExpiresAtMs (ms epoch).

type SyncRequest

type SyncRequest struct {
	Version      string           `json:"version"`
	Description  string           `json:"description,omitempty"`
	Tools        []ToolDef        `json:"tools,omitempty"`
	Webhooks     []WebhookDef     `json:"webhooks"`
	Crons        []CronDef        `json:"crons"`
	Routes       []RouteDef       `json:"routes,omitempty"`
	Topics       []TopicDef       `json:"topics,omitempty"`
	MCPServers   []MCPDef         `json:"mcpServers,omitempty"`
	EnvVars      []EnvVarDef      `json:"envVars,omitempty"`
	Directories  []DirectoryDef   `json:"directories,omitempty"`
	ExtraPrompts []ExtraPromptDef `json:"extraPrompts,omitempty"`
	ModelSlots   []ModelSlotDef   `json:"modelSlots,omitempty"`
}

SyncRequest is the body for PUT /api/agent/sync.

type SyncResponse

type SyncResponse struct {
	SystemPrompt  string          `json:"systemPrompt"`
	MCPAuthStatus []MCPAuthStatus `json:"mcpAuthStatus,omitempty"`
	// MCPSchemas carries discovered tool schemas per MCP server slug.
	// Airlock populates these from its server-side discovery cache so the
	// agent's VM can install one typed JS method per tool on each
	// `mcp_{slug}` object — no per-run discovery round-trips.
	MCPSchemas map[string][]MCPToolSchema `json:"mcpSchemas,omitempty"`
	// PublicStorageBase is the URL prefix at which directories are reachable
	// on the agent's subdomain, ending without a trailing slash. Callers
	// join with '/' and the storage path (e.g. "reports/q1.csv") to
	// construct a URL: "https://{slug}.{domain}/__air/storage/reports/q1.csv".
	// The proxy enforces the directory's Read cap at fetch time — public
	// dirs serve unauthenticated, user/admin dirs require subdomain login
	// (redirect-on-missing-cookie).
	PublicStorageBase string `json:"publicStorageBase,omitempty"`
}

SyncResponse is the response from PUT /api/agent/sync.

type Tool

type Tool[In any, Out any] struct {
	Name        string
	Description string

	// LLMHint is optional model-only guidance — usage tips, deprecation
	// warnings, "prefer X over Y" notes — that you don't want to leak to
	// member UIs that render Description (e.g. the agent dashboard's
	// Tools tab). When this struct is later lowered into goai.Tool the
	// hint is appended to Description in `[brackets]`; goai.Tool itself
	// has no LLMHint field (it mirrors ai-sdk's LanguageModelV4FunctionTool).
	LLMHint string

	Execute       func(ctx context.Context, input In) (Out, error)
	Access        Access
	InputExamples []In
}

Tool is the declarative description of a typed, schema-bearing capability the LLM can invoke via run_js. In and Out are Go structs whose JSON schemas are generated at registration time and rendered as TypeScript signatures in the system prompt. Execute receives the unmarshaled In and returns Out — the wrapper handles JSON boundaries on both sides.

Construct with a pointer literal:

agent.RegisterTool(&agentsdk.Tool[SearchIn, SearchOut]{
    Name:        "search",
    Description: "Search the web.",
    Execute:     doSearch,
    Access:      agentsdk.AccessUser,
})

type ToolDef

type ToolDef struct {
	Name          string            `json:"name"`
	Description   string            `json:"description"`
	LLMHint       string            `json:"llmHint,omitempty"`
	Access        Access            `json:"access"`
	InputSchema   json.RawMessage   `json:"inputSchema,omitempty"`
	OutputSchema  json.RawMessage   `json:"outputSchema,omitempty"`
	InputExamples []json.RawMessage `json:"inputExamples,omitempty"`
}

ToolDef describes a registered tool sent during sync. Carries the JSON schemas for input and output so Airlock can render TypeScript signatures in the system prompt and surface them in the UI.

type Topic

type Topic struct {
	Slug        string
	Description string
	LLMHint     string // optional model-only guidance — see Directory.LLMHint
	Access      Access // who may subscribe via topic_{slug}.subscribe(); default AccessUser
}

Topic is the self-contained declaration registered via agent.RegisterTopic. Conversations subscribe to a topic via topic_{slug}.subscribe() in run_js; builders publish via the *TopicHandle returned by RegisterTopic.

type TopicDef

type TopicDef struct {
	Slug        string `json:"slug"`
	Description string `json:"description"`
	LLMHint     string `json:"llmHint,omitempty"`
	Access      Access `json:"access"`
}

TopicDef is the wire format sent in SyncRequest.

type TopicHandle

type TopicHandle struct {
	// contains filtered or unexported fields
}

TopicHandle is a compile-time binding to a registered topic. Returned by Agent.RegisterTopic; used for type-safe publishing.

func (*TopicHandle) Publish

func (h *TopicHandle) Publish(ctx context.Context, parts []DisplayPart) error

Publish sends display parts to all conversations subscribed to this topic.

type Webhook

type Webhook struct {
	Path        string             // unique per agent
	Handler     WebhookHandlerFunc // required
	Verify      string             // "hmac" | "token" | "none" (default: "none")
	Header      string             // header carrying the signature/token (hmac/token modes)
	Timeout     time.Duration      // max execution time (default: 2 min)
	Description string
	Access      Access // who may invoke; default AccessUser
}

Webhook is the self-contained declaration registered via agent.RegisterWebhook. Agents serve incoming HTTP at /webhook/{Path} on their container.

type WebhookDef

type WebhookDef struct {
	Path        string `json:"path"`
	Verify      string `json:"verify"`
	Header      string `json:"header,omitempty"`
	TimeoutMs   int64  `json:"timeoutMs"`
	Description string `json:"description,omitempty"`
}

WebhookDef is a webhook definition sent during sync.

type WebhookHandlerFunc

type WebhookHandlerFunc func(ctx context.Context, data []byte, ew *EventWriter) error

WebhookHandlerFunc handles incoming webhook requests. Pass ctx to any agent.X(ctx, ...) call the body makes.

Directories

Path Synopsis
Package tsrender produces the TypeScript .d.ts blocks that Airlock embeds into the agent system prompt: typed signatures for registered tools, and per-server `declare const mcp_{slug}: {...}` namespaces for MCP tools.
Package tsrender produces the TypeScript .d.ts blocks that Airlock embeds into the agent system prompt: typed signatures for registered tools, and per-server `declare const mcp_{slug}: {...}` namespaces for MCP tools.

Jump to

Keyboard shortcuts

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