Skip to content

Releases: torkbot/code-mode

v0.6.0

Choose a tag to compare

@ggoodman ggoodman released this 02 Aug 13:26
05a4d89

Typed program results

Programs can now return ordered text and image content explicitly instead of using console output as an implicit result channel:

export default async function ({ result }) {
  result.appendText("Rendered preview");
  result.appendImage({
    data: imageBase64,
    mimeType: "image/png",
  });
}

Successful executions always include a content array. Results are committed only when the program succeeds, while console output remains diagnostic telemetry.

Each result item is transported independently, so combined output can exceed the runtime's single-frame limit without weakening per-item bounds.

This release changes the successful RunOutcome shape: consumers should read outcome.content, which is an empty array when a program appends nothing.

v0.5.0

Choose a tag to compare

@ggoodman ggoodman released this 29 Jul 16:36
aad10be

Supply typed input through complete program contracts

Code mode now models the complete agent-facing program surface with
ProgramContract: optional per-execution input plus the host tools available to
the program. An input-bearing contract validates and transforms each host value
before module execution, then exposes the transformed value as strongly typed
input beside codemode and console.

const contract = createProgramContract({
  inputSchema: Event,
  tools,
});

const client = createClient({ runtime, contract });

await client.run(source, {
  input: event,
  signal,
});

Submitted programs receive the generated schema output type directly:

export default async function ({ input, codemode }: AgentProgramScope) {
  switch (input.type) {
    case "research.requested":
      await codemode.startResearch(input.payload);
      break;
  }
}

This works with the discriminated anyOf and oneOf schemas added in v0.4.0,
including contracts assembled dynamically from an event registry. Invalid or
non-JSON transformed input rejects on the host before the submitted module can
run.

Contracts that expect no input remain explicit:

const contract = createProgramContract({ tools });
await client.run(source, { signal });

This is an intentionally breaking replacement for the tool-only API:

  • Replace Toolbox and createToolbox() with ProgramContract and
    createProgramContract().
  • Replace ToolSchema with CodeModeSchema.
  • Pass contract, not toolbox, to createClient().
  • Supply input to run() only when the contract declares inputSchema.
  • Runtime implementations must preserve whether RuntimeExecuteRequest.input
    is present when invoking the program.

Install with:

npm install @torkbot/code-mode@0.5.0

v0.4.0

Choose a tag to compare

@ggoodman ggoodman released this 29 Jul 12:57
f5291d2

Author discriminated tool and event schemas

Code mode now emits exact agent-facing TypeScript for discriminated JSON Schema
unions. anyOf and oneOf work when every branch is a closed object sharing a
required string const property with a distinct value:

const Event = Type.Union([
  Type.Object(
    {
      type: Type.Literal("research.requested"),
      payload: Type.Object(
        { query: Type.String() },
        { additionalProperties: false },
      ),
    },
    { additionalProperties: false },
  ),
  Type.Object(
    {
      type: Type.Literal("research.completed"),
      payload: Type.Object(
        { report: Type.String() },
        { additionalProperties: false },
      ),
    },
    { additionalProperties: false },
  ),
]);

Agent programs can construct, narrow, and forward these values between tools
without losing exact input checking. Other composition shapes remain rejected
instead of being emitted as looser TypeScript.

Install with:

npm install @torkbot/code-mode@0.4.0

v0.3.1

Choose a tag to compare

@ggoodman ggoodman released this 23 Jul 10:41
3ac3767

Build Node.js runtimes without copying Host Node

Runtime-driver packages can now reuse code-mode's canonical Node.js 24
declarations and guest execution semantics through
@torkbot/code-mode/node-runtime:

import {
  assertNode24Version,
  createNode24BootstrapSource,
  loadNode24TypeDefinitionFiles,
} from "@torkbot/code-mode/node-runtime";

const driver: RuntimeDriver<MyNodeOptions> = {
  description: "My Node.js 24 runtime",
  loadTypeDefinitionFiles: loadNode24TypeDefinitionFiles,
  async connect(options, { runnerSource, signal }) {
    const version = await readRuntimeNodeVersion(options, signal);
    assertNode24Version(version, "My Node runtime");

    const source = createNode24BootstrapSource({
      runnerSource,
      channelFileDescriptor: 3,
    });
    return launchRuntimeNode(options, source, signal);
  },
};

The generated self-contained ESM provides the behavior Node runtime drivers
need to share:

  • fresh root modules with native ESM and package resolution from
    process.cwd();
  • multiplexed execution scheduling;
  • Node-formatted captured console output with stdout/stderr provenance;
  • the version-matched runner supplied by createRuntimeFactory().

Drivers still own process or VM launch, the connected file descriptor, ambient
stdio, boot cancellation, and resource disposal. Host Node now consumes this
same authoring surface, so Sandbox Node and future Node substrates can share
execution semantics without importing or copying Host Node lifecycle code.

This fixes the Node driver-authoring gap in v0.3.0. Install the patch with:

npm install @torkbot/code-mode@0.3.1

v0.3.0

Choose a tag to compare

@ggoodman ggoodman released this 23 Jul 02:45
bcd9c7d

Write ordinary ESM programs

Code-mode programs are now normal TypeScript-flavoured ECMAScript modules. Static imports work naturally, and the program default-exports the function the runtime should invoke:

import { inspect } from "node:util";

