#include "glove/container/digest.hpp" #include "glove/container/receipt_producer.hpp" #include "glove/control/receipt_audit_protocol.hpp" #include "glove/control/session_registry.hpp" #include "glove/supervisor/library_bundle.hpp" #include "glove/supervisor/path_alias.hpp" #include "glove/supervisor/session_plan.hpp" #include "linux_session_executor.hpp" #include "linux_session_preparation.hpp" #include "receipt_audit_wire.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace glove_test { #define REQUIRE(cond) \ do { \ if (!(cond)) { \ std::fprintf(stderr, "REQUIRE failed: %s @ %s:%d\n", #cond, __FILE__, __LINE__); \ return 1; \ } \ } while (0) constexpr std::string_view controller_digest = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"; constexpr std::string_view audit_key = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; constexpr std::string_view bootstrap_secret = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"; struct rpc_error { std::string code; std::string message; }; struct rpc_response { std::string jsonrpc; std::string id; std::optional result; std::optional error; }; struct session_control_capabilities { bool validate_plan = false; bool create_session = false; bool start_session = false; bool session_status = false; bool attach = false; bool resize = false; bool write_stdin = false; bool signal = false; bool detach = false; bool stop_session = false; bool cleanup_session = false; }; struct resource_enforcement_capabilities { std::string cpu_time; std::string memory; std::string pids; std::string wall_time; std::string disk; std::string terminal_output; std::uint8_t receipt_schema_version = 0; }; struct backend_capabilities { std::string backend; resource_enforcement_capabilities resource_enforcement; }; struct supervisor_capabilities { std::uint8_t schema_version = 0; glz::raw_json receipt_audit; session_control_capabilities session_control; std::uint8_t agent_runtime_adapter_schema_version = 0; std::vector managed_runtime_ids; std::uint8_t path_exposure_admin_schema_version = 0; std::uint8_t path_exposure_catalog_schema_version = 0; std::uint8_t retained_write_schema_version = 0; std::uint8_t change_manifest_schema_version = 0; std::uint8_t change_apply_authorization_schema_version = 0; std::uint8_t refinement_evaluation_protocol_schema_version = 0; std::uint8_t observation_intent_channel_schema_version = 0; std::vector backends; }; struct session_record_result { std::uint8_t schema_version = 0; std::string session_id; std::string controller_plan_digest; std::string plan_content_digest; std::string state; std::uint64_t policy_revision = 0; std::uint64_t expires_at_ms = 0; std::uint64_t created_at_ms = 0; std::optional profile_digest; }; struct transcript_result { std::uint8_t schema_version = 0; std::string session_id; std::uint64_t oldest_cursor = 0; std::uint64_t next_cursor = 0; bool truncated = false; bool eof = false; std::vector bytes; }; struct session_mutation_result { std::uint8_t schema_version = 0; std::string session_id; }; struct session_cursor_result { std::uint8_t schema_version = 0; std::string session_id; std::uint64_t transcript_cursor = 0; }; auto decode_response(std::string_view frame) -> std::optional { rpc_response response; constexpr glz::opts strict{.error_on_unknown_keys = true}; if (glz::read(response, frame)) { return std::nullopt; } return response; } auto make_request( std::string_view id, std::string_view method, std::string_view payload, std::optional idempotency_key, std::uint64_t deadline_ms ) -> std::string { std::string request = "{\"jsonrpc\":\"2.0\",\"id\":\"" + std::string{id} + "\",\"method\":\"" + std::string{method} + "\",\"params\":{\"schema_version\":1,\"bootstrap_secret\":\"" + std::string{bootstrap_secret} + "\",\"deadline_ms\":" + std::to_string(deadline_ms); if (idempotency_key) { request += ",\"idempotency_key\":\"" + std::string{*idempotency_key} + "\""; } request += ",\"payload\":" + std::string{payload} + "}}"; return request; } auto interactive_runtime_digest() -> std::string { auto digest = glove::supervisor::runtime_launch_template_digest({ .runtime_discovery = {}, .executable_path = "/usr/bin/cat", .executable_search_paths = {}, .arguments = {}, .environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"}, .read_only_paths = {}, }); return digest.value_or(std::string{}); } auto safe_runtime_digest() -> std::string { auto digest = glove::supervisor::runtime_launch_template_digest({ .runtime_discovery = {}, .executable_path = "/usr/bin/true", .executable_search_paths = {}, .arguments = {"--version"}, .environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"}, .read_only_paths = {}, }); return digest.value_or(std::string{}); } auto refinement_runtime_digest() -> std::string { auto digest = glove::supervisor::runtime_launch_template_digest({ .runtime_discovery = {}, .executable_path = "/usr/bin/sh", .executable_search_paths = {}, .arguments = {"-c", "printf 'alpha beta done'"}, .environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"}, .read_only_paths = {}, }); return digest.value_or(std::string{}); } class temporary_tree { public: temporary_tree() { std::string pattern = "/tmp/glove-linux-executor-test-XXXXXX"; if (char* created = ::mkdtemp(pattern.data()); created != nullptr) { root_ = created; } } temporary_tree(const temporary_tree&) = delete; auto operator=(const temporary_tree&) -> temporary_tree& = delete; ~temporary_tree() { std::error_code error; std::filesystem::remove_all(root_, error); } [[nodiscard]] auto root() const -> const std::filesystem::path& { return root_; } private: std::filesystem::path root_; }; auto epoch_ms() -> std::uint64_t { const auto now = std::chrono::system_clock::now().time_since_epoch(); return static_cast( std::chrono::duration_cast(now).count() ); } auto validator_for(const std::filesystem::path& source, std::uint64_t page) -> glove::supervisor::result { using namespace glove::supervisor; auto paths = path_alias_registry::build({ path_alias_policy{ .alias = "workspace", .host_path = std::filesystem::canonical(source).string(), .target_path = "/workspace", .max_ttl_secs = 120, .access = { path_access_policy{ .access = path_access::ephemeral_write, .materialization = path_materialization::copy, .create_policy = path_create_policy::empty_directory, .cleanup_policy = path_cleanup_policy::remove, .max_bytes = page * 4U, }, }, }, }); if (!paths) { return std::unexpected(paths.error()); } const auto safe_digest = safe_runtime_digest(); const auto interactive_digest = interactive_runtime_digest(); const auto refinement_digest = refinement_runtime_digest(); if (safe_digest.empty() || interactive_digest.empty() || refinement_digest.empty()) { return std::unexpected(std::string{"derive runtime digest"}); } return session_plan_validator::build( session_plan_policy{ .revision = 7, .max_plan_ttl_ms = 120'000, .runtime_templates = { runtime_template_policy{ .runtime_template_id = "codex-safe", .runtime_id = "codex", .adapter_command_digest = safe_digest, .backend = sandbox_backend::linux_production, .allowed_path_aliases = {"workspace"}, .allowed_projection_destinations = {"libraries"}, .launch = runtime_launch_template{ .runtime_discovery = {}, .executable_path = "/usr/bin/true", .executable_search_paths = {}, .arguments = {"--version"}, .environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"}, .read_only_paths = {}, }, .adoption = std::nullopt, }, runtime_template_policy{ .runtime_template_id = "codex-interactive", .runtime_id = "codex", .adapter_command_digest = interactive_digest, .backend = sandbox_backend::linux_production, .allowed_path_aliases = {"workspace"}, .allowed_projection_destinations = {"libraries"}, .launch = runtime_launch_template{ .runtime_discovery = {}, .executable_path = "/usr/bin/cat", .executable_search_paths = {}, .arguments = {}, .environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"}, .read_only_paths = {}, }, .adoption = std::nullopt, }, runtime_template_policy{ .runtime_template_id = "refinement-eval-v1", .runtime_id = "refinement-harness", .adapter_command_digest = refinement_digest, .backend = sandbox_backend::linux_production, .allowed_path_aliases = {"workspace"}, .allowed_projection_destinations = {"fixtures", "skills"}, .launch = runtime_launch_template{ .runtime_discovery = {}, .executable_path = "/usr/bin/sh", .executable_search_paths = {}, .arguments = {"-c", "printf 'alpha beta done'"}, .environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"}, .read_only_paths = {}, }, .adoption = std::nullopt, }, }, .library_projection_destinations = { library_projection_destination_policy{ .alias = "libraries", .target_path = "/opt/sage/library-bundles", }, library_projection_destination_policy{ .alias = "fixtures", .target_path = "/opt/glove/fixtures", }, library_projection_destination_policy{ .alias = "skills", .target_path = "/opt/glove/refinement-skills", }, }, .resource_profiles = { resource_limits{ .cpu_time_ms = 10'000, .memory_bytes = std::uint64_t{128} * 1024U * 1024U, .pids = 16, .wall_time_ms = 5'000, .disk_bytes = page * 16U, .terminal_output_bytes = std::uint64_t{1024} * 1024U, }, }, .egress_policy_ids = {"no-network"}, .tool_policy_ids = {"sage-readonly"}, .secret_handles = {}, .egress_policies = {}, .secret_mounts = {}, }, std::move(*paths) ); } auto plan( std::uint64_t now_ms, std::uint64_t page, std::string_view runtime_template_id = "codex-safe" ) -> std::string { const auto adapter_digest = runtime_template_id == "codex-interactive" ? interactive_runtime_digest() : safe_runtime_digest(); return std::string{"{\"schema_version\":1,\"runtime_id\":\"codex\","} + "\"runtime_template_id\":\"" + std::string{runtime_template_id} + "\",\"adapter_command_digest\":\"" + adapter_digest + "\",\"sandbox_backend\":\"linux_production\",\"egress_policy_id\":\"no-network\"," + "\"tool_policy_id\":\"sage-readonly\",\"path_grants\":[{\"alias\":\"workspace\"," + "\"access\":\"ephemeral_write\",\"materialization\":\"copy\",\"max_bytes\":" + std::to_string(page * 4U) + ",\"ttl_secs\":60,\"cleanup_policy\":\"remove\"}],\"library_projections\":[]," + "\"secret_handles\":[],\"limits\":{\"cpu_time_ms\":10000,\"memory_bytes\":134217728," + "\"pids\":16,\"wall_time_ms\":5000,\"disk_bytes\":" + std::to_string(page * 16U) + ",\"terminal_output_bytes\":1048576},\"policy_revision\":7,\"expires_at_ms\":" + std::to_string(now_ms + 120'000) + "}"; } auto refinement_plan( std::uint64_t now_ms, std::uint64_t page, std::string_view fixture_digest, std::string_view candidate_digest, std::string_view matched_context_digest ) -> std::string { return std::string{ R"({"schema_version":1,"runtime_id":"refinement-harness","runtime_template_id":"refinement-eval-v1","adapter_command_digest":")" } + refinement_runtime_digest() + R"(","sandbox_backend":"linux_production","egress_policy_id":"no-network","tool_policy_id":"sage-readonly","path_grants":[],"library_projections":[{"projection_id":"candidate","content_digest":")" + std::string{candidate_digest} + R"(","destination_alias":"skills"},{"projection_id":"fixture","content_digest":")" + std::string{fixture_digest} + R"(","destination_alias":"fixtures"}],"secret_handles":[],"limits":{"cpu_time_ms":10000,"memory_bytes":134217728,"pids":16,"wall_time_ms":5000,"disk_bytes":)" + std::to_string(page * 16U) + R"(,"terminal_output_bytes":1048576},"policy_revision":7,"expires_at_ms":)" + std::to_string(now_ms + 120'000) + R"(,"refinement":{"schema_version":1,"variant":"candidate","fixture":{"projection_id":"fixture","content_digest":")" + std::string{fixture_digest} + R"(","destination_alias":"fixtures"},"base":{"projection_id":"base","content_digest":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","destination_alias":"skills"},"candidate":{"projection_id":"candidate","content_digest":")" + std::string{candidate_digest} + R"(","destination_alias":"skills"},"matched_context_digest":")" + std::string{matched_context_digest} + R"("}})"; } auto run() -> int { temporary_tree tree; REQUIRE(!tree.root().empty()); const auto source = tree.root() / "source"; const auto materializations = tree.root() / "materializations"; REQUIRE(std::filesystem::create_directory(source)); REQUIRE(std::filesystem::create_directory(materializations)); REQUIRE(::chmod(materializations.c_str(), 0700) == 0); std::ofstream{source / "tracked.txt"} << "host-owned\n"; const long page_size = ::sysconf(_SC_PAGESIZE); REQUIRE(page_size > 0); const auto page = static_cast(page_size); const auto now_ms = epoch_ms(); auto validator = validator_for(source, page); if (!validator) { std::fprintf(stderr, "validator unavailable: %s\n", validator.error().c_str()); } REQUIRE(validator.has_value()); auto shared_validator = std::make_shared(std::move(*validator)); auto registry = glove::control::session_registry::open_or_create( tree.root() / "sessions.journal", shared_validator ); REQUIRE(registry.has_value()); auto created = (*registry)->create( "session-executor", controller_digest, plan(now_ms, page), "create-session-executor", now_ms ); REQUIRE(created.has_value()); const glove::control::session_start_authorization authorization{ .schema_version = 1, .authorization_id = "approval-session-executor", .session_id = created->session_id, .controller_plan_digest = created->controller_plan_digest, .plan_content_digest = created->plan_content_digest, .approved_at_ms = now_ms, .expires_at_ms = now_ms + 30'000U, }; REQUIRE((*registry) ->reserve_start(authorization, "reserve-session-executor", now_ms + 1U) .has_value()); const auto key_path = tree.root() / "receipt.key"; { std::ofstream output{key_path, std::ios::binary | std::ios::trunc}; output << audit_key << '\n'; output.flush(); REQUIRE(output.good()); } REQUIRE(::chmod(key_path.c_str(), 0600) == 0); auto producer = glove::container::receipt_audit_producer::initialize({ .key_path = key_path, .journal_path = tree.root() / "receipts.journal", }); REQUIRE(producer.has_value()); REQUIRE((*producer)->acknowledge_bootstrap((*producer)->anchor()).has_value()); auto preparer_result = glove::control::linux_detail::linux_session_preparer::create(materializations.string()); if (!preparer_result) { std::fprintf( stderr, "Linux executor topology unavailable: %s\n", preparer_result.error().c_str() ); return 77; } auto preparer = std::make_shared( std::move(*preparer_result) ); auto exited = glove::control::linux_detail::execute_linux_session( **registry, *preparer, **producer, { .session_id = created->session_id, .authorization_id = authorization.authorization_id, .idempotency_namespace = "execute-session-executor", } ); if (!exited) { std::fprintf(stderr, "execute Linux session: %s\n", exited.error().c_str()); } REQUIRE(exited.has_value()); REQUIRE(exited->session.state == glove::control::session_state::exited); REQUIRE(exited->session.session_id == created->session_id); REQUIRE(exited->receipt_sequence == 1); REQUIRE(exited->termination_cause == glove::container::resource_termination_cause::exited); REQUIRE(exited->exit_code == 0); REQUIRE(exited->filesystem_identity.schema_version == 1); REQUIRE(exited->filesystem_identity.disk_limit_bytes == page * 16U); REQUIRE(exited->filesystem_identity.partitions.size() == 1); REQUIRE(exited->filesystem_identity.partitions.front().alias == "workspace"); REQUIRE(exited->filesystem_identity.partitions.front().quota_bytes == page * 4U); auto durable_exited = (*registry)->exited_status(created->session_id); REQUIRE(durable_exited.has_value()); REQUIRE(*durable_exited == *exited); REQUIRE((*producer)->anchor().sequence == 1); REQUIRE(std::filesystem::is_empty(materializations)); REQUIRE(std::filesystem::exists(source / "tracked.txt")); const auto crash_pid = ::fork(); REQUIRE(crash_pid >= 0); if (crash_pid == 0) { const auto crash_now_ms = epoch_ms(); auto crash_created = (*registry)->create( "session-starting-crash", controller_digest, plan(crash_now_ms, page), "create-session-starting-crash", crash_now_ms ); if (!crash_created) { ::_exit(11); } const glove::control::session_start_authorization crash_authorization{ .schema_version = 1, .authorization_id = "approval-session-starting-crash", .session_id = crash_created->session_id, .controller_plan_digest = crash_created->controller_plan_digest, .plan_content_digest = crash_created->plan_content_digest, .approved_at_ms = crash_now_ms, .expires_at_ms = crash_now_ms + 30'000U, }; if (!(*registry) ->reserve_start( crash_authorization, "reserve-session-starting-crash", crash_now_ms + 1U ) .has_value()) { ::_exit(12); } auto crash_inputs = (*registry)->resolve_start_inputs( crash_created->session_id, crash_authorization.authorization_id, crash_now_ms + 2U ); if (!crash_inputs) { ::_exit(13); } auto crash_prepared = preparer->prepare(std::move(*crash_inputs), crash_now_ms + 2U); if (!crash_prepared) { ::_exit(14); } auto crash_receipt_reservation = (*producer)->reserve_terminal( crash_prepared->session_id, crash_prepared->controller_plan_digest, crash_prepared->binding.profile_digest ); if (!crash_receipt_reservation) { ::_exit(15); } if (!(*registry) ->mark_starting( crash_prepared->execution_binding(), *crash_receipt_reservation, "starting-session-starting-crash", crash_now_ms + 3U ) .has_value()) { ::_exit(16); } ::_exit(0); } int crash_status = 0; REQUIRE(::waitpid(crash_pid, &crash_status, 0) == crash_pid); REQUIRE(WIFEXITED(crash_status)); REQUIRE(WEXITSTATUS(crash_status) == 0); REQUIRE(!std::filesystem::is_empty(materializations)); registry->reset(); auto recovered_registry_result = glove::control::session_registry::open_or_create( tree.root() / "sessions.journal", shared_validator ); REQUIRE(recovered_registry_result.has_value()); auto shared_registry = std::shared_ptr{std::move(*recovered_registry_result)}; REQUIRE(!glove::control::linux_detail::linux_session_runtime::create({}, preparer, {}, 1) .has_value()); REQUIRE(!glove::control::linux_detail::linux_session_runtime::create(shared_registry, {}, {}, 1) .has_value()); REQUIRE(!glove::control::linux_detail::linux_session_runtime::create( shared_registry, preparer, {}, 0 ) .has_value()); auto session_runtime_result = glove::control::linux_detail::linux_session_runtime::create( shared_registry, preparer, { .transcript_bytes = page, .max_read_bytes = page, .max_input_frame_bytes = page, .input_timeout_ms = 1'000, .initial_rows = 24, .initial_columns = 80, .refinement_evaluator = nullptr, }, 1 ); REQUIRE(session_runtime_result.has_value()); REQUIRE(!(*session_runtime_result) ->start(**producer, authorization, "unreconciled-start", epoch_ms()) .has_value()); auto reconciliation = (*session_runtime_result)->reconcile(**producer, epoch_ms() + 1U); REQUIRE(reconciliation.has_value()); REQUIRE(reconciliation->inspected == 1); REQUIRE(reconciliation->recovered_failed == 1); REQUIRE(reconciliation->recovered_exited == 0); REQUIRE(reconciliation->recovered_terminated == 0); REQUIRE((*session_runtime_result)->reconcile(**producer, epoch_ms() + 2U) == reconciliation); auto recovered_failure = shared_registry->failed_status("session-starting-crash"); REQUIRE(recovered_failure.has_value()); REQUIRE( recovered_failure->code == glove::control::session_failure_code::recovered_without_process ); REQUIRE(!recovered_failure->process_identity); REQUIRE(recovered_failure->cgroup_identity.has_value()); REQUIRE(recovered_failure->filesystem_identity.has_value()); REQUIRE(std::filesystem::is_empty(materializations)); REQUIRE(std::filesystem::exists(source / "tracked.txt")); auto shared_producer = std::shared_ptr{std::move(*producer)}; const auto interactive_now_ms = epoch_ms(); auto interactive_created = shared_registry->create( "session-interactive", controller_digest, plan(interactive_now_ms, page, "codex-interactive"), "create-session-interactive", interactive_now_ms ); REQUIRE(interactive_created.has_value()); const glove::control::session_start_authorization interactive_authorization{ .schema_version = 1, .authorization_id = "approval-session-interactive", .session_id = interactive_created->session_id, .controller_plan_digest = interactive_created->controller_plan_digest, .plan_content_digest = interactive_created->plan_content_digest, .approved_at_ms = interactive_now_ms, .expires_at_ms = interactive_now_ms + 30'000U, }; auto wire_runtime_result = glove::control::linux_detail::linux_session_runtime::create( shared_registry, preparer, { .transcript_bytes = page, .max_read_bytes = page, .max_input_frame_bytes = page, .input_timeout_ms = 1'000, .initial_rows = 24, .initial_columns = 80, .refinement_evaluator = nullptr, }, 1 ); REQUIRE(wire_runtime_result.has_value()); auto session_runtime = std::shared_ptr{ std::move(*wire_runtime_result) }; auto protocol = glove::control::receipt_audit_protocol::create( bootstrap_secret, shared_producer, shared_validator, shared_registry, session_runtime, {}, materializations.string() ); REQUIRE(protocol.has_value()); auto capabilities_frame = (*protocol)->handle_frame( make_request( "runtime-capabilities", "capabilities", "null", std::nullopt, interactive_now_ms + 10'000U ), interactive_now_ms ); REQUIRE(capabilities_frame.has_value()); auto capabilities_response = decode_response(*capabilities_frame); REQUIRE(capabilities_response.has_value()); REQUIRE(capabilities_response->result.has_value()); supervisor_capabilities capabilities; REQUIRE(!glz::read( capabilities, capabilities_response->result->str )); REQUIRE(capabilities.schema_version == 1); REQUIRE(capabilities.session_control.start_session); REQUIRE(capabilities.session_control.stop_session); REQUIRE(capabilities.session_control.attach); REQUIRE(capabilities.session_control.resize); REQUIRE(capabilities.session_control.write_stdin); REQUIRE(capabilities.session_control.signal); REQUIRE(capabilities.session_control.detach); REQUIRE(capabilities.session_control.cleanup_session); REQUIRE(capabilities.agent_runtime_adapter_schema_version == 1); REQUIRE( capabilities.managed_runtime_ids == std::vector({"codex", "claude-code", "pi", "copilot", "opencode"}) ); REQUIRE(capabilities.path_exposure_admin_schema_version == 0); REQUIRE(capabilities.path_exposure_catalog_schema_version == 0); REQUIRE(capabilities.retained_write_schema_version == 0); REQUIRE(capabilities.change_manifest_schema_version == 0); REQUIRE(capabilities.change_apply_authorization_schema_version == 0); REQUIRE(capabilities.refinement_evaluation_protocol_schema_version == 0); REQUIRE(capabilities.backends.size() == 2); REQUIRE(capabilities.backends[0].backend == "linux_production"); REQUIRE(capabilities.backends[0].resource_enforcement.cpu_time == "cgroup_v2"); REQUIRE(capabilities.backends[0].resource_enforcement.memory == "cgroup_v2"); REQUIRE(capabilities.backends[0].resource_enforcement.pids == "cgroup_v2"); REQUIRE(capabilities.backends[0].resource_enforcement.wall_time == "watchdog"); REQUIRE(capabilities.backends[0].resource_enforcement.disk == "filesystem_quota"); REQUIRE(capabilities.backends[0].resource_enforcement.terminal_output == "byte_counter"); REQUIRE(capabilities.backends[0].resource_enforcement.receipt_schema_version == 1); REQUIRE(capabilities.backends[1].backend == "apple_container"); REQUIRE(capabilities.backends[1].resource_enforcement.cpu_time == "unavailable"); auto authorization_json = glz::write_json(interactive_authorization); REQUIRE(authorization_json.has_value()); const auto start_payload = "{\"authorization\":" + *authorization_json + "}"; auto missing_start_idempotency = (*protocol)->handle_frame( make_request( "start-interactive-without-key", "start_session", start_payload, std::nullopt, interactive_now_ms + 10'000U ), interactive_now_ms + 1U ); REQUIRE(missing_start_idempotency.has_value()); auto missing_start_response = decode_response(*missing_start_idempotency); REQUIRE(missing_start_response.has_value()); REQUIRE(missing_start_response->error.has_value()); REQUIRE(missing_start_response->error->code == "invalid_request"); REQUIRE(session_runtime->list()->empty()); auto start_frame = (*protocol)->handle_frame( make_request( "start-interactive", "start_session", start_payload, "execute-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 1U ); REQUIRE(start_frame.has_value()); auto start_response = decode_response(*start_frame); REQUIRE(start_response.has_value()); REQUIRE(start_response->result.has_value()); session_record_result interactive_running; REQUIRE(!glz::read( interactive_running, start_response->result->str )); REQUIRE(interactive_running.session_id == interactive_created->session_id); REQUIRE(interactive_running.state == "running"); auto interactive_running_status = shared_registry->running_status(interactive_created->session_id); REQUIRE(interactive_running_status.has_value()); REQUIRE( interactive_running.profile_digest == std::optional{interactive_running_status->profile_digest} ); auto start_replay_frame = (*protocol)->handle_frame( make_request( "start-interactive-replay", "start_session", start_payload, "execute-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 2U ); REQUIRE(start_replay_frame.has_value()); auto start_replay_response = decode_response(*start_replay_frame); REQUIRE(start_replay_response.has_value()); REQUIRE(start_replay_response->result.has_value()); REQUIRE(start_replay_response->result->str == start_response->result->str); REQUIRE(session_runtime->list() == std::vector{"session-interactive"}); REQUIRE(!session_runtime->cleanup(interactive_created->session_id).has_value()); const std::string input_text = "interactive control\n"; const std::vector input_bytes{input_text.begin(), input_text.end()}; auto input_json = glz::write_json(input_bytes); REQUIRE(input_json.has_value()); const auto input_payload = "{\"session_id\":\"" + interactive_created->session_id + "\",\"bytes\":" + *input_json + "}"; auto input_frame = (*protocol)->handle_frame( make_request( "input-interactive", "write_stdin", input_payload, "input-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 2U ); REQUIRE(input_frame.has_value()); auto input_response = decode_response(*input_frame); REQUIRE(input_response.has_value()); REQUIRE(input_response->result.has_value()); session_mutation_result input_result; REQUIRE(!glz::read( input_result, input_response->result->str )); REQUIRE(input_result.schema_version == 1); REQUIRE(input_result.session_id == interactive_created->session_id); auto input_replay_frame = (*protocol)->handle_frame( make_request( "input-interactive-replay", "write_stdin", input_payload, "input-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 3U ); REQUIRE(input_replay_frame.has_value()); auto input_replay_response = decode_response(*input_replay_frame); REQUIRE(input_replay_response.has_value()); REQUIRE(input_replay_response->result.has_value()); REQUIRE(input_replay_response->result->str == input_response->result->str); const auto conflicting_input_payload = "{\"session_id\":\"" + interactive_created->session_id + "\",\"bytes\":[120]}"; auto input_conflict_frame = (*protocol)->handle_frame( make_request( "input-interactive-conflict", "write_stdin", conflicting_input_payload, "input-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 4U ); REQUIRE(input_conflict_frame.has_value()); auto input_conflict_response = decode_response(*input_conflict_frame); REQUIRE(input_conflict_response.has_value()); REQUIRE(input_conflict_response->error.has_value()); REQUIRE(input_conflict_response->error->code == "idempotency_conflict"); auto interactive_output = session_runtime->wait_read(interactive_created->session_id, 0, page, 1'000); REQUIRE(interactive_output.has_value()); REQUIRE(interactive_output->bytes.find("interactive control") != std::string::npos); const auto attach_payload = "{\"session_id\":\"" + interactive_created->session_id + "\",\"cursor\":0,\"max_bytes\":" + std::to_string(page) + "}"; auto attach_frame = (*protocol)->handle_frame( make_request( "attach-interactive", "attach", attach_payload, std::nullopt, interactive_now_ms + 10'000U ), interactive_now_ms + 5U ); REQUIRE(attach_frame.has_value()); auto attach_response = decode_response(*attach_frame); REQUIRE(attach_response.has_value()); REQUIRE(attach_response->result.has_value()); transcript_result attached; REQUIRE( !glz::read(attached, attach_response->result->str) ); REQUIRE(attached.schema_version == 1); REQUIRE(attached.session_id == interactive_created->session_id); REQUIRE(attached.oldest_cursor == interactive_output->oldest_cursor); REQUIRE(attached.next_cursor >= attached.oldest_cursor); REQUIRE(attached.next_cursor >= interactive_output->next_cursor); REQUIRE(!attached.truncated); const std::string attached_text{attached.bytes.begin(), attached.bytes.end()}; REQUIRE(attached_text.find("interactive control") != std::string::npos); const auto detach_payload = "{\"session_id\":\"" + interactive_created->session_id + "\",\"transcript_cursor\":" + std::to_string(attached.next_cursor) + "}"; auto detach_frame = (*protocol)->handle_frame( make_request( "detach-interactive", "detach", detach_payload, "detach-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 6U ); REQUIRE(detach_frame.has_value()); auto detach_response = decode_response(*detach_frame); REQUIRE(detach_response.has_value()); REQUIRE(detach_response->result.has_value()); session_cursor_result detached; REQUIRE( !glz::read(detached, detach_response->result->str) ); REQUIRE(detached.schema_version == 1); REQUIRE(detached.session_id == interactive_created->session_id); REQUIRE(detached.transcript_cursor == attached.next_cursor); const auto invalid_signal_payload = "{\"session_id\":\"" + interactive_created->session_id + "\",\"signal\":\"kill\"}"; auto invalid_signal_frame = (*protocol)->handle_frame( make_request( "signal-interactive-invalid", "signal", invalid_signal_payload, "signal-session-interactive-invalid", interactive_now_ms + 10'000U ), interactive_now_ms + 7U ); REQUIRE(invalid_signal_frame.has_value()); auto invalid_signal_response = decode_response(*invalid_signal_frame); REQUIRE(invalid_signal_response.has_value()); REQUIRE(invalid_signal_response->error.has_value()); REQUIRE(invalid_signal_response->error->code == "invalid_request"); const auto resize_payload = "{\"session_id\":\"" + interactive_created->session_id + "\",\"rows\":50,\"columns\":140}"; auto resize_frame = (*protocol)->handle_frame( make_request( "resize-interactive", "resize", resize_payload, "resize-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 6U ); REQUIRE(resize_frame.has_value()); auto resize_response = decode_response(*resize_frame); REQUIRE(resize_response.has_value()); REQUIRE(resize_response->result.has_value()); session_mutation_result resize_result; REQUIRE(!glz::read( resize_result, resize_response->result->str )); REQUIRE(resize_result.schema_version == 1); REQUIRE(resize_result.session_id == interactive_created->session_id); auto resize_replay_frame = (*protocol)->handle_frame( make_request( "resize-interactive-replay", "resize", resize_payload, "resize-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 7U ); REQUIRE(resize_replay_frame.has_value()); auto resize_replay_response = decode_response(*resize_replay_frame); REQUIRE(resize_replay_response.has_value()); REQUIRE(resize_replay_response->result.has_value()); REQUIRE(resize_replay_response->result->str == resize_response->result->str); const auto stop_payload = "{\"session_id\":\"" + interactive_created->session_id + "\"}"; auto stop_frame = (*protocol)->handle_frame( make_request( "stop-interactive", "stop_session", stop_payload, "stop-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 3U ); REQUIRE(stop_frame.has_value()); auto stop_response = decode_response(*stop_frame); REQUIRE(stop_response.has_value()); REQUIRE(stop_response->result.has_value()); session_record_result stop_record; REQUIRE(!glz::read( stop_record, stop_response->result->str )); REQUIRE(stop_record.session_id == interactive_created->session_id); REQUIRE(stop_record.state == "stopping" || stop_record.state == "exited"); REQUIRE(stop_record.profile_digest == interactive_running.profile_digest); auto interactive_stopping = shared_registry->stopping_status(interactive_created->session_id); std::uint64_t stopping_at_ms = 0; if (interactive_stopping) { REQUIRE(interactive_stopping->stopping_at_ms >= interactive_stopping->running_at_ms); stopping_at_ms = interactive_stopping->stopping_at_ms; } else { auto early_exit = shared_registry->exited_status(interactive_created->session_id); REQUIRE(early_exit.has_value()); REQUIRE(early_exit->stopping_at_ms >= early_exit->running_at_ms); stopping_at_ms = early_exit->stopping_at_ms; } auto stop_replay_frame = (*protocol)->handle_frame( make_request( "stop-interactive-replay", "stop_session", stop_payload, "stop-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 4U ); REQUIRE(stop_replay_frame.has_value()); auto stop_replay_response = decode_response(*stop_replay_frame); REQUIRE(stop_replay_response.has_value()); REQUIRE(stop_replay_response->result.has_value()); session_record_result stop_replay_record; REQUIRE(!glz::read( stop_replay_record, stop_replay_response->result->str )); REQUIRE(stop_replay_record.session_id == interactive_created->session_id); REQUIRE(stop_replay_record.state == "stopping" || stop_replay_record.state == "exited"); auto interactive_exited = session_runtime->wait(interactive_created->session_id); REQUIRE(interactive_exited.has_value()); REQUIRE(interactive_exited->session.state == glove::control::session_state::exited); REQUIRE( interactive_exited->termination_cause == glove::container::resource_termination_cause::signaled ); REQUIRE(interactive_exited->stopping_at_ms == stopping_at_ms); auto durable_interactive = shared_registry->exited_status("session-interactive"); REQUIRE(durable_interactive.has_value()); REQUIRE(durable_interactive->session == interactive_exited->session); REQUIRE(durable_interactive->profile_digest == interactive_exited->profile_digest); REQUIRE(durable_interactive->finished_at_ms == interactive_exited->finished_at_ms); REQUIRE(durable_interactive->receipt_digest == interactive_exited->receipt_digest); REQUIRE(durable_interactive->termination_cause == interactive_exited->termination_cause); const auto cleanup_payload = "{\"session_id\":\"" + interactive_created->session_id + "\"}"; auto cleanup_frame = (*protocol)->handle_frame( make_request( "cleanup-interactive", "cleanup_session", cleanup_payload, "cleanup-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 10U ); REQUIRE(cleanup_frame.has_value()); auto cleanup_response = decode_response(*cleanup_frame); REQUIRE(cleanup_response.has_value()); REQUIRE(cleanup_response->result.has_value()); session_mutation_result cleanup_result; REQUIRE(!glz::read( cleanup_result, cleanup_response->result->str )); REQUIRE(cleanup_result.schema_version == 1); REQUIRE(cleanup_result.session_id == interactive_created->session_id); auto cleanup_replay_frame = (*protocol)->handle_frame( make_request( "cleanup-interactive-replay", "cleanup_session", cleanup_payload, "cleanup-session-interactive", interactive_now_ms + 10'000U ), interactive_now_ms + 11U ); REQUIRE(cleanup_replay_frame.has_value()); auto cleanup_replay_response = decode_response(*cleanup_replay_frame); REQUIRE(cleanup_replay_response.has_value()); REQUIRE(cleanup_replay_response->result.has_value()); REQUIRE(cleanup_replay_response->result->str == cleanup_response->result->str); REQUIRE(session_runtime->list()->empty()); REQUIRE(!session_runtime->stop(interactive_created->session_id).has_value()); REQUIRE( session_runtime->start( *shared_producer, interactive_authorization, "execute-session-interactive", interactive_now_ms + 3U ) == interactive_exited->session ); REQUIRE(shared_producer->anchor().sequence == 2); REQUIRE(std::filesystem::is_empty(materializations)); REQUIRE(std::filesystem::exists(source / "tracked.txt")); const auto capability_bundle_root = tree.root() / "capability-bundles"; REQUIRE(std::filesystem::create_directory(capability_bundle_root)); REQUIRE(::chmod(capability_bundle_root.c_str(), 0700) == 0); auto capability_store = glove::supervisor::library_bundle_store::open(capability_bundle_root); REQUIRE(capability_store.has_value()); auto capability_registry = glove::control::session_registry::open_or_create( tree.root() / "capability-sessions.journal", shared_validator, std::make_shared( std::move(*capability_store) ) ); REQUIRE(capability_registry.has_value()); auto capability_registry_shared = std::shared_ptr{std::move(*capability_registry)}; auto capability_runtime = glove::control::linux_detail::linux_session_runtime::create( capability_registry_shared, preparer, {}, 1 ); REQUIRE(capability_runtime.has_value()); auto capability_runtime_shared = std::shared_ptr{ std::move(*capability_runtime) }; REQUIRE(capability_runtime_shared->refinement_evaluation_protocol_schema_version() == 1); auto capability_protocol = glove::control::receipt_audit_protocol::create( bootstrap_secret, shared_producer, shared_validator, capability_registry_shared, capability_runtime_shared, {}, materializations.string() ); REQUIRE(capability_protocol.has_value()); auto refinement_capability_frame = (*capability_protocol) ->handle_frame( make_request( "refinement-runtime-capabilities", "capabilities", "null", std::nullopt, interactive_now_ms + 20'000U ), interactive_now_ms + 12U ); REQUIRE(refinement_capability_frame.has_value()); auto refinement_capability_response = decode_response(*refinement_capability_frame); REQUIRE(refinement_capability_response.has_value()); REQUIRE(refinement_capability_response->result.has_value()); supervisor_capabilities refinement_capabilities; REQUIRE(!glz::read( refinement_capabilities, refinement_capability_response->result->str )); REQUIRE(refinement_capabilities.refinement_evaluation_protocol_schema_version == 1); REQUIRE(refinement_capabilities.observation_intent_channel_schema_version == 0); const std::string candidate_bytes = R"({"candidate":"exact"})"; const auto candidate_digest = glove::container::sha256_hex( std::span{ reinterpret_cast(candidate_bytes.data()), candidate_bytes.size() } ); REQUIRE(candidate_digest.has_value()); const auto placeholder_plan = refinement_plan( interactive_now_ms, page, std::string(64, 'f'), *candidate_digest, std::string(64, '0') ); auto plan_context = shared_validator->refinement_plan_context_digest_json(placeholder_plan, interactive_now_ms); REQUIRE(plan_context.has_value()); glove::container::refinement_fixture_manifest fixture{ .schema = std::string{glove::container::refinement_fixture_schema}, .evaluation_run_id = "run-control", .pair_id = "pair-control", .session_id = "session-refinement", .variant = glove::container::refinement_variant::candidate, .proposal_digest = std::string(64, 'a'), .base_projection_digest = std::string(64, 'b'), .candidate_projection_digest = *candidate_digest, .fixture_id = "fixture-control", .fixture_digest = std::string(64, 'd'), .dataset_ref = "fixture:control", .dataset_fingerprint = std::string(64, 'e'), .seed = 7, .model = { .provider = "test", .model_id = "synthetic", .model_family = "synthetic", .model_revision = std::nullopt, .tier = "local", .normalizer_version = 1, }, .harness = "synthetic", .module = std::nullopt, .skill_projection_id = "candidate", .skill_projection_digest = *candidate_digest, .matched_context_digest = std::string(64, '0'), .assertions = { .expected_termination = glove::container::resource_termination_cause::exited, .expected_exit_code = 0, .required_transcript_literals = {"alpha beta", "done"}, .forbidden_transcript_literals = {"forbidden"}, .max_latency_ms = 2'000, }, }; fixture.matched_context_digest = glove::container::refinement_fixture_context_digest(fixture, *plan_context).value(); const auto fixture_bytes = glove::container::canonical_refinement_fixture_bytes(fixture); REQUIRE(fixture_bytes.has_value()); const auto fixture_digest = glove::container::sha256_hex( std::span{ reinterpret_cast(fixture_bytes->data()), fixture_bytes->size() } ); REQUIRE(fixture_digest.has_value()); for (const auto& [digest, contents] : std::array{ std::pair{*candidate_digest, candidate_bytes}, std::pair{*fixture_digest, *fixture_bytes}, }) { std::ofstream output{ capability_bundle_root / (digest + ".json"), std::ios::binary | std::ios::trunc }; output << contents; output.flush(); REQUIRE(output.good()); REQUIRE(::chmod((capability_bundle_root / (digest + ".json")).c_str(), 0600) == 0); } const auto final_refinement_plan = refinement_plan( interactive_now_ms, page, *fixture_digest, *candidate_digest, fixture.matched_context_digest ); auto refinement_created = (*capability_protocol) ->handle_frame( make_request( "create-refinement", "create_session", std::string{ "{\"session_id\":\"session-refinement\",\"controller_plan_digest\":\"" } + std::string{controller_digest} + "\",\"plan\":" + final_refinement_plan + "}", "create-session-refinement", interactive_now_ms + 20'000U ), interactive_now_ms ); REQUIRE(refinement_created.has_value()); auto refinement_created_response = decode_response(*refinement_created); REQUIRE(refinement_created_response.has_value()); REQUIRE(refinement_created_response->result.has_value()); session_record_result refinement_record; REQUIRE(!glz::read( refinement_record, refinement_created_response->result->str )); REQUIRE( capability_runtime_shared->reconcile(*shared_producer, interactive_now_ms + 1U).has_value() ); const glove::control::session_start_authorization refinement_authorization{ .schema_version = 1, .authorization_id = "approval-session-refinement", .session_id = "session-refinement", .controller_plan_digest = std::string{controller_digest}, .plan_content_digest = refinement_record.plan_content_digest, .approved_at_ms = interactive_now_ms + 2U, .expires_at_ms = interactive_now_ms + 30'000U, }; auto refinement_started = capability_runtime_shared->start( *shared_producer, refinement_authorization, "execute-session-refinement", interactive_now_ms + 3U ); REQUIRE(refinement_started.has_value()); auto refinement_exited = capability_runtime_shared->wait("session-refinement"); REQUIRE(refinement_exited.has_value()); auto durable_refinement = capability_registry_shared->exited_status("session-refinement"); REQUIRE(durable_refinement.has_value()); REQUIRE(durable_refinement->refinement_receipt); REQUIRE(shared_producer->anchor().sequence == 3); return 0; } } // namespace glove_test auto main() -> int { return glove_test::run(); }