@pgnio/helios-mcp 0.2.8 → 0.2.9

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.
@@ -2,6 +2,13 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+ var getFilename = () => fileURLToPath(import.meta.url);
9
+ var getDirname = () => path.dirname(getFilename());
10
+ var __dirname = /* @__PURE__ */ getDirname();
11
+
5
12
  // ../agent/dist/index.mjs
6
13
  import { z } from "zod";
7
14
  import { z as z2 } from "zod";
@@ -116,19 +123,21 @@ var protectiveOrderKindSchema = z.enum(["STOP_LOSS", "TAKE_PROFIT"]);
116
123
  var protectiveOrderSideSchema = z.enum(["BUY", "SELL"]);
117
124
  var protectiveOrderPositionSideSchema = z.enum(["BOTH", "LONG", "SHORT"]);
118
125
  var protectiveOrderSchema = z.object({
126
+ /** Helios's record id: what helios_positions_delete_tp_sl takes (the Hyperliquid order id works there too). */
119
127
  id: z.string(),
120
- binanceOrderId: z.number().int().nonnegative().optional(),
121
- clientOrderId: z.string().optional(),
128
+ /** The Hyperliquid order id (named after the retired Binance field). */
129
+ binanceOrderId: z.number().int().nonnegative().nullish(),
130
+ clientOrderId: z.string().nullish(),
122
131
  symbol: z.string(),
123
132
  side: protectiveOrderSideSchema,
124
- positionSide: protectiveOrderPositionSideSchema,
133
+ positionSide: protectiveOrderPositionSideSchema.nullish().transform((value) => value ?? "BOTH"),
125
134
  status: z.string(),
126
135
  kind: protectiveOrderKindSchema,
127
- groupId: z.string(),
128
- stopPrice: z.string().optional(),
129
- parentClientOrderId: z.string().optional(),
130
- assetId: z.number().int().nonnegative().optional(),
131
- createdAt: z.string().datetime()
136
+ groupId: z.string().nullish(),
137
+ stopPrice: z.union([z.string(), z.number()]).nullish().transform((value) => value == null ? value : String(value)),
138
+ parentClientOrderId: z.string().nullish(),
139
+ assetId: z.number().int().nonnegative().nullish(),
140
+ createdAt: z.string().nullish()
132
141
  });
