@mrrlin-dev/mcp 0.4.2 → 0.4.4
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 +302 -174
- package/dist/consensus/personas/findings-writer.md +20 -0
- package/package.json +5 -5
package/dist/bin.cjs
CHANGED
|
@@ -4273,7 +4273,7 @@ var require_websocket = __commonJS({
|
|
|
4273
4273
|
var http2 = require("http");
|
|
4274
4274
|
var net = require("net");
|
|
4275
4275
|
var tls = require("tls");
|
|
4276
|
-
var { randomBytes: randomBytes4, createHash:
|
|
4276
|
+
var { randomBytes: randomBytes4, createHash: createHash4 } = require("crypto");
|
|
4277
4277
|
var { Duplex, Readable } = require("stream");
|
|
4278
4278
|
var { URL: URL2 } = require("url");
|
|
4279
4279
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -4941,7 +4941,7 @@ var require_websocket = __commonJS({
|
|
|
4941
4941
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
4942
4942
|
return;
|
|
4943
4943
|
}
|
|
4944
|
-
const digest =
|
|
4944
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
4945
4945
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
4946
4946
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
4947
4947
|
return;
|
|
@@ -5310,7 +5310,7 @@ var require_websocket_server = __commonJS({
|
|
|
5310
5310
|
var EventEmitter3 = require("events");
|
|
5311
5311
|
var http2 = require("http");
|
|
5312
5312
|
var { Duplex } = require("stream");
|
|
5313
|
-
var { createHash:
|
|
5313
|
+
var { createHash: createHash4 } = require("crypto");
|
|
5314
5314
|
var extension2 = require_extension();
|
|
5315
5315
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
5316
5316
|
var subprotocol2 = require_subprotocol();
|
|
@@ -5617,7 +5617,7 @@ var require_websocket_server = __commonJS({
|
|
|
5617
5617
|
);
|
|
5618
5618
|
}
|
|
5619
5619
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
5620
|
-
const digest =
|
|
5620
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
5621
5621
|
const headers = [
|
|
5622
5622
|
"HTTP/1.1 101 Switching Protocols",
|
|
5623
5623
|
"Upgrade: websocket",
|
|
@@ -21174,7 +21174,7 @@ var import_node_http = __toESM(require("node:http"), 1);
|
|
|
21174
21174
|
var import_node_os9 = __toESM(require("node:os"), 1);
|
|
21175
21175
|
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
21176
21176
|
var import_node_child_process11 = require("node:child_process");
|
|
21177
|
-
var
|
|
21177
|
+
var import_node_crypto7 = __toESM(require("node:crypto"), 1);
|
|
21178
21178
|
|
|
21179
21179
|
// src/turn-replay-buffer.ts
|
|
21180
21180
|
var DEFAULT_MAX_EVENTS = 2e3;
|
|
@@ -36574,6 +36574,8 @@ var agentPoolResponseSchema = external_exports.object({
|
|
|
36574
36574
|
|
|
36575
36575
|
// ../../packages/schemas/dist/specConsensusReview.js
|
|
36576
36576
|
var mrrlinSpecConsensusSuppressionEvidenceSchema = external_exports.record(external_exports.string(), external_exports.unknown()).nullable();
|
|
36577
|
+
var mrrlinSpecConsensusHistorySchema = external_exports.unknown();
|
|
36578
|
+
var mrrlinSpecConsensusHighlightsSchema = external_exports.object({ findings: external_exports.array(external_exports.string()) });
|
|
36577
36579
|
var mrrlinSpecConsensusReviewSchema = external_exports.object({
|
|
36578
36580
|
projectSlug: external_exports.string().min(1),
|
|
36579
36581
|
specPageId: external_exports.string().min(1),
|
|
@@ -36581,6 +36583,8 @@ var mrrlinSpecConsensusReviewSchema = external_exports.object({
|
|
|
36581
36583
|
outcome: external_exports.string().min(1),
|
|
36582
36584
|
rounds: external_exports.number().int().nonnegative(),
|
|
36583
36585
|
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.default(null),
|
|
36586
|
+
history: mrrlinSpecConsensusHistorySchema.nullable().default(null),
|
|
36587
|
+
highlights: mrrlinSpecConsensusHighlightsSchema.nullable().default(null),
|
|
36584
36588
|
createdAt: external_exports.string().datetime()
|
|
36585
36589
|
});
|
|
36586
36590
|
var mrrlinSpecConsensusReviewCreateSchema = external_exports.object({
|
|
@@ -36588,10 +36592,26 @@ var mrrlinSpecConsensusReviewCreateSchema = external_exports.object({
|
|
|
36588
36592
|
contentHash: external_exports.string().min(1),
|
|
36589
36593
|
outcome: external_exports.string().min(1),
|
|
36590
36594
|
rounds: external_exports.number().int().nonnegative(),
|
|
36591
|
-
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.optional()
|
|
36595
|
+
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.optional(),
|
|
36596
|
+
history: mrrlinSpecConsensusHistorySchema.optional(),
|
|
36597
|
+
highlights: mrrlinSpecConsensusHighlightsSchema.optional()
|
|
36592
36598
|
}).strict();
|
|
36593
36599
|
var specConsensusReviewResponseSchema = external_exports.object({ data: mrrlinSpecConsensusReviewSchema });
|
|
36594
36600
|
var specConsensusReviewMaybeResponseSchema = external_exports.object({ data: mrrlinSpecConsensusReviewSchema.nullable() });
|
|
36601
|
+
var PERSONA_ROLE_MAP = {
|
|
36602
|
+
architect: "design",
|
|
36603
|
+
"security-analyst": "security",
|
|
36604
|
+
"seo-analyst": "SEO",
|
|
36605
|
+
"compliance-reviewer": "compliance",
|
|
36606
|
+
debugger: "root cause",
|
|
36607
|
+
researcher: "research",
|
|
36608
|
+
"content-editor": "wording",
|
|
36609
|
+
"code-reviewer": "code",
|
|
36610
|
+
"content-author": "content",
|
|
36611
|
+
"seo-writer": "SEO writing",
|
|
36612
|
+
"spec-author": "spec"
|
|
36613
|
+
};
|
|
36614
|
+
var UNNAMED_PERSONAS = ["plan-reviewer", "scope-analyst"];
|
|
36595
36615
|
|
|
36596
36616
|
// ../../packages/schemas/dist/asyncJob.js
|
|
36597
36617
|
var ASYNC_JOB_STATUSES = ["queued", "running", "done", "failed", "superseded"];
|
|
@@ -40190,7 +40210,7 @@ function startCodexProbe(deps = {}) {
|
|
|
40190
40210
|
}
|
|
40191
40211
|
|
|
40192
40212
|
// src/_generated/version.ts
|
|
40193
|
-
var PKG_VERSION = "0.4.
|
|
40213
|
+
var PKG_VERSION = "0.4.4";
|
|
40194
40214
|
|
|
40195
40215
|
// src/api-base-url-migrate.ts
|
|
40196
40216
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
@@ -40992,7 +41012,7 @@ function startAgentPoolLoop(deps) {
|
|
|
40992
41012
|
|
|
40993
41013
|
// src/execution-consumer.ts
|
|
40994
41014
|
var import_node_child_process9 = require("node:child_process");
|
|
40995
|
-
var
|
|
41015
|
+
var import_node_crypto4 = __toESM(require("node:crypto"), 1);
|
|
40996
41016
|
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
40997
41017
|
var import_node_util = require("node:util");
|
|
40998
41018
|
|
|
@@ -41684,7 +41704,7 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41684
41704
|
});
|
|
41685
41705
|
if (result.outcome === "CONVERGED") {
|
|
41686
41706
|
await deps.promoteSpecReady(currentHash);
|
|
41687
|
-
return { outcome: "CONVERGED" };
|
|
41707
|
+
return { outcome: "CONVERGED", headOutcomes: result.history.at(-1)?.headOutcomes ?? [] };
|
|
41688
41708
|
}
|
|
41689
41709
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
41690
41710
|
return {
|
|
@@ -41734,6 +41754,63 @@ async function resolveSpecDomain(client, projectSlug, page) {
|
|
|
41734
41754
|
}
|
|
41735
41755
|
}
|
|
41736
41756
|
|
|
41757
|
+
// src/consensus/findings.ts
|
|
41758
|
+
function shouldCurateFindings(history) {
|
|
41759
|
+
return history.some((entry) => entry.accepted.length > 0);
|
|
41760
|
+
}
|
|
41761
|
+
var SYNTHESIZED_PLACEHOLDER = /^head .* returned .* but no parseable critical issues/;
|
|
41762
|
+
function selectFindingsCandidates(history) {
|
|
41763
|
+
const result = [];
|
|
41764
|
+
for (const entry of history) {
|
|
41765
|
+
for (const issue2 of entry.accepted) {
|
|
41766
|
+
if (SYNTHESIZED_PLACEHOLDER.test(issue2.text)) {
|
|
41767
|
+
continue;
|
|
41768
|
+
}
|
|
41769
|
+
result.push(issue2);
|
|
41770
|
+
}
|
|
41771
|
+
}
|
|
41772
|
+
if (result.length > 3) {
|
|
41773
|
+
return result.slice(0, 3);
|
|
41774
|
+
}
|
|
41775
|
+
return result;
|
|
41776
|
+
}
|
|
41777
|
+
function isFindingsHighlights(value) {
|
|
41778
|
+
return typeof value === "object" && value !== null && Array.isArray(value.findings) && value.findings.every((f) => typeof f === "string");
|
|
41779
|
+
}
|
|
41780
|
+
async function curateFindings(history, deps) {
|
|
41781
|
+
if (!shouldCurateFindings(history)) return null;
|
|
41782
|
+
try {
|
|
41783
|
+
const candidates = selectFindingsCandidates(history);
|
|
41784
|
+
const issuesBlock = candidates.length > 0 ? candidates.map((issue2) => `- (${issue2.category}) ${issue2.text}`).join("\n") : "(no issues survived the deterministic pre-filter)";
|
|
41785
|
+
const result = await deps.runCodexExec({
|
|
41786
|
+
prompt: `--- ACCEPTED ISSUES ---
|
|
41787
|
+
${issuesBlock}`,
|
|
41788
|
+
developerInstructions: deps.loadPersona("findings-writer"),
|
|
41789
|
+
cwd: deps.cwd,
|
|
41790
|
+
timeoutMs: deps.timeoutMs,
|
|
41791
|
+
sandbox: "read-only",
|
|
41792
|
+
codexExecutable: deps.codexExecutable
|
|
41793
|
+
});
|
|
41794
|
+
if (!result.ok) {
|
|
41795
|
+
process.stderr.write(`[mrrlin-mcp consensus] findings curation failed: ${result.error}
|
|
41796
|
+
`);
|
|
41797
|
+
return null;
|
|
41798
|
+
}
|
|
41799
|
+
const parsed = JSON.parse(result.text);
|
|
41800
|
+
if (!isFindingsHighlights(parsed)) {
|
|
41801
|
+
process.stderr.write("[mrrlin-mcp consensus] findings curation returned unparseable output\n");
|
|
41802
|
+
return null;
|
|
41803
|
+
}
|
|
41804
|
+
return { findings: parsed.findings.slice(0, 3) };
|
|
41805
|
+
} catch (err) {
|
|
41806
|
+
process.stderr.write(
|
|
41807
|
+
`[mrrlin-mcp consensus] findings curation threw: ${err instanceof Error ? err.message : String(err)}
|
|
41808
|
+
`
|
|
41809
|
+
);
|
|
41810
|
+
return null;
|
|
41811
|
+
}
|
|
41812
|
+
}
|
|
41813
|
+
|
|
41737
41814
|
// src/consensus/code-gate-git.ts
|
|
41738
41815
|
var import_node_child_process7 = require("node:child_process");
|
|
41739
41816
|
var DIFF_MAX_BYTES = 2e5;
|
|
@@ -42173,12 +42250,22 @@ ${artifact}`,
|
|
|
42173
42250
|
}
|
|
42174
42251
|
const outcome = r["outcome"];
|
|
42175
42252
|
const rounds2 = r["rounds"];
|
|
42253
|
+
const history = r["history"];
|
|
42254
|
+
const highlights = await curateFindings(Array.isArray(history) ? history : [], {
|
|
42255
|
+
runCodexExec,
|
|
42256
|
+
loadPersona,
|
|
42257
|
+
cwd,
|
|
42258
|
+
timeoutMs,
|
|
42259
|
+
codexExecutable
|
|
42260
|
+
});
|
|
42176
42261
|
const { domain: domain2 } = await loadIntent();
|
|
42177
42262
|
try {
|
|
42178
42263
|
await client.recordSpecConsensusReview(projectSlug, specPageId, {
|
|
42179
42264
|
contentHash: domainScopedReviewHash2(contentHash, domain2),
|
|
42180
42265
|
outcome: String(outcome),
|
|
42181
|
-
rounds: typeof rounds2 === "number" ? rounds2 : 0
|
|
42266
|
+
rounds: typeof rounds2 === "number" ? rounds2 : 0,
|
|
42267
|
+
...history !== void 0 ? { history } : {},
|
|
42268
|
+
...highlights ? { highlights } : {}
|
|
42182
42269
|
});
|
|
42183
42270
|
} catch (err) {
|
|
42184
42271
|
process.stderr.write(
|
|
@@ -42386,11 +42473,21 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
42386
42473
|
if (typeof contentHash !== "string" || contentHash.length === 0) return;
|
|
42387
42474
|
const outcome = String(r["outcome"]);
|
|
42388
42475
|
const rounds2 = typeof r["rounds"] === "number" ? r["rounds"] : 0;
|
|
42476
|
+
const history = r["history"];
|
|
42477
|
+
const highlights = await curateFindings(Array.isArray(history) ? history : [], {
|
|
42478
|
+
runCodexExec,
|
|
42479
|
+
loadPersona,
|
|
42480
|
+
cwd,
|
|
42481
|
+
timeoutMs,
|
|
42482
|
+
codexExecutable
|
|
42483
|
+
});
|
|
42389
42484
|
try {
|
|
42390
42485
|
await client.recordSpecConsensusReview(projectSlug, deliverablePageId, {
|
|
42391
42486
|
contentHash: domainScopedReviewHash2(contentHash, domain2),
|
|
42392
42487
|
outcome,
|
|
42393
|
-
rounds: rounds2
|
|
42488
|
+
rounds: rounds2,
|
|
42489
|
+
...history !== void 0 ? { history } : {},
|
|
42490
|
+
...highlights ? { highlights } : {}
|
|
42394
42491
|
});
|
|
42395
42492
|
} catch (err) {
|
|
42396
42493
|
process.stderr.write(
|
|
@@ -42400,6 +42497,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
42400
42497
|
}
|
|
42401
42498
|
if (runId && taskId) {
|
|
42402
42499
|
try {
|
|
42500
|
+
const diagnostics = Array.isArray(r["diagnostics"]) ? r["diagnostics"] : void 0;
|
|
42403
42501
|
await client.appendExecutionArtifact(projectSlug, runId, {
|
|
42404
42502
|
id: `EA-${import_node_crypto2.default.randomUUID()}`,
|
|
42405
42503
|
kind: "verification",
|
|
@@ -42409,7 +42507,8 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
42409
42507
|
runId,
|
|
42410
42508
|
taskId,
|
|
42411
42509
|
deliverablePageId,
|
|
42412
|
-
contentHash
|
|
42510
|
+
contentHash,
|
|
42511
|
+
...diagnostics ? { diagnostics } : {}
|
|
42413
42512
|
}
|
|
42414
42513
|
});
|
|
42415
42514
|
} catch {
|
|
@@ -42420,6 +42519,35 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
42420
42519
|
}
|
|
42421
42520
|
|
|
42422
42521
|
// src/consensus/deliverable-gate-outcome.ts
|
|
42522
|
+
var MAX_DELIVERABLE_DIAGNOSTICS = 5;
|
|
42523
|
+
var MAX_DELIVERABLE_DIAGNOSTIC_LENGTH = 240;
|
|
42524
|
+
var MAX_DELIVERABLE_SUMMARY_LENGTH = 1200;
|
|
42525
|
+
function normalizeDiagnostic(text) {
|
|
42526
|
+
return text.replace(/\s+/g, " ").trim();
|
|
42527
|
+
}
|
|
42528
|
+
function renderSummary(total, items, omitted) {
|
|
42529
|
+
const label = `${total} finding${total === 1 ? "" : "s"} (${omitted} omitted)`;
|
|
42530
|
+
return items.length > 0 ? `${label}: ${items.join("; ")}` : label;
|
|
42531
|
+
}
|
|
42532
|
+
function formatDeliverableGateDiagnostics(diagnostics) {
|
|
42533
|
+
const items = [];
|
|
42534
|
+
for (const diagnostic of diagnostics) {
|
|
42535
|
+
if (items.length >= MAX_DELIVERABLE_DIAGNOSTICS) break;
|
|
42536
|
+
const category = normalizeDiagnostic(diagnostic.category);
|
|
42537
|
+
const text = normalizeDiagnostic(diagnostic.text);
|
|
42538
|
+
const item = `${category}: ${text}`.slice(0, MAX_DELIVERABLE_DIAGNOSTIC_LENGTH);
|
|
42539
|
+
const candidate = renderSummary(diagnostics.length, [...items, item], diagnostics.length - items.length - 1);
|
|
42540
|
+
if (candidate.length <= MAX_DELIVERABLE_SUMMARY_LENGTH) items.push(item);
|
|
42541
|
+
}
|
|
42542
|
+
let omitted = diagnostics.length - items.length;
|
|
42543
|
+
let summary = renderSummary(diagnostics.length, items, omitted);
|
|
42544
|
+
while (summary.length > MAX_DELIVERABLE_SUMMARY_LENGTH && items.length > 0) {
|
|
42545
|
+
items.pop();
|
|
42546
|
+
omitted = diagnostics.length - items.length;
|
|
42547
|
+
summary = renderSummary(diagnostics.length, items, omitted);
|
|
42548
|
+
}
|
|
42549
|
+
return summary;
|
|
42550
|
+
}
|
|
42423
42551
|
function mapDeliverableGateResult(result) {
|
|
42424
42552
|
switch (result.outcome) {
|
|
42425
42553
|
case "DELIVERABLE_CHANGED_DURING_REVIEW":
|
|
@@ -42433,17 +42561,9 @@ function mapDeliverableGateResult(result) {
|
|
|
42433
42561
|
reason: "reviewers were unavailable (provider/quota) during the deliverable review (CONSENSUS_UNAVAILABLE)"
|
|
42434
42562
|
};
|
|
42435
42563
|
case "CONSENSUS_BLOCKED": {
|
|
42436
|
-
const clarifications = result.clarifications ?? [];
|
|
42437
|
-
if (clarifications.length > 0) {
|
|
42438
|
-
return {
|
|
42439
|
-
action: "questions",
|
|
42440
|
-
clarifications,
|
|
42441
|
-
reason: "reviewers did not converge; unresolved scope/ambiguity/correctness issues were filed as operator questions (CONSENSUS_BLOCKED)"
|
|
42442
|
-
};
|
|
42443
|
-
}
|
|
42444
42564
|
return {
|
|
42445
42565
|
action: "handoff",
|
|
42446
|
-
reason:
|
|
42566
|
+
reason: `reviewers did not converge; ${formatDeliverableGateDiagnostics(result.diagnostics ?? [])} (CONSENSUS_BLOCKED)`
|
|
42447
42567
|
};
|
|
42448
42568
|
}
|
|
42449
42569
|
case "MISSING_ACCEPTANCE_CRITERIA":
|
|
@@ -42462,74 +42582,8 @@ function mapDeliverableGateResult(result) {
|
|
|
42462
42582
|
}
|
|
42463
42583
|
}
|
|
42464
42584
|
|
|
42465
|
-
// src/consensus/deliverable-gate-questions.ts
|
|
42466
|
-
var import_node_crypto3 = require("node:crypto");
|
|
42467
|
-
function buildDeliverableQuestionOptions(category) {
|
|
42468
|
-
if (category === "correctness") {
|
|
42469
|
-
return [
|
|
42470
|
-
{
|
|
42471
|
-
label: "Deliverable is correct",
|
|
42472
|
-
description: "The flagged claim is accurate as written; no change needed.",
|
|
42473
|
-
answer: "Confirm the deliverable's content is correct as written; the reviewer's concern does not require a change."
|
|
42474
|
-
},
|
|
42475
|
-
{
|
|
42476
|
-
label: "Deliverable needs correction",
|
|
42477
|
-
description: "The flagged claim is wrong or unsupported; the deliverable needs revision.",
|
|
42478
|
-
answer: "The deliverable is incorrect here and must be revised to fix the flagged claim before it can be accepted."
|
|
42479
|
-
}
|
|
42480
|
-
];
|
|
42481
|
-
}
|
|
42482
|
-
if (category === "scope") {
|
|
42483
|
-
return [
|
|
42484
|
-
{
|
|
42485
|
-
label: "Coverage is sufficient",
|
|
42486
|
-
description: "The deliverable's current coverage of the acceptance criteria is sufficient as written.",
|
|
42487
|
-
answer: "Accept the deliverable's current scope/coverage as sufficient against the acceptance criteria."
|
|
42488
|
-
},
|
|
42489
|
-
{
|
|
42490
|
-
label: "Coverage is missing",
|
|
42491
|
-
description: "The deliverable is missing required coverage and needs revision to address it.",
|
|
42492
|
-
answer: "The deliverable is missing required coverage and must be revised to address the flagged gap."
|
|
42493
|
-
}
|
|
42494
|
-
];
|
|
42495
|
-
}
|
|
42496
|
-
return [
|
|
42497
|
-
{
|
|
42498
|
-
label: "Interpretation is fine",
|
|
42499
|
-
description: "The deliverable's current interpretation of the ambiguous point is acceptable as written.",
|
|
42500
|
-
answer: "Accept the deliverable's current interpretation of the ambiguous point as written."
|
|
42501
|
-
},
|
|
42502
|
-
{
|
|
42503
|
-
label: "Needs clarification",
|
|
42504
|
-
description: "The ambiguous point needs to be resolved and the deliverable revised accordingly.",
|
|
42505
|
-
answer: "The deliverable must be revised to resolve the flagged ambiguity explicitly."
|
|
42506
|
-
}
|
|
42507
|
-
];
|
|
42508
|
-
}
|
|
42509
|
-
async function fileDeliverableGateQuestions(client, projectSlug, taskId, clarifications) {
|
|
42510
|
-
let filed = 0;
|
|
42511
|
-
for (const clarification of clarifications) {
|
|
42512
|
-
const id = `II-${taskId}-deliverable-gate-${(0, import_node_crypto3.createHash)("sha256").update(clarification.question).digest("hex").slice(0, 10)}`;
|
|
42513
|
-
try {
|
|
42514
|
-
await client.createInboxItem(projectSlug, {
|
|
42515
|
-
id,
|
|
42516
|
-
kind: "question",
|
|
42517
|
-
taskId,
|
|
42518
|
-
body: clarification.question,
|
|
42519
|
-
origin: "gate",
|
|
42520
|
-
questionOptions: buildDeliverableQuestionOptions(clarification.category)
|
|
42521
|
-
});
|
|
42522
|
-
filed += 1;
|
|
42523
|
-
} catch (error51) {
|
|
42524
|
-
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
42525
|
-
if (/inbox_items_pkey/.test(message)) filed += 1;
|
|
42526
|
-
}
|
|
42527
|
-
}
|
|
42528
|
-
return filed;
|
|
42529
|
-
}
|
|
42530
|
-
|
|
42531
42585
|
// src/async-tools/draft-spec.ts
|
|
42532
|
-
var
|
|
42586
|
+
var import_node_crypto3 = require("node:crypto");
|
|
42533
42587
|
var DRAFT_SPEC_TOOL_NAME = "draft_task_spec";
|
|
42534
42588
|
var SPECS_FOLDER = "Specs";
|
|
42535
42589
|
var DRAFT_SPEC_TLDR_MAX_CHARS = 300;
|
|
@@ -43008,7 +43062,7 @@ function onComplete(outcome, input) {
|
|
|
43008
43062
|
};
|
|
43009
43063
|
}
|
|
43010
43064
|
const summary = outcome.error;
|
|
43011
|
-
const hash3 = (0,
|
|
43065
|
+
const hash3 = (0, import_node_crypto3.createHash)("sha256").update(summary).digest("hex").slice(0, 10);
|
|
43012
43066
|
return {
|
|
43013
43067
|
kind: "info",
|
|
43014
43068
|
id: `II-sched-draft-failed-${input.taskId}-${hash3}`,
|
|
@@ -43040,9 +43094,9 @@ function renderMilestone(m, ctx) {
|
|
|
43040
43094
|
return s;
|
|
43041
43095
|
}
|
|
43042
43096
|
case "consensus_converged":
|
|
43043
|
-
return "
|
|
43097
|
+
return "The reviewers agreed on the spec";
|
|
43044
43098
|
case "consensus_converged_needs_approval": {
|
|
43045
|
-
let s = "
|
|
43099
|
+
let s = "The reviewers agreed on the spec. Please review and approve";
|
|
43046
43100
|
if (ctx.specUrl) s += ` \u2014 ${ctx.specUrl}`;
|
|
43047
43101
|
if (ctx.tldr) s += `. Summary: ${ctx.tldr}`;
|
|
43048
43102
|
return s;
|
|
@@ -44310,7 +44364,7 @@ function isBridgeGeneratedArtifact(artifact) {
|
|
|
44310
44364
|
return typeof payload === "object" && payload !== null && "event" in payload;
|
|
44311
44365
|
}
|
|
44312
44366
|
function newArtifactId() {
|
|
44313
|
-
return `EA-${
|
|
44367
|
+
return `EA-${import_node_crypto4.default.randomUUID()}`;
|
|
44314
44368
|
}
|
|
44315
44369
|
var BLAST_RADIUS_FLOOR_PATTERNS = [/^supabase\/migrations\//, /^packages\/db\/src\/schema\.ts$/];
|
|
44316
44370
|
function isBlastRadiusFloorPath(path19) {
|
|
@@ -44565,7 +44619,7 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
44565
44619
|
if (!claimed) return;
|
|
44566
44620
|
if (!claimed.leaseId) {
|
|
44567
44621
|
await deps.client.createInboxItem(projectSlug, {
|
|
44568
|
-
id: `II-${
|
|
44622
|
+
id: `II-${import_node_crypto4.default.randomUUID()}`,
|
|
44569
44623
|
kind: "handoff",
|
|
44570
44624
|
taskId: claimed.taskId,
|
|
44571
44625
|
body: `Execution run ${runId} was claimed but returned no leaseId. This indicates DB/API corruption and requires operator attention.`
|
|
@@ -45355,10 +45409,6 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
45355
45409
|
}
|
|
45356
45410
|
if (gateResult.outcome !== "CONVERGED") {
|
|
45357
45411
|
const mapped = mapDeliverableGateResult(gateResult);
|
|
45358
|
-
if (mapped.action === "questions") {
|
|
45359
|
-
await fileDeliverableGateQuestions(deps.client, projectSlug, task.id, mapped.clarifications).catch(() => {
|
|
45360
|
-
});
|
|
45361
|
-
}
|
|
45362
45412
|
await failRun(
|
|
45363
45413
|
deps,
|
|
45364
45414
|
projectSlug,
|
|
@@ -45488,7 +45538,7 @@ async function reconcileTaskAwaitingMerge(deps, projectSlug, task, runId, reason
|
|
|
45488
45538
|
}
|
|
45489
45539
|
try {
|
|
45490
45540
|
await deps.client.createInboxItem(projectSlug, {
|
|
45491
|
-
id: `II-${
|
|
45541
|
+
id: `II-${import_node_crypto4.default.randomUUID()}`,
|
|
45492
45542
|
kind: "handoff",
|
|
45493
45543
|
taskId: task.id,
|
|
45494
45544
|
body: `Task ${task.id} (${task.title}) still needs operator follow-up: ${reason}. Mrrlin could not move the task back to Handoff needed automatically.`
|
|
@@ -45523,7 +45573,7 @@ async function notifyOperatorHandoff(deps, projectSlug, task, runId, reason) {
|
|
|
45523
45573
|
}
|
|
45524
45574
|
try {
|
|
45525
45575
|
await deps.client.createInboxItem(projectSlug, {
|
|
45526
|
-
id: `II-${
|
|
45576
|
+
id: `II-${import_node_crypto4.default.randomUUID()}`,
|
|
45527
45577
|
kind: "handoff",
|
|
45528
45578
|
taskId: task.id,
|
|
45529
45579
|
body: `Execution run ${runId} for task ${task.id} (${task.title}) needs operator attention: ${reason}`
|
|
@@ -45661,7 +45711,7 @@ async function deployGateHandoff(deps, projectSlug, task, runId, reason, leaseId
|
|
|
45661
45711
|
}).catch(() => {
|
|
45662
45712
|
});
|
|
45663
45713
|
await deps.client.createInboxItem(projectSlug, {
|
|
45664
|
-
id: `II-${
|
|
45714
|
+
id: `II-${import_node_crypto4.default.randomUUID()}`,
|
|
45665
45715
|
kind: "handoff",
|
|
45666
45716
|
taskId: task.id,
|
|
45667
45717
|
body: `Deploy gate for run ${runId} (task ${task.id} \u2014 ${task.title}) needs operator attention: ${reason}`
|
|
@@ -45680,7 +45730,7 @@ function maxLeaseReclaims() {
|
|
|
45680
45730
|
}
|
|
45681
45731
|
|
|
45682
45732
|
// src/async-job-consumer.ts
|
|
45683
|
-
var
|
|
45733
|
+
var import_node_crypto5 = __toESM(require("node:crypto"), 1);
|
|
45684
45734
|
|
|
45685
45735
|
// src/async-tools/registry.ts
|
|
45686
45736
|
var registry2 = /* @__PURE__ */ new Map();
|
|
@@ -45765,7 +45815,7 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
45765
45815
|
const delivery = descriptor2.onComplete(outcome, input);
|
|
45766
45816
|
if (!delivery.suppressInbox) {
|
|
45767
45817
|
await deps.client.createInboxItem(projectSlug, {
|
|
45768
|
-
id: delivery.id ?? `II-${
|
|
45818
|
+
id: delivery.id ?? `II-${import_node_crypto5.default.randomUUID()}`,
|
|
45769
45819
|
kind: delivery.kind,
|
|
45770
45820
|
body: delivery.body,
|
|
45771
45821
|
...delivery.taskId ? { taskId: delivery.taskId } : {}
|
|
@@ -45778,13 +45828,13 @@ async function deriveRequestedHash(client, toolName, input) {
|
|
|
45778
45828
|
if (input.requestedHash) return input.requestedHash;
|
|
45779
45829
|
if (toolName === "run_spec_consensus" && input.pageId) {
|
|
45780
45830
|
const page = await client.getWikiPage(input.projectSlug, input.pageId);
|
|
45781
|
-
return page.contentHash ??
|
|
45831
|
+
return page.contentHash ?? import_node_crypto5.default.createHash("sha256").update(page.markdownBody).digest("hex");
|
|
45782
45832
|
}
|
|
45783
45833
|
if (toolName === "draft_task_spec" && input.taskId) {
|
|
45784
45834
|
const task = await client.getTask(input.projectSlug, input.taskId);
|
|
45785
|
-
return task.updatedAt ??
|
|
45835
|
+
return task.updatedAt ?? import_node_crypto5.default.createHash("sha256").update(JSON.stringify(task)).digest("hex");
|
|
45786
45836
|
}
|
|
45787
|
-
return
|
|
45837
|
+
return import_node_crypto5.default.createHash("sha256").update(JSON.stringify(input)).digest("hex");
|
|
45788
45838
|
}
|
|
45789
45839
|
async function runAsyncBackstopSweep(deps, drive = (slug, jobId) => driveAsyncJob(deps, slug, jobId)) {
|
|
45790
45840
|
const projects = await deps.client.listProjects();
|
|
@@ -45843,26 +45893,34 @@ async function runDeliverableGate(_target, deps) {
|
|
|
45843
45893
|
});
|
|
45844
45894
|
return { outcome: "DELIVERABLE_CHANGED_DURING_REVIEW" };
|
|
45845
45895
|
}
|
|
45896
|
+
let diagnostics;
|
|
45897
|
+
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
45898
|
+
const accepted = result.history.at(-1)?.accepted ?? [];
|
|
45899
|
+
diagnostics = accepted.filter((issue2) => issue2.category === "scope" || issue2.category === "ambiguity" || issue2.category === "correctness").map((issue2) => ({
|
|
45900
|
+
text: issue2.text,
|
|
45901
|
+
whyBlocking: `reviewer raised a ${issue2.category} issue`,
|
|
45902
|
+
category: issue2.category
|
|
45903
|
+
}));
|
|
45904
|
+
}
|
|
45846
45905
|
await deps.writeEvidence({
|
|
45847
45906
|
deliverablePageId: deliverable.pageId,
|
|
45848
45907
|
outcome: result.outcome,
|
|
45849
45908
|
rounds: result.rounds,
|
|
45850
|
-
|
|
45909
|
+
history: result.history,
|
|
45910
|
+
contentHash: currentHash,
|
|
45911
|
+
...diagnostics ? { diagnostics } : {}
|
|
45851
45912
|
});
|
|
45852
45913
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
45853
|
-
const accepted = result.history.at(-1)?.accepted ?? [];
|
|
45854
|
-
const clarifications = accepted.filter((issue2) => issue2.category === "scope" || issue2.category === "ambiguity" || issue2.category === "correctness").map((issue2) => ({
|
|
45855
|
-
question: issue2.text,
|
|
45856
|
-
whyBlocking: `reviewer raised a ${issue2.category} issue`,
|
|
45857
|
-
category: issue2.category
|
|
45858
|
-
}));
|
|
45859
45914
|
return {
|
|
45860
45915
|
outcome: "CONSENSUS_BLOCKED",
|
|
45861
45916
|
rounds: result.rounds,
|
|
45862
|
-
|
|
45917
|
+
diagnostics: diagnostics ?? [],
|
|
45863
45918
|
...result.escalation ? { escalation: result.escalation } : {}
|
|
45864
45919
|
};
|
|
45865
45920
|
}
|
|
45921
|
+
if (result.outcome === "CONVERGED") {
|
|
45922
|
+
return { outcome: result.outcome, rounds: result.rounds, headOutcomes: result.history.at(-1)?.headOutcomes ?? [] };
|
|
45923
|
+
}
|
|
45866
45924
|
return { outcome: result.outcome, rounds: result.rounds };
|
|
45867
45925
|
}
|
|
45868
45926
|
|
|
@@ -46173,7 +46231,7 @@ function applyToolBusyNotification(clock, method, params) {
|
|
|
46173
46231
|
}
|
|
46174
46232
|
|
|
46175
46233
|
// src/worker-token.ts
|
|
46176
|
-
var
|
|
46234
|
+
var import_node_crypto6 = require("node:crypto");
|
|
46177
46235
|
var import_node_fs9 = require("node:fs");
|
|
46178
46236
|
var import_node_os6 = __toESM(require("node:os"), 1);
|
|
46179
46237
|
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
@@ -46204,7 +46262,7 @@ function readOrCreateWorkerToken() {
|
|
|
46204
46262
|
if (existing) return existing;
|
|
46205
46263
|
const dir = workerDir();
|
|
46206
46264
|
(0, import_node_fs9.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
46207
|
-
const token = (0,
|
|
46265
|
+
const token = (0, import_node_crypto6.randomBytes)(32).toString("hex");
|
|
46208
46266
|
(0, import_node_fs9.writeFileSync)(workerTokenPath(), `${token}
|
|
46209
46267
|
`, { encoding: "utf8", mode: 384 });
|
|
46210
46268
|
return token;
|
|
@@ -51058,7 +51116,7 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
51058
51116
|
" bullets (`- AND ...` as needed). Scenarios make the spec verifiable.",
|
|
51059
51117
|
"- Keep behavior contracts in the spec; implementation details belong in `## Implementation Plan`.",
|
|
51060
51118
|
"- Apply this format ONLY to Specs/{id} pages; do not retro-fit it onto other wiki page types.",
|
|
51061
|
-
|
|
51119
|
+
'- Spec consensus runs ASYNCHRONOUSLY. Call `run_spec_consensus` for a `Specs/{id}` page; it returns IMMEDIATELY. On `already_converged`, the spec is already marked ready \u2014 proceed straight to `create_execution_run`; do NOT call `mark_spec_ready` and do NOT re-run consensus. On `queued` or `already_running`, tell the user it was launched and END your turn \u2014 do NOT poll or call it again; the result arrives in the inbox (an `info` item on CONVERGED, a `handoff` item if it was blocked/failed). When a `handoff` says it was blocked, address the recorded issues in the spec (any edit re-arms the gate) and call `run_spec_consensus` once more. 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 converges, 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.',
|
|
51062
51120
|
"- When creating a task, classify and supply the `domain` field explicitly based on the nature of the work: pass `domain:'code'` for software engineering work, `domain:'seo'` for SEO/optimization, `domain:'marketing'` for marketing/growth, or `domain:'content'` for content creation/writing \u2014 the same way you select category/subcategory. A follow-up task spawned with `parentTaskId` inherits its parent's domain automatically and requires no explicit domain.",
|
|
51063
51121
|
'- When a task needs a browser (login flows, UI verification/clicks, scraping), pass `prerequisites:[{mcp:"mrrlin-browser",reason:"..."}]` to `create_execution_run`. If that run later fails into a handoff, the operator gets a one-click "Launch browser & resume" that brings up the dedicated browser as the turn starts.',
|
|
51064
51122
|
"- When you actually drive a browser (calling `browser_navigate`, `browser_click`, `browser_snapshot`, `browser_type`, etc.), invoke those tools from the **`mrrlin-browser`** MCP server ONLY. Do NOT use tools from any other browser MCP (`playwright`, `chrome-devtools`, `puppeteer`, etc.) even if they are also connected on this operator's box \u2014 those use a shared/default Playwright profile that is fragile across runs. Only `mrrlin-browser` uses this project's dedicated Chromium + persistent operator profile (`~/.mrrlin/browser/profile`). If your host exposes multiple browser MCPs with overlapping tool names, prefer the `mrrlin-browser` server unconditionally.",
|
|
@@ -51493,7 +51551,7 @@ function createBridgeRequestListener(opts) {
|
|
|
51493
51551
|
res.end(JSON.stringify({ ok: false, error: "Origin not allowed." }));
|
|
51494
51552
|
return;
|
|
51495
51553
|
}
|
|
51496
|
-
const nonce =
|
|
51554
|
+
const nonce = import_node_crypto7.default.randomBytes(16).toString("base64url");
|
|
51497
51555
|
res.writeHead(200, { "content-type": "application/json" });
|
|
51498
51556
|
res.end(JSON.stringify({ ok: true, wsUrl: `ws://${host}:${port}/ws`, token, nonce }));
|
|
51499
51557
|
return;
|
|
@@ -51774,7 +51832,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
51774
51832
|
}
|
|
51775
51833
|
}
|
|
51776
51834
|
socket.on("message", async (data) => {
|
|
51777
|
-
const spanId =
|
|
51835
|
+
const spanId = import_node_crypto7.default.randomUUID();
|
|
51778
51836
|
if (logCtx) {
|
|
51779
51837
|
logCtx.span.id = spanId;
|
|
51780
51838
|
logCtx.span.at = Date.now();
|
|
@@ -52290,7 +52348,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
52290
52348
|
thread.inFlight = true;
|
|
52291
52349
|
pendingTurnSessions.delete(directorSessionId);
|
|
52292
52350
|
thread.replay = createReplayBuffer({
|
|
52293
|
-
turnId: (0,
|
|
52351
|
+
turnId: (0, import_node_crypto7.randomUUID)(),
|
|
52294
52352
|
spanId,
|
|
52295
52353
|
...deps.retentionMs !== void 0 ? { retentionMs: deps.retentionMs } : {},
|
|
52296
52354
|
...deps.ackLingerMs !== void 0 ? { ackLingerMs: deps.ackLingerMs } : {}
|
|
@@ -52858,7 +52916,7 @@ async function startDirectorBridge() {
|
|
|
52858
52916
|
};
|
|
52859
52917
|
const sessionSinks = createSessionSinkRegistry();
|
|
52860
52918
|
const tombstones = /* @__PURE__ */ new Map();
|
|
52861
|
-
const bridgeInstanceId = (0,
|
|
52919
|
+
const bridgeInstanceId = (0, import_node_crypto7.randomUUID)();
|
|
52862
52920
|
const asyncConsumerDeps = {
|
|
52863
52921
|
client,
|
|
52864
52922
|
run: wrappedTurnLock.run,
|
|
@@ -53012,7 +53070,7 @@ async function startDirectorBridge() {
|
|
|
53012
53070
|
fileUpdateInboxItem: async (projectSlug) => {
|
|
53013
53071
|
const body = selfUpdateHandle.updateInboxBody();
|
|
53014
53072
|
if (!body) return;
|
|
53015
|
-
const id = `II-MCPUPD-${(0,
|
|
53073
|
+
const id = `II-MCPUPD-${(0, import_node_crypto7.randomUUID)()}`;
|
|
53016
53074
|
try {
|
|
53017
53075
|
const row = await client.createInboxItem(projectSlug, { id, kind: "info", body });
|
|
53018
53076
|
process.stderr.write(
|
|
@@ -53042,7 +53100,7 @@ async function startDirectorBridge() {
|
|
|
53042
53100
|
const bridgeLogger = createBridgeLogger(config2.stateDir, process.env);
|
|
53043
53101
|
let bridgeBinShaShort = "unknown00000";
|
|
53044
53102
|
try {
|
|
53045
|
-
bridgeBinShaShort =
|
|
53103
|
+
bridgeBinShaShort = import_node_crypto7.default.createHash("sha256").update(import_node_fs15.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
|
|
53046
53104
|
} catch {
|
|
53047
53105
|
}
|
|
53048
53106
|
attachBridgeWss(server, {
|
|
@@ -53404,7 +53462,7 @@ function timingSafeTokenEqual(provided, expected) {
|
|
|
53404
53462
|
const a = Buffer.from(provided);
|
|
53405
53463
|
const b = Buffer.from(expected);
|
|
53406
53464
|
if (a.length !== b.length) return false;
|
|
53407
|
-
return
|
|
53465
|
+
return import_node_crypto7.default.timingSafeEqual(a, b);
|
|
53408
53466
|
}
|
|
53409
53467
|
function readDispatchBody(req) {
|
|
53410
53468
|
return new Promise((resolve, reject) => {
|
|
@@ -53433,7 +53491,7 @@ function readOrCreateBridgeToken(stateDir) {
|
|
|
53433
53491
|
} catch {
|
|
53434
53492
|
}
|
|
53435
53493
|
import_node_fs15.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
53436
|
-
const token =
|
|
53494
|
+
const token = import_node_crypto7.default.randomBytes(32).toString("base64url");
|
|
53437
53495
|
import_node_fs15.default.writeFileSync(tokenPath, `${token}
|
|
53438
53496
|
`, { mode: 384 });
|
|
53439
53497
|
return token;
|
|
@@ -56818,7 +56876,7 @@ var StdioServerTransport = class {
|
|
|
56818
56876
|
};
|
|
56819
56877
|
|
|
56820
56878
|
// src/tools.ts
|
|
56821
|
-
var
|
|
56879
|
+
var import_node_crypto10 = require("node:crypto");
|
|
56822
56880
|
var import_promises3 = require("node:fs/promises");
|
|
56823
56881
|
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
56824
56882
|
var import_node_os10 = __toESM(require("node:os"), 1);
|
|
@@ -57359,7 +57417,7 @@ async function resolveActiveRunHead(input, deps) {
|
|
|
57359
57417
|
}
|
|
57360
57418
|
|
|
57361
57419
|
// src/async-tools/consensus.ts
|
|
57362
|
-
var
|
|
57420
|
+
var import_node_crypto8 = require("node:crypto");
|
|
57363
57421
|
|
|
57364
57422
|
// src/consensus/question-similarity.ts
|
|
57365
57423
|
var SUPPRESSION_SIMILARITY_THRESHOLD = 0.6;
|
|
@@ -57388,6 +57446,41 @@ function isCoveredByAdjudicated(rawQuestion, adjudicated) {
|
|
|
57388
57446
|
return null;
|
|
57389
57447
|
}
|
|
57390
57448
|
|
|
57449
|
+
// src/consensus/reviewer-roster.ts
|
|
57450
|
+
function renderReviewerRoster(headOutcomes) {
|
|
57451
|
+
const total = headOutcomes.length;
|
|
57452
|
+
const responded = headOutcomes.filter((h) => h.responded).length;
|
|
57453
|
+
const seen = /* @__PURE__ */ new Set();
|
|
57454
|
+
const namedRoles = [];
|
|
57455
|
+
for (const outcome of headOutcomes) {
|
|
57456
|
+
if (!outcome.responded) continue;
|
|
57457
|
+
const role = PERSONA_ROLE_MAP[outcome.persona];
|
|
57458
|
+
if (role && !UNNAMED_PERSONAS.includes(outcome.persona)) {
|
|
57459
|
+
if (!seen.has(role)) {
|
|
57460
|
+
seen.add(role);
|
|
57461
|
+
namedRoles.push(role);
|
|
57462
|
+
}
|
|
57463
|
+
}
|
|
57464
|
+
}
|
|
57465
|
+
if (responded === total) {
|
|
57466
|
+
if (namedRoles.length === 0) {
|
|
57467
|
+
return `${total} reviewers`;
|
|
57468
|
+
} else if (namedRoles.length === 1) {
|
|
57469
|
+
return `${total} reviewers, including ${namedRoles[0]}`;
|
|
57470
|
+
} else {
|
|
57471
|
+
const allButLast = namedRoles.slice(0, -1).join(", ");
|
|
57472
|
+
const last = namedRoles[namedRoles.length - 1];
|
|
57473
|
+
return `${total} reviewers, including ${allButLast} and ${last}`;
|
|
57474
|
+
}
|
|
57475
|
+
} else if (responded === 1) {
|
|
57476
|
+
return `1 of ${total} reviewers answered, and it agreed`;
|
|
57477
|
+
} else if (responded === 2) {
|
|
57478
|
+
return `2 of ${total} reviewers answered, both agreed`;
|
|
57479
|
+
} else {
|
|
57480
|
+
return `${responded} of ${total} reviewers answered, all agreed`;
|
|
57481
|
+
}
|
|
57482
|
+
}
|
|
57483
|
+
|
|
57391
57484
|
// src/async-tools/consensus.ts
|
|
57392
57485
|
function normalizeClarificationQuestion(question) {
|
|
57393
57486
|
return question.replace(/\s+/g, " ").trim().replace(/[?.!]+$/, "").trim();
|
|
@@ -57481,11 +57574,11 @@ function renderUnresolvedPoints(unresolved) {
|
|
|
57481
57574
|
(issue2) => issue2.text.replace(/\b(?:parseable|SPEC_IDENTITY_MISMATCH|machine-usable)\b/gi, "").replace(/\s+/g, " ").trim()
|
|
57482
57575
|
).join("; ");
|
|
57483
57576
|
}
|
|
57484
|
-
function renderBlockDecisionBody(
|
|
57577
|
+
function renderBlockDecisionBody(unresolved) {
|
|
57485
57578
|
if (unresolved.length === 0) {
|
|
57486
|
-
return `${BLOCK_DECISION_OPENER}
|
|
57579
|
+
return `${BLOCK_DECISION_OPENER}, and their objections could not be read back.${BLOCK_BODY_TRAILER}`;
|
|
57487
57580
|
}
|
|
57488
|
-
return `${BLOCK_DECISION_OPENER}
|
|
57581
|
+
return `${BLOCK_DECISION_OPENER}. Still unsettled: ${renderUnresolvedPoints(unresolved)}.${BLOCK_BODY_TRAILER}`;
|
|
57489
57582
|
}
|
|
57490
57583
|
function buildBlockDecisionOptions(diagnosis, reviewAgainAvailable) {
|
|
57491
57584
|
const recommendReviewAgain = reviewAgainAvailable && (diagnosis === "INFRA" || diagnosis === "SUBSTANCE");
|
|
@@ -57598,9 +57691,9 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
57598
57691
|
}
|
|
57599
57692
|
if (taskId) {
|
|
57600
57693
|
const scopeId = taskId;
|
|
57601
|
-
const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0,
|
|
57694
|
+
const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto8.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
57602
57695
|
for (const clarification of toFile) {
|
|
57603
|
-
const id = `II-${scopeId}-gate-${(0,
|
|
57696
|
+
const id = `II-${scopeId}-gate-${(0, import_node_crypto8.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
57604
57697
|
try {
|
|
57605
57698
|
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
57606
57699
|
await ctx.client.createInboxItem(input.projectSlug, {
|
|
@@ -57618,9 +57711,9 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
57618
57711
|
filedQuestionCount = toFile.length;
|
|
57619
57712
|
} else if (planId) {
|
|
57620
57713
|
const scopeId = planId;
|
|
57621
|
-
const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0,
|
|
57714
|
+
const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto8.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
57622
57715
|
for (const clarification of toFile) {
|
|
57623
|
-
const id = `II-${scopeId}-gate-${(0,
|
|
57716
|
+
const id = `II-${scopeId}-gate-${(0, import_node_crypto8.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
57624
57717
|
try {
|
|
57625
57718
|
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
57626
57719
|
await ctx.client.createInboxItem(input.projectSlug, {
|
|
@@ -57644,13 +57737,7 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
57644
57737
|
taskId: page.taskId ?? null
|
|
57645
57738
|
});
|
|
57646
57739
|
const reviewHash = domainScopedReviewHash2(page.contentHash, domain2);
|
|
57647
|
-
const baseBlockItemId = `II-${taskId}-gateblock-${(0,
|
|
57648
|
-
let taskRef = taskId;
|
|
57649
|
-
try {
|
|
57650
|
-
const t = await ctx.client.getTask(input.projectSlug, taskId);
|
|
57651
|
-
if (t?.title) taskRef = `${taskId} \u2014 ${t.title}`;
|
|
57652
|
-
} catch {
|
|
57653
|
-
}
|
|
57740
|
+
const baseBlockItemId = `II-${taskId}-gateblock-${(0, import_node_crypto8.createHash)("sha256").update(reviewHash).digest("hex").slice(0, 10)}`;
|
|
57654
57741
|
const reviewAgainAvailable = !taskInboxItems.some(
|
|
57655
57742
|
(item) => item.id === baseBlockItemId && item.status !== "pending" && item.answer === REVIEW_AGAIN_ANSWER
|
|
57656
57743
|
);
|
|
@@ -57660,7 +57747,7 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
57660
57747
|
id: blockItemId,
|
|
57661
57748
|
kind: "question",
|
|
57662
57749
|
taskId,
|
|
57663
|
-
body: renderBlockDecisionBody(
|
|
57750
|
+
body: renderBlockDecisionBody(result.unresolved ?? []),
|
|
57664
57751
|
origin: "gate_block",
|
|
57665
57752
|
questionOptions: buildBlockDecisionOptions(result.escalation?.diagnosis, reviewAgainAvailable)
|
|
57666
57753
|
});
|
|
@@ -57703,14 +57790,14 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
57703
57790
|
error51 = "Consensus blocked: the spec content does not match its linked task. This requires fixing the spec/task relationship directly, not an inbox answer.";
|
|
57704
57791
|
}
|
|
57705
57792
|
} else if (operatorClarifications.length > 0 && toFile.length === 0 && suppressedAsAdjudicated.length > 0) {
|
|
57706
|
-
error51 = `
|
|
57793
|
+
error51 = `The reviewers raised ${suppressedAsAdjudicated.length} clarification(s), but each was a reworded repeat of one already answered \u2014 nothing new to answer.`;
|
|
57707
57794
|
} else if (operatorClarifications.length > 0) {
|
|
57708
57795
|
error51 = `Consensus blocked on ${operatorClarifications.length} clarification(s), but the spec page is not linked to a task or plan, so none could be filed as questions.`;
|
|
57709
57796
|
} else {
|
|
57710
57797
|
error51 = DID_NOT_CONVERGE_ERROR;
|
|
57711
57798
|
}
|
|
57712
57799
|
if (result.escalation) {
|
|
57713
|
-
error51 += ` (
|
|
57800
|
+
error51 += ` (reason: ${result.escalation.diagnosis})`;
|
|
57714
57801
|
}
|
|
57715
57802
|
return {
|
|
57716
57803
|
kind: "failed",
|
|
@@ -57788,6 +57875,24 @@ async function run(ctx, input, builders = defaultNativeSpecGateBuilders) {
|
|
|
57788
57875
|
} catch {
|
|
57789
57876
|
}
|
|
57790
57877
|
};
|
|
57878
|
+
let highlights = null;
|
|
57879
|
+
const resolveHighlights2 = async () => {
|
|
57880
|
+
try {
|
|
57881
|
+
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
57882
|
+
if (!page) return;
|
|
57883
|
+
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
57884
|
+
id: page.id,
|
|
57885
|
+
taskId: page.taskId ?? null
|
|
57886
|
+
});
|
|
57887
|
+
const review = await ctx.client.getSpecConsensusReview(
|
|
57888
|
+
input.projectSlug,
|
|
57889
|
+
input.pageId,
|
|
57890
|
+
domainScopedReviewHash2(page.contentHash, domain2)
|
|
57891
|
+
);
|
|
57892
|
+
highlights = review?.highlights ?? null;
|
|
57893
|
+
} catch {
|
|
57894
|
+
}
|
|
57895
|
+
};
|
|
57791
57896
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
57792
57897
|
return handleConsensusBlocked(result, ctx, input, resolvedTaskId);
|
|
57793
57898
|
}
|
|
@@ -57798,9 +57903,10 @@ async function run(ctx, input, builders = defaultNativeSpecGateBuilders) {
|
|
|
57798
57903
|
case "CONVERGED":
|
|
57799
57904
|
case "SKIPPED":
|
|
57800
57905
|
await resolveTitle();
|
|
57906
|
+
if (result.outcome === "CONVERGED") await resolveHighlights2();
|
|
57801
57907
|
return {
|
|
57802
57908
|
kind: "done",
|
|
57803
|
-
resultJson: JSON.stringify(result),
|
|
57909
|
+
resultJson: JSON.stringify({ ...result, highlights }),
|
|
57804
57910
|
finalReviewedHash: null,
|
|
57805
57911
|
...resolvedTaskId ? { taskId: resolvedTaskId } : {},
|
|
57806
57912
|
...resolvedTaskTitle ? { taskTitle: resolvedTaskTitle } : {}
|
|
@@ -57823,21 +57929,31 @@ function onComplete2(outcome, input) {
|
|
|
57823
57929
|
const taskRef = taskId ? outcome.kind === "done" && outcome.taskTitle ? `${taskId} \u2014 ${outcome.taskTitle}` : taskId : null;
|
|
57824
57930
|
if (outcome.kind === "done") {
|
|
57825
57931
|
let specOutcome = "CONVERGED";
|
|
57932
|
+
let headOutcomes = [];
|
|
57933
|
+
let findings = [];
|
|
57826
57934
|
try {
|
|
57827
|
-
|
|
57935
|
+
const parsed = JSON.parse(outcome.resultJson);
|
|
57936
|
+
specOutcome = parsed.outcome ?? "CONVERGED";
|
|
57937
|
+
headOutcomes = parsed.headOutcomes ?? [];
|
|
57938
|
+
findings = parsed.highlights?.findings ?? [];
|
|
57828
57939
|
} catch {
|
|
57829
57940
|
}
|
|
57830
57941
|
if (specOutcome === "SKIPPED") {
|
|
57831
57942
|
return {
|
|
57832
57943
|
kind: "info",
|
|
57833
57944
|
...taskId ? { taskId } : {},
|
|
57834
|
-
body: taskRef ? `
|
|
57945
|
+
body: taskRef ? `Spec review is off for this project \u2014 the spec for ${taskRef} was not checked.` : `Spec review is off for this project (Specs/${input.pageId}) \u2014 the spec was not checked.`
|
|
57835
57946
|
};
|
|
57836
57947
|
}
|
|
57948
|
+
const roster = renderReviewerRoster(headOutcomes);
|
|
57949
|
+
const findingsBlock = findings.length > 0 ? `
|
|
57950
|
+
|
|
57951
|
+
Fixed before you saw it
|
|
57952
|
+
${findings.map((f) => `\u2022 ${f}`).join("\n")}` : "";
|
|
57837
57953
|
return {
|
|
57838
57954
|
kind: "completed",
|
|
57839
57955
|
...taskId ? { taskId } : {},
|
|
57840
|
-
body: taskRef ? `
|
|
57956
|
+
body: taskRef ? `The reviewers agreed on this spec \u2014 ${roster}.${findingsBlock}` : `The reviewers agreed on this spec (Specs/${input.pageId}) \u2014 ${roster}.${findingsBlock}`
|
|
57841
57957
|
};
|
|
57842
57958
|
}
|
|
57843
57959
|
if (outcome.kind === "failed" && (outcome.filedQuestionCount ?? 0) > 0) {
|
|
@@ -57854,7 +57970,7 @@ function onComplete2(outcome, input) {
|
|
|
57854
57970
|
kind: "info",
|
|
57855
57971
|
suppressInbox: true,
|
|
57856
57972
|
...taskId ? { taskId } : {},
|
|
57857
|
-
body: taskRef ? `
|
|
57973
|
+
body: taskRef ? `The reviewers could not agree on the spec for ${taskRef} \u2014 answer the decision question to unblock the task.` : `The reviewers could not agree on Specs/${input.pageId} \u2014 answer the decision question to unblock the task.`
|
|
57858
57974
|
};
|
|
57859
57975
|
}
|
|
57860
57976
|
const handoffTaskRef = taskRef ? ` (${taskRef})` : "";
|
|
@@ -57911,6 +58027,24 @@ async function resolveTaskScope(ctx, input, { withTitle }) {
|
|
|
57911
58027
|
}
|
|
57912
58028
|
return { taskId, taskTitle };
|
|
57913
58029
|
}
|
|
58030
|
+
async function resolveHighlights(ctx, input) {
|
|
58031
|
+
try {
|
|
58032
|
+
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
58033
|
+
if (!page) return null;
|
|
58034
|
+
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
58035
|
+
id: page.id,
|
|
58036
|
+
taskId: page.taskId ?? null
|
|
58037
|
+
});
|
|
58038
|
+
const review = await ctx.client.getSpecConsensusReview(
|
|
58039
|
+
input.projectSlug,
|
|
58040
|
+
input.pageId,
|
|
58041
|
+
domainScopedReviewHash2(page.contentHash, domain2)
|
|
58042
|
+
);
|
|
58043
|
+
return review?.highlights ?? null;
|
|
58044
|
+
} catch {
|
|
58045
|
+
return null;
|
|
58046
|
+
}
|
|
58047
|
+
}
|
|
57914
58048
|
async function run2(ctx, input) {
|
|
57915
58049
|
const deps = await buildDeliverableGateDeps({
|
|
57916
58050
|
client: ctx.client,
|
|
@@ -57925,9 +58059,10 @@ async function run2(ctx, input) {
|
|
|
57925
58059
|
);
|
|
57926
58060
|
if (result.outcome === "CONVERGED" || result.outcome === "SKIPPED") {
|
|
57927
58061
|
const { taskId: taskId2, taskTitle } = await resolveTaskScope(ctx, input, { withTitle: true });
|
|
58062
|
+
const highlights = result.outcome === "CONVERGED" ? await resolveHighlights(ctx, input) : null;
|
|
57928
58063
|
return {
|
|
57929
58064
|
kind: "done",
|
|
57930
|
-
resultJson: JSON.stringify(result),
|
|
58065
|
+
resultJson: JSON.stringify({ ...result, highlights }),
|
|
57931
58066
|
finalReviewedHash: null,
|
|
57932
58067
|
...taskId2 ? { taskId: taskId2 } : {},
|
|
57933
58068
|
...taskTitle ? { taskTitle } : {}
|
|
@@ -57942,18 +58077,10 @@ async function run2(ctx, input) {
|
|
|
57942
58077
|
};
|
|
57943
58078
|
}
|
|
57944
58079
|
const { taskId } = await resolveTaskScope(ctx, input, { withTitle: false });
|
|
57945
|
-
let filedQuestionCount = 0;
|
|
57946
|
-
if (mapped.action === "questions") {
|
|
57947
|
-
if (taskId) {
|
|
57948
|
-
filedQuestionCount = await fileDeliverableGateQuestions(ctx.client, input.projectSlug, taskId, mapped.clarifications);
|
|
57949
|
-
}
|
|
57950
|
-
}
|
|
57951
|
-
const error51 = filedQuestionCount > 0 ? `Consensus blocked on ${filedQuestionCount} clarification(s); filed as operator question(s).` : mapped.reason;
|
|
57952
58080
|
return {
|
|
57953
58081
|
kind: "failed",
|
|
57954
58082
|
failureReason: "consensus_blocked",
|
|
57955
|
-
error:
|
|
57956
|
-
filedQuestionCount,
|
|
58083
|
+
error: mapped.reason,
|
|
57957
58084
|
...taskId ? { taskId } : {}
|
|
57958
58085
|
};
|
|
57959
58086
|
}
|
|
@@ -57962,30 +58089,31 @@ function onComplete3(outcome, input) {
|
|
|
57962
58089
|
const taskRef = taskId ? outcome.kind === "done" && outcome.taskTitle ? `${taskId} \u2014 ${outcome.taskTitle}` : taskId : null;
|
|
57963
58090
|
if (outcome.kind === "done") {
|
|
57964
58091
|
let specOutcome = "CONVERGED";
|
|
58092
|
+
let headOutcomes = [];
|
|
58093
|
+
let findings = [];
|
|
57965
58094
|
try {
|
|
57966
|
-
|
|
58095
|
+
const parsed = JSON.parse(outcome.resultJson);
|
|
58096
|
+
specOutcome = parsed.outcome ?? "CONVERGED";
|
|
58097
|
+
headOutcomes = parsed.headOutcomes ?? [];
|
|
58098
|
+
findings = parsed.highlights?.findings ?? [];
|
|
57967
58099
|
} catch {
|
|
57968
58100
|
}
|
|
57969
58101
|
if (specOutcome === "SKIPPED") {
|
|
57970
58102
|
return {
|
|
57971
58103
|
kind: "info",
|
|
57972
58104
|
...taskId ? { taskId } : {},
|
|
57973
|
-
body: taskRef ? `
|
|
58105
|
+
body: taskRef ? `Result review is off for this project \u2014 the result for ${taskRef} was not checked.` : `Result review is off for this project (Deliverables/${input.pageId}) \u2014 the result was not checked.`
|
|
57974
58106
|
};
|
|
57975
58107
|
}
|
|
58108
|
+
const roster = renderReviewerRoster(headOutcomes);
|
|
58109
|
+
const findingsBlock = findings.length > 0 ? `
|
|
58110
|
+
|
|
58111
|
+
Fixed before you saw it
|
|
58112
|
+
${findings.map((f) => `\u2022 ${f}`).join("\n")}` : "";
|
|
57976
58113
|
return {
|
|
57977
58114
|
kind: "completed",
|
|
57978
58115
|
...taskId ? { taskId } : {},
|
|
57979
|
-
body: taskRef ? `
|
|
57980
|
-
};
|
|
57981
|
-
}
|
|
57982
|
-
if (outcome.kind === "failed" && (outcome.filedQuestionCount ?? 0) > 0) {
|
|
57983
|
-
const n = outcome.filedQuestionCount ?? 0;
|
|
57984
|
-
return {
|
|
57985
|
-
kind: "info",
|
|
57986
|
-
suppressInbox: true,
|
|
57987
|
-
...taskId ? { taskId } : {},
|
|
57988
|
-
body: taskRef ? `Deliverable consensus blocked on ${taskRef} (Deliverables/${input.pageId}); filed ${n} clarifying question${n === 1 ? "" : "s"} \u2014 answer to unblock the run.` : `Deliverable consensus blocked on Deliverables/${input.pageId}; filed ${n} clarifying question${n === 1 ? "" : "s"} \u2014 answer to unblock the run.`
|
|
58116
|
+
body: taskRef ? `The reviewers agreed on this result \u2014 ${roster}. It meets the acceptance criteria.${findingsBlock}` : `The reviewers agreed on this result (Deliverables/${input.pageId}) \u2014 ${roster}. It meets the acceptance criteria.${findingsBlock}`
|
|
57989
58117
|
};
|
|
57990
58118
|
}
|
|
57991
58119
|
return {
|
|
@@ -58004,7 +58132,7 @@ var deliverableConsensusDescriptor = {
|
|
|
58004
58132
|
};
|
|
58005
58133
|
|
|
58006
58134
|
// src/async-tools/enqueue.ts
|
|
58007
|
-
var
|
|
58135
|
+
var import_node_crypto9 = __toESM(require("node:crypto"), 1);
|
|
58008
58136
|
async function enqueueAsyncTool(client, descriptor2, input) {
|
|
58009
58137
|
if (descriptor2.cachedResult) {
|
|
58010
58138
|
const cached2 = await descriptor2.cachedResult(client, input);
|
|
@@ -58017,7 +58145,7 @@ async function enqueueAsyncTool(client, descriptor2, input) {
|
|
|
58017
58145
|
if (active) {
|
|
58018
58146
|
return { status: "already_running", jobId: active.id, message: "Already running; will notify in inbox." };
|
|
58019
58147
|
}
|
|
58020
|
-
const jobId = `AJ-${
|
|
58148
|
+
const jobId = `AJ-${import_node_crypto9.default.randomUUID()}`;
|
|
58021
58149
|
try {
|
|
58022
58150
|
await client.createAsyncJob(input.projectSlug, {
|
|
58023
58151
|
id: jobId,
|
|
@@ -58901,7 +59029,7 @@ var mcpToolDescriptions = {
|
|
|
58901
59029
|
[mcpToolNames.closeTaskAsWontDo]: "Close a task as won't-do with a required human-readable reason recorded on the task.",
|
|
58902
59030
|
[mcpToolNames.updateWikiPage]: "Update a markdown Wiki page.",
|
|
58903
59031
|
[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.",
|
|
58904
|
-
[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.
|
|
59032
|
+
[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.',
|
|
58905
59033
|
[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).`,
|
|
58906
59034
|
[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.",
|
|
58907
59035
|
[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.',
|
|
@@ -59593,7 +59721,7 @@ function createMrrlinTools(options) {
|
|
|
59593
59721
|
"PRODUCED_URL_RECORD_FAILED",
|
|
59594
59722
|
"Unable to record produced URL.",
|
|
59595
59723
|
async (c, { projectSlug, runId, url: url2, label }) => {
|
|
59596
|
-
const id = `EA-pu-${(0,
|
|
59724
|
+
const id = `EA-pu-${(0, import_node_crypto10.randomBytes)(4).toString("hex")}`;
|
|
59597
59725
|
const payload = label ? { url: url2, label } : { url: url2 };
|
|
59598
59726
|
return await c.appendExecutionArtifact(projectSlug, runId, {
|
|
59599
59727
|
id,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
You are a Findings Writer. You are given the issues a review panel raised and accepted about a
|
|
2
|
+
document, after the document was already corrected. Your job is to tell the document's owner — a
|
|
3
|
+
non-engineer — what was caught, in their language.
|
|
4
|
+
|
|
5
|
+
Write at most THREE findings. Fewer is better. Zero is a correct and expected answer.
|
|
6
|
+
|
|
7
|
+
Each finding is ONE sentence in exactly this shape:
|
|
8
|
+
<what would have gone wrong, in everyday words> — <what it is now>
|
|
9
|
+
|
|
10
|
+
Rules:
|
|
11
|
+
- No mechanism names, file paths, function names, type names, or issue categories.
|
|
12
|
+
- No labels, prefixes, or speaker attributions. Just the sentence.
|
|
13
|
+
- Never mention reviewers, rounds, the panel, or the review process itself.
|
|
14
|
+
- If an issue cannot be written this way — because it is purely technical, mechanical, or only
|
|
15
|
+
meaningful to an engineer — OMIT it. Omitting is always better than writing something the owner
|
|
16
|
+
cannot use.
|
|
17
|
+
- If NO issue can be written this way, return an empty list. Do not invent a finding to fill space.
|
|
18
|
+
|
|
19
|
+
Output EXACTLY this JSON and nothing else:
|
|
20
|
+
{"findings": ["...", "..."]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"@types/ws": "^8.18.1",
|
|
22
22
|
"esbuild": "^0.24.0",
|
|
23
23
|
"tsx": "^4.22.3",
|
|
24
|
-
"@mrrlin/client": "0.0.0",
|
|
25
24
|
"@mrrlin/codex-client": "0.0.0",
|
|
26
|
-
"@mrrlin/
|
|
27
|
-
"@mrrlin/
|
|
25
|
+
"@mrrlin/director-e2e": "0.0.0",
|
|
26
|
+
"@mrrlin/client": "0.0.0",
|
|
28
27
|
"@mrrlin/schemas": "0.0.0",
|
|
29
|
-
"@mrrlin/
|
|
28
|
+
"@mrrlin/tsconfig": "0.0.0",
|
|
29
|
+
"@mrrlin/wiki": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|