+
+
+ Running coding agents remotely is hard.
+
+
+ The Sandbox Agent SDK is a server that runs inside your sandbox. Your app connects remotely to control Claude Code, Codex, OpenCode, Amp, or Pi — streaming events, handling permissions, managing sessions.
+
+
+
-
- Running coding agents remotely is hard.
-
-
- Coding agents need sandboxes, but existing SDKs assume local execution. SSH breaks, CLI wrappers are fragile, and building from scratch means reimplementing everything for each coding agent.
-
-
-
-
- {frictions.map((friction, index) => {
- const styles = accentStyles[friction.accentColor as keyof typeof accentStyles];
- return (
-
- {/* Top shine */}
-
-
- {/* Hover glow */}
-
-
- {/* Corner highlight */}
-
-
-
- {/* Title */}
-
{friction.title}
-
- {/* Problem */}
-
-
- {/* Solution */}
-
-
-
{friction.solution}
-
+ {frictions.map((friction) => (
+
+
+
+
+
{friction.title}
+
+
+
+ Problem
-
- );
- })}
-
+
+ {friction.problem}
+
+
+
+
+
+ Solution
+
+
+ {friction.solution}
+
+
+
+ ))}
+
);
diff --git a/frontend/packages/website/src/layouts/Layout.astro b/frontend/packages/website/src/layouts/Layout.astro
index 8e7f8e6..25683cd 100644
--- a/frontend/packages/website/src/layouts/Layout.astro
+++ b/frontend/packages/website/src/layouts/Layout.astro
@@ -4,32 +4,81 @@ interface Props {
description?: string;
}
-const { title, description = "Universal SDK for coding agents. Control Claude Code, Codex, OpenCode, and Amp with unified events and sessions." } = Astro.props;
+const { title, description = "Universal SDK for coding agents. Control Claude Code, Codex, OpenCode, Amp, and Pi with unified events and sessions." } = Astro.props;
+const canonicalURL = new URL(Astro.url.pathname, 'https://sandbox-agent.dev');
+const ogImageURL = new URL('/og.png', 'https://sandbox-agent.dev');
+
+const structuredData = {
+ "@context": "https://schema.org",
+ "@type": "SoftwareApplication",
+ "name": "Sandbox Agent SDK",
+ "applicationCategory": "DeveloperApplication",
+ "operatingSystem": "Linux, macOS, Windows",
+ "description": description,
+ "url": "https://sandbox-agent.dev",
+ "author": {
+ "@type": "Organization",
+ "name": "Rivet",
+ "url": "https://rivet.dev"
+ },
+ "offers": {
+ "@type": "Offer",
+ "price": "0",
+ "priceCurrency": "USD"
+ },
+ "keywords": "coding agents, AI SDK, Claude Code, Codex, OpenCode, Amp, sandbox, remote code execution, developer tools"
+};
---
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{title}
+
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
-
+
diff --git a/frontend/packages/website/src/pages/index.astro b/frontend/packages/website/src/pages/index.astro
index da607db..854c82f 100644
--- a/frontend/packages/website/src/pages/index.astro
+++ b/frontend/packages/website/src/pages/index.astro
@@ -11,8 +11,7 @@ import { Footer } from '../components/Footer';
---
-
-
+
@@ -21,6 +20,5 @@ import { Footer } from '../components/Footer';
-
-
+
diff --git a/frontend/packages/website/src/styles/global.css b/frontend/packages/website/src/styles/global.css
index 8dbb6f8..6f4187e 100644
--- a/frontend/packages/website/src/styles/global.css
+++ b/frontend/packages/website/src/styles/global.css
@@ -3,13 +3,61 @@
@tailwind utilities;
@layer base {
+ :root {
+ --header-height: 3.5rem;
+
+ /* Theme colors (HSL for flexibility) */
+ --background: 20 14.3% 4.1%;
+ --foreground: 60 9.1% 97.8%;
+ --primary: 18.5 100% 50%;
+ --primary-foreground: 60 9.1% 97.8%;
+ --muted: 34 10% 10%;
+ --muted-foreground: 24 5.4% 63.9%;
+ --border: 12 6.5% 15.1%;
+ --card: 0 9.09% 6.47%;
+
+ /* Shiki syntax highlighting */
+ --shiki-color-text: theme('colors.white');
+ --shiki-foreground: hsl(var(--foreground));
+ --shiki-token-constant: theme('colors.violet.300');
+ --shiki-token-string: theme('colors.violet.300');
+ --shiki-token-comment: theme('colors.zinc.500');
+ --shiki-token-keyword: theme('colors.sky.300');
+ --shiki-token-parameter: theme('colors.pink.300');
+ --shiki-token-function: theme('colors.violet.300');
+ --shiki-token-string-expression: theme('colors.violet.300');
+ --shiki-token-punctuation: theme('colors.zinc.200');
+ }
+
+ * {
+ @apply border-white/10;
+ }
+
body {
@apply bg-black text-white antialiased;
font-family: 'Open Sans', system-ui, sans-serif;
}
+ /* Text selection - matches rivet.dev */
::selection {
- @apply bg-accent/30 text-white;
+ background-color: rgba(255, 79, 0, 0.3);
+ color: #fed7aa;
+ }
+
+ ::-moz-selection {
+ background-color: rgba(255, 79, 0, 0.3);
+ color: #fed7aa;
+ }
+
+ /* Selection style for white/light backgrounds */
+ .selection-dark::selection {
+ background-color: #18181b;
+ color: white;
+ }
+
+ .selection-dark::-moz-selection {
+ background-color: #18181b;
+ color: white;
}
/* Firefox scrollbar */
@@ -65,6 +113,7 @@
}
@layer components {
+ /* Glass morphism effects */
.glass {
@apply bg-white/[0.02] backdrop-blur-md border border-white/10;
}
@@ -72,4 +121,123 @@
.glass-hover {
@apply hover:bg-white/[0.04] hover:border-white/20 transition-all;
}
+
+ .glass-strong {
+ @apply bg-black/95 backdrop-blur-lg border border-white/10;
+ }
+
+ /* Bento box card effects */
+ .bento-box {
+ transition: border-color 0.3s ease;
+ }
+
+ .bento-box:hover {
+ border-color: rgba(255, 255, 255, 0.2);
+ }
+
+ /* Scroll-triggered animations */
+ .animate-on-scroll {
+ opacity: 0;
+ transition: opacity 0.8s ease-out, transform 0.8s cubic-bezier(0.19, 1, 0.22, 1);
+ transition-delay: var(--scroll-delay, 0s);
+ will-change: opacity, transform;
+ }
+
+ .animate-fade-up {
+ transform: translateY(30px);
+ }
+
+ .animate-on-scroll.is-visible {
+ opacity: 1;
+ transform: translateY(0);
+ }
+
+ /* Delay utilities for staggered animations */
+ .delay-100 { --scroll-delay: 100ms; }
+ .delay-200 { --scroll-delay: 200ms; }
+ .delay-300 { --scroll-delay: 300ms; }
+ .delay-400 { --scroll-delay: 400ms; }
+ .delay-500 { --scroll-delay: 500ms; }
+ .delay-600 { --scroll-delay: 600ms; }
+
+ /* Top shine highlight for cards */
+ .shine-top {
+ position: relative;
+ }
+
+ .shine-top::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ height: 1px;
+ background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.2), transparent);
+ pointer-events: none;
+ }
+
+ /* Glow effect for buttons and interactive elements */
+ .glow-accent {
+ box-shadow: 0 0 20px rgba(255, 69, 0, 0.3);
+ }
+
+ .glow-accent-hover:hover {
+ box-shadow: 0 0 30px rgba(255, 69, 0, 0.5);
+ }
+
+ /* Code highlight styling */
+ .code-highlight-ref {
+ position: relative;
+ transition: background-color 0.3s ease-out;
+ display: block;
+ margin: 0 -1.5rem;
+ padding: 0 1.5rem;
+ }
+
+ .code-highlight-ref.is-active {
+ background-color: rgba(255, 69, 0, 0.1);
+ }
+
+ .code-highlight-ref.is-active::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 2px;
+ background-color: #ff4500;
+ }
+
+ /* Hide scrollbar */
+ .scrollbar-hide::-webkit-scrollbar {
+ display: none;
+ }
+
+ .scrollbar-hide {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+ }
+}
+
+@layer utilities {
+ /* Gradient text */
+ .text-gradient-accent {
+ @apply bg-gradient-to-r from-orange-400 to-orange-600 bg-clip-text text-transparent;
+ }
+
+ /* Backdrop with blur */
+ .backdrop-glow {
+ @apply backdrop-blur-lg bg-black/80;
+ }
+
+ /* Better focus ring */
+ .focus-ring {
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-black;
+ }
+}
+
+/* View transition disable (for smooth prefetching) */
+::view-transition-old(root),
+::view-transition-new(root) {
+ animation: none;
}
diff --git a/frontend/packages/website/tailwind.config.mjs b/frontend/packages/website/tailwind.config.mjs
index 142adbc..168c2ff 100644
--- a/frontend/packages/website/tailwind.config.mjs
+++ b/frontend/packages/website/tailwind.config.mjs
@@ -4,21 +4,59 @@ export default {
theme: {
extend: {
colors: {
+ // Primary accent (OrangeRed)
accent: '#FF4500',
+ // Extended color palette
+ background: '#000000',
+ 'text-primary': '#FAFAFA',
+ 'text-secondary': '#A0A0A0',
+ border: '#252525',
+ // Code syntax highlighting
+ 'code-keyword': '#c084fc',
+ 'code-function': '#60a5fa',
+ 'code-string': '#4ade80',
+ 'code-comment': '#737373',
},
fontFamily: {
sans: ['Open Sans', 'system-ui', 'sans-serif'],
+ heading: ['Satoshi', 'Open Sans', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
animation: {
- 'fade-in-up': 'fade-in-up 0.6s ease-out forwards',
- 'pulse-slow': 'pulse 3s ease-in-out infinite',
+ 'fade-in-up': 'fade-in-up 0.8s ease-out forwards',
+ 'hero-line': 'hero-line 1s cubic-bezier(0.19, 1, 0.22, 1) forwards',
+ 'hero-p': 'hero-p 0.8s ease-out 0.6s forwards',
+ 'hero-cta': 'hero-p 0.8s ease-out 0.8s forwards',
+ 'hero-visual': 'hero-p 0.8s ease-out 1s forwards',
+ 'infinite-scroll': 'infinite-scroll 25s linear infinite',
+ 'pulse-slow': 'pulse-slow 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
keyframes: {
'fade-in-up': {
- '0%': { opacity: '0', transform: 'translateY(20px)' },
- '100%': { opacity: '1', transform: 'translateY(0)' },
+ from: { opacity: '0', transform: 'translateY(24px)' },
+ to: { opacity: '1', transform: 'translateY(0)' },
},
+ 'hero-line': {
+ '0%': { opacity: '0', transform: 'translateY(100%) skewY(6deg)' },
+ '100%': { opacity: '1', transform: 'translateY(0) skewY(0deg)' },
+ },
+ 'hero-p': {
+ from: { opacity: '0', transform: 'translateY(20px)' },
+ to: { opacity: '1', transform: 'translateY(0)' },
+ },
+ 'infinite-scroll': {
+ from: { transform: 'translateX(0)' },
+ to: { transform: 'translateX(-50%)' },
+ },
+ 'pulse-slow': {
+ '50%': { opacity: '.5' },
+ },
+ },
+ spacing: {
+ header: 'var(--header-height, 3.5rem)',
+ },
+ borderRadius: {
+ '4xl': '2rem',
},
},
},
From 46193747e66c88bc8ddb0edd3952a2dbd706b8b8 Mon Sep 17 00:00:00 2001
From: Nathan Flurry
Date: Wed, 11 Feb 2026 17:51:32 -0800
Subject: [PATCH 2/6] fix: dynamically discover packages in release script
instead of hardcoding
- sdk.ts: discoverNpmPackages() + topoSort() for library packages
- sdk.ts: discoverCrates() via cargo metadata for workspace crates
- sdk.ts: publishNpmLibraries replaces publishNpmCliShared + publishNpmSdk
- sdk.ts: publishNpmCli now discovers CLI packages from filesystem
- update_version.ts: discovers SDK package.json files via glob
- update_version.ts: discovers internal crates from Cargo.toml path deps
This prevents packages like persist-*, acp-http-client from being
silently skipped during releases.
---
scripts/release/main.ts | 19 +-
scripts/release/sdk.ts | 364 +++++++++++++++---------------
scripts/release/update_version.ts | 125 +++++-----
3 files changed, 240 insertions(+), 268 deletions(-)
diff --git a/scripts/release/main.ts b/scripts/release/main.ts
index 1a8c3a3..426b322 100755
--- a/scripts/release/main.ts
+++ b/scripts/release/main.ts
@@ -13,7 +13,7 @@ import {
createGitHubRelease,
validateGit,
} from "./git";
-import { publishCrates, publishNpmCli, publishNpmCliShared, publishNpmSdk } from "./sdk";
+import { publishCrates, publishNpmCli, publishNpmLibraries } from "./sdk";
import { updateVersion } from "./update_version";
import { assert, assertEquals, fetchGitRef, versionOrCommitToRef } from "./utils";
@@ -282,8 +282,7 @@ const STEPS = [
"run-ci-checks",
"build-js-artifacts",
"publish-crates",
- "publish-npm-cli-shared",
- "publish-npm-sdk",
+ "publish-npm-libraries",
"publish-npm-cli",
"tag-docker",
"promote-artifacts",
@@ -324,8 +323,7 @@ const PHASE_MAP: Record = {
"complete-ci": [
"update-version",
"publish-crates",
- "publish-npm-cli-shared",
- "publish-npm-sdk",
+ "publish-npm-libraries",
"publish-npm-cli",
"tag-docker",
"promote-artifacts",
@@ -604,14 +602,9 @@ async function main() {
await publishCrates(releaseOpts);
}
- if (shouldRunStep("publish-npm-cli-shared")) {
- console.log("==> Publishing NPM CLI Shared");
- await publishNpmCliShared(releaseOpts);
- }
-
- if (shouldRunStep("publish-npm-sdk")) {
- console.log("==> Publishing NPM SDK");
- await publishNpmSdk(releaseOpts);
+ if (shouldRunStep("publish-npm-libraries")) {
+ console.log("==> Publishing NPM Libraries");
+ await publishNpmLibraries(releaseOpts);
}
if (shouldRunStep("publish-npm-cli")) {
diff --git a/scripts/release/sdk.ts b/scripts/release/sdk.ts
index 66caa51..aa6ef2f 100644
--- a/scripts/release/sdk.ts
+++ b/scripts/release/sdk.ts
@@ -1,38 +1,13 @@
import { $ } from "execa";
import * as fs from "node:fs/promises";
-import { join } from "node:path";
+import { dirname, join, relative } from "node:path";
+import { glob } from "glob";
import type { ReleaseOpts } from "./main";
import { downloadFromReleases, PREFIX } from "./utils";
-// Crates to publish in dependency order
-const CRATES = [
- "error",
- "agent-credentials",
- "agent-management",
- "opencode-server-manager",
- "opencode-adapter",
- "acp-http-adapter",
- "sandbox-agent",
- "gigacode",
-] as const;
-
-// NPM CLI packages
-const CLI_PACKAGES = [
- "@sandbox-agent/cli",
- "@sandbox-agent/cli-linux-x64",
- "@sandbox-agent/cli-linux-arm64",
- "@sandbox-agent/cli-win32-x64",
- "@sandbox-agent/cli-darwin-x64",
- "@sandbox-agent/cli-darwin-arm64",
- "@sandbox-agent/gigacode",
- "@sandbox-agent/gigacode-linux-x64",
- "@sandbox-agent/gigacode-linux-arm64",
- "@sandbox-agent/gigacode-win32-x64",
- "@sandbox-agent/gigacode-darwin-x64",
- "@sandbox-agent/gigacode-darwin-arm64",
-] as const;
-
-// Mapping from npm package name to Rust target and binary extension
+// ─── Platform binary mapping (npm package → Rust target) ────────────────────
+// Maps CLI platform packages to their Rust build targets.
+// Keep in sync when adding new target platforms.
const CLI_PLATFORM_MAP: Record<
string,
{ target: string; binaryExt: string; binaryName: string }
@@ -89,6 +64,8 @@ const CLI_PLATFORM_MAP: Record<
},
};
+// ─── Shared helpers ─────────────────────────────────────────────────────────
+
async function npmVersionExists(
packageName: string,
version: string,
@@ -131,7 +108,6 @@ async function crateVersionExists(
stdout: "pipe",
stderr: "pipe",
})`cargo search ${crateName} --limit 1`;
- // cargo search output format: "cratename = \"version\" # description"
const output = result.stdout;
const match = output.match(new RegExp(`^${crateName}\\s*=\\s*"([^"]+)"`));
if (match && match[1] === version) {
@@ -139,62 +115,148 @@ async function crateVersionExists(
}
return false;
} catch (error: any) {
- // If cargo search fails, assume crate doesn't exist
return false;
}
}
+// ─── Package discovery ──────────────────────────────────────────────────────
+
+interface NpmPackageInfo {
+ name: string;
+ dir: string;
+ hasBuildScript: boolean;
+ localDeps: string[];
+}
+
+/**
+ * Discover non-private npm packages matching the given glob patterns.
+ */
+async function discoverNpmPackages(root: string, patterns: string[]): Promise {
+ const packages: NpmPackageInfo[] = [];
+
+ for (const pattern of patterns) {
+ const matches = await glob(pattern, { cwd: root });
+ for (const match of matches) {
+ const fullPath = join(root, match);
+ const pkg = JSON.parse(await fs.readFile(fullPath, "utf-8"));
+ if (pkg.private) continue;
+
+ const allDeps = { ...pkg.dependencies, ...pkg.peerDependencies };
+ const localDeps = Object.entries(allDeps || {})
+ .filter(([_, v]) => String(v).startsWith("workspace:"))
+ .map(([k]) => k);
+
+ packages.push({
+ name: pkg.name,
+ dir: dirname(fullPath),
+ hasBuildScript: !!pkg.scripts?.build,
+ localDeps,
+ });
+ }
+ }
+
+ return packages;
+}
+
+/**
+ * Topologically sort packages so dependencies are published before dependents.
+ */
+function topoSort(packages: NpmPackageInfo[]): NpmPackageInfo[] {
+ const byName = new Map(packages.map(p => [p.name, p]));
+ const visited = new Set();
+ const result: NpmPackageInfo[] = [];
+
+ function visit(pkg: NpmPackageInfo) {
+ if (visited.has(pkg.name)) return;
+ visited.add(pkg.name);
+ for (const dep of pkg.localDeps) {
+ const d = byName.get(dep);
+ if (d) visit(d);
+ }
+ result.push(pkg);
+ }
+
+ for (const pkg of packages) visit(pkg);
+ return result;
+}
+
+interface CrateInfo {
+ name: string;
+ dir: string;
+}
+
+/**
+ * Discover workspace crates via `cargo metadata` and return them in dependency order.
+ */
+async function discoverCrates(root: string): Promise {
+ const result = await $({ cwd: root, stdout: "pipe" })`cargo metadata --no-deps --format-version 1`;
+ const metadata = JSON.parse(result.stdout);
+
+ const memberIds = new Set(metadata.workspace_members);
+ const workspacePackages = metadata.packages.filter((p: any) => memberIds.has(p.id));
+
+ // Build name→package map for topo sort
+ const byName = new Map(workspacePackages.map((p: any) => [p.name, p]));
+
+ const visited = new Set();
+ const sorted: CrateInfo[] = [];
+
+ function visit(pkg: any) {
+ if (visited.has(pkg.name)) return;
+ visited.add(pkg.name);
+ for (const dep of pkg.dependencies) {
+ const internal = byName.get(dep.name);
+ if (internal) visit(internal);
+ }
+ sorted.push({
+ name: pkg.name,
+ dir: dirname(pkg.manifest_path),
+ });
+ }
+
+ for (const pkg of workspacePackages) visit(pkg);
+ return sorted;
+}
+
+// ─── Crate publishing ───────────────────────────────────────────────────────
+
export async function publishCrates(opts: ReleaseOpts) {
- console.log("==> Publishing crates to crates.io");
+ console.log("==> Discovering workspace crates");
+ const crates = await discoverCrates(opts.root);
- for (const crate of CRATES) {
- const cratePath = crate === "gigacode"
- ? join(opts.root, "gigacode")
- : join(opts.root, "server/packages", crate);
+ console.log(`Found ${crates.length} crates to publish:`);
+ for (const c of crates) console.log(` - ${c.name}`);
- // Read Cargo.toml to get the actual crate name
- const cargoTomlPath = join(cratePath, "Cargo.toml");
- const cargoToml = await fs.readFile(cargoTomlPath, "utf-8");
- const nameMatch = cargoToml.match(/^name\s*=\s*"([^"]+)"/m);
- const crateName = nameMatch ? nameMatch[1] : `sandbox-agent-${crate}`;
-
- // Check if version already exists
- const versionExists = await crateVersionExists(crateName, opts.version);
+ for (const crate of crates) {
+ const versionExists = await crateVersionExists(crate.name, opts.version);
if (versionExists) {
console.log(
- `Version ${opts.version} of ${crateName} already exists on crates.io. Skipping...`,
+ `Version ${opts.version} of ${crate.name} already exists on crates.io. Skipping...`,
);
continue;
}
- // Publish
- // Use --no-verify to skip the verification step because:
- // 1. Code was already built/checked in the setup phase
- // 2. Verification downloads published dependencies which may not have the latest
- // changes yet (crates.io indexing takes time)
- console.log(`==> Publishing to crates.io: ${crateName}@${opts.version}`);
+ console.log(`==> Publishing to crates.io: ${crate.name}@${opts.version}`);
try {
await $({
stdout: "pipe",
stderr: "pipe",
- cwd: cratePath,
+ cwd: crate.dir,
})`cargo publish --allow-dirty --no-verify`;
- console.log(`✅ Published ${crateName}@${opts.version}`);
+ console.log(`✅ Published ${crate.name}@${opts.version}`);
} catch (err: any) {
- // Check if error is because crate already exists (from a previous partial run)
if (err.stderr?.includes("already exists")) {
console.log(
- `Version ${opts.version} of ${crateName} already exists on crates.io. Skipping...`,
+ `Version ${opts.version} of ${crate.name} already exists on crates.io. Skipping...`,
);
continue;
}
- console.error(`❌ Failed to publish ${crateName}`);
+ console.error(`❌ Failed to publish ${crate.name}`);
console.error(err.stderr || err.message);
throw err;
}
- // Wait a bit for crates.io to index the new version (needed for dependency resolution)
console.log("Waiting for crates.io to index...");
await new Promise((resolve) => setTimeout(resolve, 30000));
}
@@ -202,108 +264,68 @@ export async function publishCrates(opts: ReleaseOpts) {
console.log("✅ All crates published");
}
-export async function publishNpmCliShared(opts: ReleaseOpts) {
- const cliSharedPath = join(opts.root, "sdks/cli-shared");
- const packageJsonPath = join(cliSharedPath, "package.json");
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
- const name = packageJson.name;
+// ─── NPM library publishing ────────────────────────────────────────────────
- // Check if version already exists
- const versionExists = await npmVersionExists(name, opts.version);
- if (versionExists) {
- console.log(
- `Version ${opts.version} of ${name} already exists. Skipping...`,
- );
- return;
- }
+/**
+ * Discover and publish all non-private library packages under sdks/.
+ * Excludes CLI/gigacode wrapper and platform packages (handled by publishNpmCli).
+ * Publishes in dependency order via topological sort.
+ */
+export async function publishNpmLibraries(opts: ReleaseOpts) {
+ console.log("==> Discovering library packages");
+ const all = await discoverNpmPackages(opts.root, ["sdks/*/package.json"]);
- // Build cli-shared
- console.log(`==> Building @sandbox-agent/cli-shared`);
- await $({
- stdio: "inherit",
- cwd: opts.root,
- })`pnpm --filter @sandbox-agent/cli-shared build`;
+ // Exclude CLI and gigacode directories (handled by publishNpmCli)
+ const libraries = all.filter(p => {
+ const rel = relative(opts.root, p.dir);
+ return !rel.startsWith("sdks/cli") && !rel.startsWith("sdks/gigacode");
+ });
- // Publish
- console.log(`==> Publishing to NPM: ${name}@${opts.version}`);
+ const sorted = topoSort(libraries);
+
+ console.log(`Found ${sorted.length} library packages to publish:`);
+ for (const pkg of sorted) console.log(` - ${pkg.name}`);
- // Add --tag flag for release candidates
const isReleaseCandidate = opts.version.includes("-rc.");
const tag = isReleaseCandidate ? "rc" : (opts.latest ? "latest" : opts.minorVersionChannel);
- await $({
- stdio: "inherit",
- cwd: cliSharedPath,
- })`pnpm publish --access public --tag ${tag} --no-git-checks`;
-
- console.log(`✅ Published ${name}@${opts.version}`);
-}
-
-export async function publishNpmSdk(opts: ReleaseOpts) {
- const isReleaseCandidate = opts.version.includes("-rc.");
- const tag = isReleaseCandidate ? "rc" : (opts.latest ? "latest" : opts.minorVersionChannel);
-
- // Publish acp-http-client (dependency of the SDK)
- {
- const acpHttpClientPath = join(opts.root, "sdks/acp-http-client");
- const packageJsonPath = join(acpHttpClientPath, "package.json");
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
- const name = packageJson.name;
-
- const versionExists = await npmVersionExists(name, opts.version);
+ for (const pkg of sorted) {
+ const versionExists = await npmVersionExists(pkg.name, opts.version);
if (versionExists) {
- console.log(
- `Version ${opts.version} of ${name} already exists. Skipping...`,
- );
- } else {
- console.log(`==> Building acp-http-client`);
- await $({
- stdio: "inherit",
- cwd: opts.root,
- })`pnpm --filter acp-http-client build`;
-
- console.log(`==> Publishing to NPM: ${name}@${opts.version}`);
- await $({
- stdio: "inherit",
- cwd: acpHttpClientPath,
- })`pnpm publish --access public --tag ${tag} --no-git-checks`;
-
- console.log(`✅ Published ${name}@${opts.version}`);
+ console.log(`Version ${opts.version} of ${pkg.name} already exists. Skipping...`);
+ continue;
}
+
+ if (pkg.hasBuildScript) {
+ console.log(`==> Building ${pkg.name}`);
+ await $({ stdio: "inherit", cwd: opts.root })`pnpm --filter ${pkg.name} build`;
+ }
+
+ console.log(`==> Publishing to NPM: ${pkg.name}@${opts.version}`);
+ await $({ stdio: "inherit", cwd: pkg.dir })`pnpm publish --access public --tag ${tag} --no-git-checks`;
+ console.log(`✅ Published ${pkg.name}@${opts.version}`);
}
- // Publish SDK
- const sdkPath = join(opts.root, "sdks/typescript");
- const packageJsonPath = join(sdkPath, "package.json");
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
- const name = packageJson.name;
-
- const versionExists = await npmVersionExists(name, opts.version);
- if (versionExists) {
- console.log(
- `Version ${opts.version} of ${name} already exists. Skipping...`,
- );
- return;
- }
-
- // Build the SDK (cli-shared should already be built by publishNpmCliShared)
- console.log(`==> Building TypeScript SDK`);
- await $({
- stdio: "inherit",
- cwd: opts.root,
- })`pnpm --filter sandbox-agent build`;
-
- console.log(`==> Publishing to NPM: ${name}@${opts.version}`);
- await $({
- stdio: "inherit",
- cwd: sdkPath,
- })`pnpm publish --access public --tag ${tag} --no-git-checks`;
-
- console.log(`✅ Published ${name}@${opts.version}`);
+ console.log("✅ All library packages published");
}
+// ─── NPM CLI publishing ────────────────────────────────────────────────────
+
+/**
+ * Discover and publish CLI wrapper and platform packages.
+ * Platform packages get their binaries downloaded from R2 before publishing.
+ */
export async function publishNpmCli(opts: ReleaseOpts) {
- console.log("==> Publishing CLI packages to NPM");
+ console.log("==> Discovering CLI packages");
+ const packages = await discoverNpmPackages(opts.root, [
+ "sdks/cli/package.json",
+ "sdks/cli/platforms/*/package.json",
+ "sdks/gigacode/package.json",
+ "sdks/gigacode/platforms/*/package.json",
+ ]);
+
+ console.log(`Found ${packages.length} CLI packages to publish:`);
+ for (const pkg of packages) console.log(` - ${pkg.name}`);
// Determine which commit to use for downloading binaries
let sourceCommit = opts.commit;
@@ -316,65 +338,41 @@ export async function publishNpmCli(opts: ReleaseOpts) {
console.log(`Using binaries from commit: ${sourceCommit}`);
}
- for (const packageName of CLI_PACKAGES) {
- // Check if version already exists
- const versionExists = await npmVersionExists(packageName, opts.version);
+ for (const pkg of packages) {
+ const versionExists = await npmVersionExists(pkg.name, opts.version);
if (versionExists) {
console.log(
- `Version ${opts.version} of ${packageName} already exists. Skipping...`,
+ `Version ${opts.version} of ${pkg.name} already exists. Skipping...`,
);
continue;
}
- // Determine package path
- let packagePath: string;
- if (packageName === "@sandbox-agent/cli") {
- packagePath = join(opts.root, "sdks/cli");
- } else if (packageName === "@sandbox-agent/gigacode") {
- packagePath = join(opts.root, "sdks/gigacode");
- } else if (packageName.startsWith("@sandbox-agent/cli-")) {
- // Platform-specific packages: @sandbox-agent/cli-linux-x64 -> sdks/cli/platforms/linux-x64
- const platform = packageName.replace("@sandbox-agent/cli-", "");
- packagePath = join(opts.root, "sdks/cli/platforms", platform);
- } else if (packageName.startsWith("@sandbox-agent/gigacode-")) {
- // Platform-specific packages: @sandbox-agent/gigacode-linux-x64 -> sdks/gigacode/platforms/linux-x64
- const platform = packageName.replace("@sandbox-agent/gigacode-", "");
- packagePath = join(opts.root, "sdks/gigacode/platforms", platform);
- } else {
- throw new Error(`Unknown CLI package: ${packageName}`);
- }
-
- // Download binary from R2 for platform-specific packages
- const platformInfo = CLI_PLATFORM_MAP[packageName];
+ // Download binary for platform-specific packages
+ const platformInfo = CLI_PLATFORM_MAP[pkg.name];
if (platformInfo) {
- const binDir = join(packagePath, "bin");
+ const binDir = join(pkg.dir, "bin");
const binaryName = `${platformInfo.binaryName}${platformInfo.binaryExt}`;
const localBinaryPath = join(binDir, binaryName);
const remoteBinaryPath = `${PREFIX}/${sourceCommit}/binaries/${platformInfo.binaryName}-${platformInfo.target}${platformInfo.binaryExt}`;
- console.log(`==> Downloading binary for ${packageName}`);
+ console.log(`==> Downloading binary for ${pkg.name}`);
console.log(` From: ${remoteBinaryPath}`);
console.log(` To: ${localBinaryPath}`);
- // Create bin directory
await fs.mkdir(binDir, { recursive: true });
-
- // Download binary
await downloadFromReleases(remoteBinaryPath, localBinaryPath);
- // Make binary executable (not needed on Windows)
if (!platformInfo.binaryExt) {
await fs.chmod(localBinaryPath, 0o755);
}
}
// Publish
- console.log(`==> Publishing to NPM: ${packageName}@${opts.version}`);
+ console.log(`==> Publishing to NPM: ${pkg.name}@${opts.version}`);
- // Add --tag flag for release candidates
const isReleaseCandidate = opts.version.includes("-rc.");
const tag = getCliPackageNpmTag({
- packageName,
+ packageName: pkg.name,
isReleaseCandidate,
latest: opts.latest,
minorVersionChannel: opts.minorVersionChannel,
@@ -383,12 +381,11 @@ export async function publishNpmCli(opts: ReleaseOpts) {
try {
await $({
stdio: "inherit",
- cwd: packagePath,
+ cwd: pkg.dir,
})`pnpm publish --access public --tag ${tag} --no-git-checks`;
- console.log(`✅ Published ${packageName}@${opts.version}`);
-
+ console.log(`✅ Published ${pkg.name}@${opts.version}`);
} catch (err) {
- console.error(`❌ Failed to publish ${packageName}`);
+ console.error(`❌ Failed to publish ${pkg.name}`);
throw err;
}
}
@@ -412,4 +409,3 @@ function getCliPackageNpmTag(opts: {
return opts.minorVersionChannel;
}
-
diff --git a/scripts/release/update_version.ts b/scripts/release/update_version.ts
index 67df56d..a22e0f9 100644
--- a/scripts/release/update_version.ts
+++ b/scripts/release/update_version.ts
@@ -1,4 +1,5 @@
import * as fs from "node:fs/promises";
+import { join } from "node:path";
import { $ } from "execa";
import { glob } from "glob";
import type { ReleaseOpts } from "./main";
@@ -10,70 +11,36 @@ function assert(condition: any, message?: string): asserts condition {
}
export async function updateVersion(opts: ReleaseOpts) {
- // Define substitutions
- const findReplace = [
- {
- path: "Cargo.toml",
- find: /\[workspace\.package\]\nversion = ".*"/,
- replace: `[workspace.package]\nversion = "${opts.version}"`,
- },
- {
- path: "sdks/cli-shared/package.json",
- find: /"version": ".*"/,
- replace: `"version": "${opts.version}"`,
- },
- {
- path: "sdks/acp-http-client/package.json",
- find: /"version": ".*"/,
- replace: `"version": "${opts.version}"`,
- },
- {
- path: "sdks/typescript/package.json",
- find: /"version": ".*"/,
- replace: `"version": "${opts.version}"`,
- },
- {
- path: "sdks/cli/package.json",
- find: /"version": ".*"/,
- replace: `"version": "${opts.version}"`,
- },
- {
- path: "sdks/gigacode/package.json",
- find: /"version": ".*"/,
- replace: `"version": "${opts.version}"`,
- },
- {
- path: "sdks/cli/platforms/*/package.json",
- find: /"version": ".*"/,
- replace: `"version": "${opts.version}"`,
- },
- {
- path: "sdks/gigacode/platforms/*/package.json",
- find: /"version": ".*"/,
- replace: `"version": "${opts.version}"`,
- },
- ];
-
- // Update internal crate versions in workspace dependencies
- // These need to match the new version so cargo publish can resolve them correctly
- const internalCrates = [
- "sandbox-agent",
- "sandbox-agent-error",
- "sandbox-agent-agent-management",
- "sandbox-agent-agent-credentials",
- "sandbox-agent-opencode-adapter",
- "sandbox-agent-opencode-server-manager",
- "acp-http-adapter",
- ];
-
- const cargoTomlPath = `${opts.root}/Cargo.toml`;
+ // 1. Update workspace version and internal crate versions in root Cargo.toml
+ const cargoTomlPath = join(opts.root, "Cargo.toml");
let cargoContent = await fs.readFile(cargoTomlPath, "utf-8");
+ // Update [workspace.package] version
+ assert(
+ /\[workspace\.package\]\nversion = ".*"/.test(cargoContent),
+ "Could not find workspace.package version in Cargo.toml",
+ );
+ cargoContent = cargoContent.replace(
+ /\[workspace\.package\]\nversion = ".*"/,
+ `[workspace.package]\nversion = "${opts.version}"`,
+ );
+
+ // Discover internal crates from [workspace.dependencies] by matching
+ // lines with both `version = "..."` and `path = "..."` (internal path deps)
+ const internalCratePattern = /^(\S+)\s*=\s*\{[^}]*version\s*=\s*"[^"]+"\s*,[^}]*path\s*=/gm;
+ let match;
+ const internalCrates: string[] = [];
+ while ((match = internalCratePattern.exec(cargoContent)) !== null) {
+ internalCrates.push(match[1]);
+ }
+
+ console.log(`Discovered ${internalCrates.length} internal crates to version-bump:`);
+ for (const crate of internalCrates) console.log(` - ${crate}`);
+
for (const crate of internalCrates) {
- // Match: crate-name = { version = "x.y.z", path = "..." }
const pattern = new RegExp(
`(${crate.replace(/-/g, "-")} = \\{ version = ")[^"]+(",)`,
- "g"
+ "g",
);
cargoContent = cargoContent.replace(pattern, `$1${opts.version}$2`);
}
@@ -81,18 +48,34 @@ export async function updateVersion(opts: ReleaseOpts) {
await fs.writeFile(cargoTomlPath, cargoContent);
await $({ cwd: opts.root })`git add Cargo.toml`;
- // Substitute all files
- for (const { path: globPath, find, replace } of findReplace) {
- const paths = await glob(globPath, { cwd: opts.root });
- assert(paths.length > 0, `no paths matched: ${globPath}`);
- for (const path of paths) {
- const fullPath = `${opts.root}/${path}`;
- const file = await fs.readFile(fullPath, "utf-8");
- assert(find.test(file), `file does not match ${find}: ${fullPath}`);
- const newFile = file.replace(find, replace);
- await fs.writeFile(fullPath, newFile);
+ // 2. Discover and update all non-private SDK package.json versions
+ const packageJsonPaths = await glob("sdks/**/package.json", {
+ cwd: opts.root,
+ ignore: ["**/node_modules/**"],
+ });
- await $({ cwd: opts.root })`git add ${path}`;
- }
+ // Filter to non-private packages only
+ const toUpdate: string[] = [];
+ for (const relPath of packageJsonPaths) {
+ const fullPath = join(opts.root, relPath);
+ const content = await fs.readFile(fullPath, "utf-8");
+ const pkg = JSON.parse(content);
+ if (pkg.private) continue;
+ toUpdate.push(relPath);
+ }
+
+ console.log(`Discovered ${toUpdate.length} SDK package.json files to version-bump:`);
+ for (const relPath of toUpdate) console.log(` - ${relPath}`);
+
+ for (const relPath of toUpdate) {
+ const fullPath = join(opts.root, relPath);
+ const content = await fs.readFile(fullPath, "utf-8");
+
+ const versionPattern = /"version": ".*"/;
+ assert(versionPattern.test(content), `No version field in ${relPath}`);
+
+ const updated = content.replace(versionPattern, `"version": "${opts.version}"`);
+ await fs.writeFile(fullPath, updated);
+ await $({ cwd: opts.root })`git add ${relPath}`;
}
}
From cb1f770b4716585bfd732813d68b8a005553c22d Mon Sep 17 00:00:00 2001
From: Nathan Flurry
Date: Wed, 11 Feb 2026 17:57:02 -0800
Subject: [PATCH 3/6] fix: bump missing packages to 0.2.0 and handle 404 in
npmVersionExists
- Bump acp-http-client and persist-* packages from 0.1.0 to 0.2.0
- Fix npmVersionExists to handle 404 for never-published packages
---
scripts/release/sdk.ts | 18 +++++++++---------
sdks/acp-http-client/package.json | 2 +-
sdks/persist-indexeddb/package.json | 2 +-
sdks/persist-postgres/package.json | 2 +-
sdks/persist-rivet/package.json | 2 +-
sdks/persist-sqlite/package.json | 2 +-
6 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/scripts/release/sdk.ts b/scripts/release/sdk.ts
index aa6ef2f..b5f54b0 100644
--- a/scripts/release/sdk.ts
+++ b/scripts/release/sdk.ts
@@ -81,16 +81,16 @@ async function npmVersionExists(
return true;
} catch (error: any) {
if (error.stderr) {
- if (
- !error.stderr.includes(
- `No match found for version ${version}`,
- ) &&
- !error.stderr.includes(
- `'${packageName}@${version}' is not in this registry.`,
- )
- ) {
+ const stderr = error.stderr;
+ // Expected errors when version or package doesn't exist
+ const expected =
+ stderr.includes(`No match found for version ${version}`) ||
+ stderr.includes(`'${packageName}@${version}' is not in this registry.`) ||
+ stderr.includes("404 Not Found") ||
+ stderr.includes("is not in the npm registry");
+ if (!expected) {
throw new Error(
- `unexpected npm view version output: ${error.stderr}`,
+ `unexpected npm view version output: ${stderr}`,
);
}
}
diff --git a/sdks/acp-http-client/package.json b/sdks/acp-http-client/package.json
index 6eb419a..ad1bc4e 100644
--- a/sdks/acp-http-client/package.json
+++ b/sdks/acp-http-client/package.json
@@ -1,6 +1,6 @@
{
"name": "acp-http-client",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Protocol-faithful ACP JSON-RPC over streamable HTTP client.",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-indexeddb/package.json b/sdks/persist-indexeddb/package.json
index dc204ca..04eb853 100644
--- a/sdks/persist-indexeddb/package.json
+++ b/sdks/persist-indexeddb/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-indexeddb",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "IndexedDB persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-postgres/package.json b/sdks/persist-postgres/package.json
index 0ad277d..be8afd6 100644
--- a/sdks/persist-postgres/package.json
+++ b/sdks/persist-postgres/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-postgres",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "PostgreSQL persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-rivet/package.json b/sdks/persist-rivet/package.json
index 44baf47..da170aa 100644
--- a/sdks/persist-rivet/package.json
+++ b/sdks/persist-rivet/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-rivet",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Rivet Actor persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-sqlite/package.json b/sdks/persist-sqlite/package.json
index 46f1e58..a35a4c2 100644
--- a/sdks/persist-sqlite/package.json
+++ b/sdks/persist-sqlite/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-sqlite",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "SQLite persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
From 64d13246280ee682645056585c8c231109c72637 Mon Sep 17 00:00:00 2001
From: Nathan Flurry
Date: Wed, 11 Feb 2026 18:52:32 -0800
Subject: [PATCH 4/6] docs: fix bun trust command
---
README.md | 4 ++--
docs/quickstart.mdx | 4 ++--
docs/sdk-overview.mdx | 2 +-
docs/sdks/typescript.mdx | 2 +-
docs/session-persistence.mdx | 2 +-
5 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index f57d233..b7ac39a 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ npm install sandbox-agent@0.2.x
```bash
bun add sandbox-agent@0.2.x
# Optional: allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()).
-bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
+bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
```
**Setup**
@@ -171,7 +171,7 @@ npm install -g @sandbox-agent/cli@0.2.x
```bash
# Allow Bun to run postinstall scripts for native binaries.
bun add -g @sandbox-agent/cli@0.2.x
-bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
+bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
```
Create a session and send a message:
diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx
index 37eacb9..ad2c2ca 100644
--- a/docs/quickstart.mdx
+++ b/docs/quickstart.mdx
@@ -120,7 +120,7 @@ icon: "rocket"
```bash
bun add -g @sandbox-agent/cli@0.2.x
# Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()).
- bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
+ bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
sandbox-agent server --no-token --host 0.0.0.0 --port 2468
```
@@ -145,7 +145,7 @@ icon: "rocket"
```bash
bun add sandbox-agent@0.2.x
# Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()).
- bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
+ bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
```
```typescript
diff --git a/docs/sdk-overview.mdx b/docs/sdk-overview.mdx
index 8a973a2..215ab82 100644
--- a/docs/sdk-overview.mdx
+++ b/docs/sdk-overview.mdx
@@ -18,7 +18,7 @@ The TypeScript SDK is centered on `sandbox-agent` and its `SandboxAgent` class.
```bash
bun add sandbox-agent@0.2.x
# Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()).
- bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
+ bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
```
diff --git a/docs/sdks/typescript.mdx b/docs/sdks/typescript.mdx
index 9c74214..dccc773 100644
--- a/docs/sdks/typescript.mdx
+++ b/docs/sdks/typescript.mdx
@@ -18,7 +18,7 @@ The TypeScript SDK is centered on `sandbox-agent` and its `SandboxAgentClient`,
```bash
bun add sandbox-agent
# Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()).
- bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
+ bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64
```
diff --git a/docs/session-persistence.mdx b/docs/session-persistence.mdx
index 43bbcef..676c948 100644
--- a/docs/session-persistence.mdx
+++ b/docs/session-persistence.mdx
@@ -38,7 +38,7 @@ const sdk = await SandboxAgent.connect({
Recommended for sandbox orchestration with actor state.
```bash
-npm install @sandbox-agent/persist-rivet@0.1.x
+npm install @sandbox-agent/persist-rivet@0.2.x
```
```ts
From 3c2a9cbbbbbdd3b50ebb1279700b09770e0235ae Mon Sep 17 00:00:00 2001
From: Nathan Flurry
Date: Wed, 11 Feb 2026 20:29:03 -0800
Subject: [PATCH 5/6] feat: add session persistence examples and SQLite driver
---
.gitignore | 1 +
examples/persist-memory/package.json | 18 ++
examples/persist-memory/src/index.ts | 37 ++++
examples/persist-memory/tsconfig.json | 13 ++
examples/persist-postgres/package.json | 21 ++
examples/persist-postgres/src/index.ts | 76 +++++++
examples/persist-postgres/tsconfig.json | 13 ++
examples/persist-sqlite/package.json | 19 ++
examples/persist-sqlite/src/index.ts | 39 ++++
examples/persist-sqlite/tsconfig.json | 13 ++
pnpm-lock.yaml | 273 +++++++++++++++++++++++-
sdks/persist-sqlite/package.json | 2 +
sdks/persist-sqlite/src/index.ts | 9 +-
sdks/persist-sqlite/tsup.config.ts | 1 +
14 files changed, 524 insertions(+), 11 deletions(-)
create mode 100644 examples/persist-memory/package.json
create mode 100644 examples/persist-memory/src/index.ts
create mode 100644 examples/persist-memory/tsconfig.json
create mode 100644 examples/persist-postgres/package.json
create mode 100644 examples/persist-postgres/src/index.ts
create mode 100644 examples/persist-postgres/tsconfig.json
create mode 100644 examples/persist-sqlite/package.json
create mode 100644 examples/persist-sqlite/src/index.ts
create mode 100644 examples/persist-sqlite/tsconfig.json
diff --git a/.gitignore b/.gitignore
index 07d74c2..b540697 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,7 @@ Cargo.lock
# Example temp files
.tmp-upload/
+*.db
# CLI binaries (downloaded during npm publish)
sdks/cli/platforms/*/bin/
diff --git a/examples/persist-memory/package.json b/examples/persist-memory/package.json
new file mode 100644
index 0000000..0514ad9
--- /dev/null
+++ b/examples/persist-memory/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@sandbox-agent/example-persist-memory",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "start": "tsx src/index.ts",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@sandbox-agent/example-shared": "workspace:*",
+ "sandbox-agent": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/node": "latest",
+ "tsx": "latest",
+ "typescript": "latest"
+ }
+}
diff --git a/examples/persist-memory/src/index.ts b/examples/persist-memory/src/index.ts
new file mode 100644
index 0000000..247f724
--- /dev/null
+++ b/examples/persist-memory/src/index.ts
@@ -0,0 +1,37 @@
+import { SandboxAgent, InMemorySessionPersistDriver } from "sandbox-agent";
+import { startDockerSandbox } from "@sandbox-agent/example-shared/docker";
+import { detectAgent } from "@sandbox-agent/example-shared";
+
+const persist = new InMemorySessionPersistDriver();
+
+console.log("Starting sandbox...");
+const sandbox = await startDockerSandbox({
+ port: 3000,
+ setupCommands: [
+ "sandbox-agent install-agent claude",
+ "sandbox-agent install-agent codex",
+ ],
+});
+
+const sdk = await SandboxAgent.connect({ baseUrl: sandbox.baseUrl, persist });
+
+const session = await sdk.createSession({ agent: detectAgent() });
+console.log(`Created session ${session.id}`);
+
+await session.prompt([{ type: "text", text: "Say hello in one sentence." }]);
+console.log("Prompt complete.");
+
+const sessions = await sdk.listSessions();
+console.log(`\nSessions (${sessions.items.length}):`);
+for (const s of sessions.items) {
+ console.log(` ${s.id} agent=${s.agent}`);
+}
+
+const events = await sdk.getEvents({ sessionId: session.id });
+console.log(`\nSession history (${events.items.length} events):`);
+for (const e of events.items) {
+ console.log(` [${e.eventIndex}] ${e.sender}: ${JSON.stringify(e.payload).slice(0, 120)}`);
+}
+
+await sdk.dispose();
+await sandbox.cleanup();
diff --git a/examples/persist-memory/tsconfig.json b/examples/persist-memory/tsconfig.json
new file mode 100644
index 0000000..d1c0065
--- /dev/null
+++ b/examples/persist-memory/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "allowImportingTsExtensions": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "strict": true,
+ "skipLibCheck": true
+ },
+ "include": ["src"]
+}
diff --git a/examples/persist-postgres/package.json b/examples/persist-postgres/package.json
new file mode 100644
index 0000000..8114ffb
--- /dev/null
+++ b/examples/persist-postgres/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "@sandbox-agent/example-persist-postgres",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "start": "tsx src/index.ts",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@sandbox-agent/example-shared": "workspace:*",
+ "@sandbox-agent/persist-postgres": "workspace:*",
+ "pg": "latest",
+ "sandbox-agent": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/node": "latest",
+ "@types/pg": "latest",
+ "tsx": "latest",
+ "typescript": "latest"
+ }
+}
diff --git a/examples/persist-postgres/src/index.ts b/examples/persist-postgres/src/index.ts
new file mode 100644
index 0000000..32eda13
--- /dev/null
+++ b/examples/persist-postgres/src/index.ts
@@ -0,0 +1,76 @@
+import { execFileSync } from "node:child_process";
+import { randomUUID } from "node:crypto";
+import { Client } from "pg";
+import { setTimeout as delay } from "node:timers/promises";
+import { SandboxAgent } from "sandbox-agent";
+import { PostgresSessionPersistDriver } from "@sandbox-agent/persist-postgres";
+import { startDockerSandbox } from "@sandbox-agent/example-shared/docker";
+import { detectAgent } from "@sandbox-agent/example-shared";
+
+// --- Postgres setup (Docker or DATABASE_URL) ---
+
+let containerId: string | undefined;
+let connectionString: string;
+
+if (process.env.DATABASE_URL) {
+ connectionString = process.env.DATABASE_URL;
+} else {
+ const name = `persist-example-${randomUUID().slice(0, 8)}`;
+ containerId = execFileSync("docker", [
+ "run", "-d", "--rm", "--name", name,
+ "-e", "POSTGRES_USER=postgres", "-e", "POSTGRES_PASSWORD=postgres", "-e", "POSTGRES_DB=sandbox",
+ "-p", "127.0.0.1::5432", "postgres:16-alpine",
+ ], { encoding: "utf8" }).trim();
+ const port = execFileSync("docker", ["port", containerId, "5432/tcp"], { encoding: "utf8" })
+ .trim().split("\n")[0]?.match(/:(\d+)$/)?.[1];
+ connectionString = `postgres://postgres:postgres@127.0.0.1:${port}/sandbox`;
+ console.log(`Postgres on port ${port}`);
+
+ const deadline = Date.now() + 30_000;
+ while (Date.now() < deadline) {
+ const c = new Client({ connectionString });
+ try { await c.connect(); await c.query("SELECT 1"); await c.end(); break; }
+ catch { try { await c.end(); } catch {} await delay(250); }
+ }
+}
+
+try {
+ const persist = new PostgresSessionPersistDriver({ connectionString });
+
+ console.log("Starting sandbox...");
+ const sandbox = await startDockerSandbox({
+ port: 3000,
+ setupCommands: [
+ "sandbox-agent install-agent claude",
+ "sandbox-agent install-agent codex",
+ ],
+ });
+
+ const sdk = await SandboxAgent.connect({ baseUrl: sandbox.baseUrl, persist });
+
+ const session = await sdk.createSession({ agent: detectAgent() });
+ console.log(`Created session ${session.id}`);
+
+ await session.prompt([{ type: "text", text: "Say hello in one sentence." }]);
+ console.log("Prompt complete.");
+
+ const sessions = await sdk.listSessions();
+ console.log(`\nSessions (${sessions.items.length}):`);
+ for (const s of sessions.items) {
+ console.log(` ${s.id} agent=${s.agent}`);
+ }
+
+ const events = await sdk.getEvents({ sessionId: session.id });
+ console.log(`\nSession history (${events.items.length} events):`);
+ for (const e of events.items) {
+ console.log(` [${e.eventIndex}] ${e.sender}: ${JSON.stringify(e.payload).slice(0, 120)}`);
+ }
+
+ await persist.close();
+ await sdk.dispose();
+ await sandbox.cleanup();
+} finally {
+ if (containerId) {
+ try { execFileSync("docker", ["rm", "-f", containerId], { stdio: "ignore" }); } catch {}
+ }
+}
diff --git a/examples/persist-postgres/tsconfig.json b/examples/persist-postgres/tsconfig.json
new file mode 100644
index 0000000..d1c0065
--- /dev/null
+++ b/examples/persist-postgres/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "allowImportingTsExtensions": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "strict": true,
+ "skipLibCheck": true
+ },
+ "include": ["src"]
+}
diff --git a/examples/persist-sqlite/package.json b/examples/persist-sqlite/package.json
new file mode 100644
index 0000000..8b7b822
--- /dev/null
+++ b/examples/persist-sqlite/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@sandbox-agent/example-persist-sqlite",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "start": "tsx src/index.ts",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@sandbox-agent/example-shared": "workspace:*",
+ "@sandbox-agent/persist-sqlite": "workspace:*",
+ "sandbox-agent": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/node": "latest",
+ "tsx": "latest",
+ "typescript": "latest"
+ }
+}
diff --git a/examples/persist-sqlite/src/index.ts b/examples/persist-sqlite/src/index.ts
new file mode 100644
index 0000000..9b51024
--- /dev/null
+++ b/examples/persist-sqlite/src/index.ts
@@ -0,0 +1,39 @@
+import { SandboxAgent } from "sandbox-agent";
+import { SQLiteSessionPersistDriver } from "@sandbox-agent/persist-sqlite";
+import { startDockerSandbox } from "@sandbox-agent/example-shared/docker";
+import { detectAgent } from "@sandbox-agent/example-shared";
+
+const persist = new SQLiteSessionPersistDriver({ filename: "./sessions.db" });
+
+console.log("Starting sandbox...");
+const sandbox = await startDockerSandbox({
+ port: 3000,
+ setupCommands: [
+ "sandbox-agent install-agent claude",
+ "sandbox-agent install-agent codex",
+ ],
+});
+
+const sdk = await SandboxAgent.connect({ baseUrl: sandbox.baseUrl, persist });
+
+const session = await sdk.createSession({ agent: detectAgent() });
+console.log(`Created session ${session.id}`);
+
+await session.prompt([{ type: "text", text: "Say hello in one sentence." }]);
+console.log("Prompt complete.");
+
+const sessions = await sdk.listSessions();
+console.log(`\nSessions (${sessions.items.length}):`);
+for (const s of sessions.items) {
+ console.log(` ${s.id} agent=${s.agent}`);
+}
+
+const events = await sdk.getEvents({ sessionId: session.id });
+console.log(`\nSession history (${events.items.length} events):`);
+for (const e of events.items) {
+ console.log(` [${e.eventIndex}] ${e.sender}: ${JSON.stringify(e.payload).slice(0, 120)}`);
+}
+
+persist.close();
+await sdk.dispose();
+await sandbox.cleanup();
diff --git a/examples/persist-sqlite/tsconfig.json b/examples/persist-sqlite/tsconfig.json
new file mode 100644
index 0000000..d1c0065
--- /dev/null
+++ b/examples/persist-sqlite/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "allowImportingTsExtensions": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "strict": true,
+ "skipLibCheck": true
+ },
+ "include": ["src"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5e485db..45e190a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,7 +23,7 @@ importers:
dependencies:
'@cloudflare/sandbox':
specifier: latest
- version: 0.7.1
+ version: 0.7.2
react:
specifier: ^19.1.0
version: 19.2.4
@@ -240,6 +240,75 @@ importers:
specifier: latest
version: 5.9.3
+ examples/persist-memory:
+ dependencies:
+ '@sandbox-agent/example-shared':
+ specifier: workspace:*
+ version: link:../shared
+ sandbox-agent:
+ specifier: workspace:*
+ version: link:../../sdks/typescript
+ devDependencies:
+ '@types/node':
+ specifier: latest
+ version: 25.2.3
+ tsx:
+ specifier: latest
+ version: 4.21.0
+ typescript:
+ specifier: latest
+ version: 5.9.3
+
+ examples/persist-postgres:
+ dependencies:
+ '@sandbox-agent/example-shared':
+ specifier: workspace:*
+ version: link:../shared
+ '@sandbox-agent/persist-postgres':
+ specifier: workspace:*
+ version: link:../../sdks/persist-postgres
+ pg:
+ specifier: latest
+ version: 8.18.0
+ sandbox-agent:
+ specifier: workspace:*
+ version: link:../../sdks/typescript
+ devDependencies:
+ '@types/node':
+ specifier: latest
+ version: 25.2.3
+ '@types/pg':
+ specifier: latest
+ version: 8.16.0
+ tsx:
+ specifier: latest
+ version: 4.21.0
+ typescript:
+ specifier: latest
+ version: 5.9.3
+
+ examples/persist-sqlite:
+ dependencies:
+ '@sandbox-agent/example-shared':
+ specifier: workspace:*
+ version: link:../shared
+ '@sandbox-agent/persist-sqlite':
+ specifier: workspace:*
+ version: link:../../sdks/persist-sqlite
+ sandbox-agent:
+ specifier: workspace:*
+ version: link:../../sdks/typescript
+ devDependencies:
+ '@types/node':
+ specifier: latest
+ version: 25.2.3
+ tsx:
+ specifier: latest
+ version: 4.21.0
+ typescript:
+ specifier: latest
+ version: 5.9.3
+
examples/shared:
dependencies:
dockerode:
@@ -370,6 +439,9 @@ importers:
'@astrojs/react':
specifier: ^4.2.0
version: 4.4.2(@types/node@25.2.3)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2)
+ '@astrojs/sitemap':
+ specifier: ^3.2.0
+ version: 3.7.0
'@astrojs/tailwind':
specifier: ^6.0.0
version: 6.0.2(astro@5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))
@@ -648,10 +720,16 @@ importers:
sdks/persist-sqlite:
dependencies:
+ better-sqlite3:
+ specifier: ^11.0.0
+ version: 11.10.0
sandbox-agent:
specifier: workspace:*
version: link:../typescript
devDependencies:
+ '@types/better-sqlite3':
+ specifier: ^7.0.0
+ version: 7.6.13
'@types/node':
specifier: ^22.0.0
version: 22.19.7
@@ -732,6 +810,9 @@ packages:
react: ^17.0.2 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0
+ '@astrojs/sitemap@3.7.0':
+ resolution: {integrity: sha512-+qxjUrz6Jcgh+D5VE1gKUJTA3pSthuPHe6Ao5JCxok794Lewx8hBFaWHtOnN0ntb2lfOf7gvOi9TefUswQ/ZVA==}
+
'@astrojs/tailwind@6.0.2':
resolution: {integrity: sha512-j3mhLNeugZq6A8dMNXVarUa8K6X9AW+QHU9u3lKNrPLMHhOQ0S7VeWhHwEeJFpEK1BTKEUY1U78VQv2gN6hNGg==}
peerDependencies:
@@ -1061,8 +1142,8 @@ packages:
resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==}
engines: {node: '>=18.0.0'}
- '@cloudflare/sandbox@0.7.1':
- resolution: {integrity: sha512-suDuYa+/rLmvaTqRwETsRO7gyN7XGAfxy+Y/UmCCQJzPL5FHBojV5NUw7xwe6mIznSscz2uk1iPHrF7sCecCBA==}
+ '@cloudflare/sandbox@0.7.2':
+ resolution: {integrity: sha512-q0dn6RUBLZ8CD4g99sVJbxoQK/BjJ3luw0T03mvTrUbRicjyzdFWP7dlEYzhvqpyd6TWpTsYr+TSHf/95bVuWw==}
peerDependencies:
'@openai/agents': ^0.3.3
'@opencode-ai/sdk': ^1.0.137
@@ -2651,6 +2732,9 @@ packages:
'@types/babel__traverse@7.28.0':
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+ '@types/better-sqlite3@7.6.13':
+ resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
+
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
@@ -2684,6 +2768,9 @@ packages:
'@types/nlcst@2.0.3':
resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==}
+ '@types/node@17.0.45':
+ resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
+
'@types/node@18.19.130':
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
@@ -2713,6 +2800,9 @@ packages:
'@types/retry@0.12.2':
resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==}
+ '@types/sax@1.2.7':
+ resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
+
'@types/semver@7.7.1':
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
@@ -2906,10 +2996,16 @@ packages:
bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
+ better-sqlite3@11.10.0:
+ resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==}
+
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
+ bindings@1.5.0:
+ resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@@ -3184,10 +3280,18 @@ packages:
decode-named-character-reference@1.3.0:
resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
+ decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+
deep-eql@5.0.2:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
@@ -3388,6 +3492,10 @@ packages:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
+ expand-template@2.0.3:
+ resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
+ engines: {node: '>=6'}
+
expand-tilde@2.0.2:
resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
engines: {node: '>=0.10.0'}
@@ -3442,6 +3550,9 @@ packages:
picomatch:
optional: true
+ file-uri-to-path@1.0.0:
+ resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -3551,6 +3662,9 @@ packages:
get-tsconfig@4.13.0:
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
+ github-from-package@0.0.0:
+ resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+
github-slugger@2.0.0:
resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
@@ -3663,6 +3777,9 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
@@ -4016,6 +4133,10 @@ packages:
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
engines: {node: '>=12'}
+ mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+
miniflare@4.20260210.0:
resolution: {integrity: sha512-HXR6m53IOqEzq52DuGF1x7I1K6lSIqzhbCbQXv/cTmPnPJmNkr7EBtLDm4nfSkOvlDtnwDCLUjWII5fyGJI5Tw==}
engines: {node: '>=18.0.0'}
@@ -4029,6 +4150,9 @@ packages:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -4074,6 +4198,9 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ napi-build-utils@2.0.0:
+ resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
+
negotiator@1.0.0:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
@@ -4085,6 +4212,10 @@ packages:
nlcst-to-string@4.0.0:
resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==}
+ node-abi@3.87.0:
+ resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==}
+ engines: {node: '>=10'}
+
node-fetch-native@1.6.7:
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
@@ -4384,6 +4515,11 @@ packages:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
+ prebuild-install@7.1.3:
+ resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
+ engines: {node: '>=10'}
+ hasBin: true
+
prismjs@1.30.0:
resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
engines: {node: '>=6'}
@@ -4433,6 +4569,10 @@ packages:
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
engines: {node: '>= 0.10'}
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
react-dom@18.3.1:
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
peerDependencies:
@@ -4671,9 +4811,20 @@ packages:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
+ simple-concat@1.0.1:
+ resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+
+ simple-get@4.0.1:
+ resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+ sitemap@8.0.2:
+ resolution: {integrity: sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ==}
+ engines: {node: '>=14.0.0', npm: '>=6.0.0'}
+ hasBin: true
+
smol-toml@1.6.0:
resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==}
engines: {node: '>= 18'}
@@ -4716,6 +4867,9 @@ packages:
stream-browserify@3.0.0:
resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==}
+ stream-replace-string@2.0.0:
+ resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==}
+
streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
@@ -4753,6 +4907,10 @@ packages:
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
engines: {node: '>=12'}
+ strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+
strip-literal@3.1.0:
resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
@@ -4910,6 +5068,9 @@ packages:
engines: {node: '>=18.0.0'}
hasBin: true
+ tunnel-agent@0.6.0:
+ resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+
turbo-darwin-64@2.7.6:
resolution: {integrity: sha512-bYu0qnWju2Ha3EbIkPCk1SMLT3sltKh1P/Jy5FER6BmH++H5z+T5MHh3W1Xoers9rk4N1VdKvog9FO1pxQyjhw==}
cpu: [x64]
@@ -5469,6 +5630,12 @@ snapshots:
- tsx
- yaml
+ '@astrojs/sitemap@3.7.0':
+ dependencies:
+ sitemap: 8.0.2
+ stream-replace-string: 2.0.0
+ zod: 3.25.76
+
'@astrojs/tailwind@6.0.2(astro@5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
astro: 5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
@@ -6177,7 +6344,7 @@ snapshots:
'@cloudflare/kv-asset-handler@0.4.2': {}
- '@cloudflare/sandbox@0.7.1':
+ '@cloudflare/sandbox@0.7.2':
dependencies:
'@cloudflare/containers': 0.0.30
@@ -7597,6 +7764,10 @@ snapshots:
dependencies:
'@babel/types': 7.28.6
+ '@types/better-sqlite3@7.6.13':
+ dependencies:
+ '@types/node': 25.2.3
+
'@types/chai@5.2.3':
dependencies:
'@types/deep-eql': 4.0.2
@@ -7637,6 +7808,8 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/node@17.0.45': {}
+
'@types/node@18.19.130':
dependencies:
undici-types: 5.26.5
@@ -7672,6 +7845,10 @@ snapshots:
'@types/retry@0.12.2': {}
+ '@types/sax@1.2.7':
+ dependencies:
+ '@types/node': 25.2.3
+
'@types/semver@7.7.1': {}
'@types/ssh2@1.15.5':
@@ -7731,6 +7908,14 @@ snapshots:
chai: 5.3.3
tinyrainbow: 2.0.0
+ '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@22.19.7))':
+ dependencies:
+ '@vitest/spy': 3.2.4
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 5.4.21(@types/node@22.19.7)
+
'@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.2.3))':
dependencies:
'@vitest/spy': 3.2.4
@@ -7971,8 +8156,17 @@ snapshots:
dependencies:
tweetnacl: 0.14.5
+ better-sqlite3@11.10.0:
+ dependencies:
+ bindings: 1.5.0
+ prebuild-install: 7.1.3
+
binary-extensions@2.3.0: {}
+ bindings@1.5.0:
+ dependencies:
+ file-uri-to-path: 1.0.0
+
bl@4.1.0:
dependencies:
buffer: 5.7.1
@@ -8245,8 +8439,14 @@ snapshots:
dependencies:
character-entities: 2.0.2
+ decompress-response@6.0.0:
+ dependencies:
+ mimic-response: 3.1.0
+
deep-eql@5.0.2: {}
+ deep-extend@0.6.0: {}
+
defu@6.1.4: {}
delayed-stream@1.0.0: {}
@@ -8538,6 +8738,8 @@ snapshots:
signal-exit: 4.1.0
strip-final-newline: 3.0.0
+ expand-template@2.0.3: {}
+
expand-tilde@2.0.2:
dependencies:
homedir-polyfill: 1.0.3
@@ -8612,6 +8814,8 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
+ file-uri-to-path@1.0.0: {}
+
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -8714,6 +8918,8 @@ snapshots:
dependencies:
resolve-pkg-maps: 1.0.0
+ github-from-package@0.0.0: {}
+
github-slugger@2.0.0: {}
glob-parent@5.1.2:
@@ -8892,6 +9098,8 @@ snapshots:
inherits@2.0.4: {}
+ ini@1.3.8: {}
+
invariant@2.2.4:
dependencies:
loose-envify: 1.4.0
@@ -9372,6 +9580,8 @@ snapshots:
mimic-fn@4.0.0: {}
+ mimic-response@3.1.0: {}
+
miniflare@4.20260210.0:
dependencies:
'@cspotcode/source-map-support': 0.8.1
@@ -9392,6 +9602,8 @@ snapshots:
dependencies:
brace-expansion: 2.0.2
+ minimist@1.2.8: {}
+
minipass@7.1.2: {}
minizlib@3.1.0:
@@ -9432,6 +9644,8 @@ snapshots:
nanoid@3.3.11: {}
+ napi-build-utils@2.0.0: {}
+
negotiator@1.0.0: {}
neotraverse@0.6.18: {}
@@ -9440,6 +9654,10 @@ snapshots:
dependencies:
'@types/nlcst': 2.0.3
+ node-abi@3.87.0:
+ dependencies:
+ semver: 7.7.3
+
node-fetch-native@1.6.7: {}
node-gyp-build-optional-packages@5.1.1:
@@ -9713,6 +9931,21 @@ snapshots:
dependencies:
xtend: 4.0.2
+ prebuild-install@7.1.3:
+ dependencies:
+ detect-libc: 2.1.2
+ expand-template: 2.0.3
+ github-from-package: 0.0.0
+ minimist: 1.2.8
+ mkdirp-classic: 0.5.3
+ napi-build-utils: 2.0.0
+ node-abi: 3.87.0
+ pump: 3.0.3
+ rc: 1.2.8
+ simple-get: 4.0.1
+ tar-fs: 2.1.4
+ tunnel-agent: 0.6.0
+
prismjs@1.30.0: {}
process-warning@5.0.0: {}
@@ -9770,6 +10003,13 @@ snapshots:
iconv-lite: 0.7.2
unpipe: 1.0.0
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+
react-dom@18.3.1(react@18.3.1):
dependencies:
loose-envify: 1.4.0
@@ -10134,8 +10374,23 @@ snapshots:
signal-exit@4.1.0: {}
+ simple-concat@1.0.1: {}
+
+ simple-get@4.0.1:
+ dependencies:
+ decompress-response: 6.0.0
+ once: 1.4.0
+ simple-concat: 1.0.1
+
sisteransi@1.0.5: {}
+ sitemap@8.0.2:
+ dependencies:
+ '@types/node': 17.0.45
+ '@types/sax': 1.2.7
+ arg: 5.0.2
+ sax: 1.4.4
+
smol-toml@1.6.0: {}
sonic-boom@4.2.1:
@@ -10171,6 +10426,8 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
+ stream-replace-string@2.0.0: {}
+
streamsearch@1.1.0: {}
streamx@2.23.0:
@@ -10219,6 +10476,8 @@ snapshots:
strip-final-newline@3.0.0: {}
+ strip-json-comments@2.0.1: {}
+
strip-literal@3.1.0:
dependencies:
js-tokens: 9.0.1
@@ -10421,6 +10680,10 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
+ tunnel-agent@0.6.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
turbo-darwin-64@2.7.6:
optional: true
@@ -10684,7 +10947,7 @@ snapshots:
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.2.3))
+ '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@22.19.7))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
diff --git a/sdks/persist-sqlite/package.json b/sdks/persist-sqlite/package.json
index a35a4c2..512dfe1 100644
--- a/sdks/persist-sqlite/package.json
+++ b/sdks/persist-sqlite/package.json
@@ -17,6 +17,7 @@
}
},
"dependencies": {
+ "better-sqlite3": "^11.0.0",
"sandbox-agent": "workspace:*"
},
"files": [
@@ -29,6 +30,7 @@
"test:watch": "vitest"
},
"devDependencies": {
+ "@types/better-sqlite3": "^7.0.0",
"@types/node": "^22.0.0",
"tsup": "^8.0.0",
"typescript": "^5.7.0",
diff --git a/sdks/persist-sqlite/src/index.ts b/sdks/persist-sqlite/src/index.ts
index 867e83f..73f4f48 100644
--- a/sdks/persist-sqlite/src/index.ts
+++ b/sdks/persist-sqlite/src/index.ts
@@ -1,4 +1,4 @@
-import { DatabaseSync } from "node:sqlite";
+import Database from "better-sqlite3";
import type {
ListEventsRequest,
ListPage,
@@ -12,16 +12,13 @@ const DEFAULT_LIST_LIMIT = 100;
export interface SQLiteSessionPersistDriverOptions {
filename?: string;
- open?: boolean;
}
export class SQLiteSessionPersistDriver implements SessionPersistDriver {
- private readonly db: DatabaseSync;
+ private readonly db: Database.Database;
constructor(options: SQLiteSessionPersistDriverOptions = {}) {
- this.db = new DatabaseSync(options.filename ?? ":memory:", {
- open: options.open ?? true,
- });
+ this.db = new Database(options.filename ?? ":memory:");
this.initialize();
}
diff --git a/sdks/persist-sqlite/tsup.config.ts b/sdks/persist-sqlite/tsup.config.ts
index 70a8b7e..e896e72 100644
--- a/sdks/persist-sqlite/tsup.config.ts
+++ b/sdks/persist-sqlite/tsup.config.ts
@@ -7,4 +7,5 @@ export default defineConfig({
sourcemap: true,
clean: true,
target: "es2022",
+ external: ["better-sqlite3"],
});
From ee9ad250698755495c41d29b1504bde9e48a988b Mon Sep 17 00:00:00 2001
From: Nathan Flurry
Date: Wed, 11 Feb 2026 20:45:33 -0800
Subject: [PATCH 6/6] chore(release): update version to 0.2.1
---
Cargo.toml | 16 ++--
docs/openapi.json | 2 +-
.../src/components/chat/ChatMessages.tsx | 89 +++++++++----------
.../src/components/chat/messageUtils.tsx | 13 ++-
sdks/acp-http-client/package.json | 2 +-
sdks/cli-shared/package.json | 2 +-
sdks/cli/package.json | 2 +-
sdks/cli/platforms/darwin-arm64/package.json | 2 +-
sdks/cli/platforms/darwin-x64/package.json | 2 +-
sdks/cli/platforms/linux-arm64/package.json | 2 +-
sdks/cli/platforms/linux-x64/package.json | 2 +-
sdks/cli/platforms/win32-x64/package.json | 2 +-
sdks/gigacode/package.json | 2 +-
.../platforms/darwin-arm64/package.json | 2 +-
.../platforms/darwin-x64/package.json | 2 +-
.../platforms/linux-arm64/package.json | 2 +-
.../gigacode/platforms/linux-x64/package.json | 2 +-
.../gigacode/platforms/win32-x64/package.json | 2 +-
sdks/persist-indexeddb/package.json | 2 +-
sdks/persist-postgres/package.json | 2 +-
sdks/persist-rivet/package.json | 2 +-
sdks/persist-sqlite/package.json | 2 +-
sdks/typescript/package.json | 2 +-
23 files changed, 76 insertions(+), 82 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 505efb0..ebef66d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ resolver = "2"
members = ["server/packages/*", "gigacode"]
[workspace.package]
-version = "0.2.0"
+version = "0.2.1"
edition = "2021"
authors = [ "Rivet Gaming, LLC " ]
license = "Apache-2.0"
@@ -12,13 +12,13 @@ description = "Universal API for automatic coding agents in sandboxes. Supports
[workspace.dependencies]
# Internal crates
-sandbox-agent = { version = "0.2.0", path = "server/packages/sandbox-agent" }
-sandbox-agent-error = { version = "0.2.0", path = "server/packages/error" }
-sandbox-agent-agent-management = { version = "0.2.0", path = "server/packages/agent-management" }
-sandbox-agent-agent-credentials = { version = "0.2.0", path = "server/packages/agent-credentials" }
-sandbox-agent-opencode-adapter = { version = "0.2.0", path = "server/packages/opencode-adapter" }
-sandbox-agent-opencode-server-manager = { version = "0.2.0", path = "server/packages/opencode-server-manager" }
-acp-http-adapter = { version = "0.2.0", path = "server/packages/acp-http-adapter" }
+sandbox-agent = { version = "0.2.1", path = "server/packages/sandbox-agent" }
+sandbox-agent-error = { version = "0.2.1", path = "server/packages/error" }
+sandbox-agent-agent-management = { version = "0.2.1", path = "server/packages/agent-management" }
+sandbox-agent-agent-credentials = { version = "0.2.1", path = "server/packages/agent-credentials" }
+sandbox-agent-opencode-adapter = { version = "0.2.1", path = "server/packages/opencode-adapter" }
+sandbox-agent-opencode-server-manager = { version = "0.2.1", path = "server/packages/opencode-server-manager" }
+acp-http-adapter = { version = "0.2.1", path = "server/packages/acp-http-adapter" }
# Serialization
serde = { version = "1.0", features = ["derive"] }
diff --git a/docs/openapi.json b/docs/openapi.json
index 01fd073..c6e35f4 100644
--- a/docs/openapi.json
+++ b/docs/openapi.json
@@ -10,7 +10,7 @@
"license": {
"name": "Apache-2.0"
},
- "version": "0.2.0"
+ "version": "0.2.1"
},
"servers": [
{
diff --git a/frontend/packages/inspector/src/components/chat/ChatMessages.tsx b/frontend/packages/inspector/src/components/chat/ChatMessages.tsx
index 75f1ee1..b57f13c 100644
--- a/frontend/packages/inspector/src/components/chat/ChatMessages.tsx
+++ b/frontend/packages/inspector/src/components/chat/ChatMessages.tsx
@@ -1,6 +1,5 @@
import { useState } from "react";
import { getAvatarLabel, getMessageClass } from "./messageUtils";
-import renderContentPart from "./renderContentPart";
import type { TimelineEntry } from "./types";
import { AlertTriangle, Settings, ChevronRight, ChevronDown } from "lucide-react";
@@ -34,17 +33,17 @@ const CollapsibleMessage = ({
const ChatMessages = ({
entries,
sessionError,
- eventError,
messagesEndRef
}: {
entries: TimelineEntry[];
sessionError: string | null;
- eventError: string | null;
messagesEndRef: React.RefObject;
}) => {
return (
{entries.map((entry) => {
+ const messageClass = getMessageClass(entry);
+
if (entry.kind === "meta") {
const isError = entry.meta?.severity === "error";
const title = entry.meta?.title ?? "Status";
@@ -63,7 +62,6 @@ const ChatMessages = ({
);
}
- // Other status messages - collapsible
return (
0;
- const isInProgress = item.status === "in_progress";
- const isFailed = item.status === "failed";
- const messageClass = getMessageClass(item);
- const statusLabel = item.status !== "completed" ? item.status.replace("_", " ") : "";
- const kindLabel = item.kind.replace("_", " ");
- const isTool = messageClass === "tool";
+ if (entry.kind === "reasoning") {
+ return (
+
+
{getAvatarLabel("assistant")}
+
+
+ reasoning - {entry.reasoning?.visibility ?? "public"}
+
+
{entry.reasoning?.text ?? ""}
+
+
+ );
+ }
+
+ if (entry.kind === "tool") {
+ const isComplete = entry.toolStatus === "completed" || entry.toolStatus === "failed";
+ const isFailed = entry.toolStatus === "failed";
+ const statusLabel = entry.toolStatus && entry.toolStatus !== "completed"
+ ? entry.toolStatus.replace("_", " ")
+ : "";
- // Tool results - collapsible
- if (isTool) {
return (
T}
- label={`${kindLabel}${statusLabel ? ` (${statusLabel})` : ""}`}
- className="tool"
+ label={`tool call - ${entry.toolName ?? "tool"}${statusLabel ? ` (${statusLabel})` : ""}`}
+ className={`tool${isFailed ? " error" : ""}`}
>
- {hasParts ? (
- (item.content ?? []).map(renderContentPart)
- ) : entry.deltaText ? (
- {entry.deltaText}
- ) : (
- No content.
+ {entry.toolInput && {entry.toolInput}}
+ {isComplete && entry.toolOutput && (
+
+
result
+
{entry.toolOutput}
+
+ )}
+ {!isComplete && !entry.toolInput && (
+
+
+
+
+
)}
);
}
return (
-
- {!isFailed &&
{getAvatarLabel(messageClass)}
}
+
+
{getAvatarLabel(messageClass)}
- {(item.kind !== "message" || item.status !== "completed") && (
-
- {isFailed &&
}
-
{kindLabel}
- {statusLabel && (
-
- {statusLabel}
-
- )}
-
- )}
- {hasParts ? (
- (item.content ?? []).map(renderContentPart)
- ) : entry.deltaText ? (
-
- {entry.deltaText}
- {isInProgress && }
-
- ) : isInProgress ? (
+ {entry.text ? (
+
{entry.text}
+ ) : (
- ) : (
-
No content yet.
)}
);
})}
{sessionError &&
{sessionError}
}
- {eventError &&
{eventError}
}
);
diff --git a/frontend/packages/inspector/src/components/chat/messageUtils.tsx b/frontend/packages/inspector/src/components/chat/messageUtils.tsx
index fba8962..bdf9f2d 100644
--- a/frontend/packages/inspector/src/components/chat/messageUtils.tsx
+++ b/frontend/packages/inspector/src/components/chat/messageUtils.tsx
@@ -1,13 +1,12 @@
-import type { UniversalItem } from "sandbox-agent";
+import type { TimelineEntry } from "./types";
import { Settings, AlertTriangle, User } from "lucide-react";
import type { ReactNode } from "react";
-export const getMessageClass = (item: UniversalItem) => {
- if (item.kind === "tool_call" || item.kind === "tool_result") return "tool";
- if (item.kind === "system" || item.kind === "status") return "system";
- if (item.role === "user") return "user";
- if (item.role === "tool") return "tool";
- if (item.role === "system") return "system";
+export const getMessageClass = (entry: TimelineEntry) => {
+ if (entry.kind === "tool") return "tool";
+ if (entry.kind === "meta") return entry.meta?.severity === "error" ? "error" : "system";
+ if (entry.kind === "reasoning") return "assistant";
+ if (entry.role === "user") return "user";
return "assistant";
};
diff --git a/sdks/acp-http-client/package.json b/sdks/acp-http-client/package.json
index ad1bc4e..1fd4fb6 100644
--- a/sdks/acp-http-client/package.json
+++ b/sdks/acp-http-client/package.json
@@ -1,6 +1,6 @@
{
"name": "acp-http-client",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "Protocol-faithful ACP JSON-RPC over streamable HTTP client.",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/cli-shared/package.json b/sdks/cli-shared/package.json
index 4317604..d878151 100644
--- a/sdks/cli-shared/package.json
+++ b/sdks/cli-shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/cli-shared",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "Shared helpers for sandbox-agent CLI and SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/cli/package.json b/sdks/cli/package.json
index 90ad966..e79e1cc 100644
--- a/sdks/cli/package.json
+++ b/sdks/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/cli",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "CLI for sandbox-agent - run AI coding agents in sandboxes",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/cli/platforms/darwin-arm64/package.json b/sdks/cli/platforms/darwin-arm64/package.json
index 462abb3..74deca6 100644
--- a/sdks/cli/platforms/darwin-arm64/package.json
+++ b/sdks/cli/platforms/darwin-arm64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/cli-darwin-arm64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "sandbox-agent CLI binary for macOS ARM64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/cli/platforms/darwin-x64/package.json b/sdks/cli/platforms/darwin-x64/package.json
index 20b2644..d179804 100644
--- a/sdks/cli/platforms/darwin-x64/package.json
+++ b/sdks/cli/platforms/darwin-x64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/cli-darwin-x64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "sandbox-agent CLI binary for macOS x64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/cli/platforms/linux-arm64/package.json b/sdks/cli/platforms/linux-arm64/package.json
index 5f7439f..684a4da 100644
--- a/sdks/cli/platforms/linux-arm64/package.json
+++ b/sdks/cli/platforms/linux-arm64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/cli-linux-arm64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "sandbox-agent CLI binary for Linux arm64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/cli/platforms/linux-x64/package.json b/sdks/cli/platforms/linux-x64/package.json
index 15bc1a6..7c004fc 100644
--- a/sdks/cli/platforms/linux-x64/package.json
+++ b/sdks/cli/platforms/linux-x64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/cli-linux-x64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "sandbox-agent CLI binary for Linux x64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/cli/platforms/win32-x64/package.json b/sdks/cli/platforms/win32-x64/package.json
index 3890155..9166aee 100644
--- a/sdks/cli/platforms/win32-x64/package.json
+++ b/sdks/cli/platforms/win32-x64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/cli-win32-x64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "sandbox-agent CLI binary for Windows x64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/gigacode/package.json b/sdks/gigacode/package.json
index e16e70e..3c9c614 100644
--- a/sdks/gigacode/package.json
+++ b/sdks/gigacode/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/gigacode",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "Gigacode CLI (sandbox-agent with OpenCode attach by default)",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/gigacode/platforms/darwin-arm64/package.json b/sdks/gigacode/platforms/darwin-arm64/package.json
index 2324291..ed7aea7 100644
--- a/sdks/gigacode/platforms/darwin-arm64/package.json
+++ b/sdks/gigacode/platforms/darwin-arm64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/gigacode-darwin-arm64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "gigacode CLI binary for macOS arm64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/gigacode/platforms/darwin-x64/package.json b/sdks/gigacode/platforms/darwin-x64/package.json
index 6b94c06..2a14864 100644
--- a/sdks/gigacode/platforms/darwin-x64/package.json
+++ b/sdks/gigacode/platforms/darwin-x64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/gigacode-darwin-x64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "gigacode CLI binary for macOS x64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/gigacode/platforms/linux-arm64/package.json b/sdks/gigacode/platforms/linux-arm64/package.json
index 9b82cac..a97c711 100644
--- a/sdks/gigacode/platforms/linux-arm64/package.json
+++ b/sdks/gigacode/platforms/linux-arm64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/gigacode-linux-arm64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "gigacode CLI binary for Linux arm64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/gigacode/platforms/linux-x64/package.json b/sdks/gigacode/platforms/linux-x64/package.json
index b9ac143..3ba6cd1 100644
--- a/sdks/gigacode/platforms/linux-x64/package.json
+++ b/sdks/gigacode/platforms/linux-x64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/gigacode-linux-x64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "gigacode CLI binary for Linux x64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/gigacode/platforms/win32-x64/package.json b/sdks/gigacode/platforms/win32-x64/package.json
index ae287f0..1e48389 100644
--- a/sdks/gigacode/platforms/win32-x64/package.json
+++ b/sdks/gigacode/platforms/win32-x64/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/gigacode-win32-x64",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "gigacode CLI binary for Windows x64",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-indexeddb/package.json b/sdks/persist-indexeddb/package.json
index 04eb853..ba6706d 100644
--- a/sdks/persist-indexeddb/package.json
+++ b/sdks/persist-indexeddb/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-indexeddb",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "IndexedDB persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-postgres/package.json b/sdks/persist-postgres/package.json
index be8afd6..245d820 100644
--- a/sdks/persist-postgres/package.json
+++ b/sdks/persist-postgres/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-postgres",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "PostgreSQL persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-rivet/package.json b/sdks/persist-rivet/package.json
index da170aa..4eb1791 100644
--- a/sdks/persist-rivet/package.json
+++ b/sdks/persist-rivet/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-rivet",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "Rivet Actor persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/persist-sqlite/package.json b/sdks/persist-sqlite/package.json
index 512dfe1..80623a1 100644
--- a/sdks/persist-sqlite/package.json
+++ b/sdks/persist-sqlite/package.json
@@ -1,6 +1,6 @@
{
"name": "@sandbox-agent/persist-sqlite",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "SQLite persistence driver for the Sandbox Agent TypeScript SDK",
"license": "Apache-2.0",
"repository": {
diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json
index 2df4050..8997de1 100644
--- a/sdks/typescript/package.json
+++ b/sdks/typescript/package.json
@@ -1,6 +1,6 @@
{
"name": "sandbox-agent",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "Universal API for automatic coding agents in sandboxes. Supports Claude Code, Codex, OpenCode, and Amp.",
"license": "Apache-2.0",
"repository": {