refactor(schema): extract public event definitions (#33579)
This commit is contained in:
parent
858f35f1b3
commit
24b0132bc5
93 changed files with 2743 additions and 2127 deletions
6
packages/schema/src/catalog.ts
Normal file
6
packages/schema/src/catalog.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export * as Catalog from "./catalog"
|
||||
|
||||
import { define, inventory } from "./event"
|
||||
|
||||
const Updated = define({ type: "catalog.updated", schema: {} })
|
||||
export const Event = { Updated, Definitions: inventory(Updated) }
|
||||
10
packages/schema/src/durable-event-manifest.ts
Normal file
10
packages/schema/src/durable-event-manifest.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export * as DurableEventManifest from "./durable-event-manifest"
|
||||
|
||||
import { Event } from "./event"
|
||||
import { SessionEvent } from "./session-event"
|
||||
import { SessionV1 } from "./session-v1"
|
||||
|
||||
export const Durable = Event.durable([
|
||||
...SessionV1.Event.Definitions.filter((definition) => definition.durable !== undefined),
|
||||
...SessionEvent.DurableDefinitions,
|
||||
])
|
||||
84
packages/schema/src/event-manifest.ts
Normal file
84
packages/schema/src/event-manifest.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
export * as EventManifest from "./event-manifest"
|
||||
|
||||
import { Catalog } from "./catalog"
|
||||
import { Durable } from "./durable-event-manifest"
|
||||
import { Event } from "./event"
|
||||
import { FileSystem } from "./filesystem"
|
||||
import { FileSystemWatcher } from "./filesystem-watcher"
|
||||
import { InstallationEvent } from "./installation-event"
|
||||
import { Integration } from "./integration"
|
||||
import { LegacyEvent } from "./legacy-event"
|
||||
import { LspEvent } from "./lsp-event"
|
||||
import { McpEvent } from "./mcp-event"
|
||||
import { ModelsDev } from "./models-dev"
|
||||
import { Permission } from "./permission"
|
||||
import { PermissionV1 } from "./permission-v1"
|
||||
import { Plugin } from "./plugin"
|
||||
import { Project } from "./project"
|
||||
import { ProjectDirectories } from "./project-directories"
|
||||
import { Pty } from "./pty"
|
||||
import { Question } from "./question"
|
||||
import { QuestionV1 } from "./question-v1"
|
||||
import { Reference } from "./reference"
|
||||
import { ServerEvent } from "./server-event"
|
||||
import { SessionCompactionEvent } from "./session-compaction-event"
|
||||
import { SessionEvent } from "./session-event"
|
||||
import { SessionStatusEvent } from "./session-status-event"
|
||||
import { SessionTodo } from "./session-todo"
|
||||
import { SessionV1 } from "./session-v1"
|
||||
import { TuiEvent } from "./tui-event"
|
||||
import { VcsEvent } from "./vcs-event"
|
||||
import { WorkspaceEvent } from "./workspace-event"
|
||||
import { WorktreeEvent } from "./worktree-event"
|
||||
|
||||
const sessionV1DurableDefinitions = SessionV1.Event.Definitions.filter((definition) => definition.durable !== undefined)
|
||||
const sessionV1LiveDefinitions = SessionV1.Event.Definitions.filter((definition) => definition.durable === undefined)
|
||||
|
||||
const coreDefinitions = Event.inventory(...sessionV1DurableDefinitions, ...SessionEvent.Definitions)
|
||||
|
||||
const foundationDefinitions = Event.inventory(
|
||||
...ModelsDev.Event.Definitions,
|
||||
...Integration.Event.Definitions,
|
||||
...Catalog.Event.Definitions,
|
||||
...coreDefinitions,
|
||||
)
|
||||
|
||||
const featureDefinitions = Event.inventory(
|
||||
...FileSystem.Event.Definitions,
|
||||
...Reference.Event.Definitions,
|
||||
...Permission.Event.Definitions,
|
||||
...Plugin.Event.Definitions,
|
||||
...ProjectDirectories.Event.Definitions,
|
||||
...FileSystemWatcher.Event.Definitions,
|
||||
...Pty.Event.Definitions,
|
||||
...Question.Event.Definitions,
|
||||
)
|
||||
|
||||
export const ServerDefinitions = Event.inventory(
|
||||
...foundationDefinitions,
|
||||
...featureDefinitions,
|
||||
...SessionTodo.Event.Definitions,
|
||||
)
|
||||
|
||||
export const Definitions = Event.inventory(
|
||||
...foundationDefinitions,
|
||||
...sessionV1LiveDefinitions,
|
||||
...InstallationEvent.Definitions,
|
||||
...featureDefinitions,
|
||||
...SessionTodo.Event.Definitions,
|
||||
...LspEvent.Definitions,
|
||||
...PermissionV1.Event.Definitions,
|
||||
...TuiEvent.Definitions,
|
||||
...McpEvent.Definitions,
|
||||
...LegacyEvent.Definitions,
|
||||
...Project.Event.Definitions,
|
||||
...SessionStatusEvent.Definitions,
|
||||
...QuestionV1.Event.Definitions,
|
||||
...SessionCompactionEvent.Definitions,
|
||||
...VcsEvent.Definitions,
|
||||
...WorkspaceEvent.Definitions,
|
||||
...WorktreeEvent.Definitions,
|
||||
...ServerEvent.Definitions,
|
||||
)
|
||||
export const Latest = Event.latest(Definitions)
|
||||
export { Durable }
|
||||
125
packages/schema/src/event.ts
Normal file
125
packages/schema/src/event.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
export * as Event from "./event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { ascending } from "./identifier"
|
||||
import { Location } from "./location"
|
||||
import { withStatics } from "./schema"
|
||||
|
||||
export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe(
|
||||
Schema.brand("Event.ID"),
|
||||
withStatics((schema) => ({ create: () => schema.make("evt_" + ascending()) })),
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
|
||||
export type Definition<
|
||||
Type extends string = string,
|
||||
DataSchema extends Schema.Codec<unknown, unknown> = Schema.Codec<unknown, unknown>,
|
||||
> = Schema.Top & {
|
||||
readonly type: Type
|
||||
readonly durable?: {
|
||||
readonly version: number
|
||||
readonly aggregate: string
|
||||
}
|
||||
readonly data: DataSchema
|
||||
}
|
||||
|
||||
export type Data<D extends Definition> = Schema.Schema.Type<D["data"]>
|
||||
|
||||
export type Payload<D extends Definition = Definition> = {
|
||||
readonly id: ID
|
||||
readonly type: D["type"]
|
||||
readonly data: Data<D>
|
||||
readonly durable?: {
|
||||
readonly aggregateID: string
|
||||
readonly seq: number
|
||||
readonly version: number
|
||||
}
|
||||
readonly location?: Location.Ref
|
||||
readonly metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export function define<
|
||||
const Type extends string,
|
||||
Fields extends Readonly<Record<PropertyKey, Schema.Codec<unknown, unknown>>>,
|
||||
>(input: {
|
||||
readonly type: Type
|
||||
readonly durable?: {
|
||||
readonly version: number
|
||||
readonly aggregate: string
|
||||
}
|
||||
readonly schema: Fields
|
||||
}): Schema.Schema<Payload<Definition<Type, Schema.Struct<Fields>>>> & Definition<Type, Schema.Struct<Fields>> {
|
||||
const data = Schema.Struct(input.schema)
|
||||
return Object.assign(
|
||||
Schema.Struct({
|
||||
id: ID,
|
||||
metadata: Schema.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),
|
||||
data,
|
||||
}).annotate({ identifier: input.type }),
|
||||
{
|
||||
type: input.type,
|
||||
...(input.durable === undefined ? {} : { durable: input.durable }),
|
||||
data,
|
||||
},
|
||||
) as Schema.Schema<Payload<Definition<Type, Schema.Struct<Fields>>>> & Definition<Type, Schema.Struct<Fields>>
|
||||
}
|
||||
|
||||
export function inventory<const Definitions extends ReadonlyArray<Definition>>(...definitions: Definitions) {
|
||||
return Object.freeze(definitions)
|
||||
}
|
||||
|
||||
export function latest(definitions: ReadonlyArray<Definition>) {
|
||||
return readonlyMap(
|
||||
definitions.reduce((result, definition) => {
|
||||
const existing = result.get(definition.type)
|
||||
if (!existing) {
|
||||
result.set(definition.type, definition)
|
||||
return result
|
||||
}
|
||||
if (definition.durable && existing.durable && definition.durable.version !== existing.durable.version) {
|
||||
if (definition.durable.version > existing.durable.version) result.set(definition.type, definition)
|
||||
return result
|
||||
}
|
||||
if (definition !== existing) throw new Error(`Duplicate latest event definition for ${definition.type}`)
|
||||
return result
|
||||
}, new Map<string, Definition>()),
|
||||
)
|
||||
}
|
||||
|
||||
export function versionedType(type: string, version: number) {
|
||||
return `${type}.${version}`
|
||||
}
|
||||
|
||||
export function durable(definitions: ReadonlyArray<Definition>) {
|
||||
return readonlyMap(
|
||||
definitions.reduce((result, definition) => {
|
||||
if (!definition.durable) return result
|
||||
const key = versionedType(definition.type, definition.durable.version)
|
||||
if (result.has(key)) throw new Error(`Duplicate durable event definition for ${key}`)
|
||||
result.set(key, definition)
|
||||
return result
|
||||
}, new Map<string, Definition>()),
|
||||
)
|
||||
}
|
||||
|
||||
function readonlyMap<Key, Value>(map: Map<Key, Value>): ReadonlyMap<Key, Value> {
|
||||
const result: ReadonlyMap<Key, Value> = Object.freeze({
|
||||
get size() {
|
||||
return map.size
|
||||
},
|
||||
entries: () => map.entries(),
|
||||
forEach: (callback: (value: Value, key: Key, map: ReadonlyMap<Key, Value>) => void, thisArg?: unknown) =>
|
||||
map.forEach((value, key) => callback.call(thisArg, value, key, result)),
|
||||
get: (key: Key) => map.get(key),
|
||||
has: (key: Key) => map.has(key),
|
||||
keys: () => map.keys(),
|
||||
values: () => map.values(),
|
||||
[Symbol.iterator]: () => map[Symbol.iterator](),
|
||||
})
|
||||
return result
|
||||
}
|
||||
12
packages/schema/src/file-diff.ts
Normal file
12
packages/schema/src/file-diff.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export * as FileDiff from "./file-diff"
|
||||
|
||||
import { Schema } from "effect"
|
||||
|
||||
export const Info = Schema.Struct({
|
||||
file: Schema.optional(Schema.String),
|
||||
patch: Schema.optional(Schema.String),
|
||||
additions: Schema.Finite,
|
||||
deletions: Schema.Finite,
|
||||
status: Schema.optional(Schema.Literals(["added", "deleted", "modified"])),
|
||||
}).annotate({ identifier: "SnapshotFileDiff" })
|
||||
export type Info = typeof Info.Type
|
||||
13
packages/schema/src/filesystem-watcher.ts
Normal file
13
packages/schema/src/filesystem-watcher.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export * as FileSystemWatcher from "./filesystem-watcher"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
|
||||
const Updated = define({
|
||||
type: "file.watcher.updated",
|
||||
schema: {
|
||||
file: Schema.String,
|
||||
event: Schema.Literals(["add", "change", "unlink"]),
|
||||
},
|
||||
})
|
||||
export const Event = { Updated, Definitions: inventory(Updated) }
|
||||
|
|
@ -1,8 +1,15 @@
|
|||
export * as FileSystem from "./filesystem"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
import { NonNegativeInt, PositiveInt, RelativePath } from "./schema"
|
||||
|
||||
const Edited = define({
|
||||
type: "file.edited",
|
||||
schema: { file: Schema.String },
|
||||
})
|
||||
export const Event = { Edited, Definitions: inventory(Edited) }
|
||||
|
||||
export interface Entry extends Schema.Schema.Type<typeof Entry> {}
|
||||
export const Entry = Schema.Struct({
|
||||
path: RelativePath,
|
||||
|
|
|
|||
13
packages/schema/src/ide-event.ts
Normal file
13
packages/schema/src/ide-event.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export * as IdeEvent from "./ide-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
|
||||
export const Installed = Event.define({
|
||||
type: "ide.installed",
|
||||
schema: {
|
||||
ide: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(Installed)
|
||||
|
|
@ -2,6 +2,7 @@ export { Agent } from "./agent"
|
|||
export { Command } from "./command"
|
||||
export { Connection } from "./connection"
|
||||
export { Credential } from "./credential"
|
||||
export { Event } from "./event"
|
||||
export { FileSystem } from "./filesystem"
|
||||
export { Integration } from "./integration"
|
||||
export { LLM } from "./llm"
|
||||
|
|
|
|||
20
packages/schema/src/installation-event.ts
Normal file
20
packages/schema/src/installation-event.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export * as InstallationEvent from "./installation-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
|
||||
export const Updated = Event.define({
|
||||
type: "installation.updated",
|
||||
schema: {
|
||||
version: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const UpdateAvailable = Event.define({
|
||||
type: "installation.update-available",
|
||||
schema: {
|
||||
version: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(Updated, UpdateAvailable)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
export * as Integration from "./integration"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
|
||||
export const ID = Schema.String.pipe(Schema.brand("Integration.ID"))
|
||||
export type ID = typeof ID.Type
|
||||
|
|
@ -72,6 +73,16 @@ export type Method = typeof Method.Type
|
|||
export const Inputs = Schema.Record(Schema.String, Schema.String).annotate({ identifier: "Integration.Inputs" })
|
||||
export type Inputs = typeof Inputs.Type
|
||||
|
||||
const Updated = define({
|
||||
type: "integration.updated",
|
||||
schema: {},
|
||||
})
|
||||
const ConnectionUpdated = define({
|
||||
type: "integration.connection.updated",
|
||||
schema: { integrationID: ID },
|
||||
})
|
||||
export const Event = { Updated, ConnectionUpdated, Definitions: inventory(Updated, ConnectionUpdated) }
|
||||
|
||||
export interface Ref extends Schema.Schema.Type<typeof Ref> {}
|
||||
export const Ref = Schema.Struct({
|
||||
id: ID,
|
||||
|
|
|
|||
18
packages/schema/src/legacy-event.ts
Normal file
18
packages/schema/src/legacy-event.ts
Normal 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-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)
|
||||
|
|
@ -2,12 +2,12 @@ export * as Location from "./location"
|
|||
|
||||
import { Effect, Schema } from "effect"
|
||||
import { AbsolutePath } from "./schema"
|
||||
import { Workspace } from "./workspace"
|
||||
import { WorkspaceID } from "./workspace-id"
|
||||
|
||||
export interface Ref extends Schema.Schema.Type<typeof Ref> {}
|
||||
export const Ref = Schema.Struct({
|
||||
directory: AbsolutePath,
|
||||
workspaceID: Schema.optional(Workspace.ID).pipe(
|
||||
workspaceID: Schema.optional(WorkspaceID).pipe(
|
||||
Schema.withDecodingDefault(Effect.succeed(undefined)),
|
||||
Schema.withConstructorDefault(Effect.succeed(undefined)),
|
||||
),
|
||||
|
|
|
|||
7
packages/schema/src/lsp-event.ts
Normal file
7
packages/schema/src/lsp-event.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export * as LspEvent from "./lsp-event"
|
||||
|
||||
import { Event } from "./event"
|
||||
|
||||
export const Updated = Event.define({ type: "lsp.updated", schema: {} })
|
||||
|
||||
export const Definitions = Event.inventory(Updated)
|
||||
21
packages/schema/src/mcp-event.ts
Normal file
21
packages/schema/src/mcp-event.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
export * as McpEvent from "./mcp-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
|
||||
export const ToolsChanged = Event.define({
|
||||
type: "mcp.tools.changed",
|
||||
schema: {
|
||||
server: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const BrowserOpenFailed = Event.define({
|
||||
type: "mcp.browser.open.failed",
|
||||
schema: {
|
||||
mcpName: Schema.String,
|
||||
url: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(ToolsChanged, BrowserOpenFailed)
|
||||
9
packages/schema/src/models-dev.ts
Normal file
9
packages/schema/src/models-dev.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export * as ModelsDev from "./models-dev"
|
||||
|
||||
import { define, inventory } from "./event"
|
||||
|
||||
const Refreshed = define({
|
||||
type: "models-dev.refreshed",
|
||||
schema: {},
|
||||
})
|
||||
export const Event = { Refreshed, Definitions: inventory(Refreshed) }
|
||||
67
packages/schema/src/permission-v1.ts
Normal file
67
packages/schema/src/permission-v1.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
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
|
||||
|
|
@ -1,6 +1,54 @@
|
|||
export * as Permission from "./permission"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
import { ascending } from "./identifier"
|
||||
import { SessionID } from "./session-id"
|
||||
import { withStatics } 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()) })),
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
|
||||
export const Source = Schema.Union([
|
||||
Schema.Struct({
|
||||
type: Schema.Literal("tool"),
|
||||
messageID: Schema.String,
|
||||
callID: Schema.String,
|
||||
}),
|
||||
]).annotate({ identifier: "PermissionV2.Source" })
|
||||
export type Source = typeof Source.Type
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
export const Request = Schema.Struct({
|
||||
id: ID,
|
||||
...RequestFields,
|
||||
}).annotate({ identifier: "PermissionV2.Request" })
|
||||
export type Request = typeof Request.Type
|
||||
|
||||
export const Reply = Schema.Literals(["once", "always", "reject"]).annotate({ identifier: "PermissionV2.Reply" })
|
||||
export type Reply = typeof Reply.Type
|
||||
|
||||
const Asked = define({ type: "permission.v2.asked", schema: Request.fields })
|
||||
const Replied = define({
|
||||
type: "permission.v2.replied",
|
||||
schema: {
|
||||
sessionID: SessionID,
|
||||
requestID: ID,
|
||||
reply: Reply,
|
||||
},
|
||||
})
|
||||
export const Event = { Asked, Replied, Definitions: inventory(Asked, Replied) }
|
||||
|
||||
export const Effect = Schema.Literals(["allow", "deny", "ask"]).annotate({ identifier: "PermissionV2.Effect" })
|
||||
export type Effect = typeof Effect.Type
|
||||
|
|
|
|||
15
packages/schema/src/plugin.ts
Normal file
15
packages/schema/src/plugin.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export * as Plugin from "./plugin"
|
||||
|
||||
import { Schema } from "effect"
|
||||
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
|
||||
11
packages/schema/src/project-directories.ts
Normal file
11
packages/schema/src/project-directories.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export * as ProjectDirectories from "./project-directories"
|
||||
|
||||
import { define, inventory } from "./event"
|
||||
import { Project } from "./project"
|
||||
|
||||
const Updated = define({
|
||||
type: "project.directories.updated",
|
||||
schema: { projectID: Project.ID },
|
||||
})
|
||||
export const Event = { Updated, Definitions: inventory(Updated) }
|
||||
export const ProjectDirectoriesEvent = Event
|
||||
|
|
@ -1,10 +1,43 @@
|
|||
export * as Project from "./project"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { withStatics } from "./schema"
|
||||
import { define, inventory } from "./event"
|
||||
import { NonNegativeInt, optionalOmitUndefined, withStatics } from "./schema"
|
||||
|
||||
export const ID = Schema.String.pipe(
|
||||
Schema.brand("Project.ID"),
|
||||
withStatics((schema) => ({ global: schema.make("global") })),
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
|
||||
export const Vcs = Schema.Literal("git")
|
||||
export const Icon = Schema.Struct({
|
||||
url: optionalOmitUndefined(Schema.String),
|
||||
override: optionalOmitUndefined(Schema.String),
|
||||
color: optionalOmitUndefined(Schema.String),
|
||||
})
|
||||
export const Commands = Schema.Struct({
|
||||
start: optionalOmitUndefined(
|
||||
Schema.String.annotate({ description: "Startup script to run when creating a new workspace (worktree)" }),
|
||||
),
|
||||
})
|
||||
export const Time = Schema.Struct({
|
||||
created: NonNegativeInt,
|
||||
updated: NonNegativeInt,
|
||||
initialized: optionalOmitUndefined(NonNegativeInt),
|
||||
})
|
||||
|
||||
export const Info = Schema.Struct({
|
||||
id: ID,
|
||||
worktree: Schema.String,
|
||||
vcs: optionalOmitUndefined(Vcs),
|
||||
name: optionalOmitUndefined(Schema.String),
|
||||
icon: optionalOmitUndefined(Icon),
|
||||
commands: optionalOmitUndefined(Commands),
|
||||
time: Time,
|
||||
sandboxes: Schema.Array(Schema.String),
|
||||
}).annotate({ identifier: "Project" })
|
||||
export type Info = typeof Info.Type
|
||||
|
||||
const Updated = define({ type: "project.updated", schema: Info.fields })
|
||||
export const Event = { Updated, Definitions: inventory(Updated) }
|
||||
|
|
|
|||
35
packages/schema/src/pty.ts
Normal file
35
packages/schema/src/pty.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
export * as Pty from "./pty"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
import { ascending } from "./identifier"
|
||||
import { NonNegativeInt } from "./schema"
|
||||
import { withStatics } 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()),
|
||||
})),
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
|
||||
export const Info = Schema.Struct({
|
||||
id: ID,
|
||||
title: Schema.String,
|
||||
command: Schema.String,
|
||||
args: Schema.Array(Schema.String),
|
||||
cwd: Schema.String,
|
||||
status: Schema.Literals(["running", "exited"]),
|
||||
pid: NonNegativeInt,
|
||||
exitCode: Schema.optional(NonNegativeInt),
|
||||
}).annotate({ identifier: "Pty" })
|
||||
export const PtyInfo = 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
|
||||
66
packages/schema/src/question-v1.ts
Normal file
66
packages/schema/src/question-v1.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
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),
|
||||
}
|
||||
79
packages/schema/src/question.ts
Normal file
79
packages/schema/src/question.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
export * as Question from "./question"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
import { ascending } from "./identifier"
|
||||
import { SessionID } from "./session-id"
|
||||
import { withStatics } 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()) })),
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
|
||||
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
|
||||
|
||||
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" }),
|
||||
}
|
||||
|
||||
export const Info = Schema.Struct({
|
||||
...base,
|
||||
custom: Schema.Boolean.pipe(Schema.optional).annotate({
|
||||
description: "Allow typing a custom answer (default: true)",
|
||||
}),
|
||||
}).annotate({ identifier: "QuestionV2.Info" })
|
||||
export type Info = typeof Info.Type
|
||||
|
||||
export const Prompt = Schema.Struct(base).annotate({ identifier: "QuestionV2.Prompt" })
|
||||
export type Prompt = typeof Prompt.Type
|
||||
|
||||
export const Tool = Schema.Struct({
|
||||
messageID: Schema.String,
|
||||
callID: Schema.String,
|
||||
}).annotate({ identifier: "QuestionV2.Tool" })
|
||||
export type Tool = typeof Tool.Type
|
||||
|
||||
export const Request = Schema.Struct({
|
||||
id: ID,
|
||||
sessionID: SessionID,
|
||||
questions: Schema.Array(Info).annotate({ description: "Questions to ask" }),
|
||||
tool: Tool.pipe(Schema.optional),
|
||||
}).annotate({ identifier: "QuestionV2.Request" })
|
||||
export type Request = typeof Request.Type
|
||||
|
||||
export const Answer = Schema.Array(Schema.String).annotate({ identifier: "QuestionV2.Answer" })
|
||||
export type Answer = typeof Answer.Type
|
||||
|
||||
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: "QuestionV2.Reply" })
|
||||
export type Reply = typeof Reply.Type
|
||||
|
||||
const Asked = define({ type: "question.v2.asked", schema: Request.fields })
|
||||
const Replied = define({
|
||||
type: "question.v2.replied",
|
||||
schema: {
|
||||
sessionID: SessionID,
|
||||
requestID: ID,
|
||||
answers: Schema.Array(Answer),
|
||||
},
|
||||
})
|
||||
const Rejected = define({
|
||||
type: "question.v2.rejected",
|
||||
schema: {
|
||||
sessionID: SessionID,
|
||||
requestID: ID,
|
||||
},
|
||||
})
|
||||
export const Event = { Asked, Replied, Rejected, Definitions: inventory(Asked, Replied, Rejected) }
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
export * as Reference from "./reference"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
import { AbsolutePath } from "./schema"
|
||||
|
||||
const Updated = define({ type: "reference.updated", schema: {} })
|
||||
export const Event = { Updated, Definitions: inventory(Updated) }
|
||||
|
||||
export interface LocalSource extends Schema.Schema.Type<typeof LocalSource> {}
|
||||
export const LocalSource = Schema.Struct({
|
||||
type: Schema.Literal("local"),
|
||||
|
|
|
|||
8
packages/schema/src/server-event.ts
Normal file
8
packages/schema/src/server-event.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export * as ServerEvent from "./server-event"
|
||||
|
||||
import { Event } from "./event"
|
||||
|
||||
export const Connected = Event.define({ type: "server.connected", schema: {} })
|
||||
export const Disposed = Event.define({ type: "global.disposed", schema: {} })
|
||||
|
||||
export const Definitions = Event.inventory(Connected, Disposed)
|
||||
13
packages/schema/src/session-compaction-event.ts
Normal file
13
packages/schema/src/session-compaction-event.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export * as SessionCompactionEvent from "./session-compaction-event"
|
||||
|
||||
import { Event } from "./event"
|
||||
import { SessionID } from "./session-id"
|
||||
|
||||
export const Compacted = Event.define({
|
||||
type: "session.compacted",
|
||||
schema: {
|
||||
sessionID: SessionID,
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(Compacted)
|
||||
497
packages/schema/src/session-event.ts
Normal file
497
packages/schema/src/session-event.ts
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
export * as SessionEvent from "./session-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
import { ProviderMetadata, ToolContent } from "./llm"
|
||||
import { Delivery } from "./session-delivery"
|
||||
import { Model } from "./model"
|
||||
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"
|
||||
|
||||
export { FileAttachment }
|
||||
|
||||
export const Source = Schema.Struct({
|
||||
start: NonNegativeInt,
|
||||
end: NonNegativeInt,
|
||||
text: Schema.String,
|
||||
}).annotate({
|
||||
identifier: "session.next.event.source",
|
||||
})
|
||||
export type Source = typeof Source.Type
|
||||
|
||||
const Base = {
|
||||
timestamp: DateTimeUtcFromMillis,
|
||||
sessionID: SessionID,
|
||||
}
|
||||
const PromptFields = {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
prompt: Prompt,
|
||||
delivery: Delivery,
|
||||
}
|
||||
|
||||
const options = {
|
||||
durable: {
|
||||
aggregate: "sessionID",
|
||||
version: 1,
|
||||
},
|
||||
} as const
|
||||
const stepSettlementOptions = {
|
||||
durable: {
|
||||
aggregate: "sessionID",
|
||||
version: 2,
|
||||
},
|
||||
} as const
|
||||
|
||||
export const UnknownError = SessionMessage.UnknownError
|
||||
export type UnknownError = SessionMessage.UnknownError
|
||||
|
||||
export const AgentSwitched = Event.define({
|
||||
type: "session.next.agent.switched",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
agent: Schema.String,
|
||||
},
|
||||
})
|
||||
export type AgentSwitched = typeof AgentSwitched.Type
|
||||
|
||||
export const ModelSwitched = Event.define({
|
||||
type: "session.next.model.switched",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
model: Model.Ref,
|
||||
},
|
||||
})
|
||||
export type ModelSwitched = typeof ModelSwitched.Type
|
||||
|
||||
export const Moved = Event.define({
|
||||
type: "session.next.moved",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
location: Location.Ref,
|
||||
subdirectory: RelativePath.pipe(Schema.optional),
|
||||
},
|
||||
})
|
||||
export type Moved = typeof Moved.Type
|
||||
|
||||
export const Prompted = Event.define({
|
||||
type: "session.next.prompted",
|
||||
...options,
|
||||
schema: PromptFields,
|
||||
})
|
||||
export type Prompted = typeof Prompted.Type
|
||||
|
||||
export const PromptAdmitted = Event.define({
|
||||
type: "session.next.prompt.admitted",
|
||||
...options,
|
||||
schema: PromptFields,
|
||||
})
|
||||
export type PromptAdmitted = typeof PromptAdmitted.Type
|
||||
|
||||
export const ContextUpdated = Event.define({
|
||||
type: "session.next.context.updated",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
text: Schema.String,
|
||||
},
|
||||
})
|
||||
export type ContextUpdated = typeof ContextUpdated.Type
|
||||
|
||||
export const Synthetic = Event.define({
|
||||
type: "session.next.synthetic",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
text: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Synthetic = typeof Synthetic.Type
|
||||
|
||||
export namespace Shell {
|
||||
export const Started = Event.define({
|
||||
type: "session.next.shell.started",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
callID: Schema.String,
|
||||
command: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Started = typeof Started.Type
|
||||
|
||||
export const Ended = Event.define({
|
||||
type: "session.next.shell.ended",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
callID: Schema.String,
|
||||
output: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Ended = typeof Ended.Type
|
||||
}
|
||||
|
||||
export namespace Step {
|
||||
export const Started = Event.define({
|
||||
type: "session.next.step.started",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
agent: Schema.String,
|
||||
model: Model.Ref,
|
||||
snapshot: Schema.String.pipe(Schema.optional),
|
||||
},
|
||||
})
|
||||
export type Started = typeof Started.Type
|
||||
|
||||
export const Ended = Event.define({
|
||||
type: "session.next.step.ended",
|
||||
...stepSettlementOptions,
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
finish: Schema.String,
|
||||
cost: Schema.Finite,
|
||||
tokens: Schema.Struct({
|
||||
input: Schema.Finite,
|
||||
output: Schema.Finite,
|
||||
reasoning: Schema.Finite,
|
||||
cache: Schema.Struct({
|
||||
read: Schema.Finite,
|
||||
write: Schema.Finite,
|
||||
}),
|
||||
}),
|
||||
snapshot: Schema.String.pipe(Schema.optional),
|
||||
},
|
||||
})
|
||||
export type Ended = typeof Ended.Type
|
||||
|
||||
export const Failed = Event.define({
|
||||
type: "session.next.step.failed",
|
||||
...stepSettlementOptions,
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
error: UnknownError,
|
||||
},
|
||||
})
|
||||
export type Failed = typeof Failed.Type
|
||||
}
|
||||
|
||||
export namespace Text {
|
||||
export const Started = Event.define({
|
||||
type: "session.next.text.started",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
textID: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Started = typeof Started.Type
|
||||
|
||||
// Stream fragments are live-only; Text.Ended is the replayable full-value boundary.
|
||||
export const Delta = Event.define({
|
||||
type: "session.next.text.delta",
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
textID: Schema.String,
|
||||
delta: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Delta = typeof Delta.Type
|
||||
|
||||
export const Ended = Event.define({
|
||||
type: "session.next.text.ended",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
textID: Schema.String,
|
||||
text: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Ended = typeof Ended.Type
|
||||
}
|
||||
|
||||
export namespace Reasoning {
|
||||
export const Started = Event.define({
|
||||
type: "session.next.reasoning.started",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
reasoningID: Schema.String,
|
||||
providerMetadata: ProviderMetadata.pipe(Schema.optional),
|
||||
},
|
||||
})
|
||||
export type Started = typeof Started.Type
|
||||
|
||||
// Stream fragments are live-only; Reasoning.Ended is the replayable full-value boundary.
|
||||
export const Delta = Event.define({
|
||||
type: "session.next.reasoning.delta",
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
reasoningID: Schema.String,
|
||||
delta: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Delta = typeof Delta.Type
|
||||
|
||||
export const Ended = Event.define({
|
||||
type: "session.next.reasoning.ended",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
reasoningID: Schema.String,
|
||||
text: Schema.String,
|
||||
providerMetadata: ProviderMetadata.pipe(Schema.optional),
|
||||
},
|
||||
})
|
||||
export type Ended = typeof Ended.Type
|
||||
}
|
||||
|
||||
export namespace Tool {
|
||||
const ToolBase = {
|
||||
...Base,
|
||||
assistantMessageID: SessionMessageID.ID,
|
||||
callID: Schema.String,
|
||||
}
|
||||
|
||||
export namespace Input {
|
||||
export const Started = Event.define({
|
||||
type: "session.next.tool.input.started",
|
||||
...options,
|
||||
schema: {
|
||||
...ToolBase,
|
||||
name: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Started = typeof Started.Type
|
||||
|
||||
// Stream fragments are live-only; Input.Ended is the replayable raw-input boundary.
|
||||
export const Delta = Event.define({
|
||||
type: "session.next.tool.input.delta",
|
||||
schema: {
|
||||
...ToolBase,
|
||||
delta: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Delta = typeof Delta.Type
|
||||
|
||||
export const Ended = Event.define({
|
||||
type: "session.next.tool.input.ended",
|
||||
...options,
|
||||
schema: {
|
||||
...ToolBase,
|
||||
text: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Ended = typeof Ended.Type
|
||||
}
|
||||
|
||||
export const Called = Event.define({
|
||||
type: "session.next.tool.called",
|
||||
...options,
|
||||
schema: {
|
||||
...ToolBase,
|
||||
tool: Schema.String,
|
||||
input: Schema.Record(Schema.String, Schema.Unknown),
|
||||
provider: Schema.Struct({
|
||||
executed: Schema.Boolean,
|
||||
metadata: ProviderMetadata.pipe(Schema.optional),
|
||||
}),
|
||||
},
|
||||
})
|
||||
export type Called = typeof Called.Type
|
||||
|
||||
/**
|
||||
* Replayable bounded running-tool state. Tools should checkpoint semantic
|
||||
* transitions or at a bounded cadence, not persist every stdout/stderr chunk.
|
||||
*/
|
||||
export const Progress = Event.define({
|
||||
type: "session.next.tool.progress",
|
||||
...options,
|
||||
schema: {
|
||||
...ToolBase,
|
||||
structured: Schema.Record(Schema.String, Schema.Any),
|
||||
content: Schema.Array(ToolContent),
|
||||
},
|
||||
})
|
||||
export type Progress = typeof Progress.Type
|
||||
|
||||
export const Success = Event.define({
|
||||
type: "session.next.tool.success",
|
||||
...options,
|
||||
schema: {
|
||||
...ToolBase,
|
||||
structured: Schema.Record(Schema.String, Schema.Any),
|
||||
content: Schema.Array(ToolContent),
|
||||
outputPaths: Schema.Array(Schema.String).pipe(Schema.optional),
|
||||
result: Schema.Unknown.pipe(Schema.optional),
|
||||
provider: Schema.Struct({
|
||||
executed: Schema.Boolean,
|
||||
metadata: ProviderMetadata.pipe(Schema.optional),
|
||||
}),
|
||||
},
|
||||
})
|
||||
export type Success = typeof Success.Type
|
||||
|
||||
export const Failed = Event.define({
|
||||
type: "session.next.tool.failed",
|
||||
...options,
|
||||
schema: {
|
||||
...ToolBase,
|
||||
error: UnknownError,
|
||||
result: Schema.Unknown.pipe(Schema.optional),
|
||||
provider: Schema.Struct({
|
||||
executed: Schema.Boolean,
|
||||
metadata: ProviderMetadata.pipe(Schema.optional),
|
||||
}),
|
||||
},
|
||||
})
|
||||
export type Failed = typeof Failed.Type
|
||||
}
|
||||
|
||||
export const RetryError = Schema.Struct({
|
||||
message: Schema.String,
|
||||
statusCode: Schema.Finite.pipe(Schema.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),
|
||||
}).annotate({
|
||||
identifier: "session.next.retry_error",
|
||||
})
|
||||
export type RetryError = typeof RetryError.Type
|
||||
|
||||
export const Retried = Event.define({
|
||||
type: "session.next.retried",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
attempt: Schema.Finite,
|
||||
error: RetryError,
|
||||
},
|
||||
})
|
||||
export type Retried = typeof Retried.Type
|
||||
|
||||
export namespace Compaction {
|
||||
export const Started = Event.define({
|
||||
type: "session.next.compaction.started",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
reason: Schema.Union([Schema.Literal("auto"), Schema.Literal("manual")]),
|
||||
},
|
||||
})
|
||||
export type Started = typeof Started.Type
|
||||
|
||||
export const Delta = Event.define({
|
||||
type: "session.next.compaction.delta",
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
text: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Delta = typeof Delta.Type
|
||||
|
||||
export const Ended = Event.define({
|
||||
type: "session.next.compaction.ended",
|
||||
...options,
|
||||
schema: {
|
||||
...Base,
|
||||
messageID: SessionMessageID.ID,
|
||||
reason: Started.data.fields.reason,
|
||||
text: Schema.String,
|
||||
recent: Schema.String,
|
||||
},
|
||||
})
|
||||
export type Ended = typeof Ended.Type
|
||||
}
|
||||
|
||||
export const DurableDefinitions = Event.inventory(
|
||||
AgentSwitched,
|
||||
ModelSwitched,
|
||||
Moved,
|
||||
Prompted,
|
||||
PromptAdmitted,
|
||||
ContextUpdated,
|
||||
Synthetic,
|
||||
Shell.Started,
|
||||
Shell.Ended,
|
||||
Step.Started,
|
||||
Step.Ended,
|
||||
Step.Failed,
|
||||
Text.Started,
|
||||
Text.Ended,
|
||||
Tool.Input.Started,
|
||||
Tool.Input.Ended,
|
||||
Tool.Called,
|
||||
Tool.Progress,
|
||||
Tool.Success,
|
||||
Tool.Failed,
|
||||
Reasoning.Started,
|
||||
Reasoning.Ended,
|
||||
Retried,
|
||||
Compaction.Started,
|
||||
Compaction.Ended,
|
||||
)
|
||||
|
||||
export const Definitions = Event.inventory(
|
||||
AgentSwitched,
|
||||
ModelSwitched,
|
||||
Moved,
|
||||
Prompted,
|
||||
PromptAdmitted,
|
||||
ContextUpdated,
|
||||
Synthetic,
|
||||
Shell.Started,
|
||||
Shell.Ended,
|
||||
Step.Started,
|
||||
Step.Ended,
|
||||
Step.Failed,
|
||||
Text.Started,
|
||||
Text.Delta,
|
||||
Text.Ended,
|
||||
Reasoning.Started,
|
||||
Reasoning.Delta,
|
||||
Reasoning.Ended,
|
||||
Tool.Input.Started,
|
||||
Tool.Input.Delta,
|
||||
Tool.Input.Ended,
|
||||
Tool.Called,
|
||||
Tool.Progress,
|
||||
Tool.Success,
|
||||
Tool.Failed,
|
||||
Retried,
|
||||
Compaction.Started,
|
||||
Compaction.Delta,
|
||||
Compaction.Ended,
|
||||
)
|
||||
|
||||
export const Durable = Schema.Union(DurableDefinitions, { mode: "oneOf" }).pipe(Schema.toTaggedUnion("type"))
|
||||
export type DurableEvent = typeof Durable.Type
|
||||
|
||||
export const All = Schema.Union(Definitions, { mode: "oneOf" }).pipe(Schema.toTaggedUnion("type"))
|
||||
export type Event = typeof All.Type
|
||||
export type Type = Event["type"]
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
export * as SessionID from "./session-id"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { descending } from "./identifier"
|
||||
import { withStatics } from "./schema"
|
||||
|
||||
export const ID = Schema.String.check(Schema.isStartsWith("ses")).pipe(
|
||||
export const SessionID = Schema.String.check(Schema.isStartsWith("ses")).pipe(
|
||||
Schema.brand("SessionID"),
|
||||
withStatics((schema) => {
|
||||
const create = () => schema.make("ses_" + descending())
|
||||
|
|
@ -14,4 +12,4 @@ export const ID = Schema.String.check(Schema.isStartsWith("ses")).pipe(
|
|||
}
|
||||
}),
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
export type SessionID = typeof SessionID.Type
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export interface Admitted extends Schema.Schema.Type<typeof Admitted> {}
|
|||
export const Admitted = Schema.Struct({
|
||||
admittedSeq: NonNegativeInt,
|
||||
id: SessionMessageID.ID,
|
||||
sessionID: SessionID.ID,
|
||||
sessionID: SessionID,
|
||||
prompt: Prompt,
|
||||
delivery: Delivery,
|
||||
timeCreated: DateTimeUtcFromMillis,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export const User = Schema.Struct({
|
|||
export interface Synthetic extends Schema.Schema.Type<typeof Synthetic> {}
|
||||
export const Synthetic = Schema.Struct({
|
||||
...Base,
|
||||
sessionID: SessionID.ID,
|
||||
sessionID: SessionID,
|
||||
text: Schema.String,
|
||||
type: Schema.Literal("synthetic"),
|
||||
}).annotate({ identifier: "Session.Message.Synthetic" })
|
||||
|
|
|
|||
50
packages/schema/src/session-status-event.ts
Normal file
50
packages/schema/src/session-status-event.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
export * as SessionStatusEvent from "./session-status-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
import { NonNegativeInt } from "./schema"
|
||||
import { SessionID } from "./session-id"
|
||||
|
||||
export const Info = Schema.Union([
|
||||
Schema.Struct({
|
||||
type: Schema.Literal("idle"),
|
||||
}),
|
||||
Schema.Struct({
|
||||
type: Schema.Literal("retry"),
|
||||
attempt: NonNegativeInt,
|
||||
message: Schema.String,
|
||||
action: Schema.optional(
|
||||
Schema.Struct({
|
||||
reason: Schema.String,
|
||||
provider: Schema.String,
|
||||
title: Schema.String,
|
||||
message: Schema.String,
|
||||
label: Schema.String,
|
||||
link: Schema.optional(Schema.String),
|
||||
}),
|
||||
),
|
||||
next: NonNegativeInt,
|
||||
}),
|
||||
Schema.Struct({
|
||||
type: Schema.Literal("busy"),
|
||||
}),
|
||||
]).annotate({ identifier: "SessionStatus" })
|
||||
export type Info = Schema.Schema.Type<typeof Info>
|
||||
|
||||
export const Status = Event.define({
|
||||
type: "session.status",
|
||||
schema: {
|
||||
sessionID: SessionID,
|
||||
status: Info,
|
||||
},
|
||||
})
|
||||
|
||||
// deprecated
|
||||
export const Idle = Event.define({
|
||||
type: "session.idle",
|
||||
schema: {
|
||||
sessionID: SessionID,
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(Status, Idle)
|
||||
24
packages/schema/src/session-todo.ts
Normal file
24
packages/schema/src/session-todo.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
export * as SessionTodo from "./session-todo"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { define, inventory } from "./event"
|
||||
import { SessionID } from "./session-id"
|
||||
|
||||
export const Info = 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" }),
|
||||
}).annotate({ identifier: "Todo" })
|
||||
export type Info = typeof Info.Type
|
||||
export const SessionTodoInfo = Info
|
||||
|
||||
const Updated = define({
|
||||
type: "todo.updated",
|
||||
schema: {
|
||||
sessionID: SessionID,
|
||||
todos: Schema.Array(Info),
|
||||
},
|
||||
})
|
||||
export const Event = { Updated, Definitions: inventory(Updated) }
|
||||
676
packages/schema/src/session-v1.ts
Normal file
676
packages/schema/src/session-v1.ts
Normal file
|
|
@ -0,0 +1,676 @@
|
|||
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,
|
||||
),
|
||||
}
|
||||
|
|
@ -6,10 +6,13 @@ import { Location } from "./location"
|
|||
import { Model } from "./model"
|
||||
import { Project } from "./project"
|
||||
import { DateTimeUtcFromMillis, optionalOmitUndefined, RelativePath } from "./schema"
|
||||
import { SessionEvent } from "./session-event"
|
||||
import { SessionID } from "./session-id"
|
||||
|
||||
export const ID = SessionID.ID
|
||||
export type ID = SessionID.ID
|
||||
export const ID = SessionID
|
||||
export type ID = SessionID
|
||||
|
||||
export const Event = SessionEvent
|
||||
|
||||
export interface Info extends Schema.Schema.Type<typeof Info> {}
|
||||
export const Info = Schema.Struct({
|
||||
|
|
|
|||
58
packages/schema/src/tui-event.ts
Normal file
58
packages/schema/src/tui-event.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
export * as TuiEvent from "./tui-event"
|
||||
|
||||
import { Effect, Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
import { PositiveInt } from "./schema"
|
||||
import { SessionID } from "./session-id"
|
||||
|
||||
const DEFAULT_TOAST_DURATION = 5000
|
||||
|
||||
export const PromptAppend = Event.define({ type: "tui.prompt.append", schema: { text: Schema.String } })
|
||||
|
||||
export const CommandExecute = Event.define({
|
||||
type: "tui.command.execute",
|
||||
schema: {
|
||||
command: Schema.Union([
|
||||
Schema.Literals([
|
||||
"session.list",
|
||||
"session.new",
|
||||
"session.share",
|
||||
"session.interrupt",
|
||||
"session.compact",
|
||||
"session.page.up",
|
||||
"session.page.down",
|
||||
"session.line.up",
|
||||
"session.line.down",
|
||||
"session.half.page.up",
|
||||
"session.half.page.down",
|
||||
"session.first",
|
||||
"session.last",
|
||||
"prompt.clear",
|
||||
"prompt.submit",
|
||||
"agent.cycle",
|
||||
]),
|
||||
Schema.String,
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
export const ToastShow = Event.define({
|
||||
type: "tui.toast.show",
|
||||
schema: {
|
||||
title: Schema.optional(Schema.String),
|
||||
message: Schema.String,
|
||||
variant: Schema.Literals(["info", "success", "warning", "error"]),
|
||||
duration: PositiveInt.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_TOAST_DURATION))).annotate({
|
||||
description: "Duration in milliseconds",
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
export const SessionSelect = Event.define({
|
||||
type: "tui.session.select",
|
||||
schema: {
|
||||
sessionID: SessionID.annotate({ description: "Session ID to navigate to" }),
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(PromptAppend, CommandExecute, ToastShow, SessionSelect)
|
||||
13
packages/schema/src/vcs-event.ts
Normal file
13
packages/schema/src/vcs-event.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export * as VcsEvent from "./vcs-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
|
||||
export const BranchUpdated = Event.define({
|
||||
type: "vcs.branch.updated",
|
||||
schema: {
|
||||
branch: Schema.optional(Schema.String),
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(BranchUpdated)
|
||||
32
packages/schema/src/workspace-event.ts
Normal file
32
packages/schema/src/workspace-event.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
export * as WorkspaceEvent from "./workspace-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
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
|
||||
|
||||
export const Ready = Event.define({
|
||||
type: "workspace.ready",
|
||||
schema: {
|
||||
name: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const Failed = Event.define({
|
||||
type: "workspace.failed",
|
||||
schema: {
|
||||
message: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const Status = Event.define({
|
||||
type: "workspace.status",
|
||||
schema: ConnectionStatus.fields,
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(Ready, Failed, Status)
|
||||
19
packages/schema/src/workspace-id.ts
Normal file
19
packages/schema/src/workspace-id.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Schema } from "effect"
|
||||
import { ascending } from "./identifier"
|
||||
import { withStatics } from "./schema"
|
||||
|
||||
export const WorkspaceID = Schema.String.check(Schema.isStartsWith("wrk")).pipe(
|
||||
Schema.brand("WorkspaceV2.ID"),
|
||||
withStatics((schema) => {
|
||||
const create = () => schema.make("wrk_" + ascending())
|
||||
return {
|
||||
ascending: (id?: string) => {
|
||||
if (!id) return create()
|
||||
if (!id.startsWith("wrk")) throw new Error(`ID ${id} does not start with wrk`)
|
||||
return schema.make(id)
|
||||
},
|
||||
create,
|
||||
}
|
||||
}),
|
||||
)
|
||||
export type WorkspaceID = typeof WorkspaceID.Type
|
||||
|
|
@ -1,21 +1,9 @@
|
|||
export * as Workspace from "./workspace"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { ascending } from "./identifier"
|
||||
import { withStatics } from "./schema"
|
||||
import { WorkspaceEvent } from "./workspace-event"
|
||||
import { WorkspaceID } from "./workspace-id"
|
||||
|
||||
export const ID = Schema.String.check(Schema.isStartsWith("wrk")).pipe(
|
||||
Schema.brand("WorkspaceV2.ID"),
|
||||
withStatics((schema) => {
|
||||
const create = () => schema.make("wrk_" + ascending())
|
||||
return {
|
||||
ascending: (id?: string) => {
|
||||
if (!id) return create()
|
||||
if (!id.startsWith("wrk")) throw new Error(`ID ${id} does not start with wrk`)
|
||||
return schema.make(id)
|
||||
},
|
||||
create,
|
||||
}
|
||||
}),
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
export const ID = WorkspaceID
|
||||
export type ID = WorkspaceID
|
||||
|
||||
export const Event = WorkspaceEvent
|
||||
|
|
|
|||
21
packages/schema/src/worktree-event.ts
Normal file
21
packages/schema/src/worktree-event.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
export * as WorktreeEvent from "./worktree-event"
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { Event } from "./event"
|
||||
|
||||
export const Ready = Event.define({
|
||||
type: "worktree.ready",
|
||||
schema: {
|
||||
name: Schema.String,
|
||||
branch: Schema.optional(Schema.String),
|
||||
},
|
||||
})
|
||||
|
||||
export const Failed = Event.define({
|
||||
type: "worktree.failed",
|
||||
schema: {
|
||||
message: Schema.String,
|
||||
},
|
||||
})
|
||||
|
||||
export const Definitions = Event.inventory(Ready, Failed)
|
||||
Loading…
Add table
Add a link
Reference in a new issue