export default async function ({ codemode, console }: AgentProgramScope) {
  const result = await codemode.lookup({ query: "example" });
  console.log(inspect(result));
}

This replaces the previous single-expression source format. Code mode strips erasable TypeScript in place without adding a wrapper or prepending source, so runtime stack traces preserve the submitted line and column coordinates.

The runner passes both codemode and console in the scope. Programs can use either, both, or neither. Only this supplied console is captured; ambient and imported consoles remain ordinary runtime behavior. Captured output is deliberately text with explicit provenance:

{
  stream: "stdout" | "stderr";
  text: string;
}

Keep runtimes alive across executions

The public runtime is now an already-connected, async-disposable client/server session:

import { createClient } from "@torkbot/code-mode";
import { createHostNodeRuntime } from "@torkbot/code-mode/host-node";

const runtime = await createHostNodeRuntime(
  {
    nodePath: process.execPath,
    cwd: process.cwd(),
  },
  bootSignal,
);

try {
  const client = createClient({ runtime, toolbox });
  await client.run(source, { signal: executionSignal });
} finally {
  await runtime[Symbol.asyncDispose]();
}

The built-in Host Node runtime uses one persistent Node.js 24 process and multiplexes independent executions over it. The boot signal governs startup through runner readiness and then detaches; each execution has its own cancellation signal.

Every execution evaluates a fresh root module. Imported dependencies retain the execution platform's normal module-cache behavior.

Build runtime drivers around one small connection contract

Runtime integrations now implement RuntimeDriver<Options> and expose their user-facing factory with createRuntimeFactory(driver). A driver boots its environment, installs or evaluates the supplied version-matched runner, and returns a raw full-duplex byte connection:

const createMyRuntime = createRuntimeFactory({
  description: "My runtime",
  loadTypeDefinitionFiles,
  async connect(options, { runnerSource, signal }) {
    return {
      channel: {
        readable,
        writable,
      },
      finished,
      async [Symbol.asyncDispose]() {
        await closeRuntime();
      },
    };
  },
});

Code mode owns runner readiness, the wire protocol, tool routing, output, cancellation, and opaque execution correlation. Drivers only own placement, transport, module evaluation, scheduling, environment declarations, and resource lifecycle.

The reusable runner ships both as @torkbot/code-mode/runner and as self-contained ESM source through @torkbot/code-mode/runner/source. The factory supplies that flattened source to connect() automatically, so ordinary runtime consumers never need to plumb it.

Migrating from v0.2.0

This is an intentionally breaking release with no compatibility layer:

  • Replace expression-shaped programs with ESM containing a callable default export.
  • Read tools and captured output from the default export's { codemode, console } argument.
  • Replace Runtime.start(), RuntimeInstance, payload launch objects, and termination methods with a connected Runtime and async disposal.
  • Replace the removed @torkbot/code-mode/node entry point with @torkbot/code-mode/host-node.
  • Replace structured console telemetry with program-output events containing stream and text.
  • For custom substrates, implement RuntimeDriver.connect() and return RuntimeConnection; do not implement or interpret the internal protocol.
  • Ensure tool inputs and outputs crossing the runtime boundary are JSON-compatible.
  • Run the exported testRuntime() suite against every runtime implementation. It observes only the public Runtime and Client contracts.

Reliability

The runtime now rejects post-cancellation tool calls before they can become orphaned, cleans up raw connections even when runtime construction fails, and reports non-JSON tool outputs as failed telemetry instead of briefly reporting success. Program errors are bounded so one execution cannot collapse a shared runtime with an oversized failure frame.

Install this release with:

npm install @torkbot/code-mode@0.3.0

v0.2.0

Choose a tag to compare

@ggoodman ggoodman released this 15 Jul 23:56
84e19fd

Standard Web Streams at the runtime boundary

Runtime authors can now connect code mode directly to Web-compatible streams. The bespoke ByteChannel and ByteWriter contracts are gone, so substrates no longer need to imitate code-mode-specific write and idempotent-close behavior.

For example, a runtime instance now returns standard readable and writable byte streams:

const instance: RuntimeInstance = {
  channel: {
    readable: runtimeOutput,
    writable: runtimeInput,
  },
  finished,
  terminate,
};

Code mode acquires the writable stream's writer, serializes protocol writes, and closes it when execution finishes. Runtime implementations only need to supply the transport and retain ownership of launch, termination, and failure reporting.

Node integrations can use the platform adapter directly instead of maintaining a code-mode-specific writer:

import { Duplex } from "node:stream";

const channel = Duplex.toWeb(processPipe);

The generated Node 24 bootstrap uses the same native full-duplex Web Streams conversion. This keeps host Node, sandboxed Node, and future substrates on one runtime-neutral contract without moving process lifecycle into code mode.

Migrating runtime implementations

This release intentionally changes the runtime-author API. Replace the old channel shape:

{
  incoming: AsyncIterable<Uint8Array>;
  outgoing: {
    write(chunk: Uint8Array): Promise<void>;
    close(): Promise<void>;
  };
}

with:

{
  readable: ReadableStream<Uint8Array>;
  writable: WritableStream<Uint8Array>;
}

There is no compatibility layer or dual API. This makes incorrect adapters fail at compile time and leaves one clear transport contract.

Reliability

Program shutdown no longer waits behind a pending response read after an agent failure. The regression suite covers this case along with the complete host-Node and exported runtime conformance journeys.

Install this release with:

npm install @torkbot/code-mode@0.2.0