@mrrlin-dev/mcp 0.3.55 → 0.3.56

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 +505 -158
  2. package/package.json +5 -5
package/dist/bin.cjs CHANGED
@@ -21153,11 +21153,11 @@ ${block}`;
21153
21153
  }
21154
21154
 
21155
21155
  // src/director-bridge.ts
21156
- var import_node_fs14 = __toESM(require("node:fs"), 1);
21156
+ var import_node_fs15 = __toESM(require("node:fs"), 1);
21157
21157
  var import_node_http = __toESM(require("node:http"), 1);
21158
21158
  var import_node_os9 = __toESM(require("node:os"), 1);
21159
21159
  var import_node_path18 = __toESM(require("node:path"), 1);
21160
- var import_node_child_process10 = require("node:child_process");
21160
+ var import_node_child_process11 = require("node:child_process");
21161
21161
  var import_node_crypto8 = __toESM(require("node:crypto"), 1);
21162
21162
 
21163
21163
  // src/turn-replay-buffer.ts
@@ -21289,11 +21289,25 @@ function createSuspensionDetector(opts) {
21289
21289
  const t = now();
21290
21290
  const gap = t - last;
21291
21291
  last = t;
21292
- if (gap >= gapMs) suspended += gap - tickMs;
21292
+ if (gap >= gapMs) {
21293
+ suspended += gap - tickMs;
21294
+ }
21293
21295
  }, tickMs);
21294
21296
  timer.unref?.();
21297
+ const settle = () => {
21298
+ const t = now();
21299
+ const gap = t - last;
21300
+ if (gap >= gapMs) {
21301
+ suspended += gap - tickMs;
21302
+ last = t;
21303
+ }
21304
+ };
21295
21305
  return {
21296
- suspendedMs: () => suspended,
21306
+ suspendedMs: () => {
21307
+ settle();
21308
+ return suspended;
21309
+ },
21310
+ settle,
21297
21311
  dispose: () => clearI(timer)
21298
21312
  };
21299
21313
  }
@@ -21368,20 +21382,22 @@ function transition(state, event) {
21368
21382
 
21369
21383
  // src/bridge-tombstone.ts
21370
21384
  var DEFAULT_TOMBSTONE_TTL_MS = 36e5;
21371
- function writeTombstone(tombstones, directorSessionId, buf, now) {
21385
+ function writeTombstone(tombstones, directorSessionId, buf, now, suspendedMsNow) {
21372
21386
  tombstones.set(directorSessionId, {
21373
21387
  turnId: buf.turnId,
21374
21388
  lastSeq: buf.lastSeq,
21375
- expiredAt: now
21389
+ expiredAt: now,
21390
+ expiredSuspendedMs: suspendedMsNow
21376
21391
  });
21377
21392
  }
21378
21393
  function lookupTombstone(tombstones, directorSessionId) {
21379
21394
  return tombstones.get(directorSessionId) ?? null;
21380
21395
  }
21381
- function sweepExpiredTombstones(tombstones, now, ttlMs = DEFAULT_TOMBSTONE_TTL_MS) {
21396
+ function sweepExpiredTombstones(tombstones, now, ttlMs = DEFAULT_TOMBSTONE_TTL_MS, suspendedMsNow) {
21382
21397
  let removed = 0;
21383
21398
  for (const [sid, t] of tombstones) {
21384
- if (now - t.expiredAt > ttlMs) {
21399
+ const suspendedSinceWrite = Math.max(0, suspendedMsNow - t.expiredSuspendedMs);
21400
+ if (now - t.expiredAt - suspendedSinceWrite > ttlMs) {
21385
21401
  tombstones.delete(sid);
21386
21402
  removed += 1;
21387
21403
  }
@@ -36479,6 +36495,7 @@ var openApiOperationIds = {
36479
36495
  requeueAsyncJob: "requeueAsyncJob",
36480
36496
  createGithubBranch: "createGithubBranch",
36481
36497
  createGithubPullRequest: "createGithubPullRequest",
36498
+ getGithubPullRequest: "getGithubPullRequest",
36482
36499
  getGithubWorkflowRunLatest: "getGithubWorkflowRunLatest",
36483
36500
  getGithubLatestDeployment: "getGithubLatestDeployment",
36484
36501
  mintGithubPushToken: "mintGithubPushToken",
@@ -36540,7 +36557,9 @@ var openApiOperationIds = {
36540
36557
  markSpecReady: "markSpecReady",
36541
36558
  resetDirectorThread: "resetDirectorThread",
36542
36559
  claimExecutionRun: "claimExecutionRun",
36560
+ reclaimExecutionRunLease: "reclaimExecutionRunLease",
36543
36561
  requeueExecutionRun: "requeueExecutionRun",
36562
+ restartExecutionRun: "restartExecutionRun",
36544
36563
  resolveHandoff: "resolveHandoff",
36545
36564
  touchExecutionRun: "touchExecutionRun",
36546
36565
  issueAgentCredential: "issueAgentCredential",
@@ -36824,6 +36843,14 @@ var mrrlinExecutionRunStatusSchema = external_exports.enum([
36824
36843
  "handoff_queued",
36825
36844
  "cancelled"
36826
36845
  ]);
36846
+ var mrrlinRunBlockingReasonCodeSchema = external_exports.enum([
36847
+ "local_checkout_missing",
36848
+ "task_blocked",
36849
+ "default_branch_unresolved",
36850
+ "execution_turn_timeout",
36851
+ "deliverable_gate_exhausted",
36852
+ "unknown"
36853
+ ]);
36827
36854
  var mrrlinExecutionArtifactKindSchema = external_exports.enum([
36828
36855
  "plan",
36829
36856
  "checkpoint",
@@ -36838,7 +36865,8 @@ var mrrlinExecutionArtifactKindSchema = external_exports.enum([
36838
36865
  "verification",
36839
36866
  "produced_url",
36840
36867
  "resume_spec",
36841
- "deliverable"
36868
+ "deliverable",
36869
+ "pull_request_state"
36842
36870
  ]);
36843
36871
  var mrrlinDeployAttemptPayloadSchema = external_exports.object({
36844
36872
  env: external_exports.enum(["dev", "prod"]),
@@ -36991,6 +37019,27 @@ var mrrlinGithubPullRequestSchema = external_exports.object({
36991
37019
  title: external_exports.string().min(1),
36992
37020
  url: external_exports.string().url()
36993
37021
  });
37022
+ var mrrlinGithubPullRequestCheckStateSchema = external_exports.enum([
37023
+ "passing",
37024
+ "failing",
37025
+ "running",
37026
+ "none",
37027
+ "unknown"
37028
+ ]);
37029
+ var mrrlinGithubPullRequestCheckSourceSchema = external_exports.enum(["check_run", "commit_status"]);
37030
+ var mrrlinGithubPullRequestObservedSchema = external_exports.object({
37031
+ checks: mrrlinGithubPullRequestCheckStateSchema,
37032
+ failingChecks: external_exports.array(external_exports.object({
37033
+ name: external_exports.string().min(1),
37034
+ source: mrrlinGithubPullRequestCheckSourceSchema
37035
+ })),
37036
+ headSha: external_exports.string().min(1),
37037
+ merged: external_exports.boolean(),
37038
+ mergedAt: external_exports.string().datetime().nullable(),
37039
+ number: external_exports.number().int().positive(),
37040
+ state: external_exports.string().min(1),
37041
+ url: external_exports.string().url()
37042
+ });
36994
37043
  var mrrlinGithubWorkflowSchema = external_exports.object({
36995
37044
  id: external_exports.union([external_exports.number().int().positive(), external_exports.string().min(1)]),
36996
37045
  name: external_exports.string().min(1),
@@ -37287,6 +37336,9 @@ var githubCreatePullRequestRequestSchema = external_exports.object({
37287
37336
  var githubCreatePullRequestResponseSchema = external_exports.object({
37288
37337
  data: mrrlinGithubPullRequestSchema
37289
37338
  });
37339
+ var githubPullRequestNumberParamSchema = external_exports.object({
37340
+ pullNumber: external_exports.coerce.number().int().positive()
37341
+ });
37290
37342
  var githubWorkflowIdParamSchema = external_exports.object({
37291
37343
  workflowId: external_exports.string().min(1)
37292
37344
  });
@@ -37300,6 +37352,9 @@ var githubWorkflowDispatchResponseSchema = external_exports.object({
37300
37352
  var githubWorkflowListResponseSchema = external_exports.object({
37301
37353
  data: external_exports.array(mrrlinGithubWorkflowSchema)
37302
37354
  });
37355
+ var githubPullRequestResponseSchema = external_exports.object({
37356
+ data: mrrlinGithubPullRequestObservedSchema
37357
+ });
37303
37358
  var githubWorkflowRunQuerySchema = external_exports.object({
37304
37359
  workflowId: external_exports.string().min(1),
37305
37360
  branch: external_exports.string().min(1),
@@ -37400,6 +37455,7 @@ var mrrlinExecutionRunSchema = external_exports.object({
37400
37455
  actor: external_exports.string().min(1),
37401
37456
  baseSha: external_exports.string().min(1).nullable(),
37402
37457
  branch: external_exports.string().min(1).nullable(),
37458
+ blockingReason: mrrlinRunBlockingReasonCodeSchema.nullable(),
37403
37459
  checkpointCursor: external_exports.string().min(1).nullable(),
37404
37460
  claimedAt: external_exports.string().datetime().nullable(),
37405
37461
  codeExecution: external_exports.boolean(),
@@ -37414,6 +37470,7 @@ var mrrlinExecutionRunSchema = external_exports.object({
37414
37470
  lastActor: external_exports.string().min(1).nullable(),
37415
37471
  leaseId: external_exports.string().min(1).nullable(),
37416
37472
  prerequisites: external_exports.array(mrrlinRunPrerequisiteSchema),
37473
+ prState: external_exports.enum(["open", "merged", "closed"]).optional(),
37417
37474
  prodEnvUrl: external_exports.string().min(1).nullable(),
37418
37475
  prodRunUrl: external_exports.string().min(1).nullable(),
37419
37476
  prodWorkflowId: external_exports.number().int().positive().nullable(),
@@ -37443,6 +37500,7 @@ var mrrlinExecutionRunCreateSchema = external_exports.object({
37443
37500
  var mrrlinExecutionRunUpdateSchema = external_exports.object({
37444
37501
  baseSha: external_exports.string().min(1).nullable().optional(),
37445
37502
  branch: external_exports.string().min(1).nullable().optional(),
37503
+ blockingReason: mrrlinRunBlockingReasonCodeSchema.nullable().optional(),
37446
37504
  checkpointCursor: external_exports.string().min(1).nullable().optional(),
37447
37505
  devEnvUrl: external_exports.string().min(1).nullable().optional(),
37448
37506
  devRunUrl: external_exports.string().min(1).nullable().optional(),
@@ -37469,6 +37527,7 @@ var mrrlinResumeSpecPayloadSchema = external_exports.object({
37469
37527
  class: external_exports.literal("tool_assisted"),
37470
37528
  question: external_exports.string().min(1),
37471
37529
  prerequisites: external_exports.array(external_exports.object({ mcp: external_exports.string().min(1), reason: external_exports.string().min(1) })).default([]),
37530
+ reasonCode: mrrlinRunBlockingReasonCodeSchema.optional(),
37472
37531
  steps: external_exports.array(external_exports.string().min(1)).min(1),
37473
37532
  runId: mrrlinExecutionRunIdSchema,
37474
37533
  artifactId: mrrlinExecutionArtifactIdSchema,
@@ -37498,7 +37557,13 @@ var mrrlinResolveHandoffSchema = external_exports.object({
37498
37557
  }
37499
37558
  });
37500
37559
  var resolveHandoffResponseSchema = external_exports.object({ data: mrrlinExecutionRunSchema });
37501
- var createExecutionRunHandoffSchema = external_exports.object({ reason: external_exports.string().min(1) });
37560
+ var createExecutionRunHandoffSchema = external_exports.object({
37561
+ reason: external_exports.string().min(1),
37562
+ reasonCode: mrrlinRunBlockingReasonCodeSchema.optional()
37563
+ });
37564
+ var mrrlinRestartExecutionRunSchema = external_exports.object({
37565
+ restartedBy: external_exports.string().min(1)
37566
+ });
37502
37567
  var mrrlinExecutionRunTouchSchema = external_exports.object({
37503
37568
  leaseId: external_exports.string().min(1)
37504
37569
  });
@@ -38363,6 +38428,10 @@ function createMrrlinClient(config2) {
38363
38428
  const body = await request(`${projectPath(projectSlug)}/github/workflow-runs/latest?${params.toString()}`);
38364
38429
  return githubWorkflowRunResponseSchema.parse(body).data;
38365
38430
  },
38431
+ async getGithubPullRequest(projectSlug, pullNumber) {
38432
+ const body = await request(`${projectPath(projectSlug)}/github/pull-requests/${encodeURIComponent(String(pullNumber))}`);
38433
+ return githubPullRequestResponseSchema.parse(body).data;
38434
+ },
38366
38435
  async getGithubLatestDeployment(projectSlug, query) {
38367
38436
  const params = new URLSearchParams({ environment: query.environment });
38368
38437
  const body = await request(`${projectPath(projectSlug)}/github/deployments/latest?${params.toString()}`);
@@ -38696,7 +38765,7 @@ function createMrrlinClient(config2) {
38696
38765
  async requeueExecutionRun(projectSlug, runId, staleSeconds) {
38697
38766
  try {
38698
38767
  const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/requeue`, {
38699
- body: JSON.stringify({ staleSeconds }),
38768
+ body: JSON.stringify(mrrlinExecutionRunRequeueSchema.parse({ staleSeconds })),
38700
38769
  headers: { "content-type": "application/json" },
38701
38770
  method: "POST"
38702
38771
  });
