@bolt-foundry/gambit 0.8.3 → 0.8.5-rc.11

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 (291) hide show
  1. package/CHANGELOG.md +95 -2
  2. package/README.md +95 -23
  3. package/esm/_dnt.polyfills.d.ts +17 -0
  4. package/esm/_dnt.polyfills.d.ts.map +1 -1
  5. package/esm/_dnt.polyfills.js +123 -0
  6. package/esm/deps/jsr.io/@std/collections/1.1.6/deep_merge.d.ts +322 -0
  7. package/esm/deps/jsr.io/@std/collections/1.1.6/deep_merge.d.ts.map +1 -0
  8. package/esm/deps/jsr.io/@std/collections/1.1.6/deep_merge.js +105 -0
  9. package/esm/deps/jsr.io/@std/fs/1.0.23/_create_walk_entry.d.ts +14 -0
  10. package/esm/deps/jsr.io/@std/fs/1.0.23/_create_walk_entry.d.ts.map +1 -0
  11. package/esm/deps/jsr.io/@std/fs/1.0.23/_create_walk_entry.js +34 -0
  12. package/esm/deps/jsr.io/@std/fs/1.0.23/_get_file_info_type.d.ts +13 -0
  13. package/esm/deps/jsr.io/@std/fs/1.0.23/_get_file_info_type.d.ts.map +1 -0
  14. package/esm/deps/jsr.io/@std/fs/1.0.23/_get_file_info_type.js +18 -0
  15. package/esm/deps/jsr.io/@std/fs/1.0.23/_is_same_path.d.ts +10 -0
  16. package/esm/deps/jsr.io/@std/fs/1.0.23/_is_same_path.d.ts.map +1 -0
  17. package/esm/deps/jsr.io/@std/fs/1.0.23/_is_same_path.js +17 -0
  18. package/esm/deps/jsr.io/@std/fs/1.0.23/_is_subdir.d.ts +12 -0
  19. package/esm/deps/jsr.io/@std/fs/1.0.23/_is_subdir.d.ts.map +1 -0
  20. package/esm/deps/jsr.io/@std/fs/1.0.23/_is_subdir.js +25 -0
  21. package/esm/deps/jsr.io/@std/fs/1.0.23/_to_path_string.d.ts +9 -0
  22. package/esm/deps/jsr.io/@std/fs/1.0.23/_to_path_string.d.ts.map +1 -0
  23. package/esm/deps/jsr.io/@std/fs/1.0.23/_to_path_string.js +13 -0
  24. package/esm/deps/jsr.io/@std/fs/1.0.23/copy.d.ts +117 -0
  25. package/esm/deps/jsr.io/@std/fs/1.0.23/copy.d.ts.map +1 -0
  26. package/esm/deps/jsr.io/@std/fs/1.0.23/copy.js +313 -0
  27. package/esm/deps/jsr.io/@std/fs/1.0.23/empty_dir.d.ts +48 -0
  28. package/esm/deps/jsr.io/@std/fs/1.0.23/empty_dir.d.ts.map +1 -0
  29. package/esm/deps/jsr.io/@std/fs/1.0.23/empty_dir.js +87 -0
  30. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_dir.d.ts +49 -0
  31. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_dir.d.ts.map +1 -0
  32. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_dir.js +102 -0
  33. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_file.d.ts +47 -0
  34. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_file.d.ts.map +1 -0
  35. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_file.js +90 -0
  36. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_link.d.ts +49 -0
  37. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_link.d.ts.map +1 -0
  38. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_link.js +61 -0
  39. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_symlink.d.ts +70 -0
  40. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_symlink.d.ts.map +1 -0
  41. package/esm/deps/jsr.io/@std/fs/1.0.23/ensure_symlink.js +156 -0
  42. package/esm/deps/jsr.io/@std/fs/1.0.23/eol.d.ts +52 -0
  43. package/esm/deps/jsr.io/@std/fs/1.0.23/eol.d.ts.map +1 -0
  44. package/esm/deps/jsr.io/@std/fs/1.0.23/eol.js +67 -0
  45. package/esm/deps/jsr.io/@std/fs/1.0.23/exists.d.ts +218 -0
  46. package/esm/deps/jsr.io/@std/fs/1.0.23/exists.d.ts.map +1 -0
  47. package/esm/deps/jsr.io/@std/fs/1.0.23/exists.js +271 -0
  48. package/esm/deps/jsr.io/@std/fs/1.0.23/expand_glob.d.ts +267 -0
  49. package/esm/deps/jsr.io/@std/fs/1.0.23/expand_glob.d.ts.map +1 -0
  50. package/esm/deps/jsr.io/@std/fs/1.0.23/expand_glob.js +442 -0
  51. package/esm/deps/jsr.io/@std/fs/1.0.23/mod.d.ts +29 -0
  52. package/esm/deps/jsr.io/@std/fs/1.0.23/mod.d.ts.map +1 -0
  53. package/esm/deps/jsr.io/@std/fs/1.0.23/mod.js +29 -0
  54. package/esm/deps/jsr.io/@std/fs/1.0.23/move.d.ts +86 -0
  55. package/esm/deps/jsr.io/@std/fs/1.0.23/move.d.ts.map +1 -0
  56. package/esm/deps/jsr.io/@std/fs/1.0.23/move.js +142 -0
  57. package/esm/deps/jsr.io/@std/fs/1.0.23/walk.d.ts +777 -0
  58. package/esm/deps/jsr.io/@std/fs/1.0.23/walk.d.ts.map +1 -0
  59. package/esm/deps/jsr.io/@std/fs/1.0.23/walk.js +846 -0
  60. package/esm/deps/jsr.io/@std/json/1.0.3/types.d.ts +7 -0
  61. package/esm/deps/jsr.io/@std/json/1.0.3/types.d.ts.map +1 -0
  62. package/esm/deps/jsr.io/@std/json/1.0.3/types.js +3 -0
  63. package/esm/deps/jsr.io/@std/jsonc/1.0.2/mod.d.ts +20 -0
  64. package/esm/deps/jsr.io/@std/jsonc/1.0.2/mod.d.ts.map +1 -0
  65. package/esm/deps/jsr.io/@std/jsonc/1.0.2/mod.js +21 -0
  66. package/esm/deps/jsr.io/@std/jsonc/1.0.2/parse.d.ts +21 -0
  67. package/esm/deps/jsr.io/@std/jsonc/1.0.2/parse.d.ts.map +1 -0
  68. package/esm/deps/jsr.io/@std/jsonc/1.0.2/parse.js +320 -0
  69. package/esm/deps/jsr.io/@std/path/1.1.4/constants.d.ts +1 -1
  70. package/esm/deps/jsr.io/@std/toml/1.0.11/_parser.d.ts +93 -0
  71. package/esm/deps/jsr.io/@std/toml/1.0.11/_parser.d.ts.map +1 -0
  72. package/esm/deps/jsr.io/@std/toml/1.0.11/_parser.js +753 -0
  73. package/esm/deps/jsr.io/@std/toml/1.0.11/mod.d.ts +109 -0
  74. package/esm/deps/jsr.io/@std/toml/1.0.11/mod.d.ts.map +1 -0
  75. package/esm/deps/jsr.io/@std/toml/1.0.11/mod.js +110 -0
  76. package/esm/deps/jsr.io/@std/toml/1.0.11/parse.d.ts +21 -0
  77. package/esm/deps/jsr.io/@std/toml/1.0.11/parse.d.ts.map +1 -0
  78. package/esm/deps/jsr.io/@std/toml/1.0.11/parse.js +25 -0
  79. package/esm/deps/jsr.io/@std/toml/1.0.11/stringify.d.ts +35 -0
  80. package/esm/deps/jsr.io/@std/toml/1.0.11/stringify.d.ts.map +1 -0
  81. package/esm/deps/jsr.io/@std/toml/1.0.11/stringify.js +283 -0
  82. package/esm/gambit/simulator-ui/dist/bundle.js +13788 -5157
  83. package/esm/gambit/simulator-ui/dist/bundle.js.map +4 -4
  84. package/esm/mod.d.ts +13 -3
  85. package/esm/mod.d.ts.map +1 -1
  86. package/esm/mod.js +8 -2
  87. package/esm/src/cli_utils.d.ts +1 -0
  88. package/esm/src/cli_utils.d.ts.map +1 -1
  89. package/esm/src/cli_utils.js +13 -1
  90. package/esm/src/default_runtime.d.ts +46 -0
  91. package/esm/src/default_runtime.d.ts.map +1 -0
  92. package/esm/src/default_runtime.js +415 -0
  93. package/esm/src/durable_streams.js +26 -1
  94. package/esm/src/model_matchers.d.ts +10 -0
  95. package/esm/src/model_matchers.d.ts.map +1 -0
  96. package/esm/src/model_matchers.js +26 -0
  97. package/esm/src/openai_compat.d.ts +12 -1
  98. package/esm/src/openai_compat.d.ts.map +1 -1
  99. package/esm/src/openai_compat.js +53 -1
  100. package/esm/src/project_config.d.ts +47 -0
  101. package/esm/src/project_config.d.ts.map +1 -0
  102. package/esm/src/project_config.js +134 -0
  103. package/esm/src/providers/codex.d.ts +37 -0
  104. package/esm/src/providers/codex.d.ts.map +1 -0
  105. package/esm/src/providers/codex.js +809 -0
  106. package/esm/src/providers/google.d.ts +3 -1
  107. package/esm/src/providers/google.d.ts.map +1 -1
  108. package/esm/src/providers/google.js +82 -6
  109. package/esm/src/providers/ollama.d.ts +3 -1
  110. package/esm/src/providers/ollama.d.ts.map +1 -1
  111. package/esm/src/providers/ollama.js +295 -15
  112. package/esm/src/providers/openrouter.d.ts +6 -2
  113. package/esm/src/providers/openrouter.d.ts.map +1 -1
  114. package/esm/src/providers/openrouter.js +334 -23
  115. package/esm/src/providers/router.d.ts +19 -0
  116. package/esm/src/providers/router.d.ts.map +1 -0
  117. package/esm/src/providers/router.js +93 -0
  118. package/esm/src/server.d.ts +9 -0
  119. package/esm/src/server.d.ts.map +1 -1
  120. package/esm/src/server.js +3361 -670
  121. package/esm/src/server_feedback_grading_routes.d.ts +32 -0
  122. package/esm/src/server_feedback_grading_routes.d.ts.map +1 -0
  123. package/esm/src/server_feedback_grading_routes.js +305 -0
  124. package/esm/src/server_helpers.d.ts +4 -0
  125. package/esm/src/server_helpers.d.ts.map +1 -0
  126. package/esm/src/server_helpers.js +46 -0
  127. package/esm/src/server_session_store.d.ts +87 -0
  128. package/esm/src/server_session_store.d.ts.map +1 -0
  129. package/esm/src/server_session_store.js +876 -0
  130. package/esm/src/server_types.d.ts +111 -0
  131. package/esm/src/server_types.d.ts.map +1 -0
  132. package/esm/src/server_types.js +1 -0
  133. package/esm/src/server_ui_routes.d.ts +33 -0
  134. package/esm/src/server_ui_routes.d.ts.map +1 -0
  135. package/esm/src/server_ui_routes.js +139 -0
  136. package/esm/src/session_artifacts.d.ts +22 -0
  137. package/esm/src/session_artifacts.d.ts.map +1 -0
  138. package/esm/src/session_artifacts.js +243 -0
  139. package/esm/src/trace.d.ts.map +1 -1
  140. package/esm/src/trace.js +6 -3
  141. package/esm/src/workspace.d.ts +19 -0
  142. package/esm/src/workspace.d.ts.map +1 -0
  143. package/esm/src/workspace.js +162 -0
  144. package/esm/src/workspace_contract.d.ts +78 -0
  145. package/esm/src/workspace_contract.d.ts.map +1 -0
  146. package/esm/src/workspace_contract.js +77 -0
  147. package/package.json +2 -2
  148. package/script/_dnt.polyfills.d.ts +17 -0
  149. package/script/_dnt.polyfills.d.ts.map +1 -1
  150. package/script/_dnt.polyfills.js +123 -0
  151. package/script/deps/jsr.io/@std/collections/1.1.6/deep_merge.d.ts +322 -0
  152. package/script/deps/jsr.io/@std/collections/1.1.6/deep_merge.d.ts.map +1 -0
  153. package/script/deps/jsr.io/@std/collections/1.1.6/deep_merge.js +108 -0
  154. package/script/deps/jsr.io/@std/fs/1.0.23/_create_walk_entry.d.ts +14 -0
  155. package/script/deps/jsr.io/@std/fs/1.0.23/_create_walk_entry.d.ts.map +1 -0
  156. package/script/deps/jsr.io/@std/fs/1.0.23/_create_walk_entry.js +71 -0
  157. package/script/deps/jsr.io/@std/fs/1.0.23/_get_file_info_type.d.ts +13 -0
  158. package/script/deps/jsr.io/@std/fs/1.0.23/_get_file_info_type.d.ts.map +1 -0
  159. package/script/deps/jsr.io/@std/fs/1.0.23/_get_file_info_type.js +21 -0
  160. package/script/deps/jsr.io/@std/fs/1.0.23/_is_same_path.d.ts +10 -0
  161. package/script/deps/jsr.io/@std/fs/1.0.23/_is_same_path.d.ts.map +1 -0
  162. package/script/deps/jsr.io/@std/fs/1.0.23/_is_same_path.js +20 -0
  163. package/script/deps/jsr.io/@std/fs/1.0.23/_is_subdir.d.ts +12 -0
  164. package/script/deps/jsr.io/@std/fs/1.0.23/_is_subdir.d.ts.map +1 -0
  165. package/script/deps/jsr.io/@std/fs/1.0.23/_is_subdir.js +28 -0
  166. package/script/deps/jsr.io/@std/fs/1.0.23/_to_path_string.d.ts +9 -0
  167. package/script/deps/jsr.io/@std/fs/1.0.23/_to_path_string.d.ts.map +1 -0
  168. package/script/deps/jsr.io/@std/fs/1.0.23/_to_path_string.js +16 -0
  169. package/script/deps/jsr.io/@std/fs/1.0.23/copy.d.ts +117 -0
  170. package/script/deps/jsr.io/@std/fs/1.0.23/copy.d.ts.map +1 -0
  171. package/script/deps/jsr.io/@std/fs/1.0.23/copy.js +350 -0
  172. package/script/deps/jsr.io/@std/fs/1.0.23/empty_dir.d.ts +48 -0
  173. package/script/deps/jsr.io/@std/fs/1.0.23/empty_dir.d.ts.map +1 -0
  174. package/script/deps/jsr.io/@std/fs/1.0.23/empty_dir.js +124 -0
  175. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_dir.d.ts +49 -0
  176. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_dir.d.ts.map +1 -0
  177. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_dir.js +139 -0
  178. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_file.d.ts +47 -0
  179. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_file.d.ts.map +1 -0
  180. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_file.js +127 -0
  181. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_link.d.ts +49 -0
  182. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_link.d.ts.map +1 -0
  183. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_link.js +98 -0
  184. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_symlink.d.ts +70 -0
  185. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_symlink.d.ts.map +1 -0
  186. package/script/deps/jsr.io/@std/fs/1.0.23/ensure_symlink.js +193 -0
  187. package/script/deps/jsr.io/@std/fs/1.0.23/eol.d.ts +52 -0
  188. package/script/deps/jsr.io/@std/fs/1.0.23/eol.d.ts.map +1 -0
  189. package/script/deps/jsr.io/@std/fs/1.0.23/eol.js +105 -0
  190. package/script/deps/jsr.io/@std/fs/1.0.23/exists.d.ts +218 -0
  191. package/script/deps/jsr.io/@std/fs/1.0.23/exists.d.ts.map +1 -0
  192. package/script/deps/jsr.io/@std/fs/1.0.23/exists.js +308 -0
  193. package/script/deps/jsr.io/@std/fs/1.0.23/expand_glob.d.ts +267 -0
  194. package/script/deps/jsr.io/@std/fs/1.0.23/expand_glob.d.ts.map +1 -0
  195. package/script/deps/jsr.io/@std/fs/1.0.23/expand_glob.js +479 -0
  196. package/script/deps/jsr.io/@std/fs/1.0.23/mod.d.ts +29 -0
  197. package/script/deps/jsr.io/@std/fs/1.0.23/mod.d.ts.map +1 -0
  198. package/script/deps/jsr.io/@std/fs/1.0.23/mod.js +45 -0
  199. package/script/deps/jsr.io/@std/fs/1.0.23/move.d.ts +86 -0
  200. package/script/deps/jsr.io/@std/fs/1.0.23/move.d.ts.map +1 -0
  201. package/script/deps/jsr.io/@std/fs/1.0.23/move.js +179 -0
  202. package/script/deps/jsr.io/@std/fs/1.0.23/walk.d.ts +777 -0
  203. package/script/deps/jsr.io/@std/fs/1.0.23/walk.d.ts.map +1 -0
  204. package/script/deps/jsr.io/@std/fs/1.0.23/walk.js +883 -0
  205. package/script/deps/jsr.io/@std/json/1.0.3/types.d.ts +7 -0
  206. package/script/deps/jsr.io/@std/json/1.0.3/types.d.ts.map +1 -0
  207. package/script/deps/jsr.io/@std/json/1.0.3/types.js +4 -0
  208. package/script/deps/jsr.io/@std/jsonc/1.0.2/mod.d.ts +20 -0
  209. package/script/deps/jsr.io/@std/jsonc/1.0.2/mod.d.ts.map +1 -0
  210. package/script/deps/jsr.io/@std/jsonc/1.0.2/mod.js +37 -0
  211. package/script/deps/jsr.io/@std/jsonc/1.0.2/parse.d.ts +21 -0
  212. package/script/deps/jsr.io/@std/jsonc/1.0.2/parse.d.ts.map +1 -0
  213. package/script/deps/jsr.io/@std/jsonc/1.0.2/parse.js +323 -0
  214. package/script/deps/jsr.io/@std/path/1.1.4/constants.d.ts +1 -1
  215. package/script/deps/jsr.io/@std/toml/1.0.11/_parser.d.ts +93 -0
  216. package/script/deps/jsr.io/@std/toml/1.0.11/_parser.d.ts.map +1 -0
  217. package/script/deps/jsr.io/@std/toml/1.0.11/_parser.js +781 -0
  218. package/script/deps/jsr.io/@std/toml/1.0.11/mod.d.ts +109 -0
  219. package/script/deps/jsr.io/@std/toml/1.0.11/mod.d.ts.map +1 -0
  220. package/script/deps/jsr.io/@std/toml/1.0.11/mod.js +126 -0
  221. package/script/deps/jsr.io/@std/toml/1.0.11/parse.d.ts +21 -0
  222. package/script/deps/jsr.io/@std/toml/1.0.11/parse.d.ts.map +1 -0
  223. package/script/deps/jsr.io/@std/toml/1.0.11/parse.js +28 -0
  224. package/script/deps/jsr.io/@std/toml/1.0.11/stringify.d.ts +35 -0
  225. package/script/deps/jsr.io/@std/toml/1.0.11/stringify.d.ts.map +1 -0
  226. package/script/deps/jsr.io/@std/toml/1.0.11/stringify.js +286 -0
  227. package/script/gambit/simulator-ui/dist/bundle.js +13788 -5157
  228. package/script/gambit/simulator-ui/dist/bundle.js.map +4 -4
  229. package/script/mod.d.ts +13 -3
  230. package/script/mod.d.ts.map +1 -1
  231. package/script/mod.js +14 -5
  232. package/script/src/cli_utils.d.ts +1 -0
  233. package/script/src/cli_utils.d.ts.map +1 -1
  234. package/script/src/cli_utils.js +14 -1
  235. package/script/src/default_runtime.d.ts +46 -0
  236. package/script/src/default_runtime.d.ts.map +1 -0
  237. package/script/src/default_runtime.js +452 -0
  238. package/script/src/durable_streams.js +26 -1
  239. package/script/src/model_matchers.d.ts +10 -0
  240. package/script/src/model_matchers.d.ts.map +1 -0
  241. package/script/src/model_matchers.js +29 -0
  242. package/script/src/openai_compat.d.ts +12 -1
  243. package/script/src/openai_compat.d.ts.map +1 -1
  244. package/script/src/openai_compat.js +85 -0
  245. package/script/src/project_config.d.ts +47 -0
  246. package/script/src/project_config.d.ts.map +1 -0
  247. package/script/src/project_config.js +173 -0
  248. package/script/src/providers/codex.d.ts +37 -0
  249. package/script/src/providers/codex.d.ts.map +1 -0
  250. package/script/src/providers/codex.js +849 -0
  251. package/script/src/providers/google.d.ts +3 -1
  252. package/script/src/providers/google.d.ts.map +1 -1
  253. package/script/src/providers/google.js +82 -6
  254. package/script/src/providers/ollama.d.ts +3 -1
  255. package/script/src/providers/ollama.d.ts.map +1 -1
  256. package/script/src/providers/ollama.js +295 -15
  257. package/script/src/providers/openrouter.d.ts +6 -2
  258. package/script/src/providers/openrouter.d.ts.map +1 -1
  259. package/script/src/providers/openrouter.js +334 -23
  260. package/script/src/providers/router.d.ts +19 -0
  261. package/script/src/providers/router.d.ts.map +1 -0
  262. package/script/src/providers/router.js +96 -0
  263. package/script/src/server.d.ts +9 -0
  264. package/script/src/server.d.ts.map +1 -1
  265. package/script/src/server.js +3368 -677
  266. package/script/src/server_feedback_grading_routes.d.ts +32 -0
  267. package/script/src/server_feedback_grading_routes.d.ts.map +1 -0
  268. package/script/src/server_feedback_grading_routes.js +343 -0
  269. package/script/src/server_helpers.d.ts +4 -0
  270. package/script/src/server_helpers.d.ts.map +1 -0
  271. package/script/src/server_helpers.js +84 -0
  272. package/script/src/server_session_store.d.ts +87 -0
  273. package/script/src/server_session_store.d.ts.map +1 -0
  274. package/script/src/server_session_store.js +913 -0
  275. package/script/src/server_types.d.ts +111 -0
  276. package/script/src/server_types.d.ts.map +1 -0
  277. package/script/src/server_types.js +2 -0
  278. package/script/src/server_ui_routes.d.ts +33 -0
  279. package/script/src/server_ui_routes.d.ts.map +1 -0
  280. package/script/src/server_ui_routes.js +176 -0
  281. package/script/src/session_artifacts.d.ts +22 -0
  282. package/script/src/session_artifacts.d.ts.map +1 -0
  283. package/script/src/session_artifacts.js +279 -0
  284. package/script/src/trace.d.ts.map +1 -1
  285. package/script/src/trace.js +6 -3
  286. package/script/src/workspace.d.ts +19 -0
  287. package/script/src/workspace.d.ts.map +1 -0
  288. package/script/src/workspace.js +199 -0
  289. package/script/src/workspace_contract.d.ts +78 -0
  290. package/script/src/workspace_contract.d.ts.map +1 -0
  291. package/script/src/workspace_contract.js +85 -0
