MusePi

musepi-type Guide (schema authoring in this repo)

Internal schemas use @musepi/musepi-type — an ArkType-compatible validator with a lazy JIT runtime (packages/omptype — source directory keeps its historical name). Author types with import { type } from "@musepi/musepi-type".

Why musepi-type (perf contract)

The detection contract (don’t break it)

packages/ai/src/utils/schema/wire.ts distinguishes two schema kinds:

At the provider boundary, toolWireSchema() calls toJsonSchema(), prunes T | undefined branches, and closes declared objects with additionalProperties: false. Predicates (.narrow) and morphs (.pipe) validate locally but degrade to their base schema on the wire.

Definition language (arktype-compatible subset)

Construct Form
Primitives "string", "number", "boolean", "null", "undefined", "unknown", "object", "bigint"
Integer "number.integer"
URL string "string.url"
Literals "'x'", "5", "true"
Unions "'a' \| 'b'", "string \| null"
Arrays "string[]", "(string \| number)[]", [def, "[]"]
Bounds "number >= 0", "0 < number <= 3600", "1 <= string <= 10"
Optional key { "limit?": "number" } or value-suffix { limit: "number?" }
Defaults { count: "number = 10" }, type("string[]").default(() => [])
Undeclared keys "+": "reject" (fail) / "+": "delete" (strip) / default keep
Records { "[string]": "number" } — NOT "Record<string, number>"
Runtime enums type.enumerated(...RUNTIME_ARRAY)
Runtime-built object defs type.raw({...}) (returns BaseType)
Keyword statics type.number.atLeast(5).atMost(300), type.string

Validating (same as arktype)

import { type } from "@musepi/musepi-type";
const out = schema(value);
if (out instanceof type.errors) {
  // out.summary → human message; entries have .path (array) and .problem
  throw new Error(out.summary);
}
// `out` is the validated/morphed value (defaults filled, extras stripped)

Methods

.describe(d), .default(v | () => v), .or(TypeOrStringDef), .and(Type), .array(), .atLeastLength(n) / .atMostLength(n) (string/array), .atLeast(n) / .atMost(n) (number), .pipe(fn), .narrow(fn) (with ctx.mustBe("...")), .allows(v), .assert(v), .toJsonSchema().

Note on .or() typing: schema and string operands infer precisely; object-literal operands degrade — wrap them with type({...}) first.

Adapters

TypeBox-style and Zod-style authoring are backed by the musepi-type runtime:

import { Type, type Static } from "@musepi/musepi-type/typebox";
import { z } from "@musepi/musepi-type/zod";

const User = z.object({ name: z.string() });
type User = z.infer<typeof User>;

These produce real musepi-type schemas with JIT validation and toJsonSchema. Internal code authors the string DSL directly.