@exagent/agent 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WTECTX2Z.js → chunk-ZRAOPQQW.js} +208 -147
- package/dist/cli.js +57 -37
- package/dist/index.js +1 -1
- package/package.json +18 -18
- package/src/cli.ts +32 -23
- package/src/config.ts +6 -3
- package/src/llm/anthropic.ts +3 -3
- package/src/llm/deepseek.ts +3 -3
- package/src/llm/google.ts +3 -3
- package/src/llm/groq.ts +3 -3
- package/src/llm/mistral.ts +3 -3
- package/src/llm/ollama.ts +3 -3
- package/src/llm/openai.ts +46 -3
- package/src/llm/together.ts +3 -3
- package/src/llm-providers.ts +8 -0
- package/src/prediction/client.ts +11 -4
- package/src/runtime.ts +3 -3
- package/src/setup.ts +29 -20
- package/src/strategy/loader.ts +136 -62
- package/src/strategy/templates.ts +0 -51
- package/test/strategy-loader.test.ts +150 -0
- package/.turbo/turbo-build.log +0 -17
- package/test-bridge-arb-to-base.mjs +0 -223
- package/test-funded-check.mjs +0 -79
- package/test-funded-phase19.mjs +0 -933
- package/test-hl-deposit-recover.mjs +0 -281
- package/test-hl-withdraw.mjs +0 -372
- package/test-live-signing.mjs +0 -374
- package/test-phase7.mjs +0 -416
- package/test-recover-arb.mjs +0 -206
- package/test-spot-bridge.mjs +0 -248
- package/test-wallet-setup.mjs +0 -126
|
@@ -4,7 +4,8 @@ import { chmodSync, existsSync, readFileSync, writeFileSync } from "fs";
|
|
|
4
4
|
import { homedir } from "os";
|
|
5
5
|
import { resolve } from "path";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
|
|
7
|
+
import { LLM_PROVIDER_IDS, providerRequiresApiKey } from "@exagent/sdk";
|
|
8
|
+
var providerEnum = z.enum(LLM_PROVIDER_IDS);
|
|
8
9
|
var runtimeSchema = z.object({
|
|
9
10
|
agentId: z.string(),
|
|
10
11
|
apiUrl: z.string().url(),
|
|
@@ -246,7 +247,8 @@ async function loadConfig(path = "agent-config.json", options = {}) {
|
|
|
246
247
|
if (process.env.EXAGENT_WALLET_PRIVATE_KEY) {
|
|
247
248
|
config.wallet = { privateKey: process.env.EXAGENT_WALLET_PRIVATE_KEY };
|
|
248
249
|
}
|
|
249
|
-
|
|
250
|
+
const llmNeedsApiKey = providerRequiresApiKey(String(llm.provider || ""));
|
|
251
|
+
if ((!config.apiToken || llmNeedsApiKey && !llm.apiKey || !config.wallet) && parsed.secrets?.secureStorePath) {
|
|
250
252
|
const password = process.env.EXAGENT_SECRET_PASSWORD || await options.getSecretPassword?.();
|
|
251
253
|
if (!password) {
|
|
252
254
|
throw new Error("Encrypted secret store found, but no password was provided.");
|
|
@@ -256,7 +258,7 @@ async function loadConfig(path = "agent-config.json", options = {}) {
|
|
|
256
258
|
if (!config.wallet && secrets.walletPrivateKey) config.wallet = { privateKey: secrets.walletPrivateKey };
|
|
257
259
|
if (!llm.apiKey && secrets.llmApiKey) llm.apiKey = secrets.llmApiKey;
|
|
258
260
|
}
|
|
259
|
-
if ((!config.apiToken || !llm.apiKey || !config.wallet) && parsed.secrets?.bootstrapToken && !parsed.secrets?.secureStorePath) {
|
|
261
|
+
if ((!config.apiToken || llmNeedsApiKey && !llm.apiKey || !config.wallet) && parsed.secrets?.bootstrapToken && !parsed.secrets?.secureStorePath) {
|
|
260
262
|
throw new Error(`Config ${path} still requires first-time secure setup. Run 'exagent setup --config ${path}' or start the agent interactively.`);
|
|
261
263
|
}
|
|
262
264
|
config.llm = llm;
|
|
@@ -1459,6 +1461,7 @@ var BaseLLMAdapter = class {
|
|
|
1459
1461
|
};
|
|
1460
1462
|
|
|
1461
1463
|
// src/llm/openai.ts
|
|
1464
|
+
import { getDefaultModel, shouldUseOpenAIResponses } from "@exagent/sdk";
|
|
1462
1465
|
var OpenAIAdapter = class extends BaseLLMAdapter {
|
|
1463
1466
|
endpoint;
|
|
1464
1467
|
constructor(config) {
|
|
@@ -1466,6 +1469,10 @@ var OpenAIAdapter = class extends BaseLLMAdapter {
|
|
|
1466
1469
|
this.endpoint = config.endpoint || "https://api.openai.com/v1";
|
|
1467
1470
|
}
|
|
1468
1471
|
async chatImpl(messages) {
|
|
1472
|
+
const model = this.config.model || getDefaultModel("openai");
|
|
1473
|
+
if (shouldUseOpenAIResponses(model)) {
|
|
1474
|
+
return this.chatResponses(model, messages);
|
|
1475
|
+
}
|
|
1469
1476
|
const res = await this.fetchWithTimeout(`${this.endpoint}/chat/completions`, {
|
|
1470
1477
|
method: "POST",
|
|
1471
1478
|
headers: {
|
|
@@ -1473,7 +1480,7 @@ var OpenAIAdapter = class extends BaseLLMAdapter {
|
|
|
1473
1480
|
Authorization: `Bearer ${this.config.apiKey}`
|
|
1474
1481
|
},
|
|
1475
1482
|
body: JSON.stringify({
|
|
1476
|
-
model
|
|
1483
|
+
model,
|
|
1477
1484
|
messages: messages.map((m) => ({ role: m.role, content: m.content })),
|
|
1478
1485
|
temperature: this.getTemperature(),
|
|
1479
1486
|
max_tokens: this.getMaxTokens()
|
|
@@ -1489,15 +1496,42 @@ var OpenAIAdapter = class extends BaseLLMAdapter {
|
|
|
1489
1496
|
tokens: data.usage ? { input: data.usage.prompt_tokens, output: data.usage.completion_tokens } : void 0
|
|
1490
1497
|
};
|
|
1491
1498
|
}
|
|
1499
|
+
async chatResponses(model, messages) {
|
|
1500
|
+
const res = await this.fetchWithTimeout(`${this.endpoint}/responses`, {
|
|
1501
|
+
method: "POST",
|
|
1502
|
+
headers: {
|
|
1503
|
+
"Content-Type": "application/json",
|
|
1504
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
1505
|
+
},
|
|
1506
|
+
body: JSON.stringify({
|
|
1507
|
+
model,
|
|
1508
|
+
input: messages.map((m) => ({
|
|
1509
|
+
role: m.role === "system" ? "developer" : m.role,
|
|
1510
|
+
content: m.content
|
|
1511
|
+
})),
|
|
1512
|
+
max_output_tokens: this.getMaxTokens()
|
|
1513
|
+
})
|
|
1514
|
+
});
|
|
1515
|
+
if (!res.ok) {
|
|
1516
|
+
const body = await res.text();
|
|
1517
|
+
throw new Error(`OpenAI API error ${res.status}: ${body}`);
|
|
1518
|
+
}
|
|
1519
|
+
const data = await res.json();
|
|
1520
|
+
return {
|
|
1521
|
+
content: data.output_text || data.output?.flatMap((item) => item.content?.map((content) => content.text || "") || []).join("") || "",
|
|
1522
|
+
tokens: data.usage ? { input: data.usage.input_tokens || 0, output: data.usage.output_tokens || 0 } : void 0
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1492
1525
|
getMetadata() {
|
|
1493
1526
|
return {
|
|
1494
1527
|
provider: "openai",
|
|
1495
|
-
model: this.config.model || "
|
|
1528
|
+
model: this.config.model || getDefaultModel("openai")
|
|
1496
1529
|
};
|
|
1497
1530
|
}
|
|
1498
1531
|
};
|
|
1499
1532
|
|
|
1500
1533
|
// src/llm/anthropic.ts
|
|
1534
|
+
import { getDefaultModel as getDefaultModel2 } from "@exagent/sdk";
|
|
1501
1535
|
var AnthropicAdapter = class extends BaseLLMAdapter {
|
|
1502
1536
|
endpoint;
|
|
1503
1537
|
constructor(config) {
|
|
@@ -1508,7 +1542,7 @@ var AnthropicAdapter = class extends BaseLLMAdapter {
|
|
|
1508
1542
|
const systemMessage = messages.find((m) => m.role === "system");
|
|
1509
1543
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
1510
1544
|
const body = {
|
|
1511
|
-
model: this.config.model || "
|
|
1545
|
+
model: this.config.model || getDefaultModel2("anthropic"),
|
|
1512
1546
|
messages: nonSystemMessages.map((m) => ({ role: m.role, content: m.content })),
|
|
1513
1547
|
max_tokens: this.getMaxTokens(),
|
|
1514
1548
|
temperature: this.getTemperature()
|
|
@@ -1539,18 +1573,19 @@ var AnthropicAdapter = class extends BaseLLMAdapter {
|
|
|
1539
1573
|
getMetadata() {
|
|
1540
1574
|
return {
|
|
1541
1575
|
provider: "anthropic",
|
|
1542
|
-
model: this.config.model || "
|
|
1576
|
+
model: this.config.model || getDefaultModel2("anthropic")
|
|
1543
1577
|
};
|
|
1544
1578
|
}
|
|
1545
1579
|
};
|
|
1546
1580
|
|
|
1547
1581
|
// src/llm/google.ts
|
|
1582
|
+
import { getDefaultModel as getDefaultModel3 } from "@exagent/sdk";
|
|
1548
1583
|
var GoogleAdapter = class extends BaseLLMAdapter {
|
|
1549
1584
|
constructor(config) {
|
|
1550
1585
|
super(config);
|
|
1551
1586
|
}
|
|
1552
1587
|
async chatImpl(messages) {
|
|
1553
|
-
const model = this.config.model || "
|
|
1588
|
+
const model = this.config.model || getDefaultModel3("google");
|
|
1554
1589
|
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${this.config.apiKey}`;
|
|
1555
1590
|
const systemMessage = messages.find((m) => m.role === "system");
|
|
1556
1591
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
@@ -1586,12 +1621,13 @@ var GoogleAdapter = class extends BaseLLMAdapter {
|
|
|
1586
1621
|
getMetadata() {
|
|
1587
1622
|
return {
|
|
1588
1623
|
provider: "google",
|
|
1589
|
-
model: this.config.model || "
|
|
1624
|
+
model: this.config.model || getDefaultModel3("google")
|
|
1590
1625
|
};
|
|
1591
1626
|
}
|
|
1592
1627
|
};
|
|
1593
1628
|
|
|
1594
1629
|
// src/llm/deepseek.ts
|
|
1630
|
+
import { getDefaultModel as getDefaultModel4 } from "@exagent/sdk";
|
|
1595
1631
|
var DeepSeekAdapter = class extends BaseLLMAdapter {
|
|
1596
1632
|
constructor(config) {
|
|
1597
1633
|
super(config);
|
|
@@ -1604,7 +1640,7 @@ var DeepSeekAdapter = class extends BaseLLMAdapter {
|
|
|
1604
1640
|
Authorization: `Bearer ${this.config.apiKey}`
|
|
1605
1641
|
},
|
|
1606
1642
|
body: JSON.stringify({
|
|
1607
|
-
model: this.config.model || "deepseek
|
|
1643
|
+
model: this.config.model || getDefaultModel4("deepseek"),
|
|
1608
1644
|
messages: messages.map((m) => ({ role: m.role, content: m.content })),
|
|
1609
1645
|
temperature: this.getTemperature(),
|
|
1610
1646
|
max_tokens: this.getMaxTokens()
|
|
@@ -1623,12 +1659,13 @@ var DeepSeekAdapter = class extends BaseLLMAdapter {
|
|
|
1623
1659
|
getMetadata() {
|
|
1624
1660
|
return {
|
|
1625
1661
|
provider: "deepseek",
|
|
1626
|
-
model: this.config.model || "deepseek
|
|
1662
|
+
model: this.config.model || getDefaultModel4("deepseek")
|
|
1627
1663
|
};
|
|
1628
1664
|
}
|
|
1629
1665
|
};
|
|
1630
1666
|
|
|
1631
1667
|
// src/llm/mistral.ts
|
|
1668
|
+
import { getDefaultModel as getDefaultModel5 } from "@exagent/sdk";
|
|
1632
1669
|
var MistralAdapter = class extends BaseLLMAdapter {
|
|
1633
1670
|
constructor(config) {
|
|
1634
1671
|
super(config);
|
|
@@ -1641,7 +1678,7 @@ var MistralAdapter = class extends BaseLLMAdapter {
|
|
|
1641
1678
|
Authorization: `Bearer ${this.config.apiKey}`
|
|
1642
1679
|
},
|
|
1643
1680
|
body: JSON.stringify({
|
|
1644
|
-
model: this.config.model || "mistral
|
|
1681
|
+
model: this.config.model || getDefaultModel5("mistral"),
|
|
1645
1682
|
messages: messages.map((m) => ({ role: m.role, content: m.content })),
|
|
1646
1683
|
temperature: this.getTemperature(),
|
|
1647
1684
|
max_tokens: this.getMaxTokens()
|
|
@@ -1660,12 +1697,13 @@ var MistralAdapter = class extends BaseLLMAdapter {
|
|
|
1660
1697
|
getMetadata() {
|
|
1661
1698
|
return {
|
|
1662
1699
|
provider: "mistral",
|
|
1663
|
-
model: this.config.model || "mistral
|
|
1700
|
+
model: this.config.model || getDefaultModel5("mistral")
|
|
1664
1701
|
};
|
|
1665
1702
|
}
|
|
1666
1703
|
};
|
|
1667
1704
|
|
|
1668
1705
|
// src/llm/groq.ts
|
|
1706
|
+
import { getDefaultModel as getDefaultModel6 } from "@exagent/sdk";
|
|
1669
1707
|
var GroqAdapter = class extends BaseLLMAdapter {
|
|
1670
1708
|
constructor(config) {
|
|
1671
1709
|
super(config);
|
|
@@ -1678,7 +1716,7 @@ var GroqAdapter = class extends BaseLLMAdapter {
|
|
|
1678
1716
|
Authorization: `Bearer ${this.config.apiKey}`
|
|
1679
1717
|
},
|
|
1680
1718
|
body: JSON.stringify({
|
|
1681
|
-
model: this.config.model || "
|
|
1719
|
+
model: this.config.model || getDefaultModel6("groq"),
|
|
1682
1720
|
messages: messages.map((m) => ({ role: m.role, content: m.content })),
|
|
1683
1721
|
temperature: this.getTemperature(),
|
|
1684
1722
|
max_tokens: this.getMaxTokens()
|
|
@@ -1697,12 +1735,13 @@ var GroqAdapter = class extends BaseLLMAdapter {
|
|
|
1697
1735
|
getMetadata() {
|
|
1698
1736
|
return {
|
|
1699
1737
|
provider: "groq",
|
|
1700
|
-
model: this.config.model || "
|
|
1738
|
+
model: this.config.model || getDefaultModel6("groq")
|
|
1701
1739
|
};
|
|
1702
1740
|
}
|
|
1703
1741
|
};
|
|
1704
1742
|
|
|
1705
1743
|
// src/llm/together.ts
|
|
1744
|
+
import { getDefaultModel as getDefaultModel7 } from "@exagent/sdk";
|
|
1706
1745
|
var TogetherAdapter = class extends BaseLLMAdapter {
|
|
1707
1746
|
constructor(config) {
|
|
1708
1747
|
super(config);
|
|
@@ -1715,7 +1754,7 @@ var TogetherAdapter = class extends BaseLLMAdapter {
|
|
|
1715
1754
|
Authorization: `Bearer ${this.config.apiKey}`
|
|
1716
1755
|
},
|
|
1717
1756
|
body: JSON.stringify({
|
|
1718
|
-
model: this.config.model || "
|
|
1757
|
+
model: this.config.model || getDefaultModel7("together"),
|
|
1719
1758
|
messages: messages.map((m) => ({ role: m.role, content: m.content })),
|
|
1720
1759
|
temperature: this.getTemperature(),
|
|
1721
1760
|
max_tokens: this.getMaxTokens()
|
|
@@ -1734,12 +1773,13 @@ var TogetherAdapter = class extends BaseLLMAdapter {
|
|
|
1734
1773
|
getMetadata() {
|
|
1735
1774
|
return {
|
|
1736
1775
|
provider: "together",
|
|
1737
|
-
model: this.config.model || "
|
|
1776
|
+
model: this.config.model || getDefaultModel7("together")
|
|
1738
1777
|
};
|
|
1739
1778
|
}
|
|
1740
1779
|
};
|
|
1741
1780
|
|
|
1742
1781
|
// src/llm/ollama.ts
|
|
1782
|
+
import { getDefaultModel as getDefaultModel8 } from "@exagent/sdk";
|
|
1743
1783
|
var OllamaAdapter = class extends BaseLLMAdapter {
|
|
1744
1784
|
endpoint;
|
|
1745
1785
|
constructor(config) {
|
|
@@ -1751,7 +1791,7 @@ var OllamaAdapter = class extends BaseLLMAdapter {
|
|
|
1751
1791
|
method: "POST",
|
|
1752
1792
|
headers: { "Content-Type": "application/json" },
|
|
1753
1793
|
body: JSON.stringify({
|
|
1754
|
-
model: this.config.model || "
|
|
1794
|
+
model: this.config.model || getDefaultModel8("ollama"),
|
|
1755
1795
|
messages: messages.map((m) => ({ role: m.role, content: m.content })),
|
|
1756
1796
|
stream: false,
|
|
1757
1797
|
options: {
|
|
@@ -1773,7 +1813,7 @@ var OllamaAdapter = class extends BaseLLMAdapter {
|
|
|
1773
1813
|
getMetadata() {
|
|
1774
1814
|
return {
|
|
1775
1815
|
provider: "ollama",
|
|
1776
|
-
model: this.config.model || "
|
|
1816
|
+
model: this.config.model || getDefaultModel8("ollama")
|
|
1777
1817
|
};
|
|
1778
1818
|
}
|
|
1779
1819
|
};
|
|
@@ -1820,24 +1860,7 @@ Rules:
|
|
|
1820
1860
|
- Cut losses quickly if momentum reverses
|
|
1821
1861
|
- Consider volume as confirmation of momentum
|
|
1822
1862
|
|
|
1823
|
-
Return a JSON array of trade signals
|
|
1824
|
-
code: `
|
|
1825
|
-
const prices = context.market.getPrices();
|
|
1826
|
-
const positions = context.position.openPositions;
|
|
1827
|
-
|
|
1828
|
-
const messages = [
|
|
1829
|
-
{ role: 'system', content: 'You are a momentum trading agent. Analyze market data and return trade signals as a JSON array. Each signal: { symbol, side: "buy"|"sell", confidence: 0-1, reasoning }. Return [] if no opportunities.' },
|
|
1830
|
-
{ role: 'user', content: 'Current prices: ' + JSON.stringify(prices) + '\\nOpen positions: ' + JSON.stringify(positions.map(p => p.token)) + '\\nAnalyze for momentum opportunities.' }
|
|
1831
|
-
];
|
|
1832
|
-
|
|
1833
|
-
const response = await context.llm.chat(messages);
|
|
1834
|
-
try {
|
|
1835
|
-
const signals = JSON.parse(response.content);
|
|
1836
|
-
return Array.isArray(signals) ? signals : [];
|
|
1837
|
-
} catch {
|
|
1838
|
-
return [];
|
|
1839
|
-
}
|
|
1840
|
-
`
|
|
1863
|
+
Return a JSON array of trade signals.`
|
|
1841
1864
|
},
|
|
1842
1865
|
{
|
|
1843
1866
|
id: "value",
|
|
@@ -1855,24 +1878,7 @@ Rules:
|
|
|
1855
1878
|
- Avoid chasing pumps
|
|
1856
1879
|
- Diversify across sectors
|
|
1857
1880
|
|
|
1858
|
-
Return a JSON array of trade signals
|
|
1859
|
-
code: `
|
|
1860
|
-
const prices = context.market.getPrices();
|
|
1861
|
-
const positions = context.position.openPositions;
|
|
1862
|
-
|
|
1863
|
-
const messages = [
|
|
1864
|
-
{ role: 'system', content: 'You are a value investing agent. Identify undervalued tokens. Return trade signals as JSON array: { symbol, side: "buy"|"sell", confidence: 0-1, reasoning }. Return [] if nothing is compelling.' },
|
|
1865
|
-
{ role: 'user', content: 'Current prices: ' + JSON.stringify(prices) + '\\nPositions: ' + JSON.stringify(positions.map(p => ({ token: p.token, entry: p.costBasisPerUnit }))) + '\\nLook for value opportunities.' }
|
|
1866
|
-
];
|
|
1867
|
-
|
|
1868
|
-
const response = await context.llm.chat(messages);
|
|
1869
|
-
try {
|
|
1870
|
-
const signals = JSON.parse(response.content);
|
|
1871
|
-
return Array.isArray(signals) ? signals : [];
|
|
1872
|
-
} catch {
|
|
1873
|
-
return [];
|
|
1874
|
-
}
|
|
1875
|
-
`
|
|
1881
|
+
Return a JSON array of trade signals.`
|
|
1876
1882
|
},
|
|
1877
1883
|
{
|
|
1878
1884
|
id: "arbitrage",
|
|
@@ -1889,23 +1895,7 @@ Rules:
|
|
|
1889
1895
|
- Only trade when net profit > 0.5% after all costs
|
|
1890
1896
|
- Monitor cross-chain opportunities (CEX vs DEX spreads)
|
|
1891
1897
|
|
|
1892
|
-
Return a JSON array of trade signals
|
|
1893
|
-
code: `
|
|
1894
|
-
const prices = context.market.getPrices();
|
|
1895
|
-
|
|
1896
|
-
const messages = [
|
|
1897
|
-
{ role: 'system', content: 'You are an arbitrage agent. Find price discrepancies. Return trade signals as JSON array: { symbol, side: "buy"|"sell", venue, confidence: 0-1, reasoning }. Return [] if no arb opportunities.' },
|
|
1898
|
-
{ role: 'user', content: 'Market prices: ' + JSON.stringify(prices) + '\\nScan for cross-venue arbitrage opportunities.' }
|
|
1899
|
-
];
|
|
1900
|
-
|
|
1901
|
-
const response = await context.llm.chat(messages);
|
|
1902
|
-
try {
|
|
1903
|
-
const signals = JSON.parse(response.content);
|
|
1904
|
-
return Array.isArray(signals) ? signals : [];
|
|
1905
|
-
} catch {
|
|
1906
|
-
return [];
|
|
1907
|
-
}
|
|
1908
|
-
`
|
|
1898
|
+
Return a JSON array of trade signals.`
|
|
1909
1899
|
},
|
|
1910
1900
|
{
|
|
1911
1901
|
id: "hold",
|
|
@@ -1914,8 +1904,7 @@ try {
|
|
|
1914
1904
|
category: "custom",
|
|
1915
1905
|
venues: [],
|
|
1916
1906
|
riskLevel: "conservative",
|
|
1917
|
-
systemPrompt: ""
|
|
1918
|
-
code: `return [];`
|
|
1907
|
+
systemPrompt: ""
|
|
1919
1908
|
}
|
|
1920
1909
|
];
|
|
1921
1910
|
function getTemplate(id) {
|
|
@@ -1926,8 +1915,8 @@ function listTemplates() {
|
|
|
1926
1915
|
}
|
|
1927
1916
|
|
|
1928
1917
|
// src/strategy/loader.ts
|
|
1929
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1930
|
-
import { resolve as resolve2 } from "path";
|
|
1918
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
1919
|
+
import { extname, resolve as resolve2 } from "path";
|
|
1931
1920
|
import { z as z2 } from "zod";
|
|
1932
1921
|
|
|
1933
1922
|
// src/scrub-secrets.ts
|
|
@@ -1957,29 +1946,36 @@ function scrubSecrets(text) {
|
|
|
1957
1946
|
}
|
|
1958
1947
|
|
|
1959
1948
|
// src/strategy/loader.ts
|
|
1949
|
+
var MAX_PROMPT_SIGNALS = 10;
|
|
1950
|
+
var SUPPORTED_FILE_EXPORTS = /* @__PURE__ */ new Set(["name", "description", "category", "venues", "systemPrompt", "template"]);
|
|
1960
1951
|
var promptSignalSchema = z2.object({
|
|
1961
|
-
symbol: z2.string().min(1),
|
|
1952
|
+
symbol: z2.string().min(1).max(80),
|
|
1962
1953
|
side: z2.enum(["buy", "sell", "long", "short"]),
|
|
1963
1954
|
confidence: z2.number().min(0).max(1).optional(),
|
|
1964
|
-
reasoning: z2.string().optional(),
|
|
1965
|
-
venue: z2.string().optional(),
|
|
1966
|
-
chain: z2.string().optional(),
|
|
1955
|
+
reasoning: z2.string().max(1e3).optional(),
|
|
1956
|
+
venue: z2.string().min(1).max(80).optional(),
|
|
1957
|
+
chain: z2.string().min(1).max(80).optional(),
|
|
1967
1958
|
size: z2.number().positive().optional(),
|
|
1968
1959
|
price: z2.number().positive().optional(),
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1960
|
+
leverage: z2.number().positive().max(100).optional(),
|
|
1961
|
+
orderType: z2.enum(["market", "limit", "yes", "no"]).optional()
|
|
1962
|
+
}).strict();
|
|
1963
|
+
var promptSignalArraySchema = z2.array(promptSignalSchema).max(MAX_PROMPT_SIGNALS);
|
|
1964
|
+
var strategyMetadataSchema = z2.object({
|
|
1965
|
+
name: z2.string().min(1).max(120).optional(),
|
|
1966
|
+
description: z2.string().max(1e3).optional(),
|
|
1967
|
+
category: z2.string().max(80).optional(),
|
|
1968
|
+
venues: z2.array(z2.string().min(1).max(80)).max(20).optional(),
|
|
1969
|
+
systemPrompt: z2.string().min(1).max(12e3).optional(),
|
|
1970
|
+
template: z2.string().min(1).max(80).optional()
|
|
1971
|
+
}).strict();
|
|
1976
1972
|
async function loadStrategy(config) {
|
|
1973
|
+
if (config.code?.trim()) {
|
|
1974
|
+
throw new Error("Raw JavaScript strategy code is disabled. Use a template or prompt-backed strategy instead.");
|
|
1975
|
+
}
|
|
1977
1976
|
if (config.file) {
|
|
1978
1977
|
return loadFromFile(config.file);
|
|
1979
1978
|
}
|
|
1980
|
-
if (config.code) {
|
|
1981
|
-
return loadFromCode(config.code);
|
|
1982
|
-
}
|
|
1983
1979
|
if (config.prompt) {
|
|
1984
1980
|
return loadFromPrompt(config.prompt);
|
|
1985
1981
|
}
|
|
@@ -1988,7 +1984,14 @@ async function loadStrategy(config) {
|
|
|
1988
1984
|
if (!template) {
|
|
1989
1985
|
throw new Error(`Unknown strategy template: ${config.template}. Available: momentum, value, arbitrage, hold`);
|
|
1990
1986
|
}
|
|
1991
|
-
|
|
1987
|
+
if (!template.systemPrompt.trim()) {
|
|
1988
|
+
return holdStrategy;
|
|
1989
|
+
}
|
|
1990
|
+
return loadFromPrompt({
|
|
1991
|
+
name: template.name,
|
|
1992
|
+
systemPrompt: template.systemPrompt,
|
|
1993
|
+
venues: template.venues
|
|
1994
|
+
});
|
|
1992
1995
|
}
|
|
1993
1996
|
return holdStrategy;
|
|
1994
1997
|
}
|
|
@@ -1998,41 +2001,34 @@ async function loadFromFile(filePath) {
|
|
|
1998
2001
|
throw new Error(`Strategy file not found: ${resolved}`);
|
|
1999
2002
|
}
|
|
2000
2003
|
try {
|
|
2001
|
-
const
|
|
2002
|
-
const
|
|
2003
|
-
if (
|
|
2004
|
-
|
|
2005
|
-
|
|
2004
|
+
const source = readFileSync3(resolved, "utf8");
|
|
2005
|
+
const metadata = extname(resolved) === ".json" ? strategyMetadataSchema.parse(JSON.parse(source)) : parseStrategyMetadataModule(source, resolved);
|
|
2006
|
+
if (metadata.systemPrompt?.trim()) {
|
|
2007
|
+
return loadFromPrompt({
|
|
2008
|
+
name: metadata.name,
|
|
2009
|
+
systemPrompt: metadata.systemPrompt,
|
|
2010
|
+
venues: metadata.venues
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
if (metadata.template?.trim()) {
|
|
2014
|
+
const template = getTemplate(metadata.template);
|
|
2015
|
+
if (!template) {
|
|
2016
|
+
throw new Error(`Unknown strategy template: ${metadata.template}. Available: momentum, value, arbitrage, hold`);
|
|
2006
2017
|
}
|
|
2007
|
-
if (
|
|
2008
|
-
|
|
2009
|
-
const name = typeof mod.name === "string" ? mod.name : void 0;
|
|
2010
|
-
return loadFromPrompt({
|
|
2011
|
-
name,
|
|
2012
|
-
systemPrompt: mod.systemPrompt,
|
|
2013
|
-
venues
|
|
2014
|
-
});
|
|
2018
|
+
if (!template.systemPrompt.trim()) {
|
|
2019
|
+
return holdStrategy;
|
|
2015
2020
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
return loadFromCode(template.code);
|
|
2022
|
-
}
|
|
2023
|
-
throw new Error(`Strategy file must export a default function, 'strategy' function, 'code' string, 'systemPrompt' string, or 'template' string`);
|
|
2021
|
+
return loadFromPrompt({
|
|
2022
|
+
name: template.name,
|
|
2023
|
+
systemPrompt: template.systemPrompt,
|
|
2024
|
+
venues: metadata.venues?.length ? metadata.venues : template.venues
|
|
2025
|
+
});
|
|
2024
2026
|
}
|
|
2025
|
-
|
|
2027
|
+
throw new Error("Strategy file must define a systemPrompt or template. Executable strategy files are disabled.");
|
|
2026
2028
|
} catch (err) {
|
|
2027
2029
|
throw new Error(`Failed to load strategy from ${resolved}: ${err.message}`);
|
|
2028
2030
|
}
|
|
2029
2031
|
}
|
|
2030
|
-
async function loadFromCode(code) {
|
|
2031
|
-
const AsyncFunction = Object.getPrototypeOf(async function() {
|
|
2032
|
-
}).constructor;
|
|
2033
|
-
const fn = new AsyncFunction("context", code);
|
|
2034
|
-
return fn;
|
|
2035
|
-
}
|
|
2036
2032
|
function loadFromPrompt(config) {
|
|
2037
2033
|
return async (context) => {
|
|
2038
2034
|
const prices = context.market.getPrices();
|
|
@@ -2043,30 +2039,40 @@ function loadFromPrompt(config) {
|
|
|
2043
2039
|
venue: position.venue,
|
|
2044
2040
|
chain: position.chain
|
|
2045
2041
|
}));
|
|
2042
|
+
const allowedVenues = config.venues?.filter(Boolean) ?? [];
|
|
2046
2043
|
const response = await context.llm.chat([
|
|
2047
2044
|
{ role: "system", content: config.systemPrompt },
|
|
2048
2045
|
{
|
|
2049
2046
|
role: "user",
|
|
2050
2047
|
content: [
|
|
2051
2048
|
`Strategy: ${config.name || "Prompt Strategy"}`,
|
|
2052
|
-
`Allowed venues: ${
|
|
2049
|
+
`Allowed venues: ${allowedVenues.join(", ") || "none configured"}`,
|
|
2053
2050
|
`Current prices: ${JSON.stringify(prices)}`,
|
|
2054
2051
|
`Open positions: ${JSON.stringify(positions)}`,
|
|
2055
2052
|
`Risk config: ${JSON.stringify(context.config)}`,
|
|
2056
|
-
"Return ONLY a JSON array of trade
|
|
2053
|
+
"Return ONLY a JSON array of trade intents. Do not include executable code. Do not invent fill IDs."
|
|
2057
2054
|
].join("\n")
|
|
2058
2055
|
}
|
|
2059
2056
|
]);
|
|
2060
2057
|
const scrubbedContent = scrubSecrets(response.content);
|
|
2061
|
-
const
|
|
2062
|
-
if (!
|
|
2058
|
+
const jsonPayload = extractJsonArray(scrubbedContent);
|
|
2059
|
+
if (!jsonPayload) {
|
|
2063
2060
|
context.log("Prompt strategy returned a non-JSON response; no signals emitted.");
|
|
2064
2061
|
return [];
|
|
2065
2062
|
}
|
|
2066
2063
|
try {
|
|
2067
|
-
const parsed = promptSignalArraySchema.parse(JSON.parse(
|
|
2064
|
+
const parsed = promptSignalArraySchema.parse(JSON.parse(jsonPayload));
|
|
2068
2065
|
const signals = [];
|
|
2069
2066
|
for (const signal of parsed) {
|
|
2067
|
+
const venue = signal.venue || allowedVenues[0];
|
|
2068
|
+
if (!venue) {
|
|
2069
|
+
context.log(`Prompt strategy skipped ${signal.symbol}: no allowed venue configured.`);
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
2072
|
+
if (allowedVenues.length > 0 && !allowedVenues.includes(venue)) {
|
|
2073
|
+
context.log(`Prompt strategy skipped ${signal.symbol}: venue ${venue} is not allowed.`);
|
|
2074
|
+
continue;
|
|
2075
|
+
}
|
|
2070
2076
|
const price = signal.price ?? prices[signal.symbol.toUpperCase()];
|
|
2071
2077
|
if (!price || price <= 0) {
|
|
2072
2078
|
context.log(`Prompt strategy skipped ${signal.symbol}: no usable price in response or market cache.`);
|
|
@@ -2077,13 +2083,13 @@ function loadFromPrompt(config) {
|
|
|
2077
2083
|
side: signal.side,
|
|
2078
2084
|
confidence: signal.confidence ?? 0.5,
|
|
2079
2085
|
reasoning: signal.reasoning,
|
|
2080
|
-
venue
|
|
2086
|
+
venue,
|
|
2081
2087
|
chain: signal.chain,
|
|
2082
|
-
size: signal.size ??
|
|
2088
|
+
size: signal.size ?? 0,
|
|
2083
2089
|
price,
|
|
2084
|
-
fee:
|
|
2085
|
-
venueFillId:
|
|
2086
|
-
venueTimestamp:
|
|
2090
|
+
fee: 0,
|
|
2091
|
+
venueFillId: "",
|
|
2092
|
+
venueTimestamp: "",
|
|
2087
2093
|
leverage: signal.leverage,
|
|
2088
2094
|
orderType: signal.orderType
|
|
2089
2095
|
});
|
|
@@ -2095,6 +2101,54 @@ function loadFromPrompt(config) {
|
|
|
2095
2101
|
}
|
|
2096
2102
|
};
|
|
2097
2103
|
}
|
|
2104
|
+
function extractJsonArray(content) {
|
|
2105
|
+
const trimmed = content.trim();
|
|
2106
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
2107
|
+
return trimmed;
|
|
2108
|
+
}
|
|
2109
|
+
const start = trimmed.indexOf("[");
|
|
2110
|
+
const end = trimmed.lastIndexOf("]");
|
|
2111
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
2112
|
+
return null;
|
|
2113
|
+
}
|
|
2114
|
+
return trimmed.slice(start, end + 1);
|
|
2115
|
+
}
|
|
2116
|
+
function parseStrategyMetadataModule(source, filePath) {
|
|
2117
|
+
const values = {};
|
|
2118
|
+
let statement = "";
|
|
2119
|
+
for (const rawLine of source.replace(/\r\n/g, "\n").split("\n")) {
|
|
2120
|
+
const line = rawLine.trim();
|
|
2121
|
+
if (!line || line.startsWith("//")) {
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2124
|
+
statement = statement ? `${statement}
|
|
2125
|
+
${rawLine}` : rawLine;
|
|
2126
|
+
if (!line.endsWith(";")) {
|
|
2127
|
+
continue;
|
|
2128
|
+
}
|
|
2129
|
+
const match = statement.match(/^\s*export\s+const\s+([A-Za-z_$][\w$]*)\s*=\s*([\s\S]*);\s*$/);
|
|
2130
|
+
if (!match) {
|
|
2131
|
+
throw new Error(`Unsupported statement in ${filePath}. Strategy files may only export JSON constants.`);
|
|
2132
|
+
}
|
|
2133
|
+
const [, name, rawValue] = match;
|
|
2134
|
+
if (name === "code") {
|
|
2135
|
+
throw new Error("Raw JavaScript strategy code is disabled. Replace code with systemPrompt.");
|
|
2136
|
+
}
|
|
2137
|
+
if (!SUPPORTED_FILE_EXPORTS.has(name)) {
|
|
2138
|
+
throw new Error(`Unsupported strategy export "${name}".`);
|
|
2139
|
+
}
|
|
2140
|
+
try {
|
|
2141
|
+
values[name] = JSON.parse(rawValue.trim());
|
|
2142
|
+
} catch {
|
|
2143
|
+
throw new Error(`Strategy export "${name}" must be a JSON literal.`);
|
|
2144
|
+
}
|
|
2145
|
+
statement = "";
|
|
2146
|
+
}
|
|
2147
|
+
if (statement.trim()) {
|
|
2148
|
+
throw new Error(`Unterminated export in ${filePath}.`);
|
|
2149
|
+
}
|
|
2150
|
+
return strategyMetadataSchema.parse(values);
|
|
2151
|
+
}
|
|
2098
2152
|
var holdStrategy = async (_context) => {
|
|
2099
2153
|
return [];
|
|
2100
2154
|
};
|
|
@@ -3660,7 +3714,9 @@ function decodePredictionInstrument(instrument) {
|
|
|
3660
3714
|
|
|
3661
3715
|
// src/prediction/client.ts
|
|
3662
3716
|
import { ClobClient } from "@polymarket/clob-client";
|
|
3663
|
-
import {
|
|
3717
|
+
import { createWalletClient, http } from "viem";
|
|
3718
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
3719
|
+
import { polygon as polygon2 } from "viem/chains";
|
|
3664
3720
|
var PolymarketClient = class {
|
|
3665
3721
|
config;
|
|
3666
3722
|
clobClient = null;
|
|
@@ -3671,8 +3727,13 @@ var PolymarketClient = class {
|
|
|
3671
3727
|
CACHE_TTL_MS = 6e4;
|
|
3672
3728
|
constructor(privateKey, config) {
|
|
3673
3729
|
this.config = { ...DEFAULT_PREDICTION_CONFIG, ...config };
|
|
3674
|
-
|
|
3675
|
-
this.
|
|
3730
|
+
const account = privateKeyToAccount(privateKey);
|
|
3731
|
+
this.signer = createWalletClient({
|
|
3732
|
+
account,
|
|
3733
|
+
chain: polygon2,
|
|
3734
|
+
transport: http()
|
|
3735
|
+
});
|
|
3736
|
+
this.walletAddress = account.address;
|
|
3676
3737
|
}
|
|
3677
3738
|
// ── INITIALIZATION ─────────────────────────────────────────
|
|
3678
3739
|
async initialize() {
|
|
@@ -4078,18 +4139,18 @@ var PolymarketOrderManager = class {
|
|
|
4078
4139
|
// src/spot/client.ts
|
|
4079
4140
|
import {
|
|
4080
4141
|
createPublicClient,
|
|
4081
|
-
createWalletClient,
|
|
4082
|
-
http,
|
|
4142
|
+
createWalletClient as createWalletClient2,
|
|
4143
|
+
http as http2,
|
|
4083
4144
|
maxUint256
|
|
4084
4145
|
} from "viem";
|
|
4085
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
4146
|
+
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
4086
4147
|
var SpotDEXClient = class {
|
|
4087
4148
|
account;
|
|
4088
4149
|
clients = /* @__PURE__ */ new Map();
|
|
4089
4150
|
decimalsCache = /* @__PURE__ */ new Map();
|
|
4090
4151
|
symbolCache = /* @__PURE__ */ new Map();
|
|
4091
4152
|
constructor(privateKey) {
|
|
4092
|
-
this.account =
|
|
4153
|
+
this.account = privateKeyToAccount2(privateKey);
|
|
4093
4154
|
}
|
|
4094
4155
|
get address() {
|
|
4095
4156
|
return this.account.address;
|
|
@@ -4103,12 +4164,12 @@ var SpotDEXClient = class {
|
|
|
4103
4164
|
}
|
|
4104
4165
|
const publicClient = createPublicClient({
|
|
4105
4166
|
chain: chainConfig.viemChain,
|
|
4106
|
-
transport:
|
|
4167
|
+
transport: http2(chainConfig.rpcUrl)
|
|
4107
4168
|
});
|
|
4108
|
-
const walletClient =
|
|
4169
|
+
const walletClient = createWalletClient2({
|
|
4109
4170
|
account: this.account,
|
|
4110
4171
|
chain: chainConfig.viemChain,
|
|
4111
|
-
transport:
|
|
4172
|
+
transport: http2(chainConfig.rpcUrl)
|
|
4112
4173
|
});
|
|
4113
4174
|
const entry = { publicClient, walletClient, chainConfig };
|
|
4114
4175
|
this.clients.set(chainName, entry);
|
|
@@ -4868,12 +4929,12 @@ var BridgeManager = class {
|
|
|
4868
4929
|
};
|
|
4869
4930
|
|
|
4870
4931
|
// src/runtime.ts
|
|
4871
|
-
import { createWalletClient as
|
|
4872
|
-
import { privateKeyToAccount as
|
|
4932
|
+
import { createWalletClient as createWalletClient3, http as http3, formatUnits } from "viem";
|
|
4933
|
+
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
4873
4934
|
import { arbitrum as arbitrum2 } from "viem/chains";
|
|
4874
4935
|
import { createRequire } from "module";
|
|
4875
4936
|
var _require = createRequire(import.meta.url);
|
|
4876
|
-
var SDK_VERSION = "0.3.
|
|
4937
|
+
var SDK_VERSION = "0.3.5";
|
|
4877
4938
|
try {
|
|
4878
4939
|
SDK_VERSION = _require("../package.json").version;
|
|
4879
4940
|
} catch {
|
|
@@ -5180,7 +5241,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5180
5241
|
description ? `Strategy description: ${description}` : "Strategy description: follow the owner-configured strategy exactly and do not improvise outside it.",
|
|
5181
5242
|
`Allowed venues: ${venues}.`,
|
|
5182
5243
|
"Stay inside the owner-configured scope. If the setup is unclear or the trade does not fit, return no trades.",
|
|
5183
|
-
"Return ONLY a JSON array of trade
|
|
5244
|
+
"Return ONLY a JSON array of trade intents."
|
|
5184
5245
|
].filter((line) => Boolean(line)).join("\n");
|
|
5185
5246
|
}
|
|
5186
5247
|
extractStrategyConfigFromAgentConfig(cfg) {
|
|
@@ -5193,7 +5254,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5193
5254
|
return { file: rawStrategy.file, venues };
|
|
5194
5255
|
}
|
|
5195
5256
|
if (typeof rawStrategy.code === "string" && rawStrategy.code.trim()) {
|
|
5196
|
-
|
|
5257
|
+
throw new Error("Raw JavaScript strategy code is disabled. Use a prompt-backed strategy or template instead.");
|
|
5197
5258
|
}
|
|
5198
5259
|
if (typeof rawStrategy.systemPrompt === "string" && rawStrategy.systemPrompt.trim()) {
|
|
5199
5260
|
return {
|
|
@@ -5245,16 +5306,16 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5245
5306
|
log.warn("runtime", "No wallet configured \u2014 live trading disabled");
|
|
5246
5307
|
return;
|
|
5247
5308
|
}
|
|
5248
|
-
const walletAccount =
|
|
5309
|
+
const walletAccount = privateKeyToAccount3(privateKey);
|
|
5249
5310
|
this.walletAddress = walletAccount.address;
|
|
5250
5311
|
const hlConfig = this.config.venues?.hyperliquid_perp;
|
|
5251
5312
|
if (hlConfig?.enabled) {
|
|
5252
5313
|
try {
|
|
5253
5314
|
const account = walletAccount;
|
|
5254
|
-
const walletClient =
|
|
5315
|
+
const walletClient = createWalletClient3({
|
|
5255
5316
|
account,
|
|
5256
5317
|
chain: arbitrum2,
|
|
5257
|
-
transport:
|
|
5318
|
+
transport: http3()
|
|
5258
5319
|
});
|
|
5259
5320
|
const perpConfig = {
|
|
5260
5321
|
enabled: true,
|