refactor(schema): extract shared public schemas (#33571)

This commit is contained in:
Kit Langton
2026-06-24 04:31:06 +02:00
committed by GitHub
parent 60aba622ab
commit 516cfe4e09
130 changed files with 2004 additions and 1583 deletions

View File

@@ -0,0 +1,22 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/schema",
"private": true,
"type": "module",
"license": "MIT",
"exports": {
".": "./src/index.ts",
"./*": "./src/*.ts"
},
"scripts": {
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"effect": "catalog:"
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@typescript/native-preview": "catalog:"
}
}

View File

@@ -0,0 +1,37 @@
export * as Agent from "./agent"
import { Schema } from "effect"
import { Model } from "./model"
import { Permission } from "./permission"
import { Provider } from "./provider"
import { PositiveInt, withStatics } from "./schema"
export const ID = Schema.String.pipe(Schema.brand("AgentV2.ID"))
export type ID = typeof ID.Type
export const Color = Schema.Union([
Schema.String.check(Schema.isPattern(/^#[0-9a-fA-F]{6}$/)),
Schema.Literals(["primary", "secondary", "accent", "success", "warning", "error", "info"]),
])
export type Color = typeof Color.Type
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
id: ID,
model: Model.Ref.pipe(Schema.optional),
request: Provider.Request,
system: Schema.String.pipe(Schema.optional),
description: Schema.String.pipe(Schema.optional),
mode: Schema.Literals(["subagent", "primary", "all"]),
hidden: Schema.Boolean,
color: Color.pipe(Schema.optional),
steps: PositiveInt.pipe(Schema.optional),
permissions: Permission.Ruleset,
})
.annotate({ identifier: "AgentV2.Info" })
.pipe(
withStatics((schema) => ({
empty: (id: ID) =>
schema.make({ id, request: { headers: {}, body: {} }, mode: "all", hidden: false, permissions: [] }),
})),
)

View File

@@ -0,0 +1,14 @@
export * as Command from "./command"
import { Schema } from "effect"
import { Model } from "./model"
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
name: Schema.String,
template: Schema.String,
description: Schema.String.pipe(Schema.optional),
agent: Schema.String.pipe(Schema.optional),
model: Model.Ref.pipe(Schema.optional),
subtask: Schema.Boolean.pipe(Schema.optional),
}).annotate({ identifier: "CommandV2.Info" })

View File

@@ -0,0 +1,22 @@
export * as Connection from "./connection"
import { Schema } from "effect"
import { Credential } from "./credential"
export interface CredentialInfo extends Schema.Schema.Type<typeof CredentialInfo> {}
export const CredentialInfo = Schema.Struct({
type: Schema.Literal("credential"),
id: Credential.ID,
label: Schema.String,
}).annotate({ identifier: "Connection.CredentialInfo" })
export interface EnvInfo extends Schema.Schema.Type<typeof EnvInfo> {}
export const EnvInfo = Schema.Struct({
type: Schema.Literal("env"),
name: Schema.String,
}).annotate({ identifier: "Connection.EnvInfo" })
export const Info = Schema.Union([CredentialInfo, EnvInfo])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "Connection.Info" })
export type Info = typeof Info.Type

View File

@@ -0,0 +1,34 @@
export * as Credential from "./credential"
import { Schema } from "effect"
import { Integration } from "./integration"
import { ascending } from "./identifier"
import { NonNegativeInt, withStatics } from "./schema"
export const ID = Schema.String.pipe(
Schema.brand("Credential.ID"),
withStatics((schema) => ({ create: () => schema.make("cred_" + ascending()) })),
)
export type ID = typeof ID.Type
export interface OAuth extends Schema.Schema.Type<typeof OAuth> {}
export const OAuth = Schema.Struct({
type: Schema.Literal("oauth"),
methodID: Integration.MethodID,
refresh: Schema.String,
access: Schema.String,
expires: NonNegativeInt,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "Credential.OAuth" })
export interface Key extends Schema.Schema.Type<typeof Key> {}
export const Key = Schema.Struct({
type: Schema.Literal("key"),
key: Schema.String,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "Credential.Key" })
export const Value = Schema.Union([OAuth, Key])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "Credential.Value" })
export type Value = Schema.Schema.Type<typeof Value>

View File

