session-event.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. export * as SessionEvent from "./session-event.js"
  2. import { Schema } from "effect"
  3. import { optional } from "./schema.js"
  4. import { Event } from "./event.js"
  5. import { FinishReason } from "./llm.js"
  6. import { Content } from "./tool.js"
  7. import { Model } from "./model.js"
  8. import { NonNegativeInt, PositiveInt, RelativePath } from "./schema.js"
  9. import { FileAttachment } from "./prompt.js"
  10. import { SessionID } from "./session-id.js"
  11. import { Location } from "./location.js"
  12. import { SessionMessage } from "./session-message.js"
  13. import { Revert } from "./session-revert.js"
  14. import { Shell as ShellSchema } from "./shell.js"
  15. import { SessionError } from "./session-error.js"
  16. import { Instruction } from "./instruction.js"
  17. import { Agent } from "./agent.js"
  18. import { Skill as SkillSchema } from "./skill.js"
  19. import { Money } from "./money.js"
  20. import { Snapshot } from "./snapshot.js"
  21. import { TokenUsage } from "./token-usage.js"
  22. import { SessionPending } from "./session-pending.js"
  23. import { Project } from "./project.js"
  24. import { SessionFork } from "./session-fork.js"
  25. export { FileAttachment }
  26. export const Source = Schema.Struct({
  27. start: NonNegativeInt,
  28. end: NonNegativeInt,
  29. text: Schema.String,
  30. }).annotate({
  31. identifier: "Session.Event.Source",
  32. })
  33. export interface Source extends Schema.Schema.Type<typeof Source> {}
  34. const Base = {
  35. sessionID: SessionID,
  36. }
  37. const options = {
  38. durable: {
  39. aggregate: "sessionID",
  40. version: 1,
  41. },
  42. } as const
  43. export const Created = Event.durable({
  44. type: "session.created",
  45. ...options,
  46. schema: {
  47. ...Base,
  48. projectID: Project.ID,
  49. location: Location.Ref,
  50. subpath: RelativePath.pipe(optional),
  51. parentID: SessionID.pipe(optional),
  52. slug: Schema.String,
  53. title: Schema.String.pipe(optional),
  54. agent: Agent.ID.pipe(optional),
  55. model: Model.Ref.pipe(optional),
  56. version: Schema.String,
  57. },
  58. })
  59. export type Created = typeof Created.Type
  60. export const AgentSelected = Event.durable({
  61. type: "session.agent.selected",
  62. ...options,
  63. schema: {
  64. ...Base,
  65. agent: Agent.ID,
  66. },
  67. })
  68. export type AgentSelected = typeof AgentSelected.Type
  69. export const ModelSelected = Event.durable({
  70. type: "session.model.selected",
  71. ...options,
  72. schema: {
  73. ...Base,
  74. model: Model.Ref,
  75. },
  76. })
  77. export type ModelSelected = typeof ModelSelected.Type
  78. export const Moved = Event.durable({
  79. type: "session.moved",
  80. ...options,
  81. schema: {
  82. ...Base,
  83. location: Location.Ref,
  84. projectID: Project.ID.pipe(optional),
  85. subpath: RelativePath.pipe(optional),
  86. },
  87. })
  88. export type Moved = typeof Moved.Type
  89. export const Renamed = Event.durable({
  90. type: "session.renamed",
  91. ...options,
  92. schema: {
  93. ...Base,
  94. title: Schema.String,
  95. },
  96. })
  97. export type Renamed = typeof Renamed.Type
  98. export const UsageRecorded = Event.durable({
  99. type: "session.usage.recorded",
  100. ...options,
  101. schema: {
  102. ...Base,
  103. source: Schema.Literals(["title", "compaction"]),
  104. cost: Money.USD,
  105. tokens: TokenUsage.Info,
  106. },
  107. })
  108. export type UsageRecorded = typeof UsageRecorded.Type
  109. export const UsageUpdated = Event.ephemeral({
  110. type: "session.usage.updated",
  111. schema: {
  112. ...Base,
  113. cost: Money.USD,
  114. tokens: TokenUsage.Info,
  115. },
  116. })
  117. export type UsageUpdated = typeof UsageUpdated.Type
  118. export const Deleted = Event.durable({
  119. type: "session.deleted",
  120. durable: {
  121. aggregate: "sessionID",
  122. version: 2,
  123. },
  124. schema: Base,
  125. })
  126. export type Deleted = typeof Deleted.Type
  127. export const Forked = Event.durable({
  128. type: "session.forked",
  129. durable: {
  130. aggregate: "sessionID",
  131. version: 2,
  132. },
  133. schema: {
  134. ...Base,
  135. parentID: SessionID,
  136. boundary: SessionFork.Boundary,
  137. instructions: Instruction.Values.pipe(optional),
  138. },
  139. })
  140. export type Forked = typeof Forked.Type
  141. const InputRef = {
  142. ...Base,
  143. inputID: SessionMessage.ID,
  144. }
  145. export const InputPromoted = Event.durable({
  146. type: "session.input.promoted",
  147. ...options,
  148. schema: InputRef,
  149. })
  150. export type InputPromoted = typeof InputPromoted.Type
  151. export const InputAdmitted = Event.durable({
  152. type: "session.input.admitted",
  153. ...options,
  154. schema: {
  155. ...InputRef,
  156. input: SessionPending.Message,
  157. },
  158. })
  159. export type InputAdmitted = typeof InputAdmitted.Type
  160. export const InputCancelled = Event.durable({
  161. type: "session.input.cancelled",
  162. ...options,
  163. schema: InputRef,
  164. })
  165. export type InputCancelled = typeof InputCancelled.Type
  166. export const InputSteered = Event.durable({
  167. type: "session.input.steered",
  168. ...options,
  169. schema: InputRef,
  170. })
  171. export type InputSteered = typeof InputSteered.Type
  172. export const InputQueued = Event.durable({
  173. type: "session.input.queued",
  174. ...options,
  175. schema: InputRef,
  176. })
  177. export type InputQueued = typeof InputQueued.Type
  178. export namespace Execution {
  179. export const Started = Event.durable({ type: "session.execution.started", ...options, schema: Base })
  180. export type Started = typeof Started.Type
  181. export const Succeeded = Event.durable({ type: "session.execution.succeeded", ...options, schema: Base })
  182. export type Succeeded = typeof Succeeded.Type
  183. export const Failed = Event.durable({
  184. type: "session.execution.failed",
  185. ...options,
  186. schema: { ...Base, error: SessionError.Error },
  187. })
  188. export type Failed = typeof Failed.Type
  189. export const Interrupted = Event.durable({
  190. type: "session.execution.interrupted",
  191. ...options,
  192. schema: { ...Base, reason: Schema.Literals(["user", "shutdown", "superseded"]) },
  193. })
  194. export type Interrupted = typeof Interrupted.Type
  195. }
  196. export const InstructionsUpdated = Event.durable({
  197. type: "session.instructions.updated",
  198. durable: {
  199. aggregate: "sessionID",
  200. version: 2,
  201. },
  202. schema: {
  203. ...Base,
  204. delta: Instruction.Delta,
  205. /**
  206. * The rendered chronological update shown to the model, frozen at emit time.
  207. * Absent for the initial baseline observation and for deltas that render empty.
  208. */
  209. text: Schema.String.pipe(optional),
  210. },
  211. })
  212. export type InstructionsUpdated = typeof InstructionsUpdated.Type
  213. export const Synthetic = Event.durable({
  214. type: "session.synthetic",
  215. ...options,
  216. schema: {
  217. ...Base,
  218. text: Schema.String,
  219. description: Schema.String.pipe(optional),
  220. metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(optional),
  221. },
  222. })
  223. export type Synthetic = typeof Synthetic.Type
  224. export namespace Skill {
  225. export const Activated = Event.durable({
  226. type: "session.skill.activated",
  227. ...options,
  228. schema: {
  229. ...Base,
  230. id: SkillSchema.ID,
  231. name: SkillSchema.Name,
  232. text: Schema.String,
  233. },
  234. })
  235. export type Activated = typeof Activated.Type
  236. }
  237. export namespace Shell {
  238. export const Started = Event.durable({
  239. type: "session.shell.started",
  240. ...options,
  241. schema: {
  242. ...Base,
  243. shell: ShellSchema.Info,
  244. },
  245. })
  246. export type Started = typeof Started.Type
  247. export const Ended = Event.durable({
  248. type: "session.shell.ended",
  249. ...options,
  250. schema: {
  251. ...Base,
  252. shell: ShellSchema.Info,
  253. output: ShellSchema.Output,
  254. },
  255. })
  256. export type Ended = typeof Ended.Type
  257. }
  258. export namespace Step {
  259. export const Started = Event.durable({
  260. type: "session.step.started",
  261. ...options,
  262. schema: {
  263. ...Base,
  264. assistantMessageID: SessionMessage.ID,
  265. agent: Agent.ID,
  266. model: Model.Ref,
  267. snapshot: Snapshot.ID.pipe(optional),
  268. },
  269. })
  270. export type Started = typeof Started.Type
  271. export const Ended = Event.durable({
  272. type: "session.step.ended",
  273. ...options,
  274. schema: {
  275. ...Base,
  276. assistantMessageID: SessionMessage.ID,
  277. finish: FinishReason,
  278. cost: Money.USD,
  279. tokens: TokenUsage.Info,
  280. snapshot: Snapshot.ID.pipe(optional),
  281. files: Schema.Array(RelativePath).pipe(optional),
  282. },
  283. })
  284. export type Ended = typeof Ended.Type
  285. export const Failed = Event.durable({
  286. type: "session.step.failed",
  287. ...options,
  288. schema: {
  289. ...Base,
  290. assistantMessageID: SessionMessage.ID,
  291. error: SessionError.Error,
  292. cost: Money.USD.pipe(optional),
  293. tokens: TokenUsage.Info.pipe(optional),
  294. snapshot: Snapshot.ID.pipe(optional),
  295. files: Schema.Array(RelativePath).pipe(optional),
  296. },
  297. })
  298. export type Failed = typeof Failed.Type
  299. }
  300. export namespace Text {
  301. export const Started = Event.durable({
  302. type: "session.text.started",
  303. ...options,
  304. schema: {
  305. ...Base,
  306. assistantMessageID: SessionMessage.ID,
  307. ordinal: NonNegativeInt,
  308. },
  309. })
  310. export type Started = typeof Started.Type
  311. // Stream fragments are live-only; Text.Ended is the replayable full-value boundary.
  312. export const Delta = Event.ephemeral({
  313. type: "session.text.delta",
  314. schema: {
  315. ...Base,
  316. assistantMessageID: SessionMessage.ID,
  317. ordinal: NonNegativeInt,
  318. delta: Schema.String,
  319. },
  320. })
  321. export type Delta = typeof Delta.Type
  322. export const Ended = Event.durable({
  323. type: "session.text.ended",
  324. ...options,
  325. schema: {
  326. ...Base,
  327. assistantMessageID: SessionMessage.ID,
  328. ordinal: NonNegativeInt,
  329. text: Schema.String,
  330. state: SessionMessage.ProviderState.pipe(optional),
  331. },
  332. })
  333. export type Ended = typeof Ended.Type
  334. }
  335. export namespace Reasoning {
  336. export const Started = Event.durable({
  337. type: "session.reasoning.started",
  338. ...options,
  339. schema: {
  340. ...Base,
  341. assistantMessageID: SessionMessage.ID,
  342. ordinal: NonNegativeInt,
  343. state: SessionMessage.ProviderState.pipe(optional),
  344. },
  345. })
  346. export type Started = typeof Started.Type
  347. // Stream fragments are live-only; Reasoning.Ended is the replayable full-value boundary.
  348. export const Delta = Event.ephemeral({
  349. type: "session.reasoning.delta",
  350. schema: {
  351. ...Base,
  352. assistantMessageID: SessionMessage.ID,
  353. ordinal: NonNegativeInt,
  354. delta: Schema.String,
  355. },
  356. })
  357. export type Delta = typeof Delta.Type
  358. export const Ended = Event.durable({
  359. type: "session.reasoning.ended",
  360. ...options,
  361. schema: {
  362. ...Base,
  363. assistantMessageID: SessionMessage.ID,
  364. ordinal: NonNegativeInt,
  365. text: Schema.String,
  366. state: SessionMessage.ProviderState.pipe(optional),
  367. },
  368. })
  369. export type Ended = typeof Ended.Type
  370. }
  371. export namespace Tool {
  372. const ToolBase = {
  373. ...Base,
  374. assistantMessageID: SessionMessage.ID,
  375. id: Schema.String,
  376. }
  377. export namespace Input {
  378. export const Started = Event.durable({
  379. type: "session.tool.input.started",
  380. ...options,
  381. schema: {
  382. ...ToolBase,
  383. name: Schema.String,
  384. },
  385. })
  386. export type Started = typeof Started.Type
  387. // Stream fragments are live-only; Input.Ended is the replayable raw-input boundary.
  388. export const Delta = Event.ephemeral({
  389. type: "session.tool.input.delta",
  390. schema: {
  391. ...ToolBase,
  392. delta: Schema.String,
  393. },
  394. })
  395. export type Delta = typeof Delta.Type
  396. export const Ended = Event.durable({
  397. type: "session.tool.input.ended",
  398. ...options,
  399. schema: {
  400. ...ToolBase,
  401. text: Schema.String,
  402. },
  403. })
  404. export type Ended = typeof Ended.Type
  405. }
  406. export const Called = Event.durable({
  407. type: "session.tool.called",
  408. ...options,
  409. schema: {
  410. ...ToolBase,
  411. input: Schema.Record(Schema.String, Schema.Unknown),
  412. executed: Schema.Boolean,
  413. state: SessionMessage.ProviderState.pipe(optional),
  414. },
  415. })
  416. export type Called = typeof Called.Type
  417. /** Live replacement metadata for a running tool. */
  418. export const Progress = Event.ephemeral({
  419. type: "session.tool.progress",
  420. schema: {
  421. ...ToolBase,
  422. metadata: Schema.Record(Schema.String, Schema.Json),
  423. },
  424. })
  425. export type Progress = typeof Progress.Type
  426. /** Canonical terminal success: one non-empty model representation plus optional UI metadata. */
  427. export const Success = Event.durable({
  428. type: "session.tool.success",
  429. durable: {
  430. aggregate: "sessionID",
  431. version: 2,
  432. },
  433. schema: {
  434. ...ToolBase,
  435. content: Schema.NonEmptyArray(Content),
  436. metadata: Schema.Record(Schema.String, Schema.Json).pipe(optional),
  437. executed: Schema.Boolean,
  438. resultState: SessionMessage.ProviderState.pipe(optional),
  439. },
  440. })
  441. export type Success = typeof Success.Type
  442. /**
  443. * Canonical terminal failure: one error plus the final bounded snapshot of
  444. * partial progress. The event is self-contained; projection never reaches
  445. * into ephemeral progress history.
  446. */
  447. export const Failed = Event.durable({
  448. type: "session.tool.failed",
  449. durable: {
  450. aggregate: "sessionID",
  451. version: 2,
  452. },
  453. schema: {
  454. ...ToolBase,
  455. error: SessionError.Error,
  456. content: Schema.NonEmptyArray(Content).pipe(optional),
  457. metadata: Schema.Record(Schema.String, Schema.Json).pipe(optional),
  458. executed: Schema.Boolean,
  459. resultState: SessionMessage.ProviderState.pipe(optional),
  460. },
  461. })
  462. export type Failed = typeof Failed.Type
  463. }
  464. export const RetryScheduled = Event.durable({
  465. type: "session.retry.scheduled",
  466. ...options,
  467. schema: {
  468. ...Base,
  469. assistantMessageID: SessionMessage.ID,
  470. attempt: PositiveInt,
  471. at: NonNegativeInt,
  472. error: SessionError.Error,
  473. },
  474. })
  475. export type RetryScheduled = typeof RetryScheduled.Type
  476. export namespace Compaction {
  477. export const Admitted = Event.durable({
  478. type: "session.compaction.admitted",
  479. ...options,
  480. schema: {
  481. ...Base,
  482. inputID: SessionMessage.ID,
  483. },
  484. })
  485. export type Admitted = typeof Admitted.Type
  486. export const Started = Event.durable({
  487. type: "session.compaction.started",
  488. ...options,
  489. schema: {
  490. ...Base,
  491. reason: Schema.Literals(["auto", "manual"]),
  492. recent: Schema.String,
  493. inputID: SessionMessage.ID.pipe(optional),
  494. },
  495. })
  496. export type Started = typeof Started.Type
  497. export const Delta = Event.ephemeral({
  498. type: "session.compaction.delta",
  499. schema: {
  500. ...Base,
  501. text: Schema.String,
  502. },
  503. })
  504. export type Delta = typeof Delta.Type
  505. export const Ended = Event.durable({
  506. type: "session.compaction.ended",
  507. ...options,
  508. schema: {
  509. ...Base,
  510. reason: Started.data.fields.reason,
  511. text: Schema.String,
  512. recent: Schema.String,
  513. },
  514. })
  515. export type Ended = typeof Ended.Type
  516. export const Failed = Event.durable({
  517. type: "session.compaction.failed",
  518. ...options,
  519. schema: {
  520. ...Base,
  521. reason: Started.data.fields.reason,
  522. error: SessionError.Error,
  523. inputID: SessionMessage.ID.pipe(optional),
  524. },
  525. })
  526. export type Failed = typeof Failed.Type
  527. }
  528. export namespace RevertEvent {
  529. export const Staged = Event.durable({
  530. type: "session.revert.staged",
  531. ...options,
  532. schema: { ...Base, revert: Revert },
  533. })
  534. export const Cleared = Event.durable({ type: "session.revert.cleared", ...options, schema: Base })
  535. export const Committed = Event.durable({
  536. type: "session.revert.committed",
  537. ...options,
  538. schema: { ...Base, to: SessionMessage.ID },
  539. })
  540. }
  541. export const Definitions = Event.inventory(
  542. Created,
  543. AgentSelected,
  544. ModelSelected,
  545. Moved,
  546. Renamed,
  547. UsageUpdated,
  548. Deleted,
  549. Forked,
  550. InputPromoted,
  551. InputAdmitted,
  552. InputCancelled,
  553. InputSteered,
  554. InputQueued,
  555. Execution.Started,
  556. Execution.Succeeded,
  557. Execution.Failed,
  558. Execution.Interrupted,
  559. InstructionsUpdated,
  560. Synthetic,
  561. Skill.Activated,
  562. Shell.Started,
  563. Shell.Ended,
  564. Step.Started,
  565. Step.Ended,
  566. Step.Failed,
  567. Text.Started,
  568. Text.Delta,
  569. Text.Ended,
  570. Reasoning.Started,
  571. Reasoning.Delta,
  572. Reasoning.Ended,
  573. Tool.Input.Started,
  574. Tool.Input.Delta,
  575. Tool.Input.Ended,
  576. Tool.Called,
  577. Tool.Progress,
  578. Tool.Success,
  579. Tool.Failed,
  580. RetryScheduled,
  581. Compaction.Admitted,
  582. Compaction.Started,
  583. Compaction.Delta,
  584. Compaction.Ended,
  585. Compaction.Failed,
  586. RevertEvent.Staged,
  587. RevertEvent.Cleared,
  588. RevertEvent.Committed,
  589. )
  590. // UsageRecorded is durable but internal: excluded from Definitions so it never reaches the public manifest.
  591. export const DurableDefinitions = Event.inventory(
  592. ...Definitions.filter((definition) => definition.durability === "durable"),
  593. UsageRecorded,
  594. )
  595. export const EphemeralDefinitions = Event.inventory(
  596. ...Definitions.filter((definition) => definition.durability === "ephemeral"),
  597. )
  598. export const Durable = Schema.Union(DurableDefinitions, { mode: "oneOf" })
  599. .pipe(Schema.toTaggedUnion("type"))
  600. .annotate({ identifier: "Session.Event.Durable" })
  601. export type DurableEvent = typeof Durable.Type
  602. export const All = Schema.Union([Durable, ...EphemeralDefinitions], { mode: "oneOf" }).pipe(
  603. Schema.toTaggedUnion("type"),
  604. )
  605. export type Event = typeof All.Type
  606. export type Type = Event["type"]