event-manifest.test.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import { describe, expect, test } from "bun:test"
  2. import {
  3. Agent,
  4. Config,
  5. FileSystem,
  6. Form,
  7. Integration,
  8. Permission,
  9. Project,
  10. Reference,
  11. Session,
  12. Workspace,
  13. } from "../src/index.js"
  14. import { EventManifest } from "../src/event-manifest.js"
  15. import { FileSystemV1 } from "../src/filesystem-v1.js"
  16. import { IdeEvent } from "../src/ide-event.js"
  17. import { McpEvent } from "../src/mcp-event.js"
  18. import { Plugin } from "../src/plugin.js"
  19. import { SessionEvent } from "../src/session-event.js"
  20. import { SessionID } from "../src/session-id.js"
  21. import { SessionMessage } from "../src/session-message.js"
  22. import { WorkspaceEvent } from "../src/workspace-event.js"
  23. describe("public event manifest", () => {
  24. test("owns the complete public event surface", () => {
  25. expect(EventManifest.ServerDefinitions).toContain(Agent.Event.Updated)
  26. expect(EventManifest.ServerDefinitions.filter((definition) => definition.type === "agent.updated")).toEqual([
  27. Agent.Event.Updated,
  28. ])
  29. expect(EventManifest.Definitions).toContain(Agent.Event.Updated)
  30. expect(EventManifest.Definitions.filter((definition) => definition.type === "agent.updated")).toEqual([
  31. Agent.Event.Updated,
  32. ])
  33. expect(Array.from(EventManifest.Latest.keys())).toEqual(
  34. Array.from(new Set(EventManifest.Definitions.map((definition) => definition.type))),
  35. )
  36. expect(EventManifest.Latest.get("agent.updated")).toBe(Agent.Event.Updated)
  37. expect(EventManifest.Latest.get("plugin.updated")).toBe(Plugin.Event.Updated)
  38. expect(EventManifest.Server.get("mcp.status.changed")).toBe(McpEvent.StatusChanged)
  39. expect(EventManifest.Server.get("mcp.resources.changed")).toBe(McpEvent.ResourcesChanged)
  40. expect(EventManifest.Server.get("session.created")).toBe(SessionEvent.Created)
  41. expect(EventManifest.Server.get("session.deleted")).toBe(SessionEvent.Deleted)
  42. expect(EventManifest.Server.has("mcp.tools.changed")).toBe(false)
  43. expect(Agent.Event.Updated.durable).toBeUndefined()
  44. expect(EventManifest.Durable.has("agent.updated")).toBe(false)
  45. })
  46. test("uses canonical definitions for current public events", () => {
  47. expect(Session.Event).toBe(SessionEvent)
  48. expect(Session.Event.Definitions).toBe(SessionEvent.Definitions)
  49. expect(Workspace.Event).toBe(WorkspaceEvent)
  50. expect(Workspace.Event.Definitions).toBe(WorkspaceEvent.Definitions)
  51. expect(EventManifest.Latest.get("session.step.ended")).toBe(SessionEvent.Step.Ended)
  52. expect(EventManifest.Latest.get("agent.updated")).toBe(Agent.Event.Updated)
  53. expect(EventManifest.Latest.get("project.updated")).toBe(Project.Event.Updated)
  54. expect(Agent.Event.Definitions).toEqual([Agent.Event.Updated])
  55. expect(Project.Event.Definitions).toEqual([Project.Event.Updated])
  56. expect(Config.Event.Definitions).toEqual([Config.Event.Updated])
  57. expect(FileSystem.Event.Definitions).toEqual([FileSystem.Event.Changed])
  58. expect(FileSystemV1.Event.Definitions).toEqual([FileSystemV1.Event.Edited])
  59. expect(Integration.Event.Definitions).toEqual([Integration.Event.Updated, Integration.Event.ConnectionUpdated])
  60. expect(Permission.Event.Definitions).toEqual([Permission.Event.Asked, Permission.Event.Replied])
  61. expect(Form.Event.Definitions).toEqual([Form.Event.Created, Form.Event.Replied, Form.Event.Cancelled])
  62. expect(Reference.Event.Definitions).toEqual([Reference.Event.Updated])
  63. expect(Plugin.Event.Definitions).toEqual([Plugin.Event.Added, Plugin.Event.Updated])
  64. expect(McpEvent.Definitions).toEqual([McpEvent.ToolsChanged, McpEvent.ResourcesChanged, McpEvent.StatusChanged])
  65. expect(EventManifest.Latest.has("mcp.browser.open.failed")).toBe(false)
  66. expect(EventManifest.Latest.has("ide.installed")).toBe(false)
  67. expect(IdeEvent.Definitions).toEqual([IdeEvent.Installed])
  68. expect(EventManifest.Durable.get("session.step.ended.1")).toBe(SessionEvent.Step.Ended)
  69. expect(EventManifest.Durable.has("session.step.ended.2")).toBe(false)
  70. })
  71. test("derives durable definitions from explicit definition durability", () => {
  72. expect(Array.from(EventManifest.Durable.keys()).toSorted()).toEqual(
  73. [
  74. "session.created.1",
  75. "session.deleted.2",
  76. "session.agent.selected.1",
  77. "session.model.selected.1",
  78. "session.moved.1",
  79. "session.renamed.1",
  80. "session.usage.recorded.1",
  81. "session.forked.2",
  82. "session.input.promoted.1",
  83. "session.input.admitted.1",
  84. "session.input.cancelled.1",
  85. "session.input.steered.1",
  86. "session.input.queued.1",
  87. "session.execution.started.1",
  88. "session.execution.succeeded.1",
  89. "session.execution.failed.1",
  90. "session.execution.interrupted.1",
  91. "session.instructions.updated.2",
  92. "session.synthetic.1",
  93. "session.skill.activated.1",
  94. "session.shell.started.1",
  95. "session.shell.ended.1",
  96. "session.step.started.1",
  97. "session.step.ended.1",
  98. "session.step.failed.1",
  99. "session.text.started.1",
  100. "session.text.ended.1",
  101. "session.tool.input.started.1",
  102. "session.tool.input.ended.1",
  103. "session.tool.called.1",
  104. "session.tool.success.2",
  105. "session.tool.failed.2",
  106. "session.reasoning.started.1",
  107. "session.reasoning.ended.1",
  108. "session.retry.scheduled.1",
  109. "session.compaction.admitted.1",
  110. "session.compaction.started.1",
  111. "session.compaction.ended.1",
  112. "session.compaction.failed.1",
  113. "session.revert.staged.1",
  114. "session.revert.cleared.1",
  115. "session.revert.committed.1",
  116. ].toSorted(),
  117. )
  118. expect(SessionEvent.DurableDefinitions).toEqual([
  119. ...SessionEvent.Definitions.filter((definition) => definition.durability === "durable"),
  120. SessionEvent.UsageRecorded,
  121. ])
  122. expect(SessionEvent.UsageRecorded.durability).toBe("durable")
  123. expect(EventManifest.Durable.get("session.usage.recorded.1")).toBe(SessionEvent.UsageRecorded)
  124. expect(SessionEvent.Definitions).not.toContain(SessionEvent.UsageRecorded)
  125. expect(EventManifest.Definitions).not.toContain(SessionEvent.UsageRecorded)
  126. expect(EventManifest.ServerDefinitions).not.toContain(SessionEvent.UsageRecorded)
  127. expect(EventManifest.Latest.has("session.usage.recorded")).toBe(false)
  128. expect(SessionEvent.UsageUpdated.durability).toBe("ephemeral")
  129. expect(SessionEvent.Compaction.Delta.durability).toBe("ephemeral")
  130. expect(SessionEvent.Tool.Progress.durability).toBe("ephemeral")
  131. expect(EventManifest.Server.get("session.tool.progress")).toBe(SessionEvent.Tool.Progress)
  132. expect(EventManifest.Durable.has("session.compaction.delta.1")).toBe(false)
  133. expect(EventManifest.ServerDefinitions).toContain(SessionEvent.UsageUpdated)
  134. expect(EventManifest.Definitions.every((definition) => definition.durability !== undefined)).toBe(true)
  135. })
  136. test("uses the current Session skill event as durable version 1", () => {
  137. expect(EventManifest.Durable.get("session.skill.activated.1")).toBe(SessionEvent.Skill.Activated)
  138. expect(EventManifest.Latest.get("session.skill.activated")).toBe(SessionEvent.Skill.Activated)
  139. })
  140. test("keeps simplified session fragment and tool payloads on durable version 1", () => {
  141. const sessionID = SessionID.make("ses_test")
  142. const assistantMessageID = SessionMessage.ID.make("msg_test")
  143. const text = SessionEvent.Text.Started.data.make({ sessionID, assistantMessageID, ordinal: 0 })
  144. const reasoning = SessionEvent.Reasoning.Ended.data.make({
  145. sessionID,
  146. assistantMessageID,
  147. ordinal: 0,
  148. text: "thought",
  149. state: { signature: "sig" },
  150. })
  151. const tool = SessionEvent.Tool.Called.data.make({
  152. sessionID,
  153. assistantMessageID,
  154. id: "call_test",
  155. input: {},
  156. executed: true,
  157. state: { itemId: "item_test" },
  158. })
  159. expect(text).not.toHaveProperty("textID")
  160. expect(reasoning).not.toHaveProperty("reasoningID")
  161. expect(reasoning).not.toHaveProperty("providerMetadata")
  162. expect(tool).not.toHaveProperty("tool")
  163. expect(tool).not.toHaveProperty("provider")
  164. expect(SessionEvent.Text.Started.durable?.version).toBe(1)
  165. expect(SessionEvent.Tool.Called.durable?.version).toBe(1)
  166. })
  167. test("keeps current session deletion minimal", () => {
  168. const sessionID = SessionID.make("ses_test")
  169. expect(SessionEvent.Deleted.data.make({ sessionID })).toEqual({ sessionID })
  170. expect(SessionEvent.Deleted.durable?.version).toBe(2)
  171. })
  172. })