@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.
- package/dist/bin.cjs +505 -158
- 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
|
|
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
|
|
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)
|
|
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: () =>
|
|
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
|
-
|
|
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({
|
|
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.
|
|
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
|
|
42280
|
-
" - if you are blocked and need an operator decision before the work can continue, use status=
|
|
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
|
|
42305
|
-
" PR
|
|
42306
|
-
" must decide before the work can continue, leave it at `
|
|
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
|
|
42553
|
-
const
|
|
42554
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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, {
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
49482
|
-
|
|
49483
|
-
|
|
49484
|
-
|
|
49485
|
-
|
|
49486
|
-
|
|
49487
|
-
|
|
49488
|
-
|
|
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
|
-
|
|
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: {
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
50589
|
-
|
|
50590
|
-
|
|
50591
|
-
|
|
50592
|
-
|
|
50593
|
-
|
|
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(
|
|
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
|
|
50794
|
-
|
|
50795
|
-
|
|
50796
|
-
|
|
50797
|
-
|
|
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:
|
|
51176
|
+
sessionId: directorSessionId,
|
|
50806
51177
|
type: expiredUnacked ? "<result-expired>" : "<tombstone-written>",
|
|
50807
|
-
payload: { sessionId:
|
|
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
|
-
|
|
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 =
|
|
51427
|
+
const existing = import_node_fs15.default.readFileSync(tokenPath, "utf8").trim();
|
|
51054
51428
|
if (existing) return existing;
|
|
51055
51429
|
} catch {
|
|
51056
51430
|
}
|
|
51057
|
-
|
|
51431
|
+
import_node_fs15.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
51058
51432
|
const token = import_node_crypto8.default.randomBytes(32).toString("base64url");
|
|
51059
|
-
|
|
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
|
|
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 ??
|
|
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 ??
|
|
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
|
|
57316
|
-
|
|
57317
|
-
|
|
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.
|
|
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",
|