@zltquant/zlt-mcp 0.6.0-beta.0 → 0.6.0-beta.2

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/README.md CHANGED
@@ -25,6 +25,12 @@ npx -y @zltquant/zlt-mcp call zlt-runtime-doctor --args-json '{}'
25
25
  stderr 保留诊断,失败返回非零退出码。工具执行后若写文件失败,完整结果会输出到 stdout;先核对结果,不要自动重复执行。
26
26
  不传该参数时保持原有完整 stdout。
27
27
 
28
+ 回测可按当前工具 schema 传基线路径与覆盖:新建用 `strategyPath`(策略根 localPath),
29
+ 重跑用 `codePath`(所选 runId 的运行目录)。MCP 先读配置并校验身份,再合并覆盖、补默认值,
30
+ 沿原有 IPC 链提交完整参数。策略根是工作区/调试配置,正式 RUN 不保证更新根目录;运行目录用于该次结果对账。
31
+ 无覆盖时省略 runParams,由 ZLTAF 读取库中参数;部分覆盖需合并完整基线。版本管理开启时已执行的正式 RUN 必须新建记录,不自动绕过拒绝。
32
+ `configurationEvidence` 标明基线来源/哈希与 `persistenceVerified=false`;提交回显不代表文件和数据库已回读一致。
33
+
28
34
  其他支持原生 MCP 的客户端(Trae / Claude Code / Cursor 等)可以使用下面的配置:
29
35
 
30
36
  ```json
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;已有配置基线时必须把本次 schema 支持的配置字段显式放入 runParams,再叠加用户明确要求;不要只传 strategyId 丢掉已有日期、资金、账户或费用。只有用户和配置都未提供周期时,才让 MCP 默认 end 为自然日 T-1、start 从 end 往前推半年;ZLTAF 按实际交易日执行,非交易日不产生行情或策略调用。run/create 失败直接报错,不要去复用策略里自动生成的 debug runId(建策略时已回显为 debugRunId,跑它等于跑创建那一刻的模板快照)。run/create 生成快照后,MCP 会在真正启动前拒绝 get_current_data().get(...) 这种当前对象不支持的访问。\n\n**返回 runId、codePath 和 attemptFence 后立即调用 mcp-wait(kind=backtest,id=runId)阻塞等待回测结束,不要用 mcp-get-backtest-state 轮询。CLI 创建命令启动的隐藏 guardian 会自行携带 attemptFence;任何后续显式 CLI call 或 MCP 重启后的调用,都必须把 codePath 和 attemptFence 一起传回 mcp-wait,避免把上一轮残留的 result.json 当成本轮错误;同一 MCP 会话内可省略。**\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。",
510
+ "description": "创建并启动回测。strategyId 必须来自 mcp-create-strategy 返回的策略ID;runType 固定为 2;推荐传 strategyPath(策略 localPath)和 runParams 中本次要改的字段;MCP 读取策略根目录当前配置,合并覆盖后形成完整提交参数,再走原有 run/create → run/run 同步链,不由 Agent 直接改文件或写库。策略根目录是工作区/调试配置,正式 RUN 模式不会保证更新它;只有 DEBUG 路径显式双写根目录和运行目录。不要称根目录为最近正式回测参数,运行身份以返回 runId 为准。没有 strategyPath 时保持旧式完整参数调用;仅真正缺失的字段补默认值。run/create 失败直接报错,不要去复用策略里自动生成的 debug runId(建策略时已回显为 debugRunId,跑它等于跑创建那一刻的模板快照)。run/create 生成快照后,MCP 会在真正启动前拒绝 get_current_data().get(...) 这种当前对象不支持的访问。\n\n**返回 runId、codePath 和 attemptFence 后立即调用 mcp-wait(kind=backtest,id=runId)阻塞等待回测结束,不要用 mcp-get-backtest-state 轮询。CLI 创建命令启动的隐藏 guardian 会自行携带 attemptFence;任何后续显式 CLI call 或 MCP 重启后的调用,都必须把 codePath 和 attemptFence 一起传回 mcp-wait,避免把上一轮残留的 result.json 当成本轮错误;同一 MCP 会话内可省略。**\n\n日线类频率只可见到 context.previous_date:要用交易日 T 的收盘,end 必须设成 T 的下一个交易日;end 落在未来不报错但结果为空。展开见返回的 dailyVisibilityNote / endWarning。\n\n返回 Data 的 resolvedStart/resolvedEnd/resolvedCapital、runParams 和 evidence.effectiveRunParams 是 MCP 补全后的提交参数,不是文件/数据库回读确认。configurationEvidence 标明提交证据及基线来源/哈希,persistenceVerified=false;运行目录 tradesetting.json 和库中对应运行记录是落盘对账依据。evidence 同时给出 backtestId、runDir 和代码快照 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": {
@@ -529,7 +529,7 @@
529
529
  },
530
530
  "runParams": {
531
531
  "type": "object",
532
- "description": "回测参数。有 tradesetting.json 配置基线时显式传入 schema 支持的已有字段(日期、资金、账户、频率、市场、预加载、参数/费率模板),用户本次要求覆盖对应字段;账户现金与初始资金保持一致。仅无用户要求且无配置基线的字段采用 MCP 默认值。",
532
+ "description": "本次参数覆盖。提供 strategyPath 时先读策略当前配置,再递归合并对象覆盖(数组整体替换),最后补系统默认值;不需要复述整套费率。只改 initial_capital 时同步单账户现金;多账户须明确分配。未提供 strategyPath 时必须自行携带已有配置基线,省略值会使用系统默认值。",
533
533
  "properties": {
534
534
  "account_list": {
535
535
  "type": "array",
@@ -651,6 +651,10 @@
651
651
  }
652
652
  },
653
653
  "additionalProperties": false
654
+ },
655
+ "strategyPath": {
656
+ "type": "string",
657
+ "description": "策略根目录绝对路径,使用策略创建/详情返回的 localPath;不是 backtest 下的运行副本。提供时 MCP 读取该目录 .vscode/tradesetting.json 作为当前配置基线,runParams 只传本次覆盖;路径或 strategy_id 不匹配时在执行前拒绝。不提供时保持完整参数/系统默认值的兼容调用,不自动定位磁盘配置。"
654
658
  }
655
659
  },
656
660
  "required": [
@@ -742,7 +746,7 @@
742
746
  },
743
747
  {
744
748
  "name": "mcp-re-backtest",
745
- "description": "重新运行会话中最新的回测:用同一个 runId 重跑**该次回测已生成的代码快照**(策略工程 backtest/ 目录下那份副本)。\n\n**它不会把你在策略源工程里刚改过的 main.py 拷进去**——刚编辑过源代码就必须改用 mcp-create-and-run-backtest 生成新的代码快照,否则会重复运行修改前的旧代码,表现为「源码已修复却仍报同样的错误」。所以本工具只适合调整 runParams(回测区间、资金等)后重跑。\n\nstrategyId、strategyName、runId、runType、runParams 从当前会话最近一次 mcp-create-and-run-backtest 的成功结果里取得并传入本工具,不需要用户手动输入;会话中没有运行结果就提示无回测,无法重新回测。runParams 默认沿用最近一次创建回测的条件,允许用户修改部分回测参数;MCP 重启后这份上下文会丢失,届时必须传完整 runParams。返回的 resolved* 是本次实际生效的区间、资金与基准,向用户报告时以它为准。\n\n重跑启动后同样立即调用 mcp-wait(kind=backtest,id=runId)阻塞等待结束,不要轮询状态;隐藏 guardian 会自行携带 attemptFence;任何后续显式 CLI call 或 MCP 重启后的调用,还要复用最初创建结果的 codePath,并传回本次重跑响应里的 attemptFence。",
749
+ "description": "运行指定 runId 的代码副本,不会把你在策略源工程里刚改过的 main.py 拷进去。ZLTAF 启用回测版本管理时,正式 RUN 记录一旦执行过就不能同 runId 重跑,会返回“请先创建新的回测记录”;这时报告限制,不自动重试、改 DEBUG 或绕过版本管理。需要新一轮实验时按用户授权使用 mcp-create-and-run-backtest。\n\n不改变参数时省略 runParams 或传空对象,MCP 不补旧缓存参数,由 ZLTAF 读取数据库 run_params。改变部分参数时传所选 runId 的 codePath,MCP 校验运行配置并递归合并覆盖,形成完整参数;无路径时只兼容有缓存基线或完整参数的调用。ZLTAF 对非空参数整体采用,不自行合并。返回 configurationEvidence 区分 mcp_submission 与 zltaf_stored_parameters;两者均不证明文件/数据库已回读一致。后者不回显猜测的 effectiveRunParams。\n\n成功启动后按 runId、codePath、attemptFence 调用 mcp-wait,再由收尾读取该次运行文件和终态。根目录不作为历史配置,策略实际基准也不由请求值证明。",
746
750
  "inputSchema": {
747
751
  "type": "object",
748
752
  "properties": {
@@ -760,7 +764,7 @@
760
764
  },
761
765
  "codePath": {
762
766
  "type": "string",
763
- "description": "可选的回测运行目录。CLI 独立命令或 MCP 重启后必须传最初创建回测返回的绝对 codePath;仅用于 result.json 新旧轮次对账,不发送给 IPC。"
767
+ "description": "所选 runId 的运行目录绝对路径,用于身份校验、部分覆盖的配置基线和后续等待定位,不发送给 IPC。无参数覆盖时不把文件内容作为 runParams 重发;数据库配置由 ZLTAF 读取。"
764
768
  },
765
769
  "runType": {
766
770
  "type": "integer",
@@ -772,7 +776,7 @@
772
776
  },
773
777
  "runParams": {
774
778
  "type": "object",
775
- "description": "回测参数,与 mcp-create-and-run-backtest 的 runParams 同构,字段说明见该工具。默认沿用最近一次创建回测的条件,用户只需要说明想修改的字段,没传的字段(含基准、撮合模式、滑点、费率模板)都按上一次原样继承;MCP 重启会丢掉这份上下文,之后必须传完整参数,否则会以「缺少必填字段」失败。改用期货市场(marketIds 含 zj/zs/ds/sq/gq)时,account_list 的 account_type 要一并改为 2(期货)。",
779
+ "description": "省略或空对象表示使用 ZLTAF 数据库存储参数,不依赖 MCP 缓存。非空对象表示本次覆盖:提供 codePath 时读取所选运行配置并递归合并;数组整体替换,多账户资金须明确分配。无 codePath 时需要缓存基线或完整参数,禁止把部分参数直接发送给会整体替换的后端。",
776
780
  "properties": {
777
781
  "marketIds": {
778
782
  "type": "array",
@@ -795,10 +799,7 @@
795
799
  },
796
800
  "required": [
797
801
  "strategyId",
798
- "strategyName",
799
- "runId",
800
- "runType",
801
- "runParams"
802
+ "runId"
802
803
  ],
803
804
  "additionalProperties": false
804
805
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zltquant/zlt-mcp",
3
- "version": "0.6.0-beta.0",
3
+ "version": "0.6.0-beta.2",
4
4
  "description": "智量通 ZLT Quant MCP server(Windows x64;量化策略创建与本地回测)",
5
5
  "bin": {
6
6
  "zlt-mcp": "./zlt-mcp-proxy.bundle.cjs"
@@ -1147,8 +1147,8 @@ var require_command = __commonJS({
1147
1147
  "../../node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/command.js"(exports2) {
1148
1148
  var EventEmitter = require("node:events").EventEmitter;
1149
1149
  var childProcess = require("node:child_process");
1150
- var path15 = require("node:path");
1151
- var fs14 = require("node:fs");
1150
+ var path16 = require("node:path");
1151
+ var fs15 = require("node:fs");
1152
1152
  var process4 = require("node:process");
1153
1153
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1154
1154
  var { CommanderError: CommanderError2 } = require_error();
@@ -2129,7 +2129,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2129
2129
  * @param {string} subcommandName
2130
2130
  */
2131
2131
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2132
- if (fs14.existsSync(executableFile)) return;
2132
+ if (fs15.existsSync(executableFile)) return;
2133
2133
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2134
2134
  const executableMissing = `'${executableFile}' does not exist
