@mrrlin-dev/mcp 0.3.54 → 0.3.55
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 +226 -107
- package/package.json +5 -5
package/dist/bin.cjs
CHANGED
|
@@ -36506,6 +36506,8 @@ var openApiOperationIds = {
|
|
|
36506
36506
|
getProject: "getProject",
|
|
36507
36507
|
getProjectSnapshot: "getProjectSnapshot",
|
|
36508
36508
|
getTask: "getTask",
|
|
36509
|
+
archiveTask: "archiveTask",
|
|
36510
|
+
unarchiveTask: "unarchiveTask",
|
|
36509
36511
|
getWikiPage: "getWikiPage",
|
|
36510
36512
|
handleGithubInstallCallback: "handleGithubInstallCallback",
|
|
36511
36513
|
listExecutionArtifacts: "listExecutionArtifacts",
|
|
@@ -36556,6 +36558,7 @@ var openApiOperationIds = {
|
|
|
36556
36558
|
listArtifactFiles: "listArtifactFiles",
|
|
36557
36559
|
getOperatorSettings: "getOperatorSettings",
|
|
36558
36560
|
updateOperatorSettings: "updateOperatorSettings",
|
|
36561
|
+
recordOperatorPresence: "recordOperatorPresence",
|
|
36559
36562
|
getAgentPool: "getAgentPool",
|
|
36560
36563
|
putAgentPool: "putAgentPool",
|
|
36561
36564
|
getConsensusConfig: "getConsensusConfig",
|
|
@@ -36600,6 +36603,8 @@ var mrrlinOperatorSettingsSchema = external_exports.object({
|
|
|
36600
36603
|
// to it (the web active-project selection is otherwise only a browser cookie). Null = not
|
|
36601
36604
|
// selected yet.
|
|
36602
36605
|
activeProjectSlug: external_exports.string().min(1).nullable(),
|
|
36606
|
+
// The last time an authenticated BROWSER operator session was observed. Null means never seen.
|
|
36607
|
+
lastPresenceAt: external_exports.string().datetime().nullable(),
|
|
36603
36608
|
createdAt: external_exports.string().datetime(),
|
|
36604
36609
|
updatedAt: external_exports.string().datetime()
|
|
36605
36610
|
});
|
|
@@ -36611,6 +36616,7 @@ var mrrlinOperatorSettingsUpdateSchema = external_exports.object({
|
|
|
36611
36616
|
message: "OperatorSettingsUpdate requires at least one field"
|
|
36612
36617
|
});
|
|
36613
36618
|
var operatorSettingsResponseSchema = external_exports.object({ data: mrrlinOperatorSettingsSchema });
|
|
36619
|
+
var operatorPresenceResponseSchema = external_exports.object({ data: external_exports.object({ lastPresenceAt: external_exports.string().datetime() }) });
|
|
36614
36620
|
var mrrlinConsensusConfigSchema = external_exports.object({ consensusEnabled: external_exports.boolean() });
|
|
36615
36621
|
var consensusConfigResponseSchema = external_exports.object({ data: mrrlinConsensusConfigSchema });
|
|
36616
36622
|
var agentPoolSnapshotSchema = external_exports.object({
|
|
@@ -36748,7 +36754,13 @@ var mrrlinTaskDomainSchema = external_exports.enum(["code", "seo", "marketing",
|
|
|
36748
36754
|
var mrrlinSpecApprovedSchema = external_exports.enum(["pending", "approved", "skipped", "skipped_legacy"]);
|
|
36749
36755
|
var mrrlinTaskSourceSchema = external_exports.enum(["fixture-import", "api"]);
|
|
36750
36756
|
var mrrlinPlanStatusSchema = external_exports.enum(["active", "paused", "done", "archived"]);
|
|
36751
|
-
var mrrlinTaskEventTypeSchema = external_exports.enum([
|
|
36757
|
+
var mrrlinTaskEventTypeSchema = external_exports.enum([
|
|
36758
|
+
"autonomy_set",
|
|
36759
|
+
"director_thread_reset",
|
|
36760
|
+
"note",
|
|
36761
|
+
"archived",
|
|
36762
|
+
"restored"
|
|
36763
|
+
]);
|
|
36752
36764
|
var mrrlinTaskEventCreateKindSchema = external_exports.enum(["note"]);
|
|
36753
36765
|
var githubRepositorySelectionSchema = external_exports.enum(["all", "selected"]);
|
|
36754
36766
|
var mrrlinTaskIdSchema = external_exports.string().regex(/^GT-\d+$/);
|
|
@@ -36862,6 +36874,9 @@ var taxonomyCategorySchema = external_exports.string().min(1).regex(/^[a-z][a-z0
|
|
|
36862
36874
|
var taxonomySubcategorySchema = external_exports.string().min(1).regex(/^[a-z][a-z0-9-]*$/);
|
|
36863
36875
|
var mrrlinTaskAssigneeSchema = external_exports.union([external_exports.string().regex(/^@/), external_exports.string().email()]).nullable();
|
|
36864
36876
|
var mrrlinTaskSchema = external_exports.object({
|
|
36877
|
+
// Output-optional (not defaulted-required) so existing task fixtures/mocks across the
|
|
36878
|
+
// codebase need no churn; DB rows always resolve it to `string | null` via taskRowToTask.
|
|
36879
|
+
archivedAt: external_exports.string().datetime().nullable().optional(),
|
|
36865
36880
|
assignee: mrrlinTaskAssigneeSchema,
|
|
36866
36881
|
autoDeploy: mrrlinAutoDeploySchema,
|
|
36867
36882
|
// ADR 0020. OUTPUT-optional (not defaulted-required) so hardcoded task fixtures/mocks across the
|
|
@@ -37109,6 +37124,7 @@ var mrrlinTaskUpdateSchema = external_exports.object({
|
|
|
37109
37124
|
}
|
|
37110
37125
|
});
|
|
37111
37126
|
var mrrlinTaskListFiltersSchema = external_exports.object({
|
|
37127
|
+
archived: external_exports.enum(["true", "false", "all"]).optional(),
|
|
37112
37128
|
assignee: external_exports.string().startsWith("@").optional(),
|
|
37113
37129
|
autoDeploy: mrrlinAutoDeploySchema.optional(),
|
|
37114
37130
|
autonomyLevel: mrrlinAutonomyLevelSchema.optional(),
|
|
@@ -37829,7 +37845,11 @@ var schedulerPendingTasksResponseSchema = external_exports.object({
|
|
|
37829
37845
|
// disclosure that some exist, so a misconfigured `github.operator` (e.g. it doesn't match the
|
|
37830
37846
|
// person actually running the project) is visible as a large number instead of the autopilot
|
|
37831
37847
|
// silently doing nothing.
|
|
37832
|
-
excludedByOwnershipCount: external_exports.number().int().min(0)
|
|
37848
|
+
excludedByOwnershipCount: external_exports.number().int().min(0),
|
|
37849
|
+
// Additive: true when the operator-web-presence gate is the reason nothing new is starting
|
|
37850
|
+
// (autonomyPaused takes precedence when both apply). Computed by the same shared helper the
|
|
37851
|
+
// tick uses, so this page can never claim a task is starting when the tick would decline it.
|
|
37852
|
+
presenceGated: external_exports.boolean()
|
|
37833
37853
|
})
|
|
37834
37854
|
});
|
|
37835
37855
|
var wakeDirectorRequestSchema = external_exports.object({
|
|
@@ -38386,6 +38406,7 @@ function createMrrlinClient(config2) {
|
|
|
38386
38406
|
},
|
|
38387
38407
|
async listTasks(projectSlug, filters = {}) {
|
|
38388
38408
|
const path19 = appendQuery(`${projectPath(projectSlug)}/tasks`, {
|
|
38409
|
+
archived: filters.archived,
|
|
38389
38410
|
assignee: filters.assignee,
|
|
38390
38411
|
autonomyLevel: filters.autonomyLevel,
|
|
38391
38412
|
blockedBy: filters.blockedBy,
|
|
@@ -38443,6 +38464,18 @@ function createMrrlinClient(config2) {
|
|
|
38443
38464
|
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/director-thread/reset`, { method: "POST" });
|
|
38444
38465
|
return taskResponseSchema.parse(body).data;
|
|
38445
38466
|
},
|
|
38467
|
+
async archiveTask(projectSlug, taskId) {
|
|
38468
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/archive`, {
|
|
38469
|
+
method: "POST"
|
|
38470
|
+
});
|
|
38471
|
+
return taskResponseSchema.parse(body).data;
|
|
38472
|
+
},
|
|
38473
|
+
async unarchiveTask(projectSlug, taskId) {
|
|
38474
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/unarchive`, {
|
|
38475
|
+
method: "POST"
|
|
38476
|
+
});
|
|
38477
|
+
return taskResponseSchema.parse(body).data;
|
|
38478
|
+
},
|
|
38446
38479
|
async updateWikiPage(projectSlug, pageId, patch) {
|
|
38447
38480
|
const body = await request(`${projectPath(projectSlug)}/wiki/${encodeURIComponent(pageId)}`, {
|
|
38448
38481
|
body: JSON.stringify(patch),
|
|
@@ -38808,6 +38841,10 @@ function createMrrlinClient(config2) {
|
|
|
38808
38841
|
});
|
|
38809
38842
|
return operatorSettingsResponseSchema.parse(body).data;
|
|
38810
38843
|
},
|
|
38844
|
+
async recordOperatorPresence() {
|
|
38845
|
+
const body = await request(`/operator/presence`, { method: "POST" });
|
|
38846
|
+
return operatorPresenceResponseSchema.parse(body).data;
|
|
38847
|
+
},
|
|
38811
38848
|
async getOperatorAgentPool() {
|
|
38812
38849
|
const body = await request(`/operator/agent-pool`);
|
|
38813
38850
|
return agentPoolResponseSchema.parse(body).data;
|
|
@@ -39322,7 +39359,7 @@ function startCodexProbe(deps = {}) {
|
|
|
39322
39359
|
}
|
|
39323
39360
|
|
|
39324
39361
|
// src/_generated/version.ts
|
|
39325
|
-
var PKG_VERSION = "0.3.
|
|
39362
|
+
var PKG_VERSION = "0.3.55";
|
|
39326
39363
|
|
|
39327
39364
|
// src/api-base-url-migrate.ts
|
|
39328
39365
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
@@ -48772,6 +48809,21 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
48772
48809
|
"transcript. Just call the tools and reply with the outcome \u2014 start your reply with the result.",
|
|
48773
48810
|
"When creating or updating tasks, always provide `autonomyLevel` and `autonomyReason`.",
|
|
48774
48811
|
"",
|
|
48812
|
+
"Tool-use contract \u2014 typed MCP tools are the ONLY channel for Mrrlin data:",
|
|
48813
|
+
"- Every read or write of Mrrlin data \u2014 tasks, plans, wiki pages, inbox items, execution runs,",
|
|
48814
|
+
" projects \u2014 must go through the corresponding typed MCP tool. Never use `commandExecution`,",
|
|
48815
|
+
" `curl`, `python`, or any direct HTTP call to the Mrrlin API.",
|
|
48816
|
+
"- A single `create_task` call creates a task completely; it accepts `title`, `notes`, `category`,",
|
|
48817
|
+
" `subcategory`, `impact`, `autonomyLevel`, `planId`, `parentTaskId`, and more in one call.",
|
|
48818
|
+
" Do not create then patch, and do not re-read the task afterwards unless the operator asked",
|
|
48819
|
+
" for confirmation.",
|
|
48820
|
+
"- `commandExecution` is for the operator's own machine and repositories \u2014 running builds, tests,",
|
|
48821
|
+
" `git`, inspecting local files \u2014 and for public third-party web checks. It is not a transport",
|
|
48822
|
+
" for this product's API.",
|
|
48823
|
+
"- If no typed tool exists for a needed operation, state that to the operator instead of reaching",
|
|
48824
|
+
" for the API by hand.",
|
|
48825
|
+
"- Do not spend turns re-reading state you just wrote.",
|
|
48826
|
+
"",
|
|
48775
48827
|
"Turn lifecycle \u2014 what you can and cannot do between turns:",
|
|
48776
48828
|
"- Your turn ends the moment you stop producing output. You CANNOT start a new turn yourself:",
|
|
48777
48829
|
" no timer and no scheduler wakes you up. Only a new operator message starts the next turn.",
|
|
@@ -48845,9 +48897,10 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
48845
48897
|
' Never answer "nothing found" from plans alone.',
|
|
48846
48898
|
'- Only report "nothing found" after the q-variants AND a full-list scan are empty, and then',
|
|
48847
48899
|
" state exactly what you searched.",
|
|
48848
|
-
"- For public HTTP/HTML verification through `commandExecution`, prefer built-in
|
|
48849
|
-
" as `curl`, `python3` stdlib `urllib.request`, and `html.parser`; do not assume
|
|
48850
|
-
" Python packages such as `requests` or `bs4` are installed.",
|
|
48900
|
+
"- For public/third-party HTTP/HTML verification through `commandExecution`, prefer built-in",
|
|
48901
|
+
" tooling such as `curl`, `python3` stdlib `urllib.request`, and `html.parser`; do not assume",
|
|
48902
|
+
" third-party Python packages such as `requests` or `bs4` are installed. This permission covers",
|
|
48903
|
+
" third-party sites ONLY \u2014 never call the Mrrlin API from `commandExecution`.",
|
|
48851
48904
|
"",
|
|
48852
48905
|
"Handoff contract \u2014 durable operator action MUST NOT live only in chat:",
|
|
48853
48906
|
"- If you conclude that a specific task needs human input, account access, manual evidence,",
|
|
@@ -54649,15 +54702,35 @@ function firstTaskIdToken(text) {
|
|
|
54649
54702
|
}
|
|
54650
54703
|
|
|
54651
54704
|
// src/consensus/spec-gate.ts
|
|
54705
|
+
function mapAcceptedIssueToClarification(issue2) {
|
|
54706
|
+
const text = issue2.text.toLowerCase();
|
|
54707
|
+
let kind;
|
|
54708
|
+
if (issue2.category === "scope") {
|
|
54709
|
+
const directionCues = ["only", "or also", "narrower", "broader", "include", "exclude", "versus", "vs"];
|
|
54710
|
+
kind = directionCues.some((cue) => text.includes(cue)) ? "scope_direction" : "missing_requirement";
|
|
54711
|
+
} else {
|
|
54712
|
+
const acceptanceCues = ["acceptance", "done", "success", "criteria", "satisfy"];
|
|
54713
|
+
kind = acceptanceCues.some((cue) => text.includes(cue)) ? "acceptance_criteria_detail" : "default_choice";
|
|
54714
|
+
}
|
|
54715
|
+
return {
|
|
54716
|
+
kind,
|
|
54717
|
+
diagnostic: false,
|
|
54718
|
+
rawQuestion: issue2.text,
|
|
54719
|
+
whyBlocking: `reviewer raised a ${issue2.category} issue`,
|
|
54720
|
+
category: issue2.category
|
|
54721
|
+
};
|
|
54722
|
+
}
|
|
54652
54723
|
async function runSpecGate(_target, deps, opts) {
|
|
54653
54724
|
if (!await deps.consensusEnabled()) return { outcome: "SKIPPED" };
|
|
54654
54725
|
const intent = await deps.readIntent();
|
|
54655
54726
|
if (intent.taskId !== null && (intent.seed === null || intent.seed.trim() === "")) {
|
|
54656
54727
|
return {
|
|
54657
54728
|
outcome: "CONSENSUS_BLOCKED",
|
|
54658
|
-
|
|
54729
|
+
operatorClarifications: [
|
|
54659
54730
|
{
|
|
54660
|
-
|
|
54731
|
+
kind: "missing_requirement",
|
|
54732
|
+
diagnostic: false,
|
|
54733
|
+
rawQuestion: "What are the requirements for this task? There is no operator request on record, so the spec cannot be validated against your intent \u2014 please state what this task must do.",
|
|
54661
54734
|
whyBlocking: "operator_seed_request is empty",
|
|
54662
54735
|
category: "scope"
|
|
54663
54736
|
}
|
|
@@ -54671,9 +54744,11 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
54671
54744
|
if (normalizeDomain(intent.domain) !== "code" && parseAcceptanceCriteria(spec.markdownBody) === null) {
|
|
54672
54745
|
return {
|
|
54673
54746
|
outcome: "CONSENSUS_BLOCKED",
|
|
54674
|
-
|
|
54747
|
+
operatorClarifications: [
|
|
54675
54748
|
{
|
|
54676
|
-
|
|
54749
|
+
kind: "acceptance_criteria_detail",
|
|
54750
|
+
diagnostic: false,
|
|
54751
|
+
rawQuestion: "This spec has no machine-usable acceptance criteria. Add an `## Acceptance criteria` section written as a markdown checklist (`- [ ] \u2026` items) describing exactly what the finished deliverable must satisfy \u2014 the review of the produced work is scored against it.",
|
|
54677
54752
|
whyBlocking: "non-code spec is missing a parseable `## Acceptance criteria` checklist",
|
|
54678
54753
|
category: "scope"
|
|
54679
54754
|
}
|
|
@@ -54723,9 +54798,11 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
54723
54798
|
);
|
|
54724
54799
|
return {
|
|
54725
54800
|
outcome: "CONSENSUS_BLOCKED",
|
|
54726
|
-
|
|
54801
|
+
diagnostics: [
|
|
54727
54802
|
{
|
|
54728
|
-
|
|
54803
|
+
kind: "spec_identity_mismatch",
|
|
54804
|
+
diagnostic: true,
|
|
54805
|
+
rawQuestion: `SPEC_IDENTITY_MISMATCH: this spec page gates ${intent.taskId}, but its heading identifies a different task \u2014 rewrite the spec heading and content for ${intent.taskId}, or re-link the task.`,
|
|
54729
54806
|
whyBlocking: "converged spec content belongs to a different task than the one it gates",
|
|
54730
54807
|
category: "scope"
|
|
54731
54808
|
}
|
|
@@ -54745,12 +54822,8 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
54745
54822
|
}
|
|
54746
54823
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
54747
54824
|
const accepted = result.history.at(-1)?.accepted ?? [];
|
|
54748
|
-
const
|
|
54749
|
-
|
|
54750
|
-
whyBlocking: `reviewer raised a ${issue2.category} issue`,
|
|
54751
|
-
category: issue2.category
|
|
54752
|
-
}));
|
|
54753
|
-
return clarifications.length > 0 ? { outcome: "CONSENSUS_BLOCKED", clarifications } : { outcome: "CONSENSUS_BLOCKED" };
|
|
54825
|
+
const operatorClarifications = accepted.filter((issue2) => issue2.category === "ambiguity" || issue2.category === "scope").map((issue2) => mapAcceptedIssueToClarification(issue2));
|
|
54826
|
+
return operatorClarifications.length > 0 ? { outcome: "CONSENSUS_BLOCKED", operatorClarifications } : { outcome: "CONSENSUS_BLOCKED" };
|
|
54754
54827
|
}
|
|
54755
54828
|
return { outcome: result.outcome };
|
|
54756
54829
|
}
|
|
@@ -55175,6 +55248,22 @@ function buildGateQuestionOptions(question, category) {
|
|
|
55175
55248
|
}
|
|
55176
55249
|
];
|
|
55177
55250
|
}
|
|
55251
|
+
var HUMANIZE_TEMPLATE_BY_KIND = {
|
|
55252
|
+
scope_direction: "The spec can be read in two different ways. Which direction should it follow?",
|
|
55253
|
+
default_choice: "One important detail is still ambiguous. Which interpretation should the spec use?",
|
|
55254
|
+
missing_requirement: "A required part of the task is still missing from the spec. What should it say?",
|
|
55255
|
+
acceptance_criteria_detail: "The direction is clear, but the spec still needs a sharper success check. What should count as done?"
|
|
55256
|
+
};
|
|
55257
|
+
var HUMANIZE_CONTEXT_CAP = 200;
|
|
55258
|
+
function humanizeClarificationBody(clarification) {
|
|
55259
|
+
const prefix = HUMANIZE_TEMPLATE_BY_KIND[clarification.kind];
|
|
55260
|
+
let context = clarification.rawQuestion.replace(/\b(?:parseable|SPEC_IDENTITY_MISMATCH|machine-usable)\b/gi, "").replace(/\s+/g, " ").trim();
|
|
55261
|
+
if (context.length > HUMANIZE_CONTEXT_CAP) {
|
|
55262
|
+
const cutIdx = context.lastIndexOf(" ", HUMANIZE_CONTEXT_CAP);
|
|
55263
|
+
context = context.slice(0, cutIdx > 0 ? cutIdx : HUMANIZE_CONTEXT_CAP).trim() + "\u2026";
|
|
55264
|
+
}
|
|
55265
|
+
return context ? `${prefix} ${context}` : prefix;
|
|
55266
|
+
}
|
|
55178
55267
|
var defaultNativeSpecGateBuilders = { buildSpecGateDeps, runSpecGate };
|
|
55179
55268
|
async function runNativeSpecGate(ctx, input, builders = defaultNativeSpecGateBuilders) {
|
|
55180
55269
|
const deps = builders.buildSpecGateDeps({
|
|
@@ -55215,6 +55304,100 @@ async function cachedResult(client, input) {
|
|
|
55215
55304
|
}
|
|
55216
55305
|
return null;
|
|
55217
55306
|
}
|
|
55307
|
+
async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
55308
|
+
const operatorClarifications = result.operatorClarifications ?? [];
|
|
55309
|
+
const diagnostics = result.diagnostics ?? [];
|
|
55310
|
+
if (operatorClarifications.length > 0) {
|
|
55311
|
+
try {
|
|
55312
|
+
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
55313
|
+
if (page) {
|
|
55314
|
+
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
55315
|
+
id: page.id,
|
|
55316
|
+
taskId: page.taskId ?? null
|
|
55317
|
+
});
|
|
55318
|
+
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
55319
|
+
contentHash: domainScopedReviewHash(page.contentHash, domain2),
|
|
55320
|
+
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
55321
|
+
rounds: 0
|
|
55322
|
+
});
|
|
55323
|
+
}
|
|
55324
|
+
} catch (error52) {
|
|
55325
|
+
process.stderr.write(
|
|
55326
|
+
`[mrrlin-mcp consensus] clarification verdict write failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
55327
|
+
`
|
|
55328
|
+
);
|
|
55329
|
+
}
|
|
55330
|
+
}
|
|
55331
|
+
let filedQuestionCount = 0;
|
|
55332
|
+
if (operatorClarifications.length > 0) {
|
|
55333
|
+
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
55334
|
+
const taskId = page?.taskId ?? null;
|
|
55335
|
+
if (!resolvedTaskId && taskId) resolvedTaskId = taskId;
|
|
55336
|
+
let planId = null;
|
|
55337
|
+
if (!taskId) {
|
|
55338
|
+
const plans = await ctx.client.listPlans(input.projectSlug);
|
|
55339
|
+
planId = plans.find((p) => p.specWikiPageId === input.pageId)?.id ?? null;
|
|
55340
|
+
}
|
|
55341
|
+
if (taskId) {
|
|
55342
|
+
const scopeId = taskId;
|
|
55343
|
+
const groupId = operatorClarifications.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto9.createHash)("sha256").update(operatorClarifications.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
55344
|
+
for (const clarification of operatorClarifications) {
|
|
55345
|
+
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
55346
|
+
try {
|
|
55347
|
+
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
55348
|
+
await ctx.client.createInboxItem(input.projectSlug, {
|
|
55349
|
+
id,
|
|
55350
|
+
kind: "question",
|
|
55351
|
+
taskId: scopeId,
|
|
55352
|
+
body: humanizeClarificationBody(clarification),
|
|
55353
|
+
origin: "gate",
|
|
55354
|
+
questionOptions,
|
|
55355
|
+
...groupId ? { groupId } : {}
|
|
55356
|
+
});
|
|
55357
|
+
} catch {
|
|
55358
|
+
}
|
|
55359
|
+
}
|
|
55360
|
+
filedQuestionCount = operatorClarifications.length;
|
|
55361
|
+
} else if (planId) {
|
|
55362
|
+
const scopeId = planId;
|
|
55363
|
+
const groupId = operatorClarifications.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto9.createHash)("sha256").update(operatorClarifications.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
55364
|
+
for (const clarification of operatorClarifications) {
|
|
55365
|
+
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
|
|
55366
|
+
try {
|
|
55367
|
+
const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
|
|
55368
|
+
await ctx.client.createInboxItem(input.projectSlug, {
|
|
55369
|
+
id,
|
|
55370
|
+
kind: "question",
|
|
55371
|
+
planId: scopeId,
|
|
55372
|
+
body: humanizeClarificationBody(clarification),
|
|
55373
|
+
origin: "gate",
|
|
55374
|
+
questionOptions,
|
|
55375
|
+
...groupId ? { groupId } : {}
|
|
55376
|
+
});
|
|
55377
|
+
} catch {
|
|
55378
|
+
}
|
|
55379
|
+
}
|
|
55380
|
+
filedQuestionCount = operatorClarifications.length;
|
|
55381
|
+
}
|
|
55382
|
+
}
|
|
55383
|
+
let error51;
|
|
55384
|
+
if (filedQuestionCount > 0) {
|
|
55385
|
+
error51 = `Consensus blocked on ${filedQuestionCount} clarification(s); filed as operator question(s).`;
|
|
55386
|
+
} else if (diagnostics.length > 0 && operatorClarifications.length === 0) {
|
|
55387
|
+
error51 = "Consensus blocked: the spec content does not match its linked task. This requires fixing the spec/task relationship directly, not an inbox answer.";
|
|
55388
|
+
} else if (operatorClarifications.length > 0) {
|
|
55389
|
+
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.`;
|
|
55390
|
+
} else {
|
|
55391
|
+
error51 = "Consensus blocked: reviewers did not converge within budget.";
|
|
55392
|
+
}
|
|
55393
|
+
return {
|
|
55394
|
+
kind: "failed",
|
|
55395
|
+
failureReason: "consensus_blocked",
|
|
55396
|
+
error: error51,
|
|
55397
|
+
filedQuestionCount,
|
|
55398
|
+
...resolvedTaskId ? { taskId: resolvedTaskId } : {}
|
|
55399
|
+
};
|
|
55400
|
+
}
|
|
55218
55401
|
async function run(ctx, input) {
|
|
55219
55402
|
const result = await runNativeSpecGate(ctx, input);
|
|
55220
55403
|
if (process.env[SHADOW_ENV_FLAG]) {
|
|
@@ -55243,95 +55426,7 @@ async function run(ctx, input) {
|
|
|
55243
55426
|
} catch {
|
|
55244
55427
|
}
|
|
55245
55428
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
55246
|
-
|
|
55247
|
-
if (clarifications.length > 0) {
|
|
55248
|
-
try {
|
|
55249
|
-
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
55250
|
-
if (page) {
|
|
55251
|
-
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
55252
|
-
id: page.id,
|
|
55253
|
-
taskId: page.taskId ?? null
|
|
55254
|
-
});
|
|
55255
|
-
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
55256
|
-
contentHash: domainScopedReviewHash(page.contentHash, domain2),
|
|
55257
|
-
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
55258
|
-
rounds: 0
|
|
55259
|
-
});
|
|
55260
|
-
}
|
|
55261
|
-
} catch (error52) {
|
|
55262
|
-
process.stderr.write(
|
|
55263
|
-
`[mrrlin-mcp consensus] clarification verdict write failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
55264
|
-
`
|
|
55265
|
-
);
|
|
55266
|
-
}
|
|
55267
|
-
}
|
|
55268
|
-
let filedQuestionCount = 0;
|
|
55269
|
-
if (clarifications.length > 0) {
|
|
55270
|
-
const page = await ctx.client.getWikiPage(input.projectSlug, input.pageId);
|
|
55271
|
-
const taskId = page?.taskId ?? null;
|
|
55272
|
-
if (!resolvedTaskId && taskId) resolvedTaskId = taskId;
|
|
55273
|
-
let planId = null;
|
|
55274
|
-
if (!taskId) {
|
|
55275
|
-
const plans = await ctx.client.listPlans(input.projectSlug);
|
|
55276
|
-
planId = plans.find((p) => p.specWikiPageId === input.pageId)?.id ?? null;
|
|
55277
|
-
}
|
|
55278
|
-
if (taskId) {
|
|
55279
|
-
const scopeId = taskId;
|
|
55280
|
-
const groupId = clarifications.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto9.createHash)("sha256").update(clarifications.map((c) => c.question).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
55281
|
-
for (const clarification of clarifications) {
|
|
55282
|
-
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.question).digest("hex").slice(0, 10)}`;
|
|
55283
|
-
try {
|
|
55284
|
-
const questionOptions = buildGateQuestionOptions(clarification.question, clarification.category);
|
|
55285
|
-
await ctx.client.createInboxItem(input.projectSlug, {
|
|
55286
|
-
id,
|
|
55287
|
-
kind: "question",
|
|
55288
|
-
taskId: scopeId,
|
|
55289
|
-
body: clarification.question,
|
|
55290
|
-
origin: "gate",
|
|
55291
|
-
questionOptions,
|
|
55292
|
-
...groupId ? { groupId } : {}
|
|
55293
|
-
});
|
|
55294
|
-
} catch {
|
|
55295
|
-
}
|
|
55296
|
-
}
|
|
55297
|
-
filedQuestionCount = clarifications.length;
|
|
55298
|
-
} else if (planId) {
|
|
55299
|
-
const scopeId = planId;
|
|
55300
|
-
const groupId = clarifications.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto9.createHash)("sha256").update(clarifications.map((c) => c.question).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
55301
|
-
for (const clarification of clarifications) {
|
|
55302
|
-
const id = `II-${scopeId}-gate-${(0, import_node_crypto9.createHash)("sha256").update(clarification.question).digest("hex").slice(0, 10)}`;
|
|
55303
|
-
try {
|
|
55304
|
-
const questionOptions = buildGateQuestionOptions(clarification.question, clarification.category);
|
|
55305
|
-
await ctx.client.createInboxItem(input.projectSlug, {
|
|
55306
|
-
id,
|
|
55307
|
-
kind: "question",
|
|
55308
|
-
planId: scopeId,
|
|
55309
|
-
body: clarification.question,
|
|
55310
|
-
origin: "gate",
|
|
55311
|
-
questionOptions,
|
|
55312
|
-
...groupId ? { groupId } : {}
|
|
55313
|
-
});
|
|
55314
|
-
} catch {
|
|
55315
|
-
}
|
|
55316
|
-
}
|
|
55317
|
-
filedQuestionCount = clarifications.length;
|
|
55318
|
-
}
|
|
55319
|
-
}
|
|
55320
|
-
let error51;
|
|
55321
|
-
if (filedQuestionCount > 0) {
|
|
55322
|
-
error51 = `Consensus blocked on ${filedQuestionCount} clarification(s); filed as operator question(s).`;
|
|
55323
|
-
} else if (clarifications.length > 0) {
|
|
55324
|
-
error51 = `Consensus blocked on ${clarifications.length} clarification(s), but the spec page is not linked to a task or plan, so none could be filed as questions.`;
|
|
55325
|
-
} else {
|
|
55326
|
-
error51 = "Consensus blocked: reviewers did not converge within budget.";
|
|
55327
|
-
}
|
|
55328
|
-
return {
|
|
55329
|
-
kind: "failed",
|
|
55330
|
-
failureReason: "consensus_blocked",
|
|
55331
|
-
error: error51,
|
|
55332
|
-
filedQuestionCount,
|
|
55333
|
-
...resolvedTaskId ? { taskId: resolvedTaskId } : {}
|
|
55334
|
-
};
|
|
55429
|
+
return handleConsensusBlocked(result, ctx, input, resolvedTaskId);
|
|
55335
55430
|
}
|
|
55336
55431
|
switch (result.outcome) {
|
|
55337
55432
|
case "CONVERGED":
|
|
@@ -55601,6 +55696,8 @@ var mcpToolNames = {
|
|
|
55601
55696
|
updatePlan: "update_plan",
|
|
55602
55697
|
updateTask: "update_task",
|
|
55603
55698
|
resetDirectorThread: "reset_director_thread",
|
|
55699
|
+
archiveTask: "archive_task",
|
|
55700
|
+
unarchiveTask: "unarchive_task",
|
|
55604
55701
|
updateWikiPage: "update_wiki_page",
|
|
55605
55702
|
markSpecReady: "mark_spec_ready",
|
|
55606
55703
|
runSpecConsensus: "run_spec_consensus",
|
|
@@ -56116,6 +56213,8 @@ var getTaskInputSchema = taskScopedSchema;
|
|
|
56116
56213
|
var createTaskInputSchema = projectScopedSchema.merge(mrrlinTaskCreateSchema);
|
|
56117
56214
|
var updateTaskInputSchema = taskScopedSchema.merge(mrrlinTaskUpdateSchema);
|
|
56118
56215
|
var resetDirectorThreadInputSchema = taskScopedSchema;
|
|
56216
|
+
var archiveTaskInputSchema = taskScopedSchema;
|
|
56217
|
+
var unarchiveTaskInputSchema = taskScopedSchema;
|
|
56119
56218
|
var listPlansInputSchema = projectScopedSchema;
|
|
56120
56219
|
var createPlanInputSchema = projectScopedSchema.merge(mrrlinPlanCreateSchema);
|
|
56121
56220
|
var updatePlanInputSchema = planScopedSchema.merge(mrrlinPlanUpdateSchema);
|
|
@@ -56268,6 +56367,8 @@ var mcpToolInputSchemas = {
|
|
|
56268
56367
|
[mcpToolNames.updatePlan]: updatePlanInputSchema,
|
|
56269
56368
|
[mcpToolNames.updateTask]: updateTaskInputSchema,
|
|
56270
56369
|
[mcpToolNames.resetDirectorThread]: resetDirectorThreadInputSchema,
|
|
56370
|
+
[mcpToolNames.archiveTask]: archiveTaskInputSchema,
|
|
56371
|
+
[mcpToolNames.unarchiveTask]: unarchiveTaskInputSchema,
|
|
56271
56372
|
[mcpToolNames.updateWikiPage]: updateWikiPageInputSchema,
|
|
56272
56373
|
[mcpToolNames.markSpecReady]: markSpecReadyInputSchema,
|
|
56273
56374
|
[mcpToolNames.runSpecConsensus]: runSpecConsensusInputSchema,
|
|
@@ -56331,6 +56432,8 @@ var mcpToolOperationIds = {
|
|
|
56331
56432
|
[mcpToolNames.updatePlan]: openApiOperationIds.updatePlan,
|
|
56332
56433
|
[mcpToolNames.updateTask]: openApiOperationIds.updateTask,
|
|
56333
56434
|
[mcpToolNames.resetDirectorThread]: openApiOperationIds.resetDirectorThread,
|
|
56435
|
+
[mcpToolNames.archiveTask]: openApiOperationIds.archiveTask,
|
|
56436
|
+
[mcpToolNames.unarchiveTask]: openApiOperationIds.unarchiveTask,
|
|
56334
56437
|
[mcpToolNames.updateWikiPage]: openApiOperationIds.updateWikiPage,
|
|
56335
56438
|
[mcpToolNames.markSpecReady]: openApiOperationIds.markSpecReady,
|
|
56336
56439
|
[mcpToolNames.listInboxItems]: openApiOperationIds.listInboxItems,
|
|
@@ -56378,6 +56481,8 @@ var mcpToolDescriptions = {
|
|
|
56378
56481
|
[mcpToolNames.updatePlan]: "Update a plan/epic.",
|
|
56379
56482
|
[mcpToolNames.updateTask]: "Update a task. Honors DB invariants: closed_no_result requires linked Evidence wiki pages. Set directorThreadId here to persist the worker's resumable provider thread handle.",
|
|
56380
56483
|
[mcpToolNames.resetDirectorThread]: "Clear a task's persisted Director execution thread handle so the next execution run starts a fresh provider thread. Records a director_thread_reset task event.",
|
|
56484
|
+
[mcpToolNames.archiveTask]: "Archive a task: hides it from default reads, removes it from plan progress and scheduler consideration, cancels its non-terminal execution runs, and closes its open inbox items. Reversible \u2014 the project owner can restore it with unarchive_task. Records an 'archived' task event.",
|
|
56485
|
+
[mcpToolNames.unarchiveTask]: "Restore an archived task to its original status and visibility. Owner-only \u2014 refused with NOT_PROJECT_OWNER for any other caller. Does not resurrect the execution runs or inbox items that were cancelled/closed when the task was archived. Records a 'restored' task event.",
|
|
56381
56486
|
[mcpToolNames.updateWikiPage]: "Update a markdown Wiki page.",
|
|
56382
56487
|
[mcpToolNames.markSpecReady]: "Mark a wiki page (folder=Specs) as ready (or not) for execution-run gating. The Director sets this true after validating the spec; any edit to the page's markdown auto-resets it to false.",
|
|
56383
56488
|
[mcpToolNames.runSpecConsensus]: 'Enqueue the consensus review gate over a Specs/{id} page. Returns IMMEDIATELY with one of: already_converged (a CONVERGED review already exists for the current content \u2014 proceed straight to create_execution_run, do NOT call mark_spec_ready, it is already set), already_reviewed (a terminal CONSENSUS_BLOCKED for this content), already_running (a consensus job is in flight), or queued (a new job was launched). The review runs in the background; its result/blocker arrives in the inbox (info on CONVERGED, handoff on failure) and is surfaced into the open chat. Do NOT call again for the same spec and do NOT poll. On queued/already_running, tell the user it was launched and end the turn. Pass extraHeads: ["researcher"] for specs referencing external libraries/APIs, or ["debugger"] for root-cause/investigation specs to add opt-in specialist reviewers on top of the fixed four. For a NON-code task (domain seo/marketing/content, ADR 0020): the low-risk lanes `seo`, `marketing`, and `content` proceed to `create_execution_run` after consensus, using the same flow as code tasks. Report the spec\'s review outcome to the operator.',
|
|
@@ -56808,6 +56913,20 @@ function createMrrlinTools(options) {
|
|
|
56808
56913
|
"Unable to reset director thread.",
|
|
56809
56914
|
async (c, { projectSlug, taskId }) => await c.resetDirectorThread(projectSlug, taskId)
|
|
56810
56915
|
),
|
|
56916
|
+
[mcpToolNames.archiveTask]: makeTool(
|
|
56917
|
+
client,
|
|
56918
|
+
mcpToolNames.archiveTask,
|
|
56919
|
+
"TASK_ARCHIVE_FAILED",
|
|
56920
|
+
"Unable to archive task.",
|
|
56921
|
+
async (c, { projectSlug, taskId }) => await c.archiveTask(projectSlug, taskId)
|
|
56922
|
+
),
|
|
56923
|
+
[mcpToolNames.unarchiveTask]: makeTool(
|
|
56924
|
+
client,
|
|
56925
|
+
mcpToolNames.unarchiveTask,
|
|
56926
|
+
"TASK_UNARCHIVE_FAILED",
|
|
56927
|
+
"Unable to unarchive task.",
|
|
56928
|
+
async (c, { projectSlug, taskId }) => await c.unarchiveTask(projectSlug, taskId)
|
|
56929
|
+
),
|
|
56811
56930
|
[mcpToolNames.updateWikiPage]: makeTool(
|
|
56812
56931
|
client,
|
|
56813
56932
|
mcpToolNames.updateWikiPage,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.55",
|
|
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/director-e2e": "0.0.0",
|
|
25
|
-
"@mrrlin/schemas": "0.0.0",
|
|
26
24
|
"@mrrlin/client": "0.0.0",
|
|
27
|
-
"@mrrlin/
|
|
25
|
+
"@mrrlin/codex-client": "0.0.0",
|
|
28
26
|
"@mrrlin/tsconfig": "0.0.0",
|
|
29
|
-
"@mrrlin/
|
|
27
|
+
"@mrrlin/director-e2e": "0.0.0",
|
|
28
|
+
"@mrrlin/schemas": "0.0.0",
|
|
29
|
+
"@mrrlin/wiki": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|