@@ -0,0 +1,26 @@
export * as FileSystem from "./filesystem"
import { Schema } from "effect"
import { NonNegativeInt, PositiveInt, RelativePath } from "./schema"
export interface Entry extends Schema.Schema.Type<typeof Entry> {}
export const Entry = Schema.Struct({
path: RelativePath,
type: Schema.Literals(["file", "directory"]),
}).annotate({ identifier: "FileSystem.Entry" })
export interface Submatch extends Schema.Schema.Type<typeof Submatch> {}
export const Submatch = Schema.Struct({
text: Schema.String,
start: NonNegativeInt,
end: NonNegativeInt,
})
export interface Match extends Schema.Schema.Type<typeof Match> {}
export const Match = Schema.Struct({
entry: Entry,
line: PositiveInt,
offset: NonNegativeInt,
text: Schema.String,
submatches: Schema.Array(Submatch),
}).annotate({ identifier: "FileSystem.Match" })

View File

@@ -0,0 +1,30 @@
const length = 26
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
let lastTimestamp = 0
let counter = 0
export function ascending() {
return create(false)
}
export function descending() {
return create(true)
}
export function create(descending: boolean, timestamp = Date.now()) {
if (timestamp !== lastTimestamp) {
lastTimestamp = timestamp
counter = 0
}
counter++
const current = BigInt(timestamp) * 0x1000n + BigInt(counter)
const value = descending ? ~current : current
const time = Array.from({ length: 6 }, (_, index) =>
Number((value >> BigInt(40 - 8 * index)) & 0xffn)
.toString(16)
.padStart(2, "0"),
).join("")
const bytes = crypto.getRandomValues(new Uint8Array(length - 12))
return time + Array.from(bytes, (byte) => chars[byte % 62]).join("")
}

View File

@@ -0,0 +1,21 @@
export { Agent } from "./agent"
export { Command } from "./command"
export { Connection } from "./connection"
export { Credential } from "./credential"
export { FileSystem } from "./filesystem"
export { Integration } from "./integration"
export { LLM } from "./llm"
export { Location } from "./location"
export { Model } from "./model"
export { ModelRequest } from "./model-request"
export { Permission } from "./permission"
export { Project } from "./project"
export { Provider } from "./provider"
export { Reference } from "./reference"
export { Session } from "./session"
export { SessionInput } from "./session-input"
export { SessionMessage } from "./session-message"
export { Skill } from "./skill"
export { Workspace } from "./workspace"
export { Prompt, Source, FileAttachment, AgentAttachment } from "./prompt"
export * from "./schema"

View File

@@ -0,0 +1,79 @@
export * as Integration from "./integration"
import { Schema } from "effect"
export const ID = Schema.String.pipe(Schema.brand("Integration.ID"))
export type ID = typeof ID.Type
export const MethodID = Schema.String.pipe(Schema.brand("Integration.MethodID"))
export type MethodID = typeof MethodID.Type
export interface When extends Schema.Schema.Type<typeof When> {}
export const When = Schema.Struct({
key: Schema.String,
op: Schema.Literals(["eq", "neq"]),
value: Schema.String,
}).annotate({ identifier: "Integration.When" })
export interface TextPrompt extends Schema.Schema.Type<typeof TextPrompt> {}
export const TextPrompt = Schema.Struct({
type: Schema.Literal("text"),
key: Schema.String,
message: Schema.String,
placeholder: Schema.optional(Schema.String),
when: Schema.optional(When),
}).annotate({ identifier: "Integration.TextPrompt" })
export interface SelectPrompt extends Schema.Schema.Type<typeof SelectPrompt> {}
export const SelectPrompt = Schema.Struct({
type: Schema.Literal("select"),
key: Schema.String,
message: Schema.String,
options: Schema.mutable(
Schema.Array(
Schema.Struct({
label: Schema.String,
value: Schema.String,
hint: Schema.optional(Schema.String),
}),
),
),
when: Schema.optional(When),
}).annotate({ identifier: "Integration.SelectPrompt" })
export const Prompt = Schema.Union([TextPrompt, SelectPrompt]).pipe(Schema.toTaggedUnion("type"))
export type Prompt = typeof Prompt.Type
export interface OAuthMethod extends Schema.Schema.Type<typeof OAuthMethod> {}
export const OAuthMethod = Schema.Struct({
id: MethodID,
type: Schema.Literal("oauth"),
label: Schema.String,
prompts: Schema.optional(Schema.mutable(Schema.Array(Prompt))),
}).annotate({ identifier: "Integration.OAuthMethod" })
export interface KeyMethod extends Schema.Schema.Type<typeof KeyMethod> {}
export const KeyMethod = Schema.Struct({
type: Schema.Literal("key"),
label: Schema.optional(Schema.String),
}).annotate({ identifier: "Integration.KeyMethod" })
export interface EnvMethod extends Schema.Schema.Type<typeof EnvMethod> {}
export const EnvMethod = Schema.Struct({
type: Schema.Literal("env"),
names: Schema.mutable(Schema.Array(Schema.String)),
}).annotate({ identifier: "Integration.EnvMethod" })
export const Method = Schema.Union([OAuthMethod, KeyMethod, EnvMethod])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "Integration.Method" })
export type Method = typeof Method.Type
export const Inputs = Schema.Record(Schema.String, Schema.String).annotate({ identifier: "Integration.Inputs" })
export type Inputs = typeof Inputs.Type
export interface Ref extends Schema.Schema.Type<typeof Ref> {}
export const Ref = Schema.Struct({
id: ID,
name: Schema.String,
}).annotate({ identifier: "Integration.Ref" })

