@battlegrid/mcp-server 31.1.0 → 31.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,6 +24,195 @@ Seeing package `31.x` alongside handshake `battlegrid@33.x` — the package **be
24
24
 
25
25
  **What this changes for you:** nothing about how you call anything. Upgrading the package no longer waits on a server deploy, and a server deploy no longer strands you on a package that names the wrong contract — reconnect and the announcement follows. **Contract breaking-change notes are no longer keyed to package versions**, since a contract move is no longer a release here; the v11-and-earlier notes below are kept as history, and the live vocabulary is always discovery.
26
26
 
27
+ ## Contract history — v37 → v48.1
28
+
29
+ Eleven majors reached authors while this section stopped at v36. That gap is the mechanism, not an
30
+ oversight: since v31 a contract move needs no release here, so nothing forced a note to be written —
31
+ and the documentation ships inside the tarball, so a note written but unpublished reaches nobody.
32
+ Both halves are now closed by a rule keyed to the *served* contract rather than to a release of this
33
+ package.
34
+
35
+ ### Changed meaning, unchanged shape
36
+
37
+ - **Three tools serve different values for identical input** (47.3.0, `derive-scan-fetch-from-report`).
38
+ The radar scan leg now derives its timeframe fetch from the strategy's **report** rather than the
39
+ on-duty agent's three perception rungs, so a required condition addressing an absolute timeframe
40
+ outside those rungs — never evaluated at scan before — now is. No field moves; the values do:
41
+
42
+ | Tool | What moves |
43
+ |---|---|
44
+ | `preview_radar_resolution` | `conditionReach[].reachReason` goes `AGENT_TIMEFRAME` → `null` |
45
+ | `get_radar_activity` | `scanReachReason` moves the same way, **on new rows only** — rows already written keep what they were recorded with |
46
+ | `get_agent_coin_qualification` | `reachReason` moves the same way; its sibling `verdict` moves `UNMEASURABLE` → a decided verdict |
47
+
48
+ **Read the last one carefully:** a client treating `UNMEASURABLE` as "this gate is switched off"
49
+ will now see that gate **BLOCK**. `AGENT_TIMEFRAME` keeps its member and narrows to the one cause
50
+ no fetch can discharge. Nothing in the payload tells you this moved.
51
+
52
+ ### Rejected input — something you author is no longer accepted
53
+
54
+ - **A benchmark-bound section no longer accepts crowd metrics or rank transforms** (49.0.0,
55
+ `fix-benchmark-legality-save-path`). On a custom section carrying a non-null `benchmarkTicker`, a
56
+ column whose metric is enrichment-stage (the `CROWD_*` family, `FLOW_ALIGN`, `SMART_RETAIL`,
57
+ `CAPTAIN_CONF`, `CONFIDENCE`, `SETTLED_AT`, the `PERP_SPOT_*` trio) is refused with
58
+ `REPORT_COLUMN_BENCHMARK_METRIC_UNSUPPORTED`, and one carrying a `rank` transform in either the
59
+ direct or the chained position with `REPORT_COLUMN_BENCHMARK_TRANSFORM_UNSUPPORTED`. Every tool
60
+ accepting a section array is affected, and no other byte of your body changes.
61
+
62
+ **Fix it by moving the column, not by retrying.** Both readings are defined *relative to the
63
+ cohort being evaluated* — a crowd reading is what this session's players did, a rank is a position
64
+ among the coins under evaluation — and a benchmark is deliberately outside that cohort. Neither
65
+ has a value there, which is why such a column could never render. Put it on an ordinary section
66
+ (`benchmarkTicker: null`), or drop it.
67
+
68
+ **This is a fix, not a new rule.** The restriction shipped with benchmark sections and was already
69
+ enforced by the column builder, by report materialization, and by `get_strategy_column_contract` —
70
+ so an author who checked a column against discovery first has been seeing this refusal all along.
71
+ What changed is that the SAVE path now asks the same question. Previously it did not, so such a
72
+ section persisted and then failed at every evaluation instead of at authoring.
73
+
74
+ - **A custom report section no longer accepts `timeframe`** (48.0.0,
75
+ `remove-section-anchor-override`). The per-section anchor override is gone. Every tool accepting a
76
+ section array — `compile_strategy_plan`, `preview_strategy_report`, `derive_strategy_rule_view`,
77
+ and `apply_strategy_plan` — refuses a section carrying it: `sections[N]: Unrecognized key(s)`,
78
+ with no other byte of your body changing. Drop the key. A section's columns resolve against the
79
+ **strategy** timeframe, and a column reaches any other timeframe by pinning it on the column
80
+ (`timeframe: { abs: '4h' }`) — which it could always do. Relative column references
81
+ (`anchor`/`lower`/`regime`) are untouched, and are the point: they track the strategy.
82
+
83
+ - **A custom report section requires `notes`** (43.0.0, `add-authored-section-notes`). Every tool
84
+ accepting a section array — `compile_strategy_plan`, `preview_strategy_report`,
85
+ `derive_strategy_rule_view` — refuses a section without it: `sections[N].notes: Required`, with no
86
+ byte of your body changing. Send explicit `null` for "no note". It is required rather than optional
87
+ because these payloads are a FULL REPLACE: an omitted key and an explicit `null` would be the same
88
+ request on the wire, so every rebuild site would silently clear a note its author wrote.
89
+ `benchmarkTicker` carries the same required-nullable discipline for the same reason.
90
+
91
+ - **Every condition requires `clock` and `closes`** (44.0.0, `add-condition-clock`), and **`exit`**
92
+ arrives with them. A condition entry now carries eight keys, not five. `clock` is `"LIVE"` (the
93
+ previous behaviour — the forming bar) or `"CLOSE"` (settled bars); `closes` is how many consecutive
94
+ closed bars must read true, 1–5, and is always `1` under LIVE. No wire default, for the same
95
+ whole-set-replacement reason as `notes`: a defaulted key would let an unrelated re-save silently
96
+ un-clock an enforced money gate back to forming-bar evidence.
97
+
98
+ A `CLOSE` clock is accepted only where a closed frame can change the reading — the clause must
99
+ resolve from the coin's own candle series at offset 0. Frame-inert operands (perp-payload scalars,
100
+ published rolling changes, ranks, zone entities, regime labels, enrichment metrics, session
101
+ scalars) are refused with `CONDITION_CLOCK_OPERAND_ILLEGAL` naming the header and the remedy: split
102
+ that clause into its own LIVE condition and `conditionRef` it. `exit: true` is legal only under
103
+ `clock: "CLOSE"` — an exit fired on a forming bar is an intrabar exit.
104
+
105
+ - **A strategy requires a seven-key `entry` object** (44.0.0 for four keys, 47.0.0 for three more —
106
+ `add-entry-on-close`, `add-level-trigger-execution`). Required on every CREATE, on
107
+ `compile_strategy_plan`, `apply_strategy_plan` and `update_intelligence_agent`. A client sending
108
+ 44.0.0's four-key object is refused under 47 with `entry.levelSource: Required` without one byte of
109
+ it changing.
110
+
111
+ ```jsonc
112
+ "entry": {
113
+ "trigger": "AT_SIGNAL", // | ON_CANDLE_CLOSE | STOP_THROUGH_LEVEL | ON_RETEST
114
+ "confirmTf": "4h", // the strategy timeframe or the rung below it — nothing else
115
+ "closes": 1, // 1–5; must be 1 unless ON_CANDLE_CLOSE
116
+ "bandAtrMultiple": 1.0, // > 0, and <= the platform's entry-deviation gate
117
+ "levelSource": "SWING_HIGH", // | SWING_LOW | BOLLINGER_UPPER | BOLLINGER_LOWER
118
+ "levelOffsetAtrMultiple": 0, // 0–2, UNSIGNED; must be 0 unless a level trigger
119
+ "validForBars": 4 // 1–24 of the strategy's own bars
120
+ }
121
+ ```
122
+
123
+ `AT_SIGNAL` with those values is byte-identical to pre-44 behaviour. The legality matrix runs one
124
+ way: all seven keys are always present, so a MEANINGFUL value under a trigger that ignores it is
125
+ refused rather than accepted-and-dropped — a dial never silently does nothing.
126
+
127
+ - **A `strategyTimeframe` the platform does not ingest is refused** (39.0.0,
128
+ `move-renderer-to-rendered-section`) on `get_coin_market_context`, where it was previously
129
+ accepted. The number is the only signal a client gets.
130
+
131
+ - **`eventType` gains `ENTRY_EXPIRED_UNCONFIRMED`** (48.1.0, `fix-entry-arming-lifecycle`) on
132
+ `get_radar_activity` and `get_radar_activity_summary` — an armed entry that reached its episode
133
+ lifetime without ever receiving a confirming close. A client holding its own closed copy of that
134
+ enum rejects the new member; one that switches exhaustively on it needs the branch.
135
+
136
+ `entryVoidCause` is deliberately **unchanged** and still carries exactly `BAND`, `CONDITIONS`,
137
+ `STRUCTURAL`. Each of those is a measurement taken *at* a close, so an episode that reached no
138
+ close gets its own event type rather than a fourth cause — `ENTRY_VOIDED` means "called off at the
139
+ close", a claim this outcome must not make.
140
+
141
+ The authoring boundary also gains a refusal with **no schema change**: a strategy declaring an
142
+ arming trigger whose `required` conditions read the `LIVE` clock is rejected on the existing
143
+ `VALIDATION_ERROR` code, naming the offending condition key. Same code, same shape, new reason —
144
+ so nothing in the published schema tells you it can now happen.
145
+
146
+ ### Removed — no alias exists
147
+
148
+ - **`get_coin_market_context` is REMOVED** (40.0.0, `retire-get-coin-market-context`). Calling it
149
+ returns an unknown-tool error. There is deliberately **no alias**: a silent redirect would hide a
150
+ payload shape change from a client that never asked for one. Use `get_market_context`.
151
+
152
+ - **`get_macd_heatmap` leaves the published surface** (41.0.0). Same shape of break, same absence of
153
+ an alias.
154
+
155
+ - **`isPrimary` is removed from every published `EvaluatedSignal`** (38.0.0) — `get_signal_log`,
156
+ `get_public_agent_signal_log_detail`, and every other tool returning a signal scorecard. Reading it
157
+ now finds the key absent rather than false.
158
+
159
+ ### Reshaped output — the same call returns a different shape
160
+
161
+ - **The normalized report section loses `timeframe`** (48.0.0, `remove-section-anchor-override`), on
162
+ every tool that publishes a strategy: `get_strategy`, `fork_strategy`, `archive_strategy`,
163
+ `restore_strategy`, `update_strategy_signal_rule`, `apply_strategy_plan`, and
164
+ `compile_strategy_plan`'s post-state. A strict parser rejects the shorter object; a lenient one
165
+ reads a section whose anchor is the strategy timeframe, which it now always is.
166
+
167
+ - **`get_strategy_column_contract` renames its anchor, both ways** (48.0.0). The request field
168
+ `sectionTimeframe` becomes `anchorTimeframe` — same meaning, and still optional. On the response,
169
+ `timeframe.requiresSectionTimeframe` becomes `requiresAnchorTimeframe`, and
170
+ `timeframe.sectionTimeframeOverrideAllowed` is **removed**: it published whether a column could go
171
+ in an anchor-overridden section, and no section can be overridden. One call also stops being
172
+ refused — a timeframe-inert metric supplied with an anchor now compiles.
173
+ `REPORT_COLUMN_SECTION_TIMEFRAME_UNSUPPORTED` leaves the `authoringCode` vocabulary.
174
+
175
+ - **`RenderedSection.notes` stops carrying provenance** (42.0.0, `separate-section-facts-from-read`),
176
+ on `preview_strategy_report`, `compile_strategy_plan` and `get_market_context`. A new REQUIRED
177
+ `provenance: string[]` carries it instead. A client reading provenance out of `notes` now reads an
178
+ author's prose, or nothing — which is a silent misread, not an error.
179
+
180
+ - **`preview_strategy_report.renderedSections[]` gains `authoredNote`** (43.0.0), the author's read
181
+ for a custom row and `null` on a platform row. Additive, but published on a `.strict()` shape.
182
+
183
+ - **`get_radar_activity` serves its evaluation curve on the FIRST PAGE ONLY** (37.0.0), and
184
+ `get_radar_activity_summary` is added. A client reading the curve off a later page finds it absent.
185
+
186
+ ### Widened enum — new members your own copy rejects
187
+
188
+ - **The authorable metric vocabulary widens by 29 keys** (46.1.0, `add-indicator-catalog-coverage`):
189
+ Keltner (`KC_UPPER`/`KC_MID`/`KC_LOWER`), Supertrend (`ST_LINE`/`ST_DIR`), Hull (`HMA20`),
190
+ WaveTrend (`WT1`/`WT2`), QQE (`QQE_RSI_MA`/`QQE_STOP`), Parabolic SAR (`PSAR`), Ichimoku
191
+ (`ICHI_CONV`/`ICHI_BASE`/`ICHI_SPAN_A`/`ICHI_SPAN_B`/`ICHI_LAG`), Williams %R (`WILLR14`),
192
+ Stochastic RSI (`STOCH_RSI14`), session pivots (`PIVOT_P`/`PIVOT_R1`–`R3`/`PIVOT_S1`–`S3`), plus
193
+ four already-published fields that became addressable: `BB_UPPER`, `BB_LOWER`, `DI_PLUS`,
194
+ `DI_MINUS`.
195
+
196
+ Additive on the wire — every request you can send today is still accepted. It is called out here
197
+ because a client holding its own closed copy of the metric enum rejects the new members, and
198
+ because **an agent holding a cached belief that these are inexpressible will substitute for a
199
+ primitive the platform now serves**. That failure raises no error at all: the author is simply told
200
+ a strategy cannot be built.
201
+
202
+ - **`EntryTrigger` gains `STOP_THROUGH_LEVEL` and `ON_RETEST`, and `EntryLevelSource` is published
203
+ for the first time** with four members (47.0.0). Additive on their own; the required keys above are
204
+ what make that bump a major.
205
+
206
+ - **Each signal-checklist item's `measured` object gains a required `triggered` boolean** (47.2.0,
207
+ `fix-entry-prompt-signal-evidence`) on `get_signal_log` and `get_public_agent_signal_log_detail`,
208
+ on the `numeric` and `categorical` arms. Additive and MINOR — a client ignoring it is unaffected —
209
+ but it is named here because a client parsing that output strictly rejects the new key, and because
210
+ leaving 47.2 unlisted would make a reader wonder what happened to it.
211
+
212
+ The `unavailable` arm deliberately does **not** carry `triggered`: "the claim could not be joined to
213
+ a stored result" and "the signal did not fire" are different states, and leaving the key off that
214
+ arm makes conflating them a type error rather than a convention.
215
+
27
216
  ## Contract history — v12 → v36
