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