@e2edev/e2e 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/README.md +24 -11
  2. package/dist/agent/act.d.ts +1 -1
  3. package/dist/agent/act.js +4 -2
  4. package/dist/agent/act.js.map +1 -1
  5. package/dist/agent/ai-sdk.d.ts +1 -2
  6. package/dist/agent/ai-sdk.d.ts.map +1 -1
  7. package/dist/agent/ai-sdk.js +1 -2
  8. package/dist/agent/ai-sdk.js.map +1 -1
  9. package/dist/agent/default-agent.d.ts +7 -3
  10. package/dist/agent/default-agent.d.ts.map +1 -1
  11. package/dist/agent/default-agent.js +2 -2
  12. package/dist/agent/default-agent.js.map +1 -1
  13. package/dist/agent/derived.d.ts +1 -1
  14. package/dist/agent/derived.js +1 -1
  15. package/dist/agent/error.d.ts +2 -2
  16. package/dist/agent/error.d.ts.map +1 -1
  17. package/dist/agent/error.js +2 -2
  18. package/dist/agent/error.js.map +1 -1
  19. package/dist/agent/executor.d.ts +8 -3
  20. package/dist/agent/executor.d.ts.map +1 -1
  21. package/dist/agent/executor.js +1 -1
  22. package/dist/agent/executor.js.map +1 -1
  23. package/dist/agent/index.d.ts +1 -1
  24. package/dist/agent/index.d.ts.map +1 -1
  25. package/dist/agent/index.js +8 -5
  26. package/dist/agent/index.js.map +1 -1
  27. package/dist/agent/invocation.d.ts +2 -2
  28. package/dist/agent/invocation.d.ts.map +1 -1
  29. package/dist/agent/invocation.js +9 -3
  30. package/dist/agent/invocation.js.map +1 -1
  31. package/dist/agent/ledger.d.ts +1 -1
  32. package/dist/agent/ledger.js +1 -1
  33. package/dist/agent/model/adapter.d.ts +7 -4
  34. package/dist/agent/model/adapter.d.ts.map +1 -1
  35. package/dist/agent/model/adapter.js +2 -2
  36. package/dist/agent/model/adapter.js.map +1 -1
  37. package/dist/agent/model/router.d.ts +1 -1
  38. package/dist/agent/model/router.d.ts.map +1 -1
  39. package/dist/agent/model/router.js +1 -1
  40. package/dist/agent/model/router.js.map +1 -1
  41. package/dist/agent/model/sdk.d.ts +11 -1
  42. package/dist/agent/model/sdk.d.ts.map +1 -1
  43. package/dist/agent/model/sdk.js +54 -10
  44. package/dist/agent/model/sdk.js.map +1 -1
  45. package/dist/agent/observation.d.ts +1 -1
  46. package/dist/agent/observation.d.ts.map +1 -1
  47. package/dist/agent/observation.js +2 -2
  48. package/dist/agent/observation.js.map +1 -1
  49. package/dist/agent/prompts.d.ts +1 -1
  50. package/dist/agent/prompts.js +1 -1
  51. package/dist/agent/protocol.d.ts +2 -3
  52. package/dist/agent/protocol.d.ts.map +1 -1
  53. package/dist/agent/protocol.js +2 -3
  54. package/dist/agent/protocol.js.map +1 -1
  55. package/dist/agent/public.d.ts +1 -1
  56. package/dist/agent/public.js +1 -1
  57. package/dist/agent/replay.d.ts +1 -1
  58. package/dist/agent/replay.js +1 -1
  59. package/dist/agent/secrets.d.ts +1 -1
  60. package/dist/agent/secrets.d.ts.map +1 -1
  61. package/dist/agent/secrets.js +1 -1
  62. package/dist/agent/secrets.js.map +1 -1
  63. package/dist/agent/step-cache.d.ts +1 -1
  64. package/dist/agent/step-cache.d.ts.map +1 -1
  65. package/dist/agent/step-cache.js +7 -1
  66. package/dist/agent/step-cache.js.map +1 -1
  67. package/dist/agent/tool-loop.d.ts +7 -3
  68. package/dist/agent/tool-loop.d.ts.map +1 -1
  69. package/dist/agent/tool-loop.js +6 -5
  70. package/dist/agent/tool-loop.js.map +1 -1
  71. package/dist/agent/usage.d.ts +6 -1
  72. package/dist/agent/usage.d.ts.map +1 -1
  73. package/dist/agent/usage.js +12 -0
  74. package/dist/agent/usage.js.map +1 -1
  75. package/dist/cache/anchors.d.ts +1 -1
  76. package/dist/cache/anchors.js +1 -1
  77. package/dist/cache/context.d.ts +6 -1
  78. package/dist/cache/context.d.ts.map +1 -1
  79. package/dist/cache/context.js +2 -1
  80. package/dist/cache/context.js.map +1 -1
  81. package/dist/cache/decide.d.ts +1 -1
  82. package/dist/cache/decide.js +1 -1
  83. package/dist/cache/identity.d.ts +1 -1
  84. package/dist/cache/identity.js +1 -1
  85. package/dist/cache/recorder.d.ts +4 -2
  86. package/dist/cache/recorder.d.ts.map +1 -1
  87. package/dist/cache/recorder.js +10 -1
  88. package/dist/cache/recorder.js.map +1 -1
  89. package/dist/cache/relocate.d.ts +1 -1
  90. package/dist/cache/relocate.js +1 -1
  91. package/dist/cache/store.d.ts +1 -1
  92. package/dist/cache/store.js +1 -1
  93. package/dist/cache/trace.d.ts +16 -1
  94. package/dist/cache/trace.d.ts.map +1 -1
  95. package/dist/cache/trace.js +22 -1
  96. package/dist/cache/trace.js.map +1 -1
  97. package/dist/cli/bin.js +9 -1
  98. package/dist/cli/bin.js.map +1 -1
  99. package/dist/cli/cache.d.ts +25 -0
  100. package/dist/cli/cache.d.ts.map +1 -0
  101. package/dist/cli/cache.js +249 -0
  102. package/dist/cli/cache.js.map +1 -0
  103. package/dist/cli/docs-url.d.ts +3 -0
  104. package/dist/cli/docs-url.d.ts.map +1 -0
  105. package/dist/cli/docs-url.js +3 -0
  106. package/dist/cli/docs-url.js.map +1 -0
  107. package/dist/cli/guide.d.ts +4 -0
  108. package/dist/cli/guide.d.ts.map +1 -0
  109. package/dist/cli/guide.js +16 -0
  110. package/dist/cli/guide.js.map +1 -0
  111. package/dist/cli/index.d.ts +1 -1
  112. package/dist/cli/index.d.ts.map +1 -1
  113. package/dist/cli/index.js +210 -41
  114. package/dist/cli/index.js.map +1 -1
  115. package/dist/cli/init/agent-skill.d.ts +34 -0
  116. package/dist/cli/init/agent-skill.d.ts.map +1 -0
  117. package/dist/cli/init/agent-skill.js +44 -0
  118. package/dist/cli/init/agent-skill.js.map +1 -0
  119. package/dist/cli/init/engines.d.ts +31 -23
  120. package/dist/cli/init/engines.d.ts.map +1 -1
  121. package/dist/cli/init/engines.js +34 -24
  122. package/dist/cli/init/engines.js.map +1 -1
  123. package/dist/cli/init/package.d.ts +19 -3
  124. package/dist/cli/init/package.d.ts.map +1 -1
  125. package/dist/cli/init/package.js +20 -20
  126. package/dist/cli/init/package.js.map +1 -1
  127. package/dist/cli/init/scaffold.d.ts.map +1 -1
  128. package/dist/cli/init/scaffold.js +5 -3
  129. package/dist/cli/init/scaffold.js.map +1 -1
  130. package/dist/cli/init.d.ts +14 -2
  131. package/dist/cli/init.d.ts.map +1 -1
  132. package/dist/cli/init.js +94 -16
  133. package/dist/cli/init.js.map +1 -1
  134. package/dist/cli/signals.d.ts +4 -1
  135. package/dist/cli/signals.d.ts.map +1 -1
  136. package/dist/cli/signals.js +8 -2
  137. package/dist/cli/signals.js.map +1 -1
  138. package/dist/cli/skill.d.ts +16 -0
  139. package/dist/cli/skill.d.ts.map +1 -0
  140. package/dist/cli/skill.js +57 -0
  141. package/dist/cli/skill.js.map +1 -0
  142. package/dist/collect/collect.d.ts +9 -1
  143. package/dist/collect/collect.d.ts.map +1 -1
  144. package/dist/collect/collect.js +50 -5
  145. package/dist/collect/collect.js.map +1 -1
  146. package/dist/collect/registry.d.ts +1 -1
  147. package/dist/collect/registry.d.ts.map +1 -1
  148. package/dist/collect/registry.js +1 -1
  149. package/dist/collect/registry.js.map +1 -1
  150. package/dist/collect/select.d.ts +1 -1
  151. package/dist/collect/select.d.ts.map +1 -1
  152. package/dist/collect/select.js +77 -8
  153. package/dist/collect/select.js.map +1 -1
  154. package/dist/config/agent.d.ts +14 -2
  155. package/dist/config/agent.d.ts.map +1 -1
  156. package/dist/config/agent.js +53 -16
  157. package/dist/config/agent.js.map +1 -1
  158. package/dist/config/app.d.ts +2 -2
  159. package/dist/config/app.d.ts.map +1 -1
  160. package/dist/config/app.js +3 -3
  161. package/dist/config/app.js.map +1 -1
  162. package/dist/config/diagnose.d.ts +15 -0
  163. package/dist/config/diagnose.d.ts.map +1 -0
  164. package/dist/config/diagnose.js +122 -0
  165. package/dist/config/diagnose.js.map +1 -0
  166. package/dist/config/load.d.ts +9 -1
  167. package/dist/config/load.d.ts.map +1 -1
  168. package/dist/config/load.js +35 -3
  169. package/dist/config/load.js.map +1 -1
  170. package/dist/config/resolve.d.ts +2 -2
  171. package/dist/config/resolve.d.ts.map +1 -1
  172. package/dist/config/resolve.js +51 -16
  173. package/dist/config/resolve.js.map +1 -1
  174. package/dist/config/validate.d.ts +6 -2
  175. package/dist/config/validate.d.ts.map +1 -1
  176. package/dist/config/validate.js +20 -3
  177. package/dist/config/validate.js.map +1 -1
  178. package/dist/credentials.d.ts +1 -1
  179. package/dist/credentials.d.ts.map +1 -1
  180. package/dist/credentials.js +1 -1
  181. package/dist/credentials.js.map +1 -1
  182. package/dist/engine/contract.d.ts +2 -2
  183. package/dist/engine/contract.js +1 -1
  184. package/dist/engine/index.d.ts +2 -2
  185. package/dist/engine/index.js +3 -3
  186. package/dist/engine/index.js.map +1 -1
  187. package/dist/engine/session.d.ts +1 -1
  188. package/dist/engine/session.d.ts.map +1 -1
  189. package/dist/engine/session.js +5 -2
  190. package/dist/engine/session.js.map +1 -1
  191. package/dist/expect/async.d.ts +1 -1
  192. package/dist/expect/async.d.ts.map +1 -1
  193. package/dist/expect/async.js +37 -1
  194. package/dist/expect/async.js.map +1 -1
  195. package/dist/expect/values.d.ts +1 -1
  196. package/dist/expect/values.d.ts.map +1 -1
  197. package/dist/expect/values.js +1 -1
  198. package/dist/expect/values.js.map +1 -1
  199. package/dist/index.d.ts +0 -3
  200. package/dist/index.d.ts.map +1 -1
  201. package/dist/index.js +0 -4
  202. package/dist/index.js.map +1 -1
  203. package/dist/internal/errors.d.ts +21 -3
  204. package/dist/internal/errors.d.ts.map +1 -1
  205. package/dist/internal/errors.js +55 -5
  206. package/dist/internal/errors.js.map +1 -1
  207. package/dist/internal/globs.d.ts +2 -2
  208. package/dist/internal/globs.d.ts.map +1 -1
  209. package/dist/internal/globs.js +2 -2
  210. package/dist/internal/globs.js.map +1 -1
  211. package/dist/internal/ids.d.ts +3 -3
  212. package/dist/internal/ids.d.ts.map +1 -1
  213. package/dist/internal/ids.js +3 -3
  214. package/dist/internal/ids.js.map +1 -1
  215. package/dist/internal/node-version.d.ts +10 -0
  216. package/dist/internal/node-version.d.ts.map +1 -0
  217. package/dist/internal/node-version.js +28 -0
  218. package/dist/internal/node-version.js.map +1 -0
  219. package/dist/internal/package-manager.d.ts +10 -0
  220. package/dist/internal/package-manager.d.ts.map +1 -0
  221. package/dist/internal/package-manager.js +43 -0
  222. package/dist/internal/package-manager.js.map +1 -0
  223. package/dist/internal/redact.d.ts +1 -1
  224. package/dist/internal/redact.d.ts.map +1 -1
  225. package/dist/internal/redact.js +1 -1
  226. package/dist/internal/redact.js.map +1 -1
  227. package/dist/internal/suggest.d.ts +16 -0
  228. package/dist/internal/suggest.d.ts.map +1 -0
  229. package/dist/internal/suggest.js +61 -0
  230. package/dist/internal/suggest.js.map +1 -0
  231. package/dist/internal/text.d.ts +1 -1
  232. package/dist/internal/text.d.ts.map +1 -1
  233. package/dist/internal/text.js +1 -1
  234. package/dist/internal/text.js.map +1 -1
  235. package/dist/internal/urls.d.ts +2 -2
  236. package/dist/internal/urls.d.ts.map +1 -1
  237. package/dist/internal/urls.js +2 -2
  238. package/dist/internal/urls.js.map +1 -1
  239. package/dist/locator/engine.d.ts +1 -1
  240. package/dist/locator/engine.d.ts.map +1 -1
  241. package/dist/locator/engine.js +1 -1
  242. package/dist/locator/engine.js.map +1 -1
  243. package/dist/locator/expression.d.ts +1 -1
  244. package/dist/locator/expression.d.ts.map +1 -1
  245. package/dist/locator/expression.js +1 -1
  246. package/dist/locator/expression.js.map +1 -1
  247. package/dist/locator/screen.js +1 -1
  248. package/dist/locator/screen.js.map +1 -1
  249. package/dist/report/build.d.ts +1 -1
  250. package/dist/report/build.d.ts.map +1 -1
  251. package/dist/report/build.js +4 -4
  252. package/dist/report/build.js.map +1 -1
  253. package/dist/report/format.d.ts +9 -1
  254. package/dist/report/format.d.ts.map +1 -1
  255. package/dist/report/format.js +17 -4
  256. package/dist/report/format.js.map +1 -1
  257. package/dist/report/junit.d.ts +1 -1
  258. package/dist/report/junit.d.ts.map +1 -1
  259. package/dist/report/junit.js +1 -1
  260. package/dist/report/junit.js.map +1 -1
  261. package/dist/report/list-model.d.ts +79 -0
  262. package/dist/report/list-model.d.ts.map +1 -0
  263. package/dist/report/list-model.js +10 -0
  264. package/dist/report/list-model.js.map +1 -0
  265. package/dist/report/list-steps.d.ts +27 -0
  266. package/dist/report/list-steps.d.ts.map +1 -0
  267. package/dist/report/list-steps.js +65 -0
  268. package/dist/report/list-steps.js.map +1 -0
  269. package/dist/report/list.d.ts +26 -24
  270. package/dist/report/list.d.ts.map +1 -1
  271. package/dist/report/list.js +89 -157
  272. package/dist/report/list.js.map +1 -1
  273. package/dist/report/live-window.d.ts +11 -0
  274. package/dist/report/live-window.d.ts.map +1 -1
  275. package/dist/report/live-window.js +12 -5
  276. package/dist/report/live-window.js.map +1 -1
  277. package/dist/report/running-tree.d.ts +46 -0
  278. package/dist/report/running-tree.d.ts.map +1 -0
  279. package/dist/report/running-tree.js +182 -0
  280. package/dist/report/running-tree.js.map +1 -0
  281. package/dist/report/write.d.ts +1 -1
  282. package/dist/report/write.d.ts.map +1 -1
  283. package/dist/report/write.js +1 -1
  284. package/dist/report/write.js.map +1 -1
  285. package/dist/run/budget.d.ts +2 -2
  286. package/dist/run/budget.js +2 -2
  287. package/dist/run/declared-processes.d.ts +1 -1
  288. package/dist/run/declared-processes.js +1 -1
  289. package/dist/run/events.d.ts +2 -0
  290. package/dist/run/events.d.ts.map +1 -1
  291. package/dist/run/events.js.map +1 -1
  292. package/dist/run/execute.d.ts +2 -3
  293. package/dist/run/execute.d.ts.map +1 -1
  294. package/dist/run/execute.js +4 -5
  295. package/dist/run/execute.js.map +1 -1
  296. package/dist/run/fixtures.d.ts +1 -1
  297. package/dist/run/fixtures.d.ts.map +1 -1
  298. package/dist/run/fixtures.js +41 -5
  299. package/dist/run/fixtures.js.map +1 -1
  300. package/dist/run/managed-process.d.ts +8 -1
  301. package/dist/run/managed-process.d.ts.map +1 -1
  302. package/dist/run/managed-process.js +35 -17
  303. package/dist/run/managed-process.js.map +1 -1
  304. package/dist/run/realm.d.ts +2 -2
  305. package/dist/run/realm.js +2 -2
  306. package/dist/run/runner.d.ts +22 -0
  307. package/dist/run/runner.d.ts.map +1 -1
  308. package/dist/run/runner.js +47 -9
  309. package/dist/run/runner.js.map +1 -1
  310. package/dist/run/scheduler.d.ts +1 -2
  311. package/dist/run/scheduler.d.ts.map +1 -1
  312. package/dist/run/scheduler.js +2 -3
  313. package/dist/run/scheduler.js.map +1 -1
  314. package/dist/run/serial.d.ts +1 -1
  315. package/dist/run/serial.d.ts.map +1 -1
  316. package/dist/run/serial.js +3 -3
  317. package/dist/run/serial.js.map +1 -1
  318. package/dist/run/sessions.d.ts +1 -1
  319. package/dist/run/sessions.d.ts.map +1 -1
  320. package/dist/run/sessions.js +1 -1
  321. package/dist/run/sessions.js.map +1 -1
  322. package/dist/run/steps.d.ts +8 -2
  323. package/dist/run/steps.d.ts.map +1 -1
  324. package/dist/run/steps.js +1 -1
  325. package/dist/run/steps.js.map +1 -1
  326. package/dist/run/units.d.ts +1 -1
  327. package/dist/run/units.js +1 -1
  328. package/dist/types.d.ts +27 -8
  329. package/dist/types.d.ts.map +1 -1
  330. package/dist/types.js +2 -2
  331. package/package.json +13 -12
  332. package/skills/e2e/SKILL.md +110 -0
  333. package/skills/e2e/references/agent.md +210 -0
  334. package/skills/e2e/references/debugging.md +81 -0
  335. package/skills/e2e/references/running.md +153 -0
  336. package/skills/e2e/references/setup.md +239 -0
  337. package/skills/e2e/references/writing-tests.md +289 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-model.js","sourceRoot":"","sources":["../../src/report/list-model.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,KAAgB;IAC3C,OAAO,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Renders one agent step and its calls for the list reporter: the same
