@xbbg/langgraph 1.2.7 → 1.3.1

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/index.js CHANGED
@@ -51,6 +51,7 @@ var BLOOMBERG_TOOL_NAMES = [
51
51
  "xbbg_ext_cdx",
52
52
  "xbbg_ext_currency",
53
53
  "xbbg_ext_bql_builder",
54
+ "xbbg_ext_chart_spec",
54
55
  "xbbg_ext_market_session",
55
56
  "xbbg_ext_yas_overrides",
56
57
  "xbbg_ext_constants",
@@ -65,6 +66,13 @@ var DEFAULT_MAX_BQL_QUERY_CHARS = 4e3;
65
66
  var DEFAULT_MAX_SEARCH_SPEC_CHARS = 1e3;
66
67
  var DEFAULT_MAX_STREAM_UPDATES = 10;
67
68
  var DEFAULT_MAX_STREAM_WAIT_MS = 15e3;
69
+ var DEFAULT_ENGINE_REQUEST_TIMEOUT_MS = 6e4;
70
+ function engineConfigWithDefaults(config) {
71
+ if (config?.requestTimeoutMs !== void 0) {
72
+ return config;
73
+ }
74
+ return { ...config, requestTimeoutMs: DEFAULT_ENGINE_REQUEST_TIMEOUT_MS };
75
+ }
68
76
  function positiveInteger(value, fallback, name) {
69
77
  if (value === void 0) {
70
78
  return fallback;
@@ -82,7 +90,7 @@ function normalizeBloombergToolsOptions(options = {}) {
82
90
  core: options.core,
83
91
  disabledTools: disabledToolSet(options.disabledTools),
84
92
  engine: options.engine,
85
- engineConfig: options.engineConfig,
93
+ engineConfig: engineConfigWithDefaults(options.engineConfig),
86
94
  maxBqlQueryChars: positiveInteger(
87
95
  options.maxBqlQueryChars,
88
96
  DEFAULT_MAX_BQL_QUERY_CHARS,
@@ -167,180 +175,6 @@ function createCoreResolver(options = {}) {
167
175
  options: normalized
168
176
  };
169
177
  }
170
- function inputJsonSchema(schema) {
171
- const jsonSchema = zodToJsonSchema.zodToJsonSchema(schema, {
172
- $refStrategy: "none",
173
- effectStrategy: "input",
174
- pipeStrategy: "input"
175
- });
176
- delete jsonSchema.$schema;
177
- delete jsonSchema.definitions;
178
- return jsonSchema;
179
- }
180
- function createBloombergStructuredTool(func, fields) {
181
- const providerToolDefinition = {
182
- type: "function",
183
- function: {
184
- description: fields.description,
185
- name: fields.name,
186
- parameters: inputJsonSchema(fields.schema)
187
- }
188
- };
189
- return tools.tool(
190
- func,
191
- {
192
- ...fields,
193
- extras: { providerToolDefinition }
194
- }
195
- );
196
- }
197
-
198
- // src/cdx-fields.ts
199
- var CDX_INFO_FIELDS = Object.freeze([
200
- "ROLLING_SERIES",
201
- "VERSION",
202
- "ON_THE_RUN_CURRENT_BD_INDICATOR",
203
- "CDS_FIRST_ACCRUAL_START_DATE",
204
- "NAME",
205
- "NUM_CURRENT_COMPANIES_CCY_TKR",
206
- "NUM_ORIG_COMPANIES_CRNCY_TKR",
207
- "PX_LAST"
208
- ]);
209
- var CDX_PRICING_FIELDS = Object.freeze([
210
- "PX_LAST",
211
- "PX_BID",
212
- "PX_ASK",
213
- "UPFRONT_LAST",
214
- "UPFRONT_BID",
215
- "UPFRONT_ASK",
216
- "CDS_FLAT_SPREAD",
217
- "UPFRONT_FEE",
218
- "PV_CDS_PREMIUM_LEG",
219
- "PV_CDS_DEFAULT_LEG"
220
- ]);
221
- var CDX_RISK_FIELDS = Object.freeze([
222
- "SW_CNV_BPV",
223
- "SW_EQV_BPV",
224
- "CDS_SPREAD_MID_MODIFIED_DURATION",
225
- "CDS_SPREAD_MID_CONVEXITY",
226
- "RECOVERY_RATE_SEN",
227
- "CDS_RECOVERY_RT"
228
- ]);
229
-
230
- // src/descriptions.ts
231
- var REQUIRED_TOOL_INSTRUCTIONS = [
232
- "# Bloomberg tool usage",
233
- "- Use these tools only for server-side Bloomberg data access through @xbbg/core. Never imply Bloomberg data was retrieved unless a tool call actually returned it.",
234
- "- Ask a clarifying question before calling a tool when any security identity, field mnemonic, date range, currency, periodicity, intraday interval, timezone, override, or universe is ambiguous.",
235
- "- Do not invent Bloomberg tickers, field mnemonics, overrides, or BQL functions. If the user gives a field description rather than a confident mnemonic, call xbbg_bflds first.",
236
- "",
237
- "## Security identifiers",
238
- "- Prefer fully qualified Bloomberg securities supplied by the user, such as <TICKER> <MARKET_SECTOR>, <INDEX_TICKER> <MARKET_SECTOR>, or <CREDIT_INDEX_TICKER> <MARKET_SECTOR>.",
239
- "- For raw security identifiers, request or pass Bloomberg identifier syntax directly: /isin/<ISIN> for ISINs or /cusip/<CUSIP> for CUSIPs.",
240
- "- Do not pass raw ISIN or CUSIP strings when the request is meant to identify a security. Do not use xbbg_bsrch as a replacement for a known ticker, ISIN, or CUSIP.",
241
- "- For dealer quote / BQR workflows, use xbbg_bqr with a fixed-income identifier plus a dealer quote source such as /isin/<ISIN>@<QUOTE_SOURCE> <MARKET_SECTOR>. For raw intraday ticks, use xbbg_bdtick.",
242
- "",
243
- "## Core request tools",
244
- "- xbbg_bdp: current or reference point-in-time fields. Use a small explicit securities list and a small explicit fields list. Use includeSecurityErrors only when the caller wants Bloomberg security errors in the response.",
245
- "- xbbg_bdh: historical daily or periodic time series. Always provide explicit start and end dates in YYYY-MM-DD or YYYYMMDD form. Ask before choosing periodicity, currency, fill behavior, adjustment overrides, or a wide output table.",
246
- "- xbbg_bds: Bloomberg bulk/table fields. Provide exactly one bulk field; do not use bds for ordinary multi-field reference data.",
247
- "- xbbg_bdib: intraday bars only. Provide one ticker, explicit ISO start/end datetimes with time components, a positive interval in minutes, and timezone context when datetimes are naive.",
248
- "- xbbg_bdtick: intraday tick data. Provide one ticker, explicit ISO start/end datetimes with time components, and explicit eventTypes unless the default event stream is intended. Use includeBrokerCodes or includeConditionCodes only when those columns are needed.",
249
- "- xbbg_bql: BQL expressions only when the user asks for BQL or the request is naturally expressed as a bounded BQL query. Keep queries short, explicit, and scoped to the requested universe.",
250
- "- xbbg_bsrch: Bloomberg search-grid or saved-search workflows only. Do not use it for ordinary security lookup.",
251
- "- xbbg_bqr: Bloomberg Quote Request / dealer quotes. Prefer fixed-income identifier inputs with a dealer quote source such as /isin/<ISIN>@<QUOTE_SOURCE> <MARKET_SECTOR>, explicit start/end datetimes with time components, and explicit event types.",
252
- "- xbbg_bflds: Bloomberg field metadata/search. Provide exactly one of fields or searchSpec; use searchSpec for natural-language field names and fields for known mnemonics.",
253
- "- xbbg_beqs: Bloomberg equity screening by named BEQS screen. Prefer this over hand-written BQL when the user names an existing Bloomberg screen.",
254
- "- xbbg_yas: fixed-income YAS recipe fields. Prefer this over manual YAS-style BDP requests when the user asks for yield, duration, spread, or price analytics.",
255
- "- xbbg_preferreds: preferred stock discovery from an equity ticker. Prefer this over xbbg_ext_bql_builder plus xbbg_bql when the user wants the actual preferreds result.",
256
- "- xbbg_corporate_bonds: bounded corporate bond universe query for a company ticker. Prefer this over generic BQL for company debt discovery.",
257
- "- xbbg_index_members: index constituents through the core index recipe. Prefer this over generic BDS/BQL members when the user asks for constituents.",
258
- "- xbbg_resolve_isins: resolves supplied ISIN strings to Bloomberg securities. Pass raw ISIN strings only for this recipe; otherwise use /isin/<ISIN> syntax with data tools.",
259
- "- xbbg_issuer_isins: issuer/bond ISIN workflow for supplied bond ISIN strings.",
260
- "- xbbg_etf_holdings: ETF holdings recipe for a single ETF ticker. Prefer this over generic BQL holdings when the user asks for ETF constituents.",
261
- "- xbbg_stream_snapshot: bounded live market-data observation from //blp/mktdata. Requires explicit maxUpdates and always terminates/unsubscribes.",
262
- "- xbbg_mktbar_snapshot: bounded live market-bar observation from //blp/mktbar for one ticker. Requires explicit maxUpdates and always terminates/unsubscribes.",
263
- "- xbbg_depth_snapshot: bounded market-depth observation from //blp/mktdepthdata for one ticker. Requires explicit maxUpdates and always terminates/unsubscribes.",
264
- "",
265
- "## BQL guidance",
266
- "- BQL is a complete Bloomberg Query Language expression sent as one query string; the tool does not assemble get/for/with clauses for you.",
267
- "- Basic shape: get(<FIELD_1>, <FIELD_2>) for(<UNIVERSE>). Use placeholders such as '<TICKER> <MARKET_SECTOR>', holdings('<ETF_TICKER> <MARKET_SECTOR>'), or members('<INDEX_TICKER> <MARKET_SECTOR>') until the user supplies real inputs.",
268
- "- Use BQL for universe-oriented analytics and screens only when the user provides a bounded universe, filters, and date range.",
269
- "- Prefer xbbg_ext_bql_builder instead of hand-writing BQL for supported workflows: preferred stocks, corporate bonds, and ETF holdings.",
270
- "- Do not use BQL just because the user asks for normal reference data; xbbg_bdp is simpler for current fields and xbbg_bdh is simpler for historical time series.",
271
- "",
272
- "## Output handling",
273
- "- Tool results use LangChain content_and_artifact output: content starts with a compact summary and then includes bounded model-readable JSON; artifact is the structured bounded envelope with tool, rowCount, truncated, and data for application code.",
274
- "- If a response is empty, truncated, or contains Bloomberg/security errors, say that directly. Do not fill gaps from memory or assumptions."
275
- ];
276
- var OPTIONAL_EXTENSION_INSTRUCTIONS = [
277
- "",
278
- "## Extension helper tools",
279
- "- xbbg_ext_ticker: ticker hygiene before live calls. parse_ticker splits a Bloomberg ticker, normalize_tickers trims/canonicalizes lists, filter_equity_tickers keeps equity-like tickers, is_specific_contract checks futures specificity, and validate_generic_ticker rejects malformed generic futures tickers.",
280
- "- xbbg_ext_futures: futures contract construction and selection. Use build_futures_ticker for root/month/year/asset assembly, get_futures_months for month-code lookup, generate_candidates for generic-to-specific candidates, contract_index for generic contract rank, filter_candidates_by_cycle for HMUZ/quarterly cycles, and filter_valid_contracts to keep contracts valid for a date.",
281
- "- xbbg_ext_cdx: CDX ticker workflow support. Use parse_cdx_ticker to understand a CDX ticker, previous_cdx_series to roll back a series, cdx_gen_to_specific to resolve a generic CDX to a target series, and cdx_info/cdx_pricing/cdx_risk for predefined BDP field bundles. cdx_pricing and cdx_risk accept recoveryRate, which becomes the CDS_RR override.",
282
- "- xbbg_ext_currency: currency-planning helpers. build_fx_pair constructs the Bloomberg FX pair and conversion factor, same_currency avoids unnecessary conversion, and currencies_needing_conversion identifies which currencies differ from a target before requesting converted values.",
283
- "- xbbg_ext_bql_builder: safe BQL generators for common xbbg workflows. Use build_preferreds_query for preferred-stock discovery from an equity, build_corporate_bonds_query for company bond universes with optional currency/active filters, and build_etf_holdings_query for ETF constituents. Prefer these builders over hand-writing those BQL shapes.",
284
- "- xbbg_ext_market_session: exchange calendar/timezone support. derive_sessions turns day session times into session blocks, infer_timezone maps country codes to timezones, session_times_to_utc converts local sessions to UTC, get_market_rule gets MIC/exchange rules, default_turnover_dates and default_bqr_datetimes provide bounded defaults, and get/list_exchange_override inspect configured exchange metadata.",
285
- "- xbbg_ext_yas_overrides: builds flat YAS override maps for fixed-income BDP requests when the lower-level BDP workflow is required. Prefer xbbg_yas for actual YAS recipe fields.",
286
- "- xbbg_ext_constants: static lookup/format helpers for date parsing/formatting, futures month code/name mappings, dividend type mappings, and known dividend/ETF output columns.",
287
- "- xbbg_ext_columns: post-processing helpers for Bloomberg-shaped tables. Use rename_dividend_columns, rename_etf_columns, or build_earning_header_rename when explaining or normalizing response column names after a request.",
288
- "- xbbg_ext_calculate: small numeric helper for Bloomberg workflows. calculate_level_percentages pairs observed values with levels; values and levels must have the same length."
289
- ];
290
- var OPTIONAL_LIMIT_INSTRUCTIONS = [
291
- "",
292
- "## Request limits and inputs",
293
- "- Keep Bloomberg requests bounded: explicit securities, explicit fields, explicit dates, limited rows, and no broad exploratory pulls unless the user narrows the universe.",
294
- "- Respect configured tool limits for securities, fields, rows, string size, BQL length, and search spec length. Ask the user to narrow the request rather than exceeding them.",
295
- "- Use flat primitive overrides and kwargs only: string, number, or boolean values. Do not send nested objects, arrays, or inferred defaults as overrides."
296
- ];
297
- var BLOOMBERG_TOOL_INSTRUCTIONS = [
298
- ...REQUIRED_TOOL_INSTRUCTIONS,
299
- ...OPTIONAL_EXTENSION_INSTRUCTIONS,
300
- ...OPTIONAL_LIMIT_INSTRUCTIONS
301
- ].join("\n");
302
- function getBloombergToolInstructions(options = {}) {
303
- const includeExtensionGuidance = options.includeExtensionGuidance ?? true;
304
- const includeLimitReminder = options.includeLimitReminder ?? true;
305
- const lines = [...REQUIRED_TOOL_INSTRUCTIONS];
306
- if (includeExtensionGuidance) {
307
- lines.push(...OPTIONAL_EXTENSION_INSTRUCTIONS);
308
- }
309
- if (includeLimitReminder) {
310
- lines.push(...OPTIONAL_LIMIT_INSTRUCTIONS);
311
- }
312
- return lines.join("\n");
313
- }
314
- var BDP_DESCRIPTION = 'Bloomberg reference data for current or point-in-time fields. Use for a small bounded list of fully qualified securities. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: securities ["<TICKER> <MARKET_SECTOR>"], fields ["<FIELD>"].';
315
- var BDH_DESCRIPTION = 'Bloomberg historical time series. Requires explicit start and end dates; ask before using if the date range or periodicity is ambiguous. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: securities ["<TICKER> <MARKET_SECTOR>"], fields ["<FIELD>"], start "<START_DATE>", end "<END_DATE>".';
316
- var BDS_DESCRIPTION = 'Bloomberg bulk/table reference data. Requires exactly one bulk field, not a field list. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: securities ["<INDEX_TICKER> <MARKET_SECTOR>"], field "<BULK_FIELD>".';
317
- var BDIB_DESCRIPTION = 'Bloomberg intraday bars. Requires one ticker plus explicit ISO start/end datetimes with time components and a positive interval in minutes. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: ticker "<TICKER> <MARKET_SECTOR>", start "<START_DATETIME>", end "<END_DATETIME>", interval <MINUTES>.';
318
- var BDTICK_DESCRIPTION = 'Bloomberg intraday tick data. Requires one ticker plus explicit ISO start/end datetimes with time components. Set eventTypes explicitly, for example ["<EVENT_TYPE>"], and includeBrokerCodes/includeConditionCodes only when needed.';
319
- var BQL_DESCRIPTION = "Bloomberg Query Language expression sent as one complete query string. Use for bounded universe analytics with placeholder-shaped syntax such as get(<FIELD>) for('<TICKER> <MARKET_SECTOR>'), holdings('<ETF_TICKER> <MARKET_SECTOR>'), members('<INDEX_TICKER> <MARKET_SECTOR>'), filters with with(...), or dates=range(...). Prefer xbbg_bdp/xbbg_bdh for simple reference or historical requests.";
320
- var BSRCH_DESCRIPTION = 'Bloomberg search/grid request. Use for saved-search or ExcelGetGrid-style Bloomberg searches, not ordinary security lookup. Example searchSpec "<SEARCH_SPEC>".';
321
- var BQR_DESCRIPTION = 'Bloomberg Quote Request / dealer quotes. Use for fixed-income dealer quote ticks, preferably with an ISIN plus dealer source such as "/isin/<ISIN>@<QUOTE_SOURCE> <MARKET_SECTOR>"; requires explicit ISO start/end datetimes with time components. Set eventTypes explicitly, for example ["<EVENT_TYPE>"].';
322
- var BFLDS_DESCRIPTION = 'Bloomberg field metadata and field search. Use first when a field mnemonic is uncertain. Provide exactly one of fields or searchSpec. Example: fields ["<FIELD>"] or searchSpec "<FIELD_SEARCH_TEXT>".';
323
- var BEQS_DESCRIPTION = "Bloomberg equity screening by named BEQS screen. Use when the user names an existing Bloomberg screen and wants its bounded result set. Prefer this over hand-written BQL for saved Bloomberg screens.";
324
- var YAS_DESCRIPTION = "Bloomberg fixed-income YAS recipe fields for one or more bonds. Use for yield, duration, spread, benchmark, or price analytics; provide explicit fields and optional settlement/yield/price inputs.";
325
- var PREFERREDS_DESCRIPTION = "Preferred stock discovery for one equity ticker. Use when the user asks for preferred shares or preferred stock securities related to an issuer.";
326
- var CORPORATE_BONDS_DESCRIPTION = "Corporate bond universe query for one issuer/company ticker, with optional currency, active-only filter, and result fields. Prefer this over generic BQL for company debt discovery.";
327
- var INDEX_MEMBERS_DESCRIPTION = "Index constituent recipe for one Bloomberg index. Use for bounded member lists and optional historical/as-of constituent membership.";
328
- var RESOLVE_ISINS_DESCRIPTION = "Resolve raw ISIN strings to Bloomberg securities through the core ISIN recipe. Do not add /isin/ prefixes in this tool; pass the exact ISIN strings supplied by the user.";
329
- var ISSUER_ISINS_DESCRIPTION = "Issuer/bond ISIN workflow for supplied bond ISIN strings. Use for issuer-level ISIN discovery starting from known bond ISINs.";
330
- var ETF_HOLDINGS_DESCRIPTION = "ETF holdings recipe for one ETF ticker. Use when the user asks for ETF constituents or holdings and wants the bounded holdings result.";
331
- var STREAM_SNAPSHOT_DESCRIPTION = "Bounded live market-data snapshot from //blp/mktdata. Collects at most maxUpdates updates until timeout/done, then always unsubscribes; use for finite observations, not open subscriptions.";
332
- var MKTBAR_SNAPSHOT_DESCRIPTION = "Bounded live market-bar snapshot from //blp/mktbar for one ticker. Collects at most maxUpdates updates until timeout/done, then always unsubscribes.";
333
- var DEPTH_SNAPSHOT_DESCRIPTION = "Bounded live market-depth snapshot from //blp/mktdepthdata for one ticker. Collects at most maxUpdates updates until timeout/done, then always unsubscribes.";
334
- var EXT_TICKER_DESCRIPTION = "Ticker hygiene helpers: parse_ticker, normalize_tickers, filter_equity_tickers, is_specific_contract, and validate_generic_ticker.";
335
- var EXT_FUTURES_DESCRIPTION = "Futures helpers for contract construction and selection: build_futures_ticker, generate_candidates, contract_index, filter_candidates_by_cycle, filter_valid_contracts, and get_futures_months.";
336
- var EXT_CDX_DESCRIPTION = "CDX helpers for parsing, series rolling/resolution, and predefined info/pricing/risk BDP field bundles.";
337
- var EXT_CURRENCY_DESCRIPTION = "Currency planning helpers: build FX pairs, test same-currency requests, and find currencies needing conversion.";
338
- var EXT_BQL_BUILDER_DESCRIPTION = "BQL builders for preferred stocks, corporate bonds, and ETF holdings. Prefer to construct those bounded BQL shapes before xbbg_bql.";
339
- var EXT_MARKET_SESSION_DESCRIPTION = "Market session and timezone helpers for deriving sessions, UTC windows, market rules, exchange metadata, turnover defaults, and BQR datetime defaults.";
340
- var EXT_YAS_OVERRIDES_DESCRIPTION = "Build flat Bloomberg YAS override maps for fixed-income analytics fields.";
341
- var EXT_CONSTANTS_DESCRIPTION = "Static Bloomberg helper constants for date parsing/formatting, futures months, dividend types, and ETF/dividend columns.";
342
- var EXT_COLUMNS_DESCRIPTION = "Column rename helpers for dividend, ETF, and earnings-shaped Bloomberg responses.";
343
- var EXT_CALCULATE_DESCRIPTION = "Small numeric helper operations for Bloomberg workflows, including level percentage calculations.";
344
178
 
345
179
  // src/result-limits.ts
346
180
  var MAX_RESULT_DEPTH = 32;
@@ -383,7 +217,16 @@ function limitValue(value, maxRows, maxStringChars, state, depth = 0, seen = /*
383
217
  state.truncated = true;
384
218
  return "[Circular]";
385
219
  }
220
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
221
+ state.truncated = true;
222
+ return `[binary data: ${value.byteLength} bytes]`;
223
+ }
386
224
  if (!isPlainObject(value)) {
225
+ const toJSON = value.toJSON;
226
+ if (typeof toJSON === "function") {
227
+ seen.add(value);
228
+ return limitValue(toJSON.call(value), maxRows, maxStringChars, state, depth + 1, seen);
229
+ }
387
230
  return value;
388
231
  }
389
232
  seen.add(value);
@@ -402,12 +245,12 @@ function rowCountOf(value) {
402
245
  if (typeof value !== "object" || value === null) {
403
246
  return null;
404
247
  }
405
- const record2 = value;
406
- const rowCount = record2.rowCount;
248
+ const record3 = value;
249
+ const rowCount = record3.rowCount;
407
250
  if (typeof rowCount === "number" && Number.isInteger(rowCount) && rowCount >= 0) {
408
251
  return rowCount;
409
252
  }
410
- const updateCount = record2.updateCount;
253
+ const updateCount = record3.updateCount;
411
254
  if (typeof updateCount === "number" && Number.isInteger(updateCount) && updateCount >= 0) {
412
255
  return updateCount;
413
256
  }
@@ -462,8 +305,10 @@ function limitResult(value, maxRows, maxStringChars) {
462
305
  function summarizeEnvelope(envelope) {
463
306
  const rowText = envelope.rowCount === null ? "row count unknown" : `${envelope.rowCount} row${envelope.rowCount === 1 ? "" : "s"}`;
464
307
  const notes = [];
465
- if (envelope.rowCount === 0) {
466
- notes.push("empty result");
308
+ if (envelope.rowCount === 0 || envelope.data === null || envelope.data === void 0) {
309
+ notes.push(
310
+ "empty result; verify identifiers, fields, and date range before concluding no data exists"
311
+ );
467
312
  }
468
313
  if (envelope.truncated) {
469
314
  notes.push("artifact truncated to configured limits");
@@ -503,16 +348,596 @@ function createToolResult(tool2, value, maxRows, maxStringChars) {
503
348
  function throwWithToolContext(tool2, error) {
504
349
  const prefix = `${tool2} failed`;
505
350
  if (error instanceof Error) {
506
- if (!error.message.startsWith(prefix)) {
507
- Object.defineProperty(error, "message", {
508
- configurable: true,
509
- value: `${prefix}: ${error.message}`
510
- });
351
+ if (error.message.startsWith(prefix)) {
352
+ throw error;
353
+ }
354
+ const wrapped = new Error(`${prefix}: ${error.message}`, { cause: error });
355
+ wrapped.name = error.name;
356
+ throw wrapped;
357
+ }
358
+ throw new Error(`${prefix}: ${String(error)}`);
359
+ }
360
+
361
+ // src/langchain-tool.ts
362
+ function inputJsonSchema(schema) {
363
+ const jsonSchema = zodToJsonSchema.zodToJsonSchema(schema, {
364
+ $refStrategy: "none",
365
+ effectStrategy: "input",
366
+ pipeStrategy: "input"
367
+ });
368
+ delete jsonSchema.$schema;
369
+ delete jsonSchema.definitions;
370
+ return jsonSchema;
371
+ }
372
+ function toolParameterJsonSchema(toolInstance) {
373
+ const schema = toolInstance.schema;
374
+ if (schema !== null && typeof schema === "object" && !("safeParse" in schema)) {
375
+ return schema;
376
+ }
377
+ return inputJsonSchema(schema);
378
+ }
379
+ function createBloombergStructuredTool(func, fields) {
380
+ const providerToolDefinition = {
381
+ type: "function",
382
+ function: {
383
+ description: fields.description,
384
+ name: fields.name,
385
+ parameters: inputJsonSchema(fields.schema)
386
+ }
387
+ };
388
+ const guarded = async (input, config) => {
389
+ try {
390
+ config?.signal?.throwIfAborted();
391
+ } catch (error) {
392
+ throwWithToolContext(fields.name, error);
393
+ }
394
+ return await func(input, config);
395
+ };
396
+ return tools.tool(
397
+ guarded,
398
+ {
399
+ ...fields,
400
+ extras: { providerToolDefinition }
401
+ }
402
+ );
403
+ }
404
+
405
+ // src/chart-spec.ts
406
+ var VEGA_SCHEMA = "https://vega.github.io/schema/vega-lite/v5.json";
407
+ var COMPONENT_NAME = "xbbg_chart";
408
+ var X_FIELD_CANDIDATES = ["date", "time", "datetime", "timestamp"];
409
+ var LABEL_FIELD_CANDIDATES = ["ticker", "security", "member", "name", "label"];
410
+ var SERIES_FIELD_CANDIDATES = ["ticker", "security", "field", "side", "category"];
411
+ var VALUE_FIELD_CANDIDATES = [
412
+ "value",
413
+ "PX_LAST",
414
+ "close",
415
+ "price",
416
+ "weight",
417
+ "marketValue",
418
+ "market_value"
419
+ ];
420
+ var OPEN_FIELD_CANDIDATES = ["open", "OPEN", "PX_OPEN"];
421
+ var HIGH_FIELD_CANDIDATES = ["high", "HIGH", "PX_HIGH"];
422
+ var LOW_FIELD_CANDIDATES = ["low", "LOW", "PX_LOW"];
423
+ var CLOSE_FIELD_CANDIDATES = ["close", "CLOSE", "PX_LAST", "last", "value"];
424
+ var SIDE_FIELD_CANDIDATES = ["side", "SIDE", "type"];
425
+ var PRICE_FIELD_CANDIDATES = ["price", "PRICE", "px", "PX"];
426
+ var SIZE_FIELD_CANDIDATES = ["size", "SIZE", "quantity", "qty", "volume"];
427
+ function defaultChartForSource(source) {
428
+ switch (source) {
429
+ case "bdib":
430
+ return "candlestick";
431
+ case "depth":
432
+ return "depth";
433
+ case "holdings":
434
+ return "bar";
435
+ case "bdh":
436
+ case "rows":
437
+ return "line";
438
+ }
439
+ }
440
+ function fieldExists(rows, field) {
441
+ for (const row of rows) {
442
+ if (Object.prototype.hasOwnProperty.call(row, field)) {
443
+ return true;
444
+ }
445
+ }
446
+ return false;
447
+ }
448
+ function findCandidateField(rows, candidates) {
449
+ for (const candidate of candidates) {
450
+ if (fieldExists(rows, candidate)) {
451
+ return candidate;
452
+ }
453
+ }
454
+ const first = rows[0];
455
+ if (first === void 0) {
456
+ return void 0;
457
+ }
458
+ const keys = Object.keys(first);
459
+ for (const candidate of candidates) {
460
+ const lower = candidate.toLowerCase();
461
+ const match = keys.find((key) => key.toLowerCase() === lower);
462
+ if (match !== void 0 && fieldExists(rows, match)) {
463
+ return match;
464
+ }
465
+ }
466
+ return void 0;
467
+ }
468
+ function requireField(rows, field, label, candidates) {
469
+ const resolved = field ?? findCandidateField(rows, candidates);
470
+ if (resolved === void 0 || !fieldExists(rows, resolved)) {
471
+ throw new Error(
472
+ `Missing ${label}; pass ${label} explicitly or include one of: ${candidates.join(", ")}`
473
+ );
474
+ }
475
+ return resolved;
476
+ }
477
+ function hasFiniteNumber(rows, field) {
478
+ for (const row of rows) {
479
+ if (typeof row[field] === "number" && Number.isFinite(row[field])) {
480
+ return true;
481
+ }
482
+ }
483
+ return false;
484
+ }
485
+ function firstNumericField(rows, excludedField) {
486
+ const first = rows[0];
487
+ if (first === void 0) {
488
+ return void 0;
489
+ }
490
+ for (const key of Object.keys(first)) {
491
+ if (key !== excludedField && hasFiniteNumber(rows, key)) {
492
+ return key;
493
+ }
494
+ }
495
+ return void 0;
496
+ }
497
+ function requireNumericField(rows, field, label) {
498
+ if (!hasFiniteNumber(rows, field)) {
499
+ throw new Error(`${label} (${field}) must contain at least one finite numeric value`);
500
+ }
501
+ }
502
+ function inferVegaType(rows, field) {
503
+ for (const row of rows) {
504
+ const value = row[field];
505
+ if (typeof value === "number") {
506
+ return "quantitative";
507
+ }
508
+ if (typeof value === "string" && (/^\d{4}-\d{2}-\d{2}(?:$|[T\s])/u.test(value) || /^\d{8}$/u.test(value))) {
509
+ return "temporal";
510
+ }
511
+ }
512
+ return "nominal";
513
+ }
514
+ function normalizeTemporalRows(rows, field) {
515
+ let normalized;
516
+ for (let index = 0; index < rows.length; index += 1) {
517
+ const row = rows[index];
518
+ if (row === void 0) {
519
+ continue;
520
+ }
521
+ const value = row[field];
522
+ if (typeof value !== "string" || !/^\d{8}$/u.test(value)) {
523
+ normalized?.push(row);
524
+ continue;
525
+ }
526
+ normalized ??= rows.slice(0, index);
527
+ normalized.push({
528
+ ...row,
529
+ [field]: `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6, 8)}`
530
+ });
531
+ }
532
+ return normalized ?? rows;
533
+ }
534
+ function tooltip(fields) {
535
+ return fields.map((field) => ({
536
+ field,
537
+ type: field === "_xbbg_value" ? "quantitative" : "nominal"
538
+ }));
539
+ }
540
+ function datumField(field) {
541
+ return `datum[${JSON.stringify(field)}]`;
542
+ }
543
+ function buildGenericSpec(input, rows, chart, title) {
544
+ const xField = requireField(rows, input.xField, "xField", X_FIELD_CANDIDATES);
545
+ let yFields;
546
+ if (input.yFields !== void 0) {
547
+ yFields = input.yFields;
548
+ } else {
549
+ const yField = findCandidateField(rows, VALUE_FIELD_CANDIDATES) ?? firstNumericField(rows, xField);
550
+ if (yField === void 0) {
551
+ throw new Error("Missing yFields; include at least one numeric value field");
552
+ }
553
+ yFields = [yField];
554
+ }
555
+ if (yFields.length === 0) {
556
+ throw new Error("Missing yFields; include at least one numeric value field");
557
+ }
558
+ for (const field of yFields) {
559
+ if (!fieldExists(rows, field)) {
560
+ throw new Error(`Missing y field: ${field}`);
561
+ }
562
+ requireNumericField(rows, field, "yField");
563
+ }
564
+ const normalizedRows = inferVegaType(rows, xField) === "temporal" ? normalizeTemporalRows(rows, xField) : rows;
565
+ const seriesField = input.seriesField ?? (yFields.length === 1 ? findCandidateField(rows, SERIES_FIELD_CANDIDATES) : void 0);
566
+ if (seriesField !== void 0 && !fieldExists(rows, seriesField)) {
567
+ throw new Error(`Missing series field: ${seriesField}`);
568
+ }
569
+ const mark = chart === "scatter" ? "point" : chart;
570
+ const encoding = {
571
+ x: { field: xField, title: xField, type: inferVegaType(normalizedRows, xField) }
572
+ };
573
+ const transform = [];
574
+ if (yFields.length === 1) {
575
+ const yField = yFields[0];
576
+ if (yField === void 0) {
577
+ throw new Error("Missing yFields; include at least one numeric value field");
578
+ }
579
+ encoding.y = { field: yField, title: yField, type: "quantitative" };
580
+ if (seriesField !== void 0) {
581
+ encoding.color = { field: seriesField, title: seriesField, type: "nominal" };
511
582
  }
512
- throw error;
583
+ encoding.tooltip = tooltip([
584
+ xField,
585
+ ...seriesField === void 0 ? [] : [seriesField],
586
+ yField
587
+ ]);
588
+ } else {
589
+ transform.push({ as: ["_xbbg_series", "_xbbg_value"], fold: yFields });
590
+ encoding.y = { field: "_xbbg_value", title: "value", type: "quantitative" };
591
+ encoding.color = { field: "_xbbg_series", title: "series", type: "nominal" };
592
+ if (seriesField !== void 0) {
593
+ encoding.detail = { field: seriesField, type: "nominal" };
594
+ }
595
+ encoding.tooltip = tooltip([
596
+ xField,
597
+ ...seriesField === void 0 ? [] : [seriesField],
598
+ "_xbbg_series",
599
+ "_xbbg_value"
600
+ ]);
601
+ }
602
+ const spec = {
603
+ $schema: VEGA_SCHEMA,
604
+ data: { values: normalizedRows },
605
+ description: `xbbg ${chart} chart spec for ${input.source}`,
606
+ mark: { type: mark, tooltip: true },
607
+ title,
608
+ ...transform.length === 0 ? {} : { transform },
609
+ encoding
610
+ };
611
+ return { spec, xField, yFields, ...seriesField === void 0 ? {} : { seriesField } };
612
+ }
613
+ function buildBarSpec(input, rows, title) {
614
+ const xField = requireField(
615
+ rows,
616
+ input.xField ?? input.labelField,
617
+ "labelField",
618
+ LABEL_FIELD_CANDIDATES
619
+ );
620
+ const yField = requireField(
621
+ rows,
622
+ input.valueField ?? input.yFields?.[0],
623
+ "valueField",
624
+ VALUE_FIELD_CANDIDATES
625
+ );
626
+ requireNumericField(rows, yField, "valueField");
627
+ const seriesField = input.seriesField;
628
+ if (seriesField !== void 0 && !fieldExists(rows, seriesField)) {
629
+ throw new Error(`Missing series field: ${seriesField}`);
630
+ }
631
+ const encoding = {
632
+ x: { field: xField, sort: "-y", title: xField, type: inferVegaType(rows, xField) },
633
+ y: { field: yField, title: yField, type: "quantitative" },
634
+ tooltip: tooltip([xField, ...seriesField === void 0 ? [] : [seriesField], yField])
635
+ };
636
+ if (seriesField !== void 0) {
637
+ encoding.color = { field: seriesField, title: seriesField, type: "nominal" };
638
+ }
639
+ return {
640
+ spec: {
641
+ $schema: VEGA_SCHEMA,
642
+ data: { values: rows },
643
+ description: `xbbg bar chart spec for ${input.source}`,
644
+ encoding,
645
+ mark: { type: "bar", tooltip: true },
646
+ title
647
+ },
648
+ xField,
649
+ yFields: [yField],
650
+ ...seriesField === void 0 ? {} : { seriesField }
651
+ };
652
+ }
653
+ function buildCandlestickSpec(input, rows, title) {
654
+ const xField = requireField(rows, input.xField, "xField", X_FIELD_CANDIDATES);
655
+ const openField = requireField(rows, input.openField, "openField", OPEN_FIELD_CANDIDATES);
656
+ const highField = requireField(rows, input.highField, "highField", HIGH_FIELD_CANDIDATES);
657
+ const lowField = requireField(rows, input.lowField, "lowField", LOW_FIELD_CANDIDATES);
658
+ const closeField = requireField(rows, input.closeField, "closeField", CLOSE_FIELD_CANDIDATES);
659
+ for (const [label, field] of [
660
+ ["openField", openField],
661
+ ["highField", highField],
662
+ ["lowField", lowField],
663
+ ["closeField", closeField]
664
+ ]) {
665
+ requireNumericField(rows, field, label);
666
+ }
667
+ const normalizedRows = inferVegaType(rows, xField) === "temporal" ? normalizeTemporalRows(rows, xField) : rows;
668
+ const color = {
669
+ condition: { test: `${datumField(closeField)} >= ${datumField(openField)}`, value: "#137333" },
670
+ value: "#c5221f"
671
+ };
672
+ return {
673
+ spec: {
674
+ $schema: VEGA_SCHEMA,
675
+ data: { values: normalizedRows },
676
+ description: `xbbg candlestick chart spec for ${input.source}`,
677
+ encoding: {
678
+ x: { field: xField, title: xField, type: inferVegaType(normalizedRows, xField) }
679
+ },
680
+ layer: [
681
+ {
682
+ mark: "rule",
683
+ encoding: {
684
+ color,
685
+ tooltip: tooltip([xField, openField, highField, lowField, closeField]),
686
+ y: { field: lowField, title: "price", type: "quantitative" },
687
+ y2: { field: highField }
688
+ }
689
+ },
690
+ {
691
+ mark: "bar",
692
+ encoding: {
693
+ color,
694
+ y: { field: openField, title: "price", type: "quantitative" },
695
+ y2: { field: closeField }
696
+ }
697
+ }
698
+ ],
699
+ title
700
+ },
701
+ xField,
702
+ yFields: [openField, highField, lowField, closeField]
703
+ };
704
+ }
705
+ function buildDepthSpec(input, rows, title) {
706
+ const priceField = requireField(
707
+ rows,
708
+ input.priceField ?? input.xField,
709
+ "priceField",
710
+ PRICE_FIELD_CANDIDATES
711
+ );
712
+ const sizeField = requireField(
713
+ rows,
714
+ input.sizeField ?? input.valueField ?? input.yFields?.[0],
715
+ "sizeField",
716
+ SIZE_FIELD_CANDIDATES
717
+ );
718
+ const sideField = requireField(
719
+ rows,
720
+ input.sideField ?? input.seriesField,
721
+ "sideField",
722
+ SIDE_FIELD_CANDIDATES
723
+ );
724
+ requireNumericField(rows, priceField, "priceField");
725
+ requireNumericField(rows, sizeField, "sizeField");
726
+ return {
727
+ spec: {
728
+ $schema: VEGA_SCHEMA,
729
+ data: { values: rows },
730
+ description: `xbbg market depth chart spec for ${input.source}`,
731
+ encoding: {
732
+ color: { field: sideField, title: sideField, type: "nominal" },
733
+ tooltip: tooltip([sideField, priceField, sizeField]),
734
+ x: { field: priceField, title: priceField, type: "quantitative" },
735
+ y: { field: sizeField, title: sizeField, type: "quantitative" }
736
+ },
737
+ mark: { type: "bar", tooltip: true },
738
+ title
739
+ },
740
+ xField: priceField,
741
+ yFields: [sizeField],
742
+ seriesField: sideField
743
+ };
744
+ }
745
+ function createChartSpec(input) {
746
+ const maxPoints = input.maxPoints ?? input.rows.length;
747
+ const rows = input.rows.length > maxPoints ? input.rows.slice(0, maxPoints) : input.rows;
748
+ if (rows.length === 0) {
749
+ throw new Error("rows must contain at least one chart data row");
750
+ }
751
+ const chart = input.chart ?? defaultChartForSource(input.source);
752
+ const title = input.title ?? `${input.source} ${chart}`;
753
+ const warnings = [];
754
+ if (rows.length !== input.rows.length) {
755
+ warnings.push(
756
+ `Chart spec contains first ${rows.length} of ${input.rows.length} rows; narrow the upstream request for a complete visualization.`
757
+ );
758
+ }
759
+ const built = chart === "candlestick" ? buildCandlestickSpec(input, rows, title) : chart === "depth" ? buildDepthSpec(input, rows, title) : chart === "bar" ? buildBarSpec(input, rows, title) : buildGenericSpec(input, rows, chart, title);
760
+ const summary = {
761
+ chart,
762
+ inputRows: input.rows.length,
763
+ renderer: "vega-lite",
764
+ rowCount: rows.length,
765
+ source: input.source,
766
+ title,
767
+ truncatedInput: rows.length !== input.rows.length,
768
+ xField: built.xField,
769
+ yFields: built.yFields,
770
+ ...built.seriesField === void 0 ? {} : { seriesField: built.seriesField }
771
+ };
772
+ return {
773
+ kind: "xbbg.visualization",
774
+ version: 1,
775
+ component: COMPONENT_NAME,
776
+ renderer: "vega-lite",
777
+ rowCount: rows.length,
778
+ inputRowCount: input.rows.length,
779
+ truncatedInput: rows.length !== input.rows.length,
780
+ source: input.source,
781
+ chart,
782
+ summary,
783
+ spec: built.spec,
784
+ warnings
785
+ };
786
+ }
787
+
788
+ // src/cdx-fields.ts
789
+ var CDX_INFO_FIELDS = Object.freeze([
790
+ "ROLLING_SERIES",
791
+ "VERSION",
792
+ "ON_THE_RUN_CURRENT_BD_INDICATOR",
793
+ "CDS_FIRST_ACCRUAL_START_DATE",
794
+ "NAME",
795
+ "NUM_CURRENT_COMPANIES_CCY_TKR",
796
+ "NUM_ORIG_COMPANIES_CRNCY_TKR",
797
+ "PX_LAST"
798
+ ]);
799
+ var CDX_PRICING_FIELDS = Object.freeze([
800
+ "PX_LAST",
801
+ "PX_BID",
802
+ "PX_ASK",
803
+ "UPFRONT_LAST",
804
+ "UPFRONT_BID",
805
+ "UPFRONT_ASK",
806
+ "CDS_FLAT_SPREAD",
807
+ "UPFRONT_FEE",
808
+ "PV_CDS_PREMIUM_LEG",
809
+ "PV_CDS_DEFAULT_LEG"
810
+ ]);
811
+ var CDX_RISK_FIELDS = Object.freeze([
812
+ "SW_CNV_BPV",
813
+ "SW_EQV_BPV",
814
+ "CDS_SPREAD_MID_MODIFIED_DURATION",
815
+ "CDS_SPREAD_MID_CONVEXITY",
816
+ "RECOVERY_RATE_SEN",
817
+ "CDS_RECOVERY_RT"
818
+ ]);
819
+
820
+ // src/descriptions.ts
821
+ var REQUIRED_TOOL_INSTRUCTIONS = [
822
+ "# Bloomberg tool usage",
823
+ "- Use these tools only for server-side Bloomberg data access through @xbbg/core. Never imply Bloomberg data was retrieved unless a tool call actually returned it.",
824
+ "- Ask a clarifying question before calling a tool when any security identity, field mnemonic, date range, currency, periodicity, intraday interval, timezone, override, or universe is ambiguous.",
825
+ "- Do not invent Bloomberg tickers, field mnemonics, overrides, or BQL functions. If the user gives a field description rather than a confident mnemonic, call xbbg_bflds first.",
826
+ "- Issue one tool call per dataset and read any error before retrying; never probe parameter variants in parallel. Omit optional output-shape parameters such as format unless the user asked for a specific shape.",
827
+ "",
828
+ "## Security identifiers",
829
+ "- Pass each security in the form the user supplied it; never translate between identifier kinds on your own.",
830
+ "- User supplied a Bloomberg ticker: pass it through fully qualified as <TICKER> <MARKET_SECTOR>, for example <TICKER> <EXCHANGE> Equity, <INDEX_TICKER> Index, or <CCY_PAIR> Curncy.",
831
+ "- The market sector ending (Bloomberg yellow key) is part of the security string. The sectors are: Equity, Index, Curncy, Comdty, Corp, Govt, Muni, Mtge, M-Mkt, and Pfd. Equity securities carry an exchange or composite code before the sector (<TICKER> <EXCHANGE> Equity); preferred securities use the Pfd sector; corporate and government bonds use Corp and Govt. Request tools pass the security through to Bloomberg without validating the sector, so copy it exactly as the user supplied it.",
832
+ "- User supplied a raw ISIN or CUSIP: pass Bloomberg identifier syntax directly: /isin/<ISIN> or /cusip/<CUSIP>. Never pass the bare identifier without its prefix, except to xbbg_resolve_isins and xbbg_issuer_isins, which take raw ISIN strings.",
833
+ "- <TICKER> <MARKET_SECTOR> is a format template, not authorization to construct a ticker. Never invent, recall from memory, or guess the Bloomberg ticker behind an identifier the user gave; identifier syntax is already a complete, valid security input. Use xbbg_resolve_isins only when the user wants the resolved Bloomberg security itself.",
834
+ "- Recipe tools that take tickers (xbbg_preferreds, xbbg_corporate_bonds, xbbg_index_members, xbbg_etf_holdings) do not accept identifier syntax. When the user supplied an ISIN or CUSIP for those workflows, resolve it with xbbg_resolve_isins first and use the returned Bloomberg security; never guess the ticker.",
835
+ "- Do not use xbbg_bsrch as a replacement for a known ticker, ISIN, or CUSIP.",
836
+ "- For dealer quote / BQR workflows, use xbbg_bqr with a fixed-income identifier plus a dealer quote source such as /isin/<ISIN>@<QUOTE_SOURCE> <MARKET_SECTOR>. For raw intraday ticks, use xbbg_bdtick.",
837
+ "",
838
+ "## Core request tools",
839
+ "- xbbg_bdp: current or reference point-in-time fields. Use a small explicit securities list and a small explicit fields list. Use includeSecurityErrors only when the caller wants Bloomberg security errors in the response.",
840
+ "- xbbg_bdh: historical daily or periodic time series. Always provide explicit start and end dates in YYYY-MM-DD or YYYYMMDD form. Ask before choosing periodicity, currency, fill behavior, adjustment overrides, or a wide output table.",
841
+ "- xbbg_bds: Bloomberg bulk/table fields. Provide exactly one bulk field; do not use bds for ordinary multi-field reference data.",
842
+ "- xbbg_bdib: intraday bars only. Provide one ticker, explicit ISO start/end datetimes with time components, a positive interval in minutes, and timezone context when datetimes are naive.",
843
+ "- xbbg_bdtick: intraday tick data. Provide one ticker, explicit ISO start/end datetimes with time components, and explicit eventTypes unless the default event stream is intended. Use includeBrokerCodes or includeConditionCodes only when those columns are needed.",
844
+ "- xbbg_bql: BQL expressions only when the user asks for BQL or the request is naturally expressed as a bounded BQL query. Keep queries short, explicit, and scoped to the requested universe.",
845
+ "- xbbg_bsrch: Bloomberg search-grid or saved-search workflows only. Do not use it for ordinary security lookup.",
846
+ "- xbbg_bqr: Bloomberg Quote Request / dealer quotes. Prefer fixed-income identifier inputs with a dealer quote source such as /isin/<ISIN>@<QUOTE_SOURCE> <MARKET_SECTOR>, explicit start/end datetimes with time components, and explicit event types.",
847
+ "- xbbg_bflds: Bloomberg field metadata/search. Provide exactly one of fields or searchSpec; use searchSpec for natural-language field names and fields for known mnemonics.",
848
+ "- xbbg_beqs: Bloomberg equity screening by named BEQS screen. Prefer this over hand-written BQL when the user names an existing Bloomberg screen.",
849
+ "- xbbg_yas: fixed-income YAS recipe fields. Prefer this over manual YAS-style BDP requests when the user asks for yield, duration, spread, or price analytics.",
850
+ "- xbbg_preferreds: preferred stock discovery from the issuer's common equity ticker, never a guessed preferred ('Pfd') ticker. Resolve a supplied ISIN/CUSIP with xbbg_resolve_isins first. Prefer this over xbbg_ext_bql_builder plus xbbg_bql when the user wants the actual preferreds result.",
851
+ "- xbbg_corporate_bonds: bounded corporate bond universe query for a company ticker. Prefer this over generic BQL for company debt discovery.",
852
+ "- xbbg_index_members: index constituents through the core index recipe. Prefer this over generic BDS/BQL members when the user asks for constituents.",
853
+ "- xbbg_resolve_isins: resolves supplied ISIN strings to Bloomberg securities. Pass raw ISIN strings only for this recipe; otherwise use /isin/<ISIN> syntax with data tools.",
854
+ "- xbbg_issuer_isins: issuer/bond ISIN workflow for supplied bond ISIN strings.",
855
+ "- xbbg_etf_holdings: ETF holdings recipe for a single ETF ticker. Prefer this over generic BQL holdings when the user asks for ETF constituents.",
856
+ "- xbbg_stream_snapshot: bounded live market-data observation from //blp/mktdata. Requires explicit maxUpdates and always terminates/unsubscribes.",
857
+ "- xbbg_mktbar_snapshot: bounded live market-bar observation from //blp/mktbar for one ticker. Requires explicit maxUpdates and always terminates/unsubscribes.",
858
+ "- xbbg_depth_snapshot: bounded market-depth observation from //blp/mktdepthdata for one ticker. Requires explicit maxUpdates and always terminates/unsubscribes.",
859
+ "",
860
+ "## BQL guidance",
861
+ "- BQL is a complete Bloomberg Query Language expression sent as one query string; the tool does not assemble get/for/with clauses for you.",
862
+ "- Basic shape: get(<FIELD_1>, <FIELD_2>) for(<UNIVERSE>). Use placeholders such as '<TICKER> <MARKET_SECTOR>', holdings('<ETF_TICKER> <MARKET_SECTOR>'), or members('<INDEX_TICKER> <MARKET_SECTOR>') until the user supplies real inputs.",
863
+ "- Use BQL for universe-oriented analytics and screens only when the user provides a bounded universe, filters, and date range.",
864
+ "- Prefer xbbg_ext_bql_builder instead of hand-writing BQL for supported workflows: preferred stocks, corporate bonds, and ETF holdings.",
865
+ "- Do not use BQL just because the user asks for normal reference data; xbbg_bdp is simpler for current fields and xbbg_bdh is simpler for historical time series.",
866
+ "",
867
+ "## Output handling",
868
+ "- Tool results use LangChain content_and_artifact output: content starts with a compact summary and then includes bounded model-readable JSON; artifact is the structured bounded envelope with tool, rowCount, truncated, and data for application code.",
869
+ "- If a response is empty, truncated, or contains Bloomberg/security errors, say that directly. Do not fill gaps from memory or assumptions."
870
+ ];
871
+ var OPTIONAL_EXTENSION_INSTRUCTIONS = [
872
+ "",
873
+ "## Extension helper tools",
874
+ "- xbbg_ext_ticker: ticker hygiene before live calls. parse_ticker splits generic futures-style tickers only \u2014 asset endings Index, Curncy, Comdty, or Corp as <ROOT><N> <ASSET>, or <ROOT><N> <EXCHANGE> Equity \u2014 and rejects other market sectors (Pfd, Govt, Muni, Mtge, M-Mkt) and non-futures securities. normalize_tickers trims/canonicalizes lists, filter_equity_tickers keeps equity-like tickers, is_specific_contract checks futures specificity, and validate_generic_ticker rejects malformed generic futures tickers.",
875
+ "- xbbg_ext_futures: futures contract construction and selection. Use build_futures_ticker for root/month/year/asset assembly, get_futures_months for month-code lookup, generate_candidates for generic-to-specific candidates, contract_index for generic contract rank, filter_candidates_by_cycle for HMUZ/quarterly cycles, and filter_valid_contracts to keep contracts valid for a date.",
876
+ "- xbbg_ext_cdx: CDX ticker workflow support. Use parse_cdx_ticker to understand a CDX ticker, previous_cdx_series to roll back a series, cdx_gen_to_specific to resolve a generic CDX to a target series, and cdx_info/cdx_pricing/cdx_risk for predefined BDP field bundles. cdx_pricing and cdx_risk accept recoveryRate, which becomes the CDS_RR override.",
877
+ "- xbbg_ext_currency: currency-planning helpers. build_fx_pair constructs the Bloomberg FX pair and conversion factor, same_currency avoids unnecessary conversion, and currencies_needing_conversion identifies which currencies differ from a target before requesting converted values.",
878
+ "- xbbg_ext_bql_builder: safe BQL generators for common xbbg workflows. Use build_preferreds_query for preferred-stock discovery from an equity, build_corporate_bonds_query for company bond universes with optional currency/active filters, and build_etf_holdings_query for ETF constituents. Prefer these builders over hand-writing those BQL shapes.",
879
+ "- xbbg_ext_chart_spec: renderer-neutral chart spec helper. Convert bounded rows from xbbg_bdh, xbbg_bdib, holdings, depth, or already-shaped row data into a Vega-Lite JSON spec for frontend rendering; do not use it as proof that Bloomberg data was fetched.",
880
+ "- xbbg_ext_market_session: exchange calendar/timezone support. derive_sessions turns day session times into session blocks, infer_timezone maps country codes to timezones, session_times_to_utc converts local sessions to UTC, get_market_rule gets MIC/exchange rules, default_turnover_dates and default_bqr_datetimes provide bounded defaults, and get/list_exchange_override inspect configured exchange metadata.",
881
+ "- xbbg_ext_yas_overrides: builds flat YAS override maps for fixed-income BDP requests when the lower-level BDP workflow is required. Prefer xbbg_yas for actual YAS recipe fields.",
882
+ "- xbbg_ext_constants: static lookup/format helpers for date parsing/formatting, futures month code/name mappings, dividend type mappings, and known dividend/ETF output columns.",
883
+ "- xbbg_ext_columns: post-processing helpers for Bloomberg-shaped tables. Use rename_dividend_columns, rename_etf_columns, or build_earning_header_rename when explaining or normalizing response column names after a request.",
884
+ "- xbbg_ext_calculate: small numeric helper for Bloomberg workflows. calculate_level_percentages pairs observed values with levels; values and levels must have the same length."
885
+ ];
886
+ var OPTIONAL_LIMIT_INSTRUCTIONS = [
887
+ "",
888
+ "## Request limits and inputs",
889
+ "- Keep Bloomberg requests bounded: explicit securities, explicit fields, explicit dates, limited rows, and no broad exploratory pulls unless the user narrows the universe.",
890
+ "- Respect configured tool limits for securities, fields, rows, string size, BQL length, and search spec length. Ask the user to narrow the request rather than exceeding them.",
891
+ "- Use flat primitive overrides and kwargs only: string, number, or boolean values. Do not send nested objects, arrays, or inferred defaults as overrides."
892
+ ];
893
+ var BLOOMBERG_TOOL_INSTRUCTIONS = [
894
+ ...REQUIRED_TOOL_INSTRUCTIONS,
895
+ ...OPTIONAL_EXTENSION_INSTRUCTIONS,
896
+ ...OPTIONAL_LIMIT_INSTRUCTIONS
897
+ ].join("\n");
898
+ function getBloombergToolInstructions(options = {}) {
899
+ const includeExtensionGuidance = options.includeExtensionGuidance ?? true;
900
+ const includeLimitReminder = options.includeLimitReminder ?? true;
901
+ const lines = [...REQUIRED_TOOL_INSTRUCTIONS];
902
+ if (includeExtensionGuidance) {
903
+ lines.push(...OPTIONAL_EXTENSION_INSTRUCTIONS);
513
904
  }
514
- throw new Error(`${prefix}: ${String(error)}`);
905
+ if (includeLimitReminder) {
906
+ lines.push(...OPTIONAL_LIMIT_INSTRUCTIONS);
907
+ }
908
+ return lines.join("\n");
515
909
  }
910
+ var BDP_DESCRIPTION = 'Bloomberg reference data for current or point-in-time fields. Use for a small bounded list of fully qualified securities. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: securities ["<TICKER> <MARKET_SECTOR>"], fields ["<FIELD>"].';
911
+ var BDH_DESCRIPTION = 'Bloomberg historical time series. Requires explicit start and end dates; ask before using if the date range or periodicity is ambiguous. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: securities ["<TICKER> <MARKET_SECTOR>"], fields ["<FIELD>"], start "<START_DATE>", end "<END_DATE>".';
912
+ var BDS_DESCRIPTION = 'Bloomberg bulk/table reference data. Requires exactly one bulk field, not a field list. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: securities ["<INDEX_TICKER> <MARKET_SECTOR>"], field "<BULK_FIELD>".';
913
+ var BDIB_DESCRIPTION = 'Bloomberg intraday bars. Requires one ticker plus explicit ISO start/end datetimes with time components and a positive interval in minutes. Use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Example: ticker "<TICKER> <MARKET_SECTOR>", start "<START_DATETIME>", end "<END_DATETIME>", interval <MINUTES>.';
914
+ var BDTICK_DESCRIPTION = 'Bloomberg intraday tick data. Requires one ticker plus explicit ISO start/end datetimes with time components. Set eventTypes explicitly, for example ["<EVENT_TYPE>"], and includeBrokerCodes/includeConditionCodes only when needed.';
915
+ var BQL_DESCRIPTION = "Bloomberg Query Language expression sent as one complete query string. Use for bounded universe analytics with placeholder-shaped syntax such as get(<FIELD>) for('<TICKER> <MARKET_SECTOR>'), holdings('<ETF_TICKER> <MARKET_SECTOR>'), members('<INDEX_TICKER> <MARKET_SECTOR>'), filters with with(...), or dates=range(...). Prefer xbbg_bdp/xbbg_bdh for simple reference or historical requests.";
916
+ var BSRCH_DESCRIPTION = 'Bloomberg search/grid request. Use for saved-search or ExcelGetGrid-style Bloomberg searches, not ordinary security lookup. Example searchSpec "<SEARCH_SPEC>".';
917
+ var BQR_DESCRIPTION = 'Bloomberg Quote Request / dealer quotes. Use for fixed-income dealer quote ticks, preferably with an ISIN plus dealer source such as "/isin/<ISIN>@<QUOTE_SOURCE> <MARKET_SECTOR>"; requires explicit ISO start/end datetimes with time components. Set eventTypes explicitly, for example ["<EVENT_TYPE>"].';
918
+ var BFLDS_DESCRIPTION = 'Bloomberg field metadata and field search. Use first when a field mnemonic is uncertain. Provide exactly one of fields or searchSpec. Example: fields ["<FIELD>"] or searchSpec "<FIELD_SEARCH_TEXT>".';
919
+ var BEQS_DESCRIPTION = "Bloomberg equity screening by named BEQS screen. Use when the user names an existing Bloomberg screen and wants its bounded result set. Prefer this over hand-written BQL for saved Bloomberg screens.";
920
+ var YAS_DESCRIPTION = "Bloomberg fixed-income YAS recipe fields for one or more bonds. Use for yield, duration, spread, benchmark, or price analytics; provide explicit fields and optional settlement/yield/price inputs. Pass securities as supplied: '<TICKER> <MARKET_SECTOR>' or identifier syntax such as '/isin/<ISIN> <MARKET_SECTOR>'.";
921
+ var PREFERREDS_DESCRIPTION = "Preferred stock discovery for one issuer. Takes the issuer's common equity ticker such as '<TICKER> US Equity', never a preferred ('Pfd') ticker and never a guessed one. If the user supplied an ISIN or CUSIP, resolve it with xbbg_resolve_isins first.";
922
+ var CORPORATE_BONDS_DESCRIPTION = "Corporate bond universe query for one issuer/company equity ticker, with optional currency, active-only filter, and result fields. Prefer this over generic BQL for company debt discovery. If the user supplied an ISIN or CUSIP, resolve it with xbbg_resolve_isins first; never guess the ticker.";
923
+ var INDEX_MEMBERS_DESCRIPTION = "Index constituent recipe for one Bloomberg index ticker such as '<INDEX_TICKER> Index'. Use for bounded member lists and optional historical/as-of constituent membership; never guess index tickers.";
924
+ var RESOLVE_ISINS_DESCRIPTION = "Resolve raw ISIN strings to Bloomberg securities through the core ISIN recipe. Do not add /isin/ prefixes in this tool; pass the exact ISIN strings supplied by the user.";
925
+ var ISSUER_ISINS_DESCRIPTION = "Issuer/bond ISIN workflow for supplied bond ISIN strings. Use for issuer-level ISIN discovery starting from known bond ISINs.";
926
+ var ETF_HOLDINGS_DESCRIPTION = "ETF holdings recipe for one ETF ticker such as '<ETF_TICKER> <MARKET_SECTOR>'. Use when the user asks for ETF constituents or holdings and wants the bounded holdings result. Resolve a supplied ISIN/CUSIP with xbbg_resolve_isins first; never guess the ticker.";
927
+ var STREAM_SNAPSHOT_DESCRIPTION = "Bounded live market-data snapshot from //blp/mktdata. Collects at most maxUpdates updates until timeout/done, then always unsubscribes; use for finite observations, not open subscriptions.";
928
+ var MKTBAR_SNAPSHOT_DESCRIPTION = "Bounded live market-bar snapshot from //blp/mktbar for one ticker. Collects at most maxUpdates updates until timeout/done, then always unsubscribes.";
929
+ var DEPTH_SNAPSHOT_DESCRIPTION = "Bounded live market-depth snapshot from //blp/mktdepthdata for one ticker. Collects at most maxUpdates updates until timeout/done, then always unsubscribes.";
930
+ var EXT_TICKER_DESCRIPTION = "Ticker hygiene helpers: parse_ticker (generic futures-style tickers ending in Index, Curncy, Comdty, or Corp, or <ROOT><N> <EXCHANGE> Equity; other market sectors are rejected), normalize_tickers, filter_equity_tickers, is_specific_contract, and validate_generic_ticker.";
931
+ var EXT_FUTURES_DESCRIPTION = "Futures helpers for contract construction and selection: build_futures_ticker, generate_candidates, contract_index, filter_candidates_by_cycle, filter_valid_contracts, and get_futures_months.";
932
+ var EXT_CDX_DESCRIPTION = "CDX helpers for parsing, series rolling/resolution, and predefined info/pricing/risk BDP field bundles.";
933
+ var EXT_CURRENCY_DESCRIPTION = "Currency planning helpers: build FX pairs, test same-currency requests, and find currencies needing conversion.";
934
+ var EXT_BQL_BUILDER_DESCRIPTION = "BQL builders for preferred stocks, corporate bonds, and ETF holdings. Prefer to construct those bounded BQL shapes before xbbg_bql.";
935
+ var EXT_CHART_SPEC_DESCRIPTION = "Renderer-neutral chart spec helper for frontend generative UI. Converts bounded Bloomberg rows from bdh, bdib, holdings, depth, or already-shaped row data into a Vega-Lite JSON spec; it does not fetch Bloomberg data or render images.";
936
+ var EXT_MARKET_SESSION_DESCRIPTION = "Market session and timezone helpers for deriving sessions, UTC windows, market rules, exchange metadata, turnover defaults, and BQR datetime defaults.";
937
+ var EXT_YAS_OVERRIDES_DESCRIPTION = "Build flat Bloomberg YAS override maps for fixed-income analytics fields.";
938
+ var EXT_CONSTANTS_DESCRIPTION = "Static Bloomberg helper constants for date parsing/formatting, futures months, dividend types, and ETF/dividend columns.";
939
+ var EXT_COLUMNS_DESCRIPTION = "Column rename helpers for dividend, ETF, and earnings-shaped Bloomberg responses.";
940
+ var EXT_CALCULATE_DESCRIPTION = "Small numeric helper operations for Bloomberg workflows, including level percentage calculations.";
516
941
  var stringPairSchema = z__namespace.object({
517
942
  key: z__namespace.string().trim().min(1).describe("String pair key."),
518
943
  value: z__namespace.string().trim().min(1).describe("String pair value.")
@@ -534,7 +959,7 @@ function optionalString(options, description) {
534
959
  function tickerSchema(options) {
535
960
  const ticker = nonEmptyString(
536
961
  options,
537
- "One Bloomberg ticker for parse/contract validation operations."
962
+ "One generic futures-style Bloomberg ticker: <ROOT><N> ending in Index, Curncy, Comdty, or Corp, or <ROOT><N> <EXCHANGE> Equity. parse_ticker rejects other market sectors (Pfd, Govt, Muni, Mtge, M-Mkt) and non-futures securities."
538
963
  );
539
964
  const tickers = stringArray(
540
965
  options,
@@ -550,94 +975,151 @@ function tickerSchema(options) {
550
975
  ]);
551
976
  }
552
977
  function futuresSchema(options) {
553
- return z__namespace.object({
554
- asset: optionalString(options, "Bloomberg asset class suffix supplied by the user."),
555
- candidates: z__namespace.array(futuresCandidateSchema).min(1).max(options.maxFields).optional().describe("Candidate futures contracts."),
556
- contracts: z__namespace.array(stringPairSchema).min(1).max(options.maxFields).optional().describe("Contract pairs for validity filtering."),
557
- count: z__namespace.number().int().positive().optional().describe("Maximum number of futures candidates to generate."),
558
- cycle: optionalString(options, "Futures cycle code to filter candidates by."),
559
- day: z__namespace.number().int().min(1).max(31).optional().describe("Day number for contract filtering."),
560
- freq: optionalString(options, "Futures frequency/cycle hint."),
561
- genTicker: optionalString(options, "Generic Bloomberg futures ticker."),
562
- month: z__namespace.number().int().min(1).max(12).optional().describe("Month number, 1-12."),
563
- monthCode: optionalString(options, "Bloomberg futures month code, for example H."),
564
- operation: z__namespace.enum([
565
- "build_futures_ticker",
566
- "generate_candidates",
567
- "contract_index",
568
- "filter_candidates_by_cycle",
569
- "filter_valid_contracts",
570
- "get_futures_months"
571
- ]).describe("Futures helper operation to run."),
572
- prefix: optionalString(options, "Futures ticker root prefix."),
573
- year: z__namespace.union([z__namespace.string().trim().min(1), z__namespace.number().int()]).optional().describe("Contract year.")
574
- });
978
+ const genTicker = nonEmptyString(
979
+ options,
980
+ "Generic Bloomberg futures ticker, for example ES1 Index."
981
+ );
982
+ const year = z__namespace.number().int().describe("Contract year, for example 2024.");
983
+ const month = z__namespace.number().int().min(1).max(12).describe("Month number, 1-12.");
984
+ const day = z__namespace.number().int().min(1).max(31).describe("Day number, 1-31.");
985
+ return z__namespace.discriminatedUnion("operation", [
986
+ z__namespace.object({
987
+ asset: nonEmptyString(
988
+ options,
989
+ "Bloomberg asset class suffix, for example Index or Comdty."
990
+ ),
991
+ monthCode: nonEmptyString(options, "Bloomberg futures month code, for example H."),
992
+ operation: z__namespace.literal("build_futures_ticker"),
993
+ prefix: nonEmptyString(options, "Futures ticker root prefix, for example ES."),
994
+ year: z__namespace.union([z__namespace.string().trim().min(1), z__namespace.number().int().transform(String)]).describe("Contract year, full or abbreviated, as a string or integer.")
995
+ }).strict(),
996
+ z__namespace.object({
997
+ count: z__namespace.number().int().positive().optional().describe("Maximum number of futures candidates to generate."),
998
+ day,
999
+ freq: optionalString(options, "Futures frequency/cycle hint."),
1000
+ genTicker,
1001
+ month,
1002
+ operation: z__namespace.literal("generate_candidates"),
1003
+ year
1004
+ }).strict(),
1005
+ z__namespace.object({ genTicker, operation: z__namespace.literal("contract_index") }).strict(),
1006
+ z__namespace.object({
1007
+ candidates: z__namespace.array(futuresCandidateSchema).min(1).max(options.maxFields).describe("Candidate futures contracts."),
1008
+ cycle: nonEmptyString(options, "Futures cycle code to filter candidates by."),
1009
+ operation: z__namespace.literal("filter_candidates_by_cycle")
1010
+ }).strict(),
1011
+ z__namespace.object({
1012
+ contracts: z__namespace.array(stringPairSchema).min(1).max(options.maxFields).describe("Contract pairs for validity filtering."),
1013
+ day,
1014
+ month,
1015
+ operation: z__namespace.literal("filter_valid_contracts"),
1016
+ year
1017
+ }).strict(),
1018
+ z__namespace.object({ operation: z__namespace.literal("get_futures_months") }).strict()
1019
+ ]);
575
1020
  }
576
1021
  function cdxSchema(options) {
577
- return z__namespace.object({
578
- genTicker: optionalString(options, "Generic CDX ticker."),
579
- operation: z__namespace.enum([
580
- "parse_cdx_ticker",
581
- "previous_cdx_series",
582
- "cdx_gen_to_specific",
583
- "cdx_info",
584
- "cdx_pricing",
585
- "cdx_risk"
586
- ]).describe("CDX helper operation to run."),
587
- recoveryRate: z__namespace.number().optional().describe("Optional recovery rate override for pricing/risk lookups."),
588
- series: z__namespace.number().int().positive().optional().describe("Specific CDX series number."),
589
- ticker: optionalString(options, "CDX ticker.")
590
- });
1022
+ const ticker = nonEmptyString(options, "CDX ticker, generic or specific.");
1023
+ const recoveryRate = z__namespace.number().min(0).max(1).optional().describe("Decimal recovery rate override, e.g. 0.4 for 40%; sent as the CDS_RR override.");
1024
+ return z__namespace.discriminatedUnion("operation", [
1025
+ z__namespace.object({ operation: z__namespace.literal("parse_cdx_ticker"), ticker }).strict(),
1026
+ z__namespace.object({ operation: z__namespace.literal("previous_cdx_series"), ticker }).strict(),
1027
+ z__namespace.object({ operation: z__namespace.literal("cdx_info"), ticker }).strict(),
1028
+ z__namespace.object({ operation: z__namespace.literal("cdx_pricing"), recoveryRate, ticker }).strict(),
1029
+ z__namespace.object({ operation: z__namespace.literal("cdx_risk"), recoveryRate, ticker }).strict(),
1030
+ z__namespace.object({
1031
+ genTicker: nonEmptyString(
1032
+ options,
1033
+ "Generic CDX ticker, for example CDX IG CDSI GEN 5Y Corp."
1034
+ ),
1035
+ operation: z__namespace.literal("cdx_gen_to_specific"),
1036
+ series: z__namespace.number().int().positive().describe("Specific CDX series number.")
1037
+ }).strict()
1038
+ ]);
591
1039
  }
592
1040
  function currencySchema(options) {
593
- return z__namespace.object({
594
- ccy1: optionalString(options, "First ISO currency code."),
595
- ccy2: optionalString(options, "Second ISO currency code."),
596
- currencies: stringArray(options, "ISO currency codes.").optional(),
597
- fromCcy: optionalString(options, "Source ISO currency code."),
598
- operation: z__namespace.enum(["build_fx_pair", "same_currency", "currencies_needing_conversion"]).describe("Currency helper operation to run."),
599
- target: optionalString(options, "Target ISO currency code."),
600
- toCcy: optionalString(options, "Destination ISO currency code.")
601
- });
1041
+ return z__namespace.discriminatedUnion("operation", [
1042
+ z__namespace.object({
1043
+ fromCcy: nonEmptyString(options, "Source ISO currency code."),
1044
+ operation: z__namespace.literal("build_fx_pair"),
1045
+ toCcy: nonEmptyString(options, "Destination ISO currency code.")
1046
+ }).strict(),
1047
+ z__namespace.object({
1048
+ ccy1: nonEmptyString(options, "First ISO currency code."),
1049
+ ccy2: nonEmptyString(options, "Second ISO currency code."),
1050
+ operation: z__namespace.literal("same_currency")
1051
+ }).strict(),
1052
+ z__namespace.object({
1053
+ currencies: stringArray(options, "ISO currency codes to check."),
1054
+ operation: z__namespace.literal("currencies_needing_conversion"),
1055
+ target: nonEmptyString(options, "Target ISO currency code.")
1056
+ }).strict()
1057
+ ]);
602
1058
  }
603
1059
  function bqlBuilderSchema(options) {
604
- return z__namespace.object({
605
- activeOnly: z__namespace.boolean().optional().describe("Restrict corporate bond query to active bonds."),
606
- ccy: optionalString(options, "Currency filter for corporate bond query."),
607
- equityTicker: optionalString(options, "Equity ticker for preferreds query."),
608
- etfTicker: optionalString(options, "ETF ticker for holdings query."),
609
- extraFields: stringArray(options, "Extra BQL fields to include.").optional(),
610
- operation: z__namespace.enum(["build_preferreds_query", "build_corporate_bonds_query", "build_etf_holdings_query"]).describe("BQL builder operation to run."),
611
- ticker: optionalString(options, "Ticker for corporate bond query.")
612
- });
1060
+ const extraFields = stringArray(options, "Extra BQL fields to include.").optional();
1061
+ return z__namespace.discriminatedUnion("operation", [
1062
+ z__namespace.object({
1063
+ equityTicker: nonEmptyString(options, "Equity ticker for preferreds query."),
1064
+ extraFields,
1065
+ operation: z__namespace.literal("build_preferreds_query")
1066
+ }).strict(),
1067
+ z__namespace.object({
1068
+ activeOnly: z__namespace.boolean().optional().describe("Restrict corporate bond query to active bonds."),
1069
+ ccy: optionalString(options, "Currency filter for corporate bond query."),
1070
+ extraFields,
1071
+ operation: z__namespace.literal("build_corporate_bonds_query"),
1072
+ ticker: nonEmptyString(options, "Ticker for corporate bond query.")
1073
+ }).strict(),
1074
+ z__namespace.object({
1075
+ etfTicker: nonEmptyString(options, "ETF ticker for holdings query."),
1076
+ extraFields,
1077
+ operation: z__namespace.literal("build_etf_holdings_query")
1078
+ }).strict()
1079
+ ]);
613
1080
  }
614
1081
  function marketSessionSchema(options) {
615
- return z__namespace.object({
616
- countryIso: optionalString(options, "ISO country code for timezone inference."),
617
- date: optionalString(options, "Date for UTC session conversion, YYYY-MM-DD or YYYYMMDD."),
618
- dayEnd: optionalString(options, "Exchange day end time, for example 16:00."),
619
- dayStart: optionalString(options, "Exchange day start time, for example 09:30."),
620
- endDate: optionalString(options, "Optional end date."),
621
- endDatetime: optionalString(options, "Optional end datetime."),
622
- endTime: optionalString(options, "Session end time, for example 16:00."),
623
- exchCode: optionalString(options, "Bloomberg exchange code."),
624
- exchangeTz: optionalString(options, "IANA exchange timezone."),
625
- mic: optionalString(options, "Market Identifier Code."),
626
- operation: z__namespace.enum([
627
- "derive_sessions",
628
- "get_market_rule",
629
- "infer_timezone",
630
- "session_times_to_utc",
631
- "default_turnover_dates",
632
- "default_bqr_datetimes",
633
- "get_exchange_override",
634
- "list_exchange_overrides"
635
- ]).describe("Market session helper operation to run."),
636
- startDate: optionalString(options, "Optional start date."),
637
- startDatetime: optionalString(options, "Optional start datetime."),
638
- startTime: optionalString(options, "Session start time, for example 09:30."),
639
- ticker: optionalString(options, "Ticker for exchange override lookup.")
640
- });
1082
+ const mic = optionalString(options, "Market Identifier Code, for example XNYS.");
1083
+ const exchCode = optionalString(options, "Bloomberg exchange code.");
1084
+ return z__namespace.discriminatedUnion("operation", [
1085
+ z__namespace.object({
1086
+ dayEnd: nonEmptyString(options, "Exchange day end time, for example 16:00."),
1087
+ dayStart: nonEmptyString(options, "Exchange day start time, for example 09:30."),
1088
+ exchCode,
1089
+ mic,
1090
+ operation: z__namespace.literal("derive_sessions")
1091
+ }).strict(),
1092
+ z__namespace.object({ exchCode, mic, operation: z__namespace.literal("get_market_rule") }).strict(),
1093
+ z__namespace.object({
1094
+ countryIso: nonEmptyString(options, "ISO country code for timezone inference."),
1095
+ operation: z__namespace.literal("infer_timezone")
1096
+ }).strict(),
1097
+ z__namespace.object({
1098
+ date: nonEmptyString(options, "Date for UTC session conversion, YYYY-MM-DD or YYYYMMDD."),
1099
+ endTime: nonEmptyString(options, "Session end time, for example 16:00."),
1100
+ exchangeTz: nonEmptyString(
1101
+ options,
1102
+ "IANA exchange timezone, for example America/New_York."
1103
+ ),
1104
+ operation: z__namespace.literal("session_times_to_utc"),
1105
+ startTime: nonEmptyString(options, "Session start time, for example 09:30.")
1106
+ }).strict(),
1107
+ z__namespace.object({
1108
+ endDate: optionalString(options, "Optional end date."),
1109
+ operation: z__namespace.literal("default_turnover_dates"),
1110
+ startDate: optionalString(options, "Optional start date.")
1111
+ }).strict(),
1112
+ z__namespace.object({
1113
+ endDatetime: optionalString(options, "Optional end datetime."),
1114
+ operation: z__namespace.literal("default_bqr_datetimes"),
1115
+ startDatetime: optionalString(options, "Optional start datetime.")
1116
+ }).strict(),
1117
+ z__namespace.object({
1118
+ operation: z__namespace.literal("get_exchange_override"),
1119
+ ticker: nonEmptyString(options, "Ticker for exchange override lookup.")
1120
+ }).strict(),
1121
+ z__namespace.object({ operation: z__namespace.literal("list_exchange_overrides") }).strict()
1122
+ ]);
641
1123
  }
642
1124
  function yasOverridesSchema(options) {
643
1125
  return z__namespace.object({
@@ -647,94 +1129,96 @@ function yasOverridesSchema(options) {
647
1129
  spread: z__namespace.number().optional().describe("YAS spread override."),
648
1130
  yieldType: z__namespace.number().int().optional().describe("YAS yield type override."),
649
1131
  yieldVal: z__namespace.number().optional().describe("YAS yield value override.")
650
- });
1132
+ }).strict();
651
1133
  }
652
1134
  function constantsSchema(options) {
653
- return z__namespace.object({
654
- code: optionalString(options, "Month code."),
655
- dateStr: optionalString(options, "Date string to parse."),
656
- day: z__namespace.number().int().min(1).max(31).optional().describe("Day number."),
657
- dvdType: optionalString(options, "Dividend type code or label."),
658
- fmt: optionalString(options, "Date output format."),
659
- month: z__namespace.number().int().min(1).max(12).optional().describe("Month number."),
660
- monthName: optionalString(options, "Month name."),
661
- operation: z__namespace.enum([
662
- "parse_date",
663
- "fmt_date",
664
- "get_month_code",
665
- "get_month_name",
666
- "get_futures_months",
667
- "get_dvd_type",
668
- "get_dvd_types",
669
- "get_dvd_cols",
670
- "get_etf_cols"
671
- ]).describe("Constants helper operation to run."),
672
- year: z__namespace.number().int().min(1).optional().describe("Year number.")
673
- });
1135
+ return z__namespace.discriminatedUnion("operation", [
1136
+ z__namespace.object({
1137
+ dateStr: nonEmptyString(options, "Date string to parse."),
1138
+ operation: z__namespace.literal("parse_date")
1139
+ }).strict(),
1140
+ z__namespace.object({
1141
+ day: z__namespace.number().int().min(1).max(31).describe("Day number, 1-31."),
1142
+ fmt: optionalString(options, "Date output format."),
1143
+ month: z__namespace.number().int().min(1).max(12).describe("Month number, 1-12."),
1144
+ operation: z__namespace.literal("fmt_date"),
1145
+ year: z__namespace.number().int().min(1).describe("Year number.")
1146
+ }).strict(),
1147
+ z__namespace.object({
1148
+ monthName: nonEmptyString(options, "Month name, for example March."),
1149
+ operation: z__namespace.literal("get_month_code")
1150
+ }).strict(),
1151
+ z__namespace.object({
1152
+ code: nonEmptyString(options, "Month code, for example H."),
1153
+ operation: z__namespace.literal("get_month_name")
1154
+ }).strict(),
1155
+ z__namespace.object({
1156
+ dvdType: nonEmptyString(options, "Dividend type code or label."),
1157
+ operation: z__namespace.literal("get_dvd_type")
1158
+ }).strict(),
1159
+ z__namespace.object({ operation: z__namespace.literal("get_futures_months") }).strict(),
1160
+ z__namespace.object({ operation: z__namespace.literal("get_dvd_types") }).strict(),
1161
+ z__namespace.object({ operation: z__namespace.literal("get_dvd_cols") }).strict(),
1162
+ z__namespace.object({ operation: z__namespace.literal("get_etf_cols") }).strict()
1163
+ ]);
674
1164
  }
675
1165
  function columnsSchema(options) {
676
- return z__namespace.object({
677
- columns: stringArray(options, "Column names to rename.").optional(),
678
- dataColumns: stringArray(options, "Earnings data column names.").optional(),
679
- headerRow: z__namespace.array(stringPairSchema).min(1).max(options.maxFields).optional().describe("Earnings header row key/value pairs."),
680
- operation: z__namespace.enum(["rename_dividend_columns", "rename_etf_columns", "build_earning_header_rename"]).describe("Column helper operation to run.")
681
- });
1166
+ const columns = stringArray(options, "Column names to rename.");
1167
+ return z__namespace.discriminatedUnion("operation", [
1168
+ z__namespace.object({ columns, operation: z__namespace.literal("rename_dividend_columns") }).strict(),
1169
+ z__namespace.object({ columns, operation: z__namespace.literal("rename_etf_columns") }).strict(),
1170
+ z__namespace.object({
1171
+ dataColumns: stringArray(options, "Earnings data column names."),
1172
+ headerRow: z__namespace.array(stringPairSchema).min(1).max(options.maxFields).describe("Earnings header row key/value pairs."),
1173
+ operation: z__namespace.literal("build_earning_header_rename")
1174
+ }).strict()
1175
+ ]);
682
1176
  }
683
1177
  function calculateSchema(options) {
684
1178
  return z__namespace.object({
685
1179
  levels: z__namespace.array(z__namespace.number().nullable()).min(1).max(options.maxFields).describe("Reference level values."),
686
1180
  operation: z__namespace.literal("calculate_level_percentages").describe("Numeric helper operation to run."),
687
1181
  values: z__namespace.array(z__namespace.number().nullable()).min(1).max(options.maxFields).describe("Observed values.")
1182
+ }).strict().superRefine((input, ctx) => {
1183
+ if (input.values.length !== input.levels.length) {
1184
+ ctx.addIssue({
1185
+ code: z__namespace.ZodIssueCode.custom,
1186
+ message: "values and levels must have the same length"
1187
+ });
1188
+ }
688
1189
  });
689
1190
  }
1191
+ function chartSpecSchema(options) {
1192
+ const chartScalar = z__namespace.union([
1193
+ z__namespace.string().trim().max(options.maxStringChars),
1194
+ z__namespace.number(),
1195
+ z__namespace.boolean(),
1196
+ z__namespace.null()
1197
+ ]);
1198
+ const fieldName = nonEmptyString(options, "Input row field name.");
1199
+ return z__namespace.object({
1200
+ chart: z__namespace.enum(["line", "area", "bar", "scatter", "candlestick", "depth"]).optional().describe("Chart shape to generate. Defaults from source."),
1201
+ closeField: fieldName.optional().describe("Candlestick close-value field."),
1202
+ highField: fieldName.optional().describe("Candlestick high-value field."),
1203
+ labelField: fieldName.optional().describe("Categorical label field for bar charts."),
1204
+ lowField: fieldName.optional().describe("Candlestick low-value field."),
1205
+ maxPoints: z__namespace.number().int().positive().max(options.maxRows).optional().describe("Maximum rows to include in the frontend spec; defaults to all provided rows."),
1206
+ openField: fieldName.optional().describe("Candlestick open-value field."),
1207
+ priceField: fieldName.optional().describe("Market-depth price field."),
1208
+ renderer: z__namespace.literal("vega-lite").optional().describe("Visualization spec renderer. Currently only vega-lite is generated."),
1209
+ rows: z__namespace.array(z__namespace.record(chartScalar)).min(1).max(options.maxRows).describe("Chart data rows copied from a bounded Bloomberg tool result."),
1210
+ seriesField: fieldName.optional().describe("Optional series/color field."),
1211
+ sideField: fieldName.optional().describe("Market-depth bid/ask side field."),
1212
+ sizeField: fieldName.optional().describe("Market-depth size field."),
1213
+ source: z__namespace.enum(["bdh", "bdib", "holdings", "depth", "rows"]).describe("Bloomberg result shape that produced rows."),
1214
+ title: nonEmptyString(options, "Chart title.").optional(),
1215
+ valueField: fieldName.optional().describe("Primary numeric value field."),
1216
+ xField: fieldName.optional().describe("X-axis field."),
1217
+ yFields: stringArray(options, "Numeric value fields to plot.").optional()
1218
+ }).strict();
1219
+ }
690
1220
 
691
1221
  // src/ext-tools.ts
692
- function asRecord(value) {
693
- return value;
694
- }
695
- function requireString(toolName, input, field) {
696
- const value = input[field];
697
- if (typeof value !== "string" || value.trim().length === 0) {
698
- throw new TypeError(`${toolName}: ${field} is required and must be a non-empty string`);
699
- }
700
- return value.trim();
701
- }
702
- function requireNumber(toolName, input, field) {
703
- const value = input[field];
704
- if (typeof value !== "number" || !Number.isFinite(value)) {
705
- throw new TypeError(`${toolName}: ${field} is required and must be a finite number`);
706
- }
707
- return value;
708
- }
709
- function requireInteger(toolName, input, field) {
710
- const value = requireNumber(toolName, input, field);
711
- if (!Number.isInteger(value)) {
712
- throw new TypeError(`${toolName}: ${field} must be an integer`);
713
- }
714
- return value;
715
- }
716
- function requireYearString(toolName, input, field) {
717
- const value = input[field];
718
- if (typeof value === "number" && Number.isInteger(value)) {
719
- return String(value);
720
- }
721
- if (typeof value === "string" && value.trim().length > 0) {
722
- return value.trim();
723
- }
724
- throw new TypeError(`${toolName}: ${field} is required and must be a year string or integer`);
725
- }
726
- function requireStringArray(toolName, input, field) {
727
- const value = input[field];
728
- if (!Array.isArray(value) || value.length === 0) {
729
- throw new TypeError(`${toolName}: ${field} is required and must be a non-empty string array`);
730
- }
731
- return value.map((entry) => {
732
- if (typeof entry !== "string" || entry.trim().length === 0) {
733
- throw new TypeError(`${toolName}: ${field} entries must be non-empty strings`);
734
- }
735
- return entry.trim();
736
- });
737
- }
738
1222
  function resultString(resolver, name, value) {
739
1223
  return createToolResult(name, value, resolver.options.maxRows, resolver.options.maxStringChars);
740
1224
  }
@@ -747,6 +1231,7 @@ var EXT_TOOL_DEFINITIONS = Object.freeze([
747
1231
  { create: extCdxWithResolver, name: "xbbg_ext_cdx" },
748
1232
  { create: extCurrencyWithResolver, name: "xbbg_ext_currency" },
749
1233
  { create: extBqlBuilderWithResolver, name: "xbbg_ext_bql_builder" },
1234
+ { create: extChartSpecWithResolver, name: "xbbg_ext_chart_spec" },
750
1235
  { create: extMarketSessionWithResolver, name: "xbbg_ext_market_session" },
751
1236
  { create: extYasOverridesWithResolver, name: "xbbg_ext_yas_overrides" },
752
1237
  { create: extConstantsWithResolver, name: "xbbg_ext_constants" },
@@ -756,43 +1241,42 @@ var EXT_TOOL_DEFINITIONS = Object.freeze([
756
1241
  var BLOOMBERG_EXT_TOOL_NAMES = Object.freeze(
757
1242
  EXT_TOOL_DEFINITIONS.map((definition) => definition.name)
758
1243
  );
1244
+ function extChartSpecWithResolver(resolver) {
1245
+ const name = "xbbg_ext_chart_spec";
1246
+ return createBloombergStructuredTool(
1247
+ async (input) => {
1248
+ try {
1249
+ return await Promise.resolve(resultString(resolver, name, createChartSpec(input)));
1250
+ } catch (error) {
1251
+ throwWithToolContext(name, error);
1252
+ }
1253
+ },
1254
+ {
1255
+ responseFormat: "content_and_artifact",
1256
+ description: EXT_CHART_SPEC_DESCRIPTION,
1257
+ name,
1258
+ schema: chartSpecSchema(resolver.options)
1259
+ }
1260
+ );
1261
+ }
759
1262
  function extTickerWithResolver(resolver) {
760
1263
  const name = "xbbg_ext_ticker";
761
1264
  return createBloombergStructuredTool(
762
1265
  async (input) => {
763
1266
  try {
764
1267
  const core = await resolver.getCore();
765
- const args = asRecord(input);
766
1268
  switch (input.operation) {
767
1269
  case "parse_ticker":
768
- return resultString(
769
- resolver,
770
- name,
771
- core.ext.parseTicker(requireString(name, args, "ticker"))
772
- );
1270
+ return resultString(resolver, name, core.ext.parseTicker(input.ticker));
773
1271
  case "normalize_tickers":
774
- return resultString(
775
- resolver,
776
- name,
777
- core.ext.normalizeTickers(requireStringArray(name, args, "tickers"))
778
- );
1272
+ return resultString(resolver, name, core.ext.normalizeTickers(input.tickers));
779
1273
  case "filter_equity_tickers":
780
- return resultString(
781
- resolver,
782
- name,
783
- core.ext.filterEquityTickers(requireStringArray(name, args, "tickers"))
784
- );
1274
+ return resultString(resolver, name, core.ext.filterEquityTickers(input.tickers));
785
1275
  case "is_specific_contract":
786
- return resultString(
787
- resolver,
788
- name,
789
- core.ext.isSpecificContract(requireString(name, args, "ticker"))
790
- );
791
- case "validate_generic_ticker": {
792
- const ticker = requireString(name, args, "ticker");
793
- core.ext.validateGenericTicker(ticker);
794
- return resultString(resolver, name, { ticker, valid: true });
795
- }
1276
+ return resultString(resolver, name, core.ext.isSpecificContract(input.ticker));
1277
+ case "validate_generic_ticker":
1278
+ core.ext.validateGenericTicker(input.ticker);
1279
+ return resultString(resolver, name, { ticker: input.ticker, valid: true });
796
1280
  }
797
1281
  } catch (error) {
798
1282
  throwWithToolContext(name, error);
@@ -812,63 +1296,39 @@ function extFuturesWithResolver(resolver) {
812
1296
  async (input) => {
813
1297
  try {
814
1298
  const core = await resolver.getCore();
815
- const args = asRecord(input);
816
1299
  switch (input.operation) {
817
1300
  case "build_futures_ticker":
818
1301
  return resultString(
819
1302
  resolver,
820
1303
  name,
821
- core.ext.buildFuturesTicker(
822
- requireString(name, args, "prefix"),
823
- requireString(name, args, "monthCode"),
824
- requireYearString(name, args, "year"),
825
- requireString(name, args, "asset")
826
- )
1304
+ core.ext.buildFuturesTicker(input.prefix, input.monthCode, input.year, input.asset)
827
1305
  );
828
1306
  case "generate_candidates":
829
1307
  return resultString(
830
1308
  resolver,
831
1309
  name,
832
1310
  core.ext.generateFuturesCandidates(
833
- requireString(name, args, "genTicker"),
834
- requireInteger(name, args, "year"),
835
- requireInteger(name, args, "month"),
836
- requireInteger(name, args, "day"),
1311
+ input.genTicker,
1312
+ input.year,
1313
+ input.month,
1314
+ input.day,
837
1315
  input.freq,
838
1316
  input.count
839
1317
  )
840
1318
  );
841
1319
  case "contract_index":
842
- return resultString(
843
- resolver,
844
- name,
845
- core.ext.contractIndex(requireString(name, args, "genTicker"))
846
- );
1320
+ return resultString(resolver, name, core.ext.contractIndex(input.genTicker));
847
1321
  case "filter_candidates_by_cycle":
848
- if (input.candidates === void 0) {
849
- throw new TypeError(`${name}: candidates is required`);
850
- }
851
1322
  return resultString(
852
1323
  resolver,
853
1324
  name,
854
- core.ext.filterCandidatesByCycle(
855
- input.candidates,
856
- requireString(name, args, "cycle")
857
- )
1325
+ core.ext.filterCandidatesByCycle(input.candidates, input.cycle)
858
1326
  );
859
1327
  case "filter_valid_contracts":
860
- if (input.contracts === void 0) {
861
- throw new TypeError(`${name}: contracts is required`);
862
- }
863
1328
  return resultString(
864
1329
  resolver,
865
1330
  name,
866
- core.ext.filterValidContracts(
867
- input.contracts,
868
- requireInteger(name, args, "year"),
869
- requireInteger(name, args, "month"),
870
- requireInteger(name, args, "day")
871
- )
1331
+ core.ext.filterValidContracts(input.contracts, input.year, input.month, input.day)
872
1332
  );
873
1333
  case "get_futures_months":
874
1334
  return resultString(resolver, name, core.ext.getFuturesMonths());
@@ -888,41 +1348,31 @@ function extFuturesWithResolver(resolver) {
888
1348
  function extCdxWithResolver(resolver) {
889
1349
  const name = "xbbg_ext_cdx";
890
1350
  return createBloombergStructuredTool(
891
- async (input) => {
1351
+ async (input, config) => {
892
1352
  try {
893
- const args = asRecord(input);
1353
+ config?.signal?.throwIfAborted();
894
1354
  if (input.operation === "cdx_info" || input.operation === "cdx_pricing" || input.operation === "cdx_risk") {
895
1355
  const engine = await resolver.getEngine();
896
- const ticker = requireString(name, args, "ticker");
897
1356
  const fields = input.operation === "cdx_info" ? CDX_INFO_FIELDS : input.operation === "cdx_pricing" ? CDX_PRICING_FIELDS : CDX_RISK_FIELDS;
898
- const result = await engine.bdp([ticker], fields, {
1357
+ const result = await engine.bdp([input.ticker], fields, {
899
1358
  backend: "json",
900
- overrides: recoveryOverrides(input.recoveryRate)
1359
+ overrides: recoveryOverrides(
1360
+ input.operation === "cdx_pricing" || input.operation === "cdx_risk" ? input.recoveryRate : void 0
1361
+ )
901
1362
  });
902
1363
  return resultString(resolver, name, result);
903
1364
  }
904
1365
  const core = await resolver.getCore();
905
1366
  switch (input.operation) {
906
1367
  case "parse_cdx_ticker":
907
- return resultString(
908
- resolver,
909
- name,
910
- core.ext.parseCdxTicker(requireString(name, args, "ticker"))
911
- );
1368
+ return resultString(resolver, name, core.ext.parseCdxTicker(input.ticker));
912
1369
  case "previous_cdx_series":
913
- return resultString(
914
- resolver,
915
- name,
916
- core.ext.previousCdxSeries(requireString(name, args, "ticker"))
917
- );
1370
+ return resultString(resolver, name, core.ext.previousCdxSeries(input.ticker));
918
1371
  case "cdx_gen_to_specific":
919
1372
  return resultString(
920
1373
  resolver,
921
1374
  name,
922
- core.ext.cdxGenToSpecific(
923
- requireString(name, args, "genTicker"),
924
- requireInteger(name, args, "series")
925
- )
1375
+ core.ext.cdxGenToSpecific(input.genTicker, input.series)
926
1376
  );
927
1377
  }
928
1378
  } catch (error) {
@@ -943,34 +1393,16 @@ function extCurrencyWithResolver(resolver) {
943
1393
  async (input) => {
944
1394
  try {
945
1395
  const core = await resolver.getCore();
946
- const args = asRecord(input);
947
1396
  switch (input.operation) {
948
1397
  case "build_fx_pair":
949
- return resultString(
950
- resolver,
951
- name,
952
- core.ext.buildFxPair(
953
- requireString(name, args, "fromCcy"),
954
- requireString(name, args, "toCcy")
955
- )
956
- );
1398
+ return resultString(resolver, name, core.ext.buildFxPair(input.fromCcy, input.toCcy));
957
1399
  case "same_currency":
958
- return resultString(
959
- resolver,
960
- name,
961
- core.ext.sameCurrency(
962
- requireString(name, args, "ccy1"),
963
- requireString(name, args, "ccy2")
964
- )
965
- );
1400
+ return resultString(resolver, name, core.ext.sameCurrency(input.ccy1, input.ccy2));
966
1401
  case "currencies_needing_conversion":
967
1402
  return resultString(
968
1403
  resolver,
969
1404
  name,
970
- core.ext.currenciesNeedingConversion(
971
- requireStringArray(name, args, "currencies"),
972
- requireString(name, args, "target")
973
- )
1405
+ core.ext.currenciesNeedingConversion(input.currencies, input.target)
974
1406
  );
975
1407
  }
976
1408
  } catch (error) {
@@ -991,23 +1423,19 @@ function extBqlBuilderWithResolver(resolver) {
991
1423
  async (input) => {
992
1424
  try {
993
1425
  const core = await resolver.getCore();
994
- const args = asRecord(input);
995
1426
  switch (input.operation) {
996
1427
  case "build_preferreds_query":
997
1428
  return resultString(
998
1429
  resolver,
999
1430
  name,
1000
- core.ext.buildPreferredsQuery(
1001
- requireString(name, args, "equityTicker"),
1002
- input.extraFields
1003
- )
1431
+ core.ext.buildPreferredsQuery(input.equityTicker, input.extraFields)
1004
1432
  );
1005
1433
  case "build_corporate_bonds_query":
1006
1434
  return resultString(
1007
1435
  resolver,
1008
1436
  name,
1009
1437
  core.ext.buildCorporateBondsQuery(
1010
- requireString(name, args, "ticker"),
1438
+ input.ticker,
1011
1439
  input.ccy,
1012
1440
  input.extraFields,
1013
1441
  input.activeOnly
@@ -1017,10 +1445,7 @@ function extBqlBuilderWithResolver(resolver) {
1017
1445
  return resultString(
1018
1446
  resolver,
1019
1447
  name,
1020
- core.ext.buildEtfHoldingsQuery(
1021
- requireString(name, args, "etfTicker"),
1022
- input.extraFields
1023
- )
1448
+ core.ext.buildEtfHoldingsQuery(input.etfTicker, input.extraFields)
1024
1449
  );
1025
1450
  }
1026
1451
  } catch (error) {
@@ -1041,36 +1466,26 @@ function extMarketSessionWithResolver(resolver) {
1041
1466
  async (input) => {
1042
1467
  try {
1043
1468
  const core = await resolver.getCore();
1044
- const args = asRecord(input);
1045
1469
  switch (input.operation) {
1046
1470
  case "derive_sessions":
1047
1471
  return resultString(
1048
1472
  resolver,
1049
1473
  name,
1050
- core.ext.deriveSessions(
1051
- requireString(name, args, "dayStart"),
1052
- requireString(name, args, "dayEnd"),
1053
- input.mic,
1054
- input.exchCode
1055
- )
1474
+ core.ext.deriveSessions(input.dayStart, input.dayEnd, input.mic, input.exchCode)
1056
1475
  );
1057
1476
  case "get_market_rule":
1058
1477
  return resultString(resolver, name, core.ext.getMarketRule(input.mic, input.exchCode));
1059
1478
  case "infer_timezone":
1060
- return resultString(
1061
- resolver,
1062
- name,
1063
- core.ext.inferTimezone(requireString(name, args, "countryIso"))
1064
- );
1479
+ return resultString(resolver, name, core.ext.inferTimezone(input.countryIso));
1065
1480
  case "session_times_to_utc":
1066
1481
  return resultString(
1067
1482
  resolver,
1068
1483
  name,
1069
1484
  core.ext.sessionTimesToUtc(
1070
- requireString(name, args, "startTime"),
1071
- requireString(name, args, "endTime"),
1072
- requireString(name, args, "exchangeTz"),
1073
- requireString(name, args, "date")
1485
+ input.startTime,
1486
+ input.endTime,
1487
+ input.exchangeTz,
1488
+ input.date
1074
1489
  )
1075
1490
  );
1076
1491
  case "default_turnover_dates":
@@ -1086,11 +1501,7 @@ function extMarketSessionWithResolver(resolver) {
1086
1501
  core.ext.defaultBqrDatetimes(input.startDatetime, input.endDatetime)
1087
1502
  );
1088
1503
  case "get_exchange_override":
1089
- return resultString(
1090
- resolver,
1091
- name,
1092
- core.ext.getExchangeOverride(requireString(name, args, "ticker"))
1093
- );
1504
+ return resultString(resolver, name, core.ext.getExchangeOverride(input.ticker));
1094
1505
  case "list_exchange_overrides":
1095
1506
  return resultString(resolver, name, core.ext.listExchangeOverrides());
1096
1507
  }
@@ -1142,45 +1553,23 @@ function extConstantsWithResolver(resolver) {
1142
1553
  async (input) => {
1143
1554
  try {
1144
1555
  const core = await resolver.getCore();
1145
- const args = asRecord(input);
1146
1556
  switch (input.operation) {
1147
1557
  case "parse_date":
1148
- return resultString(
1149
- resolver,
1150
- name,
1151
- core.ext.parseDate(requireString(name, args, "dateStr"))
1152
- );
1558
+ return resultString(resolver, name, core.ext.parseDate(input.dateStr));
1153
1559
  case "fmt_date":
1154
1560
  return resultString(
1155
1561
  resolver,
1156
1562
  name,
1157
- core.ext.fmtDate(
1158
- requireInteger(name, args, "year"),
1159
- requireInteger(name, args, "month"),
1160
- requireInteger(name, args, "day"),
1161
- input.fmt
1162
- )
1563
+ core.ext.fmtDate(input.year, input.month, input.day, input.fmt)
1163
1564
  );
1164
1565
  case "get_month_code":
1165
- return resultString(
1166
- resolver,
1167
- name,
1168
- core.ext.getMonthCode(requireString(name, args, "monthName"))
1169
- );
1566
+ return resultString(resolver, name, core.ext.getMonthCode(input.monthName));
1170
1567
  case "get_month_name":
1171
- return resultString(
1172
- resolver,
1173
- name,
1174
- core.ext.getMonthName(requireString(name, args, "code"))
1175
- );
1568
+ return resultString(resolver, name, core.ext.getMonthName(input.code));
1176
1569
  case "get_futures_months":
1177
1570
  return resultString(resolver, name, core.ext.getFuturesMonths());
1178
1571
  case "get_dvd_type":
1179
- return resultString(
1180
- resolver,
1181
- name,
1182
- core.ext.getDvdType(requireString(name, args, "dvdType"))
1183
- );
1572
+ return resultString(resolver, name, core.ext.getDvdType(input.dvdType));
1184
1573
  case "get_dvd_types":
1185
1574
  return resultString(resolver, name, core.ext.getDvdTypes());
1186
1575
  case "get_dvd_cols":
@@ -1206,31 +1595,16 @@ function extColumnsWithResolver(resolver) {
1206
1595
  async (input) => {
1207
1596
  try {
1208
1597
  const core = await resolver.getCore();
1209
- const args = asRecord(input);
1210
1598
  switch (input.operation) {
1211
1599
  case "rename_dividend_columns":
1212
- return resultString(
1213
- resolver,
1214
- name,
1215
- core.ext.renameDividendColumns(requireStringArray(name, args, "columns"))
1216
- );
1600
+ return resultString(resolver, name, core.ext.renameDividendColumns(input.columns));
1217
1601
  case "rename_etf_columns":
1218
- return resultString(
1219
- resolver,
1220
- name,
1221
- core.ext.renameEtfColumns(requireStringArray(name, args, "columns"))
1222
- );
1602
+ return resultString(resolver, name, core.ext.renameEtfColumns(input.columns));
1223
1603
  case "build_earning_header_rename":
1224
- if (input.headerRow === void 0) {
1225
- throw new TypeError(`${name}: headerRow is required`);
1226
- }
1227
1604
  return resultString(
1228
1605
  resolver,
1229
1606
  name,
1230
- core.ext.buildEarningHeaderRename(
1231
- input.headerRow,
1232
- requireStringArray(name, args, "dataColumns")
1233
- )
1607
+ core.ext.buildEarningHeaderRename(input.headerRow, input.dataColumns)
1234
1608
  );
1235
1609
  }
1236
1610
  } catch (error) {
@@ -1250,9 +1624,6 @@ function extCalculateWithResolver(resolver) {
1250
1624
  return createBloombergStructuredTool(
1251
1625
  async (input) => {
1252
1626
  try {
1253
- if (input.values.length !== input.levels.length) {
1254
- throw new TypeError(`${name}: values and levels must have the same length`);
1255
- }
1256
1627
  const core = await resolver.getCore();
1257
1628
  return resultString(
1258
1629
  resolver,
@@ -1301,6 +1672,9 @@ function createExtColumnsTool(options = {}) {
1301
1672
  function createExtCalculateTool(options = {}) {
1302
1673
  return extCalculateWithResolver(createCoreResolver(options));
1303
1674
  }
1675
+ function createExtChartSpecTool(options = {}) {
1676
+ return extChartSpecWithResolver(createCoreResolver(options));
1677
+ }
1304
1678
  function createBloombergExtToolsForResolver(resolver) {
1305
1679
  return EXT_TOOL_DEFINITIONS.filter(
1306
1680
  (definition) => !isToolDisabled(resolver.options, definition.name)
@@ -1321,6 +1695,9 @@ var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/u;
1321
1695
  var BBG_DATE_RE = /^\d{8}$/u;
1322
1696
  var AMBIGUOUS_DATE_RE = /^\d{1,2}[-/]\d{1,2}[-/]\d{2,4}([T \D]|$)/u;
1323
1697
  var ISO_DATE_TIME_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?$/u;
1698
+ var MIN_NUMERIC_BBG_DATE = 19000101;
1699
+ var MAX_NUMERIC_BBG_DATE = 29991231;
1700
+ var MIN_EPOCH_MS = 1e11;
1324
1701
  var primitiveSchema = z__namespace.union([
1325
1702
  z__namespace.string().transform((value) => value.trim()),
1326
1703
  z__namespace.number(),
@@ -1335,18 +1712,30 @@ function dateFromParts(year, month, day) {
1335
1712
  return formatted;
1336
1713
  }
1337
1714
  function dateToBbg(value) {
1338
- const date2 = value instanceof Date ? value : new Date(value);
1339
- if (Number.isNaN(date2.getTime())) {
1715
+ const date = value instanceof Date ? value : new Date(value);
1716
+ if (Number.isNaN(date.getTime())) {
1340
1717
  throw new TypeError("Invalid date value; expected YYYY-MM-DD, YYYYMMDD, Date, or epoch ms");
1341
1718
  }
1342
- const year = String(date2.getUTCFullYear()).padStart(4, "0");
1343
- const month = String(date2.getUTCMonth() + 1).padStart(2, "0");
1344
- const day = String(date2.getUTCDate()).padStart(2, "0");
1719
+ const year = String(date.getUTCFullYear()).padStart(4, "0");
1720
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
1721
+ const day = String(date.getUTCDate()).padStart(2, "0");
1345
1722
  return `${year}${month}${day}`;
1346
1723
  }
1724
+ function numericDateToBbg(value, unit) {
1725
+ if (Number.isFinite(value) && value >= MIN_EPOCH_MS) {
1726
+ return new Date(value);
1727
+ }
1728
+ throw new TypeError(
1729
+ `Ambiguous numeric ${unit} ${String(value)}; use "YYYY-MM-DD" text or epoch milliseconds`
1730
+ );
1731
+ }
1347
1732
  function normalizeDate(value) {
1348
- if (value instanceof Date || typeof value === "number") {
1349
- return dateToBbg(value);
1733
+ if (typeof value === "number") {
1734
+ if (Number.isInteger(value) && value >= MIN_NUMERIC_BBG_DATE && value <= MAX_NUMERIC_BBG_DATE) {
1735
+ const text2 = String(value);
1736
+ return dateFromParts(text2.slice(0, 4), text2.slice(4, 6), text2.slice(6, 8));
1737
+ }
1738
+ return dateToBbg(numericDateToBbg(value, "date"));
1350
1739
  }
1351
1740
  const text = value.trim();
1352
1741
  if (text.length === 0) {
@@ -1364,12 +1753,13 @@ function normalizeDate(value) {
1364
1753
  throw new TypeError(`Invalid date ${JSON.stringify(text)}; use YYYY-MM-DD or YYYYMMDD`);
1365
1754
  }
1366
1755
  function normalizeDateTime(value) {
1367
- if (value instanceof Date || typeof value === "number") {
1368
- const date2 = value instanceof Date ? value : new Date(value);
1369
- if (Number.isNaN(date2.getTime())) {
1370
- throw new TypeError("Invalid datetime value; expected ISO 8601 datetime, Date, or epoch ms");
1756
+ if (typeof value === "number") {
1757
+ if (Number.isInteger(value) && value >= MIN_NUMERIC_BBG_DATE && value <= MAX_NUMERIC_BBG_DATE) {
1758
+ throw new TypeError(
1759
+ `Invalid numeric datetime ${String(value)}; include an explicit time component such as "2024-01-02T09:30:00"`
1760
+ );
1371
1761
  }
1372
- return date2.toISOString();
1762
+ return numericDateToBbg(value, "datetime").toISOString();
1373
1763
  }
1374
1764
  const text = value.trim();
1375
1765
  if (text.length === 0) {
@@ -1399,8 +1789,15 @@ function nonEmptyString2(tool2, field, maxChars, example) {
1399
1789
  function stringArray2(tool2, field, maxItems, maxChars, example) {
1400
1790
  return z__namespace.array(nonEmptyString2(tool2, field, maxChars, example)).min(1, `${tool2}: ${field} must contain at least one non-empty string. Example: ${example}`).max(maxItems, `${tool2}: ${field} can contain at most ${maxItems} values`);
1401
1791
  }
1792
+ function normalizationIssue(context, tool2, field, error) {
1793
+ context.addIssue({
1794
+ code: "custom",
1795
+ message: `${tool2}: ${field}: ${error instanceof Error ? error.message : String(error)}`
1796
+ });
1797
+ return z__namespace.NEVER;
1798
+ }
1402
1799
  function primitiveMap(tool2, field) {
1403
- return z__namespace.record(z__namespace.string().min(1), primitiveSchema).optional().transform((value) => {
1800
+ return z__namespace.record(z__namespace.string().min(1), primitiveSchema).optional().transform((value, context) => {
1404
1801
  if (value === void 0) {
1405
1802
  return void 0;
1406
1803
  }
@@ -1408,10 +1805,15 @@ function primitiveMap(tool2, field) {
1408
1805
  for (const [key, entry] of Object.entries(value)) {
1409
1806
  const normalizedKey = key.trim();
1410
1807
  if (normalizedKey.length === 0) {
1411
- throw new TypeError(`${tool2}: ${field} contains an empty key`);
1808
+ return normalizationIssue(context, tool2, field, new TypeError("contains an empty key"));
1412
1809
  }
1413
1810
  if (typeof entry === "string" && entry.length === 0) {
1414
- throw new TypeError(`${tool2}: ${field}.${normalizedKey} must not be an empty string`);
1811
+ return normalizationIssue(
1812
+ context,
1813
+ tool2,
1814
+ field,
1815
+ new TypeError(`${normalizedKey} must not be an empty string`)
1816
+ );
1415
1817
  }
1416
1818
  normalized[normalizedKey] = entry;
1417
1819
  }
@@ -1419,12 +1821,18 @@ function primitiveMap(tool2, field) {
1419
1821
  });
1420
1822
  }
1421
1823
  function dateField(tool2, field) {
1422
- return z__namespace.union([z__namespace.string(), z__namespace.date(), z__namespace.number()]).transform((value) => normalizeDate(value)).describe(
1824
+ return z__namespace.union([z__namespace.string(), z__namespace.number()]).transform((value, context) => {
1825
+ try {
1826
+ return normalizeDate(value);
1827
+ } catch (error) {
1828
+ return normalizationIssue(context, tool2, field, error);
1829
+ }
1830
+ }).describe(
1423
1831
  `${field} date. Use YYYY-MM-DD or Bloomberg-native YYYYMMDD, never ambiguous MM/DD/YYYY.`
1424
1832
  );
1425
1833
  }
1426
1834
  function dateTimeField(tool2, field) {
1427
- return z__namespace.union([z__namespace.string(), z__namespace.date(), z__namespace.number()]).superRefine((value, context) => {
1835
+ return z__namespace.union([z__namespace.string(), z__namespace.number()]).superRefine((value, context) => {
1428
1836
  if (typeof value !== "string") {
1429
1837
  return;
1430
1838
  }
@@ -1435,7 +1843,13 @@ function dateTimeField(tool2, field) {
1435
1843
  message: `${tool2}: ${field} datetime requires an explicit time component; use ISO 8601 such as YYYY-MM-DDT09:30:00`
1436
1844
  });
1437
1845
  }
1438
- }).transform((value) => normalizeDateTime(value)).describe(`${field} datetime. Use ISO 8601 with an explicit time component.`);
1846
+ }).transform((value, context) => {
1847
+ try {
1848
+ return normalizeDateTime(value);
1849
+ } catch (error) {
1850
+ return normalizationIssue(context, tool2, field, error);
1851
+ }
1852
+ }).describe(`${field} datetime. Use ISO 8601 with an explicit time component.`);
1439
1853
  }
1440
1854
  function referenceFormat(tool2) {
1441
1855
  return z__namespace.enum(REFERENCE_FORMATS, {
@@ -1478,7 +1892,7 @@ function createBdpSchema(options) {
1478
1892
  options.maxStringChars,
1479
1893
  '["<TICKER> <MARKET_SECTOR>"]'
1480
1894
  ).describe(
1481
- "Fully qualified Bloomberg securities supplied by the user; use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs. Do not invent tickers."
1895
+ "Securities exactly as the user supplied them: '<TICKER> <MARKET_SECTOR>' for Bloomberg tickers, '/isin/<ISIN>' for raw ISINs, '/cusip/<CUSIP>' for raw CUSIPs. Never invent, guess, or convert identifiers into tickers."
1482
1896
  ),
1483
1897
  validateFields: z__namespace.boolean().optional().describe("Override field validation for this request.")
1484
1898
  });
@@ -1510,7 +1924,7 @@ function createBdhSchema(options) {
1510
1924
  options.maxStringChars,
1511
1925
  '["<TICKER> <MARKET_SECTOR>"]'
1512
1926
  ).describe(
1513
- "Fully qualified Bloomberg securities supplied by the user; use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs."
1927
+ "Securities exactly as the user supplied them: '<TICKER> <MARKET_SECTOR>' for Bloomberg tickers, '/isin/<ISIN>' for raw ISINs, '/cusip/<CUSIP>' for raw CUSIPs. Never invent, guess, or convert identifiers into tickers."
1514
1928
  ),
1515
1929
  start: dateField(tool2, "start").describe("Required start date. Use YYYY-MM-DD or YYYYMMDD."),
1516
1930
  validateFields: z__namespace.boolean().optional().describe("Override field validation for this request.")
@@ -1530,7 +1944,6 @@ function createBdsSchema(options) {
1530
1944
  field: nonEmptyString2(tool2, "field", options.maxStringChars, "<BULK_FIELD>").describe(
1531
1945
  "Exactly one Bloomberg bulk/table field supplied by the user."
1532
1946
  ),
1533
- format: referenceFormat(tool2).describe("JSON output shape. Usually omit."),
1534
1947
  kwargs: primitiveMap(tool2, "kwargs").describe(
1535
1948
  "Advanced Bloomberg request kwargs as flat string/number/boolean values only."
1536
1949
  ),
@@ -1544,7 +1957,7 @@ function createBdsSchema(options) {
1544
1957
  options.maxStringChars,
1545
1958
  '["<INDEX_TICKER> <MARKET_SECTOR>"]'
1546
1959
  ).describe(
1547
- "Fully qualified Bloomberg securities supplied by the user; use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs."
1960
+ "Securities exactly as the user supplied them: '<TICKER> <MARKET_SECTOR>' for Bloomberg tickers, '/isin/<ISIN>' for raw ISINs, '/cusip/<CUSIP>' for raw CUSIPs. Never invent, guess, or convert identifiers into tickers."
1548
1961
  ),
1549
1962
  validateFields: z__namespace.boolean().optional().describe("Override field validation for this request.")
1550
1963
  });
@@ -1571,7 +1984,7 @@ function createBdibSchema(options) {
1571
1984
  options.maxStringChars,
1572
1985
  "<TICKER> <MARKET_SECTOR>"
1573
1986
  ).describe(
1574
- "One fully qualified Bloomberg security supplied by the user; use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs."
1987
+ "One security exactly as the user supplied it: '<TICKER> <MARKET_SECTOR>', '/isin/<ISIN>', or '/cusip/<CUSIP>'. Never invent, guess, or convert identifiers into tickers."
1575
1988
  )
1576
1989
  });
1577
1990
  }
@@ -1610,14 +2023,13 @@ function createBdtickSchema(options) {
1610
2023
  options.maxStringChars,
1611
2024
  "<TICKER> <MARKET_SECTOR>"
1612
2025
  ).describe(
1613
- "One fully qualified Bloomberg security supplied by the user; use /isin/<ISIN> for ISINs and /cusip/<CUSIP> for CUSIPs."
2026
+ "One security exactly as the user supplied it: '<TICKER> <MARKET_SECTOR>', '/isin/<ISIN>', or '/cusip/<CUSIP>'. Never invent, guess, or convert identifiers into tickers."
1614
2027
  )
1615
2028
  });
1616
2029
  }
1617
2030
  function createBqlSchema(options) {
1618
2031
  const tool2 = "xbbg_bql";
1619
2032
  return z__namespace.object({
1620
- format: referenceFormat(tool2).describe("JSON output shape. Usually omit."),
1621
2033
  kwargs: primitiveMap(tool2, "kwargs").describe(
1622
2034
  "Advanced Bloomberg request kwargs as flat string/number/boolean values only."
1623
2035
  ),
@@ -1656,7 +2068,6 @@ function createBqrSchema(options) {
1656
2068
  function createBsrchSchema(options) {
1657
2069
  const tool2 = "xbbg_bsrch";
1658
2070
  return z__namespace.object({
1659
- format: referenceFormat(tool2).describe("JSON output shape. Usually omit."),
1660
2071
  kwargs: primitiveMap(tool2, "kwargs").describe(
1661
2072
  "Search-grid kwargs as flat string/number/boolean values only."
1662
2073
  ),
@@ -1679,7 +2090,6 @@ function createBfldsSchema(options) {
1679
2090
  fields: stringArray2(tool2, "fields", options.maxFields, options.maxStringChars, '["<FIELD>"]').optional().describe(
1680
2091
  "Specific field mnemonics to inspect. Provide either fields or searchSpec, not both."
1681
2092
  ),
1682
- format: referenceFormat(tool2).describe("JSON output shape. Usually omit."),
1683
2093
  kwargs: primitiveMap(tool2, "kwargs").describe(
1684
2094
  "Advanced Bloomberg request kwargs as flat string/number/boolean values only."
1685
2095
  ),
@@ -1707,7 +2117,6 @@ function createBeqsSchema(options) {
1707
2117
  const tool2 = "xbbg_beqs";
1708
2118
  return z__namespace.object({
1709
2119
  asof: dateField(tool2, "asof").optional().describe("Optional as-of date for the screen."),
1710
- format: referenceFormat(tool2).describe("JSON output shape. Usually omit."),
1711
2120
  group: nonEmptyString2(tool2, "group", options.maxStringChars, "<BEQS_GROUP>").optional().describe("Bloomberg BEQS group when required by the screen."),
1712
2121
  kwargs: primitiveMap(tool2, "kwargs").describe(
1713
2122
  "Advanced BEQS request kwargs as flat string/number/boolean values only."
@@ -1741,7 +2150,9 @@ function createYasSchema(options) {
1741
2150
  options.maxSecurities,
1742
2151
  options.maxStringChars,
1743
2152
  '["/isin/<ISIN> <MARKET_SECTOR>"]'
1744
- ).describe("Fully qualified fixed-income Bloomberg securities supplied by the user."),
2153
+ ).describe(
2154
+ "Fixed-income securities exactly as the user supplied them: '<TICKER> <MARKET_SECTOR>' or identifier syntax such as '/isin/<ISIN> <MARKET_SECTOR>'. Never invent or guess tickers."
2155
+ ),
1745
2156
  yieldType: z__namespace.number().int().optional().describe("Optional YAS yield type."),
1746
2157
  yieldVal: z__namespace.number().optional().describe("Optional YAS yield value input.")
1747
2158
  });
@@ -1754,8 +2165,10 @@ function createPreferredsSchema(options) {
1754
2165
  "equityTicker",
1755
2166
  options.maxStringChars,
1756
2167
  "<ISSUER_TICKER> <MARKET_SECTOR>"
1757
- ).describe("One fully qualified issuer equity ticker supplied by the user."),
1758
- fields: stringArray2(tool2, "fields", options.maxFields, options.maxStringChars, '["<FIELD>"]').optional().describe("Optional fields to include in the preferreds recipe result.")
2168
+ ).describe(
2169
+ "The issuer's common equity ticker as '<TICKER> <MARKET_SECTOR>', never a preferred ('Pfd') ticker and never a guessed one. Resolve a supplied ISIN/CUSIP with xbbg_resolve_isins first."
2170
+ ),
2171
+ fields: z__namespace.array(nonEmptyString2(tool2, "fields", options.maxStringChars, '["<FIELD>"]')).max(options.maxFields, `${tool2}: fields can contain at most ${options.maxFields} values`).transform((fields) => fields.length === 0 ? void 0 : fields).optional().describe("Optional fields to include in the preferreds recipe result.")
1759
2172
  });
1760
2173
  }
1761
2174
  function createCorporateBondsSchema(options) {
@@ -1769,7 +2182,9 @@ function createCorporateBondsSchema(options) {
1769
2182
  "ticker",
1770
2183
  options.maxStringChars,
1771
2184
  "<ISSUER_TICKER> <MARKET_SECTOR>"
1772
- ).describe("One fully qualified issuer/company ticker supplied by the user.")
2185
+ ).describe(
2186
+ "The issuer/company equity ticker as '<TICKER> <MARKET_SECTOR>' supplied by the user. Resolve a supplied ISIN/CUSIP with xbbg_resolve_isins first; never guess tickers."
2187
+ )
1773
2188
  });
1774
2189
  }
1775
2190
  function createIndexMembersSchema(options) {
@@ -1782,7 +2197,9 @@ function createIndexMembersSchema(options) {
1782
2197
  "index",
1783
2198
  options.maxStringChars,
1784
2199
  "<INDEX_TICKER> <MARKET_SECTOR>"
1785
- ).describe("One fully qualified Bloomberg index ticker supplied by the user.")
2200
+ ).describe(
2201
+ "One Bloomberg index ticker as '<INDEX_TICKER> <MARKET_SECTOR>' supplied by the user; never guess index tickers."
2202
+ )
1786
2203
  });
1787
2204
  }
1788
2205
  function createResolveIsinsSchema(options) {
@@ -1817,7 +2234,9 @@ function createEtfHoldingsSchema(options) {
1817
2234
  "etfTicker",
1818
2235
  options.maxStringChars,
1819
2236
  "<ETF_TICKER> <MARKET_SECTOR>"
1820
- ).describe("One fully qualified Bloomberg ETF ticker supplied by the user."),
2237
+ ).describe(
2238
+ "One Bloomberg ETF ticker as '<ETF_TICKER> <MARKET_SECTOR>' supplied by the user. Resolve a supplied ISIN/CUSIP with xbbg_resolve_isins first; never guess tickers."
2239
+ ),
1821
2240
  fields: stringArray2(tool2, "fields", options.maxFields, options.maxStringChars, '["<FIELD>"]').optional().describe("Optional fields to include in the ETF holdings recipe result.")
1822
2241
  });
1823
2242
  }
@@ -1826,7 +2245,7 @@ function snapshotControlFields(tool2, options) {
1826
2245
  allFields: z__namespace.boolean().optional().describe("Request all Bloomberg fields when supported."),
1827
2246
  conflate: z__namespace.boolean().optional().describe("Enable Bloomberg conflated streaming when supported."),
1828
2247
  drain: z__namespace.boolean().optional().describe(
1829
- "Pass drain=true to unsubscribe. Defaults to false; collected output remains bounded."
2248
+ "Flush buffered backlog while closing the subscription. The subscription always closes; collected output stays bounded either way. Defaults to false."
1830
2249
  ),
1831
2250
  flushThreshold: z__namespace.number().int().positive().optional().describe("Optional stream flush threshold."),
1832
2251
  maxUpdates: z__namespace.number().int(`${tool2}: maxUpdates must be a positive integer.`).positive(`${tool2}: maxUpdates must be greater than zero.`).max(
@@ -1861,7 +2280,9 @@ function createStreamSnapshotSchema(options) {
1861
2280
  options.maxSecurities,
1862
2281
  options.maxStringChars,
1863
2282
  '["<TICKER> <MARKET_SECTOR>"]'
1864
- ).describe("Fully qualified Bloomberg securities supplied by the user to observe."),
2283
+ ).describe(
2284
+ "Securities to observe, exactly as the user supplied them: '<TICKER> <MARKET_SECTOR>', '/isin/<ISIN>', or '/cusip/<CUSIP>'. Never invent or guess tickers."
2285
+ ),
1865
2286
  ...snapshotControlFields(tool2, options)
1866
2287
  });
1867
2288
  }
@@ -1874,7 +2295,9 @@ function createMktbarSnapshotSchema(options) {
1874
2295
  "ticker",
1875
2296
  options.maxStringChars,
1876
2297
  "<TICKER> <MARKET_SECTOR>"
1877
- ).describe("One fully qualified Bloomberg security supplied by the user to observe."),
2298
+ ).describe(
2299
+ "One security to observe, exactly as the user supplied it: '<TICKER> <MARKET_SECTOR>', '/isin/<ISIN>', or '/cusip/<CUSIP>'. Never invent or guess tickers."
2300
+ ),
1878
2301
  ...snapshotControlFields(tool2, options)
1879
2302
  });
1880
2303
  }
@@ -1887,7 +2310,9 @@ function createDepthSnapshotSchema(options) {
1887
2310
  "ticker",
1888
2311
  options.maxStringChars,
1889
2312
  "<TICKER> <MARKET_SECTOR>"
1890
- ).describe("One fully qualified Bloomberg security supplied by the user to observe."),
2313
+ ).describe(
2314
+ "One security to observe, exactly as the user supplied it: '<TICKER> <MARKET_SECTOR>', '/isin/<ISIN>', or '/cusip/<CUSIP>'. Never invent or guess tickers."
2315
+ ),
1891
2316
  ...snapshotControlFields(tool2, options)
1892
2317
  });
1893
2318
  }
@@ -1897,17 +2322,24 @@ function resultString2(resolver, name, value) {
1897
2322
  return createToolResult(name, value, resolver.options.maxRows, resolver.options.maxStringChars);
1898
2323
  }
1899
2324
  var STREAM_TIMEOUT = /* @__PURE__ */ Symbol("stream_timeout");
2325
+ var STREAM_ABORTED = /* @__PURE__ */ Symbol("stream_aborted");
2326
+ function abortError(signal) {
2327
+ const reason = signal?.reason;
2328
+ return reason instanceof Error ? reason : new Error("Tool call aborted");
2329
+ }
1900
2330
  function streamOptions(input) {
1901
2331
  return {
1902
2332
  allFields: input.allFields,
1903
2333
  conflate: input.conflate,
1904
- fields: input.fields,
1905
2334
  flushThreshold: input.flushThreshold,
1906
2335
  options: input.options,
1907
2336
  overflowPolicy: input.overflowPolicy,
1908
2337
  streamCapacity: input.streamCapacity
1909
2338
  };
1910
2339
  }
2340
+ function singleTickerStreamOptions(input) {
2341
+ return { ...streamOptions(input), fields: input.fields };
2342
+ }
1911
2343
  function isRecord(value) {
1912
2344
  return typeof value === "object" && value !== null;
1913
2345
  }
@@ -1963,42 +2395,55 @@ function normalizeStreamUpdate(value) {
1963
2395
  const rows = rowsFromArrowTable(value);
1964
2396
  return rows === void 0 ? jsonCompatible(value) : rows.map(jsonCompatible);
1965
2397
  }
1966
- async function nextWithinTimeout(iterator, deadlineMs) {
2398
+ async function nextWithinTimeout(iterator, deadlineMs, signal) {
2399
+ if (signal?.aborted === true) {
2400
+ return STREAM_ABORTED;
2401
+ }
1967
2402
  const remainingMs = deadlineMs - Date.now();
1968
2403
  if (remainingMs <= 0) {
1969
2404
  return STREAM_TIMEOUT;
1970
2405
  }
1971
2406
  const nextPromise = iterator.next();
1972
2407
  let timer;
1973
- const timeoutPromise = new Promise((resolve) => {
1974
- timer = setTimeout(() => resolve(STREAM_TIMEOUT), remainingMs);
1975
- });
1976
- const result = await Promise.race([nextPromise, timeoutPromise]);
2408
+ let onAbort;
2409
+ const racers = [
2410
+ nextPromise,
2411
+ new Promise((resolve) => {
2412
+ timer = setTimeout(() => resolve(STREAM_TIMEOUT), remainingMs);
2413
+ })
2414
+ ];
2415
+ if (signal !== void 0) {
2416
+ racers.push(
2417
+ new Promise((resolve) => {
2418
+ onAbort = () => resolve(STREAM_ABORTED);
2419
+ signal.addEventListener("abort", onAbort, { once: true });
2420
+ })
2421
+ );
2422
+ }
2423
+ const result = await Promise.race(racers);
1977
2424
  if (timer !== void 0) {
1978
2425
  clearTimeout(timer);
1979
2426
  }
1980
- if (result === STREAM_TIMEOUT) {
2427
+ if (signal !== void 0 && onAbort !== void 0) {
2428
+ signal.removeEventListener("abort", onAbort);
2429
+ }
2430
+ if (result === STREAM_TIMEOUT || result === STREAM_ABORTED) {
1981
2431
  void nextPromise.catch(() => void 0);
1982
2432
  }
1983
2433
  return result;
1984
2434
  }
1985
- async function unsubscribeSnapshot(subscription, drain, priorError) {
1986
- try {
1987
- await subscription.unsubscribe(drain);
1988
- } catch (error) {
1989
- if (priorError === void 0) {
1990
- throw error;
1991
- }
1992
- }
1993
- }
1994
- async function collectSnapshot(subscription, input) {
2435
+ async function collectSnapshot(subscription, input, signal) {
1995
2436
  const updates = [];
1996
2437
  const deadlineMs = Date.now() + input.timeoutMs;
1997
2438
  let reason = "max_updates";
2439
+ let failed = false;
1998
2440
  let caught;
1999
2441
  try {
2000
2442
  while (updates.length < input.maxUpdates) {
2001
- const next = await nextWithinTimeout(subscription, deadlineMs);
2443
+ const next = await nextWithinTimeout(subscription, deadlineMs, signal);
2444
+ if (next === STREAM_ABORTED) {
2445
+ throw abortError(signal);
2446
+ }
2002
2447
  if (next === STREAM_TIMEOUT) {
2003
2448
  reason = "timeout";
2004
2449
  break;
@@ -2009,26 +2454,34 @@ async function collectSnapshot(subscription, input) {
2009
2454
  }
2010
2455
  updates.push(normalizeStreamUpdate(next.value));
2011
2456
  }
2012
- return {
2013
- maxUpdates: input.maxUpdates,
2014
- reason,
2015
- timeoutMs: input.timeoutMs,
2016
- updateCount: updates.length,
2017
- updates
2018
- };
2019
2457
  } catch (error) {
2458
+ failed = true;
2020
2459
  caught = error;
2021
- throw error;
2022
- } finally {
2023
- await unsubscribeSnapshot(subscription, input.drain === true, caught);
2024
2460
  }
2461
+ const drain = input.drain === true && signal?.aborted !== true;
2462
+ let unsubscribeError;
2463
+ try {
2464
+ await subscription.unsubscribe(drain);
2465
+ } catch (error) {
2466
+ if (!failed) {
2467
+ unsubscribeError = error instanceof Error ? error.message : String(error);
2468
+ }
2469
+ }
2470
+ if (failed) {
2471
+ throw caught;
2472
+ }
2473
+ return {
2474
+ maxUpdates: input.maxUpdates,
2475
+ reason,
2476
+ timeoutMs: input.timeoutMs,
2477
+ updateCount: updates.length,
2478
+ updates,
2479
+ ...unsubscribeError === void 0 ? {} : { unsubscribeError }
2480
+ };
2025
2481
  }
2026
2482
  function validationSetting(resolver, value) {
2027
2483
  return value ?? resolver.options.validateFields;
2028
2484
  }
2029
- function enabledTool(resolver, name, creator) {
2030
- return isToolDisabled(resolver.options, name) ? [] : [creator(resolver)];
2031
- }
2032
2485
  function bdpWithResolver(resolver) {
2033
2486
  const name = "xbbg_bdp";
2034
2487
  return createBloombergStructuredTool(
@@ -2092,7 +2545,6 @@ function bdsWithResolver(resolver) {
2092
2545
  const engine = await resolver.getEngine();
2093
2546
  const result = await engine.bds(input.securities, [input.field], {
2094
2547
  backend: "json",
2095
- format: input.format,
2096
2548
  kwargs: input.kwargs,
2097
2549
  overrides: input.overrides,
2098
2550
  validateFields: validationSetting(resolver, input.validateFields)
@@ -2182,7 +2634,6 @@ function bqlWithResolver(resolver) {
2182
2634
  const engine = await resolver.getEngine();
2183
2635
  const result = await engine.bql(input.query, {
2184
2636
  backend: "json",
2185
- format: input.format,
2186
2637
  kwargs: input.kwargs
2187
2638
  });
2188
2639
  return resultString2(resolver, name, result);
@@ -2206,7 +2657,6 @@ function bsrchWithResolver(resolver) {
2206
2657
  const engine = await resolver.getEngine();
2207
2658
  const result = await engine.bsrch(input.searchSpec, {
2208
2659
  backend: "json",
2209
- format: input.format,
2210
2660
  kwargs: input.kwargs,
2211
2661
  overrides: input.overrides
2212
2662
  });
@@ -2258,7 +2708,6 @@ function bfldsWithResolver(resolver) {
2258
2708
  const result = await engine.bflds({
2259
2709
  backend: "json",
2260
2710
  fields: input.fields,
2261
- format: input.format,
2262
2711
  kwargs: input.kwargs,
2263
2712
  searchSpec: input.searchSpec
2264
2713
  });
@@ -2284,7 +2733,6 @@ function beqsWithResolver(resolver) {
2284
2733
  const result = await engine.beqs(input.screen, {
2285
2734
  asof: input.asof,
2286
2735
  backend: "json",
2287
- format: input.format,
2288
2736
  group: input.group,
2289
2737
  kwargs: input.kwargs,
2290
2738
  overrides: input.overrides,
@@ -2469,11 +2917,13 @@ function etfHoldingsWithResolver(resolver) {
2469
2917
  function streamSnapshotWithResolver(resolver) {
2470
2918
  const name = "xbbg_stream_snapshot";
2471
2919
  return createBloombergStructuredTool(
2472
- async (input) => {
2920
+ async (input, config) => {
2921
+ const signal = config?.signal;
2473
2922
  try {
2474
2923
  const engine = await resolver.getEngine();
2924
+ signal?.throwIfAborted();
2475
2925
  const subscription = await engine.stream(input.tickers, input.fields, streamOptions(input));
2476
- const result = await collectSnapshot(subscription, input);
2926
+ const result = await collectSnapshot(subscription, input, signal);
2477
2927
  return resultString2(resolver, name, result);
2478
2928
  } catch (error) {
2479
2929
  throwWithToolContext(name, error);
@@ -2490,11 +2940,13 @@ function streamSnapshotWithResolver(resolver) {
2490
2940
  function mktbarSnapshotWithResolver(resolver) {
2491
2941
  const name = "xbbg_mktbar_snapshot";
2492
2942
  return createBloombergStructuredTool(
2493
- async (input) => {
2943
+ async (input, config) => {
2944
+ const signal = config?.signal;
2494
2945
  try {
2495
2946
  const engine = await resolver.getEngine();
2496
- const subscription = await engine.mktbar(input.ticker, streamOptions(input));
2497
- const result = await collectSnapshot(subscription, input);
2947
+ signal?.throwIfAborted();
2948
+ const subscription = await engine.mktbar(input.ticker, singleTickerStreamOptions(input));
2949
+ const result = await collectSnapshot(subscription, input, signal);
2498
2950
  return resultString2(resolver, name, result);
2499
2951
  } catch (error) {
2500
2952
  throwWithToolContext(name, error);
@@ -2511,11 +2963,13 @@ function mktbarSnapshotWithResolver(resolver) {
2511
2963
  function depthSnapshotWithResolver(resolver) {
2512
2964
  const name = "xbbg_depth_snapshot";
2513
2965
  return createBloombergStructuredTool(
2514
- async (input) => {
2966
+ async (input, config) => {
2967
+ const signal = config?.signal;
2515
2968
  try {
2516
2969
  const engine = await resolver.getEngine();
2517
- const subscription = await engine.depth(input.ticker, streamOptions(input));
2518
- const result = await collectSnapshot(subscription, input);
2970
+ signal?.throwIfAborted();
2971
+ const subscription = await engine.depth(input.ticker, singleTickerStreamOptions(input));
2972
+ const result = await collectSnapshot(subscription, input, signal);
2519
2973
  return resultString2(resolver, name, result);
2520
2974
  } catch (error) {
2521
2975
  throwWithToolContext(name, error);
@@ -2589,29 +3043,32 @@ function createMktbarSnapshotTool(options = {}) {
2589
3043
  function createDepthSnapshotTool(options = {}) {
2590
3044
  return depthSnapshotWithResolver(createCoreResolver(options));
2591
3045
  }
3046
+ var CORE_TOOL_DEFINITIONS = Object.freeze([
3047
+ { create: bdpWithResolver, name: "xbbg_bdp" },
3048
+ { create: bdhWithResolver, name: "xbbg_bdh" },
3049
+ { create: bdsWithResolver, name: "xbbg_bds" },
3050
+ { create: bdibWithResolver, name: "xbbg_bdib" },
3051
+ { create: bdtickWithResolver, name: "xbbg_bdtick" },
3052
+ { create: bqlWithResolver, name: "xbbg_bql" },
3053
+ { create: bsrchWithResolver, name: "xbbg_bsrch" },
3054
+ { create: bqrWithResolver, name: "xbbg_bqr" },
3055
+ { create: bfldsWithResolver, name: "xbbg_bflds" },
3056
+ { create: beqsWithResolver, name: "xbbg_beqs" },
3057
+ { create: yasWithResolver, name: "xbbg_yas" },
3058
+ { create: preferredsWithResolver, name: "xbbg_preferreds" },
3059
+ { create: corporateBondsWithResolver, name: "xbbg_corporate_bonds" },
3060
+ { create: indexMembersWithResolver, name: "xbbg_index_members" },
3061
+ { create: resolveIsinsWithResolver, name: "xbbg_resolve_isins" },
3062
+ { create: issuerIsinsWithResolver, name: "xbbg_issuer_isins" },
3063
+ { create: etfHoldingsWithResolver, name: "xbbg_etf_holdings" },
3064
+ { create: streamSnapshotWithResolver, name: "xbbg_stream_snapshot" },
3065
+ { create: mktbarSnapshotWithResolver, name: "xbbg_mktbar_snapshot" },
3066
+ { create: depthSnapshotWithResolver, name: "xbbg_depth_snapshot" }
3067
+ ]);
2592
3068
  function createBloombergToolsForResolver(resolver) {
2593
- return [
2594
- ...enabledTool(resolver, "xbbg_bdp", bdpWithResolver),
2595
- ...enabledTool(resolver, "xbbg_bdh", bdhWithResolver),
2596
- ...enabledTool(resolver, "xbbg_bds", bdsWithResolver),
2597
- ...enabledTool(resolver, "xbbg_bdib", bdibWithResolver),
2598
- ...enabledTool(resolver, "xbbg_bdtick", bdtickWithResolver),
2599
- ...enabledTool(resolver, "xbbg_bql", bqlWithResolver),
2600
- ...enabledTool(resolver, "xbbg_bsrch", bsrchWithResolver),
2601
- ...enabledTool(resolver, "xbbg_bqr", bqrWithResolver),
2602
- ...enabledTool(resolver, "xbbg_bflds", bfldsWithResolver),
2603
- ...enabledTool(resolver, "xbbg_beqs", beqsWithResolver),
2604
- ...enabledTool(resolver, "xbbg_yas", yasWithResolver),
2605
- ...enabledTool(resolver, "xbbg_preferreds", preferredsWithResolver),
2606
- ...enabledTool(resolver, "xbbg_corporate_bonds", corporateBondsWithResolver),
2607
- ...enabledTool(resolver, "xbbg_index_members", indexMembersWithResolver),
2608
- ...enabledTool(resolver, "xbbg_resolve_isins", resolveIsinsWithResolver),
2609
- ...enabledTool(resolver, "xbbg_issuer_isins", issuerIsinsWithResolver),
2610
- ...enabledTool(resolver, "xbbg_etf_holdings", etfHoldingsWithResolver),
2611
- ...enabledTool(resolver, "xbbg_stream_snapshot", streamSnapshotWithResolver),
2612
- ...enabledTool(resolver, "xbbg_mktbar_snapshot", mktbarSnapshotWithResolver),
2613
- ...enabledTool(resolver, "xbbg_depth_snapshot", depthSnapshotWithResolver)
2614
- ];
3069
+ return CORE_TOOL_DEFINITIONS.filter(
3070
+ (definition) => !isToolDisabled(resolver.options, definition.name)
3071
+ ).map((definition) => definition.create(resolver));
2615
3072
  }
2616
3073
  function createBloombergTools(options = {}) {
2617
3074
  return createBloombergToolsForResolver(createCoreResolver(options));
@@ -2629,6 +3086,7 @@ function createAllBloombergTools(options = {}) {
2629
3086
  exports.BLOOMBERG_EXT_TOOL_NAMES = BLOOMBERG_EXT_TOOL_NAMES;
2630
3087
  exports.BLOOMBERG_TOOL_INSTRUCTIONS = BLOOMBERG_TOOL_INSTRUCTIONS;
2631
3088
  exports.BLOOMBERG_TOOL_NAMES = BLOOMBERG_TOOL_NAMES;
3089
+ exports.DEFAULT_ENGINE_REQUEST_TIMEOUT_MS = DEFAULT_ENGINE_REQUEST_TIMEOUT_MS;
2632
3090
  exports.createAllBloombergTools = createAllBloombergTools;
2633
3091
  exports.createBdhTool = createBdhTool;
2634
3092
  exports.createBdibTool = createBdibTool;
@@ -2648,6 +3106,7 @@ exports.createEtfHoldingsTool = createEtfHoldingsTool;
2648
3106
  exports.createExtBqlBuilderTool = createExtBqlBuilderTool;
2649
3107
  exports.createExtCalculateTool = createExtCalculateTool;
2650
3108
  exports.createExtCdxTool = createExtCdxTool;
3109
+ exports.createExtChartSpecTool = createExtChartSpecTool;
2651
3110
  exports.createExtColumnsTool = createExtColumnsTool;
2652
3111
  exports.createExtConstantsTool = createExtConstantsTool;
2653
3112
  exports.createExtCurrencyTool = createExtCurrencyTool;
@@ -2663,5 +3122,6 @@ exports.createResolveIsinsTool = createResolveIsinsTool;
2663
3122
  exports.createStreamSnapshotTool = createStreamSnapshotTool;
2664
3123
  exports.createYasTool = createYasTool;
2665
3124
  exports.getBloombergToolInstructions = getBloombergToolInstructions;
3125
+ exports.toolParameterJsonSchema = toolParameterJsonSchema;
2666
3126
  //# sourceMappingURL=index.js.map
2667
3127
  //# sourceMappingURL=index.js.map