Files
2026-10-09 01:36:06 -07:00

84 lines
3.1 KiB
TypeScript

import { Effect, Schema } from "effect"
import type { Namespace } from "./namespace.js"
import type { Tools } from "./tools.js"
/**
* JSON Schema subset for model-visible signatures. CodeMode does not validate values against
* these schemas.
*/
export type JsonSchema = {
readonly type?: string | ReadonlyArray<string>
readonly enum?: ReadonlyArray<unknown>
readonly const?: unknown
readonly anyOf?: ReadonlyArray<JsonSchema>
readonly oneOf?: ReadonlyArray<JsonSchema>
readonly allOf?: ReadonlyArray<JsonSchema>
readonly not?: JsonSchema
readonly properties?: Readonly<Record<string, JsonSchema>>
readonly required?: ReadonlyArray<string>
readonly items?: JsonSchema
readonly additionalProperties?: boolean | JsonSchema
readonly description?: string
readonly default?: unknown
readonly format?: string
readonly deprecated?: boolean
readonly minimum?: number
readonly maximum?: number
readonly exclusiveMinimum?: number
readonly exclusiveMaximum?: number
readonly multipleOf?: number
readonly minLength?: number
readonly maxLength?: number
readonly pattern?: string
readonly minItems?: number
readonly maxItems?: number
readonly uniqueItems?: boolean
readonly $ref?: string
readonly $defs?: Readonly<Record<string, JsonSchema>>
readonly definitions?: Readonly<Record<string, JsonSchema>>
}
/** Either a validating Effect Schema or a render-only JSON Schema document. */
export type SchemaType = Schema.Decoder<unknown> | JsonSchema
/** Executable tool exposed through CodeMode's `tools` object. */
export type Tool<R = never> = {
readonly _tag: "CodeModeTool"
readonly description: string
readonly input: SchemaType
readonly output: SchemaType | undefined
readonly execute: (input: unknown) => Effect.Effect<unknown, unknown, R>
}
type InputType<S> = S extends Schema.Decoder<unknown> ? S["Type"] : unknown
type ResultType<S> = S extends undefined ? void : S extends Schema.Decoder<unknown> ? S["Encoded"] : unknown
/** Options for declaring one CodeMode tool. */
export type Options<I extends SchemaType, O extends SchemaType | undefined, R = never> = {
readonly description: string
readonly input: I
readonly output?: O
readonly execute: (input: InputType<I>) => Effect.Effect<ResultType<O>, unknown, R>
}
export const isTool = <R = never>(value: Tool<R> | Namespace<R> | Tools<R> | undefined): value is Tool<R> =>
value !== undefined && Object.hasOwn(value, "_tag") && value._tag === "CodeModeTool"
/**
* Declares one schema-described tool available to a CodeMode program through `tools.*`.
*
* Effect Schemas validate values; JSON Schemas only shape the model-visible signature.
* Without `output`, results are exposed as `void`. Hosts remain responsible for authorization
* and durable side effects.
*/
export const make = <I extends SchemaType, const O extends SchemaType | undefined = undefined, R = never>(
options: Options<I, O, R>,
): Tool<R> => ({
_tag: "CodeModeTool",
description: options.description,
input: options.input,
output: options.output,
execute: (input) => options.execute(input as InputType<I>),
})