@tradejs/cli 3.1.29-beta.289 → 3.1.29-beta.291

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.
@@ -39,6 +39,9 @@ var import_node_path = __toESM(require("path"));
39
39
  var import_args = __toESM(require("args"));
40
40
  var import_chalk = __toESM(require("chalk"));
41
41
 
42
+ // src/lib/runtimeFeedbackNotification.ts
43
+ var import_userSettings = require("@tradejs/infra/userSettings");
44
+
42
45
  // src/lib/runtimeFeedbackArtifacts.ts
43
46
  var runtimeFeedbackArtifacts_exports = {};
44
47
  __reExport(runtimeFeedbackArtifacts_exports, require("@tradejs/node/diagnostics"));
@@ -223,11 +226,14 @@ var notifyRuntimeFeedbackBundle = async ({
223
226
  }) => {
224
227
  const verified = await (0, runtimeFeedbackArtifacts_exports.verifyRuntimeFeedbackReplayBundle)(bundleDir);
225
228
  const message = buildRuntimeFeedbackTelegramMessage(verified.replayEvidence);
226
- await send(message, { userName: verified.manifest.userName });
229
+ const settings = await (0, import_userSettings.getUserSettings)(verified.manifest.userName);
230
+ const sent = settings.NOTIFY_RUNTIME_DEBUG !== false;
231
+ if (sent) await send(message, { userName: verified.manifest.userName });
227
232
  return {
228
233
  artifactId: verified.manifest.artifactId,
229
234
  userName: verified.manifest.userName,
230
- message
235
+ message,
236
+ sent
231
237
  };
232
238
  };
233
239
 
@@ -249,7 +255,9 @@ var runtimeFeedbackNotify = async () => {
249
255
  }
250
256
  const result = await notifyRuntimeFeedbackBundle({ bundleDir });
251
257
  console.log(
252
- import_chalk.default.green(`Runtime feedback notification sent: ${result.artifactId}`)
258
+ import_chalk.default.green(
259
+ `Runtime feedback notification ${result.sent ? "sent" : "disabled"}: ${result.artifactId}`
260
+ )
253
261
  );
254
262
  };
255
263
  var main = runtimeFeedbackNotify;
@@ -43,6 +43,7 @@ var import_chalk = __toESM(require("chalk"));
43
43
  var import_node_child_process = require("child_process");
44
44
  var import_promises = __toESM(require("fs/promises"));
45
45
  var import_node_path = __toESM(require("path"));
46
+ var import_mcp = require("@tradejs/node/mcp");
46
47
 
47
48
  // src/lib/runtimeEvidenceDeployment.ts
48
49
  var runtimeEvidenceDeployment_exports = {};
