@mrrlin-dev/mcp 0.3.42 → 0.3.46

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.
Files changed (2) hide show
  1. package/dist/bin.cjs +154 -18
  2. package/package.json +3 -2
package/dist/bin.cjs CHANGED
@@ -20672,12 +20672,18 @@ function browserPaths(homeDir = import_node_os.default.homedir()) {
20672
20672
  const root = import_node_path.default.join(homeDir, ".mrrlin", "browser");
20673
20673
  return { root, browsersPath: import_node_path.default.join(root, "browsers"), profilePath: import_node_path.default.join(root, "profile") };
20674
20674
  }
20675
- function resolvePackageBin(pkg) {
20675
+ function resolvePackageBin(pkg, binName) {
20676
20676
  const require2 = moduleRequire();
20677
20677
  const pkgJsonPath = require2.resolve(`${pkg}/package.json`);
20678
20678
  const pkgJson = require2(`${pkg}/package.json`);
20679
20679
  const bin = pkgJson.bin;
20680
- const rel = typeof bin === "string" ? bin : bin ? Object.values(bin)[0] : void 0;
20680
+ let rel;
20681
+ if (binName && bin && typeof bin !== "string") {
20682
+ rel = bin[binName];
20683
+ if (!rel) throw new Error(`${pkg} has no bin entry named ${binName}`);
20684
+ } else {
20685
+ rel = typeof bin === "string" ? bin : bin ? Object.values(bin)[0] : void 0;
20686
+ }
20681
20687
  if (!rel) throw new Error(`${pkg} has no bin entry to resolve a CLI from`);
20682
20688
  return import_node_path.default.join(import_node_path.default.dirname(pkgJsonPath), rel);
20683
20689
  }
@@ -20691,6 +20697,13 @@ function browserServeArgs(homeDir = import_node_os.default.homedir()) {
20691
20697
  env: { PLAYWRIGHT_BROWSERS_PATH: browsersPath }
20692
20698
  };
20693
20699
  }
20700
+ function externalAgentsServeArgs() {
20701
+ return {
20702
+ command: process.execPath,
20703
+ // node
20704
+ args: [resolvePackageBin("@mrrlin-dev/external-agents", "external-agents-mcp")]
20705
+ };
20706
+ }
20694
20707
  async function installBrowser(opts = {}) {
20695
20708
  const homeDir = opts.homeDir ?? import_node_os.default.homedir();
20696
20709
  const { browsersPath } = browserPaths(homeDir);
@@ -20747,8 +20760,9 @@ function mrrlinTable(binPath, distribution, serveEnv) {
20747
20760
  }
20748
20761
  return { ...base, startup_timeout_sec: 30 };
20749
20762
  }
20750
- function externalAgentsTable() {
20751
- return { command: "external-agents-mcp", args: [], startup_timeout_sec: 30 };
20763
+ function externalAgentsTable(binPath, distribution) {
20764
+ const base = distribution === "npm" ? { command: "mrrlin-mcp", args: ["external-agents-serve"] } : { command: "node", args: [binPath, "external-agents-serve"] };
20765
+ return { ...base, startup_timeout_sec: 30 };
20752
20766
  }
20753
20767
  function browserTable(binPath, distribution, homeDir) {
20754
20768
  const base = distribution === "npm" ? { command: "mrrlin-mcp", args: ["browser-serve"] } : { command: "node", args: [binPath, "browser-serve"] };
@@ -20773,8 +20787,22 @@ function managedBlocks(options, distribution) {
20773
20787
  },
20774
20788
  {
20775
20789
  id: "external-agents",
20776
- expected: externalAgentsTable(),
20777
- blockRe: blockRegex("external-agents")
20790
+ expected: externalAgentsTable(options.binPath, distribution),
20791
+ blockRe: blockRegex("external-agents"),
20792
+ // Match ONLY the exact shape this installer used to write: a bare command, optionally with
20793
+ // empty args / a startup_timeout_sec, and NOTHING else. An extra key means the operator
20794
+ // customized the block — most plausibly a hand-added `env` carrying provider API keys, which
20795
+ // renderBlock() would silently drop on rewrite (it renders strictly from `expected`). Such a
20796
+ // block must fall through to CODEX_CONFIG_CONFLICT so replacing it takes an explicit --force,
20797
+ // rather than losing an operator's credentials during a routine update. We deliberately do
20798
+ // NOT carry the env forward instead: KNOWN_MRRLIN_ENV_KEYS treats any foreign key as
20799
+ // operator-authored and never-preserve, so a carried key would turn the NEXT bare re-run into
20800
+ // a conflict and break idempotency.
20801
+ isLegacyOurs: (actual) => {
20802
+ const legacyKeys = /* @__PURE__ */ new Set(["command", "args", "startup_timeout_sec"]);
20803
+ if (!Object.keys(actual).every((key) => legacyKeys.has(key))) return false;
20804
+ return actual.command === "external-agents-mcp" && (actual.args === void 0 || Array.isArray(actual.args) && actual.args.length === 0);
20805
+ }
20778
20806
  }
20779
20807
  ];
20780
20808
  }