3
+ * finished step prints in a CI log, in a file block, and in the live window,
4
+ * each at its own indent, so nothing here knows where the line lands.
5
+ */
6
+ import { type Colors } from './format.ts';
7
+ import type { FinishedStep, ShownEvent } from './list-model.ts';
8
+ /** One-line, quoted step label; `maxChars` clips it further to fit a row. */
9
+ export declare function stepLabel(label: string, maxChars?: number): string;
10
+ export interface StepLineOptions {
11
+ /** Text before the api, naming the test when the line prints away from it. */
12
+ readonly context?: string;
13
+ /** Printed width the line may take; the label is clipped so the tail stays on the row. */
14
+ readonly maxWidth?: number;
15
+ }
16
+ /**
17
+ * One finished agent step: outcome glyph, api, quoted label, then duration,
18
+ * model calls, and a non-passed status.
19
+ */
20
+ export declare function stepLine(pc: Colors, step: FinishedStep, options?: StepLineOptions): string;
21
+ /**
22
+ * One model turn or tool call under the current step. A tool call reads as
23
+ * the act it performed when the engine described it, else as the tool name
24
+ * (`tool:` prefixes come from executor tool accounting).
25
+ */
26
+ export declare function eventLine(pc: Colors, event: ShownEvent): string;
27
+ //# sourceMappingURL=list-steps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-steps.d.ts","sourceRoot":"","sources":["../../src/report/list-steps.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAOL,KAAK,MAAM,EACZ,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAehE,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAuB,GAAG,MAAM,CAGhF;AAED,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAY9F;AAaD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAQ/D"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Renders one agent step and its calls for the list reporter: the same
3
+ * finished step prints in a CI log, in a file block, and in the live window,
4
+ * each at its own indent, so nothing here knows where the line lands.
5
+ */
6
+ import { sanitizeText, truncateUtf8 } from '../internal/errors.js';
7
+ import { collapseText } from '../internal/text.js';
8
+ import { ellipsize, F_CHECK, F_CROSS, formatTime, formatTokens, visibleWidth, } from './format.js';
9
+ /** Step labels stay one glanceable line; the report holds the full text. */
10
+ const MAX_STEP_LABEL_CHARS = 72;
11
+ /** A clipped step line keeps at least this much of its label, however narrow the terminal. */
12
+ const MIN_STEP_LABEL_CHARS = 24;
13
+ /** Columns a step line spends around its label: the quotes and the space before the tail. */
14
+ const LABEL_CHROME = 3;
15
+ /** A finished model turn. */
16
+ const F_MODEL = '•';
17
+ /** A tool call the model made. */
18
+ const F_TOOL = '›';
19
+ const F_INPUT = '↑';
20
+ const F_OUTPUT = '↓';
21
+ /** One-line, quoted step label; `maxChars` clips it further to fit a row. */
22
+ export function stepLabel(label, maxChars = MAX_STEP_LABEL_CHARS) {
23
+ const max = Math.min(MAX_STEP_LABEL_CHARS, Math.max(MIN_STEP_LABEL_CHARS, maxChars));
24
+ return `"${ellipsize(collapseText(label), max)}"`;
25
+ }
26
+ /**
27
+ * One finished agent step: outcome glyph, api, quoted label, then duration,
28
+ * model calls, and a non-passed status.
29
+ */
30
+ export function stepLine(pc, step, options = {}) {
31
+ const glyph = step.status === 'passed' ? pc.green(F_CHECK) : pc.red(F_CROSS);
32
+ const calls = step.modelCalls > 0 ? ` · ${step.modelCalls} model call${step.modelCalls === 1 ? '' : 's'}` : '';
33
+ const outcome = step.status === 'passed' ? '' : ` ${step.status}`;
34
+ const head = `${glyph} ${options.context ?? ''}${pc.dim(step.api)} `;
35
+ const tail = pc.dim(`${formatTime(step.durationMs)}${calls}${outcome}`);
36
+ const room = options.maxWidth === undefined
37
+ ? undefined
38
+ : options.maxWidth - visibleWidth(head) - visibleWidth(tail) - LABEL_CHROME;
39
+ return `${head}${stepLabel(step.label, room)} ${tail}`;
40
+ }
41
+ /**
42
+ * `(↑in ↓out)` for one model call, the total when only that is known, or
43
+ * nothing when the provider reported no usage.
44
+ */
45
+ function tokenSplit(event) {
46
+ if (event.inputTokens !== undefined && event.outputTokens !== undefined) {
47
+ return ` (${F_INPUT}${formatTokens(event.inputTokens)} ${F_OUTPUT}${formatTokens(event.outputTokens)})`;
48
+ }
49
+ return event.count !== undefined && event.count > 0 ? ` (${formatTokens(event.count)} tokens)` : '';
50
+ }
51
+ /**
52
+ * One model turn or tool call under the current step. A tool call reads as
53
+ * the act it performed when the engine described it, else as the tool name
54
+ * (`tool:` prefixes come from executor tool accounting).
55
+ */
56
+ export function eventLine(pc, event) {
57
+ if (event.kind === 'model') {
58
+ return pc.dim(`${F_MODEL} Thinking (${formatTime(event.durationMs)})${tokenSplit(event)}`);
59
+ }
60
+ const name = sanitizeText(event.name ?? 'engine').replace(/^tool:/, '');
61
+ const act = event.detail === undefined ? name : sanitizeText(collapseText(event.detail));
62
+ const failed = event.status === 'passed' ? '' : ` ${pc.red(F_CROSS)}`;
63
+ return `${pc.dim(`${F_TOOL} ${truncateUtf8(act, 60)} (${formatTime(event.durationMs)})`)}${failed}`;
64
+ }
65
+ //# sourceMappingURL=list-steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-steps.js","sourceRoot":"","sources":["../../src/report/list-steps.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Human-readable list reporter (spec 06-cli.md), styled after vitest's
3
- * default reporter: one block per test file and target, a `Failed Tests`
4
- * section with code frames, and a padded summary. On a TTY a live window
5
- * below the log shows the running files, their tests, and the counters.
2
+ * Human-readable list reporter, styled after vitest's default reporter: one
3
+ * block per test file and target with each test's agent steps nested, a
4
+ * `Failed Tests` section with code frames, and a padded summary. On a TTY a
5
+ * live window below the log shows the running tree (`RunningTree`) and the
6
+ * counters.
6
7
  */
