@mrrlin-dev/mcp 0.3.68 → 0.4.0
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 +180 -29
- package/package.json +5 -5
package/dist/bin.cjs
CHANGED
|
@@ -20700,6 +20700,18 @@ function retargetRetiredApiBaseUrl(input) {
|
|
|
20700
20700
|
function retiredApiHostnames() {
|
|
20701
20701
|
return Object.keys(RETIRED_API_BASE_URLS);
|
|
20702
20702
|
}
|
|
20703
|
+
function bridgeUrlImpliesStaging(input) {
|
|
20704
|
+
const raw = input?.trim();
|
|
20705
|
+
if (!raw) return false;
|
|
20706
|
+
if (isLoopbackUrl(raw)) return true;
|
|
20707
|
+
let hostname3;
|
|
20708
|
+
try {
|
|
20709
|
+
hostname3 = new URL(raw).hostname.toLowerCase();
|
|
20710
|
+
} catch {
|
|
20711
|
+
return false;
|
|
20712
|
+
}
|
|
20713
|
+
return hostname3 === "staging.mrrlin.com";
|
|
20714
|
+
}
|
|
20703
20715
|
function isLoopbackUrl(input) {
|
|
20704
20716
|
try {
|
|
20705
20717
|
const url2 = new URL(input);
|
|
@@ -20846,11 +20858,12 @@ async function installBrowser(opts = {}) {
|
|
|
20846
20858
|
function deriveServeEnvFromProcessEnv(env) {
|
|
20847
20859
|
const out = {};
|
|
20848
20860
|
const rawApiUrl = env.MRRLIN_API_BASE_URL?.trim();
|
|
20861
|
+
const bridgeWantsStaging = bridgeUrlImpliesStaging(env.MRRLIN_BRIDGE_URL);
|
|
20849
20862
|
const apiUrl = rawApiUrl ? retargetRetiredApiBaseUrl(rawApiUrl).baseUrl : void 0;
|
|
20850
20863
|
if (apiUrl && !isStagingApiBaseUrl(apiUrl)) {
|
|
20851
20864
|
out.MRRLIN_API_BASE_URL = apiUrl;
|
|
20852
20865
|
if (env.MRRLIN_API_ALLOW_REMOTE === "1") out.MRRLIN_API_ALLOW_REMOTE = "1";
|
|
20853
|
-
} else if (apiUrl || env.MRRLIN_STAGING === "1") {
|
|
20866
|
+
} else if (apiUrl || env.MRRLIN_STAGING === "1" || bridgeWantsStaging) {
|
|
20854
20867
|
out.MRRLIN_STAGING = "1";
|
|
20855
20868
|
}
|
|
20856
20869
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
@@ -21009,7 +21022,7 @@ function blockEnvHasRetiredApiUrl(actual) {
|
|
|
21009
21022
|
}
|
|
21010
21023
|
function renderEnvPreservedNotice(blockId, envKeys) {
|
|
21011
21024
|
const keys = [...envKeys].sort().join(", ");
|
|
21012
|
-
return `[mrrlin-mcp
|
|
21025
|
+
return `[mrrlin-mcp configure bridge] kept your existing [mcp_servers.${blockId}] env keys (${keys}) so your API target is preserved. Re-run with --force to replace or remove it.`;
|
|
21013
21026
|
}
|
|
21014
21027
|
async function pathExists(target) {
|
|
21015
21028
|
try {
|
|
@@ -36401,6 +36414,8 @@ var openApiOperationIds = {
|
|
|
36401
36414
|
getTask: "getTask",
|
|
36402
36415
|
archiveTask: "archiveTask",
|
|
36403
36416
|
unarchiveTask: "unarchiveTask",
|
|
36417
|
+
takeOverTask: "takeOverTask",
|
|
36418
|
+
closeTaskAsWontDo: "closeTaskAsWontDo",
|
|
36404
36419
|
getWikiPage: "getWikiPage",
|
|
36405
36420
|
handleGithubInstallCallback: "handleGithubInstallCallback",
|
|
36406
36421
|
listExecutionArtifacts: "listExecutionArtifacts",
|
|
@@ -36889,6 +36904,32 @@ var SUPPORTED_EXECUTION_DOMAINS = Object.keys(SUPPORTED_EXECUTION_POLICY_BY_DOMA
|
|
|
36889
36904
|
var taxonomyCategorySchema = external_exports.string().min(1).regex(/^[a-z][a-z0-9-]*$/);
|
|
36890
36905
|
var taxonomySubcategorySchema = external_exports.string().min(1).regex(/^[a-z][a-z0-9-]*$/);
|
|
36891
36906
|
var mrrlinTaskAssigneeSchema = external_exports.union([external_exports.string().regex(/^@/), external_exports.string().email()]).nullable();
|
|
36907
|
+
var mrrlinStopReasonSchema = external_exports.enum([
|
|
36908
|
+
"task_terminal",
|
|
36909
|
+
"active_run_exists",
|
|
36910
|
+
"draft_running",
|
|
36911
|
+
"consensus_running",
|
|
36912
|
+
"spec_changed_during_advance",
|
|
36913
|
+
"run_limit_reached",
|
|
36914
|
+
"backoff_pending",
|
|
36915
|
+
"blocked_by_active",
|
|
36916
|
+
"spec_missing",
|
|
36917
|
+
"spec_draft_failed",
|
|
36918
|
+
"spec_not_specs_folder",
|
|
36919
|
+
"awaiting_clarification",
|
|
36920
|
+
"consensus_blocked",
|
|
36921
|
+
"consensus_run_failed",
|
|
36922
|
+
"consensus_disabled_spec_not_ready",
|
|
36923
|
+
"spec_approval_pending",
|
|
36924
|
+
"awaiting_handoff",
|
|
36925
|
+
"assigned_to_other",
|
|
36926
|
+
"attempts_exhausted",
|
|
36927
|
+
"daily_job_budget_exhausted",
|
|
36928
|
+
"taken_over",
|
|
36929
|
+
"operator_absent",
|
|
36930
|
+
"autonomy_paused",
|
|
36931
|
+
"unknown"
|
|
36932
|
+
]);
|
|
36892
36933
|
var mrrlinTaskSchema = external_exports.object({
|
|
36893
36934
|
// Output-optional (not defaulted-required) so existing task fixtures/mocks across the
|
|
36894
36935
|
// codebase need no churn; DB rows always resolve it to `string | null` via taskRowToTask.
|
|
@@ -36903,6 +36944,9 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
36903
36944
|
autonomyReason: external_exports.string().min(1).max(240),
|
|
36904
36945
|
blockedBy: external_exports.array(mrrlinTaskIdSchema),
|
|
36905
36946
|
category: taxonomyCategorySchema,
|
|
36947
|
+
// Slice 3 (task-stall-visibility): why a task was closed as won't-do -- populated only by
|
|
36948
|
+
// closeTaskAsWontDo. Output-optional so existing task fixtures need no churn.
|
|
36949
|
+
closeReason: external_exports.string().nullable().optional(),
|
|
36906
36950
|
directorThreadId: external_exports.string().nullable().default(null),
|
|
36907
36951
|
dueDate: external_exports.string().date().nullable(),
|
|
36908
36952
|
followUpAt: external_exports.string().datetime().nullable(),
|
|
@@ -36911,6 +36955,9 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
36911
36955
|
latestJudgement: external_exports.string().nullable().default(null),
|
|
36912
36956
|
latestJudgementAt: external_exports.string().datetime().nullable().default(null),
|
|
36913
36957
|
latestJudgementKind: external_exports.enum(["autonomy", "handoff"]).nullable().default(null),
|
|
36958
|
+
// Slice 3: the retry-backoff timer for exactly spec_draft_failed/consensus_run_failed -- a task
|
|
36959
|
+
// whose next_attempt_at is in the future is skipped from admission (backoff_pending).
|
|
36960
|
+
nextAttemptAt: external_exports.string().datetime().nullable().optional(),
|
|
36914
36961
|
notes: external_exports.string().nullable().default(null),
|
|
36915
36962
|
// Raw operator request captured at task creation (operator-authored ONLY, never Director-generated).
|
|
36916
36963
|
// The intent-aware spec gate judges the spec against this; null/absent ⇒ the gate fail-closes.
|
|
@@ -36920,6 +36967,14 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
36920
36967
|
planId: mrrlinPlanIdSchema.nullable(),
|
|
36921
36968
|
specApproved: mrrlinSpecApprovedSchema.optional(),
|
|
36922
36969
|
specWikiPageId: mrrlinWikiPageIdSchema.nullable(),
|
|
36970
|
+
// Slice 3: episode bookkeeping alongside the async_jobs-derived countEpisodeAttempts --
|
|
36971
|
+
// stallAttemptCount backs the exponential-backoff formula, stallRearmed guards against
|
|
36972
|
+
// re-arming the same episode's ceiling more than once.
|
|
36973
|
+
stallAttemptCount: external_exports.number().int().nonnegative().optional(),
|
|
36974
|
+
stallRearmed: external_exports.boolean().optional(),
|
|
36975
|
+
stallNextAction: external_exports.string().nullable().optional(),
|
|
36976
|
+
stallReason: mrrlinStopReasonSchema.nullable().optional(),
|
|
36977
|
+
stallSince: external_exports.string().datetime().nullable().optional(),
|
|
36923
36978
|
status: mrrlinTaskStatusSchema,
|
|
36924
36979
|
subcategory: taxonomySubcategorySchema,
|
|
36925
36980
|
tags: mrrlinTaskTagsSchema,
|
|
@@ -36982,6 +37037,12 @@ var mrrlinWikiPageSchema = external_exports.object({
|
|
|
36982
37037
|
var mrrlinMarkSpecReadyRequestSchema = external_exports.object({
|
|
36983
37038
|
ready: external_exports.boolean()
|
|
36984
37039
|
});
|
|
37040
|
+
var mrrlinTakeOverTaskRequestSchema = external_exports.object({
|
|
37041
|
+
assignee: external_exports.string().min(1)
|
|
37042
|
+
});
|
|
37043
|
+
var mrrlinCloseTaskAsWontDoRequestSchema = external_exports.object({
|
|
37044
|
+
reason: external_exports.string().min(1)
|
|
37045
|
+
});
|
|
36985
37046
|
var mrrlinGithubBindingSchema = external_exports.object({
|
|
36986
37047
|
appInstallationState: external_exports.enum(["installed", "missing"]),
|
|
36987
37048
|
installationId: external_exports.number().int().positive().nullable().default(null),
|
|
@@ -37997,7 +38058,8 @@ var mrrlinInFlightSchedulerEntrySchema = external_exports.discriminatedUnion("ki
|
|
|
37997
38058
|
]);
|
|
37998
38059
|
var mrrlinIneligibleTaskSchema = external_exports.object({
|
|
37999
38060
|
task: mrrlinTaskSchema,
|
|
38000
|
-
autonomyLevel: mrrlinAutonomyLevelSchema
|
|
38061
|
+
autonomyLevel: mrrlinAutonomyLevelSchema,
|
|
38062
|
+
reason: external_exports.string().nullable().optional()
|
|
38001
38063
|
});
|
|
38002
38064
|
var schedulerPendingTasksResponseSchema = external_exports.object({
|
|
38003
38065
|
data: external_exports.object({
|
|
@@ -38021,6 +38083,12 @@ var schedulerPendingTasksResponseSchema = external_exports.object({
|
|
|
38021
38083
|
// person actually running the project) is visible as a large number instead of the autopilot
|
|
38022
38084
|
// silently doing nothing.
|
|
38023
38085
|
excludedByOwnershipCount: external_exports.number().int().min(0),
|
|
38086
|
+
// Additive: the same ownership exclusion grouped by assignee. This reveals who the
|
|
38087
|
+
// autopilot is waiting for without returning task ids or titles in this endpoint.
|
|
38088
|
+
ownershipExcludedAssignees: external_exports.array(external_exports.object({
|
|
38089
|
+
assignee: external_exports.string(),
|
|
38090
|
+
count: external_exports.number().int().min(1)
|
|
38091
|
+
})),
|
|
38024
38092
|
// Additive: true when the operator-web-presence gate is the reason nothing new is starting
|
|
38025
38093
|
// (autonomyPaused takes precedence when both apply). Computed by the same shared helper the
|
|
38026
38094
|
// tick uses, so this page can never claim a task is starting when the tick would decline it.
|
|
@@ -38124,6 +38192,7 @@ var mrrlinInboxItemSchema = external_exports.object({
|
|
|
38124
38192
|
projectSlug: mrrlinProjectSlugSchema,
|
|
38125
38193
|
questionOptions: mrrlinInboxQuestionOptionsSchema.nullable().default(null),
|
|
38126
38194
|
readAt: external_exports.string().datetime().nullable(),
|
|
38195
|
+
reason: external_exports.string().nullable().optional(),
|
|
38127
38196
|
status: mrrlinInboxItemStatusSchema,
|
|
38128
38197
|
taskId: mrrlinTaskIdSchema.nullable()
|
|
38129
38198
|
}).superRefine((value, context) => {
|
|
@@ -38970,6 +39039,22 @@ function createMrrlinClient(config2) {
|
|
|
38970
39039
|
});
|
|
38971
39040
|
return taskResponseSchema.parse(body).data;
|
|
38972
39041
|
},
|
|
39042
|
+
async takeOverTask(projectSlug, taskId, assignee) {
|
|
39043
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/take-over`, {
|
|
39044
|
+
method: "POST",
|
|
39045
|
+
headers: { "content-type": "application/json" },
|
|
39046
|
+
body: JSON.stringify({ assignee })
|
|
39047
|
+
});
|
|
39048
|
+
return taskResponseSchema.parse(body).data;
|
|
39049
|
+
},
|
|
39050
|
+
async closeTaskAsWontDo(projectSlug, taskId, reason) {
|
|
39051
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/wont-do`, {
|
|
39052
|
+
method: "POST",
|
|
39053
|
+
headers: { "content-type": "application/json" },
|
|
39054
|
+
body: JSON.stringify({ reason })
|
|
39055
|
+
});
|
|
39056
|
+
return taskResponseSchema.parse(body).data;
|
|
39057
|
+
},
|
|
38973
39058
|
async updateWikiPage(projectSlug, pageId, patch) {
|
|
38974
39059
|
const body = await request(`${projectPath(projectSlug)}/wiki/${encodeURIComponent(pageId)}`, {
|
|
38975
39060
|
body: JSON.stringify(patch),
|
|
@@ -40050,7 +40135,7 @@ function startCodexProbe(deps = {}) {
|
|
|
40050
40135
|
}
|
|
40051
40136
|
|
|
40052
40137
|
// src/_generated/version.ts
|
|
40053
|
-
var PKG_VERSION = "0.
|
|
40138
|
+
var PKG_VERSION = "0.4.0";
|
|
40054
40139
|
|
|
40055
40140
|
// src/api-base-url-migrate.ts
|
|
40056
40141
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
@@ -43992,9 +44077,14 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
43992
44077
|
" if you are blocked and a human must decide before the work can continue, leave it at `handoff_needed`.",
|
|
43993
44078
|
"",
|
|
43994
44079
|
"File artifacts:",
|
|
43995
|
-
"- Decide BEFORE writing your result
|
|
43996
|
-
"
|
|
43997
|
-
"
|
|
44080
|
+
"- Decide BEFORE writing your result, by PROVENANCE: output assembled from data you RETRIEVED",
|
|
44081
|
+
" this run (API, browser, analytics, database, a search over project records) that comes out as",
|
|
44082
|
+
" a multi-record or multi-dimension result is a FILE, not inline text.",
|
|
44083
|
+
"- Your own synthesis or comparison stays inline however structured it looks, and so does a",
|
|
44084
|
+
" retrieval yielding one value. Both conditions must hold before it becomes a file. When it",
|
|
44085
|
+
" qualifies, produce the file; never merely offer one.",
|
|
44086
|
+
"- Mixed output counts as retrieved: if any material part came from a retrieval, treat the whole",
|
|
44087
|
+
" result as retrieved rather than splitting it between inline text and a file.",
|
|
43998
44088
|
"- When your output is a file (markdown report, HTML, screenshot), upload it with",
|
|
43999
44089
|
" `upload_artifact` and embed the returned markdown link \u2014 never inline large blobs",
|
|
44000
44090
|
" into chat, spec, or note text.",
|
|
@@ -50959,9 +51049,17 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
50959
51049
|
" is not measurable, re-set `verificationDueAt` to a new date with a reason.",
|
|
50960
51050
|
"",
|
|
50961
51051
|
"File artifacts contract \u2014 decide the delivery medium BEFORE you compose the reply:",
|
|
50962
|
-
"-
|
|
50963
|
-
"
|
|
50964
|
-
"
|
|
51052
|
+
"- Decide by PROVENANCE, not by predicting what the operator will do later. If this answer is",
|
|
51053
|
+
" assembled from data you RETRIEVED this turn (an HTTP/API call, the browser, analytics, a",
|
|
51054
|
+
" database, a search over project records) AND it comes out as a multi-record or",
|
|
51055
|
+
" multi-dimension result rather than one fact or a few values, it belongs in a FILE.",
|
|
51056
|
+
"- Your own synthesis, comparison, or judgement belongs in chat, however structured it looks.",
|
|
51057
|
+
" A retrieval that yields one number or one line also belongs in chat. Both conditions must",
|
|
51058
|
+
" hold before it becomes a file.",
|
|
51059
|
+
"- Mixed answers count as retrieved: if any material part of the result came from a retrieval,",
|
|
51060
|
+
" treat the whole answer as retrieved rather than splitting it between chat and a file.",
|
|
51061
|
+
'- When it qualifies, PRODUCE the file. Offering one ("I can give you this as CSV/JSON") is the',
|
|
51062
|
+
" failure, not the courtesy.",
|
|
50965
51063
|
"- When it is a file: write it (`.md` for linear prose, `.html` when structure or layout",
|
|
50966
51064
|
" carries meaning), upload with `upload_artifact` (class=temp for one-off downloads,",
|
|
50967
51065
|
" class=durable when a spec/evidence page references it; pass taskId when known), and paste",
|
|
@@ -52133,7 +52231,18 @@ function createBridgeMessageHandler(deps) {
|
|
|
52133
52231
|
});
|
|
52134
52232
|
const mapper = thread.mapper;
|
|
52135
52233
|
mapper.reset();
|
|
52136
|
-
const
|
|
52234
|
+
const contextFocusTaskId = msg.context?.focusTaskId?.trim();
|
|
52235
|
+
const contextProjectSlug = msg.context?.projectSlug?.trim();
|
|
52236
|
+
let answeredQuestions;
|
|
52237
|
+
if (deps.listAnsweredQuestions && contextFocusTaskId && contextProjectSlug) {
|
|
52238
|
+
try {
|
|
52239
|
+
const acknowledged = await deps.listAnsweredQuestions(contextProjectSlug, contextFocusTaskId);
|
|
52240
|
+
const withAnswers = acknowledged.filter((item) => Boolean(item.answer)).map((item) => ({ answer: item.answer, question: item.body }));
|
|
52241
|
+
answeredQuestions = withAnswers.length ? withAnswers : void 0;
|
|
52242
|
+
} catch {
|
|
52243
|
+
}
|
|
52244
|
+
}
|
|
52245
|
+
const contextPrefix = renderContextPrefix({ ...msg.context, answeredQuestions, directorSessionId });
|
|
52137
52246
|
const sendInTurn = (msg2) => {
|
|
52138
52247
|
const buf = thread.replay;
|
|
52139
52248
|
if (!buf) {
|
|
@@ -52819,7 +52928,7 @@ async function startDirectorBridge() {
|
|
|
52819
52928
|
if (!body) return;
|
|
52820
52929
|
const id = `II-MCPUPD-${(0, import_node_crypto8.randomUUID)()}`;
|
|
52821
52930
|
try {
|
|
52822
|
-
const row = await client.createInboxItem(projectSlug, { id, kind: "
|
|
52931
|
+
const row = await client.createInboxItem(projectSlug, { id, kind: "info", body });
|
|
52823
52932
|
process.stderr.write(
|
|
52824
52933
|
`[mrrlin-mcp director-bridge] info bridge.self_update.inbox_filed ${JSON.stringify({
|
|
52825
52934
|
projectSlug,
|
|
@@ -52839,6 +52948,7 @@ async function startDirectorBridge() {
|
|
|
52839
52948
|
);
|
|
52840
52949
|
}
|
|
52841
52950
|
},
|
|
52951
|
+
listAnsweredQuestions: (projectSlug, taskId) => client.listInboxItems(projectSlug, { status: "acknowledged", kind: "question", taskId }),
|
|
52842
52952
|
// External-agents control plane: the real spawn runner, with the EXTERNAL_AGENTS_CLI operator
|
|
52843
52953
|
// escape hatch bound here (the handlers stay spawn-free and this is the only production seam).
|
|
52844
52954
|
runExternalAgentsCli: (args, opts) => runExternalAgentsCli(args, { ...opts, envCliPath: process.env["EXTERNAL_AGENTS_CLI"] })
|
|
@@ -53253,11 +53363,13 @@ function renderContextPrefix(context) {
|
|
|
53253
53363
|
const urlPath = context?.urlPath?.trim() ? context.urlPath.trim() : void 0;
|
|
53254
53364
|
const focusTaskId = context?.focusTaskId?.trim() ? context.focusTaskId.trim() : void 0;
|
|
53255
53365
|
const directorSessionId = context?.directorSessionId?.trim() ? context.directorSessionId.trim() : void 0;
|
|
53366
|
+
const answeredQuestions = context?.answeredQuestions?.length ? context.answeredQuestions : void 0;
|
|
53256
53367
|
const payload = {};
|
|
53257
53368
|
if (projectSlug) payload.projectSlug = projectSlug;
|
|
53258
53369
|
if (urlPath) payload.urlPath = urlPath;
|
|
53259
53370
|
if (focusTaskId) payload.focusTaskId = focusTaskId;
|
|
53260
53371
|
if (directorSessionId) payload.directorSessionId = directorSessionId;
|
|
53372
|
+
if (answeredQuestions) payload.answeredQuestions = answeredQuestions;
|
|
53261
53373
|
const keys = Object.keys(payload);
|
|
53262
53374
|
if (keys.length === 0) return "";
|
|
53263
53375
|
return `Context (JSON): ${JSON.stringify(payload)}`;
|
|
@@ -57836,6 +57948,8 @@ var mcpToolNames = {
|
|
|
57836
57948
|
resetDirectorThread: "reset_director_thread",
|
|
57837
57949
|
archiveTask: "archive_task",
|
|
57838
57950
|
unarchiveTask: "unarchive_task",
|
|
57951
|
+
takeOverTask: "take_over_task",
|
|
57952
|
+
closeTaskAsWontDo: "close_task_as_wont_do",
|
|
57839
57953
|
updateWikiPage: "update_wiki_page",
|
|
57840
57954
|
markSpecReady: "mark_spec_ready",
|
|
57841
57955
|
runSpecConsensus: "run_spec_consensus",
|
|
@@ -58342,6 +58456,8 @@ var updateTaskInputSchema = taskScopedSchema.merge(mrrlinTaskUpdateSchema);
|
|
|
58342
58456
|
var resetDirectorThreadInputSchema = taskScopedSchema;
|
|
58343
58457
|
var archiveTaskInputSchema = taskScopedSchema;
|
|
58344
58458
|
var unarchiveTaskInputSchema = taskScopedSchema;
|
|
58459
|
+
var takeOverTaskInputSchema = taskScopedSchema.merge(mrrlinTakeOverTaskRequestSchema);
|
|
58460
|
+
var closeTaskAsWontDoInputSchema = taskScopedSchema.merge(mrrlinCloseTaskAsWontDoRequestSchema);
|
|
58345
58461
|
var listPlansInputSchema = projectScopedSchema;
|
|
58346
58462
|
var createPlanInputSchema = projectScopedSchema.merge(mrrlinPlanCreateSchema);
|
|
58347
58463
|
var updatePlanInputSchema = planScopedSchema.merge(mrrlinPlanUpdateSchema);
|
|
@@ -58499,6 +58615,8 @@ var mcpToolInputSchemas = {
|
|
|
58499
58615
|
[mcpToolNames.resetDirectorThread]: resetDirectorThreadInputSchema,
|
|
58500
58616
|
[mcpToolNames.archiveTask]: archiveTaskInputSchema,
|
|
58501
58617
|
[mcpToolNames.unarchiveTask]: unarchiveTaskInputSchema,
|
|
58618
|
+
[mcpToolNames.takeOverTask]: takeOverTaskInputSchema,
|
|
58619
|
+
[mcpToolNames.closeTaskAsWontDo]: closeTaskAsWontDoInputSchema,
|
|
58502
58620
|
[mcpToolNames.updateWikiPage]: updateWikiPageInputSchema,
|
|
58503
58621
|
[mcpToolNames.markSpecReady]: markSpecReadyInputSchema,
|
|
58504
58622
|
[mcpToolNames.runSpecConsensus]: runSpecConsensusInputSchema,
|
|
@@ -58568,6 +58686,8 @@ var mcpToolOperationIds = {
|
|
|
58568
58686
|
[mcpToolNames.resetDirectorThread]: openApiOperationIds.resetDirectorThread,
|
|
58569
58687
|
[mcpToolNames.archiveTask]: openApiOperationIds.archiveTask,
|
|
58570
58688
|
[mcpToolNames.unarchiveTask]: openApiOperationIds.unarchiveTask,
|
|
58689
|
+
[mcpToolNames.takeOverTask]: openApiOperationIds.takeOverTask,
|
|
58690
|
+
[mcpToolNames.closeTaskAsWontDo]: openApiOperationIds.closeTaskAsWontDo,
|
|
58571
58691
|
[mcpToolNames.updateWikiPage]: openApiOperationIds.updateWikiPage,
|
|
58572
58692
|
[mcpToolNames.markSpecReady]: openApiOperationIds.markSpecReady,
|
|
58573
58693
|
[mcpToolNames.listInboxItems]: openApiOperationIds.listInboxItems,
|
|
@@ -58619,6 +58739,8 @@ var mcpToolDescriptions = {
|
|
|
58619
58739
|
[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.",
|
|
58620
58740
|
[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.",
|
|
58621
58741
|
[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.",
|
|
58742
|
+
[mcpToolNames.takeOverTask]: "Take ownership of a task by assigning it to the provided assignee through the dedicated take-over route.",
|
|
58743
|
+
[mcpToolNames.closeTaskAsWontDo]: "Close a task as won't-do with a required human-readable reason recorded on the task.",
|
|
58622
58744
|
[mcpToolNames.updateWikiPage]: "Update a markdown Wiki page.",
|
|
58623
58745
|
[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.",
|
|
58624
58746
|
[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.',
|
|
@@ -59066,6 +59188,20 @@ function createMrrlinTools(options) {
|
|
|
59066
59188
|
"Unable to unarchive task.",
|
|
59067
59189
|
async (c, { projectSlug, taskId }) => await c.unarchiveTask(projectSlug, taskId)
|
|
59068
59190
|
),
|
|
59191
|
+
[mcpToolNames.takeOverTask]: makeTool(
|
|
59192
|
+
client,
|
|
59193
|
+
mcpToolNames.takeOverTask,
|
|
59194
|
+
"TASK_TAKE_OVER_FAILED",
|
|
59195
|
+
"Unable to take over task.",
|
|
59196
|
+
async (c, { projectSlug, taskId, assignee }) => await c.takeOverTask(projectSlug, taskId, assignee)
|
|
59197
|
+
),
|
|
59198
|
+
[mcpToolNames.closeTaskAsWontDo]: makeTool(
|
|
59199
|
+
client,
|
|
59200
|
+
mcpToolNames.closeTaskAsWontDo,
|
|
59201
|
+
"TASK_CLOSE_AS_WONT_DO_FAILED",
|
|
59202
|
+
"Unable to close task as won't-do.",
|
|
59203
|
+
async (c, { projectSlug, taskId, reason }) => await c.closeTaskAsWontDo(projectSlug, taskId, reason)
|
|
59204
|
+
),
|
|
59069
59205
|
[mcpToolNames.updateWikiPage]: makeTool(
|
|
59070
59206
|
client,
|
|
59071
59207
|
mcpToolNames.updateWikiPage,
|
|
@@ -59688,13 +59824,15 @@ function installService(deps) {
|
|
|
59688
59824
|
}
|
|
59689
59825
|
const resolvedBins = bins.filter((b) => b !== null);
|
|
59690
59826
|
const pathEnv = Array.from(new Set(resolvedBins.map((b) => import_node_path21.default.dirname(b)))).join(":");
|
|
59827
|
+
const apiBaseUrl = deps.env.MRRLIN_API_BASE_URL?.trim() || void 0;
|
|
59828
|
+
const staging = deps.env.MRRLIN_STAGING === "1" || !apiBaseUrl && bridgeUrlImpliesStaging(deps.env.MRRLIN_BRIDGE_URL);
|
|
59691
59829
|
const text = renderEcosystemConfig({
|
|
59692
59830
|
cwd: resolved.cwd,
|
|
59693
59831
|
home,
|
|
59694
59832
|
codexHome: deps.env.CODEX_HOME?.trim() || void 0,
|
|
59695
59833
|
pathEnv,
|
|
59696
|
-
staging
|
|
59697
|
-
apiBaseUrl
|
|
59834
|
+
staging,
|
|
59835
|
+
apiBaseUrl
|
|
59698
59836
|
});
|
|
59699
59837
|
const ecoPath = import_node_path21.default.join(home, ".mrrlin", "ecosystem.config.cjs");
|
|
59700
59838
|
deps.writeFile(ecoPath, text);
|
|
@@ -59817,22 +59955,22 @@ Usage:
|
|
|
59817
59955
|
mrrlin-mcp install-browser Idempotently install the dedicated, pinned
|
|
59818
59956
|
Chromium into ~/.mrrlin/browser/browsers (its own
|
|
59819
59957
|
copy, isolated from the operator's Chrome and
|
|
59820
|
-
codex-chrome). Run by
|
|
59958
|
+
codex-chrome). Run by configure bridge; safe to
|
|
59821
59959
|
re-run. Exits non-zero on download failure.
|
|
59822
59960
|
|
|
59823
59961
|
mrrlin-mcp browser-serve Launch the vendored @playwright/mcp against the
|
|
59824
59962
|
dedicated persistent profile
|
|
59825
59963
|
(~/.mrrlin/browser/profile, headed). This is the
|
|
59826
|
-
command
|
|
59964
|
+
command configure bridge wires for the
|
|
59827
59965
|
[mcp_servers.mrrlin-browser] block.
|
|
59828
59966
|
|
|
59829
59967
|
mrrlin-mcp external-agents-serve
|
|
59830
59968
|
Launch the bundled @mrrlin-dev/external-agents
|
|
59831
59969
|
MCP server (multi-LLM dispatch pool). This is the
|
|
59832
|
-
command
|
|
59970
|
+
command configure bridge wires for the
|
|
59833
59971
|
[mcp_servers.external-agents] block.
|
|
59834
59972
|
|
|
59835
|
-
mrrlin-mcp
|
|
59973
|
+
mrrlin-mcp configure bridge Idempotently register Mrrlin in
|
|
59836
59974
|
[--force] ~/.codex/config.toml (or CODEX_HOME). Adds an
|
|
59837
59975
|
[mcp_servers.mrrlin] block. Development
|
|
59838
59976
|
checkouts register the local dist/bin.cjs;
|
|
@@ -59841,12 +59979,12 @@ Usage:
|
|
|
59841
59979
|
Bakes API-target env vars into the block as an
|
|
59842
59980
|
inline env table so \`serve\` boots even when the
|
|
59843
59981
|
local 127.0.0.1:8787 is not running. Setup guide:
|
|
59844
|
-
MRRLIN_STAGING=1 mrrlin-mcp
|
|
59982
|
+
MRRLIN_STAGING=1 mrrlin-mcp configure bridge
|
|
59845
59983
|
is the canonical path (staging alias + remote-
|
|
59846
59984
|
allow gate in one flag). For a non-staging remote,
|
|
59847
59985
|
pass BOTH the URL AND the allow gate:
|
|
59848
59986
|
MRRLIN_API_BASE_URL=https://... \\
|
|
59849
|
-
MRRLIN_API_ALLOW_REMOTE=1 mrrlin-mcp
|
|
59987
|
+
MRRLIN_API_ALLOW_REMOTE=1 mrrlin-mcp configure bridge
|
|
59850
59988
|
Without an allow gate, \`serve\` still refuses any
|
|
59851
59989
|
non-loopback URL (see \`mrrlin-mcp serve\` above).
|
|
59852
59990
|
URL and MRRLIN_STAGING are mutually exclusive at
|
|
@@ -59959,8 +60097,8 @@ Usage:
|
|
|
59959
60097
|
mrrlin-mcp --help Show this help.
|
|
59960
60098
|
|
|
59961
60099
|
Operator notes:
|
|
59962
|
-
-
|
|
59963
|
-
-
|
|
60100
|
+
- configure bridge is idempotent: a second run with the same target is a no-op.
|
|
60101
|
+
- configure bridge auto-detects how it was installed. Development checkouts keep
|
|
59964
60102
|
using the local dist/bin.cjs; npm installs use
|
|
59965
60103
|
\`mrrlin-mcp serve\`.
|
|
59966
60104
|
`;
|
|
@@ -59995,16 +60133,29 @@ async function main() {
|
|
|
59995
60133
|
process.exitCode = r.status ?? 1;
|
|
59996
60134
|
return;
|
|
59997
60135
|
}
|
|
59998
|
-
case "
|
|
59999
|
-
const
|
|
60136
|
+
case "configure": {
|
|
60137
|
+
const [target, ...configureRest] = rest;
|
|
60138
|
+
if (target !== "bridge") {
|
|
60139
|
+
process.stderr.write(
|
|
60140
|
+
`configure requires a target. The only supported form is:
|
|
60141
|
+
mrrlin-mcp configure bridge [--force]
|
|
60142
|
+
`
|
|
60143
|
+
);
|
|
60144
|
+
if (target !== void 0) {
|
|
60145
|
+
process.stderr.write(`Unknown configure target: ${target}
|
|
60146
|
+
`);
|
|
60147
|
+
}
|
|
60148
|
+
process.exit(2);
|
|
60149
|
+
}
|
|
60150
|
+
const force = configureRest.includes("--force");
|
|
60000
60151
|
const binPath = resolveSelfBinPath();
|
|
60001
60152
|
try {
|
|
60002
60153
|
const b = await installBrowser();
|
|
60003
|
-
process.stderr.write(`[mrrlin-mcp
|
|
60154
|
+
process.stderr.write(`[mrrlin-mcp configure bridge] install-browser ${b.action} ${b.browsersPath}
|
|
60004
60155
|
`);
|
|
60005
60156
|
} catch (error51) {
|
|
60006
60157
|
process.stderr.write(
|
|
60007
|
-
`[mrrlin-mcp
|
|
60158
|
+
`[mrrlin-mcp configure bridge] install-browser FAILED \u2014 config.toml left untouched: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
60008
60159
|
`
|
|
60009
60160
|
);
|
|
60010
60161
|
process.exit(2);
|
|
@@ -60015,18 +60166,18 @@ async function main() {
|
|
|
60015
60166
|
force,
|
|
60016
60167
|
serveEnv
|
|
60017
60168
|
});
|
|
60018
|
-
process.stderr.write(`[mrrlin-mcp
|
|
60169
|
+
process.stderr.write(`[mrrlin-mcp configure bridge] ${result.action} ${result.configPath}
|
|
60019
60170
|
`);
|
|
60020
60171
|
for (const preserved of result.preservedEnvBlocks) {
|
|
60021
60172
|
process.stderr.write(`${renderEnvPreservedNotice(preserved.blockId, preserved.envKeys)}
|
|
60022
60173
|
`);
|
|
60023
60174
|
}
|
|
60024
60175
|
for (const p of result.prompts) {
|
|
60025
|
-
process.stderr.write(`[mrrlin-mcp
|
|
60176
|
+
process.stderr.write(`[mrrlin-mcp configure bridge] prompt ${p.name}: ${p.action} ${p.promptPath}
|
|
60026
60177
|
`);
|
|
60027
60178
|
if (p.action === "skipped-modified") {
|
|
60028
60179
|
process.stderr.write(
|
|
60029
|
-
`[mrrlin-mcp
|
|
60180
|
+
`[mrrlin-mcp configure bridge] prompt ${p.name}: kept your local edits; run with --force-prompts to overwrite (or delete ${p.promptPath} to take the new bundled version)
|
|
60030
60181
|
`
|
|
60031
60182
|
);
|
|
60032
60183
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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/wiki": "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/director-e2e": "0.0.0",
|
|
29
|
-
"@mrrlin/
|
|
27
|
+
"@mrrlin/schemas": "0.0.0",
|
|
28
|
+
"@mrrlin/tsconfig": "0.0.0",
|
|
29
|
+
"@mrrlin/wiki": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|