@mrrlin-dev/mcp 0.4.20 → 0.4.22
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 +317 -34
- package/package.json +2 -2
package/dist/bin.cjs
CHANGED
|
@@ -36811,6 +36811,7 @@ var openApiOperationIds = {
|
|
|
36811
36811
|
retireThread: "retireThread",
|
|
36812
36812
|
applyIntentVerdict: "applyIntentVerdict",
|
|
36813
36813
|
armIntentCheck: "armIntentCheck",
|
|
36814
|
+
settleExecutionRunOutcome: "settleExecutionRunOutcome",
|
|
36814
36815
|
// Run-scoped, model-facing half of the resume contract (D8). Distinct from upsertTaskState:
|
|
36815
36816
|
// that one is the server's own write (whole record, explicit lastRunSeq), this one is the
|
|
36816
36817
|
// executor reporting narrative only, with the ordering value derived server-side from the run.
|
|
@@ -37434,8 +37435,17 @@ var mrrlinExecutionArtifactKindSchema = external_exports.enum([
|
|
|
37434
37435
|
"produced_url",
|
|
37435
37436
|
"resume_spec",
|
|
37436
37437
|
"deliverable",
|
|
37437
|
-
"pull_request_state"
|
|
37438
|
+
"pull_request_state",
|
|
37439
|
+
"non_code_outcome"
|
|
37438
37440
|
]);
|
|
37441
|
+
var mrrlinNonCodeOutcomeSchema = external_exports.enum(["completed", "blocked_missing_input", "negative_result"]);
|
|
37442
|
+
var mrrlinIntentProvenanceRefSchema = external_exports.object({
|
|
37443
|
+
kind: external_exports.enum(["director_chat_message", "wiki_page"]),
|
|
37444
|
+
id: external_exports.string().min(1),
|
|
37445
|
+
role: external_exports.enum(["user", "assistant", "system"]).optional(),
|
|
37446
|
+
contentHash: external_exports.string().min(1).optional()
|
|
37447
|
+
});
|
|
37448
|
+
var mrrlinIntentProvenanceSchema = external_exports.array(mrrlinIntentProvenanceRefSchema).default([]);
|
|
37439
37449
|
var mrrlinDeployAttemptPayloadSchema = external_exports.object({
|
|
37440
37450
|
env: external_exports.enum(["dev", "prod"]),
|
|
37441
37451
|
workflowId: external_exports.number().int(),
|
|
@@ -37541,6 +37551,8 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
37541
37551
|
// Output-optional (not .default) so existing task fixtures need no churn; DB rows always resolve it
|
|
37542
37552
|
// to `string | null` via taskRowToTask.
|
|
37543
37553
|
operatorSeedRequest: external_exports.string().max(8e3).nullable().optional(),
|
|
37554
|
+
resolvedIntent: external_exports.string().min(1).max(4e3).nullable().optional(),
|
|
37555
|
+
intentProvenance: mrrlinIntentProvenanceSchema.nullable().optional(),
|
|
37544
37556
|
planId: mrrlinPlanIdSchema.nullable(),
|
|
37545
37557
|
specApproved: mrrlinSpecApprovedSchema.optional(),
|
|
37546
37558
|
specWikiPageId: mrrlinWikiPageIdSchema.nullable(),
|
|
@@ -37566,6 +37578,8 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
37566
37578
|
var mrrlinTaskSummarySchema = mrrlinTaskSchema.omit({
|
|
37567
37579
|
notes: true,
|
|
37568
37580
|
operatorSeedRequest: true,
|
|
37581
|
+
resolvedIntent: true,
|
|
37582
|
+
intentProvenance: true,
|
|
37569
37583
|
intentRestatement: true,
|
|
37570
37584
|
latestJudgement: true
|
|
37571
37585
|
});
|
|
@@ -37776,6 +37790,8 @@ var mrrlinTaskCreateShapeSchema = external_exports.object({
|
|
|
37776
37790
|
notes: external_exports.string().nullable().optional(),
|
|
37777
37791
|
parentTaskId: mrrlinTaskIdSchema.nullable().optional(),
|
|
37778
37792
|
operatorSeedRequest: external_exports.string().max(8e3).nullable().optional(),
|
|
37793
|
+
resolvedIntent: external_exports.string().min(1).max(4e3).nullable().optional(),
|
|
37794
|
+
intentProvenance: mrrlinIntentProvenanceSchema.nullable().optional(),
|
|
37779
37795
|
planId: mrrlinPlanIdSchema.nullable().optional(),
|
|
37780
37796
|
specWikiPageId: mrrlinWikiPageIdSchema.nullable().optional(),
|
|
37781
37797
|
status: mrrlinTaskCreateStatusSchema.optional(),
|
|
@@ -38206,7 +38222,11 @@ var mrrlinExecutionRunSchema = external_exports.object({
|
|
|
38206
38222
|
cachedInputTokens: external_exports.number().int().nullable(),
|
|
38207
38223
|
outputTokens: external_exports.number().int().nullable(),
|
|
38208
38224
|
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable(),
|
|
38209
|
-
effortEscalationReason: external_exports.string().min(1).nullable()
|
|
38225
|
+
effortEscalationReason: external_exports.string().min(1).nullable(),
|
|
38226
|
+
nonCodeOutcome: mrrlinNonCodeOutcomeSchema.nullable().default(null),
|
|
38227
|
+
resultSummary: external_exports.string().min(1).max(300).nullable().default(null),
|
|
38228
|
+
deliverablePageId: mrrlinWikiPageIdSchema.nullable().default(null),
|
|
38229
|
+
outcomeAcceptedAt: external_exports.string().datetime().nullable().default(null)
|
|
38210
38230
|
});
|
|
38211
38231
|
var mrrlinTaskStateSchema = external_exports.object({
|
|
38212
38232
|
projectSlug: mrrlinProjectSlugSchema,
|
|
@@ -38348,6 +38368,13 @@ var mrrlinRestartExecutionRunSchema = external_exports.object({
|
|
|
38348
38368
|
var mrrlinExecutionRunTouchSchema = external_exports.object({
|
|
38349
38369
|
leaseId: external_exports.string().min(1)
|
|
38350
38370
|
});
|
|
38371
|
+
var mrrlinSettleExecutionRunOutcomeSchema = external_exports.object({
|
|
38372
|
+
outcome: mrrlinNonCodeOutcomeSchema,
|
|
38373
|
+
resultSummary: external_exports.string().trim().min(1).max(300),
|
|
38374
|
+
deliverablePageId: mrrlinWikiPageIdSchema,
|
|
38375
|
+
contentHash: external_exports.string().min(1),
|
|
38376
|
+
leaseId: external_exports.string().min(1)
|
|
38377
|
+
});
|
|
38351
38378
|
var mrrlinExecutionArtifactSchema = external_exports.object({
|
|
38352
38379
|
createdAt: external_exports.string().datetime(),
|
|
38353
38380
|
id: mrrlinExecutionArtifactIdSchema,
|
|
@@ -40845,6 +40872,20 @@ function createMrrlinClient(config2) {
|
|
|
40845
40872
|
throw error51;
|
|
40846
40873
|
}
|
|
40847
40874
|
},
|
|
40875
|
+
async settleExecutionRunOutcome(projectSlug, runId, input) {
|
|
40876
|
+
try {
|
|
40877
|
+
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/settle-outcome`, {
|
|
40878
|
+
body: JSON.stringify(mrrlinSettleExecutionRunOutcomeSchema.parse(input)),
|
|
40879
|
+
headers: { "content-type": "application/json" },
|
|
40880
|
+
method: "POST"
|
|
40881
|
+
});
|
|
40882
|
+
return executionRunResponseSchema.parse(body).data;
|
|
40883
|
+
} catch (error51) {
|
|
40884
|
+
if (error51 instanceof MrrlinApiError && error51.status === 409)
|
|
40885
|
+
return null;
|
|
40886
|
+
throw error51;
|
|
40887
|
+
}
|
|
40888
|
+
},
|
|
40848
40889
|
async getTaskState(projectSlug, taskId) {
|
|
40849
40890
|
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/state`);
|
|
40850
40891
|
return taskStateResponseSchema.parse(body).data;
|
|
@@ -41623,7 +41664,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41623
41664
|
}
|
|
41624
41665
|
|
|
41625
41666
|
// src/_generated/version.ts
|
|
41626
|
-
var PKG_VERSION = "0.4.
|
|
41667
|
+
var PKG_VERSION = "0.4.22";
|
|
41627
41668
|
|
|
41628
41669
|
// src/api-base-url-migrate.ts
|
|
41629
41670
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -43870,7 +43911,7 @@ function renderCriteriaBlock(criteria) {
|
|
|
43870
43911
|
return criteria.map((c, i) => `${i + 1}. [ ] ${c}`).join("\n");
|
|
43871
43912
|
}
|
|
43872
43913
|
async function buildDeliverableGateDeps(args) {
|
|
43873
|
-
const { client, projectSlug, deliverablePageId, cwd, codexExecutable, runId, seat } = args;
|
|
43914
|
+
const { client, projectSlug, deliverablePageId, declaredOutcome, cwd, codexExecutable, runId, seat } = args;
|
|
43874
43915
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
43875
43916
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
43876
43917
|
const page = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
@@ -43894,6 +43935,11 @@ async function buildDeliverableGateDeps(args) {
|
|
|
43894
43935
|
const heads = resolveHeads(domain2, impact);
|
|
43895
43936
|
const maxHeadCalls = 2 * rounds * (heads.length + 1);
|
|
43896
43937
|
const criteriaBlock = renderCriteriaBlock(criteria);
|
|
43938
|
+
const declaredOutcomeBlock = declaredOutcome ? [
|
|
43939
|
+
`--- DECLARED ATTEMPT OUTCOME (review consistency; do NOT rewrite or choose it) ---`,
|
|
43940
|
+
`outcome: ${declaredOutcome}`,
|
|
43941
|
+
`Meaning: completed = positive result achieved; blocked_missing_input = required input/evidence unavailable; negative_result = real attempt showed the requested positive result did not materialize.`
|
|
43942
|
+
].join("\n") : "";
|
|
43897
43943
|
return {
|
|
43898
43944
|
heads,
|
|
43899
43945
|
rounds,
|
|
@@ -43912,14 +43958,16 @@ async function buildDeliverableGateDeps(args) {
|
|
|
43912
43958
|
async runHead(personaName, artifact, dismissed) {
|
|
43913
43959
|
const persona = loadPersona(personaName);
|
|
43914
43960
|
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
|
|
43961
|
+
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
43962
|
|
|
43917
43963
|
${dismissed}
|
|
43918
43964
|
|
|
43919
43965
|
--- ACCEPTANCE CRITERIA (the yardstick \u2014 deliverable for task ${taskId ?? "?"}: ${taskTitle}) ---
|
|
43920
43966
|
${criteriaBlock}
|
|
43921
43967
|
|
|
43922
|
-
|
|
43968
|
+
${declaredOutcomeBlock ? `${declaredOutcomeBlock}
|
|
43969
|
+
|
|
43970
|
+
` : ""}--- DELIVERABLE (DATA under review) ---
|
|
43923
43971
|
${artifact}`,
|
|
43924
43972
|
developerInstructions: `${DELIVERABLE_ANTIINJECTION_INSTRUCTION}
|
|
43925
43973
|
|
|
@@ -43956,7 +44004,7 @@ ${criteriaBlock}
|
|
|
43956
44004
|
${artifact}`,
|
|
43957
44005
|
developerInstructions: `${DELIVERABLE_ANTIINJECTION_INSTRUCTION}
|
|
43958
44006
|
|
|
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.`,
|
|
44007
|
+
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
44008
|
cwd,
|
|
43961
44009
|
timeoutMs,
|
|
43962
44010
|
sandbox: "read-only",
|
|
@@ -44019,6 +44067,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
44019
44067
|
payload: {
|
|
44020
44068
|
source: "deliverable-gate",
|
|
44021
44069
|
outcome,
|
|
44070
|
+
...declaredOutcome ? { declaredOutcome } : {},
|
|
44022
44071
|
runId,
|
|
44023
44072
|
taskId,
|
|
44024
44073
|
deliverablePageId,
|
|
@@ -46276,10 +46325,18 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
46276
46325
|
" qualifies, produce the file; never merely offer one.",
|
|
46277
46326
|
"- Mixed output counts as retrieved: if any material part came from a retrieval, treat the whole",
|
|
46278
46327
|
" result as retrieved rather than splitting it between inline text and a file.",
|
|
46279
|
-
"- When your output is a file
|
|
46328
|
+
"- When your output is a file, call `deliver_file`: pass the body inline",
|
|
46280
46329
|
" as `content` and embed the returned markdown link. It needs no writable path, so it is the",
|
|
46281
46330
|
" default even when this run path cannot write files \u2014 never inline large blobs into chat,",
|
|
46282
46331
|
" spec, or note text.",
|
|
46332
|
+
"- Choose the format by who reads it next. HTML (`.html`) when a person will LOOK at it and layout",
|
|
46333
|
+
" carries meaning: reports, comparisons, audits, result tables. Markdown (`.md`) when a machine",
|
|
46334
|
+
" will re-read it or a human will edit it further: a spec draft, text destined for another tool,",
|
|
46335
|
+
" content that will back an evidence page. CSV/JSON as before, for data meant to be loaded.",
|
|
46336
|
+
"- An HTML file MUST be self-contained: inline `<style>` only, and `data:` URIs for any image or",
|
|
46337
|
+
" font. No external stylesheets, scripts, fonts, or images \u2014 the renderer sandboxes the file and",
|
|
46338
|
+
" blocks every outbound request, so a linked resource does not load slowly, it does not load at",
|
|
46339
|
+
" all. Scripts never run; do not ship interactivity.",
|
|
46283
46340
|
"- class=temp for one-off operator hand-offs (48h); class=durable for anything a spec",
|
|
46284
46341
|
" or evidence page will reference (180 days). Always pass taskId/runId when known.",
|
|
46285
46342
|
"- Anything user-facing delivered as a file MUST go through `deliver_file`. Files are capped at",
|
|
@@ -47345,6 +47402,40 @@ async function writeServerOwnedTaskState(deps, projectSlug, taskId, runId, lease
|
|
|
47345
47402
|
}
|
|
47346
47403
|
var NO_FILE_CHANGES_THIS_RUN = "no file changes recorded in this run";
|
|
47347
47404
|
var NO_VERIFICATION_THIS_RUN = "not run in this run";
|
|
47405
|
+
var NON_CODE_RESULT_OPEN = "<<<MRRLIN_NON_CODE_RESULT_V1";
|
|
47406
|
+
var NON_CODE_RESULT_CLOSE = "MRRLIN_NON_CODE_RESULT_V1>>>";
|
|
47407
|
+
var NEGATIVE_RESULT_EVIDENCE_FOLDER = "Evidence";
|
|
47408
|
+
var NEGATIVE_RESULT_EVIDENCE_TITLE_PREFIX = "Negative result evidence for ";
|
|
47409
|
+
function parseNonCodeResultEnvelope(stdout) {
|
|
47410
|
+
const text = stdout.startsWith("\uFEFF") ? stdout.slice(1) : stdout;
|
|
47411
|
+
const lines2 = text.split(/\r?\n/);
|
|
47412
|
+
if (lines2[0] !== NON_CODE_RESULT_OPEN) {
|
|
47413
|
+
throw new Error("missing MRRLIN_NON_CODE_RESULT_V1 opening delimiter on the first non-BOM line");
|
|
47414
|
+
}
|
|
47415
|
+
if (!lines2[1] || lines2[2] !== NON_CODE_RESULT_CLOSE) {
|
|
47416
|
+
throw new Error("malformed MRRLIN_NON_CODE_RESULT_V1 envelope");
|
|
47417
|
+
}
|
|
47418
|
+
let payload;
|
|
47419
|
+
try {
|
|
47420
|
+
payload = JSON.parse(lines2[1]);
|
|
47421
|
+
} catch (error51) {
|
|
47422
|
+
throw new Error(`invalid MRRLIN_NON_CODE_RESULT_V1 JSON: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
47423
|
+
}
|
|
47424
|
+
const record2 = payload;
|
|
47425
|
+
const outcome = mrrlinNonCodeOutcomeSchema.parse(record2.outcome);
|
|
47426
|
+
if (typeof record2.resultSummary !== "string") throw new Error("resultSummary must be a string");
|
|
47427
|
+
const resultSummary = record2.resultSummary.trim();
|
|
47428
|
+
if (resultSummary.length < 1 || resultSummary.length > 300) {
|
|
47429
|
+
throw new Error("resultSummary must be 1-300 characters");
|
|
47430
|
+
}
|
|
47431
|
+
const bodyLines = lines2.slice(3);
|
|
47432
|
+
if (bodyLines[0] === "") bodyLines.shift();
|
|
47433
|
+
const markdownBody = bodyLines.join("\n").trim();
|
|
47434
|
+
if (markdownBody.length === 0) {
|
|
47435
|
+
throw new Error("markdown deliverable body must be non-empty");
|
|
47436
|
+
}
|
|
47437
|
+
return { outcome, resultSummary, markdownBody };
|
|
47438
|
+
}
|
|
47348
47439
|
function buildDeliverableAuthorPrompt(task, specMarkdown) {
|
|
47349
47440
|
return [
|
|
47350
47441
|
`Produce the deliverable for the task below. Write the FULL deliverable content as markdown.`,
|
|
@@ -47361,8 +47452,21 @@ function buildDeliverableAuthorPrompt(task, specMarkdown) {
|
|
|
47361
47452
|
specMarkdown,
|
|
47362
47453
|
``,
|
|
47363
47454
|
`--- OUTPUT CONTRACT (strict) ---`,
|
|
47364
|
-
`Return ONLY
|
|
47365
|
-
|
|
47455
|
+
`Return ONLY this exact envelope followed by the markdown deliverable body:`,
|
|
47456
|
+
``,
|
|
47457
|
+
NON_CODE_RESULT_OPEN,
|
|
47458
|
+
`{"outcome":"completed","resultSummary":"One concise sentence, 1-300 chars."}`,
|
|
47459
|
+
NON_CODE_RESULT_CLOSE,
|
|
47460
|
+
``,
|
|
47461
|
+
`<markdown deliverable body>`,
|
|
47462
|
+
``,
|
|
47463
|
+
`Choose exactly one outcome:`,
|
|
47464
|
+
`- completed: the requested positive result was achieved.`,
|
|
47465
|
+
`- blocked_missing_input: required input/evidence is unavailable, so the work remains actionable.`,
|
|
47466
|
+
`- negative_result: the work was actually attempted and valid evidence shows the requested positive result did not materialize.`,
|
|
47467
|
+
``,
|
|
47468
|
+
`The markdown body must justify the declared outcome and satisfy every applicable item in the spec's`,
|
|
47469
|
+
`"## Acceptance criteria" checklist. Do not add commentary before the opening delimiter.`
|
|
47366
47470
|
].join("\n");
|
|
47367
47471
|
}
|
|
47368
47472
|
async function writeDeliverablePage(client, projectSlug, task, markdown) {
|
|
@@ -47399,6 +47503,50 @@ async function writeDeliverablePage(client, projectSlug, task, markdown) {
|
|
|
47399
47503
|
}
|
|
47400
47504
|
throw new Error(`could not allocate a deliverable page id for ${task.id} (last conflict: ${lastConflict})`);
|
|
47401
47505
|
}
|
|
47506
|
+
async function writeNegativeResultEvidencePage(client, projectSlug, task, input) {
|
|
47507
|
+
const evidencePath = `/evidence/${task.id.toLowerCase()}-non-code-result`;
|
|
47508
|
+
const title = `${NEGATIVE_RESULT_EVIDENCE_TITLE_PREFIX}${task.id}`;
|
|
47509
|
+
const markdownBody = [
|
|
47510
|
+
`# ${title}`,
|
|
47511
|
+
``,
|
|
47512
|
+
`Outcome: negative_result`,
|
|
47513
|
+
``,
|
|
47514
|
+
`Summary: ${input.resultSummary}`,
|
|
47515
|
+
``,
|
|
47516
|
+
`Deliverable: Deliverables/${input.deliverablePageId}`
|
|
47517
|
+
].join("\n");
|
|
47518
|
+
const existing = (await client.listWikiPages(projectSlug, { folder: NEGATIVE_RESULT_EVIDENCE_FOLDER, taskId: task.id })).find((page) => page.path === evidencePath);
|
|
47519
|
+
if (existing) {
|
|
47520
|
+
await client.updateWikiPage(projectSlug, existing.id, {
|
|
47521
|
+
markdownBody,
|
|
47522
|
+
taskId: task.id,
|
|
47523
|
+
title
|
|
47524
|
+
});
|
|
47525
|
+
return;
|
|
47526
|
+
}
|
|
47527
|
+
let lastConflict = "";
|
|
47528
|
+
for (let attempt = 1; attempt <= DELIVERABLE_PAGE_ID_MAX_ATTEMPTS; attempt += 1) {
|
|
47529
|
+
const candidate = nextWikiPageId(await client.listWikiPages(projectSlug));
|
|
47530
|
+
try {
|
|
47531
|
+
await client.createWikiPage(projectSlug, {
|
|
47532
|
+
id: candidate,
|
|
47533
|
+
folder: NEGATIVE_RESULT_EVIDENCE_FOLDER,
|
|
47534
|
+
path: evidencePath,
|
|
47535
|
+
title,
|
|
47536
|
+
markdownBody,
|
|
47537
|
+
taskId: task.id
|
|
47538
|
+
});
|
|
47539
|
+
return;
|
|
47540
|
+
} catch (error51) {
|
|
47541
|
+
if (error51 instanceof MrrlinApiError && error51.status === 409 && attempt < DELIVERABLE_PAGE_ID_MAX_ATTEMPTS) {
|
|
47542
|
+
lastConflict = error51.message;
|
|
47543
|
+
continue;
|
|
47544
|
+
}
|
|
47545
|
+
throw error51;
|
|
47546
|
+
}
|
|
47547
|
+
}
|
|
47548
|
+
throw new Error(`could not allocate negative-result evidence page for ${task.id} (last conflict: ${lastConflict})`);
|
|
47549
|
+
}
|
|
47402
47550
|
async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2) {
|
|
47403
47551
|
const personaName = resolveProducerPersona(domain2);
|
|
47404
47552
|
if (!personaName) {
|
|
@@ -47458,7 +47606,7 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47458
47606
|
});
|
|
47459
47607
|
}, deps.touchIntervalMs);
|
|
47460
47608
|
touchTimer.unref?.();
|
|
47461
|
-
let
|
|
47609
|
+
let parsedResult;
|
|
47462
47610
|
try {
|
|
47463
47611
|
const exec = await runExec({
|
|
47464
47612
|
prompt: buildDeliverableAuthorPrompt(task, specMarkdown),
|
|
@@ -47472,18 +47620,26 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47472
47620
|
await failRun(deps, projectSlug, task, runId, leaseId, `deliverable_authoring_failed: ${exec.error}`);
|
|
47473
47621
|
return;
|
|
47474
47622
|
}
|
|
47475
|
-
|
|
47476
|
-
|
|
47623
|
+
try {
|
|
47624
|
+
parsedResult = parseNonCodeResultEnvelope(exec.text);
|
|
47625
|
+
} catch (error51) {
|
|
47626
|
+
await failRun(
|
|
47627
|
+
deps,
|
|
47628
|
+
projectSlug,
|
|
47629
|
+
task,
|
|
47630
|
+
runId,
|
|
47631
|
+
leaseId,
|
|
47632
|
+
`non_code_result_envelope_invalid: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
47633
|
+
);
|
|
47477
47634
|
return;
|
|
47478
47635
|
}
|
|
47479
|
-
deliverableMarkdown = exec.text.trim();
|
|
47480
47636
|
} finally {
|
|
47481
47637
|
clearInterval(touchTimer);
|
|
47482
47638
|
}
|
|
47483
47639
|
let pageId;
|
|
47484
47640
|
let contentHash;
|
|
47485
47641
|
try {
|
|
47486
|
-
({ pageId, contentHash } = await writeDeliverablePage(deps.client, projectSlug, task,
|
|
47642
|
+
({ pageId, contentHash } = await writeDeliverablePage(deps.client, projectSlug, task, parsedResult.markdownBody));
|
|
47487
47643
|
} catch (error51) {
|
|
47488
47644
|
await failRun(
|
|
47489
47645
|
deps,
|
|
@@ -47514,7 +47670,13 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47514
47670
|
}
|
|
47515
47671
|
let gateResult;
|
|
47516
47672
|
try {
|
|
47517
|
-
gateResult = await deps.runDeliverableGate({
|
|
47673
|
+
gateResult = await deps.runDeliverableGate({
|
|
47674
|
+
projectSlug,
|
|
47675
|
+
taskId: task.id,
|
|
47676
|
+
runId,
|
|
47677
|
+
deliverablePageId: pageId,
|
|
47678
|
+
declaredOutcome: parsedResult.outcome
|
|
47679
|
+
});
|
|
47518
47680
|
} catch (error51) {
|
|
47519
47681
|
await failRun(
|
|
47520
47682
|
deps,
|
|
@@ -47538,14 +47700,52 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47538
47700
|
);
|
|
47539
47701
|
return;
|
|
47540
47702
|
}
|
|
47541
|
-
if (
|
|
47542
|
-
|
|
47543
|
-
|
|
47544
|
-
|
|
47545
|
-
|
|
47546
|
-
|
|
47547
|
-
|
|
47548
|
-
|
|
47703
|
+
if (parsedResult.outcome === "negative_result") {
|
|
47704
|
+
try {
|
|
47705
|
+
await writeNegativeResultEvidencePage(deps.client, projectSlug, task, {
|
|
47706
|
+
deliverablePageId: pageId,
|
|
47707
|
+
resultSummary: parsedResult.resultSummary
|
|
47708
|
+
});
|
|
47709
|
+
} catch (error51) {
|
|
47710
|
+
await failRun(
|
|
47711
|
+
deps,
|
|
47712
|
+
projectSlug,
|
|
47713
|
+
task,
|
|
47714
|
+
runId,
|
|
47715
|
+
leaseId,
|
|
47716
|
+
`negative_result_evidence_write_failed: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
47717
|
+
);
|
|
47718
|
+
return;
|
|
47719
|
+
}
|
|
47720
|
+
}
|
|
47721
|
+
try {
|
|
47722
|
+
const settled = await deps.client.settleExecutionRunOutcome(projectSlug, runId, {
|
|
47723
|
+
leaseId,
|
|
47724
|
+
outcome: parsedResult.outcome,
|
|
47725
|
+
resultSummary: parsedResult.resultSummary,
|
|
47726
|
+
deliverablePageId: pageId,
|
|
47727
|
+
contentHash
|
|
47728
|
+
});
|
|
47729
|
+
if (!settled && await runHasLease(deps, projectSlug, runId, leaseId)) {
|
|
47730
|
+
await failRun(
|
|
47731
|
+
deps,
|
|
47732
|
+
projectSlug,
|
|
47733
|
+
task,
|
|
47734
|
+
runId,
|
|
47735
|
+
leaseId,
|
|
47736
|
+
`non_code_outcome_settlement_conflict: settlement was rejected while this consumer still held the lease.`
|
|
47737
|
+
);
|
|
47738
|
+
}
|
|
47739
|
+
} catch (error51) {
|
|
47740
|
+
await failRun(
|
|
47741
|
+
deps,
|
|
47742
|
+
projectSlug,
|
|
47743
|
+
task,
|
|
47744
|
+
runId,
|
|
47745
|
+
leaseId,
|
|
47746
|
+
`non_code_outcome_settlement_failed: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
47747
|
+
);
|
|
47748
|
+
}
|
|
47549
47749
|
}
|
|
47550
47750
|
async function resetWedgedThread(deps, projectSlug, taskId, interruptFailed) {
|
|
47551
47751
|
if (!interruptFailed) return;
|
|
@@ -48057,6 +48257,22 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48057
48257
|
}
|
|
48058
48258
|
const includePrompts = resolvePromptsEnabled(env);
|
|
48059
48259
|
const fileFor = () => (0, import_node_path16.join)(logsDir, `bridge-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`);
|
|
48260
|
+
const MAX_TRACKED_SESSIONS = 64;
|
|
48261
|
+
const contextSlugBySession = /* @__PURE__ */ new Map();
|
|
48262
|
+
const rememberContextSlug = (sessionId, payload) => {
|
|
48263
|
+
if (!payload || typeof payload !== "object") return;
|
|
48264
|
+
const ctx = payload.context;
|
|
48265
|
+
if (!ctx || typeof ctx !== "object") return;
|
|
48266
|
+
const raw = ctx.projectSlug;
|
|
48267
|
+
const slug = typeof raw === "string" ? raw.trim() : "";
|
|
48268
|
+
if (!slug) return;
|
|
48269
|
+
contextSlugBySession.delete(sessionId);
|
|
48270
|
+
contextSlugBySession.set(sessionId, slug);
|
|
48271
|
+
if (contextSlugBySession.size > MAX_TRACKED_SESSIONS) {
|
|
48272
|
+
const oldest = contextSlugBySession.keys().next();
|
|
48273
|
+
if (!oldest.done) contextSlugBySession.delete(oldest.value);
|
|
48274
|
+
}
|
|
48275
|
+
};
|
|
48060
48276
|
const gainDay = (sessionId) => {
|
|
48061
48277
|
const sessionsPath = (0, import_node_path16.join)(logsDir, ".sessions.json");
|
|
48062
48278
|
let sessionsObj = {};
|
|
@@ -48086,6 +48302,23 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48086
48302
|
projectionFailed = true;
|
|
48087
48303
|
}
|
|
48088
48304
|
}
|
|
48305
|
+
if (rec.sessionId !== null && rec.type === "turn") rememberContextSlug(rec.sessionId, rec.payload);
|
|
48306
|
+
let projectScopeMismatch = null;
|
|
48307
|
+
if (rec.sessionId !== null && projection?.kind === "tool_use" && typeof projection.projectSlug === "string") {
|
|
48308
|
+
const expected = contextSlugBySession.get(rec.sessionId);
|
|
48309
|
+
if (expected !== void 0 && expected !== projection.projectSlug) {
|
|
48310
|
+
projectScopeMismatch = { expected, actual: projection.projectSlug };
|
|
48311
|
+
console.warn(
|
|
48312
|
+
JSON.stringify({
|
|
48313
|
+
event: "bridge.project_scope_mismatch",
|
|
48314
|
+
sessionId: rec.sessionId,
|
|
48315
|
+
toolName: projection.toolName,
|
|
48316
|
+
expected,
|
|
48317
|
+
actual: projection.projectSlug
|
|
48318
|
+
})
|
|
48319
|
+
);
|
|
48320
|
+
}
|
|
48321
|
+
}
|
|
48089
48322
|
const { payload: redactedPayload, redactedCount } = redactMessage(rec.payload, { includePrompts });
|
|
48090
48323
|
const line = JSON.stringify({
|
|
48091
48324
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -48097,7 +48330,8 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48097
48330
|
payload: redactedPayload,
|
|
48098
48331
|
redactedCount,
|
|
48099
48332
|
projection,
|
|
48100
|
-
...projectionFailed ? { projectionFailed: true } : {}
|
|
48333
|
+
...projectionFailed ? { projectionFailed: true } : {},
|
|
48334
|
+
...projectScopeMismatch ? { projectScopeMismatch } : {}
|
|
48101
48335
|
}) + "\n";
|
|
48102
48336
|
(0, import_node_fs8.appendFileSync)(fileFor(), line);
|
|
48103
48337
|
if (rec.sessionId !== null) gainDay(rec.sessionId);
|
|
@@ -48294,7 +48528,7 @@ async function runAsyncBackstopSweep(deps, drive = (slug, jobId) => driveAsyncJo
|
|
|
48294
48528
|
}
|
|
48295
48529
|
|
|
48296
48530
|
// src/consensus/deliverable-gate.ts
|
|
48297
|
-
async function runDeliverableGate(
|
|
48531
|
+
async function runDeliverableGate(target, deps) {
|
|
48298
48532
|
if (!await deps.consensusEnabled()) return { outcome: "SKIPPED" };
|
|
48299
48533
|
const ac = await deps.readAcceptanceCriteria();
|
|
48300
48534
|
if (!ac || ac.criteria.length === 0) return { outcome: "MISSING_ACCEPTANCE_CRITERIA" };
|
|
@@ -48342,6 +48576,7 @@ async function runDeliverableGate(_target, deps) {
|
|
|
48342
48576
|
}
|
|
48343
48577
|
await deps.writeEvidence({
|
|
48344
48578
|
deliverablePageId: deliverable.pageId,
|
|
48579
|
+
...target.declaredOutcome ? { declaredOutcome: target.declaredOutcome } : {},
|
|
48345
48580
|
outcome: result.outcome,
|
|
48346
48581
|
rounds: result.rounds,
|
|
48347
48582
|
history: result.history,
|
|
@@ -53773,6 +54008,18 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
53773
54008
|
"order, separated by a blank line. Do NOT paraphrase, summarise, or rewrite any of it, and do",
|
|
53774
54009
|
"NOT add words of your own.",
|
|
53775
54010
|
"",
|
|
54011
|
+
"Set resolvedIntent to the accepted task intent in your own concise words only after the outcome",
|
|
54012
|
+
"is resolved enough to create the task. Use it to capture the actionable understanding that came",
|
|
54013
|
+
"from the conversation and, when needed, retrieved wiki context. Do NOT use resolvedIntent as a",
|
|
54014
|
+
"replacement for operatorSeedRequest: the seed stays raw operator wording; resolvedIntent is the",
|
|
54015
|
+
"structured interpretation.",
|
|
54016
|
+
"",
|
|
54017
|
+
"When the turn Context JSON includes contextMessages, cite the messages you actually used in",
|
|
54018
|
+
'intentProvenance as refs like {kind:"director_chat_message",id:"...",role:"user"}. If a wiki',
|
|
54019
|
+
'page resolved part of the intent, cite it as {kind:"wiki_page",id:"WK-..."}. Do not put copied',
|
|
54020
|
+
"message text in intentProvenance. Leave it unset when there is no accepted resolved intent; use",
|
|
54021
|
+
"an explicit empty array only when you intentionally verified there are no useful references.",
|
|
54022
|
+
"",
|
|
53776
54023
|
"If a task has no real operator request behind it at all (pure system bookkeeping nobody asked",
|
|
53777
54024
|
"for), do NOT invent one to fill this field \u2014 leave operatorSeedRequest unset. Never fabricate",
|
|
53778
54025
|
"operator wording; an unset seed is handled downstream, a fabricated one is not.",
|
|
@@ -53878,6 +54125,8 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
53878
54125
|
" needs no local file (class=temp for one-off downloads, class=durable when a spec/evidence",
|
|
53879
54126
|
" page references it; pass taskId when known), and paste the returned markdown link into your",
|
|
53880
54127
|
" reply. Use `upload_artifact` only for a file that already exists on disk (e.g. a screenshot).",
|
|
54128
|
+
"- An HTML file must be self-contained \u2014 inline `<style>`, `data:` URIs, no external resources and",
|
|
54129
|
+
" no scripts. The viewer sandboxes it and blocks outbound requests, so linked assets never load.",
|
|
53881
54130
|
"- The chat reply must still stand alone as a complete answer even if the file is never opened:",
|
|
53882
54131
|
" the headline finding, the few points that matter, then the link. Never reduce it to",
|
|
53883
54132
|
' "see attached" or "details in the report".',
|
|
@@ -55761,16 +56010,17 @@ async function startDirectorBridge() {
|
|
|
55761
56010
|
// Builds the gate deps (resolving domain/heads/criteria from the page's task + spec) and runs the
|
|
55762
56011
|
// same convergence loop the spec-gate uses. runId is threaded for verdict provenance (decision 10).
|
|
55763
56012
|
// Authoring uses the defaults (runAgentExec + loadPersona) — no extra wiring needed for those.
|
|
55764
|
-
runDeliverableGate: async ({ projectSlug, runId, deliverablePageId }) => {
|
|
56013
|
+
runDeliverableGate: async ({ projectSlug, runId, deliverablePageId, declaredOutcome }) => {
|
|
55765
56014
|
const gateDeps = await buildDeliverableGateDeps({
|
|
55766
56015
|
client,
|
|
55767
56016
|
projectSlug,
|
|
55768
56017
|
deliverablePageId,
|
|
56018
|
+
declaredOutcome,
|
|
55769
56019
|
cwd: config2.codexCwd ?? process.cwd(),
|
|
55770
56020
|
codexExecutable: config2.codexExecutable,
|
|
55771
56021
|
runId
|
|
55772
56022
|
});
|
|
55773
|
-
return runDeliverableGate({ projectSlug, deliverablePageId }, gateDeps);
|
|
56023
|
+
return runDeliverableGate({ projectSlug, deliverablePageId, declaredOutcome }, gateDeps);
|
|
55774
56024
|
}
|
|
55775
56025
|
};
|
|
55776
56026
|
const sessionSinks = createSessionSinkRegistry();
|
|
@@ -56538,12 +56788,16 @@ function renderContextPrefix(context) {
|
|
|
56538
56788
|
const urlPath = context?.urlPath?.trim() ? context.urlPath.trim() : void 0;
|
|
56539
56789
|
const focusTaskId = context?.focusTaskId?.trim() ? context.focusTaskId.trim() : void 0;
|
|
56540
56790
|
const directorSessionId = context?.directorSessionId?.trim() ? context.directorSessionId.trim() : void 0;
|
|
56791
|
+
const contextMessages = context?.contextMessages?.filter(
|
|
56792
|
+
(message) => (message.role === "user" || message.role === "assistant") && typeof message.id === "string" && message.id.trim() && typeof message.content === "string" && message.content.length > 0
|
|
56793
|
+
);
|
|
56541
56794
|
const answeredQuestions = context?.answeredQuestions?.length ? context.answeredQuestions : void 0;
|
|
56542
56795
|
const payload = {};
|
|
56543
56796
|
if (projectSlug) payload.projectSlug = projectSlug;
|
|
56544
56797
|
if (urlPath) payload.urlPath = urlPath;
|
|
56545
56798
|
if (focusTaskId) payload.focusTaskId = focusTaskId;
|
|
56546
56799
|
if (directorSessionId) payload.directorSessionId = directorSessionId;
|
|
56800
|
+
if (contextMessages?.length) payload.contextMessages = contextMessages;
|
|
56547
56801
|
if (answeredQuestions) payload.answeredQuestions = answeredQuestions;
|
|
56548
56802
|
const keys = Object.keys(payload);
|
|
56549
56803
|
if (keys.length === 0) return "";
|
|
@@ -62861,6 +63115,31 @@ function makeReadTool(client, name, fallbackCode, fallbackMessage, handler, proj
|
|
|
62861
63115
|
}
|
|
62862
63116
|
};
|
|
62863
63117
|
}
|
|
63118
|
+
var agentProviderCache = null;
|
|
63119
|
+
var AGENT_PROVIDER_CACHE_TTL_MS = 3e4;
|
|
63120
|
+
async function getAgentProviderForPanel(client, opts = {}) {
|
|
63121
|
+
const now = (opts.now ?? (() => Date.now()))();
|
|
63122
|
+
if (agentProviderCache !== null && now - agentProviderCache.fetchedAt < AGENT_PROVIDER_CACHE_TTL_MS) {
|
|
63123
|
+
return agentProviderCache.value;
|
|
63124
|
+
}
|
|
63125
|
+
try {
|
|
63126
|
+
const settings = await client.getOperatorSettings();
|
|
63127
|
+
agentProviderCache = { value: settings.agentProvider, fetchedAt: now };
|
|
63128
|
+
return settings.agentProvider;
|
|
63129
|
+
} catch (err) {
|
|
63130
|
+
console.warn(
|
|
63131
|
+
JSON.stringify({
|
|
63132
|
+
event: "agent_provider_for_panel_fetch_failed",
|
|
63133
|
+
hadCache: agentProviderCache !== null,
|
|
63134
|
+
error: err instanceof Error ? err.message : String(err)
|
|
63135
|
+
})
|
|
63136
|
+
);
|
|
63137
|
+
if (agentProviderCache !== null) {
|
|
63138
|
+
return agentProviderCache.value;
|
|
63139
|
+
}
|
|
63140
|
+
return null;
|
|
63141
|
+
}
|
|
63142
|
+
}
|
|
62864
63143
|
function createMrrlinTools(options) {
|
|
62865
63144
|
const client = createMrrlinClient(withAuth(options));
|
|
62866
63145
|
async function uploadArtifactBytes(c, args) {
|
|
@@ -63327,13 +63606,17 @@ function createMrrlinTools(options) {
|
|
|
63327
63606
|
codexExecutable: process.env.MRRLIN_DIRECTOR_BRIDGE_CODEX_BIN?.trim() || void 0,
|
|
63328
63607
|
extraHeads
|
|
63329
63608
|
});
|
|
63330
|
-
const
|
|
63331
|
-
|
|
63332
|
-
|
|
63333
|
-
|
|
63609
|
+
const persistedProvider = await getAgentProviderForPanel(c);
|
|
63610
|
+
let activeCodeRunHead = null;
|
|
63611
|
+
if (persistedProvider != null) {
|
|
63612
|
+
activeCodeRunHead = await resolveActiveRunHead({
|
|
63613
|
+
heads: ["code-reviewer", ...extraHeads ?? []],
|
|
63614
|
+
persistedProvider
|
|
63615
|
+
});
|
|
63616
|
+
}
|
|
63334
63617
|
if (activeCodeRunHead) deps.runHead = activeCodeRunHead;
|
|
63335
63618
|
const codeGateResult = await runCodeGate({ projectSlug, baseRef }, deps);
|
|
63336
|
-
if (process.env[SHADOW_ENV_FLAG]) {
|
|
63619
|
+
if (process.env[SHADOW_ENV_FLAG] && persistedProvider != null) {
|
|
63337
63620
|
try {
|
|
63338
63621
|
const diff = await readDiff({ cwd: process.cwd(), baseRef, run: defaultRunCmd });
|
|
63339
63622
|
await maybeRunShadow({
|
|
@@ -63341,7 +63624,7 @@ function createMrrlinTools(options) {
|
|
|
63341
63624
|
artifact: diff,
|
|
63342
63625
|
heads: ["code-reviewer", ...extraHeads ?? []],
|
|
63343
63626
|
correlationId: `code:${baseRef}`,
|
|
63344
|
-
persistedProvider
|
|
63627
|
+
persistedProvider
|
|
63345
63628
|
});
|
|
63346
63629
|
} catch {
|
|
63347
63630
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"@types/ws": "^8.18.1",
|
|
22
22
|
"esbuild": "^0.24.0",
|
|
23
23
|
"tsx": "^4.22.3",
|
|
24
|
+
"@mrrlin/client": "0.0.0",
|
|
24
25
|
"@mrrlin/agent-client": "0.0.0",
|
|
25
26
|
"@mrrlin/director-e2e": "0.0.0",
|
|
26
|
-
"@mrrlin/client": "0.0.0",
|
|
27
27
|
"@mrrlin/schemas": "0.0.0",
|
|
28
28
|
"@mrrlin/tsconfig": "0.0.0",
|
|
29
29
|
"@mrrlin/wiki": "0.0.0"
|