@battlegrid/mcp-server 31.2.63 → 31.2.65
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 +171 -21
- package/SKILL.md +33 -28
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/EXPORT.json +4 -4
- package/skills/battlegrid-strategy-authoring/SKILL.md +7 -4
- package/skills/battlegrid-strategy-examples/SKILL.md +65 -26
- package/skills/battlegrid-trade-analysis/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,157 @@ 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 — v85 (agents and strategies change only through their drafts)
|
|
28
|
+
|
|
29
|
+
**Breaking at v85: seven tools are retired, and a surviving tool refuses an input it accepted at v84.**
|
|
30
|
+
v85 is the contract on top of v84.
|
|
31
|
+
|
|
32
|
+
Every agent and strategy is now created and changed the same way: stage the change into the
|
|
33
|
+
entity's draft, read the draft back with its diff, diagnostics and impact, and commit exactly the
|
|
34
|
+
draft version and the live revision that read returned.
|
|
35
|
+
|
|
36
|
+
### Removed tools
|
|
37
|
+
|
|
38
|
+
- **`compile_strategy_plan`, `stage_strategy_plan`, `apply_strategy_plan`, `update_strategy_signal_rule`,
|
|
39
|
+
`create_intelligence_agent`, `update_intelligence_agent`, `rebind_intelligence_agent`** are gone, with
|
|
40
|
+
no alias; a call gets an unknown-tool error. The replacements: `get_strategy_draft` →
|
|
41
|
+
`stage_strategy_draft` → `commit_strategy_draft` for a strategy (a signal rule is a row of the draft's
|
|
42
|
+
`SIGNAL_RULES` axis), and `stage_agent_draft` → `get_agent_draft` → `commit_agent_draft` for an agent
|
|
43
|
+
(a rebind is the draft's `STRATEGY_BINDING` axis).
|
|
44
|
+
|
|
45
|
+
### Added tools
|
|
46
|
+
|
|
47
|
+
- **`stage_strategy_draft { strategyId?, draftVersion, axes }`**, **`commit_strategy_draft`** and
|
|
48
|
+
**`commit_agent_draft`** (`{ <id>, draftVersion, expectedRevision }`, `expectedRevision` null for a
|
|
49
|
+
create). A commit publishes exactly the draft version and the live revision the caller read, is
|
|
50
|
+
idempotent on `{ id, draftVersion }`, and returns the kind's committer response plus the draft's
|
|
51
|
+
`draftVersion` after its clear (null when the commit emptied it).
|
|
52
|
+
|
|
53
|
+
### Rejected input
|
|
54
|
+
|
|
55
|
+
- **`archive_strategy` no longer takes `confirm`.** A request carrying it is refused with the
|
|
56
|
+
unknown-key error — a break on a surviving tool.
|
|
57
|
+
- **`discard_agent_draft` and `discard_strategy_draft` take `draftVersion`** (the version
|
|
58
|
+
`get_<kind>_draft` returned) in place of `confirm`; `confirm` is refused as an unknown key, and a
|
|
59
|
+
version the draft moved past is refused `DRAFT_VERSION_MOVED`.
|
|
60
|
+
- **`preview_strategy_report` takes `{ coinSelection, source }`.** The report fields move under
|
|
61
|
+
`source: { kind: 'FIELDS', … }`, and `source: { kind: 'DRAFT', strategyId, draftVersion }` previews the
|
|
62
|
+
owner's draft as a commit would compose it. A body with the fields at the top level is refused.
|
|
63
|
+
- **A strategy's ENTRY axis is a union discriminated by `trigger`**: `{ trigger: 'ON_CANDLE_CLOSE' }`
|
|
64
|
+
alone, or a level trigger with `levelOffsetAtrMultiple` and `validForBars`. `ON_CANDLE_CLOSE` naming
|
|
65
|
+
either dial is refused, which the retired compile accepted; the server stamps the seed values.
|
|
66
|
+
|
|
67
|
+
### Widened input
|
|
68
|
+
|
|
69
|
+
- **`stage_agent_draft` accepts `STRATEGY_BINDING` on an edit draft**, where v84 refused it: a staged
|
|
70
|
+
binding commits as the agent's rebind. Its TRADING_CONFIG axis now describes each size preset as the
|
|
71
|
+
risk budget lost at the hard stop.
|
|
72
|
+
|
|
73
|
+
### Reshaped output
|
|
74
|
+
|
|
75
|
+
- **`fork_strategy` returns `{ strategyId, draftVersion }`** — the create draft it wrote, holding a copy
|
|
76
|
+
of the source — in place of `{ strategy }`. No strategy exists until that draft commits.
|
|
77
|
+
- **`get_agent_draft` and `get_strategy_draft` gain `draftVersion`** (0 when no draft is held, beside a
|
|
78
|
+
null `draft`), **`committedRevision`, a per-axis `diff` against live, `diagnostics` and `impact`.**
|
|
79
|
+
- **`stage_agent_draft` gains `diagnostics`**, the composed draft's errors and warnings.
|
|
80
|
+
- **`discard_agent_draft` and `discard_strategy_draft` return `discarded: true` only**; a discard that
|
|
81
|
+
finds nothing is refused rather than answered `false`.
|
|
82
|
+
|
|
83
|
+
### Widened enum
|
|
84
|
+
|
|
85
|
+
- **Strategy draft axes gain `ORIGIN`**, the create-only axis a fork writes, on `list_strategy_drafts`
|
|
86
|
+
and every strategy draft read. A client switching exhaustively on a strategy draft axis must add the
|
|
87
|
+
branch.
|
|
88
|
+
- **Error codes gain `DRAFT_VERSION_MOVED`** (details `draftVersion`, null when no draft is held) **and
|
|
89
|
+
`DRAFT_AXIS_CONTESTED`** (details `contestedAxes` and `draftVersion`), both 409.
|
|
90
|
+
- **Refusal details gain `nextAct`.** Every draft-specific refusal, the revision `CONFLICT` and the
|
|
91
|
+
in-flight commit refusal carry `nextAct: 'get_draft'`; a commit's validation refusal carries
|
|
92
|
+
`nextAct: 'stage'`; every other refusal carries none. `details.expectedRevision` widens to
|
|
93
|
+
`number | null`.
|
|
94
|
+
|
|
95
|
+
### Removed error code
|
|
96
|
+
|
|
97
|
+
- **`PLAN_APPROVAL_NOT_FOUND`**, with the plan path that raised it.
|
|
98
|
+
|
|
99
|
+
### Behaviour behind unchanged schemas
|
|
100
|
+
|
|
101
|
+
- **`archive_strategy` and `restore_strategy` no longer refuse around the owner's unsaved draft**: they
|
|
102
|
+
re-base it onto the revision they write. `restore_strategy`'s description drops the compile route — a
|
|
103
|
+
repair is staged into the draft and committed, which restores the strategy.
|
|
104
|
+
- **The `author-strategy` prompt** reads "Discover, stage, review, and commit a BattleGrid strategy
|
|
105
|
+
through its draft".
|
|
106
|
+
- **The server's manifest records a description hash per tool** (manifest format 4), and the tool count
|
|
107
|
+
moves 142 → 138.
|
|
108
|
+
|
|
109
|
+
A strict client that rejects unknown keys fails to parse `fork_strategy` and the draft reads at v85;
|
|
110
|
+
list the tools again after the server deploys.
|
|
111
|
+
|
|
112
|
+
## Contract history — v84 (the hold moves from the condition to each clause and group)
|
|
113
|
+
|
|
114
|
+
**Breaking at v84: a condition body accepted at v83 is refused, and condition evidence is reshaped.**
|
|
115
|
+
v84 is the contract on top of v83.
|
|
116
|
+
|
|
117
|
+
How many completed bars a reading must hold is no longer one number on the condition. Each clause
|
|
118
|
+
holds its own reading, counted on its own column's timeframe, and a group can hold its members'
|
|
119
|
+
same-bar reading.
|
|
120
|
+
|
|
121
|
+
### Rejected input — `compile_strategy_plan`, `apply_strategy_plan`
|
|
122
|
+
|
|
123
|
+
- **A condition no longer carries `closes`.** A condition entry naming it is refused with the
|
|
124
|
+
unknown-key error, in a plan body and in every normalized post-state and strategy draft those tools
|
|
125
|
+
carry.
|
|
126
|
+
- **Every clause and every group requires `hold: { atLeast, of }`**, whole numbers of at least 1.
|
|
127
|
+
`{ "atLeast": 1, "of": 1 }` is one read (what every v83 condition did), `{n, n}` is "n closes in a
|
|
128
|
+
row", `{1, n}` is "within n closes", and anything else is "at least m of n closes". A clause or group
|
|
129
|
+
without `hold` is refused. A plan staged under v83 must be recompiled.
|
|
130
|
+
- **What a header admits is refused by the builder, never by the schema.** `atLeast` above `of`, a
|
|
131
|
+
window above the header's maximum, or a group hold over members that hold their own bars is a typed
|
|
132
|
+
remediation error.
|
|
133
|
+
|
|
134
|
+
### Replaced output — `preview_strategy_report`, `list_strategy_vocabulary`
|
|
135
|
+
|
|
136
|
+
- **`closesReadable: boolean` is replaced by `conditionHold: { timeframe, maxWindow, maxAtLeast,
|
|
137
|
+
refusal }`** on each report condition column and each scalar metric. It names the timeframe a
|
|
138
|
+
clause counts on, the largest `of` and `atLeast` it admits (`maxAtLeast` is 1 for a cross or other
|
|
139
|
+
event, so an event can only be held "within n closes"), and why it admits one bar when it does:
|
|
140
|
+
`NOT_REWINDABLE`, `DEVELOPING`, `OFFSET`, `BAR_STATE` or `HISTORY`. Read the window from here
|
|
141
|
+
rather than assuming a ceiling; there is no longer a fixed 1–5 limit.
|
|
142
|
+
|
|
143
|
+
### Reshaped output — condition evidence
|
|
144
|
+
|
|
145
|
+
- **Every clause-evidence entry gains `hold`, `counts` and `decidedBar`**, on `preview_strategy_report`,
|
|
146
|
+
`list_gate_blocks`, `get_radar_close_decisions`, `get_agent_coin_qualification`,
|
|
147
|
+
`preview_radar_resolution` and `get_signal_log`. `counts` is `{ trueCount, total, unresolvedCount }`
|
|
148
|
+
over the clause's own bars. `decidedBar` is `{ timeframe, closesBack }`, the bar its operand was read
|
|
149
|
+
on, or `null` when the hold that counts it reads one bar.
|
|
150
|
+
- **A condition outcome loses `hold`** (`closesHeld`, `closesRequired`, `liveOutcome`, `nextCloseAt`):
|
|
151
|
+
the counted outcome is the clause's own, on its evidence. A decision record's condition
|
|
152
|
+
`declaration` loses `closes`.
|
|
153
|
+
- **Every strategy read returns `hold` on each clause and group** and no `closes` on the condition.
|
|
154
|
+
|
|
155
|
+
### Widened enum — the evidence union
|
|
156
|
+
|
|
157
|
+
- **The evidence union gains a `group` arm**, `{ kind: 'group', op, hold, counts, decidedBar, outcome,
|
|
158
|
+
memberCount }`, emitted only for a group holding more than one bar, just before the `memberCount`
|
|
159
|
+
entries that belong to it. A client switching exhaustively on `kind` must add the branch. A strategy
|
|
160
|
+
without group holds serves no new arm.
|
|
161
|
+
|
|
162
|
+
A strict client that rejects unknown keys fails to parse condition evidence at v84; list the tools
|
|
163
|
+
again after the server deploys.
|
|
164
|
+
|
|
165
|
+
## Contract history — v70.1 (budget run-state)
|
|
166
|
+
|
|
167
|
+
**Additive at v70.1: one output field added to the risk budget.**
|
|
168
|
+
|
|
169
|
+
### Added output — `get_agent_budget`, `reset_agent_drawdown_baseline`
|
|
170
|
+
|
|
171
|
+
- **`budget.runStatus`** is your run-state for the agent: `blocked`, `paused` or `active`, already
|
|
172
|
+
ranked by the server. `blocked` means the admission gate is refusing the agent for a reason other
|
|
173
|
+
than its halt (`blockedReason` names it). A halted agent reads `paused`, even while its own
|
|
174
|
+
`AGENT_HALTED` block stands. Read it rather than re-deriving it from `blockedReason` and `haltedAt`.
|
|
175
|
+
- A client that ignores the field is unaffected. A client that validates results against a cached,
|
|
176
|
+
closed output schema should list the tools again after the server deploys.
|
|
177
|
+
|
|
27
178
|
## Contract history — v83 (a model's vendor is its vendor slug)
|
|
28
179
|
|
|
29
180
|
**Breaking at v83: four output fields renamed and retyped, one aggregation regrouped.** v83 is the
|
|
@@ -1007,7 +1158,8 @@ preview names it.
|
|
|
1007
1158
|
|
|
1008
1159
|
- **`scalarFamilies` is where the vocabulary went.** Every report-level scalar family with its
|
|
1009
1160
|
section, and one entry per METRIC carrying its gloss, legal condition operators, closed label
|
|
1010
|
-
vocabulary, read contract (`closesReadable` / `developingRead
|
|
1161
|
+
vocabulary, read contract (`closesReadable` / `developingRead`; v84 replaces `closesReadable` with
|
|
1162
|
+
`conditionHold`) and the scopes it is measured at.
|
|
1011
1163
|
One gloss per metric with its scopes named against it, never one per pair.
|
|
1012
1164
|
|
|
1013
1165
|
- **Served whole under every category.** A scalar describes the report, not a metric family, so it
|
|
@@ -2183,7 +2335,7 @@ For the strict authoring tools, the multi-account input is exactly `{ account, r
|
|
|
2183
2335
|
|
|
2184
2336
|
```json
|
|
2185
2337
|
{
|
|
2186
|
-
"name": "
|
|
2338
|
+
"name": "commit_strategy_draft",
|
|
2187
2339
|
"inputSchema": {
|
|
2188
2340
|
"type": "object",
|
|
2189
2341
|
"properties": {
|
|
@@ -2193,11 +2345,14 @@ For the strict authoring tools, the multi-account input is exactly `{ account, r
|
|
|
2193
2345
|
"description": "Which BattleGrid account to use for this action"
|
|
2194
2346
|
},
|
|
2195
2347
|
"request": {
|
|
2196
|
-
"
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2348
|
+
"type": "object",
|
|
2349
|
+
"properties": {
|
|
2350
|
+
"strategyId": { "type": "string", "format": "uuid" },
|
|
2351
|
+
"draftVersion": { "type": "integer", "minimum": 1 },
|
|
2352
|
+
"expectedRevision": { "anyOf": [{ "type": "integer", "minimum": 1 }, { "type": "null" }] }
|
|
2353
|
+
},
|
|
2354
|
+
"required": ["strategyId", "draftVersion", "expectedRevision"],
|
|
2355
|
+
"additionalProperties": false
|
|
2201
2356
|
}
|
|
2202
2357
|
},
|
|
2203
2358
|
"required": ["account", "request"],
|
|
@@ -2220,24 +2375,19 @@ Each account supports one active key at a time. Generating a new key automatical
|
|
|
2220
2375
|
|
|
2221
2376
|
For paid games and autonomous wagering, enable **Server-Signed Wagers** in the MCP tab (`mcp:wager` scope). Strategy discovery and non-financial configuration writes only need `mcp:read`.
|
|
2222
2377
|
|
|
2223
|
-
## Strategy authoring (
|
|
2378
|
+
## Strategy and agent authoring (stage → read → commit)
|
|
2224
2379
|
|
|
2225
|
-
Strategies
|
|
2380
|
+
Strategies and intelligence agents change only through their drafts, with one tool family per kind (`<kind>` is `strategy` or `agent`): `stage_<kind>_draft`, `get_<kind>_draft`, `commit_<kind>_draft`, `discard_<kind>_draft` and `list_<kind>_drafts`. **A stage writes the player's unsaved draft and commits nothing; `commit_<kind>_draft` is the only write to the entity itself**, and it publishes exactly the draft the caller read.
|
|
2226
2381
|
|
|
2227
|
-
1. **Choose the
|
|
2382
|
+
1. **Choose the entity.** `list_strategies` (add `includeInactive:true` when repairing an archived strategy) and `get_strategy`, or `list_intelligence_agents` and `get_intelligence_agent`. `fork_strategy({ strategyId, sourceRevision })` copies a visible revision into a new create draft and returns its `strategyId` and `draftVersion`; it creates nothing.
|
|
2228
2383
|
2. **Discover the report vocabulary live.** Walk `list_strategy_categories` → `list_strategy_vocabulary` → `get_metric_construction_hints` → `get_strategy_column_contract`, and use `get_strategy_section_template` / `preview_strategy_report`. Do not guess metric, transform, parameter, template, or enabled-timeframe facts — they are server-discovered.
|
|
2229
|
-
3. **
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
- **RESTORE** targets an owned inactive revision (with any repair axes).
|
|
2233
|
-
4. **Review before confirming.** Inspect the returned `approvedPlan` (complete post-state, proposed revision, diff, bound-agent impact, expiry) and `reviewContext` (column contracts, point-in-time report preview, open positions, quota/name admission). The plan token expires after five minutes; recompile after expiry or drift.
|
|
2234
|
-
5. **Apply the plan the server already holds.** After explicit user approval, call `apply_strategy_plan({ request: { planToken, confirm: true } })`. **There is no `plan` member** — one is rejected as an unknown key. The server keeps the plan its own compile approved and reads it back, so nothing is copied out of the compile response and nothing can be mistyped or truncated in transit. Forward `planToken` byte-for-byte exactly as received: it is an opaque signed value, never retyped, paraphrased or rebuilt from memory, and a mangled one addresses no approved plan and is refused. `PLAN_APPROVAL_NOT_FOUND` means no approved plan answers to this token — already applied, lapsed, or never issued — and the recovery is to compile again; so is `TOKEN_EXPIRED` once the five minutes run out. Any other refusal (quota, name collision, a bound agent that changed, a moved catalog) **leaves the plan applicable**: clear the cause and confirm again with the same token while it lives. Changed configuration propagates to every bound agent immediately.
|
|
2235
|
-
|
|
2236
|
-
The authored axes — including normalized `sections` and `conditions`, each condition carrying its own required, nullable `verdict` — belong on the **compile** request. Every derived field (`diff`, `viability`, `mismatches`, `signalRules`, `creationSeed`, `proposedRevision`, `bindingImpact`, `authoringCatalogDigest`, `reviewContext`) is re-derived server-side and rejected as an unknown key if resubmitted, and `conditionVerdicts` is rejected with a message naming its replacement — the verdict belongs on the condition.
|
|
2384
|
+
3. **Read the draft.** `get_<kind>_draft({ request: { <id> } })` returns the draft (or `null`), its `draftVersion` (0 when none is held), the live `committedRevision` (null for an entity not created yet), a per-axis `diff` against live, `diagnostics` and `impact`.
|
|
2385
|
+
4. **Stage the change.** `stage_<kind>_draft({ request: { <id>?, draftVersion, axes } })` names the version you read. Each axis is whole, except a strategy's `SIGNAL_RULES`, whose rows replace the drafted rows for the same signals and keep every other one. Omit the id to open a new create draft; the response carries the minted id. A stage that has written never refuses: the composed draft's errors and warnings come back as `diagnostics`. It is refused only when the version is one the draft never reached (`DRAFT_VERSION_MOVED`) or when the player's own form changed an axis you are staging after that version (`DRAFT_AXIS_CONTESTED`, naming `contestedAxes`).
|
|
2386
|
+
5. **Review with the player, then commit.** Read the draft again, show the player its diff and impact, and on their explicit word call `commit_<kind>_draft({ request: { <id>, draftVersion, expectedRevision } })` with exactly the two numbers that read returned — `expectedRevision: null` creates the entity at revision 1. A draft that moved is refused `DRAFT_VERSION_MOVED`, a moved revision `CONFLICT`, and an invalid draft with the same code its diagnostics carried; nothing is written. Each refusal's `details.nextAct` says what to do next (`get_draft` or `stage`). The commit is idempotent on `{ id, draftVersion }`: a retried commit replays its outcome. Changed strategy configuration propagates to every bound agent immediately; committing a draft of an archived strategy restores it.
|
|
2237
2387
|
|
|
2238
|
-
`
|
|
2388
|
+
`discard_<kind>_draft({ request: { <id>, draftVersion } })` removes the draft at the version you read and never one that moved since. `preview_strategy_report({ coinSelection, source: { kind: 'DRAFT', strategyId, draftVersion } })` previews a draft as a commit would compose it. In multi-account mode every one of these calls uses the `{ account, request }` sibling envelope.
|
|
2239
2389
|
|
|
2240
|
-
**Strategy-bound agents.**
|
|
2390
|
+
**Strategy-bound agents.** An agent binds to a strategy through its draft's `STRATEGY_BINDING` axis — on a create draft, or on an edit draft as a rebind that commits through the same `commit_agent_draft` (discover `strategyId` via `list_strategies`). The plan tools, `update_strategy_signal_rule` and the direct agent writers are retired, and so is `create_strategy`.
|
|
2241
2391
|
|
|
2242
2392
|
## Capabilities
|
|
2243
2393
|
|
|
@@ -2401,7 +2551,7 @@ you reach over MCP:
|
|
|
2401
2551
|
| `battlegrid-arena-play` | Enter Market Grid sessions: find an open session, read its coin pool and live market context, compose a grid with real per-coin reasoning or have an agent generate it, submit, then read results and the reasoning journal |
|
|
2402
2552
|
| `battlegrid-market-analysis` | Read the current crypto market — regime, funding and open interest, leaders and laggards, a deep-dive on any named coin — and close with the levels worth watching |
|
|
2403
2553
|
| `battlegrid-radar-deployment` | Put agents on standing duty: per-coin Radar policies that fire on confirmed regime flips, and per-preset Arena deployment policies, previewed before they are written and un-deployed with the blast radius stated |
|
|
2404
|
-
| `battlegrid-strategy-authoring` | Build a strategy from a plain-English idea: gather evidence, lock the spec,
|
|
2554
|
+
| `battlegrid-strategy-authoring` | Build a strategy from a plain-English idea: gather evidence, lock the spec, stage it into the strategy's draft, review the diff, diagnostics and impact, commit only on confirmation. Also fork, tune, restore, archive, preview |
|
|
2405
2555
|
| `battlegrid-strategy-doctor` | Diagnose an agent that is not doing what was expected — why it has not traded, why it stopped, whether it is healthy — from typed fields, then rank the fixes with the exact lever each needs |
|
|
2406
2556
|
| `battlegrid-strategy-examples` | Full-surface composition patterns: custom report sections and header grammar, benchmark sections, condition trees with verdicts and enforcement gates, tiered signal weights and the aggregate gate math, routing gates, ATR trade levels, position management, plus validated desk-grade playbooks and TradingView process ports |
|
|
2407
2557
|
| `battlegrid-trade-analysis` | Read your own trading position: where the money is, whether each agent is doing its job, what is open and how close it sits to its protections, and whether the automation is actually running |
|
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: battlegrid
|
|
3
|
-
description: MCP skill for BattleGrid — play crypto prediction games (Market Grid), author trading strategies
|
|
3
|
+
description: MCP skill for BattleGrid — play crypto prediction games (Market Grid), author trading strategies and intelligence agents through their drafts (stage → read the diff → commit on the player's word), and manage strategy-bound agents from AI agents.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# BattleGrid
|
|
@@ -34,44 +34,47 @@ to connect and to know which one to open.
|
|
|
34
34
|
|
|
35
35
|
## Single-account vs multi-account request shape
|
|
36
36
|
|
|
37
|
-
The
|
|
37
|
+
The draft lifecycle tools — `stage_<kind>_draft`, `get_<kind>_draft`, `commit_<kind>_draft`, `discard_<kind>_draft` and `list_<kind>_drafts` for the `strategy` and `agent` kinds — and `get_strategy_section_template` use one strict server-owned envelope, `{ request: canonicalPayload }`.
|
|
38
38
|
|
|
39
|
-
- **Single account:** call them as the server publishes them, e.g. `
|
|
39
|
+
- **Single account:** call them as the server publishes them, e.g. `stage_strategy_draft({ request })`.
|
|
40
40
|
- **Multiple accounts (proxy):** live discovery adds a sibling `account`, so the shape is exactly `{ account, request }`. Select the account in the outer field; keep `request` exactly as discovered. The proxy strips only `account` and forwards the unchanged `{ request }`.
|
|
41
41
|
|
|
42
42
|
Never put `account` inside `request`, and never flatten request fields beside it. Other tools keep whatever input shape live discovery reports for them.
|
|
43
43
|
|
|
44
44
|
## Scopes
|
|
45
45
|
|
|
46
|
-
- `mcp:read` — strategy discovery **and** non-financial configuration writes (
|
|
46
|
+
- `mcp:read` — strategy discovery **and** non-financial configuration writes (stage and commit strategy and agent drafts, customize signals). Treat it as configuration authority, not view-only.
|
|
47
47
|
- `mcp:wager` — financial actions (submit paid entries, accept/cancel entry decisions, deployment policies). Enable **Server-Signed Wagers** in Profile → MCP to grant it. Pending entry decisions come from the conversational surface, which waits for approval; an agent deployed to a radar coin or a trading-enabled arena slot executes without one.
|
|
48
48
|
|
|
49
|
-
## Author a strategy, and bind
|
|
49
|
+
## Author a strategy or an agent, and bind them
|
|
50
50
|
|
|
51
|
-
**The arc lives in `battlegrid-strategy-authoring
|
|
52
|
-
|
|
53
|
-
it states only what the *proxy* adds to
|
|
51
|
+
**The arc lives in `battlegrid-strategy-authoring` and `battlegrid-agent-management`** — activate the
|
|
52
|
+
one you need, and `battlegrid-strategy-examples` alongside the first when the strategy goes beyond a
|
|
53
|
+
bare template. Do not compose a draft from this document; it states only what the *proxy* adds to
|
|
54
|
+
that arc.
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
Four facts about transport, which are this skill's to state because they are about the wire rather
|
|
56
57
|
than about authoring:
|
|
57
58
|
|
|
58
59
|
- **The envelope is `{ request }`, or `{ account, request }` on a multi-account proxy** (see above).
|
|
59
|
-
It applies to `get_strategy_section_template
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
agent
|
|
60
|
+
It applies to every draft lifecycle tool and to `get_strategy_section_template`.
|
|
61
|
+
- **Every change goes through the entity's draft.** `stage_<kind>_draft({ request: { <id>?,
|
|
62
|
+
draftVersion, axes } })` writes proposed axes into the player's unsaved draft and commits nothing;
|
|
63
|
+
omit the id to open a new create draft, whose minted id the response carries.
|
|
64
|
+
`get_<kind>_draft({ request: { <id> } })` returns the draft, its `draftVersion`, the live
|
|
65
|
+
`committedRevision`, a per-axis `diff`, `diagnostics` and `impact`.
|
|
66
|
+
- **A commit names exactly the two numbers that read returned.**
|
|
67
|
+
`commit_<kind>_draft({ request: { <id>, draftVersion, expectedRevision } })` — `expectedRevision`
|
|
68
|
+
is `null` for an entity not created yet. Commit only after the player has seen the diff and impact
|
|
69
|
+
and said yes. `discard_<kind>_draft({ request: { <id>, draftVersion } })` is the same fence.
|
|
70
|
+
- **The proxy forwards numbers and `null` unchanged.** It never fills in a version, never retries a
|
|
71
|
+
refused call and never rebuilds a request: a `DRAFT_VERSION_MOVED`, `DRAFT_AXIS_CONTESTED` or
|
|
72
|
+
revision `CONFLICT` refusal carries `details.nextAct`, and the next act is the caller's.
|
|
73
|
+
|
|
74
|
+
`fork_strategy` copies a revision into a new create draft and creates nothing until that draft is
|
|
75
|
+
committed. An agent binds to a strategy through its draft's `STRATEGY_BINDING` axis, on a create or
|
|
76
|
+
as a rebind. **`battlegrid-radar-deployment`** carries putting an agent on standing duty;
|
|
77
|
+
**`battlegrid-strategy-doctor`** carries diagnosing one that is not trading.
|
|
75
78
|
|
|
76
79
|
## Play a game (Market Grid)
|
|
77
80
|
|
|
@@ -85,7 +88,7 @@ The `play-market-grid` prompt (discover via `prompts/list`) provides a guided en
|
|
|
85
88
|
|
|
86
89
|
## Retired operations
|
|
87
90
|
|
|
88
|
-
`create_strategy`
|
|
91
|
+
`create_strategy`, the plan tools (`compile_strategy_plan`, `stage_strategy_plan`, `apply_strategy_plan`), `update_strategy_signal_rule`, and the direct agent writers (`create_intelligence_agent`, `update_intelligence_agent`, `rebind_intelligence_agent`) are **retired** — they are absent from discovery and cannot be invoked. Stage into the entity's draft, read it back, and commit the version you read; a signal rule is a row of the strategy draft's `SIGNAL_RULES` axis, and a strategy binding is the agent draft's `STRATEGY_BINDING` axis. Do not attempt flat legacy payloads; the server enforces a closed-world request root and the proxy never reconstructs them.
|
|
89
92
|
|
|
90
93
|
## Common errors
|
|
91
94
|
|
|
@@ -95,7 +98,9 @@ The `play-market-grid` prompt (discover via `prompts/list`) provides a guided en
|
|
|
95
98
|
| `API key must start with "bg_live_"` | Invalid key format | Generate a new key at battlegrid.trade → Profile → MCP |
|
|
96
99
|
| Authentication failed (401/403) | Key revoked/rotated | Generate a new key and **restart** the proxy (keys read once at startup) |
|
|
97
100
|
| `"account" parameter is required` | Multi-account call missing `account` | Add the outer `account`; keep `request` unchanged |
|
|
98
|
-
|
|
|
101
|
+
| `DRAFT_VERSION_MOVED` | The draft changed after you read it, or no draft is held (`details.draftVersion` null) | Call `get_<kind>_draft` again and commit what it now holds |
|
|
102
|
+
| `DRAFT_AXIS_CONTESTED` | The player changed `details.contestedAxes` in their open form after the version you staged against | Read the draft again and propose against what they now have |
|
|
103
|
+
| `CONFLICT` on a commit | The live revision moved after the draft read | Read the draft again; commit at the new `committedRevision` |
|
|
99
104
|
| Required at allocation Off | A rule flags `required` on a signal weighted `0` (contract 34) | Read `details.inertRequiredSignalIds`; per signal either raise `allocation` or set `required: false` |
|
|
100
|
-
| Method not found | Calling a retired/unknown tool | Re-run `tools/list`;
|
|
105
|
+
| Method not found | Calling a retired/unknown tool | Re-run `tools/list`; stage, read and commit through the entity's draft |
|
|
101
106
|
| `Wager scope required` | `mcp:wager` not enabled | Enable Server-Signed Wagers in Profile → MCP |
|
package/dist/index.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ import { type Implementation, type Prompt, type Resource } from '@modelcontextpr
|
|
|
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.2.
|
|
52
|
+
export declare const PACKAGE_VERSION = "31.2.65";
|
|
53
53
|
export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
|
|
54
54
|
export interface EnvConfig {
|
|
55
55
|
apiKeys: string[];
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ 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.2.
|
|
55
|
+
export const PACKAGE_VERSION = '31.2.65';
|
|
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];
|
package/package.json
CHANGED
package/skills/EXPORT.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
|
|
3
|
-
"contractVersion": "
|
|
3
|
+
"contractVersion": "84.0.0",
|
|
4
4
|
"files": {
|
|
5
5
|
"battlegrid-agent-management/SKILL.md": "5ea8384ff76ccb9fdf00e4ec8b584f88fdba6d1c9d5679ce0b7380c23bfbaab1",
|
|
6
6
|
"battlegrid-arena-play/SKILL.md": "4cfaa92a8d0dcaf477b227abd774e7aa8642c185d72108dfd401be0cf8aecd7f",
|
|
7
7
|
"battlegrid-market-analysis/SKILL.md": "22fde9c4eac0261c89a8056ab8b20b66fcbe1fd58447fa0ac84fd410b4d134d7",
|
|
8
8
|
"battlegrid-radar-deployment/SKILL.md": "17301fa66f4b128154435462ffa967efcb3d016df671b03c485d0f8b840fc891",
|
|
9
|
-
"battlegrid-strategy-authoring/SKILL.md": "
|
|
9
|
+
"battlegrid-strategy-authoring/SKILL.md": "6aa3a04fd4f0624a671d70aead009f4943db1ffa34402e403b588dd998e828c9",
|
|
10
10
|
"battlegrid-strategy-doctor/SKILL.md": "a3446f24059be72ae797ba4e9669848d851ac3dc38675923fe3d307f557eb82f",
|
|
11
|
-
"battlegrid-strategy-examples/SKILL.md": "
|
|
11
|
+
"battlegrid-strategy-examples/SKILL.md": "534c053e75021b6954e2365dbec4893ed9fc807a064a922f47f2d34a4e528221",
|
|
12
12
|
"battlegrid-strategy-examples/references/playbooks.md": "02168e3af35268a30fd156f3337d25fe15e6df67da1592f4cb4d31090be3f94f",
|
|
13
13
|
"battlegrid-strategy-examples/references/recipes.md": "53887daece17995543e7d3fe1bf2577fc41a6254c291acacde48d7de63853641",
|
|
14
14
|
"battlegrid-strategy-examples/references/tradingview-ports.md": "2b13adbc137178955edcca31b6ab40c01c745a932a0750b91db8820c736700bb",
|
|
15
|
-
"battlegrid-trade-analysis/SKILL.md": "
|
|
15
|
+
"battlegrid-trade-analysis/SKILL.md": "f5d817022ab912f00e9c17ffa720a7599f6e6fa4b806d873fe5e7520c51a8dcd",
|
|
16
16
|
"battlegrid-trade-proposal/SKILL.md": "9bf7b7648fb009db118a3a428b15abf38df5ba56a2ef4c8a4eae3d979bdc8bcb"
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -115,10 +115,13 @@ question you would otherwise guess — and a refusal you would otherwise earn:
|
|
|
115
115
|
- **The `entry` axis is required on every CREATE**, all three keys, no defaults — `trigger`,
|
|
116
116
|
`levelOffsetAtrMultiple`, `validForBars`. Every trigger is decided at the close of the strategy's
|
|
117
117
|
OWN bar, so there is no confirm-timeframe key; for the level triggers the level itself is derived
|
|
118
|
-
from the trigger and the trade's direction, never named. A multi-bar hold belongs to the
|
|
119
|
-
that needs it
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
from the trigger and the trade's direction, never named. A multi-bar hold belongs to the clause
|
|
119
|
+
or group that needs it — its own `hold: { atLeast, of }`, which the editor calls **Hold** (Once,
|
|
120
|
+
In a row, Within, At least), counted in completed bars of the column's own timeframe — not to this
|
|
121
|
+
axis. Every clause and group carries `hold`; `{ "atLeast": 1, "of": 1 }` is Once. The
|
|
122
|
+
`strategy-examples` skill carries the vocabulary, the served hold domain (a crossover is held
|
|
123
|
+
"within" only; a Developing column cannot be held) and the per-column Confirmed / Developing read;
|
|
124
|
+
a CREATE without it is refused outright.
|
|
122
125
|
- `get_strategy_column_contract` → `outputs[].conditionOperators`. An empty array means that
|
|
123
126
|
rendered header has no comparison semantics and cannot appear in a condition clause at all.
|
|
124
127
|
Legality is per rendered header, not per column: a trajectory's slot header and its `_trend`
|
|
@@ -86,12 +86,15 @@ against a literal; neither of those shapes is what a previous-session level need
|
|
|
86
86
|
|
|
87
87
|
## Conditions
|
|
88
88
|
|
|
89
|
-
`{ conditionKey, name, definition, verdict, required, exit
|
|
90
|
-
required, no defaults. A `clock` key is REFUSED
|
|
91
|
-
contract `61.0.0
|
|
89
|
+
`{ conditionKey, name, definition, verdict, required, exit }` — all six
|
|
90
|
+
required, no defaults. A `clock` key is REFUSED (the per-condition evidence clock was retired in
|
|
91
|
+
contract `61.0.0`), and so is a condition-level `closes` key: the hold lives on each clause and each
|
|
92
|
+
group (contract `84.0.0`). Clauses: numeric/rank headers take `lt|lte|gte|gt|between`;
|
|
92
93
|
classification/direction headers take `is|in` with the served vocabulary. Groups:
|
|
93
|
-
`ALL | ANY | NOT | N_OF` (with `n`), depth ≤ 2.
|
|
94
|
-
|
|
94
|
+
`ALL | ANY | NOT | N_OF` (with `n`), depth ≤ 2. Every clause and every group carries a required
|
|
95
|
+
`hold: { atLeast, of }` — `{ "atLeast": 1, "of": 1 }` is a single read; a clause or group without
|
|
96
|
+
`hold` is refused (`CONDITION_HOLD_INVALID`). `conditionRef` composes named conditions (no
|
|
97
|
+
cycles; forward refs legal) and carries no hold of its own. `sectionKey: null` is sugar for a report-unique header only.
|
|
95
98
|
Verdicts: resolution is taken over the DISTINCT verdicts of the carriers that read TRUE. One
|
|
96
99
|
distinct verdict decides, and the first carrier in declaration order carrying it is named as the
|
|
97
100
|
decider — so order breaks ties between carriers that AGREE. Carriers that DISAGREE resolve
|
|
@@ -121,19 +124,55 @@ bar a condition reads is decided by the surface asking — a decision (the radar
|
|
|
121
124
|
compose that takes the trade, the exit sweep) reads completed strategy bars; a display read shows the
|
|
122
125
|
forming one and decides nothing — and by each candle column's own Confirmed / Developing selector.
|
|
123
126
|
|
|
124
|
-
**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
127
|
+
**Hold — how many closes a clause counts.** A clause's `hold: { atLeast, of }` reads TRUE when the
|
|
128
|
+
clause read TRUE on at least `atLeast` of its newest `of` COMPLETED bars — counted on the clause's
|
|
129
|
+
OWN column timeframe, not the strategy's: a `4h` column on a `1h` strategy held 3 reads three `4h`
|
|
130
|
+
bars. The editor calls the control **Hold** and offers four modes:
|
|
131
|
+
|
|
132
|
+
| Mode | Hold | Reads as |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| Once | `{ 1, 1 }` | a single read of the bar being decided |
|
|
135
|
+
| In a row | `{ n, n }` | "3 closes in a row" |
|
|
136
|
+
| Within | `{ 1, n }` | "within 5 closes" |
|
|
137
|
+
| At least | `{ m, n }` | "at least 2 of 5 closes" |
|
|
138
|
+
|
|
139
|
+
A hold counts the same completed bars whichever surface asks. Each bar is read or unreadable; an
|
|
140
|
+
unreadable bar is never replaced by an older one, and the count decides as soon as the bars read
|
|
141
|
+
decide it (TRUE once `atLeast` bars read TRUE, FALSE once the rest can no longer reach it), else
|
|
142
|
+
UNRESOLVED.
|
|
143
|
+
|
|
144
|
+
What a header admits is SERVED, never guessed: every column in the strategy's report catalog
|
|
145
|
+
(`preview_strategy_report` → `conditionColumns[].outputs[].conditionHold`, and each scalar metric in
|
|
146
|
+
the vocabulary) carries `{ timeframe, maxWindow, maxAtLeast, refusal }` — the timeframe it counts,
|
|
147
|
+
the largest `of`, the largest `atLeast`, and, when it admits only Once, why. `maxWindow` is the
|
|
148
|
+
history the store keeps for that column, less the column's own lookback and warm-up. A hold past
|
|
149
|
+
the served domain is refused (`CONDITION_HOLD_ILLEGAL`, the domain in `allowedDomain`). The reasons
|
|
150
|
+
a header admits only Once:
|
|
151
|
+
|
|
152
|
+
- `NOT_REWINDABLE` — the value is stored for the current bar only (session and universe scalars,
|
|
153
|
+
ranks, zone entities, enrichment metrics, published rolling changes). **Worked liquidity floor:**
|
|
154
|
+
`LIQUID_FLOOR` holds Once because `vol24hUsd` is a bundle scalar.
|
|
155
|
+
- `DEVELOPING` — the column reads the bar still forming. **Developing columns cannot be held**; set
|
|
156
|
+
the column Confirmed to count closes.
|
|
157
|
+
- `OFFSET` — the column already reads an earlier bar; remove the offset to count closes.
|
|
158
|
+
- `BAR_STATE` — the header describes only the bar being decided.
|
|
159
|
+
- `HISTORY` — not enough history is stored on that timeframe to count more than one close.
|
|
160
|
+
|
|
161
|
+
Candle columns (at, above or below the strategy timeframe), the regime (counted on the regime rung),
|
|
162
|
+
and the funding and open-interest histories (on the coarser of the strategy timeframe and their own
|
|
163
|
+
hourly cadence) can all be held. **A crossover is an event**: it fires on the bar a state changes,
|
|
164
|
+
so its `maxAtLeast` is 1 — hold a cross "within n closes", never "in a row".
|
|
165
|
+
|
|
166
|
+
**Group hold — closes counted together.** A group's `hold` counts its members' joint reading on the
|
|
167
|
+
same bar: the group's own outcome, its members folded by its operator, must read TRUE on at least
|
|
168
|
+
`atLeast` of its newest `of` bars, counted on its finest member's timeframe. The phrases add
|
|
169
|
+
"together": "together on 3 closes in a row", "together within 5 closes", "together on at least 2 of
|
|
170
|
+
5 closes". Inside a group holding more than Once, every member holds Once and none is a
|
|
171
|
+
`conditionRef` — each read is counted by exactly one hold — so a row holding its own closes, a
|
|
172
|
+
reference, or a held group inside is refused, naming the member. A conjunction that needs a
|
|
173
|
+
crossover to be TRUE (`ALL(cross, x)`) admits "within" only. Reach for a split to keep a condition's
|
|
174
|
+
MEANING separable; a reference carries its condition's one answer for the bar being decided and is
|
|
175
|
+
never counted across closes.
|
|
137
176
|
|
|
138
177
|
**Confirmed / Developing, per candle column.** Every transform whose home is the coin's candle series
|
|
139
178
|
carries `bars`: `"closed"` is Confirmed — the newest bar completed at the strategy close — and
|
|
@@ -142,18 +181,18 @@ it completes. Left unset the default is a rule, not a value: Confirmed for a col
|
|
|
142
181
|
strategy timeframe, and at or below it the series as the frame carries it. At or below the strategy
|
|
143
182
|
timeframe the choice therefore sets only what the live lane shows — no bar is in progress at the
|
|
144
183
|
anchor's own close — so Developing there is legal and changes no decision. A condition reading a
|
|
145
|
-
developing bar is single-frame:
|
|
146
|
-
role. One exception overrides both the stored value and the default: a column reading a metric with
|
|
184
|
+
developing bar is single-frame: its clause holds Once (a Developing column cannot be held), it
|
|
185
|
+
carries no verdict and it cannot take the `exit` role. One exception overrides both the stored value and the default: a column reading a metric with
|
|
147
186
|
no live value (itself, or a `spread` operand) reads completed bars at every rung, because the forming
|
|
148
187
|
bar publishes nothing for it. The resolved answer for a column is served on
|
|
149
188
|
`effectiveParameters.bars`, and it states that exception.
|
|
150
189
|
|
|
151
190
|
**A higher-timeframe level is measured from the current strategy-bar price.** The distance to a `4h`
|
|
152
191
|
Donchian band, and every candle label classifier (`MA_ALIGN`, `PRICE_ZONE`, `BB_TOUCH`), compares
|
|
153
|
-
against the frame's current price — the strategy bar's close under a decision, the
|
|
154
|
-
display — not against the higher-timeframe bar's own close, which is up to one
|
|
155
|
-
stale. A distance CHAINED into a series transform keeps every slot on its own
|
|
156
|
-
series carries one reference basis.
|
|
192
|
+
against the frame's current price, `LAST` — the strategy bar's close under a decision, the last
|
|
193
|
+
traded price on display — not against the higher-timeframe bar's own close, which is up to one
|
|
194
|
+
higher-timeframe bar stale. A distance CHAINED into a series transform keeps every slot on its own
|
|
195
|
+
bar's close, so one series carries one reference basis.
|
|
157
196
|
|
|
158
197
|
**The lane a strategy is deployed to.** Report-level scalars split by LANE, and the split is not a
|
|
159
198
|
quality of the header — it is which reader runs. Market breadth and the reference pairs are ordinary
|
|
@@ -232,8 +271,8 @@ and is REFUSED on every save; there is no live-reading entry to author.
|
|
|
232
271
|
short) by the offset, waiting for a return to it. Not filling is a correct outcome, not a
|
|
233
272
|
failure; no unrecovered break in memory means no setup, never a fallback level.
|
|
234
273
|
|
|
235
|
-
**A multi-bar hold belongs to the
|
|
236
|
-
the entry axis counts no bars, and there is no displacement band: the
|
|
274
|
+
**A multi-bar hold belongs to the CLAUSE (or group) that needs it.** Declare that clause's own
|
|
275
|
+
`hold` — "3 closes in a row", "within 5 closes" — the entry axis counts no bars, and there is no displacement band: the
|
|
237
276
|
platform's entry-deviation gate measures the live mark against the decided close and refuses a fill
|
|
238
277
|
that drifted past the budget in either direction.
|
|
239
278
|
|
|
@@ -14,7 +14,7 @@ Start with where the money is, always, even when the question is narrower — a
|
|
|
14
14
|
means nothing without the whole.
|
|
15
15
|
|
|
16
16
|
- `get_account_state` for the account total.
|
|
17
|
-
- `
|
|
17
|
+
- `get_agent_budget` for each agent's capital at risk, and `get_agent_fund_allocation` for its funding envelope.
|
|
18
18
|
|
|
19
19
|
Then state a **reconciliation line**: the account total against the sum of its parts. If they
|
|
20
20
|
agree, say they agree. **If they do not, state the gap as a gap** — name the amount and say you
|