nexus-agents 2.94.0 → 2.96.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.
- package/README.md +4 -4
- package/dist/{child-mcp-config-PPFHJZSO.js → child-mcp-config-HZUEFKXB.js} +2 -2
- package/dist/{chunk-77JUKFTG.js → chunk-23NNSR45.js} +7 -7
- package/dist/{chunk-G5EBHQ2L.js → chunk-2472O6HR.js} +2 -2
- package/dist/{chunk-OT6V5ROA.js → chunk-2KHD2ANY.js} +39 -4
- package/dist/chunk-2KHD2ANY.js.map +1 -0
- package/dist/chunk-3ZROPKLV.js +2133 -0
- package/dist/chunk-3ZROPKLV.js.map +1 -0
- package/dist/{chunk-UC3PVIN4.js → chunk-7P3UIYJH.js} +415 -1410
- package/dist/chunk-7P3UIYJH.js.map +1 -0
- package/dist/{chunk-Q3NUA7CW.js → chunk-AJMHTC4J.js} +3 -3
- package/dist/{chunk-63OGXDHS.js → chunk-ANH2A44T.js} +4 -4
- package/dist/{chunk-XBPFVWA2.js → chunk-AREIPUCD.js} +2 -2
- package/dist/{chunk-L7I55FAN.js → chunk-AW3HJHJT.js} +2 -2
- package/dist/{chunk-ROAANOOB.js → chunk-DTFM37IG.js} +2 -2
- package/dist/{chunk-24WC4XO2.js → chunk-EUUWRNDZ.js} +2 -2
- package/dist/{chunk-EDPDH5YT.js → chunk-FHZUOIVC.js} +3 -3
- package/dist/{chunk-R2NKJPUX.js → chunk-FPAUF3YB.js} +2 -2
- package/dist/{chunk-ZY2EMD72.js → chunk-ICOHLRHT.js} +5 -5
- package/dist/{chunk-ADRTAUJA.js → chunk-INFJH4JX.js} +4 -4
- package/dist/{chunk-FUGPKYEJ.js → chunk-JEK5CHK2.js} +2 -2
- package/dist/{chunk-43UDRX3N.js → chunk-MMW4KIBH.js} +2 -2
- package/dist/{chunk-EJR6U67L.js → chunk-MVCJEIUU.js} +2 -2
- package/dist/{chunk-2YMRRZRM.js → chunk-NJN5SGU2.js} +7 -7
- package/dist/{chunk-OSSGG7KW.js → chunk-OHGKVMT3.js} +2 -2
- package/dist/{chunk-76535ZNP.js → chunk-ONIHS7K7.js} +4 -4
- package/dist/{chunk-OKGHHCS5.js → chunk-P7Y3KLKG.js} +3 -3
- package/dist/{chunk-EB43BGAR.js → chunk-R2KJ22LX.js} +2 -2
- package/dist/chunk-SRSAMAPP.js +1095 -0
- package/dist/chunk-SRSAMAPP.js.map +1 -0
- package/dist/{chunk-YDXGYKZJ.js → chunk-SSHL5TAX.js} +2 -2
- package/dist/{chunk-5ZB3MTA7.js → chunk-THVTU2UG.js} +3 -3
- package/dist/{chunk-4QESFYGV.js → chunk-XMYJWMWN.js} +4 -4
- package/dist/{chunk-5B4VLSMP.js → chunk-YSYBVLYS.js} +3 -3
- package/dist/{chunk-GMZDRTVJ.js → chunk-YWIZ3UQ6.js} +2 -2
- package/dist/{chunk-LCLVSNST.js → chunk-Z3XF5LRT.js} +2 -2
- package/dist/{chunk-TNBWHOPY.js → chunk-Z7PILY4E.js} +361 -2468
- package/dist/chunk-Z7PILY4E.js.map +1 -0
- package/dist/{chunk-KSJPDQSG.js → chunk-ZBQXPP7M.js} +2 -2
- package/dist/{cli-circuit-breaker-BAERQZ3H.js → cli-circuit-breaker-L6SWUVOP.js} +4 -4
- package/dist/cli.js +179 -76
- package/dist/cli.js.map +1 -1
- package/dist/{composite-router-EONKFRDD.js → composite-router-HFBOUCXD.js} +2 -2
- package/dist/{consensus-vote-TO5575P3.js → consensus-vote-YEZIP2JQ.js} +13 -12
- package/dist/{context-retriever-JM564MOV.js → context-retriever-PTRDEBBH.js} +6 -6
- package/dist/{doctor-deep-VFBQ7TY4.js → doctor-deep-WHCDAV3Z.js} +3 -3
- package/dist/{expert-bridge-OXME7FES.js → expert-bridge-N4L47MHU.js} +4 -4
- package/dist/{factory-FVZLVIYH.js → factory-EFLOKY32.js} +5 -5
- package/dist/{factory-MDT5CFA5.js → factory-ZKGYCGDT.js} +5 -5
- package/dist/improvement-review-4MTIYCOZ.js +27 -0
- package/dist/index.js +52 -49
- package/dist/index.js.map +1 -1
- package/dist/{init-opencode-Y6XKR6WS.js → init-opencode-42GKRQDA.js} +5 -5
- package/dist/{issue-triage-2XVD4JQ7.js → issue-triage-TEPE6VMA.js} +5 -5
- package/dist/{pr-reviewer-helpers-JLWEQIN6.js → pr-reviewer-helpers-G4O22XF6.js} +4 -4
- package/dist/{registry-command-FYVOMCBG.js → registry-command-32PUZNNG.js} +2 -2
- package/dist/{repo-security-plan-JRDHWI2R.js → repo-security-plan-XWUDQB34.js} +3 -3
- package/dist/{research-helpers-synthesize-F7L7M7FJ.js → research-helpers-synthesize-YI6S7SN6.js} +3 -3
- package/dist/{routing-memory-JAV2ICXR.js → routing-memory-Z3ZCTPFM.js} +2 -2
- package/dist/{session-memory-2XAGY3PX.js → session-memory-5ZZ2SXDE.js} +3 -3
- package/dist/{setup-command-6WVEOE4Q.js → setup-command-U3ILRY5H.js} +11 -11
- package/dist/{setup-config-KUZU2GFI.js → setup-config-WZYLXPDO.js} +3 -3
- package/dist/{setup-custom-api-3M7CU3TF.js → setup-custom-api-K4EN47CP.js} +4 -4
- package/dist/{tool-memory-YWX4FTIQ.js → tool-memory-53Y2TUR3.js} +5 -5
- package/dist/{weather-report-ZZM5EU7N.js → weather-report-JOBWXWQ3.js} +2 -2
- package/dist/weather-report-JOBWXWQ3.js.map +1 -0
- package/package.json +12 -2
- package/dist/chunk-OT6V5ROA.js.map +0 -1
- package/dist/chunk-TNBWHOPY.js.map +0 -1
- package/dist/chunk-UC3PVIN4.js.map +0 -1
- /package/dist/{child-mcp-config-PPFHJZSO.js.map → child-mcp-config-HZUEFKXB.js.map} +0 -0
- /package/dist/{chunk-77JUKFTG.js.map → chunk-23NNSR45.js.map} +0 -0
- /package/dist/{chunk-G5EBHQ2L.js.map → chunk-2472O6HR.js.map} +0 -0
- /package/dist/{chunk-Q3NUA7CW.js.map → chunk-AJMHTC4J.js.map} +0 -0
- /package/dist/{chunk-63OGXDHS.js.map → chunk-ANH2A44T.js.map} +0 -0
- /package/dist/{chunk-XBPFVWA2.js.map → chunk-AREIPUCD.js.map} +0 -0
- /package/dist/{chunk-L7I55FAN.js.map → chunk-AW3HJHJT.js.map} +0 -0
- /package/dist/{chunk-ROAANOOB.js.map → chunk-DTFM37IG.js.map} +0 -0
- /package/dist/{chunk-24WC4XO2.js.map → chunk-EUUWRNDZ.js.map} +0 -0
- /package/dist/{chunk-EDPDH5YT.js.map → chunk-FHZUOIVC.js.map} +0 -0
- /package/dist/{chunk-R2NKJPUX.js.map → chunk-FPAUF3YB.js.map} +0 -0
- /package/dist/{chunk-ZY2EMD72.js.map → chunk-ICOHLRHT.js.map} +0 -0
- /package/dist/{chunk-ADRTAUJA.js.map → chunk-INFJH4JX.js.map} +0 -0
- /package/dist/{chunk-FUGPKYEJ.js.map → chunk-JEK5CHK2.js.map} +0 -0
- /package/dist/{chunk-43UDRX3N.js.map → chunk-MMW4KIBH.js.map} +0 -0
- /package/dist/{chunk-EJR6U67L.js.map → chunk-MVCJEIUU.js.map} +0 -0
- /package/dist/{chunk-2YMRRZRM.js.map → chunk-NJN5SGU2.js.map} +0 -0
- /package/dist/{chunk-OSSGG7KW.js.map → chunk-OHGKVMT3.js.map} +0 -0
- /package/dist/{chunk-76535ZNP.js.map → chunk-ONIHS7K7.js.map} +0 -0
- /package/dist/{chunk-OKGHHCS5.js.map → chunk-P7Y3KLKG.js.map} +0 -0
- /package/dist/{chunk-EB43BGAR.js.map → chunk-R2KJ22LX.js.map} +0 -0
- /package/dist/{chunk-YDXGYKZJ.js.map → chunk-SSHL5TAX.js.map} +0 -0
- /package/dist/{chunk-5ZB3MTA7.js.map → chunk-THVTU2UG.js.map} +0 -0
- /package/dist/{chunk-4QESFYGV.js.map → chunk-XMYJWMWN.js.map} +0 -0
- /package/dist/{chunk-5B4VLSMP.js.map → chunk-YSYBVLYS.js.map} +0 -0
- /package/dist/{chunk-GMZDRTVJ.js.map → chunk-YWIZ3UQ6.js.map} +0 -0
- /package/dist/{chunk-LCLVSNST.js.map → chunk-Z3XF5LRT.js.map} +0 -0
- /package/dist/{chunk-KSJPDQSG.js.map → chunk-ZBQXPP7M.js.map} +0 -0
- /package/dist/{cli-circuit-breaker-BAERQZ3H.js.map → cli-circuit-breaker-L6SWUVOP.js.map} +0 -0
- /package/dist/{composite-router-EONKFRDD.js.map → composite-router-HFBOUCXD.js.map} +0 -0
- /package/dist/{consensus-vote-TO5575P3.js.map → consensus-vote-YEZIP2JQ.js.map} +0 -0
- /package/dist/{context-retriever-JM564MOV.js.map → context-retriever-PTRDEBBH.js.map} +0 -0
- /package/dist/{doctor-deep-VFBQ7TY4.js.map → doctor-deep-WHCDAV3Z.js.map} +0 -0
- /package/dist/{expert-bridge-OXME7FES.js.map → expert-bridge-N4L47MHU.js.map} +0 -0
- /package/dist/{factory-FVZLVIYH.js.map → factory-EFLOKY32.js.map} +0 -0
- /package/dist/{factory-MDT5CFA5.js.map → factory-ZKGYCGDT.js.map} +0 -0
- /package/dist/{issue-triage-2XVD4JQ7.js.map → improvement-review-4MTIYCOZ.js.map} +0 -0
- /package/dist/{init-opencode-Y6XKR6WS.js.map → init-opencode-42GKRQDA.js.map} +0 -0
- /package/dist/{pr-reviewer-helpers-JLWEQIN6.js.map → issue-triage-TEPE6VMA.js.map} +0 -0
- /package/dist/{repo-security-plan-JRDHWI2R.js.map → pr-reviewer-helpers-G4O22XF6.js.map} +0 -0
- /package/dist/{registry-command-FYVOMCBG.js.map → registry-command-32PUZNNG.js.map} +0 -0
- /package/dist/{research-helpers-synthesize-F7L7M7FJ.js.map → repo-security-plan-XWUDQB34.js.map} +0 -0
- /package/dist/{routing-memory-JAV2ICXR.js.map → research-helpers-synthesize-YI6S7SN6.js.map} +0 -0
- /package/dist/{session-memory-2XAGY3PX.js.map → routing-memory-Z3ZCTPFM.js.map} +0 -0
- /package/dist/{setup-command-6WVEOE4Q.js.map → session-memory-5ZZ2SXDE.js.map} +0 -0
- /package/dist/{setup-config-KUZU2GFI.js.map → setup-command-U3ILRY5H.js.map} +0 -0
- /package/dist/{tool-memory-YWX4FTIQ.js.map → setup-config-WZYLXPDO.js.map} +0 -0
- /package/dist/{setup-custom-api-3M7CU3TF.js.map → setup-custom-api-K4EN47CP.js.map} +0 -0
- /package/dist/{weather-report-ZZM5EU7N.js.map → tool-memory-53Y2TUR3.js.map} +0 -0
|
@@ -4,1820 +4,90 @@ import {
|
|
|
4
4
|
createStream,
|
|
5
5
|
requireApiKey,
|
|
6
6
|
validateApiKeyPresence
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AW3HJHJT.js";
|
|
8
8
|
import {
|
|
9
9
|
CUSTOM_API_BASE_URL_ENV,
|
|
10
10
|
PROVIDER_ENV_KEYS,
|
|
11
11
|
validateCustomApiBaseUrl
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-SSHL5TAX.js";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} from "./chunk-
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
89
|
-
var internalLogger = createLogger({ component: "mcp-notifier" });
|
|
90
|
-
function createMcpNotifier(server) {
|
|
91
|
-
function send(level, logger11, data) {
|
|
92
|
-
try {
|
|
93
|
-
server.sendLoggingMessage({ level, logger: logger11, data }).catch((error) => {
|
|
94
|
-
internalLogger.debug("Failed to send MCP notification", {
|
|
95
|
-
level,
|
|
96
|
-
logger: logger11,
|
|
97
|
-
error: getErrorMessage(error)
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
} catch (error) {
|
|
101
|
-
internalLogger.debug("Failed to send MCP notification", {
|
|
102
|
-
level,
|
|
103
|
-
logger: logger11,
|
|
104
|
-
error: getErrorMessage(error)
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
info: (logger11, data) => {
|
|
110
|
-
send("info", logger11, data);
|
|
111
|
-
},
|
|
112
|
-
debug: (logger11, data) => {
|
|
113
|
-
send("debug", logger11, data);
|
|
114
|
-
},
|
|
115
|
-
warn: (logger11, data) => {
|
|
116
|
-
send("warning", logger11, data);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
var NOOP_NOTIFIER = {
|
|
121
|
-
info: () => void 0,
|
|
122
|
-
debug: () => void 0,
|
|
123
|
-
warn: () => void 0
|
|
124
|
-
};
|
|
125
|
-
var progressContextStorage = new AsyncLocalStorage();
|
|
126
|
-
var abortSignalStorage = new AsyncLocalStorage();
|
|
127
|
-
async function withProgressHeartbeat(toolName, notifier, operation, intervalMs = 15e3) {
|
|
128
|
-
const startTime = Date.now();
|
|
129
|
-
let beatCount = 0;
|
|
130
|
-
const progressCtx = progressContextStorage.getStore();
|
|
131
|
-
const timer = setInterval(() => {
|
|
132
|
-
beatCount++;
|
|
133
|
-
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
134
|
-
if (progressCtx !== void 0) {
|
|
135
|
-
progressCtx.sendNotification(beatCount);
|
|
136
|
-
}
|
|
137
|
-
notifier.debug(toolName, {
|
|
138
|
-
event: "heartbeat",
|
|
139
|
-
elapsedSeconds: elapsed,
|
|
140
|
-
beatCount,
|
|
141
|
-
hasProgressToken: progressCtx !== void 0
|
|
142
|
-
});
|
|
143
|
-
}, intervalMs);
|
|
144
|
-
try {
|
|
145
|
-
return await operation();
|
|
146
|
-
} finally {
|
|
147
|
-
clearInterval(timer);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// src/mcp/middleware/tool-wrapper.ts
|
|
152
|
-
import { randomUUID } from "crypto";
|
|
153
|
-
import { appendFileSync, existsSync, mkdirSync } from "fs";
|
|
154
|
-
import { dirname, join } from "path";
|
|
155
|
-
|
|
156
|
-
// src/mcp/middleware/rate-limiter.ts
|
|
157
|
-
var DEFAULT_REFILL_INTERVAL_MS = CACHE_TIMEOUTS.rateLimitRefillMs;
|
|
158
|
-
var RateLimiter = class {
|
|
159
|
-
tokens;
|
|
160
|
-
capacity;
|
|
161
|
-
refillRate;
|
|
162
|
-
refillIntervalMs;
|
|
163
|
-
lastRefillTime;
|
|
164
|
-
logger;
|
|
165
|
-
name;
|
|
166
|
-
constructor(config) {
|
|
167
|
-
this.capacity = config.capacity;
|
|
168
|
-
this.refillRate = config.refillRate;
|
|
169
|
-
this.refillIntervalMs = config.refillIntervalMs ?? DEFAULT_REFILL_INTERVAL_MS;
|
|
170
|
-
this.tokens = this.capacity;
|
|
171
|
-
this.lastRefillTime = getTimeProvider().now();
|
|
172
|
-
this.name = config.name ?? "rate-limiter";
|
|
173
|
-
this.logger = config.logger ?? createLogger({ component: this.name });
|
|
174
|
-
this.logger.debug("Rate limiter initialized", {
|
|
175
|
-
capacity: this.capacity,
|
|
176
|
-
refillRate: this.refillRate,
|
|
177
|
-
refillIntervalMs: this.refillIntervalMs
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Refills tokens based on elapsed time.
|
|
182
|
-
* Called automatically before each acquire attempt.
|
|
183
|
-
*/
|
|
184
|
-
refill() {
|
|
185
|
-
const now = getTimeProvider().now();
|
|
186
|
-
const elapsed = now - this.lastRefillTime;
|
|
187
|
-
const intervals = Math.floor(elapsed / this.refillIntervalMs);
|
|
188
|
-
if (intervals > 0) {
|
|
189
|
-
const tokensToAdd = intervals * this.refillRate;
|
|
190
|
-
this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
|
|
191
|
-
this.lastRefillTime = now - elapsed % this.refillIntervalMs;
|
|
192
|
-
if (tokensToAdd > 0) {
|
|
193
|
-
this.logger.debug("Tokens refilled", {
|
|
194
|
-
added: tokensToAdd,
|
|
195
|
-
current: this.tokens
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Attempts to acquire a token.
|
|
202
|
-
*
|
|
203
|
-
* @param count - Number of tokens to acquire (default: 1)
|
|
204
|
-
* @returns True if tokens were acquired, false if rate limited
|
|
205
|
-
*/
|
|
206
|
-
tryAcquire(count = 1) {
|
|
207
|
-
this.refill();
|
|
208
|
-
if (this.tokens >= count) {
|
|
209
|
-
this.tokens -= count;
|
|
210
|
-
this.logger.debug("Token acquired", {
|
|
211
|
-
requested: count,
|
|
212
|
-
remaining: this.tokens
|
|
213
|
-
});
|
|
214
|
-
return true;
|
|
215
|
-
}
|
|
216
|
-
this.logger.warn("Rate limit exceeded", {
|
|
217
|
-
requested: count,
|
|
218
|
-
available: this.tokens
|
|
219
|
-
});
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Gets the current state of the rate limiter.
|
|
224
|
-
*
|
|
225
|
-
* @returns The current rate limiter state
|
|
226
|
-
*/
|
|
227
|
-
getState() {
|
|
228
|
-
this.refill();
|
|
229
|
-
const nextTokenMs = this.tokens > 0 ? 0 : this.refillIntervalMs - (getTimeProvider().now() - this.lastRefillTime);
|
|
230
|
-
return {
|
|
231
|
-
tokens: this.tokens,
|
|
232
|
-
capacity: this.capacity,
|
|
233
|
-
nextTokenMs: Math.max(0, nextTokenMs)
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Resets the rate limiter to full capacity.
|
|
238
|
-
* Useful for testing or after configuration changes.
|
|
239
|
-
*/
|
|
240
|
-
reset() {
|
|
241
|
-
this.tokens = this.capacity;
|
|
242
|
-
this.lastRefillTime = getTimeProvider().now();
|
|
243
|
-
this.logger.debug("Rate limiter reset", { tokens: this.tokens });
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
function createDefaultRateLimiter(name, logger11) {
|
|
247
|
-
const config = {
|
|
248
|
-
capacity: 100,
|
|
249
|
-
refillRate: 10,
|
|
250
|
-
refillIntervalMs: 1e3
|
|
251
|
-
};
|
|
252
|
-
if (name !== void 0) {
|
|
253
|
-
config.name = name;
|
|
254
|
-
}
|
|
255
|
-
if (logger11 !== void 0) {
|
|
256
|
-
config.logger = logger11;
|
|
257
|
-
}
|
|
258
|
-
return new RateLimiter(config);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/mcp/middleware/validation.ts
|
|
262
|
-
function validateToolInput(schema, args) {
|
|
263
|
-
const parsed = schema.safeParse(args);
|
|
264
|
-
if (parsed.success) {
|
|
265
|
-
return ok(parsed.data);
|
|
266
|
-
}
|
|
267
|
-
const message = formatZodError(parsed.error);
|
|
268
|
-
const validationError = new ValidationError(`Invalid tool input: ${message}`, {
|
|
269
|
-
context: {
|
|
270
|
-
issues: parsed.error.issues,
|
|
271
|
-
receivedType: typeof args
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
return err(validationError);
|
|
275
|
-
}
|
|
276
|
-
function createValidator(schema) {
|
|
277
|
-
return (args) => validateToolInput(schema, args);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// src/mcp/middleware/policy-types.ts
|
|
281
|
-
import { z } from "zod";
|
|
282
|
-
var PolicyError = class extends SecurityError {
|
|
283
|
-
decision;
|
|
284
|
-
constructor(message, decision) {
|
|
285
|
-
super(message, {
|
|
286
|
-
context: {
|
|
287
|
-
allowed: decision.allowed,
|
|
288
|
-
reason: decision.reason,
|
|
289
|
-
ruleName: decision.ruleName,
|
|
290
|
-
requiredArtifact: decision.requiredArtifact
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
this.name = "PolicyError";
|
|
294
|
-
this.decision = decision;
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
var PolicyConfigSchema = z.object({
|
|
298
|
-
defaultMode: z.enum(["read-only", "read-write"]).default("read-only"),
|
|
299
|
-
policyMode: z.enum(["enforce", "warn"]).default("enforce"),
|
|
300
|
-
allowedPaths: z.array(z.string()).default(["./"])
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
// src/mcp/middleware/policy-helpers.ts
|
|
304
|
-
function isPathSafe(targetPath, allowedPaths) {
|
|
305
|
-
const normalizedTarget = normalizePath(targetPath);
|
|
306
|
-
for (const allowed of allowedPaths) {
|
|
307
|
-
const normalizedAllowed = normalizePath(allowed);
|
|
308
|
-
if (normalizedTarget.startsWith(normalizedAllowed)) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
function normalizePath(p) {
|
|
315
|
-
let normalized = p.replace(/\/{1,100}$/, "");
|
|
316
|
-
if (normalized === ".") {
|
|
317
|
-
normalized = "";
|
|
318
|
-
} else if (normalized.startsWith("./")) {
|
|
319
|
-
normalized = normalized.slice(2);
|
|
320
|
-
}
|
|
321
|
-
if (!normalized.startsWith("/")) {
|
|
322
|
-
normalized = "/" + normalized;
|
|
323
|
-
}
|
|
324
|
-
return normalized;
|
|
325
|
-
}
|
|
326
|
-
function extractPathFromArgs(args) {
|
|
327
|
-
if (args === null || typeof args !== "object") {
|
|
328
|
-
return void 0;
|
|
329
|
-
}
|
|
330
|
-
const argsObj = args;
|
|
331
|
-
const pathFields = ["path", "filePath", "file_path", "directory", "dir", "target"];
|
|
332
|
-
for (const field of pathFields) {
|
|
333
|
-
const value = argsObj[field];
|
|
334
|
-
if (typeof value === "string") {
|
|
335
|
-
return value;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return void 0;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// src/mcp/middleware/policy-rules.ts
|
|
342
|
-
var MUTATION_TOOLS = /* @__PURE__ */ new Set([
|
|
343
|
-
"write_file",
|
|
344
|
-
"edit_file",
|
|
345
|
-
"delete_file",
|
|
346
|
-
"create_directory",
|
|
347
|
-
"remove_directory",
|
|
348
|
-
"execute_command",
|
|
349
|
-
"run_shell",
|
|
350
|
-
"bash",
|
|
351
|
-
"create_expert",
|
|
352
|
-
"run_workflow"
|
|
353
|
-
]);
|
|
354
|
-
var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
355
|
-
"read_file",
|
|
356
|
-
"list_directory",
|
|
357
|
-
"search_files",
|
|
358
|
-
"get_status",
|
|
359
|
-
"orchestrate",
|
|
360
|
-
"delegate_to_model"
|
|
361
|
-
]);
|
|
362
|
-
function isMutationTool(toolName) {
|
|
363
|
-
if (MUTATION_TOOLS.has(toolName)) {
|
|
364
|
-
return true;
|
|
365
|
-
}
|
|
366
|
-
if (READ_ONLY_TOOLS.has(toolName)) {
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
return true;
|
|
370
|
-
}
|
|
371
|
-
var denyMutationsWithoutModeRule = {
|
|
372
|
-
name: "deny-mutations-without-mode",
|
|
373
|
-
description: "Blocks write operations unless mode is read-write",
|
|
374
|
-
check(ctx) {
|
|
375
|
-
if (ctx.mode === "read-write") {
|
|
376
|
-
return { allowed: true, reason: "Read-write mode enabled" };
|
|
377
|
-
}
|
|
378
|
-
if (isMutationTool(ctx.toolName)) {
|
|
379
|
-
return {
|
|
380
|
-
allowed: false,
|
|
381
|
-
reason: `Tool '${ctx.toolName}' is a mutation operation but mode is '${ctx.mode}'. Set mode to 'read-write' to enable.`
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
return { allowed: true, reason: "Read-only operation allowed" };
|
|
385
|
-
}
|
|
386
|
-
};
|
|
387
|
-
var safePathsRule = {
|
|
388
|
-
name: "safe-paths",
|
|
389
|
-
description: "Validates paths against allowed root directories",
|
|
390
|
-
check(ctx) {
|
|
391
|
-
const targetPath = extractPathFromArgs(ctx.args);
|
|
392
|
-
if (targetPath === void 0) {
|
|
393
|
-
return { allowed: true, reason: "No path argument found" };
|
|
394
|
-
}
|
|
395
|
-
if (targetPath.includes("..")) {
|
|
396
|
-
return {
|
|
397
|
-
allowed: false,
|
|
398
|
-
reason: `Path contains '..' which may indicate path traversal: ${targetPath}`
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
const allowedPaths = ctx.allowedPaths ?? ["./"];
|
|
402
|
-
if (!isPathSafe(targetPath, allowedPaths)) {
|
|
403
|
-
return {
|
|
404
|
-
allowed: false,
|
|
405
|
-
reason: `Path '${targetPath}' is outside allowed directories: ${allowedPaths.join(", ")}`
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
return { allowed: true, reason: "Path is within allowed directories" };
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
// src/mcp/middleware/policy.ts
|
|
413
|
-
var PolicyFirewall = class {
|
|
414
|
-
rules = [];
|
|
415
|
-
mode;
|
|
416
|
-
logger;
|
|
417
|
-
constructor(config) {
|
|
418
|
-
this.mode = config?.mode ?? "enforce";
|
|
419
|
-
this.logger = config?.logger ?? createLogger({ component: "policy-firewall" });
|
|
420
|
-
if (config?.rules) {
|
|
421
|
-
for (const rule of config.rules) {
|
|
422
|
-
this.rules.push(rule);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
this.logger.debug("Policy firewall initialized", {
|
|
426
|
-
mode: this.mode,
|
|
427
|
-
ruleCount: this.rules.length
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Evaluates all policy rules against the given context.
|
|
432
|
-
*
|
|
433
|
-
* Rules are evaluated in order. The first rule that denies stops
|
|
434
|
-
* evaluation and returns the denial decision.
|
|
435
|
-
*
|
|
436
|
-
* @param ctx - The policy context to evaluate
|
|
437
|
-
* @returns The policy decision
|
|
438
|
-
*/
|
|
439
|
-
evaluate(ctx) {
|
|
440
|
-
this.logger.debug("Evaluating policy", {
|
|
441
|
-
toolName: ctx.toolName,
|
|
442
|
-
mode: ctx.mode,
|
|
443
|
-
ruleCount: this.rules.length
|
|
444
|
-
});
|
|
445
|
-
if (this.rules.length === 0) {
|
|
446
|
-
return this.allowWithReason(ctx, "No policy rules configured");
|
|
447
|
-
}
|
|
448
|
-
for (const rule of this.rules) {
|
|
449
|
-
const decision = rule.check(ctx);
|
|
450
|
-
if (!decision.allowed) {
|
|
451
|
-
return this.handleDenial(ctx, rule, decision);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
return this.allowWithReason(ctx, "All policy rules passed");
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Creates an allow decision with the given reason and logs it.
|
|
458
|
-
*/
|
|
459
|
-
allowWithReason(ctx, reason) {
|
|
460
|
-
const decision = { allowed: true, reason };
|
|
461
|
-
this.logDecision(ctx, decision);
|
|
462
|
-
return decision;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Handles a rule denial, respecting warn mode if configured.
|
|
466
|
-
*/
|
|
467
|
-
handleDenial(ctx, rule, decision) {
|
|
468
|
-
const denialDecision = {
|
|
469
|
-
...decision,
|
|
470
|
-
ruleName: rule.name
|
|
471
|
-
};
|
|
472
|
-
this.logDecision(ctx, denialDecision);
|
|
473
|
-
if (this.mode === "warn") {
|
|
474
|
-
this.logger.warn("Policy denial overridden by warn mode", {
|
|
475
|
-
toolName: ctx.toolName,
|
|
476
|
-
ruleName: rule.name,
|
|
477
|
-
reason: decision.reason
|
|
478
|
-
});
|
|
479
|
-
return {
|
|
480
|
-
allowed: true,
|
|
481
|
-
reason: `[WARN MODE] Would be denied: ${decision.reason}`,
|
|
482
|
-
ruleName: rule.name
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
return denialDecision;
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* Adds a policy rule to the firewall.
|
|
489
|
-
*
|
|
490
|
-
* @param rule - The rule to add
|
|
491
|
-
*/
|
|
492
|
-
addRule(rule) {
|
|
493
|
-
const existingIndex = this.rules.findIndex((r) => r.name === rule.name);
|
|
494
|
-
if (existingIndex >= 0) {
|
|
495
|
-
this.logger.warn("Replacing existing policy rule", { ruleName: rule.name });
|
|
496
|
-
this.rules[existingIndex] = rule;
|
|
497
|
-
} else {
|
|
498
|
-
this.rules.push(rule);
|
|
499
|
-
this.logger.debug("Policy rule added", { ruleName: rule.name });
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* Removes a policy rule by name.
|
|
504
|
-
*
|
|
505
|
-
* @param name - The name of the rule to remove
|
|
506
|
-
* @returns True if the rule was found and removed
|
|
507
|
-
*/
|
|
508
|
-
removeRule(name) {
|
|
509
|
-
const index = this.rules.findIndex((r) => r.name === name);
|
|
510
|
-
if (index >= 0) {
|
|
511
|
-
this.rules.splice(index, 1);
|
|
512
|
-
this.logger.debug("Policy rule removed", { ruleName: name });
|
|
513
|
-
return true;
|
|
514
|
-
}
|
|
515
|
-
return false;
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Gets all registered policy rules.
|
|
519
|
-
*
|
|
520
|
-
* @returns A readonly array of policy rules
|
|
521
|
-
*/
|
|
522
|
-
getRules() {
|
|
523
|
-
return [...this.rules];
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Sets the policy enforcement mode.
|
|
527
|
-
*
|
|
528
|
-
* @param mode - The new enforcement mode
|
|
529
|
-
*/
|
|
530
|
-
setMode(mode) {
|
|
531
|
-
const previousMode = this.mode;
|
|
532
|
-
this.mode = mode;
|
|
533
|
-
this.logger.info("Policy mode changed", { from: previousMode, to: mode });
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Gets the current policy enforcement mode.
|
|
537
|
-
*
|
|
538
|
-
* @returns The current mode
|
|
539
|
-
*/
|
|
540
|
-
getMode() {
|
|
541
|
-
return this.mode;
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Logs a policy decision for audit purposes.
|
|
545
|
-
*/
|
|
546
|
-
logDecision(ctx, decision) {
|
|
547
|
-
const logData = {
|
|
548
|
-
toolName: ctx.toolName,
|
|
549
|
-
mode: ctx.mode,
|
|
550
|
-
workflowId: ctx.workflowId,
|
|
551
|
-
allowed: decision.allowed,
|
|
552
|
-
reason: decision.reason,
|
|
553
|
-
ruleName: decision.ruleName
|
|
554
|
-
};
|
|
555
|
-
if (decision.allowed) {
|
|
556
|
-
this.logger.debug("Policy decision: ALLOWED", logData);
|
|
557
|
-
} else {
|
|
558
|
-
this.logger.warn("Policy decision: DENIED", logData);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
function createDefaultPolicyFirewall(config) {
|
|
563
|
-
const firewall = new PolicyFirewall(config);
|
|
564
|
-
firewall.addRule(denyMutationsWithoutModeRule);
|
|
565
|
-
firewall.addRule(safePathsRule);
|
|
566
|
-
return firewall;
|
|
567
|
-
}
|
|
568
|
-
function evaluatePolicy(firewall, ctx) {
|
|
569
|
-
const decision = firewall.evaluate(ctx);
|
|
570
|
-
if (decision.allowed) {
|
|
571
|
-
return ok(void 0);
|
|
572
|
-
}
|
|
573
|
-
return err(new PolicyError(`Policy denied: ${decision.reason}`, decision));
|
|
574
|
-
}
|
|
575
|
-
function createPolicyContext(toolName, args, options) {
|
|
576
|
-
const base = {
|
|
577
|
-
toolName,
|
|
578
|
-
args,
|
|
579
|
-
mode: options?.mode ?? "read-only"
|
|
580
|
-
};
|
|
581
|
-
const result = { ...base };
|
|
582
|
-
if (options?.artifacts !== void 0) {
|
|
583
|
-
result["artifacts"] = options.artifacts;
|
|
584
|
-
}
|
|
585
|
-
if (options?.workflowId !== void 0) {
|
|
586
|
-
result["workflowId"] = options.workflowId;
|
|
587
|
-
}
|
|
588
|
-
if (options?.allowedPaths !== void 0) {
|
|
589
|
-
result["allowedPaths"] = options.allowedPaths;
|
|
590
|
-
}
|
|
591
|
-
return result;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// src/mcp/middleware/timeout-guard.ts
|
|
595
|
-
var DEFAULT_TIMEOUT_MS = TIMEOUT_GUARD.defaultMs;
|
|
596
|
-
var MAX_TIMEOUT_MS = TIMEOUT_GUARD.maxMs;
|
|
597
|
-
var NEAR_TIMEOUT_THRESHOLD = TIMEOUT_GUARD.nearTimeoutThreshold;
|
|
598
|
-
function createTimeoutError(operationName, timeoutMs) {
|
|
599
|
-
return {
|
|
600
|
-
code: "OPERATION_TIMEOUT",
|
|
601
|
-
message: `Operation '${operationName}' timed out after ${String(timeoutMs)}ms`,
|
|
602
|
-
operation: operationName,
|
|
603
|
-
timeoutMs
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
function createGuardError(error, operationName) {
|
|
607
|
-
const guardError = {
|
|
608
|
-
code: "GUARD_ERROR",
|
|
609
|
-
message: getErrorMessage(error),
|
|
610
|
-
operation: operationName
|
|
611
|
-
};
|
|
612
|
-
if (error instanceof Error) {
|
|
613
|
-
return { ...guardError, cause: error };
|
|
614
|
-
}
|
|
615
|
-
return guardError;
|
|
616
|
-
}
|
|
617
|
-
var TimeoutGuard = class {
|
|
618
|
-
defaultTimeoutMs;
|
|
619
|
-
maxTimeoutMs;
|
|
620
|
-
enableLogging;
|
|
621
|
-
logger;
|
|
622
|
-
constructor(config) {
|
|
623
|
-
this.defaultTimeoutMs = config?.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
624
|
-
this.maxTimeoutMs = config?.maxTimeoutMs ?? MAX_TIMEOUT_MS;
|
|
625
|
-
this.enableLogging = config?.enableLogging ?? true;
|
|
626
|
-
this.logger = config?.logger ?? createLogger({ component: "timeout-guard" });
|
|
627
|
-
}
|
|
628
|
-
/** Resolves effective timeout and operation name from options. */
|
|
629
|
-
resolveOptions(options) {
|
|
630
|
-
return {
|
|
631
|
-
timeoutMs: Math.min(options?.timeoutMs ?? this.defaultTimeoutMs, this.maxTimeoutMs),
|
|
632
|
-
operationName: options?.operationName ?? "unknown"
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* Executes an async operation with timeout protection.
|
|
637
|
-
*/
|
|
638
|
-
async execute(operation, options) {
|
|
639
|
-
const { timeoutMs, operationName } = this.resolveOptions(options);
|
|
640
|
-
const validationError = this.validateTimeout(timeoutMs, operationName);
|
|
641
|
-
if (validationError !== null) {
|
|
642
|
-
return err(validationError);
|
|
643
|
-
}
|
|
644
|
-
return this.runGuarded(operation, timeoutMs, operationName, options);
|
|
645
|
-
}
|
|
646
|
-
/** Runs the operation with timeout and optional abort signal. */
|
|
647
|
-
async runGuarded(operation, timeoutMs, operationName, options) {
|
|
648
|
-
this.logStart(operationName, timeoutMs);
|
|
649
|
-
const startTime = getTimeProvider().now();
|
|
650
|
-
const state = { timeoutId: void 0, timedOut: false };
|
|
651
|
-
try {
|
|
652
|
-
const result = await this.runWithTimeout(
|
|
653
|
-
operation,
|
|
654
|
-
timeoutMs,
|
|
655
|
-
state,
|
|
656
|
-
options?.onTimeout,
|
|
657
|
-
options?.signal
|
|
658
|
-
);
|
|
659
|
-
return this.handleSuccess(result, startTime, timeoutMs, operationName);
|
|
660
|
-
} catch {
|
|
661
|
-
const cancelled = options?.signal?.aborted === true && !state.timedOut;
|
|
662
|
-
return err(
|
|
663
|
-
this.handleFailure(state.timedOut, operationName, timeoutMs, startTime, cancelled)
|
|
664
|
-
);
|
|
665
|
-
} finally {
|
|
666
|
-
if (state.timeoutId !== void 0) {
|
|
667
|
-
clearTimeout(state.timeoutId);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
validateTimeout(timeoutMs, operationName) {
|
|
672
|
-
if (timeoutMs <= 0) {
|
|
673
|
-
return {
|
|
674
|
-
code: "INVALID_TIMEOUT",
|
|
675
|
-
message: `Invalid timeout: ${String(timeoutMs)}ms`,
|
|
676
|
-
operation: operationName
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
return null;
|
|
680
|
-
}
|
|
681
|
-
logStart(operationName, timeoutMs) {
|
|
682
|
-
if (this.enableLogging) {
|
|
683
|
-
this.logger.debug("Starting guarded operation", { operation: operationName, timeoutMs });
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
async runWithTimeout(operation, timeoutMs, state, onTimeout, signal) {
|
|
687
|
-
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
688
|
-
state.timeoutId = setTimeout(() => {
|
|
689
|
-
state.timedOut = true;
|
|
690
|
-
onTimeout?.();
|
|
691
|
-
reject(new Error(`Operation timed out after ${String(timeoutMs)}ms`));
|
|
692
|
-
}, timeoutMs);
|
|
693
|
-
});
|
|
694
|
-
const promises = [operation(), timeoutPromise];
|
|
695
|
-
if (signal !== void 0 && !signal.aborted) {
|
|
696
|
-
const abortPromise = new Promise((_resolve, reject) => {
|
|
697
|
-
signal.addEventListener(
|
|
698
|
-
"abort",
|
|
699
|
-
() => {
|
|
700
|
-
reject(new Error("Operation cancelled by client"));
|
|
701
|
-
},
|
|
702
|
-
{ once: true }
|
|
703
|
-
);
|
|
704
|
-
});
|
|
705
|
-
promises.push(abortPromise);
|
|
706
|
-
}
|
|
707
|
-
return Promise.race(promises);
|
|
708
|
-
}
|
|
709
|
-
handleSuccess(result, startTime, timeoutMs, operationName) {
|
|
710
|
-
const durationMs = getTimeProvider().now() - startTime;
|
|
711
|
-
const nearTimeout = durationMs > timeoutMs * NEAR_TIMEOUT_THRESHOLD;
|
|
712
|
-
if (nearTimeout && this.enableLogging) {
|
|
713
|
-
this.logger.warn("Operation completed near timeout threshold", {
|
|
714
|
-
operation: operationName,
|
|
715
|
-
durationMs,
|
|
716
|
-
timeoutMs,
|
|
717
|
-
thresholdPercent: Math.round(durationMs / timeoutMs * 100)
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
if (this.enableLogging) {
|
|
721
|
-
this.logger.debug("Guarded operation completed", { operation: operationName, durationMs });
|
|
722
|
-
}
|
|
723
|
-
return ok({ value: result, durationMs, nearTimeout });
|
|
724
|
-
}
|
|
725
|
-
handleFailure(timedOut, operationName, timeoutMs, startTime, cancelled = false) {
|
|
726
|
-
const durationMs = getTimeProvider().now() - startTime;
|
|
727
|
-
if (cancelled) {
|
|
728
|
-
this.logger.info("Operation cancelled by client", {
|
|
729
|
-
operation: operationName,
|
|
730
|
-
durationMs
|
|
731
|
-
});
|
|
732
|
-
return {
|
|
733
|
-
code: "OPERATION_CANCELLED",
|
|
734
|
-
message: `Operation '${operationName}' cancelled by client`,
|
|
735
|
-
operation: operationName
|
|
736
|
-
};
|
|
737
|
-
}
|
|
738
|
-
if (timedOut) {
|
|
739
|
-
this.logger.error("Operation timed out", void 0, {
|
|
740
|
-
operation: operationName,
|
|
741
|
-
timeoutMs,
|
|
742
|
-
durationMs
|
|
743
|
-
});
|
|
744
|
-
return createTimeoutError(operationName, timeoutMs);
|
|
745
|
-
}
|
|
746
|
-
return createGuardError(new Error("Unknown error"), operationName);
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Creates a guarded version of an async function.
|
|
750
|
-
*/
|
|
751
|
-
guard(fn, options) {
|
|
752
|
-
return async (...args) => {
|
|
753
|
-
return this.execute(() => fn(...args), options);
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
// src/mcp/middleware/request-context.ts
|
|
759
|
-
import { randomBytes } from "crypto";
|
|
760
|
-
function generateRequestId() {
|
|
761
|
-
const bytes = randomBytes(8);
|
|
762
|
-
return `req_${bytes.toString("hex")}`;
|
|
763
|
-
}
|
|
764
|
-
function formatTimestamp() {
|
|
765
|
-
const now = new Date(getTimeProvider().now());
|
|
766
|
-
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
767
|
-
timeZone: "America/New_York",
|
|
768
|
-
timeZoneName: "shortOffset"
|
|
769
|
-
});
|
|
770
|
-
const parts = formatter.formatToParts(now);
|
|
771
|
-
const offset = parts.find((p) => p.type === "timeZoneName")?.value ?? "-05:00";
|
|
772
|
-
const base = now.toLocaleString("sv-SE", {
|
|
773
|
-
timeZone: "America/New_York",
|
|
774
|
-
hour12: false
|
|
775
|
-
});
|
|
776
|
-
return base.replace(" ", "T") + offset.replace("GMT", "");
|
|
777
|
-
}
|
|
778
|
-
function deriveTrustTier(caller) {
|
|
779
|
-
const knownClients = ["claude-cli", "gemini-cli", "codex-cli"];
|
|
780
|
-
if (caller.transport === "stdio") return "1";
|
|
781
|
-
if (caller.authenticated === true) {
|
|
782
|
-
if (caller.clientId !== void 0 && knownClients.includes(caller.clientId)) {
|
|
783
|
-
return "1";
|
|
784
|
-
}
|
|
785
|
-
return "2";
|
|
786
|
-
}
|
|
787
|
-
return "3";
|
|
788
|
-
}
|
|
789
|
-
function createRequestContext(options) {
|
|
790
|
-
const caller = options.caller ?? {};
|
|
791
|
-
const context = {
|
|
792
|
-
requestId: generateRequestId(),
|
|
793
|
-
timestamp: formatTimestamp(),
|
|
794
|
-
toolName: options.toolName,
|
|
795
|
-
caller,
|
|
796
|
-
trustTier: options.trustTier ?? deriveTrustTier(caller),
|
|
797
|
-
...options.traceId !== void 0 && { traceId: options.traceId },
|
|
798
|
-
...options.parentSpanId !== void 0 && { parentSpanId: options.parentSpanId }
|
|
799
|
-
};
|
|
800
|
-
return Object.freeze(context);
|
|
801
|
-
}
|
|
802
|
-
function contextForLogging(ctx) {
|
|
803
|
-
return {
|
|
804
|
-
requestId: ctx.requestId,
|
|
805
|
-
toolName: ctx.toolName,
|
|
806
|
-
trustTier: ctx.trustTier,
|
|
807
|
-
...ctx.caller.clientId !== void 0 && { clientId: ctx.caller.clientId },
|
|
808
|
-
...ctx.traceId !== void 0 && { traceId: ctx.traceId }
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
// src/security/access-constraint-deriver/denylist.ts
|
|
813
|
-
var UNBYPASSABLE_PATH_PATTERNS = [
|
|
814
|
-
// Environment files
|
|
815
|
-
".env",
|
|
816
|
-
".env.*",
|
|
817
|
-
"**/.env",
|
|
818
|
-
"**/.env.*",
|
|
819
|
-
// SSH credentials
|
|
820
|
-
"~/.ssh/**",
|
|
821
|
-
"**/ssh/id_*",
|
|
822
|
-
"**/*_rsa",
|
|
823
|
-
"**/*_ed25519",
|
|
824
|
-
"**/*.pem",
|
|
825
|
-
// Cloud credentials
|
|
826
|
-
"~/.aws/**",
|
|
827
|
-
"~/.azure/**",
|
|
828
|
-
"~/.gcp/**",
|
|
829
|
-
"~/.config/gcloud/**",
|
|
830
|
-
"~/.kube/config",
|
|
831
|
-
// Unix secret files
|
|
832
|
-
"/etc/shadow",
|
|
833
|
-
"/etc/sudoers",
|
|
834
|
-
"/etc/sudoers.d/**",
|
|
835
|
-
// Common secret file patterns
|
|
836
|
-
"**/secrets.*",
|
|
837
|
-
"**/credentials.*",
|
|
838
|
-
"**/private_key.*",
|
|
839
|
-
"**/id_rsa*"
|
|
840
|
-
];
|
|
841
|
-
var UNBYPASSABLE_TOOL_NAMES = [
|
|
842
|
-
// Destructive git operations
|
|
843
|
-
"git_push_force",
|
|
844
|
-
"git_reset_hard",
|
|
845
|
-
"git_branch_delete_force",
|
|
846
|
-
"git_clean_force",
|
|
847
|
-
// Destructive filesystem
|
|
848
|
-
"rm_recursive_force",
|
|
849
|
-
"chmod_recursive",
|
|
850
|
-
// Identity / auth mutations
|
|
851
|
-
"ssh_add_key",
|
|
852
|
-
"gpg_add_key",
|
|
853
|
-
"npm_publish_force",
|
|
854
|
-
// Remote destruction
|
|
855
|
-
"github_repo_delete",
|
|
856
|
-
"github_org_transfer",
|
|
857
|
-
"aws_account_close"
|
|
858
|
-
];
|
|
859
|
-
function compileGlobToRegex(pattern) {
|
|
860
|
-
const pat = pattern.toLowerCase();
|
|
861
|
-
const escaped = pat.replace(/[\\.+^$()|[\]{}]/g, "\\$&").replace(/\*\*/g, "__DOUBLESTAR__").replace(/\*/g, "[^/]*").replace(/__DOUBLESTAR__/g, ".*");
|
|
862
|
-
const anchored = escaped.startsWith("~/") ? `(^|/)${escaped.slice(2)}$` : escaped.startsWith("/") ? `^${escaped}$` : `(^|/)${escaped}$`;
|
|
863
|
-
return new RegExp(anchored);
|
|
864
|
-
}
|
|
865
|
-
var COMPILED_PATH_PATTERNS = UNBYPASSABLE_PATH_PATTERNS.map((pattern) => ({
|
|
866
|
-
pattern,
|
|
867
|
-
regex: compileGlobToRegex(pattern)
|
|
868
|
-
}));
|
|
869
|
-
function isPathDenied(path) {
|
|
870
|
-
const normalized = path.toLowerCase();
|
|
871
|
-
return COMPILED_PATH_PATTERNS.some((c) => c.regex.test(normalized));
|
|
872
|
-
}
|
|
873
|
-
function isToolDenied(toolName) {
|
|
874
|
-
return UNBYPASSABLE_TOOL_NAMES.includes(toolName);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
// src/security/access-constraint-deriver/tool-risk.ts
|
|
878
|
-
var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
|
|
879
|
-
// Discovery / listing
|
|
880
|
-
"list_experts",
|
|
881
|
-
"list_workflows",
|
|
882
|
-
// Research reads
|
|
883
|
-
"research_query",
|
|
884
|
-
"research_analyze",
|
|
885
|
-
"research_catalog_review",
|
|
886
|
-
"research_synthesize",
|
|
887
|
-
// Memory reads
|
|
888
|
-
"memory_query",
|
|
889
|
-
"memory_stats",
|
|
890
|
-
// Observability
|
|
891
|
-
"weather_report",
|
|
892
|
-
"query_trace",
|
|
893
|
-
"query_task_state",
|
|
894
|
-
// Async-mode result polling (#3042 / epic #2631)
|
|
895
|
-
"get_job_result",
|
|
896
|
-
// Async-mode job discovery (#3046 / epic #2631 Stage 5)
|
|
897
|
-
"list_jobs",
|
|
898
|
-
// Codebase intelligence (read-only over local files)
|
|
899
|
-
"search_codebase",
|
|
900
|
-
"extract_symbols",
|
|
901
|
-
// Repo analysis (read-only)
|
|
902
|
-
"repo_analyze",
|
|
903
|
-
"repo_security_plan",
|
|
904
|
-
// Routing recommendation (no side effects — returns recommendation)
|
|
905
|
-
"delegate_to_model",
|
|
906
|
-
// Registry import (returns a draft template — does not write)
|
|
907
|
-
"registry_import"
|
|
908
|
-
]);
|
|
909
|
-
function isRiskyTool(toolName) {
|
|
910
|
-
return !READ_ONLY_TOOLS2.has(toolName);
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
// src/security/access-constraint-deriver/enforcer.ts
|
|
914
|
-
function checkAccess(toolName, policy, args) {
|
|
915
|
-
if (isToolDenied(toolName)) {
|
|
916
|
-
return {
|
|
917
|
-
decision: "deny",
|
|
918
|
-
reason: `tool "${toolName}" is on the unbypassable deny-tool list`,
|
|
919
|
-
matchedRule: "unbypassable:tool"
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
if (typeof args?.path === "string" && args.path.length > 0 && isPathDenied(args.path)) {
|
|
923
|
-
return {
|
|
924
|
-
decision: "deny",
|
|
925
|
-
reason: `path "${args.path}" is on the unbypassable deny-path list`,
|
|
926
|
-
matchedRule: "unbypassable:path"
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
if (policy.allowedTools === "*") return { decision: "allow" };
|
|
930
|
-
if (policy.allowedTools.includes(toolName)) return { decision: "allow" };
|
|
931
|
-
return decideOnViolation(toolName, policy.mode);
|
|
932
|
-
}
|
|
933
|
-
function decideOnViolation(toolName, mode) {
|
|
934
|
-
if (mode === "audit") {
|
|
935
|
-
return {
|
|
936
|
-
decision: "log-and-allow",
|
|
937
|
-
warning: `tool "${toolName}" not in derived policy (audit mode)`
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
if (mode === "confirm_risky") {
|
|
941
|
-
if (!isRiskyTool(toolName)) {
|
|
942
|
-
return {
|
|
943
|
-
decision: "log-and-allow",
|
|
944
|
-
warning: `tool "${toolName}" not in derived policy (confirm_risky mode, read-only \u2014 would have required human approval, allowed because read-only)`
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
return {
|
|
948
|
-
decision: "deny",
|
|
949
|
-
reason: `tool "${toolName}" not in derived policy (confirm_risky mode, risky \u2014 would have required human approval; denied for now, add to allowedTools or run in audit mode to allow)`,
|
|
950
|
-
matchedRule: "allowedTools:confirm_risky"
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
return {
|
|
954
|
-
decision: "deny",
|
|
955
|
-
reason: `tool "${toolName}" not in derived policy`,
|
|
956
|
-
matchedRule: "allowedTools"
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// src/security/access-constraint-deriver/mcp-guard.ts
|
|
961
|
-
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
962
|
-
var accessPolicyStorage = new AsyncLocalStorage2();
|
|
963
|
-
function withAccessPolicy(policy, fn) {
|
|
964
|
-
return accessPolicyStorage.run(policy, fn);
|
|
965
|
-
}
|
|
966
|
-
function getActivePolicy() {
|
|
967
|
-
return accessPolicyStorage.getStore();
|
|
968
|
-
}
|
|
969
|
-
function denyToToolResult(decision, requestId) {
|
|
970
|
-
return {
|
|
971
|
-
isError: true,
|
|
972
|
-
content: [
|
|
973
|
-
{
|
|
974
|
-
type: "text",
|
|
975
|
-
text: `access denied: ${decision.reason} (rule: ${decision.matchedRule}, request: ${requestId})`
|
|
976
|
-
}
|
|
977
|
-
]
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// src/security/access-constraint-deriver/chain-adapter.ts
|
|
982
|
-
function toGuardArgs(args) {
|
|
983
|
-
if (typeof args !== "object" || args === null) return void 0;
|
|
984
|
-
const path = args["path"];
|
|
985
|
-
return typeof path === "string" && path.length > 0 ? { path } : void 0;
|
|
986
|
-
}
|
|
987
|
-
function createAccessPolicyChainMiddleware(toolName) {
|
|
988
|
-
return async (args, ctx, next) => {
|
|
989
|
-
const policy = getActivePolicy();
|
|
990
|
-
if (policy === void 0 || policy.mode === "off") {
|
|
991
|
-
return next(args, ctx);
|
|
992
|
-
}
|
|
993
|
-
const decision = checkAccess(toolName, policy, toGuardArgs(args));
|
|
994
|
-
if (decision.decision === "allow") {
|
|
995
|
-
return next(args, ctx);
|
|
996
|
-
}
|
|
997
|
-
if (decision.decision === "log-and-allow") {
|
|
998
|
-
ctx.logger.warn("access-policy: audit violation", {
|
|
999
|
-
tool: toolName,
|
|
1000
|
-
warning: decision.warning,
|
|
1001
|
-
policySource: policy.source,
|
|
1002
|
-
requestId: ctx.requestContext.requestId
|
|
1003
|
-
});
|
|
1004
|
-
return next(args, ctx);
|
|
1005
|
-
}
|
|
1006
|
-
ctx.logger.info("access-policy: tool call denied", {
|
|
1007
|
-
tool: toolName,
|
|
1008
|
-
reason: decision.reason,
|
|
1009
|
-
matchedRule: decision.matchedRule,
|
|
1010
|
-
policySource: policy.source,
|
|
1011
|
-
mode: policy.mode,
|
|
1012
|
-
requestId: ctx.requestContext.requestId
|
|
1013
|
-
});
|
|
1014
|
-
return denyToToolResult(decision, ctx.requestContext.requestId);
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// src/mcp/error-envelope.ts
|
|
1019
|
-
import { z as z2 } from "zod";
|
|
1020
|
-
var ErrorCategorySchema = z2.enum([
|
|
1021
|
-
"transient",
|
|
1022
|
-
"validation",
|
|
1023
|
-
"permission",
|
|
1024
|
-
"business",
|
|
1025
|
-
"internal"
|
|
1026
|
-
]);
|
|
1027
|
-
var ToolErrorEnvelopeSchema = z2.object({
|
|
1028
|
-
errorCategory: ErrorCategorySchema,
|
|
1029
|
-
/** Whether retrying the same call could succeed without caller changes. */
|
|
1030
|
-
isRetryable: z2.boolean(),
|
|
1031
|
-
/** Human-readable summary. Bounded to keep stack traces out of results. */
|
|
1032
|
-
message: z2.string().min(1).max(2e3),
|
|
1033
|
-
/**
|
|
1034
|
-
* Optional structured context. MUST NOT carry secrets, credentials,
|
|
1035
|
-
* absolute filesystem paths, or raw Error/response objects — those are
|
|
1036
|
-
* an information-disclosure risk (this field is not output-sanitized).
|
|
1037
|
-
*/
|
|
1038
|
-
detail: z2.record(z2.string(), z2.unknown()).optional()
|
|
1039
|
-
});
|
|
1040
|
-
var ERROR_ENVELOPE_META_KEY = "nexus-agents/error";
|
|
1041
|
-
function defaultRetryable(category) {
|
|
1042
|
-
return category === "transient";
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
// src/mcp/tools/tool-result.ts
|
|
1046
|
-
function toolSuccess(text) {
|
|
1047
|
-
return {
|
|
1048
|
-
content: [{ type: "text", text }]
|
|
1049
|
-
};
|
|
1050
|
-
}
|
|
1051
|
-
function toolSuccessStructured(data) {
|
|
1052
|
-
return {
|
|
1053
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
1054
|
-
structuredContent: data
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
function toolStructuredError(input) {
|
|
1058
|
-
const envelope = {
|
|
1059
|
-
errorCategory: input.errorCategory,
|
|
1060
|
-
isRetryable: input.isRetryable ?? defaultRetryable(input.errorCategory),
|
|
1061
|
-
message: input.message,
|
|
1062
|
-
...input.detail !== void 0 ? { detail: input.detail } : {}
|
|
1063
|
-
};
|
|
1064
|
-
return {
|
|
1065
|
-
isError: true,
|
|
1066
|
-
content: [{ type: "text", text: envelope.message }],
|
|
1067
|
-
_meta: { [ERROR_ENVELOPE_META_KEY]: envelope }
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
function toolError(message) {
|
|
1071
|
-
return toolStructuredError({ errorCategory: "internal", message });
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
// src/mcp/middleware/middleware-chain.ts
|
|
1075
|
-
function errorResult(category, message, requestId) {
|
|
1076
|
-
return toolStructuredError({
|
|
1077
|
-
errorCategory: category,
|
|
1078
|
-
message: `${message} (request: ${requestId})`
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
function createValidationMiddleware(schema) {
|
|
1082
|
-
return async (args, ctx, next) => {
|
|
1083
|
-
const result = validateToolInput(schema, args);
|
|
1084
|
-
if (!result.ok) {
|
|
1085
|
-
ctx.logger.warn("Validation failed", {
|
|
1086
|
-
error: result.error.message
|
|
1087
|
-
});
|
|
1088
|
-
return errorResult(
|
|
1089
|
-
"validation",
|
|
1090
|
-
`Validation error: ${result.error.message}`,
|
|
1091
|
-
ctx.requestContext.requestId
|
|
1092
|
-
);
|
|
1093
|
-
}
|
|
1094
|
-
ctx.validatedArgs = result.value;
|
|
1095
|
-
return next(result.value, ctx);
|
|
1096
|
-
};
|
|
1097
|
-
}
|
|
1098
|
-
function createPolicyMiddleware(firewall, toolName, mode, allowedPaths) {
|
|
1099
|
-
return async (args, ctx, next) => {
|
|
1100
|
-
const policyCtx = createPolicyContext(toolName, args, {
|
|
1101
|
-
mode,
|
|
1102
|
-
...allowedPaths !== void 0 && { allowedPaths }
|
|
1103
|
-
});
|
|
1104
|
-
const decision = firewall.evaluate(policyCtx);
|
|
1105
|
-
if (!decision.allowed) {
|
|
1106
|
-
ctx.logger.warn("Policy denied", {
|
|
1107
|
-
reason: decision.reason,
|
|
1108
|
-
ruleName: decision.ruleName
|
|
1109
|
-
});
|
|
1110
|
-
return errorResult(
|
|
1111
|
-
"permission",
|
|
1112
|
-
`Policy denied: ${decision.reason}`,
|
|
1113
|
-
ctx.requestContext.requestId
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
ctx.logger.debug("Policy check passed", { reason: decision.reason });
|
|
1117
|
-
return next(args, ctx);
|
|
1118
|
-
};
|
|
1119
|
-
}
|
|
1120
|
-
function createRateLimitMiddleware(limiter) {
|
|
1121
|
-
return async (args, ctx, next) => {
|
|
1122
|
-
const acquired = limiter.tryAcquire();
|
|
1123
|
-
if (!acquired) {
|
|
1124
|
-
const state = limiter.getState();
|
|
1125
|
-
ctx.logger.warn("Rate limit exceeded", {
|
|
1126
|
-
nextTokenMs: state.nextTokenMs
|
|
1127
|
-
});
|
|
1128
|
-
return errorResult(
|
|
1129
|
-
"transient",
|
|
1130
|
-
`Rate limit exceeded. Try again in ${String(state.nextTokenMs)}ms`,
|
|
1131
|
-
ctx.requestContext.requestId
|
|
1132
|
-
);
|
|
1133
|
-
}
|
|
1134
|
-
return next(args, ctx);
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
function createTimeoutMiddleware(guard, toolName) {
|
|
1138
|
-
return async (args, ctx, next) => {
|
|
1139
|
-
const signal = abortSignalStorage.getStore();
|
|
1140
|
-
const result = await guard.execute(() => next(args, ctx), {
|
|
1141
|
-
operationName: toolName,
|
|
1142
|
-
...signal !== void 0 ? { signal } : {}
|
|
1143
|
-
});
|
|
1144
|
-
if (!result.ok) {
|
|
1145
|
-
ctx.logger.error("Operation timed out", void 0, {
|
|
1146
|
-
code: result.error.code,
|
|
1147
|
-
timeoutMs: result.error.timeoutMs
|
|
1148
|
-
});
|
|
1149
|
-
return errorResult("transient", result.error.message, ctx.requestContext.requestId);
|
|
1150
|
-
}
|
|
1151
|
-
if (result.value.nearTimeout) {
|
|
1152
|
-
ctx.logger.warn("Operation completed near timeout threshold", {
|
|
1153
|
-
durationMs: result.value.durationMs
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
return result.value.value;
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
function createAuditMiddleware() {
|
|
1160
|
-
return async (args, ctx, next) => {
|
|
1161
|
-
const startTime = getTimeProvider().now();
|
|
1162
|
-
ctx.logger.info("Tool invocation started");
|
|
1163
|
-
try {
|
|
1164
|
-
const result = await next(args, ctx);
|
|
1165
|
-
const durationMs = getTimeProvider().now() - startTime;
|
|
1166
|
-
if (result.isError === true) {
|
|
1167
|
-
ctx.logger.warn("Tool execution completed with error", { durationMs });
|
|
1168
|
-
} else {
|
|
1169
|
-
ctx.logger.info("Tool execution completed", { durationMs });
|
|
1170
|
-
}
|
|
1171
|
-
return result;
|
|
1172
|
-
} catch (error) {
|
|
1173
|
-
const durationMs = getTimeProvider().now() - startTime;
|
|
1174
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1175
|
-
ctx.logger.error("Tool execution failed", error instanceof Error ? error : void 0, {
|
|
1176
|
-
durationMs
|
|
1177
|
-
});
|
|
1178
|
-
return errorResult("internal", `Internal error: ${message}`, ctx.requestContext.requestId);
|
|
1179
|
-
}
|
|
1180
|
-
};
|
|
1181
|
-
}
|
|
1182
|
-
function composeMiddleware(middlewares) {
|
|
1183
|
-
return (args, ctx, finalHandler) => {
|
|
1184
|
-
const dispatch = (index, currentArgs) => {
|
|
1185
|
-
if (index >= middlewares.length) {
|
|
1186
|
-
return finalHandler(currentArgs, ctx);
|
|
1187
|
-
}
|
|
1188
|
-
const middleware = middlewares[index];
|
|
1189
|
-
if (middleware === void 0) {
|
|
1190
|
-
return finalHandler(currentArgs, ctx);
|
|
1191
|
-
}
|
|
1192
|
-
return middleware(currentArgs, ctx, (nextArgs) => dispatch(index + 1, nextArgs));
|
|
1193
|
-
};
|
|
1194
|
-
return dispatch(0, args);
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
function addAuditMiddleware(middlewares, skip) {
|
|
1198
|
-
if (skip.audit !== true) {
|
|
1199
|
-
middlewares.push(createAuditMiddleware());
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
function addRateLimitMiddleware(middlewares, config, skip) {
|
|
1203
|
-
if (skip.rateLimit !== true && config.rateLimiter !== void 0) {
|
|
1204
|
-
const limiter = config.rateLimiter instanceof RateLimiter ? config.rateLimiter : new RateLimiter(config.rateLimiter);
|
|
1205
|
-
middlewares.push(createRateLimitMiddleware(limiter));
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
function addValidationMiddleware(middlewares, config, skip) {
|
|
1209
|
-
if (skip.validation !== true && config.schema !== void 0) {
|
|
1210
|
-
middlewares.push(createValidationMiddleware(config.schema));
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
function addPolicyMiddleware(middlewares, config, skip) {
|
|
1214
|
-
if (skip.policy !== true && config.policyFirewall !== void 0) {
|
|
1215
|
-
const mode = config.executionMode ?? "read-only";
|
|
1216
|
-
middlewares.push(
|
|
1217
|
-
createPolicyMiddleware(config.policyFirewall, config.toolName, mode, config.allowedPaths)
|
|
1218
|
-
);
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
function addTimeoutMiddleware(middlewares, config, skip) {
|
|
1222
|
-
if (skip.timeout !== true && config.timeout !== void 0) {
|
|
1223
|
-
const guard = new TimeoutGuard(config.timeout);
|
|
1224
|
-
middlewares.push(createTimeoutMiddleware(guard, config.toolName));
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
function addAccessPolicyMiddleware(middlewares, config, skip) {
|
|
1228
|
-
if (skip.accessPolicy !== true) {
|
|
1229
|
-
middlewares.push(createAccessPolicyChainMiddleware(config.toolName));
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
function buildMiddlewareStack(config) {
|
|
1233
|
-
const skip = config.skip ?? {};
|
|
1234
|
-
const middlewares = [];
|
|
1235
|
-
middlewares.push(createMetricsMiddleware());
|
|
1236
|
-
addAuditMiddleware(middlewares, skip);
|
|
1237
|
-
addRateLimitMiddleware(middlewares, config, skip);
|
|
1238
|
-
addValidationMiddleware(middlewares, config, skip);
|
|
1239
|
-
addPolicyMiddleware(middlewares, config, skip);
|
|
1240
|
-
addAccessPolicyMiddleware(middlewares, config, skip);
|
|
1241
|
-
addTimeoutMiddleware(middlewares, config, skip);
|
|
1242
|
-
return middlewares;
|
|
1243
|
-
}
|
|
1244
|
-
function createMiddlewareChain(config) {
|
|
1245
|
-
const logger11 = config.logger ?? createLogger({ tool: config.toolName });
|
|
1246
|
-
const middlewares = buildMiddlewareStack(config);
|
|
1247
|
-
const composed = composeMiddleware(middlewares);
|
|
1248
|
-
return (handler) => {
|
|
1249
|
-
return async (args) => {
|
|
1250
|
-
const requestContext = createRequestContext({ toolName: config.toolName });
|
|
1251
|
-
const requestLogger = logger11.child(contextForLogging(requestContext));
|
|
1252
|
-
const ctx = { requestContext, logger: requestLogger };
|
|
1253
|
-
return composed(args, ctx, (finalArgs, finalCtx) => handler(finalArgs, finalCtx));
|
|
1254
|
-
};
|
|
1255
|
-
};
|
|
1256
|
-
}
|
|
1257
|
-
function withMiddleware(toolName, handler, options) {
|
|
1258
|
-
const config = {
|
|
1259
|
-
toolName,
|
|
1260
|
-
...options
|
|
1261
|
-
};
|
|
1262
|
-
const chain = createMiddlewareChain(config);
|
|
1263
|
-
const wrappedHandler = (args, ctx) => {
|
|
1264
|
-
if (handler.length >= 2) {
|
|
1265
|
-
return handler(args, ctx);
|
|
1266
|
-
}
|
|
1267
|
-
return handler(args);
|
|
1268
|
-
};
|
|
1269
|
-
return chain(wrappedHandler);
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
// src/mcp/middleware/tool-wrapper.ts
|
|
1273
|
-
var DEFAULT_TIMEOUT_CONFIG = {
|
|
1274
|
-
defaultTimeoutMs: MCP_TIMEOUTS.defaultMs,
|
|
1275
|
-
maxTimeoutMs: MCP_TIMEOUTS.maxMs,
|
|
1276
|
-
enableLogging: true,
|
|
1277
|
-
uriValidation: true
|
|
1278
|
-
};
|
|
1279
|
-
var DEFAULT_TOOL_TIMEOUTS = {
|
|
1280
|
-
...MCP_TIMEOUTS.perTool
|
|
1281
|
-
};
|
|
1282
|
-
function getToolTimeout(toolName, security, explicitMs) {
|
|
1283
|
-
if (explicitMs !== void 0) {
|
|
1284
|
-
return explicitMs;
|
|
1285
|
-
}
|
|
1286
|
-
const perToolConfig = security?.timeout?.perToolTimeout;
|
|
1287
|
-
const perToolMs = perToolConfig?.[toolName];
|
|
1288
|
-
if (perToolMs !== void 0) {
|
|
1289
|
-
return perToolMs;
|
|
1290
|
-
}
|
|
1291
|
-
const builtInDefault = DEFAULT_TOOL_TIMEOUTS[toolName];
|
|
1292
|
-
if (builtInDefault !== void 0) {
|
|
1293
|
-
return builtInDefault;
|
|
1294
|
-
}
|
|
1295
|
-
return security?.timeout?.defaultTimeoutMs ?? DEFAULT_TIMEOUT_CONFIG.defaultTimeoutMs;
|
|
1296
|
-
}
|
|
1297
|
-
function getTimeoutConfig(security, overrideMs) {
|
|
1298
|
-
const timeoutConfig = security?.timeout ?? DEFAULT_TIMEOUT_CONFIG;
|
|
1299
|
-
return {
|
|
1300
|
-
defaultTimeoutMs: overrideMs ?? timeoutConfig.defaultTimeoutMs,
|
|
1301
|
-
maxTimeoutMs: timeoutConfig.maxTimeoutMs,
|
|
1302
|
-
enableLogging: timeoutConfig.enableLogging
|
|
1303
|
-
};
|
|
1304
|
-
}
|
|
1305
|
-
function wrapToolWithTimeout(toolName, handler, options) {
|
|
1306
|
-
return withMiddleware(toolName, handler, {
|
|
1307
|
-
timeout: getTimeoutConfig(void 0, options?.timeoutMs),
|
|
1308
|
-
logger: options?.logger
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
var wrapperLogger = createLogger({ component: "tool-wrapper" });
|
|
1312
|
-
function extractProgressContext(extra) {
|
|
1313
|
-
const sdk = extra;
|
|
1314
|
-
const token = sdk?._meta?.progressToken;
|
|
1315
|
-
const sendFn = sdk?.sendNotification;
|
|
1316
|
-
if (token === void 0 || sendFn === void 0) return void 0;
|
|
1317
|
-
return {
|
|
1318
|
-
progressToken: token,
|
|
1319
|
-
sendNotification: (progress, total) => {
|
|
1320
|
-
const params = {
|
|
1321
|
-
progressToken: token,
|
|
1322
|
-
progress
|
|
1323
|
-
};
|
|
1324
|
-
if (total !== void 0) params["total"] = total;
|
|
1325
|
-
sendFn({ method: "notifications/progress", params }).catch((err2) => {
|
|
1326
|
-
wrapperLogger.debug("Failed to send progress notification", {
|
|
1327
|
-
error: getErrorMessage(err2)
|
|
1328
|
-
});
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
};
|
|
1332
|
-
}
|
|
1333
|
-
function runWithContexts(handler, args, progressCtx, signal) {
|
|
1334
|
-
const run = () => handler(args);
|
|
1335
|
-
if (signal !== void 0 && progressCtx !== void 0) {
|
|
1336
|
-
return abortSignalStorage.run(signal, () => progressContextStorage.run(progressCtx, run));
|
|
1337
|
-
}
|
|
1338
|
-
if (signal !== void 0) {
|
|
1339
|
-
return abortSignalStorage.run(signal, run);
|
|
1340
|
-
}
|
|
1341
|
-
if (progressCtx !== void 0) {
|
|
1342
|
-
return progressContextStorage.run(progressCtx, run);
|
|
1343
|
-
}
|
|
1344
|
-
return run();
|
|
1345
|
-
}
|
|
1346
|
-
function toSdkCallback(handler) {
|
|
1347
|
-
return (args, extra) => {
|
|
1348
|
-
const progressCtx = extractProgressContext(extra);
|
|
1349
|
-
const signal = extra?.signal;
|
|
1350
|
-
return runWithContexts(handler, args, progressCtx, signal);
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
|
-
var MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
1354
|
-
var TIMEOUT_MISMATCH_TELEMETRY_REL_PATH = "mcp-telemetry/timeout-mismatch-events.jsonl";
|
|
1355
|
-
var ensuredDirs = /* @__PURE__ */ new Set();
|
|
1356
|
-
function appendTimeoutMismatchEvent(event) {
|
|
1357
|
-
try {
|
|
1358
|
-
const path = join(getNexusDataDir(), TIMEOUT_MISMATCH_TELEMETRY_REL_PATH);
|
|
1359
|
-
const dir = dirname(path);
|
|
1360
|
-
if (!ensuredDirs.has(dir)) {
|
|
1361
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
1362
|
-
ensuredDirs.add(dir);
|
|
1363
|
-
}
|
|
1364
|
-
appendFileSync(path, JSON.stringify(event) + "\n", "utf-8");
|
|
1365
|
-
} catch (err2) {
|
|
1366
|
-
wrapperLogger.debug("Best-effort timeout-mismatch event recording failed", {
|
|
1367
|
-
error: getErrorMessage(err2)
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
function extractErrorCategoryFromResult(result) {
|
|
1372
|
-
const envelope = result._meta?.["nexus-agents/error"];
|
|
1373
|
-
if (envelope !== null && typeof envelope === "object" && "errorCategory" in envelope) {
|
|
1374
|
-
const cat = envelope.errorCategory;
|
|
1375
|
-
if (typeof cat === "string") return cat;
|
|
1376
|
-
}
|
|
1377
|
-
return void 0;
|
|
1378
|
-
}
|
|
1379
|
-
function extractErrorMessageFromResult(result) {
|
|
1380
|
-
const first = result.content[0];
|
|
1381
|
-
if (first?.type === "text" && typeof first.text === "string") {
|
|
1382
|
-
return first.text.slice(0, 500);
|
|
1383
|
-
}
|
|
1384
|
-
return void 0;
|
|
1385
|
-
}
|
|
1386
|
-
function buildMismatchEvent(ctx, eventId, t0, outcome) {
|
|
1387
|
-
const t1 = Date.now();
|
|
1388
|
-
return {
|
|
1389
|
-
eventId,
|
|
1390
|
-
toolName: ctx.toolName,
|
|
1391
|
-
configuredTimeoutMs: ctx.configuredTimeoutMs,
|
|
1392
|
-
mcpSdkDefaultMs: MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS,
|
|
1393
|
-
startedAt: new Date(t0).toISOString(),
|
|
1394
|
-
endedAt: new Date(t1).toISOString(),
|
|
1395
|
-
durationMs: t1 - t0,
|
|
1396
|
-
outcome: outcome.outcome,
|
|
1397
|
-
...outcome.errorCategory !== void 0 ? { errorCategory: outcome.errorCategory } : {},
|
|
1398
|
-
...outcome.errorMessage !== void 0 ? { errorMessage: outcome.errorMessage } : {}
|
|
1399
|
-
};
|
|
1400
|
-
}
|
|
1401
|
-
function classifyResult(result) {
|
|
1402
|
-
if (result.isError !== true) return { outcome: "success" };
|
|
1403
|
-
const errorCategory = extractErrorCategoryFromResult(result);
|
|
1404
|
-
const errorMessage = extractErrorMessageFromResult(result);
|
|
1405
|
-
return {
|
|
1406
|
-
outcome: "error",
|
|
1407
|
-
...errorCategory !== void 0 ? { errorCategory } : {},
|
|
1408
|
-
...errorMessage !== void 0 ? { errorMessage } : {}
|
|
1409
|
-
};
|
|
1410
|
-
}
|
|
1411
|
-
async function runMismatchedCall(ctx) {
|
|
1412
|
-
const eventId = randomUUID();
|
|
1413
|
-
const t0 = Date.now();
|
|
1414
|
-
ctx.log.warn(
|
|
1415
|
-
"MCP tool budget exceeds client default and no progressToken received \u2014 request likely to be killed by client before server-side deadline",
|
|
1416
|
-
{
|
|
1417
|
-
tool: ctx.toolName,
|
|
1418
|
-
eventId,
|
|
1419
|
-
configuredTimeoutMs: ctx.configuredTimeoutMs,
|
|
1420
|
-
mcpSdkDefaultMs: MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS,
|
|
1421
|
-
remediation: "Client should pass `onprogress` and `resetTimeoutOnProgress: true` when calling, or extend `options.timeout`. See docs/architecture/MCP_PROTOCOL.md."
|
|
1422
|
-
}
|
|
1423
|
-
);
|
|
1424
|
-
try {
|
|
1425
|
-
const result = await runWithContexts(ctx.handler, ctx.args, ctx.progressCtx, ctx.signal);
|
|
1426
|
-
appendTimeoutMismatchEvent(buildMismatchEvent(ctx, eventId, t0, classifyResult(result)));
|
|
1427
|
-
return result;
|
|
1428
|
-
} catch (err2) {
|
|
1429
|
-
appendTimeoutMismatchEvent(
|
|
1430
|
-
buildMismatchEvent(ctx, eventId, t0, {
|
|
1431
|
-
outcome: "error",
|
|
1432
|
-
errorMessage: getErrorMessage(err2).slice(0, 500)
|
|
1433
|
-
})
|
|
1434
|
-
);
|
|
1435
|
-
throw err2;
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
function toSdkCallbackWithBudgetCheck(handler, toolName, configuredTimeoutMs, logger11) {
|
|
1439
|
-
const log = logger11 ?? wrapperLogger;
|
|
1440
|
-
return (args, extra) => {
|
|
1441
|
-
const progressCtx = extractProgressContext(extra);
|
|
1442
|
-
const signal = extra?.signal;
|
|
1443
|
-
const isMismatch = configuredTimeoutMs > MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS && progressCtx === void 0;
|
|
1444
|
-
if (!isMismatch) return runWithContexts(handler, args, progressCtx, signal);
|
|
1445
|
-
return runMismatchedCall({
|
|
1446
|
-
log,
|
|
1447
|
-
handler,
|
|
1448
|
-
args,
|
|
1449
|
-
progressCtx,
|
|
1450
|
-
signal,
|
|
1451
|
-
toolName,
|
|
1452
|
-
configuredTimeoutMs
|
|
1453
|
-
});
|
|
1454
|
-
};
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
// src/audit/secure-handler-audit.ts
|
|
1458
|
-
function actorFromContext(ctx, fallback) {
|
|
1459
|
-
const caller = ctx.caller;
|
|
1460
|
-
const clientId = caller.clientId;
|
|
1461
|
-
if (clientId !== void 0 && clientId.length > 0) {
|
|
1462
|
-
return {
|
|
1463
|
-
type: clientId.includes("api") ? "external" : clientId.includes("cli") ? "agent" : "user",
|
|
1464
|
-
id: clientId,
|
|
1465
|
-
name: caller.userAgent
|
|
1466
|
-
};
|
|
1467
|
-
}
|
|
1468
|
-
return fallback ?? { type: "system", id: "unknown", name: "Unknown Caller" };
|
|
1469
|
-
}
|
|
1470
|
-
function resultToOutcome(isError, isPolicyDenied) {
|
|
1471
|
-
if (isPolicyDenied) return "denied";
|
|
1472
|
-
if (isError === true) return "failure";
|
|
1473
|
-
return "success";
|
|
1474
|
-
}
|
|
1475
|
-
function logToolInvocationAudit(opts) {
|
|
1476
|
-
opts.auditLogger.logToolInvocation({
|
|
1477
|
-
toolName: opts.toolName,
|
|
1478
|
-
outcome: opts.outcome,
|
|
1479
|
-
actor: opts.actor,
|
|
1480
|
-
requestId: opts.requestId,
|
|
1481
|
-
durationMs: opts.durationMs,
|
|
1482
|
-
errorMessage: opts.errorMessage
|
|
1483
|
-
});
|
|
1484
|
-
}
|
|
1485
|
-
function logPolicyAudit(opts) {
|
|
1486
|
-
opts.auditLogger.logPolicyDecision({
|
|
1487
|
-
policyName: opts.policyName,
|
|
1488
|
-
decision: opts.decision,
|
|
1489
|
-
reason: opts.reason,
|
|
1490
|
-
toolName: opts.toolName,
|
|
1491
|
-
actor: opts.actor,
|
|
1492
|
-
requestId: opts.requestId
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
|
-
function logRateLimitAudit(opts) {
|
|
1496
|
-
opts.auditLogger.logRateLimitViolation({
|
|
1497
|
-
toolName: opts.toolName,
|
|
1498
|
-
actor: opts.actor,
|
|
1499
|
-
currentRate: opts.currentRate,
|
|
1500
|
-
limitRate: opts.limitRate,
|
|
1501
|
-
requestId: opts.requestId
|
|
1502
|
-
});
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
// src/mcp/middleware/tool-input-sanitizer.ts
|
|
1506
|
-
var XML_INJECTION_PATTERN = /<\/?(system|human|assistant|instructions|user|prompt|context|tool_use|tool_result)\b[^>]*>/gi;
|
|
1507
|
-
var INJECTION_DETECTORS = [
|
|
1508
|
-
{ name: "system_prompt_override", pattern: /ignore (?:all )?previous (?:instructions|rules)/i },
|
|
1509
|
-
{ name: "role_impersonation", pattern: /i(?:'m| am) the (?:repo |project )?(?:owner|admin)/i },
|
|
1510
|
-
{ name: "hidden_instruction", pattern: /<!--[\s\S]*?(?:execute|delete|merge|apply)[\s\S]*?-->/i }
|
|
1511
|
-
];
|
|
1512
|
-
function sanitizeString(value) {
|
|
1513
|
-
XML_INJECTION_PATTERN.lastIndex = 0;
|
|
1514
|
-
const cleaned = value.replace(XML_INJECTION_PATTERN, "");
|
|
1515
|
-
return { cleaned, modified: cleaned !== value };
|
|
1516
|
-
}
|
|
1517
|
-
function detectPatterns(value) {
|
|
1518
|
-
const detected = [];
|
|
1519
|
-
for (const { name, pattern } of INJECTION_DETECTORS) {
|
|
1520
|
-
pattern.lastIndex = 0;
|
|
1521
|
-
if (pattern.test(value)) {
|
|
1522
|
-
detected.push(name);
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
return detected;
|
|
1526
|
-
}
|
|
1527
|
-
function sanitizeValue(value, stats) {
|
|
1528
|
-
if (typeof value === "string") {
|
|
1529
|
-
const patterns = detectPatterns(value);
|
|
1530
|
-
if (patterns.length > 0) {
|
|
1531
|
-
stats.patterns.push(...patterns);
|
|
1532
|
-
}
|
|
1533
|
-
const { cleaned, modified } = sanitizeString(value);
|
|
1534
|
-
if (modified) stats.count++;
|
|
1535
|
-
return cleaned;
|
|
1536
|
-
}
|
|
1537
|
-
if (Array.isArray(value)) {
|
|
1538
|
-
return value.map((item) => sanitizeValue(item, stats));
|
|
1539
|
-
}
|
|
1540
|
-
if (value !== null && typeof value === "object") {
|
|
1541
|
-
const result = {};
|
|
1542
|
-
for (const [key, val] of Object.entries(value)) {
|
|
1543
|
-
result[key] = sanitizeValue(val, stats);
|
|
1544
|
-
}
|
|
1545
|
-
return result;
|
|
1546
|
-
}
|
|
1547
|
-
return value;
|
|
1548
|
-
}
|
|
1549
|
-
function sanitizeToolInput(args) {
|
|
1550
|
-
if (args === void 0 || args === null) {
|
|
1551
|
-
return { sanitized: args, wasModified: false, modifiedCount: 0, detectedPatterns: [] };
|
|
1552
|
-
}
|
|
1553
|
-
const stats = { count: 0, patterns: [] };
|
|
1554
|
-
const sanitized = sanitizeValue(args, stats);
|
|
1555
|
-
const uniquePatterns = [...new Set(stats.patterns)];
|
|
1556
|
-
return {
|
|
1557
|
-
sanitized,
|
|
1558
|
-
wasModified: stats.count > 0,
|
|
1559
|
-
modifiedCount: stats.count,
|
|
1560
|
-
detectedPatterns: uniquePatterns
|
|
1561
|
-
};
|
|
1562
|
-
}
|
|
1563
|
-
function logSanitizationResult(result, logger11, toolName) {
|
|
1564
|
-
if (result.wasModified) {
|
|
1565
|
-
logger11.warn("Tool input sanitized \u2014 XML injection tags stripped", {
|
|
1566
|
-
tool: toolName,
|
|
1567
|
-
modifiedFields: result.modifiedCount
|
|
1568
|
-
});
|
|
1569
|
-
}
|
|
1570
|
-
if (result.detectedPatterns.length > 0) {
|
|
1571
|
-
logger11.warn("Injection patterns detected in tool input", {
|
|
1572
|
-
tool: toolName,
|
|
1573
|
-
patterns: result.detectedPatterns
|
|
1574
|
-
});
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
14
|
+
NOOP_NOTIFIER,
|
|
15
|
+
createMcpNotifier,
|
|
16
|
+
createSecureHandler,
|
|
17
|
+
getToolAnnotations,
|
|
18
|
+
getToolTimeout,
|
|
19
|
+
toSdkCallbackWithBudgetCheck,
|
|
20
|
+
toolStructuredError,
|
|
21
|
+
toolSuccess,
|
|
22
|
+
withProgressHeartbeat,
|
|
23
|
+
wrapToolWithTimeout
|
|
24
|
+
} from "./chunk-3ZROPKLV.js";
|
|
25
|
+
import {
|
|
26
|
+
getToolMemory
|
|
27
|
+
} from "./chunk-ANH2A44T.js";
|
|
28
|
+
import {
|
|
29
|
+
getDefaultAvailableModelsCache
|
|
30
|
+
} from "./chunk-ZBQXPP7M.js";
|
|
31
|
+
import {
|
|
32
|
+
CUSTOM_API_DEFAULT_MODEL
|
|
33
|
+
} from "./chunk-MMW4KIBH.js";
|
|
34
|
+
import {
|
|
35
|
+
createCliAdapter,
|
|
36
|
+
createCliDetectionCache,
|
|
37
|
+
delay,
|
|
38
|
+
getAvailableClis,
|
|
39
|
+
isCliAvailable,
|
|
40
|
+
sanitizeOutput,
|
|
41
|
+
withTimeout
|
|
42
|
+
} from "./chunk-XMYJWMWN.js";
|
|
43
|
+
import {
|
|
44
|
+
AgentError,
|
|
45
|
+
CLI_NAMES,
|
|
46
|
+
CircularBuffer,
|
|
47
|
+
DEFAULT_CLI,
|
|
48
|
+
ErrorCode,
|
|
49
|
+
ModelCapability,
|
|
50
|
+
ModelError,
|
|
51
|
+
OutcomeStore,
|
|
52
|
+
TASK_SPECIALIZATION_MATRIX,
|
|
53
|
+
TaskOutcomeSchema,
|
|
54
|
+
VOTE_TIMEOUTS,
|
|
55
|
+
categorizeOutcomeErrorMessage,
|
|
56
|
+
clamp01,
|
|
57
|
+
createLogger,
|
|
58
|
+
detectTaskCategory,
|
|
59
|
+
err,
|
|
60
|
+
estimateTokens,
|
|
61
|
+
formatPercentage,
|
|
62
|
+
formatZodError,
|
|
63
|
+
getCliModelName,
|
|
64
|
+
getDefaultModelForCli,
|
|
65
|
+
getDefaultRegistry,
|
|
66
|
+
getErrorMessage,
|
|
67
|
+
getInTreeCapabilitiesMatrix,
|
|
68
|
+
getMcpSafeDeadlineMs,
|
|
69
|
+
getOutcomeStore,
|
|
70
|
+
getPipelineEventBus,
|
|
71
|
+
getRandomProvider,
|
|
72
|
+
getTimeProvider,
|
|
73
|
+
getTokenEstimator,
|
|
74
|
+
isRateLimitLikeError,
|
|
75
|
+
ok,
|
|
76
|
+
recordRateLimitEvent,
|
|
77
|
+
registerPersistentOutcomeStoreFactory,
|
|
78
|
+
resolveCliAlias,
|
|
79
|
+
resolveVoteTimeout,
|
|
80
|
+
toRateLimitError
|
|
81
|
+
} from "./chunk-2KHD2ANY.js";
|
|
82
|
+
import {
|
|
83
|
+
ensureLearningDir,
|
|
84
|
+
getOutcomesFile,
|
|
85
|
+
nexusDataPath,
|
|
86
|
+
nexusDataPathEnsure
|
|
87
|
+
} from "./chunk-CH7QIDHQ.js";
|
|
1577
88
|
|
|
1578
|
-
// src/mcp/
|
|
1579
|
-
|
|
1580
|
-
return toolStructuredError({
|
|
1581
|
-
errorCategory: "transient",
|
|
1582
|
-
message: `Rate limit exceeded. Try again in ${String(nextTokenMs)}ms.`
|
|
1583
|
-
});
|
|
1584
|
-
}
|
|
1585
|
-
function policyDeniedError(reason, requestId) {
|
|
1586
|
-
return toolStructuredError({
|
|
1587
|
-
errorCategory: "permission",
|
|
1588
|
-
message: `Policy denied: ${reason} (request: ${requestId})`
|
|
1589
|
-
});
|
|
1590
|
-
}
|
|
1591
|
-
function internalError(message, requestId) {
|
|
1592
|
-
return toolStructuredError({
|
|
1593
|
-
errorCategory: "internal",
|
|
1594
|
-
message: `Internal error: ${message} (request: ${requestId})`
|
|
1595
|
-
});
|
|
1596
|
-
}
|
|
1597
|
-
var MAX_INPUT_SIZE_BYTES = 10 * 1024 * 1024;
|
|
1598
|
-
var SECRET_PATTERNS = [
|
|
1599
|
-
// API keys with common prefixes
|
|
1600
|
-
/\b(sk-[a-zA-Z0-9]{20,})\b/g,
|
|
1601
|
-
/\b(pk-[a-zA-Z0-9]{20,})\b/g,
|
|
1602
|
-
// AWS-style keys
|
|
1603
|
-
/\b(AKIA[A-Z0-9]{16})\b/g,
|
|
1604
|
-
// Bearer tokens in output
|
|
1605
|
-
/Bearer\s+[a-zA-Z0-9_\-.~+/]+=*/g,
|
|
1606
|
-
// Generic long hex secrets (40+ chars)
|
|
1607
|
-
/\b[0-9a-f]{40,}\b/gi,
|
|
1608
|
-
// password= or token= in output
|
|
1609
|
-
/(?:password|token|secret|apikey|api_key)\s*[=:]\s*\S{8,}/gi
|
|
1610
|
-
];
|
|
1611
|
-
function sanitizeOutput2(text, logger11) {
|
|
1612
|
-
let sanitized = text;
|
|
1613
|
-
for (const pattern of SECRET_PATTERNS) {
|
|
1614
|
-
const before = sanitized;
|
|
1615
|
-
sanitized = sanitized.replace(pattern, "[REDACTED]");
|
|
1616
|
-
if (sanitized !== before) {
|
|
1617
|
-
logger11.warn("Potential secret detected in tool output, redacting", {
|
|
1618
|
-
pattern: pattern.source.slice(0, 30)
|
|
1619
|
-
});
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
return sanitized;
|
|
1623
|
-
}
|
|
1624
|
-
function sanitizeToolResult(result, logger11) {
|
|
1625
|
-
for (const item of result.content) {
|
|
1626
|
-
item.text = sanitizeOutput2(item.text, logger11);
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
function checkInputSize(args, logger11, requestId) {
|
|
1630
|
-
if (args === void 0) return null;
|
|
1631
|
-
const inputSize = JSON.stringify(args).length;
|
|
1632
|
-
if (inputSize > MAX_INPUT_SIZE_BYTES) {
|
|
1633
|
-
logger11.warn("Input size exceeds limit", { inputSize, limit: MAX_INPUT_SIZE_BYTES });
|
|
1634
|
-
return internalError("Input too large", requestId);
|
|
1635
|
-
}
|
|
1636
|
-
return null;
|
|
1637
|
-
}
|
|
1638
|
-
function checkRateLimit(rateLimiter, logger11) {
|
|
1639
|
-
const acquired = rateLimiter.tryAcquire();
|
|
1640
|
-
if (!acquired) {
|
|
1641
|
-
const state = rateLimiter.getState();
|
|
1642
|
-
logger11.warn("Rate limit exceeded");
|
|
1643
|
-
return rateLimitError(state.nextTokenMs);
|
|
1644
|
-
}
|
|
1645
|
-
return null;
|
|
1646
|
-
}
|
|
1647
|
-
function checkPolicy(opts) {
|
|
1648
|
-
const ctxOpts = {
|
|
1649
|
-
mode: opts.mode,
|
|
1650
|
-
...opts.allowedPaths && { allowedPaths: opts.allowedPaths }
|
|
1651
|
-
};
|
|
1652
|
-
const decision = opts.firewall.evaluate(createPolicyContext(opts.toolName, opts.args, ctxOpts));
|
|
1653
|
-
if (!decision.allowed) {
|
|
1654
|
-
opts.logger.warn("Policy denied tool execution", {
|
|
1655
|
-
reason: decision.reason,
|
|
1656
|
-
ruleName: decision.ruleName
|
|
1657
|
-
});
|
|
1658
|
-
return policyDeniedError(decision.reason, opts.requestId);
|
|
1659
|
-
}
|
|
1660
|
-
opts.logger.debug("Policy check passed", { reason: decision.reason });
|
|
1661
|
-
return null;
|
|
1662
|
-
}
|
|
1663
|
-
async function executeHandler(handler, args, ctx, logger11) {
|
|
1664
|
-
const startTime = getTimeProvider().now();
|
|
1665
|
-
const result = handler.length >= 2 ? await handler(args, ctx) : await handler(args);
|
|
1666
|
-
const durationMs = getTimeProvider().now() - startTime;
|
|
1667
|
-
if (result.isError === true) {
|
|
1668
|
-
logger11.warn("Tool execution completed with error", { durationMs });
|
|
1669
|
-
} else {
|
|
1670
|
-
logger11.info("Tool execution completed", { durationMs });
|
|
1671
|
-
}
|
|
1672
|
-
return result;
|
|
1673
|
-
}
|
|
1674
|
-
function emitToolAudit(auditLogger, toolName, ctx, result, durationMs) {
|
|
1675
|
-
const actor = actorFromContext(ctx);
|
|
1676
|
-
const outcome = resultToOutcome(result.isError, false);
|
|
1677
|
-
auditLogger.logToolInvocation({
|
|
1678
|
-
toolName,
|
|
1679
|
-
outcome,
|
|
1680
|
-
actor,
|
|
1681
|
-
requestId: ctx.requestId,
|
|
1682
|
-
durationMs
|
|
1683
|
-
});
|
|
1684
|
-
}
|
|
1685
|
-
function emitToolAuditException(auditLogger, toolName, ctx, durationMs) {
|
|
1686
|
-
const actor = actorFromContext(ctx);
|
|
1687
|
-
auditLogger.logToolInvocation({
|
|
1688
|
-
toolName,
|
|
1689
|
-
outcome: "error",
|
|
1690
|
-
actor,
|
|
1691
|
-
requestId: ctx.requestId,
|
|
1692
|
-
durationMs
|
|
1693
|
-
});
|
|
1694
|
-
}
|
|
1695
|
-
function emitPolicyAudit(auditLogger, toolName, ctx, reason) {
|
|
1696
|
-
const actor = actorFromContext(ctx);
|
|
1697
|
-
auditLogger.logPolicyDecision({
|
|
1698
|
-
policyName: "default",
|
|
1699
|
-
decision: "deny",
|
|
1700
|
-
reason,
|
|
1701
|
-
toolName,
|
|
1702
|
-
actor,
|
|
1703
|
-
requestId: ctx.requestId
|
|
1704
|
-
});
|
|
1705
|
-
}
|
|
1706
|
-
function emitRateLimitAudit(auditLogger, toolName, ctx) {
|
|
1707
|
-
const actor = actorFromContext(ctx);
|
|
1708
|
-
auditLogger.logRateLimitViolation({
|
|
1709
|
-
toolName,
|
|
1710
|
-
actor,
|
|
1711
|
-
currentRate: 0,
|
|
1712
|
-
limitRate: 0,
|
|
1713
|
-
requestId: ctx.requestId
|
|
1714
|
-
});
|
|
1715
|
-
}
|
|
1716
|
-
function checkSecurityTier(config, sanitizeResult, logger11) {
|
|
1717
|
-
const tier = config.securityTier ?? "standard";
|
|
1718
|
-
if (tier === "standard" || sanitizeResult.detectedPatterns.length === 0) {
|
|
1719
|
-
return null;
|
|
1720
|
-
}
|
|
1721
|
-
logger11.warn("Input rejected by security tier validation", {
|
|
1722
|
-
tier,
|
|
1723
|
-
patterns: sanitizeResult.detectedPatterns
|
|
1724
|
-
});
|
|
1725
|
-
return toolStructuredError({
|
|
1726
|
-
errorCategory: "permission",
|
|
1727
|
-
message: `Input validation failed: detected patterns [${sanitizeResult.detectedPatterns.join(", ")}]. Remove prompt injection patterns and retry.`
|
|
1728
|
-
});
|
|
1729
|
-
}
|
|
1730
|
-
function runPreChecks(config, args, mode, requestContext, logger11) {
|
|
1731
|
-
const sizeResult = checkInputSize(args, logger11, requestContext.requestId);
|
|
1732
|
-
if (sizeResult) return { error: sizeResult, sanitizedArgs: args };
|
|
1733
|
-
const sanitizeResult = sanitizeToolInput(args);
|
|
1734
|
-
logSanitizationResult(sanitizeResult, logger11, config.toolName);
|
|
1735
|
-
const sanitizedArgs = sanitizeResult.wasModified ? sanitizeResult.sanitized : args;
|
|
1736
|
-
const tierError = checkSecurityTier(config, sanitizeResult, logger11);
|
|
1737
|
-
if (tierError !== null) return { error: tierError, sanitizedArgs };
|
|
1738
|
-
if (config.rateLimiter) {
|
|
1739
|
-
const rlResult = checkRateLimit(config.rateLimiter, logger11);
|
|
1740
|
-
if (rlResult) {
|
|
1741
|
-
if (config.auditLogger)
|
|
1742
|
-
emitRateLimitAudit(config.auditLogger, config.toolName, requestContext);
|
|
1743
|
-
return { error: rlResult, sanitizedArgs };
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
if (config.policyFirewall) {
|
|
1747
|
-
const pResult = checkPolicy({
|
|
1748
|
-
firewall: config.policyFirewall,
|
|
1749
|
-
toolName: config.toolName,
|
|
1750
|
-
args: sanitizedArgs,
|
|
1751
|
-
mode,
|
|
1752
|
-
allowedPaths: config.allowedPaths,
|
|
1753
|
-
logger: logger11,
|
|
1754
|
-
requestId: requestContext.requestId
|
|
1755
|
-
});
|
|
1756
|
-
if (pResult) {
|
|
1757
|
-
if (config.auditLogger)
|
|
1758
|
-
emitPolicyAudit(config.auditLogger, config.toolName, requestContext, "policy denied");
|
|
1759
|
-
return { error: pResult, sanitizedArgs };
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
return { error: null, sanitizedArgs };
|
|
1763
|
-
}
|
|
1764
|
-
function createSecureHandler(handler, config) {
|
|
1765
|
-
const logger11 = config.logger ?? createLogger({ tool: config.toolName });
|
|
1766
|
-
const mode = config.executionMode ?? "read-only";
|
|
1767
|
-
return async (args) => {
|
|
1768
|
-
const ctxOpts = {
|
|
1769
|
-
toolName: config.toolName,
|
|
1770
|
-
...config.callerInfo && { caller: config.callerInfo }
|
|
1771
|
-
};
|
|
1772
|
-
const requestContext = createRequestContext(ctxOpts);
|
|
1773
|
-
const requestLogger = logger11.child(contextForLogging(requestContext));
|
|
1774
|
-
requestLogger.info("Tool invocation started");
|
|
1775
|
-
const { error: preCheckError, sanitizedArgs } = runPreChecks(
|
|
1776
|
-
config,
|
|
1777
|
-
args,
|
|
1778
|
-
mode,
|
|
1779
|
-
requestContext,
|
|
1780
|
-
requestLogger
|
|
1781
|
-
);
|
|
1782
|
-
if (preCheckError) return preCheckError;
|
|
1783
|
-
return executeAndAudit(handler, sanitizedArgs, requestContext, requestLogger, config);
|
|
1784
|
-
};
|
|
1785
|
-
}
|
|
1786
|
-
async function executeAndAudit(handler, sanitizedArgs, requestContext, requestLogger, config) {
|
|
1787
|
-
const execStartTime = getTimeProvider().now();
|
|
1788
|
-
try {
|
|
1789
|
-
const result = await executeHandler(
|
|
1790
|
-
handler,
|
|
1791
|
-
sanitizedArgs,
|
|
1792
|
-
{ requestContext, logger: requestLogger },
|
|
1793
|
-
requestLogger
|
|
1794
|
-
);
|
|
1795
|
-
sanitizeToolResult(result, requestLogger);
|
|
1796
|
-
if (config.auditLogger) {
|
|
1797
|
-
emitToolAudit(
|
|
1798
|
-
config.auditLogger,
|
|
1799
|
-
config.toolName,
|
|
1800
|
-
requestContext,
|
|
1801
|
-
result,
|
|
1802
|
-
getTimeProvider().now() - execStartTime
|
|
1803
|
-
);
|
|
1804
|
-
}
|
|
1805
|
-
return result;
|
|
1806
|
-
} catch (error) {
|
|
1807
|
-
const rawMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1808
|
-
requestLogger.error("Tool execution failed", error instanceof Error ? error : void 0);
|
|
1809
|
-
if (config.auditLogger) {
|
|
1810
|
-
emitToolAuditException(
|
|
1811
|
-
config.auditLogger,
|
|
1812
|
-
config.toolName,
|
|
1813
|
-
requestContext,
|
|
1814
|
-
getTimeProvider().now() - execStartTime
|
|
1815
|
-
);
|
|
1816
|
-
}
|
|
1817
|
-
const sanitized = sanitizeOutput2(rawMessage, requestLogger);
|
|
1818
|
-
return internalError(sanitized, requestContext.requestId);
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
89
|
+
// src/mcp/tools/consensus-vote.ts
|
|
90
|
+
import { z as z10 } from "zod";
|
|
1821
91
|
|
|
1822
92
|
// src/cli/vote-types.ts
|
|
1823
93
|
var VOTER_ROLES = {
|
|
@@ -2824,7 +1094,7 @@ async function createAutoAdapter(config = {}) {
|
|
|
2824
1094
|
}
|
|
2825
1095
|
|
|
2826
1096
|
// src/agents/collaboration/event-bus-helpers.ts
|
|
2827
|
-
import { randomUUID
|
|
1097
|
+
import { randomUUID } from "crypto";
|
|
2828
1098
|
var DEFAULT_MAX_HISTORY_SIZE = 1e3;
|
|
2829
1099
|
var MAX_SUBSCRIPTIONS = 500;
|
|
2830
1100
|
var MAX_TOPIC_PATTERN_LENGTH = 200;
|
|
@@ -2845,10 +1115,10 @@ function topicMatchesPattern(topic, regex) {
|
|
|
2845
1115
|
return regex.test(topic);
|
|
2846
1116
|
}
|
|
2847
1117
|
function generateEventId() {
|
|
2848
|
-
return `evt-${String(getTimeProvider().now())}-${
|
|
1118
|
+
return `evt-${String(getTimeProvider().now())}-${randomUUID().slice(0, 8)}`;
|
|
2849
1119
|
}
|
|
2850
1120
|
function generateSubscriptionId() {
|
|
2851
|
-
return `sub-${String(getTimeProvider().now())}-${
|
|
1121
|
+
return `sub-${String(getTimeProvider().now())}-${randomUUID().slice(0, 8)}`;
|
|
2852
1122
|
}
|
|
2853
1123
|
function applyHistoryFilters(history, filter) {
|
|
2854
1124
|
let result = history;
|
|
@@ -4000,7 +2270,7 @@ var SIMULATED_VOTE_REASONING = {
|
|
|
4000
2270
|
};
|
|
4001
2271
|
|
|
4002
2272
|
// src/cli/voter-response.ts
|
|
4003
|
-
import { z
|
|
2273
|
+
import { z } from "zod";
|
|
4004
2274
|
var SyntheticVoteError = class extends Error {
|
|
4005
2275
|
constructor(reason, rawOutput) {
|
|
4006
2276
|
super(
|
|
@@ -4010,26 +2280,26 @@ var SyntheticVoteError = class extends Error {
|
|
|
4010
2280
|
this.name = "SyntheticVoteError";
|
|
4011
2281
|
}
|
|
4012
2282
|
};
|
|
4013
|
-
var RawFindingSchema =
|
|
4014
|
-
summary:
|
|
4015
|
-
location:
|
|
4016
|
-
severity:
|
|
4017
|
-
gate:
|
|
4018
|
-
reread_cited_line:
|
|
4019
|
-
traced_call_path:
|
|
4020
|
-
named_assertion:
|
|
4021
|
-
ruled_out_language_non_issue:
|
|
2283
|
+
var RawFindingSchema = z.object({
|
|
2284
|
+
summary: z.string().min(1).max(500).describe("One-line summary of the issue"),
|
|
2285
|
+
location: z.string().min(1).max(200).describe("path/file.ext:line"),
|
|
2286
|
+
severity: z.enum(["critical", "high", "medium", "low"]).default("medium"),
|
|
2287
|
+
gate: z.object({
|
|
2288
|
+
reread_cited_line: z.enum(["passed", "failed", "skipped"]).default("skipped"),
|
|
2289
|
+
traced_call_path: z.enum(["passed", "failed", "skipped"]).default("skipped"),
|
|
2290
|
+
named_assertion: z.string().default("").describe("Concrete failing assertion \u2014 substantive, not a rubber-stamp word"),
|
|
2291
|
+
ruled_out_language_non_issue: z.enum(["passed", "failed", "skipped"]).default("skipped")
|
|
4022
2292
|
}),
|
|
4023
|
-
claim:
|
|
2293
|
+
claim: z.string().min(1).max(2e3).describe("What is wrong and why it justifies blocking")
|
|
4024
2294
|
});
|
|
4025
|
-
var VoteResponseSchema =
|
|
4026
|
-
decision:
|
|
4027
|
-
reasoning:
|
|
4028
|
-
confidence:
|
|
4029
|
-
conditions:
|
|
2295
|
+
var VoteResponseSchema = z.object({
|
|
2296
|
+
decision: z.enum(["approve", "reject", "abstain"]).describe("Your vote decision"),
|
|
2297
|
+
reasoning: z.string().min(10).max(4e3).describe("Explanation for your vote (10-4000 chars)"),
|
|
2298
|
+
confidence: z.number().min(0).max(1).describe("Confidence level 0-1"),
|
|
2299
|
+
conditions: z.array(z.string()).optional().describe("Optional conditions for approval"),
|
|
4030
2300
|
/** Structured rejection categories for reject→refine→re-vote loops (Issue #1213). */
|
|
4031
|
-
rejectionCategories:
|
|
4032
|
-
|
|
2301
|
+
rejectionCategories: z.array(
|
|
2302
|
+
z.enum([
|
|
4033
2303
|
"YAGNI",
|
|
4034
2304
|
"DRY_VIOLATION",
|
|
4035
2305
|
"OVER_ENGINEERING",
|
|
@@ -4041,7 +2311,7 @@ var VoteResponseSchema = z3.object({
|
|
|
4041
2311
|
).optional().describe("Rejection reason categories when decision is reject"),
|
|
4042
2312
|
/** Top-level structured findings for PR-review mode (#2245 v4 follow-up).
|
|
4043
2313
|
* Replaces the YAML-in-reasoning encoding that proved lossy. */
|
|
4044
|
-
findings:
|
|
2314
|
+
findings: z.array(RawFindingSchema).optional().describe("Structured findings (PR review only)")
|
|
4045
2315
|
});
|
|
4046
2316
|
var VOTE_PROMPT_EXAMPLES = `Example approve response:
|
|
4047
2317
|
{
|
|
@@ -4572,8 +2842,8 @@ async function collectRealVotes(options) {
|
|
|
4572
2842
|
}
|
|
4573
2843
|
|
|
4574
2844
|
// src/consensus/types-core.ts
|
|
4575
|
-
import { z as
|
|
4576
|
-
var ConsensusAlgorithmSchema =
|
|
2845
|
+
import { z as z2 } from "zod";
|
|
2846
|
+
var ConsensusAlgorithmSchema = z2.enum([
|
|
4577
2847
|
"simple_majority",
|
|
4578
2848
|
"supermajority",
|
|
4579
2849
|
"unanimous",
|
|
@@ -4581,8 +2851,8 @@ var ConsensusAlgorithmSchema = z4.enum([
|
|
|
4581
2851
|
"opinion_wise",
|
|
4582
2852
|
"higher_order"
|
|
4583
2853
|
]);
|
|
4584
|
-
var VoteDecisionSchema =
|
|
4585
|
-
var ProposalStatusSchema =
|
|
2854
|
+
var VoteDecisionSchema = z2.enum(["approve", "reject", "abstain"]);
|
|
2855
|
+
var ProposalStatusSchema = z2.enum([
|
|
4586
2856
|
"pending",
|
|
4587
2857
|
"voting",
|
|
4588
2858
|
"approved",
|
|
@@ -4590,7 +2860,7 @@ var ProposalStatusSchema = z4.enum([
|
|
|
4590
2860
|
"timeout",
|
|
4591
2861
|
"closed"
|
|
4592
2862
|
]);
|
|
4593
|
-
var RejectionCategorySchema =
|
|
2863
|
+
var RejectionCategorySchema = z2.enum([
|
|
4594
2864
|
"YAGNI",
|
|
4595
2865
|
"DRY_VIOLATION",
|
|
4596
2866
|
"OVER_ENGINEERING",
|
|
@@ -4600,69 +2870,69 @@ var RejectionCategorySchema = z4.enum([
|
|
|
4600
2870
|
"INSUFFICIENT_EVIDENCE"
|
|
4601
2871
|
]);
|
|
4602
2872
|
var REJECTION_CATEGORIES = RejectionCategorySchema.options;
|
|
4603
|
-
var FindingShapeSchema =
|
|
4604
|
-
summary:
|
|
4605
|
-
location:
|
|
4606
|
-
severity:
|
|
4607
|
-
gate:
|
|
4608
|
-
reread_cited_line:
|
|
4609
|
-
traced_call_path:
|
|
4610
|
-
named_assertion:
|
|
4611
|
-
ruled_out_language_non_issue:
|
|
2873
|
+
var FindingShapeSchema = z2.object({
|
|
2874
|
+
summary: z2.string().min(1).max(500),
|
|
2875
|
+
location: z2.string().min(1).max(200),
|
|
2876
|
+
severity: z2.enum(["critical", "high", "medium", "low"]).default("medium"),
|
|
2877
|
+
gate: z2.object({
|
|
2878
|
+
reread_cited_line: z2.enum(["passed", "failed", "skipped"]).default("skipped"),
|
|
2879
|
+
traced_call_path: z2.enum(["passed", "failed", "skipped"]).default("skipped"),
|
|
2880
|
+
named_assertion: z2.string().default(""),
|
|
2881
|
+
ruled_out_language_non_issue: z2.enum(["passed", "failed", "skipped"]).default("skipped")
|
|
4612
2882
|
}),
|
|
4613
|
-
claim:
|
|
2883
|
+
claim: z2.string().min(1).max(2e3)
|
|
4614
2884
|
});
|
|
4615
|
-
var VoteSchema =
|
|
2885
|
+
var VoteSchema = z2.object({
|
|
4616
2886
|
decision: VoteDecisionSchema,
|
|
4617
|
-
reasoning:
|
|
4618
|
-
confidence:
|
|
4619
|
-
conditions:
|
|
2887
|
+
reasoning: z2.string().min(1).describe("Explanation for the vote"),
|
|
2888
|
+
confidence: z2.number().min(0).max(1).describe("Confidence level 0-1"),
|
|
2889
|
+
conditions: z2.array(z2.string()).optional().describe("Conditions for approval"),
|
|
4620
2890
|
/** Structured rejection categories for reject→refine→re-vote loops (Issue #1213). */
|
|
4621
|
-
rejectionCategories:
|
|
2891
|
+
rejectionCategories: z2.array(RejectionCategorySchema).optional().describe("Rejection reason categories when decision is reject"),
|
|
4622
2892
|
/** Pre-verified PR-review findings (#2245 v4 follow-up). Optional;
|
|
4623
2893
|
* populated only when the voter emits the structured top-level array. */
|
|
4624
|
-
findings:
|
|
4625
|
-
timestamp:
|
|
2894
|
+
findings: z2.array(FindingShapeSchema).optional().describe("PR-review findings (pre-verified)"),
|
|
2895
|
+
timestamp: z2.iso.datetime().optional()
|
|
4626
2896
|
});
|
|
4627
|
-
var ProposalSchema =
|
|
4628
|
-
id:
|
|
4629
|
-
title:
|
|
4630
|
-
description:
|
|
2897
|
+
var ProposalSchema = z2.object({
|
|
2898
|
+
id: z2.string().optional().describe("Auto-generated if not provided"),
|
|
2899
|
+
title: z2.string().min(1).max(200).describe("Short proposal title"),
|
|
2900
|
+
description: z2.string().min(1).describe("Detailed proposal description"),
|
|
4631
2901
|
algorithm: ConsensusAlgorithmSchema,
|
|
4632
|
-
timeout:
|
|
4633
|
-
requiredVoters:
|
|
4634
|
-
metadata:
|
|
4635
|
-
createdAt:
|
|
2902
|
+
timeout: z2.number().int().positive().optional().describe("Timeout in milliseconds"),
|
|
2903
|
+
requiredVoters: z2.array(z2.string()).optional().describe("Agent IDs that must vote"),
|
|
2904
|
+
metadata: z2.record(z2.string(), z2.unknown()).optional().describe("Additional context"),
|
|
2905
|
+
createdAt: z2.iso.datetime().optional()
|
|
4636
2906
|
});
|
|
4637
|
-
var ConsensusResultSchema =
|
|
4638
|
-
proposalId:
|
|
2907
|
+
var ConsensusResultSchema = z2.object({
|
|
2908
|
+
proposalId: z2.string(),
|
|
4639
2909
|
proposal: ProposalSchema,
|
|
4640
2910
|
outcome: ProposalStatusSchema,
|
|
4641
|
-
votes:
|
|
4642
|
-
voteCounts:
|
|
4643
|
-
approve:
|
|
4644
|
-
reject:
|
|
4645
|
-
abstain:
|
|
4646
|
-
total:
|
|
2911
|
+
votes: z2.map(z2.string(), VoteSchema),
|
|
2912
|
+
voteCounts: z2.object({
|
|
2913
|
+
approve: z2.number().int().nonnegative(),
|
|
2914
|
+
reject: z2.number().int().nonnegative(),
|
|
2915
|
+
abstain: z2.number().int().nonnegative(),
|
|
2916
|
+
total: z2.number().int().nonnegative()
|
|
4647
2917
|
}),
|
|
4648
|
-
weightedCounts:
|
|
4649
|
-
approve:
|
|
4650
|
-
reject:
|
|
4651
|
-
abstain:
|
|
4652
|
-
totalWeight:
|
|
2918
|
+
weightedCounts: z2.object({
|
|
2919
|
+
approve: z2.number().nonnegative(),
|
|
2920
|
+
reject: z2.number().nonnegative(),
|
|
2921
|
+
abstain: z2.number().nonnegative(),
|
|
2922
|
+
totalWeight: z2.number().nonnegative()
|
|
4653
2923
|
}).optional(),
|
|
4654
|
-
approvalPercentage:
|
|
4655
|
-
quorumReached:
|
|
4656
|
-
startedAt:
|
|
4657
|
-
closedAt:
|
|
4658
|
-
durationMs:
|
|
2924
|
+
approvalPercentage: z2.number().min(0).max(100),
|
|
2925
|
+
quorumReached: z2.boolean(),
|
|
2926
|
+
startedAt: z2.iso.datetime(),
|
|
2927
|
+
closedAt: z2.iso.datetime(),
|
|
2928
|
+
durationMs: z2.number().int().nonnegative()
|
|
4659
2929
|
});
|
|
4660
|
-
var AgentPerformanceSchema =
|
|
4661
|
-
agentId:
|
|
4662
|
-
totalVotes:
|
|
4663
|
-
correctVotes:
|
|
4664
|
-
successRate:
|
|
4665
|
-
lastUpdated:
|
|
2930
|
+
var AgentPerformanceSchema = z2.object({
|
|
2931
|
+
agentId: z2.string(),
|
|
2932
|
+
totalVotes: z2.number().int().nonnegative(),
|
|
2933
|
+
correctVotes: z2.number().int().nonnegative(),
|
|
2934
|
+
successRate: z2.number().min(0).max(1),
|
|
2935
|
+
lastUpdated: z2.iso.datetime()
|
|
4666
2936
|
});
|
|
4667
2937
|
var DEFAULT_INCREMENTAL_QUORUM_CONFIG = {
|
|
4668
2938
|
enabled: false,
|
|
@@ -4671,19 +2941,19 @@ var DEFAULT_INCREMENTAL_QUORUM_CONFIG = {
|
|
|
4671
2941
|
confidenceThreshold: 0.6,
|
|
4672
2942
|
ambiguityBand: 0.15
|
|
4673
2943
|
};
|
|
4674
|
-
var ProposalCacheConfigSchema =
|
|
4675
|
-
enabled:
|
|
4676
|
-
ttlMs:
|
|
2944
|
+
var ProposalCacheConfigSchema = z2.object({
|
|
2945
|
+
enabled: z2.boolean().default(false),
|
|
2946
|
+
ttlMs: z2.number().int().positive().default(36e5),
|
|
4677
2947
|
// 1 hour
|
|
4678
|
-
maxEntries:
|
|
2948
|
+
maxEntries: z2.number().int().positive().default(500)
|
|
4679
2949
|
});
|
|
4680
|
-
var ConsensusEngineConfigSchema =
|
|
4681
|
-
defaultTimeout:
|
|
2950
|
+
var ConsensusEngineConfigSchema = z2.object({
|
|
2951
|
+
defaultTimeout: z2.number().int().positive().default(3e5),
|
|
4682
2952
|
// 5 minutes
|
|
4683
|
-
minVotersForQuorum:
|
|
4684
|
-
maxActiveProposals:
|
|
4685
|
-
enablePerformanceTracking:
|
|
4686
|
-
maxClosedProposals:
|
|
2953
|
+
minVotersForQuorum: z2.number().int().positive().default(2),
|
|
2954
|
+
maxActiveProposals: z2.number().int().positive().default(100),
|
|
2955
|
+
enablePerformanceTracking: z2.boolean().default(true),
|
|
2956
|
+
maxClosedProposals: z2.number().int().positive().default(1e3),
|
|
4687
2957
|
// Issue #549
|
|
4688
2958
|
proposalCache: ProposalCacheConfigSchema.optional()
|
|
4689
2959
|
// Issue #589
|
|
@@ -4708,50 +2978,50 @@ var VOTING_THRESHOLDS = {
|
|
|
4708
2978
|
higher_order: 0.5
|
|
4709
2979
|
// Alias for opinion_wise (Issue #514)
|
|
4710
2980
|
};
|
|
4711
|
-
var ConsensusMetricsSchema =
|
|
4712
|
-
totalProposals:
|
|
4713
|
-
approvedProposals:
|
|
4714
|
-
rejectedProposals:
|
|
4715
|
-
timedOutProposals:
|
|
4716
|
-
averageDurationMs:
|
|
4717
|
-
averageVotesPerProposal:
|
|
4718
|
-
algorithmUsage:
|
|
2981
|
+
var ConsensusMetricsSchema = z2.object({
|
|
2982
|
+
totalProposals: z2.number().int().nonnegative(),
|
|
2983
|
+
approvedProposals: z2.number().int().nonnegative(),
|
|
2984
|
+
rejectedProposals: z2.number().int().nonnegative(),
|
|
2985
|
+
timedOutProposals: z2.number().int().nonnegative(),
|
|
2986
|
+
averageDurationMs: z2.number().nonnegative(),
|
|
2987
|
+
averageVotesPerProposal: z2.number().nonnegative(),
|
|
2988
|
+
algorithmUsage: z2.record(ConsensusAlgorithmSchema, z2.number().int().nonnegative())
|
|
4719
2989
|
});
|
|
4720
2990
|
|
|
4721
2991
|
// src/consensus/types-voting-protocol.ts
|
|
4722
|
-
import { z as
|
|
4723
|
-
var VotingRoundPhaseSchema =
|
|
4724
|
-
var VotingRoundStatusSchema =
|
|
2992
|
+
import { z as z3 } from "zod";
|
|
2993
|
+
var VotingRoundPhaseSchema = z3.enum(["analysis", "deliberation", "consensus"]);
|
|
2994
|
+
var VotingRoundStatusSchema = z3.enum([
|
|
4725
2995
|
"pending",
|
|
4726
2996
|
"in_progress",
|
|
4727
2997
|
"awaiting_votes",
|
|
4728
2998
|
"completed",
|
|
4729
2999
|
"aborted"
|
|
4730
3000
|
]);
|
|
4731
|
-
var AgentFindingSchema =
|
|
4732
|
-
agentId:
|
|
4733
|
-
category:
|
|
4734
|
-
severity:
|
|
4735
|
-
description:
|
|
4736
|
-
location:
|
|
4737
|
-
suggestion:
|
|
4738
|
-
confidence:
|
|
4739
|
-
timestamp:
|
|
3001
|
+
var AgentFindingSchema = z3.object({
|
|
3002
|
+
agentId: z3.string(),
|
|
3003
|
+
category: z3.enum(["bug", "security", "performance", "style", "design", "documentation", "other"]),
|
|
3004
|
+
severity: z3.enum(["critical", "major", "minor", "suggestion"]),
|
|
3005
|
+
description: z3.string().min(1),
|
|
3006
|
+
location: z3.string().optional().describe("File path and line range if applicable"),
|
|
3007
|
+
suggestion: z3.string().optional().describe("Recommended fix"),
|
|
3008
|
+
confidence: z3.number().min(0).max(1),
|
|
3009
|
+
timestamp: z3.iso.datetime().optional()
|
|
4740
3010
|
});
|
|
4741
|
-
var FindingVoteSchema =
|
|
4742
|
-
agentId:
|
|
4743
|
-
findingId:
|
|
4744
|
-
agree:
|
|
4745
|
-
reasoning:
|
|
4746
|
-
amendedSeverity:
|
|
3011
|
+
var FindingVoteSchema = z3.object({
|
|
3012
|
+
agentId: z3.string(),
|
|
3013
|
+
findingId: z3.string(),
|
|
3014
|
+
agree: z3.boolean(),
|
|
3015
|
+
reasoning: z3.string().optional(),
|
|
3016
|
+
amendedSeverity: z3.enum(["critical", "major", "minor", "suggestion"]).optional()
|
|
4747
3017
|
});
|
|
4748
|
-
var VotingProtocolConfigSchema =
|
|
4749
|
-
committeeSize:
|
|
4750
|
-
maxRounds:
|
|
4751
|
-
roundTimeoutMs:
|
|
4752
|
-
agreementThreshold:
|
|
4753
|
-
enableAntiSycophancy:
|
|
4754
|
-
sycophancyThreshold:
|
|
3018
|
+
var VotingProtocolConfigSchema = z3.object({
|
|
3019
|
+
committeeSize: z3.number().int().min(2).max(7).default(3),
|
|
3020
|
+
maxRounds: z3.number().int().min(1).max(5).default(3),
|
|
3021
|
+
roundTimeoutMs: z3.number().int().positive().default(6e4),
|
|
3022
|
+
agreementThreshold: z3.number().min(0.5).max(1).default(0.67),
|
|
3023
|
+
enableAntiSycophancy: z3.boolean().default(true),
|
|
3024
|
+
sycophancyThreshold: z3.number().min(0).max(1).default(0.8)
|
|
4755
3025
|
});
|
|
4756
3026
|
var DEFAULT_VOTING_PROTOCOL_CONFIG = {
|
|
4757
3027
|
committeeSize: 3,
|
|
@@ -4763,30 +3033,30 @@ var DEFAULT_VOTING_PROTOCOL_CONFIG = {
|
|
|
4763
3033
|
};
|
|
4764
3034
|
|
|
4765
3035
|
// src/consensus/types-weighted-voting.ts
|
|
4766
|
-
import { z as
|
|
4767
|
-
var TaskOutcomeSchema2 =
|
|
4768
|
-
var WeightedAgentRecordSchema =
|
|
4769
|
-
agentId:
|
|
4770
|
-
totalTasks:
|
|
4771
|
-
successfulTasks:
|
|
4772
|
-
failedTasks:
|
|
4773
|
-
partialTasks:
|
|
4774
|
-
successRate:
|
|
4775
|
-
weight:
|
|
4776
|
-
trustScore:
|
|
4777
|
-
byzantineFlags:
|
|
4778
|
-
lastActive:
|
|
4779
|
-
createdAt:
|
|
3036
|
+
import { z as z4 } from "zod";
|
|
3037
|
+
var TaskOutcomeSchema2 = z4.enum(["success", "failure", "partial", "unknown"]);
|
|
3038
|
+
var WeightedAgentRecordSchema = z4.object({
|
|
3039
|
+
agentId: z4.string().min(1),
|
|
3040
|
+
totalTasks: z4.number().int().nonnegative(),
|
|
3041
|
+
successfulTasks: z4.number().int().nonnegative(),
|
|
3042
|
+
failedTasks: z4.number().int().nonnegative(),
|
|
3043
|
+
partialTasks: z4.number().int().nonnegative(),
|
|
3044
|
+
successRate: z4.number().min(0).max(1),
|
|
3045
|
+
weight: z4.number().min(0).max(1),
|
|
3046
|
+
trustScore: z4.number().min(0).max(1),
|
|
3047
|
+
byzantineFlags: z4.number().int().nonnegative(),
|
|
3048
|
+
lastActive: z4.date(),
|
|
3049
|
+
createdAt: z4.date()
|
|
4780
3050
|
});
|
|
4781
|
-
var WeightedVotingConfigSchema =
|
|
4782
|
-
minWeight:
|
|
4783
|
-
maxByzantineFraction:
|
|
4784
|
-
weightDecayFactor:
|
|
4785
|
-
weightRecoveryFactor:
|
|
4786
|
-
minTrustScore:
|
|
4787
|
-
byzantineFlagThreshold:
|
|
4788
|
-
initialWeight:
|
|
4789
|
-
quorumThreshold:
|
|
3051
|
+
var WeightedVotingConfigSchema = z4.object({
|
|
3052
|
+
minWeight: z4.number().min(0).max(1).default(0.1),
|
|
3053
|
+
maxByzantineFraction: z4.number().min(0).max(0.5).default(0.33),
|
|
3054
|
+
weightDecayFactor: z4.number().min(0.5).max(1).default(0.9),
|
|
3055
|
+
weightRecoveryFactor: z4.number().min(1).max(2).default(1.05),
|
|
3056
|
+
minTrustScore: z4.number().min(0).max(1).default(0.3),
|
|
3057
|
+
byzantineFlagThreshold: z4.number().int().positive().default(3),
|
|
3058
|
+
initialWeight: z4.number().min(0).max(1).default(0.5),
|
|
3059
|
+
quorumThreshold: z4.number().min(0.5).max(1).default(0.67)
|
|
4790
3060
|
});
|
|
4791
3061
|
var DEFAULT_WEIGHTED_VOTING_CONFIG = {
|
|
4792
3062
|
minWeight: 0.1,
|
|
@@ -4800,7 +3070,7 @@ var DEFAULT_WEIGHTED_VOTING_CONFIG = {
|
|
|
4800
3070
|
};
|
|
4801
3071
|
|
|
4802
3072
|
// src/consensus/higher-order-types.ts
|
|
4803
|
-
import { z as
|
|
3073
|
+
import { z as z5 } from "zod";
|
|
4804
3074
|
function createAgentPairKey(agentA, agentB) {
|
|
4805
3075
|
return agentA < agentB ? `${agentA}:${agentB}` : `${agentB}:${agentA}`;
|
|
4806
3076
|
}
|
|
@@ -4811,40 +3081,40 @@ function parseAgentPairKey(key) {
|
|
|
4811
3081
|
}
|
|
4812
3082
|
return [parts[0], parts[1]];
|
|
4813
3083
|
}
|
|
4814
|
-
var CorrelationCoefficientSchema =
|
|
4815
|
-
var IndependentSubsetSchema =
|
|
4816
|
-
id:
|
|
4817
|
-
agentIds:
|
|
4818
|
-
independenceScore:
|
|
4819
|
-
observationCount:
|
|
3084
|
+
var CorrelationCoefficientSchema = z5.number().min(-1).max(1);
|
|
3085
|
+
var IndependentSubsetSchema = z5.object({
|
|
3086
|
+
id: z5.string(),
|
|
3087
|
+
agentIds: z5.array(z5.string()),
|
|
3088
|
+
independenceScore: z5.number().min(0).max(1),
|
|
3089
|
+
observationCount: z5.number().int().nonnegative()
|
|
4820
3090
|
});
|
|
4821
|
-
var VotingObservationSchema =
|
|
4822
|
-
proposalId:
|
|
4823
|
-
agentId:
|
|
4824
|
-
decision:
|
|
4825
|
-
confidence:
|
|
4826
|
-
alignedWithOutcome:
|
|
4827
|
-
timestamp:
|
|
3091
|
+
var VotingObservationSchema = z5.object({
|
|
3092
|
+
proposalId: z5.string(),
|
|
3093
|
+
agentId: z5.string(),
|
|
3094
|
+
decision: z5.enum(["approve", "reject", "abstain"]),
|
|
3095
|
+
confidence: z5.number().min(0).max(1),
|
|
3096
|
+
alignedWithOutcome: z5.boolean(),
|
|
3097
|
+
timestamp: z5.date()
|
|
4828
3098
|
});
|
|
4829
|
-
var PairwiseVotingHistorySchema =
|
|
4830
|
-
pairKey:
|
|
4831
|
-
jointObservations:
|
|
4832
|
-
agreements:
|
|
4833
|
-
disagreements:
|
|
3099
|
+
var PairwiseVotingHistorySchema = z5.object({
|
|
3100
|
+
pairKey: z5.string(),
|
|
3101
|
+
jointObservations: z5.number().int().nonnegative(),
|
|
3102
|
+
agreements: z5.number().int().nonnegative(),
|
|
3103
|
+
disagreements: z5.number().int().nonnegative(),
|
|
4834
3104
|
correlation: CorrelationCoefficientSchema,
|
|
4835
|
-
lastUpdated:
|
|
3105
|
+
lastUpdated: z5.date()
|
|
4836
3106
|
});
|
|
4837
|
-
var HigherOrderVotingConfigSchema =
|
|
4838
|
-
minObservationsForCorrelation:
|
|
4839
|
-
correlationThreshold:
|
|
4840
|
-
correlationMaxAgeMs:
|
|
3107
|
+
var HigherOrderVotingConfigSchema = z5.object({
|
|
3108
|
+
minObservationsForCorrelation: z5.number().int().positive().default(10),
|
|
3109
|
+
correlationThreshold: z5.number().min(0).max(1).default(0.3),
|
|
3110
|
+
correlationMaxAgeMs: z5.number().int().positive().default(864e5),
|
|
4841
3111
|
// 24 hours
|
|
4842
|
-
independenceThreshold:
|
|
4843
|
-
fallbackToSimpleVoting:
|
|
4844
|
-
observationDecayFactor:
|
|
4845
|
-
maxObservationsPerAgent:
|
|
4846
|
-
maxProposals:
|
|
4847
|
-
maxTrackedPairs:
|
|
3112
|
+
independenceThreshold: z5.number().min(0).max(1).default(0.2),
|
|
3113
|
+
fallbackToSimpleVoting: z5.boolean().default(true),
|
|
3114
|
+
observationDecayFactor: z5.number().min(0).max(1).default(0.95),
|
|
3115
|
+
maxObservationsPerAgent: z5.number().int().positive().default(1e3),
|
|
3116
|
+
maxProposals: z5.number().int().positive().default(5e3),
|
|
3117
|
+
maxTrackedPairs: z5.number().int().positive().default(100)
|
|
4848
3118
|
});
|
|
4849
3119
|
var DEFAULT_HIGHER_ORDER_CONFIG = {
|
|
4850
3120
|
minObservationsForCorrelation: 10,
|
|
@@ -4858,25 +3128,25 @@ var DEFAULT_HIGHER_ORDER_CONFIG = {
|
|
|
4858
3128
|
maxProposals: 5e3,
|
|
4859
3129
|
maxTrackedPairs: 100
|
|
4860
3130
|
};
|
|
4861
|
-
var HigherOrderVotingResultSchema =
|
|
4862
|
-
decision:
|
|
4863
|
-
posteriorApproval:
|
|
4864
|
-
posteriorRejection:
|
|
4865
|
-
effectiveVoteCount:
|
|
4866
|
-
usedCorrelationData:
|
|
4867
|
-
method:
|
|
4868
|
-
improvementOverBaseline:
|
|
4869
|
-
independentSubsets:
|
|
4870
|
-
downweightedAgents:
|
|
4871
|
-
reasoning:
|
|
3131
|
+
var HigherOrderVotingResultSchema = z5.object({
|
|
3132
|
+
decision: z5.enum(["approve", "reject", "no_consensus"]),
|
|
3133
|
+
posteriorApproval: z5.number().min(0).max(1),
|
|
3134
|
+
posteriorRejection: z5.number().min(0).max(1),
|
|
3135
|
+
effectiveVoteCount: z5.number().nonnegative(),
|
|
3136
|
+
usedCorrelationData: z5.boolean(),
|
|
3137
|
+
method: z5.enum(["ow", "isp", "simple"]),
|
|
3138
|
+
improvementOverBaseline: z5.number(),
|
|
3139
|
+
independentSubsets: z5.array(IndependentSubsetSchema).optional(),
|
|
3140
|
+
downweightedAgents: z5.array(z5.string()),
|
|
3141
|
+
reasoning: z5.string()
|
|
4872
3142
|
});
|
|
4873
|
-
var CorrelationTrackerStatsSchema =
|
|
4874
|
-
totalAgents:
|
|
4875
|
-
trackedPairs:
|
|
4876
|
-
totalObservations:
|
|
4877
|
-
averageCorrelation:
|
|
4878
|
-
independentSubsetCount:
|
|
4879
|
-
pairsWithSufficientData:
|
|
3143
|
+
var CorrelationTrackerStatsSchema = z5.object({
|
|
3144
|
+
totalAgents: z5.number().int().nonnegative(),
|
|
3145
|
+
trackedPairs: z5.number().int().nonnegative(),
|
|
3146
|
+
totalObservations: z5.number().int().nonnegative(),
|
|
3147
|
+
averageCorrelation: z5.number(),
|
|
3148
|
+
independentSubsetCount: z5.number().int().nonnegative(),
|
|
3149
|
+
pairsWithSufficientData: z5.number().int().nonnegative()
|
|
4880
3150
|
});
|
|
4881
3151
|
|
|
4882
3152
|
// src/consensus/higher-order-helpers.ts
|
|
@@ -7564,7 +5834,7 @@ function createCorrelationTracker(config) {
|
|
|
7564
5834
|
|
|
7565
5835
|
// src/consensus/correlation-persistence.ts
|
|
7566
5836
|
import * as fs from "fs";
|
|
7567
|
-
import { z as
|
|
5837
|
+
import { z as z6 } from "zod";
|
|
7568
5838
|
var logger6 = createLogger({ component: "correlation-persistence" });
|
|
7569
5839
|
var VOTING_SUBDIR = "voting";
|
|
7570
5840
|
var CORRELATIONS_FILE = "correlations.json";
|
|
@@ -7572,21 +5842,21 @@ var CORRELATIONS_JSONL_FILE = "correlations.jsonl";
|
|
|
7572
5842
|
var FILE_MODE = 384;
|
|
7573
5843
|
var DIR_MODE = 448;
|
|
7574
5844
|
var SCHEMA_VERSION = 2;
|
|
7575
|
-
var PersistedVoteSchema =
|
|
7576
|
-
agentId:
|
|
7577
|
-
decision:
|
|
7578
|
-
confidence:
|
|
5845
|
+
var PersistedVoteSchema = z6.object({
|
|
5846
|
+
agentId: z6.string(),
|
|
5847
|
+
decision: z6.enum(["approve", "reject", "abstain"]),
|
|
5848
|
+
confidence: z6.number().min(0).max(1)
|
|
7579
5849
|
});
|
|
7580
|
-
var PersistedProposalSchema =
|
|
7581
|
-
proposalId:
|
|
7582
|
-
votes:
|
|
7583
|
-
outcome:
|
|
7584
|
-
timestamp:
|
|
5850
|
+
var PersistedProposalSchema = z6.object({
|
|
5851
|
+
proposalId: z6.string(),
|
|
5852
|
+
votes: z6.array(PersistedVoteSchema),
|
|
5853
|
+
outcome: z6.enum(["approved", "rejected"]),
|
|
5854
|
+
timestamp: z6.iso.datetime()
|
|
7585
5855
|
});
|
|
7586
|
-
var PersistedCorrelationDataSchema =
|
|
7587
|
-
version:
|
|
7588
|
-
proposals:
|
|
7589
|
-
savedAt:
|
|
5856
|
+
var PersistedCorrelationDataSchema = z6.object({
|
|
5857
|
+
version: z6.number().int().positive(),
|
|
5858
|
+
proposals: z6.array(PersistedProposalSchema),
|
|
5859
|
+
savedAt: z6.iso.datetime()
|
|
7590
5860
|
});
|
|
7591
5861
|
function getCorrelationDataPath() {
|
|
7592
5862
|
return nexusDataPath(VOTING_SUBDIR, CORRELATIONS_FILE);
|
|
@@ -7787,9 +6057,9 @@ function createPersistedProposal(proposalId, votes, outcome) {
|
|
|
7787
6057
|
}
|
|
7788
6058
|
|
|
7789
6059
|
// src/mcp/tools/consensus-vote-types.ts
|
|
7790
|
-
import { z as
|
|
6060
|
+
import { z as z7 } from "zod";
|
|
7791
6061
|
var MAX_PROPOSAL_LENGTH = 4e3;
|
|
7792
|
-
var VotingStrategySchema =
|
|
6062
|
+
var VotingStrategySchema = z7.enum([
|
|
7793
6063
|
"simple_majority",
|
|
7794
6064
|
"supermajority",
|
|
7795
6065
|
"unanimous",
|
|
@@ -7797,8 +6067,8 @@ var VotingStrategySchema = z9.enum([
|
|
|
7797
6067
|
"higher_order",
|
|
7798
6068
|
"opinion_wise"
|
|
7799
6069
|
]);
|
|
7800
|
-
var ErrorPolicySchema =
|
|
7801
|
-
var VoteThresholdSchema =
|
|
6070
|
+
var ErrorPolicySchema = z7.enum(["reduce_denominator", "count_as_abstain", "fail_closed"]);
|
|
6071
|
+
var VoteThresholdSchema = z7.enum(["majority", "supermajority", "unanimous"]);
|
|
7802
6072
|
var ERROR_FLOOR_FRACTION = 0.5;
|
|
7803
6073
|
function getDefaultErrorPolicy(strategy) {
|
|
7804
6074
|
if (strategy === "unanimous") {
|
|
@@ -7806,8 +6076,8 @@ function getDefaultErrorPolicy(strategy) {
|
|
|
7806
6076
|
}
|
|
7807
6077
|
return "reduce_denominator";
|
|
7808
6078
|
}
|
|
7809
|
-
var ConsensusVoteInputSchema =
|
|
7810
|
-
proposal:
|
|
6079
|
+
var ConsensusVoteInputSchema = z7.object({
|
|
6080
|
+
proposal: z7.string().min(1).max(MAX_PROPOSAL_LENGTH).describe("Proposal text to vote on"),
|
|
7811
6081
|
threshold: VoteThresholdSchema.optional().describe(
|
|
7812
6082
|
"Voting threshold (legacy): majority, supermajority, unanimous. Use strategy instead."
|
|
7813
6083
|
),
|
|
@@ -7817,8 +6087,8 @@ var ConsensusVoteInputSchema = z9.object({
|
|
|
7817
6087
|
errorPolicy: ErrorPolicySchema.optional().describe(
|
|
7818
6088
|
"How to treat voters that errored or timed out (#2630). Default: fail_closed for unanimous only; reduce_denominator for all other strategies incl. higher_order/opinion_wise (#3138 \u2014 a single infra timeout should not void an otherwise-unanimous vote). Regardless of policy, errors > 50% always fails."
|
|
7819
6089
|
),
|
|
7820
|
-
quickMode:
|
|
7821
|
-
simulateVotes:
|
|
6090
|
+
quickMode: z7.boolean().optional().default(false).describe("Use 3 agents instead of the full 7-role panel for faster execution"),
|
|
6091
|
+
simulateVotes: z7.boolean().optional().default(false).describe(
|
|
7822
6092
|
"TESTS ONLY \u2014 when true, voters return random decisions. Output must not be used for real decisions. (#2319)"
|
|
7823
6093
|
),
|
|
7824
6094
|
/**
|
|
@@ -7838,7 +6108,7 @@ var ConsensusVoteInputSchema = z9.object({
|
|
|
7838
6108
|
* Kept optional (no `.default()`) so the inferred type doesn't force
|
|
7839
6109
|
* `mode: 'sync'` on every existing call site / test fixture.
|
|
7840
6110
|
*/
|
|
7841
|
-
mode:
|
|
6111
|
+
mode: z7.enum(["sync", "async"]).optional().describe(
|
|
7842
6112
|
'Dispatch mode (default: sync). Use "async" for higher-order strategies with 7 voters.'
|
|
7843
6113
|
),
|
|
7844
6114
|
/**
|
|
@@ -7847,7 +6117,7 @@ var ConsensusVoteInputSchema = z9.object({
|
|
|
7847
6117
|
* same key with different inputs fails closed with
|
|
7848
6118
|
* `idempotency_key_collision`. Sync mode ignores this.
|
|
7849
6119
|
*/
|
|
7850
|
-
idempotencyKey:
|
|
6120
|
+
idempotencyKey: z7.string().min(1).max(256).optional().describe(
|
|
7851
6121
|
"Replay-safe key for async-mode dispatch (#3042 Stage 1c). Same (key, inputs) returns existing jobId."
|
|
7852
6122
|
)
|
|
7853
6123
|
});
|
|
@@ -7954,7 +6224,7 @@ function applyErrorPolicy(votes, policy) {
|
|
|
7954
6224
|
}
|
|
7955
6225
|
|
|
7956
6226
|
// src/orchestration/outcomes/outcome-store-persistence.ts
|
|
7957
|
-
import { appendFileSync as
|
|
6227
|
+
import { appendFileSync as appendFileSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
7958
6228
|
var PersistentOutcomeStore = class extends OutcomeStore {
|
|
7959
6229
|
filePath;
|
|
7960
6230
|
logger;
|
|
@@ -8005,7 +6275,7 @@ var PersistentOutcomeStore = class extends OutcomeStore {
|
|
|
8005
6275
|
}
|
|
8006
6276
|
}
|
|
8007
6277
|
hydrate() {
|
|
8008
|
-
if (!
|
|
6278
|
+
if (!existsSync2(this.filePath)) {
|
|
8009
6279
|
this.logger.debug("No outcomes file found, starting fresh", {
|
|
8010
6280
|
path: this.filePath
|
|
8011
6281
|
});
|
|
@@ -8064,7 +6334,7 @@ var PersistentOutcomeStore = class extends OutcomeStore {
|
|
|
8064
6334
|
}
|
|
8065
6335
|
persistLine(outcome) {
|
|
8066
6336
|
try {
|
|
8067
|
-
|
|
6337
|
+
appendFileSync2(this.filePath, JSON.stringify(outcome) + "\n", "utf-8");
|
|
8068
6338
|
} catch (error) {
|
|
8069
6339
|
const msg = getErrorMessage(error);
|
|
8070
6340
|
this.logger.warn("Failed to persist outcome to disk", {
|
|
@@ -8190,384 +6460,39 @@ function warnIfSimulatedOutsideTests(toolName, logger11) {
|
|
|
8190
6460
|
);
|
|
8191
6461
|
}
|
|
8192
6462
|
|
|
8193
|
-
// src/mcp/tool-annotations.ts
|
|
8194
|
-
var TOOL_ANNOTATIONS = {
|
|
8195
|
-
// ============================================================================
|
|
8196
|
-
// Orchestration & expert lifecycle
|
|
8197
|
-
// ============================================================================
|
|
8198
|
-
/** Orchestrator coordinates expert agents; spawns workers that may mutate state. */
|
|
8199
|
-
orchestrate: {
|
|
8200
|
-
readOnlyHint: false,
|
|
8201
|
-
destructiveHint: false,
|
|
8202
|
-
idempotentHint: false,
|
|
8203
|
-
openWorldHint: true
|
|
8204
|
-
},
|
|
8205
|
-
/** Creates an expert and adds it to the in-memory expert registry. */
|
|
8206
|
-
create_expert: {
|
|
8207
|
-
readOnlyHint: false,
|
|
8208
|
-
destructiveHint: false,
|
|
8209
|
-
idempotentHint: false,
|
|
8210
|
-
openWorldHint: false
|
|
8211
|
-
},
|
|
8212
|
-
/** Executes a previously created expert; invokes external CLIs. */
|
|
8213
|
-
execute_expert: {
|
|
8214
|
-
readOnlyHint: false,
|
|
8215
|
-
destructiveHint: false,
|
|
8216
|
-
idempotentHint: false,
|
|
8217
|
-
openWorldHint: true
|
|
8218
|
-
},
|
|
8219
|
-
// ============================================================================
|
|
8220
|
-
// Workflow / pipeline execution
|
|
8221
|
-
// ============================================================================
|
|
8222
|
-
/** Runs a workflow template; steps may write to the registry or filesystem. */
|
|
8223
|
-
run_workflow: {
|
|
8224
|
-
readOnlyHint: false,
|
|
8225
|
-
destructiveHint: false,
|
|
8226
|
-
idempotentHint: false,
|
|
8227
|
-
openWorldHint: true
|
|
8228
|
-
},
|
|
8229
|
-
/** Runs a graph-based workflow with checkpointing. */
|
|
8230
|
-
run_graph_workflow: {
|
|
8231
|
-
readOnlyHint: false,
|
|
8232
|
-
destructiveHint: false,
|
|
8233
|
-
idempotentHint: false,
|
|
8234
|
-
openWorldHint: true
|
|
8235
|
-
},
|
|
8236
|
-
/** Runs a pipeline plugin by name. */
|
|
8237
|
-
run_pipeline: {
|
|
8238
|
-
readOnlyHint: false,
|
|
8239
|
-
destructiveHint: false,
|
|
8240
|
-
idempotentHint: false,
|
|
8241
|
-
openWorldHint: true
|
|
8242
|
-
},
|
|
8243
|
-
/** Full dev pipeline: research → plan → vote → implement → QA. */
|
|
8244
|
-
run_dev_pipeline: {
|
|
8245
|
-
readOnlyHint: false,
|
|
8246
|
-
destructiveHint: false,
|
|
8247
|
-
idempotentHint: false,
|
|
8248
|
-
openWorldHint: true
|
|
8249
|
-
},
|
|
8250
|
-
/** Executes an AI-software-factory spec pipeline. */
|
|
8251
|
-
execute_spec: {
|
|
8252
|
-
readOnlyHint: false,
|
|
8253
|
-
destructiveHint: false,
|
|
8254
|
-
idempotentHint: false,
|
|
8255
|
-
openWorldHint: true
|
|
8256
|
-
},
|
|
8257
|
-
// ============================================================================
|
|
8258
|
-
// Voting / decision
|
|
8259
|
-
// ============================================================================
|
|
8260
|
-
/** Records vote outcomes to the audit log; doesn't write to repo state. */
|
|
8261
|
-
consensus_vote: {
|
|
8262
|
-
readOnlyHint: false,
|
|
8263
|
-
destructiveHint: false,
|
|
8264
|
-
idempotentHint: false,
|
|
8265
|
-
openWorldHint: true
|
|
8266
|
-
},
|
|
8267
|
-
/** Per-axis tradeoff vote; records outcome to the store. */
|
|
8268
|
-
supply_chain_tradeoff_panel: {
|
|
8269
|
-
readOnlyHint: false,
|
|
8270
|
-
destructiveHint: false,
|
|
8271
|
-
idempotentHint: false,
|
|
8272
|
-
openWorldHint: true
|
|
8273
|
-
},
|
|
8274
|
-
/** Multi-voter PR review; may write review comments. */
|
|
8275
|
-
pr_review: {
|
|
8276
|
-
readOnlyHint: false,
|
|
8277
|
-
destructiveHint: false,
|
|
8278
|
-
idempotentHint: false,
|
|
8279
|
-
openWorldHint: true
|
|
8280
|
-
},
|
|
8281
|
-
// ============================================================================
|
|
8282
|
-
// Routing & introspection (read-only)
|
|
8283
|
-
// ============================================================================
|
|
8284
|
-
/** Returns a routing recommendation; doesn't execute. */
|
|
8285
|
-
delegate_to_model: {
|
|
8286
|
-
readOnlyHint: true,
|
|
8287
|
-
destructiveHint: false,
|
|
8288
|
-
idempotentHint: true,
|
|
8289
|
-
openWorldHint: false
|
|
8290
|
-
},
|
|
8291
|
-
/** Lists registered expert types. */
|
|
8292
|
-
list_experts: {
|
|
8293
|
-
readOnlyHint: true,
|
|
8294
|
-
destructiveHint: false,
|
|
8295
|
-
idempotentHint: true,
|
|
8296
|
-
openWorldHint: false
|
|
8297
|
-
},
|
|
8298
|
-
/** Lists registered workflow templates. */
|
|
8299
|
-
list_workflows: {
|
|
8300
|
-
readOnlyHint: true,
|
|
8301
|
-
destructiveHint: false,
|
|
8302
|
-
idempotentHint: true,
|
|
8303
|
-
openWorldHint: false
|
|
8304
|
-
},
|
|
8305
|
-
// ============================================================================
|
|
8306
|
-
// Research registry — reads
|
|
8307
|
-
// ============================================================================
|
|
8308
|
-
/** Queries the research registry. */
|
|
8309
|
-
research_query: {
|
|
8310
|
-
readOnlyHint: true,
|
|
8311
|
-
destructiveHint: false,
|
|
8312
|
-
idempotentHint: true,
|
|
8313
|
-
openWorldHint: false
|
|
8314
|
-
},
|
|
8315
|
-
/** Analyzes registry for gaps, trends, coverage. */
|
|
8316
|
-
research_analyze: {
|
|
8317
|
-
readOnlyHint: true,
|
|
8318
|
-
destructiveHint: false,
|
|
8319
|
-
idempotentHint: true,
|
|
8320
|
-
openWorldHint: false
|
|
8321
|
-
},
|
|
8322
|
-
/** Synthesizes registry into topic clusters. */
|
|
8323
|
-
research_synthesize: {
|
|
8324
|
-
readOnlyHint: true,
|
|
8325
|
-
destructiveHint: false,
|
|
8326
|
-
idempotentHint: true,
|
|
8327
|
-
openWorldHint: false
|
|
8328
|
-
},
|
|
8329
|
-
/** Discovers research items via external APIs; doesn't mutate registry. */
|
|
8330
|
-
research_discover: {
|
|
8331
|
-
readOnlyHint: true,
|
|
8332
|
-
destructiveHint: false,
|
|
8333
|
-
idempotentHint: true,
|
|
8334
|
-
openWorldHint: true
|
|
8335
|
-
},
|
|
8336
|
-
// ============================================================================
|
|
8337
|
-
// Research registry — mutations
|
|
8338
|
-
// ============================================================================
|
|
8339
|
-
/** Adds an arXiv paper to the registry. Dedup-checked but first call has effects. */
|
|
8340
|
-
research_add: {
|
|
8341
|
-
readOnlyHint: false,
|
|
8342
|
-
destructiveHint: false,
|
|
8343
|
-
idempotentHint: false,
|
|
8344
|
-
openWorldHint: true
|
|
8345
|
-
},
|
|
8346
|
-
/** Adds a non-paper source to the registry. */
|
|
8347
|
-
research_add_source: {
|
|
8348
|
-
readOnlyHint: false,
|
|
8349
|
-
destructiveHint: false,
|
|
8350
|
-
idempotentHint: false,
|
|
8351
|
-
openWorldHint: true
|
|
8352
|
-
},
|
|
8353
|
-
/** Reviews auto-cataloged research references; may approve/dismiss. */
|
|
8354
|
-
research_catalog_review: {
|
|
8355
|
-
readOnlyHint: false,
|
|
8356
|
-
destructiveHint: false,
|
|
8357
|
-
idempotentHint: false,
|
|
8358
|
-
openWorldHint: false
|
|
8359
|
-
},
|
|
8360
|
-
// ============================================================================
|
|
8361
|
-
// Transient lookups (no registry persistence)
|
|
8362
|
-
// ============================================================================
|
|
8363
|
-
/** Transient OSS search via GitHub API; no persistence. */
|
|
8364
|
-
survey_oss_landscape: {
|
|
8365
|
-
readOnlyHint: true,
|
|
8366
|
-
destructiveHint: false,
|
|
8367
|
-
idempotentHint: true,
|
|
8368
|
-
openWorldHint: true
|
|
8369
|
-
},
|
|
8370
|
-
/** Static lookup against curated vendor seed data. */
|
|
8371
|
-
vendor_publishing_audit: {
|
|
8372
|
-
readOnlyHint: true,
|
|
8373
|
-
destructiveHint: false,
|
|
8374
|
-
idempotentHint: true,
|
|
8375
|
-
openWorldHint: false
|
|
8376
|
-
},
|
|
8377
|
-
/** Diffs two YAML/JSON files; local-only. */
|
|
8378
|
-
compare_data_feeds: {
|
|
8379
|
-
readOnlyHint: true,
|
|
8380
|
-
destructiveHint: false,
|
|
8381
|
-
idempotentHint: true,
|
|
8382
|
-
openWorldHint: false
|
|
8383
|
-
},
|
|
8384
|
-
// ============================================================================
|
|
8385
|
-
// Memory
|
|
8386
|
-
// ============================================================================
|
|
8387
|
-
/** Reads from memory backends. */
|
|
8388
|
-
memory_query: {
|
|
8389
|
-
readOnlyHint: true,
|
|
8390
|
-
destructiveHint: false,
|
|
8391
|
-
idempotentHint: true,
|
|
8392
|
-
openWorldHint: false
|
|
8393
|
-
},
|
|
8394
|
-
/** Returns memory statistics. */
|
|
8395
|
-
memory_stats: {
|
|
8396
|
-
readOnlyHint: true,
|
|
8397
|
-
destructiveHint: false,
|
|
8398
|
-
idempotentHint: true,
|
|
8399
|
-
openWorldHint: false
|
|
8400
|
-
},
|
|
8401
|
-
/** Writes to a memory backend. */
|
|
8402
|
-
memory_write: {
|
|
8403
|
-
readOnlyHint: false,
|
|
8404
|
-
destructiveHint: false,
|
|
8405
|
-
idempotentHint: false,
|
|
8406
|
-
openWorldHint: false
|
|
8407
|
-
},
|
|
8408
|
-
// ============================================================================
|
|
8409
|
-
// Observability
|
|
8410
|
-
// ============================================================================
|
|
8411
|
-
/** Multi-CLI performance weather report; reads outcome store. */
|
|
8412
|
-
weather_report: {
|
|
8413
|
-
readOnlyHint: true,
|
|
8414
|
-
destructiveHint: false,
|
|
8415
|
-
idempotentHint: true,
|
|
8416
|
-
openWorldHint: false
|
|
8417
|
-
},
|
|
8418
|
-
/** Queries execution traces from disk. */
|
|
8419
|
-
query_trace: {
|
|
8420
|
-
readOnlyHint: true,
|
|
8421
|
-
destructiveHint: false,
|
|
8422
|
-
idempotentHint: true,
|
|
8423
|
-
openWorldHint: false
|
|
8424
|
-
},
|
|
8425
|
-
/** Queries structured task-state log. */
|
|
8426
|
-
query_task_state: {
|
|
8427
|
-
readOnlyHint: true,
|
|
8428
|
-
destructiveHint: false,
|
|
8429
|
-
idempotentHint: true,
|
|
8430
|
-
openWorldHint: false
|
|
8431
|
-
},
|
|
8432
|
-
/** Reads job-result sidecar file for an async-mode dispatch (#3042 / #2631). */
|
|
8433
|
-
get_job_result: {
|
|
8434
|
-
readOnlyHint: true,
|
|
8435
|
-
destructiveHint: false,
|
|
8436
|
-
idempotentHint: true,
|
|
8437
|
-
openWorldHint: false
|
|
8438
|
-
},
|
|
8439
|
-
/** Lists async-mode job summaries from the sidecar dir (#3046 / #2631 Stage 5). */
|
|
8440
|
-
list_jobs: {
|
|
8441
|
-
readOnlyHint: true,
|
|
8442
|
-
destructiveHint: false,
|
|
8443
|
-
idempotentHint: true,
|
|
8444
|
-
openWorldHint: false
|
|
8445
|
-
},
|
|
8446
|
-
/** Cancels an in-flight async-mode job (#3042 Stage 1b / #2631). */
|
|
8447
|
-
cancel_job: {
|
|
8448
|
-
// Mutates the sidecar record — NOT read-only.
|
|
8449
|
-
readOnlyHint: false,
|
|
8450
|
-
// Reversibility: cancel writes a new state, but doesn't delete data —
|
|
8451
|
-
// not destructive in the OWASP sense (no irreversible data loss).
|
|
8452
|
-
destructiveHint: false,
|
|
8453
|
-
// Idempotent: repeat cancels are no-ops with explicit outcome envelope.
|
|
8454
|
-
idempotentHint: true,
|
|
8455
|
-
openWorldHint: false
|
|
8456
|
-
},
|
|
8457
|
-
/** Verifies hash chain of audit log files. */
|
|
8458
|
-
verify_audit_chain: {
|
|
8459
|
-
readOnlyHint: true,
|
|
8460
|
-
destructiveHint: false,
|
|
8461
|
-
idempotentHint: true,
|
|
8462
|
-
openWorldHint: false
|
|
8463
|
-
},
|
|
8464
|
-
/** CI infrastructure health check — composes status-page + recent-runs signals (#3076). */
|
|
8465
|
-
ci_health_check: {
|
|
8466
|
-
readOnlyHint: true,
|
|
8467
|
-
destructiveHint: false,
|
|
8468
|
-
idempotentHint: true,
|
|
8469
|
-
// Hits external services (githubstatus.com + GitHub API) — `openWorldHint`.
|
|
8470
|
-
openWorldHint: true
|
|
8471
|
-
},
|
|
8472
|
-
/** Threshold-gated observability loop; may file GitHub issues when fileIssues=true. */
|
|
8473
|
-
improvement_review: {
|
|
8474
|
-
readOnlyHint: false,
|
|
8475
|
-
destructiveHint: false,
|
|
8476
|
-
idempotentHint: false,
|
|
8477
|
-
openWorldHint: true
|
|
8478
|
-
},
|
|
8479
|
-
// ============================================================================
|
|
8480
|
-
// Repository analysis
|
|
8481
|
-
// ============================================================================
|
|
8482
|
-
/** Analyzes a GitHub repository structure; doesn't write. */
|
|
8483
|
-
repo_analyze: {
|
|
8484
|
-
readOnlyHint: true,
|
|
8485
|
-
destructiveHint: false,
|
|
8486
|
-
idempotentHint: true,
|
|
8487
|
-
openWorldHint: true
|
|
8488
|
-
},
|
|
8489
|
-
/** Generates a security-scanning pipeline plan; returns plan, doesn't write it. */
|
|
8490
|
-
repo_security_plan: {
|
|
8491
|
-
readOnlyHint: true,
|
|
8492
|
-
destructiveHint: false,
|
|
8493
|
-
idempotentHint: true,
|
|
8494
|
-
openWorldHint: true
|
|
8495
|
-
},
|
|
8496
|
-
/** Extracts code symbols from source files. */
|
|
8497
|
-
extract_symbols: {
|
|
8498
|
-
readOnlyHint: true,
|
|
8499
|
-
destructiveHint: false,
|
|
8500
|
-
idempotentHint: true,
|
|
8501
|
-
openWorldHint: false
|
|
8502
|
-
},
|
|
8503
|
-
/** Searches codebase for patterns or symbols. */
|
|
8504
|
-
search_codebase: {
|
|
8505
|
-
readOnlyHint: true,
|
|
8506
|
-
destructiveHint: false,
|
|
8507
|
-
idempotentHint: true,
|
|
8508
|
-
openWorldHint: false
|
|
8509
|
-
},
|
|
8510
|
-
// ============================================================================
|
|
8511
|
-
// Issue triage & registry imports
|
|
8512
|
-
// ============================================================================
|
|
8513
|
-
/** Triages a GitHub issue; may write labels/comments when authorized. */
|
|
8514
|
-
issue_triage: {
|
|
8515
|
-
readOnlyHint: false,
|
|
8516
|
-
destructiveHint: false,
|
|
8517
|
-
idempotentHint: false,
|
|
8518
|
-
openWorldHint: true
|
|
8519
|
-
},
|
|
8520
|
-
/** Generates a draft model-registry entry; doesn't write. */
|
|
8521
|
-
registry_import: {
|
|
8522
|
-
readOnlyHint: true,
|
|
8523
|
-
destructiveHint: false,
|
|
8524
|
-
idempotentHint: true,
|
|
8525
|
-
openWorldHint: true
|
|
8526
|
-
}
|
|
8527
|
-
};
|
|
8528
|
-
function getToolAnnotations(name) {
|
|
8529
|
-
const a = TOOL_ANNOTATIONS[name];
|
|
8530
|
-
if (a === void 0) {
|
|
8531
|
-
throw new Error(
|
|
8532
|
-
`getToolAnnotations: no entry for tool "${name}". Add it to TOOL_ANNOTATIONS in src/mcp/tool-annotations.ts (#2648).`
|
|
8533
|
-
);
|
|
8534
|
-
}
|
|
8535
|
-
return a;
|
|
8536
|
-
}
|
|
8537
|
-
|
|
8538
6463
|
// src/mcp/jobs/job-result-store.ts
|
|
8539
|
-
import { existsSync as
|
|
8540
|
-
import { z as
|
|
6464
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, writeFileSync as writeFileSync3 } from "fs";
|
|
6465
|
+
import { z as z8 } from "zod";
|
|
8541
6466
|
var logger8 = createLogger({ component: "job-result-store" });
|
|
8542
|
-
var JobStatusSchema =
|
|
8543
|
-
var JobResultSchema =
|
|
6467
|
+
var JobStatusSchema = z8.enum(["pending", "complete", "failed", "cancelled"]);
|
|
6468
|
+
var JobResultSchema = z8.object({
|
|
8544
6469
|
/** Format version. Currently `1` — bump if the shape changes. */
|
|
8545
|
-
v:
|
|
8546
|
-
jobId:
|
|
6470
|
+
v: z8.literal(1),
|
|
6471
|
+
jobId: z8.string().min(1),
|
|
8547
6472
|
/** Tool that was invoked (e.g. `orchestrate`). */
|
|
8548
|
-
toolName:
|
|
6473
|
+
toolName: z8.string().min(1),
|
|
8549
6474
|
status: JobStatusSchema,
|
|
8550
|
-
createdAt:
|
|
6475
|
+
createdAt: z8.iso.datetime(),
|
|
8551
6476
|
/** Set when the job leaves `pending` — either via `complete` or `failed` or `cancelled`. */
|
|
8552
|
-
completedAt:
|
|
6477
|
+
completedAt: z8.iso.datetime().optional(),
|
|
8553
6478
|
/**
|
|
8554
6479
|
* Structured payload the synchronous mode would have returned. Present
|
|
8555
6480
|
* only when `status === 'complete'`. Shape is tool-specific — readers
|
|
8556
6481
|
* cast to the tool's known output type after status check.
|
|
8557
6482
|
*/
|
|
8558
|
-
result:
|
|
6483
|
+
result: z8.unknown().optional(),
|
|
8559
6484
|
/**
|
|
8560
6485
|
* Failure message when `status === 'failed'`. Cannot be paired with
|
|
8561
6486
|
* `result` — the discriminator is `status`.
|
|
8562
6487
|
*/
|
|
8563
|
-
error:
|
|
6488
|
+
error: z8.string().optional()
|
|
8564
6489
|
});
|
|
8565
6490
|
function jobResultPath(jobId) {
|
|
8566
6491
|
return nexusDataPathEnsure("jobs", `result-${jobId}.json`);
|
|
8567
6492
|
}
|
|
8568
6493
|
function writeJobPending(jobId, toolName) {
|
|
8569
6494
|
const path = jobResultPath(jobId);
|
|
8570
|
-
if (
|
|
6495
|
+
if (existsSync3(path)) {
|
|
8571
6496
|
logger8.debug("Job result file already exists \u2014 leaving in place", { jobId });
|
|
8572
6497
|
return;
|
|
8573
6498
|
}
|
|
@@ -8622,7 +6547,7 @@ function writeJobCancelled(jobId, toolName, reason) {
|
|
|
8622
6547
|
}
|
|
8623
6548
|
function readJobResult(jobId) {
|
|
8624
6549
|
const path = nexusDataPath("jobs", `result-${jobId}.json`);
|
|
8625
|
-
if (!
|
|
6550
|
+
if (!existsSync3(path)) return null;
|
|
8626
6551
|
try {
|
|
8627
6552
|
const raw = JSON.parse(readFileSync3(path, "utf-8"));
|
|
8628
6553
|
const parsed = JobResultSchema.safeParse(raw);
|
|
@@ -8642,7 +6567,7 @@ function readJobResult(jobId) {
|
|
|
8642
6567
|
}
|
|
8643
6568
|
function listJobs() {
|
|
8644
6569
|
const dir = nexusDataPath("jobs");
|
|
8645
|
-
if (!
|
|
6570
|
+
if (!existsSync3(dir)) return [];
|
|
8646
6571
|
let entries;
|
|
8647
6572
|
try {
|
|
8648
6573
|
entries = readdirSync(dir);
|
|
@@ -8675,22 +6600,22 @@ function listJobs() {
|
|
|
8675
6600
|
}
|
|
8676
6601
|
|
|
8677
6602
|
// src/mcp/jobs/job-idempotency.ts
|
|
8678
|
-
import { createHash, randomUUID as
|
|
8679
|
-
import { existsSync as
|
|
8680
|
-
import { z as
|
|
6603
|
+
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
6604
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
6605
|
+
import { z as z9 } from "zod";
|
|
8681
6606
|
var logger9 = createLogger({ component: "job-idempotency" });
|
|
8682
|
-
var IdempotencyIndexEntrySchema =
|
|
8683
|
-
v:
|
|
6607
|
+
var IdempotencyIndexEntrySchema = z9.object({
|
|
6608
|
+
v: z9.literal(1),
|
|
8684
6609
|
/** MCP tool name (e.g. `orchestrate`, `run_workflow`, `consensus_vote`). */
|
|
8685
|
-
tool:
|
|
6610
|
+
tool: z9.string().min(1),
|
|
8686
6611
|
/** Caller-supplied idempotency key. */
|
|
8687
|
-
key:
|
|
6612
|
+
key: z9.string().min(1),
|
|
8688
6613
|
/** sha256 of the canonical input JSON. */
|
|
8689
|
-
inputsHash:
|
|
6614
|
+
inputsHash: z9.string().length(64),
|
|
8690
6615
|
/** Existing jobId for this (tool, key, inputs) tuple. */
|
|
8691
|
-
jobId:
|
|
6616
|
+
jobId: z9.string().min(1),
|
|
8692
6617
|
/** ISO timestamp of first dispatch. */
|
|
8693
|
-
createdAt:
|
|
6618
|
+
createdAt: z9.iso.datetime()
|
|
8694
6619
|
});
|
|
8695
6620
|
function sha256Hex(input) {
|
|
8696
6621
|
return createHash("sha256").update(input).digest("hex");
|
|
@@ -8712,13 +6637,13 @@ function indexFilePathReadOnly(tool, key) {
|
|
|
8712
6637
|
const filename = `key-${sha256Hex(`${tool}:${key}`)}.json`;
|
|
8713
6638
|
return nexusDataPath("jobs", filename);
|
|
8714
6639
|
}
|
|
8715
|
-
function resolveIdempotency(tool, idempotencyKey, inputs, randomFallback =
|
|
6640
|
+
function resolveIdempotency(tool, idempotencyKey, inputs, randomFallback = randomUUID2) {
|
|
8716
6641
|
if (idempotencyKey === void 0 || idempotencyKey === "") {
|
|
8717
6642
|
return { kind: "fresh", jobId: randomFallback() };
|
|
8718
6643
|
}
|
|
8719
6644
|
const incomingInputsHash = computeInputsHash(inputs);
|
|
8720
6645
|
const path = indexFilePathReadOnly(tool, idempotencyKey);
|
|
8721
|
-
if (
|
|
6646
|
+
if (existsSync4(path)) {
|
|
8722
6647
|
const entry = readIndexEntry(path);
|
|
8723
6648
|
if (entry !== null) {
|
|
8724
6649
|
if (entry.inputsHash === incomingInputsHash) {
|
|
@@ -8738,7 +6663,7 @@ function resolveIdempotency(tool, idempotencyKey, inputs, randomFallback = rando
|
|
|
8738
6663
|
}
|
|
8739
6664
|
function registerIdempotentJob(params) {
|
|
8740
6665
|
const path = indexFilePath(params.tool, params.idempotencyKey);
|
|
8741
|
-
if (
|
|
6666
|
+
if (existsSync4(path)) {
|
|
8742
6667
|
logger9.debug("Idempotency index entry already present; skipping write", {
|
|
8743
6668
|
tool: params.tool,
|
|
8744
6669
|
jobId: params.jobId
|
|
@@ -8862,7 +6787,7 @@ function suggestRetryAfterMs(toolName) {
|
|
|
8862
6787
|
}
|
|
8863
6788
|
|
|
8864
6789
|
// src/mcp/tools/consensus-vote.ts
|
|
8865
|
-
import { randomUUID as
|
|
6790
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
8866
6791
|
var persistentCorrelationTracker;
|
|
8867
6792
|
function getOrCreateCorrelationTracker() {
|
|
8868
6793
|
persistentCorrelationTracker ??= createPersistentCorrelationTracker();
|
|
@@ -9043,7 +6968,7 @@ async function processVotesWithCascade(engineVotes, opts) {
|
|
|
9043
6968
|
var CONTRARIAN_ESCALATION_THRESHOLD = 0.8;
|
|
9044
6969
|
async function runContrarianCheck(proposal, log) {
|
|
9045
6970
|
try {
|
|
9046
|
-
const { executeExpert } = await import("./expert-bridge-
|
|
6971
|
+
const { executeExpert } = await import("./expert-bridge-N4L47MHU.js");
|
|
9047
6972
|
const prompt = [
|
|
9048
6973
|
"You are a contrarian analyst. Your job is to find reasons this proposal should be REJECTED.",
|
|
9049
6974
|
"Look for: YAGNI (not needed), MISALIGNED (wrong tech/architecture), SECURITY_RISK, SCOPE_CREEP.",
|
|
@@ -9237,7 +7162,7 @@ function dispatchAsyncConsensusVote(deps, args) {
|
|
|
9237
7162
|
tool: "consensus_vote",
|
|
9238
7163
|
idempotencyKey: args.idempotencyKey,
|
|
9239
7164
|
inputs: args,
|
|
9240
|
-
freshJobId: () => `job-vote-${
|
|
7165
|
+
freshJobId: () => `job-vote-${randomUUID3()}`,
|
|
9241
7166
|
replayEnvelope: buildConsensusVoteReplayEnvelope,
|
|
9242
7167
|
collisionEnvelope: buildConsensusVoteCollisionEnvelope
|
|
9243
7168
|
});
|
|
@@ -9348,27 +7273,27 @@ function createConsensusVoteHandler(deps) {
|
|
|
9348
7273
|
};
|
|
9349
7274
|
}
|
|
9350
7275
|
var CONSENSUS_VOTE_OUTPUT_SCHEMA = {
|
|
9351
|
-
proposal:
|
|
7276
|
+
proposal: z10.string(),
|
|
9352
7277
|
strategy: VotingStrategySchema,
|
|
9353
|
-
decision:
|
|
9354
|
-
approvalPercentage:
|
|
9355
|
-
voteCounts:
|
|
9356
|
-
approve:
|
|
9357
|
-
reject:
|
|
9358
|
-
abstain:
|
|
9359
|
-
error:
|
|
7278
|
+
decision: z10.enum(["approved", "rejected", "no_quorum"]),
|
|
7279
|
+
approvalPercentage: z10.number(),
|
|
7280
|
+
voteCounts: z10.object({
|
|
7281
|
+
approve: z10.number(),
|
|
7282
|
+
reject: z10.number(),
|
|
7283
|
+
abstain: z10.number(),
|
|
7284
|
+
error: z10.number()
|
|
9360
7285
|
}),
|
|
9361
|
-
votes:
|
|
9362
|
-
|
|
9363
|
-
role:
|
|
9364
|
-
decision:
|
|
9365
|
-
confidence:
|
|
9366
|
-
reasoning:
|
|
9367
|
-
simulated:
|
|
9368
|
-
error:
|
|
9369
|
-
modelUsed:
|
|
9370
|
-
rejectionCategories:
|
|
9371
|
-
|
|
7286
|
+
votes: z10.array(
|
|
7287
|
+
z10.object({
|
|
7288
|
+
role: z10.string().max(100),
|
|
7289
|
+
decision: z10.enum(["approve", "reject", "abstain"]),
|
|
7290
|
+
confidence: z10.number(),
|
|
7291
|
+
reasoning: z10.string().max(4e3),
|
|
7292
|
+
simulated: z10.boolean(),
|
|
7293
|
+
error: z10.boolean(),
|
|
7294
|
+
modelUsed: z10.string().max(100).optional(),
|
|
7295
|
+
rejectionCategories: z10.array(
|
|
7296
|
+
z10.enum([
|
|
9372
7297
|
"YAGNI",
|
|
9373
7298
|
"DRY_VIOLATION",
|
|
9374
7299
|
"OVER_ENGINEERING",
|
|
@@ -9381,34 +7306,34 @@ var CONSENSUS_VOTE_OUTPUT_SCHEMA = {
|
|
|
9381
7306
|
})
|
|
9382
7307
|
),
|
|
9383
7308
|
threshold: VoteThresholdSchema.optional(),
|
|
9384
|
-
durationMs:
|
|
9385
|
-
simulateVotes:
|
|
9386
|
-
higherOrderMetadata:
|
|
9387
|
-
posteriorApproval:
|
|
9388
|
-
posteriorRejection:
|
|
9389
|
-
effectiveVoteCount:
|
|
9390
|
-
method:
|
|
9391
|
-
usedCorrelationData:
|
|
9392
|
-
improvementOverBaseline:
|
|
9393
|
-
downweightedAgents:
|
|
9394
|
-
reasoning:
|
|
7309
|
+
durationMs: z10.number(),
|
|
7310
|
+
simulateVotes: z10.boolean(),
|
|
7311
|
+
higherOrderMetadata: z10.object({
|
|
7312
|
+
posteriorApproval: z10.number(),
|
|
7313
|
+
posteriorRejection: z10.number(),
|
|
7314
|
+
effectiveVoteCount: z10.number(),
|
|
7315
|
+
method: z10.enum(["ow", "isp", "simple"]),
|
|
7316
|
+
usedCorrelationData: z10.boolean(),
|
|
7317
|
+
improvementOverBaseline: z10.number(),
|
|
7318
|
+
downweightedAgents: z10.array(z10.string().max(100)).max(10),
|
|
7319
|
+
reasoning: z10.string().max(2e3)
|
|
9395
7320
|
}).optional(),
|
|
9396
7321
|
// #3124: explains a `rejected` decision that coexists with a high
|
|
9397
7322
|
// approvalPercentage (an error-policy short-circuit, e.g. fail_closed).
|
|
9398
|
-
policyReason:
|
|
7323
|
+
policyReason: z10.string().max(200).optional()
|
|
9399
7324
|
};
|
|
9400
7325
|
function registerConsensusVoteTool(server, deps) {
|
|
9401
7326
|
const logger11 = deps.logger ?? createLogger({ tool: "consensus_vote" });
|
|
9402
7327
|
const notifier = deps.notifier ?? createMcpNotifier(server);
|
|
9403
7328
|
const depsWithNotifier = { ...deps, notifier };
|
|
9404
7329
|
const toolSchema = {
|
|
9405
|
-
proposal:
|
|
9406
|
-
threshold:
|
|
7330
|
+
proposal: z10.string().min(1).max(MAX_PROPOSAL_LENGTH).describe("Proposal text to vote on"),
|
|
7331
|
+
threshold: z10.enum(["majority", "supermajority", "unanimous"]).optional().describe("Voting threshold (legacy). Use strategy instead."),
|
|
9407
7332
|
strategy: VotingStrategySchema.optional().describe(
|
|
9408
7333
|
"Voting strategy: simple_majority (default), supermajority, unanimous, proof_of_learning, or higher_order"
|
|
9409
7334
|
),
|
|
9410
|
-
quickMode:
|
|
9411
|
-
simulateVotes:
|
|
7335
|
+
quickMode: z10.boolean().optional().default(false).describe("Use 3 agents instead of 7"),
|
|
7336
|
+
simulateVotes: z10.boolean().optional().default(false).describe("TESTS ONLY \u2014 random output, must not be used for real decisions (#2319)")
|
|
9412
7337
|
};
|
|
9413
7338
|
const description = "Execute multi-model consensus voting on a proposal. Uses 7 specialized agent roles (architect, security, devex, ai_ml, pm, catfish, scope_steward) to vote on proposals with configurable strategies. Supports higher_order strategy for Bayesian-optimal aggregation with correlation awareness (Issue #514).";
|
|
9414
7339
|
const secureHandler = createSecureHandler(createConsensusVoteHandler(depsWithNotifier), {
|
|
@@ -9451,38 +7376,6 @@ export {
|
|
|
9451
7376
|
wrapResilientWithFallback,
|
|
9452
7377
|
createUnifiedRegistry,
|
|
9453
7378
|
getGlobalRegistry,
|
|
9454
|
-
validateToolInput,
|
|
9455
|
-
createValidator,
|
|
9456
|
-
RateLimiter,
|
|
9457
|
-
createDefaultRateLimiter,
|
|
9458
|
-
PolicyError,
|
|
9459
|
-
PolicyConfigSchema,
|
|
9460
|
-
denyMutationsWithoutModeRule,
|
|
9461
|
-
safePathsRule,
|
|
9462
|
-
PolicyFirewall,
|
|
9463
|
-
createDefaultPolicyFirewall,
|
|
9464
|
-
evaluatePolicy,
|
|
9465
|
-
createPolicyContext,
|
|
9466
|
-
actorFromContext,
|
|
9467
|
-
resultToOutcome,
|
|
9468
|
-
logToolInvocationAudit,
|
|
9469
|
-
logPolicyAudit,
|
|
9470
|
-
logRateLimitAudit,
|
|
9471
|
-
toolSuccess,
|
|
9472
|
-
toolSuccessStructured,
|
|
9473
|
-
toolStructuredError,
|
|
9474
|
-
toolError,
|
|
9475
|
-
createSecureHandler,
|
|
9476
|
-
createMcpNotifier,
|
|
9477
|
-
NOOP_NOTIFIER,
|
|
9478
|
-
abortSignalStorage,
|
|
9479
|
-
withProgressHeartbeat,
|
|
9480
|
-
withAccessPolicy,
|
|
9481
|
-
getToolTimeout,
|
|
9482
|
-
wrapToolWithTimeout,
|
|
9483
|
-
toSdkCallback,
|
|
9484
|
-
toSdkCallbackWithBudgetCheck,
|
|
9485
|
-
getToolAnnotations,
|
|
9486
7379
|
PersistentOutcomeStore,
|
|
9487
7380
|
JobStatusSchema,
|
|
9488
7381
|
writeJobPending,
|
|
@@ -9566,4 +7459,4 @@ export {
|
|
|
9566
7459
|
CONSENSUS_VOTE_OUTPUT_SCHEMA,
|
|
9567
7460
|
registerConsensusVoteTool
|
|
9568
7461
|
};
|
|
9569
|
-
//# sourceMappingURL=chunk-
|
|
7462
|
+
//# sourceMappingURL=chunk-Z7PILY4E.js.map
|