@antonbabenko/deliberation-mcp 3.3.0 → 3.5.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 +292 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -750,6 +750,243 @@ var require_prompts = __commonJS({
|
|
|
750
750
|
}
|
|
751
751
|
});
|
|
752
752
|
|
|
753
|
+
// ../../core/analyze.js
|
|
754
|
+
var require_analyze = __commonJS({
|
|
755
|
+
"../../core/analyze.js"(exports2, module2) {
|
|
756
|
+
"use strict";
|
|
757
|
+
var SLOW_FACTOR = 2;
|
|
758
|
+
var MIN_BASELINE_MS = 200;
|
|
759
|
+
var MIN_CALLS = 2;
|
|
760
|
+
var ABS_SLOW_MS = 12e4;
|
|
761
|
+
var HIGH_ERROR_RATE = 0.5;
|
|
762
|
+
var HIGH_AGREEMENT = 0.9;
|
|
763
|
+
var MIN_VOTES = 3;
|
|
764
|
+
var OR_PREFIX = "openrouter:";
|
|
765
|
+
function parseDebugLog(text) {
|
|
766
|
+
if (typeof text !== "string" || text.length === 0) return [];
|
|
767
|
+
const out = [];
|
|
768
|
+
for (const line of text.split("\n")) {
|
|
769
|
+
const trimmed = line.trim();
|
|
770
|
+
if (!trimmed) continue;
|
|
771
|
+
let obj;
|
|
772
|
+
try {
|
|
773
|
+
obj = JSON.parse(trimmed);
|
|
774
|
+
} catch {
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (obj && typeof obj === "object" && typeof obj.event === "string") {
|
|
778
|
+
out.push(
|
|
779
|
+
/** @type {DebugEvent} */
|
|
780
|
+
obj
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return out;
|
|
785
|
+
}
|
|
786
|
+
function percentile(sorted, p) {
|
|
787
|
+
if (!sorted.length) return 0;
|
|
788
|
+
if (sorted.length === 1) return sorted[0];
|
|
789
|
+
const idx = p / 100 * (sorted.length - 1);
|
|
790
|
+
const lo = Math.floor(idx);
|
|
791
|
+
const hi = Math.ceil(idx);
|
|
792
|
+
if (lo === hi) return sorted[lo];
|
|
793
|
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);
|
|
794
|
+
}
|
|
795
|
+
function aggregateByModel(events) {
|
|
796
|
+
const groups = /* @__PURE__ */ new Map();
|
|
797
|
+
for (const e of Array.isArray(events) ? events : []) {
|
|
798
|
+
if (!e || e.event !== "provider_result" || typeof e.provider !== "string") continue;
|
|
799
|
+
const provider = e.provider;
|
|
800
|
+
const model = typeof e.model === "string" ? e.model : "";
|
|
801
|
+
const key = `${provider}|${model}`;
|
|
802
|
+
let g = groups.get(key);
|
|
803
|
+
if (!g) {
|
|
804
|
+
g = { provider, model, ms: [], errors: 0, calls: 0, tokens: [], efforts: /* @__PURE__ */ new Set(), tools: /* @__PURE__ */ new Set() };
|
|
805
|
+
groups.set(key, g);
|
|
806
|
+
}
|
|
807
|
+
g.calls += 1;
|
|
808
|
+
if (e.isError) g.errors += 1;
|
|
809
|
+
if (typeof e.ms === "number" && Number.isFinite(e.ms)) g.ms.push(e.ms);
|
|
810
|
+
const tot = e.usage && typeof e.usage.totalTokens === "number" ? e.usage.totalTokens : void 0;
|
|
811
|
+
if (typeof tot === "number" && Number.isFinite(tot)) g.tokens.push(tot);
|
|
812
|
+
g.efforts.add(e.reasoningEffort == null ? "n/a" : String(e.reasoningEffort));
|
|
813
|
+
if (typeof e.tool === "string") g.tools.add(e.tool);
|
|
814
|
+
}
|
|
815
|
+
const stats = [];
|
|
816
|
+
for (const g of groups.values()) {
|
|
817
|
+
const sorted = g.ms.slice().sort((a, b) => a - b);
|
|
818
|
+
const mean = sorted.length ? sorted.reduce((a, b) => a + b, 0) / sorted.length : 0;
|
|
819
|
+
stats.push({
|
|
820
|
+
provider: g.provider,
|
|
821
|
+
model: g.model,
|
|
822
|
+
calls: g.calls,
|
|
823
|
+
errors: g.errors,
|
|
824
|
+
errorRate: g.calls ? g.errors / g.calls : 0,
|
|
825
|
+
ms: {
|
|
826
|
+
p50: Math.round(percentile(sorted, 50)),
|
|
827
|
+
p95: Math.round(percentile(sorted, 95)),
|
|
828
|
+
max: sorted.length ? sorted[sorted.length - 1] : 0,
|
|
829
|
+
mean: Math.round(mean)
|
|
830
|
+
},
|
|
831
|
+
meanTokens: g.tokens.length ? Math.round(g.tokens.reduce((a, b) => a + b, 0) / g.tokens.length) : null,
|
|
832
|
+
reasoningEfforts: Array.from(g.efforts).sort(),
|
|
833
|
+
tools: Array.from(g.tools).sort()
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
stats.sort((a, b) => b.ms.p95 - a.ms.p95);
|
|
837
|
+
return stats;
|
|
838
|
+
}
|
|
839
|
+
function aggregateAgreement(records) {
|
|
840
|
+
const groups = /* @__PURE__ */ new Map();
|
|
841
|
+
for (const rec of Array.isArray(records) ? records : []) {
|
|
842
|
+
if (!rec || !Array.isArray(rec.opinions)) continue;
|
|
843
|
+
const finalVerdict = typeof rec.verdict === "string" ? rec.verdict : null;
|
|
844
|
+
for (const op of rec.opinions) {
|
|
845
|
+
if (!op || typeof op.provider !== "string") continue;
|
|
846
|
+
const provider = op.provider;
|
|
847
|
+
const model = typeof op.model === "string" ? op.model : "";
|
|
848
|
+
const key = `${provider}|${model}`;
|
|
849
|
+
let g = groups.get(key);
|
|
850
|
+
if (!g) {
|
|
851
|
+
g = { provider, model, votes: 0, agreed: 0, abstained: 0 };
|
|
852
|
+
groups.set(key, g);
|
|
853
|
+
}
|
|
854
|
+
const opVerdict = typeof op.verdict === "string" ? op.verdict : null;
|
|
855
|
+
if (finalVerdict && opVerdict) {
|
|
856
|
+
g.votes += 1;
|
|
857
|
+
if (opVerdict === finalVerdict) g.agreed += 1;
|
|
858
|
+
} else {
|
|
859
|
+
g.abstained += 1;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
const out = [];
|
|
864
|
+
for (const g of groups.values()) {
|
|
865
|
+
out.push({
|
|
866
|
+
provider: g.provider,
|
|
867
|
+
model: g.model,
|
|
868
|
+
votes: g.votes,
|
|
869
|
+
agreed: g.agreed,
|
|
870
|
+
agreementRate: g.votes ? g.agreed / g.votes : null,
|
|
871
|
+
abstained: g.abstained
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
out.sort((a, b) => {
|
|
875
|
+
const ar = a.agreementRate == null ? Infinity : a.agreementRate;
|
|
876
|
+
const br = b.agreementRate == null ? Infinity : b.agreementRate;
|
|
877
|
+
return ar - br;
|
|
878
|
+
});
|
|
879
|
+
return out;
|
|
880
|
+
}
|
|
881
|
+
function detectOutliers(stats) {
|
|
882
|
+
const eligible = (Array.isArray(stats) ? stats : []).filter((s) => s.calls >= MIN_CALLS);
|
|
883
|
+
if (!eligible.length) return [];
|
|
884
|
+
const fastestP95 = Math.min(...eligible.map((s) => s.ms.p95));
|
|
885
|
+
const baseline = Math.max(fastestP95, MIN_BASELINE_MS);
|
|
886
|
+
const out = [];
|
|
887
|
+
for (const s of eligible) {
|
|
888
|
+
if (s.errorRate >= HIGH_ERROR_RATE) {
|
|
889
|
+
out.push({ provider: s.provider, model: s.model, kind: "high-error", detail: `${Math.round(s.errorRate * 100)}% of ${s.calls} calls errored` });
|
|
890
|
+
}
|
|
891
|
+
if (s.ms.p95 >= ABS_SLOW_MS) {
|
|
892
|
+
out.push({ provider: s.provider, model: s.model, kind: "slow-absolute", detail: `p95 ${s.ms.p95}ms (>= ${ABS_SLOW_MS}ms)` });
|
|
893
|
+
} else if (s.ms.p95 >= SLOW_FACTOR * baseline) {
|
|
894
|
+
out.push({ provider: s.provider, model: s.model, kind: "slow-relative", detail: `p95 ${s.ms.p95}ms vs fastest-peer baseline ${Math.round(baseline)}ms` });
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return out;
|
|
898
|
+
}
|
|
899
|
+
function leverFor(provider) {
|
|
900
|
+
if (provider.startsWith(OR_PREFIX)) return { kind: "openrouter", alias: provider.slice(OR_PREFIX.length) };
|
|
901
|
+
if (provider === "codex" || provider === "gemini") return { kind: "external" };
|
|
902
|
+
if (provider === "grok") return { kind: "grok" };
|
|
903
|
+
return { kind: "unknown" };
|
|
904
|
+
}
|
|
905
|
+
function recommend(stats, agreement, config) {
|
|
906
|
+
const cfg = config && typeof config === "object" ? config : {};
|
|
907
|
+
const models = cfg.models && typeof cfg.models === "object" ? cfg.models : {};
|
|
908
|
+
const outliers = detectOutliers(stats);
|
|
909
|
+
const agreeBy = /* @__PURE__ */ new Map();
|
|
910
|
+
for (const a of Array.isArray(agreement) ? agreement : []) agreeBy.set(a.provider, a);
|
|
911
|
+
const out = [];
|
|
912
|
+
let slowOpenRouterCount = 0;
|
|
913
|
+
for (const o of outliers) {
|
|
914
|
+
if (o.kind === "high-error") {
|
|
915
|
+
const lever2 = leverFor(o.provider);
|
|
916
|
+
out.push({
|
|
917
|
+
target: lever2.kind === "openrouter" ? "deliberation" : "external",
|
|
918
|
+
subject: o.provider,
|
|
919
|
+
configKey: lever2.kind === "openrouter" ? `models.${lever2.alias}.askAll` : null,
|
|
920
|
+
action: lever2.kind === "openrouter" ? `set models.${lever2.alias}.askAll=false until it stabilizes` : `check the ${o.provider} credentials/CLI session`,
|
|
921
|
+
rationale: o.detail
|
|
922
|
+
});
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
const lever = leverFor(o.provider);
|
|
926
|
+
const agree = agreeBy.get(o.provider);
|
|
927
|
+
const rarelyDissents = !!(agree && agree.agreementRate != null && agree.votes >= MIN_VOTES && agree.agreementRate >= HIGH_AGREEMENT);
|
|
928
|
+
const valueNote = rarelyDissents ? ` It also agreed with the final verdict ${agree ? Math.round((agree.agreementRate || 0) * 100) : 0}% of ${agree ? agree.votes : 0} votes (rarely adds dissent), so it is the strongest cut candidate.` : "";
|
|
929
|
+
if (lever.kind === "openrouter") {
|
|
930
|
+
slowOpenRouterCount += 1;
|
|
931
|
+
const alias = typeof lever.alias === "string" ? lever.alias : "";
|
|
932
|
+
const entry = models[alias] && typeof models[alias] === "object" ? models[alias] : null;
|
|
933
|
+
const effort = entry && typeof entry.reasoningEffort === "string" ? entry.reasoningEffort : null;
|
|
934
|
+
if (effort && effort !== "low") {
|
|
935
|
+
out.push({ target: "deliberation", subject: o.provider, configKey: `models.${alias}.reasoningEffort`, action: `lower models.${alias}.reasoningEffort (currently ${effort})`, rationale: `Slowest in the panel (${o.detail}).${valueNote}` });
|
|
936
|
+
}
|
|
937
|
+
out.push({ target: "deliberation", subject: o.provider, configKey: `models.${alias}.askAll`, action: `set models.${alias}.askAll=false to drop it from /ask-all fan-out`, rationale: `In parallel fan-out, wall-time is the slowest model (${o.detail}).${valueNote}` });
|
|
938
|
+
} else if (lever.kind === "external") {
|
|
939
|
+
out.push({ target: "external", subject: o.provider, configKey: null, action: o.provider === "codex" ? "lower model_reasoning_effort in ~/.codex/config.toml (or pass it per-call)" : "lower the Gemini/agy reasoning setting", rationale: `Slowest in the panel (${o.detail}); its reasoning lever is outside deliberation's config.${valueNote}` });
|
|
940
|
+
} else {
|
|
941
|
+
out.push({ target: "deliberation", subject: o.provider, configKey: null, action: `consider whether ${o.provider} earns its latency in the panel`, rationale: `${o.detail}.${valueNote}` });
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (slowOpenRouterCount >= 2) {
|
|
945
|
+
const fanout = cfg.routing && typeof cfg.routing.maxFanout === "number" ? cfg.routing.maxFanout : null;
|
|
946
|
+
out.push({ target: "deliberation", subject: "panel", configKey: "routing.maxFanout", action: fanout ? `lower routing.maxFanout (currently ${fanout})` : "set routing.maxFanout to 1-2", rationale: `${slowOpenRouterCount} OpenRouter models are slow outliers; a smaller fan-out cuts cost and parallel wall-time.` });
|
|
947
|
+
}
|
|
948
|
+
return out;
|
|
949
|
+
}
|
|
950
|
+
function buildAnalysis(events, records, config, meta) {
|
|
951
|
+
const evs = Array.isArray(events) ? events : [];
|
|
952
|
+
const recs = Array.isArray(records) ? records : [];
|
|
953
|
+
const stats = aggregateByModel(evs);
|
|
954
|
+
const agreement = aggregateAgreement(recs);
|
|
955
|
+
const outliers = detectOutliers(stats);
|
|
956
|
+
const recommendations = recommend(stats, agreement, config);
|
|
957
|
+
return {
|
|
958
|
+
stats,
|
|
959
|
+
agreement,
|
|
960
|
+
outliers,
|
|
961
|
+
recommendations,
|
|
962
|
+
meta: {
|
|
963
|
+
logPath: meta && meta.logPath,
|
|
964
|
+
debugEnabled: !!(meta && meta.debugEnabled),
|
|
965
|
+
sessionsPersist: !!(meta && meta.sessionsPersist),
|
|
966
|
+
eventsParsed: evs.length,
|
|
967
|
+
sessionsRead: recs.length,
|
|
968
|
+
insufficientData: stats.length === 0
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
module2.exports = {
|
|
973
|
+
SLOW_FACTOR,
|
|
974
|
+
MIN_CALLS,
|
|
975
|
+
ABS_SLOW_MS,
|
|
976
|
+
HIGH_ERROR_RATE,
|
|
977
|
+
HIGH_AGREEMENT,
|
|
978
|
+
MIN_VOTES,
|
|
979
|
+
parseDebugLog,
|
|
980
|
+
percentile,
|
|
981
|
+
aggregateByModel,
|
|
982
|
+
aggregateAgreement,
|
|
983
|
+
detectOutliers,
|
|
984
|
+
recommend,
|
|
985
|
+
buildAnalysis
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
|
|
753
990
|
// ../../core/sessions.js
|
|
754
991
|
var require_sessions = __commonJS({
|
|
755
992
|
"../../core/sessions.js"(exports2, module2) {
|
|
@@ -4162,6 +4399,7 @@ var require_openrouter = __commonJS({
|
|
|
4162
4399
|
var { makeRegistry, pinAlias } = require_registry();
|
|
4163
4400
|
var { askAll, askOne, consensus, runToConvergence } = require_orchestrate();
|
|
4164
4401
|
var { PROMPTS } = require_prompts();
|
|
4402
|
+
var analyzeCore = require_analyze();
|
|
4165
4403
|
var ADVISORY = { readOnlyHint: true };
|
|
4166
4404
|
var ASK_PROVIDER = { "ask-gpt": "codex", "ask-gemini": "gemini", "ask-grok": "grok", "ask-openrouter": "openrouter" };
|
|
4167
4405
|
var EXPERTS = ["architect", "plan-reviewer", "scope-analyst", "code-reviewer", "security-analyst", "researcher", "debugger"];
|
|
@@ -4183,6 +4421,15 @@ function panelInputSchema() {
|
|
|
4183
4421
|
}
|
|
4184
4422
|
};
|
|
4185
4423
|
}
|
|
4424
|
+
function analyzeInputSchema() {
|
|
4425
|
+
return {
|
|
4426
|
+
type: "object",
|
|
4427
|
+
properties: {
|
|
4428
|
+
sessions: { type: "integer", description: "How many recent session records to read for the agreement lens (default 50)." },
|
|
4429
|
+
limitBytes: { type: "integer", description: "Tail size of the debug log to read, in bytes (default 1048576)." }
|
|
4430
|
+
}
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4186
4433
|
function askOneInputSchema() {
|
|
4187
4434
|
return {
|
|
4188
4435
|
type: "object",
|
|
@@ -4294,7 +4541,8 @@ function toolList() {
|
|
|
4294
4541
|
{ 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 },
|
|
4295
4542
|
{ 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
4543
|
{ 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 }
|
|
4544
|
+
{ 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 },
|
|
4545
|
+
{ name: "analyze", description: "Analyze recent runs from the opt-in debug log (latency/tokens/reasoning-effort per model) plus the session store (verdict agreement rate), and return advisory tuning suggestions (disable a slow/redundant model in ask-all, lower an OpenRouter model's reasoning, adjust maxFanout). Two lenses reported side by side - timing and agreement are NOT joined (no shared run id). Suggestions are advisory; it writes nothing. Requires `debug.enabled` for the timing lens. Read-only. The `/deliberation:analyze` slash command renders this for humans.", inputSchema: analyzeInputSchema(), annotations: ADVISORY }
|
|
4298
4546
|
];
|
|
4299
4547
|
for (const t of Object.keys(ASK_PROVIDER)) {
|
|
4300
4548
|
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 });
|
|
@@ -4789,6 +5037,46 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir, notify
|
|
|
4789
5037
|
return { error: /expected status/.test(msg) ? "unexpected-action-for-status" : "step-failed", detail: msg };
|
|
4790
5038
|
}
|
|
4791
5039
|
}
|
|
5040
|
+
function runAnalyze(args) {
|
|
5041
|
+
const fs = require("node:fs");
|
|
5042
|
+
const cfg = getConfig() || {};
|
|
5043
|
+
const dbg = cfg.debug || {};
|
|
5044
|
+
const debugEnabled = !!dbg.enabled;
|
|
5045
|
+
const logPath = typeof dbg.path === "string" && dbg.path || resolveDebugLogPath();
|
|
5046
|
+
const limitBytes = Number.isInteger(args.limitBytes) && args.limitBytes > 0 ? args.limitBytes : 1024 * 1024;
|
|
5047
|
+
let text = "";
|
|
5048
|
+
try {
|
|
5049
|
+
const fd = fs.openSync(logPath, "r");
|
|
5050
|
+
try {
|
|
5051
|
+
const size = fs.fstatSync(fd).size;
|
|
5052
|
+
const start = size > limitBytes ? size - limitBytes : 0;
|
|
5053
|
+
const len = size - start;
|
|
5054
|
+
if (len > 0) {
|
|
5055
|
+
const buf = Buffer.alloc(len);
|
|
5056
|
+
fs.readSync(fd, buf, 0, len, start);
|
|
5057
|
+
text = buf.toString("utf8");
|
|
5058
|
+
if (start > 0) {
|
|
5059
|
+
const nl = text.indexOf("\n");
|
|
5060
|
+
if (nl >= 0) text = text.slice(nl + 1);
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
} finally {
|
|
5064
|
+
fs.closeSync(fd);
|
|
5065
|
+
}
|
|
5066
|
+
} catch {
|
|
5067
|
+
}
|
|
5068
|
+
const events = analyzeCore.parseDebugLog(text);
|
|
5069
|
+
const records = [];
|
|
5070
|
+
const persist = persistEnabled();
|
|
5071
|
+
if (persist) {
|
|
5072
|
+
const n = Number.isInteger(args.sessions) && args.sessions > 0 ? args.sessions : 50;
|
|
5073
|
+
for (const e of sessions.listSessions({ dir: sessionsDir }).slice(0, n)) {
|
|
5074
|
+
const rec = sessions.readSession(e.id, { dir: sessionsDir });
|
|
5075
|
+
if (rec) records.push(rec);
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
return analyzeCore.buildAnalysis(events, records, cfg, { logPath, debugEnabled, sessionsPersist: persist });
|
|
5079
|
+
}
|
|
4792
5080
|
async function call(name, args) {
|
|
4793
5081
|
const namedExpert = EXPERTS.includes(name) ? name : void 0;
|
|
4794
5082
|
const argExpert = typeof args.expert === "string" ? args.expert : void 0;
|
|
@@ -4818,6 +5106,9 @@ function buildServer({ providers, getConfig, getConfigError, sessionsDir, notify
|
|
|
4818
5106
|
const result = await askOne(p, withPersona(req, expert), { logger: currentLogger(), tool: "ask-one", cache: resultCache });
|
|
4819
5107
|
return jsonResult({ result });
|
|
4820
5108
|
}
|
|
5109
|
+
if (name === "analyze") {
|
|
5110
|
+
return jsonResult(runAnalyze(args));
|
|
5111
|
+
}
|
|
4821
5112
|
if (name === "ask-all") {
|
|
4822
5113
|
const { payload, parts } = await runAskAll(req, expert);
|
|
4823
5114
|
const sid = persistRun("ask-all", req, expert, parts);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antonbabenko/deliberation-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.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" },
|