@mrrlin-dev/mcp 0.4.16 → 0.4.18
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/bin.cjs +243 -32
- package/package.json +3 -3
package/dist/bin.cjs
CHANGED
|
@@ -41623,7 +41623,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41623
41623
|
}
|
|
41624
41624
|
|
|
41625
41625
|
// src/_generated/version.ts
|
|
41626
|
-
var PKG_VERSION = "0.4.
|
|
41626
|
+
var PKG_VERSION = "0.4.18";
|
|
41627
41627
|
|
|
41628
41628
|
// src/api-base-url-migrate.ts
|
|
41629
41629
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -53688,6 +53688,29 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
53688
53688
|
" for the API by hand.",
|
|
53689
53689
|
"- Do not spend turns re-reading state you just wrote.",
|
|
53690
53690
|
"",
|
|
53691
|
+
"Checking a decision you are unsure about:",
|
|
53692
|
+
"- `run_decision_consensus` reviews a DECISION before you act on it, not a document. You pass the",
|
|
53693
|
+
" question and the position you lean toward, and it tries to break that position. Calling it is",
|
|
53694
|
+
" your judgement: nothing requires it, and no action of yours is blocked for lacking it.",
|
|
53695
|
+
"- It is blind on purpose. Reviewers see only what you put in `question`, `position` and `context`",
|
|
53696
|
+
" \u2014 never this conversation and never your reasoning \u2014 so anything you leave out is invisible to",
|
|
53697
|
+
" them. Put the facts they need into `context` in your own words.",
|
|
53698
|
+
"- Reviewers are EXPECTED, not exotic, whenever any of these holds:",
|
|
53699
|
+
" (a) the decision is hard to reverse \u2014 a DB schema or migration, a published interface, a merge,",
|
|
53700
|
+
" a deploy, deleting data;",
|
|
53701
|
+
" (b) two readings of the facts both fit and you are choosing one;",
|
|
53702
|
+
" (c) the blind check objects and you still believe your position \u2014 disagreeing with your own",
|
|
53703
|
+
" check is exactly the moment an outside voice is worth having;",
|
|
53704
|
+
" (d) the operator says they are unsure, or asks you to check with someone else.",
|
|
53705
|
+
" In those cases pass `reviewers` between 1 and 3. Quota and latency are NOT a reason to skip",
|
|
53706
|
+
" reviewers on a decision that meets one of those conditions.",
|
|
53707
|
+
"- Read what came back instead of assuming it. The result reports how many of the requested",
|
|
53708
|
+
" reviewers actually answered, and two reviewer ids that differ only by a trailing digit, or that",
|
|
53709
|
+
" name the same model, are ONE opinion \u2014 not two that agree.",
|
|
53710
|
+
"- An objection is something you report, never something you route around. State the objection in",
|
|
53711
|
+
" plain language and let the operator decide. Do not quietly adopt a different answer, and do not",
|
|
53712
|
+
" drop the objection because you disagree with it.",
|
|
53713
|
+
"",
|
|
53691
53714
|
"Turn lifecycle \u2014 what you can and cannot do between turns:",
|
|
53692
53715
|
"- Your turn ends the moment you stop producing output. You CANNOT start a new turn yourself:",
|
|
53693
53716
|
" no timer and no scheduler wakes you up. Only a new operator message starts the next turn.",
|
|
@@ -55495,6 +55518,7 @@ function checkTelemetryDefaultProject(env, warn) {
|
|
|
55495
55518
|
}
|
|
55496
55519
|
async function startDirectorBridge() {
|
|
55497
55520
|
const config2 = parseConfig();
|
|
55521
|
+
let telemetryPipelineRunning = false;
|
|
55498
55522
|
checkTelemetryDefaultProject(process.env, (m) => console.error(m));
|
|
55499
55523
|
const turnLock = createTurnLock();
|
|
55500
55524
|
let lastTurnEndedAt = Date.now();
|
|
@@ -56174,42 +56198,54 @@ async function startDirectorBridge() {
|
|
|
56174
56198
|
});
|
|
56175
56199
|
}
|
|
56176
56200
|
}
|
|
56177
|
-
|
|
56178
|
-
|
|
56179
|
-
|
|
56180
|
-
|
|
56181
|
-
|
|
56182
|
-
|
|
56183
|
-
|
|
56184
|
-
|
|
56185
|
-
|
|
56186
|
-
|
|
56187
|
-
|
|
56188
|
-
|
|
56189
|
-
|
|
56190
|
-
|
|
56191
|
-
|
|
56192
|
-
|
|
56193
|
-
|
|
56201
|
+
if (telemetryPipelineRunning) {
|
|
56202
|
+
bridgeLogger.logOut({
|
|
56203
|
+
spanId: "gc",
|
|
56204
|
+
sessionId: null,
|
|
56205
|
+
type: "<telemetry-pipeline-skipped>",
|
|
56206
|
+
payload: { reason: "previous run still in flight" }
|
|
56207
|
+
});
|
|
56208
|
+
} else {
|
|
56209
|
+
telemetryPipelineRunning = true;
|
|
56210
|
+
void (async () => {
|
|
56211
|
+
try {
|
|
56212
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
56213
|
+
await foldClosedDays(config2.stateDir, today);
|
|
56214
|
+
await uploadPendingDays(config2.stateDir, client, bridgeHealth, {
|
|
56215
|
+
cwd: config2.codexCwd ?? process.cwd(),
|
|
56216
|
+
codexExecutable: config2.codexExecutable,
|
|
56217
|
+
timeoutMs: readEnvInt("MRRLIN_TELEMETRY_GATE_TIMEOUT_SECONDS", 60) * 1e3
|
|
56218
|
+
});
|
|
56219
|
+
const pruneResult = await prune(config2.stateDir, {
|
|
56220
|
+
ageDays: readEnvInt("MRRLIN_LOG_RETENTION_AGE_DAYS", 30),
|
|
56221
|
+
budgetMb: readEnvInt("MRRLIN_LOG_RETENTION_BUDGET_MB", 1024),
|
|
56222
|
+
// The reconstruction window's day-files are off limits. Derived from the thread
|
|
56223
|
+
// TTL so the two numbers cannot drift apart.
|
|
56224
|
+
protectedSinceMs: Date.now() - config2.idleThreadTtlMs
|
|
56225
|
+
});
|
|
56226
|
+
if (pruneResult.budgetOvershootBytes > 0) {
|
|
56227
|
+
bridgeLogger.logOut({
|
|
56228
|
+
spanId: "gc",
|
|
56229
|
+
sessionId: null,
|
|
56230
|
+
type: "<log-budget-overshoot>",
|
|
56231
|
+
payload: {
|
|
56232
|
+
overshootBytes: pruneResult.budgetOvershootBytes,
|
|
56233
|
+
reason: "protected re-seed window not pruned"
|
|
56234
|
+
}
|
|
56235
|
+
});
|
|
56236
|
+
}
|
|
56237
|
+
} catch (error51) {
|
|
56194
56238
|
bridgeLogger.logOut({
|
|
56195
56239
|
spanId: "gc",
|
|
56196
56240
|
sessionId: null,
|
|
56197
|
-
type: "<
|
|
56198
|
-
payload: {
|
|
56199
|
-
overshootBytes: pruneResult.budgetOvershootBytes,
|
|
56200
|
-
reason: "protected re-seed window not pruned"
|
|
56201
|
-
}
|
|
56241
|
+
type: "<telemetry-pipeline-error>",
|
|
56242
|
+
payload: { error: error51 instanceof Error ? error51.message : String(error51) }
|
|
56202
56243
|
});
|
|
56244
|
+
} finally {
|
|
56245
|
+
telemetryPipelineRunning = false;
|
|
56203
56246
|
}
|
|
56204
|
-
}
|
|
56205
|
-
|
|
56206
|
-
spanId: "gc",
|
|
56207
|
-
sessionId: null,
|
|
56208
|
-
type: "<telemetry-pipeline-error>",
|
|
56209
|
-
payload: { error: error51 instanceof Error ? error51.message : String(error51) }
|
|
56210
|
-
});
|
|
56211
|
-
}
|
|
56212
|
-
})();
|
|
56247
|
+
})();
|
|
56248
|
+
}
|
|
56213
56249
|
scheduleClaimLoop();
|
|
56214
56250
|
}, claimIntervalMs());
|
|
56215
56251
|
claimTimer.unref?.();
|
|
@@ -60533,6 +60569,146 @@ async function resolveActiveRunHead(input, deps) {
|
|
|
60533
60569
|
}
|
|
60534
60570
|
}
|
|
60535
60571
|
|
|
60572
|
+
// src/decision-consensus.ts
|
|
60573
|
+
var ANCHOR_TIMEOUT_MS = Number(process.env["MRRLIN_DECISION_TIMEOUT_MS"] ?? 9e4);
|
|
60574
|
+
var PICK_TIMEOUT_MS3 = 15e3;
|
|
60575
|
+
var DISPATCH_TIMEOUT_MS3 = 6e4;
|
|
60576
|
+
function buildReviewArtifact(input) {
|
|
60577
|
+
const blocks = [
|
|
60578
|
+
`--- QUESTION ---
|
|
60579
|
+
${input.question}`,
|
|
60580
|
+
`--- POSITION UNDER REVIEW ---
|
|
60581
|
+
${input.position}`
|
|
60582
|
+
];
|
|
60583
|
+
if (input.context !== void 0 && input.context.trim() !== "") {
|
|
60584
|
+
blocks.push(`--- CONTEXT ---
|
|
60585
|
+
${input.context}`);
|
|
60586
|
+
}
|
|
60587
|
+
return blocks.join("\n\n");
|
|
60588
|
+
}
|
|
60589
|
+
var LADDER = ["architect", "plan-reviewer", "scope-analyst"];
|
|
60590
|
+
var MAX_ISSUES = 3;
|
|
60591
|
+
var MAX_ISSUE_TEXT = 200;
|
|
60592
|
+
function boundIssues(issues) {
|
|
60593
|
+
return issues.slice(0, MAX_ISSUES).map(
|
|
60594
|
+
(issue2) => issue2.text.length > MAX_ISSUE_TEXT ? { category: issue2.category, text: `${issue2.text.slice(0, MAX_ISSUE_TEXT)}\u2026` } : issue2
|
|
60595
|
+
);
|
|
60596
|
+
}
|
|
60597
|
+
async function runDecisionConsensus(input, deps) {
|
|
60598
|
+
const runExec = deps.runExec ?? runAgentExec;
|
|
60599
|
+
const buildRunner = deps.buildRunner ?? buildMultiHeadRunner;
|
|
60600
|
+
const persona = deps.loadPersona ?? loadPersona;
|
|
60601
|
+
const chosen = input.heads && input.heads.length > 0 ? input.heads : LADDER;
|
|
60602
|
+
const firstChosen = chosen[0];
|
|
60603
|
+
const anchorPersona = firstChosen === void 0 ? LADDER[0] : firstChosen;
|
|
60604
|
+
const seatedPersonas = input.reviewers === 0 ? [] : Array.from({ length: input.reviewers }, (_, i) => chosen[i % chosen.length] ?? anchorPersona);
|
|
60605
|
+
const artifact = buildReviewArtifact(input);
|
|
60606
|
+
const anchorLeg = async () => {
|
|
60607
|
+
const result = await runExec(
|
|
60608
|
+
{
|
|
60609
|
+
prompt: artifact,
|
|
60610
|
+
developerInstructions: persona(anchorPersona),
|
|
60611
|
+
cwd: input.cwd,
|
|
60612
|
+
timeoutMs: ANCHOR_TIMEOUT_MS,
|
|
60613
|
+
sandbox: "read-only",
|
|
60614
|
+
codexExecutable: process.env["MRRLIN_DIRECTOR_BRIDGE_CODEX_BIN"]?.trim() || void 0
|
|
60615
|
+
},
|
|
60616
|
+
void 0,
|
|
60617
|
+
deps.persistedProvider
|
|
60618
|
+
);
|
|
60619
|
+
if (!result.ok) return { unavailable: result.error };
|
|
60620
|
+
const parsed = parseVerdict(result.text);
|
|
60621
|
+
return { persona: anchorPersona, verdict: parsed.verdict, issues: boundIssues(parsed.issues) };
|
|
60622
|
+
};
|
|
60623
|
+
const panelLeg = async () => {
|
|
60624
|
+
if (input.reviewers === 0) return null;
|
|
60625
|
+
const seat = await deps.mintSeat();
|
|
60626
|
+
const personaInstructions = {};
|
|
60627
|
+
for (const name of seatedPersonas) {
|
|
60628
|
+
try {
|
|
60629
|
+
personaInstructions[name] = persona(name);
|
|
60630
|
+
} catch {
|
|
60631
|
+
personaInstructions[name] = "";
|
|
60632
|
+
}
|
|
60633
|
+
}
|
|
60634
|
+
const built = await buildRunner({
|
|
60635
|
+
personas: seatedPersonas,
|
|
60636
|
+
personaInstructions,
|
|
60637
|
+
excludeProvider: seat.capabilities.poolProviderLabels.join(","),
|
|
60638
|
+
tierPrefer: "strong",
|
|
60639
|
+
pickTimeoutMs: PICK_TIMEOUT_MS3,
|
|
60640
|
+
dispatchTimeoutMs: DISPATCH_TIMEOUT_MS3,
|
|
60641
|
+
seat
|
|
60642
|
+
});
|
|
60643
|
+
if (!built.ok) return { ok: false, error: built.error };
|
|
60644
|
+
if (built.runner.agents.length === 0) {
|
|
60645
|
+
return { ok: false, error: "the reviewer pool returned an empty panel" };
|
|
60646
|
+
}
|
|
60647
|
+
const responses = await Promise.all(
|
|
60648
|
+
seatedPersonas.map(async (personaName) => {
|
|
60649
|
+
const agentId = built.runner.assignment[personaName] ?? "";
|
|
60650
|
+
const result = await built.runner.runHead(personaName, artifact, "");
|
|
60651
|
+
return { personaName, agentId, result };
|
|
60652
|
+
})
|
|
60653
|
+
);
|
|
60654
|
+
const seats = [];
|
|
60655
|
+
for (const response of responses) {
|
|
60656
|
+
if (!response.result.ok) continue;
|
|
60657
|
+
const parsed = parseVerdict(response.result.text);
|
|
60658
|
+
seats.push({
|
|
60659
|
+
persona: response.personaName,
|
|
60660
|
+
agentId: response.agentId,
|
|
60661
|
+
verdict: parsed.verdict,
|
|
60662
|
+
issues: boundIssues(parsed.issues)
|
|
60663
|
+
});
|
|
60664
|
+
}
|
|
60665
|
+
return { ok: true, agents: built.runner.agents, seats };
|
|
60666
|
+
};
|
|
60667
|
+
const [anchor, panel] = await Promise.all([anchorLeg(), panelLeg()]);
|
|
60668
|
+
if (panel === null) return { anchor, panel: null, degraded: null };
|
|
60669
|
+
if (!panel.ok) {
|
|
60670
|
+
return {
|
|
60671
|
+
anchor,
|
|
60672
|
+
panel: {
|
|
60673
|
+
requested: input.reviewers,
|
|
60674
|
+
filled: 0,
|
|
60675
|
+
agentIds: [],
|
|
60676
|
+
underpowered: true,
|
|
60677
|
+
responded: 0,
|
|
60678
|
+
seats: []
|
|
60679
|
+
},
|
|
60680
|
+
degraded: `the reviewer pool could not be seated (${panel.error}); this review is the blind check alone`
|
|
60681
|
+
};
|
|
60682
|
+
}
|
|
60683
|
+
const filled = panel.agents.length;
|
|
60684
|
+
const responded = panel.seats.length;
|
|
60685
|
+
return {
|
|
60686
|
+
anchor,
|
|
60687
|
+
// D6: the seated identities are reported verbatim and no "distinct voices" number is computed.
|
|
60688
|
+
// Deriving one from id strings alone is a heuristic that will be wrong — `groq-gpt-oss-120b`,
|
|
60689
|
+
// `groq-gpt-oss-120b-2` and `ollama-gpt-oss-120b` are three ids and one model — so the reading
|
|
60690
|
+
// rule lives in the Director's instruction instead, where it can be stated honestly.
|
|
60691
|
+
panel: {
|
|
60692
|
+
requested: input.reviewers,
|
|
60693
|
+
filled,
|
|
60694
|
+
agentIds: panel.agents,
|
|
60695
|
+
underpowered: filled < input.reviewers,
|
|
60696
|
+
responded,
|
|
60697
|
+
seats: panel.seats
|
|
60698
|
+
},
|
|
60699
|
+
// `degraded` fires whenever FEWER voices answered than were asked for, not only at zero: asking
|
|
60700
|
+
// for three and hearing one is a thinner review than the Director requested, and it has to say
|
|
60701
|
+
// so rather than present one answer as the panel's. `underpowered` above stays the mechanical
|
|
60702
|
+
// seat count; this is the sentence the Director reads out.
|
|
60703
|
+
// Two halves, both load-bearing. `filled` is the honest measure of INDEPENDENCE: two personas
|
|
60704
|
+
// answering from one seated model are one voice wearing two hats, and reporting that as a full
|
|
60705
|
+
// panel is exactly the trap D6 exists to avoid. `responded` catches the other shape — seats
|
|
60706
|
+
// filled that then stayed silent. `underpowered` above is the mechanical flag; this is the
|
|
60707
|
+
// sentence the Director reads out.
|
|
60708
|
+
degraded: filled < input.reviewers || responded < input.reviewers ? `${filled} of ${input.reviewers} requested reviewers seated, ${responded} answered` + (responded === 0 ? "; this review is the blind check alone" : "") : null
|
|
60709
|
+
};
|
|
60710
|
+
}
|
|
60711
|
+
|
|
60536
60712
|
// src/async-tools/consensus.ts
|
|
60537
60713
|
var import_node_crypto11 = require("node:crypto");
|
|
60538
60714
|
|
|
@@ -61670,6 +61846,7 @@ var mcpToolNames = {
|
|
|
61670
61846
|
markSpecReady: "mark_spec_ready",
|
|
61671
61847
|
runSpecConsensus: "run_spec_consensus",
|
|
61672
61848
|
runCodeReviewerGate: "run_code_reviewer_gate",
|
|
61849
|
+
runDecisionConsensus: "run_decision_consensus",
|
|
61673
61850
|
uploadArtifact: "upload_artifact",
|
|
61674
61851
|
deliverFile: "deliver_file",
|
|
61675
61852
|
listArtifactFiles: "list_artifact_files",
|
|
@@ -62213,6 +62390,20 @@ var runCodeReviewerGateInputSchema = external_exports.object({
|
|
|
62213
62390
|
'Opt-in reviewer personas to add on top of the fixed Code Reviewer head. "security-analyst" \u2014 for sensitive diffs (auth, secrets, input handling, new endpoints); adds a threat-modeling reviewer. OFF by default.'
|
|
62214
62391
|
)
|
|
62215
62392
|
});
|
|
62393
|
+
var runDecisionConsensusInputSchema = external_exports.object({
|
|
62394
|
+
projectSlug: external_exports.string().min(1),
|
|
62395
|
+
question: external_exports.string().min(1).describe("The decision to be reviewed, as a question."),
|
|
62396
|
+
position: external_exports.string().min(1).describe("The answer you are leaning toward. Reviewers try to break THIS, so state it plainly."),
|
|
62397
|
+
context: external_exports.string().optional().describe(
|
|
62398
|
+
"Only the facts a reviewer needs, in your own words. Reviewers see nothing else \u2014 not this chat, not your reasoning \u2014 so anything you omit here is invisible to them. Keep it an excerpt, not a file dump."
|
|
62399
|
+
),
|
|
62400
|
+
reviewers: external_exports.number().int().min(0).max(3).default(0).describe(
|
|
62401
|
+
"How many independent reviewers to seat from the pool, on top of the blind check that always runs. 0 (default) contacts nobody outside this machine's own model."
|
|
62402
|
+
),
|
|
62403
|
+
heads: external_exports.array(external_exports.enum(["architect", "plan-reviewer", "scope-analyst", "security-analyst", "debugger", "researcher"])).min(1).max(3).optional().describe(
|
|
62404
|
+
'Which reviewer lenses to use. Omitted, the blind check uses "architect" and seats fill from architect / plan-reviewer / scope-analyst. Name one explicitly when the doubt has a shape \u2014 "security-analyst" for auth or secrets, "debugger" for a root-cause claim, "researcher" for a dependency or API behaviour claim.'
|
|
62405
|
+
)
|
|
62406
|
+
});
|
|
62216
62407
|
var searchKnowledgeInputSchema = wikiKnowledgeSearchInputSchema;
|
|
62217
62408
|
var createGithubBranchInputSchema = projectScopedSchema.merge(githubCreateBranchRequestSchema);
|
|
62218
62409
|
var createGithubPullRequestInputSchema = projectScopedSchema.merge(githubCreatePullRequestRequestSchema);
|
|
@@ -62354,6 +62545,7 @@ var mcpToolInputSchemas = {
|
|
|
62354
62545
|
[mcpToolNames.markSpecReady]: markSpecReadyInputSchema,
|
|
62355
62546
|
[mcpToolNames.runSpecConsensus]: runSpecConsensusInputSchema,
|
|
62356
62547
|
[mcpToolNames.runCodeReviewerGate]: runCodeReviewerGateInputSchema,
|
|
62548
|
+
[mcpToolNames.runDecisionConsensus]: runDecisionConsensusInputSchema,
|
|
62357
62549
|
[mcpToolNames.listInboxItems]: listInboxItemsInputSchema,
|
|
62358
62550
|
[mcpToolNames.createInboxItem]: createInboxItemInputSchema,
|
|
62359
62551
|
[mcpToolNames.decideInboxItem]: decideInboxItemInputSchema,
|
|
@@ -62479,6 +62671,7 @@ var mcpToolDescriptions = {
|
|
|
62479
62671
|
[mcpToolNames.markSpecReady]: "Mark a wiki page (folder=Specs) as ready (or not) for execution-run gating. The Director sets this true after validating the spec; any edit to the page's markdown auto-resets it to false.",
|
|
62480
62672
|
[mcpToolNames.runSpecConsensus]: 'Enqueue the consensus review gate over a Specs/{id} page. Returns IMMEDIATELY with one of: already_converged (a CONVERGED review already exists for the current content \u2014 proceed straight to create_execution_run, do NOT call mark_spec_ready, it is already set), already_reviewed (a terminal CONSENSUS_BLOCKED for this content), already_running (a consensus job is in flight), or queued (a new job was launched). The review runs in the background; its result/blocker arrives in the inbox (info on CONVERGED, handoff on failure) and is surfaced into the open chat. Do NOT call again for the same spec and do NOT poll. On queued/already_running, tell the user it was launched and end the turn. Pass extraHeads: ["researcher"] for specs referencing external libraries/APIs, or ["debugger"] for root-cause/investigation specs to add opt-in specialist reviewers on top of the fixed four. For a NON-code task (domain seo/marketing/content, ADR 0020): the low-risk lanes `seo`, `marketing`, and `content` proceed to `create_execution_run` after consensus, using the same flow as code tasks. When the review agrees, tell the operator "the reviewers agreed on the spec" and proceed to create_execution_run. When it does not, tell them the reviewers could not agree and that there is a decision question in the inbox. Never say "consensus", "converged", or "CONVERGED" to the operator; those are internal words.',
|
|
62481
62673
|
[mcpToolNames.runCodeReviewerGate]: `Review the current branch's code diff (vs baseRef) with a Code Reviewer gate \u2014 independent reviewers iterate review\u2192fix\u2192re-check (checkpoint, scope-limited reviser edits, typecheck/build fast-gate, rollback) until the diff converges or is blocked. Run before opening a PR. Pass extraHeads: ["security-analyst"] for sensitive diffs (auth, secrets, input handling, new endpoints).`,
|
|
62674
|
+
[mcpToolNames.runDecisionConsensus]: 'Check a DECISION you are about to act on, before you act on it \u2014 not a document. Where run_spec_consensus reviews a spec page and run_code_reviewer_gate reviews a branch diff, this one reviews a judgement: "new API route or widen the existing accessor", "does this need a DB change", "is this task ready to start". Pass the `question` and the `position` you are leaning toward; the review tries to break that position. It is BLIND: reviewers see only `question`, `position` and `context` \u2014 never this conversation and never your reasoning \u2014 so put the facts they need into `context` in your own words, and expect anything you leave out to be invisible to them. With `reviewers` omitted (the default) the check runs entirely on this machine\'s own model: nothing is dispatched, no external provider is contacted, no quota is spent. Set `reviewers` to 1-3 to also seat that many independent reviewers from the pool for the same question; they run once, in parallel, and are never re-asked. A seated reviewer can time out or be unavailable, in which case the result reports how many of the requested reviewers actually answered and the check falls back to the blind pass alone \u2014 read that number rather than assuming a full panel, and treat two reviewer ids that differ only by a trailing digit, or that name the same model, as ONE opinion rather than two agreeing ones. When the result objects to your position, do not quietly adopt a different answer and do not drop the objection: tell the operator what the objection is, in plain language, and let them decide. Never say "consensus", "converged", "anchor", or a verdict code like APPROVE or REQUEST_CHANGES to the operator; those are internal words.',
|
|
62482
62675
|
[mcpToolNames.appendExecutionArtifact]: "Append a durable artifact (plan, checkpoint, command output, test result, diff summary, self-review, error, deploy attempt, or handoff judgement) to an execution run.",
|
|
62483
62676
|
[mcpToolNames.recordProducedUrl]: 'Call this after publishing each web page produced by the task, so the UI link opens the page itself instead of the deployed site root. Pass the page\'s full https URL (for external pages, e.g. "https://dev.to/<slug>") OR a path starting with "/" for the deploy environment (e.g. "/blog/<slug>") \u2014 paths are resolved against the run\'s deployed env URL at render time. Optional `label` (\u226440 chars) is shown next to the link; omit and the UI defaults to the page title. Call once per page; multiple calls dedupe by resolved URL in the UI.',
|
|
62484
62677
|
[mcpToolNames.updateTaskState]: "Record where this task actually stands, in your own words, BEFORE you end your turn. This is the handoff the next run reads: it survives even when this conversation does not, because a thread is retired once it has accumulated too many runs and the next run then starts from this record plus the repository. Report what only you know \u2014 the goal as you now understand it, decisions you made AND WHY (a decision without its reason is the thing most often lost), problems still open, and the next concrete actions. Omit any field you learned nothing new about; an omitted field keeps its previous value rather than being cleared. Use one entry per line for decisions/next actions. Do not restate the spec or the file list \u2014 the next run gets those anyway.",
|
|
@@ -63075,6 +63268,24 @@ function createMrrlinTools(options) {
|
|
|
63075
63268
|
return result;
|
|
63076
63269
|
}
|
|
63077
63270
|
),
|
|
63271
|
+
[mcpToolNames.runDecisionConsensus]: makeTool(
|
|
63272
|
+
client,
|
|
63273
|
+
mcpToolNames.runDecisionConsensus,
|
|
63274
|
+
"DECISION_CONSENSUS_FAILED",
|
|
63275
|
+
"Unable to run the decision review.",
|
|
63276
|
+
// No client call: this review reads and writes no Mrrlin state. `projectSlug` is accepted for
|
|
63277
|
+
// signature consistency with every other Director tool (see design.md D4a), not consumed here.
|
|
63278
|
+
// tools.ts is an allowed provider mint site (eslint.config.mjs); decision-consensus.ts is
|
|
63279
|
+
// deliberately not, so the seat and the persisted provider are minted HERE and threaded down.
|
|
63280
|
+
// mintSeat stays a thunk so the anchor-only path never mints a seat it has no use for.
|
|
63281
|
+
async (_c, input) => runDecisionConsensus(
|
|
63282
|
+
{ ...input, cwd: process.cwd() },
|
|
63283
|
+
{
|
|
63284
|
+
persistedProvider: getSelectedProvider(),
|
|
63285
|
+
mintSeat: () => mintResolvedSeat(resolveProvider(process.env, getSelectedProvider()))
|
|
63286
|
+
}
|
|
63287
|
+
)
|
|
63288
|
+
),
|
|
63078
63289
|
[mcpToolNames.runCodeReviewerGate]: makeTool(
|
|
63079
63290
|
client,
|
|
63080
63291
|
mcpToolNames.runCodeReviewerGate,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
"esbuild": "^0.24.0",
|
|
23
23
|
"tsx": "^4.22.3",
|
|
24
24
|
"@mrrlin/agent-client": "0.0.0",
|
|
25
|
+
"@mrrlin/director-e2e": "0.0.0",
|
|
25
26
|
"@mrrlin/schemas": "0.0.0",
|
|
26
|
-
"@mrrlin/client": "0.0.0",
|
|
27
27
|
"@mrrlin/tsconfig": "0.0.0",
|
|
28
28
|
"@mrrlin/wiki": "0.0.0",
|
|
29
|
-
"@mrrlin/
|
|
29
|
+
"@mrrlin/client": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|