@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.
@@ -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
- var providerEnum = z.enum(["openai", "anthropic", "google", "deepseek", "mistral", "groq", "together", "ollama"]);
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
- if ((!config.apiToken || !llm.apiKey || !config.wallet) && parsed.secrets?.secureStorePath) {
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: this.config.model || "gpt-4o",
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 || "gpt-4o"
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 || "claude-sonnet-4-20250514",
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 || "claude-sonnet-4-20250514"
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 || "gemini-2.5-flash";
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 || "gemini-2.5-flash"
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-chat",
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-chat"
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-large-latest",
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-large-latest"
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 || "llama-3.3-70b-versatile",
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 || "llama-3.3-70b-versatile"
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 || "meta-llama/Llama-3.3-70B-Instruct-Turbo",
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 || "meta-llama/Llama-3.3-70B-Instruct-Turbo"
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 || "llama3.3",
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 || "llama3.3"
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
- fee: z2.number().min(0).optional(),
1970
- venueFillId: z2.string().optional(),
1971
- venueTimestamp: z2.string().optional(),
1972
- leverage: z2.number().positive().optional(),
1973
- orderType: z2.string().optional()
1974
- });
1975
- var promptSignalArraySchema = z2.array(promptSignalSchema);
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
- return loadFromCode(template.code);
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 mod = await import(resolved);
2002
- const fn = mod.default || mod.strategy;
2003
- if (typeof fn !== "function") {
2004
- if (typeof mod.code === "string" && mod.code.trim()) {
2005
- return loadFromCode(mod.code);
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 (typeof mod.systemPrompt === "string" && mod.systemPrompt.trim()) {
2008
- const venues = Array.isArray(mod.venues) ? mod.venues.filter((venue) => typeof venue === "string") : void 0;
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
- if (typeof mod.template === "string" && mod.template.trim()) {
2017
- const template = getTemplate(mod.template);
2018
- if (!template) {
2019
- throw new Error(`Unknown strategy template: ${mod.template}. Available: momentum, value, arbitrage, hold`);
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
- return fn;
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: ${(config.venues || []).join(", ") || "any"}`,
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 signals."
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 match = scrubbedContent.match(/\[[\s\S]*\]/);
2062
- if (!match) {
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(match[0]));
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: signal.venue || config.venues?.[0] || "manual",
2086
+ venue,
2081
2087
  chain: signal.chain,
2082
- size: signal.size ?? 1,
2088
+ size: signal.size ?? 0,
2083
2089
  price,
2084
- fee: signal.fee ?? 0,
2085
- venueFillId: signal.venueFillId ?? "",
2086
- venueTimestamp: signal.venueTimestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
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 { Wallet } from "ethers";
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
- this.signer = new Wallet(privateKey);
3675
- this.walletAddress = this.signer.address;
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 = privateKeyToAccount(privateKey);
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: http(chainConfig.rpcUrl)
4167
+ transport: http2(chainConfig.rpcUrl)
4107
4168
  });
4108
- const walletClient = createWalletClient({
4169
+ const walletClient = createWalletClient2({
4109
4170
  account: this.account,
4110
4171
  chain: chainConfig.viemChain,
4111
- transport: http(chainConfig.rpcUrl)
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 createWalletClient2, http as http2, formatUnits } from "viem";
4872
- import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
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.0";
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 signals."
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
- return { code: rawStrategy.code, venues };
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 = privateKeyToAccount2(privateKey);
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 = createWalletClient2({
5315
+ const walletClient = createWalletClient3({
5255
5316
  account,
5256
5317
  chain: arbitrum2,
5257
- transport: http2()
5318
+ transport: http3()
5258
5319
  });
5259
5320
  const perpConfig = {
5260
5321
  enabled: true,