View File

@@ -0,0 +1,23 @@
export * as LLM from "./llm"
import { Schema } from "effect"
export const ProviderMetadata = Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown))
export type ProviderMetadata = Schema.Schema.Type<typeof ProviderMetadata>
export interface ToolTextContent extends Schema.Schema.Type<typeof ToolTextContent> {}
export const ToolTextContent = Schema.Struct({
type: Schema.Literal("text"),
text: Schema.String,
}).annotate({ identifier: "Tool.TextContent" })
export interface ToolFileContent extends Schema.Schema.Type<typeof ToolFileContent> {}
export const ToolFileContent = Schema.Struct({
type: Schema.Literal("file"),
uri: Schema.String,
mime: Schema.String,
name: Schema.optional(Schema.String),
}).annotate({ identifier: "Tool.FileContent" })
export const ToolContent = Schema.Union([ToolTextContent, ToolFileContent]).pipe(Schema.toTaggedUnion("type"))
export type ToolContent = Schema.Schema.Type<typeof ToolContent>

View File

@@ -0,0 +1,14 @@
export * as Location from "./location"
import { Effect, Schema } from "effect"
import { AbsolutePath } from "./schema"
import { Workspace } from "./workspace"
export interface Ref extends Schema.Schema.Type<typeof Ref> {}
export const Ref = Schema.Struct({
directory: AbsolutePath,
workspaceID: Schema.optional(Workspace.ID).pipe(
Schema.withDecodingDefault(Effect.succeed(undefined)),
Schema.withConstructorDefault(Effect.succeed(undefined)),
),
}).annotate({ identifier: "Location.Ref" })

View File

@@ -0,0 +1,31 @@
export * as ModelRequest from "./model-request"
import { Effect, Schema } from "effect"
import { Provider } from "./provider"
export interface Generation extends Schema.Schema.Type<typeof Generation> {}
export const Generation = Schema.Struct({
maxTokens: Schema.Number.pipe(Schema.optional),
temperature: Schema.Number.pipe(Schema.optional),
topP: Schema.Number.pipe(Schema.optional),
topK: Schema.Number.pipe(Schema.optional),
frequencyPenalty: Schema.Number.pipe(Schema.optional),
presencePenalty: Schema.Number.pipe(Schema.optional),
seed: Schema.Number.pipe(Schema.optional),
stop: Schema.String.pipe(Schema.Array, Schema.mutable, Schema.optional),
})
export interface Request extends Schema.Schema.Type<typeof Request> {}
export const Request = Schema.Struct({
...Provider.Request.fields,
generation: Generation.pipe(
Schema.optionalKey,
Schema.withConstructorDefault(Effect.succeed({})),
Schema.withDecodingDefaultKey(Effect.succeed({})),
),
options: Schema.Record(Schema.String, Schema.Any).pipe(
Schema.optionalKey,
Schema.withConstructorDefault(Effect.succeed({})),
Schema.withDecodingDefaultKey(Effect.succeed({})),
),
})

View File

