@mrrlin-dev/mcp 0.4.21 → 0.4.23
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 +383 -33
- package/package.json +4 -4
package/dist/bin.cjs
CHANGED
|
@@ -20815,6 +20815,66 @@ function browserServeArgs(homeDir = import_node_os.default.homedir()) {
|
|
|
20815
20815
|
env: { PLAYWRIGHT_BROWSERS_PATH: browsersPath }
|
|
20816
20816
|
};
|
|
20817
20817
|
}
|
|
20818
|
+
function defaultListProcesses() {
|
|
20819
|
+
const r = (0, import_node_child_process.spawnSync)("ps", ["-ww", "-eo", "pid,ppid,command"], { encoding: "utf8" });
|
|
20820
|
+
const lines2 = (r.stdout ?? "").split("\n").slice(1);
|
|
20821
|
+
const out = [];
|
|
20822
|
+
for (const line of lines2) {
|
|
20823
|
+
const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
|
|
20824
|
+
if (!m || m[1] === void 0 || m[2] === void 0 || m[3] === void 0) continue;
|
|
20825
|
+
out.push({ pid: Number(m[1]), ppid: Number(m[2]), command: m[3] });
|
|
20826
|
+
}
|
|
20827
|
+
return out;
|
|
20828
|
+
}
|
|
20829
|
+
function defaultIsAlive(pid) {
|
|
20830
|
+
try {
|
|
20831
|
+
process.kill(pid, 0);
|
|
20832
|
+
return true;
|
|
20833
|
+
} catch (err) {
|
|
20834
|
+
return err.code === "EPERM";
|
|
20835
|
+
}
|
|
20836
|
+
}
|
|
20837
|
+
function defaultKill(pid, signal) {
|
|
20838
|
+
try {
|
|
20839
|
+
process.kill(pid, signal);
|
|
20840
|
+
return true;
|
|
20841
|
+
} catch (err) {
|
|
20842
|
+
if (err.code === "ESRCH") return true;
|
|
20843
|
+
return false;
|
|
20844
|
+
}
|
|
20845
|
+
}
|
|
20846
|
+
function safeKill(kill, pid, signal) {
|
|
20847
|
+
try {
|
|
20848
|
+
return kill(pid, signal);
|
|
20849
|
+
} catch {
|
|
20850
|
+
return false;
|
|
20851
|
+
}
|
|
20852
|
+
}
|
|
20853
|
+
function reapOrphanedBrowserProcesses(opts = {}) {
|
|
20854
|
+
const homeDir = opts.homeDir ?? import_node_os.default.homedir();
|
|
20855
|
+
const { profilePath } = browserPaths(homeDir);
|
|
20856
|
+
const listProcesses = opts.listProcesses ?? defaultListProcesses;
|
|
20857
|
+
const isAlive = opts.isAlive ?? defaultIsAlive;
|
|
20858
|
+
const kill = opts.kill ?? defaultKill;
|
|
20859
|
+
const selfPid = opts.selfPid ?? process.pid;
|
|
20860
|
+
const allProcesses = listProcesses();
|
|
20861
|
+
const candidates = allProcesses.filter(
|
|
20862
|
+
(p) => p.pid !== selfPid && p.command.includes("@playwright/mcp") && p.command.includes(profilePath)
|
|
20863
|
+
);
|
|
20864
|
+
const killedPids = [];
|
|
20865
|
+
const killedChildPids = [];
|
|
20866
|
+
for (const proc of candidates) {
|
|
20867
|
+
if (!isAlive(proc.ppid)) {
|
|
20868
|
+
for (const child of allProcesses) {
|
|
20869
|
+
if (child.ppid === proc.pid) {
|
|
20870
|
+
if (safeKill(kill, child.pid, "SIGKILL")) killedChildPids.push(child.pid);
|
|
20871
|
+
}
|
|
20872
|
+
}
|
|
20873
|
+
if (safeKill(kill, proc.pid, "SIGTERM")) killedPids.push(proc.pid);
|
|
20874
|
+
}
|
|
20875
|
+
}
|
|
20876
|
+
return { checked: candidates.length, killedPids, killedChildPids };
|
|
20877
|
+
}
|
|
20818
20878
|
function externalAgentsServeArgs() {
|
|
20819
20879
|
return {
|
|
20820
20880
|
command: process.execPath,
|
|
@@ -36811,6 +36871,7 @@ var openApiOperationIds = {
|
|
|
36811
36871
|
retireThread: "retireThread",
|
|
36812
36872
|
applyIntentVerdict: "applyIntentVerdict",
|
|
36813
36873
|
armIntentCheck: "armIntentCheck",
|
|
36874
|
+
settleExecutionRunOutcome: "settleExecutionRunOutcome",
|
|
36814
36875
|
// Run-scoped, model-facing half of the resume contract (D8). Distinct from upsertTaskState:
|
|
36815
36876
|
// that one is the server's own write (whole record, explicit lastRunSeq), this one is the
|
|
36816
36877
|
// executor reporting narrative only, with the ordering value derived server-side from the run.
|
|
@@ -37434,8 +37495,17 @@ var mrrlinExecutionArtifactKindSchema = external_exports.enum([
|
|
|
37434
37495
|
"produced_url",
|
|
37435
37496
|
"resume_spec",
|
|
37436
37497
|
"deliverable",
|
|
37437
|
-
"pull_request_state"
|
|
37498
|
+
"pull_request_state",
|
|
37499
|
+
"non_code_outcome"
|
|
37438
37500
|
]);
|
|
37501
|
+
var mrrlinNonCodeOutcomeSchema = external_exports.enum(["completed", "blocked_missing_input", "negative_result"]);
|
|
37502
|
+
var mrrlinIntentProvenanceRefSchema = external_exports.object({
|
|
37503
|
+
kind: external_exports.enum(["director_chat_message", "wiki_page"]),
|
|
37504
|
+
id: external_exports.string().min(1),
|
|
37505
|
+
role: external_exports.enum(["user", "assistant", "system"]).optional(),
|
|
37506
|
+
contentHash: external_exports.string().min(1).optional()
|
|
37507
|
+
});
|
|
37508
|
+
var mrrlinIntentProvenanceSchema = external_exports.array(mrrlinIntentProvenanceRefSchema).default([]);
|
|
37439
37509
|
var mrrlinDeployAttemptPayloadSchema = external_exports.object({
|
|
37440
37510
|
env: external_exports.enum(["dev", "prod"]),
|
|
37441
37511
|
workflowId: external_exports.number().int(),
|
|
@@ -37541,6 +37611,8 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
37541
37611
|
// Output-optional (not .default) so existing task fixtures need no churn; DB rows always resolve it
|
|
37542
37612
|
// to `string | null` via taskRowToTask.
|
|
37543
37613
|
operatorSeedRequest: external_exports.string().max(8e3).nullable().optional(),
|
|
37614
|
+
resolvedIntent: external_exports.string().min(1).max(4e3).nullable().optional(),
|
|
37615
|
+
intentProvenance: mrrlinIntentProvenanceSchema.nullable().optional(),
|
|
37544
37616
|
planId: mrrlinPlanIdSchema.nullable(),
|
|
37545
37617
|
specApproved: mrrlinSpecApprovedSchema.optional(),
|
|
37546
37618
|
specWikiPageId: mrrlinWikiPageIdSchema.nullable(),
|
|
@@ -37566,6 +37638,8 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
37566
37638
|
var mrrlinTaskSummarySchema = mrrlinTaskSchema.omit({
|
|
37567
37639
|
notes: true,
|
|
37568
37640
|
operatorSeedRequest: true,
|
|
37641
|
+
resolvedIntent: true,
|
|
37642
|
+
intentProvenance: true,
|
|
37569
37643
|
intentRestatement: true,
|
|
37570
37644
|
latestJudgement: true
|
|
37571
37645
|
});
|
|
@@ -37776,6 +37850,8 @@ var mrrlinTaskCreateShapeSchema = external_exports.object({
|
|
|
37776
37850
|
notes: external_exports.string().nullable().optional(),
|
|
37777
37851
|
parentTaskId: mrrlinTaskIdSchema.nullable().optional(),
|
|
37778
37852
|
operatorSeedRequest: external_exports.string().max(8e3).nullable().optional(),
|
|
37853
|
+
resolvedIntent: external_exports.string().min(1).max(4e3).nullable().optional(),
|
|
37854
|
+
intentProvenance: mrrlinIntentProvenanceSchema.nullable().optional(),
|
|
37779
37855
|
planId: mrrlinPlanIdSchema.nullable().optional(),
|
|
37780
37856
|
specWikiPageId: mrrlinWikiPageIdSchema.nullable().optional(),
|
|
37781
37857
|
status: mrrlinTaskCreateStatusSchema.optional(),
|
|
@@ -38206,7 +38282,11 @@ var mrrlinExecutionRunSchema = external_exports.object({
|
|
|
38206
38282
|
cachedInputTokens: external_exports.number().int().nullable(),
|
|
38207
38283
|
outputTokens: external_exports.number().int().nullable(),
|
|
38208
38284
|
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable(),
|
|
38209
|
-
effortEscalationReason: external_exports.string().min(1).nullable()
|
|
38285
|
+
effortEscalationReason: external_exports.string().min(1).nullable(),
|
|
38286
|
+
nonCodeOutcome: mrrlinNonCodeOutcomeSchema.nullable().default(null),
|
|
38287
|
+
resultSummary: external_exports.string().min(1).max(300).nullable().default(null),
|
|
38288
|
+
deliverablePageId: mrrlinWikiPageIdSchema.nullable().default(null),
|
|
38289
|
+
outcomeAcceptedAt: external_exports.string().datetime().nullable().default(null)
|
|
38210
38290
|
});
|
|
38211
38291
|
var mrrlinTaskStateSchema = external_exports.object({
|
|
38212
38292
|
projectSlug: mrrlinProjectSlugSchema,
|
|
@@ -38348,6 +38428,13 @@ var mrrlinRestartExecutionRunSchema = external_exports.object({
|
|
|
38348
38428
|
var mrrlinExecutionRunTouchSchema = external_exports.object({
|
|
38349
38429
|
leaseId: external_exports.string().min(1)
|
|
38350
38430
|
});
|
|
38431
|
+
var mrrlinSettleExecutionRunOutcomeSchema = external_exports.object({
|
|
38432
|
+
outcome: mrrlinNonCodeOutcomeSchema,
|
|
38433
|
+
resultSummary: external_exports.string().trim().min(1).max(300),
|
|
38434
|
+
deliverablePageId: mrrlinWikiPageIdSchema,
|
|
38435
|
+
contentHash: external_exports.string().min(1),
|
|
38436
|
+
leaseId: external_exports.string().min(1)
|
|
38437
|
+
});
|
|
38351
38438
|
var mrrlinExecutionArtifactSchema = external_exports.object({
|
|
38352
38439
|
createdAt: external_exports.string().datetime(),
|
|
38353
38440
|
id: mrrlinExecutionArtifactIdSchema,
|
|
@@ -40845,6 +40932,20 @@ function createMrrlinClient(config2) {
|
|
|
40845
40932
|
throw error51;
|
|
40846
40933
|
}
|
|
40847
40934
|
},
|
|
40935
|
+
async settleExecutionRunOutcome(projectSlug, runId, input) {
|
|
40936
|
+
try {
|
|
40937
|
+
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/settle-outcome`, {
|
|
40938
|
+
body: JSON.stringify(mrrlinSettleExecutionRunOutcomeSchema.parse(input)),
|
|
40939
|
+
headers: { "content-type": "application/json" },
|
|
40940
|
+
method: "POST"
|
|
40941
|
+
});
|
|
40942
|
+
return executionRunResponseSchema.parse(body).data;
|
|
40943
|
+
} catch (error51) {
|
|
40944
|
+
if (error51 instanceof MrrlinApiError && error51.status === 409)
|
|
40945
|
+
return null;
|
|
40946
|
+
throw error51;
|
|
40947
|
+
}
|
|
40948
|
+
},
|
|
40848
40949
|
async getTaskState(projectSlug, taskId) {
|
|
40849
40950
|
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/state`);
|
|
40850
40951
|
return taskStateResponseSchema.parse(body).data;
|
|
@@ -41623,7 +41724,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41623
41724
|
}
|
|
41624
41725
|
|
|
41625
41726
|
// src/_generated/version.ts
|
|
41626
|
-
var PKG_VERSION = "0.4.
|
|
41727
|
+
var PKG_VERSION = "0.4.23";
|
|
41627
41728
|
|
|
41628
41729
|
// src/api-base-url-migrate.ts
|
|
41629
41730
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -43870,7 +43971,7 @@ function renderCriteriaBlock(criteria) {
|
|
|
43870
43971
|
return criteria.map((c, i) => `${i + 1}. [ ] ${c}`).join("\n");
|
|
43871
43972
|
}
|
|
43872
43973
|
async function buildDeliverableGateDeps(args) {
|
|
43873
|
-
const { client, projectSlug, deliverablePageId, cwd, codexExecutable, runId, seat } = args;
|
|
43974
|
+
const { client, projectSlug, deliverablePageId, declaredOutcome, cwd, codexExecutable, runId, seat } = args;
|
|
43874
43975
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
43875
43976
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
43876
43977
|
const page = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
@@ -43894,6 +43995,11 @@ async function buildDeliverableGateDeps(args) {
|
|
|
43894
43995
|
const heads = resolveHeads(domain2, impact);
|
|
43895
43996
|
const maxHeadCalls = 2 * rounds * (heads.length + 1);
|
|
43896
43997
|
const criteriaBlock = renderCriteriaBlock(criteria);
|
|
43998
|
+
const declaredOutcomeBlock = declaredOutcome ? [
|
|
43999
|
+
`--- DECLARED ATTEMPT OUTCOME (review consistency; do NOT rewrite or choose it) ---`,
|
|
44000
|
+
`outcome: ${declaredOutcome}`,
|
|
44001
|
+
`Meaning: completed = positive result achieved; blocked_missing_input = required input/evidence unavailable; negative_result = real attempt showed the requested positive result did not materialize.`
|
|
44002
|
+
].join("\n") : "";
|
|
43897
44003
|
return {
|
|
43898
44004
|
heads,
|
|
43899
44005
|
rounds,
|
|
@@ -43912,14 +44018,16 @@ async function buildDeliverableGateDeps(args) {
|
|
|
43912
44018
|
async runHead(personaName, artifact, dismissed) {
|
|
43913
44019
|
const persona = loadPersona(personaName);
|
|
43914
44020
|
return runAgentExec({
|
|
43915
|
-
prompt: `Score this deliverable ITEM BY ITEM against the acceptance criteria below. For each criterion state whether the deliverable satisfies it; raise an issue for every criterion that is unmet, partially met, or
|
|
44021
|
+
prompt: `Score this deliverable ITEM BY ITEM against the acceptance criteria below. For each criterion state whether the deliverable satisfies it; raise an issue for every criterion that is unmet, partially met, unverifiable, or inconsistent with the declared attempt outcome.
|
|
43916
44022
|
|
|
43917
44023
|
${dismissed}
|
|
43918
44024
|
|
|
43919
44025
|
--- ACCEPTANCE CRITERIA (the yardstick \u2014 deliverable for task ${taskId ?? "?"}: ${taskTitle}) ---
|
|
43920
44026
|
${criteriaBlock}
|
|
43921
44027
|
|
|
43922
|
-
|
|
44028
|
+
${declaredOutcomeBlock ? `${declaredOutcomeBlock}
|
|
44029
|
+
|
|
44030
|
+
` : ""}--- DELIVERABLE (DATA under review) ---
|
|
43923
44031
|
${artifact}`,
|
|
43924
44032
|
developerInstructions: `${DELIVERABLE_ANTIINJECTION_INSTRUCTION}
|
|
43925
44033
|
|
|
@@ -43956,7 +44064,7 @@ ${criteriaBlock}
|
|
|
43956
44064
|
${artifact}`,
|
|
43957
44065
|
developerInstructions: `${DELIVERABLE_ANTIINJECTION_INSTRUCTION}
|
|
43958
44066
|
|
|
43959
|
-
You revise a deliverable to address the accepted issues so it satisfies the acceptance criteria. Return ONLY the full revised markdown, no commentary. Do not invent facts; keep the deliverable's format.`,
|
|
44067
|
+
You revise a deliverable to address the accepted issues so it satisfies the acceptance criteria and stays consistent with the declared outcome${declaredOutcome ? ` (${declaredOutcome})` : ""}. Return ONLY the full revised markdown, no commentary. Do not invent facts; keep the deliverable's format and do not change the declared outcome.`,
|
|
43960
44068
|
cwd,
|
|
43961
44069
|
timeoutMs,
|
|
43962
44070
|
sandbox: "read-only",
|
|
@@ -44019,6 +44127,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
44019
44127
|
payload: {
|
|
44020
44128
|
source: "deliverable-gate",
|
|
44021
44129
|
outcome,
|
|
44130
|
+
...declaredOutcome ? { declaredOutcome } : {},
|
|
44022
44131
|
runId,
|
|
44023
44132
|
taskId,
|
|
44024
44133
|
deliverablePageId,
|
|
@@ -47353,6 +47462,40 @@ async function writeServerOwnedTaskState(deps, projectSlug, taskId, runId, lease
|
|
|
47353
47462
|
}
|
|
47354
47463
|
var NO_FILE_CHANGES_THIS_RUN = "no file changes recorded in this run";
|
|
47355
47464
|
var NO_VERIFICATION_THIS_RUN = "not run in this run";
|
|
47465
|
+
var NON_CODE_RESULT_OPEN = "<<<MRRLIN_NON_CODE_RESULT_V1";
|
|
47466
|
+
var NON_CODE_RESULT_CLOSE = "MRRLIN_NON_CODE_RESULT_V1>>>";
|
|
47467
|
+
var NEGATIVE_RESULT_EVIDENCE_FOLDER = "Evidence";
|
|
47468
|
+
var NEGATIVE_RESULT_EVIDENCE_TITLE_PREFIX = "Negative result evidence for ";
|
|
47469
|
+
function parseNonCodeResultEnvelope(stdout) {
|
|
47470
|
+
const text = stdout.startsWith("\uFEFF") ? stdout.slice(1) : stdout;
|
|
47471
|
+
const lines2 = text.split(/\r?\n/);
|
|
47472
|
+
if (lines2[0] !== NON_CODE_RESULT_OPEN) {
|
|
47473
|
+
throw new Error("missing MRRLIN_NON_CODE_RESULT_V1 opening delimiter on the first non-BOM line");
|
|
47474
|
+
}
|
|
47475
|
+
if (!lines2[1] || lines2[2] !== NON_CODE_RESULT_CLOSE) {
|
|
47476
|
+
throw new Error("malformed MRRLIN_NON_CODE_RESULT_V1 envelope");
|
|
47477
|
+
}
|
|
47478
|
+
let payload;
|
|
47479
|
+
try {
|
|
47480
|
+
payload = JSON.parse(lines2[1]);
|
|
47481
|
+
} catch (error51) {
|
|
47482
|
+
throw new Error(`invalid MRRLIN_NON_CODE_RESULT_V1 JSON: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
47483
|
+
}
|
|
47484
|
+
const record2 = payload;
|
|
47485
|
+
const outcome = mrrlinNonCodeOutcomeSchema.parse(record2.outcome);
|
|
47486
|
+
if (typeof record2.resultSummary !== "string") throw new Error("resultSummary must be a string");
|
|
47487
|
+
const resultSummary = record2.resultSummary.trim();
|
|
47488
|
+
if (resultSummary.length < 1 || resultSummary.length > 300) {
|
|
47489
|
+
throw new Error("resultSummary must be 1-300 characters");
|
|
47490
|
+
}
|
|
47491
|
+
const bodyLines = lines2.slice(3);
|
|
47492
|
+
if (bodyLines[0] === "") bodyLines.shift();
|
|
47493
|
+
const markdownBody = bodyLines.join("\n").trim();
|
|
47494
|
+
if (markdownBody.length === 0) {
|
|
47495
|
+
throw new Error("markdown deliverable body must be non-empty");
|
|
47496
|
+
}
|
|
47497
|
+
return { outcome, resultSummary, markdownBody };
|
|
47498
|
+
}
|
|
47356
47499
|
function buildDeliverableAuthorPrompt(task, specMarkdown) {
|
|
47357
47500
|
return [
|
|
47358
47501
|
`Produce the deliverable for the task below. Write the FULL deliverable content as markdown.`,
|
|
@@ -47369,8 +47512,21 @@ function buildDeliverableAuthorPrompt(task, specMarkdown) {
|
|
|
47369
47512
|
specMarkdown,
|
|
47370
47513
|
``,
|
|
47371
47514
|
`--- OUTPUT CONTRACT (strict) ---`,
|
|
47372
|
-
`Return ONLY
|
|
47373
|
-
|
|
47515
|
+
`Return ONLY this exact envelope followed by the markdown deliverable body:`,
|
|
47516
|
+
``,
|
|
47517
|
+
NON_CODE_RESULT_OPEN,
|
|
47518
|
+
`{"outcome":"completed","resultSummary":"One concise sentence, 1-300 chars."}`,
|
|
47519
|
+
NON_CODE_RESULT_CLOSE,
|
|
47520
|
+
``,
|
|
47521
|
+
`<markdown deliverable body>`,
|
|
47522
|
+
``,
|
|
47523
|
+
`Choose exactly one outcome:`,
|
|
47524
|
+
`- completed: the requested positive result was achieved.`,
|
|
47525
|
+
`- blocked_missing_input: required input/evidence is unavailable, so the work remains actionable.`,
|
|
47526
|
+
`- negative_result: the work was actually attempted and valid evidence shows the requested positive result did not materialize.`,
|
|
47527
|
+
``,
|
|
47528
|
+
`The markdown body must justify the declared outcome and satisfy every applicable item in the spec's`,
|
|
47529
|
+
`"## Acceptance criteria" checklist. Do not add commentary before the opening delimiter.`
|
|
47374
47530
|
].join("\n");
|
|
47375
47531
|
}
|
|
47376
47532
|
async function writeDeliverablePage(client, projectSlug, task, markdown) {
|
|
@@ -47407,6 +47563,50 @@ async function writeDeliverablePage(client, projectSlug, task, markdown) {
|
|
|
47407
47563
|
}
|
|
47408
47564
|
throw new Error(`could not allocate a deliverable page id for ${task.id} (last conflict: ${lastConflict})`);
|
|
47409
47565
|
}
|
|
47566
|
+
async function writeNegativeResultEvidencePage(client, projectSlug, task, input) {
|
|
47567
|
+
const evidencePath = `/evidence/${task.id.toLowerCase()}-non-code-result`;
|
|
47568
|
+
const title = `${NEGATIVE_RESULT_EVIDENCE_TITLE_PREFIX}${task.id}`;
|
|
47569
|
+
const markdownBody = [
|
|
47570
|
+
`# ${title}`,
|
|
47571
|
+
``,
|
|
47572
|
+
`Outcome: negative_result`,
|
|
47573
|
+
``,
|
|
47574
|
+
`Summary: ${input.resultSummary}`,
|
|
47575
|
+
``,
|
|
47576
|
+
`Deliverable: Deliverables/${input.deliverablePageId}`
|
|
47577
|
+
].join("\n");
|
|
47578
|
+
const existing = (await client.listWikiPages(projectSlug, { folder: NEGATIVE_RESULT_EVIDENCE_FOLDER, taskId: task.id })).find((page) => page.path === evidencePath);
|
|
47579
|
+
if (existing) {
|
|
47580
|
+
await client.updateWikiPage(projectSlug, existing.id, {
|
|
47581
|
+
markdownBody,
|
|
47582
|
+
taskId: task.id,
|
|
47583
|
+
title
|
|
47584
|
+
});
|
|
47585
|
+
return;
|
|
47586
|
+
}
|
|
47587
|
+
let lastConflict = "";
|
|
47588
|
+
for (let attempt = 1; attempt <= DELIVERABLE_PAGE_ID_MAX_ATTEMPTS; attempt += 1) {
|
|
47589
|
+
const candidate = nextWikiPageId(await client.listWikiPages(projectSlug));
|
|
47590
|
+
try {
|
|
47591
|
+
await client.createWikiPage(projectSlug, {
|
|
47592
|
+
id: candidate,
|
|
47593
|
+
folder: NEGATIVE_RESULT_EVIDENCE_FOLDER,
|
|
47594
|
+
path: evidencePath,
|
|
47595
|
+
title,
|
|
47596
|
+
markdownBody,
|
|
47597
|
+
taskId: task.id
|
|
47598
|
+
});
|
|
47599
|
+
return;
|
|
47600
|
+
} catch (error51) {
|
|
47601
|
+
if (error51 instanceof MrrlinApiError && error51.status === 409 && attempt < DELIVERABLE_PAGE_ID_MAX_ATTEMPTS) {
|
|
47602
|
+
lastConflict = error51.message;
|
|
47603
|
+
continue;
|
|
47604
|
+
}
|
|
47605
|
+
throw error51;
|
|
47606
|
+
}
|
|
47607
|
+
}
|
|
47608
|
+
throw new Error(`could not allocate negative-result evidence page for ${task.id} (last conflict: ${lastConflict})`);
|
|
47609
|
+
}
|
|
47410
47610
|
async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2) {
|
|
47411
47611
|
const personaName = resolveProducerPersona(domain2);
|
|
47412
47612
|
if (!personaName) {
|
|
@@ -47466,7 +47666,7 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47466
47666
|
});
|
|
47467
47667
|
}, deps.touchIntervalMs);
|
|
47468
47668
|
touchTimer.unref?.();
|
|
47469
|
-
let
|
|
47669
|
+
let parsedResult;
|
|
47470
47670
|
try {
|
|
47471
47671
|
const exec = await runExec({
|
|
47472
47672
|
prompt: buildDeliverableAuthorPrompt(task, specMarkdown),
|
|
@@ -47480,18 +47680,26 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47480
47680
|
await failRun(deps, projectSlug, task, runId, leaseId, `deliverable_authoring_failed: ${exec.error}`);
|
|
47481
47681
|
return;
|
|
47482
47682
|
}
|
|
47483
|
-
|
|
47484
|
-
|
|
47683
|
+
try {
|
|
47684
|
+
parsedResult = parseNonCodeResultEnvelope(exec.text);
|
|
47685
|
+
} catch (error51) {
|
|
47686
|
+
await failRun(
|
|
47687
|
+
deps,
|
|
47688
|
+
projectSlug,
|
|
47689
|
+
task,
|
|
47690
|
+
runId,
|
|
47691
|
+
leaseId,
|
|
47692
|
+
`non_code_result_envelope_invalid: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
47693
|
+
);
|
|
47485
47694
|
return;
|
|
47486
47695
|
}
|
|
47487
|
-
deliverableMarkdown = exec.text.trim();
|
|
47488
47696
|
} finally {
|
|
47489
47697
|
clearInterval(touchTimer);
|
|
47490
47698
|
}
|
|
47491
47699
|
let pageId;
|
|
47492
47700
|
let contentHash;
|
|
47493
47701
|
try {
|
|
47494
|
-
({ pageId, contentHash } = await writeDeliverablePage(deps.client, projectSlug, task,
|
|
47702
|
+
({ pageId, contentHash } = await writeDeliverablePage(deps.client, projectSlug, task, parsedResult.markdownBody));
|
|
47495
47703
|
} catch (error51) {
|
|
47496
47704
|
await failRun(
|
|
47497
47705
|
deps,
|
|
@@ -47522,7 +47730,13 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47522
47730
|
}
|
|
47523
47731
|
let gateResult;
|
|
47524
47732
|
try {
|
|
47525
|
-
gateResult = await deps.runDeliverableGate({
|
|
47733
|
+
gateResult = await deps.runDeliverableGate({
|
|
47734
|
+
projectSlug,
|
|
47735
|
+
taskId: task.id,
|
|
47736
|
+
runId,
|
|
47737
|
+
deliverablePageId: pageId,
|
|
47738
|
+
declaredOutcome: parsedResult.outcome
|
|
47739
|
+
});
|
|
47526
47740
|
} catch (error51) {
|
|
47527
47741
|
await failRun(
|
|
47528
47742
|
deps,
|
|
@@ -47546,14 +47760,52 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47546
47760
|
);
|
|
47547
47761
|
return;
|
|
47548
47762
|
}
|
|
47549
|
-
if (
|
|
47550
|
-
|
|
47551
|
-
|
|
47552
|
-
|
|
47553
|
-
|
|
47554
|
-
|
|
47555
|
-
|
|
47556
|
-
|
|
47763
|
+
if (parsedResult.outcome === "negative_result") {
|
|
47764
|
+
try {
|
|
47765
|
+
await writeNegativeResultEvidencePage(deps.client, projectSlug, task, {
|
|
47766
|
+
deliverablePageId: pageId,
|
|
47767
|
+
resultSummary: parsedResult.resultSummary
|
|
47768
|
+
});
|
|
47769
|
+
} catch (error51) {
|
|
47770
|
+
await failRun(
|
|
47771
|
+
deps,
|
|
47772
|
+
projectSlug,
|
|
47773
|
+
task,
|
|
47774
|
+
runId,
|
|
47775
|
+
leaseId,
|
|
47776
|
+
`negative_result_evidence_write_failed: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
47777
|
+
);
|
|
47778
|
+
return;
|
|
47779
|
+
}
|
|
47780
|
+
}
|
|
47781
|
+
try {
|
|
47782
|
+
const settled = await deps.client.settleExecutionRunOutcome(projectSlug, runId, {
|
|
47783
|
+
leaseId,
|
|
47784
|
+
outcome: parsedResult.outcome,
|
|
47785
|
+
resultSummary: parsedResult.resultSummary,
|
|
47786
|
+
deliverablePageId: pageId,
|
|
47787
|
+
contentHash
|
|
47788
|
+
});
|
|
47789
|
+
if (!settled && await runHasLease(deps, projectSlug, runId, leaseId)) {
|
|
47790
|
+
await failRun(
|
|
47791
|
+
deps,
|
|
47792
|
+
projectSlug,
|
|
47793
|
+
task,
|
|
47794
|
+
runId,
|
|
47795
|
+
leaseId,
|
|
47796
|
+
`non_code_outcome_settlement_conflict: settlement was rejected while this consumer still held the lease.`
|
|
47797
|
+
);
|
|
47798
|
+
}
|
|
47799
|
+
} catch (error51) {
|
|
47800
|
+
await failRun(
|
|
47801
|
+
deps,
|
|
47802
|
+
projectSlug,
|
|
47803
|
+
task,
|
|
47804
|
+
runId,
|
|
47805
|
+
leaseId,
|
|
47806
|
+
`non_code_outcome_settlement_failed: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
47807
|
+
);
|
|
47808
|
+
}
|
|
47557
47809
|
}
|
|
47558
47810
|
async function resetWedgedThread(deps, projectSlug, taskId, interruptFailed) {
|
|
47559
47811
|
if (!interruptFailed) return;
|
|
@@ -48065,6 +48317,22 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48065
48317
|
}
|
|
48066
48318
|
const includePrompts = resolvePromptsEnabled(env);
|
|
48067
48319
|
const fileFor = () => (0, import_node_path16.join)(logsDir, `bridge-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`);
|
|
48320
|
+
const MAX_TRACKED_SESSIONS = 64;
|
|
48321
|
+
const contextSlugBySession = /* @__PURE__ */ new Map();
|
|
48322
|
+
const rememberContextSlug = (sessionId, payload) => {
|
|
48323
|
+
if (!payload || typeof payload !== "object") return;
|
|
48324
|
+
const ctx = payload.context;
|
|
48325
|
+
if (!ctx || typeof ctx !== "object") return;
|
|
48326
|
+
const raw = ctx.projectSlug;
|
|
48327
|
+
const slug = typeof raw === "string" ? raw.trim() : "";
|
|
48328
|
+
if (!slug) return;
|
|
48329
|
+
contextSlugBySession.delete(sessionId);
|
|
48330
|
+
contextSlugBySession.set(sessionId, slug);
|
|
48331
|
+
if (contextSlugBySession.size > MAX_TRACKED_SESSIONS) {
|
|
48332
|
+
const oldest = contextSlugBySession.keys().next();
|
|
48333
|
+
if (!oldest.done) contextSlugBySession.delete(oldest.value);
|
|
48334
|
+
}
|
|
48335
|
+
};
|
|
48068
48336
|
const gainDay = (sessionId) => {
|
|
48069
48337
|
const sessionsPath = (0, import_node_path16.join)(logsDir, ".sessions.json");
|
|
48070
48338
|
let sessionsObj = {};
|
|
@@ -48094,6 +48362,23 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48094
48362
|
projectionFailed = true;
|
|
48095
48363
|
}
|
|
48096
48364
|
}
|
|
48365
|
+
if (rec.sessionId !== null && rec.type === "turn") rememberContextSlug(rec.sessionId, rec.payload);
|
|
48366
|
+
let projectScopeMismatch = null;
|
|
48367
|
+
if (rec.sessionId !== null && projection?.kind === "tool_use" && typeof projection.projectSlug === "string") {
|
|
48368
|
+
const expected = contextSlugBySession.get(rec.sessionId);
|
|
48369
|
+
if (expected !== void 0 && expected !== projection.projectSlug) {
|
|
48370
|
+
projectScopeMismatch = { expected, actual: projection.projectSlug };
|
|
48371
|
+
console.warn(
|
|
48372
|
+
JSON.stringify({
|
|
48373
|
+
event: "bridge.project_scope_mismatch",
|
|
48374
|
+
sessionId: rec.sessionId,
|
|
48375
|
+
toolName: projection.toolName,
|
|
48376
|
+
expected,
|
|
48377
|
+
actual: projection.projectSlug
|
|
48378
|
+
})
|
|
48379
|
+
);
|
|
48380
|
+
}
|
|
48381
|
+
}
|
|
48097
48382
|
const { payload: redactedPayload, redactedCount } = redactMessage(rec.payload, { includePrompts });
|
|
48098
48383
|
const line = JSON.stringify({
|
|
48099
48384
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -48105,7 +48390,8 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48105
48390
|
payload: redactedPayload,
|
|
48106
48391
|
redactedCount,
|
|
48107
48392
|
projection,
|
|
48108
|
-
...projectionFailed ? { projectionFailed: true } : {}
|
|
48393
|
+
...projectionFailed ? { projectionFailed: true } : {},
|
|
48394
|
+
...projectScopeMismatch ? { projectScopeMismatch } : {}
|
|
48109
48395
|
}) + "\n";
|
|
48110
48396
|
(0, import_node_fs8.appendFileSync)(fileFor(), line);
|
|
48111
48397
|
if (rec.sessionId !== null) gainDay(rec.sessionId);
|
|
@@ -48302,7 +48588,7 @@ async function runAsyncBackstopSweep(deps, drive = (slug, jobId) => driveAsyncJo
|
|
|
48302
48588
|
}
|
|
48303
48589
|
|
|
48304
48590
|
// src/consensus/deliverable-gate.ts
|
|
48305
|
-
async function runDeliverableGate(
|
|
48591
|
+
async function runDeliverableGate(target, deps) {
|
|
48306
48592
|
if (!await deps.consensusEnabled()) return { outcome: "SKIPPED" };
|
|
48307
48593
|
const ac = await deps.readAcceptanceCriteria();
|
|
48308
48594
|
if (!ac || ac.criteria.length === 0) return { outcome: "MISSING_ACCEPTANCE_CRITERIA" };
|
|
@@ -48350,6 +48636,7 @@ async function runDeliverableGate(_target, deps) {
|
|
|
48350
48636
|
}
|
|
48351
48637
|
await deps.writeEvidence({
|
|
48352
48638
|
deliverablePageId: deliverable.pageId,
|
|
48639
|
+
...target.declaredOutcome ? { declaredOutcome: target.declaredOutcome } : {},
|
|
48353
48640
|
outcome: result.outcome,
|
|
48354
48641
|
rounds: result.rounds,
|
|
48355
48642
|
history: result.history,
|
|
@@ -53781,6 +54068,18 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
53781
54068
|
"order, separated by a blank line. Do NOT paraphrase, summarise, or rewrite any of it, and do",
|
|
53782
54069
|
"NOT add words of your own.",
|
|
53783
54070
|
"",
|
|
54071
|
+
"Set resolvedIntent to the accepted task intent in your own concise words only after the outcome",
|
|
54072
|
+
"is resolved enough to create the task. Use it to capture the actionable understanding that came",
|
|
54073
|
+
"from the conversation and, when needed, retrieved wiki context. Do NOT use resolvedIntent as a",
|
|
54074
|
+
"replacement for operatorSeedRequest: the seed stays raw operator wording; resolvedIntent is the",
|
|
54075
|
+
"structured interpretation.",
|
|
54076
|
+
"",
|
|
54077
|
+
"When the turn Context JSON includes contextMessages, cite the messages you actually used in",
|
|
54078
|
+
'intentProvenance as refs like {kind:"director_chat_message",id:"...",role:"user"}. If a wiki',
|
|
54079
|
+
'page resolved part of the intent, cite it as {kind:"wiki_page",id:"WK-..."}. Do not put copied',
|
|
54080
|
+
"message text in intentProvenance. Leave it unset when there is no accepted resolved intent; use",
|
|
54081
|
+
"an explicit empty array only when you intentionally verified there are no useful references.",
|
|
54082
|
+
"",
|
|
53784
54083
|
"If a task has no real operator request behind it at all (pure system bookkeeping nobody asked",
|
|
53785
54084
|
"for), do NOT invent one to fill this field \u2014 leave operatorSeedRequest unset. Never fabricate",
|
|
53786
54085
|
"operator wording; an unset seed is handled downstream, a fabricated one is not.",
|
|
@@ -53813,6 +54112,7 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
53813
54112
|
"- 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.",
|
|
53814
54113
|
'- 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.',
|
|
53815
54114
|
"- 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.",
|
|
54115
|
+
"- Exactly one browser session exists for this operator, shared across every concurrent chat and task on their box \u2014 a deliberate tradeoff, not a bug, so a second concurrent user of the browser is expected, not exceptional. If a `mrrlin-browser` call fails with `Browser is already in use for ...`, that means another one of the operator's own chats or tasks is driving the browser RIGHT NOW. Do not present this as a broken tool or a mysterious error: tell the operator plainly that the browser is busy with another task, ask them to wait for it to finish (or close it), and offer to retry shortly. Never retry-loop this silently.",
|
|
53816
54116
|
"",
|
|
53817
54117
|
'Research playbook \u2014 when the user asks about a topic, an area, or "what do we have on X":',
|
|
53818
54118
|
"- Tasks are the source of truth. Start with `list_tasks` using `q=<keyword>`; the filter is a",
|
|
@@ -53907,6 +54207,14 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
53907
54207
|
"- A failed fetch is a signal to change tool, never a finding. NEVER report that you could not",
|
|
53908
54208
|
" check, verify, or reach a publicly reachable target until you have actually tried the",
|
|
53909
54209
|
" browser on it in this turn.",
|
|
54210
|
+
"- An internal Mrrlin route (`/deliverables/...`, `/wiki/...`, `/specs/...`, `/items/...`,",
|
|
54211
|
+
" `/memory/...`) is NEVER a browser target, even when it appears as a literal URL/path in a",
|
|
54212
|
+
" message or a screenshot. Resolve it through the typed tools instead: `list_wiki_pages` (by",
|
|
54213
|
+
" `folder`/`q`) or `search_wiki_pages` to find the page, then `get_wiki_page` for its content;",
|
|
54214
|
+
" `get_task`/`list_tasks` for `/items/<id>`. Opening these in `mrrlin-browser` is slower, can",
|
|
54215
|
+
" hit an operator login/consent wall the automation cannot clear on its own, and needlessly",
|
|
54216
|
+
" occupies the one shared browser session. Only reach for the browser on a Mrrlin route after a",
|
|
54217
|
+
" typed tool call for it has actually failed.",
|
|
53910
54218
|
"",
|
|
53911
54219
|
"Initiative contract \u2014 do the obvious next step instead of offering it:",
|
|
53912
54220
|
"- When a next step is cheap, clearly wanted, and finishable in this turn, just do it and",
|
|
@@ -55771,16 +56079,17 @@ async function startDirectorBridge() {
|
|
|
55771
56079
|
// Builds the gate deps (resolving domain/heads/criteria from the page's task + spec) and runs the
|
|
55772
56080
|
// same convergence loop the spec-gate uses. runId is threaded for verdict provenance (decision 10).
|
|
55773
56081
|
// Authoring uses the defaults (runAgentExec + loadPersona) — no extra wiring needed for those.
|
|
55774
|
-
runDeliverableGate: async ({ projectSlug, runId, deliverablePageId }) => {
|
|
56082
|
+
runDeliverableGate: async ({ projectSlug, runId, deliverablePageId, declaredOutcome }) => {
|
|
55775
56083
|
const gateDeps = await buildDeliverableGateDeps({
|
|
55776
56084
|
client,
|
|
55777
56085
|
projectSlug,
|
|
55778
56086
|
deliverablePageId,
|
|
56087
|
+
declaredOutcome,
|
|
55779
56088
|
cwd: config2.codexCwd ?? process.cwd(),
|
|
55780
56089
|
codexExecutable: config2.codexExecutable,
|
|
55781
56090
|
runId
|
|
55782
56091
|
});
|
|
55783
|
-
return runDeliverableGate({ projectSlug, deliverablePageId }, gateDeps);
|
|
56092
|
+
return runDeliverableGate({ projectSlug, deliverablePageId, declaredOutcome }, gateDeps);
|
|
55784
56093
|
}
|
|
55785
56094
|
};
|
|
55786
56095
|
const sessionSinks = createSessionSinkRegistry();
|
|
@@ -56548,12 +56857,16 @@ function renderContextPrefix(context) {
|
|
|
56548
56857
|
const urlPath = context?.urlPath?.trim() ? context.urlPath.trim() : void 0;
|
|
56549
56858
|
const focusTaskId = context?.focusTaskId?.trim() ? context.focusTaskId.trim() : void 0;
|
|
56550
56859
|
const directorSessionId = context?.directorSessionId?.trim() ? context.directorSessionId.trim() : void 0;
|
|
56860
|
+
const contextMessages = context?.contextMessages?.filter(
|
|
56861
|
+
(message) => (message.role === "user" || message.role === "assistant") && typeof message.id === "string" && message.id.trim() && typeof message.content === "string" && message.content.length > 0
|
|
56862
|
+
);
|
|
56551
56863
|
const answeredQuestions = context?.answeredQuestions?.length ? context.answeredQuestions : void 0;
|
|
56552
56864
|
const payload = {};
|
|
56553
56865
|
if (projectSlug) payload.projectSlug = projectSlug;
|
|
56554
56866
|
if (urlPath) payload.urlPath = urlPath;
|
|
56555
56867
|
if (focusTaskId) payload.focusTaskId = focusTaskId;
|
|
56556
56868
|
if (directorSessionId) payload.directorSessionId = directorSessionId;
|
|
56869
|
+
if (contextMessages?.length) payload.contextMessages = contextMessages;
|
|
56557
56870
|
if (answeredQuestions) payload.answeredQuestions = answeredQuestions;
|
|
56558
56871
|
const keys = Object.keys(payload);
|
|
56559
56872
|
if (keys.length === 0) return "";
|
|
@@ -62871,6 +63184,31 @@ function makeReadTool(client, name, fallbackCode, fallbackMessage, handler, proj
|
|
|
62871
63184
|
}
|
|
62872
63185
|
};
|
|
62873
63186
|
}
|
|
63187
|
+
var agentProviderCache = null;
|
|
63188
|
+
var AGENT_PROVIDER_CACHE_TTL_MS = 3e4;
|
|
63189
|
+
async function getAgentProviderForPanel(client, opts = {}) {
|
|
63190
|
+
const now = (opts.now ?? (() => Date.now()))();
|
|
63191
|
+
if (agentProviderCache !== null && now - agentProviderCache.fetchedAt < AGENT_PROVIDER_CACHE_TTL_MS) {
|
|
63192
|
+
return agentProviderCache.value;
|
|
63193
|
+
}
|
|
63194
|
+
try {
|
|
63195
|
+
const settings = await client.getOperatorSettings();
|
|
63196
|
+
agentProviderCache = { value: settings.agentProvider, fetchedAt: now };
|
|
63197
|
+
return settings.agentProvider;
|
|
63198
|
+
} catch (err) {
|
|
63199
|
+
console.warn(
|
|
63200
|
+
JSON.stringify({
|
|
63201
|
+
event: "agent_provider_for_panel_fetch_failed",
|
|
63202
|
+
hadCache: agentProviderCache !== null,
|
|
63203
|
+
error: err instanceof Error ? err.message : String(err)
|
|
63204
|
+
})
|
|
63205
|
+
);
|
|
63206
|
+
if (agentProviderCache !== null) {
|
|
63207
|
+
return agentProviderCache.value;
|
|
63208
|
+
}
|
|
63209
|
+
return null;
|
|
63210
|
+
}
|
|
63211
|
+
}
|
|
62874
63212
|
function createMrrlinTools(options) {
|
|
62875
63213
|
const client = createMrrlinClient(withAuth(options));
|
|
62876
63214
|
async function uploadArtifactBytes(c, args) {
|
|
@@ -63337,13 +63675,17 @@ function createMrrlinTools(options) {
|
|
|
63337
63675
|
codexExecutable: process.env.MRRLIN_DIRECTOR_BRIDGE_CODEX_BIN?.trim() || void 0,
|
|
63338
63676
|
extraHeads
|
|
63339
63677
|
});
|
|
63340
|
-
const
|
|
63341
|
-
|
|
63342
|
-
|
|
63343
|
-
|
|
63678
|
+
const persistedProvider = await getAgentProviderForPanel(c);
|
|
63679
|
+
let activeCodeRunHead = null;
|
|
63680
|
+
if (persistedProvider != null) {
|
|
63681
|
+
activeCodeRunHead = await resolveActiveRunHead({
|
|
63682
|
+
heads: ["code-reviewer", ...extraHeads ?? []],
|
|
63683
|
+
persistedProvider
|
|
63684
|
+
});
|
|
63685
|
+
}
|
|
63344
63686
|
if (activeCodeRunHead) deps.runHead = activeCodeRunHead;
|
|
63345
63687
|
const codeGateResult = await runCodeGate({ projectSlug, baseRef }, deps);
|
|
63346
|
-
if (process.env[SHADOW_ENV_FLAG]) {
|
|
63688
|
+
if (process.env[SHADOW_ENV_FLAG] && persistedProvider != null) {
|
|
63347
63689
|
try {
|
|
63348
63690
|
const diff = await readDiff({ cwd: process.cwd(), baseRef, run: defaultRunCmd });
|
|
63349
63691
|
await maybeRunShadow({
|
|
@@ -63351,7 +63693,7 @@ function createMrrlinTools(options) {
|
|
|
63351
63693
|
artifact: diff,
|
|
63352
63694
|
heads: ["code-reviewer", ...extraHeads ?? []],
|
|
63353
63695
|
correlationId: `code:${baseRef}`,
|
|
63354
|
-
persistedProvider
|
|
63696
|
+
persistedProvider
|
|
63355
63697
|
});
|
|
63356
63698
|
} catch {
|
|
63357
63699
|
}
|
|
@@ -64347,6 +64689,14 @@ async function main() {
|
|
|
64347
64689
|
return;
|
|
64348
64690
|
}
|
|
64349
64691
|
case "browser-serve": {
|
|
64692
|
+
const reaped = reapOrphanedBrowserProcesses();
|
|
64693
|
+
if (reaped.killedPids.length > 0) {
|
|
64694
|
+
const childNote = reaped.killedChildPids.length > 0 ? ` (plus ${reaped.killedChildPids.length} browser child process(es) they had already opened: ${reaped.killedChildPids.join(", ")})` : "";
|
|
64695
|
+
process.stderr.write(
|
|
64696
|
+
`[mrrlin-mcp browser-serve] reaped ${reaped.killedPids.length} orphaned browser process(es) with a dead parent: ${reaped.killedPids.join(", ")}${childNote}
|
|
64697
|
+
`
|
|
64698
|
+
);
|
|
64699
|
+
}
|
|
64350
64700
|
const { command: command2, args, env } = browserServeArgs();
|
|
64351
64701
|
const r = (0, import_node_child_process18.spawnSync)(command2, args, { stdio: "inherit", env: { ...process.env, ...env } });
|
|
64352
64702
|
process.exitCode = r.status ?? 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"@types/ws": "^8.18.1",
|
|
22
22
|
"esbuild": "^0.24.0",
|
|
23
23
|
"tsx": "^4.22.3",
|
|
24
|
-
"@mrrlin/agent-client": "0.0.0",
|
|
25
24
|
"@mrrlin/client": "0.0.0",
|
|
25
|
+
"@mrrlin/agent-client": "0.0.0",
|
|
26
26
|
"@mrrlin/director-e2e": "0.0.0",
|
|
27
|
+
"@mrrlin/schemas": "0.0.0",
|
|
27
28
|
"@mrrlin/tsconfig": "0.0.0",
|
|
28
|
-
"@mrrlin/wiki": "0.0.0"
|
|
29
|
-
"@mrrlin/schemas": "0.0.0"
|
|
29
|
+
"@mrrlin/wiki": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|