diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 000000000..d693ee9f8 --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,80 @@ +cmake_minimum_required(VERSION 3.25) +project(uos_cpp_pilot VERSION 0.1.0 LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_SKIP_RPATH ON) +set(CMAKE_BUILD_RPATH "") +set(CMAKE_INSTALL_RPATH "") +set(Boost_USE_STATIC_LIBS ON) +find_package(Boost 1.81.0 EXACT CONFIG REQUIRED COMPONENTS json system) +find_package(OpenSSL 3 REQUIRED) +find_package(Threads REQUIRED) +if(NOT CMAKE_CROSSCOMPILING) + foreach(target Boost::headers Boost::json Boost::system Boost::container) + if(TARGET ${target}) + set_property(TARGET ${target} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$ENV{UOS_CPP_TOOLCHAIN_DIR}/host-tools/include") + endif() + endforeach() +endif() +add_library(pilot_core src/core.cpp src/project_input.cpp src/project_tools.cpp src/payload_policy.cpp src/translator.cpp src/fixture_state.cpp src/fixture_control.cpp) +target_include_directories(pilot_core PUBLIC include) +target_link_libraries(pilot_core PUBLIC Boost::json Boost::system OpenSSL::SSL OpenSSL::Crypto Threads::Threads) +add_executable(uos_cpp_pilot src/main.cpp src/server.cpp src/session.cpp src/upstream.cpp src/transport.cpp) +target_link_libraries(uos_cpp_pilot PRIVATE pilot_core) +execute_process(COMMAND git rev-parse HEAD WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/.." OUTPUT_VARIABLE UOS_COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE UOS_GIT_RESULT) +execute_process(COMMAND git status --porcelain --untracked-files=normal -- cpp docs/DECISIONS.md WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/.." OUTPUT_VARIABLE UOS_DIRTY OUTPUT_STRIP_TRAILING_WHITESPACE) +if(UOS_GIT_RESULT EQUAL 0 AND UOS_DIRTY STREQUAL "") + set(UOS_IDENTITY_SHA "${UOS_COMMIT}") + set(UOS_IDENTITY_DEPLOYMENT "cpp-${UOS_COMMIT}") + set(UOS_IDENTITY_DIRTY 0) +else() + set(UOS_IDENTITY_SHA "unknown") + set(UOS_IDENTITY_DEPLOYMENT "cpp-dirty") + set(UOS_IDENTITY_DIRTY 1) +endif() +target_compile_definitions(uos_cpp_pilot PRIVATE UOS_GIT_SHA="${UOS_IDENTITY_SHA}" UOS_DEPLOYMENT_ID="${UOS_IDENTITY_DEPLOYMENT}" UOS_BUILD_DIRTY=${UOS_IDENTITY_DIRTY}) + +foreach(target pilot_core uos_cpp_pilot) + target_compile_options(${target} PRIVATE -Wall -Wextra -Wconversion -Wshadow -Werror=return-type -ffile-prefix-map=${PROJECT_SOURCE_DIR}=uos-cpp-pilot -ffile-prefix-map=$ENV{UOS_CPP_TOOLCHAIN_DIR}=uos-sdk -fdebug-prefix-map=${CMAKE_BINARY_DIR}=uos-cpp-build) + if(UOS_SANITIZE) + target_compile_options(${target} PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer) + target_link_options(${target} PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer) + endif() +endforeach() +install(TARGETS uos_cpp_pilot RUNTIME DESTINATION bin) +enable_testing() +add_executable(protocol_tests tests/protocol_tests.cpp) +target_link_libraries(protocol_tests PRIVATE pilot_core) +if(UOS_SANITIZE) + target_compile_options(protocol_tests PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer) + target_link_options(protocol_tests PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer) +endif() +add_test(NAME protocol COMMAND protocol_tests) +set_tests_properties(protocol PROPERTIES TIMEOUT 30 RUN_SERIAL TRUE) + +if(UOS_BUN_EXECUTABLE) + add_test(NAME socket COMMAND "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/../scripts/test-network.ts" -- "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/tests/conformance.ts" "$" "${UOS_RUNTIME_PREFIX}") + set_tests_properties(socket PROPERTIES TIMEOUT 45 RUN_SERIAL TRUE) +endif() + +if(UOS_BUN_EXECUTABLE) + add_test(NAME disconnect COMMAND "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/../scripts/test-network.ts" -- "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/tests/conformance.ts" "$" "${UOS_RUNTIME_PREFIX}" --disconnect) + set_tests_properties(disconnect PROPERTIES TIMEOUT 30 RUN_SERIAL TRUE) +endif() + +if(UOS_BUN_EXECUTABLE) + add_test(NAME control COMMAND "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/../scripts/test-network.ts" -- "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/tests/conformance.ts" "$" "${UOS_RUNTIME_PREFIX}" --control) + set_tests_properties(control PROPERTIES TIMEOUT 30 RUN_SERIAL TRUE) +endif() + +if(UOS_BUN_EXECUTABLE) + add_test(NAME recovery COMMAND "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/../scripts/test-network.ts" -- "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/tests/recovery.ts" "$" "${UOS_RUNTIME_PREFIX}") + set_tests_properties(recovery PROPERTIES TIMEOUT 90 RUN_SERIAL TRUE) +endif() + +if(UOS_BUN_EXECUTABLE) + add_test(NAME resources COMMAND "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/../scripts/test-network.ts" -- "${UOS_BUN_EXECUTABLE}" --no-env-file --no-install "${PROJECT_SOURCE_DIR}/tests/resources.ts" "$" "${UOS_RUNTIME_PREFIX}") + set_tests_properties(resources PROPERTIES TIMEOUT 45 RUN_SERIAL TRUE) +endif() diff --git a/cpp/CMakePresets.json b/cpp/CMakePresets.json new file mode 100644 index 000000000..1aad42672 --- /dev/null +++ b/cpp/CMakePresets.json @@ -0,0 +1,84 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "pi-release", + "generator": "Ninja", + "binaryDir": "${sourceDir}/../.data/cpp-build/pi-release", + "cacheVariables": { + "CMAKE_MAKE_PROGRAM": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/bin/ninja", + "Boost_DIR": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/lib/aarch64-linux-gnu/cmake/Boost-1.81.0", + "CMAKE_PREFIX_PATH": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr", + "CMAKE_CXX_COMPILER_LAUNCHER": "/usr/bin/python3;${sourceDir}/tools/compiler-limit.py", + "CMAKE_FIND_USE_PACKAGE_REGISTRY": "OFF", + "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY": "OFF", + "CMAKE_TOOLCHAIN_FILE": "$env{UOS_CPP_TOOLCHAIN_DIR}/probe/pi-arm64.cmake", + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_FLAGS_RELEASE": "-O2 -DNDEBUG -g1", + "CMAKE_CXX_FLAGS": "-B$env{UOS_CPP_TOOLCHAIN_DIR}/host-tools/bin/", + "CMAKE_AR": "$env{UOS_CPP_TOOLCHAIN_DIR}/host-tools/bin/ar", + "CMAKE_RANLIB": "$env{UOS_CPP_TOOLCHAIN_DIR}/host-tools/bin/ranlib", + "CMAKE_LINKER": "$env{UOS_CPP_TOOLCHAIN_DIR}/host-tools/bin/ld", + "CMAKE_STRIP": "$env{UOS_CPP_TOOLCHAIN_DIR}/host-tools/bin/strip", + "OPENSSL_INCLUDE_DIR": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/include", + "OPENSSL_SSL_LIBRARY": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/lib/aarch64-linux-gnu/libssl.so", + "OPENSSL_CRYPTO_LIBRARY": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/lib/aarch64-linux-gnu/libcrypto.so", + "CMAKE_CROSSCOMPILING_EMULATOR": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/lib/ld-linux-aarch64.so.1;--library-path;$env{UOS_CPP_TOOLCHAIN_DIR}/root/lib/aarch64-linux-gnu:$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/lib/aarch64-linux-gnu" + } + }, + { + "name": "native-asan", + "generator": "Ninja", + "binaryDir": "${sourceDir}/../.data/cpp-build/native-asan", + "cacheVariables": { + "CMAKE_MAKE_PROGRAM": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/bin/ninja", + "Boost_DIR": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr/lib/aarch64-linux-gnu/cmake/Boost-1.81.0", + "CMAKE_PREFIX_PATH": "$env{UOS_CPP_TOOLCHAIN_DIR}/root/usr", + "CMAKE_CXX_COMPILER_LAUNCHER": "/usr/bin/python3;${sourceDir}/tools/compiler-limit.py", + "CMAKE_FIND_USE_PACKAGE_REGISTRY": "OFF", + "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY": "OFF", + "CMAKE_CXX_COMPILER": "/usr/bin/g++", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS_DEBUG": "-O0 -g1", + "UOS_SANITIZE": "ON", + "OPENSSL_INCLUDE_DIR": "/usr/include", + "OPENSSL_SSL_LIBRARY": "/usr/lib/libssl.so", + "OPENSSL_CRYPTO_LIBRARY": "/usr/lib/libcrypto.so" + } + } + ], + "buildPresets": [ + { + "name": "pi-release", + "configurePreset": "pi-release", + "jobs": 1 + }, + { + "name": "native-asan", + "configurePreset": "native-asan", + "jobs": 1 + } + ], + "testPresets": [ + { + "name": "pi-release", + "configurePreset": "pi-release", + "execution": { + "jobs": 1 + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "native-asan", + "configurePreset": "native-asan", + "execution": { + "jobs": 1 + }, + "output": { + "outputOnFailure": true + } + } + ] +} diff --git a/cpp/README.md b/cpp/README.md new file mode 100644 index 000000000..856aacc54 --- /dev/null +++ b/cpp/README.md @@ -0,0 +1,65 @@ +# C++ direct DeepSeek fixture pilot + +This isolated C++20 candidate serves an authenticated streamed loopback `POST /v1/responses` for direct `deepseek-flash`, using the TypeScript Chat translation as its oracle. It is a fixture candidate, with a separate synthetic authority and storage codec. It grants no production standby, credential, database, provider-spend or activation authority. + +PR1 is merged at `db1d78cf9037775ba29b8aa72e89f2b2dbd6ba80`, tree `26b895d911b50f3ffcc3c139aa2869aec05706d8`. Its underlying deployed handler is `d94fef52ccc76e0d5aa019154f11e47d5e10fba6`. The C++ implementation contract and acceptance matrix remain the gate authority; a successful build or health response does not establish acceptance. + +The runtime has one Asio IO thread, one provider attempt per request, two active permits, no waiting queue, and at most eight header-reading sockets. It preserves source dispatch-quota reservation/defer/release order, full history, tool-call IDs, reasoning and usage missingness. The captured namespace replay uses the bare original name; the immediate peer-FIN vector is interruption, with no response, dispatch or charge. Those source quirks are retained deliberately. + +The reduced profile caps headers at16KiB, ingress at4MiB, an upstream SSE frame at1MiB, and the full terminal at2MiB. JSON depth64 and named allocation/node/event limits apply. These are declared pilot capacities; they do not claim the production32MiB ingress or unbounded cumulative output contracts. The request-owned first-frame/inactivity deadline does not authorize provider retry or quota failover. + +## Reproducible offline build + +Use the controller-qualified Debian ARM64 SDK manifest `9dde9684e06dcbbdc5433fd26c8517cb0ff0b63c2a80e2501f05f6ce27eb52f5`. It contains GCC12.2, Boost1.81, OpenSSL3, CMake3.25 and Ninja1.11. No dependency downloads, installs or application compilation occur at runtime. The builder's installed GCC16 is used only for the native sanitizer preset. + +```sh +python3 cpp/tools/build.py /absolute/qualified-sdk pi-release checkpoint +python3 cpp/tools/build.py /absolute/qualified-sdk pi-release test +python3 cpp/tools/build.py /absolute/qualified-sdk native-asan checkpoint +python3 cpp/tools/build.py /absolute/qualified-sdk native-asan test +python3 cpp/tools/build.py /absolute/qualified-sdk pi-release tidy +``` + +The driver creates one reusable directory per preset under `.data/cpp-build`, a task-local CMake executable view, disk-backed compiler scratch, and full command/resource logs under `.data/cpp-evidence`. Its view executes the same qualified SDK binaries through their pinned loader with `--argv0`, so generated archive, regeneration, test and install commands use that same loader. The SDK itself stays read-only; no global library path changes are required. + +Build jobs remain1. The first512MiB compiler virtual-memory limit was measured insufficient for the transport translation unit. The owner authorized768MiB for the compiler and bounded build cgroup; this is distinct from the candidate's provisional96MiB service-memory acceptance target. The one-second build sampler reports a lower-bound disk peak, enforces the additional768MiB/aggregate2GB storage envelope and refuses filesystem free space below2GiB. Native ASan runtime requires its shadow address space and does not inherit the compiler VM cap. + +The Pi release uses the SDK compiler/sysroot, static Boost.JSON/System and dynamic target OpenSSL/libstdc++/libc. CTest launches that binary through the same target loader on the VPS; actual Pi execution remains a separate acceptance surface. The native sanitizer preset uses host Linux headers and only the SDK's Boost header subtree. + +## Private fixture configuration + +Create an owned mode700 state directory and a private JSON configuration. The only runtime command is `uos_cpp_pilot --config /path/to/config.json`. A minimal synthetic configuration is: + +```json +{ + "fixture_mode": true, + "token": "synthetic-secret", + "upstream_key": "synthetic-provider-secret", + "upstream_origin": "http://127.0.0.1:12345", + "state_directory": "/private/task/fixture-state", + "usage_limit": 50, + "port": 0 +} +``` + +Read the `ready` stdout event for the actual loopback endpoint. Plain HTTP upstreams require the explicit fixture mode and loopback address. Ordinary TLS transport fixes the provider origin, verifies certificate chain and hostname, sends SNI, and uses the configured CA file. No request body can choose an upstream URL, credential, filesystem path or provider. + +The journal owns an exclusive lock and records source quota transitions separately from semantic terminal, physical delivery and internal settlement. Each committed journal frame is checksummed and fsynced before publication. Snapshots use file fsync, atomic rename and directory fsync with a high-water sequence. Restart never dispatches a generation. A reserved lease is not reclaimed merely because a process disappeared; an ambiguous dispatched request remains charged. The codec is `uos-cpp-fixture-state-v1`, not Bun or Deno KV. + +## Async owners + +`Runtime` owns the IO loop dependencies, acceptor, authority, control descriptors and graceful-drain deadline. Each `Session` owns its HTTP parser/buffer, socket, request arenas, upstream, cancellation state, permit and delivery facts. A downstream wait callback owns the session until it completes; teardown cancels and joins that wait before the permit and buffers are released. Each write retains its complete owned wire buffer until completion, and upstream reads pause until downstream events drain. + +`Transport` owns resolver/TLS/socket/timer state across each await. Timer callbacks use weak ownership and cannot dereference a destroyed transport. `Upstream` separately owns HTTP response framing and the current bounded body buffer. JSON arenas outlive every value using them. Synchronous fixture durability occurs on this same IO thread; its fsync latency is measured and cannot be preempted by an Asio timer. + +The optional inherited fixture-control channel has no HTTP route. It requires actual owned anonymous pipe descriptors with exact read/write direction; an arbitrary child-process `pipe` abstraction must not be assumed to provide them. Fixed framed commands control only synthetic revocation, bounded readback and finite crash fences. Ordinary runtime has no armed fence or channel. + +## Qualification status + +The first Pi-release checkpoint built and passed the pure protocol smoke plus the shared real-socket two-turn tool loop and schema-refusal accounting. It was a dirty working-tree checkpoint, not a final release identity. Real disconnect, pinned native client, extended boundaries, control/crash recovery, sanitizer/tidy and independent Pi resource/soak evidence remain to be bound to the final source and binary. Buffered/nonstream requests are UNSUPPORTED pilot scope: false or missing `stream` is rejected with HTTP400 `invalid_request_error`/`param: stream` before provider dispatch, preserving eager reserve/release accounting when quota exists. TypeScript buffered behavior is unchanged; this is not buffered parity or a general OpenAI replacement. + +The shared TypeScript producer and client remain in `tests/helpers/responses-conformance-*.ts`. The C++ adapter adds no translation or expected-output repair: it starts the actual candidate, records its real socket output and checks the private checksummed quota journal independently. Official event field reference: . + +The24MiB request budget includes named JSON allocations plus conservative external capacity reservations: four times observed ingress bytes plus256KiB during projection and8MiB plus tool-name metadata during relay. These reservations allocate no backing arrays and are reported as capacity budget, separately from actual arena/cgroup peaks. Namespace labels share one immutable owner rather than multiplying a large label per nested tool. + +The accepted downstream socket requests a fixed16KiB `SO_SNDBUF` (Linux commonly reports32KiB); the actual value and same-loop upstream-read/downstream-write pending/start/completion counters are available only through inherited `READ_STATE`. They are process-epoch observations, not durable quota counters. This bounded profile seam supports observed slow-reader pressure without adding a public API or environment setting. The ASan Session translation unit exceeded the measured768MiB build VM cap; the controller authorized a1GiB compiler/task retry after fresh2GiB host headroom, with the service96MiB target unchanged. diff --git a/cpp/include/pilot/control.hpp b/cpp/include/pilot/control.hpp new file mode 100644 index 000000000..a8a4106bc --- /dev/null +++ b/cpp/include/pilot/control.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "pilot/state.hpp" +#include +#include +#include +#include +#include +namespace pilot { + namespace asio=boost::asio; + class Control:public std::enable_shared_from_this { + State& state_; + IoCounters& counters_; + asio::posix::stream_descriptor input_,output_; + asio::steady_timer fence_timer_,send_gate_,write_deadline_; + std::function stop_; + bool enabled_=false,armed_=false,held_=false,stopped_=false,sending_=false,send_waiter_=false; + std::uint64_t last_id_=0,fence_id_=0,ordinal_=0; + std::size_t commands_=0; + std::string phase_,request_id_,key_id_; + asio::awaitable send(json::object message); + asio::awaitable read_frame(); + public: Control(asio::io_context& io,Config const& config,State& state,IoCounters& counters,std::function stop); + asio::awaitable run(); + asio::awaitable fence(std::uint64_t ordinal,std::string_view phase,std::string_view request_id=""); + void stop(); + bool enabled() const { + return enabled_; + } + }; +} diff --git a/cpp/include/pilot/core.hpp b/cpp/include/pilot/core.hpp new file mode 100644 index 000000000..493f581e9 --- /dev/null +++ b/cpp/include/pilot/core.hpp @@ -0,0 +1,104 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace pilot { + namespace json = boost::json; + struct Failure : std::runtime_error { + int status; + std::string code, param; + Failure(int status, std::string message, std::string code = "invalid_request_error", std::string param = ""); + }; + std::string text(json::value const& value); + std::string field(json::object const& value, std::string_view key, std::string fallback = ""); + json::value const& member(json::object const& value, std::string_view key); + bool flag(json::object const& value, std::string_view key, bool fallback = false); + std::int64_t integer(json::object const& value, std::string_view key, std::int64_t fallback = 0); + std::int64_t now_ms(); + std::string sha256(std::string_view value); + std::string random_id(std::string_view prefix); +std::string repair_utf8(std::string_view input); +bool nonblank_utf16(std::string_view input); + json::object error_payload(Failure const& failure); + struct IoCounters { + std::string epoch=random_id("io_"); + std::uint64_t upstream_read_starts=0,upstream_read_completions=0,pending_upstream_reads=0; + std::uint64_t downstream_write_starts=0,downstream_write_completions=0,pending_downstream_writes=0; + std::uint64_t downstream_write_successes=0,actual_downstream_send_buffer_bytes=0,active_requests=0; + }; + struct Limits { + std::size_t request_bytes = 4 * 1024 * 1024, frame_bytes = 1024 * 1024, terminal_bytes = 2 * 1024 * 1024; + std::size_t events = 65536, nodes = 65536, depth = 64; + std::chrono::milliseconds header { + 5000 + }, body { + 30000 + }, connect { + 10000 + }, headers { + 120000 + }, frame { + 1800000 + }, write { + 30000 + }; + }; + struct Config { + std::string host = "127.0.0.1", upstream_host, upstream_port, upstream_target = "/chat/completions", upstream_key; + std::string ca_file, token_hash, key_id = "responses-conformance-key", state_directory; + unsigned short port = 0; + bool tls = true, fixture = false; + std::int64_t quota_limit = 50; + int control_in = -1, control_out = -1; + Limits limits; + }; + Config read_config(std::filesystem::path const& path); + struct AllocationGroup { + std::size_t current = 0, peak = 0, limit = 24 * 1024 * 1024; + }; + class BudgetResource final : public json::memory_resource { + std::shared_ptr group_; + std::size_t limit_, current_ = 0, peak_ = 0; + void* do_allocate(std::size_t bytes, std::size_t alignment) override; + void do_deallocate(void* pointer, std::size_t bytes, std::size_t alignment) override; + bool do_is_equal(json::memory_resource const& other) const noexcept override; + public: BudgetResource(std::shared_ptr group, std::size_t limit) : group_(std::move(group)), limit_(limit) { + } std::size_t peak() const { + return peak_; + } + }; + struct JsonScan { + std::size_t bytes = 0, nodes = 0, depth = 0; + bool quoted = false, escaped = false, atom = false; + void feed(std::string_view input, Limits const& limits); + }; + struct ToolName { + std::string chat, name; + std::shared_ptr space; + bool custom = false; + }; + struct Prepared { + json::object body, echo; + std::vector names; + std::string model; + bool stream = true; + std::string warning; + explicit Prepared(json::storage_ptr storage) : body(storage), echo(storage) { + } + }; + Prepared project(json::object const& raw, Limits const& limits, json::storage_ptr storage); + void project_input(json::object const& raw, json::array& messages, bool reject); + void project_tools(json::object const& raw, Prepared& result); + std::string bounded_payload(std::string value, std::string const& path, bool reject, bool arguments = false); + std::size_t estimate_tokens(std::string_view input); + inline constexpr std::string_view summary_prefix = "Another language model started to solve this problem and produced a summary of its thinking process. You also have access to the state of the tools that were used by that language model. Use this to build on the work that has already been done and avoid duplicating work. Here is the summary produced by the other language model, use the information in this summary to assist with your own analysis:"; +} diff --git a/cpp/include/pilot/network.hpp b/cpp/include/pilot/network.hpp new file mode 100644 index 000000000..9ae72f039 --- /dev/null +++ b/cpp/include/pilot/network.hpp @@ -0,0 +1,46 @@ +#pragma once +#include "pilot/control.hpp" +#include +#include +namespace pilot { + using tcp=asio::ip::tcp; + class Upstream { + struct Impl; + std::shared_ptr impl_; + public: Upstream(asio::io_context& io,Config const& config,IoCounters& counters); + ~Upstream(); + asio::awaitable open(std::string body); + asio::awaitable read(); + int status() const; + std::string header(std::string_view key) const; + void reset_frame_deadline(); + void cancel(); + }; + class Session; + struct Runtime { + asio::io_context& io; + Config config; + State state; + tcp::acceptor acceptor; + IoCounters counters; + std::shared_ptr control; + unsigned sockets=0,active=0; + std::uint64_t ordinal=0; + bool stopping=false; + std::function on_drained; + asio::steady_timer drain_timer; + std::vector> sessions; + Runtime(asio::io_context& io,Config config); + void stop(bool force=false); + void drained(); + asio::awaitable accept_loop(); + }; + class Session:public std::enable_shared_from_this { + struct Impl; + std::unique_ptr impl_; + public: Session(Runtime& runtime,tcp::socket socket); + ~Session(); + asio::awaitable run(); + void cancel(); + }; +} diff --git a/cpp/include/pilot/state.hpp b/cpp/include/pilot/state.hpp new file mode 100644 index 000000000..404f1fb6f --- /dev/null +++ b/cpp/include/pilot/state.hpp @@ -0,0 +1,48 @@ +#pragma once +#include "pilot/core.hpp" +namespace pilot { + struct OwnedFd { + int value = -1; + operator int() const { return value; } + OwnedFd& operator=(int descriptor) { value = descriptor; return *this; } + ~OwnedFd(); +}; +class State { + Config const& config_; + OwnedFd lock_, journal_; + std::filesystem::path root_; + std::shared_ptr budget_ = std::make_shared(); + BudgetResource resource_ { + budget_,8*1024*1024 + }; + json::object data_ { + json::storage_ptr(&resource_) + }; + std::uint64_t seq_ = 0; + std::size_t journal_bytes_ = 0, frames_ = 0; + bool unavailable_ = false; + void append(std::string const& op,json::object const& value,bool quota); + void apply(std::string const& op,json::object const& value,bool quota); + void load(); + public: explicit State(Config const& config); + ~State(); + State(State const&) = delete; + State& operator=(State const&) = delete; + bool authenticate(std::string_view token) const; + bool reserve(std::string const& id); + void dispatch(std::string const& id); + void release(std::string const& id,bool compensate = false); + void terminal(std::string const& id,std::string const& kind,json::value const& usage); + void delivered(std::string const& id,bool success); + void settle(std::string const& id); + void revoke(); + json::object inspect(std::string const& id = "") const; + bool needs_snapshot() const; + void snapshot_publish(); + void snapshot_sync_directory(); + void snapshot_reset_journal(); + std::uint64_t sequence() const { + return seq_; + } + }; +} diff --git a/cpp/include/pilot/translator.hpp b/cpp/include/pilot/translator.hpp new file mode 100644 index 000000000..9349b861a --- /dev/null +++ b/cpp/include/pilot/translator.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "pilot/core.hpp" +namespace pilot { + class Translator { + Prepared const& request_; + Limits const& limits_; + json::storage_ptr storage_; + std::string id_, message_id_, finish_reason_; + std::int64_t created_; + json::object strings_, calls_; + json::array output_; + json::value usage_; + bool started_ = false, finished_ = false, message_open_ = false, message_done_ = false, reasoning_open_ = false, reasoning_pending_ = false; + int next_ = 0, message_index_ = -1, reasoning_index_ = -1, text_index_ = -1, refusal_index_ = -1; + json::array start(); + json::object envelope(std::string const& status); + void close_reasoning(json::array& events); + void open_reasoning(json::array& events); + void open_message(json::array& events); + void close_message(json::array& events); + void announce_tool(json::object& call,json::array& events); + void retain(std::string_view key,std::string_view delta); + public: Translator(Prepared const& request,Limits const& limits,std::string id,json::storage_ptr storage); + json::array open(); + json::array push(json::object const& chunk); + json::array finish(); + json::value const& usage() const { + return usage_; + } std::string terminal_kind() const; + std::string const& id() const { + return id_; + } + }; + class SseReader { + std::string pending_; + std::size_t cap_, frames_ = 0, max_frames_; + public: SseReader(std::size_t cap,std::size_t frames):cap_(cap),max_frames_(frames) { + } void feed(std::string_view input); + std::optional next(); + }; +} diff --git a/cpp/include/pilot/transport.hpp b/cpp/include/pilot/transport.hpp new file mode 100644 index 000000000..e0df51078 --- /dev/null +++ b/cpp/include/pilot/transport.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "pilot/core.hpp" +#include +#include +#include +namespace pilot { +class Transport { + struct Impl; + std::shared_ptr impl_; +public: + Transport(boost::asio::io_context& io,Config const& config,IoCounters& counters); + ~Transport(); + boost::asio::awaitable connect(); + boost::asio::awaitable write(std::string value); + boost::asio::awaitable read(boost::asio::mutable_buffer buffer); + void deadline(std::chrono::steady_clock::time_point time); + void cancel(); +}; +} diff --git a/cpp/src/core.cpp b/cpp/src/core.cpp new file mode 100644 index 000000000..553c276d2 --- /dev/null +++ b/cpp/src/core.cpp @@ -0,0 +1,173 @@ +#include "pilot/core.hpp" +#include +#include +#include +#include +#include +#include +namespace pilot { + Failure::Failure(int s, std::string message, std::string c, std::string p) : std::runtime_error(std::move(message)), status(s), code(std::move(c)), param(std::move(p)) { + } std::string text(json::value const& v) { + return v.is_string() ? std::string(v.as_string()) : ""; + } json::value const& member(json::object const& v, std::string_view k) { + static json::value const empty; + auto p = v.if_contains(k); + return p ? *p : empty; + } std::string field(json::object const& v, std::string_view k, std::string fallback) { + auto const& x = member(v,k); + return x.is_string() ? text(x) : std::move(fallback); + } bool flag(json::object const& v, std::string_view k, bool fallback) { + auto const& x = member(v,k); + return x.is_bool() ? x.as_bool() : fallback; + } std::int64_t integer(json::object const& v, std::string_view k, std::int64_t fallback) { + auto const& x = member(v,k); + if(x.is_int64()) return x.as_int64(); + if(x.is_uint64() && x.as_uint64() <= static_cast(INT64_MAX)) return static_cast(x.as_uint64()); + return fallback; + } std::int64_t now_ms() { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + } std::string sha256(std::string_view v) { + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(v.data()),v.size(),digest); + std::string r; + r.reserve(64); + constexpr char hex[]="0123456789abcdef"; + for(auto c:digest) { + r+=hex[c>>4]; + r+=hex[c&15]; + } return r; + } std::string random_id(std::string_view prefix) { + unsigned char bytes[16]; + if(RAND_bytes(bytes,16)!=1) throw std::runtime_error("Random identity unavailable"); + std::string r(prefix); + constexpr char hex[]="0123456789abcdef"; + for(auto c:bytes) { + r+=hex[c>>4]; + r+=hex[c&15]; + } return r; + } json::object error_payload(Failure const& f) { + json::object e { + { + "message",f.what() + }, { + "type",f.status>=500?"server_error":f.status==429?"rate_limit_error":"invalid_request_error" + }, { + "code",f.code + } + }; + if(!f.param.empty()) e["param"]=f.param; + return { + { + "error",std::move(e) + } + }; + } void* BudgetResource::do_allocate(std::size_t n, std::size_t a) { + if(n>limit_-current_ || n>group_->limit-group_->current) throw std::bad_alloc(); + void* p=::operator new(n,std::align_val_t(a)); + current_+=n; + group_->current+=n; + peak_=std::max(peak_,current_); + group_->peak=std::max(group_->peak,group_->current); + return p; + } void BudgetResource::do_deallocate(void* p,std::size_t n,std::size_t a) { + current_-=n; + group_->current-=n; + ::operator delete(p,std::align_val_t(a)); + } bool BudgetResource::do_is_equal(json::memory_resource const& other) const noexcept { + return this==&other; + } void JsonScan::feed(std::string_view input, Limits const& l) { + bytes+=input.size(); + if(bytes>l.request_bytes) throw Failure(413,"Request exceeds the pilot byte bound"); + for(char c:input) { + if(quoted) { + if(escaped)escaped=false; + else if(c=='\\')escaped=true; + else if(c=='"')quoted=false; + continue; + } if(c=='"') { + quoted=true; + atom=false; + ++nodes; + } + else if(c=='{'||c=='[') { + ++nodes; + ++depth; + atom=false; + } + else if(c=='}'||c==']') { + if(depth)--depth; + atom=false; + } + else if(c==','||c==':'||c==' '||c=='\n'||c=='\r'||c=='\t')atom=false; + else if(!atom) { + ++nodes; + atom=true; + } + if(depth>l.depth||nodes>l.nodes)throw Failure(400,"JSON exceeds the pilot depth or node bound"); + } + } Config read_config(std::filesystem::path const& path) { + if(std::filesystem::is_symlink(path)||!std::filesystem::is_regular_file(path)) throw std::runtime_error("Configuration must be a regular private file"); + std::ifstream file(path); + if(!file)throw std::runtime_error("Configuration file unavailable"); + file.seekg(0,std::ios::end); + if(file.tellg()>16384)throw std::runtime_error("Configuration exceeds its bound"); + file.seekg(0); + std::string data((std::istreambuf_iterator(file)), { + }); + if(data.size()>16384)throw std::runtime_error("Configuration exceeds its bound"); + auto v=json::parse(data); + if(!v.is_object())throw std::runtime_error("Configuration must be an object"); + auto const& o=v.as_object(); + Config c; + c.fixture=flag(o,"fixture_mode"); + c.port=static_cast(integer(o,"port")); + c.state_directory=field(o,"state_directory"); + c.quota_limit=integer(o,"usage_limit",50); + c.key_id=field(o,"key_id",c.key_id); + c.ca_file=field(o,"ca_file","/etc/ssl/certs/ca-certificates.crt"); + c.upstream_key=field(o,"upstream_key"); + auto token=field(o,"token"); + if(token.empty()||token.size()>256||c.state_directory.empty()||c.quota_limit<0)throw std::runtime_error("Synthetic token/state/quota configuration required"); + c.token_hash=sha256(token); + auto origin=field(o,"upstream_origin","https://api.deepseek.com"); + auto start=origin.find("://"); + if(start==std::string::npos)throw std::runtime_error("Invalid upstream origin"); + auto scheme=origin.substr(0,start); + auto authority=origin.substr(start+3); + if(authority.find_first_of("/@?#")!=std::string::npos)throw std::runtime_error("Upstream must be a root origin"); + auto colon=authority.find(':'); + c.upstream_host=authority.substr(0,colon); + c.tls=scheme=="https"; + c.upstream_port=colon==std::string::npos?(c.tls?"443":"80"):authority.substr(colon+1); + if(!c.tls && !(c.fixture&&scheme=="http"&&c.upstream_host=="127.0.0.1"))throw std::runtime_error("Plain HTTP requires an isolated loopback fixture"); + if(c.tls&&c.upstream_host!="api.deepseek.com"&&!c.fixture)throw std::runtime_error("Provider origin is fixed"); + c.control_in=static_cast(integer(o,"control_read_fd",-1)); + c.control_out=static_cast(integer(o,"control_write_fd",-1)); + if(!c.fixture&&(c.control_in>=0||c.control_out>=0))throw std::runtime_error("Control descriptors require fixture mode"); + if(c.fixture&&flag(o,"short_deadlines")) { + c.limits.body=std::chrono::milliseconds(5000); + c.limits.headers=c.limits.frame=c.limits.write=c.limits.body; + } return c; + } +std::string repair_utf8(std::string_view input) { + std::string result; + for (std::size_t i = 0; i < input.size();) { + auto first = static_cast(input[i]); + if (first < 0x80) { result += input[i++]; continue; } + unsigned length = first >= 0xc2 && first <= 0xdf ? 2 : first >= 0xe0 && first <= 0xef ? 3 : first >= 0xf0 && first <= 0xf4 ? 4 : 0; + if (!length) { result += "\xef\xbf\xbd"; ++i; continue; } + unsigned consumed = 1; + while (consumed < length && i + consumed < input.size()) { + auto c = static_cast(input[i + consumed]); + bool valid = c >= 0x80 && c <= 0xbf; + if (consumed == 1) valid = valid && !(first == 0xe0 && c < 0xa0) && !(first == 0xed && c > 0x9f) && !(first == 0xf0 && c < 0x90) && !(first == 0xf4 && c > 0x8f); + if (!valid) break; + ++consumed; + } + if (consumed == length) result.append(input.substr(i, length)); + else result += "\xef\xbf\xbd"; + i += consumed; + } + return result; +} +} diff --git a/cpp/src/fixture_control.cpp b/cpp/src/fixture_control.cpp new file mode 100644 index 000000000..48e425c6e --- /dev/null +++ b/cpp/src/fixture_control.cpp @@ -0,0 +1,195 @@ +#include "pilot/control.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace pilot { + Control::Control(asio::io_context& io,Config const& c,State& s,IoCounters& counters,std::function stop):state_(s),counters_(counters),input_(io),output_(io),fence_timer_(io),send_gate_(io),write_deadline_(io),stop_(std::move(stop)),key_id_(c.key_id) { + if(c.control_in<0&&c.control_out<0)return; + if(!c.fixture||c.control_in<0||c.control_out<0||c.control_in==c.control_out)throw std::runtime_error("Invalid fixture descriptors"); + for(auto pair: { + std::pair { + c.control_in,O_RDONLY + },std::pair { + c.control_out,O_WRONLY + } + }) { + struct stat st { + }; + int mode=::fcntl(pair.first,F_GETFL); + if(::fstat(pair.first,&st)||!S_ISFIFO(st.st_mode)||st.st_uid!=::getuid()||mode<0||(mode&O_ACCMODE)!=pair.second)throw std::runtime_error("Fixture descriptors must be owned anonymous pipes with exact directions"); + std::array target{}; + auto count=::readlink(("/proc/self/fd/"+std::to_string(pair.first)).c_str(),target.data(),target.size()); + if(count<6||!std::string_view(target.data(),static_cast(count)).starts_with("pipe:["))throw std::runtime_error("Fixture control requires anonymous pipes"); + auto cap=::fcntl(pair.first,F_GETPIPE_SZ); + if(cap<0||cap>65536)throw std::runtime_error("Fixture pipe capacity exceeds bound"); + } input_.assign(c.control_in); + output_.assign(c.control_out); + enabled_=true; + } asio::awaitable Control::send(json::object message) { + if(stopped_)co_return; + if(sending_) { + if(send_waiter_)throw std::runtime_error("Fixture reply queue exceeded"); + send_waiter_=true; + send_gate_.expires_at(std::chrono::steady_clock::time_point::max()); + boost::system::error_code ec; + co_await send_gate_.async_wait(asio::redirect_error(asio::use_awaitable,ec)); + send_waiter_=false; + } + if(stopped_)co_return; + sending_=true; + auto value=json::serialize(message); + if(value.size()>4092)throw std::runtime_error("Fixture reply exceeds bound"); + std::string wire(4,'\0'); + for(unsigned i=0;i<4;++i)wire[3-i]=static_cast((value.size()>>(i*8))&255); + wire+=value; + boost::system::error_code ec; + write_deadline_.expires_after(std::chrono::seconds(5)); + write_deadline_.async_wait([this](boost::system::error_code error){if(!error){boost::system::error_code ignored;output_.cancel(ignored);}}); + co_await asio::async_write(output_,asio::buffer(wire),asio::redirect_error(asio::use_awaitable,ec)); + write_deadline_.cancel(); + sending_=false; + send_gate_.cancel(); + if(ec)throw boost::system::system_error(ec); + } asio::awaitable Control::read_frame() { + auto owner=shared_from_this(); + std::array prefix { + }; + co_await asio::async_read(input_,asio::buffer(prefix),asio::use_awaitable); + std::size_t n=0; + for(auto c:prefix)n=(n<<8)|c; + if(!n||n>4092||commands_>=1024)throw std::runtime_error("Fixture control frame bound exceeded"); + std::string payload(n,'\0'); + co_await asio::async_read(input_,asio::buffer(payload),asio::use_awaitable); + Limits limits; + limits.request_bytes=4092; + limits.depth=8; + limits.nodes=64; + JsonScan scan; + scan.feed(payload,limits); + auto v=json::parse(payload); + if(!v.is_object()||v.as_object().size()>16)throw std::runtime_error("Invalid fixture control object"); + co_return std::move(v.as_object()); + + } asio::awaitable Control::run() { + if(!enabled_)co_return; + auto owner=shared_from_this(); + try { + while(!stopped_) { + auto o=co_await read_frame(); + ++commands_; + static std::setconst keys= { + "version","id","op","key_id","request_id","request_ordinal","phase","fence_id" + }; + for(auto const& x:o)if(!keys.contains(std::string(x.key())))throw std::runtime_error("Unknown fixture control key"); + if(integer(o,"id")<=0)throw std::runtime_error("Control IDs must be positive"); + auto id=static_cast(integer(o,"id")); + if(integer(o,"version")!=1||id<=last_id_)throw std::runtime_error("Invalid fixture control version/sequence"); + last_id_=id; + auto op=field(o,"op"); + json::object reply { + { + "version",1 + }, { + "id",id + } + }; + if(op=="READ_STATE") { + reply["kind"]="STATE"; + auto snapshot=state_.inspect(field(o,"request_id")); + reply["io_counter_epoch"]=counters_.epoch; + snapshot["active_requests"]=counters_.active_requests;snapshot["queued_requests"]=0; + snapshot["upstream_read_starts"]=counters_.upstream_read_starts;snapshot["upstream_read_completions"]=counters_.upstream_read_completions;snapshot["pending_upstream_reads"]=counters_.pending_upstream_reads; + snapshot["downstream_write_starts"]=counters_.downstream_write_starts;snapshot["downstream_write_completions"]=counters_.downstream_write_completions;snapshot["pending_downstream_writes"]=counters_.pending_downstream_writes; + snapshot["downstream_write_successes"]=counters_.downstream_write_successes;snapshot["requested_downstream_send_buffer_bytes"]=16384;snapshot["actual_downstream_send_buffer_bytes"]=counters_.actual_downstream_send_buffer_bytes; + snapshot["upstream_paused_for_downstream_write"]=counters_.pending_downstream_writes>0&&counters_.pending_upstream_reads==0; + reply["state"]=std::move(snapshot); + } else if(op=="REVOKE_KEY") { + if(field(o,"key_id")!=key_id_)throw std::runtime_error("Unknown synthetic key identity"); + + state_.revoke(); + reply["kind"]="REVOKED"; + reply["journal_sequence"]=state_.sequence(); + } else if(op=="ARM_FENCE") { + static std::setconst phases= { + "before_reserve_write","after_reserve_fsync","before_dispatch_intent_write","after_dispatch_intent_fsync","after_transport_started","after_terminal_intent_fsync","after_terminal_write","after_settlement_fsync","after_snapshot_rename","after_directory_fsync","after_journal_reset" + }; + if(armed_||!phases.contains(field(o,"phase")))throw std::runtime_error("Invalid or overlapping fixture fence"); + armed_=true; + held_=false; + phase_=field(o,"phase"); + request_id_=field(o,"request_id"); + if(!request_id_.empty()&&o.contains("request_ordinal"))throw std::runtime_error("A fence has exactly one request selector"); + ordinal_=request_id_.empty()?static_cast(integer(o,"request_ordinal",1)):0; + if(request_id_.size()>64||(!ordinal_&&request_id_.empty()))throw std::runtime_error("Invalid fence request selector"); + fence_id_=id; + fence_timer_.expires_after(std::chrono::seconds(10)); + auto expected=id; + fence_timer_.async_wait([this,expected](boost::system::error_code error){if(!error&&armed_&&!held_&&expected==fence_id_)armed_=false;}); + reply["kind"]="FENCE_ARMED"; + reply["fence_id"]=id; + } else if(op=="RELEASE_FENCE") { + if(!held_||static_cast(integer(o,"fence_id"))!=fence_id_)throw std::runtime_error("Mismatched fixture fence release"); + armed_=false; + held_=false; + fence_timer_.cancel(); + reply["kind"]="FENCE_RELEASED"; + reply["fence_id"]=fence_id_; + } else throw std::runtime_error("Unknown fixture control opcode"); + co_await send(std::move(reply)); + } + } catch(std::exception const&) { + if(!stopped_) { + stop(); + stop_(); + } + } + } asio::awaitable Control::fence(std::uint64_t ordinal,std::string_view phase,std::string_view request) { + if(!enabled_||!armed_||phase!=phase_||(request_id_.empty()?ordinal!=ordinal_:request!=request_id_))co_return; + if(fence_timer_.expiry()<=std::chrono::steady_clock::now()) { + armed_=false; + throw Failure(499,"Fixture fence expired","request_cancelled"); + } + held_=true; + fence_timer_.expires_after(std::chrono::seconds(5)); + fence_timer_.async_wait([this](boost::system::error_code error){if(!error&&held_){stop();stop_();}}); + json::object event { + { + "version",1 + }, { + "kind","FENCE_REACHED" + }, { + "fence_id",fence_id_ + }, { + "phase",phase_ + }, { + "request_id",std::string(request) + }, { + "journal_sequence",state_.sequence() + } + }; + co_await send(std::move(event)); + boost::system::error_code ec; + co_await fence_timer_.async_wait(asio::redirect_error(asio::use_awaitable,ec)); + if(held_||stopped_) { + held_=false; + armed_=false; + throw Failure(499,"Fixture fence owner/deadline ended","request_cancelled"); + } + } void Control::stop() { + if(stopped_)return; + stopped_=true; + boost::system::error_code ec; + input_.close(ec); + output_.close(ec); + fence_timer_.cancel(); + send_gate_.cancel(); + write_deadline_.cancel(); + } +} diff --git a/cpp/src/fixture_state.cpp b/cpp/src/fixture_state.cpp new file mode 100644 index 000000000..db80fe453 --- /dev/null +++ b/cpp/src/fixture_state.cpp @@ -0,0 +1,338 @@ +#include "pilot/state.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace pilot { + namespace { + void check(bool value,char const* message) { + if(!value)throw std::runtime_error(message); + } void write_all(int fd,void const* data,std::size_t size) { + auto p=static_cast(data); + while(size) { + auto n=::write(fd,p,size); + if(n<0&&errno==EINTR)continue; + check(n>0,"Fixture state write failed"); + p+=n; + size-=static_cast(n); + } + } void sync_fd(int fd) { + auto begin=std::chrono::steady_clock::now(); + check(::fsync(fd)==0,"Fixture state fsync failed"); + auto us=std::chrono::duration_cast(std::chrono::steady_clock::now()-begin).count(); + std::cerr<<"{\"state_fsync_us\":"<=0&&static_cast(metadata.st_size)<=cap,"Fixture state exceeds disk bound"); + std::string result; + std::array buffer{}; + for(;;){auto n=::read(fd,buffer.data(),buffer.size());if(n<0&&errno==EINTR)continue;check(n>=0,"Fixture state read failed");if(n==0)break;result.append(buffer.data(),static_cast(n));check(result.size()<=cap,"Fixture state exceeds disk bound");} + return result; + } + } State::State(Config const& c):config_(c),root_(std::filesystem::canonical(c.state_directory)) { + auto path=root_.string(); + check((path.find("/.data/")==std::string::npos||path.find("/.data/cpp-tmp/")!=std::string::npos||path.find("/.data/cpp-evidence/")!=std::string::npos)&&path.find("kv.sqlite")==std::string::npos&&path.find("kv-bun.sqlite")==std::string::npos,"Production state path is prohibited"); + check(std::filesystem::exists(root_)&&std::filesystem::is_directory(root_)&&!std::filesystem::is_symlink(root_),"Create a private fixture state directory before launch"); + struct stat meta { + }; + check(::lstat(root_.c_str(),&meta)==0&&meta.st_uid==::getuid()&&(meta.st_mode&077)==0,"Fixture state directory must be owned and mode700"); + lock_=::open((root_/"owner.lock").c_str(),O_CREAT|O_RDWR|O_CLOEXEC|O_NOFOLLOW,0600); + check(lock_>=0&&::flock(lock_,LOCK_EX|LOCK_NB)==0,"Fixture state has another writer"); + data_= { + { + "magic","uos-cpp-fixture-state-v1" + }, { + "seq",0 + }, { + "revoked",false + }, { + "token_hash",c.token_hash + }, { + "key_id",c.key_id + }, { + "limit",c.quota_limit + }, { + "window_start_ms",now_ms() + }, { + "window_reset_ms",now_ms()+3600000 + }, { + "committed",0 + }, { + "reserved",0 + }, { + "quota_transactions",0 + }, { + "quota_mutations",0 + }, { + "rows",json::object { + } + } + }; + load(); + check(field(data_,"token_hash")==c.token_hash&&field(data_,"key_id")==c.key_id,"Fixture configuration identity does not match retained state"); + journal_=::open((root_/"journal.bin").c_str(),O_CREAT|O_RDWR|O_APPEND|O_CLOEXEC|O_NOFOLLOW,0600); + check(journal_>=0,"Fixture journal unavailable"); + if(!std::filesystem::exists(root_/"snapshot.json")) { + snapshot_publish(); + snapshot_sync_directory(); + snapshot_reset_journal(); + } std::vector settle_ids; + for(auto const& e:data_["rows"].as_object())if(e.value().is_object()&&!member(e.value().as_object(),"terminal").is_null()&&!flag(e.value().as_object(),"settled"))settle_ids.emplace_back(e.key()); + for(auto const& id:settle_ids)settle(id); + } OwnedFd::~OwnedFd() { if(value>=0) ::close(value); } + State::~State()=default; + void State::load() { + auto snap=read_file(root_/"snapshot.json",8*1024*1024); + if(!snap.empty()) { + auto v=json::parse(snap,json::storage_ptr(&resource_)); + check(v.is_object()&&field(v.as_object(),"magic")=="uos-cpp-fixture-state-v1","Wrong fixture snapshot codec"); + data_=std::move(v.as_object()); + seq_=static_cast(integer(data_,"seq")); + } auto wire=read_file(root_/"journal.bin",8*1024*1024); + std::size_t pos=0; + while(pos=4,"Incomplete final fixture frame retained; explicit recovery required"); + std::uint32_t n=0; + for(unsigned k=0;k<4;++k)n=(n<<8)|static_cast(wire[pos+k]); + check(n>64&&n<=2048&&wire.size()-pos-4>=n,"Incomplete or oversized fixture frame"); + auto payload=std::string_view(wire).substr(pos+4,n-64); + auto digest=std::string_view(wire).substr(pos+4+n-64,64); + check(sha256(payload)==digest,"Fixture journal checksum mismatch"); + auto v=json::parse(payload); + check(v.is_object(),"Invalid fixture journal object"); + auto const& o=v.as_object(); + check(field(o,"magic")=="uos-cpp-fixture-journal-v1"&&(field(o,"op")=="row"||field(o,"op")=="policy")&&member(o,"quota").is_bool()&&member(o,"value").is_object()&&integer(o,"seq")>0,"Invalid fixture journal schema"); + auto seq=static_cast(integer(o,"seq")); + if(seq>seq_) { + check(seq==seq_+1,"Fixture journal sequence gap"); + apply(field(o,"op"),member(o,"value").as_object(),flag(o,"quota")); + seq_=seq; + data_["seq"]=seq_; + } + pos+=4+n; + ++frames_; + } + journal_bytes_=wire.size(); + } void State::apply(std::string const& op,json::object const& v,bool quota) { + if(op=="policy") { + data_["revoked"]=flag(v,"revoked"); + return; + } + auto id=field(v,"request_id"); + check(!id.empty(),"Missing internal state identity"); + auto& rows=data_["rows"].as_object(); + auto old=rows.if_contains(id); + auto old_state=old&&old->is_object()?field(old->as_object(),"state"):""; + auto next=field(v,"state"); + auto reserved=integer(data_,"reserved"),committed=integer(data_,"committed"); + if(old_state=="reserved")--reserved; + if(old_state=="dispatched")--committed; + if(next=="reserved")++reserved; + if(next=="dispatched")++committed; + check(reserved>=0&&committed>=0,"Fixture aggregate underflow"); + data_["reserved"]=reserved; + data_["committed"]=committed; + rows[id]=v; + if(quota) { + data_["quota_transactions"]=integer(data_,"quota_transactions")+1; + data_["quota_mutations"]=integer(data_,"quota_mutations")+2; + } + } void State::append(std::string const& op,json::object const& v,bool quota) { + if(unavailable_) throw std::runtime_error("Fixture state is unavailable"); + struct FailureGuard { bool& value; bool complete=false; ~FailureGuard(){ if(!complete)value=true; } } guard{unavailable_}; + check(frames_<4096,"Fixture journal frame limit reached"); + check(op=="policy"||data_["rows"].as_object().size()<16384||data_["rows"].as_object().contains(field(v,"request_id")),"Fixture record limit reached"); + json::object entry { + { + "magic","uos-cpp-fixture-journal-v1" + }, { + "seq",seq_+1 + }, { + "op",op + }, { + "quota",quota + }, { + "value",v + } + }; + auto payload=json::serialize(entry); + auto n=payload.size()+64; + check(n<=2048&&journal_bytes_+n+4<=8*1024*1024,"Fixture journal byte limit reached"); + std::array prefix { + }; + for(unsigned k=0;k<4;++k)prefix[3-k]=static_cast((n>>(8*k))&255); + write_all(journal_,prefix.data(),prefix.size()); + write_all(journal_,payload.data(),payload.size()); + auto hash=sha256(payload); + write_all(journal_,hash.data(),hash.size()); + sync_fd(journal_); + apply(op,v,quota); + ++seq_; + data_["seq"]=seq_; + journal_bytes_+=n+4; + ++frames_; + guard.complete=true; + } bool State::authenticate(std::string_view token) const { + auto hash=sha256(token); + auto expected=field(data_,"token_hash"); + return !flag(data_,"revoked")&&hash.size()==expected.size()&&CRYPTO_memcmp(hash.data(),expected.data(),hash.size())==0; + } bool State::reserve(std::string const& id) { + auto& rows=data_["rows"].as_object(); + if(rows.contains(id))return field(rows[id].as_object(),"state")=="reserved"; + std::vector expired; + for(auto const& e:rows)if(field(e.value().as_object(),"state")=="reserved"&&integer(e.value().as_object(),"lease_expires_at_ms")<=now_ms())expired.emplace_back(e.key()); + for(auto const& key:expired)release(key); + if(flag(data_,"revoked"))throw Failure(401,"Unauthorized","invalid_api_key"); + if(integer(data_,"committed")+integer(data_,"reserved")>=integer(data_,"limit"))return false; + json::object row { + { + "request_id",id + }, { + "state","reserved" + }, { + "reserved_at_ms",now_ms() + }, { + "lease_expires_at_ms",now_ms()+300000 + }, { + "provider",nullptr + }, { + "terminal",nullptr + }, { + "usage",nullptr + }, { + "delivery","unobserved" + }, { + "settled",false + } + }; + append("row",row,true); + return true; + } void State::dispatch(std::string const& id) { + auto const* p=data_["rows"].as_object().if_contains(id); + if(!p||field(p->as_object(),"state")!="reserved")throw Failure(429,"Usage limit exceeded","rate_limit_exceeded"); + auto row=p->as_object(); + row["state"]="dispatched"; + row["provider"]="deepseek"; + row["dispatched_at_ms"]=now_ms(); + append("row",row,true); + } void State::release(std::string const& id,bool compensate) { + auto const* p=data_["rows"].as_object().if_contains(id); + if(!p)return; + auto row=p->as_object(); + auto s=field(row,"state"); + if(s!="reserved"&&!(compensate&&s=="dispatched"))return; + row["state"]="released"; + row["release_reason"]=compensate?"cancelled_before_transport":"route_completed_without_provider_dispatch"; + append("row",row,true); + } void State::terminal(std::string const& id,std::string const& kind,json::value const& usage) { + auto p=data_["rows"].as_object().if_contains(id); + if(!p||!member(p->as_object(),"terminal").is_null())return; + auto row=p->as_object(); + row["terminal"]=kind; + row["usage"]=usage; + append("row",row,false); + } void State::delivered(std::string const& id,bool success) { + auto p=data_["rows"].as_object().if_contains(id); + if(!p||field(p->as_object(),"delivery")!="unobserved")return; + auto row=p->as_object(); + row["delivery"]=success?"delivered":"interrupted"; + append("row",row,false); + } void State::settle(std::string const& id) { + auto p=data_["rows"].as_object().if_contains(id); + if(!p||flag(p->as_object(),"settled"))return; + auto row=p->as_object(); + row["settled"]=true; + append("row",row,false); + } void State::revoke() { + append("policy",json::object { + { + "revoked",true + } + },false); + } json::object State::inspect(std::string const& id) const { + json::object r { + { + "fixture_authority",true + }, { + "journal_sequence",seq_ + }, { + "revoked",flag(data_,"revoked") + }, { + "committed_requests",integer(data_,"committed") + }, { + "reserved_requests",integer(data_,"reserved") + }, { + "quota_transactions",integer(data_,"quota_transactions") + }, { + "quota_mutations",integer(data_,"quota_mutations") + }, { + "record_count",data_.at("rows").as_object().size() + }, { + "paid_fallback","not_applicable" + }, { + "kernel_quota","not_applicable" + } + }; + std::size_t settled=0,unsettled=0; + for(auto const& row:data_.at("rows").as_object()){if(flag(row.value().as_object(),"settled"))++settled;else ++unsettled;} + r["logical_dispatch_count"]=integer(data_,"committed");r["settlement_count"]=settled;r["unsettled_count"]=unsettled; + if(!id.empty()) { + auto p=data_.at("rows").as_object().if_contains(id); + r["request"]=p?*p:json::value(nullptr); + } + return r; + } bool State::needs_snapshot() const { + return frames_>=1024||journal_bytes_>=2*1024*1024; + } void State::snapshot_publish() { + auto path=root_/"snapshot.tmp"; + int fd=::open(path.c_str(),O_CREAT|O_TRUNC|O_WRONLY|O_CLOEXEC|O_NOFOLLOW,0600); + check(fd>=0,"Snapshot temporary unavailable"); + try { + json::serializer sr; + sr.reset(&data_); + std::array buffer { + }; + std::size_t total=0; + while(!sr.done()) { + auto part=sr.read(buffer.data(),buffer.size()); + total+=part.size(); + check(total<=8*1024*1024,"Snapshot cap exceeded"); + write_all(fd,part.data(),part.size()); + } + sync_fd(fd); + ::close(fd); + fd=-1; + check(::rename(path.c_str(),(root_/"snapshot.json").c_str())==0,"Snapshot rename failed"); + } + catch(...) { + if(fd>=0)::close(fd); + throw; + } + } void State::snapshot_sync_directory() { + int fd=::open(root_.c_str(),O_RDONLY|O_DIRECTORY|O_CLOEXEC); + check(fd>=0,"Fixture directory unavailable"); + try { + sync_fd(fd); + ::close(fd); + } + catch(...) { + ::close(fd); + throw; + } + } void State::snapshot_reset_journal() { + check(::ftruncate(journal_,0)==0,"Journal reset failed"); + sync_fd(journal_); + frames_=0; + journal_bytes_=0; + } +} diff --git a/cpp/src/main.cpp b/cpp/src/main.cpp new file mode 100644 index 000000000..377f183cd --- /dev/null +++ b/cpp/src/main.cpp @@ -0,0 +1,48 @@ +#include "pilot/network.hpp" +#include +#include +#include +#include +#include +int main(int argc,char** argv) { + std::signal(SIGPIPE,SIG_IGN); + try { + if(argc!=3||std::string_view(argv[1])!="--config")throw std::runtime_error("usage: uos_cpp_pilot --config "); + auto config=pilot::read_config(argv[2]); + boost::asio::io_context io(1); + pilot::Runtime runtime(io,std::move(config)); + boost::asio::signal_set signals(io,SIGINT,SIGTERM); + runtime.on_drained = [&]{ boost::system::error_code error; signals.cancel(error); }; + signals.async_wait([&](boost::system::error_code ec,int) { + if(!ec)runtime.stop(); + }); + boost::asio::co_spawn(io,runtime.accept_loop(),[&](std::exception_ptr e) { + if(e) { + std::cerr<<"Listener failed\n";runtime.stop(); + } + }); + if(runtime.control->enabled()) { + auto owner=runtime.control; + boost::asio::co_spawn(io,owner->run(),[owner](std::exception_ptr error){if(error)owner->stop();}); + } + std::cout< +namespace pilot { + namespace { + std::u16string units(std::string_view input) { + std::u16string result; + for(std::size_t i=0;i(input[i++]); + std::uint32_t value=first; unsigned count=0; + if(first>=0xc2&&first<=0xdf){value=first&31;count=1;} + else if(first>=0xe0&&first<=0xef){value=first&15;count=2;} + else if(first>=0xf0&&first<=0xf4){value=first&7;count=3;} + else if(first>=0x80)throw std::runtime_error("Invalid decoded UTF8"); + if(i+count>input.size())throw std::runtime_error("Truncated decoded UTF8"); + while(count--){auto c=static_cast(input[i++]);if((c&0xc0)!=0x80)throw std::runtime_error("Invalid decoded UTF8");value=(value<<6)|(c&63);} + if(value<=0xffff)result.push_back(static_cast(value)); + else{value-=0x10000;result.push_back(static_cast(0xd800+(value>>10)));result.push_back(static_cast(0xdc00+(value&1023)));} + } + return result; + } + std::string bytes(std::u16string const& input) { + std::string result; + for(std::size_t i=0;i=0xd800&&value<=0xdbff&&i+1=0xdc00&&input[i+1]<=0xdfff){value=0x10000+((value-0xd800)<<10)+(input[++i]-0xdc00);} + else if(value>=0xd800&&value<=0xdfff)value=0xfffd; + if(value<0x80)result+=static_cast(value); + else if(value<0x800){result+=static_cast(0xc0|(value>>6));result+=static_cast(0x80|(value&63));} + else if(value<0x10000){result+=static_cast(0xe0|(value>>12));result+=static_cast(0x80|((value>>6)&63));result+=static_cast(0x80|(value&63));} + else{result+=static_cast(0xf0|(value>>18));result+=static_cast(0x80|((value>>12)&63));result+=static_cast(0x80|((value>>6)&63));result+=static_cast(0x80|(value&63));} + } + return result; + } + std::u16string head(std::u16string const& input,std::size_t length) { + auto r=input.substr(0,length); + if(r.size()=0xd800&&r.back()<=0xdbff)r.pop_back(); + return r; + } bool space(char16_t c) { + return c==9||c==10||c==11||c==12||c==13||c==32||c==0xa0||c==0xfeff||c==0x1680||(c>=0x2000&&c<=0x200a)||c==0x2028||c==0x2029||c==0x202f||c==0x205f||c==0x3000; + } + } std::string bounded_payload(std::string value,std::string const& path,bool reject,bool arguments) { + constexpr std::size_t limit=65536; + if(value.size()<=limit)return value; + if(reject)throw Failure(400,"input item "+path+" carries "+std::to_string(value.size())+" bytes; this route forwards at most 65536 bytes per message under deepseek-forwarded-payload/v2, and the request set truncation 'disabled', so the gateway will not reduce it. Reduce the payload or send truncation 'auto' to allow the declared reduction.","context_length_exceeded",path); + if(arguments) { + auto count=static_cast(value.size()); + return json::serialize(json::object { + { + "gateway_elision",json::object { + { + "policy","deepseek-forwarded-payload/v2" + }, { + "kind","tool_arguments" + }, { + "originalBytes",count + }, { + "omittedBytes",count + }, { + "message","[gateway: deepseek-forwarded-payload/v2 elided "+std::to_string(count)+" bytes omitted; the model did not receive these tool-call arguments; this route forwards at most 65536 bytes per message]" + } + } + } + }); + } auto original=value.size(); + auto all=units(value); + auto prefix=head(all,limit); + while(!prefix.empty()&&bytes(prefix).size()>limit)prefix=head(prefix,static_cast(static_cast(prefix.size())*0.9)); + for(;;) { + auto encoded=bytes(prefix); + auto marker="\n[gateway: deepseek-forwarded-payload/v2 elided "+std::to_string(original-encoded.size())+" bytes omitted; the model did not receive the elided bytes; this route forwards at most 65536 bytes per message because the provider counts forwarded payloads as text tokens]"; + if(prefix.empty()||encoded.size()+marker.size()<=limit)return encoded+marker; + prefix=head(prefix,static_cast(static_cast(prefix.size())*0.9)); + } + } std::size_t estimate_tokens(std::string_view input) { + auto s=units(input); + double tokens=0; + for(std::size_t i=0;i=u'0'&&c<=u'9'; + bool alpha=(c>=u'A'&&c<=u'Z')||(c>=u'a'&&c<=u'z'); + if(digit||alpha) { + auto begin=i++; + while(i=u'0'&&s[i]<=u'9'):((s[i]>=u'A'&&s[i]<=u'Z')||(s[i]>=u'a'&&s[i]<=u'z'))))++i; + auto n=i-begin; + tokens+=digit?static_cast(n)/2.0:1.0+static_cast((n-1)/6); + } + else { + tokens+=0.9; + ++i; + } + } return static_cast(std::ceil(tokens)); + } +bool nonblank_utf16(std::string_view input){auto value=units(input);for(auto unit:value)if(!space(unit))return true;return false;} +} diff --git a/cpp/src/project_input.cpp b/cpp/src/project_input.cpp new file mode 100644 index 000000000..18a51c0f1 --- /dev/null +++ b/cpp/src/project_input.cpp @@ -0,0 +1,230 @@ +#include "pilot/core.hpp" +#include +namespace pilot { + namespace { + std::string reasoning(json::object const& item) { + std::string result; + for(auto key: { + "summary","content" + })if(auto parts=member(item,key).if_array())for(auto const& part:*parts)if(part.is_object())result+=field(part.as_object(),"text"); + return result+field(item,"text"); + } json::value content(json::value const& raw,std::string const& role,bool reject,std::string const& path,bool exempt,json::storage_ptr sp) { + if(raw.is_string())return json::value(bounded_payload(text(raw),path+".content",reject)); + if(!raw.is_array())throw Failure(400,"input.content must be a string or an array","invalid_request_error","input.content"); + std::string joined; + json::array images(sp); + for(auto const& p:raw.as_array()) { + if(!p.is_object())throw Failure(400,"input.content items must be objects"); + auto const& o=p.as_object(); + auto type=field(o,"type"); + if(type=="input_text"||type=="output_text"||type=="text") { + if(!member(o,"text").is_string())throw Failure(400,"input.content text must be a string"); + joined+=field(o,"text"); + } else if(type=="refusal"&&role=="assistant") { + if(!member(o,"refusal").is_string())throw Failure(400,"input.content refusal must be a string"); + joined+=field(o,"refusal"); + } else if(type=="input_image") { + auto url=field(o,"image_url",field(o,"file_url")); + if(url.empty())throw Failure(400,"input_image requires image_url"); + if(url.size()>65536) { + if(reject)bounded_payload(url,path,reject); + joined+="[gateway: deepseek-forwarded-payload/v2 image omitted ("+std::to_string(url.size())+" bytes); the model did not receive this image; this route forwards at most 65536 bytes per message because the provider counts forwarded payloads as text tokens]"; + } + else { + json::object image { + { + "url",url + } + }; + if(member(o,"detail").is_string())image["detail"]=member(o,"detail"); + images.push_back(json::object { + { + "type","image_url" + }, { + "image_url",std::move(image) + } + }); + } + } else throw Failure(400,"input.content type '"+type+"' is not supported","invalid_request_error","input.content.type"); + } if(images.empty())return json::value(exempt?joined:bounded_payload(joined,path+".content",reject)); + if(!joined.empty())images.insert(images.begin(),json::object { + { + "type","text" + }, { + "text",joined + } + }); + std::size_t total=joined.size(); + for(auto const& p:images)if(field(p.as_object(),"type")=="image_url")total+=field(member(p.as_object(),"image_url").as_object(),"url").size(); + if(total>65536) { + if(reject)throw Failure(400,"Image message exceeds forwarded payload bound","context_length_exceeded",path); + return json::value(bounded_payload(json::serialize(images),path,reject)); + } return images; + } bool summary_item(json::value const& raw,bool& seen) { + if(raw.is_string()) { + if(text(raw).find(summary_prefix)!=std::string::npos)throw Failure(400,"Summary prefix outside exact user input_text envelope","context_length_exceeded","input"); + return false; + } if(!raw.is_object())return false; + auto const& o=raw.as_object(); + auto const& c=member(o,"content"); + bool prefixed=false; + if(c.is_string())prefixed=text(c).starts_with(summary_prefix); + else if(c.is_array())for(auto const& p:c.as_array())if(p.is_object()&&field(p.as_object(),"text").starts_with(summary_prefix))prefixed=true; + if(!prefixed)return false; + bool valid=field(o,"role")=="user"&&field(o,"type","message")=="message"&&c.is_array()&&c.as_array().size()==1; + if(valid) { + auto const& p=c.as_array()[0]; + valid=p.is_object()&&field(p.as_object(),"type")=="input_text"&&field(p.as_object(),"text").starts_with(std::string(summary_prefix)+"\n"); + } if(!valid||seen)throw Failure(400,"Invalid or repeated codex-compaction-summary/v1 envelope","context_length_exceeded","input"); + seen=true; + return true; + } + } void project_input(json::object const& raw,json::array& messages,bool reject) { + auto sp=messages.storage(); + auto instructions=field(raw,"instructions"); + if(!instructions.empty())messages.push_back(json::object { + { + "role","system" + }, { + "content",bounded_payload(instructions,"instructions",reject) + } + }); + auto const& input=member(raw,"input"); + if(input.is_null())return; + if(input.is_string()) { + bool seen=false; + summary_item(input,seen); + if(!text(input).empty())messages.push_back(json::object { + { + "role","user" + }, { + "content",bounded_payload(text(input),"input",reject) + } + }); + return; + } if(!input.is_array())throw Failure(400,"input must be a string or an array","invalid_request_error","input"); + if(input.as_array().size()>4096)throw Failure(400,"Input item bound exceeded"); + std::string pending; + bool seen_summary=false; + std::size_t index=0; + for(auto const& item:input.as_array()) { + auto path="input["+std::to_string(index++)+"]"; + bool exempt=summary_item(item,seen_summary); + if(item.is_string()) { + messages.push_back(json::object { + { + "role","user" + }, { + "content",bounded_payload(text(item),path,reject) + } + }); + pending.clear(); + continue; + } + if(!item.is_object())throw Failure(400,"input items must be objects"); + auto const& o=item.as_object(); + auto type=field(o,"type","message"); + if(type=="reasoning") { + pending+=bounded_payload(reasoning(o),path,reject); + continue; + } if(type=="function_call"||type=="custom_tool_call") { + auto id=field(o,"call_id",field(o,"id")); + auto name=field(o,"name"); + if(id.empty()||name.empty())throw Failure(400,type+" items require call_id and name","invalid_request_error","input"); + auto args=type=="custom_tool_call"?json::serialize(json::object { + { + "input",field(o,"input") + } + }):field(o,"arguments","{}"); + json::object call { + { + "id",id + }, { + "type","function" + }, { + "function",json::object { + { + "name",name + }, { + "arguments",bounded_payload(args,path+".arguments",reject,true) + } + } + } + }; + if(!messages.empty()&&messages.back().is_object()&&field(messages.back().as_object(),"role")=="assistant"&&member(messages.back().as_object(),"content").is_null()&&member(messages.back().as_object(),"tool_calls").is_array()) { + auto& last=messages.back().as_object(); + last["tool_calls"].as_array().push_back(call); + if(!pending.empty())last["reasoning_content"]=field(last,"reasoning_content")+pending; + } else { + json::object turn { + { + "role","assistant" + }, { + "content",nullptr + }, { + "tool_calls",json::array { + call + } + } + }; + if(!pending.empty())turn["reasoning_content"]=pending; + messages.push_back(turn); + } + pending.clear(); + continue; + } if(type=="function_call_output"||type=="custom_tool_call_output") { + auto id=field(o,"call_id"); + if(id.empty())continue; + auto const& out=member(o,"output"); + auto value=out.is_string()?text(out):json::serialize(out.is_null()?json::value(""):out); + messages.push_back(json::object { + { + "role","tool" + }, { + "tool_call_id",id + }, { + "content",bounded_payload(value,path+".output",reject) + } + }); + continue; + } if(type=="agent_message") { + std::string value="[agent message] "+field(o,"author","agent")+" -> "+field(o,"recipient","root")+":\n"; + std::size_t unreadable=0; + if(auto parts=member(o,"content").if_array())for(auto const& part:*parts)if(part.is_object()) { + auto t=field(part.as_object(),"text",field(part.as_object(),"encrypted_content")); + if(t.starts_with("gAAAAA")&&t.size()>=100) { + ++unreadable; + continue; + } + if(value.back()!='\n')value+='\n'; + value+=t; + } + if(unreadable)value+="\n[gateway: "+std::to_string(unreadable)+" sealed agent-message part(s) were not readable and omitted]"; + messages.push_back(json::object { + { + "role","user" + }, { + "content",bounded_payload(value,path,reject) + } + }); + pending.clear(); + continue; + } if(type!="message")throw Failure(400,"input item type '"+type+"' is not supported","invalid_request_error","input.type"); + auto role=field(o,"role","user"); + if(role!="user"&&role!="assistant"&&role!="developer"&&role!="system")throw Failure(400,"Unsupported input role","invalid_request_error","input.role"); + if(role=="user")pending.clear(); + json::object message { + { + "role",role=="developer"?"system":role + }, { + "content",content(member(o,"content"),role,reject,path,exempt,sp) + } + }; + if(role=="assistant"&&!pending.empty()) { + message["reasoning_content"]=pending; + pending.clear(); + } + messages.push_back(message); + } + } +} diff --git a/cpp/src/project_tools.cpp b/cpp/src/project_tools.cpp new file mode 100644 index 000000000..32fd3eaac --- /dev/null +++ b/cpp/src/project_tools.cpp @@ -0,0 +1,226 @@ +#include "pilot/core.hpp" +#include +#include +namespace pilot { + namespace { + std::string unique(std::set& used,std::string name) { + auto r=name.substr(0,128); + for(unsigned n=2;used.contains(r);++n) { + auto suffix="_"+std::to_string(n); + r=name.substr(0,128-suffix.size())+suffix; + } + used.insert(r); + return r; + } std::string mapped(Prepared const& r,std::string name,std::string const& space) { + for(auto const& n:r.names)if(n.name==name&&(n.space?*n.space:std::string{})==space)return n.chat; + return name; + } + } void project_tools(json::object const& raw,Prepared& r) { + std::set used; + json::array tools(r.body.storage()); + auto add=[&](json::object const& o,std::shared_ptr space,bool custom) { + auto name=field(o,"name"); + if(name.empty())throw Failure(400,"function tools require a name","invalid_request_error","tools.name"); + auto prefer=space&&used.contains(name)?*space+"_"+name:name; + auto chat=unique(used,prefer); + r.names.push_back( { + chat,name,space,custom + }); + json::object fn { + { + "name",chat + } + }; + if(member(o,"description").is_string())fn["description"]=member(o,"description"); + if(custom)fn["parameters"]=json::object { + { + "type","object" + }, { + "properties",json::object { + { + "input",json::object { + { + "type","string" + }, { + "description","Freeform input for the tool." + } + } + } + } + }, { + "required",json::array { + "input" + } + }, { + "additionalProperties",false + } + }; + else { + if(!member(o,"parameters").is_null())fn["parameters"]=member(o,"parameters"); + if(member(o,"strict").is_bool())fn["strict"]=member(o,"strict"); + } + tools.push_back(json::object { + { + "type","function" + }, { + "function",fn + } + }); + if(tools.size()>256)throw Failure(400,"Tool definition bound exceeded"); + }; + if(!member(raw,"tools").is_null()) { + if(!member(raw,"tools").is_array())throw Failure(400,"tools must be an array","invalid_request_error","tools"); + for(auto const& v:member(raw,"tools").as_array()) { + if(!v.is_object())throw Failure(400,"tools must contain objects"); + auto const& o=v.as_object(); + auto type=field(o,"type"); + if(type=="namespace") { + auto namespace_owner=std::make_shared(field(o,"name")); + auto const& nested=member(o,"tools"); + if(!nested.is_array())throw Failure(400,"namespace tools must nest a tools array"); + for(auto const& t:nested.as_array()) { + if(!t.is_object())throw Failure(400,"namespace tools must contain objects"); + if(field(t.as_object(),"type")=="function")add(t.as_object(),namespace_owner,false); + } + } + else if(type=="custom")add(o,nullptr,true); + else if(member(o,"function").is_object())add(member(o,"function").as_object(),nullptr,false); + else if(type=="function")add(o,nullptr,false); + } + } if(!tools.empty())r.body["tools"]=tools; + auto const& choice=member(raw,"tool_choice"); + if(choice.is_string()) { + auto v=text(choice); + if(v!="none"&&v!="auto"&&v!="required")throw Failure(400,"Unsupported tool_choice","invalid_request_error","tool_choice"); + r.body["tool_choice"]=v; + } + else if(choice.is_object()) { + auto const& o=choice.as_object(); + auto const& f=member(o,"function"); + auto name=field(o,"name",f.is_object()?field(f.as_object(),"name"):""); + auto space=field(o,"namespace",f.is_object()?field(f.as_object(),"namespace"):""); + if(name.empty())throw Failure(400,"a named tool_choice requires a name"); + r.body["tool_choice"]=json::object { + { + "type","function" + }, { + "function",json::object { + { + "name",mapped(r,name,space) + } + } + } + }; + } + else if(!choice.is_null())throw Failure(400,"tool_choice must be a string or object"); + bool thinking=field(r.body,"reasoning_effort","high")!="none"; + if(thinking&&(field(r.body,"tool_choice")=="required"||member(r.body,"tool_choice").is_object()))throw Failure(400,"tool_choice is not supported while reasoning.effort keeps DeepSeek thinking mode active; set reasoning.effort to 'none' or use tool_choice 'auto'","invalid_request_error","tool_choice"); + if(!tools.empty()) { + auto& messages=r.body["messages"].as_array(); + if(field(raw,"tool_choice")!="none") { + std::string reminder="When tools are available, a progress update does not complete a requested action. If required work remains and you can perform it, continue with the next appropriate tool call instead of ending with a status message. Provide a final answer when the requested work is complete, or when you need user input or are blocked. Never claim a tool action has been done unless its result is in the conversation."; + bool found=false; + for(auto& v:messages)if(field(v.as_object(),"role")=="system"&&member(v.as_object(),"content").is_string()) { + v.as_object()["content"]=bounded_payload(field(v.as_object(),"content")+"\n\n"+reminder,"instructions",field(raw,"truncation")=="disabled"); + found=true; + break; + } + if(!found)messages.insert(messages.begin(),json::object { + { + "role","system" + }, { + "content",reminder + } + }); + } + std::size_t tail=0; + for(std::size_t i=0;i const allowed= { + "background","context_management","conversation","include","input","instructions","max_output_tokens","max_tool_calls","metadata","model","moderation","parallel_tool_calls","previous_response_id","prompt","prompt_cache_key","prompt_cache_options","prompt_cache_retention","reasoning","safety_identifier","service_tier","store","stream","stream_options","temperature","text","tool_choice","tools","top_logprobs","top_p","truncation","user","client_metadata" + }; + for(auto const& x:raw)if(!allowed.contains(std::string(x.key())))throw Failure(400,"Unrecognized request argument supplied: "+std::string(x.key())); + Prepared r(sp); + r.model=field(raw,"model"); + if(r.model!="deepseek-flash"&&r.model!="deepseek-v4-flash")throw Failure(400,"The requested model is not available","model_not_found","model"); + if(!member(raw,"stream").is_null()&&!member(raw,"stream").is_bool())throw Failure(400,"stream must be a boolean","invalid_request_error","stream"); + r.stream=flag(raw,"stream"); + if(!r.stream)throw Failure(400,"This pilot requires stream: true; buffered Responses are unsupported.","invalid_request_error","stream"); + auto trunc=field(raw,"truncation","auto"); + if(trunc!="auto"&&trunc!="disabled")throw Failure(400,"Unsupported truncation","invalid_request_error","truncation"); + for(auto key: { + "max_output_tokens","max_tool_calls" + })if(!member(raw,key).is_null()&&(integer(raw,key,-1)<=0))throw Failure(400,std::string(key)+" must be a positive integer","invalid_request_error",key); + if(raw.contains("parallel_tool_calls")&&!member(raw,"parallel_tool_calls").is_bool())throw Failure(400,"parallel_tool_calls must be a boolean","invalid_request_error","parallel_tool_calls"); + if(!member(raw,"client_metadata").is_null()) { + if(!member(raw,"client_metadata").is_object())throw Failure(400,"client_metadata must be an object with string values"); + for(auto const& x:member(raw,"client_metadata").as_object())if(!x.value().is_string())throw Failure(400,"client_metadata must contain string values"); + } if(!member(raw,"prompt_cache_options").is_null())throw Failure(400,"prompt_cache_options is not supported by this model","invalid_request_error","prompt_cache_options"); + json::array messages(sp); + project_input(raw,messages,trunc=="disabled"); + if(messages.empty())throw Failure(400,"input must contain at least one message","invalid_request_error","input"); + r.body= { + { + "model","deepseek-flash" + }, { + "messages",messages + }, { + "stream",r.stream + } + }; + if(r.stream)r.body["stream_options"]=json::object { + { + "include_usage",true + } + }; + if(integer(raw,"max_output_tokens")>0)r.body["max_tokens"]=integer(raw,"max_output_tokens"); + if(!member(raw,"reasoning").is_null()) { + if(!member(raw,"reasoning").is_object())throw Failure(400,"reasoning must be an object","invalid_request_error","reasoning"); + auto effort=field(member(raw,"reasoning").as_object(),"effort"); + if(!effort.empty())r.body["reasoning_effort"]=effort=="ultra"?"max":effort; + } if(member(raw,"parallel_tool_calls").is_bool())r.body["parallel_tool_calls"]=member(raw,"parallel_tool_calls"); + bool thinking=field(r.body,"reasoning_effort","high")!="none"; + for(auto key: { + "temperature","top_p" + })if(raw.contains(key)) { + if(thinking) { + if(!r.warning.empty())r.warning+=", "; + r.warning+=std::string(key)+"_ignored"; + } + else r.body[key]=member(raw,key); + } if(member(raw,"text").is_object()&&member(member(raw,"text").as_object(),"format").is_object()) { + auto kind=field(member(member(raw,"text").as_object(),"format").as_object(),"type","text"); + if(kind=="json_object")r.body["response_format"]=json::object { + { + "type","json_object" + } + }; + else if(kind!="text")throw Failure(400,"text.format type '"+kind+"' is not supported upstream","invalid_request_error","text.format.type"); + } r.echo= { + { + "tools",member(raw,"tools").is_array()?member(raw,"tools"):json::value(json::array { + }) + }, { + "tool_choice",member(raw,"tool_choice").is_null()?json::value("auto"):member(raw,"tool_choice") + }, { + "parallel_tool_calls",flag(raw,"parallel_tool_calls",true) + }, { + "instructions",member(raw,"instructions").is_string()?member(raw,"instructions"):json::value(nullptr) + } + }; + + if(json::serialize(r.echo).size()+1024>limits.terminal_bytes)throw Failure(400,"Terminal echo exceeds the pilot bound"); + project_tools(raw,r); + auto wire=json::serialize(r.body); + auto cost=estimate_tokens(wire)+256+r.body["messages"].as_array().size()*16+(member(r.body,"tools").is_array()?member(r.body,"tools").as_array().size()*32:0); + auto reserve=std::max(16384,integer(r.body,"max_tokens")); + if(reserve>=1000000||cost>static_cast(1000000-reserve))throw Failure(400,"The complete projected DeepSeek prompt exceeds chat-prompt-admission/v3; no payload was dispatched.","context_length_exceeded","input"); + if(wire.size()>6*1024*1024||json::serialize(r.echo).size()+1024>limits.terminal_bytes)throw Failure(400,"Projected request or terminal echo exceeds the pilot bound"); + return r; + } +} diff --git a/cpp/src/server.cpp b/cpp/src/server.cpp new file mode 100644 index 000000000..9df264c55 --- /dev/null +++ b/cpp/src/server.cpp @@ -0,0 +1,53 @@ +#include "pilot/network.hpp" +#include +#include +#include +#include +#include +#include +namespace pilot { + Runtime::Runtime(asio::io_context& context,Config c):io(context),config(std::move(c)),state(config),acceptor(io),control(std::make_shared(io,config,state,counters,[this] { + stop(true); + })),drain_timer(io) { + tcp::endpoint endpoint(asio::ip::make_address("127.0.0.1"),config.port); + acceptor.open(endpoint.protocol()); + acceptor.set_option(tcp::acceptor::reuse_address(true)); + acceptor.bind(endpoint); + acceptor.listen(8); + } asio::awaitable Runtime::accept_loop() { + while(!stopping) { + boost::system::error_code ec; + auto socket=co_await acceptor.async_accept(asio::redirect_error(asio::use_awaitable,ec)); + if(ec) { + if(stopping)co_return; + throw boost::system::system_error(ec); + } + if(sockets>=8) { + socket.close(ec); + continue; + } + ++sockets; + auto session=std::make_shared(*this,std::move(socket)); + sessions.erase(std::remove_if(sessions.begin(),sessions.end(),[](auto const& p) { + return p.expired(); + }),sessions.end()); + sessions.push_back(session); + asio::co_spawn(io,session->run(),[session](std::exception_ptr error){ + if(error)session->cancel(); + }); + } + } void Runtime::drained(){ + if(stopping&&sockets==0){drain_timer.cancel();if(on_drained)on_drained();} + } + void Runtime::stop(bool force){ + if(stopping){if(force)for(auto const& p:sessions)if(auto session=p.lock())session->cancel();return;} + stopping=true; + boost::system::error_code ec; + acceptor.close(ec); + control->stop(); + if(force)for(auto const& p:sessions)if(auto session=p.lock())session->cancel(); + drain_timer.expires_after(std::chrono::seconds(15)); + drain_timer.async_wait([this](boost::system::error_code error){if(!error)for(auto const& p:sessions)if(auto session=p.lock())session->cancel();}); + drained(); + } +} diff --git a/cpp/src/session.cpp b/cpp/src/session.cpp new file mode 100644 index 000000000..0563ce48b --- /dev/null +++ b/cpp/src/session.cpp @@ -0,0 +1,371 @@ +#include "pilot/network.hpp" +#include "pilot/translator.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace pilot { + namespace http=boost::beast::http; + struct Session::Impl { + Runtime& runtime; + boost::beast::tcp_stream downstream; + boost::beast::flat_static_buffer<32768> buffer; + asio::steady_timer watch_join; + std::unique_ptr upstream; + bool cancelled=false,watch_pending=false,permit=false,headers_sent=false,transport=false,delivered=false,dispatch_intent=false,native_headers=false; + std::string id=random_id("cpp_"), response_id; + std::uint64_t ordinal=0,sequence=0; + std::size_t event_count=0; + std::string semantic="cancelled"; + json::value usage; + std::shared_ptr budget=std::make_shared(); + BudgetResource input_resource { + budget,8*1024*1024 + },projection_resource { + budget,8*1024*1024 + },output_resource { + budget,4*1024*1024 + },event_resource { + budget,4*1024*1024 + }; + std::size_t external_reserved=0; + void reserve_external(std::size_t bytes){if(bytes>budget->limit-budget->current+external_reserved)throw std::bad_alloc();budget->current=budget->current-external_reserved+bytes;external_reserved=bytes;budget->peak=std::max(budget->peak,budget->current);} + explicit Impl(Runtime& r,tcp::socket socket):runtime(r),downstream(std::move(socket)),watch_join(r.io) { + reserve_external(262144); + asio::socket_base::send_buffer_size send_buffer(16384); + downstream.socket().set_option(send_buffer); + int actual_send_buffer=0; + socklen_t option_size=sizeof(actual_send_buffer); + if(::getsockopt(downstream.socket().native_handle(),SOL_SOCKET,SO_SNDBUF,&actual_send_buffer,&option_size)!=0)throw std::runtime_error("Kernel send buffer readback failed"); + runtime.counters.actual_downstream_send_buffer_bytes=std::max(runtime.counters.actual_downstream_send_buffer_bytes,static_cast(actual_send_buffer)); + } ~Impl(){budget->current-=external_reserved;} + bool peer_closed() { + char c; + auto n=::recv(downstream.socket().native_handle(),&c,1,MSG_PEEK|MSG_DONTWAIT); + return n==0||(n<0&&errno!=EAGAIN&&errno!=EWOULDBLOCK&&errno!=EINTR); + } void cancel() { + if(cancelled)return; + cancelled=true; + if(upstream)upstream->cancel(); + boost::system::error_code ec; + downstream.socket().cancel(ec); + downstream.socket().close(ec); + } asio::awaitable write(std::string wire) { + if(cancelled)throw Failure(499,"Request was cancelled.","request_cancelled"); + auto& io=runtime.counters;++io.downstream_write_starts;++io.pending_downstream_writes; + struct WriteCompletion { IoCounters& io; ~WriteCompletion(){++io.downstream_write_completions;--io.pending_downstream_writes;} } completion{io}; + downstream.expires_after(runtime.config.limits.write); + co_await asio::async_write(downstream,asio::buffer(wire),asio::use_awaitable); + ++io.downstream_write_successes; + } asio::awaitable reply(int status,json::object payload,std::string extra="") { + auto body=json::serialize(payload); + auto title=status==200?"OK":status==401?"Unauthorized":status==429?"Too Many Requests":status==503?"Service Unavailable":"Bad Request"; + std::string wire="HTTP/1.1 "+std::to_string(status)+" "+title+"\r\nContent-Type: application/json\r\nConnection: close\r\nx-uos-request-id: "+id+"\r\nx-uos-git-sha: "+std::string(UOS_GIT_SHA)+"\r\nx-uos-deployment-id: "+std::string(UOS_DEPLOYMENT_ID)+"\r\n"+extra+"Content-Length: "+std::to_string(body.size())+"\r\n\r\n"+body; + headers_sent=true; + co_await write(std::move(wire)); + delivered=true; + } asio::awaitable stream_headers(std::string const& provider,std::string const& warning) { + std::string wire="HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nCache-Control: "+std::string(native_headers?"no-store":"no-cache")+"\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nx-uos-request-id: "+id+"\r\nx-uos-git-sha: "+std::string(UOS_GIT_SHA)+"\r\nx-uos-deployment-id: "+std::string(UOS_DEPLOYMENT_ID)+"\r\n"; + if(!native_headers)wire+="x-uos-upstream: deepseek\r\n"; + if(!native_headers&&!provider.empty())wire+="x-uos-provider-request-id: "+provider+"\r\n"; + if(!warning.empty())wire+="x-uos-warning: "+warning+"\r\n"; + wire+="\r\n"; + headers_sent=true; + co_await write(std::move(wire)); + } asio::awaitable emit(json::array events) { + for(auto& event:events) { + if(++event_count>runtime.config.limits.events)throw Failure(502,"Response event count exceeded"); + auto& o=event.as_object(); + o["sequence_number"]=sequence++; + auto frame="event: "+field(o,"type")+"\ndata: "+json::serialize(o)+"\n\n"; + if(frame.size()>runtime.config.limits.terminal_bytes)throw Failure(502,"Encoded response exceeds bound"); + std::ostringstream size; + size<(r,std::move(socket))) { + } Session::~Session()=default; + void Session::cancel() { + impl_->cancel(); + } asio::awaitable Session::run() { + auto self=shared_from_this(); + auto& p=*impl_; + auto& runtime=p.runtime; + auto const& limits=runtime.config.limits; + std::optional failure; + bool admitted=false; + try { + http::request_parser parser; + parser.header_limit(16384); + parser.body_limit(boost::none); + p.downstream.expires_after(limits.header); + co_await http::async_read_header(p.downstream,p.buffer,parser,asio::use_awaitable); + auto const& request=parser.get(); + if(request.method()==http::verb::get&&request.target()=="/health") { + auto health = json::object { + { + "status","ready" + }, { + "fixture_authority",true + }, { + "production_standby",false + }, {"git_sha",UOS_GIT_SHA}, {"deployment_id",UOS_DEPLOYMENT_ID}, {"build_dirty",static_cast(UOS_BUILD_DIRTY) + }, { + "active",runtime.active + } + }; + co_await p.reply(200,std::move(health)); + } + else { + if(request.method()!=http::verb::post||request.target()!="/v1/responses")throw Failure(404,"Not found","not_found"); + auto auth=request[http::field::authorization]; + std::string bearer(auth); + if(!bearer.starts_with("Bearer ")||!runtime.state.authenticate(bearer.substr(7)))throw Failure(401,"Unauthorized","invalid_api_key"); + if(p.peer_closed())throw Failure(499,"Request was cancelled.","request_cancelled"); + if(runtime.active>=2)throw Failure(503,"The gateway is at its concurrent inference limit and its waiting queue is full.","local_inference_overload"); + ++runtime.active;runtime.counters.active_requests=runtime.active; + p.permit=true; + p.ordinal=++runtime.ordinal; + auto length=parser.content_length(); + if(length&&*length>limits.request_bytes)throw Failure(413,"Request exceeds the pilot byte bound"); + parser.body_limit(limits.request_bytes); + co_await runtime.control->fence(p.ordinal,"before_reserve_write",p.id); + admitted=runtime.state.reserve(p.id); + co_await runtime.control->fence(p.ordinal,"after_reserve_fsync",p.id); + std::string body; + if(length){p.reserve_external(4*static_cast(*length)+262144);body.reserve(static_cast(*length));} + std::array part { + }; + p.downstream.expires_after(limits.body); + while(!parser.is_done()) { + parser.get().body().data=part.data(); + parser.get().body().size=part.size(); + boost::system::error_code ec; + co_await http::async_read_some(p.downstream,p.buffer,parser,asio::redirect_error(asio::use_awaitable,ec)); + auto n=part.size()-parser.get().body().size; + if(body.size()+n>limits.request_bytes)throw Failure(413,"Request exceeds the pilot byte bound"); + p.reserve_external(4*(body.size()+n)+262144); + if(body.capacity()impl_;state.watch_pending=false;if(!ec) { + char value;auto n=::recv(state.downstream.socket().native_handle(),&value,1,MSG_PEEK|MSG_DONTWAIT);if(n==0||(n<0&&errno!=EAGAIN&&errno!=EWOULDBLOCK&&errno!=EINTR))state.cancel();else if(n>0)state.cancel(); + } + state.watch_join.cancel(); + }); + JsonScan scan; + scan.feed(body,limits); + json::parse_options options; + options.max_depth=64; + boost::system::error_code parse_error; + auto raw=json::parse(body,parse_error,json::storage_ptr(&p.input_resource),options); + std::string { + } + .swap(body); + if(parse_error||!raw.is_object())throw Failure(400,"Invalid JSON body"); + auto prepared=project(raw.as_object(),limits,json::storage_ptr(&p.projection_resource)); + if(prepared.stream){auto metadata=request["x-codex-turn-metadata"];try{auto identity=json::parse(std::string(metadata));if(identity.is_object()){p.native_headers=true;for(auto key:{"session_id","thread_id","turn_id"})if(!member(identity.as_object(),key).is_string()||!nonblank_utf16(field(identity.as_object(),key)))p.native_headers=false;}}catch(std::exception const&){p.native_headers=false;}} + raw=nullptr; + if(!admitted)admitted=runtime.state.reserve(p.id); + if(!admitted)throw Failure(429,"Usage limit exceeded","rate_limit_exceeded"); + co_await runtime.control->fence(p.ordinal,"before_dispatch_intent_write",p.id); + runtime.state.dispatch(p.id);p.dispatch_intent=true; + co_await runtime.control->fence(p.ordinal,"after_dispatch_intent_fsync",p.id); + if(p.cancelled||p.peer_closed()) { + runtime.state.release(p.id,true); + throw Failure(499,"Request was cancelled.","request_cancelled"); + } + p.transport=true; + co_await runtime.control->fence(p.ordinal,"after_transport_started",p.id); + p.upstream=std::make_unique(runtime.io,runtime.config,runtime.counters); + co_await p.upstream->open(json::serialize(prepared.body)); + prepared.body.clear(); + std::size_t name_bytes=0;for(auto const& name:prepared.names)name_bytes+=name.name.size()+name.chat.size();p.reserve_external(8*1024*1024+name_bytes+262144); + if(p.upstream->status()<200||p.upstream->status()>=300) { + std::string error; + while(error.size()<65536) { + auto chunk=co_await p.upstream->read(); + if(chunk.empty())break; + error+=chunk; + } + auto code="deepseek_upstream_http_error"; + std::string message="DeepSeek upstream request failed."; + try { + auto v=json::parse(error); + if(v.is_object()&&member(v.as_object(),"error").is_object()) { + auto const& e=member(v.as_object(),"error").as_object(); + message=field(e,"message",message); + } + } + catch(std::exception const&) { + } + throw Failure(p.upstream->status(),message,code); + } auto provider=p.upstream->header("x-request-id"); + if(provider.size()>256||provider.find_first_not_of("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")!=std::string::npos)provider.clear(); + auto response_id=provider.empty()?random_id("resp_"):"resp_"; + if(!provider.empty())for(char c:provider)if(std::isalnum(static_cast(c)))response_id+=c; + p.response_id = response_id; + Translator translator(prepared,limits,response_id,json::storage_ptr(&p.output_resource)); + SseReader frames(limits.frame_bytes,limits.events); + bool done=false; + if(prepared.stream) { + co_await p.stream_headers(provider,prepared.warning); + + } while(!done) { + auto frame=frames.next(); + if(frame) { + p.upstream->reset_frame_deadline(); + if(*frame=="[DONE]") { + done=true; + break; + } + if(frame->empty())continue; + Limits frame_limits=limits; + frame_limits.request_bytes=limits.frame_bytes; + frame_limits.nodes=16384; + JsonScan frame_scan; + frame_scan.feed(*frame,frame_limits); + auto v=json::parse(repair_utf8(*frame),json::storage_ptr(&p.event_resource)); + if(!v.is_object())throw Failure(502,"Invalid upstream Chat chunk","deepseek_upstream_stream_error"); + auto events=translator.push(v.as_object()); + p.usage=translator.usage(); + if(prepared.stream)co_await p.emit(std::move(events)); + continue; + } + auto chunk=co_await p.upstream->read(); + if(chunk.empty())throw Failure(502,"Upstream Chat Completions stream ended before [DONE].","deepseek_upstream_stream_error"); + frames.feed(chunk); + } auto events=translator.finish(); + p.usage=translator.usage(); + p.semantic=events.empty()?"response.failed":field(events.back().as_object(),"type"); + runtime.state.terminal(p.id,p.semantic,p.usage); + co_await runtime.control->fence(p.ordinal,"after_terminal_intent_fsync",p.id); + if(prepared.stream) { + co_await p.emit(std::move(events)); + co_await p.write("0\r\n\r\n"); + p.delivered=true; + } + else { + if(events.empty()||!member(events.back().as_object(),"response").is_object())throw Failure(502,"Missing terminal response"); + co_await p.reply(200,member(events.back().as_object(),"response").as_object()); + } + co_await runtime.control->fence(p.ordinal,"after_terminal_write",p.id); + } + } + catch(Failure const& e) { + failure=e; + } + catch(std::bad_alloc const&) { + failure=Failure(p.transport?502:503,"Pilot allocation budget exhausted",p.transport?"deepseek_upstream_stream_error":"local_inference_overload"); + } + catch(std::exception const&) { + failure=Failure(p.cancelled?499:502,p.cancelled?"Request was cancelled.":"Upstream request could not be completed.",p.cancelled?"request_cancelled":p.headers_sent?"deepseek_upstream_stream_error":"deepseek_upstream_unreachable"); + } if(failure) { + if(p.upstream)p.upstream->cancel(); + if(!p.transport){try{runtime.state.release(p.id,p.dispatch_intent);}catch(std::exception const&){runtime.stop();p.cancel();}} + if(p.semantic=="cancelled")p.semantic=p.cancelled||failure->status==499?"cancelled":"response.failed"; + try { + runtime.state.terminal(p.id,p.semantic,p.usage); + } + catch(std::exception const&) { + } + if(!p.cancelled&&failure->status!=499) { + try { + if(!p.headers_sent)co_await p.reply(failure->status,error_payload(*failure),failure->status==503?"Retry-After: 1\r\n":failure->status==429?"Retry-After: 60\r\n":""); + else { + auto failure_events = json::array { + json::object { + { + "type","response.failed" + }, { + "response",json::object { + { + "id",p.response_id.empty()?p.id:p.response_id + }, { + "object","response" + }, { + "status","failed" + }, { + "error",json::object { + { + "code",failure->code + }, { + "message","Upstream Chat Completions stream failed." + } + } + } + } + } + } + }; + co_await p.emit(std::move(failure_events)); + co_await p.write("0\r\n\r\n"); + p.delivered=true; + } + } + catch(std::exception const&) { + p.delivered=false; + } + } + } if(p.upstream)p.upstream->cancel(); + boost::system::error_code ec; + p.downstream.socket().cancel(ec); + if(p.watch_pending) { + co_await p.watch_join.async_wait(asio::redirect_error(asio::use_awaitable,ec)); + } + p.downstream.socket().close(ec); + try { + runtime.state.delivered(p.id,p.delivered); + runtime.state.settle(p.id); + co_await runtime.control->fence(p.ordinal,"after_settlement_fsync",p.id); + if(runtime.state.needs_snapshot()) { + runtime.state.snapshot_publish(); + co_await runtime.control->fence(p.ordinal,"after_snapshot_rename",p.id); + runtime.state.snapshot_sync_directory(); + co_await runtime.control->fence(p.ordinal,"after_directory_fsync",p.id); + runtime.state.snapshot_reset_journal(); + co_await runtime.control->fence(p.ordinal,"after_journal_reset",p.id); + } + } + catch(std::exception const&) { + runtime.stop(); + } std::cerr<peak + }, { + "authority","private_fixture" + } + })<<"\n"; + if(p.permit) { + --runtime.active;runtime.counters.active_requests=runtime.active; + p.permit=false; + } + --runtime.sockets; + runtime.drained(); + } +} diff --git a/cpp/src/translator.cpp b/cpp/src/translator.cpp new file mode 100644 index 000000000..16866f8db --- /dev/null +++ b/cpp/src/translator.cpp @@ -0,0 +1,705 @@ +#include "pilot/translator.hpp" +#include +#include +namespace pilot { + namespace { + void append(json::array& target,json::array source) { + for(auto& value:source)target.push_back(std::move(value)); + } json::object reasoning_item(std::string const& id,std::string const& value) { + return { + { + "id",id + }, { + "type","reasoning" + }, { + "status","completed" + }, { + "summary",json::array { + json::object { + { + "type","summary_text" + }, { + "text",value + } + } + } + } + }; + } std::int64_t counter(json::object const& o,std::string_view key) { + auto const& v=member(o,key); + double n=v.is_int64()?static_cast(v.as_int64()):v.is_uint64()?static_cast(v.as_uint64()):v.is_double()?v.as_double():-1; + return std::isfinite(n)&&n>=0&&n<=9007199254740991.0?static_cast(n):-1; + } + } Translator::Translator(Prepared const& r,Limits const& l,std::string id,json::storage_ptr sp):request_(r),limits_(l),storage_(sp),id_(std::move(id)),message_id_("msg_"+id_+"_0"),created_(now_ms()/1000),strings_(sp),calls_(sp),output_(sp),usage_(nullptr,sp) { + strings_= { + { + "text","" + }, { + "refusal","" + }, { + "reasoning","" + } + }; + } json::object Translator::envelope(std::string const& status) { + json::object r(request_.echo,storage_); + r["id"]=id_; + r["object"]="response"; + r["created_at"]=created_; + r["status"]=status; + r["model"]=request_.model; + r["output"]=json::array { + }; + r["error"]=nullptr; + r["incomplete_details"]=nullptr; + r["metadata"]=json::object { + }; + for(auto k: { + "temperature","top_p","max_output_tokens","previous_response_id","reasoning","usage" + })r[k]=nullptr; + r["store"]=false; + r["truncation"]="disabled"; + return r; + } json::array Translator::start() { + json::array events(storage_); + if(started_)return events; + started_=true; + auto r=envelope("in_progress"); + events.push_back(json::object { + { + "type","response.created" + }, { + "response",r + } + }); + events.push_back(json::object { + { + "type","response.in_progress" + }, { + "response",r + } + }); + return events; + } json::array Translator::open() { + return start(); + } void Translator::retain(std::string_view key,std::string_view delta) { + auto& value=strings_[key].as_string(); + value.append(json::string_view(delta.data(),delta.size())); + if(json::serialize(strings_).size()+json::serialize(calls_).size()+json::serialize(request_.echo).size()+1024>limits_.terminal_bytes)throw Failure(502,"Accumulated response exceeds the pilot terminal bound","deepseek_upstream_stream_error"); + } void Translator::open_reasoning(json::array& e) { + if(reasoning_open_)return; + reasoning_open_=true; + reasoning_pending_=false; + reasoning_index_=next_++; + auto id="rs_"+id_+"_"+std::to_string(reasoning_index_); + e.push_back(json::object { + { + "type","response.output_item.added" + }, { + "output_index",reasoning_index_ + }, { + "item",json::object { + { + "id",id + }, { + "type","reasoning" + }, { + "status","in_progress" + }, { + "summary",json::array { + } + } + } + } + }); + e.push_back(json::object { + { + "type","response.reasoning_summary_part.added" + }, { + "item_id",id + }, { + "output_index",reasoning_index_ + }, { + "summary_index",0 + }, { + "part",json::object { + { + "type","summary_text" + }, { + "text","" + } + } + } + }); + } void Translator::close_reasoning(json::array& e) { + if(!reasoning_open_)return; + reasoning_open_=false; + auto id="rs_"+id_+"_"+std::to_string(reasoning_index_); + auto value=field(strings_,"reasoning"); + auto item=reasoning_item(id,value); + if(output_.size()<=static_cast(reasoning_index_))output_.resize(static_cast(reasoning_index_+1)); + output_[static_cast(reasoning_index_)]=item; + e.push_back(json::object { + { + "type","response.reasoning_summary_text.done" + }, { + "item_id",id + }, { + "output_index",reasoning_index_ + }, { + "summary_index",0 + }, { + "text",value + } + }); + e.push_back(json::object { + { + "type","response.reasoning_summary_part.done" + }, { + "item_id",id + }, { + "output_index",reasoning_index_ + }, { + "summary_index",0 + }, { + "part",json::object { + { + "type","summary_text" + }, { + "text",value + } + } + } + }); + e.push_back(json::object { + { + "type","response.output_item.done" + }, { + "output_index",reasoning_index_ + }, { + "item",item + } + }); + strings_["reasoning"]=""; + } void Translator::open_message(json::array& e) { + if(message_open_)return; + close_reasoning(e); + message_open_=true; + message_index_=next_++; + e.push_back(json::object { + { + "type","response.output_item.added" + }, { + "output_index",message_index_ + }, { + "item",json::object { + { + "id",message_id_ + }, { + "type","message" + }, { + "status","in_progress" + }, { + "role","assistant" + }, { + "content",json::array { + } + } + } + } + }); + } void Translator::close_message(json::array& e) { + if(!message_open_||message_done_)return; + message_done_=true; + json::array content(storage_); + content.resize(static_cast((text_index_>=0?1:0)+(refusal_index_>=0?1:0))); + for(auto kind: { + "text","refusal" + }) { + int index=std::string_view(kind)=="text"?text_index_:refusal_index_; + if(index<0)continue; + bool plain=std::string_view(kind)=="text"; + auto value=field(strings_,kind); + json::object part=plain?json::object { + { + "type","output_text" + }, { + "text",value + }, { + "annotations",json::array { + } + } + } + :json::object { + { + "type","refusal" + }, { + "refusal",value + } + }; + json::object event { + { + "type",plain?"response.output_text.done":"response.refusal.done" + }, { + "item_id",message_id_ + }, { + "output_index",message_index_ + }, { + "content_index",index + } + }; + event[plain?"text":"refusal"]=value; + if(plain)event["logprobs"]=json::array { + }; + e.push_back(event); + e.push_back(json::object { + { + "type","response.content_part.done" + }, { + "item_id",message_id_ + }, { + "output_index",message_index_ + }, { + "content_index",index + }, { + "part",part + } + }); + content[static_cast(index)]=part; + } + json::object item { + { + "id",message_id_ + }, { + "type","message" + }, { + "status","completed" + }, { + "role","assistant" + }, { + "content",content + } + }; + if(output_.size()<=static_cast(message_index_))output_.resize(static_cast(message_index_+1)); + output_[static_cast(message_index_)]=item; + e.push_back(json::object { + { + "type","response.output_item.done" + }, { + "output_index",message_index_ + }, { + "item",item + } + }); + } void Translator::announce_tool(json::object& c,json::array& e) { + close_reasoning(e); + auto chat=field(c,"name"); + auto found=std::find_if(request_.names.begin(),request_.names.end(),[&](auto const& n) { + return n.chat==chat; + }); + bool custom=found!=request_.names.end()&&found->custom; + auto id=std::string(custom?"ctc_":"fc_")+id_+"_"+field(c,"key"); + c["id"]=id; + c["index"]=next_++; + c["announced"]=true; + c["custom"]=custom; + c["original_name"]=found==request_.names.end()?chat:found->name; + c["namespace"]=found==request_.names.end()||!found->space?"":*found->space; + json::object item { + { + "id",id + }, { + "type",custom?"custom_tool_call":"function_call" + }, { + "status","in_progress" + }, { + "call_id",c["call_id"] + }, { + "name",c["original_name"] + } + }; + if(!field(c,"namespace").empty())item["namespace"]=c["namespace"]; + item[custom?"input":"arguments"]=""; + e.push_back(json::object { + { + "type","response.output_item.added" + }, { + "output_index",c["index"] + }, { + "item",item + } + }); + } json::array Translator::push(json::object const& chunk) { + auto e=start(); + auto const& raw_usage=member(chunk,"usage"); + if(!raw_usage.is_null()) { + if(!raw_usage.is_object())throw Failure(502,"Invalid upstream usage","deepseek_upstream_stream_error"); + auto const& u=raw_usage.as_object(); + auto input=counter(u,"prompt_tokens"),output=counter(u,"completion_tokens"),total=counter(u,"total_tokens"); + if(input<0||output<0||total<0)throw Failure(502,"Incomplete upstream usage","deepseek_upstream_stream_error"); + json::object usage { + { + "input_tokens",input + }, { + "output_tokens",output + }, { + "total_tokens",total + } + }; + auto cache=counter(u,"prompt_cache_hit_tokens"); + if(cache<0&&member(u,"prompt_tokens_details").is_object())cache=counter(member(u,"prompt_tokens_details").as_object(),"cached_tokens"); + if(cache>=0&&cache<=input)usage["input_tokens_details"]=json::object { + { + "cached_tokens",cache + } + }; + if(member(u,"completion_tokens_details").is_object()) { + auto n=counter(member(u,"completion_tokens_details").as_object(),"reasoning_tokens"); + if(n>=0&&n<=output)usage["output_tokens_details"]=json::object { + { + "reasoning_tokens",n + } + }; + } + usage_=usage; + } auto const& choices=member(chunk,"choices"); + if(!choices.is_array())throw Failure(502,"Invalid upstream choices","deepseek_upstream_stream_error"); + for(auto const& v:choices.as_array()) { + if(!v.is_object())throw Failure(502,"Invalid upstream choice"); + auto const& choice=v.as_object(); + if(member(choice,"finish_reason").is_string())finish_reason_=field(choice,"finish_reason"); + auto const& dv=member(choice,"delta"); + if(dv.is_null())continue; + if(!dv.is_object())throw Failure(502,"Invalid upstream delta"); + auto const& d=dv.as_object(); + for(auto k: { + "content","reasoning_content","refusal" + })if(!member(d,k).is_null()&&!member(d,k).is_string())throw Failure(502,"Invalid upstream delta text"); + auto reason=field(d,"reasoning_content"); + if(!reason.empty()) { + retain("reasoning",reason); + if(reasoning_open_||next_==0) { + open_reasoning(e); + e.push_back(json::object { + { + "type","response.reasoning_summary_text.delta" + }, { + "item_id","rs_"+id_+"_"+std::to_string(reasoning_index_) + }, { + "output_index",reasoning_index_ + }, { + "summary_index",0 + }, { + "delta",reason + } + }); + } + else reasoning_pending_=true; + } for(auto kind: { + "content","refusal" + }) { + auto value=field(d,kind); + if(value.empty())continue; + bool plain=std::string_view(kind)=="content"; + retain(plain?"text":"refusal",value); + open_message(e); + int& index=plain?text_index_:refusal_index_; + if(index<0) { + index=(text_index_>=0?1:0)+(refusal_index_>=0?1:0); + json::object part=plain?json::object { + { + "type","output_text" + }, { + "text","" + }, { + "annotations",json::array { + } + } + } + :json::object { + { + "type","refusal" + }, { + "refusal","" + } + }; + e.push_back(json::object { + { + "type","response.content_part.added" + }, { + "item_id",message_id_ + }, { + "output_index",message_index_ + }, { + "content_index",index + }, { + "part",part + } + }); + } + json::object event { + { + "type",plain?"response.output_text.delta":"response.refusal.delta" + }, { + "item_id",message_id_ + }, { + "output_index",message_index_ + }, { + "content_index",index + }, { + "delta",value + } + }; + if(plain)event["logprobs"]=json::array { + }; + e.push_back(event); + } auto const& tools=member(d,"tool_calls"); + if(!tools.is_null()&&!tools.is_array())throw Failure(502,"Invalid upstream tool calls"); + if(tools.is_array()) { + std::size_t position=0; + for(auto const& tv:tools.as_array()) { + if(!tv.is_object())throw Failure(502,"Invalid upstream tool delta"); + auto const& t=tv.as_object(); + auto key=std::to_string(integer(t,"index",static_cast(position++))); + if(!calls_.contains(key)) { + if(calls_.size()>=256)throw Failure(502,"Tool call count exceeded"); + calls_[key]=json::object { + { + "key",key + }, { + "id","" + }, { + "call_id",field(t,"id",id_+"_call_"+key) + }, { + "name","" + }, { + "arguments","" + }, { + "announced",false + }, { + "index",-1 + } + }; + } + auto& c=calls_[key].as_object(); + if(!field(t,"id").empty())c["call_id"]=field(t,"id"); + auto const& fn=member(t,"function"); + if(fn.is_object()) { + auto name=field(fn.as_object(),"name"); + if(!name.empty())c["name"]=name; + auto args=field(fn.as_object(),"arguments"); + c["arguments"].as_string().append(args); + if(!flag(c,"announced")&&!field(c,"name").empty()&&!reasoning_pending_)announce_tool(c,e); + if(flag(c,"announced")&&!flag(c,"custom")&&!args.empty())e.push_back(json::object { + { + "type","response.function_call_arguments.delta" + }, { + "item_id",c["id"] + }, { + "output_index",c["index"] + }, { + "delta",args + } + }); + } + } + } + } + if(json::serialize(calls_).size()+json::serialize(strings_).size()+json::serialize(request_.echo).size()+1024>limits_.terminal_bytes)throw Failure(502,"Retained response exceeds bound"); + return e; + } std::string Translator::terminal_kind() const { + if(finish_reason_.empty()||finish_reason_=="stop"||finish_reason_=="tool_calls")return "response.completed"; + if(finish_reason_=="length"||finish_reason_=="content_filter")return "response.incomplete"; + return "response.failed"; + } json::array Translator::finish() { + json::array e(storage_); + if(finished_)return e; + finished_=true; + append(e,start()); + auto kind=terminal_kind(); + bool answer=!field(strings_,"text").empty()||!field(strings_,"refusal").empty(); + for(auto const& c:calls_)answer|=!field(c.value().as_object(),"name").empty(); + if(kind=="response.completed"&&!answer) { + e.push_back(json::object { + { + "type","response.failed" + }, { + "response",json::object { + { + "id",id_ + }, { + "object","response" + }, { + "status","failed" + }, { + "error",json::object { + { + "code","empty_upstream_completion" + }, { + "message","Upstream returned an empty completion." + } + } + } + } + } + }); + return e; + } + close_message(e); + if(reasoning_pending_) { + open_reasoning(e); + e.push_back(json::object { + { + "type","response.reasoning_summary_text.delta" + }, { + "item_id","rs_"+id_+"_"+std::to_string(reasoning_index_) + }, { + "output_index",reasoning_index_ + }, { + "summary_index",0 + }, { + "delta",strings_["reasoning"] + } + }); + close_reasoning(e); + } + std::vector keys; + for(auto const& c:calls_)keys.push_back(std::stoi(std::string(c.key()))); + std::sort(keys.begin(),keys.end()); + for(int key:keys) { + auto& c=calls_[std::to_string(key)].as_object(); + if(field(c,"name").empty())continue; + if(!flag(c,"announced"))announce_tool(c,e); + bool custom=flag(c,"custom"); + auto args=field(c,"arguments"); + json::object item { + { + "id",c["id"] + }, { + "type",custom?"custom_tool_call":"function_call" + }, { + "status",kind=="response.completed"?"completed":"incomplete" + }, { + "call_id",c["call_id"] + }, { + "name",c["original_name"] + } + }; + if(!field(c,"namespace").empty())item["namespace"]=c["namespace"]; + if(custom) { + std::string input=args; + try { + auto v=json::parse(args); + if(v.is_string())input=text(v); + else if(v.is_object()&&member(v.as_object(),"input").is_string())input=field(v.as_object(),"input"); + } + catch(std::exception const&) { + } + item["input"]=input; + if(!input.empty())e.push_back(json::object { + { + "type","response.custom_tool_call_input.delta" + }, { + "item_id",c["id"] + }, { + "output_index",c["index"] + }, { + "call_id",c["call_id"] + }, { + "delta",input + } + }); + } + else { + item["arguments"]=args; + e.push_back(json::object { + { + "type","response.function_call_arguments.done" + }, { + "item_id",c["id"] + }, { + "output_index",c["index"] + }, { + "arguments",args + } + }); + } + auto index=static_cast(integer(c,"index")); + if(output_.size()<=index)output_.resize(index+1); + output_[index]=item; + e.push_back(json::object { + { + "type","response.output_item.done" + }, { + "output_index",c["index"] + }, { + "item",item + } + }); + } + close_reasoning(e); + auto response=envelope(kind=="response.completed"?"completed":kind=="response.incomplete"?"incomplete":"failed"); + response["output"]=output_; + response["usage"]=usage_; + if(kind=="response.incomplete")response["incomplete_details"]=json::object { + { + "reason",finish_reason_=="length"?"max_output_tokens":"content_filter" + } + }; + if(kind=="response.failed") { + auto code=finish_reason_=="aborted"||finish_reason_=="insufficient_system_resource"?finish_reason_:"unrecognized_finish_reason:"+finish_reason_; + response["error"]=json::object { + { + "code",code + }, { + "message","DeepSeek stopped generating: "+code + } + }; + } + if(json::serialize(response).size()>limits_.terminal_bytes)throw Failure(502,"Terminal exceeds the pilot bound"); + e.push_back(json::object { + { + "type",kind + }, { + "response",response + } + }); + return e; + } void SseReader::feed(std::string_view input) { + pending_.append(input); + if(pending_.size()>cap_)throw Failure(502,"Upstream SSE frame exceeded the gateway bound","deepseek_upstream_stream_error"); + } std::optional SseReader::next() { + std::size_t pos=std::string::npos,width=0; + for(auto boundary: { + "\r\n\r\n","\n\n","\r\r" + }) { + auto p=pending_.find(boundary); + if(p::length(boundary); + } + } + if(pos==std::string::npos)return { + }; + if(++frames_>max_frames_)throw Failure(502,"Upstream frame count exceeded"); + auto block=pending_.substr(0,pos); + pending_.erase(0,pos+width); + std::string data; + bool any=false; + std::size_t begin=0; + while(begin +#include +#include +#include +#include +#include +#include +#include +#include +namespace pilot { +namespace asio=boost::asio; +using tcp=asio::ip::tcp; +struct Transport::Impl:std::enable_shared_from_this{ + Config const& config; + IoCounters& counters; + tcp::resolver resolver; + tcp::socket plain; + asio::ssl::context context{asio::ssl::context::tls_client}; + std::unique_ptr> tls; + asio::steady_timer timer; + Impl(asio::io_context& io,Config const& c,IoCounters& counts):config(c),counters(counts),resolver(io),plain(io),timer(io){ + if(c.tls){ + context.set_verify_mode(asio::ssl::verify_peer); + context.load_verify_file(c.ca_file); + tls=std::make_unique>(io,context); + tls->set_verify_callback(asio::ssl::host_name_verification(c.upstream_host)); + if(SSL_set_tlsext_host_name(tls->native_handle(),c.upstream_host.c_str())!=1)throw std::runtime_error("TLS SNI setup failed"); + } + } + tcp::socket& socket(){return tls?tls->next_layer():plain;} + void cancel(){resolver.cancel();timer.cancel();boost::system::error_code ec;socket().cancel(ec);socket().close(ec);} + void deadline(std::chrono::steady_clock::time_point at){ + timer.expires_at(at); + std::weak_ptr weak=shared_from_this(); + timer.async_wait([weak](boost::system::error_code ec){if(!ec)if(auto owner=weak.lock())owner->cancel();}); + } +}; +Transport::Transport(asio::io_context& io,Config const& c,IoCounters& counters):impl_(std::make_shared(io,c,counters)){} +Transport::~Transport(){impl_->cancel();} +asio::awaitable Transport::connect(){ + auto p=impl_; + p->deadline(std::chrono::steady_clock::now()+p->config.limits.connect); + auto endpoints=co_await p->resolver.async_resolve(p->config.upstream_host,p->config.upstream_port,asio::use_awaitable); + p->deadline(std::chrono::steady_clock::now()+p->config.limits.connect); + co_await asio::async_connect(p->socket(),endpoints,asio::use_awaitable); + if(p->tls){p->deadline(std::chrono::steady_clock::now()+p->config.limits.connect);co_await p->tls->async_handshake(asio::ssl::stream_base::client,asio::use_awaitable);} + p->timer.cancel(); +} +asio::awaitable Transport::write(std::string value){ + auto p=impl_; + p->deadline(std::chrono::steady_clock::now()+p->config.limits.write); + if(p->tls)co_await asio::async_write(*p->tls,asio::buffer(value),asio::use_awaitable); + else co_await asio::async_write(p->plain,asio::buffer(value),asio::use_awaitable); + p->timer.cancel(); +} +asio::awaitable Transport::read(asio::mutable_buffer buffer){ + auto p=impl_;auto& io=p->counters;++io.upstream_read_starts;++io.pending_upstream_reads; + struct ReadCompletion { IoCounters& io; ~ReadCompletion(){++io.upstream_read_completions;--io.pending_upstream_reads;} } completion{io}; + boost::system::error_code ec;std::size_t n; + if(p->tls)n=co_await p->tls->async_read_some(buffer,asio::redirect_error(asio::use_awaitable,ec)); + else n=co_await p->plain.async_read_some(buffer,asio::redirect_error(asio::use_awaitable,ec)); + if(ec==asio::error::eof||ec==asio::ssl::error::stream_truncated)co_return 0; + if(ec)throw boost::system::system_error(ec); + co_return n; +} +void Transport::deadline(std::chrono::steady_clock::time_point time){impl_->deadline(time);} +void Transport::cancel(){impl_->cancel();} +} diff --git a/cpp/src/upstream.cpp b/cpp/src/upstream.cpp new file mode 100644 index 000000000..6bbfc3b42 --- /dev/null +++ b/cpp/src/upstream.cpp @@ -0,0 +1,56 @@ +#include "pilot/network.hpp" +#include "pilot/transport.hpp" +#include +#include +#include +#include +namespace pilot { +namespace http=boost::beast::http; +struct Upstream::Impl { + Config const& config; + Transport transport; + http::response_parser parser; + std::string pending,ready; + bool eof=false; + Impl(asio::io_context& io,Config const& c,IoCounters& counters):config(c),transport(io,c,counters){parser.header_limit(16384);parser.body_limit(std::numeric_limits::max());parser.eager(true);} + asio::awaitable pump(bool headers_only=false){ + std::array network{},body{}; + for(;;){ + if(!pending.empty()){ + parser.get().body().data=body.data();parser.get().body().size=body.size(); + boost::system::error_code ec; + auto used=parser.put(asio::buffer(pending),ec);pending.erase(0,used); + auto produced=body.size()-parser.get().body().size; + if(produced)ready.append(body.data(),produced); + if(ec&&ec!=http::error::need_more&&ec!=http::error::need_buffer)throw boost::system::system_error(ec); + if(produced||parser.is_done()||(headers_only&&parser.is_header_done()))co_return; + } + if(eof){boost::system::error_code ec;parser.put_eof(ec);if(ec)throw boost::system::system_error(ec);co_return;} + auto n=co_await transport.read(asio::buffer(network)); + eof=n==0;pending.append(network.data(),n); + if(pending.size()>32768)throw Failure(502,"Upstream header buffer exceeded bound"); + } + } +}; +Upstream::Upstream(asio::io_context& io,Config const& c,IoCounters& counters):impl_(std::make_shared(io,c,counters)){} +Upstream::~Upstream(){impl_->transport.cancel();} +asio::awaitable Upstream::open(std::string body){ + auto p=impl_;co_await p->transport.connect(); + std::string wire="POST "+p->config.upstream_target+" HTTP/1.1\r\nHost: "+p->config.upstream_host+"\r\nAuthorization: Bearer "+p->config.upstream_key+"\r\nContent-Type: application/json\r\nAccept: application/json\r\nConnection: close\r\nContent-Length: "+std::to_string(body.size())+"\r\n\r\n"+body; + std::string{}.swap(body);co_await p->transport.write(std::move(wire)); + p->transport.deadline(std::chrono::steady_clock::now()+p->config.limits.headers); + while(!p->parser.is_header_done()&&!p->eof)co_await p->pump(true); + if(!p->parser.is_header_done())throw Failure(502,"Upstream closed before headers","deepseek_upstream_unreachable"); + reset_frame_deadline(); +} +asio::awaitable Upstream::read(){ + auto p=impl_; + if(!p->ready.empty()){std::string result;result.swap(p->ready);co_return result;} + while(!p->parser.is_done()){co_await p->pump();if(!p->ready.empty()){std::string result;result.swap(p->ready);co_return result;}if(p->eof)break;} + co_return std::string{}; +} +int Upstream::status()const{return static_cast(impl_->parser.get().result_int());} +std::string Upstream::header(std::string_view key)const{auto const& headers=impl_->parser.get().base();auto found=headers.find(boost::beast::string_view(key.data(),key.size()));return found==headers.end()?"":std::string(found->value());} +void Upstream::reset_frame_deadline(){impl_->transport.deadline(std::chrono::steady_clock::now()+impl_->config.limits.frame);} +void Upstream::cancel(){impl_->transport.cancel();} +} diff --git a/cpp/tests/candidate-process.ts b/cpp/tests/candidate-process.ts new file mode 100644 index 000000000..802224c71 --- /dev/null +++ b/cpp/tests/candidate-process.ts @@ -0,0 +1,94 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { mkdir, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { createInterface } from "node:readline"; +import { createFixtureControl } from "./fixture-control.ts"; + +export const SYNTHETIC_TOKEN = `u_${"d".repeat(64)}`; +export const startCandidate = async (options: { executable: string; prefix: string[]; directory: string; stateDirectory: string; providerOrigin: string }) => { + await mkdir(options.directory, { recursive: true, mode: 0o700 }); + await mkdir(options.stateDirectory, { recursive: true, mode: 0o700 }); + const control = createFixtureControl(); + const path = join(options.directory, "config.json"); + await writeFile( + path, + JSON.stringify({ + fixture_mode: true, + short_deadlines: true, + token: SYNTHETIC_TOKEN, + upstream_key: "fixture-provider-secret", + upstream_origin: options.providerOrigin, + state_directory: options.stateDirectory, + usage_limit: 50, + ...control.config, + }), + { mode: 0o600 } + ); + const argv = [...options.prefix, options.executable, "--config", path]; + const process = spawn(argv[0], argv.slice(1), { + stdio: ["ignore", "pipe", "pipe", control.commandRead, control.replyWrite], + env: { + PATH: "/usr/bin:/bin", + TMPDIR: options.directory, + ASAN_OPTIONS: "detect_leaks=1:halt_on_error=1:abort_on_error=1", + UBSAN_OPTIONS: "halt_on_error=1:print_stacktrace=1", + }, + }); + process.once("spawn", () => { + control.started(); + }); + assert.ok(process.stdout && process.stderr); + let stderr = ""; + process.stderr.on("data", (data: Buffer) => { + stderr += data.toString(); + if (stderr.length > 2 * 1024 * 1024) process.kill("SIGTERM"); + }); + const exited = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve) => { + process.once("exit", (code, signal) => { + resolve({ code, signal }); + }); + }); + const lines = createInterface({ input: process.stdout }); + const endpoint = await new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject(new Error("Candidate readiness exceeded its bound")); + }, 5000); + process.once("error", (error) => { + clearTimeout(timer); + reject(error); + }); + process.once("exit", () => { + clearTimeout(timer); + reject(new Error("Candidate exited before readiness")); + }); + lines.on("line", (line) => { + const value: unknown = JSON.parse(line); + if (typeof value === "object" && value !== null && "endpoint" in value && typeof value.endpoint === "string") { + clearTimeout(timer); + resolve(value.endpoint); + } + }); + }); + const finish = async (signal: NodeJS.Signals) => { + if (process.exitCode === null && process.signalCode === null) process.kill(signal); + let timer: ReturnType | undefined; + try { + return await Promise.race([ + exited, + new Promise((_, reject) => { + timer = setTimeout(() => { + process.kill("SIGKILL"); + reject(new Error("Candidate exit deadline exceeded")); + }, 5000); + }), + ]); + } finally { + if (timer) clearTimeout(timer); + control.close(); + lines.close(); + await writeFile(join(options.directory, "candidate.stderr"), stderr); + } + }; + return { endpoint, control, exited, argv, finish }; +}; diff --git a/cpp/tests/conformance.ts b/cpp/tests/conformance.ts new file mode 100644 index 000000000..ff25d4740 --- /dev/null +++ b/cpp/tests/conformance.ts @@ -0,0 +1,263 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { createHash } from "node:crypto"; +import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { createInterface } from "node:readline"; +import { createFixtureControl } from "./fixture-control.ts"; +import { + conformanceRequest, + openConformanceResponse, + runConformanceRoundTrip, + runCodexConformance, + createConformanceJournal, +} from "../../tests/helpers/responses-conformance-client.ts"; +import { initializeTestNativeKv } from "../../scripts/test-native-kv.ts"; +import { setKvForTest } from "../../src/kv.ts"; +import { openKv } from "../../src/storage/kv.ts"; + +initializeTestNativeKv({ + artifactDirectory: "/home/codex/repos/ubiquity/ai.ubq.fi/.data/releases/d94fef52ccc76e0d5aa019154f11e47d5e10fba6/.native/kv", + expectedProvenanceSha256: "57d7405783c9f34343f3d4fe089b3733effc079d9521cd341a3dc5bc0c87c201", + inputsManifest: new URL("../../tools/native-kv/inputs.json", import.meta.url).pathname, +}); +const driverKv = await openKv(":memory:"); +setKvForTest(driverKv); +const { startConformanceMock } = await import("../../tests/helpers/responses-conformance-fixture.ts"); +const { shutdownOptionalTelemetry } = await import("../../serve.ts"); + +const executable = process.argv[2]; +assert.ok(executable, "A candidate executable is required"); +const prefixValue: unknown = JSON.parse(process.argv[3] ?? "[]"); +assert.ok(Array.isArray(prefixValue) && prefixValue.every((value): value is string => typeof value === "string")); +const prefix: string[] = prefixValue; +const native = process.argv.includes("--native"); +const disconnect = process.argv.includes("--disconnect"); +const controlled = process.argv.includes("--control"); +const control = controlled ? createFixtureControl() : null; +const candidateJournal = createConformanceJournal(); +const directory = await mkdtemp(join(tmpdir(), "uos-cpp-conformance-")); +const token = `u_${"d".repeat(64)}`; +const mock = await startConformanceMock({ holdToolDelta: !native }); +const stateDirectory = join(directory, "state"); +await mkdir(stateDirectory, { mode: 0o700 }); +const configPath = join(directory, "config.json"); +await writeFile( + configPath, + JSON.stringify({ + fixture_mode: true, + short_deadlines: true, + token, + upstream_key: "fixture-provider-secret", + upstream_origin: mock.origin, + state_directory: stateDirectory, + ...control?.config, + }), + { mode: 0o600 } +); +const argv = [...prefix, executable, "--config", configPath]; +const child = spawn(argv[0], argv.slice(1), { + stdio: control ? ["ignore", "pipe", "pipe", control.commandRead, control.replyWrite] : ["ignore", "pipe", "pipe"], + env: { + PATH: "/usr/bin:/bin", + HOME: process.env.HOME ?? "/home/codex", + TMPDIR: directory, + ASAN_OPTIONS: "detect_leaks=1:halt_on_error=1:abort_on_error=1", + UBSAN_OPTIONS: "halt_on_error=1:print_stacktrace=1", + }, +}); +if (control) + child.once("spawn", () => { + control.started(); + }); +assert.ok(child.stdout && child.stderr); +const candidateOut = child.stdout; +const candidateError = child.stderr; +let stderr = ""; +let stderrTail = ""; +candidateError.on("data", (value: Buffer) => { + stderr += value.toString(); + stderrTail += value.toString(); + let boundary = stderrTail.indexOf("\n"); + while (boundary >= 0) { + const line = stderrTail.slice(0, boundary); + stderrTail = stderrTail.slice(boundary + 1); + if (line.startsWith('{"event":"request_terminal"')) candidateJournal.add("terminal", JSON.parse(line) as Record); + boundary = stderrTail.indexOf("\n"); + } + if (stderr.length > 2 * 1024 * 1024) child.kill("SIGTERM"); +}); +const exit = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve) => { + child.once("exit", (code, signal) => { + resolve({ code, signal }); + }); +}); +const lines = createInterface({ input: candidateOut }); +const ready = new Promise((resolve, reject) => { + const deadline = setTimeout(() => { + reject(new Error("C++ readiness exceeded five seconds")); + }, 5000); + child.once("error", reject); + child.once("exit", () => { + reject(new Error("C++ exited before readiness")); + }); + lines.on("line", (line) => { + const value: unknown = JSON.parse(line); + if (typeof value === "object" && value !== null && "endpoint" in value && typeof value.endpoint === "string") { + clearTimeout(deadline); + resolve(value.endpoint); + } + }); +}); +let failure: unknown = null; +let captures: unknown; +try { + const endpoint = await ready; + if (control) { + const armed = await control.send("ARM_FENCE", { request_ordinal: 1, phase: "after_reserve_fsync" }); + assert.equal(armed.kind, "FENCE_ARMED"); + const opening = openConformanceResponse(endpoint, token, conformanceRequest()); + const reached = await control.wait("FENCE_REACHED"); + const observed = await control.send("READ_STATE", { request_id: reached.request_id }); + assert.ok(typeof observed.state === "object" && observed.state !== null && "reserved_requests" in observed.state && "committed_requests" in observed.state); + assert.equal(observed.state.reserved_requests, 1); + assert.equal(observed.state.committed_requests, 0); + assert.equal(mock.calls.length, 0); + const revoked = await control.send("REVOKE_KEY", { key_id: "responses-conformance-key" }); + assert.equal(revoked.kind, "REVOKED"); + await control.send("RELEASE_FENCE", { fence_id: armed.fence_id }); + const response = await opening; + const first = await response.waitFor("response.function_call_arguments.delta"); + mock.receipt(1, first); + const completed = await response.finished; + assert.equal(completed.status, 200); + assert.equal(completed.events.at(-1)?.type, "response.completed"); + const next = await openConformanceResponse(endpoint, token, conformanceRequest()); + const refused = await next.finished; + assert.equal(refused.status, 401); + assert.equal(mock.calls.length, 1); + captures = { observed, completed, refused, pipeReports: control.reports, controls: control.entries }; + } else if (disconnect) { + const response = await openConformanceResponse(endpoint, token, conformanceRequest()); + await response.waitFor("response.function_call_arguments.delta"); + response.disconnect(); + captures = await response.finished; + await mock.journal.wait((entry) => entry.kind === "provider_cancelled"); + const terminal = await candidateJournal.wait((entry) => entry.kind === "terminal"); + assert.equal(terminal.detail.semantic_terminal, "cancelled"); + assert.equal(terminal.detail.delivery_outcome, "interrupted"); + assert.equal(mock.calls.length, 1); + assert.equal( + mock.journal.entries.some((entry) => entry.kind === "provider_resumed"), + false + ); + } else if (native) { + captures = await runCodexConformance({ + executable: "/home/codex/.codex/packages/standalone/releases/0.167.2-aarch64-unknown-linux-musl/bin/codex", + expectedSha256: "b581df3989da98e36fccd7d40cc980727a9ee87bcf0e6ef52ffb2544d6a5302f", + expectedVersion: "0.167.2", + endpoint, + token, + captureDirectory: join(directory, "native"), + onTextDelta: (event) => { + mock.receipt(2, event); + }, + }); + } else { + captures = await runConformanceRoundTrip({ + endpoint, + token, + acknowledge: (turn, event) => { + mock.receipt(turn, event); + }, + }); + assert.equal(mock.calls.length, 2); + const continuation: unknown = JSON.parse(mock.calls[1].body); + assert.ok(typeof continuation === "object" && continuation !== null && "messages" in continuation && Array.isArray(continuation.messages)); + assert.ok( + continuation.messages.some( + (message: unknown) => + typeof message === "object" && + message !== null && + "role" in message && + message.role === "tool" && + "content" in message && + message.content === "fixture-tool-ok" + ) + ); + const invalid = await openConformanceResponse(endpoint, token, { ...conformanceRequest(), max_output_tokens: 0 }); + const refusal = await invalid.finished; + assert.equal(refusal.status, 400); + const missingStream = { ...conformanceRequest() }; + delete missingStream.stream; + for (const unsupported of [{ ...conformanceRequest(), stream: false }, missingStream]) { + const request = await openConformanceResponse(endpoint, token, unsupported); + const rejected = await request.finished; + assert.equal(rejected.status, 400); + const payload: unknown = JSON.parse(Buffer.concat(rejected.chunksBase64.map((chunk) => Buffer.from(chunk, "base64"))).toString()); + assert.ok( + typeof payload === "object" && + payload !== null && + "error" in payload && + typeof payload.error === "object" && + payload.error !== null && + "param" in payload.error + ); + assert.equal(payload.error.param, "stream"); + } + + assert.equal(mock.calls.length, 2); + } +} catch (error) { + failure = error; +} finally { + child.kill("SIGTERM"); + const stopped = await Promise.race([ + exit, + new Promise((_, reject) => + setTimeout(() => { + child.kill("SIGKILL"); + reject(new Error("C++ failed to drain")); + }, 5000) + ), + ]); + control?.close(); + lines.close(); + await mock.stop(); + await shutdownOptionalTelemetry(); + driverKv.close(); + setKvForTest(null); + await writeFile(join(directory, "candidate.stderr"), stderr); + await writeFile( + join(directory, "receipt.json"), + JSON.stringify( + { argv, stopped, captures, upstream: mock.calls, chronology: mock.journal.entries, failure: failure instanceof Error ? failure.message : failure }, + null, + 2 + ) + ); + if (!failure) assert.equal(stopped.code, 0, stderr); +} +assert.equal(failure, null, failure instanceof Error ? failure.message : "Conformance failed"); +// Independent readback of the isolated versioned journal; never reads production KV. +const snapshot: unknown = JSON.parse(await readFile(join(stateDirectory, "snapshot.json"), "utf8")); +assert.ok(typeof snapshot === "object" && snapshot !== null); +const journal = await readFile(join(stateDirectory, "journal.bin")); +const transitions: string[] = []; +let offset = 0; +while (offset < journal.length) { + const length = journal.readUInt32BE(offset); + const payload = journal.subarray(offset + 4, offset + 4 + length - 64); + const digest = journal.subarray(offset + 4 + length - 64, offset + 4 + length).toString(); + assert.equal(createHash("sha256").update(payload).digest("hex"), digest); + const frame: unknown = JSON.parse(payload.toString()); + assert.ok(typeof frame === "object" && frame !== null && "value" in frame && typeof frame.value === "object" && frame.value !== null); + if ("quota" in frame && frame.quota && "state" in frame.value) transitions.push(String(frame.value.state)); + offset += length + 4; +} +let expectedTransitions = ["reserved", "dispatched", "reserved", "dispatched", "reserved", "released", "reserved", "released", "reserved", "released"]; +if (native) expectedTransitions = ["reserved", "dispatched", "reserved", "dispatched"]; +if (disconnect || controlled) expectedTransitions = ["reserved", "dispatched"]; +assert.deepEqual(transitions, expectedTransitions); +console.log(JSON.stringify({ status: "passed", directory, native, disconnect, controlled, providerPosts: mock.calls.length, quotaTransitions: transitions })); diff --git a/cpp/tests/fixture-control.ts b/cpp/tests/fixture-control.ts new file mode 100644 index 000000000..f29718fab --- /dev/null +++ b/cpp/tests/fixture-control.ts @@ -0,0 +1,90 @@ +import assert from "node:assert/strict"; +import { dlopen, FFIType, ptr } from "bun:ffi"; +import { createReadStream, fstatSync, readlinkSync, writeSync } from "node:fs"; +import { createConformanceJournal, type ConformanceEvent } from "../../tests/helpers/responses-conformance-client.ts"; + +const libc = dlopen("libc.so.6", { + pipe2: { args: [FFIType.ptr, FFIType.i32], returns: FFIType.i32 }, + fcntl: { args: [FFIType.i32, FFIType.i32, FFIType.i32], returns: FFIType.i32 }, + close: { args: [FFIType.i32], returns: FFIType.i32 }, +}); +const makePipe = () => { + const descriptors = new Int32Array(2); + assert.equal(libc.symbols.pipe2(ptr(descriptors), 0x80000), 0, "Create actual anonymous close-on-exec pipes"); + for (const fd of descriptors) { + assert.equal(libc.symbols.fcntl(fd, 1031, 4096), 4096, "Bound the owned pipe capacity"); + assert.equal(fstatSync(fd).isFIFO(), true); + assert.match(readlinkSync(`/proc/self/fd/${fd}`), /^pipe:\[/); + } + return { read: descriptors[0], write: descriptors[1] }; +}; + +/** No resident bridge: the actual kernel pipe ends are inherited by the candidate. */ +export const createFixtureControl = () => { + const command = makePipe(); + const reply = makePipe(); + const journal = createConformanceJournal(); + const reports = [command.read, command.write, reply.read, reply.write].map((fd) => ({ + fd, + target: readlinkSync(`/proc/self/fd/${fd}`), + uid: fstatSync(fd).uid, + fifo: fstatSync(fd).isFIFO(), + flags: libc.symbols.fcntl(fd, 3, 0), + capacity: libc.symbols.fcntl(fd, 1032, 0), + })); + let sequence = 0; + let pending = Buffer.alloc(0); + let started = false; + let closed = false; + const input = createReadStream("", { fd: reply.read, autoClose: true }); + input.on("data", (chunk: Buffer | string) => { + pending = Buffer.concat([pending, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); + while (pending.length >= 4) { + const size = pending.readUInt32BE(0); + assert.ok(size > 0 && size <= 4092); + if (pending.length < size + 4) break; + const value: unknown = JSON.parse(pending.subarray(4, size + 4).toString()); + assert.ok(typeof value === "object" && value !== null && !Array.isArray(value)); + journal.add("reply", value as ConformanceEvent); + pending = pending.subarray(size + 4); + } + assert.ok(pending.length <= 4096); + }); + input.on("error", (error) => { + journal.add("pipe_error", { message: error.message }); + }); + return { + reports, + commandRead: command.read, + replyWrite: reply.write, + config: { control_read_fd: 3, control_write_fd: 4 }, + started: (): void => { + assert.equal(started, false); + started = true; + assert.equal(libc.symbols.close(command.read), 0); + assert.equal(libc.symbols.close(reply.write), 0); + }, + send: async (op: string, fields: ConformanceEvent = {}): Promise => { + const id = ++sequence; + const body = Buffer.from(JSON.stringify({ version: 1, id, op, ...fields })); + assert.ok(body.length <= 4092); + const frame = Buffer.alloc(body.length + 4); + frame.writeUInt32BE(body.length); + body.copy(frame, 4); + assert.equal(writeSync(command.write, frame), frame.length); + return (await journal.wait((entry) => entry.kind === "reply" && entry.detail.id === id)).detail; + }, + wait: async (kind: string): Promise => (await journal.wait((entry) => entry.kind === "reply" && entry.detail.kind === kind)).detail, + entries: journal.entries, + close: (): void => { + if (closed) return; + closed = true; + if (!started) { + libc.symbols.close(command.read); + libc.symbols.close(reply.write); + } + libc.symbols.close(command.write); + input.destroy(); + }, + }; +}; diff --git a/cpp/tests/mock.ts b/cpp/tests/mock.ts new file mode 100644 index 000000000..e843cc1e3 --- /dev/null +++ b/cpp/tests/mock.ts @@ -0,0 +1,17 @@ +import { initializeTestNativeKv } from "../../scripts/test-native-kv.ts"; +import { setKvForTest } from "../../src/kv.ts"; +import { openKv } from "../../src/storage/kv.ts"; +initializeTestNativeKv({ + artifactDirectory: "/home/codex/repos/ubiquity/ai.ubq.fi/.data/releases/d94fef52ccc76e0d5aa019154f11e47d5e10fba6/.native/kv", + expectedProvenanceSha256: "57d7405783c9f34343f3d4fe089b3733effc079d9521cd341a3dc5bc0c87c201", + inputsManifest: new URL("../../tools/native-kv/inputs.json", import.meta.url).pathname, +}); +const database = await openKv(":memory:"); +setKvForTest(database); +export const { startConformanceMock } = await import("../../tests/helpers/responses-conformance-fixture.ts"); +const { shutdownOptionalTelemetry } = await import("../../serve.ts"); +export const closeMockEnvironment = async () => { + await shutdownOptionalTelemetry(); + database.close(); + setKvForTest(null); +}; diff --git a/cpp/tests/protocol_tests.cpp b/cpp/tests/protocol_tests.cpp new file mode 100644 index 000000000..842fc81db --- /dev/null +++ b/cpp/tests/protocol_tests.cpp @@ -0,0 +1,64 @@ +#include "pilot/core.hpp" +#include "pilot/translator.hpp" +#include +namespace { +void require(bool condition,char const* message){if(!condition)throw std::runtime_error(message);} +std::string repeated(std::string_view value,std::size_t count){std::string result;result.reserve(value.size()*count);while(count--)result+=value;return result;} +void unicode_source_vectors(){ + std::string emoji="\xf0\x9f\x98\x80",accent="\xc3\xa9"; + struct Vector{std::string value,prefix;}; + std::string byte_prefix=repeated(accent,10000)+std::string(43082,'a'); + std::string marker_prefix=repeated(accent,6552)+std::string(46530,'a'); + std::vector vectors={ + {std::string(65535,'a')+emoji+std::string(1023,'a'),std::string(58981,'a')}, + {byte_prefix+emoji+std::string(13476,'a'),byte_prefix}, + {marker_prefix+emoji+std::string(13476,'a'),marker_prefix}, + {std::string(66560,'a'),std::string(58982,'a')}, + {repeated(accent,66560),repeated(accent,31343)} + }; + for(auto const& vector:vectors){ + auto result=pilot::bounded_payload(vector.value,"input[1].output",false); + require(result.size()<=65536,"UTF8 forwarding cap"); + auto marker=result.find("\n[gateway:"); + require(marker!=std::string::npos&&result.substr(0,marker)==vector.prefix,"Exact frozen source UTF16 prefix"); + require(result.find("the model did not receive the elided bytes")!=std::string::npos,"Visible source elision notice"); + bool refused=false; + try{pilot::bounded_payload(vector.value,"input[1].output",true);}catch(pilot::Failure const& f){refused=f.status==400&&f.code=="context_length_exceeded";} + require(refused,"Disabled truncation must fail closed"); + } + auto repaired=pilot::repair_utf8(std::string("before")+"\xc3"+"(after"); + require(repaired=="before\xef\xbf\xbd(after","Selected-route nonfatal UTF8 decoder"); +} +void budget_vectors(){ + auto group=std::make_shared();group->limit=256; + pilot::BudgetResource first(group,192),second(group,192); + auto a=first.allocate(128,8);auto b=second.allocate(128,8); + bool refused=false; + try{second.allocate(1,8);}catch(std::bad_alloc const&){refused=true;} + require(refused&&group->peak==256,"Aggregate allocation must refuse before crossing bound"); + first.deallocate(a,128,8);second.deallocate(b,128,8); + require(group->current==0,"All allocation owners released"); + pilot::Limits limits;limits.depth=2;pilot::JsonScan scan; + refused=false;try{scan.feed("[[[]]]",limits);}catch(pilot::Failure const&){refused=true;} + require(refused,"Depth bound"); + limits.depth=64;limits.nodes=4;pilot::JsonScan nodes; + refused=false;try{nodes.feed("{\"x\":[1,2,3]}",limits);}catch(pilot::Failure const&){refused=true;} + require(refused,"Count nodes before JSON key collapse"); +} +} +int main(){ + try{ + unicode_source_vectors();budget_vectors(); + auto group=std::make_shared(); + pilot::BudgetResource resource(group,8*1024*1024);pilot::Limits limits; + auto input=pilot::json::parse(R"({"model":"deepseek-flash","stream":true,"input":"hello","reasoning":{"effort":"none"}})"); + auto request=pilot::project(input.as_object(),limits,pilot::json::storage_ptr(&resource)); + require(pilot::field(request.body,"model")=="deepseek-flash","Canonical source model"); + pilot::Translator translator(request,limits,"resp_test",pilot::json::storage_ptr(&resource)); + auto chunk=pilot::json::parse(R"({"choices":[{"index":0,"delta":{"content":"hello"},"finish_reason":"stop"}],"usage":{"prompt_tokens":1,"completion_tokens":1,"total_tokens":2}})"); + translator.push(chunk.as_object());auto end=translator.finish(); + require(pilot::field(end.back().as_object(),"type")=="response.completed"&&translator.finish().empty(),"Exactly one accumulated terminal"); + pilot::SseReader reader(1024,20);reader.feed("data: [DONE]\r\n\r");require(!reader.next(),"Split CRLF delimiter is incomplete");reader.feed("\n");require(reader.next()==std::optional("[DONE]"),"Completed CRLF delimiter"); + std::cout<<"protocol, source Unicode and hard allocation vectors passed\n";return 0; + }catch(std::exception const& error){std::cerr< typeof part === "string")); +const prefix: string[] = value; +const directory = await mkdtemp(join(tmpdir(), "uos-cpp-recovery-")); +const phases = ["after_reserve_fsync", "after_dispatch_intent_fsync", "after_terminal_intent_fsync", "after_settlement_fsync"]; +const receipts: unknown[] = []; +try { + for (const phase of phases) { + const root = join(directory, phase); + const stateDirectory = join(root, "state"); + const mock = await startConformanceMock(); + let candidate = await startCandidate({ executable, prefix, directory: join(root, "original"), stateDirectory, providerOrigin: mock.origin }); + try { + const armed = await candidate.control.send("ARM_FENCE", { request_ordinal: 1, phase }); + assert.equal(armed.kind, "FENCE_ARMED"); + const flow = (async () => { + const response = await openConformanceResponse(candidate.endpoint, SYNTHETIC_TOKEN, conformanceRequest()); + const delta = await response.waitFor("response.function_call_arguments.delta"); + mock.receipt(1, delta); + return await response.finished; + })().then( + (capture) => ({ capture }), + (error: unknown) => ({ error: error instanceof Error ? error.message : String(error) }) + ); + const reached = await candidate.control.wait("FENCE_REACHED"); + const before = await candidate.control.send("READ_STATE", { request_id: reached.request_id }); + const abrupt = await candidate.finish("SIGKILL"); + assert.equal(abrupt.signal, "SIGKILL"); + const client = await flow; + const postsAtCrash = mock.calls.length; + candidate = await startCandidate({ executable, prefix, directory: join(root, "restart-one"), stateDirectory, providerOrigin: mock.origin }); + const after = await candidate.control.send("READ_STATE", { request_id: reached.request_id }); + const state = after.state as ConformanceEvent; + assert.equal(mock.calls.length, postsAtCrash, "Startup cannot issue any provider request"); + assert.equal(state.quota_transactions, phase === "after_reserve_fsync" ? 1 : 2); + assert.equal(state.committed_requests, phase === "after_reserve_fsync" ? 0 : 1); + assert.equal(state.reserved_requests, phase === "after_reserve_fsync" ? 1 : 0); + const row = state.request as ConformanceEvent; + assert.equal(row.state, phase === "after_reserve_fsync" ? "reserved" : "dispatched"); + if (phase === "after_terminal_intent_fsync") { + assert.equal(row.terminal, "response.completed"); + assert.equal(row.delivery, "unobserved"); + assert.equal(row.settled, true); + } + if (phase === "after_settlement_fsync") { + assert.equal(row.settled, true); + assert.equal(row.delivery, "delivered"); + } + assert.equal((await candidate.finish("SIGTERM")).code, 0); + candidate = await startCandidate({ executable, prefix, directory: join(root, "restart-two"), stateDirectory, providerOrigin: mock.origin }); + const repeated = await candidate.control.send("READ_STATE", { request_id: reached.request_id }); + assert.deepEqual(repeated.state, after.state, "A second restart must not apply settlement twice"); + assert.equal(mock.calls.length, postsAtCrash); + receipts.push({ phase, reached, before, abrupt, client, after, repeated, postsAtCrash, upstream: mock.calls, chronology: mock.journal.entries }); + } finally { + assert.equal((await candidate.finish("SIGTERM")).code, 0); + await mock.stop(); + } + } +} finally { + await closeMockEnvironment(); + await writeFile(join(directory, "recovery.json"), JSON.stringify(receipts, null, 2)); +} +console.log(JSON.stringify({ status: "passed", directory, phases, actualProcessRestarts: phases.length * 2, startupReplays: 0 })); diff --git a/cpp/tests/resources.ts b/cpp/tests/resources.ts new file mode 100644 index 000000000..1362c3216 --- /dev/null +++ b/cpp/tests/resources.ts @@ -0,0 +1,126 @@ +import assert from "node:assert/strict"; +import { createServer, request } from "node:http"; +import { mkdtemp, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { toDeepSeekResponsesChatBody } from "../../src/deepseek/chat-projection.ts"; +import { COMPACTION_SUMMARY_POLICY } from "../../src/deepseek/forwarded-payload-policy.ts"; +import { startCandidate, SYNTHETIC_TOKEN } from "./candidate-process.ts"; + +const executable = process.argv[2]; +assert.ok(executable); +const parsedPrefix: unknown = JSON.parse(process.argv[3] ?? "[]"); +assert.ok(Array.isArray(parsedPrefix) && parsedPrefix.every((value): value is string => typeof value === "string")); +const prefix: string[] = parsedPrefix; +const directory = await mkdtemp(join(tmpdir(), "uos-cpp-resources-")); +const forwarded: unknown[] = []; +const server = createServer((incoming, outgoing) => { + void (async () => { + const chunks: Buffer[] = []; + let bytes = 0; + for await (const chunk of incoming) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)); + bytes += buffer.length; + assert.ok(bytes <= 6 * 1024 * 1024); + chunks.push(buffer); + } + forwarded.push(JSON.parse(Buffer.concat(chunks).toString()) as unknown); + const chunk = { + id: "resource-provider", + object: "chat.completion.chunk", + created: 1780000001, + model: "deepseek-flash", + choices: [{ index: 0, delta: { content: "resource-ok" }, finish_reason: "stop" }], + usage: { prompt_tokens: 100, completion_tokens: 2, total_tokens: 102 }, + }; + outgoing.writeHead(200, { "content-type": "text/event-stream", "x-request-id": "resource-provider" }); + outgoing.end(`data: ${JSON.stringify(chunk)}\n\ndata: [DONE]\n\n`); + })().catch((error: unknown) => outgoing.destroy(error instanceof Error ? error : new Error(String(error)))); +}); +await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); +const address = server.address(); +assert.ok(address && typeof address !== "string"); +const candidate = await startCandidate({ + executable, + prefix, + directory: join(directory, "candidate"), + stateDirectory: join(directory, "state"), + providerOrigin: `http://127.0.0.1:${address.port}`, +}); +const send = (body: unknown, declaredBytes?: number) => + new Promise<{ status: number; text: string }>((resolve, reject) => { + const data = JSON.stringify(body); + const outgoing = request( + `${candidate.endpoint}/v1/responses`, + { + method: "POST", + agent: false, + headers: { authorization: `Bearer ${SYNTHETIC_TOKEN}`, "content-type": "application/json", "content-length": declaredBytes ?? Buffer.byteLength(data) }, + }, + (response) => { + let text = ""; + response.setEncoding("utf8"); + response.on("data", (value: string) => { + text += value; + assert.ok(text.length < 262144); + }); + response.once("error", reject); + response.once("end", () => { + resolve({ status: response.statusCode ?? 0, text }); + outgoing.destroy(); + }); + } + ); + outgoing.once("error", reject); + outgoing.setTimeout(10000, () => outgoing.destroy(new Error("Resource scenario deadline exceeded"))); + if (declaredBytes) outgoing.flushHeaders(); + else outgoing.end(data); + }); +const receipts: unknown[] = []; +try { + const base = + "alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xray yankee zulu "; + const part = base.repeat(Math.ceil(65536 / base.length)).slice(0, 65536); + const normal = { + model: "deepseek-flash", + stream: true, + input: Array.from({ length: 24 }, () => ({ role: "user", content: [{ type: "input_text", text: part }] })), + }; + const head = `${COMPACTION_SUMMARY_POLICY.prefix}\n`; + const summary = { + model: "deepseek-flash", + stream: true, + truncation: "disabled", + input: [{ role: "user", content: [{ type: "input_text", text: head + "s".repeat(720 * 1024 - Buffer.byteLength(head)) }] }], + }; + for (const body of [normal, summary]) { + const oracle = toDeepSeekResponsesChatBody(body, "deepseek-flash", true); + assert.equal(oracle.ok, true); + const response = await send(body); + assert.equal(response.status, 200); + assert.match(response.text, /event: response.completed/); + assert.deepEqual(forwarded.at(-1), oracle.value.body, "Large input must preserve the actual source projection"); + receipts.push({ requestBytes: Buffer.byteLength(JSON.stringify(body)), sourceProjectionMatches: true, response }); + } + const oversize = await send({}, 4 * 1024 * 1024 + 1); + assert.equal(oversize.status, 413); + let nested: unknown = 0; + for (let index = 0; index < 65; index++) nested = [nested]; + assert.equal((await send({ model: "deepseek-flash", stream: true, input: "ok", metadata: nested })).status, 400); + assert.equal((await send({ model: "deepseek-flash", stream: true, input: "ok", metadata: Array.from({ length: 65537 }, () => 0) })).status, 400); + assert.equal(forwarded.length, 2); + const state = await candidate.control.send("READ_STATE"); + assert.ok(typeof state.state === "object" && state.state !== null && "quota_transactions" in state.state); + assert.equal(state.state.quota_transactions, 8, "Two dispatches and two post-reservation bound refusals"); + receipts.push({ oversize, state, providerPosts: forwarded.length }); +} finally { + assert.equal((await candidate.finish("SIGTERM")).code, 0); + await new Promise((resolve, reject) => { + server.close((error) => { + if (error) reject(error); + else resolve(); + }); + }); + await writeFile(join(directory, "resources.json"), JSON.stringify(receipts, null, 2)); +} +console.log(JSON.stringify({ status: "passed", directory, providerPosts: forwarded.length, largeInputs: 2 })); diff --git a/cpp/tools/build.py b/cpp/tools/build.py new file mode 100644 index 000000000..2889a693a --- /dev/null +++ b/cpp/tools/build.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +"""Offline, serial, measured invocation of the root-qualified SDK; never installs.""" +import json +import os +import resource +from pathlib import Path +import shutil +import subprocess +import sys +import time + +sdk = Path(sys.argv[1]).resolve() +preset = sys.argv[2] +phase = sys.argv[3] +if preset not in ("pi-release", "native-asan") or phase not in ("configure", "build", "test", "checkpoint", "test-checkpoint", "complete", "tidy"): + raise SystemExit("usage: build.py SDK {pi-release|native-asan} {configure|build|test|checkpoint|tidy}") +workspace = Path(__file__).resolve().parents[2] +source = workspace / "cpp" +build = workspace / ".data/cpp-build" / preset +temporary = workspace / ".data/cpp-tmp" / preset +evidence = workspace / ".data/cpp-evidence" +for path in (temporary, evidence): + path.mkdir(parents=True, exist_ok=True) +roots = [workspace / (".data/" + name) for name in ("cpp-build", "cpp-tmp", "cpp-stage", "cpp-debug", "cpp-evidence", "cpp-tool-view")] + +def allocated(paths): + seen = set() + total = 0 + for root in paths: + if not root.exists(): + continue + for directory, _, files in os.walk(root): + for path in [Path(directory)] + [Path(directory) / value for value in files]: + st = path.lstat() + key = (st.st_dev, st.st_ino) + if key not in seen: + seen.add(key) + total += st.st_blocks * 512 + return total + +sdk_bytes = int(subprocess.check_output(["du", "-sx", "--block-size=1", str(sdk)], text=True).split()[0]) +baseline = allocated(roots) +free = shutil.disk_usage(workspace).free +if free - 256 * 1024 * 1024 < 2 * 1024 ** 3 or sdk_bytes + baseline + 256 * 1024 * 1024 > 2000000000: + raise SystemExit("Insufficient bounded disk reserve for compiler phase") +env = {"PATH": "/usr/bin:/bin", "HOME": os.environ.get("HOME", "/home/codex"), "TMPDIR": str(temporary), "TZ": "UTC", "UOS_CPP_TOOLCHAIN_DIR": str(sdk)} +if preset == "native-asan": + env.update(ASAN_OPTIONS="detect_leaks=1:halt_on_error=1:abort_on_error=1", UBSAN_OPTIONS="halt_on_error=1:print_stacktrace=1") +loader = str(sdk / "root/lib/ld-linux-aarch64.so.1") +libraries = str(sdk / "root/lib/aarch64-linux-gnu") + ":" + str(sdk / "root/usr/lib/aarch64-linux-gnu") +view = workspace / ".data/cpp-tool-view" +(view / "bin").mkdir(parents=True, exist_ok=True) +(view / "share").mkdir(exist_ok=True) +resource_link = view / "share/cmake-3.25" +if not resource_link.exists(): + resource_link.symlink_to(sdk / "root/usr/share/cmake-3.25") +for tool in ("cmake", "ctest", "cpack"): + wrapper = view / "bin" / tool + wrapper.write_text('#!/bin/sh\nset -eu\nexec "' + loader + '" --library-path "' + libraries + '" --argv0 "$0" "' + str(sdk / "root/usr/bin" / tool) + '" "$@"\n') + wrapper.chmod(0o755) +cmake = [str(view / "bin/cmake")] +commands = { + "configure": cmake + ["--preset", preset, "-DUOS_BUN_EXECUTABLE=/home/codex/repos/ubiquity/ai.ubq.fi/.data/runtimes/bun/linux-arm64/1.4.0-086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9/bun", "-DUOS_RUNTIME_PREFIX=" + json.dumps([loader, "--library-path", libraries] if preset == "pi-release" else [])], + "build": cmake + ["--build", "--preset", preset, "--", "-k0"], + "test": [str(view / "bin/ctest"), "--preset", preset], +} +phases = ["configure", "build", "test"] if phase == "complete" else ["configure", "build"] if phase == "checkpoint" else ["configure", "test"] if phase == "test-checkpoint" else [phase] +receipts = [] +for current in phases: + if current == "tidy": + units = sorted((source / "src").glob("*.cpp")) + argv = [loader, "--library-path", libraries, str(sdk / "root/usr/bin/clang-tidy-14"), *map(str, units), "-p", str(build), "--checks=-*,clang-analyzer-core.*,clang-analyzer-cplusplus.*,bugprone-dangling-handle,bugprone-use-after-move", "--warnings-as-errors=*", "--extra-arg=--gcc-toolchain=" + str(sdk / "root/usr"), "--extra-arg=-resource-dir=" + str(sdk / "root/usr/lib/llvm-14/lib/clang/14.0.6")] + argv = ["/usr/bin/python3", str(source / "tools/compiler-limit.py"), *argv] + else: + argv = commands[current] + stamp = str(time.time_ns()) + log = evidence / (preset + "-" + current + "-" + stamp + ".log") + timing = log.with_suffix(".time") + start = time.monotonic() + peak = baseline + samples = [] + with log.open("w") as output: + child = subprocess.Popen(argv, cwd=source, env=env, stdout=output, stderr=subprocess.STDOUT) + while child.poll() is None: + size = allocated(roots) + peak = max(peak, size) + available = shutil.disk_usage(workspace).free + samples.append({"elapsed": round(time.monotonic() - start, 3), "candidate_bytes": size, "free": available}) + if size - baseline > 768 * 1024 * 1024 or sdk_bytes + size > 2000000000 or available < 2 * 1024 ** 3 or time.monotonic() - start > 300: + child.terminate() + try: + child.wait(timeout=3) + except subprocess.TimeoutExpired: + child.kill() + raise RuntimeError("Owned compiler phase crossed disk or time budget") + time.sleep(1) + code = child.wait() + usage = resource.getrusage(resource.RUSAGE_CHILDREN) + timing.write_text(json.dumps({"measurement": "Python RUSAGE_CHILDREN after wait; cumulative maximum across this invocation", "max_rss_kib": usage.ru_maxrss, "user_s": usage.ru_utime, "system_s": usage.ru_stime}) + "\n") + receipt = {"preset": preset, "phase": current, "command": argv, "code": code, "duration_s": round(time.monotonic() - start, 3), "baseline_candidate_bytes": baseline, "peak_candidate_bytes_lower_bound": peak, "sample_interval_s": 1, "sdk_bytes": sdk_bytes, "samples": samples, "log": str(log), "timing": str(timing)} + log.with_suffix(".json").write_text(json.dumps(receipt, indent=2) + "\n") + print(json.dumps({key: receipt[key] for key in ("preset", "phase", "code", "duration_s", "peak_candidate_bytes_lower_bound", "log", "timing")}), flush=True) + receipts.append(receipt) + if code: + print("".join(log.read_text().splitlines(keepends=True)[-35:]), file=sys.stderr) + raise SystemExit(code) diff --git a/cpp/tools/compiler-limit.py b/cpp/tools/compiler-limit.py new file mode 100644 index 000000000..229a29f67 --- /dev/null +++ b/cpp/tools/compiler-limit.py @@ -0,0 +1,7 @@ +#!/usr/bin/python3 +"""Replace this process with the one compiler under the approved VM ceiling.""" +import os +import resource +import sys +resource.setrlimit(resource.RLIMIT_AS, (1073741824, 1073741824)) +os.execv(sys.argv[1], sys.argv[1:]) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 5d21610a5..9b7e08b30 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -2658,3 +2658,17 @@ The offline copy/capture operator uses the canonical Python role from the existi The strict profile grants root-directory data and data for only the validated private output-anchor directory, plus existing explicit immutable source/artifact, canonical role, source-file and new-output rules. Literal directory data reveals names only and never authorizes descendant file bytes. Bun SQLite/CoreFoundation also requires one exact root-owned readonly ICU data file. Its bytes, size and hash are bound through the existing immutable files inventory; the profile emits one literal file-read-data rule plus exact metadata, never a directory/subtree or general file-read grant. Current Mac receipt is icudt78l.dat, 36,103,568 bytes, SHA-256 09781b9fdd79428c724099159f85dc522b07ddbf51cb7c23d16606a254d758f1. All unrelated read/write/source-write/exec/outbound denials remain required, including a direct forbidden sibling-file test. Exploratory xcode-select/libxcrun permissions are not production policy. Existing per-launch SQLite temporary storage is bound internally to the already approved new private output root. This introduces no public setting or new environment name. Actual strict SQLite/ICU runtime qualification passed on the frozen seven-file candidate: five helper cases and five real Darwin boundary cases, evidence bef89a59-bc75-430f-bdb4-a13f4de84165. The executed immutable launcher hash and 0400 identity were checked before/after success and malformed-input runs. Fixture seals a*/b* remain synthetic and are never reported as application Git identities. Final selected static gates passed on commit4980be305a08a929c743a9bd82017d17779474aa/tree966175f1f015ee062893036b0b46f73222052b99; integration and candidate gates remain separate. These source/tooling decisions authorize no private capture read, credential change, service action, live adoption or production deployment. + +## C++ Pi standby pilot remains a private source comparison - 2026-11-13 + +The owner authorized a separate C++20 direct DeepSeek fixture candidate under `cpp/`, after the shared TypeScript real-socket foundation merged at `db1d78cf9037775ba29b8aa72e89f2b2dbd6ba80`. The deployed TypeScript Chat-backed Responses behavior remains the oracle: preserve full history, call-ID and namespace relationships, reasoning and measured usage, source quota reservation/defer/release/dispatch phases, semantic terminal truth and physical delivery as separate facts. Preserve the captured bare-name namespace replay and immediate peer-FIN interruption rather than silently repairing them during the port. + +The pilot uses one IO loop, explicit reduced limits, a constructor-bound provider destination, and a separately marked synthetic authority with an exclusive checksummed journal. It never opens or converts production Bun/Deno KV, installs automation, replays generation on restart, refreshes credentials or grants production standby authority. The packaged SDK stays read-only; a task-owned CMake executable view preserves that SDK's loader for generated commands. Compiler memory and actual Pi service memory are measured separately. Root still owns independent resource/client/soak acceptance and delivery; a build, fixture pass or health response authorizes no production activation or provider-credit spend. + +## Standalone pilot and platform test selection follows consumed inputs - 2026-11-13 + +Research evidence and standalone CMake pilot changes use first-party import/literal-file reachability before selecting Bun work; consumed data and real configs stay conservative, and CPP TS/JS adapters retain static checks. Private-copy confinement is selected through an explicit affected Darwin segment and is never run by Linux main/portable profiles; existing Darwin job fields and named-file command boundaries remain intact. The pinned synthetic TS/Codex replay is a committed script entrypoint with a fresh native KV reopen receipt, not a Knip suppression or ordinary CI requirement for a host-installed Codex binary. + +The first C++ pilot is explicitly `stream: true` only, matching the qualified pinned Codex profile. False or missing stream requests refuse before provider dispatch with a standard OpenAI-shaped validation error and source-compatible authenticated reserve/release counts. Buffered TypeScript behavior remains unchanged and unsupported in this pilot; supporting it or claiming a general OpenAI replacement requires a separate accepted scope. + +The loopback C++ resource profile requests16KiB downstream `SO_SNDBUF` and records the effective kernel value. Its existing inherited fixture read-state exposes request/write/read counters so a namespace-local paused reader can prove pressure, stable upstream reads and eventual drain/cancellation. This is part of bounded transport acceptance, not a new public control surface. diff --git a/docs/research/cpp-pi-standby-pilot-2026-11-13.json b/docs/research/cpp-pi-standby-pilot-2026-11-13.json new file mode 100644 index 000000000..50dd10d21 --- /dev/null +++ b/docs/research/cpp-pi-standby-pilot-2026-11-13.json @@ -0,0 +1,568 @@ +{ + "schema": "uos-cpp-pi-pilot-report/v1", + "candidate": { + "sourceCommit": "7069c31b3f50d0552ef77e99b984f59fde479b13", + "sourceTree": "827f3281deaa0b36002d710c172a6b0e43d89a38", + "strippedSha256": "bbb18300ea225e2803bc1f95ce5576b42af862c135c828667fd163a191c5bfd3", + "bytes": 991848, + "profile": { + "stream": true, + "unsupportedStreamFalseOrMissing": true, + "model": "deepseek-flash", + "upstream": "official DeepSeek Chat translated to Responses", + "active": 2, + "waiting": 0, + "headerSockets": 8, + "requestBytes": 4194304, + "upstreamEventBytes": 1048576, + "terminalBytes": 2097152, + "headerBytes": 16384, + "jsonDepth": 64, + "authority": "synthetic fixture credential/quota/journal; not production Bun/Deno KV compatibility" + } + }, + "verdicts": { + "gatewayNamespaceBudget": "PASS", + "observedHostReserve": "PASS", + "prefixSoakPostOverloadAndIdle": "PASS", + "wholeTransportZeroNewSwap": "NOT_MET_IN_OBSERVED_INTERVAL; INITIAL_ATTRIBUTION_UNPROVED", + "productionStandby": "NOT_QUALIFIED" + }, + "physicalPi": { + "prefixRows": [ + { + "name": "cold_idle", + "operation": "idle", + "elapsed_seconds": 5.081593, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 0, + "settlements": 0, + "unsettled": 0, + "CPP_RSS_kib": 7960, + "CPP_PSS_kib": 3949, + "sampled_current_bytes": 20418560, + "cumulative_peak_bytes": 20856832, + "host_MemAvailable_kib": 178944 + }, + { + "name": "native_two_turn", + "operation": "native", + "elapsed_seconds": 2.239521, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 2, + "settlements": 2, + "unsettled": 0, + "CPP_RSS_kib": 8880, + "CPP_PSS_kib": 4745, + "sampled_current_bytes": 21213184, + "cumulative_peak_bytes": 21860352, + "host_MemAvailable_kib": 178688 + }, + { + "name": "warm_idle", + "operation": "idle", + "elapsed_seconds": 10.073461, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 2, + "settlements": 2, + "unsettled": 0, + "CPP_RSS_kib": 8880, + "CPP_PSS_kib": 4853, + "sampled_current_bytes": 21213184, + "cumulative_peak_bytes": 21860352, + "host_MemAvailable_kib": 187936 + }, + { + "name": "one_stream", + "operation": "batch", + "elapsed_seconds": 3.31634, + "N": 20, + "p95_ms_recomputed_fullN": 145.73189099999945, + "reported_helper_p95_ms": 211.37294999999722, + "posts": 22, + "settlements": 22, + "unsettled": 0, + "CPP_RSS_kib": 9136, + "CPP_PSS_kib": 5109, + "sampled_current_bytes": 21733376, + "cumulative_peak_bytes": 22441984, + "host_MemAvailable_kib": 190980 + }, + { + "name": "two_streams", + "operation": "batch", + "elapsed_seconds": 1.798725, + "N": 20, + "p95_ms_recomputed_fullN": 155.52779299999747, + "reported_helper_p95_ms": 158.53037299999778, + "posts": 42, + "settlements": 42, + "unsettled": 0, + "CPP_RSS_kib": 9140, + "CPP_PSS_kib": 5113, + "sampled_current_bytes": 22179840, + "cumulative_peak_bytes": 22798336, + "host_MemAvailable_kib": 191028 + }, + { + "name": "accepted_large_1536k", + "operation": "large", + "elapsed_seconds": 1.741167, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 43, + "settlements": 43, + "unsettled": 0, + "CPP_RSS_kib": 15824, + "CPP_PSS_kib": 11797, + "sampled_current_bytes": 28897280, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 189880 + }, + { + "name": "accepted_summary_720k", + "operation": "large", + "elapsed_seconds": 0.880402, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 44, + "settlements": 44, + "unsettled": 0, + "CPP_RSS_kib": 9240, + "CPP_PSS_kib": 5213, + "sampled_current_bytes": 21901312, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 196820 + }, + { + "name": "slow_reader", + "operation": "slow_reader", + "elapsed_seconds": 2.040373, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 46, + "settlements": 46, + "unsettled": 0, + "CPP_RSS_kib": 11664, + "CPP_PSS_kib": 7637, + "sampled_current_bytes": 24571904, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 192976 + }, + { + "name": "repeated_cancel", + "operation": "cancel", + "elapsed_seconds": 1.541645, + "N": 10, + "p95_ms_recomputed_fullN": 157.4041249999973, + "reported_helper_p95_ms": 157.4041249999973, + "posts": 56, + "settlements": 56, + "unsettled": 0, + "CPP_RSS_kib": 11664, + "CPP_PSS_kib": 7637, + "sampled_current_bytes": 24801280, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 192868 + }, + { + "name": "four_stream_overload", + "operation": "overload", + "elapsed_seconds": 0.418299, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 58, + "settlements": 58, + "unsettled": 0, + "CPP_RSS_kib": 11664, + "CPP_PSS_kib": 7637, + "sampled_current_bytes": 24899584, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 192420 + }, + { + "name": "four_stream_overload_postsoak", + "operation": "overload", + "elapsed_seconds": 0.424785, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 778, + "settlements": 778, + "unsettled": 0, + "CPP_RSS_kib": 10360, + "CPP_PSS_kib": 6418, + "sampled_current_bytes": 24526848, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 196544 + }, + { + "name": "post_idle", + "operation": "idle", + "elapsed_seconds": 30.078841, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 778, + "settlements": 778, + "unsettled": 0, + "CPP_RSS_kib": 10360, + "CPP_PSS_kib": 6312, + "sampled_current_bytes": 24211456, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 186424 + } + ], + "soak": { + "start_epoch_ms": 1794560337927, + "deadline_epoch_ms": 1794563937927, + "finish_epoch_ms": 1794563937927, + "monotonic_elapsed_seconds": 3600.0002261849586, + "batch_period_seconds": 10, + "concurrency": 2, + "N": 718, + "batches": 359, + "p50_ms": 141.50930499998503, + "p95_ms": 292.40199199999915, + "p99_ms": 762.2158449999988, + "max_ms": 1459.1072420000564, + "timing_surface": "off-Pi client start to first meaningful delta through private SSH ingress; producer waits actual receipt; warmups/native excluded", + "p95_method": "nearest rank ceil(0.95*N), recomputed from all718 retained raw samples" + }, + "allPhaseMemory": { + "gateway_namespace_limit_bytes": 100663296, + "gateway_namespace_swap_limit_bytes": 0, + "cumulative_all_phase_peak_bytes": 34422784, + "cumulative_all_phase_peak_MiB": 32.828125, + "final_held_current_bytes": 13881344, + "all_sampled_gateway_swap_zero": true, + "final_events": "low 0\nhigh 0\nmax 0\noom 0\noom_kill 0\noom_group_kill 0", + "host_internal_1Hz_low_water_kib": 114840, + "host_phase_sample_low_water_kib": 119532, + "required_host_reserve_kib": 65536, + "CPP_pre_exit_kernel_VmHWM_kib": 21220, + "CPP_pre_exit_VmRSS_kib": 10360, + "CPP_pre_exit_VmSwap_kib": 0, + "HWM_observation_utc": "2026-11-13T09:57:29Z", + "outside_helper_HWM_sum_kib": 53212, + "gateway_peak_plus_outside_HWM_bytes": 88911872, + "gateway_peak_plus_outside_HWM_MiB": 84.79296875, + "bound_caveat": "Conservative charged-memory plus per-PID RSS high-water sum, shared mappings double-counted; not physical incremental RAM. Pre-exit HWM coverage ends at09:57:29, final cgroup peak includes normal shutdown.", + "phase_peak_caveat": "Kernel peak was cumulative and not reset; per-phase current samples are lower bounds on phase-only peaks." + }, + "accounting": { + "snapshot_sequence": 3074, + "last_sequence": 3890, + "validated_frames": 816, + "journal_bytes": 364962, + "records": 778, + "dispatched": 778, + "settled": 778, + "reserved": 0, + "quota_transactions": 1556, + "quota_mutations": 3112, + "terminals": { + "response.completed": 767, + "response.failed": 1, + "cancelled": 10 + }, + "delivery": { + "delivered": 767, + "interrupted": 11 + } + }, + "outsideHelpers": { + "sampleUTC": "2026-11-13T09:57:29Z", + "rows": [ + { + "name": "sshd", + "VmHWM_kib": 9808, + "VmRSS_kib": 9808, + "VmSwap_kib": 100, + "continuity": true + }, + { + "name": "sshd", + "VmHWM_kib": 6724, + "VmRSS_kib": 6180, + "VmSwap_kib": 484, + "continuity": true + }, + { + "name": "systemd-run", + "VmHWM_kib": 6204, + "VmRSS_kib": 3680, + "VmSwap_kib": 924, + "continuity": "first-HWM-sample" + }, + { + "name": "python3", + "VmHWM_kib": 10316, + "VmRSS_kib": 2596, + "VmSwap_kib": 5236, + "continuity": "first-HWM-sample" + }, + { + "name": "sshd", + "VmHWM_kib": 9956, + "VmRSS_kib": 9820, + "VmSwap_kib": 92, + "continuity": true + }, + { + "name": "sshd", + "VmHWM_kib": 9032, + "VmRSS_kib": 6396, + "VmSwap_kib": 1048, + "continuity": true + }, + { + "name": "cat", + "VmHWM_kib": 1172, + "VmRSS_kib": 852, + "VmSwap_kib": 88, + "continuity": "first-HWM-sample" + } + ], + "HWM_sum_kib": 53212, + "sharedMasterExcluded": true, + "RSS_and_charge_upper_bound_MiB": 84.79296875, + "bound": "Non-concurrent per-PID HWM plus cgroup charge double-counts shared pages; not physical incremental RAM; HWM coverage ends09:57:29 while cgroup peak includes later shutdown", + "swapSnapshots": { + "firstUTC": "2026-11-13T09:13:59Z", + "laterUTC": "2026-11-13T09:28:04Z", + "firstSSHSmapsSwapKiB": 1724, + "laterSSHSmapsSwapKiB": 1728, + "observedDeltaKiB": 4, + "prestartBaselineAndInheritance": "UNPROVED", + "perProcessDeltasKiB": [0, 0, 0, 4] + } + }, + "overload": [ + { + "statuses": [200, 200, 503, 503], + "codes": ["local_inference_overload", "local_inference_overload"], + "retryAfter": ["1", "1"], + "actualPosts": 2, + "logicalDispatches": 2, + "heldActive": 2, + "heldQueued": 0 + }, + { + "statuses": [200, 200, 503, 503], + "codes": ["local_inference_overload", "local_inference_overload"], + "retryAfter": ["1", "1"], + "actualPosts": 2, + "logicalDispatches": 2, + "heldActive": 2, + "heldQueued": 0 + } + ], + "cleanup": { + "bothUnitsCollected": true, + "cgroupAbsent": true, + "taskPidsRemaining": 0, + "taskListenersRemaining": 0, + "local18799Listeners": 0, + "allHandlesSettled0": true, + "sharedSshdMasterPreserved": true, + "binaryHashUnchanged": true, + "allSevenLibraryHashesMatch": true + } + }, + "builds": [ + { + "label": "pi-release-clean", + "wall_seconds": 121.282365303, + "single_child_peak_rss_kib": 596240, + "cgroup_peak_bytes": 798167040, + "memory_max_events": 0, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 376860672, + "candidate_peak_sampled_lower_bound_bytes": 402448384, + "additional_peak_sampled_lower_bound_bytes": 25587712, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 13, + "link_seconds": 0.539, + "all_ninja_edge_seconds": 120.359, + "ninja_edge_span_seconds": 120.365, + "artifact_sha256": "143d542bb842420f7a5d317de4cf9d91f4b31a643738f80be7670fbe0f6fbbd7", + "one_file_exact": null + }, + { + "label": "pi-release-one-file", + "wall_seconds": 5.538512283, + "single_child_peak_rss_kib": 276108, + "cgroup_peak_bytes": 289214464, + "memory_max_events": 0, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 403992576, + "candidate_peak_sampled_lower_bound_bytes": 404156416, + "additional_peak_sampled_lower_bound_bytes": 163840, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 1, + "link_seconds": 0.461, + "all_ninja_edge_seconds": 4.667, + "ninja_edge_span_seconds": 4.669, + "artifact_sha256": "143d542bb842420f7a5d317de4cf9d91f4b31a643738f80be7670fbe0f6fbbd7", + "one_file_exact": true + }, + { + "label": "native-asan-clean", + "wall_seconds": 154.145103471, + "single_child_peak_rss_kib": 921300, + "cgroup_peak_bytes": 1073741824, + "memory_max_events": 211, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 271097856, + "candidate_peak_sampled_lower_bound_bytes": 399708160, + "additional_peak_sampled_lower_bound_bytes": 128610304, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 13, + "link_seconds": 2.099, + "all_ninja_edge_seconds": 152.974, + "ninja_edge_span_seconds": 152.978, + "artifact_sha256": "c451963df901583d5cdd481b189ca2d2bfb1d70887685a8cb08df1fe90590d9b", + "one_file_exact": null + }, + { + "label": "native-asan-one-file", + "wall_seconds": 7.789552909, + "single_child_peak_rss_kib": 361668, + "cgroup_peak_bytes": 370892800, + "memory_max_events": 0, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 404320256, + "candidate_peak_sampled_lower_bound_bytes": 405008384, + "additional_peak_sampled_lower_bound_bytes": 688128, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 1, + "link_seconds": 1.428, + "all_ninja_edge_seconds": 7.35, + "ninja_edge_span_seconds": 7.35, + "artifact_sha256": "c451963df901583d5cdd481b189ca2d2bfb1d70887685a8cb08df1fe90590d9b", + "one_file_exact": true + } + ], + "installedFileFootprint": { + "installed": { + "bytes": 991848, + "sha256": "bbb18300ea225e2803bc1f95ce5576b42af862c135c828667fd163a191c5bfd3" + }, + "unstripped": { + "bytes": 6706448, + "sha256": "143d542bb842420f7a5d317de4cf9d91f4b31a643738f80be7670fbe0f6fbbd7" + }, + "builderDebug": { + "bytes": 5717544, + "sha256": "e6a5815ec6ab9f469baf5810f3483a68c8cf63e18d166fcc2d933a0459365557" + }, + "elf": { + "machine": "AArch64", + "class": "ELF64", + "interpreter": "/lib/ld-linux-aarch64.so.1", + "needed": [ + "libssl.so.3", + "libcrypto.so.3", + "libstdc++.so.6", + "libgcc_s.so.1", + "libc.so.6", + "ld-linux-aarch64.so.1" + ], + "maximum_version_requirements": { + "GLIBC": "2.34", + "GLIBCXX": "3.4.29", + "CXXABI": "1.3.13" + }, + "rpath": null, + "runpath": null, + "debuglink": "uos_cpp_pilot.debug", + "builder_paths_absent": true + }, + "addedBinaryPayloadBytes": 991848, + "existingOsLibraryBytes": 10023608, + "osLibrariesBundled": false, + "binaryPlusRequiredLibrariesBytes": 11015456, + "runtimeConfigAndState": "Generated separately; not claimed zero or included in binary-only install", + "libraryHashes": [ + { + "target": "/usr/lib/aarch64-linux-gnu/libssl.so.3", + "bytes": 737192, + "sha256": "09fe45f6e578f6a598b3f67a193d7dc56f679c2259933d90e78a368d594bc8b0" + }, + { + "target": "/usr/lib/aarch64-linux-gnu/libcrypto.so.3", + "bytes": 4532392, + "sha256": "908bfe9966f80a31cec61ec4cbd0661d9fe9673edcca1848e038351e122eff74" + }, + { + "target": "/usr/lib/aarch64-linux-gnu/libstdc++.so.6", + "bytes": 2174296, + "sha256": "532f3a12d7b8fef6cc7a064ea106b6c4ab2365674b73e743e4a385d4ffb6c8ab" + }, + { + "target": "/lib/aarch64-linux-gnu/libgcc_s.so.1", + "bytes": 133448, + "sha256": "046856f95f4636f1fc7c3a12bf4f3cd5634c2fc5145c3fdf7395d4f349fa69c7" + }, + { + "target": "/lib/aarch64-linux-gnu/libc.so.6", + "bytes": 1651408, + "sha256": "31f932525f5373857f0c8b5d94520b841a099e659388763a92484088b2bca235" + }, + { + "target": "/lib/ld-linux-aarch64.so.1", + "bytes": 202912, + "sha256": "7fa5037b2ea1643fd2b8b7f3f3c1d9d546271011b4385aeea3e85fed0d5dcadd" + }, + { + "target": "/lib/aarch64-linux-gnu/libm.so.6", + "bytes": 591960, + "sha256": "a84b0bfeb5abf4ad4eaebcd4a69a783736c2319c4f4fcd5d678b29c57d441951" + } + ] + }, + "staticAndLocalReceipts": { + "node": "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/f87ecea8-bcb2-4ada-94e0-7e1dc5d67c92", + "core": "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/8b0902cc-1c71-4f4c-8425-e937f586b498", + "TLSAndPressure": [ + "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/d7858bb1-7fc4-4043-90fb-a36697ad89b7", + "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/8452eda1-6019-4596-a64a-f5fc70e28ce1" + ] + }, + "remainingGaps": [ + "Whole-transport zero-new-swap not met/unproved; source of initial helper swap unproved", + "Production credentials/state/codec import, distributed revocation/quota and single-writer failover not implemented/qualified", + "Public/independent ingress and complete VPS-host outage unproved; current mock/client require VPS", + "Power-loss/host-reboot durability unmeasured; local crash tests and stopped journal readback are separate", + "Development-host C++ primary memory/p95 curves unmeasured; unlike-host/feature comparisons cannot imply language-level RAM savings", + "Exact final filesystem type not restated in sealed summary; disk-backed Pi/tmp provenance retained" + ], + "recommendations": { + "piRedundancy": "Best currently measured Pi runtime candidate within the declared C++ profile; production redundancy remains conditional and not activated. A verified small Rust fix that meets Pi acceptance retains user preference; none was provided here.", + "nextSmallVpsUtility": "Recommend ONE bounded C++ utility pilot with explicit ownership, compatibility, build/disk and sanitizer gates; no default or language-wide rewrite." + }, + "receiptFiles": ["pi-receipt.json", "build-receipt.json", "local-receipt.json", "sources.json"] +} diff --git a/docs/research/cpp-pi-standby-pilot-2026-11-13.md b/docs/research/cpp-pi-standby-pilot-2026-11-13.md new file mode 100644 index 000000000..b377f9bcc --- /dev/null +++ b/docs/research/cpp-pi-standby-pilot-2026-11-13.md @@ -0,0 +1,101 @@ +# C++ Pi standby pilot — qualification report, 2026-11-13 + +**Gateway/namespace budget, native/raw prefix, one-hour soak and post-soak controls passed. Strict whole-transport zero-new-swap was NOT MET/UNPROVED. Production standby is not qualified or activated.** The C++ route is the best currently measured Pi runtime candidate for this bounded profile; the separate recommendation is one small C++ VPS utility pilot, not a default rewrite. + +## Six requested deliverables + +| Deliverable | Result | +| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1. Pinned TS/Rust source, runtimes, binary identities and profiles | Recorded below and in the existing PR1 baseline; historical Rust is not substituted with an unbuilt current lane. | +| 2. Repeatable two-turn Codex fixture, real disconnect and accounting | Shared committed raw/native fixture, actual Codex first pair through Pi SSH ingress, local/final pressured cancellation and stopped journal accounting qualified. | +| 3. TS-versus-Rust results, mismatches and uncovered behavior | Preserved: bounded VPS results plus the historical Rust upstream-release failure and accounting/delivery gaps. | +| 4. Actual Pi-userland C++ release, reproducible build and sanitizer results | Exact stripped artifact ran on Pi; ABI/library readback matched; four measured builds and release/native ASan+UBSan 6+6 CTests passed. | +| 5. Memory, latency and build/disk scorecard | Exact scope/method tables below; missing or differently bounded measures remain explicit. | +| 6. Two separate decisions | C++ Pi candidate conditional on unmet transport/production gaps; yes to one bounded new utility pilot, no default rewrite. | + +## Frozen identities and scope + +Candidate source **`7069c31b3f50d0552ef77e99b984f59fde479b13`**, tree **`827f3281deaa0b36002d710c172a6b0e43d89a38`**. Installed stripped SHA-256 **`bbb18300ea225e2803bc1f95ce5576b42af862c135c828667fd163a191c5bfd3`**, **991,848 B**. Full source identity/`cpp-` and clean-build identity were asserted. The physical controller workspace was7aafe3de5f73237dd428c4e8677eecd6961ea1f8; it is distinct from candidate source. + +One loopback POST /v1/responses supports **stream:true only**, one canonical `deepseek-flash` via official Chat-to-Responses translation, two active requests, zero waiters and eight header sockets. Reduced limits are4MiB request,1MiB upstream event,2MiB complete terminal,16KiB headers and depth64. Missing/false stream is a declared refusal, not buffered API parity. Synthetic credential/quota/custom journal authority is not production credentials, Bun-JSC/Deno-V8 KV compatibility, subscription routing or distributed state. + +Pi state used disk-backed /tmp per host preflight; the sealed summary does not restate its exact filesystem type, which remains an explicit metadata gap. VPS baseline scratch was tmpfs, so persistence timing is not interchangeable. Actual Pi controls were memory.max/high96MiB, swap0, cpu.max90000/100000 and pids.max256. + +All inference work used synthetic providers; local TLS used private test CAs. No live-provider generation, credit, quota or production-network latency is established. The shared fixtures and opt-in CLI are committed source; use `cpp/README.md`, `cpp/tools/build.py` and the shared conformance helpers with the pinned SDK/native bindings. Exact executed argv, profiles and original-artifact hashes remain in the selected provenance; this report adds no source/replay framework. + +| Earlier bounded VPS reference | Warm RSS MiB c1/c2 | Held RSS MiB c1/c2 | Raw p95 ms c1/c2 | +| ------------------------------------------------------ | ------------------ | ------------------ | ---------------- | +| TS d94 application / measured96cf foundation, Bun1.4.0 | 85.621 /89.586 | 86.055 /90.734 | 34.827 /62.067 | +| Historical Rust b588 WASM / workerd1.20261012.1 | 287.496 /275.133 | 291.613 /280.191 | 231.545 /373.316 | + +TS source d94fef52ccc76e0d5aa019154f11e47d5e10fba6, measured fixture96cfca54d033aedff8e237a4cbcebb051f62f066/tree7ae301184cfe261d45b1add6e0587e77d1f26688; Bun SHA086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9. Rust sourceb588420dc1275ac1f7548a6932a5e63330751cea/treec9bf439743b2f03d4761639fedd17ef2731430d3; WASM SHA99a3e146e0193c3c0d77847167374ecf1cef5834e8e123420b7a395e6df57f53; workerd SHAfabfd6c40a3bb74a98aac54999b456bdb3b7aa7ede4cbbb33898e1f8836a2564. Rust profile LTO=true/codegen-units1/opt-level=s/strip=debuginfo. Its disconnect released upstream only at the5s mock deadline (~4878ms after disconnect); delivery stayed unobserved and API/kernel/paid reservations were absent. These N20 warmup-excluded VPS numbers, older builds/features and unlike SSH/Pi hardware are not a language-only comparison or the owner's production700MB/350MB estimate. + +## Physical Pi scorecard + +Full-pipeline child0 ran3666.630s. Native Codex0.167.2 SHA b581df3989da98e36fccd7d40cc980727a9ee87bcf0e6ef52ffb2544d6a5302f was the first two-request inference pair through SSH; exact tool/result/final readback passed. The following RSS/PSS/current samples are phase-end observations. **Kernel peak is cumulative across all phases, not a per-phase or soak-only peak.** + +| Phase | CPP RSS/PSS KiB | Group current / cumulative peak MiB | N / full-N p95 ms | POSTs / settlements, cumulative | +| ----------------------------- | --------------- | ----------------------------------- | ----------------- | ------------------------------- | +| cold_idle | 7960 /3949 | 19.473 /19.891 | — | 0 /0 | +| native_two_turn | 8880 /4745 | 20.230 /20.848 | — | 2 /2 | +| warm_idle | 8880 /4853 | 20.230 /20.848 | — | 2 /2 | +| one_stream | 9136 /5109 | 20.727 /21.402 | 20 /145.732 | 22 /22 | +| two_streams | 9140 /5113 | 21.152 /21.742 | 20 /155.528 | 42 /42 | +| accepted_large_1536k | 15824 /11797 | 27.559 /32.828 | — | 43 /43 | +| accepted_summary_720k | 9240 /5213 | 20.887 /32.828 | — | 44 /44 | +| slow_reader | 11664 /7637 | 23.434 /32.828 | — | 46 /46 | +| repeated_cancel | 11664 /7637 | 23.652 /32.828 | 10 /157.404 | 56 /56 | +| four_stream_overload | 11664 /7637 | 23.746 /32.828 | — | 58 /58 | +| four_stream_overload_postsoak | 10360 /6418 | 23.391 /32.828 | — | 778 /778 | +| post_idle | 10360 /6312 | 23.090 /32.828 | — | 778 /778 | + +The large cases preserve exact1.5MiB transcript/720KiB summary projection. Ten repeated cancellations and actual paused-receiver pressure passed; Pi pressured cancellation released upstream in32.430687ms (a single cancellation observation, not p95). Kernel send buffer was32768 B; same-epoch reads stayed111/111 or139/139 while a downstream write was pending. Both four-request overload rounds held exactly2 active/0queued and dispatched2POSTs; the other2 returned503/local_inference_overload/Retry-After1. Both accepted streams then completed and drained. + +The soak ran08:58:57.927–09:58:57.927 UTC, **3600.000226s**,359 two-stream batches at10s intervals, **N718**. Nearest-rank p50/p95/p99/max were **141.509305 /292.401992 /762.215845 /1459.107242ms**. Timing is off-Pi client start to the first parsed meaningful delta through private SSH ingress before producer ACK; warmups/native excluded. Although some helper fields are named TTFB, this boundary is explicitly first-meaningful-delta, not a native-Codex percentile or unconstrained throughput. Prefix p95 uses independently recomputed full N20, not the helper's last sub-batch percentile. + +## Resource verdict and explicit swap gap + +Gateway+namespace ALL-PHASE peak was **34,422,784 B =32.828125MiB**, under actual96MiB/zero-swap limits; all high/max/OOM counters were0. The group includes namespace/relay/control helpers. Host internal1Hz observed MemAvailable low was **114,840KiB =112.1484375MiB**, above the64MiB reserve; phase-only samples had a higher low119,532KiB. Sampling does not prove an absolute continuous minimum. CPP's pre-exit kernel VmHWM was21,220KiB (20.722656MiB), VmRSS10,360KiB, VmSwap0 at09:57:29; later shutdown is included only in the final cgroup peak. + +**Strict whole-transport zero-new-swap: NOT MET in the observed interval; initial attribution/inheritance UNPROVED.** Outside SSH/guard/systemd-run/cat helpers had swap. Four tracked SSH processes had smaps swap1724→1728KiB between09:13:59 and09:28:04, including one+4KiB delta. Those are mid-run snapshots, not process-start baselines; do not label earlier pages inherited or conclude whole-transport zero-new-swap. Gateway/namespace swap remained0. The existing shared SSH master is excluded and was preserved. + +| Outside helper at09:57:29 | VmHWM /VmRSS /VmSwap KiB | +| ------------------------- | ------------------------ | +| sshd | 9808 /9808 /100 | +| sshd | 6724 /6180 /484 | +| systemd-run | 6204 /3680 /924 | +| python3 | 10316 /2596 /5236 | +| sshd | 9956 /9820 /92 | +| sshd | 9032 /6396 /1048 | +| cat | 1172 /852 /88 | + +Outside helper HWM sum53,212KiB plus gateway-group charge peak gives **84.79296875MiB**. This is a conservative non-concurrent RSS/charged-memory sum with shared-page double counting, **not physical incremental RAM**. Outside HWM coverage ends09:57:29 and is not an independently complete outside-helper lifetime peak. This bound cannot erase the swap criterion gap. + +Final actual POST/logical dispatch/settlement was **778/778/778**:767 delivered and11 interrupted; reserved/unsettled/active/queued all0. Terminals were767completed,1failed and10cancelled. Independent stopped-state validation combined snapshot high-water3074 with816 valid remaining journal frames through sequence3890,778unique dispatched/settled records,1556quota transactions/3112mutations. No generation replay/refund was introduced. Snapshot/journal hashes and sizes are in the selected receipt; private state files are not published. + +Independent readback confirmed both units not-found/inactive/dead, no cgroup/task PID/Unix listener/local18799listener, all handles settled0, shared master alive and final binary/all7OS library hashes unchanged. The two earlier failed Pi attempts and their separate58-dispatch/56-settlement ledger remain failed, outside the successful cohort. + +## Build, package and static/local qualification + +| Build case | Wall s | Child RSS /group peak MiB | Extra sampled disk MiB | TU /link s | +| -------------------- | ------- | ------------------------- | ---------------------- | ---------- | +| pi-release-clean | 121.282 | 582.27 /761.19 | 24.40 | 13 /0.539 | +| pi-release-one-file | 5.539 | 269.64 /275.82 | 0.16 | 1 /0.461 | +| native-asan-clean | 154.145 | 899.71 /1024.00 | 122.65 | 13 /2.099 | +| native-asan-one-file | 7.790 | 353.19 /353.71 | 0.66 | 1 /1.428 | + +Clean builds compiled13 TUs; one-file builds compiled only payload_policy.cpp plus archive/gateway/protocol links. Both profiles passed6/6 post-incremental CTests; native ASan+UBSan/leak/warnings/focused tidy passed. Native clean hit its1GiB cap with211memory.max events but0OOM/swap; do not call that uncapped cost. Configured caches remained warm;1Hz disk peaks are lower bounds, wall includes observer/exit quantization, child RSS and group/cache peak differ. Compiler/test RAM is not gateway RAM. + +Added stripped binary payload991,848 B; unstripped6,706,448 B and builder debug5,717,544 B remain separate. Already-existing OS library/loader closure10,023,608 B is not bundled; binary+required libraries11,015,456 B excludes separately generated runtime config/state. SDK1,230,209,024 B +owned outputs412,377,088 B =1,642,586,112 B; free snapshot3,083,436,032 B. CPP tracked source177,888 B; measured scope including Decisions478,631 B. ELF64/AArch64 interpreter/lib closure, GLIBC2.34/GLIBCXX3.4.29/CXXABI1.3.13 maxima, no RPATH/builder paths and basename debuglink were audited; final Pi library hashes matched. + +Full no-emit TypeScript6.0.3 passed unchanged compiler/config/types/source on supported pinned Node24.20.0, exit0/72.051s, ~1254MiB child RSS/~1251MiB group peak, no max/OOM/swap under actual2GiB cap. First-party TS/application stays Bun. Both diagnostic-free240s/360s Bun timeouts remain incomplete. Frozen local staged core/native/header, TLS3 and pressure2 passed exactBBB: zero selected header/body/event/terminal differences; correct SNI and fail-closed wrong SAN/untrusted CA; no retry; raw SO_SNDBUF32768 and6.048879ms local pressured cancel, one charge/settlement and drained state. Local128MiB/outer768MiB controls are distinct from physical Pi96MiB. Dirty prototypes were never promoted. + +## Two recommendations and remaining scope + +**Pi redundancy:** C++ is the best currently measured runtime candidate for this bounded Pi profile, with completed physical-Pi evidence. No verified small Rust fix meeting the Pi requirements was supplied; user preference for such a fix remains. Production redundancy is **conditional, not activated**: strict whole-transport zero-new-swap is unmet/unproved, production credential/state/codec import, revocation/quota ownership, single-writer failover and independent public ingress are unimplemented/unqualified. Mock/client currently require VPS, so this is not a full VPS-host-outage proof. Process crash/reopen and fsync tests are not power-loss or host-reboot durability proof. + +**Next small VPS utility:** recommend one narrowly owned C++ utility pilot using the demonstrated build/sanitizer/compatibility discipline and measured build/disk tradeoffs. Do not infer a language-wide RAM win, choose a default rewrite, expand the provider/feature surface or install persistent automation. Development-host C++ primary memory/p95 curves and live-provider inference remain UNMEASURED; the unlike TS/Rust/physical-Pi profiles cannot supply them. + +PR1366 shipped the real opt-in CLI/scope repair and permanent ignore protection for9existing env/backup paths; root verified no tracked secrets and removed its temporary guard. Historical hosted outcomes remain separate: PR1365 CI43355368629 failed misplaced Darwin cases plus a new Knip finding; PR1366 CI43368578978 passed portable/Knip/docs/mac-containment but failed pinned Darwin utility and real macOS signing/private-copy gates. Earlier local reviews were unavailable without terminal verdicts or ingested findings; they are not clean reviews and remain non-gating. PR1368 publication and its delivery receipt are root-owned; no new hosted run or deployment was performed by this report task. + +Receipt namespace is c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c. Pi:ae9e0f0b-6924-4833-829c-0c446d160c4b; build:eac26a0e-15ca-426f-8f8b-b0ea792b5262; package:6764590e-a563-4974-aba7-f34a021e5344; static:f87ecea8-bcb2-4ada-94e0-7e1dc5d67c92; core:8b0902cc-1c71-4f4c-8425-e937f586b498; TLS:d7858bb1-7fc4-4043-90fb-a36697ad89b7; pressure:8452eda1-6019-4596-a64a-f5fc70e28ce1. The compact scorecard and selected sanitized receipts retain exact values and source hashes. No keys, raw native prompts, private configs/databases or new source bundle are published. Publication gates belong to the publishing commit/tree's delivery receipt; runtime receipts retain their exactF/G/BBB identities. diff --git a/docs/research/cpp-pi-standby-pilot-2026-11-13/build-receipt.json b/docs/research/cpp-pi-standby-pilot-2026-11-13/build-receipt.json new file mode 100644 index 000000000..ee16ea52a --- /dev/null +++ b/docs/research/cpp-pi-standby-pilot-2026-11-13/build-receipt.json @@ -0,0 +1,230 @@ +{ + "sourceCommit": "7069c31b3f50d0552ef77e99b984f59fde479b13", + "sourceTree": "827f3281deaa0b36002d710c172a6b0e43d89a38", + "clean": true, + "sourceBytesUnchanged": true, + "builds": [ + { + "label": "pi-release-clean", + "wall_seconds": 121.282365303, + "single_child_peak_rss_kib": 596240, + "cgroup_peak_bytes": 798167040, + "memory_max_events": 0, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 376860672, + "candidate_peak_sampled_lower_bound_bytes": 402448384, + "additional_peak_sampled_lower_bound_bytes": 25587712, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 13, + "link_seconds": 0.539, + "all_ninja_edge_seconds": 120.359, + "ninja_edge_span_seconds": 120.365, + "artifact_sha256": "143d542bb842420f7a5d317de4cf9d91f4b31a643738f80be7670fbe0f6fbbd7", + "one_file_exact": null + }, + { + "label": "pi-release-one-file", + "wall_seconds": 5.538512283, + "single_child_peak_rss_kib": 276108, + "cgroup_peak_bytes": 289214464, + "memory_max_events": 0, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 403992576, + "candidate_peak_sampled_lower_bound_bytes": 404156416, + "additional_peak_sampled_lower_bound_bytes": 163840, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 1, + "link_seconds": 0.461, + "all_ninja_edge_seconds": 4.667, + "ninja_edge_span_seconds": 4.669, + "artifact_sha256": "143d542bb842420f7a5d317de4cf9d91f4b31a643738f80be7670fbe0f6fbbd7", + "one_file_exact": true + }, + { + "label": "native-asan-clean", + "wall_seconds": 154.145103471, + "single_child_peak_rss_kib": 921300, + "cgroup_peak_bytes": 1073741824, + "memory_max_events": 211, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 271097856, + "candidate_peak_sampled_lower_bound_bytes": 399708160, + "additional_peak_sampled_lower_bound_bytes": 128610304, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 13, + "link_seconds": 2.099, + "all_ninja_edge_seconds": 152.974, + "ninja_edge_span_seconds": 152.978, + "artifact_sha256": "c451963df901583d5cdd481b189ca2d2bfb1d70887685a8cb08df1fe90590d9b", + "one_file_exact": null + }, + { + "label": "native-asan-one-file", + "wall_seconds": 7.789552909, + "single_child_peak_rss_kib": 361668, + "cgroup_peak_bytes": 370892800, + "memory_max_events": 0, + "oom": 0, + "oom_kill": 0, + "swap_peak_bytes": 0, + "candidate_baseline_bytes": 404320256, + "candidate_peak_sampled_lower_bound_bytes": 405008384, + "additional_peak_sampled_lower_bound_bytes": 688128, + "sample_interval_s": 1, + "ninja_edge_method": "exact appended Ninja log records", + "compiled_tus": 1, + "link_seconds": 1.428, + "all_ninja_edge_seconds": 7.35, + "ninja_edge_span_seconds": 7.35, + "artifact_sha256": "c451963df901583d5cdd481b189ca2d2bfb1d70887685a8cb08df1fe90590d9b", + "one_file_exact": true + } + ], + "postIncrementalTests": [ + { + "preset": "pi-release", + "exit": 0, + "tests_passed": 6, + "wall_seconds": 21.995190062, + "cgroup_peak_bytes": 131174400, + "swap_peak_bytes": 0 + }, + { + "preset": "native-asan", + "exit": 0, + "tests_passed": 6, + "wall_seconds": 25.440632139, + "cgroup_peak_bytes": 184057856, + "swap_peak_bytes": 0 + } + ], + "tools": { + "pi-release": { + "compiler_id": "GNU", + "compiler_version": "12.2.0", + "cmake_version": "3.25.1", + "compile_commands_sha256": "4df68676d7f8279e92c1308d6b03d78df8019cc6018e9619b4948252c7cc19bb", + "cache_sha256": "5cb23c42c5a1238cbbbc2e774640b5589582b3649bc47765106abec2e30376e7" + }, + "native-asan": { + "compiler_id": "GNU", + "compiler_version": "16.1.1", + "cmake_version": "3.25.1", + "compile_commands_sha256": "82eaf94692453afa38bdc8f39a1f6de550969d2db1a1789759354a0da44f1f55", + "cache_sha256": "72e742c2b8027e993acf1746c81500302646e5655759e3b4aaa1d007d6c72eb6" + } + }, + "refs": [ + "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/eac26a0e-15ca-426f-8f8b-b0ea792b5262", + "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/6764590e-a563-4974-aba7-f34a021e5344" + ], + "package": { + "installed": { + "bytes": 991848, + "sha256": "bbb18300ea225e2803bc1f95ce5576b42af862c135c828667fd163a191c5bfd3" + }, + "unstripped": { + "bytes": 6706448, + "sha256": "143d542bb842420f7a5d317de4cf9d91f4b31a643738f80be7670fbe0f6fbbd7" + }, + "builderDebug": { + "bytes": 5717544, + "sha256": "e6a5815ec6ab9f469baf5810f3483a68c8cf63e18d166fcc2d933a0459365557" + }, + "elf": { + "machine": "AArch64", + "class": "ELF64", + "interpreter": "/lib/ld-linux-aarch64.so.1", + "needed": [ + "libssl.so.3", + "libcrypto.so.3", + "libstdc++.so.6", + "libgcc_s.so.1", + "libc.so.6", + "ld-linux-aarch64.so.1" + ], + "maximum_version_requirements": { + "GLIBC": "2.34", + "GLIBCXX": "3.4.29", + "CXXABI": "1.3.13" + }, + "rpath": null, + "runpath": null, + "debuglink": "uos_cpp_pilot.debug", + "builder_paths_absent": true + }, + "addedBinaryPayloadBytes": 991848, + "existingOsLibraryBytes": 10023608, + "osLibrariesBundled": false, + "binaryPlusRequiredLibrariesBytes": 11015456, + "runtimeConfigAndState": "Generated separately; not claimed zero or included in binary-only install", + "libraryHashes": [ + { + "target": "/usr/lib/aarch64-linux-gnu/libssl.so.3", + "bytes": 737192, + "sha256": "09fe45f6e578f6a598b3f67a193d7dc56f679c2259933d90e78a368d594bc8b0" + }, + { + "target": "/usr/lib/aarch64-linux-gnu/libcrypto.so.3", + "bytes": 4532392, + "sha256": "908bfe9966f80a31cec61ec4cbd0661d9fe9673edcca1848e038351e122eff74" + }, + { + "target": "/usr/lib/aarch64-linux-gnu/libstdc++.so.6", + "bytes": 2174296, + "sha256": "532f3a12d7b8fef6cc7a064ea106b6c4ab2365674b73e743e4a385d4ffb6c8ab" + }, + { + "target": "/lib/aarch64-linux-gnu/libgcc_s.so.1", + "bytes": 133448, + "sha256": "046856f95f4636f1fc7c3a12bf4f3cd5634c2fc5145c3fdf7395d4f349fa69c7" + }, + { + "target": "/lib/aarch64-linux-gnu/libc.so.6", + "bytes": 1651408, + "sha256": "31f932525f5373857f0c8b5d94520b841a099e659388763a92484088b2bca235" + }, + { + "target": "/lib/ld-linux-aarch64.so.1", + "bytes": 202912, + "sha256": "7fa5037b2ea1643fd2b8b7f3f3c1d9d546271011b4385aeea3e85fed0d5dcadd" + }, + { + "target": "/lib/aarch64-linux-gnu/libm.so.6", + "bytes": 591960, + "sha256": "a84b0bfeb5abf4ad4eaebcd4a69a783736c2319c4f4fcd5d678b29c57d441951" + } + ] + }, + "sdkAllocatedBytes": 1230209024, + "ownedOutputsAllocatedBytes": { + "cpp-build": 161759232, + "cpp-tmp": 5820416, + "cpp-stage": 1011712, + "cpp-debug": 5730304, + "cpp-evidence": 238026752, + "cpp-tool-view": 28672 + }, + "combinedAllocatedBytes": 1642586112, + "freeSnapshotBytes": 3083436032, + "cppSourceBytes": 177888, + "measuredScopeSourceBytes": 478631, + "caveats": [ + "Configured clean builds retain SDK/CMake/OS filesystem caches; not machine-cold.", + "Wall time includes driver observer overhead and up to about1s exit-poll delay; raw Ninja edge timing is separately retained.", + "Disk1Hz sampled maxima are lower bounds; no filesystem high-water counter was available.", + "RUSAGE_CHILDREN is maximum single-child RSS for one invocation, not aggregate service memory.", + "Fresh cgroups include gate/worker/driver/compiler/linker and file cache; external controller/evidence wrapper overhead is outside those counters.", + "Native clean reached1GiB cgroup cap and max events, so its observed peak is resource-constrained.", + "Required APP no-emit timeout remains a separate root-owned gate gap; physical/client acceptance of this staged hash is independently assigned." + ] +} diff --git a/docs/research/cpp-pi-standby-pilot-2026-11-13/local-receipt.json b/docs/research/cpp-pi-standby-pilot-2026-11-13/local-receipt.json new file mode 100644 index 000000000..2a5e6fe8d --- /dev/null +++ b/docs/research/cpp-pi-standby-pilot-2026-11-13/local-receipt.json @@ -0,0 +1,206 @@ +{ + "binding": { + "schema": "uos-final-staged-acceptance/v1", + "ready": true, + "status": "verified", + "sourceCommit": "7069c31b3f50d0552ef77e99b984f59fde479b13", + "sourceTree": "827f3281deaa0b36002d710c172a6b0e43d89a38", + "exactStrippedBinarySha256": "bbb18300ea225e2803bc1f95ce5576b42af862c135c828667fd163a191c5bfd3", + "bytes": 991848, + "F": { + "core": "passed", + "native": "passed", + "headers": "passed", + "receipt": "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/8b0902cc-1c71-4f4c-8425-e937f586b498" + }, + "G": { + "tls": "passed", + "pressure": "passed", + "rawSendBuffer": 32768, + "receipts": [ + "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/d7858bb1-7fc4-4043-90fb-a36697ad89b7", + "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/8452eda1-6019-4596-a64a-f5fc70e28ce1" + ] + }, + "cleanup": { + "core": { + "state": "LoadState=not-found\nActiveState=inactive\nSubState=dead\nMainPID=0", + "cgroupExists": false + }, + "tls": { + "state": "LoadState=not-found\nActiveState=inactive\nSubState=dead\nMainPID=0", + "cgroupExists": false + }, + "pressure": { + "state": "LoadState=not-found\nActiveState=inactive\nSubState=dead\nMainPID=0", + "cgroupExists": false + } + }, + "preservedFailure": { + "ref": "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/58294add-c5d3-465d-a663-dcd15f29d298", + "scope": "harness metadata missing before candidate launch", + "repaired": "fresh host-context.json only", + "verdict": "failed, never relabeled" + }, + "priorPrototypes": "retained outside final-staged and not reused as final receipts", + "physicalPiAcceptance": "m01/controller-owned, not claimed by these local final-artifact smokes" + }, + "core": [ + { + "name": "raw", + "nativePass": null, + "match": true, + "headerDiffs": [], + "eventDiffCount": 0, + "upstreamDiffCount": 0 + }, + { + "name": "native", + "nativePass": true, + "match": true, + "headerDiffs": [], + "eventDiffCount": 0, + "upstreamDiffCount": 0 + } + ], + "tls": [ + { + "name": "valid", + "verdict": "verified", + "http": 200, + "sni": ["localhost"], + "tcpConnections": 1, + "httpPosts": 1, + "logicalCommitted": 1, + "reserved": 0, + "cppExit": 0 + }, + { + "name": "wrong-san", + "verdict": "verified", + "http": 502, + "sni": ["localhost"], + "tcpConnections": 1, + "httpPosts": 0, + "logicalCommitted": 1, + "reserved": 0, + "cppExit": 0 + }, + { + "name": "untrusted", + "verdict": "verified", + "http": 502, + "sni": ["localhost"], + "tcpConnections": 1, + "httpPosts": 0, + "logicalCommitted": 1, + "reserved": 0, + "cppExit": 0 + } + ], + "pressure": [ + { + "mode": "resume", + "epoch": "io_527b7f7e74619e44b7f4d6c4bc1be4ca", + "pendingBefore": 1, + "rawSendBuffer": 32768, + "readsBefore": 26, + "readsAfter": 26, + "releaseLatencyMs": null, + "final": { + "fixture_authority": true, + "journal_sequence": 5, + "revoked": false, + "committed_requests": 1, + "reserved_requests": 0, + "quota_transactions": 2, + "quota_mutations": 4, + "record_count": 1, + "paid_fallback": "not_applicable", + "kernel_quota": "not_applicable", + "logical_dispatch_count": 1, + "settlement_count": 1, + "unsettled_count": 0, + "active_requests": 0, + "queued_requests": 0, + "upstream_read_starts": 28, + "upstream_read_completions": 28, + "pending_upstream_reads": 0, + "downstream_write_starts": 13, + "downstream_write_completions": 13, + "pending_downstream_writes": 0, + "downstream_write_successes": 13, + "requested_downstream_send_buffer_bytes": 16384, + "actual_downstream_send_buffer_bytes": 32768, + "upstream_paused_for_downstream_write": false + } + }, + { + "mode": "cancel", + "epoch": "io_404183017da74c1a696b9e10ba0c0703", + "pendingBefore": 1, + "rawSendBuffer": 32768, + "readsBefore": 28, + "readsAfter": 28, + "releaseLatencyMs": 6.04887900000017, + "final": { + "fixture_authority": true, + "journal_sequence": 5, + "revoked": false, + "committed_requests": 1, + "reserved_requests": 0, + "quota_transactions": 2, + "quota_mutations": 4, + "record_count": 1, + "paid_fallback": "not_applicable", + "kernel_quota": "not_applicable", + "logical_dispatch_count": 1, + "settlement_count": 1, + "unsettled_count": 0, + "active_requests": 0, + "queued_requests": 0, + "upstream_read_starts": 28, + "upstream_read_completions": 28, + "pending_upstream_reads": 0, + "downstream_write_starts": 8, + "downstream_write_completions": 8, + "pending_downstream_writes": 0, + "downstream_write_successes": 6, + "requested_downstream_send_buffer_bytes": 16384, + "actual_downstream_send_buffer_bytes": 32768, + "upstream_paused_for_downstream_write": false + } + } + ], + "scope": "Final committed stripped artifact local VPS qualification; candidate128MiB/outer768MiB/swap0/CPU1, not Pi resource measurements", + "staticNode": { + "status": "passed", + "ref": "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/f87ecea8-bcb2-4ada-94e0-7e1dc5d67c92", + "sourceCommit": "7069c31b3f50d0552ef77e99b984f59fde479b13", + "sourceTree": "827f3281deaa0b36002d710c172a6b0e43d89a38", + "exactCommand": [ + "/opt/node-lts/bin/node", + "--max-old-space-size=1536", + "node_modules/typescript/bin/tsc", + "--noEmit", + "--project", + "tsconfig.json" + ], + "officialCompilerVersion": "6.0.3", + "nodeVersion": "v24.20.0", + "nodeSha256": "23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477", + "compilerJsSha256": "1c59e77a54b186ec43fa7f3e0d3c4bb15ca5eb5ba43e96b1d3a267139eddd3e3", + "exit": 0, + "captureDurationMs": 72051, + "maxChildRssKiB": 1284248, + "actualLimits": { + "memory.max": "2147483648", + "memory.swap.max": "0", + "cpu.max": "100000 100000", + "pids.max": "128" + }, + "identityUnchangedBeforeAfter": true + }, + "bunTimeouts": "240s/360s diagnostic-free attempts remain incomplete; not retroactively passed", + "noPrivateData": "No keys, auth values, raw native prompts, state databases or source bundle copied" +} diff --git a/docs/research/cpp-pi-standby-pilot-2026-11-13/pi-receipt.json b/docs/research/cpp-pi-standby-pilot-2026-11-13/pi-receipt.json new file mode 100644 index 000000000..f427ad697 --- /dev/null +++ b/docs/research/cpp-pi-standby-pilot-2026-11-13/pi-receipt.json @@ -0,0 +1,445 @@ +{ + "source_commit": "7069c31b3f50d0552ef77e99b984f59fde479b13", + "source_tree": "827f3281deaa0b36002d710c172a6b0e43d89a38", + "stripped_binary": { + "bytes": 991848, + "sha256": "bbb18300ea225e2803bc1f95ce5576b42af862c135c828667fd163a191c5bfd3" + }, + "evidence_ref": "c18ba89e18e8c4c810e8dfab831978ea7ed5aa33d6f343bf3282dea4e12db13c/ae9e0f0b-6924-4833-829c-0c446d160c4b", + "evidence_childCode": 0, + "execution_duration_ms": 3666630, + "soak": { + "start_epoch_ms": 1794560337927, + "deadline_epoch_ms": 1794563937927, + "finish_epoch_ms": 1794563937927, + "monotonic_elapsed_seconds": 3600.0002261849586, + "batch_period_seconds": 10, + "concurrency": 2, + "N": 718, + "batches": 359, + "p50_ms": 141.50930499998503, + "p95_ms": 292.40199199999915, + "p99_ms": 762.2158449999988, + "max_ms": 1459.1072420000564, + "timing_surface": "off-Pi client start to first meaningful delta through private SSH ingress; producer waits actual receipt; warmups/native excluded", + "p95_method": "nearest rank ceil(0.95*N), recomputed from all718 retained raw samples" + }, + "memory": { + "gateway_namespace_limit_bytes": 100663296, + "gateway_namespace_swap_limit_bytes": 0, + "cumulative_all_phase_peak_bytes": 34422784, + "cumulative_all_phase_peak_MiB": 32.828125, + "final_held_current_bytes": 13881344, + "all_sampled_gateway_swap_zero": true, + "final_events": "low 0\nhigh 0\nmax 0\noom 0\noom_kill 0\noom_group_kill 0", + "host_internal_1Hz_low_water_kib": 114840, + "host_phase_sample_low_water_kib": 119532, + "required_host_reserve_kib": 65536, + "CPP_pre_exit_kernel_VmHWM_kib": 21220, + "CPP_pre_exit_VmRSS_kib": 10360, + "CPP_pre_exit_VmSwap_kib": 0, + "HWM_observation_utc": "2026-11-13T09:57:29Z", + "outside_helper_HWM_sum_kib": 53212, + "gateway_peak_plus_outside_HWM_bytes": 88911872, + "gateway_peak_plus_outside_HWM_MiB": 84.79296875, + "bound_caveat": "Conservative charged-memory plus per-PID RSS high-water sum, shared mappings double-counted; not physical incremental RAM. Pre-exit HWM coverage ends at09:57:29, final cgroup peak includes normal shutdown.", + "phase_peak_caveat": "Kernel peak was cumulative and not reset; per-phase current samples are lower bounds on phase-only peaks." + }, + "pressure_cases": [ + { + "mode": "resume", + "epoch": "io_414e3410585e7597d87c484ce1941dbc", + "pending_write_before": 1, + "pending_upstream_read_before": 0, + "read_starts_before_after": [111, 111], + "read_completions_before_after": [111, 111], + "raw_sender_bytes": 32768, + "raw_receiver_bytes": [16384], + "release_latency_ms": null, + "outcome": "delivered", + "capture_bytes": 987226, + "final_active": 0, + "final_unsettled": 0 + }, + { + "mode": "cancel", + "epoch": "io_414e3410585e7597d87c484ce1941dbc", + "pending_write_before": 1, + "pending_upstream_read_before": 0, + "read_starts_before_after": [139, 139], + "read_completions_before_after": [139, 139], + "raw_sender_bytes": 32768, + "raw_receiver_bytes": [16384], + "release_latency_ms": 32.430687000000034, + "outcome": "interrupted", + "capture_bytes": 2048, + "final_active": 0, + "final_unsettled": 0 + } + ], + "source_binary_config_unchanged_during_successful_run": true, + "status": { + "gatewayNamespaceBudget": "PASS", + "observedHostReserve": "PASS", + "prefixSoakPostOverloadAndIdle": "PASS", + "wholeTransportZeroNewSwap": "NOT_MET_IN_OBSERVED_INTERVAL; INITIAL_ATTRIBUTION_UNPROVED", + "productionStandby": "NOT_QUALIFIED" + }, + "phaseSamples": [ + { + "name": "cold_idle", + "operation": "idle", + "elapsed_seconds": 5.081593, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 0, + "settlements": 0, + "unsettled": 0, + "CPP_RSS_kib": 7960, + "CPP_PSS_kib": 3949, + "sampled_current_bytes": 20418560, + "cumulative_peak_bytes": 20856832, + "host_MemAvailable_kib": 178944 + }, + { + "name": "native_two_turn", + "operation": "native", + "elapsed_seconds": 2.239521, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 2, + "settlements": 2, + "unsettled": 0, + "CPP_RSS_kib": 8880, + "CPP_PSS_kib": 4745, + "sampled_current_bytes": 21213184, + "cumulative_peak_bytes": 21860352, + "host_MemAvailable_kib": 178688 + }, + { + "name": "warm_idle", + "operation": "idle", + "elapsed_seconds": 10.073461, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 2, + "settlements": 2, + "unsettled": 0, + "CPP_RSS_kib": 8880, + "CPP_PSS_kib": 4853, + "sampled_current_bytes": 21213184, + "cumulative_peak_bytes": 21860352, + "host_MemAvailable_kib": 187936 + }, + { + "name": "one_stream", + "operation": "batch", + "elapsed_seconds": 3.31634, + "N": 20, + "p95_ms_recomputed_fullN": 145.73189099999945, + "reported_helper_p95_ms": 211.37294999999722, + "posts": 22, + "settlements": 22, + "unsettled": 0, + "CPP_RSS_kib": 9136, + "CPP_PSS_kib": 5109, + "sampled_current_bytes": 21733376, + "cumulative_peak_bytes": 22441984, + "host_MemAvailable_kib": 190980 + }, + { + "name": "two_streams", + "operation": "batch", + "elapsed_seconds": 1.798725, + "N": 20, + "p95_ms_recomputed_fullN": 155.52779299999747, + "reported_helper_p95_ms": 158.53037299999778, + "posts": 42, + "settlements": 42, + "unsettled": 0, + "CPP_RSS_kib": 9140, + "CPP_PSS_kib": 5113, + "sampled_current_bytes": 22179840, + "cumulative_peak_bytes": 22798336, + "host_MemAvailable_kib": 191028 + }, + { + "name": "accepted_large_1536k", + "operation": "large", + "elapsed_seconds": 1.741167, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 43, + "settlements": 43, + "unsettled": 0, + "CPP_RSS_kib": 15824, + "CPP_PSS_kib": 11797, + "sampled_current_bytes": 28897280, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 189880 + }, + { + "name": "accepted_summary_720k", + "operation": "large", + "elapsed_seconds": 0.880402, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 44, + "settlements": 44, + "unsettled": 0, + "CPP_RSS_kib": 9240, + "CPP_PSS_kib": 5213, + "sampled_current_bytes": 21901312, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 196820 + }, + { + "name": "slow_reader", + "operation": "slow_reader", + "elapsed_seconds": 2.040373, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 46, + "settlements": 46, + "unsettled": 0, + "CPP_RSS_kib": 11664, + "CPP_PSS_kib": 7637, + "sampled_current_bytes": 24571904, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 192976 + }, + { + "name": "repeated_cancel", + "operation": "cancel", + "elapsed_seconds": 1.541645, + "N": 10, + "p95_ms_recomputed_fullN": 157.4041249999973, + "reported_helper_p95_ms": 157.4041249999973, + "posts": 56, + "settlements": 56, + "unsettled": 0, + "CPP_RSS_kib": 11664, + "CPP_PSS_kib": 7637, + "sampled_current_bytes": 24801280, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 192868 + }, + { + "name": "four_stream_overload", + "operation": "overload", + "elapsed_seconds": 0.418299, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 58, + "settlements": 58, + "unsettled": 0, + "CPP_RSS_kib": 11664, + "CPP_PSS_kib": 7637, + "sampled_current_bytes": 24899584, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 192420 + }, + { + "name": "four_stream_overload_postsoak", + "operation": "overload", + "elapsed_seconds": 0.424785, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 778, + "settlements": 778, + "unsettled": 0, + "CPP_RSS_kib": 10360, + "CPP_PSS_kib": 6418, + "sampled_current_bytes": 24526848, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 196544 + }, + { + "name": "post_idle", + "operation": "idle", + "elapsed_seconds": 30.078841, + "N": 0, + "p95_ms_recomputed_fullN": null, + "reported_helper_p95_ms": null, + "posts": 778, + "settlements": 778, + "unsettled": 0, + "CPP_RSS_kib": 10360, + "CPP_PSS_kib": 6312, + "sampled_current_bytes": 24211456, + "cumulative_peak_bytes": 34422784, + "host_MemAvailable_kib": 186424 + } + ], + "soakSamples": { + "batchCount": 359, + "N": 718, + "CPP_RSS_kib_min": 9440, + "CPP_RSS_kib_max": 11664, + "CPP_PSS_kib_min": 5362, + "CPP_PSS_kib_max": 7637, + "sampled_current_bytes_min": 20721664, + "sampled_current_bytes_max": 25223168, + "cumulativePeakIsNotSoakOnly": true + }, + "accounting": { + "snapshot_sequence": 3074, + "last_sequence": 3890, + "validated_frames": 816, + "journal_bytes": 364962, + "records": 778, + "dispatched": 778, + "settled": 778, + "reserved": 0, + "quota_transactions": 1556, + "quota_mutations": 3112, + "terminals": { + "response.completed": 767, + "response.failed": 1, + "cancelled": 10 + }, + "delivery": { + "delivered": 767, + "interrupted": 11 + } + }, + "stateFileHashes": { + "snapshot.json": { + "bytes": 227592, + "sha256": "5d39420cf4245513a9d2dd3c6bc2f4f2216ab3c5941caaed2b402e9434b14910" + }, + "owner.lock": { + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "journal.bin": { + "bytes": 364962, + "sha256": "fb29ee256a9d2103a39d692ed9c661b90f8435119dfc797de85a273fd4b026bb" + } + }, + "outsideHelpers": { + "sampleUTC": "2026-11-13T09:57:29Z", + "rows": [ + { + "name": "sshd", + "VmHWM_kib": 9808, + "VmRSS_kib": 9808, + "VmSwap_kib": 100, + "continuity": true + }, + { + "name": "sshd", + "VmHWM_kib": 6724, + "VmRSS_kib": 6180, + "VmSwap_kib": 484, + "continuity": true + }, + { + "name": "systemd-run", + "VmHWM_kib": 6204, + "VmRSS_kib": 3680, + "VmSwap_kib": 924, + "continuity": "first-HWM-sample" + }, + { + "name": "python3", + "VmHWM_kib": 10316, + "VmRSS_kib": 2596, + "VmSwap_kib": 5236, + "continuity": "first-HWM-sample" + }, + { + "name": "sshd", + "VmHWM_kib": 9956, + "VmRSS_kib": 9820, + "VmSwap_kib": 92, + "continuity": true + }, + { + "name": "sshd", + "VmHWM_kib": 9032, + "VmRSS_kib": 6396, + "VmSwap_kib": 1048, + "continuity": true + }, + { + "name": "cat", + "VmHWM_kib": 1172, + "VmRSS_kib": 852, + "VmSwap_kib": 88, + "continuity": "first-HWM-sample" + } + ], + "HWM_sum_kib": 53212, + "sharedMasterExcluded": true, + "RSS_and_charge_upper_bound_MiB": 84.79296875, + "bound": "Non-concurrent per-PID HWM plus cgroup charge double-counts shared pages; not physical incremental RAM; HWM coverage ends09:57:29 while cgroup peak includes later shutdown", + "swapSnapshots": { + "firstUTC": "2026-11-13T09:13:59Z", + "laterUTC": "2026-11-13T09:28:04Z", + "firstSSHSmapsSwapKiB": 1724, + "laterSSHSmapsSwapKiB": 1728, + "observedDeltaKiB": 4, + "prestartBaselineAndInheritance": "UNPROVED", + "perProcessDeltasKiB": [0, 0, 0, 4] + } + }, + "overload": [ + { + "statuses": [200, 200, 503, 503], + "codes": ["local_inference_overload", "local_inference_overload"], + "retryAfter": ["1", "1"], + "actualPosts": 2, + "logicalDispatches": 2, + "heldActive": 2, + "heldQueued": 0 + }, + { + "statuses": [200, 200, 503, 503], + "codes": ["local_inference_overload", "local_inference_overload"], + "retryAfter": ["1", "1"], + "actualPosts": 2, + "logicalDispatches": 2, + "heldActive": 2, + "heldQueued": 0 + } + ], + "nativeClient": { + "version": "0.167.2", + "sha256": "b581df3989da98e36fccd7d40cc980727a9ee87bcf0e6ef52ffb2544d6a5302f", + "offPi": true, + "firstInferencePair": true + }, + "cleanup": { + "bothUnitsCollected": true, + "cgroupAbsent": true, + "taskPidsRemaining": 0, + "taskListenersRemaining": 0, + "local18799Listeners": 0, + "allHandlesSettled0": true, + "sharedSshdMasterPreserved": true, + "binaryHashUnchanged": true, + "allSevenLibraryHashesMatch": true + }, + "filesystem": { + "stateLocation": "disk-backed Pi /tmp per prior host preflight", + "exactFinalFstype": "not restated in sealed summary; no new probe performed", + "VpsScratch": "tmpfs; distinct from Pi persistence timing" + }, + "limitations": [ + "Outside helper swap present; mid-run SSH smaps delta included+4KiB with no pre-start baseline, so no strict whole-transport zero-new-swap or inheritance claim.", + "Mock/client are on VPS; no whole-VPS-host outage or production authority/single-writer failover proof.", + "One-provider synthetic fixture authority, not production activation or full gateway parity.", + "TLS final staged proof was separate VPS acceptance; this Pi soak uses isolated loopback HTTP transport.", + "Prefix helper p95 field for remaining sub-batch is retained; full-N prefix percentiles are independently recomputed in phase-scorecard.json.", + "Initial launcher packing and wrong-overload-status attempts remain failed; their evidence is not merged into the successful cohort." + ] +} diff --git a/docs/research/cpp-pi-standby-pilot-2026-11-13/sources.json b/docs/research/cpp-pi-standby-pilot-2026-11-13/sources.json new file mode 100644 index 000000000..7d81a6463 --- /dev/null +++ b/docs/research/cpp-pi-standby-pilot-2026-11-13/sources.json @@ -0,0 +1,80 @@ +{ + "schema": 1, + "sourceCommit": "7069c31b3f50d0552ef77e99b984f59fde479b13", + "sourceTree": "827f3281deaa0b36002d710c172a6b0e43d89a38", + "stagedSha256": "bbb18300ea225e2803bc1f95ce5576b42af862c135c828667fd163a191c5bfd3", + "inputs": [ + { + "name": "HANDOFF.md", + "sha256": "b840be66228d22051fe2253512933b7ba3e7b8181bf7097d4e3b7ec867d3d17c", + "bytes": 4530 + }, + { + "name": "summary.json", + "sha256": "0fa7fd64e7fce612e079b4aeaf6829326e77cd464614d82992b08d5f30f246d0", + "bytes": 8245 + }, + { + "name": "phase-scorecard.json", + "sha256": "7a26158d3315bc5db2837f68b4f633ece55b5e94f00241a8a31269dff4fc7eb9", + "bytes": 154569 + }, + { + "name": "MANIFEST.json", + "sha256": "ce2545ac72f56a13e252fce8c922b0133588accb47716f1d97b8f30bb016f0e4", + "bytes": 182289 + }, + { + "name": "run/ssh-swap-comparison.json", + "sha256": "fd15c4117a3a40ab528af3ca8d334022d685aa1ab4909abe46e0fc4daf94ffa9", + "bytes": 2604 + }, + { + "name": "run/pre-exit-helper-high-water.json", + "sha256": "d675c3ae0292640570592b566bad61ab3385756f9407e6205c8180b1a53622d8", + "bytes": 11114 + }, + { + "name": "run/pi-final-evidence/independent-final-settlement.json", + "sha256": "66b1220edd1684b2b2b7569d9d9e64eb5699bc81194d75a5914bd193306dbde3", + "bytes": 789 + }, + { + "name": "run/final-stopped-ledger.json", + "sha256": "ed9d5bf242b3d87e1826b248907b15c20749bbfc3609b4ee943eae4856523031", + "bytes": 826 + }, + { + "name": "summary.json", + "sha256": "2c2cbc97732cfb1ddc7932d138dd47e64f6f42227ec75f2bf54cbd349f3aaf82", + "bytes": 20046 + }, + { + "name": "package-manifest.json", + "sha256": "6be353baebb049d62429abc46742648c459865d17ae1f00ac0dbd047ffa01426", + "bytes": 10959 + }, + { + "name": "F-G-bindings.json", + "sha256": "f3587f8413ded7f699983c486745c13fcfed9317fb17c57f812739a25574e28c", + "bytes": 1781 + }, + { + "name": "HANDOFF.json", + "sha256": "8b58ff5a3a8237d0dba2aa5bf2fa8db542ba20ef9af2d2def498cc46da5bc2f5", + "bytes": 3159 + } + ], + "sealedPiInventory": { + "files": 1231, + "bytes": 32969400, + "publishedAsWhole": false + }, + "sanitization": "Only whitelisted scalar verdicts, phase/resource/accounting/build data and original hashes. No key/config/native prompt/DB/source bundle copied.", + "author": { + "model": "gpt-6.1-sol", + "effort": "max", + "turn": "01a1d45b-e479-76c3-8542-70faf11ff10f" + }, + "actions": "Scratch authoring only; no new tests/Git/CI/model/deploy actions." +} diff --git a/scripts/test-scope.test.ts b/scripts/test-scope.test.ts index 46560c895..e081b0928 100644 --- a/scripts/test-scope.test.ts +++ b/scripts/test-scope.test.ts @@ -14,6 +14,7 @@ import { ROOT, selectAffectedTestFiles, testRequiresNativeKv, + testSelectionForPaths, type TestSelection, } from "./test-scope.ts"; @@ -948,3 +949,67 @@ describe("separate research and CMake component inputs", () => { }); }); }); + +describe("the exact lint project is validation-only", () => { + test("lint include changes retain static gates without selecting runtime or native jobs", () => { + const result = scopeCommand( + ["scripts/test-scope.ts", "--ci-json"], + { GITHUB_EVENT_NAME: "pull_request", GITHUB_BASE_REF: "development" }, + "tsconfig.lint.json" + ); + expect(result.status).toBe(0); + const scope = JSON.parse(result.stdout); + expect(scope).toMatchObject({ + reason: "validation-only", + runAll: false, + affectedTests: [], + needsValidate: true, + needsTypeScriptValidate: true, + needsRustNative: false, + needsDarwinTests: false, + needsDarwinNative: false, + needsNativeKv: false, + needsArtifact: false, + }); + const verify = scopeCommand(["scripts/test-scope.ts"], {}, "tsconfig.lint.json"); + expect(verify.status).toBe(10); + expect(verify.stdout).toContain("strict no-emit checks"); + }); + test("runtime configurations are still conservative, and complete-set overrides remain effective", () => { + for (const path of ["package.json", "tsconfig.json"]) { + const result = scopeCommand(["scripts/test-scope.ts", "--ci-json"], {}, path); + expect(result.status).toBe(0); + const scope = JSON.parse(result.stdout); + expect(scope.reason).toBe("config"); + expect(scope.affectedTests.length).toBeGreaterThan(1); + } + const selection: TestSelection = { paths: ["tsconfig.lint.json"], documentationOnly: false, affectedTests: [], runAll: false, reason: "validation-only" }; + expect(ciScopeForSelection(selection, fixture, { GITHUB_EVENT_NAME: "merge_group" })).toMatchObject({ + completeCheckSet: true, + needsRustNative: true, + needsDarwinNative: true, + }); + }); + test("an actual import or literal runtime read of the lint JSON restores runtime validation", () => { + for (const operation of ["import", "read"]) { + const root = mkdtempSync(join(fixture, "lint-consumer-")); + mkdirSync(join(root, "src")); + mkdirSync(join(root, "tests")); + writeFileSync(join(root, "tsconfig.lint.json"), "{}\n"); + const specifier = "../tsconfig.lint.json"; + const source = + operation === "import" + ? `import config from ${JSON.stringify(specifier)};\nexport {config};\n` + : `import {readFileSync} from 'node:fs';\nreadFileSync(new URL(${JSON.stringify(specifier)},import.meta.url));\n`; + writeFileSync(join(root, "src/consumer.ts"), source); + writeFileSync(join(root, "tests/consumer.test.ts"), "import '../src/consumer.ts';\n"); + writeFileSync(join(root, "tests/unrelated.test.ts"), "// Unrelated source selection control.\n"); + const candidates = ["tests/consumer.test.ts", "tests/unrelated.test.ts"]; + const selection = testSelectionForPaths(["tsconfig.lint.json"], candidates, root); + expect(selection.reason).toBe("config"); + expect(selection.affectedTests).toEqual(candidates); + expect(ciScopeForSelection(selection, root, {}).needsTypeScriptValidate).toBe(true); + expect(classifyChangedPaths(selection.paths, root).documentationOnly).toBe(false); + } + }); +}); diff --git a/scripts/test-scope.ts b/scripts/test-scope.ts index c9cbc7e01..12def925b 100644 --- a/scripts/test-scope.ts +++ b/scripts/test-scope.ts @@ -17,6 +17,8 @@ const UNTRACKED_ROOTS = ["src/", "scripts/", "tests/", "ops/", "benchmarks/", "t const PRIVATE_COPY_CONTROLLER = "tests/private-bun-copy-confinement.test.ts"; const PRIVATE_COPY_PRODUCT = "tools/cloudflare/private-bun-copy.ts"; const RESEARCH_ARTIFACT = /^docs\/research\/.+\.(?:json|py)$/i; +const LINT_PROJECT_CONFIG = "tsconfig.lint.json"; +const requiresStaticValidation = (paths: readonly string[]): boolean => paths.some((path) => SOURCE_FILE.test(path) || path === LINT_PROJECT_CONFIG); const WORKER_RUNTIME_TEST = "tests/native-authority-workerd.test.ts"; const WORKER_RUNTIME_INPUT = /^(?:tests\/fixtures\/native-authority\/|tools\/worker-test-runtime\/|scripts\/prepare-worker-test-runtime\.ts$)/; const WORKER_RUNTIME_FIXTURES = ["tests/fixtures/native-authority/fixture-worker.mjs", "tests/fixtures/native-authority/bun-client.ts"]; @@ -34,7 +36,7 @@ export type TestSelection = ChangeScope & Readonly<{ affectedTests: readonly string[]; runAll: boolean; - reason: "no-base" | "no-changes" | "documentation-only" | "no-dependent-tests" | "selected" | "config"; + reason: "no-base" | "no-changes" | "documentation-only" | "no-dependent-tests" | "validation-only" | "selected" | "config"; }>; export type CiScope = TestSelection & @@ -244,9 +246,11 @@ const literalReferences = (file: string, root: string): string[] => { const referencesComponent = (reference: string, target: string, component: string): boolean => reference === target || (!extname(reference) && (reference === component || reference.startsWith(component + "/")) && target.startsWith(reference + "/")); -/** Research records and the CMake pilot are separate unless a Bun consumer reaches them. */ +/** Research, lint-project and CMake inputs stay separate unless a Bun consumer reaches them. */ const separateComponentPaths = (paths: readonly string[], root: string): ReadonlySet => { - const candidates = uniqueSorted(paths.map(normalizePath).filter((path) => RESEARCH_ARTIFACT.test(path) || path.startsWith("cpp/"))); + const candidates = uniqueSorted( + paths.map(normalizePath).filter((path) => RESEARCH_ARTIFACT.test(path) || path.startsWith("cpp/") || path === LINT_PROJECT_CONFIG) + ); if (!candidates.length) return new Set(); const targets = candidates.map((path) => ({ path, @@ -325,25 +329,33 @@ const DARWIN_PATH = const candidateTestFiles = (changedPaths: readonly string[]): string[] => uniqueSorted([...pathList(["ls-files", "-z"]).map(normalizePath).filter(isTestPath), ...changedPaths.filter(isTestPath)]); -export const detectTestSelection = (): TestSelection => { - const paths = changedPathsFromGit(); - if (paths === null) return { paths: [], documentationOnly: false, affectedTests: [], runAll: true, reason: "no-base" }; - const separate = separateComponentPaths(paths, ROOT); +export const testSelectionForPaths = (paths: readonly string[], candidates: readonly string[], root = ROOT): TestSelection => { + const separate = separateComponentPaths(paths, root); const scope = classifyPaths(paths, separate); if (scope.paths.length === 0) return { ...scope, affectedTests: [], runAll: true, reason: "no-changes" }; if (scope.documentationOnly) return { ...scope, affectedTests: [], runAll: false, reason: "documentation-only" }; const changedCode = scope.paths.filter((path) => !isDocumentationPath(path) && !isRustPath(path) && !separate.has(path) && path !== WORKFLOW_CONTRACT_PATH); - const candidates = candidateTestFiles(scope.paths); const dependencyScoped = changedCode.every((path) => SOURCE_FILE.test(path) || isTestPath(path)); // The workflow is a readFile dependency of this contract test, not a TS import. const workflowTests = scope.paths.includes(WORKFLOW_CONTRACT_PATH) ? ["scripts/test-scope.test.ts"] : []; // Retain the existing explicit Darwin signing gate for its native/profile inputs. const darwinTests = scope.paths.some((path) => DARWIN_PATH.test(path)) && candidates.includes("tests/gpg-dsh.test.ts") ? ["tests/gpg-dsh.test.ts"] : []; - const affectedTests = uniqueSorted([...(dependencyScoped ? selectAffectedTestFiles(changedCode, candidates) : candidates), ...workflowTests, ...darwinTests]); - if (affectedTests.length === 0) return { ...scope, affectedTests, runAll: false, reason: "no-dependent-tests" }; + const affectedTests = uniqueSorted([ + ...(dependencyScoped ? selectAffectedTestFiles(changedCode, candidates, root) : candidates), + ...workflowTests, + ...darwinTests, + ]); + if (affectedTests.length === 0) + return { ...scope, affectedTests, runAll: false, reason: separate.has(LINT_PROJECT_CONFIG) ? "validation-only" : "no-dependent-tests" }; return { ...scope, affectedTests, runAll: false, reason: dependencyScoped ? "selected" : "config" }; }; +export const detectTestSelection = (): TestSelection => { + const paths = changedPathsFromGit(); + return paths === null + ? { paths: [], documentationOnly: false, affectedTests: [], runAll: true, reason: "no-base" } + : testSelectionForPaths(paths, candidateTestFiles(paths)); +}; const manualDispatchInputs = (environment: NodeJS.ProcessEnv): Record => { if (environment.GITHUB_EVENT_NAME !== "workflow_dispatch" || !environment.GITHUB_EVENT_PATH) return {}; @@ -379,7 +391,8 @@ export const ciScopeForSelection = (selection: TestSelection, root = ROOT, envir const nativeOnly = rustPaths.length > 0 && rustPaths.every((path) => RUST_NATIVE_ONLY_PACKAGES.some((name) => path.startsWith(`rust/crates/${name}/`))); const needsRustWasm = needsRustNative && (completeCheckSet || selection.runAll || !nativeOnly); const separate = separateComponentPaths(selection.paths, root); - const hasTypeScriptWork = selection.paths.some((path) => SOURCE_FILE.test(path) || (!isDocumentationPath(path) && !isRustPath(path) && !separate.has(path))); + const hasTypeScriptWork = + requiresStaticValidation(selection.paths) || selection.paths.some((path) => !isDocumentationPath(path) && !isRustPath(path) && !separate.has(path)); const needsNativeKv = selection.runAll || selection.paths.some((path) => NATIVE_PATH.test(path)) || @@ -437,7 +450,7 @@ export const formatScopeMessage = (selection: TestSelection, command: string): s "Required checks for this change:", " - Format only the changed files.", " - Run `git diff --check`.", - ...(selection.paths.some((path) => SOURCE_FILE.test(path)) + ...(requiresStaticValidation(selection.paths) ? [" - Retain changed-source lint, formatting, and strict no-emit checks; do not run unrelated test suites."] : [" - Do not run unrelated tests, lint, Knip, native builds, or deployment suites."]), "Override only for an explicitly requested test run: TEST_SCOPE_FORCE=1", @@ -473,5 +486,5 @@ if (import.meta.main) { const selection = detectTestSelection(); if (selection.runAll) process.exit(10); console.log(formatScopeMessage(selection, "bun run verify")); - process.exit(selection.affectedTests.length === 0 && !selection.paths.some((path) => SOURCE_FILE.test(path)) ? 0 : 10); + process.exit(selection.affectedTests.length === 0 && !requiresStaticValidation(selection.paths) ? 0 : 10); } diff --git a/tsconfig.lint.json b/tsconfig.lint.json index 468f096be..3d4858c47 100644 --- a/tsconfig.lint.json +++ b/tsconfig.lint.json @@ -5,6 +5,7 @@ "serve.ts", "src/**/*.ts", "tests/**/*.ts", + "cpp/tests/**/*.ts", "scripts/**/*.ts", "benchmarks/**/*.ts", "ops/**/*.ts",