nexus-agents 8.96.0 → 8.97.0

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.
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  DEFAULT_TASK_TTL_MS,
23
23
  clampTaskTtl
24
- } from "./chunk-WFFE7JSD.js";
24
+ } from "./chunk-LFGVIWQ7.js";
25
25
  import {
26
26
  executeExpert
27
27
  } from "./chunk-FUU6LXIA.js";
@@ -70,9 +70,10 @@ import {
70
70
  simulationDeniedResult,
71
71
  toJobSummary,
72
72
  updateStage,
73
+ withFileLock,
73
74
  withWarnings,
74
75
  writeJobCancelled
75
- } from "./chunk-4LHLEHIS.js";
76
+ } from "./chunk-AVGO46BN.js";
76
77
  import {
77
78
  allOf,
78
79
  anyOf
@@ -95,7 +96,7 @@ import {
95
96
  } from "./chunk-CYP33W2C.js";
96
97
  import {
97
98
  withPrerequisite
98
- } from "./chunk-5VW2Z25X.js";
99
+ } from "./chunk-7TXK4ZU3.js";
99
100
  import {
100
101
  NOOP_NOTIFIER,
101
102
  RateLimiter,
@@ -123,10 +124,10 @@ import {
123
124
  toolSuccessStructured,
124
125
  withProgressHeartbeat,
125
126
  wrapToolWithTimeout
126
- } from "./chunk-GWVSAAW4.js";
127
+ } from "./chunk-IR25EHPP.js";
127
128
  import {
128
129
  VERSION
129
- } from "./chunk-CSNLW5ZS.js";
130
+ } from "./chunk-6I6JETP7.js";
130
131
  import {
131
132
  EventTopics,
132
133
  UNRESOLVED_MODEL_ID,
@@ -17404,7 +17405,7 @@ function wouldCreateCycle(from, to, getNode) {
17404
17405
  return canReach(to, from, /* @__PURE__ */ new Set(), getNode);
17405
17406
  }
17406
17407
  function findCyclePath(from, to, getNode) {
17407
- const path11 = [from, to];
17408
+ const path12 = [from, to];
17408
17409
  const visited = /* @__PURE__ */ new Set([from]);
17409
17410
  const findPath = (current) => {
17410
17411
  if (current === from) return true;
@@ -17413,9 +17414,9 @@ function findCyclePath(from, to, getNode) {
17413
17414
  for (const depId of node.dependencies.keys()) {
17414
17415
  if (!visited.has(depId)) {
17415
17416
  visited.add(depId);
17416
- path11.push(depId);
17417
+ path12.push(depId);
17417
17418
  if (findPath(depId)) return true;
17418
- path11.pop();
17419
+ path12.pop();
17419
17420
  } else if (depId === from) {
17420
17421
  return true;
17421
17422
  }
@@ -17423,7 +17424,7 @@ function findCyclePath(from, to, getNode) {
17423
17424
  return false;
17424
17425
  };
17425
17426
  findPath(to);
17426
- return path11;
17427
+ return path12;
17427
17428
  }
17428
17429
  function detectCycleDFS(nodeId, visited, stack, getNode) {
17429
17430
  visited.add(nodeId);
@@ -17442,29 +17443,29 @@ function detectCycleDFS(nodeId, visited, stack, getNode) {
17442
17443
  return false;
17443
17444
  }
17444
17445
  function findCycleFromNode(startId, getNode) {
17445
- const path11 = [];
17446
+ const path12 = [];
17446
17447
  const visited = /* @__PURE__ */ new Set();
17447
17448
  const dfs = (nodeId) => {
17448
- if (path11.includes(nodeId)) {
17449
- const cycleStart = path11.indexOf(nodeId);
17450
- path11.splice(0, cycleStart);
17451
- path11.push(nodeId);
17449
+ if (path12.includes(nodeId)) {
17450
+ const cycleStart = path12.indexOf(nodeId);
17451
+ path12.splice(0, cycleStart);
17452
+ path12.push(nodeId);
17452
17453
  return true;
17453
17454
  }
17454
17455
  if (visited.has(nodeId)) return false;
17455
17456
  visited.add(nodeId);
17456
- path11.push(nodeId);
17457
+ path12.push(nodeId);
17457
17458
  const node = getNode(nodeId);
17458
17459
  if (node) {
17459
17460
  for (const depId of node.dependencies.keys()) {
17460
17461
  if (dfs(depId)) return true;
17461
17462
  }
17462
17463
  }
17463
- path11.pop();
17464
+ path12.pop();
17464
17465
  return false;
17465
17466
  };
17466
17467
  dfs(startId);
17467
- return path11;
17468
+ return path12;
17468
17469
  }
17469
17470
 
17470
17471
  // src/agents/skills/skill-dependency-graph.ts
@@ -22447,18 +22448,18 @@ var DependencyGraph = class {
22447
22448
  }
22448
22449
  const startNode = firstNode;
22449
22450
  const visited = /* @__PURE__ */ new Set();
22450
- const path11 = [];
22451
+ const path12 = [];
22451
22452
  const dfs = (nodeId) => {
22452
- if (path11.includes(nodeId)) {
22453
- const cycleStart = path11.indexOf(nodeId);
22454
- path11.splice(0, cycleStart);
22453
+ if (path12.includes(nodeId)) {
22454
+ const cycleStart = path12.indexOf(nodeId);
22455
+ path12.splice(0, cycleStart);
22455
22456
  return true;
22456
22457
  }
22457
22458
  if (visited.has(nodeId)) {
22458
22459
  return false;
22459
22460
  }
22460
22461
  visited.add(nodeId);
22461
- path11.push(nodeId);
22462
+ path12.push(nodeId);
22462
22463
  const node = this.nodes.get(nodeId);
22463
22464
  if (node) {
22464
22465
  for (const depId of node.dependencies) {
@@ -22469,11 +22470,11 @@ var DependencyGraph = class {
22469
22470
  }
22470
22471
  }
22471
22472
  }
22472
- path11.pop();
22473
+ path12.pop();
22473
22474
  return false;
22474
22475
  };
22475
22476
  dfs(startNode);
22476
- return path11.length > 0 ? path11 : cycleNodes.slice(0, 3);
22477
+ return path12.length > 0 ? path12 : cycleNodes.slice(0, 3);
22477
22478
  }
22478
22479
  /**
22479
22480
  * Gets the execution order (topologically sorted step IDs).
@@ -23328,9 +23329,9 @@ function getBuiltInTemplatesPath() {
23328
23329
  join3(dirname(currentDir), "workflows", "templates"),
23329
23330
  join3(dirname(currentDir), "dist", "workflows", "templates")
23330
23331
  ];
23331
- for (const path11 of possiblePaths) {
23332
- if (existsSync3(path11)) {
23333
- return path11;
23332
+ for (const path12 of possiblePaths) {
23333
+ if (existsSync3(path12)) {
23334
+ return path12;
23334
23335
  }
23335
23336
  }
23336
23337
  return join3(currentDir, "templates");
@@ -23871,9 +23872,9 @@ function validateRequiredInputs(inputs, required) {
23871
23872
  }
23872
23873
 
23873
23874
  // src/workflows/expression-resolver-helpers.ts
23874
- function getNestedValue(obj, path11) {
23875
+ function getNestedValue(obj, path12) {
23875
23876
  let current = obj;
23876
- for (const key of path11) {
23877
+ for (const key of path12) {
23877
23878
  if (current === null || current === void 0) {
23878
23879
  return void 0;
23879
23880
  }
@@ -23884,24 +23885,24 @@ function getNestedValue(obj, path11) {
23884
23885
  }
23885
23886
  return current;
23886
23887
  }
23887
- function resolveInputs(path11, context) {
23888
- const value = getNestedValue(context.inputs, path11);
23888
+ function resolveInputs(path12, context) {
23889
+ const value = getNestedValue(context.inputs, path12);
23889
23890
  if (value === void 0) {
23890
23891
  return {
23891
23892
  success: false,
23892
- error: `Input '${path11.join(".")}' not found`
23893
+ error: `Input '${path12.join(".")}' not found`
23893
23894
  };
23894
23895
  }
23895
23896
  return { success: true, value };
23896
23897
  }
23897
- function validateStepPath(path11) {
23898
- if (path11.length < 2) {
23898
+ function validateStepPath(path12) {
23899
+ if (path12.length < 2) {
23899
23900
  return {
23900
23901
  success: false,
23901
23902
  error: "Steps expression requires at least stepId and output"
23902
23903
  };
23903
23904
  }
23904
- if (path11[0] === void 0 || path11[1] === void 0) {
23905
+ if (path12[0] === void 0 || path12[1] === void 0) {
23905
23906
  return {
23906
23907
  success: false,
23907
23908
  error: "Steps expression requires stepId and output"
@@ -23927,12 +23928,12 @@ function validateStepResult(stepId, outputKey, stepResult) {
23927
23928
  }
23928
23929
  return null;
23929
23930
  }
23930
- function resolveSteps(path11, context) {
23931
- const pathError = validateStepPath(path11);
23931
+ function resolveSteps(path12, context) {
23932
+ const pathError = validateStepPath(path12);
23932
23933
  if (pathError !== null) return pathError;
23933
- const stepId = path11[0] ?? "";
23934
- const outputKey = path11[1] ?? "";
23935
- const rest = path11.slice(2);
23934
+ const stepId = path12[0] ?? "";
23935
+ const outputKey = path12[1] ?? "";
23936
+ const rest = path12.slice(2);
23936
23937
  const stepResult = context.stepResults.get(stepId);
23937
23938
  const earlyReturn = validateStepResult(stepId, outputKey, stepResult);
23938
23939
  if (earlyReturn !== null) return earlyReturn;
@@ -23948,15 +23949,15 @@ function resolveSteps(path11, context) {
23948
23949
  }
23949
23950
  return { success: true, value };
23950
23951
  }
23951
- function resolveVariables(path11, context) {
23952
- if (path11.length === 0) {
23952
+ function resolveVariables(path12, context) {
23953
+ if (path12.length === 0) {
23953
23954
  return {
23954
23955
  success: false,
23955
23956
  error: "Variables expression requires a variable name"
23956
23957
  };
23957
23958
  }
23958
- const varName = path11[0];
23959
- const rest = path11.slice(1);
23959
+ const varName = path12[0];
23960
+ const rest = path12.slice(1);
23960
23961
  if (varName === void 0) {
23961
23962
  return {
23962
23963
  success: false,
@@ -25000,8 +25001,8 @@ var WorkflowEngine = class {
25000
25001
  this.config = resolveConfig2(config);
25001
25002
  }
25002
25003
  /** Load workflow template from file. */
25003
- async loadTemplate(path11) {
25004
- return this.deps.loadWorkflowFile(path11);
25004
+ async loadTemplate(path12) {
25005
+ return this.deps.loadWorkflowFile(path12);
25005
25006
  }
25006
25007
  /**
25007
25008
  * Execute a workflow with inputs.
@@ -25130,8 +25131,8 @@ var WorkflowEngine = class {
25130
25131
  }
25131
25132
  return Promise.resolve(templates);
25132
25133
  }
25133
- createTemplate(workflow, path11, category) {
25134
- const t = { name: workflow.name, version: workflow.version, path: path11, category };
25134
+ createTemplate(workflow, path12, category) {
25135
+ const t = { name: workflow.name, version: workflow.version, path: path12, category };
25135
25136
  if (workflow.description !== void 0) t.description = workflow.description;
25136
25137
  return t;
25137
25138
  }
@@ -25438,8 +25439,8 @@ function createParseWorkflow() {
25438
25439
  };
25439
25440
  }
25440
25441
  function createLoadWorkflowFile() {
25441
- return async (path11) => {
25442
- const result = await loadWorkflowFile(path11);
25442
+ return async (path12) => {
25443
+ const result = await loadWorkflowFile(path12);
25443
25444
  if (!result.ok) {
25444
25445
  if (result.error instanceof ParseError) {
25445
25446
  return result;
@@ -31016,31 +31017,31 @@ var GraphBuilder = class {
31016
31017
  }
31017
31018
  const visited = /* @__PURE__ */ new Set();
31018
31019
  const stack = /* @__PURE__ */ new Set();
31019
- const path11 = [];
31020
+ const path12 = [];
31020
31021
  for (const nodeId of this.nodes.keys()) {
31021
- const cycle = this.dfs(nodeId, adj, visited, stack, path11);
31022
+ const cycle = this.dfs(nodeId, adj, visited, stack, path12);
31022
31023
  if (cycle !== void 0) return cycle;
31023
31024
  }
31024
31025
  return void 0;
31025
31026
  }
31026
- dfs(nodeId, adj, visited, stack, path11) {
31027
+ dfs(nodeId, adj, visited, stack, path12) {
31027
31028
  if (stack.has(nodeId)) {
31028
- const cycleStart = path11.indexOf(nodeId);
31029
- const cyclePath = path11.slice(cycleStart);
31029
+ const cycleStart = path12.indexOf(nodeId);
31030
+ const cyclePath = path12.slice(cycleStart);
31030
31031
  cyclePath.push(nodeId);
31031
31032
  return { type: "cycle_detected", path: cyclePath };
31032
31033
  }
31033
31034
  if (visited.has(nodeId)) return void 0;
31034
31035
  visited.add(nodeId);
31035
31036
  stack.add(nodeId);
31036
- path11.push(nodeId);
31037
+ path12.push(nodeId);
31037
31038
  const neighbors = adj.get(nodeId) ?? [];
31038
31039
  for (const neighbor of neighbors) {
31039
- const cycle = this.dfs(neighbor, adj, visited, stack, path11);
31040
+ const cycle = this.dfs(neighbor, adj, visited, stack, path12);
31040
31041
  if (cycle !== void 0) return cycle;
31041
31042
  }
31042
31043
  stack.delete(nodeId);
31043
- path11.pop();
31044
+ path12.pop();
31044
31045
  return void 0;
31045
31046
  }
31046
31047
  checkReachability() {
@@ -32008,8 +32009,8 @@ function extractModelAttribution(event) {
32008
32009
  };
32009
32010
  }
32010
32011
  function extractRoutingAttribution(event) {
32011
- const path11 = event.decisionPath !== void 0 ? [...event.decisionPath] : void 0;
32012
- return { modelId: event.selectedModel, reasoning: event.reasoning, decisionPath: path11 };
32012
+ const path12 = event.decisionPath !== void 0 ? [...event.decisionPath] : void 0;
32013
+ return { modelId: event.selectedModel, reasoning: event.reasoning, decisionPath: path12 };
32013
32014
  }
32014
32015
  function extractPipelineAttribution(event) {
32015
32016
  if (event.type === "pipeline.completed") {
@@ -41761,12 +41762,12 @@ function extractFileReferences(markdown) {
41761
41762
  let match;
41762
41763
  FILE_REF_PATTERN.lastIndex = 0;
41763
41764
  while ((match = FILE_REF_PATTERN.exec(markdown)) !== null) {
41764
- const path11 = match[1];
41765
- if (path11 === void 0 || seen.has(path11)) continue;
41766
- seen.add(path11);
41765
+ const path12 = match[1];
41766
+ if (path12 === void 0 || seen.has(path12)) continue;
41767
+ seen.add(path12);
41767
41768
  const lineStr = match[2];
41768
41769
  const line = lineStr !== void 0 ? parseInt(lineStr, 10) : void 0;
41769
- refs.push(line !== void 0 ? { path: path11, line } : { path: path11 });
41770
+ refs.push(line !== void 0 ? { path: path12, line } : { path: path12 });
41770
41771
  }
41771
41772
  return refs;
41772
41773
  }
@@ -44509,11 +44510,54 @@ var InMemoryAuditStorage = class {
44509
44510
  // src/audit/audit-storage.ts
44510
44511
  import * as fs5 from "node:fs";
44511
44512
  import * as os from "node:os";
44513
+ import * as path4 from "node:path";
44514
+
44515
+ // src/audit/audit-chain-tail.ts
44516
+ import { open } from "node:fs/promises";
44512
44517
  import * as path3 from "node:path";
44518
+ var TAIL_CHUNK_BYTES = 64 * 1024;
44519
+ async function lastEventIn(filePath) {
44520
+ const handle = await open(filePath, "r");
44521
+ try {
44522
+ const { size } = await handle.stat();
44523
+ for (let window = TAIL_CHUNK_BYTES; ; window *= 2) {
44524
+ const start = Math.max(0, size - window);
44525
+ const buffer = Buffer.alloc(size - start);
44526
+ await handle.read(buffer, 0, buffer.length, start);
44527
+ const lines = buffer.toString("utf-8").split("\n");
44528
+ const whole = start === 0 ? lines : lines.slice(1);
44529
+ for (let i = whole.length - 1; i >= 0; i--) {
44530
+ const event = parseEvent(whole[i] ?? "");
44531
+ if (event !== null) return event;
44532
+ }
44533
+ if (start === 0) return null;
44534
+ }
44535
+ } finally {
44536
+ await handle.close();
44537
+ }
44538
+ }
44539
+ function parseEvent(line) {
44540
+ if (line.trim().length === 0) return null;
44541
+ try {
44542
+ const parsed = AuditEventSchema.safeParse(JSON.parse(line));
44543
+ return parsed.success ? { hash: parsed.data.hash } : null;
44544
+ } catch {
44545
+ return null;
44546
+ }
44547
+ }
44548
+ async function readChainTailHash(logDir, fileNamesNewestFirst) {
44549
+ for (const name of fileNamesNewestFirst) {
44550
+ const last = await lastEventIn(path3.join(logDir, name));
44551
+ if (last !== null) return last.hash;
44552
+ }
44553
+ return void 0;
44554
+ }
44555
+
44556
+ // src/audit/audit-storage.ts
44513
44557
  function validateLogDir(logDir, allowedRoot) {
44514
- const resolvedRoot = path3.resolve(allowedRoot);
44515
- const resolvedLogDir = path3.resolve(allowedRoot, logDir);
44516
- if (!resolvedLogDir.startsWith(resolvedRoot + path3.sep) && resolvedLogDir !== resolvedRoot) {
44558
+ const resolvedRoot = path4.resolve(allowedRoot);
44559
+ const resolvedLogDir = path4.resolve(allowedRoot, logDir);
44560
+ if (!resolvedLogDir.startsWith(resolvedRoot + path4.sep) && resolvedLogDir !== resolvedRoot) {
44517
44561
  return {
44518
44562
  ok: false,
44519
44563
  error: new SecurityError("Path traversal detected: logDir escapes allowed root directory", {
@@ -44536,8 +44580,8 @@ function validateLogDirBasic(logDir) {
44536
44580
  };
44537
44581
  }
44538
44582
  }
44539
- const resolved = path3.resolve(logDir);
44540
- if (isRefusedSystemPath(resolved, path3.resolve(os.homedir()))) {
44583
+ const resolved = path4.resolve(logDir);
44584
+ if (isRefusedSystemPath(resolved, path4.resolve(os.homedir()))) {
44541
44585
  return {
44542
44586
  ok: false,
44543
44587
  error: new SecurityError("logDir cannot be a system directory", {
@@ -44550,12 +44594,12 @@ function validateLogDirBasic(logDir) {
44550
44594
  var ALWAYS_REFUSED_DIRS = ["/etc", "/usr", "/bin", "/sbin", "/proc", "/sys"];
44551
44595
  var REFUSED_OUTSIDE_HOME_DIRS = ["/root", "/var"];
44552
44596
  function isWithin(child, parent) {
44553
- const rel = path3.relative(parent, child);
44554
- return rel === "" || rel !== ".." && !rel.startsWith(".." + path3.sep) && !path3.isAbsolute(rel);
44597
+ const rel = path4.relative(parent, child);
44598
+ return rel === "" || rel !== ".." && !rel.startsWith(".." + path4.sep) && !path4.isAbsolute(rel);
44555
44599
  }
44556
44600
  function isRefusedSystemPath(resolved, home) {
44557
44601
  if (ALWAYS_REFUSED_DIRS.some((dir) => isWithin(resolved, dir))) return true;
44558
- const homeGrants = home !== path3.parse(home).root && isWithin(resolved, home);
44602
+ const homeGrants = home !== path4.parse(home).root && isWithin(resolved, home);
44559
44603
  return REFUSED_OUTSIDE_HOME_DIRS.some((dir) => isWithin(resolved, dir)) && !homeGrants;
44560
44604
  }
44561
44605
  var FileAuditStorage = class _FileAuditStorage {
@@ -44636,7 +44680,7 @@ var FileAuditStorage = class _FileAuditStorage {
44636
44680
  const existingFiles = this.getExistingLogFiles();
44637
44681
  const latestFileName = existingFiles[0];
44638
44682
  if (existingFiles.length > 0 && latestFileName !== void 0) {
44639
- const latestFile = path3.join(this.logDir, latestFileName);
44683
+ const latestFile = path4.join(this.logDir, latestFileName);
44640
44684
  const stats = fs5.statSync(latestFile);
44641
44685
  if (stats.size < this.maxFileSizeBytes) {
44642
44686
  this.currentFile = latestFile;
@@ -44659,8 +44703,9 @@ var FileAuditStorage = class _FileAuditStorage {
44659
44703
  this.writeStream.end();
44660
44704
  this.writeStream = null;
44661
44705
  }
44662
- this.currentFile = path3.join(this.logDir, this.generateFileName());
44706
+ this.currentFile = path4.join(this.logDir, this.generateFileName());
44663
44707
  this.currentFileSize = 0;
44708
+ fs5.closeSync(fs5.openSync(this.currentFile, "a"));
44664
44709
  this.openWriteStream();
44665
44710
  this.logger.debug("Rotated audit log file", { file: this.currentFile });
44666
44711
  this.pruneOldFiles();
@@ -44670,7 +44715,7 @@ var FileAuditStorage = class _FileAuditStorage {
44670
44715
  if (files.length > this.maxFiles) {
44671
44716
  const filesToDelete = files.slice(this.maxFiles);
44672
44717
  for (const file of filesToDelete) {
44673
- const filePath = path3.join(this.logDir, file);
44718
+ const filePath = path4.join(this.logDir, file);
44674
44719
  fs5.unlinkSync(filePath);
44675
44720
  this.logger.info("Pruned old audit log", { file });
44676
44721
  }
@@ -44702,6 +44747,60 @@ var FileAuditStorage = class _FileAuditStorage {
44702
44747
  });
44703
44748
  });
44704
44749
  }
44750
+ /**
44751
+ * Append a chained batch under the cross-process lock (#6546). The tail
44752
+ * read, the seal and the flush to disk all happen while the lock is held,
44753
+ * so two processes can neither both chain from the same tail (a fork) nor
44754
+ * interleave lines between another process's read and write.
44755
+ */
44756
+ async appendChained(seal) {
44757
+ await withFileLock(path4.join(this.logDir, `${this.filePrefix}.lock`), async () => {
44758
+ this.adoptLatestFile();
44759
+ this.terminateTornLine();
44760
+ const tailHash = await readChainTailHash(this.logDir, this.getExistingLogFiles());
44761
+ for (const event of seal(tailHash)) await this.write(event);
44762
+ await this.flush();
44763
+ });
44764
+ }
44765
+ /**
44766
+ * Under the append lock, follow whichever file is newest now: another
44767
+ * process may have rotated or appended since this one last wrote. Appending
44768
+ * to an older file would place this batch BEFORE events it chains after.
44769
+ */
44770
+ adoptLatestFile() {
44771
+ const latest = this.getExistingLogFiles()[0];
44772
+ if (latest === void 0) {
44773
+ this.rotateFile();
44774
+ return;
44775
+ }
44776
+ const latestPath = path4.join(this.logDir, latest);
44777
+ if (latestPath !== this.currentFile) {
44778
+ this.writeStream?.end();
44779
+ this.writeStream = null;
44780
+ this.currentFile = latestPath;
44781
+ this.openWriteStream();
44782
+ }
44783
+ this.currentFileSize = fs5.statSync(latestPath).size;
44784
+ }
44785
+ /**
44786
+ * Under the append lock: if a writer crashed mid-line, end that line before
44787
+ * appending, or the next event would be glued onto it and lost with it. The
44788
+ * torn line itself is left in place — the verifier still counts it as
44789
+ * skipped — and the tail read links the new batch past it.
44790
+ */
44791
+ terminateTornLine() {
44792
+ if (this.currentFile === null || this.currentFileSize === 0) return;
44793
+ const last = Buffer.alloc(1);
44794
+ const fd = fs5.openSync(this.currentFile, "r");
44795
+ try {
44796
+ fs5.readSync(fd, last, 0, 1, this.currentFileSize - 1);
44797
+ } finally {
44798
+ fs5.closeSync(fd);
44799
+ }
44800
+ if (last[0] === 10) return;
44801
+ fs5.appendFileSync(this.currentFile, "\n");
44802
+ this.currentFileSize += 1;
44803
+ }
44705
44804
  async close() {
44706
44805
  await this.flush();
44707
44806
  return new Promise((resolve19) => {
@@ -44724,7 +44823,7 @@ var FileAuditStorage = class _FileAuditStorage {
44724
44823
  let skipped = 0;
44725
44824
  for (const file of files) {
44726
44825
  if (results.length >= limit) break;
44727
- const filePath = path3.join(this.logDir, file);
44826
+ const filePath = path4.join(this.logDir, file);
44728
44827
  const events = await readAuditFile({
44729
44828
  filePath,
44730
44829
  criteria,
@@ -44980,14 +45079,30 @@ var AuditLogger = class {
44980
45079
  policyName: input.policyName,
44981
45080
  policyDecision: input.policyDecision,
44982
45081
  violationType: input.violationType,
44983
- previousHash: this.enableHashChain ? this.lastHash ?? void 0 : void 0
45082
+ // Linked at flush time by sealChain (#6546), not here.
45083
+ previousHash: void 0
44984
45084
  };
44985
45085
  if (hasTierTransitionPayload(event)) event.hashVersion = AUDIT_HASH_VERSION_TIER_TRANSITION;
44986
- if (this.enableHashChain) {
45086
+ return JSON.parse(JSON.stringify(event));
45087
+ }
45088
+ /**
45089
+ * Link `events` onto the chain whose head is `tailHash` (#6546).
45090
+ *
45091
+ * Runs at flush time, not at log() time: the head of a shared log is only
45092
+ * known under the storage's cross-process lock, after reading the last
45093
+ * persisted event. `tailHash` undefined is the empty case — a fresh log —
45094
+ * and the first event is then a genesis event with no `previousHash`.
45095
+ */
45096
+ sealChain(events, tailHash) {
45097
+ if (!this.enableHashChain) return events;
45098
+ let prior = tailHash;
45099
+ for (const event of events) {
45100
+ event.previousHash = prior;
44987
45101
  event.hash = computeEventHash(event);
44988
- this.lastHash = event.hash;
45102
+ prior = event.hash;
44989
45103
  }
44990
- return event;
45104
+ this.lastHash = prior ?? null;
45105
+ return events;
44991
45106
  }
44992
45107
  log(input) {
44993
45108
  if (this.closed) {
@@ -44995,7 +45110,13 @@ var AuditLogger = class {
44995
45110
  return;
44996
45111
  }
44997
45112
  if (!this.shouldLog(input)) return;
44998
- const event = this.createEvent(input);
45113
+ let event;
45114
+ try {
45115
+ event = this.createEvent(input);
45116
+ } catch (err2) {
45117
+ this.recordPersistFailure(err2);
45118
+ return;
45119
+ }
44999
45120
  this.eventQueue.push(event);
45000
45121
  if (this.eventQueue.length > this.maxQueueDepth) {
45001
45122
  const dropCount = this.eventQueue.length - this.maxQueueDepth;
@@ -45196,7 +45317,20 @@ var AuditLogger = class {
45196
45317
  async drainAndFlushOnce() {
45197
45318
  if (this.eventQueue.length > 0) {
45198
45319
  const events = this.eventQueue.splice(0, this.eventQueue.length);
45199
- for (const event of events) {
45320
+ if (this.storage.appendChained !== void 0) {
45321
+ const progress = { sealed: false };
45322
+ try {
45323
+ await this.storage.appendChained((tailHash) => {
45324
+ progress.sealed = true;
45325
+ return this.sealChain(events, tailHash);
45326
+ });
45327
+ } catch (err2) {
45328
+ if (!progress.sealed) this.eventQueue.unshift(...events);
45329
+ throw err2;
45330
+ }
45331
+ return;
45332
+ }
45333
+ for (const event of this.sealChain(events, this.lastHash ?? void 0)) {
45200
45334
  await this.storage.write(event);
45201
45335
  }
45202
45336
  }
@@ -45535,8 +45669,8 @@ function computePrReviewRecordHash(payload) {
45535
45669
  }
45536
45670
 
45537
45671
  // src/audit/pr-review-record-store.ts
45538
- import { appendFileSync as appendFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6 } from "node:fs";
45539
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join9, resolve as resolve11 } from "node:path";
45672
+ import { appendFileSync as appendFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6 } from "node:fs";
45673
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join10, resolve as resolve11 } from "node:path";
45540
45674
  var PR_REVIEW_RECORDS_REL_PATH = "governance/pr-review-records.jsonl";
45541
45675
  var PR_REVIEW_RECORDS_PATH_ENV = "NEXUS_PR_REVIEW_RECORDS_PATH";
45542
45676
  var MAX_SUMMARY_RECORD_CHARS = 500;
@@ -45575,11 +45709,11 @@ function resolvePrReviewRecordsPath(repoPathOverride) {
45575
45709
  }
45576
45710
  if (repoPathOverride !== void 0 && repoPathOverride.trim() !== "") {
45577
45711
  const overrideRoot = findRepoRoot(repoPathOverride);
45578
- if (overrideRoot !== null) return join9(overrideRoot, PR_REVIEW_RECORDS_REL_PATH);
45712
+ if (overrideRoot !== null) return join10(overrideRoot, PR_REVIEW_RECORDS_REL_PATH);
45579
45713
  }
45580
45714
  const root = findRepoRoot(process.cwd());
45581
45715
  if (root === null) return void 0;
45582
- return join9(root, PR_REVIEW_RECORDS_REL_PATH);
45716
+ return join10(root, PR_REVIEW_RECORDS_REL_PATH);
45583
45717
  }
45584
45718
  function readPrReviewRecords(filePath) {
45585
45719
  const records = [];
@@ -45631,7 +45765,7 @@ function persistPrReviewRecord(opts) {
45631
45765
  sequence: maxSequence + 1,
45632
45766
  previousHash: lastHash
45633
45767
  });
45634
- appendFileSync2(
45768
+ appendFileSync3(
45635
45769
  filePath,
45636
45770
  serializeValidatedRecord(PrReviewRecordSchema, record, "pr-review"),
45637
45771
  "utf-8"
@@ -46332,7 +46466,7 @@ function ensureRunnableTemplate(template, stages) {
46332
46466
 
46333
46467
  // src/pipeline/pipeline-checkpoint.ts
46334
46468
  import * as fs6 from "node:fs";
46335
- import * as path4 from "node:path";
46469
+ import * as path5 from "node:path";
46336
46470
  import { z as z99 } from "zod";
46337
46471
  var logger39 = createLogger({ component: "pipeline-checkpoint" });
46338
46472
  var SESSION_ID_REGEX = /^[a-zA-Z0-9_-]{1,128}$/;
@@ -46345,7 +46479,7 @@ function getCheckpointPath(sessionId, customDir) {
46345
46479
  logger39.warn("Checkpoint directory unavailable", { error: dirResult.error.message });
46346
46480
  return null;
46347
46481
  }
46348
- return path4.join(dirResult.value, `pipeline-${sessionId}.jsonl`);
46482
+ return path5.join(dirResult.value, `pipeline-${sessionId}.jsonl`);
46349
46483
  }
46350
46484
  function saveStageCheckpoint(sessionId, stage, data, customDir) {
46351
46485
  if (!validateSessionId(sessionId)) {
@@ -46599,7 +46733,7 @@ function buildVotingInput(plan, config) {
46599
46733
  }
46600
46734
  async function executeSingleVote(plan, config, log) {
46601
46735
  try {
46602
- const { executeVoting } = await import("./consensus-vote-BBLGVUVU.js");
46736
+ const { executeVoting } = await import("./consensus-vote-44KYPBV4.js");
46603
46737
  const input = buildVotingInput(plan, config);
46604
46738
  const result = await executeVoting(input, log);
46605
46739
  return parseVotingResult(result);
@@ -47565,7 +47699,7 @@ function createVoteStage({ config, startStage }) {
47565
47699
  const strategy = config.votingStrategy ?? "higher_order";
47566
47700
  await postProgress(config, "Vote", `Running consensus with ${strategy} strategy...`);
47567
47701
  try {
47568
- const { executeVoting } = await import("./consensus-vote-BBLGVUVU.js");
47702
+ const { executeVoting } = await import("./consensus-vote-44KYPBV4.js");
47569
47703
  const votingResult = await executeVoting(
47570
47704
  {
47571
47705
  proposal: buildVoteProposal(plan, research),
@@ -47688,8 +47822,8 @@ function parseEnvelope(json) {
47688
47822
  }
47689
47823
  function describeZodError(error) {
47690
47824
  return error.issues.slice(0, 3).map((issue) => {
47691
- const path11 = issue.path.length > 0 ? issue.path.join(".") : "(root)";
47692
- return `${path11}: ${issue.message}`;
47825
+ const path12 = issue.path.length > 0 ? issue.path.join(".") : "(root)";
47826
+ return `${path12}: ${issue.message}`;
47693
47827
  }).join("; ");
47694
47828
  }
47695
47829
  function parseSarif(sarifJson, maxFindings = 100) {
@@ -47868,7 +48002,7 @@ function resolveConfidence(rule) {
47868
48002
  }
47869
48003
 
47870
48004
  // src/mcp/tools/security-scan.ts
47871
- import * as path5 from "node:path";
48005
+ import * as path6 from "node:path";
47872
48006
  var logger46 = createLogger({ component: "security-scan" });
47873
48007
  var SCAN_TIMEOUT_MS = 3e5;
47874
48008
  async function isSemgrepAvailable() {
@@ -47895,9 +48029,9 @@ async function runSemgrep(targetDir, rulesets) {
47895
48029
  return stdout;
47896
48030
  }
47897
48031
  function validateTargetPath(target) {
47898
- const root = path5.resolve(process.cwd());
47899
- const resolved = path5.resolve(root, target);
47900
- if (resolved !== root && !resolved.startsWith(root + path5.sep)) {
48032
+ const root = path6.resolve(process.cwd());
48033
+ const resolved = path6.resolve(root, target);
48034
+ if (resolved !== root && !resolved.startsWith(root + path6.sep)) {
47901
48035
  throw new Error(`Invalid target path: must resolve inside ${root} (got ${resolved})`);
47902
48036
  }
47903
48037
  return resolved;
@@ -48098,8 +48232,8 @@ async function runOsvCheck(targetDir, enabled) {
48098
48232
  if (!enabled) return OSV_EMPTY;
48099
48233
  try {
48100
48234
  const fs12 = await import("node:fs");
48101
- const path11 = await import("node:path");
48102
- const pkgPath = path11.join(targetDir, "package.json");
48235
+ const path12 = await import("node:path");
48236
+ const pkgPath = path12.join(targetDir, "package.json");
48103
48237
  if (!fs12.existsSync(pkgPath)) return OSV_EMPTY;
48104
48238
  const pkg = JSON.parse(fs12.readFileSync(pkgPath, "utf-8"));
48105
48239
  const declared = Object.keys(pkg.dependencies ?? {}).length;
@@ -48158,7 +48292,7 @@ function buildScanSummary(total, blocking, osvCount, osv, sarifParseErrors = 0)
48158
48292
 
48159
48293
  // src/security/quality-gate-commands.ts
48160
48294
  import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
48161
- import { join as join11 } from "node:path";
48295
+ import { join as join12 } from "node:path";
48162
48296
  var LOCKFILES = [
48163
48297
  { file: "pnpm-lock.yaml", manager: "pnpm" },
48164
48298
  { file: "yarn.lock", manager: "yarn" },
@@ -48173,12 +48307,12 @@ var SCRIPT_CANDIDATES = {
48173
48307
  };
48174
48308
  function detectPackageManager(projectDir) {
48175
48309
  for (const { file, manager } of LOCKFILES) {
48176
- if (existsSync9(join11(projectDir, file))) return manager;
48310
+ if (existsSync9(join12(projectDir, file))) return manager;
48177
48311
  }
48178
48312
  return "npm";
48179
48313
  }
48180
48314
  function readScripts(projectDir) {
48181
- const manifest = join11(projectDir, "package.json");
48315
+ const manifest = join12(projectDir, "package.json");
48182
48316
  if (!existsSync9(manifest)) return void 0;
48183
48317
  try {
48184
48318
  const parsed = JSON.parse(readFileSync8(manifest, "utf-8"));
@@ -49245,8 +49379,8 @@ var SENSITIVE_PATH_PATTERNS = Object.freeze([
49245
49379
  "permission",
49246
49380
  "sql"
49247
49381
  ]);
49248
- function isSensitivePath(path11) {
49249
- const lower = path11.toLowerCase();
49382
+ function isSensitivePath(path12) {
49383
+ const lower = path12.toLowerCase();
49250
49384
  return SENSITIVE_PATH_PATTERNS.some((p) => lower.includes(p));
49251
49385
  }
49252
49386
  function byteLen(text) {
@@ -50391,7 +50525,7 @@ function registerVendorPublishingAuditTool(server, deps) {
50391
50525
 
50392
50526
  // src/mcp/tools/compare-data-feeds.ts
50393
50527
  import { z as z105 } from "zod";
50394
- import * as path6 from "node:path";
50528
+ import * as path7 from "node:path";
50395
50529
  import * as fs7 from "node:fs";
50396
50530
  import * as yaml4 from "yaml";
50397
50531
  var CompareDataFeedsInputSchema = z105.object({
@@ -50415,7 +50549,7 @@ function readDottedPath(obj, dottedPath) {
50415
50549
  return cursor;
50416
50550
  }
50417
50551
  function parseFeedContent(content, sourcePath) {
50418
- const ext = path6.extname(sourcePath).toLowerCase();
50552
+ const ext = path7.extname(sourcePath).toLowerCase();
50419
50553
  if (ext === ".json") {
50420
50554
  return JSON.parse(content);
50421
50555
  }
@@ -50522,9 +50656,9 @@ function buildSummary(feedAPath, feedBPath, diff, fieldDiffs, fields) {
50522
50656
  return lines.join("\n");
50523
50657
  }
50524
50658
  function loadFeed(feedPath) {
50525
- const resolved = path6.resolve(feedPath);
50526
- const cwdRoot = path6.resolve(".");
50527
- if (resolved !== cwdRoot && !resolved.startsWith(cwdRoot + path6.sep)) {
50659
+ const resolved = path7.resolve(feedPath);
50660
+ const cwdRoot = path7.resolve(".");
50661
+ if (resolved !== cwdRoot && !resolved.startsWith(cwdRoot + path7.sep)) {
50528
50662
  throw new Error(`Path traversal denied: ${feedPath} must be within ${cwdRoot}`);
50529
50663
  }
50530
50664
  if (!fs7.existsSync(resolved)) {
@@ -50692,7 +50826,7 @@ function registerQueryTaskStateTool(server, deps) {
50692
50826
  import { z as z109 } from "zod";
50693
50827
 
50694
50828
  // src/mcp/tools/ci-health-log.ts
50695
- import { appendFileSync as appendFileSync4, existsSync as existsSync11, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync3 } from "node:fs";
50829
+ import { appendFileSync as appendFileSync5, existsSync as existsSync11, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync3 } from "node:fs";
50696
50830
  import { z as z108 } from "zod";
50697
50831
 
50698
50832
  // src/mcp/tools/ci-health-types.ts
@@ -50724,17 +50858,17 @@ function maxLogBytes() {
50724
50858
  const n = Number.parseInt(raw, 10);
50725
50859
  return Number.isFinite(n) && n > 0 ? n : DEFAULT_CI_HEALTH_MAX_BYTES;
50726
50860
  }
50727
- function capLogSize(path11) {
50861
+ function capLogSize(path12) {
50728
50862
  const max = maxLogBytes();
50729
50863
  let size;
50730
50864
  try {
50731
- size = statSync2(path11).size;
50865
+ size = statSync2(path12).size;
50732
50866
  } catch {
50733
50867
  return;
50734
50868
  }
50735
50869
  if (size <= max) return;
50736
50870
  try {
50737
- const lines = readFileSync10(path11, "utf-8").split("\n").filter((l) => l !== "");
50871
+ const lines = readFileSync10(path12, "utf-8").split("\n").filter((l) => l !== "");
50738
50872
  const kept = [];
50739
50873
  let bytes2 = 0;
50740
50874
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -50746,7 +50880,7 @@ function capLogSize(path11) {
50746
50880
  bytes2 += lineBytes;
50747
50881
  }
50748
50882
  kept.reverse();
50749
- writeFileSync3(path11, kept.length > 0 ? `${kept.join("\n")}
50883
+ writeFileSync3(path12, kept.length > 0 ? `${kept.join("\n")}
50750
50884
  ` : "");
50751
50885
  logger51.warn("ci-health log exceeded size cap \u2014 dropped oldest events", {
50752
50886
  maxBytes: max,
@@ -50767,10 +50901,10 @@ function appendCiHealthEvent(event) {
50767
50901
  ...event
50768
50902
  };
50769
50903
  try {
50770
- const path11 = logFilePath();
50771
- appendFileSync4(path11, `${JSON.stringify(record)}
50904
+ const path12 = logFilePath();
50905
+ appendFileSync5(path12, `${JSON.stringify(record)}
50772
50906
  `);
50773
- capLogSize(path11);
50907
+ capLogSize(path12);
50774
50908
  } catch (err2) {
50775
50909
  logger51.warn("Failed to append ci-health event", {
50776
50910
  error: err2 instanceof Error ? err2.message : String(err2)
@@ -51079,7 +51213,7 @@ function registerRunQualityGateTool(server, deps) {
51079
51213
 
51080
51214
  // src/mcp/tools/verify-audit-chain-tool.ts
51081
51215
  import * as fs8 from "node:fs/promises";
51082
- import * as path7 from "node:path";
51216
+ import * as path8 from "node:path";
51083
51217
  import { z as z111 } from "zod";
51084
51218
 
51085
51219
  // src/cli-server-auth.ts
@@ -51295,7 +51429,7 @@ async function loadAuditEvents(dir, logger60, filePrefix = DEFAULT_AUDIT_FILE_PR
51295
51429
  let skippedLines = 0;
51296
51430
  let unreadableFiles = 0;
51297
51431
  for (const filename of auditFiles) {
51298
- const fullPath = path7.join(dir, filename);
51432
+ const fullPath = path8.join(dir, filename);
51299
51433
  let content;
51300
51434
  try {
51301
51435
  content = await fs8.readFile(fullPath, "utf-8");
@@ -51319,7 +51453,7 @@ async function handler2(args, ctx) {
51319
51453
  message: `Validation error: ${formatZodError(parsed.error)}`
51320
51454
  });
51321
51455
  }
51322
- const resolvedDir = path7.resolve(parsed.data.logDir);
51456
+ const resolvedDir = path8.resolve(parsed.data.logDir);
51323
51457
  let dirStats;
51324
51458
  try {
51325
51459
  dirStats = await fs8.stat(resolvedDir);
@@ -51975,7 +52109,7 @@ function createMetaOrchestrator(options) {
51975
52109
  }
51976
52110
 
51977
52111
  // src/orchestration/meta-shadow-selector.ts
51978
- import { appendFileSync as appendFileSync5, existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
52112
+ import { appendFileSync as appendFileSync6, existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
51979
52113
  import { z as z113 } from "zod";
51980
52114
  var SHADOW_STRATEGY_ARMS = [
51981
52115
  "single-shot",
@@ -52091,7 +52225,7 @@ function persistMetaOutcome(strategy, decision, success) {
52091
52225
  };
52092
52226
  try {
52093
52227
  ensureLearningDir();
52094
- appendFileSync5(getMetaOutcomesFile(), JSON.stringify(record) + "\n", "utf-8");
52228
+ appendFileSync6(getMetaOutcomesFile(), JSON.stringify(record) + "\n", "utf-8");
52095
52229
  } catch (err2) {
52096
52230
  persistLogger.warn("Failed to persist meta-outcome (ignored)", {
52097
52231
  error: getErrorMessage(err2)
@@ -52472,12 +52606,12 @@ function evaluateMetaStrategyReadiness(result, config = DEFAULT_META_STRATEGY_RE
52472
52606
  // src/mcp/tools/dev-pipeline-tool.ts
52473
52607
  import { z as z114 } from "zod";
52474
52608
  import * as fs10 from "node:fs";
52475
- import * as path9 from "node:path";
52609
+ import * as path10 from "node:path";
52476
52610
  import { randomUUID as randomUUID15 } from "node:crypto";
52477
52611
 
52478
52612
  // src/pipeline/task-tracker.ts
52479
52613
  import * as fs9 from "node:fs";
52480
- import * as path8 from "node:path";
52614
+ import * as path9 from "node:path";
52481
52615
  var logger52 = createLogger({ component: "task-tracker" });
52482
52616
  async function exec(cmd, args, timeout = 15e3) {
52483
52617
  const { execFile: execFile2 } = await import("node:child_process");
@@ -52557,8 +52691,8 @@ var JsonTaskTracker = class {
52557
52691
  outputPath;
52558
52692
  constructor(config) {
52559
52693
  const dir = config.outputDir ?? nexusDataPath("pipeline");
52560
- this.outputPath = path8.resolve(dir, "tasks.json");
52561
- fs9.mkdirSync(path8.dirname(this.outputPath), { recursive: true });
52694
+ this.outputPath = path9.resolve(dir, "tasks.json");
52695
+ fs9.mkdirSync(path9.dirname(this.outputPath), { recursive: true });
52562
52696
  }
52563
52697
  async createTask(title, body) {
52564
52698
  const id = String(this.nextId++);
@@ -52710,9 +52844,9 @@ async function resolveTaskInput(input) {
52710
52844
  return input.task;
52711
52845
  }
52712
52846
  if (input.planFile !== void 0) {
52713
- const resolved = path9.resolve(input.planFile);
52714
- const cwdRoot = path9.resolve(".");
52715
- if (resolved !== cwdRoot && !resolved.startsWith(cwdRoot + path9.sep)) {
52847
+ const resolved = path10.resolve(input.planFile);
52848
+ const cwdRoot = path10.resolve(".");
52849
+ if (resolved !== cwdRoot && !resolved.startsWith(cwdRoot + path10.sep)) {
52716
52850
  throw new Error(`Path traversal denied: planFile must be within ${cwdRoot}`);
52717
52851
  }
52718
52852
  try {
@@ -52943,7 +53077,7 @@ function registerDevPipelineTool(server, deps) {
52943
53077
  // src/mcp/tools/pipeline-tool.ts
52944
53078
  import { z as z115 } from "zod";
52945
53079
  import * as fs11 from "node:fs";
52946
- import * as path10 from "node:path";
53080
+ import * as path11 from "node:path";
52947
53081
  import { randomUUID as randomUUID16 } from "node:crypto";
52948
53082
  var PIPELINE_ASYNC_HINT = "A full run_pipeline run can exceed the synchronous MCP request timeout. Retry with `dispatch: 'async'` to get a jobId immediately, then poll get_job_result({ jobId }) for the result.";
52949
53083
  var PipelineInputSchema = z115.object({
@@ -53006,9 +53140,9 @@ function buildOutput3(result, simulated) {
53006
53140
  }
53007
53141
  async function resolveTask(task, specFile) {
53008
53142
  if (specFile === void 0) return task;
53009
- const resolved = path10.resolve(specFile);
53010
- const cwdRoot = path10.resolve(".");
53011
- if (resolved !== cwdRoot && !resolved.startsWith(cwdRoot + path10.sep)) {
53143
+ const resolved = path11.resolve(specFile);
53144
+ const cwdRoot = path11.resolve(".");
53145
+ if (resolved !== cwdRoot && !resolved.startsWith(cwdRoot + path11.sep)) {
53012
53146
  throw new Error(`Path traversal denied: specFile must be within ${cwdRoot}`);
53013
53147
  }
53014
53148
  try {
@@ -56798,4 +56932,4 @@ export {
56798
56932
  startPipelineEventBridge,
56799
56933
  shutdownPipelineEventBridge
56800
56934
  };
56801
- //# sourceMappingURL=chunk-KQJHYWF5.js.map
56935
+ //# sourceMappingURL=chunk-GUEUV3ND.js.map