@@ -20917,6 +20945,12 @@ async function installCodex(options) {
20917
20945
  if (action === "noop") action = "env-preserved";
20918
20946
  continue;
20919
20947
  }
20948
+ if (block.isLegacyOurs?.(existingTable)) {
20949
+ content = replaceBlock(content, block.blockRe, renderBlock(block));
20950
+ action = "replaced";
20951
+ mutated = true;
20952
+ continue;
20953
+ }
20920
20954
  if (!options.force) {
20921
20955
  const err = {
20922
20956
  code: "CODEX_CONFIG_CONFLICT",
@@ -37497,6 +37531,38 @@ var mrrlinSchedulerPendingReasonSchema = external_exports.enum([
37497
37531
  "verification_due",
37498
37532
  "wake_floor_passed"
37499
37533
  ]);
37534
+ var mrrlinAutonomousStepSchema = external_exports.enum([
37535
+ "noop",
37536
+ "needs_human",
37537
+ "enqueue_spec_draft",
37538
+ "enqueue_consensus",
37539
+ "auto_approve_spec_and_run",
37540
+ "create_execution_run"
37541
+ ]);
37542
+ var mrrlinAutonomousStepReasonSchema = external_exports.enum([
37543
+ "task_terminal",
37544
+ "active_run_exists",
37545
+ "blocked_by_active",
37546
+ "draft_running",
37547
+ "consensus_running",
37548
+ "spec_missing",
37549
+ "spec_draft_failed",
37550
+ "spec_not_specs_folder",
37551
+ "awaiting_clarification",
37552
+ "consensus_blocked",
37553
+ "consensus_disabled_spec_not_ready",
37554
+ "spec_approval_pending",
37555
+ "non_code_execution_unsupported"
37556
+ ]);
37557
+ var mrrlinAutonomousStepVerdictSchema = external_exports.object({
37558
+ step: mrrlinAutonomousStepSchema,
37559
+ reason: mrrlinAutonomousStepReasonSchema.nullable(),
37560
+ // Additive: when reason is "spec_draft_failed" or "consensus_blocked" (the two reasons that park
37561
+ // a task until a human intervenes), the attempt_count of the async_jobs row that produced that
37562
+ // outcome — so the operator can see "failed x3" and judge whether a retry mechanism is needed.
37563
+ // null for every other reason/step (not applicable), and also null if the job could not be found.
37564
+ failureCount: external_exports.number().int().min(0).nullable()
37565
+ });
37500
37566
  var mrrlinSchedulerPendingTaskSchema = external_exports.object({
37501
37567
  dueAt: external_exports.string().datetime(),
37502
37568
  reason: mrrlinSchedulerPendingReasonSchema,
@@ -37506,7 +37572,12 @@ var mrrlinSchedulerPendingTaskSchema = external_exports.object({
37506
37572
  // trigger at all. Consumers must read this instead of re-deriving due-ness by re-parsing `dueAt`
37507
37573
  // against "now" — a task with no trigger has `dueAt === evaluatedAt`, which a naive `dueAt <= now`
37508
37574
  // re-derivation would misclassify as due.
37509
- due: external_exports.boolean()
37575
+ due: external_exports.boolean(),
37576
+ // Additive: the REAL admission verdict for this row, computed only inside the tick's bounded
37577
+ // per-cycle slice (`resolveSchedulerProjectLimit`). `null` means "not evaluated this cycle" —
37578
+ // the row is a genuine actionable candidate by the cheap classifier, but beyond the bound the
37579
+ // tick actually looks at, so no real verdict exists yet. Never infer readiness from `null`.
37580
+ verdict: mrrlinAutonomousStepVerdictSchema.nullable()
37510
37581
  });
37511
37582
  var mrrlinSchedulerParkedReasonSchema = external_exports.enum([
37512
37583
  "running",
@@ -37522,6 +37593,32 @@ var mrrlinSchedulerParkedTaskSchema = external_exports.object({
37522
37593
  inboxItemId: external_exports.string().nullable(),
37523
37594
  runId: mrrlinExecutionRunIdSchema.nullable()
37524
37595
  });
37596
+ var mrrlinInFlightSchedulerEntrySchema = external_exports.discriminatedUnion("kind", [
37597
+ external_exports.object({
37598
+ kind: external_exports.literal("run"),
37599
+ taskId: mrrlinTaskIdSchema,
37600
+ runId: mrrlinExecutionRunIdSchema,
37601
+ status: external_exports.string(),
37602
+ // Additive: the task's own title, resolved server-side from the same task list the handler
37603
+ // already loaded for this request (no extra query). null only when the task itself could not
37604
+ // be found (should not happen for a `run` entry, whose taskId is required, but the wire
37605
+ // contract stays honest about it rather than assuming).
37606
+ taskTitle: external_exports.string().nullable()
37607
+ }),
37608
+ external_exports.object({
37609
+ kind: external_exports.literal("async_job"),
37610
+ taskId: mrrlinTaskIdSchema.nullable(),
37611
+ jobId: external_exports.string(),
37612
+ toolName: external_exports.enum(["draft_task_spec", "run_spec_consensus"]),
37613
+ // Additive: same resolution as the `run` variant above. null when `taskId` is null (a job
37614
+ // genuinely untied to a visible task) or the task could not be found.
37615
+ taskTitle: external_exports.string().nullable()
37616
+ })
37617
+ ]);
37618
+ var mrrlinIneligibleTaskSchema = external_exports.object({
37619
+ task: mrrlinTaskSchema,
37620
+ autonomyLevel: mrrlinAutonomyLevelSchema
37621
+ });
37525
37622
  var schedulerPendingTasksResponseSchema = external_exports.object({
37526
37623
  data: external_exports.object({
37527
37624
  evaluatedAt: external_exports.string().datetime(),
@@ -37529,7 +37626,21 @@ var schedulerPendingTasksResponseSchema = external_exports.object({
37529
37626
  parked: external_exports.array(mrrlinSchedulerParkedTaskSchema),
37530
37627
  slots: external_exports.number().int(),
37531
37628
  perProjectLimit: external_exports.number().int().min(1),
37532
- policy: mrrlinProjectSchedulerPolicySchema
37629
+ policy: mrrlinProjectSchedulerPolicySchema,
37630
+ // Additive (see mrrlinInFlightSchedulerEntrySchema doc): the actual runs/jobs occupying
37631
+ // autonomous capacity right now. `slots` is derived from THIS SAME list server-side, so the
37632
+ // count and the list can never disagree.
37633
+ inFlight: external_exports.array(mrrlinInFlightSchedulerEntrySchema),
37634
+ // Additive: tasks the tick never even considers (autonomyLevel outside
37635
+ // policy.eligibleAutonomyLevels), excluding terminal tasks.
37636
+ ineligible: external_exports.array(mrrlinIneligibleTaskSchema),
37637
+ // Additive: how many non-terminal tasks were excluded from BOTH `pending` and `parked` because
37638
+ // their assignee is set and does not match the project operator (github.operator). A COUNT
37639
+ // only, deliberately — the operator asked not to show a list of other people's tasks, just a
37640
+ // disclosure that some exist, so a misconfigured `github.operator` (e.g. it doesn't match the
37641
+ // person actually running the project) is visible as a large number instead of the autopilot
37642
+ // silently doing nothing.
37643
+ excludedByOwnershipCount: external_exports.number().int().min(0)
37533
37644
  })
37534
37645
  });
37535
37646
  var wakeDirectorRequestSchema = external_exports.object({
@@ -38999,7 +39110,7 @@ function startCodexProbe(deps = {}) {
38999
39110
  }
39000
39111
 
39001
39112
  // src/_generated/version.ts
39002
- var PKG_VERSION = "0.3.42";
39113
+ var PKG_VERSION = "0.3.46";
39003
39114
 
39004
39115
  // src/api-base-url.ts
39005
39116
  var DEFAULT_API_BASE_URL = "http://127.0.0.1:8787";
@@ -39292,6 +39403,19 @@ async function collectAgentPoolSnapshot(deps) {
39292
39403
  );
39293
39404
  }
39294
39405
 
39406
+ // src/external-agents-bin.ts
39407
+ function resolveExternalAgentsCliInvocation(override) {
39408
+ if (override && override.trim()) return { command: override, prefixArgs: [] };
39409
+ try {
39410
+ return {
39411
+ command: "node",
39412
+ prefixArgs: [resolvePackageBin("@mrrlin-dev/external-agents", "external-agents")]
39413
+ };
39414
+ } catch {
39415
+ return { command: "external-agents", prefixArgs: [] };
39416
+ }
39417
+ }
39418
+
39295
39419
  // src/agent-pool-loop.ts
39296
39420
  var DEFAULT_INTERVAL_MS2 = 6e4;
39297
39421
  function agentPoolLoopDisabled(env = process.env) {
@@ -39299,7 +39423,7 @@ function agentPoolLoopDisabled(env = process.env) {
39299
39423
  }
39300
39424
  function defaultRunCli(args) {
39301
39425
  const cli = process.env["EXTERNAL_AGENTS_CLI"];
39302
- const [cmd, baseArgs] = cli ? ["node", [cli, ...args]] : ["external-agents", args];
39426
+ const { command, prefixArgs } = cli ? { command: "node", prefixArgs: [cli] } : resolveExternalAgentsCliInvocation();
39303
39427
  return new Promise((resolve) => {
39304
39428
  let stdout = "";
39305
39429
  let settled = false;
@@ -39310,7 +39434,7 @@ function defaultRunCli(args) {
39310
39434
  }
39311
39435
  };
39312
39436
  try {
39313
- const child = (0, import_node_child_process5.spawn)(cmd, baseArgs, { stdio: ["ignore", "pipe", "ignore"] });
39437
+ const child = (0, import_node_child_process5.spawn)(command, [...prefixArgs, ...args], { stdio: ["ignore", "pipe", "ignore"] });
39314
39438
  const timer = setTimeout(() => {
39315
39439
  try {
39316
39440
  child.kill("SIGKILL");
@@ -53172,7 +53296,7 @@ var import_node_child_process11 = require("node:child_process");
53172
53296
  function errMessage3(err) {
53173
53297
  return err instanceof Error ? err.message : String(err);
53174
53298
  }
53175
- function runExternalAgents(bin, args, timeoutMs, spawn3) {
53299
+ function runExternalAgents(bin, args, label, timeoutMs, spawn3) {
53176
53300
  return new Promise((resolve) => {
53177
53301
  let settled = false;
53178
53302
  const timerRef = { id: void 0 };
@@ -53198,20 +53322,20 @@ function runExternalAgents(bin, args, timeoutMs, spawn3) {
53198
53322
  let stdout = "";
53199
53323
  timerRef.id = setTimeout(() => {
53200
53324
  child.kill("SIGKILL");
53201
- finish({ code: null, stdout, error: `external-agents ${args[0]} timed out after ${timeoutMs}ms` });
53325
+ finish({ code: null, stdout, error: `external-agents ${label} timed out after ${timeoutMs}ms` });
53202
53326
  }, timeoutMs);
53203
53327
  child.stdout?.on("data", (chunk) => {
53204
53328
  stdout += chunk.toString();
53205
53329
  });
53206
53330
  child.stderr?.on("data", () => {
53207
53331
  });
53208
- child.on("error", (err) => finish({ code: null, stdout, error: `external-agents ${args[0]} error: ${errMessage3(err)}` }));
53332
+ child.on("error", (err) => finish({ code: null, stdout, error: `external-agents ${label} error: ${errMessage3(err)}` }));
53209
53333
  child.on("close", (code) => finish({ code, stdout }));
53210
53334
  });
53211
53335
  }
53212
53336
  async function pickPanel(input, deps) {
53213
53337
  const spawn3 = deps?.spawn ?? import_node_child_process11.spawn;
53214
- const bin = deps?.externalAgentsBin ?? "external-agents";
53338
+ const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
53215
53339
  const args = [
53216
53340
  "pick",
53217
53341
  "--tier-prefer",
@@ -53223,7 +53347,7 @@ async function pickPanel(input, deps) {
53223
53347
  "--exclude-providers",
53224
53348
  input.excludeProvider
53225
53349
  ];
53226
- const r = await runExternalAgents(bin, args, input.timeoutMs, spawn3);
53350
+ const r = await runExternalAgents(command, [...prefixArgs, ...args], "pick", input.timeoutMs, spawn3);
53227
53351
  if (r.error) return { ok: false, error: r.error };
53228
53352
  if (r.code === 3) return { ok: true, agents: [] };
53229
53353
  if (r.code !== 0) return { ok: false, error: `external-agents pick exited ${r.code}` };
@@ -53232,9 +53356,9 @@ async function pickPanel(input, deps) {
53232
53356
  }
53233
53357
  async function dispatchHead(agentId, prompt, timeoutMs, deps) {
53234
53358
  const spawn3 = deps?.spawn ?? import_node_child_process11.spawn;
53235
- const bin = deps?.externalAgentsBin ?? "external-agents";
53359
+ const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
53236
53360
  const args = ["dispatch", agentId, "--json", prompt];
53237
- const r = await runExternalAgents(bin, args, timeoutMs, spawn3);
53361
+ const r = await runExternalAgents(command, [...prefixArgs, ...args], "dispatch", timeoutMs, spawn3);
53238
53362
  if (r.error) return { ok: false, error: r.error };
53239
53363
  const lines2 = r.stdout.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
53240
53364
  const jsonLine = [...lines2].reverse().find((l) => l[0] === "{");
@@ -55902,6 +56026,12 @@ Usage:
55902
56026
  command install-codex wires for the
55903
56027
  [mcp_servers.mrrlin-browser] block.
55904
56028
 
56029
+ mrrlin-mcp external-agents-serve
56030
+ Launch the bundled @mrrlin-dev/external-agents
56031
+ MCP server (multi-LLM dispatch pool). This is the
56032
+ command install-codex wires for the
56033
+ [mcp_servers.external-agents] block.
56034
+
55905
56035
  mrrlin-mcp install-codex Idempotently register Mrrlin in
55906
56036
  [--force] ~/.codex/config.toml (or CODEX_HOME). Adds an
55907
56037
  [mcp_servers.mrrlin] block. Development
@@ -56057,6 +56187,12 @@ async function main() {
56057
56187
  process.exitCode = r.status ?? 1;
56058
56188
  return;
56059
56189
  }
56190
+ case "external-agents-serve": {
56191
+ const { command: command2, args } = externalAgentsServeArgs();
56192
+ const r = (0, import_node_child_process13.spawnSync)(command2, args, { stdio: "inherit", env: process.env });
56193
+ process.exitCode = r.status ?? 1;
56194
+ return;
56195
+ }
56060
56196
  case "install-codex": {
56061
56197
  const force = rest.includes("--force");
56062
56198
  const binPath = resolveSelfBinPath();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/mcp",
3
- "version": "0.3.42",
3
+ "version": "0.3.46",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"
@@ -21,16 +21,17 @@
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/director-e2e": "0.0.0",
25
26
  "@mrrlin/wiki": "0.0.0",
26
27
  "@mrrlin/schemas": "0.0.0",
27
- "@mrrlin/client": "0.0.0",
28
28
  "@mrrlin/codex-client": "0.0.0",
29
29
  "@mrrlin/tsconfig": "0.0.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@iarna/toml": "^2.2.5",
33
33
  "@modelcontextprotocol/sdk": "^1.0.0",
34
+ "@mrrlin-dev/external-agents": "^0.33.1",
34
35
  "@playwright/mcp": "0.0.76",
35
36
  "playwright-core": "1.61.0-alpha-1781023400000",
36
37
  "proper-lockfile": "^4.1.2",