stream.test.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { describe, expect, test } from "bun:test"
  2. import { writeSessionOutput } from "@/cli/cmd/run/stream"
  3. import type { FooterApi, FooterEvent, StreamCommit } from "@/cli/cmd/run/types"
  4. function footer() {
  5. const events: FooterEvent[] = []
  6. const commits: StreamCommit[] = []
  7. const api: FooterApi = {
  8. isClosed: false,
  9. onPrompt: () => () => {},
  10. onClose: () => () => {},
  11. event: (next) => {
  12. events.push(next)
  13. },
  14. append: (next) => {
  15. commits.push(next)
  16. },
  17. idle: () => Promise.resolve(),
  18. close: () => {},
  19. destroy: () => {},
  20. }
  21. return { api, events, commits }
  22. }
  23. describe("run stream bridge", () => {
  24. test("forwards commits in order", () => {
  25. const out = footer()
  26. const commits: StreamCommit[] = [
  27. { kind: "assistant", text: "one", phase: "progress", source: "assistant", partID: "a" },
  28. { kind: "tool", text: "two", phase: "final", source: "tool", partID: "b", tool: "bash" },
  29. ]
  30. writeSessionOutput(
  31. {
  32. footer: out.api,
  33. },
  34. {
  35. commits,
  36. },
  37. )
  38. expect(out.commits).toEqual(commits)
  39. })
  40. test("defaults status patches to running phase", () => {
  41. const out = footer()
  42. writeSessionOutput(
  43. {
  44. footer: out.api,
  45. },
  46. {
  47. commits: [],
  48. footer: {
  49. patch: {
  50. status: "assistant responding",
  51. },
  52. },
  53. },
  54. )
  55. expect(out.events).toEqual([
  56. {
  57. type: "stream.patch",
  58. patch: {
  59. phase: "running",
  60. status: "assistant responding",
  61. },
  62. },
  63. ])
  64. })
  65. test("forwards footer view updates as stream.view events", () => {
  66. const out = footer()
  67. writeSessionOutput(
  68. {
  69. footer: out.api,
  70. },
  71. {
  72. commits: [],
  73. footer: {
  74. view: {
  75. type: "prompt",
  76. },
  77. },
  78. },
  79. )
  80. expect(out.events).toEqual([
  81. {
  82. type: "stream.view",
  83. view: {
  84. type: "prompt",
  85. },
  86. },
  87. ])
  88. })
  89. test("forwards subagent footer snapshots as stream.subagent events", () => {
  90. const out = footer()
  91. writeSessionOutput(
  92. {
  93. footer: out.api,
  94. },
  95. {
  96. commits: [],
  97. footer: {
  98. subagent: {
  99. tabs: [
  100. {
  101. sessionID: "child-1",
  102. partID: "part-1",
  103. callID: "call-1",
  104. label: "Explore",
  105. description: "Scan reducer paths",
  106. status: "running",
  107. lastUpdatedAt: 1,
  108. },
  109. ],
  110. details: {
  111. "child-1": {
  112. sessionID: "child-1",
  113. commits: [],
  114. },
  115. },
  116. permissions: [],
  117. questions: [],
  118. },
  119. },
  120. },
  121. )
  122. expect(out.events).toEqual([
  123. {
  124. type: "stream.subagent",
  125. state: {
  126. tabs: [
  127. {
  128. sessionID: "child-1",
  129. partID: "part-1",
  130. callID: "call-1",
  131. label: "Explore",
  132. description: "Scan reducer paths",
  133. status: "running",
  134. lastUpdatedAt: 1,
  135. },
  136. ],
  137. details: {
  138. "child-1": {
  139. sessionID: "child-1",
  140. commits: [],
  141. },
  142. },
  143. permissions: [],
  144. questions: [],
  145. },
  146. },
  147. ])
  148. })
  149. })