nexus-agents 2.91.0 → 2.92.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{chunk-W7YQOXCJ.js → chunk-74AO4RKT.js} +2 -2
  2. package/dist/{chunk-PLFKZUVT.js → chunk-G3CYK3WA.js} +7 -5
  3. package/dist/{chunk-PLFKZUVT.js.map → chunk-G3CYK3WA.js.map} +1 -1
  4. package/dist/{chunk-3RZWLQSC.js → chunk-G5XKEUAP.js} +74 -2
  5. package/dist/chunk-G5XKEUAP.js.map +1 -0
  6. package/dist/{chunk-6IV43POQ.js → chunk-L6JZCAFH.js} +96 -624
  7. package/dist/chunk-L6JZCAFH.js.map +1 -0
  8. package/dist/chunk-NR4NSTJH.js +547 -0
  9. package/dist/chunk-NR4NSTJH.js.map +1 -0
  10. package/dist/{chunk-X2M7OF27.js → chunk-OK6U5N5Y.js} +3 -1
  11. package/dist/chunk-OK6U5N5Y.js.map +1 -0
  12. package/dist/{chunk-DH4V6S5K.js → chunk-ZNR3ZVC6.js} +3 -3
  13. package/dist/cli.js +43 -20
  14. package/dist/cli.js.map +1 -1
  15. package/dist/index.d.ts +190 -190
  16. package/dist/index.js +19 -17
  17. package/dist/index.js.map +1 -1
  18. package/dist/{issue-triage-CDSHKFPP.js → issue-triage-FVZOVIRX.js} +3 -2
  19. package/dist/{pr-reviewer-helpers-WYPUYQ2U.js → pr-reviewer-helpers-RBUEJI6V.js} +14 -3
  20. package/dist/{setup-command-B5QPHCKY.js → setup-command-AZAWBZOP.js} +3 -3
  21. package/package.json +1 -1
  22. package/dist/chunk-3RZWLQSC.js.map +0 -1
  23. package/dist/chunk-6IV43POQ.js.map +0 -1
  24. package/dist/chunk-X2M7OF27.js.map +0 -1
  25. /package/dist/{chunk-W7YQOXCJ.js.map → chunk-74AO4RKT.js.map} +0 -0
  26. /package/dist/{chunk-DH4V6S5K.js.map → chunk-ZNR3ZVC6.js.map} +0 -0
  27. /package/dist/{issue-triage-CDSHKFPP.js.map → issue-triage-FVZOVIRX.js.map} +0 -0
  28. /package/dist/{pr-reviewer-helpers-WYPUYQ2U.js.map → pr-reviewer-helpers-RBUEJI6V.js.map} +0 -0
  29. /package/dist/{setup-command-B5QPHCKY.js.map → setup-command-AZAWBZOP.js.map} +0 -0
@@ -3,332 +3,22 @@ import {
3
3
  ScmError
4
4
  } from "./chunk-EFHZHCF2.js";
5
5
  import {
6
- CACHE_TIMEOUTS,
6
+ ROLE_DEFAULT_TRUST,
7
+ ReputationCache,
8
+ TRUST_TIER_NUMERIC,
9
+ TrustTierSchema,
10
+ assessReputation,
11
+ gateWithReputation,
12
+ resolveReputationGatingMode,
13
+ sanitizeInput
14
+ } from "./chunk-NR4NSTJH.js";
15
+ import {
7
16
  createLogger,
8
17
  err,
9
18
  getTimeProvider,
10
19
  ok
11
20
  } from "./chunk-W5I6L4UT.js";
12
21
 
