nexus-agents 3.14.0 → 3.14.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.
Files changed (28) hide show
  1. package/dist/{chunk-MHLXBATW.js → chunk-267ZPTK3.js} +173 -37
  2. package/dist/chunk-267ZPTK3.js.map +1 -0
  3. package/dist/{chunk-JTH44T6O.js → chunk-4FXWGQ6Q.js} +82 -235
  4. package/dist/chunk-4FXWGQ6Q.js.map +1 -0
  5. package/dist/{chunk-NF2LMA5A.js → chunk-HOOYCFLS.js} +3 -3
  6. package/dist/{chunk-AYLYRBS6.js → chunk-OQIOIE3P.js} +22 -10
  7. package/dist/{chunk-AYLYRBS6.js.map → chunk-OQIOIE3P.js.map} +1 -1
  8. package/dist/{chunk-WZGXBPIY.js → chunk-QW52WKQJ.js} +2 -2
  9. package/dist/{chunk-2LURPL6Q.js → chunk-TRJD7NPG.js} +2 -2
  10. package/dist/chunk-TRJD7NPG.js.map +1 -0
  11. package/dist/{chunk-AV4X4YH3.js → chunk-ZR2DDDJ3.js} +2 -2
  12. package/dist/cli.js +8 -8
  13. package/dist/{consensus-vote-NFXP65ET.js → consensus-vote-X5FAIP32.js} +3 -3
  14. package/dist/{improvement-review-AZHHCQ2Q.js → improvement-review-S2ZLDKO4.js} +3 -3
  15. package/dist/index.js +7 -7
  16. package/dist/{issue-triage-HCKBAQYA.js → issue-triage-NITM7SA7.js} +2 -2
  17. package/dist/{setup-command-NAXE7M4Q.js → setup-command-7FHNWHK4.js} +3 -3
  18. package/package.json +1 -1
  19. package/dist/chunk-2LURPL6Q.js.map +0 -1
  20. package/dist/chunk-JTH44T6O.js.map +0 -1
  21. package/dist/chunk-MHLXBATW.js.map +0 -1
  22. /package/dist/{chunk-NF2LMA5A.js.map → chunk-HOOYCFLS.js.map} +0 -0
  23. /package/dist/{chunk-WZGXBPIY.js.map → chunk-QW52WKQJ.js.map} +0 -0
  24. /package/dist/{chunk-AV4X4YH3.js.map → chunk-ZR2DDDJ3.js.map} +0 -0
  25. /package/dist/{consensus-vote-NFXP65ET.js.map → consensus-vote-X5FAIP32.js.map} +0 -0
  26. /package/dist/{improvement-review-AZHHCQ2Q.js.map → improvement-review-S2ZLDKO4.js.map} +0 -0
  27. /package/dist/{issue-triage-HCKBAQYA.js.map → issue-triage-NITM7SA7.js.map} +0 -0
  28. /package/dist/{setup-command-NAXE7M4Q.js.map → setup-command-7FHNWHK4.js.map} +0 -0
@@ -1,15 +1,13 @@
1
1
  import {
2
2
  REJECTION_CATEGORIES,
3
- UNATTRIBUTED_WORKSPACE,
4
3
  createSecureHandler,
5
4
  getToolAnnotations,
6
- getToolFitnessLedger,
7
5
  getToolTimeout,
8
6
  toSdkCallback,
9
7
  toolStructuredError,
10
8
  toolSuccessStructured,
11
9
  wrapToolWithTimeout
12
- } from "./chunk-JTH44T6O.js";
10
+ } from "./chunk-4FXWGQ6Q.js";
13
11
  import {
14
12
  JsonlStore,
15
13
  SECURITY_KEYWORDS,
@@ -31,7 +29,7 @@ import {
31
29
  import { execFile as execFile2 } from "child_process";
32
30
  import { readFile } from "fs/promises";
33
31
  import { promisify as promisify2 } from "util";
34
- import { z as z2 } from "zod";
32
+ import { z as z3 } from "zod";
35
33
 
36
34
  // src/mcp/middleware/tool-prerequisites.ts
37
35
  import { execFile } from "child_process";
@@ -895,6 +893,144 @@ function consolidationSignal(stat, family, busiestTool, busiestCount, windowLabe
895
893
  });
896
894
  }
