@almanak/mcp-server 0.2.7 → 0.2.9

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/dist/tools.js CHANGED
@@ -137,8 +137,7 @@ const BACKTESTS_RUN_V3_DEFINITION = {
137
137
  'run: local or unpushed edits are never used, so commit and push the backtest YAML (and any strategy change) first, ' +
138
138
  'then call this with commit_sha set to the pushed commit (git rev-parse HEAD after the push). ' +
139
139
  'backtest_config_path names the file; omitted, the only committed backtest.yaml or backtest-economic.yaml at the ' +
140
- 'repository root is used. Economic mode (`mode: economic`) refuses banded liquidity allocations: a config.yaml with ' +
141
- 'a `band` allocation is refused before submission (V3_BACKTEST_BAND_UNSUPPORTED). ' +
140
+ 'repository root is used. ' +
142
141
  'The call submits the run, renders a live backtest widget in the chat and PAUSES until the run finishes; it then ' +
143
142
  'resumes you with the outcome as _widgetResponse. A v3 summary carries no v2 metrics: call backtests_results with ' +
144
143
  'the backtest_id for the report (return, drawdown, positions, decision interval) or the runner failure (stage, code, ' +
@@ -147,7 +146,7 @@ const BACKTESTS_RUN_V3_DEFINITION = {
147
146
  'Errors carry errorCode: COMMIT_UNRESOLVED (the commit is not in the repository: push it), REPO_ACCESS or ' +
148
147
  'GITHUB_UNAVAILABLE (repository access, not a push problem), BACKTEST_CONFIG_NOT_FOUND (file not in that commit), ' +
149
148
  'BACKTEST_CONFIG_UNREADABLE, BACKTEST_CONFIG_AMBIGUOUS (several files, pass backtest_config_path), ' +
150
- 'V3_BACKTEST_INPUT_INVALID, V3_BACKTEST_BAND_UNSUPPORTED, SDK_GENERATION_MISMATCH (the strategy link is not stored as ' +
149
+ 'V3_BACKTEST_INPUT_INVALID, SDK_GENERATION_MISMATCH (the strategy link is not stored as ' +
151
150
  'SDK v3; do not add v2 inputs). Users can have at most 10 active backtests; a 429 BACKTEST_CONCURRENCY_LIMIT error ' +
152
151
  'means do not retry until one finishes or is cancelled.',
153
152
  inputSchema: {
@@ -165,11 +164,12 @@ const BACKTESTS_RUN_V3_DEFINITION = {
165
164
  commit_sha: {
166
165
  type: 'string',
167
166
  description: 'Optional 7-40 character hexadecimal commit SHA or unique prefix of a pushed commit; it is resolved to a full SHA ' +
168
- 'in the linked repository. Pass the commit you just pushed. Omitted, the head of branch (or the default branch) runs.',
167
+ 'in the linked repository. Pass the commit you just pushed, and then omit branch. Omitted, the head of branch (or ' +
168
+ 'the default branch) runs.',
169
169
  },
170
170
  branch: {
171
171
  type: 'string',
172
- description: 'Optional branch whose head runs when commit_sha is omitted. Defaults to the repository default branch.',
172
+ description: 'Optional branch whose head runs instead of a commit_sha; never pass both. Omitted, the repository default branch runs.',
173
173
  },
174
174
  show_widget: {
175
175
  const: true,
@@ -876,17 +876,14 @@ class PlatformToolHandler {
876
876
  },
877
877
  required: ['agent_id'],
878
878
  },
879
- }, (args) => {
880
- var _a, _b;
881
- return this.fetchAgentLogsToFile({
882
- agentId: args.agent_id,
883
- navigationMode: (_a = args.navigation_mode) !== null && _a !== void 0 ? _a : 'GO_TO_END',
884
- page: (_b = args.page) !== null && _b !== void 0 ? _b : 1,
885
- targetTimestamp: args.target_timestamp,
886
- severity: args.severity,
887
- searchText: args.search_text,
888
- });
889
- });
879
+ }, (args) => { var _a, _b; return this.fetchAgentLogsToFile({
880
+ agentId: args.agent_id,
881
+ navigationMode: (_a = args.navigation_mode) !== null && _a !== void 0 ? _a : 'GO_TO_END',
882
+ page: (_b = args.page) !== null && _b !== void 0 ? _b : 1,
883
+ targetTimestamp: args.target_timestamp,
884
+ severity: args.severity,
885
+ searchText: args.search_text,
886
+ }); });
890
887
  this.register('agents', {
891
888
  name: 'agents_pnl',
892
889
  description: 'Get profit and loss data for an agent including current value, returns, and APY.',
@@ -909,15 +906,12 @@ class PlatformToolHandler {
909
906
  },
910
907
  required: ['agent_id'],
911
908
  },
912
- }, (args) => {
913
- var _a, _b, _c;
914
- return this.client.getAgentTransactions({
915
- agentId: args.agent_id,
916
- page: (_a = args.page) !== null && _a !== void 0 ? _a : 1,
917
- offset: (_b = args.offset) !== null && _b !== void 0 ? _b : 25,
918
- sort: (_c = args.sort) !== null && _c !== void 0 ? _c : 'desc',
919
- });
920
- });
909
+ }, (args) => { var _a, _b, _c; return this.client.getAgentTransactions({
910
+ agentId: args.agent_id,
911
+ page: (_a = args.page) !== null && _a !== void 0 ? _a : 1,
912
+ offset: (_b = args.offset) !== null && _b !== void 0 ? _b : 25,
913
+ sort: (_c = args.sort) !== null && _c !== void 0 ? _c : 'desc',
914
+ }); });
921
915
  this.register('agents', {
922
916
  name: 'agents_sync_status',
923
917
  description: 'Check if a deployed agent is in sync with its linked strategy repository. Returns IN_SYNC, OUT_OF_SYNC, or UNKNOWN.',
@@ -1004,16 +998,13 @@ class PlatformToolHandler {
1004
998
  },
1005
999
  required: ['wallet_id', 'eoa_address', 'chain_id'],
1006
1000
  },
1007
- }, async (args) => {
1008
- var _a;
1009
- return ({
1010
- wallet_id: args.wallet_id,
1011
- eoa_address: args.eoa_address,
1012
- chain_id: args.chain_id,
1013
- chain_name: (_a = args.chain_name) !== null && _a !== void 0 ? _a : '',
1014
- show_widget: true,
1015
- });
1016
- });
1001
+ }, async (args) => { var _a; return ({
1002
+ wallet_id: args.wallet_id,
1003
+ eoa_address: args.eoa_address,
1004
+ chain_id: args.chain_id,
1005
+ chain_name: (_a = args.chain_name) !== null && _a !== void 0 ? _a : '',
1006
+ show_widget: true,
1007
+ }); });
1017
1008
  // ─── Strategy tools ─────────────────────────────────────────
