event-manifest.test.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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(EventManifest.Server.has("question.asked")).toBe(false)
  44. expect(EventManifest.Server.has("question.replied")).toBe(false)
  45. expect(EventManifest.Server.has("question.rejected")).toBe(false)
  46. expect(Agent.Event.Updated.durable).toBeUndefined()
  47. expect(EventManifest.Durable.has("agent.updated")).toBe(false)
  48. })
  49. test("uses canonical definitions for current public events", () => {
  50. expect(Session.Event).toBe(SessionEvent)
  51. expect(Session.Event.Definitions).toBe(SessionEvent.Definitions)
  52. expect(Workspace.Event).toBe(WorkspaceEvent)
  53. expect(Workspace.Event.Definitions).toBe(WorkspaceEvent.Definitions)
  54. expect(EventManifest.Latest.get("session.step.ended")).toBe(SessionEvent.Step.Ended)
  55. expect(EventManifest.Latest.get("agent.updated")).toBe(Agent.Event.Updated)
  56. expect(EventManifest.Latest.get("project.updated")).toBe(Project.Event.Updated)
  57. expect(Agent.Event.Definitions).toEqual([Agent.Event.Updated])
  58. expect(Project.Event.Definitions).toEqual([Project.Event.Updated])
  59. expect(Config.Event.Definitions).toEqual([Config.Event.Updated])
  60. expect(FileSystem.Event.Definitions).toEqual([FileSystem.Event.Changed])
  61. expect(FileSystemV1.Event.Definitions).toEqual([FileSystemV1.Event.Edited])
  62. expect(Integration.Event.Definitions).toEqual([Integration.Event.Updated, Integration.Event.ConnectionUpdated])
  63. expect(Permission.Event.Definitions).toEqual([Permission.Event.Asked, Permission.Event.Replied])
  64. expect(Form.Event.Definitions).toEqual([Form.Event.Created, Form.Event.Replied, Form.Event.Cancelled])
  65. expect(Reference.Event.Definitions).toEqual([Reference.Event.Updated])
  66. expect(Plugin.Event.Definitions).toEqual([Plugin.Event.Added, Plugin.Event.Updated])
  67. expect(McpEvent.Definitions).toEqual([McpEvent.ToolsChanged, McpEvent.ResourcesChanged, McpEvent.StatusChanged])
  68. expect(EventManifest.Latest.has("mcp.browser.open.failed")).toBe(false)
  69. expect(EventManifest.Latest.has("ide.installed")).toBe(false)
  70. expect(IdeEvent.Definitions).toEqual([IdeEvent.Installed])
  71. expect(EventManifest.Durable.get("session.step.ended.1")).toBe(SessionEvent.Step.Ended)
  72. expect(EventManifest.Durable.has("session.step.ended.2")).toBe(false)
  73. })
  74. test("derives durable definitions from explicit definition durability", () => {
  75. expect(Array.from(EventManifest.Durable.keys()).toSorted()).toEqual(
  76. [
  77. "session.created.1",
  78. "session.deleted.2",
  79. "session.agent.selected.1",
  80. "session.model.selected.1",
  81. "session.moved.1",
  82. "session.renamed.1",
  83. "session.usage.recorded.1",
  84. "session.forked.2",
  85. "session.inbox.delivered.1",
  86. "session.inbox.enqueued.1",
  87. "session.inbox.cancelled.1",
  88. "session.inbox.delivery.changed.1",
  89. "session.execution.started.1",
  90. "session.execution.succeeded.1",
  91. "session.execution.failed.1",
  92. "session.execution.interrupted.1",
  93. "session.instructions.updated.2",
  94. "session.synthetic.1",
  95. "session.skill.activated.1",
  96. "session.shell.started.1",
  97. "session.shell.ended.1",
  98. "session.step.started.1",
  99. "session.step.ended.1",
  100. "session.step.failed.1",
  101. "session.text.started.1",
  102. "session.text.ended.1",
  103. "session.tool.input.started.1",
  104. "session.tool.input.ended.1",
  105. "session.tool.called.1",
  106. "session.tool.success.2",
  107. "session.tool.failed.2",
  108. "session.reasoning.started.1",
  109. "session.reasoning.ended.1",
  110. "session.retry.scheduled.1",
  111. "session.compaction.started.1",
  112. "session.compaction.ended.1",
  113. "session.compaction.failed.1",
  114. "session.revert.staged.1",
  115. "session.revert.cleared.1",
  116. "session.revert.committed.1",
  117. "worktree.resolved.1",
  118. ].toSorted(),
  119. )
  120. expect(SessionEvent.DurableDefinitions).toEqual([
  121. ...SessionEvent.Definitions.filter((definition) => definition.durability === "durable"),
  122. SessionEvent.UsageRecorded,
  123. ])
  124. expect(SessionEvent.UsageRecorded.durability).toBe("durable")
  125. expect(EventManifest.Durable.get("session.usage.recorded.1")).toBe(SessionEvent.UsageRecorded)
  126. expect(SessionEvent.Definitions).not.toContain(SessionEvent.UsageRecorded)
  127. expect(EventManifest.Definitions).not.toContain(SessionEvent.UsageRecorded)
  128. expect(EventManifest.ServerDefinitions).not.toContain(SessionEvent.UsageRecorded)
  129. expect(EventManifest.Latest.has("session.usage.recorded")).toBe(false)
  130. expect(SessionEvent.UsageUpdated.durability).toBe("ephemeral")
  131. expect(SessionEvent.Compaction.Delta.durability).toBe("ephemeral")
  132. expect(SessionEvent.Tool.Progress.durability).toBe("ephemeral")
  133. expect(EventManifest.Server.get("session.tool.progress")).toBe(SessionEvent.Tool.Progress)
  134. expect(EventManifest.Durable.has("session.compaction.delta.1")).toBe(false)
  135. expect(EventManifest.ServerDefinitions).toContain(SessionEvent.UsageUpdated)
  136. expect(EventManifest.Definitions.every((definition) => definition.durability !== undefined)).toBe(true)
  137. })
  138. test("uses the current Session skill event as durable version 1", () => {
  139. expect(EventManifest.Durable.get("session.skill.activated.1")).toBe(SessionEvent.Skill.Activated)
  140. expect(EventManifest.Latest.get("session.skill.activated")).toBe(SessionEvent.Skill.Activated)
  141. })
  142. test("keeps simplified session fragment and tool payloads on durable version 1", () => {
  143. const sessionID = SessionID.make("ses_test")
  144. const assistantMessageID = SessionMessage.ID.make("msg_test")
  145. const text = SessionEvent.Text.Started.data.make({ sessionID, assistantMessageID, ordinal: 0 })
  146. const reasoning = SessionEvent.Reasoning.Ended.data.make({
  147. sessionID,
  148. assistantMessageID,
  149. ordinal: 0,
  150. text: "thought",
  151. state: { signature: "sig" },
  152. })
  153. const tool = SessionEvent.Tool.Called.data.make({
  154. sessionID,
  155. assistantMessageID,
  156. id: "call_test",
  157. input: {},
  158. executed: true,
  159. state: { itemId: "item_test" },
  160. })
  161. expect(text).not.toHaveProperty("textID")
  162. expect(reasoning).not.toHaveProperty("reasoningID")
  163. expect(reasoning).not.toHaveProperty("providerMetadata")
  164. expect(tool).not.toHaveProperty("tool")
  165. expect(tool).not.toHaveProperty("provider")
  166. expect(SessionEvent.Text.Started.durable?.version).toBe(1)
  167. expect(SessionEvent.Tool.Called.durable?.version).toBe(1)
  168. })
  169. test("keeps current session deletion minimal", () => {
  170. const sessionID = SessionID.make("ses_test")
  171. expect(SessionEvent.Deleted.data.make({ sessionID })).toEqual({ sessionID })
  172. expect(SessionEvent.Deleted.durable?.version).toBe(2)
  173. })
  174. })