diff --git a/packages/client/src/promise/generated/types.ts b/packages/client/src/promise/generated/types.ts index 798155e88e..13dab421d5 100644 --- a/packages/client/src/promise/generated/types.ts +++ b/packages/client/src/promise/generated/types.ts @@ -1107,74 +1107,75 @@ export type SessionLogOutput = | ( | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.agent.switched" + readonly type: "agent.selected" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly agent: string - } + readonly data: { readonly sessionID: string; readonly agent: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.model.switched" + readonly type: "model.selected" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string } } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.moved" + readonly type: "session.moved" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly location: { readonly directory: string; readonly workspaceID?: string } - readonly subdirectory?: string + readonly subpath?: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.renamed" + readonly type: "renamed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { readonly timestamp: number; readonly sessionID: string; readonly title: string } + readonly data: { readonly sessionID: string; readonly title: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.forked" + readonly type: "forked" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly parentID: string - readonly messageID?: string - } + readonly data: { readonly sessionID: string; readonly parentID: string; readonly from?: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.prompted" + readonly type: "prompt.promoted" + readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } + readonly location?: { readonly directory: string; readonly workspaceID?: string } + readonly data: { readonly sessionID: string; readonly inputID: string } + } + | { + readonly id: string + readonly created: number + readonly metadata?: { readonly [x: string]: unknown } + readonly type: "prompt.admitted" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string + readonly inputID: string readonly prompt: { readonly text: string readonly files?: ReadonlyArray<{ @@ -1194,54 +1195,22 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.prompt.admitted" + readonly type: "session.context.updated" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly prompt: { - readonly text: string - readonly files?: ReadonlyArray<{ - readonly uri: string - readonly mime: string - readonly name?: string - readonly description?: string - readonly source?: { readonly start: number; readonly end: number; readonly text: string } - }> - readonly agents?: ReadonlyArray<{ - readonly name: string - readonly source?: { readonly start: number; readonly end: number; readonly text: string } - }> - } - readonly delivery: "steer" | "queue" - } + readonly data: { readonly sessionID: string; readonly text: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.context.updated" + readonly type: "synthetic" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string - readonly text: string - } - } - | { - readonly id: string - readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.synthetic" - readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } - readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string readonly text: string readonly description?: string readonly metadata?: { readonly [x: string]: unknown } @@ -1249,53 +1218,39 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.skill.activated" + readonly type: "skill.activated" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly name: string - readonly text: string - } + readonly data: { readonly sessionID: string; readonly name: string; readonly text: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.shell.started" + readonly type: "shell.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly callID: string - readonly command: string - } + readonly data: { readonly sessionID: string; readonly callID: string; readonly command: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.shell.ended" + readonly type: "shell.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly callID: string - readonly output: string - } + readonly data: { readonly sessionID: string; readonly callID: string; readonly output: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.step.started" + readonly type: "step.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly agent: string @@ -1305,12 +1260,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.step.ended" + readonly type: "step.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly finish: string @@ -1327,12 +1282,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.step.failed" + readonly type: "step.failed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly error: { readonly type: "unknown"; readonly message: string } @@ -1340,25 +1295,21 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.text.started" + readonly type: "text.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly assistantMessageID: string - readonly textID: string - } + readonly data: { readonly sessionID: string; readonly assistantMessageID: string; readonly textID: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.text.ended" + readonly type: "text.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly textID: string @@ -1367,12 +1318,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.reasoning.started" + readonly type: "reasoning.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly reasoningID: string @@ -1381,12 +1332,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.reasoning.ended" + readonly type: "reasoning.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly reasoningID: string @@ -1396,12 +1347,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.input.started" + readonly type: "tool.input.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -1410,12 +1361,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.input.ended" + readonly type: "tool.input.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -1424,12 +1375,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.called" + readonly type: "tool.called" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -1443,12 +1394,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.progress" + readonly type: "tool.progress" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -1461,12 +1412,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.success" + readonly type: "tool.success" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -1485,12 +1436,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.failed" + readonly type: "tool.failed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -1504,12 +1455,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.retried" + readonly type: "retried" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly attempt: number readonly error: { @@ -1524,27 +1475,22 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.compaction.started" + readonly type: "compaction.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly reason: "auto" | "manual" - } + readonly data: { readonly sessionID: string; readonly reason: "auto" | "manual" } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.compaction.ended" + readonly type: "compaction.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string readonly reason: "auto" | "manual" readonly text: string readonly recent: string @@ -1552,12 +1498,12 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.revert.staged" + readonly type: "revert.staged" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly revert: { readonly messageID: string @@ -1576,19 +1522,21 @@ export type SessionLogOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.revert.cleared" + readonly type: "revert.cleared" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { readonly timestamp: number; readonly sessionID: string } + readonly data: { readonly sessionID: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.revert.committed" + readonly type: "revert.committed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { readonly timestamp: number; readonly sessionID: string; readonly messageID: string } + readonly data: { readonly sessionID: string; readonly messageID: string } } ) | { readonly type: "log.synced"; readonly aggregateID: string; readonly seq?: number } @@ -3771,6 +3719,7 @@ export type SkillListOutput = { export type EventSubscribeOutput = | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "models-dev.refreshed" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -3778,6 +3727,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "integration.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -3785,6 +3735,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "integration.connection.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -3792,6 +3743,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "catalog.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -3799,6 +3751,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "agent.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -3806,6 +3759,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "session.created" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } @@ -3867,6 +3821,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "session.updated" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } @@ -3928,6 +3883,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "session.deleted" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } @@ -3989,6 +3945,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "message.updated" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } @@ -4093,6 +4050,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "message.removed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } @@ -4101,6 +4059,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "message.part.updated" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } @@ -4339,6 +4298,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "message.part.removed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } @@ -4347,74 +4307,75 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.agent.switched" + readonly type: "agent.selected" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly agent: string - } + readonly data: { readonly sessionID: string; readonly agent: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.model.switched" + readonly type: "model.selected" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string } } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.moved" + readonly type: "session.moved" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly location: { readonly directory: string; readonly workspaceID?: string } - readonly subdirectory?: string + readonly subpath?: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.renamed" + readonly type: "renamed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { readonly timestamp: number; readonly sessionID: string; readonly title: string } + readonly data: { readonly sessionID: string; readonly title: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.forked" + readonly type: "forked" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly parentID: string - readonly messageID?: string - } + readonly data: { readonly sessionID: string; readonly parentID: string; readonly from?: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.prompted" + readonly type: "prompt.promoted" + readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } + readonly location?: { readonly directory: string; readonly workspaceID?: string } + readonly data: { readonly sessionID: string; readonly inputID: string } + } + | { + readonly id: string + readonly created: number + readonly metadata?: { readonly [x: string]: unknown } + readonly type: "prompt.admitted" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string + readonly inputID: string readonly prompt: { readonly text: string readonly files?: ReadonlyArray<{ @@ -4434,38 +4395,11 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.prompt.admitted" - readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } + readonly type: "execution.settled" readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly prompt: { - readonly text: string - readonly files?: ReadonlyArray<{ - readonly uri: string - readonly mime: string - readonly name?: string - readonly description?: string - readonly source?: { readonly start: number; readonly end: number; readonly text: string } - }> - readonly agents?: ReadonlyArray<{ - readonly name: string - readonly source?: { readonly start: number; readonly end: number; readonly text: string } - }> - } - readonly delivery: "steer" | "queue" - } - } - | { - readonly id: string - readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.execution.settled" - readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number readonly sessionID: string readonly outcome: "success" | "failure" | "interrupted" readonly error?: { readonly type: "unknown"; readonly message: string } @@ -4473,27 +4407,22 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.context.updated" + readonly type: "session.context.updated" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly text: string - } + readonly data: { readonly sessionID: string; readonly text: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.synthetic" + readonly type: "synthetic" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string readonly text: string readonly description?: string readonly metadata?: { readonly [x: string]: unknown } @@ -4501,53 +4430,39 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.skill.activated" + readonly type: "skill.activated" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly name: string - readonly text: string - } + readonly data: { readonly sessionID: string; readonly name: string; readonly text: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.shell.started" + readonly type: "shell.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly callID: string - readonly command: string - } + readonly data: { readonly sessionID: string; readonly callID: string; readonly command: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.shell.ended" + readonly type: "shell.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly callID: string - readonly output: string - } + readonly data: { readonly sessionID: string; readonly callID: string; readonly output: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.step.started" + readonly type: "step.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly agent: string @@ -4557,12 +4472,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.step.ended" + readonly type: "step.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly finish: string @@ -4579,12 +4494,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.step.failed" + readonly type: "step.failed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly error: { readonly type: "unknown"; readonly message: string } @@ -4592,24 +4507,20 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.text.started" + readonly type: "text.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly assistantMessageID: string - readonly textID: string - } + readonly data: { readonly sessionID: string; readonly assistantMessageID: string; readonly textID: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.text.delta" + readonly type: "text.delta" readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly textID: string @@ -4618,12 +4529,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.text.ended" + readonly type: "text.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly textID: string @@ -4632,12 +4543,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.reasoning.started" + readonly type: "reasoning.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly reasoningID: string @@ -4646,11 +4557,11 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.reasoning.delta" + readonly type: "reasoning.delta" readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly reasoningID: string @@ -4659,12 +4570,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.reasoning.ended" + readonly type: "reasoning.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly reasoningID: string @@ -4674,12 +4585,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.input.started" + readonly type: "tool.input.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -4688,11 +4599,11 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.input.delta" + readonly type: "tool.input.delta" readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -4701,12 +4612,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.input.ended" + readonly type: "tool.input.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -4715,12 +4626,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.called" + readonly type: "tool.called" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -4734,12 +4645,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.progress" + readonly type: "tool.progress" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -4752,12 +4663,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.success" + readonly type: "tool.success" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -4776,12 +4687,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.tool.failed" + readonly type: "tool.failed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly assistantMessageID: string readonly callID: string @@ -4795,12 +4706,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.retried" + readonly type: "retried" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly attempt: number readonly error: { @@ -4815,39 +4726,30 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.compaction.started" + readonly type: "compaction.started" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly reason: "auto" | "manual" - } + readonly data: { readonly sessionID: string; readonly reason: "auto" | "manual" } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.compaction.delta" + readonly type: "compaction.delta" readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { - readonly timestamp: number - readonly sessionID: string - readonly messageID: string - readonly text: string - } + readonly data: { readonly sessionID: string; readonly text: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.compaction.ended" + readonly type: "compaction.ended" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string - readonly messageID: string readonly reason: "auto" | "manual" readonly text: string readonly recent: string @@ -4855,12 +4757,12 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.revert.staged" + readonly type: "revert.staged" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { - readonly timestamp: number readonly sessionID: string readonly revert: { readonly messageID: string @@ -4879,22 +4781,25 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.revert.cleared" + readonly type: "revert.cleared" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { readonly timestamp: number; readonly sessionID: string } + readonly data: { readonly sessionID: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } - readonly type: "session.next.revert.committed" + readonly type: "revert.committed" readonly durable: { readonly aggregateID: string; readonly seq: number; readonly version: number } readonly location?: { readonly directory: string; readonly workspaceID?: string } - readonly data: { readonly timestamp: number; readonly sessionID: string; readonly messageID: string } + readonly data: { readonly sessionID: string; readonly messageID: string } } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "file.edited" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4902,6 +4807,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "reference.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4909,6 +4815,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "permission.v2.asked" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4924,6 +4831,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "permission.v2.replied" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4935,6 +4843,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "plugin.added" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4942,6 +4851,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "project.directories.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4949,6 +4859,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "command.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4956,6 +4867,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "skill.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4963,6 +4875,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "file.watcher.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4970,6 +4883,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "pty.created" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -4988,6 +4902,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "pty.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5006,6 +4921,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "pty.exited" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5013,6 +4929,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "pty.deleted" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5020,6 +4937,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "shell.created" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5040,6 +4958,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "shell.exited" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5051,6 +4970,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "shell.deleted" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5058,6 +4978,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "question.v2.asked" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5076,6 +4997,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "question.v2.replied" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5087,6 +5009,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "question.v2.rejected" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5094,6 +5017,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "form.created" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5208,6 +5132,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "form.replied" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5219,6 +5144,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "form.cancelled" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5226,6 +5152,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "todo.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5236,6 +5163,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "session.status" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5262,6 +5190,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "session.idle" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5269,6 +5198,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "tui.prompt.append" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5276,6 +5206,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "tui.command.execute" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5303,6 +5234,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "tui.toast.show" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5315,6 +5247,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "tui.session.select" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5322,6 +5255,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "installation.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5329,6 +5263,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "installation.update-available" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5336,6 +5271,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "vcs.branch.updated" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5343,6 +5279,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "mcp.status.changed" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5350,6 +5287,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "permission.asked" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5365,6 +5303,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "permission.replied" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5376,6 +5315,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "question.asked" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5394,6 +5334,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "question.replied" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5405,6 +5346,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "question.rejected" readonly location?: { readonly directory: string; readonly workspaceID?: string } @@ -5412,6 +5354,7 @@ export type EventSubscribeOutput = } | { readonly id: string + readonly created: number readonly metadata?: { readonly [x: string]: unknown } readonly type: "session.error" readonly location?: { readonly directory: string; readonly workspaceID?: string } diff --git a/packages/client/test/effect.test.ts b/packages/client/test/effect.test.ts index 445a9cf3de..5a7ec9b7cc 100644 --- a/packages/client/test/effect.test.ts +++ b/packages/client/test/effect.test.ts @@ -1,7 +1,17 @@ import { expect, test } from "bun:test" import { DateTime, Effect, Stream } from "effect" import { HttpClient, HttpClientResponse } from "effect/unstable/http" -import { AbsolutePath, Agent, Event, Location, Model, OpenCode, Prompt, Session, SessionMessage } from "../src/effect/index" +import { + AbsolutePath, + Agent, + Event, + Location, + Model, + OpenCode, + Prompt, + Session, + SessionMessage, +} from "../src/effect/index" const synced = { type: "log.synced" as const, aggregateID: "ses_test", seq: Event.Seq.make(1) } @@ -23,7 +33,7 @@ test("event.subscribe exposes and decodes the native Effect event stream", async HttpClientResponse.fromWeb( request, new Response( - `data: ${JSON.stringify({ id: "evt_connected", type: "server.connected", data: {} })}\n\n` + + `data: ${JSON.stringify({ id: "evt_connected", created: 0, type: "server.connected", data: {} })}\n\n` + `data: ${JSON.stringify(modelSwitchedEvent)}\n\n`, { headers: { "content-type": "text/event-stream" } }, ), @@ -35,10 +45,10 @@ test("event.subscribe exposes and decodes the native Effect event stream", async return yield* client.event.subscribe().pipe(Stream.runCollect) }).pipe(Effect.provideService(HttpClient.HttpClient, httpClient), Effect.runPromise) - expect(Array.from(events).map((event) => event.type)).toEqual(["server.connected", "session.next.model.switched"]) + expect(Array.from(events).map((event) => event.type)).toEqual(["server.connected", "model.selected"]) const durable = events[1] - if (durable?.type !== "session.next.model.switched") throw new Error("Expected model event") - expect(DateTime.toEpochMillis(durable.data.timestamp)).toBe(1_717_171_717_000) + if (durable?.type !== "model.selected") throw new Error("Expected model event") + expect(DateTime.toEpochMillis(durable.created)).toBe(1_717_171_717_000) expect(durable.durable).toEqual({ aggregateID: "ses_test", seq: 1, version: 1 }) }) @@ -149,8 +159,8 @@ test("session methods retain decoded Effect inputs and outputs", async () => { expect(result.context).toEqual([]) expect(logQueries[0]).toEqual({ after: "0" }) const logged = Array.from(result.log) - expect(logged.map((item) => item.type)).toEqual(["session.next.model.switched", "log.synced"]) - expect(logged[0]?.type === "session.next.model.switched" && DateTime.toEpochMillis(logged[0].data.timestamp)).toBe( + expect(logged.map((item) => item.type)).toEqual(["model.selected", "log.synced"]) + expect(logged[0]?.type === "model.selected" && DateTime.toEpochMillis(logged[0].created)).toBe( 1_717_171_717_000, ) expect(logged.at(-1)).toEqual(synced) @@ -217,12 +227,11 @@ const modelSwitchedMessage = { const modelSwitchedEvent = { id: "evt_model", - type: "session.next.model.switched", + created: 1_717_171_717_000, + type: "model.selected", durable: { aggregateID: "ses_test", seq: 1, version: 1 }, data: { - timestamp: 1_717_171_717_000, sessionID: "ses_test", - messageID: "msg_model", model: { id: "claude", providerID: "anthropic" }, }, } diff --git a/packages/client/test/promise.test.ts b/packages/client/test/promise.test.ts index 030b761fa6..9e8ea7acfe 100644 --- a/packages/client/test/promise.test.ts +++ b/packages/client/test/promise.test.ts @@ -151,7 +151,7 @@ test("event.subscribe exposes the Promise event stream wire projection", async ( baseUrl: "http://localhost:3000", fetch: async () => new Response( - `: heartbeat\n\ndata: ${JSON.stringify({ id: "evt_connected", type: "server.connected", data: {} })}\n\n` + + `: heartbeat\n\ndata: ${JSON.stringify({ id: "evt_connected", created: 0, type: "server.connected", data: {} })}\n\n` + `data: ${JSON.stringify(modelSwitchedEvent)}\n\n`, { headers: { "content-type": "text/event-stream" } }, ), @@ -159,8 +159,8 @@ test("event.subscribe exposes the Promise event stream wire projection", async ( const events = [] for await (const event of client.event.subscribe()) events.push(event) - expect(events).toEqual([{ id: "evt_connected", type: "server.connected", data: {} }, modelSwitchedEvent]) - expect(events[1]?.type === "session.next.model.switched" && events[1].data.timestamp).toBe(1_717_171_717_000) + expect(events).toEqual([{ id: "evt_connected", created: 0, type: "server.connected", data: {} }, modelSwitchedEvent]) + expect(events[1]?.type === "model.selected" && events[1].created).toBe(1_717_171_717_000) }) test("event.subscribe terminates on malformed Promise SSE data", async () => { @@ -328,12 +328,11 @@ const synced = { type: "log.synced", aggregateID: "ses_test", seq: 1 } const modelSwitchedEvent = { id: "evt_model", - type: "session.next.model.switched", + created: 1_717_171_717_000, + type: "model.selected", durable: { aggregateID: "ses_test", seq: 1, version: 1 }, data: { - timestamp: 1_717_171_717_000, sessionID: "ses_test", - messageID: "msg_model", model: { id: "claude", providerID: "anthropic" }, }, } diff --git a/packages/core/schema.json b/packages/core/schema.json index e8a2502a34..d9e0b20b22 100644 --- a/packages/core/schema.json +++ b/packages/core/schema.json @@ -1,9 +1,9 @@ { "version": "7", "dialect": "sqlite", - "id": "22e57fed-b9b8-4e94-a3b4-f94bece680a8", + "id": "96e9fe64-d810-4102-8f79-3317a88bb6d2", "prevIds": [ - "f14a9b18-8207-487e-a3d3-227e629ba9ad" + "22e57fed-b9b8-4e94-a3b4-f94bece680a8" ], "ddl": [ { @@ -526,6 +526,16 @@ "entityType": "columns", "table": "event" }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created", + "entityType": "columns", + "table": "event" + }, { "type": "text", "notNull": true, diff --git a/packages/core/src/control-plane/move-session.ts b/packages/core/src/control-plane/move-session.ts index e227f88df5..6f0c6e3081 100644 --- a/packages/core/src/control-plane/move-session.ts +++ b/packages/core/src/control-plane/move-session.ts @@ -106,8 +106,7 @@ const layer = Layer.effect( yield* events.publish(SessionEvent.Moved, { sessionID: input.sessionID, location: Location.Ref.make({ directory }), - subdirectory: RelativePath.make(path.relative(destination.directory, directory).replaceAll("\\", "/")), - timestamp: yield* DateTime.now, + subpath: RelativePath.make(path.relative(destination.directory, directory).replaceAll("\\", "/")), }) if (patch) { diff --git a/packages/core/src/database/migration.gen.ts b/packages/core/src/database/migration.gen.ts index 55c1b212cd..7956b64f4d 100644 --- a/packages/core/src/database/migration.gen.ts +++ b/packages/core/src/database/migration.gen.ts @@ -41,5 +41,7 @@ export const migrations = ( import("./migration/20260622170816_reset_v2_session_state"), import("./migration/20260622202450_simplify_session_input"), import("./migration/20260702134641_add_session_context_entry"), + import("./migration/20260703090000_reset_v2_event_rename_sweep"), + import("./migration/20260703181610_event_created_column"), ]) ).map((module) => module.default) satisfies DatabaseMigration.Migration[] diff --git a/packages/core/src/database/migration/20260703090000_reset_v2_event_rename_sweep.ts b/packages/core/src/database/migration/20260703090000_reset_v2_event_rename_sweep.ts new file mode 100644 index 0000000000..20672f0734 --- /dev/null +++ b/packages/core/src/database/migration/20260703090000_reset_v2_event_rename_sweep.ts @@ -0,0 +1,17 @@ +import { Effect } from "effect" +import type { DatabaseMigration } from "../migration" + +export default { + id: "20260703090000_reset_v2_event_rename_sweep", + up(tx) { + return Effect.gen(function* () { + yield* tx.run(`DELETE FROM \`session_input\`;`) + yield* tx.run(`DELETE FROM \`session_message\`;`) + yield* tx.run(`DELETE FROM \`event\`;`) + yield* tx.run(`DELETE FROM \`event_sequence\`;`) + // `created` column is added by the generated 20260703181610_event_created_column + // migration, which runs after this wipe (NOT NULL without default is safe on the + // emptied table). + }) + }, +} satisfies DatabaseMigration.Migration diff --git a/packages/core/src/database/migration/20260703181610_event_created_column.ts b/packages/core/src/database/migration/20260703181610_event_created_column.ts new file mode 100644 index 0000000000..29d2cf9c12 --- /dev/null +++ b/packages/core/src/database/migration/20260703181610_event_created_column.ts @@ -0,0 +1,11 @@ +import { Effect } from "effect" +import type { DatabaseMigration } from "../migration" + +export default { + id: "20260703181610_event_created_column", + up(tx) { + return Effect.gen(function* () { + yield* tx.run(`ALTER TABLE \`event\` ADD \`created\` integer NOT NULL;`) + }) + }, +} satisfies DatabaseMigration.Migration diff --git a/packages/core/src/database/schema.gen.ts b/packages/core/src/database/schema.gen.ts index d8f0f1c665..17a7d12aed 100644 --- a/packages/core/src/database/schema.gen.ts +++ b/packages/core/src/database/schema.gen.ts @@ -81,6 +81,7 @@ export default { \`id\` text PRIMARY KEY, \`aggregate_id\` text NOT NULL, \`seq\` integer NOT NULL, + \`created\` integer NOT NULL, \`type\` text NOT NULL, \`data\` text NOT NULL, CONSTRAINT \`fk_event_aggregate_id_event_sequence_aggregate_id_fk\` FOREIGN KEY (\`aggregate_id\`) REFERENCES \`event_sequence\`(\`aggregate_id\`) ON DELETE CASCADE diff --git a/packages/core/src/event.ts b/packages/core/src/event.ts index dd0aa43181..587688d814 100644 --- a/packages/core/src/event.ts +++ b/packages/core/src/event.ts @@ -1,6 +1,6 @@ export * as EventV2 from "./event" -import { Cause, Context, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect" +import { Cause, Context, DateTime, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect" import { Event } from "@opencode-ai/schema/event" import type { Data, Definition, Payload } from "@opencode-ai/schema/event" import type { EventLog } from "@opencode-ai/schema/event-log" @@ -55,6 +55,7 @@ export const reserveSequence = Effect.fn("EventV2.reserveSequence")(function* ( export type SerializedEvent = { readonly id: ID readonly type: string + readonly created?: DateTime.Utc readonly seq: number readonly aggregateID: string readonly data: Record @@ -81,6 +82,7 @@ const decodeSerializedEvent = (event: SerializedEvent): Payload => { } return { id: event.id, + created: event.created ?? DateTime.makeUnsafe(0), type: definition.type, durable: envelope(event.aggregateID, event.seq, definition.durable.version), data: Schema.decodeUnknownSync(definition.data)(event.data), @@ -295,6 +297,7 @@ export const layerWith = (options?: LayerOptions) => if ( stored?.id === event.id && stored.type === versionedType(definition.type, durable.version) && + stored.created === DateTime.toEpochMillis(event.created ?? DateTime.makeUnsafe(0)) && isDeepStrictEqual(stored.data, encoded) ) { if (input.ownerID && row?.ownerID == null) { @@ -366,6 +369,7 @@ export const layerWith = (options?: LayerOptions) => id: event.id, aggregate_id: aggregateID, seq, + created: DateTime.toEpochMillis(event.created ?? DateTime.makeUnsafe(0)), type: versionedType(definition.type, durable.version), data: encoded, }, @@ -471,6 +475,7 @@ export const layerWith = (options?: LayerOptions) => definition, { id: options?.id ?? ID.create(), + created: yield* DateTime.now, ...(options?.metadata ? { metadata: options.metadata } : {}), type: definition.type, ...(location ? { location } : {}), @@ -494,6 +499,7 @@ export const layerWith = (options?: LayerOptions) => } else { const payload = { id: event.id, + created: event.created ?? DateTime.makeUnsafe(0), type: definition.type, data: Schema.decodeUnknownSync(definition.data)(event.data), } as Payload @@ -610,6 +616,7 @@ export const layerWith = (options?: LayerOptions) => return [ decodeSerializedEvent({ id: event.id, + created: DateTime.makeUnsafe(event.created), aggregateID: event.aggregate_id, seq: event.seq, type: event.type, diff --git a/packages/core/src/event/sql.ts b/packages/core/src/event/sql.ts index 38fe34f1e3..17c88cefd3 100644 --- a/packages/core/src/event/sql.ts +++ b/packages/core/src/event/sql.ts @@ -15,6 +15,7 @@ export const EventTable = sqliteTable( .notNull() .references(() => EventSequenceTable.aggregate_id, { onDelete: "cascade" }), seq: integer().notNull(), + created: integer().notNull(), type: text().notNull(), data: text({ mode: "json" }).$type>().notNull(), }, diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index acae2691c8..108d36707a 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -363,8 +363,7 @@ const layer = Layer.effect( yield* events.publish(SessionEvent.Forked, { sessionID, parentID: parent.id, - messageID: input.messageID, - timestamp: yield* DateTime.now, + from: input.messageID, }) return yield* result.get(sessionID).pipe(Effect.orDie) }), @@ -551,16 +550,13 @@ const layer = Layer.effect( Effect.gen(function* () { activeShells.add(input.sessionID) if ((yield* execution.active).has(input.sessionID)) yield* execution.awaitIdle(input.sessionID) - const messageID = SessionMessage.ID.create() const callID = Identifier.ascending() yield* events.publish( SessionEvent.Shell.Started, { sessionID: input.sessionID, - messageID, callID, command: input.command, - timestamp: yield* DateTime.now, }, { id: input.id }, ) @@ -571,7 +567,6 @@ const layer = Layer.effect( sessionID: input.sessionID, callID, output, - timestamp: yield* DateTime.now, }) }).pipe( Effect.ensuring( @@ -590,8 +585,6 @@ const layer = Layer.effect( if (!skill) return yield* new SkillNotFoundError({ skill: input.skill }) yield* events.publish(SessionEvent.Skill.Activated, { sessionID: input.sessionID, - messageID: input.id ?? SessionMessage.ID.create(), - timestamp: yield* DateTime.now, name: skill.name, text: skill.content, }) @@ -602,10 +595,8 @@ const layer = Layer.effect( }), switchAgent: Effect.fn("V2Session.switchAgent")(function* (input) { yield* result.get(input.sessionID) - yield* events.publish(SessionEvent.AgentSwitched, { + yield* events.publish(SessionEvent.AgentSelected, { sessionID: input.sessionID, - messageID: SessionMessage.ID.create(), - timestamp: yield* DateTime.now, agent: input.agent, }) }), @@ -617,10 +608,8 @@ const layer = Layer.effect( (session.model.variant ?? "default") === (input.model.variant ?? "default") ) return - yield* events.publish(SessionEvent.ModelSwitched, { + yield* events.publish(SessionEvent.ModelSelected, { sessionID: input.sessionID, - messageID: SessionMessage.ID.create(), - timestamp: yield* DateTime.now, model: input.model, }) }), @@ -628,7 +617,6 @@ const layer = Layer.effect( yield* result.get(input.sessionID) yield* events.publish(SessionEvent.Renamed, { sessionID: input.sessionID, - timestamp: yield* DateTime.now, title: input.title, }) }), @@ -676,8 +664,6 @@ const layer = Layer.effect( yield* result.get(input.sessionID) yield* events.publish(SessionEvent.Synthetic, { sessionID: input.sessionID, - messageID: SessionMessage.ID.create(), - timestamp: yield* DateTime.now, text: input.text, description: input.description, metadata: input.metadata, diff --git a/packages/core/src/session/compaction.ts b/packages/core/src/session/compaction.ts index fdb81c5045..1174b65f40 100644 --- a/packages/core/src/session/compaction.ts +++ b/packages/core/src/session/compaction.ts @@ -7,7 +7,7 @@ import { EventV2 } from "../event" import { makeLocationNode } from "../effect/app-node" import { llmClient } from "../effect/app-node-platform" import { SessionEvent } from "./event" -import { SessionMessage } from "./message" +import type { SessionMessage } from "./message" import { SessionRunnerModel } from "./runner/model" import { SessionSchema } from "./schema" import { Token } from "../util/token" @@ -206,11 +206,8 @@ const make = (dependencies: Dependencies) => { const summaryPrompt = buildPrompt({ previousSummary: input.previousSummary, context: input.context }) const summaryOutput = Math.min(output || SUMMARY_OUTPUT_TOKENS, SUMMARY_OUTPUT_TOKENS) if (Token.estimate(summaryPrompt) > context - summaryOutput) return false - const messageID = SessionMessage.ID.create() yield* dependencies.events.publish(SessionEvent.Compaction.Started, { sessionID: input.sessionID, - messageID, - timestamp: yield* DateTime.now, reason: input.reason, }) @@ -238,8 +235,6 @@ const make = (dependencies: Dependencies) => { if (!summarized || failed || !summary.trim()) return false yield* dependencies.events.publish(SessionEvent.Compaction.Ended, { sessionID: input.sessionID, - messageID, - timestamp: yield* DateTime.now, reason: input.reason, text: summary, recent: input.recent, diff --git a/packages/core/src/session/context-checkpoint.ts b/packages/core/src/session/context-checkpoint.ts index 8ea1f46513..fa3b24aedb 100644 --- a/packages/core/src/session/context-checkpoint.ts +++ b/packages/core/src/session/context-checkpoint.ts @@ -1,13 +1,12 @@ export * as SessionContextCheckpoint from "./context-checkpoint" import { eq } from "drizzle-orm" -import { DateTime, Effect, Option, Schema } from "effect" +import { Effect, Option, Schema } from "effect" import type { Database } from "../database/database" import { EventV2 } from "../event" import { SystemContext } from "../system-context/index" import { SessionEvent } from "./event" import { SessionHistory } from "./history" -import { SessionMessage } from "./message" import { SessionSchema } from "./schema" import { SessionContextCheckpointTable } from "./sql" @@ -50,7 +49,7 @@ export const prepare = Effect.fn("SessionContextCheckpoint.prepare")(function* ( yield* events.publish( SessionEvent.ContextUpdated, - { sessionID, messageID: SessionMessage.ID.create(), timestamp: yield* DateTime.now, text: result.text }, + { sessionID, text: result.text }, { commit: () => advance(db, sessionID, result.applied).pipe(Effect.orDie) }, ) return { baseline: stored.baseline, baselineSeq: stored.baseline_seq } diff --git a/packages/core/src/session/execution/local.ts b/packages/core/src/session/execution/local.ts index 60e32b7c2c..1a45537490 100644 --- a/packages/core/src/session/execution/local.ts +++ b/packages/core/src/session/execution/local.ts @@ -36,7 +36,6 @@ const layer = Layer.effect( Exit.isFailure(exit) && !Cause.hasInterrupts(exit.cause) ? Cause.squash(exit.cause) : undefined yield* events.publish(SessionEvent.ExecutionSettled, { sessionID, - timestamp: yield* DateTime.now, outcome: Exit.isSuccess(exit) ? "success" : Cause.hasInterrupts(exit.cause) ? "interrupted" : "failure", error: failure !== undefined diff --git a/packages/core/src/session/input.ts b/packages/core/src/session/input.ts index 8d89563543..1d0dfc71c3 100644 --- a/packages/core/src/session/input.ts +++ b/packages/core/src/session/input.ts @@ -50,12 +50,10 @@ export const admit = Effect.fn("SessionInput.admit")(function* ( ) { const existing = yield* find(db, input.id) if (existing !== undefined) return existing - const timestamp = yield* DateTime.now return yield* events .publish(SessionEvent.PromptAdmitted, { - messageID: input.id, + inputID: input.id, sessionID: input.sessionID, - timestamp, prompt: input.prompt, delivery: input.delivery, }) @@ -70,7 +68,7 @@ export const admit = Effect.fn("SessionInput.admit")(function* ( sessionID: input.sessionID, prompt: input.prompt, delivery: input.delivery, - timeCreated: timestamp, + timeCreated: event.created, }), ), ), @@ -115,14 +113,11 @@ export const projectAdmitted = Effect.fn("SessionInput.projectAdmitted")(functio if (!stored) return yield* Effect.die(new LifecycleConflict({ id: input.id })) }) -export const projectPrompted = Effect.fn("SessionInput.projectPrompted")(function* ( +export const projectPromptPromoted = Effect.fn("SessionInput.projectPromptPromoted")(function* ( db: DatabaseService, input: { readonly id: SessionMessage.ID readonly sessionID: SessionSchema.ID - readonly prompt: Prompt - readonly delivery: Delivery - readonly timeCreated: DateTime.Utc readonly promotedSeq: number }, ) { @@ -141,15 +136,16 @@ export const projectPrompted = Effect.fn("SessionInput.projectPrompted")(functio .pipe(Effect.orDie) if (updated) { const stored = fromRow(updated) - if (!matchesProjection(stored, input)) return yield* Effect.die(new LifecycleConflict({ id: input.id })) - return + if (stored.sessionID !== input.sessionID) return yield* Effect.die(new LifecycleConflict({ id: input.id })) + return stored } - // Every Prompted event is published from an admitted inbox row, so a missing or + // Every PromptPromoted event is published from an admitted inbox row, so a missing or // divergent row on replay is an invariant violation. const stored = yield* find(db, input.id) - if (!stored || !matchesProjection(stored, input) || stored.promotedSeq !== input.promotedSeq) + if (!stored || stored.sessionID !== input.sessionID || stored.promotedSeq !== input.promotedSeq) return yield* Effect.die(new LifecycleConflict({ id: input.id })) + return stored }) export const hasPending = Effect.fn("SessionInput.hasPending")(function* ( @@ -206,12 +202,9 @@ const publish = Effect.fn("SessionInput.publish")(function* ( for (const row of rows) { const id = SessionMessage.ID.make(row.id) yield* events - .publish(SessionEvent.Prompted, { + .publish(SessionEvent.PromptPromoted, { sessionID, - timestamp: DateTime.makeUnsafe(row.time_created), - messageID: id, - prompt: decodePrompt(row.prompt), - delivery: row.delivery, + inputID: id, }) .pipe( Effect.catchDefect((defect) => diff --git a/packages/core/src/session/instructions.ts b/packages/core/src/session/instructions.ts index 913f5c7334..a75074ae04 100644 --- a/packages/core/src/session/instructions.ts +++ b/packages/core/src/session/instructions.ts @@ -60,9 +60,9 @@ const layer = Layer.effect( const files = yield* Effect.forEach( toInject, (path) => - fs.readFileStringSafe(path).pipe( - Effect.map((content) => (content === undefined ? undefined : { path, content })), - ), + fs + .readFileStringSafe(path) + .pipe(Effect.map((content) => (content === undefined ? undefined : { path, content }))), { concurrency: "unbounded" }, ) const readable = files.filter((file): file is { path: string; content: string } => file !== undefined) @@ -74,8 +74,6 @@ const layer = Layer.effect( // metadata so it survives across Location layer restarts. yield* events.publish(SessionEvent.Synthetic, { sessionID: input.sessionID, - messageID: SessionMessage.ID.create(), - timestamp: yield* DateTime.now, text: readable.map((file) => `Instructions from: ${file.path}\n${file.content}`).join("\n\n"), description: `Loaded ${readable.map((file) => describePath(root, file.path)).join(", ")}`, metadata: { instruction: { paths: readable.map((file) => file.path) } }, diff --git a/packages/core/src/session/message-updater.ts b/packages/core/src/session/message-updater.ts index 9ee8f5f739..b07d0d1234 100644 --- a/packages/core/src/session/message-updater.ts +++ b/packages/core/src/session/message-updater.ts @@ -100,112 +100,100 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { return Effect.gen(function* () { yield* SessionEvent.All.match(event, { - "session.next.agent.switched": (event) => { + "agent.selected": (event) => { return adapter.appendMessage( - SessionMessage.AgentSwitched.make({ - id: event.data.messageID, + SessionMessage.AgentSelected.make({ + id: SessionMessage.ID.fromEvent(event.id), type: "agent-switched", metadata: event.metadata, agent: event.data.agent, - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ) }, - "session.next.model.switched": (event) => { + "model.selected": (event) => { return adapter.appendMessage( - SessionMessage.ModelSwitched.make({ - id: event.data.messageID, + SessionMessage.ModelSelected.make({ + id: SessionMessage.ID.fromEvent(event.id), type: "model-switched", metadata: event.metadata, model: event.data.model, - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ) }, - "session.next.moved": () => Effect.void, - "session.next.renamed": () => Effect.void, - "session.next.forked": () => Effect.void, - "session.next.prompted": (event) => { - return adapter.appendMessage( - SessionMessage.User.make({ - id: event.data.messageID, - type: "user", - metadata: event.metadata, - text: event.data.prompt.text, - files: event.data.prompt.files, - agents: event.data.prompt.agents, - time: { created: event.data.timestamp }, - }), - ) - }, - "session.next.prompt.admitted": () => Effect.void, - "session.next.execution.settled": () => Effect.void, - "session.next.context.updated": (event) => + "session.moved": () => Effect.void, + renamed: () => Effect.void, + forked: () => Effect.void, + "prompt.promoted": () => Effect.void, + "prompt.admitted": () => Effect.void, + "execution.settled": () => Effect.void, + "session.context.updated": (event) => adapter.appendMessage( SessionMessage.System.make({ - id: event.data.messageID, + id: SessionMessage.ID.fromEvent(event.id), type: "system", text: event.data.text, - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ), - "session.next.synthetic": (event) => { + synthetic: (event) => { return adapter.appendMessage( SessionMessage.Synthetic.make({ sessionID: event.data.sessionID, text: event.data.text, description: event.data.description, metadata: event.data.metadata, - id: event.data.messageID, + id: SessionMessage.ID.fromEvent(event.id), type: "synthetic", - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ) }, - "session.next.skill.activated": (event) => { + "skill.activated": (event) => { return adapter.appendMessage( SessionMessage.Skill.make({ - id: event.data.messageID, + id: SessionMessage.ID.fromEvent(event.id), type: "skill", name: event.data.name, text: event.data.text, - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ) }, - "session.next.shell.started": (event) => { + "shell.started": (event) => { return adapter.appendMessage( SessionMessage.Shell.make({ - id: event.data.messageID, + id: SessionMessage.ID.fromEvent(event.id), type: "shell", metadata: event.metadata, callID: event.data.callID, command: event.data.command, output: "", - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ) }, - "session.next.shell.ended": (event) => { + "shell.ended": (event) => { return Effect.gen(function* () { const currentShell = yield* adapter.getCurrentShell(event.data.callID) if (currentShell) { yield* adapter.updateShell( produce(currentShell, (draft) => { draft.output = event.data.output - draft.time.completed = event.data.timestamp + draft.time.completed = event.created }), ) } }) }, - "session.next.step.started": (event) => { + "step.started": (event) => { return Effect.gen(function* () { const currentAssistant = yield* adapter.getCurrentAssistant() if (currentAssistant) { yield* adapter.updateAssistant( produce(currentAssistant, (draft) => { - draft.time.completed = event.data.timestamp + draft.time.completed = event.created }), ) } @@ -215,16 +203,16 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { type: "assistant", agent: event.data.agent, model: event.data.model, - time: { created: event.data.timestamp }, + time: { created: event.created }, content: [], snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined, }), ) }) }, - "session.next.step.ended": (event) => { + "step.ended": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { - draft.time.completed = event.data.timestamp + draft.time.completed = event.created draft.finish = event.data.finish draft.cost = event.data.cost draft.tokens = event.data.tokens @@ -236,33 +224,33 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { } }) }, - "session.next.step.failed": (event) => { + "step.failed": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { - draft.time.completed = event.data.timestamp + draft.time.completed = event.created draft.finish = "error" draft.error = event.data.error }) }, - "session.next.text.started": (event) => { + "text.started": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { draft.content.push( castDraft(SessionMessage.AssistantText.make({ type: "text", id: event.data.textID, text: "" })), ) }) }, - "session.next.text.delta": (event) => { + "text.delta": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestText(draft, event.data.textID) if (match) match.text += event.data.delta }) }, - "session.next.text.ended": (event) => { + "text.ended": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestText(draft, event.data.textID) if (match) match.text = event.data.text }) }, - "session.next.tool.input.started": (event) => { + "tool.input.started": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { draft.content.push( castDraft( @@ -270,26 +258,26 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { type: "tool", id: event.data.callID, name: event.data.name, - time: { created: event.data.timestamp }, + time: { created: event.created }, state: SessionMessage.ToolStatePending.make({ status: "pending", input: "" }), }), ), ) }) }, - "session.next.tool.input.delta": () => Effect.void, - "session.next.tool.input.ended": (event) => { + "tool.input.delta": () => Effect.void, + "tool.input.ended": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestTool(draft, event.data.callID) if (match && match.state.status === "pending") match.state.input = event.data.text }) }, - "session.next.tool.called": (event) => { + "tool.called": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestTool(draft, event.data.callID) if (match) { match.provider = event.data.provider - match.time.ran = event.data.timestamp + match.time.ran = event.created match.state = castDraft( SessionMessage.ToolStateRunning.make({ status: "running", @@ -301,7 +289,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { } }) }, - "session.next.tool.progress": (event) => { + "tool.progress": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestTool(draft, event.data.callID) if (match && match.state.status === "running") { @@ -310,7 +298,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { } }) }, - "session.next.tool.success": (event) => { + "tool.success": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestTool(draft, event.data.callID) if (match && match.state.status === "running") { @@ -319,7 +307,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { metadata: match.provider?.metadata, resultMetadata: event.data.provider.metadata, } - match.time.completed = event.data.timestamp + match.time.completed = event.created match.state = castDraft( SessionMessage.ToolStateCompleted.make({ status: "completed", @@ -333,7 +321,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { } }) }, - "session.next.tool.failed": (event) => { + "tool.failed": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestTool(draft, event.data.callID) if (match && (match.state.status === "pending" || match.state.status === "running")) { @@ -342,7 +330,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { metadata: match.provider?.metadata, resultMetadata: event.data.provider.metadata, } - match.time.completed = event.data.timestamp + match.time.completed = event.created match.state = castDraft( SessionMessage.ToolStateError.make({ status: "error", @@ -356,7 +344,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { } }) }, - "session.next.reasoning.started": (event) => { + "reasoning.started": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { draft.content.push( castDraft( @@ -365,47 +353,47 @@ export function update(adapter: Adapter, event: SessionEvent.Event) { id: event.data.reasoningID, text: "", providerMetadata: event.data.providerMetadata, - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ), ) }) }, - "session.next.reasoning.delta": (event) => { + "reasoning.delta": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestReasoning(draft, event.data.reasoningID) if (match) match.text += event.data.delta }) }, - "session.next.reasoning.ended": (event) => { + "reasoning.ended": (event) => { return updateOwnedAssistant(event.data.assistantMessageID, (draft) => { const match = latestReasoning(draft, event.data.reasoningID) if (match) { match.text = event.data.text - match.time = { created: match.time?.created ?? event.data.timestamp, completed: event.data.timestamp } + match.time = { created: match.time?.created ?? event.created, completed: event.created } if (event.data.providerMetadata !== undefined) match.providerMetadata = event.data.providerMetadata } }) }, - "session.next.retried": () => Effect.void, - "session.next.compaction.started": () => Effect.void, - "session.next.compaction.delta": () => Effect.void, - "session.next.compaction.ended": (event) => { + retried: () => Effect.void, + "compaction.started": () => Effect.void, + "compaction.delta": () => Effect.void, + "compaction.ended": (event) => { return adapter.appendMessage( SessionMessage.Compaction.make({ - id: event.data.messageID, + id: SessionMessage.ID.fromEvent(event.id), type: "compaction", metadata: event.metadata, reason: event.data.reason, summary: event.data.text, recent: event.data.recent, - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ) }, - "session.next.revert.staged": () => Effect.void, - "session.next.revert.cleared": () => Effect.void, - "session.next.revert.committed": () => Effect.void, + "revert.staged": () => Effect.void, + "revert.cleared": () => Effect.void, + "revert.committed": () => Effect.void, }) }) } diff --git a/packages/core/src/session/projector.ts b/packages/core/src/session/projector.ts index 7b08733684..62ad7ec2ce 100644 --- a/packages/core/src/session/projector.ts +++ b/packages/core/src/session/projector.ts @@ -158,21 +158,17 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* ( .get() .pipe(Effect.orDie) if (!parent) return yield* Effect.die(new Error(`Fork parent session not found: ${event.data.parentID}`)) - const boundary = event.data.messageID + const boundary = event.data.from ? yield* db .select({ seq: SessionMessageTable.seq }) .from(SessionMessageTable) .where( - and( - eq(SessionMessageTable.session_id, event.data.parentID), - eq(SessionMessageTable.id, event.data.messageID), - ), + and(eq(SessionMessageTable.session_id, event.data.parentID), eq(SessionMessageTable.id, event.data.from)), ) .get() .pipe(Effect.orDie) : undefined - if (event.data.messageID && !boundary) - return yield* Effect.die(new Error(`Fork boundary message not found: ${event.data.messageID}`)) + if (event.data.from && !boundary) return yield* Effect.die(new Error(`Fork boundary message not found: ${event.data.from}`)) const copied = yield* db .select({ seq: SessionMessageTable.seq }) .from(SessionMessageTable) @@ -208,8 +204,8 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* ( tokens_reasoning: 0, tokens_cache_read: 0, tokens_cache_write: 0, - time_created: DateTime.toEpochMillis(event.data.timestamp), - time_updated: DateTime.toEpochMillis(event.data.timestamp), + time_created: DateTime.toEpochMillis(event.created), + time_updated: DateTime.toEpochMillis(event.created), }) .onConflictDoNothing() .returning({ sessionID: SessionTable.id }) @@ -474,9 +470,9 @@ const layer = Layer.effectDiscard( .update(SessionTable) .set({ directory: event.data.location.directory, - path: event.data.subdirectory, + path: event.data.subpath, workspace_id: event.data.location.workspaceID ? WorkspaceV2.ID.make(event.data.location.workspaceID) : null, - time_updated: DateTime.toEpochMillis(event.data.timestamp), + time_updated: DateTime.toEpochMillis(event.created), }) .where(eq(SessionTable.id, event.data.sessionID)) .run() @@ -556,19 +552,19 @@ const layer = Layer.effectDiscard( if (next) yield* applyUsage(db, sessionID, next) }), ) - yield* events.project(SessionEvent.AgentSwitched, (event) => + yield* events.project(SessionEvent.AgentSelected, (event) => db .update(SessionTable) - .set({ agent: event.data.agent, time_updated: DateTime.toEpochMillis(event.data.timestamp) }) + .set({ agent: event.data.agent, time_updated: DateTime.toEpochMillis(event.created) }) .where(eq(SessionTable.id, event.data.sessionID)) .run() .pipe(Effect.orDie, Effect.andThen(run(db, event))), ) - yield* events.project(SessionEvent.ModelSwitched, (event) => + yield* events.project(SessionEvent.ModelSelected, (event) => Effect.gen(function* () { yield* db .update(SessionTable) - .set({ model: event.data.model, time_updated: DateTime.toEpochMillis(event.data.timestamp) }) + .set({ model: event.data.model, time_updated: DateTime.toEpochMillis(event.created) }) .where(eq(SessionTable.id, event.data.sessionID)) .run() .pipe(Effect.orDie) @@ -578,25 +574,30 @@ const layer = Layer.effectDiscard( yield* events.project(SessionEvent.Renamed, (event) => db .update(SessionTable) - .set({ title: event.data.title, time_updated: DateTime.toEpochMillis(event.data.timestamp) }) + .set({ title: event.data.title, time_updated: DateTime.toEpochMillis(event.created) }) .where(eq(SessionTable.id, event.data.sessionID)) .run() .pipe(Effect.orDie), ) yield* events.project(SessionEvent.Forked, (event) => projectFork(db, event)) - yield* events.project(SessionEvent.Prompted, (event) => + yield* events.project(SessionEvent.PromptPromoted, (event) => Effect.gen(function* () { if (event.durable === undefined) return yield* Effect.die(new Error("Durable Session event is missing aggregate sequence")) - yield* SessionInput.projectPrompted(db, { - id: event.data.messageID, + const input = yield* SessionInput.projectPromptPromoted(db, { + id: event.data.inputID, sessionID: event.data.sessionID, - prompt: event.data.prompt, - delivery: event.data.delivery, - timeCreated: event.data.timestamp, promotedSeq: event.durable.seq, }) - yield* run(db, event) + yield* insertMessage(db, event, { + id: input.id, + type: "user", + metadata: event.metadata, + text: input.prompt.text, + files: input.prompt.files, + agents: input.prompt.agents, + time: { created: event.created }, + }) }), ) yield* events.project(SessionEvent.PromptAdmitted, (event) => @@ -605,11 +606,11 @@ const layer = Layer.effectDiscard( return yield* Effect.die(new Error("Durable Session event is missing aggregate sequence")) yield* SessionInput.projectAdmitted(db, { admittedSeq: event.durable.seq, - id: event.data.messageID, + id: event.data.inputID, sessionID: event.data.sessionID, prompt: event.data.prompt, delivery: event.data.delivery, - timeCreated: event.data.timestamp, + timeCreated: event.created, }) }), ) @@ -617,11 +618,11 @@ const layer = Layer.effectDiscard( yield* events.project(SessionEvent.Synthetic, (event) => run(db, event)) yield* events.project(SessionEvent.Skill.Activated, (event) => insertMessage(db, event, { - id: event.data.messageID, + id: SessionMessage.ID.fromEvent(event.id), type: "skill", name: event.data.name, text: event.data.text, - time: { created: event.data.timestamp }, + time: { created: event.created }, }), ) yield* events.project(SessionEvent.Shell.Started, (event) => run(db, event)) @@ -646,7 +647,7 @@ const layer = Layer.effectDiscard( .update(SessionTable) .set({ revert: { ...event.data.revert, files: event.data.revert.files ? [...event.data.revert.files] : undefined }, - time_updated: DateTime.toEpochMillis(event.data.timestamp), + time_updated: DateTime.toEpochMillis(event.created), }) .where(eq(SessionTable.id, event.data.sessionID)) .run() @@ -655,7 +656,7 @@ const layer = Layer.effectDiscard( yield* events.project(SessionEvent.RevertEvent.Cleared, (event) => db .update(SessionTable) - .set({ revert: null, time_updated: DateTime.toEpochMillis(event.data.timestamp) }) + .set({ revert: null, time_updated: DateTime.toEpochMillis(event.created) }) .where(eq(SessionTable.id, event.data.sessionID)) .run() .pipe(Effect.orDie, Effect.asVoid), @@ -693,7 +694,7 @@ const layer = Layer.effectDiscard( .pipe(Effect.orDie) yield* db .update(SessionTable) - .set({ revert: null, time_updated: DateTime.toEpochMillis(event.data.timestamp) }) + .set({ revert: null, time_updated: DateTime.toEpochMillis(event.created) }) .where(eq(SessionTable.id, event.data.sessionID)) .run() .pipe(Effect.orDie) diff --git a/packages/core/src/session/revert.ts b/packages/core/src/session/revert.ts index 9999d5da5a..8c0b8bbf7b 100644 --- a/packages/core/src/session/revert.ts +++ b/packages/core/src/session/revert.ts @@ -89,7 +89,6 @@ export const stage = Effect.fn("SessionRevert.stage")(function* (input: { } satisfies SessionSchema.Info["revert"] yield* events.publish(SessionEvent.RevertEvent.Staged, { sessionID: input.session.id, - timestamp: yield* DateTime.now, revert, }) return revert @@ -106,7 +105,6 @@ export const clear = Effect.fn("SessionRevert.clear")(function* (session: Sessio const events = yield* EventV2.Service yield* events.publish(SessionEvent.RevertEvent.Cleared, { sessionID: session.id, - timestamp: yield* DateTime.now, }) }) @@ -116,6 +114,5 @@ export const commit = Effect.fn("SessionRevert.commit")(function* (session: Sess yield* events.publish(SessionEvent.RevertEvent.Committed, { sessionID: session.id, messageID: session.revert.messageID, - timestamp: yield* DateTime.now, }) }) diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts index f76ee10a3f..ba575b8146 100644 --- a/packages/core/src/session/runner/llm.ts +++ b/packages/core/src/session/runner/llm.ts @@ -134,7 +134,6 @@ const layer = Layer.effect( if (tool.type !== "tool" || (tool.state.status !== "pending" && tool.state.status !== "running")) continue yield* events.publish(SessionEvent.Tool.Failed, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID: message.id, callID: tool.id, error: { type: "unknown", message: "Tool execution interrupted" }, @@ -297,7 +296,6 @@ const layer = Layer.effect( yield* serialized( events.publish(SessionEvent.Step.Ended, { sessionID: session.id, - timestamp: yield* DateTime.now, assistantMessageID: yield* publisher.startAssistant(), finish: settlement.finish, cost: 0, diff --git a/packages/core/src/session/runner/publish-llm-event.ts b/packages/core/src/session/runner/publish-llm-event.ts index 04c6437ef4..4b7b6fb92c 100644 --- a/packages/core/src/session/runner/publish-llm-event.ts +++ b/packages/core/src/session/runner/publish-llm-event.ts @@ -78,7 +78,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* events.publish(SessionEvent.Step.Started, { ...input, assistantMessageID, - timestamp: yield* timestamp, snapshot: input.snapshot, }) return assistantMessageID @@ -123,7 +122,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* events.publish(SessionEvent.Text.Ended, { sessionID: input.sessionID, assistantMessageID: yield* currentAssistantMessageID(), - timestamp: yield* timestamp, textID, text: value, }) @@ -134,7 +132,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* events.publish(SessionEvent.Reasoning.Ended, { sessionID: input.sessionID, assistantMessageID: yield* currentAssistantMessageID(), - timestamp: yield* timestamp, reasoningID, text: value, providerMetadata, @@ -147,7 +144,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) if (!tool) return yield* Effect.die(new Error(`Tool input end before start: ${callID}`)) yield* events.publish(SessionEvent.Tool.Input.Ended, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID: tool.assistantMessageID, callID, text: value, @@ -176,7 +172,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* toolInput.start(event.id) yield* events.publish(SessionEvent.Tool.Input.Started, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID, callID: event.id, name: event.name, @@ -204,7 +199,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) assistantFailed = true yield* events.publish(SessionEvent.Step.Failed, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID, error: { type: "unknown", message }, }) @@ -219,7 +213,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) tool.settled = true yield* events.publish(SessionEvent.Tool.Failed, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID: tool.assistantMessageID, callID, error: { type: "unknown", message }, @@ -248,7 +241,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* events.publish(SessionEvent.Text.Started, { sessionID: input.sessionID, assistantMessageID: yield* startAssistant(), - timestamp: yield* timestamp, textID: event.id, }) return @@ -257,7 +249,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* events.publish(SessionEvent.Text.Delta, { sessionID: input.sessionID, assistantMessageID: yield* currentAssistantMessageID(), - timestamp: yield* timestamp, textID: event.id, delta: event.text, }) @@ -270,7 +261,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* events.publish(SessionEvent.Reasoning.Started, { sessionID: input.sessionID, assistantMessageID: yield* startAssistant(), - timestamp: yield* timestamp, reasoningID: event.id, providerMetadata: event.providerMetadata, }) @@ -280,7 +270,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* events.publish(SessionEvent.Reasoning.Delta, { sessionID: input.sessionID, assistantMessageID: yield* currentAssistantMessageID(), - timestamp: yield* timestamp, reasoningID: event.id, delta: event.text, }) @@ -300,7 +289,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) yield* toolInput.append(event.id, event.text) yield* events.publish(SessionEvent.Tool.Input.Delta, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID: tool.assistantMessageID, callID: event.id, delta: event.text, @@ -322,7 +310,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) tool.providerMetadata = event.providerMetadata yield* events.publish(SessionEvent.Tool.Called, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID: tool.assistantMessageID, callID: event.id, tool: event.name, @@ -352,7 +339,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) if ("error" in result) { yield* events.publish(SessionEvent.Tool.Failed, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID: tool.assistantMessageID, callID: event.id, error: result.error, @@ -363,7 +349,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) } yield* events.publish(SessionEvent.Tool.Success, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID: tool.assistantMessageID, callID: event.id, ...result, @@ -382,7 +367,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) tool.settled = true yield* events.publish(SessionEvent.Tool.Failed, { sessionID: input.sessionID, - timestamp: yield* timestamp, assistantMessageID: tool.assistantMessageID, callID: event.id, error: { type: "unknown", message: event.message }, diff --git a/packages/core/src/session/title.ts b/packages/core/src/session/title.ts index abed5ce33c..8e7b5857b7 100644 --- a/packages/core/src/session/title.ts +++ b/packages/core/src/session/title.ts @@ -42,9 +42,10 @@ const make = (dependencies: Dependencies) => { if (!firstUser) return const agent = yield* dependencies.agents.get(AgentV2.ID.make("title")) if (!agent) return - const resolved = yield* (agent.model - ? dependencies.models.resolve({ ...session, model: agent.model }) - : dependencies.models.resolve(session) + const resolved = yield* ( + agent.model + ? dependencies.models.resolve({ ...session, model: agent.model }) + : dependencies.models.resolve(session) ).pipe(Effect.catch(() => Effect.succeed(undefined))) if (!resolved) return const chunks: string[] = [] @@ -76,7 +77,6 @@ const make = (dependencies: Dependencies) => { if (!title) return yield* dependencies.events.publish(SessionEvent.Renamed, { sessionID: session.id, - timestamp: yield* DateTime.now, title: truncate(title), }) }) diff --git a/packages/core/test/database-migration.test.ts b/packages/core/test/database-migration.test.ts index b381cc7418..b36de1abac 100644 --- a/packages/core/test/database-migration.test.ts +++ b/packages/core/test/database-migration.test.ts @@ -255,7 +255,7 @@ describe("DatabaseMigration", () => { ) yield* db.run(sql`INSERT INTO event_sequence (aggregate_id, seq) VALUES ('session', 9)`) yield* db.run( - sql`INSERT INTO event (id, aggregate_id, seq, type, data) VALUES ('event', 'session', 9, 'session.updated.1', '{}')`, + sql`INSERT INTO event (id, aggregate_id, seq, type, data, created) VALUES ('event', 'session', 9, 'session.updated.1', '{}', 1)`, ) yield* db.run( sql`INSERT INTO session_input (id, session_id, prompt, delivery, admitted_seq, time_created) VALUES ('input', 'session', '{}', 'steer', 9, 1)`, diff --git a/packages/core/test/event.test.ts b/packages/core/test/event.test.ts index a9220df422..65c370f1eb 100644 --- a/packages/core/test/event.test.ts +++ b/packages/core/test/event.test.ts @@ -554,6 +554,7 @@ describe("EventV2", () => { yield* events.replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -573,6 +574,7 @@ describe("EventV2", () => { yield* events.replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -609,6 +611,7 @@ describe("EventV2", () => { const exit = yield* events .replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID: envelopeAggregateID, @@ -642,6 +645,7 @@ describe("EventV2", () => { yield* events.replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -650,6 +654,7 @@ describe("EventV2", () => { const exit = yield* events .replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 5, aggregateID, @@ -674,13 +679,14 @@ describe("EventV2", () => { yield* events.replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(SessionEvent.ContextUpdated.type, 1), seq: 0, aggregateID, - data: { sessionID: aggregateID, messageID: "msg_context", timestamp: 0, text: "context" }, + data: { sessionID: aggregateID, text: "context" }, }) - expect(received[0]?.data.timestamp).toEqual(DateTime.makeUnsafe(0)) + expect(received[0]?.created).toEqual(DateTime.makeUnsafe(0)) }), ) @@ -690,6 +696,7 @@ describe("EventV2", () => { const exit = yield* events .replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: "unknown.event.1", seq: 0, aggregateID: EventV2.ID.create(), @@ -708,6 +715,7 @@ describe("EventV2", () => { const source = yield* events.replayAll([ { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -715,6 +723,7 @@ describe("EventV2", () => { }, { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID, @@ -735,6 +744,7 @@ describe("EventV2", () => { const one = yield* events.replayAll([ { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -742,6 +752,7 @@ describe("EventV2", () => { }, { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID, @@ -751,6 +762,7 @@ describe("EventV2", () => { const two = yield* events.replayAll([ { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 2, aggregateID, @@ -758,6 +770,7 @@ describe("EventV2", () => { }, { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 3, aggregateID, @@ -793,6 +806,7 @@ describe("EventV2", () => { yield* events.replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID, @@ -812,6 +826,7 @@ describe("EventV2", () => { const id = EventV2.ID.create() const replayed = { id, + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -833,6 +848,7 @@ describe("EventV2", () => { const published = yield* events.publish(DurableMessage, durableData(aggregateID, "owned")) const replayed = { id: published.id, + created: published.created, type: EventV2.versionedType(DurableMessage.type, 1), seq: published.durable!.seq, aggregateID, @@ -867,6 +883,7 @@ describe("EventV2", () => { yield* events.replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -895,6 +912,7 @@ describe("EventV2", () => { yield* events.replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID, @@ -905,6 +923,7 @@ describe("EventV2", () => { yield* events.replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 2, aggregateID, @@ -937,6 +956,7 @@ describe("EventV2", () => { yield* events.replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -949,6 +969,7 @@ describe("EventV2", () => { .replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID, @@ -970,6 +991,7 @@ describe("EventV2", () => { yield* events.listen((event) => Effect.sync(() => received.push(event))) const replayed = { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -990,6 +1012,7 @@ describe("EventV2", () => { const aggregateID = Session.ID.create() const replayed = { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -1014,6 +1037,7 @@ describe("EventV2", () => { const id = EventV2.ID.create() yield* events.replay({ id, + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -1023,6 +1047,7 @@ describe("EventV2", () => { const exit = yield* events .replay({ id, + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID, @@ -1045,6 +1070,7 @@ describe("EventV2", () => { yield* events.replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, @@ -1055,6 +1081,7 @@ describe("EventV2", () => { yield* events.replay( { id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 1, aggregateID, @@ -1116,6 +1143,7 @@ describe("EventV2", () => { yield* events.replay({ id: EventV2.ID.create(), + created: DateTime.makeUnsafe(0), type: EventV2.versionedType(DurableMessage.type, 1), seq: 0, aggregateID, diff --git a/packages/core/test/session-compact.test.ts b/packages/core/test/session-compact.test.ts index cee32ade04..4a9a520d59 100644 --- a/packages/core/test/session-compact.test.ts +++ b/packages/core/test/session-compact.test.ts @@ -85,17 +85,13 @@ describe("SessionV2.compact", () => { const prompt = Prompt.make({ text: "Please compact this session history." }) yield* events.publish(SessionEvent.PromptAdmitted, { sessionID: created.id, - messageID, - timestamp: DateTime.makeUnsafe(0), + inputID: messageID, prompt, delivery: "steer", }) - yield* events.publish(SessionEvent.Prompted, { + yield* events.publish(SessionEvent.PromptPromoted, { sessionID: created.id, - messageID, - timestamp: DateTime.makeUnsafe(0), - prompt, - delivery: "steer", + inputID: messageID, }) yield* session.compact({ sessionID: created.id }) diff --git a/packages/core/test/session-create.test.ts b/packages/core/test/session-create.test.ts index b22c65ec97..0100ca4ae9 100644 --- a/packages/core/test/session-create.test.ts +++ b/packages/core/test/session-create.test.ts @@ -197,8 +197,6 @@ describe("SessionV2.create", () => { yield* SessionInput.promoteSteers(db, events, parent.id) yield* events.publish(SessionEvent.Synthetic, { sessionID: parent.id, - messageID: SessionMessage.ID.create(), - timestamp: yield* DateTime.now, text: "parent note", }) @@ -215,7 +213,7 @@ describe("SessionV2.create", () => { expect(forkContext.map((message) => message.id)).not.toEqual(parentContext.map((message) => message.id)) expect(history).toHaveLength(1) expect(history[0]).toMatchObject({ - type: "session.next.forked", + type: "forked", durable: { seq: 0 }, data: { sessionID: forked.id, parentID: parent.id }, }) @@ -267,7 +265,7 @@ describe("SessionV2.create", () => { const history = Array.from(yield* Stream.runCollect(logEvents(session, forked.id))) expect(context).toMatchObject([{ text: "First" }]) expect(context[0]?.id).not.toBe(first.id) - expect(history[0]).toMatchObject({ data: { messageID: second.id } }) + expect(history[0]).toMatchObject({ data: { from: second.id } }) }), ) @@ -380,8 +378,8 @@ describe("SessionV2.create", () => { expect( Array.from(yield* logEvents(session, created.id, true).pipe(Stream.take(2), Stream.runCollect)), ).toMatchObject([ - { durable: { seq: 1 }, type: "session.next.prompt.admitted", data: { prompt: { text: "Hello" } } }, - { durable: { seq: 2 }, type: "session.next.prompted" }, + { durable: { seq: 1 }, type: "prompt.admitted", data: { prompt: { text: "Hello" } } }, + { durable: { seq: 2 }, type: "prompt.promoted" }, ]) }), ) @@ -406,6 +404,7 @@ describe("SessionV2.create", () => { .all() .pipe(Effect.orDie)).map((event) => ({ id: event.id, + created: DateTime.makeUnsafe(event.created), aggregateID: event.aggregate_id, seq: event.seq, type: event.type, @@ -466,7 +465,7 @@ describe("SessionV2.create", () => { ).toEqual([ [0, EventV2.versionedType(SessionV1.Event.Created.type, 1)], [1, EventV2.versionedType(SessionEvent.PromptAdmitted.type, 1)], - [2, EventV2.versionedType(SessionEvent.Prompted.type, 1)], + [2, EventV2.versionedType(SessionEvent.PromptPromoted.type, 1)], ]) }).pipe(Effect.provide(Layer.fresh(targetLayer))) }), @@ -530,7 +529,7 @@ describe("SessionV2.create", () => { expect(yield* session.get(created.id)).toMatchObject({ agent: "plan" }) expect( Array.from(yield* logEvents(session, created.id, true).pipe(Stream.take(1), Stream.runCollect)), - ).toMatchObject([{ type: "session.next.agent.switched", data: { agent: "plan" } }]) + ).toMatchObject([{ type: "agent.selected", data: { agent: "plan" } }]) }), ) @@ -563,7 +562,7 @@ describe("SessionV2.create", () => { expect(yield* session.get(created.id)).toMatchObject({ model }) expect( Array.from(yield* logEvents(session, created.id, true).pipe(Stream.take(1), Stream.runCollect)), - ).toMatchObject([{ type: "session.next.model.switched", data: { model } }]) + ).toMatchObject([{ type: "model.selected", data: { model } }]) }), ) diff --git a/packages/core/test/session-instructions.test.ts b/packages/core/test/session-instructions.test.ts index e87fce7234..daf0308b6f 100644 --- a/packages/core/test/session-instructions.test.ts +++ b/packages/core/test/session-instructions.test.ts @@ -133,8 +133,6 @@ const seedSynthetic = (sessionID: SessionV2.ID, paths: string[]) => const events = yield* EventV2.Service yield* events.publish(SessionEvent.Synthetic, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: yield* DateTime.now, text: `Instructions from: ${paths[0]}\nprior`, description: `Loaded ${paths[0]}`, metadata: { instruction: { paths } }, diff --git a/packages/core/test/session-log.test.ts b/packages/core/test/session-log.test.ts index 3b9229589c..8c49b94582 100644 --- a/packages/core/test/session-log.test.ts +++ b/packages/core/test/session-log.test.ts @@ -47,7 +47,7 @@ describe("SessionV2.log", () => { // Session creation commits a non-public durable event, so the marker's // seq covers more of the aggregate than the public events emitted. - expect(items.map((item) => item.type)).toEqual(["session.next.renamed", "log.synced"]) + expect(items.map((item) => item.type)).toEqual(["renamed", "log.synced"]) expect(items.at(-1)).toEqual({ type: "log.synced", aggregateID: created.id, seq: watermark }) }), ) @@ -64,7 +64,7 @@ describe("SessionV2.log", () => { yield* session.rename({ sessionID: created.id, title: "renamed live" }) const items = Array.from(yield* Fiber.join(fiber)) - expect(items.map((item) => item.type)).toEqual(["log.synced", "session.next.renamed"]) + expect(items.map((item) => item.type)).toEqual(["log.synced", "renamed"]) }), ) diff --git a/packages/core/test/session-projector.test.ts b/packages/core/test/session-projector.test.ts index 4191a58715..c0d172088e 100644 --- a/packages/core/test/session-projector.test.ts +++ b/packages/core/test/session-projector.test.ts @@ -79,7 +79,6 @@ describe("SessionProjector", () => { const events = yield* EventV2.Service yield* events.publish(SessionEvent.RevertEvent.Staged, { sessionID, - timestamp: DateTime.makeUnsafe(1), revert: { messageID: boundary, snapshot: Snapshot.ID.make("tree"), diff: "patch", files: [] }, }) expect((yield* db.select({ revert: SessionTable.revert }).from(SessionTable).get())?.revert).toMatchObject({ @@ -87,17 +86,15 @@ describe("SessionProjector", () => { snapshot: "tree", files: [], }) - yield* events.publish(SessionEvent.RevertEvent.Cleared, { sessionID, timestamp: DateTime.makeUnsafe(2) }) + yield* events.publish(SessionEvent.RevertEvent.Cleared, { sessionID }) expect((yield* db.select({ revert: SessionTable.revert }).from(SessionTable).get())?.revert).toBeNull() yield* events.publish(SessionEvent.RevertEvent.Staged, { sessionID, - timestamp: DateTime.makeUnsafe(3), revert: { messageID: boundary, files: [] }, }) yield* events.publish(SessionEvent.RevertEvent.Committed, { sessionID, messageID: boundary, - timestamp: DateTime.makeUnsafe(4), }) expect( (yield* db.select({ id: SessionMessageTable.id }).from(SessionMessageTable).all()).map((row) => row.id), @@ -131,37 +128,29 @@ describe("SessionProjector", () => { yield* events.publish(SessionEvent.PromptAdmitted, { sessionID, - messageID: SessionMessage.ID.make("msg_first"), - timestamp: created, + inputID: SessionMessage.ID.make("msg_first"), prompt: Prompt.make({ text: "first" }), delivery: "steer", }) yield* events.publish( - SessionEvent.Prompted, + SessionEvent.PromptPromoted, { sessionID, - messageID: SessionMessage.ID.make("msg_first"), - timestamp: created, - prompt: Prompt.make({ text: "first" }), - delivery: "steer", + inputID: SessionMessage.ID.make("msg_first"), }, { id: EventV2.ID.make("evt_z") }, ) yield* events.publish(SessionEvent.PromptAdmitted, { sessionID, - messageID: SessionMessage.ID.make("msg_second"), - timestamp: created, + inputID: SessionMessage.ID.make("msg_second"), prompt: Prompt.make({ text: "second" }), delivery: "steer", }) yield* events.publish( - SessionEvent.Prompted, + SessionEvent.PromptPromoted, { sessionID, - messageID: SessionMessage.ID.make("msg_second"), - timestamp: created, - prompt: Prompt.make({ text: "second" }), - delivery: "steer", + inputID: SessionMessage.ID.make("msg_second"), }, { id: EventV2.ID.make("evt_a") }, ) @@ -190,7 +179,7 @@ describe("SessionProjector", () => { }).pipe(Effect.provide(sessionsLayer)), ) - it.effect("marks an inbox row promoted with the Prompted event sequence", () => + it.effect("marks an inbox row promoted with the PromptPromoted event sequence", () => Effect.gen(function* () { const { db } = yield* Database.Service yield* db @@ -220,12 +209,9 @@ describe("SessionProjector", () => { }) if (!admitted) return yield* Effect.die("Prompt admission failed") - const event = yield* events.publish(SessionEvent.Prompted, { + const event = yield* events.publish(SessionEvent.PromptPromoted, { sessionID, - timestamp: admitted.timeCreated, - messageID: id, - prompt: Prompt.make({ text: "promote me" }), - delivery: "steer", + inputID: id, }) expect( @@ -256,49 +242,36 @@ describe("SessionProjector", () => { .pipe(Effect.orDie) const events = yield* EventV2.Service - yield* events.publish(SessionEvent.AgentSwitched, { + yield* events.publish(SessionEvent.AgentSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: created, agent: "build", }) - yield* events.publish(SessionEvent.ModelSwitched, { + yield* events.publish(SessionEvent.ModelSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: created, model, }) yield* events.publish(SessionEvent.Synthetic, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: created, text: "synthetic context", metadata: { source: "projector-test" }, }) yield* events.publish(SessionEvent.Shell.Started, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: created, callID: "shell-1", command: "pwd", }) yield* events.publish(SessionEvent.Shell.Ended, { sessionID, - timestamp: DateTime.makeUnsafe(1), callID: "shell-1", output: "/project", }) const compactionID = SessionMessage.ID.create() yield* events.publish(SessionEvent.Compaction.Started, { sessionID, - messageID: compactionID, - timestamp: created, reason: "manual", }) yield* events.publish(SessionEvent.Compaction.Delta, { sessionID, - messageID: compactionID, - timestamp: created, text: "partial", }) expect( @@ -319,8 +292,6 @@ describe("SessionProjector", () => { ).toEqual([]) yield* events.publish(SessionEvent.Compaction.Ended, { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(1), reason: "manual", text: "summary", recent: "recent context", @@ -350,7 +321,7 @@ describe("SessionProjector", () => { }) expect(messages.find((message) => message.type === "shell")).toMatchObject({ output: "/project", - time: { completed: DateTime.makeUnsafe(1) }, + time: { completed: DateTime.makeUnsafe(0) }, }) expect(messages.find((message) => message.type === "compaction")).toMatchObject({ summary: "summary", @@ -388,13 +359,20 @@ describe("SessionProjector", () => { .pipe(Effect.orDie) const events = yield* EventV2.Service const id = SessionMessage.ID.make("msg_creator_collision") + const { + id: _, + type, + ...data + } = encodeMessage({ id, sessionID, type: "synthetic", text: "existing", time: { created } }) + yield* db + .insert(SessionMessageTable) + .values({ id, session_id: sessionID, type, seq: 0, time_created: 0, data }) + .run() - yield* events.publish(SessionEvent.Synthetic, { sessionID, messageID: id, timestamp: created, text: "keep me" }) const exit = yield* events .publish(SessionEvent.Step.Started, { sessionID, assistantMessageID: id, - timestamp: created, agent: "build", model, }) @@ -464,7 +442,6 @@ describe("SessionProjector", () => { const service = yield* EventV2.Service yield* service.publish(SessionEvent.Step.Ended, { sessionID, - timestamp: DateTime.makeUnsafe(1), assistantMessageID: SessionMessage.ID.make("msg_assistant_2"), finish: "stop", cost: 0, @@ -485,7 +462,7 @@ describe("SessionProjector", () => { expect(messages[1]).toMatchObject({ type: "assistant", finish: "stop", - time: { completed: DateTime.makeUnsafe(1) }, + time: { completed: DateTime.makeUnsafe(0) }, }) }), ) @@ -526,7 +503,6 @@ describe("SessionProjector", () => { yield* service.publish(SessionEvent.Text.Started, { sessionID, assistantMessageID: SessionMessage.ID.make("msg_assistant_completed"), - timestamp: DateTime.makeUnsafe(3), textID: "text-stale", }) diff --git a/packages/core/test/session-prompt.test.ts b/packages/core/test/session-prompt.test.ts index 58e26b7b5c..58a613a324 100644 --- a/packages/core/test/session-prompt.test.ts +++ b/packages/core/test/session-prompt.test.ts @@ -201,7 +201,6 @@ describe("SessionV2.prompt", () => { yield* db.insert(SessionMessageTable).values(assistantRow(stale, 100)).run().pipe(Effect.orDie) yield* events.publish(SessionEvent.RevertEvent.Staged, { sessionID, - timestamp: yield* DateTime.now, revert: { messageID: boundary.id, files: [] }, }) expect((yield* session.get(sessionID)).revert?.messageID).toBe(boundary.id) @@ -257,16 +256,16 @@ describe("SessionV2.prompt", () => { const streamed = Array.from(yield* Fiber.join(fiber)) expect(streamed.map((event): [number | undefined, string] => [event.durable?.seq, event.type])).toEqual([ - [0, "session.next.prompt.admitted"], - [1, "session.next.prompt.admitted"], - [2, "session.next.prompted"], - [3, "session.next.prompted"], + [0, "prompt.admitted"], + [1, "prompt.admitted"], + [2, "prompt.promoted"], + [3, "prompt.promoted"], ]) expect( Array.from( yield* publicEvents({ sessionID, after: streamed[0].durable?.seq }).pipe(Stream.take(1), Stream.runCollect), ).map((event): [number | undefined, string] => [event.durable?.seq, event.type]), - ).toEqual([[1, "session.next.prompt.admitted"]]) + ).toEqual([[1, "prompt.admitted"]]) }), ) @@ -429,7 +428,7 @@ describe("SessionV2.prompt", () => { { concurrency: "unbounded" }, ) - expect(yield* eventCount(EventV2.versionedType(SessionEvent.Prompted.type, 1))).toBe(1) + expect(yield* eventCount(EventV2.versionedType(SessionEvent.PromptPromoted.type, 1))).toBe(1) expect(yield* admitted(messageID)).toMatchObject({ promotedSeq: 1 }) expect(yield* session.messages({ sessionID })).toMatchObject([ { id: messageID, type: "user", text: "Promote once" }, @@ -467,6 +466,7 @@ describe("SessionV2.prompt", () => { yield* events.replayAll( recorded.map((event) => ({ id: event.id, + created: DateTime.makeUnsafe(event.created), aggregateID: event.aggregate_id, seq: event.seq, type: event.type, @@ -514,13 +514,23 @@ describe("SessionV2.prompt", () => { Effect.gen(function* () { yield* setup const session = yield* SessionV2.Service - const events = yield* EventV2.Service - yield* events.publish(SessionEvent.Synthetic, { + const { db } = yield* Database.Service + const { + id: _, + type, + ...data + } = encodeMessage({ + id: messageID, sessionID, - messageID, - timestamp: yield* DateTime.now, + type: "synthetic", text: "Existing history", + time: { created: DateTime.makeUnsafe(0) }, }) + yield* db + .insert(SessionMessageTable) + .values({ id: messageID, session_id: sessionID, type, seq: 0, time_created: 0, data }) + .run() + .pipe(Effect.orDie) const failure = yield* session .prompt({ id: messageID, sessionID, prompt: Prompt.make({ text: "Conflicting prompt" }), resume: false }) diff --git a/packages/core/test/session-runner-message.test.ts b/packages/core/test/session-runner-message.test.ts index 5798b665a8..840a4e424c 100644 --- a/packages/core/test/session-runner-message.test.ts +++ b/packages/core/test/session-runner-message.test.ts @@ -51,13 +51,13 @@ describe("toLLMMessages", () => { const file = FileAttachment.make({ uri: "data:image/png;base64,aGVsbG8=", mime: "image/png", name: "hello.png" }) const messages = toLLMMessages( [ - SessionMessage.AgentSwitched.make({ + SessionMessage.AgentSelected.make({ id: id("agent"), type: "agent-switched", agent: "build", time: { created }, }), - SessionMessage.ModelSwitched.make({ + SessionMessage.ModelSelected.make({ id: id("model"), type: "model-switched", model: { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") }, diff --git a/packages/core/test/session-runner-recorded.test.ts b/packages/core/test/session-runner-recorded.test.ts index 0ebe7a6f9e..2768be820a 100644 --- a/packages/core/test/session-runner-recorded.test.ts +++ b/packages/core/test/session-runner-recorded.test.ts @@ -193,12 +193,12 @@ describe("SessionRunnerLLM recorded", () => { .orderBy(EventTable.seq) .all()).map((event) => event.type), ).toEqual([ - "session.next.prompt.admitted.1", - "session.next.prompted.1", - "session.next.step.started.1", - "session.next.text.started.1", - "session.next.text.ended.1", - "session.next.step.ended.2", + "prompt.admitted.1", + "prompt.promoted.1", + "step.started.1", + "text.started.1", + "text.ended.1", + "step.ended.1", ]) }), ) diff --git a/packages/core/test/session-runner-tool-events.test.ts b/packages/core/test/session-runner-tool-events.test.ts index 048766e439..5c8d7e07f4 100644 --- a/packages/core/test/session-runner-tool-events.test.ts +++ b/packages/core/test/session-runner-tool-events.test.ts @@ -76,7 +76,7 @@ test("local tool success serializes media base64 once and reconstructs from stru await Effect.runPromise(publisher.publish(call)) await Effect.runPromise(publisher.publish(result)) - const success = published.find((event) => event.type === "session.next.tool.success.1") + const success = published.find((event) => event.type === "tool.success.1") expect(success).toBeDefined() const serialized = JSON.stringify(success) expect(serialized.split(base64)).toHaveLength(2) @@ -94,7 +94,7 @@ test("provider-executed success retains its compatibility result", async () => { const { published, publisher } = capture() await Effect.runPromise(publisher.publish(LLMEvent.toolCall({ ...call, providerExecuted: true }))) await Effect.runPromise(publisher.publish(LLMEvent.toolResult({ ...result, providerExecuted: true }))) - const success = published.find((event) => event.type === "session.next.tool.success.1") + const success = published.find((event) => event.type === "tool.success.1") expect(success?.data).toHaveProperty("result") }) @@ -110,14 +110,13 @@ test("binary failure emits no success event", async () => { }), ), ) - expect(published.some((event) => event.type === "session.next.tool.success.1")).toBe(false) - expect(published.some((event) => event.type === "session.next.tool.failed.1")).toBe(true) + expect(published.some((event) => event.type === "tool.success.1")).toBe(false) + expect(published.some((event) => event.type === "tool.failed.1")).toBe(true) }) test("old success event data containing result still decodes", () => { const decoded = Schema.decodeUnknownSync(SessionEvent.Tool.Success.data)({ sessionID, - timestamp: Date.now(), assistantMessageID: SessionMessage.ID.create(), callID: "call-old", structured: { type: "media", mime: "image/png" }, @@ -133,6 +132,6 @@ test("step finish records settlement without publishing step ended", async () => await Effect.runPromise(publisher.publish(LLMEvent.stepStart({ index: 0 }))) await Effect.runPromise(publisher.publish(LLMEvent.stepFinish({ index: 0, reason: "stop" }))) - expect(published.some((event) => event.type === "session.next.step.ended.2")).toBe(false) + expect(published.some((event) => event.type === "step.ended.2")).toBe(false) expect(publisher.stepSettlement()).toMatchObject({ finish: "stop" }) }) diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts index 4c9446394c..ca8ec42775 100644 --- a/packages/core/test/session-runner.test.ts +++ b/packages/core/test/session-runner.test.ts @@ -423,6 +423,7 @@ const replaySessionProjection = (id: SessionV2.ID) => yield* events.replayAll( recorded.map((event) => ({ id: event.id, + created: DateTime.makeUnsafe(event.created), aggregateID: event.aggregate_id, seq: event.seq, type: event.type, @@ -740,7 +741,6 @@ describe("SessionRunnerLLM", () => { yield* events.publish(SessionEvent.Moved, { sessionID, - timestamp: DateTime.makeUnsafe(1), location: Location.Ref.make({ directory: AbsolutePath.make("/moved") }), }) expect( @@ -848,7 +848,7 @@ describe("SessionRunnerLLM", () => { yield* db .select({ id: EventTable.id }) .from(EventTable) - .where(eq(EventTable.type, "session.next.context.updated.1")) + .where(eq(EventTable.type, "session.context.updated.1")) .all() .pipe(Effect.orDie), ).toHaveLength(1) @@ -1010,10 +1010,8 @@ describe("SessionRunnerLLM", () => { response = [] yield* session.resume(sessionID) skillBaselines.set(AgentV2.ID.make("reviewer"), "Reviewer skills") - yield* events.publish(SessionEvent.AgentSwitched, { + yield* events.publish(SessionEvent.AgentSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(1), agent: "reviewer", }) yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false }) @@ -1039,10 +1037,8 @@ describe("SessionRunnerLLM", () => { if (switched) return Effect.void switched = true return events - .publish(SessionEvent.AgentSwitched, { + .publish(SessionEvent.AgentSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(1), agent: "reviewer", }) .pipe(Effect.asVoid) @@ -1069,10 +1065,8 @@ describe("SessionRunnerLLM", () => { if (switched) return Effect.void switched = true return events - .publish(SessionEvent.ModelSwitched, { + .publish(SessionEvent.ModelSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(1), model: { id: ModelV2.ID.make("replacement"), providerID: ProviderV2.ID.make("fake") }, }) .pipe(Effect.asVoid) @@ -1175,10 +1169,8 @@ describe("SessionRunnerLLM", () => { systemBaseline = "Changed context" yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false }) yield* session.resume(sessionID) - yield* events.publish(SessionEvent.ModelSwitched, { + yield* events.publish(SessionEvent.ModelSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(1), model: { id: ModelV2.ID.make("replacement"), providerID: ProviderV2.ID.make("fake") }, }) systemBaseline = "Replacement context" @@ -1217,10 +1209,8 @@ describe("SessionRunnerLLM", () => { requests.length = 0 response = [] yield* session.resume(sessionID) - yield* events.publish(SessionEvent.ModelSwitched, { + yield* events.publish(SessionEvent.ModelSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(1), model: { id: ModelV2.ID.make("replacement"), providerID: ProviderV2.ID.make("fake") }, }) systemUnavailable = true @@ -1252,14 +1242,10 @@ describe("SessionRunnerLLM", () => { const compactionID = SessionMessage.ID.create() yield* events.publish(SessionEvent.Compaction.Started, { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(1), reason: "manual", }) yield* events.publish(SessionEvent.Compaction.Ended, { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(2), reason: "manual", text: "summary", recent: "", @@ -1482,14 +1468,10 @@ describe("SessionRunnerLLM", () => { const compactionID = SessionMessage.ID.create() yield* events.publish(SessionEvent.Compaction.Started, { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(1), reason: "manual", }) yield* events.publish(SessionEvent.Compaction.Ended, { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(2), reason: "manual", text: "summary", recent: "", @@ -1686,10 +1668,8 @@ describe("SessionRunnerLLM", () => { toolExecutionsReady = 1 const run = yield* Effect.forkChild(session.resume(sessionID)) yield* Deferred.await(toolExecutionsStarted) - yield* events.publish(SessionEvent.ModelSwitched, { + yield* events.publish(SessionEvent.ModelSelected, { sessionID, - messageID: SessionMessage.ID.create(), - timestamp: DateTime.makeUnsafe(1), model: { id: ModelV2.ID.make("replacement"), providerID: ProviderV2.ID.make("fake") }, }) systemBaseline = "Replacement context" @@ -2403,27 +2383,23 @@ describe("SessionRunnerLLM", () => { yield* events.publish(SessionEvent.Step.Started, { sessionID, assistantMessageID, - timestamp: yield* DateTime.now, agent: "build", model: { id: ModelV2.ID.make("fake-model"), providerID: ProviderV2.ID.make("fake") }, }) yield* events.publish(SessionEvent.Tool.Input.Started, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID, callID: "call-interrupted", name: "echo", }) yield* events.publish(SessionEvent.Tool.Input.Ended, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID, callID: "call-interrupted", text: '{"text":"stale"}', }) yield* events.publish(SessionEvent.Tool.Called, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID, callID: "call-interrupted", tool: "echo", @@ -2467,27 +2443,23 @@ describe("SessionRunnerLLM", () => { yield* events.publish(SessionEvent.Step.Started, { sessionID, assistantMessageID, - timestamp: yield* DateTime.now, agent: "build", model: { id: ModelV2.ID.make("fake-model"), providerID: ProviderV2.ID.make("fake") }, }) yield* events.publish(SessionEvent.Tool.Input.Started, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID, callID: "call-hosted-interrupted", name: "web_search", }) yield* events.publish(SessionEvent.Tool.Input.Ended, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID, callID: "call-hosted-interrupted", text: '{"query":"stale"}', }) yield* events.publish(SessionEvent.Tool.Called, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID, callID: "call-hosted-interrupted", tool: "web_search", @@ -2527,13 +2499,11 @@ describe("SessionRunnerLLM", () => { yield* events.publish(SessionEvent.Step.Started, { sessionID, assistantMessageID, - timestamp: yield* DateTime.now, agent: "build", model: { id: ModelV2.ID.make("fake-model"), providerID: ProviderV2.ID.make("fake") }, }) yield* events.publish(SessionEvent.Tool.Input.Started, { sessionID, - timestamp: yield* DateTime.now, assistantMessageID, callID: "call-pending-interrupted", name: "echo", @@ -2578,7 +2548,7 @@ describe("SessionRunnerLLM", () => { const events = yield* EventV2.Service const defect = new Error("fail after prompt promotion") let fail = true - yield* events.project(SessionEvent.Prompted, () => (fail ? Effect.die(defect) : Effect.void)) + yield* events.project(SessionEvent.PromptPromoted, () => (fail ? Effect.die(defect) : Effect.void)) yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Recover promoted input" }), resume: false }) expect(yield* session.resume(sessionID).pipe(Effect.catchDefect(Effect.succeed))).toBe(defect) @@ -2603,7 +2573,9 @@ describe("SessionRunnerLLM", () => { const session = yield* SessionV2.Service const events = yield* EventV2.Service yield* events.listen((event) => - event.type === SessionEvent.Prompted.type ? Effect.die("fail after prompt promotion commits") : Effect.void, + event.type === SessionEvent.PromptPromoted.type + ? Effect.die("fail after prompt promotion commits") + : Effect.void, ) yield* session.prompt({ sessionID, @@ -3013,7 +2985,7 @@ describe("SessionRunnerLLM", () => { { type: "user", text: "Interrupt provider" }, { type: "assistant", finish: "error", error: { type: "unknown", message: "Provider turn interrupted" } }, ]) - expect(yield* recordedEventTypes(sessionID)).toContain("session.next.step.failed.2") + expect(yield* recordedEventTypes(sessionID)).toContain("step.failed.1") yield* session.interrupt(sessionID) }), ) @@ -3057,8 +3029,8 @@ describe("SessionRunnerLLM", () => { }, ]) const eventTypes = yield* recordedEventTypes(sessionID) - expect(eventTypes).toContain("session.next.step.failed.2") - expect(eventTypes).not.toContain("session.next.step.ended.2") + expect(eventTypes).toContain("step.failed.1") + expect(eventTypes).not.toContain("step.ended.1") }), ) diff --git a/packages/core/test/session-title.test.ts b/packages/core/test/session-title.test.ts index 1e9bcfbeb8..59abb8fabc 100644 --- a/packages/core/test/session-title.test.ts +++ b/packages/core/test/session-title.test.ts @@ -86,17 +86,13 @@ const prompt = (sessionID: SessionV2.ID, text: string) => const messageID = SessionMessage.ID.create() yield* events.publish(SessionEvent.PromptAdmitted, { sessionID, - messageID, - timestamp: DateTime.makeUnsafe(0), + inputID: messageID, prompt: Prompt.make({ text }), delivery: "steer", }) - yield* events.publish(SessionEvent.Prompted, { + yield* events.publish(SessionEvent.PromptPromoted, { sessionID, - messageID, - timestamp: DateTime.makeUnsafe(0), - prompt: Prompt.make({ text }), - delivery: "steer", + inputID: messageID, }) }) diff --git a/packages/core/test/session-tool-progress.test.ts b/packages/core/test/session-tool-progress.test.ts index 6d07b14657..e730b3556b 100644 --- a/packages/core/test/session-tool-progress.test.ts +++ b/packages/core/test/session-tool-progress.test.ts @@ -51,7 +51,6 @@ describe("Tool.Progress", () => { yield* service.publish(SessionEvent.Step.Started, { sessionID, assistantMessageID, - timestamp, agent: "build", model, }) @@ -69,14 +68,12 @@ describe("Tool.Progress", () => { Effect.gen(function* () { yield* service.publish(SessionEvent.Tool.Input.Started, { sessionID, - timestamp, assistantMessageID, callID, name: "bash", }) yield* service.publish(SessionEvent.Tool.Called, { sessionID, - timestamp, assistantMessageID, callID, tool: "bash", @@ -92,7 +89,6 @@ describe("Tool.Progress", () => { yield* service.publish(SessionEvent.Tool.Progress, { sessionID, - timestamp, assistantMessageID, callID: "call-success", structured: { phase: "checkpoint" }, @@ -104,7 +100,6 @@ describe("Tool.Progress", () => { const success = yield* service.publish(SessionEvent.Tool.Success, { sessionID, - timestamp, assistantMessageID, callID: "call-success", structured: { phase: "done" }, @@ -118,7 +113,6 @@ describe("Tool.Progress", () => { yield* start("call-failed") yield* service.publish(SessionEvent.Tool.Progress, { sessionID, - timestamp, assistantMessageID, callID: "call-failed", structured: { phase: "checkpoint" }, @@ -126,7 +120,6 @@ describe("Tool.Progress", () => { }) const failed = yield* service.publish(SessionEvent.Tool.Failed, { sessionID, - timestamp, assistantMessageID, callID: "call-failed", error: { type: "unknown", message: "boom" }, diff --git a/packages/core/test/shared-schema.test.ts b/packages/core/test/shared-schema.test.ts index e7556a8b7c..ecdd555fa8 100644 --- a/packages/core/test/shared-schema.test.ts +++ b/packages/core/test/shared-schema.test.ts @@ -148,8 +148,8 @@ test("Core reuses the canonical shared schemas", async () => { [coreSessionInput.Admitted, SessionInput.Admitted], [coreSessionMessage.ID, SessionMessage.ID], [coreSessionMessage.UnknownError, SessionMessage.UnknownError], - [coreSessionMessage.AgentSwitched, SessionMessage.AgentSwitched], - [coreSessionMessage.ModelSwitched, SessionMessage.ModelSwitched], + [coreSessionMessage.AgentSelected, SessionMessage.AgentSelected], + [coreSessionMessage.ModelSelected, SessionMessage.ModelSelected], [coreSessionMessage.User, SessionMessage.User], [coreSessionMessage.Synthetic, SessionMessage.Synthetic], [coreSessionMessage.System, SessionMessage.System], diff --git a/packages/core/test/tool-shell.test.ts b/packages/core/test/tool-shell.test.ts index 15bd741a2e..6fd43ee2a2 100644 --- a/packages/core/test/tool-shell.test.ts +++ b/packages/core/test/tool-shell.test.ts @@ -79,27 +79,23 @@ const executionNode = makeGlobalNode({ yield* events.publish(SessionEvent.Step.Started, { sessionID: id, assistantMessageID, - timestamp: yield* DateTime.now, agent: session.agent ?? AgentV2.ID.make("code"), model: sessionModel, }) yield* events.publish(SessionEvent.Text.Started, { sessionID: id, assistantMessageID, - timestamp: yield* DateTime.now, textID, }) yield* events.publish(SessionEvent.Text.Ended, { sessionID: id, assistantMessageID, - timestamp: yield* DateTime.now, textID, text: "ok", }) yield* events.publish(SessionEvent.Step.Ended, { sessionID: id, assistantMessageID, - timestamp: yield* DateTime.now, finish: "stop", cost: 0, tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, diff --git a/packages/core/test/tool-subagent.test.ts b/packages/core/test/tool-subagent.test.ts index 6274e329aa..4dd46da691 100644 --- a/packages/core/test/tool-subagent.test.ts +++ b/packages/core/test/tool-subagent.test.ts @@ -53,27 +53,23 @@ const executionNode = makeGlobalNode({ yield* events.publish(SessionEvent.Step.Started, { sessionID, assistantMessageID, - timestamp: yield* DateTime.now, agent: AgentV2.ID.make("reviewer"), model: childModel, }) yield* events.publish(SessionEvent.Text.Started, { sessionID, assistantMessageID, - timestamp: yield* DateTime.now, textID, }) yield* events.publish(SessionEvent.Text.Ended, { sessionID, assistantMessageID, - timestamp: yield* DateTime.now, textID, text: childText, }) yield* events.publish(SessionEvent.Step.Ended, { sessionID, assistantMessageID, - timestamp: yield* DateTime.now, finish: "stop", cost: 0, tokens, diff --git a/packages/opencode/src/cli/cmd/run/noninteractive.ts b/packages/opencode/src/cli/cmd/run/noninteractive.ts index fe6b95267c..60ded4ebd9 100644 --- a/packages/opencode/src/cli/cmd/run/noninteractive.ts +++ b/packages/opencode/src/cli/cmd/run/noninteractive.ts @@ -156,17 +156,16 @@ export async function runNonInteractivePrompt(input: Input) { continue } if (!("sessionID" in event.data) || event.data.sessionID !== input.sessionID) continue - const time = "timestamp" in event.data ? toMillis(event.data.timestamp) : Date.now() + const time = toMillis(event.created) - if (event.type === "session.next.prompted") { - if (event.data.messageID === messageID) { + if (event.type === "prompt.promoted") { + if (event.data.inputID === messageID) { promoted = true continue } - if (promoted && event.data.delivery === "queue") return } if ( - event.type === "session.next.execution.settled" && + event.type === "execution.settled" && event.data.outcome === "interrupted" && (interrupted || permissionRejected || questionRejected || formCancelled) ) { @@ -174,7 +173,7 @@ export async function runNonInteractivePrompt(input: Input) { } if (!promoted) continue - if (event.type === "session.next.step.started") { + if (event.type === "step.started") { const part: StepStartPart = { id: partID(event.id), sessionID: input.sessionID, @@ -190,11 +189,11 @@ export async function runNonInteractivePrompt(input: Input) { continue } - if (event.type === "session.next.text.started") { + if (event.type === "text.started") { starts.set(event.data.textID, { id: partID(event.id), timestamp: time }) continue } - if (event.type === "session.next.text.ended") { + if (event.type === "text.ended") { const started = starts.get(event.data.textID) const part: TextPart = { id: started?.id ?? partID(event.id), @@ -208,11 +207,11 @@ export async function runNonInteractivePrompt(input: Input) { continue } - if (event.type === "session.next.reasoning.started") { + if (event.type === "reasoning.started") { starts.set(event.data.reasoningID, { id: partID(event.id), timestamp: time }) continue } - if (event.type === "session.next.reasoning.ended" && input.thinking) { + if (event.type === "reasoning.ended" && input.thinking) { const started = starts.get(event.data.reasoningID) const part: ReasoningPart = { id: started?.id ?? partID(event.id), @@ -237,7 +236,7 @@ export async function runNonInteractivePrompt(input: Input) { continue } - if (event.type === "session.next.tool.input.started") { + if (event.type === "tool.input.started") { tools.set(event.data.callID, { id: partID(event.id), timestamp: time, @@ -247,12 +246,12 @@ export async function runNonInteractivePrompt(input: Input) { }) continue } - if (event.type === "session.next.tool.input.ended") { + if (event.type === "tool.input.ended") { const current = tools.get(event.data.callID) if (current) current.raw = event.data.text continue } - if (event.type === "session.next.tool.called") { + if (event.type === "tool.called") { const current = tools.get(event.data.callID) tools.set(event.data.callID, { id: current?.id ?? partID(event.id), @@ -265,7 +264,7 @@ export async function runNonInteractivePrompt(input: Input) { }) continue } - if (event.type === "session.next.tool.success") { + if (event.type === "tool.success") { const current = tools.get(event.data.callID) ?? fallbackTool(event) const part: ToolPart = { id: current.id, @@ -298,7 +297,7 @@ export async function runNonInteractivePrompt(input: Input) { if (!emit("tool_use", time, { part })) await input.renderTool(part) continue } - if (event.type === "session.next.tool.failed") { + if (event.type === "tool.failed") { const current = tools.get(event.data.callID) ?? fallbackTool(event) const error = event.data.error.message const part: ToolPart = { @@ -329,7 +328,7 @@ export async function runNonInteractivePrompt(input: Input) { continue } - if (event.type === "session.next.step.ended") { + if (event.type === "step.ended") { const part: StepFinishPart = { id: partID(event.id), sessionID: input.sessionID, @@ -343,19 +342,19 @@ export async function runNonInteractivePrompt(input: Input) { emit("step_finish", time, { part }) continue } - if (event.type === "session.next.step.failed") { + if (event.type === "step.failed") { if (interrupted || permissionRejected || questionRejected || formCancelled) continue emittedError = true process.exitCode = 1 if (!emit("error", time, { error: event.data.error })) UI.error(event.data.error.message) continue } - if (event.type === "session.next.execution.settled") { + if (event.type === "execution.settled") { if (event.data.outcome === "failure" && !emittedError && !questionRejected && !formCancelled) { emittedError = true process.exitCode = 1 const error = event.data.error ?? { type: "unknown", message: "Session execution failed" } - if (!emit("error", toMillis(event.data.timestamp), { error })) UI.error(error.message) + if (!emit("error", time, { error })) UI.error(error.message) } if (event.data.outcome === "interrupted" && interrupted) process.exitCode = 130 return @@ -458,11 +457,12 @@ function partID(eventID: string) { function fallbackTool(event: { id: string - data: { timestamp: number; assistantMessageID: string; callID: string } + created: number + data: { assistantMessageID: string; callID: string } }): ToolState { return { id: partID(event.id), - timestamp: toMillis(event.data.timestamp), + timestamp: toMillis(event.created), assistantMessageID: event.data.assistantMessageID, tool: "tool", input: {}, diff --git a/packages/opencode/src/cli/cmd/run/session-data.ts b/packages/opencode/src/cli/cmd/run/session-data.ts index 05daa8b423..9450f6cf78 100644 --- a/packages/opencode/src/cli/cmd/run/session-data.ts +++ b/packages/opencode/src/cli/cmd/run/session-data.ts @@ -728,7 +728,7 @@ export function reduceSessionData(input: SessionDataInput): SessionDataOutput { const data = input.data const event = input.event - if (event.type === "session.next.shell.started") { + if (event.type === "shell.started") { if (event.properties.sessionID !== input.sessionID) { return out(data, commits) } @@ -748,7 +748,7 @@ export function reduceSessionData(input: SessionDataInput): SessionDataOutput { return out(data, commits, patch({ status: "running shell" })) } - if (event.type === "session.next.shell.ended") { + if (event.type === "shell.ended") { if (event.properties.sessionID !== input.sessionID) { return out(data, commits) } diff --git a/packages/opencode/src/cli/cmd/run/stream-v2.subagent.ts b/packages/opencode/src/cli/cmd/run/stream-v2.subagent.ts index eac116b0b8..ac652423a1 100644 --- a/packages/opencode/src/cli/cmd/run/stream-v2.subagent.ts +++ b/packages/opencode/src/cli/cmd/run/stream-v2.subagent.ts @@ -424,21 +424,21 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac } const reduce = (child: ChildState, event: V2Event) => { - if (event.type === "session.next.prompted") { - if (userFrame(child, event.data.messageID, event.data.prompt.text)) { - touch(child, event.data.timestamp) + if (event.type === "prompt.promoted") { + if (userFrame(child, event.data.inputID, "")) { + touch(child, event.created) notifyDetail(child) } return } - if (event.type === "session.next.step.started") { - touch(child, event.data.timestamp) + if (event.type === "step.started") { + touch(child, event.created) if (child.label === FALLBACK_LABEL && event.data.agent) child.label = Locale.titlecase(event.data.agent) if (child.status !== "running") child.status = "running" input.emit() return } - if (event.type === "session.next.text.delta") { + if (event.type === "text.delta") { const projected = child.projectedText.get(event.data.textID) const covered = projected?.indexOf(event.data.delta) ?? -1 if (projected && covered >= 0) { @@ -455,11 +455,11 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac messageID: event.data.assistantMessageID, partID: event.data.textID, }) - touch(child, event.data.timestamp) + touch(child, event.created) notifyDetail(child) return } - if (event.type === "session.next.text.ended") { + if (event.type === "text.ended") { child.text.set(event.data.textID, event.data.text) child.projectedText.delete(event.data.textID) setFrame(child, `text:${event.data.textID}`, { @@ -470,11 +470,11 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac messageID: event.data.assistantMessageID, partID: event.data.textID, }) - touch(child, event.data.timestamp) + touch(child, event.created) notifyDetail(child) return } - if (event.type === "session.next.reasoning.delta") { + if (event.type === "reasoning.delta") { const projected = child.projectedReasoning.get(event.data.reasoningID) const covered = projected?.indexOf(event.data.delta) ?? -1 if (projected && covered >= 0) { @@ -495,7 +495,7 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac notifyDetail(child) return } - if (event.type === "session.next.reasoning.ended") { + if (event.type === "reasoning.ended") { child.reasoning.set(event.data.reasoningID, event.data.text) child.projectedReasoning.delete(event.data.reasoningID) if (!input.thinking) return @@ -510,16 +510,16 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac notifyDetail(child) return } - if (event.type === "session.next.tool.input.started") { - child.tools.set(event.data.callID, { name: event.data.name, input: {}, started: event.data.timestamp }) + if (event.type === "tool.input.started") { + child.tools.set(event.data.callID, { name: event.data.name, input: {}, started: event.created }) return } - if (event.type === "session.next.tool.called") { + if (event.type === "tool.called") { const current = child.tools.get(event.data.callID) child.tools.set(event.data.callID, { name: event.data.tool, input: event.data.input, - started: current?.started ?? event.data.timestamp, + started: current?.started ?? event.created, }) childTool( child, @@ -529,18 +529,18 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac name: event.data.tool, provider: event.data.provider, state: { status: "running", input: event.data.input, structured: {}, content: [] }, - time: { created: current?.started ?? event.data.timestamp, ran: event.data.timestamp }, + time: { created: current?.started ?? event.created, ran: event.created }, }, event.data.assistantMessageID, ) - touch(child, event.data.timestamp) + touch(child, event.created) notifyDetail(child) return } - if (event.type === "session.next.tool.success" || event.type === "session.next.tool.failed") { + if (event.type === "tool.success" || event.type === "tool.failed") { if (child.finishedTools.has(event.data.callID)) return const current = child.tools.get(event.data.callID) - const failed = event.type === "session.next.tool.failed" + const failed = event.type === "tool.failed" childTool( child, { @@ -566,18 +566,18 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac result: event.data.result, }, time: { - created: current?.started ?? event.data.timestamp, + created: current?.started ?? event.created, ran: current?.started, - completed: event.data.timestamp, + completed: event.created, }, }, event.data.assistantMessageID, ) - touch(child, event.data.timestamp) + touch(child, event.created) notifyDetail(child) return } - if (event.type === "session.next.step.failed") { + if (event.type === "step.failed") { setFrame(child, `error:step:${event.data.assistantMessageID}`, { kind: "error", source: "system", @@ -585,14 +585,14 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac phase: "start", messageID: event.data.assistantMessageID, }) - touch(child, event.data.timestamp) + touch(child, event.created) notifyDetail(child) return } - if (event.type === "session.next.execution.settled") { + if (event.type === "execution.settled") { child.status = event.data.outcome === "success" ? "completed" : event.data.outcome === "interrupted" ? "cancelled" : "error" - touch(child, event.data.timestamp) + touch(child, event.created) input.emit() } } @@ -613,15 +613,15 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac return { main(event) { - if (event.type === "session.next.tool.called") { + if (event.type === "tool.called") { if (event.data.tool === "subagent") pendingCalls.set(event.data.callID, event.data.input) return } - if (event.type === "session.next.tool.failed") { + if (event.type === "tool.failed") { pendingCalls.delete(event.data.callID) return } - if (event.type !== "session.next.tool.success") return + if (event.type !== "tool.success") return const pending = pendingCalls.get(event.data.callID) pendingCalls.delete(event.data.callID) const found = childSessionID(record(event.data.structured)) @@ -633,7 +633,7 @@ export function createSubagentTracker(input: SubagentTrackerInput): SubagentTrac child.status = "running" } if (!found.running && child.status === "running") child.status = "completed" - touch(child, event.data.timestamp) + touch(child, event.created) input.emit() if (!child.hydrated) void hydrateChild(child) }, diff --git a/packages/opencode/src/cli/cmd/run/stream-v2.transport.ts b/packages/opencode/src/cli/cmd/run/stream-v2.transport.ts index 7700ddd024..614a960634 100644 --- a/packages/opencode/src/cli/cmd/run/stream-v2.transport.ts +++ b/packages/opencode/src/cli/cmd/run/stream-v2.transport.ts @@ -294,7 +294,14 @@ export async function createSessionTransport(input: StreamInput): Promise { @@ -395,17 +402,17 @@ export async function createSessionTransport(input: StreamInput): Promise previous.length) @@ -445,7 +452,7 @@ export async function createSessionTransport(input: StreamInput): Promise previous.length) @@ -486,17 +493,17 @@ export async function createSessionTransport(input: StreamInput): Promise 0 ? total.toLocaleString() : "" - write([], { phase: event.data.finish === "tool-calls" ? "running" : "idle", usage: event.data.cost ? `${usage} · ${money.format(event.data.cost)}` : usage }) + write([], { + phase: event.data.finish === "tool-calls" ? "running" : "idle", + usage: event.data.cost ? `${usage} · ${money.format(event.data.cost)}` : usage, + }) return } - if (event.type === "session.next.step.failed") { + if (event.type === "step.failed") { state.errors.add(event.data.assistantMessageID) if (state.wait) state.wait.failureRendered = true write([{ kind: "error", source: "system", text: errorMessage(event.data.error), phase: "start" }]) return } - if (event.type === "session.next.execution.settled") { + if (event.type === "execution.settled") { write([], { phase: "idle", status: "" }) const current = state.wait if (!current || (!current.promoted && !current.interrupted)) return @@ -611,7 +628,7 @@ export async function createSessionTransport(input: StreamInput): Promise + const stream = response.stream[Symbol.asyncIterator]() as AsyncGenerator try { const first = await stream.next() if (first.done || first.value.type !== "server.connected") throw new Error("Event stream disconnected") @@ -689,10 +706,7 @@ export async function createSessionTransport(input: StreamInput): Promise (file.attachment ? [file.attachment] : [])), - ...promptFiles, - ] + const attachments = [...prepared.flatMap((file) => (file.attachment ? [file.attachment] : [])), ...promptFiles] const agents = next.prompt.parts.flatMap((part) => part.type === "agent" ? [ @@ -735,10 +749,7 @@ export async function createSessionTransport(input: StreamInput): Promise (file.text ? [file.text] : [])), - ].join("\n\n"), + text: [next.prompt.text, ...prepared.flatMap((file) => (file.text ? [file.text] : []))].join("\n\n"), files: attachments.length ? attachments : undefined, agents: agents.length ? agents : undefined, }, diff --git a/packages/opencode/test/cli/run/noninteractive.test.ts b/packages/opencode/test/cli/run/noninteractive.test.ts index d8bff61a73..839b99656b 100644 --- a/packages/opencode/test/cli/run/noninteractive.test.ts +++ b/packages/opencode/test/cli/run/noninteractive.test.ts @@ -18,31 +18,33 @@ function form(id: string, sessionID: string): FormInfo { } function formCreated(info: FormInfo): V2Event { - return { id: `evt_${info.id}`, type: "form.created", data: { form: info } } + return { id: `evt_${info.id}`, created: 0, type: "form.created", data: { form: info } } } -function prompted(messageID: string): V2Event { +function prompted(inputID: string): V2Event { return { id: "evt_prompted", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: { aggregateID: "ses_1", seq: 0, version: 1 }, - data: { timestamp: 1, sessionID: "ses_1", messageID, prompt: { text: "hello" }, delivery: "steer" }, + data: { sessionID: "ses_1", inputID }, } } function settled(outcome: "success" | "interrupted" = "success"): V2Event { return { id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 2, sessionID: "ses_1", outcome }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome }, } } // Runs one non-interactive prompt against a mocked SDK. `turn` produces the // live events the prompt admission triggers, keyed by the generated message ID. -async function run(input: { turn: (messageID: string) => V2Event[]; pendingForms?: FormInfo[]; attached?: boolean }) { +async function run(input: { turn: (inputID: string) => V2Event[]; pendingForms?: FormInfo[]; attached?: boolean }) { const sdk = new OpencodeClient() - const values: V2Event[] = [{ id: "evt_connected", type: "server.connected", data: {} }] + const values: V2Event[] = [{ id: "evt_connected", created: 0, type: "server.connected", data: {} }] let wake: (() => void) | undefined const stream = (async function* (): AsyncGenerator { while (true) { diff --git a/packages/opencode/test/cli/run/session-data.test.ts b/packages/opencode/test/cli/run/session-data.test.ts index ec21cd007e..89a6751d1b 100644 --- a/packages/opencode/test/cli/run/session-data.test.ts +++ b/packages/opencode/test/cli/run/session-data.test.ts @@ -329,7 +329,7 @@ describe("run session data", () => { test("renders direct shell mode from first-class shell events", () => { let data = createSessionData() const started = reduce(data, { - type: "session.next.shell.started", + type: "shell.started", properties: { sessionID: "session-1", timestamp: 1, @@ -353,7 +353,7 @@ describe("run session data", () => { data = started.data const ended = reduce(data, { - type: "session.next.shell.ended", + type: "shell.ended", properties: { sessionID: "session-1", timestamp: 2, @@ -380,7 +380,7 @@ describe("run session data", () => { test("suppresses legacy bash part updates once shell events claim the call", () => { let data = reduce(createSessionData(), { - type: "session.next.shell.started", + type: "shell.started", properties: { sessionID: "session-1", timestamp: 1, @@ -409,7 +409,7 @@ describe("run session data", () => { ).toEqual([]) data = reduce(data, { - type: "session.next.shell.ended", + type: "shell.ended", properties: { sessionID: "session-1", timestamp: 2, @@ -463,7 +463,7 @@ describe("run session data", () => { expect( reduce(data, { - type: "session.next.shell.started", + type: "shell.started", properties: { sessionID: "session-1", timestamp: 1, @@ -497,7 +497,7 @@ describe("run session data", () => { expect( reduce(data, { - type: "session.next.shell.ended", + type: "shell.ended", properties: { sessionID: "session-1", timestamp: 2, diff --git a/packages/opencode/test/cli/run/stream-v2.transport.test.ts b/packages/opencode/test/cli/run/stream-v2.transport.test.ts index 2f78d106cf..8bf429e500 100644 --- a/packages/opencode/test/cli/run/stream-v2.transport.test.ts +++ b/packages/opencode/test/cli/run/stream-v2.transport.test.ts @@ -50,7 +50,7 @@ function ok(data: T) { } function connected(id = "evt_connected") { - return { id, type: "server.connected", data: {} } satisfies RunV2Event + return { id, created: 0, type: "server.connected", data: {} } satisfies RunV2Event } function durable(sessionID: string, seq = 0, version = 1) { @@ -125,22 +125,20 @@ function sdk(input: { spyOn(client.v2.session, "switchModel").mockImplementation(() => ok(undefined)) // The generated methods have conditional return types for throwOnError; the // minimal shapes below are enough for family discovery and model fallback. - spyOn(client.v2.session, "list").mockImplementation( - (request) => { - const parentID = request?.parentID - return ok({ - location: { directory: "/tmp", project: { id: "proj_1", directory: "/tmp" } }, - data: - input.sessions?.filter((session) => - parentID === undefined - ? true - : parentID === null - ? session.parentID === undefined - : session.parentID === parentID, - ) ?? [], - }) as never - }, - ) + spyOn(client.v2.session, "list").mockImplementation((request) => { + const parentID = request?.parentID + return ok({ + location: { directory: "/tmp", project: { id: "proj_1", directory: "/tmp" } }, + data: + input.sessions?.filter((session) => + parentID === undefined + ? true + : parentID === null + ? session.parentID === undefined + : session.parentID === parentID, + ) ?? [], + }) as never + }) spyOn(client.v2.model, "default").mockImplementation( () => ok({ @@ -201,21 +199,19 @@ describe("V2 mini transport", () => { while (!admitted) await Bun.sleep(0) events.push({ id: "evt_prompted", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: durable("ses_1"), data: { - timestamp: 2, sessionID: "ses_1", - messageID: "msg_prompt", - prompt: { text: "hello" }, - delivery: "steer", + inputID: "msg_prompt", }, }) events.push({ id: "evt_text", - type: "session.next.text.delta", + created: 0, + type: "text.delta", data: { - timestamp: 3, sessionID: "ses_1", assistantMessageID: "msg_assistant", textID: "txt_1", @@ -224,8 +220,9 @@ describe("V2 mini transport", () => { }) events.push({ id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 4, sessionID: "ses_1", outcome: "success" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome: "success" }, }) await turn @@ -261,20 +258,19 @@ describe("V2 mini transport", () => { queueMicrotask(() => { events.push({ id: "evt_prompted", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: durable("ses_1"), data: { - timestamp: 2, sessionID: "ses_1", - messageID: "msg_prompt", - prompt: { text: input.prompt?.text ?? "" }, - delivery: "steer", + inputID: "msg_prompt", }, }) events.push({ id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 3, sessionID: "ses_1", outcome: "success" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome: "success" }, }) }) return ok({ @@ -356,20 +352,19 @@ describe("V2 mini transport", () => { queueMicrotask(() => { events.push({ id: "evt_prompted", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: durable("ses_1"), data: { - timestamp: 2, sessionID: "ses_1", - messageID: "msg_prompt", - prompt: { text: input.prompt?.text ?? "" }, - delivery: "steer", + inputID: "msg_prompt", }, }) events.push({ id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 3, sessionID: "ses_1", outcome: "success" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome: "success" }, }) }) return ok({ @@ -454,20 +449,19 @@ describe("V2 mini transport", () => { queueMicrotask(() => { events.push({ id: "evt_prompted", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: durable("ses_1"), data: { - timestamp: 2, sessionID: "ses_1", - messageID: "msg_prompt", - prompt: { text: input.prompt?.text ?? "" }, - delivery: "steer", + inputID: "msg_prompt", }, }) events.push({ id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 3, sessionID: "ses_1", outcome: "success" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome: "success" }, }) }) return ok({ @@ -528,6 +522,7 @@ describe("V2 mini transport", () => { }) events.push({ id: "evt_permission", + created: 0, type: "permission.v2.asked", data: { id: "per_1", sessionID: "ses_1", action: "read", resources: ["/tmp/file"] }, }) @@ -728,9 +723,9 @@ describe("V2 mini transport", () => { const replay = transport.replayOnResize({ localRows: () => [], reset: () => resetting }) events.push({ id: "evt_text", - type: "session.next.text.delta", + created: 0, + type: "text.delta", data: { - timestamp: 3, sessionID: "ses_1", assistantMessageID: "msg_assistant", textID: "txt_1", @@ -813,10 +808,10 @@ describe("V2 mini transport", () => { }) events.push({ id: "evt_reasoning", - type: "session.next.reasoning.ended", + created: 0, + type: "reasoning.ended", durable: durable("ses_1"), data: { - timestamp: 3, sessionID: "ses_1", assistantMessageID: "msg_assistant", reasoningID: "reasoning_1", @@ -869,8 +864,9 @@ describe("V2 mini transport", () => { await transport.interruptActiveTurn() events.push({ id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 3, sessionID: "ses_1", outcome: "interrupted" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome: "success" }, }) await turn @@ -924,20 +920,19 @@ describe("V2 mini transport", () => { while (!admitted) await Bun.sleep(0) events.push({ id: "evt_prompted", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: durable("ses_1"), data: { - timestamp: 2, sessionID: "ses_1", - messageID: "msg_prompt", - prompt: { text: "hello" }, - delivery: "steer", + inputID: "msg_prompt", }, }) events.push({ id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 3, sessionID: "ses_1", outcome: "success" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome: "success" }, }) await turn @@ -985,22 +980,21 @@ describe("V2 mini transport", () => { while (!admitted) await Bun.sleep(0) events.push({ id: "evt_prompted", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: durable("ses_1"), data: { - timestamp: 2, sessionID: "ses_1", - messageID: "msg_prompt", - prompt: { text: "hello" }, - delivery: "steer", + inputID: "msg_prompt", }, }) await Bun.sleep(0) controller.abort() events.push({ id: "evt_settled", - type: "session.next.execution.settled", - data: { timestamp: 3, sessionID: "ses_1", outcome: "interrupted" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_1", outcome: "success" }, }) await turn @@ -1054,10 +1048,10 @@ describe("V2 mini transport", () => { events.push({ id: "evt_child_step", - type: "session.next.step.started", + created: 0, + type: "step.started", durable: durable("ses_child"), data: { - timestamp: 2, sessionID: "ses_child", assistantMessageID: "msg_child_a", agent: "explore", @@ -1072,9 +1066,9 @@ describe("V2 mini transport", () => { events.push({ id: "evt_child_text", - type: "session.next.text.delta", + created: 0, + type: "text.delta", data: { - timestamp: 3, sessionID: "ses_child", assistantMessageID: "msg_child_a", textID: "txt_child", @@ -1086,8 +1080,9 @@ describe("V2 mini transport", () => { events.push({ id: "evt_child_settled", - type: "session.next.execution.settled", - data: { timestamp: 4, sessionID: "ses_child", outcome: "success" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_child", outcome: "success" }, }) while (!states().some((state) => state.tabs.some((tab) => tab.status === "completed"))) await Bun.sleep(0) await transport.close() @@ -1130,10 +1125,10 @@ describe("V2 mini transport", () => { // Both events arrive while session.get is still in flight. events.push({ id: "evt_child_step", - type: "session.next.step.started", + created: 0, + type: "step.started", durable: durable("ses_child"), data: { - timestamp: 2, sessionID: "ses_child", assistantMessageID: "msg_child_a", agent: "explore", @@ -1142,8 +1137,9 @@ describe("V2 mini transport", () => { }) events.push({ id: "evt_child_settled", - type: "session.next.execution.settled", - data: { timestamp: 3, sessionID: "ses_child", outcome: "interrupted" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_child", outcome: "interrupted" }, }) await Bun.sleep(0) resolveGet?.() @@ -1188,10 +1184,10 @@ describe("V2 mini transport", () => { // Child event arrives first and gets buffered behind the gated session.get. events.push({ id: "evt_child_step", - type: "session.next.step.started", + created: 0, + type: "step.started", durable: durable("ses_child"), data: { - timestamp: 2, sessionID: "ses_child", assistantMessageID: "msg_child_a", agent: "explore", @@ -1201,10 +1197,10 @@ describe("V2 mini transport", () => { // Parent's background subagent tool.success adopts the child mid-discovery. events.push({ id: "evt_parent_call", - type: "session.next.tool.called", + created: 0, + type: "tool.called", durable: durable("ses_1"), data: { - timestamp: 3, sessionID: "ses_1", assistantMessageID: "msg_parent_a", callID: "call_sub", @@ -1215,10 +1211,10 @@ describe("V2 mini transport", () => { }) events.push({ id: "evt_parent_success", - type: "session.next.tool.success", + created: 0, + type: "tool.success", durable: durable("ses_1", 1), data: { - timestamp: 4, sessionID: "ses_1", assistantMessageID: "msg_parent_a", callID: "call_sub", @@ -1230,8 +1226,9 @@ describe("V2 mini transport", () => { // The settled event arrives after adoption, so it applies directly. events.push({ id: "evt_child_settled", - type: "session.next.execution.settled", - data: { timestamp: 5, sessionID: "ses_child", outcome: "interrupted" }, + created: 0, + type: "execution.settled", + data: { sessionID: "ses_child", outcome: "interrupted" }, }) while (!states().some((state) => state.tabs.some((tab) => tab.status === "cancelled"))) await Bun.sleep(0) diff --git a/packages/opencode/test/v2/session-message-updater.test.ts b/packages/opencode/test/v2/session-message-updater.test.ts index e85afaeeff..4da4dc9d13 100644 --- a/packages/opencode/test/v2/session-message-updater.test.ts +++ b/packages/opencode/test/v2/session-message-updater.test.ts @@ -21,12 +21,12 @@ test.skip("step snapshots carry over to assistant messages", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.step.started", + created: DateTime.makeUnsafe(0), + type: "step.started", durable: durable(sessionID), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(1), agent: "build", model: { id: ModelV2.ID.make("model"), @@ -43,12 +43,12 @@ test.skip("step snapshots carry over to assistant messages", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.step.ended", + created: DateTime.makeUnsafe(0), + type: "step.ended", durable: durable(sessionID, 1, 2), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(2), finish: "stop", cost: 0, tokens: { @@ -76,12 +76,12 @@ test.skip("text ended populates assistant text content", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.step.started", + created: DateTime.makeUnsafe(0), + type: "step.started", durable: durable(sessionID), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(1), agent: "build", model: { id: ModelV2.ID.make("model"), @@ -95,12 +95,12 @@ test.skip("text ended populates assistant text content", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.text.started", + created: DateTime.makeUnsafe(0), + type: "text.started", durable: durable(sessionID, 1), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(2), textID: "text-1", }, } satisfies SessionEvent.Event), @@ -109,12 +109,12 @@ test.skip("text ended populates assistant text content", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.text.ended", + created: DateTime.makeUnsafe(0), + type: "text.ended", durable: durable(sessionID, 2), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(3), textID: "text-1", text: "hello assistant", }, @@ -135,12 +135,12 @@ test.skip("tool completion stores completed timestamp", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.step.started", + created: DateTime.makeUnsafe(0), + type: "step.started", durable: durable(sessionID), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(1), agent: "build", model: { id: ModelV2.ID.make("model"), @@ -154,12 +154,12 @@ test.skip("tool completion stores completed timestamp", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.tool.input.started", + created: DateTime.makeUnsafe(0), + type: "tool.input.started", durable: durable(sessionID, 1), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(2), callID, name: "bash", }, @@ -169,12 +169,12 @@ test.skip("tool completion stores completed timestamp", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.tool.called", + created: DateTime.makeUnsafe(0), + type: "tool.called", durable: durable(sessionID, 2), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(3), callID, tool: "bash", input: { command: "pwd" }, @@ -186,12 +186,12 @@ test.skip("tool completion stores completed timestamp", () => { Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.tool.success", + created: DateTime.makeUnsafe(0), + type: "tool.success", durable: durable(sessionID, 3), data: { sessionID, assistantMessageID, - timestamp: DateTime.makeUnsafe(4), callID, structured: {}, content: [{ type: "text", text: "/tmp" }], @@ -212,17 +212,16 @@ test("compaction events reduce to compaction message only when completed", () => const state: SessionMessageUpdater.MemoryState = { messages: [] } const sessionID = SessionID.make("session") const id = EventV2.ID.create() - const compactionID = SessionMessage.ID.create() + const endedID = EventV2.ID.create() Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id, - type: "session.next.compaction.started", + created: DateTime.makeUnsafe(0), + type: "compaction.started", durable: durable(sessionID), data: { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(1), reason: "auto", }, } satisfies SessionEvent.Event), @@ -233,11 +232,10 @@ test("compaction events reduce to compaction message only when completed", () => Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.compaction.delta", + created: DateTime.makeUnsafe(0), + type: "compaction.delta", data: { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(2), text: "hello ", }, } satisfies SessionEvent.Event), @@ -246,11 +244,10 @@ test("compaction events reduce to compaction message only when completed", () => Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { id: EventV2.ID.create(), - type: "session.next.compaction.delta", + created: DateTime.makeUnsafe(0), + type: "compaction.delta", data: { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(3), text: "summary", }, } satisfies SessionEvent.Event), @@ -258,13 +255,12 @@ test("compaction events reduce to compaction message only when completed", () => Effect.runSync( SessionMessageUpdater.update(SessionMessageUpdater.memory(state), { - id: EventV2.ID.create(), - type: "session.next.compaction.ended", + id: endedID, + created: DateTime.makeUnsafe(0), + type: "compaction.ended", durable: durable(sessionID, 1), data: { sessionID, - messageID: compactionID, - timestamp: DateTime.makeUnsafe(4), reason: "auto", text: "final summary", recent: "recent context", @@ -274,11 +270,11 @@ test("compaction events reduce to compaction message only when completed", () => expect(state.messages).toHaveLength(1) expect(state.messages[0]).toMatchObject({ - id: compactionID, + id: SessionMessage.ID.fromEvent(endedID), type: "compaction", reason: "auto", summary: "final summary", recent: "recent context", - time: { created: DateTime.makeUnsafe(4) }, + time: { created: DateTime.makeUnsafe(0) }, }) }) diff --git a/packages/schema/src/event.ts b/packages/schema/src/event.ts index aac2f0315e..9f7c347b03 100644 --- a/packages/schema/src/event.ts +++ b/packages/schema/src/event.ts @@ -4,7 +4,7 @@ import { Schema } from "effect" import { optional } from "./schema.js" import { ascending } from "./identifier.js" import { Location } from "./location.js" -import { statics } from "./schema.js" +import { DateTimeUtcFromMillis, statics } from "./schema.js" export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe( Schema.brand("Event.ID"), @@ -60,6 +60,7 @@ export type Data = Schema.Schema.Type type PayloadBase = { readonly id: ID readonly type: D["type"] + readonly created: typeof DateTimeUtcFromMillis.Type readonly data: Data readonly location?: Location.Ref readonly metadata?: Record @@ -85,6 +86,7 @@ export function durable< const data = Schema.Struct(input.schema) return Schema.Struct({ id: ID, + created: DateTimeUtcFromMillis, metadata: optional(Schema.Record(Schema.String, Schema.Unknown)), type: Schema.Literal(input.type), durable: DurableEnvelope, @@ -109,6 +111,7 @@ export function ephemeral< const data = Schema.Struct(input.schema) return Schema.Struct({ id: ID, + created: DateTimeUtcFromMillis, metadata: optional(Schema.Record(Schema.String, Schema.Unknown)), type: Schema.Literal(input.type), location: optional(Location.Ref), diff --git a/packages/schema/src/session-event.ts b/packages/schema/src/session-event.ts index fe1a698555..fce091545a 100644 --- a/packages/schema/src/session-event.ts +++ b/packages/schema/src/session-event.ts @@ -6,7 +6,7 @@ import { Event } from "./event.js" import { ProviderMetadata, ToolContent } from "./llm.js" import { Delivery } from "./session-delivery.js" import { Model } from "./model.js" -import { DateTimeUtcFromMillis, NonNegativeInt, RelativePath } from "./schema.js" +import { NonNegativeInt, RelativePath } from "./schema.js" import { FileAttachment, Prompt } from "./prompt.js" import { SessionID } from "./session-id.js" import { Location } from "./location.js" @@ -20,17 +20,16 @@ export const Source = Schema.Struct({ end: NonNegativeInt, text: Schema.String, }).annotate({ - identifier: "session.next.event.source", + identifier: "session.event.source", }) export interface Source extends Schema.Schema.Type {} const Base = { - timestamp: DateTimeUtcFromMillis, sessionID: SessionID, } const PromptFields = { ...Base, - messageID: SessionMessage.ID, + inputID: SessionMessage.ID, prompt: Prompt, delivery: Delivery, } @@ -44,48 +43,46 @@ const options = { const stepSettlementOptions = { durable: { aggregate: "sessionID", - version: 2, + version: 1, }, } as const export const UnknownError = SessionMessage.UnknownError export type UnknownError = SessionMessage.UnknownError -export const AgentSwitched = Event.durable({ - type: "session.next.agent.switched", +export const AgentSelected = Event.durable({ + type: "agent.selected", ...options, schema: { ...Base, - messageID: SessionMessage.ID, agent: Schema.String, }, }) -export type AgentSwitched = typeof AgentSwitched.Type +export type AgentSelected = typeof AgentSelected.Type -export const ModelSwitched = Event.durable({ - type: "session.next.model.switched", +export const ModelSelected = Event.durable({ + type: "model.selected", ...options, schema: { ...Base, - messageID: SessionMessage.ID, model: Model.Ref, }, }) -export type ModelSwitched = typeof ModelSwitched.Type +export type ModelSelected = typeof ModelSelected.Type export const Moved = Event.durable({ - type: "session.next.moved", + type: "session.moved", ...options, schema: { ...Base, location: Location.Ref, - subdirectory: RelativePath.pipe(optional), + subpath: RelativePath.pipe(optional), }, }) export type Moved = typeof Moved.Type export const Renamed = Event.durable({ - type: "session.next.renamed", + type: "renamed", ...options, schema: { ...Base, @@ -95,32 +92,35 @@ export const Renamed = Event.durable({ export type Renamed = typeof Renamed.Type export const Forked = Event.durable({ - type: "session.next.forked", + type: "forked", ...options, schema: { ...Base, parentID: SessionID, - messageID: SessionMessage.ID.pipe(optional), + from: SessionMessage.ID.pipe(optional), }, }) export type Forked = typeof Forked.Type -export const Prompted = Event.durable({ - type: "session.next.prompted", +export const PromptPromoted = Event.durable({ + type: "prompt.promoted", ...options, - schema: PromptFields, + schema: { + sessionID: SessionID, + inputID: SessionMessage.ID, + }, }) -export type Prompted = typeof Prompted.Type +export type PromptPromoted = typeof PromptPromoted.Type export const PromptAdmitted = Event.durable({ - type: "session.next.prompt.admitted", + type: "prompt.admitted", ...options, schema: PromptFields, }) export type PromptAdmitted = typeof PromptAdmitted.Type export const ExecutionSettled = Event.ephemeral({ - type: "session.next.execution.settled", + type: "execution.settled", schema: { ...Base, outcome: Schema.Literals(["success", "failure", "interrupted"]), @@ -130,22 +130,20 @@ export const ExecutionSettled = Event.ephemeral({ export type ExecutionSettled = typeof ExecutionSettled.Type export const ContextUpdated = Event.durable({ - type: "session.next.context.updated", + type: "session.context.updated", ...options, schema: { ...Base, - messageID: SessionMessage.ID, text: Schema.String, }, }) export type ContextUpdated = typeof ContextUpdated.Type export const Synthetic = Event.durable({ - type: "session.next.synthetic", + type: "synthetic", ...options, schema: { ...Base, - messageID: SessionMessage.ID, text: Schema.String, description: Schema.String.pipe(optional), metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(optional), @@ -155,11 +153,10 @@ export type Synthetic = typeof Synthetic.Type export namespace Skill { export const Activated = Event.durable({ - type: "session.next.skill.activated", + type: "skill.activated", ...options, schema: { ...Base, - messageID: SessionMessage.ID, name: Schema.String, text: Schema.String, }, @@ -169,11 +166,10 @@ export namespace Skill { export namespace Shell { export const Started = Event.durable({ - type: "session.next.shell.started", + type: "shell.started", ...options, schema: { ...Base, - messageID: SessionMessage.ID, callID: Schema.String, command: Schema.String, }, @@ -181,7 +177,7 @@ export namespace Shell { export type Started = typeof Started.Type export const Ended = Event.durable({ - type: "session.next.shell.ended", + type: "shell.ended", ...options, schema: { ...Base, @@ -194,7 +190,7 @@ export namespace Shell { export namespace Step { export const Started = Event.durable({ - type: "session.next.step.started", + type: "step.started", ...options, schema: { ...Base, @@ -207,7 +203,7 @@ export namespace Step { export type Started = typeof Started.Type export const Ended = Event.durable({ - type: "session.next.step.ended", + type: "step.ended", ...stepSettlementOptions, schema: { ...Base, @@ -230,7 +226,7 @@ export namespace Step { export type Ended = typeof Ended.Type export const Failed = Event.durable({ - type: "session.next.step.failed", + type: "step.failed", ...stepSettlementOptions, schema: { ...Base, @@ -243,7 +239,7 @@ export namespace Step { export namespace Text { export const Started = Event.durable({ - type: "session.next.text.started", + type: "text.started", ...options, schema: { ...Base, @@ -255,7 +251,7 @@ export namespace Text { // Stream fragments are live-only; Text.Ended is the replayable full-value boundary. export const Delta = Event.ephemeral({ - type: "session.next.text.delta", + type: "text.delta", schema: { ...Base, assistantMessageID: SessionMessage.ID, @@ -266,7 +262,7 @@ export namespace Text { export type Delta = typeof Delta.Type export const Ended = Event.durable({ - type: "session.next.text.ended", + type: "text.ended", ...options, schema: { ...Base, @@ -280,7 +276,7 @@ export namespace Text { export namespace Reasoning { export const Started = Event.durable({ - type: "session.next.reasoning.started", + type: "reasoning.started", ...options, schema: { ...Base, @@ -293,7 +289,7 @@ export namespace Reasoning { // Stream fragments are live-only; Reasoning.Ended is the replayable full-value boundary. export const Delta = Event.ephemeral({ - type: "session.next.reasoning.delta", + type: "reasoning.delta", schema: { ...Base, assistantMessageID: SessionMessage.ID, @@ -304,7 +300,7 @@ export namespace Reasoning { export type Delta = typeof Delta.Type export const Ended = Event.durable({ - type: "session.next.reasoning.ended", + type: "reasoning.ended", ...options, schema: { ...Base, @@ -326,7 +322,7 @@ export namespace Tool { export namespace Input { export const Started = Event.durable({ - type: "session.next.tool.input.started", + type: "tool.input.started", ...options, schema: { ...ToolBase, @@ -337,7 +333,7 @@ export namespace Tool { // Stream fragments are live-only; Input.Ended is the replayable raw-input boundary. export const Delta = Event.ephemeral({ - type: "session.next.tool.input.delta", + type: "tool.input.delta", schema: { ...ToolBase, delta: Schema.String, @@ -346,7 +342,7 @@ export namespace Tool { export type Delta = typeof Delta.Type export const Ended = Event.durable({ - type: "session.next.tool.input.ended", + type: "tool.input.ended", ...options, schema: { ...ToolBase, @@ -357,7 +353,7 @@ export namespace Tool { } export const Called = Event.durable({ - type: "session.next.tool.called", + type: "tool.called", ...options, schema: { ...ToolBase, @@ -376,7 +372,7 @@ export namespace Tool { * transitions or at a bounded cadence, not persist every stdout/stderr chunk. */ export const Progress = Event.durable({ - type: "session.next.tool.progress", + type: "tool.progress", ...options, schema: { ...ToolBase, @@ -387,7 +383,7 @@ export namespace Tool { export type Progress = typeof Progress.Type export const Success = Event.durable({ - type: "session.next.tool.success", + type: "tool.success", ...options, schema: { ...ToolBase, @@ -404,7 +400,7 @@ export namespace Tool { export type Success = typeof Success.Type export const Failed = Event.durable({ - type: "session.next.tool.failed", + type: "tool.failed", ...options, schema: { ...ToolBase, @@ -427,12 +423,12 @@ export const RetryError = Schema.Struct({ responseBody: Schema.String.pipe(optional), metadata: Schema.Record(Schema.String, Schema.String).pipe(optional), }).annotate({ - identifier: "session.next.retry_error", + identifier: "session.retry.error", }) export interface RetryError extends Schema.Schema.Type {} export const Retried = Event.durable({ - type: "session.next.retried", + type: "retried", ...options, schema: { ...Base, @@ -444,32 +440,29 @@ export type Retried = typeof Retried.Type export namespace Compaction { export const Started = Event.durable({ - type: "session.next.compaction.started", + type: "compaction.started", ...options, schema: { ...Base, - messageID: SessionMessage.ID, reason: Schema.Union([Schema.Literal("auto"), Schema.Literal("manual")]), }, }) export type Started = typeof Started.Type export const Delta = Event.ephemeral({ - type: "session.next.compaction.delta", + type: "compaction.delta", schema: { ...Base, - messageID: SessionMessage.ID, text: Schema.String, }, }) export type Delta = typeof Delta.Type export const Ended = Event.durable({ - type: "session.next.compaction.ended", + type: "compaction.ended", ...options, schema: { ...Base, - messageID: SessionMessage.ID, reason: Started.data.fields.reason, text: Schema.String, recent: Schema.String, @@ -480,25 +473,25 @@ export namespace Compaction { export namespace RevertEvent { export const Staged = Event.durable({ - type: "session.next.revert.staged", + type: "revert.staged", ...options, schema: { ...Base, revert: Revert.State }, }) - export const Cleared = Event.durable({ type: "session.next.revert.cleared", ...options, schema: Base }) + export const Cleared = Event.durable({ type: "revert.cleared", ...options, schema: Base }) export const Committed = Event.durable({ - type: "session.next.revert.committed", + type: "revert.committed", ...options, schema: { ...Base, messageID: SessionMessage.ID }, }) } export const Definitions = Event.inventory( - AgentSwitched, - ModelSwitched, + AgentSelected, + ModelSelected, Moved, Renamed, Forked, - Prompted, + PromptPromoted, PromptAdmitted, ExecutionSettled, ContextUpdated, diff --git a/packages/schema/src/session-message.ts b/packages/schema/src/session-message.ts index 457cf87206..349d04d06e 100644 --- a/packages/schema/src/session-message.ts +++ b/packages/schema/src/session-message.ts @@ -8,10 +8,14 @@ import { FileAttachment, Prompt } from "./prompt.js" import { DateTimeUtcFromMillis, RelativePath, statics } from "./schema.js" import { SessionID } from "./session-id.js" import { ascending } from "./identifier.js" +import { Event } from "./event.js" export const ID = Schema.String.check(Schema.isStartsWith("msg_")).pipe( Schema.brand("Session.Message.ID"), - statics((schema) => ({ create: () => schema.make("msg_" + ascending()) })), + statics((schema) => ({ + create: () => schema.make("msg_" + ascending()), + fromEvent: (eventID: Event.ID) => schema.make(eventID.replace(/^evt_/, "msg_")), + })), ) export type ID = typeof ID.Type @@ -27,19 +31,19 @@ const Base = { time: Schema.Struct({ created: DateTimeUtcFromMillis }), } -export interface AgentSwitched extends Schema.Schema.Type {} -export const AgentSwitched = Schema.Struct({ +export interface AgentSelected extends Schema.Schema.Type {} +export const AgentSelected = Schema.Struct({ ...Base, type: Schema.Literal("agent-switched"), agent: Schema.String, -}).annotate({ identifier: "Session.Message.AgentSwitched" }) +}).annotate({ identifier: "Session.Message.AgentSelected" }) -export interface ModelSwitched extends Schema.Schema.Type {} -export const ModelSwitched = Schema.Struct({ +export interface ModelSelected extends Schema.Schema.Type {} +export const ModelSelected = Schema.Struct({ ...Base, type: Schema.Literal("model-switched"), model: Model.Ref, -}).annotate({ identifier: "Session.Message.ModelSwitched" }) +}).annotate({ identifier: "Session.Message.ModelSelected" }) export interface User extends Schema.Schema.Type {} export const User = Schema.Struct({ @@ -207,8 +211,8 @@ export const Compaction = Schema.Struct({ }).annotate({ identifier: "Session.Message.Compaction" }) export const Message = Schema.Union([ - AgentSwitched, - ModelSwitched, + AgentSelected, + ModelSelected, User, Synthetic, System, @@ -219,5 +223,5 @@ export const Message = Schema.Union([ ]) .pipe(Schema.toTaggedUnion("type")) .annotate({ identifier: "Session.Message" }) -export type Message = AgentSwitched | ModelSwitched | User | Synthetic | System | Skill | Shell | Assistant | Compaction +export type Message = AgentSelected | ModelSelected | User | Synthetic | System | Skill | Shell | Assistant | Compaction export type Type = Message["type"] diff --git a/packages/schema/test/event-manifest.test.ts b/packages/schema/test/event-manifest.test.ts index 4e0c6eaa18..9cdf3a787a 100644 --- a/packages/schema/test/event-manifest.test.ts +++ b/packages/schema/test/event-manifest.test.ts @@ -52,7 +52,7 @@ describe("public event manifest", () => { expect(Session.Event.Definitions).toBe(SessionEvent.Definitions) expect(Workspace.Event).toBe(WorkspaceEvent) expect(Workspace.Event.Definitions).toBe(WorkspaceEvent.Definitions) - expect(EventManifest.Latest.get("session.next.step.ended")).toBe(SessionEvent.Step.Ended) + expect(EventManifest.Latest.get("step.ended")).toBe(SessionEvent.Step.Ended) expect(EventManifest.Latest.get("todo.updated")).toBe(SessionTodo.Event.Updated) expect(EventManifest.Latest.get("agent.updated")).toBe(Agent.Event.Updated) expect(EventManifest.Latest.get("project.updated")).toBe(Project.Event.Updated) @@ -71,8 +71,8 @@ describe("public event manifest", () => { SessionV1.Event.Diff, SessionV1.Event.Error, ]) - expect(EventManifest.Durable.has("session.next.step.ended.1")).toBe(false) - expect(EventManifest.Durable.get("session.next.step.ended.2")).toBe(SessionEvent.Step.Ended) + expect(EventManifest.Durable.get("step.ended.1")).toBe(SessionEvent.Step.Ended) + expect(EventManifest.Durable.has("step.ended.2")).toBe(false) }) test("derives durable definitions from explicit definition durability", () => { @@ -85,37 +85,37 @@ describe("public event manifest", () => { "message.removed.1", "message.part.updated.1", "message.part.removed.1", - "session.next.agent.switched.1", - "session.next.model.switched.1", - "session.next.moved.1", - "session.next.renamed.1", - "session.next.forked.1", - "session.next.prompted.1", - "session.next.prompt.admitted.1", - "session.next.context.updated.1", - "session.next.synthetic.1", - "session.next.skill.activated.1", - "session.next.shell.started.1", - "session.next.shell.ended.1", - "session.next.step.started.1", - "session.next.step.ended.2", - "session.next.step.failed.2", - "session.next.text.started.1", - "session.next.text.ended.1", - "session.next.tool.input.started.1", - "session.next.tool.input.ended.1", - "session.next.tool.called.1", - "session.next.tool.progress.1", - "session.next.tool.success.1", - "session.next.tool.failed.1", - "session.next.reasoning.started.1", - "session.next.reasoning.ended.1", - "session.next.retried.1", - "session.next.compaction.started.1", - "session.next.compaction.ended.1", - "session.next.revert.staged.1", - "session.next.revert.cleared.1", - "session.next.revert.committed.1", + "agent.selected.1", + "model.selected.1", + "session.moved.1", + "renamed.1", + "forked.1", + "prompt.promoted.1", + "prompt.admitted.1", + "session.context.updated.1", + "synthetic.1", + "skill.activated.1", + "shell.started.1", + "shell.ended.1", + "step.started.1", + "step.ended.1", + "step.failed.1", + "text.started.1", + "text.ended.1", + "tool.input.started.1", + "tool.input.ended.1", + "tool.called.1", + "tool.progress.1", + "tool.success.1", + "tool.failed.1", + "reasoning.started.1", + "reasoning.ended.1", + "retried.1", + "compaction.started.1", + "compaction.ended.1", + "revert.staged.1", + "revert.cleared.1", + "revert.committed.1", ].toSorted(), ) expect(SessionEvent.DurableDefinitions).toEqual( diff --git a/packages/sdk-next/test/embedded.test.ts b/packages/sdk-next/test/embedded.test.ts index 81f3d480ad..5419b7e838 100644 --- a/packages/sdk-next/test/embedded.test.ts +++ b/packages/sdk-next/test/embedded.test.ts @@ -78,7 +78,7 @@ it.live( prompt: fixture.sdk.Prompt.make({ text: "Promote this input" }), }) const prompted = yield* opencode.sessions.log({ sessionID: id, follow: true }).pipe( - Stream.filter((event) => event.type === "session.next.prompted" && event.data.messageID === wake.id), + Stream.filter((event) => event.type === "prompt.promoted" && event.data.inputID === wake.id), Stream.runHead, Effect.timeout("10 seconds"), Effect.map(Option.getOrThrow), @@ -119,7 +119,7 @@ it.live( expect(page.data.some((session) => session.id === id)).toBe(true) expect(active).toEqual({ data: {}, watermarks: {} }) expect(admitted.sessionID).toBe(id) - expect(prompted.type).toBe("session.next.prompted") + expect(prompted.type).toBe("prompt.promoted") expect(wakeContext).toContainEqual(expect.objectContaining({ id: wake.id, type: "user" })) expect(contextEntries).toEqual([ { key: "deploy-target", value: "production" }, @@ -127,7 +127,7 @@ it.live( ]) expect(remainingContextEntries).toEqual([{ key: "deploy-target", value: "production" }]) expect(context.some((message) => message.type === "model-switched")).toBe(true) - expect(event).toMatchObject({ type: "session.next.model.switched", durable: { seq: 1 } }) + expect(event).toMatchObject({ type: "model.selected", durable: { seq: 1 } }) expect(message).toEqual(modelMessage) expect(missing.map((error) => error._tag)).toEqual([ "SessionNotFoundError", @@ -149,13 +149,13 @@ it.live( const opencode = yield* fixture.sdk.OpenCode.create() const id = sessionID(fixture) const connected = yield* Latch.make(false) - const prompted = yield* Deferred.make>() + const prompted = yield* Deferred.make>() yield* opencode.events.subscribe().pipe( Stream.runForEach((event) => event.type === "server.connected" ? connected.open - : event.type === "session.next.prompted" && event.data.sessionID === id + : event.type === "prompt.promoted" && event.data.sessionID === id ? Deferred.succeed(prompted, event).pipe(Effect.asVoid) : Effect.void, ), @@ -191,7 +191,7 @@ it.live( Stream.runForEach((notification: OpenCodeEvent) => notification.type === "server.connected" ? ready.open - : notification.type === "session.next.agent.switched" && notification.data.sessionID === id + : notification.type === "agent.selected" && notification.data.sessionID === id ? event.open : Effect.void, ) diff --git a/packages/sdk/js/script/build.ts b/packages/sdk/js/script/build.ts index 8fc0f363e7..d87f8bf98a 100755 --- a/packages/sdk/js/script/build.ts +++ b/packages/sdk/js/script/build.ts @@ -59,7 +59,13 @@ if (schemas) { } visit({ ...document, components: { ...document.components, schemas: undefined } }) for (const name of Object.keys(schemas)) { - if (/^SessionNext\w+1$/.test(name) && !reachable.has(name)) delete schemas[name] + if ( + /^(AgentSelected|ModelSelected|SessionMoved|Renamed|Forked|PromptPromoted|PromptAdmitted|ExecutionSettled|ContextUpdated|Synthetic|SkillActivated|ShellStarted|ShellEnded|StepStarted|StepEnded|StepFailed|TextStarted|TextDelta|TextEnded|ReasoningStarted|ReasoningDelta|ReasoningEnded|ToolInputStarted|ToolInputDelta|ToolInputEnded|ToolCalled|ToolProgress|ToolSuccess|ToolFailed|Retried|CompactionStarted|CompactionDelta|CompactionEnded|RevertStaged|RevertCleared|RevertCommitted)1$/.test( + name, + ) && + !reachable.has(name) + ) + delete schemas[name] } await Bun.write("./openapi.json", JSON.stringify(document)) } @@ -93,7 +99,11 @@ await createClient({ const generatedTypesPath = "./src/v2/gen/types.gen.ts" const generatedTypes = await Bun.file(generatedTypesPath).text() -if (/export type SessionNext\w+1 =/.test(generatedTypes)) { +if ( + /export type (AgentSelected|ModelSelected|SessionMoved|Renamed|Forked|PromptPromoted|PromptAdmitted|ExecutionSettled|ContextUpdated|Synthetic|SkillActivated|ShellStarted|ShellEnded|StepStarted|StepEnded|StepFailed|TextStarted|TextDelta|TextEnded|ReasoningStarted|ReasoningDelta|ReasoningEnded|ToolInputStarted|ToolInputDelta|ToolInputEnded|ToolCalled|ToolProgress|ToolSuccess|ToolFailed|Retried|CompactionStarted|CompactionDelta|CompactionEnded|RevertStaged|RevertCleared|RevertCommitted)1 =/.test( + generatedTypes, + ) +) { throw new Error("Session history generated duplicate Session event variants") } const logTypesPatched = generatedTypes.replace( diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index a1ba35e5ae..d97eb59298 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -17,42 +17,42 @@ export type Event = | EventMessageRemoved | EventMessagePartUpdated | EventMessagePartRemoved - | EventSessionNextAgentSwitched - | EventSessionNextModelSwitched - | EventSessionNextMoved - | EventSessionNextRenamed - | EventSessionNextForked - | EventSessionNextPrompted - | EventSessionNextPromptAdmitted - | EventSessionNextExecutionSettled - | EventSessionNextContextUpdated - | EventSessionNextSynthetic - | EventSessionNextSkillActivated - | EventSessionNextShellStarted - | EventSessionNextShellEnded - | EventSessionNextStepStarted - | EventSessionNextStepEnded - | EventSessionNextStepFailed - | EventSessionNextTextStarted - | EventSessionNextTextDelta - | EventSessionNextTextEnded - | EventSessionNextReasoningStarted - | EventSessionNextReasoningDelta - | EventSessionNextReasoningEnded - | EventSessionNextToolInputStarted - | EventSessionNextToolInputDelta - | EventSessionNextToolInputEnded - | EventSessionNextToolCalled - | EventSessionNextToolProgress - | EventSessionNextToolSuccess - | EventSessionNextToolFailed - | EventSessionNextRetried - | EventSessionNextCompactionStarted - | EventSessionNextCompactionDelta - | EventSessionNextCompactionEnded - | EventSessionNextRevertStaged - | EventSessionNextRevertCleared - | EventSessionNextRevertCommitted + | EventAgentSelected + | EventModelSelected + | EventSessionMoved + | EventRenamed + | EventForked + | EventPromptPromoted + | EventPromptAdmitted + | EventExecutionSettled + | EventSessionContextUpdated + | EventSynthetic + | EventSkillActivated + | EventShellStarted + | EventShellEnded + | EventStepStarted + | EventStepEnded + | EventStepFailed + | EventTextStarted + | EventTextDelta + | EventTextEnded + | EventReasoningStarted + | EventReasoningDelta + | EventReasoningEnded + | EventToolInputStarted + | EventToolInputDelta + | EventToolInputEnded + | EventToolCalled + | EventToolProgress + | EventToolSuccess + | EventToolFailed + | EventRetried + | EventCompactionStarted + | EventCompactionDelta + | EventCompactionEnded + | EventRevertStaged + | EventRevertCleared + | EventRevertCommitted | EventMessagePartDelta | EventSessionDiff | EventSessionError @@ -859,80 +859,68 @@ export type GlobalEvent = { } | { id: string - type: "session.next.agent.switched" + type: "agent.selected" properties: { - timestamp: number sessionID: string - messageID: string agent: string } } | { id: string - type: "session.next.model.switched" + type: "model.selected" properties: { - timestamp: number sessionID: string - messageID: string model: ModelRef } } | { id: string - type: "session.next.moved" + type: "session.moved" properties: { - timestamp: number sessionID: string location: LocationRef - subdirectory?: string + subpath?: string } } | { id: string - type: "session.next.renamed" + type: "renamed" properties: { - timestamp: number sessionID: string title: string } } | { id: string - type: "session.next.forked" + type: "forked" properties: { - timestamp: number sessionID: string parentID: string - messageID?: string + from?: string } } | { id: string - type: "session.next.prompted" + type: "prompt.promoted" properties: { - timestamp: number sessionID: string - messageID: string + inputID: string + } + } + | { + id: string + type: "prompt.admitted" + properties: { + sessionID: string + inputID: string prompt: Prompt delivery: "steer" | "queue" } } | { id: string - type: "session.next.prompt.admitted" + type: "execution.settled" properties: { - timestamp: number - sessionID: string - messageID: string - prompt: Prompt - delivery: "steer" | "queue" - } - } - | { - id: string - type: "session.next.execution.settled" - properties: { - timestamp: number sessionID: string outcome: "success" | "failure" | "interrupted" error?: SessionErrorUnknown @@ -940,21 +928,17 @@ export type GlobalEvent = { } | { id: string - type: "session.next.context.updated" + type: "session.context.updated" properties: { - timestamp: number sessionID: string - messageID: string text: string } } | { id: string - type: "session.next.synthetic" + type: "synthetic" properties: { - timestamp: number sessionID: string - messageID: string text: string description?: string metadata?: { @@ -964,31 +948,26 @@ export type GlobalEvent = { } | { id: string - type: "session.next.skill.activated" + type: "skill.activated" properties: { - timestamp: number sessionID: string - messageID: string name: string text: string } } | { id: string - type: "session.next.shell.started" + type: "shell.started" properties: { - timestamp: number sessionID: string - messageID: string callID: string command: string } } | { id: string - type: "session.next.shell.ended" + type: "shell.ended" properties: { - timestamp: number sessionID: string callID: string output: string @@ -996,9 +975,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.step.started" + type: "step.started" properties: { - timestamp: number sessionID: string assistantMessageID: string agent: string @@ -1008,9 +986,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.step.ended" + type: "step.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string finish: string @@ -1030,9 +1007,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.step.failed" + type: "step.failed" properties: { - timestamp: number sessionID: string assistantMessageID: string error: SessionErrorUnknown @@ -1040,9 +1016,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.text.started" + type: "text.started" properties: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -1050,9 +1025,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.text.delta" + type: "text.delta" properties: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -1061,9 +1035,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.text.ended" + type: "text.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -1072,9 +1045,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.reasoning.started" + type: "reasoning.started" properties: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -1083,9 +1055,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.reasoning.delta" + type: "reasoning.delta" properties: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -1094,9 +1065,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.reasoning.ended" + type: "reasoning.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -1106,9 +1076,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.tool.input.started" + type: "tool.input.started" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -1117,9 +1086,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.tool.input.delta" + type: "tool.input.delta" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -1128,9 +1096,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.tool.input.ended" + type: "tool.input.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -1139,9 +1106,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.tool.called" + type: "tool.called" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -1157,9 +1123,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.tool.progress" + type: "tool.progress" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -1171,9 +1136,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.tool.success" + type: "tool.success" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -1191,9 +1155,8 @@ export type GlobalEvent = { } | { id: string - type: "session.next.tool.failed" + type: "tool.failed" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -1207,41 +1170,34 @@ export type GlobalEvent = { } | { id: string - type: "session.next.retried" + type: "retried" properties: { - timestamp: number sessionID: string attempt: number - error: SessionNextRetryError + error: SessionRetryError } } | { id: string - type: "session.next.compaction.started" + type: "compaction.started" properties: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" } } | { id: string - type: "session.next.compaction.delta" + type: "compaction.delta" properties: { - timestamp: number sessionID: string - messageID: string text: string } } | { id: string - type: "session.next.compaction.ended" + type: "compaction.ended" properties: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" text: string recent: string @@ -1249,26 +1205,23 @@ export type GlobalEvent = { } | { id: string - type: "session.next.revert.staged" + type: "revert.staged" properties: { - timestamp: number sessionID: string revert: RevertState } } | { id: string - type: "session.next.revert.cleared" + type: "revert.cleared" properties: { - timestamp: number sessionID: string } } | { id: string - type: "session.next.revert.committed" + type: "revert.committed" properties: { - timestamp: number sessionID: string messageID: string } @@ -1760,37 +1713,37 @@ export type GlobalEvent = { | SyncEventMessageRemoved | SyncEventMessagePartUpdated | SyncEventMessagePartRemoved - | SyncEventSessionNextAgentSwitched - | SyncEventSessionNextModelSwitched - | SyncEventSessionNextMoved - | SyncEventSessionNextRenamed - | SyncEventSessionNextForked - | SyncEventSessionNextPrompted - | SyncEventSessionNextPromptAdmitted - | SyncEventSessionNextContextUpdated - | SyncEventSessionNextSynthetic - | SyncEventSessionNextSkillActivated - | SyncEventSessionNextShellStarted - | SyncEventSessionNextShellEnded - | SyncEventSessionNextStepStarted - | SyncEventSessionNextStepEnded - | SyncEventSessionNextStepFailed - | SyncEventSessionNextTextStarted - | SyncEventSessionNextTextEnded - | SyncEventSessionNextReasoningStarted - | SyncEventSessionNextReasoningEnded - | SyncEventSessionNextToolInputStarted - | SyncEventSessionNextToolInputEnded - | SyncEventSessionNextToolCalled - | SyncEventSessionNextToolProgress - | SyncEventSessionNextToolSuccess - | SyncEventSessionNextToolFailed - | SyncEventSessionNextRetried - | SyncEventSessionNextCompactionStarted - | SyncEventSessionNextCompactionEnded - | SyncEventSessionNextRevertStaged - | SyncEventSessionNextRevertCleared - | SyncEventSessionNextRevertCommitted + | SyncEventAgentSelected + | SyncEventModelSelected + | SyncEventSessionMoved + | SyncEventRenamed + | SyncEventForked + | SyncEventPromptPromoted + | SyncEventPromptAdmitted + | SyncEventSessionContextUpdated + | SyncEventSynthetic + | SyncEventSkillActivated + | SyncEventShellStarted + | SyncEventShellEnded + | SyncEventStepStarted + | SyncEventStepEnded + | SyncEventStepFailed + | SyncEventTextStarted + | SyncEventTextEnded + | SyncEventReasoningStarted + | SyncEventReasoningEnded + | SyncEventToolInputStarted + | SyncEventToolInputEnded + | SyncEventToolCalled + | SyncEventToolProgress + | SyncEventToolSuccess + | SyncEventToolFailed + | SyncEventRetried + | SyncEventCompactionStarted + | SyncEventCompactionEnded + | SyncEventRevertStaged + | SyncEventRevertCleared + | SyncEventRevertCommitted } /** @@ -2910,38 +2863,120 @@ export type UnknownError1 = { ref?: string } +export type Renamed = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "renamed" + durable: { + aggregateID: string + seq: number + version: number + } + location?: LocationRef + data: { + sessionID: string + title: string + } +} + +export type Forked = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "forked" + durable: { + aggregateID: string + seq: number + version: number + } + location?: LocationRef + data: { + sessionID: string + parentID: string + from?: string + } +} + +export type Synthetic = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "synthetic" + durable: { + aggregateID: string + seq: number + version: number + } + location?: LocationRef + data: { + sessionID: string + text: string + description?: string + metadata?: { + [key: string]: unknown + } + } +} + +export type Retried = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "retried" + durable: { + aggregateID: string + seq: number + version: number + } + location?: LocationRef + data: { + sessionID: string + attempt: number + error: SessionRetryError + } +} + export type SessionDurableEvent = - | SessionNextAgentSwitched - | SessionNextModelSwitched - | SessionNextMoved - | SessionNextRenamed - | SessionNextForked - | SessionNextPrompted - | SessionNextPromptAdmitted - | SessionNextContextUpdated - | SessionNextSynthetic - | SessionNextSkillActivated - | SessionNextShellStarted - | SessionNextShellEnded - | SessionNextStepStarted - | SessionNextStepEnded - | SessionNextStepFailed - | SessionNextTextStarted - | SessionNextTextEnded - | SessionNextReasoningStarted - | SessionNextReasoningEnded - | SessionNextToolInputStarted - | SessionNextToolInputEnded - | SessionNextToolCalled - | SessionNextToolProgress - | SessionNextToolSuccess - | SessionNextToolFailed - | SessionNextRetried - | SessionNextCompactionStarted - | SessionNextCompactionEnded - | SessionNextRevertStaged - | SessionNextRevertCleared - | SessionNextRevertCommitted + | AgentSelected + | ModelSelected + | SessionMoved + | Renamed + | Forked + | PromptPromoted + | PromptAdmitted + | SessionContextUpdated + | Synthetic + | SkillActivated + | ShellStarted + | ShellEnded + | StepStarted + | StepEnded + | StepFailed + | TextStarted + | TextEnded + | ReasoningStarted + | ReasoningEnded + | ToolInputStarted + | ToolInputEnded + | ToolCalled + | ToolProgress + | ToolSuccess + | ToolFailed + | Retried + | CompactionStarted + | CompactionEnded + | RevertStaged + | RevertCleared + | RevertCommitted export type SessionLogItem = SessionDurableEvent | EventLogSynced @@ -3016,6 +3051,7 @@ export type Shell1 = { export type SessionStatus2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -3029,6 +3065,7 @@ export type SessionStatus2 = { export type QuestionReplied2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -3043,6 +3080,7 @@ export type QuestionReplied2 = { export type QuestionRejected2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -3067,42 +3105,42 @@ export type V2Event = | MessageRemoved | MessagePartUpdated | MessagePartRemoved - | SessionNextAgentSwitched - | SessionNextModelSwitched - | SessionNextMoved - | SessionNextRenamed - | SessionNextForked - | SessionNextPrompted - | SessionNextPromptAdmitted - | SessionNextExecutionSettled - | SessionNextContextUpdated - | SessionNextSynthetic - | SessionNextSkillActivated - | SessionNextShellStarted - | SessionNextShellEnded - | SessionNextStepStarted - | SessionNextStepEnded - | SessionNextStepFailed - | SessionNextTextStarted - | SessionNextTextDelta - | SessionNextTextEnded - | SessionNextReasoningStarted - | SessionNextReasoningDelta - | SessionNextReasoningEnded - | SessionNextToolInputStarted - | SessionNextToolInputDelta - | SessionNextToolInputEnded - | SessionNextToolCalled - | SessionNextToolProgress - | SessionNextToolSuccess - | SessionNextToolFailed - | SessionNextRetried - | SessionNextCompactionStarted - | SessionNextCompactionDelta - | SessionNextCompactionEnded - | SessionNextRevertStaged - | SessionNextRevertCleared - | SessionNextRevertCommitted + | AgentSelected + | ModelSelected + | SessionMoved + | Renamed + | Forked + | PromptPromoted + | PromptAdmitted + | ExecutionSettled + | SessionContextUpdated + | Synthetic + | SkillActivated + | ShellStarted + | ShellEnded + | StepStarted + | StepEnded + | StepFailed + | TextStarted + | TextDelta + | TextEnded + | ReasoningStarted + | ReasoningDelta + | ReasoningEnded + | ToolInputStarted + | ToolInputDelta + | ToolInputEnded + | ToolCalled + | ToolProgress + | ToolSuccess + | ToolFailed + | Retried + | CompactionStarted + | CompactionDelta + | CompactionEnded + | RevertStaged + | RevertCleared + | RevertCommitted | MessagePartDelta | SessionDiff | SessionError @@ -3338,7 +3376,7 @@ export type ToolFileContent = { export type LlmToolContent = ToolTextContent | ToolFileContent -export type SessionNextRetryError = { +export type SessionRetryError = { message: string statusCode?: number isRetryable: boolean @@ -3661,155 +3699,140 @@ export type SyncEventMessagePartRemoved = { } } -export type SyncEventSessionNextAgentSwitched = { +export type SyncEventAgentSelected = { type: "sync" id: string syncEvent: { - type: "session.next.agent.switched.1" + type: "agent.selected.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string agent: string } } } -export type SyncEventSessionNextModelSwitched = { +export type SyncEventModelSelected = { type: "sync" id: string syncEvent: { - type: "session.next.model.switched.1" + type: "model.selected.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string model: ModelRef } } } -export type SyncEventSessionNextMoved = { +export type SyncEventSessionMoved = { type: "sync" id: string syncEvent: { - type: "session.next.moved.1" + type: "session.moved.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string location: LocationRef - subdirectory?: string + subpath?: string } } } -export type SyncEventSessionNextRenamed = { +export type SyncEventRenamed = { type: "sync" id: string syncEvent: { - type: "session.next.renamed.1" + type: "renamed.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string title: string } } } -export type SyncEventSessionNextForked = { +export type SyncEventForked = { type: "sync" id: string syncEvent: { - type: "session.next.forked.1" + type: "forked.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string parentID: string - messageID?: string + from?: string } } } -export type SyncEventSessionNextPrompted = { +export type SyncEventPromptPromoted = { type: "sync" id: string syncEvent: { - type: "session.next.prompted.1" + type: "prompt.promoted.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string + inputID: string + } + } +} + +export type SyncEventPromptAdmitted = { + type: "sync" + id: string + syncEvent: { + type: "prompt.admitted.1" + id: string + seq: number + aggregateID: string + data: { + sessionID: string + inputID: string prompt: Prompt delivery: "steer" | "queue" } } } -export type SyncEventSessionNextPromptAdmitted = { +export type SyncEventSessionContextUpdated = { type: "sync" id: string syncEvent: { - type: "session.next.prompt.admitted.1" + type: "session.context.updated.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string - prompt: Prompt - delivery: "steer" | "queue" - } - } -} - -export type SyncEventSessionNextContextUpdated = { - type: "sync" - id: string - syncEvent: { - type: "session.next.context.updated.1" - id: string - seq: number - aggregateID: string - data: { - timestamp: number - sessionID: string - messageID: string text: string } } } -export type SyncEventSessionNextSynthetic = { +export type SyncEventSynthetic = { type: "sync" id: string syncEvent: { - type: "session.next.synthetic.1" + type: "synthetic.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string text: string description?: string metadata?: { @@ -3819,52 +3842,47 @@ export type SyncEventSessionNextSynthetic = { } } -export type SyncEventSessionNextSkillActivated = { +export type SyncEventSkillActivated = { type: "sync" id: string syncEvent: { - type: "session.next.skill.activated.1" + type: "skill.activated.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string name: string text: string } } } -export type SyncEventSessionNextShellStarted = { +export type SyncEventShellStarted = { type: "sync" id: string syncEvent: { - type: "session.next.shell.started.1" + type: "shell.started.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string callID: string command: string } } } -export type SyncEventSessionNextShellEnded = { +export type SyncEventShellEnded = { type: "sync" id: string syncEvent: { - type: "session.next.shell.ended.1" + type: "shell.ended.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string callID: string output: string @@ -3872,16 +3890,15 @@ export type SyncEventSessionNextShellEnded = { } } -export type SyncEventSessionNextStepStarted = { +export type SyncEventStepStarted = { type: "sync" id: string syncEvent: { - type: "session.next.step.started.1" + type: "step.started.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string agent: string @@ -3891,16 +3908,15 @@ export type SyncEventSessionNextStepStarted = { } } -export type SyncEventSessionNextStepEnded = { +export type SyncEventStepEnded = { type: "sync" id: string syncEvent: { - type: "session.next.step.ended.2" + type: "step.ended.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string finish: string @@ -3920,16 +3936,15 @@ export type SyncEventSessionNextStepEnded = { } } -export type SyncEventSessionNextStepFailed = { +export type SyncEventStepFailed = { type: "sync" id: string syncEvent: { - type: "session.next.step.failed.2" + type: "step.failed.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string error: SessionErrorUnknown @@ -3937,16 +3952,15 @@ export type SyncEventSessionNextStepFailed = { } } -export type SyncEventSessionNextTextStarted = { +export type SyncEventTextStarted = { type: "sync" id: string syncEvent: { - type: "session.next.text.started.1" + type: "text.started.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -3954,16 +3968,15 @@ export type SyncEventSessionNextTextStarted = { } } -export type SyncEventSessionNextTextEnded = { +export type SyncEventTextEnded = { type: "sync" id: string syncEvent: { - type: "session.next.text.ended.1" + type: "text.ended.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -3972,16 +3985,15 @@ export type SyncEventSessionNextTextEnded = { } } -export type SyncEventSessionNextReasoningStarted = { +export type SyncEventReasoningStarted = { type: "sync" id: string syncEvent: { - type: "session.next.reasoning.started.1" + type: "reasoning.started.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -3990,16 +4002,15 @@ export type SyncEventSessionNextReasoningStarted = { } } -export type SyncEventSessionNextReasoningEnded = { +export type SyncEventReasoningEnded = { type: "sync" id: string syncEvent: { - type: "session.next.reasoning.ended.1" + type: "reasoning.ended.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -4009,16 +4020,15 @@ export type SyncEventSessionNextReasoningEnded = { } } -export type SyncEventSessionNextToolInputStarted = { +export type SyncEventToolInputStarted = { type: "sync" id: string syncEvent: { - type: "session.next.tool.input.started.1" + type: "tool.input.started.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -4027,16 +4037,15 @@ export type SyncEventSessionNextToolInputStarted = { } } -export type SyncEventSessionNextToolInputEnded = { +export type SyncEventToolInputEnded = { type: "sync" id: string syncEvent: { - type: "session.next.tool.input.ended.1" + type: "tool.input.ended.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -4045,16 +4054,15 @@ export type SyncEventSessionNextToolInputEnded = { } } -export type SyncEventSessionNextToolCalled = { +export type SyncEventToolCalled = { type: "sync" id: string syncEvent: { - type: "session.next.tool.called.1" + type: "tool.called.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -4070,16 +4078,15 @@ export type SyncEventSessionNextToolCalled = { } } -export type SyncEventSessionNextToolProgress = { +export type SyncEventToolProgress = { type: "sync" id: string syncEvent: { - type: "session.next.tool.progress.1" + type: "tool.progress.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -4091,16 +4098,15 @@ export type SyncEventSessionNextToolProgress = { } } -export type SyncEventSessionNextToolSuccess = { +export type SyncEventToolSuccess = { type: "sync" id: string syncEvent: { - type: "session.next.tool.success.1" + type: "tool.success.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -4118,16 +4124,15 @@ export type SyncEventSessionNextToolSuccess = { } } -export type SyncEventSessionNextToolFailed = { +export type SyncEventToolFailed = { type: "sync" id: string syncEvent: { - type: "session.next.tool.failed.1" + type: "tool.failed.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -4141,52 +4146,47 @@ export type SyncEventSessionNextToolFailed = { } } -export type SyncEventSessionNextRetried = { +export type SyncEventRetried = { type: "sync" id: string syncEvent: { - type: "session.next.retried.1" + type: "retried.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string attempt: number - error: SessionNextRetryError + error: SessionRetryError } } } -export type SyncEventSessionNextCompactionStarted = { +export type SyncEventCompactionStarted = { type: "sync" id: string syncEvent: { - type: "session.next.compaction.started.1" + type: "compaction.started.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" } } } -export type SyncEventSessionNextCompactionEnded = { +export type SyncEventCompactionEnded = { type: "sync" id: string syncEvent: { - type: "session.next.compaction.ended.1" + type: "compaction.ended.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" text: string recent: string @@ -4194,47 +4194,44 @@ export type SyncEventSessionNextCompactionEnded = { } } -export type SyncEventSessionNextRevertStaged = { +export type SyncEventRevertStaged = { type: "sync" id: string syncEvent: { - type: "session.next.revert.staged.1" + type: "revert.staged.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string revert: RevertState } } } -export type SyncEventSessionNextRevertCleared = { +export type SyncEventRevertCleared = { type: "sync" id: string syncEvent: { - type: "session.next.revert.cleared.1" + type: "revert.cleared.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string } } } -export type SyncEventSessionNextRevertCommitted = { +export type SyncEventRevertCommitted = { type: "sync" id: string syncEvent: { - type: "session.next.revert.committed.1" + type: "revert.committed.1" id: string seq: number aggregateID: string data: { - timestamp: number sessionID: string messageID: string } @@ -4375,7 +4372,7 @@ export type SessionInputAdmitted = { promotedSeq?: number } -export type SessionMessageAgentSwitched = { +export type SessionMessageAgentSelected = { id: string metadata?: { [key: string]: unknown @@ -4387,7 +4384,7 @@ export type SessionMessageAgentSwitched = { agent: string } -export type SessionMessageModelSwitched = { +export type SessionMessageModelSelected = { id: string metadata?: { [key: string]: unknown @@ -4596,8 +4593,8 @@ export type SessionMessageCompaction = { } export type SessionMessage = - | SessionMessageAgentSwitched - | SessionMessageModelSwitched + | SessionMessageAgentSelected + | SessionMessageModelSelected | SessionMessageUser | SessionMessageSynthetic | SessionMessageSystem @@ -4613,12 +4610,13 @@ export type SessionContextEntryInfo = { value: unknown } -export type SessionNextAgentSwitched = { +export type AgentSelected = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.agent.switched" + type: "agent.selected" durable: { aggregateID: string seq: number @@ -4626,19 +4624,18 @@ export type SessionNextAgentSwitched = { } location?: LocationRef data: { - timestamp: number sessionID: string - messageID: string agent: string } } -export type SessionNextModelSwitched = { +export type ModelSelected = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.model.switched" + type: "model.selected" durable: { aggregateID: string seq: number @@ -4646,19 +4643,18 @@ export type SessionNextModelSwitched = { } location?: LocationRef data: { - timestamp: number sessionID: string - messageID: string model: ModelRef } } -export type SessionNextMoved = { +export type SessionMoved = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.moved" + type: "session.moved" durable: { aggregateID: string seq: number @@ -4666,19 +4662,19 @@ export type SessionNextMoved = { } location?: LocationRef data: { - timestamp: number sessionID: string location: LocationRef - subdirectory?: string + subpath?: string } } -export type SessionNextRenamed = { +export type PromptPromoted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.renamed" + type: "prompt.promoted" durable: { aggregateID: string seq: number @@ -4686,18 +4682,18 @@ export type SessionNextRenamed = { } location?: LocationRef data: { - timestamp: number sessionID: string - title: string + inputID: string } } -export type SessionNextForked = { +export type PromptAdmitted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.forked" + type: "prompt.admitted" durable: { aggregateID: string seq: number @@ -4705,40 +4701,20 @@ export type SessionNextForked = { } location?: LocationRef data: { - timestamp: number sessionID: string - parentID: string - messageID?: string - } -} - -export type SessionNextPrompted = { - id: string - metadata?: { - [key: string]: unknown - } - type: "session.next.prompted" - durable: { - aggregateID: string - seq: number - version: number - } - location?: LocationRef - data: { - timestamp: number - sessionID: string - messageID: string + inputID: string prompt: Prompt delivery: "steer" | "queue" } } -export type SessionNextPromptAdmitted = { +export type SessionContextUpdated = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.prompt.admitted" + type: "session.context.updated" durable: { aggregateID: string seq: number @@ -4746,40 +4722,18 @@ export type SessionNextPromptAdmitted = { } location?: LocationRef data: { - timestamp: number sessionID: string - messageID: string - prompt: Prompt - delivery: "steer" | "queue" - } -} - -export type SessionNextContextUpdated = { - id: string - metadata?: { - [key: string]: unknown - } - type: "session.next.context.updated" - durable: { - aggregateID: string - seq: number - version: number - } - location?: LocationRef - data: { - timestamp: number - sessionID: string - messageID: string text: string } } -export type SessionNextSynthetic = { +export type SkillActivated = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.synthetic" + type: "skill.activated" durable: { aggregateID: string seq: number @@ -4787,44 +4741,19 @@ export type SessionNextSynthetic = { } location?: LocationRef data: { - timestamp: number sessionID: string - messageID: string - text: string - description?: string - metadata?: { - [key: string]: unknown - } - } -} - -export type SessionNextSkillActivated = { - id: string - metadata?: { - [key: string]: unknown - } - type: "session.next.skill.activated" - durable: { - aggregateID: string - seq: number - version: number - } - location?: LocationRef - data: { - timestamp: number - sessionID: string - messageID: string name: string text: string } } -export type SessionNextShellStarted = { +export type ShellStarted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.shell.started" + type: "shell.started" durable: { aggregateID: string seq: number @@ -4832,20 +4761,19 @@ export type SessionNextShellStarted = { } location?: LocationRef data: { - timestamp: number sessionID: string - messageID: string callID: string command: string } } -export type SessionNextShellEnded = { +export type ShellEnded = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.shell.ended" + type: "shell.ended" durable: { aggregateID: string seq: number @@ -4853,19 +4781,19 @@ export type SessionNextShellEnded = { } location?: LocationRef data: { - timestamp: number sessionID: string callID: string output: string } } -export type SessionNextStepStarted = { +export type StepStarted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.step.started" + type: "step.started" durable: { aggregateID: string seq: number @@ -4873,7 +4801,6 @@ export type SessionNextStepStarted = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string agent: string @@ -4882,12 +4809,13 @@ export type SessionNextStepStarted = { } } -export type SessionNextStepEnded = { +export type StepEnded = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.step.ended" + type: "step.ended" durable: { aggregateID: string seq: number @@ -4895,7 +4823,6 @@ export type SessionNextStepEnded = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string finish: string @@ -4914,12 +4841,13 @@ export type SessionNextStepEnded = { } } -export type SessionNextStepFailed = { +export type StepFailed = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.step.failed" + type: "step.failed" durable: { aggregateID: string seq: number @@ -4927,19 +4855,19 @@ export type SessionNextStepFailed = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string error: SessionErrorUnknown } } -export type SessionNextTextStarted = { +export type TextStarted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.text.started" + type: "text.started" durable: { aggregateID: string seq: number @@ -4947,19 +4875,19 @@ export type SessionNextTextStarted = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string textID: string } } -export type SessionNextTextEnded = { +export type TextEnded = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.text.ended" + type: "text.ended" durable: { aggregateID: string seq: number @@ -4967,7 +4895,6 @@ export type SessionNextTextEnded = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -4975,12 +4902,13 @@ export type SessionNextTextEnded = { } } -export type SessionNextReasoningStarted = { +export type ReasoningStarted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.reasoning.started" + type: "reasoning.started" durable: { aggregateID: string seq: number @@ -4988,7 +4916,6 @@ export type SessionNextReasoningStarted = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -4996,12 +4923,13 @@ export type SessionNextReasoningStarted = { } } -export type SessionNextReasoningEnded = { +export type ReasoningEnded = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.reasoning.ended" + type: "reasoning.ended" durable: { aggregateID: string seq: number @@ -5009,7 +4937,6 @@ export type SessionNextReasoningEnded = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -5018,12 +4945,13 @@ export type SessionNextReasoningEnded = { } } -export type SessionNextToolInputStarted = { +export type ToolInputStarted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.input.started" + type: "tool.input.started" durable: { aggregateID: string seq: number @@ -5031,7 +4959,6 @@ export type SessionNextToolInputStarted = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -5039,12 +4966,13 @@ export type SessionNextToolInputStarted = { } } -export type SessionNextToolInputEnded = { +export type ToolInputEnded = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.input.ended" + type: "tool.input.ended" durable: { aggregateID: string seq: number @@ -5052,7 +4980,6 @@ export type SessionNextToolInputEnded = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -5060,12 +4987,13 @@ export type SessionNextToolInputEnded = { } } -export type SessionNextToolCalled = { +export type ToolCalled = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.called" + type: "tool.called" durable: { aggregateID: string seq: number @@ -5073,7 +5001,6 @@ export type SessionNextToolCalled = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -5088,12 +5015,13 @@ export type SessionNextToolCalled = { } } -export type SessionNextToolProgress = { +export type ToolProgress = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.progress" + type: "tool.progress" durable: { aggregateID: string seq: number @@ -5101,7 +5029,6 @@ export type SessionNextToolProgress = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -5112,12 +5039,13 @@ export type SessionNextToolProgress = { } } -export type SessionNextToolSuccess = { +export type ToolSuccess = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.success" + type: "tool.success" durable: { aggregateID: string seq: number @@ -5125,7 +5053,6 @@ export type SessionNextToolSuccess = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -5142,12 +5069,13 @@ export type SessionNextToolSuccess = { } } -export type SessionNextToolFailed = { +export type ToolFailed = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.failed" + type: "tool.failed" durable: { aggregateID: string seq: number @@ -5155,7 +5083,6 @@ export type SessionNextToolFailed = { } location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -5168,12 +5095,13 @@ export type SessionNextToolFailed = { } } -export type SessionNextRetried = { +export type CompactionStarted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.retried" + type: "compaction.started" durable: { aggregateID: string seq: number @@ -5181,39 +5109,18 @@ export type SessionNextRetried = { } location?: LocationRef data: { - timestamp: number sessionID: string - attempt: number - error: SessionNextRetryError - } -} - -export type SessionNextCompactionStarted = { - id: string - metadata?: { - [key: string]: unknown - } - type: "session.next.compaction.started" - durable: { - aggregateID: string - seq: number - version: number - } - location?: LocationRef - data: { - timestamp: number - sessionID: string - messageID: string reason: "auto" | "manual" } } -export type SessionNextCompactionEnded = { +export type CompactionEnded = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.compaction.ended" + type: "compaction.ended" durable: { aggregateID: string seq: number @@ -5221,21 +5128,20 @@ export type SessionNextCompactionEnded = { } location?: LocationRef data: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" text: string recent: string } } -export type SessionNextRevertStaged = { +export type RevertStaged = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.revert.staged" + type: "revert.staged" durable: { aggregateID: string seq: number @@ -5243,18 +5149,18 @@ export type SessionNextRevertStaged = { } location?: LocationRef data: { - timestamp: number sessionID: string revert: RevertState } } -export type SessionNextRevertCleared = { +export type RevertCleared = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.revert.cleared" + type: "revert.cleared" durable: { aggregateID: string seq: number @@ -5262,17 +5168,17 @@ export type SessionNextRevertCleared = { } location?: LocationRef data: { - timestamp: number sessionID: string } } -export type SessionNextRevertCommitted = { +export type RevertCommitted = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.revert.committed" + type: "revert.committed" durable: { aggregateID: string seq: number @@ -5280,7 +5186,6 @@ export type SessionNextRevertCommitted = { } location?: LocationRef data: { - timestamp: number sessionID: string messageID: string } @@ -5615,6 +5520,7 @@ export type SkillV2Info = { export type ModelsDevRefreshed = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5627,6 +5533,7 @@ export type ModelsDevRefreshed = { export type IntegrationUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5639,6 +5546,7 @@ export type IntegrationUpdated = { export type IntegrationConnectionUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5651,6 +5559,7 @@ export type IntegrationConnectionUpdated = { export type CatalogUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5663,6 +5572,7 @@ export type CatalogUpdated = { export type AgentUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5675,6 +5585,7 @@ export type AgentUpdated = { export type SessionCreated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5693,6 +5604,7 @@ export type SessionCreated = { export type SessionUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5711,6 +5623,7 @@ export type SessionUpdated = { export type SessionDeleted = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5729,6 +5642,7 @@ export type SessionDeleted = { export type MessageUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5747,6 +5661,7 @@ export type MessageUpdated = { export type MessageRemoved = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5765,6 +5680,7 @@ export type MessageRemoved = { export type MessagePartUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5784,6 +5700,7 @@ export type MessagePartUpdated = { export type MessagePartRemoved = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5801,30 +5718,30 @@ export type MessagePartRemoved = { } } -export type SessionNextExecutionSettled = { +export type ExecutionSettled = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.execution.settled" + type: "execution.settled" location?: LocationRef data: { - timestamp: number sessionID: string outcome: "success" | "failure" | "interrupted" error?: SessionErrorUnknown } } -export type SessionNextTextDelta = { +export type TextDelta = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.text.delta" + type: "text.delta" location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -5832,15 +5749,15 @@ export type SessionNextTextDelta = { } } -export type SessionNextReasoningDelta = { +export type ReasoningDelta = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.reasoning.delta" + type: "reasoning.delta" location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -5848,15 +5765,15 @@ export type SessionNextReasoningDelta = { } } -export type SessionNextToolInputDelta = { +export type ToolInputDelta = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.input.delta" + type: "tool.input.delta" location?: LocationRef data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -5864,23 +5781,23 @@ export type SessionNextToolInputDelta = { } } -export type SessionNextCompactionDelta = { +export type CompactionDelta = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.compaction.delta" + type: "compaction.delta" location?: LocationRef data: { - timestamp: number sessionID: string - messageID: string text: string } } export type MessagePartDelta = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5897,6 +5814,7 @@ export type MessagePartDelta = { export type SessionDiff = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5910,6 +5828,7 @@ export type SessionDiff = { export type SessionError = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5931,6 +5850,7 @@ export type SessionError = { export type InstallationUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5943,6 +5863,7 @@ export type InstallationUpdated = { export type InstallationUpdateAvailable = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5955,6 +5876,7 @@ export type InstallationUpdateAvailable = { export type FileEdited = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5967,6 +5889,7 @@ export type FileEdited = { export type ReferenceUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5979,6 +5902,7 @@ export type ReferenceUpdated = { export type PermissionV2Asked = { id: string + created: number metadata?: { [key: string]: unknown } @@ -5999,6 +5923,7 @@ export type PermissionV2Asked = { export type PermissionV2Replied = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6013,6 +5938,7 @@ export type PermissionV2Replied = { export type PluginAdded = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6025,6 +5951,7 @@ export type PluginAdded = { export type ProjectDirectoriesUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6037,6 +5964,7 @@ export type ProjectDirectoriesUpdated = { export type CommandUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6049,6 +5977,7 @@ export type CommandUpdated = { export type SkillUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6061,6 +5990,7 @@ export type SkillUpdated = { export type FileWatcherUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6074,6 +6004,7 @@ export type FileWatcherUpdated = { export type PtyCreated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6086,6 +6017,7 @@ export type PtyCreated = { export type PtyUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6098,6 +6030,7 @@ export type PtyUpdated = { export type PtyExited = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6111,6 +6044,7 @@ export type PtyExited = { export type PtyDeleted = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6123,6 +6057,7 @@ export type PtyDeleted = { export type ShellCreated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6135,6 +6070,7 @@ export type ShellCreated = { export type ShellExited = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6149,6 +6085,7 @@ export type ShellExited = { export type ShellDeleted = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6161,6 +6098,7 @@ export type ShellDeleted = { export type QuestionV2Asked = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6179,6 +6117,7 @@ export type QuestionV2Asked = { export type QuestionV2Replied = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6193,6 +6132,7 @@ export type QuestionV2Replied = { export type QuestionV2Rejected = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6236,6 +6176,7 @@ export type FormIntegerField1 = { export type FormCreated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6250,6 +6191,7 @@ export type FormValue1 = string | number | "NaN" | "Infinity" | "-Infinity" | bo export type FormReplied = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6264,6 +6206,7 @@ export type FormReplied = { export type FormCancelled = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6277,6 +6220,7 @@ export type FormCancelled = { export type TodoUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6290,6 +6234,7 @@ export type TodoUpdated = { export type LspUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6302,6 +6247,7 @@ export type LspUpdated = { export type PermissionAsked = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6325,6 +6271,7 @@ export type PermissionAsked = { export type PermissionReplied = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6339,6 +6286,7 @@ export type PermissionReplied = { export type TuiPromptAppend = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6351,6 +6299,7 @@ export type TuiPromptAppend = { export type TuiCommandExecute = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6381,6 +6330,7 @@ export type TuiCommandExecute = { export type TuiToastShow = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6396,6 +6346,7 @@ export type TuiToastShow = { export type TuiSessionSelect = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6411,6 +6362,7 @@ export type TuiSessionSelect = { export type McpToolsChanged = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6423,6 +6375,7 @@ export type McpToolsChanged = { export type McpBrowserOpenFailed = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6436,6 +6389,7 @@ export type McpBrowserOpenFailed = { export type McpStatusChanged = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6448,6 +6402,7 @@ export type McpStatusChanged = { export type CommandExecuted = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6463,6 +6418,7 @@ export type CommandExecuted = { export type ProjectUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6482,6 +6438,7 @@ export type ProjectUpdated = { export type SessionIdle = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6494,6 +6451,7 @@ export type SessionIdle = { export type QuestionAsked = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6512,6 +6470,7 @@ export type QuestionAsked = { export type SessionCompacted = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6524,6 +6483,7 @@ export type SessionCompacted = { export type VcsBranchUpdated = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6536,6 +6496,7 @@ export type VcsBranchUpdated = { export type WorkspaceReady = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6548,6 +6509,7 @@ export type WorkspaceReady = { export type WorkspaceFailed = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6560,6 +6522,7 @@ export type WorkspaceFailed = { export type WorkspaceStatus = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6573,6 +6536,7 @@ export type WorkspaceStatus = { export type WorktreeReady = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6586,6 +6550,7 @@ export type WorktreeReady = { export type WorktreeFailed = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6598,6 +6563,7 @@ export type WorktreeFailed = { export type ServerConnected = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6610,6 +6576,7 @@ export type ServerConnected = { export type GlobalDisposed = { id: string + created: number metadata?: { [key: string]: unknown } @@ -6787,113 +6754,97 @@ export type EventMessagePartRemoved = { } } -export type EventSessionNextAgentSwitched = { +export type EventAgentSelected = { id: string - type: "session.next.agent.switched" + type: "agent.selected" properties: { - timestamp: number sessionID: string - messageID: string agent: string } } -export type EventSessionNextModelSwitched = { +export type EventModelSelected = { id: string - type: "session.next.model.switched" + type: "model.selected" properties: { - timestamp: number sessionID: string - messageID: string model: ModelRef } } -export type EventSessionNextMoved = { +export type EventSessionMoved = { id: string - type: "session.next.moved" + type: "session.moved" properties: { - timestamp: number sessionID: string location: LocationRef - subdirectory?: string + subpath?: string } } -export type EventSessionNextRenamed = { +export type EventRenamed = { id: string - type: "session.next.renamed" + type: "renamed" properties: { - timestamp: number sessionID: string title: string } } -export type EventSessionNextForked = { +export type EventForked = { id: string - type: "session.next.forked" + type: "forked" properties: { - timestamp: number sessionID: string parentID: string - messageID?: string + from?: string } } -export type EventSessionNextPrompted = { +export type EventPromptPromoted = { id: string - type: "session.next.prompted" + type: "prompt.promoted" properties: { - timestamp: number sessionID: string - messageID: string + inputID: string + } +} + +export type EventPromptAdmitted = { + id: string + type: "prompt.admitted" + properties: { + sessionID: string + inputID: string prompt: Prompt delivery: "steer" | "queue" } } -export type EventSessionNextPromptAdmitted = { +export type EventExecutionSettled = { id: string - type: "session.next.prompt.admitted" + type: "execution.settled" properties: { - timestamp: number - sessionID: string - messageID: string - prompt: Prompt - delivery: "steer" | "queue" - } -} - -export type EventSessionNextExecutionSettled = { - id: string - type: "session.next.execution.settled" - properties: { - timestamp: number sessionID: string outcome: "success" | "failure" | "interrupted" error?: SessionErrorUnknown } } -export type EventSessionNextContextUpdated = { +export type EventSessionContextUpdated = { id: string - type: "session.next.context.updated" + type: "session.context.updated" properties: { - timestamp: number sessionID: string - messageID: string text: string } } -export type EventSessionNextSynthetic = { +export type EventSynthetic = { id: string - type: "session.next.synthetic" + type: "synthetic" properties: { - timestamp: number sessionID: string - messageID: string text: string description?: string metadata?: { @@ -6902,46 +6853,40 @@ export type EventSessionNextSynthetic = { } } -export type EventSessionNextSkillActivated = { +export type EventSkillActivated = { id: string - type: "session.next.skill.activated" + type: "skill.activated" properties: { - timestamp: number sessionID: string - messageID: string name: string text: string } } -export type EventSessionNextShellStarted = { +export type EventShellStarted = { id: string - type: "session.next.shell.started" + type: "shell.started" properties: { - timestamp: number sessionID: string - messageID: string callID: string command: string } } -export type EventSessionNextShellEnded = { +export type EventShellEnded = { id: string - type: "session.next.shell.ended" + type: "shell.ended" properties: { - timestamp: number sessionID: string callID: string output: string } } -export type EventSessionNextStepStarted = { +export type EventStepStarted = { id: string - type: "session.next.step.started" + type: "step.started" properties: { - timestamp: number sessionID: string assistantMessageID: string agent: string @@ -6950,11 +6895,10 @@ export type EventSessionNextStepStarted = { } } -export type EventSessionNextStepEnded = { +export type EventStepEnded = { id: string - type: "session.next.step.ended" + type: "step.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string finish: string @@ -6973,33 +6917,30 @@ export type EventSessionNextStepEnded = { } } -export type EventSessionNextStepFailed = { +export type EventStepFailed = { id: string - type: "session.next.step.failed" + type: "step.failed" properties: { - timestamp: number sessionID: string assistantMessageID: string error: SessionErrorUnknown } } -export type EventSessionNextTextStarted = { +export type EventTextStarted = { id: string - type: "session.next.text.started" + type: "text.started" properties: { - timestamp: number sessionID: string assistantMessageID: string textID: string } } -export type EventSessionNextTextDelta = { +export type EventTextDelta = { id: string - type: "session.next.text.delta" + type: "text.delta" properties: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -7007,11 +6948,10 @@ export type EventSessionNextTextDelta = { } } -export type EventSessionNextTextEnded = { +export type EventTextEnded = { id: string - type: "session.next.text.ended" + type: "text.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -7019,11 +6959,10 @@ export type EventSessionNextTextEnded = { } } -export type EventSessionNextReasoningStarted = { +export type EventReasoningStarted = { id: string - type: "session.next.reasoning.started" + type: "reasoning.started" properties: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -7031,11 +6970,10 @@ export type EventSessionNextReasoningStarted = { } } -export type EventSessionNextReasoningDelta = { +export type EventReasoningDelta = { id: string - type: "session.next.reasoning.delta" + type: "reasoning.delta" properties: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -7043,11 +6981,10 @@ export type EventSessionNextReasoningDelta = { } } -export type EventSessionNextReasoningEnded = { +export type EventReasoningEnded = { id: string - type: "session.next.reasoning.ended" + type: "reasoning.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -7056,11 +6993,10 @@ export type EventSessionNextReasoningEnded = { } } -export type EventSessionNextToolInputStarted = { +export type EventToolInputStarted = { id: string - type: "session.next.tool.input.started" + type: "tool.input.started" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -7068,11 +7004,10 @@ export type EventSessionNextToolInputStarted = { } } -export type EventSessionNextToolInputDelta = { +export type EventToolInputDelta = { id: string - type: "session.next.tool.input.delta" + type: "tool.input.delta" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -7080,11 +7015,10 @@ export type EventSessionNextToolInputDelta = { } } -export type EventSessionNextToolInputEnded = { +export type EventToolInputEnded = { id: string - type: "session.next.tool.input.ended" + type: "tool.input.ended" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -7092,11 +7026,10 @@ export type EventSessionNextToolInputEnded = { } } -export type EventSessionNextToolCalled = { +export type EventToolCalled = { id: string - type: "session.next.tool.called" + type: "tool.called" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -7111,11 +7044,10 @@ export type EventSessionNextToolCalled = { } } -export type EventSessionNextToolProgress = { +export type EventToolProgress = { id: string - type: "session.next.tool.progress" + type: "tool.progress" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -7126,11 +7058,10 @@ export type EventSessionNextToolProgress = { } } -export type EventSessionNextToolSuccess = { +export type EventToolSuccess = { id: string - type: "session.next.tool.success" + type: "tool.success" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -7147,11 +7078,10 @@ export type EventSessionNextToolSuccess = { } } -export type EventSessionNextToolFailed = { +export type EventToolFailed = { id: string - type: "session.next.tool.failed" + type: "tool.failed" properties: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -7164,76 +7094,66 @@ export type EventSessionNextToolFailed = { } } -export type EventSessionNextRetried = { +export type EventRetried = { id: string - type: "session.next.retried" + type: "retried" properties: { - timestamp: number sessionID: string attempt: number - error: SessionNextRetryError + error: SessionRetryError } } -export type EventSessionNextCompactionStarted = { +export type EventCompactionStarted = { id: string - type: "session.next.compaction.started" + type: "compaction.started" properties: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" } } -export type EventSessionNextCompactionDelta = { +export type EventCompactionDelta = { id: string - type: "session.next.compaction.delta" + type: "compaction.delta" properties: { - timestamp: number sessionID: string - messageID: string text: string } } -export type EventSessionNextCompactionEnded = { +export type EventCompactionEnded = { id: string - type: "session.next.compaction.ended" + type: "compaction.ended" properties: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" text: string recent: string } } -export type EventSessionNextRevertStaged = { +export type EventRevertStaged = { id: string - type: "session.next.revert.staged" + type: "revert.staged" properties: { - timestamp: number sessionID: string revert: RevertState } } -export type EventSessionNextRevertCleared = { +export type EventRevertCleared = { id: string - type: "session.next.revert.cleared" + type: "revert.cleared" properties: { - timestamp: number sessionID: string } } -export type EventSessionNextRevertCommitted = { +export type EventRevertCommitted = { id: string - type: "session.next.revert.committed" + type: "revert.committed" properties: { - timestamp: number sessionID: string messageID: string } @@ -8035,7 +7955,7 @@ export type UnknownErrorV2 = { ref?: string | null } -export type SessionMessageAgentSwitched2 = { +export type SessionMessageAgentSelected2 = { id: string metadata?: { [key: string]: unknown @@ -8047,7 +7967,7 @@ export type SessionMessageAgentSwitched2 = { agent: string } -export type SessionMessageModelSwitched2 = { +export type SessionMessageModelSelected2 = { id: string metadata?: { [key: string]: unknown @@ -8281,8 +8201,8 @@ export type SessionMessageCompaction2 = { } export type SessionMessage2 = - | SessionMessageAgentSwitched2 - | SessionMessageModelSwitched2 + | SessionMessageAgentSelected2 + | SessionMessageModelSelected2 | SessionMessageUser2 | SessionMessageSynthetic2 | SessionMessageSystem2 @@ -8301,12 +8221,13 @@ export type SessionContextEntryInfo2 = { value: unknown } -export type SessionNextAgentSwitched2 = { +export type AgentSelected2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.agent.switched" + type: "agent.selected" durable: { aggregateID: string seq: number @@ -8314,19 +8235,18 @@ export type SessionNextAgentSwitched2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string agent: string } } -export type SessionNextModelSwitched2 = { +export type ModelSelected2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.model.switched" + type: "model.selected" durable: { aggregateID: string seq: number @@ -8334,19 +8254,18 @@ export type SessionNextModelSwitched2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string model: ModelRef2 } } -export type SessionNextMoved2 = { +export type SessionMoved2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.moved" + type: "session.moved" durable: { aggregateID: string seq: number @@ -8354,19 +8273,19 @@ export type SessionNextMoved2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string location: LocationRef2 - subdirectory?: string + subpath?: string } } -export type SessionNextRenamed2 = { +export type RenamedV2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.renamed" + type: "renamed" durable: { aggregateID: string seq: number @@ -8374,18 +8293,18 @@ export type SessionNextRenamed2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string title: string } } -export type SessionNextForked2 = { +export type ForkedV2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.forked" + type: "forked" durable: { aggregateID: string seq: number @@ -8393,19 +8312,19 @@ export type SessionNextForked2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string parentID: string - messageID?: string + from?: string } } -export type SessionNextPrompted2 = { +export type PromptPromoted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.prompted" + type: "prompt.promoted" durable: { aggregateID: string seq: number @@ -8413,20 +8332,39 @@ export type SessionNextPrompted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string + inputID: string + } +} + +export type PromptAdmitted2 = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "prompt.admitted" + durable: { + aggregateID: string + seq: number + version: number + } + location?: LocationRef2 + data: { + sessionID: string + inputID: string prompt: PromptV2 delivery: "steer" | "queue" } } -export type SessionNextPromptAdmitted2 = { +export type SessionContextUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.prompt.admitted" + type: "session.context.updated" durable: { aggregateID: string seq: number @@ -8434,40 +8372,18 @@ export type SessionNextPromptAdmitted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string - prompt: PromptV2 - delivery: "steer" | "queue" - } -} - -export type SessionNextContextUpdated2 = { - id: string - metadata?: { - [key: string]: unknown - } - type: "session.next.context.updated" - durable: { - aggregateID: string - seq: number - version: number - } - location?: LocationRef2 - data: { - timestamp: number - sessionID: string - messageID: string text: string } } -export type SessionNextSynthetic2 = { +export type SyntheticV2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.synthetic" + type: "synthetic" durable: { aggregateID: string seq: number @@ -8475,9 +8391,7 @@ export type SessionNextSynthetic2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string text: string description?: string metadata?: { @@ -8486,12 +8400,13 @@ export type SessionNextSynthetic2 = { } } -export type SessionNextSkillActivated2 = { +export type SkillActivated2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.skill.activated" + type: "skill.activated" durable: { aggregateID: string seq: number @@ -8499,20 +8414,19 @@ export type SessionNextSkillActivated2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string name: string text: string } } -export type SessionNextShellStarted2 = { +export type ShellStarted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.shell.started" + type: "shell.started" durable: { aggregateID: string seq: number @@ -8520,20 +8434,19 @@ export type SessionNextShellStarted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string callID: string command: string } } -export type SessionNextShellEnded2 = { +export type ShellEnded2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.shell.ended" + type: "shell.ended" durable: { aggregateID: string seq: number @@ -8541,19 +8454,19 @@ export type SessionNextShellEnded2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string callID: string output: string } } -export type SessionNextStepStarted2 = { +export type StepStarted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.step.started" + type: "step.started" durable: { aggregateID: string seq: number @@ -8561,7 +8474,6 @@ export type SessionNextStepStarted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string agent: string @@ -8570,12 +8482,13 @@ export type SessionNextStepStarted2 = { } } -export type SessionNextStepEnded2 = { +export type StepEnded2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.step.ended" + type: "step.ended" durable: { aggregateID: string seq: number @@ -8583,7 +8496,6 @@ export type SessionNextStepEnded2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string finish: string @@ -8602,12 +8514,13 @@ export type SessionNextStepEnded2 = { } } -export type SessionNextStepFailed2 = { +export type StepFailed2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.step.failed" + type: "step.failed" durable: { aggregateID: string seq: number @@ -8615,19 +8528,19 @@ export type SessionNextStepFailed2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string error: SessionErrorUnknown2 } } -export type SessionNextTextStarted2 = { +export type TextStarted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.text.started" + type: "text.started" durable: { aggregateID: string seq: number @@ -8635,19 +8548,19 @@ export type SessionNextTextStarted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string textID: string } } -export type SessionNextTextEnded2 = { +export type TextEnded2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.text.ended" + type: "text.ended" durable: { aggregateID: string seq: number @@ -8655,7 +8568,6 @@ export type SessionNextTextEnded2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -8669,12 +8581,13 @@ export type LlmProviderMetadata3 = { } } -export type SessionNextReasoningStarted2 = { +export type ReasoningStarted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.reasoning.started" + type: "reasoning.started" durable: { aggregateID: string seq: number @@ -8682,7 +8595,6 @@ export type SessionNextReasoningStarted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -8696,12 +8608,13 @@ export type LlmProviderMetadata4 = { } } -export type SessionNextReasoningEnded2 = { +export type ReasoningEnded2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.reasoning.ended" + type: "reasoning.ended" durable: { aggregateID: string seq: number @@ -8709,7 +8622,6 @@ export type SessionNextReasoningEnded2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -8718,12 +8630,13 @@ export type SessionNextReasoningEnded2 = { } } -export type SessionNextToolInputStarted2 = { +export type ToolInputStarted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.input.started" + type: "tool.input.started" durable: { aggregateID: string seq: number @@ -8731,7 +8644,6 @@ export type SessionNextToolInputStarted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -8739,12 +8651,13 @@ export type SessionNextToolInputStarted2 = { } } -export type SessionNextToolInputEnded2 = { +export type ToolInputEnded2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.input.ended" + type: "tool.input.ended" durable: { aggregateID: string seq: number @@ -8752,7 +8665,6 @@ export type SessionNextToolInputEnded2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -8766,12 +8678,13 @@ export type LlmProviderMetadata5 = { } } -export type SessionNextToolCalled2 = { +export type ToolCalled2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.called" + type: "tool.called" durable: { aggregateID: string seq: number @@ -8779,7 +8692,6 @@ export type SessionNextToolCalled2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -8794,12 +8706,13 @@ export type SessionNextToolCalled2 = { } } -export type SessionNextToolProgress2 = { +export type ToolProgress2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.progress" + type: "tool.progress" durable: { aggregateID: string seq: number @@ -8807,7 +8720,6 @@ export type SessionNextToolProgress2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -8824,12 +8736,13 @@ export type LlmProviderMetadata6 = { } } -export type SessionNextToolSuccess2 = { +export type ToolSuccess2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.success" + type: "tool.success" durable: { aggregateID: string seq: number @@ -8837,7 +8750,6 @@ export type SessionNextToolSuccess2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -8860,12 +8772,13 @@ export type LlmProviderMetadata7 = { } } -export type SessionNextToolFailed2 = { +export type ToolFailed2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.failed" + type: "tool.failed" durable: { aggregateID: string seq: number @@ -8873,7 +8786,6 @@ export type SessionNextToolFailed2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -8886,7 +8798,7 @@ export type SessionNextToolFailed2 = { } } -export type SessionNextRetryError2 = { +export type SessionRetryError2 = { message: string statusCode?: number isRetryable: boolean @@ -8899,12 +8811,13 @@ export type SessionNextRetryError2 = { } } -export type SessionNextRetried2 = { +export type RetriedV2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.retried" + type: "retried" durable: { aggregateID: string seq: number @@ -8912,19 +8825,19 @@ export type SessionNextRetried2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string attempt: number - error: SessionNextRetryError2 + error: SessionRetryError2 } } -export type SessionNextCompactionStarted2 = { +export type CompactionStarted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.compaction.started" + type: "compaction.started" durable: { aggregateID: string seq: number @@ -8932,19 +8845,18 @@ export type SessionNextCompactionStarted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" } } -export type SessionNextCompactionEnded2 = { +export type CompactionEnded2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.compaction.ended" + type: "compaction.ended" durable: { aggregateID: string seq: number @@ -8952,21 +8864,20 @@ export type SessionNextCompactionEnded2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string reason: "auto" | "manual" text: string recent: string } } -export type SessionNextRevertStaged2 = { +export type RevertStaged2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.revert.staged" + type: "revert.staged" durable: { aggregateID: string seq: number @@ -8974,18 +8885,18 @@ export type SessionNextRevertStaged2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string revert: RevertState2 } } -export type SessionNextRevertCleared2 = { +export type RevertCleared2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.revert.cleared" + type: "revert.cleared" durable: { aggregateID: string seq: number @@ -8993,17 +8904,17 @@ export type SessionNextRevertCleared2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string } } -export type SessionNextRevertCommitted2 = { +export type RevertCommitted2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.revert.committed" + type: "revert.committed" durable: { aggregateID: string seq: number @@ -9011,44 +8922,43 @@ export type SessionNextRevertCommitted2 = { } location?: LocationRef2 data: { - timestamp: number sessionID: string messageID: string } } export type SessionDurableEventV2 = - | SessionNextAgentSwitched2 - | SessionNextModelSwitched2 - | SessionNextMoved2 - | SessionNextRenamed2 - | SessionNextForked2 - | SessionNextPrompted2 - | SessionNextPromptAdmitted2 - | SessionNextContextUpdated2 - | SessionNextSynthetic2 - | SessionNextSkillActivated2 - | SessionNextShellStarted2 - | SessionNextShellEnded2 - | SessionNextStepStarted2 - | SessionNextStepEnded2 - | SessionNextStepFailed2 - | SessionNextTextStarted2 - | SessionNextTextEnded2 - | SessionNextReasoningStarted2 - | SessionNextReasoningEnded2 - | SessionNextToolInputStarted2 - | SessionNextToolInputEnded2 - | SessionNextToolCalled2 - | SessionNextToolProgress2 - | SessionNextToolSuccess2 - | SessionNextToolFailed2 - | SessionNextRetried2 - | SessionNextCompactionStarted2 - | SessionNextCompactionEnded2 - | SessionNextRevertStaged2 - | SessionNextRevertCleared2 - | SessionNextRevertCommitted2 + | AgentSelected2 + | ModelSelected2 + | SessionMoved2 + | RenamedV2 + | ForkedV2 + | PromptPromoted2 + | PromptAdmitted2 + | SessionContextUpdated2 + | SyntheticV2 + | SkillActivated2 + | ShellStarted2 + | ShellEnded2 + | StepStarted2 + | StepEnded2 + | StepFailed2 + | TextStarted2 + | TextEnded2 + | ReasoningStarted2 + | ReasoningEnded2 + | ToolInputStarted2 + | ToolInputEnded2 + | ToolCalled2 + | ToolProgress2 + | ToolSuccess2 + | ToolFailed2 + | RetriedV2 + | CompactionStarted2 + | CompactionEnded2 + | RevertStaged2 + | RevertCleared2 + | RevertCommitted2 /** * Marker emitted once when a log read reaches its captured watermark. The reader holds every event committed at or below seq. @@ -9565,6 +9475,7 @@ export type SkillV2Info2 = { export type ModelsDevRefreshed2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9579,6 +9490,7 @@ export type ModelsDevRefreshed2 = { export type IntegrationUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9593,6 +9505,7 @@ export type IntegrationUpdated2 = { export type IntegrationConnectionUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9605,6 +9518,7 @@ export type IntegrationConnectionUpdated2 = { export type CatalogUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9619,6 +9533,7 @@ export type CatalogUpdated2 = { export type AgentUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9704,6 +9619,7 @@ export type SessionV2 = { export type SessionCreated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9722,6 +9638,7 @@ export type SessionCreated2 = { export type SessionUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9740,6 +9657,7 @@ export type SessionUpdated2 = { export type SessionDeleted2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9914,6 +9832,7 @@ export type MessageV2 = UserMessageV2 | AssistantMessageV2 export type MessageUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -9932,6 +9851,7 @@ export type MessageUpdated2 = { export type MessageRemoved2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10213,6 +10133,7 @@ export type PartV2 = export type MessagePartUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10232,6 +10153,7 @@ export type MessagePartUpdated2 = { export type MessagePartRemoved2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10249,30 +10171,30 @@ export type MessagePartRemoved2 = { } } -export type SessionNextExecutionSettled2 = { +export type ExecutionSettled2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.execution.settled" + type: "execution.settled" location?: LocationRef2 data: { - timestamp: number sessionID: string outcome: "success" | "failure" | "interrupted" error?: SessionErrorUnknown2 } } -export type SessionNextTextDelta2 = { +export type TextDelta2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.text.delta" + type: "text.delta" location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string textID: string @@ -10280,15 +10202,15 @@ export type SessionNextTextDelta2 = { } } -export type SessionNextReasoningDelta2 = { +export type ReasoningDelta2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.reasoning.delta" + type: "reasoning.delta" location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string reasoningID: string @@ -10296,15 +10218,15 @@ export type SessionNextReasoningDelta2 = { } } -export type SessionNextToolInputDelta2 = { +export type ToolInputDelta2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.tool.input.delta" + type: "tool.input.delta" location?: LocationRef2 data: { - timestamp: number sessionID: string assistantMessageID: string callID: string @@ -10312,23 +10234,23 @@ export type SessionNextToolInputDelta2 = { } } -export type SessionNextCompactionDelta2 = { +export type CompactionDelta2 = { id: string + created: number metadata?: { [key: string]: unknown } - type: "session.next.compaction.delta" + type: "compaction.delta" location?: LocationRef2 data: { - timestamp: number sessionID: string - messageID: string text: string } } export type FileEdited2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10341,6 +10263,7 @@ export type FileEdited2 = { export type ReferenceUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10355,6 +10278,7 @@ export type ReferenceUpdated2 = { export type PermissionV2Asked2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10375,6 +10299,7 @@ export type PermissionV2Asked2 = { export type PermissionV2Replied2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10389,6 +10314,7 @@ export type PermissionV2Replied2 = { export type PluginAdded2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10401,6 +10327,7 @@ export type PluginAdded2 = { export type ProjectDirectoriesUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10413,6 +10340,7 @@ export type ProjectDirectoriesUpdated2 = { export type CommandUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10427,6 +10355,7 @@ export type CommandUpdated2 = { export type SkillUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10441,6 +10370,7 @@ export type SkillUpdated2 = { export type FileWatcherUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10465,6 +10395,7 @@ export type PtyV2 = { export type PtyCreated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10477,6 +10408,7 @@ export type PtyCreated2 = { export type PtyUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10489,6 +10421,7 @@ export type PtyUpdated2 = { export type PtyExited2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10502,6 +10435,7 @@ export type PtyExited2 = { export type PtyDeleted2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10532,6 +10466,7 @@ export type ShellV2 = { export type ShellCreated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10544,6 +10479,7 @@ export type ShellCreated2 = { export type ShellExited2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10558,6 +10494,7 @@ export type ShellExited2 = { export type ShellDeleted2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10603,6 +10540,7 @@ export type QuestionV2Tool2 = { export type QuestionV2Asked2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10623,6 +10561,7 @@ export type QuestionV2Answer2 = Array export type QuestionV2Replied2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10637,6 +10576,7 @@ export type QuestionV2Replied2 = { export type QuestionV2Rejected2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10743,6 +10683,7 @@ export type FormUrlInfo1 = { export type FormCreated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10761,6 +10702,7 @@ export type FormAnswer1 = { export type FormReplied2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10775,6 +10717,7 @@ export type FormReplied2 = { export type FormCancelled2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10803,6 +10746,7 @@ export type TodoV2 = { export type TodoUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10838,6 +10782,7 @@ export type SessionStatusV2 = export type SessionStatusV22 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10851,6 +10796,7 @@ export type SessionStatusV22 = { export type SessionIdle2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10863,6 +10809,7 @@ export type SessionIdle2 = { export type TuiPromptAppend2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10875,6 +10822,7 @@ export type TuiPromptAppend2 = { export type TuiCommandExecute2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10905,6 +10853,7 @@ export type TuiCommandExecute2 = { export type TuiToastShow2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10920,6 +10869,7 @@ export type TuiToastShow2 = { export type TuiSessionSelect2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10935,6 +10885,7 @@ export type TuiSessionSelect2 = { export type InstallationUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10947,6 +10898,7 @@ export type InstallationUpdated2 = { export type InstallationUpdateAvailable2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10959,6 +10911,7 @@ export type InstallationUpdateAvailable2 = { export type VcsBranchUpdated2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10971,6 +10924,7 @@ export type VcsBranchUpdated2 = { export type McpStatusChanged2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -10983,6 +10937,7 @@ export type McpStatusChanged2 = { export type PermissionAsked2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -11006,6 +10961,7 @@ export type PermissionAsked2 = { export type PermissionReplied2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -11059,6 +11015,7 @@ export type QuestionToolV2 = { export type QuestionAsked2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -11079,6 +11036,7 @@ export type QuestionAnswerV2 = Array export type QuestionRepliedV2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -11093,6 +11051,7 @@ export type QuestionRepliedV2 = { export type QuestionRejectedV2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -11106,6 +11065,7 @@ export type QuestionRejectedV2 = { export type SessionError2 = { id: string + created: number metadata?: { [key: string]: unknown } @@ -11153,42 +11113,42 @@ export type V2EventV2 = | MessageRemoved2 | MessagePartUpdated2 | MessagePartRemoved2 - | SessionNextAgentSwitched2 - | SessionNextModelSwitched2 - | SessionNextMoved2 - | SessionNextRenamed2 - | SessionNextForked2 - | SessionNextPrompted2 - | SessionNextPromptAdmitted2 - | SessionNextExecutionSettled2 - | SessionNextContextUpdated2 - | SessionNextSynthetic2 - | SessionNextSkillActivated2 - | SessionNextShellStarted2 - | SessionNextShellEnded2 - | SessionNextStepStarted2 - | SessionNextStepEnded2 - | SessionNextStepFailed2 - | SessionNextTextStarted2 - | SessionNextTextDelta2 - | SessionNextTextEnded2 - | SessionNextReasoningStarted2 - | SessionNextReasoningDelta2 - | SessionNextReasoningEnded2 - | SessionNextToolInputStarted2 - | SessionNextToolInputDelta2 - | SessionNextToolInputEnded2 - | SessionNextToolCalled2 - | SessionNextToolProgress2 - | SessionNextToolSuccess2 - | SessionNextToolFailed2 - | SessionNextRetried2 - | SessionNextCompactionStarted2 - | SessionNextCompactionDelta2 - | SessionNextCompactionEnded2 - | SessionNextRevertStaged2 - | SessionNextRevertCleared2 - | SessionNextRevertCommitted2 + | AgentSelected2 + | ModelSelected2 + | SessionMoved2 + | RenamedV2 + | ForkedV2 + | PromptPromoted2 + | PromptAdmitted2 + | ExecutionSettled2 + | SessionContextUpdated2 + | SyntheticV2 + | SkillActivated2 + | ShellStarted2 + | ShellEnded2 + | StepStarted2 + | StepEnded2 + | StepFailed2 + | TextStarted2 + | TextDelta2 + | TextEnded2 + | ReasoningStarted2 + | ReasoningDelta2 + | ReasoningEnded2 + | ToolInputStarted2 + | ToolInputDelta2 + | ToolInputEnded2 + | ToolCalled2 + | ToolProgress2 + | ToolSuccess2 + | ToolFailed2 + | RetriedV2 + | CompactionStarted2 + | CompactionDelta2 + | CompactionEnded2 + | RevertStaged2 + | RevertCleared2 + | RevertCommitted2 | FileEdited2 | ReferenceUpdated2 | PermissionV2Asked2 diff --git a/packages/tui/src/context/data.tsx b/packages/tui/src/context/data.tsx index d70a71be5c..cc9378dbac 100644 --- a/packages/tui/src/context/data.tsx +++ b/packages/tui/src/context/data.tsx @@ -27,6 +27,8 @@ import { createSignal, onCleanup } from "solid-js" export type DataSessionStatus = "idle" | "running" +const messageIDFromEvent = (eventID: string) => eventID.replace(/^evt_/, "msg_") + type LocationData = { agent?: AgentV2Info[] command?: CommandV2Info[] @@ -181,17 +183,21 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ const info = store.session.info[sessionID] if (!info) return const rootID = resolveRoot(sessionID) - setStore("session", "family", produce((draft) => { - if (sessionID !== rootID && draft[sessionID]) { - const members = draft[rootID] ??= [] - for (const id of draft[sessionID]) { - if (!members.includes(id)) members.push(id) + setStore( + "session", + "family", + produce((draft) => { + if (sessionID !== rootID && draft[sessionID]) { + const members = (draft[rootID] ??= []) + for (const id of draft[sessionID]) { + if (!members.includes(id)) members.push(id) + } + delete draft[sessionID] } - delete draft[sessionID] - } - const family = draft[rootID] ??= [] - if (!family.includes(sessionID)) family.push(sessionID) - })) + const family = (draft[rootID] ??= []) + if (!family.includes(sessionID)) family.push(sessionID) + }), + ) } function handleEvent(event: V2Event) { @@ -214,124 +220,113 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ case "skill.updated": void result.location.skill.refresh(event.location) break - case "session.next.agent.switched": + case "agent.selected": if (store.session.info[event.data.sessionID]) setStore("session", "info", event.data.sessionID, "agent", event.data.agent) message.update(event.data.sessionID, (draft, index) => { message.append(draft, index, { - id: event.data.messageID, + id: messageIDFromEvent(event.id), type: "agent-switched", agent: event.data.agent, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.model.switched": + case "model.selected": if (store.session.info[event.data.sessionID]) setStore("session", "info", event.data.sessionID, "model", event.data.model) message.update(event.data.sessionID, (draft, index) => { message.append(draft, index, { - id: event.data.messageID, + id: messageIDFromEvent(event.id), type: "model-switched", model: event.data.model, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.renamed": + case "renamed": if (store.session.info[event.data.sessionID]) setStore("session", "info", event.data.sessionID, "title", event.data.title) break - case "session.next.prompted": { + case "prompt.promoted": { setStore("session", "status", event.data.sessionID, "running") message.update(event.data.sessionID, (draft, index) => { - const position = index.get(event.data.messageID) + const position = index.get(event.data.inputID) const existing = position === undefined ? undefined : draft[position] if (existing?.type === "user") { - existing.text = event.data.prompt.text - existing.files = event.data.prompt.files - existing.agents = event.data.prompt.agents - existing.time.created = event.data.timestamp + existing.time.created = event.created if (existing.metadata?.queued === true) { delete existing.metadata.queued if (Object.keys(existing.metadata).length === 0) existing.metadata = undefined } return } - message.append(draft, index, { - id: event.data.messageID, - type: "user", - text: event.data.prompt.text, - files: event.data.prompt.files, - agents: event.data.prompt.agents, - time: { created: event.data.timestamp }, - }) }) break } - case "session.next.prompt.admitted": + case "prompt.admitted": message.update(event.data.sessionID, (draft, index) => { message.append(draft, index, { - id: event.data.messageID, + id: event.data.inputID, type: "user", text: event.data.prompt.text, files: event.data.prompt.files, agents: event.data.prompt.agents, metadata: { queued: true }, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.context.updated": + case "session.context.updated": message.update(event.data.sessionID, (draft, index) => { message.append(draft, index, { - id: event.data.messageID, + id: messageIDFromEvent(event.id), type: "system", text: event.data.text, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.synthetic": + case "synthetic": message.update(event.data.sessionID, (draft, index) => { message.append(draft, index, { - id: event.data.messageID, + id: messageIDFromEvent(event.id), type: "synthetic", sessionID: event.data.sessionID, text: event.data.text, description: event.data.description, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.shell.started": + case "shell.started": setStore("session", "status", event.data.sessionID, "running") message.update(event.data.sessionID, (draft, index) => { message.append(draft, index, { - id: event.data.messageID, + id: messageIDFromEvent(event.id), type: "shell", callID: event.data.callID, command: event.data.command, output: "", - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.shell.ended": + case "shell.ended": setStore("session", "status", event.data.sessionID, "idle") message.update(event.data.sessionID, (draft, index) => { const match = message.activeShell(draft, event.data.callID) if (!match) return match.output = event.data.output - match.time.completed = event.data.timestamp + match.time.completed = event.created }) break - case "session.next.step.started": + case "step.started": setStore("session", "status", event.data.sessionID, "running") message.update(event.data.sessionID, (draft, index) => { if (index.has(event.data.assistantMessageID)) return const currentAssistant = message.activeAssistant(draft) - if (currentAssistant) currentAssistant.time.completed = event.data.timestamp + if (currentAssistant) currentAssistant.time.completed = event.created message.append(draft, index, { id: event.data.assistantMessageID, type: "assistant", @@ -339,16 +334,16 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ model: event.data.model, content: [], snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.step.ended": + case "step.ended": setStore("session", "status", event.data.sessionID, "running") message.update(event.data.sessionID, (draft, index) => { const currentAssistant = message.assistant(draft, index, event.data.assistantMessageID) if (!currentAssistant) return - currentAssistant.time.completed = event.data.timestamp + currentAssistant.time.completed = event.created currentAssistant.finish = event.data.finish currentAssistant.cost = event.data.cost currentAssistant.tokens = event.data.tokens @@ -356,16 +351,16 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ currentAssistant.snapshot = { ...currentAssistant.snapshot, end: event.data.snapshot } }) break - case "session.next.step.failed": + case "step.failed": message.update(event.data.sessionID, (draft, index) => { const currentAssistant = message.assistant(draft, index, event.data.assistantMessageID) if (!currentAssistant) return - currentAssistant.time.completed = event.data.timestamp + currentAssistant.time.completed = event.created currentAssistant.finish = "error" currentAssistant.error = event.data.error }) break - case "session.next.text.started": + case "text.started": message.update(event.data.sessionID, (draft, index) => { message.assistant(draft, index, event.data.assistantMessageID)?.content.push({ type: "text", @@ -374,7 +369,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ }) }) break - case "session.next.text.delta": + case "text.delta": message.update(event.data.sessionID, (draft, index) => { const match = message.latestText( message.assistant(draft, index, event.data.assistantMessageID), @@ -383,7 +378,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ if (match) match.text += event.data.delta }) break - case "session.next.text.ended": + case "text.ended": message.update(event.data.sessionID, (draft, index) => { const match = message.latestText( message.assistant(draft, index, event.data.assistantMessageID), @@ -392,18 +387,18 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ if (match) match.text = event.data.text }) break - case "session.next.tool.input.started": + case "tool.input.started": message.update(event.data.sessionID, (draft, index) => { message.assistant(draft, index, event.data.assistantMessageID)?.content.push({ type: "tool", id: event.data.callID, name: event.data.name, - time: { created: event.data.timestamp }, + time: { created: event.created }, state: { status: "pending", input: "" }, }) }) break - case "session.next.tool.input.delta": + case "tool.input.delta": message.update(event.data.sessionID, (draft, index) => { const match = message.latestTool( message.assistant(draft, index, event.data.assistantMessageID), @@ -412,7 +407,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ if (match?.state.status === "pending") match.state.input += event.data.delta }) break - case "session.next.tool.input.ended": + case "tool.input.ended": message.update(event.data.sessionID, (draft, index) => { const match = message.latestTool( message.assistant(draft, index, event.data.assistantMessageID), @@ -421,19 +416,19 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ if (match?.state.status === "pending") match.state.input = event.data.text }) break - case "session.next.tool.called": + case "tool.called": message.update(event.data.sessionID, (draft, index) => { const match = message.latestTool( message.assistant(draft, index, event.data.assistantMessageID), event.data.callID, ) if (!match) return - match.time.ran = event.data.timestamp + match.time.ran = event.created match.provider = event.data.provider match.state = { status: "running", input: event.data.input, structured: {}, content: [] } }) break - case "session.next.tool.progress": + case "tool.progress": message.update(event.data.sessionID, (draft, index) => { const match = message.latestTool( message.assistant(draft, index, event.data.assistantMessageID), @@ -444,7 +439,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ match.state.content = [...event.data.content] }) break - case "session.next.tool.success": + case "tool.success": message.update(event.data.sessionID, (draft, index) => { const match = message.latestTool( message.assistant(draft, index, event.data.assistantMessageID), @@ -463,10 +458,10 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ metadata: match.provider?.metadata, resultMetadata: event.data.provider.metadata, } - match.time.completed = event.data.timestamp + match.time.completed = event.created }) break - case "session.next.tool.failed": + case "tool.failed": message.update(event.data.sessionID, (draft, index) => { const match = message.latestTool( message.assistant(draft, index, event.data.assistantMessageID), @@ -486,21 +481,21 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ metadata: match.provider?.metadata, resultMetadata: event.data.provider.metadata, } - match.time.completed = event.data.timestamp + match.time.completed = event.created }) break - case "session.next.reasoning.started": + case "reasoning.started": message.update(event.data.sessionID, (draft, index) => { message.assistant(draft, index, event.data.assistantMessageID)?.content.push({ type: "reasoning", id: event.data.reasoningID, text: "", providerMetadata: event.data.providerMetadata, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break - case "session.next.reasoning.delta": + case "reasoning.delta": message.update(event.data.sessionID, (draft, index) => { const match = message.latestReasoning( message.assistant(draft, index, event.data.assistantMessageID), @@ -509,7 +504,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ if (match) match.text += event.data.delta }) break - case "session.next.reasoning.ended": + case "reasoning.ended": message.update(event.data.sessionID, (draft, index) => { const match = message.latestReasoning( message.assistant(draft, index, event.data.assistantMessageID), @@ -517,38 +512,38 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ ) if (match) { match.text = event.data.text - match.time = { created: match.time?.created ?? event.data.timestamp, completed: event.data.timestamp } + match.time = { created: match.time?.created ?? event.created, completed: event.created } if (event.data.providerMetadata !== undefined) match.providerMetadata = event.data.providerMetadata } }) break - case "session.next.retried": - case "session.next.compaction.started": + case "retried": + case "compaction.started": setStore("session", "status", event.data.sessionID, "running") break - case "session.next.execution.settled": + case "execution.settled": setStore("session", "status", event.data.sessionID, "idle") break - case "session.next.revert.staged": + case "revert.staged": if (store.session.info[event.data.sessionID]) setStore("session", "info", event.data.sessionID, "revert", event.data.revert) break - case "session.next.revert.cleared": - case "session.next.revert.committed": + case "revert.cleared": + case "revert.committed": if (store.session.info[event.data.sessionID]) setStore("session", "info", event.data.sessionID, "revert", undefined) break - case "session.next.compaction.delta": + case "compaction.delta": break - case "session.next.compaction.ended": + case "compaction.ended": message.update(event.data.sessionID, (draft, index) => { message.append(draft, index, { - id: event.data.messageID, + id: messageIDFromEvent(event.id), type: "compaction", reason: event.data.reason, summary: event.data.text, recent: event.data.recent, - time: { created: event.data.timestamp }, + time: { created: event.created }, }) }) break @@ -692,8 +687,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ return liveByID.get(message.id) ?? message }), ...live.filter((message) => !loadedIDs.has(message.id)), - ] - .toSorted((a, b) => a.time.created - b.time.created) + ].toSorted((a, b) => a.time.created - b.time.created) messageIndex.set(sessionID, new Map(messages.map((message, index) => [message.id, index]))) setStore("session", "message", sessionID, messages) }, diff --git a/packages/tui/src/feature-plugins/system/notifications.ts b/packages/tui/src/feature-plugins/system/notifications.ts index 81c696ef13..aa8dcace19 100644 --- a/packages/tui/src/feature-plugins/system/notifications.ts +++ b/packages/tui/src/feature-plugins/system/notifications.ts @@ -74,17 +74,17 @@ const tui: TuiPlugin = async (api) => { notify(api, sessionID, "Session done", session?.parentID ? "subagent_done" : "done") } - api.event.on("session.next.prompted", (event) => started(event.data.sessionID)) - api.event.on("session.next.shell.started", (event) => started(event.data.sessionID)) - api.event.on("session.next.step.started", (event) => started(event.data.sessionID)) - api.event.on("session.next.retried", (event) => started(event.data.sessionID)) - api.event.on("session.next.compaction.started", (event) => started(event.data.sessionID)) - api.event.on("session.next.shell.ended", (event) => ended(event.data.sessionID)) - api.event.on("session.next.step.ended", (event) => { + api.event.on("prompt.promoted", (event) => started(event.data.sessionID)) + api.event.on("shell.started", (event) => started(event.data.sessionID)) + api.event.on("step.started", (event) => started(event.data.sessionID)) + api.event.on("retried", (event) => started(event.data.sessionID)) + api.event.on("compaction.started", (event) => started(event.data.sessionID)) + api.event.on("shell.ended", (event) => ended(event.data.sessionID)) + api.event.on("step.ended", (event) => { if (event.data.finish === "tool-calls") return ended(event.data.sessionID) }) - api.event.on("session.next.step.failed", (event) => { + api.event.on("step.failed", (event) => { const sessionID = event.data.sessionID if (!active.has(sessionID)) return errored.add(sessionID) diff --git a/packages/tui/src/routes/session/rows.ts b/packages/tui/src/routes/session/rows.ts index f1c4714276..23b43c3b17 100644 --- a/packages/tui/src/routes/session/rows.ts +++ b/packages/tui/src/routes/session/rows.ts @@ -126,45 +126,49 @@ export function createSessionRows(sessionID: Accessor) { return index === -1 ? rows.length : index } - const message = (event: { data: { sessionID: string; messageID: string } }) => { - if (event.data.sessionID === sessionID()) appendMessage(event.data.messageID) + const message = (event: { id: string; data: { sessionID: string } }) => { + if (event.data.sessionID === sessionID()) appendMessage(event.id.replace(/^evt_/, "msg_")) + } + const input = (event: { data: { sessionID: string; inputID: string } }) => { + if (event.data.sessionID === sessionID()) appendMessage(event.data.inputID) } const subscriptions = [ - data.on("session.next.prompt.admitted", message), - data.on("session.next.prompted", message), - data.on("session.next.context.updated", message), - data.on("session.next.synthetic", (event) => { - if (event.data.sessionID === sessionID() && event.data.description?.trim()) appendMessage(event.data.messageID) + data.on("prompt.admitted", input), + data.on("prompt.promoted", input), + data.on("session.context.updated", message), + data.on("synthetic", (event) => { + if (event.data.sessionID === sessionID() && event.data.description?.trim()) + appendMessage(event.id.replace(/^evt_/, "msg_")) }), - data.on("session.next.shell.started", message), - data.on("session.next.agent.switched", message), - data.on("session.next.model.switched", message), - data.on("session.next.compaction.ended", message), - data.on("session.next.text.delta", (event) => { + data.on("shell.started", message), + data.on("agent.selected", message), + data.on("model.selected", message), + data.on("compaction.ended", message), + data.on("text.delta", (event) => { if (event.data.sessionID === sessionID()) appendPart({ messageID: event.data.assistantMessageID, partID: event.data.textID }) }), - data.on("session.next.text.ended", (event) => { + data.on("text.ended", (event) => { if (event.data.sessionID === sessionID() && event.data.text.trim()) appendPart({ messageID: event.data.assistantMessageID, partID: event.data.textID }) }), - data.on("session.next.reasoning.delta", (event) => { + data.on("reasoning.delta", (event) => { if (event.data.sessionID === sessionID()) appendPart({ messageID: event.data.assistantMessageID, partID: event.data.reasoningID }) }), - data.on("session.next.reasoning.ended", (event) => { + data.on("reasoning.ended", (event) => { if (event.data.sessionID === sessionID() && event.data.text.trim()) appendPart({ messageID: event.data.assistantMessageID, partID: event.data.reasoningID }) }), - data.on("session.next.tool.input.started", (event) => { + data.on("tool.input.started", (event) => { if (event.data.sessionID === sessionID()) appendPart({ messageID: event.data.assistantMessageID, partID: event.data.callID }, event.data.name) }), - data.on("session.next.step.ended", (event) => { + data.on("step.ended", (event) => { if (event.data.sessionID !== sessionID() || ["tool-calls", "unknown"].includes(event.data.finish)) return appendFooter(event.data.assistantMessageID) }), - data.on("session.next.step.failed", (event) => { + data.on("step.failed", (event) => { if (event.data.sessionID === sessionID()) appendFooter(event.data.assistantMessageID) }), ] @@ -229,8 +233,6 @@ function hasPart(rows: SessionRow[], ref: PartRef) { return rows.some((row) => { if (row.type === "part") return row.ref.messageID === ref.messageID && row.ref.partID === ref.partID if (row.type !== "group") return false - return [...row.refs, ...row.pending].some( - (item) => item.messageID === ref.messageID && item.partID === ref.partID, - ) + return [...row.refs, ...row.pending].some((item) => item.messageID === ref.messageID && item.partID === ref.partID) }) } diff --git a/packages/tui/test/cli/cmd/tui/notifications.test.ts b/packages/tui/test/cli/cmd/tui/notifications.test.ts index a914b34dbc..5f1c6355b3 100644 --- a/packages/tui/test/cli/cmd/tui/notifications.test.ts +++ b/packages/tui/test/cli/cmd/tui/notifications.test.ts @@ -90,12 +90,12 @@ function durable(sessionID: string) { function stepStarted(id: string, sessionID = "session"): V2Event { return { id, - type: "session.next.step.started", + created: 0, + type: "step.started", durable: durable(sessionID), data: { sessionID, assistantMessageID: `msg_${id}`, - timestamp: 0, agent: "build", model: { id: "model", providerID: "provider" }, }, @@ -105,12 +105,12 @@ function stepStarted(id: string, sessionID = "session"): V2Event { function stepEnded(id: string, sessionID = "session", finish = "stop"): V2Event { return { id, - type: "session.next.step.ended", + created: 0, + type: "step.ended", durable: durable(sessionID), data: { sessionID, assistantMessageID: `msg_${id}`, - timestamp: 0, finish, cost: 0, tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, @@ -121,12 +121,12 @@ function stepEnded(id: string, sessionID = "session", finish = "stop"): V2Event function stepFailed(id: string, sessionID = "session"): V2Event { return { id, - type: "session.next.step.failed", + created: 0, + type: "step.failed", durable: durable(sessionID), data: { sessionID, assistantMessageID: `msg_${id}`, - timestamp: 0, error: { type: "unknown", message: "boom" }, }, } @@ -150,8 +150,8 @@ describe("internal notifications TUI plugin", () => { test("notifies for question and permission requests with blurred notifications and always-on sounds", async () => { const harness = await setup() - harness.emit({ id: "event-1", type: "question.asked", data: question("question-1") }) - harness.emit({ id: "event-2", type: "permission.asked", data: permission("permission-1") }) + harness.emit({ id: "event-1", created: 0, type: "question.asked", data: question("question-1") }) + harness.emit({ id: "event-2", created: 0, type: "permission.asked", data: permission("permission-1") }) expect(harness.notifications).toEqual([questionNotification, permissionNotification]) }) @@ -159,23 +159,25 @@ describe("internal notifications TUI plugin", () => { test("dedupes pending questions and permissions until they are resolved", async () => { const harness = await setup() - harness.emit({ id: "event-1", type: "question.asked", data: question("question-1") }) - harness.emit({ id: "event-2", type: "question.asked", data: question("question-1") }) + harness.emit({ id: "event-1", created: 0, type: "question.asked", data: question("question-1") }) + harness.emit({ id: "event-2", created: 0, type: "question.asked", data: question("question-1") }) harness.emit({ id: "event-3", + created: 0, type: "question.replied", data: { sessionID: "session", requestID: "question-1", answers: [] }, }) - harness.emit({ id: "event-4", type: "question.asked", data: question("question-1") }) + harness.emit({ id: "event-4", created: 0, type: "question.asked", data: question("question-1") }) - harness.emit({ id: "event-5", type: "permission.asked", data: permission("permission-1") }) - harness.emit({ id: "event-6", type: "permission.asked", data: permission("permission-1") }) + harness.emit({ id: "event-5", created: 0, type: "permission.asked", data: permission("permission-1") }) + harness.emit({ id: "event-6", created: 0, type: "permission.asked", data: permission("permission-1") }) harness.emit({ id: "event-7", + created: 0, type: "permission.replied", data: { sessionID: "session", requestID: "permission-1", reply: "once" }, }) - harness.emit({ id: "event-8", type: "permission.asked", data: permission("permission-1") }) + harness.emit({ id: "event-8", created: 0, type: "permission.asked", data: permission("permission-1") }) expect(harness.notifications).toEqual([ questionNotification, @@ -205,7 +207,7 @@ describe("internal notifications TUI plugin", () => { test("uses sound-only notifications and subagent_done sound for subagent sessions", async () => { const harness = await setup() - harness.emit({ id: "event-1", type: "question.asked", data: question("question-1", "subagent") }) + harness.emit({ id: "event-1", created: 0, type: "question.asked", data: question("question-1", "subagent") }) harness.emit(stepStarted("event-2", "subagent")) harness.emit(stepEnded("event-3", "subagent")) @@ -248,12 +250,14 @@ describe("internal notifications TUI plugin", () => { harness.emit(stepStarted("event-1", "abort")) harness.emit({ id: "event-2", + created: 0, type: "session.error", data: { sessionID: "abort", error: { name: "MessageAbortedError", data: { message: "Aborted" } } }, }) harness.emit(stepStarted("event-3", "timeout")) harness.emit({ id: "event-4", + created: 0, type: "session.error", data: { sessionID: "timeout", error: { name: "UnknownError", data: { message: "SSE read timed out" } } }, }) diff --git a/packages/tui/test/cli/tui/data.test.tsx b/packages/tui/test/cli/tui/data.test.tsx index b2bacfc0dd..a71bdc47d8 100644 --- a/packages/tui/test/cli/tui/data.test.tsx +++ b/packages/tui/test/cli/tui/data.test.tsx @@ -2,6 +2,8 @@ import { expect, test } from "bun:test" import { testRender } from "@opentui/solid" import type { V2Event } from "@opencode-ai/sdk/v2" +import { SessionMessage } from "@opencode-ai/core/session/message" +import { EventV2 } from "@opencode-ai/core/event" import { onMount } from "solid-js" import { ProjectProvider } from "../../../src/context/project" import { SDKProvider } from "../../../src/context/sdk" @@ -47,8 +49,8 @@ test("refreshes resources into reactive getters", async () => { if (url.pathname === "/api/session/ses_test/message") return json({ data: [ - { id: "msg_second", type: "user", text: "Second", time: { created: 2 } }, - { id: "msg_first", type: "user", text: "First", time: { created: 1 } }, + { id: "msg_second", created: 0, type: "user", text: "Second", time: { created: 2 } }, + { id: "msg_first", created: 0, type: "user", text: "First", time: { created: 1 } }, ], cursor: {}, }) @@ -187,7 +189,9 @@ test("connectedOnce is false until first connect and persists across disconnect" ) const connect = () => stream?.enqueue( - encoder.encode(`data: ${JSON.stringify({ id: "evt_connected", type: "server.connected", data: {} })}\n\n`), + encoder.encode( + `data: ${JSON.stringify({ id: "evt_connected", created: 0, type: "server.connected", data: {} })}\n\n`, + ), ) const disconnect = () => { stream?.close() @@ -264,12 +268,12 @@ test("tracks session status from active sessions and execution events", async () emitEvent(events, { id: "evt_step_started", - type: "session.next.step.started", + created: 0, + type: "step.started", durable: durable("session-live"), data: { sessionID: "session-live", assistantMessageID: "message-live", - timestamp: 1, agent: "build", model: { id: "model", providerID: "provider" }, }, @@ -278,12 +282,12 @@ test("tracks session status from active sessions and execution events", async () emitEvent(events, { id: "evt_step_ended", - type: "session.next.step.ended", + created: 0, + type: "step.ended", durable: durable("session-live", 1, 2), data: { sessionID: "session-live", assistantMessageID: "message-live", - timestamp: 2, finish: "stop", cost: 0, tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, @@ -297,10 +301,10 @@ test("tracks session status from active sessions and execution events", async () emitEvent(events, { id: "evt_execution_settled", - type: "session.next.execution.settled", + created: 0, + type: "execution.settled", data: { sessionID: "session-live", - timestamp: 3, outcome: "success", }, }) @@ -308,12 +312,12 @@ test("tracks session status from active sessions and execution events", async () emitEvent(events, { id: "evt_failed_step_started", - type: "session.next.step.started", + created: 0, + type: "step.started", durable: durable("session-failed"), data: { sessionID: "session-failed", assistantMessageID: "message-failed", - timestamp: 3, agent: "build", model: { id: "model", providerID: "provider" }, }, @@ -322,12 +326,12 @@ test("tracks session status from active sessions and execution events", async () emitEvent(events, { id: "evt_step_failed", - type: "session.next.step.failed", + created: 0, + type: "step.failed", durable: durable("session-failed", 1, 2), data: { sessionID: "session-failed", assistantMessageID: "message-failed", - timestamp: 4, error: { type: "unknown", message: "Provider unavailable" }, }, }) @@ -339,10 +343,10 @@ test("tracks session status from active sessions and execution events", async () emitEvent(events, { id: "evt_failed_execution_settled", - type: "session.next.execution.settled", + created: 0, + type: "execution.settled", data: { sessionID: "session-failed", - timestamp: 5, outcome: "failure", error: { type: "unknown", message: "Provider unavailable" }, }, @@ -411,7 +415,7 @@ test("refreshes integrations after integration updates", async () => { expect(data.location.integration.list()).toEqual([]) const before = { ...requests } - emitEvent(events, { id: "evt_integration", type: "integration.updated", data: {} }) + emitEvent(events, { id: "evt_integration", created: 0, type: "integration.updated", data: {} }) await wait(() => data.location.integration.list()?.length === 1) await wait(() => requests.model > before.model && requests.provider > before.provider) expect(data.location.integration.list()?.[0]).toMatchObject({ id: "openai", name: "OpenAI" }) @@ -449,7 +453,7 @@ test("refreshes effective catalog data after catalog updates", async () => { try { await wait(() => requests.model > 0 && requests.provider > 0) const before = { ...requests } - emitEvent(events, { id: "evt_catalog", type: "catalog.updated", data: {} }) + emitEvent(events, { id: "evt_catalog", created: 0, type: "catalog.updated", data: {} }) await wait(() => requests.model > before.model && requests.provider > before.provider) } finally { app.renderer.destroy() @@ -496,7 +500,7 @@ test("refreshes agents after agent updates", async () => { try { await wait(() => data.location.agent.list()?.[0]?.id === "build") - emitEvent(events, { id: "evt_agent", type: "agent.updated", data: {} }) + emitEvent(events, { id: "evt_agent", created: 0, type: "agent.updated", data: {} }) await wait(() => data.location.agent.list()?.[0]?.id === "reviewer") } finally { app.renderer.destroy() @@ -541,7 +545,7 @@ test("refreshes references after updates", async () => { try { await mounted await wait(() => requests === 1) - emitEvent(events, { id: "evt_reference_1", type: "reference.updated", data: {} }) + emitEvent(events, { id: "evt_reference_1", created: 0, type: "reference.updated", data: {} }) await wait(() => data.location.reference.list()?.length === 1) expect(data.location.reference.list()?.[0]?.name).toBe("docs") } finally { @@ -602,6 +606,7 @@ test("keeps shell state scoped to location", async () => { events.emit({ id: "evt_shell_created", + created: 0, type: "shell.created", location: { directory: other }, data: { @@ -650,6 +655,7 @@ test("adds and dismisses permission requests from live events", async () => { await wait(() => data.connection.status() === "connected") emitEvent(events, { id: "evt_permission_asked_1", + created: 0, type: "permission.v2.asked", data: { id: "per_1", @@ -660,6 +666,7 @@ test("adds and dismisses permission requests from live events", async () => { }) emitEvent(events, { id: "evt_permission_asked_2", + created: 0, type: "permission.v2.asked", data: { id: "per_2", @@ -672,6 +679,7 @@ test("adds and dismisses permission requests from live events", async () => { emitEvent(events, { id: "evt_permission_replied_1", + created: 0, type: "permission.v2.replied", data: { sessionID: "ses_1", requestID: "per_1", reply: "once" }, }) @@ -680,6 +688,7 @@ test("adds and dismisses permission requests from live events", async () => { emitEvent(events, { id: "evt_permission_replied_2", + created: 0, type: "permission.v2.replied", data: { sessionID: "ses_1", requestID: "per_2", reply: "reject" }, }) @@ -715,6 +724,7 @@ test("adds and dismisses question requests from live events", async () => { await wait(() => data.connection.status() === "connected") emitEvent(events, { id: "evt_question_asked_1", + created: 0, type: "question.v2.asked", data: { id: "que_1", @@ -724,6 +734,7 @@ test("adds and dismisses question requests from live events", async () => { }) emitEvent(events, { id: "evt_question_asked_2", + created: 0, type: "question.v2.asked", data: { id: "que_2", @@ -735,6 +746,7 @@ test("adds and dismisses question requests from live events", async () => { emitEvent(events, { id: "evt_question_replied_1", + created: 0, type: "question.v2.replied", data: { sessionID: "ses_1", requestID: "que_1", answers: [["First"]] }, }) @@ -743,6 +755,7 @@ test("adds and dismisses question requests from live events", async () => { emitEvent(events, { id: "evt_question_rejected_2", + created: 0, type: "question.v2.rejected", data: { sessionID: "ses_1", requestID: "que_2" }, }) @@ -783,52 +796,52 @@ test("settles pending tools when a live failure arrives", async () => { await mounted emitEvent(events, { id: "evt_agent_1", - type: "session.next.agent.switched", + created: 0, + type: "agent.selected", durable: durable("session-1"), - data: { sessionID: "session-1", messageID: "msg_agent_1", timestamp: 0, agent: "build" }, + data: { sessionID: "session-1", agent: "build" }, }) emitEvent(events, { id: "evt_model_1", - type: "session.next.model.switched", + created: 0, + type: "model.selected", durable: durable("session-1", 1), data: { sessionID: "session-1", - messageID: "msg_model_1", - timestamp: 0, model: { id: "model-1", providerID: "provider-1" }, }, }) emitEvent(events, { id: "evt_step_started_1", - type: "session.next.step.started", + created: 0, + type: "step.started", durable: durable("session-1", 2), data: { sessionID: "session-1", assistantMessageID: "msg_explicit_assistant_9", - timestamp: 1, agent: "build", model: { id: "model-1", providerID: "provider-1" }, }, }) emitEvent(events, { id: "evt_input_1", - type: "session.next.tool.input.started", + created: 0, + type: "tool.input.started", durable: durable("session-1", 3), data: { sessionID: "session-1", assistantMessageID: "msg_explicit_assistant_9", - timestamp: 2, callID: "call-1", name: "bash", }, }) emitEvent(events, { id: "evt_called_1", - type: "session.next.tool.called", + created: 0, + type: "tool.called", durable: durable("session-1", 4), data: { sessionID: "session-1", - timestamp: 2, assistantMessageID: "msg_explicit_assistant_9", callID: "call-1", tool: "bash", @@ -838,11 +851,11 @@ test("settles pending tools when a live failure arrives", async () => { }) emitEvent(events, { id: "evt_failed_1", - type: "session.next.tool.failed", + created: 0, + type: "tool.failed", durable: durable("session-1", 5), data: { sessionID: "session-1", - timestamp: 3, assistantMessageID: "msg_explicit_assistant_9", callID: "call-1", error: { type: "unknown", message: "aborted" }, @@ -928,12 +941,12 @@ test("renders admitted prompts immediately with queued marker and clears when pr const unsubscribe = sync.listen((event) => received.push(event.name)) emitEvent(events, { id: "evt_admitted_1", - type: "session.next.prompt.admitted", + created: 0, + type: "prompt.admitted", durable: durable(sessionID), data: { sessionID, - messageID, - timestamp: 0, + inputID: messageID, prompt: { text: "hello" }, delivery: "steer", }, @@ -947,19 +960,17 @@ test("renders admitted prompts immediately with queued marker and clears when pr emitEvent(events, { id: "evt_prompted_1", - type: "session.next.prompted", + created: 0, + type: "prompt.promoted", durable: durable(sessionID, 1), data: { sessionID, - messageID, - timestamp: 0, - prompt: { text: "hello" }, - delivery: "steer", + inputID: messageID, }, }) - await wait(() => received.at(-1) === "session.next.prompted") - expect(received.slice(-2)).toEqual(["session.next.prompt.admitted", "session.next.prompted"]) + await wait(() => received.at(-1) === "prompt.promoted") + expect(received.slice(-2)).toEqual(["prompt.admitted", "prompt.promoted"]) unsubscribe() const message = sync.session.message.list(sessionID)?.[0] expect(message?.type).toBe("user") @@ -1007,21 +1018,21 @@ test("projects live context updates with their message ID", async () => { await mounted emitEvent(events, { id: "evt_context_1", - type: "session.next.context.updated", + created: 0, + type: "session.context.updated", durable: durable("session-1"), data: { sessionID: "session-1", - messageID: "msg_context_1", - timestamp: 1, text: "Updated context", }, }) await wait(() => sync.session.message.list("session-1")?.length === 1) expect(sync.session.message.list("session-1")?.[0]).toMatchObject({ - id: "msg_context_1", + id: SessionMessage.ID.fromEvent(EventV2.ID.make("evt_context_1")), type: "system", text: "Updated context", + time: { created: 0 }, }) } finally { app.renderer.destroy() diff --git a/packages/tui/test/cli/tui/use-event.test.tsx b/packages/tui/test/cli/tui/use-event.test.tsx index 6ba38a83b8..7a4465c927 100644 --- a/packages/tui/test/cli/tui/use-event.test.tsx +++ b/packages/tui/test/cli/tui/use-event.test.tsx @@ -30,6 +30,7 @@ function event(payload: V2Event, input: { directory: string; project?: string; w function vcs(branch: string): V2Event { return { id: `evt_vcs_${branch}`, + created: 0, type: "vcs.branch.updated", data: { branch, @@ -40,6 +41,7 @@ function vcs(branch: string): V2Event { function update(version: string): V2Event { return { id: `evt_update_${version}`, + created: 0, type: "installation.update-available", data: { version, diff --git a/specs/v2/schema-changelog.md b/specs/v2/schema-changelog.md index d526b5d8e7..466cc73a8a 100644 --- a/specs/v2/schema-changelog.md +++ b/specs/v2/schema-changelog.md @@ -904,3 +904,17 @@ Compatibility: - Existing Context Epoch rows migrate in place by dropping the obsolete selection and pending-replacement columns. - Model and agent switches no longer discard earlier chronological System Context updates by forcing a new baseline. + +## 2026-07-03: Normalize Session Event Names And Envelope Time + +- Drop the experimental `session.next.` prefix from current Session event names. +- Rename `agent.switched` to `agent.selected`, `model.switched` to `model.selected`, and `prompted` to `prompt.promoted`; add `prompt.admitted` as the durable prompt admission record. +- Add an envelope-level `created` timestamp stored on each event row; payload-level `timestamp` fields are removed. +- Remove projection-only `messageID` fields from selected/message-producing events; projected message IDs derive from the event ID. `revert.committed.messageID` remains, and `forked.messageID` is now `forked.from`. +- Rename `session.moved.subdirectory` to `subpath` and normalize event-related schema identifiers. + +Compatibility: + +- V2 durable events and projections are experimental and are reset by `20260703090000_reset_v2_event_rename_sweep`; existing V2 event rows, event sequences, projected session messages, and admitted inputs are wiped. +- All renamed durable event types restart at version 1 under their normalized names. +- Generated Promise, Effect, and legacy JavaScript SDK surfaces were regenerated from the normalized schemas. diff --git a/specs/v2/session.md b/specs/v2/session.md index 15bc970b47..bb04c21356 100644 --- a/specs/v2/session.md +++ b/specs/v2/session.md @@ -114,7 +114,7 @@ Before each provider turn, the runner estimates the complete model-visible reque Compaction keeps the full transcript durable while replacing its active model representation with one hidden checkpoint containing a structured rolling summary and token-bounded serialized recent context. Provider-native assistant, reasoning, and tool messages never survive across the boundary, avoiding signature and encrypted-reasoning failures when the earlier prefix changes. -`session.next.compaction.started.1` durably identifies the attempt. Compaction deltas are live-only progress. `session.next.compaction.ended.1` durably stores the final summary and serialized recent context; only this completed event projects a model-visible compaction message. On the next provider attempt, the runner observes that completed compaction and directly renders a fresh Context Epoch baseline. A failed or interrupted attempt therefore leaves the previous history boundary active. +`compaction.started.1` durably identifies the attempt. Compaction deltas are live-only progress. `compaction.ended.1` durably stores the final summary and serialized recent context; only this completed event projects a model-visible compaction message. On the next provider attempt, the runner observes that completed compaction and directly renders a fresh Context Epoch baseline. A failed or interrupted attempt therefore leaves the previous history boundary active. Repeated compactions update the previous structured summary with newly compacted messages. The runner then reloads projected history and executes the original pending turn. @@ -170,9 +170,9 @@ The coordinator's active registry is also the source for `sessions.active()`. It Inbox promotion coalesces pending steers in durable admission order. Once continuation would otherwise end, it promotes one queued input at a time in FIFO order. Add explicit inbox backlog and steering-batch limits before exposing broad multi-caller admission or untrusted queue growth. -Eager local-tool execution is intentionally unbounded in the current local slice. This minimizes tool latency but does not increase SQLite settlement throughput: Session-event publication remains serialized per provider turn. Before broadening exposure, revisit per-turn call limits, output truncation, and operational backpressure using observed workloads. The `session.next.*` event schemas remain experimental and unshipped; databases created by earlier experimental builds are disposable rather than compatibility targets. +Eager local-tool execution is intentionally unbounded in the current local slice. This minimizes tool latency but does not increase SQLite settlement throughput: Session-event publication remains serialized per provider turn. Before broadening exposure, revisit per-turn call limits, output truncation, and operational backpressure using observed workloads. The normalized Session event schemas remain experimental and unshipped; databases created by earlier experimental builds are disposable rather than compatibility targets. -The synchronized `session.next.*` event family and projected Session-message model predate this branch. This slice refines their replay contract: projected Session messages retain their source aggregate sequence so canonical context ordering and `sessions.messages(...)` pagination follow durable event order even when caller-supplied IDs or timestamps do not. Consumers can use `sessions.log({ sessionID, after? })` to replay durable `session.next.*` events after an aggregate sequence cursor, then tail durable events without a race. Live-only text, reasoning, and tool-input fragments remain available through EventV2 subscriptions for connected renderers; they are intentionally absent from the replayable Session stream. +The normalized Session event family and projected Session-message model predate this branch. This slice refines their replay contract: projected Session messages retain their source aggregate sequence so canonical context ordering and `sessions.messages(...)` pagination follow durable event order even when caller-supplied IDs differ; event time is carried by the envelope `created` field rather than duplicated in payloads. Consumers can use `sessions.log({ sessionID, after? })` to replay durable Session events after an aggregate sequence cursor, then tail durable events without a race. Live-only text, reasoning, and tool-input fragments remain available through EventV2 subscriptions for connected renderers; they are intentionally absent from the replayable Session stream. The first `sessions.log(...)` contract is durable-only during both replay and live tailing. This keeps one cursor equal to one persisted aggregate sequence and is sufficient for reconnect-safe consumers. A later UI-facing API may optionally interleave live-only deltas while connected, but those fragments must remain explicitly ephemeral: they cannot advance the durable cursor, replay after reconnect, or be mistaken for publication boundaries.