session-event.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. export * as SessionEvent from "./session-event"
  2. import { Schema } from "effect"
  3. import { optional } from "./schema"
  4. import { Event } from "./event"
  5. import { ProviderMetadata, ToolContent } from "./llm"
  6. import { Delivery } from "./session-delivery"
  7. import { Model } from "./model"
  8. import { DateTimeUtcFromMillis, NonNegativeInt, RelativePath } from "./schema"
  9. import { FileAttachment, Prompt } from "./prompt"
  10. import { SessionID } from "./session-id"
  11. import { Location } from "./location"
  12. import { SessionMessage } from "./session-message"
  13. import { Revert } from "./revert"
  14. export { FileAttachment }
  15. export const Source = Schema.Struct({
  16. start: NonNegativeInt,
  17. end: NonNegativeInt,
  18. text: Schema.String,
  19. }).annotate({
  20. identifier: "session.next.event.source",
  21. })
  22. export interface Source extends Schema.Schema.Type<typeof Source> {}
  23. const Base = {
  24. timestamp: DateTimeUtcFromMillis,
  25. sessionID: SessionID,
  26. }
  27. const PromptFields = {
  28. ...Base,
  29. messageID: SessionMessage.ID,
  30. prompt: Prompt,
  31. delivery: Delivery,
  32. }
  33. const options = {
  34. durable: {
  35. aggregate: "sessionID",
  36. version: 1,
  37. },
  38. } as const
  39. const stepSettlementOptions = {
  40. durable: {
  41. aggregate: "sessionID",
  42. version: 2,
  43. },
  44. } as const
  45. export const UnknownError = SessionMessage.UnknownError
  46. export type UnknownError = SessionMessage.UnknownError
  47. export const AgentSwitched = Event.define({
  48. type: "session.next.agent.switched",
  49. ...options,
  50. schema: {
  51. ...Base,
  52. messageID: SessionMessage.ID,
  53. agent: Schema.String,
  54. },
  55. })
  56. export type AgentSwitched = typeof AgentSwitched.Type
  57. export const ModelSwitched = Event.define({
  58. type: "session.next.model.switched",
  59. ...options,
  60. schema: {
  61. ...Base,
  62. messageID: SessionMessage.ID,
  63. model: Model.Ref,
  64. },
  65. })
  66. export type ModelSwitched = typeof ModelSwitched.Type
  67. export const Moved = Event.define({
  68. type: "session.next.moved",
  69. ...options,
  70. schema: {
  71. ...Base,
  72. location: Location.Ref,
  73. subdirectory: RelativePath.pipe(optional),
  74. },
  75. })
  76. export type Moved = typeof Moved.Type
  77. export const Prompted = Event.define({
  78. type: "session.next.prompted",
  79. ...options,
  80. schema: PromptFields,
  81. })
  82. export type Prompted = typeof Prompted.Type
  83. export const PromptAdmitted = Event.define({
  84. type: "session.next.prompt.admitted",
  85. ...options,
  86. schema: PromptFields,
  87. })
  88. export type PromptAdmitted = typeof PromptAdmitted.Type
  89. export const ContextUpdated = Event.define({
  90. type: "session.next.context.updated",
  91. ...options,
  92. schema: {
  93. ...Base,
  94. messageID: SessionMessage.ID,
  95. text: Schema.String,
  96. },
  97. })
  98. export type ContextUpdated = typeof ContextUpdated.Type
  99. export const Synthetic = Event.define({
  100. type: "session.next.synthetic",
  101. ...options,
  102. schema: {
  103. ...Base,
  104. messageID: SessionMessage.ID,
  105. text: Schema.String,
  106. },
  107. })
  108. export type Synthetic = typeof Synthetic.Type
  109. export namespace Shell {
  110. export const Started = Event.define({
  111. type: "session.next.shell.started",
  112. ...options,
  113. schema: {
  114. ...Base,
  115. messageID: SessionMessage.ID,
  116. callID: Schema.String,
  117. command: Schema.String,
  118. },
  119. })
  120. export type Started = typeof Started.Type
  121. export const Ended = Event.define({
  122. type: "session.next.shell.ended",
  123. ...options,
  124. schema: {
  125. ...Base,
  126. callID: Schema.String,
  127. output: Schema.String,
  128. },
  129. })
  130. export type Ended = typeof Ended.Type
  131. }
  132. export namespace Step {
  133. export const Started = Event.define({
  134. type: "session.next.step.started",
  135. ...options,
  136. schema: {
  137. ...Base,
  138. assistantMessageID: SessionMessage.ID,
  139. agent: Schema.String,
  140. model: Model.Ref,
  141. snapshot: Schema.String.pipe(optional),
  142. },
  143. })
  144. export type Started = typeof Started.Type
  145. export const Ended = Event.define({
  146. type: "session.next.step.ended",
  147. ...stepSettlementOptions,
  148. schema: {
  149. ...Base,
  150. assistantMessageID: SessionMessage.ID,
  151. finish: Schema.String,
  152. cost: Schema.Finite,
  153. tokens: Schema.Struct({
  154. input: Schema.Finite,
  155. output: Schema.Finite,
  156. reasoning: Schema.Finite,
  157. cache: Schema.Struct({
  158. read: Schema.Finite,
  159. write: Schema.Finite,
  160. }),
  161. }),
  162. snapshot: Schema.String.pipe(optional),
  163. files: Schema.Array(RelativePath).pipe(optional),
  164. },
  165. })
  166. export type Ended = typeof Ended.Type
  167. export const Failed = Event.define({
  168. type: "session.next.step.failed",
  169. ...stepSettlementOptions,
  170. schema: {
  171. ...Base,
  172. assistantMessageID: SessionMessage.ID,
  173. error: UnknownError,
  174. },
  175. })
  176. export type Failed = typeof Failed.Type
  177. }
  178. export namespace Text {
  179. export const Started = Event.define({
  180. type: "session.next.text.started",
  181. ...options,
  182. schema: {
  183. ...Base,
  184. assistantMessageID: SessionMessage.ID,
  185. textID: Schema.String,
  186. },
  187. })
  188. export type Started = typeof Started.Type
  189. // Stream fragments are live-only; Text.Ended is the replayable full-value boundary.
  190. export const Delta = Event.define({
  191. type: "session.next.text.delta",
  192. schema: {
  193. ...Base,
  194. assistantMessageID: SessionMessage.ID,
  195. textID: Schema.String,
  196. delta: Schema.String,
  197. },
  198. })
  199. export type Delta = typeof Delta.Type
  200. export const Ended = Event.define({
  201. type: "session.next.text.ended",
  202. ...options,
  203. schema: {
  204. ...Base,
  205. assistantMessageID: SessionMessage.ID,
  206. textID: Schema.String,
  207. text: Schema.String,
  208. },
  209. })
  210. export type Ended = typeof Ended.Type
  211. }
  212. export namespace Reasoning {
  213. export const Started = Event.define({
  214. type: "session.next.reasoning.started",
  215. ...options,
  216. schema: {
  217. ...Base,
  218. assistantMessageID: SessionMessage.ID,
  219. reasoningID: Schema.String,
  220. providerMetadata: ProviderMetadata.pipe(optional),
  221. },
  222. })
  223. export type Started = typeof Started.Type
  224. // Stream fragments are live-only; Reasoning.Ended is the replayable full-value boundary.
  225. export const Delta = Event.define({
  226. type: "session.next.reasoning.delta",
  227. schema: {
  228. ...Base,
  229. assistantMessageID: SessionMessage.ID,
  230. reasoningID: Schema.String,
  231. delta: Schema.String,
  232. },
  233. })
  234. export type Delta = typeof Delta.Type
  235. export const Ended = Event.define({
  236. type: "session.next.reasoning.ended",
  237. ...options,
  238. schema: {
  239. ...Base,
  240. assistantMessageID: SessionMessage.ID,
  241. reasoningID: Schema.String,
  242. text: Schema.String,
  243. providerMetadata: ProviderMetadata.pipe(optional),
  244. },
  245. })
  246. export type Ended = typeof Ended.Type
  247. }
  248. export namespace Tool {
  249. const ToolBase = {
  250. ...Base,
  251. assistantMessageID: SessionMessage.ID,
  252. callID: Schema.String,
  253. }
  254. export namespace Input {
  255. export const Started = Event.define({
  256. type: "session.next.tool.input.started",
  257. ...options,
  258. schema: {
  259. ...ToolBase,
  260. name: Schema.String,
  261. },
  262. })
  263. export type Started = typeof Started.Type
  264. // Stream fragments are live-only; Input.Ended is the replayable raw-input boundary.
  265. export const Delta = Event.define({
  266. type: "session.next.tool.input.delta",
  267. schema: {
  268. ...ToolBase,
  269. delta: Schema.String,
  270. },
  271. })
  272. export type Delta = typeof Delta.Type
  273. export const Ended = Event.define({
  274. type: "session.next.tool.input.ended",
  275. ...options,
  276. schema: {
  277. ...ToolBase,
  278. text: Schema.String,
  279. },
  280. })
  281. export type Ended = typeof Ended.Type
  282. }
  283. export const Called = Event.define({
  284. type: "session.next.tool.called",
  285. ...options,
  286. schema: {
  287. ...ToolBase,
  288. tool: Schema.String,
  289. input: Schema.Record(Schema.String, Schema.Unknown),
  290. provider: Schema.Struct({
  291. executed: Schema.Boolean,
  292. metadata: ProviderMetadata.pipe(optional),
  293. }),
  294. },
  295. })
  296. export type Called = typeof Called.Type
  297. /**
  298. * Replayable bounded running-tool state. Tools should checkpoint semantic
  299. * transitions or at a bounded cadence, not persist every stdout/stderr chunk.
  300. */
  301. export const Progress = Event.define({
  302. type: "session.next.tool.progress",
  303. ...options,
  304. schema: {
  305. ...ToolBase,
  306. structured: Schema.Record(Schema.String, Schema.Unknown),
  307. content: Schema.Array(ToolContent),
  308. },
  309. })
  310. export type Progress = typeof Progress.Type
  311. export const Success = Event.define({
  312. type: "session.next.tool.success",
  313. ...options,
  314. schema: {
  315. ...ToolBase,
  316. structured: Schema.Record(Schema.String, Schema.Unknown),
  317. content: Schema.Array(ToolContent),
  318. outputPaths: Schema.Array(Schema.String).pipe(optional),
  319. result: Schema.Unknown.pipe(optional),
  320. provider: Schema.Struct({
  321. executed: Schema.Boolean,
  322. metadata: ProviderMetadata.pipe(optional),
  323. }),
  324. },
  325. })
  326. export type Success = typeof Success.Type
  327. export const Failed = Event.define({
  328. type: "session.next.tool.failed",
  329. ...options,
  330. schema: {
  331. ...ToolBase,
  332. error: UnknownError,
  333. result: Schema.Unknown.pipe(optional),
  334. provider: Schema.Struct({
  335. executed: Schema.Boolean,
  336. metadata: ProviderMetadata.pipe(optional),
  337. }),
  338. },
  339. })
  340. export type Failed = typeof Failed.Type
  341. }
  342. export const RetryError = Schema.Struct({
  343. message: Schema.String,
  344. statusCode: Schema.Finite.pipe(optional),
  345. isRetryable: Schema.Boolean,
  346. responseHeaders: Schema.Record(Schema.String, Schema.String).pipe(optional),
  347. responseBody: Schema.String.pipe(optional),
  348. metadata: Schema.Record(Schema.String, Schema.String).pipe(optional),
  349. }).annotate({
  350. identifier: "session.next.retry_error",
  351. })
  352. export interface RetryError extends Schema.Schema.Type<typeof RetryError> {}
  353. export const Retried = Event.define({
  354. type: "session.next.retried",
  355. ...options,
  356. schema: {
  357. ...Base,
  358. attempt: Schema.Finite,
  359. error: RetryError,
  360. },
  361. })
  362. export type Retried = typeof Retried.Type
  363. export namespace Compaction {
  364. export const Started = Event.define({
  365. type: "session.next.compaction.started",
  366. ...options,
  367. schema: {
  368. ...Base,
  369. messageID: SessionMessage.ID,
  370. reason: Schema.Union([Schema.Literal("auto"), Schema.Literal("manual")]),
  371. },
  372. })
  373. export type Started = typeof Started.Type
  374. export const Delta = Event.define({
  375. type: "session.next.compaction.delta",
  376. schema: {
  377. ...Base,
  378. messageID: SessionMessage.ID,
  379. text: Schema.String,
  380. },
  381. })
  382. export type Delta = typeof Delta.Type
  383. export const Ended = Event.define({
  384. type: "session.next.compaction.ended",
  385. ...options,
  386. schema: {
  387. ...Base,
  388. messageID: SessionMessage.ID,
  389. reason: Started.data.fields.reason,
  390. text: Schema.String,
  391. recent: Schema.String,
  392. },
  393. })
  394. export type Ended = typeof Ended.Type
  395. }
  396. export namespace RevertEvent {
  397. export const Staged = Event.define({
  398. type: "session.next.revert.staged",
  399. ...options,
  400. schema: { ...Base, revert: Revert.State },
  401. })
  402. export const Cleared = Event.define({ type: "session.next.revert.cleared", ...options, schema: Base })
  403. export const Committed = Event.define({
  404. type: "session.next.revert.committed",
  405. ...options,
  406. schema: { ...Base, messageID: SessionMessage.ID },
  407. })
  408. }
  409. export const DurableDefinitions = Event.inventory(
  410. AgentSwitched,
  411. ModelSwitched,
  412. Moved,
  413. Prompted,
  414. PromptAdmitted,
  415. ContextUpdated,
  416. Synthetic,
  417. Shell.Started,
  418. Shell.Ended,
  419. Step.Started,
  420. Step.Ended,
  421. Step.Failed,
  422. Text.Started,
  423. Text.Ended,
  424. Tool.Input.Started,
  425. Tool.Input.Ended,
  426. Tool.Called,
  427. Tool.Progress,
  428. Tool.Success,
  429. Tool.Failed,
  430. Reasoning.Started,
  431. Reasoning.Ended,
  432. Retried,
  433. Compaction.Started,
  434. Compaction.Ended,
  435. RevertEvent.Staged,
  436. RevertEvent.Cleared,
  437. RevertEvent.Committed,
  438. )
  439. export const Definitions = Event.inventory(
  440. AgentSwitched,
  441. ModelSwitched,
  442. Moved,
  443. Prompted,
  444. PromptAdmitted,
  445. ContextUpdated,
  446. Synthetic,
  447. Shell.Started,
  448. Shell.Ended,
  449. Step.Started,
  450. Step.Ended,
  451. Step.Failed,
  452. Text.Started,
  453. Text.Delta,
  454. Text.Ended,
  455. Reasoning.Started,
  456. Reasoning.Delta,
  457. Reasoning.Ended,
  458. Tool.Input.Started,
  459. Tool.Input.Delta,
  460. Tool.Input.Ended,
  461. Tool.Called,
  462. Tool.Progress,
  463. Tool.Success,
  464. Tool.Failed,
  465. Retried,
  466. Compaction.Started,
  467. Compaction.Delta,
  468. Compaction.Ended,
  469. RevertEvent.Staged,
  470. RevertEvent.Cleared,
  471. RevertEvent.Committed,
  472. )
  473. export const Durable = Schema.Union(DurableDefinitions, { mode: "oneOf" })
  474. .pipe(Schema.toTaggedUnion("type"))
  475. .annotate({ identifier: "SessionDurableEvent" })
  476. export type DurableEvent = typeof Durable.Type
  477. export const All = Schema.Union(Definitions, { mode: "oneOf" }).pipe(Schema.toTaggedUnion("type"))
  478. export type Event = typeof All.Type
  479. export type Type = Event["type"]