@@ -0,0 +1,104 @@
export * as Model from "./model"
import { Schema } from "effect"
import { ModelRequest } from "./model-request"
import { Provider } from "./provider"
import { withStatics } from "./schema"
export const ID = Schema.String.pipe(Schema.brand("ModelV2.ID"))
export type ID = typeof ID.Type
export const VariantID = Schema.String.pipe(Schema.brand("VariantID"))
export type VariantID = typeof VariantID.Type
export const Ref = Schema.Struct({
id: ID,
providerID: Provider.ID,
variant: VariantID.pipe(Schema.optional),
})
export type Ref = typeof Ref.Type
export const Family = Schema.String.pipe(Schema.brand("Family"))
export type Family = typeof Family.Type
export interface Capabilities extends Schema.Schema.Type<typeof Capabilities> {}
export const Capabilities = Schema.Struct({
tools: Schema.Boolean,
input: Schema.String.pipe(Schema.Array, Schema.mutable),
output: Schema.String.pipe(Schema.Array, Schema.mutable),
})
export interface Cost extends Schema.Schema.Type<typeof Cost> {}
export const Cost = Schema.Struct({
tier: Schema.Struct({
type: Schema.Literal("context"),
size: Schema.Int,
}).pipe(Schema.optional),
input: Schema.Finite,
output: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
})
export const Api = Schema.Union([
Schema.Struct({
id: ID,
...Provider.AISDK.fields,
}),
Schema.Struct({
id: ID,
...Provider.Native.fields,
}),
]).pipe(Schema.toTaggedUnion("type"))
export type Api = typeof Api.Type
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
id: ID,
providerID: Provider.ID,
family: Family.pipe(Schema.optional),
name: Schema.String,
api: Api,
capabilities: Capabilities,
request: Schema.Struct({
...ModelRequest.Request.fields,
variant: Schema.String.pipe(Schema.optional),
}),
variants: Schema.Struct({
id: VariantID,
...ModelRequest.Request.fields,
}).pipe(Schema.Array, Schema.mutable),
time: Schema.Struct({
released: Schema.Finite,
}),
cost: Cost.pipe(Schema.Array, Schema.mutable),
status: Schema.Literals(["alpha", "beta", "deprecated", "active"]),
enabled: Schema.Boolean,
limit: Schema.Struct({
context: Schema.Int,
input: Schema.Int.pipe(Schema.optional),
output: Schema.Int,
}),
})
.annotate({ identifier: "ModelV2.Info" })
.pipe(
withStatics((schema) => ({
empty: (providerID: Provider.ID, modelID: ID) =>
schema.make({
id: modelID,
providerID,
name: modelID,
api: { id: modelID, type: "native", settings: {} },
capabilities: { tools: false, input: [], output: [] },
request: { headers: {}, body: {}, generation: {}, options: {} },
variants: [],
time: { released: 0 },
cost: [],
status: "active",
enabled: true,
limit: { context: 0, output: 0 },
}),
})),
)

View File

@@ -0,0 +1,16 @@
export * as Permission from "./permission"
import { Schema } from "effect"
export const Effect = Schema.Literals(["allow", "deny", "ask"]).annotate({ identifier: "PermissionV2.Effect" })
export type Effect = typeof Effect.Type
export interface Rule extends Schema.Schema.Type<typeof Rule> {}
export const Rule = Schema.Struct({
action: Schema.String,
resource: Schema.String,
effect: Effect,
}).annotate({ identifier: "PermissionV2.Rule" })
export const Ruleset = Schema.mutable(Schema.Array(Rule)).annotate({ identifier: "PermissionV2.Ruleset" })
export type Ruleset = typeof Ruleset.Type

View File

@@ -0,0 +1,10 @@
export * as Project from "./project"
import { Schema } from "effect"
import { withStatics } from "./schema"
export const ID = Schema.String.pipe(
Schema.brand("Project.ID"),
withStatics((schema) => ({ global: schema.make("global") })),
)
export type ID = typeof ID.Type

View File

@@ -0,0 +1,56 @@
import { Schema } from "effect"
import { withStatics } from "./schema"
export interface Source extends Schema.Schema.Type<typeof Source> {}
export const Source = Schema.Struct({
start: Schema.Finite,
end: Schema.Finite,
text: Schema.String,
}).annotate({ identifier: "Prompt.Source" })
export interface FileAttachment extends Schema.Schema.Type<typeof FileAttachment> {}
export const FileAttachment = Schema.Struct({
uri: Schema.String,
mime: Schema.String,
name: Schema.String.pipe(Schema.optional),
description: Schema.String.pipe(Schema.optional),
source: Source.pipe(Schema.optional),
})
.annotate({ identifier: "Prompt.FileAttachment" })
.pipe(
withStatics((schema) => ({
create: (input: FileAttachment) =>
schema.make({
uri: input.uri,
mime: input.mime,
name: input.name,
description: input.description,
source: input.source,
}),
})),
)
export interface AgentAttachment extends Schema.Schema.Type<typeof AgentAttachment> {}
export const AgentAttachment = Schema.Struct({
name: Schema.String,
source: Source.pipe(Schema.optional),
}).annotate({ identifier: "Prompt.AgentAttachment" })
export interface Prompt extends Schema.Schema.Type<typeof Prompt> {}
export const Prompt = Schema.Struct({
text: Schema.String,
files: Schema.Array(FileAttachment).pipe(Schema.optional),
agents: Schema.Array(AgentAttachment).pipe(Schema.optional),
})
.annotate({ identifier: "Prompt" })
.pipe(
withStatics((schema) => ({
equivalence: Schema.toEquivalence(schema),
fromUserMessage: (input: Pick<Prompt, "text" | "files" | "agents">) =>
schema.make({
text: input.text,
...(input.files === undefined ? {} : { files: input.files }),
...(input.agents === undefined ? {} : { agents: input.agents }),
}),
})),
)

