refactor(schema): distinguish published event durability
This commit is contained in:
@@ -141,6 +141,7 @@ _Avoid_: Response envelope
|
||||
- Promise streaming methods return a lazy `AsyncIterable` directly rather than a Promise-wrapped stream object. Iteration opens the connection, `AbortSignal` cancels it, and ending iteration closes the underlying request; the Effect emitter analogously returns `Stream` directly.
|
||||
- Promise SSE connection establishment, declared HTTP failures, and infrastructure failures occur during `AsyncIterable` iteration, beginning with its first `next()` call, rather than during synchronous method construction.
|
||||
- Neither generated streaming runtime automatically reconnects after disconnection. Promise `AsyncIterable` and Effect `Stream` fail explicitly; live consumers refresh and resubscribe, while durable sequence-based resume remains explicit composition above the generated client.
|
||||
- Event definition durability is authoritative for published payloads. Durable definitions publish and decode only with commit metadata (`aggregateID`, `seq`, and `version`); live definitions forbid that metadata. Core's pre-commit payload without an assigned sequence is a separate internal type and never reaches subscribers or projectors.
|
||||
- Promise client construction is synchronous and network-free. It requires `baseUrl`, defaults to `globalThis.fetch`, accepts client-level headers, and merges them with per-call header overrides.
|
||||
- Effect client construction accepts an explicit `baseUrl` and obtains `HttpClient.HttpClient` from the Effect environment. It does not install fetch or duplicate per-call transport policy; callers transform/provide the client for headers, tracing, retries, recording, and tests, while fiber interruption owns cancellation.
|
||||
- Promise and Effect emitters each own their generated public type modules. The **SDK Contract IR**, not a physically shared generated type package, is the common source; this permits zero-Effect wire types and rich decoded Effect types to evolve independently.
|
||||
|
||||
+61
-47
@@ -2,7 +2,14 @@ export * as EventV2 from "./event"
|
||||
|
||||
import { Cause, Context, Effect, Layer, Option, PubSub, Schema, Stream } from "effect"
|
||||
import { Event } from "@opencode-ai/schema/event"
|
||||
import type { Data, Definition, Payload } from "@opencode-ai/schema/event"
|
||||
import type {
|
||||
Data,
|
||||
Definition,
|
||||
DurableDefinition,
|
||||
Payload,
|
||||
PublishedPayload,
|
||||
UncommittedPayload,
|
||||
} from "@opencode-ai/schema/event"
|
||||
import { and, asc, eq, gt } from "drizzle-orm"
|
||||
import { Database } from "./database/database"
|
||||
import { EventSequenceTable, EventTable } from "./event/sql"
|
||||
@@ -13,7 +20,14 @@ import { Durable } from "@opencode-ai/schema/durable-event-manifest"
|
||||
|
||||
export const ID = Event.ID
|
||||
export type ID = import("@opencode-ai/schema/event").ID
|
||||
export type { Data, Definition, Payload } from "@opencode-ai/schema/event"
|
||||
export type {
|
||||
Data,
|
||||
Definition,
|
||||
DurableDefinition,
|
||||
Payload,
|
||||
PublishedPayload,
|
||||
UncommittedPayload,
|
||||
} from "@opencode-ai/schema/event"
|
||||
|
||||
export type Subscriber<D extends Definition = Definition> = (event: Payload<D>) => Effect.Effect<void>
|
||||
export type Unsubscribe = Effect.Effect<void>
|
||||
@@ -66,10 +80,13 @@ export interface Interface {
|
||||
) => Effect.Effect<Payload<D>>
|
||||
readonly subscribe: <D extends Definition>(definition: D) => Stream.Stream<Payload<D>>
|
||||
readonly all: () => Stream.Stream<Payload>
|
||||
readonly durable: (input: { readonly aggregateID: string; readonly after?: number }) => Stream.Stream<Payload>
|
||||
readonly durable: (input: {
|
||||
readonly aggregateID: string
|
||||
readonly after?: number
|
||||
}) => Stream.Stream<Payload<DurableDefinition>>
|
||||
/** @deprecated Use `all()` and consume the returned stream. */
|
||||
readonly listen: (listener: Subscriber) => Effect.Effect<Unsubscribe>
|
||||
readonly project: <D extends Definition>(definition: D, projector: Subscriber<D>) => Effect.Effect<void>
|
||||
readonly project: <D extends DurableDefinition>(definition: D, projector: Subscriber<D>) => Effect.Effect<void>
|
||||
readonly replay: (
|
||||
event: SerializedEvent,
|
||||
options?: { readonly publish?: boolean; readonly ownerID?: string; readonly strictOwner?: boolean },
|
||||
@@ -124,8 +141,8 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
)
|
||||
|
||||
function commitDurableEvent(
|
||||
definition: Definition,
|
||||
event: Payload,
|
||||
definition: DurableDefinition,
|
||||
event: UncommittedPayload<DurableDefinition>,
|
||||
input?: {
|
||||
readonly seq: number
|
||||
readonly aggregateID: string
|
||||
@@ -135,7 +152,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
commit?: (seq: number) => Effect.Effect<void>,
|
||||
) {
|
||||
return Effect.gen(function* () {
|
||||
const durable = definition?.durable
|
||||
const durable = definition.durable
|
||||
if (durable) {
|
||||
const aggregateID = (event.data as Record<string, unknown>)[durable.aggregate]
|
||||
if (typeof aggregateID !== "string") {
|
||||
@@ -200,7 +217,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
yield* Effect.die(
|
||||
new InvalidDurableEventError({
|
||||
@@ -210,7 +227,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
)
|
||||
}
|
||||
if (input && row?.ownerID && row.ownerID !== input.ownerID) {
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
const seq = input?.seq ?? latest + 1
|
||||
if (input && seq !== latest + 1) {
|
||||
@@ -234,10 +251,10 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
message: `Event ${event.id} already exists at aggregate ${stored.aggregateID} sequence ${stored.seq}`,
|
||||
}),
|
||||
)
|
||||
const committed = {
|
||||
const committed: Payload<DurableDefinition> = {
|
||||
...event,
|
||||
durable: { aggregateID, seq, version: durable.version },
|
||||
} as Payload
|
||||
}
|
||||
for (const projector of list) {
|
||||
yield* projector(committed)
|
||||
}
|
||||
@@ -267,14 +284,14 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
])
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
return { aggregateID, seq }
|
||||
return committed
|
||||
}),
|
||||
{ behavior: "immediate" },
|
||||
)
|
||||
.pipe(Effect.orDie)
|
||||
if (committed) {
|
||||
yield* Effect.forEach(
|
||||
pubsub.durable.get(committed.aggregateID) ?? [],
|
||||
pubsub.durable.get(committed.durable.aggregateID) ?? [],
|
||||
(wake) => PubSub.publish(wake, undefined),
|
||||
{ discard: true },
|
||||
)
|
||||
@@ -287,7 +304,11 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
})
|
||||
}
|
||||
|
||||
function publishEvent<D extends Definition>(definition: D, event: Payload<D>, commit?: PublishOptions["commit"]) {
|
||||
function publishEvent<D extends Definition>(
|
||||
definition: D,
|
||||
event: UncommittedPayload<D>,
|
||||
commit?: PublishOptions["commit"],
|
||||
): Effect.Effect<PublishedPayload<D>> {
|
||||
return Effect.gen(function* () {
|
||||
if (!definition?.durable && commit)
|
||||
return yield* Effect.die(
|
||||
@@ -297,22 +318,22 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
}),
|
||||
)
|
||||
if (definition?.durable) {
|
||||
const committed = yield* commitDurableEvent(definition, event as Payload, undefined, commit)
|
||||
if (committed) {
|
||||
event = {
|
||||
...event,
|
||||
durable: {
|
||||
aggregateID: committed.aggregateID,
|
||||
seq: committed.seq,
|
||||
version: definition.durable.version,
|
||||
},
|
||||
}
|
||||
yield* notify(event as Payload, true)
|
||||
return event
|
||||
}
|
||||
const committed = yield* commitDurableEvent(
|
||||
definition,
|
||||
event as UncommittedPayload<DurableDefinition>,
|
||||
undefined,
|
||||
commit,
|
||||
)
|
||||
if (!committed)
|
||||
return yield* Effect.die(
|
||||
new InvalidDurableEventError({ type: event.type, message: "New durable event was not committed" }),
|
||||
)
|
||||
yield* notify(committed, true)
|
||||
return committed as PublishedPayload<D>
|
||||
}
|
||||
yield* notify(event as Payload, false)
|
||||
return event
|
||||
const published = event as PublishedPayload<D>
|
||||
yield* notify(published, false)
|
||||
return published
|
||||
})
|
||||
}
|
||||
|
||||
@@ -353,7 +374,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
type: definition.type,
|
||||
...(location ? { location } : {}),
|
||||
data,
|
||||
} as Payload<D>,
|
||||
} as UncommittedPayload<D>,
|
||||
options?.commit,
|
||||
)
|
||||
})
|
||||
@@ -370,11 +391,11 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
new InvalidDurableEventError({ type: event.type, message: `Unknown durable event type ${event.type}` }),
|
||||
)
|
||||
} else {
|
||||
const payload = {
|
||||
const payload: UncommittedPayload<DurableDefinition> = {
|
||||
id: event.id,
|
||||
type: definition.type,
|
||||
data: Schema.decodeUnknownSync(definition.data)(event.data),
|
||||
} as Payload
|
||||
}
|
||||
const committed = yield* commitDurableEvent(definition, payload, {
|
||||
seq: event.seq,
|
||||
aggregateID: event.aggregateID,
|
||||
@@ -382,17 +403,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
strictOwner: options?.strictOwner,
|
||||
})
|
||||
if (committed && options?.publish) {
|
||||
yield* notify(
|
||||
{
|
||||
...payload,
|
||||
durable: {
|
||||
aggregateID: committed.aggregateID,
|
||||
seq: committed.seq,
|
||||
version: definition.durable.version,
|
||||
},
|
||||
},
|
||||
true,
|
||||
)
|
||||
yield* notify(committed, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -459,7 +470,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
|
||||
const streamAll = (): Stream.Stream<Payload> => Stream.fromPubSub(pubsub.all)
|
||||
|
||||
const decodeSerializedEvent = (event: SerializedEvent) => {
|
||||
const decodeSerializedEvent = (event: SerializedEvent): Payload<DurableDefinition> => {
|
||||
const definition = Durable.get(event.type)
|
||||
if (!definition?.durable) {
|
||||
throw new InvalidDurableEventError({ type: event.type, message: `Unknown durable event type ${event.type}` })
|
||||
@@ -516,7 +527,10 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
return subscription
|
||||
})
|
||||
|
||||
const durable = (input: { readonly aggregateID: string; readonly after?: number }): Stream.Stream<Payload> =>
|
||||
const durable = (input: {
|
||||
readonly aggregateID: string
|
||||
readonly after?: number
|
||||
}): Stream.Stream<Payload<DurableDefinition>> =>
|
||||
Stream.unwrap(
|
||||
Effect.gen(function* () {
|
||||
const wakes = yield* subscribeDurable(input.aggregateID)
|
||||
@@ -524,7 +538,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
const read = Effect.suspend(() => readAfter(input.aggregateID, sequence)).pipe(
|
||||
Effect.tap((events) =>
|
||||
Effect.sync(() => {
|
||||
sequence = events.at(-1)?.durable?.seq ?? sequence
|
||||
sequence = events.at(-1)?.durable.seq ?? sequence
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -546,7 +560,7 @@ export const layerWith = (options?: LayerOptions) =>
|
||||
})
|
||||
})
|
||||
|
||||
const project = <D extends Definition>(definition: D, projector: Subscriber<D>): Effect.Effect<void> =>
|
||||
const project = <D extends DurableDefinition>(definition: D, projector: Subscriber<D>): Effect.Effect<void> =>
|
||||
Effect.sync(() => {
|
||||
const list = projectors.get(definition.type) ?? []
|
||||
list.push((event) => projector(event as Payload<D>))
|
||||
|
||||
@@ -116,7 +116,7 @@ describe("EventV2", () => {
|
||||
const event = yield* events.publish(VersionedMessage, { id: "one", text: "hello" })
|
||||
|
||||
expect(event.type).toBe("test.versioned")
|
||||
expect(event.durable?.version).toBe(2)
|
||||
expect(event.durable.version).toBe(2)
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -764,7 +764,7 @@ describe("EventV2", () => {
|
||||
const replayed = {
|
||||
id: published.id,
|
||||
type: EventV2.versionedType(DurableMessage.type, 1),
|
||||
seq: published.durable!.seq,
|
||||
seq: published.durable.seq,
|
||||
aggregateID,
|
||||
data: published.data,
|
||||
}
|
||||
|
||||
@@ -99,6 +99,16 @@ describe("PublicApi OpenAPI v2 errors", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("documents durable metadata only on durable events", () => {
|
||||
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
||||
const durable = spec.components.schemas.V2EventSessionCreated
|
||||
const live = spec.components.schemas.V2EventSessionNextTextDelta
|
||||
|
||||
expect(durable?.required).toContain("durable")
|
||||
expect(durable?.properties?.durable).toBeDefined()
|
||||
expect(live?.properties?.durable).toBeUndefined()
|
||||
})
|
||||
|
||||
test("preserves /api auth responses", () => {
|
||||
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
||||
|
||||
|
||||
@@ -9,6 +9,12 @@ import { SessionEvent } from "@opencode-ai/core/session/event"
|
||||
import { SessionMessageUpdater } from "@opencode-ai/core/session/message-updater"
|
||||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||
|
||||
function durable(sessionID: SessionID, seq?: number): { aggregateID: SessionID; seq: number; version: 1 }
|
||||
function durable(sessionID: SessionID, seq: number, version: 2): { aggregateID: SessionID; seq: number; version: 2 }
|
||||
function durable(sessionID: SessionID, seq = 0, version: 1 | 2 = 1) {
|
||||
return { aggregateID: sessionID, seq, version }
|
||||
}
|
||||
|
||||
test.skip("step snapshots carry over to assistant messages", () => {
|
||||
const state: SessionMessageUpdater.MemoryState = { messages: [] }
|
||||
const sessionID = SessionID.make("session")
|
||||
@@ -17,6 +23,7 @@ test.skip("step snapshots carry over to assistant messages", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID),
|
||||
type: "session.next.step.started",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -38,6 +45,7 @@ test.skip("step snapshots carry over to assistant messages", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID, 1, 2),
|
||||
type: "session.next.step.ended",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -70,6 +78,7 @@ test.skip("text ended populates assistant text content", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID),
|
||||
type: "session.next.step.started",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -88,6 +97,7 @@ test.skip("text ended populates assistant text content", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID, 1),
|
||||
type: "session.next.text.started",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -101,6 +111,7 @@ test.skip("text ended populates assistant text content", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID, 2),
|
||||
type: "session.next.text.ended",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -126,6 +137,7 @@ test.skip("tool completion stores completed timestamp", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID),
|
||||
type: "session.next.step.started",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -144,6 +156,7 @@ test.skip("tool completion stores completed timestamp", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID, 1),
|
||||
type: "session.next.tool.input.started",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -158,6 +171,7 @@ test.skip("tool completion stores completed timestamp", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID, 2),
|
||||
type: "session.next.tool.called",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -174,6 +188,7 @@ test.skip("tool completion stores completed timestamp", () => {
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID, 3),
|
||||
type: "session.next.tool.success",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -204,6 +219,7 @@ test("compaction events reduce to compaction message only when completed", () =>
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id,
|
||||
durable: durable(sessionID),
|
||||
type: "session.next.compaction.started",
|
||||
data: {
|
||||
sessionID,
|
||||
@@ -245,6 +261,7 @@ test("compaction events reduce to compaction message only when completed", () =>
|
||||
Effect.runSync(
|
||||
SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
|
||||
id: EventV2.ID.create(),
|
||||
durable: durable(sessionID, 3),
|
||||
type: "session.next.compaction.ended",
|
||||
data: {
|
||||
sessionID,
|
||||
|
||||
@@ -8,18 +8,24 @@ import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
const fields = {
|
||||
id: Event.ID,
|
||||
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
||||
durable: Schema.optional(Schema.Struct({ aggregateID: Schema.String, seq: Schema.Int, version: Schema.Int })),
|
||||
location: Schema.optional(Location.Ref),
|
||||
}
|
||||
|
||||
const schema = (definitions: ReadonlyArray<Definition>) =>
|
||||
Schema.Union([
|
||||
...definitions.map((definition) =>
|
||||
Schema.Struct({
|
||||
...fields,
|
||||
type: Schema.Literal(definition.type),
|
||||
data: definition.data,
|
||||
}).annotate({ identifier: `V2Event.${definition.type}` }),
|
||||
definition.durable
|
||||
? Schema.Struct({
|
||||
...fields,
|
||||
durable: Event.durableEnvelope(definition.durable.version),
|
||||
type: Schema.Literal(definition.type),
|
||||
data: definition.data,
|
||||
}).annotate({ identifier: `V2Event.${definition.type}` })
|
||||
: Schema.Struct({
|
||||
...fields,
|
||||
type: Schema.Literal(definition.type),
|
||||
data: definition.data,
|
||||
}).annotate({ identifier: `V2Event.${definition.type}` }),
|
||||
),
|
||||
...(definitions.some((definition) => definition.type === "server.connected")
|
||||
? []
|
||||
|
||||
@@ -3,7 +3,7 @@ export * as Event from "./event"
|
||||
import { Schema } from "effect"
|
||||
import { ascending } from "./identifier"
|
||||
import { Location } from "./location"
|
||||
import { statics } from "./schema"
|
||||
import { NonNegativeInt, statics } from "./schema"
|
||||
|
||||
export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe(
|
||||
Schema.brand("Event.ID"),
|
||||
@@ -11,53 +11,95 @@ export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe(
|
||||
)
|
||||
export type ID = typeof ID.Type
|
||||
|
||||
export type Definition<
|
||||
export type DurableOptions = {
|
||||
readonly version: number
|
||||
readonly aggregate: string
|
||||
}
|
||||
|
||||
export type DurableEnvelope<Version extends number = number> = {
|
||||
readonly aggregateID: string
|
||||
readonly seq: number
|
||||
readonly version: Version
|
||||
}
|
||||
|
||||
export const durableEnvelope = <const Version extends number>(version: Version) =>
|
||||
Schema.Struct({ aggregateID: Schema.String, seq: NonNegativeInt, version: Schema.Literal(version) })
|
||||
|
||||
const NoDurableEnvelope = Schema.optional(Schema.Never)
|
||||
|
||||
export type LiveDefinition<
|
||||
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
|
||||
readonly durable?: never
|
||||
}
|
||||
|
||||
export type DurableDefinition<
|
||||
Type extends string = string,
|
||||
DataSchema extends Schema.Codec<unknown, unknown> = Schema.Codec<unknown, unknown>,
|
||||
Durability extends DurableOptions = DurableOptions,
|
||||
> = Schema.Top & {
|
||||
readonly type: Type
|
||||
readonly data: DataSchema
|
||||
readonly durable: Durability
|
||||
}
|
||||
|
||||
export type Definition = LiveDefinition | DurableDefinition
|
||||
|
||||
type Defined<
|
||||
Type extends string,
|
||||
DataSchema extends Schema.Codec<unknown, unknown>,
|
||||
Durability extends DurableOptions | undefined,
|
||||
> = Durability extends DurableOptions
|
||||
? DurableDefinition<Type, DataSchema, Durability>
|
||||
: LiveDefinition<Type, 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 type UncommittedPayload<D extends Definition = Definition> = D extends Definition
|
||||
? {
|
||||
readonly id: ID
|
||||
readonly type: D["type"]
|
||||
readonly data: Data<D>
|
||||
readonly location?: Location.Ref
|
||||
readonly metadata?: Record<string, unknown>
|
||||
}
|
||||
: never
|
||||
|
||||
export type PublishedPayload<D extends Definition = Definition> = D extends Definition
|
||||
? UncommittedPayload<D> &
|
||||
(D extends { readonly durable: infer Durability extends DurableOptions }
|
||||
? { readonly durable: DurableEnvelope<Durability["version"]> }
|
||||
: { readonly durable?: never })
|
||||
: never
|
||||
|
||||
export type Payload<D extends Definition = Definition> = PublishedPayload<D>
|
||||
|
||||
type EventSchema<
|
||||
Type extends string,
|
||||
Fields extends Readonly<Record<PropertyKey, Schema.Codec<unknown, unknown>>>,
|
||||
Durability extends DurableOptions | undefined,
|
||||
> = Schema.Schema<PublishedPayload<Defined<Type, Schema.Struct<Fields>, Durability>>> &
|
||||
Defined<Type, Schema.Struct<Fields>, Durability>
|
||||
|
||||
export function define<
|
||||
const Type extends string,
|
||||
Fields extends Readonly<Record<PropertyKey, Schema.Codec<unknown, unknown>>>,
|
||||
const Durability extends DurableOptions | undefined = undefined,
|
||||
>(input: {
|
||||
readonly type: Type
|
||||
readonly durable?: {
|
||||
readonly version: number
|
||||
readonly aggregate: string
|
||||
}
|
||||
readonly durable?: Durability
|
||||
readonly schema: Fields
|
||||
}): Schema.Schema<Payload<Definition<Type, Schema.Struct<Fields>>>> & Definition<Type, Schema.Struct<Fields>> {
|
||||
}): EventSchema<Type, Fields, Durability> {
|
||||
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 }),
|
||||
),
|
||||
durable: input.durable === undefined ? NoDurableEnvelope : durableEnvelope(input.durable.version),
|
||||
location: Schema.optional(Location.Ref),
|
||||
data,
|
||||
}).annotate({ identifier: input.type }),
|
||||
@@ -66,7 +108,7 @@ export function define<
|
||||
...(input.durable === undefined ? {} : { durable: input.durable }),
|
||||
data,
|
||||
},
|
||||
) as Schema.Schema<Payload<Definition<Type, Schema.Struct<Fields>>>> & Definition<Type, Schema.Struct<Fields>>
|
||||
) as unknown as EventSchema<Type, Fields, Durability>
|
||||
}
|
||||
|
||||
export function inventory<const Definitions extends ReadonlyArray<Definition>>(...definitions: Definitions) {
|
||||
@@ -103,7 +145,7 @@ export function durable(definitions: ReadonlyArray<Definition>) {
|
||||
if (result.has(key)) throw new Error(`Duplicate durable event definition for ${key}`)
|
||||
result.set(key, definition)
|
||||
return result
|
||||
}, new Map<string, Definition>()),
|
||||
}, new Map<string, DurableDefinition>()),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,99 @@ describe("public event schemas", () => {
|
||||
|
||||
expect(Event.durable([definition]).get("test.durable.1")).toBe(definition)
|
||||
})
|
||||
|
||||
test("durable definitions require published commit metadata", () => {
|
||||
const definition = Event.define({
|
||||
type: "test.durable",
|
||||
durable: { aggregate: "id", version: 1 },
|
||||
schema: { id: Schema.String },
|
||||
})
|
||||
const payload: typeof definition.Type = {
|
||||
id: Event.ID.create(),
|
||||
type: definition.type,
|
||||
durable: { aggregateID: "aggregate", seq: 0, version: 1 },
|
||||
data: { id: "aggregate" },
|
||||
}
|
||||
|
||||
expect(Schema.is(definition)(payload)).toBe(true)
|
||||
expect(
|
||||
Schema.is(definition)({
|
||||
id: Event.ID.create(),
|
||||
type: definition.type,
|
||||
data: { id: "aggregate" },
|
||||
}),
|
||||
).toBe(false)
|
||||
expect(Schema.is(definition)({ ...payload, durable: { ...payload.durable, seq: -1 } })).toBe(false)
|
||||
expect(Schema.is(definition)({ ...payload, durable: { ...payload.durable, version: 2 } })).toBe(false)
|
||||
|
||||
// @ts-expect-error Published durable payloads require commit metadata.
|
||||
const missing: typeof definition.Type = { id: Event.ID.create(), type: definition.type, data: { id: "aggregate" } }
|
||||
void missing
|
||||
})
|
||||
|
||||
test("live definitions reject durable commit metadata", () => {
|
||||
const definition = Event.define({
|
||||
type: "test.live",
|
||||
schema: { value: Schema.String },
|
||||
})
|
||||
const payload: typeof definition.Type = {
|
||||
id: Event.ID.create(),
|
||||
type: definition.type,
|
||||
data: { value: "value" },
|
||||
}
|
||||
|
||||
expect(Schema.is(definition)(payload)).toBe(true)
|
||||
expect(
|
||||
Schema.is(definition)({
|
||||
...payload,
|
||||
durable: { aggregateID: "aggregate", seq: 0, version: 1 },
|
||||
}),
|
||||
).toBe(false)
|
||||
|
||||
const invalid: typeof definition.Type = {
|
||||
...payload,
|
||||
// @ts-expect-error Live payloads cannot carry durable commit metadata.
|
||||
durable: { aggregateID: "aggregate", seq: 0, version: 1 },
|
||||
}
|
||||
void invalid
|
||||
})
|
||||
|
||||
test("mixed definition payloads preserve durability correlation", () => {
|
||||
const durable = Event.define({
|
||||
type: "test.mixed.durable",
|
||||
durable: { aggregate: "id", version: 2 },
|
||||
schema: { id: Schema.String },
|
||||
})
|
||||
const live = Event.define({
|
||||
type: "test.mixed.live",
|
||||
schema: { value: Schema.String },
|
||||
})
|
||||
type Mixed = Event.Payload<typeof durable | typeof live>
|
||||
|
||||
const committed: Mixed = {
|
||||
id: Event.ID.create(),
|
||||
type: durable.type,
|
||||
durable: { aggregateID: "aggregate", seq: 0, version: 2 },
|
||||
data: { id: "aggregate" },
|
||||
}
|
||||
const ephemeral: Mixed = {
|
||||
id: Event.ID.create(),
|
||||
type: live.type,
|
||||
data: { value: "value" },
|
||||
}
|
||||
void committed
|
||||
void ephemeral
|
||||
|
||||
// @ts-expect-error Durable union members require commit metadata.
|
||||
const uncommitted: Mixed = { id: Event.ID.create(), type: durable.type, data: { id: "aggregate" } }
|
||||
const falselyCommitted: Mixed = {
|
||||
id: Event.ID.create(),
|
||||
type: live.type,
|
||||
// @ts-expect-error Live union members cannot carry durable commit metadata.
|
||||
durable: { aggregateID: "aggregate", seq: 0, version: 2 },
|
||||
data: { value: "value" },
|
||||
}
|
||||
void uncommitted
|
||||
void falselyCommitted
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4328,11 +4328,6 @@ export type V2EventModelsDevRefreshed = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "models-dev.refreshed"
|
||||
data: {
|
||||
@@ -4345,11 +4340,6 @@ export type V2EventIntegrationUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "integration.updated"
|
||||
data: {
|
||||
@@ -4362,11 +4352,6 @@ export type V2EventIntegrationConnectionUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "integration.connection.updated"
|
||||
data: {
|
||||
@@ -4379,11 +4364,6 @@ export type V2EventCatalogUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "catalog.updated"
|
||||
data: {
|
||||
@@ -4396,12 +4376,12 @@ export type V2EventSessionCreated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.created"
|
||||
data: {
|
||||
sessionID: string
|
||||
@@ -4414,12 +4394,12 @@ export type V2EventSessionUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.updated"
|
||||
data: {
|
||||
sessionID: string
|
||||
@@ -4432,12 +4412,12 @@ export type V2EventSessionDeleted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.deleted"
|
||||
data: {
|
||||
sessionID: string
|
||||
@@ -4450,12 +4430,12 @@ export type V2EventMessageUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "message.updated"
|
||||
data: {
|
||||
sessionID: string
|
||||
@@ -4468,12 +4448,12 @@ export type V2EventMessageRemoved = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "message.removed"
|
||||
data: {
|
||||
sessionID: string
|
||||
@@ -4486,12 +4466,12 @@ export type V2EventMessagePartUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "message.part.updated"
|
||||
data: {
|
||||
sessionID: string
|
||||
@@ -4505,12 +4485,12 @@ export type V2EventMessagePartRemoved = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "message.part.removed"
|
||||
data: {
|
||||
sessionID: string
|
||||
@@ -4524,12 +4504,12 @@ export type V2EventSessionNextAgentSwitched = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.agent.switched"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4544,12 +4524,12 @@ export type V2EventSessionNextModelSwitched = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.model.switched"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4568,12 +4548,12 @@ export type V2EventSessionNextMoved = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.moved"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4588,12 +4568,12 @@ export type V2EventSessionNextPrompted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.prompted"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4609,12 +4589,12 @@ export type V2EventSessionNextPromptAdmitted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.prompt.admitted"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4630,12 +4610,12 @@ export type V2EventSessionNextContextUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.context.updated"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4650,12 +4630,12 @@ export type V2EventSessionNextSynthetic = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.synthetic"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4670,12 +4650,12 @@ export type V2EventSessionNextShellStarted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.shell.started"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4691,12 +4671,12 @@ export type V2EventSessionNextShellEnded = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.shell.ended"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4711,12 +4691,12 @@ export type V2EventSessionNextStepStarted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.step.started"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4737,12 +4717,12 @@ export type V2EventSessionNextStepEnded = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 2
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.step.ended"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4769,12 +4749,12 @@ export type V2EventSessionNextStepFailed = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 2
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.step.failed"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4789,12 +4769,12 @@ export type V2EventSessionNextTextStarted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.text.started"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4809,11 +4789,6 @@ export type V2EventSessionNextTextDelta = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.text.delta"
|
||||
data: {
|
||||
@@ -4830,12 +4805,12 @@ export type V2EventSessionNextTextEnded = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.text.ended"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4851,12 +4826,12 @@ export type V2EventSessionNextReasoningStarted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.reasoning.started"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4876,11 +4851,6 @@ export type V2EventSessionNextReasoningDelta = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.reasoning.delta"
|
||||
data: {
|
||||
@@ -4897,12 +4867,12 @@ export type V2EventSessionNextReasoningEnded = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.reasoning.ended"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4923,12 +4893,12 @@ export type V2EventSessionNextToolInputStarted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.tool.input.started"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4944,11 +4914,6 @@ export type V2EventSessionNextToolInputDelta = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.tool.input.delta"
|
||||
data: {
|
||||
@@ -4965,12 +4930,12 @@ export type V2EventSessionNextToolInputEnded = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.tool.input.ended"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -4986,12 +4951,12 @@ export type V2EventSessionNextToolCalled = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.tool.called"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5018,12 +4983,12 @@ export type V2EventSessionNextToolProgress = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.tool.progress"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5042,12 +5007,12 @@ export type V2EventSessionNextToolSuccess = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.tool.success"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5076,12 +5041,12 @@ export type V2EventSessionNextToolFailed = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.tool.failed"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5106,12 +5071,12 @@ export type V2EventSessionNextRetried = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.retried"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5126,12 +5091,12 @@ export type V2EventSessionNextCompactionStarted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.compaction.started"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5146,11 +5111,6 @@ export type V2EventSessionNextCompactionDelta = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.compaction.delta"
|
||||
data: {
|
||||
@@ -5166,12 +5126,12 @@ export type V2EventSessionNextCompactionEnded = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.compaction.ended"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5188,12 +5148,12 @@ export type V2EventSessionNextRevertStaged = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.revert.staged"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5213,12 +5173,12 @@ export type V2EventSessionNextRevertCleared = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.revert.cleared"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5231,12 +5191,12 @@ export type V2EventSessionNextRevertCommitted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
location?: LocationRef
|
||||
durable: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
version: 1
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.next.revert.committed"
|
||||
data: {
|
||||
timestamp: number
|
||||
@@ -5250,11 +5210,6 @@ export type V2EventMessagePartDelta = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "message.part.delta"
|
||||
data: {
|
||||
@@ -5271,11 +5226,6 @@ export type V2EventSessionDiff = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.diff"
|
||||
data: {
|
||||
@@ -5289,11 +5239,6 @@ export type V2EventSessionError = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.error"
|
||||
data: {
|
||||
@@ -5315,11 +5260,6 @@ export type V2EventInstallationUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "installation.updated"
|
||||
data: {
|
||||
@@ -5332,11 +5272,6 @@ export type V2EventInstallationUpdateAvailable = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "installation.update-available"
|
||||
data: {
|
||||
@@ -5349,11 +5284,6 @@ export type V2EventFileEdited = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "file.edited"
|
||||
data: {
|
||||
@@ -5366,11 +5296,6 @@ export type V2EventReferenceUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "reference.updated"
|
||||
data: {
|
||||
@@ -5383,11 +5308,6 @@ export type V2EventPermissionV2Asked = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "permission.v2.asked"
|
||||
data: {
|
||||
@@ -5408,11 +5328,6 @@ export type V2EventPermissionV2Replied = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "permission.v2.replied"
|
||||
data: {
|
||||
@@ -5427,11 +5342,6 @@ export type V2EventPluginAdded = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "plugin.added"
|
||||
data: {
|
||||
@@ -5444,11 +5354,6 @@ export type V2EventProjectDirectoriesUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "project.directories.updated"
|
||||
data: {
|
||||
@@ -5461,11 +5366,6 @@ export type V2EventFileWatcherUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "file.watcher.updated"
|
||||
data: {
|
||||
@@ -5479,11 +5379,6 @@ export type V2EventPtyCreated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "pty.created"
|
||||
data: {
|
||||
@@ -5496,11 +5391,6 @@ export type V2EventPtyUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "pty.updated"
|
||||
data: {
|
||||
@@ -5513,11 +5403,6 @@ export type V2EventPtyExited = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "pty.exited"
|
||||
data: {
|
||||
@@ -5531,11 +5416,6 @@ export type V2EventPtyDeleted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "pty.deleted"
|
||||
data: {
|
||||
@@ -5548,11 +5428,6 @@ export type V2EventQuestionV2Asked = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "question.v2.asked"
|
||||
data: {
|
||||
@@ -5571,11 +5446,6 @@ export type V2EventQuestionV2Replied = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "question.v2.replied"
|
||||
data: {
|
||||
@@ -5590,11 +5460,6 @@ export type V2EventQuestionV2Rejected = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "question.v2.rejected"
|
||||
data: {
|
||||
@@ -5608,11 +5473,6 @@ export type V2EventTodoUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "todo.updated"
|
||||
data: {
|
||||
@@ -5626,11 +5486,6 @@ export type V2EventLspUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "lsp.updated"
|
||||
data: {
|
||||
@@ -5643,11 +5498,6 @@ export type V2EventPermissionAsked = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "permission.asked"
|
||||
data: {
|
||||
@@ -5671,11 +5521,6 @@ export type V2EventPermissionReplied = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "permission.replied"
|
||||
data: {
|
||||
@@ -5690,11 +5535,6 @@ export type V2EventTuiPromptAppend = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "tui.prompt.append"
|
||||
data: {
|
||||
@@ -5707,11 +5547,6 @@ export type V2EventTuiCommandExecute = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "tui.command.execute"
|
||||
data: {
|
||||
@@ -5741,11 +5576,6 @@ export type V2EventTuiToastShow = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "tui.toast.show"
|
||||
data: {
|
||||
@@ -5761,11 +5591,6 @@ export type V2EventTuiSessionSelect = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "tui.session.select"
|
||||
data: {
|
||||
@@ -5781,11 +5606,6 @@ export type V2EventMcpToolsChanged = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "mcp.tools.changed"
|
||||
data: {
|
||||
@@ -5798,11 +5618,6 @@ export type V2EventMcpBrowserOpenFailed = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "mcp.browser.open.failed"
|
||||
data: {
|
||||
@@ -5816,11 +5631,6 @@ export type V2EventCommandExecuted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "command.executed"
|
||||
data: {
|
||||
@@ -5836,11 +5646,6 @@ export type V2EventProjectUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "project.updated"
|
||||
data: {
|
||||
@@ -5873,11 +5678,6 @@ export type V2EventSessionStatus = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.status"
|
||||
data: {
|
||||
@@ -5891,11 +5691,6 @@ export type V2EventSessionIdle = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.idle"
|
||||
data: {
|
||||
@@ -5908,11 +5703,6 @@ export type V2EventQuestionAsked = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "question.asked"
|
||||
data: {
|
||||
@@ -5931,11 +5721,6 @@ export type V2EventQuestionReplied = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "question.replied"
|
||||
data: {
|
||||
@@ -5950,11 +5735,6 @@ export type V2EventQuestionRejected = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "question.rejected"
|
||||
data: {
|
||||
@@ -5968,11 +5748,6 @@ export type V2EventSessionCompacted = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "session.compacted"
|
||||
data: {
|
||||
@@ -5985,11 +5760,6 @@ export type V2EventVcsBranchUpdated = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "vcs.branch.updated"
|
||||
data: {
|
||||
@@ -6002,11 +5772,6 @@ export type V2EventWorkspaceReady = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "workspace.ready"
|
||||
data: {
|
||||
@@ -6019,11 +5784,6 @@ export type V2EventWorkspaceFailed = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "workspace.failed"
|
||||
data: {
|
||||
@@ -6036,11 +5796,6 @@ export type V2EventWorkspaceStatus = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "workspace.status"
|
||||
data: {
|
||||
@@ -6054,11 +5809,6 @@ export type V2EventWorktreeReady = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "worktree.ready"
|
||||
data: {
|
||||
@@ -6072,11 +5822,6 @@ export type V2EventWorktreeFailed = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "worktree.failed"
|
||||
data: {
|
||||
@@ -6089,11 +5834,6 @@ export type V2EventServerConnected = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "server.connected"
|
||||
data: {
|
||||
@@ -6106,11 +5846,6 @@ export type V2EventGlobalDisposed = {
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
durable?: {
|
||||
aggregateID: string
|
||||
seq: number
|
||||
version: number
|
||||
}
|
||||
location?: LocationRef
|
||||
type: "global.disposed"
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user