Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166import { Effect, Schema } from "effect"import type { Extension } from "./extension.js"import { executeProgram } from "./interpreter/execute.js"import { extensionGlobals } from "./interpreter/extensions.js"import { globalNames } from "./interpreter/globals.js"import { type Services, type ToolDescription, ToolRuntime } from "./tool-runtime.js"import type { Tools } from "./tools.js"
/** A tool call admitted during an execution. */export type { CallResult, ExtensionInvocation, Hooks, ToolCall, ToolDescription, ToolInvocation,} from "./tool-runtime.js"/** Signature-construction helpers for host-owned catalog instructions. */export { searchSignature, toolExpression } from "./tool-runtime.js"
/** Resource budgets enforced independently during each CodeMode program execution. */export type ExecutionLimits = { /** * Wall-clock milliseconds before interruption. Result delivery waits for tool cleanup. * No default: absent means no timeout. */ readonly timeoutMs?: number /** Maximum number of tool calls admitted by the runtime. No default: absent means unlimited. */ readonly maxToolCalls?: number /** * Maximum UTF-8 bytes retained from the result and logs. Warnings have a separate equal budget; * truncation notices and host formatting are additional. */ readonly maxOutputBytes?: number}
export type ResolvedExecutionLimits = { readonly timeoutMs: number | undefined readonly maxToolCalls: number | undefined readonly maxOutputBytes: number | undefined}
/** Configuration shared by `CodeMode.make` and `CodeMode.execute`. */export type Options<Provided extends Record<string, unknown> = {}> = { /** Explicit tools exposed to the program as `tools`. */ tools?: Provided & Tools<Services<Provided>> /** Hooks around every tool and extension call the program makes; see `Hooks`. */ hooks?: ToolRuntime.Hooks<Services<Provided>> /** Host functions exposed as globals; see `Extension.make`. */ extensions?: ReadonlyArray<Extension> /** Resource limits enforced on each execution. */ limits?: ExecutionLimits}
/** Options for one CodeMode execution. */export type ExecuteOptions<Provided extends Record<string, unknown> = {}> = Options<Provided> & { /** Source for one program in the supported JavaScript subset. */ code: string}
/** A JSON value that can cross the confined interpreter boundary. */export type DataValue = Schema.Json
/** Schema for a host tool input containing CodeMode source. */export const Input = Schema.Struct({ code: Schema.String })export type Input = typeof Input.Type
export const DiagnosticKind = Schema.Literals([ "ParseError", "UnsupportedSyntax", "UnknownTool", "InvalidToolInput", "InvalidToolOutput", "InvalidDataValue", "ToolCallLimitExceeded", "TimeoutExceeded", "ToolFailure", "ExecutionFailure", "Truncated",])/** Stable categories produced by program, schema, tool, limit, and truncation diagnostics. */export type DiagnosticKind = typeof DiagnosticKind.Type
export const Diagnostic = Schema.Struct({ kind: DiagnosticKind, message: Schema.String, location: Schema.optionalKey(Schema.Struct({ line: Schema.Number, column: Schema.Number })), suggestions: Schema.optionalKey(Schema.Array(Schema.String)),})/** A normalized program diagnostic safe to return across an agent tool boundary. */export type Diagnostic = typeof Diagnostic.Type
const ToolCallSchema = Schema.Struct({ name: Schema.String })export const Success = Schema.Struct({ ok: Schema.Literal(true), value: Schema.Json, warnings: Schema.optionalKey(Schema.Array(Diagnostic)), logs: Schema.optionalKey(Schema.Array(Schema.String)), truncated: Schema.optionalKey(Schema.Boolean), toolCalls: Schema.Array(ToolCallSchema),})/** Successful execution after the result has crossed the plain-data boundary. */export type Success = typeof Success.Type
export const Failure = Schema.Struct({ ok: Schema.Literal(false), error: Diagnostic, logs: Schema.optionalKey(Schema.Array(Schema.String)), truncated: Schema.optionalKey(Schema.Boolean), toolCalls: Schema.Array(ToolCallSchema),})/** Failed execution with calls admitted before the diagnostic was produced. */export type Failure = typeof Failure.Type
/** Schema for the structured success or diagnostic returned by CodeMode execution. */export const Result = Schema.Union([Success, Failure])/** Result of executing a CodeMode program. Program failures are data, not Effect failures. */export type Result = typeof Result.Type
/** Reusable confined runtime over explicit tools. */export type Runtime<R = never> = { readonly catalog: ReadonlyArray<ToolDescription> readonly execute: (code: string) => Effect.Effect<Result, never, R>}
const validateLimit = (name: keyof ExecutionLimits, value: number | undefined, minimum: number): number | undefined => { if (value !== undefined && (!Number.isSafeInteger(value) || value < minimum)) { throw new RangeError(`${name} must be a safe integer greater than or equal to ${minimum}.`) } return value}
const resolveExecutionLimits = (limits?: ExecutionLimits): ResolvedExecutionLimits => ({ timeoutMs: validateLimit("timeoutMs", limits?.timeoutMs, 1), maxToolCalls: validateLimit("maxToolCalls", limits?.maxToolCalls, 0), maxOutputBytes: validateLimit("maxOutputBytes", limits?.maxOutputBytes, 0),})
/** Executes one Effect-native CodeMode program without constructing a reusable runtime. */export const execute = <const Provided extends Record<string, unknown>>( options: ExecuteOptions<Provided>,): Effect.Effect<Result, never, Services<Provided>> => make(options).execute(options.code)
/** Creates an Effect-native runtime over explicit, schema-described tools. */export const make = <const Provided extends Record<string, unknown> = {}>( options: Options<Provided> = {},): Runtime<Services<Provided>> => { const prepared = ToolRuntime.prepare((options.tools ?? {}) as Tools<Services<Provided>>) const limits = resolveExecutionLimits(options.limits) const extensions = options.extensions ?? [] const bound = new Set(globalNames) for (const extension of extensions) { for (const name of Object.keys(extension.globals)) { if (bound.has(name)) throw new TypeError(`Extension "${extension.name}" global "${name}" is already defined.`) bound.add(name) } } return { get catalog() { return prepared.catalog }, execute: (code) => executeProgram(code, prepared, limits, options.hooks ?? {}, (ctx) => extensionGlobals(ctx, extensions)), }}