@tradejs/cli 3.1.8 → 3.1.9-beta.214

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.
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/lib/runEnvironment.ts
31
31
  var runEnvironment_exports = {};
32
32
  __export(runEnvironment_exports, {
33
+ loadDeploymentReplayStrategies: () => loadDeploymentReplayStrategies,
33
34
  loadReplayStrategies: () => loadReplayStrategies,
34
35
  prepareRunEnvironment: () => prepareRunEnvironment,
35
36
  resolveBacktestExecutionPreloadInterval: () => resolveBacktestExecutionPreloadInterval
@@ -40,6 +41,54 @@ var import_connectors3 = require("@tradejs/node/connectors");
40
41
  var import_cli3 = require("@tradejs/node/cli");
41
42
  var import_constants = require("@tradejs/core/constants");
42
43
  var import_time2 = require("@tradejs/core/time");
44
+ var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
45
+
46
+ // src/lib/runtimeStrategyBacktest.ts
47
+ var import_chalk = __toESM(require("chalk"));
48
+
49
+ // src/lib/runtimeRedis.ts
50
+ var import_redis = require("@tradejs/infra/redis");
51
+ var import_time = require("@tradejs/core/time");
52
+ var import_runtimeStrategyConfigs = require("@tradejs/infra/runtimeStrategyConfigs");
53
+ var isRuntimeStrategyEnabled = (strategyConfig) => {
54
+ const enabled = strategyConfig.ENABLE;
55
+ return enabled !== false;
56
+ };
57
+
58
+ // src/lib/runtimeStrategyBacktest.ts
59
+ var BACKTEST_CLI_RUNTIME_CONFIG_KEYS = /* @__PURE__ */ new Set([
60
+ "ENV",
61
+ "INTERVAL",
62
+ "MAKE_ORDERS",
63
+ "CLOSE_OPPOSITE_POSITIONS",
64
+ "BACKTEST_PRICE_MODE",
65
+ "BACKTEST_ENTRY_DELAY_BARS"
66
+ ]);
67
+ var toStrategyConfigGrid = (strategyConfig) => Object.fromEntries(
68
+ Object.entries(strategyConfig).filter(([key]) => !BACKTEST_CLI_RUNTIME_CONFIG_KEYS.has(key)).map(([key, value]) => [key, [value]])
69
+ );
70
+ var loadRuntimeStrategyBacktestConfigs = async (userName) => {
71
+ const configs = await (0, import_runtimeStrategyConfigs.loadRuntimeStrategyConfigs)(userName, {
72
+ onInvalidConfig: (key) => {
73
+ console.log(import_chalk.default.yellow(`Skip invalid runtime strategy config: ${key}`));
74
+ }
75
+ });
76
+ return configs.filter(({ configId }) => configId === "config").filter(({ strategyName, strategyConfig }) => {
77
+ const enabled = isRuntimeStrategyEnabled(strategyConfig);
78
+ if (!enabled) {
79
+ console.log(
80
+ import_chalk.default.yellow(
81
+ `Skip inactive runtime strategy config by ENABLE=false: ${strategyName}`
82
+ )
83
+ );
84
+ }
85
+ return enabled;
86
+ }).map(({ strategyName, strategyConfig }) => ({
87
+ strategyName,
88
+ strategyConfig,
89
+ backtestConfig: toStrategyConfigGrid(strategyConfig)
90
+ }));
91
+ };
43
92
 
44
93
  // src/lib/timeWindow.ts
45
94
  var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
@@ -143,53 +192,6 @@ var resolveTimeWindow = ({
143
192
  );
144
193
  };
145
194
 
146
- // src/lib/runtimeStrategyBacktest.ts
147
- var import_chalk = __toESM(require("chalk"));
148
-
149
- // src/lib/runtimeRedis.ts
150
- var import_redis = require("@tradejs/infra/redis");
151
- var import_time = require("@tradejs/core/time");
152
- var import_runtimeStrategyConfigs = require("@tradejs/infra/runtimeStrategyConfigs");
153
- var isRuntimeStrategyEnabled = (strategyConfig) => {
154
- const enabled = strategyConfig.ENABLE;
155
- return enabled !== false;
156
- };
157
-
158
- // src/lib/runtimeStrategyBacktest.ts
159
- var BACKTEST_CLI_RUNTIME_CONFIG_KEYS = /* @__PURE__ */ new Set([
160
- "ENV",
161
- "INTERVAL",
162
- "MAKE_ORDERS",
163
- "CLOSE_OPPOSITE_POSITIONS",
164
- "BACKTEST_PRICE_MODE",
165
- "BACKTEST_ENTRY_DELAY_BARS"
166
- ]);
167
- var toStrategyConfigGrid = (strategyConfig) => Object.fromEntries(
168
- Object.entries(strategyConfig).filter(([key]) => !BACKTEST_CLI_RUNTIME_CONFIG_KEYS.has(key)).map(([key, value]) => [key, [value]])
169
- );
170
- var loadRuntimeStrategyBacktestConfigs = async (userName) => {
171
- const configs = await (0, import_runtimeStrategyConfigs.loadRuntimeStrategyConfigs)(userName, {
172
- onInvalidConfig: (key) => {
173
- console.log(import_chalk.default.yellow(`Skip invalid runtime strategy config: ${key}`));
174
- }
175
- });
176
- return configs.filter(({ configId }) => configId === "config").filter(({ strategyName, strategyConfig }) => {
177
- const enabled = isRuntimeStrategyEnabled(strategyConfig);
178
- if (!enabled) {
179
- console.log(
180
- import_chalk.default.yellow(
181
- `Skip inactive runtime strategy config by ENABLE=false: ${strategyName}`
182
- )
183
- );
184
- }
185
- return enabled;
186
- }).map(({ strategyName, strategyConfig }) => ({
187
- strategyName,
188
- strategyConfig,
189
- backtestConfig: toStrategyConfigGrid(strategyConfig)
190
- }));
191
- };
192
-
193
195
  // src/lib/runFormatting.ts
194
196
  var import_date_fns = require("date-fns");
195
197
  var ListIt = require("list-it");
@@ -490,10 +492,47 @@ var loadReplayStrategies = async (userName) => {
490
492
  `No active runtime strategy configs found by users:${userName}:strategies:*:config`
491
493
  )
492
494
  );
493
- return [];
494
495
  }