13
- // src/security/trust-types.ts
14
- import { z } from "zod";
15
- var TrustTierSchema = z.enum(["1", "2", "3", "4"]);
16
- var TRUST_TIER_NUMERIC = {
17
- "1": 1,
18
- "2": 2,
19
- "3": 3,
20
- "4": 4
21
- };
22
- var GitHubUserRoleSchema = z.enum([
23
- "owner",
24
- "maintainer",
25
- "collaborator",
26
- "contributor",
27
- "member",
28
- "unknown"
29
- ]);
30
- var ROLE_DEFAULT_TRUST = {
31
- owner: "1",
32
- maintainer: "1",
33
- collaborator: "2",
34
- contributor: "2",
35
- member: "3",
36
- unknown: "3"
37
- };
38
- var InjectionFlagSchema = z.enum([
39
- "authority_claim",
40
- "instruction_pattern",
41
- "system_prompt_manipulation",
42
- "hidden_content",
43
- "urgency_manipulation",
44
- "fake_conversation",
45
- "base64_encoded",
46
- "external_link_instruction"
47
- ]);
48
- var StrippedElementSchema = z.object({
49
- /** Type of element stripped. */
50
- tag: z.string().min(1),
51
- /** Reason for stripping. */
52
- reason: z.string().min(1),
53
- /** Start index in original content. */
54
- startIndex: z.number().int().nonnegative(),
55
- /** Length of stripped content. */
56
- length: z.number().int().positive()
57
- });
58
- var SanitizedInputSchema = z.object({
59
- /** Sanitized content with dangerous elements removed. */
60
- content: z.string(),
61
- /** Original content before sanitization (for audit). */
62
- originalLength: z.number().int().nonnegative(),
63
- /** Assigned trust tier based on user role and content analysis. */
64
- trustTier: TrustTierSchema,
65
- /** GitHub user role of the input source. */
66
- userRole: GitHubUserRoleSchema,
67
- /** Injection patterns detected in content. */
68
- injectionFlags: z.array(InjectionFlagSchema),
69
- /** Elements stripped during sanitization (audit trail). */
70
- strippedElements: z.array(StrippedElementSchema),
71
- /** Whether any dangerous content was detected and stripped. */
72
- wasModified: z.boolean(),
73
- /** Timestamp of sanitization (ISO 8601). */
74
- sanitizedAt: z.iso.datetime()
75
- });
76
- var SanitizerConfigSchema = z.object({
77
- /** GitHub usernames that are always Tier 1 (allowlisted maintainers). */
78
- allowlistedMaintainers: z.array(z.string().min(1)).default([]),
79
- /** Whether to fail open (log only) or fail closed (block). Phase 1 = open. */
80
- failOpen: z.boolean().default(true),
81
- /** Maximum input length before truncation. */
82
- maxInputLength: z.number().int().positive().default(5e4)
83
- });
84
-
85
- // src/security/input-sanitizer.ts
86
- var DANGEROUS_HTML_PATTERN = /<(picture|source|img)\b[^>]*>[\s\S]*?<\/\1>|<(picture|source|img)\b[^>]*\/?>/gi;
87
- var XML_INJECTION_PATTERN = /<\/?(system|human|assistant|instructions|user|prompt|context|tool_use|tool_result)\b[^>]*>/gi;
88
- var HTML_COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
89
- var INJECTION_PATTERNS = [
90
- {
91
- flag: "authority_claim",
92
- pattern: /\b(as (?:a|the) (?:maintainer|admin|owner|security lead|repo owner|developer))\b/i
93
- },
94
- {
95
- flag: "authority_claim",
96
- pattern: /\b(i(?:'m| am) the (?:repo |project )?(?:owner|maintainer|admin))\b/i
97
- },
98
- {
99
- flag: "instruction_pattern",
100
- pattern: /\b(please (?:close|merge|label|mark|apply|delete|remove|approve|reject))\b/i
101
- },
102
- {
103
- flag: "instruction_pattern",
104
- pattern: /\b(you (?:should|must|need to) (?:close|merge|label|apply|delete))\b/i
105
- },
106
- {
107
- flag: "system_prompt_manipulation",
108
- pattern: /\b(ignore (?:all )?previous (?:instructions|rules|prompts))\b/i
109
- },
110
- {
111
- flag: "system_prompt_manipulation",
112
- pattern: /\b(forget (?:your |all )?(?:instructions|rules|safety))\b/i
113
- },
114
- {
115
- flag: "system_prompt_manipulation",
116
- pattern: /\b(new (?:instructions|rules|system prompt|directives))\b/i
117
- },
118
- {
119
- flag: "urgency_manipulation",
120
- pattern: /\b(critical|emergency|urgent|must act now|immediately|time[- ]?sensitive)\b/i
121
- },
122
- {
123
- flag: "fake_conversation",
124
- pattern: /<(?:assistant|human|user|system)>/i
125
- },
126
- // base64_encoded is detected separately by `looksLikeBase64Payload` below.
127
- // The lookahead-based regex it replaced exhibited catastrophic backtracking
128
- // on long hex-only inputs (#2191) — see `looksLikeBase64Payload` for the
129
- // two-phase rewrite that preserves the #1811 SHA-hash false-positive guard.
130
- {
131
- flag: "external_link_instruction",
132
- pattern: /(?:apply|run|execute|install)\s+(?:this\s+)?(?:from\s+)?https?:\/\//i
133
- }
134
- ];
135
- var BASE64_RUN_GLOBAL = /[A-Za-z0-9+/]{40,}={0,2}/g;
136
- var BASE64_DISCRIMINATOR = /[g-zG-Z+/=]/;
137
- function looksLikeBase64Payload(content) {
138
- for (const match of content.matchAll(BASE64_RUN_GLOBAL)) {
139
- if (BASE64_DISCRIMINATOR.test(match[0])) return true;
140
- }
141
- return false;
142
- }
143
- var DANGEROUS_TAG_NAMES = "picture|source|img|system|human|assistant|instructions|user|prompt|context|tool_use|tool_result";
144
- var ENCODED_DANGEROUS_TAG_PATTERN = new RegExp(
145
- `&(?:lt|#0*60|#x0*3c);\\s*\\/?\\s*(?:${DANGEROUS_TAG_NAMES})\\b`,
146
- "i"
147
- );
148
- function decodeEntities(content) {
149
- return content.replace(/&#(\d+);/g, (_match, dec) => {
150
- const code = Number.parseInt(dec, 10);
151
- return Number.isFinite(code) ? String.fromCodePoint(code) : _match;
152
- }).replace(/&#x([0-9a-f]+);/gi, (_match, hex) => {
153
- const code = Number.parseInt(hex, 16);
154
- return Number.isFinite(code) ? String.fromCodePoint(code) : _match;
155
- }).replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&apos;/gi, "'").replace(/&amp;/gi, "&");
156
- }
157
- function applyEntityEvasionDefense(content) {
158
- if (!ENCODED_DANGEROUS_TAG_PATTERN.test(content)) {
159
- return { cleaned: content, stripped: [] };
160
- }
161
- const decoded = decodeEntities(content);
162
- return {
163
- cleaned: decoded,
164
- stripped: [
165
- {
166
- tag: "&\u2026;",
167
- reason: "HTML entity-encoded dangerous tag decoded for stripping (CWE-79)",
168
- startIndex: 0,
169
- length: content.length
170
- }
171
- ]
172
- };
173
- }
174
- function stripDangerousHtml(content) {
175
- const stripped = [];
176
- let cleaned = content;
177
- const MAX_PASSES = 5;
178
- for (let pass = 0; pass < MAX_PASSES; pass++) {
179
- DANGEROUS_HTML_PATTERN.lastIndex = 0;
180
- if (!DANGEROUS_HTML_PATTERN.test(cleaned)) break;
181
- cleaned = cleaned.replace(DANGEROUS_HTML_PATTERN, (match, _g1, _g2, offset) => {
182
- stripped.push({
183
- tag: match.slice(0, 30) + (match.length > 30 ? "..." : ""),
184
- reason: "Dangerous HTML tag (Trail of Bits injection vector)",
185
- startIndex: offset,
186
- length: match.length
187
- });
188
- return "";
189
- });
190
- }
191
- return { cleaned, stripped };
192
- }
193
- function stripXmlTags(content) {
194
- const stripped = [];
195
- let cleaned = content;
196
- const MAX_PASSES = 5;
197
- for (let pass = 0; pass < MAX_PASSES; pass++) {
198
- XML_INJECTION_PATTERN.lastIndex = 0;
199
- if (!XML_INJECTION_PATTERN.test(cleaned)) break;
200
- cleaned = cleaned.replace(XML_INJECTION_PATTERN, (match, _g1, offset) => {
201
- stripped.push({
202
- tag: match,
203
- reason: "XML-like conversation injection tag",
204
- startIndex: offset,
205
- length: match.length
206
- });
207
- return "";
208
- });
209
- }
210
- return { cleaned, stripped };
211
- }
212
- function stripHtmlComments(content) {
213
- const stripped = [];
214
- let cleaned = content;
215
- const MAX_PASSES = 5;
216
- for (let pass = 0; pass < MAX_PASSES; pass++) {
217
- HTML_COMMENT_PATTERN.lastIndex = 0;
218
- const prevLength = cleaned.length;
219
- cleaned = cleaned.replace(HTML_COMMENT_PATTERN, (match, offset) => {
220
- const hasInstruction = /\b(ignore|execute|close|merge|delete|apply)\b/i.test(match);
221
- if (!hasInstruction) return match;
222
- stripped.push({
223
- tag: "<!-- ... -->",
224
- reason: "HTML comment with instruction-like content",
225
- startIndex: offset,
226
- length: match.length
227
- });
228
- return "";
229
- });
230
- if (cleaned.length === prevLength) break;
231
- }
232
- let searchFrom = 0;
233
- while (searchFrom < cleaned.length) {
234
- const openIdx = cleaned.indexOf("<!--", searchFrom);
235
- if (openIdx === -1) break;
236
- const closeIdx = cleaned.indexOf("-->", openIdx + 4);
237
- if (closeIdx === -1) {
238
- stripped.push({
239
- tag: "<!--",
240
- reason: "Unclosed HTML comment (potential injection vector)",
241
- startIndex: openIdx,
242
- length: cleaned.length - openIdx
243
- });
244
- cleaned = cleaned.slice(0, openIdx);
245
- break;
246
- }
247
- searchFrom = closeIdx + 3;
248
- }
249
- return { cleaned, stripped };
250
- }
251
- function detectInjectionPatterns(content) {
252
- const flags = /* @__PURE__ */ new Set();
253
- for (const { flag, pattern } of INJECTION_PATTERNS) {
254
- pattern.lastIndex = 0;
255
- if (pattern.test(content)) {
256
- flags.add(flag);
257
- }
258
- }
259
- if (looksLikeBase64Payload(content)) {
260
- flags.add("base64_encoded");
261
- }
262
- return Array.from(flags);
263
- }
264
- function normalizeRole(userRole) {
265
- const lower = userRole.toLowerCase();
266
- if (lower in ROLE_DEFAULT_TRUST) return lower;
267
- return "unknown";
268
- }
269
- function assignTrustTier(userRole, injectionFlags, allowlisted) {
270
- if (allowlisted) return "1";
271
- const normalizedRole = normalizeRole(userRole);
272
- const baseTier = ROLE_DEFAULT_TRUST[normalizedRole];
273
- const hostileFlags = ["system_prompt_manipulation", "fake_conversation"];
274
- if (injectionFlags.some((f) => hostileFlags.includes(f))) return "4";
275
- if (injectionFlags.includes("authority_claim") && normalizedRole !== "owner" && normalizedRole !== "maintainer") {
276
- return "4";
277
- }
278
- return baseTier;
279
- }
280
- function sanitizeInput(content, userRole, username, config) {
281
- const cfg = SanitizerConfigSchema.parse(config ?? {});
282
- const truncated = content.slice(0, cfg.maxInputLength);
283
- const allowlisted = cfg.allowlistedMaintainers.includes(username);
284
- try {
285
- const entityDecoded = applyEntityEvasionDefense(truncated);
286
- const html = stripDangerousHtml(entityDecoded.cleaned);
287
- const xml = stripXmlTags(html.cleaned);
288
- const comments = stripHtmlComments(xml.cleaned);
289
- const allStripped = [
290
- ...entityDecoded.stripped,
291
- ...html.stripped,
292
- ...xml.stripped,
293
- ...comments.stripped
294
- ];
295
- const injectionFlags = detectInjectionPatterns(truncated);
296
- const trustTier = assignTrustTier(userRole, injectionFlags, allowlisted);
297
- return {
298
- content: comments.cleaned,
299
- originalLength: content.length,
300
- trustTier,
301
- userRole,
302
- injectionFlags,
303
- strippedElements: allStripped,
304
- wasModified: allStripped.length > 0,
305
- sanitizedAt: (/* @__PURE__ */ new Date()).toISOString()
306
- };
307
- } catch (err2) {
308
- return buildFailClosedResult(err2, content, truncated, userRole, allowlisted);
309
- }
310
- }
311
- function buildFailClosedResult(err2, originalContent, truncated, userRole, allowlisted) {
312
- const message = err2 instanceof Error ? err2.message : String(err2);
313
- return {
314
- content: "",
315
- originalLength: originalContent.length,
316
- trustTier: allowlisted ? "1" : "4",
317
- userRole,
318
- injectionFlags: [],
319
- strippedElements: [
320
- {
321
- tag: "(pipeline-failure)",
322
- reason: `Sanitizer pipeline threw; input discarded as fail-closed: ${message}`,
323
- startIndex: 0,
324
- length: truncated.length
325
- }
326
- ],
327
- wasModified: true,
328
- sanitizedAt: (/* @__PURE__ */ new Date()).toISOString()
329
- };
330
- }
331
-
332
22
  // src/security/trust-classifier.ts