@@ -38707,6 +38776,28 @@ function createMrrlinClient(config2) {
38707
38776
  throw error51;
38708
38777
  }
38709
38778
  },
38779
+ async reclaimExecutionRunLease(projectSlug, runId, staleSeconds) {
38780
+ try {
38781
+ const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/reclaim-lease`, {
38782
+ body: JSON.stringify(mrrlinExecutionRunRequeueSchema.parse({ staleSeconds })),
38783
+ headers: { "content-type": "application/json" },
38784
+ method: "POST"
38785
+ });
38786
+ return executionRunResponseSchema.parse(body).data;
38787
+ } catch (error51) {
38788
+ if (error51 instanceof MrrlinApiError && error51.status === 409 && error51.code === "RUN_LEASE_NOT_STALE")
38789
+ return null;
38790
+ throw error51;
38791
+ }
38792
+ },
38793
+ async restartExecutionRun(projectSlug, runId, input) {
38794
+ const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/restart`, {
38795
+ body: JSON.stringify(mrrlinRestartExecutionRunSchema.parse(input)),
38796
+ headers: { "content-type": "application/json" },
38797
+ method: "POST"
38798
+ });
38799
+ return executionRunResponseSchema.parse(body).data;
38800
+ },
38710
38801
  async touchExecutionRun(projectSlug, runId, leaseId) {
38711
38802
  try {
38712
38803
  const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/touch`, {
@@ -39359,7 +39450,7 @@ function startCodexProbe(deps = {}) {
39359
39450
  }
39360
39451
 
39361
39452
  // src/_generated/version.ts
39362
- var PKG_VERSION = "0.3.55";
39453
+ var PKG_VERSION = "0.3.56";
39363
39454
 
39364
39455
  // src/api-base-url-migrate.ts
39365
39456
  var import_node_fs2 = __toESM(require("node:fs"), 1);
@@ -42186,6 +42277,7 @@ ${lines2.join("\n")}`;
42186
42277
 
42187
42278
  // src/execution-consumer.ts
42188
42279
  var DEFAULT_STALE_SECONDS = 300;
42280
+ var DEFAULT_MAX_LEASE_RECLAIMS = 3;
42189
42281
  var DEFAULT_TOUCH_INTERVAL_MS = 6e4;
42190
42282
  var DEFAULT_CLAIM_INTERVAL_SECONDS = 30;
42191
42283
  var DEFAULT_EXEC_TURN_IDLE_TIMEOUT_SECONDS = 600;
@@ -42276,8 +42368,8 @@ function buildCompletionTurnPrompt(taskId, currentStatus) {
42276
42368
  "If the work is NOT complete \u2014 be honest, do not close incomplete work:",
42277
42369
  "1. Call update_task with the status that matches why it is not complete, and explain what remains",
42278
42370
  " in the notes:",
42279
- " - if the code work itself is done and only a PR or merge remains, use status=awaiting_verification;",
42280
- " - if you are blocked and need an operator decision before the work can continue, use status=needs_human.",
42371
+ " - if the code work itself is done and only an open PR still awaits operator merge or follow-up, use status=awaiting_verification;",
42372
+ " - if you are blocked and need an operator decision before the work can continue, use status=handoff_needed.",
42281
42373
  "2. Append a handoff_judgement artifact explaining what the operator needs to do.",
42282
42374
  "",
42283
42375
  "You must pick one of these paths \u2014 do not leave the task in_progress."
@@ -42301,9 +42393,9 @@ var BASE_INSTRUCTIONS_PREFIX = [
42301
42393
  "- For a CODE task the run opens a draft PR for you after your turn; opening that PR is NOT",
42302
42394
  " completion. Do NOT set closed_done for code work \u2014 leave the task open; the run parks at",
42303
42395
  " `pull_request` (waiting for merge) and the operator merges. Closure happens after merge.",
42304
- "- Pick the right open status when you stop a CODE task: if the code work itself is done and only a",
42305
- " PR or merge remains, leave the task at `awaiting_verification`; if you are blocked and a human",
42306
- " must decide before the work can continue, leave it at `needs_human`.",
42396
+ "- Pick the right open status when you stop a CODE task: if the code work itself is done and only an",
42397
+ " already-open PR still awaits operator merge or follow-up, leave the task at `awaiting_verification`;",
42398
+ " if you are blocked and a human must decide before the work can continue, leave it at `handoff_needed`.",
42307
42399
  "",
42308
42400
  "File artifacts:",
42309
42401
  "- When your output is a file (markdown report, HTML, screenshot), upload it with",
@@ -42549,14 +42641,19 @@ async function assertRunCompletionReady(deps, projectSlug, taskId, runId, allowe
42549
42641
  task
42550
42642
  };
42551
42643
  }
42552
- const artifacts = await deps.client.listExecutionArtifacts(projectSlug, runId);
42553
- const hasEvidence = artifacts.some(
42554
- (artifact) => COMPLETION_EVIDENCE_KINDS.has(artifact.kind) && !isBridgeGeneratedArtifact(artifact)
42644
+ const runs = await deps.client.listExecutionRuns(projectSlug, { taskId });
42645
+ const runIds = new Set(runs.map((run3) => run3.id));
42646
+ runIds.add(runId);
42647
+ const artifactsNested = await Promise.all(
42648
+ [...runIds].map(async (candidateRunId) => deps.client.listExecutionArtifacts(projectSlug, candidateRunId))
42649
+ );
42650
+ const hasEvidence = artifactsNested.flat().some(
42651
+ (artifact) => COMPLETION_EVIDENCE_KINDS.has(artifact.kind) && !isBridgeGeneratedArtifact(artifact) && runIds.has(artifact.runId)
42555
42652
  );
42556
42653
  if (!hasEvidence) {
42557
42654
  return {
42558
42655
  ok: false,
42559
- reason: `Codex turn completed and task ${task.id} closed, but run ${runId} has no durable completion evidence artifact.`,
42656
+ reason: `Codex turn completed and task ${task.id} closed, but task lineage for run ${runId} has no durable completion evidence artifact.`,
42560
42657
  task
42561
42658
  };
42562
42659
  }
@@ -42699,7 +42796,8 @@ async function driveRun(deps, projectSlug, runId) {
42699
42796
  task,
42700
42797
  runId,
42701
42798
  leaseId,
42702
- `task_blocked: run ${runId} targets ${task.id} but blocker tasks could not be loaded: ${missingBlockers.join(", ")}`
42799
+ `task_blocked: run ${runId} targets ${task.id} but blocker tasks could not be loaded: ${missingBlockers.join(", ")}`,
42800
+ "task_blocked"
42703
42801
  );
42704
42802
  return;
42705
42803
  }
@@ -42711,7 +42809,8 @@ async function driveRun(deps, projectSlug, runId) {
42711
42809
  task,
42712
42810
  runId,
42713
42811
  leaseId,
42714
- `task_blocked: run ${runId} targets ${task.id} but active blockers remain open: ${activeBlockers.join(", ")}`
42812
+ `task_blocked: run ${runId} targets ${task.id} but active blockers remain open: ${activeBlockers.join(", ")}`,
42813
+ "task_blocked"
42715
42814
  );
42716
42815
  return;
42717
42816
  }
@@ -42748,7 +42847,7 @@ async function driveRun(deps, projectSlug, runId) {
42748
42847
  2. If you find a likely match, tell the operator where you think it is and ask them to confirm \u2014 e.g. "For code changes I need this project's repo checked out here. I think it's at <path> \u2014 should I register that one, or do you want to point me at a different path?"
42749
42848
  3. When the operator confirms a path, call register_local_checkout({ projectSlug: "${projectSlug}", path: "<absolute path>", repo: "<this project's GitHub repo>" }).
42750
42849
  4. If no candidate is found, ask the operator for the absolute path to the checkout (or to clone the repo first), then register it. Once a checkout is registered, the run can be re-queued.`;
42751
- await failRun(deps, projectSlug, task, runId, leaseId, reason);
42850
+ await failRun(deps, projectSlug, task, runId, leaseId, reason, "local_checkout_missing");
42752
42851
  return;
42753
42852
  }
42754
42853
  try {
@@ -42782,7 +42881,15 @@ async function driveRun(deps, projectSlug, runId) {
42782
42881
  } catch (error51) {
42783
42882
  const detail = error51 instanceof Error ? error51.message : String(error51);
42784
42883
  const reason = `worktree_setup_failed: run ${runId} could not provision a code-execution worktree for project ${projectSlug}: ${detail}. (A missing default branch surfaces here as default_branch_unresolved; the operator must resolve the repo state before re-queuing.)`;
42785
- await failRun(deps, projectSlug, task, runId, leaseId, reason);
42884
+ await failRun(
42885
+ deps,
42886
+ projectSlug,
42887
+ task,
42888
+ runId,
42889
+ leaseId,
42890
+ reason,
42891
+ detail.includes("default_branch_unresolved") ? "default_branch_unresolved" : void 0
42892
+ );
42786
42893
  return;
42787
42894
  }
42788
42895
  } else {
@@ -42821,7 +42928,15 @@ async function driveRun(deps, projectSlug, runId) {
42821
42928
  if (!resumed) {
42822
42929
  const primingBudgetMs = remainingTurnBudgetMs();
42823
42930
  if (primingBudgetMs <= 0) {
42824
- await failRun(deps, projectSlug, task, runId, leaseId, executionTurnBudgetMessage(execRunMaxMs()));
42931
+ await failRun(
42932
+ deps,
42933
+ projectSlug,
42934
+ task,
42935
+ runId,
42936
+ leaseId,
42937
+ executionTurnBudgetMessage(execRunMaxMs()),
42938
+ "execution_turn_timeout"
42939
+ );
42825
42940
  return;
42826
42941
  }
42827
42942
  const primed = await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, bundle, primingBudgetMs, "priming");
@@ -42836,7 +42951,15 @@ async function driveRun(deps, projectSlug, runId) {
42836
42951
  Continue executing task ${task.id}. Make the next concrete increment of progress and record artifacts/judgements via MCP tools.` : `Continue executing task ${task.id}. Make the next concrete increment of progress and record artifacts/judgements via MCP tools.`;
42837
42952
  const workBudgetMs = remainingTurnBudgetMs();
42838
42953
  if (workBudgetMs <= 0) {
42839
- await failRun(deps, projectSlug, task, runId, leaseId, executionTurnBudgetMessage(execRunMaxMs()));
42954
+ await failRun(
42955
+ deps,
42956
+ projectSlug,
42957
+ task,
42958
+ runId,
42959
+ leaseId,
42960
+ executionTurnBudgetMessage(execRunMaxMs()),
42961
+ "execution_turn_timeout"
42962
+ );
42840
42963
  return;
42841
42964
  }
42842
42965
  const outcome = await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, executionPrompt, workBudgetMs, "work");
@@ -43003,6 +43126,13 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
43003
43126
  pullRequestUrl: pr.url,
43004
43127
  status: "dev_deploy"
43005
43128
  });