2135
2135
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2147,11 +2147,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2147
2147
  let launchWithNode = false;
2148
2148
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2149
2149
  function findFile(baseDir, baseName) {
2150
- const localBin = path15.resolve(baseDir, baseName);
2151
- if (fs14.existsSync(localBin)) return localBin;
2152
- if (sourceExt.includes(path15.extname(baseName))) return void 0;
2150
+ const localBin = path16.resolve(baseDir, baseName);
2151
+ if (fs15.existsSync(localBin)) return localBin;
2152
+ if (sourceExt.includes(path16.extname(baseName))) return void 0;
2153
2153
  const foundExt = sourceExt.find(
2154
- (ext) => fs14.existsSync(`${localBin}${ext}`)
2154
+ (ext) => fs15.existsSync(`${localBin}${ext}`)
2155
2155
  );
2156
2156
  if (foundExt) return `${localBin}${foundExt}`;
2157
2157
  return void 0;
@@ -2163,21 +2163,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2163
2163
  if (this._scriptPath) {
2164
2164
  let resolvedScriptPath;
2165
2165
  try {
2166
- resolvedScriptPath = fs14.realpathSync(this._scriptPath);
2166
+ resolvedScriptPath = fs15.realpathSync(this._scriptPath);
2167
2167
  } catch {
2168
2168
  resolvedScriptPath = this._scriptPath;
2169
2169
  }
2170
- executableDir = path15.resolve(
2171
- path15.dirname(resolvedScriptPath),
2170
+ executableDir = path16.resolve(
2171
+ path16.dirname(resolvedScriptPath),
2172
2172
  executableDir
2173
2173
  );
2174
2174
  }
2175
2175
  if (executableDir) {
2176
2176
  let localFile = findFile(executableDir, executableFile);
2177
2177
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2178
- const legacyName = path15.basename(
2178
+ const legacyName = path16.basename(
2179
2179
  this._scriptPath,
2180
- path15.extname(this._scriptPath)
2180
+ path16.extname(this._scriptPath)
2181
2181
  );
2182
2182
  if (legacyName !== this._name) {
2183
2183
  localFile = findFile(
@@ -2188,7 +2188,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2188
2188
  }
2189
2189
  executableFile = localFile || executableFile;
2190
2190
  }
2191
- launchWithNode = sourceExt.includes(path15.extname(executableFile));
2191
+ launchWithNode = sourceExt.includes(path16.extname(executableFile));
2192
2192
  let proc;
2193
2193
  if (process4.platform !== "win32") {
2194
2194
  if (launchWithNode) {
@@ -3035,7 +3035,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3035
3035
  * @return {Command}
3036
3036
  */
3037
3037
  nameFromFilename(filename) {
3038
- this._name = path15.basename(filename, path15.extname(filename));
3038
+ this._name = path16.basename(filename, path16.extname(filename));
3039
3039
  return this;
3040
3040
  }
3041
3041
  /**
@@ -3049,9 +3049,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3049
3049
  * @param {string} [path]
3050
3050
  * @return {(string|null|Command)}
3051
3051
  */
3052
- executableDir(path16) {
3053
- if (path16 === void 0) return this._executableDir;
3054
- this._executableDir = path16;
3052
+ executableDir(path17) {
3053
+ if (path17 === void 0) return this._executableDir;
3054
+ this._executableDir = path17;
3055
3055
  return this;
3056
3056
  }
3057
3057
  /**
@@ -6516,8 +6516,8 @@ var require_utils = __commonJS({
6516
6516
  }
6517
6517
  return ind;
6518
6518
  }
6519
- function removeDotSegments(path15) {
6520
- let input = path15;
6519
+ function removeDotSegments(path16) {
6520
+ let input = path16;
6521
6521
  const output = [];
6522
6522
  let nextSlash = -1;
6523
6523
  let len = 0;
@@ -6716,8 +6716,8 @@ var require_schemes = __commonJS({
6716
6716
  wsComponent.secure = void 0;
6717
6717
  }
6718
6718
  if (wsComponent.resourceName) {
6719
- const [path15, query] = wsComponent.resourceName.split("?");
6720
- wsComponent.path = path15 && path15 !== "/" ? path15 : void 0;
6719
+ const [path16, query] = wsComponent.resourceName.split("?");
6720
+ wsComponent.path = path16 && path16 !== "/" ? path16 : void 0;
6721
6721
  wsComponent.query = query;
6722
6722
  wsComponent.resourceName = void 0;
6723
6723
  }
@@ -10079,12 +10079,12 @@ var require_dist = __commonJS({
10079
10079
  throw new Error(`Unknown format "${name}"`);
10080
10080
  return f;
10081
10081
  };
10082
- function addFormats(ajv, list, fs14, exportName) {
10082
+ function addFormats(ajv, list, fs15, exportName) {
10083
10083
  var _a;
10084
10084
  var _b;
10085
10085
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
10086
10086
  for (const f of list)
10087
- ajv.addFormat(f, fs14[f]);
10087
+ ajv.addFormat(f, fs15[f]);
10088
10088
  }
10089
10089
  module2.exports = exports2 = formatsPlugin;
10090
10090
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -10097,8 +10097,8 @@ var require_windows = __commonJS({
10097
10097
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js"(exports2, module2) {
10098
10098
  module2.exports = isexe;
10099
10099
  isexe.sync = sync;
10100
- var fs14 = require("fs");
10101
- function checkPathExt(path15, options) {
10100
+ var fs15 = require("fs");
10101
+ function checkPathExt(path16, options) {
10102
10102
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
10103
10103
  if (!pathext) {
10104
10104
  return true;
@@ -10109,25 +10109,25 @@ var require_windows = __commonJS({
10109
10109
  }
10110
10110
  for (var i = 0; i < pathext.length; i++) {
10111
10111
  var p = pathext[i].toLowerCase();
10112
- if (p && path15.substr(-p.length).toLowerCase() === p) {
10112
+ if (p && path16.substr(-p.length).toLowerCase() === p) {
10113
10113
  return true;
10114
10114
  }
10115
10115
  }
10116
10116
  return false;
10117
10117
  }
10118
- function checkStat(stat, path15, options) {
10118
+ function checkStat(stat, path16, options) {
10119
10119
  if (!stat.isSymbolicLink() && !stat.isFile()) {
10120
10120
  return false;
10121
10121
  }
10122
- return checkPathExt(path15, options);
10122
+ return checkPathExt(path16, options);
10123
10123
  }
10124
- function isexe(path15, options, cb) {
10125
- fs14.stat(path15, function(er, stat) {
10126
- cb(er, er ? false : checkStat(stat, path15, options));
10124
+ function isexe(path16, options, cb) {
10125
+ fs15.stat(path16, function(er, stat) {
10126
+ cb(er, er ? false : checkStat(stat, path16, options));
10127
10127
  });
10128
10128
  }
10129
- function sync(path15, options) {
10130
- return checkStat(fs14.statSync(path15), path15, options);
10129
+ function sync(path16, options) {
10130
+ return checkStat(fs15.statSync(path16), path16, options);
10131
10131
  }
10132
10132
  }
10133
10133
  });
@@ -10137,14 +10137,14 @@ var require_mode = __commonJS({
10137
10137
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js"(exports2, module2) {
10138
10138
  module2.exports = isexe;
10139
10139
  isexe.sync = sync;
10140
- var fs14 = require("fs");
10141
- function isexe(path15, options, cb) {
10142
- fs14.stat(path15, function(er, stat) {
10140
+ var fs15 = require("fs");
10141
+ function isexe(path16, options, cb) {
10142
+ fs15.stat(path16, function(er, stat) {
10143
10143
  cb(er, er ? false : checkStat(stat, options));
10144
10144
  });
10145
10145
  }
10146
- function sync(path15, options) {
10147
- return checkStat(fs14.statSync(path15), options);
10146
+ function sync(path16, options) {
10147
+ return checkStat(fs15.statSync(path16), options);
10148
10148
  }
10149
10149
  function checkStat(stat, options) {
10150
10150
  return stat.isFile() && checkMode(stat, options);
@@ -10168,7 +10168,7 @@ var require_mode = __commonJS({
10168
10168
  // ../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js
10169
10169
  var require_isexe = __commonJS({
10170
10170
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js"(exports2, module2) {
10171
- var fs14 = require("fs");
10171
+ var fs15 = require("fs");
10172
10172
  var core;
10173
10173
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
10174
10174
  core = require_windows();
@@ -10177,7 +10177,7 @@ var require_isexe = __commonJS({
10177
10177
  }
10178
10178
  module2.exports = isexe;
10179
10179
  isexe.sync = sync;
10180
- function isexe(path15, options, cb) {
10180
+ function isexe(path16, options, cb) {
10181
10181
  if (typeof options === "function") {
10182
10182
  cb = options;
10183
10183
  options = {};
@@ -10187,7 +10187,7 @@ var require_isexe = __commonJS({
10187
10187
  throw new TypeError("callback not provided");
10188
10188
  }
10189
10189
  return new Promise(function(resolve8, reject) {
10190
- isexe(path15, options || {}, function(er, is) {
10190
+ isexe(path16, options || {}, function(er, is) {
10191
10191
  if (er) {
10192
10192
  reject(er);
10193
10193
  } else {
@@ -10196,7 +10196,7 @@ var require_isexe = __commonJS({
10196
10196
  });
10197
10197
  });
10198
10198
  }
10199
- core(path15, options || {}, function(er, is) {
10199
+ core(path16, options || {}, function(er, is) {
10200
10200
  if (er) {
10201
10201
  if (er.code === "EACCES" || options && options.ignoreErrors) {
10202
10202
  er = null;
@@ -10206,9 +10206,9 @@ var require_isexe = __commonJS({
10206
10206
  cb(er, is);
10207
10207
  });
10208
10208
  }
10209
- function sync(path15, options) {
10209
+ function sync(path16, options) {
10210
10210
  try {
10211
- return core.sync(path15, options || {});
10211
+ return core.sync(path16, options || {});
10212
10212
  } catch (er) {
10213
10213
  if (options && options.ignoreErrors || er.code === "EACCES") {
10214
10214
  return false;
@@ -10224,7 +10224,7 @@ var require_isexe = __commonJS({
10224
10224
  var require_which = __commonJS({
10225
10225
  "../../node_modules/.pnpm/which@2.0.2/node_modules/which/which.js"(exports2, module2) {
10226
10226
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
10227
- var path15 = require("path");
10227
+ var path16 = require("path");
10228
10228
  var COLON = isWindows ? ";" : ":";
10229
10229
  var isexe = require_isexe();
10230
10230
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -10262,7 +10262,7 @@ var require_which = __commonJS({
10262
10262
  return opt.all && found.length ? resolve8(found) : reject(getNotFoundError(cmd));
10263
10263
  const ppRaw = pathEnv[i];
10264
10264
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
10265
- const pCmd = path15.join(pathPart, cmd);
10265
+ const pCmd = path16.join(pathPart, cmd);
10266
10266
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
10267
10267
  resolve8(subStep(p, i, 0));
10268
10268
  });
@@ -10289,7 +10289,7 @@ var require_which = __commonJS({
10289
10289
  for (let i = 0; i < pathEnv.length; i++) {
10290
10290
  const ppRaw = pathEnv[i];
10291
10291
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
10292
- const pCmd = path15.join(pathPart, cmd);
10292
+ const pCmd = path16.join(pathPart, cmd);
10293
10293
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
10294
10294
  for (let j = 0; j < pathExt.length; j++) {
10295
10295
  const cur = p + pathExt[j];
@@ -10337,7 +10337,7 @@ var require_path_key = __commonJS({
10337
10337
  var require_resolveCommand = __commonJS({
10338
10338
  "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports2, module2) {
10339
10339
  "use strict";
10340
- var path15 = require("path");
10340
+ var path16 = require("path");
10341
10341
  var which = require_which();
10342
10342
  var getPathKey = require_path_key();
10343
10343
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -10355,7 +10355,7 @@ var require_resolveCommand = __commonJS({
10355
10355
  try {
10356
10356
  resolved = which.sync(parsed.command, {
10357
10357
  path: env[getPathKey({ env })],
10358
- pathExt: withoutPathExt ? path15.delimiter : void 0
10358
+ pathExt: withoutPathExt ? path16.delimiter : void 0
10359
10359
  });
10360
10360
  } catch (e) {
10361
10361
  } finally {
@@ -10364,7 +10364,7 @@ var require_resolveCommand = __commonJS({
10364
10364
  }
10365
10365
  }
10366
10366
  if (resolved) {
10367
- resolved = path15.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
10367
+ resolved = path16.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
10368
10368
  }
10369
10369
  return resolved;
10370
10370
  }
@@ -10418,8 +10418,8 @@ var require_shebang_command = __commonJS({
10418
10418
  if (!match) {
10419
10419
  return null;
10420
10420
  }
10421
- const [path15, argument] = match[0].replace(/#! ?/, "").split(" ");
10422
- const binary = path15.split("/").pop();
10421
+ const [path16, argument] = match[0].replace(/#! ?/, "").split(" ");
10422
+ const binary = path16.split("/").pop();
10423
10423
  if (binary === "env") {
10424
10424
  return argument;
10425
10425
  }
@@ -10432,16 +10432,16 @@ var require_shebang_command = __commonJS({
10432
10432
  var require_readShebang = __commonJS({
10433
10433
  "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js"(exports2, module2) {
10434
10434
  "use strict";
10435
- var fs14 = require("fs");
10435
+ var fs15 = require("fs");
10436
10436
  var shebangCommand = require_shebang_command();
10437
10437
  function readShebang(command) {
10438
10438
  const size = 150;
10439
10439
  const buffer = Buffer.alloc(size);
10440
10440
  let fd;
10441
10441
  try {
10442
- fd = fs14.openSync(command, "r");
10443
- fs14.readSync(fd, buffer, 0, size, 0);
10444
- fs14.closeSync(fd);
10442
+ fd = fs15.openSync(command, "r");
10443
+ fs15.readSync(fd, buffer, 0, size, 0);
10444
+ fs15.closeSync(fd);
10445
10445
  } catch (e) {
10446
10446
  }
10447
10447
  return shebangCommand(buffer.toString());
@@ -10454,7 +10454,7 @@ var require_readShebang = __commonJS({
10454
10454
  var require_parse = __commonJS({
10455
10455
  "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js"(exports2, module2) {
10456
10456
  "use strict";
10457
- var path15 = require("path");
10457
+ var path16 = require("path");
10458
10458
  var resolveCommand = require_resolveCommand();
10459
10459
  var escape2 = require_escape();
10460
10460
  var readShebang = require_readShebang();
@@ -10479,7 +10479,7 @@ var require_parse = __commonJS({
10479
10479
  const needsShell = !isExecutableRegExp.test(commandFile);
10480
10480
  if (parsed.options.forceShell || needsShell) {
10481
10481
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
10482
- parsed.command = path15.normalize(parsed.command);
10482
+ parsed.command = path16.normalize(parsed.command);
10483
10483
  parsed.command = escape2.command(parsed.command);
10484
10484
  parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars));
10485
10485
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -10607,8 +10607,8 @@ var {
10607
10607
  } = import_index.default;
10608
10608
 
10609
10609
  // src/index.ts
10610
- var fs13 = __toESM(require("node:fs"), 1);
10611
- var path14 = __toESM(require("node:path"), 1);
10610
+ var fs14 = __toESM(require("node:fs"), 1);
10611
+ var path15 = __toESM(require("node:path"), 1);
10612
10612
 
10613
10613
  // src/cli.ts
10614
10614
  var fs9 = __toESM(require("fs"), 1);
@@ -10812,10 +10812,10 @@ function assignProp(target, prop, value) {
10812
10812
  configurable: true
10813
10813
  });
10814
10814
  }
10815
- function getElementAtPath(obj, path15) {
10816
- if (!path15)
10815
+ function getElementAtPath(obj, path16) {
10816
+ if (!path16)
10817
10817
  return obj;
10818
- return path15.reduce((acc, key) => acc?.[key], obj);
10818
+ return path16.reduce((acc, key) => acc?.[key], obj);
10819
10819
  }
10820
10820
  function promiseAllObject(promisesObj) {
10821
10821
  const keys = Object.keys(promisesObj);
@@ -11135,11 +11135,11 @@ function aborted(x, startIndex = 0) {
11135
11135
  }
11136
11136
  return false;
11137
11137
  }
11138
- function prefixIssues(path15, issues) {
11138
+ function prefixIssues(path16, issues) {
11139
11139
  return issues.map((iss) => {
11140
11140
  var _a;
11141
11141
  (_a = iss).path ?? (_a.path = []);
11142
- iss.path.unshift(path15);
11142
+ iss.path.unshift(path16);
11143
11143
  return iss;
11144
11144
  });
11145
11145
  }
@@ -19166,18 +19166,18 @@ async function waitForPipe(workRoot, pipePrefix, timeout) {
19166
19166
  throw new Error(`\u7B49\u5F85 ${pipePrefix} \u7BA1\u9053\u8D85\u65F6`);
19167
19167
  }
19168
19168
  async function readZltContent(workRoot, temporaryZltFile) {
19169
- const fs14 = await import("fs");
19170
- const path15 = await import("path");
19171
- const zltFile = path15.join(workRoot, temporaryZltFile || "Temporary/zlt");
19172
- return fs14.existsSync(zltFile) ? fs14.readFileSync(zltFile, "utf-8").trim() : "";
19169
+ const fs15 = await import("fs");
19170
+ const path16 = await import("path");
19171
+ const zltFile = path16.join(workRoot, temporaryZltFile || "Temporary/zlt");
19172
+ return fs15.existsSync(zltFile) ? fs15.readFileSync(zltFile, "utf-8").trim() : "";
19173
19173
  }
19174
19174
  async function pipeExists2(pipeName) {
19175
- const fs14 = await import("fs");
19175
+ const fs15 = await import("fs");
19176
19176
  try {
19177
19177
  if (process.platform === "win32") {
19178
- return fs14.readdirSync("\\\\.\\pipe\\").includes(pipeName);
19178
+ return fs15.readdirSync("\\\\.\\pipe\\").includes(pipeName);
19179
19179
  }
19180
- return fs14.existsSync(`/tmp/${pipeName}`);
19180
+ return fs15.existsSync(`/tmp/${pipeName}`);
19181
19181
  } catch {
19182
19182
  return false;
19183
19183
  }
@@ -19387,8 +19387,8 @@ async function updateRuntimeLeaseActiveJob(handle, runId, active, options = {})
19387
19387
  async function releaseRuntimeLease(handle, options = {}) {
19388
19388
  const deps = options.deps ?? defaultRuntimeLifecycleDeps;
19389
19389
  return withLifecycleLock(handle.workRoot, deps, async (stateDir) => {
19390
- const record2 = readJson(leasePath(stateDir, handle.token));
19391
- if (record2 && sameIdentity(record2.holder, handle.holder)) {
19390
+ const record3 = readJson(leasePath(stateDir, handle.token));
19391
+ if (record3 && sameIdentity(record3.holder, handle.holder)) {
19392
19392
  fs3.rmSync(leasePath(stateDir, handle.token), { force: true });
19393
19393
  }
19394
19394
  const recordedOwner = readJson(ownerPath(stateDir));
@@ -19591,13 +19591,13 @@ function readAndPruneStore(stateDir, deps) {
19591
19591
  if (!entry.isFile() || !entry.name.endsWith(".json"))
19592
19592
  continue;
19593
19593
  const filePath = path3.join(leasesDir, entry.name);
19594
- const record2 = readJson(filePath);
19595
- const current = record2 ? deps.inspectProcess(record2.holder.pid) : null;
19596
- if (!record2 || !current || !sameIdentity(current, record2.holder)) {
19594
+ const record3 = readJson(filePath);
19595
+ const current = record3 ? deps.inspectProcess(record3.holder.pid) : null;
19596
+ if (!record3 || !current || !sameIdentity(current, record3.holder)) {
19597
19597
  fs3.rmSync(filePath, { force: true });
19598
19598
  continue;
19599
19599
  }
19600
- leases.push(record2);
19600
+ leases.push(record3);
19601
19601
  }
19602
19602
  let owner = normalizeRuntimeOwnerRecord(readJson(ownerPath(stateDir)));
19603
19603
  if (owner) {
@@ -19609,21 +19609,21 @@ function readAndPruneStore(stateDir, deps) {
19609
19609
  }
19610
19610
  return { owner, leases };
19611
19611
  }
19612
- function normalizeRuntimeOwnerRecord(record2) {
19613
- if (!record2)
19612
+ function normalizeRuntimeOwnerRecord(record3) {
19613
+ if (!record3)
19614
19614
  return null;
19615
- const activeJobIds = new Set(normalizeRunIds(record2.activeJobIds));
19616
- const legacyBoundJobIds = normalizeRunIds(record2.legacyBoundJobIds);
19617
- const legacyResolvedJobIds = new Set(normalizeRunIds(record2.legacyResolvedJobIds));
19615
+ const activeJobIds = new Set(normalizeRunIds(record3.activeJobIds));
19616
+ const legacyBoundJobIds = normalizeRunIds(record3.legacyBoundJobIds);
19617
+ const legacyResolvedJobIds = new Set(normalizeRunIds(record3.legacyResolvedJobIds));
19618
19618
  for (const runId of legacyBoundJobIds) {
19619
19619
  activeJobIds.add(runId);
19620
19620
  legacyResolvedJobIds.delete(runId);
19621
19621
  }
19622
19622
  return {
19623
- process: record2.process,
19624
- startedAt: record2.startedAt,
19623
+ process: record3.process,
19624
+ startedAt: record3.startedAt,
19625
19625
  activeJobIds: [...activeJobIds].sort(),
19626
- legacyActiveJobs: record2.legacyActiveJobs === void 0 ? normalizeLegacyActiveJobs(record2.activeJobs) : normalizeLegacyActiveJobs(record2.legacyActiveJobs),
19626
+ legacyActiveJobs: record3.legacyActiveJobs === void 0 ? normalizeLegacyActiveJobs(record3.activeJobs) : normalizeLegacyActiveJobs(record3.legacyActiveJobs),
19627
19627
  legacyBoundJobIds,
19628
19628
  legacyResolvedJobIds: [...legacyResolvedJobIds].sort()
19629
19629
  };
@@ -21715,14 +21715,14 @@ function appendHistory(outputDir, uri, statusCode, contentSize, respFile) {
21715
21715
  fs9.writeFileSync(historyPath, kept.join("\n") + "\n", "utf-8");
21716
21716
  }
21717
21717
  }
21718
- const record2 = {
21718
+ const record3 = {
21719
21719
  time: getTimestampISO(),
21720
21720
  uri,
21721
21721
  status: statusCode,
21722
21722
  size: contentSize
21723
21723
  };
21724
- if (respFile) record2.file = respFile;
21725
- fs9.appendFileSync(historyPath, JSON.stringify(record2) + "\n", "utf-8");
21724
+ if (respFile) record3.file = respFile;
21725
+ fs9.appendFileSync(historyPath, JSON.stringify(record3) + "\n", "utf-8");
21726
21726
  }
21727
21727
  function cleanOutput(clientRootDir, maxDays = 1) {
21728
21728
  const outputDir = getOutputDir(clientRootDir);
@@ -21895,13 +21895,13 @@ function backtestHandoff(result) {
21895
21895
  const payload = parseMcpResult(result);
21896
21896
  const data = payload?.Data;
21897
21897
  if (!data || typeof data !== "object" || Array.isArray(data)) return null;
21898
- const record2 = data;
21899
- const status = Number(record2.status);
21898
+ const record3 = data;
21899
+ const status = Number(record3.status);
21900
21900
  if (Number.isFinite(status) && status !== 0 && status !== 1) return null;
21901
- const runId = typeof record2.runId === "string" ? record2.runId.trim() : "";
21901
+ const runId = typeof record3.runId === "string" ? record3.runId.trim() : "";
21902
21902
  if (!runId) return null;
21903
- const evidence = record2.evidence && typeof record2.evidence === "object" && !Array.isArray(record2.evidence) ? record2.evidence : void 0;
21904
- const rawCodePath = typeof record2.codePath === "string" ? record2.codePath.trim() : typeof evidence?.runDir === "string" ? evidence.runDir.trim() : "";
21903
+ const evidence = record3.evidence && typeof record3.evidence === "object" && !Array.isArray(record3.evidence) ? record3.evidence : void 0;
21904
+ const rawCodePath = typeof record3.codePath === "string" ? record3.codePath.trim() : typeof evidence?.runDir === "string" ? evidence.runDir.trim() : "";
21905
21905
  return {
21906
21906
  runId,
21907
21907
  ...rawCodePath && path9.isAbsolute(rawCodePath) ? { codePath: rawCodePath } : {}
@@ -21915,13 +21915,13 @@ function parseKeepaliveLockRecord(raw) {
21915
21915
  try {
21916
21916
  const parsed = JSON.parse(raw.toString("utf-8"));
21917
21917
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
21918
- const record2 = parsed;
21919
- const processIdentity = parseRuntimeProcessIdentity(record2.processIdentity);
21920
- return typeof record2.token === "string" && record2.token.length > 0 && Number.isSafeInteger(record2.pid) && Number(record2.pid) > 0 && processIdentity?.pid === Number(record2.pid) && Number.isSafeInteger(record2.heartbeatAt) && Number(record2.heartbeatAt) > 0 ? {
21921
- token: record2.token,
21922
- pid: Number(record2.pid),
21918
+ const record3 = parsed;
21919
+ const processIdentity = parseRuntimeProcessIdentity(record3.processIdentity);
21920
+ return typeof record3.token === "string" && record3.token.length > 0 && Number.isSafeInteger(record3.pid) && Number(record3.pid) > 0 && processIdentity?.pid === Number(record3.pid) && Number.isSafeInteger(record3.heartbeatAt) && Number(record3.heartbeatAt) > 0 ? {
21921
+ token: record3.token,
21922
+ pid: Number(record3.pid),
21923
21923
  processIdentity,
21924
- heartbeatAt: Number(record2.heartbeatAt)
21924
+ heartbeatAt: Number(record3.heartbeatAt)
21925
21925
  } : null;
21926
21926
  } catch {
21927
21927
  return null;
@@ -21929,11 +21929,11 @@ function parseKeepaliveLockRecord(raw) {
21929
21929
  }
21930
21930
  function parseRuntimeProcessIdentity(value) {
21931
21931
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
21932
- const record2 = value;
21933
- return Number.isSafeInteger(record2.pid) && Number(record2.pid) > 0 && typeof record2.executablePath === "string" && record2.executablePath.length > 0 && typeof record2.creationTime === "string" && record2.creationTime.length > 0 ? {
21934
- pid: Number(record2.pid),
21935
- executablePath: record2.executablePath,
21936
- creationTime: record2.creationTime
21932
+ const record3 = value;
21933
+ return Number.isSafeInteger(record3.pid) && Number(record3.pid) > 0 && typeof record3.executablePath === "string" && record3.executablePath.length > 0 && typeof record3.creationTime === "string" && record3.creationTime.length > 0 ? {
21934
+ pid: Number(record3.pid),
21935
+ executablePath: record3.executablePath,
21936
+ creationTime: record3.creationTime
21937
21937
  } : null;
21938
21938
  }
21939
21939
  function readKeepaliveLock(lockPath) {
@@ -21975,15 +21975,15 @@ function currentKeepaliveProcessIdentity() {
21975
21975
  if (!identity) throw new Error(`Unable to identify keepalive process pid=${process.pid}`);
21976
21976
  return identity;
21977
21977
  }
21978
- function keepaliveProcessIsCurrent(record2) {
21979
- if (!processIsAlive(record2.pid)) return false;
21980
- const current = inspectRuntimeProcess(record2.pid);
21981
- return current ? sameProcessIdentity(current, record2.processIdentity) : true;
21978
+ function keepaliveProcessIsCurrent(record3) {
21979
+ if (!processIsAlive(record3.pid)) return false;
21980
+ const current = inspectRuntimeProcess(record3.pid);
21981
+ return current ? sameProcessIdentity(current, record3.processIdentity) : true;
21982
21982
  }
21983
- function writeNewKeepaliveLock(lockPath, record2) {
21983
+ function writeNewKeepaliveLock(lockPath, record3) {
21984
21984
  const descriptor = fs9.openSync(lockPath, "wx", 384);
21985
21985
  try {
21986
- fs9.writeFileSync(descriptor, JSON.stringify(record2), "utf-8");
21986
+ fs9.writeFileSync(descriptor, JSON.stringify(record3), "utf-8");
21987
21987
  } finally {
21988
21988
  fs9.closeSync(descriptor);
21989
21989
  }
@@ -22054,8 +22054,8 @@ function releaseKeepaliveLock(ownership) {
22054
22054
  retireKeepaliveLockIfUnchanged(ownership.lockPath, current.identity);
22055
22055
  }
22056
22056
  }
22057
- function keepaliveLeaseIsFresh(record2) {
22058
- return keepaliveTimestampIsFresh(record2.heartbeatAt);
22057
+ function keepaliveLeaseIsFresh(record3) {
22058
+ return keepaliveTimestampIsFresh(record3.heartbeatAt);
22059
22059
  }
22060
22060
  function keepaliveTimestampIsFresh(timestamp) {
22061
22061
  const age = Date.now() - timestamp;
@@ -23123,9 +23123,9 @@ var StdioServerTransport = class {
23123
23123
  };
23124
23124
 
23125
23125
  // src/mcp-server.ts
23126
- var fs12 = __toESM(require("node:fs"), 1);
23126
+ var fs13 = __toESM(require("node:fs"), 1);
23127
23127
  var os5 = __toESM(require("node:os"), 1);
23128
- var path13 = __toESM(require("node:path"), 1);
23128
+ var path14 = __toESM(require("node:path"), 1);
23129
23129
 
23130
23130
  // src/tool-profile-contract.mjs
23131
23131
  var UNIVERSAL_AGENT_RUNTIME_TOOL_NAMES = Object.freeze([
@@ -23188,9 +23188,9 @@ var NPM_PACKAGE_CONFIG_TOOL_NAMES = Object.freeze([
23188
23188
  ]);
23189
23189
 
23190
23190
  // src/tool-pipeline.ts
23191
- var fs11 = __toESM(require("node:fs"), 1);
23192
- var path11 = __toESM(require("node:path"), 1);
23193
- var import_node_crypto2 = require("node:crypto");
23191
+ var fs12 = __toESM(require("node:fs"), 1);
23192
+ var path12 = __toESM(require("node:path"), 1);
23193
+ var import_node_crypto3 = require("node:crypto");
23194
23194
 
23195
23195
  // src/feature-set.ts
23196
23196
  function resolveFeatureSet() {
@@ -23199,6 +23199,119 @@ function resolveFeatureSet() {
23199
23199
  var MASKED_FACTOR_MESSAGE = "\u56E0\u5B50\u7814\u7A76\u529F\u80FD\u5728\u5F53\u524D\u7248\u672C\u672A\u5F00\u653E\u3002";
23200
23200
  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";
23201
23201
 
23202
+ // src/backtest-config.ts
23203
+ var fs11 = __toESM(require("node:fs"), 1);
23204
+ var path11 = __toESM(require("node:path"), 1);
23205
+ var import_node_crypto2 = require("node:crypto");
23206
+ var BACKTEST_CONFIG_KEYS = [
23207
+ "start",
23208
+ "end",
23209
+ "freq",
23210
+ "kcount",
23211
+ "marketIds",
23212
+ "trade_type",
23213
+ "initial_capital",
23214
+ "account_list",
23215
+ "base_tpl_obj",
23216
+ "ratio_tpl_obj"
23217
+ ];
23218
+ var INTERNAL_CONFIG_KEYS = ["btmode", "st_parmas", "mode", "class_type", "base_tpl_id", "ratio_tpl_id"];
23219
+ var BASE_KEYS = [
23220
+ "name",
23221
+ "zhCnName",
23222
+ "benchmark",
23223
+ "matching_mode",
23224
+ "slippage",
23225
+ "avoid_future",
23226
+ "open_cache",
23227
+ "match_with_order_book",
23228
+ "max_trade_ratio",
23229
+ "order_volume_ratio",
23230
+ "take_fee_type",
23231
+ "adjust_aqty_by_avail",
23232
+ "jv_kuan_open_avg_cost_switch"
23233
+ ];
23234
+ var FEE_KEYS = [
23235
+ "open_commission",
23236
+ "close_commission",
23237
+ "close_today_commission",
23238
+ "min_commission",
23239
+ "open_tax",
23240
+ "close_tax",
23241
+ "margin_ratio"
23242
+ ];
23243
+ var record2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
23244
+ var pick2 = (v, keys) => Object.fromEntries(keys.filter((k) => Object.prototype.hasOwnProperty.call(v, k)).map((k) => [k, v[k]]));
23245
+ function readBacktestBaseline(tool, args) {
23246
+ const create = tool === "mcp-create-and-run-backtest";
23247
+ if (!create && tool !== "mcp-re-backtest") return void 0;
23248
+ const directory = args[create ? "strategyPath" : "codePath"];
23249
+ if (directory === void 0) return void 0;
23250
+ if (typeof directory !== "string" || !path11.isAbsolute(directory)) throw new Error("Configuration baseline requires an absolute directory path");
23251
+ const root = fs11.realpathSync(directory);
23252
+ if (create && path11.basename(path11.dirname(root)).toLowerCase() === "backtest") {
23253
+ throw new Error("strategyPath must identify the strategy root, not a historical backtest directory");
23254
+ }
23255
+ const file = path11.join(root, ".vscode", "tradesetting.json");
23256
+ const raw = fs11.readFileSync(file);
23257
+ let config2;
23258
+ try {
23259
+ config2 = JSON.parse(raw.toString("utf8").replace(/^\uFEFF/, ""));
23260
+ } catch {
23261
+ throw new Error("Configuration baseline is not valid JSON");
23262
+ }
23263
+ if (!record2(config2) || config2.strategy_id !== args.strategyId) throw new Error("Configuration baseline strategy_id is missing or mismatched");
23264
+ if (!create && (typeof args.runId !== "string" || config2.backtest_id !== args.runId)) {
23265
+ throw new Error("Configuration baseline backtest_id does not match the requested runId");
23266
+ }
23267
+ const params = pick2(config2, [...BACKTEST_CONFIG_KEYS, ...INTERNAL_CONFIG_KEYS]);
23268
+ for (const key of ["base_tpl_obj", "ratio_tpl_obj"]) {
23269
+ if (params[key] !== void 0 && !record2(params[key])) throw new Error(`Configuration baseline ${key} must be an object`);
23270
+ }
23271
+ if (record2(params.base_tpl_obj)) params.base_tpl_obj = pick2(params.base_tpl_obj, BASE_KEYS);
23272
+ if (record2(params.ratio_tpl_obj)) {
23273
+ const fees = params.ratio_tpl_obj;
23274
+ for (const asset of ["stock", "future", "fund", "bond"]) {
23275
+ if (fees[asset] !== void 0 && !record2(fees[asset])) throw new Error(`Configuration baseline fee section ${asset} must be an object`);
23276
+ }
23277
+ params.ratio_tpl_obj = {
23278
+ ...pick2(fees, ["name", "zhCnName"]),
23279
+ ...Object.fromEntries(["stock", "future", "fund", "bond"].filter((k) => record2(fees[k])).map((k) => [k, pick2(fees[k], FEE_KEYS)]))
23280
+ };
23281
+ }
23282
+ if (Array.isArray(params.account_list)) {
23283
+ params.account_list = params.account_list.map((account) => {
23284
+ if (!record2(account)) throw new Error("Configuration baseline account_list contains a non-object");
23285
+ const result = pick2(account, ["pindex", "account_id", "cash", "account_type"]);
23286
+ if (create && typeof result.account_id === "string" && /^RN-[A-Za-z0-9-]+_\d+$/.test(result.account_id)) result.account_id = "";
23287
+ return result;
23288
+ });
23289
+ }
23290
+ return { params, evidence: {
23291
+ kind: create ? "strategy_current" : "selected_run",
23292
+ path: file,
23293
+ sha256: (0, import_node_crypto2.createHash)("sha256").update(raw).digest("hex")
23294
+ } };
23295
+ }
23296
+ function reconcileBaselineCapital(params, overrides) {
23297
+ const accounts = params.account_list;
23298
+ if (!Array.isArray(accounts) || !accounts.length || accounts.some((a) => !record2(a) || typeof a.cash !== "number" || !Number.isFinite(a.cash) || a.cash < 0)) {
23299
+ throw new Error("Configuration baseline requires accounts with finite nonnegative cash");
23300
+ }
23301
+ const explicitCapital = Object.prototype.hasOwnProperty.call(overrides, "initial_capital");
23302
+ const explicitAccounts = Object.prototype.hasOwnProperty.call(overrides, "account_list");
23303
+ if (explicitCapital && !explicitAccounts) {
23304
+ if (accounts.length !== 1) throw new Error("Changing capital for multiple accounts requires an explicit account_list allocation");
23305
+ if (typeof params.initial_capital !== "number" || !Number.isFinite(params.initial_capital) || params.initial_capital <= 0) throw new Error("initial_capital must be positive and finite");
23306
+ accounts[0] = { ...accounts[0], cash: params.initial_capital };
23307
+ }
23308
+ const total = accounts.reduce((sum, a) => sum + a.cash, 0);
23309
+ if (explicitAccounts && !explicitCapital || params.initial_capital === void 0) params.initial_capital = total;
23310
+ if (typeof params.initial_capital !== "number" || !Number.isFinite(params.initial_capital) || params.initial_capital <= 0 || Math.abs(params.initial_capital - total) > 5e-3) {
23311
+ throw new Error("initial_capital must match the sum of account cash; supply a consistent allocation");
23312
+ }
23313
+ }
23314
+
23202
23315
  // src/tool-pipeline.ts
23203
23316
  function finitePositiveNumber(value) {
23204
23317
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
@@ -23228,6 +23341,44 @@ function isFuturesRun(marketIds) {
23228
23341
  return marketIds.some((id) => FUTURES_MARKET_IDS.has(String(id).trim().toLowerCase()));
23229
23342
  }
23230
23343
  var identityRepairHint = (data) => data;
23344
+ function prepareToolCallArgs(tool, args, contexts) {
23345
+ const clean = { ...args };
23346
+ delete clean.__configBaseline;
23347
+ delete clean.__storedRunParams;
23348
+ try {
23349
+ if (tool.name === "mcp-re-backtest" && (clean.runParams === void 0 || isRecord2(clean.runParams) && Object.keys(clean.runParams).length === 0)) {
23350
+ readBacktestBaseline(tool.name, clean);
23351
+ const prior = typeof clean.runId === "string" ? contexts.get(clean.runId) : void 0;
23352
+ if (prior && prior.strategyId !== clean.strategyId) throw new Error("Cached run belongs to another strategy");
23353
+ delete clean.runParams;
23354
+ return prepareToolArgs(tool, applyToolArgDefaults(tool, { ...clean, __storedRunParams: true }), contexts);
23355
+ }
23356
+ const baseline = readBacktestBaseline(tool.name, clean);
23357
+ if (!baseline) {
23358
+ if (tool.name === "mcp-re-backtest") {
23359
+ const prior = typeof clean.runId === "string" ? contexts.get(clean.runId) : void 0;
23360
+ if (prior && prior.strategyId !== clean.strategyId) throw new Error("Cached run belongs to another strategy");
23361
+ const supplied = isRecord2(clean.runParams) ? clean.runParams : {};
23362
+ if (!prior?.runParams && BACKTEST_CONFIG_KEYS.some((key) => supplied[key] === void 0 || supplied[key] === null)) {
23363
+ throw new Error("Rerun has no persistent or cached baseline; supply codePath or complete runParams");
23364
+ }
23365
+ }
23366
+ return prepareToolArgs(tool, applyToolArgDefaults(tool, clean), contexts);
23367
+ }
23368
+ if (clean.runParams !== void 0 && !isRecord2(clean.runParams)) throw new Error("runParams overrides must be an object");
23369
+ const overrides = clean.runParams ?? {};
23370
+ const runParams = applyBodyDefaults(baseline.params, overrides);
23371
+ const merged = applyToolArgDefaults(tool, { ...clean, runParams, __configBaseline: baseline.evidence });
23372
+ reconcileBaselineCapital(merged.runParams, overrides);
23373
+ return prepareToolArgs(tool, merged, contexts);
23374
+ } catch (error2) {
23375
+ return jsonToolResult(10063, "\u56DE\u6D4B\u914D\u7F6E\u57FA\u7EBF\u4E0D\u53EF\u7528\u6216\u8986\u76D6\u53C2\u6570\u51B2\u7A81", {
23376
+ reason: error2 instanceof Error ? error2.message : String(error2),
23377
+ toolExecution: "not_started",
23378
+ action: "Check the supplied strategyPath/codePath, configuration identity and account allocation, then retry."
23379
+ }, true);
23380
+ }
23381
+ }
23231
23382
  function prepareToolArgs(tool, args, contexts) {
23232
23383
  const inherited = inheritReBacktestRunParams(tool, args, contexts);
23233
23384
  const dated = applyBacktestDateDefaults(tool, inherited);
@@ -23362,7 +23513,7 @@ function readSlaveProperties(filePath) {
23362
23513
  const entries = /* @__PURE__ */ new Map();
23363
23514
  let raw;
23364
23515
  try {
23365
- raw = fs11.readFileSync(filePath, "utf-8");
23516
+ raw = fs12.readFileSync(filePath, "utf-8");
23366
23517
  } catch {
23367
23518
  return entries;
23368
23519
  }
@@ -23386,18 +23537,18 @@ function resolveFactorProjectRoot(clientRootDir) {
23386
23537
  if (cached2) return cached2;
23387
23538
  const root = clientRootDir.replace(/[\\/]+$/, "");
23388
23539
  const configured = expandSlaveValue(
23389
- readSlaveProperties(path11.join(root, ...ZLTAF_CONFIG_SEGMENTS)),
23540
+ readSlaveProperties(path12.join(root, ...ZLTAF_CONFIG_SEGMENTS)),
23390
23541
  "slave.factor.project-path",
23391
23542
  root
23392
23543
  );
23393
- const resolved = configured ? path11.normalize(configured) : path11.join(root, ...FALLBACK_FACTOR_PROJECT_PATH);
23544
+ const resolved = configured ? path12.normalize(configured) : path12.join(root, ...FALLBACK_FACTOR_PROJECT_PATH);
23394
23545
  factorProjectRootCache.set(clientRootDir, resolved);
23395
23546
  return resolved;
23396
23547
  }
23397
23548
  function resolveFactorWorkspacePath(clientRootDir, factorId) {
23398
23549
  if (!clientRootDir || !factorId) return void 0;
23399
23550
  const projectRoot = resolveFactorProjectRoot(clientRootDir);
23400
- return projectRoot ? path11.join(projectRoot, factorId) : void 0;
23551
+ return projectRoot ? path12.join(projectRoot, factorId) : void 0;
23401
23552
  }
23402
23553
  var MODULE_LEVEL_FACTOR_EXPORT = /^(?:(?:async\s+)?def\s+get_factor\s*\(|get_factor\s*=(?!=)|Factor\s*=(?!=))/m;
23403
23554
  function checkFactorSourceLoadable(clientRootDir, factorId) {
@@ -23405,7 +23556,7 @@ function checkFactorSourceLoadable(clientRootDir, factorId) {
23405
23556
  if (!workspacePath) return null;
23406
23557
  let source;
23407
23558
  try {
23408
- source = fs11.readFileSync(path11.join(workspacePath, "main.py"), "utf8");
23559
+ source = fs12.readFileSync(path12.join(workspacePath, "main.py"), "utf8");
23409
23560
  } catch {
23410
23561
  return null;
23411
23562
  }
@@ -23504,6 +23655,9 @@ var CONTROL_PARAMS = [
23504
23655
  "timeout",
23505
23656
  "confirm",
23506
23657
  "codePath",
23658
+ "strategyPath",
23659
+ "__configBaseline",
23660
+ "__storedRunParams",
23507
23661
  "attemptFence",
23508
23662
  "__attemptStartedAtMs",
23509
23663
  "__resultBaseline"
@@ -23524,12 +23678,12 @@ function normalizeBacktestRunName(tool, args) {
23524
23678
  };
23525
23679
  }
23526
23680
  function inheritReBacktestRunParams(tool, args, contexts) {
23527
- if (tool.name !== "mcp-re-backtest") return args;
23681
+ if (tool.name !== "mcp-re-backtest" || args.__configBaseline || args.__storedRunParams) return args;
23528
23682
  const runId = typeof args.runId === "string" ? args.runId.trim() : "";
23529
23683
  const prior = runId ? contexts.get(runId)?.runParams : void 0;
23530
23684
  if (!prior) return args;
23531
23685
  const runParams = isRecord2(args.runParams) ? args.runParams : {};
23532
- return { ...args, runParams: { ...prior, ...runParams } };
23686
+ return { ...args, runParams: applyBodyDefaults(prior, runParams) };
23533
23687
  }
23534
23688
  function applyBacktestDateDefaults(tool, args) {
23535
23689
  if (tool.name !== "mcp-create-and-run-backtest") return args;
@@ -23645,18 +23799,18 @@ function backtestWindowPreflightError(tool, reason, message, details) {
23645
23799
  }, true);
23646
23800
  }
23647
23801
  function isExistingAbsoluteDirectory(value) {
23648
- if (!value || !path11.isAbsolute(value)) return false;
23802
+ if (!value || !path12.isAbsolute(value)) return false;
23649
23803
  try {
23650
- return fs11.statSync(value).isDirectory();
23804
+ return fs12.statSync(value).isDirectory();
23651
23805
  } catch {
23652
23806
  return false;
23653
23807
  }
23654
23808
  }
23655
23809
  function readBacktestIdFromCodePath(codePath) {
23656
23810
  try {
23657
- const tradesettingPath = path11.join(codePath, ".vscode", "tradesetting.json");
23658
- if (!fs11.existsSync(tradesettingPath)) return void 0;
23659
- const parsed = JSON.parse(fs11.readFileSync(tradesettingPath, "utf-8"));
23811
+ const tradesettingPath = path12.join(codePath, ".vscode", "tradesetting.json");
23812
+ if (!fs12.existsSync(tradesettingPath)) return void 0;
23813
+ const parsed = JSON.parse(fs12.readFileSync(tradesettingPath, "utf-8"));
23660
23814
  if (!isRecord2(parsed)) return void 0;
23661
23815
  const value = parsed.backtest_id ?? parsed.backtestId;
23662
23816
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
@@ -23680,7 +23834,7 @@ function capturePendingBacktestAttempt(tool, args, contexts) {
23680
23834
  if (tool.name !== "mcp-create-and-run-backtest" && tool.name !== "mcp-re-backtest") return;
23681
23835
  const runId = typeof args.runId === "string" ? args.runId.trim() : "";
23682
23836
  const suppliedCodePath = typeof args.codePath === "string" ? args.codePath.trim() : "";
23683
- const codePath = (runId ? contexts.get(runId)?.codePath : void 0) || suppliedCodePath;
23837
+ const codePath = args.__configBaseline ? suppliedCodePath : (runId ? contexts.get(runId)?.codePath : void 0) || suppliedCodePath;
23684
23838
  const resultFile = codePath ? readRunResultFile(codePath) : null;
23685
23839
  pendingBacktestAttempts.set(args, {
23686
23840
  ...resultFile ? { resultBaseline: resultFile.identity } : {},
@@ -24352,9 +24506,9 @@ var FACTOR_FAILURE_SIGNATURES = [
24352
24506
  function factorFailureMessage(data) {
24353
24507
  if (typeof data.errorMessage === "string" && data.errorMessage) return data.errorMessage;
24354
24508
  for (const key of ["calculateRecord", "detectRecord"]) {
24355
- const record2 = data[key];
24356
- if (isRecord2(record2) && typeof record2.errorMessage === "string" && record2.errorMessage) {
24357
- return record2.errorMessage;
24509
+ const record3 = data[key];
24510
+ if (isRecord2(record3) && typeof record3.errorMessage === "string" && record3.errorMessage) {
24511
+ return record3.errorMessage;
24358
24512
  }
24359
24513
  }
24360
24514
  return "";
@@ -24798,18 +24952,18 @@ function applyBacktestResultGuards(tool, args, result, states, contexts, activeR
24798
24952
  }
24799
24953
  function readRunResultFile(codePath) {
24800
24954
  try {
24801
- const resultPath = path11.join(codePath, "log", "result.json");
24802
- if (!fs11.existsSync(resultPath)) return null;
24803
- const before = fs11.statSync(resultPath);
24804
- const raw = fs11.readFileSync(resultPath);
24805
- const after = fs11.statSync(resultPath);
24955
+ const resultPath = path12.join(codePath, "log", "result.json");
24956
+ if (!fs12.existsSync(resultPath)) return null;
24957
+ const before = fs12.statSync(resultPath);
24958
+ const raw = fs12.readFileSync(resultPath);
24959
+ const after = fs12.statSync(resultPath);
24806
24960
  if (before.mtimeMs !== after.mtimeMs || before.size !== after.size) return null;
24807
24961
  const parsed = JSON.parse(raw.toString("utf-8"));
24808
24962
  if (!isRecord2(parsed)) return null;
24809
24963
  return {
24810
24964
  data: parsed,
24811
24965
  identity: {
24812
- sha256: (0, import_node_crypto2.createHash)("sha256").update(raw).digest("hex"),
24966
+ sha256: (0, import_node_crypto3.createHash)("sha256").update(raw).digest("hex"),
24813
24967
  mtimeMs: after.mtimeMs,
24814
24968
  size: after.size
24815
24969
  }
@@ -24918,7 +25072,7 @@ function captureBacktestRunContext(tool, args, result, contexts, activeRunIds) {
24918
25072
  const strategyId = typeof args.strategyId === "string" ? args.strategyId : "";
24919
25073
  const start = typeof runParams.start === "string" ? runParams.start : "";
24920
25074
  const end = typeof runParams.end === "string" ? runParams.end : "";
24921
- if (!strategyId || !start || !end) return;
25075
+ if (!strategyId || (!start || !end) && !args.__storedRunParams) return;
24922
25076
  try {
24923
25077
  const parsed = JSON.parse(result.content[0].text);
24924
25078
  if (!isRecord2(parsed) || !isRecord2(parsed.Data)) return;
@@ -24926,7 +25080,7 @@ function captureBacktestRunContext(tool, args, result, contexts, activeRunIds) {
24926
25080
  const runId = echoedRunId || (isRerun && typeof args.runId === "string" ? args.runId.trim() : "");
24927
25081
  const prior = runId ? contexts.get(runId) : void 0;
24928
25082
  const suppliedCodePath = typeof args.codePath === "string" ? args.codePath.trim() : "";
24929
- const codePath = typeof parsed.Data.codePath === "string" && parsed.Data.codePath.trim() ? parsed.Data.codePath.trim() : prior?.codePath || suppliedCodePath;
25083
+ const codePath = typeof parsed.Data.codePath === "string" && parsed.Data.codePath.trim() ? parsed.Data.codePath.trim() : args.__configBaseline ? suppliedCodePath : prior?.codePath || suppliedCodePath;
24930
25084
  if (runId) {
24931
25085
  const marketIds = Array.isArray(runParams.marketIds) ? runParams.marketIds.filter((id) => typeof id === "string") : prior?.marketIds;
24932
25086
  const sourceSha256 = hashBacktestSource(codePath) ?? prior?.sourceSha256;
@@ -24955,6 +25109,11 @@ function echoResolvedBacktestConfig(tool, args, result, contexts) {
24955
25109
  const resolvedCapital = resolveBacktestCapital(runParams);
24956
25110
  const resolvedBenchmark = resolveBacktestBenchmark(runParams);
24957
25111
  const echo = {};
25112
+ echo.configurationEvidence = {
25113
+ source: args.__storedRunParams ? "zltaf_stored_parameters" : "mcp_submission",
25114
+ persistenceVerified: false,
25115
+ ...args.__configBaseline ? { baseline: args.__configBaseline } : {}
25116
+ };
24958
25117
  if (resolvedStart) echo.resolvedStart = resolvedStart;
24959
25118
  if (resolvedEnd) echo.resolvedEnd = resolvedEnd;
24960
25119
  if (resolvedCapital !== null) echo.resolvedCapital = resolvedCapital;
@@ -24976,7 +25135,7 @@ function echoResolvedBacktestConfig(tool, args, result, contexts) {
24976
25135
  echo.codePath = context.codePath;
24977
25136
  echo.runDir = context.codePath;
24978
25137
  }
24979
- if (context?.runParams) echo.runParams = { ...context.runParams };
25138
+ if (context?.runParams && !args.__storedRunParams) echo.runParams = { ...context.runParams };
24980
25139
  if (context?.attemptStartedAtMs) {
24981
25140
  echo.attemptFence = {
24982
25141
  attemptStartedAtMs: context.attemptStartedAtMs,
@@ -24989,7 +25148,7 @@ function echoResolvedBacktestConfig(tool, args, result, contexts) {
24989
25148
  backtestId: runId,
24990
25149
  ...context?.codePath ? { runDir: context.codePath } : {},
24991
25150
  ...context?.strategyId ? { strategyId: context.strategyId } : {},
24992
- ...context?.runParams ? { effectiveRunParams: { ...context.runParams } } : {},
25151
+ ...context?.runParams && !args.__storedRunParams ? { effectiveRunParams: { ...context.runParams } } : {},
24993
25152
  ...context?.sourceSha256 ? { sourceSha256: context.sourceSha256 } : {}
24994
25153
  };
24995
25154
  }
@@ -25011,9 +25170,9 @@ function echoResolvedBacktestConfig(tool, args, result, contexts) {
25011
25170
  function hashBacktestSource(codePath) {
25012
25171
  if (!codePath) return void 0;
25013
25172
  try {
25014
- const sourcePath = path11.join(codePath, "main.py");
25015
- if (!fs11.statSync(sourcePath).isFile()) return void 0;
25016
- return (0, import_node_crypto2.createHash)("sha256").update(fs11.readFileSync(sourcePath)).digest("hex");
25173
+ const sourcePath = path12.join(codePath, "main.py");
25174
+ if (!fs12.statSync(sourcePath).isFile()) return void 0;
25175
+ return (0, import_node_crypto3.createHash)("sha256").update(fs12.readFileSync(sourcePath)).digest("hex");
25017
25176
  } catch {
25018
25177
  return void 0;
25019
25178
  }
@@ -25059,7 +25218,7 @@ function resolveBacktestBenchmark(runParams) {
25059
25218
  var STRATEGY_DOCS_TOOLS = /* @__PURE__ */ new Set(["mcp-create-strategy", "mcp-create-strategy-with-template"]);
25060
25219
  function resolveStrategyDocsPath(clientRootDir) {
25061
25220
  if (!clientRootDir) return void 0;
25062
- const docsPath = path11.join(
25221
+ const docsPath = path12.join(
25063
25222
  clientRootDir,
25064
25223
  "python",
25065
25224
  "Lib",
@@ -25069,7 +25228,7 @@ function resolveStrategyDocsPath(clientRootDir) {
25069
25228
  "docs"
25070
25229
  );
25071
25230
  try {
25072
- if (!fs11.existsSync(docsPath)) return void 0;
25231
+ if (!fs12.existsSync(docsPath)) return void 0;
25073
25232
  } catch {
25074
25233
  return void 0;
25075
25234
  }
@@ -25086,7 +25245,7 @@ function injectDebugRunId(tool, result) {
25086
25245
  const localPath = typeof parsed.Data.localPath === "string" ? parsed.Data.localPath.trim() : "";
25087
25246
  if (!localPath) return result;
25088
25247
  const settings = JSON.parse(
25089
- fs11.readFileSync(path11.join(localPath, ".vscode", "tradesetting.json"), "utf8")
25248
+ fs12.readFileSync(path12.join(localPath, ".vscode", "tradesetting.json"), "utf8")
25090
25249
  );
25091
25250
  const debugRunId = isRecord2(settings) && typeof settings.backtest_id === "string" ? settings.backtest_id.trim() : "";
25092
25251
  if (!debugRunId) return result;
@@ -25582,12 +25741,12 @@ function preflightCreatedBacktestSource(toolName, body) {
25582
25741
  if (toolName !== "mcp-create-and-run-backtest") return null;
25583
25742
  const runId = typeof body.runId === "string" ? body.runId.trim() : "";
25584
25743
  const codePath = typeof body.codePath === "string" ? body.codePath.trim() : "";
25585
- if (!codePath || !fs11.existsSync(codePath)) return null;
25586
- const sourcePath = path11.join(codePath, "main.py");
25587
- if (!fs11.existsSync(sourcePath)) return null;
25744
+ if (!codePath || !fs12.existsSync(codePath)) return null;
25745
+ const sourcePath = path12.join(codePath, "main.py");
25746
+ if (!fs12.existsSync(sourcePath)) return null;
25588
25747
  let source;
25589
25748
  try {
25590
- source = fs11.readFileSync(sourcePath, "utf8");
25749
+ source = fs12.readFileSync(sourcePath, "utf8");
25591
25750
  } catch (error2) {
25592
25751
  const message = error2 instanceof Error ? error2.message : String(error2);
25593
25752
  return jsonToolResult(10061, "\u56DE\u6D4B\u4EE3\u7801\u5FEB\u7167\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u672A\u542F\u52A8\u7B56\u7565\u5F15\u64CE\u3002", {
@@ -25607,7 +25766,7 @@ function preflightCreatedBacktestSource(toolName, body) {
25607
25766
  codePath,
25608
25767
  sourcePath,
25609
25768
  sourcePreflight: "blocked",
25610
- sourceSha256: (0, import_node_crypto2.createHash)("sha256").update(source).digest("hex"),
25769
+ sourceSha256: (0, import_node_crypto3.createHash)("sha256").update(source).digest("hex"),
25611
25770
  violations,
25612
25771
  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"
25613
25772
  }, true);
@@ -25827,7 +25986,7 @@ function normalizeDataRoot(raw, clientRootDir) {
25827
25986
  }
25828
25987
  function readJsonFile(filePath) {
25829
25988
  try {
25830
- const parsed = JSON.parse(fs11.readFileSync(filePath, "utf-8"));
25989
+ const parsed = JSON.parse(fs12.readFileSync(filePath, "utf-8"));
25831
25990
  return isRecord2(parsed) ? parsed : void 0;
25832
25991
  } catch {
25833
25992
  return void 0;
@@ -25841,8 +26000,8 @@ function resolveDataRoots(clientRootDir) {
25841
26000
  const cached2 = Date.now() - readAt < DATA_ROOTS_TTL_MS ? dataRootsCache.get(clientRootDir) : void 0;
25842
26001
  if (cached2) return cached2;
25843
26002
  const root = clientRootDir.replace(/[\\/]+$/, "");
25844
- const dcSdk = readJsonFile(path11.join(root, ...DC_SDK_CONFIG_SEGMENTS));
25845
- const dcm = readJsonFile(path11.join(root, ...DCM_CONFIG_SEGMENTS));
26003
+ const dcSdk = readJsonFile(path12.join(root, ...DC_SDK_CONFIG_SEGMENTS));
26004
+ const dcm = readJsonFile(path12.join(root, ...DCM_CONFIG_SEGMENTS));
25846
26005
  const engineRoot = normalizeDataRoot(isRecord2(dcSdk?.mdf) ? dcSdk.mdf.path : void 0, root);
25847
26006
  const downloadRoot = normalizeDataRoot(dcm?.download_home, root);
25848
26007
  const resolved = Boolean(engineRoot && downloadRoot);
@@ -25872,9 +26031,9 @@ function hqSeriesFromResults(resultItems) {
25872
26031
  function engineHasAnyHqSeries(engineRoot, series) {
25873
26032
  if (!engineRoot || series.length === 0) return void 0;
25874
26033
  for (const { exchangeId, dataType } of series) {
25875
- const dir = path11.join(engineRoot, "hqdata", "his", exchangeId, dataType);
26034
+ const dir = path12.join(engineRoot, "hqdata", "his", exchangeId, dataType);
25876
26035
  try {
25877
- if (fs11.existsSync(dir) && fs11.readdirSync(dir).length > 0) return true;
26036
+ if (fs12.existsSync(dir) && fs12.readdirSync(dir).length > 0) return true;
25878
26037
  } catch {
25879
26038
  return void 0;
25880
26039
  }
@@ -26869,8 +27028,7 @@ async function startMcpServer() {
26869
27028
  isError: true
26870
27029
  };
26871
27030
  }
26872
- const defaultedToolArgs = applyToolArgDefaults(tool, args ?? {});
26873
- const preparedArgs = prepareToolArgs(tool, defaultedToolArgs, backtestRunContexts);
27031
+ const preparedArgs = prepareToolCallArgs(tool, args ?? {}, backtestRunContexts);
26874
27032
  if ("content" in preparedArgs) return preparedArgs;
26875
27033
  const toolArgs = preparedArgs;
26876
27034
  if (tool.localHandler === "currentUser") {
@@ -27444,13 +27602,13 @@ function syncCredentialsFromEnvAtStartup() {
27444
27602
  function signalCliKeepaliveReady() {
27445
27603
  const raw = process.env.ZLT_MCP_KEEPALIVE_READY_FILE?.trim();
27446
27604
  if (process.env.ZLT_MCP_CLI !== "1" || !raw) return;
27447
- const readyFile = path13.resolve(raw);
27448
- if (path13.dirname(readyFile) !== path13.resolve(os5.tmpdir()) || !path13.basename(readyFile).startsWith("zlt-mcp-keepalive-")) {
27605
+ const readyFile = path14.resolve(raw);
27606
+ if (path14.dirname(readyFile) !== path14.resolve(os5.tmpdir()) || !path14.basename(readyFile).startsWith("zlt-mcp-keepalive-")) {
27449
27607
  return;
27450
27608
  }
27451
27609
  try {
27452
- const fd = fs12.openSync(readyFile, "wx", 384);
27453
- fs12.closeSync(fd);
27610
+ const fd = fs13.openSync(readyFile, "wx", 384);
27611
+ fs13.closeSync(fd);
27454
27612
  } catch (error2) {
27455
27613
  if (error2.code !== "EEXIST") {
27456
27614
  process.stderr.write(`[zlt-mcp-cli] keepalive readiness signal failed: ${error2 instanceof Error ? error2.message : String(error2)}
@@ -27556,6 +27714,8 @@ if (process.argv.length <= 2) {
27556
27714
  Input: omitted arguments mean {}; use --args-json for small objects and --args-file
27557
27715
  for nested configuration or shell-sensitive text. Follow the selected tool schema
27558
27716
  from list --tool <name> --json. Windows paths must be native paths (C:/...), not /c/....
27717
+ Tool schema properties are JSON keys, NOT CLI flags: use --args-json '{"params": [...]}'
27718
+ for a tool's params property, not --params. Pass the complete argument object.
27559
27719
  Output: by default stdout contains the complete MCP JSON envelope plus cliProvenance.
27560
27720
  JSON business responses are inside content[].text (or structuredContent when present).
27561
27721
  Use --output-json for large results or downstream evidence: it saves the complete
@@ -27566,45 +27726,69 @@ parsing output. Do not discard stderr, merge it into JSON, or pipe calls through
27566
27726
  head/tail/parsers that can hide the CLI exit code. --no-progress only disables progress.
27567
27727
  If saving fails after execution, the complete response is on stdout; preserve it
27568
27728
  and inspect the returned state before retrying any operation that may have run.
27729
+ Thrown call failures emit Error: followed by JSON on stderr with stage, message,
27730
+ toolExecution and retry. not_started means the CLI did not call the tool; correct
27731
+ the input/path before retrying. unknown means execution may have occurred (including
27732
+ transport or keepalive failure); inspect state first. response_received means a
27733
+ response was received, not that the business succeeded; inspect that result first.
27569
27734
  Credentials belong in the configured local credential store, never in arguments.
27570
27735
  `).action(async (tool, options) => {
27571
27736
  let outputFd;
27737
+ let stage = "arguments";
27738
+ let toolExecution = "not_started";
27572
27739
  try {
27573
27740
  const args = parseMcpArgs(options.argsJson, options.argsFile);
27574
27741
  const timeoutMs = parseTimeoutOption(options.timeoutMs, "--timeout-ms");
27575
27742
  const maxTotalTimeoutMs = parseTimeoutOption(options.maxTotalTimeoutMs, "--max-total-timeout-ms");
27576
- const resultFile = options.outputJson === void 0 ? void 0 : path14.resolve(options.outputJson);
27577
- if (resultFile !== void 0) outputFd = fs13.openSync(resultFile, "wx");
27743
+ const resultFile = options.outputJson === void 0 ? void 0 : path15.resolve(options.outputJson);
27744
+ stage = "output_prepare";
27745
+ if (resultFile !== void 0) outputFd = fs14.openSync(resultFile, "wx");
27746
+ stage = "tool_call";
27747
+ toolExecution = "unknown";
27578
27748
  const result = await callMcpTool(tool, args, {
27579
27749
  timeoutMs,
27580
27750
  maxTotalTimeoutMs,
27581
27751
  showProgress: options.progress !== false
27582
27752
  });
27753
+ toolExecution = "response_received";
27754
+ stage = "output_serialize";
27583
27755
  const complete = withCliProvenance(result);
27584
27756
  const serialized = JSON.stringify(complete) + "\n";
27585
27757
  if (outputFd !== void 0 && resultFile !== void 0) {
27758
+ stage = "output_write";
27586
27759
  try {
27587
- fs13.writeFileSync(outputFd, serialized, "utf-8");
27588
- fs13.closeSync(outputFd);
27760
+ fs14.writeFileSync(outputFd, serialized, "utf-8");
27761
+ fs14.closeSync(outputFd);
27589
27762
  outputFd = void 0;
27590
27763
  } catch (error2) {
27591
27764
  process.stdout.write(serialized);
27592
27765
  throw new Error(`Tool returned a result, but saving ${resultFile} failed; complete result is on stdout. Do not repeat the tool automatically. ${String(error2)}`);
27593
27766
  }
27767
+ stage = "output_emit";
27594
27768
  const receipt = compactCliReceipt(tool, complete, resultFile);
27595
27769
  process.stdout.write(JSON.stringify(receipt) + "\n");
27596
27770
  if (!receipt.ok) process.exitCode = 1;
27597
27771
  } else {
27772
+ stage = "output_emit";
27598
27773
  process.stdout.write(serialized);
27599
27774
  }
27600
27775
  if (result.isError === true) process.exitCode = 1;
27601
27776
  } catch (error2) {
27602
27777
  const message = error2 instanceof Error ? error2.message : String(error2);
27603
- process.stderr.write(`Error: ${message}
27778
+ const code = error2?.code;
27779
+ const retry = toolExecution === "not_started" ? "fix_input_then_retry" : toolExecution === "response_received" ? "inspect_result_before_retry" : "inspect_state_before_retry";
27780
+ process.stderr.write(`Error: ${JSON.stringify({
27781
+ tool,
27782
+ stage,
27783
+ toolExecution,
27784
+ retry,
27785
+ ...typeof code === "string" ? { code } : {},
27786
+ message
27787
+ })}
27604
27788
  `);
27605
27789
  process.exitCode = 1;
27606
27790
  } finally {
27607
- if (outputFd !== void 0) fs13.closeSync(outputFd);
27791
+ if (outputFd !== void 0) fs14.closeSync(outputFd);
27608
27792
  }
27609
27793
  });
27610
27794
  program2.command("list").description("List MCP tools exposed by the bundled server").option("--json", "Emit the complete tools/list response as JSON").option("--tool <name>", "Select one exact tool name; combine with --json for its complete schema").addHelpText("after", "\nReuse discovery within a task. Query one schema with list --tool <name> --json.\nUnknown names fail with exit code 1. Preserve stderr separately from JSON stdout.").action(async (options) => {
@@ -27613,7 +27797,12 @@ Credentials belong in the configured local credential store, never in arguments.
27613
27797
  if (options.tool !== void 0) {
27614
27798
  const tools2 = Array.isArray(result.tools) ? result.tools.filter((tool) => tool.name === options.tool) : [];
27615
27799
  if (tools2.length === 0) throw new Error(`Unknown tool: ${options.tool}. Use list to discover available names.`);
27616
- result = { ...result, tools: tools2 };
27800
+ result = { ...result, tools: tools2, cliUsage: {
27801
+ command: `call ${options.tool}`,
27802
+ argumentsOption: "--args-json",
27803
+ argumentsAreCliFlags: false,
27804
+ instruction: "Put tool schema properties inside one JSON object passed to --args-json (or use --args-file). Do not turn properties into --flags."
27805
+ } };
27617
27806
  }
27618
27807
  if (options.json) {
27619
27808
  process.stdout.write(JSON.stringify(withCliProvenance(result)) + "\n");
@@ -27684,17 +27873,17 @@ function resolveCliVersion() {
27684
27873
  return resolveCliPackageProvenance().packageVersion;
27685
27874
  }
27686
27875
  function resolveCliPackageProvenance() {
27687
- let current = path14.dirname(path14.resolve(process.argv[1] || process.cwd()));
27876
+ let current = path15.dirname(path15.resolve(process.argv[1] || process.cwd()));
27688
27877
  for (let depth = 0; depth < 6; depth += 1) {
27689
- const manifestPath = path14.join(current, "package.json");
27878
+ const manifestPath = path15.join(current, "package.json");
27690
27879
  try {
27691
- const manifest = JSON.parse(fs13.readFileSync(manifestPath, "utf-8"));
27880
+ const manifest = JSON.parse(fs14.readFileSync(manifestPath, "utf-8"));
27692
27881
  if ((manifest.name === "@zltquant/zlt-mcp" || manifest.name === "zlt-mcp-proxy") && typeof manifest.version === "string") {
27693
27882
  return { packageName: manifest.name, packageVersion: manifest.version };
27694
27883
  }
27695
27884
  } catch {
27696
27885
  }
27697
- const parent = path14.dirname(current);
27886
+ const parent = path15.dirname(current);
27698
27887
  if (parent === current) break;
27699
27888
  current = parent;
27700
27889
  }