28
217
 
29
218
  > **The number in this heading is a CONTRACT version, not this package's version.** The npm badge at the top
@@ -176,6 +365,38 @@ Nothing in the proxy changes. No configuration, no environment variable, no call
176
365
 
177
366
  ### Additive in the same span
178
367
 
368
+ - **`get_regime_snapshot` publishes the evidence behind the verdict** (47.1.0,
369
+ `publish-regime-classification-evidence`). The snapshot gains `evidence`: the quantities the
370
+ classifier read, the gates it tested them against, the signed margin to the gate deciding whether
371
+ the current label survives, and the two decision facts only the classifier holds — `gateState`
372
+ (`cleared` / `held` / `dropped`) and `directionSource` (`di` / `ema`). Nothing narrows; a client
373
+ that ignores the field is unaffected.
374
+
375
+ Read `gateState` before you trust a trend label: **`held` means the ADX hysteresis buffer is
376
+ carrying the PREVIOUS bar's label rather than this bar re-confirming it** — a materially weaker
377
+ claim wearing the same word, and one no client could previously detect. `directionSource: 'ema'`
378
+ is the same shape of warning: the direction came from the fallback that fires precisely when the
379
+ DI spread is indecisive. The margin is signed so **positive always means "the current label
380
+ survives by this much"**, in every gate state, so it is safe to branch on its sign.
381
+
382
+ `conviction` is a BRANCH DISCRIMINATOR, not a confidence: it encodes *which* rule in the priority
383
+ ladder matched, not how comfortably it matched. The margins carry comfort. A client reading
384
+ conviction as a strength score is reading it wrong, and always was — this release just makes the
385
+ alternative available.
386
+
387
+ - **Thirteen metric keys join the catalog** (47.1.0) — the `regime` family gains `REGIME_STATE`,
388
+ `REGIME_CONVICTION`, `REGIME_RUN_BARS`, `REGIME_TREND_GATE`, `REGIME_TREND_MARGIN`,
389
+ `REGIME_TREND_SOURCE`, `REGIME_DI_SPREAD`, `REGIME_VOL_ATR_RATIO`, `REGIME_VOL_BBW_RATIO`,
390
+ `REGIME_MOM_BULL_VOTES`, `REGIME_MOM_BEAR_VOTES`, `REGIME_CRASH_MARGIN` and `REGIME_CRASH_LATCH`,
391
+ making the composite regime and its evidence addressable in a report column or condition for the
392
+ first time. Only a client that switches exhaustively on `MetricKey` needs new branches.
393
+
394
+ Not a contract change, but worth knowing if you author conditions: the report grammar's regime
395
+ metrics now resolve from the **confirmed close** on every path. They previously resolved from the
396
+ forming bar when a report was rendered and the confirmed close when the scan swept, so the same
397
+ condition could read differently in preview than in production. Same wire shape; same bar
398
+ everywhere now.
399
+
179
400
  `27.1.0` exit-policy authoring input on `compile_strategy_plan` · `19.2.0` `get_account_state` account identity · `19.1.0` Standing Orders marker authoring · `18.4.0` `list_gate_blocks` summary groups · `18.3.0` radar maintenance pause · `18.1.0` protection geometry · `17.2.0` break-even/trailing status · `17.1.0` `get_signal_log` condition evaluation · `13.1.0` four owner-scoped read tools · `12.1.0` cross-venue spot price metrics · `11.1.0` discoverable rate limit.
180
401
 
181
402
  ## v11 and earlier — contract history (v6 → v11)
@@ -484,7 +705,7 @@ Tools, prompts, and resources are **discovered live** from the connected server
484
705
 
