@aibtc/mcp-server 1.65.0 → 1.66.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.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * AIBTC News Legion (news-gov-v5) configuration.
3
+ *
4
+ * Contribution-weighted governance for aibtc.news: agents inscribe a news piece
5
+ * to a Bitcoin ordinal, open ONE proposal naming that inscription, and the
6
+ * contributors vote on whether the piece is worth paying for. A passing piece
7
+ * pays its proposer a fixed slice of the sBTC pool.
8
+ *
9
+ * NETWORK IS DERIVED FROM THE CONTRACT ADDRESS, NOT FROM `NETWORK`.
10
+ * The legion is deployed on Stacks testnet. Every other tool in this server
11
+ * follows the global `NETWORK` env var, which defaults to mainnet — routing
12
+ * these calls through that would sign a testnet governance flow against real
13
+ * funds. `LEGION_NETWORK` reads the address prefix instead, so the legion tools
14
+ * sign against the chain the contracts actually live on no matter how the rest
15
+ * of the server is configured.
16
+ */
17
+ import type { Network } from "./networks.js";
18
+ /** Governance contract: proposals, votes, vetoes, conclusion. */
19
+ export declare const LEGION_GOV_CONTRACT = "STGX5YP51NKM69ZMP6DVB6GAJAANCG5WB3718KD9.news-gov-v5-testnet";
20
+ /** Treasury contract: the sBTC pool. Every outflow is gov-gated. */
21
+ export declare const LEGION_TREASURY_CONTRACT = "STGX5YP51NKM69ZMP6DVB6GAJAANCG5WB3718KD9.news-treasury-v5";
22
+ /**
23
+ * Which chain a Stacks contract id lives on, from its c32 address prefix:
24
+ * SP/SM are mainnet, ST/SN are testnet.
25
+ */
26
+ export declare function networkOfContract(contractId: string): Network;
27
+ /** The chain the legion contracts are deployed on. */
28
+ export declare const LEGION_NETWORK: Network;
29
+ /**
30
+ * The viewer page for an inscription. This is the form every proposal on chain
31
+ * already stores, and the form a voter wants: clicking it opens the ordinals
32
+ * viewer rather than dumping raw markdown.
33
+ */
34
+ export declare const ORDINALS_INSCRIPTION_BASE = "https://ordinals.com/inscription/";
35
+ /** The raw bytes of an inscription — what a reader fetches to render the piece. */
36
+ export declare const ORDINALS_CONTENT_BASE = "https://ordinals.com/content/";
37
+ /** The public reader for this legion. */
38
+ export declare const LEGION_SITE_URL = "https://legions.aibtc.news";
39
+ /** `link` is `(string-ascii 200)`. */
40
+ export declare const MAX_LINK_LENGTH = 200;
41
+ /** `title` is `(string-ascii 128)`. */
42
+ export declare const MAX_TITLE_LENGTH = 128;
43
+ /** `description` is `(string-ascii 512)`. */
44
+ export declare const MAX_DESCRIPTION_LENGTH = 512;
45
+ /** `sponsor-in` `name` is `(string-ascii 40)`. */
46
+ export declare const MAX_SPONSOR_NAME_LENGTH = 40;
47
+ /** `sponsor-in` `link` is `(optional (string-ascii 96))`. */
48
+ export declare const MAX_SPONSOR_LINK_LENGTH = 96;
49
+ /** `sponsor-in` `memo` is `(string-ascii 128)`. */
50
+ export declare const MAX_SPONSOR_MEMO_LENGTH = 128;
51
+ /** Story status uints as stored by the gov contract. */
52
+ export declare const STORY_STATUS: Record<number, string>;
53
+ /**
54
+ * Contract error codes, so a revert reads as a sentence rather than a `u4xx`.
55
+ * Gov and treasury codes are disjoint, so one map covers both.
56
+ */
57
+ export declare const LEGION_ERRORS: Record<number, string>;
58
+ //# sourceMappingURL=legion.d.ts.map
@@ -0,0 +1,100 @@
1
+ /**
2
+ * AIBTC News Legion (news-gov-v5) configuration.
3
+ *
4
+ * Contribution-weighted governance for aibtc.news: agents inscribe a news piece
5
+ * to a Bitcoin ordinal, open ONE proposal naming that inscription, and the
6
+ * contributors vote on whether the piece is worth paying for. A passing piece
7
+ * pays its proposer a fixed slice of the sBTC pool.
8
+ *
9
+ * NETWORK IS DERIVED FROM THE CONTRACT ADDRESS, NOT FROM `NETWORK`.
10
+ * The legion is deployed on Stacks testnet. Every other tool in this server
11
+ * follows the global `NETWORK` env var, which defaults to mainnet — routing
12
+ * these calls through that would sign a testnet governance flow against real
13
+ * funds. `LEGION_NETWORK` reads the address prefix instead, so the legion tools
14
+ * sign against the chain the contracts actually live on no matter how the rest
15
+ * of the server is configured.
16
+ */
17
+ /** Governance contract: proposals, votes, vetoes, conclusion. */
18
+ export const LEGION_GOV_CONTRACT = "STGX5YP51NKM69ZMP6DVB6GAJAANCG5WB3718KD9.news-gov-v5-testnet";
19
+ /** Treasury contract: the sBTC pool. Every outflow is gov-gated. */
20
+ export const LEGION_TREASURY_CONTRACT = "STGX5YP51NKM69ZMP6DVB6GAJAANCG5WB3718KD9.news-treasury-v5";
21
+ /**
22
+ * Which chain a Stacks contract id lives on, from its c32 address prefix:
23
+ * SP/SM are mainnet, ST/SN are testnet.
24
+ */
25
+ export function networkOfContract(contractId) {
26
+ const prefix = contractId.slice(0, 2).toUpperCase();
27
+ if (prefix === "SP" || prefix === "SM")
28
+ return "mainnet";
29
+ if (prefix === "ST" || prefix === "SN")
30
+ return "testnet";
31
+ throw new Error(`Cannot derive a network from contract id "${contractId}": ` +
32
+ `expected an address starting with SP/SM (mainnet) or ST/SN (testnet).`);
33
+ }
34
+ /** The chain the legion contracts are deployed on. */
35
+ export const LEGION_NETWORK = networkOfContract(LEGION_GOV_CONTRACT);
36
+ /**
37
+ * The viewer page for an inscription. This is the form every proposal on chain
38
+ * already stores, and the form a voter wants: clicking it opens the ordinals
39
+ * viewer rather than dumping raw markdown.
40
+ */
41
+ export const ORDINALS_INSCRIPTION_BASE = "https://ordinals.com/inscription/";
42
+ /** The raw bytes of an inscription — what a reader fetches to render the piece. */
43
+ export const ORDINALS_CONTENT_BASE = "https://ordinals.com/content/";
44
+ /** The public reader for this legion. */
45
+ export const LEGION_SITE_URL = "https://legions.aibtc.news";
46
+ /** `link` is `(string-ascii 200)`. */
47
+ export const MAX_LINK_LENGTH = 200;
48
+ /** `title` is `(string-ascii 128)`. */
49
+ export const MAX_TITLE_LENGTH = 128;
50
+ /** `description` is `(string-ascii 512)`. */
51
+ export const MAX_DESCRIPTION_LENGTH = 512;
52
+ /** `sponsor-in` `name` is `(string-ascii 40)`. */
53
+ export const MAX_SPONSOR_NAME_LENGTH = 40;
54
+ /** `sponsor-in` `link` is `(optional (string-ascii 96))`. */
55
+ export const MAX_SPONSOR_LINK_LENGTH = 96;
56
+ /** `sponsor-in` `memo` is `(string-ascii 128)`. */
57
+ export const MAX_SPONSOR_MEMO_LENGTH = 128;
58
+ /** Story status uints as stored by the gov contract. */
59
+ export const STORY_STATUS = {
60
+ 0: "OPEN",
61
+ 1: "PASSED",
62
+ 2: "FAILED",
63
+ 3: "EXPIRED",
64
+ };
65
+ /**
66
+ * Contract error codes, so a revert reads as a sentence rather than a `u4xx`.
67
+ * Gov and treasury codes are disjoint, so one map covers both.
68
+ */
69
+ export const LEGION_ERRORS = {
70
+ 401: "ineligible — your weight is below minWeight (contribute more first)",
71
+ 402: "treasury has insufficient balance",
72
+ 403: "treasury already wired to a gov contract",
73
+ 404: "no proposal with that id",
74
+ 405: "you have already voted on this proposal",
75
+ 407: "voting has closed on this proposal",
76
+ 408: "too early to conclude — the veto window has not closed yet",
77
+ 409: "amount must be greater than zero",
78
+ 410: "this proposal is already concluded",
79
+ 411: "invalid payout recipient",
80
+ 413: "your free weight does not cover the bond",
81
+ 416: "this payout was already settled",
82
+ 417: "the treasury payout failed",
83
+ 418: "the pool is empty — nothing to draw against",
84
+ 419: "the draw would round to zero sats",
85
+ 421: "the ordinals link must not be empty",
86
+ 423: "a proposer cannot vote on their own piece",
87
+ 424: "outside the veto window",
88
+ 425: "you have already vetoed this proposal",
89
+ 426: "contribution too small to mint any weight",
90
+ 432: "too soon to propose — the global propose interval has not elapsed",
91
+ 433: "the title must not be empty",
92
+ 434: "you already have a live proposal (one at a time per principal)",
93
+ 435: "the conclude window has passed — this proposal has already expired",
94
+ 436: "voting has not opened yet (still in the pending period)",
95
+ 437: "contribution is below minContribution",
96
+ 450: "sponsorship is below the treasury's minimum sponsor amount",
97
+ 451: "the sponsor name must not be empty",
98
+ 452: "the treasury is not wired to a gov contract yet",
99
+ };
100
+ //# sourceMappingURL=legion.js.map
@@ -0,0 +1,188 @@
1
+ /**
2
+ * AIBTC News Legion (news-gov-v5) chain reads.
3
+ *
4
+ * Everything here reads the deployed contracts rather than trusting constants:
5
+ * governance parameters, the timing mode (which clock the windows count on),
6
+ * and the sBTC token the treasury actually holds all come off chain and are
7
+ * cached per process. A doc that drifts from the contract is a doc; a read that
8
+ * drifts from the contract is a bug, so there is nothing to drift.
9
+ */
10
+ import { type ClarityValue } from "@stacks/transactions";
11
+ import type { Account } from "../transactions/builder.js";
12
+ /**
13
+ * Deep-unwrap a ClarityValue into plain JS. `cvToValue` only unwraps the top
14
+ * level — nested tuples and optionals come back as `{type, value}` envelopes —
15
+ * so the legion's tuple-heavy views need this instead.
16
+ *
17
+ * uints/ints become strings to stay exact; callers narrow with `num()`.
18
+ */
19
+ export declare function simplifyCV(cv: ClarityValue): unknown;
20
+ /** A uint field from a simplified tuple, as a JS number. */
21
+ export declare function num(value: unknown): number;
22
+ /**
23
+ * Call a read-only function on a legion contract and return it deep-unwrapped.
24
+ * Always reads the legion's own chain, never the globally configured one.
25
+ */
26
+ export declare function readLegionContract(contractId: string, functionName: string, functionArgs?: ClarityValue[]): Promise<unknown>;
27
+ export declare function readGov(functionName: string, functionArgs?: ClarityValue[]): Promise<unknown>;
28
+ export declare function readTreasury(functionName: string, functionArgs?: ClarityValue[]): Promise<unknown>;
29
+ export interface LegionParams {
30
+ votingQuorum: number;
31
+ votingThreshold: number;
32
+ vetoQuorum: number;
33
+ minParticipants: number;
34
+ minWeight: number;
35
+ minContribution: number;
36
+ drawBps: number;
37
+ votingDelay: number;
38
+ voteWindow: number;
39
+ vetoWindow: number;
40
+ concludeWindow: number;
41
+ proposeInterval: number;
42
+ }
43
+ /** Every governance parameter, straight from `get-params`. */
44
+ export declare function getParams(): Promise<LegionParams>;
45
+ /**
46
+ * Which clock the windows count on. `"TEST-STACKS-BLOCKS"` counts Stacks
47
+ * blocks; `"PROD-BURN"` counts Bitcoin burn blocks. Never assume — a window
48
+ * measured against the wrong tip is off by a factor of ten.
49
+ */
50
+ export declare function getTimingMode(): Promise<string>;
51
+ /**
52
+ * The sBTC token the treasury actually moves, plus its fungible-token asset
53
+ * name — both needed to write an exact post-condition. Read from the treasury
54
+ * and its contract interface so a post-condition can never name the wrong asset.
55
+ */
56
+ export declare function getLegionToken(): Promise<{
57
+ contract: string;
58
+ assetName: string;
59
+ }>;
60
+ /**
61
+ * The height the contract's windows are measured against, on the clock
62
+ * `get-timing-mode` names.
63
+ */
64
+ export declare function getClockHeight(): Promise<{
65
+ height: number;
66
+ clock: "stacks" | "burn";
67
+ timingMode: string;
68
+ }>;
69
+ /**
70
+ * The unlocked wallet, re-derived for the legion's network.
71
+ *
72
+ * The same private key yields a different address per chain, so a mainnet
73
+ * session still signs legion calls from its testnet address against the testnet
74
+ * node. This is the guard that makes a mainnet-configured server safe here.
75
+ */
76
+ export declare function getLegionAccount(): Promise<Account>;
77
+ export interface StoryRecord {
78
+ proposer: string;
79
+ bond: number;
80
+ draw: number;
81
+ createdAt: number;
82
+ voteEnd: number;
83
+ eligibleSnapshot: number;
84
+ yesWeight: number;
85
+ noWeight: number;
86
+ vetoWeight: number;
87
+ voterCount: number;
88
+ status: number;
89
+ reason: string;
90
+ }
91
+ export interface StoryMeta {
92
+ title: string;
93
+ description: string;
94
+ link: string;
95
+ }
96
+ export declare function getStory(proposalId: number): Promise<StoryRecord | null>;
97
+ export declare function getStoryMeta(proposalId: number): Promise<StoryMeta | null>;
98
+ /**
99
+ * `"none" | "pending" | "voting" | "veto" | "concludable" | "expired" |
100
+ * "passed" | "failed"` — the contract's own view of where a piece stands.
101
+ */
102
+ export declare function getPhase(proposalId: number): Promise<string>;
103
+ export declare function getLastProposalId(): Promise<number>;
104
+ export declare function getVoteRecord(proposalId: number, voter: string): Promise<{
105
+ support: boolean;
106
+ weight: number;
107
+ } | null>;
108
+ export declare function getVetoRecord(proposalId: number, voter: string): Promise<{
109
+ weight: number;
110
+ } | null>;
111
+ export interface ProposeStatus {
112
+ canPropose: boolean;
113
+ eligible: boolean;
114
+ slotOpen: boolean;
115
+ noLiveProposal: boolean;
116
+ poolOk: boolean;
117
+ nextProposeHeight: number;
118
+ lockOnPropose: number;
119
+ draw: number;
120
+ freeWeight: number;
121
+ }
122
+ /** Every propose precondition folded into one read, with the individual gates. */
123
+ export declare function getProposeStatus(who: string): Promise<ProposeStatus>;
124
+ /** Why a propose would revert right now, in the caller's words. */
125
+ export declare function proposeBlockers(status: ProposeStatus, minWeight: number): string[];
126
+ export interface StoryTimeline {
127
+ createdAt: number;
128
+ votingOpensAt: number;
129
+ voteEnd: number;
130
+ vetoEnd: number;
131
+ concludeDeadline: number;
132
+ currentHeight: number;
133
+ clock: "stacks" | "burn";
134
+ blocksUntilNextTransition: number | null;
135
+ }
136
+ /** When each window opens and closes, on the clock the contract counts. */
137
+ export declare function buildTimeline(story: StoryRecord, params: LegionParams, height: number, clock: "stacks" | "burn"): StoryTimeline;
138
+ export interface PredictedOutcome {
139
+ outcome: "passed" | "failed" | "expired";
140
+ reason: string;
141
+ /** True when the outcome is what the contract already recorded, not a forecast. */
142
+ settled: boolean;
143
+ cast: number;
144
+ quorumPct: number;
145
+ yesPct: number;
146
+ vetoPct: number;
147
+ quorumMet: boolean;
148
+ thresholdMet: boolean;
149
+ vetoed: boolean;
150
+ participantsMet: boolean;
151
+ payout: number;
152
+ }
153
+ /**
154
+ * What `conclude` would decide if it ran at this height, following the
155
+ * contract's decision order exactly (integer division included, since Clarity
156
+ * floors every percentage).
157
+ *
158
+ * A story that is already terminal reports what the contract RECORDED, not a
159
+ * forecast. Forecasting a settled piece would be worse than useless: a passed
160
+ * proposal is necessarily past its conclude window, so the forecast branch
161
+ * would call every settled piece "expired".
162
+ */
163
+ export declare function predictOutcome(story: StoryRecord, params: LegionParams, poolBalance: number, height: number): PredictedOutcome;
164
+ /**
165
+ * Accept either a bare inscription id (`<txid>i0`) or a full URL, and return
166
+ * the link the contract should store.
167
+ *
168
+ * A bare id becomes the VIEWER url, not the content url: that is what every
169
+ * proposal already on chain uses, and it is what a voter wants from a click.
170
+ * The reader at legions.aibtc.news accepts either form and both bare ids.
171
+ */
172
+ export declare function normalizeOrdinalsLink(input: string): string;
173
+ /** The inscription id inside an ordinals link, when there is one. */
174
+ export declare function inscriptionIdFromLink(link: string): string | null;
175
+ /**
176
+ * The raw-bytes url for a stored link, so a voter can read the piece rather
177
+ * than just look at where it lives. Null when the link names no inscription.
178
+ */
179
+ export declare function contentUrlFromLink(link: string): string | null;
180
+ /**
181
+ * Clarity `string-ascii` holds one byte per character, so a smart quote or an
182
+ * em dash pasted into a headline is a revert, not a truncation. Catch it here
183
+ * with a message that names the character.
184
+ */
185
+ export declare function assertAscii(value: string, field: string, maxLength: number): void;
186
+ /** `(err uXXX)` from a failed read or a broadcast rejection, as a sentence. */
187
+ export declare function explainError(error: unknown, errors: Record<number, string>): string;
188
+ //# sourceMappingURL=legion.service.d.ts.map