CodeMode
Code mode presents a toolkit to a model as one tool, execute, which runs code the model writes.
The code calls the tools of the presented toolkit as functions, await tools.<name>(input), so a
model does in one run what takes it many calls otherwise: it calls a tool, reads the result in
code, and filters it or goes on with the next call, and only what the code returns comes back to
the model. What it is shown is the same however many tools there are, and costs the tokens of their
declarations.
Running code is the job of a CodeRuntime, which this package does not have: it takes an
isolate that cannot reach anything but the tools it is given, and an adapter that has one
implements the interface, as @amends-dev/codemode-opencode does over opencode’s interpreter.
Everything else is here. A tool the code calls runs as it does when a model calls it directly, so
its input is checked and its failures are what it says. The code fails when it throws, runs too
long, or returns too much, and the model reads that.
A tool that needs approval cannot be called from code, which a session cannot ask a person about
in the middle of a run. It stays a tool of its own beside execute, and a session asks about its
calls as it does for any tool.
Since v0.1.0
Constructors
Section titled “Constructors”toolkit
Section titled “toolkit”Makes the toolkit of execute, and the tools of presented that need approval, over presented
and the runtime that runs the code. Building contacts nothing.
Example (Presenting tools as code the model writes)
import { Assembly, CodeMode } from "@amends-dev/toolkit"import { Effect } from "effect"
declare const members: ReadonlyArray<Assembly.Member>declare const runtime: CodeMode.CodeRuntime // an isolate that reaches only the tools it is given
const program = Effect.gen(function* () { const assembled = yield* Assembly.toolkit(members, (_source, definition) => definition.annotations.readOnly === true ? "allow" : "approve" ) // The model is shown `execute`, which calls the read-only tools as `await tools.<name>(input)`, // and each tool that needs approval as a tool of its own. return yield* CodeMode.toolkit(assembled, runtime, { timeout: "30 seconds" })})Signature
declare const toolkit: <Tools extends Record<string, Tool.Any>>( presented: Toolkit.WithHandler<Tools>, runtime: CodeRuntime, options?: Options | undefined) => Effect.Effect< Toolkit.WithHandler< Toolkit.MergeRecords< | Tools | { readonly execute: Tool.Tool< "execute", { readonly parameters: Schema.Struct<{ readonly code: Schema.String }> readonly success: Schema.Struct<{ readonly result: Schema.Codec<Schema.Json, Schema.Json, never, never> readonly logs: Schema.$Array<Schema.String> }> readonly failure: Schema.Struct<{ readonly code: Schema.String readonly message: Schema.String readonly retryable: Schema.Boolean }> readonly failureMode: "return" }, never > } > >, never, Tool.HandlerServices<Tools[keyof Tools]>>Since v0.1.0
Errors
Section titled “Errors”CodeError (class)
Section titled “CodeError (class)”Why code did not finish: it threw, or the runtime could not run it. A CodeRuntime fails with
it, and the model reads it as a Failure with what the code logged before.
Signature
declare class CodeErrorSince v0.1.0
Models
Section titled “Models”CodeRuntime (interface)
Section titled “CodeRuntime (interface)”Runs code in an isolate that can reach nothing but the tools of the execution, which it gives
the code as tools, beside console. An adapter that has such an isolate implements it, as
@amends-dev/codemode-opencode does; this package does not.
Signature
export interface CodeRuntime { /** * Runs the code, and ends the run when interrupted. It fails with `CodeError` when the code * throws, and for anything else that stops the run, such as a runtime that cannot start. */ readonly run: (execution: Execution) => Effect.Effect<Outcome, CodeError>}Since v0.1.0
Execution (interface)
Section titled “Execution (interface)”One run of code, which a CodeRuntime carries out.
Signature
export interface Execution { /** The code, as the body of an async function, which `return`s what the run gives back. */ readonly code: string
/** The names of the tools the code may call, as `tools.<name>`, which any other name is not. */ readonly tools: ReadonlyArray<string>
/** * Calls a tool with an input object, which the code gave or left empty, and answers with what the * tool gave, or fails as the tool failed. The code reads a failure as an error it can catch. */ readonly call: (tool: string, input: Schema.JsonObject) => Effect.Effect<Schema.Json, ToolSource.Failure>}Since v0.1.0
Options
Section titled “Options”Options (interface)
Section titled “Options (interface)”What toolkit takes besides the toolkit it presents and the runtime.
Signature
export interface Options { /** How long a run may take, which its tool calls count in. A minute by default. */ readonly timeout?: Duration.Input
/** The most characters of JSON a run may return. 50,000 by default. */ readonly maxResultLength?: number}Since v0.1.0
Schemas
Section titled “Schemas”Outcome
Section titled “Outcome”What a run gave back, which is what the model reads of execute when the code finishes.
Signature
declare const Outcome: Schema.Struct<{ readonly result: Schema.Codec<Schema.Json, Schema.Json, never, never> readonly logs: Schema.$Array<Schema.String>}>Since v0.1.0
Outcome (interface)
Section titled “Outcome (interface)”Signature
export interface Outcome extends Schema.Schema.Type<typeof Outcome> {}Since v0.1.0