485
706
  - **The announced contract is relayed, not declared.** The proxy reads the upstream server's identity from the handshake it just completed and re-announces it verbatim to the local client. A local client therefore always reads the contract it will actually reach, and the package version is free to mean only what it should: this proxy's own code. **There is no pairing rule to keep, and no publish-time deploy gate** — a released package makes no claim about the server, so there is no ordering between a release here and a deploy there. This replaces the `MAJOR.MINOR` pairing that held through v11; see [v12](#v12--the-announced-contract-is-read-from-the-server-not-declared-here).
486
707
  - **Fails closed, never falls back.** If a connected server announced no `serverInfo` — a protocol violation, since it is required in a successful `initialize` result — the proxy refuses to start rather than substituting its own version. There is no honest number to announce in that case, and announcing a dishonest one silently is the failure this design removes.
487
- - **Rediscover after a server cutover.** Package publication does not refresh a running proxy's cached startup snapshot. Restart/reconnect the proxy and re-run `tools/list`, `prompts/list`, and `resources/list` after the server deploys.
708
+ - **Rediscover after a server cutover.** The proxy resolves its catalog on first use and then caches it for the life of the process; a resolution that *failed* is retried on the next request, but a server deploy is never noticed. Package publication does not refresh it either. Restart/reconnect the proxy and re-run `tools/list`, `prompts/list`, and `resources/list` after the server deploys.
488
709
  - **Restart after key rotation.** API keys are read once at process startup; rotate a key, then restart the proxy.
489
710
 
490
711
  | Version | Changes |
@@ -577,7 +798,7 @@ Also confirm npm's Trusted Publisher for `@battlegrid/mcp-server` is GitHub Acti
577
798
 
578
799
  Two things do still need doing, neither of them a release:
579
800
 
580
- - **Reconnect** to pick up the new contract — the announcement and the capability snapshot are both read once at startup (see [Rediscovery & versioning](#rediscovery--versioning)).
801
+ - **Reconnect** to pick up the new contract — the announcement is read once from the handshake at startup, and the capability snapshot is resolved on first use and cached thereafter (see [Rediscovery & versioning](#rediscovery--versioning)).
581
802
  - **Document the break** where the contract is documented, in `battlegrid-app`. Contract breaking-change notes are no longer keyed to package versions in this README, because a contract move is no longer a release here.
582
803
 
583
804
  ### Verify publication
package/dist/index.d.ts CHANGED
@@ -37,7 +37,7 @@
37
37
  */
38
38
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
39
39
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
40
- import { type Implementation } from '@modelcontextprotocol/sdk/types.js';
40
+ import { type Implementation, type Prompt, type Resource } from '@modelcontextprotocol/sdk/types.js';
41
41
  /**
42
42
  * This package's own version — the proxy's code, and nothing else.
43
43
  *
@@ -49,7 +49,7 @@ import { type Implementation } from '@modelcontextprotocol/sdk/types.js';
49
49
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
50
50
  * correction. Never move it to track the server.
51
51
  */
52
- export declare const PACKAGE_VERSION = "31.1.0";
52
+ export declare const PACKAGE_VERSION = "31.2.0";
53
53
  export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
54
54
  export interface EnvConfig {
55
55
  apiKeys: string[];
@@ -106,26 +106,73 @@ export declare function injectAccountParam(tools: ToolDefinition[], accountNames
106
106
  */
107
107
  export declare function announcedIdentityOf(serverInfo: Implementation | undefined): Implementation;
108
108
  export interface CreateProxyServerOptions {
109
- /** Discovered account identities (index 0 is the primary key used for capability discovery). */
110
- identities: AccountIdentity[];
111
- /** Connect (or reuse) a remote MCP client for a given API key. Called once per key, lazily. */
109
+ /**
110
+ * The operator's FIRST configured key — used for the announcing handshake, the prompt and
111
+ * resource surfaces, and single-account routing. Deliberately not derived from whichever
112
+ * identity lookups happened to succeed: that made a mistyped first key silently promote the
113
+ * second, relocating every unqualified call to a different account.
114
+ */
115
+ primaryKey: string;
116
+ /**
117
+ * Whether a tool call must name an account. Derived from the CONFIGURED key count, so the guard
118
+ * is armed before any network call. Deriving it from resolved identities would let a transient
119
+ * identity failure convert a multi-account proxy into a single-account one and route an
120
+ * unqualified money-moving call to the primary.
121
+ */
122
+ isMultiAccount: boolean;
123
+ /** Connect (or reuse) a remote MCP client for a given API key. */
112
124
  connect: (apiKey: string) => Promise<Client>;
125
+ /** Resolve account identities. Awaited lazily, on the first request that needs the catalog. */
126
+ resolveIdentities: () => Promise<AccountIdentity[]>;
113
127
  }
114
- export interface ProxyServer {
115
- server: Server;
128
+ /**
129
+ * Everything the catalog resolution produces, resolved together as one shared value.
130
+ *
131
+ * The account selector published on every tool schema and the membership check applied to an
132
+ * incoming call are one rule at two altitudes, so `accountNames` and `keyByAccount` are members
133
+ * here rather than re-derived at each handler.
134
+ */
135
+ export interface ProxyCatalog {
136
+ identities: AccountIdentity[];
137
+ accountNames: string[];
138
+ keyByAccount: Map<string, string>;
139
+ tools: ToolDefinition[];
140
+ prompts: Prompt[];
141
+ resources: Resource[];
142
+ }
143
+ /**
144
+ * What a completed warm-up reports back to `main()`, which owns every startup diagnostic.
145
+ *
146
+ * Carries `accountNames` as well as the counts because the account-roster line is the only surface
147
+ * naming which accounts actually resolved — the signal a dangling primary or a silent single-account
148
+ * collapse would otherwise hide.
149
+ */
150
+ export interface WarmSummary {
151
+ accountNames: string[];
116
152
  toolCount: number;
117
153
  promptCount: number;
118
154
  resourceCount: number;
119
- isMultiAccount: boolean;
120
- accountNames: string[];
155
+ }
156
+ export interface ProxyServer {
157
+ server: Server;
121
158
  /** The upstream identity relayed downstream — the contract a local client will actually reach. */
122
159
  announced: Implementation;
160
+ /**
161
+ * Resolve the catalog, returning its counts. Returns the SAME memoized resolution a request
162
+ * triggers — it starts nothing of its own, so warm-up and a first request never resolve twice.
163
+ */
164
+ warm: () => Promise<WarmSummary>;
123
165
  }
124
166
  /**
125
- * Build the local stdio-facing MCP server that proxies to the remote BattleGrid
126
- * server. Discovers remote capabilities through the primary client, augments
127
- * tools with the `account` enum when multiple accounts resolved, and wires
128
- * strip-only-account routing to a lazily-populated per-key client pool.
167
+ * Build the local stdio-facing MCP server that proxies to the remote BattleGrid server.
168
+ *
169
+ * ORDERING IS THE POINT. Exactly one upstream operation happens here: the handshake that yields the
170
+ * identity this proxy relays. `serverInfo` is captured by the SDK at `Server` construction and read
171
+ * when `initialize` is answered, with no setter, so relaying the upstream's identity verbatim makes
172
+ * one round trip inherent. Everything else — identity discovery and the three catalog calls —
173
+ * resolves lazily, after the caller has bound the transport, because none of it is needed to answer
174
+ * `initialize` and every unit of work placed ahead of the bind is paid by every client on every
175
+ * start.
129
176
  */
130
177
  export declare function createProxyServer(options: CreateProxyServerOptions): Promise<ProxyServer>;
131
178
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,oCAAoC,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,WAAW,qCAAqC,CAAC;AAM9D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAID,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CA+B9E;AAID,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,EAAE,CAAC,CA6C5B;AA6DD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAgBpG;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,cAAc,GAAG,SAAS,GAAG,cAAc,CAS1F;AAID,MAAM,WAAW,wBAAwB;IACvC,gGAAgG;IAChG,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,+FAA+F;IAC/F,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kGAAkG;IAClG,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,WAAW,CAAC,CAmI/F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -52,10 +52,16 @@ import { ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema
52
52
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
53
53
  * correction. Never move it to track the server.
54
54
  */
55
- export const PACKAGE_VERSION = '31.1.0';
55
+ export const PACKAGE_VERSION = '31.2.0';
56
56
  export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
57
57
  const MAX_RETRIES = 3;
58
58
  const RETRY_DELAYS_MS = [2000, 4000, 8000];
59
+ // One rule, two expressions. Asserted here so the delay table genuinely bounds the attempt count,
60
+ // which is what lets connectWithRetry index it without a fallback.
61
+ if (RETRY_DELAYS_MS.length !== MAX_RETRIES) {
62
+ throw new Error(`Retry budget disagrees with itself: MAX_RETRIES=${MAX_RETRIES} but RETRY_DELAYS_MS carries ` +
63
+ `${RETRY_DELAYS_MS.length} delays. Reconcile them at their declaration.`);
64
+ }
59
65
  // --- Environment validation (exported for testing) ---
60
66
  export function validateEnv(env) {
61
67
  const apiUrl = env.BATTLEGRID_API_URL || DEFAULT_URL;
@@ -133,7 +139,8 @@ function isAuthError(error) {
133
139
  function sleep(ms) {
134
140
  return new Promise((resolve) => setTimeout(resolve, ms));
135
141
  }
136
- async function connectWithRetry(client, transport, apiUrl) {
142
+ async function connectWithRetry(client, transport, apiUrl, apiKey) {
143
+ const keyPrefix = apiKey.substring(0, 12);
137
144
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
138
145
  try {
139
146
  await client.connect(transport);
@@ -146,7 +153,7 @@ async function connectWithRetry(client, transport, apiUrl) {
146
153
  : undefined;
147
154
  const errorMessage = error instanceof Error ? error.message : String(error);
148
155
  if (isAuthError(error)) {
149
- throw new Error(`Authentication failed (HTTP ${errorCode ?? 'unknown'}): ${errorMessage}\n` +
156
+ throw new Error(`Authentication failed for key ${keyPrefix} (HTTP ${errorCode ?? 'unknown'}): ${errorMessage}\n` +
150
157
  'This means the server rejected your API key. Possible causes:\n' +
151
158
  ' 1. Key was revoked (a new key invalidates all previous keys)\n' +
152
159
  ' 2. Key was corrupted during copy-paste\n' +
@@ -154,12 +161,13 @@ async function connectWithRetry(client, transport, apiUrl) {
154
161
  'Create a new key at: https://battlegrid.trade → Profile → MCP tab');
155
162
  }
156
163
  if (attempt === MAX_RETRIES) {
157
- throw new Error(`Cannot connect to BattleGrid server at ${apiUrl} after ${MAX_RETRIES + 1} attempts.\n` +
164
+ throw new Error(`Cannot connect to BattleGrid server at ${apiUrl} with key ${keyPrefix} after ` +
165
+ `${MAX_RETRIES + 1} attempts.\n` +
158
166
  `Last error: ${errorMessage}\n` +
159
167
  `Check your internet connection or verify the server is running.\n` +
160
168
  `Health check: ${apiUrl.replace('/mcp', '')}/health`);
161
169
  }
162
- const delay = RETRY_DELAYS_MS[attempt] ?? 8000;
170
+ const delay = RETRY_DELAYS_MS[attempt];
163
171
  process.stderr.write(`Connection attempt ${attempt + 1} failed (${errorMessage}), retrying in ${delay / 1000}s...\n`);
164
172
  await sleep(delay);
165
173
  }
@@ -222,64 +230,153 @@ export function announcedIdentityOf(serverInfo) {
222
230
  return serverInfo;
223
231
  }
224
232
  /**
225
- * Build the local stdio-facing MCP server that proxies to the remote BattleGrid
226
- * server. Discovers remote capabilities through the primary client, augments
227
- * tools with the `account` enum when multiple accounts resolved, and wires
228
- * strip-only-account routing to a lazily-populated per-key client pool.
233
+ * Build the local stdio-facing MCP server that proxies to the remote BattleGrid server.
234
+ *
235
+ * ORDERING IS THE POINT. Exactly one upstream operation happens here: the handshake that yields the
236
+ * identity this proxy relays. `serverInfo` is captured by the SDK at `Server` construction and read
237
+ * when `initialize` is answered, with no setter, so relaying the upstream's identity verbatim makes
238
+ * one round trip inherent. Everything else — identity discovery and the three catalog calls —
239
+ * resolves lazily, after the caller has bound the transport, because none of it is needed to answer
240
+ * `initialize` and every unit of work placed ahead of the bind is paid by every client on every
241
+ * start.
229
242
  */
230
243
  export async function createProxyServer(options) {
231
- const { identities, connect } = options;
232
- const isMultiAccount = identities.length > 1;
233
- const accountNames = identities.map(id => id.username);
234
- // Build lookup: username → apiKey
235
- const keyByAccount = new Map();
236
- for (const id of identities) {
237
- keyByAccount.set(id.username, id.apiKey);
238
- }
239
- // Primary client (first key) is used for capability discovery and non-tool proxying.
240
- const primaryKey = identities[0].apiKey;
244
+ const { primaryKey, isMultiAccount, connect, resolveIdentities } = options;
245
+ // The one retained pre-bind round trip.
241
246
  const primaryClient = await connect(primaryKey);
242
247
  // What this proxy announces downstream is what the upstream just announced to it — read here,
243
248
  // from the completed handshake, so it can never be a stale constant.
244
249
  const announced = announcedIdentityOf(primaryClient.getServerVersion());
245
- // Discover remote capabilities
246
- const [toolsResult, promptsResult, resourcesResult] = await Promise.all([
247
- primaryClient.listTools(),
248
- primaryClient.listPrompts(),
249
- primaryClient.listResources(),
250
- ]);
251
- // Augment tools with account parameter if multi-account
252
- const exposedTools = isMultiAccount
253
- ? injectAccountParam(toolsResult.tools, accountNames)
254
- : toolsResult.tools;
255
- // Create local stdio server, announcing the upstream's identity rather than one of our own.
250
+ // `listChanged` is declared HERE and can never be added later: registerCapabilities throws once a
251
+ // transport is connected, and a client only registers list-changed handlers for capabilities the
252
+ // server advertised. Without it, a client whose first tools/list failed holds an empty catalog for
253
+ // the life of the process — the symptom this lazy catalog exists to remove, made silent.
256
254
  const localServer = new Server(announced, {
257
255
  capabilities: {
258
- tools: {},
259
- prompts: {},
260
- resources: {},
256
+ tools: { listChanged: true },
257
+ prompts: { listChanged: true },
258
+ resources: { listChanged: true },
261
259
  },
262
260
  });
263
- // Keep a pool of remote clients keyed by API key for routing
261
+ // Pool of PROMISES, not clients: concurrent callers for one key share one in-flight connect
262
+ // rather than each building a transport that is never closed. A rejection is removed so the next
263
+ // caller retries.
264
264
  const clientPool = new Map();
265
- clientPool.set(primaryKey, primaryClient);
266
- async function getClientForKey(apiKey) {
265
+ clientPool.set(primaryKey, Promise.resolve(primaryClient));
266
+ function getClientForKey(apiKey) {
267
267
  const existing = clientPool.get(apiKey);
268
268
  if (existing)
269
269
  return existing;
270
- const client = await connect(apiKey);
271
- clientPool.set(apiKey, client);
272
- return client;
270
+ const pending = connect(apiKey);
271
+ clientPool.set(apiKey, pending);
272
+ pending.catch(() => {
273
+ if (clientPool.get(apiKey) === pending)
274
+ clientPool.delete(apiKey);
275
+ });
276
+ return pending;
277
+ }
278
+ let catalogMemo = null;
279
+ let servedCatalogError = false;
280
+ async function announceListsChanged() {
281
+ try {
282
+ await Promise.all([
283
+ localServer.sendToolListChanged(),
284
+ localServer.sendPromptListChanged(),
285
+ localServer.sendResourceListChanged(),
286
+ ]);
287
+ }
288
+ catch (error) {
289
+ const message = error instanceof Error ? error.message : String(error);
290
+ process.stderr.write(`Warning: could not notify client that the catalog recovered: ${message}\n`);
291
+ }
292
+ }
293
+ async function buildCatalog() {
294
+ const identities = await resolveIdentities();
295
+ // The declared primary must be in the resolved roster. Identity discovery is a single unretried
296
+ // request while the handshake retries three times, so a primary can complete the handshake and
297
+ // still be missing here — which would serve prompts and resources as an account that appears in
298
+ // no selector and no log line.
299
+ if (!identities.some((identity) => identity.apiKey === primaryKey)) {
300
+ throw new Error(`Primary key (${primaryKey.substring(0, 12)}) is absent from the resolved account roster, ` +
301
+ 'so every call routed to it would execute as an account this proxy never resolved.');
302
+ }
303
+ const accountNames = identities.map((identity) => identity.username);
304
+ const keyByAccount = new Map();
305
+ for (const identity of identities) {
306
+ if (keyByAccount.has(identity.username)) {
307
+ throw new Error(`Two configured accounts resolved to the display name "${identity.username}", so an ` +
308
+ 'account selection cannot name one of them unambiguously.');
309
+ }
310
+ keyByAccount.set(identity.username, identity.apiKey);
311
+ }
312
+ const [toolsResult, promptsResult, resourcesResult] = await Promise.all([
313
+ primaryClient.listTools(),
314
+ primaryClient.listPrompts(),
315
+ primaryClient.listResources(),
316
+ ]);
317
+ const discovered = toolsResult.tools;
318
+ return {
319
+ identities,
320
+ accountNames,
321
+ keyByAccount,
322
+ tools: isMultiAccount ? injectAccountParam(discovered, accountNames) : discovered,
323
+ prompts: promptsResult.prompts,
324
+ resources: resourcesResult.resources,
325
+ };
326
+ }
327
+ /**
328
+ * The catalog, resolved at most once and shared by every caller.
329
+ *
330
+ * The clear-on-failure is identity-guarded deliberately. The obvious shape — an async IIFE whose
331
+ * inner catch nulls the memo — poisons it permanently when the body throws before its first
332
+ * `await`: that catch runs during the synchronous prefix and the outer assignment then
333
+ * re-installs the rejected promise. Guarding on identity is correct for both timings and also
334
+ * cannot discard a newer resolution another request installed.
335
+ */
336
+ function resolveCatalog() {
337
+ const existing = catalogMemo;
338
+ if (existing)
339
+ return existing;
340
+ const pending = buildCatalog();
341
+ catalogMemo = pending;
342
+ pending.catch(() => {
343
+ if (catalogMemo === pending)
344
+ catalogMemo = null;
345
+ });
346
+ void pending.then(() => {
347
+ if (!servedCatalogError)
348
+ return;
349
+ servedCatalogError = false;
350
+ void announceListsChanged();
351
+ }, () => undefined);
352
+ return pending;
353
+ }
354
+ /** Resolve the catalog, remembering that a failure was served so a later recovery can be announced. */
355
+ async function catalogOrThrow() {
356
+ try {
357
+ return await resolveCatalog();
358
+ }
359
+ catch (error) {
360
+ servedCatalogError = true;
361
+ throw error;
362
+ }
273
363
  }
274
364
  // --- Proxy: tools ---
275
- localServer.setRequestHandler(ListToolsRequestSchema, async () => ({
276
- tools: exposedTools,
277
- }));
365
+ // Throws on rejection: ListToolsResult carries only `tools`, so the tools/call in-band
366
+ // `{content, isError}` shape would ship as a SUCCESSFUL result that fails the client's own parse.
367
+ // The SDK maps a thrown handler error to a proper JSON-RPC error.
368
+ localServer.setRequestHandler(ListToolsRequestSchema, async () => {
369
+ const catalog = await catalogOrThrow();
370
+ return { tools: catalog.tools };
371
+ });
278
372
  localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
279
373
  try {
280
374
  let targetKey = primaryKey;
281
375
  const args = { ...request.params.arguments };
376
+ // Read from the constructor option, never from the catalog: the guard must be armed even
377
+ // while the account set is still unknown.
282
378
  if (isMultiAccount) {
379
+ const { keyByAccount, accountNames } = await catalogOrThrow();
283
380
  const selectedAccount = args.account;
284
381
  if (!selectedAccount || !keyByAccount.has(selectedAccount)) {
285
382
  return {
@@ -308,9 +405,10 @@ export async function createProxyServer(options) {
308
405
  }
309
406
  });
310
407
  // --- Proxy: prompts ---
311
- localServer.setRequestHandler(ListPromptsRequestSchema, async () => ({
312
- prompts: promptsResult.prompts,
313
- }));
408
+ localServer.setRequestHandler(ListPromptsRequestSchema, async () => {
409
+ const catalog = await catalogOrThrow();
410
+ return { prompts: catalog.prompts };
411
+ });
314
412
  localServer.setRequestHandler(GetPromptRequestSchema, async (request) => {
315
413
  return await primaryClient.getPrompt({
316
414
  name: request.params.name,
@@ -318,9 +416,10 @@ export async function createProxyServer(options) {
318
416
  });
319
417
  });
320
418
  // --- Proxy: resources ---
321
- localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({
322
- resources: resourcesResult.resources,
323
- }));
419
+ localServer.setRequestHandler(ListResourcesRequestSchema, async () => {
420
+ const catalog = await catalogOrThrow();
421
+ return { resources: catalog.resources };
422
+ });
324
423
  localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
325
424
  return await primaryClient.readResource({
326
425
  uri: request.params.uri,
@@ -328,12 +427,16 @@ export async function createProxyServer(options) {
328
427
  });
329
428
  return {
330
429
  server: localServer,
331
- toolCount: toolsResult.tools.length,
332
- promptCount: promptsResult.prompts.length,
333
- resourceCount: resourcesResult.resources.length,
334
- isMultiAccount,
335
- accountNames,
336
430
  announced,
431
+ warm: async () => {
432
+ const catalog = await resolveCatalog();
433
+ return {
434
+ accountNames: catalog.accountNames,
435
+ toolCount: catalog.tools.length,
436
+ promptCount: catalog.prompts.length,
437
+ resourceCount: catalog.resources.length,
438
+ };
439
+ },
337
440
  };
338
441
  }
339
442
  // --- Main ---
@@ -352,27 +455,25 @@ async function main() {
352
455
  const hashPrefix = createHash('sha256').update(key).digest('hex').substring(0, 16);
353
456
  process.stderr.write(`BattleGrid MCP: Key #${i + 1} prefix=${key.substring(0, 12)} hashPrefix=${hashPrefix} len=${key.length}\n`);
354
457
  }
355
- // Discover identities for all keys
356
- let identities;
357
- try {
358
- identities = await discoverIdentities(config.apiKeys, config.apiUrl);
359
- }
360
- catch (error) {
361
- process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
362
- process.exit(1);
363
- }
364
- process.stderr.write(`BattleGrid MCP: ${identities.length} account(s) discovered — ${identities.map(id => id.username).join(', ')}\n`);
365
- // Lazily connect a remote MCP client per API key, with retry + auth diagnostics.
458
+ // Connect a remote MCP client for a given API key, with retry + auth diagnostics.
366
459
  const connect = async (apiKey) => {
367
460
  const transport = new StreamableHTTPClientTransport(new URL(config.apiUrl), { requestInit: { headers: { Authorization: `Bearer ${apiKey}` } } });
368
461
  // Upstream is told which proxy build is calling — the one question PACKAGE_VERSION answers.
369
462
  const client = new Client({ name: 'battlegrid-proxy', version: PACKAGE_VERSION }, { capabilities: {} });
370
- await connectWithRetry(client, transport, config.apiUrl);
463
+ await connectWithRetry(client, transport, config.apiUrl, apiKey);
371
464
  return client;
372
465
  };
373
466
  let proxy;
374
467
  try {
375
- proxy = await createProxyServer({ identities, connect });
468
+ proxy = await createProxyServer({
469
+ // The operator's declared first key, chosen before discovery so an unreachable one fails
470
+ // loudly here instead of silently promoting the second.
471
+ primaryKey: config.apiKeys[0],
472
+ // Armed by configuration, so a later identity failure cannot stand the account guard down.
473
+ isMultiAccount: config.apiKeys.length > 1,
474
+ connect,
475
+ resolveIdentities: () => discoverIdentities(config.apiKeys, config.apiUrl),
476
+ });
376
477
  }
377
478
  catch (error) {
378
479
  process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -383,11 +484,25 @@ async function main() {
383
484
  // unnoticed for ten days.
384
485
  process.stderr.write(`BattleGrid MCP: announcing ${proxy.announced.name}@${proxy.announced.version} ` +
385
486
  `(server contract, read from the upstream handshake) — proxy ${PACKAGE_VERSION}\n`);
386
- process.stderr.write(`BattleGrid MCP: ${proxy.toolCount} tools, ${proxy.promptCount} prompts, ${proxy.resourceCount} resources\n`);
387
- // --- Start stdio transport ---
487
+ // --- Start stdio transport, BEFORE any catalog work ---
488
+ //
489
+ // Everything above is the single upstream handshake the relayed serverInfo requires. From here the
490
+ // process answers `initialize`; identity discovery and the three catalog calls happen behind it.
388
491
  const stdioTransport = new StdioServerTransport();
389
492
  await proxy.server.connect(stdioTransport);
390
493
  process.stderr.write('BattleGrid MCP server running on stdio\n');
494
+ // Warm the catalog so the first tools/list joins an in-flight resolution rather than starting one.
495
+ // Deliberately NOT awaited, and the rejection handler is the whole of the guarantee that a
496
+ // background failure cannot kill a process that has already answered `initialize`. The failure is
497
+ // reported and absorbed; the next request that needs the catalog retries it.
498
+ void proxy.warm().then((summary) => {
499
+ process.stderr.write(`BattleGrid MCP: ${summary.accountNames.length} account(s) discovered — ${summary.accountNames.join(', ')}\n`);
500
+ process.stderr.write(`BattleGrid MCP: ${summary.toolCount} tools, ${summary.promptCount} prompts, ${summary.resourceCount} resources\n`);
501
+ }, (error) => {
502
+ const message = error instanceof Error ? error.message : String(error);
503
+ process.stderr.write(`Warning: catalog warm-up failed (${message}). The proxy is connected; the next request ` +
504
+ 'that needs the catalog will retry it.\n');
505
+ });
391
506
  }
392
507
  // Only run when executed directly (not when imported for testing)
393
508
  const isDirectExecution = process.argv[1] &&
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,GAI1B,MAAM,oCAAoC,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,kCAAkC,CAAC;AAC9D,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAE3C,kGAAkG;AAClG,mEAAmE;AACnE,IAAI,eAAe,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;IAC3C,MAAM,IAAI,KAAK,CACb,mDAAmD,WAAW,+BAA+B;QAC7F,GAAG,eAAe,CAAC,MAAM,+CAA+C,CACzE,CAAC;AACJ,CAAC;AAgBD,wDAAwD;AAExD,MAAM,UAAU,WAAW,CAAC,GAAuC;IACjE,MAAM,MAAM,GAAG,GAAG,CAAC,kBAAkB,IAAI,WAAW,CAAC;IAErD,yDAAyD;IACzD,MAAM,OAAO,GAAG,GAAG,CAAC,mBAAmB,CAAC;IACxC,IAAI,OAAiB,CAAC;IAEtB,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,MAAM,SAAS,GAAG,GAAG,CAAC,kBAAkB,CAAC;QACzC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,+EAA+E;YAC/E,mEAAmE,CACpE,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,4CAA4C,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS;gBACrE,mEAAmE,CACpE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC;AAED,oDAAoD;AAEpD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAiB,EACjB,MAAc;IAEd,kDAAkD;IAClD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAE3C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,WAAW,EAAE;YAClD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;SAC/C,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA0E,CAAC;QAC3G,OAAO;YACL,MAAM;YACN,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YAClD,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACE,CAAC;IAC9B,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,UAAU,GAAsB,EAAE,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9F,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+CAA+C,CAAC,GAAG,CAAC,KAAK,SAAS,MAAM,MAAM,IAAI,CACnF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,oDAAoD;YACpD,kEAAkE,CACnE,CAAC;IACJ,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gCAAgC;AAEhC,SAAS,WAAW,CAAC,KAAc;IACjC,iFAAiF;IACjF,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAI,KAA2B,CAAC,IAAI,CAAC;QAC/C,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;IAChD,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAChG,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,SAAwC,EAAE,MAAc,EAAE,MAAc;IACtH,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1C,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,wCAAwC;YACxC,MAAM,SAAS,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK;gBACrE,CAAC,CAAE,KAA2B,CAAC,IAAI;gBACnC,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE5E,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CACb,iCAAiC,SAAS,UAAU,SAAS,IAAI,SAAS,MAAM,YAAY,IAAI;oBAChG,iEAAiE;oBACjE,kEAAkE;oBAClE,4CAA4C;oBAC5C,kEAAkE;oBAClE,mEAAmE,CACpE,CAAC;YACJ,CAAC;YAED,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CACb,0CAA0C,MAAM,aAAa,SAAS,SAAS;oBAC/E,GAAG,WAAW,GAAG,CAAC,cAAc;oBAChC,eAAe,YAAY,IAAI;oBAC/B,mEAAmE;oBACnE,iBAAiB,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CACrD,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;YACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,sBAAsB,OAAO,GAAG,CAAC,YAAY,YAAY,kBAAkB,KAAK,GAAG,IAAI,QAAQ,CAChG,CAAC;YACF,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;AACH,CAAC;AAeD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAuB,EAAE,YAAsB;IAChF,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxB,GAAG,IAAI;QACP,WAAW,EAAE;YACX,GAAG,IAAI,CAAC,WAAW;YACnB,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,YAAY;oBAClB,WAAW,EAAE,+DAA+D,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;iBACtG;gBACD,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU;aAC/B;YACD,QAAQ,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;SAC5D;KACF,CAAC,CAAC,CAAC;AACN,CAAC;AAED,oDAAoD;AAEpD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAsC;IACxE,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,yFAAyF;YACzF,2FAA2F;YAC3F,wFAAwF,CACzF,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAkED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAiC;IACvE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC;IAE3E,wCAAwC;IACxC,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IAEhD,8FAA8F;IAC9F,qEAAqE;IACrE,MAAM,SAAS,GAAG,mBAAmB,CAAC,aAAa,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAExE,kGAAkG;IAClG,iGAAiG;IACjG,mGAAmG;IACnG,yFAAyF;IACzF,MAAM,WAAW,GAAG,IAAI,MAAM,CAC5B,SAAS,EACT;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;YAC5B,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;YAC9B,SAAS,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;SACjC;KACF,CACF,CAAC;IAEF,4FAA4F;IAC5F,iGAAiG;IACjG,kBAAkB;IAClB,MAAM,UAAU,GAAG,IAAI,GAAG,EAA2B,CAAC;IACtD,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;IAE3D,SAAS,eAAe,CAAC,MAAc;QACrC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAChC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;YACjB,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,OAAO;gBAAE,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,WAAW,GAAiC,IAAI,CAAC;IACrD,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAE/B,KAAK,UAAU,oBAAoB;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,GAAG,CAAC;gBAChB,WAAW,CAAC,mBAAmB,EAAE;gBACjC,WAAW,CAAC,qBAAqB,EAAE;gBACnC,WAAW,CAAC,uBAAuB,EAAE;aACtC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gEAAgE,OAAO,IAAI,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IAED,KAAK,UAAU,YAAY;QACzB,MAAM,UAAU,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAE7C,gGAAgG;QAChG,+FAA+F;QAC/F,gGAAgG;QAChG,+BAA+B;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,KAAK,CACb,gBAAgB,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,gDAAgD;gBAC3F,mFAAmF,CACpF,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC/C,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YAClC,IAAI,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACxC,MAAM,IAAI,KAAK,CACb,yDAAyD,QAAQ,CAAC,QAAQ,WAAW;oBACrF,0DAA0D,CAC3D,CAAC;YACJ,CAAC;YACD,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,CAAC,WAAW,EAAE,aAAa,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACtE,aAAa,CAAC,SAAS,EAAE;YACzB,aAAa,CAAC,WAAW,EAAE;YAC3B,aAAa,CAAC,aAAa,EAAE;SAC9B,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,WAAW,CAAC,KAAyB,CAAC;QAEzD,OAAO;YACL,UAAU;YACV,YAAY;YACZ,YAAY;YACZ,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,UAAU;YACjF,OAAO,EAAE,aAAa,CAAC,OAAO;YAC9B,SAAS,EAAE,eAAe,CAAC,SAAS;SACrC,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,SAAS,cAAc;QACrB,MAAM,QAAQ,GAAG,WAAW,CAAC;QAC7B,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;QAC/B,WAAW,GAAG,OAAO,CAAC;QAEtB,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;YACjB,IAAI,WAAW,KAAK,OAAO;gBAAE,WAAW,GAAG,IAAI,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,KAAK,OAAO,CAAC,IAAI,CACf,GAAG,EAAE;YACH,IAAI,CAAC,kBAAkB;gBAAE,OAAO;YAChC,kBAAkB,GAAG,KAAK,CAAC;YAC3B,KAAK,oBAAoB,EAAE,CAAC;QAC9B,CAAC,EACD,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;QAEF,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,uGAAuG;IACvG,KAAK,UAAU,cAAc;QAC3B,IAAI,CAAC;YACH,OAAO,MAAM,cAAc,EAAE,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,kBAAkB,GAAG,IAAI,CAAC;YAC1B,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,uBAAuB;IAEvB,uFAAuF;IACvF,kGAAkG;IAClG,kEAAkE;IAClE,WAAW,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACrE,IAAI,CAAC;YACH,IAAI,SAAS,GAAG,UAAU,CAAC;YAC3B,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAA6B,CAAC;YAExE,yFAAyF;YACzF,0CAA0C;YAC1C,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC;gBAC9D,MAAM,eAAe,GAAG,IAAI,CAAC,OAA6B,CAAC;gBAC3D,IAAI,CAAC,eAAe,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;oBAC3D,OAAO;wBACL,OAAO,EAAE,CAAC;gCACR,IAAI,EAAE,MAAe;gCACrB,IAAI,EAAE,0DAA0D,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;6BAC1F,CAAC;wBACF,OAAO,EAAE,IAAI;qBACd,CAAC;gBACJ,CAAC;gBACD,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,eAAe,CAAE,CAAC;gBAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,uEAAuE;YAC9F,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;YAChD,OAAO,MAAM,MAAM,CAAC,QAAQ,CAAC;gBAC3B,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;gBACzB,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;gBAC/D,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,yBAAyB;IAEzB,WAAW,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACtE,OAAO,MAAM,aAAa,CAAC,SAAS,CAAC;YACnC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;YACzB,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS;SACpC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,2BAA2B;IAE3B,WAAW,CAAC,iBAAiB,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,iBAAiB,CAAC,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACzE,OAAO,MAAM,aAAa,CAAC,YAAY,CAAC;YACtC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG;SACxB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,WAAW;QACnB,SAAS;QACT,IAAI,EAAE,KAAK,IAA0B,EAAE;YACrC,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;YACvC,OAAO;gBACL,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;gBAC/B,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM;gBACnC,aAAa,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM;aACxC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,eAAe;AAEf,KAAK,UAAU,IAAI;IACjB,IAAI,MAAiB,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,6DAA6D;IAC7D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wBAAwB,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,UAAU,QAAQ,GAAG,CAAC,MAAM,IAAI,CAC5G,CAAC;IACJ,CAAC;IAED,kFAAkF;IAClF,MAAM,OAAO,GAAG,KAAK,EAAE,MAAc,EAAmB,EAAE;QACxD,MAAM,SAAS,GAAG,IAAI,6BAA6B,CACjD,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EACtB,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE,EAAE,EAAE,CACpE,CAAC;QACF,4FAA4F;QAC5F,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,eAAe,EAAE,EACtD,EAAE,YAAY,EAAE,EAAE,EAAE,CACrB,CAAC;QACF,MAAM,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjE,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,IAAI,KAAkB,CAAC;IACvB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,iBAAiB,CAAC;YAC9B,yFAAyF;YACzF,wDAAwD;YACxD,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC7B,2FAA2F;YAC3F,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YACzC,OAAO;YACP,iBAAiB,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;SAC3E,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,+FAA+F;IAC/F,8FAA8F;IAC9F,0BAA0B;IAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8BAA8B,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,GAAG;QAChF,+DAA+D,eAAe,IAAI,CACnF,CAAC;IAEF,yDAAyD;IACzD,EAAE;IACF,mGAAmG;IACnG,iGAAiG;IAEjG,MAAM,cAAc,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAClD,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAE3C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAEjE,mGAAmG;IACnG,2FAA2F;IAC3F,kGAAkG;IAClG,6EAA6E;IAC7E,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CACpB,CAAC,OAAO,EAAE,EAAE;QACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mBAAmB,OAAO,CAAC,YAAY,CAAC,MAAM,4BAA4B,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAC9G,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mBAAmB,OAAO,CAAC,SAAS,WAAW,OAAO,CAAC,WAAW,aAAa,OAAO,CAAC,aAAa,cAAc,CACnH,CAAC;IACJ,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;QACjB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oCAAoC,OAAO,8CAA8C;YACzF,yCAAyC,CAC1C,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAGD,kEAAkE;AAClE,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAEzF,IAAI,iBAAiB,EAAE,CAAC;IACtB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,KAAK,IAAI,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@battlegrid/mcp-server",
3
- "version": "31.1.0",
3
+ "version": "31.2.0",
4
4
  "description": "BattleGrid MCP server — play crypto prediction games from AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -88,14 +88,22 @@ values instead of the evaluated coin's — the standard way to gate a whole book
88
88
  regime. `benchmarkTicker` is required-nullable on every custom section: send `null` for an
89
89
  ordinary section, never omit it.
90
90
 
91
+ A bound section takes **indicator** columns only. Crowd/session metrics (the `CROWD_*` family,
92
+ `FLOW_ALIGN`, `SMART_RETAIL`, `CAPTAIN_CONF`, `CONFIDENCE`, `SETTLED_AT`, `PERP_SPOT_*`) and any
93
+ `rank` transform are refused there (contract 49), because both are defined relative to the cohort
94
+ being evaluated and a benchmark sits outside it — a crowd reading is what this session's players
95
+ did, a rank is a position among the coins under evaluation. Neither has a value for BTC-as-yardstick.
96
+ Put those columns on an ordinary section and `conditionRef` across.
97
+
91
98
  Budgets are served by discovery (validated today: 32 sections, 32 custom columns, 8 distinct
92
99
  timeframes, 16 conditions, 16 clauses, 16k estimated report tokens). `preview_strategy_report`
93
100
  echoes your usage against each cap.
94
101
 
95
102
  ## Conditions — deterministic logic over your own report
96
103
 
97
- Each condition: `{ conditionKey, name, definition, verdict, required }` — all five required;
98
- `verdict` and `required` have no defaults.
104
+ Each condition: `{ conditionKey, name, definition, verdict, required, exit, clock, closes }` —
105
+ all eight required, no defaults. The conditions axis saves by whole-set replacement, so an omitted
106
+ key would silently un-clock a money gate on the next unrelated edit rather than be refused.
99
107
 
100
108
  - **Clauses** compare one column: numeric/rank headers take `lt|lte|gte|gt|between`;
101
109
  classification/direction/event headers take `is|in` with the column's exact vocabulary
@@ -106,8 +114,11 @@ Each condition: `{ conditionKey, name, definition, verdict, required }` — all
106
114
  rejected, forward references are legal.
107
115
  - **Column addressing**: `{sectionKey, header}`. `sectionKey: null` is authoring sugar for a
108
116
  header unique across the whole report; a duplicated header (e.g. `ADX` in two sections) must
109
- be section-qualified. To reference your own custom sections at CREATE time, mint the
110
- `custom:<uuid>` sectionKey yourself and reuse it in the clauses.
117
+ be section-qualified. On a **CREATE you omit `sectionKey` entirely** — the server derives it
118
+ from the section itself, so the same submitted section yields the same key on every compile, and
119
+ minting a `custom:<uuid>` yourself is refused. On an UPDATE or RESTORE, send back the keys
120
+ `get_strategy` returned. To qualify a duplicated header, read the key from a preview's
121
+ `conditionColumns` or from the candidates a `CONDITION_COLUMN_AMBIGUOUS` refusal offers.
111
122
  - **Verdict**: `UP` | `DOWN` | `NEITHER` on deciding conditions, explicit `null` on building
112
123
  blocks. Declaration order is precedence: the first TRUE condition with a non-null verdict
113
124
  decides the coin's verdict. Put the more specific carrier first.
@@ -116,6 +127,48 @@ Each condition: `{ conditionKey, name, definition, verdict, required }` — all
116
127
  use it for liquidity floors, regime vetoes, and "never fight the HTF" rules.
117
128
  - Evaluation is three-valued: `UNRESOLVED` (missing input) is never collapsed to FALSE, and
118
129
  outcomes read from a still-forming bar are marked provisional.
130
+ - **The evidence clock.** `clock: "LIVE"` reads the forming bar; `clock: "CLOSE"` reads settled
131
+ bars, and `closes` (1–5) is how many consecutive closed bars must read TRUE — always `1` under
132
+ LIVE, which has exactly one frame. A CLOSE clock is legal **only** over a header resolved from
133
+ the coin's own candle series at offset 0; frame-inert operands (perp-payload scalars, published
134
+ rolling changes, ranks, zone entities, regime labels, enrichment metrics, session scalars) are
135
+ refused with `CONDITION_CLOCK_OPERAND_ILLEGAL`. The remedy is a split, not a re-clock: move that
136
+ clause into its own LIVE condition and `conditionRef` it.
137
+ - **The exit role.** `exit: true` makes a settled TRUE reading close open positions its verdict
138
+ opposes — UP exits SHORTs, DOWN exits LONGs, a `NEITHER` or `null` verdict exits both. Legal only
139
+ under `clock: "CLOSE"`, because an exit fired on a forming bar is an intrabar exit. Orthogonal to
140
+ `required`: the two act on disjoint lifecycles, pre-entry versus open.
141
+
142
+ ## Entry — when, and where, an entry is taken
143
+
144
+ `{ trigger, confirmTf, closes, bandAtrMultiple, levelSource, levelOffsetAtrMultiple, validForBars }`
145
+ — all seven required on every CREATE, no defaults. Replaced WHOLE on save, so an omitted key
146
+ reverts an author's discipline silently rather than being refused.
147
+
148
+ | `trigger` | What it does |
149
+ |---|---|
150
+ | `AT_SIGNAL` | Fire on the qualification flip, at whatever bar is on the tape. The platform's flat wall-clock entry window applies. |
151
+ | `ON_CANDLE_CLOSE` | The flip ARMS the pair; entry is taken only after a `confirmTf` close that still reads the conditions true and has not displaced beyond the band. |
152
+ | `STOP_THROUGH_LEVEL` | A TRIGGER order rests at the level ± offset; the exchange book is the watcher. |
153
+ | `ON_RETEST` | A LIMIT order rests at the broken level. Not filling is a correct outcome, not a failure. |
154
+
155
+ - `confirmTf` — exactly two values are legal: the strategy's own timeframe and the rung below it
156
+ (one only, at the ladder floor). Not the authorable main-candle set.
157
+ - `closes` — 1–5 consecutive confirming closes. Must be `1` under any trigger but `ON_CANDLE_CLOSE`.
158
+ - `bandAtrMultiple` — the veto width. The entry is VOIDED when the confirming close moved at or
159
+ beyond that many ATR against the armed verdict. Strictly `> 0` (zero voids on any adverse move,
160
+ which is a filter, not "off"), and at or below the platform's own entry-deviation gate — read it
161
+ from `get_trading_config_catalog`.
162
+ - `levelSource` — `SWING_HIGH` | `SWING_LOW` | `BOLLINGER_UPPER` | `BOLLINGER_LOWER`, resolved once
163
+ at decision time.
164
+ - `levelOffsetAtrMultiple` — 0–2, UNSIGNED. Direction is implied by the trigger and the verdict, so
165
+ a signed value would invert the trigger's meaning. Must be `0` under a non-level trigger.
166
+ - `validForBars` — 1–24 of the strategy's OWN bars, not minutes: a 1h setup waiting for a retest has
167
+ not failed after fifteen minutes.
168
+
169
+ The legality matrix runs **one way**: all seven keys are always present, so the question is never
170
+ "is it set" but "is it set to something that MEANS anything under this trigger". Leave a dial at its
171
+ inert value rather than setting one the platform will ignore.
119
172
 
120
173
  ## Signal weights — the scorecard is a weighted average, budget it
121
174
 
@@ -54,10 +54,16 @@ before any billing or LLM call:
54
54
 
55
55
  ```json
56
56
  { "conditionKey": "NO_EVENT_RISK", "name": "No liquidation tape", "verdict": null, "required": true,
57
+ "exit": false, "clock": "LIVE", "closes": 1,
57
58
  "definition": { "kind": "group", "op": "NOT", "members": [
58
59
  { "kind": "clause", "column": { "sectionKey": null, "header": "oiRegime" }, "op": "is", "label": "long liquidation" } ] } }
59
60
  ```
60
61
 
62
+ All eight keys are required on every condition — `exit`, `clock` and `closes` included. `clock`
63
+ selects the evidence frame (`LIVE` reads the forming bar, `CLOSE` reads settled bars) and `closes`
64
+ (1–5) is how many consecutive closed bars must hold; `closes` is always `1` under `LIVE`. `exit:
65
+ true` closes open positions the verdict opposes and is legal only under `clock: "CLOSE"`.
66
+
61
67
  **Quorum (N_OF).** Robust confirmation instead of a brittle ALL:
62
68
 
63
69
  ```json
@@ -72,11 +78,42 @@ directional carriers; the first TRUE carrier **in declaration order** decides
72
78
  most-specific first.
73
79
 
74
80
  **Benchmark gate.** Read regime off a `benchmarkTicker` section (all rows are the benchmark's
75
- values) and `conditionRef` it from every carrier — one place to flip the book risk-on/off.
81
+ values) and `conditionRef` it from every carrier — one place to flip the book risk-on/off. Keep that
82
+ section to indicators: crowd/session metrics and `rank` transforms are refused on a bound section
83
+ (contract 49) — rank the coins on an ordinary section and `conditionRef` the two together.
76
84
 
77
85
  **Required-count trap.** `required: true` at `allocation: 0` on a *signal rule* is rejected
78
86
  (contract 34). A required *condition* is independent of signal weights — the two gates stack.
79
87
 
88
+ ## Entry discipline recipes
89
+
90
+ The `entry` object is required on every CREATE, all seven keys, no defaults.
91
+
92
+ | Intent | Object |
93
+ |---|---|
94
+ | Today's behaviour, unchanged | `{ "trigger": "AT_SIGNAL", "confirmTf": "<strategy tf>", "closes": 1, "bandAtrMultiple": 1.0, "levelSource": "SWING_HIGH", "levelOffsetAtrMultiple": 0, "validForBars": 4 }` |
95
+ | Wait for the bar to close, skip a runaway | `{ "trigger": "ON_CANDLE_CLOSE", "confirmTf": "<strategy tf>", "closes": 1, "bandAtrMultiple": 1.0, … }` |
96
+ | Two-close confirmation on the lower rung | `{ "trigger": "ON_CANDLE_CLOSE", "confirmTf": "<lower rung>", "closes": 2, "bandAtrMultiple": 0.75, … }` |
97
+ | Break of the swing high, half an ATR through | `{ "trigger": "STOP_THROUGH_LEVEL", "levelSource": "SWING_HIGH", "levelOffsetAtrMultiple": 0.5, "validForBars": 8, "closes": 1, … }` |
98
+ | Buy the retest of the broken level | `{ "trigger": "ON_RETEST", "levelSource": "SWING_HIGH", "levelOffsetAtrMultiple": 0, "validForBars": 12, "closes": 1, … }` |
99
+
100
+ Bounds: `closes` 1–5 and `1` unless `ON_CANDLE_CLOSE`; `bandAtrMultiple` > 0 and ≤ the platform's
101
+ `defaultMaxEntryDeviationAtrMultiple`; `levelOffsetAtrMultiple` 0–2 unsigned and `0` unless a level
102
+ trigger; `validForBars` 1–24. `confirmTf` is the strategy timeframe or the rung below it, nothing
103
+ else. A meaningful value under a trigger that ignores it is REFUSED, not ignored — that is
104
+ deliberate, so a dial never silently does nothing.
105
+
106
+ ## Custom section shape
107
+
108
+ `{ kind, sectionKey, title, benchmarkTicker, notes, columns }`. `benchmarkTicker` and
109
+ `notes` are **required-nullable** — send explicit `null` rather than omitting them, because the
110
+ section is rebuilt whole on save and an omitted key clears the value. **Omit `sectionKey` on a
111
+ CREATE**: the server derives it from the section.
112
+
113
+ A section carries **no `timeframe`** (48.0.0). Its relative columns resolve against the strategy
114
+ timeframe; a column reaches any other timeframe by pinning it — `timeframe: { abs: '4h' }` on the
115
+ column, which it could always do.
116
+
80
117
  ## Weight matrices (rules)
81
118
 
82
119
  **Conviction pyramid (default).** 1–2 × Critical (the thesis signals, usually `required`),
@@ -107,6 +144,10 @@ send the full replacement object. Examples validated as canonical defaults: `vol
107
144
  | Swing breakout (4h) | 0.75 – 1.75 | 2 | 1R | 1.2R, giveback 35, buffer 0.3 | off |
108
145
  | Swing trend (4h) | 1.0 – 2.5 | 2 | 1R | 1.5R, giveback 45–55, buffer 0.3 | off |
109
146
 
147
+ Post-entry invalidation: `decisionInvalidationExitEnabled: true` closes a filled position when a
148
+ **closed** strategy-timeframe candle finishes beyond the decision's invalidation level, reduce-only.
149
+ Bar-close only — the protective stop still owns intrabar moves.
150
+
110
151
  Bounds to respect (validated): break-even trigger 0.5–2R; trailing trigger 0–2R step 0.01
111
152
  (0 = trail from entry); giveback 25–55%; buffer 0.01–1%; grace ≥ interval; stop-band floor <
112
153
  ceiling ≤ the 3×ATR structural cap; RR within the catalog's served range (0.5–3 today). Wider
@@ -37,8 +37,8 @@ closes while stops, trailing, and time decay keep managing the position intraday
37
37
 
38
38
  | TradingView script | Verdict | Port anchor |
39
39
  |---|---|---|
40
- | Squeeze Momentum [LazyBear] / TTM Squeeze | Process port (Keltner not in catalog — substitute named) | `bbWidthPct` + rank, `bollinger_squeeze`, momentum trajectory |
41
- | Supertrend / UT Bot Alerts | Split port: entry substitute + **native stop engine** | `EMA5_13`, `MAalign`, position-management trailing |
40
+ | Squeeze Momentum [LazyBear] / TTM Squeeze | **Direct port** — Keltner is native | `BB_UPPER spread KC_UPPER`, `BB_LOWER spread KC_LOWER`, `bollinger_squeeze` |
41
+ | Supertrend / UT Bot Alerts | **Direct port** for the state; flip still substituted | `ST_LINE`, `ST_DIR`, `EMA5_13` for the flip, position-management trailing |
42
42
  | Chandelier Exit | Direct port of the *mechanism* | trailing from entry (`trailingTriggerR: 0`) |
43
43
  | MACD + 200 MA filter | Direct process port | `MACD_cross`, `dist_SMA200`, `macd_bull_cross` |
44
44
  | Golden / Death Cross | Direct process port | `SMA50_SMA200_spread` + `_trend` |
@@ -46,7 +46,7 @@ closes while stops, trailing, and time decay keep managing the position intraday
46
46
  | VWAP reversion | Direct process port | `dist_VWAP`, `dist_VWAP_rank_far` |
47
47
  | Donchian / Turtle breakout | Process port (N-bar channel → swing structure) | `zone`, `dist_swingHi`, `sr_resistance_break` |
48
48
  | ICT/SMC: FVG + Order Blocks | Direct process port of the zone logic | `STRUCT_ZONES` columns + `structure_*` signals |
49
- | WaveTrend, QQE, Hull Suite, Ichimoku, Parabolic SAR, Keltner | **Not expressible** — say so; nearest neighbors below | — |
49
+ | WaveTrend, QQE, Hull Suite, Ichimoku, Parabolic SAR, Keltner | **All native since contract 46.1** — port directly | `WT1`/`WT2`, `QQE_RSI_MA`/`QQE_STOP`, `HMA20`, `ICHI_*`, `PSAR`, `KC_*` |
50
50
 
51
51
  ---
52
52
 
@@ -55,7 +55,10 @@ closes while stops, trailing, and time decay keep managing the position intraday
55
55
  **Source process.** Squeeze ON while Bollinger Bands sit inside Keltner Channels (volatility
56
56
  coiled); wait; when the squeeze releases, enter in the direction of the momentum histogram.
57
57
 
58
- **Port.** Keltner Channels are not in the catalog, so squeeze detection substitutes
58
+ **Port.** Keltner Channels are native (`KC_UPPER`/`KC_MID`/`KC_LOWER`), so the squeeze is the
59
+ source's own definition: `BB_UPPER spread KC_UPPER` negative AND `BB_LOWER spread KC_LOWER`
60
+ positive is bands-inside-Keltner. The board-relative proxy below remains serviceable but is no
61
+ longer the only option — it substitutes
59
62
  *cross-sectional and absolute* Bollinger compression — a stricter, universe-aware read:
60
63
  `bbWidthPct_rank_lo lte 10` AND `ADX lt 20` as the `SQUEEZE_ON` building block. Momentum
61
64
  direction comes from the MACD histogram trajectory (`MACD_now gt 0` with `MACD_trend rising`
@@ -75,7 +78,10 @@ Pine script simulates with a plotted line, position management executes: enable
75
78
  `trailingTriggerR: 0` (trail from entry — the Supertrend/UT Bot behavior), `giveback` as the
76
79
  ATR-offset analog (30–40 tight like factor-2 Supertrend, 45–55 loose like factor-3), plus the
77
80
  stop band (`minStopLossAtrMultiple`/`max…`) bounding the initial distance. The *flip entry*
78
- has no direct equivalent (no supertrend metric) — the named substitute is a trend-state change:
81
+ is native: `ST_DIR` carries the direction and `ST_LINE` the plotted stop. **`ST_DIR` is a
82
+ persisting state, not a flip event** — it reads the same on every bar of a trend, so the FLIP
83
+ itself still needs an event column beside it. The named substitute for the flip is a trend-state
84
+ change:
79
85
  `MAalign is "bullish"` (state) with the `EMA5_13 is "Bullish"` cross event as the trigger, and
80
86
  `ma_ema_bull_cross` / `ma_ema_aligned_bull` Critical/Important-required in `rules`. Say the
81
87
  substitution out loud when presenting the strategy.
@@ -139,7 +145,8 @@ the book out of single-name traps. Rules: `ma_sma200_above` 3 required, `ma_ema_
139
145
  **Source process.** In an uptrend (close > 200 SMA), buy panic dips (RSI(2) < 10); exit fast
140
146
  (cross of the 5-period average / a few bars).
141
147
 
142
- **Port — named substitute.** The catalog carries no RSI(2); `RSI7` is the fastest RSI series
148
+ **Port — named substitute.** The catalog carries `RSI14` and `RSI7` only, so there is no `RSI2`;
149
+ `RSI7` is the fastest RSI series
143
150
  and `lte 10` on it is a rarer, deeper panic — state that trade-off rather than hiding it.
144
151
  `DIP_BUY` verdict UP = `dist_SMA200 gt 0` (ref a required `ABOVE_200`) AND `RSI7 lte 10`.
145
152
  Rules: `rsi_oversold` 3 required `{"threshold": 25}` (RSI14 gate tuned toward the fast-dip
@@ -193,22 +200,36 @@ quorum (`N_OF(2)`: `buyPres gte 0.55`, `RVOL gte 1.2`, `closeChg gt 0`). Rules:
193
200
  `structure_fvg_approach` / `structure_ob_approach` 3/2 required (their `proximityPct` params
194
201
  are the "in the zone" dial), `structure_zone_confluence` 2, `sr_at_support` 2. Stops: tight
195
202
  band (0.5–1.5 ATR) — the zone's far edge is the invalidation, and the studio places stops by
196
- ATR/structure natively. **Not expressible, say so:** liquidity sweeps, displacement legs,
203
+ ATR/structure natively. **A grammar limit, not a missing metric** — a clause compares one column
204
+ against a literal, so an ordered sequence of events has no expressible form at all: liquidity
205
+ sweeps, displacement legs,
197
206
  killzone clocks, and Turtle-Soup false-break sequencing (ordering between events is outside
198
207
  the grammar) — offer this zone-reaction port as the nearest expressible neighbor, labelled.
199
208
 
200
- ## Not expressible — and the honest nearest neighbor
209
+ ## Now native — do not substitute for these
201
210
 
202
- | Script | Missing primitive | Nearest expressible neighbor |
211
+ WaveTrend (`WT1`/`WT2`), QQE (`QQE_RSI_MA`/`QQE_STOP`), Hull (`HMA20`), Ichimoku
212
+ (`ICHI_CONV`/`ICHI_BASE`/`ICHI_SPAN_A`/`ICHI_SPAN_B`/`ICHI_LAG`), Parabolic SAR (`PSAR`), Keltner
213
+ (`KC_UPPER`/`KC_MID`/`KC_LOWER`), daily pivots (`PIVOT_P`/`PIVOT_R1`–`R3`/`PIVOT_S1`–`S3`),
214
+ Williams %R (`WILLR14`), Stochastic RSI (`STOCH_RSI14`), Bollinger boundaries
215
+ (`BB_UPPER`/`BB_LOWER`), ADX components (`DI_PLUS`/`DI_MINUS`). All arrived with contract 46.1 —
216
+ every one of them was listed as inexpressible in this file before it.
217
+
218
+ ## Not expressible — the catalog keys this needs
219
+
220
+ The one section where a claim that the catalog LACKS something may live, and every row names the key
221
+ it denies so the claim stays checkable. A claim about the grammar's SHAPE (a clause compares one
222
+ column against a literal; `distance` rejects an `offset`) is permanent and belongs in prose above.
223
+ A claim that a metric is absent belongs here, or nowhere.
224
+
225
+ | Script / primitive | Key the catalog would need | Nearest expressible neighbour |
203
226
  |---|---|---|
204
- | WaveTrend [LazyBear] | WT oscillator | `STOCH_K`/`STOCH_D` zones + crosses (`stoch_*` signals) |
205
- | QQE / QQE MOD | smoothed-RSI ATR bands | `RSI7` trajectory + `rsi_*` signals with tuned thresholds |
206
- | Hull Suite | Hull MA | `EMA5/13/20` ribbon (`MAalign`, EMA spread trend) |
207
- | Ichimoku | cloud spans | `MAalign` + `SMA50_SMA200_spread` + `zones_htf_*` levels |
208
- | Parabolic SAR | SAR dots | trailing stop from entry (`trailingTriggerR: 0`) |
209
- | Keltner Channels | ATR envelope | `bbWidthPct` compression + `atrPct` band logic |
210
- | Pivot Points (daily) | session pivots | `VWAP` + `swingHi`/`swingLo` distances |
211
- | Previous-day / previous-week high-low (PDH/PDL) | offset-able levels (`distance` takes no `offset`; clauses are column-vs-literal) | daily swing structure: `zone_1d is "breakout high"`, `dist_swingHi_1d` / `dist_swingLo_1d` |
227
+ | RSI-2 (Connors) | `RSI2` | `RSI7 lte 10` as a named fast-dip substitute |
228
+
229
+ **PDH/PDL** stays inexpressible for a different reason, and the distinction matters: `distance`
230
+ rejects an `offset` and a clause never compares two columns, so the *grammar* has no form for it.
231
+ No metric would fix that. Use the daily swing structure (`zone_1d`, `dist_swingHi_1d`) and name the
232
+ substitution.
212
233
 
213
234
  Never compile a "port" of these under the source's name without the substitution note — a
214
235
  player who asked for WaveTrend and silently got Stochastic has no way to learn otherwise.