adhdev 0.9.82-rc.422 → 0.9.82-rc.423
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/cli/adhdev-mcp.js +5 -5
- package/dist/cli/index.js +5 -5
- package/dist/index.js +5 -5
- package/package.json +3 -3
- package/vendor/mcp-server/index.js +285 -36
- package/vendor/mcp-server/index.js.map +1 -1
package/dist/cli/adhdev-mcp.js
CHANGED
|
@@ -1273,10 +1273,10 @@ function readInjected(value) {
|
|
|
1273
1273
|
}
|
|
1274
1274
|
function getDaemonBuildInfo() {
|
|
1275
1275
|
if (cached) return cached;
|
|
1276
|
-
const commit = readInjected(true ? "
|
|
1277
|
-
const commitShort = readInjected(true ? "
|
|
1278
|
-
const version2 = readInjected(true ? "0.9.82-rc.
|
|
1279
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
1276
|
+
const commit = readInjected(true ? "b8cb72411ebf328f26357d963c53d2fa07f19c78" : void 0) ?? "unknown";
|
|
1277
|
+
const commitShort = readInjected(true ? "b8cb7241" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
1278
|
+
const version2 = readInjected(true ? "0.9.82-rc.423" : void 0) ?? readInjected(typeof process !== "undefined" ? "0.9.82-rc.423" : void 0) ?? "unknown";
|
|
1279
|
+
const builtAt = readInjected(true ? "2026-06-29T07:40:56.706Z" : void 0);
|
|
1280
1280
|
cached = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
1281
1281
|
return cached;
|
|
1282
1282
|
}
|
|
@@ -137165,7 +137165,7 @@ var init_adhdev_daemon = __esm({
|
|
|
137165
137165
|
init_version();
|
|
137166
137166
|
init_src();
|
|
137167
137167
|
init_runtime_defaults();
|
|
137168
|
-
pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.
|
|
137168
|
+
pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.423" });
|
|
137169
137169
|
LOCAL_DAEMON_HEALTH_TIMEOUT_MS = 8e3;
|
|
137170
137170
|
AdhdevDaemon = class _AdhdevDaemon {
|
|
137171
137171
|
localHttpServer = null;
|
package/dist/cli/index.js
CHANGED
|
@@ -1273,10 +1273,10 @@ function readInjected(value) {
|
|
|
1273
1273
|
}
|
|
1274
1274
|
function getDaemonBuildInfo() {
|
|
1275
1275
|
if (cached) return cached;
|
|
1276
|
-
const commit = readInjected(true ? "
|
|
1277
|
-
const commitShort = readInjected(true ? "
|
|
1278
|
-
const version2 = readInjected(true ? "0.9.82-rc.
|
|
1279
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
1276
|
+
const commit = readInjected(true ? "b8cb72411ebf328f26357d963c53d2fa07f19c78" : void 0) ?? "unknown";
|
|
1277
|
+
const commitShort = readInjected(true ? "b8cb7241" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
1278
|
+
const version2 = readInjected(true ? "0.9.82-rc.423" : void 0) ?? readInjected(typeof process !== "undefined" ? "0.9.82-rc.423" : void 0) ?? "unknown";
|
|
1279
|
+
const builtAt = readInjected(true ? "2026-06-29T07:40:56.706Z" : void 0);
|
|
1280
1280
|
cached = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
1281
1281
|
return cached;
|
|
1282
1282
|
}
|
|
@@ -137135,7 +137135,7 @@ var init_adhdev_daemon = __esm({
|
|
|
137135
137135
|
init_version();
|
|
137136
137136
|
init_src();
|
|
137137
137137
|
init_runtime_defaults();
|
|
137138
|
-
pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.
|
|
137138
|
+
pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.423" });
|
|
137139
137139
|
LOCAL_DAEMON_HEALTH_TIMEOUT_MS = 8e3;
|
|
137140
137140
|
AdhdevDaemon = class _AdhdevDaemon {
|
|
137141
137141
|
localHttpServer = null;
|
package/dist/index.js
CHANGED
|
@@ -753,10 +753,10 @@ function readInjected(value) {
|
|
|
753
753
|
}
|
|
754
754
|
function getDaemonBuildInfo() {
|
|
755
755
|
if (cached) return cached;
|
|
756
|
-
const commit = readInjected(true ? "
|
|
757
|
-
const commitShort = readInjected(true ? "
|
|
758
|
-
const version2 = readInjected(true ? "0.9.82-rc.
|
|
759
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
756
|
+
const commit = readInjected(true ? "b8cb72411ebf328f26357d963c53d2fa07f19c78" : void 0) ?? "unknown";
|
|
757
|
+
const commitShort = readInjected(true ? "b8cb7241" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
758
|
+
const version2 = readInjected(true ? "0.9.82-rc.423" : void 0) ?? readInjected(typeof process !== "undefined" ? "0.9.82-rc.423" : void 0) ?? "unknown";
|
|
759
|
+
const builtAt = readInjected(true ? "2026-06-29T07:40:56.706Z" : void 0);
|
|
760
760
|
cached = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
761
761
|
return cached;
|
|
762
762
|
}
|
|
@@ -105957,7 +105957,7 @@ var init_adhdev_daemon = __esm({
|
|
|
105957
105957
|
init_version();
|
|
105958
105958
|
init_src();
|
|
105959
105959
|
init_runtime_defaults();
|
|
105960
|
-
pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.
|
|
105960
|
+
pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.423" });
|
|
105961
105961
|
LOCAL_DAEMON_HEALTH_TIMEOUT_MS = 8e3;
|
|
105962
105962
|
AdhdevDaemon = class _AdhdevDaemon {
|
|
105963
105963
|
localHttpServer = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adhdev",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.423",
|
|
4
4
|
"description": "ADHDev — Agent Dashboard Hub for Dev. The control hub for your AI coding agents (Claude Code, Codex, Hermes, …).",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
"node": ">=18"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@adhdev/daemon-core": "0.9.82-rc.
|
|
52
|
+
"@adhdev/daemon-core": "0.9.82-rc.423",
|
|
53
53
|
"@adhdev/ghostty-vt-node": "*",
|
|
54
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
54
|
+
"@adhdev/mesh-shared": "0.9.82-rc.423",
|
|
55
55
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
56
56
|
"@xterm/addon-serialize": "^0.14.0",
|
|
57
57
|
"@xterm/xterm": "^6.0.0",
|
|
@@ -1172,7 +1172,9 @@ var MESH_MAGI_REVIEW_TOOL = {
|
|
|
1172
1172
|
}
|
|
1173
1173
|
},
|
|
1174
1174
|
n: { type: "number", description: "Global replica override per member (clamped by the total-replica guard cap, default 12)." },
|
|
1175
|
-
|
|
1175
|
+
task_kind: { type: "string", enum: ["claim_audit", "rca", "design", "freeform"], description: "Selects the SINGLE output schema injected into each replica prompt and the strict parser used at collection (no schema-on-schema conflict). claim_audit (DEFAULT, backward-compatible): {claims[],top_findings[],open_questions[]}. rca: {rootCause,failsAt,mechanism,evidence[],fixDirection,confidence}. design: {recommendation,rationale,alternatives[],tradeoffs[],risks[],evidence[],confidence}. freeform: no schema \u2014 natural-language answer, parsing/evidence checks waived, cross-verification is weak. Every kind except freeform requires non-empty evidence[]; an empty-evidence or schema-invalid answer triggers ONE delta re-request before being dropped as unparseable. Do NOT also embed an output-format schema in the question \u2014 it collides with this contract (a warning is surfaced if detected)." },
|
|
1176
|
+
mode: { type: "string", enum: ["rca", "investigation", "claim_audit", "design_review", "code_audit"], description: "Synthesis emphasis hint \u2014 affects labels only, never the agent count or schema. Distinct from task_kind (which selects the output schema)." },
|
|
1177
|
+
use_judge: { type: "boolean", description: "Default false (clustering synthesis). STUB: judge synthesis is not yet implemented \u2014 passing true currently falls back to clustering with a warning. Reserved interface only." },
|
|
1176
1178
|
require_independent_evidence: { type: "boolean", description: "Default true \u2014 high-impact claims with no file:line/source evidence are routed to needs_verification." },
|
|
1177
1179
|
include_stale: { type: "boolean", description: "Default false. By default, panel members whose node HEAD commit differs from the coordinator reference commit are EXCLUDED (they would investigate different code). Set true to fan out to them anyway \u2014 results will be git-skewed and a warning is surfaced. If exclusion drops the panel below 2 independent targets the call errors rather than degrading to N=1; include_stale=true is one way to recover." },
|
|
1178
1180
|
wait: { type: "boolean", description: "Default true \u2014 collect replica outputs and return the synthesis. Set false to dispatch async and return a consensusGroupId handle; collect later with mesh_magi_collect." },
|
|
@@ -1188,6 +1190,7 @@ var MESH_MAGI_COLLECT_TOOL = {
|
|
|
1188
1190
|
type: "object",
|
|
1189
1191
|
properties: {
|
|
1190
1192
|
consensus_group_id: { type: "string", description: "The consensusGroupId returned by a wait=false mesh_magi_review." },
|
|
1193
|
+
task_kind: { type: "string", enum: ["claim_audit", "rca", "design", "freeform"], description: "Optional override of the task_kind used to parse replica answers. Normally recovered automatically from the original dispatch \u2014 only set this if the dispatched ledger entry was pruned and auto-recovery falls back to claim_audit incorrectly." },
|
|
1191
1194
|
require_independent_evidence: { type: "boolean", description: "Default true \u2014 high-impact claims with no file:line/source evidence are routed to needs_verification." },
|
|
1192
1195
|
wait: { type: "boolean", description: "Default false (snapshot). Set true to block for outstanding replicas up to wait_timeout_ms before synthesizing." },
|
|
1193
1196
|
wait_timeout_ms: { type: "number", description: "When wait=true, max time to wait for remaining replica completion. Default ~4 min." }
|
|
@@ -4071,6 +4074,12 @@ var MAGI_CLUSTER_JACCARD = 0.5;
|
|
|
4071
4074
|
var MAGI_DEFAULT_WAIT_MS = 18e4;
|
|
4072
4075
|
var MAGI_MAX_WAIT_MS = 6e5;
|
|
4073
4076
|
var MAGI_POLL_INTERVAL_MS = 5e3;
|
|
4077
|
+
var VALID_TASK_KINDS = ["claim_audit", "rca", "design", "freeform"];
|
|
4078
|
+
var DEFAULT_TASK_KIND = "claim_audit";
|
|
4079
|
+
function normalizeMagiTaskKind(raw) {
|
|
4080
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : "";
|
|
4081
|
+
return VALID_TASK_KINDS.includes(s) ? s : DEFAULT_TASK_KIND;
|
|
4082
|
+
}
|
|
4074
4083
|
var VALID_STANCES = /* @__PURE__ */ new Set(["support", "oppose", "uncertain"]);
|
|
4075
4084
|
function coerceClaim(raw) {
|
|
4076
4085
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
@@ -4145,6 +4154,130 @@ function parseMagiResponse(text) {
|
|
|
4145
4154
|
}
|
|
4146
4155
|
return null;
|
|
4147
4156
|
}
|
|
4157
|
+
function asStringArray(raw) {
|
|
4158
|
+
return Array.isArray(raw) ? raw.map((e) => typeof e === "string" ? e.trim() : "").filter(Boolean) : [];
|
|
4159
|
+
}
|
|
4160
|
+
function asConfidence(raw) {
|
|
4161
|
+
return typeof raw === "number" && Number.isFinite(raw) ? Math.min(1, Math.max(0, raw)) : 0.5;
|
|
4162
|
+
}
|
|
4163
|
+
function asTrimmedString(raw) {
|
|
4164
|
+
return typeof raw === "string" ? raw.trim() : "";
|
|
4165
|
+
}
|
|
4166
|
+
function coerceRcaResponse(raw) {
|
|
4167
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
4168
|
+
const r = raw;
|
|
4169
|
+
const rootCause = asTrimmedString(r.rootCause);
|
|
4170
|
+
const mechanism = asTrimmedString(r.mechanism);
|
|
4171
|
+
const failsAt = asTrimmedString(r.failsAt);
|
|
4172
|
+
const fixDirection = asTrimmedString(r.fixDirection);
|
|
4173
|
+
const evidence = asStringArray(r.evidence);
|
|
4174
|
+
const confidence = asConfidence(r.confidence);
|
|
4175
|
+
if (!rootCause && !mechanism && !failsAt && !fixDirection && evidence.length === 0) return null;
|
|
4176
|
+
const payload = { rootCause, failsAt, mechanism, evidence, fixDirection, confidence };
|
|
4177
|
+
if (!rootCause || !mechanism) return { payload, failReason: "missing_required_fields" };
|
|
4178
|
+
if (evidence.length === 0) return { payload, failReason: "empty_evidence" };
|
|
4179
|
+
return { payload };
|
|
4180
|
+
}
|
|
4181
|
+
function coerceDesignResponse(raw) {
|
|
4182
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
4183
|
+
const r = raw;
|
|
4184
|
+
const recommendation = asTrimmedString(r.recommendation);
|
|
4185
|
+
const rationale = asTrimmedString(r.rationale);
|
|
4186
|
+
const alternatives = asStringArray(r.alternatives);
|
|
4187
|
+
const tradeoffs = asStringArray(r.tradeoffs);
|
|
4188
|
+
const risks = asStringArray(r.risks);
|
|
4189
|
+
const evidence = asStringArray(r.evidence);
|
|
4190
|
+
const confidence = asConfidence(r.confidence);
|
|
4191
|
+
if (!recommendation && !rationale && alternatives.length === 0 && tradeoffs.length === 0 && risks.length === 0 && evidence.length === 0) return null;
|
|
4192
|
+
const payload = { recommendation, rationale, alternatives, tradeoffs, risks, evidence, confidence };
|
|
4193
|
+
if (!recommendation || !rationale) return { payload, failReason: "missing_required_fields" };
|
|
4194
|
+
if (evidence.length === 0) return { payload, failReason: "empty_evidence" };
|
|
4195
|
+
return { payload };
|
|
4196
|
+
}
|
|
4197
|
+
function rcaToCommonSchema(p) {
|
|
4198
|
+
return {
|
|
4199
|
+
claims: [{ claim: p.rootCause, stance: "support", evidence: p.evidence, confidence: p.confidence }],
|
|
4200
|
+
top_findings: [
|
|
4201
|
+
...p.failsAt ? [`fails at: ${p.failsAt}`] : [],
|
|
4202
|
+
...p.mechanism ? [`mechanism: ${p.mechanism}`] : [],
|
|
4203
|
+
...p.fixDirection ? [`fix direction: ${p.fixDirection}`] : []
|
|
4204
|
+
],
|
|
4205
|
+
open_questions: []
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
function designToCommonSchema(p) {
|
|
4209
|
+
return {
|
|
4210
|
+
claims: [{ claim: p.recommendation, stance: "support", evidence: p.evidence, confidence: p.confidence }],
|
|
4211
|
+
top_findings: [
|
|
4212
|
+
...p.rationale ? [`rationale: ${p.rationale}`] : [],
|
|
4213
|
+
...p.alternatives.map((a) => `alternative: ${a}`),
|
|
4214
|
+
...p.tradeoffs.map((t) => `tradeoff: ${t}`)
|
|
4215
|
+
],
|
|
4216
|
+
open_questions: p.risks.map((r) => `risk: ${r}`)
|
|
4217
|
+
};
|
|
4218
|
+
}
|
|
4219
|
+
function firstJsonCandidate(text, coerce) {
|
|
4220
|
+
if (typeof text !== "string" || !text.trim()) return null;
|
|
4221
|
+
try {
|
|
4222
|
+
const direct = coerce(JSON.parse(text));
|
|
4223
|
+
if (direct) return direct;
|
|
4224
|
+
} catch {
|
|
4225
|
+
}
|
|
4226
|
+
for (const candidate of extractJsonObjectCandidates(text)) {
|
|
4227
|
+
try {
|
|
4228
|
+
const parsed = coerce(JSON.parse(candidate));
|
|
4229
|
+
if (parsed) return parsed;
|
|
4230
|
+
} catch {
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
return null;
|
|
4234
|
+
}
|
|
4235
|
+
function parseMagiResponseForKind(text, kind) {
|
|
4236
|
+
if (kind === "freeform") {
|
|
4237
|
+
const trimmed = typeof text === "string" ? text.trim() : "";
|
|
4238
|
+
if (!trimmed) return { ok: false, failReason: "no_parseable_output" };
|
|
4239
|
+
const payload = { text: trimmed };
|
|
4240
|
+
return { ok: true, response: { claims: [], top_findings: [trimmed], open_questions: [] }, payload };
|
|
4241
|
+
}
|
|
4242
|
+
if (kind === "claim_audit") {
|
|
4243
|
+
const parsed = parseMagiResponse(text);
|
|
4244
|
+
if (!parsed) return { ok: false, failReason: "no_parseable_output" };
|
|
4245
|
+
const hasEvidence = parsed.claims.some((c) => c.evidence.length > 0) || parsed.top_findings.length > 0;
|
|
4246
|
+
if (!hasEvidence && parsed.claims.length > 0) return { ok: false, payload: parsed, failReason: "empty_evidence" };
|
|
4247
|
+
return { ok: true, response: parsed, payload: parsed };
|
|
4248
|
+
}
|
|
4249
|
+
if (kind === "rca") {
|
|
4250
|
+
const result2 = firstJsonCandidate(text, coerceRcaResponse);
|
|
4251
|
+
if (!result2) return { ok: false, failReason: "no_parseable_output" };
|
|
4252
|
+
if (result2.failReason) return { ok: false, payload: result2.payload, failReason: result2.failReason };
|
|
4253
|
+
return { ok: true, response: rcaToCommonSchema(result2.payload), payload: result2.payload };
|
|
4254
|
+
}
|
|
4255
|
+
const result = firstJsonCandidate(text, coerceDesignResponse);
|
|
4256
|
+
if (!result) return { ok: false, failReason: "no_parseable_output" };
|
|
4257
|
+
if (result.failReason) return { ok: false, payload: result.payload, failReason: result.failReason };
|
|
4258
|
+
return { ok: true, response: designToCommonSchema(result.payload), payload: result.payload };
|
|
4259
|
+
}
|
|
4260
|
+
function parseFirstMagiCandidateForKind(payload, kind, opts = {}) {
|
|
4261
|
+
const rawCandidates = collectMagiCandidateTexts(payload);
|
|
4262
|
+
let compactCandidates = [];
|
|
4263
|
+
try {
|
|
4264
|
+
compactCandidates = collectMagiCandidateTexts(
|
|
4265
|
+
compactChatPayload(payload, { sessionId: opts.sessionId ?? null })
|
|
4266
|
+
);
|
|
4267
|
+
} catch {
|
|
4268
|
+
}
|
|
4269
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4270
|
+
let lastFail = { ok: false, failReason: "no_parseable_output" };
|
|
4271
|
+
for (const candidate of [...rawCandidates, ...compactCandidates]) {
|
|
4272
|
+
const trimmed = candidate.trim();
|
|
4273
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
4274
|
+
seen.add(trimmed);
|
|
4275
|
+
const result = parseMagiResponseForKind(candidate, kind);
|
|
4276
|
+
if (result.ok) return result;
|
|
4277
|
+
if (result.failReason !== "no_parseable_output") lastFail = result;
|
|
4278
|
+
}
|
|
4279
|
+
return lastFail;
|
|
4280
|
+
}
|
|
4148
4281
|
var CLAIM_STOPWORDS = /* @__PURE__ */ new Set([
|
|
4149
4282
|
"the",
|
|
4150
4283
|
"a",
|
|
@@ -4476,16 +4609,72 @@ function resolveMagiReferenceCommit(ctx) {
|
|
|
4476
4609
|
const node = resolveCoordinatorNode(ctx);
|
|
4477
4610
|
return nodeHeadCommit(node);
|
|
4478
4611
|
}
|
|
4479
|
-
var
|
|
4612
|
+
var MAGI_CLAIM_AUDIT_CONTRACT = `When done, respond with ONLY a single JSON object (no prose, no code fence) matching this exact schema:
|
|
4480
4613
|
{
|
|
4481
4614
|
"claims": [ { "claim": "string", "stance": "support | oppose | uncertain", "evidence": ["file:line or external source"], "confidence": 0.0 } ],
|
|
4482
4615
|
"top_findings": ["string"],
|
|
4483
4616
|
"open_questions": ["string"]
|
|
4484
4617
|
}
|
|
4485
|
-
Each claim MUST carry concrete evidence (file:line or a cited source)
|
|
4618
|
+
Each claim MUST carry concrete evidence (file:line or a cited source) \u2014 unevidenced claims are flagged for re-verification. "stance" is your stance toward the claim being true. Do not invent agreement; report uncertainty honestly.`;
|
|
4619
|
+
var MAGI_RCA_CONTRACT = `When done, respond with ONLY a single JSON object (no prose, no code fence) matching this exact schema:
|
|
4620
|
+
{
|
|
4621
|
+
"rootCause": "string \u2014 the single underlying root cause",
|
|
4622
|
+
"failsAt": "file:line \u2014 the precise location the failure manifests",
|
|
4623
|
+
"mechanism": "string \u2014 how the root cause produces the observed symptom",
|
|
4624
|
+
"evidence": ["file:line or external source"],
|
|
4625
|
+
"fixDirection": "string \u2014 the direction a fix should take (do NOT write the fix)",
|
|
4626
|
+
"confidence": 0.0
|
|
4627
|
+
}
|
|
4628
|
+
"rootCause" and "mechanism" are REQUIRED. "evidence" MUST be non-empty (concrete file:line or cited source) \u2014 an empty evidence array is rejected and re-requested. Report uncertainty honestly.`;
|
|
4629
|
+
var MAGI_DESIGN_CONTRACT = `When done, respond with ONLY a single JSON object (no prose, no code fence) matching this exact schema:
|
|
4630
|
+
{
|
|
4631
|
+
"recommendation": "string \u2014 the recommended approach",
|
|
4632
|
+
"rationale": "string \u2014 why this approach",
|
|
4633
|
+
"alternatives": ["string \u2014 approaches considered and not chosen"],
|
|
4634
|
+
"tradeoffs": ["string"],
|
|
4635
|
+
"risks": ["string"],
|
|
4636
|
+
"evidence": ["file:line or external source backing the recommendation"],
|
|
4637
|
+
"confidence": 0.0
|
|
4638
|
+
}
|
|
4639
|
+
"recommendation" and "rationale" are REQUIRED. "evidence" MUST be non-empty \u2014 an empty evidence array is rejected and re-requested. Report uncertainty honestly.`;
|
|
4640
|
+
var MAGI_FREEFORM_CONTRACT = `Answer the question in natural language. No JSON schema is required for this task \u2014 write your analysis directly. (Note: a freeform answer is cross-verified only weakly, because it is unstructured.)`;
|
|
4641
|
+
function magiOutputContractFor(kind) {
|
|
4642
|
+
switch (kind) {
|
|
4643
|
+
case "rca":
|
|
4644
|
+
return MAGI_RCA_CONTRACT;
|
|
4645
|
+
case "design":
|
|
4646
|
+
return MAGI_DESIGN_CONTRACT;
|
|
4647
|
+
case "freeform":
|
|
4648
|
+
return MAGI_FREEFORM_CONTRACT;
|
|
4649
|
+
case "claim_audit":
|
|
4650
|
+
default:
|
|
4651
|
+
return MAGI_CLAIM_AUDIT_CONTRACT;
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
function detectQuestionOutputSchemaConflict(question) {
|
|
4655
|
+
const q = typeof question === "string" ? question : "";
|
|
4656
|
+
if (!q.trim()) return null;
|
|
4657
|
+
const lower = q.toLowerCase();
|
|
4658
|
+
const signals = [
|
|
4659
|
+
"respond with only",
|
|
4660
|
+
"respond with a single json",
|
|
4661
|
+
"single json object",
|
|
4662
|
+
"output format",
|
|
4663
|
+
"output schema",
|
|
4664
|
+
"reply with only",
|
|
4665
|
+
'"claims"',
|
|
4666
|
+
'"top_findings"',
|
|
4667
|
+
"matching this exact schema"
|
|
4668
|
+
];
|
|
4669
|
+
const hit = signals.find((s) => lower.includes(s));
|
|
4670
|
+
if (!hit) return null;
|
|
4671
|
+
return `The question text appears to embed an output-format schema (matched "${hit}"). MAGI already injects exactly one output contract for the selected task_kind, so a second schema in the question collides with it and replicas may fuse the two into unparseable output. Move any output-format instructions OUT of the question \u2014 describe only WHAT to investigate.`;
|
|
4672
|
+
}
|
|
4486
4673
|
function buildMagiTaskPrompt(args) {
|
|
4674
|
+
const kind = args.taskKind ?? DEFAULT_TASK_KIND;
|
|
4487
4675
|
const parts = [];
|
|
4488
4676
|
parts.push("You are one independent member of a multi-agent cross-verification quorum (MAGI). Several other agents on different machines/providers are answering the SAME question independently; your job is a rigorous, READ-ONLY investigation. Do NOT write, edit, commit, or push anything.");
|
|
4677
|
+
parts.push(`Task kind: ${kind}.`);
|
|
4489
4678
|
if (args.mode) parts.push(`Investigation mode: ${args.mode}.`);
|
|
4490
4679
|
parts.push(`
|
|
4491
4680
|
## Question
|
|
@@ -4500,7 +4689,7 @@ ${args.artifacts.map((a) => String(a)).join("\n\n---\n\n")}`);
|
|
|
4500
4689
|
}
|
|
4501
4690
|
parts.push(`
|
|
4502
4691
|
## Output
|
|
4503
|
-
${
|
|
4692
|
+
${magiOutputContractFor(kind)}`);
|
|
4504
4693
|
return parts.join("\n");
|
|
4505
4694
|
}
|
|
4506
4695
|
function collectMagiCandidateTexts(payload) {
|
|
@@ -4536,25 +4725,6 @@ function collectMagiCandidateTexts(payload) {
|
|
|
4536
4725
|
push(p.text);
|
|
4537
4726
|
return out;
|
|
4538
4727
|
}
|
|
4539
|
-
function parseFirstMagiCandidateWithCompactFallback(payload, opts = {}) {
|
|
4540
|
-
const rawCandidates = collectMagiCandidateTexts(payload);
|
|
4541
|
-
let compactCandidates = [];
|
|
4542
|
-
try {
|
|
4543
|
-
compactCandidates = collectMagiCandidateTexts(
|
|
4544
|
-
compactChatPayload(payload, { sessionId: opts.sessionId ?? null })
|
|
4545
|
-
);
|
|
4546
|
-
} catch {
|
|
4547
|
-
}
|
|
4548
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4549
|
-
for (const candidate of [...rawCandidates, ...compactCandidates]) {
|
|
4550
|
-
const trimmed = candidate.trim();
|
|
4551
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
4552
|
-
seen.add(trimmed);
|
|
4553
|
-
const parsed = parseMagiResponse(candidate);
|
|
4554
|
-
if (parsed) return parsed;
|
|
4555
|
-
}
|
|
4556
|
-
return null;
|
|
4557
|
-
}
|
|
4558
4728
|
function replicaCompletionIsWeak(meshId, taskId) {
|
|
4559
4729
|
try {
|
|
4560
4730
|
const entries = (0, import_daemon_core4.readLedgerEntries)(meshId, { kind: ["task_completed"], tail: 200 });
|
|
@@ -4659,6 +4829,10 @@ async function meshMagiPanelList(ctx, args = {}) {
|
|
|
4659
4829
|
async function meshMagiReview(ctx, args) {
|
|
4660
4830
|
const question = readString(args.question);
|
|
4661
4831
|
if (!question) return JSON.stringify({ success: false, error: "question required" });
|
|
4832
|
+
const taskKind = normalizeMagiTaskKind(args.task_kind ?? args.taskKind);
|
|
4833
|
+
const questionSchemaWarning = detectQuestionOutputSchemaConflict(question);
|
|
4834
|
+
const useJudge = (args.use_judge ?? args.useJudge) === true;
|
|
4835
|
+
const judgeWarning = useJudge ? "use_judge=true requested, but judge synthesis is not yet implemented \u2014 falling back to clustering synthesis." : null;
|
|
4662
4836
|
await refreshMeshFromDaemon(ctx);
|
|
4663
4837
|
const hasInlineMembers = Array.isArray(args.members) && args.members.length > 0;
|
|
4664
4838
|
let panel;
|
|
@@ -4713,7 +4887,7 @@ async function meshMagiReview(ctx, args) {
|
|
|
4713
4887
|
goal: `Cross-verify (read-only) across panel '${panelName}': ${question}${args.target ? `
|
|
4714
4888
|
Target: ${args.target}` : ""}`
|
|
4715
4889
|
});
|
|
4716
|
-
const prompt = buildMagiTaskPrompt({ question, target: args.target, artifacts: args.artifacts, mode: mode || void 0 });
|
|
4890
|
+
const prompt = buildMagiTaskPrompt({ question, target: args.target, artifacts: args.artifacts, mode: mode || void 0, taskKind });
|
|
4717
4891
|
const replicaRecords = [];
|
|
4718
4892
|
for (const replica of plan.replicas) {
|
|
4719
4893
|
try {
|
|
@@ -4745,7 +4919,8 @@ Target: ${args.target}` : ""}`
|
|
|
4745
4919
|
missionId: mission.id,
|
|
4746
4920
|
panel: panelName,
|
|
4747
4921
|
question,
|
|
4748
|
-
replicaCount: replicaRecords.length
|
|
4922
|
+
replicaCount: replicaRecords.length,
|
|
4923
|
+
taskKind
|
|
4749
4924
|
});
|
|
4750
4925
|
const queueTrigger = await triggerMeshQueueAndReport(ctx);
|
|
4751
4926
|
const baseResult = {
|
|
@@ -4754,6 +4929,9 @@ Target: ${args.target}` : ""}`
|
|
|
4754
4929
|
missionId: mission.id,
|
|
4755
4930
|
panel: panelName,
|
|
4756
4931
|
...hasInlineMembers ? { inline: true } : {},
|
|
4932
|
+
taskKind,
|
|
4933
|
+
...questionSchemaWarning ? { questionSchemaWarning } : {},
|
|
4934
|
+
...judgeWarning ? { judgeWarning } : {},
|
|
4757
4935
|
question,
|
|
4758
4936
|
replicaCount: replicaRecords.length,
|
|
4759
4937
|
replicas: replicaRecords.map((r) => ({ taskId: r.taskId, provider: r.provider, targetNodeId: r.targetNodeId })),
|
|
@@ -4791,12 +4969,14 @@ Target: ${args.target}` : ""}`
|
|
|
4791
4969
|
}
|
|
4792
4970
|
const collected = await collectMagiResponses(ctx, {
|
|
4793
4971
|
replicaTaskIds: replicaRecords.map((r) => r.taskId),
|
|
4794
|
-
timeoutMs: waitTimeoutMs
|
|
4972
|
+
timeoutMs: waitTimeoutMs,
|
|
4973
|
+
taskKind
|
|
4795
4974
|
});
|
|
4796
4975
|
const synthesis = synthesizeMagiResponses(collected.responses, {
|
|
4797
4976
|
replicasExpected: replicaRecords.length,
|
|
4798
4977
|
requireIndependentEvidence
|
|
4799
4978
|
});
|
|
4979
|
+
const freeformBanner = taskKind === "freeform" ? "task_kind=freeform: answers are unstructured natural language; cross-verification is WEAK (no claim clustering / independence scoring). Treat the collected answers as parallel opinions, not a verified consensus." : null;
|
|
4800
4980
|
persistMagiSynthesis(ctx, {
|
|
4801
4981
|
consensusGroupId,
|
|
4802
4982
|
missionId: mission.id,
|
|
@@ -4815,8 +4995,10 @@ Target: ${args.target}` : ""}`
|
|
|
4815
4995
|
missing: synthesis.replicasMissing,
|
|
4816
4996
|
staleReplicas: collected.staleCount,
|
|
4817
4997
|
...collected.staleCount > 0 ? { staleNote: `${collected.staleCount} replica(s) were detected STALE \u2014 assigned to a node/session no longer present in the live mesh; collection stopped early rather than waiting out the timeout.` } : {},
|
|
4818
|
-
...
|
|
4998
|
+
...collected.retriedCount > 0 ? { retriedReplicas: collected.retriedCount, retryNote: `${collected.retriedCount} replica(s) failed the ${taskKind} schema and were sent one delta re-request for a corrected single-JSON answer.` } : {},
|
|
4999
|
+
...synthesis.replicasMissing > 0 ? { missingNote: `Partial synthesis \u2014 ${synthesis.replicasMissing} of ${replicaRecords.length} replicas did not return a parseable response (timed out / failed / unparseable / schema-invalid / stale).` } : {}
|
|
4819
5000
|
},
|
|
5001
|
+
...freeformBanner ? { freeformBanner } : {},
|
|
4820
5002
|
synthesis
|
|
4821
5003
|
}, null, 2);
|
|
4822
5004
|
}
|
|
@@ -4824,6 +5006,8 @@ async function meshMagiCollect(ctx, args) {
|
|
|
4824
5006
|
const consensusGroupId = readString(args.consensus_group_id) || readString(args.consensusGroupId);
|
|
4825
5007
|
if (!consensusGroupId) return JSON.stringify({ success: false, error: "consensus_group_id required" });
|
|
4826
5008
|
await refreshMeshFromDaemon(ctx);
|
|
5009
|
+
const explicitKind = args.task_kind ?? args.taskKind;
|
|
5010
|
+
const taskKind = explicitKind !== void 0 ? normalizeMagiTaskKind(explicitKind) : recoverMagiTaskKind(ctx, consensusGroupId);
|
|
4827
5011
|
const replicaTasks = findMagiReplicaTasks((0, import_daemon_core4.getQueue)(ctx.mesh.id), consensusGroupId);
|
|
4828
5012
|
if (replicaTasks.length === 0) {
|
|
4829
5013
|
return JSON.stringify({
|
|
@@ -4837,11 +5021,12 @@ async function meshMagiCollect(ctx, args) {
|
|
|
4837
5021
|
const wait = args.wait === true;
|
|
4838
5022
|
const timeoutMs = wait ? Math.min(MAGI_MAX_WAIT_MS, Math.max(MAGI_POLL_INTERVAL_MS, Number(args.wait_timeout_ms ?? args.waitTimeoutMs) || MAGI_DEFAULT_WAIT_MS)) : 0;
|
|
4839
5023
|
const replicaTaskIds = replicaTasks.map((t) => readString(t.id)).filter(Boolean);
|
|
4840
|
-
const collected = await collectMagiResponses(ctx, { replicaTaskIds, timeoutMs });
|
|
5024
|
+
const collected = await collectMagiResponses(ctx, { replicaTaskIds, timeoutMs, taskKind });
|
|
4841
5025
|
const synthesis = synthesizeMagiResponses(collected.responses, {
|
|
4842
5026
|
replicasExpected: replicaTaskIds.length,
|
|
4843
5027
|
requireIndependentEvidence
|
|
4844
5028
|
});
|
|
5029
|
+
const freeformBanner = taskKind === "freeform" ? "task_kind=freeform: answers are unstructured natural language; cross-verification is WEAK (no claim clustering / independence scoring). Treat the collected answers as parallel opinions, not a verified consensus." : null;
|
|
4845
5030
|
persistMagiSynthesis(ctx, {
|
|
4846
5031
|
consensusGroupId,
|
|
4847
5032
|
missionId: readString(replicaTasks[0]?.missionId),
|
|
@@ -4851,6 +5036,7 @@ async function meshMagiCollect(ctx, args) {
|
|
|
4851
5036
|
return JSON.stringify({
|
|
4852
5037
|
success: true,
|
|
4853
5038
|
consensusGroupId,
|
|
5039
|
+
taskKind,
|
|
4854
5040
|
replicaCount: replicaTaskIds.length,
|
|
4855
5041
|
waited: wait,
|
|
4856
5042
|
collection: {
|
|
@@ -4860,8 +5046,10 @@ async function meshMagiCollect(ctx, args) {
|
|
|
4860
5046
|
missing: synthesis.replicasMissing,
|
|
4861
5047
|
staleReplicas: collected.staleCount,
|
|
4862
5048
|
...collected.staleCount > 0 ? { staleNote: `${collected.staleCount} replica(s) were detected STALE \u2014 assigned to a node/session no longer present in the live mesh.` } : {},
|
|
5049
|
+
...collected.retriedCount > 0 ? { retriedReplicas: collected.retriedCount, retryNote: `${collected.retriedCount} replica(s) failed the ${taskKind} schema and were sent one delta re-request for a corrected single-JSON answer.` } : {},
|
|
4863
5050
|
...!collected.terminal ? { pendingNote: "Not all replicas are terminal yet \u2014 this is a partial snapshot. Re-collect once mission/pendingCoordinatorEvents report more completions." } : {}
|
|
4864
5051
|
},
|
|
5052
|
+
...freeformBanner ? { freeformBanner } : {},
|
|
4865
5053
|
synthesis
|
|
4866
5054
|
}, null, 2);
|
|
4867
5055
|
}
|
|
@@ -4896,12 +5084,29 @@ function persistMagiDispatched(ctx, args) {
|
|
|
4896
5084
|
...args.missionId ? { missionId: args.missionId } : {},
|
|
4897
5085
|
...args.panel ? { panel: args.panel } : {},
|
|
4898
5086
|
...args.question ? { question: args.question.slice(0, 300) } : {},
|
|
4899
|
-
replicaCount: args.replicaCount
|
|
5087
|
+
replicaCount: args.replicaCount,
|
|
5088
|
+
// MAGI-REDESIGN: persist the task_kind so a later mesh_magi_collect
|
|
5089
|
+
// (which rediscovers replicas from the queue, not the original call)
|
|
5090
|
+
// re-derives the right schema parser for this group.
|
|
5091
|
+
...args.taskKind ? { taskKind: args.taskKind } : {}
|
|
4900
5092
|
}
|
|
4901
5093
|
});
|
|
4902
5094
|
} catch {
|
|
4903
5095
|
}
|
|
4904
5096
|
}
|
|
5097
|
+
function recoverMagiTaskKind(ctx, consensusGroupId) {
|
|
5098
|
+
try {
|
|
5099
|
+
const entries = (0, import_daemon_core4.readLedgerEntries)(ctx.mesh.id, { kind: ["magi_dispatched"], tail: 200 });
|
|
5100
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
5101
|
+
const payload = entries[i]?.payload;
|
|
5102
|
+
if (!payload || typeof payload !== "object") continue;
|
|
5103
|
+
if (readString(payload.consensusGroupId) !== consensusGroupId) continue;
|
|
5104
|
+
return normalizeMagiTaskKind(payload.taskKind);
|
|
5105
|
+
}
|
|
5106
|
+
} catch {
|
|
5107
|
+
}
|
|
5108
|
+
return DEFAULT_TASK_KIND;
|
|
5109
|
+
}
|
|
4905
5110
|
function persistMagiSynthesis(ctx, args) {
|
|
4906
5111
|
try {
|
|
4907
5112
|
(0, import_daemon_core4.appendLedgerEntry)(ctx.mesh.id, {
|
|
@@ -4935,7 +5140,9 @@ async function collectMagiResponses(ctx, args) {
|
|
|
4935
5140
|
const ids = new Set(args.replicaTaskIds);
|
|
4936
5141
|
const deadline = Date.now() + args.timeoutMs;
|
|
4937
5142
|
const TERMINAL = MAGI_TERMINAL_STATUSES;
|
|
5143
|
+
const kind = args.taskKind ?? DEFAULT_TASK_KIND;
|
|
4938
5144
|
const emptyResponse = () => ({ claims: [], top_findings: [], open_questions: [] });
|
|
5145
|
+
const retried = /* @__PURE__ */ new Set();
|
|
4939
5146
|
const finalized = /* @__PURE__ */ new Map();
|
|
4940
5147
|
const provisional = /* @__PURE__ */ new Map();
|
|
4941
5148
|
const buildSource = (task) => {
|
|
@@ -4949,6 +5156,42 @@ async function collectMagiResponses(ctx, args) {
|
|
|
4949
5156
|
...gitRef ? { git: gitRef } : {}
|
|
4950
5157
|
};
|
|
4951
5158
|
};
|
|
5159
|
+
const sendKindRetry = async (task, failReason) => {
|
|
5160
|
+
const node = ctx.mesh.nodes.find((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, task.assignedNodeId));
|
|
5161
|
+
if (!node || !task.assignedSessionId) return false;
|
|
5162
|
+
const why = failReason === "empty_evidence" ? "your previous answer had an empty evidence array" : failReason === "missing_required_fields" ? "your previous answer was missing required fields" : "your previous answer did not parse as the required JSON";
|
|
5163
|
+
const message = `Your previous MAGI answer could not be accepted (${why}). Respond NOW with ONLY a single JSON object (no prose, no code fence) matching EXACTLY this schema, with non-empty evidence:
|
|
5164
|
+
|
|
5165
|
+
${magiOutputContractFor(kind)}`;
|
|
5166
|
+
try {
|
|
5167
|
+
const coordinatorDaemonId = ctx.localDaemonId;
|
|
5168
|
+
await commandForNode(ctx, node, "agent_command", {
|
|
5169
|
+
targetSessionId: task.assignedSessionId,
|
|
5170
|
+
providerType: task.assignedProviderType,
|
|
5171
|
+
cliType: task.assignedProviderType,
|
|
5172
|
+
agentType: task.assignedProviderType,
|
|
5173
|
+
action: "send_chat",
|
|
5174
|
+
message,
|
|
5175
|
+
meshContext: {
|
|
5176
|
+
meshId: ctx.mesh.id,
|
|
5177
|
+
nodeId: task.assignedNodeId,
|
|
5178
|
+
taskId: task.id,
|
|
5179
|
+
...coordinatorDaemonId ? { coordinatorDaemonId } : {},
|
|
5180
|
+
...ctx.coordinatorSessionId ? { coordinatorSessionId: ctx.coordinatorSessionId } : {}
|
|
5181
|
+
}
|
|
5182
|
+
});
|
|
5183
|
+
try {
|
|
5184
|
+
(0, import_daemon_core4.appendLedgerEntry)(ctx.mesh.id, {
|
|
5185
|
+
kind: "magi_replica_retry",
|
|
5186
|
+
payload: { taskId: task.id, kind, failReason }
|
|
5187
|
+
});
|
|
5188
|
+
} catch {
|
|
5189
|
+
}
|
|
5190
|
+
return true;
|
|
5191
|
+
} catch {
|
|
5192
|
+
return false;
|
|
5193
|
+
}
|
|
5194
|
+
};
|
|
4952
5195
|
const tryResolveReplica = async (task, staleTaskIds2, staleReasons2, force) => {
|
|
4953
5196
|
const taskId = task.id;
|
|
4954
5197
|
const source = buildSource(task);
|
|
@@ -4971,7 +5214,7 @@ async function collectMagiResponses(ctx, args) {
|
|
|
4971
5214
|
}
|
|
4972
5215
|
return false;
|
|
4973
5216
|
}
|
|
4974
|
-
let
|
|
5217
|
+
let kindResult;
|
|
4975
5218
|
try {
|
|
4976
5219
|
const node = ctx.mesh.nodes.find((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, task.assignedNodeId));
|
|
4977
5220
|
if (!node) throw new Error("assigned node not in mesh");
|
|
@@ -4982,7 +5225,7 @@ async function collectMagiResponses(ctx, args) {
|
|
|
4982
5225
|
tailLimit: 6
|
|
4983
5226
|
});
|
|
4984
5227
|
const payload = unwrapCommandPayload(result);
|
|
4985
|
-
|
|
5228
|
+
kindResult = parseFirstMagiCandidateForKind(payload, kind, {
|
|
4986
5229
|
sessionId: task.assignedSessionId
|
|
4987
5230
|
});
|
|
4988
5231
|
} catch (e) {
|
|
@@ -4993,22 +5236,28 @@ async function collectMagiResponses(ctx, args) {
|
|
|
4993
5236
|
}
|
|
4994
5237
|
return false;
|
|
4995
5238
|
}
|
|
4996
|
-
if (
|
|
5239
|
+
if (kindResult.ok && kindResult.response) {
|
|
4997
5240
|
const weak = replicaCompletionIsWeak(ctx.mesh.id, taskId);
|
|
4998
5241
|
if (weak && !force) {
|
|
4999
|
-
provisional.set(taskId, { source: { ...source, ok: true }, response:
|
|
5242
|
+
provisional.set(taskId, { source: { ...source, ok: true }, response: kindResult.response });
|
|
5000
5243
|
return false;
|
|
5001
5244
|
}
|
|
5002
|
-
finalized.set(taskId, { source: { ...source, ok: true }, response:
|
|
5245
|
+
finalized.set(taskId, { source: { ...source, ok: true }, response: kindResult.response });
|
|
5003
5246
|
return true;
|
|
5004
5247
|
}
|
|
5248
|
+
const isSchemaFailure = kindResult.failReason === "missing_required_fields" || kindResult.failReason === "empty_evidence";
|
|
5249
|
+
if (isSchemaFailure && !retried.has(taskId) && !force) {
|
|
5250
|
+
retried.add(taskId);
|
|
5251
|
+
const sent = await sendKindRetry(task, kindResult.failReason);
|
|
5252
|
+
if (sent) return false;
|
|
5253
|
+
}
|
|
5005
5254
|
if (force) {
|
|
5006
5255
|
const prov = provisional.get(taskId);
|
|
5007
5256
|
if (prov) {
|
|
5008
5257
|
finalized.set(taskId, prov);
|
|
5009
5258
|
return true;
|
|
5010
5259
|
}
|
|
5011
|
-
source.error = "unparseable_output";
|
|
5260
|
+
source.error = isSchemaFailure ? `schema_invalid: ${kindResult.failReason}` : "unparseable_output";
|
|
5012
5261
|
finalized.set(taskId, { source, response: emptyResponse() });
|
|
5013
5262
|
return true;
|
|
5014
5263
|
}
|
|
@@ -5044,7 +5293,7 @@ async function collectMagiResponses(ctx, args) {
|
|
|
5044
5293
|
const responses = args.replicaTaskIds.map((id) => finalized.get(id)).filter((r) => !!r);
|
|
5045
5294
|
const terminal = presentIds.size === ids.size && finalTasks.every((t) => TERMINAL.has(String(t.status)));
|
|
5046
5295
|
const staleCount = responses.filter((r) => r.source.stale === true).length;
|
|
5047
|
-
return { responses, terminal, timedOut: !terminal, staleCount };
|
|
5296
|
+
return { responses, terminal, timedOut: !terminal, staleCount, retriedCount: retried.size };
|
|
5048
5297
|
}
|
|
5049
5298
|
|
|
5050
5299
|
// src/tools/mesh-tools-session.ts
|