apex-code 0.0.1-alpha.0 → 0.0.1-alpha.2

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 (426) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +66 -681
  3. package/dist/cli/args.d.ts +5 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +45 -6
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/auth-command.d.ts.map +1 -1
  8. package/dist/cli/auth-command.js +3 -3
  9. package/dist/cli/auth-command.js.map +1 -1
  10. package/dist/cli/cost-command.d.ts +19 -0
  11. package/dist/cli/cost-command.d.ts.map +1 -0
  12. package/dist/cli/cost-command.js +107 -0
  13. package/dist/cli/cost-command.js.map +1 -0
  14. package/dist/cli/startup-ui.d.ts +1 -1
  15. package/dist/cli/startup-ui.d.ts.map +1 -1
  16. package/dist/cli/startup-ui.js +1 -2
  17. package/dist/cli/startup-ui.js.map +1 -1
  18. package/dist/cli.d.ts.map +1 -1
  19. package/dist/cli.js +45 -16
  20. package/dist/cli.js.map +1 -1
  21. package/dist/config.d.ts +2 -2
  22. package/dist/config.d.ts.map +1 -1
  23. package/dist/config.js +14 -7
  24. package/dist/config.js.map +1 -1
  25. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  26. package/dist/core/agent-session-runtime.js +2 -0
  27. package/dist/core/agent-session-runtime.js.map +1 -1
  28. package/dist/core/agent-session-services.d.ts +9 -0
  29. package/dist/core/agent-session-services.d.ts.map +1 -1
  30. package/dist/core/agent-session-services.js +30 -4
  31. package/dist/core/agent-session-services.js.map +1 -1
  32. package/dist/core/agent-session.d.ts +46 -0
  33. package/dist/core/agent-session.d.ts.map +1 -1
  34. package/dist/core/agent-session.js +137 -19
  35. package/dist/core/agent-session.js.map +1 -1
  36. package/dist/core/auth-storage.d.ts.map +1 -1
  37. package/dist/core/auth-storage.js +6 -6
  38. package/dist/core/auth-storage.js.map +1 -1
  39. package/dist/core/bash-executor.d.ts.map +1 -1
  40. package/dist/core/bash-executor.js +1 -1
  41. package/dist/core/bash-executor.js.map +1 -1
  42. package/dist/core/context/deferred-schemas.d.ts +92 -0
  43. package/dist/core/context/deferred-schemas.d.ts.map +1 -0
  44. package/dist/core/context/deferred-schemas.js +74 -0
  45. package/dist/core/context/deferred-schemas.js.map +1 -0
  46. package/dist/core/context/eviction.d.ts +47 -0
  47. package/dist/core/context/eviction.d.ts.map +1 -0
  48. package/dist/core/context/eviction.js +83 -0
  49. package/dist/core/context/eviction.js.map +1 -0
  50. package/dist/core/context/pipeline.d.ts +90 -0
  51. package/dist/core/context/pipeline.d.ts.map +1 -0
  52. package/dist/core/context/pipeline.js +109 -0
  53. package/dist/core/context/pipeline.js.map +1 -0
  54. package/dist/core/credential-failover.d.ts +30 -0
  55. package/dist/core/credential-failover.d.ts.map +1 -0
  56. package/dist/core/credential-failover.js +46 -0
  57. package/dist/core/credential-failover.js.map +1 -0
  58. package/dist/core/credential-pool.d.ts +60 -0
  59. package/dist/core/credential-pool.d.ts.map +1 -0
  60. package/dist/core/credential-pool.js +84 -0
  61. package/dist/core/credential-pool.js.map +1 -0
  62. package/dist/core/delegation/agents.d.ts +15 -0
  63. package/dist/core/delegation/agents.d.ts.map +1 -0
  64. package/dist/core/delegation/agents.js +55 -0
  65. package/dist/core/delegation/agents.js.map +1 -0
  66. package/dist/core/delegation/ceiling.d.ts +29 -0
  67. package/dist/core/delegation/ceiling.d.ts.map +1 -0
  68. package/dist/core/delegation/ceiling.js +34 -0
  69. package/dist/core/delegation/ceiling.js.map +1 -0
  70. package/dist/core/delegation/runtime.d.ts +94 -0
  71. package/dist/core/delegation/runtime.d.ts.map +1 -0
  72. package/dist/core/delegation/runtime.js +117 -0
  73. package/dist/core/delegation/runtime.js.map +1 -0
  74. package/dist/core/durable-state/daemon.d.ts +37 -0
  75. package/dist/core/durable-state/daemon.d.ts.map +1 -0
  76. package/dist/core/durable-state/daemon.js +62 -0
  77. package/dist/core/durable-state/daemon.js.map +1 -0
  78. package/dist/core/durable-state/provenance.d.ts +7 -0
  79. package/dist/core/durable-state/provenance.d.ts.map +1 -0
  80. package/dist/core/durable-state/provenance.js +24 -0
  81. package/dist/core/durable-state/provenance.js.map +1 -0
  82. package/dist/core/durable-state/sqlite.d.ts +84 -0
  83. package/dist/core/durable-state/sqlite.d.ts.map +1 -0
  84. package/dist/core/durable-state/sqlite.js +331 -0
  85. package/dist/core/durable-state/sqlite.js.map +1 -0
  86. package/dist/core/environment.d.ts +12 -0
  87. package/dist/core/environment.d.ts.map +1 -0
  88. package/dist/core/environment.js +69 -0
  89. package/dist/core/environment.js.map +1 -0
  90. package/dist/core/evidence-policy.d.ts +17 -0
  91. package/dist/core/evidence-policy.d.ts.map +1 -0
  92. package/dist/core/evidence-policy.js +25 -0
  93. package/dist/core/evidence-policy.js.map +1 -0
  94. package/dist/core/evidence.d.ts +16 -0
  95. package/dist/core/evidence.d.ts.map +1 -0
  96. package/dist/core/evidence.js +51 -0
  97. package/dist/core/evidence.js.map +1 -0
  98. package/dist/core/experimental.d.ts.map +1 -1
  99. package/dist/core/experimental.js +2 -1
  100. package/dist/core/experimental.js.map +1 -1
  101. package/dist/core/extensions/types.d.ts +2 -0
  102. package/dist/core/extensions/types.d.ts.map +1 -1
  103. package/dist/core/extensions/types.js.map +1 -1
  104. package/dist/core/index.d.ts +1 -0
  105. package/dist/core/index.d.ts.map +1 -1
  106. package/dist/core/index.js +1 -0
  107. package/dist/core/index.js.map +1 -1
  108. package/dist/core/model-config.d.ts +8 -0
  109. package/dist/core/model-config.d.ts.map +1 -1
  110. package/dist/core/model-config.js +21 -5
  111. package/dist/core/model-config.js.map +1 -1
  112. package/dist/core/model-resolver.d.ts +20 -0
  113. package/dist/core/model-resolver.d.ts.map +1 -1
  114. package/dist/core/model-resolver.js +59 -0
  115. package/dist/core/model-resolver.js.map +1 -1
  116. package/dist/core/model-runtime.d.ts +73 -0
  117. package/dist/core/model-runtime.d.ts.map +1 -1
  118. package/dist/core/model-runtime.js +225 -8
  119. package/dist/core/model-runtime.js.map +1 -1
  120. package/dist/core/observability/aggregate.d.ts +21 -0
  121. package/dist/core/observability/aggregate.d.ts.map +1 -0
  122. package/dist/core/observability/aggregate.js +36 -0
  123. package/dist/core/observability/aggregate.js.map +1 -0
  124. package/dist/core/observability/otlp.d.ts +66 -0
  125. package/dist/core/observability/otlp.d.ts.map +1 -0
  126. package/dist/core/observability/otlp.js +95 -0
  127. package/dist/core/observability/otlp.js.map +1 -0
  128. package/dist/core/package-manager.d.ts.map +1 -1
  129. package/dist/core/package-manager.js +3 -2
  130. package/dist/core/package-manager.js.map +1 -1
  131. package/dist/core/permissions/gate.d.ts +35 -0
  132. package/dist/core/permissions/gate.d.ts.map +1 -0
  133. package/dist/core/permissions/gate.js +72 -0
  134. package/dist/core/permissions/gate.js.map +1 -0
  135. package/dist/core/permissions/modes.d.ts +18 -0
  136. package/dist/core/permissions/modes.d.ts.map +1 -0
  137. package/dist/core/permissions/modes.js +57 -0
  138. package/dist/core/permissions/modes.js.map +1 -0
  139. package/dist/core/permissions/responder.d.ts +30 -0
  140. package/dist/core/permissions/responder.d.ts.map +1 -0
  141. package/dist/core/permissions/responder.js +32 -0
  142. package/dist/core/permissions/responder.js.map +1 -0
  143. package/dist/core/permissions/rules.d.ts +34 -0
  144. package/dist/core/permissions/rules.d.ts.map +1 -0
  145. package/dist/core/permissions/rules.js +74 -0
  146. package/dist/core/permissions/rules.js.map +1 -0
  147. package/dist/core/permissions/startup.d.ts +33 -0
  148. package/dist/core/permissions/startup.d.ts.map +1 -0
  149. package/dist/core/permissions/startup.js +55 -0
  150. package/dist/core/permissions/startup.js.map +1 -0
  151. package/dist/core/permissions/store.d.ts +114 -0
  152. package/dist/core/permissions/store.d.ts.map +1 -0
  153. package/dist/core/permissions/store.js +229 -0
  154. package/dist/core/permissions/store.js.map +1 -0
  155. package/dist/core/project-trust.d.ts.map +1 -1
  156. package/dist/core/project-trust.js +1 -1
  157. package/dist/core/project-trust.js.map +1 -1
  158. package/dist/core/provider-attribution.d.ts.map +1 -1
  159. package/dist/core/provider-attribution.js +6 -7
  160. package/dist/core/provider-attribution.js.map +1 -1
  161. package/dist/core/remote-catalog-provider.d.ts +2 -2
  162. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  163. package/dist/core/remote-catalog-provider.js +20 -16
  164. package/dist/core/remote-catalog-provider.js.map +1 -1
  165. package/dist/core/sandbox/child-entry.d.ts +2 -0
  166. package/dist/core/sandbox/child-entry.d.ts.map +1 -0
  167. package/dist/core/sandbox/child-entry.js +11 -0
  168. package/dist/core/sandbox/child-entry.js.map +1 -0
  169. package/dist/core/sandbox/cli-launch.d.ts +27 -0
  170. package/dist/core/sandbox/cli-launch.d.ts.map +1 -0
  171. package/dist/core/sandbox/cli-launch.js +133 -0
  172. package/dist/core/sandbox/cli-launch.js.map +1 -0
  173. package/dist/core/sandbox/cli-supervisor.d.ts +25 -0
  174. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -0
  175. package/dist/core/sandbox/cli-supervisor.js +57 -0
  176. package/dist/core/sandbox/cli-supervisor.js.map +1 -0
  177. package/dist/core/sandbox/linux-backend.d.ts +18 -0
  178. package/dist/core/sandbox/linux-backend.d.ts.map +1 -0
  179. package/dist/core/sandbox/linux-backend.js +186 -0
  180. package/dist/core/sandbox/linux-backend.js.map +1 -0
  181. package/dist/core/sandbox/macos-backend.d.ts +18 -0
  182. package/dist/core/sandbox/macos-backend.d.ts.map +1 -0
  183. package/dist/core/sandbox/macos-backend.js +195 -0
  184. package/dist/core/sandbox/macos-backend.js.map +1 -0
  185. package/dist/core/sandbox/network-proxy.d.ts +19 -0
  186. package/dist/core/sandbox/network-proxy.d.ts.map +1 -0
  187. package/dist/core/sandbox/network-proxy.js +79 -0
  188. package/dist/core/sandbox/network-proxy.js.map +1 -0
  189. package/dist/core/sandbox/policy.d.ts +32 -0
  190. package/dist/core/sandbox/policy.d.ts.map +1 -0
  191. package/dist/core/sandbox/policy.js +38 -0
  192. package/dist/core/sandbox/policy.js.map +1 -0
  193. package/dist/core/sandbox/supervisor.d.ts +31 -0
  194. package/dist/core/sandbox/supervisor.d.ts.map +1 -0
  195. package/dist/core/sandbox/supervisor.js +18 -0
  196. package/dist/core/sandbox/supervisor.js.map +1 -0
  197. package/dist/core/sandbox/violations.d.ts +23 -0
  198. package/dist/core/sandbox/violations.d.ts.map +1 -0
  199. package/dist/core/sandbox/violations.js +30 -0
  200. package/dist/core/sandbox/violations.js.map +1 -0
  201. package/dist/core/sdk.d.ts +20 -36
  202. package/dist/core/sdk.d.ts.map +1 -1
  203. package/dist/core/sdk.js +129 -2
  204. package/dist/core/sdk.js.map +1 -1
  205. package/dist/core/session-manager.d.ts +41 -2
  206. package/dist/core/session-manager.d.ts.map +1 -1
  207. package/dist/core/session-manager.js +57 -0
  208. package/dist/core/session-manager.js.map +1 -1
  209. package/dist/core/session-share.d.ts +16 -0
  210. package/dist/core/session-share.d.ts.map +1 -0
  211. package/dist/core/session-share.js +22 -0
  212. package/dist/core/session-share.js.map +1 -0
  213. package/dist/core/settings-manager.d.ts +38 -9
  214. package/dist/core/settings-manager.d.ts.map +1 -1
  215. package/dist/core/settings-manager.js +39 -24
  216. package/dist/core/settings-manager.js.map +1 -1
  217. package/dist/core/system-prompt.d.ts.map +1 -1
  218. package/dist/core/system-prompt.js +5 -5
  219. package/dist/core/system-prompt.js.map +1 -1
  220. package/dist/core/timings.d.ts +1 -1
  221. package/dist/core/timings.d.ts.map +1 -1
  222. package/dist/core/timings.js +3 -2
  223. package/dist/core/timings.js.map +1 -1
  224. package/dist/core/tools/ask-user.d.ts +25 -0
  225. package/dist/core/tools/ask-user.d.ts.map +1 -0
  226. package/dist/core/tools/ask-user.js +49 -0
  227. package/dist/core/tools/ask-user.js.map +1 -0
  228. package/dist/core/tools/bash-command-segments.d.ts +22 -0
  229. package/dist/core/tools/bash-command-segments.d.ts.map +1 -0
  230. package/dist/core/tools/bash-command-segments.js +128 -0
  231. package/dist/core/tools/bash-command-segments.js.map +1 -0
  232. package/dist/core/tools/bash.d.ts +26 -3
  233. package/dist/core/tools/bash.d.ts.map +1 -1
  234. package/dist/core/tools/bash.js +105 -16
  235. package/dist/core/tools/bash.js.map +1 -1
  236. package/dist/core/tools/contract.d.ts +156 -0
  237. package/dist/core/tools/contract.d.ts.map +1 -0
  238. package/dist/core/tools/contract.js +48 -0
  239. package/dist/core/tools/contract.js.map +1 -0
  240. package/dist/core/tools/delegate.d.ts +38 -0
  241. package/dist/core/tools/delegate.d.ts.map +1 -0
  242. package/dist/core/tools/delegate.js +66 -0
  243. package/dist/core/tools/delegate.js.map +1 -0
  244. package/dist/core/tools/edit.d.ts +2 -2
  245. package/dist/core/tools/edit.d.ts.map +1 -1
  246. package/dist/core/tools/edit.js +24 -0
  247. package/dist/core/tools/edit.js.map +1 -1
  248. package/dist/core/tools/find.d.ts +2 -2
  249. package/dist/core/tools/find.d.ts.map +1 -1
  250. package/dist/core/tools/find.js +12 -0
  251. package/dist/core/tools/find.js.map +1 -1
  252. package/dist/core/tools/grep.d.ts +2 -2
  253. package/dist/core/tools/grep.d.ts.map +1 -1
  254. package/dist/core/tools/grep.js +12 -0
  255. package/dist/core/tools/grep.js.map +1 -1
  256. package/dist/core/tools/index.d.ts +29 -4
  257. package/dist/core/tools/index.d.ts.map +1 -1
  258. package/dist/core/tools/index.js +118 -2
  259. package/dist/core/tools/index.js.map +1 -1
  260. package/dist/core/tools/ls.d.ts +2 -2
  261. package/dist/core/tools/ls.d.ts.map +1 -1
  262. package/dist/core/tools/ls.js +12 -0
  263. package/dist/core/tools/ls.js.map +1 -1
  264. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  265. package/dist/core/tools/output-accumulator.js +1 -1
  266. package/dist/core/tools/output-accumulator.js.map +1 -1
  267. package/dist/core/tools/path-permission.d.ts +18 -0
  268. package/dist/core/tools/path-permission.d.ts.map +1 -0
  269. package/dist/core/tools/path-permission.js +28 -0
  270. package/dist/core/tools/path-permission.js.map +1 -0
  271. package/dist/core/tools/plan-present.d.ts +27 -0
  272. package/dist/core/tools/plan-present.d.ts.map +1 -0
  273. package/dist/core/tools/plan-present.js +55 -0
  274. package/dist/core/tools/plan-present.js.map +1 -0
  275. package/dist/core/tools/read.d.ts +2 -2
  276. package/dist/core/tools/read.d.ts.map +1 -1
  277. package/dist/core/tools/read.js +12 -0
  278. package/dist/core/tools/read.js.map +1 -1
  279. package/dist/core/tools/test.d.ts +34 -0
  280. package/dist/core/tools/test.d.ts.map +1 -0
  281. package/dist/core/tools/test.js +75 -0
  282. package/dist/core/tools/test.js.map +1 -0
  283. package/dist/core/tools/todo-write.d.ts +31 -0
  284. package/dist/core/tools/todo-write.d.ts.map +1 -0
  285. package/dist/core/tools/todo-write.js +51 -0
  286. package/dist/core/tools/todo-write.js.map +1 -0
  287. package/dist/core/tools/tool-definition-wrapper.d.ts +13 -2
  288. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  289. package/dist/core/tools/tool-definition-wrapper.js +11 -1
  290. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  291. package/dist/core/tools/tool-schema.d.ts +22 -0
  292. package/dist/core/tools/tool-schema.d.ts.map +1 -0
  293. package/dist/core/tools/tool-schema.js +45 -0
  294. package/dist/core/tools/tool-schema.js.map +1 -0
  295. package/dist/core/tools/web-fetch.d.ts +26 -0
  296. package/dist/core/tools/web-fetch.d.ts.map +1 -0
  297. package/dist/core/tools/web-fetch.js +62 -0
  298. package/dist/core/tools/web-fetch.js.map +1 -0
  299. package/dist/core/tools/web-search.d.ts +26 -0
  300. package/dist/core/tools/web-search.d.ts.map +1 -0
  301. package/dist/core/tools/web-search.js +53 -0
  302. package/dist/core/tools/web-search.js.map +1 -0
  303. package/dist/core/tools/write.d.ts +6 -2
  304. package/dist/core/tools/write.d.ts.map +1 -1
  305. package/dist/core/tools/write.js +32 -1
  306. package/dist/core/tools/write.js.map +1 -1
  307. package/dist/core/usage-performance-store.d.ts +56 -0
  308. package/dist/core/usage-performance-store.d.ts.map +1 -0
  309. package/dist/core/usage-performance-store.js +85 -0
  310. package/dist/core/usage-performance-store.js.map +1 -0
  311. package/dist/index.d.ts +1 -0
  312. package/dist/index.d.ts.map +1 -1
  313. package/dist/index.js +1 -0
  314. package/dist/index.js.map +1 -1
  315. package/dist/main.d.ts.map +1 -1
  316. package/dist/main.js +48 -6
  317. package/dist/main.js.map +1 -1
  318. package/dist/migrations.d.ts.map +1 -1
  319. package/dist/migrations.js +2 -2
  320. package/dist/migrations.js.map +1 -1
  321. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  322. package/dist/modes/interactive/components/config-selector.js +9 -6
  323. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  324. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -4
  325. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  326. package/dist/modes/interactive/components/first-time-setup.js +11 -39
  327. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  328. package/dist/modes/interactive/components/footer.d.ts +8 -1
  329. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  330. package/dist/modes/interactive/components/footer.js +49 -20
  331. package/dist/modes/interactive/components/footer.js.map +1 -1
  332. package/dist/modes/interactive/components/settings-selector.d.ts +2 -2
  333. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  334. package/dist/modes/interactive/components/settings-selector.js +6 -6
  335. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  336. package/dist/modes/interactive/components/tool-execution.d.ts +2 -1
  337. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  338. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  339. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  340. package/dist/modes/interactive/external-editor.js +58 -9
  341. package/dist/modes/interactive/external-editor.js.map +1 -1
  342. package/dist/modes/interactive/interactive-mode.d.ts +2 -3
  343. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  344. package/dist/modes/interactive/interactive-mode.js +92 -104
  345. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  346. package/dist/modes/interactive/theme/theme-schema.json +2 -2
  347. package/dist/package-manager-cli.d.ts.map +1 -1
  348. package/dist/package-manager-cli.js +15 -10
  349. package/dist/package-manager-cli.js.map +1 -1
  350. package/dist/rpc-entry.d.ts.map +1 -1
  351. package/dist/rpc-entry.js +2 -1
  352. package/dist/rpc-entry.js.map +1 -1
  353. package/dist/server/create-harness.d.ts +1 -1
  354. package/dist/server/create-harness.d.ts.map +1 -1
  355. package/dist/server/create-harness.js +6 -5
  356. package/dist/server/create-harness.js.map +1 -1
  357. package/dist/testing/replay/metrics.d.ts +24 -0
  358. package/dist/testing/replay/metrics.d.ts.map +1 -0
  359. package/dist/testing/replay/metrics.js +35 -0
  360. package/dist/testing/replay/metrics.js.map +1 -0
  361. package/dist/testing/replay/recorded-provider.d.ts +10 -0
  362. package/dist/testing/replay/recorded-provider.d.ts.map +1 -0
  363. package/dist/testing/replay/recorded-provider.js +108 -0
  364. package/dist/testing/replay/recorded-provider.js.map +1 -0
  365. package/dist/testing/replay/runner.d.ts +56 -0
  366. package/dist/testing/replay/runner.d.ts.map +1 -0
  367. package/dist/testing/replay/runner.js +406 -0
  368. package/dist/testing/replay/runner.js.map +1 -0
  369. package/dist/utils/apex-code-user-agent.d.ts +2 -0
  370. package/dist/utils/apex-code-user-agent.d.ts.map +1 -0
  371. package/dist/utils/apex-code-user-agent.js +5 -0
  372. package/dist/utils/apex-code-user-agent.js.map +1 -0
  373. package/dist/utils/clipboard-image.d.ts.map +1 -1
  374. package/dist/utils/clipboard-image.js +1 -1
  375. package/dist/utils/clipboard-image.js.map +1 -1
  376. package/dist/utils/tools-manager.d.ts +32 -0
  377. package/dist/utils/tools-manager.d.ts.map +1 -1
  378. package/dist/utils/tools-manager.js +198 -100
  379. package/dist/utils/tools-manager.js.map +1 -1
  380. package/dist/utils/version-check.d.ts +5 -5
  381. package/dist/utils/version-check.d.ts.map +1 -1
  382. package/dist/utils/version-check.js +23 -14
  383. package/dist/utils/version-check.js.map +1 -1
  384. package/dist/utils/windows-self-update.d.ts.map +1 -1
  385. package/dist/utils/windows-self-update.js +1 -1
  386. package/dist/utils/windows-self-update.js.map +1 -1
  387. package/docs/compaction.md +21 -21
  388. package/docs/containerization.md +24 -21
  389. package/docs/custom-provider.md +9 -9
  390. package/docs/development.md +11 -11
  391. package/docs/environment-variables.md +36 -77
  392. package/docs/extensions.md +47 -47
  393. package/docs/index.md +12 -24
  394. package/docs/json.md +6 -6
  395. package/docs/keybindings.md +3 -3
  396. package/docs/llama-cpp.md +7 -7
  397. package/docs/models.md +11 -11
  398. package/docs/packages.md +46 -42
  399. package/docs/prompt-templates.md +3 -3
  400. package/docs/providers.md +10 -10
  401. package/docs/quickstart.md +49 -32
  402. package/docs/rpc.md +11 -11
  403. package/docs/sdk.md +4 -4
  404. package/docs/security.md +18 -18
  405. package/docs/session-format.md +9 -9
  406. package/docs/sessions.md +13 -13
  407. package/docs/settings.md +10 -14
  408. package/docs/shell-aliases.md +1 -1
  409. package/docs/skills.md +8 -8
  410. package/docs/terminal-setup.md +12 -12
  411. package/docs/termux.md +4 -4
  412. package/docs/themes.md +8 -8
  413. package/docs/tmux.md +4 -4
  414. package/docs/tui.md +3 -3
  415. package/docs/usage.md +54 -51
  416. package/docs/windows.md +1 -1
  417. package/npm-shrinkwrap.json +5 -5
  418. package/package.json +2 -3
  419. package/dist/core/telemetry.d.ts +0 -3
  420. package/dist/core/telemetry.d.ts.map +0 -1
  421. package/dist/core/telemetry.js +0 -9
  422. package/dist/core/telemetry.js.map +0 -1
  423. package/dist/utils/pi-user-agent.d.ts +0 -2
  424. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  425. package/dist/utils/pi-user-agent.js +0 -5
  426. package/dist/utils/pi-user-agent.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"create-harness.d.ts","sourceRoot":"","sources":["../../src/server/create-harness.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,YAAY,EACZ,KAAK,mBAAmB,EAMxB,KAAK,YAAY,EAEjB,KAAK,WAAW,EAChB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,KAAK,wBAAwB,EAAqB,MAAM,0BAA0B,CAAC;AAM5F,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAeD,MAAM,WAAW,+BAAgC,SAAQ,IAAI,CAAC,mBAAmB,EAAE,aAAa,GAAG,OAAO,CAAC;IAC1G,GAAG,EAAE,YAAY,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACjC,mBAAmB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,kBAAkB,GAAG,eAAe,GAAG,cAAc,CAAC,CAAC;CACpH;AAED,MAAM,WAAW,0CAA0C;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACzC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,mBAAmB,CAAC,EAAE,+BAA+B,CAAC,qBAAqB,CAAC,CAAC;CAC7E;AAED,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,0CAA0C,GAAG,MAAM,CAsB/G;AAED,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B;;;GAiFtF","sourcesContent":["import {\n\tAgentHarness,\n\ttype AgentHarnessOptions,\n\ttype AgentHarnessTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ExecutionEnv,\n\ttype ExecutionToolContext,\n\ttype HarnessTool,\n} from \"apex-code-agent-core\";\nimport type { Static, TSchema } from \"typebox\";\nimport { type BuildSystemPromptOptions, buildSystemPrompt } from \"../core/system-prompt.ts\";\nimport { bashToolSystemPromptContribution } from \"../core/tools/bash.ts\";\nimport { editToolSystemPromptContribution } from \"../core/tools/edit.ts\";\nimport { readToolSystemPromptContribution } from \"../core/tools/read.ts\";\nimport { writeToolSystemPromptContribution } from \"../core/tools/write.ts\";\n\nexport interface CodingAgentHarnessTool extends HarnessTool {\n\tpromptSnippet?: string;\n\tpromptGuidelines?: readonly string[];\n}\n\nfunction createCodingAgentHarnessTool<TParameters extends TSchema, TDetails>(\n\ttool: AgentHarnessTool<ExecutionToolContext, TParameters, TDetails>,\n\tcontext: ExecutionToolContext,\n\tprompt: Required<Pick<CodingAgentHarnessTool, \"promptSnippet\" | \"promptGuidelines\">>,\n): CodingAgentHarnessTool {\n\treturn {\n\t\t...tool,\n\t\t...prompt,\n\t\texecute: (toolCallId, params, signal, onUpdate) =>\n\t\t\ttool.execute(toolCallId, params as Static<TParameters>, signal, onUpdate, context),\n\t};\n}\n\nexport interface CreateCodingAgentHarnessOptions extends Omit<AgentHarnessOptions, \"toolContext\" | \"tools\"> {\n\tenv: ExecutionEnv;\n\tbashCommandPrefix?: string;\n\t/** Path to the JSONL session file exposed to default bash commands as PI_SESSION_FILE. */\n\tsessionFile?: string;\n\ttools?: CodingAgentHarnessTool[];\n\tsystemPromptOptions?: Omit<BuildSystemPromptOptions, \"cwd\" | \"promptGuidelines\" | \"selectedTools\" | \"toolSnippets\">;\n}\n\nexport interface BuildCodingAgentHarnessSystemPromptOptions {\n\tcwd: string;\n\ttools: readonly CodingAgentHarnessTool[];\n\tactiveToolNames: readonly string[];\n\tsystemPromptOptions?: CreateCodingAgentHarnessOptions[\"systemPromptOptions\"];\n}\n\nexport function buildCodingAgentHarnessSystemPrompt(options: BuildCodingAgentHarnessSystemPromptOptions): string {\n\tconst activeTools = options.activeToolNames.flatMap((name) => {\n\t\tconst tool = options.tools.find((candidate) => candidate.name === name);\n\t\treturn tool ? [tool] : [];\n\t});\n\tconst toolSnippets = Object.fromEntries(\n\t\tactiveTools.flatMap((tool) => {\n\t\t\tconst promptSnippet = tool.promptSnippet\n\t\t\t\t?.replace(/[\\r\\n]+/g, \" \")\n\t\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t\t.trim();\n\t\t\treturn promptSnippet ? [[tool.name, promptSnippet]] : [];\n\t\t}),\n\t);\n\tconst promptGuidelines = activeTools.flatMap((tool) => tool.promptGuidelines ?? []);\n\treturn buildSystemPrompt({\n\t\t...options.systemPromptOptions,\n\t\tcwd: options.cwd,\n\t\tselectedTools: activeTools.map((tool) => tool.name),\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t});\n}\n\nexport async function createCodingAgentHarness(options: CreateCodingAgentHarnessOptions) {\n\tconst {\n\t\tenv,\n\t\tbashCommandPrefix,\n\t\tsessionFile,\n\t\tsystemPromptOptions,\n\t\ttools: providedTools,\n\t\tactiveToolNames: providedActiveToolNames,\n\t\tsystemPrompt: providedSystemPrompt,\n\t\t...harnessOptions\n\t} = options;\n\tlet harness: AgentHarness | undefined;\n\tconst getHarness = (): AgentHarness => {\n\t\tif (!harness) throw new Error(\"Coding-agent Harness callback ran before Harness initialization\");\n\t\treturn harness;\n\t};\n\tlet tools = providedTools;\n\tif (tools === undefined) {\n\t\tconst metadata = await options.session.getMetadata();\n\t\tconst toolContext = { env } satisfies ExecutionToolContext;\n\t\ttools = [\n\t\t\tcreateCodingAgentHarnessTool(createReadTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: readToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: readToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(\n\t\t\t\tcreateBashTool<ExecutionToolContext>({\n\t\t\t\t\tcommandPrefix: bashCommandPrefix,\n\t\t\t\t\tprepare: async (execution) => {\n\t\t\t\t\t\tconst currentHarness = getHarness();\n\t\t\t\t\t\tconst [model, thinkingLevel] = await Promise.all([\n\t\t\t\t\t\t\tcurrentHarness.getModel(),\n\t\t\t\t\t\t\tcurrentHarness.getThinkingLevel(),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\texecution.env.PI_SESSION_ID = metadata.id;\n\t\t\t\t\t\texecution.env.PI_SESSION_FILE = sessionFile ?? \"\";\n\t\t\t\t\t\texecution.env.PI_PROVIDER = model.provider;\n\t\t\t\t\t\texecution.env.PI_MODEL = model.id;\n\t\t\t\t\t\texecution.env.PI_REASONING_LEVEL = thinkingLevel;\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\ttoolContext,\n\t\t\t\t{\n\t\t\t\t\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\t\t\t\t\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\t\t\t\t},\n\t\t\t),\n\t\t\tcreateCodingAgentHarnessTool(createEditTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: editToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(createWriteTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: writeToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t];\n\t}\n\tconst activeToolNames = [...(providedActiveToolNames ?? tools.map((tool) => tool.name))];\n\tconst systemPrompt =\n\t\tprovidedSystemPrompt ??\n\t\t(async () => {\n\t\t\tconst currentHarness = getHarness();\n\t\t\tconst [currentTools, currentActiveToolNames] = await Promise.all([\n\t\t\t\tcurrentHarness.getTools(),\n\t\t\t\tcurrentHarness.getActiveTools(),\n\t\t\t]);\n\t\t\treturn buildCodingAgentHarnessSystemPrompt({\n\t\t\t\tcwd: env.cwd,\n\t\t\t\ttools: currentTools,\n\t\t\t\tactiveToolNames: currentActiveToolNames,\n\t\t\t\tsystemPromptOptions,\n\t\t\t});\n\t\t});\n\tconst created = await AgentHarness.create({\n\t\t...harnessOptions,\n\t\ttools,\n\t\tactiveToolNames,\n\t\tsystemPrompt,\n\t});\n\tharness = created.harness;\n\treturn created;\n}\n"]}
