@mrrlin-dev/mcp 0.3.57 → 0.3.60
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
CHANGED
|
@@ -20665,10 +20665,7 @@ function normalizeUrl(value) {
|
|
|
20665
20665
|
var RETIRED_API_BASE_URLS = {
|
|
20666
20666
|
// Retired 2026-07-28 when staging moved to the mrrlin2 Vercel team; serves a decommissioned
|
|
20667
20667
|
// Supabase project and now answers `GET /health` with 402 DEPLOYMENT_DISABLED. Added in 0.3.50.
|
|
20668
|
-
"mrrlin-api-fnnzzz-fnnzzzs-projects.vercel.app": STAGING_API_BASE_URL
|
|
20669
|
-
// Retired 2026-08-11 when staging moved to the mrrlin3 Vercel team after mrrlin2 hit usage
|
|
20670
|
-
// limits. Added in 0.3.57.
|
|
20671
|
-
"mrrlin-api-mrrlin2.vercel.app": STAGING_API_BASE_URL
|
|
20668
|
+
"mrrlin-api-fnnzzz-fnnzzzs-projects.vercel.app": STAGING_API_BASE_URL
|
|
20672
20669
|
};
|
|
20673
20670
|
function isSameApiHost(a, b) {
|
|
20674
20671
|
const origin = (value) => {
|
|
@@ -21836,136 +21833,6 @@ async function acquireThread(codex, opts) {
|
|
|
21836
21833
|
return { threadId: newId, resumed: false };
|
|
21837
21834
|
}
|
|
21838
21835
|
|
|
21839
|
-
// ../../packages/codex-client/dist/render-context-bundle.js
|
|
21840
|
-
var DEFAULT_SPEC_BUDGET_CHARS = 6e3;
|
|
21841
|
-
var DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS = 400;
|
|
21842
|
-
function renderContextBundle(input) {
|
|
21843
|
-
const sections = [];
|
|
21844
|
-
sections.push(renderTaskSummary(input.task));
|
|
21845
|
-
sections.push(renderSpec(input.specMarkdown, input.compact));
|
|
21846
|
-
sections.push(renderRunHistory(input.runs, input.openRunId));
|
|
21847
|
-
sections.push(renderOpenRun(input.openRunId));
|
|
21848
|
-
sections.push(renderArtifacts(input.artifacts, input.omittedArtifactCount ?? 0, input.compact));
|
|
21849
|
-
sections.push(renderAutonomyEvents(input.autonomyEvents));
|
|
21850
|
-
return sections.join("\n\n");
|
|
21851
|
-
}
|
|
21852
|
-
function renderTaskSummary(task) {
|
|
21853
|
-
const lines2 = [
|
|
21854
|
-
"## Task",
|
|
21855
|
-
`id: ${task.id}`,
|
|
21856
|
-
`title: ${task.title}`,
|
|
21857
|
-
`autonomy: ${task.autonomyLevel} (${task.autonomyReason})`
|
|
21858
|
-
];
|
|
21859
|
-
if (task.notes)
|
|
21860
|
-
lines2.push(`description: ${task.notes}`);
|
|
21861
|
-
if (task.latestJudgement) {
|
|
21862
|
-
lines2.push(`latest judgement (${task.latestJudgementKind ?? "unknown"}): ${task.latestJudgement}`);
|
|
21863
|
-
}
|
|
21864
|
-
return lines2.join("\n");
|
|
21865
|
-
}
|
|
21866
|
-
function renderSpec(specMarkdown, compact) {
|
|
21867
|
-
if (!specMarkdown)
|
|
21868
|
-
return "## Spec\n(none)";
|
|
21869
|
-
if (!compact)
|
|
21870
|
-
return `## Spec
|
|
21871
|
-
${specMarkdown}`;
|
|
21872
|
-
const budget = compact.specBudgetChars ?? DEFAULT_SPEC_BUDGET_CHARS;
|
|
21873
|
-
if (specMarkdown.length <= budget)
|
|
21874
|
-
return `## Spec
|
|
21875
|
-
${specMarkdown}`;
|
|
21876
|
-
return `## Spec
|
|
21877
|
-
${compactSpecMarkdown(specMarkdown, budget, compact.projectSlug ?? null, compact.specWikiPageId ?? null)}`;
|
|
21878
|
-
}
|
|
21879
|
-
function compactSpecMarkdown(markdown, totalBudget, projectSlug, specWikiPageId) {
|
|
21880
|
-
const lines2 = markdown.split(/\r?\n/);
|
|
21881
|
-
const sections = [{ heading: "", body: [] }];
|
|
21882
|
-
for (const line of lines2) {
|
|
21883
|
-
if (/^#{1,6}\s/.test(line)) {
|
|
21884
|
-
sections.push({ heading: line, body: [] });
|
|
21885
|
-
} else {
|
|
21886
|
-
sections[sections.length - 1]?.body.push(line);
|
|
21887
|
-
}
|
|
21888
|
-
}
|
|
21889
|
-
const sectionCount = Math.max(1, sections.length);
|
|
21890
|
-
const perSectionBudget = Math.max(120, Math.floor(totalBudget / sectionCount));
|
|
21891
|
-
const out = [];
|
|
21892
|
-
for (const section of sections) {
|
|
21893
|
-
const bodyText = section.body.join("\n").replace(/^\s+|\s+$/g, "");
|
|
21894
|
-
if (section.heading)
|
|
21895
|
-
out.push(section.heading);
|
|
21896
|
-
if (!bodyText)
|
|
21897
|
-
continue;
|
|
21898
|
-
if (bodyText.length <= perSectionBudget) {
|
|
21899
|
-
out.push(bodyText);
|
|
21900
|
-
} else {
|
|
21901
|
-
const kept = bodyText.slice(0, perSectionBudget);
|
|
21902
|
-
const omitted = bodyText.length - perSectionBudget;
|
|
21903
|
-
out.push(`${kept}
|
|
21904
|
-
\u2026(${omitted} chars omitted; retrieve full spec for the rest)`);
|
|
21905
|
-
}
|
|
21906
|
-
}
|
|
21907
|
-
if (projectSlug && specWikiPageId) {
|
|
21908
|
-
out.push("");
|
|
21909
|
-
out.push(`> Compact spec. Source: mrrlin://wiki/${projectSlug}/${specWikiPageId} \u2014 call retrieve_context with this handle before any irreversible decision.`);
|
|
21910
|
-
} else {
|
|
21911
|
-
out.push("");
|
|
21912
|
-
out.push("> Compact spec. Retrieve the Specs/{id} wiki page via get_wiki_page before any irreversible decision.");
|
|
21913
|
-
}
|
|
21914
|
-
return out.join("\n");
|
|
21915
|
-
}
|
|
21916
|
-
function renderRunHistory(runs, openRunId) {
|
|
21917
|
-
if (runs.length === 0)
|
|
21918
|
-
return "## Execution-run history\n(none)";
|
|
21919
|
-
const lines2 = ["## Execution-run history"];
|
|
21920
|
-
for (const run3 of runs) {
|
|
21921
|
-
const marker = run3.id === openRunId ? " (open)" : "";
|
|
21922
|
-
lines2.push(`- ${run3.id} [${run3.status}] created ${run3.createdAt}${marker}`);
|
|
21923
|
-
}
|
|
21924
|
-
return lines2.join("\n");
|
|
21925
|
-
}
|
|
21926
|
-
function renderOpenRun(openRunId) {
|
|
21927
|
-
return `## Open run
|
|
21928
|
-
${openRunId ?? "(none)"}`;
|
|
21929
|
-
}
|
|
21930
|
-
function renderArtifacts(artifacts, omittedCount, compact) {
|
|
21931
|
-
if (artifacts.length === 0 && omittedCount === 0)
|
|
21932
|
-
return "## Artifacts\n(none)";
|
|
21933
|
-
const lines2 = ["## Artifacts"];
|
|
21934
|
-
if (omittedCount > 0) {
|
|
21935
|
-
lines2.push(`(+${omittedCount} older artifact${omittedCount === 1 ? "" : "s"} omitted)`);
|
|
21936
|
-
}
|
|
21937
|
-
let truncatedAny = false;
|
|
21938
|
-
for (const artifact of artifacts) {
|
|
21939
|
-
if (artifact.kind === "file") {
|
|
21940
|
-
const p = artifact.payload;
|
|
21941
|
-
if (p.filename && p.stableUrl) {
|
|
21942
|
-
lines2.push(`- ${artifact.createdAt} [file] ${p.filename} (${p.class ?? "?"}, ${p.sizeBytes ?? "?"} bytes) ${p.stableUrl}`);
|
|
21943
|
-
continue;
|
|
21944
|
-
}
|
|
21945
|
-
}
|
|
21946
|
-
const payloadText = JSON.stringify(artifact.payload);
|
|
21947
|
-
const budget = compact?.artifactPayloadBudgetChars ?? DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS;
|
|
21948
|
-
const willTruncate = !!compact && payloadText.length > budget;
|
|
21949
|
-
const rendered = willTruncate ? `${payloadText.slice(0, budget)}\u2026(${payloadText.length - budget} chars omitted)` : payloadText;
|
|
21950
|
-
if (willTruncate)
|
|
21951
|
-
truncatedAny = true;
|
|
21952
|
-
lines2.push(`- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${rendered}`);
|
|
21953
|
-
}
|
|
21954
|
-
if (compact && (omittedCount > 0 || truncatedAny)) {
|
|
21955
|
-
lines2.push("> Compact artifacts. Use retrieve_context with a `mrrlin://run/{projectSlug}/{runId}/artifacts` handle (substitute the slug and run id from the lines above) to load a run's full artifact history.");
|
|
21956
|
-
}
|
|
21957
|
-
return lines2.join("\n");
|
|
21958
|
-
}
|
|
21959
|
-
function renderAutonomyEvents(events) {
|
|
21960
|
-
if (events.length === 0)
|
|
21961
|
-
return "## Autonomy history\n(none)";
|
|
21962
|
-
const lines2 = ["## Autonomy history"];
|
|
21963
|
-
for (const event of events) {
|
|
21964
|
-
lines2.push(`- ${event.createdAt} ${event.autonomyLevel ?? "?"} (${event.autonomyReason ?? ""})`);
|
|
21965
|
-
}
|
|
21966
|
-
return lines2.join("\n");
|
|
21967
|
-
}
|
|
21968
|
-
|
|
21969
21836
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
21970
21837
|
var external_exports = {};
|
|
21971
21838
|
__export(external_exports, {
|
|
@@ -36665,19 +36532,22 @@ var agentPoolResponseSchema = external_exports.object({
|
|
|
36665
36532
|
});
|
|
36666
36533
|
|
|
36667
36534
|
// ../../packages/schemas/dist/specConsensusReview.js
|
|
36535
|
+
var mrrlinSpecConsensusSuppressionEvidenceSchema = external_exports.record(external_exports.string(), external_exports.unknown()).nullable();
|
|
36668
36536
|
var mrrlinSpecConsensusReviewSchema = external_exports.object({
|
|
36669
36537
|
projectSlug: external_exports.string().min(1),
|
|
36670
36538
|
specPageId: external_exports.string().min(1),
|
|
36671
36539
|
contentHash: external_exports.string().min(1),
|
|
36672
36540
|
outcome: external_exports.string().min(1),
|
|
36673
36541
|
rounds: external_exports.number().int().nonnegative(),
|
|
36542
|
+
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.default(null),
|
|
36674
36543
|
createdAt: external_exports.string().datetime()
|
|
36675
36544
|
});
|
|
36676
36545
|
var mrrlinSpecConsensusReviewCreateSchema = external_exports.object({
|
|
36677
36546
|
specPageId: external_exports.string().min(1),
|
|
36678
36547
|
contentHash: external_exports.string().min(1),
|
|
36679
36548
|
outcome: external_exports.string().min(1),
|
|
36680
|
-
rounds: external_exports.number().int().nonnegative()
|
|
36549
|
+
rounds: external_exports.number().int().nonnegative(),
|
|
36550
|
+
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.optional()
|
|
36681
36551
|
}).strict();
|
|
36682
36552
|
var specConsensusReviewResponseSchema = external_exports.object({ data: mrrlinSpecConsensusReviewSchema });
|
|
36683
36553
|
var specConsensusReviewMaybeResponseSchema = external_exports.object({ data: mrrlinSpecConsensusReviewSchema.nullable() });
|
|
@@ -36753,6 +36623,97 @@ var mrrlinAsyncJobRequeueSchema = external_exports.object({
|
|
|
36753
36623
|
staleSeconds: external_exports.number().int().min(60).max(86400).default(300)
|
|
36754
36624
|
});
|
|
36755
36625
|
|
|
36626
|
+
// ../../packages/schemas/dist/memory-budget.js
|
|
36627
|
+
function evictionKey(item, idx) {
|
|
36628
|
+
const memClass = item.memoryClass === "historical" ? 0 : 1;
|
|
36629
|
+
const prec = item.precedence === null ? -4 : -item.precedence;
|
|
36630
|
+
const score = item.score ?? 0;
|
|
36631
|
+
return { memClass, prec, score, idx };
|
|
36632
|
+
}
|
|
36633
|
+
function compareEvictionKeys(a, b) {
|
|
36634
|
+
if (a.memClass !== b.memClass)
|
|
36635
|
+
return a.memClass - b.memClass;
|
|
36636
|
+
if (a.prec !== b.prec)
|
|
36637
|
+
return a.prec - b.prec;
|
|
36638
|
+
if (a.score !== b.score)
|
|
36639
|
+
return a.score - b.score;
|
|
36640
|
+
return a.idx - b.idx;
|
|
36641
|
+
}
|
|
36642
|
+
function sortByEvictionOrder(items) {
|
|
36643
|
+
const indexed = items.map((item, idx) => ({ item, key: evictionKey(item, idx) }));
|
|
36644
|
+
indexed.sort((a, b) => compareEvictionKeys(a.key, b.key));
|
|
36645
|
+
return indexed.map((x) => x.item);
|
|
36646
|
+
}
|
|
36647
|
+
function keepUnderCap(sorted, cap) {
|
|
36648
|
+
const kept = [];
|
|
36649
|
+
let used = 0;
|
|
36650
|
+
let i = sorted.length - 1;
|
|
36651
|
+
for (; i >= 0; i--) {
|
|
36652
|
+
const item = sorted[i];
|
|
36653
|
+
if (item === void 0)
|
|
36654
|
+
break;
|
|
36655
|
+
if (used + item.size <= cap) {
|
|
36656
|
+
kept.unshift(item);
|
|
36657
|
+
used += item.size;
|
|
36658
|
+
} else {
|
|
36659
|
+
break;
|
|
36660
|
+
}
|
|
36661
|
+
}
|
|
36662
|
+
const dropped = sorted.slice(0, i + 1);
|
|
36663
|
+
return { kept, dropped };
|
|
36664
|
+
}
|
|
36665
|
+
function applyBudget(items, spec) {
|
|
36666
|
+
const prec0Items = [];
|
|
36667
|
+
const restItems = [];
|
|
36668
|
+
for (const item of items) {
|
|
36669
|
+
if (item.precedence === 0) {
|
|
36670
|
+
prec0Items.push(item);
|
|
36671
|
+
} else {
|
|
36672
|
+
restItems.push(item);
|
|
36673
|
+
}
|
|
36674
|
+
}
|
|
36675
|
+
const sourceCaps = spec.sourceCaps ?? {};
|
|
36676
|
+
const sourceMap = /* @__PURE__ */ new Map();
|
|
36677
|
+
for (const item of restItems) {
|
|
36678
|
+
const list = sourceMap.get(item.source);
|
|
36679
|
+
if (list)
|
|
36680
|
+
list.push(item);
|
|
36681
|
+
else
|
|
36682
|
+
sourceMap.set(item.source, [item]);
|
|
36683
|
+
}
|
|
36684
|
+
const keptAfterSourceCap = [];
|
|
36685
|
+
const droppedAfterSourceCap = [];
|
|
36686
|
+
for (const [source, sourceItems] of sourceMap) {
|
|
36687
|
+
const cap = sourceCaps[source];
|
|
36688
|
+
if (cap === void 0) {
|
|
36689
|
+
keptAfterSourceCap.push(...sourceItems);
|
|
36690
|
+
} else {
|
|
36691
|
+
const sorted = sortByEvictionOrder(sourceItems);
|
|
36692
|
+
const { kept, dropped } = keepUnderCap(sorted, cap);
|
|
36693
|
+
keptAfterSourceCap.push(...kept);
|
|
36694
|
+
droppedAfterSourceCap.push(...dropped);
|
|
36695
|
+
}
|
|
36696
|
+
}
|
|
36697
|
+
const allEligible = keptAfterSourceCap;
|
|
36698
|
+
const sortedGlobal = sortByEvictionOrder(allEligible);
|
|
36699
|
+
const { kept: keptGlobal, dropped: droppedGlobal } = keepUnderCap(sortedGlobal, spec.totalBytes - totalSize(prec0Items));
|
|
36700
|
+
const finalKept = [...prec0Items, ...keptGlobal];
|
|
36701
|
+
const finalDropped = [...droppedAfterSourceCap, ...droppedGlobal];
|
|
36702
|
+
const trimmed = finalDropped.length > 0;
|
|
36703
|
+
const keptSize = totalSize(finalKept);
|
|
36704
|
+
const overflowBy = Math.max(0, keptSize - spec.totalBytes);
|
|
36705
|
+
const handles = [];
|
|
36706
|
+
for (const item of finalDropped) {
|
|
36707
|
+
if (item.handle && item.handle.trim() !== "") {
|
|
36708
|
+
handles.push(item.handle);
|
|
36709
|
+
}
|
|
36710
|
+
}
|
|
36711
|
+
return { kept: finalKept, dropped: finalDropped, handles, trimmed, overflowBy };
|
|
36712
|
+
}
|
|
36713
|
+
function totalSize(items) {
|
|
36714
|
+
return items.reduce((sum, i) => sum + i.size, 0);
|
|
36715
|
+
}
|
|
36716
|
+
|
|
36756
36717
|
// ../../packages/schemas/dist/index.js
|
|
36757
36718
|
var mrrlinTaskStatusSchema = external_exports.enum([
|
|
36758
36719
|
"todo",
|
|
@@ -36984,6 +36945,7 @@ var mrrlinTaskEventSchema = external_exports.object({
|
|
|
36984
36945
|
});
|
|
36985
36946
|
var mrrlinWikiPageSchema = external_exports.object({
|
|
36986
36947
|
contentHash: external_exports.string().default(""),
|
|
36948
|
+
createdAt: external_exports.string().datetime().nullable().default(null),
|
|
36987
36949
|
folder: external_exports.string().min(1),
|
|
36988
36950
|
id: mrrlinWikiPageIdSchema,
|
|
36989
36951
|
markdownBody: external_exports.string().min(1),
|
|
@@ -36991,7 +36953,8 @@ var mrrlinWikiPageSchema = external_exports.object({
|
|
|
36991
36953
|
preview: external_exports.string(),
|
|
36992
36954
|
specReady: external_exports.boolean().default(false),
|
|
36993
36955
|
taskId: mrrlinTaskIdSchema.nullable().default(null),
|
|
36994
|
-
title: external_exports.string().min(1)
|
|
36956
|
+
title: external_exports.string().min(1),
|
|
36957
|
+
updatedAt: external_exports.string().datetime().nullable().default(null)
|
|
36995
36958
|
});
|
|
36996
36959
|
var mrrlinMarkSpecReadyRequestSchema = external_exports.object({
|
|
36997
36960
|
ready: external_exports.boolean()
|
|
@@ -37115,14 +37078,12 @@ var mrrlinProjectSnapshotSchema = external_exports.object({
|
|
|
37115
37078
|
taxonomy: external_exports.array(mrrlinTaxonomyEntrySchema).default([]),
|
|
37116
37079
|
wikiPages: external_exports.array(mrrlinWikiPageSchema).default([])
|
|
37117
37080
|
});
|
|
37118
|
-
var
|
|
37081
|
+
var mrrlinTaskCreateShapeSchema = external_exports.object({
|
|
37119
37082
|
// Auto-deploy design Part B: input-optional (NOT .default("off")) so createTask can tell
|
|
37120
37083
|
// "caller omitted this" apart from "caller explicitly chose off" -- omitted falls through to
|
|
37121
37084
|
// the project's default_auto_deploy; the tasks column itself still defaults to 'off' at the
|
|
37122
37085
|
// SQL level for any direct-insert path that bypasses createTask entirely.
|
|
37123
37086
|
autoDeploy: mrrlinAutoDeploySchema.optional(),
|
|
37124
|
-
// Input-optional so existing createTask call sites need no churn; createTask fills 'code' for the
|
|
37125
|
-
// INSERT (the pg column is NOT NULL, so a concrete value is required, not undefined→NULL).
|
|
37126
37087
|
domain: mrrlinTaskDomainSchema.optional(),
|
|
37127
37088
|
autonomyLevel: mrrlinAutonomyLevelSchema.default("human_review"),
|
|
37128
37089
|
autonomyReason: external_exports.string().min(1).max(240).default("Needs human review before automation."),
|
|
@@ -37144,6 +37105,17 @@ var mrrlinTaskCreateSchema = external_exports.object({
|
|
|
37144
37105
|
type: mrrlinTaskTypeSchema.default("task"),
|
|
37145
37106
|
verificationDueAt: external_exports.string().datetime().nullable().optional()
|
|
37146
37107
|
});
|
|
37108
|
+
function refineTaskCreateDomain(data, ctx) {
|
|
37109
|
+
if (data.domain === void 0 && data.parentTaskId == null) {
|
|
37110
|
+
ctx.addIssue({
|
|
37111
|
+
code: external_exports.ZodIssueCode.custom,
|
|
37112
|
+
path: ["domain"],
|
|
37113
|
+
message: "domain is required unless parentTaskId is set (a follow-up inherits its parent's domain)"
|
|
37114
|
+
});
|
|
37115
|
+
}
|
|
37116
|
+
}
|
|
37117
|
+
var mrrlinTaskCreateSchema = mrrlinTaskCreateShapeSchema.superRefine(refineTaskCreateDomain);
|
|
37118
|
+
var mrrlinTaskCreateWithoutIdSchema = mrrlinTaskCreateShapeSchema.omit({ id: true }).superRefine(refineTaskCreateDomain);
|
|
37147
37119
|
var mrrlinTaskUpdateSchema = external_exports.object({
|
|
37148
37120
|
actor: external_exports.string().min(1).optional(),
|
|
37149
37121
|
assignee: mrrlinTaskAssigneeSchema.optional(),
|
|
@@ -37261,13 +37233,19 @@ var mrrlinWikiPageSearchQuerySchema = external_exports.object({
|
|
|
37261
37233
|
q: external_exports.string().min(2)
|
|
37262
37234
|
});
|
|
37263
37235
|
var mrrlinWikiPageSearchResultSchema = external_exports.object({
|
|
37236
|
+
ageDays: external_exports.number().nullable(),
|
|
37264
37237
|
folder: external_exports.string().min(1),
|
|
37265
37238
|
id: mrrlinWikiPageIdSchema,
|
|
37239
|
+
// Task-relative; always null from a generic (non-task-scoped) search — resolved only where
|
|
37240
|
+
// a caller has task context (director-loop retrieve()), never at this layer or in the API (D14).
|
|
37241
|
+
memoryClass: external_exports.enum(["normative", "historical"]),
|
|
37266
37242
|
path: external_exports.string().min(1),
|
|
37243
|
+
precedence: external_exports.union([external_exports.literal(0), external_exports.literal(1), external_exports.literal(2), external_exports.literal(3)]).nullable(),
|
|
37267
37244
|
preview: external_exports.string(),
|
|
37268
37245
|
rank: external_exports.number(),
|
|
37269
37246
|
snippet: external_exports.string(),
|
|
37270
|
-
title: external_exports.string().min(1)
|
|
37247
|
+
title: external_exports.string().min(1),
|
|
37248
|
+
updatedAt: external_exports.string().datetime().nullable()
|
|
37271
37249
|
});
|
|
37272
37250
|
var wikiKnowledgeChunkSchema = external_exports.object({
|
|
37273
37251
|
chunk_text: external_exports.string().min(1),
|
|
@@ -38093,6 +38071,210 @@ var inboxItemsMarkReadResponseSchema = external_exports.object({
|
|
|
38093
38071
|
var inboxItemResponseSchema = external_exports.object({ data: mrrlinInboxItemSchema });
|
|
38094
38072
|
var inboxItemListResponseSchema = external_exports.object({ data: external_exports.array(mrrlinInboxItemSchema) });
|
|
38095
38073
|
|
|
38074
|
+
// ../../packages/codex-client/dist/render-context-bundle.js
|
|
38075
|
+
var DEFAULT_SPEC_BUDGET_CHARS = 6e3;
|
|
38076
|
+
var DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS = 400;
|
|
38077
|
+
function renderContextBundle(input) {
|
|
38078
|
+
const sections = [];
|
|
38079
|
+
sections.push(renderTaskSummary(input.task));
|
|
38080
|
+
sections.push(renderSpec(input.specMarkdown, input.compact));
|
|
38081
|
+
const { keptRuns, keptArtifacts, keptAutonomy, anyHistoricalDropped, droppedHandles } = applyHistoricalBudgetIfNeeded(input);
|
|
38082
|
+
sections.push(renderRunHistory(keptRuns, input.openRunId, input.compact, anyHistoricalDropped));
|
|
38083
|
+
sections.push(renderOpenRun(input.openRunId));
|
|
38084
|
+
sections.push(renderArtifacts(keptArtifacts, input.omittedArtifactCount ?? 0, input.compact, anyHistoricalDropped, droppedHandles));
|
|
38085
|
+
sections.push(renderAutonomyEvents(keptAutonomy, input.compact, anyHistoricalDropped));
|
|
38086
|
+
return sections.join("\n\n");
|
|
38087
|
+
}
|
|
38088
|
+
function renderTaskSummary(task) {
|
|
38089
|
+
const lines2 = [
|
|
38090
|
+
"## Task (normative)",
|
|
38091
|
+
`id: ${task.id}`,
|
|
38092
|
+
`title: ${task.title}`,
|
|
38093
|
+
`autonomy: ${task.autonomyLevel} (${task.autonomyReason})`
|
|
38094
|
+
];
|
|
38095
|
+
if (task.notes)
|
|
38096
|
+
lines2.push(`description: ${task.notes}`);
|
|
38097
|
+
if (task.latestJudgement) {
|
|
38098
|
+
lines2.push(`latest judgement (${task.latestJudgementKind ?? "unknown"}): ${task.latestJudgement}`);
|
|
38099
|
+
}
|
|
38100
|
+
return lines2.join("\n");
|
|
38101
|
+
}
|
|
38102
|
+
function renderSpec(specMarkdown, compact) {
|
|
38103
|
+
if (!specMarkdown)
|
|
38104
|
+
return "## Spec (normative)\n(none)";
|
|
38105
|
+
if (!compact)
|
|
38106
|
+
return `## Spec (normative)
|
|
38107
|
+
${specMarkdown}`;
|
|
38108
|
+
const budget = compact.specBudgetChars ?? DEFAULT_SPEC_BUDGET_CHARS;
|
|
38109
|
+
if (specMarkdown.length <= budget)
|
|
38110
|
+
return `## Spec (normative)
|
|
38111
|
+
${specMarkdown}`;
|
|
38112
|
+
return `## Spec (normative)
|
|
38113
|
+
${compactSpecMarkdown(specMarkdown, budget, compact.projectSlug ?? null, compact.specWikiPageId ?? null)}`;
|
|
38114
|
+
}
|
|
38115
|
+
function compactSpecMarkdown(markdown, totalBudget, projectSlug, specWikiPageId) {
|
|
38116
|
+
const lines2 = markdown.split(/\r?\n/);
|
|
38117
|
+
const sections = [{ heading: "", body: [] }];
|
|
38118
|
+
for (const line of lines2) {
|
|
38119
|
+
if (/^#{1,6}\s/.test(line)) {
|
|
38120
|
+
sections.push({ heading: line, body: [] });
|
|
38121
|
+
} else {
|
|
38122
|
+
sections[sections.length - 1]?.body.push(line);
|
|
38123
|
+
}
|
|
38124
|
+
}
|
|
38125
|
+
const sectionCount = Math.max(1, sections.length);
|
|
38126
|
+
const perSectionBudget = Math.max(120, Math.floor(totalBudget / sectionCount));
|
|
38127
|
+
const out = [];
|
|
38128
|
+
for (const section of sections) {
|
|
38129
|
+
const bodyText = section.body.join("\n").replace(/^\s+|\s+$/g, "");
|
|
38130
|
+
if (section.heading)
|
|
38131
|
+
out.push(section.heading);
|
|
38132
|
+
if (!bodyText)
|
|
38133
|
+
continue;
|
|
38134
|
+
if (bodyText.length <= perSectionBudget) {
|
|
38135
|
+
out.push(bodyText);
|
|
38136
|
+
} else {
|
|
38137
|
+
const kept = bodyText.slice(0, perSectionBudget);
|
|
38138
|
+
const omitted = bodyText.length - perSectionBudget;
|
|
38139
|
+
out.push(`${kept}
|
|
38140
|
+
\u2026(${omitted} chars omitted; retrieve full spec for the rest)`);
|
|
38141
|
+
}
|
|
38142
|
+
}
|
|
38143
|
+
if (projectSlug && specWikiPageId) {
|
|
38144
|
+
out.push("");
|
|
38145
|
+
out.push(`> Compact spec. Source: mrrlin://wiki/${projectSlug}/${specWikiPageId} \u2014 call retrieve_context with this handle before any irreversible decision.`);
|
|
38146
|
+
} else {
|
|
38147
|
+
out.push("");
|
|
38148
|
+
out.push("> Compact spec. Retrieve the Specs/{id} wiki page via get_wiki_page before any irreversible decision.");
|
|
38149
|
+
}
|
|
38150
|
+
return out.join("\n");
|
|
38151
|
+
}
|
|
38152
|
+
function renderRunHistory(runs, openRunId, compact, anyHistoricalDropped) {
|
|
38153
|
+
if (runs.length === 0)
|
|
38154
|
+
return "## Execution-run history (historical)\n(none)";
|
|
38155
|
+
const lines2 = ["## Execution-run history (historical)"];
|
|
38156
|
+
for (const run3 of runs) {
|
|
38157
|
+
const marker = run3.id === openRunId ? " (open)" : "";
|
|
38158
|
+
lines2.push(`- ${run3.id} [${run3.status}] created ${run3.createdAt}${marker}`);
|
|
38159
|
+
}
|
|
38160
|
+
if (compact && anyHistoricalDropped) {
|
|
38161
|
+
lines2.push("> Compact execution-run history. Use retrieve_context with a `mrrlin://run/{projectSlug}/{runId}/artifacts` handle (substitute the slug and run id from the lines above) to load a run's full artifact history.");
|
|
38162
|
+
}
|
|
38163
|
+
return lines2.join("\n");
|
|
38164
|
+
}
|
|
38165
|
+
function renderOpenRun(openRunId) {
|
|
38166
|
+
return `## Open run (normative)
|
|
38167
|
+
${openRunId ?? "(none)"}`;
|
|
38168
|
+
}
|
|
38169
|
+
function renderArtifacts(artifacts, omittedCount, compact, anyHistoricalDropped, droppedHandles) {
|
|
38170
|
+
if (artifacts.length === 0 && omittedCount === 0)
|
|
38171
|
+
return "## Artifacts (historical)\n(none)";
|
|
38172
|
+
const lines2 = ["## Artifacts (historical)"];
|
|
38173
|
+
if (omittedCount > 0) {
|
|
38174
|
+
lines2.push(`(+${omittedCount} older artifact${omittedCount === 1 ? "" : "s"} omitted)`);
|
|
38175
|
+
}
|
|
38176
|
+
let truncatedAny = false;
|
|
38177
|
+
for (const artifact of artifacts) {
|
|
38178
|
+
if (artifact.kind === "file") {
|
|
38179
|
+
const p = artifact.payload;
|
|
38180
|
+
if (p.filename && p.stableUrl) {
|
|
38181
|
+
lines2.push(`- ${artifact.createdAt} [file] ${p.filename} (${p.class ?? "?"}, ${p.sizeBytes ?? "?"} bytes) ${p.stableUrl}`);
|
|
38182
|
+
continue;
|
|
38183
|
+
}
|
|
38184
|
+
}
|
|
38185
|
+
const payloadText = JSON.stringify(artifact.payload);
|
|
38186
|
+
const budget = compact?.artifactPayloadBudgetChars ?? DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS;
|
|
38187
|
+
const willTruncate = !!compact && payloadText.length > budget;
|
|
38188
|
+
const rendered = willTruncate ? `${payloadText.slice(0, budget)}\u2026(${payloadText.length - budget} chars omitted)` : payloadText;
|
|
38189
|
+
if (willTruncate)
|
|
38190
|
+
truncatedAny = true;
|
|
38191
|
+
lines2.push(`- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${rendered}`);
|
|
38192
|
+
}
|
|
38193
|
+
if (compact && (omittedCount > 0 || truncatedAny || anyHistoricalDropped)) {
|
|
38194
|
+
lines2.push("> Compact artifacts. Use retrieve_context with a `mrrlin://run/{projectSlug}/{runId}/artifacts` handle (substitute the slug and run id from the lines above) to load a run's full artifact history.");
|
|
38195
|
+
}
|
|
38196
|
+
if (compact && droppedHandles.length > 0) {
|
|
38197
|
+
lines2.push(`> Dropped by budget, retrievable via: ${droppedHandles.map((h) => `\`${h}\``).join(", ")}`);
|
|
38198
|
+
}
|
|
38199
|
+
return lines2.join("\n");
|
|
38200
|
+
}
|
|
38201
|
+
function renderAutonomyEvents(events, compact, anyHistoricalDropped) {
|
|
38202
|
+
if (events.length === 0)
|
|
38203
|
+
return "## Autonomy history (historical)\n(none)";
|
|
38204
|
+
const lines2 = ["## Autonomy history (historical)"];
|
|
38205
|
+
for (const event of events) {
|
|
38206
|
+
lines2.push(`- ${event.createdAt} ${event.autonomyLevel ?? "?"} (${event.autonomyReason ?? ""})`);
|
|
38207
|
+
}
|
|
38208
|
+
if (compact && anyHistoricalDropped) {
|
|
38209
|
+
lines2.push("> Compact autonomy history. Use retrieve_context with a `mrrlin://run/{projectSlug}/{runId}/artifacts` handle (substitute the slug and run id from the lines above) to load a run's full artifact history.");
|
|
38210
|
+
}
|
|
38211
|
+
return lines2.join("\n");
|
|
38212
|
+
}
|
|
38213
|
+
function applyHistoricalBudgetIfNeeded(input) {
|
|
38214
|
+
if (!input.compact?.budget) {
|
|
38215
|
+
return {
|
|
38216
|
+
keptRuns: input.runs,
|
|
38217
|
+
keptArtifacts: input.artifacts,
|
|
38218
|
+
keptAutonomy: input.autonomyEvents,
|
|
38219
|
+
anyHistoricalDropped: false,
|
|
38220
|
+
droppedHandles: []
|
|
38221
|
+
};
|
|
38222
|
+
}
|
|
38223
|
+
const candidates = [];
|
|
38224
|
+
for (const run3 of input.runs) {
|
|
38225
|
+
const line = `- ${run3.id} [${run3.status}] created ${run3.createdAt}`;
|
|
38226
|
+
candidates.push({
|
|
38227
|
+
kind: "run",
|
|
38228
|
+
runId: run3.id,
|
|
38229
|
+
size: line.length,
|
|
38230
|
+
memoryClass: "historical",
|
|
38231
|
+
precedence: 3,
|
|
38232
|
+
handle: input.compact.projectSlug ? `mrrlin://run/${input.compact.projectSlug}/${run3.id}/artifacts` : null,
|
|
38233
|
+
source: "run_history"
|
|
38234
|
+
});
|
|
38235
|
+
}
|
|
38236
|
+
for (const artifact of input.artifacts) {
|
|
38237
|
+
const line = `- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${JSON.stringify(artifact.payload)}`;
|
|
38238
|
+
candidates.push({
|
|
38239
|
+
kind: "artifact",
|
|
38240
|
+
artifactId: artifact.id,
|
|
38241
|
+
size: line.length,
|
|
38242
|
+
memoryClass: "historical",
|
|
38243
|
+
precedence: 3,
|
|
38244
|
+
handle: input.compact.projectSlug ? `mrrlin://run/${input.compact.projectSlug}/${artifact.runId}/artifacts` : null,
|
|
38245
|
+
source: "artifacts"
|
|
38246
|
+
});
|
|
38247
|
+
}
|
|
38248
|
+
for (const event of input.autonomyEvents) {
|
|
38249
|
+
const line = `- ${event.createdAt} ${event.autonomyLevel ?? "?"} (${event.autonomyReason ?? ""})`;
|
|
38250
|
+
candidates.push({
|
|
38251
|
+
kind: "autonomy",
|
|
38252
|
+
autonomyEventId: event.id,
|
|
38253
|
+
size: line.length,
|
|
38254
|
+
memoryClass: "historical",
|
|
38255
|
+
precedence: 3,
|
|
38256
|
+
handle: null,
|
|
38257
|
+
source: "autonomy"
|
|
38258
|
+
});
|
|
38259
|
+
}
|
|
38260
|
+
const { kept, dropped } = applyBudget(candidates, input.compact.budget);
|
|
38261
|
+
const keptRunIds = new Set(kept.filter((c) => c.kind === "run").map((c) => c.runId));
|
|
38262
|
+
const keptArtifactIds = new Set(kept.filter((c) => c.kind === "artifact").map((c) => c.artifactId));
|
|
38263
|
+
const keptAutonomyIds = new Set(kept.filter((c) => c.kind === "autonomy").map((c) => c.autonomyEventId));
|
|
38264
|
+
const droppedHandles = [];
|
|
38265
|
+
for (const item of dropped) {
|
|
38266
|
+
if (item.handle)
|
|
38267
|
+
droppedHandles.push(item.handle);
|
|
38268
|
+
}
|
|
38269
|
+
return {
|
|
38270
|
+
keptRuns: input.runs.filter((r) => keptRunIds.has(r.id)),
|
|
38271
|
+
keptArtifacts: input.artifacts.filter((a) => keptArtifactIds.has(a.id)),
|
|
38272
|
+
keptAutonomy: input.autonomyEvents.filter((e) => keptAutonomyIds.has(e.id)),
|
|
38273
|
+
anyHistoricalDropped: dropped.length > 0,
|
|
38274
|
+
droppedHandles
|
|
38275
|
+
};
|
|
38276
|
+
}
|
|
38277
|
+
|
|
38096
38278
|
// ../../packages/client/dist/agent-token-provider.js
|
|
38097
38279
|
var AgentTokenProviderError = class extends Error {
|
|
38098
38280
|
code;
|
|
@@ -39460,7 +39642,7 @@ function startCodexProbe(deps = {}) {
|
|
|
39460
39642
|
}
|
|
39461
39643
|
|
|
39462
39644
|
// src/_generated/version.ts
|
|
39463
|
-
var PKG_VERSION = "0.3.
|
|
39645
|
+
var PKG_VERSION = "0.3.60";
|
|
39464
39646
|
|
|
39465
39647
|
// src/api-base-url-migrate.ts
|
|
39466
39648
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
@@ -40276,13 +40458,26 @@ var HEADS_BY_DOMAIN = {
|
|
|
40276
40458
|
content: ["plan-reviewer", "scope-analyst", "content-editor", "compliance-reviewer"]
|
|
40277
40459
|
};
|
|
40278
40460
|
var SPEC_HEADS = HEADS_BY_DOMAIN.code;
|
|
40461
|
+
var CODE_HEADS_BY_IMPACT = {
|
|
40462
|
+
high: HEADS_BY_DOMAIN.code,
|
|
40463
|
+
medium: ["plan-reviewer", "security-analyst", "scope-analyst"],
|
|
40464
|
+
low: ["plan-reviewer", "scope-analyst"]
|
|
40465
|
+
};
|
|
40279
40466
|
function normalizeDomain(value) {
|
|
40280
40467
|
if (typeof value !== "string") return DEFAULT_DOMAIN;
|
|
40281
40468
|
const v = value.trim().toLowerCase();
|
|
40282
40469
|
return DOMAINS.includes(v) ? v : DEFAULT_DOMAIN;
|
|
40283
40470
|
}
|
|
40284
|
-
function
|
|
40285
|
-
|
|
40471
|
+
function normalizeImpact(value) {
|
|
40472
|
+
if (typeof value !== "string") return null;
|
|
40473
|
+
const v = value.trim().toLowerCase();
|
|
40474
|
+
return v === "low" || v === "medium" || v === "high" ? v : null;
|
|
40475
|
+
}
|
|
40476
|
+
function resolveHeads(domain2, impact) {
|
|
40477
|
+
const d = normalizeDomain(domain2);
|
|
40478
|
+
if (d !== "code") return [...HEADS_BY_DOMAIN[d]];
|
|
40479
|
+
const i = normalizeImpact(impact) ?? "high";
|
|
40480
|
+
return [...CODE_HEADS_BY_IMPACT[i]];
|
|
40286
40481
|
}
|
|
40287
40482
|
var PERSONAS_BY_DOMAIN = {
|
|
40288
40483
|
seo: "seo-writer",
|
|
@@ -40729,7 +40924,7 @@ function loadPersona(name) {
|
|
|
40729
40924
|
personaCache.set(name, content);
|
|
40730
40925
|
return content;
|
|
40731
40926
|
}
|
|
40732
|
-
function specHeadIntentBlock(taskId, title, seed) {
|
|
40927
|
+
function specHeadIntentBlock(taskId, title, seed, impact, adjudicated) {
|
|
40733
40928
|
const identity = taskId ? `--- TASK IDENTITY (the spec MUST be about THIS task; if its scope or direction contradicts this identity, raise a \`scope\` issue instead of steering the spec elsewhere) ---
|
|
40734
40929
|
TASK ${taskId}: ${title ?? ""}
|
|
40735
40930
|
|
|
@@ -40738,7 +40933,15 @@ TASK ${taskId}: ${title ?? ""}
|
|
|
40738
40933
|
${seed}
|
|
40739
40934
|
|
|
40740
40935
|
` : "";
|
|
40741
|
-
|
|
40936
|
+
const impactLine = impact ? `--- IMPACT: ${impact} ---
|
|
40937
|
+
|
|
40938
|
+
` : "";
|
|
40939
|
+
const adjudicatedBlock = adjudicated && adjudicated.length > 0 ? `--- ADJUDICATED \u2014 the operator already decided these; do not re-raise without new information ---
|
|
40940
|
+
${adjudicated.map((q) => `- Q: ${q.body}
|
|
40941
|
+
${q.status === "rejected" ? "(rejected" + (q.answer ? `: ${q.answer}` : "") + ")" : `A: ${q.answer ?? "(acknowledged, no answer recorded)"}`}`).join("\n")}
|
|
40942
|
+
|
|
40943
|
+
` : "";
|
|
40944
|
+
return `${identity}${intent}${impactLine}${adjudicatedBlock}`;
|
|
40742
40945
|
}
|
|
40743
40946
|
function reviserIdentityInstructions(taskId, title) {
|
|
40744
40947
|
const lock = taskId ? `This spec gates task ${taskId}${title ? ` ("${title}")` : ""}. You MUST NOT change the spec's task identity or heading, retarget it to a different task id, or invert its direction (e.g. live-submission vs preparation-only). Preserve the leading \`task: ${taskId}\` frontmatter EXACTLY; never change it to another task id. Address ONLY the accepted issues.
|
|
@@ -40749,7 +40952,7 @@ function reviserIdentityInstructions(taskId, title) {
|
|
|
40749
40952
|
function buildSpecGateDeps(args) {
|
|
40750
40953
|
const { client, projectSlug, specPageId, cwd, codexExecutable, extraHeadCount = 0 } = args;
|
|
40751
40954
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
40752
|
-
const maxHeadCalls = rounds * (4 + extraHeadCount + 1);
|
|
40955
|
+
const maxHeadCalls = 2 * rounds * (4 + extraHeadCount + 1);
|
|
40753
40956
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
40754
40957
|
let intentPromise = null;
|
|
40755
40958
|
function loadIntent() {
|
|
@@ -40777,7 +40980,21 @@ function buildSpecGateDeps(args) {
|
|
|
40777
40980
|
resolvedSeed = "(No operator statement on record for this task. Derived from the task's own notes \u2014 the following acceptance criteria were extracted verbatim:)\n\n" + parsed.items.map((c) => `- ${c}`).join("\n");
|
|
40778
40981
|
}
|
|
40779
40982
|
}
|
|
40780
|
-
|
|
40983
|
+
const taskInboxItems = await client.listInboxItems(projectSlug, { taskId });
|
|
40984
|
+
const adjudicated = taskInboxItems.filter((item) => item.origin === "gate" && (item.status === "acknowledged" || item.status === "rejected")).map((item) => ({
|
|
40985
|
+
body: item.body,
|
|
40986
|
+
answer: item.answer,
|
|
40987
|
+
status: item.status
|
|
40988
|
+
}));
|
|
40989
|
+
return {
|
|
40990
|
+
taskId,
|
|
40991
|
+
seed: resolvedSeed,
|
|
40992
|
+
title: task.title ?? "",
|
|
40993
|
+
domain: task.domain,
|
|
40994
|
+
notes,
|
|
40995
|
+
impact: task.impact,
|
|
40996
|
+
adjudicated
|
|
40997
|
+
};
|
|
40781
40998
|
})();
|
|
40782
40999
|
}
|
|
40783
41000
|
return intentPromise;
|
|
@@ -40802,8 +41019,8 @@ function buildSpecGateDeps(args) {
|
|
|
40802
41019
|
},
|
|
40803
41020
|
async runHead(personaName, artifact, dismissed) {
|
|
40804
41021
|
const persona = loadPersona(personaName);
|
|
40805
|
-
const { taskId, title, seed } = await loadIntent();
|
|
40806
|
-
const intentBlock = specHeadIntentBlock(taskId, title, seed);
|
|
41022
|
+
const { taskId, title, seed, impact, adjudicated } = await loadIntent();
|
|
41023
|
+
const intentBlock = specHeadIntentBlock(taskId, title, seed, impact, adjudicated);
|
|
40807
41024
|
return runCodexExec(
|
|
40808
41025
|
{
|
|
40809
41026
|
prompt: `Review this spec against the operator's intent.
|
|
@@ -40913,7 +41130,7 @@ function buildCodeGateDeps(args) {
|
|
|
40913
41130
|
const { client, projectSlug, baseRef, cwd, codexExecutable, extraHeads } = args;
|
|
40914
41131
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
40915
41132
|
const heads = ["code-reviewer", ...extraHeads ?? []];
|
|
40916
|
-
const maxHeadCalls = rounds * (heads.length + 1);
|
|
41133
|
+
const maxHeadCalls = 2 * rounds * (heads.length + 1);
|
|
40917
41134
|
const headTimeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
40918
41135
|
const reviserTimeoutMs = Number(process.env["MRRLIN_CODEGATE_REVISER_TIMEOUT_MS"] ?? 6e5);
|
|
40919
41136
|
const gitArgs = { cwd, baseRef, run: defaultRunCmd };
|
|
@@ -40997,12 +41214,14 @@ async function buildDeliverableGateDeps(args) {
|
|
|
40997
41214
|
const page = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
40998
41215
|
const taskId = page.taskId ?? null;
|
|
40999
41216
|
let domain2 = "code";
|
|
41217
|
+
let impact;
|
|
41000
41218
|
let taskTitle = "";
|
|
41001
41219
|
let specPageId = null;
|
|
41002
41220
|
let criteria = [];
|
|
41003
41221
|
if (taskId) {
|
|
41004
41222
|
const task = await client.getTask(projectSlug, taskId);
|
|
41005
41223
|
domain2 = normalizeDomain(task?.domain);
|
|
41224
|
+
impact = task?.impact;
|
|
41006
41225
|
taskTitle = task?.title ?? "";
|
|
41007
41226
|
if (task?.specWikiPageId) {
|
|
41008
41227
|
specPageId = task.specWikiPageId;
|
|
@@ -41010,8 +41229,8 @@ async function buildDeliverableGateDeps(args) {
|
|
|
41010
41229
|
criteria = parseAcceptanceCriteria(specPage.markdownBody)?.items ?? [];
|
|
41011
41230
|
}
|
|
41012
41231
|
}
|
|
41013
|
-
const heads = resolveHeads(domain2);
|
|
41014
|
-
const maxHeadCalls = rounds * (heads.length + 1);
|
|
41232
|
+
const heads = resolveHeads(domain2, impact);
|
|
41233
|
+
const maxHeadCalls = 2 * rounds * (heads.length + 1);
|
|
41015
41234
|
const criteriaBlock = renderCriteriaBlock(criteria);
|
|
41016
41235
|
return {
|
|
41017
41236
|
heads,
|
|
@@ -41253,6 +41472,17 @@ var SPECS_FOLDER = "Specs";
|
|
|
41253
41472
|
var DRAFT_SPEC_TLDR_MAX_CHARS = 300;
|
|
41254
41473
|
var DRAFT_SPEC_PERSONA = "spec-author";
|
|
41255
41474
|
var PAGE_ID_MAX_ATTEMPTS = 5;
|
|
41475
|
+
function resolveSpecBudget(impact) {
|
|
41476
|
+
const tier = impact?.toLowerCase();
|
|
41477
|
+
switch (tier) {
|
|
41478
|
+
case "high":
|
|
41479
|
+
return { maxRequirements: 12, maxChars: 25e3 };
|
|
41480
|
+
case "medium":
|
|
41481
|
+
return { maxRequirements: 6, maxChars: 12e3 };
|
|
41482
|
+
default:
|
|
41483
|
+
return { maxRequirements: 3, maxChars: 6e3 };
|
|
41484
|
+
}
|
|
41485
|
+
}
|
|
41256
41486
|
function defaultSleep(ms) {
|
|
41257
41487
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41258
41488
|
}
|
|
@@ -41265,7 +41495,7 @@ function enforceSpecOutputContract(markdown, taskId, title) {
|
|
|
41265
41495
|
${body}`;
|
|
41266
41496
|
}
|
|
41267
41497
|
let tldrChars = 0;
|
|
41268
|
-
const tldrMatch = body.match(/(^##
|
|
41498
|
+
const tldrMatch = body.match(/(^## TL;DR\s*\n)([\s\S]*?)(?=\n## |$)/im);
|
|
41269
41499
|
if (tldrMatch && tldrMatch.index !== void 0) {
|
|
41270
41500
|
const heading = tldrMatch[1] ?? "";
|
|
41271
41501
|
const section = (tldrMatch[2] ?? "").trim();
|
|
@@ -41289,6 +41519,7 @@ function nextWikiPageId(pages) {
|
|
|
41289
41519
|
return `WK-${max + 1}`;
|
|
41290
41520
|
}
|
|
41291
41521
|
function buildDraftSpecPrompt(task, constitution) {
|
|
41522
|
+
const budget = resolveSpecBudget(task.impact);
|
|
41292
41523
|
const lines2 = [
|
|
41293
41524
|
`Write a complete implementation spec for the task below.`,
|
|
41294
41525
|
``,
|
|
@@ -41310,11 +41541,19 @@ function buildDraftSpecPrompt(task, constitution) {
|
|
|
41310
41541
|
}
|
|
41311
41542
|
lines2.push(
|
|
41312
41543
|
`--- OUTPUT CONTRACT (strict) ---`,
|
|
41313
|
-
`Return ONLY the full spec markdown, no commentary. Required structure:`,
|
|
41544
|
+
`Return ONLY the full spec markdown, no commentary. Required structure, in this order:`,
|
|
41314
41545
|
`- First line heading: \`# ${task.id} \u2014 ${task.title}\``,
|
|
41315
|
-
`-
|
|
41316
|
-
`-
|
|
41317
|
-
`
|
|
41546
|
+
`- \`## TL;DR\``,
|
|
41547
|
+
`- One or more \`### Requirement: <name>\` sections, each stating what the change SHALL do.`,
|
|
41548
|
+
` Each requirement has one or more \`#### Scenario: <name>\` sub-sections using WHEN/THEN`,
|
|
41549
|
+
` (optionally AND) to describe a concrete situation and its expected outcome.`,
|
|
41550
|
+
`- \`## Acceptance criteria\`: a markdown checklist (\`- [ ] ...\`) of concrete, verifiable`,
|
|
41551
|
+
` conditions \u2014 this is the machine-usable yardstick the review scores against.`,
|
|
41552
|
+
`- \`## Verification\`: the exact tests, commands, or manual checks that prove each`,
|
|
41553
|
+
` acceptance criterion holds.`,
|
|
41554
|
+
``,
|
|
41555
|
+
`The \`## TL;DR\` section MUST be at most ${DRAFT_SPEC_TLDR_MAX_CHARS} characters.`,
|
|
41556
|
+
`The spec MUST contain at most ${budget.maxRequirements} acceptance-criteria items and no more than ${budget.maxChars} characters total.`
|
|
41318
41557
|
);
|
|
41319
41558
|
return lines2.join("\n");
|
|
41320
41559
|
}
|
|
@@ -42316,6 +42555,13 @@ function readPositiveIntEnv(name, fallback) {
|
|
|
42316
42555
|
function delayMs(ms) {
|
|
42317
42556
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
42318
42557
|
}
|
|
42558
|
+
function executionBundleBudget() {
|
|
42559
|
+
if ((process.env.MRRLIN_RETRIEVAL_BUDGET_DISABLE ?? "").trim() === "1") return void 0;
|
|
42560
|
+
return {
|
|
42561
|
+
totalBytes: readPositiveIntEnv("MRRLIN_RETRIEVAL_BUDGET_BUNDLE_CHARS", 15500),
|
|
42562
|
+
roundTripCap: 0
|
|
42563
|
+
};
|
|
42564
|
+
}
|
|
42319
42565
|
function execTurnIdleTimeoutMs() {
|
|
42320
42566
|
return readPositiveIntEnv("MRRLIN_DIRECTOR_BRIDGE_EXEC_TURN_IDLE_TIMEOUT_SECONDS", DEFAULT_EXEC_TURN_IDLE_TIMEOUT_SECONDS) * 1e3;
|
|
42321
42567
|
}
|
|
@@ -42836,7 +43082,8 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
42836
43082
|
...context,
|
|
42837
43083
|
compact: {
|
|
42838
43084
|
projectSlug,
|
|
42839
|
-
specWikiPageId: task.specWikiPageId ?? null
|
|
43085
|
+
specWikiPageId: task.specWikiPageId ?? null,
|
|
43086
|
+
budget: executionBundleBudget()
|
|
42840
43087
|
}
|
|
42841
43088
|
} : context
|
|
42842
43089
|
);
|
|
@@ -43897,6 +44144,46 @@ function createBoundedSemaphore(slots) {
|
|
|
43897
44144
|
};
|
|
43898
44145
|
}
|
|
43899
44146
|
|
|
44147
|
+
// src/consensus/escalation.ts
|
|
44148
|
+
function diagnoseStall(history, configuredRounds) {
|
|
44149
|
+
const n = history.length;
|
|
44150
|
+
const nonRespPerRound = history.map((h) => h.headOutcomes.filter((o) => !o.responded).length);
|
|
44151
|
+
const infraRoundCount = nonRespPerRound.filter((c) => c >= 2).length;
|
|
44152
|
+
const infraQualifies = infraRoundCount > n / 2;
|
|
44153
|
+
if (infraQualifies) {
|
|
44154
|
+
const roundIndices = [];
|
|
44155
|
+
nonRespPerRound.forEach((c, i) => {
|
|
44156
|
+
if (c >= 2) roundIndices.push(i + 1);
|
|
44157
|
+
});
|
|
44158
|
+
const evidence2 = `INFRA basis: \u22652 non\u2011responding heads in ${infraRoundCount} of ${n} rounds (rounds ${roundIndices.join(",")})`;
|
|
44159
|
+
let grant = 3;
|
|
44160
|
+
const headroom = 2 * configuredRounds - n;
|
|
44161
|
+
grant = Math.max(1, Math.min(grant, headroom));
|
|
44162
|
+
return { diagnosis: "INFRA", grantedRounds: grant, evidence: evidence2 };
|
|
44163
|
+
}
|
|
44164
|
+
if (n >= 2) {
|
|
44165
|
+
const counts = history.map((h) => h.accepted.length);
|
|
44166
|
+
const last = counts[n - 1];
|
|
44167
|
+
const secondLast = counts[n - 2];
|
|
44168
|
+
const lastFall = last < secondLast;
|
|
44169
|
+
if (lastFall) {
|
|
44170
|
+
let falls = 1;
|
|
44171
|
+
if (n >= 3 && secondLast < counts[n - 3]) {
|
|
44172
|
+
falls++;
|
|
44173
|
+
}
|
|
44174
|
+
let grant = 2 + falls;
|
|
44175
|
+
if (grant > 5) grant = 5;
|
|
44176
|
+
const headroom = 2 * configuredRounds - n;
|
|
44177
|
+
grant = Math.max(1, Math.min(grant, headroom));
|
|
44178
|
+
const seqDesc = counts.map((c, i) => `round${i + 1}:${c}`).join(" \u2192 ");
|
|
44179
|
+
const evidence2 = `SUBSTANCE basis: accepted count fell (${seqDesc})`;
|
|
44180
|
+
return { diagnosis: "SUBSTANCE", grantedRounds: grant, evidence: evidence2 };
|
|
44181
|
+
}
|
|
44182
|
+
}
|
|
44183
|
+
const evidence = n === 0 ? "empty history" : `no infra or substance basis found in ${n} rounds`;
|
|
44184
|
+
return { diagnosis: "AMBIGUITY", grantedRounds: 0, evidence };
|
|
44185
|
+
}
|
|
44186
|
+
|
|
43900
44187
|
// src/consensus/loop.ts
|
|
43901
44188
|
async function runConsensus(input) {
|
|
43902
44189
|
const sem = createBoundedSemaphore(input.concurrency ?? input.heads.length);
|
|
@@ -43904,9 +44191,12 @@ async function runConsensus(input) {
|
|
|
43904
44191
|
let artifact = input.artifact;
|
|
43905
44192
|
let calls = 0;
|
|
43906
44193
|
const dismissedFwd = [];
|
|
43907
|
-
|
|
44194
|
+
let effectiveRounds = input.rounds;
|
|
44195
|
+
let escalated = false;
|
|
44196
|
+
let decision;
|
|
44197
|
+
for (let round = 1; round <= effectiveRounds; round++) {
|
|
43908
44198
|
if (calls + input.heads.length > input.maxHeadCalls) {
|
|
43909
|
-
return { outcome: "CONSENSUS_BLOCKED", rounds: round - 1, finalArtifact: artifact, history };
|
|
44199
|
+
return { outcome: "CONSENSUS_BLOCKED", rounds: round - 1, finalArtifact: artifact, history, ...decision ? { escalation: decision } : {} };
|
|
43910
44200
|
}
|
|
43911
44201
|
const dismissedBlock = dismissedFwd.length ? `PREVIOUSLY DISMISSED (do not re-raise unless new information):
|
|
43912
44202
|
${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
@@ -43930,12 +44220,19 @@ ${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
|
43930
44220
|
if (responding.length === 0) {
|
|
43931
44221
|
return { outcome: "CONSENSUS_UNAVAILABLE", rounds: round, finalArtifact: artifact, history };
|
|
43932
44222
|
}
|
|
44223
|
+
const headOutcomes = input.heads.map((persona) => ({
|
|
44224
|
+
persona,
|
|
44225
|
+
responded: false
|
|
44226
|
+
}));
|
|
43933
44227
|
const issues = [];
|
|
43934
|
-
const verdicts = [];
|
|
43935
44228
|
for (const { idx, res } of responding) {
|
|
43936
44229
|
const parsed = parseVerdict(res.text);
|
|
43937
|
-
verdicts.push(parsed.verdict);
|
|
43938
44230
|
parsed.issues.forEach((iss, n) => issues.push({ ...iss, id: `r${round}-h${idx}-${n}` }));
|
|
44231
|
+
const outcome = headOutcomes[idx];
|
|
44232
|
+
if (outcome) {
|
|
44233
|
+
outcome.responded = true;
|
|
44234
|
+
outcome.verdict = parsed.verdict;
|
|
44235
|
+
}
|
|
43939
44236
|
if (parsed.verdict !== "APPROVE" && parsed.issues.length === 0 && parsed.parseFallbacks.length === 0) {
|
|
43940
44237
|
issues.push({
|
|
43941
44238
|
category: "ambiguity",
|
|
@@ -43947,7 +44244,7 @@ ${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
|
43947
44244
|
let adj;
|
|
43948
44245
|
if (issues.length > 0) {
|
|
43949
44246
|
if (calls + 1 > input.maxHeadCalls) {
|
|
43950
|
-
return { outcome: "CONSENSUS_BLOCKED", rounds: round, finalArtifact: artifact, history };
|
|
44247
|
+
return { outcome: "CONSENSUS_BLOCKED", rounds: round, finalArtifact: artifact, history, ...decision ? { escalation: decision } : {} };
|
|
43951
44248
|
}
|
|
43952
44249
|
calls++;
|
|
43953
44250
|
try {
|
|
@@ -43958,17 +44255,25 @@ ${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
|
43958
44255
|
} else {
|
|
43959
44256
|
adj = { adjudicatedVerdict: "APPROVE", accepted: [], dismissed: [] };
|
|
43960
44257
|
}
|
|
43961
|
-
history.push({ round,
|
|
44258
|
+
history.push({ round, accepted: adj.accepted, dismissed: adj.dismissed, headOutcomes });
|
|
43962
44259
|
adj.dismissed.forEach((d) => dismissedFwd.push(`- ${d.id}: ${d.reason}`));
|
|
43963
44260
|
if (adj.accepted.length === 0) {
|
|
43964
|
-
return { outcome: "CONVERGED", rounds: round, finalArtifact: artifact, history };
|
|
44261
|
+
return { outcome: "CONVERGED", rounds: round, finalArtifact: artifact, history, ...decision ? { escalation: decision } : {} };
|
|
43965
44262
|
}
|
|
43966
|
-
if (round ===
|
|
43967
|
-
|
|
44263
|
+
if (round === effectiveRounds) {
|
|
44264
|
+
if (!decision) {
|
|
44265
|
+
decision = diagnoseStall(history, input.rounds);
|
|
44266
|
+
}
|
|
44267
|
+
if (decision.grantedRounds > 0 && !escalated) {
|
|
44268
|
+
escalated = true;
|
|
44269
|
+
effectiveRounds = Math.min(effectiveRounds + decision.grantedRounds, input.rounds * 2);
|
|
44270
|
+
} else {
|
|
44271
|
+
return { outcome: "CONSENSUS_BLOCKED", rounds: round, finalArtifact: artifact, history, ...decision ? { escalation: decision } : {} };
|
|
44272
|
+
}
|
|
43968
44273
|
}
|
|
43969
44274
|
artifact = await input.revise(artifact, adj.accepted);
|
|
43970
44275
|
}
|
|
43971
|
-
return { outcome: "CONSENSUS_BLOCKED", rounds:
|
|
44276
|
+
return { outcome: "CONSENSUS_BLOCKED", rounds: effectiveRounds, finalArtifact: artifact, history };
|
|
43972
44277
|
}
|
|
43973
44278
|
|
|
43974
44279
|
// src/consensus/deliverable-gate.ts
|
|
@@ -44018,7 +44323,12 @@ async function runDeliverableGate(_target, deps) {
|
|
|
44018
44323
|
whyBlocking: `reviewer raised a ${issue2.category} issue`,
|
|
44019
44324
|
category: issue2.category
|
|
44020
44325
|
}));
|
|
44021
|
-
return
|
|
44326
|
+
return {
|
|
44327
|
+
outcome: "CONSENSUS_BLOCKED",
|
|
44328
|
+
rounds: result.rounds,
|
|
44329
|
+
...clarifications.length > 0 ? { clarifications } : {},
|
|
44330
|
+
...result.escalation ? { escalation: result.escalation } : {}
|
|
44331
|
+
};
|
|
44022
44332
|
}
|
|
44023
44333
|
return { outcome: result.outcome, rounds: result.rounds };
|
|
44024
44334
|
}
|
|
@@ -48907,8 +49217,8 @@ var DEFAULT_DIRECTOR_BRIDGE_ALLOWED_ORIGINS = [
|
|
|
48907
49217
|
"http://[::1]:*",
|
|
48908
49218
|
// Staging Vercel team migrated 2026-07-28 (see docs/reference/deployments.md). The prior
|
|
48909
49219
|
// fnnzzz-fnnzzzs-projects web alias was dropped once no operator was left on an old build.
|
|
48910
|
-
// mrrlin2 hit usage limits and was replaced by mrrlin3 on 2026-08-11
|
|
48911
|
-
|
|
49220
|
+
// mrrlin2 hit usage limits and was replaced by mrrlin3 on 2026-08-11; the mrrlin2 web alias
|
|
49221
|
+
// was dropped the same day since no operator was left on that build either.
|
|
48912
49222
|
"https://mrrlin-web-mrrlin3.vercel.app",
|
|
48913
49223
|
"https://app.mrrlin.dev",
|
|
48914
49224
|
"https://mrrlin-web.vercel.app"
|
|
@@ -49148,6 +49458,7 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
49148
49458
|
"- Keep behavior contracts in the spec; implementation details belong in `## Implementation Plan`.",
|
|
49149
49459
|
"- Apply this format ONLY to Specs/{id} pages; do not retro-fit it onto other wiki page types.",
|
|
49150
49460
|
"- 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 consensus `info` item says CONVERGED, proceed to `create_execution_run`. 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. Report the spec's review outcome to the operator.",
|
|
49461
|
+
"- 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.",
|
|
49151
49462
|
'- 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.',
|
|
49152
49463
|
"- 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.",
|
|
49153
49464
|
"",
|
|
@@ -55104,6 +55415,44 @@ function mapAcceptedIssueToClarification(issue2) {
|
|
|
55104
55415
|
category: issue2.category
|
|
55105
55416
|
};
|
|
55106
55417
|
}
|
|
55418
|
+
var CLARIFICATION_CAP_BY_IMPACT = { low: 2, medium: 3, high: 5 };
|
|
55419
|
+
function resolveClarificationCap(impact) {
|
|
55420
|
+
const tier = impact?.trim().toLowerCase();
|
|
55421
|
+
if (tier === "medium") return CLARIFICATION_CAP_BY_IMPACT.medium;
|
|
55422
|
+
if (tier === "high") return CLARIFICATION_CAP_BY_IMPACT.high;
|
|
55423
|
+
return CLARIFICATION_CAP_BY_IMPACT.low;
|
|
55424
|
+
}
|
|
55425
|
+
function headIndexOf(id) {
|
|
55426
|
+
const match = /^r\d+-h(\d+)-\d+$/.exec(id);
|
|
55427
|
+
return match?.[1] !== void 0 ? Number(match[1]) : null;
|
|
55428
|
+
}
|
|
55429
|
+
function orderClarificationIssues(issues) {
|
|
55430
|
+
const distinctHeadsByKey = /* @__PURE__ */ new Map();
|
|
55431
|
+
for (const issue2 of issues) {
|
|
55432
|
+
const key = `${issue2.category} ${issue2.text}`;
|
|
55433
|
+
const heads = distinctHeadsByKey.get(key) ?? /* @__PURE__ */ new Set();
|
|
55434
|
+
const headIdx = headIndexOf(issue2.id);
|
|
55435
|
+
if (headIdx !== null) heads.add(headIdx);
|
|
55436
|
+
distinctHeadsByKey.set(key, heads);
|
|
55437
|
+
}
|
|
55438
|
+
const categoryRank = (category) => category === "scope" ? 0 : category === "ambiguity" ? 1 : 2;
|
|
55439
|
+
return issues.map((issue2, index) => ({ issue: issue2, index })).sort((a, b) => {
|
|
55440
|
+
const rankDiff = categoryRank(a.issue.category) - categoryRank(b.issue.category);
|
|
55441
|
+
if (rankDiff !== 0) return rankDiff;
|
|
55442
|
+
const headCountA = distinctHeadsByKey.get(`${a.issue.category} ${a.issue.text}`)?.size ?? 0;
|
|
55443
|
+
const headCountB = distinctHeadsByKey.get(`${b.issue.category} ${b.issue.text}`)?.size ?? 0;
|
|
55444
|
+
if (headCountA !== headCountB) return headCountB - headCountA;
|
|
55445
|
+
return a.index - b.index;
|
|
55446
|
+
}).map(({ issue: issue2 }) => issue2);
|
|
55447
|
+
}
|
|
55448
|
+
function applyClarificationCap(issues, impact) {
|
|
55449
|
+
const ordered = orderClarificationIssues(issues);
|
|
55450
|
+
const cap = resolveClarificationCap(impact);
|
|
55451
|
+
return {
|
|
55452
|
+
kept: ordered.slice(0, cap).map(mapAcceptedIssueToClarification),
|
|
55453
|
+
dropped: ordered.slice(cap).map(mapAcceptedIssueToClarification)
|
|
55454
|
+
};
|
|
55455
|
+
}
|
|
55107
55456
|
async function runSpecGate(_target, deps, opts) {
|
|
55108
55457
|
if (!await deps.consensusEnabled()) return { outcome: "SKIPPED" };
|
|
55109
55458
|
const intent = await deps.readIntent();
|
|
@@ -55121,11 +55470,11 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
55121
55470
|
]
|
|
55122
55471
|
};
|
|
55123
55472
|
}
|
|
55124
|
-
const baseHeads = resolveHeads(intent.domain);
|
|
55473
|
+
const baseHeads = resolveHeads(intent.domain, intent.impact);
|
|
55125
55474
|
const extraHeads = opts?.extraHeads ?? [];
|
|
55126
55475
|
const heads = [...baseHeads, ...extraHeads.filter((h) => !baseHeads.includes(h))];
|
|
55127
55476
|
const spec = await deps.readSpec();
|
|
55128
|
-
if (
|
|
55477
|
+
if (parseAcceptanceCriteria(spec.markdownBody) === null) {
|
|
55129
55478
|
return {
|
|
55130
55479
|
outcome: "CONSENSUS_BLOCKED",
|
|
55131
55480
|
operatorClarifications: [
|
|
@@ -55193,21 +55542,34 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
55193
55542
|
]
|
|
55194
55543
|
};
|
|
55195
55544
|
}
|
|
55545
|
+
let keptClarifications = [];
|
|
55546
|
+
let droppedByCap = [];
|
|
55547
|
+
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
55548
|
+
const accepted = result.history.at(-1)?.accepted ?? [];
|
|
55549
|
+
const materialIssues = accepted.filter((issue2) => issue2.category === "ambiguity" || issue2.category === "scope");
|
|
55550
|
+
const capped = applyClarificationCap(materialIssues, intent.impact);
|
|
55551
|
+
keptClarifications = capped.kept;
|
|
55552
|
+
droppedByCap = capped.dropped;
|
|
55553
|
+
}
|
|
55196
55554
|
await deps.writeEvidence({
|
|
55197
55555
|
specPageId: spec.pageId,
|
|
55198
55556
|
outcome: result.outcome,
|
|
55199
55557
|
rounds: result.rounds,
|
|
55200
55558
|
history: result.history,
|
|
55201
|
-
contentHash: currentHash
|
|
55559
|
+
contentHash: currentHash,
|
|
55560
|
+
...droppedByCap.length > 0 ? { droppedByCap } : {}
|
|
55202
55561
|
});
|
|
55203
55562
|
if (result.outcome === "CONVERGED") {
|
|
55204
55563
|
await deps.promoteSpecReady(currentHash);
|
|
55205
55564
|
return { outcome: "CONVERGED" };
|
|
55206
55565
|
}
|
|
55207
55566
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
55208
|
-
|
|
55209
|
-
|
|
55210
|
-
|
|
55567
|
+
return {
|
|
55568
|
+
outcome: "CONSENSUS_BLOCKED",
|
|
55569
|
+
...keptClarifications.length > 0 ? { operatorClarifications: keptClarifications } : {},
|
|
55570
|
+
...droppedByCap.length > 0 ? { droppedByCap } : {},
|
|
55571
|
+
...result.escalation ? { escalation: result.escalation } : {}
|
|
55572
|
+
};
|
|
55211
55573
|
}
|
|
55212
55574
|
return { outcome: result.outcome };
|
|
55213
55575
|
}
|
|
@@ -55486,7 +55848,7 @@ async function maybeRunShadow(input, deps, logger) {
|
|
|
55486
55848
|
const productionVerdict = productionVerdictFromOutcome(input.outcome);
|
|
55487
55849
|
if (productionVerdict === null) return false;
|
|
55488
55850
|
try {
|
|
55489
|
-
const heads = input.heads ?? [...resolveHeads(input.domain), ...input.extraHeads ?? []];
|
|
55851
|
+
const heads = input.heads ?? [...resolveHeads(input.domain, input.impact), ...input.extraHeads ?? []];
|
|
55490
55852
|
const personaInstructions = {};
|
|
55491
55853
|
for (const name of heads) {
|
|
55492
55854
|
try {
|
|
@@ -55528,7 +55890,7 @@ function multiLlmActive(env = process.env) {
|
|
|
55528
55890
|
async function resolveActiveRunHead(input, deps) {
|
|
55529
55891
|
if (!multiLlmActive(input.env ?? process.env)) return null;
|
|
55530
55892
|
try {
|
|
55531
|
-
const heads = input.heads ?? [...resolveHeads(input.domain), ...input.extraHeads ?? []];
|
|
55893
|
+
const heads = input.heads ?? [...resolveHeads(input.domain, input.impact), ...input.extraHeads ?? []];
|
|
55532
55894
|
if (heads.length === 0) return null;
|
|
55533
55895
|
const personaInstructions = {};
|
|
55534
55896
|
for (const name of heads) {
|
|
@@ -55558,6 +55920,35 @@ async function resolveActiveRunHead(input, deps) {
|
|
|
55558
55920
|
|
|
55559
55921
|
// src/async-tools/consensus.ts
|
|
55560
55922
|
var import_node_crypto9 = require("node:crypto");
|
|
55923
|
+
|
|
55924
|
+
// src/consensus/question-similarity.ts
|
|
55925
|
+
var SUPPRESSION_SIMILARITY_THRESHOLD = 0.6;
|
|
55926
|
+
function tokenSet(text) {
|
|
55927
|
+
const normalized = text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").trim();
|
|
55928
|
+
if (!normalized) return /* @__PURE__ */ new Set();
|
|
55929
|
+
return new Set(normalized.split(/\s+/));
|
|
55930
|
+
}
|
|
55931
|
+
function jaccardSimilarity(a, b) {
|
|
55932
|
+
const setA = tokenSet(a);
|
|
55933
|
+
const setB = tokenSet(b);
|
|
55934
|
+
if (setA.size === 0 || setB.size === 0) return 0;
|
|
55935
|
+
let intersection2 = 0;
|
|
55936
|
+
for (const token of setA) {
|
|
55937
|
+
if (setB.has(token)) intersection2 += 1;
|
|
55938
|
+
}
|
|
55939
|
+
const union2 = setA.size + setB.size - intersection2;
|
|
55940
|
+
return union2 === 0 ? 0 : intersection2 / union2;
|
|
55941
|
+
}
|
|
55942
|
+
function isCoveredByAdjudicated(rawQuestion, adjudicated) {
|
|
55943
|
+
for (const item of adjudicated) {
|
|
55944
|
+
if (jaccardSimilarity(rawQuestion, item.body) >= SUPPRESSION_SIMILARITY_THRESHOLD) {
|
|
55945
|
+
return item;
|
|
55946
|
+
}
|
|
55947
|
+
}
|
|
55948
|
+
return null;
|
|
55949
|
+
}
|
|
55950
|
+
|
|
55951
|
+
// src/async-tools/consensus.ts
|
|
55561
55952
|
function normalizeClarificationQuestion(question) {
|
|
55562
55953
|
return question.replace(/\s+/g, " ").trim().replace(/[?.!]+$/, "").trim();
|
|
55563
55954
|
}
|
|
@@ -55688,35 +56079,43 @@ async function cachedResult(client, input) {
|
|
|
55688
56079
|
}
|
|
55689
56080
|
return null;
|
|
55690
56081
|
}
|
|
56082
|
+
function stripHumanizeOpener(body) {
|
|
56083
|
+
for (const prefix of Object.values(HUMANIZE_TEMPLATE_BY_KIND)) {
|
|
56084
|
+
if (body.startsWith(prefix)) return body.slice(prefix.length).trim();
|
|
56085
|
+
}
|
|
56086
|
+
return body;
|
|
56087
|
+
}
|
|
55691
56088
|
async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
55692
56089
|
const operatorClarifications = result.operatorClarifications ?? [];
|
|
55693
56090
|
const diagnostics = result.diagnostics ?? [];
|
|
55694
|
-
|
|
56091
|
+
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
56092
|
+
const taskId = page?.taskId ?? null;
|
|
56093
|
+
if (!resolvedTaskId && taskId) resolvedTaskId = taskId;
|
|
56094
|
+
const adjudicated = [];
|
|
56095
|
+
if (taskId) {
|
|
55695
56096
|
try {
|
|
55696
|
-
const
|
|
55697
|
-
|
|
55698
|
-
|
|
55699
|
-
|
|
55700
|
-
|
|
55701
|
-
});
|
|
55702
|
-
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
55703
|
-
contentHash: domainScopedReviewHash(page.contentHash, domain2),
|
|
55704
|
-
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
55705
|
-
rounds: 0
|
|
55706
|
-
});
|
|
56097
|
+
const taskInboxItems = await ctx.client.listInboxItems(input.projectSlug, { taskId });
|
|
56098
|
+
for (const item of taskInboxItems) {
|
|
56099
|
+
if (item.origin === "gate" && (item.status === "acknowledged" || item.status === "rejected")) {
|
|
56100
|
+
adjudicated.push({ body: stripHumanizeOpener(item.body), answer: item.answer, status: item.status });
|
|
56101
|
+
}
|
|
55707
56102
|
}
|
|
55708
56103
|
} catch (error52) {
|
|
55709
56104
|
process.stderr.write(
|
|
55710
|
-
`[mrrlin-mcp consensus]
|
|
56105
|
+
`[mrrlin-mcp consensus] adjudicated-questions read failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
55711
56106
|
`
|
|
55712
56107
|
);
|
|
55713
56108
|
}
|
|
55714
56109
|
}
|
|
56110
|
+
const suppressedAsAdjudicated = [];
|
|
56111
|
+
const toFile = operatorClarifications.filter((clarification) => {
|
|
56112
|
+
const covering = isCoveredByAdjudicated(clarification.rawQuestion, adjudicated);
|
|
56113
|
+
if (!covering) return true;
|
|
56114
|
+
suppressedAsAdjudicated.push({ rawQuestion: clarification.rawQuestion, coveredByAnswer: covering.answer });
|
|
56115
|
+
return false;
|
|
56116
|
+
});
|
|
55715
56117
|
let filedQuestionCount = 0;
|
|
55716
|
-
if (
|
|
55717
|
-
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
55718
|
-
const taskId = page?.taskId ?? null;
|
|
55719
|
-
if (!resolvedTaskId && taskId) resolvedTaskId = taskId;
|
|
56118
|
+
if (toFile.length > 0) {
|
|
55720
56119
|
let planId = null;
|
|
55721
56120
|
if (!taskId) {
|
|
55722
56121
|
const plans = await ctx.client.listPlans(input.projectSlug);
|
|
@@ -55724,8 +56123,8 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55724
56123
|
}
|
|
55725
56124
|
if (taskId) {
|
|
55726
56125
|
const scopeId = taskId;
|
|
55727
|
-
const groupId =
|
|
55728
|
-
for (const clarification of
|
|
56126
|
+
const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto9.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
56127
|
+
for (const clarification of toFile) {
|
|
55729
56128
|
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
55730
56129
|
try {
|
|
55731
56130
|
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
@@ -55741,11 +56140,11 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55741
56140
|
} catch {
|
|
55742
56141
|
}
|
|
55743
56142
|
}
|
|
55744
|
-
filedQuestionCount =
|
|
56143
|
+
filedQuestionCount = toFile.length;
|
|
55745
56144
|
} else if (planId) {
|
|
55746
56145
|
const scopeId = planId;
|
|
55747
|
-
const groupId =
|
|
55748
|
-
for (const clarification of
|
|
56146
|
+
const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto9.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
56147
|
+
for (const clarification of toFile) {
|
|
55749
56148
|
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
55750
56149
|
try {
|
|
55751
56150
|
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
@@ -55761,7 +56160,32 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55761
56160
|
} catch {
|
|
55762
56161
|
}
|
|
55763
56162
|
}
|
|
55764
|
-
filedQuestionCount =
|
|
56163
|
+
filedQuestionCount = toFile.length;
|
|
56164
|
+
}
|
|
56165
|
+
}
|
|
56166
|
+
if (operatorClarifications.length > 0) {
|
|
56167
|
+
try {
|
|
56168
|
+
if (page) {
|
|
56169
|
+
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
56170
|
+
id: page.id,
|
|
56171
|
+
taskId: page.taskId ?? null
|
|
56172
|
+
});
|
|
56173
|
+
const suppressionEvidence = (result.droppedByCap?.length ?? 0) > 0 || suppressedAsAdjudicated.length > 0 ? {
|
|
56174
|
+
...result.droppedByCap?.length ? { droppedByCap: result.droppedByCap } : {},
|
|
56175
|
+
...suppressedAsAdjudicated.length ? { suppressedAsAdjudicated } : {}
|
|
56176
|
+
} : void 0;
|
|
56177
|
+
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
56178
|
+
contentHash: domainScopedReviewHash(page.contentHash, domain2),
|
|
56179
|
+
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
56180
|
+
rounds: 0,
|
|
56181
|
+
...suppressionEvidence ? { suppressionEvidence } : {}
|
|
56182
|
+
});
|
|
56183
|
+
}
|
|
56184
|
+
} catch (error52) {
|
|
56185
|
+
process.stderr.write(
|
|
56186
|
+
`[mrrlin-mcp consensus] clarification verdict write failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
56187
|
+
`
|
|
56188
|
+
);
|
|
55765
56189
|
}
|
|
55766
56190
|
}
|
|
55767
56191
|
let error51;
|
|
@@ -55769,11 +56193,16 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55769
56193
|
error51 = `Consensus blocked on ${filedQuestionCount} clarification(s); filed as operator question(s).`;
|
|
55770
56194
|
} else if (diagnostics.length > 0 && operatorClarifications.length === 0) {
|
|
55771
56195
|
error51 = "Consensus blocked: the spec content does not match its linked task. This requires fixing the spec/task relationship directly, not an inbox answer.";
|
|
56196
|
+
} else if (operatorClarifications.length > 0 && toFile.length === 0 && suppressedAsAdjudicated.length > 0) {
|
|
56197
|
+
error51 = `Consensus blocked, but all ${suppressedAsAdjudicated.length} clarification(s) were reworded repeats of already-adjudicated questions \u2014 nothing new filed.`;
|
|
55772
56198
|
} else if (operatorClarifications.length > 0) {
|
|
55773
56199
|
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.`;
|
|
55774
56200
|
} else {
|
|
55775
56201
|
error51 = "Consensus blocked: reviewers did not converge within budget.";
|
|
55776
56202
|
}
|
|
56203
|
+
if (result.escalation) {
|
|
56204
|
+
error51 += ` (diagnosis: ${result.escalation.diagnosis})`;
|
|
56205
|
+
}
|
|
55777
56206
|
return {
|
|
55778
56207
|
kind: "failed",
|
|
55779
56208
|
failureReason: "consensus_blocked",
|
|
@@ -56091,7 +56520,8 @@ function readCompactPolicy(env = process.env) {
|
|
|
56091
56520
|
enabled: !disabled,
|
|
56092
56521
|
thresholdChars: readPositiveInt(env.MRRLIN_MCP_COMPACT_THRESHOLD_CHARS, DEFAULT_THRESHOLD_CHARS),
|
|
56093
56522
|
maxItems: readPositiveInt(env.MRRLIN_MCP_COMPACT_MAX_ITEMS, DEFAULT_MAX_ITEMS),
|
|
56094
|
-
debug: (env.MRRLIN_MCP_COMPACT_DEBUG ?? "").trim() === "1"
|
|
56523
|
+
debug: (env.MRRLIN_MCP_COMPACT_DEBUG ?? "").trim() === "1",
|
|
56524
|
+
retrievalBudgetDisable: (env.MRRLIN_RETRIEVAL_BUDGET_DISABLE ?? "").trim() === "1"
|
|
56095
56525
|
};
|
|
56096
56526
|
}
|
|
56097
56527
|
|
|
@@ -56221,6 +56651,7 @@ function compactWikiPageList(projectSlug, pages, maxItems) {
|
|
|
56221
56651
|
title: truncate(p.title, TITLE_TRUNCATE_CHARS),
|
|
56222
56652
|
folder: p.folder,
|
|
56223
56653
|
path: p.path,
|
|
56654
|
+
updatedAt: p.updatedAt,
|
|
56224
56655
|
taskId: p.taskId,
|
|
56225
56656
|
specReady: p.specReady,
|
|
56226
56657
|
firstHeading: firstHeadingLine(p.markdownBody),
|
|
@@ -56248,6 +56679,10 @@ function compactWikiSearch(projectSlug, results, maxItems) {
|
|
|
56248
56679
|
folder: r.folder,
|
|
56249
56680
|
path: r.path,
|
|
56250
56681
|
rank: r.rank,
|
|
56682
|
+
updatedAt: r.updatedAt,
|
|
56683
|
+
ageDays: r.ageDays,
|
|
56684
|
+
memoryClass: r.memoryClass,
|
|
56685
|
+
precedence: r.precedence,
|
|
56251
56686
|
snippet: truncate(r.snippet, EXCERPT_TRUNCATE_CHARS),
|
|
56252
56687
|
handle: formatWikiHandle(projectSlug, r.id)
|
|
56253
56688
|
}));
|
|
@@ -56830,7 +57265,7 @@ var mcpToolDescriptions = {
|
|
|
56830
57265
|
[mcpToolNames.appendTaskEvent]: "Append an operator note to a task's activity timeline.",
|
|
56831
57266
|
[mcpToolNames.createWikiPage]: "Create a markdown Wiki page.",
|
|
56832
57267
|
[mcpToolNames.deleteTaskDependency]: "Delete a blocked-by relationship.",
|
|
56833
|
-
[mcpToolNames.createTask]: "Create a new task in a project. Always starts as status='todo' with a blank assignee. Pass parentTaskId when spawning a follow-up task to continue work from an existing task \u2014 its operator_seed_request is inherited automatically if you don't supply your own.",
|
|
57268
|
+
[mcpToolNames.createTask]: "Create a new task in a project. Always starts as status='todo' with a blank assignee. Pass parentTaskId when spawning a follow-up task to continue work from an existing task \u2014 its operator_seed_request is inherited automatically if you don't supply your own. Domain is required UNLESS parentTaskId is supplied (a follow-up inherits its parent's domain automatically). Domain selects which reviewer roster judges the task's spec. Valid values: code, seo, marketing, content.",
|
|
56834
57269
|
[mcpToolNames.getProject]: "Fetch a single project summary by slug.",
|
|
56835
57270
|
[mcpToolNames.getProjectSnapshot]: "Fetch the full project snapshot: GitHub binding, tasks, plans, and wiki pages.",
|
|
56836
57271
|
[mcpToolNames.getTask]: "Fetch a single task by project slug and task id.",
|