View File

@@ -0,0 +1,69 @@
export * as Provider from "./provider"
import { Schema } from "effect"
import { Integration } from "./integration"
import { withStatics } from "./schema"
export const ID = Schema.String.pipe(
Schema.brand("ProviderV2.ID"),
withStatics((schema) => ({
opencode: schema.make("opencode"),
anthropic: schema.make("anthropic"),
openai: schema.make("openai"),
google: schema.make("google"),
googleVertex: schema.make("google-vertex"),
githubCopilot: schema.make("github-copilot"),
amazonBedrock: schema.make("amazon-bedrock"),
azure: schema.make("azure"),
openrouter: schema.make("openrouter"),
mistral: schema.make("mistral"),
gitlab: schema.make("gitlab"),
})),
)
export type ID = typeof ID.Type
export interface AISDK extends Schema.Schema.Type<typeof AISDK> {}
export const AISDK = Schema.Struct({
type: Schema.Literal("aisdk"),
package: Schema.String,
url: Schema.String.pipe(Schema.optional),
settings: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
})
export interface Native extends Schema.Schema.Type<typeof Native> {}
export const Native = Schema.Struct({
type: Schema.Literal("native"),
url: Schema.String.pipe(Schema.optional),
settings: Schema.Record(Schema.String, Schema.Unknown),
})
export const Api = Schema.Union([AISDK, Native]).pipe(Schema.toTaggedUnion("type"))
export type Api = typeof Api.Type
export interface Request extends Schema.Schema.Type<typeof Request> {}
export const Request = Schema.Struct({
headers: Schema.Record(Schema.String, Schema.String),
body: Schema.Record(Schema.String, Schema.Any),
})
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
id: ID,
integrationID: Integration.ID.pipe(Schema.optional),
name: Schema.String,
disabled: Schema.Boolean.pipe(Schema.optional),
api: Api,
request: Request,
})
.annotate({ identifier: "ProviderV2.Info" })
.pipe(
withStatics((schema) => ({
empty: (id: ID) =>
schema.make({
id,
name: id,
api: { type: "native", settings: {} },
request: { headers: {}, body: {} },
}),
})),
)

View File

@@ -0,0 +1,24 @@
export * as Reference from "./reference"
import { Schema } from "effect"
import { AbsolutePath } from "./schema"
export interface LocalSource extends Schema.Schema.Type<typeof LocalSource> {}
export const LocalSource = Schema.Struct({
type: Schema.Literal("local"),
path: AbsolutePath,
description: Schema.String.pipe(Schema.optional),
hidden: Schema.Boolean.pipe(Schema.optional),
}).annotate({ identifier: "Reference.LocalSource" })
export interface GitSource extends Schema.Schema.Type<typeof GitSource> {}
export const GitSource = Schema.Struct({
type: Schema.Literal("git"),
repository: Schema.String,
branch: Schema.String.pipe(Schema.optional),
description: Schema.String.pipe(Schema.optional),
hidden: Schema.Boolean.pipe(Schema.optional),
}).annotate({ identifier: "Reference.GitSource" })
export const Source = Schema.Union([LocalSource, GitSource]).pipe(Schema.toTaggedUnion("type"))
export type Source = typeof Source.Type

View File

@@ -0,0 +1,30 @@
import { DateTime, Option, Schema, SchemaGetter } from "effect"
export const PositiveInt = Schema.Int.check(Schema.isGreaterThan(0))
export const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
export const RelativePath = Schema.String.pipe(Schema.brand("RelativePath"))
export type RelativePath = typeof RelativePath.Type
export const AbsolutePath = Schema.String.pipe(Schema.brand("AbsolutePath"))
export type AbsolutePath = typeof AbsolutePath.Type
export const optionalOmitUndefined = <S extends Schema.Top>(schema: S) =>
Schema.optionalKey(schema).pipe(
Schema.decodeTo(Schema.optional(schema), {
decode: SchemaGetter.passthrough({ strict: false }),
encode: SchemaGetter.transformOptional(Option.filter((value) => value !== undefined)),
}),
)
export const withStatics =
<S extends object, M extends Record<string, unknown>>(methods: (schema: S) => M) =>
(schema: S): S & M =>
Object.assign(schema, methods(schema))
export const DateTimeUtcFromMillis = Schema.Finite.pipe(
Schema.decodeTo(Schema.DateTimeUtc, {
decode: SchemaGetter.transform((value) => DateTime.makeUnsafe(value)),
encode: SchemaGetter.transform((value) => DateTime.toEpochMillis(value)),
}),
)

View File

