Skip to main content

Agent Shell

A virtual bash environment with a persistent filesystem backed by Tigris object storage, written in TypeScript and designed for AI agents.

AI agents produce artifacts — reports, data, configs, logs. These need to go somewhere durable, shareable, and globally accessible. Agent Shell gives agents a familiar bash interface (cat, grep, sed, jq, awk, pipes, redirects) where every file operation is backed by a Tigris bucket.

  • Isolated — writes stay in-memory until you explicitly flush. No partial state leaks to storage.
  • Durable — flush persists files to Tigris, globally distributed.
  • Checkpointable — take snapshots of your storage at any point. Roll back if needed.
  • Forkable — create copy-on-write forks of a bucket for safe experimentation.
  • Shareable — generate presigned URLs for any stored file.

Built on just-bash for the shell engine and @tigrisdata/storage for the storage layer.

Getting Started

Installation

npm install @tigrisdata/agent-shell

Programmatic Usage

import { TigrisShell } from "@tigrisdata/agent-shell";

const shell = new TigrisShell({
accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
bucket: process.env.TIGRIS_STORAGE_BUCKET, // optional — auto-mounts at cwd (default: /workspace)
});

await shell.exec('echo "Hello from agent-shell" > greeting.txt');
await shell.exec("cat greeting.txt | tr a-z A-Z");
// stdout: "HELLO FROM AGENT-SHELL"

// Persist changes to Tigris
await shell.flush();

Interactive Shell

Launch a shell directly — no install needed:

npx @tigrisdata/agent-shell

Authenticate with access keys:

$ configure --key tid_... --secret tsec_...
Mounted 2 bucket(s) at /. Run 'df' to list them.

/ $ ls
my-bucket shared-data
/ $ cd my-bucket
/my-bucket $ echo "hello" > greeting.txt
/my-bucket $ cat greeting.txt
hello
/my-bucket $ flush
Flushed 2 mount(s)

Or login with your Tigris account:

$ login
Open this URL in your browser:
https://auth.storage.tigrisdata.io/activate?user_code=XKCD-1234

Waiting for authorization... done!
Logged in as you@example.com

Mounted 2 bucket(s) at /. Run 'df' to list them.

You can also pass credentials as flags:

npx @tigrisdata/agent-shell --key tid_... --secret tsec_... --bucket my-bucket

Storage Model

Agent Shell uses an in-memory write-back cache that provides isolation:

Agent writes file → cached in memory (isolated)
Agent reads file → cache hit or fetch from Tigris
Agent calls flush → all changes persisted atomically

This gives you:

  • Isolation — nothing touches storage until you say so
  • Atomic commits — if the agent fails midway, no partial state is written
  • Fast execution — most operations never hit the network
const shell = new TigrisShell({
accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
bucket: process.env.TIGRIS_STORAGE_BUCKET,
});

try {
await shell.exec('echo "processing..." > status.txt');
await shell.exec("echo '{\"score\": 0.95}' > results.json");
await shell.exec("cat results.json | jq .score"); // "0.95\n"

// Only persist on success
await shell.flush();
} catch (e) {
// Nothing was written to Tigris — storage is clean
}

Configuration

TigrisConfig

OptionTypeRequiredDescription
bucketstringNoBucket name — auto-mounted at cwd
accessKeyIdstringNo*Tigris access key ID
secretAccessKeystringNo*Tigris secret access key
sessionTokenstringNo*Session token (from OAuth)
organizationIdstringNo*Organization ID (required with token)
endpointstringNoTigris endpoint URL
note

At least one auth mode is required: (accessKeyId + secretAccessKey) or (sessionToken + organizationId).

ShellOptions

OptionTypeDefaultDescription
cwdstring/workspaceStarting working directory and auto-mount point
envRecord<string, string>{}Initial environment variables for the shell
const shell = new TigrisShell(
{
accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
bucket: process.env.TIGRIS_STORAGE_BUCKET,
},
{ cwd: "/my-project", env: { NODE_ENV: "production" } },
);

