@antonbabenko/deliberation-mcp 3.1.1 → 3.2.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/dist/index.js +495 -114
- package/dist/setup.js +10 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -87,9 +87,9 @@ var require_registry = __commonJS({
|
|
|
87
87
|
var require_provider = __commonJS({
|
|
88
88
|
"../../core/provider.js"(exports2, module2) {
|
|
89
89
|
"use strict";
|
|
90
|
-
function toErrorResult(name, model, started, err, classify) {
|
|
90
|
+
function toErrorResult(name, model, started, err, classify, extra) {
|
|
91
91
|
const { errorKind, retryable } = classify(err && err.status, err && err.code);
|
|
92
|
-
return { provider: name, model, isError: true, errorKind, retryable, ms: Date.now() - started };
|
|
92
|
+
return { ...extra && typeof extra === "object" ? extra : {}, provider: name, model, isError: true, errorKind, retryable, ms: Date.now() - started };
|
|
93
93
|
}
|
|
94
94
|
function deepFreeze(o) {
|
|
95
95
|
if (o && typeof o === "object" && !Object.isFrozen(o)) {
|
|
@@ -430,17 +430,130 @@ ${state.currentPlan}`
|
|
|
430
430
|
}
|
|
431
431
|
});
|
|
432
432
|
|
|
433
|
+
// ../../core/debug-log.js
|
|
434
|
+
var require_debug_log = __commonJS({
|
|
435
|
+
"../../core/debug-log.js"(exports2, module2) {
|
|
436
|
+
"use strict";
|
|
437
|
+
var NULL_LOGGER = Object.freeze({ logEvent(_event) {
|
|
438
|
+
} });
|
|
439
|
+
var ALLOWED_KEYS = Object.freeze([
|
|
440
|
+
"event",
|
|
441
|
+
"at",
|
|
442
|
+
"tool",
|
|
443
|
+
"provider",
|
|
444
|
+
"model",
|
|
445
|
+
"reasoningEffort",
|
|
446
|
+
"ms",
|
|
447
|
+
"isError",
|
|
448
|
+
"errorKind",
|
|
449
|
+
"usage",
|
|
450
|
+
"round",
|
|
451
|
+
"verdict",
|
|
452
|
+
"blindVerdict",
|
|
453
|
+
"converged",
|
|
454
|
+
"acceptedCritical",
|
|
455
|
+
"voices"
|
|
456
|
+
]);
|
|
457
|
+
function sanitizeEvent(event) {
|
|
458
|
+
const out = {};
|
|
459
|
+
if (!event || typeof event !== "object") return out;
|
|
460
|
+
for (const k of ALLOWED_KEYS) {
|
|
461
|
+
const v = (
|
|
462
|
+
/** @type {Record<string, unknown>} */
|
|
463
|
+
event[k]
|
|
464
|
+
);
|
|
465
|
+
if (v !== void 0) out[k] = v;
|
|
466
|
+
}
|
|
467
|
+
return out;
|
|
468
|
+
}
|
|
469
|
+
function createFileLogger(path) {
|
|
470
|
+
const fs = require("node:fs");
|
|
471
|
+
return {
|
|
472
|
+
logEvent(event) {
|
|
473
|
+
try {
|
|
474
|
+
fs.appendFileSync(path, JSON.stringify(sanitizeEvent(event)) + "\n");
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function composeLoggers(sinks) {
|
|
481
|
+
const real = (Array.isArray(sinks) ? sinks : []).filter((s) => s && typeof s.logEvent === "function");
|
|
482
|
+
if (!real.length) return NULL_LOGGER;
|
|
483
|
+
if (real.length === 1) return real[0];
|
|
484
|
+
return {
|
|
485
|
+
logEvent(event) {
|
|
486
|
+
for (const s of real) {
|
|
487
|
+
try {
|
|
488
|
+
s.logEvent(event);
|
|
489
|
+
} catch {
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
module2.exports = { NULL_LOGGER, createFileLogger, composeLoggers, sanitizeEvent, ALLOWED_KEYS };
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
433
499
|
// ../../core/orchestrate.js
|
|
434
500
|
var require_orchestrate = __commonJS({
|
|
435
501
|
"../../core/orchestrate.js"(exports2, module2) {
|
|
436
502
|
"use strict";
|
|
437
503
|
var { parseReview } = require_provider();
|
|
438
504
|
var loop = require_consensus_loop();
|
|
439
|
-
|
|
505
|
+
var { NULL_LOGGER } = require_debug_log();
|
|
506
|
+
function logProviderResult(logger, tool, r) {
|
|
507
|
+
try {
|
|
508
|
+
logger.logEvent({
|
|
509
|
+
event: "provider_result",
|
|
510
|
+
at: Date.now(),
|
|
511
|
+
tool,
|
|
512
|
+
provider: r.provider,
|
|
513
|
+
model: r.model,
|
|
514
|
+
reasoningEffort: r.reasoningEffort ?? null,
|
|
515
|
+
ms: r.ms,
|
|
516
|
+
isError: r.isError,
|
|
517
|
+
errorKind: r.isError ? r.errorKind : void 0,
|
|
518
|
+
usage: r.isError ? void 0 : r.usage
|
|
519
|
+
});
|
|
520
|
+
} catch {
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async function callProvider(provider, req, logger, tool, cache) {
|
|
524
|
+
const useCache = cache && !(Array.isArray(req.files) && req.files.length);
|
|
525
|
+
if (useCache) {
|
|
526
|
+
const hit = cache.get(provider.name, req);
|
|
527
|
+
if (hit) {
|
|
528
|
+
logProviderResult(logger, tool, hit);
|
|
529
|
+
return hit;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const started = Date.now();
|
|
533
|
+
let r;
|
|
534
|
+
try {
|
|
535
|
+
r = await provider.ask({ ...req, files: req.files ? req.files.map((f) => ({ ...f })) : void 0 });
|
|
536
|
+
} catch (e) {
|
|
537
|
+
r = {
|
|
538
|
+
provider: provider.name,
|
|
539
|
+
model: "unknown",
|
|
540
|
+
isError: true,
|
|
541
|
+
errorKind: "unknown",
|
|
542
|
+
retryable: false,
|
|
543
|
+
message: String(e && /** @type {any} */
|
|
544
|
+
e.message || e),
|
|
545
|
+
ms: Date.now() - started
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
logProviderResult(logger, tool, r);
|
|
549
|
+
if (useCache) cache.set(provider.name, req, r);
|
|
550
|
+
return r;
|
|
551
|
+
}
|
|
552
|
+
async function askAll2(providers, req, opts = {}) {
|
|
553
|
+
const logger = opts.logger || NULL_LOGGER;
|
|
554
|
+
const tool = opts.tool || "ask-all";
|
|
440
555
|
const settled = await Promise.allSettled(
|
|
441
|
-
providers.map(
|
|
442
|
-
(p) => p.ask({ ...req, files: req.files ? req.files.map((f) => ({ ...f })) : void 0 })
|
|
443
|
-
)
|
|
556
|
+
providers.map((p) => callProvider(p, req, logger, tool, opts.cache))
|
|
444
557
|
);
|
|
445
558
|
return settled.map(
|
|
446
559
|
(s, i) => s.status === "fulfilled" ? s.value : {
|
|
@@ -454,8 +567,8 @@ var require_orchestrate = __commonJS({
|
|
|
454
567
|
}
|
|
455
568
|
);
|
|
456
569
|
}
|
|
457
|
-
async function askOne2(provider, req) {
|
|
458
|
-
return provider
|
|
570
|
+
async function askOne2(provider, req, opts = {}) {
|
|
571
|
+
return callProvider(provider, req, opts.logger || NULL_LOGGER, opts.tool || "ask-one", opts.cache);
|
|
459
572
|
}
|
|
460
573
|
function buildArbiterPrompt(question, opinions) {
|
|
461
574
|
const blocks = opinions.map((o, i) => `### Opinion ${i + 1}
|
|
@@ -490,7 +603,7 @@ ${blocks}`,
|
|
|
490
603
|
/** @type {DelegationResult|null} */
|
|
491
604
|
null
|
|
492
605
|
);
|
|
493
|
-
const [opinions, blindVerdict] = await Promise.all([askAll2(providers, req), blindPromise]);
|
|
606
|
+
const [opinions, blindVerdict] = await Promise.all([askAll2(providers, req, { logger: opts.logger, tool: "consensus" }), blindPromise]);
|
|
494
607
|
const ok = (
|
|
495
608
|
/** @type {DelegationSuccess[]} */
|
|
496
609
|
opinions.filter((o) => !o.isError)
|
|
@@ -539,6 +652,7 @@ ${feedback || "(reviewers gave no specific issues; tighten the weakest part)"}`
|
|
|
539
652
|
}
|
|
540
653
|
async function runToConvergence2(providers, req, opts = {}) {
|
|
541
654
|
const arbiter = opts.arbiter;
|
|
655
|
+
const logger = opts.logger || NULL_LOGGER;
|
|
542
656
|
if (!arbiter) return { converged: false, verdict: null, confidence: "none", rounds: [], opinions: [], error: "no-arbiter" };
|
|
543
657
|
let state = loop.initConsensusLoop({
|
|
544
658
|
plan: typeof req.prompt === "string" ? req.prompt : "",
|
|
@@ -550,9 +664,10 @@ ${feedback || "(reviewers gave no specific issues; tighten the weakest part)"}`
|
|
|
550
664
|
try {
|
|
551
665
|
while (state.status !== "converged" && state.status !== "unresolved") {
|
|
552
666
|
const { peerPrompt, blindPrompt } = loop.prepareRound(state);
|
|
667
|
+
const roundNo = state.round;
|
|
553
668
|
const [blindRes, peerResults] = await Promise.all([
|
|
554
669
|
Promise.resolve().then(() => arbiter.ask({ ...req, prompt: blindPrompt })).then((r) => r, () => null),
|
|
555
|
-
askAll2(providers, { ...req, prompt: peerPrompt })
|
|
670
|
+
askAll2(providers, { ...req, prompt: peerPrompt }, { logger, tool: "consensus" })
|
|
556
671
|
]);
|
|
557
672
|
state = loop.recordBlindVerdict(state, okText(blindRes) || "(blind pass unavailable)");
|
|
558
673
|
lastResults = peerResults.map(
|
|
@@ -570,6 +685,19 @@ ${feedback || "(reviewers gave no specific issues; tighten the weakest part)"}`
|
|
|
570
685
|
} catch {
|
|
571
686
|
}
|
|
572
687
|
state = loop.submitAdjudication(state, { verdict, decisions: [] });
|
|
688
|
+
try {
|
|
689
|
+
logger.logEvent({
|
|
690
|
+
event: "round",
|
|
691
|
+
at: Date.now(),
|
|
692
|
+
tool: "consensus",
|
|
693
|
+
round: roundNo,
|
|
694
|
+
verdict,
|
|
695
|
+
converged: state.status === "converged",
|
|
696
|
+
blindVerdict: okText(blindRes) ? "(recorded)" : null,
|
|
697
|
+
voices: lastResults.length
|
|
698
|
+
});
|
|
699
|
+
} catch {
|
|
700
|
+
}
|
|
573
701
|
if (state.status === "converged") break;
|
|
574
702
|
let revised = state.currentPlan;
|
|
575
703
|
try {
|
|
@@ -918,6 +1046,147 @@ var require_loop_store = __commonJS({
|
|
|
918
1046
|
}
|
|
919
1047
|
});
|
|
920
1048
|
|
|
1049
|
+
// ../../core/result-cache.js
|
|
1050
|
+
var require_result_cache = __commonJS({
|
|
1051
|
+
"../../core/result-cache.js"(exports2, module2) {
|
|
1052
|
+
"use strict";
|
|
1053
|
+
var DEFAULT_MAX = 100;
|
|
1054
|
+
var DEFAULT_TTL_MS = 6e5;
|
|
1055
|
+
function keyFor(providerName, req) {
|
|
1056
|
+
const files = Array.isArray(req.files) ? req.files.map((f) => `${f.path || ""}|${f.dir || ""}|${f.file_id || ""}|${f.file_url || ""}|${f.mode || ""}`) : [];
|
|
1057
|
+
return JSON.stringify([
|
|
1058
|
+
providerName,
|
|
1059
|
+
req.model || "",
|
|
1060
|
+
req.reasoningEffort || "",
|
|
1061
|
+
typeof req.temperature === "number" ? req.temperature : "",
|
|
1062
|
+
req.developerInstructions || "",
|
|
1063
|
+
req.prompt || "",
|
|
1064
|
+
files
|
|
1065
|
+
]);
|
|
1066
|
+
}
|
|
1067
|
+
function makeResultCache(opts = {}) {
|
|
1068
|
+
const max = Number.isInteger(opts.max) && /** @type {number} */
|
|
1069
|
+
opts.max > 0 ? (
|
|
1070
|
+
/** @type {number} */
|
|
1071
|
+
opts.max
|
|
1072
|
+
) : DEFAULT_MAX;
|
|
1073
|
+
const ttlMs = Number.isInteger(opts.ttlMs) && /** @type {number} */
|
|
1074
|
+
opts.ttlMs > 0 ? (
|
|
1075
|
+
/** @type {number} */
|
|
1076
|
+
opts.ttlMs
|
|
1077
|
+
) : DEFAULT_TTL_MS;
|
|
1078
|
+
const map = /* @__PURE__ */ new Map();
|
|
1079
|
+
return {
|
|
1080
|
+
get(providerName, req) {
|
|
1081
|
+
const k = keyFor(providerName, req);
|
|
1082
|
+
const e = map.get(k);
|
|
1083
|
+
if (!e) return void 0;
|
|
1084
|
+
if (Date.now() - e.at > ttlMs) {
|
|
1085
|
+
map.delete(k);
|
|
1086
|
+
return void 0;
|
|
1087
|
+
}
|
|
1088
|
+
map.delete(k);
|
|
1089
|
+
map.set(k, e);
|
|
1090
|
+
return e.result;
|
|
1091
|
+
},
|
|
1092
|
+
set(providerName, req, result) {
|
|
1093
|
+
if (!result || result.isError) return;
|
|
1094
|
+
const k = keyFor(providerName, req);
|
|
1095
|
+
map.set(k, { result: { .../** @type {DelegationSuccess} */
|
|
1096
|
+
result, cached: true, ms: 0 }, at: Date.now() });
|
|
1097
|
+
if (map.size > max) {
|
|
1098
|
+
const oldest = map.keys().next().value;
|
|
1099
|
+
if (oldest !== void 0) map.delete(oldest);
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
get size() {
|
|
1103
|
+
return map.size;
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
module2.exports = { makeResultCache, keyFor, DEFAULT_MAX, DEFAULT_TTL_MS };
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
// ../../core/paths.js
|
|
1112
|
+
var require_paths = __commonJS({
|
|
1113
|
+
"../../core/paths.js"(exports2, module2) {
|
|
1114
|
+
"use strict";
|
|
1115
|
+
var os = require("node:os");
|
|
1116
|
+
var path = require("node:path");
|
|
1117
|
+
function resolveInjection(opts) {
|
|
1118
|
+
return {
|
|
1119
|
+
home: opts && opts.home || os.homedir(),
|
|
1120
|
+
env: opts && opts.env || process.env,
|
|
1121
|
+
platform: opts && opts.platform || process.platform
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
function isUsableBase(value, platform) {
|
|
1125
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
1126
|
+
const impl = platform === "win32" ? path.win32 : path.posix;
|
|
1127
|
+
return impl.isAbsolute(value);
|
|
1128
|
+
}
|
|
1129
|
+
function canonicalConfigDir(home, env, platform) {
|
|
1130
|
+
if (platform === "win32") {
|
|
1131
|
+
const appData = env.APPDATA;
|
|
1132
|
+
const base2 = isUsableBase(appData, platform) ? appData : path.join(home, "AppData", "Roaming");
|
|
1133
|
+
return path.join(base2, "deliberation");
|
|
1134
|
+
}
|
|
1135
|
+
const xdg = env.XDG_CONFIG_HOME;
|
|
1136
|
+
const base = isUsableBase(xdg, platform) ? xdg : path.join(home, ".config");
|
|
1137
|
+
return path.join(base, "deliberation");
|
|
1138
|
+
}
|
|
1139
|
+
function resolveConfigPath(opts) {
|
|
1140
|
+
const { home, env, platform } = resolveInjection(opts);
|
|
1141
|
+
const override = env.DELIBERATION_CONFIG;
|
|
1142
|
+
if (typeof override === "string" && override.length > 0) {
|
|
1143
|
+
return override;
|
|
1144
|
+
}
|
|
1145
|
+
return path.join(canonicalConfigDir(home, env, platform), "config.json");
|
|
1146
|
+
}
|
|
1147
|
+
function canonicalCacheDir(home, env, platform) {
|
|
1148
|
+
if (platform === "win32") {
|
|
1149
|
+
const localAppData = env.LOCALAPPDATA;
|
|
1150
|
+
const base2 = isUsableBase(localAppData, platform) ? localAppData : path.join(home, "AppData", "Local");
|
|
1151
|
+
return path.join(base2, "deliberation");
|
|
1152
|
+
}
|
|
1153
|
+
const xdg = env.XDG_CACHE_HOME;
|
|
1154
|
+
const base = isUsableBase(xdg, platform) ? xdg : path.join(home, ".cache");
|
|
1155
|
+
return path.join(base, "deliberation");
|
|
1156
|
+
}
|
|
1157
|
+
function resolveGrokCachePath(opts) {
|
|
1158
|
+
const { home, env, platform } = resolveInjection(opts);
|
|
1159
|
+
const override = env.DELIBERATION_CACHE;
|
|
1160
|
+
if (typeof override === "string" && override.length > 0) {
|
|
1161
|
+
return override;
|
|
1162
|
+
}
|
|
1163
|
+
return path.join(canonicalCacheDir(home, env, platform), "grok-files.json");
|
|
1164
|
+
}
|
|
1165
|
+
function resolveSessionsDir(opts) {
|
|
1166
|
+
const { home, env, platform } = resolveInjection(opts);
|
|
1167
|
+
const override = env.DELIBERATION_SESSIONS;
|
|
1168
|
+
if (typeof override === "string" && override.length > 0) {
|
|
1169
|
+
return override;
|
|
1170
|
+
}
|
|
1171
|
+
return path.join(canonicalCacheDir(home, env, platform), "sessions");
|
|
1172
|
+
}
|
|
1173
|
+
function resolveDebugLogPath(opts) {
|
|
1174
|
+
const { home, env, platform } = resolveInjection(opts);
|
|
1175
|
+
const override = env.DELIBERATION_DEBUG_LOG;
|
|
1176
|
+
if (typeof override === "string" && override.length > 0) {
|
|
1177
|
+
return override;
|
|
1178
|
+
}
|
|
1179
|
+
return path.join(canonicalCacheDir(home, env, platform), "debug.jsonl");
|
|
1180
|
+
}
|
|
1181
|
+
module2.exports = {
|
|
1182
|
+
resolveConfigPath,
|
|
1183
|
+
resolveGrokCachePath,
|
|
1184
|
+
resolveSessionsDir,
|
|
1185
|
+
resolveDebugLogPath
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
|
|
921
1190
|
// ../../core/providers/openai-compatible.js
|
|
922
1191
|
var require_openai_compatible = __commonJS({
|
|
923
1192
|
"../../core/providers/openai-compatible.js"(exports2, module2) {
|
|
@@ -960,13 +1229,14 @@ var require_openai_compatible = __commonJS({
|
|
|
960
1229
|
started,
|
|
961
1230
|
/** @type {any} */
|
|
962
1231
|
e,
|
|
963
|
-
() => ({ errorKind: "config", retryable: false })
|
|
1232
|
+
() => ({ errorKind: "config", retryable: false }),
|
|
1233
|
+
{ reasoningEffort: req.reasoningEffort ?? null }
|
|
964
1234
|
);
|
|
965
1235
|
}
|
|
966
1236
|
}
|
|
967
1237
|
const turns = prior ? [...prior, { role: "user", text: req.prompt, inlineBlocks: blocks }] : bridge.buildInitialTurns(req.developerInstructions, req.prompt, blocks);
|
|
968
1238
|
try {
|
|
969
|
-
const { text } = await bridge.callOpenRouter({
|
|
1239
|
+
const { text, usage } = await bridge.callOpenRouter({
|
|
970
1240
|
apiBase,
|
|
971
1241
|
apiKey: req && req.apiKey || process.env[apiKeyEnv],
|
|
972
1242
|
model,
|
|
@@ -981,7 +1251,7 @@ var require_openai_compatible = __commonJS({
|
|
|
981
1251
|
const outText = notes.length ? `${text}
|
|
982
1252
|
|
|
983
1253
|
[files] ${notes.join("; ")}` : text;
|
|
984
|
-
return { provider: name, model, text: outText, threadId, isError: false, ms: Date.now() - started };
|
|
1254
|
+
return { provider: name, model, text: outText, threadId, isError: false, ms: Date.now() - started, reasoningEffort: req.reasoningEffort ?? null, usage };
|
|
985
1255
|
} catch (e) {
|
|
986
1256
|
return toErrorResult(
|
|
987
1257
|
name,
|
|
@@ -989,7 +1259,8 @@ var require_openai_compatible = __commonJS({
|
|
|
989
1259
|
started,
|
|
990
1260
|
/** @type {any} */
|
|
991
1261
|
e,
|
|
992
|
-
bridge.classifyError
|
|
1262
|
+
bridge.classifyError,
|
|
1263
|
+
{ reasoningEffort: req.reasoningEffort ?? null }
|
|
993
1264
|
);
|
|
994
1265
|
}
|
|
995
1266
|
}
|
|
@@ -1044,7 +1315,7 @@ var require_grok = __commonJS({
|
|
|
1044
1315
|
reasoningEffort,
|
|
1045
1316
|
timeoutMs: req.timeoutMs
|
|
1046
1317
|
});
|
|
1047
|
-
return { provider: "grok", model, text: out.text || "", isError: false, ms: Date.now() - started };
|
|
1318
|
+
return { provider: "grok", model, text: out.text || "", isError: false, ms: Date.now() - started, reasoningEffort: reasoningEffort ?? null, usage: out.usage };
|
|
1048
1319
|
} catch (e) {
|
|
1049
1320
|
return toErrorResult(
|
|
1050
1321
|
"grok",
|
|
@@ -1052,7 +1323,8 @@ var require_grok = __commonJS({
|
|
|
1052
1323
|
started,
|
|
1053
1324
|
/** @type {any} */
|
|
1054
1325
|
e,
|
|
1055
|
-
bridge.classifyGrokError
|
|
1326
|
+
bridge.classifyGrokError,
|
|
1327
|
+
{ reasoningEffort: reasoningEffort ?? null }
|
|
1056
1328
|
);
|
|
1057
1329
|
}
|
|
1058
1330
|
}
|
|
@@ -1091,7 +1363,7 @@ var require_antigravity = __commonJS({
|
|
|
1091
1363
|
});
|
|
1092
1364
|
try {
|
|
1093
1365
|
const out = await bridge.runGemini(args, req.cwd, req.timeoutMs, void 0);
|
|
1094
|
-
return { provider: "gemini", model, text: out.response || "", threadId: out.threadId, isError: false, ms: Date.now() - started };
|
|
1366
|
+
return { provider: "gemini", model, text: out.response || "", threadId: out.threadId, isError: false, ms: Date.now() - started, reasoningEffort: null };
|
|
1095
1367
|
} catch (e) {
|
|
1096
1368
|
const err = (
|
|
1097
1369
|
/** @type {any} */
|
|
@@ -1102,7 +1374,8 @@ var require_antigravity = __commonJS({
|
|
|
1102
1374
|
model,
|
|
1103
1375
|
started,
|
|
1104
1376
|
err,
|
|
1105
|
-
(_status, code) => bridge.classifyGeminiError(err && err.message || "", code)
|
|
1377
|
+
(_status, code) => bridge.classifyGeminiError(err && err.message || "", code),
|
|
1378
|
+
{ reasoningEffort: null }
|
|
1106
1379
|
);
|
|
1107
1380
|
}
|
|
1108
1381
|
}
|
|
@@ -1166,7 +1439,7 @@ var require_codex = __commonJS({
|
|
|
1166
1439
|
${req.prompt}` : req.prompt;
|
|
1167
1440
|
const { code, stdout, stderr } = await run({ prompt: full, cwd: req.cwd, timeoutMs: req.timeoutMs });
|
|
1168
1441
|
if (code === 0) {
|
|
1169
|
-
return { provider: "codex", model, text: stdout.trim(), isError: false, ms: Date.now() - started };
|
|
1442
|
+
return { provider: "codex", model, text: stdout.trim(), isError: false, ms: Date.now() - started, reasoningEffort: null };
|
|
1170
1443
|
}
|
|
1171
1444
|
const { errorKind, retryable } = classifyCodex(stderr);
|
|
1172
1445
|
return {
|
|
@@ -1177,7 +1450,8 @@ ${req.prompt}` : req.prompt;
|
|
|
1177
1450
|
retryable,
|
|
1178
1451
|
// Error results carry no text; surface stdout/stderr diagnostics in message.
|
|
1179
1452
|
message: stdout && stdout.trim() || stderr || void 0,
|
|
1180
|
-
ms: Date.now() - started
|
|
1453
|
+
ms: Date.now() - started,
|
|
1454
|
+
reasoningEffort: null
|
|
1181
1455
|
};
|
|
1182
1456
|
}
|
|
1183
1457
|
};
|
|
@@ -1243,6 +1517,7 @@ var require_config = __commonJS({
|
|
|
1243
1517
|
const invalidModels = enabled ? parsed.invalidModels : [];
|
|
1244
1518
|
const { consensus: consensus2, warnings } = resolveConsensus(raw.consensus, models);
|
|
1245
1519
|
const { sessions, warnings: sessionsWarnings } = resolveSessions(raw.sessions);
|
|
1520
|
+
const { debug, warnings: debugWarnings } = resolveDebug(raw.debug);
|
|
1246
1521
|
return {
|
|
1247
1522
|
ok: true,
|
|
1248
1523
|
error: null,
|
|
@@ -1252,13 +1527,32 @@ var require_config = __commonJS({
|
|
|
1252
1527
|
openrouter: { enabled, apiKeyEnv, apiBase, allowRawModel, maxFanout, defaultModel, defaults, models, invalidModels },
|
|
1253
1528
|
consensus: consensus2,
|
|
1254
1529
|
sessions,
|
|
1255
|
-
|
|
1256
|
-
//
|
|
1257
|
-
//
|
|
1258
|
-
|
|
1530
|
+
debug,
|
|
1531
|
+
// Defaults-, sessions-, and debug-validation warnings ride the same
|
|
1532
|
+
// consensusWarnings channel the bridge already surfaces, so a dropped/degraded
|
|
1533
|
+
// value is visible, not silent.
|
|
1534
|
+
consensusWarnings: [...defaultsWarnings, ...warnings, ...sessionsWarnings, ...debugWarnings]
|
|
1259
1535
|
}
|
|
1260
1536
|
};
|
|
1261
1537
|
}
|
|
1538
|
+
function resolveDebug(raw) {
|
|
1539
|
+
const warnings = [];
|
|
1540
|
+
const out = { enabled: false, path: null };
|
|
1541
|
+
if (raw === void 0) return { debug: out, warnings };
|
|
1542
|
+
if (!isObject(raw)) {
|
|
1543
|
+
warnings.push(`debug must be an object (got ${JSON.stringify(raw)}); debug logging disabled`);
|
|
1544
|
+
return { debug: out, warnings };
|
|
1545
|
+
}
|
|
1546
|
+
if (raw.enabled !== void 0) {
|
|
1547
|
+
if (typeof raw.enabled === "boolean") out.enabled = raw.enabled;
|
|
1548
|
+
else warnings.push(`debug.enabled must be a boolean (got ${JSON.stringify(raw.enabled)}); using false`);
|
|
1549
|
+
}
|
|
1550
|
+
if (raw.path !== void 0) {
|
|
1551
|
+
if (typeof raw.path === "string" && raw.path.trim()) out.path = raw.path.trim();
|
|
1552
|
+
else warnings.push(`debug.path must be a non-empty string (got ${JSON.stringify(raw.path)}); using the default cache-dir path`);
|
|
1553
|
+
}
|
|
1554
|
+
return { debug: out, warnings };
|
|
1555
|
+
}
|
|
1262
1556
|
function resolveSessions(raw) {
|
|
1263
1557
|
const warnings = [];
|
|
1264
1558
|
const out = { persist: false, maxRecords: DEFAULT_SESSIONS_MAX_RECORDS, maxAgeDays: DEFAULT_SESSIONS_MAX_AGE_DAYS };
|
|
@@ -1537,76 +1831,6 @@ var require_config = __commonJS({
|
|
|
1537
1831
|
}
|
|
1538
1832
|
});
|
|
1539
1833
|
|
|
1540
|
-
// ../../core/paths.js
|
|
1541
|
-
var require_paths = __commonJS({
|
|
1542
|
-
"../../core/paths.js"(exports2, module2) {
|
|
1543
|
-
"use strict";
|
|
1544
|
-
var os = require("node:os");
|
|
1545
|
-
var path = require("node:path");
|
|
1546
|
-
function resolveInjection(opts) {
|
|
1547
|
-
return {
|
|
1548
|
-
home: opts && opts.home || os.homedir(),
|
|
1549
|
-
env: opts && opts.env || process.env,
|
|
1550
|
-
platform: opts && opts.platform || process.platform
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1553
|
-
function isUsableBase(value, platform) {
|
|
1554
|
-
if (typeof value !== "string" || value.length === 0) return false;
|
|
1555
|
-
const impl = platform === "win32" ? path.win32 : path.posix;
|
|
1556
|
-
return impl.isAbsolute(value);
|
|
1557
|
-
}
|
|
1558
|
-
function canonicalConfigDir(home, env, platform) {
|
|
1559
|
-
if (platform === "win32") {
|
|
1560
|
-
const appData = env.APPDATA;
|
|
1561
|
-
const base2 = isUsableBase(appData, platform) ? appData : path.join(home, "AppData", "Roaming");
|
|
1562
|
-
return path.join(base2, "deliberation");
|
|
1563
|
-
}
|
|
1564
|
-
const xdg = env.XDG_CONFIG_HOME;
|
|
1565
|
-
const base = isUsableBase(xdg, platform) ? xdg : path.join(home, ".config");
|
|
1566
|
-
return path.join(base, "deliberation");
|
|
1567
|
-
}
|
|
1568
|
-
function resolveConfigPath(opts) {
|
|
1569
|
-
const { home, env, platform } = resolveInjection(opts);
|
|
1570
|
-
const override = env.DELIBERATION_CONFIG;
|
|
1571
|
-
if (typeof override === "string" && override.length > 0) {
|
|
1572
|
-
return override;
|
|
1573
|
-
}
|
|
1574
|
-
return path.join(canonicalConfigDir(home, env, platform), "config.json");
|
|
1575
|
-
}
|
|
1576
|
-
function canonicalCacheDir(home, env, platform) {
|
|
1577
|
-
if (platform === "win32") {
|
|
1578
|
-
const localAppData = env.LOCALAPPDATA;
|
|
1579
|
-
const base2 = isUsableBase(localAppData, platform) ? localAppData : path.join(home, "AppData", "Local");
|
|
1580
|
-
return path.join(base2, "deliberation");
|
|
1581
|
-
}
|
|
1582
|
-
const xdg = env.XDG_CACHE_HOME;
|
|
1583
|
-
const base = isUsableBase(xdg, platform) ? xdg : path.join(home, ".cache");
|
|
1584
|
-
return path.join(base, "deliberation");
|
|
1585
|
-
}
|
|
1586
|
-
function resolveGrokCachePath(opts) {
|
|
1587
|
-
const { home, env, platform } = resolveInjection(opts);
|
|
1588
|
-
const override = env.DELIBERATION_CACHE;
|
|
1589
|
-
if (typeof override === "string" && override.length > 0) {
|
|
1590
|
-
return override;
|
|
1591
|
-
}
|
|
1592
|
-
return path.join(canonicalCacheDir(home, env, platform), "grok-files.json");
|
|
1593
|
-
}
|
|
1594
|
-
function resolveSessionsDir(opts) {
|
|
1595
|
-
const { home, env, platform } = resolveInjection(opts);
|
|
1596
|
-
const override = env.DELIBERATION_SESSIONS;
|
|
1597
|
-
if (typeof override === "string" && override.length > 0) {
|
|
1598
|
-
return override;
|
|
1599
|
-
}
|
|
1600
|
-
return path.join(canonicalCacheDir(home, env, platform), "sessions");
|
|
1601
|
-
}
|
|
1602
|
-
module2.exports = {
|
|
1603
|
-
resolveConfigPath,
|
|
1604
|
-
resolveGrokCachePath,
|
|
1605
|
-
resolveSessionsDir
|
|
1606
|
-
};
|
|
1607
|
-
}
|
|
1608
|
-
});
|
|
1609
|
-
|
|
1610
1834
|
// ../gemini/index.js
|
|
1611
1835
|
var require_gemini = __commonJS({
|
|
1612
1836
|
"../gemini/index.js"(exports2, module2) {
|
|
@@ -2931,7 +3155,19 @@ ${ref.inline_text}` });
|
|
|
2931
3155
|
throw e;
|
|
2932
3156
|
}
|
|
2933
3157
|
const text = parseResponsesOutput(data);
|
|
2934
|
-
return { text, output: Array.isArray(data.output) ? data.output : null };
|
|
3158
|
+
return { text, output: Array.isArray(data.output) ? data.output : null, usage: normalizeUsage(data.usage) };
|
|
3159
|
+
}
|
|
3160
|
+
function normalizeUsage(u) {
|
|
3161
|
+
if (!u || typeof u !== "object") return void 0;
|
|
3162
|
+
const prompt = typeof u.prompt_tokens === "number" ? u.prompt_tokens : typeof u.input_tokens === "number" ? u.input_tokens : void 0;
|
|
3163
|
+
const completion = typeof u.completion_tokens === "number" ? u.completion_tokens : typeof u.output_tokens === "number" ? u.output_tokens : void 0;
|
|
3164
|
+
const total = typeof u.total_tokens === "number" ? u.total_tokens : void 0;
|
|
3165
|
+
if (prompt === void 0 && completion === void 0 && total === void 0) return void 0;
|
|
3166
|
+
const out = {};
|
|
3167
|
+
if (prompt !== void 0) out.promptTokens = prompt;
|
|
3168
|
+
if (completion !== void 0) out.completionTokens = completion;
|
|
3169
|
+
if (total !== void 0) out.totalTokens = total;
|
|
3170
|
+
return out;
|
|
2935
3171
|
}
|
|
2936
3172
|
var STALE_FILE_ID_TEST = /file[-_][A-Za-z0-9_-]+/;
|
|
2937
3173
|
var STALE_FILE_ID_EXTRACT = /file[-_][A-Za-z0-9_-]+/g;
|
|
@@ -2966,7 +3202,7 @@ ${ref.inline_text}` });
|
|
|
2966
3202
|
}
|
|
2967
3203
|
try {
|
|
2968
3204
|
const out = await attempt(buildTurns(refs));
|
|
2969
|
-
return { text: out.text, output: out.output, refs, ownedIds };
|
|
3205
|
+
return { text: out.text, output: out.output, refs, ownedIds, usage: out.usage };
|
|
2970
3206
|
} catch (e) {
|
|
2971
3207
|
if (!isStaleFileError(e)) throw e;
|
|
2972
3208
|
const matches = (e.message || "").match(STALE_FILE_ID_EXTRACT) || [];
|
|
@@ -2998,7 +3234,7 @@ ${ref.inline_text}` });
|
|
|
2998
3234
|
if (!reuploaded._fromCache) ownedIds.push(reuploaded.id);
|
|
2999
3235
|
}
|
|
3000
3236
|
const out = await attempt(buildTurns(refs));
|
|
3001
|
-
return { text: out.text, output: out.output, refs, ownedIds };
|
|
3237
|
+
return { text: out.text, output: out.output, refs, ownedIds, usage: out.usage };
|
|
3002
3238
|
}
|
|
3003
3239
|
}
|
|
3004
3240
|
function validateRoots(roots) {
|
|
@@ -3643,7 +3879,15 @@ var require_openrouter = __commonJS({
|
|
|
3643
3879
|
e.code = "parse";
|
|
3644
3880
|
throw e;
|
|
3645
3881
|
}
|
|
3646
|
-
return { text: parseCompletion(data) };
|
|
3882
|
+
return { text: parseCompletion(data), usage: normalizeUsage(data.usage) };
|
|
3883
|
+
}
|
|
3884
|
+
function normalizeUsage(u) {
|
|
3885
|
+
if (!u || typeof u !== "object") return void 0;
|
|
3886
|
+
const out = {};
|
|
3887
|
+
if (typeof u.prompt_tokens === "number") out.promptTokens = u.prompt_tokens;
|
|
3888
|
+
if (typeof u.completion_tokens === "number") out.completionTokens = u.completion_tokens;
|
|
3889
|
+
if (typeof u.total_tokens === "number") out.totalTokens = u.total_tokens;
|
|
3890
|
+
return Object.keys(out).length ? out : void 0;
|
|
3647
3891
|
}
|
|
3648
3892
|
var crypto = require("node:crypto");
|
|
3649
3893
|
var { makeConfigReader } = require_config();
|
|
@@ -3930,6 +4174,42 @@ var EXPERT_DESCRIPTIONS = {
|
|
|
3930
4174
|
"researcher": "Research specialist for external libraries, frameworks, APIs, and open-source code. Use for 'how do I use X', best-practice, or 'why does this dependency behave this way' questions, with evidence and honest unverified flags.",
|
|
3931
4175
|
"debugger": "Debugging specialist that produces ranked root-cause hypotheses and the smallest safe fix from a bug report, logs, and code - or says honestly that the evidence shows no bug. Use for crashes, failing tests, or wrong output."
|
|
3932
4176
|
};
|
|
4177
|
+
function panelInputSchema() {
|
|
4178
|
+
return {
|
|
4179
|
+
type: "object",
|
|
4180
|
+
properties: {
|
|
4181
|
+
expert: { type: "string" },
|
|
4182
|
+
cwd: { type: "string" }
|
|
4183
|
+
}
|
|
4184
|
+
};
|
|
4185
|
+
}
|
|
4186
|
+
function askOneInputSchema() {
|
|
4187
|
+
return {
|
|
4188
|
+
type: "object",
|
|
4189
|
+
required: ["provider", "prompt"],
|
|
4190
|
+
properties: {
|
|
4191
|
+
provider: { type: "string", description: 'A name from `panel` (e.g. "codex", "openrouter:<alias>")' },
|
|
4192
|
+
prompt: { type: "string" },
|
|
4193
|
+
expert: { type: "string" },
|
|
4194
|
+
developerInstructions: { type: "string" },
|
|
4195
|
+
cwd: { type: "string" },
|
|
4196
|
+
reasoningEffort: { type: "string", enum: ["low", "medium", "high", "none"] },
|
|
4197
|
+
files: {
|
|
4198
|
+
type: "array",
|
|
4199
|
+
items: {
|
|
4200
|
+
type: "object",
|
|
4201
|
+
properties: {
|
|
4202
|
+
path: { type: "string" },
|
|
4203
|
+
dir: { type: "string" },
|
|
4204
|
+
file_id: { type: "string" },
|
|
4205
|
+
file_url: { type: "string" },
|
|
4206
|
+
mode: { type: "string", enum: ["auto", "inline", "upload"] }
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
};
|
|
4212
|
+
}
|
|
3933
4213
|
function inputSchema() {
|
|
3934
4214
|
return {
|
|
3935
4215
|
type: "object",
|
|
@@ -4012,7 +4292,9 @@ function toolList() {
|
|
|
4012
4292
|
const tools = [
|
|
4013
4293
|
{ name: "ask-all", description: "Fan out one question to GPT, Gemini, Grok, and any configured OpenRouter models in parallel for independent second opinions, then return all results (advisory, no cross-contamination). Pass `expert` to apply a persona to every delegate.", inputSchema: inputSchema(), annotations: ADVISORY },
|
|
4014
4294
|
{ name: "consensus", description: "Run the FULL multi-round consensus convergence loop server-side with a provider arbiter (blind pass + peer fan-out -> adjudicate -> revise) and return the converged verdict. Default depth is `consensus.maxRounds` (config, default 5); pass `maxRounds` to override. Pass `synthesizeAlways:true` for a SINGLE arbiter synthesis pass instead of the loop (best for open questions, not plan convergence): it returns a free-text `synthesis` and `maxRounds` is ignored. Configure the arbiter via `consensus.arbiter` - a concrete provider/openrouter alias runs server-side; `host` mode returns the opinions for YOU to synthesize. Advisory; pass `expert` to apply a persona. NOTE (Claude Code): use the `/consensus` slash command for the transcript-visible host-arbiter loop (it drives `consensus-step`); this tool is the provider-arbiter path for any host.", inputSchema: consensusInputSchema(), annotations: ADVISORY },
|
|
4015
|
-
{ name: "consensus-step", description: "Client-driven consensus loop where YOU (the host model) are the arbiter, one step per call: action=init (start, returns sessionId + blind prompt) -> record_blind (your pre-commit verdict) -> dispatch_peers (server fans out to the providers) -> submit_adjudication (your verdict + per-issue accept/dismiss/defer) -> submit_revision (your revised plan), looping until converged or consensus.maxRounds rounds (default 5). State is held server-side by sessionId. Advisory.", inputSchema: consensusStepInputSchema(), annotations: ADVISORY }
|
|
4295
|
+
{ name: "consensus-step", description: "Client-driven consensus loop where YOU (the host model) are the arbiter, one step per call: action=init (start, returns sessionId + blind prompt) -> record_blind (your pre-commit verdict) -> dispatch_peers (server fans out to the providers) -> submit_adjudication (your verdict + per-issue accept/dismiss/defer) -> submit_revision (your revised plan), looping until converged or consensus.maxRounds rounds (default 5). State is held server-side by sessionId. Advisory.", inputSchema: consensusStepInputSchema(), annotations: ADVISORY },
|
|
4296
|
+
{ name: "panel", description: "Return the names of the providers `ask-all` WOULD dispatch for the current config + expert (enabled built-ins + eligible OpenRouter aliases, fanout cap applied), WITHOUT calling them. Use this to discover the panel, then issue one `ask-one` call per provider in parallel for visible per-provider progress. Advisory, read-only.", inputSchema: panelInputSchema(), annotations: ADVISORY },
|
|
4297
|
+
{ name: "ask-one", description: "Second opinion from ONE named provider in the active panel (e.g. `codex`, `gemini`, `grok`, `openrouter:<alias>` - get the names from `panel`). Returns the standard result envelope. Issue N of these in parallel (one per `panel` name) so each renders independently as it lands. Advisory, single-shot.", inputSchema: askOneInputSchema(), annotations: ADVISORY }
|
|
4016
4298
|
];
|
|
4017
4299
|
for (const t of Object.keys(ASK_PROVIDER)) {
|
|
4018
4300
|
tools.push({ name: t, description: `Single-provider second opinion via ${ASK_PROVIDER[t]} (advisory, single-shot). Pass \`expert\` to apply one of the expert personas.`, inputSchema: inputSchema(), annotations: ADVISORY });
|
|
@@ -4068,8 +4350,10 @@ async function isHealthy(p) {
|
|
|
4068
4350
|
return false;
|
|
4069
4351
|
}
|
|
4070
4352
|
}
|
|
4071
|
-
function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
4353
|
+
function buildServer({ providers, getConfig, getConfigError, sessionsDir, notify }) {
|
|
4072
4354
|
const registry = makeRegistry(providers);
|
|
4355
|
+
const sendNotify = typeof notify === "function" ? notify : (_m, _p) => {
|
|
4356
|
+
};
|
|
4073
4357
|
const sessions = (
|
|
4074
4358
|
/** @type {any} */
|
|
4075
4359
|
require_sessions()
|
|
@@ -4081,6 +4365,52 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4081
4365
|
const { parseReview } = require_provider();
|
|
4082
4366
|
const { makeLoopStore } = require_loop_store();
|
|
4083
4367
|
const loopStore = makeLoopStore();
|
|
4368
|
+
const { makeResultCache } = require_result_cache();
|
|
4369
|
+
const resultCache = makeResultCache();
|
|
4370
|
+
const debugLog = require_debug_log();
|
|
4371
|
+
const { resolveDebugLogPath } = require_paths();
|
|
4372
|
+
let _fileCache = { key: "", logger: debugLog.NULL_LOGGER };
|
|
4373
|
+
function fileSink() {
|
|
4374
|
+
const dbg = (getConfig() || {}).debug || { enabled: false, path: null };
|
|
4375
|
+
if (!dbg.enabled) {
|
|
4376
|
+
_fileCache = { key: "", logger: debugLog.NULL_LOGGER };
|
|
4377
|
+
return debugLog.NULL_LOGGER;
|
|
4378
|
+
}
|
|
4379
|
+
const path = typeof dbg.path === "string" && dbg.path || resolveDebugLogPath();
|
|
4380
|
+
const key = `file:${path}`;
|
|
4381
|
+
if (_fileCache.key !== key) _fileCache = { key, logger: debugLog.createFileLogger(path) };
|
|
4382
|
+
return _fileCache.logger;
|
|
4383
|
+
}
|
|
4384
|
+
const LEVEL_RANK = Object.freeze({ debug: 0, info: 1, notice: 2, warning: 3, error: 4, critical: 5, alert: 6, emergency: 7 });
|
|
4385
|
+
let notifyMinLevel = (
|
|
4386
|
+
/** @type {keyof typeof LEVEL_RANK} */
|
|
4387
|
+
"info"
|
|
4388
|
+
);
|
|
4389
|
+
const notifySink = {
|
|
4390
|
+
/** @param {import("../../core/debug-log.js").DebugEvent} e */
|
|
4391
|
+
logEvent(e) {
|
|
4392
|
+
if (LEVEL_RANK.info < LEVEL_RANK[notifyMinLevel]) return;
|
|
4393
|
+
try {
|
|
4394
|
+
sendNotify("notifications/message", {
|
|
4395
|
+
level: "info",
|
|
4396
|
+
logger: "deliberation",
|
|
4397
|
+
data: { event: e.event, tool: e.tool, provider: e.provider, ms: e.ms, round: e.round, verdict: e.verdict, isError: e.isError, errorKind: e.errorKind }
|
|
4398
|
+
});
|
|
4399
|
+
} catch {
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4402
|
+
};
|
|
4403
|
+
function setLogLevel(level) {
|
|
4404
|
+
if (typeof level === "string" && Object.prototype.hasOwnProperty.call(LEVEL_RANK, level)) {
|
|
4405
|
+
notifyMinLevel = /** @type {keyof typeof LEVEL_RANK} */
|
|
4406
|
+
level;
|
|
4407
|
+
return true;
|
|
4408
|
+
}
|
|
4409
|
+
return false;
|
|
4410
|
+
}
|
|
4411
|
+
function currentLogger() {
|
|
4412
|
+
return debugLog.composeLoggers([fileSink(), notifySink]);
|
|
4413
|
+
}
|
|
4084
4414
|
let clientName = (
|
|
4085
4415
|
/** @type {string|null} */
|
|
4086
4416
|
null
|
|
@@ -4183,9 +4513,17 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4183
4513
|
return null;
|
|
4184
4514
|
}
|
|
4185
4515
|
}
|
|
4186
|
-
async function runAskAll(req, expert
|
|
4516
|
+
async function runAskAll(req, expert, opts = (
|
|
4517
|
+
/** @type {{noCache?:boolean}} */
|
|
4518
|
+
{}
|
|
4519
|
+
)) {
|
|
4187
4520
|
const { providers: selected, omitted } = registry.selectForAskAll({ config: getConfig(), expert: expert || "" });
|
|
4188
|
-
const
|
|
4521
|
+
const lg = currentLogger();
|
|
4522
|
+
try {
|
|
4523
|
+
lg.logEvent({ event: "dispatch_start", at: Date.now(), tool: "ask-all", voices: selected.length });
|
|
4524
|
+
} catch {
|
|
4525
|
+
}
|
|
4526
|
+
const results = await askAll(selected, withPersona(req, expert), { logger: lg, tool: "ask-all", cache: opts.noCache ? void 0 : resultCache });
|
|
4189
4527
|
return {
|
|
4190
4528
|
payload: { results, omitted },
|
|
4191
4529
|
parts: { opinions: results, blindVerdict: null, verdict: null, arbiter: null, warnings: [] }
|
|
@@ -4203,13 +4541,13 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4203
4541
|
const resolved = await resolveArbiter(arbiterSpec, selected, registry, getConfig);
|
|
4204
4542
|
if (resolved.warning) warnings.push(resolved.warning);
|
|
4205
4543
|
if (resolved.mode === "host") {
|
|
4206
|
-
const opinions = await askAll(selected, withPersona(req, expert));
|
|
4544
|
+
const opinions = await askAll(selected, withPersona(req, expert), { logger: currentLogger(), tool: "consensus" });
|
|
4207
4545
|
const arbiter2 = { mode: "host" };
|
|
4208
4546
|
const body = { opinions, blindVerdict: null, verdict: null, arbiter: arbiter2, warnings };
|
|
4209
4547
|
return { payload: body, parts: body };
|
|
4210
4548
|
}
|
|
4211
4549
|
if (!resolved.provider) {
|
|
4212
|
-
const out2 = await consensus(selected, withPersona(req, expert), { arbiterInstructions: PROMPTS.arbiter });
|
|
4550
|
+
const out2 = await consensus(selected, withPersona(req, expert), { arbiterInstructions: PROMPTS.arbiter, logger: currentLogger() });
|
|
4213
4551
|
const arbiter2 = { mode: "server", provider: null };
|
|
4214
4552
|
return {
|
|
4215
4553
|
payload: { opinions: out2.opinions, blindVerdict: out2.blindVerdict, verdict: out2.verdict, error: out2.error, arbiter: arbiter2, warnings },
|
|
@@ -4222,7 +4560,7 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4222
4560
|
peers = selected;
|
|
4223
4561
|
warnings.push(`panel too small to exclude arbiter '${arbiterP.name}'; kept it in the peer panel (floor of 2)`);
|
|
4224
4562
|
}
|
|
4225
|
-
const out = await consensus(peers, withPersona(req, expert), { arbiter: arbiterP, arbiterInstructions: PROMPTS.arbiter, blindVote });
|
|
4563
|
+
const out = await consensus(peers, withPersona(req, expert), { arbiter: arbiterP, arbiterInstructions: PROMPTS.arbiter, blindVote, logger: currentLogger() });
|
|
4226
4564
|
const arbiter = { mode: "server", provider: arbiterP.name };
|
|
4227
4565
|
return {
|
|
4228
4566
|
payload: { opinions: out.opinions, blindVerdict: out.blindVerdict, verdict: out.verdict, error: out.error, arbiter, warnings },
|
|
@@ -4252,7 +4590,7 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4252
4590
|
}
|
|
4253
4591
|
const maxRounds = Number.isInteger(maxRoundsOverride) && /** @type {number} */
|
|
4254
4592
|
maxRoundsOverride > 0 ? maxRoundsOverride : Number.isInteger(cc.maxRounds) && cc.maxRounds > 0 ? cc.maxRounds : void 0;
|
|
4255
|
-
const out = await runToConvergence(peers, withPersona(req, expert), { arbiter: arbiterP, maxRounds });
|
|
4593
|
+
const out = await runToConvergence(peers, withPersona(req, expert), { arbiter: arbiterP, maxRounds, logger: currentLogger() });
|
|
4256
4594
|
const allWarnings = out.error ? warnings.concat([`loop: ${out.error}`]) : warnings;
|
|
4257
4595
|
const rounds = Array.isArray(out.rounds) ? out.rounds.length : 0;
|
|
4258
4596
|
const arbiter = { mode: "server", provider: arbiterP.name };
|
|
@@ -4388,9 +4726,14 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4388
4726
|
const ex = cur.expert || expert || void 0;
|
|
4389
4727
|
const { providers: selected } = registry.selectForConsensus({ config: getConfig() || {}, expert: ex || "" });
|
|
4390
4728
|
const peerReq = { prompt: peerPrompt, expert: ex, cwd: typeof args.cwd === "string" ? args.cwd : void 0 };
|
|
4391
|
-
const
|
|
4729
|
+
const lg = currentLogger();
|
|
4730
|
+
try {
|
|
4731
|
+
lg.logEvent({ event: "dispatch_start", at: Date.now(), tool: "consensus", round: cur.round, voices: selected.length });
|
|
4732
|
+
} catch {
|
|
4733
|
+
}
|
|
4734
|
+
const peerResults = await askAll(selected, withPersona(peerReq, ex), { logger: lg, tool: "consensus" });
|
|
4392
4735
|
const results = peerResults.map(
|
|
4393
|
-
(r) => r.isError ? { source: r.provider, isError: true, errorKind: r.errorKind, verdict: null, criticalIssues: [] } : { ...parseReview(typeof r.text === "string" ? r.text : ""), source: r.provider, isError: false }
|
|
4736
|
+
(r) => r.isError ? { source: r.provider, isError: true, errorKind: r.errorKind, verdict: null, criticalIssues: [], model: r.model, reasoningEffort: r.reasoningEffort ?? null, ms: r.ms } : { ...parseReview(typeof r.text === "string" ? r.text : ""), source: r.provider, isError: false, model: r.model, reasoningEffort: r.reasoningEffort ?? null, ms: r.ms }
|
|
4394
4737
|
);
|
|
4395
4738
|
const next = loop.addOpinions(cur, results);
|
|
4396
4739
|
loopStore.put(sid, next);
|
|
@@ -4398,13 +4741,28 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4398
4741
|
sessionId: sid,
|
|
4399
4742
|
status: next.status,
|
|
4400
4743
|
round: next.round,
|
|
4401
|
-
|
|
4744
|
+
// model + reasoningEffort + ms ride along so the command can show real
|
|
4745
|
+
// reasoning effort per voice (no more hardcoded "n/a") and a time footer.
|
|
4746
|
+
opinions: results.map((r) => ({ source: r.source, isError: r.isError, errorKind: r.errorKind, verdict: r.verdict, criticalIssues: r.criticalIssues, model: r.model, reasoningEffort: r.reasoningEffort, ms: r.ms })),
|
|
4402
4747
|
note: "adjudicate the opinions, then call submit_adjudication with your verdict + per-issue decisions"
|
|
4403
4748
|
};
|
|
4404
4749
|
}
|
|
4405
4750
|
if (action === "submit_adjudication") {
|
|
4406
4751
|
const decisions = Array.isArray(args.decisions) ? args.decisions : [];
|
|
4407
4752
|
const next = loop.submitAdjudication(cur, { verdict: args.verdict, decisions });
|
|
4753
|
+
try {
|
|
4754
|
+
currentLogger().logEvent({
|
|
4755
|
+
event: "round",
|
|
4756
|
+
at: Date.now(),
|
|
4757
|
+
tool: "consensus",
|
|
4758
|
+
round: cur.round,
|
|
4759
|
+
verdict: typeof args.verdict === "string" ? args.verdict : null,
|
|
4760
|
+
converged: next.status === "converged",
|
|
4761
|
+
acceptedCritical: decisions.filter((d) => d && d.action === "accept").length,
|
|
4762
|
+
voices: Array.isArray(cur.results) ? cur.results.length : void 0
|
|
4763
|
+
});
|
|
4764
|
+
} catch {
|
|
4765
|
+
}
|
|
4408
4766
|
if (next.status === "converged") {
|
|
4409
4767
|
const { finalReport, confidence } = loop.finalize(next);
|
|
4410
4768
|
loopStore.delete(sid);
|
|
@@ -4443,6 +4801,23 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4443
4801
|
reasoningEffort: args.reasoningEffort,
|
|
4444
4802
|
files: args.files
|
|
4445
4803
|
};
|
|
4804
|
+
if (name === "panel") {
|
|
4805
|
+
const { providers: selected, omitted } = registry.selectForAskAll({ config: getConfig(), expert: expert || "" });
|
|
4806
|
+
return jsonResult({
|
|
4807
|
+
providers: selected.map((p) => p.name),
|
|
4808
|
+
omitted: (Array.isArray(omitted) ? omitted : []).map((o) => o && o.alias || String(o))
|
|
4809
|
+
});
|
|
4810
|
+
}
|
|
4811
|
+
if (name === "ask-one") {
|
|
4812
|
+
const want = typeof args.provider === "string" ? args.provider : "";
|
|
4813
|
+
const { providers: selected } = registry.selectForAskAll({ config: getConfig(), expert: expert || "" });
|
|
4814
|
+
const p = selected.find((x) => x.name === want);
|
|
4815
|
+
if (!p) {
|
|
4816
|
+
return jsonResult({ error: `provider "${want}" is not in the active panel`, panel: selected.map((x) => x.name) });
|
|
4817
|
+
}
|
|
4818
|
+
const result = await askOne(p, withPersona(req, expert), { logger: currentLogger(), tool: "ask-one", cache: resultCache });
|
|
4819
|
+
return jsonResult({ result });
|
|
4820
|
+
}
|
|
4446
4821
|
if (name === "ask-all") {
|
|
4447
4822
|
const { payload, parts } = await runAskAll(req, expert);
|
|
4448
4823
|
const sid = persistRun("ask-all", req, expert, parts);
|
|
@@ -4495,7 +4870,7 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4495
4870
|
cwd: typeof args.cwd === "string" ? args.cwd : void 0
|
|
4496
4871
|
};
|
|
4497
4872
|
const tool = rec.tool === "ask-all" ? "ask-all" : "consensus";
|
|
4498
|
-
const { payload, parts } = tool === "ask-all" ? await runAskAll(childReq, childExpert) : await runConsensusTool(childReq, childExpert, { synthesizeAlways: rec.synthesizeAlways === true });
|
|
4873
|
+
const { payload, parts } = tool === "ask-all" ? await runAskAll(childReq, childExpert, { noCache: true }) : await runConsensusTool(childReq, childExpert, { synthesizeAlways: rec.synthesizeAlways === true });
|
|
4499
4874
|
if (parts) {
|
|
4500
4875
|
const sid = persistRun(tool, childReq, childExpert, { ...parts, parentId: rec.id });
|
|
4501
4876
|
if (sid) payload.sessionId = sid;
|
|
@@ -4506,12 +4881,12 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4506
4881
|
if (Object.prototype.hasOwnProperty.call(ASK_PROVIDER, name)) {
|
|
4507
4882
|
const p = registry.get(ASK_PROVIDER[name]);
|
|
4508
4883
|
if (!p) return { content: [{ type: "text", text: JSON.stringify({ error: `provider ${ASK_PROVIDER[name]} not registered` }) }] };
|
|
4509
|
-
const result = await askOne(p, withPersona(req, expert));
|
|
4884
|
+
const result = await askOne(p, withPersona(req, expert), { logger: currentLogger(), tool: "ask-one", cache: resultCache });
|
|
4510
4885
|
return { content: [{ type: "text", text: JSON.stringify({ result }) }] };
|
|
4511
4886
|
}
|
|
4512
4887
|
if (EXPERTS.includes(name)) {
|
|
4513
4888
|
const { providers: selected } = registry.selectForAskAll({ config: getConfig(), expert: name });
|
|
4514
|
-
const results = await askAll(selected, withPersona({ ...req, expert: name }, expert));
|
|
4889
|
+
const results = await askAll(selected, withPersona({ ...req, expert: name }, expert), { logger: currentLogger(), tool: name, cache: resultCache });
|
|
4515
4890
|
return { content: [{ type: "text", text: JSON.stringify({ results }) }] };
|
|
4516
4891
|
}
|
|
4517
4892
|
throw new Error(`unknown tool: ${name}`);
|
|
@@ -4521,7 +4896,12 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir }) {
|
|
|
4521
4896
|
if (msg.method === "initialize") {
|
|
4522
4897
|
const ci = msg.params && msg.params.clientInfo;
|
|
4523
4898
|
if (ci && typeof ci.name === "string") clientName = ci.name;
|
|
4524
|
-
return { jsonrpc: "2.0", id: msg.id, result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "deliberation-mcp", version: "0.1.0" } } };
|
|
4899
|
+
return { jsonrpc: "2.0", id: msg.id, result: { protocolVersion: "2024-11-05", capabilities: { tools: {}, logging: {} }, serverInfo: { name: "deliberation-mcp", version: "0.1.0" } } };
|
|
4900
|
+
}
|
|
4901
|
+
if (msg.method === "logging/setLevel") {
|
|
4902
|
+
const level = msg.params && msg.params.level;
|
|
4903
|
+
if (!setLogLevel(level)) return { jsonrpc: "2.0", id: msg.id, error: { code: -32602, message: `invalid log level: ${String(level)}` } };
|
|
4904
|
+
return { jsonrpc: "2.0", id: msg.id, result: {} };
|
|
4525
4905
|
}
|
|
4526
4906
|
if (msg.method === "tools/list") return { jsonrpc: "2.0", id: msg.id, result: { tools: toolList() } };
|
|
4527
4907
|
if (msg.method === "tools/call") {
|
|
@@ -4569,7 +4949,8 @@ function startStdio() {
|
|
|
4569
4949
|
})
|
|
4570
4950
|
];
|
|
4571
4951
|
const sessionsDir = require_paths().resolveSessionsDir();
|
|
4572
|
-
const
|
|
4952
|
+
const notify = (method, params) => process.stdout.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n");
|
|
4953
|
+
const srv = buildServer({ providers, getConfig, getConfigError, sessionsDir, notify });
|
|
4573
4954
|
if (typeof globalThis.fetch !== "function") {
|
|
4574
4955
|
console.error("deliberation-mcp requires Node 18+ (global fetch unavailable).");
|
|
4575
4956
|
process.exit(1);
|
package/dist/setup.js
CHANGED
|
@@ -67,10 +67,19 @@ var require_paths = __commonJS({
|
|
|
67
67
|
}
|
|
68
68
|
return path2.join(canonicalCacheDir(home, env, platform), "sessions");
|
|
69
69
|
}
|
|
70
|
+
function resolveDebugLogPath(opts) {
|
|
71
|
+
const { home, env, platform } = resolveInjection(opts);
|
|
72
|
+
const override = env.DELIBERATION_DEBUG_LOG;
|
|
73
|
+
if (typeof override === "string" && override.length > 0) {
|
|
74
|
+
return override;
|
|
75
|
+
}
|
|
76
|
+
return path2.join(canonicalCacheDir(home, env, platform), "debug.jsonl");
|
|
77
|
+
}
|
|
70
78
|
module2.exports = {
|
|
71
79
|
resolveConfigPath: resolveConfigPath2,
|
|
72
80
|
resolveGrokCachePath,
|
|
73
|
-
resolveSessionsDir
|
|
81
|
+
resolveSessionsDir,
|
|
82
|
+
resolveDebugLogPath
|
|
74
83
|
};
|
|
75
84
|
}
|
|
76
85
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antonbabenko/deliberation-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.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" },
|