Skip to content

Commit 02330b6

Browse files
authored
refactor!: simplify runtime package boundaries (#24)
1 parent afb6a2c commit 02330b6

55 files changed

Lines changed: 547 additions & 508 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/runtime-boundaries.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@execbox/core": minor
3+
"@execbox/quickjs": minor
4+
"@execbox/remote": minor
5+
"@execbox/isolated-vm": patch
6+
---
7+
8+
Simplify pre-1.0 runtime boundaries. `@execbox/core/runtime` replaces the unsupported `@execbox/core/_internal` subpath and now owns executor-author helpers such as runtime option resolution, manifest dispatch, code normalization, timeout helpers, log formatting, and error normalization. The main `@execbox/core` entrypoint is now focused on app-facing provider, executor, result, error, and typegen APIs.
9+
10+
Move the QuickJS remote runner endpoint to `@execbox/quickjs/remote-endpoint` and remove the hidden `@execbox/quickjs` dependency from `@execbox/remote`. Remote transports now expose only `RemoteExecutor`, `RemoteRunnerPort`, and transport contracts from `@execbox/remote`.

CLAUDE.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/architecture/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ flowchart LR
4040

4141
### Package Roles
4242

43-
| Package | Role |
44-
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- |
45-
| `@execbox/core` | Core types, provider resolution, shared runner semantics, MCP adapters, and the `@execbox/core/protocol` transport subpath |
46-
| `@execbox/quickjs` | Default QuickJS executor package with inline, worker-hosted, and process-hosted modes plus a reusable runner |
47-
| `@execbox/remote` | Transport-backed executor that reuses the QuickJS protocol endpoint across an app-defined boundary |
48-
| `@execbox/isolated-vm` | Alternate executor backend using a fresh `isolated-vm` context and a reusable isolated-vm runner |
43+
| Package | Role |
44+
| ---------------------- | ------------------------------------------------------------------------------------------------------------ |
45+
| `@execbox/core` | App-facing core types, provider resolution, MCP adapters, plus runtime and protocol subpaths |
46+
| `@execbox/quickjs` | Default QuickJS executor package with inline, worker-hosted, and process-hosted modes plus a reusable runner |
47+
| `@execbox/remote` | Transport-backed executor that runs against an app-defined runner boundary |
48+
| `@execbox/isolated-vm` | Alternate executor backend using a fresh `isolated-vm` context and a reusable isolated-vm runner |
4949

5050
## End-to-End Execution Model
5151

@@ -100,4 +100,4 @@ Key implications:
100100

101101
## Architecture In One Paragraph
102102

103-
`@execbox/core` owns the stable execution contract, provider resolution, shared runner semantics, MCP adapters, and the `@execbox/core/protocol` transport surface. `@execbox/quickjs` and `@execbox/isolated-vm` each expose a runtime-specific reusable runner. Hosted `@execbox/quickjs` modes and `@execbox/remote` sit on top of `@execbox/core/protocol`, which owns the transport boundary: message shapes, shared host sessions, and reusable resource pools for transport-backed execution.
103+
`@execbox/core` owns the app-facing execution contract, provider resolution, MCP adapters, and the `@execbox/core/protocol` transport surface. Runtime implementers use `@execbox/core/runtime` for shared dispatch, manifest, timeout, log, and normalization helpers. `@execbox/quickjs` and `@execbox/isolated-vm` each expose a runtime-specific reusable runner. Hosted `@execbox/quickjs` modes and `@execbox/remote` sit on top of `@execbox/core/protocol`, which owns the transport boundary: message shapes, shared host sessions, and reusable resource pools for transport-backed execution.

docs/architecture/execbox-core.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ This page covers the parts of execbox that stay stable regardless of which execu
1717
The core package exposes three main responsibilities:
1818

1919
- Resolve host-authored tools into a deterministic guest namespace
20-
- Normalize guest code into an executable async shape
21-
- Define the stable execution contract and shared runner semantics used by all executors
20+
- Define the stable app-facing execution contract
21+
- Provide runtime implementers with shared runner semantics through `@execbox/core/runtime`
2222

2323
The main public concepts are:
2424

@@ -66,7 +66,7 @@ The resolved provider also carries two maps:
6666

6767
## Guest Code Normalization
6868

69-
Executors do not evaluate arbitrary snippets directly. `normalizeCode()` first turns model- or user-produced text into a consistent async function body.
69+
Executors do not evaluate arbitrary snippets directly. Runtime implementers import `normalizeCode()` from `@execbox/core/runtime` to turn model- or user-produced text into a consistent async function body.
7070

7171
That normalization handles:
7272

@@ -79,27 +79,28 @@ The practical effect is that all executors can treat guest code as “an async f
7979

8080
## Shared Runner Semantics
8181

82-
The core package owns the small runner-level contract that sits between resolved providers and runtime-specific runners.
82+
The `@execbox/core/runtime` entrypoint owns the small runner-level helper surface that sits between resolved providers and runtime-specific runners.
8383

8484
That contract is intentionally transport-neutral:
8585

8686
- `extractProviderManifests()` converts resolved providers into transport-safe manifests
8787
- `createToolCallDispatcher()` turns a runner-emitted tool call back into a trusted host invocation
88-
- `ExecutorRuntimeOptions` carries timeout, memory, and log limits in a runtime-agnostic form
88+
- `resolveExecutorRuntimeOptions()` applies shared timeout, memory, and log defaults
89+
- `ExecutorRuntimeOptions` carries the runtime-agnostic limit shape used by public executor options
8990

9091
```mermaid
9192
sequenceDiagram
9293
participant Host as Host app
93-
participant Core as execbox core
94+
participant Runtime as core runtime
9495
participant Runner as Runtime-specific runner
9596
participant Tool as Resolved tool wrapper
9697
97-
Host->>Core: extractProviderManifests(providers)
98+
Host->>Runtime: extractProviderManifests(providers)
9899
Host->>Runner: runSession(code, manifests, onToolCall)
99-
Runner->>Core: onToolCall({ providerName, safeToolName, input })
100-
Core->>Tool: descriptor.execute(input, context)
101-
Tool-->>Core: JSON-safe result or ExecuteError
102-
Core-->>Runner: ToolCallResult
100+
Runner->>Runtime: onToolCall({ providerName, safeToolName, input })
101+
Runtime->>Tool: descriptor.execute(input, context)
102+
Tool-->>Runtime: JSON-safe result or ExecuteError
103+
Runtime-->>Runner: ToolCallResult
103104
```
104105

105106
This seam is what lets execbox share semantics across:
@@ -198,7 +199,8 @@ The core package does not own QuickJS, `isolated-vm`, worker threads, or transpo
198199

199200
The consequence is deliberate separation between:
200201

201-
- core execution and runner semantics in `@execbox/core`
202+
- app-facing execution contracts in `@execbox/core`
203+
- runtime implementer helpers in `@execbox/core/runtime`
202204
- transport/session mechanics in `@execbox/core/protocol`
203205
- runtime-specific bridge code in executor packages
204206

examples/execbox-remote.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { resolveProvider } from "@execbox/core";
2-
import { RemoteExecutor, attachQuickJsRemoteEndpoint } from "@execbox/remote";
2+
import { attachQuickJsRemoteEndpoint } from "@execbox/quickjs/remote-endpoint";
3+
import { RemoteExecutor } from "@execbox/remote";
34
import type {
45
DispatcherMessage,
56
HostTransport,

package-lock.json

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"package:check": "npm_config_cache=$PWD/.npm-cache CI=1 npm run build",
3939
"test": "vitest run",
4040
"test:dist-smoke": "node --import tsx scripts/test-dist-smoke.ts",
41-
"test:security": "node ./node_modules/vitest/vitest.mjs run packages/core/__tests__/security/isJsonSerializable.test.ts packages/core/__tests__/core/createToolCallDispatcher.test.ts packages/core/__tests__/protocol/hostSession.test.ts packages/quickjs/__tests__/protocolEndpoint.test.ts packages/remote/__tests__/runnerEndpoint.test.ts && node ./node_modules/vitest/vitest.mjs run packages/core/__tests__/mcp/penetration.test.ts packages/remote/__tests__/penetration.test.ts packages/quickjs/__tests__/hostedPenetration.test.ts && node ./node_modules/vitest/vitest.mjs run packages/quickjs/__tests__/processHostLifecycle.test.ts packages/quickjs/__tests__/workerHostLifecycle.test.ts",
41+
"test:security": "node ./node_modules/vitest/vitest.mjs run packages/core/__tests__/security/isJsonSerializable.test.ts packages/core/__tests__/core/createToolCallDispatcher.test.ts packages/core/__tests__/protocol/hostSession.test.ts packages/quickjs/__tests__/protocolEndpoint.test.ts packages/quickjs/__tests__/remoteEndpoint.test.ts && node ./node_modules/vitest/vitest.mjs run packages/core/__tests__/mcp/penetration.test.ts packages/remote/__tests__/penetration.test.ts packages/quickjs/__tests__/hostedPenetration.test.ts && node ./node_modules/vitest/vitest.mjs run packages/quickjs/__tests__/processHostLifecycle.test.ts packages/quickjs/__tests__/workerHostLifecycle.test.ts",
4242
"test:isolated-vm": "VITEST_INCLUDE_ISOLATED_VM=1 NODE_OPTIONS=--no-node-snapshot node --no-node-snapshot ./node_modules/vitest/vitest.mjs run packages/isolated-vm/__tests__",
4343
"test:watch": "vitest",
4444
"typecheck": "tsc --noEmit",

packages/core/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ npm install @execbox/core @execbox/quickjs
3232

3333
Swap in `@execbox/remote` or `@execbox/isolated-vm` when you need a different runtime boundary.
3434

35+
## Runtime Implementer Surface
36+
37+
Application code should usually import from `@execbox/core`, `@execbox/core/mcp`, or `@execbox/core/protocol`.
38+
Executor and runner packages should import shared runtime helpers from `@execbox/core/runtime` instead. That subpath contains the manifest dispatcher, runtime option defaults, timeout helpers, log formatting, code normalization, and error normalization used to keep runtime implementations aligned.
39+
3540
## Smallest Working Usage
3641

3742
```ts

packages/core/__tests__/core/createToolCallDispatcher.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { describe, expect, it } from "vitest";
33
import {
44
createToolCallDispatcher,
55
getExecutionTimeoutMessage,
6-
resolveProvider,
7-
} from "@execbox/core";
6+
} from "@execbox/core/runtime";
7+
import { resolveProvider } from "@execbox/core";
88

99
describe("createToolCallDispatcher", () => {
1010
it("does not start new host tool work after the execution has been aborted", async () => {

packages/core/__tests__/core/normalize.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vitest";
2-
import { normalizeCode } from "@execbox/core";
2+
import { normalizeCode } from "@execbox/core/runtime";
33

44
describe("normalizeCode", () => {
55
it("strips fenced code blocks before wrapping", () => {

0 commit comments

Comments
 (0)