@@ -0,0 +1,6 @@
export * as SessionDelivery from "./session-delivery"
import { Schema } from "effect"
export const Delivery = Schema.Literals(["steer", "queue"])
export type Delivery = typeof Delivery.Type

View File

@@ -0,0 +1,17 @@
export * as SessionID from "./session-id"
import { Schema } from "effect"
import { descending } from "./identifier"
import { withStatics } from "./schema"
export const ID = Schema.String.check(Schema.isStartsWith("ses")).pipe(
Schema.brand("SessionID"),
withStatics((schema) => {
const create = () => schema.make("ses_" + descending())
return {
create,
descending: (id?: string) => (id === undefined ? create() : schema.make(id)),
}
}),
)
export type ID = typeof ID.Type

View File

@@ -0,0 +1,22 @@
export * as SessionInput from "./session-input"
import { Schema } from "effect"
import { Prompt } from "./prompt"
import { DateTimeUtcFromMillis, NonNegativeInt } from "./schema"
import { SessionDelivery } from "./session-delivery"
import { SessionID } from "./session-id"
import { SessionMessageID } from "./session-message-id"
export const Delivery = SessionDelivery.Delivery
export type Delivery = SessionDelivery.Delivery
export interface Admitted extends Schema.Schema.Type<typeof Admitted> {}
export const Admitted = Schema.Struct({
admittedSeq: NonNegativeInt,
id: SessionMessageID.ID,
sessionID: SessionID.ID,
prompt: Prompt,
delivery: Delivery,
timeCreated: DateTimeUtcFromMillis,
promotedSeq: NonNegativeInt.pipe(Schema.optional),
}).annotate({ identifier: "SessionInput.Admitted" })

View File

@@ -0,0 +1,11 @@
export * as SessionMessageID from "./session-message-id"
import { Schema } from "effect"
import { ascending } from "./identifier"
import { withStatics } from "./schema"
export const ID = Schema.String.check(Schema.isStartsWith("msg_")).pipe(
Schema.brand("Session.Message.ID"),
withStatics((schema) => ({ create: () => schema.make("msg_" + ascending()) })),
)
export type ID = typeof ID.Type

View File