897
895
 
896
+ // src/governance/tool-fitness-ledger.ts
897
+ import { z } from "zod";
898
+ var ToolFitnessEventSchema = z.object({
899
+ /** Schema version. Forward-compat for on-disk evolution. */
900
+ v: z.literal(1),
901
+ /** ISO-8601 timestamp of the invocation. */
902
+ ts: z.iso.datetime(),
903
+ /** Tool identifier (e.g. the MCP tool name). */
904
+ tool: z.string().min(1).max(100),
905
+ /** Whether the invocation succeeded. Drives the success/failure correlation. */
906
+ success: z.boolean(),
907
+ /**
908
+ * Cost of the invocation in arbitrary units (e.g. tokens), when the calling
909
+ * surface exposes it. Placeholder until full cost accounting lands with
910
+ * Epic G — absent (not zero) when unknown so aggregates don't conflate
911
+ * "free" with "unmeasured".
912
+ */
913
+ cost: z.number().nonnegative().optional(),
914
+ /**
915
+ * OPTIONAL workspace/repo dimension (#3852 concern 1 — context-poisoning).
916
+ * The ledger is homedir-global, so a tool that fails only in ONE workspace
917
+ * (local perms, missing deps, repo-specific config) would otherwise aggregate
918
+ * into a single global fitness number and could wrongly flag a tool that is
919
+ * healthy everywhere else. Recording the originating workspace lets the
920
+ * consumer scope/weight fitness so a one-workspace failure doesn't get
921
+ * globally penalized. BACKWARD-COMPATIBLE: optional, absent on legacy events
922
+ * (treated as the unattributed/global bucket by the consumer).
923
+ */
924
+ workspace: z.string().min(1).max(256).optional()
925
+ });
926
+ var UNATTRIBUTED_WORKSPACE = "(unattributed)";
927
+ var DEFAULT_MAX_EVENTS = 5e4;
928
+ var LEDGER_SUBDIR = "tool-fitness";
929
+ var LEDGER_FILE = "ledger.jsonl";
930
+ var ToolFitnessLedger = class {
931
+ store;
932
+ constructor(config) {
933
+ this.store = new JsonlStore({
934
+ filePath: config?.filePath ?? nexusDataPath(LEDGER_SUBDIR, LEDGER_FILE),
935
+ schema: ToolFitnessEventSchema,
936
+ maxRecords: config?.maxEvents ?? DEFAULT_MAX_EVENTS,
937
+ component: "ToolFitnessLedger"
938
+ });
939
+ }
940
+ /**
941
+ * Record one tool invocation. `v`/`ts` are stamped here so callers pass only
942
+ * the observed signal. Best-effort durability (inherited from JsonlStore):
943
+ * never throws into the observed operation.
944
+ */
945
+ record(event) {
946
+ const record = {
947
+ v: 1,
948
+ ts: event.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
949
+ tool: event.tool,
950
+ success: event.success,
951
+ ...event.cost !== void 0 ? { cost: event.cost } : {},
952
+ ...event.workspace !== void 0 ? { workspace: event.workspace } : {}
953
+ };
954
+ this.store.append(record);
955
+ }
956
+ /** Total retained events across all tools. */
957
+ size() {
958
+ return this.store.count();
959
+ }
960
+ /**
961
+ * Aggregate stats for a single tool, or `undefined` when the tool has no
962
+ * recorded events (unknown tool / empty ledger). Pure over the current
963
+ * event set — no I/O.
964
+ */
965
+ statFor(tool) {
966
+ const events = this.store.all().filter((e) => e.tool === tool);
967
+ if (events.length === 0) return void 0;
968
+ return aggregate(tool, events);
969
+ }
970
+ /**
971
+ * Aggregate stats for a single tool RESTRICTED to one workspace (#3852
972
+ * concern 1 — context-poisoning). Pass {@link UNATTRIBUTED_WORKSPACE} to
973
+ * select the legacy/global bucket of events that carried no `workspace`.
974
+ * Returns `undefined` when the tool has no events in that workspace.
975
+ *
976
+ * This is the primitive that lets the consumer answer "is this tool low-fitness
977
+ * EVERYWHERE, or only in one repo?" so a single-workspace failure can't
978
+ * globally mis-flag a healthy tool.
979
+ */
980
+ statForInWorkspace(tool, workspace) {
981
+ const events = this.store.all().filter((e) => e.tool === tool && (e.workspace ?? UNATTRIBUTED_WORKSPACE) === workspace);
982
+ if (events.length === 0) return void 0;
983
+ return aggregate(tool, events);
984
+ }
985
+ /**
986
+ * Aggregate stats for every tool with at least one event, sorted by
987
+ * descending invocation count (most-used first) then tool name for a stable
988
+ * order. Returns `[]` for an empty ledger.
989
+ */
990
+ report() {
991
+ const byTool = /* @__PURE__ */ new Map();
992
+ for (const event of this.store.all()) {
993
+ const bucket = byTool.get(event.tool);
994
+ if (bucket === void 0) byTool.set(event.tool, [event]);
995
+ else bucket.push(event);
996
+ }
997
+ const stats = [];
998
+ for (const [tool, events] of byTool) {
999
+ stats.push(aggregate(tool, events));
1000
+ }
1001
+ stats.sort((a, b) => b.invocationCount - a.invocationCount || a.tool.localeCompare(b.tool));
1002
+ return stats;
1003
+ }
1004
+ };
1005
+ function aggregate(tool, events) {
1006
+ let successCount = 0;
1007
+ let lastUsedAt = "";
1008
+ let totalCost;
1009
+ const workspaces = /* @__PURE__ */ new Set();
1010
+ for (const event of events) {
1011
+ if (event.success) successCount++;
1012
+ if (lastUsedAt === "" || Date.parse(event.ts) >= Date.parse(lastUsedAt)) lastUsedAt = event.ts;
1013
+ if (event.cost !== void 0) totalCost = (totalCost ?? 0) + event.cost;
1014
+ workspaces.add(event.workspace ?? UNATTRIBUTED_WORKSPACE);
1015
+ }
1016
+ const invocationCount = events.length;
1017
+ return {
1018
+ tool,
1019
+ invocationCount,
1020
+ successCount,
1021
+ failureCount: invocationCount - successCount,
1022
+ successRate: successCount / invocationCount,
1023
+ lastUsedAt,
1024
+ totalCost,
1025
+ workspaces: [...workspaces].sort((a, b) => a.localeCompare(b))
1026
+ };
1027
+ }
1028
+ var singleton;
1029
+ function getToolFitnessLedger() {
1030
+ singleton ??= new ToolFitnessLedger();
1031
+ return singleton;
1032
+ }
1033
+
898
1034
  // src/mcp/tools/improvement-review-tool-fitness.ts