API Reference

TigrisShell

Method / PropertyTypeDescription
constructor(config: TigrisConfig, options?: ShellOptions)Create a new shell instance
exec(command)Promise<BashExecResult>Execute a bash command
mount(bucket, mountPoint)voidMount a bucket at a path
unmount(mountPoint)voidUnmount a path
listMounts()Array<{ bucket, mountPoint }>List current mounts
flush(mountPoint?)Promise<void>Flush all mounts or a specific one
engineBashUnderlying just-bash instance

Sub-exports

Agent Shell exposes additional modules for advanced use cases:

// Filesystem adapter for custom storage layouts
import { TigrisAdapter } from "@tigrisdata/agent-shell/fs";

// Individual Tigris command factories
import {
createTigrisCommands,
createPresignCommand,
createSnapshotCommand,
createForkCommand,
} from "@tigrisdata/agent-shell/commands";

Built-in Tigris Commands

Beyond standard bash commands, Agent Shell includes Tigris-specific commands.

presign

Generate presigned URLs for sharing or uploading:

presign /path/to/file.txt # GET URL, 1 hour expiry
presign /path/to/file.txt --expires 7200 # GET URL, 2 hour expiry
presign /path/to/file.txt --put # PUT URL for uploads

snapshot

Checkpoint your storage. Create or list point-in-time bucket snapshots:

snapshot my-bucket # Create a snapshot
snapshot my-bucket --name "checkpoint-1" # Create a named snapshot
snapshot my-bucket --list # List all snapshots

fork

Branch your storage. Create a copy-on-write fork for safe experimentation:

fork source-bucket my-fork # Fork a bucket
fork source-bucket my-fork --snapshot 1713200000 # Fork from a specific snapshot

forks

List forks of a bucket:

forks my-bucket

Multi-Bucket

Mount multiple buckets at different paths:

import { TigrisShell } from "@tigrisdata/agent-shell";

const shell = new TigrisShell({
accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
bucket: "agent-workspace", // auto-mounted at cwd (default: /workspace)
});

shell.mount("shared-datasets", "/datasets");

await shell.exec("cat /datasets/training/labels.csv | head -10");
await shell.exec("cp /datasets/training/labels.csv ./local-copy.csv");
await shell.exec('echo "processed" > results.txt');

// Flush all mounts, or a specific one
await shell.flush(); // all
await shell.flush("/datasets"); // just /datasets

// List and unmount
shell.listMounts();
shell.unmount("/datasets");

Or in the interactive shell:

$ mount shared-datasets /datasets
Mounted shared-datasets at /datasets

$ cp /datasets/data.csv ./local.csv
$ df
Bucket Mounted on
agent-workspace /agent-workspace
shared-datasets /datasets

$ umount /datasets
Unmounted /datasets

Examples

Processing Files

import { TigrisShell } from "@tigrisdata/agent-shell";

const shell = new TigrisShell({
bucket: process.env.TIGRIS_STORAGE_BUCKET,
accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
});

// Read a file already in the bucket
const result = await shell.exec("cat data.csv");
console.log(result.stdout);

// Process with standard Unix tools
const sorted = await shell.exec("cat data.csv | tail -n +2 | sort -t, -k2 -rn");
console.log(sorted.stdout);

// Write results and persist
await shell.exec("cat data.csv | wc -l > stats.txt");
await shell.flush();

Snapshots, Forks, and Presigned URLs

import { TigrisShell } from "@tigrisdata/agent-shell";

const shell = new TigrisShell({
bucket: process.env.TIGRIS_STORAGE_BUCKET,
accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
});

// Write a file and persist
await shell.exec('echo "quarterly report" > report.txt');
await shell.flush();

// Generate a shareable URL
const getUrl = await shell.exec("presign report.txt");
console.log(getUrl.stdout.trim());

// Checkpoint storage before changes
await shell.exec("snapshot my-bucket --name before-migration");

// Branch storage for safe experimentation
await shell.exec("fork my-bucket my-bucket-experiment");