133
142
  var hyperliquidPortfolioSnapshotSchema = z.object({
134
143
  provider: z.literal("hyperliquid"),
@@ -137,6 +146,7 @@ var hyperliquidPortfolioSnapshotSchema = z.object({
137
146
  totalMargin: z.number(),
138
147
  unrealizedPnl: z.number(),
139
148
  positions: z.array(openPositionSchema),
149
+ /** Unreadable entries are dropped (see readProtectiveOrders) rather than failing the read. */
140
150
  protectiveOrders: z.array(protectiveOrderSchema)
141
151
  });
142
152
  var portfolioInputSchema = z.object({
@@ -331,6 +341,8 @@ var closePositionInputSchema = z.object({
331
341
  });
332
342
  var closePositionResultSchema = z.object({
333
343
  status: z.literal("submitted"),
344
+ /** `no_position`: nothing was open on this coin, so no order was sent. Absent from older data-bridges. */
345
+ outcome: z.enum(["closed", "no_position"]).optional(),
334
346
  provider: providerSchema.optional(),
335
347
  symbol: z.string(),
336
348
  mode: z.literal("FULL"),
@@ -442,10 +454,10 @@ var HeliosAgentAuthClient = class {
442
454
  const parsed = agentRefreshInputSchema.parse({ refreshToken });
443
455
  return this.post("/api/agents/auth/refresh", parsed, agentTokenResponseSchema);
444
456
  }
445
- async post(path3, body, schema) {
457
+ async post(path4, body, schema) {
446
458
  let response;
447
459
  try {
448
- response = await this.fetchImpl(`${this.baseUrl}${path3}`, {
460
+ response = await this.fetchImpl(`${this.baseUrl}${path4}`, {
449
461
  method: "POST",
450
462
  headers: {
451
463
  Accept: "application/json",
@@ -521,7 +533,8 @@ var HeliosAgentDataBridgeClient = class {
521
533
  }
522
534
  return portfolioSnapshotSchema.parse({
523
535
  ...result,
524
- provider: parsedInput.provider
536
+ provider: parsedInput.provider,
537
+ protectiveOrders: readProtectiveOrders(result.protectiveOrders)
525
538
  });
526
539
  }
527
540
  async getLedgerFills(accessToken, input = {}) {
@@ -574,7 +587,7 @@ var HeliosAgentDataBridgeClient = class {
574
587
  }
575
588
  async closePosition(accessToken, input) {
576
589
  const parsedInput = closePositionInputSchema.parse(input);
577
- await this.request(
590
+ const payload = await this.request(
578
591
  accessToken,
579
592
  "positions.close",
580
593
  {
@@ -585,8 +598,10 @@ var HeliosAgentDataBridgeClient = class {
585
598
  },
586
599
  "POST"
587
600
  );
601
+ const outcome = this.tryUnwrapResult(payload)?.outcome;
588
602
  return closePositionResultSchema.parse({
589
603
  status: "submitted",
604
+ ...outcome === "closed" || outcome === "no_position" ? { outcome } : {},
590
605
  provider: parsedInput.provider,
591
606
  symbol: parsedInput.symbol,
592
607
  mode: "FULL",
@@ -684,6 +699,14 @@ var HeliosAgentDataBridgeClient = class {
684
699
  });
685
700
  }
686
701
  }
702
+ /** The result of a mutation older data-bridges answer with nothing; null when there is none. */
703
+ tryUnwrapResult(payload) {
704
+ try {
705
+ return this.unwrapResult(payload) ?? null;
706
+ } catch {
707
+ return null;
708
+ }
709
+ }
687
710
  unwrapResult(payload) {
688
711
  if (!payload || typeof payload !== "object") {
689
712
  throw new HeliosAgentApiError({
@@ -744,6 +767,13 @@ var HeliosAgentDataBridgeClient = class {
744
767
  return envelope;
745
768
  }
746
769
  };
770
+ function readProtectiveOrders(value) {
771
+ if (!Array.isArray(value)) return [];
772
+ return value.flatMap((entry) => {
773
+ const parsed = protectiveOrderSchema.safeParse(entry);
774
+ return parsed.success ? [parsed.data] : [];
775
+ });
776
+ }
747
777
  var DEFAULT_AGENT_REFRESH_WINDOW_MS = 60 * 1e3;
748
778
  var agentConnectionStatusSchema = z3.enum([
749
779
  "disconnected",
@@ -1181,8 +1211,8 @@ var HeliosAgentMcpAccountService = class {
1181
1211
  };
1182
1212
 
1183
1213
  // src/docs-service.ts
1184
- import { readdirSync, readFileSync, statSync } from "fs";
1185
- import path from "path";
1214
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
1215
+ import path2 from "path";
1186
1216
  var HeliosAgentMcpDocsService = class {
1187
1217
  constructor(options) {
1188
1218
  __publicField(this, "docsRoot");
@@ -1197,9 +1227,12 @@ var HeliosAgentMcpDocsService = class {
1197
1227
  }
1198
1228
  async getDocument(input) {
1199
1229
  const documentPath = resolveDocumentPath(this.docsRoot, input.path);
1230
+ if (!existsSync(documentPath) || !statSync(documentPath).isFile()) {
1231
+ throw new Error(`No document "${input.path}". helios_docs_get_index lists the documents.`);
1232
+ }
1200
1233
  const body = readFileSync(documentPath, "utf8");
1201
1234
  const stats = statSync(documentPath);
1202
- const relativePath = toPosixPath(path.relative(this.docsRoot, documentPath));
1235
+ const relativePath = toPosixPath(path2.relative(this.docsRoot, documentPath));
1203
1236
  return {
1204
1237
  path: relativePath,
1205
1238
  section: getDocumentSection(relativePath),
@@ -1211,7 +1244,7 @@ var HeliosAgentMcpDocsService = class {
1211
1244
  readDocumentMetadata(documentPath) {
1212
1245
  const body = readFileSync(documentPath, "utf8");
1213
1246
  const stats = statSync(documentPath);
1214
- const relativePath = toPosixPath(path.relative(this.docsRoot, documentPath));
1247
+ const relativePath = toPosixPath(path2.relative(this.docsRoot, documentPath));
1215
1248
  return {
1216
1249
  path: relativePath,
1217
1250
  section: getDocumentSection(relativePath),
@@ -1221,7 +1254,7 @@ var HeliosAgentMcpDocsService = class {
1221
1254
  }
1222
1255
  };
1223
1256
  function getDefaultHeliosAgentMcpDocsRoot() {
1224
- return path.resolve(__dirname, "..", "docs");
1257
+ return path2.resolve(__dirname, "..", "docs");
1225
1258
  }
1226
1259
  function listDocumentPaths(root) {
1227
1260
  return walk(root).filter((filePath) => filePath.endsWith(".txt")).sort((left, right) => left.localeCompare(right));
@@ -1230,7 +1263,7 @@ function walk(currentPath) {
1230
1263
  const entries = readdirSync(currentPath, { withFileTypes: true });
1231
1264
  const files = [];
1232
1265
  for (const entry of entries) {
1233
- const nextPath = path.join(currentPath, entry.name);
1266
+ const nextPath = path2.join(currentPath, entry.name);
1234
1267
  if (entry.isDirectory()) {
1235
1268
  files.push(...walk(nextPath));
1236
1269
  continue;
@@ -1243,12 +1276,12 @@ function walk(currentPath) {
1243
1276
  }
1244
1277
  function resolveDocumentPath(root, relativeDocumentPath) {
1245
1278
  const normalizedPath = relativeDocumentPath.replace(/\\/g, "/").replace(/^\/+/, "");
1246
- const resolvedPath = path.resolve(root, normalizedPath);
1247
- const relativePath = path.relative(root, resolvedPath);
1279
+ const resolvedPath = path2.resolve(root, normalizedPath);
1280
+ const relativePath = path2.relative(root, resolvedPath);
1248
1281
  if (!normalizedPath) {
1249
1282
  throw new Error("Document path is required.");
1250
1283
  }
1251
- if (path.isAbsolute(normalizedPath) || relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
1284
+ if (path2.isAbsolute(normalizedPath) || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
1252
1285
  throw new Error(`Document path "${relativeDocumentPath}" is outside the docs root.`);
1253
1286
  }
1254
1287
  if (!resolvedPath.endsWith(".txt")) {
@@ -1268,13 +1301,13 @@ function extractDocumentTitle(body, relativePath) {
1268
1301
  }
1269
1302
  return trimmed;
1270
1303
  }
1271
- return path.basename(relativePath, ".txt");
1304
+ return path2.basename(relativePath, ".txt");
1272
1305
  }
1273
1306
  function getDocumentSection(relativePath) {
1274
1307
  return relativePath.split("/")[0] ?? "docs";
1275
1308
  }
1276
1309
  function toPosixPath(value) {
1277
- return value.split(path.sep).join("/");
1310
+ return value.split(path2.sep).join("/");
1278
1311
  }
1279
1312
 
1280
1313
  // src/ledger-service.ts
@@ -1624,10 +1657,10 @@ var HeliosAgentMcpProviderService = class {
1624
1657
  // src/session-store.ts
1625
1658
  import { chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
1626
1659
  import { homedir } from "os";
1627
- import path2 from "path";
1660
+ import path3 from "path";
1628
1661
  import { z as z4 } from "zod";
1629
1662
  function getDefaultHeliosAgentMcpSessionPath() {
1630
- return path2.join(homedir(), ".helios-agent-mcp", "session.json");
1663
+ return path3.join(homedir(), ".helios-agent-mcp", "session.json");
1631
1664
  }
1632
1665
  var pendingAgentLoginSchema = z4.object({
1633
1666
  status: z4.literal("pending"),
@@ -1748,7 +1781,7 @@ var FileAgentSessionStore = class {
1748
1781
  }
1749
1782
  }
1750
1783
  async writeState(state) {
1751
- await mkdir(path2.dirname(this.filePath), { recursive: true });
1784
+ await mkdir(path3.dirname(this.filePath), { recursive: true });
1752
1785
  await writeFile(this.filePath, `${JSON.stringify(state, null, 2)}
1753
1786
  `, { encoding: "utf8", mode: 384 });
1754
1787
  await chmod(this.filePath, 384).catch(() => void 0);
@@ -2533,7 +2566,7 @@ function createHeliosAccountToolDefinitions(service) {
2533
2566
  {
2534
2567
  name: "helios_portfolio_get",
2535
2568
  title: "Get Helios Portfolio",
2536
- description: "Returns a provider-specific portfolio snapshot. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2569
+ description: "Returns balances, open positions and their TP/SL (protectiveOrders, each with the id helios_positions_delete_tp_sl takes). Provider may be omitted if one is already pinned with helios_provider_set_active.",
2537
2570
  inputSchema: providerSelectionInputSchema,
2538
2571
  async execute(input) {
2539
2572
  const result = portfolioToolOutputSchema.parse(await service.getPortfolio(input));
@@ -2629,7 +2662,7 @@ function createHeliosMarketToolDefinitions(service) {
2629
2662
  {
2630
2663
  name: "helios_market_get_snapshot",
2631
2664
  title: "Get Helios Market Snapshot",
2632
- description: "Returns a compact market snapshot for the selected, all, or explicitly requested symbols. By default: the top 50 coins by market cap with the main columns; name symbols to get other coins, pass fields for other columns.",
2665
+ description: "Returns a compact market snapshot for the selected, all, or explicitly requested symbols. By default: the top 50 coins by market cap with the main columns; name symbols to get other coins, pass fields for other columns. What each column means: helios_docs_get_document with path ai-agent-indicators/00_columns.txt (read it once).",
2633
2666
  inputSchema: marketSnapshotInputSchema,
2634
2667
  async execute(input) {
2635
2668
  const result = marketSnapshotToolOutputSchema.parse(await service.getSnapshot(input));
@@ -2770,7 +2803,7 @@ function createHeliosTradingToolDefinitions(service) {
2770
2803
  content: [
2771
2804
  {
2772
2805
  type: "text",
2773
- text: result.status === "connected" ? `Submitted Helios full close for ${result.closure.provider ?? input.provider} ${result.closure.symbol}.` : result.message
2806
+ text: result.status !== "connected" ? result.message : result.closure.outcome === "no_position" ? `No open ${result.closure.symbol} position: nothing was closed and no order was sent.` : `Sent a market order closing the full ${result.closure.provider ?? input.provider ?? "hyperliquid"} ${result.closure.symbol} position. Check helios_positions_get_open to confirm.`
2774
2807
  }
2775
2808
  ],
2776
2809
  structuredContent: result.status === "connected" ? result : {
@@ -2804,7 +2837,7 @@ function createHeliosTradingToolDefinitions(service) {
2804
2837
  {
2805
2838
  name: "helios_positions_delete_tp_sl",
2806
2839
  title: "Delete Helios TP/SL",
2807
- description: "Deletes an existing Helios take-profit or stop-loss order by order id. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2840
+ description: "Deletes a take-profit or stop-loss. orderId is the id from helios_portfolio_get protectiveOrders, or the Hyperliquid order id of the TP/SL. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2808
2841
  inputSchema: deleteTpSlInputSchema,
2809
2842
  async execute(input) {
2810
2843
  const result = deleteTpSlToolOutputSchema.parse(await service.deleteTpSl(input));
@@ -2897,7 +2930,7 @@ async function createHeliosAgentMcpServer(options) {
2897
2930
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
2898
2931
  const server = new McpServer({
2899
2932
  name: "helios",
2900
- version: "0.2.7"
2933
+ version: "0.2.9"
2901
2934
  });
2902
2935
  const sessionStore = options.sessionStore ?? new FileAgentSessionStore(options.sessionPath ?? getDefaultHeliosAgentMcpSessionPath());
2903
2936
  const client = new HeliosAgentAuthClient({
@@ -2,6 +2,13 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+ var getFilename = () => fileURLToPath(import.meta.url);
9
+ var getDirname = () => path.dirname(getFilename());
10
+ var __dirname = /* @__PURE__ */ getDirname();
11
+
5
12
  // ../agent/dist/index.mjs
6
13
  import { z } from "zod";
7
14
  import { z as z2 } from "zod";
@@ -116,19 +123,21 @@ var protectiveOrderKindSchema = z.enum(["STOP_LOSS", "TAKE_PROFIT"]);
116
123
  var protectiveOrderSideSchema = z.enum(["BUY", "SELL"]);
117
124
  var protectiveOrderPositionSideSchema = z.enum(["BOTH", "LONG", "SHORT"]);
118
125
  var protectiveOrderSchema = z.object({
126
+ /** Helios's record id: what helios_positions_delete_tp_sl takes (the Hyperliquid order id works there too). */
119
127
  id: z.string(),
120
- binanceOrderId: z.number().int().nonnegative().optional(),
121
- clientOrderId: z.string().optional(),
128
+ /** The Hyperliquid order id (named after the retired Binance field). */
129
+ binanceOrderId: z.number().int().nonnegative().nullish(),
130
+ clientOrderId: z.string().nullish(),
122
131
  symbol: z.string(),
123
132
  side: protectiveOrderSideSchema,
124
- positionSide: protectiveOrderPositionSideSchema,
133
+ positionSide: protectiveOrderPositionSideSchema.nullish().transform((value) => value ?? "BOTH"),
125
134
  status: z.string(),
126
135
  kind: protectiveOrderKindSchema,
127
- groupId: z.string(),
128
- stopPrice: z.string().optional(),
129
- parentClientOrderId: z.string().optional(),
130
- assetId: z.number().int().nonnegative().optional(),
131
- createdAt: z.string().datetime()
136
+ groupId: z.string().nullish(),
137
+ stopPrice: z.union([z.string(), z.number()]).nullish().transform((value) => value == null ? value : String(value)),
138
+ parentClientOrderId: z.string().nullish(),
139
+ assetId: z.number().int().nonnegative().nullish(),
140
+ createdAt: z.string().nullish()
132
141
  });
133
142
  var hyperliquidPortfolioSnapshotSchema = z.object({
134
143
  provider: z.literal("hyperliquid"),
@@ -137,6 +146,7 @@ var hyperliquidPortfolioSnapshotSchema = z.object({
137
146
  totalMargin: z.number(),
138
147
  unrealizedPnl: z.number(),
139
148
  positions: z.array(openPositionSchema),
149
+ /** Unreadable entries are dropped (see readProtectiveOrders) rather than failing the read. */
140
150
  protectiveOrders: z.array(protectiveOrderSchema)
141
151
  });
142
152
  var portfolioInputSchema = z.object({
@@ -331,6 +341,8 @@ var closePositionInputSchema = z.object({
331
341
  });
332
342
  var closePositionResultSchema = z.object({
333
343
  status: z.literal("submitted"),
344
+ /** `no_position`: nothing was open on this coin, so no order was sent. Absent from older data-bridges. */
345
+ outcome: z.enum(["closed", "no_position"]).optional(),
334
346
  provider: providerSchema.optional(),
335
347
  symbol: z.string(),
336
348
  mode: z.literal("FULL"),
@@ -442,10 +454,10 @@ var HeliosAgentAuthClient = class {
442
454
  const parsed = agentRefreshInputSchema.parse({ refreshToken });
443
455
  return this.post("/api/agents/auth/refresh", parsed, agentTokenResponseSchema);
444
456
  }
445
- async post(path3, body, schema) {
457
+ async post(path4, body, schema) {
446
458
  let response;
447
459
  try {
448
- response = await this.fetchImpl(`${this.baseUrl}${path3}`, {
460
+ response = await this.fetchImpl(`${this.baseUrl}${path4}`, {
449
461
  method: "POST",
450
462
  headers: {
451
463
  Accept: "application/json",
@@ -521,7 +533,8 @@ var HeliosAgentDataBridgeClient = class {
521
533
  }
522
534
  return portfolioSnapshotSchema.parse({
523
535
  ...result,
524
- provider: parsedInput.provider
536
+ provider: parsedInput.provider,
537
+ protectiveOrders: readProtectiveOrders(result.protectiveOrders)
525
538
  });
526
539
  }
527
540
  async getLedgerFills(accessToken, input = {}) {
@@ -574,7 +587,7 @@ var HeliosAgentDataBridgeClient = class {
574
587
  }
575
588
  async closePosition(accessToken, input) {
576
589
  const parsedInput = closePositionInputSchema.parse(input);
577
- await this.request(
590
+ const payload = await this.request(
578
591
  accessToken,
579
592
  "positions.close",
580
593
  {
@@ -585,8 +598,10 @@ var HeliosAgentDataBridgeClient = class {
585
598
  },
586
599
  "POST"
587
600
  );
601
+ const outcome = this.tryUnwrapResult(payload)?.outcome;
588
602
  return closePositionResultSchema.parse({
589
603
  status: "submitted",
604
+ ...outcome === "closed" || outcome === "no_position" ? { outcome } : {},
590
605
  provider: parsedInput.provider,
591
606
  symbol: parsedInput.symbol,
592
607
  mode: "FULL",
@@ -684,6 +699,14 @@ var HeliosAgentDataBridgeClient = class {
684
699
  });
685
700
  }
686
701
  }
702
+ /** The result of a mutation older data-bridges answer with nothing; null when there is none. */
703
+ tryUnwrapResult(payload) {
704
+ try {
705
+ return this.unwrapResult(payload) ?? null;
706
+ } catch {
707
+ return null;
708
+ }
709
+ }
687
710
  unwrapResult(payload) {
688
711
  if (!payload || typeof payload !== "object") {
689
712
  throw new HeliosAgentApiError({
@@ -744,6 +767,13 @@ var HeliosAgentDataBridgeClient = class {
744
767
  return envelope;
745
768
  }
746
769
  };
770
+ function readProtectiveOrders(value) {
771
+ if (!Array.isArray(value)) return [];
772
+ return value.flatMap((entry) => {
773
+ const parsed = protectiveOrderSchema.safeParse(entry);
774
+ return parsed.success ? [parsed.data] : [];
775
+ });
776
+ }
747
777
  var DEFAULT_AGENT_REFRESH_WINDOW_MS = 60 * 1e3;
748
778
  var agentConnectionStatusSchema = z3.enum([
749
779
  "disconnected",
@@ -1181,8 +1211,8 @@ var HeliosAgentMcpAccountService = class {
1181
1211
  };
1182
1212
 
1183
1213
  // src/docs-service.ts
1184
- import { readdirSync, readFileSync, statSync } from "fs";
1185
- import path from "path";
1214
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
1215
+ import path2 from "path";
1186
1216
  var HeliosAgentMcpDocsService = class {
1187
1217
  constructor(options) {
1188
1218
  __publicField(this, "docsRoot");
@@ -1197,9 +1227,12 @@ var HeliosAgentMcpDocsService = class {
1197
1227
  }
1198
1228
  async getDocument(input) {
1199
1229
  const documentPath = resolveDocumentPath(this.docsRoot, input.path);
1230
+ if (!existsSync(documentPath) || !statSync(documentPath).isFile()) {
1231
+ throw new Error(`No document "${input.path}". helios_docs_get_index lists the documents.`);
1232
+ }
1200
1233
  const body = readFileSync(documentPath, "utf8");
1201
1234
  const stats = statSync(documentPath);
1202
- const relativePath = toPosixPath(path.relative(this.docsRoot, documentPath));
1235
+ const relativePath = toPosixPath(path2.relative(this.docsRoot, documentPath));
1203
1236
  return {
1204
1237
  path: relativePath,
1205
1238
  section: getDocumentSection(relativePath),
@@ -1211,7 +1244,7 @@ var HeliosAgentMcpDocsService = class {
1211
1244
  readDocumentMetadata(documentPath) {
1212
1245
  const body = readFileSync(documentPath, "utf8");
1213
1246
  const stats = statSync(documentPath);
1214
- const relativePath = toPosixPath(path.relative(this.docsRoot, documentPath));
1247
+ const relativePath = toPosixPath(path2.relative(this.docsRoot, documentPath));
1215
1248
  return {
1216
1249
  path: relativePath,
1217
1250
  section: getDocumentSection(relativePath),
@@ -1221,7 +1254,7 @@ var HeliosAgentMcpDocsService = class {
1221
1254
  }
1222
1255
  };
1223
1256
  function getDefaultHeliosAgentMcpDocsRoot() {
1224
- return path.resolve(__dirname, "..", "docs");
1257
+ return path2.resolve(__dirname, "..", "docs");
1225
1258
  }
1226
1259
  function listDocumentPaths(root) {
1227
1260
  return walk(root).filter((filePath) => filePath.endsWith(".txt")).sort((left, right) => left.localeCompare(right));
@@ -1230,7 +1263,7 @@ function walk(currentPath) {
1230
1263
  const entries = readdirSync(currentPath, { withFileTypes: true });
1231
1264
  const files = [];
1232
1265
  for (const entry of entries) {
1233
- const nextPath = path.join(currentPath, entry.name);
1266
+ const nextPath = path2.join(currentPath, entry.name);
1234
1267
  if (entry.isDirectory()) {
1235
1268
  files.push(...walk(nextPath));
1236
1269
  continue;
@@ -1243,12 +1276,12 @@ function walk(currentPath) {
1243
1276
  }
1244
1277
  function resolveDocumentPath(root, relativeDocumentPath) {
1245
1278
  const normalizedPath = relativeDocumentPath.replace(/\\/g, "/").replace(/^\/+/, "");
1246
- const resolvedPath = path.resolve(root, normalizedPath);
1247
- const relativePath = path.relative(root, resolvedPath);
1279
+ const resolvedPath = path2.resolve(root, normalizedPath);
1280
+ const relativePath = path2.relative(root, resolvedPath);
1248
1281
  if (!normalizedPath) {
1249
1282
  throw new Error("Document path is required.");
1250
1283
  }
1251
- if (path.isAbsolute(normalizedPath) || relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
1284
+ if (path2.isAbsolute(normalizedPath) || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
1252
1285
  throw new Error(`Document path "${relativeDocumentPath}" is outside the docs root.`);
1253
1286
  }
1254
1287
  if (!resolvedPath.endsWith(".txt")) {
@@ -1268,13 +1301,13 @@ function extractDocumentTitle(body, relativePath) {
1268
1301
  }
1269
1302
  return trimmed;
1270
1303
  }
1271
- return path.basename(relativePath, ".txt");
1304
+ return path2.basename(relativePath, ".txt");
1272
1305
  }
1273
1306
  function getDocumentSection(relativePath) {
1274
1307
  return relativePath.split("/")[0] ?? "docs";
1275
1308
  }
1276
1309
  function toPosixPath(value) {
1277
- return value.split(path.sep).join("/");
1310
+ return value.split(path2.sep).join("/");
1278
1311
  }
1279
1312
 
1280
1313
  // src/ledger-service.ts
@@ -1624,10 +1657,10 @@ var HeliosAgentMcpProviderService = class {
1624
1657
  // src/session-store.ts
1625
1658
  import { chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
1626
1659
  import { homedir } from "os";
1627
- import path2 from "path";
1660
+ import path3 from "path";
1628
1661
  import { z as z4 } from "zod";
1629
1662
  function getDefaultHeliosAgentMcpSessionPath() {
1630
- return path2.join(homedir(), ".helios-agent-mcp", "session.json");
1663
+ return path3.join(homedir(), ".helios-agent-mcp", "session.json");
1631
1664
  }
1632
1665
  var pendingAgentLoginSchema = z4.object({
1633
1666
  status: z4.literal("pending"),
@@ -1748,7 +1781,7 @@ var FileAgentSessionStore = class {
1748
1781
  }
1749
1782
  }
1750
1783
  async writeState(state) {
1751
- await mkdir(path2.dirname(this.filePath), { recursive: true });
1784
+ await mkdir(path3.dirname(this.filePath), { recursive: true });
1752
1785
  await writeFile(this.filePath, `${JSON.stringify(state, null, 2)}
1753
1786
  `, { encoding: "utf8", mode: 384 });
1754
1787
  await chmod(this.filePath, 384).catch(() => void 0);
@@ -2533,7 +2566,7 @@ function createHeliosAccountToolDefinitions(service) {
2533
2566
  {
2534
2567
  name: "helios_portfolio_get",
2535
2568
  title: "Get Helios Portfolio",
2536
- description: "Returns a provider-specific portfolio snapshot. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2569
+ description: "Returns balances, open positions and their TP/SL (protectiveOrders, each with the id helios_positions_delete_tp_sl takes). Provider may be omitted if one is already pinned with helios_provider_set_active.",
2537
2570
  inputSchema: providerSelectionInputSchema,
2538
2571
  async execute(input) {
2539
2572
  const result = portfolioToolOutputSchema.parse(await service.getPortfolio(input));
@@ -2770,7 +2803,7 @@ function createHeliosTradingToolDefinitions(service) {
2770
2803
  content: [
2771
2804
  {
2772
2805
  type: "text",
2773
- text: result.status === "connected" ? `Submitted Helios full close for ${result.closure.provider ?? input.provider} ${result.closure.symbol}.` : result.message
2806
+ text: result.status !== "connected" ? result.message : result.closure.outcome === "no_position" ? `No open ${result.closure.symbol} position: nothing was closed and no order was sent.` : `Sent a market order closing the full ${result.closure.provider ?? input.provider ?? "hyperliquid"} ${result.closure.symbol} position. Check helios_positions_get_open to confirm.`
2774
2807
  }
2775
2808
  ],
2776
2809
  structuredContent: result.status === "connected" ? result : {
@@ -2804,7 +2837,7 @@ function createHeliosTradingToolDefinitions(service) {
2804
2837
  {
2805
2838
  name: "helios_positions_delete_tp_sl",
2806
2839
  title: "Delete Helios TP/SL",
2807
- description: "Deletes an existing Helios take-profit or stop-loss order by order id. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2840
+ description: "Deletes a take-profit or stop-loss. orderId is the id from helios_portfolio_get protectiveOrders, or the Hyperliquid order id of the TP/SL. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2808
2841
  inputSchema: deleteTpSlInputSchema,
2809
2842
  async execute(input) {
2810
2843
  const result = deleteTpSlToolOutputSchema.parse(await service.deleteTpSl(input));