experimental-ash 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/docs/public/auth-and-route-protection.md +9 -0
- package/dist/docs/public/sandbox.md +42 -19
- package/dist/docs/public/session-context.md +1 -1
- package/dist/src/compiled/.vendor-stamp.json +2 -2
- package/dist/src/compiled/@vercel/sandbox/index.d.ts +11 -2
- package/dist/src/compiled/@vercel/sandbox/index.js +3 -3
- package/dist/src/compiled/@vercel/sandbox/package.json +1 -1
- package/dist/src/compiled/_chunks/node/{auth-ZhCJAHxl.js → auth-CVVvWjaK.js} +1 -1
- package/dist/src/compiled/_chunks/node/{version-D4IYmfaS.js → version-nR4RSpFw.js} +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +1 -1
- package/dist/src/execution/sandbox/session.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/logging.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/packages/ash-scaffold/src/web-template.js +2 -2
- package/dist/src/public/channels/auth.d.ts +22 -11
- package/dist/src/public/channels/auth.js +1 -1
- package/dist/src/public/definitions/sandbox.d.ts +1 -1
- package/dist/src/public/sandbox/index.d.ts +1 -1
- package/dist/src/public/sandbox/vercel-sandbox.d.ts +4 -4
- package/dist/src/runtime/governance/auth/oidc.js +1 -1
- package/dist/src/runtime/governance/auth/token-claims.d.ts +2 -0
- package/dist/src/runtime/governance/auth/token-claims.js +1 -1
- package/dist/src/runtime/governance/auth/types.d.ts +6 -0
- package/dist/src/shared/sandbox-session.d.ts +0 -17
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# experimental-ash
|
|
2
2
|
|
|
3
|
+
## 0.35.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 24aa0cd: `localDev()` now also grants the local-dev session when running under `vercel dev` (`VERCEL=1` and `VERCEL_ENV=development`), not just loopback requests, so the local Vercel dev server can reach the agent without an OIDC token. `ASH_LOG_LEVEL` now defaults to `info` in every environment (previously `debug` outside production), making `debug` opt-in so dev output is no longer flooded with best-effort lines.
|
|
8
|
+
- a59d91c: Remove deprecated `SandboxSession.runCommand` alias and `SandboxRunCommandOptions` type. Use `sandbox.run({ command })` and `SandboxRunOptions` instead.
|
|
9
|
+
- 083c20a: `vercelOidc()` now accepts Vercel OIDC tokens with `external_sub` as user principals when the token matches the configured Vercel project and deployment environment. The resulting session auth uses `external_sub` as the subject, prefers `external_iss` / `connector_id` as the issuer, and exposes string OIDC profile claims such as `name`, `picture`, and `email` as attributes.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 321bae2: Upgrade `@vercel/sandbox` from 2.0.0 to 2.0.1. Sessions are now persistent by default and auto-resume on `Sandbox.get()`. Vendored types updated with `resume`, `onResume`, and `readFile`.
|
|
14
|
+
|
|
3
15
|
## 0.34.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -156,6 +156,15 @@ Use this for the common Vercel deployment path. Verifies a bearer JWT against th
|
|
|
156
156
|
issuer; tokens minted for the current `VERCEL_PROJECT_ID` are always accepted (so internal
|
|
157
157
|
subagent / runtime callers authenticate without configuration).
|
|
158
158
|
|
|
159
|
+
It accepts Vercel OIDC bearer tokens after signature, issuer, audience, and time-claim verification.
|
|
160
|
+
Tokens for the current `VERCEL_PROJECT_ID` authenticate as runtime/service callers. Tokens with
|
|
161
|
+
`external_sub` authenticate as user callers when their `project_id` matches `VERCEL_PROJECT_ID` if
|
|
162
|
+
configured, and their `environment` matches `VERCEL_TARGET_ENV` or `VERCEL_ENV` if configured. For
|
|
163
|
+
those user tokens, `external_sub` becomes the session subject, `external_iss` becomes the session
|
|
164
|
+
issuer when present, and `connector_id` is used as the issuer fallback before the Vercel OIDC issuer.
|
|
165
|
+
String OIDC profile claims such as `name`, `picture`, and `email` are exposed in
|
|
166
|
+
`getSession().auth.current.attributes`.
|
|
167
|
+
|
|
159
168
|
### `none`
|
|
160
169
|
|
|
161
170
|
Returns a synthetic anonymous `SessionAuthContext`. Use as the final entry in `auth` to accept
|
|
@@ -31,7 +31,7 @@ import { defineSandbox } from "experimental-ash/sandbox";
|
|
|
31
31
|
export default defineSandbox({
|
|
32
32
|
async bootstrap({ use }) {
|
|
33
33
|
const sandbox = await use();
|
|
34
|
-
await sandbox.
|
|
34
|
+
await sandbox.run({ command: "apt-get install -y jq" });
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
37
|
```
|
|
@@ -58,7 +58,7 @@ The public lifecycle surface is intentionally small:
|
|
|
58
58
|
sandbox — creation happens at prewarm time and at first-time session-create, both driven by the
|
|
59
59
|
backend factory's options.
|
|
60
60
|
- `sandbox.resolvePath(path)` — translate a logical `/workspace/...` path into the live filesystem
|
|
61
|
-
- `sandbox.
|
|
61
|
+
- `sandbox.run({ command })` — run a shell command inside the sandbox
|
|
62
62
|
|
|
63
63
|
`defineSandbox` lives on `experimental-ash/sandbox`.
|
|
64
64
|
|
|
@@ -126,9 +126,9 @@ export default defineTool({
|
|
|
126
126
|
inputSchema: z.object({ kind: z.string(), payload: z.string() }),
|
|
127
127
|
async execute({ kind, payload }) {
|
|
128
128
|
const sandbox = await getSandbox();
|
|
129
|
-
await sandbox.
|
|
130
|
-
`echo ${JSON.stringify(JSON.stringify({ kind, payload }))} >> /var/lib/analytics/events.log`,
|
|
131
|
-
);
|
|
129
|
+
await sandbox.run({
|
|
130
|
+
command: `echo ${JSON.stringify(JSON.stringify({ kind, payload }))} >> /var/lib/analytics/events.log`,
|
|
131
|
+
});
|
|
132
132
|
return { ok: true };
|
|
133
133
|
},
|
|
134
134
|
});
|
|
@@ -144,7 +144,7 @@ Important rules:
|
|
|
144
144
|
### Workspace Paths
|
|
145
145
|
|
|
146
146
|
Every backend runs `bash` with `/workspace` as the working directory. `readFile(...)`,
|
|
147
|
-
`writeFile(...)`, and `
|
|
147
|
+
`writeFile(...)`, and `run(...)` all share that single namespace — `/workspace/foo` refers
|
|
148
148
|
to the same file whether the backend is local or Vercel.
|
|
149
149
|
|
|
150
150
|
`sandbox.resolvePath(...)` anchors a sandbox-relative path to `/workspace` and returns the
|
|
@@ -158,11 +158,11 @@ const sandbox = await getSandbox();
|
|
|
158
158
|
const analysisRoot = sandbox.resolvePath("python-analysis");
|
|
159
159
|
|
|
160
160
|
await sandbox.writeFile("python-analysis/run.py", "print('ok')\n");
|
|
161
|
-
await sandbox.
|
|
161
|
+
await sandbox.run({ command: `python ${JSON.stringify(`${analysisRoot}/run.py`)}` });
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
`readFile(...)` and `writeFile(...)` apply the same anchoring internally, so you only need to
|
|
165
|
-
call `resolvePath(...)` explicitly when building paths for `
|
|
165
|
+
call `resolvePath(...)` explicitly when building paths for `run(...)` or returning them
|
|
166
166
|
from authored helpers.
|
|
167
167
|
|
|
168
168
|
## Subagents Get Their Own Sandbox
|
|
@@ -236,7 +236,8 @@ export default defineSandbox({
|
|
|
236
236
|
|
|
237
237
|
Ash ships two built-in backends, exposed as factory functions from `experimental-ash/sandbox`:
|
|
238
238
|
|
|
239
|
-
- `vercelBackend(opts?)` — runs the sandbox on Vercel Sandbox
|
|
239
|
+
- `vercelBackend(opts?)` — runs the sandbox on [Vercel Sandbox](https://vercel.com/docs/sandbox) via
|
|
240
|
+
[`@vercel/sandbox`](https://vercel.com/docs/sandbox/sdk-reference).
|
|
240
241
|
- `localBackend(opts?)` — runs the sandbox locally via `just-bash`. Default for `pnpm ash dev`.
|
|
241
242
|
|
|
242
243
|
Attach a backend via `defineSandbox({ backend })`:
|
|
@@ -249,7 +250,7 @@ export default defineSandbox({
|
|
|
249
250
|
backend: vercelBackend({ runtime: "node24", resources: { vcpus: 2 } }),
|
|
250
251
|
async bootstrap({ use }) {
|
|
251
252
|
const sandbox = await use();
|
|
252
|
-
await sandbox.
|
|
253
|
+
await sandbox.run({ command: "git clone https://example.com/repo.git repo" });
|
|
253
254
|
},
|
|
254
255
|
async onSession({ use }) {
|
|
255
256
|
await use({ networkPolicy: "deny-all" });
|
|
@@ -282,9 +283,10 @@ The factory `opts` parameter is forwarded to the underlying SDK's `Sandbox.creat
|
|
|
282
283
|
every fresh sandbox the framework creates — both the template at prewarm time and the session at
|
|
283
284
|
first-time session-create.
|
|
284
285
|
|
|
285
|
-
On resume (when the framework reattaches to an existing
|
|
286
|
-
`Sandbox.
|
|
287
|
-
|
|
286
|
+
On resume (when the framework reattaches to an existing
|
|
287
|
+
[persistent](#session-persistence-and-resume) session via `Sandbox.get`), no `Sandbox.create` call
|
|
288
|
+
happens, so the factory opts are not re-applied. The existing sandbox keeps whatever configuration
|
|
289
|
+
it had from its prior creation.
|
|
288
290
|
|
|
289
291
|
Lifecycle hooks remain update-time and override post-create:
|
|
290
292
|
|
|
@@ -361,9 +363,8 @@ lock down via `onSession`.
|
|
|
361
363
|
|
|
362
364
|
### Timeout
|
|
363
365
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
a different default on the factory, or override per-session in `onSession`:
|
|
366
|
+
Sandbox VMs shut down after a configurable timeout. Ash defaults to **30 minutes**. Set a different
|
|
367
|
+
value on the factory, or override per-session in `onSession`:
|
|
367
368
|
|
|
368
369
|
```ts
|
|
369
370
|
// factory default — applies to every fresh sandbox
|
|
@@ -376,7 +377,28 @@ async onSession({ use }) {
|
|
|
376
377
|
```
|
|
377
378
|
|
|
378
379
|
The maximum timeout depends on your Vercel plan: **5 hours** for Pro/Enterprise, **45 minutes** for
|
|
379
|
-
Hobby.
|
|
380
|
+
Hobby. When the timeout fires, the VM shuts down — but because sessions are
|
|
381
|
+
[persistent](#session-persistence-and-resume), the filesystem state is preserved and the sandbox
|
|
382
|
+
resumes automatically on the next request.
|
|
383
|
+
|
|
384
|
+
### Session Persistence and Resume
|
|
385
|
+
|
|
386
|
+
Sandbox sessions are **persistent by default**: when the VM shuts down (timeout or inactivity), the
|
|
387
|
+
filesystem state is preserved. The next time a message arrives — even days or weeks later — the
|
|
388
|
+
sandbox automatically resumes from that state.
|
|
389
|
+
|
|
390
|
+
This means:
|
|
391
|
+
|
|
392
|
+
- A user can send a message days after the last interaction and pick up where they left off.
|
|
393
|
+
Files created during earlier turns, installed dependencies, and workspace state are all preserved.
|
|
394
|
+
- The resume is transparent to your code — no configuration is required.
|
|
395
|
+
- If a sandbox has been deleted (by cleanup policies or manual deletion), Ash creates a fresh
|
|
396
|
+
session from the prewarmed template snapshot. The user gets a clean sandbox seeded with framework
|
|
397
|
+
defaults, bootstrap output, and workspace files — but any session-specific state from prior turns
|
|
398
|
+
is lost.
|
|
399
|
+
|
|
400
|
+
See the [Vercel Sandbox documentation](https://vercel.com/docs/sandbox) for details on persistence
|
|
401
|
+
behavior and plan-specific retention limits.
|
|
380
402
|
|
|
381
403
|
### Tags
|
|
382
404
|
|
|
@@ -386,8 +408,7 @@ Ash attaches Vercel Sandbox tags for runtime attribution:
|
|
|
386
408
|
- `channel` — the active channel adapter kind
|
|
387
409
|
- `sessionId` — the Ash session id
|
|
388
410
|
|
|
389
|
-
Custom tags can be set on the factory
|
|
390
|
-
`onSession`'s `use()` (applied via `sandbox.update`):
|
|
411
|
+
Custom tags can be set on the factory or via `onSession`'s `use()`:
|
|
391
412
|
|
|
392
413
|
```ts
|
|
393
414
|
backend: vercelBackend({ tags: { team: "infra" } });
|
|
@@ -425,3 +446,5 @@ Important behavior:
|
|
|
425
446
|
- [Tools](./tools.md)
|
|
426
447
|
- [Workspace](./workspace.md)
|
|
427
448
|
- [Session Context](./session-context.md)
|
|
449
|
+
- [Vercel Sandbox](https://vercel.com/docs/sandbox) — platform documentation for Vercel Sandbox
|
|
450
|
+
- [Vercel Sandbox SDK Reference](https://vercel.com/docs/sandbox/sdk-reference) — `@vercel/sandbox` API reference
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"@standard-schema/spec": "1.1.0",
|
|
21
21
|
"turndown": "7.2.4",
|
|
22
22
|
"@vercel/oidc": "3.4.1",
|
|
23
|
-
"@vercel/sandbox": "2.0.
|
|
23
|
+
"@vercel/sandbox": "2.0.1",
|
|
24
24
|
"@workflow/core": "5.0.0-beta.7",
|
|
25
25
|
"@workflow/errors": "5.0.0-beta.4",
|
|
26
26
|
"zod": "4.4.3",
|
|
27
27
|
"zod-validation-error": "5.0.0"
|
|
28
28
|
},
|
|
29
|
-
"scriptHash": "
|
|
29
|
+
"scriptHash": "de60e990328c8664beec98bc5e81625ecdc0cdace1ba8bd8aa267bf9bea29af4"
|
|
30
30
|
}
|
|
@@ -41,6 +41,7 @@ export interface SandboxCreateOptions {
|
|
|
41
41
|
env?: Record<string, string> | undefined;
|
|
42
42
|
name?: string | undefined;
|
|
43
43
|
networkPolicy?: NetworkPolicy | undefined;
|
|
44
|
+
onResume?: ((sandbox: Sandbox) => Promise<void>) | undefined;
|
|
44
45
|
persistent?: boolean | undefined;
|
|
45
46
|
ports?: number[] | undefined;
|
|
46
47
|
resources?: { vcpus?: number | undefined } | undefined;
|
|
@@ -52,6 +53,13 @@ export interface SandboxCreateOptions {
|
|
|
52
53
|
timeout?: number | undefined;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
export interface SandboxGetOptions {
|
|
57
|
+
name: string;
|
|
58
|
+
onResume?: ((sandbox: Sandbox) => Promise<void>) | undefined;
|
|
59
|
+
resume?: boolean | undefined;
|
|
60
|
+
signal?: AbortSignal | undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
export interface SandboxRunCommandParams {
|
|
56
64
|
args?: readonly string[] | undefined;
|
|
57
65
|
cmd: string;
|
|
@@ -95,9 +103,10 @@ export declare class Sandbox {
|
|
|
95
103
|
status: string;
|
|
96
104
|
tags?: Record<string, string> | undefined;
|
|
97
105
|
static create(options?: SandboxCreateOptions): Promise<Sandbox>;
|
|
98
|
-
static get(options:
|
|
106
|
+
static get(options: SandboxGetOptions): Promise<Sandbox>;
|
|
99
107
|
domain(port: number): string;
|
|
100
|
-
|
|
108
|
+
readFile(file: { path: string }): Promise<ReadableStream<Uint8Array> | null>;
|
|
109
|
+
readFileToBuffer(file: { path: string }): Promise<Buffer | null>;
|
|
101
110
|
runCommand(input: SandboxRunCommandParams & { detached: true }): Promise<SandboxCommand>;
|
|
102
111
|
runCommand(input: SandboxRunCommandParams): Promise<SandboxCommandFinished>;
|
|
103
112
|
snapshot(options?: unknown): Promise<{ snapshotId: string }>;
|