495
496
  return runtimeStrategies;
496
497
  };
498
+ var loadDeploymentReplayStrategies = async ({
499
+ userName,
500
+ projectRoot,
501
+ deploymentId
502
+ }) => {
503
+ const [deployment, runtimeStrategies] = await Promise.all([
504
+ (0, import_runtimeStrategies.getRuntimeDeployment)({ userName, projectRoot, deploymentId }),
505
+ (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({ userName, projectRoot, deploymentId })
506
+ ]);
507
+ if (!deployment) {
508
+ throw new Error(`Runtime deployment not found: ${deploymentId}`);
509
+ }
510
+ const strategies = deployment.enabled ? runtimeStrategies.filter((strategy) => strategy.enabled).map(
511
+ ({
512
+ strategyName,
513
+ version,
514
+ strategyPackage,
515
+ strategyPackageVersion,
516
+ runtimePackageVersion,
517
+ sourceStrategyConfig
518
+ }) => ({
519
+ strategyName,
520
+ version,
521
+ strategyPackage,
522
+ strategyPackageVersion,
523
+ runtimePackageVersion,
524
+ strategyConfig: sourceStrategyConfig
525
+ })
526
+ ) : [];
527
+ if (!strategies.length) {
528
+ console.log(
529
+ import_chalk3.default.yellow(
530
+ `No enabled strategies found in runtime deployment ${deploymentId}`
531
+ )
532
+ );
533
+ }
534
+ return { deployment, strategies };
535
+ };
497
536
  var prepareRunEnvironment = async ({
498
537
  connector,
499
538
  userName,
@@ -638,6 +677,7 @@ var prepareRunEnvironment = async ({
638
677
  };
639
678
  // Annotate the CommonJS export names for ESM import in node:
640
679
  0 && (module.exports = {
680
+ loadDeploymentReplayStrategies,
641
681
  loadReplayStrategies,
642
682
  prepareRunEnvironment,
643
683
  resolveBacktestExecutionPreloadInterval
@@ -220,18 +220,39 @@ var loadRuntimeSignalEvaluations = async (userName, {
220
220
  }
221
221
  return sortByTimestamp([...deduped.values()]);
222
222
  };
223
+ var loadRuntimeLineageScopes = async (userName, {
224
+ startTime,
225
+ endTime
226
+ }) => {
227
+ const records = (await Promise.all(
228
+ (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime).map(
229
+ (dayKey) => (0, import_redis2.getHashJsonValues)(
230
+ import_redis2.redisKeys.runtimeLineageScopeBucket(userName, dayKey)
231
+ )
232
+ )
233
+ )).flat().filter(
234
+ (record) => record != null && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.firstTimestamp === "number" && typeof record.lastTimestamp === "number" && record.lineage != null
235
+ );
236
+ return records.sort(
237
+ (left, right) => left.firstTimestamp - right.firstTimestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol)
238
+ );
239
+ };
223
240
  var parseBucketKey = (prefix, key) => {
224
241
  if (!key.startsWith(prefix)) {
225
242
  return null;
226
243
  }
227
244
  const suffix = key.slice(prefix.length);
228
- const firstColon = suffix.indexOf(":");
229
- if (firstColon <= 0 || firstColon >= suffix.length - 1) {
245
+ const parts = suffix.split(":");
246
+ if (parts.length < 2 || parts.some((part) => !part)) {
230
247
  return null;
231
248
  }
249
+ const [dayKey, ...scopeParts] = parts;
250
+ const strategy = scopeParts.at(-1);
251
+ if (!dayKey || !strategy) return null;
232
252
  return {
233
- dayKey: suffix.slice(0, firstColon),
234
- strategy: suffix.slice(firstColon + 1)
253
+ dayKey,
254
+ ...scopeParts.length > 1 ? { deploymentId: scopeParts.slice(0, -1).join(":") } : {},
255
+ strategy
235
256
  };
236
257
  };
237
258
  var loadRuntimeSignalEvaluationStatsBuckets = async (userName) => {
@@ -293,7 +314,8 @@ var collectRuntimeDebugEvidence = async ({
293
314
  userName,
294
315
  startTime,
295
316
  endTime,
296
- strategies
317
+ strategies,
318
+ deploymentId
297
319
  }) => {
298
320
  const dayKeys = (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime);
299
321
  const dayKeySet = new Set(dayKeys);
@@ -303,6 +325,7 @@ var collectRuntimeDebugEvidence = async ({
303
325
  signals,
304
326
  evaluations,
305
327
  evaluationStatsBuckets,
328
+ lineageScopes,
306
329
  configs
307
330
  ] = await Promise.all([
308
331
  loadRuntimeTrades(userName, { startTime, endTime }),
@@ -310,6 +333,7 @@ var collectRuntimeDebugEvidence = async ({
310
333
  loadRuntimeSignals(userName, { startTime, endTime }),
311
334
  loadRuntimeSignalEvaluations(userName, { startTime, endTime }),
312
335
  loadRuntimeSignalEvaluationStatsBuckets(userName),
336
+ loadRuntimeLineageScopes(userName, { startTime, endTime }),
313
337
  (0, import_runtimeStrategyConfigs.loadRuntimeStrategyConfigs)(userName)
314
338
  ]);
315
339
  const tradesByOrderId = new Map(
@@ -322,15 +346,21 @@ var collectRuntimeDebugEvidence = async ({
322
346
  (left, right) => left.entryTimestamp - right.entryTimestamp
323
347
  );
324
348
  const filteredStats = evaluationStatsBuckets.filter(
325
- (entry) => dayKeySet.has(entry.dayKey) && (!strategies?.length || strategies.includes(entry.strategy))
349
+ (entry) => dayKeySet.has(entry.dayKey) && (!deploymentId || entry.deploymentId === deploymentId) && (!strategies?.length || strategies.includes(entry.strategy))
326
350
  );
351
+ const matchesDeployment = (value) => !deploymentId || value.deploymentId === deploymentId;
327
352
  return {
328
353
  dayKeys,
329
- trades: filterByStrategies(trades, strategies),
330
- signals: filterByStrategies(signals, strategies),
331
- evaluations: filterByStrategies(evaluations, strategies),
354
+ trades: filterByStrategies(trades, strategies).filter(matchesDeployment),
355
+ signals: filterByStrategies(signals, strategies).filter(matchesDeployment),
356
+ evaluations: filterByStrategies(evaluations, strategies).filter(
357
+ matchesDeployment
358
+ ),
332
359
  evaluationStatsBuckets: filteredStats,
333
- strategyConfigs: configs.filter(
360
+ lineageScopes: lineageScopes.filter(matchesDeployment).filter(
361
+ (scope) => !strategies?.length || strategies.includes(scope.strategy)
362
+ ),
363
+ strategyConfigs: deploymentId ? [] : configs.filter(
334
364
  (config) => !strategies?.length || strategies.includes(config.strategyName)
335
365
  )
336
366
  };
@@ -343,7 +373,8 @@ var buildRuntimeDebugReportPayload = async ({
343
373
  evaluations,
344
374
  trades,
345
375
  strategyConfigs,
346
- evaluationStatsBuckets
376
+ evaluationStatsBuckets,
377
+ lineageScopes
347
378
  }) => {
348
379
  const signalById = new Map(
349
380
  signals.map((signal) => [signal.signalId, signal])
@@ -471,6 +502,7 @@ var buildRuntimeDebugReportPayload = async ({
471
502
  signals: signals.length,
472
503
  evaluations: reportEvaluations.length,
473
504
  evaluationStatsBuckets: evaluationStatsBuckets?.length ?? 0,
505
+ lineageScopes: lineageScopes?.length ?? 0,
474
506
  strategyConfigs: strategyConfigs?.length ?? 0
475
507
  },
476
508
  trades: debugTrades,
@@ -494,6 +526,7 @@ var buildRuntimeDebugReportPayload = async ({
494
526
  evaluation
495
527
  })),
496
528
  evaluationStatsBuckets: evaluationStatsBuckets ?? [],
529
+ lineageScopes: lineageScopes ?? [],
497
530
  strategyConfigs: strategyConfigs ?? []
498
531
  };
499
532
  };
@@ -505,7 +538,8 @@ var buildRuntimeEvidenceReportPayload = ({
505
538
  evaluations,
506
539
  trades,
507
540
  strategyConfigs,
508
- evaluationStatsBuckets
541
+ evaluationStatsBuckets,
542
+ lineageScopes
509
543
  }) => ({
510
544
  reportType: "runtime-daily-evidence",
511
545
  generatedAt: Date.now(),
@@ -522,12 +556,14 @@ var buildRuntimeEvidenceReportPayload = ({
522
556
  signals: signals.length,
523
557
  evaluations: evaluations.length,
524
558
  evaluationStatsBuckets: evaluationStatsBuckets?.length ?? 0,
559
+ lineageScopes: lineageScopes?.length ?? 0,
525
560
  strategyConfigs: strategyConfigs?.length ?? 0
526
561
  },
527
562
  trades: trades.map((trade) => ({ trade })),
528
563
  signals: signals.map((signal) => ({ signal })),
529
564
  evaluations: evaluations.map((evaluation) => ({ evaluation })),
530
565
  evaluationStatsBuckets: evaluationStatsBuckets ?? [],
566
+ lineageScopes: lineageScopes ?? [],
531
567
  strategyConfigs: strategyConfigs ?? []
532
568
  });
533
569
  var buildRuntimeDebugReportAttachment = async (params) => {
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/runtimeEvidenceDeployment.ts
21
+ var runtimeEvidenceDeployment_exports = {};
22
+ __export(runtimeEvidenceDeployment_exports, {
23
+ activeRuntimeEvidenceStrategies: () => activeRuntimeEvidenceStrategies,
24
+ parseRuntimeEvidenceDeploymentSnapshot: () => parseRuntimeEvidenceDeploymentSnapshot,
25
+ resolveRuntimeEvidenceDeploymentSnapshot: () => resolveRuntimeEvidenceDeploymentSnapshot,
26
+ runtimeDeploymentFromEvidence: () => runtimeDeploymentFromEvidence
27
+ });
28
+ module.exports = __toCommonJS(runtimeEvidenceDeployment_exports);
29
+ var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
30
+ var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
31
+ var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
32
+ var INTERVALS = /* @__PURE__ */ new Set([
33
+ "1",
34
+ "3",
35
+ "5",
36
+ "15",
37
+ "30",
38
+ "60",
39
+ "120",
40
+ "240",
41
+ "360",
42
+ "720",
43
+ "D",
44
+ "W",
45
+ "M"
46
+ ]);
47
+ var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
48
+ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
49
+ if (!isRecord(value) || value.schemaVersion !== 1 || !isNonEmptyString(value.id) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
50
+ throw new Error(
51
+ "Runtime evidence deployment snapshot is missing or invalid"
52
+ );
53
+ }
54
+ const strategies = value.strategies.map((candidate) => {
55
+ if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !Number.isSafeInteger(candidate.version) || Number(candidate.version) <= 0 || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig)) {
56
+ throw new Error(
57
+ "Runtime evidence deployment strategy snapshot is invalid"
58
+ );
59
+ }
60
+ if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
61
+ throw new Error(
62
+ `Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
63
+ );
64
+ }
65
+ if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
66
+ throw new Error(
67
+ `Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
68
+ );
69
+ }
70
+ return candidate;
71
+ });
72
+ const identities = /* @__PURE__ */ new Set();
73
+ for (const strategy of strategies) {
74
+ if (identities.has(strategy.strategyName)) {
75
+ throw new Error(
76
+ `Duplicate runtime evidence strategy: ${strategy.strategyName}`
77
+ );
78
+ }
79
+ identities.add(strategy.strategyName);
80
+ }
81
+ return {
82
+ ...value,
83
+ strategies
84
+ };
85
+ };
86
+ var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
87
+ var runtimeDeploymentFromEvidence = (deployment) => ({
88
+ id: deployment.id,
89
+ label: deployment.label,
90
+ connectorName: deployment.connectorName,
91
+ provider: deployment.provider,
92
+ accountId: deployment.accountId,
93
+ enabled: deployment.enabled,
94
+ strategies: deployment.strategies.map(
95
+ ({ strategyName, version, enabled, controlState }) => ({
96
+ strategyName,
97
+ version,
98
+ enabled,
99
+ controlState
100
+ })
101
+ ),
102
+ ...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
103
+ ...deployment.tickers ? { tickers: [...deployment.tickers] } : {}
104
+ });
105
+ var resolveRuntimeEvidenceDeploymentSnapshot = async ({
106
+ userName,
107
+ projectRoot,
108
+ deploymentId
109
+ }) => {
110
+ const [deployment, strategies] = await Promise.all([
111
+ (0, import_runtimeStrategies.getRuntimeDeployment)({ userName, projectRoot, deploymentId }),
112
+ (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({ userName, projectRoot, deploymentId })
113
+ ]);
114
+ if (!deployment) {
115
+ throw new Error(`Runtime deployment not found: ${deploymentId}`);
116
+ }
117
+ return {
118
+ schemaVersion: 1,
119
+ id: deployment.id,
120
+ label: deployment.label,
121
+ connectorName: deployment.connectorName,
122
+ provider: deployment.provider,
123
+ accountId: deployment.accountId,
124
+ enabled: deployment.enabled,
125
+ strategies: strategies.map(
126
+ ({
127
+ strategyName,
128
+ version,
129
+ enabled,
130
+ controlState,
131
+ interval,
132
+ universe,
133
+ accountId,
134
+ strategyPackage,
135
+ strategyPackageVersion,
136
+ runtimePackageVersion,
137
+ sourceStrategyConfig
138
+ }) => ({
139
+ strategyName,
140
+ version,
141
+ enabled,
142
+ controlState,
143
+ interval,
144
+ universe,
145
+ ...accountId ? { accountId } : {},
146
+ strategyPackage,
147
+ strategyPackageVersion,
148
+ runtimePackageVersion,
149
+ strategyConfig: sourceStrategyConfig
150
+ })
151
+ ),
152
+ ...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
153
+ ...deployment.tickers ? { tickers: [...deployment.tickers] } : {}
154
+ };
155
+ };
156
+ // Annotate the CommonJS export names for ESM import in node:
157
+ 0 && (module.exports = {
158
+ activeRuntimeEvidenceStrategies,
159
+ parseRuntimeEvidenceDeploymentSnapshot,
160
+ resolveRuntimeEvidenceDeploymentSnapshot,
161
+ runtimeDeploymentFromEvidence
162
+ });
@@ -34,6 +34,7 @@ __export(runtimeLineage_exports, {
34
34
  fingerprintRuntimeValue: () => fingerprintRuntimeValue,
35
35
  resetRuntimeLineageCachesForTests: () => resetRuntimeLineageCachesForTests,
36
36
  runtimeLineageKey: () => runtimeLineageKey,
37
+ runtimeLineagesComparable: () => runtimeLineagesComparable,
37
38
  runtimeLineagesMatch: () => runtimeLineagesMatch
38
39
  });
39
40
  module.exports = __toCommonJS(runtimeLineage_exports);
@@ -330,7 +331,12 @@ async function buildRuntimeLineage({
330
331
  maxLossValue: resolveRuntimeMaxLossValue(config)
331
332
  };
332
333
  }
333
- var runtimeLineageKey = (lineage) => lineage.schemaVersion === 2 ? ["v2", lineage.version].join(":") : [
334
+ var runtimeLineageKey = (lineage) => lineage.schemaVersion === 2 ? [
335
+ "v2",
336
+ lineage.version,
337
+ lineage.strategyPackageVersion ?? "strategy:unknown",
338
+ lineage.runtimePackageVersion ?? "runtime:unknown"
339
+ ].join(":") : [
334
340
  lineage.schemaVersion,
335
341
  lineage.compositionId ?? "unbound-composition",
336
342
  lineage.gitSha ?? "unknown",
@@ -340,6 +346,7 @@ var runtimeLineageKey = (lineage) => lineage.schemaVersion === 2 ? ["v2", lineag
340
346
  lineage.contextFingerprint
341
347
  ].join(":");
342
348
  var runtimeLineagesMatch = (left, right) => left != null && right != null && runtimeLineageKey(left) === runtimeLineageKey(right);
349
+ var runtimeLineagesComparable = (left, right) => runtimeLineagesMatch(left, right) && typeof left?.maxLossValue === "number" && Number.isFinite(left.maxLossValue) && typeof right?.maxLossValue === "number" && Number.isFinite(right.maxLossValue) && left.maxLossValue === right.maxLossValue;
343
350
  var resetRuntimeLineageCachesForTests = () => {
344
351
  gitLineageCache.clear();
345
352
  gateFingerprintCache.clear();
@@ -352,5 +359,6 @@ var resetRuntimeLineageCachesForTests = () => {
352
359
  fingerprintRuntimeValue,
353
360
  resetRuntimeLineageCachesForTests,
354
361
  runtimeLineageKey,
362
+ runtimeLineagesComparable,
355
363
  runtimeLineagesMatch
356
364
  });