@@ -0,0 +1,204 @@
export * as SessionMessage from "./session-message"
import { Schema } from "effect"
import { ProviderMetadata, ToolContent } from "./llm"
import { Model } from "./model"
import { FileAttachment, Prompt } from "./prompt"
import { DateTimeUtcFromMillis } from "./schema"
import { SessionID } from "./session-id"
import { SessionMessageID } from "./session-message-id"
export const ID = SessionMessageID.ID
export type ID = SessionMessageID.ID
export interface UnknownError extends Schema.Schema.Type<typeof UnknownError> {}
export const UnknownError = Schema.Struct({
type: Schema.Literal("unknown"),
message: Schema.String,
}).annotate({ identifier: "Session.Error.Unknown" })
const Base = {
id: ID,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
time: Schema.Struct({ created: DateTimeUtcFromMillis }),
}
export interface AgentSwitched extends Schema.Schema.Type<typeof AgentSwitched> {}
export const AgentSwitched = Schema.Struct({
...Base,
type: Schema.Literal("agent-switched"),
agent: Schema.String,
}).annotate({ identifier: "Session.Message.AgentSwitched" })
export interface ModelSwitched extends Schema.Schema.Type<typeof ModelSwitched> {}
export const ModelSwitched = Schema.Struct({
...Base,
type: Schema.Literal("model-switched"),
model: Model.Ref,
}).annotate({ identifier: "Session.Message.ModelSwitched" })
export interface User extends Schema.Schema.Type<typeof User> {}
export const User = Schema.Struct({
...Base,
text: Prompt.fields.text,
files: Prompt.fields.files,
agents: Prompt.fields.agents,
type: Schema.Literal("user"),
}).annotate({ identifier: "Session.Message.User" })
export interface Synthetic extends Schema.Schema.Type<typeof Synthetic> {}
export const Synthetic = Schema.Struct({
...Base,
sessionID: SessionID.ID,
text: Schema.String,
type: Schema.Literal("synthetic"),
}).annotate({ identifier: "Session.Message.Synthetic" })
export interface System extends Schema.Schema.Type<typeof System> {}
export const System = Schema.Struct({
...Base,
type: Schema.Literal("system"),
text: Schema.String,
}).annotate({ identifier: "Session.Message.System" })
export interface Shell extends Schema.Schema.Type<typeof Shell> {}
export const Shell = Schema.Struct({
...Base,
type: Schema.Literal("shell"),
callID: Schema.String,
command: Schema.String,
output: Schema.String,
time: Schema.Struct({
created: DateTimeUtcFromMillis,
completed: DateTimeUtcFromMillis.pipe(Schema.optional),
}),
}).annotate({ identifier: "Session.Message.Shell" })
export interface ToolStatePending extends Schema.Schema.Type<typeof ToolStatePending> {}
export const ToolStatePending = Schema.Struct({
status: Schema.Literal("pending"),
input: Schema.String,
}).annotate({ identifier: "Session.Message.ToolState.Pending" })
export interface ToolStateRunning extends Schema.Schema.Type<typeof ToolStateRunning> {}
export const ToolStateRunning = Schema.Struct({
status: Schema.Literal("running"),
input: Schema.Record(Schema.String, Schema.Unknown),
structured: Schema.Record(Schema.String, Schema.Any),
content: ToolContent.pipe(Schema.Array),
}).annotate({ identifier: "Session.Message.ToolState.Running" })
export interface ToolStateCompleted extends Schema.Schema.Type<typeof ToolStateCompleted> {}
export const ToolStateCompleted = Schema.Struct({
status: Schema.Literal("completed"),
input: Schema.Record(Schema.String, Schema.Unknown),
attachments: FileAttachment.pipe(Schema.Array, Schema.optional),
content: ToolContent.pipe(Schema.Array),
outputPaths: Schema.Array(Schema.String).pipe(Schema.optional),
structured: Schema.Record(Schema.String, Schema.Any),
result: Schema.Unknown.pipe(Schema.optional),
}).annotate({ identifier: "Session.Message.ToolState.Completed" })
export interface ToolStateError extends Schema.Schema.Type<typeof ToolStateError> {}
export const ToolStateError = Schema.Struct({
status: Schema.Literal("error"),
input: Schema.Record(Schema.String, Schema.Unknown),
content: ToolContent.pipe(Schema.Array),
structured: Schema.Record(Schema.String, Schema.Any),
error: UnknownError,
result: Schema.Unknown.pipe(Schema.optional),
}).annotate({ identifier: "Session.Message.ToolState.Error" })
export const ToolState = Schema.Union([ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError]).pipe(
Schema.toTaggedUnion("status"),
)
export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError
export interface AssistantTool extends Schema.Schema.Type<typeof AssistantTool> {}
export const AssistantTool = Schema.Struct({
type: Schema.Literal("tool"),
id: Schema.String,
name: Schema.String,
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: ProviderMetadata.pipe(Schema.optional),
resultMetadata: ProviderMetadata.pipe(Schema.optional),
}).pipe(Schema.optional),
state: ToolState,
time: Schema.Struct({
created: DateTimeUtcFromMillis,
ran: DateTimeUtcFromMillis.pipe(Schema.optional),
completed: DateTimeUtcFromMillis.pipe(Schema.optional),
pruned: DateTimeUtcFromMillis.pipe(Schema.optional),
}),
}).annotate({ identifier: "Session.Message.Assistant.Tool" })
export interface AssistantText extends Schema.Schema.Type<typeof AssistantText> {}
export const AssistantText = Schema.Struct({
type: Schema.Literal("text"),
id: Schema.String,
text: Schema.String,
}).annotate({ identifier: "Session.Message.Assistant.Text" })
export interface AssistantReasoning extends Schema.Schema.Type<typeof AssistantReasoning> {}
export const AssistantReasoning = Schema.Struct({
type: Schema.Literal("reasoning"),
id: Schema.String,
text: Schema.String,
providerMetadata: ProviderMetadata.pipe(Schema.optional),
}).annotate({ identifier: "Session.Message.Assistant.Reasoning" })
export const AssistantContent = Schema.Union([AssistantText, AssistantReasoning, AssistantTool]).pipe(
Schema.toTaggedUnion("type"),
)
export type AssistantContent = AssistantText | AssistantReasoning | AssistantTool
export interface Assistant extends Schema.Schema.Type<typeof Assistant> {}
export const Assistant = Schema.Struct({
...Base,
type: Schema.Literal("assistant"),
agent: Schema.String,
model: Model.Ref,
content: AssistantContent.pipe(Schema.Array),
snapshot: Schema.Struct({
start: Schema.String.pipe(Schema.optional),
end: Schema.String.pipe(Schema.optional),
}).pipe(Schema.optional),
finish: Schema.String.pipe(Schema.optional),
cost: Schema.Finite.pipe(Schema.optional),
tokens: Schema.Struct({
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({ read: Schema.Finite, write: Schema.Finite }),
}).pipe(Schema.optional),
error: UnknownError.pipe(Schema.optional),
time: Schema.Struct({
created: DateTimeUtcFromMillis,
completed: DateTimeUtcFromMillis.pipe(Schema.optional),
}),
}).annotate({ identifier: "Session.Message.Assistant" })
export interface Compaction extends Schema.Schema.Type<typeof Compaction> {}
export const Compaction = Schema.Struct({
type: Schema.Literal("compaction"),
reason: Schema.Literals(["auto", "manual"]),
summary: Schema.String,
recent: Schema.String,
...Base,
}).annotate({ identifier: "Session.Message.Compaction" })
export const Message = Schema.Union([
AgentSwitched,
ModelSwitched,
User,
Synthetic,
System,
Shell,
Assistant,
Compaction,
])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "Session.Message" })
export type Message = AgentSwitched | ModelSwitched | User | Synthetic | System | Shell | Assistant | Compaction
export type Type = Message["type"]

