Repository navigation
Add step.sandbox.python() - #1783
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ations Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Takes the span kind and title, so other scoped calls can reuse the create, run and destroy lifecycle. No behaviour change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Runs a snippet with input as globals and a top-level return as the result. step.sandbox.python is one create, run and destroy group of kind python; the same method is on a created sandbox. A non-zero exit fails with stderr. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A destroy step that fails for good no longer changes the run's outcome or surfaces as a SuppressedError. The failed destroy attempt records an inngest.warnings entry on its step instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: e815586 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
[Medium impact] Adds Python code execution in sandboxes. Fix silent loss of large Python results and the missing advertised schema support before merging. Findings
Fix with agent prompt### Issue 1
packages/inngest/src/components/sandbox/runners.ts:87-91
`runCode` ignores `output` from `commands.run` and parses potentially shortened `stdout`. For example, `return 'x' * 3000000` exceeds the 2 MiB limit. The saved output keeps only the tail, removing `__INNGEST_RESULT__`, so `parseRunnerOutput` returns `undefined`. The one-shot runner then destroys the sandbox and reports success with the wrong result.
Detect a missing result caused by truncation and fail clearly instead of treating it as no return value.
### Issue 2
packages/inngest/src/components/sandbox/runners.ts:24-29
`RunnerOptions` has no `schema` option, and `runCode` returns parsed JSON without checking an output schema. The PR's advertised `schema: { output: z.object(...) }` example therefore fails TypeScript's option check and cannot infer or validate its result.
Add the promised schema support and a test covering inference and invalid output.
### Issue 3
packages/inngest/src/components/sandbox/runners.ts:136-141
`createOneShotRunner` creates the sandbox before `buildRunner` checks the input and payload size. Invalid input or a payload over 16 KiB throws `SandboxValidationError`, which is retryable and does not trigger cleanup while attempts remain. The same invalid payload then fails on every retry while keeping the sandbox alive.
Build and validate the command before creating the sandbox to avoid unnecessary allocation and retries.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.SummaryAdds
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ail on truncated results Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thon Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntax test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n its step The group is named for the user's step ID. Create, run, result and destroy steps get display names and the SDK origin so a trace dims them, and the result step parses and validates the file so its output is the value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runners parse and validate the result file inside its step through an internal helper, so the public files.read is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only step.sandbox.python remains. A user-built sandbox can't be assumed to have the language installed, and a handle runner is stateless, so it offers nothing over a one-shot plus commands.run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| options: Omit<OneShotRunnerOptions, "schema"> & { | ||
| schema: { output: TSchema }; | ||
| }, | ||
| ): Promise<StandardSchemaV1.InferOutput<TSchema>>; |
There was a problem hiding this comment.
should we account for serialization in the return type here? with schema.output: z.coerce.date(), TS says we get a Date, but after replay it’s a string
i repro’d value.getUTCFullYear() compiling and then throwing at runtime with both step.sandbox.python() and step.sandbox.typescript(). should we apply the same output typing as step.run?
There was a problem hiding this comment.
Ah nice, aye we'll Jsonify here atm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
## Summary
<!-- Succinctly describe your change, providing context, what you've
changed, and why. -->
Adds `step.sandbox.typescript()`, a managed-lifecycle Sandbox that runs
TypeScript code and can take `input`.
### Direct step usage
Run TS, with the sandbox's lifecycle managed for you.
`input`s become variables accessible by the code.
```ts
const stats = await step.sandbox.typescript("sum", `
return numbers.reduce((a: number, b: number) => a + b, 0)
`, {
input: { numbers: [1, 2, 3] },
});
```
### Reusable functions
Create a reusable function usable during any run.
Output can also be typed and validated using any
`StandardSchema`-compliant schema library.
```ts
import { step } from "inngest";
import { z } from "zod";
export const stats = async (id: string, values: number[]) => {
return step.sandbox.typescript(id, `
...
`, {
input: { values },
schema: { output: z.object({ words: z.number() }),
});
};
```
```ts
// later in a run
const { words } = await stats("get-stats", event.data.body);
```
### Trace
<img width="1082" height="227" alt="image"
src="https://github.com/user-attachments/assets/3d1c8ad2-7f82-4756-b088-24844543b5fd"
/>
<img width="1086" height="323" alt="image"
src="https://github.com/user-attachments/assets/0524e5ab-c794-47a5-9f5d-a05fd6cfc58f"
/>
## Checklist
<!-- Tick these items off as you progress. -->
<!-- If an item isn't applicable, ideally please strikeout the item by
wrapping it in "~~"" and suffix it with "N/A My reason for skipping
this." -->
<!-- e.g. "- [ ] ~~Added tests~~ N/A Only touches docs" -->
- [ ] ~Added a [docs PR](https://github.com/inngest/website) that
references this PR~ N/A Later bump
- [x] Added unit/integration tests
- [x] Added changesets if applicable
## Related
<!-- A space for any related links, issues, or PRs. -->
<!-- Linear issues are autolinked. -->
<!-- e.g. - INN-123 -->
<!-- GitHub issues/PRs can be linked using shorthand. -->
<!-- e.g. "- inngest/inngest#123" -->
<!-- Feel free to remove this section if there are no applicable related
links.-->
- Based on #1778 and #1783
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Summary
Adds
step.sandbox.python(), a managed-lifecycle Sandbox that runs Python code and can takeinput.Direct step usage
Use Python from TS, with the sandbox's lifecycle managed for you.
inputs become variables accessible by the code.Reusable functions
Create a reusable function usable during any run.
Output can also be typed and validated using any
StandardSchema-compliant schema library.Trace
Checklist
Added a docs PR that references this PRN/A Later bumpRelated