@antonbabenko/deliberation-mcp 3.6.2 → 3.7.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 (2) hide show
  1. package/dist/index.js +49 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -241,7 +241,10 @@ var require_provider = __commonJS({
241
241
  var STRIP_TRAIL = /[*_`~\s]+$/;
242
242
  var HEADING_RE = /^#{1,6}\s/;
243
243
  function normVerdict(tok) {
244
- return tok.replace(/\s+/g, "_").toUpperCase();
244
+ return (
245
+ /** @type {any} */
246
+ tok.replace(/\s+/g, "_").toUpperCase()
247
+ );
245
248
  }
246
249
  function parseReview(text) {
247
250
  const raw = safeString(text);
@@ -259,36 +262,24 @@ var require_provider = __commonJS({
259
262
  function resolveVerdict(lines) {
260
263
  for (const ln of lines) {
261
264
  const m = ln.match(SENTINEL_RE);
262
- if (m) return (
263
- /** @type {any} */
264
- normVerdict(m[1])
265
- );
265
+ if (m) return normVerdict(m[1]);
266
266
  }
267
267
  for (const ln of lines) {
268
268
  const m = ln.match(VERDICT_RE);
269
- if (m) return (
270
- /** @type {any} */
271
- normVerdict(m[1])
272
- );
269
+ if (m) return normVerdict(m[1]);
273
270
  }
274
271
  for (let i = 0; i < lines.length; i++) {
275
272
  if (!VERDICT_WORD_RE.test(lines[i].trim())) continue;
276
273
  for (let j = i + 1; j < lines.length && j <= i + 3; j++) {
277
274
  const t = lines[j].replace(MD_EMPHASIS, "").trim();
278
275
  if (!t) continue;
279
- if (TOKEN_LINE_RE.test(t)) return (
280
- /** @type {any} */
281
- normVerdict(t)
282
- );
276
+ if (TOKEN_LINE_RE.test(t)) return normVerdict(t);
283
277
  break;
284
278
  }
285
279
  }
286
280
  const nonEmpty = lines.map((l) => l.replace(MD_EMPHASIS, "").trim()).filter((t) => t !== "");
287
281
  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
- );
282
+ if (t && TOKEN_LINE_RE.test(t)) return normVerdict(t);
292
283
  }
293
284
  return null;
294
285
  }
@@ -345,6 +336,7 @@ var require_consensus_loop = __commonJS({
345
336
  "use strict";
346
337
  var MAX_ROUNDS_DEFAULT = 5;
347
338
  var VERDICTS = Object.freeze(["APPROVE", "REQUEST_CHANGES", "REJECT"]);
339
+ var REVIEW_FORMAT_INSTRUCTION = "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.";
348
340
  function assertStatus(state, expected, op) {
349
341
  if (state.status !== expected) {
350
342
  throw new Error(`${op}: expected status '${expected}', got '${state.status}'`);
@@ -387,12 +379,12 @@ ${state.currentPlan}`
387
379
  const peerPrompt = [
388
380
  body,
389
381
  "Review the plan for correctness, security, scope, ambiguity, performance, and ops gaps.",
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."
382
+ REVIEW_FORMAT_INSTRUCTION
391
383
  ].join("\n\n");
392
384
  const blindPrompt = [
393
385
  body,
394
386
  "Give your own independent verdict BEFORE seeing peer opinions.",
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."
387
+ REVIEW_FORMAT_INSTRUCTION
396
388
  ].join("\n\n");
397
389
  return { peerPrompt, blindPrompt };
398
390
  }
@@ -640,9 +632,14 @@ var require_orchestrate = __commonJS({
640
632
  async function askOne2(provider, req, opts = {}) {
641
633
  return callProvider(provider, req, opts.logger || NULL_LOGGER, opts.tool || "ask-one", opts.cache, opts.orientationFiles);
642
634
  }
635
+ var MAX_PEER_OPINION_CHARS = 2e3;
636
+ function capPeerOpinion(text) {
637
+ if (typeof text !== "string" || text.length <= MAX_PEER_OPINION_CHARS) return text;
638
+ return text.slice(0, MAX_PEER_OPINION_CHARS) + "\n...[truncated]";
639
+ }
643
640
  function buildArbiterPrompt(question, opinions) {
644
641
  const blocks = opinions.map((o, i) => `### Opinion ${i + 1}
645
- ${o.text}`).join("\n\n");
642
+ ${capPeerOpinion(o.text)}`).join("\n\n");
646
643
  return [
647
644
  "You are the arbiter. Below are independent expert opinions on the same question.",
648
645
  "Cross-review them: note where they agree, where they disagree, and which view is best supported.",
@@ -696,7 +693,7 @@ ${blocks}`,
696
693
  const peerBlocks = results.map((r) => {
697
694
  if (r.isError) return `Peer ${r.source}: ERRORED`;
698
695
  const issues = (r.criticalIssues || []).map((i) => ` - [${i.category}] ${i.description}`).join("\n");
699
- return `Peer ${r.source}: ${r.verdict || "UNKNOWN"}${issues ? "\n" + issues : ""}`;
696
+ return capPeerOpinion(`Peer ${r.source}: ${r.verdict || "UNKNOWN"}${issues ? "\n" + issues : ""}`);
700
697
  }).join("\n");
701
698
  return [
702
699
  "ADJUDICATE the peer reviews below and give ONE overall verdict.",
@@ -798,7 +795,7 @@ ${feedback || "(reviewers gave no specific issues; tighten the weakest part)"}`
798
795
  opinions: lastResults
799
796
  };
800
797
  }
801
- module2.exports = { askAll: askAll2, askOne: askOne2, consensus: consensus2, buildArbiterPrompt, runToConvergence: runToConvergence2 };
798
+ module2.exports = { askAll: askAll2, askOne: askOne2, consensus: consensus2, buildArbiterPrompt, buildAdjudicationPrompt, runToConvergence: runToConvergence2 };
802
799
  }
803
800
  });
804
801
 
@@ -1699,24 +1696,27 @@ var require_antigravity = __commonJS({
1699
1696
  );
1700
1697
  if (!bridge) throw new Error("makeAntigravityProvider requires opts.bridge (core is transport-agnostic; inject the gemini bridge)");
1701
1698
  const model = opts.model || process.env.GEMINI_DEFAULT_MODEL || "auto-gemini-3";
1699
+ const allowImplement = opts.allowImplement === true;
1702
1700
  return {
1703
1701
  name: "gemini",
1704
- capabilities: { canImplement: true, fileUpload: false, multiTurn: true, walksFilesystem: true },
1702
+ // canImplement reflects the construction lock so discovery (panel) is honest about THIS process.
1703
+ capabilities: { canImplement: allowImplement, fileUpload: false, multiTurn: true, walksFilesystem: true },
1705
1704
  async health() {
1706
1705
  return typeof bridge.runGemini === "function" ? { ok: true } : { ok: false, reason: "agy bridge unavailable" };
1707
1706
  },
1708
1707
  async ask(req) {
1709
1708
  const started = Date.now();
1709
+ const implement = allowImplement && req.mode === "implement";
1710
1710
  const includeDirs = (req.files || []).filter((f) => f.dir).map((f) => f.dir);
1711
1711
  const args = bridge.buildAgyArgs({
1712
1712
  prompt: req.prompt,
1713
1713
  model,
1714
- sandbox: "read-only",
1714
+ sandbox: implement ? "workspace-write" : "read-only",
1715
1715
  developerInstructions: req.developerInstructions,
1716
1716
  includeDirs
1717
1717
  });
1718
1718
  try {
1719
- const out = await bridge.runGemini(args, req.cwd, req.timeoutMs, void 0, { readOnly: true, includeDirs });
1719
+ const out = await bridge.runGemini(args, req.cwd, req.timeoutMs, void 0, { readOnly: !implement, includeDirs });
1720
1720
  return { provider: "gemini", model, text: out.response || "", threadId: out.threadId, isError: false, ms: Date.now() - started, reasoningEffort: null, ...out.workspaceMutated ? { workspaceMutated: true } : {} };
1721
1721
  } catch (e) {
1722
1722
  const err = (
@@ -1751,12 +1751,13 @@ var require_codex = __commonJS({
1751
1751
  if (s.includes("rate")) return { errorKind: "rate-limit", retryable: true };
1752
1752
  return { errorKind: "unknown", retryable: false };
1753
1753
  }
1754
- function codexExecArgs() {
1755
- return ["exec", "--sandbox", "read-only", "--skip-git-repo-check"];
1754
+ function codexExecArgs(mode) {
1755
+ const sandbox = mode === "implement" ? "workspace-write" : "read-only";
1756
+ return ["exec", "--sandbox", sandbox, "--skip-git-repo-check"];
1756
1757
  }
1757
- function defaultRun({ prompt, cwd, timeoutMs }) {
1758
+ function defaultRun({ prompt, cwd, timeoutMs, mode }) {
1758
1759
  return new Promise((resolve) => {
1759
- const child = spawn("codex", codexExecArgs(), { cwd: cwd || process.cwd() });
1760
+ const child = spawn("codex", codexExecArgs(mode), { cwd: cwd || process.cwd() });
1760
1761
  let stdout = "", stderr = "", settled = false;
1761
1762
  const timer = timeoutMs ? setTimeout(() => child.kill("SIGKILL"), timeoutMs) : null;
1762
1763
  if (timer) timer.unref();
@@ -1780,21 +1781,24 @@ var require_codex = __commonJS({
1780
1781
  function makeCodexProvider(opts = {}) {
1781
1782
  const run = opts.run || defaultRun;
1782
1783
  const model = opts.model || "default";
1784
+ const allowImplement = opts.allowImplement === true;
1783
1785
  return {
1784
1786
  name: "codex",
1785
- capabilities: { canImplement: true, fileUpload: false, multiTurn: false, walksFilesystem: true },
1786
- // Option A: no threadId continuity
1787
+ // canImplement reflects the construction lock so discovery (panel) is honest about THIS
1788
+ // process. Option A: no threadId continuity (multiTurn:false).
1789
+ capabilities: { canImplement: allowImplement, fileUpload: false, multiTurn: false, walksFilesystem: true },
1787
1790
  async health() {
1788
1791
  return { ok: true };
1789
1792
  },
1790
1793
  async ask(req) {
1791
1794
  const started = Date.now();
1795
+ const mode = allowImplement && req.mode === "implement" ? "implement" : "advisory";
1792
1796
  const full = req.developerInstructions ? `${req.developerInstructions}
1793
1797
 
1794
1798
  ---
1795
1799
 
1796
1800
  ${req.prompt}` : req.prompt;
1797
- const { code, stdout, stderr } = await run({ prompt: full, cwd: req.cwd, timeoutMs: req.timeoutMs });
1801
+ const { code, stdout, stderr } = await run({ prompt: full, cwd: req.cwd, timeoutMs: req.timeoutMs, mode });
1798
1802
  if (code === 0) {
1799
1803
  return { provider: "codex", model, text: stdout.trim(), isError: false, ms: Date.now() - started, reasoningEffort: null };
1800
1804
  }
@@ -2225,7 +2229,7 @@ ${prompt}`;
2225
2229
  args.push("-p", prompt);
2226
2230
  return args;
2227
2231
  }
2228
- var ADVISORY_ENV_SCRUB = ["GITHUB_TOKEN", "GH_TOKEN", "GIT_ASKPASS", "SSH_AUTH_SOCK"];
2232
+ var ADVISORY_ENV_SCRUB = ["GIT_ASKPASS", "SSH_AUTH_SOCK"];
2229
2233
  var CREDENTIAL_NAME_RE = /(?:^|_)(?:KEY|TOKEN|SECRET|SECRETS|PASSWORD|PASSWD|CREDENTIAL|CREDENTIALS)$|API_KEY|ACCESS_KEY|SESSION_TOKEN|PRIVATE_KEY/i;
2230
2234
  function advisoryEnv(env) {
2231
2235
  const out = { ...env };
@@ -2397,7 +2401,10 @@ ${prompt}`;
2397
2401
  process.stderr.write("[deliberation] agy read-only run wrapped in sandbox-exec (workspace writes denied)\n");
2398
2402
  }
2399
2403
  const agyProcess = spawn(spawnPlan.cmd, spawnPlan.argv, {
2400
- env: readOnly ? advisoryEnv(process.env) : process.env,
2404
+ // Scrub credentials in BOTH read-only and workspace-write runs. A write run
2405
+ // legitimately mutates the worktree but still has no need for the operator's
2406
+ // API keys / GIT_ASKPASS / SSH_AUTH_SOCK - the human commits and pushes, not agy.
2407
+ env: advisoryEnv(process.env),
2401
2408
  shell: false,
2402
2409
  cwd: effCwd,
2403
2410
  // agy -p (print mode) waits for stdin EOF before returning; if the stdin
@@ -3045,6 +3052,12 @@ var require_glob = __commonJS({
3045
3052
  for (const c of res) if (c.re.test(rel)) return true;
3046
3053
  return false;
3047
3054
  }
3055
+ function pushFile(rel, abs, size) {
3056
+ files.push({ rel, abs, size });
3057
+ totalBytes += size;
3058
+ if (files.length > maxFiles) throw new Error(`directory expansion exceeded maxFiles=${maxFiles}. Narrow include or raise the limit.`);
3059
+ if (totalBytes > maxBytes) throw new Error(`directory expansion exceeded maxBytes=${maxBytes} bytes. Narrow include or raise the limit.`);
3060
+ }
3048
3061
  function descend(absDir, relDir) {
3049
3062
  let entries;
3050
3063
  try {
@@ -3076,10 +3089,7 @@ var require_glob = __commonJS({
3076
3089
  if (!st.isFile()) continue;
3077
3090
  if (matches(excludeRes, relPosix)) continue;
3078
3091
  if (!matches(includeRes, relPosix)) continue;
3079
- files.push({ rel: relPosix, abs: realTarget, size: st.size });
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.`);
3092
+ pushFile(relPosix, realTarget, st.size);
3083
3093
  } else if (ent.isDirectory()) {
3084
3094
  if (matches(excludeRes, relPosix) || matches(excludeRes, relPosix + "/**")) continue;
3085
3095
  descend(absChild, relPosix);
@@ -3092,20 +3102,11 @@ var require_glob = __commonJS({
3092
3102
  } catch (_) {
3093
3103
  continue;
3094
3104
  }
3095
- files.push({ rel: relPosix, abs: absChild, size: st.size });
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.`);
3105
+ pushFile(relPosix, absChild, st.size);
3099
3106
  }
3100
3107
  }
3101
3108
  }
3102
3109
  descend(rootAbs, "");
3103
- if (files.length > maxFiles) {
3104
- throw new Error(`directory expansion selected ${files.length} files; exceeds maxFiles=${maxFiles}. Narrow include or raise the limit.`);
3105
- }
3106
- if (totalBytes > maxBytes) {
3107
- throw new Error(`directory expansion selected ${totalBytes} bytes; exceeds maxBytes=${maxBytes}. Narrow include or raise the limit.`);
3108
- }
3109
3110
  files.sort((a, b) => a.rel.localeCompare(b.rel, "en"));
3110
3111
  return { files, totalBytes };
3111
3112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antonbabenko/deliberation-mcp",
3
- "version": "3.6.2",
3
+ "version": "3.7.1",
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" },