@@ -190,44 +191,58 @@ var runLoggedCommand = async ({
190
191
  env,
191
192
  logHandle
192
193
  }) => {
193
- await logHandle.write(`
194
- $ ${[executable, ...command].join(" ")}
195
- `);
196
- await new Promise((resolve, reject) => {
197
- let pendingLogWrite = Promise.resolve();
198
- const appendLog = (chunk) => {
199
- pendingLogWrite = pendingLogWrite.then(async () => {
200
- await logHandle.write(chunk);
201
- });
202
- };
203
- const child = (0, import_node_child_process.spawn)(executable, command, {
204
- cwd,
205
- env,
206
- stdio: ["ignore", "pipe", "pipe"]
207
- });
208
- child.stdout.on("data", (chunk) => {
209
- process.stdout.write(chunk);
210
- appendLog(chunk);
211
- });
212
- child.stderr.on("data", (chunk) => {
213
- process.stderr.write(chunk);
214
- appendLog(chunk);
215
- });
194
+ await logHandle.writeFile(
195
+ "\n" + (0, import_mcp.formatMcpArtifactLogLine)(`$ ${[executable, ...command].join(" ")}`)
196
+ );
197
+ const child = (0, import_node_child_process.spawn)(executable, command, {
198
+ cwd,
199
+ env,
200
+ stdio: ["ignore", "pipe", "pipe"]
201
+ });
202
+ const exit = new Promise((resolve, reject) => {
216
203
  child.once("error", reject);
217
204
  child.once("close", (code, signal) => {
218
- void pendingLogWrite.then(() => {
219
- if (code === 0) {
220
- resolve();
221
- return;
222
- }
205
+ if (code === 0) resolve();
206
+ else {
223
207
  reject(
224
208
  new Error(
225
209
  `Runtime feedback command failed: exit=${String(code)} signal=${String(signal)}`
226
210
  )
227
211
  );
228
- }, reject);
212
+ }
229
213
  });
230
214
  });
215
+ let pendingLogWrite = Promise.resolve();
216
+ const copyLog = async (input, output) => {
217
+ const writeLine = async (line) => {
218
+ const formatted = (0, import_mcp.formatMcpArtifactLogLine)(line);
219
+ pendingLogWrite = pendingLogWrite.then(
220
+ () => logHandle.writeFile(formatted)
221
+ );
222
+ await pendingLogWrite;
223
+ output.write(formatted);
224
+ };
225
+ let pendingLine = "";
226
+ try {
227
+ for await (const chunk of input.setEncoding("utf8")) {
228
+ const lines = (pendingLine + chunk).split("\n");
229
+ pendingLine = lines.pop() ?? "";
230
+ for (const line of lines) await writeLine(line);
231
+ }
232
+ if (pendingLine) await writeLine(pendingLine);
233
+ } catch (error) {
234
+ child.kill();
235
+ throw error;
236
+ }
237
+ };
238
+ const results = await Promise.allSettled([
239
+ copyLog(child.stdout, process.stdout),
240
+ copyLog(child.stderr, process.stderr),
241
+ exit
242
+ ]);
243
+ for (const result of results) {
244
+ if (result.status === "rejected") throw result.reason;
245
+ }
231
246
  };
232
247
  var runRuntimeFeedbackReplay = async ({
233
248
  runtimeEvidencePath,
@@ -159,6 +159,7 @@ var extractRuntimeParityEntries = (trades) => trades.filter(
159
159
  direction: trade.direction,
160
160
  qty: toFiniteNumberOrNull(trade.qty),
161
161
  timestamp: trade.entryTimestamp,
162
+ ...Number.isFinite(trade.signalTimestamp) ? { signalTimestamp: trade.signalTimestamp } : {},
162
163
  price: toFiniteNumberOrNull(trade.entryPrice),
163
164
  orderId: trade.orderId,
164
165
  signalId: typeof trade.signalId === "string" ? trade.signalId : void 0,
@@ -35,6 +35,7 @@ __export(signalsSummary_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(signalsSummary_exports);
37
37
  var import_config = require("dotenv/config");
38
+ var import_userSettings = require("@tradejs/infra/userSettings");
38
39
  var import_args = __toESM(require("args"));
39
40
  var import_logger = require("@tradejs/infra/logger");
40
41
  var import_connectors = require("@tradejs/node/connectors");
@@ -1295,6 +1296,7 @@ none`
1295
1296
  };
1296
1297
  };
1297
1298
  var signalsSummary = async () => {
1299
+ const includeDebug = flags.printOnly || (await (0, import_userSettings.getUserSettings)(flags.user)).NOTIFY_RUNTIME_DEBUG !== false;
1298
1300
  const hours = Math.max(
1299
1301
  1,
1300
1302
  Number.parseInt(String(flags.hours ?? 24), 10) || 24
@@ -1415,18 +1417,15 @@ var signalsSummary = async () => {
1415
1417
  content: signalsMessage,
1416
1418
  deploymentId: scope.deploymentId
1417
1419
  });
1418
- const tradesMessageWithAttachmentSummary = appendReportAttachmentSummary({
1420
+ const tradesMessageWithAttachmentSummary = includeDebug ? appendReportAttachmentSummary({
1419
1421
  message: tradesMessage,
1420
1422
  signalsFilename: signalsAttachment.filename,
1421
1423
  filename: debugAttachment?.filename,
1422
1424
  tradesCount: debugAttachment ? scopeTrades.length : void 0,
1423
1425
  signalsCount: debugAttachment ? debugAttachment.summary?.signals ?? debugSignals.length : void 0,
1424
1426
  evaluationsCount: debugAttachment ? debugAttachment.summary?.evaluations ?? 0 : void 0
1425
- });
1426
- const attachments = [
1427
- signalsAttachment,
1428
- ...debugAttachment ? [debugAttachment] : []
1429
- ];
1427
+ }) : tradesMessage;
1428
+ const attachments = includeDebug ? [signalsAttachment, ...debugAttachment ? [debugAttachment] : []] : [];
1430
1429
  if (flags.printOnly) {
1431
1430
  console.log(tradesMessageWithAttachmentSummary);
1432
1431
  console.log("");
@@ -42,8 +42,40 @@ var import_chalk = __toESM(require("chalk"));
42
42
 
43
43
  // src/lib/strategyRelease/researchRetention.ts
44
44
  var import_promises = __toESM(require("fs/promises"));
45
- var import_node_path2 = __toESM(require("path"));
45
+
46
+ // src/lib/researchMetricSnapshot.ts
46
47
  var import_node_crypto = require("crypto");
48
+ var import_node_zlib = require("zlib");
49
+ var MAX_BYTES = 256 * 1024 * 1024;
50
+ var hash = (bytes) => (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex");
51
+ var validatePermanentResearchMetrics = (value) => {
52
+ if (!value || typeof value !== "object") return;
53
+ for (const [key, child] of Object.entries(value)) {
54
+ if (["matchedPairs", "tradeRows", "allProjectedLeaves"].includes(key) && Array.isArray(child) && child.length)
55
+ throw new Error(`Temporary payload ${key} is not a permanent metric`);
56
+ if (key === "trades" && Array.isArray(child) && child.some(
57
+ (row) => row && typeof row === "object" && ("signalId" in row || "entryTimestamp" in row)
58
+ ))
59
+ throw new Error("Temporary trade records are not permanent metrics");
60
+ validatePermanentResearchMetrics(child);
61
+ }
62
+ };
63
+ var decodeResearchMetricSnapshot = (snapshot) => {
64
+ if (!snapshot || typeof snapshot !== "object" || !("schema" in snapshot) || snapshot.schema !== "tradejs-metric-snapshot/v1" || !("encoding" in snapshot) || snapshot.encoding !== "gzip+base64" || !("decodedBytes" in snapshot) || typeof snapshot.decodedBytes !== "number" || !Number.isSafeInteger(snapshot.decodedBytes) || snapshot.decodedBytes <= 0 || snapshot.decodedBytes > MAX_BYTES || !("sha256" in snapshot) || typeof snapshot.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(snapshot.sha256) || !("payload" in snapshot) || typeof snapshot.payload !== "string")
65
+ throw new Error("Invalid metric snapshot envelope");
66
+ const bytes = (0, import_node_zlib.gunzipSync)(Buffer.from(snapshot.payload, "base64"), {
67
+ maxOutputLength: snapshot.decodedBytes
68
+ });
69
+ if (bytes.length !== snapshot.decodedBytes || hash(bytes) !== snapshot.sha256)
70
+ throw new Error("Metric snapshot checksum or size mismatch");
71
+ const metrics = JSON.parse(bytes.toString("utf8"));
72
+ validatePermanentResearchMetrics(metrics);
73
+ return metrics;
74
+ };
75
+
76
+ // src/lib/strategyRelease/researchRetention.ts
77
+ var import_node_path2 = __toESM(require("path"));
78
+ var import_node_crypto2 = require("crypto");
47
79
  var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
48
80
 
49
81
  // src/lib/strategyRelease/researchFileConsumers.ts
@@ -235,7 +267,7 @@ var mapFiles = async (entries, visit) => {
235
267
  if (worker.status === "rejected") throw worker.reason;
236
268
  return results;
237
269
  };
238
- var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto.createHash)("sha256").update(await import_promises.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
270
+ var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto2.createHash)("sha256").update(await import_promises.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
239
271
  var inside = (root, file) => {
240
272
  const relative = import_node_path2.default.relative(root, file);
241
273
  return relative !== "" && !relative.startsWith(`..${import_node_path2.default.sep}`) && relative !== ".." && !import_node_path2.default.isAbsolute(relative);
@@ -279,13 +311,27 @@ var applyResearchRetention = async (options) => {
279
311
  const header = Buffer.alloc(4096);
280
312
  const { bytesRead } = await handle.read(header, 0, header.length, 0);
281
313
  const frontmatter = header.subarray(0, bytesRead).toString("utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/);
282
- if (!frontmatter || !/^schema:\s*tradejs-research\/v1\s*$/m.test(frontmatter[1]) || !(retired ? /^reproduction:\s*(?:complete|partial|blocked|legacy-partial)\s*$/m.test(
314
+ if (!frontmatter || !/^schema:\s*tradejs-research\/v[12]\s*$/m.test(frontmatter[1]) || !(retired ? /^reproduction:\s*(?:complete|partial|blocked|legacy-partial)\s*$/m.test(
283
315
  frontmatter[1]
284
316
  ) : /^reproduction:\s*complete\s*$/m.test(frontmatter[1]))) {
285
317
  throw new Error(
286
318
  `Research note must have reproduction: complete: ${file}`
287
319
  );
288
320
  }
321
+ if (!retired && /^schema:\s*tradejs-research\/v2\s*$/m.test(frontmatter[1])) {
322
+ if ((await import_promises.default.stat(file)).size > 8 * 1024 * 1024)
323
+ throw new Error("Compact permanent note exceeds 8 MiB");
324
+ const content = await import_promises.default.readFile(file, "utf8");
325
+ const section = content.split("## Metrics snapshot (machine-readable)")[1]?.split("## Reported metrics")[0] ?? "";
326
+ const blocks = [
327
+ ...section.matchAll(/```json\r?\n([\s\S]*?)\r?\n```/g)
328
+ ];
329
+ if (blocks.length !== 1)
330
+ throw new Error(
331
+ "Complete v2 note needs one verified metric snapshot"
332
+ );
333
+ decodeResearchMetricSnapshot(JSON.parse(blocks[0][1]));
334
+ }
289
335
  } finally {
290
336
  await handle.close();
291
337
  }
@@ -369,7 +415,7 @@ var applyResearchRetention = async (options) => {
369
415
  };
370
416
 
371
417
  // src/lib/strategyRelease.ts
372
- var import_node_crypto2 = require("crypto");
418
+ var import_node_crypto3 = require("crypto");
373
419
  var import_promises2 = __toESM(require("fs/promises"));
374
420
  var import_node_path3 = __toESM(require("path"));
375
421
  var import_types2 = require("@tradejs/types");
@@ -1539,7 +1585,7 @@ var verifyCompletedCoreResearchBundle = async ({
1539
1585
  const artifactHashes = manifest.artifactHashes != null && typeof manifest.artifactHashes === "object" && !Array.isArray(manifest.artifactHashes) ? manifest.artifactHashes : null;
1540
1586
  const parsedSpec = JSON.parse(specBytes.toString("utf8"));
1541
1587
  assert2(
1542
- manifest.schema === "tradejs-core-research-manifest/v1" && manifest.status === "completed" && manifest.researchId === researchId && manifest.specSha256 === specSha256 && artifactHashes?.["result.json"] === resultSha256 && (0, import_node_crypto2.createHash)("sha256").update(canonicalStrategyReleaseJson(parsedSpec)).digest("hex") === specSha256,
1588
+ manifest.schema === "tradejs-core-research-manifest/v1" && manifest.status === "completed" && manifest.researchId === researchId && manifest.specSha256 === specSha256 && artifactHashes?.["result.json"] === resultSha256 && (0, import_node_crypto3.createHash)("sha256").update(canonicalStrategyReleaseJson(parsedSpec)).digest("hex") === specSha256,
1543
1589
  "core result is not bound by its completed core research manifest"
1544
1590
  );
1545
1591
  for (const [relativePath, expectedSha256] of Object.entries(
@@ -1587,12 +1633,18 @@ async function collectReleaseEvidenceReferences(references) {
1587
1633
  }
1588
1634
  const nested = (value) => value != null && typeof value === "object" && !Array.isArray(value) ? value : null;
1589
1635
  const array = (value) => Array.isArray(value) ? value : [];
1590
- const semanticallyValid = reference.kind === "core_research" ? artifact.schema === "tradejs-core-research-result/v1" || artifact.schema === "tradejs-core-research-manifest/v1" && artifact.status === "completed" : reference.kind === "ai_gate" ? nested(artifact.run)?.mode === "local-deterministic" && nested(artifact.research)?.lineage != null && nested(artifact.outcome) != null : reference.kind === "runtime_parity" ? artifact.reportType === "replay-runtime-evidence" && nested(nested(artifact.replay)?.runtimeComparison) != null : reference.kind === "execution_calibration" ? artifact.reportType === "execution-calibration" && nested(artifact.summary) != null : reference.kind === "runtime_evidence" ? artifact.reportType === "runtime-evidence" : artifact.reportType === "runtime-scorecard";
1636
+ const semanticallyValid = reference.kind === "core_research" ? [
1637
+ "tradejs-core-research-result/v1",
1638
+ "tradejs-core-research-result/v2"
1639
+ ].includes(String(artifact.schema)) || artifact.schema === "tradejs-core-research-manifest/v1" && artifact.status === "completed" : reference.kind === "ai_gate" ? nested(artifact.run)?.mode === "local-deterministic" && nested(artifact.research)?.lineage != null && nested(artifact.outcome) != null : reference.kind === "runtime_parity" ? artifact.reportType === "replay-runtime-evidence" && nested(nested(artifact.replay)?.runtimeComparison) != null : reference.kind === "execution_calibration" ? artifact.reportType === "execution-calibration" && nested(artifact.summary) != null : reference.kind === "runtime_evidence" ? artifact.reportType === "runtime-evidence" : artifact.reportType === "runtime-scorecard";
1591
1640
  assert2(
1592
1641
  semanticallyValid,
1593
1642
  `evidence ${reference.artifactId} does not match ${reference.kind}`
1594
1643
  );
1595
- if (reference.kind === "core_research" && artifact.schema === "tradejs-core-research-result/v1") {
1644
+ if (reference.kind === "core_research" && [
1645
+ "tradejs-core-research-result/v1",
1646
+ "tradejs-core-research-result/v2"
1647
+ ].includes(String(artifact.schema))) {
1596
1648
  await verifyCompletedCoreResearchBundle({
1597
1649
  artifact,
1598
1650
  resultPath: reference.path,