333
23
  function mapAuthorAssociation(association) {
334
24
  switch (association.toUpperCase()) {
@@ -413,106 +103,106 @@ function getRequiredTrustTier(actionType) {
413
103
  }
414
104
 
415
105
  // src/security/policy-gate.ts
416
- import { z as z3 } from "zod";
106
+ import { z as z2 } from "zod";
417
107
 
418
108
  // src/security/action-schema.ts
419
- import { z as z2 } from "zod";
420
- var RepoFileSource = z2.object({
421
- type: z2.literal("repoFile"),
422
- path: z2.string().min(1),
423
- line: z2.number().int().positive().optional(),
424
- commit: z2.string().regex(/^[a-f0-9]{7,40}$/).optional()
109
+ import { z } from "zod";
110
+ var RepoFileSource = z.object({
111
+ type: z.literal("repoFile"),
112
+ path: z.string().min(1),
113
+ line: z.number().int().positive().optional(),
114
+ commit: z.string().regex(/^[a-f0-9]{7,40}$/).optional()
425
115
  });
426
- var IssueCommentSource = z2.object({
427
- type: z2.literal("issueComment"),
428
- issueNumber: z2.number().int().positive(),
429
- commentId: z2.number().int().positive(),
430
- author: z2.string().min(1),
116
+ var IssueCommentSource = z.object({
117
+ type: z.literal("issueComment"),
118
+ issueNumber: z.number().int().positive(),
119
+ commentId: z.number().int().positive(),
120
+ author: z.string().min(1),
431
121
  authorTrustTier: TrustTierSchema
432
122
  });
433
- var CIResultSource = z2.object({
434
- type: z2.literal("ciResult"),
435
- runId: z2.number().int().positive(),
436
- status: z2.enum(["pass", "fail"]),
437
- job: z2.string().min(1)
123
+ var CIResultSource = z.object({
124
+ type: z.literal("ciResult"),
125
+ runId: z.number().int().positive(),
126
+ status: z.enum(["pass", "fail"]),
127
+ job: z.string().min(1)
438
128
  });
439
- var PolicyDocSource = z2.object({
440
- type: z2.literal("policyDoc"),
441
- path: z2.string().min(1),
442
- section: z2.string().min(1)
129
+ var PolicyDocSource = z.object({
130
+ type: z.literal("policyDoc"),
131
+ path: z.string().min(1),
132
+ section: z.string().min(1)
443
133
  });
444
- var MaintainerCommandSource = z2.object({
445
- type: z2.literal("maintainerCommand"),
446
- username: z2.string().min(1),
447
- commentId: z2.number().int().positive()
134
+ var MaintainerCommandSource = z.object({
135
+ type: z.literal("maintainerCommand"),
136
+ username: z.string().min(1),
137
+ commentId: z.number().int().positive()
448
138
  });
449
- var SourceCitationSchema = z2.discriminatedUnion("type", [
139
+ var SourceCitationSchema = z.discriminatedUnion("type", [
450
140
  RepoFileSource,
451
141
  IssueCommentSource,
452
142
  CIResultSource,
453
143
  PolicyDocSource,
454
144
  MaintainerCommandSource
455
145
  ]);
456
- var SummarizeIssueAction = z2.object({
457
- type: z2.literal("SummarizeIssue"),
458
- summary: z2.string().min(10).max(2e3),
459
- sources: z2.array(SourceCitationSchema).min(1).max(20)
146
+ var SummarizeIssueAction = z.object({
147
+ type: z.literal("SummarizeIssue"),
148
+ summary: z.string().min(10).max(2e3),
149
+ sources: z.array(SourceCitationSchema).min(1).max(20)
460
150
  });
461
- var ProposeLabelsAction = z2.object({
462
- type: z2.literal("ProposeLabels"),
463
- labels: z2.array(z2.string()).min(1).max(5),
464
- reason: z2.string().min(10).max(500),
465
- sources: z2.array(SourceCitationSchema).min(1).max(20)
151
+ var ProposeLabelsAction = z.object({
152
+ type: z.literal("ProposeLabels"),
153
+ labels: z.array(z.string()).min(1).max(5),
154
+ reason: z.string().min(10).max(500),
155
+ sources: z.array(SourceCitationSchema).min(1).max(20)
466
156
  });
467
- var DraftReplyAction = z2.object({
468
- type: z2.literal("DraftReply"),
469
- body: z2.string().min(10).max(2e3),
470
- requiresApproval: z2.literal(true),
471
- sources: z2.array(SourceCitationSchema).min(1).max(20)
157
+ var DraftReplyAction = z.object({
158
+ type: z.literal("DraftReply"),
159
+ body: z.string().min(10).max(2e3),
160
+ requiresApproval: z.literal(true),
161
+ sources: z.array(SourceCitationSchema).min(1).max(20)
472
162
  });
473
- var RequestHumanApprovalAction = z2.object({
474
- type: z2.literal("RequestHumanApproval"),
475
- reason: z2.string().min(10).max(500),
476
- context: z2.string().min(10).max(2e3)
163
+ var RequestHumanApprovalAction = z.object({
164
+ type: z.literal("RequestHumanApproval"),
165
+ reason: z.string().min(10).max(500),
166
+ context: z.string().min(10).max(2e3)
477
167
  });
478
- var GeneratePatchPlanAction = z2.object({
479
- type: z2.literal("GeneratePatchPlan"),
480
- files: z2.array(
481
- z2.object({
482
- path: z2.string().min(1),
483
- operation: z2.enum(["modify", "create", "delete"]),
484
- description: z2.string().min(10).max(500)
168
+ var GeneratePatchPlanAction = z.object({
169
+ type: z.literal("GeneratePatchPlan"),
170
+ files: z.array(
171
+ z.object({
172
+ path: z.string().min(1),
173
+ operation: z.enum(["modify", "create", "delete"]),
174
+ description: z.string().min(10).max(500)
485
175
  })
486
176
  ).min(1).max(10),
487
- rationale: z2.string().min(10).max(1e3),
488
- requiresApproval: z2.literal(true),
489
- sources: z2.array(SourceCitationSchema).min(2).max(20)
177
+ rationale: z.string().min(10).max(1e3),
178
+ requiresApproval: z.literal(true),
179
+ sources: z.array(SourceCitationSchema).min(2).max(20)
490
180
  });
491
- var ClassifyIssueAction = z2.object({
492
- type: z2.literal("ClassifyIssue"),
493
- category: z2.enum(["bug", "feature", "question", "documentation", "security", "performance"]),
494
- confidence: z2.number().min(0).max(1),
495
- sources: z2.array(SourceCitationSchema).min(1).max(20)
181
+ var ClassifyIssueAction = z.object({
182
+ type: z.literal("ClassifyIssue"),
183
+ category: z.enum(["bug", "feature", "question", "documentation", "security", "performance"]),
184
+ confidence: z.number().min(0).max(1),
185
+ sources: z.array(SourceCitationSchema).min(1).max(20)
496
186
  });
497
- var IdentifyDuplicatesAction = z2.object({
498
- type: z2.literal("IdentifyDuplicates"),
499
- candidates: z2.array(z2.number().int().positive()).min(1).max(10),
500
- similarity: z2.array(z2.number().min(0).max(1)),
501
- sources: z2.array(SourceCitationSchema).min(1).max(20)
187
+ var IdentifyDuplicatesAction = z.object({
188
+ type: z.literal("IdentifyDuplicates"),
189
+ candidates: z.array(z.number().int().positive()).min(1).max(10),
190
+ similarity: z.array(z.number().min(0).max(1)),
191
+ sources: z.array(SourceCitationSchema).min(1).max(20)
502
192
  });
503
- var RefuseActionAction = z2.object({
504
- type: z2.literal("RefuseAction"),
505
- reason: z2.string().min(10).max(500),
506
- escalateTo: z2.enum(["maintainer", "security"])
193
+ var RefuseActionAction = z.object({
194
+ type: z.literal("RefuseAction"),
195
+ reason: z.string().min(10).max(500),
196
+ escalateTo: z.enum(["maintainer", "security"])
507
197
  });
508
- var HandoffMessageAction = z2.object({
509
- type: z2.literal("HandoffMessage"),
510
- targetCapability: z2.string().min(1).max(100),
511
- reason: z2.string().min(5).max(500),
512
- inputTrustTier: z2.enum(["1", "2", "3", "4"]),
513
- sources: z2.array(SourceCitationSchema).min(1).max(20)
198
+ var HandoffMessageAction = z.object({
199
+ type: z.literal("HandoffMessage"),
200
+ targetCapability: z.string().min(1).max(100),
201
+ reason: z.string().min(5).max(500),
202
+ inputTrustTier: z.enum(["1", "2", "3", "4"]),
203
+ sources: z.array(SourceCitationSchema).min(1).max(20)
514
204
  });
515
- var AgentActionSchema = z2.discriminatedUnion("type", [
205
+ var AgentActionSchema = z.discriminatedUnion("type", [
516
206
  SummarizeIssueAction,
517
207
  ProposeLabelsAction,
518
208
  DraftReplyAction,
@@ -564,13 +254,13 @@ function requiresCitation(actionType) {
564
254
  }
565
255
 
566
256
  // src/security/policy-gate.ts
567
- var ViolationSchema = z3.object({
257
+ var ViolationSchema = z2.object({
568
258
  /** Machine-readable rule identifier. */
569
- rule: z3.string().min(1),
259
+ rule: z2.string().min(1),
570
260
  /** Human-readable description of the violation. */
571
- message: z3.string().min(1),
261
+ message: z2.string().min(1),
572
262
  /** Severity: 'block' prevents execution, 'warn' logs only. */
573
- severity: z3.enum(["block", "warn"])
263
+ severity: z2.enum(["block", "warn"])
574
264
  });
575
265
  function getActionSources(action) {
576
266
  if ("sources" in action) {
@@ -802,211 +492,6 @@ function getCorroborationRules(actionType) {
802
492
  return ACTION_CORROBORATION_RULES[actionType];
803
493
  }
804
494
 
805
- // src/security/reputation-model.ts
806
- import { z as z4 } from "zod";
807
- var SuspiciousSignalSchema = z4.enum([
808
- "new_account",
809
- "no_prior_contributions",
810
- "injection_patterns_detected",
811
- "rapid_comments",
812
- "mismatched_authority_claim"
813
- ]);
814
- var DAYS_PER_YEAR_APPROX = 36.5;
815
- var SUSPICIOUS_THRESHOLDS = {
816
- /** Account younger than this (days) is flagged. */
817
- newAccountDays: 30,
818
- /** Fewer contributions than this is flagged. */
819
- minContributions: 1,
820
- /** More comments than this in the window triggers rapid-comment flag. */
821
- rapidCommentThreshold: 5,
822
- /** Time window (minutes) for rapid comment detection. */
823
- rapidCommentWindowMinutes: 10
824
- };
825
- var DEFAULT_TTL_MS = CACHE_TIMEOUTS.reputationTtlMs;
826
- var DEFAULT_MAX_SIZE = 1e3;
827
- var ReputationCache = class {
828
- cache = /* @__PURE__ */ new Map();
829
- ttlMs;
830
- maxSize;
831
- constructor(ttlMs = DEFAULT_TTL_MS, maxSize = DEFAULT_MAX_SIZE) {
832
- this.ttlMs = ttlMs;
833
- this.maxSize = maxSize;
834
- }
835
- get(username) {
836
- const entry = this.cache.get(username);
837
- if (entry === void 0) return void 0;
838
- if (getTimeProvider().now() > entry.expiresAt) {
839
- this.cache.delete(username);
840
- return void 0;
841
- }
842
- return entry.assessment;
843
- }
844
- set(username, assessment) {
845
- if (this.cache.size >= this.maxSize && !this.cache.has(username)) {
846
- this.evictOldest();
847
- }
848
- this.cache.set(username, {
849
- assessment,
850
- expiresAt: getTimeProvider().now() + this.ttlMs
851
- });
852
- }
853
- /** Evict a batch of oldest entries (10% of maxSize, minimum 1). */
854
- evictOldest() {
855
- const batchSize = Math.max(1, Math.floor(this.maxSize * 0.1));
856
- const keys = this.cache.keys();
857
- for (let i = 0; i < batchSize; i++) {
858
- const next = keys.next();
859
- if (next.done === true) break;
860
- this.cache.delete(next.value);
861
- }
862
- }
863
- clear() {
864
- this.cache.clear();
865
- }
866
- get size() {
867
- return this.cache.size;
868
- }
869
- };
870
- var HOSTILE_INJECTION_FLAGS = [
871
- "system_prompt_manipulation",
872
- "fake_conversation",
873
- "authority_claim",
874
- "hidden_content"
875
- ];
876
- function hasHostileInjection(flags) {
877
- return flags.some((f) => HOSTILE_INJECTION_FLAGS.includes(f));
878
- }
879
- function isRapidCommenting(m) {
880
- return m.recentCommentCount !== void 0 && m.recentCommentWindowMinutes !== void 0 && m.recentCommentCount > SUSPICIOUS_THRESHOLDS.rapidCommentThreshold && m.recentCommentWindowMinutes <= SUSPICIOUS_THRESHOLDS.rapidCommentWindowMinutes;
881
- }
882
- function isMismatchedAuthority(m) {
883
- if (!m.injectionFlags.includes("authority_claim")) return false;
884
- const association = m.authorAssociation.toUpperCase();
885
- return association !== "OWNER" && association !== "MEMBER";
886
- }
887
- function detectSuspiciousSignals(metadata) {
888
- const signals = [];
889
- const { accountAgeDays, priorContributions } = metadata;
890
- if (accountAgeDays !== void 0 && accountAgeDays < SUSPICIOUS_THRESHOLDS.newAccountDays) {
891
- signals.push("new_account");
892
- }
893
- if (priorContributions !== void 0 && priorContributions < SUSPICIOUS_THRESHOLDS.minContributions) {
894
- signals.push("no_prior_contributions");
895
- }
896
- if (hasHostileInjection(metadata.injectionFlags)) signals.push("injection_patterns_detected");
897
- if (isRapidCommenting(metadata)) signals.push("rapid_comments");
898
- if (isMismatchedAuthority(metadata)) signals.push("mismatched_authority_claim");
899
- return signals;
900
- }
901
- function calculateReputationScore(metadata, signals, userRole) {
902
- let score = 50;
903
- const roleBonus = {
904
- owner: 40,
905
- maintainer: 35,
906
- collaborator: 25,
907
- contributor: 15,
908
- member: 5,
909
- unknown: 0
910
- };
911
- score += roleBonus[userRole];
912
- if (metadata.accountAgeDays !== void 0) {
913
- score += Math.min(metadata.accountAgeDays / DAYS_PER_YEAR_APPROX, 10);
914
- }
915
- if (metadata.priorContributions !== void 0) {
916
- score += Math.min(metadata.priorContributions, 10);
917
- }
918
- const signalPenalty = {
919
- new_account: -15,
920
- no_prior_contributions: -10,
921
- injection_patterns_detected: -25,
922
- rapid_comments: -20,
923
- mismatched_authority_claim: -30
924
- };
925
- for (const signal of signals) {
926
- score += signalPenalty[signal];
927
- }
928
- return Math.max(0, Math.min(100, Math.round(score)));
929
- }
930
- function mapRole(association) {
931
- switch (association.toUpperCase()) {
932
- case "OWNER":
933
- return "owner";
934
- case "MEMBER":
935
- return "member";
936
- case "COLLABORATOR":
937
- return "collaborator";
938
- case "CONTRIBUTOR":
939
- return "contributor";
940
- default:
941
- return "unknown";
942
- }
943
- }
944
- function determineEffectiveTier(userRole, signals) {
945
- const baseTier = ROLE_DEFAULT_TRUST[userRole];
946
- const hostileSignals = [
947
- "injection_patterns_detected",
948
- "mismatched_authority_claim"
949
- ];
950
- if (signals.some((s) => hostileSignals.includes(s))) return "4";
951
- if (signals.length >= 2) {
952
- const baseNumeric = TRUST_TIER_NUMERIC[baseTier];
953
- const downgraded = Math.min(baseNumeric + 1, 4);
954
- return String(downgraded);
955
- }
956
- return baseTier;
957
- }
958
- function assessReputation(metadata, cache) {
959
- const cached = cache?.get(metadata.username);
960
- if (cached !== void 0) return cached;
961
- const userRole = mapRole(metadata.authorAssociation);
962
- const signals = detectSuspiciousSignals(metadata);
963
- const score = calculateReputationScore(metadata, signals, userRole);
964
- const effectiveTier = determineEffectiveTier(userRole, signals);
965
- const assessment = {
966
- username: metadata.username,
967
- userRole,
968
- suspiciousSignals: signals,
969
- isSuspicious: signals.length > 0,
970
- effectiveTrustTier: effectiveTier,
971
- reputationScore: score,
972
- reason: buildReason(userRole, signals, effectiveTier),
973
- assessedAt: (/* @__PURE__ */ new Date()).toISOString()
974
- };
975
- cache?.set(metadata.username, assessment);
976
- return assessment;
977
- }
978
- function reconcileTrustTier(classifierTier, reputation) {
979
- if (classifierTier === "1") return "1";
980
- const repTier = reputation?.effectiveTrustTier;
981
- if (repTier === void 0) return classifierTier;
982
- return TRUST_TIER_NUMERIC[repTier] > TRUST_TIER_NUMERIC[classifierTier] ? repTier : classifierTier;
983
- }
984
- var ReputationGatingModeSchema = z4.enum(["off", "audit", "enforce"]);
985
- var DEFAULT_REPUTATION_GATING_MODE = "audit";
986
- function resolveReputationGatingMode(env = process.env) {
987
- const raw = env["NEXUS_REPUTATION_GATING"];
988
- if (typeof raw !== "string" || raw.length === 0) return DEFAULT_REPUTATION_GATING_MODE;
989
- const parsed = ReputationGatingModeSchema.safeParse(raw.toLowerCase());
990
- return parsed.success ? parsed.data : DEFAULT_REPUTATION_GATING_MODE;
991
- }
992
- function gateWithReputation(classifierTier, reputation, mode) {
993
- const reconciledTier = mode === "off" ? classifierTier : reconcileTrustTier(classifierTier, reputation);
994
- const enforcedTier = mode === "enforce" ? reconciledTier : classifierTier;
995
- return {
996
- enforcedTier,
997
- reconciledTier,
998
- demotionSuppressed: mode !== "enforce" && reconciledTier !== classifierTier,
999
- mode
1000
- };
1001
- }
1002
- function buildReason(role, signals, tier) {
1003
- if (signals.length === 0) {
1004
- return `Role ${role} \u2192 Tier ${tier} (no suspicious signals)`;
1005
- }
1006
- const signalList = signals.join(", ");
1007
- return `Role ${role} \u2192 Tier ${tier} (signals: ${signalList})`;
1008
- }
1009
-
1010
495
  // src/scm/url-parsers.ts
1011
496
  function parsePRUrl(url) {
1012
497
  const httpPattern = /github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/;
@@ -1262,7 +747,7 @@ function createFullGitHubProvider(repo) {
1262
747
  }
1263
748
 
1264
749
  // src/dogfooding/issue-triage-types.ts
1265
- import { z as z5 } from "zod";
750
+ import { z as z3 } from "zod";
1266
751
  var CATEGORY_DISPLAY_NAMES = {
1267
752
  bug: "Bug Report",
1268
753
  feature: "Feature Request",
@@ -1284,11 +769,11 @@ var DEFAULT_ISSUE_TRIAGE_CONFIG = {
1284
769
  maxComments: 50,
1285
770
  enableReputation: true
1286
771
  };
1287
- var IssueTriageConfigSchema = z5.object({
1288
- dryRun: z5.boolean().default(true),
1289
- githubToken: z5.string().optional(),
1290
- maxComments: z5.number().int().min(1).max(100).default(50),
1291
- enableReputation: z5.boolean().default(true)
772
+ var IssueTriageConfigSchema = z3.object({
773
+ dryRun: z3.boolean().default(true),
774
+ githubToken: z3.string().optional(),
775
+ maxComments: z3.number().int().min(1).max(100).default(50),
776
+ enableReputation: z3.boolean().default(true)
1292
777
  });
1293
778
 
1294
779
  // src/dogfooding/issue-triage-helpers.ts
@@ -1686,15 +1171,6 @@ function createIssueTriage(config) {
1686
1171
  }
1687
1172
 
1688
1173
  export {
1689
- TrustTierSchema,
1690
- TRUST_TIER_NUMERIC,
1691
- GitHubUserRoleSchema,
1692
- ROLE_DEFAULT_TRUST,
1693
- InjectionFlagSchema,
1694
- StrippedElementSchema,
1695
- SanitizedInputSchema,
1696
- SanitizerConfigSchema,
1697
- sanitizeInput,
1698
1174
  mapAuthorAssociation,
1699
1175
  classifyTrust,
1700
1176
  canInfluenceDecisions,
@@ -1711,10 +1187,6 @@ export {
1711
1187
  canProceed,
1712
1188
  validateCorroboration,
1713
1189
  getCorroborationRules,
1714
- SuspiciousSignalSchema,
1715
- ReputationCache,
1716
- assessReputation,
1717
- reconcileTrustTier,
1718
1190
  parsePRUrl,
1719
1191
  GitHubReviewer,
1720
1192
  GitHubUserInfo,
@@ -1723,4 +1195,4 @@ export {
1723
1195
  IssueTriage,
1724
1196
  createIssueTriage
1725
1197
  };
1726
- //# sourceMappingURL=chunk-6IV43POQ.js.map
1198
+ //# sourceMappingURL=chunk-L6JZCAFH.js.map