W20: Tool Calling 闭环 + Stream+Tools + 回归测试 + session auto-save + ASan CI (W20.1-W20.6)
Some checks failed
CI / Determine matrix (push) Has been cancelled
CI / ${{ matrix.os }} / ${{ matrix.build_type }} (push) Has been cancelled
CI / Sanitizer (ASan+UBSan) / ubuntu-24.04 (push) Has been cancelled

- W20.1: CLI tool_calls→execute→result→re-call 循环(5轮上限)
- W20.2: deepseek 流式 tool_calls 增量解析(configure 缓存,无 ABI break)
- W20.3: plugin_loader 回归测试 5 块 32 断言(路径/原子性/mock 日志)
- W20.4: plugin_loader ABI 契约校验(name/version/on_init 字段验证)
- W20.5: ASan/UBSan CMake preset + CI sanitizer job(PR-only Linux)
- W20.6: session auto-save(on_shutdown 写 %APPDATA%/dstalk/session.json)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 20:15:00 +08:00
parent 3250b5a8bf
commit 20ead86e88
14 changed files with 730 additions and 21 deletions

View File

@@ -12,6 +12,7 @@
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <mutex>
@@ -287,6 +288,24 @@ static dstalk_session_service_t g_session_service = {
session_token_count
};
// ============================================================
// W20.6: 默认会话保存路径(平台标准目录)
// ============================================================
static std::string get_default_session_path() {
#ifdef _WIN32
char* buf = nullptr;
size_t len = 0;
_dupenv_s(&buf, &len, "APPDATA");
std::string dir = buf ? std::string(buf) + "/dstalk" : "dstalk";
free(buf);
#else
const char* home = std::getenv("HOME");
std::string dir = home ? std::string(home) + "/.dstalk" : "/tmp/dstalk";
#endif
return dir + "/session.json";
}
// ============================================================
// 插件生命周期
// ============================================================
@@ -304,7 +323,13 @@ static int on_init(const dstalk_host_api_t* host) {
g_file_io.store(static_cast<const dstalk_file_io_service_t*>(raw), std::memory_order_release);
// 注册自身服务
return host->register_service("session", 1, &g_session_service);
int ret = host->register_service("session", 1, &g_session_service);
if (ret != 0) return ret;
// W20.6: 从默认路径恢复会话(文件不存在则静默失败)
session_load(get_default_session_path().c_str());
return 0;
} catch (const std::exception& e) {
const dstalk_host_api_t* h = g_host.load(std::memory_order_acquire);
if (h) h->log(DSTALK_LOG_ERROR, "on_init[session]: %s", e.what());
@@ -318,6 +343,9 @@ static int on_init(const dstalk_host_api_t* host) {
static void on_shutdown() {
try {
// W20.6: 清空前自动保存到默认路径
session_save(get_default_session_path().c_str());
std::lock_guard<std::mutex> lock(g_session_mutex);
rebuild_cached_history_locked();
g_cached_history.clear();