899
1035
  function isHealthyInAnyOtherWorkspace(stat, statInWorkspace) {
900
1036
  const realWorkspaces = stat.workspaces.filter((w) => w !== UNATTRIBUTED_WORKSPACE);
@@ -1125,7 +1261,7 @@ function improvementSignalsToTasks(signals, existingTaskIds) {
1125
1261
  }
1126
1262
 
1127
1263
  // src/mcp/tools/improvement-remediation-shadow.ts
1128
- import { z } from "zod";
1264
+ import { z as z2 } from "zod";
1129
1265
 
1130
1266
  // src/mcp/tools/diff-secret-scan.ts
1131
1267
  var SECRET_PATTERNS = [
@@ -1210,30 +1346,30 @@ function recordRemediationShadow(signals, sink = getRemediationShadowSink()) {
1210
1346
  for (const record of records) sink.record(record);
1211
1347
  return records;
1212
1348
  }
1213
- var SoakVoteOutcomeSchema = z.object({
1214
- approved: z.boolean(),
1349
+ var SoakVoteOutcomeSchema = z2.object({
1350
+ approved: z2.boolean(),
1215
1351
  /** Approval percentage at vote time, 0–100 (the consensus tally). */
1216
- approvalPercentage: z.number()
1352
+ approvalPercentage: z2.number()
1217
1353
  });
1218
- var RemediationSoakRecordSchema = z.object({
1354
+ var RemediationSoakRecordSchema = z2.object({
1219
1355
  /** ISO-8601 capture time. */
1220
- timestamp: z.string(),
1356
+ timestamp: z2.string(),
1221
1357
  /** The improvement signal's stable key. */
1222
- signalKey: z.string(),
1358
+ signalKey: z2.string(),
1223
1359
  /** The signal's category. */
1224
- category: z.string(),
1360
+ category: z2.string(),
1225
1361
  /** The classified remediation priority (p0–p4). */
1226
- priority: z.string(),
1362
+ priority: z2.string(),
1227
1363
  /** The signal's declared severity. */
1228
- severity: z.string(),
1364
+ severity: z2.string(),
1229
1365
  /** Vote outcome (approved/rejected + tally), undefined if the signal never reached a vote. */
1230
1366
  voteOutcome: SoakVoteOutcomeSchema.optional(),
1231
1367
  /** Number of plan steps research produced (0 if research/plan failed). */
1232
- planStepCount: z.number(),
1368
+ planStepCount: z2.number(),
1233
1369
  /** p0 dry-run result detail (scrubbed); undefined for non-p0 or when no dry-run ran. */
1234
- dryRunResult: z.string().optional(),
1370
+ dryRunResult: z2.string().optional(),
1235
1371
  /** Human-readable verdict reason (scrubbed). */
1236
- reason: z.string()
1372
+ reason: z2.string()
1237
1373
  });
1238
1374
  var SOAK_MAX_RECORDS = 1e4;
1239
1375
  function getRemediationSoakFile() {
@@ -1421,14 +1557,14 @@ function classifySignalPriority(signal) {
1421
1557
 
1422
1558
  // src/mcp/tools/improvement-review.ts
1423
1559
  var execFileAsync2 = promisify2(execFile2);
1424
- var ImprovementReviewInputSchema = z2.object({
1425
- lookbackDays: z2.number().int().min(1).max(90).optional().default(7).describe("Lookback window for outcome data, in days. Default 7."),
1426
- fileIssues: z2.boolean().optional().default(false).describe(
1560
+ var ImprovementReviewInputSchema = z3.object({
1561
+ lookbackDays: z3.number().int().min(1).max(90).optional().default(7).describe("Lookback window for outcome data, in days. Default 7."),
1562
+ fileIssues: z3.boolean().optional().default(false).describe(
1427
1563
  "When true, file candidate issues via `gh issue create` for crossed thresholds (rate-limited to 5 per run, deduped against open issues). When false (default), return signals only."
1428
1564
  ),
1429
- minSampleSize: z2.number().int().min(1).max(1e3).optional().default(5).describe("Minimum sample size before a CLI/category signal can fire."),
1430
- fitnessFloor: z2.number().int().min(0).max(100).optional().default(90).describe("Fitness score below this threshold triggers a tech-debt signal."),
1431
- selfEvalReportPath: z2.string().optional().describe(
1565
+ minSampleSize: z3.number().int().min(1).max(1e3).optional().default(5).describe("Minimum sample size before a CLI/category signal can fire."),
1566
+ fitnessFloor: z3.number().int().min(0).max(100).optional().default(90).describe("Fitness score below this threshold triggers a tech-debt signal."),
1567
+ selfEvalReportPath: z3.string().optional().describe(
1432
1568
  "Optional path to a self-eval JSON report (from `self-eval --json`). When set, high-confidence unanimous deprecate/refactor findings are surfaced as tech-debt signals through the same deduped/rate-limited issue path (#3224). Unreadable/malformed reports are skipped (no signal). Absent \u2192 no self-eval signals."
1433
1569
  )
1434
1570
  });
@@ -1707,14 +1843,14 @@ function detectFitnessSignals(audit, fitnessFloor) {
1707
1843
  return signals;
1708
1844
  }
1709
1845
  var SELF_EVAL_CONFIDENCE_FLOOR = 0.8;
1710
- var SelfEvalReportSchema = z2.object({
1711
- results: z2.array(
1712
- z2.object({
1713
- component: z2.string(),
1714
- finalRecommendation: z2.string(),
1715
- confidence: z2.number(),
1716
- dissent: z2.array(z2.unknown()).optional().default([]),
1717
- evidenceQuality: z2.number().optional()
1846
+ var SelfEvalReportSchema = z3.object({
1847
+ results: z3.array(
1848
+ z3.object({
1849
+ component: z3.string(),
1850
+ finalRecommendation: z3.string(),
1851
+ confidence: z3.number(),
1852
+ dissent: z3.array(z3.unknown()).optional().default([]),
1853
+ evidenceQuality: z3.number().optional()
1718
1854
  })
1719
1855
  )
1720
1856
  });
@@ -1937,13 +2073,13 @@ async function reviewHandler(args, ctx) {
1937
2073
  }
1938
2074
  var description = "Periodic threshold-gated observability-driven improvement loop. Reads OutcomeStore + fitness audit, surfaces patterns crossing documented thresholds as candidate findings. When fileIssues=true, files candidate GitHub issues via `gh issue create` (rate-limited to 5 per run, deduped against open issues). Never auto-merges. Replaces the deleted self-development engine (#2402).";
1939
2075
  var TOOL_INPUT_SCHEMA = {
1940
- lookbackDays: z2.number().int().min(1).max(90).optional().describe("Lookback window for outcome data, in days. Default 7."),
1941
- fileIssues: z2.boolean().optional().describe(
2076
+ lookbackDays: z3.number().int().min(1).max(90).optional().describe("Lookback window for outcome data, in days. Default 7."),
2077
+ fileIssues: z3.boolean().optional().describe(
1942
2078
  "When true, file candidate issues for crossed thresholds (default false \u2014 return signals only)"
1943
2079
  ),
1944
- minSampleSize: z2.number().int().min(1).max(1e3).optional().describe("Minimum sample size before a CLI/category signal fires (default 5)."),
1945
- fitnessFloor: z2.number().int().min(0).max(100).optional().describe("Fitness score below this threshold triggers a tech-debt signal (default 90)."),
1946
- selfEvalReportPath: z2.string().optional().describe(
2080
+ minSampleSize: z3.number().int().min(1).max(1e3).optional().describe("Minimum sample size before a CLI/category signal fires (default 5)."),
2081
+ fitnessFloor: z3.number().int().min(0).max(100).optional().describe("Fitness score below this threshold triggers a tech-debt signal (default 90)."),
2082
+ selfEvalReportPath: z3.string().optional().describe(
1947
2083
  "Optional path to a self-eval JSON report. High-confidence unanimous deprecate/refactor findings surface as tech-debt signals (#3224)."
1948
2084
  )
1949
2085
  };
@@ -1996,4 +2132,4 @@ export {
1996
2132
  runImprovementReview,
1997
2133
  registerImprovementReviewTool
1998
2134
  };
1999
- //# sourceMappingURL=chunk-MHLXBATW.js.map
2135
+ //# sourceMappingURL=chunk-267ZPTK3.js.map