@antonbabenko/deliberation-mcp 3.6.0 → 3.6.2

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 (2) hide show
  1. package/dist/index.js +99 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -231,18 +231,71 @@ var require_provider = __commonJS({
231
231
  var VERDICT_RE = /\bverdict\b[^A-Za-z0-9]*\b(APPROVE|REJECT|REQUEST[_\s]CHANGES)\b/i;
232
232
  var BULLET_RE = /^([-*+•]|`?\[)/;
233
233
  var BRACKET_CAT_RE = /\[\s*([A-Za-z_]+)\s*\]/g;
234
+ var SENTINEL_RE = /^[#>*_`\s]*verdict\s*[:=]\s*[*_`\s]*(APPROVE|REJECT|REQUEST[_\s]CHANGES)\b/i;
235
+ var VERDICT_WORD_RE = /^[#>*_`\s]*verdict[*_`:\s]*$/i;
236
+ var TOKEN_LINE_RE = /^(APPROVE|REJECT|REQUEST[_\s]CHANGES)$/i;
237
+ var MD_EMPHASIS = /[*_`~]/g;
238
+ var FENCE_RE = /^\s*(```|~~~)/;
239
+ var ARTIFACT_RE = /^[*_`~\s:.\-]*$/;
240
+ var STRIP_LEAD = /^[*_`~\s:.\-]+/;
241
+ var STRIP_TRAIL = /[*_`~\s]+$/;
242
+ var HEADING_RE = /^#{1,6}\s/;
243
+ function normVerdict(tok) {
244
+ return tok.replace(/\s+/g, "_").toUpperCase();
245
+ }
234
246
  function parseReview(text) {
235
247
  const raw = safeString(text);
236
- const lines = raw.split(/\r?\n/);
237
- let verdict = null;
238
- const criticalIssues = [];
239
- for (const line of lines) {
240
- if (verdict === null) {
241
- const vm = line.match(VERDICT_RE);
242
- if (vm) verdict = /** @type {ParsedReview["verdict"]} */
243
- vm[1].replace(/\s+/g, "_").toUpperCase();
248
+ const lines = [];
249
+ let inFence = false;
250
+ for (const ln of raw.split(/\r?\n/)) {
251
+ if (FENCE_RE.test(ln)) {
252
+ inFence = !inFence;
253
+ continue;
244
254
  }
245
- const trimmed = line.trim();
255
+ if (!inFence) lines.push(ln);
256
+ }
257
+ return { verdict: resolveVerdict(lines), criticalIssues: resolveIssues(lines) };
258
+ }
259
+ function resolveVerdict(lines) {
260
+ for (const ln of lines) {
261
+ const m = ln.match(SENTINEL_RE);
262
+ if (m) return (
263
+ /** @type {any} */
264
+ normVerdict(m[1])
265
+ );
266
+ }
267
+ for (const ln of lines) {
268
+ const m = ln.match(VERDICT_RE);
269
+ if (m) return (
270
+ /** @type {any} */
271
+ normVerdict(m[1])
272
+ );
273
+ }
274
+ for (let i = 0; i < lines.length; i++) {
275
+ if (!VERDICT_WORD_RE.test(lines[i].trim())) continue;
276
+ for (let j = i + 1; j < lines.length && j <= i + 3; j++) {
277
+ const t = lines[j].replace(MD_EMPHASIS, "").trim();
278
+ if (!t) continue;
279
+ if (TOKEN_LINE_RE.test(t)) return (
280
+ /** @type {any} */
281
+ normVerdict(t)
282
+ );
283
+ break;
284
+ }
285
+ }
286
+ const nonEmpty = lines.map((l) => l.replace(MD_EMPHASIS, "").trim()).filter((t) => t !== "");
287
+ for (const t of [nonEmpty[0], nonEmpty[nonEmpty.length - 1]]) {
288
+ if (t && TOKEN_LINE_RE.test(t)) return (
289
+ /** @type {any} */
290
+ normVerdict(t)
291
+ );
292
+ }
293
+ return null;
294
+ }
295
+ function resolveIssues(lines) {
296
+ const out = [];
297
+ for (let i = 0; i < lines.length; i++) {
298
+ const trimmed = lines[i].trim();
246
299
  if (!BULLET_RE.test(trimmed)) continue;
247
300
  let chosen = null;
248
301
  for (const mm of trimmed.matchAll(BRACKET_CAT_RE)) {
@@ -258,10 +311,20 @@ var require_provider = __commonJS({
258
311
  /** @type {ReviewCriticalIssue["category"]} */
259
312
  REVIEW_CATEGORIES.includes(cat) ? cat : REVIEW_FALLBACK_CATEGORY
260
313
  );
261
- const description = trimmed.slice((chosen.index || 0) + chosen[0].length).replace(/^[`\s:.\-]+/, "").trim();
262
- if (description) criticalIssues.push({ category, description });
314
+ let description = trimmed.slice(chosen.index + chosen[0].length).replace(STRIP_LEAD, "").replace(STRIP_TRAIL, "").trim();
315
+ if (!description || ARTIFACT_RE.test(description)) {
316
+ for (let j = i + 1; j < lines.length; j++) {
317
+ const nt = lines[j].trim();
318
+ if (!nt) break;
319
+ if (BULLET_RE.test(nt) || HEADING_RE.test(nt)) break;
320
+ if (SENTINEL_RE.test(nt) || VERDICT_WORD_RE.test(nt) || VERDICT_RE.test(nt)) break;
321
+ description = nt.replace(STRIP_LEAD, "").replace(STRIP_TRAIL, "").trim();
322
+ break;
323
+ }
324
+ }
325
+ if (description && !ARTIFACT_RE.test(description)) out.push({ category, description });
263
326
  }
264
- return { verdict, criticalIssues };
327
+ return out;
265
328
  }
266
329
  module2.exports = {
267
330
  toErrorResult,
@@ -324,12 +387,12 @@ ${state.currentPlan}`
324
387
  const peerPrompt = [
325
388
  body,
326
389
  "Review the plan for correctness, security, scope, ambiguity, performance, and ops gaps.",
327
- "End with: **Verdict**: APPROVE | REQUEST_CHANGES | REJECT, then categorized critical issues."
390
+ "End with a line by itself in exactly this form (no markdown, token on the SAME line): VERDICT: APPROVE (or VERDICT: REQUEST_CHANGES, or VERDICT: REJECT). Then list any critical issues, one per line as: - [category] description where category is one of security, correctness, scope, ambiguity, performance, ops."
328
391
  ].join("\n\n");
329
392
  const blindPrompt = [
330
393
  body,
331
394
  "Give your own independent verdict BEFORE seeing peer opinions.",
332
- "End with: **Verdict**: APPROVE | REQUEST_CHANGES | REJECT, then categorized critical issues."
395
+ "End with a line by itself in exactly this form (no markdown, token on the SAME line): VERDICT: APPROVE (or VERDICT: REQUEST_CHANGES, or VERDICT: REJECT). Then list any critical issues, one per line as: - [category] description where category is one of security, correctness, scope, ambiguity, performance, ops."
333
396
  ].join("\n\n");
334
397
  return { peerPrompt, blindPrompt };
335
398
  }
@@ -1055,7 +1118,7 @@ var require_sessions = __commonJS({
1055
1118
  var DEFAULT_MAX_AGE_DAYS = 30;
1056
1119
  function scrubSecrets(text) {
1057
1120
  if (typeof text !== "string" || text.length === 0) return text;
1058
- return text.replace(/\bsk-or-[A-Za-z0-9_-]{20,}/g, "[REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{20,}/g, "[REDACTED]").replace(/\bxai-[A-Za-z0-9_-]{20,}/g, "[REDACTED]").replace(/\bgh[pousr]_[A-Za-z0-9]{20,}/g, "[REDACTED]").replace(/\bAKIA[0-9A-Z]{16}\b/g, "[REDACTED]").replace(/\bAIza[0-9A-Za-z_-]{35,}/g, "[REDACTED]").replace(/\bBearer\s+[A-Za-z0-9._~+/-]{20,}={0,2}/g, "Bearer [REDACTED]");
1121
+ return text.replace(/\bsk-or-[A-Za-z0-9_-]{20,}/g, "[REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{20,}/g, "[REDACTED]").replace(/\bxai-[A-Za-z0-9_-]{20,}/g, "[REDACTED]").replace(/\bgh[pousr]_[A-Za-z0-9]{20,}/g, "[REDACTED]").replace(/\bAKIA[0-9A-Z]{16}\b/g, "[REDACTED]").replace(/\bAIza[0-9A-Za-z_-]{35,}/g, "[REDACTED]").replace(/\b([a-z][a-z0-9+.-]*:\/\/[^\s:@/]+:)[^\s@/]{6,}@/gi, "$1[REDACTED]@").replace(/\bToken\s+[A-Za-z0-9._~+/-]{20,}={0,2}/g, "Token [REDACTED]").replace(/\bBearer\s+[A-Za-z0-9._~+/-]{20,}={0,2}/g, "Bearer [REDACTED]");
1059
1122
  }
1060
1123
  function capText(text) {
1061
1124
  if (typeof text !== "string") return text;
@@ -2139,6 +2202,7 @@ var require_gemini = __commonJS({
2139
2202
  var DEFAULT_RECOVERY_GRACE_MS = 12e4;
2140
2203
  var MAX_MS = 6e5;
2141
2204
  var VALID_SANDBOX_VALUES = /* @__PURE__ */ new Set(["read-only", "workspace-write"]);
2205
+ var THREAD_ID_RE = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,127}$/;
2142
2206
  function goDuration(ms) {
2143
2207
  return Math.ceil(ms / 1e3) + "s";
2144
2208
  }
@@ -2162,10 +2226,14 @@ ${prompt}`;
2162
2226
  return args;
2163
2227
  }
2164
2228
  var ADVISORY_ENV_SCRUB = ["GITHUB_TOKEN", "GH_TOKEN", "GIT_ASKPASS", "SSH_AUTH_SOCK"];
2229
+ var CREDENTIAL_NAME_RE = /(?:^|_)(?:KEY|TOKEN|SECRET|SECRETS|PASSWORD|PASSWD|CREDENTIAL|CREDENTIALS)$|API_KEY|ACCESS_KEY|SESSION_TOKEN|PRIVATE_KEY/i;
2165
2230
  function advisoryEnv(env) {
2166
2231
  const out = { ...env };
2167
2232
  delete out.DELIBERATION_DISABLE_OS_SANDBOX;
2168
2233
  for (const k of ADVISORY_ENV_SCRUB) delete out[k];
2234
+ for (const k of Object.keys(out)) {
2235
+ if (CREDENTIAL_NAME_RE.test(k)) delete out[k];
2236
+ }
2169
2237
  return out;
2170
2238
  }
2171
2239
  function seatbeltLiteral(p) {
@@ -2286,7 +2354,8 @@ ${prompt}`;
2286
2354
  real = fs.realpathSync(resolved);
2287
2355
  } catch (_) {
2288
2356
  }
2289
- return map[real] ?? map[resolved] ?? map[cwd] ?? null;
2357
+ const candidate = map[real] ?? map[resolved] ?? map[cwd] ?? null;
2358
+ return typeof candidate === "string" && THREAD_ID_RE.test(candidate) ? candidate : null;
2290
2359
  } catch (_) {
2291
2360
  return null;
2292
2361
  }
@@ -2611,8 +2680,8 @@ ${prompt}`;
2611
2680
  return;
2612
2681
  }
2613
2682
  const threadId2 = args.threadId.trim();
2614
- if (threadId2 === "" || threadId2 === "latest" || threadId2 === "unknown") {
2615
- if (shouldRespond) sendError(id, -32602, "Invalid params: 'threadId' must be an explicit conversation id, not '" + threadId2 + "'");
2683
+ if (!THREAD_ID_RE.test(threadId2) || threadId2 === "latest" || threadId2 === "unknown") {
2684
+ if (shouldRespond) sendError(id, -32602, "Invalid params: 'threadId' must be an explicit conversation id (alphanumeric/underscore start, then [A-Za-z0-9_-], 1..128 chars)");
2616
2685
  return;
2617
2686
  }
2618
2687
  if (!isNonEmptyString(args.prompt)) {
@@ -2709,6 +2778,7 @@ ${prompt}`;
2709
2778
  module2.exports.READ_ONLY_GUARD = READ_ONLY_GUARD;
2710
2779
  module2.exports.applyReadOnlyGuard = applyReadOnlyGuard;
2711
2780
  module2.exports.advisoryEnv = advisoryEnv;
2781
+ module2.exports.THREAD_ID_RE = THREAD_ID_RE;
2712
2782
  module2.exports.buildSeatbeltProfile = buildSeatbeltProfile;
2713
2783
  module2.exports.buildSpawnCommand = buildSpawnCommand;
2714
2784
  module2.exports.diffGitState = diffGitState;
@@ -2743,7 +2813,7 @@ var require_lock = __commonJS({
2743
2813
  try {
2744
2814
  fs.mkdirSync(lockDir);
2745
2815
  const markerPath = path.join(lockDir, markerName);
2746
- fs.writeFileSync(markerPath, JSON.stringify({ pid: process.pid, token, t: Date.now() }));
2816
+ fs.writeFileSync(markerPath, JSON.stringify({ pid: process.pid, token, t: Date.now() }), { mode: 384 });
2747
2817
  return { lockDir, markerPath, token };
2748
2818
  } catch (e) {
2749
2819
  if (e.code !== "EEXIST") throw e;
@@ -2837,7 +2907,7 @@ var require_cache = __commonJS({
2837
2907
  function writeCache(file, data) {
2838
2908
  mkdirSync(path.dirname(file), { recursive: true });
2839
2909
  const tmp = `${file}.tmp.${process.pid}.${Date.now()}`;
2840
- writeFileSync(tmp, JSON.stringify(data));
2910
+ writeFileSync(tmp, JSON.stringify(data), { mode: 384 });
2841
2911
  renameSync(tmp, file);
2842
2912
  }
2843
2913
  var _inflight = /* @__PURE__ */ new Map();
@@ -3008,6 +3078,8 @@ var require_glob = __commonJS({
3008
3078
  if (!matches(includeRes, relPosix)) continue;
3009
3079
  files.push({ rel: relPosix, abs: realTarget, size: st.size });
3010
3080
  totalBytes += st.size;
3081
+ if (files.length > maxFiles) throw new Error(`directory expansion exceeded maxFiles=${maxFiles}. Narrow include or raise the limit.`);
3082
+ if (totalBytes > maxBytes) throw new Error(`directory expansion exceeded maxBytes=${maxBytes} bytes. Narrow include or raise the limit.`);
3011
3083
  } else if (ent.isDirectory()) {
3012
3084
  if (matches(excludeRes, relPosix) || matches(excludeRes, relPosix + "/**")) continue;
3013
3085
  descend(absChild, relPosix);
@@ -3022,6 +3094,8 @@ var require_glob = __commonJS({
3022
3094
  }
3023
3095
  files.push({ rel: relPosix, abs: absChild, size: st.size });
3024
3096
  totalBytes += st.size;
3097
+ if (files.length > maxFiles) throw new Error(`directory expansion exceeded maxFiles=${maxFiles}. Narrow include or raise the limit.`);
3098
+ if (totalBytes > maxBytes) throw new Error(`directory expansion exceeded maxBytes=${maxBytes} bytes. Narrow include or raise the limit.`);
3025
3099
  }
3026
3100
  }
3027
3101
  }
@@ -3346,7 +3420,8 @@ ${ref.inline_text}` });
3346
3420
  res = await f(`${base}/files`, {
3347
3421
  method: "POST",
3348
3422
  headers: { "Authorization": `Bearer ${apiKey}` },
3349
- body: form
3423
+ body: form,
3424
+ redirect: "error"
3350
3425
  });
3351
3426
  } catch (err) {
3352
3427
  const e = new Error(`File upload network error: ${err && err.message || err}`);
@@ -3536,7 +3611,8 @@ ${ref.inline_text}` });
3536
3611
  "Authorization": `Bearer ${apiKey}`
3537
3612
  },
3538
3613
  body: JSON.stringify(payload),
3539
- signal: controller.signal
3614
+ signal: controller.signal,
3615
+ redirect: "error"
3540
3616
  });
3541
3617
  } catch (err) {
3542
3618
  const name = err && err.name;
@@ -4263,7 +4339,7 @@ var require_openrouter = __commonJS({
4263
4339
  const timer = setTimeout(() => controller.abort(), t);
4264
4340
  let res;
4265
4341
  try {
4266
- res = await f(url, { method: "POST", headers, body: JSON.stringify(payload), signal: controller.signal });
4342
+ res = await f(url, { method: "POST", headers, body: JSON.stringify(payload), signal: controller.signal, redirect: "error" });
4267
4343
  } catch (err) {
4268
4344
  const msg = String(err && err.message || err);
4269
4345
  if (err && err.name === "AbortError" || /abort/i.test(msg)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antonbabenko/deliberation-mcp",
3
- "version": "3.6.0",
3
+ "version": "3.6.2",
4
4
  "description": "Deliberation for Claude Code and any MCP host - GPT, Gemini, Grok, and OpenRouter expert subagents.",
5
5
  "mcpName": "io.github.antonbabenko/deliberation",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/antonbabenko/deliberation.git", "directory": "server/mcp" },