@@ -0,0 +1,876 @@
1
+ import * as dntShim from "../_dnt.shims.js";
2
+ import * as path from "../deps/jsr.io/@std/path/1.1.4/mod.js";
3
+ import { existsSync } from "../deps/jsr.io/@std/fs/1.0.23/mod.js";
4
+ const TRACE_EVENT_TYPES = new Set([
5
+ "run.start",
6
+ "message.user",
7
+ "run.end",
8
+ "deck.start",
9
+ "deck.end",
10
+ "action.start",
11
+ "action.end",
12
+ "tool.call",
13
+ "tool.result",
14
+ "model.call",
15
+ "model.result",
16
+ "model.stream.event",
17
+ "log",
18
+ "monolog",
19
+ ]);
20
+ const isTraceEventType = (type) => TRACE_EVENT_TYPES.has(type) || type.startsWith("response.") ||
21
+ (type.startsWith("gambit.") &&
22
+ TRACE_EVENT_TYPES.has(type.slice("gambit.".length)));
23
+ const normalizePersistedTraceRecord = (record) => {
24
+ const type = typeof record.type === "string" ? record.type : "";
25
+ if (!type)
26
+ return null;
27
+ if (TRACE_EVENT_TYPES.has(type) || type.startsWith("response.")) {
28
+ return record;
29
+ }
30
+ if (!type.startsWith("gambit."))
31
+ return null;
32
+ const rawMeta = record._gambit;
33
+ const meta = rawMeta && typeof rawMeta === "object" && !Array.isArray(rawMeta)
34
+ ? rawMeta
35
+ : undefined;
36
+ const sourceType = typeof meta?.source_type === "string" &&
37
+ meta.source_type.trim().length > 0
38
+ ? meta.source_type.trim()
39
+ : type.slice("gambit.".length);
40
+ if (!TRACE_EVENT_TYPES.has(sourceType))
41
+ return null;
42
+ return {
43
+ ...record,
44
+ type: sourceType,
45
+ };
46
+ };
47
+ const inferWorkspaceDomain = (payloadType) => {
48
+ if (payloadType === "buildBotStatus" || payloadType === "buildBotTrace" ||
49
+ payloadType === "buildBotStream" || payloadType === "buildBotStreamEnd" ||
50
+ payloadType.startsWith("gambit.build.")) {
51
+ return "build";
52
+ }
53
+ if (payloadType === "testBotStatus" || payloadType === "testBotTrace" ||
54
+ payloadType === "testBotStream" || payloadType === "testBotStreamEnd" ||
55
+ payloadType.startsWith("gambit.test.")) {
56
+ return "test";
57
+ }
58
+ if (payloadType === "calibrateSession" || payloadType === "grading.run" ||
59
+ payloadType === "grading.flag" || payloadType === "grading.flag.reason" ||
60
+ payloadType === "grading.reference" ||
61
+ payloadType.startsWith("gambit.grade.") ||
62
+ payloadType.startsWith("gambit.grading.")) {
63
+ return "grade";
64
+ }
65
+ return "session";
66
+ };
67
+ const isWorkspaceEventDomain = (value) => value === "build" || value === "test" || value === "grade" ||
68
+ value === "session";
69
+ const CANONICAL_EVENT_TYPE_BY_LEGACY = new Map([
70
+ ["buildBotStatus", "gambit.build.status"],
71
+ ["buildBotTrace", "gambit.build.trace"],
72
+ ["buildBotStream", "gambit.build.stream.delta"],
73
+ ["buildBotStreamEnd", "gambit.build.stream.done"],
74
+ ["testBotStatus", "gambit.test.status"],
75
+ ["testBotStream", "gambit.test.stream.delta"],
76
+ ["testBotStreamEnd", "gambit.test.stream.done"],
77
+ ["calibrateSession", "gambit.grade.session"],
78
+ ["feedback.update", "gambit.feedback.update"],
79
+ ["grading.run", "gambit.grading.run"],
80
+ ["grading.flag", "gambit.grading.flag"],
81
+ ["grading.flag.reason", "gambit.grading.flag.reason"],
82
+ ["grading.reference", "gambit.grading.reference"],
83
+ ["notes.update", "gambit.notes.update"],
84
+ ["conversation.score.update", "gambit.conversation.score.update"],
85
+ ["session.start", "gambit.session.start"],
86
+ ["server.error", "gambit.server.error"],
87
+ ]);
88
+ const normalizeEventType = (type, kind) => {
89
+ if (type.startsWith("response.") || type.startsWith("gambit.")) {
90
+ return { canonicalType: type };
91
+ }
92
+ if (kind === "trace" || isTraceEventType(type)) {
93
+ return { canonicalType: type };
94
+ }
95
+ const mapped = CANONICAL_EVENT_TYPE_BY_LEGACY.get(type);
96
+ if (mapped) {
97
+ return { canonicalType: mapped, legacyType: type };
98
+ }
99
+ return { canonicalType: `gambit.${type}`, legacyType: type };
100
+ };
101
+ const safeStringify = (value, space) => {
102
+ const stack = [];
103
+ return JSON.stringify(value, function (_key, candidate) {
104
+ if (!candidate || typeof candidate !== "object") {
105
+ return candidate;
106
+ }
107
+ while (stack.length > 0 && stack[stack.length - 1] !== this) {
108
+ stack.pop();
109
+ }
110
+ if (stack.includes(candidate)) {
111
+ return "[Circular]";
112
+ }
113
+ stack.push(candidate);
114
+ return candidate;
115
+ }, space);
116
+ };
117
+ export const createSessionStore = (deps) => {
118
+ const { sessionsRoot, ensureDir, randomId, logger, enrichStateWithSession, workspaceStateSchemaVersion, workspaceSchemaError, } = deps;
119
+ const sessionStateCache = new Map();
120
+ const sessionWriteQueues = new Map();
121
+ const sessionWriteActive = new Set();
122
+ const sessionOffsetById = new Map();
123
+ const buildProjectionCache = new Map();
124
+ const enqueueSessionWrite = (sessionId, task) => {
125
+ const queue = sessionWriteQueues.get(sessionId) ?? [];
126
+ queue.push(task);
127
+ sessionWriteQueues.set(sessionId, queue);
128
+ if (sessionWriteActive.has(sessionId))
129
+ return;
130
+ sessionWriteActive.add(sessionId);
131
+ while (queue.length) {
132
+ const next = queue.shift();
133
+ if (!next)
134
+ continue;
135
+ try {
136
+ next();
137
+ }
138
+ catch (err) {
139
+ logger.warn(`[sim] session write failed: ${err instanceof Error ? err.message : err}`);
140
+ }
141
+ }
142
+ sessionWriteActive.delete(sessionId);
143
+ };
144
+ const mergeSessionState = (current, next) => {
145
+ if (!current)
146
+ return next;
147
+ const merged = {
148
+ ...current,
149
+ ...next,
150
+ meta: {
151
+ ...(current.meta ?? {}),
152
+ ...(next.meta ?? {}),
153
+ },
154
+ messages: next.messages ?? current.messages,
155
+ items: next.items ?? current.items,
156
+ format: next.format ?? current.format,
157
+ messageRefs: next.messageRefs ?? current.messageRefs,
158
+ feedback: next.feedback ?? current.feedback,
159
+ notes: next.notes ?? current.notes,
160
+ conversationScore: next.conversationScore ?? current.conversationScore,
161
+ traces: next.traces ?? current.traces,
162
+ };
163
+ return merged;
164
+ };
165
+ const parseFiniteInteger = (value) => {
166
+ if (typeof value !== "number" || !Number.isFinite(value))
167
+ return undefined;
168
+ if (!Number.isInteger(value))
169
+ return undefined;
170
+ return value;
171
+ };
172
+ const normalizeBuildProjectionRun = (workspaceId, value) => {
173
+ if (!value || typeof value !== "object") {
174
+ return {
175
+ id: workspaceId,
176
+ status: "idle",
177
+ messages: [],
178
+ traces: [],
179
+ toolInserts: [],
180
+ };
181
+ }
182
+ const run = value;
183
+ const status = run.status;
184
+ const normalizedStatus = status === "running" || status === "completed" ||
185
+ status === "error" || status === "canceled"
186
+ ? status
187
+ : "idle";
188
+ return {
189
+ id: typeof run.id === "string" && run.id.trim().length > 0
190
+ ? run.id
191
+ : workspaceId,
192
+ status: normalizedStatus,
193
+ error: typeof run.error === "string" ? run.error : undefined,
194
+ startedAt: typeof run.startedAt === "string" ? run.startedAt : undefined,
195
+ finishedAt: typeof run.finishedAt === "string"
196
+ ? run.finishedAt
197
+ : undefined,
198
+ messages: Array.isArray(run.messages)
199
+ ? run.messages
200
+ : [],
201
+ traces: Array.isArray(run.traces) ? run.traces : [],
202
+ toolInserts: Array.isArray(run.toolInserts)
203
+ ? run.toolInserts
204
+ : [],
205
+ };
206
+ };
207
+ const toCanonicalEventRecord = (args) => {
208
+ const payloadType = typeof args.data.type === "string"
209
+ ? args.data.type
210
+ : `gambit.${args.eventType}.event`;
211
+ const rawMeta = args.data._gambit;
212
+ const meta = rawMeta && typeof rawMeta === "object" && !Array.isArray(rawMeta)
213
+ ? rawMeta
214
+ : {};
215
+ return {
216
+ ...args.data,
217
+ type: payloadType,
218
+ offset: args.offset,
219
+ createdAt: args.createdAt,
220
+ _gambit: {
221
+ ...meta,
222
+ domain: args.eventType,
223
+ offset: args.offset,
224
+ },
225
+ };
226
+ };
227
+ const readEnvelopeRecords = (eventsPath) => {
228
+ try {
229
+ const text = dntShim.Deno.readTextFileSync(eventsPath);
230
+ const records = [];
231
+ let maxOffset = -1;
232
+ for (const line of text.split("\n")) {
233
+ if (!line.trim())
234
+ continue;
235
+ const parsed = JSON.parse(line);
236
+ const offset = parseFiniteInteger(parsed.offset) ??
237
+ parseFiniteInteger(parsed._gambit?.offset);
238
+ if (offset === undefined)
239
+ continue;
240
+ const envelopeType = typeof parsed.type === "string" ? parsed.type : "";
241
+ const nestedData = parsed.data;
242
+ const payload = isWorkspaceEventDomain(envelopeType) &&
243
+ nestedData && typeof nestedData === "object" &&
244
+ !Array.isArray(nestedData)
245
+ ? nestedData
246
+ : parsed;
247
+ const payloadType = typeof payload.type === "string"
248
+ ? payload.type
249
+ : "";
250
+ if (!payloadType)
251
+ continue;
252
+ const meta = parsed._gambit &&
253
+ typeof parsed._gambit === "object" &&
254
+ !Array.isArray(parsed._gambit)
255
+ ? parsed._gambit
256
+ : null;
257
+ const domain = (() => {
258
+ const explicit = meta?.domain;
259
+ if (explicit === "build" || explicit === "test" ||
260
+ explicit === "grade" || explicit === "session") {
261
+ return explicit;
262
+ }
263
+ if (isWorkspaceEventDomain(envelopeType)) {
264
+ return envelopeType;
265
+ }
266
+ return inferWorkspaceDomain(payloadType);
267
+ })();
268
+ if (!domain)
269
+ continue;
270
+ const createdAt = typeof parsed.createdAt === "string"
271
+ ? parsed.createdAt
272
+ : typeof parsed.created_at === "string"
273
+ ? parsed.created_at
274
+ : typeof payload.createdAt === "string"
275
+ ? payload.createdAt
276
+ : typeof payload.created_at === "string"
277
+ ? payload.created_at
278
+ : typeof meta?.created_at === "string"
279
+ ? meta.created_at
280
+ : new Date(0).toISOString();
281
+ const envelope = {
282
+ offset,
283
+ type: domain,
284
+ createdAt,
285
+ data: payload,
286
+ };
287
+ records.push(envelope);
288
+ if (offset > maxOffset) {
289
+ maxOffset = offset;
290
+ }
291
+ }
292
+ return { records, maxOffset };
293
+ }
294
+ catch {
295
+ return { records: [], maxOffset: -1 };
296
+ }
297
+ };
298
+ const ensureMonotonicOffsets = (records, eventsPath) => {
299
+ let expected = 0;
300
+ let highest = -1;
301
+ for (const record of records) {
302
+ if (record.offset !== expected) {
303
+ throw new Error(`Non-monotonic offset in ${eventsPath}: expected ${expected}, got ${record.offset}`);
304
+ }
305
+ highest = record.offset;
306
+ expected = record.offset + 1;
307
+ }
308
+ return highest;
309
+ };
310
+ const getCurrentSessionOffset = (sessionId, state) => {
311
+ const cached = sessionOffsetById.get(sessionId);
312
+ if (cached !== undefined)
313
+ return cached;
314
+ const fromMeta = parseFiniteInteger(state?.meta
315
+ ?.lastAppliedOffset) ??
316
+ parseFiniteInteger(state?.meta
317
+ ?.lastAppliedEventSeq);
318
+ if (fromMeta !== undefined) {
319
+ sessionOffsetById.set(sessionId, fromMeta);
320
+ return fromMeta;
321
+ }
322
+ const eventsPath = typeof state?.meta?.sessionEventsPath === "string"
323
+ ? state.meta.sessionEventsPath
324
+ : path.join(sessionsRoot, sessionId, "events.jsonl");
325
+ const { records } = readEnvelopeRecords(eventsPath);
326
+ if (records.length > 0) {
327
+ const validated = ensureMonotonicOffsets(records, eventsPath);
328
+ sessionOffsetById.set(sessionId, validated);
329
+ return validated;
330
+ }
331
+ sessionOffsetById.set(sessionId, -1);
332
+ return -1;
333
+ };
334
+ const nextSessionOffsetCandidate = (sessionId, state) => getCurrentSessionOffset(sessionId, state) + 1;
335
+ const upsertScenarioRunSummary = (meta) => {
336
+ const scenarioRunId = typeof meta.scenarioRunId === "string"
337
+ ? meta.scenarioRunId
338
+ : undefined;
339
+ if (!scenarioRunId)
340
+ return;
341
+ const lastEventSeq = parseFiniteInteger(meta.lastAppliedOffset) ??
342
+ parseFiniteInteger(meta.lastAppliedEventSeq) ??
343
+ 0;
344
+ const updatedAt = typeof meta.sessionUpdatedAt === "string"
345
+ ? meta.sessionUpdatedAt
346
+ : new Date().toISOString();
347
+ const selectedScenarioDeckId = typeof meta.selectedScenarioDeckId === "string"
348
+ ? meta.selectedScenarioDeckId
349
+ : typeof meta.testBotName === "string"
350
+ ? meta.testBotName
351
+ : "unknown";
352
+ const selectedScenarioDeckLabel = typeof meta.selectedScenarioDeckLabel === "string"
353
+ ? meta.selectedScenarioDeckLabel
354
+ : undefined;
355
+ const scenarioConfigPath = typeof meta.scenarioConfigPath === "string"
356
+ ? meta.scenarioConfigPath
357
+ : typeof meta.testBotConfigPath === "string"
358
+ ? meta.testBotConfigPath
359
+ : typeof meta.deck === "string"
360
+ ? meta.deck
361
+ : "unknown";
362
+ const previous = Array.isArray(meta.scenarioRunSummaries)
363
+ ? meta.scenarioRunSummaries
364
+ : [];
365
+ const nextSummary = {
366
+ scenarioRunId,
367
+ lastEventSeq,
368
+ updatedAt,
369
+ selectedScenarioDeckId,
370
+ selectedScenarioDeckLabel,
371
+ scenarioConfigPath,
372
+ };
373
+ const existingIdx = previous.findIndex((entry) => entry.scenarioRunId === scenarioRunId);
374
+ meta.scenarioRunSummaries = existingIdx >= 0
375
+ ? previous.map((entry, idx) => idx === existingIdx ? nextSummary : entry)
376
+ : [...previous, nextSummary];
377
+ meta.scenarioRunSummary = nextSummary;
378
+ };
379
+ const normalizeScenarioRunSummary = (value) => {
380
+ if (!value || typeof value !== "object")
381
+ return null;
382
+ const summary = value;
383
+ const scenarioRunId = typeof summary.scenarioRunId === "string"
384
+ ? summary.scenarioRunId
385
+ : null;
386
+ const lastEventSeq = parseFiniteInteger(summary.lastEventSeq);
387
+ const updatedAt = typeof summary.updatedAt === "string"
388
+ ? summary.updatedAt
389
+ : null;
390
+ const selectedScenarioDeckId = typeof summary.selectedScenarioDeckId === "string"
391
+ ? summary.selectedScenarioDeckId
392
+ : null;
393
+ const selectedScenarioDeckLabel = typeof summary.selectedScenarioDeckLabel === "string"
394
+ ? summary.selectedScenarioDeckLabel
395
+ : undefined;
396
+ const scenarioConfigPath = typeof summary.scenarioConfigPath === "string"
397
+ ? summary.scenarioConfigPath
398
+ : null;
399
+ if (!scenarioRunId || lastEventSeq === undefined || !updatedAt ||
400
+ !selectedScenarioDeckId || !scenarioConfigPath) {
401
+ return null;
402
+ }
403
+ return {
404
+ scenarioRunId,
405
+ lastEventSeq,
406
+ updatedAt,
407
+ selectedScenarioDeckId,
408
+ selectedScenarioDeckLabel,
409
+ scenarioConfigPath,
410
+ };
411
+ };
412
+ const selectCanonicalScenarioRunSummary = (meta) => {
413
+ const fromCurrent = normalizeScenarioRunSummary(meta.scenarioRunSummary);
414
+ const fromListRaw = Array.isArray(meta.scenarioRunSummaries)
415
+ ? meta.scenarioRunSummaries
416
+ : [];
417
+ const fromList = fromListRaw
418
+ .map((entry) => normalizeScenarioRunSummary(entry))
419
+ .filter((entry) => Boolean(entry));
420
+ const all = fromCurrent ? [fromCurrent, ...fromList] : fromList;
421
+ if (!all.length)
422
+ return null;
423
+ all.sort((a, b) => {
424
+ if (a.lastEventSeq !== b.lastEventSeq) {
425
+ return b.lastEventSeq - a.lastEventSeq;
426
+ }
427
+ if (a.updatedAt !== b.updatedAt) {
428
+ return b.updatedAt.localeCompare(a.updatedAt);
429
+ }
430
+ return a.scenarioRunId.localeCompare(b.scenarioRunId);
431
+ });
432
+ return all[0] ?? null;
433
+ };
434
+ const materializeSnapshot = (state) => {
435
+ const snapshot = { ...state };
436
+ const sessionId = typeof snapshot.meta?.sessionId === "string"
437
+ ? snapshot.meta.sessionId
438
+ : undefined;
439
+ if (sessionId) {
440
+ const meta = { ...(snapshot.meta ?? {}) };
441
+ const lastAppliedOffset = getCurrentSessionOffset(sessionId, snapshot);
442
+ meta.lastAppliedOffset = lastAppliedOffset;
443
+ meta.lastAppliedEventSeq = lastAppliedOffset;
444
+ upsertScenarioRunSummary(meta);
445
+ snapshot.meta = meta;
446
+ }
447
+ delete snapshot.traces;
448
+ return snapshot;
449
+ };
450
+ const writeJsonAtomic = (filePath, payload) => {
451
+ const dir = path.dirname(filePath);
452
+ ensureDir(dir);
453
+ const tmpPath = path.join(dir, `.tmp-${path.basename(filePath)}-${randomId("tmp")}`);
454
+ dntShim.Deno.writeTextFileSync(tmpPath, safeStringify(payload, 2));
455
+ dntShim.Deno.renameSync(tmpPath, filePath);
456
+ };
457
+ const appendJsonl = (filePath, payload) => {
458
+ const dir = path.dirname(filePath);
459
+ ensureDir(dir);
460
+ const line = safeStringify(payload);
461
+ dntShim.Deno.writeTextFileSync(filePath, `${line}\n`, { append: true });
462
+ };
463
+ const readBuildProjection = (workspaceId) => {
464
+ const cached = buildProjectionCache.get(workspaceId);
465
+ if (cached)
466
+ return cached;
467
+ const filePath = path.join(sessionsRoot, workspaceId, "build_state.json");
468
+ try {
469
+ const parsed = JSON.parse(dntShim.Deno.readTextFileSync(filePath));
470
+ const lastAppliedOffset = parseFiniteInteger(parsed.lastAppliedOffset) ??
471
+ -1;
472
+ const run = normalizeBuildProjectionRun(workspaceId, parsed.run);
473
+ const projection = {
474
+ workspaceId,
475
+ lastAppliedOffset,
476
+ run,
477
+ state: parsed.state && typeof parsed.state === "object"
478
+ ? parsed.state
479
+ : undefined,
480
+ };
481
+ buildProjectionCache.set(workspaceId, projection);
482
+ return projection;
483
+ }
484
+ catch {
485
+ const empty = {
486
+ workspaceId,
487
+ lastAppliedOffset: -1,
488
+ run: {
489
+ id: workspaceId,
490
+ status: "idle",
491
+ messages: [],
492
+ traces: [],
493
+ toolInserts: [],
494
+ },
495
+ state: undefined,
496
+ };
497
+ buildProjectionCache.set(workspaceId, empty);
498
+ return empty;
499
+ }
500
+ };
501
+ const replayBuildProjection = (workspaceId, envelopes) => {
502
+ const state = {
503
+ workspaceId,
504
+ lastAppliedOffset: -1,
505
+ run: {
506
+ id: workspaceId,
507
+ status: "idle",
508
+ messages: [],
509
+ traces: [],
510
+ toolInserts: [],
511
+ },
512
+ };
513
+ for (const envelope of envelopes) {
514
+ state.lastAppliedOffset = envelope.offset;
515
+ if (envelope.type !== "build")
516
+ continue;
517
+ const payloadType = typeof envelope.data.type === "string"
518
+ ? envelope.data.type
519
+ : "";
520
+ if (payloadType === "buildBotStatus" ||
521
+ payloadType === "gambit.build.status") {
522
+ const run = normalizeBuildProjectionRun(workspaceId, envelope.data.run);
523
+ state.run = run;
524
+ const buildStateSnapshot = envelope.data.state;
525
+ if (buildStateSnapshot && typeof buildStateSnapshot === "object") {
526
+ state.state = buildStateSnapshot;
527
+ }
528
+ continue;
529
+ }
530
+ if (payloadType === "buildBotTrace" || payloadType === "gambit.build.trace") {
531
+ const event = envelope.data.event;
532
+ if (event && typeof event === "object") {
533
+ const currentTraces = Array.isArray(state.run.traces)
534
+ ? state.run.traces
535
+ : [];
536
+ state.run = {
537
+ ...state.run,
538
+ traces: [...currentTraces, event],
539
+ };
540
+ }
541
+ }
542
+ }
543
+ return state;
544
+ };
545
+ const rebuildBuildProjectionFromEvents = (workspaceId, eventsPath) => {
546
+ const { records } = readEnvelopeRecords(eventsPath);
547
+ if (records.length > 0) {
548
+ ensureMonotonicOffsets(records, eventsPath);
549
+ }
550
+ const projection = replayBuildProjection(workspaceId, records);
551
+ const buildPath = path.join(sessionsRoot, workspaceId, "build_state.json");
552
+ writeJsonAtomic(buildPath, projection);
553
+ buildProjectionCache.set(workspaceId, projection);
554
+ };
555
+ const updateSnapshotBoundary = (sessionId, statePath, offset) => {
556
+ if (!statePath || !existsSync(statePath))
557
+ return;
558
+ try {
559
+ const text = dntShim.Deno.readTextFileSync(statePath);
560
+ const parsed = JSON.parse(text);
561
+ const parsedMeta = parsed.meta && typeof parsed.meta === "object"
562
+ ? parsed.meta
563
+ : {};
564
+ const previousBoundary = parseFiniteInteger(parsedMeta.lastAppliedOffset) ??
565
+ parseFiniteInteger(parsedMeta.lastAppliedEventSeq) ??
566
+ -1;
567
+ if (offset <= previousBoundary)
568
+ return;
569
+ parsedMeta.lastAppliedOffset = offset;
570
+ parsedMeta.lastAppliedEventSeq = offset;
571
+ parsedMeta.sessionUpdatedAt = new Date().toISOString();
572
+ upsertScenarioRunSummary(parsedMeta);
573
+ const nextState = { ...parsed, meta: parsedMeta };
574
+ writeJsonAtomic(statePath, nextState);
575
+ sessionStateCache.set(sessionId, nextState);
576
+ }
577
+ catch {
578
+ // Keep append-only logging best-effort even if snapshot boundary update fails.
579
+ }
580
+ };
581
+ const appendWorkspaceEnvelope = (state, eventType, data) => {
582
+ const sessionId = typeof state.meta?.sessionId === "string"
583
+ ? state.meta.sessionId
584
+ : undefined;
585
+ const eventsPath = typeof state.meta?.sessionEventsPath === "string"
586
+ ? state.meta.sessionEventsPath
587
+ : undefined;
588
+ const statePath = typeof state.meta?.sessionStatePath === "string"
589
+ ? state.meta.sessionStatePath
590
+ : undefined;
591
+ if (!sessionId || !eventsPath)
592
+ return null;
593
+ const normalizedData = (() => {
594
+ const rawType = typeof data.type === "string" ? data.type : "";
595
+ if (!rawType)
596
+ return data;
597
+ const { canonicalType, legacyType } = normalizeEventType(rawType, data.kind);
598
+ if (!legacyType) {
599
+ return { ...data, type: canonicalType };
600
+ }
601
+ const rawMeta = data._gambit;
602
+ const meta = rawMeta && typeof rawMeta === "object" && !Array.isArray(rawMeta)
603
+ ? rawMeta
604
+ : {};
605
+ return {
606
+ ...data,
607
+ type: canonicalType,
608
+ _gambit: {
609
+ ...meta,
610
+ legacy_type: legacyType,
611
+ domain: eventType,
612
+ },
613
+ };
614
+ })();
615
+ const createdAt = new Date().toISOString();
616
+ const optimisticOffset = nextSessionOffsetCandidate(sessionId, state);
617
+ const envelope = {
618
+ offset: optimisticOffset,
619
+ createdAt,
620
+ type: eventType,
621
+ data: normalizedData,
622
+ };
623
+ enqueueSessionWrite(sessionId, () => {
624
+ const offset = nextSessionOffsetCandidate(sessionId, state);
625
+ appendJsonl(eventsPath, toCanonicalEventRecord({
626
+ eventType,
627
+ offset,
628
+ createdAt,
629
+ data: normalizedData,
630
+ }));
631
+ sessionOffsetById.set(sessionId, offset);
632
+ if (state.meta && typeof state.meta === "object") {
633
+ state.meta.lastAppliedOffset = offset;
634
+ state.meta.lastAppliedEventSeq = offset;
635
+ }
636
+ updateSnapshotBoundary(sessionId, statePath, offset);
637
+ if (eventType === "build") {
638
+ rebuildBuildProjectionFromEvents(sessionId, eventsPath);
639
+ }
640
+ });
641
+ return envelope;
642
+ };
643
+ const appendSessionEvent = (state, payload) => appendWorkspaceEnvelope(state, "session", payload);
644
+ const appendFeedbackLog = (state, payload) => appendWorkspaceEnvelope(state, "session", {
645
+ ...payload,
646
+ logType: "feedback",
647
+ });
648
+ const appendGradingLog = (state, payload) => appendWorkspaceEnvelope(state, "grade", payload);
649
+ const appendErrorLog = (state, payload) => appendWorkspaceEnvelope(state, "session", {
650
+ ...payload,
651
+ logType: "error",
652
+ });
653
+ const appendServerErrorLog = (workspaceId, payload) => {
654
+ if (!workspaceId)
655
+ return;
656
+ const state = readSessionState(workspaceId);
657
+ if (!state)
658
+ return;
659
+ appendErrorLog(state, {
660
+ type: "server.error",
661
+ ...payload,
662
+ });
663
+ };
664
+ const loadSessionTraces = (state) => {
665
+ const eventsPath = typeof state.meta?.sessionEventsPath === "string"
666
+ ? state.meta.sessionEventsPath
667
+ : undefined;
668
+ if (!eventsPath)
669
+ return [];
670
+ try {
671
+ const { records } = readEnvelopeRecords(eventsPath);
672
+ if (records.length > 0) {
673
+ ensureMonotonicOffsets(records, eventsPath);
674
+ }
675
+ const traces = [];
676
+ for (const envelope of records) {
677
+ const record = envelope.data;
678
+ const kind = typeof record.kind === "string" ? record.kind : "";
679
+ const type = typeof record.type === "string" ? record.type : "";
680
+ if (kind === "trace" || isTraceEventType(type)) {
681
+ const normalized = normalizePersistedTraceRecord(record);
682
+ if (normalized)
683
+ traces.push(normalized);
684
+ }
685
+ }
686
+ return traces;
687
+ }
688
+ catch {
689
+ return [];
690
+ }
691
+ };
692
+ const persistSessionState = (state) => {
693
+ const { state: enriched, dir } = enrichStateWithSession(state);
694
+ const sessionId = typeof enriched.meta?.sessionId === "string"
695
+ ? enriched.meta.sessionId
696
+ : undefined;
697
+ const merged = sessionId
698
+ ? mergeSessionState(sessionStateCache.get(sessionId), enriched)
699
+ : enriched;
700
+ if (sessionId) {
701
+ sessionStateCache.set(sessionId, merged);
702
+ }
703
+ if (dir && sessionId) {
704
+ const snapshot = materializeSnapshot(merged);
705
+ const eventsPath = typeof snapshot.meta?.sessionEventsPath === "string"
706
+ ? snapshot.meta.sessionEventsPath
707
+ : path.join(dir, "events.jsonl");
708
+ const statePath = typeof snapshot.meta?.sessionStatePath === "string"
709
+ ? snapshot.meta.sessionStatePath
710
+ : path.join(dir, "state.json");
711
+ enqueueSessionWrite(sessionId, () => {
712
+ try {
713
+ ensureDir(dir);
714
+ const firstWrite = !existsSync(eventsPath);
715
+ if (firstWrite) {
716
+ const startOffset = nextSessionOffsetCandidate(sessionId, snapshot);
717
+ appendJsonl(eventsPath, toCanonicalEventRecord({
718
+ eventType: "session",
719
+ offset: startOffset,
720
+ createdAt: new Date().toISOString(),
721
+ data: {
722
+ type: "gambit.session.start",
723
+ _gambit: {
724
+ legacy_type: "session.start",
725
+ domain: "session",
726
+ },
727
+ category: "lifecycle",
728
+ sessionId,
729
+ runId: snapshot.runId,
730
+ deck: snapshot.meta?.deck,
731
+ },
732
+ }));
733
+ sessionOffsetById.set(sessionId, startOffset);
734
+ }
735
+ const snapshotOffset = nextSessionOffsetCandidate(sessionId, snapshot);
736
+ const snapshotToWrite = {
737
+ ...snapshot,
738
+ meta: {
739
+ ...(snapshot.meta ?? {}),
740
+ lastAppliedOffset: snapshotOffset,
741
+ lastAppliedEventSeq: snapshotOffset,
742
+ },
743
+ };
744
+ appendJsonl(eventsPath, toCanonicalEventRecord({
745
+ eventType: "session",
746
+ offset: snapshotOffset,
747
+ createdAt: new Date().toISOString(),
748
+ data: {
749
+ type: "session.snapshot",
750
+ category: "snapshot",
751
+ sessionId,
752
+ runId: snapshotToWrite.runId,
753
+ state: snapshotToWrite,
754
+ },
755
+ }));
756
+ // Advance in-memory offset immediately after append so a later
757
+ // snapshot write failure cannot cause duplicate offsets on retry.
758
+ sessionOffsetById.set(sessionId, snapshotOffset);
759
+ writeJsonAtomic(statePath, snapshotToWrite);
760
+ rebuildBuildProjectionFromEvents(sessionId, eventsPath);
761
+ }
762
+ catch (err) {
763
+ logger.warn(`[sim] failed to persist session state: ${err instanceof Error ? err.message : err}`);
764
+ }
765
+ });
766
+ }
767
+ return merged;
768
+ };
769
+ const readSessionStateStrict = (sessionId, opts) => {
770
+ const dir = path.join(sessionsRoot, sessionId);
771
+ const filePath = path.join(dir, "state.json");
772
+ const text = dntShim.Deno.readTextFileSync(filePath);
773
+ const parsed = JSON.parse(text);
774
+ if (!parsed || typeof parsed !== "object") {
775
+ throw new Error(`Invalid workspace state payload at ${filePath}`);
776
+ }
777
+ const parsedMeta = parsed.meta && typeof parsed.meta === "object"
778
+ ? parsed.meta
779
+ : {};
780
+ const schemaVersion = typeof parsedMeta.workspaceSchemaVersion === "string"
781
+ ? parsedMeta.workspaceSchemaVersion.trim()
782
+ : null;
783
+ // Backward compatibility: legacy workspace state files may not include
784
+ // workspaceSchemaVersion yet. Treat missing schema as v1-equivalent.
785
+ if (schemaVersion && schemaVersion !== workspaceStateSchemaVersion) {
786
+ throw new Error(workspaceSchemaError(sessionId, schemaVersion));
787
+ }
788
+ const meta = {
789
+ ...parsedMeta,
790
+ sessionId,
791
+ workspaceId: typeof parsedMeta.workspaceId === "string" &&
792
+ parsedMeta.workspaceId.trim().length > 0
793
+ ? parsedMeta.workspaceId
794
+ : sessionId,
795
+ sessionDir: dir,
796
+ workspaceSchemaVersion: workspaceStateSchemaVersion,
797
+ };
798
+ if (typeof meta.sessionStatePath !== "string") {
799
+ meta.sessionStatePath = filePath;
800
+ }
801
+ if (typeof meta.sessionEventsPath !== "string") {
802
+ meta.sessionEventsPath = path.join(dir, "events.jsonl");
803
+ }
804
+ if (typeof meta.sessionBuildStatePath !== "string") {
805
+ meta.sessionBuildStatePath = path.join(dir, "build_state.json");
806
+ }
807
+ const eventsPath = typeof meta.sessionEventsPath === "string"
808
+ ? meta.sessionEventsPath
809
+ : undefined;
810
+ if (eventsPath) {
811
+ const { records, maxOffset } = readEnvelopeRecords(eventsPath);
812
+ if (records.length > 0) {
813
+ const validated = ensureMonotonicOffsets(records, eventsPath);
814
+ const lastAppliedOffset = validated >= 0 ? validated : maxOffset;
815
+ meta.lastAppliedOffset = lastAppliedOffset;
816
+ meta.lastAppliedEventSeq = lastAppliedOffset;
817
+ sessionOffsetById.set(sessionId, lastAppliedOffset);
818
+ }
819
+ else if (typeof meta.lastAppliedOffset !== "number") {
820
+ meta.lastAppliedOffset = -1;
821
+ meta.lastAppliedEventSeq = -1;
822
+ }
823
+ }
824
+ const enriched = { ...parsed, meta };
825
+ if (opts?.withTraces) {
826
+ const loadedTraces = loadSessionTraces(enriched);
827
+ const fallbackTraces = Array.isArray(enriched.traces)
828
+ ? enriched.traces
829
+ : [];
830
+ const traces = loadedTraces.length > 0 ? loadedTraces : fallbackTraces;
831
+ const withTraces = { ...enriched, traces };
832
+ sessionStateCache.set(sessionId, withTraces);
833
+ return withTraces;
834
+ }
835
+ sessionStateCache.set(sessionId, enriched);
836
+ return enriched;
837
+ };
838
+ const readSessionState = (sessionId, opts) => {
839
+ try {
840
+ return readSessionStateStrict(sessionId, opts);
841
+ }
842
+ catch (err) {
843
+ if (err instanceof dntShim.Deno.errors.NotFound)
844
+ return undefined;
845
+ logger.warn(`[sim] failed to read workspace state for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
846
+ return undefined;
847
+ }
848
+ };
849
+ const readBuildState = (workspaceId) => {
850
+ const state = readSessionState(workspaceId);
851
+ if (!state)
852
+ return undefined;
853
+ const eventsPath = typeof state.meta?.sessionEventsPath === "string"
854
+ ? state.meta.sessionEventsPath
855
+ : path.join(sessionsRoot, workspaceId, "events.jsonl");
856
+ if (existsSync(eventsPath)) {
857
+ rebuildBuildProjectionFromEvents(workspaceId, eventsPath);
858
+ }
859
+ return readBuildProjection(workspaceId);
860
+ };
861
+ return {
862
+ parseFiniteInteger,
863
+ selectCanonicalScenarioRunSummary,
864
+ appendWorkspaceEnvelope,
865
+ appendSessionEvent,
866
+ appendFeedbackLog,
867
+ appendGradingLog,
868
+ appendErrorLog,
869
+ appendServerErrorLog,
870
+ persistSessionState,
871
+ readSessionStateStrict,
872
+ readSessionState,
873
+ readBuildState,
874
+ replayBuildProjection,
875
+ };
876
+ };