Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/publish-ghcr-platform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -542,8 +542,12 @@ jobs:
docker buildx imagetools create "${application_tags[@]}" "${application_sources[@]}"
docker buildx imagetools create "${initializer_tags[@]}" "${initializer_sources[@]}"

application_digest=$(docker buildx imagetools inspect "${application_sources[0]}" --format '{{.Manifest.Digest}}')
initializer_digest=$(docker buildx imagetools inspect "${initializer_sources[0]}" --format '{{.Manifest.Digest}}')
# @note the Compose artifact must pin the merged manifest list just
# created, never a source: after a fresh build the sources are
# single-architecture digests, so pinning one would leave the stack
# unable to start on the other architecture
application_digest=$(docker buildx imagetools inspect "${application_tags[1]}" --format '{{.Manifest.Digest}}')
initializer_digest=$(docker buildx imagetools inspect "${initializer_tags[1]}" --format '{{.Manifest.Digest}}')
echo "application_digest=${application_digest}" >> "$GITHUB_OUTPUT"
echo "initializer_digest=${initializer_digest}" >> "$GITHUB_OUTPUT"

Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ here. The release version is defined in the workspace root `package.json`.

## [Unreleased]

## [0.2.2] - 2026-09-10

### Fixed

- Upgrade the default sandbox runtime to AgentOS `0.2.20-rc.1` so fast shell
pipelines finish without waiting for the blocking-read watchdog at each
stage. Node output redirection and slow-writer limitations remain documented
in the sandbox package.
- Pin the multi-architecture image manifests in the published Compose
distribution artifacts. A freshly built release pinned one arbitrary
per-architecture digest per image, so `docker compose -f oci://...` failed
with `unsupported: "platform linux/arm64"` (or amd64) depending on the
build.

## [0.2.1] - 2026-09-10

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "platform",
"version": "0.2.1",
"version": "0.2.2",
"private": true,
"license": "Apache-2.0",
"packageManager": "pnpm@11.24.0",
Expand Down
20 changes: 14 additions & 6 deletions packages/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,15 @@ community image.
served by a driver in this process, so they are as slow as the store and as
plain as an object store is - no symlinks, no partial writes, a rename is a
copy and a delete.
- **Fast pipelines.** A pipe between two commands can stall for the
runtime's ten-second blocking-read limit at end of stream, once per stage.
Upstream defect, tracked in
[rivet-dev/agentos#1959](https://github.com/rivet-dev/agentos/issues/1959).
@todo check the ticket when bumping the runtime and drop this entry once
a release fixes it.
- **Reliable Node output redirection.** Node's `console.log`, `console.error`
and stdout/stderr streams can lose output when redirected to a pipe or
file. Tracked in
[rivet-dev/agentos#1967](https://github.com/rivet-dev/agentos/pull/1967).
- **Blocking reads from slow writers.** A writer that stays quiet beyond
`maxBlockingReadMs` can still cause a pipe reader to fail with `EAGAIN` and
lose output. The default limit is unchanged; lowering it makes this easier
to trigger. Tracked in
[rivet-dev/agentos#1966](https://github.com/rivet-dev/agentos/pull/1966).
- **A clean `ls -la` of `/workspace`.** The listing prints, then the command
exits 1 with `Invalid argument` from the mount's directory entries at this
version. `ls -l` is unaffected.
Expand Down Expand Up @@ -99,6 +102,11 @@ run.

## Requirements

The runtime is pinned to `@rivet-dev/agentos-core@0.2.20-rc.1`, a release
candidate that fixes the fast-pipeline stalls in
[rivet-dev/agentos#1959](https://github.com/rivet-dev/agentos/issues/1959).
The Node redirection and slow-writer limitations above remain in this version.

The sidecar is a native binary the package resolves for the current platform:
Linux x64 and arm64 with glibc, and macOS. Alpine images cannot load it; the
platform image builds on Debian for this reason. `assertConfigured` starts a VM
Expand Down
2 changes: 1 addition & 1 deletion packages/sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@chatbotkit-dev/sandbox-spec": "workspace:*",
"@chatbotkit-dev/storage": "workspace:*",
"@chatbotkit-dev/storage-spec": "workspace:*",
"@rivet-dev/agentos-core": "0.2.19"
"@rivet-dev/agentos-core": "0.2.20-rc.1"
},
"devDependencies": {
"@chatbotkit-dev/eslint-config": "workspace:*",
Expand Down
22 changes: 4 additions & 18 deletions packages/sandbox/src/mount.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
// 64KB `pread` slices, a truncating `writeFile` before the real one - is what
// the caches exist for.

import { jest } from '@jest/globals'

import { AgentOs } from '@rivet-dev/agentos-core'

import { createStorageDriver } from './mount.ts'
Expand All @@ -22,15 +20,6 @@ const PERMISSIONS = {
network: 'allow',
}

// @note generous because the runtime, not the driver, sets the pace: at this
// version a stalled pipe holds a command for its ten-second blocking-read
// limit and a process started meanwhile wedges the VM, so one slow step on a
// CI runner cascades into the tests after it. @todo once
// rivet-dev/agentos#1959 is fixed in a pinned release, bring this back to the
// default and put the `| sort` back into the nested-directories test.

jest.setTimeout(120_000)

let store
let vm

Expand Down Expand Up @@ -96,14 +85,11 @@ describe('reading', () => {
})

it('walks nested directories', async () => {
// @note no `| sort`: a pipe between two guest commands stalls for the
// runtime's blocking-read limit at this version (rivet-dev/agentos#1959),
// and a second process started while it stalls wedges the VM. See the
// timeout note at the top for when to restore it.
const { code, out, err } = await sh('find /space -type f | sort')

const { out } = await sh('find /space -type f')

expect(out.trim().split('\n').sort()).toEqual([
expect(code).toBe(0)
expect(err).toBe('')
expect(out.trim().split('\n')).toEqual([
'/space/hello.txt',
'/space/sub/a.txt',
'/space/sub/deeper/b.txt',
Expand Down
45 changes: 45 additions & 0 deletions packages/sandbox/src/pipelines.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { AgentOs } from '@rivet-dev/agentos-core'

let vm

beforeAll(async () => {
vm = await AgentOs.create({
// @note a short watchdog exposes the pipe stall as EAGAIN instead of
// waiting ten seconds per stage (rivet-dev/agentos#1959)
limits: { resources: { maxBlockingReadMs: 500 } },
})
}, 60_000)

afterAll(async () => {
await vm?.dispose()
})

const sh = (cmd) =>
vm.process.exec(cmd, {
cwd: '/workspace',
output: { capture: 'all' },
timeoutMs: 5_000,
})

it('sorts and filters environment output without a blocking-read failure', async () => {
const env = await sh('env')
const result = await sh("env | sort | sed -n '1,2p'")

expect(env.exitCode).toBe(0)
expect(result.exitCode).toBe(0)
expect(result.stderr).toBe('')
expect(result.stdout).toBe(
env.stdout.trim().split('\n').sort().slice(0, 2).join('\n') + '\n'
)
})

it.each([
['seq 1 5 | paste -sd,', '1,2,3,4,5\n'],
['seq 1 5000 | tail -n 1', '5000\n'],
])('preserves all expected output through %s', async (cmd, expected) => {
const result = await sh(cmd)

expect(result.exitCode).toBe(0)
expect(result.stderr).toBe('')
expect(result.stdout).toBe(expected)
})
Loading