@zltquant/zlt-mcp 0.1.0 → 0.3.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/config.json CHANGED
@@ -507,7 +507,7 @@
507
507
  },
508
508
  {
509
509
  "name": "mcp-create-and-run-backtest",
510
- "description": "创建并启动回测。strategyId 必须来自 mcp-create-strategy 返回的策略ID;runType 固定为 2;普通日线回测只传 strategyId,其余参数 MCP 补齐。未指定周期时 end 取自然日 T-1、start 从 end 往前推半年;ZLTAF 按实际交易日执行,非交易日不产生行情或策略调用。run/create 失败直接报错,不要去复用策略里自动生成的 debug runId(建策略时已回显为 debugRunId,跑它等于跑创建那一刻的模板快照)。\n\n**返回 runId 后立即调用 mcp-wait(kind=backtest,id=runId)阻塞等待回测结束,不要用 mcp-get-backtest-state 轮询。**\n\n日线类频率只可见到 context.previous_date:要用交易日 T 的收盘,end 必须设成 T 的下一个交易日;end 落在未来不报错但结果为空。展开见返回的 dailyVisibilityNote / endWarning。\n\n返回 Data 回显 resolvedStart/resolvedEnd/resolvedCapital——向用户汇报区间和初始资金时一律以这些字段为准,不要自行推算或臆测。**基准不在此列**:回显的 requestedBenchmark 只是请求值,策略 initialize 里的 set_benchmark() 会覆盖它,收益产物里的「基准收益」按策略实际用的基准算。说基准是什么之前先读策略代码里的 set_benchmark,否则会把一只个股的涨跌报成指数的。\n\n**期货回测要同时改三处,少一处引擎就按股票跑**:runParams.marketIds 加上对应期货交易所(zj 中金所、zs 郑商所、ds 大商所、sq 上期所、gq 广期所),account_list 的 account_type 改成 2(实测用 6 直接废单),费率用 ratio_tpl_obj.future。合约代码形如 zjIM2605。基准保持默认 sz399300 即可,它对期货策略只是业绩比较基准。数据前置见 mcp-download-data:除行情外还必须有市场规则 mr。",
510
+ "description": "创建并启动回测。strategyId 必须来自 mcp-create-strategy 返回的策略ID;runType 固定为 2;普通日线回测只传 strategyId,其余参数 MCP 补齐。未指定周期时 end 取自然日 T-1、start 从 end 往前推半年;ZLTAF 按实际交易日执行,非交易日不产生行情或策略调用。run/create 失败直接报错,不要去复用策略里自动生成的 debug runId(建策略时已回显为 debugRunId,跑它等于跑创建那一刻的模板快照)。run/create 生成快照后,MCP 会在真正启动前拒绝 get_current_data().get(...) 这种当前对象不支持的访问。\n\n**返回 runId 后立即调用 mcp-wait(kind=backtest,id=runId)阻塞等待回测结束,不要用 mcp-get-backtest-state 轮询。**\n\n日线类频率只可见到 context.previous_date:要用交易日 T 的收盘,end 必须设成 T 的下一个交易日;end 落在未来不报错但结果为空。展开见返回的 dailyVisibilityNote / endWarning。\n\n返回 Data 回显 resolvedStart/resolvedEnd/resolvedCapital——向用户汇报区间和初始资金时一律以这些字段为准,不要自行推算或臆测。evidence 给出 backtestId、实际 runDir、完整 effectiveRunParams 和代码快照 sourceSha256,后续结果必须与它对账。**基准不在此列**:回显的 requestedBenchmark 只是请求值,策略 initialize 里的 set_benchmark() 会覆盖它,收益产物里的「基准收益」按策略实际用的基准算。说基准是什么之前先读策略代码里的 set_benchmark,否则会把一只个股的涨跌报成指数的。\n\n**期货回测要同时改三处,少一处引擎就按股票跑**:runParams.marketIds 加上对应期货交易所(zj 中金所、zs 郑商所、ds 大商所、sq 上期所、gq 广期所),account_list 的 account_type 改成 2(实测用 6 直接废单),费率用 ratio_tpl_obj.future。合约代码形如 zjIM2605。基准保持默认 sz399300 即可,它对期货策略只是业绩比较基准。数据前置见 mcp-download-data:除行情外还必须有市场规则 mr。",
511
511
  "inputSchema": {
512
512
  "type": "object",
513
513
  "properties": {
@@ -913,7 +913,7 @@
913
913
  },
914
914
  {
915
915
  "name": "mcp-get-backtest-profit",
916
- "description": "读取已完成回测的收益表现,只能在 mcp-get-backtest-state 返回 status=5 后调用。直接读回测副本目录下的 log/zlt_backtest_profit_detail.csv,不经过 ZLTAF。正常只传 mcp-create-and-run-backtest 返回的 runId;MCP 重启后上下文丢失时补传 codePath。\n\nperformance 是整轮汇总,取自产物最后一行。**名字带 Pct 的已经是百分数**:-15.62 就是 -15.62%,不要再乘 100。winRate 与 dailyWinRate 是 0-1 的比例,其余是无量纲比值。上游没有值时返回 null(原文是 nan 或空单元格),不要当成 0。\n\n另给 rows(逐日行数)、firstDate/lastDate、capital、maxDrawdownWindow(已按北京时间换算成日期)。逐日净值序列默认不返回,要画净值曲线才传 includeSeries=true。\n\n**notes 里的内容必须转述**:产物回测ID 与请求不一致,说明 codePath 指向了另一次运行,那批数字不属于本次回测;超额收益校验不通过,说明上游口径变了,此时三个收益数原样给出,不得自行重算,也不得据此宣称跑赢基准。",
916
+ "description": "读取已完成回测的收益表现,只能在 mcp-get-backtest-state 返回 status=5 后调用。直接读回测副本目录下的 log/zlt_backtest_profit_detail.csv,不经过 ZLTAF。正常只传 mcp-create-and-run-backtest 返回的 runId;MCP 重启后上下文丢失时补传 codePath。\n\nperformance 是整轮汇总,取自产物最后一行。**名字带 Pct 的已经是百分数**:-15.62 就是 -15.62%,不要再乘 100。winRate 与 dailyWinRate 是 0-1 的比例,其余是无量纲比值。上游没有值时返回 null(原文是 nan 或空单元格),不要当成 0。\n\n另给 rows(逐日行数)、firstDate/lastDate、capital、maxDrawdownWindow(已按北京时间换算成日期)。evidence 对账 requestedBacktestId、artifactBacktestId、runDir、effectiveRunParams 和 sourceSha256;contextAvailable=false 表示 MCP 已重启,只能证明显式 codePath 和产物内 ID。逐日净值序列默认不返回,要画净值曲线才传 includeSeries=true。\n\n**notes 里的内容必须转述**:产物回测ID 与请求不一致,说明 codePath 指向了另一次运行,那批数字不属于本次回测;超额收益校验不通过,说明上游口径变了,此时三个收益数原样给出,不得自行重算,也不得据此宣称跑赢基准。",
917
917
  "inputSchema": {
918
918
  "type": "object",
919
919
  "properties": {
@@ -928,7 +928,7 @@
928
928
  "includeSeries": {
929
929
  "type": "boolean",
930
930
  "default": false,
931
- "description": "返回逐日序列(date / totalAsset / benchmarkPrice / positionRatePct / turnoverRatePct)。超过 250 行只返回最后 250 行并标 seriesTruncated。"
931
+ "description": "返回逐日序列(date / totalAsset / benchmarkPrice / positionRatePct / turnoverRatePct)。超过 250 行会**等距抽样**到 250 点并标 seriesTruncated:首尾保留、跨度完整、逐点数值真实,只是变稀——多年回测画净值曲线够用,不会只拿到末段。要每一行时 notes 会给出该次产物 CSV 的列号,照它读。"
932
932
  }
933
933
  },
934
934
  "required": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zltquant/zlt-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.3.0-beta.0",
4
4
  "description": "智量通 ZLT Quant MCP server(Windows x64;量化策略创建与本地回测)",
5
5
  "bin": {
6
6
  "zlt-mcp": "./zlt-mcp-proxy.bundle.cjs"
@@ -10496,7 +10496,95 @@ var DEFAULT_BACKTEST_MODULE_INDICES = [TY_MODULE_INDEX];
10496
10496
  var DEBUG_ASSISTANT_INSTALLER_URL = "https://beta.zltquant.com/download/setup-ZLTStudio-Dbg-mini-20260714.4.exe";
10497
10497
  var PUBLIC_PYTHON_SDK_INDEX_URL = "https://pypip.zltquant.com/";
10498
10498
  var ZLTAF_CONFIG_FILE_DEFAULT = "application-local.properties";
10499
- var ZLTAF_CONFIG_FILE_DAY = "application-local-day.properties";
10499
+ var OFFICIAL_MIRROR_ROOTS = {
10500
+ public: "https://zlt-data-house.oss-cn-wuhan-lr.aliyuncs.com/zltkj.site/down/v1/up/Zlt.Std.Official",
10501
+ internal: "http://192.168.1.243:9000/zlt-data-house/zltkj.site/down/v1/up/Zlt.Std.Official"
10502
+ };
10503
+ var OFFICIAL_INSTALLERS = {
10504
+ public: {
10505
+ // 2026-08-31 抬到 2026.08.31.3,接替 2026.06.18.11 —— 运维已把件传上公网 OSS。
10506
+ // 摘要不必重下:公网与内网是同一份字节,HEAD 实测 Content-Length 168,548,568
10507
+ // 与 ETag(OSS 侧即 MD5)C404C12F…33DA 两边完全一致,所以沿用内网那份已核过的
10508
+ // SHA256=5BE939F0…5B19。
10509
+ // 卡在 2026.06.18.11 的代价是实打实的:它低于我们自己发布的
10510
+ // zlt_quant_version_index.json 里的 Client.min = 2026.08.12.3,于是一台 full
10511
+ // 装机撞上闸门只会得到 blockedByPlugin,被告知「请先升级 ZLT Quant 插件」——
10512
+ // 而插件已经是最新的那一版,闸门永远消不掉。
10513
+ // 本条与 internal.full 这次同版本,那是因为公网刚补齐,不是两张表从此必然同步。
10514
+ // **本版没有装机验证**,与 internal.full 同一句话。
10515
+ full: {
10516
+ artifact: "zlt_client",
10517
+ version: "2026.08.31.3",
10518
+ sha256: "5BE939F069E69C05F37017FBB06FF76DE4DBCBD4CBCAF1322479255A5CE55B19"
10519
+ },
10520
+ // 2026-08-15 抬到 2026.08.14.5:公网 OSS 与内网镜像是同一份字节(ETag 113b3cff…、
10521
+ // 75,075,160 字节两边一致),本机下载实测 SHA256=9D40AE9A…D551,与 2026-08-14 覆盖
10522
+ // 安装时校验过的摘要一致,两条独立路径互证。这一版是投研模块下限(≥2026.08.11.2)
10523
+ // 之上的第一个已发布 mini:低于它,投研模块 upgrade/check 直接以「客户端版本过低」
10524
+ // 拒绝,且该下限只在模块服务端。version 取安装包自身 FileVersion 的点分四段(不是
10525
+ // 构建目录名的紧凑形 20260814.5):升级判定拿它和 InstallInfo.xml 的
10526
+ // InstallAppReleaseVer 比,写成紧凑形会让「有可用更新」永远消不掉。
10527
+ // 2026-08-31 抬到 2026.08.31.4,接替 2026.08.14.5。判据与 full 同一条:HEAD 实测
10528
+ // Content-Length 79,605,832 与 ETag 7A53D1EE…1375 公网内网一致,摘要沿用内网已核过的
10529
+ // SHA256=115A3EED…83CE。
10530
+ // 这一槽位是市场版的首装路径 —— VSCode 只装 mini(runtime-profile.mjs 的
10531
+ // VSCODE_INSTALL_TYPE),full 槽位只在已装 full 的机器上参与升级判定。
10532
+ // **本版没有装机验证**:本机停在 2026.08.28.7,08-30 对 .7 做的那轮活体复核
10533
+ // (三进程起得来、ZLTHQHost 管道在、两轮 check:npm-live 全绿)不适用于本版。
10534
+ mini: {
10535
+ artifact: "zlt_plugin",
10536
+ version: "2026.08.31.4",
10537
+ sha256: "115A3EEDC07211F6FBE56696573F668AE8315E351B017FECB0F6136AAAD983CE"
10538
+ }
10539
+ },
10540
+ internal: {
10541
+ // 2026-08-31 抬到 2026.08.31.3(1.0.2 RC 用件),接替 2026.08.28.8。内网镜像下载
10542
+ // 核验:168,548,568 字节,SHA256=5BE939F0…5B19,安装包 FileVersion 自报
10543
+ // 2026.08.31.3(版本取它,不取文件名)。它仍高于 Client.min = 2026.08.12.3,
10544
+ // 所以内测通道上一台 full 装机不会以 blockedByPlugin 收场。
10545
+ // 公网 OSS 上这一版 404(2026-08-31 实测 HEAD),public.full 不动。
10546
+ full: {
10547
+ artifact: "zlt_client",
10548
+ version: "2026.08.31.3",
10549
+ sha256: "5BE939F069E69C05F37017FBB06FF76DE4DBCBD4CBCAF1322479255A5CE55B19"
10550
+ },
10551
+ // 2026-08-31 抬到 2026.08.31.4(1.0.2 RC 用件),接替 2026.08.28.7。下载核验:
10552
+ // 79,605,832 字节,SHA256=115A3EED…83CE,FileVersion 自报 2026.08.31.4。
10553
+ //
10554
+ // **本条两件都只核到字节与版本,没有装机验证。** 本机停在 2026.08.28.7
10555
+ // (InstallInfo.xml 的 InstallAppReleaseVer 实读如此),所以 08-30 对 .7 做的那轮
10556
+ // 活体复核(三进程起得来、ZLTHQHost 管道在、两轮 check:npm-live 全绿)**不适用于
10557
+ // 这两版**,别把它顺移过来当证据。RC 装机后要重新确认两件事:ZLTHome 起得来、
10558
+ // 且 InstallAppReleaseVer 回读等于本条点分四段(对不上会让「有可用更新」永远消不掉)。
10559
+ //
10560
+ // 这条 08-28 起的历史仍然有效,记着是因为它解释了为什么这批件在密集换版:
10561
+ // 2026.08.28.3 现场装过并 ZLTHome 启动即崩(TTaskFunc 持栈上 lambda 的悬垂引用,
10562
+ // 100% 复现,ZLTHQHost 管道从未存活);同版起把 zltaf 从投研模块的子模块集里去掉,
10563
+ // 修的是首装必炸的另一条权限死路 —— 安装器与模块各发一份 zltaf,模块解压要覆盖
10564
+ // 安装器以管理员身份写下的文件,而 ZLTBackService 跑在登录账户的 UAC 过滤令牌上,
10565
+ // 卷根 ACL 缺 Authenticated Users:(M) 时覆盖必被拒,2 毫秒失败在第一个文件上。
10566
+ // 见 docs/reports/2026-08-28-zltaf-submodule-extract-access-denied.md。
10567
+ // 这些版本都没进过本表,不要读成本槽位的版本序列(上一个 pin 是 2026.08.28.7)。
10568
+ //
10569
+ // 公网 OSS 上这一版同样 404,所以 public.mini 仍停在 2026.08.14.5 —— 两张表不同版本,
10570
+ // 这正是按镜像分表的理由。
10571
+ mini: {
10572
+ artifact: "zlt_plugin",
10573
+ version: "2026.08.31.4",
10574
+ sha256: "115A3EEDC07211F6FBE56696573F668AE8315E351B017FECB0F6136AAAD983CE"
10575
+ }
10576
+ }
10577
+ };
10578
+ function resolveInstallerMirror() {
10579
+ return false ? "internal" : "public";
10580
+ }
10581
+ function officialInstallerUrl(slot, mirror = "public") {
10582
+ const spec = OFFICIAL_INSTALLERS[mirror][slot];
10583
+ return `${OFFICIAL_MIRROR_ROOTS[mirror]}/pkg/${spec.artifact}_${spec.version}.exe`;
10584
+ }
10585
+ function officialVersionIndexUrl(mirror = "public") {
10586
+ return `${OFFICIAL_MIRROR_ROOTS[mirror]}/VscodeMarketPlugin/zlt_quant_version_index.json`;
10587
+ }
10500
10588
  var INTERNAL_PYTHON_SDK_INDEX_URL = "https://192.168.1.32:9100/pypi/simple/";
10501
10589
  var INTERNAL_PYTHON_SDK_TRUSTED_HOST = "192.168.1.32";
10502
10590
  var PYTHON_SDK_PACKAGES = ["zltquant", "zltsdk-plugin"];
@@ -10517,8 +10605,8 @@ var defaultAssistantRuntimeConfig = {
10517
10605
  envMode: "OFFICIAL",
10518
10606
  backserviceDownload: {
10519
10607
  OFFICIAL: {
10520
- full: "https://zlt-data-house.oss-cn-wuhan-lr.aliyuncs.com/zltkj.site/down/v1/up/Zlt.Std.Official/pkg/zlt_client_2026.06.18.11.exe",
10521
- mini: "https://zlt-data-house.oss-cn-wuhan-lr.aliyuncs.com/zltkj.site/down/v1/up/Zlt.Std.Official/pkg/zlt_plugin_2026.08.14.5.exe"
10608
+ full: officialInstallerUrl("full"),
10609
+ mini: officialInstallerUrl("mini")
10522
10610
  },
10523
10611
  REL: {
10524
10612
  full: "",
@@ -10531,10 +10619,13 @@ var defaultAssistantRuntimeConfig = {
10531
10619
  },
10532
10620
  // Exact artifact digests. A URL update requires a matching digest update in the
10533
10621
  // same reviewed change; downloadRuntimeInstaller fails closed when it is absent.
10622
+ // OFFICIAL 的取值来自 OFFICIAL_INSTALLERS.public —— URL/版本/摘要三者同源,只有那一处可改。
10623
+ // 这里恒取公网表:模块级默认必须公网可达(runtime.test.ts 的 LAN 守卫钉的就是它),
10624
+ // 内网镜像那档由 applyEnvOverrides 按镜像轴整行换掉。
10534
10625
  backserviceDownloadSha256: {
10535
10626
  OFFICIAL: {
10536
- full: "376D9C60A4BA7FF4745DF67A789BA252025EA72BB74DB6A963513FE1EFC10AE2",
10537
- mini: "9D40AE9ABCA459CF60CD5377A24013FA4B14BF96DE2D82E04CDD268AE1FDD551"
10627
+ full: OFFICIAL_INSTALLERS.public.full.sha256,
10628
+ mini: OFFICIAL_INSTALLERS.public.mini.sha256
10538
10629
  },
10539
10630
  REL: { full: "", mini: "" },
10540
10631
  DEBUG: {
@@ -10542,15 +10633,12 @@ var defaultAssistantRuntimeConfig = {
10542
10633
  mini: "05C57A5D212684A801665774E9F0813EFC1AA4762D1540EC353BB41173712B07"
10543
10634
  }
10544
10635
  },
10545
- // 与上面的 URL/摘要同属一次评审:本体升级判定拿它当「我们能立刻交付的版本下限」。
10546
- // 只改 URL 不改这里,判定就会用别的版本号去比对已装本体。
10636
+ // 本体升级判定拿它当「我们能立刻交付的版本下限」。OFFICIAL 与 URL 同源自
10637
+ // OFFICIAL_INSTALLERS,结构上不可能只改 URL 不改这里。
10547
10638
  backserviceDownloadVersion: {
10548
10639
  OFFICIAL: {
10549
- full: "2026.06.18.11",
10550
- // 取安装包自身的 FileVersion,不是构建目录名里的紧凑形(20260814.5):判定拿它和
10551
- // InstallInfo.xml 的 InstallAppReleaseVer 比,那边是点分四段。写成紧凑形会让
10552
- // 20260814 > 2026 恒成立,「有可用更新」永远消不掉。
10553
- mini: "2026.08.14.5"
10640
+ full: OFFICIAL_INSTALLERS.public.full.version,
10641
+ mini: OFFICIAL_INSTALLERS.public.mini.version
10554
10642
  },
10555
10643
  REL: { full: "", mini: "" },
10556
10644
  DEBUG: {
@@ -10567,8 +10655,10 @@ var defaultAssistantRuntimeConfig = {
10567
10655
  //
10568
10656
  // REL/DEBUG 没有对应发布面,留空即跳过远程判定。地址不可达或文件不存在同样 fail-open,
10569
10657
  // 所以这条链路默认是关的,直到有人把文件传上去 —— 见 deploy/README.md。
10658
+ // 与安装包同源自 OFFICIAL_MIRROR_ROOTS:内测构建把安装包指到内网时,这条也必须跟着走,
10659
+ // 否则一个 -internal 产物会拿公网索引判闸门 —— 而索引取不到时闸门是 fail-open。
10570
10660
  baseRequirementUrl: {
10571
- OFFICIAL: "https://zlt-data-house.oss-cn-wuhan-lr.aliyuncs.com/zltkj.site/down/v1/up/Zlt.Std.Official/VscodeMarketPlugin/zlt_quant_version_index.json",
10661
+ OFFICIAL: officialVersionIndexUrl("public"),
10572
10662
  REL: "",
10573
10663
  DEBUG: ""
10574
10664
  },
@@ -10771,13 +10861,16 @@ function applyEnvOverrides(config2) {
10771
10861
  const sdkTrustedHostOverride = process.env.ZLT_PYTHON_SDK_TRUSTED_HOST;
10772
10862
  const baseRequirementUrlOverride = process.env.ZLT_BASE_REQUIREMENT_URL?.trim();
10773
10863
  const installer = parseInstallerOverride(process.env.ZLT_ASSISTANT_INSTALLER_OVERRIDE);
10864
+ const mirror = resolveInstallerMirror();
10865
+ const mirrored = applyInstallerMirror(config2, mirror);
10866
+ const mirroredIndex = mirror === "public" ? config2.baseRequirementUrl : { ...config2.baseRequirementUrl, OFFICIAL: officialVersionIndexUrl(mirror) };
10774
10867
  return {
10775
10868
  ...config2,
10776
10869
  envMode,
10777
- baseRequirementUrl: baseRequirementUrlOverride ? { ...config2.baseRequirementUrl, [envMode]: baseRequirementUrlOverride } : config2.baseRequirementUrl,
10778
- backserviceDownload: applyInstallerOverride(config2.backserviceDownload, envMode, installer, "url"),
10779
- backserviceDownloadSha256: applyInstallerOverride(config2.backserviceDownloadSha256, envMode, installer, "sha256"),
10780
- backserviceDownloadVersion: applyInstallerOverride(config2.backserviceDownloadVersion, envMode, installer, "version"),
10870
+ baseRequirementUrl: baseRequirementUrlOverride ? { ...mirroredIndex, [envMode]: baseRequirementUrlOverride } : mirroredIndex,
10871
+ backserviceDownload: applyInstallerOverride(mirrored.backserviceDownload, envMode, installer, "url"),
10872
+ backserviceDownloadSha256: applyInstallerOverride(mirrored.backserviceDownloadSha256, envMode, installer, "sha256"),
10873
+ backserviceDownloadVersion: applyInstallerOverride(mirrored.backserviceDownloadVersion, envMode, installer, "version"),
10781
10874
  pythonRuntimePackages: runtimePackages.length > 0 ? runtimePackages : config2.pythonRuntimePackages,
10782
10875
  pythonRuntimeIndexUrl: process.env.ZLT_PYTHON_RUNTIME_INDEX_URL?.trim() || config2.pythonRuntimeIndexUrl,
10783
10876
  pythonSdkPackages: packages.length > 0 ? packages : envModeChanged ? config2.pythonSdkPackagesByMode[envMode] : config2.pythonSdkPackages,
@@ -10787,6 +10880,25 @@ function applyEnvOverrides(config2) {
10787
10880
  pythonPipWheelSha256: process.env.ZLT_PYTHON_PIP_WHEEL_SHA256?.trim() || config2.pythonPipWheelSha256
10788
10881
  };
10789
10882
  }
10883
+ function applyInstallerMirror(config2, mirror) {
10884
+ if (mirror === "public")
10885
+ return config2;
10886
+ const pins = OFFICIAL_INSTALLERS[mirror];
10887
+ return {
10888
+ backserviceDownload: {
10889
+ ...config2.backserviceDownload,
10890
+ OFFICIAL: { full: officialInstallerUrl("full", mirror), mini: officialInstallerUrl("mini", mirror) }
10891
+ },
10892
+ backserviceDownloadSha256: {
10893
+ ...config2.backserviceDownloadSha256,
10894
+ OFFICIAL: { full: pins.full.sha256, mini: pins.mini.sha256 }
10895
+ },
10896
+ backserviceDownloadVersion: {
10897
+ ...config2.backserviceDownloadVersion,
10898
+ OFFICIAL: { full: pins.full.version, mini: pins.mini.version }
10899
+ }
10900
+ };
10901
+ }
10790
10902
  function parseInstallerOverride(raw) {
10791
10903
  const text = raw?.trim();
10792
10904
  if (!text)
@@ -11038,7 +11150,34 @@ var AssistantMasterIpcClient = class {
11038
11150
  }
11039
11151
  }
11040
11152
  }
11041
- async startDefaultServices(config2) {
11153
+ /**
11154
+ * 安装/启动通道动手前,先把 VSCode PID 注册进智能管家的监控表。
11155
+ *
11156
+ * 智能管家侧(xzs)每 ~10 秒查一次 monitor/check_reg_empty,注册表持续为空约 30 秒
11157
+ * 就把整族进程自退回收 —— 2026-08-25 全新装机实测:TY 模块 202MB 下载到 88% 时
11158
+ * ZLTBackService 被回收,ZLTHome 随后退出,下载轮询以管道断开告终
11159
+ * (FAILURE-PATTERNS #77)。常规注册挂在 ZLTAF connected 边沿(vscode-ext 的
11160
+ * assistant-monitor),而首次安装期间 ZLTAF 尚不存在,那条边沿永远等不到;
11161
+ * claw-host 的安装通道在每次 IPC 连接后都上报 PID
11162
+ * (assistant-installer.ts:1132/:1442),此处对齐。重复注册幂等。
11163
+ * 仅检测通道(checkBacktestModuleUpdates)刻意不注册 —— 后台检查若注册会阻止
11164
+ * 空闲自退,把「已装但未运行」这个合法状态挡掉。
11165
+ *
11166
+ * 注册失败绝不拦安装:旧本体可能没有 monitor API,失败只降级为一行进度日志。
11167
+ *
11168
+ * @param config 运行时配置,取其 ipcConnectTimeout 作为请求超时
11169
+ * @param onProgress 可选进度回调,仅在注册失败时收到一行说明
11170
+ */
11171
+ async reportInstallLanePid(config2, onProgress) {
11172
+ try {
11173
+ await this.reportVSCodePid(process.pid, config2);
11174
+ } catch (error2) {
11175
+ const message = error2 instanceof Error ? error2.message : String(error2);
11176
+ onProgress?.(`VSCode \u8FDB\u7A0B\u6CE8\u518C\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5B89\u88C5\u7EE7\u7EED\uFF09: ${message}`);
11177
+ }
11178
+ }
11179
+ async startDefaultServices(config2, onProgress) {
11180
+ await this.reportInstallLanePid(config2, onProgress);
11042
11181
  for (const serviceIndex of config2.manualServiceIndices) {
11043
11182
  await this.sendRequest("zltMClient", "CallInnerRpc", {
11044
11183
  modeType: 0,
@@ -11092,6 +11231,7 @@ var AssistantMasterIpcClient = class {
11092
11231
  }
11093
11232
  }
11094
11233
  async installBacktestModules(workRoot, config2, onProgress, hooks = {}) {
11234
+ await this.reportInstallLanePid(config2, onProgress);
11095
11235
  const moduleIndices = [
11096
11236
  CLIENT_PLUGIN_MODULE_INDEX,
11097
11237
  ...config2.backtestModuleIndices.filter((moduleIndex) => moduleIndex !== CLIENT_PLUGIN_MODULE_INDEX)
@@ -11514,17 +11654,26 @@ var AssistantMasterIpcClient = class {
11514
11654
  throw new Error(formatModuleInstallError("\u6295\u7814\u6A21\u5757\u5305\u8986\u76D6\u5B89\u88C5\u72B6\u6001\u8F6E\u8BE2\u8D85\u65F6", moduleIndex, lastProgress ?? { status: 0, process: 0, phase: 0, response: null }));
11515
11655
  }
11516
11656
  };
11517
- async function startManualServices(workRoot, zltContent, config2) {
11657
+ async function startManualServices(workRoot, zltContent, config2, onProgress) {
11518
11658
  const client = new AssistantMasterIpcClient();
11519
11659
  try {
11520
11660
  await client.connect(zltContent, config2);
11521
- await client.startDefaultServices(config2);
11661
+ await client.startDefaultServices(config2, onProgress);
11522
11662
  await client.pollStartupServices(config2);
11523
11663
  await waitForPipe(workRoot, "ZLTAF", config2.zltafPipeWait);
11524
11664
  } finally {
11525
11665
  await client.disconnect();
11526
11666
  }
11527
11667
  }
11668
+ async function unregisterRuntimeConsumerPid(zltContent, config2) {
11669
+ const client = new AssistantMasterIpcClient();
11670
+ try {
11671
+ await client.connect(zltContent, config2);
11672
+ await client.logoutVSCodePid(process.pid, config2);
11673
+ } finally {
11674
+ await client.disconnect();
11675
+ }
11676
+ }
11528
11677
  async function installBacktestModules(workRoot, zltContent, config2, onProgress, hooks = {}) {
11529
11678
  const client = new AssistantMasterIpcClient();
11530
11679
  try {
@@ -12224,25 +12373,11 @@ async function acquireMcpRuntime(config2, explicitWorkRoot) {
12224
12373
  const workRoot = explicitWorkRoot ?? await locateInstalledRuntime(config2);
12225
12374
  return acquireRuntimeLease(workRoot, "starter", {
12226
12375
  ensureRuntime: async () => {
12227
- const launchedPid = await startRuntimeAndServices(workRoot, config2);
12228
- if (!launchedPid)
12229
- return null;
12230
- const identity = await waitForProcessIdentity(launchedPid, config2.serviceStartupWait);
12231
- if (!identity)
12232
- throw new Error(`\u65E0\u6CD5\u786E\u8BA4 MCP \u542F\u52A8\u7684 ZLTAF \u8FDB\u7A0B\u8EAB\u4EFD: pid=${launchedPid}`);
12233
- return identity;
12376
+ await startAssistantManagedRuntime(workRoot, config2);
12377
+ return null;
12234
12378
  }
12235
12379
  });
12236
12380
  }
12237
- async function startRuntimeAndServices(workRoot, config2) {
12238
- ensureUsersProjectsDirectory(workRoot);
12239
- const launchReady = validateRuntimeLaunchReady(workRoot, config2);
12240
- if (!launchReady.complete) {
12241
- throw new Error(launchReady.reason || "ZLTAF runtime \u542F\u52A8\u4F9D\u8D56\u4E0D\u5B8C\u6574");
12242
- }
12243
- await startAssistantBackServices(workRoot, config2);
12244
- return waitForZltafPipe(workRoot, config2, { startIfMissing: true });
12245
- }
12246
12381
  var AssistantNotRunningError = class extends Error {
12247
12382
  code = "ASSISTANT_NOT_RUNNING";
12248
12383
  constructor(message = "\u667A\u80FD\u7BA1\u5BB6\u5F53\u524D\u672A\u8FD0\u884C\uFF1B\u5F53\u524D\u64CD\u4F5C\u4EC5\u68C0\u6D4B runtime\uFF0C\u4E0D\u4F1A\u542F\u52A8\u667A\u80FD\u7BA1\u5BB6") {
@@ -12267,7 +12402,7 @@ async function startAssistantBackServices(workRoot, config2, onProgress, options
12267
12402
  return;
12268
12403
  const zltContent = await ensureAssistantHostRunning(workRoot, config2, onProgress, options);
12269
12404
  onProgress?.("\u542F\u52A8\u667A\u80FD\u7BA1\u5BB6\u540E\u53F0\u670D\u52A1");
12270
- await startManualServices(workRoot, zltContent, config2);
12405
+ await startManualServices(workRoot, zltContent, config2, onProgress);
12271
12406
  }
12272
12407
  async function ensureAssistantHostRunning(workRoot, config2, onProgress, options = {}) {
12273
12408
  const masterPipeExists = (suffix) => pipeExists(workRoot, `${config2.masterPipePrefix}_${suffix}`);
@@ -12291,11 +12426,13 @@ var ASSISTANT_UPGRADE_FINISH_LOCK = path6.join("Temporary", "upgradeFinsh.lock")
12291
12426
  function launchAssistantHost(workRoot, config2, onProgress) {
12292
12427
  const hostPath = path6.join(workRoot, config2.hostPath);
12293
12428
  onProgress?.("\u542F\u52A8\u667A\u80FD\u7BA1\u5BB6");
12429
+ const { ELECTRON_RUN_AS_NODE: _dropped, ...env } = process.env;
12294
12430
  const child = (0, import_child_process3.spawn)(hostPath, ["startwithplugin", "selfstart"], {
12295
12431
  cwd: path6.dirname(hostPath),
12296
12432
  detached: true,
12297
12433
  stdio: "ignore",
12298
- windowsHide: true
12434
+ windowsHide: true,
12435
+ env
12299
12436
  });
12300
12437
  if (!child.pid)
12301
12438
  throw new Error(`\u667A\u80FD\u7BA1\u5BB6\u542F\u52A8\u540E\u672A\u8FD4\u56DE PID: ${hostPath}`);
@@ -12379,15 +12516,18 @@ async function installRuntime(options) {
12379
12516
  const installDir = options.installDir || getDefaultInstallPath();
12380
12517
  const installerPath = options.installerPath || await downloadRuntimeInstaller(options.config, options.onProgress, options.installType || "full");
12381
12518
  fs6.mkdirSync(installDir, { recursive: true });
12519
+ const installerLogPath = path6.join(installDir, "Temporary", "logs", "install.log");
12520
+ fs6.mkdirSync(path6.dirname(installerLogPath), { recursive: true });
12382
12521
  options.onProgress?.(`\u6267\u884C\u5B89\u88C5\u7A0B\u5E8F: ${installerPath}`);
12383
- const exitCode = await executeInstallerProcess(installerPath, installDir);
12522
+ const exitCode = await executeInstallerProcess(installerPath, installDir, installerLogPath);
12384
12523
  options.onProgress?.(`\u5B89\u88C5\u8FDB\u7A0B\u9000\u51FA\u7801: ${exitCode}`);
12385
12524
  if (exitCode !== 0) {
12386
12525
  const validateInstalled = options.installModules === false ? validateAssistantBaseComplete : validateWorkRootComplete;
12387
12526
  const directValidation = validateInstalled(installDir, options.config);
12388
12527
  const registryWorkRoot = options.installModules === false ? await readRegistryInstallLocationRaw(options.config) : await readRegistryInstallLocation(options.config);
12389
12528
  if (!directValidation.complete && !registryWorkRoot) {
12390
- throw new Error(`\u5B89\u88C5\u7A0B\u5E8F\u6267\u884C\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${exitCode}; ${directValidation.reason || "\u672A\u53D1\u73B0\u5B8C\u6574 ZLTAF runtime"}`);
12529
+ const cause = describeInstallerExitCode(exitCode);
12530
+ throw new Error(`\u5B89\u88C5\u7A0B\u5E8F\u6267\u884C\u5931\u8D25\uFF08\u9000\u51FA\u7801 ${exitCode}\uFF09` + (cause ? `\uFF1A${cause}` : "") + (directValidation.reason ? `\uFF08${directValidation.reason}\uFF09` : "") + `\u3002\u5B89\u88C5\u65E5\u5FD7: ${installerLogPath}`);
12391
12531
  }
12392
12532
  options.onProgress?.(`\u5B89\u88C5\u7A0B\u5E8F\u9000\u51FA\u7801\u975E 0\uFF0C\u4F46\u57FA\u7840 runtime \u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u7EE7\u7EED\u521D\u59CB\u5316: ${exitCode}`);
12393
12533
  }
@@ -12956,7 +13096,28 @@ async function downloadRuntimeInstaller(config2, onProgress, installType = "full
12956
13096
  onProgress?.(`\u5B89\u88C5\u5305 SHA256 \u6821\u9A8C\u901A\u8FC7: ${sha256}`);
12957
13097
  return destination;
12958
13098
  }
12959
- function executeInstallerProcess(installerPath, installDir) {
13099
+ function describeInstallerExitCode(exitCode) {
13100
+ switch (exitCode) {
13101
+ case 1:
13102
+ return "\u5B89\u88C5\u7A0B\u5E8F\u672A\u80FD\u5F00\u59CB\u5B89\u88C5\uFF0C\u6700\u5E38\u89C1\u539F\u56E0\u662F\u53E6\u4E00\u4E2A\u667A\u80FD\u7BA1\u5BB6\u5B89\u88C5\u6216\u5378\u8F7D\u7A0B\u5E8F\u6B63\u5728\u8FD0\u884C\uFF08\u7A97\u53E3\u53EF\u80FD\u88AB\u906E\u6321\uFF0C\u6216\u505C\u5728\u63D0\u6743\u786E\u8BA4\u4E0A\uFF09\u3002\u8BF7\u5173\u95ED\u6240\u6709\u5B89\u88C5/\u5378\u8F7D\u7A97\u53E3\u540E\u91CD\u8BD5";
13103
+ case 2:
13104
+ return "\u5B89\u88C5\u5728\u5F00\u59CB\u524D\u88AB\u7528\u6237\u53D6\u6D88";
13105
+ case 3:
13106
+ case 4:
13107
+ return "\u5B89\u88C5\u8FC7\u7A0B\u4E2D\u53D1\u751F\u81F4\u547D\u9519\u8BEF\uFF08\u5E38\u89C1\u539F\u56E0\uFF1A\u78C1\u76D8\u7A7A\u95F4\u4E0D\u8DB3\u3001\u76EE\u6807\u6587\u4EF6\u88AB\u5176\u4ED6\u7A0B\u5E8F\u5360\u7528\u3001\u6216\u88AB\u5B89\u5168\u8F6F\u4EF6\u62E6\u622A\uFF09";
13108
+ case 5:
13109
+ return "\u5B89\u88C5\u8FC7\u7A0B\u4E2D\u88AB\u7528\u6237\u53D6\u6D88";
13110
+ case 7:
13111
+ return "\u5B89\u88C5\u524D\u68C0\u67E5\u5224\u5B9A\u5F53\u524D\u73AF\u5883\u65E0\u6CD5\u7EE7\u7EED\u5B89\u88C5";
13112
+ case 8:
13113
+ return "\u4E0A\u4E00\u6B21\u5B89\u88C5\u5C1A\u672A\u5B8C\u6210\uFF0C\u9700\u8981\u91CD\u542F\u7CFB\u7EDF\u540E\u624D\u80FD\u7EE7\u7EED\u5B89\u88C5";
13114
+ case 1223:
13115
+ return "\u5B89\u88C5\u7A0B\u5E8F\u8BF7\u6C42\u7BA1\u7406\u5458\u6743\u9650\u65F6\u88AB\u62D2\u7EDD\uFF08UAC\uFF09";
13116
+ default:
13117
+ return void 0;
13118
+ }
13119
+ }
13120
+ function executeInstallerProcess(installerPath, installDir, logPath) {
12960
13121
  return new Promise((resolve5, reject) => {
12961
13122
  const child = (0, import_child_process3.spawn)(installerPath, [
12962
13123
  "/VERYSILENT",
@@ -12966,7 +13127,7 @@ function executeInstallerProcess(installerPath, installDir) {
12966
13127
  "/NO_ICONS",
12967
13128
  "/MERGETASKS=!desktopicon",
12968
13129
  `/DIR=${installDir}`,
12969
- `/LOG=${path6.join(installDir, "install.log")}`,
13130
+ `/LOG=${logPath}`,
12970
13131
  "/m"
12971
13132
  ], {
12972
13133
  shell: false,
@@ -12975,7 +13136,7 @@ function executeInstallerProcess(installerPath, installDir) {
12975
13136
  });
12976
13137
  child.on("close", (code) => {
12977
13138
  if (code === 2) {
12978
- reject(new Error("\u5B89\u88C5\u88AB\u7528\u6237\u53D6\u6D88\u6216 UAC \u6743\u9650\u88AB\u62D2\u7EDD\uFF0C\u9000\u51FA\u7801: 2"));
13139
+ reject(new Error(`\u5B89\u88C5\u7A0B\u5E8F\u6267\u884C\u5931\u8D25\uFF08\u9000\u51FA\u7801 2\uFF09\uFF1A${describeInstallerExitCode(2)}`));
12979
13140
  } else if (code === null) {
12980
13141
  reject(new Error("\u5B89\u88C5\u7A0B\u5E8F\u6267\u884C\u5931\u8D25\uFF0C\u672A\u8FD4\u56DE\u9000\u51FA\u7801"));
12981
13142
  } else {
@@ -13257,16 +13418,6 @@ async function waitForZltafPipe(workRoot, config2, options) {
13257
13418
  }
13258
13419
  throw new Error(`\u7B49\u5F85 ZLTAF \u7BA1\u9053\u8D85\u65F6: ${discoverPipePath(workRoot, "ZLTAF")}`);
13259
13420
  }
13260
- async function waitForProcessIdentity(pid, timeoutMs) {
13261
- const deadline = Date.now() + Math.max(1e3, timeoutMs);
13262
- while (Date.now() <= deadline) {
13263
- const identity = inspectRuntimeProcess(pid);
13264
- if (identity)
13265
- return identity;
13266
- await delay(100);
13267
- }
13268
- return null;
13269
- }
13270
13421
 
13271
13422
  // ../zlt-runtime-core/dist/auth.js
13272
13423
  var fs7 = __toESM(require("fs"), 1);
@@ -20885,6 +21036,16 @@ var NPM_PACKAGE_CONFIG_TOOL_NAMES = Object.freeze([
20885
21036
  // src/tool-pipeline.ts
20886
21037
  var fs11 = __toESM(require("node:fs"), 1);
20887
21038
  var path11 = __toESM(require("node:path"), 1);
21039
+ var import_node_crypto2 = require("node:crypto");
21040
+
21041
+ // src/feature-set.ts
21042
+ function resolveFeatureSet() {
21043
+ return false ? "core" : "full";
21044
+ }
21045
+ var MASKED_FACTOR_MESSAGE = "\u56E0\u5B50\u7814\u7A76\u529F\u80FD\u5728\u5F53\u524D\u7248\u672C\u672A\u5F00\u653E\u3002";
21046
+ var MASKED_FUTURES_MESSAGE = "\u671F\u8D27\u56DE\u6D4B\u5728\u5F53\u524D\u7248\u672C\u672A\u5F00\u653E\uFF1ArunParams.marketIds \u542B\u671F\u8D27\u4EA4\u6613\u6240\uFF08zj/zs/ds/sq/gq\uFF09\u6216 account_list \u7684 account_type \u4E3A\u671F\u8D27\u3002\u8BF7\u4F7F\u7528\u80A1\u7968\u5E02\u573A\uFF08sh/sz\uFF09\u4E0E account_type=1\u3002";
21047
+
21048
+ // src/tool-pipeline.ts
20888
21049
  var FUTURES_MARKET_IDS = /* @__PURE__ */ new Set(["zj", "zs", "ds", "sq", "gq"]);
20889
21050
  function isFuturesRun(marketIds) {
20890
21051
  if (!marketIds) return false;
@@ -20895,7 +21056,26 @@ function prepareToolArgs(tool, args, contexts) {
20895
21056
  const inherited = inheritReBacktestRunParams(tool, args, contexts);
20896
21057
  const dated = applyBacktestDateDefaults(tool, inherited);
20897
21058
  const named = normalizeBacktestRunName(tool, dated);
20898
- return stripEmptyLegacyUserId(tool, named);
21059
+ const prepared = stripEmptyLegacyUserId(tool, named);
21060
+ return refuseMaskedFuturesBacktest(tool, prepared) ?? prepared;
21061
+ }
21062
+ var FUTURES_GATED_TOOLS = /* @__PURE__ */ new Set(["mcp-create-and-run-backtest", "mcp-re-backtest"]);
21063
+ var FUTURES_ACCOUNT_TYPES = /* @__PURE__ */ new Set([2, 6]);
21064
+ function refuseMaskedFuturesBacktest(tool, args) {
21065
+ if (!FUTURES_GATED_TOOLS.has(tool.name) || resolveFeatureSet() !== "core") return null;
21066
+ const runParams = args.runParams ?? {};
21067
+ const marketIds = Array.isArray(runParams.marketIds) ? runParams.marketIds : [];
21068
+ const accounts = Array.isArray(runParams.account_list) ? runParams.account_list : [];
21069
+ const futuresAccount = accounts.some(
21070
+ (account) => !!account && typeof account === "object" && FUTURES_ACCOUNT_TYPES.has(Number(account.account_type))
21071
+ );
21072
+ if (!isFuturesRun(marketIds) && !futuresAccount) return null;
21073
+ return jsonToolResult(10057, MASKED_FUTURES_MESSAGE, {
21074
+ retryableByAgent: false,
21075
+ tool: tool.name,
21076
+ marketIds,
21077
+ userAction: "\u5F53\u524D\u7248\u672C\u4EC5\u652F\u6301\u80A1\u7968\u56DE\u6D4B\uFF08sh/sz\uFF09\uFF1B\u671F\u8D27\u56DE\u6D4B\u8BF7\u7B49\u5F85\u540E\u7EED\u7248\u672C\u5F00\u653E\u3002"
21078
+ }, true);
20899
21079
  }
20900
21080
  async function dispatchSpecialTool(tool, args, client, timeout, session, waitControl) {
20901
21081
  if (tool.name === "mcp-factor-task-status") {
@@ -20938,7 +21118,7 @@ function shapeToolResult(tool, args, result, session) {
20938
21118
  if (tool.name === "mcp-stop-backtest" && !result.isError && typeof args.runId === "string") {
20939
21119
  session.activeBacktestRunIds.delete(args.runId);
20940
21120
  }
20941
- const echoed = echoResolvedBacktestConfig(tool, args, result);
21121
+ const echoed = echoResolvedBacktestConfig(tool, args, result, session.backtestRunContexts);
20942
21122
  const guarded = applyBacktestResultGuards(
20943
21123
  tool,
20944
21124
  args,
@@ -22098,6 +22278,12 @@ async function runWait(_tool, args, client, timeout, session, control) {
22098
22278
  { action: 'Pass kind="factor" with the executionId from a factor start tool, kind="backtest" with the runId from mcp-create-and-run-backtest, or kind="download" with the uuid from mcp-download-data.' }
22099
22279
  );
22100
22280
  }
22281
+ if (kind === "factor" && resolveFeatureSet() === "core") {
22282
+ return waitError(10057, MASKED_FACTOR_MESSAGE, {
22283
+ action: "Factor research is not available in this build; do not retry with other factor tools.",
22284
+ userAction: MASKED_FACTOR_MESSAGE
22285
+ });
22286
+ }
22101
22287
  if (!id) {
22102
22288
  if (kind === "factor") {
22103
22289
  return waitError(
@@ -22244,13 +22430,22 @@ function captureBacktestRunContext(tool, args, result, contexts, activeRunIds) {
22244
22430
  const codePath = typeof parsed.Data.codePath === "string" && parsed.Data.codePath.trim() ? parsed.Data.codePath.trim() : prior?.codePath;
22245
22431
  if (runId) {
22246
22432
  const marketIds = Array.isArray(runParams.marketIds) ? runParams.marketIds.filter((id) => typeof id === "string") : prior?.marketIds;
22247
- contexts.set(runId, { strategyId, start, end, codePath, marketIds, runParams: { ...runParams } });
22433
+ const sourceSha256 = hashBacktestSource(codePath) ?? prior?.sourceSha256;
22434
+ contexts.set(runId, {
22435
+ strategyId,
22436
+ start,
22437
+ end,
22438
+ codePath,
22439
+ marketIds,
22440
+ runParams: { ...runParams },
22441
+ sourceSha256
22442
+ });
22248
22443
  activeRunIds.add(runId);
22249
22444
  }
22250
22445
  } catch {
22251
22446
  }
22252
22447
  }
22253
- function echoResolvedBacktestConfig(tool, args, result) {
22448
+ function echoResolvedBacktestConfig(tool, args, result, contexts) {
22254
22449
  if (tool.name !== "mcp-create-and-run-backtest" && tool.name !== "mcp-re-backtest" || result.isError || result.content.length === 0) return result;
22255
22450
  const runParams = isRecord2(args.runParams) ? args.runParams : {};
22256
22451
  const resolvedStart = typeof runParams.start === "string" ? runParams.start : "";
@@ -22268,10 +22463,23 @@ function echoResolvedBacktestConfig(tool, args, result) {
22268
22463
  if (isDailyBacktestFrequency(runParams.freq)) echo.dailyVisibilityNote = DAILY_VISIBILITY_NOTE;
22269
22464
  const endWarning = describeFutureBacktestEnd(resolvedEnd);
22270
22465
  if (endWarning) echo.endWarning = endWarning;
22271
- if (Object.keys(echo).length === 0) return result;
22272
22466
  try {
22273
22467
  const parsed = JSON.parse(result.content[0].text);
22274
22468
  if (!isRecord2(parsed) || !isRecord2(parsed.Data)) return result;
22469
+ const echoedRunId = typeof parsed.Data.runId === "string" ? parsed.Data.runId.trim() : "";
22470
+ const runId = echoedRunId || (tool.name === "mcp-re-backtest" && typeof args.runId === "string" ? args.runId.trim() : "");
22471
+ const context = runId ? contexts.get(runId) : void 0;
22472
+ if (runId) {
22473
+ echo.evidence = {
22474
+ contextAvailable: Boolean(context),
22475
+ backtestId: runId,
22476
+ ...context?.codePath ? { runDir: context.codePath } : {},
22477
+ ...context?.strategyId ? { strategyId: context.strategyId } : {},
22478
+ ...context?.runParams ? { effectiveRunParams: { ...context.runParams } } : {},
22479
+ ...context?.sourceSha256 ? { sourceSha256: context.sourceSha256 } : {}
22480
+ };
22481
+ }
22482
+ if (Object.keys(echo).length === 0) return result;
22275
22483
  return {
22276
22484
  ...result,
22277
22485
  content: [{
@@ -22286,6 +22494,16 @@ function echoResolvedBacktestConfig(tool, args, result) {
22286
22494
  return result;
22287
22495
  }
22288
22496
  }
22497
+ function hashBacktestSource(codePath) {
22498
+ if (!codePath) return void 0;
22499
+ try {
22500
+ const sourcePath = path11.join(codePath, "main.py");
22501
+ if (!fs11.statSync(sourcePath).isFile()) return void 0;
22502
+ return (0, import_node_crypto2.createHash)("sha256").update(fs11.readFileSync(sourcePath)).digest("hex");
22503
+ } catch {
22504
+ return void 0;
22505
+ }
22506
+ }
22289
22507
  var BENCHMARK_OVERRIDE_NOTE = "\u8FD9\u662F\u8BF7\u6C42\u7684\u57FA\u51C6\uFF0C\u4E0D\u4E00\u5B9A\u662F\u5B9E\u9645\u751F\u6548\u7684\uFF1A\u7B56\u7565 initialize \u91CC\u7684 set_benchmark() \u4F1A\u8986\u76D6\u5B83\uFF0C\u6536\u76CA\u4EA7\u7269\u4E2D\u7684\u300C\u57FA\u51C6\u6536\u76CA\u300D\u6309\u7B56\u7565\u5B9E\u9645\u7528\u7684\u57FA\u51C6\u8BA1\u7B97\u3002\u62A5\u544A\u57FA\u51C6\u662F\u4EC0\u4E48\u4E4B\u524D\uFF0C\u5148\u8BFB\u7B56\u7565\u4EE3\u7801\u91CC\u7684 set_benchmark\u3002";
22290
22508
  var DAILY_VISIBILITY_NOTE = "\u65E5\u7EBF\u7C7B\u9891\u7387\u7684\u7B80\u5355\u56DE\u6D4B\u53EA\u80FD\u770B\u5230 context.previous_date \u53CA\u4EE5\u524D\u7684\u5B8C\u6574\u65E5 K\uFF1B\u5F53\u65E5\u6536\u76D8\u5728\u56DE\u6D4B\u4E2D\u4E0D\u53EF\u89C1\uFF0Cafter_trading_end \u540C\u6837\u770B\u4E0D\u5230\u3002\u6240\u4EE5\u8981\u7528\u67D0\u4E2A\u4EA4\u6613\u65E5 T \u7684\u6536\u76D8\u505A\u7B5B\u9009\u6216\u8BA1\u7B97\uFF0CrunParams.end \u5FC5\u987B\u8BBE\u6210 T \u7684\u4E0B\u4E00\u4E2A\u4EA4\u6613\u65E5\uFF1B\u53EA\u8DD1 [T, T] \u5355\u65E5\uFF0C\u53D6\u5230\u7684\u662F T-1 \u7684\u6536\u76D8\u3002\u5206\u949F\u3001Tick\u3001\u7814\u7A76\u3001\u4EFF\u771F\u3001\u5B9E\u76D8\u4E0D\u53D7\u6B64\u9650\u3002";
22291
22509
  function isDailyBacktestFrequency(value) {
@@ -22709,7 +22927,7 @@ function extractStrategyTreeIds(result) {
22709
22927
  }
22710
22928
  return ids;
22711
22929
  }
22712
- async function executeSteps(steps, client, initialBody, timeout, repairHint = identityRepairHint) {
22930
+ async function executeSteps(steps, client, initialBody, timeout, repairHint = identityRepairHint, toolName) {
22713
22931
  let body = { ...initialBody };
22714
22932
  for (let i = 0; i < steps.length; i++) {
22715
22933
  const step = steps[i];
@@ -22746,6 +22964,10 @@ async function executeSteps(steps, client, initialBody, timeout, repairHint = id
22746
22964
  }
22747
22965
  }
22748
22966
  }
22967
+ if (i < steps.length - 1) {
22968
+ const blocked = preflightCreatedBacktestSource(toolName, body);
22969
+ if (blocked) return blocked;
22970
+ }
22749
22971
  if (i === steps.length - 1) {
22750
22972
  const finalData = repairHint(mergeStepFieldsToFinalData(parsed.data, body, step.mergeToFinalData));
22751
22973
  return {
@@ -22756,6 +22978,126 @@ async function executeSteps(steps, client, initialBody, timeout, repairHint = id
22756
22978
  }
22757
22979
  return { content: [{ type: "text", text: "{}" }] };
22758
22980
  }
22981
+ function pythonCodeLines(source) {
22982
+ let state = null;
22983
+ return source.split(/\r?\n/).map((line) => {
22984
+ let code = "";
22985
+ for (let i = 0; i < line.length; ) {
22986
+ if (state === "'''" || state === '"""') {
22987
+ if (line.startsWith(state, i)) {
22988
+ i += 3;
22989
+ state = null;
22990
+ } else {
22991
+ code += " ";
22992
+ i += 1;
22993
+ }
22994
+ continue;
22995
+ }
22996
+ if (state === "'" || state === '"') {
22997
+ if (line[i] === "\\") {
22998
+ code += " ";
22999
+ i += 2;
23000
+ } else if (line[i] === state) {
23001
+ code += " ";
23002
+ i += 1;
23003
+ state = null;
23004
+ } else {
23005
+ code += " ";
23006
+ i += 1;
23007
+ }
23008
+ continue;
23009
+ }
23010
+ if (line[i] === "#") break;
23011
+ if (line.startsWith("'''", i) || line.startsWith('"""', i)) {
23012
+ state = line.slice(i, i + 3);
23013
+ code += " ";
23014
+ i += 3;
23015
+ continue;
23016
+ }
23017
+ if (line[i] === "'" || line[i] === '"') {
23018
+ state = line[i];
23019
+ code += " ";
23020
+ i += 1;
23021
+ continue;
23022
+ }
23023
+ code += line[i];
23024
+ i += 1;
23025
+ }
23026
+ if (state === "'" || state === '"') state = null;
23027
+ return code;
23028
+ });
23029
+ }
23030
+ function findUnsupportedCurrentDataGet(source) {
23031
+ const lines = pythonCodeLines(source);
23032
+ const aliases = /* @__PURE__ */ new Set();
23033
+ const assignment = /\b([A-Za-z_]\w*)\s*(?::[^=]+)?=\s*get_current_data\s*\(\s*\)/g;
23034
+ for (const line of lines) {
23035
+ assignment.lastIndex = 0;
23036
+ let match;
23037
+ while ((match = assignment.exec(line)) !== null) aliases.add(match[1]);
23038
+ }
23039
+ let changed = true;
23040
+ while (changed) {
23041
+ changed = false;
23042
+ for (const line of lines) {
23043
+ for (const alias of aliases) {
23044
+ const escaped = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
23045
+ const copied = new RegExp(`\\b([A-Za-z_]\\w*)\\s*(?::[^=]+)?=\\s*${escaped}\\b`).exec(line);
23046
+ if (copied && !aliases.has(copied[1])) {
23047
+ aliases.add(copied[1]);
23048
+ changed = true;
23049
+ }
23050
+ }
23051
+ }
23052
+ }
23053
+ const violations = [];
23054
+ lines.forEach((line, index) => {
23055
+ if (/get_current_data\s*\(\s*\)\s*\.\s*get\s*\(/.test(line)) {
23056
+ violations.push({ line: index + 1, expression: "get_current_data().get(...)" });
23057
+ }
23058
+ for (const alias of aliases) {
23059
+ const escaped = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
23060
+ if (new RegExp(`\\b${escaped}\\s*\\.\\s*get\\s*\\(`).test(line)) {
23061
+ violations.push({ line: index + 1, expression: `${alias}.get(...)` });
23062
+ }
23063
+ }
23064
+ });
23065
+ return violations.filter((item, index, all) => all.findIndex((candidate) => candidate.line === item.line && candidate.expression === item.expression) === index);
23066
+ }
23067
+ function preflightCreatedBacktestSource(toolName, body) {
23068
+ if (toolName !== "mcp-create-and-run-backtest") return null;
23069
+ const runId = typeof body.runId === "string" ? body.runId.trim() : "";
23070
+ const codePath = typeof body.codePath === "string" ? body.codePath.trim() : "";
23071
+ if (!codePath || !fs11.existsSync(codePath)) return null;
23072
+ const sourcePath = path11.join(codePath, "main.py");
23073
+ if (!fs11.existsSync(sourcePath)) return null;
23074
+ let source;
23075
+ try {
23076
+ source = fs11.readFileSync(sourcePath, "utf8");
23077
+ } catch (error2) {
23078
+ const message = error2 instanceof Error ? error2.message : String(error2);
23079
+ return jsonToolResult(10061, "\u56DE\u6D4B\u4EE3\u7801\u5FEB\u7167\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u672A\u542F\u52A8\u7B56\u7565\u5F15\u64CE\u3002", {
23080
+ retryableByAgent: false,
23081
+ runId,
23082
+ codePath,
23083
+ sourcePath,
23084
+ sourcePreflight: "blocked",
23085
+ reason: message
23086
+ }, true);
23087
+ }
23088
+ const violations = findUnsupportedCurrentDataGet(source);
23089
+ if (violations.length === 0) return null;
23090
+ return jsonToolResult(10061, "\u56DE\u6D4B\u4EE3\u7801\u4F7F\u7528\u4E86 get_current_data().get(...)\uFF0C\u5F53\u524D\u5BF9\u8C61\u4E0D\u652F\u6301 .get()\uFF1B\u5DF2\u521B\u5EFA\u5FEB\u7167\u4F46\u672A\u542F\u52A8\u7B56\u7565\u5F15\u64CE\u3002", {
23091
+ retryableByAgent: false,
23092
+ runId,
23093
+ codePath,
23094
+ sourcePath,
23095
+ sourcePreflight: "blocked",
23096
+ sourceSha256: (0, import_node_crypto2.createHash)("sha256").update(source).digest("hex"),
23097
+ violations,
23098
+ userAction: "\u628A current_data.get(security) \u6539\u4E3A current_data[security]\uFF0C\u5E76\u7528 KeyError \u5904\u7406\u7F3A\u5931\u8BC1\u5238\uFF1B\u7136\u540E\u91CD\u65B0\u521B\u5EFA\u5E76\u8FD0\u884C\u56DE\u6D4B\u3002"
23099
+ }, true);
23100
+ }
22759
23101
  function buildStepBody(step, body) {
22760
23102
  if (!step.bodyMapping) return body;
22761
23103
  const mappedBody = {};
@@ -23793,7 +24135,7 @@ function summarizeBacktestProfit(runDir, options = {}) {
23793
24135
  function handleBacktestProfitTool(args, contexts) {
23794
24136
  const backtestId = typeof args.backtestId === "string" ? args.backtestId.trim() : "";
23795
24137
  if (!backtestId) {
23796
- return toolText({ ErrNo: 10057, ErrMsg: "\u7F3A\u5C11 backtestId\uFF0C\u65E0\u6CD5\u5B9A\u4F4D\u56DE\u6D4B\u4EA7\u7269\u76EE\u5F55\u3002", Data: null }, true);
24138
+ return toolText({ ErrNo: 10060, ErrMsg: "\u7F3A\u5C11 backtestId\uFF0C\u65E0\u6CD5\u5B9A\u4F4D\u56DE\u6D4B\u4EA7\u7269\u76EE\u5F55\u3002", Data: { retryableByAgent: true } }, true);
23797
24139
  }
23798
24140
  const explicit = typeof args.codePath === "string" ? args.codePath.trim() : "";
23799
24141
  const runDir = explicit || contexts.get(backtestId)?.codePath || "";
@@ -23804,13 +24146,24 @@ function handleBacktestProfitTool(args, contexts) {
23804
24146
  Data: null
23805
24147
  }, true);
23806
24148
  }
24149
+ const context = contexts.get(backtestId);
24150
+ const summary = summarizeBacktestProfit(runDir, {
24151
+ expectedBacktestId: backtestId,
24152
+ includeSeries: args.includeSeries === true
24153
+ });
24154
+ summary.evidence = {
24155
+ contextAvailable: Boolean(context),
24156
+ requestedBacktestId: backtestId,
24157
+ ...summary.backtestId ? { artifactBacktestId: summary.backtestId } : {},
24158
+ runDir,
24159
+ ...context?.strategyId ? { strategyId: context.strategyId } : {},
24160
+ ...context?.runParams ? { effectiveRunParams: { ...context.runParams } } : {},
24161
+ ...context?.sourceSha256 ? { sourceSha256: context.sourceSha256 } : {}
24162
+ };
23807
24163
  return toolText({
23808
24164
  ErrNo: 0,
23809
24165
  ErrMsg: "",
23810
- Data: summarizeBacktestProfit(runDir, {
23811
- expectedBacktestId: backtestId,
23812
- includeSeries: args.includeSeries === true
23813
- })
24166
+ Data: summary
23814
24167
  });
23815
24168
  }
23816
24169
 
@@ -23848,7 +24201,7 @@ var RUNTIME_TOOLS = [
23848
24201
  },
23849
24202
  {
23850
24203
  name: "zlt-runtime-start",
23851
- description: "\u4EE5 ZLTAF direct mode \u542F\u52A8\u5DF2\u5B89\u88C5\u7684\u667A\u91CF\u901A\u672C\u5730\u56DE\u6D4B runtime\uFF1A\u76F4\u63A5\u542F\u52A8 zltaf/zltaf.exe \u5E76\u7B49\u5F85 ZLTAF \u7BA1\u9053\u53EF\u7528\u3002\u672C\u5DE5\u5177\u4E0D\u4F1A\u542F\u52A8 ZLTHome\u3001ZLTBackService\u3001Code.exe \u6216 jsservice\uFF0C\u4E5F\u4E0D\u8BFB\u53D6 backServiceRun.json\u3002\u5B89\u88C5\u5B8C\u6210\u540E\u5982\u679C backtestReady=false\uFF0C\u5E94\u8C03\u7528 zlt-runtime-install \u4FEE\u590D zltaf\u3001Python/Git \u6216 SDK\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\u3002",
24204
+ description: "\u542F\u52A8\u5DF2\u5B89\u88C5\u7684\u667A\u91CF\u901A\u672C\u5730\u56DE\u6D4B runtime\uFF1A\u7ECF\u667A\u80FD\u7BA1\u5BB6\uFF08ZLTHome selfstart\uFF09\u6258\u7BA1\u62C9\u8D77\u5E76\u7B49\u5F85\u5176\u521B\u5EFA\u7684 ZLTAF \u7BA1\u9053\uFF0C\u7EDD\u4E0D\u76F4\u63A5\u542F\u52A8 zltaf.exe\u3002\u672C\u5DE5\u5177\u4E0D\u4F1A\u542F\u52A8 Code.exe \u6216 jsservice\uFF0C\u4E5F\u4E0D\u8BFB\u53D6 backServiceRun.json\u3002\u5B89\u88C5\u5B8C\u6210\u540E\u5982\u679C backtestReady=false\uFF0C\u5E94\u8C03\u7528 zlt-runtime-install \u4FEE\u590D zltaf\u3001Python/Git \u6216 SDK\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\u3002",
23852
24205
  inputSchema: {
23853
24206
  type: "object",
23854
24207
  properties: {},
@@ -23858,7 +24211,7 @@ var RUNTIME_TOOLS = [
23858
24211
  },
23859
24212
  {
23860
24213
  name: "zlt-runtime-install",
23861
- description: "\u4E0B\u8F7D\u5E76\u5B89\u88C5\u667A\u91CF\u901A\u672C\u5730 ZLTAF runtime\uFF0C\u6821\u9A8C\u6295\u7814\u6A21\u5757\u4E0B\u53D1\u7684 Python/MinGit \u5DE5\u5177\u94FE\u4E0E zltquant/zltsdk\uFF0C\u5E76\u4EE5 ZLTAF direct mode \u542F\u52A8\u56DE\u6D4B\u670D\u52A1\u3002Python \u4E0E MinGit \u5F52\u6295\u7814\u6A21\u5757\u4E0B\u53D1\uFF0C\u672C\u5DE5\u5177\u53EA\u6821\u9A8C\u3001\u4E0D\u518D\u81EA\u884C\u4E0B\u8F7D\u6216\u4FEE\u590D\u5B83\u4EEC\uFF1A\u7F3A\u5931\u65F6\u4F1A\u660E\u786E\u62A5\u51FA\u300C\u8BF7\u5728\u667A\u80FD\u7BA1\u5BB6\u4E2D\u5B89\u88C5\u6216\u4FEE\u590D\u6295\u7814\u6A21\u5757\u300D\u3002\u8BE5\u64CD\u4F5C\u4F1A\u5199\u5165\u672C\u673A\u78C1\u76D8\u5E76\u53EF\u80FD\u89E6\u53D1 Windows \u5B89\u88C5/UAC\uFF0C\u8C03\u7528\u524D\u5FC5\u987B\u53D6\u5F97\u7528\u6237\u786E\u8BA4\uFF0C\u5E76\u4F20\u5165 confirm=true\u3002\u5B89\u88C5\u5305\u5FC5\u987B\u81EA\u5E26 zltaf\uFF1B\u672C\u5DE5\u5177\u4E0D\u4F1A\u901A\u8FC7 Assistant IPC\u3001ZLTHome \u6216 ZLTBackService \u8865\u88C5\u6A21\u5757\uFF0C\u4E5F\u4E0D\u4F1A\u542F\u52A8 Code.exe/jsservice\u3002",
24214
+ description: "\u4E0B\u8F7D\u5E76\u5B89\u88C5\u667A\u91CF\u901A\u672C\u5730 ZLTAF runtime\uFF0C\u6821\u9A8C\u6295\u7814\u6A21\u5757\u4E0B\u53D1\u7684 Python/MinGit \u5DE5\u5177\u94FE\u4E0E zltquant/zltsdk\uFF0C\u5E76\u7ECF\u667A\u80FD\u7BA1\u5BB6\u6258\u7BA1\u542F\u52A8\u56DE\u6D4B\u670D\u52A1\u3002Python \u4E0E MinGit \u5F52\u6295\u7814\u6A21\u5757\u4E0B\u53D1\uFF0C\u672C\u5DE5\u5177\u53EA\u6821\u9A8C\u3001\u4E0D\u518D\u81EA\u884C\u4E0B\u8F7D\u6216\u4FEE\u590D\u5B83\u4EEC\uFF1A\u7F3A\u5931\u65F6\u4F1A\u660E\u786E\u62A5\u51FA\u300C\u8BF7\u5728\u667A\u80FD\u7BA1\u5BB6\u4E2D\u5B89\u88C5\u6216\u4FEE\u590D\u6295\u7814\u6A21\u5757\u300D\u3002\u8BE5\u64CD\u4F5C\u4F1A\u5199\u5165\u672C\u673A\u78C1\u76D8\u5E76\u53EF\u80FD\u89E6\u53D1 Windows \u5B89\u88C5/UAC\uFF0C\u8C03\u7528\u524D\u5FC5\u987B\u53D6\u5F97\u7528\u6237\u786E\u8BA4\uFF0C\u5E76\u4F20\u5165 confirm=true\u3002\u5B89\u88C5\u5305\u5FC5\u987B\u81EA\u5E26 zltaf\uFF1B\u672C\u5DE5\u5177\u4E0D\u4F1A\u901A\u8FC7 Assistant IPC\u3001ZLTHome \u6216 ZLTBackService \u8865\u88C5\u6A21\u5757\uFF0C\u4E5F\u4E0D\u4F1A\u542F\u52A8 Code.exe/jsservice\u3002",
23862
24215
  inputSchema: {
23863
24216
  type: "object",
23864
24217
  properties: {
@@ -23892,14 +24245,7 @@ async function startMcpServer() {
23892
24245
  ...resolveListedRuntimeTools(RUNTIME_TOOLS, toolProfile),
23893
24246
  ...resolveListedIpcTools(config2.tools, toolProfile)
23894
24247
  ];
23895
- const runtimeConfig = {
23896
- ...loadAssistantRuntimeConfig(resolveDefaultRuntimeConfigPath()),
23897
- zltafConfigFile: ZLTAF_CONFIG_FILE_DAY,
23898
- // Trae/WorkBuddy owns a standalone direct-ZLTAF lane. 智能管家 and its
23899
- // Assistant-managed services belong to the zlt-quant VSCode runtime.
23900
- manualServiceIndices: [],
23901
- startupCheckIndices: []
23902
- };
24248
+ const runtimeConfig = loadAssistantRuntimeConfig(resolveDefaultRuntimeConfigPath());
23903
24249
  let runtimeStartAttempted = false;
23904
24250
  let runtimeLease;
23905
24251
  let leaseStateUpdate = Promise.resolve();
@@ -24074,7 +24420,7 @@ async function startMcpServer() {
24074
24420
  );
24075
24421
  if (special) return special;
24076
24422
  if (tool.steps && tool.steps.length > 0) {
24077
- const result = await executeSteps(tool.steps, ipcClient, stripControlParams(toolArgs), timeout, withRuntimeRepairHint);
24423
+ const result = await executeSteps(tool.steps, ipcClient, stripControlParams(toolArgs), timeout, withRuntimeRepairHint, tool.name);
24078
24424
  return shapeToolResult(tool, toolArgs, result, session);
24079
24425
  }
24080
24426
  try {
@@ -24118,6 +24464,14 @@ async function startMcpServer() {
24118
24464
  if (runtimeLease) {
24119
24465
  const lease = runtimeLease;
24120
24466
  runtimeLease = void 0;
24467
+ try {
24468
+ const zltContent = await readZltContent(lease.workRoot, runtimeConfig.temporaryZltFile);
24469
+ if (zltContent) await unregisterRuntimeConsumerPid(zltContent, runtimeConfig);
24470
+ process.stderr.write("[zlt-mcp-proxy] runtime PID \u5DF2\u6CE8\u9500\n");
24471
+ } catch (error2) {
24472
+ process.stderr.write(`[zlt-mcp-proxy] runtime PID \u6CE8\u9500\u5931\u8D25\uFF08\u5FFD\u7565\uFF09: ${error2 instanceof Error ? error2.message : String(error2)}
24473
+ `);
24474
+ }
24121
24475
  try {
24122
24476
  const released = await releaseRuntimeLease(lease);
24123
24477
  process.stderr.write(`[zlt-mcp-proxy] runtime lease released: ${released.reason}
@@ -24473,7 +24827,7 @@ function getRuntimeRecommendedAction(runtimeReady, backtestReady) {
24473
24827
  return "call zlt-runtime-install with confirm=true to install the local backtest runtime";
24474
24828
  }
24475
24829
  if (!backtestReady) {
24476
- return "if zltaf is missing, call zlt-runtime-install with confirm=true. Python/Git toolchain and SDK packages come from the \u6295\u7814 module: ask the user to install or repair it in \u667A\u80FD\u7BA1\u5BB6. Then start ZLTAF direct mode.";
24830
+ return "if zltaf is missing, call zlt-runtime-install with confirm=true. Python/Git toolchain and SDK packages come from the \u6295\u7814 module: ask the user to install or repair it in \u667A\u80FD\u7BA1\u5BB6. Then start the managed runtime.";
24477
24831
  }
24478
24832
  return "call zlt-runtime-start before backtest if the ZLTAF pipe is not running";
24479
24833
  }
@@ -24485,10 +24839,10 @@ function getRuntimeStartUserAction(message) {
24485
24839
  return "\u667A\u91CF\u901A\u672C\u5730 ZLTAF runtime \u4E0D\u5B8C\u6574\uFF0C\u672A\u627E\u5230 C:\\ZLTClient\\zltaf\\zltaf.exe\u3002\u8BF7\u53D6\u5F97\u7528\u6237\u786E\u8BA4\u540E\u8C03\u7528 zlt-runtime-install\uFF0C\u4F7F\u7528\u5305\u542B zltaf \u7684\u5B89\u88C5\u5305\u4FEE\u590D\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\u3002";
24486
24840
  }
24487
24841
  if (/handshake/i.test(message)) {
24488
- return "\u667A\u91CF\u901A ZLTAF direct mode \u542F\u52A8\u63E1\u624B\u5931\u8D25\u3002\u8BF7\u628A ErrMsg \u548C doctor \u7ED3\u679C\u53CD\u9988\u7ED9\u5F00\u53D1\u8005\u6392\u67E5 ZLTAF \u7BA1\u9053\u534F\u8BAE\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\uFF0C\u4E5F\u4E0D\u8981\u542F\u52A8 ZLTHome\u3001ZLTBackService\u3001Code.exe \u6216 jsservice\u3002";
24842
+ return "\u667A\u91CF\u901A\u6258\u7BA1\u542F\u52A8\uFF08ZLTHome selfstart\uFF09\u63E1\u624B\u5931\u8D25\u3002\u8BF7\u628A ErrMsg \u548C doctor \u7ED3\u679C\u53CD\u9988\u7ED9\u5F00\u53D1\u8005\u6392\u67E5 ZLTAF \u7BA1\u9053\u534F\u8BAE\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\uFF0C\u4E5F\u4E0D\u8981\u542F\u52A8 ZLTHome\u3001ZLTBackService\u3001Code.exe \u6216 jsservice\u3002";
24489
24843
  }
24490
24844
  if (/pipe|管道|ZLTAF/i.test(message)) {
24491
- return "\u667A\u91CF\u901A ZLTAF \u7BA1\u9053\u4ECD\u4E0D\u53EF\u7528\u3002\u8BF7\u628A ErrMsg\u3001doctor \u7ED3\u679C\u548C zltaf \u65E5\u5FD7\u53CD\u9988\u7ED9\u5F00\u53D1\u8005\u6392\u67E5 ZLTAF direct mode\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\uFF0C\u4E5F\u4E0D\u8981\u542F\u52A8 ZLTHome\u3001ZLTBackService\u3001Code.exe \u6216 jsservice\u3002";
24845
+ return "\u667A\u91CF\u901A ZLTAF \u7BA1\u9053\u4ECD\u4E0D\u53EF\u7528\u3002\u8BF7\u628A ErrMsg\u3001doctor \u7ED3\u679C\u548C zltaf \u65E5\u5FD7\u53CD\u9988\u7ED9\u5F00\u53D1\u8005\u6392\u67E5\u6258\u7BA1\u542F\u52A8\u94FE\u8DEF\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\uFF0C\u4E5F\u4E0D\u8981\u542F\u52A8 ZLTHome\u3001ZLTBackService\u3001Code.exe \u6216 jsservice\u3002";
24492
24846
  }
24493
24847
  return "\u667A\u91CF\u901A ZLTAF runtime \u542F\u52A8\u5931\u8D25\u3002\u8BF7\u628A ErrMsg \u548C doctor \u7ED3\u679C\u53CD\u9988\u7ED9\u7528\u6237\u6216\u5F00\u53D1\u8005\u6392\u67E5\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\u3001\u6740\u8FDB\u7A0B\u3001\u542F\u52A8 ZLTHome/ZLTBackService/Code.exe/jsservice \u6216\u91CD\u590D\u5B89\u88C5 runtime\u3002";
24494
24848
  }
@@ -24502,7 +24856,7 @@ function getRuntimeInstallUserAction(message) {
24502
24856
  if (/handshake|pipe|管道|ZLTAF/i.test(message)) {
24503
24857
  return getRuntimeStartUserAction(message);
24504
24858
  }
24505
- return "\u667A\u91CF\u901A ZLTAF runtime \u5B89\u88C5/\u4FEE\u590D\u5931\u8D25\u3002\u8BF7\u628A ErrMsg \u548C doctor \u7ED3\u679C\u53CD\u9988\u7ED9\u5F00\u53D1\u8005\u6392\u67E5\u5B89\u88C5\u5668\u6216 ZLTAF direct mode\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\u3001\u6740\u8FDB\u7A0B\u3001\u542F\u52A8 ZLTHome/ZLTBackService/Code.exe/jsservice \u6216\u91CD\u590D\u5B89\u88C5 runtime\u3002";
24859
+ return "\u667A\u91CF\u901A ZLTAF runtime \u5B89\u88C5/\u4FEE\u590D\u5931\u8D25\u3002\u8BF7\u628A ErrMsg \u548C doctor \u7ED3\u679C\u53CD\u9988\u7ED9\u5F00\u53D1\u8005\u6392\u67E5\u5B89\u88C5\u5668\u6216\u6258\u7BA1\u542F\u52A8\u94FE\u8DEF\uFF1B\u4E0D\u8981\u624B\u52A8\u521B\u5EFA zltaf \u76EE\u5F55\u3001\u6740\u8FDB\u7A0B\u3001\u542F\u52A8 ZLTHome/ZLTBackService/Code.exe/jsservice \u6216\u91CD\u590D\u5B89\u88C5 runtime\u3002";
24506
24860
  }
24507
24861
  function runtimeRecoveryStopData(message, extra = {}, handler = "runtimeStart") {
24508
24862
  return {