1018
1009
  this.register('strategies', {
1019
1010
  name: 'strategies_token_funding',
@@ -1040,23 +1031,25 @@ class PlatformToolHandler {
1040
1031
  },
1041
1032
  required: ['tokens'],
1042
1033
  },
1043
- }, async (args) => {
1044
- var _a, _b;
1045
- return ({
1046
- tokens: (_a = args.tokens) !== null && _a !== void 0 ? _a : [],
1047
- prompt: (_b = args.prompt) !== null && _b !== void 0 ? _b : 'How much (in USD) do you want to allocate per token?',
1048
- show_widget: true,
1049
- });
1050
- });
1034
+ }, async (args) => { var _a, _b; return ({
1035
+ tokens: (_a = args.tokens) !== null && _a !== void 0 ? _a : [],
1036
+ prompt: (_b = args.prompt) !== null && _b !== void 0 ? _b : 'How much (in USD) do you want to allocate per token?',
1037
+ show_widget: true,
1038
+ }); });
1051
1039
  this.register('strategies', {
1052
1040
  name: 'strategies_spec',
1053
- description: "Show the user an editable Strategy Spec panel to review and approve the strategy's risk posture BEFORE building. Call early — after confirming protocol support, before scaffolding. Pass plainSummary (one-line plain-language money-at-risk), fields (the few user-facing posture choices, each {key,label,description,value,unit}), the full content (the SPEC.json values), optional presets (conservative/balanced/aggressive content variants), and show_widget=true. content must include an `exit` object stating what stop/teardown does (close actions, asset policy), and fields must include an `exit.*` entry so the user sees it. If user constraints conflict with a clean exit, surface the resolution here rather than deciding silently. Give each field the unit that matches its meaning (see fields[].unit): a plain percentage such as an LP range width is `pct`, never `pct_of_balance`. Execution pauses; the user reviews/edits, then the reply is a fenced ```json``` block `{action, content}` — use the returned `content` as the approved build contract (the full SPEC values with the user's edits applied). Surface only genuine posture choices the strategy actually consults — keep safety internals in config.json.",
1041
+ description: "Show the user an editable Strategy Spec panel to review and approve the strategy's risk posture BEFORE building. Call early — after confirming protocol support, before scaffolding. Pass plainSummary (one-line plain-language money-at-risk), fields (the few user-facing posture choices, each {key,label,description,value,unit}), the full content (the SPEC.json values), optional presets (conservative/balanced/aggressive content variants), and show_widget=true. content must include an `exit` object stating what stop/teardown does (close actions, asset policy), and fields must include an `exit.*` entry so the user sees it. If user constraints conflict with a clean exit, surface the resolution here rather than deciding silently. Give each field the unit that matches its meaning (see fields[].unit): a plain percentage such as an LP range width is `pct`, never `pct_of_balance`. Execution pauses; the user reviews/edits, then the reply is a fenced ```json``` block `{action, content}` — use the returned `content` as the approved build contract (the full SPEC values with the user's edits applied). If the reply's action is `revise` it also carries `note` (a change the user asked for) and is NOT an approval: apply the note to `content` (keep fields, `behavior` and `exit` consistent), say in one line what changed or what cannot be done and the closest option, then call strategies_spec again for approval. Surface only genuine posture choices the strategy actually consults — keep safety internals in config.json.",
1054
1042
  inputSchema: {
1055
1043
  type: 'object',
1056
1044
  properties: {
1057
1045
  widgetId: { type: 'string', description: 'Widget identifier (default: "spec").' },
1058
1046
  plainSummary: { type: 'string', description: 'One-line plain-language summary of what the strategy does and risks.' },
1059
- content: { type: 'object', description: 'The SPEC.json values (the build contract).' },
1047
+ content: {
1048
+ type: 'object',
1049
+ description: 'The SPEC.json values (the build contract). Optional `behavior`: an array of 3–6 plain-language ' +
1050
+ 'trigger → action lines (entry, while a position is held, missing data, stop), shown read-only ' +
1051
+ 'under the fields as "How it behaves".',
1052
+ },
1060
1053
  fields: {
1061
1054
  type: 'array',
1062
1055
  description: 'User-facing posture fields to render. Each references a key in content.',
@@ -1683,19 +1676,20 @@ class PlatformToolHandler {
1683
1676
  * turn into a widget (the error card auto-resolves the gate immediately).
1684
1677
  */
1685
1678
  async runBacktest(args) {
1686
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
1687
- const strategyLinkId = String((_a = args.strategy_link_id) !== null && _a !== void 0 ? _a : '').trim();
1688
- const startTime = String((_b = args.start_time) !== null && _b !== void 0 ? _b : '').trim();
1679
+ var _a;
1680
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
1681
+ const strategyLinkId = String((_b = args.strategy_link_id) !== null && _b !== void 0 ? _b : '').trim();
1682
+ const startTime = String((_c = args.start_time) !== null && _c !== void 0 ? _c : '').trim();
1689
1683
  // required[] only checks presence — an empty/whitespace id would call
1690
1684
  // getStrategy('') → GET /github/strategies/ (the list route). Fail early.
1691
1685
  if (!strategyLinkId) {
1692
1686
  return {
1693
1687
  statusCode: 400,
1694
1688
  error: 'strategy_link_id is required — pass the linked strategy id (from strategies_list or the strategies_export_workspace result).',
1695
- backtest_config: { start_time: startTime, end_time: String((_c = args.end_time) !== null && _c !== void 0 ? _c : '').trim(), include_gas_costs: args.include_gas_costs !== false },
1689
+ backtest_config: { start_time: startTime, end_time: String((_d = args.end_time) !== null && _d !== void 0 ? _d : '').trim(), include_gas_costs: args.include_gas_costs !== false },
1696
1690
  };
1697
1691
  }
1698
- const endTime = String((_d = args.end_time) !== null && _d !== void 0 ? _d : '').trim();
1692
+ const endTime = String((_e = args.end_time) !== null && _e !== void 0 ? _e : '').trim();
1699
1693
  const includeGasCosts = args.include_gas_costs !== false;
1700
1694
  const backtestConfig = { start_time: startTime, end_time: endTime, include_gas_costs: includeGasCosts };
1701
1695
  const fail = (statusCode, error, extra) => ({
@@ -1728,7 +1722,7 @@ class PlatformToolHandler {
1728
1722
  try {
1729
1723
  const strategyRes = await this.client.getStrategy(strategyLinkId);
1730
1724
  if ((strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.valid) === false || !(strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.data)) {
1731
- return fail((_e = strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.statusCode) !== null && _e !== void 0 ? _e : 404, (_g = (_f = strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.message) !== null && _f !== void 0 ? _f : strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.error) !== null && _g !== void 0 ? _g : 'Strategy not found');
1725
+ return fail((_f = strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.statusCode) !== null && _f !== void 0 ? _f : 404, (_h = (_g = strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.message) !== null && _g !== void 0 ? _g : strategyRes === null || strategyRes === void 0 ? void 0 : strategyRes.error) !== null && _h !== void 0 ? _h : 'Strategy not found');
1732
1726
  }
1733
1727
  const strategy = strategyRes.data;
1734
1728
  // The stored framework_version is authoritative for which runner family serves the strategy.
@@ -1743,7 +1737,7 @@ class PlatformToolHandler {
1743
1737
  return await this.runV3Backtest(args, strategyLinkId, strategy);
1744
1738
  }
1745
1739
  if (sessionIsV3) {
1746
- return fail(400, `The strategy link is stored as SDK ${String((_h = strategy.framework_version) !== null && _h !== void 0 ? _h : 'v2').toLowerCase()}, but this chat session ` +
1740
+ return fail(400, `The strategy link is stored as SDK ${String((_j = strategy.framework_version) !== null && _j !== void 0 ? _j : 'v2').toLowerCase()}, but this chat session ` +
1747
1741
  'runs the SDK v3 lane. The stored generation decides which backtest runs, and the two disagree, so nothing was ' +
1748
1742
  'submitted. Do not add SDK v2 window inputs; tell the user the strategy link is not recognised as SDK v3 ' +
1749
1743
  '(its repository must resolve almanak 3.x and carry strategy.py and config.yaml at the root).', { errorCode: 'SDK_GENERATION_MISMATCH' });
@@ -1778,19 +1772,19 @@ class PlatformToolHandler {
1778
1772
  const refType = explicitSha ? 'commit' : 'branch';
1779
1773
  const refConfigRes = await this.client.getStrategyRefConfig(strategyLinkId, ref, refType);
1780
1774
  if ((refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.valid) === false || !(refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.data)) {
1781
- const baseError = (_k = (_j = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.message) !== null && _j !== void 0 ? _j : refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.error) !== null && _k !== void 0 ? _k : `Could not resolve ref "${ref}" for the linked strategy repository.`;
1775
+ const baseError = (_l = (_k = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.message) !== null && _k !== void 0 ? _k : refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.error) !== null && _l !== void 0 ? _l : `Could not resolve ref "${ref}" for the linked strategy repository.`;
1782
1776
  const recoveryGuidance = explicitSha
1783
1777
  ? ` Omit commit_sha only if the user explicitly approves running the latest commit on ${defaultBranch}; ` +
1784
1778
  'do not retry with different code automatically.'
1785
1779
  : '';
1786
- return fail((_l = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.statusCode) !== null && _l !== void 0 ? _l : 400, `${baseError}${recoveryGuidance}`, {
1787
- errorCode: (_m = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.errorCode) !== null && _m !== void 0 ? _m : 'INTERNAL_ERROR',
1780
+ return fail((_m = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.statusCode) !== null && _m !== void 0 ? _m : 400, `${baseError}${recoveryGuidance}`, {
1781
+ errorCode: (_o = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.errorCode) !== null && _o !== void 0 ? _o : 'INTERNAL_ERROR',
1788
1782
  retryable: (refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.retryable) === true,
1789
1783
  ...recoveryContext,
1790
1784
  });
1791
1785
  }
1792
1786
  const refConfig = refConfigRes.data;
1793
- const commitSha = (_o = refConfig.github_commit_sha) !== null && _o !== void 0 ? _o : '';
1787
+ const commitSha = (_p = refConfig.github_commit_sha) !== null && _p !== void 0 ? _p : '';
1794
1788
  if (!FULL_COMMIT_SHA_RE.test(commitSha)) {
1795
1789
  return fail(400, `Could not resolve "${ref}" to a full commit SHA — has the strategy been pushed to its repository?`, {
1796
1790
  errorCode: 'COMMIT_UNRESOLVED',
@@ -1801,13 +1795,13 @@ class PlatformToolHandler {
1801
1795
  // /ref-config — pin it to the resolved commit so it matches the config.
1802
1796
  const refMetaRes = await this.client.getStrategyRefMetadata(strategyLinkId, commitSha);
1803
1797
  if ((refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.valid) === false || !(refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.data)) {
1804
- return fail((_p = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.statusCode) !== null && _p !== void 0 ? _p : 400, (_r = (_q = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.message) !== null && _q !== void 0 ? _q : refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.error) !== null && _r !== void 0 ? _r : 'Could not load strategy ref metadata');
1798
+ return fail((_q = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.statusCode) !== null && _q !== void 0 ? _q : 400, (_s = (_r = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.message) !== null && _r !== void 0 ? _r : refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.error) !== null && _s !== void 0 ? _s : 'Could not load strategy ref metadata');
1805
1799
  }
1806
- const sdkVersion = (_s = refMetaRes.data.almanak_sdk_version) !== null && _s !== void 0 ? _s : null;
1800
+ const sdkVersion = (_t = refMetaRes.data.almanak_sdk_version) !== null && _t !== void 0 ? _t : null;
1807
1801
  if (!sdkVersion) {
1808
1802
  return fail(400, 'Could not detect the Almanak SDK version from pyproject.toml at the selected commit.');
1809
1803
  }
1810
- if (((_u = (_t = parseSdkVersion(sdkVersion)) === null || _t === void 0 ? void 0 : _t.major) !== null && _u !== void 0 ? _u : 0) >= 3) {
1804
+ if (((_u = (_a = parseSdkVersion(sdkVersion)) === null || _a === void 0 ? void 0 : _a.major) !== null && _u !== void 0 ? _u : 0) >= 3) {
1811
1805
  // The backend refuses a v3 SDK against a V2 record; say why instead of submitting the v2 shape.
1812
1806
  return fail(400, `Commit ${commitSha.slice(0, 7)} resolves almanak ${sdkVersion} (SDK v3), but the strategy link is stored as SDK v2. ` +
1813
1807
  'The generations disagree, so nothing was submitted; SDK v2 window inputs cannot backtest an SDK v3 repository.', { errorCode: 'SDK_GENERATION_MISMATCH', commit_sha: commitSha, sdk_version: sdkVersion });
@@ -1857,7 +1851,8 @@ class PlatformToolHandler {
1857
1851
  * throws and every path returns a widget-renderable shape.
1858
1852
  */
1859
1853
  async runV3Backtest(args, strategyLinkId, strategy) {
1860
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
1854
+ var _a, _b, _c;
1855
+ var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
1861
1856
  const strategyName = (typeof strategy.display_name === 'string' && strategy.display_name) ||
1862
1857
  (typeof strategy.github_repo_name === 'string' && strategy.github_repo_name) ||
1863
1858
  null;
@@ -1897,7 +1892,8 @@ class PlatformToolHandler {
1897
1892
  defaultBranch,
1898
1893
  });
1899
1894
  }
1900
- if (args.branch !== undefined && !(0, backtest_v3_1.isValidBranchName)(branch)) {
1895
+ // An empty string reads as omitted, like an empty commit_sha.
1896
+ if (branch && !(0, backtest_v3_1.isValidBranchName)(branch)) {
1901
1897
  return fail(400, `branch must be a git branch name, got "${branch}".`, { errorCode: 'V3_BACKTEST_INPUT_INVALID' });
1902
1898
  }
1903
1899
  const ref = explicitSha ? explicitSha.toLowerCase() : branch || defaultBranch;
@@ -1905,18 +1901,18 @@ class PlatformToolHandler {
1905
1901
  const recoveryContext = { requestedCommit: explicitSha || null, linkedRepo, defaultBranch };
1906
1902
  try {
1907
1903
  const refConfigRes = await this.client.getStrategyRefConfig(strategyLinkId, ref, refType);
1908
- const commitSha = (refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.valid) === false ? '' : String((_b = (_a = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.data) === null || _a === void 0 ? void 0 : _a.github_commit_sha) !== null && _b !== void 0 ? _b : '');
1904
+ const commitSha = (refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.valid) === false ? '' : String((_d = (_a = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.data) === null || _a === void 0 ? void 0 : _a.github_commit_sha) !== null && _d !== void 0 ? _d : '');
1909
1905
  if (!FULL_COMMIT_SHA_RE.test(commitSha)) {
1910
- const baseError = (_d = (_c = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.message) !== null && _c !== void 0 ? _c : refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.error) !== null && _d !== void 0 ? _d : `Could not resolve ${refType} "${ref}" in the linked strategy repository.`;
1906
+ const baseError = (_f = (_e = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.message) !== null && _e !== void 0 ? _e : refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.error) !== null && _f !== void 0 ? _f : `Could not resolve ${refType} "${ref}" in the linked strategy repository.`;
1911
1907
  // Push advice fits only a ref the repository does not have; access and availability errors keep their own text.
1912
- const errorCode = (refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.valid) === false ? ((_e = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.errorCode) !== null && _e !== void 0 ? _e : 'INTERNAL_ERROR') : 'COMMIT_UNRESOLVED';
1908
+ const errorCode = (refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.valid) === false ? ((_g = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.errorCode) !== null && _g !== void 0 ? _g : 'INTERNAL_ERROR') : 'COMMIT_UNRESOLVED';
1913
1909
  const guidance = errorCode !== 'COMMIT_UNRESOLVED'
1914
1910
  ? ''
1915
1911
  : explicitSha
1916
1912
  ? ` Only pushed commits can be backtested: make sure the commit is pushed to ${linkedRepo !== null && linkedRepo !== void 0 ? linkedRepo : 'the linked repository'} ` +
1917
1913
  '(git push, then git rev-parse HEAD) and call again with that SHA. Do not switch to a different commit without the user.'
1918
1914
  : ` Push the branch to ${linkedRepo !== null && linkedRepo !== void 0 ? linkedRepo : 'the linked repository'} first.`;
1919
- return fail((_f = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.statusCode) !== null && _f !== void 0 ? _f : 400, `${baseError}${guidance}`, {
1915
+ return fail((_h = refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.statusCode) !== null && _h !== void 0 ? _h : 400, `${baseError}${guidance}`, {
1920
1916
  errorCode,
1921
1917
  retryable: (refConfigRes === null || refConfigRes === void 0 ? void 0 : refConfigRes.retryable) === true,
1922
1918
  ...recoveryContext,
@@ -1925,7 +1921,7 @@ class PlatformToolHandler {
1925
1921
  context.commit_sha = commitSha;
1926
1922
  const refMetaRes = await this.client.getStrategyRefMetadata(strategyLinkId, commitSha);
1927
1923
  if ((refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.valid) === false || !(refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.data)) {
1928
- return fail((_g = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.statusCode) !== null && _g !== void 0 ? _g : 400, (_j = (_h = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.message) !== null && _h !== void 0 ? _h : refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.error) !== null && _j !== void 0 ? _j : 'Could not load strategy ref metadata');
1924
+ return fail((_j = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.statusCode) !== null && _j !== void 0 ? _j : 400, (_l = (_k = refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.message) !== null && _k !== void 0 ? _k : refMetaRes === null || refMetaRes === void 0 ? void 0 : refMetaRes.error) !== null && _l !== void 0 ? _l : 'Could not load strategy ref metadata');
1929
1925
  }
1930
1926
  const meta = refMetaRes.data;
1931
1927
  const sdkVersion = typeof meta.almanak_sdk_version === 'string' ? meta.almanak_sdk_version : '';
@@ -1935,7 +1931,7 @@ class PlatformToolHandler {
1935
1931
  });
1936
1932
  }
1937
1933
  context.sdk_version = sdkVersion;
1938
- if (((_l = (_k = parseSdkVersion(sdkVersion)) === null || _k === void 0 ? void 0 : _k.major) !== null && _l !== void 0 ? _l : 0) < 3 || !isSdkVersionBacktestable(sdkVersion, backtest_v3_1.MIN_BACKTEST_SDK_VERSION_V3)) {
1934
+ if (((_m = (_b = parseSdkVersion(sdkVersion)) === null || _b === void 0 ? void 0 : _b.major) !== null && _m !== void 0 ? _m : 0) < 3 || !isSdkVersionBacktestable(sdkVersion, backtest_v3_1.MIN_BACKTEST_SDK_VERSION_V3)) {
1939
1935
  return fail(400, `Hosted SDK v3 backtests need almanak ${backtest_v3_1.MIN_BACKTEST_SDK_VERSION_V3} or newer; commit ${commitSha.slice(0, 7)} resolves ` +
1940
1936
  `${sdkVersion}. Update the almanak pin (uv.lock / pyproject.toml), commit, push and re-run.`, { errorCode: 'V3_BACKTEST_INPUT_INVALID' });
1941
1937
  }
@@ -1963,36 +1959,27 @@ class PlatformToolHandler {
1963
1959
  }
1964
1960
  }
1965
1961
  context.backtest_config_path = backtestPath;
1966
- let backtestYaml = (_m = committed[backtestPath]) !== null && _m !== void 0 ? _m : null;
1967
- if (backtestYaml === null) {
1962
+ // The file must exist at the commit; its contents are validated by the runner's pinned SDK, not here.
1963
+ if (committed[backtestPath] === undefined) {
1968
1964
  const fileRes = await this.client.getStrategyFileContent(strategyLinkId, backtestPath, commitSha);
1969
- const file = (fileRes === null || fileRes === void 0 ? void 0 : fileRes.valid) === false ? null : (_o = fileRes === null || fileRes === void 0 ? void 0 : fileRes.data) === null || _o === void 0 ? void 0 : _o.file;
1970
- const lookupError = (fileRes === null || fileRes === void 0 ? void 0 : fileRes.valid) === false ? String((_q = (_p = fileRes === null || fileRes === void 0 ? void 0 : fileRes.message) !== null && _p !== void 0 ? _p : fileRes === null || fileRes === void 0 ? void 0 : fileRes.error) !== null && _q !== void 0 ? _q : '') : '';
1971
- if (typeof (file === null || file === void 0 ? void 0 : file.content) === 'string') {
1972
- backtestYaml = file.content;
1973
- }
1974
- else if (file) {
1965
+ const file = (fileRes === null || fileRes === void 0 ? void 0 : fileRes.valid) === false ? null : (_c = fileRes === null || fileRes === void 0 ? void 0 : fileRes.data) === null || _c === void 0 ? void 0 : _c.file;
1966
+ const lookupError = (fileRes === null || fileRes === void 0 ? void 0 : fileRes.valid) === false ? String((_p = (_o = fileRes === null || fileRes === void 0 ? void 0 : fileRes.message) !== null && _o !== void 0 ? _o : fileRes === null || fileRes === void 0 ? void 0 : fileRes.error) !== null && _p !== void 0 ? _p : '') : '';
1967
+ if (file && typeof file.content !== 'string') {
1975
1968
  // The preview refuses some present files (build/dist/venv paths, directories, oversized files) with a reason.
1976
- return fail(400, `Could not read ${backtestPath} at ${shortSha}: ${(_r = file.message) !== null && _r !== void 0 ? _r : 'the file is not readable as text'}. Keep the backtest ` +
1969
+ return fail(400, `Could not read ${backtestPath} at ${shortSha}: ${(_q = file.message) !== null && _q !== void 0 ? _q : 'the file is not readable as text'}. Keep the backtest ` +
1977
1970
  'YAML a small text file outside build output directories, commit and push it, then call again.', { errorCode: 'BACKTEST_CONFIG_UNREADABLE' });
1978
1971
  }
1979
- else if (lookupError !== 'File not found') {
1980
- return fail((_s = fileRes === null || fileRes === void 0 ? void 0 : fileRes.statusCode) !== null && _s !== void 0 ? _s : 502, `Could not read ${backtestPath} at ${shortSha}: ${lookupError || 'lookup failed'}`, {
1972
+ else if (!file && lookupError !== 'File not found') {
1973
+ return fail((_r = fileRes === null || fileRes === void 0 ? void 0 : fileRes.statusCode) !== null && _r !== void 0 ? _r : 502, `Could not read ${backtestPath} at ${shortSha}: ${lookupError || 'lookup failed'}`, {
1981
1974
  errorCode: 'BACKTEST_CONFIG_UNREADABLE',
1982
1975
  });
1983
1976
  }
1984
- else {
1977
+ else if (!file) {
1985
1978
  return fail(404, `${backtestPath} is not in commit ${shortSha}. Only committed, pushed files run: commit and push it, then call ` +
1986
1979
  'again with the pushed commit_sha.' +
1987
1980
  (discovered.length > 0 ? ` Committed backtest files at that commit: ${discovered.join(', ')}.` : ''), { errorCode: 'BACKTEST_CONFIG_NOT_FOUND', ...(discovered.length > 0 ? { backtest_config_paths: discovered } : {}) });
1988
1981
  }
1989
1982
  }
1990
- const configYaml = typeof meta.config_yaml === 'string' ? meta.config_yaml : '';
1991
- if ((0, backtest_v3_1.backtestMode)(backtestYaml) === 'economic' && (0, backtest_v3_1.declaresBandAllocation)(configYaml)) {
1992
- return fail(400, `${backtestPath} selects the economic model, and config.yaml at ${shortSha} declares a banded liquidity allocation ` +
1993
- `(band). The SDK refuses that combination ("${backtest_v3_1.SDK_BAND_ECONOMIC_REFUSAL}"), so the run was not submitted. ` +
1994
- 'Tell the user; backtesting this strategy needs a fixed range allocation instead of a band.', { errorCode: 'V3_BACKTEST_BAND_UNSUPPORTED' });
1995
- }
1996
1983
  const submit = await this.client.submitBacktest({
1997
1984
  github_strategy_link_id: strategyLinkId,
1998
1985
  commit_sha: commitSha,
@@ -2001,7 +1988,7 @@ class PlatformToolHandler {
2001
1988
  backtest_config_path: backtestPath,
2002
1989
  });
2003
1990
  if ((submit === null || submit === void 0 ? void 0 : submit.valid) === false || typeof (submit === null || submit === void 0 ? void 0 : submit.id) !== 'string') {
2004
- return fail((_t = submit === null || submit === void 0 ? void 0 : submit.statusCode) !== null && _t !== void 0 ? _t : 400, (_v = (_u = submit === null || submit === void 0 ? void 0 : submit.error) !== null && _u !== void 0 ? _u : submit === null || submit === void 0 ? void 0 : submit.message) !== null && _v !== void 0 ? _v : 'Backtest submission failed', {
1991
+ return fail((_s = submit === null || submit === void 0 ? void 0 : submit.statusCode) !== null && _s !== void 0 ? _s : 400, (_u = (_t = submit === null || submit === void 0 ? void 0 : submit.error) !== null && _t !== void 0 ? _t : submit === null || submit === void 0 ? void 0 : submit.message) !== null && _u !== void 0 ? _u : 'Backtest submission failed', {
2005
1992
  ...(typeof (submit === null || submit === void 0 ? void 0 : submit.errorCode) === 'string' ? { errorCode: submit.errorCode } : {}),
2006
1993
  ...(typeof (submit === null || submit === void 0 ? void 0 : submit.activeBacktests) === 'number' ? { activeBacktests: submit.activeBacktests } : {}),
2007
1994
  ...(typeof (submit === null || submit === void 0 ? void 0 : submit.maxActiveBacktests) === 'number' ? { maxActiveBacktests: submit.maxActiveBacktests } : {}),
@@ -2010,14 +1997,14 @@ class PlatformToolHandler {
2010
1997
  }
2011
1998
  return {
2012
1999
  backtest_id: submit.id,
2013
- status: (_w = submit.status) !== null && _w !== void 0 ? _w : 'PENDING',
2014
- created_at: (_x = submit.created_at) !== null && _x !== void 0 ? _x : null,
2000
+ status: (_v = submit.status) !== null && _v !== void 0 ? _v : 'PENDING',
2001
+ created_at: (_w = submit.created_at) !== null && _w !== void 0 ? _w : null,
2015
2002
  ...context,
2016
2003
  show_widget: true,
2017
2004
  };
2018
2005
  }
2019
2006
  catch (err) {
2020
- return fail(502, `Backtest submission failed: ${(_y = err === null || err === void 0 ? void 0 : err.message) !== null && _y !== void 0 ? _y : String(err)}`);
2007
+ return fail(502, `Backtest submission failed: ${(_x = err === null || err === void 0 ? void 0 : err.message) !== null && _x !== void 0 ? _x : String(err)}`);
2021
2008
  }
2022
2009
  }
2023
2010
  /**
@@ -2029,7 +2016,8 @@ class PlatformToolHandler {
2029
2016
  * path returned — equity curves and per-trade detail are read on demand.
2030
2017
  */
2031
2018
  async getBacktestResults(args) {
2032
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
2019
+ var _a, _b;
2020
+ var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
2033
2021
  const backtestId = typeof args.backtest_id === 'string' ? args.backtest_id.trim() : '';
2034
2022
  if (!backtestId) {
2035
2023
  return {
@@ -2042,30 +2030,30 @@ class PlatformToolHandler {
2042
2030
  run = await this.client.getBacktest(backtestId);
2043
2031
  }
2044
2032
  catch (err) {
2045
- return { statusCode: 502, error: `Backtest lookup failed: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : String(err)}` };
2033
+ return { statusCode: 502, error: `Backtest lookup failed: ${(_c = err === null || err === void 0 ? void 0 : err.message) !== null && _c !== void 0 ? _c : String(err)}` };
2046
2034
  }
2047
2035
  if (!run || run.valid === false) {
2048
2036
  return {
2049
- statusCode: (_b = run === null || run === void 0 ? void 0 : run.statusCode) !== null && _b !== void 0 ? _b : 404,
2050
- error: (_c = run === null || run === void 0 ? void 0 : run.error) !== null && _c !== void 0 ? _c : 'Backtest run not found',
2037
+ statusCode: (_d = run === null || run === void 0 ? void 0 : run.statusCode) !== null && _d !== void 0 ? _d : 404,
2038
+ error: (_e = run === null || run === void 0 ? void 0 : run.error) !== null && _e !== void 0 ? _e : 'Backtest run not found',
2051
2039
  backtest_id: backtestId,
2052
2040
  };
2053
2041
  }
2054
2042
  if ((0, backtest_v3_1.isV3BacktestRun)(run)) {
2055
2043
  return this.getV3BacktestResults(backtestId, run);
2056
2044
  }
2057
- const summaryVerdict = summarizeRunValidity((_d = run.result_summary) === null || _d === void 0 ? void 0 : _d.run_validity);
2045
+ const summaryVerdict = summarizeRunValidity((_a = run.result_summary) === null || _a === void 0 ? void 0 : _a.run_validity);
2058
2046
  const base = {
2059
- backtest_id: (_e = run.id) !== null && _e !== void 0 ? _e : backtestId,
2047
+ backtest_id: (_f = run.id) !== null && _f !== void 0 ? _f : backtestId,
2060
2048
  status: run.status,
2061
- created_at: (_f = run.created_at) !== null && _f !== void 0 ? _f : null,
2062
- completed_at: (_g = run.completed_at) !== null && _g !== void 0 ? _g : null,
2063
- strategy_link_id: (_h = run.github_strategy_link_id) !== null && _h !== void 0 ? _h : null,
2064
- commit_sha: (_j = run.commit_sha) !== null && _j !== void 0 ? _j : null,
2065
- sdk_version: (_k = run.sdk_version) !== null && _k !== void 0 ? _k : null,
2066
- result_summary: (_l = run.result_summary) !== null && _l !== void 0 ? _l : null,
2049
+ created_at: (_g = run.created_at) !== null && _g !== void 0 ? _g : null,
2050
+ completed_at: (_h = run.completed_at) !== null && _h !== void 0 ? _h : null,
2051
+ strategy_link_id: (_j = run.github_strategy_link_id) !== null && _j !== void 0 ? _j : null,
2052
+ commit_sha: (_k = run.commit_sha) !== null && _k !== void 0 ? _k : null,
2053
+ sdk_version: (_l = run.sdk_version) !== null && _l !== void 0 ? _l : null,
2054
+ result_summary: (_m = run.result_summary) !== null && _m !== void 0 ? _m : null,
2067
2055
  run_validity: summaryVerdict,
2068
- error_message: (_m = run.error_message) !== null && _m !== void 0 ? _m : null,
2056
+ error_message: (_o = run.error_message) !== null && _o !== void 0 ? _o : null,
2069
2057
  };
2070
2058
  if (run.status !== 'COMPLETED') {
2071
2059
  let hint;
@@ -2094,24 +2082,24 @@ class PlatformToolHandler {
2094
2082
  doc = await this.client.getBacktestResult(backtestId);
2095
2083
  }
2096
2084
  catch (err) {
2097
- return { ...base, result_fetch_error: `Result fetch failed: ${(_o = err === null || err === void 0 ? void 0 : err.message) !== null && _o !== void 0 ? _o : String(err)}` };
2085
+ return { ...base, result_fetch_error: `Result fetch failed: ${(_p = err === null || err === void 0 ? void 0 : err.message) !== null && _p !== void 0 ? _p : String(err)}` };
2098
2086
  }
2099
2087
  if (!doc || doc.valid === false) {
2100
- return { ...base, result_fetch_error: (_p = doc === null || doc === void 0 ? void 0 : doc.error) !== null && _p !== void 0 ? _p : 'Backtest result not found' };
2088
+ return { ...base, result_fetch_error: (_q = doc === null || doc === void 0 ? void 0 : doc.error) !== null && _q !== void 0 ? _q : 'Backtest result not found' };
2101
2089
  }
2102
2090
  // Compute the compact inline fields BEFORE artifact persistence, so a
2103
2091
  // failed write degrades to summaries-without-a-file instead of losing
2104
2092
  // the whole (successful) lookup.
2105
- const result = (_q = doc.result) !== null && _q !== void 0 ? _q : {};
2093
+ const result = (_r = doc.result) !== null && _r !== void 0 ? _r : {};
2106
2094
  const trades = Array.isArray(result.trades) ? result.trades : [];
2107
- const decisionSummary = (_r = result.decision_summary) !== null && _r !== void 0 ? _r : null;
2108
- const runValidity = (_s = summarizeRunValidity(result.run_validity)) !== null && _s !== void 0 ? _s : summaryVerdict;
2095
+ const decisionSummary = (_s = result.decision_summary) !== null && _s !== void 0 ? _s : null;
2096
+ const runValidity = (_t = summarizeRunValidity(result.run_validity)) !== null && _t !== void 0 ? _t : summaryVerdict;
2109
2097
  const inline = {
2110
2098
  ...base,
2111
2099
  run_validity: runValidity,
2112
2100
  decision_summary: decisionSummary,
2113
- decision_input_failures: (_t = result.decision_input_failures) !== null && _t !== void 0 ? _t : [],
2114
- data_manifest_summary: (_v = (_u = result.data_manifest) === null || _u === void 0 ? void 0 : _u.summary) !== null && _v !== void 0 ? _v : null,
2101
+ decision_input_failures: (_u = result.decision_input_failures) !== null && _u !== void 0 ? _u : [],
2102
+ data_manifest_summary: (_v = (_b = result.data_manifest) === null || _b === void 0 ? void 0 : _b.summary) !== null && _v !== void 0 ? _v : null,
2115
2103
  trades_count: trades.length,
2116
2104
  };
2117
2105
  const passiveHint = (runValidity === null || runValidity === void 0 ? void 0 : runValidity.passive_only) === true