43129
+ await reconcileTaskAwaitingMerge(
43130
+ deps,
43131
+ projectSlug,
43132
+ readiness.task,
43133
+ runId,
43134
+ `PR opened (${pr.url}); deploy gate is running, but the operator still owns the merge decision.`
43135
+ );
43006
43136
  } else {
43007
43137
  await deps.client.updateExecutionRun(projectSlug, runId, {
43008
43138
  leaseId,
@@ -43258,7 +43388,7 @@ async function resetWedgedThread(deps, projectSlug, taskId, interruptFailed) {
43258
43388
  await deps.client.resetDirectorThread(projectSlug, taskId).catch(() => {
43259
43389
  });
43260
43390
  }
43261
- async function failRun(deps, projectSlug, task, runId, leaseId, reason) {
43391
+ async function failRun(deps, projectSlug, task, runId, leaseId, reason, reasonCode) {
43262
43392
  let currentRun;
43263
43393
  try {
43264
43394
  currentRun = await deps.client.getExecutionRun(projectSlug, runId);
@@ -43317,13 +43447,21 @@ async function failRun(deps, projectSlug, task, runId, leaseId, reason) {
43317
43447
  }
43318
43448
  }
43319
43449
  try {
43320
- await deps.client.createExecutionRunHandoff(projectSlug, runId, { reason });
43450
+ await deps.client.createExecutionRunHandoff(projectSlug, runId, {
43451
+ reason,
43452
+ ...reasonCode ? { reasonCode } : {}
43453
+ });
43321
43454
  } catch {
43322
43455
  await notifyOperatorHandoff(deps, projectSlug, task, runId, reason);
43323
43456
  }
43324
43457
  }
43325
43458
  async function reconcileTaskAwaitingMerge(deps, projectSlug, task, runId, reason) {
43326
43459
  if (task.status === "handoff_needed") return;
43460
+ const run3 = await deps.client.getExecutionRun(projectSlug, runId).catch(() => null);
43461
+ if (run3?.prState === "merged") {
43462
+ console.log(JSON.stringify({ event: "pr_merge_already_observed", projectSlug, runId, taskId: task.id }));
43463
+ return;
43464
+ }
43327
43465
  await deps.client.appendExecutionArtifact(projectSlug, runId, {
43328
43466
  id: newArtifactId(),
43329
43467
  kind: "handoff_judgement",
@@ -43425,6 +43563,27 @@ async function runBackstopSweep(deps, drive = (slug, id) => driveRun(deps, slug,
43425
43563
  pending = [];
43426
43564
  }
43427
43565
  for (const run3 of pending) {
43566
+ const updatedAtMs = run3.updatedAt ? Date.parse(run3.updatedAt) : Number.NaN;
43567
+ const staleBeforeMs = Date.now() - deps.staleSeconds * 1e3;
43568
+ if (run3.leaseId) {
43569
+ if (!Number.isFinite(updatedAtMs) || updatedAtMs >= staleBeforeMs) continue;
43570
+ try {
43571
+ const reclaimed = await deps.client.reclaimExecutionRunLease(slug, run3.id, deps.staleSeconds);
43572
+ if (!reclaimed) continue;
43573
+ } catch (error51) {
43574
+ if (error51 instanceof MrrlinApiError && error51.status === 404) continue;
43575
+ if (error51 instanceof MrrlinApiError && error51.status === 409 && error51.code === "RUN_LEASE_RECLAIM_EXHAUSTED") {
43576
+ await deps.client.createInboxItem(slug, {
43577
+ id: `II-${run3.id}-lease-stuck`,
43578
+ kind: "handoff",
43579
+ taskId: run3.taskId,
43580
+ body: `Execution run ${run3.id} exceeded its stale-lease reclaim budget (${maxLeaseReclaims()} attempts) and still is not making progress.`
43581
+ }).catch(() => {
43582
+ });
43583
+ }
43584
+ continue;
43585
+ }
43586
+ }
43428
43587
  await drive(slug, run3.id);
43429
43588
  }
43430
43589
  if (deps.deployGate) {
@@ -43493,6 +43652,9 @@ function claimIntervalMs() {
43493
43652
  const jitter = base * 0.25 * (Math.random() * 2 - 1);
43494
43653
  return Math.round((base + jitter) * 1e3);
43495
43654
  }
43655
+ function maxLeaseReclaims() {
43656
+ return readPositiveIntEnv("MRRLIN_MAX_LEASE_RECLAIMS", DEFAULT_MAX_LEASE_RECLAIMS);
43657
+ }
43496
43658
 
43497
43659
  // src/async-job-consumer.ts
43498
43660
  var import_node_crypto6 = __toESM(require("node:crypto"), 1);
@@ -47950,6 +48112,103 @@ var CheckoutRegistry = class {
47950
48112
  }
47951
48113
  };
47952
48114
 
48115
+ // src/checkout-handlers.ts
48116
+ var import_node_child_process10 = require("node:child_process");
48117
+ var import_node_fs12 = require("node:fs");
48118
+
48119
+ // src/git-remote-match.ts
48120
+ function normalizeRemote(url2) {
48121
+ const trimmed = url2.trim().replace(/\.git$/i, "");
48122
+ const scp = /^[^@]+@[^:]+:(.+)$/.exec(trimmed);
48123
+ const pathPart = scp ? scp[1] : (() => {
48124
+ try {
48125
+ return new URL(trimmed).pathname.replace(/^\/+/, "");
48126
+ } catch {
48127
+ return null;
48128
+ }
48129
+ })();
48130
+ if (!pathPart) return null;
48131
+ const segs = pathPart.split("/").filter(Boolean);
48132
+ if (segs.length < 2) return null;
48133
+ return `${segs[segs.length - 2]}/${segs[segs.length - 1]}`.toLowerCase();
48134
+ }
48135
+ function remoteMatchesRepo(remoteUrl, repoFullName) {
48136
+ const a = normalizeRemote(remoteUrl);
48137
+ return a !== null && a === repoFullName.trim().toLowerCase();
48138
+ }
48139
+
48140
+ // src/checkout-handlers.ts
48141
+ function validateCheckoutPath(checkoutPath, repo) {
48142
+ const trimmedPath = checkoutPath.trim();
48143
+ if (!trimmedPath.startsWith("/")) {
48144
+ return {
48145
+ ok: false,
48146
+ code: "not_absolute",
48147
+ detail: `Path must be absolute, got: ${trimmedPath}`
48148
+ };
48149
+ }
48150
+ let resolvedPath;
48151
+ try {
48152
+ resolvedPath = (0, import_node_fs12.realpathSync)(trimmedPath);
48153
+ } catch {
48154
+ return {
48155
+ ok: false,
48156
+ code: "not_found",
48157
+ detail: `Path does not exist or is not accessible: ${trimmedPath}`
48158
+ };
48159
+ }
48160
+ let remoteUrl;
48161
+ try {
48162
+ remoteUrl = (0, import_node_child_process10.execFileSync)("git", ["-C", resolvedPath, "remote", "get-url", "origin"], {
48163
+ encoding: "utf8",
48164
+ stdio: ["ignore", "pipe", "ignore"],
48165
+ timeout: 15e3,
48166
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
48167
+ }).trim();
48168
+ } catch {
48169
+ return {
48170
+ ok: false,
48171
+ code: "not_a_git_repo",
48172
+ detail: `Path is not a git repository or has no origin remote: ${resolvedPath}`
48173
+ };
48174
+ }
48175
+ if (!remoteMatchesRepo(remoteUrl, repo)) {
48176
+ return {
48177
+ ok: false,
48178
+ code: "remote_mismatch",
48179
+ detail: `Origin remote ${remoteUrl} does not match repo ${repo}.`
48180
+ };
48181
+ }
48182
+ return { ok: true, resolvedPath };
48183
+ }
48184
+ function readCheckoutState(registry3, projectSlug, machineLabel) {
48185
+ const entry = registry3.get(projectSlug);
48186
+ if (!entry) return null;
48187
+ return checkoutStateData(projectSlug, entry, machineLabel);
48188
+ }
48189
+ function registerCheckout(registry3, input, machineLabel, now = (/* @__PURE__ */ new Date()).toISOString()) {
48190
+ const validated = validateCheckoutPath(input.path, input.repo);
48191
+ if (!validated.ok) return validated;
48192
+ registry3.confirm(input.projectSlug, validated.resolvedPath, now);
48193
+ return {
48194
+ ok: true,
48195
+ data: {
48196
+ projectSlug: input.projectSlug,
48197
+ path: validated.resolvedPath,
48198
+ confirmedAt: now,
48199
+ machineLabel
48200
+ }
48201
+ };
48202
+ }
48203
+ function checkoutStateData(projectSlug, entry, machineLabel) {
48204
+ return {
48205
+ projectSlug,
48206
+ path: entry.path,
48207
+ confirmedAt: entry.confirmedAt,
48208
+ machineLabel
48209
+ };
48210
+ }
48211
+
47953
48212
  // src/executor-server.ts
47954
48213
  async function startExecutorServer(opts) {
47955
48214
  const manager = new CodexServerManager();
@@ -48157,7 +48416,7 @@ function extractVerificationSection(markdown) {
48157
48416
  }
48158
48417
 
48159
48418
  // src/bridge-log.ts
48160
- var import_node_fs12 = require("node:fs");
48419
+ var import_node_fs13 = require("node:fs");
48161
48420
  var import_node_path16 = require("node:path");
48162
48421
 
48163
48422
  // src/redact.ts
@@ -48245,7 +48504,7 @@ function createBridgeLogger(stateDir, env = process.env) {
48245
48504
  if (!resolveBridgeLogEnabled(env)) return NOOP_LOGGER;
48246
48505
  const logsDir = (0, import_node_path16.join)(stateDir, "logs");
48247
48506
  try {
48248
- (0, import_node_fs12.mkdirSync)(logsDir, { recursive: true });
48507
+ (0, import_node_fs13.mkdirSync)(logsDir, { recursive: true });
48249
48508
  } catch {
48250
48509
  return NOOP_LOGGER;
48251
48510
  }
@@ -48262,7 +48521,7 @@ function createBridgeLogger(stateDir, env = process.env) {
48262
48521
  ...rec.ms !== void 0 ? { ms: rec.ms } : {},
48263
48522
  payload: redactMessage(rec.payload, { includePrompts })
48264
48523
  }) + "\n";
48265
- (0, import_node_fs12.appendFileSync)(fileFor(), line);
48524
+ (0, import_node_fs13.appendFileSync)(fileFor(), line);
48266
48525
  } catch {
48267
48526
  }
48268
48527
  };
@@ -48275,7 +48534,7 @@ function createBridgeLogger(stateDir, env = process.env) {
48275
48534
  }
48276
48535
 
48277
48536
  // src/external-agents-scrub.ts
48278
- var import_node_fs13 = __toESM(require("node:fs"), 1);
48537
+ var import_node_fs14 = __toESM(require("node:fs"), 1);
48279
48538
  var import_node_os8 = __toESM(require("node:os"), 1);
48280
48539
  var import_node_path17 = __toESM(require("node:path"), 1);
48281
48540
  var REDACTION = "[REDACTED]";
@@ -48286,7 +48545,7 @@ function readKeysEnv() {
48286
48545
  const filePath = KEYS_ENV_PATH();
48287
48546
  let mtimeMs = 0;
48288
48547
  try {
48289
- mtimeMs = import_node_fs13.default.statSync(filePath).mtimeMs;
48548
+ mtimeMs = import_node_fs14.default.statSync(filePath).mtimeMs;
48290
48549
  } catch {
48291
48550
  mtimeMs = 0;
48292
48551
  }
@@ -48295,7 +48554,7 @@ function readKeysEnv() {
48295
48554
  }
48296
48555
  const map2 = /* @__PURE__ */ new Map();
48297
48556
  try {
48298
- const content = import_node_fs13.default.readFileSync(filePath, "utf8");
48557
+ const content = import_node_fs14.default.readFileSync(filePath, "utf8");
48299
48558
  for (const rawLine of content.split(/\r?\n/)) {
48300
48559
  const line = rawLine.trim();
48301
48560
  if (line === "" || line.startsWith("#")) continue;
@@ -48687,7 +48946,7 @@ function addBridgeModel(target, model) {
48687
48946
  }
48688
48947
  function readTextIfExists(filePath) {
48689
48948
  try {
48690
- return import_node_fs14.default.readFileSync(filePath, "utf8");
48949
+ return import_node_fs15.default.readFileSync(filePath, "utf8");
48691
48950
  } catch {
48692
48951
  return null;
48693
48952
  }
@@ -48766,7 +49025,7 @@ function readCodexFallbackModels() {
48766
49025
  }
48767
49026
  const automationsDir = import_node_path18.default.join(codexHome, "automations");
48768
49027
  try {
48769
- for (const entry of import_node_fs14.default.readdirSync(automationsDir, { withFileTypes: true })) {
49028
+ for (const entry of import_node_fs15.default.readdirSync(automationsDir, { withFileTypes: true })) {
48770
49029
  if (!entry.isDirectory()) continue;
48771
49030
  for (const slug of readKnownGptSlugsFromTomlFile(import_node_path18.default.join(automationsDir, entry.name, "automation.toml"))) {
48772
49031
  addBridgeModel(
@@ -48790,7 +49049,7 @@ function readCodexFallbackModels() {
48790
49049
  function neutralCheckoutCwd(stateDir) {
48791
49050
  const dir = import_node_path18.default.join(stateDir, "no-checkout");
48792
49051
  try {
48793
- import_node_fs14.default.mkdirSync(dir, { recursive: true });
49052
+ import_node_fs15.default.mkdirSync(dir, { recursive: true });
48794
49053
  } catch {
48795
49054
  }
48796
49055
  return dir;
@@ -49111,7 +49370,7 @@ function resolveDefaultBranch(checkoutPath) {
49111
49370
  if (!checkoutPath) return "main";
49112
49371
  const runGit = (args) => {
49113
49372
  try {
49114
- return (0, import_node_child_process10.execFileSync)("git", ["-C", checkoutPath, ...args], {
49373
+ return (0, import_node_child_process11.execFileSync)("git", ["-C", checkoutPath, ...args], {
49115
49374
  encoding: "utf8",
49116
49375
  stdio: ["ignore", "pipe", "ignore"],
49117
49376
  timeout: 15e3,
@@ -49143,7 +49402,7 @@ async function runOrphanWorktreeSweep(client, opts) {
49143
49402
  const cutoff = Date.now() - worktreeRetentionMs();
49144
49403
  const isReapable = (worktreePath) => {
49145
49404
  try {
49146
- return import_node_fs14.default.statSync(worktreePath).mtimeMs < cutoff;
49405
+ return import_node_fs15.default.statSync(worktreePath).mtimeMs < cutoff;
49147
49406
  } catch {
49148
49407
  return false;
49149
49408
  }
@@ -49447,7 +49706,9 @@ function createBridgeMessageHandler(deps) {
49447
49706
  logResumeRejected(req.directorSessionId, reason);
49448
49707
  return;
49449
49708
  }
49450
- const buf = t?.replay ?? null;
49709
+ const parked = t?.supersededReplay ?? null;
49710
+ const buf = req.acceptSuperseded === true && parked && parked.turnId === req.turnId ? parked : t?.replay ?? null;
49711
+ const servingSuperseded = buf !== null && buf === parked;
49451
49712
  if (!buf) {
49452
49713
  sendMsg({ type: "resume", status: "no-buffer", reason: "wrong-session" });
49453
49714
  logResumeRejected(req.directorSessionId, "wrong-session");
@@ -49478,15 +49739,17 @@ function createBridgeMessageHandler(deps) {
49478
49739
  buf.state = result.next;
49479
49740
  incrementBindEpoch(buf);
49480
49741
  buf.replaying = true;
49481
- deps.bindSessionSink?.(req.directorSessionId, send);
49482
- boundSessions.add(req.directorSessionId);
49483
- if (buf.triggerDetach) {
49484
- const detach = buf.triggerDetach;
49485
- const epochAtBind = buf.bindEpoch;
49486
- socket.once("close", () => {
49487
- if (buf.bindEpoch !== epochAtBind) return;
49488
- detach();
49489
- });
49742
+ if (!servingSuperseded) {
49743
+ deps.bindSessionSink?.(req.directorSessionId, send);
49744
+ boundSessions.add(req.directorSessionId);
49745
+ if (buf.triggerDetach) {
49746
+ const detach = buf.triggerDetach;
49747
+ const epochAtBind = buf.bindEpoch;
49748
+ socket.once("close", () => {
49749
+ if (buf.bindEpoch !== epochAtBind) return;
49750
+ detach();
49751
+ });
49752
+ }
49490
49753
  }
49491
49754
  const willStreamLive = result.next === "live";
49492
49755
  sendMsg({
@@ -49563,7 +49826,8 @@ function createBridgeMessageHandler(deps) {
49563
49826
  }
49564
49827
  const msg = parsed;
49565
49828
  const sid = sessionIdOf(msg);
49566
- if (sid && deps.bindSessionSink && !boundSessions.has(sid)) {
49829
+ const skipEagerBindForSupersededResume = msg.type === "resume" && msg.acceptSuperseded === true;
49830
+ if (sid && deps.bindSessionSink && !boundSessions.has(sid) && !skipEagerBindForSupersededResume) {
49567
49831
  deps.bindSessionSink(sid, send);
49568
49832
  boundSessions.add(sid);
49569
49833
  }
@@ -49585,6 +49849,57 @@ function createBridgeMessageHandler(deps) {
49585
49849
  sendForSpan({ type: "pairing-qr", ...result });
49586
49850
  return;
49587
49851
  }
49852
+ if (msg.type === "checkout-state") {
49853
+ if (!deps.checkoutRegistry) {
49854
+ sendForSpan({ type: "checkout-state", ok: false, error: { code: "bridge_offline", detail: "checkout registry is not configured on this bridge" } });
49855
+ return;
49856
+ }
49857
+ const projectSlug = typeof msg.projectSlug === "string" ? msg.projectSlug.trim() : "";
49858
+ if (!projectSlug) {
49859
+ sendForSpan({ type: "checkout-state", ok: false, error: { code: "invalid_input", fields: { projectSlug: "required" } } });
49860
+ return;
49861
+ }
49862
+ const state = readCheckoutState(deps.checkoutRegistry, projectSlug, import_node_os9.default.hostname());
49863
+ if (!state) {
49864
+ sendForSpan({ type: "checkout-state", ok: false, error: { code: "checkout_missing" } });
49865
+ return;
49866
+ }
49867
+ sendForSpan({ type: "checkout-state", ok: true, data: state });
49868
+ return;
49869
+ }
49870
+ if (msg.type === "checkout-register") {
49871
+ if (!deps.checkoutRegistry) {
49872
+ sendForSpan({ type: "checkout-register", ok: false, error: { code: "bridge_offline", detail: "checkout registry is not configured on this bridge" } });
49873
+ return;
49874
+ }
49875
+ const projectSlug = typeof msg.projectSlug === "string" ? msg.projectSlug.trim() : "";
49876
+ const repo = typeof msg.repo === "string" ? msg.repo.trim() : "";
49877
+ if (!projectSlug || !repo) {
49878
+ sendForSpan({
49879
+ type: "checkout-register",
49880
+ ok: false,
49881
+ error: {
49882
+ code: "invalid_input",
49883
+ fields: {
49884
+ ...!projectSlug ? { projectSlug: "required" } : {},
49885
+ ...!repo ? { repo: "required" } : {}
49886
+ }
49887
+ }
49888
+ });
49889
+ return;
49890
+ }
49891
+ const result = registerCheckout(
49892
+ deps.checkoutRegistry,
49893
+ { projectSlug, path: typeof msg.path === "string" ? msg.path : "", repo },
49894
+ import_node_os9.default.hostname()
49895
+ );
49896
+ if (!result.ok) {
49897
+ sendForSpan({ type: "checkout-register", ok: false, error: { code: result.code, detail: result.detail } });
49898
+ return;
49899
+ }
49900
+ sendForSpan({ type: "checkout-register", ok: true, data: result.data });
49901
+ return;
49902
+ }
49588
49903
  if (msg.type === "agents-state") {
49589
49904
  if (!deps.runExternalAgentsCli) {
49590
49905
  sendForSpan({ type: "agents-state", ok: false, error: { code: "bridge_offline", detail: "external-agents is not configured on this bridge" } });
@@ -49894,6 +50209,10 @@ function createBridgeMessageHandler(deps) {
49894
50209
  recordInterrupted(oldBuf, Date.now(), deps.suspendedMs?.() ?? 0);
49895
50210
  }
49896
50211
  }
50212
+ const droppedParked = takenOver.supersededReplay;
50213
+ if (oldBuf) {
50214
+ takenOver.supersededReplay = oldBuf;
50215
+ }
49897
50216
  takenOver.cancel?.();
49898
50217
  void withTimeout2(
49899
50218
  codexClient.turn.interrupt({ threadId: takenOver.threadId }),
@@ -49911,7 +50230,13 @@ function createBridgeMessageHandler(deps) {
49911
50230
  spanId,
49912
50231
  sessionId: directorSessionId,
49913
50232
  type: "<turn-takeover>",
49914
- payload: { sessionId: directorSessionId, clean: cleared, oldTurnId: oldBuf?.turnId ?? null }
50233
+ payload: {
50234
+ sessionId: directorSessionId,
50235
+ clean: cleared,
50236
+ oldTurnId: oldBuf?.turnId ?? null,
50237
+ parkedTurnId: oldBuf?.turnId ?? null,
50238
+ droppedParkedTurnId: droppedParked?.turnId ?? null
50239
+ }
49915
50240
  });
49916
50241
  }
49917
50242
  const desiredModel = normalizeModel(msg.model);
@@ -49923,10 +50248,12 @@ function createBridgeMessageHandler(deps) {
49923
50248
  threadId: "",
49924
50249
  mapper: new CodexBridgeEventMapper(),
49925
50250
  lastUsedAt: Date.now(),
50251
+ lastUsedAtSuspendedMs: deps.suspendedMs?.() ?? 0,
49926
50252
  inFlight: false,
49927
50253
  model: desiredModel,
49928
50254
  reasoningEffort: desiredEffort,
49929
- replay: null
50255
+ replay: null,
50256
+ supersededReplay: null
49930
50257
  };
49931
50258
  await acquireThread(codexClient, {
49932
50259
  threadId: null,
@@ -49992,6 +50319,7 @@ function createBridgeMessageHandler(deps) {
49992
50319
  ...deps.ackLingerMs !== void 0 ? { ackLingerMs: deps.ackLingerMs } : {}
49993
50320
  });
49994
50321
  thread.lastUsedAt = Date.now();
50322
+ thread.lastUsedAtSuspendedMs = deps.suspendedMs?.() ?? 0;
49995
50323
  const threadId = thread.threadId;
49996
50324
  const currentReplayBuf = thread.replay;
49997
50325
  sendForSpan({
@@ -50210,6 +50538,7 @@ ${effectiveText}` : effectiveText;
50210
50538
  thread.inFlight = false;
50211
50539
  }
50212
50540
  thread.lastUsedAt = Date.now();
50541
+ thread.lastUsedAtSuspendedMs = deps.suspendedMs?.() ?? 0;
50213
50542
  deps.persistThreads();
50214
50543
  deps.releaseCodex();
50215
50544
  }
@@ -50235,6 +50564,56 @@ function createBridgeShutdown(deps) {
50235
50564
  deps.exit(0);
50236
50565
  };
50237
50566
  }
50567
+ function shouldReapIdleThread(thread, now, suspendedMsNow, idleThreadTtlMs) {
50568
+ const suspendedSinceUse = Math.max(0, suspendedMsNow - thread.lastUsedAtSuspendedMs);
50569
+ return now - thread.lastUsedAt - suspendedSinceUse > idleThreadTtlMs;
50570
+ }
50571
+ function sweepIdleThreads(threads, opts) {
50572
+ const suspendedNow = opts.suspendedMs();
50573
+ const reaped = [];
50574
+ for (const [directorSessionId, thread] of threads.entries()) {
50575
+ if (thread.inFlight) continue;
50576
+ if (thread.replay && !shouldGc(thread.replay, opts.now, suspendedNow)) continue;
50577
+ if (thread.supersededReplay && !shouldGc(thread.supersededReplay, opts.now, suspendedNow)) continue;
50578
+ if (!shouldReapIdleThread(thread, opts.now, suspendedNow, opts.idleThreadTtlMs)) continue;
50579
+ threads.delete(directorSessionId);
50580
+ reaped.push(directorSessionId);
50581
+ }
50582
+ return reaped;
50583
+ }
50584
+ function sweepReplayBuffers(threads, tombstones, opts) {
50585
+ const suspendedNow = opts.suspendedMs();
50586
+ let retainedBuffers = 0;
50587
+ let retainedBytes = 0;
50588
+ for (const [directorSessionId, thread] of threads) {
50589
+ if (thread.replay && shouldGc(thread.replay, opts.now, suspendedNow)) {
50590
+ const turnId = thread.replay.turnId;
50591
+ const lastSeq = thread.replay.lastSeq;
50592
+ const expiredUnacked = thread.replay.ackedAt === null;
50593
+ writeTombstone(tombstones, directorSessionId, thread.replay, opts.now, suspendedNow);
50594
+ opts.onTombstone?.({ directorSessionId, turnId, lastSeq, expiredUnacked });
50595
+ thread.replay = null;
50596
+ } else if (thread.replay) {
50597
+ retainedBuffers += 1;
50598
+ retainedBytes += thread.replay.bytesUsed;
50599
+ }
50600
+ if (thread.supersededReplay && shouldGc(thread.supersededReplay, opts.now, suspendedNow)) {
50601
+ const turnId = thread.supersededReplay.turnId;
50602
+ thread.supersededReplay = null;
50603
+ opts.onSupersededDropped?.({ directorSessionId, turnId });
50604
+ } else if (thread.supersededReplay) {
50605
+ retainedBuffers += 1;
50606
+ retainedBytes += thread.supersededReplay.bytesUsed;
50607
+ }
50608
+ }
50609
+ const tombstonesRemoved = sweepExpiredTombstones(
50610
+ tombstones,
50611
+ opts.now,
50612
+ opts.tombstoneTtlMs ?? DEFAULT_TOMBSTONE_TTL_MS,
50613
+ suspendedNow
50614
+ );
50615
+ return { retainedBuffers, retainedBytes, tombstonesRemoved };
50616
+ }
50238
50617
  function parseConfig() {
50239
50618
  const host = (process.env.MRRLIN_DIRECTOR_BRIDGE_HOST ?? "127.0.0.1").trim() || "127.0.0.1";
50240
50619
  const port = readEnvInt("MRRLIN_DIRECTOR_BRIDGE_PORT", 8791);
@@ -50248,7 +50627,7 @@ function parseConfig() {
50248
50627
  }
50249
50628
  function scanForOtherBridges() {
50250
50629
  try {
50251
- const result = (0, import_node_child_process10.spawnSync)("pgrep", ["-f", "mrrlin-mcp director-bridge"], {
50630
+ const result = (0, import_node_child_process11.spawnSync)("pgrep", ["-f", "mrrlin-mcp director-bridge"], {
50252
50631
  encoding: "utf8"
50253
50632
  });
50254
50633
  if (result.status !== 0) return [];
@@ -50428,13 +50807,13 @@ async function startDirectorBridge() {
50428
50807
  // egressDomains is retained in the seam signature (verification-turn.ts still computes the
50429
50808
  // env host) but no longer constrains anything — the executor is unrestricted.
50430
50809
  startSandbox: async (_egressDomains) => {
50431
- const cwd = import_node_fs14.default.mkdtempSync(import_node_path18.default.join(import_node_os9.default.tmpdir(), "mrrlin-verify-"));
50810
+ const cwd = import_node_fs15.default.mkdtempSync(import_node_path18.default.join(import_node_os9.default.tmpdir(), "mrrlin-verify-"));
50432
50811
  let handle;
50433
50812
  try {
50434
50813
  handle = await startExecutorServer({ worktree: cwd });
50435
50814
  } catch (error51) {
50436
50815
  try {
50437
- import_node_fs14.default.rmSync(cwd, { recursive: true, force: true });
50816
+ import_node_fs15.default.rmSync(cwd, { recursive: true, force: true });
50438
50817
  } catch {
50439
50818
  }
50440
50819
  throw error51;
@@ -50445,7 +50824,7 @@ async function startDirectorBridge() {
50445
50824
  dispose: () => {
50446
50825
  handle.dispose();
50447
50826
  try {
50448
- import_node_fs14.default.rmSync(cwd, { recursive: true, force: true });
50827
+ import_node_fs15.default.rmSync(cwd, { recursive: true, force: true });
50449
50828
  } catch {
50450
50829
  }
50451
50830
  }
@@ -50557,10 +50936,12 @@ async function startDirectorBridge() {
50557
50936
  threadId: entry.threadId,
50558
50937
  mapper: new CodexBridgeEventMapper(),
50559
50938
  lastUsedAt: entry.lastUsedAt,
50939
+ lastUsedAtSuspendedMs: suspensionDetector.suspendedMs(),
50560
50940
  inFlight: false,
50561
50941
  model: entry.model,
50562
50942
  reasoningEffort: normalizeReasoningEffort(entry.reasoningEffort),
50563
- replay: null
50943
+ replay: null,
50944
+ supersededReplay: null
50564
50945
  });
50565
50946
  }
50566
50947
  const persistThreads = () => {
@@ -50585,17 +50966,12 @@ async function startDirectorBridge() {
50585
50966
  }
50586
50967
  };
50587
50968
  const cleanupTimer = setInterval(() => {
50588
- const now = Date.now();
50589
- let changed = false;
50590
- for (const [directorSessionId, thread] of threads.entries()) {
50591
- if (thread.inFlight) continue;
50592
- if (thread.replay && !shouldGc(thread.replay, now, suspensionDetector.suspendedMs())) continue;
50593
- if (now - thread.lastUsedAt > config2.idleThreadTtlMs) {
50594
- threads.delete(directorSessionId);
50595
- changed = true;
50596
- }
50597
- }
50598
- if (changed) persistThreads();
50969
+ const reaped = sweepIdleThreads(threads, {
50970
+ now: Date.now(),
50971
+ suspendedMs: () => suspensionDetector.suspendedMs(),
50972
+ idleThreadTtlMs: config2.idleThreadTtlMs
50973
+ });
50974
+ if (reaped.length > 0) persistThreads();
50599
50975
  }, 6e4);
50600
50976
  cleanupTimer.unref?.();
50601
50977
  let remoteRelay = null;
@@ -50656,7 +51032,7 @@ async function startDirectorBridge() {
50656
51032
  const bridgeLogger = createBridgeLogger(config2.stateDir, process.env);
50657
51033
  let bridgeBinShaShort = "unknown00000";
50658
51034
  try {
50659
- bridgeBinShaShort = import_node_crypto8.default.createHash("sha256").update(import_node_fs14.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
51035
+ bridgeBinShaShort = import_node_crypto8.default.createHash("sha256").update(import_node_fs15.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
50660
51036
  } catch {
50661
51037
  }
50662
51038
  attachBridgeWss(server, {
@@ -50790,28 +51166,27 @@ async function startDirectorBridge() {
50790
51166
  })
50791
51167
  );
50792
51168
  enqueueRun(async () => {
50793
- const now = Date.now();
50794
- const suspendedNow = suspensionDetector.suspendedMs();
50795
- let retainedBuffers = 0;
50796
- let retainedBytes = 0;
50797
- for (const [sid, t] of threads) {
50798
- if (t.replay && shouldGc(t.replay, now, suspendedNow)) {
50799
- const turnId = t.replay.turnId;
50800
- const lastSeq = t.replay.lastSeq;
50801
- const expiredUnacked = t.replay.ackedAt === null;
50802
- writeTombstone(tombstones, sid, t.replay, now);
51169
+ const { retainedBuffers, retainedBytes, tombstonesRemoved } = sweepReplayBuffers(threads, tombstones, {
51170
+ now: Date.now(),
51171
+ suspendedMs: () => suspensionDetector.suspendedMs(),
51172
+ tombstoneTtlMs: DEFAULT_TOMBSTONE_TTL_MS,
51173
+ onTombstone: ({ directorSessionId, turnId, lastSeq, expiredUnacked }) => {
50803
51174
  bridgeLogger.logOut({
50804
51175
  spanId: "gc",
50805
- sessionId: sid,
51176
+ sessionId: directorSessionId,
50806
51177
  type: expiredUnacked ? "<result-expired>" : "<tombstone-written>",
50807
- payload: { sessionId: sid, turnId, lastSeq }
51178
+ payload: { sessionId: directorSessionId, turnId, lastSeq }
51179
+ });
51180
+ },
51181
+ onSupersededDropped: ({ directorSessionId, turnId }) => {
51182
+ bridgeLogger.logOut({
51183
+ spanId: "gc",
51184
+ sessionId: directorSessionId,
51185
+ type: "<superseded-dropped>",
51186
+ payload: { sessionId: directorSessionId, turnId }
50808
51187
  });
50809
- t.replay = null;
50810
- } else if (t.replay) {
50811
- retainedBuffers += 1;
50812
- retainedBytes += t.replay.bytesUsed;
50813
51188
  }
50814
- }
51189
+ });
50815
51190
  if (retainedBuffers > 0) {
50816
51191
  bridgeLogger.logOut({
50817
51192
  spanId: "gc",
@@ -50820,13 +51195,12 @@ async function startDirectorBridge() {
50820
51195
  payload: { retainedBuffers, retainedBytes }
50821
51196
  });
50822
51197
  }
50823
- const removed = sweepExpiredTombstones(tombstones, now);
50824
- if (removed > 0) {
51198
+ if (tombstonesRemoved > 0) {
50825
51199
  bridgeLogger.logOut({
50826
51200
  spanId: "gc",
50827
51201
  sessionId: null,
50828
51202
  type: "<tombstone-expired>",
50829
- payload: { removed }
51203
+ payload: { removed: tombstonesRemoved }
50830
51204
  });
50831
51205
  }
50832
51206
  });
@@ -51050,13 +51424,13 @@ function readOrCreateBridgeToken(stateDir) {
51050
51424
  if (explicit) return explicit;
51051
51425
  const tokenPath = import_node_path18.default.join(stateDir, "token.txt");
51052
51426
  try {
51053
- const existing = import_node_fs14.default.readFileSync(tokenPath, "utf8").trim();
51427
+ const existing = import_node_fs15.default.readFileSync(tokenPath, "utf8").trim();
51054
51428
  if (existing) return existing;
51055
51429
  } catch {
51056
51430
  }
51057
- import_node_fs14.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
51431
+ import_node_fs15.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
51058
51432
  const token = import_node_crypto8.default.randomBytes(32).toString("base64url");
51059
- import_node_fs14.default.writeFileSync(tokenPath, `${token}
51433
+ import_node_fs15.default.writeFileSync(tokenPath, `${token}
51060
51434
  `, { mode: 384 });
51061
51435
  return token;
51062
51436
  }
@@ -54440,10 +54814,8 @@ var StdioServerTransport = class {
54440
54814
  // src/tools.ts
54441
54815
  var import_node_crypto11 = require("node:crypto");
54442
54816
  var import_promises3 = require("node:fs/promises");
54443
- var import_node_fs15 = require("node:fs");
54444
54817
  var import_node_path20 = __toESM(require("node:path"), 1);
54445
54818
  var import_node_os10 = __toESM(require("node:os"), 1);
54446
- var import_node_child_process12 = require("node:child_process");
54447
54819
 
54448
54820
  // ../../packages/wiki/dist/index.js
54449
54821
  var import_promises2 = __toESM(require("node:fs/promises"), 1);
@@ -54901,7 +55273,7 @@ async function runCodeGate(_target, deps) {
54901
55273
  }
54902
55274
 
54903
55275
  // src/consensus/multi-llm-head.ts
54904
- var import_node_child_process11 = require("node:child_process");
55276
+ var import_node_child_process12 = require("node:child_process");
54905
55277
  function errMessage3(err) {
54906
55278
  return err instanceof Error ? err.message : String(err);
54907
55279
  }
@@ -54943,7 +55315,7 @@ function runExternalAgents(bin, args, label, timeoutMs, spawn3) {
54943
55315
  });
54944
55316
  }
54945
55317
  async function pickPanel(input, deps) {
54946
- const spawn3 = deps?.spawn ?? import_node_child_process11.spawn;
55318
+ const spawn3 = deps?.spawn ?? import_node_child_process12.spawn;
54947
55319
  const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
54948
55320
  const args = [
54949
55321
  "pick",
@@ -54964,7 +55336,7 @@ async function pickPanel(input, deps) {
54964
55336
  return { ok: true, agents };
54965
55337
  }
54966
55338
  async function dispatchHead(agentId, prompt, timeoutMs, deps) {
54967
- const spawn3 = deps?.spawn ?? import_node_child_process11.spawn;
55339
+ const spawn3 = deps?.spawn ?? import_node_child_process12.spawn;
54968
55340
  const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
54969
55341
  const args = ["dispatch", agentId, "--json", prompt];
54970
55342
  const r = await runExternalAgents(command, [...prefixArgs, ...args], "dispatch", timeoutMs, spawn3);
@@ -55622,27 +55994,6 @@ async function enqueueAsyncTool(client, descriptor2, input) {
55622
55994
  }
55623
55995
  }
55624
55996
 
55625
- // src/git-remote-match.ts
55626
- function normalizeRemote(url2) {
55627
- const trimmed = url2.trim().replace(/\.git$/i, "");
55628
- const scp = /^[^@]+@[^:]+:(.+)$/.exec(trimmed);
55629
- const pathPart = scp ? scp[1] : (() => {
55630
- try {
55631
- return new URL(trimmed).pathname.replace(/^\/+/, "");
55632
- } catch {
55633
- return null;
55634
- }
55635
- })();
55636
- if (!pathPart) return null;
55637
- const segs = pathPart.split("/").filter(Boolean);
55638
- if (segs.length < 2) return null;
55639
- return `${segs[segs.length - 2]}/${segs[segs.length - 1]}`.toLowerCase();
55640
- }
55641
- function remoteMatchesRepo(remoteUrl, repoFullName) {
55642
- const a = normalizeRemote(remoteUrl);
55643
- return a !== null && a === repoFullName.trim().toLowerCase();
55644
- }
55645
-
55646
55997
  // src/tool-names.ts
55647
55998
  var mcpToolNames = {
55648
55999
  appendExecutionArtifact: "append_execution_artifact",
@@ -55704,6 +56055,8 @@ var mcpToolNames = {
55704
56055
  runCodeReviewerGate: "run_code_reviewer_gate",
55705
56056
  uploadArtifact: "upload_artifact",
55706
56057
  listArtifactFiles: "list_artifact_files",
56058
+ reclaimExecutionRunLease: "reclaim_execution_run_lease",
56059
+ restartExecutionRun: "restart_execution_run",
55707
56060
  resolveHandoff: "resolve_handoff",
55708
56061
  recordProducedUrl: "record_produced_url",
55709
56062
  registerLocalCheckout: "register_local_checkout",
@@ -56302,6 +56655,8 @@ var listArtifactFilesInputSchema = projectScopedSchema.merge(
56302
56655
  runId: mrrlinExecutionRunIdSchema.optional()
56303
56656
  })
56304
56657
  );
56658
+ var reclaimExecutionRunLeaseInputSchema = projectScopedSchema.extend({ runId: mrrlinExecutionRunIdSchema }).merge(mrrlinExecutionRunRequeueSchema);
56659
+ var restartExecutionRunInputSchema = projectScopedSchema.extend({ runId: mrrlinExecutionRunIdSchema }).merge(mrrlinRestartExecutionRunSchema);
56305
56660
  var listInboxItemsInputSchema = projectScopedSchema.merge(mrrlinInboxItemListFiltersSchema);
56306
56661
  var createInboxItemInputSchema = projectScopedSchema.merge(mrrlinInboxItemCreateSchema);
56307
56662
  var decideInboxItemInputSchema = projectScopedSchema.extend({ itemId: mrrlinInboxItemIdSchema }).merge(mrrlinInboxItemDecideSchema);
@@ -56378,6 +56733,8 @@ var mcpToolInputSchemas = {
56378
56733
  [mcpToolNames.decideInboxItem]: decideInboxItemInputSchema,
56379
56734
  [mcpToolNames.uploadArtifact]: uploadArtifactInputSchema,
56380
56735
  [mcpToolNames.listArtifactFiles]: listArtifactFilesInputSchema,
56736
+ [mcpToolNames.reclaimExecutionRunLease]: reclaimExecutionRunLeaseInputSchema,
56737
+ [mcpToolNames.restartExecutionRun]: restartExecutionRunInputSchema,
56381
56738
  [mcpToolNames.resolveHandoff]: resolveHandoffInputSchema,
56382
56739
  [mcpToolNames.recordProducedUrl]: recordProducedUrlInputSchema,
56383
56740
  [mcpToolNames.registerLocalCheckout]: registerLocalCheckoutInputSchema,
@@ -56441,6 +56798,8 @@ var mcpToolOperationIds = {
56441
56798
  [mcpToolNames.decideInboxItem]: openApiOperationIds.decideInboxItem,
56442
56799
  [mcpToolNames.uploadArtifact]: openApiOperationIds.createArtifactFile,
56443
56800
  [mcpToolNames.listArtifactFiles]: openApiOperationIds.listArtifactFiles,
56801
+ [mcpToolNames.reclaimExecutionRunLease]: openApiOperationIds.reclaimExecutionRunLease,
56802
+ [mcpToolNames.restartExecutionRun]: openApiOperationIds.restartExecutionRun,
56444
56803
  [mcpToolNames.resolveHandoff]: openApiOperationIds.resolveHandoff
56445
56804
  };
56446
56805
  var mcpToolDescriptions = {
@@ -56504,6 +56863,8 @@ var mcpToolDescriptions = {
56504
56863
  [mcpToolNames.decideInboxItem]: "Approve, reject, or acknowledge an operator-inbox item. Rejection requires a reason.",
56505
56864
  [mcpToolNames.uploadArtifact]: "Upload a local file (md/txt/html/csv/json/pdf/png/jpg/webp/gif, <=5MB) as a project artifact. Returns a stable markdown link to embed in chat/spec/handoff text. Use class=temp for one-off hand-offs, class=durable for anything referenced by specs or evidence. Pass runId to record the file in that run's artifact history; pass taskId to surface it on the task's Artifacts list.",
56506
56865
  [mcpToolNames.listArtifactFiles]: "List uploaded artifact files for the project (optionally filtered by taskId/runId), newest first.",
56866
+ [mcpToolNames.reclaimExecutionRunLease]: "Clear a stale pending execution-run lease when the lease has aged past staleSeconds. Returns the reclaimed run on success, null when the lease is not stale yet, and surfaces a structured conflict when reclaim retries are exhausted.",
56867
+ [mcpToolNames.restartExecutionRun]: "Restart a parked execution run (handoff_requested or handoff_queued) back to pending so the queue can resume it.",
56507
56868
  [mcpToolNames.resolveHandoff]: "Resolve a tool_assisted handoff for an execution run (outcome success|failure). Success returns the task to the board; failure opens a new handoff cycle. Idempotent.",
56508
56869
  [mcpToolNames.registerLocalCheckout]: "Register the operator-local git checkout path for `projectSlug` so execution-run workers run code on this machine instead of the cloud. Validates the path is a git repo whose `origin` remote matches `repo`, then writes the (slug -> absolute path) entry to the operator-local checkout registry. Call this after locating the checkout on disk (e.g. via filesystem search) and confirming the choice with the user in chat \u2014 they should NOT have to paste the path into Settings. Returns `{ projectSlug, path, confirmedAt }`. Idempotent: re-registering overwrites the prior entry.",
56509
56870
  [mcpToolNames.retrieveContext]: "Rehydrate a mrrlin:// handle emitted by the compact-context layer into the full canonical Mrrlin record (task/wiki page/project snapshot/execution run/artifacts). Use whenever you need the full source \u2014 irreversible decisions (closing tasks, dispatching deploys, writing specs) MUST call this first; never act on a compact envelope alone. Returns `{ kind, data }`. Invalid or unsupported handle \u2192 structured `HANDLE_INVALID` error."
@@ -57261,6 +57622,20 @@ function createMrrlinTools(options) {
57261
57622
  "Unable to list artifact files.",
57262
57623
  async (c, { projectSlug, taskId, runId }) => await c.listArtifactFiles(projectSlug, { runId, taskId })
57263
57624
  ),
57625
+ [mcpToolNames.reclaimExecutionRunLease]: makeTool(
57626
+ client,
57627
+ mcpToolNames.reclaimExecutionRunLease,
57628
+ "EXECUTION_RUN_RECLAIM_FAILED",
57629
+ "Unable to reclaim the execution run lease.",
57630
+ async (c, { projectSlug, runId, staleSeconds }) => await c.reclaimExecutionRunLease(projectSlug, runId, staleSeconds)
57631
+ ),
57632
+ [mcpToolNames.restartExecutionRun]: makeTool(
57633
+ client,
57634
+ mcpToolNames.restartExecutionRun,
57635
+ "EXECUTION_RUN_RESTART_FAILED",
57636
+ "Unable to restart the execution run.",
57637
+ async (c, { projectSlug, runId, ...input }) => await c.restartExecutionRun(projectSlug, runId, input)
57638
+ ),
57264
57639
  [mcpToolNames.resolveHandoff]: makeTool(
57265
57640
  client,
57266
57641
  mcpToolNames.resolveHandoff,
@@ -57274,47 +57649,19 @@ function createMrrlinTools(options) {
57274
57649
  "LOCAL_CHECKOUT_REGISTER_FAILED",
57275
57650
  "Unable to register local checkout.",
57276
57651
  async (_c, { projectSlug, path: checkoutPath, repo }) => {
57277
- const trimmedPath = checkoutPath.trim();
57278
- if (!import_node_path20.default.isAbsolute(trimmedPath)) {
57279
- throw new McpToolCodedError(
57280
- "LOCAL_CHECKOUT_PATH_NOT_ABSOLUTE",
57281
- `Path must be absolute, got: ${trimmedPath}`
57282
- );
57283
- }
57284
- let resolvedPath;
57285
- try {
57286
- resolvedPath = (0, import_node_fs15.realpathSync)(trimmedPath);
57287
- } catch {
57288
- throw new McpToolCodedError(
57289
- "LOCAL_CHECKOUT_PATH_NOT_FOUND",
57290
- `Path does not exist or is not accessible: ${trimmedPath}`
57291
- );
57292
- }
57293
- let remoteUrl;
57294
- try {
57295
- remoteUrl = (0, import_node_child_process12.execFileSync)("git", ["-C", resolvedPath, "remote", "get-url", "origin"], {
57296
- encoding: "utf8",
57297
- stdio: ["ignore", "pipe", "ignore"],
57298
- timeout: 15e3,
57299
- env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
57300
- }).trim();
57301
- } catch {
57302
- throw new McpToolCodedError(
57303
- "LOCAL_CHECKOUT_NOT_A_GIT_REPO",
57304
- `Path is not a git repository or has no origin remote: ${resolvedPath}`
57305
- );
57306
- }
57307
- if (!remoteMatchesRepo(remoteUrl, repo)) {
57308
- throw new McpToolCodedError(
57309
- "LOCAL_CHECKOUT_REMOTE_MISMATCH",
57310
- `Origin remote ${remoteUrl} does not match repo ${repo}.`
57311
- );
57312
- }
57313
57652
  const stateDir = process.env.CODEX_HOME ? import_node_path20.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge") : import_node_path20.default.join(import_node_os10.default.homedir(), ".mrrlin", "director-bridge");
57314
57653
  const registry3 = new CheckoutRegistry(stateDir);
57315
- const confirmedAt = (/* @__PURE__ */ new Date()).toISOString();
57316
- registry3.confirm(projectSlug, resolvedPath, confirmedAt);
57317
- return { projectSlug, path: resolvedPath, confirmedAt };
57654
+ const result = registerCheckout(registry3, { projectSlug, path: checkoutPath, repo }, import_node_os10.default.hostname());
57655
+ if (!result.ok) {
57656
+ const codeMap = {
57657
+ not_absolute: "LOCAL_CHECKOUT_PATH_NOT_ABSOLUTE",
57658
+ not_found: "LOCAL_CHECKOUT_PATH_NOT_FOUND",
57659
+ not_a_git_repo: "LOCAL_CHECKOUT_NOT_A_GIT_REPO",
57660
+ remote_mismatch: "LOCAL_CHECKOUT_REMOTE_MISMATCH"
57661
+ };
57662
+ throw new McpToolCodedError(codeMap[result.code], result.detail);
57663
+ }
57664
+ return result.data;
57318
57665
  }
57319
57666
  ),
57320
57667
  [mcpToolNames.retrieveContext]: makeTool(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/mcp",
3
- "version": "0.3.55",
3
+ "version": "0.3.56",
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/client": "0.0.0",
25
- "@mrrlin/codex-client": "0.0.0",
26
- "@mrrlin/tsconfig": "0.0.0",
27
24
  "@mrrlin/director-e2e": "0.0.0",
28
25
  "@mrrlin/schemas": "0.0.0",
29
- "@mrrlin/wiki": "0.0.0"
26
+ "@mrrlin/wiki": "0.0.0",
27
+ "@mrrlin/client": "0.0.0",
28
+ "@mrrlin/tsconfig": "0.0.0",
29
+ "@mrrlin/codex-client": "0.0.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@iarna/toml": "^2.2.5",