7
8
  import type { RunEventFact } from '../run/events.ts';
8
9
  export interface ListReporterOutput {
@@ -25,6 +26,7 @@ export declare class ListReporter {
25
26
  private readonly output;
26
27
  private readonly pc;
27
28
  private readonly window;
29
+ private readonly tree;
28
30
  private readonly separator;
29
31
  private projectRoot;
30
32
  /** Selected targets in declaration order; decides each badge's color. */
@@ -33,8 +35,10 @@ export declare class ListReporter {
33
35
  private startedAt;
34
36
  /** File groups keyed `${target}\u0000${file}`, in first-seen order. */
35
37
  private readonly groups;
36
- /** Pairs executing right now, keyed by `pairKey`, in start order. */
37
- private readonly running;
38
+ /** Pairs that started and whose result is still to come, keyed by `pairKey`, in start order. */
39
+ private readonly pairs;
40
+ /** Whether a live window paints; without one, finished steps stream permanently. */
41
+ private readonly live;
38
42
  private readonly failures;
39
43
  /**
40
44
  * Serial groups whose members' results are still to come, by group id. The
@@ -66,18 +70,19 @@ export declare class ListReporter {
66
70
  /**
67
71
  * A worker began one test-target pair: show it in the live window. A serial
68
72
  * member replaces the previous member of its group, which has finished
69
- * executing even though its result only arrives with the whole group.
73
+ * executing even though its result only arrives with the whole group. A
74
+ * serial group retries as a whole, so a member starts once per group
75
+ * attempt; its steps from earlier attempts stay with it, as an ordinary
76
+ * test's do.
70
77
  */
71
78
  private testStarted;
72
79
  /**
73
- * Streams step progress of a running pair. With a single test running, its
74
- * agent steps print permanently and chronologically under a header naming
75
- * the file and test - the scrollback of a long agentic run reads without
76
- * `--debug`. Deterministic steps are fast and many, so they only ever show
77
- * in the live window. With parallel tests the stream would interleave, so
78
- * each pair instead shows its current step and latest calls transiently in
79
- * the live window, and finished agent steps print with the test they
80
- * belong to.
80
+ * Tracks step progress of a running pair. With a live window the pair's
81
+ * finished agent steps accumulate under its row there and print once, nested
82
+ * under its line, when the file block prints - so every name appears exactly
83
+ * once in the scrollback. Without a window (CI logs) nothing is transient, so
84
+ * each finished agent step prints at once, prefixed with its test. Deterministic
85
+ * steps are fast and many, so they only ever show in the live window.
81
86
  */
82
87
  private step;
83
88
  /** Holds a serial group until each of its members' results has read its details. */
@@ -95,8 +100,11 @@ export declare class ListReporter {
95
100
  private fileCounters;
96
101
  /**
97
102
  * Prints one file's block: the file line with its counts, then - when the
98
- * file failed, had a flaky pass, streamed its steps, or is the run's only
99
- * file - one line per test, as vitest lists tests for a failed module.
103
+ * file failed, had a flaky pass, or is the run's only file - one line per
104
+ * test with its finished agent steps nested, as vitest lists tests for a
105
+ * failed module. With a live window the block is also the only permanent
106
+ * record of a file's agent steps, so a file that has any lists its tests
107
+ * too; without one the steps already printed as they finished.
100
108
  */
101
109
  private printGroup;
102
110
  /** One test row under its file line, plus the first error line for a failure. */
@@ -106,13 +114,7 @@ export declare class ListReporter {
106
114
  * files, tests, model usage, run errors, start time, and elapsed time.
107
115
  */
108
116
  private summaryRows;
109
- /**
110
- * The live window: running files and tests, then the summary. The block
111
- * must fit the screen, because repainting more rows than the terminal has
112
- * breaks the cursor-up erase: rows left after the summary go to the running
113
- * tests, each test's calls share what remains once every test has its own
114
- * line, and tests that still do not fit fold into one `more running` marker.
115
- */
117
+ /** The live window: the running tree, then the summary. */
116
118
  private renderWindow;
117
119
  /** vitest's `Failed Tests` section: a banner, then each failure with its code frame. */
118
120
  private printFailures;
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/report/list.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,kBAAkB,CAAC;AA0DjF,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uEAAuE;IACvE,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA+HD;;;;GAIG;AACH,qBAAa,YAAY;IA0BrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAzBzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,WAAW,CAAqB;IACxC,yEAAyE;IACzE,OAAO,CAAC,OAAO,CAAyB;IACxC,iFAAiF;IACjF,OAAO,CAAC,SAAS,CAAc;IAC/B,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsE;IACpG,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC,YACmB,MAAM,GAAE,kBAAmC,EAC5D,OAAO,GAAE,mBAAwB,EAMlC;IAED;;;OAGG;IACH,QAAQ,CAAC,MAAM,UAAW,YAAY,KAAG,IAAI,CAiC3C;IAEF,oEAAoE;IACpE,OAAO,CAAC,KAAK;IAMb,mFAAmF;IACnF,OAAO,CAAC,KAAK;IAQb,yEAAyE;IACzE,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,IAAI;IAOZ,kFAAkF;IAClF,OAAO,CAAC,cAAc;IAUtB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAoBnB;;;;;;;;;OASG;IACH,OAAO,CAAC,IAAI;IAkDZ,oFAAoF;IACpF,OAAO,CAAC,WAAW;IAInB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,YAAY;IAyBpB,wFAAwF;IACxF,OAAO,CAAC,YAAY;IAOpB,uFAAuF;IACvF,OAAO,CAAC,YAAY;IASpB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA0ClB,iFAAiF;IACjF,OAAO,CAAC,QAAQ;IA+BhB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAmBnB;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAsDpB,wFAAwF;IACxF,OAAO,CAAC,aAAa;IA0BrB,gEAAgE;IAChE,OAAO,CAAC,WAAW;IAOnB,wEAAwE;IACxE,OAAO,CAAC,oBAAoB;IAU5B;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;CAyBpB"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/report/list.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,kBAAkB,CAAC;AAoDjF,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uEAAuE;IACvE,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA0DD;;;;GAIG;AACH,qBAAa,YAAY;IA6BrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IA5BzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,WAAW,CAAqB;IACxC,yEAAyE;IACzE,OAAO,CAAC,OAAO,CAAyB;IACxC,iFAAiF;IACjF,OAAO,CAAC,SAAS,CAAc;IAC/B,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,gGAAgG;IAChG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsE;IACpG,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC,YACmB,MAAM,GAAE,kBAAmC,EAC5D,OAAO,GAAE,mBAAwB,EAQlC;IAED;;;OAGG;IACH,QAAQ,CAAC,MAAM,UAAW,YAAY,KAAG,IAAI,CAiC3C;IAEF,oEAAoE;IACpE,OAAO,CAAC,KAAK;IAMb,mFAAmF;IACnF,OAAO,CAAC,KAAK;IAQb,yEAAyE;IACzE,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,IAAI;IAOZ,kFAAkF;IAClF,OAAO,CAAC,cAAc;IAUtB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IAmBnB;;;;;;;OAOG;IACH,OAAO,CAAC,IAAI;IA2CZ,oFAAoF;IACpF,OAAO,CAAC,WAAW;IAInB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,YAAY;IA6BpB,wFAAwF;IACxF,OAAO,CAAC,YAAY;IAOpB,uFAAuF;IACvF,OAAO,CAAC,YAAY;IASpB;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IA2ClB,iFAAiF;IACjF,OAAO,CAAC,QAAQ;IA+BhB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAmBnB,2DAA2D;IAC3D,OAAO,CAAC,YAAY;IAKpB,wFAAwF;IACxF,OAAO,CAAC,aAAa;IA0BrB,gEAAgE;IAChE,OAAO,CAAC,WAAW;IAOnB,wEAAwE;IACxE,OAAO,CAAC,oBAAoB;IAU5B;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;CAyBpB"}
@@ -1,63 +1,35 @@
1
1
  /**
2
- * Human-readable list reporter (spec 06-cli.md), styled after vitest's
3
- * default reporter: one block per test file and target, a `Failed Tests`
4
- * section with code frames, and a padded summary. On a TTY a live window
5
- * below the log shows the running files, their tests, and the counters.
2
+ * Human-readable list reporter, styled after vitest's default reporter: one
3
+ * block per test file and target with each test's agent steps nested, a
4
+ * `Failed Tests` section with code frames, and a padded summary. On a TTY a
5
+ * live window below the log shows the running tree (`RunningTree`) and the
6
+ * counters.
6
7
  */
7
8
  import path from 'node:path';
8
9
  import picocolors from 'picocolors';
9
- import { sanitizeText, truncateUtf8 } from '../internal/errors.js';
10
10
  import { packageVersion } from '../internal/package-version.js';
11
- import { collapseText } from '../internal/text.js';
12
11
  import { codeFrame, userFrame } from './code-frame.js';
13
- import { addUsage, aiSegment, bounded, ellipsize, emptyCounters, emptyUsage, fileOutcome, formatClock, formatTime, formatTokens, padTitle, rule, stateString, statusBucket, stepsUsage, sumUsage, tally, terminalColumns, terminalRows, } from './format.js';
12
+ import { addUsage, aiSegment, bounded, ellipsize, emptyCounters, emptyUsage, F_CHECK, F_CROSS, F_POINTER, fileOutcome, formatClock, formatTime, padTitle, rule, stateString, statusBucket, stepsUsage, sumUsage, tally, terminalColumns, usageText, } from './format.js';
13
+ import { isShownEvent } from './list-model.js';
14
+ import { stepLine } from './list-steps.js';
14
15
  import { LiveWindow } from './live-window.js';
15
- /** Step labels stay one glanceable line; the report holds the full text. */
16
- const MAX_STEP_LABEL_BYTES = 72;
17
- const F_POINTER = '❯';
18
- const F_CHECK = '✓';
19
- const F_CROSS = '×';
16
+ import { RunningTree } from './running-tree.js';
20
17
  const F_DOWN = '↓';
21
18
  const F_RIGHT = '→';
22
- const F_DOWN_RIGHT = '↳';
23
- const F_TREE_MIDDLE = '├──';
24
- const F_TREE_END = '└──';
25
19
  /** Indentation under a badge line, matching vitest's banner padding. */
26
20
  const BADGE_PADDING = ' ';
27
- /** Badge backgrounds, assigned to targets in declaration order. */
28
- const BADGE_COLORS = ['bgYellow', 'bgCyan', 'bgGreen', 'bgMagenta'];
21
+ /** Indentation of a test line under its file line. */
22
+ const TEST_INDENT = ' ';
23
+ /** Indentation of a finished agent step under its test line in a file block. */
24
+ const STEP_INDENT = ' ';
29
25
  /**
30
- * Rows the live window spends outside the running tests and the summary: its
31
- * blank lines, the `… more running` marker, and a margin above the prompt.
26
+ * Badge backgrounds, assigned to targets in declaration order. Bright variants
27
+ * because GitHub Actions renders plain yellow as dark brown, which swallows the
28
+ * black label.
32
29
  */
33
- const WINDOW_CHROME_ROWS = 6;
34
- /** Rows one running test takes before its calls: the test line and its current step. */
35
- const WINDOW_ROWS_PER_TEST = 2;
30
+ const BADGE_COLORS = ['bgYellowBright', 'bgCyanBright', 'bgGreenBright', 'bgMagentaBright'];
36
31
  /** Indentation of the `→ first error line` under a failed test in a file block. */
37
32
  const ERROR_GLANCE_INDENT = 7;
38
- /** One-line, quoted step label bounded for the live view. */
39
- function stepLabel(label) {
40
- const flat = collapseText(label);
41
- const shown = truncateUtf8(flat, MAX_STEP_LABEL_BYTES);
42
- return `"${shown}${shown === flat ? '' : '…'}"`;
43
- }
44
- /**
45
- * Live tail for one step event. Model and engine calls are the ones worth a
46
- * glance (`tool:` prefixes come from executor tool accounting); polls and
47
- * policy decisions stay quiet.
48
- */
49
- function eventTail(event) {
50
- if (event.kind === 'model') {
51
- const tokens = event.count !== undefined && event.count > 0 ? ` · ${formatTokens(event.count)} tokens` : '';
52
- return `model turn ${formatTime(event.durationMs)}${tokens}`;
53
- }
54
- if (event.kind === 'engine') {
55
- const name = sanitizeText(event.name ?? 'engine').replace(/^tool:/, '');
56
- const failed = event.status === 'passed' ? '' : ` ${F_CROSS}`;
57
- return `${truncateUtf8(name, 40)} ${formatTime(event.durationMs)}${failed}`;
58
- }
59
- return undefined;
60
- }
61
33
  /** The identity of one test-target pair across `test-started`, `step`, and `test-finished`. */
62
34
  function pairKey(testId, target) {
63
35
  return `${testId}@${target}`;
@@ -104,6 +76,7 @@ export class ListReporter {
104
76
  output;
105
77
  pc;
106
78
  window;
79
+ tree;
107
80
  separator;
108
81
  projectRoot;
109
82
  /** Selected targets in declaration order; decides each badge's color. */
@@ -112,8 +85,10 @@ export class ListReporter {
112
85
  startedAt = new Date();
113
86
  /** File groups keyed `${target}\u0000${file}`, in first-seen order. */
114
87
  groups = new Map();
115
- /** Pairs executing right now, keyed by `pairKey`, in start order. */
116
- running = new Map();
88
+ /** Pairs that started and whose result is still to come, keyed by `pairKey`, in start order. */
89
+ pairs = new Map();
90
+ /** Whether a live window paints; without one, finished steps stream permanently. */
91
+ live;
117
92
  failures = [];
118
93
  /**
119
94
  * Serial groups whose members' results are still to come, by group id. The
@@ -128,8 +103,10 @@ export class ListReporter {
128
103
  constructor(output = DEFAULT_OUTPUT, options = {}) {
129
104
  this.output = output;
130
105
  const live = (options.live ?? process.stdout.isTTY === true) && output.raw !== undefined;
106
+ this.live = live;
131
107
  this.pc = picocolors.createColors(options.colors ?? (live || picocolors.isColorSupported));
132
108
  this.separator = this.pc.dim(' > ');
109
+ this.tree = new RunningTree(this.pc, (target) => this.badge(target));
133
110
  this.window = new LiveWindow(live ? output.raw?.bind(output) : undefined, () => this.renderWindow());
134
111
  }
135
112
  /**
@@ -194,7 +171,7 @@ export class ListReporter {
194
171
  const key = `${target}\u0000${file}`;
195
172
  let group = this.groups.get(key);
196
173
  if (group === undefined) {
197
- group = { file, target, planned: undefined, lines: [], streamed: false, printed: false };
174
+ group = { file, target, planned: undefined, lines: [], printed: false };
198
175
  this.groups.set(key, group);
199
176
  }
200
177
  return group;
@@ -211,6 +188,9 @@ export class ListReporter {
211
188
  if (event.ci)
212
189
  details.push('CI');
213
190
  this.output.write(BADGE_PADDING + pc.dim(details.join(' · ')));
191
+ if (event.model !== undefined) {
192
+ this.output.write(BADGE_PADDING + pc.dim(`model ${bounded(event.model)}`));
193
+ }
214
194
  this.output.write('');
215
195
  this.window.start();
216
196
  }
@@ -229,79 +209,80 @@ export class ListReporter {
229
209
  /**
230
210
  * A worker began one test-target pair: show it in the live window. A serial
231
211
  * member replaces the previous member of its group, which has finished
232
- * executing even though its result only arrives with the whole group.
212
+ * executing even though its result only arrives with the whole group. A
213
+ * serial group retries as a whole, so a member starts once per group
214
+ * attempt; its steps from earlier attempts stay with it, as an ordinary
215
+ * test's do.
233
216
  */
234
217
  testStarted(event) {
235
218
  if (event.serialId !== undefined) {
236
- for (const [key, test] of this.running) {
237
- if (test.serialId === event.serialId && test.group.target === event.target) {
238
- this.running.delete(key);
239
- }
219
+ for (const test of this.pairs.values()) {
220
+ if (test.serialId === event.serialId && test.group.target === event.target)
221
+ test.executing = false;
240
222
  }
241
223
  }
242
- this.running.set(pairKey(event.testId, event.target), {
224
+ const key = pairKey(event.testId, event.target);
225
+ this.pairs.set(key, {
243
226
  group: this.group(event.file, event.target),
244
227
  serialId: event.serialId,
245
228
  title: bounded(event.title),
246
229
  startedMs: Date.now(),
247
- step: undefined,
248
- events: [],
249
- streaming: false,
230
+ executing: true,
231
+ current: undefined,
232
+ steps: this.pairs.get(key)?.steps ?? [],
250
233
  });
251
234
  this.window.redraw();
252
235
  }
253
236
  /**
254
- * Streams step progress of a running pair. With a single test running, its
255
- * agent steps print permanently and chronologically under a header naming
256
- * the file and test - the scrollback of a long agentic run reads without
257
- * `--debug`. Deterministic steps are fast and many, so they only ever show
258
- * in the live window. With parallel tests the stream would interleave, so
259
- * each pair instead shows its current step and latest calls transiently in
260
- * the live window, and finished agent steps print with the test they
261
- * belong to.
237
+ * Tracks step progress of a running pair. With a live window the pair's
238
+ * finished agent steps accumulate under its row there and print once, nested
239
+ * under its line, when the file block prints - so every name appears exactly
240
+ * once in the scrollback. Without a window (CI logs) nothing is transient, so
241
+ * each finished agent step prints at once, prefixed with its test. Deterministic
242
+ * steps are fast and many, so they only ever show in the live window.
262
243
  */
263
244
  step(event) {
264
- const { pc } = this;
265
- const running = this.running.get(pairKey(event.testId, event.target));
245
+ const running = this.pairs.get(pairKey(event.testId, event.target));
266
246
  if (running === undefined)
267
247
  return;
268
248
  const { progress } = event;
269
249
  switch (progress.phase) {
270
250
  case 'start': {
271
- running.step = `${progress.api} ${stepLabel(progress.label)}`;
272
- running.events = [];
273
- if (progress.kind === 'agent' && this.running.size === 1 && !running.streaming) {
274
- running.streaming = true;
275
- const { group } = running;
276
- group.streamed = true;
277
- this.print(` ${pc.yellow(F_POINTER)} ${this.badge(group.target)} ${pc.dim(bounded(group.file))}${this.separator}${running.title}`);
278
- }
251
+ const { api, label, kind } = progress;
252
+ running.current = { api, label, kind, events: [], turnStart: 0 };
279
253
  this.window.redraw();
280
254
  break;
281
255
  }
282
256
  case 'event': {
283
- const tail = eventTail(progress.event);
284
- if (running.step === undefined || tail === undefined)
257
+ const { current } = running;
258
+ if (current === undefined || !isShownEvent(progress.event))
285
259
  break;
286
- running.events.push(tail);
260
+ if (progress.event.kind === 'model') {
261
+ current.events.splice(current.turnStart, 0, progress.event);
262
+ current.turnStart = current.events.length;
263
+ }
264
+ else {
265
+ current.events.push(progress.event);
266
+ }
287
267
  this.window.redraw();
288
268
  break;
289
269
  }
290
270
  case 'end': {
291
- running.step = undefined;
292
- running.events = [];
271
+ running.current = undefined;
293
272
  if (progress.kind !== 'agent') {
294
273
  this.window.redraw();
295
274
  break;
296
275
  }
297
- const glyph = progress.status === 'passed' ? pc.green(F_CHECK) : pc.red(F_CROSS);
298
- const calls = progress.modelCalls > 0
299
- ? ` · ${progress.modelCalls} model call${progress.modelCalls === 1 ? '' : 's'}`
300
- : '';
301
- const outcome = progress.status === 'passed' ? '' : ` ${progress.status}`;
302
- const context = running.streaming ? '' : `${pc.dim(running.title)}${this.separator}`;
303
- this.print(` ${glyph} ${context}${pc.dim(progress.api)} ${stepLabel(progress.label)} ` +
304
- pc.dim(`${formatTime(progress.durationMs)}${calls}${outcome}`));
276
+ const { api, label, status, durationMs, modelCalls } = progress;
277
+ const step = { api, label, status, durationMs, modelCalls };
278
+ if (this.live) {
279
+ running.steps.push(step);
280
+ this.window.redraw();
281
+ }
282
+ else {
283
+ const context = `${this.pc.dim(running.title)}${this.separator}`;
284
+ this.print(`${TEST_INDENT}${stepLine(this.pc, step, { context })}`);
285
+ }
305
286
  break;
306
287
  }
307
288
  }
@@ -331,12 +312,14 @@ export class ListReporter {
331
312
  // counted them.
332
313
  if (!result.selected)
333
314
  return;
334
- this.running.delete(pairKey(result.test.id, result.target.name));
315
+ const key = pairKey(result.test.id, result.target.name);
316
+ const steps = this.pairs.get(key)?.steps ?? [];
317
+ this.pairs.delete(key);
335
318
  const group = this.group(result.test.file, result.target.name);
336
319
  const { durationMs, usage, error } = this.detailsOf(result);
337
320
  addUsage(this.runUsage, usage);
338
321
  const title = bounded(result.test.titlePath.join(' > '));
339
- group.lines.push({
322
+ const line = {
340
323
  title,
341
324
  declarationIndex: result.test.declarationIndex,
342
325
  status: result.status,
@@ -344,7 +327,9 @@ export class ListReporter {
344
327
  usage,
345
328
  firstErrorLine: error === undefined ? undefined : bounded(error.message).split('\n')[0],
346
329
  skipReason: result.skip === undefined ? undefined : bounded(result.skip.reason),
347
- });
330
+ steps,
331
+ };
332
+ group.lines.push(line);
348
333
  if (statusBucket(result.status) === 'failed') {
349
334
  this.failures.push({ group, title, status: result.status, error });
350
335
  }
@@ -372,8 +357,11 @@ export class ListReporter {
372
357
  }
373
358
  /**
374
359
  * Prints one file's block: the file line with its counts, then - when the
375
- * file failed, had a flaky pass, streamed its steps, or is the run's only
376
- * file - one line per test, as vitest lists tests for a failed module.
360
+ * file failed, had a flaky pass, or is the run's only file - one line per
361
+ * test with its finished agent steps nested, as vitest lists tests for a
362
+ * failed module. With a live window the block is also the only permanent
363
+ * record of a file's agent steps, so a file that has any lists its tests
364
+ * too; without one the steps already printed as they finished.
377
365
  */
378
366
  printGroup(group) {
379
367
  if (group.printed)
@@ -409,14 +397,16 @@ export class ListReporter {
409
397
  this.print(parts.join(' '));
410
398
  const verbose = counts.failed > 0 ||
411
399
  counts.flaky > 0 ||
412
- group.streamed ||
413
- this.groups.size === 1;
400
+ this.groups.size === 1 ||
401
+ (this.live && group.lines.some((line) => line.steps.length > 0));
414
402
  if (!verbose)
415
403
  return;
416
404
  const ordered = group.lines.toSorted((a, b) => a.declarationIndex - b.declarationIndex);
417
405
  for (const line of ordered) {
418
406
  for (const rendered of this.testLine(line))
419
407
  this.print(rendered);
408
+ for (const step of line.steps)
409
+ this.print(`${STEP_INDENT}${stepLine(pc, step)}`);
420
410
  }
421
411
  }
422
412
  /** One test row under its file line, plus the first error line for a failure. */
@@ -461,7 +451,7 @@ export class ListReporter {
461
451
  padTitle(pc, 'Test Files') + (files.total === 0 ? pc.dim('no test files') : stateString(pc, files)),
462
452
  padTitle(pc, 'Tests') + (tests.total === 0 ? pc.dim('no tests executed') : stateString(pc, tests)),
463
453
  ];
464
- const ai = aiSegment(this.runUsage);
454
+ const ai = usageText(this.runUsage);
465
455
  if (ai !== undefined)
466
456
  rows.push(padTitle(pc, 'AI') + `${ai} · ${this.runUsage.calls} model calls`);
467
457
  if (this.errors.length > 0) {
@@ -472,68 +462,10 @@ export class ListReporter {
472
462
  rows.push(padTitle(pc, 'Duration') + formatTime(Date.now() - this.startedAt.getTime()));
473
463
  return rows;
474
464
  }
475
- /**
476
- * The live window: running files and tests, then the summary. The block
477
- * must fit the screen, because repainting more rows than the terminal has
478
- * breaks the cursor-up erase: rows left after the summary go to the running
479
- * tests, each test's calls share what remains once every test has its own
480
- * line, and tests that still do not fit fold into one `more running` marker.
481
- */
465
+ /** The live window: the running tree, then the summary. */
482
466
  renderWindow() {
483
- const { pc } = this;
484
- const now = Date.now();
485
- const active = new Map();
486
- for (const test of this.running.values()) {
487
- const tests = active.get(test.group);
488
- if (tests === undefined)
489
- active.set(test.group, [test]);
490
- else
491
- tests.push(test);
492
- }
493
- const summary = this.summaryRows();
494
- let budget = terminalRows() - summary.length - WINDOW_CHROME_ROWS;
495
- const spare = budget - active.size - this.running.size * WINDOW_ROWS_PER_TEST;
496
- const maxEvents = Math.max(0, Math.floor(spare / Math.max(1, this.running.size)));
497
- const lines = [''];
498
- let hidden = 0;
499
- for (const [group, tests] of active) {
500
- if (budget < 2) {
501
- hidden += tests.length;
502
- continue;
503
- }
504
- const progress = pc.dim(` ${group.lines.length}/${group.planned ?? '?'}`);
505
- lines.push(`${pc.bold(pc.yellow(` ${F_POINTER} `))}${this.badge(group.target)} ${bounded(group.file)}${progress}`);
506
- budget -= 1;
507
- tests.forEach((test, index) => {
508
- if (budget < 1) {
509
- hidden += 1;
510
- return;
511
- }
512
- const glyph = index === tests.length - 1 ? F_TREE_END : F_TREE_MIDDLE;
513
- const elapsed = pc.bold(pc.yellow(formatTime(Math.max(0, now - test.startedMs))));
514
- lines.push(`${pc.bold(pc.yellow(` ${glyph} `))}${test.title} ${elapsed}`);
515
- budget -= 1;
516
- if (test.step === undefined)
517
- return;
518
- const detail = [` ${pc.dim(`${F_DOWN_RIGHT} ${test.step}`)}`];
519
- const overflow = test.events.length - maxEvents;
520
- if (overflow > 0) {
521
- detail.push(` ${pc.dim(`… ${overflow} earlier call${overflow === 1 ? '' : 's'}`)}`);
522
- }
523
- for (const tail of overflow > 0 ? test.events.slice(overflow) : test.events) {
524
- detail.push(` ${pc.dim(tail)}`);
525
- }
526
- const shown = detail.slice(0, budget);
527
- lines.push(...shown);
528
- budget -= shown.length;
529
- });
530
- }
531
- if (hidden > 0)
532
- lines.push(pc.dim(` … ${hidden} more running`));
533
- if (active.size > 0)
534
- lines.push('');
535
- lines.push(...summary, '');
536
- return lines;
467
+ const running = [...this.pairs.values()].filter((test) => test.executing);
468
+ return this.tree.render(running, this.summaryRows(), Date.now());
537
469
  }
538
470
  /** vitest's `Failed Tests` section: a banner, then each failure with its code frame. */
539
471
  printFailures() {