Wave 5+6: plugin ABI hardening, build modernization, ABI/security docs
Some checks failed
CI / Determine matrix (push) Has been cancelled
CI / ${{ matrix.os }} / ${{ matrix.build_type }} (push) Has been cancelled

Wave 5 (9 parallel agents):
- W1.1 atomic diag callback + DLL handle release on shutdown (lin)
- W2.1 unify cross-DLL heap discipline (host->alloc/free/strdup) (chen)
- W2.2 secure_zero api_key on shutdown for deepseek/anthropic (cao)
- W3 CMake modernization: target-based cxx_std_20, dstalk_boost_config
  INTERFACE lib, root-level RUNTIME_OUTPUT_DIRECTORY (hu)
- W4 GitHub Actions CI with dynamic Linux/Windows matrix (ma)
- W5.1 SSE buffer_body to cut peak memory ~67% on 32K streams (zhou)
- W6.1 LSP JSON-RPC frame parser hardened against header reordering (sun)
- W7 smoke test: copy plugin DLLs post-build + Boost.JSON src.hpp fix
  for full 9-plugin load coverage (wang)
- W8.1 README slimmed 398->92, Diataxis docs/ skeleton (deng)

Wave 6 (6 parallel agents):
- W9.1 docs/explanation: architecture + plugin-lifecycle (deng)
- W9.3 log credential leak audit (0 vulns, audit trail in
  docs/explanation/security-logging.md) (cao)
- W9.4 docs/reference/plugin-abi.md - 7-point ABI contract (lin)
- W9.6 CLI /history command + status integration (zhao)
- W9.8 plugin_loader fault tolerance: per-plugin failure no longer
  aborts dstalk_init (huang)
- W9.10 host_api unit tests: tests/host_api_test.cpp, 8 cases (liu)

CEO oversight (preexisting bugs fixed during Wave 5 verification):
- lsp_plugin.cpp:449 forward decl mismatch (int vs void)
- tools_plugin.cpp:109 missing forward decl

Multi-agent collaboration framework:
- agents/WORKFLOW.md: 6-stage protocol, two-tier governance,
  prompt template, technical constraints registry

Build: cmake --build 0 error / 0 warning. Tests: 2/2 100% pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-27 05:39:10 +08:00
parent 4433218853
commit 5766938524
49 changed files with 1640 additions and 449 deletions

View File

