Compare commits

...

14 Commits

Author SHA1 Message Date
Kit Langton 7b50bdedb4 refactor(schema): simplify v1 entrypoints 2026-06-24 23:38:12 -04:00
Kit Langton 4be660c73b refactor(schema): isolate v1 contracts 2026-06-24 23:38:12 -04:00
Kit Langton 2fb949754f refactor(schema): reuse id constructors 2026-06-24 23:38:08 -04:00
Kit Langton 3cb81aebe5 chore(client): regenerate schema contracts 2026-06-24 23:38:08 -04:00
Kit Langton 4e980858ea fix(schema): project persisted todos 2026-06-24 23:38:08 -04:00
Kit Langton 61aaa250c4 fix(schema): preserve session event names 2026-06-24 23:38:07 -04:00
Kit Langton cb87a172c1 test(schema): align identifier coverage 2026-06-24 23:38:07 -04:00
Kit Langton 92544483d8 refactor(schema): preserve compatibility shapes 2026-06-24 23:38:07 -04:00
Kit Langton fb6770a69d refactor(schema): tighten public contracts 2026-06-24 23:38:07 -04:00
Kit Langton 3497c2dfe9 refactor(schema): simplify pty imports 2026-06-24 23:38:02 -04:00
Kit Langton 36ddebf770 refactor(schema): normalize module patterns 2026-06-24 23:38:02 -04:00
Kit Langton 5cd941a503 docs(schema): update sdk next status 2026-06-24 23:37:58 -04:00
Kit Langton d47287911f docs(schema): simplify package guide 2026-06-24 23:37:58 -04:00
Kit Langton 39ab49c9e5 docs(schema): add package conventions 2026-06-24 23:37:57 -04:00
89 changed files with 1892 additions and 1880 deletions
@@ -1 +1,5 @@
["client-error.ts", "client.ts", "index.ts"]
[
"client-error.ts",
"client.ts",
"index.ts"
]
@@ -1 +1,6 @@
["client-error.ts", "client.ts", "index.ts", "types.ts"]
[
"client-error.ts",
"client.ts",
"index.ts",
"types.ts"
]
+155 -192
View File
@@ -152,8 +152,8 @@ export type SessionsListOutput = {
readonly id: string
readonly parentID?: string
readonly projectID: string
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string | null } | null
readonly agent?: string
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly cost: number
readonly tokens: {
readonly input: number
@@ -161,23 +161,23 @@ export type SessionsListOutput = {
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number | null }
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number }
readonly title: string
readonly location: { readonly directory: string; readonly workspaceID?: string | null | null }
readonly subpath?: string | null
readonly location: { readonly directory: string; readonly workspaceID?: string }
readonly subpath?: string
readonly revert?: {
readonly messageID: string
readonly partID?: string | null
readonly snapshot?: string | null
readonly diff?: string | null
readonly partID?: string
readonly snapshot?: string
readonly diff?: string
readonly files?: ReadonlyArray<{
readonly path: string
readonly status: "added" | "modified" | "deleted"
readonly additions: number
readonly deletions: number
readonly patch: string
}> | null
} | null
}>
}
}>
readonly cursor: { readonly previous?: string | null; readonly next?: string | null }
}
@@ -186,26 +186,26 @@ export type SessionsCreateInput = {
readonly id?: {
readonly id?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string | null } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string | null | null } | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string } | null
}["id"]
readonly agent?: {
readonly id?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string | null } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string | null | null } | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string } | null
}["agent"]
readonly model?: {
readonly id?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string | null } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string | null | null } | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string } | null
}["model"]
readonly location?: {
readonly id?: string | null
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string | null } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string | null | null } | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } | null
readonly location?: { readonly directory: string; readonly workspaceID?: string } | null
}["location"]
}
@@ -214,8 +214,8 @@ export type SessionsCreateOutput = {
readonly id: string
readonly parentID?: string
readonly projectID: string
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string | null } | null
readonly agent?: string
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly cost: number
readonly tokens: {
readonly input: number
@@ -223,23 +223,23 @@ export type SessionsCreateOutput = {
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number | null }
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number }
readonly title: string
readonly location: { readonly directory: string; readonly workspaceID?: string | null | null }
readonly subpath?: string | null
readonly location: { readonly directory: string; readonly workspaceID?: string }
readonly subpath?: string
readonly revert?: {
readonly messageID: string
readonly partID?: string | null
readonly snapshot?: string | null
readonly diff?: string | null
readonly partID?: string
readonly snapshot?: string
readonly diff?: string
readonly files?: ReadonlyArray<{
readonly path: string
readonly status: "added" | "modified" | "deleted"
readonly additions: number
readonly deletions: number
readonly patch: string
}> | null
} | null
}>
}
}
}["data"]
@@ -250,8 +250,8 @@ export type SessionsGetOutput = {
readonly id: string
readonly parentID?: string
readonly projectID: string
readonly agent?: string | null
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string | null } | null
readonly agent?: string
readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly cost: number
readonly tokens: {
readonly input: number
@@ -259,23 +259,23 @@ export type SessionsGetOutput = {
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number | null }
readonly time: { readonly created: number; readonly updated: number; readonly archived?: number }
readonly title: string
readonly location: { readonly directory: string; readonly workspaceID?: string | null | null }
readonly subpath?: string | null
readonly location: { readonly directory: string; readonly workspaceID?: string }
readonly subpath?: string
readonly revert?: {
readonly messageID: string
readonly partID?: string | null
readonly snapshot?: string | null
readonly diff?: string | null
readonly partID?: string
readonly snapshot?: string
readonly diff?: string
readonly files?: ReadonlyArray<{
readonly path: string
readonly status: "added" | "modified" | "deleted"
readonly additions: number
readonly deletions: number
readonly patch: string
}> | null
} | null
}>
}
}
}["data"]
@@ -289,7 +289,7 @@ export type SessionsSwitchAgentOutput = void
export type SessionsSwitchModelInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
readonly model: {
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string | undefined }
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
}["model"]
}
@@ -298,96 +298,80 @@ export type SessionsSwitchModelOutput = void
export type SessionsPromptInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
readonly id?: {
readonly id?: string | undefined
readonly id?: string | null
readonly prompt: {
readonly text: string
readonly files?:
| ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string | undefined
readonly description?: string | undefined
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly agents?:
| ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly files?: ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string
readonly description?: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
}
readonly delivery?: "steer" | "queue" | undefined
readonly resume?: boolean | undefined
readonly delivery?: "steer" | "queue" | null
readonly resume?: boolean | null
}["id"]
readonly prompt: {
readonly id?: string | undefined
readonly id?: string | null
readonly prompt: {
readonly text: string
readonly files?:
| ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string | undefined
readonly description?: string | undefined
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly agents?:
| ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly files?: ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string
readonly description?: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
}
readonly delivery?: "steer" | "queue" | undefined
readonly resume?: boolean | undefined
readonly delivery?: "steer" | "queue" | null
readonly resume?: boolean | null
}["prompt"]
readonly delivery?: {
readonly id?: string | undefined
readonly id?: string | null
readonly prompt: {
readonly text: string
readonly files?:
| ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string | undefined
readonly description?: string | undefined
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly agents?:
| ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly files?: ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string
readonly description?: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
}
readonly delivery?: "steer" | "queue" | undefined
readonly resume?: boolean | undefined
readonly delivery?: "steer" | "queue" | null
readonly resume?: boolean | null
}["delivery"]
readonly resume?: {
readonly id?: string | undefined
readonly id?: string | null
readonly prompt: {
readonly text: string
readonly files?:
| ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string | undefined
readonly description?: string | undefined
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly agents?:
| ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | undefined
}>
| undefined
readonly files?: ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string
readonly description?: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
}
readonly delivery?: "steer" | "queue" | undefined
readonly resume?: boolean | undefined
readonly delivery?: "steer" | "queue" | null
readonly resume?: boolean | null
}["resume"]
}
@@ -401,18 +385,18 @@ export type SessionsPromptOutput = {
readonly files?: ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string | null
readonly description?: string | null
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | null
}> | null
readonly name?: string
readonly description?: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | null
}> | null
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
}
readonly delivery: "steer" | "queue"
readonly timeCreated: number
readonly promotedSeq?: number | null
readonly promotedSeq?: number
}
}["data"]
@@ -433,18 +417,16 @@ export type SessionsStageInput = {
export type SessionsStageOutput = {
readonly data: {
readonly messageID: string
readonly partID?: string | undefined
readonly snapshot?: string | undefined
readonly diff?: string | undefined
readonly files?:
| ReadonlyArray<{
readonly path: string
readonly status: "added" | "modified" | "deleted"
readonly additions: number
readonly deletions: number
readonly patch: string
}>
| undefined
readonly partID?: string
readonly snapshot?: string
readonly diff?: string
readonly files?: ReadonlyArray<{
readonly path: string
readonly status: "added" | "modified" | "deleted"
readonly additions: number
readonly deletions: number
readonly patch: string
}>
}
}["data"]
@@ -462,39 +444,39 @@ export type SessionsContextOutput = {
readonly data: ReadonlyArray<
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "agent-switched"
readonly agent: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "model-switched"
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string | null }
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly text: string
readonly files?: ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string | null
readonly description?: string | null
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | null
}> | null
readonly name?: string
readonly description?: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly agents?: ReadonlyArray<{
readonly name: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | null
}> | null
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly type: "user"
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly sessionID: string
readonly text: string
@@ -502,15 +484,15 @@ export type SessionsContextOutput = {
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
readonly type: "system"
readonly text: string
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly time: { readonly created: number; readonly completed?: number | null }
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "shell"
readonly callID: string
readonly command: string
@@ -518,18 +500,18 @@ export type SessionsContextOutput = {
}
| {
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly time: { readonly created: number; readonly completed?: number | null }
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number; readonly completed?: number }
readonly type: "assistant"
readonly agent: string
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string | null }
readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
readonly content: ReadonlyArray<
| { readonly type: "text"; readonly id: string; readonly text: string }
| {
readonly type: "reasoning"
readonly id: string
readonly text: string
readonly providerMetadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } } | null
readonly providerMetadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } }
}
| {
readonly type: "tool"
@@ -537,23 +519,18 @@ export type SessionsContextOutput = {
readonly name: string
readonly provider?: {
readonly executed: boolean
readonly metadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } } | null
readonly resultMetadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } } | null
} | null
readonly metadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } }
readonly resultMetadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } }
}
readonly state:
| { readonly status: "pending"; readonly input: string }
| {
readonly status: "running"
readonly input: { readonly [x: string]: JsonValue }
readonly structured: { readonly [x: string]: any }
readonly structured: { readonly [x: string]: JsonValue }
readonly content: ReadonlyArray<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
| { readonly type: "file"; readonly uri: string; readonly mime: string; readonly name?: string }
>
}
| {
@@ -562,61 +539,47 @@ export type SessionsContextOutput = {
readonly attachments?: ReadonlyArray<{
readonly uri: string
readonly mime: string
readonly name?: string | null
readonly description?: string | null
readonly source?: { readonly start: number; readonly end: number; readonly text: string } | null
}> | null
readonly name?: string
readonly description?: string
readonly source?: { readonly start: number; readonly end: number; readonly text: string }
}>
readonly content: ReadonlyArray<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
| { readonly type: "file"; readonly uri: string; readonly mime: string; readonly name?: string }
>
readonly outputPaths?: ReadonlyArray<string> | null
readonly structured: { readonly [x: string]: any }
readonly result?: JsonValue | null
readonly outputPaths?: ReadonlyArray<string>
readonly structured: { readonly [x: string]: JsonValue }
readonly result?: JsonValue
}
| {
readonly status: "error"
readonly input: { readonly [x: string]: JsonValue }
readonly content: ReadonlyArray<
| { readonly type: "text"; readonly text: string }
| {
readonly type: "file"
readonly uri: string
readonly mime: string
readonly name?: string | null
}
| { readonly type: "file"; readonly uri: string; readonly mime: string; readonly name?: string }
>
readonly structured: { readonly [x: string]: any }
readonly structured: { readonly [x: string]: JsonValue }
readonly error: { readonly type: "unknown"; readonly message: string }
readonly result?: JsonValue | null
readonly result?: JsonValue
}
readonly time: {
readonly created: number
readonly ran?: number | null
readonly completed?: number | null
readonly pruned?: number | null
readonly ran?: number
readonly completed?: number
readonly pruned?: number
}
}
>
readonly snapshot?: {
readonly start?: string | null
readonly end?: string | null
readonly files?: ReadonlyArray<string> | null
} | null
readonly finish?: string | null
readonly cost?: number | null
readonly snapshot?: { readonly start?: string; readonly end?: string; readonly files?: ReadonlyArray<string> }
readonly finish?: string
readonly cost?: number
readonly tokens?: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
} | null
readonly error?: { readonly type: "unknown"; readonly message: string } | null
}
readonly error?: { readonly type: "unknown"; readonly message: string }
}
| {
readonly type: "compaction"
@@ -624,7 +587,7 @@ export type SessionsContextOutput = {
readonly summary: string
readonly recent: string
readonly id: string
readonly metadata?: { readonly [x: string]: JsonValue } | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly time: { readonly created: number }
}
>
+8 -8
View File
@@ -1,8 +1,7 @@
export * as PluginV2 from "./plugin"
import { Context, Deferred, Effect, Exit, Layer, Scope } from "effect"
import type { Plugin } from "@opencode-ai/plugin/v2/effect"
import { PluginEvent, PluginID } from "@opencode-ai/schema/plugin"
import { Plugin } from "@opencode-ai/schema/plugin"
import { AgentV2 } from "./agent"
import { AISDK } from "./aisdk"
import { Catalog } from "./catalog"
@@ -15,13 +14,14 @@ import { Reference } from "./reference"
import { SkillV2 } from "./skill"
import { State } from "./state"
export const ID = PluginID
export type ID = typeof ID.Type
type PluginRuntime = import("@opencode-ai/plugin/v2/effect").Plugin
export const Event = PluginEvent
export const ID = Plugin.ID
export type ID = typeof ID.Type
export const Event = Plugin.Event
export interface Interface {
readonly add: (id: ID, effect: Plugin["effect"]) => Effect.Effect<void>
readonly add: (id: ID, effect: PluginRuntime["effect"]) => Effect.Effect<void>
readonly remove: (id: ID) => Effect.Effect<void>
readonly wait: (id: ID) => Effect.Effect<void>
}
@@ -38,9 +38,9 @@ export const layer = Layer.effect(
const loading = new Set<ID>()
const waiters = new Map<ID, Set<Deferred.Deferred<void>>>()
const failures = new Map<ID, Exit.Exit<void, never>>()
let host: Parameters<Plugin["effect"]>[0]
let host: Parameters<PluginRuntime["effect"]>[0]
const add = Effect.fn("Plugin.add")(function* (id: ID, effect: Plugin["effect"]) {
const add = Effect.fn("Plugin.add")(function* (id: ID, effect: PluginRuntime["effect"]) {
if (loading.has(id)) return yield* Effect.die(`Plugin load cycle detected for ${id}`)
yield* locks.withLock(id)(
+2 -2
View File
@@ -13,7 +13,7 @@ import { Slug } from "../util/slug"
import { EventV2 } from "../event"
import { Database } from "../database/database"
import { Location } from "../location"
import { ProjectDirectoriesEvent } from "@opencode-ai/schema/project-directories"
import { Event } from "@opencode-ai/schema/project-directories"
import { ProjectCopy } from "@opencode-ai/schema/project-copy"
export const StrategyID = ProjectCopy.StrategyID
@@ -96,7 +96,7 @@ export interface Strategy {
readonly list: (directory: AbsolutePath) => Effect.Effect<ListEntry[], Git.WorktreeError | DirectoryUnavailableError>
}
export const Event = ProjectDirectoriesEvent
export { Event }
export interface Interface {
readonly register: (strategy: Strategy) => Effect.Effect<void, DuplicateStrategyError>
+2 -2
View File
@@ -4,7 +4,7 @@ import { and, asc, desc, eq, isNotNull, isNull, ne, or } from "drizzle-orm"
import { Context, Effect, Layer, Schema } from "effect"
import { Database } from "../database/database"
import { LayerNode } from "../effect/layer-node"
import { AbsolutePath, optionalOmitUndefined } from "../schema"
import { AbsolutePath, optional } from "../schema"
import { ProjectSchema } from "./schema"
import { ProjectDirectoryTable } from "./sql"
import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
@@ -39,7 +39,7 @@ export type ListInput = typeof ListInput.Type
export const ListOutput = Schema.Array(
Schema.Struct({
directory: AbsolutePath,
strategy: optionalOmitUndefined(Schema.String),
strategy: optional(Schema.String),
}),
).annotate({ identifier: "Project.Directories" })
export type ListOutput = typeof ListOutput.Type
+4 -5
View File
@@ -2,7 +2,7 @@ export * as Pty from "./pty"
import type { Disp, Proc } from "#pty"
import { Context, Effect, Layer, Schema, Types } from "effect"
import { PtyEvent, PtyInfo, Pty } from "@opencode-ai/schema/pty"
import { Pty } from "@opencode-ai/schema/pty"
import { Config } from "./config"
import { EventV2 } from "./event"
import { Location } from "./location"
@@ -35,8 +35,7 @@ type Active = {
listeners: Disp[]
}
export const Info = PtyInfo
export const Info = Pty.Info
export type Info = Types.DeepMutable<typeof Info.Type>
export const CreateInput = Pty.CreateInput
@@ -47,6 +46,8 @@ export const UpdateInput = Pty.UpdateInput
export type UpdateInput = Types.DeepMutable<typeof UpdateInput.Type>
export const Event = Pty.Event
export type AttachInput = {
// Absolute output cursor to replay from. -1 tails from the current end; omitted replays the full retained buffer.
readonly cursor?: number
@@ -75,8 +76,6 @@ export class ExitedError extends Schema.TaggedErrorClass<ExitedError>()("Pty.Exi
ptyID: PtyID,
}) {}
export const Event = PtyEvent
export interface Interface {
readonly list: () => Effect.Effect<Info[]>
readonly get: (id: PtyID) => Effect.Effect<Info, NotFoundError>
+4 -4
View File
@@ -3,20 +3,20 @@ import {
AbsolutePath,
DateTimeUtcFromMillis,
NonNegativeInt,
optionalOmitUndefined,
optional,
PositiveInt,
RelativePath,
withStatics,
statics,
} from "@opencode-ai/schema/schema"
export {
AbsolutePath,
DateTimeUtcFromMillis,
NonNegativeInt,
optionalOmitUndefined,
optional,
PositiveInt,
RelativePath,
withStatics,
statics,
}
/**
+2 -2
View File
@@ -9,7 +9,7 @@ import { ContextSnapshotDecodeError } from "./error"
import { SessionEvent } from "./event"
import { SessionHistory } from "./history"
import { SessionInput } from "./input"
import { SessionMessageID } from "./message-id"
import { SessionMessage } from "./message"
import { SessionSchema } from "./schema"
import { SessionContextEpochTable } from "./sql"
@@ -71,7 +71,7 @@ const prepareOnce = Effect.fnUntraced(function* (
yield* events.publish(
SessionEvent.ContextUpdated,
{ sessionID, messageID: SessionMessageID.ID.create(), timestamp: yield* DateTime.now, text: result.text },
{ sessionID, messageID: SessionMessage.ID.create(), timestamp: yield* DateTime.now, text: result.text },
{ commit: () => advance(db, sessionID, result.snapshot).pipe(Effect.orDie) },
)
return { baseline: stored.baseline, baselineSeq: stored.baseline_seq }
+2 -2
View File
@@ -8,7 +8,7 @@ import { AbsolutePath, RelativePath } from "../schema"
import { WorkspaceV2 } from "../workspace"
import { SessionSchema } from "./schema"
import { SessionTable } from "./sql"
import { SessionMessageID } from "./message-id"
import { SessionMessage } from "./message"
import { Snapshot } from "../snapshot"
export function fromRow(row: typeof SessionTable.$inferSelect): SessionSchema.Info {
@@ -40,7 +40,7 @@ export function fromRow(row: typeof SessionTable.$inferSelect): SessionSchema.In
workspaceID: row.workspace_id ? WorkspaceV2.ID.make(row.workspace_id) : undefined,
}),
subpath: row.path ? RelativePath.make(row.path) : undefined,
revert: row.revert ? { ...row.revert, messageID: SessionMessageID.ID.make(row.revert.messageID) } : undefined,
revert: row.revert ? { ...row.revert, messageID: SessionMessage.ID.make(row.revert.messageID) } : undefined,
time: {
created: DateTime.makeUnsafe(row.time_created),
updated: DateTime.makeUnsafe(row.time_updated),
-2
View File
@@ -1,2 +0,0 @@
export * as SessionMessageID from "./message-id"
export { ID } from "@opencode-ai/schema/session-message-id"
+1 -2
View File
@@ -15,7 +15,6 @@ import { WorkspaceV2 } from "../workspace"
import { SessionContextEpoch } from "./context-epoch"
import { MessageTable, PartTable, SessionInputTable, SessionMessageTable, SessionTable } from "./sql"
import type { DeepMutable } from "../schema"
import { SessionMessageID } from "./message-id"
type DatabaseService = Database.Interface["db"]
@@ -67,7 +66,7 @@ function sessionRow(info: SessionV1.SessionInfo): typeof SessionTable.$inferInse
tokens_reasoning: (info.tokens ?? { reasoning: 0 }).reasoning,
tokens_cache_read: (info.tokens ?? { cache: { read: 0 } }).cache.read,
tokens_cache_write: (info.tokens ?? { cache: { write: 0 } }).cache.write,
revert: info.revert ? { ...info.revert, messageID: SessionMessageID.ID.make(info.revert.messageID) } : null,
revert: info.revert ? { ...info.revert, messageID: SessionMessage.ID.make(info.revert.messageID) } : null,
permission: info.permission ? [...info.permission] : undefined,
time_created: info.time.created,
time_updated: info.time.updated,
+3 -2
View File
@@ -14,6 +14,7 @@ import { Timestamps } from "../database/schema.sql"
import type { SystemContext } from "../system-context/index"
import { AgentV2 } from "../agent"
import type { Revert } from "@opencode-ai/schema/revert"
import type { SessionTodo } from "@opencode-ai/schema/session-todo"
type SessionMessageData = Omit<(typeof SessionMessage.Message)["Encoded"], "type" | "id">
type V1MessageData = Omit<SessionV1.Info, "id" | "sessionID">
@@ -105,8 +106,8 @@ export const TodoTable = sqliteTable(
.notNull()
.references(() => SessionTable.id, { onDelete: "cascade" }),
content: text().notNull(),
status: text().notNull(),
priority: text().notNull(),
status: text().$type<SessionTodo.Info["status"]>().notNull(),
priority: text().$type<SessionTodo.Info["priority"]>().notNull(),
position: integer().notNull(),
...Timestamps,
},
+2 -3
View File
@@ -2,15 +2,14 @@ export * as SessionTodo from "./todo"
import { asc, eq } from "drizzle-orm"
import { Context, Effect, Layer } from "effect"
import { SessionTodo, SessionTodoInfo } from "@opencode-ai/schema/session-todo"
import { SessionTodo } from "@opencode-ai/schema/session-todo"
import { Database } from "../database/database"
import { EventV2 } from "../event"
import { SessionSchema } from "./schema"
import { TodoTable } from "./sql"
export const Info = SessionTodoInfo
export const Info = SessionTodo.Info
export type Info = typeof Info.Type
export const Event = SessionTodo.Event
export interface Interface {
+27 -1
View File
@@ -9,6 +9,8 @@ import { Model } from "@opencode-ai/schema/model"
import { AgentAttachment, FileAttachment, Prompt, Source } from "@opencode-ai/schema/prompt"
import { Provider } from "@opencode-ai/schema/provider"
import { Project } from "@opencode-ai/schema/project"
import { ProjectDirectories } from "@opencode-ai/schema/project-directories"
import { PermissionV1 } from "@opencode-ai/schema/permission-v1"
import { Session } from "@opencode-ai/schema/session"
import { SessionInput } from "@opencode-ai/schema/session-input"
import { SessionMessage } from "@opencode-ai/schema/session-message"
@@ -20,10 +22,14 @@ import { FileSystem } from "@opencode-ai/schema/filesystem"
import { Integration } from "@opencode-ai/schema/integration"
import { LLM } from "@opencode-ai/schema/llm"
import { Permission } from "@opencode-ai/schema/permission"
import { Plugin } from "@opencode-ai/schema/plugin"
import { Pty } from "@opencode-ai/schema/pty"
import { Reference } from "@opencode-ai/schema/reference"
import { SessionTodo } from "@opencode-ai/schema/session-todo"
import { Skill } from "@opencode-ai/schema/skill"
import { AbsolutePath, DateTimeUtcFromMillis } from "@opencode-ai/schema/schema"
import { AbsolutePath, DateTimeUtcFromMillis, optional, statics } from "@opencode-ai/schema/schema"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { PluginV2 } from "@opencode-ai/core/plugin"
test("Core reuses the canonical shared schemas", async () => {
const [
@@ -35,13 +41,18 @@ test("Core reuses the canonical shared schemas", async () => {
coreLocation,
coreLLM,
corePermission,
corePermissionV1,
coreProjectCopy,
corePty,
coreProject,
coreReference,
coreSessionInput,
coreSessionMessage,
coreSessionTodo,
corePrompt,
coreSkill,
coreV2Schema,
coreSchema,
coreWorkspace,
] = await Promise.all([
import("@opencode-ai/core/command"),
@@ -52,13 +63,18 @@ test("Core reuses the canonical shared schemas", async () => {
import("@opencode-ai/core/location"),
import("@opencode-ai/llm"),
import("@opencode-ai/core/permission"),
import("@opencode-ai/core/v1/permission"),
import("@opencode-ai/core/project/copy"),
import("@opencode-ai/core/pty"),
import("@opencode-ai/core/project/schema"),
import("@opencode-ai/core/reference"),
import("@opencode-ai/core/session/input"),
import("@opencode-ai/core/session/message"),
import("@opencode-ai/core/session/todo"),
import("@opencode-ai/core/session/prompt"),
import("@opencode-ai/core/skill"),
import("@opencode-ai/core/v2-schema"),
import("@opencode-ai/core/schema"),
import("@opencode-ai/core/workspace"),
])
@@ -111,6 +127,12 @@ test("Core reuses the canonical shared schemas", async () => {
[corePermission.Effect, Permission.Effect],
[corePermission.Rule, Permission.Rule],
[corePermission.Ruleset, Permission.Ruleset],
[corePermissionV1.Event, PermissionV1.Event],
[coreProjectCopy.Event, ProjectDirectories.Event],
[PluginV2.ID, Plugin.ID],
[PluginV2.Event, Plugin.Event],
[corePty.Info, Pty.Info],
[corePty.Event, Pty.Event],
[coreProject.ID, Project.ID],
[coreReference.LocalSource, Reference.LocalSource],
[coreReference.GitSource, Reference.GitSource],
@@ -140,6 +162,8 @@ test("Core reuses the canonical shared schemas", async () => {
[coreSessionMessage.Assistant, SessionMessage.Assistant],
[coreSessionMessage.Compaction, SessionMessage.Compaction],
[coreSessionMessage.Message, SessionMessage.Message],
[coreSessionTodo.Info, SessionTodo.Info],
[coreSessionTodo.Event, SessionTodo.Event],
[corePrompt.Source, Source],
[corePrompt.FileAttachment, FileAttachment],
[corePrompt.AgentAttachment, AgentAttachment],
@@ -150,6 +174,8 @@ test("Core reuses the canonical shared schemas", async () => {
[coreSkill.Source, Skill.Source],
[coreSkill.Info, Skill.Info],
[coreV2Schema.DateTimeUtcFromMillis, DateTimeUtcFromMillis],
[coreSchema.optional, optional],
[coreSchema.statics, statics],
[coreWorkspace.ID, Workspace.ID],
]
for (const [core, shared] of schemas) expect(core).toBe(shared)
+3 -1
View File
@@ -77,7 +77,9 @@ describe("TodoWriteTool", () => {
yield* setup
const registry = yield* ToolRegistry.Service
const service = yield* SessionTodo.Service
const todoList = [{ content: "Implement slice", status: "in_progress", priority: "high" }]
const todoList: ReadonlyArray<SessionTodo.Info> = [
{ content: "Implement slice", status: "in_progress", priority: "high" },
]
expect((yield* toolDefinitions(registry)).map((tool) => tool.name)).toEqual([TodoWriteTool.name])
expect(yield* settleTool(registry, call(todoList))).toEqual({
+1 -2
View File
@@ -6,9 +6,8 @@ import { Deferred, Effect, Layer, Context } from "effect"
import os from "os"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import { EventV2Bridge } from "@/event-v2-bridge"
import { PermissionV1Event } from "@opencode-ai/schema/permission-v1"
export const Event = PermissionV1Event
export const Event = PermissionV1.Event
export interface Interface {
readonly ask: (input: PermissionV1.AskInput) => Effect.Effect<void, PermissionV1.Error>
+6 -6
View File
@@ -3,7 +3,7 @@ import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
import { serviceUse } from "@opencode-ai/core/effect/service-use"
import { Auth } from "@/auth"
import { InstanceState } from "@/effect/instance-state"
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
import { optional } from "@opencode-ai/core/schema"
import { Plugin } from "../plugin"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { Array as Arr, Effect, Layer, Record, Result, Context, Schema } from "effect"
@@ -18,14 +18,14 @@ const TextPrompt = Schema.Struct({
type: Schema.Literal("text"),
key: Schema.String,
message: Schema.String,
placeholder: optionalOmitUndefined(Schema.String),
when: optionalOmitUndefined(When),
placeholder: optional(Schema.String),
when: optional(When),
})
const SelectOption = Schema.Struct({
label: Schema.String,
value: Schema.String,
hint: optionalOmitUndefined(Schema.String),
hint: optional(Schema.String),
})
const SelectPrompt = Schema.Struct({
@@ -33,7 +33,7 @@ const SelectPrompt = Schema.Struct({
key: Schema.String,
message: Schema.String,
options: Schema.Array(SelectOption),
when: optionalOmitUndefined(When),
when: optional(When),
})
const Prompt = Schema.Union([TextPrompt, SelectPrompt])
@@ -41,7 +41,7 @@ const Prompt = Schema.Union([TextPrompt, SelectPrompt])
export class Method extends Schema.Class<Method>("ProviderAuthMethod")({
type: Schema.Literals(["oauth", "api"]),
label: Schema.String,
prompts: optionalOmitUndefined(Schema.Array(Prompt)),
prompts: optional(Schema.Array(Prompt)),
}) {}
export const Methods = Schema.Record(Schema.String, Schema.Array(Method))
+7 -7
View File
@@ -24,7 +24,7 @@ import { InstanceState } from "@/effect/instance-state"
import { EffectPromise } from "@/effect/promise"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { isRecord } from "@/util/record"
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
import { optional } from "@opencode-ai/core/schema"
import { ProviderTransform } from "./transform"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
@@ -999,8 +999,8 @@ const ProviderCost = Schema.Struct({
input: Schema.Finite,
output: Schema.Finite,
cache: ProviderCacheCost,
tiers: optionalOmitUndefined(Schema.Array(ProviderCostTier)),
experimentalOver200K: optionalOmitUndefined(
tiers: optional(Schema.Array(ProviderCostTier)),
experimentalOver200K: optional(
Schema.Struct({
input: Schema.Finite,
output: Schema.Finite,
@@ -1011,7 +1011,7 @@ const ProviderCost = Schema.Struct({
const ProviderLimit = Schema.Struct({
context: Schema.Finite,
input: optionalOmitUndefined(Schema.Finite),
input: optional(Schema.Finite),
output: Schema.Finite,
})
@@ -1020,7 +1020,7 @@ export const Model = Schema.Struct({
providerID: ProviderV2.ID,
api: ProviderApiInfo,
name: Schema.String,
family: optionalOmitUndefined(Schema.String),
family: optional(Schema.String),
capabilities: ProviderCapabilities,
cost: ProviderCost,
limit: ProviderLimit,
@@ -1028,7 +1028,7 @@ export const Model = Schema.Struct({
options: Schema.Record(Schema.String, Schema.Any),
headers: Schema.Record(Schema.String, Schema.String),
release_date: Schema.String,
variants: optionalOmitUndefined(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Any))),
variants: optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Any))),
}).annotate({ identifier: "Model" })
export type Model = Types.DeepMutable<Schema.Schema.Type<typeof Model>>
@@ -1037,7 +1037,7 @@ export const Info = Schema.Struct({
name: Schema.String,
source: Schema.Literals(["env", "config", "custom", "api"]),
env: Schema.Array(Schema.String),
key: optionalOmitUndefined(Schema.String),
key: optional(Schema.String),
options: Schema.Record(Schema.String, Schema.Any),
models: Schema.Record(Schema.String, Model),
}).annotate({ identifier: "Provider" })
+3 -3
View File
@@ -2,14 +2,14 @@ import { Schema } from "effect"
import { Identifier } from "@/id/id"
import { SessionV2 } from "@opencode-ai/core/session"
import { withStatics } from "@opencode-ai/core/schema"
import { statics } from "@opencode-ai/core/schema"
export const SessionID = SessionV2.ID
export type SessionID = Schema.Schema.Type<typeof SessionID>
export const MessageID = Schema.String.check(Schema.isStartsWith("msg")).pipe(
Schema.brand("MessageID"),
withStatics((s) => ({
statics((s) => ({
ascending: (id?: string) => s.make(Identifier.ascending("message", id)),
})),
)
@@ -18,7 +18,7 @@ export type MessageID = Schema.Schema.Type<typeof MessageID>
export const PartID = Schema.String.check(Schema.isStartsWith("prt")).pipe(
Schema.brand("PartID"),
withStatics((s) => ({
statics((s) => ({
ascending: (id?: string) => s.make(Identifier.ascending("part", id)),
})),
)
+23 -23
View File
@@ -39,11 +39,11 @@ import { SessionID, MessageID, PartID } from "./schema"
import type { Provider } from "@/provider/provider"
import { Global } from "@opencode-ai/core/global"
import { Effect, Layer, Option, Context, Schema, Types } from "effect"
import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
import { NonNegativeInt, optional } from "@opencode-ai/core/schema"
import { RuntimeFlags } from "@/effect/runtime-flags"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
import { SessionMessageID } from "@opencode-ai/schema/session-message-id"
import { SessionMessage } from "@opencode-ai/schema/session-message"
const runtime = makeRuntime(Database.Service, Database.defaultLayer)
@@ -146,7 +146,7 @@ export function toRow(info: Info) {
tokens_cache_write: (info.tokens ?? EmptyTokens).cache.write,
revert: info.revert
? {
messageID: SessionMessageID.ID.make(info.revert.messageID),
messageID: SessionMessage.ID.make(info.revert.messageID),
partID: info.revert.partID,
snapshot: info.revert.snapshot,
diff: info.revert.diff,
@@ -178,7 +178,7 @@ const Summary = Schema.Struct({
additions: Schema.Finite,
deletions: Schema.Finite,
files: Schema.Finite,
diffs: optionalOmitUndefined(Schema.Array(Snapshot.FileDiff)),
diffs: optional(Schema.Array(Snapshot.FileDiff)),
})
const Tokens = Schema.Struct({
@@ -204,21 +204,21 @@ export const ArchivedTimestamp = Schema.Finite
const Time = Schema.Struct({
created: NonNegativeInt,
updated: NonNegativeInt,
compacting: optionalOmitUndefined(NonNegativeInt),
archived: optionalOmitUndefined(ArchivedTimestamp),
compacting: optional(NonNegativeInt),
archived: optional(ArchivedTimestamp),
})
const Revert = Schema.Struct({
messageID: MessageID,
partID: optionalOmitUndefined(PartID),
snapshot: optionalOmitUndefined(Schema.String),
diff: optionalOmitUndefined(Schema.String),
partID: optional(PartID),
snapshot: optional(Schema.String),
diff: optional(Schema.String),
})
const Model = Schema.Struct({
id: ModelV2.ID,
providerID: ProviderV2.ID,
variant: optionalOmitUndefined(Schema.String),
variant: optional(Schema.String),
})
export const Metadata = Schema.Record(Schema.String, Schema.Any)
@@ -227,28 +227,28 @@ export const Info = Schema.Struct({
id: SessionID,
slug: Schema.String,
projectID: ProjectV2.ID,
workspaceID: optionalOmitUndefined(WorkspaceV2.ID),
workspaceID: optional(WorkspaceV2.ID),
directory: Schema.String,
path: optionalOmitUndefined(Schema.String),
parentID: optionalOmitUndefined(SessionID),
summary: optionalOmitUndefined(Summary),
cost: optionalOmitUndefined(Schema.Finite),
tokens: optionalOmitUndefined(Tokens),
share: optionalOmitUndefined(Share),
path: optional(Schema.String),
parentID: optional(SessionID),
summary: optional(Summary),
cost: optional(Schema.Finite),
tokens: optional(Tokens),
share: optional(Share),
title: Schema.String,
agent: optionalOmitUndefined(Schema.String),
model: optionalOmitUndefined(Model),
agent: optional(Schema.String),
model: optional(Model),
version: Schema.String,
metadata: optionalOmitUndefined(Metadata),
metadata: optional(Metadata),
time: Time,
permission: optionalOmitUndefined(PermissionV1.Ruleset),
revert: optionalOmitUndefined(Revert),
permission: optional(PermissionV1.Ruleset),
revert: optional(Revert),
}).annotate({ identifier: "Session" })
export type Info = Types.DeepMutable<Schema.Schema.Type<typeof Info>>
export const ProjectInfo = Schema.Struct({
id: ProjectV2.ID,
name: optionalOmitUndefined(Schema.String),
name: optional(Schema.String),
worktree: Schema.String,
}).annotate({ identifier: "ProjectSummary" })
export type ProjectInfo = Types.DeepMutable<Schema.Schema.Type<typeof ProjectInfo>>
+2 -2
View File
@@ -14,7 +14,7 @@ export type Info = SessionTodo.Info
export const Event = SessionTodo.Event
export interface Interface {
readonly update: (input: { sessionID: SessionID; todos: Info[] }) => Effect.Effect<void>
readonly update: (input: { sessionID: SessionID; todos: ReadonlyArray<Info> }) => Effect.Effect<void>
readonly get: (sessionID: SessionID) => Effect.Effect<Info[]>
}
@@ -26,7 +26,7 @@ export const layer = Layer.effect(
const events = yield* EventV2Bridge.Service
const { db } = yield* Database.Service
const update = Effect.fn("Todo.update")(function* (input: { sessionID: SessionID; todos: Info[] }) {
const update = Effect.fn("Todo.update")(function* (input: { sessionID: SessionID; todos: ReadonlyArray<Info> }) {
yield* db
.transaction((tx) =>
Effect.gen(function* () {
+2 -2
View File
@@ -1,11 +1,11 @@
import { Schema } from "effect"
import { Identifier } from "@/id/id"
import { withStatics } from "@opencode-ai/core/schema"
import { statics } from "@opencode-ai/core/schema"
export const EventID = Schema.String.check(Schema.isStartsWith("evt")).pipe(
Schema.brand("EventID"),
withStatics((s) => ({
statics((s) => ({
ascending: (id?: string) => s.make(Identifier.ascending("event", id)),
})),
)
+2 -2
View File
@@ -1,14 +1,14 @@
import { Schema } from "effect"
import { Identifier } from "@/id/id"
import { withStatics } from "@opencode-ai/core/schema"
import { statics } from "@opencode-ai/core/schema"
const toolIdSchema = Schema.String.check(Schema.isStartsWith("tool")).pipe(Schema.brand("ToolID"))
export type ToolID = typeof toolIdSchema.Type
export const ToolID = toolIdSchema.pipe(
withStatics((schema: typeof toolIdSchema) => ({
statics((schema: typeof toolIdSchema) => ({
ascending: (id?: string) => schema.make(Identifier.ascending("tool", id)),
})),
)
+1 -12
View File
@@ -3,19 +3,8 @@ import * as Tool from "./tool"
import DESCRIPTION_WRITE from "./todowrite.txt"
import { Todo } from "../session/todo"
// Todo.Info is still a zod schema (session/todo.ts). Inline the field shape
// here rather than referencing its `.shape` — the LLM-visible JSON Schema is
// identical, and it removes the last zod dependency from this tool.
const TodoItem = Schema.Struct({
content: Schema.String.annotate({ description: "Brief description of the task" }),
status: Schema.String.annotate({
description: "Current status of the task: pending, in_progress, completed, cancelled",
}),
priority: Schema.String.annotate({ description: "Priority level of the task: high, medium, low" }),
})
export const Parameters = Schema.Struct({
todos: Schema.mutable(Schema.Array(TodoItem)).annotate({ description: "The updated todo list" }),
todos: Schema.mutable(Schema.Array(Todo.Info)).annotate({ description: "The updated todo list" }),
})
type Metadata = {
@@ -1170,7 +1170,7 @@ const scenarios: Scenario[] = [
.seeded((ctx) =>
Effect.gen(function* () {
const session = yield* ctx.session({ title: "Todo session" })
const todos = [{ content: "cover session todo", status: "pending", priority: "high" }]
const todos = [{ content: "cover session todo", status: "pending" as const, priority: "high" as const }]
yield* ctx.todos(session.id, todos)
return { session, todos }
}),
@@ -119,5 +119,9 @@ export type Result =
| { status: "skip"; scenario: TodoScenario }
export type SessionInfo = { id: SessionID; title: string; parentID?: SessionID }
export type TodoInfo = { content: string; status: string; priority: string }
export type TodoInfo = {
content: string
status: "pending" | "in_progress" | "completed" | "cancelled"
priority: "high" | "medium" | "low"
}
export type MessageSeed = { info: SessionV1.User; part: SessionV1.TextPart }
@@ -256,7 +256,7 @@ describe("Todo.Info", () => {
const decode = decodeUnknown(Todo.Info)
test("three-field round-trip", () => {
const input = { content: "do a thing", status: "pending", priority: "high" }
const input = Todo.Info.make({ content: "do a thing", status: "pending", priority: "high" })
expect(decode(input)).toEqual(input)
})
})
+3 -2
View File
@@ -1,9 +1,10 @@
import type { AgentV2Info } from "@opencode-ai/sdk/v2/types"
import type { ReadonlyDeep } from "../options.js"
import type { Hooks } from "./registration.js"
export interface AgentDraft {
list(): readonly AgentV2Info[]
get(id: string): AgentV2Info | undefined
list(): readonly ReadonlyDeep<AgentV2Info>[]
get(id: string): ReadonlyDeep<AgentV2Info> | undefined
default(id: string | undefined): void
update(id: string, update: (agent: AgentV2Info) => void): void
remove(id: string): void
+3 -2
View File
@@ -1,16 +1,17 @@
import type { LanguageModelV3 } from "@ai-sdk/provider"
import type { ModelV2Info } from "@opencode-ai/sdk/v2/types"
import type { ReadonlyDeep } from "../options.js"
import type { Hooks } from "./registration.js"
export type AISDKHooks = Hooks<{
sdk: {
readonly model: ModelV2Info
readonly model: ReadonlyDeep<ModelV2Info>
readonly package: string
readonly options: Record<string, any>
sdk?: any
}
language: {
readonly model: ModelV2Info
readonly model: ReadonlyDeep<ModelV2Info>
readonly sdk: any
readonly options: Record<string, any>
language?: LanguageModelV3
+4 -3
View File
@@ -1,9 +1,10 @@
import type { ModelV2Info, ProviderV2Info } from "@opencode-ai/sdk/v2/types"
import type { ReadonlyDeep } from "../options.js"
import type { Hooks } from "./registration.js"
export interface CatalogProviderRecord {
readonly provider: ProviderV2Info
readonly models: ReadonlyMap<string, ModelV2Info>
readonly provider: ReadonlyDeep<ProviderV2Info>
readonly models: ReadonlyMap<string, ReadonlyDeep<ModelV2Info>>
}
export interface CatalogDraft {
@@ -14,7 +15,7 @@ export interface CatalogDraft {
remove(providerID: string): void
}
readonly model: {
get(providerID: string, modelID: string): ModelV2Info | undefined
get(providerID: string, modelID: string): ReadonlyDeep<ModelV2Info> | undefined
update(providerID: string, modelID: string, update: (model: ModelV2Info) => void): void
remove(providerID: string, modelID: string): void
readonly default: {
+4 -3
View File
@@ -11,6 +11,7 @@ import type {
} from "@opencode-ai/sdk/v2/types"
import type { Effect, Scope } from "effect"
import type { Hooks } from "./registration.js"
import type { ReadonlyDeep } from "../options.js"
export type IntegrationOAuthAuthorization = {
readonly url: string
@@ -44,12 +45,12 @@ export type IntegrationMethodRegistration =
}
export interface IntegrationDraft {
list(): readonly IntegrationRef[]
get(id: string): IntegrationRef | undefined
list(): readonly ReadonlyDeep<IntegrationRef>[]
get(id: string): ReadonlyDeep<IntegrationRef> | undefined
update(id: string, update: (integration: IntegrationRef) => void): void
remove(id: string): void
readonly method: {
list(integrationID: string): readonly IntegrationMethod[]
list(integrationID: string): readonly ReadonlyDeep<IntegrationMethod>[]
update(input: IntegrationMethodRegistration): void
remove(integrationID: string, method: IntegrationMethod): void
}
+10
View File
@@ -1 +1,11 @@
export type PluginOptions = Readonly<Record<string, any>>
export type ReadonlyDeep<T> = T extends (...args: any[]) => any
? T
: T extends ReadonlyMap<infer K, infer V>
? ReadonlyMap<ReadonlyDeep<K>, ReadonlyDeep<V>>
: T extends readonly (infer U)[]
? readonly ReadonlyDeep<U>[]
: T extends object
? { readonly [K in keyof T]: ReadonlyDeep<T[K]> }
: T
+3 -2
View File
@@ -1,16 +1,17 @@
import type { LanguageModelV3 } from "@ai-sdk/provider"
import type { ModelV2Info } from "@opencode-ai/sdk/v2/types"
import type { ReadonlyDeep } from "../options.js"
import type { Hooks } from "./registration.js"
export type AISDKHooks = Hooks<{
sdk: {
readonly model: ModelV2Info
readonly model: ReadonlyDeep<ModelV2Info>
readonly package: string
readonly options: Record<string, any>
sdk?: any
}
language: {
readonly model: ModelV2Info
readonly model: ReadonlyDeep<ModelV2Info>
readonly sdk: any
readonly options: Record<string, any>
language?: LanguageModelV3
+2 -2
View File
@@ -3,7 +3,7 @@ import { SessionInput } from "@opencode-ai/schema/session-input"
import { Prompt } from "@opencode-ai/schema/prompt"
import { Session } from "@opencode-ai/schema/session"
import { Project } from "@opencode-ai/schema/project"
import { AbsolutePath, PositiveInt, RelativePath, withStatics } from "@opencode-ai/schema/schema"
import { AbsolutePath, PositiveInt, RelativePath, statics } from "@opencode-ai/schema/schema"
import { Workspace } from "@opencode-ai/schema/workspace"
import { Context, Encoding, Result, Schema, Struct } from "effect"
import { HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"
@@ -62,7 +62,7 @@ const decodeSessionsCursor = Schema.decodeUnknownEffect(SessionsCursorJson)
export const SessionsCursor = Schema.String.pipe(
Schema.brand("SessionsCursor"),
withStatics((schema) => {
statics((schema) => {
const make = schema.make.bind(schema)
return {
make: (input: typeof SessionsCursorInput.Type) => make(Encoding.encodeBase64Url(encodeSessionsCursor(input))),
+88
View File
@@ -0,0 +1,88 @@
# Schema Package Guide
`@opencode-ai/schema` owns browser-safe wire and storage contracts shared by protocol, server, core, and generated SDKs. Keep runtime behavior, service layers, side effects, and host-local implementation details in the domain package that owns them.
## Package Boundary
- Preserve the dependency direction: `@opencode-ai/schema <- @opencode-ai/protocol <- @opencode-ai/server`.
- Schema values should be serializable contract definitions, not service implementations or runtime registries.
- A domain may keep a minimal public wire contract here when SDK generation needs it, but do not move the broader runtime model into Schema just because an event is public. `plugin.added` is the current example: Schema may own the minimum browser-safe event payload, while plugin runtime behavior stays outside Schema.
- The root barrel exports canonical current domain contracts. Specialized event modules, manifests, infrastructure modules, and V1 contracts use direct entrypoints instead of becoming first-class root exports.
## Current Versus V1
- Current contracts are unversioned: use names like `Session`, `Permission`, `Question`, and identifiers like `Permission.Request`.
- Legacy contracts retained for active compatibility, persistence, or migration are explicitly `V1`: use names like `SessionV1`, `PermissionV1`, and identifiers like `PermissionV1.Request`.
- Do not preserve `V2` as the permanent name for the replacement architecture. Remove `V2` from current namespaces, brands, and identifiers as the contracts are normalized.
- Retained V1 contracts should live under a dedicated `src/v1/` subtree once the V1 isolation PR runs. New/current code must not depend on that subtree.
- V1 coexistence is temporary. Keep compatibility entrypoints only where migration requires them, and delete the V1 subtree when the legacy runtime is retired.
- `@opencode-ai/protocol` and `@opencode-ai/sdk-next` are current `/api/...` surfaces.
## Events
- Classify event definitions by protocol role before adding them to a public manifest: `current`, `shared transitional`, or `V1-only`.
- Being emitted by V1 is not enough to include an event in Protocol or SDK Next.
- Keep clearly V1-only events, such as `message.updated` and `message.part.*`, out of the current Protocol/SDK Next event surface unless a current-client requirement is documented.
- Keep compatibility events available only to the existing App/TUI/CLI compatibility surface while they are still needed.
- Preserve a single canonical event definition. Do not duplicate definitions for generation convenience.
## Module Shape
- Use one canonical exported value for each contract. Avoid bridge aliases such as `PluginID`, `PluginEvent`, `PtyInfo`, `PtyEvent`, and `SessionTodoInfo`.
- Prefer importing the schema module namespace and reading canonical members, for example `Plugin.ID` or `SessionTodo.Info`.
- Core may compose Schema contracts with runtime behavior into a deliberate domain facade, but the facade must re-export the exact canonical Schema value. Do not create a second schema identity.
- Use flat top-level exports plus the package's existing namespace projection pattern, for example `export * as SessionMessage from "./session-message"`.
- Keep standalone ID modules only when they prevent real cycles or heavy dependency edges. Inline one-off IDs into their owning contract module when no cycle exists.
## Naming
- Exported schema values and namespace objects use `PascalCase`.
- Schema-building functions and combinators use `camelCase`.
- The package's static-method combinator is `statics(...)`.
- Keep descriptive schema value names such as `PositiveInt`, `NonNegativeInt`, `AbsolutePath`, `RelativePath`, and `DateTimeUtcFromMillis`.
## Optional Fields And Defaults
- Use the package `optional(...)` helper for optional object properties, including nested structs and event payloads, so encoded objects omit `undefined` keys.
- Use raw `Schema.optional(...)` only when preserving `undefined` as an explicitly encoded property is intentional and documented.
- External convenience defaults are normally decode-only with `Schema.withDecodingDefault(...)`.
- Add constructor defaults only when the domain value itself requires construction-time normalization.
## Public Types
- Public `Schema.Struct` records use same-name interfaces:
```ts
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({ ... })
```
- Use type aliases for unions, scalars, arrays, branded scalar types, and event payload helper types.
- Closed documented string sets use `Schema.Literals(...)`. If arbitrary strings are valid, document the field as arbitrary rather than listing a closed set.
## Mutability
- Public Schema contracts are readonly by default.
- Do not use `Schema.mutable(...)` in public contracts for runtime convenience.
- Runtime code that needs mutation should opt in at the boundary with `Types.DeepMutable`, a purpose-built draft type, or another explicit mutable API.
## Unknown Values
- Current public contracts avoid `Schema.Any`.
- Use `Schema.Json` for values that must be JSON-serializable.
- Use `Schema.Unknown` for genuinely opaque values that require consumer-side narrowing.
- Keep `Schema.Any` only at an explicitly unsafe compatibility boundary with a documented reason.
## IDs And Identifiers
- Current ID constructors expose `create()`.
- Directional constructors such as `ascending()` or `descending()` remain only where ordering semantics are part of the public contract or compatibility requires the old method.
- New generated ID schemas must validate exactly the prefix they emit, including the underscore.
- Do not tighten legacy loose ID validators without an explicit compatibility and migration decision; existing callers and tests may rely on accepted non-canonical IDs.
- Reusable exported public schemas get stable, domain-qualified identifiers such as `Model.Ref` or `Agent.Color`.
- Public schema identifiers and brands must be unique and stable. Private one-use nested schemas may remain anonymous.
## Tests For Contract Changes
- Add focused tests when changing contract behavior or generated surface.
- Cover optional properties omitting `undefined`, no accidental current-contract `Schema.Any`, stable and unique public identifiers, exact facade/schema identity, and current Protocol manifests excluding V1-only events.
+9 -8
View File
@@ -1,10 +1,11 @@
export * as Agent from "./agent"
import { Schema } from "effect"
import { optional } from "./schema"
import { Model } from "./model"
import { Permission } from "./permission"
import { Provider } from "./provider"
import { PositiveInt, withStatics } from "./schema"
import { PositiveInt, statics } from "./schema"
export const ID = Schema.String.pipe(Schema.brand("AgentV2.ID"))
export type ID = typeof ID.Type
@@ -12,25 +13,25 @@ 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"]),
])
]).annotate({ identifier: "Agent.Color" })
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),
model: Model.Ref.pipe(optional),
request: Provider.Request,
system: Schema.String.pipe(Schema.optional),
description: Schema.String.pipe(Schema.optional),
system: Schema.String.pipe(optional),
description: Schema.String.pipe(optional),
mode: Schema.Literals(["subagent", "primary", "all"]),
hidden: Schema.Boolean,
color: Color.pipe(Schema.optional),
steps: PositiveInt.pipe(Schema.optional),
color: Color.pipe(optional),
steps: PositiveInt.pipe(optional),
permissions: Permission.Ruleset,
})
.annotate({ identifier: "AgentV2.Info" })
.pipe(
withStatics((schema) => ({
statics((schema) => ({
empty: (id: ID) =>
schema.make({ id, request: { headers: {}, body: {} }, mode: "all", hidden: false, permissions: [] }),
})),
+5 -4
View File
@@ -1,14 +1,15 @@
export * as Command from "./command"
import { Schema } from "effect"
import { optional } from "./schema"
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),
description: Schema.String.pipe(optional),
agent: Schema.String.pipe(optional),
model: Model.Ref.pipe(optional),
subtask: Schema.Boolean.pipe(optional),
}).annotate({ identifier: "CommandV2.Info" })
+5 -4
View File
@@ -1,13 +1,14 @@
export * as Credential from "./credential"
import { Schema } from "effect"
import { optional } from "./schema"
import { IntegrationMethodID } from "./integration-id"
import { ascending } from "./identifier"
import { NonNegativeInt, withStatics } from "./schema"
import { NonNegativeInt, statics } from "./schema"
export const ID = Schema.String.pipe(
Schema.brand("Credential.ID"),
withStatics((schema) => ({ create: () => schema.make("cred_" + ascending()) })),
statics((schema) => ({ create: () => schema.make("cred_" + ascending()) })),
)
export type ID = typeof ID.Type
@@ -18,14 +19,14 @@ export const OAuth = Schema.Struct({
refresh: Schema.String,
access: Schema.String,
expires: NonNegativeInt,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
metadata: optional(Schema.Record(Schema.String, Schema.Unknown)),
}).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)),
metadata: optional(Schema.Record(Schema.String, Schema.Unknown)),
}).annotate({ identifier: "Credential.Key" })
export const Value = Schema.Union([OAuth, Key])
+6 -7
View File
@@ -1,13 +1,14 @@
export * as Event from "./event"
import { Schema } from "effect"
import { optional } from "./schema"
import { ascending } from "./identifier"
import { Location } from "./location"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe(
Schema.brand("Event.ID"),
withStatics((schema) => ({ create: () => schema.make("evt_" + ascending()) })),
statics((schema) => ({ create: () => schema.make("evt_" + ascending()) })),
)
export type ID = typeof ID.Type
@@ -53,12 +54,10 @@ export function define<
return Object.assign(
Schema.Struct({
id: ID,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
metadata: optional(Schema.Record(Schema.String, Schema.Unknown)),
type: Schema.Literal(input.type),
durable: Schema.optional(
Schema.Struct({ aggregateID: Schema.String, seq: Schema.Number, version: Schema.Number }),
),
location: Schema.optional(Location.Ref),
durable: optional(Schema.Struct({ aggregateID: Schema.String, seq: Schema.Number, version: Schema.Number })),
location: optional(Location.Ref),
data,
}).annotate({ identifier: input.type }),
{
+5 -4
View File
@@ -1,12 +1,13 @@
export * as FileDiff from "./file-diff"
import { Schema } from "effect"
import { optional } from "./schema"
export const Info = Schema.Struct({
file: Schema.optional(Schema.String),
patch: Schema.optional(Schema.String),
file: optional(Schema.String),
patch: optional(Schema.String),
additions: Schema.Finite,
deletions: Schema.Finite,
status: Schema.optional(Schema.Literals(["added", "deleted", "modified"])),
status: optional(Schema.Literals(["added", "deleted", "modified"])),
}).annotate({ identifier: "SnapshotFileDiff" })
export type Info = typeof Info.Type
export interface Info extends Schema.Schema.Type<typeof Info> {}
+4 -3
View File
@@ -1,6 +1,7 @@
export * as FileSystem from "./filesystem"
import { Schema } from "effect"
import { optional } from "./schema"
import { define, inventory } from "./event"
import { NonNegativeInt, PositiveInt, RelativePath } from "./schema"
@@ -21,7 +22,7 @@ export const Submatch = Schema.Struct({
text: Schema.String,
start: NonNegativeInt,
end: NonNegativeInt,
})
}).annotate({ identifier: "FileSystem.Submatch" })
export interface Match extends Schema.Schema.Type<typeof Match> {}
export const Match = Schema.Struct({
@@ -34,6 +35,6 @@ export const Match = Schema.Struct({
export class FindInput extends Schema.Class<FindInput>("FileSystem.FindInput")({
query: Schema.String,
type: Schema.Literals(["file", "directory"]).pipe(Schema.optional),
limit: PositiveInt.pipe(Schema.optional),
type: Schema.Literals(["file", "directory"]).pipe(optional),
limit: PositiveInt.pipe(optional),
}) {}
+20 -19
View File
@@ -1,10 +1,11 @@
export * as Integration from "./integration"
import { Schema } from "effect"
import { optional } from "./schema"
import { define, inventory } from "./event"
import { Connection } from "./connection"
import { ascending } from "./identifier"
import { withStatics } from "./schema"
import { statics } from "./schema"
import { IntegrationID, IntegrationMethodID } from "./integration-id"
export const ID = IntegrationID
@@ -25,8 +26,8 @@ export const TextPrompt = Schema.Struct({
type: Schema.Literal("text"),
key: Schema.String,
message: Schema.String,
placeholder: Schema.optional(Schema.String),
when: Schema.optional(When),
placeholder: optional(Schema.String),
when: optional(When),
}).annotate({ identifier: "Integration.TextPrompt" })
export interface SelectPrompt extends Schema.Schema.Type<typeof SelectPrompt> {}
@@ -34,16 +35,14 @@ 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),
}),
),
options: Schema.Array(
Schema.Struct({
label: Schema.String,
value: Schema.String,
hint: optional(Schema.String),
}),
),
when: Schema.optional(When),
when: optional(When),
}).annotate({ identifier: "Integration.SelectPrompt" })
export const Prompt = Schema.Union([TextPrompt, SelectPrompt]).pipe(Schema.toTaggedUnion("type"))
@@ -54,19 +53,19 @@ export const OAuthMethod = Schema.Struct({
id: MethodID,
type: Schema.Literal("oauth"),
label: Schema.String,
prompts: Schema.optional(Schema.mutable(Schema.Array(Prompt))),
prompts: optional(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),
label: 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)),
names: Schema.Array(Schema.String),
}).annotate({ identifier: "Integration.EnvMethod" })
export const Method = Schema.Union([OAuthMethod, KeyMethod, EnvMethod])
@@ -96,13 +95,13 @@ export const Ref = Schema.Struct({
export class Info extends Schema.Class<Info>("Integration.Info")({
id: ID,
name: Schema.String,
methods: Schema.mutable(Schema.Array(Method)),
connections: Schema.mutable(Schema.Array(Connection.Info)),
methods: Schema.Array(Method),
connections: Schema.Array(Connection.Info),
}) {}
export const AttemptID = Schema.String.pipe(
Schema.brand("Integration.AttemptID"),
withStatics((schema) => ({ create: () => schema.make("con_" + ascending()) })),
statics((schema) => ({ create: () => schema.make("con_" + ascending()) })),
)
export type AttemptID = typeof AttemptID.Type
@@ -124,5 +123,7 @@ export const AttemptStatus = Schema.Union([
Schema.Struct({ status: Schema.Literal("complete"), time: AttemptTime }),
Schema.Struct({ status: Schema.Literal("failed"), message: Schema.String, time: AttemptTime }),
Schema.Struct({ status: Schema.Literal("expired"), time: AttemptTime }),
]).pipe(Schema.toTaggedUnion("status"))
])
.pipe(Schema.toTaggedUnion("status"))
.annotate({ identifier: "Integration.AttemptStatus" })
export type AttemptStatus = typeof AttemptStatus.Type
+1 -18
View File
@@ -1,18 +1 @@
export * as LegacyEvent from "./legacy-event"
import { Schema } from "effect"
import { define, inventory } from "./event"
import { SessionID } from "./session-id"
import { SessionV1 } from "./session-v1"
export const CommandExecuted = define({
type: "command.executed",
schema: {
name: Schema.String,
sessionID: SessionID,
arguments: Schema.String,
messageID: SessionV1.MessageID,
},
})
export const Definitions = inventory(CommandExecuted)
export * from "./v1/legacy-event"
+8 -3
View File
@@ -1,8 +1,11 @@
export * as LLM from "./llm"
import { Schema } from "effect"
import { optional } from "./schema"
export const ProviderMetadata = Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown))
export const ProviderMetadata = Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown)).annotate({
identifier: "LLM.ProviderMetadata",
})
export type ProviderMetadata = Schema.Schema.Type<typeof ProviderMetadata>
export interface ToolTextContent extends Schema.Schema.Type<typeof ToolTextContent> {}
@@ -16,8 +19,10 @@ export const ToolFileContent = Schema.Struct({
type: Schema.Literal("file"),
uri: Schema.String,
mime: Schema.String,
name: Schema.optional(Schema.String),
name: optional(Schema.String),
}).annotate({ identifier: "Tool.FileContent" })
export const ToolContent = Schema.Union([ToolTextContent, ToolFileContent]).pipe(Schema.toTaggedUnion("type"))
export const ToolContent = Schema.Union([ToolTextContent, ToolFileContent])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "LLM.ToolContent" })
export type ToolContent = Schema.Schema.Type<typeof ToolContent>
+4 -7
View File
@@ -1,22 +1,19 @@
export * as Location from "./location"
import { Effect, Schema } from "effect"
import { AbsolutePath, optionalOmitUndefined } from "./schema"
import { Schema } from "effect"
import { AbsolutePath, optional } from "./schema"
import { ProjectID } from "./project-id"
import { WorkspaceID } from "./workspace-id"
export interface Ref extends Schema.Schema.Type<typeof Ref> {}
export const Ref = Schema.Struct({
directory: AbsolutePath,
workspaceID: Schema.optional(WorkspaceID).pipe(
Schema.withDecodingDefault(Effect.succeed(undefined)),
Schema.withConstructorDefault(Effect.succeed(undefined)),
),
workspaceID: optional(WorkspaceID),
}).annotate({ identifier: "Location.Ref" })
export class Info extends Schema.Class<Info>("Location.Info")({
directory: AbsolutePath,
workspaceID: optionalOmitUndefined(WorkspaceID),
workspaceID: optional(WorkspaceID),
project: Schema.Struct({
id: ProjectID,
directory: AbsolutePath,
+19 -16
View File
@@ -1,8 +1,9 @@
export * as Model from "./model"
import { Schema } from "effect"
import { optional } from "./schema"
import { Provider } from "./provider"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const ID = Schema.String.pipe(Schema.brand("ModelV2.ID"))
export type ID = typeof ID.Type
@@ -13,9 +14,9 @@ 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
variant: VariantID.pipe(optional),
}).annotate({ identifier: "Model.Ref" })
export interface Ref extends Schema.Schema.Type<typeof Ref> {}
export const Family = Schema.String.pipe(Schema.brand("Family"))
export type Family = typeof Family.Type
@@ -23,23 +24,23 @@ 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),
})
input: Schema.Array(Schema.String),
output: Schema.Array(Schema.String),
}).annotate({ identifier: "Model.Capabilities" })
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),
}).pipe(optional),
input: Schema.Finite,
output: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
})
}).annotate({ identifier: "Model.Cost" })
export const Api = Schema.Union([
Schema.Struct({
@@ -50,40 +51,42 @@ export const Api = Schema.Union([
id: ID,
...Provider.Native.fields,
}),
]).pipe(Schema.toTaggedUnion("type"))
])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "Model.Api" })
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),
family: Family.pipe(optional),
name: Schema.String,
api: Api,
capabilities: Capabilities,
request: Schema.Struct({
...Provider.Request.fields,
variant: Schema.String.pipe(Schema.optional),
variant: Schema.String.pipe(optional),
}),
variants: Schema.Struct({
id: VariantID,
...Provider.Request.fields,
}).pipe(Schema.Array, Schema.mutable),
}).pipe(Schema.Array),
time: Schema.Struct({
released: Schema.Finite,
}),
cost: Cost.pipe(Schema.Array, Schema.mutable),
cost: Schema.Array(Cost),
status: Schema.Literals(["alpha", "beta", "deprecated", "active"]),
enabled: Schema.Boolean,
limit: Schema.Struct({
context: Schema.Int,
input: Schema.Int.pipe(Schema.optional),
input: Schema.Int.pipe(optional),
output: Schema.Int,
}),
})
.annotate({ identifier: "ModelV2.Info" })
.pipe(
withStatics((schema) => ({
statics((schema) => ({
empty: (providerID: Provider.ID, modelID: ID) =>
schema.make({
id: modelID,
+3 -3
View File
@@ -3,11 +3,11 @@ export * as PermissionSaved from "./permission-saved"
import { Schema } from "effect"
import { ascending } from "./identifier"
import { ProjectID } from "./project-id"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const ID = Schema.String.pipe(
Schema.brand("PermissionSaved.ID"),
withStatics((schema) => ({ create: () => schema.make("psv_" + ascending()) })),
statics((schema) => ({ create: () => schema.make("psv_" + ascending()) })),
)
export type ID = typeof ID.Type
@@ -17,4 +17,4 @@ export const Info = Schema.Struct({
action: Schema.String,
resource: Schema.String,
}).annotate({ identifier: "PermissionSaved.Info" })
export type Info = typeof Info.Type
export interface Info extends Schema.Schema.Type<typeof Info> {}
+1 -67
View File
@@ -1,67 +1 @@
export * as PermissionV1 from "./permission-v1"
import { Schema } from "effect"
import { define, inventory } from "./event"
import { ascending } from "./identifier"
import { Project } from "./project"
import { withStatics } from "./schema"
import { SessionID } from "./session-id"
export const ID = Schema.String.check(Schema.isStartsWith("per")).pipe(
Schema.brand("PermissionID"),
withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
)
export type ID = typeof ID.Type
export const Action = Schema.Literals(["allow", "deny", "ask"]).annotate({ identifier: "PermissionAction" })
export type Action = typeof Action.Type
export const Rule = Schema.Struct({ permission: Schema.String, pattern: Schema.String, action: Action }).annotate({
identifier: "PermissionRule",
})
export type Rule = typeof Rule.Type
export const Ruleset = Schema.Array(Rule).annotate({ identifier: "PermissionRuleset" })
export type Ruleset = typeof Ruleset.Type
export const Request = Schema.Struct({
id: ID,
sessionID: SessionID,
permission: Schema.String,
patterns: Schema.Array(Schema.String),
metadata: Schema.Record(Schema.String, Schema.Unknown),
always: Schema.Array(Schema.String),
tool: Schema.optional(Schema.Struct({ messageID: Schema.String, callID: Schema.String })),
}).annotate({ identifier: "PermissionRequest" })
export type Request = typeof Request.Type
export const Reply = Schema.Literals(["once", "always", "reject"])
export type Reply = typeof Reply.Type
export const ReplyBody = Schema.Struct({ reply: Reply, message: Schema.optional(Schema.String) }).annotate({
identifier: "PermissionReplyBody",
})
export type ReplyBody = typeof ReplyBody.Type
export const Approval = Schema.Struct({ projectID: Project.ID, patterns: Schema.Array(Schema.String) }).annotate({
identifier: "PermissionApproval",
})
export type Approval = typeof Approval.Type
export const AskInput = Schema.Struct({ ...Request.fields, id: Schema.optional(ID), ruleset: Ruleset }).annotate({
identifier: "PermissionAskInput",
})
export type AskInput = typeof AskInput.Type
export const ReplyInput = Schema.Struct({ requestID: ID, ...ReplyBody.fields }).annotate({
identifier: "PermissionReplyInput",
})
export type ReplyInput = typeof ReplyInput.Type
const Asked = define({ type: "permission.asked", schema: Request.fields })
const Replied = define({
type: "permission.replied",
schema: { sessionID: SessionID, requestID: ID, reply: Reply },
})
export const Event = { Asked, Replied, Definitions: inventory(Asked, Replied) }
export const PermissionV1Event = Event
export * from "./v1/permission"
+8 -7
View File
@@ -1,14 +1,15 @@
export * as Permission from "./permission"
import { Schema } from "effect"
import { optional } from "./schema"
import { define, inventory } from "./event"
import { ascending } from "./identifier"
import { SessionID } from "./session-id"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const ID = Schema.String.check(Schema.isStartsWith("per")).pipe(
Schema.brand("PermissionV2.ID"),
withStatics((schema) => ({ create: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
statics((schema) => ({ create: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
)
export type ID = typeof ID.Type
@@ -25,16 +26,16 @@ const RequestFields = {
sessionID: SessionID,
action: Schema.String,
resources: Schema.Array(Schema.String),
save: Schema.Array(Schema.String).pipe(Schema.optional),
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
source: Source.pipe(Schema.optional),
save: Schema.Array(Schema.String).pipe(optional),
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(optional),
source: Source.pipe(optional),
}
export const Request = Schema.Struct({
id: ID,
...RequestFields,
}).annotate({ identifier: "PermissionV2.Request" })
export type Request = typeof Request.Type
export interface Request extends Schema.Schema.Type<typeof Request> {}
export const Reply = Schema.Literals(["once", "always", "reject"]).annotate({ identifier: "PermissionV2.Reply" })
export type Reply = typeof Reply.Type
@@ -60,5 +61,5 @@ export const Rule = Schema.Struct({
effect: Effect,
}).annotate({ identifier: "PermissionV2.Rule" })
export const Ruleset = Schema.mutable(Schema.Array(Rule)).annotate({ identifier: "PermissionV2.Ruleset" })
export const Ruleset = Schema.Array(Rule).annotate({ identifier: "PermissionV2.Ruleset" })
export type Ruleset = typeof Ruleset.Type
-2
View File
@@ -5,11 +5,9 @@ import { define, inventory } from "./event"
export const ID = Schema.String.pipe(Schema.brand("Plugin.ID"))
export type ID = typeof ID.Type
export const PluginID = ID
const Added = define({
type: "plugin.added",
schema: { id: ID },
})
export const Event = { Added, Definitions: inventory(Added) }
export const PluginEvent = Event
+5 -4
View File
@@ -1,6 +1,7 @@
export * as ProjectCopy from "./project-copy"
import { Schema } from "effect"
import { optional } from "./schema"
import { ProjectID } from "./project-id"
import { AbsolutePath } from "./schema"
@@ -12,18 +13,18 @@ export const CreateInput = Schema.Struct({
strategy: StrategyID,
sourceDirectory: AbsolutePath,
directory: AbsolutePath,
name: Schema.optional(Schema.String),
name: optional(Schema.String),
}).annotate({ identifier: "ProjectCopy.CreateInput" })
export type CreateInput = typeof CreateInput.Type
export interface CreateInput extends Schema.Schema.Type<typeof CreateInput> {}
export const RemoveInput = Schema.Struct({
projectID: ProjectID,
directory: AbsolutePath,
force: Schema.Boolean,
}).annotate({ identifier: "ProjectCopy.RemoveInput" })
export type RemoveInput = typeof RemoveInput.Type
export interface RemoveInput extends Schema.Schema.Type<typeof RemoveInput> {}
export const Copy = Schema.Struct({
directory: AbsolutePath,
}).annotate({ identifier: "ProjectCopy.Copy" })
export type Copy = typeof Copy.Type
export interface Copy extends Schema.Schema.Type<typeof Copy> {}
@@ -8,4 +8,3 @@ const Updated = define({
schema: { projectID: Project.ID },
})
export const Event = { Updated, Definitions: inventory(Updated) }
export const ProjectDirectoriesEvent = Event
+2 -2
View File
@@ -1,8 +1,8 @@
import { Schema } from "effect"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const ProjectID = Schema.String.pipe(
Schema.brand("Project.ID"),
withStatics((schema) => ({ global: schema.make("global") })),
statics((schema) => ({ global: schema.make("global") })),
)
export type ProjectID = typeof ProjectID.Type
+18 -15
View File
@@ -2,40 +2,43 @@ export * as Project from "./project"
import { Schema } from "effect"
import { define, inventory } from "./event"
import { NonNegativeInt, optionalOmitUndefined } from "./schema"
import { NonNegativeInt, optional } from "./schema"
import { ProjectID } from "./project-id"
export const ID = ProjectID
export type ID = typeof ID.Type
export const Vcs = Schema.Literal("git")
export const Vcs = Schema.Literal("git").annotate({ identifier: "Project.Vcs" })
export const Icon = Schema.Struct({
url: optionalOmitUndefined(Schema.String),
override: optionalOmitUndefined(Schema.String),
color: optionalOmitUndefined(Schema.String),
})
url: optional(Schema.String),
override: optional(Schema.String),
color: optional(Schema.String),
}).annotate({ identifier: "Project.Icon" })
export interface Icon extends Schema.Schema.Type<typeof Icon> {}
export const Commands = Schema.Struct({
start: optionalOmitUndefined(
start: optional(
Schema.String.annotate({ description: "Startup script to run when creating a new workspace (worktree)" }),
),
})
}).annotate({ identifier: "Project.Commands" })
export interface Commands extends Schema.Schema.Type<typeof Commands> {}
export const Time = Schema.Struct({
created: NonNegativeInt,
updated: NonNegativeInt,
initialized: optionalOmitUndefined(NonNegativeInt),
})
initialized: optional(NonNegativeInt),
}).annotate({ identifier: "Project.Time" })
export interface Time extends Schema.Schema.Type<typeof Time> {}
export const Info = Schema.Struct({
id: ID,
worktree: Schema.String,
vcs: optionalOmitUndefined(Vcs),
name: optionalOmitUndefined(Schema.String),
icon: optionalOmitUndefined(Icon),
commands: optionalOmitUndefined(Commands),
vcs: optional(Vcs),
name: optional(Schema.String),
icon: optional(Icon),
commands: optional(Commands),
time: Time,
sandboxes: Schema.Array(Schema.String),
}).annotate({ identifier: "Project" })
export type Info = typeof Info.Type
export interface Info extends Schema.Schema.Type<typeof Info> {}
const Updated = define({ type: "project.updated", schema: Info.fields })
export const Event = { Updated, Definitions: inventory(Updated) }
+10 -9
View File
@@ -1,5 +1,6 @@
import { Schema } from "effect"
import { withStatics } from "./schema"
import { optional } from "./schema"
import { statics } from "./schema"
export interface Source extends Schema.Schema.Type<typeof Source> {}
export const Source = Schema.Struct({
@@ -12,13 +13,13 @@ 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),
name: Schema.String.pipe(optional),
description: Schema.String.pipe(optional),
source: Source.pipe(optional),
})
.annotate({ identifier: "Prompt.FileAttachment" })
.pipe(
withStatics((schema) => ({
statics((schema) => ({
create: (input: FileAttachment) =>
schema.make({
uri: input.uri,
@@ -33,18 +34,18 @@ export const FileAttachment = Schema.Struct({
export interface AgentAttachment extends Schema.Schema.Type<typeof AgentAttachment> {}
export const AgentAttachment = Schema.Struct({
name: Schema.String,
source: Source.pipe(Schema.optional),
source: Source.pipe(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),
files: Schema.Array(FileAttachment).pipe(optional),
agents: Schema.Array(AgentAttachment).pipe(optional),
})
.annotate({ identifier: "Prompt" })
.pipe(
withStatics((schema) => ({
statics((schema) => ({
equivalence: Schema.toEquivalence(schema),
fromUserMessage: (input: Pick<Prompt, "text" | "files" | "agents">) =>
schema.make({
+16 -13
View File
@@ -1,12 +1,13 @@
export * as Provider from "./provider"
import { Schema } from "effect"
import { optional } from "./schema"
import { Integration } from "./integration"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const ID = Schema.String.pipe(
Schema.brand("ProviderV2.ID"),
withStatics((schema) => ({
statics((schema) => ({
opencode: schema.make("opencode"),
anthropic: schema.make("anthropic"),
openai: schema.make("openai"),
@@ -26,38 +27,40 @@ 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),
})
url: Schema.String.pipe(optional),
settings: Schema.Record(Schema.String, Schema.Unknown).pipe(optional),
}).annotate({ identifier: "Provider.AISDK" })
export interface Native extends Schema.Schema.Type<typeof Native> {}
export const Native = Schema.Struct({
type: Schema.Literal("native"),
url: Schema.String.pipe(Schema.optional),
url: Schema.String.pipe(optional),
settings: Schema.Record(Schema.String, Schema.Unknown),
})
}).annotate({ identifier: "Provider.Native" })
export const Api = Schema.Union([AISDK, Native]).pipe(Schema.toTaggedUnion("type"))
export const Api = Schema.Union([AISDK, Native])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "Provider.Api" })
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),
})
body: Schema.Record(Schema.String, Schema.Json),
}).annotate({ identifier: "Provider.Request" })
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
id: ID,
integrationID: Integration.ID.pipe(Schema.optional),
integrationID: Integration.ID.pipe(optional),
name: Schema.String,
disabled: Schema.Boolean.pipe(Schema.optional),
disabled: Schema.Boolean.pipe(optional),
api: Api,
request: Request,
})
.annotate({ identifier: "ProviderV2.Info" })
.pipe(
withStatics((schema) => ({
statics((schema) => ({
empty: (id: ID) =>
schema.make({
id,
+2 -1
View File
@@ -6,4 +6,5 @@ import { PositiveInt } from "./schema"
export const ConnectToken = Schema.Struct({
ticket: Schema.String,
expires_in: PositiveInt,
})
}).annotate({ identifier: "PtyTicket.ConnectToken" })
export interface ConnectToken extends Schema.Schema.Type<typeof ConnectToken> {}
+20 -17
View File
@@ -1,17 +1,21 @@
export * as Pty from "./pty"
import { Schema } from "effect"
import { optional } from "./schema"
import { define, inventory } from "./event"
import { ascending } from "./identifier"
import { NonNegativeInt, PositiveInt } from "./schema"
import { withStatics } from "./schema"
import { NonNegativeInt, PositiveInt, statics } from "./schema"
const IDSchema = Schema.String.check(Schema.isStartsWith("pty")).pipe(Schema.brand("PtyID"))
export const ID = IDSchema.pipe(
withStatics((schema: typeof IDSchema) => ({
ascending: (id?: string) => schema.make(id ?? "pty_" + ascending()),
})),
statics((schema: typeof IDSchema) => {
const create = () => schema.make("pty_" + ascending())
return {
create,
ascending: (id?: string) => (id === undefined ? create() : schema.make(id)),
}
}),
)
export type ID = typeof ID.Type
@@ -23,33 +27,32 @@ export const Info = Schema.Struct({
cwd: Schema.String,
status: Schema.Literals(["running", "exited"]),
pid: NonNegativeInt,
exitCode: Schema.optional(NonNegativeInt),
exitCode: optional(NonNegativeInt),
}).annotate({ identifier: "Pty" })
export const PtyInfo = Info
export interface Info extends Schema.Schema.Type<typeof Info> {}
const Created = define({ type: "pty.created", schema: { info: Info } })
const Updated = define({ type: "pty.updated", schema: { info: Info } })
const Exited = define({ type: "pty.exited", schema: { id: ID, exitCode: NonNegativeInt } })
const Deleted = define({ type: "pty.deleted", schema: { id: ID } })
export const Event = { Created, Updated, Exited, Deleted, Definitions: inventory(Created, Updated, Exited, Deleted) }
export const PtyEvent = Event
export const CreateInput = Schema.Struct({
command: Schema.optional(Schema.String),
args: Schema.optional(Schema.Array(Schema.String)),
cwd: Schema.optional(Schema.String),
title: Schema.optional(Schema.String),
env: Schema.optional(Schema.Record(Schema.String, Schema.String)),
command: optional(Schema.String),
args: optional(Schema.Array(Schema.String)),
cwd: optional(Schema.String),
title: optional(Schema.String),
env: optional(Schema.Record(Schema.String, Schema.String)),
})
export type CreateInput = typeof CreateInput.Type
export interface CreateInput extends Schema.Schema.Type<typeof CreateInput> {}
export const UpdateInput = Schema.Struct({
title: Schema.optional(Schema.String),
size: Schema.optional(
title: optional(Schema.String),
size: optional(
Schema.Struct({
rows: PositiveInt,
cols: PositiveInt,
}),
),
})
export type UpdateInput = typeof UpdateInput.Type
export interface UpdateInput extends Schema.Schema.Type<typeof UpdateInput> {}
+1 -66
View File
@@ -1,66 +1 @@
export * as QuestionV1 from "./question-v1"
import { Schema } from "effect"
import { define, inventory } from "./event"
import { ascending } from "./identifier"
import { withStatics } from "./schema"
import { SessionID } from "./session-id"
import { SessionV1 } from "./session-v1"
export const ID = Schema.String.check(Schema.isStartsWith("que")).pipe(
Schema.brand("QuestionID"),
withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "que_" + ascending()) })),
)
export const Option = Schema.Struct({
label: Schema.String.annotate({ description: "Display text (1-5 words, concise)" }),
description: Schema.String.annotate({ description: "Explanation of choice" }),
}).annotate({ identifier: "QuestionOption" })
const base = {
question: Schema.String.annotate({ description: "Complete question" }),
header: Schema.String.annotate({ description: "Very short label (max 30 chars)" }),
options: Schema.Array(Option).annotate({ description: "Available choices" }),
multiple: Schema.optional(Schema.Boolean).annotate({ description: "Allow selecting multiple choices" }),
}
export const Info = Schema.Struct({
...base,
custom: Schema.optional(Schema.Boolean).annotate({ description: "Allow typing a custom answer (default: true)" }),
}).annotate({ identifier: "QuestionInfo" })
export const Prompt = Schema.Struct(base).annotate({ identifier: "QuestionPrompt" })
export const Tool = Schema.Struct({ messageID: SessionV1.MessageID, callID: Schema.String }).annotate({
identifier: "QuestionTool",
})
export const Request = Schema.Struct({
id: ID,
sessionID: SessionID,
questions: Schema.Array(Info).annotate({ description: "Questions to ask" }),
tool: Schema.optional(Tool),
}).annotate({ identifier: "QuestionRequest" })
export const Answer = Schema.Array(Schema.String).annotate({ identifier: "QuestionAnswer" })
export const Reply = Schema.Struct({
answers: Schema.Array(Answer).annotate({
description: "User answers in order of questions (each answer is an array of selected labels)",
}),
}).annotate({ identifier: "QuestionReply" })
export const Replied = Schema.Struct({
sessionID: SessionID,
requestID: ID,
answers: Schema.Array(Answer),
}).annotate({
identifier: "QuestionReplied",
})
export const Rejected = Schema.Struct({ sessionID: SessionID, requestID: ID }).annotate({
identifier: "QuestionRejected",
})
const Asked = define({ type: "question.asked", schema: Request.fields })
const RepliedEvent = define({ type: "question.replied", schema: Replied.fields })
const RejectedEvent = define({ type: "question.rejected", schema: Rejected.fields })
export const Event = {
Asked,
Replied: RepliedEvent,
Rejected: RejectedEvent,
Definitions: inventory(Asked, RepliedEvent, RejectedEvent),
}
export * from "./v1/question"
+18 -11
View File
@@ -1,14 +1,21 @@
export * as Question from "./question"
import { Schema } from "effect"
import { optional } from "./schema"
import { define, inventory } from "./event"
import { ascending } from "./identifier"
import { SessionID } from "./session-id"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const ID = Schema.String.check(Schema.isStartsWith("que")).pipe(
Schema.brand("QuestionV2.ID"),
withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "que_" + ascending()) })),
statics((schema) => {
const create = () => schema.make("que_" + ascending())
return {
create,
ascending: (id?: string) => (id === undefined ? create() : schema.make(id)),
}
}),
)
export type ID = typeof ID.Type
@@ -16,39 +23,39 @@ export const Option = Schema.Struct({
label: Schema.String.annotate({ description: "Display text (1-5 words, concise)" }),
description: Schema.String.annotate({ description: "Explanation of choice" }),
}).annotate({ identifier: "QuestionV2.Option" })
export type Option = typeof Option.Type
export interface Option extends Schema.Schema.Type<typeof Option> {}
const base = {
question: Schema.String.annotate({ description: "Complete question" }),
header: Schema.String.annotate({ description: "Very short label (max 30 chars)" }),
options: Schema.Array(Option).annotate({ description: "Available choices" }),
multiple: Schema.Boolean.pipe(Schema.optional).annotate({ description: "Allow selecting multiple choices" }),
multiple: Schema.Boolean.pipe(optional).annotate({ description: "Allow selecting multiple choices" }),
}
export const Info = Schema.Struct({
...base,
custom: Schema.Boolean.pipe(Schema.optional).annotate({
custom: Schema.Boolean.pipe(optional).annotate({
description: "Allow typing a custom answer (default: true)",
}),
}).annotate({ identifier: "QuestionV2.Info" })
export type Info = typeof Info.Type
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Prompt = Schema.Struct(base).annotate({ identifier: "QuestionV2.Prompt" })
export type Prompt = typeof Prompt.Type
export interface Prompt extends Schema.Schema.Type<typeof Prompt> {}
export const Tool = Schema.Struct({
messageID: Schema.String,
callID: Schema.String,
}).annotate({ identifier: "QuestionV2.Tool" })
export type Tool = typeof Tool.Type
export interface Tool extends Schema.Schema.Type<typeof Tool> {}
export const Request = Schema.Struct({
id: ID,
sessionID: SessionID,
questions: Schema.Array(Info).annotate({ description: "Questions to ask" }),
tool: Tool.pipe(Schema.optional),
tool: Tool.pipe(optional),
}).annotate({ identifier: "QuestionV2.Request" })
export type Request = typeof Request.Type
export interface Request extends Schema.Schema.Type<typeof Request> {}
export const Answer = Schema.Array(Schema.String).annotate({ identifier: "QuestionV2.Answer" })
export type Answer = typeof Answer.Type
@@ -58,7 +65,7 @@ export const Reply = Schema.Struct({
description: "User answers in order of questions (each answer is an array of selected labels)",
}),
}).annotate({ identifier: "QuestionV2.Reply" })
export type Reply = typeof Reply.Type
export interface Reply extends Schema.Schema.Type<typeof Reply> {}
const Asked = define({ type: "question.v2.asked", schema: Request.fields })
const Replied = define({
+11 -8
View File
@@ -1,6 +1,7 @@
export * as Reference from "./reference"
import { Schema } from "effect"
import { optional } from "./schema"
import { define, inventory } from "./event"
import { AbsolutePath } from "./schema"
@@ -11,26 +12,28 @@ 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),
description: Schema.String.pipe(optional),
hidden: Schema.Boolean.pipe(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),
branch: Schema.String.pipe(optional),
description: Schema.String.pipe(optional),
hidden: Schema.Boolean.pipe(optional),
}).annotate({ identifier: "Reference.GitSource" })
export const Source = Schema.Union([LocalSource, GitSource]).pipe(Schema.toTaggedUnion("type"))
export const Source = Schema.Union([LocalSource, GitSource])
.pipe(Schema.toTaggedUnion("type"))
.annotate({ identifier: "Reference.Source" })
export type Source = typeof Source.Type
export class Info extends Schema.Class<Info>("Reference.Info")({
name: Schema.String,
path: AbsolutePath,
description: Schema.String.pipe(Schema.optional),
hidden: Schema.Boolean.pipe(Schema.optional),
description: Schema.String.pipe(optional),
hidden: Schema.Boolean.pipe(optional),
source: Source,
}) {}
+10 -9
View File
@@ -1,8 +1,9 @@
export * as Revert from "./revert"
import { Schema } from "effect"
import { optional } from "./schema"
import { NonNegativeInt, RelativePath } from "./schema"
import { SessionMessageID } from "./session-message-id"
import { SessionMessage } from "./session-message"
export const FileDiff = Schema.Struct({
path: RelativePath,
@@ -11,13 +12,13 @@ export const FileDiff = Schema.Struct({
deletions: NonNegativeInt,
patch: Schema.String,
}).annotate({ identifier: "File.Diff" })
export type FileDiff = typeof FileDiff.Type
export interface FileDiff extends Schema.Schema.Type<typeof FileDiff> {}
export const State = Schema.Struct({
messageID: SessionMessageID.ID,
partID: Schema.String.pipe(Schema.optional),
snapshot: Schema.String.pipe(Schema.optional),
diff: Schema.String.pipe(Schema.optional),
files: Schema.Array(FileDiff).pipe(Schema.optional),
})
export type State = typeof State.Type
messageID: SessionMessage.ID,
partID: Schema.String.pipe(optional),
snapshot: Schema.String.pipe(optional),
diff: Schema.String.pipe(optional),
files: Schema.Array(FileDiff).pipe(optional),
}).annotate({ identifier: "Revert.State" })
export interface State extends Schema.Schema.Type<typeof State> {}
+2 -2
View File
@@ -9,7 +9,7 @@ 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) =>
export const optional = <S extends Schema.Top>(schema: S) =>
Schema.optionalKey(schema).pipe(
Schema.decodeTo(Schema.optional(schema), {
decode: SchemaGetter.passthrough({ strict: false }),
@@ -17,7 +17,7 @@ export const optionalOmitUndefined = <S extends Schema.Top>(schema: S) =>
}),
)
export const withStatics =
export const statics =
<S extends object, M extends Record<string, unknown>>(methods: (schema: S) => M) =>
(schema: S): S & M =>
Object.assign(schema, methods(schema))
+41 -41
View File
@@ -1,6 +1,7 @@
export * as SessionEvent from "./session-event"
import { Schema } from "effect"
import { optional } from "./schema"
import { Event } from "./event"
import { ProviderMetadata, ToolContent } from "./llm"
import { Delivery } from "./session-delivery"
@@ -9,7 +10,6 @@ import { DateTimeUtcFromMillis, NonNegativeInt, RelativePath } from "./schema"
import { FileAttachment, Prompt } from "./prompt"
import { SessionID } from "./session-id"
import { Location } from "./location"
import { SessionMessageID } from "./session-message-id"
import { SessionMessage } from "./session-message"
import { Revert } from "./revert"
@@ -22,7 +22,7 @@ export const Source = Schema.Struct({
}).annotate({
identifier: "session.next.event.source",
})
export type Source = typeof Source.Type
export interface Source extends Schema.Schema.Type<typeof Source> {}
const Base = {
timestamp: DateTimeUtcFromMillis,
@@ -30,7 +30,7 @@ const Base = {
}
const PromptFields = {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
prompt: Prompt,
delivery: Delivery,
}
@@ -56,7 +56,7 @@ export const AgentSwitched = Event.define({
...options,
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
agent: Schema.String,
},
})
@@ -67,7 +67,7 @@ export const ModelSwitched = Event.define({
...options,
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
model: Model.Ref,
},
})
@@ -79,7 +79,7 @@ export const Moved = Event.define({
schema: {
...Base,
location: Location.Ref,
subdirectory: RelativePath.pipe(Schema.optional),
subdirectory: RelativePath.pipe(optional),
},
})
export type Moved = typeof Moved.Type
@@ -103,7 +103,7 @@ export const ContextUpdated = Event.define({
...options,
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
text: Schema.String,
},
})
@@ -114,7 +114,7 @@ export const Synthetic = Event.define({
...options,
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
text: Schema.String,
},
})
@@ -126,7 +126,7 @@ export namespace Shell {
...options,
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
callID: Schema.String,
command: Schema.String,
},
@@ -151,10 +151,10 @@ export namespace Step {
...options,
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
agent: Schema.String,
model: Model.Ref,
snapshot: Schema.String.pipe(Schema.optional),
snapshot: Schema.String.pipe(optional),
},
})
export type Started = typeof Started.Type
@@ -164,7 +164,7 @@ export namespace Step {
...stepSettlementOptions,
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
finish: Schema.String,
cost: Schema.Finite,
tokens: Schema.Struct({
@@ -176,8 +176,8 @@ export namespace Step {
write: Schema.Finite,
}),
}),
snapshot: Schema.String.pipe(Schema.optional),
files: Schema.Array(RelativePath).pipe(Schema.optional),
snapshot: Schema.String.pipe(optional),
files: Schema.Array(RelativePath).pipe(optional),
},
})
export type Ended = typeof Ended.Type
@@ -187,7 +187,7 @@ export namespace Step {
...stepSettlementOptions,
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
error: UnknownError,
},
})
@@ -200,7 +200,7 @@ export namespace Text {
...options,
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
textID: Schema.String,
},
})
@@ -211,7 +211,7 @@ export namespace Text {
type: "session.next.text.delta",
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
textID: Schema.String,
delta: Schema.String,
},
@@ -223,7 +223,7 @@ export namespace Text {
...options,
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
textID: Schema.String,
text: Schema.String,
},
@@ -237,9 +237,9 @@ export namespace Reasoning {
...options,
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
reasoningID: Schema.String,
providerMetadata: ProviderMetadata.pipe(Schema.optional),
providerMetadata: ProviderMetadata.pipe(optional),
},
})
export type Started = typeof Started.Type
@@ -249,7 +249,7 @@ export namespace Reasoning {
type: "session.next.reasoning.delta",
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
reasoningID: Schema.String,
delta: Schema.String,
},
@@ -261,10 +261,10 @@ export namespace Reasoning {
...options,
schema: {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
reasoningID: Schema.String,
text: Schema.String,
providerMetadata: ProviderMetadata.pipe(Schema.optional),
providerMetadata: ProviderMetadata.pipe(optional),
},
})
export type Ended = typeof Ended.Type
@@ -273,7 +273,7 @@ export namespace Reasoning {
export namespace Tool {
const ToolBase = {
...Base,
assistantMessageID: SessionMessageID.ID,
assistantMessageID: SessionMessage.ID,
callID: Schema.String,
}
@@ -318,7 +318,7 @@ export namespace Tool {
input: Schema.Record(Schema.String, Schema.Unknown),
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: ProviderMetadata.pipe(Schema.optional),
metadata: ProviderMetadata.pipe(optional),
}),
},
})
@@ -333,7 +333,7 @@ export namespace Tool {
...options,
schema: {
...ToolBase,
structured: Schema.Record(Schema.String, Schema.Any),
structured: Schema.Record(Schema.String, Schema.Unknown),
content: Schema.Array(ToolContent),
},
})
@@ -344,13 +344,13 @@ export namespace Tool {
...options,
schema: {
...ToolBase,
structured: Schema.Record(Schema.String, Schema.Any),
structured: Schema.Record(Schema.String, Schema.Unknown),
content: Schema.Array(ToolContent),
outputPaths: Schema.Array(Schema.String).pipe(Schema.optional),
result: Schema.Unknown.pipe(Schema.optional),
outputPaths: Schema.Array(Schema.String).pipe(optional),
result: Schema.Unknown.pipe(optional),
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: ProviderMetadata.pipe(Schema.optional),
metadata: ProviderMetadata.pipe(optional),
}),
},
})
@@ -362,10 +362,10 @@ export namespace Tool {
schema: {
...ToolBase,
error: UnknownError,
result: Schema.Unknown.pipe(Schema.optional),
result: Schema.Unknown.pipe(optional),
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: ProviderMetadata.pipe(Schema.optional),
metadata: ProviderMetadata.pipe(optional),
}),
},
})
@@ -374,15 +374,15 @@ export namespace Tool {
export const RetryError = Schema.Struct({
message: Schema.String,
statusCode: Schema.Finite.pipe(Schema.optional),
statusCode: Schema.Finite.pipe(optional),
isRetryable: Schema.Boolean,
responseHeaders: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),
responseBody: Schema.String.pipe(Schema.optional),
metadata: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),
responseHeaders: Schema.Record(Schema.String, Schema.String).pipe(optional),
responseBody: Schema.String.pipe(optional),
metadata: Schema.Record(Schema.String, Schema.String).pipe(optional),
}).annotate({
identifier: "session.next.retry_error",
})
export type RetryError = typeof RetryError.Type
export interface RetryError extends Schema.Schema.Type<typeof RetryError> {}
export const Retried = Event.define({
type: "session.next.retried",
@@ -401,7 +401,7 @@ export namespace Compaction {
...options,
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
reason: Schema.Union([Schema.Literal("auto"), Schema.Literal("manual")]),
},
})
@@ -411,7 +411,7 @@ export namespace Compaction {
type: "session.next.compaction.delta",
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
text: Schema.String,
},
})
@@ -422,7 +422,7 @@ export namespace Compaction {
...options,
schema: {
...Base,
messageID: SessionMessageID.ID,
messageID: SessionMessage.ID,
reason: Started.data.fields.reason,
text: Schema.String,
recent: Schema.String,
@@ -441,7 +441,7 @@ export namespace RevertEvent {
export const Committed = Event.define({
type: "session.next.revert.committed",
...options,
schema: { ...Base, messageID: SessionMessageID.ID },
schema: { ...Base, messageID: SessionMessage.ID },
})
}
+2 -2
View File
@@ -1,10 +1,10 @@
import { Schema } from "effect"
import { descending } from "./identifier"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const SessionID = Schema.String.check(Schema.isStartsWith("ses")).pipe(
Schema.brand("SessionID"),
withStatics((schema) => {
statics((schema) => {
const create = () => schema.make("ses_" + descending())
return {
create,
+4 -3
View File
@@ -1,11 +1,12 @@
export * as SessionInput from "./session-input"
import { Schema } from "effect"
import { optional } from "./schema"
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"
import { SessionMessage } from "./session-message"
export const Delivery = SessionDelivery.Delivery
export type Delivery = SessionDelivery.Delivery
@@ -13,10 +14,10 @@ export type Delivery = SessionDelivery.Delivery
export interface Admitted extends Schema.Schema.Type<typeof Admitted> {}
export const Admitted = Schema.Struct({
admittedSeq: NonNegativeInt,
id: SessionMessageID.ID,
id: SessionMessage.ID,
sessionID: SessionID,
prompt: Prompt,
delivery: Delivery,
timeCreated: DateTimeUtcFromMillis,
promotedSeq: NonNegativeInt.pipe(Schema.optional),
promotedSeq: NonNegativeInt.pipe(optional),
}).annotate({ identifier: "SessionInput.Admitted" })
-11
View File
@@ -1,11 +0,0 @@
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
+33 -29
View File
@@ -1,15 +1,19 @@
export * as SessionMessage from "./session-message"
import { Schema } from "effect"
import { optional } from "./schema"
import { ProviderMetadata, ToolContent } from "./llm"
import { Model } from "./model"
import { FileAttachment, Prompt } from "./prompt"
import { DateTimeUtcFromMillis, RelativePath } from "./schema"
import { DateTimeUtcFromMillis, RelativePath, statics } from "./schema"
import { SessionID } from "./session-id"
import { SessionMessageID } from "./session-message-id"
import { ascending } from "./identifier"
export const ID = SessionMessageID.ID
export type ID = SessionMessageID.ID
export const ID = Schema.String.check(Schema.isStartsWith("msg_")).pipe(
Schema.brand("Session.Message.ID"),
statics((schema) => ({ create: () => schema.make("msg_" + ascending()) })),
)
export type ID = typeof ID.Type
export interface UnknownError extends Schema.Schema.Type<typeof UnknownError> {}
export const UnknownError = Schema.Struct({
@@ -19,7 +23,7 @@ export const UnknownError = Schema.Struct({
const Base = {
id: ID,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(optional),
time: Schema.Struct({ created: DateTimeUtcFromMillis }),
}
@@ -70,7 +74,7 @@ export const Shell = Schema.Struct({
output: Schema.String,
time: Schema.Struct({
created: DateTimeUtcFromMillis,
completed: DateTimeUtcFromMillis.pipe(Schema.optional),
completed: DateTimeUtcFromMillis.pipe(optional),
}),
}).annotate({ identifier: "Session.Message.Shell" })
@@ -84,7 +88,7 @@ export interface ToolStateRunning extends Schema.Schema.Type<typeof ToolStateRun
export const ToolStateRunning = Schema.Struct({
status: Schema.Literal("running"),
input: Schema.Record(Schema.String, Schema.Unknown),
structured: Schema.Record(Schema.String, Schema.Any),
structured: Schema.Record(Schema.String, Schema.Unknown),
content: ToolContent.pipe(Schema.Array),
}).annotate({ identifier: "Session.Message.ToolState.Running" })
@@ -92,11 +96,11 @@ export interface ToolStateCompleted extends Schema.Schema.Type<typeof ToolStateC
export const ToolStateCompleted = Schema.Struct({
status: Schema.Literal("completed"),
input: Schema.Record(Schema.String, Schema.Unknown),
attachments: FileAttachment.pipe(Schema.Array, Schema.optional),
attachments: FileAttachment.pipe(Schema.Array, 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),
outputPaths: Schema.Array(Schema.String).pipe(optional),
structured: Schema.Record(Schema.String, Schema.Unknown),
result: Schema.Unknown.pipe(optional),
}).annotate({ identifier: "Session.Message.ToolState.Completed" })
export interface ToolStateError extends Schema.Schema.Type<typeof ToolStateError> {}
@@ -104,9 +108,9 @@ 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),
structured: Schema.Record(Schema.String, Schema.Unknown),
error: UnknownError,
result: Schema.Unknown.pipe(Schema.optional),
result: Schema.Unknown.pipe(optional),
}).annotate({ identifier: "Session.Message.ToolState.Error" })
export const ToolState = Schema.Union([ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError]).pipe(
@@ -121,15 +125,15 @@ export const AssistantTool = Schema.Struct({
name: Schema.String,
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: ProviderMetadata.pipe(Schema.optional),
resultMetadata: ProviderMetadata.pipe(Schema.optional),
}).pipe(Schema.optional),
metadata: ProviderMetadata.pipe(optional),
resultMetadata: ProviderMetadata.pipe(optional),
}).pipe(optional),
state: ToolState,
time: Schema.Struct({
created: DateTimeUtcFromMillis,
ran: DateTimeUtcFromMillis.pipe(Schema.optional),
completed: DateTimeUtcFromMillis.pipe(Schema.optional),
pruned: DateTimeUtcFromMillis.pipe(Schema.optional),
ran: DateTimeUtcFromMillis.pipe(optional),
completed: DateTimeUtcFromMillis.pipe(optional),
pruned: DateTimeUtcFromMillis.pipe(optional),
}),
}).annotate({ identifier: "Session.Message.Assistant.Tool" })
@@ -145,7 +149,7 @@ export const AssistantReasoning = Schema.Struct({
type: Schema.Literal("reasoning"),
id: Schema.String,
text: Schema.String,
providerMetadata: ProviderMetadata.pipe(Schema.optional),
providerMetadata: ProviderMetadata.pipe(optional),
}).annotate({ identifier: "Session.Message.Assistant.Reasoning" })
export const AssistantContent = Schema.Union([AssistantText, AssistantReasoning, AssistantTool]).pipe(
@@ -161,22 +165,22 @@ export const Assistant = Schema.Struct({
model: Model.Ref,
content: AssistantContent.pipe(Schema.Array),
snapshot: Schema.Struct({
start: Schema.String.pipe(Schema.optional),
end: Schema.String.pipe(Schema.optional),
files: Schema.Array(RelativePath).pipe(Schema.optional),
}).pipe(Schema.optional),
finish: Schema.String.pipe(Schema.optional),
cost: Schema.Finite.pipe(Schema.optional),
start: Schema.String.pipe(optional),
end: Schema.String.pipe(optional),
files: Schema.Array(RelativePath).pipe(optional),
}).pipe(optional),
finish: Schema.String.pipe(optional),
cost: Schema.Finite.pipe(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),
}).pipe(optional),
error: UnknownError.pipe(optional),
time: Schema.Struct({
created: DateTimeUtcFromMillis,
completed: DateTimeUtcFromMillis.pipe(Schema.optional),
completed: DateTimeUtcFromMillis.pipe(optional),
}),
}).annotate({ identifier: "Session.Message.Assistant" })
+3 -2
View File
@@ -1,6 +1,7 @@
export * as SessionStatusEvent from "./session-status-event"
import { Schema } from "effect"
import { optional } from "./schema"
import { Event } from "./event"
import { NonNegativeInt } from "./schema"
import { SessionID } from "./session-id"
@@ -13,14 +14,14 @@ export const Info = Schema.Union([
type: Schema.Literal("retry"),
attempt: NonNegativeInt,
message: Schema.String,
action: Schema.optional(
action: optional(
Schema.Struct({
reason: Schema.String,
provider: Schema.String,
title: Schema.String,
message: Schema.String,
label: Schema.String,
link: Schema.optional(Schema.String),
link: optional(Schema.String),
}),
),
next: NonNegativeInt,
+5 -4
View File
@@ -6,13 +6,14 @@ import { SessionID } from "./session-id"
export const Info = Schema.Struct({
content: Schema.String.annotate({ description: "Brief description of the task" }),
status: Schema.String.annotate({
status: Schema.Literals(["pending", "in_progress", "completed", "cancelled"]).annotate({
description: "Current status of the task: pending, in_progress, completed, cancelled",
}),
priority: Schema.String.annotate({ description: "Priority level of the task: high, medium, low" }),
priority: Schema.Literals(["high", "medium", "low"]).annotate({
description: "Priority level of the task: high, medium, low",
}),
}).annotate({ identifier: "Todo" })
export type Info = typeof Info.Type
export const SessionTodoInfo = Info
export interface Info extends Schema.Schema.Type<typeof Info> {}
const Updated = define({
type: "todo.updated",
+1 -676
View File
@@ -1,676 +1 @@
export * as SessionV1 from "./session-v1"
import { Effect, Schema, Types } from "effect"
import { define, inventory } from "./event"
import { FileDiff } from "./file-diff"
import { PermissionV1 } from "./permission-v1"
import { Project } from "./project"
import { Provider } from "./provider"
import { Model } from "./model"
import { NonNegativeInt, optionalOmitUndefined, withStatics } from "./schema"
import { ascending } from "./identifier"
import { SessionID } from "./session-id"
import { WorkspaceID } from "./workspace-id"
const Timestamp = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))
export const MessageID = Schema.String.check(Schema.isStartsWith("msg")).pipe(
Schema.brand("MessageID"),
withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "msg_" + ascending()) })),
)
export type MessageID = typeof MessageID.Type
export const PartID = Schema.String.check(Schema.isStartsWith("prt")).pipe(
Schema.brand("PartID"),
withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "prt_" + ascending()) })),
)
export type PartID = typeof PartID.Type
const namedError = <Name extends string, Fields extends Schema.Struct.Fields>(name: Name, fields: Fields) => {
const schema = Schema.Struct({ name: Schema.Literal(name), data: Schema.Struct(fields) }).annotate({
identifier: name,
})
return { Schema: schema, EffectSchema: schema }
}
export const OutputLengthError = namedError("MessageOutputLengthError", {})
export const AuthError = namedError("ProviderAuthError", {
providerID: Schema.String,
message: Schema.String,
})
export const AbortedError = namedError("MessageAbortedError", { message: Schema.String })
export const StructuredOutputError = namedError("StructuredOutputError", {
message: Schema.String,
retries: NonNegativeInt,
})
export const APIError = namedError("APIError", {
message: Schema.String,
statusCode: Schema.optional(NonNegativeInt),
isRetryable: Schema.Boolean,
responseHeaders: Schema.optional(Schema.Record(Schema.String, Schema.String)),
responseBody: Schema.optional(Schema.String),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.String)),
})
export type APIError = Schema.Schema.Type<typeof APIError.Schema>
export const ContextOverflowError = namedError("ContextOverflowError", {
message: Schema.String,
responseBody: Schema.optional(Schema.String),
})
export const ContentFilterError = namedError("ContentFilterError", {
message: Schema.String,
})
export class OutputFormatText extends Schema.Class<OutputFormatText>("OutputFormatText")({
type: Schema.Literal("text"),
}) {}
export class OutputFormatJsonSchema extends Schema.Class<OutputFormatJsonSchema>("OutputFormatJsonSchema")({
type: Schema.Literal("json_schema"),
schema: Schema.Record(Schema.String, Schema.Any).annotate({ identifier: "JSONSchema" }),
retryCount: NonNegativeInt.pipe(Schema.optional, Schema.withDecodingDefault(Effect.succeed(2))),
}) {}
export const Format = Schema.Union([OutputFormatText, OutputFormatJsonSchema]).annotate({
discriminator: "type",
identifier: "OutputFormat",
})
export type OutputFormat = Schema.Schema.Type<typeof Format>
const partBase = {
id: PartID,
sessionID: SessionID,
messageID: MessageID,
}
export const SnapshotPart = Schema.Struct({
...partBase,
type: Schema.Literal("snapshot"),
snapshot: Schema.String,
}).annotate({ identifier: "SnapshotPart" })
export type SnapshotPart = Types.DeepMutable<Schema.Schema.Type<typeof SnapshotPart>>
export const PatchPart = Schema.Struct({
...partBase,
type: Schema.Literal("patch"),
hash: Schema.String,
files: Schema.Array(Schema.String),
}).annotate({ identifier: "PatchPart" })
export type PatchPart = Types.DeepMutable<Schema.Schema.Type<typeof PatchPart>>
export const TextPart = Schema.Struct({
...partBase,
type: Schema.Literal("text"),
text: Schema.String,
synthetic: Schema.optional(Schema.Boolean),
ignored: Schema.optional(Schema.Boolean),
time: Schema.optional(
Schema.Struct({
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "TextPart" })
export type TextPart = Types.DeepMutable<Schema.Schema.Type<typeof TextPart>>
export const ReasoningPart = Schema.Struct({
...partBase,
type: Schema.Literal("reasoning"),
text: Schema.String,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
}).annotate({ identifier: "ReasoningPart" })
export type ReasoningPart = Types.DeepMutable<Schema.Schema.Type<typeof ReasoningPart>>
const filePartSourceBase = {
text: Schema.Struct({
value: Schema.String,
start: Schema.Finite,
end: Schema.Finite,
}).annotate({ identifier: "FilePartSourceText" }),
}
export const Range = Schema.Struct({
start: Schema.Struct({ line: NonNegativeInt, character: NonNegativeInt }),
end: Schema.Struct({ line: NonNegativeInt, character: NonNegativeInt }),
}).annotate({ identifier: "Range" })
export type Range = typeof Range.Type
export const FileSource = Schema.Struct({
...filePartSourceBase,
type: Schema.Literal("file"),
path: Schema.String,
}).annotate({ identifier: "FileSource" })
export const SymbolSource = Schema.Struct({
...filePartSourceBase,
type: Schema.Literal("symbol"),
path: Schema.String,
range: Range,
name: Schema.String,
kind: NonNegativeInt,
}).annotate({ identifier: "SymbolSource" })
export const ResourceSource = Schema.Struct({
...filePartSourceBase,
type: Schema.Literal("resource"),
clientName: Schema.String,
uri: Schema.String,
}).annotate({ identifier: "ResourceSource" })
export const FilePartSource = Schema.Union([FileSource, SymbolSource, ResourceSource]).annotate({
discriminator: "type",
identifier: "FilePartSource",
})
export const FilePart = Schema.Struct({
...partBase,
type: Schema.Literal("file"),
mime: Schema.String,
filename: Schema.optional(Schema.String),
url: Schema.String,
source: Schema.optional(FilePartSource),
}).annotate({ identifier: "FilePart" })
export type FilePart = Types.DeepMutable<Schema.Schema.Type<typeof FilePart>>
export const AgentPart = Schema.Struct({
...partBase,
type: Schema.Literal("agent"),
name: Schema.String,
source: Schema.optional(
Schema.Struct({
value: Schema.String,
start: NonNegativeInt,
end: NonNegativeInt,
}),
),
}).annotate({ identifier: "AgentPart" })
export type AgentPart = Types.DeepMutable<Schema.Schema.Type<typeof AgentPart>>
export const CompactionPart = Schema.Struct({
...partBase,
type: Schema.Literal("compaction"),
auto: Schema.Boolean,
overflow: Schema.optional(Schema.Boolean),
tail_start_id: Schema.optional(MessageID),
}).annotate({ identifier: "CompactionPart" })
export type CompactionPart = Types.DeepMutable<Schema.Schema.Type<typeof CompactionPart>>
export const SubtaskPart = Schema.Struct({
...partBase,
type: Schema.Literal("subtask"),
prompt: Schema.String,
description: Schema.String,
agent: Schema.String,
model: Schema.optional(
Schema.Struct({
providerID: Provider.ID,
modelID: Model.ID,
}),
),
command: Schema.optional(Schema.String),
}).annotate({ identifier: "SubtaskPart" })
export type SubtaskPart = Types.DeepMutable<Schema.Schema.Type<typeof SubtaskPart>>
export const RetryPart = Schema.Struct({
...partBase,
type: Schema.Literal("retry"),
attempt: NonNegativeInt,
error: APIError.EffectSchema,
time: Schema.Struct({
created: NonNegativeInt,
}),
}).annotate({ identifier: "RetryPart" })
export type RetryPart = Omit<Types.DeepMutable<Schema.Schema.Type<typeof RetryPart>>, "error"> & {
error: APIError
}
export const StepStartPart = Schema.Struct({
...partBase,
type: Schema.Literal("step-start"),
snapshot: Schema.optional(Schema.String),
}).annotate({ identifier: "StepStartPart" })
export type StepStartPart = Types.DeepMutable<Schema.Schema.Type<typeof StepStartPart>>
export const StepFinishPart = Schema.Struct({
...partBase,
type: Schema.Literal("step-finish"),
reason: Schema.String,
snapshot: Schema.optional(Schema.String),
cost: Schema.Finite,
tokens: Schema.Struct({
total: Schema.optional(Schema.Finite),
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
}),
}).annotate({ identifier: "StepFinishPart" })
export type StepFinishPart = Types.DeepMutable<Schema.Schema.Type<typeof StepFinishPart>>
export const ToolStatePending = Schema.Struct({
status: Schema.Literal("pending"),
input: Schema.Record(Schema.String, Schema.Any),
raw: Schema.String,
}).annotate({ identifier: "ToolStatePending" })
export type ToolStatePending = Types.DeepMutable<Schema.Schema.Type<typeof ToolStatePending>>
export const ToolStateRunning = Schema.Struct({
status: Schema.Literal("running"),
input: Schema.Record(Schema.String, Schema.Any),
title: Schema.optional(Schema.String),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: NonNegativeInt,
}),
}).annotate({ identifier: "ToolStateRunning" })
export type ToolStateRunning = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateRunning>>
export const ToolStateCompleted = Schema.Struct({
status: Schema.Literal("completed"),
input: Schema.Record(Schema.String, Schema.Any),
output: Schema.String,
title: Schema.String,
metadata: Schema.Record(Schema.String, Schema.Any),
time: Schema.Struct({
start: NonNegativeInt,
end: NonNegativeInt,
compacted: Schema.optional(NonNegativeInt),
}),
attachments: Schema.optional(Schema.Array(FilePart)),
}).annotate({ identifier: "ToolStateCompleted" })
export type ToolStateCompleted = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateCompleted>>
export const ToolStateError = Schema.Struct({
status: Schema.Literal("error"),
input: Schema.Record(Schema.String, Schema.Any),
error: Schema.String,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: NonNegativeInt,
end: NonNegativeInt,
}),
}).annotate({ identifier: "ToolStateError" })
export type ToolStateError = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateError>>
export const ToolState = Schema.Union([
ToolStatePending,
ToolStateRunning,
ToolStateCompleted,
ToolStateError,
]).annotate({
discriminator: "status",
identifier: "ToolState",
})
export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError
export const ToolPart = Schema.Struct({
...partBase,
type: Schema.Literal("tool"),
callID: Schema.String,
tool: Schema.String,
state: ToolState,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "ToolPart" })
export type ToolPart = Omit<Types.DeepMutable<Schema.Schema.Type<typeof ToolPart>>, "state"> & {
state: ToolState
}
const messageBase = {
id: MessageID,
sessionID: partBase.sessionID,
}
export const User = Schema.Struct({
...messageBase,
role: Schema.Literal("user"),
time: Schema.Struct({
created: Timestamp,
}),
format: Schema.optional(Format),
summary: Schema.optional(
Schema.Struct({
title: Schema.optional(Schema.String),
body: Schema.optional(Schema.String),
diffs: Schema.Array(FileDiff.Info),
}),
),
agent: Schema.String,
model: Schema.Struct({
providerID: Provider.ID,
modelID: Model.ID,
variant: Schema.optional(Schema.String),
}),
system: Schema.optional(Schema.String),
tools: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)),
}).annotate({ identifier: "UserMessage" })
export type User = Types.DeepMutable<Schema.Schema.Type<typeof User>>
export const Part = Schema.Union([
TextPart,
SubtaskPart,
ReasoningPart,
FilePart,
ToolPart,
StepStartPart,
StepFinishPart,
SnapshotPart,
PatchPart,
AgentPart,
RetryPart,
CompactionPart,
]).annotate({ discriminator: "type", identifier: "Part" })
export type Part =
| TextPart
| SubtaskPart
| ReasoningPart
| FilePart
| ToolPart
| StepStartPart
| StepFinishPart
| SnapshotPart
| PatchPart
| AgentPart
| RetryPart
| CompactionPart
const AssistantErrorSchema = Schema.Union([
AuthError.EffectSchema,
namedError("UnknownError", { message: Schema.String, ref: Schema.optional(Schema.String) }).EffectSchema,
OutputLengthError.EffectSchema,
AbortedError.EffectSchema,
StructuredOutputError.EffectSchema,
ContextOverflowError.EffectSchema,
ContentFilterError.EffectSchema,
APIError.EffectSchema,
]).annotate({ discriminator: "name" })
type AssistantError = Schema.Schema.Type<typeof AssistantErrorSchema>
export const TextPartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("text"),
text: Schema.String,
synthetic: Schema.optional(Schema.Boolean),
ignored: Schema.optional(Schema.Boolean),
time: Schema.optional(
Schema.Struct({
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "TextPartInput" })
export type TextPartInput = Types.DeepMutable<Schema.Schema.Type<typeof TextPartInput>>
export const FilePartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("file"),
mime: Schema.String,
filename: Schema.optional(Schema.String),
url: Schema.String,
source: Schema.optional(FilePartSource),
}).annotate({ identifier: "FilePartInput" })
export type FilePartInput = Types.DeepMutable<Schema.Schema.Type<typeof FilePartInput>>
export const AgentPartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("agent"),
name: Schema.String,
source: Schema.optional(
Schema.Struct({
value: Schema.String,
start: NonNegativeInt,
end: NonNegativeInt,
}),
),
}).annotate({ identifier: "AgentPartInput" })
export type AgentPartInput = Types.DeepMutable<Schema.Schema.Type<typeof AgentPartInput>>
export const SubtaskPartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("subtask"),
prompt: Schema.String,
description: Schema.String,
agent: Schema.String,
model: Schema.optional(
Schema.Struct({
providerID: Provider.ID,
modelID: Model.ID,
}),
),
command: Schema.optional(Schema.String),
}).annotate({ identifier: "SubtaskPartInput" })
export type SubtaskPartInput = Types.DeepMutable<Schema.Schema.Type<typeof SubtaskPartInput>>
export const Assistant = Schema.Struct({
...messageBase,
role: Schema.Literal("assistant"),
time: Schema.Struct({
created: NonNegativeInt,
completed: Schema.optional(NonNegativeInt),
}),
error: Schema.optional(AssistantErrorSchema),
parentID: MessageID,
modelID: Model.ID,
providerID: Provider.ID,
mode: Schema.String,
agent: Schema.String,
path: Schema.Struct({
cwd: Schema.String,
root: Schema.String,
}),
summary: Schema.optional(Schema.Boolean),
cost: Schema.Finite,
tokens: Schema.Struct({
total: Schema.optional(Schema.Finite),
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
}),
structured: Schema.optional(Schema.Any),
variant: Schema.optional(Schema.String),
finish: Schema.optional(Schema.String),
}).annotate({ identifier: "AssistantMessage" })
export type Assistant = Omit<Types.DeepMutable<Schema.Schema.Type<typeof Assistant>>, "error"> & {
error?: AssistantError
}
export const Info = Schema.Union([User, Assistant]).annotate({ discriminator: "role", identifier: "Message" })
export type Info = User | Assistant
export const WithParts = Schema.Struct({
info: Info,
parts: Schema.Array(Part),
})
export type WithParts = {
info: Info
parts: Part[]
}
const options = {
durable: {
aggregate: "sessionID",
version: 1,
},
} as const
const SessionSummary = Schema.Struct({
additions: Schema.Finite,
deletions: Schema.Finite,
files: Schema.Finite,
diffs: optionalOmitUndefined(Schema.Array(FileDiff.Info)),
})
const SessionTokens = Schema.Struct({
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
})
const SessionShare = Schema.Struct({
url: Schema.String,
})
const SessionRevert = Schema.Struct({
messageID: MessageID,
partID: optionalOmitUndefined(PartID),
snapshot: optionalOmitUndefined(Schema.String),
diff: optionalOmitUndefined(Schema.String),
})
const SessionModel = Schema.Struct({
id: Model.ID,
providerID: Provider.ID,
variant: optionalOmitUndefined(Schema.String),
})
export const SessionInfo = Schema.Struct({
id: SessionID,
slug: Schema.String,
projectID: Project.ID,
workspaceID: optionalOmitUndefined(WorkspaceID),
directory: Schema.String,
path: optionalOmitUndefined(Schema.String),
parentID: optionalOmitUndefined(SessionID),
summary: optionalOmitUndefined(SessionSummary),
cost: optionalOmitUndefined(Schema.Finite),
tokens: optionalOmitUndefined(SessionTokens),
share: optionalOmitUndefined(SessionShare),
title: Schema.String,
agent: optionalOmitUndefined(Schema.String),
model: optionalOmitUndefined(SessionModel),
version: Schema.String,
metadata: optionalOmitUndefined(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
created: NonNegativeInt,
updated: NonNegativeInt,
compacting: optionalOmitUndefined(NonNegativeInt),
archived: optionalOmitUndefined(Schema.Finite),
}),
permission: optionalOmitUndefined(PermissionV1.Ruleset),
revert: optionalOmitUndefined(SessionRevert),
}).annotate({ identifier: "Session" })
export type SessionInfo = typeof SessionInfo.Type
const events = {
Created: define({
type: "session.created",
...options,
schema: {
sessionID: SessionID,
info: SessionInfo,
},
}),
Updated: define({
type: "session.updated",
...options,
schema: {
sessionID: SessionID,
info: SessionInfo,
},
}),
Deleted: define({
type: "session.deleted",
...options,
schema: {
sessionID: SessionID,
info: SessionInfo,
},
}),
MessageUpdated: define({
type: "message.updated",
...options,
schema: {
sessionID: SessionID,
info: Info,
},
}),
MessageRemoved: define({
type: "message.removed",
...options,
schema: {
sessionID: SessionID,
messageID: MessageID,
},
}),
PartUpdated: define({
type: "message.part.updated",
...options,
schema: {
sessionID: SessionID,
part: Part,
time: Schema.Finite,
},
}),
PartRemoved: define({
type: "message.part.removed",
...options,
schema: {
sessionID: SessionID,
messageID: MessageID,
partID: PartID,
},
}),
}
export const PartDelta = define({
type: "message.part.delta",
schema: {
sessionID: SessionID,
messageID: MessageID,
partID: PartID,
field: Schema.String,
delta: Schema.String,
},
})
export const Diff = define({
type: "session.diff",
schema: {
sessionID: SessionID,
diff: Schema.Array(FileDiff.Info),
},
})
export const Error = define({
type: "session.error",
schema: {
sessionID: Schema.optional(SessionID),
error: Assistant.fields.error,
},
})
export const Event = {
...events,
PartDelta,
Diff,
Error,
Definitions: inventory(
events.Created,
events.Updated,
events.Deleted,
events.MessageUpdated,
events.MessageRemoved,
events.PartUpdated,
events.PartRemoved,
PartDelta,
Diff,
Error,
),
}
export * from "./v1/session"
+9 -9
View File
@@ -5,7 +5,7 @@ import { Agent } from "./agent"
import { Location } from "./location"
import { Model } from "./model"
import { Project } from "./project"
import { DateTimeUtcFromMillis, optionalOmitUndefined, RelativePath } from "./schema"
import { DateTimeUtcFromMillis, optional, RelativePath } from "./schema"
import { SessionEvent } from "./session-event"
import { SessionID } from "./session-id"
import { Revert } from "./revert"
@@ -18,10 +18,10 @@ export const Event = SessionEvent
export interface Info extends Schema.Schema.Type<typeof Info> {}
export const Info = Schema.Struct({
id: ID,
parentID: ID.pipe(optionalOmitUndefined),
parentID: ID.pipe(optional),
projectID: Project.ID,
agent: Agent.ID.pipe(Schema.optional),
model: Model.Ref.pipe(Schema.optional),
agent: Agent.ID.pipe(optional),
model: Model.Ref.pipe(optional),
cost: Schema.Finite,
tokens: Schema.Struct({
input: Schema.Finite,
@@ -35,17 +35,17 @@ export const Info = Schema.Struct({
time: Schema.Struct({
created: DateTimeUtcFromMillis,
updated: DateTimeUtcFromMillis,
archived: DateTimeUtcFromMillis.pipe(Schema.optional),
archived: DateTimeUtcFromMillis.pipe(optional),
}),
title: Schema.String,
location: Location.Ref,
subpath: RelativePath.pipe(Schema.optional),
revert: Revert.State.pipe(Schema.optional),
subpath: RelativePath.pipe(optional),
revert: Revert.State.pipe(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
}).annotate({ identifier: "Session.ListAnchor" })
export interface ListAnchor extends Schema.Schema.Type<typeof ListAnchor> {}
+3 -2
View File
@@ -1,6 +1,7 @@
export * as Skill from "./skill"
import { Schema } from "effect"
import { optional } from "./schema"
import { AbsolutePath } from "./schema"
export interface DirectorySource extends Schema.Schema.Type<typeof DirectorySource> {}
@@ -18,8 +19,8 @@ export const UrlSource = Schema.Struct({
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),
description: Schema.String.pipe(optional),
slash: Schema.Boolean.pipe(optional),
location: AbsolutePath,
content: Schema.String,
}).annotate({ identifier: "SkillV2.Info" })
+2 -1
View File
@@ -1,6 +1,7 @@
export * as TuiEvent from "./tui-event"
import { Effect, Schema } from "effect"
import { optional } from "./schema"
import { Event } from "./event"
import { PositiveInt } from "./schema"
import { SessionID } from "./session-id"
@@ -39,7 +40,7 @@ export const CommandExecute = Event.define({
export const ToastShow = Event.define({
type: "tui.toast.show",
schema: {
title: Schema.optional(Schema.String),
title: optional(Schema.String),
message: Schema.String,
variant: Schema.Literals(["info", "success", "warning", "error"]),
duration: PositiveInt.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_TOAST_DURATION))).annotate({
+18
View File
@@ -0,0 +1,18 @@
export * as LegacyEvent from "./legacy-event"
import { Schema } from "effect"
import { define, inventory } from "../event"
import { SessionID } from "../session-id"
import { SessionV1 } from "./session"
export const CommandExecuted = define({
type: "command.executed",
schema: {
name: Schema.String,
sessionID: SessionID,
arguments: Schema.String,
messageID: SessionV1.MessageID,
},
})
export const Definitions = inventory(CommandExecuted)
+66
View File
@@ -0,0 +1,66 @@
export * as PermissionV1 from "./permission"
import { Schema } from "effect"
import { define, inventory } from "../event"
import { ascending } from "../identifier"
import { Project } from "../project"
import { statics } from "../schema"
import { SessionID } from "../session-id"
export const ID = Schema.String.check(Schema.isStartsWith("per")).pipe(
Schema.brand("PermissionID"),
statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
)
export type ID = typeof ID.Type
export const Action = Schema.Literals(["allow", "deny", "ask"]).annotate({ identifier: "PermissionAction" })
export type Action = typeof Action.Type
export const Rule = Schema.Struct({ permission: Schema.String, pattern: Schema.String, action: Action }).annotate({
identifier: "PermissionRule",
})
export type Rule = typeof Rule.Type
export const Ruleset = Schema.Array(Rule).annotate({ identifier: "PermissionRuleset" })
export type Ruleset = typeof Ruleset.Type
export const Request = Schema.Struct({
id: ID,
sessionID: SessionID,
permission: Schema.String,
patterns: Schema.Array(Schema.String),
metadata: Schema.Record(Schema.String, Schema.Unknown),
always: Schema.Array(Schema.String),
tool: Schema.optional(Schema.Struct({ messageID: Schema.String, callID: Schema.String })),
}).annotate({ identifier: "PermissionRequest" })
export type Request = typeof Request.Type
export const Reply = Schema.Literals(["once", "always", "reject"])
export type Reply = typeof Reply.Type
export const ReplyBody = Schema.Struct({ reply: Reply, message: Schema.optional(Schema.String) }).annotate({
identifier: "PermissionReplyBody",
})
export type ReplyBody = typeof ReplyBody.Type
export const Approval = Schema.Struct({ projectID: Project.ID, patterns: Schema.Array(Schema.String) }).annotate({
identifier: "PermissionApproval",
})
export type Approval = typeof Approval.Type
export const AskInput = Schema.Struct({ ...Request.fields, id: Schema.optional(ID), ruleset: Ruleset }).annotate({
identifier: "PermissionAskInput",
})
export type AskInput = typeof AskInput.Type
export const ReplyInput = Schema.Struct({ requestID: ID, ...ReplyBody.fields }).annotate({
identifier: "PermissionReplyInput",
})
export type ReplyInput = typeof ReplyInput.Type
const Asked = define({ type: "permission.asked", schema: Request.fields })
const Replied = define({
type: "permission.replied",
schema: { sessionID: SessionID, requestID: ID, reply: Reply },
})
export const Event = { Asked, Replied, Definitions: inventory(Asked, Replied) }
+66
View File
@@ -0,0 +1,66 @@
export * as QuestionV1 from "./question"
import { Schema } from "effect"
import { define, inventory } from "../event"
import { ascending } from "../identifier"
import { statics } from "../schema"
import { SessionID } from "../session-id"
import { SessionV1 } from "./session"
export const ID = Schema.String.check(Schema.isStartsWith("que")).pipe(
Schema.brand("QuestionID"),
statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "que_" + ascending()) })),
)
export const Option = Schema.Struct({
label: Schema.String.annotate({ description: "Display text (1-5 words, concise)" }),
description: Schema.String.annotate({ description: "Explanation of choice" }),
}).annotate({ identifier: "QuestionOption" })
const base = {
question: Schema.String.annotate({ description: "Complete question" }),
header: Schema.String.annotate({ description: "Very short label (max 30 chars)" }),
options: Schema.Array(Option).annotate({ description: "Available choices" }),
multiple: Schema.optional(Schema.Boolean).annotate({ description: "Allow selecting multiple choices" }),
}
export const Info = Schema.Struct({
...base,
custom: Schema.optional(Schema.Boolean).annotate({ description: "Allow typing a custom answer (default: true)" }),
}).annotate({ identifier: "QuestionInfo" })
export const Prompt = Schema.Struct(base).annotate({ identifier: "QuestionPrompt" })
export const Tool = Schema.Struct({ messageID: SessionV1.MessageID, callID: Schema.String }).annotate({
identifier: "QuestionTool",
})
export const Request = Schema.Struct({
id: ID,
sessionID: SessionID,
questions: Schema.Array(Info).annotate({ description: "Questions to ask" }),
tool: Schema.optional(Tool),
}).annotate({ identifier: "QuestionRequest" })
export const Answer = Schema.Array(Schema.String).annotate({ identifier: "QuestionAnswer" })
export const Reply = Schema.Struct({
answers: Schema.Array(Answer).annotate({
description: "User answers in order of questions (each answer is an array of selected labels)",
}),
}).annotate({ identifier: "QuestionReply" })
export const Replied = Schema.Struct({
sessionID: SessionID,
requestID: ID,
answers: Schema.Array(Answer),
}).annotate({
identifier: "QuestionReplied",
})
export const Rejected = Schema.Struct({ sessionID: SessionID, requestID: ID }).annotate({
identifier: "QuestionRejected",
})
const Asked = define({ type: "question.asked", schema: Request.fields })
const RepliedEvent = define({ type: "question.replied", schema: Replied.fields })
const RejectedEvent = define({ type: "question.rejected", schema: Rejected.fields })
export const Event = {
Asked,
Replied: RepliedEvent,
Rejected: RejectedEvent,
Definitions: inventory(Asked, RepliedEvent, RejectedEvent),
}
+676
View File
@@ -0,0 +1,676 @@
export * as SessionV1 from "./session"
import { Effect, Schema, Types } from "effect"
import { define, inventory } from "../event"
import { FileDiff } from "../file-diff"
import { Project } from "../project"
import { Provider } from "../provider"
import { Model } from "../model"
import { NonNegativeInt, optional, statics } from "../schema"
import { ascending } from "../identifier"
import { SessionID } from "../session-id"
import { WorkspaceID } from "../workspace-id"
import { PermissionV1 } from "./permission"
const Timestamp = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))
export const MessageID = Schema.String.check(Schema.isStartsWith("msg")).pipe(
Schema.brand("MessageID"),
statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "msg_" + ascending()) })),
)
export type MessageID = typeof MessageID.Type
export const PartID = Schema.String.check(Schema.isStartsWith("prt")).pipe(
Schema.brand("PartID"),
statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "prt_" + ascending()) })),
)
export type PartID = typeof PartID.Type
const namedError = <Name extends string, Fields extends Schema.Struct.Fields>(name: Name, fields: Fields) => {
const schema = Schema.Struct({ name: Schema.Literal(name), data: Schema.Struct(fields) }).annotate({
identifier: name,
})
return { Schema: schema, EffectSchema: schema }
}
export const OutputLengthError = namedError("MessageOutputLengthError", {})
export const AuthError = namedError("ProviderAuthError", {
providerID: Schema.String,
message: Schema.String,
})
export const AbortedError = namedError("MessageAbortedError", { message: Schema.String })
export const StructuredOutputError = namedError("StructuredOutputError", {
message: Schema.String,
retries: NonNegativeInt,
})
export const APIError = namedError("APIError", {
message: Schema.String,
statusCode: Schema.optional(NonNegativeInt),
isRetryable: Schema.Boolean,
responseHeaders: Schema.optional(Schema.Record(Schema.String, Schema.String)),
responseBody: Schema.optional(Schema.String),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.String)),
})
export type APIError = Schema.Schema.Type<typeof APIError.Schema>
export const ContextOverflowError = namedError("ContextOverflowError", {
message: Schema.String,
responseBody: Schema.optional(Schema.String),
})
export const ContentFilterError = namedError("ContentFilterError", {
message: Schema.String,
})
export class OutputFormatText extends Schema.Class<OutputFormatText>("OutputFormatText")({
type: Schema.Literal("text"),
}) {}
export class OutputFormatJsonSchema extends Schema.Class<OutputFormatJsonSchema>("OutputFormatJsonSchema")({
type: Schema.Literal("json_schema"),
schema: Schema.Record(Schema.String, Schema.Any).annotate({ identifier: "JSONSchema" }),
retryCount: NonNegativeInt.pipe(Schema.optional, Schema.withDecodingDefault(Effect.succeed(2))),
}) {}
export const Format = Schema.Union([OutputFormatText, OutputFormatJsonSchema]).annotate({
discriminator: "type",
identifier: "OutputFormat",
})
export type OutputFormat = Schema.Schema.Type<typeof Format>
const partBase = {
id: PartID,
sessionID: SessionID,
messageID: MessageID,
}
export const SnapshotPart = Schema.Struct({
...partBase,
type: Schema.Literal("snapshot"),
snapshot: Schema.String,
}).annotate({ identifier: "SnapshotPart" })
export type SnapshotPart = Types.DeepMutable<Schema.Schema.Type<typeof SnapshotPart>>
export const PatchPart = Schema.Struct({
...partBase,
type: Schema.Literal("patch"),
hash: Schema.String,
files: Schema.Array(Schema.String),
}).annotate({ identifier: "PatchPart" })
export type PatchPart = Types.DeepMutable<Schema.Schema.Type<typeof PatchPart>>
export const TextPart = Schema.Struct({
...partBase,
type: Schema.Literal("text"),
text: Schema.String,
synthetic: Schema.optional(Schema.Boolean),
ignored: Schema.optional(Schema.Boolean),
time: Schema.optional(
Schema.Struct({
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "TextPart" })
export type TextPart = Types.DeepMutable<Schema.Schema.Type<typeof TextPart>>
export const ReasoningPart = Schema.Struct({
...partBase,
type: Schema.Literal("reasoning"),
text: Schema.String,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
}).annotate({ identifier: "ReasoningPart" })
export type ReasoningPart = Types.DeepMutable<Schema.Schema.Type<typeof ReasoningPart>>
const filePartSourceBase = {
text: Schema.Struct({
value: Schema.String,
start: Schema.Finite,
end: Schema.Finite,
}).annotate({ identifier: "FilePartSourceText" }),
}
export const Range = Schema.Struct({
start: Schema.Struct({ line: NonNegativeInt, character: NonNegativeInt }),
end: Schema.Struct({ line: NonNegativeInt, character: NonNegativeInt }),
}).annotate({ identifier: "Range" })
export type Range = typeof Range.Type
export const FileSource = Schema.Struct({
...filePartSourceBase,
type: Schema.Literal("file"),
path: Schema.String,
}).annotate({ identifier: "FileSource" })
export const SymbolSource = Schema.Struct({
...filePartSourceBase,
type: Schema.Literal("symbol"),
path: Schema.String,
range: Range,
name: Schema.String,
kind: NonNegativeInt,
}).annotate({ identifier: "SymbolSource" })
export const ResourceSource = Schema.Struct({
...filePartSourceBase,
type: Schema.Literal("resource"),
clientName: Schema.String,
uri: Schema.String,
}).annotate({ identifier: "ResourceSource" })
export const FilePartSource = Schema.Union([FileSource, SymbolSource, ResourceSource]).annotate({
discriminator: "type",
identifier: "FilePartSource",
})
export const FilePart = Schema.Struct({
...partBase,
type: Schema.Literal("file"),
mime: Schema.String,
filename: Schema.optional(Schema.String),
url: Schema.String,
source: Schema.optional(FilePartSource),
}).annotate({ identifier: "FilePart" })
export type FilePart = Types.DeepMutable<Schema.Schema.Type<typeof FilePart>>
export const AgentPart = Schema.Struct({
...partBase,
type: Schema.Literal("agent"),
name: Schema.String,
source: Schema.optional(
Schema.Struct({
value: Schema.String,
start: NonNegativeInt,
end: NonNegativeInt,
}),
),
}).annotate({ identifier: "AgentPart" })
export type AgentPart = Types.DeepMutable<Schema.Schema.Type<typeof AgentPart>>
export const CompactionPart = Schema.Struct({
...partBase,
type: Schema.Literal("compaction"),
auto: Schema.Boolean,
overflow: Schema.optional(Schema.Boolean),
tail_start_id: Schema.optional(MessageID),
}).annotate({ identifier: "CompactionPart" })
export type CompactionPart = Types.DeepMutable<Schema.Schema.Type<typeof CompactionPart>>
export const SubtaskPart = Schema.Struct({
...partBase,
type: Schema.Literal("subtask"),
prompt: Schema.String,
description: Schema.String,
agent: Schema.String,
model: Schema.optional(
Schema.Struct({
providerID: Provider.ID,
modelID: Model.ID,
}),
),
command: Schema.optional(Schema.String),
}).annotate({ identifier: "SubtaskPart" })
export type SubtaskPart = Types.DeepMutable<Schema.Schema.Type<typeof SubtaskPart>>
export const RetryPart = Schema.Struct({
...partBase,
type: Schema.Literal("retry"),
attempt: NonNegativeInt,
error: APIError.EffectSchema,
time: Schema.Struct({
created: NonNegativeInt,
}),
}).annotate({ identifier: "RetryPart" })
export type RetryPart = Omit<Types.DeepMutable<Schema.Schema.Type<typeof RetryPart>>, "error"> & {
error: APIError
}
export const StepStartPart = Schema.Struct({
...partBase,
type: Schema.Literal("step-start"),
snapshot: Schema.optional(Schema.String),
}).annotate({ identifier: "StepStartPart" })
export type StepStartPart = Types.DeepMutable<Schema.Schema.Type<typeof StepStartPart>>
export const StepFinishPart = Schema.Struct({
...partBase,
type: Schema.Literal("step-finish"),
reason: Schema.String,
snapshot: Schema.optional(Schema.String),
cost: Schema.Finite,
tokens: Schema.Struct({
total: Schema.optional(Schema.Finite),
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
}),
}).annotate({ identifier: "StepFinishPart" })
export type StepFinishPart = Types.DeepMutable<Schema.Schema.Type<typeof StepFinishPart>>
export const ToolStatePending = Schema.Struct({
status: Schema.Literal("pending"),
input: Schema.Record(Schema.String, Schema.Any),
raw: Schema.String,
}).annotate({ identifier: "ToolStatePending" })
export type ToolStatePending = Types.DeepMutable<Schema.Schema.Type<typeof ToolStatePending>>
export const ToolStateRunning = Schema.Struct({
status: Schema.Literal("running"),
input: Schema.Record(Schema.String, Schema.Any),
title: Schema.optional(Schema.String),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: NonNegativeInt,
}),
}).annotate({ identifier: "ToolStateRunning" })
export type ToolStateRunning = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateRunning>>
export const ToolStateCompleted = Schema.Struct({
status: Schema.Literal("completed"),
input: Schema.Record(Schema.String, Schema.Any),
output: Schema.String,
title: Schema.String,
metadata: Schema.Record(Schema.String, Schema.Any),
time: Schema.Struct({
start: NonNegativeInt,
end: NonNegativeInt,
compacted: Schema.optional(NonNegativeInt),
}),
attachments: Schema.optional(Schema.Array(FilePart)),
}).annotate({ identifier: "ToolStateCompleted" })
export type ToolStateCompleted = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateCompleted>>
export const ToolStateError = Schema.Struct({
status: Schema.Literal("error"),
input: Schema.Record(Schema.String, Schema.Any),
error: Schema.String,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: NonNegativeInt,
end: NonNegativeInt,
}),
}).annotate({ identifier: "ToolStateError" })
export type ToolStateError = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateError>>
export const ToolState = Schema.Union([
ToolStatePending,
ToolStateRunning,
ToolStateCompleted,
ToolStateError,
]).annotate({
discriminator: "status",
identifier: "ToolState",
})
export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError
export const ToolPart = Schema.Struct({
...partBase,
type: Schema.Literal("tool"),
callID: Schema.String,
tool: Schema.String,
state: ToolState,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "ToolPart" })
export type ToolPart = Omit<Types.DeepMutable<Schema.Schema.Type<typeof ToolPart>>, "state"> & {
state: ToolState
}
const messageBase = {
id: MessageID,
sessionID: partBase.sessionID,
}
export const User = Schema.Struct({
...messageBase,
role: Schema.Literal("user"),
time: Schema.Struct({
created: Timestamp,
}),
format: Schema.optional(Format),
summary: Schema.optional(
Schema.Struct({
title: Schema.optional(Schema.String),
body: Schema.optional(Schema.String),
diffs: Schema.Array(FileDiff.Info),
}),
),
agent: Schema.String,
model: Schema.Struct({
providerID: Provider.ID,
modelID: Model.ID,
variant: Schema.optional(Schema.String),
}),
system: Schema.optional(Schema.String),
tools: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)),
}).annotate({ identifier: "UserMessage" })
export type User = Types.DeepMutable<Schema.Schema.Type<typeof User>>
export const Part = Schema.Union([
TextPart,
SubtaskPart,
ReasoningPart,
FilePart,
ToolPart,
StepStartPart,
StepFinishPart,
SnapshotPart,
PatchPart,
AgentPart,
RetryPart,
CompactionPart,
]).annotate({ discriminator: "type", identifier: "Part" })
export type Part =
| TextPart
| SubtaskPart
| ReasoningPart
| FilePart
| ToolPart
| StepStartPart
| StepFinishPart
| SnapshotPart
| PatchPart
| AgentPart
| RetryPart
| CompactionPart
const AssistantErrorSchema = Schema.Union([
AuthError.EffectSchema,
namedError("UnknownError", { message: Schema.String, ref: Schema.optional(Schema.String) }).EffectSchema,
OutputLengthError.EffectSchema,
AbortedError.EffectSchema,
StructuredOutputError.EffectSchema,
ContextOverflowError.EffectSchema,
ContentFilterError.EffectSchema,
APIError.EffectSchema,
]).annotate({ discriminator: "name" })
type AssistantError = Schema.Schema.Type<typeof AssistantErrorSchema>
export const TextPartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("text"),
text: Schema.String,
synthetic: Schema.optional(Schema.Boolean),
ignored: Schema.optional(Schema.Boolean),
time: Schema.optional(
Schema.Struct({
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "TextPartInput" })
export type TextPartInput = Types.DeepMutable<Schema.Schema.Type<typeof TextPartInput>>
export const FilePartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("file"),
mime: Schema.String,
filename: Schema.optional(Schema.String),
url: Schema.String,
source: Schema.optional(FilePartSource),
}).annotate({ identifier: "FilePartInput" })
export type FilePartInput = Types.DeepMutable<Schema.Schema.Type<typeof FilePartInput>>
export const AgentPartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("agent"),
name: Schema.String,
source: Schema.optional(
Schema.Struct({
value: Schema.String,
start: NonNegativeInt,
end: NonNegativeInt,
}),
),
}).annotate({ identifier: "AgentPartInput" })
export type AgentPartInput = Types.DeepMutable<Schema.Schema.Type<typeof AgentPartInput>>
export const SubtaskPartInput = Schema.Struct({
id: Schema.optional(PartID),
type: Schema.Literal("subtask"),
prompt: Schema.String,
description: Schema.String,
agent: Schema.String,
model: Schema.optional(
Schema.Struct({
providerID: Provider.ID,
modelID: Model.ID,
}),
),
command: Schema.optional(Schema.String),
}).annotate({ identifier: "SubtaskPartInput" })
export type SubtaskPartInput = Types.DeepMutable<Schema.Schema.Type<typeof SubtaskPartInput>>
export const Assistant = Schema.Struct({
...messageBase,
role: Schema.Literal("assistant"),
time: Schema.Struct({
created: NonNegativeInt,
completed: Schema.optional(NonNegativeInt),
}),
error: Schema.optional(AssistantErrorSchema),
parentID: MessageID,
modelID: Model.ID,
providerID: Provider.ID,
mode: Schema.String,
agent: Schema.String,
path: Schema.Struct({
cwd: Schema.String,
root: Schema.String,
}),
summary: Schema.optional(Schema.Boolean),
cost: Schema.Finite,
tokens: Schema.Struct({
total: Schema.optional(Schema.Finite),
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
}),
structured: Schema.optional(Schema.Any),
variant: Schema.optional(Schema.String),
finish: Schema.optional(Schema.String),
}).annotate({ identifier: "AssistantMessage" })
export type Assistant = Omit<Types.DeepMutable<Schema.Schema.Type<typeof Assistant>>, "error"> & {
error?: AssistantError
}
export const Info = Schema.Union([User, Assistant]).annotate({ discriminator: "role", identifier: "Message" })
export type Info = User | Assistant
export const WithParts = Schema.Struct({
info: Info,
parts: Schema.Array(Part),
})
export type WithParts = {
info: Info
parts: Part[]
}
const options = {
durable: {
aggregate: "sessionID",
version: 1,
},
} as const
const SessionSummary = Schema.Struct({
additions: Schema.Finite,
deletions: Schema.Finite,
files: Schema.Finite,
diffs: optional(Schema.Array(FileDiff.Info)),
})
const SessionTokens = Schema.Struct({
input: Schema.Finite,
output: Schema.Finite,
reasoning: Schema.Finite,
cache: Schema.Struct({
read: Schema.Finite,
write: Schema.Finite,
}),
})
const SessionShare = Schema.Struct({
url: Schema.String,
})
const SessionRevert = Schema.Struct({
messageID: MessageID,
partID: optional(PartID),
snapshot: optional(Schema.String),
diff: optional(Schema.String),
})
const SessionModel = Schema.Struct({
id: Model.ID,
providerID: Provider.ID,
variant: optional(Schema.String),
})
export const SessionInfo = Schema.Struct({
id: SessionID,
slug: Schema.String,
projectID: Project.ID,
workspaceID: optional(WorkspaceID),
directory: Schema.String,
path: optional(Schema.String),
parentID: optional(SessionID),
summary: optional(SessionSummary),
cost: optional(Schema.Finite),
tokens: optional(SessionTokens),
share: optional(SessionShare),
title: Schema.String,
agent: optional(Schema.String),
model: optional(SessionModel),
version: Schema.String,
metadata: optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
created: NonNegativeInt,
updated: NonNegativeInt,
compacting: optional(NonNegativeInt),
archived: optional(Schema.Finite),
}),
permission: optional(PermissionV1.Ruleset),
revert: optional(SessionRevert),
}).annotate({ identifier: "Session" })
export type SessionInfo = typeof SessionInfo.Type
const events = {
Created: define({
type: "session.created",
...options,
schema: {
sessionID: SessionID,
info: SessionInfo,
},
}),
Updated: define({
type: "session.updated",
...options,
schema: {
sessionID: SessionID,
info: SessionInfo,
},
}),
Deleted: define({
type: "session.deleted",
...options,
schema: {
sessionID: SessionID,
info: SessionInfo,
},
}),
MessageUpdated: define({
type: "message.updated",
...options,
schema: {
sessionID: SessionID,
info: Info,
},
}),
MessageRemoved: define({
type: "message.removed",
...options,
schema: {
sessionID: SessionID,
messageID: MessageID,
},
}),
PartUpdated: define({
type: "message.part.updated",
...options,
schema: {
sessionID: SessionID,
part: Part,
time: Schema.Finite,
},
}),
PartRemoved: define({
type: "message.part.removed",
...options,
schema: {
sessionID: SessionID,
messageID: MessageID,
partID: PartID,
},
}),
}
export const PartDelta = define({
type: "message.part.delta",
schema: {
sessionID: SessionID,
messageID: MessageID,
partID: PartID,
field: Schema.String,
delta: Schema.String,
},
})
export const Diff = define({
type: "session.diff",
schema: {
sessionID: SessionID,
diff: Schema.Array(FileDiff.Info),
},
})
export const Error = define({
type: "session.error",
schema: {
sessionID: Schema.optional(SessionID),
error: Assistant.fields.error,
},
})
export const Event = {
...events,
PartDelta,
Diff,
Error,
Definitions: inventory(
events.Created,
events.Updated,
events.Deleted,
events.MessageUpdated,
events.MessageRemoved,
events.PartUpdated,
events.PartRemoved,
PartDelta,
Diff,
Error,
),
}
+2 -1
View File
@@ -1,12 +1,13 @@
export * as VcsEvent from "./vcs-event"
import { Schema } from "effect"
import { optional } from "./schema"
import { Event } from "./event"
export const BranchUpdated = Event.define({
type: "vcs.branch.updated",
schema: {
branch: Schema.optional(Schema.String),
branch: optional(Schema.String),
},
})
+2 -2
View File
@@ -7,8 +7,8 @@ import { WorkspaceID } from "./workspace-id"
export const ConnectionStatus = Schema.Struct({
workspaceID: WorkspaceID,
status: Schema.Literals(["connected", "connecting", "disconnected", "error"]),
})
export type ConnectionStatus = typeof ConnectionStatus.Type
}).annotate({ identifier: "WorkspaceEvent.ConnectionStatus" })
export interface ConnectionStatus extends Schema.Schema.Type<typeof ConnectionStatus> {}
export const Ready = Event.define({
type: "workspace.ready",
+2 -2
View File
@@ -1,10 +1,10 @@
import { Schema } from "effect"
import { ascending } from "./identifier"
import { withStatics } from "./schema"
import { statics } from "./schema"
export const WorkspaceID = Schema.String.check(Schema.isStartsWith("wrk")).pipe(
Schema.brand("WorkspaceV2.ID"),
withStatics((schema) => {
statics((schema) => {
const create = () => schema.make("wrk_" + ascending())
return {
ascending: (id?: string) => {
+2 -1
View File
@@ -1,13 +1,14 @@
export * as WorktreeEvent from "./worktree-event"
import { Schema } from "effect"
import { optional } from "./schema"
import { Event } from "./event"
export const Ready = Event.define({
type: "worktree.ready",
schema: {
name: Schema.String,
branch: Schema.optional(Schema.String),
branch: optional(Schema.String),
},
})
@@ -0,0 +1,67 @@
import { describe, expect, test } from "bun:test"
import { Schema } from "effect"
import { Agent } from "../src/agent"
import { FileSystem } from "../src/filesystem"
import { Model } from "../src/model"
import { Project } from "../src/project"
import { Pty } from "../src/pty"
import { Question } from "../src/question"
import { Session } from "../src/session"
import { SessionEvent } from "../src/session-event"
import { SessionTodo } from "../src/session-todo"
import { optional } from "../src/schema"
describe("contract hygiene", () => {
test("optional properties omit undefined while encoding", () => {
const Value = Schema.Struct({ value: optional(Schema.String) })
expect(Schema.encodeSync(Value)({ value: undefined })).toEqual({})
})
test("todo status and priority are closed sets", () => {
const decode = Schema.decodeUnknownSync(SessionTodo.Info)
expect(decode({ content: "ship", status: "pending", priority: "high" })).toEqual({
content: "ship",
status: "pending",
priority: "high",
})
expect(() => decode({ content: "ship", status: "waiting", priority: "high" })).toThrow()
expect(() => decode({ content: "ship", status: "pending", priority: "urgent" })).toThrow()
})
test("current ID constructors expose create", () => {
expect(Question.ID.create()).toStartWith("que_")
expect(Pty.ID.create()).toStartWith("pty_")
})
test("reusable public identifiers are stable and unique", () => {
const identifiers = [
Agent.Color,
FileSystem.Submatch,
Model.Ref,
Model.Capabilities,
Model.Cost,
Model.Api,
Project.Icon,
Project.Commands,
Project.Time,
Project.Info,
Pty.Info,
Session.ListAnchor,
].map((schema) => schema.ast.annotations?.identifier)
expect(identifiers.every((identifier) => typeof identifier === "string")).toBe(true)
expect(new Set(identifiers).size).toBe(identifiers.length)
})
test("current source avoids Any and mutable contract wrappers", async () => {
const files = [...new Bun.Glob("*.ts").scanSync(new URL("../src", import.meta.url).pathname)].filter(
(file) => !file.endsWith("-v1.ts"),
)
const source = await Promise.all(
files.map((file) => Bun.file(new URL(`../src/${file}`, import.meta.url)).text()),
).then((values) => values.join("\n"))
expect(source).not.toContain("Schema.Any")
expect(source).not.toContain("Schema.mutable")
})
})
+28
View File
@@ -0,0 +1,28 @@
import { expect, test } from "bun:test"
import { LegacyEvent } from "../src/legacy-event"
import { PermissionV1 } from "../src/permission-v1"
import { QuestionV1 } from "../src/question-v1"
import { SessionV1 } from "../src/session-v1"
import { LegacyEvent as IsolatedLegacyEvent } from "../src/v1/legacy-event"
import { PermissionV1 as IsolatedPermissionV1 } from "../src/v1/permission"
import { QuestionV1 as IsolatedQuestionV1 } from "../src/v1/question"
import { SessionV1 as IsolatedSessionV1 } from "../src/v1/session"
test("compatibility entrypoints preserve isolated V1 schema identity", () => {
expect(LegacyEvent).toBe(IsolatedLegacyEvent)
expect(PermissionV1).toBe(IsolatedPermissionV1)
expect(QuestionV1).toBe(IsolatedQuestionV1)
expect(SessionV1).toBe(IsolatedSessionV1)
})
test("current source does not import the V1 subtree directly", async () => {
const allowed = new Set(["legacy-event.ts", "permission-v1.ts", "question-v1.ts", "session-v1.ts"])
const files = [...new Bun.Glob("*.ts").scanSync(new URL("../src", import.meta.url).pathname)].filter(
(file) => !allowed.has(file),
)
const directImports = await Promise.all(
files.map(async (file) => ({ file, source: await Bun.file(new URL(`../src/${file}`, import.meta.url)).text() })),
).then((values) => values.filter((value) => value.source.includes('from "./v1/')))
expect(directImports).toEqual([])
})
+7 -21
View File
@@ -111,6 +111,7 @@ import type {
McpRemoteConfig,
McpStatusErrors,
McpStatusResponses,
ModelRef,
MoveSessionDestination,
OutputFormat,
Part as Part2,
@@ -128,10 +129,12 @@ import type {
PermissionRespondResponses,
PermissionRuleset,
PermissionV2Reply,
ProjectCommands,
ProjectCurrentErrors,
ProjectCurrentResponses,
ProjectDirectoriesErrors,
ProjectDirectoriesResponses,
ProjectIcon,
ProjectInitGitErrors,
ProjectInitGitResponses,
ProjectListErrors,
@@ -2611,17 +2614,8 @@ export class Project extends HeyApiClient {
directory?: string
workspace?: string
name?: string
icon?: {
url?: string
override?: string
color?: string
}
commands?: {
/**
* Startup script to run when creating a new workspace (worktree)
*/
start?: string
}
icon?: ProjectIcon
commands?: ProjectCommands
},
options?: Options<never, ThrowOnError>,
) {
@@ -5379,11 +5373,7 @@ export class Session3 extends HeyApiClient {
parameters?: {
id?: string
agent?: string
model?: {
id: string
providerID: string
variant?: string
}
model?: ModelRef
location?: LocationRef
},
options?: Options<never, ThrowOnError>,
@@ -5479,11 +5469,7 @@ export class Session3 extends HeyApiClient {
public switchModel<ThrowOnError extends boolean = false>(
parameters: {
sessionID: string
model?: {
id: string
providerID: string
variant?: string
}
model?: ModelRef
},
options?: Options<never, ThrowOnError>,
) {
+241 -435
View File
@@ -663,11 +663,11 @@ export type Todo = {
/**
* Current status of the task: pending, in_progress, completed, cancelled
*/
status: string
status: "pending" | "in_progress" | "completed" | "cancelled"
/**
* Priority level of the task: high, medium, low
*/
priority: string
priority: "high" | "medium" | "low"
}
export type SessionStatus =
@@ -835,11 +835,7 @@ export type GlobalEvent = {
timestamp: number
sessionID: string
messageID: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
}
}
| {
@@ -923,11 +919,7 @@ export type GlobalEvent = {
sessionID: string
assistantMessageID: string
agent: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
snapshot?: string
}
}
@@ -1003,11 +995,7 @@ export type GlobalEvent = {
sessionID: string
assistantMessageID: string
reasoningID: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
| {
@@ -1030,11 +1018,7 @@ export type GlobalEvent = {
assistantMessageID: string
reasoningID: string
text: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
| {
@@ -1084,11 +1068,7 @@ export type GlobalEvent = {
}
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -1103,7 +1083,7 @@ export type GlobalEvent = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
}
}
| {
@@ -1117,16 +1097,12 @@ export type GlobalEvent = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
outputPaths?: Array<string>
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -1142,11 +1118,7 @@ export type GlobalEvent = {
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -1198,13 +1170,7 @@ export type GlobalEvent = {
properties: {
timestamp: number
sessionID: string
revert: {
messageID: string
partID?: string
snapshot?: string
diff?: string
files?: Array<FileDiff>
}
revert: RevertState
}
}
| {
@@ -1516,24 +1482,11 @@ export type GlobalEvent = {
properties: {
id: string
worktree: string
vcs?: "git"
vcs?: ProjectVcs
name?: string
icon?: {
url?: string
override?: string
color?: string
}
commands?: {
/**
* Startup script to run when creating a new workspace (worktree)
*/
start?: string
}
time: {
created: number
updated: number
initialized?: number
}
icon?: ProjectIcon
commands?: ProjectCommands
time: ProjectTime
sandboxes: Array<string>
}
}
@@ -2466,24 +2419,11 @@ export type McpServerNotFoundError = {
export type Project = {
id: string
worktree: string
vcs?: "git"
vcs?: ProjectVcs
name?: string
icon?: {
url?: string
override?: string
color?: string
}
commands?: {
/**
* Startup script to run when creating a new workspace (worktree)
*/
start?: string
}
time: {
created: number
updated: number
initialized?: number
}
icon?: ProjectIcon
commands?: ProjectCommands
time: ProjectTime
sandboxes: Array<string>
}
@@ -2976,6 +2916,12 @@ export type MoveSessionDestination = {
directory: string
}
export type ModelRef = {
id: string
providerID: string
variant?: string
}
export type LocationRef = {
directory: string
workspaceID?: string
@@ -3005,6 +2951,12 @@ export type SessionErrorUnknown = {
message: string
}
export type LlmProviderMetadata = {
[key: string]: {
[key: string]: unknown
}
}
export type ToolTextContent = {
type: "text"
text: string
@@ -3017,6 +2969,8 @@ export type ToolFileContent = {
name?: string
}
export type LlmToolContent = ToolTextContent | ToolFileContent
export type SessionNextRetryError = {
message: string
statusCode?: number
@@ -3038,6 +2992,14 @@ export type FileDiff = {
patch: string
}
export type RevertState = {
messageID: string
partID?: string
snapshot?: string
diff?: string
files?: Array<FileDiff>
}
export type PermissionV2Source = {
type: "tool"
messageID: string
@@ -3081,6 +3043,27 @@ export type QuestionV2Tool = {
export type QuestionV2Answer = Array<string>
export type ProjectVcs = "git"
export type ProjectIcon = {
url?: string
override?: string
color?: string
}
export type ProjectCommands = {
/**
* Startup script to run when creating a new workspace (worktree)
*/
start?: string
}
export type ProjectTime = {
created: number
updated: number
initialized?: number
}
export type EventServerInstanceDisposed = {
id: string
type: "server.instance.disposed"
@@ -3225,11 +3208,7 @@ export type SyncEventSessionNextModelSwitched = {
timestamp: number
sessionID: string
messageID: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
}
}
}
@@ -3369,11 +3348,7 @@ export type SyncEventSessionNextStepStarted = {
sessionID: string
assistantMessageID: string
agent: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
snapshot?: string
}
}
@@ -3473,11 +3448,7 @@ export type SyncEventSessionNextReasoningStarted = {
sessionID: string
assistantMessageID: string
reasoningID: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
}
@@ -3496,11 +3467,7 @@ export type SyncEventSessionNextReasoningEnded = {
assistantMessageID: string
reasoningID: string
text: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
}
@@ -3560,11 +3527,7 @@ export type SyncEventSessionNextToolCalled = {
}
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -3586,7 +3549,7 @@ export type SyncEventSessionNextToolProgress = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
}
}
}
@@ -3607,16 +3570,12 @@ export type SyncEventSessionNextToolSuccess = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
outputPaths?: Array<string>
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -3639,11 +3598,7 @@ export type SyncEventSessionNextToolFailed = {
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -3713,13 +3668,7 @@ export type SyncEventSessionNextRevertStaged = {
data: {
timestamp: number
sessionID: string
revert: {
messageID: string
partID?: string
snapshot?: string
diff?: string
files?: Array<FileDiff>
}
revert: RevertState
}
}
}
@@ -3781,6 +3730,16 @@ export type ProjectDirectories = Array<{
strategy?: string
}>
export type PtyTicketConnectToken = {
ticket: string
expires_in: number
}
export type WorkspaceEventConnectionStatus = {
workspaceID: string
status: "connected" | "connecting" | "disconnected" | "error"
}
export type LocationInfo = {
directory: string
workspaceID?: string
@@ -3790,6 +3749,17 @@ export type LocationInfo = {
}
}
export type ProviderRequest = {
headers: {
[key: string]: string
}
body: {
[key: string]: unknown
}
}
export type AgentColor = string | "primary" | "secondary" | "accent" | "success" | "warning" | "error" | "info"
export type PermissionV2Effect = "allow" | "deny" | "ask"
export type PermissionV2Rule = {
@@ -3802,24 +3772,13 @@ export type PermissionV2Ruleset = Array<PermissionV2Rule>
export type AgentV2Info = {
id: string
model?: {
id: string
providerID: string
variant?: string
}
request: {
headers: {
[key: string]: string
}
body: {
[key: string]: unknown
}
}
model?: ModelRef
request: ProviderRequest
system?: string
description?: string
mode: "subagent" | "primary" | "all"
hidden: boolean
color?: string | "primary" | "secondary" | "accent" | "success" | "warning" | "error" | "info"
color?: AgentColor
steps?: number
permissions: PermissionV2Ruleset
}
@@ -3829,11 +3788,7 @@ export type SessionV2Info = {
parentID?: string
projectID: string
agent?: string
model?: {
id: string
providerID: string
variant?: string
}
model?: ModelRef
cost: number
tokens: {
input: number
@@ -3852,13 +3807,7 @@ export type SessionV2Info = {
title: string
location: LocationRef
subpath?: string
revert?: {
messageID: string
partID?: string
snapshot?: string
diff?: string
files?: Array<FileDiff>
}
revert?: RevertState
}
export type SessionInputAdmitted = {
@@ -3892,11 +3841,7 @@ export type SessionMessageModelSwitched = {
created: number
}
type: "model-switched"
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
}
export type SessionMessageUser = {
@@ -3963,11 +3908,7 @@ export type SessionMessageAssistantReasoning = {
type: "reasoning"
id: string
text: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
export type SessionMessageToolStatePending = {
@@ -3983,7 +3924,7 @@ export type SessionMessageToolStateRunning = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
}
export type SessionMessageToolStateCompleted = {
@@ -3992,7 +3933,7 @@ export type SessionMessageToolStateCompleted = {
[key: string]: unknown
}
attachments?: Array<PromptFileAttachment>
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
outputPaths?: Array<string>
structured: {
[key: string]: unknown
@@ -4005,7 +3946,7 @@ export type SessionMessageToolStateError = {
input: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
structured: {
[key: string]: unknown
}
@@ -4019,16 +3960,8 @@ export type SessionMessageAssistantTool = {
name: string
provider?: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
resultMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
resultMetadata?: LlmProviderMetadata
}
state:
| SessionMessageToolStatePending
@@ -4054,11 +3987,7 @@ export type SessionMessageAssistant = {
}
type: "assistant"
agent: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
content: Array<SessionMessageAssistantText | SessionMessageAssistantReasoning | SessionMessageAssistantTool>
snapshot?: {
start?: string
@@ -4103,34 +4032,51 @@ export type SessionMessage =
| SessionMessageAssistant
| SessionMessageCompaction
export type ModelApi =
| {
id: string
type: "aisdk"
package: string
url?: string
settings?: {
[key: string]: unknown
}
}
| {
id: string
type: "native"
url?: string
settings: {
[key: string]: unknown
}
}
export type ModelCapabilities = {
tools: boolean
input: Array<string>
output: Array<string>
}
export type ModelCost = {
tier?: {
type: "context"
size: number
}
input: number
output: number
cache: {
read: number
write: number
}
}
export type ModelV2Info = {
id: string
providerID: string
family?: string
name: string
api:
| {
id: string
type: "aisdk"
package: string
url?: string
settings?: {
[key: string]: unknown
}
}
| {
id: string
type: "native"
url?: string
settings: {
[key: string]: unknown
}
}
capabilities: {
tools: boolean
input: Array<string>
output: Array<string>
}
api: ModelApi
capabilities: ModelCapabilities
request: {
headers: {
[key: string]: string
@@ -4152,18 +4098,7 @@ export type ModelV2Info = {
time: {
released: number
}
cost: Array<{
tier?: {
type: "context"
size: number
}
input: number
output: number
cache: {
read: number
write: number
}
}>
cost: Array<ModelCost>
status: "alpha" | "beta" | "deprecated" | "active"
enabled: boolean
limit: {
@@ -4173,35 +4108,32 @@ export type ModelV2Info = {
}
}
export type ProviderAisdk = {
type: "aisdk"
package: string
url?: string
settings?: {
[key: string]: unknown
}
}
export type ProviderNative = {
type: "native"
url?: string
settings: {
[key: string]: unknown
}
}
export type ProviderApi = ProviderAisdk | ProviderNative
export type ProviderV2Info = {
id: string
integrationID?: string
name: string
disabled?: boolean
api:
| {
type: "aisdk"
package: string
url?: string
settings?: {
[key: string]: unknown
}
}
| {
type: "native"
url?: string
settings: {
[key: string]: unknown
}
}
request: {
headers: {
[key: string]: string
}
body: {
[key: string]: unknown
}
}
api: ProviderApi
request: ProviderRequest
}
export type IntegrationWhen = {
@@ -4278,6 +4210,37 @@ export type IntegrationAttempt = {
}
}
export type IntegrationAttemptStatus =
| {
status: "pending"
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
| {
status: "complete"
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
| {
status: "failed"
message: string
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
| {
status: "expired"
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
export type PermissionV2Request = {
id: string
sessionID: string
@@ -4307,11 +4270,7 @@ export type CommandV2Info = {
template: string
description?: string
agent?: string
model?: {
id: string
providerID: string
variant?: string
}
model?: ModelRef
subtask?: boolean
}
@@ -4555,11 +4514,7 @@ export type V2EventSessionNextModelSwitched = {
timestamp: number
sessionID: string
messageID: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
}
}
@@ -4723,11 +4678,7 @@ export type V2EventSessionNextStepStarted = {
sessionID: string
assistantMessageID: string
agent: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
snapshot?: string
}
}
@@ -4863,11 +4814,7 @@ export type V2EventSessionNextReasoningStarted = {
sessionID: string
assistantMessageID: string
reasoningID: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
@@ -4910,11 +4857,7 @@ export type V2EventSessionNextReasoningEnded = {
assistantMessageID: string
reasoningID: string
text: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
@@ -5004,11 +4947,7 @@ export type V2EventSessionNextToolCalled = {
}
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -5033,7 +4972,7 @@ export type V2EventSessionNextToolProgress = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
}
}
@@ -5057,16 +4996,12 @@ export type V2EventSessionNextToolSuccess = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
outputPaths?: Array<string>
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -5092,11 +5027,7 @@ export type V2EventSessionNextToolFailed = {
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -5198,13 +5129,7 @@ export type V2EventSessionNextRevertStaged = {
data: {
timestamp: number
sessionID: string
revert: {
messageID: string
partID?: string
snapshot?: string
diff?: string
files?: Array<FileDiff>
}
revert: RevertState
}
}
@@ -5846,24 +5771,11 @@ export type V2EventProjectUpdated = {
data: {
id: string
worktree: string
vcs?: "git"
vcs?: ProjectVcs
name?: string
icon?: {
url?: string
override?: string
color?: string
}
commands?: {
/**
* Startup script to run when creating a new workspace (worktree)
*/
start?: string
}
time: {
created: number
updated: number
initialized?: number
}
icon?: ProjectIcon
commands?: ProjectCommands
time: ProjectTime
sandboxes: Array<string>
}
}
@@ -6150,12 +6062,14 @@ export type ReferenceGitSource = {
hidden?: boolean
}
export type ReferenceSource = ReferenceLocalSource | ReferenceGitSource
export type ReferenceInfo = {
name: string
path: string
description?: string
hidden?: boolean
source: ReferenceLocalSource | ReferenceGitSource
source: ReferenceSource
}
export type ProjectCopyCopy = {
@@ -6277,11 +6191,7 @@ export type EventSessionNextModelSwitched = {
timestamp: number
sessionID: string
messageID: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
}
}
@@ -6373,11 +6283,7 @@ export type EventSessionNextStepStarted = {
sessionID: string
assistantMessageID: string
agent: string
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
snapshot?: string
}
}
@@ -6459,11 +6365,7 @@ export type EventSessionNextReasoningStarted = {
sessionID: string
assistantMessageID: string
reasoningID: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
@@ -6488,11 +6390,7 @@ export type EventSessionNextReasoningEnded = {
assistantMessageID: string
reasoningID: string
text: string
providerMetadata?: {
[key: string]: {
[key: string]: unknown
}
}
providerMetadata?: LlmProviderMetadata
}
}
@@ -6546,11 +6444,7 @@ export type EventSessionNextToolCalled = {
}
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -6566,7 +6460,7 @@ export type EventSessionNextToolProgress = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
}
}
@@ -6581,16 +6475,12 @@ export type EventSessionNextToolSuccess = {
structured: {
[key: string]: unknown
}
content: Array<ToolTextContent | ToolFileContent>
content: Array<LlmToolContent>
outputPaths?: Array<string>
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -6607,11 +6497,7 @@ export type EventSessionNextToolFailed = {
result?: unknown
provider: {
executed: boolean
metadata?: {
[key: string]: {
[key: string]: unknown
}
}
metadata?: LlmProviderMetadata
}
}
}
@@ -6668,13 +6554,7 @@ export type EventSessionNextRevertStaged = {
properties: {
timestamp: number
sessionID: string
revert: {
messageID: string
partID?: string
snapshot?: string
diff?: string
files?: Array<FileDiff>
}
revert: RevertState
}
}
@@ -6964,24 +6844,11 @@ export type EventProjectUpdated = {
properties: {
id: string
worktree: string
vcs?: "git"
vcs?: ProjectVcs
name?: string
icon?: {
url?: string
override?: string
color?: string
}
commands?: {
/**
* Startup script to run when creating a new workspace (worktree)
*/
start?: string
}
time: {
created: number
updated: number
initialized?: number
}
icon?: ProjectIcon
commands?: ProjectCommands
time: ProjectTime
sandboxes: Array<string>
}
}
@@ -8847,17 +8714,8 @@ export type ProjectInitGitResponse = ProjectInitGitResponses[keyof ProjectInitGi
export type ProjectUpdateData = {
body?: {
name?: string
icon?: {
url?: string
override?: string
color?: string
}
commands?: {
/**
* Startup script to run when creating a new workspace (worktree)
*/
start?: string
}
icon?: ProjectIcon
commands?: ProjectCommands
}
path: {
projectID: string
@@ -9194,10 +9052,7 @@ export type PtyConnectTokenResponses = {
/**
* WebSocket connect token
*/
200: {
ticket: string
expires_in: number
}
200: PtyTicketConnectToken
}
export type PtyConnectTokenResponse = PtyConnectTokenResponses[keyof PtyConnectTokenResponses]
@@ -11222,10 +11077,7 @@ export type ExperimentalWorkspaceStatusResponses = {
/**
* Workspace status
*/
200: Array<{
workspaceID: string
status: "connected" | "connecting" | "disconnected" | "error"
}>
200: Array<WorkspaceEventConnectionStatus>
}
export type ExperimentalWorkspaceStatusResponse =
@@ -11447,11 +11299,7 @@ export type V2SessionCreateData = {
body: {
id?: string
agent?: string
model?: {
id: string
providerID: string
variant?: string
}
model?: ModelRef
location?: LocationRef
}
path?: never
@@ -11559,11 +11407,7 @@ export type V2SessionSwitchAgentResponse = V2SessionSwitchAgentResponses[keyof V
export type V2SessionSwitchModelData = {
body: {
model: {
id: string
providerID: string
variant?: string
}
model: ModelRef
}
path: {
sessionID: string
@@ -11760,13 +11604,7 @@ export type V2SessionRevertStageResponses = {
* Success
*/
200: {
data: {
messageID: string
partID?: string
snapshot?: string
diff?: string
files?: Array<FileDiff>
}
data: RevertState
}
}
@@ -12293,36 +12131,7 @@ export type V2IntegrationAttemptStatusResponses = {
*/
200: {
location: LocationInfo
data:
| {
status: "pending"
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
| {
status: "complete"
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
| {
status: "failed"
message: string
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
| {
status: "expired"
time: {
created: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
expires: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN"
}
}
data: IntegrationAttemptStatus
}
}
@@ -13091,10 +12900,7 @@ export type V2PtyConnectTokenResponses = {
*/
200: {
location: LocationInfo
data: {
ticket: string
expires_in: number
}
data: PtyTicketConnectToken
}
}
+1 -1
View File
@@ -92,7 +92,7 @@ Use Effect schemas as the public contract:
- branded schemas for ids
- `Schema.Class` or `Schema.Struct` for domain data
- `Schema.TaggedErrorClass` for expected errors
- existing core helpers like `DeepMutable`, `withStatics`, and integer schemas where appropriate
- existing core helpers like `DeepMutable`, `statics`, and integer schemas where appropriate
Prefer `Info` objects as the stored domain records. Add static `empty(...)` constructors when update APIs need to create records on first mutation.
+1 -1
View File
@@ -5,7 +5,7 @@
```ts
export const ID = Schema.String.pipe(
Schema.brand("ProviderV2.ID"),
withStatics((schema) => ({
statics((schema) => ({
opencode: schema.make("opencode"),
anthropic: schema.make("anthropic"),
openai: schema.make("openai"),