@mrrlin-dev/mcp 0.3.59 → 0.3.62
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
|
@@ -6543,7 +6543,7 @@ var require_truncate = __commonJS({
|
|
|
6543
6543
|
var parse5 = require_parse2();
|
|
6544
6544
|
var constants = require_constants2();
|
|
6545
6545
|
var SemVer = require_semver();
|
|
6546
|
-
var
|
|
6546
|
+
var truncate3 = (version2, truncation, options) => {
|
|
6547
6547
|
if (!constants.RELEASE_TYPES.includes(truncation)) {
|
|
6548
6548
|
return null;
|
|
6549
6549
|
}
|
|
@@ -6573,7 +6573,7 @@ var require_truncate = __commonJS({
|
|
|
6573
6573
|
var isPrerelease = (type) => {
|
|
6574
6574
|
return type.startsWith("pre");
|
|
6575
6575
|
};
|
|
6576
|
-
module2.exports =
|
|
6576
|
+
module2.exports = truncate3;
|
|
6577
6577
|
}
|
|
6578
6578
|
});
|
|
6579
6579
|
|
|
@@ -7611,7 +7611,7 @@ var require_semver2 = __commonJS({
|
|
|
7611
7611
|
var lte = require_lte();
|
|
7612
7612
|
var cmp = require_cmp();
|
|
7613
7613
|
var coerce = require_coerce();
|
|
7614
|
-
var
|
|
7614
|
+
var truncate3 = require_truncate();
|
|
7615
7615
|
var Comparator = require_comparator();
|
|
7616
7616
|
var Range = require_range();
|
|
7617
7617
|
var satisfies = require_satisfies();
|
|
@@ -7650,7 +7650,7 @@ var require_semver2 = __commonJS({
|
|
|
7650
7650
|
lte,
|
|
7651
7651
|
cmp,
|
|
7652
7652
|
coerce,
|
|
7653
|
-
truncate:
|
|
7653
|
+
truncate: truncate3,
|
|
7654
7654
|
Comparator,
|
|
7655
7655
|
Range,
|
|
7656
7656
|
satisfies,
|
|
@@ -21833,136 +21833,6 @@ async function acquireThread(codex, opts) {
|
|
|
21833
21833
|
return { threadId: newId, resumed: false };
|
|
21834
21834
|
}
|
|
21835
21835
|
|
|
21836
|
-
// ../../packages/codex-client/dist/render-context-bundle.js
|
|
21837
|
-
var DEFAULT_SPEC_BUDGET_CHARS = 6e3;
|
|
21838
|
-
var DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS = 400;
|
|
21839
|
-
function renderContextBundle(input) {
|
|
21840
|
-
const sections = [];
|
|
21841
|
-
sections.push(renderTaskSummary(input.task));
|
|
21842
|
-
sections.push(renderSpec(input.specMarkdown, input.compact));
|
|
21843
|
-
sections.push(renderRunHistory(input.runs, input.openRunId));
|
|
21844
|
-
sections.push(renderOpenRun(input.openRunId));
|
|
21845
|
-
sections.push(renderArtifacts(input.artifacts, input.omittedArtifactCount ?? 0, input.compact));
|
|
21846
|
-
sections.push(renderAutonomyEvents(input.autonomyEvents));
|
|
21847
|
-
return sections.join("\n\n");
|
|
21848
|
-
}
|
|
21849
|
-
function renderTaskSummary(task) {
|
|
21850
|
-
const lines2 = [
|
|
21851
|
-
"## Task",
|
|
21852
|
-
`id: ${task.id}`,
|
|
21853
|
-
`title: ${task.title}`,
|
|
21854
|
-
`autonomy: ${task.autonomyLevel} (${task.autonomyReason})`
|
|
21855
|
-
];
|
|
21856
|
-
if (task.notes)
|
|
21857
|
-
lines2.push(`description: ${task.notes}`);
|
|
21858
|
-
if (task.latestJudgement) {
|
|
21859
|
-
lines2.push(`latest judgement (${task.latestJudgementKind ?? "unknown"}): ${task.latestJudgement}`);
|
|
21860
|
-
}
|
|
21861
|
-
return lines2.join("\n");
|
|
21862
|
-
}
|
|
21863
|
-
function renderSpec(specMarkdown, compact) {
|
|
21864
|
-
if (!specMarkdown)
|
|
21865
|
-
return "## Spec\n(none)";
|
|
21866
|
-
if (!compact)
|
|
21867
|
-
return `## Spec
|
|
21868
|
-
${specMarkdown}`;
|
|
21869
|
-
const budget = compact.specBudgetChars ?? DEFAULT_SPEC_BUDGET_CHARS;
|
|
21870
|
-
if (specMarkdown.length <= budget)
|
|
21871
|
-
return `## Spec
|
|
21872
|
-
${specMarkdown}`;
|
|
21873
|
-
return `## Spec
|
|
21874
|
-
${compactSpecMarkdown(specMarkdown, budget, compact.projectSlug ?? null, compact.specWikiPageId ?? null)}`;
|
|
21875
|
-
}
|
|
21876
|
-
function compactSpecMarkdown(markdown, totalBudget, projectSlug, specWikiPageId) {
|
|
21877
|
-
const lines2 = markdown.split(/\r?\n/);
|
|
21878
|
-
const sections = [{ heading: "", body: [] }];
|
|
21879
|
-
for (const line of lines2) {
|
|
21880
|
-
if (/^#{1,6}\s/.test(line)) {
|
|
21881
|
-
sections.push({ heading: line, body: [] });
|
|
21882
|
-
} else {
|
|
21883
|
-
sections[sections.length - 1]?.body.push(line);
|
|
21884
|
-
}
|
|
21885
|
-
}
|
|
21886
|
-
const sectionCount = Math.max(1, sections.length);
|
|
21887
|
-
const perSectionBudget = Math.max(120, Math.floor(totalBudget / sectionCount));
|
|
21888
|
-
const out = [];
|
|
21889
|
-
for (const section of sections) {
|
|
21890
|
-
const bodyText = section.body.join("\n").replace(/^\s+|\s+$/g, "");
|
|
21891
|
-
if (section.heading)
|
|
21892
|
-
out.push(section.heading);
|
|
21893
|
-
if (!bodyText)
|
|
21894
|
-
continue;
|
|
21895
|
-
if (bodyText.length <= perSectionBudget) {
|
|
21896
|
-
out.push(bodyText);
|
|
21897
|
-
} else {
|
|
21898
|
-
const kept = bodyText.slice(0, perSectionBudget);
|
|
21899
|
-
const omitted = bodyText.length - perSectionBudget;
|
|
21900
|
-
out.push(`${kept}
|
|
21901
|
-
\u2026(${omitted} chars omitted; retrieve full spec for the rest)`);
|
|
21902
|
-
}
|
|
21903
|
-
}
|
|
21904
|
-
if (projectSlug && specWikiPageId) {
|
|
21905
|
-
out.push("");
|
|
21906
|
-
out.push(`> Compact spec. Source: mrrlin://wiki/${projectSlug}/${specWikiPageId} \u2014 call retrieve_context with this handle before any irreversible decision.`);
|
|
21907
|
-
} else {
|
|
21908
|
-
out.push("");
|
|
21909
|
-
out.push("> Compact spec. Retrieve the Specs/{id} wiki page via get_wiki_page before any irreversible decision.");
|
|
21910
|
-
}
|
|
21911
|
-
return out.join("\n");
|
|
21912
|
-
}
|
|
21913
|
-
function renderRunHistory(runs, openRunId) {
|
|
21914
|
-
if (runs.length === 0)
|
|
21915
|
-
return "## Execution-run history\n(none)";
|
|
21916
|
-
const lines2 = ["## Execution-run history"];
|
|
21917
|
-
for (const run3 of runs) {
|
|
21918
|
-
const marker = run3.id === openRunId ? " (open)" : "";
|
|
21919
|
-
lines2.push(`- ${run3.id} [${run3.status}] created ${run3.createdAt}${marker}`);
|
|
21920
|
-
}
|
|
21921
|
-
return lines2.join("\n");
|
|
21922
|
-
}
|
|
21923
|
-
function renderOpenRun(openRunId) {
|
|
21924
|
-
return `## Open run
|
|
21925
|
-
${openRunId ?? "(none)"}`;
|
|
21926
|
-
}
|
|
21927
|
-
function renderArtifacts(artifacts, omittedCount, compact) {
|
|
21928
|
-
if (artifacts.length === 0 && omittedCount === 0)
|
|
21929
|
-
return "## Artifacts\n(none)";
|
|
21930
|
-
const lines2 = ["## Artifacts"];
|
|
21931
|
-
if (omittedCount > 0) {
|
|
21932
|
-
lines2.push(`(+${omittedCount} older artifact${omittedCount === 1 ? "" : "s"} omitted)`);
|
|
21933
|
-
}
|
|
21934
|
-
let truncatedAny = false;
|
|
21935
|
-
for (const artifact of artifacts) {
|
|
21936
|
-
if (artifact.kind === "file") {
|
|
21937
|
-
const p = artifact.payload;
|
|
21938
|
-
if (p.filename && p.stableUrl) {
|
|
21939
|
-
lines2.push(`- ${artifact.createdAt} [file] ${p.filename} (${p.class ?? "?"}, ${p.sizeBytes ?? "?"} bytes) ${p.stableUrl}`);
|
|
21940
|
-
continue;
|
|
21941
|
-
}
|
|
21942
|
-
}
|
|
21943
|
-
const payloadText = JSON.stringify(artifact.payload);
|
|
21944
|
-
const budget = compact?.artifactPayloadBudgetChars ?? DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS;
|
|
21945
|
-
const willTruncate = !!compact && payloadText.length > budget;
|
|
21946
|
-
const rendered = willTruncate ? `${payloadText.slice(0, budget)}\u2026(${payloadText.length - budget} chars omitted)` : payloadText;
|
|
21947
|
-
if (willTruncate)
|
|
21948
|
-
truncatedAny = true;
|
|
21949
|
-
lines2.push(`- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${rendered}`);
|
|
21950
|
-
}
|
|
21951
|
-
if (compact && (omittedCount > 0 || truncatedAny)) {
|
|
21952
|
-
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.");
|
|
21953
|
-
}
|
|
21954
|
-
return lines2.join("\n");
|
|
21955
|
-
}
|
|
21956
|
-
function renderAutonomyEvents(events) {
|
|
21957
|
-
if (events.length === 0)
|
|
21958
|
-
return "## Autonomy history\n(none)";
|
|
21959
|
-
const lines2 = ["## Autonomy history"];
|
|
21960
|
-
for (const event of events) {
|
|
21961
|
-
lines2.push(`- ${event.createdAt} ${event.autonomyLevel ?? "?"} (${event.autonomyReason ?? ""})`);
|
|
21962
|
-
}
|
|
21963
|
-
return lines2.join("\n");
|
|
21964
|
-
}
|
|
21965
|
-
|
|
21966
21836
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
21967
21837
|
var external_exports = {};
|
|
21968
21838
|
__export(external_exports, {
|
|
@@ -36562,6 +36432,13 @@ var openApiOperationIds = {
|
|
|
36562
36432
|
restartExecutionRun: "restartExecutionRun",
|
|
36563
36433
|
resolveHandoff: "resolveHandoff",
|
|
36564
36434
|
touchExecutionRun: "touchExecutionRun",
|
|
36435
|
+
getTaskState: "getTaskState",
|
|
36436
|
+
upsertTaskState: "upsertTaskState",
|
|
36437
|
+
retireThread: "retireThread",
|
|
36438
|
+
// Run-scoped, model-facing half of the resume contract (D8). Distinct from upsertTaskState:
|
|
36439
|
+
// that one is the server's own write (whole record, explicit lastRunSeq), this one is the
|
|
36440
|
+
// executor reporting narrative only, with the ordering value derived server-side from the run.
|
|
36441
|
+
reportTaskState: "reportTaskState",
|
|
36565
36442
|
issueAgentCredential: "issueAgentCredential",
|
|
36566
36443
|
listAgentCredentials: "listAgentCredentials",
|
|
36567
36444
|
revokeAgentCredential: "revokeAgentCredential",
|
|
@@ -36662,19 +36539,22 @@ var agentPoolResponseSchema = external_exports.object({
|
|
|
36662
36539
|
});
|
|
36663
36540
|
|
|
36664
36541
|
// ../../packages/schemas/dist/specConsensusReview.js
|
|
36542
|
+
var mrrlinSpecConsensusSuppressionEvidenceSchema = external_exports.record(external_exports.string(), external_exports.unknown()).nullable();
|
|
36665
36543
|
var mrrlinSpecConsensusReviewSchema = external_exports.object({
|
|
36666
36544
|
projectSlug: external_exports.string().min(1),
|
|
36667
36545
|
specPageId: external_exports.string().min(1),
|
|
36668
36546
|
contentHash: external_exports.string().min(1),
|
|
36669
36547
|
outcome: external_exports.string().min(1),
|
|
36670
36548
|
rounds: external_exports.number().int().nonnegative(),
|
|
36549
|
+
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.default(null),
|
|
36671
36550
|
createdAt: external_exports.string().datetime()
|
|
36672
36551
|
});
|
|
36673
36552
|
var mrrlinSpecConsensusReviewCreateSchema = external_exports.object({
|
|
36674
36553
|
specPageId: external_exports.string().min(1),
|
|
36675
36554
|
contentHash: external_exports.string().min(1),
|
|
36676
36555
|
outcome: external_exports.string().min(1),
|
|
36677
|
-
rounds: external_exports.number().int().nonnegative()
|
|
36556
|
+
rounds: external_exports.number().int().nonnegative(),
|
|
36557
|
+
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.optional()
|
|
36678
36558
|
}).strict();
|
|
36679
36559
|
var specConsensusReviewResponseSchema = external_exports.object({ data: mrrlinSpecConsensusReviewSchema });
|
|
36680
36560
|
var specConsensusReviewMaybeResponseSchema = external_exports.object({ data: mrrlinSpecConsensusReviewSchema.nullable() });
|
|
@@ -36750,6 +36630,97 @@ var mrrlinAsyncJobRequeueSchema = external_exports.object({
|
|
|
36750
36630
|
staleSeconds: external_exports.number().int().min(60).max(86400).default(300)
|
|
36751
36631
|
});
|
|
36752
36632
|
|
|
36633
|
+
// ../../packages/schemas/dist/memory-budget.js
|
|
36634
|
+
function evictionKey(item, idx) {
|
|
36635
|
+
const memClass = item.memoryClass === "historical" ? 0 : 1;
|
|
36636
|
+
const prec = item.precedence === null ? -4 : -item.precedence;
|
|
36637
|
+
const score = item.score ?? 0;
|
|
36638
|
+
return { memClass, prec, score, idx };
|
|
36639
|
+
}
|
|
36640
|
+
function compareEvictionKeys(a, b) {
|
|
36641
|
+
if (a.memClass !== b.memClass)
|
|
36642
|
+
return a.memClass - b.memClass;
|
|
36643
|
+
if (a.prec !== b.prec)
|
|
36644
|
+
return a.prec - b.prec;
|
|
36645
|
+
if (a.score !== b.score)
|
|
36646
|
+
return a.score - b.score;
|
|
36647
|
+
return a.idx - b.idx;
|
|
36648
|
+
}
|
|
36649
|
+
function sortByEvictionOrder(items) {
|
|
36650
|
+
const indexed = items.map((item, idx) => ({ item, key: evictionKey(item, idx) }));
|
|
36651
|
+
indexed.sort((a, b) => compareEvictionKeys(a.key, b.key));
|
|
36652
|
+
return indexed.map((x) => x.item);
|
|
36653
|
+
}
|
|
36654
|
+
function keepUnderCap(sorted, cap) {
|
|
36655
|
+
const kept = [];
|
|
36656
|
+
let used = 0;
|
|
36657
|
+
let i = sorted.length - 1;
|
|
36658
|
+
for (; i >= 0; i--) {
|
|
36659
|
+
const item = sorted[i];
|
|
36660
|
+
if (item === void 0)
|
|
36661
|
+
break;
|
|
36662
|
+
if (used + item.size <= cap) {
|
|
36663
|
+
kept.unshift(item);
|
|
36664
|
+
used += item.size;
|
|
36665
|
+
} else {
|
|
36666
|
+
break;
|
|
36667
|
+
}
|
|
36668
|
+
}
|
|
36669
|
+
const dropped = sorted.slice(0, i + 1);
|
|
36670
|
+
return { kept, dropped };
|
|
36671
|
+
}
|
|
36672
|
+
function applyBudget(items, spec) {
|
|
36673
|
+
const prec0Items = [];
|
|
36674
|
+
const restItems = [];
|
|
36675
|
+
for (const item of items) {
|
|
36676
|
+
if (item.precedence === 0) {
|
|
36677
|
+
prec0Items.push(item);
|
|
36678
|
+
} else {
|
|
36679
|
+
restItems.push(item);
|
|
36680
|
+
}
|
|
36681
|
+
}
|
|
36682
|
+
const sourceCaps = spec.sourceCaps ?? {};
|
|
36683
|
+
const sourceMap = /* @__PURE__ */ new Map();
|
|
36684
|
+
for (const item of restItems) {
|
|
36685
|
+
const list = sourceMap.get(item.source);
|
|
36686
|
+
if (list)
|
|
36687
|
+
list.push(item);
|
|
36688
|
+
else
|
|
36689
|
+
sourceMap.set(item.source, [item]);
|
|
36690
|
+
}
|
|
36691
|
+
const keptAfterSourceCap = [];
|
|
36692
|
+
const droppedAfterSourceCap = [];
|
|
36693
|
+
for (const [source, sourceItems] of sourceMap) {
|
|
36694
|
+
const cap = sourceCaps[source];
|
|
36695
|
+
if (cap === void 0) {
|
|
36696
|
+
keptAfterSourceCap.push(...sourceItems);
|
|
36697
|
+
} else {
|
|
36698
|
+
const sorted = sortByEvictionOrder(sourceItems);
|
|
36699
|
+
const { kept, dropped } = keepUnderCap(sorted, cap);
|
|
36700
|
+
keptAfterSourceCap.push(...kept);
|
|
36701
|
+
droppedAfterSourceCap.push(...dropped);
|
|
36702
|
+
}
|
|
36703
|
+
}
|
|
36704
|
+
const allEligible = keptAfterSourceCap;
|
|
36705
|
+
const sortedGlobal = sortByEvictionOrder(allEligible);
|
|
36706
|
+
const { kept: keptGlobal, dropped: droppedGlobal } = keepUnderCap(sortedGlobal, spec.totalBytes - totalSize(prec0Items));
|
|
36707
|
+
const finalKept = [...prec0Items, ...keptGlobal];
|
|
36708
|
+
const finalDropped = [...droppedAfterSourceCap, ...droppedGlobal];
|
|
36709
|
+
const trimmed = finalDropped.length > 0;
|
|
36710
|
+
const keptSize = totalSize(finalKept);
|
|
36711
|
+
const overflowBy = Math.max(0, keptSize - spec.totalBytes);
|
|
36712
|
+
const handles = [];
|
|
36713
|
+
for (const item of finalDropped) {
|
|
36714
|
+
if (item.handle && item.handle.trim() !== "") {
|
|
36715
|
+
handles.push(item.handle);
|
|
36716
|
+
}
|
|
36717
|
+
}
|
|
36718
|
+
return { kept: finalKept, dropped: finalDropped, handles, trimmed, overflowBy };
|
|
36719
|
+
}
|
|
36720
|
+
function totalSize(items) {
|
|
36721
|
+
return items.reduce((sum, i) => sum + i.size, 0);
|
|
36722
|
+
}
|
|
36723
|
+
|
|
36753
36724
|
// ../../packages/schemas/dist/index.js
|
|
36754
36725
|
var mrrlinTaskStatusSchema = external_exports.enum([
|
|
36755
36726
|
"todo",
|
|
@@ -36981,6 +36952,7 @@ var mrrlinTaskEventSchema = external_exports.object({
|
|
|
36981
36952
|
});
|
|
36982
36953
|
var mrrlinWikiPageSchema = external_exports.object({
|
|
36983
36954
|
contentHash: external_exports.string().default(""),
|
|
36955
|
+
createdAt: external_exports.string().datetime().nullable().default(null),
|
|
36984
36956
|
folder: external_exports.string().min(1),
|
|
36985
36957
|
id: mrrlinWikiPageIdSchema,
|
|
36986
36958
|
markdownBody: external_exports.string().min(1),
|
|
@@ -36988,7 +36960,8 @@ var mrrlinWikiPageSchema = external_exports.object({
|
|
|
36988
36960
|
preview: external_exports.string(),
|
|
36989
36961
|
specReady: external_exports.boolean().default(false),
|
|
36990
36962
|
taskId: mrrlinTaskIdSchema.nullable().default(null),
|
|
36991
|
-
title: external_exports.string().min(1)
|
|
36963
|
+
title: external_exports.string().min(1),
|
|
36964
|
+
updatedAt: external_exports.string().datetime().nullable().default(null)
|
|
36992
36965
|
});
|
|
36993
36966
|
var mrrlinMarkSpecReadyRequestSchema = external_exports.object({
|
|
36994
36967
|
ready: external_exports.boolean()
|
|
@@ -37112,14 +37085,12 @@ var mrrlinProjectSnapshotSchema = external_exports.object({
|
|
|
37112
37085
|
taxonomy: external_exports.array(mrrlinTaxonomyEntrySchema).default([]),
|
|
37113
37086
|
wikiPages: external_exports.array(mrrlinWikiPageSchema).default([])
|
|
37114
37087
|
});
|
|
37115
|
-
var
|
|
37088
|
+
var mrrlinTaskCreateShapeSchema = external_exports.object({
|
|
37116
37089
|
// Auto-deploy design Part B: input-optional (NOT .default("off")) so createTask can tell
|
|
37117
37090
|
// "caller omitted this" apart from "caller explicitly chose off" -- omitted falls through to
|
|
37118
37091
|
// the project's default_auto_deploy; the tasks column itself still defaults to 'off' at the
|
|
37119
37092
|
// SQL level for any direct-insert path that bypasses createTask entirely.
|
|
37120
37093
|
autoDeploy: mrrlinAutoDeploySchema.optional(),
|
|
37121
|
-
// Input-optional so existing createTask call sites need no churn; createTask fills 'code' for the
|
|
37122
|
-
// INSERT (the pg column is NOT NULL, so a concrete value is required, not undefined→NULL).
|
|
37123
37094
|
domain: mrrlinTaskDomainSchema.optional(),
|
|
37124
37095
|
autonomyLevel: mrrlinAutonomyLevelSchema.default("human_review"),
|
|
37125
37096
|
autonomyReason: external_exports.string().min(1).max(240).default("Needs human review before automation."),
|
|
@@ -37141,6 +37112,17 @@ var mrrlinTaskCreateSchema = external_exports.object({
|
|
|
37141
37112
|
type: mrrlinTaskTypeSchema.default("task"),
|
|
37142
37113
|
verificationDueAt: external_exports.string().datetime().nullable().optional()
|
|
37143
37114
|
});
|
|
37115
|
+
function refineTaskCreateDomain(data, ctx) {
|
|
37116
|
+
if (data.domain === void 0 && data.parentTaskId == null) {
|
|
37117
|
+
ctx.addIssue({
|
|
37118
|
+
code: external_exports.ZodIssueCode.custom,
|
|
37119
|
+
path: ["domain"],
|
|
37120
|
+
message: "domain is required unless parentTaskId is set (a follow-up inherits its parent's domain)"
|
|
37121
|
+
});
|
|
37122
|
+
}
|
|
37123
|
+
}
|
|
37124
|
+
var mrrlinTaskCreateSchema = mrrlinTaskCreateShapeSchema.superRefine(refineTaskCreateDomain);
|
|
37125
|
+
var mrrlinTaskCreateWithoutIdSchema = mrrlinTaskCreateShapeSchema.omit({ id: true }).superRefine(refineTaskCreateDomain);
|
|
37144
37126
|
var mrrlinTaskUpdateSchema = external_exports.object({
|
|
37145
37127
|
actor: external_exports.string().min(1).optional(),
|
|
37146
37128
|
assignee: mrrlinTaskAssigneeSchema.optional(),
|
|
@@ -37258,13 +37240,19 @@ var mrrlinWikiPageSearchQuerySchema = external_exports.object({
|
|
|
37258
37240
|
q: external_exports.string().min(2)
|
|
37259
37241
|
});
|
|
37260
37242
|
var mrrlinWikiPageSearchResultSchema = external_exports.object({
|
|
37243
|
+
ageDays: external_exports.number().nullable(),
|
|
37261
37244
|
folder: external_exports.string().min(1),
|
|
37262
37245
|
id: mrrlinWikiPageIdSchema,
|
|
37246
|
+
// Task-relative; always null from a generic (non-task-scoped) search — resolved only where
|
|
37247
|
+
// a caller has task context (director-loop retrieve()), never at this layer or in the API (D14).
|
|
37248
|
+
memoryClass: external_exports.enum(["normative", "historical"]),
|
|
37263
37249
|
path: external_exports.string().min(1),
|
|
37250
|
+
precedence: external_exports.union([external_exports.literal(0), external_exports.literal(1), external_exports.literal(2), external_exports.literal(3)]).nullable(),
|
|
37264
37251
|
preview: external_exports.string(),
|
|
37265
37252
|
rank: external_exports.number(),
|
|
37266
37253
|
snippet: external_exports.string(),
|
|
37267
|
-
title: external_exports.string().min(1)
|
|
37254
|
+
title: external_exports.string().min(1),
|
|
37255
|
+
updatedAt: external_exports.string().datetime().nullable()
|
|
37268
37256
|
});
|
|
37269
37257
|
var wikiKnowledgeChunkSchema = external_exports.object({
|
|
37270
37258
|
chunk_text: external_exports.string().min(1),
|
|
@@ -37482,7 +37470,55 @@ var mrrlinExecutionRunSchema = external_exports.object({
|
|
|
37482
37470
|
status: mrrlinExecutionRunStatusSchema,
|
|
37483
37471
|
taskId: mrrlinTaskIdSchema,
|
|
37484
37472
|
updatedAt: external_exports.string().datetime(),
|
|
37485
|
-
worktreeId: external_exports.string().min(1).nullable()
|
|
37473
|
+
worktreeId: external_exports.string().min(1).nullable(),
|
|
37474
|
+
stateSeq: external_exports.number().int(),
|
|
37475
|
+
promptBytes: external_exports.number().int().nullable(),
|
|
37476
|
+
turnCount: external_exports.number().int().nullable(),
|
|
37477
|
+
inputTokens: external_exports.number().int().nullable(),
|
|
37478
|
+
cachedInputTokens: external_exports.number().int().nullable(),
|
|
37479
|
+
outputTokens: external_exports.number().int().nullable(),
|
|
37480
|
+
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable(),
|
|
37481
|
+
effortEscalationReason: external_exports.string().min(1).nullable()
|
|
37482
|
+
});
|
|
37483
|
+
var mrrlinTaskStateSchema = external_exports.object({
|
|
37484
|
+
projectSlug: mrrlinProjectSlugSchema,
|
|
37485
|
+
taskId: mrrlinTaskIdSchema,
|
|
37486
|
+
goal: external_exports.string().min(1).nullable(),
|
|
37487
|
+
acceptedRequirements: external_exports.string().min(1).nullable(),
|
|
37488
|
+
decisions: external_exports.string().min(1).nullable(),
|
|
37489
|
+
relevantFiles: external_exports.string().min(1).nullable(),
|
|
37490
|
+
filesChanged: external_exports.string().min(1).nullable(),
|
|
37491
|
+
latestVerification: external_exports.string().min(1).nullable(),
|
|
37492
|
+
openProblems: external_exports.string().min(1).nullable(),
|
|
37493
|
+
nextActions: external_exports.string().min(1).nullable(),
|
|
37494
|
+
lastRunSeq: external_exports.number().int(),
|
|
37495
|
+
threadRunCount: external_exports.number().int(),
|
|
37496
|
+
retiredThreadId: external_exports.string().min(1).nullable(),
|
|
37497
|
+
updatedAt: external_exports.string().datetime()
|
|
37498
|
+
});
|
|
37499
|
+
var mrrlinTaskStateUpsertSchema = external_exports.object({
|
|
37500
|
+
goal: external_exports.string().min(1).nullable(),
|
|
37501
|
+
acceptedRequirements: external_exports.string().min(1).nullable(),
|
|
37502
|
+
decisions: external_exports.string().min(1).nullable(),
|
|
37503
|
+
relevantFiles: external_exports.string().min(1).nullable(),
|
|
37504
|
+
filesChanged: external_exports.string().min(1).nullable(),
|
|
37505
|
+
latestVerification: external_exports.string().min(1).nullable(),
|
|
37506
|
+
openProblems: external_exports.string().min(1).nullable(),
|
|
37507
|
+
nextActions: external_exports.string().min(1).nullable(),
|
|
37508
|
+
lastRunSeq: external_exports.number().int()
|
|
37509
|
+
});
|
|
37510
|
+
var mrrlinTaskStateReportSchema = external_exports.object({
|
|
37511
|
+
goal: external_exports.string().min(1).nullish(),
|
|
37512
|
+
acceptedRequirements: external_exports.string().min(1).nullish(),
|
|
37513
|
+
decisions: external_exports.string().min(1).nullish(),
|
|
37514
|
+
openProblems: external_exports.string().min(1).nullish(),
|
|
37515
|
+
nextActions: external_exports.string().min(1).nullish()
|
|
37516
|
+
});
|
|
37517
|
+
var taskStateResponseSchema = external_exports.object({
|
|
37518
|
+
data: mrrlinTaskStateSchema.nullable()
|
|
37519
|
+
});
|
|
37520
|
+
var retireThreadResponseSchema = external_exports.object({
|
|
37521
|
+
data: external_exports.object({ retiredThreadId: external_exports.string().min(1).nullable() })
|
|
37486
37522
|
});
|
|
37487
37523
|
var mrrlinExecutionRunCreateSchema = external_exports.object({
|
|
37488
37524
|
actor: external_exports.string().min(1),
|
|
@@ -37514,7 +37550,18 @@ var mrrlinExecutionRunUpdateSchema = external_exports.object({
|
|
|
37514
37550
|
prodWorkflowId: external_exports.number().int().positive().nullable().optional(),
|
|
37515
37551
|
pullRequestUrl: external_exports.string().min(1).nullable().optional(),
|
|
37516
37552
|
status: mrrlinExecutionRunStatusSchema.optional(),
|
|
37517
|
-
worktreeId: external_exports.string().min(1).nullable().optional()
|
|
37553
|
+
worktreeId: external_exports.string().min(1).nullable().optional(),
|
|
37554
|
+
// Slice 6 (cost telemetry) + Slice 7 (effort). All nullable and optional: per D6, NULL means
|
|
37555
|
+
// "not measured" and must never be confused with a measured zero, so a disabled slice omits its
|
|
37556
|
+
// fields entirely rather than writing 0. `nonnegative` rather than `positive` because a real
|
|
37557
|
+
// measured zero is legitimate here (a turn can genuinely produce 0 output tokens).
|
|
37558
|
+
promptBytes: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37559
|
+
turnCount: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37560
|
+
inputTokens: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37561
|
+
cachedInputTokens: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37562
|
+
outputTokens: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37563
|
+
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
|
|
37564
|
+
effortEscalationReason: external_exports.string().min(1).nullable().optional()
|
|
37518
37565
|
}).refine((value) => Object.keys(value).length > 0, {
|
|
37519
37566
|
message: "ExecutionRunUpdate requires at least one field"
|
|
37520
37567
|
});
|
|
@@ -38090,6 +38137,321 @@ var inboxItemsMarkReadResponseSchema = external_exports.object({
|
|
|
38090
38137
|
var inboxItemResponseSchema = external_exports.object({ data: mrrlinInboxItemSchema });
|
|
38091
38138
|
var inboxItemListResponseSchema = external_exports.object({ data: external_exports.array(mrrlinInboxItemSchema) });
|
|
38092
38139
|
|
|
38140
|
+
// ../../packages/codex-client/dist/render-context-bundle.js
|
|
38141
|
+
var DEFAULT_SPEC_BUDGET_CHARS = 6e3;
|
|
38142
|
+
var DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS = 400;
|
|
38143
|
+
function renderContextBundle(input) {
|
|
38144
|
+
const sections = [];
|
|
38145
|
+
sections.push(renderTaskSummary(input.task));
|
|
38146
|
+
sections.push(renderSpec(input.specMarkdown, input.compact));
|
|
38147
|
+
const { keptRuns, keptArtifacts, keptAutonomy, anyHistoricalDropped, droppedHandles } = applyHistoricalBudgetIfNeeded(input);
|
|
38148
|
+
sections.push(renderRunHistory(keptRuns, input.openRunId, input.compact, anyHistoricalDropped));
|
|
38149
|
+
sections.push(renderOpenRun(input.openRunId));
|
|
38150
|
+
sections.push(renderArtifacts(keptArtifacts, input.omittedArtifactCount ?? 0, input.compact, anyHistoricalDropped, droppedHandles));
|
|
38151
|
+
sections.push(renderAutonomyEvents(keptAutonomy, input.compact, anyHistoricalDropped));
|
|
38152
|
+
return sections.join("\n\n");
|
|
38153
|
+
}
|
|
38154
|
+
function renderTaskSummary(task) {
|
|
38155
|
+
const lines2 = [
|
|
38156
|
+
"## Task (normative)",
|
|
38157
|
+
`id: ${task.id}`,
|
|
38158
|
+
`title: ${task.title}`,
|
|
38159
|
+
`autonomy: ${task.autonomyLevel} (${task.autonomyReason})`
|
|
38160
|
+
];
|
|
38161
|
+
if (task.notes)
|
|
38162
|
+
lines2.push(`description: ${task.notes}`);
|
|
38163
|
+
if (task.latestJudgement) {
|
|
38164
|
+
lines2.push(`latest judgement (${task.latestJudgementKind ?? "unknown"}): ${task.latestJudgement}`);
|
|
38165
|
+
}
|
|
38166
|
+
return lines2.join("\n");
|
|
38167
|
+
}
|
|
38168
|
+
function renderSpec(specMarkdown, compact) {
|
|
38169
|
+
if (!specMarkdown)
|
|
38170
|
+
return "## Spec (normative)\n(none)";
|
|
38171
|
+
if (!compact)
|
|
38172
|
+
return `## Spec (normative)
|
|
38173
|
+
${specMarkdown}`;
|
|
38174
|
+
const budget = compact.specBudgetChars ?? DEFAULT_SPEC_BUDGET_CHARS;
|
|
38175
|
+
if (specMarkdown.length <= budget)
|
|
38176
|
+
return `## Spec (normative)
|
|
38177
|
+
${specMarkdown}`;
|
|
38178
|
+
return `## Spec (normative)
|
|
38179
|
+
${compactSpecMarkdown(specMarkdown, budget, compact.projectSlug ?? null, compact.specWikiPageId ?? null)}`;
|
|
38180
|
+
}
|
|
38181
|
+
function compactSpecMarkdown(markdown, totalBudget, projectSlug, specWikiPageId) {
|
|
38182
|
+
const lines2 = markdown.split(/\r?\n/);
|
|
38183
|
+
const sections = [{ heading: "", body: [] }];
|
|
38184
|
+
for (const line of lines2) {
|
|
38185
|
+
if (/^#{1,6}\s/.test(line)) {
|
|
38186
|
+
sections.push({ heading: line, body: [] });
|
|
38187
|
+
} else {
|
|
38188
|
+
sections[sections.length - 1]?.body.push(line);
|
|
38189
|
+
}
|
|
38190
|
+
}
|
|
38191
|
+
const sectionCount = Math.max(1, sections.length);
|
|
38192
|
+
const perSectionBudget = Math.max(120, Math.floor(totalBudget / sectionCount));
|
|
38193
|
+
const out = [];
|
|
38194
|
+
for (const section of sections) {
|
|
38195
|
+
const bodyText = section.body.join("\n").replace(/^\s+|\s+$/g, "");
|
|
38196
|
+
if (section.heading)
|
|
38197
|
+
out.push(section.heading);
|
|
38198
|
+
if (!bodyText)
|
|
38199
|
+
continue;
|
|
38200
|
+
if (bodyText.length <= perSectionBudget) {
|
|
38201
|
+
out.push(bodyText);
|
|
38202
|
+
} else {
|
|
38203
|
+
const kept = bodyText.slice(0, perSectionBudget);
|
|
38204
|
+
const omitted = bodyText.length - perSectionBudget;
|
|
38205
|
+
out.push(`${kept}
|
|
38206
|
+
\u2026(${omitted} chars omitted; retrieve full spec for the rest)`);
|
|
38207
|
+
}
|
|
38208
|
+
}
|
|
38209
|
+
if (projectSlug && specWikiPageId) {
|
|
38210
|
+
out.push("");
|
|
38211
|
+
out.push(`> Compact spec. Source: mrrlin://wiki/${projectSlug}/${specWikiPageId} \u2014 call retrieve_context with this handle before any irreversible decision.`);
|
|
38212
|
+
} else {
|
|
38213
|
+
out.push("");
|
|
38214
|
+
out.push("> Compact spec. Retrieve the Specs/{id} wiki page via get_wiki_page before any irreversible decision.");
|
|
38215
|
+
}
|
|
38216
|
+
return out.join("\n");
|
|
38217
|
+
}
|
|
38218
|
+
function renderRunHistory(runs, openRunId, compact, anyHistoricalDropped) {
|
|
38219
|
+
if (runs.length === 0)
|
|
38220
|
+
return "## Execution-run history (historical)\n(none)";
|
|
38221
|
+
const lines2 = ["## Execution-run history (historical)"];
|
|
38222
|
+
for (const run3 of runs) {
|
|
38223
|
+
const marker = run3.id === openRunId ? " (open)" : "";
|
|
38224
|
+
lines2.push(`- ${run3.id} [${run3.status}] created ${run3.createdAt}${marker}`);
|
|
38225
|
+
}
|
|
38226
|
+
if (compact && anyHistoricalDropped) {
|
|
38227
|
+
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.");
|
|
38228
|
+
}
|
|
38229
|
+
return lines2.join("\n");
|
|
38230
|
+
}
|
|
38231
|
+
function renderOpenRun(openRunId) {
|
|
38232
|
+
return `## Open run (normative)
|
|
38233
|
+
${openRunId ?? "(none)"}`;
|
|
38234
|
+
}
|
|
38235
|
+
function renderArtifacts(artifacts, omittedCount, compact, anyHistoricalDropped, droppedHandles) {
|
|
38236
|
+
if (artifacts.length === 0 && omittedCount === 0)
|
|
38237
|
+
return "## Artifacts (historical)\n(none)";
|
|
38238
|
+
const lines2 = ["## Artifacts (historical)"];
|
|
38239
|
+
if (omittedCount > 0) {
|
|
38240
|
+
lines2.push(`(+${omittedCount} older artifact${omittedCount === 1 ? "" : "s"} omitted)`);
|
|
38241
|
+
}
|
|
38242
|
+
let truncatedAny = false;
|
|
38243
|
+
for (const artifact of artifacts) {
|
|
38244
|
+
if (artifact.kind === "file") {
|
|
38245
|
+
const p = artifact.payload;
|
|
38246
|
+
if (p.filename && p.stableUrl) {
|
|
38247
|
+
lines2.push(`- ${artifact.createdAt} [file] ${p.filename} (${p.class ?? "?"}, ${p.sizeBytes ?? "?"} bytes) ${p.stableUrl}`);
|
|
38248
|
+
continue;
|
|
38249
|
+
}
|
|
38250
|
+
}
|
|
38251
|
+
const payloadText = JSON.stringify(artifact.payload);
|
|
38252
|
+
const budget = compact?.artifactPayloadBudgetChars ?? DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS;
|
|
38253
|
+
const willTruncate = !!compact && payloadText.length > budget;
|
|
38254
|
+
const rendered = willTruncate ? `${payloadText.slice(0, budget)}\u2026(${payloadText.length - budget} chars omitted)` : payloadText;
|
|
38255
|
+
if (willTruncate)
|
|
38256
|
+
truncatedAny = true;
|
|
38257
|
+
lines2.push(`- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${rendered}`);
|
|
38258
|
+
}
|
|
38259
|
+
if (compact && (omittedCount > 0 || truncatedAny || anyHistoricalDropped)) {
|
|
38260
|
+
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.");
|
|
38261
|
+
}
|
|
38262
|
+
if (compact && droppedHandles.length > 0) {
|
|
38263
|
+
lines2.push(`> Dropped by budget, retrievable via: ${droppedHandles.map((h) => `\`${h}\``).join(", ")}`);
|
|
38264
|
+
}
|
|
38265
|
+
return lines2.join("\n");
|
|
38266
|
+
}
|
|
38267
|
+
function renderAutonomyEvents(events, compact, anyHistoricalDropped) {
|
|
38268
|
+
if (events.length === 0)
|
|
38269
|
+
return "## Autonomy history (historical)\n(none)";
|
|
38270
|
+
const lines2 = ["## Autonomy history (historical)"];
|
|
38271
|
+
for (const event of events) {
|
|
38272
|
+
lines2.push(`- ${event.createdAt} ${event.autonomyLevel ?? "?"} (${event.autonomyReason ?? ""})`);
|
|
38273
|
+
}
|
|
38274
|
+
if (compact && anyHistoricalDropped) {
|
|
38275
|
+
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.");
|
|
38276
|
+
}
|
|
38277
|
+
return lines2.join("\n");
|
|
38278
|
+
}
|
|
38279
|
+
function applyHistoricalBudgetIfNeeded(input) {
|
|
38280
|
+
if (!input.compact?.budget) {
|
|
38281
|
+
return {
|
|
38282
|
+
keptRuns: input.runs,
|
|
38283
|
+
keptArtifacts: input.artifacts,
|
|
38284
|
+
keptAutonomy: input.autonomyEvents,
|
|
38285
|
+
anyHistoricalDropped: false,
|
|
38286
|
+
droppedHandles: []
|
|
38287
|
+
};
|
|
38288
|
+
}
|
|
38289
|
+
const candidates = [];
|
|
38290
|
+
for (const run3 of input.runs) {
|
|
38291
|
+
const line = `- ${run3.id} [${run3.status}] created ${run3.createdAt}`;
|
|
38292
|
+
candidates.push({
|
|
38293
|
+
kind: "run",
|
|
38294
|
+
runId: run3.id,
|
|
38295
|
+
size: line.length,
|
|
38296
|
+
memoryClass: "historical",
|
|
38297
|
+
precedence: 3,
|
|
38298
|
+
handle: input.compact.projectSlug ? `mrrlin://run/${input.compact.projectSlug}/${run3.id}/artifacts` : null,
|
|
38299
|
+
source: "run_history"
|
|
38300
|
+
});
|
|
38301
|
+
}
|
|
38302
|
+
for (const artifact of input.artifacts) {
|
|
38303
|
+
const line = `- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${JSON.stringify(artifact.payload)}`;
|
|
38304
|
+
candidates.push({
|
|
38305
|
+
kind: "artifact",
|
|
38306
|
+
artifactId: artifact.id,
|
|
38307
|
+
size: line.length,
|
|
38308
|
+
memoryClass: "historical",
|
|
38309
|
+
precedence: 3,
|
|
38310
|
+
handle: input.compact.projectSlug ? `mrrlin://run/${input.compact.projectSlug}/${artifact.runId}/artifacts` : null,
|
|
38311
|
+
source: "artifacts"
|
|
38312
|
+
});
|
|
38313
|
+
}
|
|
38314
|
+
for (const event of input.autonomyEvents) {
|
|
38315
|
+
const line = `- ${event.createdAt} ${event.autonomyLevel ?? "?"} (${event.autonomyReason ?? ""})`;
|
|
38316
|
+
candidates.push({
|
|
38317
|
+
kind: "autonomy",
|
|
38318
|
+
autonomyEventId: event.id,
|
|
38319
|
+
size: line.length,
|
|
38320
|
+
memoryClass: "historical",
|
|
38321
|
+
precedence: 3,
|
|
38322
|
+
handle: null,
|
|
38323
|
+
source: "autonomy"
|
|
38324
|
+
});
|
|
38325
|
+
}
|
|
38326
|
+
const { kept, dropped } = applyBudget(candidates, input.compact.budget);
|
|
38327
|
+
const keptRunIds = new Set(kept.filter((c) => c.kind === "run").map((c) => c.runId));
|
|
38328
|
+
const keptArtifactIds = new Set(kept.filter((c) => c.kind === "artifact").map((c) => c.artifactId));
|
|
38329
|
+
const keptAutonomyIds = new Set(kept.filter((c) => c.kind === "autonomy").map((c) => c.autonomyEventId));
|
|
38330
|
+
const droppedHandles = [];
|
|
38331
|
+
for (const item of dropped) {
|
|
38332
|
+
if (item.handle)
|
|
38333
|
+
droppedHandles.push(item.handle);
|
|
38334
|
+
}
|
|
38335
|
+
return {
|
|
38336
|
+
keptRuns: input.runs.filter((r) => keptRunIds.has(r.id)),
|
|
38337
|
+
keptArtifacts: input.artifacts.filter((a) => keptArtifactIds.has(a.id)),
|
|
38338
|
+
keptAutonomy: input.autonomyEvents.filter((e) => keptAutonomyIds.has(e.id)),
|
|
38339
|
+
anyHistoricalDropped: dropped.length > 0,
|
|
38340
|
+
droppedHandles
|
|
38341
|
+
};
|
|
38342
|
+
}
|
|
38343
|
+
function renderTaskStateSection(state) {
|
|
38344
|
+
const rows = [
|
|
38345
|
+
["goal", state.goal],
|
|
38346
|
+
["accepted requirements", state.acceptedRequirements],
|
|
38347
|
+
["decisions (and why)", state.decisions],
|
|
38348
|
+
["relevant files", state.relevantFiles],
|
|
38349
|
+
["files changed", state.filesChanged],
|
|
38350
|
+
["latest verification", state.latestVerification],
|
|
38351
|
+
["open problems", state.openProblems],
|
|
38352
|
+
["next actions", state.nextActions]
|
|
38353
|
+
];
|
|
38354
|
+
const present = rows.filter(([, value]) => typeof value === "string" && value.trim().length > 0);
|
|
38355
|
+
if (present.length === 0) {
|
|
38356
|
+
return "## Task state (normative)\n(no state recorded yet \u2014 establish it before ending your turn)";
|
|
38357
|
+
}
|
|
38358
|
+
const lines2 = ["## Task state (normative)"];
|
|
38359
|
+
for (const [label, value] of present) {
|
|
38360
|
+
lines2.push(`${label}: ${value}`);
|
|
38361
|
+
}
|
|
38362
|
+
return lines2.join("\n");
|
|
38363
|
+
}
|
|
38364
|
+
function renderRunDelta(input) {
|
|
38365
|
+
const sections = [
|
|
38366
|
+
renderDeltaTaskStatus(input.task),
|
|
38367
|
+
renderDeltaRuns(input.newlyFinishedRuns),
|
|
38368
|
+
renderDeltaArtifacts(input.newArtifacts, input.omittedArtifactCount ?? 0)
|
|
38369
|
+
];
|
|
38370
|
+
return sections.join("\n\n");
|
|
38371
|
+
}
|
|
38372
|
+
function renderDeltaArtifacts(artifacts, omittedCount) {
|
|
38373
|
+
if (artifacts.length === 0 && omittedCount === 0)
|
|
38374
|
+
return "## New artifacts since your last turn\n(none)";
|
|
38375
|
+
const lines2 = ["## New artifacts since your last turn"];
|
|
38376
|
+
if (omittedCount > 0) {
|
|
38377
|
+
lines2.push(`(+${omittedCount} older artifact${omittedCount === 1 ? "" : "s"} omitted)`);
|
|
38378
|
+
}
|
|
38379
|
+
for (const artifact of artifacts) {
|
|
38380
|
+
if (artifact.kind === "file") {
|
|
38381
|
+
const p = artifact.payload;
|
|
38382
|
+
if (p.filename && p.stableUrl) {
|
|
38383
|
+
lines2.push(`- ${artifact.createdAt} [file] ${p.filename} (${p.class ?? "?"}, ${p.sizeBytes ?? "?"} bytes) ${p.stableUrl}`);
|
|
38384
|
+
continue;
|
|
38385
|
+
}
|
|
38386
|
+
}
|
|
38387
|
+
lines2.push(`- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${JSON.stringify(artifact.payload)}`);
|
|
38388
|
+
}
|
|
38389
|
+
return lines2.join("\n");
|
|
38390
|
+
}
|
|
38391
|
+
function renderDeltaTaskStatus(task) {
|
|
38392
|
+
const lines2 = ["## Since your last turn", `task ${task.id} status: ${task.status}`];
|
|
38393
|
+
if (task.latestJudgement) {
|
|
38394
|
+
lines2.push(`latest judgement (${task.latestJudgementKind ?? "unknown"}): ${task.latestJudgement}`);
|
|
38395
|
+
}
|
|
38396
|
+
return lines2.join("\n");
|
|
38397
|
+
}
|
|
38398
|
+
function renderDeltaRuns(runs) {
|
|
38399
|
+
if (runs.length === 0)
|
|
38400
|
+
return "## Runs completed since your last turn\n(none)";
|
|
38401
|
+
const lines2 = ["## Runs completed since your last turn"];
|
|
38402
|
+
for (const run3 of runs) {
|
|
38403
|
+
lines2.push(`- ${run3.id} [${run3.status}] finished ${run3.finishedAt ?? run3.createdAt}`);
|
|
38404
|
+
}
|
|
38405
|
+
return lines2.join("\n");
|
|
38406
|
+
}
|
|
38407
|
+
|
|
38408
|
+
// ../../packages/codex-client/dist/token-usage.js
|
|
38409
|
+
var TOKEN_USAGE_NOTIFICATION_METHOD = "thread/tokenUsage/updated";
|
|
38410
|
+
function readCount(source, key) {
|
|
38411
|
+
const value = source[key];
|
|
38412
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
38413
|
+
}
|
|
38414
|
+
function extractTurnTokenUsage(method, params) {
|
|
38415
|
+
if (method !== TOKEN_USAGE_NOTIFICATION_METHOD)
|
|
38416
|
+
return null;
|
|
38417
|
+
if (params === null || typeof params !== "object")
|
|
38418
|
+
return null;
|
|
38419
|
+
const usage = params.tokenUsage;
|
|
38420
|
+
if (usage === null || typeof usage !== "object")
|
|
38421
|
+
return null;
|
|
38422
|
+
const last = usage.last;
|
|
38423
|
+
if (last === null || typeof last !== "object")
|
|
38424
|
+
return null;
|
|
38425
|
+
const block = last;
|
|
38426
|
+
const extracted = {
|
|
38427
|
+
inputTokens: readCount(block, "inputTokens"),
|
|
38428
|
+
cachedInputTokens: readCount(block, "cachedInputTokens"),
|
|
38429
|
+
outputTokens: readCount(block, "outputTokens")
|
|
38430
|
+
};
|
|
38431
|
+
if (extracted.inputTokens === null && extracted.cachedInputTokens === null && extracted.outputTokens === null) {
|
|
38432
|
+
return null;
|
|
38433
|
+
}
|
|
38434
|
+
return extracted;
|
|
38435
|
+
}
|
|
38436
|
+
function addTurnTokenUsage(total, turn) {
|
|
38437
|
+
if (turn === null)
|
|
38438
|
+
return total;
|
|
38439
|
+
if (total === null)
|
|
38440
|
+
return { ...turn };
|
|
38441
|
+
const add2 = (a, b) => {
|
|
38442
|
+
if (a === null)
|
|
38443
|
+
return b;
|
|
38444
|
+
if (b === null)
|
|
38445
|
+
return a;
|
|
38446
|
+
return a + b;
|
|
38447
|
+
};
|
|
38448
|
+
return {
|
|
38449
|
+
inputTokens: add2(total.inputTokens, turn.inputTokens),
|
|
38450
|
+
cachedInputTokens: add2(total.cachedInputTokens, turn.cachedInputTokens),
|
|
38451
|
+
outputTokens: add2(total.outputTokens, turn.outputTokens)
|
|
38452
|
+
};
|
|
38453
|
+
}
|
|
38454
|
+
|
|
38093
38455
|
// ../../packages/client/dist/agent-token-provider.js
|
|
38094
38456
|
var AgentTokenProviderError = class extends Error {
|
|
38095
38457
|
code;
|
|
@@ -38813,6 +39175,34 @@ function createMrrlinClient(config2) {
|
|
|
38813
39175
|
throw error51;
|
|
38814
39176
|
}
|
|
38815
39177
|
},
|
|
39178
|
+
async getTaskState(projectSlug, taskId) {
|
|
39179
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/state`);
|
|
39180
|
+
return taskStateResponseSchema.parse(body).data;
|
|
39181
|
+
},
|
|
39182
|
+
async upsertTaskState(projectSlug, taskId, input) {
|
|
39183
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/state`, {
|
|
39184
|
+
body: JSON.stringify(mrrlinTaskStateUpsertSchema.parse(input)),
|
|
39185
|
+
headers: { "content-type": "application/json" },
|
|
39186
|
+
method: "PUT"
|
|
39187
|
+
});
|
|
39188
|
+
return taskStateResponseSchema.parse(body).data;
|
|
39189
|
+
},
|
|
39190
|
+
async retireThread(projectSlug, taskId, newThreadId) {
|
|
39191
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/retire-thread`, {
|
|
39192
|
+
body: JSON.stringify({ newThreadId }),
|
|
39193
|
+
headers: { "content-type": "application/json" },
|
|
39194
|
+
method: "POST"
|
|
39195
|
+
});
|
|
39196
|
+
return retireThreadResponseSchema.parse(body).data.retiredThreadId;
|
|
39197
|
+
},
|
|
39198
|
+
async reportTaskState(projectSlug, runId, report) {
|
|
39199
|
+
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/task-state`, {
|
|
39200
|
+
body: JSON.stringify(mrrlinTaskStateReportSchema.parse(report)),
|
|
39201
|
+
headers: { "content-type": "application/json" },
|
|
39202
|
+
method: "POST"
|
|
39203
|
+
});
|
|
39204
|
+
return taskStateResponseSchema.parse(body).data;
|
|
39205
|
+
},
|
|
38816
39206
|
async resolveHandoff(projectSlug, runId, input) {
|
|
38817
39207
|
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/resolve-handoff`, {
|
|
38818
39208
|
body: JSON.stringify(input),
|
|
@@ -39016,6 +39406,36 @@ function createMrrlinClient(config2) {
|
|
|
39016
39406
|
};
|
|
39017
39407
|
}
|
|
39018
39408
|
|
|
39409
|
+
// src/describe-error.ts
|
|
39410
|
+
var MAX_DETAIL_CHARS = 200;
|
|
39411
|
+
function truncate(value) {
|
|
39412
|
+
return value.length > MAX_DETAIL_CHARS ? value.slice(0, MAX_DETAIL_CHARS) : value;
|
|
39413
|
+
}
|
|
39414
|
+
function codeAndMessage(value) {
|
|
39415
|
+
if (!value || typeof value !== "object") return null;
|
|
39416
|
+
const obj = value;
|
|
39417
|
+
const code = typeof obj.code === "string" ? obj.code : null;
|
|
39418
|
+
const message = typeof obj.message === "string" ? obj.message : null;
|
|
39419
|
+
if (code) return message ? `${code}: ${message}` : code;
|
|
39420
|
+
return message;
|
|
39421
|
+
}
|
|
39422
|
+
function detailFromCause(cause) {
|
|
39423
|
+
if (!cause || typeof cause !== "object") return null;
|
|
39424
|
+
const direct = codeAndMessage(cause);
|
|
39425
|
+
const errors = cause.errors;
|
|
39426
|
+
const hasCode = typeof cause.code === "string";
|
|
39427
|
+
if (!hasCode && Array.isArray(errors) && errors.length > 0) {
|
|
39428
|
+
const fromFirst = codeAndMessage(errors[0]);
|
|
39429
|
+
if (fromFirst) return fromFirst;
|
|
39430
|
+
}
|
|
39431
|
+
return direct;
|
|
39432
|
+
}
|
|
39433
|
+
function describeError(error51) {
|
|
39434
|
+
if (!(error51 instanceof Error)) return String(error51);
|
|
39435
|
+
const detail = detailFromCause(error51.cause);
|
|
39436
|
+
return detail ? `${error51.message} (${truncate(detail)})` : error51.message;
|
|
39437
|
+
}
|
|
39438
|
+
|
|
39019
39439
|
// src/token-refresher.ts
|
|
39020
39440
|
function createTokenRefresher(opts) {
|
|
39021
39441
|
const setIntervalFn = opts.setIntervalFn ?? setInterval;
|
|
@@ -39032,7 +39452,7 @@ function createTokenRefresher(opts) {
|
|
|
39032
39452
|
opts.onOk?.();
|
|
39033
39453
|
} catch (error51) {
|
|
39034
39454
|
process.stderr.write(
|
|
39035
|
-
`[mrrlin-mcp director-bridge] token refresh failed (will retry next tick): ${
|
|
39455
|
+
`[mrrlin-mcp director-bridge] token refresh failed (will retry next tick): ${describeError(error51)}
|
|
39036
39456
|
`
|
|
39037
39457
|
);
|
|
39038
39458
|
opts.onError?.(error51);
|
|
@@ -39071,29 +39491,117 @@ function createServiceHealth() {
|
|
|
39071
39491
|
}
|
|
39072
39492
|
|
|
39073
39493
|
// src/turn-lock.ts
|
|
39494
|
+
var CHAT_LIMIT = 5;
|
|
39495
|
+
var AUTONOMOUS_LIMIT = 10;
|
|
39074
39496
|
function createTurnLock() {
|
|
39075
|
-
let
|
|
39076
|
-
let
|
|
39077
|
-
|
|
39078
|
-
|
|
39079
|
-
|
|
39080
|
-
|
|
39081
|
-
|
|
39082
|
-
|
|
39083
|
-
|
|
39084
|
-
|
|
39085
|
-
|
|
39086
|
-
|
|
39087
|
-
|
|
39088
|
-
|
|
39089
|
-
|
|
39090
|
-
|
|
39497
|
+
let chatTotalActive = 0;
|
|
39498
|
+
let autonomousTotalActive = 0;
|
|
39499
|
+
let chatRunning = 0;
|
|
39500
|
+
let autonomousRunning = 0;
|
|
39501
|
+
const chatQueue = [];
|
|
39502
|
+
const execQueue = [];
|
|
39503
|
+
const asyncQueue = [];
|
|
39504
|
+
let lastServed = null;
|
|
39505
|
+
let autonomousBound = false;
|
|
39506
|
+
const logBound = (queued) => {
|
|
39507
|
+
console.warn(
|
|
39508
|
+
`[mrrlin-mcp director-bridge] warn autonomous_ceiling_bound {"limit":${AUTONOMOUS_LIMIT},"queued":${queued}}`
|
|
39509
|
+
);
|
|
39510
|
+
};
|
|
39511
|
+
const maybeEnterBound = () => {
|
|
39512
|
+
if (!autonomousBound && autonomousRunning === AUTONOMOUS_LIMIT && execQueue.length + asyncQueue.length > 0) {
|
|
39513
|
+
autonomousBound = true;
|
|
39514
|
+
logBound(execQueue.length + asyncQueue.length);
|
|
39515
|
+
}
|
|
39516
|
+
};
|
|
39517
|
+
const maybeLeaveBound = () => {
|
|
39518
|
+
if (autonomousBound && (autonomousRunning < AUTONOMOUS_LIMIT || execQueue.length + asyncQueue.length === 0)) {
|
|
39519
|
+
autonomousBound = false;
|
|
39520
|
+
logBound(execQueue.length + asyncQueue.length);
|
|
39521
|
+
}
|
|
39522
|
+
};
|
|
39523
|
+
const acquire = (lane, startRunner) => {
|
|
39524
|
+
if (lane === "chat") {
|
|
39525
|
+
if (chatRunning < CHAT_LIMIT) {
|
|
39526
|
+
chatRunning += 1;
|
|
39527
|
+
startRunner();
|
|
39528
|
+
} else {
|
|
39529
|
+
chatQueue.push(startRunner);
|
|
39530
|
+
}
|
|
39531
|
+
} else {
|
|
39532
|
+
if (autonomousRunning < AUTONOMOUS_LIMIT) {
|
|
39533
|
+
autonomousRunning += 1;
|
|
39534
|
+
startRunner();
|
|
39535
|
+
} else {
|
|
39536
|
+
if (lane === "exec") {
|
|
39537
|
+
execQueue.push(startRunner);
|
|
39538
|
+
} else {
|
|
39539
|
+
asyncQueue.push(startRunner);
|
|
39091
39540
|
}
|
|
39092
|
-
|
|
39093
|
-
|
|
39094
|
-
}
|
|
39095
|
-
|
|
39541
|
+
maybeEnterBound();
|
|
39542
|
+
}
|
|
39543
|
+
}
|
|
39544
|
+
};
|
|
39545
|
+
const release = (lane) => {
|
|
39546
|
+
if (lane === "chat") {
|
|
39547
|
+
chatRunning -= 1;
|
|
39548
|
+
const next = chatQueue.shift();
|
|
39549
|
+
if (next) {
|
|
39550
|
+
chatRunning += 1;
|
|
39551
|
+
next();
|
|
39552
|
+
}
|
|
39553
|
+
} else {
|
|
39554
|
+
autonomousRunning -= 1;
|
|
39555
|
+
const execWait = execQueue.length > 0;
|
|
39556
|
+
const asyncWait = asyncQueue.length > 0;
|
|
39557
|
+
if (execWait || asyncWait) {
|
|
39558
|
+
let serveLane;
|
|
39559
|
+
if (execWait && !asyncWait) {
|
|
39560
|
+
serveLane = "exec";
|
|
39561
|
+
} else if (!execWait && asyncWait) {
|
|
39562
|
+
serveLane = "async";
|
|
39563
|
+
} else {
|
|
39564
|
+
serveLane = lastServed === "exec" ? "async" : "exec";
|
|
39565
|
+
}
|
|
39566
|
+
const queue = serveLane === "exec" ? execQueue : asyncQueue;
|
|
39567
|
+
const next = queue.shift();
|
|
39568
|
+
if (next) {
|
|
39569
|
+
autonomousRunning += 1;
|
|
39570
|
+
lastServed = serveLane;
|
|
39571
|
+
next();
|
|
39572
|
+
}
|
|
39573
|
+
}
|
|
39574
|
+
maybeLeaveBound();
|
|
39575
|
+
}
|
|
39096
39576
|
};
|
|
39577
|
+
const run3 = async (lane, fn) => {
|
|
39578
|
+
if (lane === "chat") {
|
|
39579
|
+
chatTotalActive += 1;
|
|
39580
|
+
} else {
|
|
39581
|
+
autonomousTotalActive += 1;
|
|
39582
|
+
}
|
|
39583
|
+
return new Promise((resolve, reject) => {
|
|
39584
|
+
const start = () => {
|
|
39585
|
+
let bodyPromise;
|
|
39586
|
+
try {
|
|
39587
|
+
bodyPromise = fn();
|
|
39588
|
+
} catch (e) {
|
|
39589
|
+
bodyPromise = Promise.reject(e);
|
|
39590
|
+
}
|
|
39591
|
+
bodyPromise.then((v) => resolve(v)).catch((e) => reject(e)).finally(() => {
|
|
39592
|
+
if (lane === "chat") {
|
|
39593
|
+
chatTotalActive -= 1;
|
|
39594
|
+
} else {
|
|
39595
|
+
autonomousTotalActive -= 1;
|
|
39596
|
+
}
|
|
39597
|
+
release(lane);
|
|
39598
|
+
});
|
|
39599
|
+
};
|
|
39600
|
+
acquire(lane, start);
|
|
39601
|
+
});
|
|
39602
|
+
};
|
|
39603
|
+
const activeCount = () => chatTotalActive + autonomousTotalActive;
|
|
39604
|
+
return { run: run3, activeCount };
|
|
39097
39605
|
}
|
|
39098
39606
|
|
|
39099
39607
|
// src/self-update.ts
|
|
@@ -39457,7 +39965,7 @@ function startCodexProbe(deps = {}) {
|
|
|
39457
39965
|
}
|
|
39458
39966
|
|
|
39459
39967
|
// src/_generated/version.ts
|
|
39460
|
-
var PKG_VERSION = "0.3.
|
|
39968
|
+
var PKG_VERSION = "0.3.62";
|
|
39461
39969
|
|
|
39462
39970
|
// src/api-base-url-migrate.ts
|
|
39463
39971
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
@@ -39957,7 +40465,7 @@ async function attemptSchedulerTick(deps) {
|
|
|
39957
40465
|
return { nextWakeAt: typeof data.nextWakeAt === "string" ? data.nextWakeAt : null, failed: false };
|
|
39958
40466
|
} catch (error51) {
|
|
39959
40467
|
process.stderr.write(
|
|
39960
|
-
`[mrrlin-mcp director-bridge] scheduler tick failed: ${
|
|
40468
|
+
`[mrrlin-mcp director-bridge] scheduler tick failed: ${describeError(error51)}
|
|
39961
40469
|
`
|
|
39962
40470
|
);
|
|
39963
40471
|
bridgeHealth?.recordFailure("schedulerTick", error51);
|
|
@@ -40273,13 +40781,26 @@ var HEADS_BY_DOMAIN = {
|
|
|
40273
40781
|
content: ["plan-reviewer", "scope-analyst", "content-editor", "compliance-reviewer"]
|
|
40274
40782
|
};
|
|
40275
40783
|
var SPEC_HEADS = HEADS_BY_DOMAIN.code;
|
|
40784
|
+
var CODE_HEADS_BY_IMPACT = {
|
|
40785
|
+
high: HEADS_BY_DOMAIN.code,
|
|
40786
|
+
medium: ["plan-reviewer", "security-analyst", "scope-analyst"],
|
|
40787
|
+
low: ["plan-reviewer", "scope-analyst"]
|
|
40788
|
+
};
|
|
40276
40789
|
function normalizeDomain(value) {
|
|
40277
40790
|
if (typeof value !== "string") return DEFAULT_DOMAIN;
|
|
40278
40791
|
const v = value.trim().toLowerCase();
|
|
40279
40792
|
return DOMAINS.includes(v) ? v : DEFAULT_DOMAIN;
|
|
40280
40793
|
}
|
|
40281
|
-
function
|
|
40282
|
-
|
|
40794
|
+
function normalizeImpact(value) {
|
|
40795
|
+
if (typeof value !== "string") return null;
|
|
40796
|
+
const v = value.trim().toLowerCase();
|
|
40797
|
+
return v === "low" || v === "medium" || v === "high" ? v : null;
|
|
40798
|
+
}
|
|
40799
|
+
function resolveHeads(domain2, impact) {
|
|
40800
|
+
const d = normalizeDomain(domain2);
|
|
40801
|
+
if (d !== "code") return [...HEADS_BY_DOMAIN[d]];
|
|
40802
|
+
const i = normalizeImpact(impact) ?? "high";
|
|
40803
|
+
return [...CODE_HEADS_BY_IMPACT[i]];
|
|
40283
40804
|
}
|
|
40284
40805
|
var PERSONAS_BY_DOMAIN = {
|
|
40285
40806
|
seo: "seo-writer",
|
|
@@ -40726,7 +41247,7 @@ function loadPersona(name) {
|
|
|
40726
41247
|
personaCache.set(name, content);
|
|
40727
41248
|
return content;
|
|
40728
41249
|
}
|
|
40729
|
-
function specHeadIntentBlock(taskId, title, seed) {
|
|
41250
|
+
function specHeadIntentBlock(taskId, title, seed, impact, adjudicated) {
|
|
40730
41251
|
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) ---
|
|
40731
41252
|
TASK ${taskId}: ${title ?? ""}
|
|
40732
41253
|
|
|
@@ -40735,7 +41256,15 @@ TASK ${taskId}: ${title ?? ""}
|
|
|
40735
41256
|
${seed}
|
|
40736
41257
|
|
|
40737
41258
|
` : "";
|
|
40738
|
-
|
|
41259
|
+
const impactLine = impact ? `--- IMPACT: ${impact} ---
|
|
41260
|
+
|
|
41261
|
+
` : "";
|
|
41262
|
+
const adjudicatedBlock = adjudicated && adjudicated.length > 0 ? `--- ADJUDICATED \u2014 the operator already decided these; do not re-raise without new information ---
|
|
41263
|
+
${adjudicated.map((q) => `- Q: ${q.body}
|
|
41264
|
+
${q.status === "rejected" ? "(rejected" + (q.answer ? `: ${q.answer}` : "") + ")" : `A: ${q.answer ?? "(acknowledged, no answer recorded)"}`}`).join("\n")}
|
|
41265
|
+
|
|
41266
|
+
` : "";
|
|
41267
|
+
return `${identity}${intent}${impactLine}${adjudicatedBlock}`;
|
|
40739
41268
|
}
|
|
40740
41269
|
function reviserIdentityInstructions(taskId, title) {
|
|
40741
41270
|
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.
|
|
@@ -40774,7 +41303,21 @@ function buildSpecGateDeps(args) {
|
|
|
40774
41303
|
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");
|
|
40775
41304
|
}
|
|
40776
41305
|
}
|
|
40777
|
-
|
|
41306
|
+
const taskInboxItems = await client.listInboxItems(projectSlug, { taskId });
|
|
41307
|
+
const adjudicated = taskInboxItems.filter((item) => item.origin === "gate" && (item.status === "acknowledged" || item.status === "rejected")).map((item) => ({
|
|
41308
|
+
body: item.body,
|
|
41309
|
+
answer: item.answer,
|
|
41310
|
+
status: item.status
|
|
41311
|
+
}));
|
|
41312
|
+
return {
|
|
41313
|
+
taskId,
|
|
41314
|
+
seed: resolvedSeed,
|
|
41315
|
+
title: task.title ?? "",
|
|
41316
|
+
domain: task.domain,
|
|
41317
|
+
notes,
|
|
41318
|
+
impact: task.impact,
|
|
41319
|
+
adjudicated
|
|
41320
|
+
};
|
|
40778
41321
|
})();
|
|
40779
41322
|
}
|
|
40780
41323
|
return intentPromise;
|
|
@@ -40799,8 +41342,8 @@ function buildSpecGateDeps(args) {
|
|
|
40799
41342
|
},
|
|
40800
41343
|
async runHead(personaName, artifact, dismissed) {
|
|
40801
41344
|
const persona = loadPersona(personaName);
|
|
40802
|
-
const { taskId, title, seed } = await loadIntent();
|
|
40803
|
-
const intentBlock = specHeadIntentBlock(taskId, title, seed);
|
|
41345
|
+
const { taskId, title, seed, impact, adjudicated } = await loadIntent();
|
|
41346
|
+
const intentBlock = specHeadIntentBlock(taskId, title, seed, impact, adjudicated);
|
|
40804
41347
|
return runCodexExec(
|
|
40805
41348
|
{
|
|
40806
41349
|
prompt: `Review this spec against the operator's intent.
|
|
@@ -40994,12 +41537,14 @@ async function buildDeliverableGateDeps(args) {
|
|
|
40994
41537
|
const page = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
40995
41538
|
const taskId = page.taskId ?? null;
|
|
40996
41539
|
let domain2 = "code";
|
|
41540
|
+
let impact;
|
|
40997
41541
|
let taskTitle = "";
|
|
40998
41542
|
let specPageId = null;
|
|
40999
41543
|
let criteria = [];
|
|
41000
41544
|
if (taskId) {
|
|
41001
41545
|
const task = await client.getTask(projectSlug, taskId);
|
|
41002
41546
|
domain2 = normalizeDomain(task?.domain);
|
|
41547
|
+
impact = task?.impact;
|
|
41003
41548
|
taskTitle = task?.title ?? "";
|
|
41004
41549
|
if (task?.specWikiPageId) {
|
|
41005
41550
|
specPageId = task.specWikiPageId;
|
|
@@ -41007,7 +41552,7 @@ async function buildDeliverableGateDeps(args) {
|
|
|
41007
41552
|
criteria = parseAcceptanceCriteria(specPage.markdownBody)?.items ?? [];
|
|
41008
41553
|
}
|
|
41009
41554
|
}
|
|
41010
|
-
const heads = resolveHeads(domain2);
|
|
41555
|
+
const heads = resolveHeads(domain2, impact);
|
|
41011
41556
|
const maxHeadCalls = 2 * rounds * (heads.length + 1);
|
|
41012
41557
|
const criteriaBlock = renderCriteriaBlock(criteria);
|
|
41013
41558
|
return {
|
|
@@ -41250,6 +41795,17 @@ var SPECS_FOLDER = "Specs";
|
|
|
41250
41795
|
var DRAFT_SPEC_TLDR_MAX_CHARS = 300;
|
|
41251
41796
|
var DRAFT_SPEC_PERSONA = "spec-author";
|
|
41252
41797
|
var PAGE_ID_MAX_ATTEMPTS = 5;
|
|
41798
|
+
function resolveSpecBudget(impact) {
|
|
41799
|
+
const tier = impact?.toLowerCase();
|
|
41800
|
+
switch (tier) {
|
|
41801
|
+
case "high":
|
|
41802
|
+
return { maxRequirements: 12, maxChars: 25e3 };
|
|
41803
|
+
case "medium":
|
|
41804
|
+
return { maxRequirements: 6, maxChars: 12e3 };
|
|
41805
|
+
default:
|
|
41806
|
+
return { maxRequirements: 3, maxChars: 6e3 };
|
|
41807
|
+
}
|
|
41808
|
+
}
|
|
41253
41809
|
function defaultSleep(ms) {
|
|
41254
41810
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41255
41811
|
}
|
|
@@ -41262,7 +41818,7 @@ function enforceSpecOutputContract(markdown, taskId, title) {
|
|
|
41262
41818
|
${body}`;
|
|
41263
41819
|
}
|
|
41264
41820
|
let tldrChars = 0;
|
|
41265
|
-
const tldrMatch = body.match(/(^##
|
|
41821
|
+
const tldrMatch = body.match(/(^## TL;DR\s*\n)([\s\S]*?)(?=\n## |$)/im);
|
|
41266
41822
|
if (tldrMatch && tldrMatch.index !== void 0) {
|
|
41267
41823
|
const heading = tldrMatch[1] ?? "";
|
|
41268
41824
|
const section = (tldrMatch[2] ?? "").trim();
|
|
@@ -41286,6 +41842,7 @@ function nextWikiPageId(pages) {
|
|
|
41286
41842
|
return `WK-${max + 1}`;
|
|
41287
41843
|
}
|
|
41288
41844
|
function buildDraftSpecPrompt(task, constitution) {
|
|
41845
|
+
const budget = resolveSpecBudget(task.impact);
|
|
41289
41846
|
const lines2 = [
|
|
41290
41847
|
`Write a complete implementation spec for the task below.`,
|
|
41291
41848
|
``,
|
|
@@ -41307,11 +41864,19 @@ function buildDraftSpecPrompt(task, constitution) {
|
|
|
41307
41864
|
}
|
|
41308
41865
|
lines2.push(
|
|
41309
41866
|
`--- OUTPUT CONTRACT (strict) ---`,
|
|
41310
|
-
`Return ONLY the full spec markdown, no commentary. Required structure:`,
|
|
41867
|
+
`Return ONLY the full spec markdown, no commentary. Required structure, in this order:`,
|
|
41311
41868
|
`- First line heading: \`# ${task.id} \u2014 ${task.title}\``,
|
|
41312
|
-
`-
|
|
41313
|
-
`-
|
|
41314
|
-
`
|
|
41869
|
+
`- \`## TL;DR\``,
|
|
41870
|
+
`- One or more \`### Requirement: <name>\` sections, each stating what the change SHALL do.`,
|
|
41871
|
+
` Each requirement has one or more \`#### Scenario: <name>\` sub-sections using WHEN/THEN`,
|
|
41872
|
+
` (optionally AND) to describe a concrete situation and its expected outcome.`,
|
|
41873
|
+
`- \`## Acceptance criteria\`: a markdown checklist (\`- [ ] ...\`) of concrete, verifiable`,
|
|
41874
|
+
` conditions \u2014 this is the machine-usable yardstick the review scores against.`,
|
|
41875
|
+
`- \`## Verification\`: the exact tests, commands, or manual checks that prove each`,
|
|
41876
|
+
` acceptance criterion holds.`,
|
|
41877
|
+
``,
|
|
41878
|
+
`The \`## TL;DR\` section MUST be at most ${DRAFT_SPEC_TLDR_MAX_CHARS} characters.`,
|
|
41879
|
+
`The spec MUST contain at most ${budget.maxRequirements} acceptance-criteria items and no more than ${budget.maxChars} characters total.`
|
|
41315
41880
|
);
|
|
41316
41881
|
return lines2.join("\n");
|
|
41317
41882
|
}
|
|
@@ -41589,6 +42154,11 @@ function renderMilestone(m, ctx) {
|
|
|
41589
42154
|
return "Autodeploy is enabled, but this task has nothing deployable \u2014 skipping";
|
|
41590
42155
|
case "daily_limit_reached":
|
|
41591
42156
|
return "Daily autonomous operations limit reached \u2014 I will continue tomorrow";
|
|
42157
|
+
case "thread_retired": {
|
|
42158
|
+
if (ctx.reason === "bounded_no_state")
|
|
42159
|
+
return "Started a fresh conversation for this task \u2014 the previous one grew too long and no progress notes were saved, so some earlier reasoning may be lost";
|
|
42160
|
+
return "Started a fresh conversation for this task \u2014 the previous one grew too long, so I carried the saved progress notes over";
|
|
42161
|
+
}
|
|
41592
42162
|
}
|
|
41593
42163
|
}
|
|
41594
42164
|
|
|
@@ -41612,6 +42182,82 @@ async function postRunFeed(client, projectSlug, runId, taskId, milestone, ctx =
|
|
|
41612
42182
|
}
|
|
41613
42183
|
}
|
|
41614
42184
|
|
|
42185
|
+
// src/output-projection.ts
|
|
42186
|
+
var FAILURE_PATTERNS = [
|
|
42187
|
+
/^\s*(?:E|FAIL|ERROR|ERR!)\b/i,
|
|
42188
|
+
/\bTraceback \(most recent call last\)/,
|
|
42189
|
+
/^\s*at\s+.+:\d+:\d+/,
|
|
42190
|
+
// stack frames
|
|
42191
|
+
/\bAssertionError\b|\bexpected\b.*\breceived\b|\bexpected:\b/i,
|
|
42192
|
+
/\berror\b[:\s]|\bfailed\b[:\s]|\bfailure\b[:\s]/i,
|
|
42193
|
+
/^\s*not ok\b/,
|
|
42194
|
+
// TAP
|
|
42195
|
+
/\b(?:SyntaxError|TypeError|ReferenceError|RangeError)\b/,
|
|
42196
|
+
/^\s*✕|^\s*✗|^\s*×/,
|
|
42197
|
+
/\bexit(?:ed)? (?:code|status)\s*[:=]?\s*[1-9]/i
|
|
42198
|
+
];
|
|
42199
|
+
var DEFAULT_OUTPUT_PROJECTION_MAX_CHARS = 2e3;
|
|
42200
|
+
function projectCommandOutput(output3, options = {}) {
|
|
42201
|
+
const maxChars = options.maxChars ?? DEFAULT_OUTPUT_PROJECTION_MAX_CHARS;
|
|
42202
|
+
const exitStatus = options.exitStatus ?? null;
|
|
42203
|
+
const fullOutputRef = options.fullOutputRef ?? null;
|
|
42204
|
+
const label = options.label ?? "command output";
|
|
42205
|
+
const lines2 = output3.split(/\r?\n/);
|
|
42206
|
+
const matched = [];
|
|
42207
|
+
for (let index = 0; index < lines2.length; index += 1) {
|
|
42208
|
+
const line = lines2[index] ?? "";
|
|
42209
|
+
if (FAILURE_PATTERNS.some((pattern) => pattern.test(line))) matched.push(index);
|
|
42210
|
+
}
|
|
42211
|
+
const candidates = matched.length > 0 ? matched : lines2.map((_, index) => index);
|
|
42212
|
+
const kept = [];
|
|
42213
|
+
let used = 0;
|
|
42214
|
+
for (let cursor = candidates.length - 1; cursor >= 0; cursor -= 1) {
|
|
42215
|
+
const index = candidates[cursor];
|
|
42216
|
+
if (index === void 0) continue;
|
|
42217
|
+
let text = lines2[index] ?? "";
|
|
42218
|
+
if (text.trim().length === 0) continue;
|
|
42219
|
+
if (text.length > maxChars) {
|
|
42220
|
+
text = `${text.slice(0, Math.max(0, maxChars - 14))}\u2026[line cut]`;
|
|
42221
|
+
}
|
|
42222
|
+
const cost = text.length + 1;
|
|
42223
|
+
if (used + cost > maxChars) break;
|
|
42224
|
+
kept.push({ index, text });
|
|
42225
|
+
used += cost;
|
|
42226
|
+
}
|
|
42227
|
+
kept.sort((a, b) => a.index - b.index);
|
|
42228
|
+
const failureLines = kept.map((entry) => entry.text);
|
|
42229
|
+
const carried = failureLines.reduce((total, line) => total + line.length + 1, 0);
|
|
42230
|
+
const omittedChars = Math.max(0, output3.length - carried);
|
|
42231
|
+
const parts = [`${label}:`];
|
|
42232
|
+
parts.push(exitStatus === null ? "exit status unknown" : `exit status ${exitStatus}`);
|
|
42233
|
+
parts.push(
|
|
42234
|
+
matched.length > 0 ? `${failureLines.length} of ${matched.length} failure-bearing line(s) shown` : `no failure markers found; showing the tail (${failureLines.length} line(s))`
|
|
42235
|
+
);
|
|
42236
|
+
if (omittedChars > 0) {
|
|
42237
|
+
parts.push(
|
|
42238
|
+
fullOutputRef ? `${omittedChars} chars omitted \u2014 retrieve ${fullOutputRef} for the full output` : `${omittedChars} chars omitted`
|
|
42239
|
+
);
|
|
42240
|
+
}
|
|
42241
|
+
return {
|
|
42242
|
+
exitStatus,
|
|
42243
|
+
failureLines,
|
|
42244
|
+
summary: parts.join(" "),
|
|
42245
|
+
omittedChars,
|
|
42246
|
+
fullOutputRef,
|
|
42247
|
+
usedTailFallback: matched.length === 0
|
|
42248
|
+
};
|
|
42249
|
+
}
|
|
42250
|
+
function renderOutputProjection(projection) {
|
|
42251
|
+
const lines2 = [projection.summary];
|
|
42252
|
+
if (projection.failureLines.length > 0) {
|
|
42253
|
+
lines2.push(...projection.failureLines);
|
|
42254
|
+
}
|
|
42255
|
+
return lines2.join("\n");
|
|
42256
|
+
}
|
|
42257
|
+
function outputProjectionDisabled(env = process.env) {
|
|
42258
|
+
return (env.MRRLIN_EXEC_OUTPUT_PROJECTION_DISABLE ?? "").trim() === "1";
|
|
42259
|
+
}
|
|
42260
|
+
|
|
41615
42261
|
// src/deploy-gate.ts
|
|
41616
42262
|
var DEFAULT_DEPLOY_POLL_BUDGET_SECONDS = 1200;
|
|
41617
42263
|
var DEFAULT_PROD_GRACE_SECONDS = 900;
|
|
@@ -41967,10 +42613,48 @@ async function runDeployGateTick(deps, projectSlug, run3, task) {
|
|
|
41967
42613
|
task,
|
|
41968
42614
|
run: run3
|
|
41969
42615
|
});
|
|
42616
|
+
const verificationAttempt = attemptIndex(artifacts, env, "verification") + 1;
|
|
42617
|
+
let outputRef = null;
|
|
42618
|
+
if (verdict.fullOutput && !outputProjectionDisabled()) {
|
|
42619
|
+
outputRef = `EA-veroutput-${env}-${verificationAttempt}`;
|
|
42620
|
+
const stored = await deps.client.appendExecutionArtifact(projectSlug, run3.id, {
|
|
42621
|
+
id: outputRef,
|
|
42622
|
+
kind: "command_output",
|
|
42623
|
+
payload: { env, source: "verification-turn", output: verdict.fullOutput }
|
|
42624
|
+
}).then(() => true).catch(() => false);
|
|
42625
|
+
if (!stored) outputRef = null;
|
|
42626
|
+
}
|
|
42627
|
+
const projection = verdict.fullOutput && !outputProjectionDisabled() ? projectCommandOutput(verdict.fullOutput, {
|
|
42628
|
+
// A model turn reports no exit code; claiming 0 would assert a success never observed.
|
|
42629
|
+
exitStatus: null,
|
|
42630
|
+
fullOutputRef: outputRef,
|
|
42631
|
+
label: `verification (${env})`
|
|
42632
|
+
}) : null;
|
|
42633
|
+
if (projection?.usedTailFallback) {
|
|
42634
|
+
console.warn(
|
|
42635
|
+
JSON.stringify({
|
|
42636
|
+
event: "output_projection_tail_fallback",
|
|
42637
|
+
projectSlug,
|
|
42638
|
+
runId: run3.id,
|
|
42639
|
+
env,
|
|
42640
|
+
detail: "no failure marker matched; the projection carries the tail instead"
|
|
42641
|
+
})
|
|
42642
|
+
);
|
|
42643
|
+
}
|
|
41970
42644
|
await deps.client.appendExecutionArtifact(projectSlug, run3.id, {
|
|
41971
|
-
id: `EA-verification-${env}-${
|
|
42645
|
+
id: `EA-verification-${env}-${verificationAttempt}`,
|
|
41972
42646
|
kind: "verification",
|
|
41973
|
-
payload: {
|
|
42647
|
+
payload: {
|
|
42648
|
+
env,
|
|
42649
|
+
verdict: verdict.verdict,
|
|
42650
|
+
summary: verdict.summary,
|
|
42651
|
+
turnRef: verdict.turnRef,
|
|
42652
|
+
...projection ? {
|
|
42653
|
+
projection: renderOutputProjection(projection),
|
|
42654
|
+
fullOutputArtifactId: projection.fullOutputRef,
|
|
42655
|
+
omittedChars: projection.omittedChars
|
|
42656
|
+
} : {}
|
|
42657
|
+
}
|
|
41974
42658
|
});
|
|
41975
42659
|
if (verdict.verdict === "fail") {
|
|
41976
42660
|
if (env === "prod" && task.autonomyLevel === "auto") {
|
|
@@ -42282,6 +42966,112 @@ function buildPullRequestBody(input) {
|
|
|
42282
42966
|
${lines2.join("\n")}`;
|
|
42283
42967
|
}
|
|
42284
42968
|
|
|
42969
|
+
// src/context-compact/policy.ts
|
|
42970
|
+
var DEFAULT_THRESHOLD_CHARS = 12e3;
|
|
42971
|
+
var DEFAULT_MAX_ITEMS = 25;
|
|
42972
|
+
function readPositiveInt(value, fallback) {
|
|
42973
|
+
const raw = (value ?? "").trim();
|
|
42974
|
+
if (!raw) return fallback;
|
|
42975
|
+
const parsed = Number.parseInt(raw, 10);
|
|
42976
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
42977
|
+
}
|
|
42978
|
+
function readCompactPolicy(env = process.env) {
|
|
42979
|
+
const disabled = (env.MRRLIN_MCP_COMPACT_DISABLE ?? "").trim() === "1";
|
|
42980
|
+
return {
|
|
42981
|
+
enabled: !disabled,
|
|
42982
|
+
thresholdChars: readPositiveInt(env.MRRLIN_MCP_COMPACT_THRESHOLD_CHARS, DEFAULT_THRESHOLD_CHARS),
|
|
42983
|
+
maxItems: readPositiveInt(env.MRRLIN_MCP_COMPACT_MAX_ITEMS, DEFAULT_MAX_ITEMS),
|
|
42984
|
+
debug: (env.MRRLIN_MCP_COMPACT_DEBUG ?? "").trim() === "1",
|
|
42985
|
+
retrievalBudgetDisable: (env.MRRLIN_RETRIEVAL_BUDGET_DISABLE ?? "").trim() === "1"
|
|
42986
|
+
};
|
|
42987
|
+
}
|
|
42988
|
+
function warmDeltaDisabled(env = process.env) {
|
|
42989
|
+
return (env.MRRLIN_EXEC_WARM_DELTA_DISABLE ?? "").trim() === "1";
|
|
42990
|
+
}
|
|
42991
|
+
var DEFAULT_THREAD_MAX_RUNS = 12;
|
|
42992
|
+
var THREAD_MAX_RUNS_MIN = 4;
|
|
42993
|
+
var THREAD_MAX_RUNS_MAX = 50;
|
|
42994
|
+
var DEFAULT_THREAD_RETIRE_GRACE_RUNS = 6;
|
|
42995
|
+
var warnedOutOfRange = /* @__PURE__ */ new Set();
|
|
42996
|
+
function readBoundedInt(name, raw, fallback, min, max) {
|
|
42997
|
+
const trimmed = (raw ?? "").trim();
|
|
42998
|
+
if (!trimmed) return fallback;
|
|
42999
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
43000
|
+
if (Number.isInteger(parsed) && parsed >= min && parsed <= max) return parsed;
|
|
43001
|
+
if (!warnedOutOfRange.has(name)) {
|
|
43002
|
+
warnedOutOfRange.add(name);
|
|
43003
|
+
console.warn(
|
|
43004
|
+
JSON.stringify({
|
|
43005
|
+
event: "thread_retirement_bound_out_of_range",
|
|
43006
|
+
variable: name,
|
|
43007
|
+
provided: trimmed,
|
|
43008
|
+
accepted: `${min}-${max}`,
|
|
43009
|
+
usingDefault: fallback
|
|
43010
|
+
})
|
|
43011
|
+
);
|
|
43012
|
+
}
|
|
43013
|
+
return fallback;
|
|
43014
|
+
}
|
|
43015
|
+
function threadMaxRuns(env = process.env) {
|
|
43016
|
+
return readBoundedInt(
|
|
43017
|
+
"MRRLIN_EXEC_THREAD_MAX_RUNS",
|
|
43018
|
+
env.MRRLIN_EXEC_THREAD_MAX_RUNS,
|
|
43019
|
+
DEFAULT_THREAD_MAX_RUNS,
|
|
43020
|
+
THREAD_MAX_RUNS_MIN,
|
|
43021
|
+
THREAD_MAX_RUNS_MAX
|
|
43022
|
+
);
|
|
43023
|
+
}
|
|
43024
|
+
function threadRetireGraceRuns(env = process.env) {
|
|
43025
|
+
return readBoundedInt(
|
|
43026
|
+
"MRRLIN_EXEC_THREAD_RETIRE_GRACE_RUNS",
|
|
43027
|
+
env.MRRLIN_EXEC_THREAD_RETIRE_GRACE_RUNS,
|
|
43028
|
+
DEFAULT_THREAD_RETIRE_GRACE_RUNS,
|
|
43029
|
+
0,
|
|
43030
|
+
THREAD_MAX_RUNS_MAX
|
|
43031
|
+
);
|
|
43032
|
+
}
|
|
43033
|
+
function threadRetireDisabled(env = process.env) {
|
|
43034
|
+
return (env.MRRLIN_EXEC_THREAD_RETIRE_DISABLE ?? "").trim() === "1";
|
|
43035
|
+
}
|
|
43036
|
+
|
|
43037
|
+
// src/thread-retirement.ts
|
|
43038
|
+
function decideThreadRetirement(input) {
|
|
43039
|
+
if (input.disabled) return { retire: false };
|
|
43040
|
+
if (input.threadRunCount < input.maxRuns) return { retire: false };
|
|
43041
|
+
if (input.hasNarrative) return { retire: true, reason: "bounded_run_count" };
|
|
43042
|
+
if (input.threadRunCount >= input.maxRuns + input.graceRuns) {
|
|
43043
|
+
return { retire: true, reason: "bounded_no_state" };
|
|
43044
|
+
}
|
|
43045
|
+
return { retire: false };
|
|
43046
|
+
}
|
|
43047
|
+
function hasNarrativeContent(state) {
|
|
43048
|
+
if (!state) return false;
|
|
43049
|
+
return [state.goal, state.acceptedRequirements, state.decisions, state.openProblems, state.nextActions].some(
|
|
43050
|
+
(field) => typeof field === "string" && field.trim().length > 0
|
|
43051
|
+
);
|
|
43052
|
+
}
|
|
43053
|
+
|
|
43054
|
+
// src/effort-tier.ts
|
|
43055
|
+
var DEFAULT_EFFORT_LEVEL = "medium";
|
|
43056
|
+
var MECHANICAL_EFFORT_LEVEL = "low";
|
|
43057
|
+
var ESCALATED_EFFORT_LEVEL = "high";
|
|
43058
|
+
function deriveEffort(input) {
|
|
43059
|
+
if (input.disabled) return null;
|
|
43060
|
+
if (input.impact === "high") {
|
|
43061
|
+
return {
|
|
43062
|
+
level: ESCALATED_EFFORT_LEVEL,
|
|
43063
|
+
escalationReason: "task impact is high (operator-set)"
|
|
43064
|
+
};
|
|
43065
|
+
}
|
|
43066
|
+
if (input.impact === "low") {
|
|
43067
|
+
return { level: MECHANICAL_EFFORT_LEVEL, escalationReason: null };
|
|
43068
|
+
}
|
|
43069
|
+
return { level: DEFAULT_EFFORT_LEVEL, escalationReason: null };
|
|
43070
|
+
}
|
|
43071
|
+
function effortDisabled(env = process.env) {
|
|
43072
|
+
return (env.MRRLIN_EXEC_EFFORT_DISABLE ?? "").trim() === "1";
|
|
43073
|
+
}
|
|
43074
|
+
|
|
42285
43075
|
// src/execution-consumer.ts
|
|
42286
43076
|
var DEFAULT_STALE_SECONDS = 300;
|
|
42287
43077
|
var DEFAULT_MAX_LEASE_RECLAIMS = 3;
|
|
@@ -42313,6 +43103,13 @@ function readPositiveIntEnv(name, fallback) {
|
|
|
42313
43103
|
function delayMs(ms) {
|
|
42314
43104
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
42315
43105
|
}
|
|
43106
|
+
function executionBundleBudget() {
|
|
43107
|
+
if ((process.env.MRRLIN_RETRIEVAL_BUDGET_DISABLE ?? "").trim() === "1") return void 0;
|
|
43108
|
+
return {
|
|
43109
|
+
totalBytes: readPositiveIntEnv("MRRLIN_RETRIEVAL_BUDGET_BUNDLE_CHARS", 15500),
|
|
43110
|
+
roundTripCap: 0
|
|
43111
|
+
};
|
|
43112
|
+
}
|
|
42316
43113
|
function execTurnIdleTimeoutMs() {
|
|
42317
43114
|
return readPositiveIntEnv("MRRLIN_DIRECTOR_BRIDGE_EXEC_TURN_IDLE_TIMEOUT_SECONDS", DEFAULT_EXEC_TURN_IDLE_TIMEOUT_SECONDS) * 1e3;
|
|
42318
43115
|
}
|
|
@@ -42439,6 +43236,18 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
42439
43236
|
" ## Result \u2014 what was accomplished, key findings or metrics, follow-up tasks created.",
|
|
42440
43237
|
" Do NOT remove earlier sections \u2014 they remain useful for context.",
|
|
42441
43238
|
" Adapt depth to the task: a one-line fix needs one sentence, not four headings.",
|
|
43239
|
+
"",
|
|
43240
|
+
// Slice 4 of token-efficient-execution-runs (D8). This is the ONLY way the reasoning behind a
|
|
43241
|
+
// decision survives past this conversation: a thread is retired once it has accumulated too many
|
|
43242
|
+
// runs, and the next run then starts from this record plus the repository. Everything else the
|
|
43243
|
+
// next run receives (spec, run history, artifacts) is reconstructed from the database anyway —
|
|
43244
|
+
// what only the model holds is why it chose what it chose, and what it intended to do next.
|
|
43245
|
+
"BEFORE you end your turn, call `update_task_state` to record where this task now stands: the",
|
|
43246
|
+
"goal as you understand it, decisions you made AND WHY, problems still open, and the next",
|
|
43247
|
+
"concrete actions. A later run may start from a FRESH conversation that has none of this turn's",
|
|
43248
|
+
"context \u2014 that record is what it will read. A decision without its reason is the thing most",
|
|
43249
|
+
"often lost, so state the reason. Omit any field you learned nothing new about; an omitted field",
|
|
43250
|
+
"keeps its previous value. Do not restate the spec or the file list \u2014 the next run gets those.",
|
|
42442
43251
|
""
|
|
42443
43252
|
];
|
|
42444
43253
|
var CONSUMER_BASE_INSTRUCTIONS = [
|
|
@@ -42674,13 +43483,15 @@ async function runHasLease(deps, projectSlug, runId, leaseId) {
|
|
|
42674
43483
|
return false;
|
|
42675
43484
|
}
|
|
42676
43485
|
}
|
|
42677
|
-
async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inputText, overallMs, phase) {
|
|
43486
|
+
async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inputText, overallMs, phase, effort) {
|
|
42678
43487
|
const mapper = new CodexBridgeEventMapper();
|
|
42679
43488
|
mapper.reset();
|
|
42680
43489
|
let turnError = null;
|
|
42681
43490
|
let interruptFailed = false;
|
|
42682
43491
|
let touch = () => {
|
|
42683
43492
|
};
|
|
43493
|
+
let turnUsage = null;
|
|
43494
|
+
const promptBytes = Buffer.byteLength(inputText, "utf8");
|
|
42684
43495
|
const turnStartedAt = Date.now();
|
|
42685
43496
|
let lastActivityAt = turnStartedAt;
|
|
42686
43497
|
const persist = async (event) => {
|
|
@@ -42696,7 +43507,7 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
42696
43507
|
} catch {
|
|
42697
43508
|
}
|
|
42698
43509
|
};
|
|
42699
|
-
return deps.
|
|
43510
|
+
return deps.run("exec", async () => {
|
|
42700
43511
|
const pending = [];
|
|
42701
43512
|
let resolveTurnEnded = null;
|
|
42702
43513
|
const turnEnded = new Promise((resolve) => {
|
|
@@ -42709,6 +43520,8 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
42709
43520
|
lastActivityAt = Date.now();
|
|
42710
43521
|
touch();
|
|
42711
43522
|
}
|
|
43523
|
+
const usage = !notifThreadId || notifThreadId === threadId ? extractTurnTokenUsage(method, params) : null;
|
|
43524
|
+
if (usage) turnUsage = usage;
|
|
42712
43525
|
const events = mapper.mapNotification(
|
|
42713
43526
|
{ method, params },
|
|
42714
43527
|
threadId
|
|
@@ -42734,7 +43547,8 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
42734
43547
|
touch = activity.touch;
|
|
42735
43548
|
const accepted = await codex.turn.start({
|
|
42736
43549
|
threadId,
|
|
42737
|
-
input: [{ type: "text", text: inputText }]
|
|
43550
|
+
input: [{ type: "text", text: inputText }],
|
|
43551
|
+
...effort === void 0 ? {} : { effort }
|
|
42738
43552
|
});
|
|
42739
43553
|
acceptedTurnId = accepted?.turn?.id ?? null;
|
|
42740
43554
|
await turnEnded;
|
|
@@ -42769,7 +43583,8 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
42769
43583
|
clearInterval(touchTimer);
|
|
42770
43584
|
unsub();
|
|
42771
43585
|
}
|
|
42772
|
-
|
|
43586
|
+
const telemetry = { promptBytes, usage: turnUsage };
|
|
43587
|
+
return turnError ? { ok: false, error: turnError, interruptFailed, telemetry } : { ok: true, telemetry };
|
|
42773
43588
|
});
|
|
42774
43589
|
}
|
|
42775
43590
|
async function driveRun(deps, projectSlug, runId) {
|
|
@@ -42833,11 +43648,53 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
42833
43648
|
...context,
|
|
42834
43649
|
compact: {
|
|
42835
43650
|
projectSlug,
|
|
42836
|
-
specWikiPageId: task.specWikiPageId ?? null
|
|
43651
|
+
specWikiPageId: task.specWikiPageId ?? null,
|
|
43652
|
+
budget: executionBundleBudget()
|
|
42837
43653
|
}
|
|
42838
43654
|
} : context
|
|
42839
43655
|
);
|
|
42840
43656
|
bundle += await deployWorkflowReverifyNotice(deps.client, projectSlug);
|
|
43657
|
+
const runTelemetry = {
|
|
43658
|
+
promptBytes: 0,
|
|
43659
|
+
turnCount: 0,
|
|
43660
|
+
usage: null
|
|
43661
|
+
};
|
|
43662
|
+
const recordTurn = (outcome) => {
|
|
43663
|
+
runTelemetry.promptBytes += outcome.telemetry.promptBytes;
|
|
43664
|
+
runTelemetry.turnCount += 1;
|
|
43665
|
+
runTelemetry.usage = addTurnTokenUsage(runTelemetry.usage, outcome.telemetry.usage);
|
|
43666
|
+
return outcome;
|
|
43667
|
+
};
|
|
43668
|
+
const effortDecision = deriveEffort({
|
|
43669
|
+
impact: task.impact,
|
|
43670
|
+
type: task.type,
|
|
43671
|
+
disabled: effortDisabled()
|
|
43672
|
+
});
|
|
43673
|
+
const taskState = await deps.client.getTaskState(projectSlug, task.id).catch(() => null);
|
|
43674
|
+
const retirement = decideThreadRetirement({
|
|
43675
|
+
threadRunCount: taskState?.threadRunCount ?? 0,
|
|
43676
|
+
hasNarrative: hasNarrativeContent(taskState),
|
|
43677
|
+
disabled: threadRetireDisabled(),
|
|
43678
|
+
maxRuns: threadMaxRuns(),
|
|
43679
|
+
graceRuns: threadRetireGraceRuns()
|
|
43680
|
+
});
|
|
43681
|
+
const threadIdForAcquire = retirement.retire ? null : task.directorThreadId;
|
|
43682
|
+
if (retirement.retire) {
|
|
43683
|
+
console.warn(
|
|
43684
|
+
JSON.stringify({
|
|
43685
|
+
event: "thread_retirement_triggered",
|
|
43686
|
+
projectSlug,
|
|
43687
|
+
runId,
|
|
43688
|
+
taskId: task.id,
|
|
43689
|
+
reason: retirement.reason,
|
|
43690
|
+
threadRunCount: taskState?.threadRunCount ?? 0,
|
|
43691
|
+
retiringThreadId: task.directorThreadId
|
|
43692
|
+
})
|
|
43693
|
+
);
|
|
43694
|
+
}
|
|
43695
|
+
if (taskState) bundle += `
|
|
43696
|
+
|
|
43697
|
+
${renderTaskStateSection(taskState)}`;
|
|
42841
43698
|
const checkout = deps.checkoutRegistry?.get(projectSlug) ?? null;
|
|
42842
43699
|
let sandbox = "read-only";
|
|
42843
43700
|
let turnCwd = deps.codexCwd ?? null;
|
|
@@ -42913,7 +43770,7 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
42913
43770
|
const pushFn = deps.pushRunBranch ?? pushRunBranch;
|
|
42914
43771
|
try {
|
|
42915
43772
|
const { threadId, resumed } = await acquireThread(codex, {
|
|
42916
|
-
threadId:
|
|
43773
|
+
threadId: threadIdForAcquire,
|
|
42917
43774
|
startParams: {
|
|
42918
43775
|
cwd: turnCwd,
|
|
42919
43776
|
approvalPolicy: "never",
|
|
@@ -42929,9 +43786,36 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
42929
43786
|
},
|
|
42930
43787
|
onThreadIdChanged: async (newId) => {
|
|
42931
43788
|
if (!await runHasLease(deps, projectSlug, runId, leaseId)) return;
|
|
43789
|
+
if (retirement.retire) return;
|
|
42932
43790
|
await deps.client.updateTask(projectSlug, task.id, { directorThreadId: newId });
|
|
42933
43791
|
}
|
|
42934
43792
|
});
|
|
43793
|
+
if (retirement.retire && await runHasLease(deps, projectSlug, runId, leaseId)) {
|
|
43794
|
+
let persisted = true;
|
|
43795
|
+
const retiredThreadId = await deps.client.retireThread(projectSlug, task.id, threadId).catch(() => {
|
|
43796
|
+
persisted = false;
|
|
43797
|
+
return null;
|
|
43798
|
+
});
|
|
43799
|
+
if (persisted) {
|
|
43800
|
+
await postRunFeed(deps.client, projectSlug, runId, task.id, "thread_retired", {
|
|
43801
|
+
reason: retirement.reason
|
|
43802
|
+
});
|
|
43803
|
+
}
|
|
43804
|
+
console.warn(
|
|
43805
|
+
JSON.stringify({
|
|
43806
|
+
event: persisted ? "thread_retired" : "thread_retirement_write_failed",
|
|
43807
|
+
projectSlug,
|
|
43808
|
+
runId,
|
|
43809
|
+
taskId: task.id,
|
|
43810
|
+
reason: retirement.reason,
|
|
43811
|
+
retiredThreadId,
|
|
43812
|
+
newThreadId: threadId,
|
|
43813
|
+
// Says out loud what an unpersisted retirement costs, so the log line is actionable
|
|
43814
|
+
// rather than merely present.
|
|
43815
|
+
...persisted ? {} : { impact: "counter not reset and task still on the old thread; later runs will re-retire until this write succeeds" }
|
|
43816
|
+
})
|
|
43817
|
+
);
|
|
43818
|
+
}
|
|
42935
43819
|
if (!resumed) {
|
|
42936
43820
|
const primingBudgetMs = remainingTurnBudgetMs();
|
|
42937
43821
|
if (primingBudgetMs <= 0) {
|
|
@@ -42946,14 +43830,52 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
42946
43830
|
);
|
|
42947
43831
|
return;
|
|
42948
43832
|
}
|
|
42949
|
-
const primed =
|
|
43833
|
+
const primed = recordTurn(
|
|
43834
|
+
await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, bundle, primingBudgetMs, "priming", effortDecision?.level)
|
|
43835
|
+
);
|
|
42950
43836
|
if (!primed.ok) {
|
|
43837
|
+
if (retirement.retire && task.directorThreadId) {
|
|
43838
|
+
await deps.client.updateTask(projectSlug, task.id, { directorThreadId: task.directorThreadId }).catch(() => {
|
|
43839
|
+
});
|
|
43840
|
+
console.warn(
|
|
43841
|
+
JSON.stringify({
|
|
43842
|
+
event: "thread_retirement_reverted",
|
|
43843
|
+
projectSlug,
|
|
43844
|
+
runId,
|
|
43845
|
+
taskId: task.id,
|
|
43846
|
+
reason: retirement.reason,
|
|
43847
|
+
restoredThreadId: task.directorThreadId,
|
|
43848
|
+
detail: "re-prime failed; predecessor thread restored"
|
|
43849
|
+
})
|
|
43850
|
+
);
|
|
43851
|
+
}
|
|
42951
43852
|
await resetWedgedThread(deps, projectSlug, task.id, primed.interruptFailed);
|
|
42952
43853
|
await failRun(deps, projectSlug, task, runId, leaseId, primed.error);
|
|
42953
43854
|
return;
|
|
42954
43855
|
}
|
|
42955
43856
|
}
|
|
42956
|
-
const
|
|
43857
|
+
const priorRuns = context.runs.filter((r) => r.id !== runId);
|
|
43858
|
+
const priorRun = priorRuns[priorRuns.length - 1] ?? null;
|
|
43859
|
+
const useDelta = resumed && !warmDeltaDisabled() && priorRun !== null;
|
|
43860
|
+
let resumeContent;
|
|
43861
|
+
if (useDelta) {
|
|
43862
|
+
const cutoffTime = priorRun.finishedAt ?? priorRun.createdAt;
|
|
43863
|
+
const newArtifacts = context.artifacts.filter((a) => a.createdAt > cutoffTime);
|
|
43864
|
+
resumeContent = renderRunDelta({
|
|
43865
|
+
task: {
|
|
43866
|
+
id: task.id,
|
|
43867
|
+
status: task.status,
|
|
43868
|
+
latestJudgement: task.latestJudgement,
|
|
43869
|
+
latestJudgementKind: task.latestJudgementKind
|
|
43870
|
+
},
|
|
43871
|
+
newlyFinishedRuns: [priorRun],
|
|
43872
|
+
newArtifacts,
|
|
43873
|
+
omittedArtifactCount: context.omittedArtifactCount
|
|
43874
|
+
});
|
|
43875
|
+
} else {
|
|
43876
|
+
resumeContent = bundle;
|
|
43877
|
+
}
|
|
43878
|
+
const executionPrompt = resumed ? `${resumeContent}
|
|
42957
43879
|
|
|
42958
43880
|
Continue executing task ${task.id}. Make the next concrete increment of progress and record artifacts/judgements via MCP tools.` : `Continue executing task ${task.id}. Make the next concrete increment of progress and record artifacts/judgements via MCP tools.`;
|
|
42959
43881
|
const workBudgetMs = remainingTurnBudgetMs();
|
|
@@ -42969,7 +43891,9 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
42969
43891
|
);
|
|
42970
43892
|
return;
|
|
42971
43893
|
}
|
|
42972
|
-
const outcome =
|
|
43894
|
+
const outcome = recordTurn(
|
|
43895
|
+
await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, executionPrompt, workBudgetMs, "work", effortDecision?.level)
|
|
43896
|
+
);
|
|
42973
43897
|
if (!outcome.ok) {
|
|
42974
43898
|
await resetWedgedThread(deps, projectSlug, task.id, outcome.interruptFailed);
|
|
42975
43899
|
await failRun(deps, projectSlug, task, runId, leaseId, outcome.error);
|
|
@@ -42993,16 +43917,19 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
42993
43917
|
})
|
|
42994
43918
|
);
|
|
42995
43919
|
const completionPrompt = buildCompletionTurnPrompt(task.id, readiness.task.status);
|
|
42996
|
-
const completionOutcome =
|
|
42997
|
-
|
|
42998
|
-
|
|
42999
|
-
|
|
43000
|
-
|
|
43001
|
-
|
|
43002
|
-
|
|
43003
|
-
|
|
43004
|
-
|
|
43005
|
-
|
|
43920
|
+
const completionOutcome = recordTurn(
|
|
43921
|
+
await driveTurn(
|
|
43922
|
+
deps,
|
|
43923
|
+
codex,
|
|
43924
|
+
projectSlug,
|
|
43925
|
+
runId,
|
|
43926
|
+
leaseId,
|
|
43927
|
+
threadId,
|
|
43928
|
+
completionPrompt,
|
|
43929
|
+
completionBudget,
|
|
43930
|
+
"completion",
|
|
43931
|
+
effortDecision?.level
|
|
43932
|
+
)
|
|
43006
43933
|
);
|
|
43007
43934
|
if (!completionOutcome.ok) {
|
|
43008
43935
|
await resetWedgedThread(deps, projectSlug, task.id, completionOutcome.interruptFailed);
|
|
@@ -43181,9 +44108,97 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
43181
44108
|
error51 instanceof Error ? error51.message : String(error51)
|
|
43182
44109
|
);
|
|
43183
44110
|
} finally {
|
|
44111
|
+
await deps.client.updateExecutionRun(projectSlug, runId, {
|
|
44112
|
+
promptBytes: runTelemetry.promptBytes,
|
|
44113
|
+
turnCount: runTelemetry.turnCount,
|
|
44114
|
+
inputTokens: runTelemetry.usage?.inputTokens ?? null,
|
|
44115
|
+
cachedInputTokens: runTelemetry.usage?.cachedInputTokens ?? null,
|
|
44116
|
+
outputTokens: runTelemetry.usage?.outputTokens ?? null,
|
|
44117
|
+
// Slice 7 (7.4): record the level actually used and, when it was an escalation, what caused
|
|
44118
|
+
// it — so "why did this run cost that much?" is answerable after the fact. NULL when the
|
|
44119
|
+
// slice is disabled, which per D6 reads as "not set" rather than a level we chose.
|
|
44120
|
+
effort: effortDecision?.level ?? null,
|
|
44121
|
+
effortEscalationReason: effortDecision?.escalationReason ?? null
|
|
44122
|
+
}).catch((error51) => {
|
|
44123
|
+
console.warn(
|
|
44124
|
+
JSON.stringify({
|
|
44125
|
+
event: "run_telemetry_write_failed",
|
|
44126
|
+
projectSlug,
|
|
44127
|
+
runId,
|
|
44128
|
+
detail: error51 instanceof Error ? error51.message : String(error51)
|
|
44129
|
+
})
|
|
44130
|
+
);
|
|
44131
|
+
});
|
|
44132
|
+
await writeServerOwnedTaskState(deps, projectSlug, task.id, runId, leaseId).catch((error51) => {
|
|
44133
|
+
console.warn(
|
|
44134
|
+
JSON.stringify({
|
|
44135
|
+
event: "task_state_server_write_failed",
|
|
44136
|
+
projectSlug,
|
|
44137
|
+
runId,
|
|
44138
|
+
taskId: task.id,
|
|
44139
|
+
detail: error51 instanceof Error ? error51.message : String(error51)
|
|
44140
|
+
})
|
|
44141
|
+
);
|
|
44142
|
+
});
|
|
43184
44143
|
disposeCodex();
|
|
43185
44144
|
}
|
|
43186
44145
|
}
|
|
44146
|
+
async function writeServerOwnedTaskState(deps, projectSlug, taskId, runId, leaseId) {
|
|
44147
|
+
const run3 = await deps.client.getExecutionRun(projectSlug, runId).catch(() => null);
|
|
44148
|
+
if (!run3) {
|
|
44149
|
+
console.warn(
|
|
44150
|
+
JSON.stringify({
|
|
44151
|
+
event: "task_state_server_write_skipped",
|
|
44152
|
+
projectSlug,
|
|
44153
|
+
runId,
|
|
44154
|
+
taskId,
|
|
44155
|
+
detail: "could not read the run's state_seq; skipped rather than writing a defaulted ordering value"
|
|
44156
|
+
})
|
|
44157
|
+
);
|
|
44158
|
+
return;
|
|
44159
|
+
}
|
|
44160
|
+
if (run3.leaseId !== null && run3.leaseId !== leaseId) {
|
|
44161
|
+
console.warn(
|
|
44162
|
+
JSON.stringify({
|
|
44163
|
+
event: "task_state_server_write_skipped",
|
|
44164
|
+
projectSlug,
|
|
44165
|
+
runId,
|
|
44166
|
+
taskId,
|
|
44167
|
+
detail: "the run is held by another lease (reclaimed); refusing to write state for a run we no longer own"
|
|
44168
|
+
})
|
|
44169
|
+
);
|
|
44170
|
+
return;
|
|
44171
|
+
}
|
|
44172
|
+
let artifacts = null;
|
|
44173
|
+
try {
|
|
44174
|
+
artifacts = await deps.client.listExecutionArtifacts(projectSlug, runId);
|
|
44175
|
+
} catch {
|
|
44176
|
+
artifacts = null;
|
|
44177
|
+
}
|
|
44178
|
+
let changed = null;
|
|
44179
|
+
if (artifacts !== null) {
|
|
44180
|
+
const summaries = artifacts.filter((artifact) => artifact.kind === "diff_summary").map((artifact) => JSON.stringify(artifact.payload));
|
|
44181
|
+
changed = summaries.length > 0 ? summaries.join("\n") : NO_FILE_CHANGES_THIS_RUN;
|
|
44182
|
+
}
|
|
44183
|
+
let latestVerification = null;
|
|
44184
|
+
if (artifacts !== null) {
|
|
44185
|
+
const verifications = artifacts.filter((artifact) => artifact.kind === "verification").map((artifact) => JSON.stringify(artifact.payload));
|
|
44186
|
+
latestVerification = verifications[verifications.length - 1] ?? NO_VERIFICATION_THIS_RUN;
|
|
44187
|
+
}
|
|
44188
|
+
await deps.client.upsertTaskState(projectSlug, taskId, {
|
|
44189
|
+
goal: null,
|
|
44190
|
+
acceptedRequirements: null,
|
|
44191
|
+
decisions: null,
|
|
44192
|
+
openProblems: null,
|
|
44193
|
+
nextActions: null,
|
|
44194
|
+
relevantFiles: null,
|
|
44195
|
+
filesChanged: changed,
|
|
44196
|
+
latestVerification,
|
|
44197
|
+
lastRunSeq: run3.stateSeq
|
|
44198
|
+
});
|
|
44199
|
+
}
|
|
44200
|
+
var NO_FILE_CHANGES_THIS_RUN = "no file changes recorded in this run";
|
|
44201
|
+
var NO_VERIFICATION_THIS_RUN = "not run in this run";
|
|
43187
44202
|
function buildDeliverableAuthorPrompt(task, specMarkdown) {
|
|
43188
44203
|
return [
|
|
43189
44204
|
`Produce the deliverable for the task below. Write the FULL deliverable content as markdown.`,
|
|
@@ -43713,7 +44728,8 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
43713
44728
|
break;
|
|
43714
44729
|
}
|
|
43715
44730
|
try {
|
|
43716
|
-
outcome = await deps.
|
|
44731
|
+
outcome = await deps.run(
|
|
44732
|
+
"async",
|
|
43717
44733
|
() => descriptor2.run(
|
|
43718
44734
|
{ client: deps.client, cwd: deps.cwd, codexExecutable: deps.codexExecutable },
|
|
43719
44735
|
input
|
|
@@ -43788,7 +44804,7 @@ async function runAsyncBackstopSweep(deps, drive = (slug, jobId) => driveAsyncJo
|
|
|
43788
44804
|
} catch {
|
|
43789
44805
|
queued = [];
|
|
43790
44806
|
}
|
|
43791
|
-
|
|
44807
|
+
await Promise.all(queued.map((job) => drive(slug, job.id)));
|
|
43792
44808
|
}
|
|
43793
44809
|
}
|
|
43794
44810
|
|
|
@@ -44256,13 +45272,16 @@ function createInactivityWatchdog(opts) {
|
|
|
44256
45272
|
const clearT = opts.clearTimeoutFn ?? clearTimeout;
|
|
44257
45273
|
const now = opts.now ?? Date.now;
|
|
44258
45274
|
const suspended = opts.suspendedMs ?? (() => 0);
|
|
45275
|
+
const excluded = opts.idleExcludedMs ?? (() => 0);
|
|
44259
45276
|
let idleTimer = null;
|
|
44260
45277
|
let maxTimer = null;
|
|
44261
45278
|
let done = false;
|
|
44262
45279
|
const startAt = now();
|
|
44263
45280
|
const suspendedAtStart = suspended();
|
|
45281
|
+
const excludedAtStart = excluded();
|
|
44264
45282
|
let lastPokeAt = startAt;
|
|
44265
45283
|
let suspendedAtLastPoke = suspendedAtStart;
|
|
45284
|
+
let excludedAtLastPoke = excludedAtStart;
|
|
44266
45285
|
const expire = (reason) => {
|
|
44267
45286
|
if (done) return;
|
|
44268
45287
|
done = true;
|
|
@@ -44280,7 +45299,7 @@ function createInactivityWatchdog(opts) {
|
|
|
44280
45299
|
};
|
|
44281
45300
|
const onIdleDeadline = () => {
|
|
44282
45301
|
if (done) return;
|
|
44283
|
-
const awakeSilence = now() - lastPokeAt - (suspended() - suspendedAtLastPoke);
|
|
45302
|
+
const awakeSilence = now() - lastPokeAt - (suspended() - suspendedAtLastPoke) - (excluded() - excludedAtLastPoke);
|
|
44284
45303
|
if (awakeSilence < opts.idleMs) {
|
|
44285
45304
|
armIdle(opts.idleMs - awakeSilence);
|
|
44286
45305
|
return;
|
|
@@ -44304,6 +45323,7 @@ function createInactivityWatchdog(opts) {
|
|
|
44304
45323
|
if (idleTimer) clearT(idleTimer);
|
|
44305
45324
|
lastPokeAt = now();
|
|
44306
45325
|
suspendedAtLastPoke = suspended();
|
|
45326
|
+
excludedAtLastPoke = excluded();
|
|
44307
45327
|
armIdle(opts.idleMs);
|
|
44308
45328
|
},
|
|
44309
45329
|
cancel: () => {
|
|
@@ -44313,8 +45333,8 @@ function createInactivityWatchdog(opts) {
|
|
|
44313
45333
|
}
|
|
44314
45334
|
};
|
|
44315
45335
|
}
|
|
44316
|
-
var DEFAULT_CHAT_TURN_IDLE_SECONDS =
|
|
44317
|
-
var DEFAULT_CHAT_TURN_MAX_SECONDS =
|
|
45336
|
+
var DEFAULT_CHAT_TURN_IDLE_SECONDS = 300;
|
|
45337
|
+
var DEFAULT_CHAT_TURN_MAX_SECONDS = 2700;
|
|
44318
45338
|
function readPositiveSeconds(name, fallback) {
|
|
44319
45339
|
const raw = (process.env[name] ?? "").trim();
|
|
44320
45340
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -44327,6 +45347,64 @@ function chatTurnMaxMs() {
|
|
|
44327
45347
|
return readPositiveSeconds("MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_MAX_SECONDS", DEFAULT_CHAT_TURN_MAX_SECONDS) * 1e3;
|
|
44328
45348
|
}
|
|
44329
45349
|
|
|
45350
|
+
// src/tool-busy.ts
|
|
45351
|
+
function createToolBusyClock(opts) {
|
|
45352
|
+
const now = opts?.now ?? Date.now;
|
|
45353
|
+
const open = /* @__PURE__ */ new Set();
|
|
45354
|
+
let total = 0;
|
|
45355
|
+
let segmentStart = null;
|
|
45356
|
+
function start(itemId) {
|
|
45357
|
+
if (!open.has(itemId)) {
|
|
45358
|
+
open.add(itemId);
|
|
45359
|
+
if (open.size === 1) {
|
|
45360
|
+
segmentStart = now();
|
|
45361
|
+
}
|
|
45362
|
+
}
|
|
45363
|
+
}
|
|
45364
|
+
function end(itemId) {
|
|
45365
|
+
if (open.has(itemId)) {
|
|
45366
|
+
open.delete(itemId);
|
|
45367
|
+
if (open.size === 0) {
|
|
45368
|
+
const nowVal = now();
|
|
45369
|
+
if (segmentStart !== null) {
|
|
45370
|
+
total += nowVal - segmentStart;
|
|
45371
|
+
segmentStart = null;
|
|
45372
|
+
}
|
|
45373
|
+
}
|
|
45374
|
+
}
|
|
45375
|
+
}
|
|
45376
|
+
function endAll() {
|
|
45377
|
+
if (open.size > 0) {
|
|
45378
|
+
const nowVal = now();
|
|
45379
|
+
if (segmentStart !== null) {
|
|
45380
|
+
total += nowVal - segmentStart;
|
|
45381
|
+
segmentStart = null;
|
|
45382
|
+
}
|
|
45383
|
+
open.clear();
|
|
45384
|
+
}
|
|
45385
|
+
}
|
|
45386
|
+
function ms() {
|
|
45387
|
+
const nowVal = now();
|
|
45388
|
+
if (open.size > 0 && segmentStart !== null) {
|
|
45389
|
+
return total + (nowVal - segmentStart);
|
|
45390
|
+
}
|
|
45391
|
+
return total;
|
|
45392
|
+
}
|
|
45393
|
+
return { start, end, endAll, ms };
|
|
45394
|
+
}
|
|
45395
|
+
var TOOL_ITEM_TYPES = /* @__PURE__ */ new Set(["commandExecution", "mcpToolCall", "fileChange"]);
|
|
45396
|
+
function applyToolBusyNotification(clock, method, params) {
|
|
45397
|
+
if (method !== "item/started" && method !== "item/completed") return;
|
|
45398
|
+
if (typeof params !== "object" || params === null) return;
|
|
45399
|
+
const p = params;
|
|
45400
|
+
const item = typeof p.item === "object" && p.item !== null ? p.item : void 0;
|
|
45401
|
+
if (typeof item?.type !== "string" || !TOOL_ITEM_TYPES.has(item.type)) return;
|
|
45402
|
+
const id = typeof item.id === "string" ? item.id : typeof p.itemId === "string" ? p.itemId : void 0;
|
|
45403
|
+
if (id === void 0) return;
|
|
45404
|
+
if (method === "item/started") clock.start(id);
|
|
45405
|
+
else clock.end(id);
|
|
45406
|
+
}
|
|
45407
|
+
|
|
44330
45408
|
// src/worker-token.ts
|
|
44331
45409
|
var import_node_crypto7 = require("node:crypto");
|
|
44332
45410
|
var import_node_fs9 = require("node:fs");
|
|
@@ -48331,11 +49409,16 @@ function parseVerdict2(text) {
|
|
|
48331
49409
|
let last = null;
|
|
48332
49410
|
while ((match = re.exec(text)) !== null) last = match;
|
|
48333
49411
|
if (!last) {
|
|
48334
|
-
return {
|
|
49412
|
+
return {
|
|
49413
|
+
verdict: "fail",
|
|
49414
|
+
summary: "no parseable VERDICT line in verification output",
|
|
49415
|
+
turnRef: null,
|
|
49416
|
+
fullOutput: text
|
|
49417
|
+
};
|
|
48335
49418
|
}
|
|
48336
49419
|
const verdict = (last[1] ?? "").toLowerCase() === "pass" ? "pass" : "fail";
|
|
48337
49420
|
const summary = (last[2] ?? "").trim() || `verification ${verdict}`;
|
|
48338
|
-
return { verdict, summary, turnRef: null };
|
|
49421
|
+
return { verdict, summary, turnRef: null, fullOutput: text };
|
|
48339
49422
|
}
|
|
48340
49423
|
function isBlockedVerificationHost(hostname3) {
|
|
48341
49424
|
const h = hostname3.toLowerCase().replace(/^\[|\]$/g, "");
|
|
@@ -49208,6 +50291,7 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
49208
50291
|
"- Keep behavior contracts in the spec; implementation details belong in `## Implementation Plan`.",
|
|
49209
50292
|
"- Apply this format ONLY to Specs/{id} pages; do not retro-fit it onto other wiki page types.",
|
|
49210
50293
|
"- 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.",
|
|
50294
|
+
"- 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.",
|
|
49211
50295
|
'- 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.',
|
|
49212
50296
|
"- 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.",
|
|
49213
50297
|
"",
|
|
@@ -50434,7 +51518,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
50434
51518
|
}
|
|
50435
51519
|
};
|
|
50436
51520
|
try {
|
|
50437
|
-
await deps.
|
|
51521
|
+
await deps.run("chat", async () => {
|
|
50438
51522
|
if (currentReplayBuf.state === "interrupted" || currentReplayBuf.state === "completed") {
|
|
50439
51523
|
logCtx?.logger.logOut({
|
|
50440
51524
|
spanId,
|
|
@@ -50463,7 +51547,9 @@ function createBridgeMessageHandler(deps) {
|
|
|
50463
51547
|
outputEvents += 1;
|
|
50464
51548
|
sendInTurn({ type: "event", directorSessionId, event });
|
|
50465
51549
|
};
|
|
51550
|
+
const toolBusy = createToolBusyClock();
|
|
50466
51551
|
const unsub = codexClient.onNotification((method, params) => {
|
|
51552
|
+
applyToolBusyNotification(toolBusy, method, params);
|
|
50467
51553
|
pokeActivity();
|
|
50468
51554
|
if (method === "account/rateLimits/updated") {
|
|
50469
51555
|
const snapshot = params?.rateLimits ?? null;
|
|
@@ -50491,6 +51577,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
50491
51577
|
idleMs,
|
|
50492
51578
|
maxMs,
|
|
50493
51579
|
...deps.suspendedMs ? { suspendedMs: deps.suspendedMs } : {},
|
|
51580
|
+
idleExcludedMs: () => toolBusy.ms(),
|
|
50494
51581
|
onExpire: (reason) => {
|
|
50495
51582
|
rejectWatchdog?.(
|
|
50496
51583
|
new Error(
|
|
@@ -50594,6 +51681,7 @@ ${effectiveText}` : effectiveText;
|
|
|
50594
51681
|
}
|
|
50595
51682
|
watchdog.cancel();
|
|
50596
51683
|
unsub();
|
|
51684
|
+
toolBusy.endAll();
|
|
50597
51685
|
thread.cancel = null;
|
|
50598
51686
|
}
|
|
50599
51687
|
});
|
|
@@ -50713,8 +51801,8 @@ async function startDirectorBridge() {
|
|
|
50713
51801
|
const turnLock = createTurnLock();
|
|
50714
51802
|
let lastTurnEndedAt = Date.now();
|
|
50715
51803
|
const wrappedTurnLock = {
|
|
50716
|
-
|
|
50717
|
-
return turnLock.
|
|
51804
|
+
run(lane, fn) {
|
|
51805
|
+
return turnLock.run(lane, async () => {
|
|
50718
51806
|
try {
|
|
50719
51807
|
return await fn();
|
|
50720
51808
|
} finally {
|
|
@@ -50757,7 +51845,11 @@ async function startDirectorBridge() {
|
|
|
50757
51845
|
}
|
|
50758
51846
|
});
|
|
50759
51847
|
const codexProbeHandle = startCodexProbe();
|
|
50760
|
-
const consumerState = {
|
|
51848
|
+
const consumerState = {
|
|
51849
|
+
inFlightRunIds: /* @__PURE__ */ new Set(),
|
|
51850
|
+
lastClaimedRunId: null,
|
|
51851
|
+
lastClaimAt: null
|
|
51852
|
+
};
|
|
50761
51853
|
const health = createServiceHealth();
|
|
50762
51854
|
try {
|
|
50763
51855
|
migrateRetiredApiBaseUrls();
|
|
@@ -50836,7 +51928,7 @@ async function startDirectorBridge() {
|
|
|
50836
51928
|
// handle returned by acquireExecutorCodex (Fix A + G), so there is no shared
|
|
50837
51929
|
// mutable `activeExecutorClient` slot to race a concurrent chat/bootstrap turn.
|
|
50838
51930
|
releaseCodex: () => codexKeepalive.release(),
|
|
50839
|
-
|
|
51931
|
+
run: wrappedTurnLock.run,
|
|
50840
51932
|
staleSeconds,
|
|
50841
51933
|
touchIntervalMs: Math.min(DEFAULT_TOUCH_INTERVAL_MS, Math.floor(staleSeconds * 1e3 / 2)),
|
|
50842
51934
|
codexCwd: config2.codexCwd,
|
|
@@ -50863,7 +51955,7 @@ async function startDirectorBridge() {
|
|
|
50863
51955
|
// ADR 0007 self-review: critique the committed diff on the executor's own client before
|
|
50864
51956
|
// push/PR. Reuses the verification turn-runner (acquireThread + collect assistant text).
|
|
50865
51957
|
selfReviewTurn: createSelfReviewTurn({
|
|
50866
|
-
runTurn: (codexClient, cwd, prompt) => runCollectedCodexTurn(codexClient, prompt, cwd, SELF_REVIEW_BASE_INSTRUCTIONS)
|
|
51958
|
+
runTurn: (codexClient, cwd, prompt) => wrappedTurnLock.run("chat", () => runCollectedCodexTurn(codexClient, prompt, cwd, SELF_REVIEW_BASE_INSTRUCTIONS))
|
|
50867
51959
|
}),
|
|
50868
51960
|
// ADR 0015 deploy gate: after a pushed run reaches dev_deploy/prod_deploy, the backstop sweep
|
|
50869
51961
|
// dispatches the deploy + polls + runs a verification turn against the deployed env URL in a
|
|
@@ -50902,7 +51994,7 @@ async function startDirectorBridge() {
|
|
|
50902
51994
|
}
|
|
50903
51995
|
};
|
|
50904
51996
|
},
|
|
50905
|
-
runTurn: (codexClient, prompt, cwd) => runCollectedCodexTurn(codexClient, prompt, cwd, VERIFICATION_BASE_INSTRUCTIONS)
|
|
51997
|
+
runTurn: (codexClient, prompt, cwd) => wrappedTurnLock.run("chat", () => runCollectedCodexTurn(codexClient, prompt, cwd, VERIFICATION_BASE_INSTRUCTIONS))
|
|
50906
51998
|
}),
|
|
50907
51999
|
budgetSeconds: deployPollBudgetSeconds()
|
|
50908
52000
|
},
|
|
@@ -50927,7 +52019,7 @@ async function startDirectorBridge() {
|
|
|
50927
52019
|
const bridgeInstanceId = (0, import_node_crypto8.randomUUID)();
|
|
50928
52020
|
const asyncConsumerDeps = {
|
|
50929
52021
|
client,
|
|
50930
|
-
|
|
52022
|
+
run: wrappedTurnLock.run,
|
|
50931
52023
|
cwd: config2.codexCwd ?? process.cwd(),
|
|
50932
52024
|
codexExecutable: config2.codexExecutable,
|
|
50933
52025
|
staleSeconds,
|
|
@@ -50943,40 +52035,34 @@ async function startDirectorBridge() {
|
|
|
50943
52035
|
sink({ type: "async-result", directorSessionId, projectSlug: slug, deliveryKind, body });
|
|
50944
52036
|
}
|
|
50945
52037
|
};
|
|
50946
|
-
|
|
50947
|
-
|
|
50948
|
-
|
|
50949
|
-
};
|
|
50950
|
-
const driveTracked = (slug, runId) => enqueueRun(async () => {
|
|
50951
|
-
consumerState.inFlightRunId = runId;
|
|
52038
|
+
const driveTracked = (slug, runId) => {
|
|
52039
|
+
consumerState.inFlightRunIds.add(runId);
|
|
52040
|
+
consumerState.lastClaimedRunId = runId;
|
|
50952
52041
|
consumerState.lastClaimAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
50953
|
-
|
|
50954
|
-
await driveRun(consumerDeps, slug, runId);
|
|
50955
|
-
} catch (error51) {
|
|
52042
|
+
void driveRun(consumerDeps, slug, runId).catch((error51) => {
|
|
50956
52043
|
process.stderr.write(`[mrrlin-mcp director-bridge] drive failed for ${slug}/${runId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
50957
52044
|
`);
|
|
50958
|
-
}
|
|
50959
|
-
consumerState.
|
|
50960
|
-
}
|
|
50961
|
-
}
|
|
52045
|
+
}).finally(() => {
|
|
52046
|
+
consumerState.inFlightRunIds.delete(runId);
|
|
52047
|
+
});
|
|
52048
|
+
};
|
|
50962
52049
|
const driveRunNow = (slug, runId) => {
|
|
50963
|
-
consumerState.
|
|
52050
|
+
consumerState.inFlightRunIds.add(runId);
|
|
52051
|
+
consumerState.lastClaimedRunId = runId;
|
|
50964
52052
|
consumerState.lastClaimAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
50965
52053
|
void driveRun(consumerDeps, slug, runId).catch((error51) => {
|
|
50966
52054
|
process.stderr.write(`[mrrlin-mcp director-bridge] drive failed for ${slug}/${runId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
50967
52055
|
`);
|
|
50968
52056
|
}).finally(() => {
|
|
50969
|
-
consumerState.
|
|
52057
|
+
consumerState.inFlightRunIds.delete(runId);
|
|
50970
52058
|
});
|
|
50971
52059
|
};
|
|
50972
|
-
const driveAsyncTracked = (slug, jobId) =>
|
|
50973
|
-
|
|
50974
|
-
await driveAsyncJob(asyncConsumerDeps, slug, jobId);
|
|
50975
|
-
} catch (error51) {
|
|
52060
|
+
const driveAsyncTracked = (slug, jobId) => {
|
|
52061
|
+
void driveAsyncJob(asyncConsumerDeps, slug, jobId).catch((error51) => {
|
|
50976
52062
|
process.stderr.write(`[mrrlin-mcp director-bridge] async drive failed for ${slug}/${jobId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
50977
52063
|
`);
|
|
50978
|
-
}
|
|
50979
|
-
}
|
|
52064
|
+
});
|
|
52065
|
+
};
|
|
50980
52066
|
const driveAsyncNow = (slug, jobId) => {
|
|
50981
52067
|
void driveAsyncJob(asyncConsumerDeps, slug, jobId).catch((error51) => {
|
|
50982
52068
|
process.stderr.write(`[mrrlin-mcp director-bridge] async drive failed for ${slug}/${jobId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
@@ -50989,7 +52075,15 @@ async function startDirectorBridge() {
|
|
|
50989
52075
|
port: config2.port,
|
|
50990
52076
|
token: config2.token,
|
|
50991
52077
|
allowedOrigins: config2.allowedOrigins,
|
|
50992
|
-
getHealth: () => ({
|
|
52078
|
+
getHealth: () => ({
|
|
52079
|
+
ok: true,
|
|
52080
|
+
// Most recently claimed run, or null once nothing is in flight — kept for existing consumers.
|
|
52081
|
+
inFlightRunId: consumerState.inFlightRunIds.size > 0 ? consumerState.lastClaimedRunId : null,
|
|
52082
|
+
inFlightRunIds: Array.from(consumerState.inFlightRunIds),
|
|
52083
|
+
inFlightCount: consumerState.inFlightRunIds.size,
|
|
52084
|
+
lastClaimAt: consumerState.lastClaimAt,
|
|
52085
|
+
service: health.snapshot()
|
|
52086
|
+
})
|
|
50993
52087
|
})
|
|
50994
52088
|
);
|
|
50995
52089
|
const threads = /* @__PURE__ */ new Map();
|
|
@@ -51052,7 +52146,7 @@ async function startDirectorBridge() {
|
|
|
51052
52146
|
const sharedHandlerDeps = {
|
|
51053
52147
|
acquireCodex: () => codexKeepalive.acquire(),
|
|
51054
52148
|
releaseCodex: () => codexKeepalive.release(),
|
|
51055
|
-
|
|
52149
|
+
run: wrappedTurnLock.run,
|
|
51056
52150
|
suspendedMs: () => suspensionDetector.suspendedMs(),
|
|
51057
52151
|
buildStartParams: (msg, desiredModel) => buildThreadStartParams(msg, config2, desiredModel),
|
|
51058
52152
|
turnInactivity: () => ({ idleMs: chatTurnIdleMs(), maxMs: chatTurnMaxMs() }),
|
|
@@ -51198,7 +52292,7 @@ async function startDirectorBridge() {
|
|
|
51198
52292
|
await bridgeHealth.recordSuccess();
|
|
51199
52293
|
} catch (error51) {
|
|
51200
52294
|
process.stderr.write(
|
|
51201
|
-
`[mrrlin-mcp director-bridge] ${label} failed: ${
|
|
52295
|
+
`[mrrlin-mcp director-bridge] ${label} failed: ${describeError(error51)}
|
|
51202
52296
|
`
|
|
51203
52297
|
);
|
|
51204
52298
|
bridgeHealth.recordFailure(id, error51);
|
|
@@ -51210,34 +52304,28 @@ async function startDirectorBridge() {
|
|
|
51210
52304
|
const scheduleClaimLoop = () => {
|
|
51211
52305
|
if (claimLoopStopped) return;
|
|
51212
52306
|
claimTimer = setTimeout(() => {
|
|
51213
|
-
|
|
51214
|
-
|
|
51215
|
-
|
|
51216
|
-
|
|
51217
|
-
(
|
|
51218
|
-
|
|
51219
|
-
|
|
51220
|
-
})
|
|
51221
|
-
)
|
|
51222
|
-
);
|
|
51223
|
-
enqueueRun(
|
|
51224
|
-
() => runTrackedSweep(
|
|
51225
|
-
"asyncSweep",
|
|
51226
|
-
"async sweep",
|
|
51227
|
-
() => runAsyncBackstopSweep(asyncConsumerDeps, (slug, jobId) => {
|
|
51228
|
-
driveAsyncTracked(slug, jobId);
|
|
51229
|
-
return Promise.resolve();
|
|
51230
|
-
})
|
|
51231
|
-
)
|
|
52307
|
+
void runTrackedSweep(
|
|
52308
|
+
"claimSweep",
|
|
52309
|
+
"claim sweep",
|
|
52310
|
+
() => runBackstopSweep(consumerDeps, (slug, runId) => {
|
|
52311
|
+
driveTracked(slug, runId);
|
|
52312
|
+
return Promise.resolve();
|
|
52313
|
+
})
|
|
51232
52314
|
);
|
|
51233
|
-
|
|
51234
|
-
|
|
51235
|
-
|
|
51236
|
-
|
|
51237
|
-
|
|
52315
|
+
void runTrackedSweep(
|
|
52316
|
+
"asyncSweep",
|
|
52317
|
+
"async sweep",
|
|
52318
|
+
() => runAsyncBackstopSweep(asyncConsumerDeps, (slug, jobId) => {
|
|
52319
|
+
driveAsyncTracked(slug, jobId);
|
|
52320
|
+
return Promise.resolve();
|
|
51238
52321
|
})
|
|
51239
52322
|
);
|
|
51240
|
-
|
|
52323
|
+
void runOrphanWorktreeSweep(client, {
|
|
52324
|
+
checkoutRegistry,
|
|
52325
|
+
worktreeRoot: import_node_path18.default.join(config2.stateDir, "worktrees"),
|
|
52326
|
+
locksDir: import_node_path18.default.join(config2.stateDir, "locks")
|
|
52327
|
+
});
|
|
52328
|
+
{
|
|
51241
52329
|
const { retainedBuffers, retainedBytes, tombstonesRemoved } = sweepReplayBuffers(threads, tombstones, {
|
|
51242
52330
|
now: Date.now(),
|
|
51243
52331
|
suspendedMs: () => suspensionDetector.suspendedMs(),
|
|
@@ -51275,7 +52363,7 @@ async function startDirectorBridge() {
|
|
|
51275
52363
|
payload: { removed: tombstonesRemoved }
|
|
51276
52364
|
});
|
|
51277
52365
|
}
|
|
51278
|
-
}
|
|
52366
|
+
}
|
|
51279
52367
|
scheduleClaimLoop();
|
|
51280
52368
|
}, claimIntervalMs());
|
|
51281
52369
|
claimTimer.unref?.();
|
|
@@ -51313,7 +52401,7 @@ async function startDirectorBridge() {
|
|
|
51313
52401
|
agentPoolLoopHandle = startAgentPoolLoop({
|
|
51314
52402
|
push: (snapshot) => client.putOperatorAgentPool(snapshot),
|
|
51315
52403
|
onError: (err) => process.stderr.write(
|
|
51316
|
-
`[mrrlin-mcp director-bridge] agent-pool snapshot push failed: ${
|
|
52404
|
+
`[mrrlin-mcp director-bridge] agent-pool snapshot push failed: ${describeError(err)}
|
|
51317
52405
|
`
|
|
51318
52406
|
)
|
|
51319
52407
|
});
|
|
@@ -55164,6 +56252,44 @@ function mapAcceptedIssueToClarification(issue2) {
|
|
|
55164
56252
|
category: issue2.category
|
|
55165
56253
|
};
|
|
55166
56254
|
}
|
|
56255
|
+
var CLARIFICATION_CAP_BY_IMPACT = { low: 2, medium: 3, high: 5 };
|
|
56256
|
+
function resolveClarificationCap(impact) {
|
|
56257
|
+
const tier = impact?.trim().toLowerCase();
|
|
56258
|
+
if (tier === "medium") return CLARIFICATION_CAP_BY_IMPACT.medium;
|
|
56259
|
+
if (tier === "high") return CLARIFICATION_CAP_BY_IMPACT.high;
|
|
56260
|
+
return CLARIFICATION_CAP_BY_IMPACT.low;
|
|
56261
|
+
}
|
|
56262
|
+
function headIndexOf(id) {
|
|
56263
|
+
const match = /^r\d+-h(\d+)-\d+$/.exec(id);
|
|
56264
|
+
return match?.[1] !== void 0 ? Number(match[1]) : null;
|
|
56265
|
+
}
|
|
56266
|
+
function orderClarificationIssues(issues) {
|
|
56267
|
+
const distinctHeadsByKey = /* @__PURE__ */ new Map();
|
|
56268
|
+
for (const issue2 of issues) {
|
|
56269
|
+
const key = `${issue2.category} ${issue2.text}`;
|
|
56270
|
+
const heads = distinctHeadsByKey.get(key) ?? /* @__PURE__ */ new Set();
|
|
56271
|
+
const headIdx = headIndexOf(issue2.id);
|
|
56272
|
+
if (headIdx !== null) heads.add(headIdx);
|
|
56273
|
+
distinctHeadsByKey.set(key, heads);
|
|
56274
|
+
}
|
|
56275
|
+
const categoryRank = (category) => category === "scope" ? 0 : category === "ambiguity" ? 1 : 2;
|
|
56276
|
+
return issues.map((issue2, index) => ({ issue: issue2, index })).sort((a, b) => {
|
|
56277
|
+
const rankDiff = categoryRank(a.issue.category) - categoryRank(b.issue.category);
|
|
56278
|
+
if (rankDiff !== 0) return rankDiff;
|
|
56279
|
+
const headCountA = distinctHeadsByKey.get(`${a.issue.category} ${a.issue.text}`)?.size ?? 0;
|
|
56280
|
+
const headCountB = distinctHeadsByKey.get(`${b.issue.category} ${b.issue.text}`)?.size ?? 0;
|
|
56281
|
+
if (headCountA !== headCountB) return headCountB - headCountA;
|
|
56282
|
+
return a.index - b.index;
|
|
56283
|
+
}).map(({ issue: issue2 }) => issue2);
|
|
56284
|
+
}
|
|
56285
|
+
function applyClarificationCap(issues, impact) {
|
|
56286
|
+
const ordered = orderClarificationIssues(issues);
|
|
56287
|
+
const cap = resolveClarificationCap(impact);
|
|
56288
|
+
return {
|
|
56289
|
+
kept: ordered.slice(0, cap).map(mapAcceptedIssueToClarification),
|
|
56290
|
+
dropped: ordered.slice(cap).map(mapAcceptedIssueToClarification)
|
|
56291
|
+
};
|
|
56292
|
+
}
|
|
55167
56293
|
async function runSpecGate(_target, deps, opts) {
|
|
55168
56294
|
if (!await deps.consensusEnabled()) return { outcome: "SKIPPED" };
|
|
55169
56295
|
const intent = await deps.readIntent();
|
|
@@ -55181,11 +56307,11 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
55181
56307
|
]
|
|
55182
56308
|
};
|
|
55183
56309
|
}
|
|
55184
|
-
const baseHeads = resolveHeads(intent.domain);
|
|
56310
|
+
const baseHeads = resolveHeads(intent.domain, intent.impact);
|
|
55185
56311
|
const extraHeads = opts?.extraHeads ?? [];
|
|
55186
56312
|
const heads = [...baseHeads, ...extraHeads.filter((h) => !baseHeads.includes(h))];
|
|
55187
56313
|
const spec = await deps.readSpec();
|
|
55188
|
-
if (
|
|
56314
|
+
if (parseAcceptanceCriteria(spec.markdownBody) === null) {
|
|
55189
56315
|
return {
|
|
55190
56316
|
outcome: "CONSENSUS_BLOCKED",
|
|
55191
56317
|
operatorClarifications: [
|
|
@@ -55253,23 +56379,32 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
55253
56379
|
]
|
|
55254
56380
|
};
|
|
55255
56381
|
}
|
|
56382
|
+
let keptClarifications = [];
|
|
56383
|
+
let droppedByCap = [];
|
|
56384
|
+
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
56385
|
+
const accepted = result.history.at(-1)?.accepted ?? [];
|
|
56386
|
+
const materialIssues = accepted.filter((issue2) => issue2.category === "ambiguity" || issue2.category === "scope");
|
|
56387
|
+
const capped = applyClarificationCap(materialIssues, intent.impact);
|
|
56388
|
+
keptClarifications = capped.kept;
|
|
56389
|
+
droppedByCap = capped.dropped;
|
|
56390
|
+
}
|
|
55256
56391
|
await deps.writeEvidence({
|
|
55257
56392
|
specPageId: spec.pageId,
|
|
55258
56393
|
outcome: result.outcome,
|
|
55259
56394
|
rounds: result.rounds,
|
|
55260
56395
|
history: result.history,
|
|
55261
|
-
contentHash: currentHash
|
|
56396
|
+
contentHash: currentHash,
|
|
56397
|
+
...droppedByCap.length > 0 ? { droppedByCap } : {}
|
|
55262
56398
|
});
|
|
55263
56399
|
if (result.outcome === "CONVERGED") {
|
|
55264
56400
|
await deps.promoteSpecReady(currentHash);
|
|
55265
56401
|
return { outcome: "CONVERGED" };
|
|
55266
56402
|
}
|
|
55267
56403
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
55268
|
-
const accepted = result.history.at(-1)?.accepted ?? [];
|
|
55269
|
-
const operatorClarifications = accepted.filter((issue2) => issue2.category === "ambiguity" || issue2.category === "scope").map((issue2) => mapAcceptedIssueToClarification(issue2));
|
|
55270
56404
|
return {
|
|
55271
56405
|
outcome: "CONSENSUS_BLOCKED",
|
|
55272
|
-
...
|
|
56406
|
+
...keptClarifications.length > 0 ? { operatorClarifications: keptClarifications } : {},
|
|
56407
|
+
...droppedByCap.length > 0 ? { droppedByCap } : {},
|
|
55273
56408
|
...result.escalation ? { escalation: result.escalation } : {}
|
|
55274
56409
|
};
|
|
55275
56410
|
}
|
|
@@ -55550,7 +56685,7 @@ async function maybeRunShadow(input, deps, logger) {
|
|
|
55550
56685
|
const productionVerdict = productionVerdictFromOutcome(input.outcome);
|
|
55551
56686
|
if (productionVerdict === null) return false;
|
|
55552
56687
|
try {
|
|
55553
|
-
const heads = input.heads ?? [...resolveHeads(input.domain), ...input.extraHeads ?? []];
|
|
56688
|
+
const heads = input.heads ?? [...resolveHeads(input.domain, input.impact), ...input.extraHeads ?? []];
|
|
55554
56689
|
const personaInstructions = {};
|
|
55555
56690
|
for (const name of heads) {
|
|
55556
56691
|
try {
|
|
@@ -55592,7 +56727,7 @@ function multiLlmActive(env = process.env) {
|
|
|
55592
56727
|
async function resolveActiveRunHead(input, deps) {
|
|
55593
56728
|
if (!multiLlmActive(input.env ?? process.env)) return null;
|
|
55594
56729
|
try {
|
|
55595
|
-
const heads = input.heads ?? [...resolveHeads(input.domain), ...input.extraHeads ?? []];
|
|
56730
|
+
const heads = input.heads ?? [...resolveHeads(input.domain, input.impact), ...input.extraHeads ?? []];
|
|
55596
56731
|
if (heads.length === 0) return null;
|
|
55597
56732
|
const personaInstructions = {};
|
|
55598
56733
|
for (const name of heads) {
|
|
@@ -55622,6 +56757,35 @@ async function resolveActiveRunHead(input, deps) {
|
|
|
55622
56757
|
|
|
55623
56758
|
// src/async-tools/consensus.ts
|
|
55624
56759
|
var import_node_crypto9 = require("node:crypto");
|
|
56760
|
+
|
|
56761
|
+
// src/consensus/question-similarity.ts
|
|
56762
|
+
var SUPPRESSION_SIMILARITY_THRESHOLD = 0.6;
|
|
56763
|
+
function tokenSet(text) {
|
|
56764
|
+
const normalized = text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").trim();
|
|
56765
|
+
if (!normalized) return /* @__PURE__ */ new Set();
|
|
56766
|
+
return new Set(normalized.split(/\s+/));
|
|
56767
|
+
}
|
|
56768
|
+
function jaccardSimilarity(a, b) {
|
|
56769
|
+
const setA = tokenSet(a);
|
|
56770
|
+
const setB = tokenSet(b);
|
|
56771
|
+
if (setA.size === 0 || setB.size === 0) return 0;
|
|
56772
|
+
let intersection2 = 0;
|
|
56773
|
+
for (const token of setA) {
|
|
56774
|
+
if (setB.has(token)) intersection2 += 1;
|
|
56775
|
+
}
|
|
56776
|
+
const union2 = setA.size + setB.size - intersection2;
|
|
56777
|
+
return union2 === 0 ? 0 : intersection2 / union2;
|
|
56778
|
+
}
|
|
56779
|
+
function isCoveredByAdjudicated(rawQuestion, adjudicated) {
|
|
56780
|
+
for (const item of adjudicated) {
|
|
56781
|
+
if (jaccardSimilarity(rawQuestion, item.body) >= SUPPRESSION_SIMILARITY_THRESHOLD) {
|
|
56782
|
+
return item;
|
|
56783
|
+
}
|
|
56784
|
+
}
|
|
56785
|
+
return null;
|
|
56786
|
+
}
|
|
56787
|
+
|
|
56788
|
+
// src/async-tools/consensus.ts
|
|
55625
56789
|
function normalizeClarificationQuestion(question) {
|
|
55626
56790
|
return question.replace(/\s+/g, " ").trim().replace(/[?.!]+$/, "").trim();
|
|
55627
56791
|
}
|
|
@@ -55752,35 +56916,43 @@ async function cachedResult(client, input) {
|
|
|
55752
56916
|
}
|
|
55753
56917
|
return null;
|
|
55754
56918
|
}
|
|
56919
|
+
function stripHumanizeOpener(body) {
|
|
56920
|
+
for (const prefix of Object.values(HUMANIZE_TEMPLATE_BY_KIND)) {
|
|
56921
|
+
if (body.startsWith(prefix)) return body.slice(prefix.length).trim();
|
|
56922
|
+
}
|
|
56923
|
+
return body;
|
|
56924
|
+
}
|
|
55755
56925
|
async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
55756
56926
|
const operatorClarifications = result.operatorClarifications ?? [];
|
|
55757
56927
|
const diagnostics = result.diagnostics ?? [];
|
|
55758
|
-
|
|
56928
|
+
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
56929
|
+
const taskId = page?.taskId ?? null;
|
|
56930
|
+
if (!resolvedTaskId && taskId) resolvedTaskId = taskId;
|
|
56931
|
+
const adjudicated = [];
|
|
56932
|
+
if (taskId) {
|
|
55759
56933
|
try {
|
|
55760
|
-
const
|
|
55761
|
-
|
|
55762
|
-
|
|
55763
|
-
|
|
55764
|
-
|
|
55765
|
-
});
|
|
55766
|
-
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
55767
|
-
contentHash: domainScopedReviewHash(page.contentHash, domain2),
|
|
55768
|
-
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
55769
|
-
rounds: 0
|
|
55770
|
-
});
|
|
56934
|
+
const taskInboxItems = await ctx.client.listInboxItems(input.projectSlug, { taskId });
|
|
56935
|
+
for (const item of taskInboxItems) {
|
|
56936
|
+
if (item.origin === "gate" && (item.status === "acknowledged" || item.status === "rejected")) {
|
|
56937
|
+
adjudicated.push({ body: stripHumanizeOpener(item.body), answer: item.answer, status: item.status });
|
|
56938
|
+
}
|
|
55771
56939
|
}
|
|
55772
56940
|
} catch (error52) {
|
|
55773
56941
|
process.stderr.write(
|
|
55774
|
-
`[mrrlin-mcp consensus]
|
|
56942
|
+
`[mrrlin-mcp consensus] adjudicated-questions read failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
55775
56943
|
`
|
|
55776
56944
|
);
|
|
55777
56945
|
}
|
|
55778
56946
|
}
|
|
56947
|
+
const suppressedAsAdjudicated = [];
|
|
56948
|
+
const toFile = operatorClarifications.filter((clarification) => {
|
|
56949
|
+
const covering = isCoveredByAdjudicated(clarification.rawQuestion, adjudicated);
|
|
56950
|
+
if (!covering) return true;
|
|
56951
|
+
suppressedAsAdjudicated.push({ rawQuestion: clarification.rawQuestion, coveredByAnswer: covering.answer });
|
|
56952
|
+
return false;
|
|
56953
|
+
});
|
|
55779
56954
|
let filedQuestionCount = 0;
|
|
55780
|
-
if (
|
|
55781
|
-
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
55782
|
-
const taskId = page?.taskId ?? null;
|
|
55783
|
-
if (!resolvedTaskId && taskId) resolvedTaskId = taskId;
|
|
56955
|
+
if (toFile.length > 0) {
|
|
55784
56956
|
let planId = null;
|
|
55785
56957
|
if (!taskId) {
|
|
55786
56958
|
const plans = await ctx.client.listPlans(input.projectSlug);
|
|
@@ -55788,8 +56960,8 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55788
56960
|
}
|
|
55789
56961
|
if (taskId) {
|
|
55790
56962
|
const scopeId = taskId;
|
|
55791
|
-
const groupId =
|
|
55792
|
-
for (const clarification of
|
|
56963
|
+
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;
|
|
56964
|
+
for (const clarification of toFile) {
|
|
55793
56965
|
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
55794
56966
|
try {
|
|
55795
56967
|
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
@@ -55805,11 +56977,11 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55805
56977
|
} catch {
|
|
55806
56978
|
}
|
|
55807
56979
|
}
|
|
55808
|
-
filedQuestionCount =
|
|
56980
|
+
filedQuestionCount = toFile.length;
|
|
55809
56981
|
} else if (planId) {
|
|
55810
56982
|
const scopeId = planId;
|
|
55811
|
-
const groupId =
|
|
55812
|
-
for (const clarification of
|
|
56983
|
+
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;
|
|
56984
|
+
for (const clarification of toFile) {
|
|
55813
56985
|
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
55814
56986
|
try {
|
|
55815
56987
|
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
@@ -55825,7 +56997,32 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55825
56997
|
} catch {
|
|
55826
56998
|
}
|
|
55827
56999
|
}
|
|
55828
|
-
filedQuestionCount =
|
|
57000
|
+
filedQuestionCount = toFile.length;
|
|
57001
|
+
}
|
|
57002
|
+
}
|
|
57003
|
+
if (operatorClarifications.length > 0) {
|
|
57004
|
+
try {
|
|
57005
|
+
if (page) {
|
|
57006
|
+
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
57007
|
+
id: page.id,
|
|
57008
|
+
taskId: page.taskId ?? null
|
|
57009
|
+
});
|
|
57010
|
+
const suppressionEvidence = (result.droppedByCap?.length ?? 0) > 0 || suppressedAsAdjudicated.length > 0 ? {
|
|
57011
|
+
...result.droppedByCap?.length ? { droppedByCap: result.droppedByCap } : {},
|
|
57012
|
+
...suppressedAsAdjudicated.length ? { suppressedAsAdjudicated } : {}
|
|
57013
|
+
} : void 0;
|
|
57014
|
+
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
57015
|
+
contentHash: domainScopedReviewHash(page.contentHash, domain2),
|
|
57016
|
+
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
57017
|
+
rounds: 0,
|
|
57018
|
+
...suppressionEvidence ? { suppressionEvidence } : {}
|
|
57019
|
+
});
|
|
57020
|
+
}
|
|
57021
|
+
} catch (error52) {
|
|
57022
|
+
process.stderr.write(
|
|
57023
|
+
`[mrrlin-mcp consensus] clarification verdict write failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
57024
|
+
`
|
|
57025
|
+
);
|
|
55829
57026
|
}
|
|
55830
57027
|
}
|
|
55831
57028
|
let error51;
|
|
@@ -55833,6 +57030,8 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
55833
57030
|
error51 = `Consensus blocked on ${filedQuestionCount} clarification(s); filed as operator question(s).`;
|
|
55834
57031
|
} else if (diagnostics.length > 0 && operatorClarifications.length === 0) {
|
|
55835
57032
|
error51 = "Consensus blocked: the spec content does not match its linked task. This requires fixing the spec/task relationship directly, not an inbox answer.";
|
|
57033
|
+
} else if (operatorClarifications.length > 0 && toFile.length === 0 && suppressedAsAdjudicated.length > 0) {
|
|
57034
|
+
error51 = `Consensus blocked, but all ${suppressedAsAdjudicated.length} clarification(s) were reworded repeats of already-adjudicated questions \u2014 nothing new filed.`;
|
|
55836
57035
|
} else if (operatorClarifications.length > 0) {
|
|
55837
57036
|
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.`;
|
|
55838
57037
|
} else {
|
|
@@ -56138,30 +57337,12 @@ var mcpToolNames = {
|
|
|
56138
57337
|
restartExecutionRun: "restart_execution_run",
|
|
56139
57338
|
resolveHandoff: "resolve_handoff",
|
|
56140
57339
|
recordProducedUrl: "record_produced_url",
|
|
57340
|
+
updateTaskState: "update_task_state",
|
|
56141
57341
|
registerLocalCheckout: "register_local_checkout",
|
|
56142
57342
|
/** Compact-context retrieval: rehydrate a mrrlin:// handle into the canonical full record. */
|
|
56143
57343
|
retrieveContext: "retrieve_context"
|
|
56144
57344
|
};
|
|
56145
57345
|
|
|
56146
|
-
// src/context-compact/policy.ts
|
|
56147
|
-
var DEFAULT_THRESHOLD_CHARS = 12e3;
|
|
56148
|
-
var DEFAULT_MAX_ITEMS = 25;
|
|
56149
|
-
function readPositiveInt(value, fallback) {
|
|
56150
|
-
const raw = (value ?? "").trim();
|
|
56151
|
-
if (!raw) return fallback;
|
|
56152
|
-
const parsed = Number.parseInt(raw, 10);
|
|
56153
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
56154
|
-
}
|
|
56155
|
-
function readCompactPolicy(env = process.env) {
|
|
56156
|
-
const disabled = (env.MRRLIN_MCP_COMPACT_DISABLE ?? "").trim() === "1";
|
|
56157
|
-
return {
|
|
56158
|
-
enabled: !disabled,
|
|
56159
|
-
thresholdChars: readPositiveInt(env.MRRLIN_MCP_COMPACT_THRESHOLD_CHARS, DEFAULT_THRESHOLD_CHARS),
|
|
56160
|
-
maxItems: readPositiveInt(env.MRRLIN_MCP_COMPACT_MAX_ITEMS, DEFAULT_MAX_ITEMS),
|
|
56161
|
-
debug: (env.MRRLIN_MCP_COMPACT_DEBUG ?? "").trim() === "1"
|
|
56162
|
-
};
|
|
56163
|
-
}
|
|
56164
|
-
|
|
56165
57346
|
// src/context-compact/handles.ts
|
|
56166
57347
|
var MRRLIN_HANDLE_SCHEME = "mrrlin://";
|
|
56167
57348
|
var SLUG_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
@@ -56236,7 +57417,7 @@ function parseMrrlinHandle(value) {
|
|
|
56236
57417
|
var NOTES_EXCERPT_CHARS = 200;
|
|
56237
57418
|
var TITLE_TRUNCATE_CHARS = 160;
|
|
56238
57419
|
var EXCERPT_TRUNCATE_CHARS = 240;
|
|
56239
|
-
function
|
|
57420
|
+
function truncate2(value, maxChars) {
|
|
56240
57421
|
if (value.length <= maxChars) return value;
|
|
56241
57422
|
return `${value.slice(0, Math.max(0, maxChars - 1))}\u2026`;
|
|
56242
57423
|
}
|
|
@@ -56252,7 +57433,7 @@ function compactTaskList(projectSlug, tasks, maxItems) {
|
|
|
56252
57433
|
const slice = tasks.slice(0, included);
|
|
56253
57434
|
const items = slice.map((t) => ({
|
|
56254
57435
|
id: t.id,
|
|
56255
|
-
title:
|
|
57436
|
+
title: truncate2(t.title, TITLE_TRUNCATE_CHARS),
|
|
56256
57437
|
status: t.status,
|
|
56257
57438
|
type: t.type,
|
|
56258
57439
|
category: t.category,
|
|
@@ -56263,8 +57444,8 @@ function compactTaskList(projectSlug, tasks, maxItems) {
|
|
|
56263
57444
|
assignee: t.assignee,
|
|
56264
57445
|
planId: t.planId,
|
|
56265
57446
|
specWikiPageId: t.specWikiPageId,
|
|
56266
|
-
notesExcerpt: t.notes ?
|
|
56267
|
-
latestJudgementExcerpt: t.latestJudgement ?
|
|
57447
|
+
notesExcerpt: t.notes ? truncate2(t.notes, NOTES_EXCERPT_CHARS) : null,
|
|
57448
|
+
latestJudgementExcerpt: t.latestJudgement ? truncate2(t.latestJudgement, NOTES_EXCERPT_CHARS) : null,
|
|
56268
57449
|
handle: formatTaskHandle(projectSlug, t.id)
|
|
56269
57450
|
}));
|
|
56270
57451
|
const handles = slice.map((t) => ({
|
|
@@ -56285,9 +57466,10 @@ function compactWikiPageList(projectSlug, pages, maxItems) {
|
|
|
56285
57466
|
const slice = pages.slice(0, included);
|
|
56286
57467
|
const items = slice.map((p) => ({
|
|
56287
57468
|
id: p.id,
|
|
56288
|
-
title:
|
|
57469
|
+
title: truncate2(p.title, TITLE_TRUNCATE_CHARS),
|
|
56289
57470
|
folder: p.folder,
|
|
56290
57471
|
path: p.path,
|
|
57472
|
+
updatedAt: p.updatedAt,
|
|
56291
57473
|
taskId: p.taskId,
|
|
56292
57474
|
specReady: p.specReady,
|
|
56293
57475
|
firstHeading: firstHeadingLine(p.markdownBody),
|
|
@@ -56311,11 +57493,15 @@ function compactWikiSearch(projectSlug, results, maxItems) {
|
|
|
56311
57493
|
const slice = results.slice(0, included);
|
|
56312
57494
|
const items = slice.map((r) => ({
|
|
56313
57495
|
pageId: r.id,
|
|
56314
|
-
title:
|
|
57496
|
+
title: truncate2(r.title, TITLE_TRUNCATE_CHARS),
|
|
56315
57497
|
folder: r.folder,
|
|
56316
57498
|
path: r.path,
|
|
56317
57499
|
rank: r.rank,
|
|
56318
|
-
|
|
57500
|
+
updatedAt: r.updatedAt,
|
|
57501
|
+
ageDays: r.ageDays,
|
|
57502
|
+
memoryClass: r.memoryClass,
|
|
57503
|
+
precedence: r.precedence,
|
|
57504
|
+
snippet: truncate2(r.snippet, EXCERPT_TRUNCATE_CHARS),
|
|
56319
57505
|
handle: formatWikiHandle(projectSlug, r.id)
|
|
56320
57506
|
}));
|
|
56321
57507
|
const handles = slice.map((r) => ({
|
|
@@ -56344,7 +57530,7 @@ function compactProjectSnapshot(projectSlug, snapshot, maxItems) {
|
|
|
56344
57530
|
}
|
|
56345
57531
|
const compactPlans = snapshot.plans.map((p) => ({
|
|
56346
57532
|
id: p.id,
|
|
56347
|
-
name:
|
|
57533
|
+
name: truncate2(p.name, TITLE_TRUNCATE_CHARS),
|
|
56348
57534
|
status: p.status,
|
|
56349
57535
|
taskCount: p.taskCount,
|
|
56350
57536
|
closedTaskCount: p.closedTaskCount,
|
|
@@ -56416,7 +57602,7 @@ function compactArtifactPayload(artifact) {
|
|
|
56416
57602
|
cwd,
|
|
56417
57603
|
exitCode,
|
|
56418
57604
|
firstError,
|
|
56419
|
-
tail:
|
|
57605
|
+
tail: truncate2(tail2, ARTIFACT_PAYLOAD_BUDGET),
|
|
56420
57606
|
totalLines: lines2.length
|
|
56421
57607
|
};
|
|
56422
57608
|
}
|
|
@@ -56424,7 +57610,7 @@ function compactArtifactPayload(artifact) {
|
|
|
56424
57610
|
return {
|
|
56425
57611
|
status: payload.status ?? null,
|
|
56426
57612
|
command: payload.command ?? null,
|
|
56427
|
-
summary: typeof payload.summary === "string" ?
|
|
57613
|
+
summary: typeof payload.summary === "string" ? truncate2(payload.summary, ARTIFACT_PAYLOAD_BUDGET) : null,
|
|
56428
57614
|
failed: Array.isArray(payload.failedTests) ? payload.failedTests.slice(0, 25) : null
|
|
56429
57615
|
};
|
|
56430
57616
|
}
|
|
@@ -56442,7 +57628,7 @@ function compactArtifactPayload(artifact) {
|
|
|
56442
57628
|
case "checkpoint":
|
|
56443
57629
|
default: {
|
|
56444
57630
|
const flattened = JSON.stringify(payload);
|
|
56445
|
-
return { excerpt:
|
|
57631
|
+
return { excerpt: truncate2(flattened, ARTIFACT_PAYLOAD_BUDGET) };
|
|
56446
57632
|
}
|
|
56447
57633
|
}
|
|
56448
57634
|
}
|
|
@@ -56481,7 +57667,7 @@ function compactTaskEvents(projectSlug, taskId, events, maxItems) {
|
|
|
56481
57667
|
createdAt: e.createdAt,
|
|
56482
57668
|
autonomyLevel: e.autonomyLevel,
|
|
56483
57669
|
autonomyReason: e.autonomyReason,
|
|
56484
|
-
bodyExcerpt: e.body ?
|
|
57670
|
+
bodyExcerpt: e.body ? truncate2(e.body, NOTES_EXCERPT_CHARS) : null
|
|
56485
57671
|
}));
|
|
56486
57672
|
const handles = [
|
|
56487
57673
|
{
|
|
@@ -56503,7 +57689,7 @@ function compactGeneric(value, maxItems, projectSlug) {
|
|
|
56503
57689
|
const included = Math.min(total, maxItems);
|
|
56504
57690
|
const slice = value.slice(0, included).map((entry) => {
|
|
56505
57691
|
const text2 = typeof entry === "string" ? entry : JSON.stringify(entry);
|
|
56506
|
-
return
|
|
57692
|
+
return truncate2(text2, ARTIFACT_PAYLOAD_BUDGET);
|
|
56507
57693
|
});
|
|
56508
57694
|
return {
|
|
56509
57695
|
summary: `Compact array view: ${total} entries, ${included} included.`,
|
|
@@ -56534,7 +57720,7 @@ function compactGeneric(value, maxItems, projectSlug) {
|
|
|
56534
57720
|
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
56535
57721
|
return {
|
|
56536
57722
|
summary: "Compact scalar view.",
|
|
56537
|
-
items: [
|
|
57723
|
+
items: [truncate2(text, ARTIFACT_PAYLOAD_BUDGET)],
|
|
56538
57724
|
handles: [],
|
|
56539
57725
|
counts: { total: 1, included: 1, omitted: 0 }
|
|
56540
57726
|
};
|
|
@@ -56704,6 +57890,7 @@ var updateExecutionRunInputSchema = runScopedSchema.merge(mrrlinExecutionRunUpda
|
|
|
56704
57890
|
var listExecutionArtifactsInputSchema = runScopedSchema;
|
|
56705
57891
|
var appendExecutionArtifactInputSchema = runScopedSchema.merge(mrrlinExecutionArtifactCreateSchema);
|
|
56706
57892
|
var recordProducedUrlInputSchema = runScopedSchema.merge(mrrlinProducedUrlPayloadSchema);
|
|
57893
|
+
var updateTaskStateInputSchema = runScopedSchema.merge(mrrlinTaskStateReportSchema);
|
|
56707
57894
|
var getProjectSchedulerPolicyInputSchema = projectScopedSchema;
|
|
56708
57895
|
var upsertProjectSchedulerPolicyInputSchema = projectScopedSchema.merge(mrrlinProjectSchedulerPolicyUpsertSchema);
|
|
56709
57896
|
var recordDeployWorkflowInputSchema = projectScopedSchema.extend({
|
|
@@ -56816,6 +58003,7 @@ var mcpToolInputSchemas = {
|
|
|
56816
58003
|
[mcpToolNames.restartExecutionRun]: restartExecutionRunInputSchema,
|
|
56817
58004
|
[mcpToolNames.resolveHandoff]: resolveHandoffInputSchema,
|
|
56818
58005
|
[mcpToolNames.recordProducedUrl]: recordProducedUrlInputSchema,
|
|
58006
|
+
[mcpToolNames.updateTaskState]: updateTaskStateInputSchema,
|
|
56819
58007
|
[mcpToolNames.registerLocalCheckout]: registerLocalCheckoutInputSchema,
|
|
56820
58008
|
[mcpToolNames.retrieveContext]: retrieveContextInputSchema
|
|
56821
58009
|
};
|
|
@@ -56867,6 +58055,7 @@ var mcpToolOperationIds = {
|
|
|
56867
58055
|
[mcpToolNames.recordDeployWorkflow]: openApiOperationIds.upsertProjectDeployWorkflow,
|
|
56868
58056
|
[mcpToolNames.updatePlan]: openApiOperationIds.updatePlan,
|
|
56869
58057
|
[mcpToolNames.updateTask]: openApiOperationIds.updateTask,
|
|
58058
|
+
[mcpToolNames.updateTaskState]: openApiOperationIds.reportTaskState,
|
|
56870
58059
|
[mcpToolNames.resetDirectorThread]: openApiOperationIds.resetDirectorThread,
|
|
56871
58060
|
[mcpToolNames.archiveTask]: openApiOperationIds.archiveTask,
|
|
56872
58061
|
[mcpToolNames.unarchiveTask]: openApiOperationIds.unarchiveTask,
|
|
@@ -56897,7 +58086,7 @@ var mcpToolDescriptions = {
|
|
|
56897
58086
|
[mcpToolNames.appendTaskEvent]: "Append an operator note to a task's activity timeline.",
|
|
56898
58087
|
[mcpToolNames.createWikiPage]: "Create a markdown Wiki page.",
|
|
56899
58088
|
[mcpToolNames.deleteTaskDependency]: "Delete a blocked-by relationship.",
|
|
56900
|
-
[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.",
|
|
58089
|
+
[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.",
|
|
56901
58090
|
[mcpToolNames.getProject]: "Fetch a single project summary by slug.",
|
|
56902
58091
|
[mcpToolNames.getProjectSnapshot]: "Fetch the full project snapshot: GitHub binding, tasks, plans, and wiki pages.",
|
|
56903
58092
|
[mcpToolNames.getTask]: "Fetch a single task by project slug and task id.",
|
|
@@ -56927,6 +58116,7 @@ var mcpToolDescriptions = {
|
|
|
56927
58116
|
[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).`,
|
|
56928
58117
|
[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.",
|
|
56929
58118
|
[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.',
|
|
58119
|
+
[mcpToolNames.updateTaskState]: "Record where this task actually stands, in your own words, BEFORE you end your turn. This is the handoff the next run reads: it survives even when this conversation does not, because a thread is retired once it has accumulated too many runs and the next run then starts from this record plus the repository. Report what only you know \u2014 the goal as you now understand it, decisions you made AND WHY (a decision without its reason is the thing most often lost), problems still open, and the next concrete actions. Omit any field you learned nothing new about; an omitted field keeps its previous value rather than being cleared. Use one entry per line for decisions/next actions. Do not restate the spec or the file list \u2014 the next run gets those anyway.",
|
|
56930
58120
|
[mcpToolNames.createExecutionRun]: 'Create a durable Director execution run for a task. Persists status, branch/SHA, workflow ids, and checkpoint cursor outside the chat transcript. For tasks that require a browser (login flows, UI verification, scraping), set prerequisites:[{mcp:"mrrlin-browser",reason:"<why a browser is needed>"}] so any resulting handoff offers a one-click "Launch browser & resume". When actually driving the browser inside the run, invoke tools from the `mrrlin-browser` MCP server ONLY (browser_navigate/browser_click/browser_snapshot/browser_type/etc.) \u2014 never from `playwright`, `chrome-devtools`, `puppeteer`, or any other browser MCP that may also be connected on the operator\'s box. Only `mrrlin-browser` uses the dedicated Chromium + persistent operator profile.',
|
|
56931
58121
|
[mcpToolNames.getExecutionRun]: "Fetch a single execution run by id.",
|
|
56932
58122
|
[mcpToolNames.listExecutionArtifacts]: "List artifacts captured during an execution run, in chronological order.",
|
|
@@ -57524,6 +58714,13 @@ function createMrrlinTools(options) {
|
|
|
57524
58714
|
"Unable to append execution artifact.",
|
|
57525
58715
|
async (c, { projectSlug, runId, ...input }) => await c.appendExecutionArtifact(projectSlug, runId, input)
|
|
57526
58716
|
),
|
|
58717
|
+
[mcpToolNames.updateTaskState]: makeTool(
|
|
58718
|
+
client,
|
|
58719
|
+
mcpToolNames.updateTaskState,
|
|
58720
|
+
"TASK_STATE_REPORT_FAILED",
|
|
58721
|
+
"Unable to record task state.",
|
|
58722
|
+
async (c, { projectSlug, runId, ...report }) => await c.reportTaskState(projectSlug, runId, report)
|
|
58723
|
+
),
|
|
57527
58724
|
[mcpToolNames.recordProducedUrl]: makeTool(
|
|
57528
58725
|
client,
|
|
57529
58726
|
mcpToolNames.recordProducedUrl,
|
|
@@ -58185,11 +59382,13 @@ Usage:
|
|
|
58185
59382
|
- MRRLIN_WORKER_PORT (dispatch listener, default 8792)
|
|
58186
59383
|
- MRRLIN_DIRECTOR_BRIDGE_CLAIM_INTERVAL_SECONDS (default 30)
|
|
58187
59384
|
- MRRLIN_DIRECTOR_BRIDGE_STALE_SECONDS (default 300)
|
|
58188
|
-
- MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_IDLE_SECONDS (default
|
|
59385
|
+
- MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_IDLE_SECONDS (default 300;
|
|
58189
59386
|
a chat turn is cut only after this long with NO codex
|
|
58190
|
-
signal at all \u2014 long-but-active
|
|
58191
|
-
|
|
58192
|
-
|
|
59387
|
+
signal at all AND no tool call in flight \u2014 long-but-active
|
|
59388
|
+
turns, and long single tool calls, keep running)
|
|
59389
|
+
- MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_MAX_SECONDS (default 2700;
|
|
59390
|
+
absolute ceiling for one chat turn regardless of activity;
|
|
59391
|
+
the only guard against a genuinely hung tool call)
|
|
58193
59392
|
- MRRLIN_DIRECTOR_BRIDGE_CODEX_IDLE_SECONDS (default 300;
|
|
58194
59393
|
how long the warm codex app-server is kept alive
|
|
58195
59394
|
between turns before it is released)
|