@@ -9,21 +9,11 @@ add_library(plugin-anthropic SHARED
src/anthropic_plugin.cpp
)
target_include_directories(plugin-anthropic PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-anthropic PRIVATE dstalk)
# Boost.JSON 用于构建/解析请求和响应
find_package(Boost REQUIRED CONFIG)
target_link_libraries(plugin-anthropic PRIVATE boost::boost)
target_compile_definitions(plugin-anthropic PRIVATE
BOOST_ALL_NO_LIB
BOOST_ERROR_CODE_HEADER_ONLY
BOOST_JSON_HEADER_ONLY
)
target_link_libraries(plugin-anthropic PRIVATE boost::boost dstalk_boost_config)
set_target_properties(plugin-anthropic PROPERTIES
PREFIX ""

View File

@@ -2,6 +2,7 @@
#include "dstalk/dstalk_services.h"
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <cstring>
#include <string>
@@ -27,6 +28,14 @@ struct PluginConfig {
};
static PluginConfig g_cfg;
// ============================================================================
// 安全擦除:用 volatile 写零循环防止编译器优化
// ============================================================================
static void secure_zero(void* p, size_t n) {
volatile char* vp = (volatile char*)p;
while (n--) *vp++ = 0;
}
// ============================================================================
// 辅助:提取 host / target
// ============================================================================
@@ -461,6 +470,8 @@ static int on_init(const dstalk_host_api_t* host)
static void on_shutdown()
{
if (g_host) g_host->log(DSTALK_LOG_INFO, "[anthropic] shutdown");
secure_zero(g_cfg.api_key.data(), g_cfg.api_key.size());
g_cfg.api_key.clear();
g_http = nullptr;
g_config = nullptr;
g_host = nullptr;

View File

@@ -1,9 +1,5 @@
add_library(plugin-config SHARED src/config_plugin.cpp)
target_include_directories(plugin-config PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-config PRIVATE dstalk)
set_target_properties(plugin-config PROPERTIES

View File

@@ -1,9 +1,5 @@
add_library(plugin-context SHARED src/context_plugin.cpp)
target_include_directories(plugin-context PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-context PRIVATE dstalk)
set_target_properties(plugin-context PROPERTIES

View File

@@ -9,21 +9,11 @@ add_library(plugin-deepseek SHARED
src/deepseek_plugin.cpp
)
target_include_directories(plugin-deepseek PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-deepseek PRIVATE dstalk)
# Boost.JSON 用于构建/解析请求和响应
find_package(Boost REQUIRED CONFIG)
target_link_libraries(plugin-deepseek PRIVATE boost::boost)
target_compile_definitions(plugin-deepseek PRIVATE
BOOST_ALL_NO_LIB
BOOST_ERROR_CODE_HEADER_ONLY
BOOST_JSON_HEADER_ONLY
)
target_link_libraries(plugin-deepseek PRIVATE boost::boost dstalk_boost_config)
set_target_properties(plugin-deepseek PROPERTIES
PREFIX ""

View File

@@ -2,6 +2,7 @@
#include "dstalk/dstalk_services.h"
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <cstring>
#include <string>
@@ -27,6 +28,14 @@ struct PluginConfig {
};
static PluginConfig g_cfg;
// ============================================================================
// 安全擦除:用 volatile 写零循环防止编译器优化
// ============================================================================
static void secure_zero(void* p, size_t n) {
volatile char* vp = (volatile char*)p;
while (n--) *vp++ = 0;
}
// ============================================================================
// 辅助:从 base_url 提取 host 和 target
// ============================================================================
@@ -450,6 +459,8 @@ static int on_init(const dstalk_host_api_t* host)
static void on_shutdown()
{
if (g_host) g_host->log(DSTALK_LOG_INFO, "[deepseek] shutdown");
secure_zero(g_cfg.api_key.data(), g_cfg.api_key.size());
g_cfg.api_key.clear();
g_http = nullptr;
g_config = nullptr;
g_host = nullptr;

View File

@@ -1,9 +1,5 @@
add_library(plugin-file-io SHARED src/file_io_plugin.cpp)
target_include_directories(plugin-file-io PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-file-io PRIVATE dstalk)
set_target_properties(plugin-file-io PROPERTIES

View File

@@ -29,8 +29,8 @@ static int file_read(const char* path, char** content) {
return -1;
}
// Allocate buffer (+1 for null terminator)
char* buf = (char*)malloc((size_t)fsize + 1);
// Allocate buffer via host allocator (+1 for null terminator)
char* buf = (char*)g_host->alloc((size_t)fsize + 1);
if (!buf) {
fclose(fp);
return -1;
@@ -40,7 +40,7 @@ static int file_read(const char* path, char** content) {
fclose(fp);
if (read_bytes != (size_t)fsize) {
free(buf);
g_host->free(buf);
return -1;
}

View File

@@ -9,21 +9,11 @@ add_library(plugin-lsp SHARED
src/lsp_plugin.cpp
)
target_include_directories(plugin-lsp PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-lsp PRIVATE dstalk)
# Boost.JSON 用于 JSON-RPC 消息构建/解析
find_package(Boost REQUIRED CONFIG)
target_link_libraries(plugin-lsp PRIVATE boost::boost)
target_compile_definitions(plugin-lsp PRIVATE
BOOST_ALL_NO_LIB
BOOST_ERROR_CODE_HEADER_ONLY
BOOST_JSON_HEADER_ONLY
)
target_link_libraries(plugin-lsp PRIVATE boost::boost dstalk_boost_config)
# POSIX 平台需要 pthread (用于 std::thread)
if(NOT WIN32)

View File

@@ -9,6 +9,7 @@
#include "dstalk/dstalk_services.h"
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <atomic>
#include <chrono>
@@ -417,18 +418,33 @@ static void handle_message(const std::string& body) {
static void reader_loop() {
while (g_lsp.running) {
std::string header_line;
if (!g_lsp.proc.read_line(header_line)) break;
int content_length = -1;
bool pipe_ok = true;
int content_length = parse_content_length(header_line);
if (content_length < 0) continue;
// 跳过后续头直到空行 (\r\n 换行被视为非空行,只检查空行)
while (true) {
// 状态机式读取 header 块:循环 read_line 直到读到空行
// LSP 3.17: header 块以空行(\r\n)结束,允许 Content-Type 等其他 header
while (pipe_ok) {
std::string line;
if (!g_lsp.proc.read_line(line)) break;
if (!g_lsp.proc.read_line(line)) {
pipe_ok = false;
break;
}
// header 块以空行结束
auto sv = trim(std::string_view(line));
if (sv.empty()) break;
// 累积 Content-Length遇到其他 header 不丢弃,继续读取下一行
int len = parse_content_length(line);
if (len >= 0) content_length = len;
}
if (!pipe_ok) break;
// 空行前都没读到 Content-Length协议错误——记日志并跳过这一帧
if (content_length < 0) {
if (g_host) g_host->log(DSTALK_LOG_ERROR, "[lsp] Invalid LSP frame: missing Content-Length header");
continue;
}
std::string body;
@@ -446,7 +462,7 @@ static void reader_loop() {
// LSP 服务 vtable 实现 (定义在 vtable 变量之前)
// ============================================================================
static int g_lsp_impl_stop();
static void g_lsp_impl_stop();
static int g_lsp_impl_start(const char* server_cmd, const char* language) {
if (!server_cmd || !server_cmd[0]) return -1;

View File

@@ -3,10 +3,6 @@ find_package(OpenSSL REQUIRED CONFIG)
add_library(plugin-network SHARED src/network_plugin.cpp)
target_include_directories(plugin-network PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-network PRIVATE
dstalk
boost::boost

View File

@@ -1,15 +1,9 @@
add_library(plugin-session SHARED src/session_plugin.cpp)
target_include_directories(plugin-session PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-session PRIVATE dstalk)
find_package(Boost REQUIRED CONFIG)
target_link_libraries(plugin-session PRIVATE boost::boost)
target_compile_definitions(plugin-session PRIVATE
BOOST_ALL_NO_LIB BOOST_ERROR_CODE_HEADER_ONLY BOOST_JSON_HEADER_ONLY)
target_link_libraries(plugin-session PRIVATE boost::boost dstalk_boost_config)
set_target_properties(plugin-session PROPERTIES
PREFIX ""

View File

@@ -6,6 +6,7 @@
#include "dstalk/dstalk_services.h"
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <algorithm>
#include <cstddef>
@@ -163,7 +164,7 @@ static int session_load(const char* path) {
if (ret != 0 || !content) return -1;
std::string data(content);
std::free(content);
g_host->free(content);
std::vector<InternalMessage> parsed;
size_t pos = 0;

View File

@@ -1,15 +1,9 @@
add_library(plugin-tools SHARED src/tools_plugin.cpp)
target_include_directories(plugin-tools PRIVATE
${CMAKE_SOURCE_DIR}/dstalk-core/include
)
target_link_libraries(plugin-tools PRIVATE dstalk)
find_package(Boost REQUIRED CONFIG)
target_link_libraries(plugin-tools PRIVATE boost::boost)
target_compile_definitions(plugin-tools PRIVATE
BOOST_ALL_NO_LIB BOOST_ERROR_CODE_HEADER_ONLY BOOST_JSON_HEADER_ONLY)
target_link_libraries(plugin-tools PRIVATE boost::boost dstalk_boost_config)
set_target_properties(plugin-tools PROPERTIES
PREFIX ""

View File

@@ -6,6 +6,7 @@
#include "dstalk/dstalk_services.h"
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <cstdlib>
#include <cstring>
@@ -55,7 +56,7 @@ static char* builtin_file_read(const char* args_json) {
}
std::string escaped_content = json::serialize(json::string(content));
std::free(content);
g_host->free(content);
std::string result = "{\"content\":" + escaped_content + "}";
return g_host->strdup(result.c_str());
@@ -100,6 +101,8 @@ static char* builtin_file_write(const char* args_json) {
// Tools 服务 vtable 实现
// ============================================================
static void tools_unregister_tool(const char* name);
static int tools_register_tool(const char* name, const char* desc,
const char* params_schema,
dstalk_tool_handler_fn handler) {