View File

@@ -0,0 +1,46 @@
export * as Session from "./session"
import { Schema } from "effect"
import { Agent } from "./agent"
import { Location } from "./location"
import { Model } from "./model"
import { Project } from "./project"
import { DateTimeUtcFromMillis, optionalOmitUndefined, RelativePath } from "./schema"
import { SessionID } from "./session-id"
export const ID = SessionID.ID
export type ID = SessionID.ID
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
id: ID,
parentID: ID.pipe(optionalOmitUndefined),
projectID: Project.ID,
agent: Agent.ID.pipe(Schema.optional),
model: Model.Ref.pipe(Schema.optional),
cost: Schema.Finite,
tokens: Schema.Struct({
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
}),
time: Schema.Struct({
created: DateTimeUtcFromMillis,
updated: DateTimeUtcFromMillis,
archived: DateTimeUtcFromMillis.pipe(Schema.optional),
}),
title: Schema.String,
location: Location.Ref,
subpath: RelativePath.pipe(Schema.optional),
}).annotate({ identifier: "SessionV2.Info" })
export const ListAnchor = Schema.Struct({
id: ID,
time: Schema.Finite,
direction: Schema.Literals(["previous", "next"]),
})
export type ListAnchor = typeof ListAnchor.Type

View File

@@ -0,0 +1,54 @@
export * as Skill from "./skill"
import { Schema } from "effect"
import { AbsolutePath } from "./schema"
export interface DirectorySource extends Schema.Schema.Type<typeof DirectorySource> {}
export const DirectorySource = Schema.Struct({
type: Schema.Literal("directory"),
path: AbsolutePath,
}).annotate({ identifier: "SkillV2.DirectorySource" })
export interface UrlSource extends Schema.Schema.Type<typeof UrlSource> {}
export const UrlSource = Schema.Struct({
type: Schema.Literal("url"),
url: Schema.String,
}).annotate({ identifier: "SkillV2.UrlSource" })
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
name: Schema.String,
description: Schema.String.pipe(Schema.optional),
slash: Schema.Boolean.pipe(Schema.optional),
location: AbsolutePath,
content: Schema.String,
}).annotate({ identifier: "SkillV2.Info" })
export interface EmbeddedSource extends Schema.Schema.Type<typeof EmbeddedSource> {}
export const EmbeddedSource = Schema.Struct({
type: Schema.Literal("embedded"),
skill: Schema.suspend(() => Info),
}).annotate({ identifier: "SkillV2.EmbeddedSource" })
export type Source = DirectorySource | UrlSource | EmbeddedSource
export const Source = Object.assign(
Schema.Union([DirectorySource, UrlSource, EmbeddedSource]).pipe(
Schema.toTaggedUnion("type"),
Schema.annotate({ identifier: "SkillV2.Source" }),
),
{
equals: (a: Source, b: Source) => {
if (a.type !== b.type) return false
if (a.type === "directory" && b.type === "directory") return a.path === b.path
if (a.type === "url" && b.type === "url") return a.url === b.url
if (a.type === "embedded" && b.type === "embedded") return a.skill.name === b.skill.name
return false
},
key: (source: Source) =>
source.type === "directory"
? `directory:${source.path}`
: source.type === "url"
? `url:${source.url}`
: `embedded:${source.skill.name}`,
},
)

View File

@@ -0,0 +1,21 @@
export * as Workspace from "./workspace"
import { Schema } from "effect"
import { ascending } from "./identifier"
import { withStatics } from "./schema"
export const ID = Schema.String.check(Schema.isStartsWith("wrk")).pipe(
Schema.brand("WorkspaceV2.ID"),
withStatics((schema) => {
const create = () => schema.make("wrk_" + ascending())
return {
ascending: (id?: string) => {
if (!id) return create()
if (!id.startsWith("wrk")) throw new Error(`ID ${id} does not start with wrk`)
return schema.make(id)
},
create,
}
}),
)
export type ID = typeof ID.Type

View File

@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"noUncheckedIndexedAccess": false
}
}