refactor(form): model links as fields (#36129)

This commit is contained in:
Aiden Cline 2026-07-10 00:22:01 -05:00 committed by GitHub
commit a6449cb45c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 2409 additions and 1430 deletions

View file

@ -94,10 +94,27 @@ export const MultiselectField = Schema.Struct({
}).annotate({ identifier: "Form.MultiselectField" })
export interface MultiselectField extends Schema.Schema.Type<typeof MultiselectField> {}
export const Field = Schema.Union([StringField, NumberField, IntegerField, BooleanField, MultiselectField]).pipe(
Schema.toTaggedUnion("type"),
)
export type Field = StringField | NumberField | IntegerField | BooleanField | MultiselectField
export const ExternalField = Schema.Struct({
key: Schema.String,
type: Schema.Literal("external"),
url: Schema.String,
title: Schema.String.pipe(optional),
description: Schema.String.pipe(optional),
}).annotate({ identifier: "Form.ExternalField" })
export interface ExternalField extends Schema.Schema.Type<typeof ExternalField> {}
export const Field = Schema.Union([
StringField,
NumberField,
IntegerField,
BooleanField,
MultiselectField,
ExternalField,
]).pipe(Schema.toTaggedUnion("type"), Schema.annotate({ identifier: "Form.Field" }))
export type Field = StringField | NumberField | IntegerField | BooleanField | MultiselectField | ExternalField
export const Fields = Schema.NonEmptyArray(Field).annotate({ identifier: "Form.Fields" })
export type Fields = typeof Fields.Type
const InfoBase = {
id: ID,
@ -110,22 +127,11 @@ const InfoBase = {
metadata: Metadata.pipe(optional),
}
export const FormInfo = Schema.Struct({
export const Info = Schema.Struct({
...InfoBase,
mode: Schema.Literal("form"),
fields: Schema.Array(Field),
}).annotate({ identifier: "Form.FormInfo" })
export interface FormInfo extends Schema.Schema.Type<typeof FormInfo> {}
export const UrlInfo = Schema.Struct({
...InfoBase,
mode: Schema.Literal("url"),
url: Schema.String,
}).annotate({ identifier: "Form.UrlInfo" })
export interface UrlInfo extends Schema.Schema.Type<typeof UrlInfo> {}
export const Info = Schema.Union([FormInfo, UrlInfo]).pipe(Schema.toTaggedUnion("mode"))
export type Info = FormInfo | UrlInfo
fields: Fields,
}).annotate({ identifier: "Form.Info" })
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Value = Schema.Union([Schema.String, Schema.Number, Schema.Boolean, Schema.Array(Schema.String)]).annotate(
{

View file

@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test"
import { DateTime, Schema } from "effect"
import { Agent } from "../src/agent.js"
import { FileSystem } from "../src/filesystem.js"
import { Form } from "../src/form.js"
import { Mcp } from "../src/mcp.js"
import { Model } from "../src/model.js"
import { Project } from "../src/project.js"
@ -47,6 +48,33 @@ describe("contract hygiene", () => {
).toEqual({ text: "completed" })
})
test("forms require at least one field", () => {
expect(() =>
Schema.decodeUnknownSync(Form.Info)({
id: Form.ID.create(),
sessionID: "global",
title: "Empty form",
fields: [],
}),
).toThrow()
expect(
Schema.decodeUnknownSync(Form.Info)({
id: Form.ID.create(),
sessionID: "global",
title: "External form",
fields: [{ key: "authorization", type: "external", url: "https://example.com" }],
}).fields,
).toHaveLength(1)
expect(() =>
Schema.decodeUnknownSync(Form.Info)({
id: Form.ID.create(),
sessionID: "global",
title: "External form",
fields: [{ type: "external", url: "https://example.com" }],
}),
).toThrow()
})
test("model defaults and provider overlays preserve public invariants", () => {
const id = Model.ID.make("model")
expect(Model.Info.empty(Provider.ID.make("provider"), id)).toMatchObject({ modelID: id, variants: [] })
@ -69,6 +97,10 @@ describe("contract hygiene", () => {
const identifiers = [
Agent.Color,
FileSystem.Submatch,
Form.Field,
Form.Fields,
Form.Info,
Form.ExternalField,
Mcp.Resource,
Mcp.ResourceTemplate,
Mcp.ResourceCatalog,