@antonbabenko/deliberation-mcp 3.6.2 → 3.7.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.
Files changed (2) hide show
  1. package/dist/index.js +41 -45
  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
  }
@@ -1699,24 +1691,27 @@ var require_antigravity = __commonJS({
1699
1691
  );
1700
1692
  if (!bridge) throw new Error("makeAntigravityProvider requires opts.bridge (core is transport-agnostic; inject the gemini bridge)");
1701
1693
  const model = opts.model || process.env.GEMINI_DEFAULT_MODEL || "auto-gemini-3";
1694
+ const allowImplement = opts.allowImplement === true;
1702
1695
  return {
1703
1696
  name: "gemini",
1704
- capabilities: { canImplement: true, fileUpload: false, multiTurn: true, walksFilesystem: true },
1697
+ // canImplement reflects the construction lock so discovery (panel) is honest about THIS process.
1698
+ capabilities: { canImplement: allowImplement, fileUpload: false, multiTurn: true, walksFilesystem: true },
1705
1699
  async health() {
1706
1700
  return typeof bridge.runGemini === "function" ? { ok: true } : { ok: false, reason: "agy bridge unavailable" };
1707
1701
  },
1708
1702
  async ask(req) {
1709
1703
  const started = Date.now();
1704
+ const implement = allowImplement && req.mode === "implement";
1710
1705
  const includeDirs = (req.files || []).filter((f) => f.dir).map((f) => f.dir);
1711
1706
  const args = bridge.buildAgyArgs({
1712
1707
  prompt: req.prompt,
1713
1708
  model,
1714
- sandbox: "read-only",
1709
+ sandbox: implement ? "workspace-write" : "read-only",
1715
1710
  developerInstructions: req.developerInstructions,
1716
1711
  includeDirs
1717
1712
  });
1718
1713
  try {
1719
- const out = await bridge.runGemini(args, req.cwd, req.timeoutMs, void 0, { readOnly: true, includeDirs });
1714
+ const out = await bridge.runGemini(args, req.cwd, req.timeoutMs, void 0, { readOnly: !implement, includeDirs });
1720
1715
  return { provider: "gemini", model, text: out.response || "", threadId: out.threadId, isError: false, ms: Date.now() - started, reasoningEffort: null, ...out.workspaceMutated ? { workspaceMutated: true } : {} };
1721
1716
  } catch (e) {
1722
1717
  const err = (
@@ -1751,12 +1746,13 @@ var require_codex = __commonJS({
1751
1746
  if (s.includes("rate")) return { errorKind: "rate-limit", retryable: true };
1752
1747
  return { errorKind: "unknown", retryable: false };
1753
1748
  }
1754
- function codexExecArgs() {
1755
- return ["exec", "--sandbox", "read-only", "--skip-git-repo-check"];
1749
+ function codexExecArgs(mode) {
1750
+ const sandbox = mode === "implement" ? "workspace-write" : "read-only";
1751
+ return ["exec", "--sandbox", sandbox, "--skip-git-repo-check"];
1756
1752
  }
1757
- function defaultRun({ prompt, cwd, timeoutMs }) {
1753
+ function defaultRun({ prompt, cwd, timeoutMs, mode }) {
1758
1754
  return new Promise((resolve) => {
1759
- const child = spawn("codex", codexExecArgs(), { cwd: cwd || process.cwd() });
1755
+ const child = spawn("codex", codexExecArgs(mode), { cwd: cwd || process.cwd() });
1760
1756
  let stdout = "", stderr = "", settled = false;
1761
1757
  const timer = timeoutMs ? setTimeout(() => child.kill("SIGKILL"), timeoutMs) : null;
1762
1758
  if (timer) timer.unref();
@@ -1780,21 +1776,24 @@ var require_codex = __commonJS({
1780
1776
  function makeCodexProvider(opts = {}) {
1781
1777
  const run = opts.run || defaultRun;
1782
1778
  const model = opts.model || "default";
1779
+ const allowImplement = opts.allowImplement === true;
1783
1780
  return {
1784
1781
  name: "codex",
1785
- capabilities: { canImplement: true, fileUpload: false, multiTurn: false, walksFilesystem: true },
1786
- // Option A: no threadId continuity
1782
+ // canImplement reflects the construction lock so discovery (panel) is honest about THIS
1783
+ // process. Option A: no threadId continuity (multiTurn:false).
1784
+ capabilities: { canImplement: allowImplement, fileUpload: false, multiTurn: false, walksFilesystem: true },
1787
1785
  async health() {
1788
1786
  return { ok: true };
1789
1787
  },
1790
1788
  async ask(req) {
1791
1789
  const started = Date.now();
1790
+ const mode = allowImplement && req.mode === "implement" ? "implement" : "advisory";
1792
1791
  const full = req.developerInstructions ? `${req.developerInstructions}
1793
1792
 
1794
1793
  ---
1795
1794
 
1796
1795
  ${req.prompt}` : req.prompt;
1797
- const { code, stdout, stderr } = await run({ prompt: full, cwd: req.cwd, timeoutMs: req.timeoutMs });
1796
+ const { code, stdout, stderr } = await run({ prompt: full, cwd: req.cwd, timeoutMs: req.timeoutMs, mode });
1798
1797
  if (code === 0) {
1799
1798
  return { provider: "codex", model, text: stdout.trim(), isError: false, ms: Date.now() - started, reasoningEffort: null };
1800
1799
  }
@@ -2225,7 +2224,7 @@ ${prompt}`;
2225
2224
  args.push("-p", prompt);
2226
2225
  return args;
2227
2226
  }
2228
- var ADVISORY_ENV_SCRUB = ["GITHUB_TOKEN", "GH_TOKEN", "GIT_ASKPASS", "SSH_AUTH_SOCK"];
2227
+ var ADVISORY_ENV_SCRUB = ["GIT_ASKPASS", "SSH_AUTH_SOCK"];
2229
2228
  var CREDENTIAL_NAME_RE = /(?:^|_)(?:KEY|TOKEN|SECRET|SECRETS|PASSWORD|PASSWD|CREDENTIAL|CREDENTIALS)$|API_KEY|ACCESS_KEY|SESSION_TOKEN|PRIVATE_KEY/i;
2230
2229
  function advisoryEnv(env) {
2231
2230
  const out = { ...env };
@@ -2397,7 +2396,10 @@ ${prompt}`;
2397
2396
  process.stderr.write("[deliberation] agy read-only run wrapped in sandbox-exec (workspace writes denied)\n");
2398
2397
  }
2399
2398
  const agyProcess = spawn(spawnPlan.cmd, spawnPlan.argv, {
2400
- env: readOnly ? advisoryEnv(process.env) : process.env,
2399
+ // Scrub credentials in BOTH read-only and workspace-write runs. A write run
2400
+ // legitimately mutates the worktree but still has no need for the operator's
2401
+ // API keys / GIT_ASKPASS / SSH_AUTH_SOCK - the human commits and pushes, not agy.
2402
+ env: advisoryEnv(process.env),
2401
2403
  shell: false,
2402
2404
  cwd: effCwd,
2403
2405
  // agy -p (print mode) waits for stdin EOF before returning; if the stdin
@@ -3045,6 +3047,12 @@ var require_glob = __commonJS({
3045
3047
  for (const c of res) if (c.re.test(rel)) return true;
3046
3048
  return false;
3047
3049
  }
3050
+ function pushFile(rel, abs, size) {
3051
+ files.push({ rel, abs, size });
3052
+ totalBytes += size;
3053
+ if (files.length > maxFiles) throw new Error(`directory expansion exceeded maxFiles=${maxFiles}. Narrow include or raise the limit.`);
3054
+ if (totalBytes > maxBytes) throw new Error(`directory expansion exceeded maxBytes=${maxBytes} bytes. Narrow include or raise the limit.`);
3055
+ }
3048
3056
  function descend(absDir, relDir) {
3049
3057
  let entries;
3050
3058
  try {
@@ -3076,10 +3084,7 @@ var require_glob = __commonJS({
3076
3084
  if (!st.isFile()) continue;
3077
3085
  if (matches(excludeRes, relPosix)) continue;
3078
3086
  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.`);
3087
+ pushFile(relPosix, realTarget, st.size);
3083
3088
  } else if (ent.isDirectory()) {
3084
3089
  if (matches(excludeRes, relPosix) || matches(excludeRes, relPosix + "/**")) continue;
3085
3090
  descend(absChild, relPosix);
@@ -3092,20 +3097,11 @@ var require_glob = __commonJS({
3092
3097
  } catch (_) {
3093
3098
  continue;
3094
3099
  }
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.`);
3100
+ pushFile(relPosix, absChild, st.size);
3099
3101
  }
3100
3102
  }
3101
3103
  }
3102
3104
  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
3105
  files.sort((a, b) => a.rel.localeCompare(b.rel, "en"));
3110
3106
  return { files, totalBytes };
3111
3107
  }
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.0",
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" },