1
+ {"version":3,"file":"create-harness.d.ts","sourceRoot":"","sources":["../../src/server/create-harness.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,YAAY,EACZ,KAAK,mBAAmB,EAMxB,KAAK,YAAY,EAEjB,KAAK,WAAW,EAChB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,KAAK,wBAAwB,EAAqB,MAAM,0BAA0B,CAAC;AAM5F,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAeD,MAAM,WAAW,+BAAgC,SAAQ,IAAI,CAAC,mBAAmB,EAAE,aAAa,GAAG,OAAO,CAAC;IAC1G,GAAG,EAAE,YAAY,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iGAAiG;IACjG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACjC,mBAAmB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,kBAAkB,GAAG,eAAe,GAAG,cAAc,CAAC,CAAC;CACpH;AAED,MAAM,WAAW,0CAA0C;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACzC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,mBAAmB,CAAC,EAAE,+BAA+B,CAAC,qBAAqB,CAAC,CAAC;CAC7E;AAED,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,0CAA0C,GAAG,MAAM,CAsB/G;AAED,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B;;;GAiFtF","sourcesContent":["import {\n\tAgentHarness,\n\ttype AgentHarnessOptions,\n\ttype AgentHarnessTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ExecutionEnv,\n\ttype ExecutionToolContext,\n\ttype HarnessTool,\n} from \"apex-code-agent-core\";\nimport type { Static, TSchema } from \"typebox\";\nimport { setApexEnvironment } from \"../core/environment.ts\";\nimport { type BuildSystemPromptOptions, buildSystemPrompt } from \"../core/system-prompt.ts\";\nimport { bashToolSystemPromptContribution } from \"../core/tools/bash.ts\";\nimport { editToolSystemPromptContribution } from \"../core/tools/edit.ts\";\nimport { readToolSystemPromptContribution } from \"../core/tools/read.ts\";\nimport { writeToolSystemPromptContribution } from \"../core/tools/write.ts\";\n\nexport interface CodingAgentHarnessTool extends HarnessTool {\n\tpromptSnippet?: string;\n\tpromptGuidelines?: readonly string[];\n}\n\nfunction createCodingAgentHarnessTool<TParameters extends TSchema, TDetails>(\n\ttool: AgentHarnessTool<ExecutionToolContext, TParameters, TDetails>,\n\tcontext: ExecutionToolContext,\n\tprompt: Required<Pick<CodingAgentHarnessTool, \"promptSnippet\" | \"promptGuidelines\">>,\n): CodingAgentHarnessTool {\n\treturn {\n\t\t...tool,\n\t\t...prompt,\n\t\texecute: (toolCallId, params, signal, onUpdate) =>\n\t\t\ttool.execute(toolCallId, params as Static<TParameters>, signal, onUpdate, context),\n\t};\n}\n\nexport interface CreateCodingAgentHarnessOptions extends Omit<AgentHarnessOptions, \"toolContext\" | \"tools\"> {\n\tenv: ExecutionEnv;\n\tbashCommandPrefix?: string;\n\t/** Path to the JSONL session file exposed to default bash commands as APEX_CODE_SESSION_FILE. */\n\tsessionFile?: string;\n\ttools?: CodingAgentHarnessTool[];\n\tsystemPromptOptions?: Omit<BuildSystemPromptOptions, \"cwd\" | \"promptGuidelines\" | \"selectedTools\" | \"toolSnippets\">;\n}\n\nexport interface BuildCodingAgentHarnessSystemPromptOptions {\n\tcwd: string;\n\ttools: readonly CodingAgentHarnessTool[];\n\tactiveToolNames: readonly string[];\n\tsystemPromptOptions?: CreateCodingAgentHarnessOptions[\"systemPromptOptions\"];\n}\n\nexport function buildCodingAgentHarnessSystemPrompt(options: BuildCodingAgentHarnessSystemPromptOptions): string {\n\tconst activeTools = options.activeToolNames.flatMap((name) => {\n\t\tconst tool = options.tools.find((candidate) => candidate.name === name);\n\t\treturn tool ? [tool] : [];\n\t});\n\tconst toolSnippets = Object.fromEntries(\n\t\tactiveTools.flatMap((tool) => {\n\t\t\tconst promptSnippet = tool.promptSnippet\n\t\t\t\t?.replace(/[\\r\\n]+/g, \" \")\n\t\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t\t.trim();\n\t\t\treturn promptSnippet ? [[tool.name, promptSnippet]] : [];\n\t\t}),\n\t);\n\tconst promptGuidelines = activeTools.flatMap((tool) => tool.promptGuidelines ?? []);\n\treturn buildSystemPrompt({\n\t\t...options.systemPromptOptions,\n\t\tcwd: options.cwd,\n\t\tselectedTools: activeTools.map((tool) => tool.name),\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t});\n}\n\nexport async function createCodingAgentHarness(options: CreateCodingAgentHarnessOptions) {\n\tconst {\n\t\tenv,\n\t\tbashCommandPrefix,\n\t\tsessionFile,\n\t\tsystemPromptOptions,\n\t\ttools: providedTools,\n\t\tactiveToolNames: providedActiveToolNames,\n\t\tsystemPrompt: providedSystemPrompt,\n\t\t...harnessOptions\n\t} = options;\n\tlet harness: AgentHarness | undefined;\n\tconst getHarness = (): AgentHarness => {\n\t\tif (!harness) throw new Error(\"Coding-agent Harness callback ran before Harness initialization\");\n\t\treturn harness;\n\t};\n\tlet tools = providedTools;\n\tif (tools === undefined) {\n\t\tconst metadata = await options.session.getMetadata();\n\t\tconst toolContext = { env } satisfies ExecutionToolContext;\n\t\ttools = [\n\t\t\tcreateCodingAgentHarnessTool(createReadTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: readToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: readToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(\n\t\t\t\tcreateBashTool<ExecutionToolContext>({\n\t\t\t\t\tcommandPrefix: bashCommandPrefix,\n\t\t\t\t\tprepare: async (execution) => {\n\t\t\t\t\t\tconst currentHarness = getHarness();\n\t\t\t\t\t\tconst [model, thinkingLevel] = await Promise.all([\n\t\t\t\t\t\t\tcurrentHarness.getModel(),\n\t\t\t\t\t\t\tcurrentHarness.getThinkingLevel(),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", metadata.id, execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_SESSION_FILE\", sessionFile ?? \"\", execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", model.provider, execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_MODEL\", model.id, execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", thinkingLevel, execution.env);\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\ttoolContext,\n\t\t\t\t{\n\t\t\t\t\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\t\t\t\t\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\t\t\t\t},\n\t\t\t),\n\t\t\tcreateCodingAgentHarnessTool(createEditTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: editToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(createWriteTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: writeToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t];\n\t}\n\tconst activeToolNames = [...(providedActiveToolNames ?? tools.map((tool) => tool.name))];\n\tconst systemPrompt =\n\t\tprovidedSystemPrompt ??\n\t\t(async () => {\n\t\t\tconst currentHarness = getHarness();\n\t\t\tconst [currentTools, currentActiveToolNames] = await Promise.all([\n\t\t\t\tcurrentHarness.getTools(),\n\t\t\t\tcurrentHarness.getActiveTools(),\n\t\t\t]);\n\t\t\treturn buildCodingAgentHarnessSystemPrompt({\n\t\t\t\tcwd: env.cwd,\n\t\t\t\ttools: currentTools,\n\t\t\t\tactiveToolNames: currentActiveToolNames,\n\t\t\t\tsystemPromptOptions,\n\t\t\t});\n\t\t});\n\tconst created = await AgentHarness.create({\n\t\t...harnessOptions,\n\t\ttools,\n\t\tactiveToolNames,\n\t\tsystemPrompt,\n\t});\n\tharness = created.harness;\n\treturn created;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { AgentHarness, createBashTool, createEditTool, createReadTool, createWriteTool, } from "apex-code-agent-core";
2
+ import { setApexEnvironment } from "../core/environment.js";
2
3
  import { buildSystemPrompt } from "../core/system-prompt.js";
3
4
  import { bashToolSystemPromptContribution } from "../core/tools/bash.js";
4
5
  import { editToolSystemPromptContribution } from "../core/tools/edit.js";
@@ -57,11 +58,11 @@ export async function createCodingAgentHarness(options) {
57
58
  currentHarness.getModel(),
58
59
  currentHarness.getThinkingLevel(),
59
60
  ]);
60
- execution.env.PI_SESSION_ID = metadata.id;
61
- execution.env.PI_SESSION_FILE = sessionFile ?? "";
62
- execution.env.PI_PROVIDER = model.provider;
63
- execution.env.PI_MODEL = model.id;
64
- execution.env.PI_REASONING_LEVEL = thinkingLevel;
61
+ setApexEnvironment("APEX_CODE_SESSION_ID", metadata.id, execution.env);
62
+ setApexEnvironment("APEX_CODE_SESSION_FILE", sessionFile ?? "", execution.env);
63
+ setApexEnvironment("APEX_CODE_PROVIDER", model.provider, execution.env);
64
+ setApexEnvironment("APEX_CODE_MODEL", model.id, execution.env);
65
+ setApexEnvironment("APEX_CODE_REASONING_LEVEL", thinkingLevel, execution.env);
65
66
  },
66
67
  }), toolContext, {
67
68
  promptSnippet: bashToolSystemPromptContribution.snippet,
@@ -1 +1 @@
1
- {"version":3,"file":"create-harness.js","sourceRoot":"","sources":["../../src/server/create-harness.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tAgentHarness,\n\ttype AgentHarnessOptions,\n\ttype AgentHarnessTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ExecutionEnv,\n\ttype ExecutionToolContext,\n\ttype HarnessTool,\n} from \"apex-code-agent-core\";\nimport type { Static, TSchema } from \"typebox\";\nimport { type BuildSystemPromptOptions, buildSystemPrompt } from \"../core/system-prompt.ts\";\nimport { bashToolSystemPromptContribution } from \"../core/tools/bash.ts\";\nimport { editToolSystemPromptContribution } from \"../core/tools/edit.ts\";\nimport { readToolSystemPromptContribution } from \"../core/tools/read.ts\";\nimport { writeToolSystemPromptContribution } from \"../core/tools/write.ts\";\n\nexport interface CodingAgentHarnessTool extends HarnessTool {\n\tpromptSnippet?: string;\n\tpromptGuidelines?: readonly string[];\n}\n\nfunction createCodingAgentHarnessTool<TParameters extends TSchema, TDetails>(\n\ttool: AgentHarnessTool<ExecutionToolContext, TParameters, TDetails>,\n\tcontext: ExecutionToolContext,\n\tprompt: Required<Pick<CodingAgentHarnessTool, \"promptSnippet\" | \"promptGuidelines\">>,\n): CodingAgentHarnessTool {\n\treturn {\n\t\t...tool,\n\t\t...prompt,\n\t\texecute: (toolCallId, params, signal, onUpdate) =>\n\t\t\ttool.execute(toolCallId, params as Static<TParameters>, signal, onUpdate, context),\n\t};\n}\n\nexport interface CreateCodingAgentHarnessOptions extends Omit<AgentHarnessOptions, \"toolContext\" | \"tools\"> {\n\tenv: ExecutionEnv;\n\tbashCommandPrefix?: string;\n\t/** Path to the JSONL session file exposed to default bash commands as PI_SESSION_FILE. */\n\tsessionFile?: string;\n\ttools?: CodingAgentHarnessTool[];\n\tsystemPromptOptions?: Omit<BuildSystemPromptOptions, \"cwd\" | \"promptGuidelines\" | \"selectedTools\" | \"toolSnippets\">;\n}\n\nexport interface BuildCodingAgentHarnessSystemPromptOptions {\n\tcwd: string;\n\ttools: readonly CodingAgentHarnessTool[];\n\tactiveToolNames: readonly string[];\n\tsystemPromptOptions?: CreateCodingAgentHarnessOptions[\"systemPromptOptions\"];\n}\n\nexport function buildCodingAgentHarnessSystemPrompt(options: BuildCodingAgentHarnessSystemPromptOptions): string {\n\tconst activeTools = options.activeToolNames.flatMap((name) => {\n\t\tconst tool = options.tools.find((candidate) => candidate.name === name);\n\t\treturn tool ? [tool] : [];\n\t});\n\tconst toolSnippets = Object.fromEntries(\n\t\tactiveTools.flatMap((tool) => {\n\t\t\tconst promptSnippet = tool.promptSnippet\n\t\t\t\t?.replace(/[\\r\\n]+/g, \" \")\n\t\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t\t.trim();\n\t\t\treturn promptSnippet ? [[tool.name, promptSnippet]] : [];\n\t\t}),\n\t);\n\tconst promptGuidelines = activeTools.flatMap((tool) => tool.promptGuidelines ?? []);\n\treturn buildSystemPrompt({\n\t\t...options.systemPromptOptions,\n\t\tcwd: options.cwd,\n\t\tselectedTools: activeTools.map((tool) => tool.name),\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t});\n}\n\nexport async function createCodingAgentHarness(options: CreateCodingAgentHarnessOptions) {\n\tconst {\n\t\tenv,\n\t\tbashCommandPrefix,\n\t\tsessionFile,\n\t\tsystemPromptOptions,\n\t\ttools: providedTools,\n\t\tactiveToolNames: providedActiveToolNames,\n\t\tsystemPrompt: providedSystemPrompt,\n\t\t...harnessOptions\n\t} = options;\n\tlet harness: AgentHarness | undefined;\n\tconst getHarness = (): AgentHarness => {\n\t\tif (!harness) throw new Error(\"Coding-agent Harness callback ran before Harness initialization\");\n\t\treturn harness;\n\t};\n\tlet tools = providedTools;\n\tif (tools === undefined) {\n\t\tconst metadata = await options.session.getMetadata();\n\t\tconst toolContext = { env } satisfies ExecutionToolContext;\n\t\ttools = [\n\t\t\tcreateCodingAgentHarnessTool(createReadTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: readToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: readToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(\n\t\t\t\tcreateBashTool<ExecutionToolContext>({\n\t\t\t\t\tcommandPrefix: bashCommandPrefix,\n\t\t\t\t\tprepare: async (execution) => {\n\t\t\t\t\t\tconst currentHarness = getHarness();\n\t\t\t\t\t\tconst [model, thinkingLevel] = await Promise.all([\n\t\t\t\t\t\t\tcurrentHarness.getModel(),\n\t\t\t\t\t\t\tcurrentHarness.getThinkingLevel(),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\texecution.env.PI_SESSION_ID = metadata.id;\n\t\t\t\t\t\texecution.env.PI_SESSION_FILE = sessionFile ?? \"\";\n\t\t\t\t\t\texecution.env.PI_PROVIDER = model.provider;\n\t\t\t\t\t\texecution.env.PI_MODEL = model.id;\n\t\t\t\t\t\texecution.env.PI_REASONING_LEVEL = thinkingLevel;\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\ttoolContext,\n\t\t\t\t{\n\t\t\t\t\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\t\t\t\t\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\t\t\t\t},\n\t\t\t),\n\t\t\tcreateCodingAgentHarnessTool(createEditTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: editToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(createWriteTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: writeToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t];\n\t}\n\tconst activeToolNames = [...(providedActiveToolNames ?? tools.map((tool) => tool.name))];\n\tconst systemPrompt =\n\t\tprovidedSystemPrompt ??\n\t\t(async () => {\n\t\t\tconst currentHarness = getHarness();\n\t\t\tconst [currentTools, currentActiveToolNames] = await Promise.all([\n\t\t\t\tcurrentHarness.getTools(),\n\t\t\t\tcurrentHarness.getActiveTools(),\n\t\t\t]);\n\t\t\treturn buildCodingAgentHarnessSystemPrompt({\n\t\t\t\tcwd: env.cwd,\n\t\t\t\ttools: currentTools,\n\t\t\t\tactiveToolNames: currentActiveToolNames,\n\t\t\t\tsystemPromptOptions,\n\t\t\t});\n\t\t});\n\tconst created = await AgentHarness.create({\n\t\t...harnessOptions,\n\t\ttools,\n\t\tactiveToolNames,\n\t\tsystemPrompt,\n\t});\n\tharness = created.harness;\n\treturn created;\n}\n"]}
1
+ {"version":3,"file":"create-harness.js","sourceRoot":"","sources":["../../src/server/create-harness.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tAgentHarness,\n\ttype AgentHarnessOptions,\n\ttype AgentHarnessTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ExecutionEnv,\n\ttype ExecutionToolContext,\n\ttype HarnessTool,\n} from \"apex-code-agent-core\";\nimport type { Static, TSchema } from \"typebox\";\nimport { setApexEnvironment } from \"../core/environment.ts\";\nimport { type BuildSystemPromptOptions, buildSystemPrompt } from \"../core/system-prompt.ts\";\nimport { bashToolSystemPromptContribution } from \"../core/tools/bash.ts\";\nimport { editToolSystemPromptContribution } from \"../core/tools/edit.ts\";\nimport { readToolSystemPromptContribution } from \"../core/tools/read.ts\";\nimport { writeToolSystemPromptContribution } from \"../core/tools/write.ts\";\n\nexport interface CodingAgentHarnessTool extends HarnessTool {\n\tpromptSnippet?: string;\n\tpromptGuidelines?: readonly string[];\n}\n\nfunction createCodingAgentHarnessTool<TParameters extends TSchema, TDetails>(\n\ttool: AgentHarnessTool<ExecutionToolContext, TParameters, TDetails>,\n\tcontext: ExecutionToolContext,\n\tprompt: Required<Pick<CodingAgentHarnessTool, \"promptSnippet\" | \"promptGuidelines\">>,\n): CodingAgentHarnessTool {\n\treturn {\n\t\t...tool,\n\t\t...prompt,\n\t\texecute: (toolCallId, params, signal, onUpdate) =>\n\t\t\ttool.execute(toolCallId, params as Static<TParameters>, signal, onUpdate, context),\n\t};\n}\n\nexport interface CreateCodingAgentHarnessOptions extends Omit<AgentHarnessOptions, \"toolContext\" | \"tools\"> {\n\tenv: ExecutionEnv;\n\tbashCommandPrefix?: string;\n\t/** Path to the JSONL session file exposed to default bash commands as APEX_CODE_SESSION_FILE. */\n\tsessionFile?: string;\n\ttools?: CodingAgentHarnessTool[];\n\tsystemPromptOptions?: Omit<BuildSystemPromptOptions, \"cwd\" | \"promptGuidelines\" | \"selectedTools\" | \"toolSnippets\">;\n}\n\nexport interface BuildCodingAgentHarnessSystemPromptOptions {\n\tcwd: string;\n\ttools: readonly CodingAgentHarnessTool[];\n\tactiveToolNames: readonly string[];\n\tsystemPromptOptions?: CreateCodingAgentHarnessOptions[\"systemPromptOptions\"];\n}\n\nexport function buildCodingAgentHarnessSystemPrompt(options: BuildCodingAgentHarnessSystemPromptOptions): string {\n\tconst activeTools = options.activeToolNames.flatMap((name) => {\n\t\tconst tool = options.tools.find((candidate) => candidate.name === name);\n\t\treturn tool ? [tool] : [];\n\t});\n\tconst toolSnippets = Object.fromEntries(\n\t\tactiveTools.flatMap((tool) => {\n\t\t\tconst promptSnippet = tool.promptSnippet\n\t\t\t\t?.replace(/[\\r\\n]+/g, \" \")\n\t\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t\t.trim();\n\t\t\treturn promptSnippet ? [[tool.name, promptSnippet]] : [];\n\t\t}),\n\t);\n\tconst promptGuidelines = activeTools.flatMap((tool) => tool.promptGuidelines ?? []);\n\treturn buildSystemPrompt({\n\t\t...options.systemPromptOptions,\n\t\tcwd: options.cwd,\n\t\tselectedTools: activeTools.map((tool) => tool.name),\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t});\n}\n\nexport async function createCodingAgentHarness(options: CreateCodingAgentHarnessOptions) {\n\tconst {\n\t\tenv,\n\t\tbashCommandPrefix,\n\t\tsessionFile,\n\t\tsystemPromptOptions,\n\t\ttools: providedTools,\n\t\tactiveToolNames: providedActiveToolNames,\n\t\tsystemPrompt: providedSystemPrompt,\n\t\t...harnessOptions\n\t} = options;\n\tlet harness: AgentHarness | undefined;\n\tconst getHarness = (): AgentHarness => {\n\t\tif (!harness) throw new Error(\"Coding-agent Harness callback ran before Harness initialization\");\n\t\treturn harness;\n\t};\n\tlet tools = providedTools;\n\tif (tools === undefined) {\n\t\tconst metadata = await options.session.getMetadata();\n\t\tconst toolContext = { env } satisfies ExecutionToolContext;\n\t\ttools = [\n\t\t\tcreateCodingAgentHarnessTool(createReadTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: readToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: readToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(\n\t\t\t\tcreateBashTool<ExecutionToolContext>({\n\t\t\t\t\tcommandPrefix: bashCommandPrefix,\n\t\t\t\t\tprepare: async (execution) => {\n\t\t\t\t\t\tconst currentHarness = getHarness();\n\t\t\t\t\t\tconst [model, thinkingLevel] = await Promise.all([\n\t\t\t\t\t\t\tcurrentHarness.getModel(),\n\t\t\t\t\t\t\tcurrentHarness.getThinkingLevel(),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", metadata.id, execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_SESSION_FILE\", sessionFile ?? \"\", execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", model.provider, execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_MODEL\", model.id, execution.env);\n\t\t\t\t\t\tsetApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", thinkingLevel, execution.env);\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\ttoolContext,\n\t\t\t\t{\n\t\t\t\t\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\t\t\t\t\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\t\t\t\t},\n\t\t\t),\n\t\t\tcreateCodingAgentHarnessTool(createEditTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: editToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t\tcreateCodingAgentHarnessTool(createWriteTool<ExecutionToolContext>(), toolContext, {\n\t\t\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\t\t\tpromptGuidelines: writeToolSystemPromptContribution.guidelines,\n\t\t\t}),\n\t\t];\n\t}\n\tconst activeToolNames = [...(providedActiveToolNames ?? tools.map((tool) => tool.name))];\n\tconst systemPrompt =\n\t\tprovidedSystemPrompt ??\n\t\t(async () => {\n\t\t\tconst currentHarness = getHarness();\n\t\t\tconst [currentTools, currentActiveToolNames] = await Promise.all([\n\t\t\t\tcurrentHarness.getTools(),\n\t\t\t\tcurrentHarness.getActiveTools(),\n\t\t\t]);\n\t\t\treturn buildCodingAgentHarnessSystemPrompt({\n\t\t\t\tcwd: env.cwd,\n\t\t\t\ttools: currentTools,\n\t\t\t\tactiveToolNames: currentActiveToolNames,\n\t\t\t\tsystemPromptOptions,\n\t\t\t});\n\t\t});\n\tconst created = await AgentHarness.create({\n\t\t...harnessOptions,\n\t\ttools,\n\t\tactiveToolNames,\n\t\tsystemPrompt,\n\t});\n\tharness = created.harness;\n\treturn created;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { AssistantMessage, Tool } from "@earendil-works/pi-ai";
2
+ /** The stable, provider-independent measurements emitted by an offline replay. */
3
+ export interface ReplayMetrics {
4
+ contextTokensByTurn: number[];
5
+ /** Estimated static provider input: the production system prompt plus tool schemas. */
6
+ systemPromptTokens: number;
7
+ cacheHitRate: number;
8
+ toolCallsByName: Record<string, number>;
9
+ /** Deterministic placeholder until recordings carry request durations. */
10
+ wallTimeMs: number;
11
+ costUsd: number;
12
+ turnsCompleted: number;
13
+ }
14
+ export interface ReplayMetricsInput {
15
+ systemPrompt: string;
16
+ tools: readonly Tool[];
17
+ recordedResponses: readonly AssistantMessage[];
18
+ replayedResponses: readonly AssistantMessage[];
19
+ contextTokensByTurn: readonly number[];
20
+ turnsCompleted: number;
21
+ }
22
+ /** Build metrics from stable provider-boundary and replay output data. */
23
+ export declare function buildReplayMetrics(input: ReplayMetricsInput): ReplayMetrics;
24
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/testing/replay/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAEpE,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC7B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,uFAAuF;IACvF,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IACvB,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CA+B3E","sourcesContent":["import type { AssistantMessage, Tool } from \"@earendil-works/pi-ai\";\n\n/** The stable, provider-independent measurements emitted by an offline replay. */\nexport interface ReplayMetrics {\n\tcontextTokensByTurn: number[];\n\t/** Estimated static provider input: the production system prompt plus tool schemas. */\n\tsystemPromptTokens: number;\n\tcacheHitRate: number;\n\ttoolCallsByName: Record<string, number>;\n\t/** Deterministic placeholder until recordings carry request durations. */\n\twallTimeMs: number;\n\tcostUsd: number;\n\tturnsCompleted: number;\n}\n\nexport interface ReplayMetricsInput {\n\tsystemPrompt: string;\n\ttools: readonly Tool[];\n\trecordedResponses: readonly AssistantMessage[];\n\treplayedResponses: readonly AssistantMessage[];\n\tcontextTokensByTurn: readonly number[];\n\tturnsCompleted: number;\n}\n\n/** Build metrics from stable provider-boundary and replay output data. */\nexport function buildReplayMetrics(input: ReplayMetricsInput): ReplayMetrics {\n\tlet inputTokens = 0;\n\tlet cacheRead = 0;\n\tlet cacheWrite = 0;\n\tlet costUsd = 0;\n\tfor (const response of input.recordedResponses) {\n\t\tinputTokens += response.usage.input;\n\t\tcacheRead += response.usage.cacheRead;\n\t\tcacheWrite += response.usage.cacheWrite;\n\t\tcostUsd += response.usage.cost.total;\n\t}\n\tconst toolCallCounts = new Map<string, number>();\n\tfor (const response of input.replayedResponses) {\n\t\tfor (const part of response.content) {\n\t\t\tif (part.type === \"toolCall\") toolCallCounts.set(part.name, (toolCallCounts.get(part.name) ?? 0) + 1);\n\t\t}\n\t}\n\tconst toolCallsByName = Object.fromEntries([...toolCallCounts.entries()].sort(([a], [b]) => a.localeCompare(b)));\n\tconst staticProviderInput = `${input.systemPrompt}\n${JSON.stringify(input.tools)}`;\n\tconst promptTokens = inputTokens + cacheRead + cacheWrite;\n\treturn {\n\t\tcontextTokensByTurn: [...input.contextTokensByTurn],\n\t\tsystemPromptTokens: Math.ceil(staticProviderInput.length / 4),\n\t\tcacheHitRate: promptTokens === 0 ? 0 : cacheRead / promptTokens,\n\t\ttoolCallsByName,\n\t\t// A live timer would make the equality gate flaky; recordings do not yet carry durations.\n\t\twallTimeMs: 0,\n\t\tcostUsd,\n\t\tturnsCompleted: input.turnsCompleted,\n\t};\n}\n"]}
@@ -0,0 +1,35 @@
1
+ /** Build metrics from stable provider-boundary and replay output data. */
2
+ export function buildReplayMetrics(input) {
3
+ let inputTokens = 0;
4
+ let cacheRead = 0;
5
+ let cacheWrite = 0;
6
+ let costUsd = 0;
7
+ for (const response of input.recordedResponses) {
8
+ inputTokens += response.usage.input;
9
+ cacheRead += response.usage.cacheRead;
10
+ cacheWrite += response.usage.cacheWrite;
11
+ costUsd += response.usage.cost.total;
12
+ }
13
+ const toolCallCounts = new Map();
14
+ for (const response of input.replayedResponses) {
15
+ for (const part of response.content) {
16
+ if (part.type === "toolCall")
17
+ toolCallCounts.set(part.name, (toolCallCounts.get(part.name) ?? 0) + 1);
18
+ }
19
+ }
20
+ const toolCallsByName = Object.fromEntries([...toolCallCounts.entries()].sort(([a], [b]) => a.localeCompare(b)));
21
+ const staticProviderInput = `${input.systemPrompt}
22
+ ${JSON.stringify(input.tools)}`;
23
+ const promptTokens = inputTokens + cacheRead + cacheWrite;
24
+ return {
25
+ contextTokensByTurn: [...input.contextTokensByTurn],
26
+ systemPromptTokens: Math.ceil(staticProviderInput.length / 4),
27
+ cacheHitRate: promptTokens === 0 ? 0 : cacheRead / promptTokens,
28
+ toolCallsByName,
29
+ // A live timer would make the equality gate flaky; recordings do not yet carry durations.
30
+ wallTimeMs: 0,
31
+ costUsd,
32
+ turnsCompleted: input.turnsCompleted,
33
+ };
34
+ }
35
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../src/testing/replay/metrics.ts"],"names":[],"mappings":"AAwBA,0EAA0E;AAC1E,MAAM,UAAU,kBAAkB,CAAC,KAAyB,EAAiB;IAC5E,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAChD,WAAW,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;QACpC,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;QACtC,UAAU,IAAI,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC;QACxC,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;IACtC,CAAC;IACD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjD,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAChD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;gBAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACvG,CAAC;IACF,CAAC;IACD,MAAM,eAAe,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjH,MAAM,mBAAmB,GAAG,GAAG,KAAK,CAAC,YAAY;EAChD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;IAC1D,OAAO;QACN,mBAAmB,EAAE,CAAC,GAAG,KAAK,CAAC,mBAAmB,CAAC;QACnD,kBAAkB,EAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7D,YAAY,EAAE,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,YAAY;QAC/D,eAAe;QACf,0FAA0F;QAC1F,UAAU,EAAE,CAAC;QACb,OAAO;QACP,cAAc,EAAE,KAAK,CAAC,cAAc;KACpC,CAAC;AAAA,CACF","sourcesContent":["import type { AssistantMessage, Tool } from \"@earendil-works/pi-ai\";\n\n/** The stable, provider-independent measurements emitted by an offline replay. */\nexport interface ReplayMetrics {\n\tcontextTokensByTurn: number[];\n\t/** Estimated static provider input: the production system prompt plus tool schemas. */\n\tsystemPromptTokens: number;\n\tcacheHitRate: number;\n\ttoolCallsByName: Record<string, number>;\n\t/** Deterministic placeholder until recordings carry request durations. */\n\twallTimeMs: number;\n\tcostUsd: number;\n\tturnsCompleted: number;\n}\n\nexport interface ReplayMetricsInput {\n\tsystemPrompt: string;\n\ttools: readonly Tool[];\n\trecordedResponses: readonly AssistantMessage[];\n\treplayedResponses: readonly AssistantMessage[];\n\tcontextTokensByTurn: readonly number[];\n\tturnsCompleted: number;\n}\n\n/** Build metrics from stable provider-boundary and replay output data. */\nexport function buildReplayMetrics(input: ReplayMetricsInput): ReplayMetrics {\n\tlet inputTokens = 0;\n\tlet cacheRead = 0;\n\tlet cacheWrite = 0;\n\tlet costUsd = 0;\n\tfor (const response of input.recordedResponses) {\n\t\tinputTokens += response.usage.input;\n\t\tcacheRead += response.usage.cacheRead;\n\t\tcacheWrite += response.usage.cacheWrite;\n\t\tcostUsd += response.usage.cost.total;\n\t}\n\tconst toolCallCounts = new Map<string, number>();\n\tfor (const response of input.replayedResponses) {\n\t\tfor (const part of response.content) {\n\t\t\tif (part.type === \"toolCall\") toolCallCounts.set(part.name, (toolCallCounts.get(part.name) ?? 0) + 1);\n\t\t}\n\t}\n\tconst toolCallsByName = Object.fromEntries([...toolCallCounts.entries()].sort(([a], [b]) => a.localeCompare(b)));\n\tconst staticProviderInput = `${input.systemPrompt}\n${JSON.stringify(input.tools)}`;\n\tconst promptTokens = inputTokens + cacheRead + cacheWrite;\n\treturn {\n\t\tcontextTokensByTurn: [...input.contextTokensByTurn],\n\t\tsystemPromptTokens: Math.ceil(staticProviderInput.length / 4),\n\t\tcacheHitRate: promptTokens === 0 ? 0 : cacheRead / promptTokens,\n\t\ttoolCallsByName,\n\t\t// A live timer would make the equality gate flaky; recordings do not yet carry durations.\n\t\twallTimeMs: 0,\n\t\tcostUsd,\n\t\tturnsCompleted: input.turnsCompleted,\n\t};\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { type AssistantMessage, type Model } from "@earendil-works/pi-ai";
2
+ import type { ModelRuntime } from "../../core/model-runtime.ts";
3
+ export interface RecordedProvider {
4
+ readonly requestCount: number;
5
+ readonly contexts: readonly import("@earendil-works/pi-ai").Context[];
6
+ getModel(modelId: string): Model<string>;
7
+ assertExhausted(): void;
8
+ }
9
+ export declare function registerRecordedProvider(runtime: ModelRuntime, responses: readonly AssistantMessage[]): RecordedProvider;
10
+ //# sourceMappingURL=recorded-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorded-provider.d.ts","sourceRoot":"","sources":["../../../src/testing/replay/recorded-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EAGrB,KAAK,KAAK,EACV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAMhE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,uBAAuB,EAAE,OAAO,EAAE,CAAC;IACtE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,eAAe,IAAI,IAAI,CAAC;CACxB;AA8DD,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,SAAS,gBAAgB,EAAE,GACpC,gBAAgB,CA2ClB","sourcesContent":["import {\n\ttype AssistantMessage,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\ttype Model,\n} from \"@earendil-works/pi-ai\";\nimport type { ModelRuntime } from \"../../core/model-runtime.ts\";\n\nconst RECORDED_PROVIDER_ID = \"recorded\";\nconst RECORDED_API = \"apex-recorded\";\nconst ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\nexport interface RecordedProvider {\n\treadonly requestCount: number;\n\treadonly contexts: readonly import(\"@earendil-works/pi-ai\").Context[];\n\tgetModel(modelId: string): Model<string>;\n\tassertExhausted(): void;\n}\n\nfunction completedStream(message: AssistantMessage): AssistantMessageEventStream {\n\tconst stream = createAssistantMessageEventStream();\n\tqueueMicrotask(() => {\n\t\tconst cloned = structuredClone(message);\n\t\tstream.push({ type: \"start\", partial: { ...cloned, content: [] } });\n\t\tif (cloned.stopReason === \"error\" || cloned.stopReason === \"aborted\") {\n\t\t\tstream.push({ type: \"error\", reason: cloned.stopReason, error: cloned });\n\t\t} else if (cloned.stopReason === \"pending\") {\n\t\t\tconst error = errorMessage(\"Recorded response cannot have a pending stop reason\", cloned.model);\n\t\t\tstream.push({ type: \"error\", reason: \"error\", error });\n\t\t\tstream.end(error);\n\t\t\treturn;\n\t\t} else {\n\t\t\tstream.push({ type: \"done\", reason: cloned.stopReason, message: cloned });\n\t\t}\n\t\tstream.end(cloned);\n\t});\n\treturn stream;\n}\n\nfunction errorMessage(message: string, modelId: string): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: RECORDED_API,\n\t\tprovider: RECORDED_PROVIDER_ID,\n\t\tmodel: modelId,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { ...ZERO_COST, total: 0 },\n\t\t},\n\t\tstopReason: \"error\",\n\t\terrorMessage: message,\n\t\ttimestamp: 0,\n\t};\n}\n\nfunction failedStream(message: string, modelId: string): AssistantMessageEventStream {\n\treturn completedStream(errorMessage(message, modelId));\n}\n\nfunction recordedModel(modelId: string): Model<string> {\n\treturn {\n\t\tid: modelId,\n\t\tname: modelId,\n\t\tapi: RECORDED_API,\n\t\tprovider: RECORDED_PROVIDER_ID,\n\t\tbaseUrl: \"replay://recorded\",\n\t\treasoning: false,\n\t\tinput: [\"text\", \"image\"],\n\t\tcost: ZERO_COST,\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 100_000,\n\t};\n}\n\nexport function registerRecordedProvider(\n\truntime: ModelRuntime,\n\tresponses: readonly AssistantMessage[],\n): RecordedProvider {\n\tconst pending = responses.map((message) => structuredClone(message));\n\tconst modelIds = [...new Set(pending.map((message) => message.model))];\n\tconst models = new Map(modelIds.map((modelId) => [modelId, recordedModel(modelId)]));\n\tlet requestCount = 0;\n\tconst contexts: import(\"@earendil-works/pi-ai\").Context[] = [];\n\n\truntime.registerProvider(RECORDED_PROVIDER_ID, {\n\t\tname: \"Recorded replay provider\",\n\t\tapi: RECORDED_API,\n\t\tapiKey: \"offline-replay\",\n\t\tmodels: [...models.values()],\n\t\tstreamSimple: (model, context) => {\n\t\t\trequestCount++;\n\t\t\tcontexts.push({\n\t\t\t\tsystemPrompt: context.systemPrompt,\n\t\t\t\tmessages: structuredClone(context.messages),\n\t\t\t});\n\t\t\tconst response = pending.shift();\n\t\t\tif (!response) return failedStream(`Recorded replay exhausted before request ${requestCount}`, model.id);\n\t\t\tif (response.model !== model.id) {\n\t\t\t\treturn failedStream(`Recorded model mismatch: expected ${response.model}, received ${model.id}`, model.id);\n\t\t\t}\n\t\t\treturn completedStream(response);\n\t\t},\n\t});\n\n\treturn {\n\t\tget requestCount() {\n\t\t\treturn requestCount;\n\t\t},\n\t\tget contexts() {\n\t\t\treturn contexts;\n\t\t},\n\t\tgetModel(modelId) {\n\t\t\tconst model = models.get(modelId);\n\t\t\tif (!model) throw new Error(`Unknown recorded model: ${modelId}`);\n\t\t\treturn model;\n\t\t},\n\t\tassertExhausted() {\n\t\t\tif (pending.length > 0) throw new Error(`Recorded replay left ${pending.length} response(s) unused`);\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,108 @@
1
+ import { createAssistantMessageEventStream, } from "@earendil-works/pi-ai";
2
+ const RECORDED_PROVIDER_ID = "recorded";
3
+ const RECORDED_API = "apex-recorded";
4
+ const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
5
+ function completedStream(message) {
6
+ const stream = createAssistantMessageEventStream();
7
+ queueMicrotask(() => {
8
+ const cloned = structuredClone(message);
9
+ stream.push({ type: "start", partial: { ...cloned, content: [] } });
10
+ if (cloned.stopReason === "error" || cloned.stopReason === "aborted") {
11
+ stream.push({ type: "error", reason: cloned.stopReason, error: cloned });
12
+ }
13
+ else if (cloned.stopReason === "pending") {
14
+ const error = errorMessage("Recorded response cannot have a pending stop reason", cloned.model);
15
+ stream.push({ type: "error", reason: "error", error });
16
+ stream.end(error);
17
+ return;
18
+ }
19
+ else {
20
+ stream.push({ type: "done", reason: cloned.stopReason, message: cloned });
21
+ }
22
+ stream.end(cloned);
23
+ });
24
+ return stream;
25
+ }
26
+ function errorMessage(message, modelId) {
27
+ return {
28
+ role: "assistant",
29
+ content: [],
30
+ api: RECORDED_API,
31
+ provider: RECORDED_PROVIDER_ID,
32
+ model: modelId,
33
+ usage: {
34
+ input: 0,
35
+ output: 0,
36
+ cacheRead: 0,
37
+ cacheWrite: 0,
38
+ totalTokens: 0,
39
+ cost: { ...ZERO_COST, total: 0 },
40
+ },
41
+ stopReason: "error",
42
+ errorMessage: message,
43
+ timestamp: 0,
44
+ };
45
+ }
46
+ function failedStream(message, modelId) {
47
+ return completedStream(errorMessage(message, modelId));
48
+ }
49
+ function recordedModel(modelId) {
50
+ return {
51
+ id: modelId,
52
+ name: modelId,
53
+ api: RECORDED_API,
54
+ provider: RECORDED_PROVIDER_ID,
55
+ baseUrl: "replay://recorded",
56
+ reasoning: false,
57
+ input: ["text", "image"],
58
+ cost: ZERO_COST,
59
+ contextWindow: 1_000_000,
60
+ maxTokens: 100_000,
61
+ };
62
+ }
63
+ export function registerRecordedProvider(runtime, responses) {
64
+ const pending = responses.map((message) => structuredClone(message));
65
+ const modelIds = [...new Set(pending.map((message) => message.model))];
66
+ const models = new Map(modelIds.map((modelId) => [modelId, recordedModel(modelId)]));
67
+ let requestCount = 0;
68
+ const contexts = [];
69
+ runtime.registerProvider(RECORDED_PROVIDER_ID, {
70
+ name: "Recorded replay provider",
71
+ api: RECORDED_API,
72
+ apiKey: "offline-replay",
73
+ models: [...models.values()],
74
+ streamSimple: (model, context) => {
75
+ requestCount++;
76
+ contexts.push({
77
+ systemPrompt: context.systemPrompt,
78
+ messages: structuredClone(context.messages),
79
+ });
80
+ const response = pending.shift();
81
+ if (!response)
82
+ return failedStream(`Recorded replay exhausted before request ${requestCount}`, model.id);
83
+ if (response.model !== model.id) {
84
+ return failedStream(`Recorded model mismatch: expected ${response.model}, received ${model.id}`, model.id);
85
+ }
86
+ return completedStream(response);
87
+ },
88
+ });
89
+ return {
90
+ get requestCount() {
91
+ return requestCount;
92
+ },
93
+ get contexts() {
94
+ return contexts;
95
+ },
96
+ getModel(modelId) {
97
+ const model = models.get(modelId);
98
+ if (!model)
99
+ throw new Error(`Unknown recorded model: ${modelId}`);
100
+ return model;
101
+ },
102
+ assertExhausted() {
103
+ if (pending.length > 0)
104
+ throw new Error(`Recorded replay left ${pending.length} response(s) unused`);
105
+ },
106
+ };
107
+ }
108
+ //# sourceMappingURL=recorded-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorded-provider.js","sourceRoot":"","sources":["../../../src/testing/replay/recorded-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AssistantMessage,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\ttype Model,\n} from \"@earendil-works/pi-ai\";\nimport type { ModelRuntime } from \"../../core/model-runtime.ts\";\n\nconst RECORDED_PROVIDER_ID = \"recorded\";\nconst RECORDED_API = \"apex-recorded\";\nconst ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\nexport interface RecordedProvider {\n\treadonly requestCount: number;\n\treadonly contexts: readonly import(\"@earendil-works/pi-ai\").Context[];\n\tgetModel(modelId: string): Model<string>;\n\tassertExhausted(): void;\n}\n\nfunction completedStream(message: AssistantMessage): AssistantMessageEventStream {\n\tconst stream = createAssistantMessageEventStream();\n\tqueueMicrotask(() => {\n\t\tconst cloned = structuredClone(message);\n\t\tstream.push({ type: \"start\", partial: { ...cloned, content: [] } });\n\t\tif (cloned.stopReason === \"error\" || cloned.stopReason === \"aborted\") {\n\t\t\tstream.push({ type: \"error\", reason: cloned.stopReason, error: cloned });\n\t\t} else if (cloned.stopReason === \"pending\") {\n\t\t\tconst error = errorMessage(\"Recorded response cannot have a pending stop reason\", cloned.model);\n\t\t\tstream.push({ type: \"error\", reason: \"error\", error });\n\t\t\tstream.end(error);\n\t\t\treturn;\n\t\t} else {\n\t\t\tstream.push({ type: \"done\", reason: cloned.stopReason, message: cloned });\n\t\t}\n\t\tstream.end(cloned);\n\t});\n\treturn stream;\n}\n\nfunction errorMessage(message: string, modelId: string): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: RECORDED_API,\n\t\tprovider: RECORDED_PROVIDER_ID,\n\t\tmodel: modelId,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { ...ZERO_COST, total: 0 },\n\t\t},\n\t\tstopReason: \"error\",\n\t\terrorMessage: message,\n\t\ttimestamp: 0,\n\t};\n}\n\nfunction failedStream(message: string, modelId: string): AssistantMessageEventStream {\n\treturn completedStream(errorMessage(message, modelId));\n}\n\nfunction recordedModel(modelId: string): Model<string> {\n\treturn {\n\t\tid: modelId,\n\t\tname: modelId,\n\t\tapi: RECORDED_API,\n\t\tprovider: RECORDED_PROVIDER_ID,\n\t\tbaseUrl: \"replay://recorded\",\n\t\treasoning: false,\n\t\tinput: [\"text\", \"image\"],\n\t\tcost: ZERO_COST,\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 100_000,\n\t};\n}\n\nexport function registerRecordedProvider(\n\truntime: ModelRuntime,\n\tresponses: readonly AssistantMessage[],\n): RecordedProvider {\n\tconst pending = responses.map((message) => structuredClone(message));\n\tconst modelIds = [...new Set(pending.map((message) => message.model))];\n\tconst models = new Map(modelIds.map((modelId) => [modelId, recordedModel(modelId)]));\n\tlet requestCount = 0;\n\tconst contexts: import(\"@earendil-works/pi-ai\").Context[] = [];\n\n\truntime.registerProvider(RECORDED_PROVIDER_ID, {\n\t\tname: \"Recorded replay provider\",\n\t\tapi: RECORDED_API,\n\t\tapiKey: \"offline-replay\",\n\t\tmodels: [...models.values()],\n\t\tstreamSimple: (model, context) => {\n\t\t\trequestCount++;\n\t\t\tcontexts.push({\n\t\t\t\tsystemPrompt: context.systemPrompt,\n\t\t\t\tmessages: structuredClone(context.messages),\n\t\t\t});\n\t\t\tconst response = pending.shift();\n\t\t\tif (!response) return failedStream(`Recorded replay exhausted before request ${requestCount}`, model.id);\n\t\t\tif (response.model !== model.id) {\n\t\t\t\treturn failedStream(`Recorded model mismatch: expected ${response.model}, received ${model.id}`, model.id);\n\t\t\t}\n\t\t\treturn completedStream(response);\n\t\t},\n\t});\n\n\treturn {\n\t\tget requestCount() {\n\t\t\treturn requestCount;\n\t\t},\n\t\tget contexts() {\n\t\t\treturn contexts;\n\t\t},\n\t\tgetModel(modelId) {\n\t\t\tconst model = models.get(modelId);\n\t\t\tif (!model) throw new Error(`Unknown recorded model: ${modelId}`);\n\t\t\treturn model;\n\t\t},\n\t\tassertExhausted() {\n\t\t\tif (pending.length > 0) throw new Error(`Recorded replay left ${pending.length} response(s) unused`);\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,56 @@
1
+ import { type AssistantMessage, type Message, type ToolResultMessage } from "@earendil-works/pi-ai";
2
+ import { type ReplayMetrics } from "./metrics.ts";
3
+ export interface ReplayResult {
4
+ turns: number;
5
+ requests: number;
6
+ networkCalls: number;
7
+ responses: AssistantMessage[];
8
+ toolResults: ToolResultMessage[];
9
+ metrics: ReplayMetrics;
10
+ /**
11
+ * The final outbound provider request's messages for each user turn — the same
12
+ * array `contextTokensByTurn` is measured from, exposed so tests can assert
13
+ * directly on context-pipeline effects (eviction markers, deferred schemas)
14
+ * without reaching into `RecordedProvider` internals.
15
+ */
16
+ contextsByTurn: Message[][];
17
+ }
18
+ /**
19
+ * Dedicated eviction budget for the replay harness — deliberately **not** derived
20
+ * from the fixture model's `contextWindow` (`recorded-provider.ts` hardcodes
21
+ * `1_000_000` for unrelated reasons predating this pipeline, shared by every
22
+ * fixture in the corpus, and is not to be changed here). Feeding that real
23
+ * `contextWindow` through production's `evictionBudget` formula
24
+ * (`../../core/context/pipeline.ts`) would yield a budget of roughly 490,000 tokens
25
+ * — orders of magnitude above anything in this corpus, so eviction would never
26
+ * observably fire and the gate it is meant to measure would stay blind, which is
27
+ * exactly the gap this task closes.
28
+ *
29
+ * `1_000` was chosen empirically — measured directly against `replayCorpus()`, not
30
+ * estimated — against three constraints that all have to hold simultaneously:
31
+ *
32
+ * - `tool-error-recovery.jsonl`'s single turn carries its entire ~759 tokens
33
+ * (mostly the static system prompt/tool-schema prefix) under budget, so its
34
+ * recorded `contextTokensByTurn: [759]` (`test/replay-runner.test.ts`) is
35
+ * unaffected — confirmed unchanged at this budget.
36
+ * - `heavy-tool-output.jsonl` carries a single ~8,400-token recoverable `read`
37
+ * result — comfortably over budget, so it evicts, which is the fixture's
38
+ * documented purpose ("Large deterministic result for eviction measurements",
39
+ * `fixtures/corpus/README.md`). Measured post-eviction turn: 748 tokens.
40
+ * - `long-tool-heavy.jsonl` accumulates roughly 14,300 evictable tokens by turn 20
41
+ * across ten interleaved `read`/`grep` results. `1_000` was verified against
42
+ * `REPLAY_EVICTION_BUDGET: 0` (the theoretical floor — evict every eligible
43
+ * result unconditionally) and produces the *same* turn-20 result, 1,769 tokens
44
+ * — an 88.4% drop from the pre-pipeline baseline of 15,272 — confirming `1_000`
45
+ * already reaches that floor rather than under-evicting. A first pass at
46
+ * `3_000` measured 3,195 (79.1%, just short of the phase's 80% per-fixture
47
+ * goal); `1_000` was chosen after confirming the floor is actually lower.
48
+ *
49
+ * This constant governs replay measurement only; it has no effect on production,
50
+ * which always calls `evictionBudget` with the real model's `contextWindow`.
51
+ */
52
+ export declare const REPLAY_EVICTION_BUDGET = 1000;
53
+ export declare function replay(filePath: string): Promise<ReplayResult>;
54
+ /** Replay every JSONL fixture in lexical order for byte-stable corpus output. */
55
+ export declare function replayCorpus(directory: string): Promise<Record<string, ReplayMetrics>>;
56
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/testing/replay/runner.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,gBAAgB,EAErB,KAAK,OAAO,EAGZ,KAAK,iBAAiB,EAEtB,MAAM,uBAAuB,CAAC;AAiB/B,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAGtE,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,OAAO,EAAE,aAAa,CAAC;IACvB;;;;;OAKG;IACH,cAAc,EAAE,OAAO,EAAE,EAAE,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AA8L5C,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmLpE;AAED,iFAAiF;AACjF,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAQ5F","sourcesContent":["import { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\ttype AssistantMessage,\n\tInMemoryModelsStore,\n\ttype Message,\n\ttype Tool,\n\ttype ToolCall,\n\ttype ToolResultMessage,\n\tType,\n} from \"@earendil-works/pi-ai\";\nimport { Agent, type AgentTool, type AgentToolResult } from \"apex-code-agent-core\";\nimport { AuthStorage } from \"../../core/auth-storage.ts\";\nimport { estimateTokens } from \"../../core/compaction/compaction.ts\";\nimport type { ContractLookup } from \"../../core/context/eviction.ts\";\nimport { installContextPipeline } from \"../../core/context/pipeline.ts\";\nimport { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport {\n\tbuildSessionContext,\n\tloadEntriesFromFile,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\tSessionManager,\n\ttype SessionMessageEntry,\n} from \"../../core/session-manager.ts\";\nimport { buildSystemPrompt } from \"../../core/system-prompt.ts\";\nimport { createAllToolDefinitions, type ToolDef } from \"../../core/tools/index.ts\";\nimport { buildReplayMetrics, type ReplayMetrics } from \"./metrics.ts\";\nimport { registerRecordedProvider } from \"./recorded-provider.ts\";\n\nexport interface ReplayResult {\n\tturns: number;\n\trequests: number;\n\tnetworkCalls: number;\n\tresponses: AssistantMessage[];\n\ttoolResults: ToolResultMessage[];\n\tmetrics: ReplayMetrics;\n\t/**\n\t * The final outbound provider request's messages for each user turn — the same\n\t * array `contextTokensByTurn` is measured from, exposed so tests can assert\n\t * directly on context-pipeline effects (eviction markers, deferred schemas)\n\t * without reaching into `RecordedProvider` internals.\n\t */\n\tcontextsByTurn: Message[][];\n}\n\n/**\n * Dedicated eviction budget for the replay harness — deliberately **not** derived\n * from the fixture model's `contextWindow` (`recorded-provider.ts` hardcodes\n * `1_000_000` for unrelated reasons predating this pipeline, shared by every\n * fixture in the corpus, and is not to be changed here). Feeding that real\n * `contextWindow` through production's `evictionBudget` formula\n * (`../../core/context/pipeline.ts`) would yield a budget of roughly 490,000 tokens\n * — orders of magnitude above anything in this corpus, so eviction would never\n * observably fire and the gate it is meant to measure would stay blind, which is\n * exactly the gap this task closes.\n *\n * `1_000` was chosen empirically — measured directly against `replayCorpus()`, not\n * estimated — against three constraints that all have to hold simultaneously:\n *\n * - `tool-error-recovery.jsonl`'s single turn carries its entire ~759 tokens\n * (mostly the static system prompt/tool-schema prefix) under budget, so its\n * recorded `contextTokensByTurn: [759]` (`test/replay-runner.test.ts`) is\n * unaffected — confirmed unchanged at this budget.\n * - `heavy-tool-output.jsonl` carries a single ~8,400-token recoverable `read`\n * result — comfortably over budget, so it evicts, which is the fixture's\n * documented purpose (\"Large deterministic result for eviction measurements\",\n * `fixtures/corpus/README.md`). Measured post-eviction turn: 748 tokens.\n * - `long-tool-heavy.jsonl` accumulates roughly 14,300 evictable tokens by turn 20\n * across ten interleaved `read`/`grep` results. `1_000` was verified against\n * `REPLAY_EVICTION_BUDGET: 0` (the theoretical floor — evict every eligible\n * result unconditionally) and produces the *same* turn-20 result, 1,769 tokens\n * — an 88.4% drop from the pre-pipeline baseline of 15,272 — confirming `1_000`\n * already reaches that floor rather than under-evicting. A first pass at\n * `3_000` measured 3,195 (79.1%, just short of the phase's 80% per-fixture\n * goal); `1_000` was chosen after confirming the floor is actually lower.\n *\n * This constant governs replay measurement only; it has no effect on production,\n * which always calls `evictionBudget` with the real model's `contextWindow`.\n */\nexport const REPLAY_EVICTION_BUDGET = 1_000;\n\n/**\n * Builds a `ContractLookup` from the real production tool definitions — a second,\n * independent call to `createAllToolDefinitions` alongside the one\n * `productionPromptAndSchemas` already makes for the static-prompt baseline. Kept\n * separate rather than threading that function's `definitions` out: the two\n * concerns (building the static prompt/schema baseline vs. resolving a tool's\n * contract for the pipeline) are unrelated, and `createAllToolDefinitions` is cheap\n * and pure, so a second call costs nothing and keeps both call sites simple.\n */\nfunction replayContractLookup(cwd: string): ContractLookup {\n\tconst definitions = createAllToolDefinitions(cwd);\n\treturn (name) => (Reflect.get(definitions, name) as Partial<ToolDef> | undefined)?.contract;\n}\n\nfunction isVersionThreeHeader(entry: unknown): entry is SessionHeader & { version: 3 } {\n\tif (entry === null || typeof entry !== \"object\") return false;\n\treturn Reflect.get(entry, \"type\") === \"session\" && Reflect.get(entry, \"version\") === 3;\n}\n\nfunction isSessionEntry(entry: unknown): entry is SessionEntry {\n\tif (entry === null || typeof entry !== \"object\") return false;\n\tconst type = Reflect.get(entry, \"type\");\n\tconst id = Reflect.get(entry, \"id\");\n\tconst parentId = Reflect.get(entry, \"parentId\");\n\treturn (\n\t\ttype !== \"session\" &&\n\t\ttypeof type === \"string\" &&\n\t\ttypeof id === \"string\" &&\n\t\t(parentId === null || typeof parentId === \"string\")\n\t);\n}\n\nfunction validateSessionTree(entries: readonly SessionEntry[]): void {\n\tconst ids = new Set<string>();\n\tconst parentById = new Map<string, string | null>();\n\tlet roots = 0;\n\tfor (const entry of entries) {\n\t\tif (!entry || typeof entry.id !== \"string\" || ids.has(entry.id))\n\t\t\tthrow new Error(\"Replay session contains invalid or duplicate entry ids\");\n\t\tids.add(entry.id);\n\t\tparentById.set(entry.id, entry.parentId);\n\t\tif (entry.parentId === null) roots++;\n\t\telse if (typeof entry.parentId !== \"string\") throw new Error(\"Replay session contains an invalid parentId\");\n\t}\n\tif (roots !== 1) throw new Error(\"Replay session must contain exactly one tree root\");\n\tfor (const entry of entries) {\n\t\tif (entry.parentId !== null && !ids.has(entry.parentId))\n\t\t\tthrow new Error(`Replay session has missing parent ${entry.parentId}`);\n\t\tconst seen = new Set<string>();\n\t\tlet current: string | null = entry.id;\n\t\twhile (current !== null) {\n\t\t\tif (seen.has(current)) throw new Error(\"Replay session contains a parent cycle\");\n\t\t\tseen.add(current);\n\t\t\tcurrent = parentById.get(current) ?? null;\n\t\t}\n\t}\n}\n\nfunction isMessageEntry(entry: SessionEntry): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\nfunction selectedResponses(entries: readonly SessionEntry[]): AssistantMessage[] {\n\treturn entries\n\t\t.filter(isMessageEntry)\n\t\t.map((entry) => entry.message)\n\t\t.filter((message): message is AssistantMessage => message.role === \"assistant\");\n}\n\nfunction selectedToolResults(entries: readonly SessionEntry[]): ToolResultMessage[] {\n\treturn entries\n\t\t.filter(isMessageEntry)\n\t\t.map((entry) => entry.message)\n\t\t.filter((message): message is ToolResultMessage => message.role === \"toolResult\");\n}\n\nfunction selectedUserEntries(entries: readonly SessionEntry[]): SessionMessageEntry[] {\n\treturn entries.filter(isMessageEntry).filter((entry) => entry.message.role === \"user\");\n}\n\nfunction toolCalls(responses: readonly AssistantMessage[]): ToolCall[] {\n\treturn responses.flatMap((message) => message.content.filter((part): part is ToolCall => part.type === \"toolCall\"));\n}\n\nfunction createRecordedTools(\n\tcalls: readonly ToolCall[],\n\tresults: readonly ToolResultMessage[],\n): { tools: AgentTool[]; assertExhausted(): void } {\n\tconst resultByCallId = new Map(results.map((result) => [result.toolCallId, structuredClone(result)]));\n\tconst unconsumed = new Set(resultByCallId.keys());\n\tconst tools = [...new Set(calls.map((call) => call.name))].map(\n\t\t(name): AgentTool => ({\n\t\t\tname,\n\t\t\tlabel: `Recorded ${name}`,\n\t\t\tdescription: \"Replays a recorded tool result without external side effects.\",\n\t\t\tparameters: Type.Any(),\n\t\t\texecute: async (toolCallId): Promise<AgentToolResult<unknown>> => {\n\t\t\t\tconst result = resultByCallId.get(toolCallId);\n\t\t\t\tif (!result) throw new Error(`Missing recorded tool result for ${toolCallId}`);\n\t\t\t\tunconsumed.delete(toolCallId);\n\t\t\t\tif (result.isError) {\n\t\t\t\t\tconst text = result.content.find((part) => part.type === \"text\")?.text ?? \"Recorded tool error\";\n\t\t\t\t\tthrow new Error(text);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: structuredClone(result.content),\n\t\t\t\t\tdetails: structuredClone(result.details),\n\t\t\t\t\tusage: structuredClone(result.usage),\n\t\t\t\t\taddedToolNames: structuredClone(result.addedToolNames),\n\t\t\t\t};\n\t\t\t},\n\t\t}),\n\t);\n\treturn {\n\t\ttools,\n\t\tassertExhausted() {\n\t\t\tif (unconsumed.size > 0) throw new Error(`Replay left ${unconsumed.size} tool result(s) unused`);\n\t\t},\n\t};\n}\n\nfunction assertResponsesMatch(actual: readonly AssistantMessage[], expected: readonly AssistantMessage[]): void {\n\tif (actual.length !== expected.length) {\n\t\tthrow new Error(`Replayed ${actual.length} assistant response(s), expected ${expected.length}`);\n\t}\n\tfor (const [index, response] of actual.entries()) {\n\t\tif (JSON.stringify(response) !== JSON.stringify(expected[index])) {\n\t\t\tthrow new Error(`Replayed assistant response ${index + 1} does not match the recording`);\n\t\t}\n\t}\n}\n\nfunction assertToolResultsMatch(actual: readonly ToolResultMessage[], expected: readonly ToolResultMessage[]): void {\n\tif (actual.length !== expected.length) {\n\t\tthrow new Error(`Replayed ${actual.length} tool result(s), expected ${expected.length}`);\n\t}\n\tfor (const [index, result] of actual.entries()) {\n\t\tconst recorded = expected[index];\n\t\tif (!recorded) throw new Error(`Missing recorded tool result ${index + 1}`);\n\t\tconst { timestamp: _actualTimestamp, ...actualStable } = result;\n\t\tconst { timestamp: _recordedTimestamp, ...recordedStable } = recorded;\n\t\tconst actualComparable = { ...actualStable };\n\t\tconst recordedComparable = { ...recordedStable };\n\t\tif (\n\t\t\tactualComparable.details === undefined ||\n\t\t\t(typeof actualComparable.details === \"object\" && Object.keys(actualComparable.details ?? {}).length === 0)\n\t\t)\n\t\t\tdelete actualComparable.details;\n\t\tif (\n\t\t\trecordedComparable.details === undefined ||\n\t\t\t(typeof recordedComparable.details === \"object\" && Object.keys(recordedComparable.details ?? {}).length === 0)\n\t\t)\n\t\t\tdelete recordedComparable.details;\n\t\tif (JSON.stringify(actualComparable) !== JSON.stringify(recordedComparable)) {\n\t\t\tthrow new Error(`Replayed tool result ${index + 1} does not match the recording`);\n\t\t}\n\t}\n}\n\nfunction productionPromptAndSchemas(toolNames: readonly string[]): { systemPrompt: string; tools: Tool[] } {\n\tconst definitions = createAllToolDefinitions(\"$HOME/replay-fixtures\");\n\tconst defaultToolNames = [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst selected = [...new Set([...defaultToolNames, ...toolNames])].sort().flatMap((name) => {\n\t\tconst definition = Reflect.get(definitions, name);\n\t\treturn definition && typeof definition === \"object\" ? [definition] : [];\n\t});\n\tconst toolSnippets = Object.fromEntries(\n\t\tselected.flatMap((definition) =>\n\t\t\ttypeof definition.promptSnippet === \"string\" ? [[definition.name, definition.promptSnippet]] : [],\n\t\t),\n\t);\n\tconst promptGuidelines = selected.flatMap((definition) => definition.promptGuidelines ?? []);\n\treturn {\n\t\tsystemPrompt: buildSystemPrompt({\n\t\t\tcwd: \"$HOME/replay-fixtures\",\n\t\t\tcustomPrompt: \"You are an expert coding assistant operating inside Apex Code.\",\n\t\t\tselectedTools: selected.map((definition) => definition.name),\n\t\t\ttoolSnippets,\n\t\t\tpromptGuidelines,\n\t\t}),\n\t\ttools: selected.map((definition) => ({\n\t\t\tname: definition.name,\n\t\t\tdescription: definition.description,\n\t\t\tparameters: definition.parameters,\n\t\t})),\n\t};\n}\n\nexport async function replay(filePath: string): Promise<ReplayResult> {\n\tlet physicalLines: unknown[];\n\ttry {\n\t\tphysicalLines = readFileSync(filePath, \"utf8\")\n\t\t\t.split(\"\\n\")\n\t\t\t.filter((line) => line.trim().length > 0)\n\t\t\t.map((line) => JSON.parse(line));\n\t} catch (error) {\n\t\tthrow new Error(`Replay requires valid JSONL: ${filePath}`, { cause: error });\n\t}\n\tconst fileEntries = loadEntriesFromFile(filePath);\n\tif (physicalLines.length !== fileEntries.length) throw new Error(\"Replay session contains an invalid physical line\");\n\tif (!isVersionThreeHeader(fileEntries[0])) {\n\t\tthrow new Error(`Replay requires a native version 3 session: ${filePath}`);\n\t}\n\tconst rawEntries = fileEntries.slice(1);\n\tconst rawSessionEntries = rawEntries.filter(isSessionEntry);\n\tif (rawSessionEntries.length !== rawEntries.length) throw new Error(\"Replay session contains an invalid entry\");\n\tvalidateSessionTree(rawSessionEntries);\n\tconst sessionManager = SessionManager.open(filePath);\n\tconst entries = sessionManager.getBranch();\n\tvalidateSessionTree(entries);\n\tconst responses = selectedResponses(entries);\n\tconst recordedToolResults = selectedToolResults(entries);\n\tconst users = selectedUserEntries(entries);\n\tif (users.length === 0 || responses.length === 0)\n\t\tthrow new Error(\"Replay session must contain user and assistant messages\");\n\n\tconst runtime = await ModelRuntime.create({\n\t\tcredentials: AuthStorage.inMemory(),\n\t\tmodelsPath: null,\n\t\tmodelsStore: new InMemoryModelsStore(),\n\t\tallowModelNetwork: false,\n\t\trefreshOnCreate: false,\n\t});\n\tconst recorded = registerRecordedProvider(runtime, responses);\n\tconst recordedTools = createRecordedTools(toolCalls(responses), recordedToolResults);\n\tconst replayedResponses: AssistantMessage[] = [];\n\tconst replayedToolResults: ToolResultMessage[] = [];\n\tconst contextTokensByTurn: number[] = [];\n\tlet turnsCompleted = 0;\n\tconst promptBaseline = productionPromptAndSchemas(recordedTools.tools.map((tool) => tool.name));\n\tlet networkCalls = 0;\n\tconst rejectNetwork: typeof globalThis.fetch = async () => {\n\t\tnetworkCalls++;\n\t\tthrow new Error(\"Network access is disabled during replay\");\n\t};\n\tconst firstContext = buildSessionContext(entries, users[0]?.id);\n\tconst firstResponse = responses[0];\n\tif (!firstResponse) throw new Error(\"Replay session has no recorded response\");\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tmodel: recorded.getModel(firstContext.model?.modelId ?? firstResponse.model),\n\t\t\tsystemPrompt: promptBaseline.systemPrompt,\n\t\t\ttools: recordedTools.tools,\n\t\t},\n\t\tstreamFn: (model, context, options) => runtime.streamSimple(model, context, { ...options, fetch: rejectNetwork }),\n\t\tgetApiKey: () => \"offline-replay\",\n\t\tconvertToLlm: (messages) => {\n\t\t\tconst converted: Message[] = [];\n\t\t\tfor (const message of messages) {\n\t\t\t\tif (message.role === \"user\") {\n\t\t\t\t\tconverted.push({ role: \"user\", content: message.content, timestamp: message.timestamp });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (message.role === \"assistant\") {\n\t\t\t\t\tconverted.push(message);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (message.role !== \"toolResult\") continue;\n\t\t\t\t// Validate the call id is one this replay actually recorded, but send the\n\t\t\t\t// *incoming* message — not a fresh lookup by id — so that whatever the\n\t\t\t\t// context pipeline's eviction stage did to `message.content` upstream\n\t\t\t\t// (in `transformContext`) is what reaches the provider request. Re-fetching\n\t\t\t\t// the original recorded content here would silently discard eviction,\n\t\t\t\t// since eviction never touches `recordedToolResults` itself (see\n\t\t\t\t// `assertToolResultsMatch`, which intentionally still compares against the\n\t\t\t\t// untouched recording).\n\t\t\t\tconst recordedResult = recordedToolResults.find((result) => result.toolCallId === message.toolCallId);\n\t\t\t\tif (!recordedResult) throw new Error(`Missing recorded tool result for ${message.toolCallId}`);\n\t\t\t\tconverted.push(structuredClone(message));\n\t\t\t}\n\t\t\treturn converted;\n\t\t},\n\t\tafterToolCall: async ({ toolCall }) => {\n\t\t\tconst result = recordedToolResults.find((candidate) => candidate.toolCallId === toolCall.id);\n\t\t\tif (!result) throw new Error(`Missing recorded tool result for ${toolCall.id}`);\n\t\t\treturn {\n\t\t\t\tcontent: structuredClone(result.content),\n\t\t\t\tdetails: result.details,\n\t\t\t\tusage: result.usage,\n\t\t\t\tisError: result.isError,\n\t\t\t};\n\t\t},\n\t\ttoolExecution: \"sequential\",\n\t});\n\n\t// Same pipeline `AgentSession` installs in production (`_installContextPipeline`\n\t// in `core/agent-session.ts`), via the shared `installContextPipeline` — deferred\n\t// -schema resolution ahead of every request, then tool-result eviction — so the\n\t// replay gate can observe both. The contract lookup is built from the real\n\t// production tool definitions; the budget is `REPLAY_EVICTION_BUDGET`, a\n\t// dedicated constant documented above (not derived from the fixture model's\n\t// `contextWindow`; see that constant's comment for why).\n\tinstallContextPipeline(agent, {\n\t\tcontractLookup: replayContractLookup(\"$HOME/replay-fixtures\"),\n\t\tevictionBudget: () => REPLAY_EVICTION_BUDGET,\n\t});\n\n\tconst contextsByTurn: Message[][] = [];\n\tfor (const user of users) {\n\t\tconst historical = buildSessionContext(entries, user.id);\n\t\tconst nextResponse = responses[recorded.requestCount];\n\t\tif (!nextResponse) throw new Error(`Missing recorded response for user turn ${contextTokensByTurn.length + 1}`);\n\t\tagent.state.model = recorded.getModel(historical.model?.modelId ?? nextResponse.model);\n\t\tagent.state.messages = historical.messages.slice(0, -1);\n\t\tconst requestStart = recorded.requestCount;\n\t\tawait agent.prompt(user.message);\n\t\tconst turnContexts = recorded.contexts.slice(requestStart);\n\t\tconst lastContext = turnContexts.at(-1);\n\t\tif (!lastContext) throw new Error(`User turn ${contextTokensByTurn.length + 1} made no provider request`);\n\t\tconst staticInputTokens = Math.ceil(\n\t\t\t`${promptBaseline.systemPrompt}\\n${JSON.stringify(promptBaseline.tools)}`.length / 4,\n\t\t);\n\t\tcontextTokensByTurn.push(\n\t\t\tstaticInputTokens + lastContext.messages.reduce((total, message) => total + estimateTokens(message), 0),\n\t\t);\n\t\t// Normalize toolResult timestamps to the recorded value before exposing this\n\t\t// turn's context, mirroring the same normalization the `toolResults` field\n\t\t// below already does and for the same reason: a toolResult's timestamp is\n\t\t// stamped live (via `agent.state.messages`) rather than replayed from the\n\t\t// fixture, so leaving it as-is would make `contextsByTurn` — and anything\n\t\t// that serializes it, like the determinism test below — flaky across runs.\n\t\t// Content (what eviction may have rewritten) is untouched.\n\t\tcontextsByTurn.push(\n\t\t\tlastContext.messages.map((message) =>\n\t\t\t\tmessage.role === \"toolResult\"\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...message,\n\t\t\t\t\t\t\ttimestamp:\n\t\t\t\t\t\t\t\trecordedToolResults.find((result) => result.toolCallId === message.toolCallId)?.timestamp ??\n\t\t\t\t\t\t\t\tmessage.timestamp,\n\t\t\t\t\t\t}\n\t\t\t\t\t: message,\n\t\t\t),\n\t\t);\n\t\tfor (const message of agent.state.messages.slice(historical.messages.length - 1)) {\n\t\t\tif (message.role === \"assistant\") replayedResponses.push(message);\n\t\t\tif (message.role === \"toolResult\") replayedToolResults.push(message);\n\t\t}\n\t\tconst terminalResponse = replayedResponses.at(-1);\n\t\tif (terminalResponse && terminalResponse.stopReason !== \"error\" && terminalResponse.stopReason !== \"aborted\") {\n\t\t\tturnsCompleted++;\n\t\t}\n\t}\n\trecorded.assertExhausted();\n\trecordedTools.assertExhausted();\n\tassertResponsesMatch(replayedResponses, responses);\n\tassertToolResultsMatch(replayedToolResults, recordedToolResults);\n\n\treturn {\n\t\tturns: users.length,\n\t\trequests: recorded.requestCount,\n\t\tnetworkCalls,\n\t\tresponses: replayedResponses,\n\t\ttoolResults: replayedToolResults.map((result, index) => ({\n\t\t\t...structuredClone(result),\n\t\t\ttimestamp: recordedToolResults[index]?.timestamp ?? result.timestamp,\n\t\t})),\n\t\tcontextsByTurn,\n\t\tmetrics: buildReplayMetrics({\n\t\t\tsystemPrompt: promptBaseline.systemPrompt,\n\t\t\ttools: promptBaseline.tools,\n\t\t\trecordedResponses: responses,\n\t\t\treplayedResponses,\n\t\t\tcontextTokensByTurn,\n\t\t\tturnsCompleted,\n\t\t}),\n\t};\n}\n\n/** Replay every JSONL fixture in lexical order for byte-stable corpus output. */\nexport async function replayCorpus(directory: string): Promise<Record<string, ReplayMetrics>> {\n\tconst metrics: Record<string, ReplayMetrics> = {};\n\tfor (const name of readdirSync(directory)\n\t\t.filter((entry) => entry.endsWith(\".jsonl\"))\n\t\t.sort()) {\n\t\tmetrics[name] = (await replay(join(directory, name))).metrics;\n\t}\n\treturn metrics;\n}\n"]}