@mrrlin-dev/mcp 0.3.60 → 0.3.64
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 +1053 -159
- package/package.json +4 -4
package/dist/bin.cjs
CHANGED
|
@@ -6543,7 +6543,7 @@ var require_truncate = __commonJS({
|
|
|
6543
6543
|
var parse5 = require_parse2();
|
|
6544
6544
|
var constants = require_constants2();
|
|
6545
6545
|
var SemVer = require_semver();
|
|
6546
|
-
var
|
|
6546
|
+
var truncate3 = (version2, truncation, options) => {
|
|
6547
6547
|
if (!constants.RELEASE_TYPES.includes(truncation)) {
|
|
6548
6548
|
return null;
|
|
6549
6549
|
}
|
|
@@ -6573,7 +6573,7 @@ var require_truncate = __commonJS({
|
|
|
6573
6573
|
var isPrerelease = (type) => {
|
|
6574
6574
|
return type.startsWith("pre");
|
|
6575
6575
|
};
|
|
6576
|
-
module2.exports =
|
|
6576
|
+
module2.exports = truncate3;
|
|
6577
6577
|
}
|
|
6578
6578
|
});
|
|
6579
6579
|
|
|
@@ -7611,7 +7611,7 @@ var require_semver2 = __commonJS({
|
|
|
7611
7611
|
var lte = require_lte();
|
|
7612
7612
|
var cmp = require_cmp();
|
|
7613
7613
|
var coerce = require_coerce();
|
|
7614
|
-
var
|
|
7614
|
+
var truncate3 = require_truncate();
|
|
7615
7615
|
var Comparator = require_comparator();
|
|
7616
7616
|
var Range = require_range();
|
|
7617
7617
|
var satisfies = require_satisfies();
|
|
@@ -7650,7 +7650,7 @@ var require_semver2 = __commonJS({
|
|
|
7650
7650
|
lte,
|
|
7651
7651
|
cmp,
|
|
7652
7652
|
coerce,
|
|
7653
|
-
truncate:
|
|
7653
|
+
truncate: truncate3,
|
|
7654
7654
|
Comparator,
|
|
7655
7655
|
Range,
|
|
7656
7656
|
satisfies,
|
|
@@ -36432,6 +36432,13 @@ var openApiOperationIds = {
|
|
|
36432
36432
|
restartExecutionRun: "restartExecutionRun",
|
|
36433
36433
|
resolveHandoff: "resolveHandoff",
|
|
36434
36434
|
touchExecutionRun: "touchExecutionRun",
|
|
36435
|
+
getTaskState: "getTaskState",
|
|
36436
|
+
upsertTaskState: "upsertTaskState",
|
|
36437
|
+
retireThread: "retireThread",
|
|
36438
|
+
// Run-scoped, model-facing half of the resume contract (D8). Distinct from upsertTaskState:
|
|
36439
|
+
// that one is the server's own write (whole record, explicit lastRunSeq), this one is the
|
|
36440
|
+
// executor reporting narrative only, with the ordering value derived server-side from the run.
|
|
36441
|
+
reportTaskState: "reportTaskState",
|
|
36435
36442
|
issueAgentCredential: "issueAgentCredential",
|
|
36436
36443
|
listAgentCredentials: "listAgentCredentials",
|
|
36437
36444
|
revokeAgentCredential: "revokeAgentCredential",
|
|
@@ -37463,7 +37470,55 @@ var mrrlinExecutionRunSchema = external_exports.object({
|
|
|
37463
37470
|
status: mrrlinExecutionRunStatusSchema,
|
|
37464
37471
|
taskId: mrrlinTaskIdSchema,
|
|
37465
37472
|
updatedAt: external_exports.string().datetime(),
|
|
37466
|
-
worktreeId: external_exports.string().min(1).nullable()
|
|
37473
|
+
worktreeId: external_exports.string().min(1).nullable(),
|
|
37474
|
+
stateSeq: external_exports.number().int(),
|
|
37475
|
+
promptBytes: external_exports.number().int().nullable(),
|
|
37476
|
+
turnCount: external_exports.number().int().nullable(),
|
|
37477
|
+
inputTokens: external_exports.number().int().nullable(),
|
|
37478
|
+
cachedInputTokens: external_exports.number().int().nullable(),
|
|
37479
|
+
outputTokens: external_exports.number().int().nullable(),
|
|
37480
|
+
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable(),
|
|
37481
|
+
effortEscalationReason: external_exports.string().min(1).nullable()
|
|
37482
|
+
});
|
|
37483
|
+
var mrrlinTaskStateSchema = external_exports.object({
|
|
37484
|
+
projectSlug: mrrlinProjectSlugSchema,
|
|
37485
|
+
taskId: mrrlinTaskIdSchema,
|
|
37486
|
+
goal: external_exports.string().min(1).nullable(),
|
|
37487
|
+
acceptedRequirements: external_exports.string().min(1).nullable(),
|
|
37488
|
+
decisions: external_exports.string().min(1).nullable(),
|
|
37489
|
+
relevantFiles: external_exports.string().min(1).nullable(),
|
|
37490
|
+
filesChanged: external_exports.string().min(1).nullable(),
|
|
37491
|
+
latestVerification: external_exports.string().min(1).nullable(),
|
|
37492
|
+
openProblems: external_exports.string().min(1).nullable(),
|
|
37493
|
+
nextActions: external_exports.string().min(1).nullable(),
|
|
37494
|
+
lastRunSeq: external_exports.number().int(),
|
|
37495
|
+
threadRunCount: external_exports.number().int(),
|
|
37496
|
+
retiredThreadId: external_exports.string().min(1).nullable(),
|
|
37497
|
+
updatedAt: external_exports.string().datetime()
|
|
37498
|
+
});
|
|
37499
|
+
var mrrlinTaskStateUpsertSchema = external_exports.object({
|
|
37500
|
+
goal: external_exports.string().min(1).nullable(),
|
|
37501
|
+
acceptedRequirements: external_exports.string().min(1).nullable(),
|
|
37502
|
+
decisions: external_exports.string().min(1).nullable(),
|
|
37503
|
+
relevantFiles: external_exports.string().min(1).nullable(),
|
|
37504
|
+
filesChanged: external_exports.string().min(1).nullable(),
|
|
37505
|
+
latestVerification: external_exports.string().min(1).nullable(),
|
|
37506
|
+
openProblems: external_exports.string().min(1).nullable(),
|
|
37507
|
+
nextActions: external_exports.string().min(1).nullable(),
|
|
37508
|
+
lastRunSeq: external_exports.number().int()
|
|
37509
|
+
});
|
|
37510
|
+
var mrrlinTaskStateReportSchema = external_exports.object({
|
|
37511
|
+
goal: external_exports.string().min(1).nullish(),
|
|
37512
|
+
acceptedRequirements: external_exports.string().min(1).nullish(),
|
|
37513
|
+
decisions: external_exports.string().min(1).nullish(),
|
|
37514
|
+
openProblems: external_exports.string().min(1).nullish(),
|
|
37515
|
+
nextActions: external_exports.string().min(1).nullish()
|
|
37516
|
+
});
|
|
37517
|
+
var taskStateResponseSchema = external_exports.object({
|
|
37518
|
+
data: mrrlinTaskStateSchema.nullable()
|
|
37519
|
+
});
|
|
37520
|
+
var retireThreadResponseSchema = external_exports.object({
|
|
37521
|
+
data: external_exports.object({ retiredThreadId: external_exports.string().min(1).nullable() })
|
|
37467
37522
|
});
|
|
37468
37523
|
var mrrlinExecutionRunCreateSchema = external_exports.object({
|
|
37469
37524
|
actor: external_exports.string().min(1),
|
|
@@ -37495,7 +37550,18 @@ var mrrlinExecutionRunUpdateSchema = external_exports.object({
|
|
|
37495
37550
|
prodWorkflowId: external_exports.number().int().positive().nullable().optional(),
|
|
37496
37551
|
pullRequestUrl: external_exports.string().min(1).nullable().optional(),
|
|
37497
37552
|
status: mrrlinExecutionRunStatusSchema.optional(),
|
|
37498
|
-
worktreeId: external_exports.string().min(1).nullable().optional()
|
|
37553
|
+
worktreeId: external_exports.string().min(1).nullable().optional(),
|
|
37554
|
+
// Slice 6 (cost telemetry) + Slice 7 (effort). All nullable and optional: per D6, NULL means
|
|
37555
|
+
// "not measured" and must never be confused with a measured zero, so a disabled slice omits its
|
|
37556
|
+
// fields entirely rather than writing 0. `nonnegative` rather than `positive` because a real
|
|
37557
|
+
// measured zero is legitimate here (a turn can genuinely produce 0 output tokens).
|
|
37558
|
+
promptBytes: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37559
|
+
turnCount: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37560
|
+
inputTokens: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37561
|
+
cachedInputTokens: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37562
|
+
outputTokens: external_exports.number().int().nonnegative().nullable().optional(),
|
|
37563
|
+
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).nullable().optional(),
|
|
37564
|
+
effortEscalationReason: external_exports.string().min(1).nullable().optional()
|
|
37499
37565
|
}).refine((value) => Object.keys(value).length > 0, {
|
|
37500
37566
|
message: "ExecutionRunUpdate requires at least one field"
|
|
37501
37567
|
});
|
|
@@ -38274,6 +38340,117 @@ function applyHistoricalBudgetIfNeeded(input) {
|
|
|
38274
38340
|
droppedHandles
|
|
38275
38341
|
};
|
|
38276
38342
|
}
|
|
38343
|
+
function renderTaskStateSection(state) {
|
|
38344
|
+
const rows = [
|
|
38345
|
+
["goal", state.goal],
|
|
38346
|
+
["accepted requirements", state.acceptedRequirements],
|
|
38347
|
+
["decisions (and why)", state.decisions],
|
|
38348
|
+
["relevant files", state.relevantFiles],
|
|
38349
|
+
["files changed", state.filesChanged],
|
|
38350
|
+
["latest verification", state.latestVerification],
|
|
38351
|
+
["open problems", state.openProblems],
|
|
38352
|
+
["next actions", state.nextActions]
|
|
38353
|
+
];
|
|
38354
|
+
const present = rows.filter(([, value]) => typeof value === "string" && value.trim().length > 0);
|
|
38355
|
+
if (present.length === 0) {
|
|
38356
|
+
return "## Task state (normative)\n(no state recorded yet \u2014 establish it before ending your turn)";
|
|
38357
|
+
}
|
|
38358
|
+
const lines2 = ["## Task state (normative)"];
|
|
38359
|
+
for (const [label, value] of present) {
|
|
38360
|
+
lines2.push(`${label}: ${value}`);
|
|
38361
|
+
}
|
|
38362
|
+
return lines2.join("\n");
|
|
38363
|
+
}
|
|
38364
|
+
function renderRunDelta(input) {
|
|
38365
|
+
const sections = [
|
|
38366
|
+
renderDeltaTaskStatus(input.task),
|
|
38367
|
+
renderDeltaRuns(input.newlyFinishedRuns),
|
|
38368
|
+
renderDeltaArtifacts(input.newArtifacts, input.omittedArtifactCount ?? 0)
|
|
38369
|
+
];
|
|
38370
|
+
return sections.join("\n\n");
|
|
38371
|
+
}
|
|
38372
|
+
function renderDeltaArtifacts(artifacts, omittedCount) {
|
|
38373
|
+
if (artifacts.length === 0 && omittedCount === 0)
|
|
38374
|
+
return "## New artifacts since your last turn\n(none)";
|
|
38375
|
+
const lines2 = ["## New artifacts since your last turn"];
|
|
38376
|
+
if (omittedCount > 0) {
|
|
38377
|
+
lines2.push(`(+${omittedCount} older artifact${omittedCount === 1 ? "" : "s"} omitted)`);
|
|
38378
|
+
}
|
|
38379
|
+
for (const artifact of artifacts) {
|
|
38380
|
+
if (artifact.kind === "file") {
|
|
38381
|
+
const p = artifact.payload;
|
|
38382
|
+
if (p.filename && p.stableUrl) {
|
|
38383
|
+
lines2.push(`- ${artifact.createdAt} [file] ${p.filename} (${p.class ?? "?"}, ${p.sizeBytes ?? "?"} bytes) ${p.stableUrl}`);
|
|
38384
|
+
continue;
|
|
38385
|
+
}
|
|
38386
|
+
}
|
|
38387
|
+
lines2.push(`- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${JSON.stringify(artifact.payload)}`);
|
|
38388
|
+
}
|
|
38389
|
+
return lines2.join("\n");
|
|
38390
|
+
}
|
|
38391
|
+
function renderDeltaTaskStatus(task) {
|
|
38392
|
+
const lines2 = ["## Since your last turn", `task ${task.id} status: ${task.status}`];
|
|
38393
|
+
if (task.latestJudgement) {
|
|
38394
|
+
lines2.push(`latest judgement (${task.latestJudgementKind ?? "unknown"}): ${task.latestJudgement}`);
|
|
38395
|
+
}
|
|
38396
|
+
return lines2.join("\n");
|
|
38397
|
+
}
|
|
38398
|
+
function renderDeltaRuns(runs) {
|
|
38399
|
+
if (runs.length === 0)
|
|
38400
|
+
return "## Runs completed since your last turn\n(none)";
|
|
38401
|
+
const lines2 = ["## Runs completed since your last turn"];
|
|
38402
|
+
for (const run3 of runs) {
|
|
38403
|
+
lines2.push(`- ${run3.id} [${run3.status}] finished ${run3.finishedAt ?? run3.createdAt}`);
|
|
38404
|
+
}
|
|
38405
|
+
return lines2.join("\n");
|
|
38406
|
+
}
|
|
38407
|
+
|
|
38408
|
+
// ../../packages/codex-client/dist/token-usage.js
|
|
38409
|
+
var TOKEN_USAGE_NOTIFICATION_METHOD = "thread/tokenUsage/updated";
|
|
38410
|
+
function readCount(source, key) {
|
|
38411
|
+
const value = source[key];
|
|
38412
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
38413
|
+
}
|
|
38414
|
+
function extractTurnTokenUsage(method, params) {
|
|
38415
|
+
if (method !== TOKEN_USAGE_NOTIFICATION_METHOD)
|
|
38416
|
+
return null;
|
|
38417
|
+
if (params === null || typeof params !== "object")
|
|
38418
|
+
return null;
|
|
38419
|
+
const usage = params.tokenUsage;
|
|
38420
|
+
if (usage === null || typeof usage !== "object")
|
|
38421
|
+
return null;
|
|
38422
|
+
const last = usage.last;
|
|
38423
|
+
if (last === null || typeof last !== "object")
|
|
38424
|
+
return null;
|
|
38425
|
+
const block = last;
|
|
38426
|
+
const extracted = {
|
|
38427
|
+
inputTokens: readCount(block, "inputTokens"),
|
|
38428
|
+
cachedInputTokens: readCount(block, "cachedInputTokens"),
|
|
38429
|
+
outputTokens: readCount(block, "outputTokens")
|
|
38430
|
+
};
|
|
38431
|
+
if (extracted.inputTokens === null && extracted.cachedInputTokens === null && extracted.outputTokens === null) {
|
|
38432
|
+
return null;
|
|
38433
|
+
}
|
|
38434
|
+
return extracted;
|
|
38435
|
+
}
|
|
38436
|
+
function addTurnTokenUsage(total, turn) {
|
|
38437
|
+
if (turn === null)
|
|
38438
|
+
return total;
|
|
38439
|
+
if (total === null)
|
|
38440
|
+
return { ...turn };
|
|
38441
|
+
const add2 = (a, b) => {
|
|
38442
|
+
if (a === null)
|
|
38443
|
+
return b;
|
|
38444
|
+
if (b === null)
|
|
38445
|
+
return a;
|
|
38446
|
+
return a + b;
|
|
38447
|
+
};
|
|
38448
|
+
return {
|
|
38449
|
+
inputTokens: add2(total.inputTokens, turn.inputTokens),
|
|
38450
|
+
cachedInputTokens: add2(total.cachedInputTokens, turn.cachedInputTokens),
|
|
38451
|
+
outputTokens: add2(total.outputTokens, turn.outputTokens)
|
|
38452
|
+
};
|
|
38453
|
+
}
|
|
38277
38454
|
|
|
38278
38455
|
// ../../packages/client/dist/agent-token-provider.js
|
|
38279
38456
|
var AgentTokenProviderError = class extends Error {
|
|
@@ -38998,6 +39175,34 @@ function createMrrlinClient(config2) {
|
|
|
38998
39175
|
throw error51;
|
|
38999
39176
|
}
|
|
39000
39177
|
},
|
|
39178
|
+
async getTaskState(projectSlug, taskId) {
|
|
39179
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/state`);
|
|
39180
|
+
return taskStateResponseSchema.parse(body).data;
|
|
39181
|
+
},
|
|
39182
|
+
async upsertTaskState(projectSlug, taskId, input) {
|
|
39183
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/state`, {
|
|
39184
|
+
body: JSON.stringify(mrrlinTaskStateUpsertSchema.parse(input)),
|
|
39185
|
+
headers: { "content-type": "application/json" },
|
|
39186
|
+
method: "PUT"
|
|
39187
|
+
});
|
|
39188
|
+
return taskStateResponseSchema.parse(body).data;
|
|
39189
|
+
},
|
|
39190
|
+
async retireThread(projectSlug, taskId, newThreadId) {
|
|
39191
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/retire-thread`, {
|
|
39192
|
+
body: JSON.stringify({ newThreadId }),
|
|
39193
|
+
headers: { "content-type": "application/json" },
|
|
39194
|
+
method: "POST"
|
|
39195
|
+
});
|
|
39196
|
+
return retireThreadResponseSchema.parse(body).data.retiredThreadId;
|
|
39197
|
+
},
|
|
39198
|
+
async reportTaskState(projectSlug, runId, report) {
|
|
39199
|
+
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/task-state`, {
|
|
39200
|
+
body: JSON.stringify(mrrlinTaskStateReportSchema.parse(report)),
|
|
39201
|
+
headers: { "content-type": "application/json" },
|
|
39202
|
+
method: "POST"
|
|
39203
|
+
});
|
|
39204
|
+
return taskStateResponseSchema.parse(body).data;
|
|
39205
|
+
},
|
|
39001
39206
|
async resolveHandoff(projectSlug, runId, input) {
|
|
39002
39207
|
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/resolve-handoff`, {
|
|
39003
39208
|
body: JSON.stringify(input),
|
|
@@ -39201,6 +39406,36 @@ function createMrrlinClient(config2) {
|
|
|
39201
39406
|
};
|
|
39202
39407
|
}
|
|
39203
39408
|
|
|
39409
|
+
// src/describe-error.ts
|
|
39410
|
+
var MAX_DETAIL_CHARS = 200;
|
|
39411
|
+
function truncate(value) {
|
|
39412
|
+
return value.length > MAX_DETAIL_CHARS ? value.slice(0, MAX_DETAIL_CHARS) : value;
|
|
39413
|
+
}
|
|
39414
|
+
function codeAndMessage(value) {
|
|
39415
|
+
if (!value || typeof value !== "object") return null;
|
|
39416
|
+
const obj = value;
|
|
39417
|
+
const code = typeof obj.code === "string" ? obj.code : null;
|
|
39418
|
+
const message = typeof obj.message === "string" ? obj.message : null;
|
|
39419
|
+
if (code) return message ? `${code}: ${message}` : code;
|
|
39420
|
+
return message;
|
|
39421
|
+
}
|
|
39422
|
+
function detailFromCause(cause) {
|
|
39423
|
+
if (!cause || typeof cause !== "object") return null;
|
|
39424
|
+
const direct = codeAndMessage(cause);
|
|
39425
|
+
const errors = cause.errors;
|
|
39426
|
+
const hasCode = typeof cause.code === "string";
|
|
39427
|
+
if (!hasCode && Array.isArray(errors) && errors.length > 0) {
|
|
39428
|
+
const fromFirst = codeAndMessage(errors[0]);
|
|
39429
|
+
if (fromFirst) return fromFirst;
|
|
39430
|
+
}
|
|
39431
|
+
return direct;
|
|
39432
|
+
}
|
|
39433
|
+
function describeError(error51) {
|
|
39434
|
+
if (!(error51 instanceof Error)) return String(error51);
|
|
39435
|
+
const detail = detailFromCause(error51.cause);
|
|
39436
|
+
return detail ? `${error51.message} (${truncate(detail)})` : error51.message;
|
|
39437
|
+
}
|
|
39438
|
+
|
|
39204
39439
|
// src/token-refresher.ts
|
|
39205
39440
|
function createTokenRefresher(opts) {
|
|
39206
39441
|
const setIntervalFn = opts.setIntervalFn ?? setInterval;
|
|
@@ -39217,7 +39452,7 @@ function createTokenRefresher(opts) {
|
|
|
39217
39452
|
opts.onOk?.();
|
|
39218
39453
|
} catch (error51) {
|
|
39219
39454
|
process.stderr.write(
|
|
39220
|
-
`[mrrlin-mcp director-bridge] token refresh failed (will retry next tick): ${
|
|
39455
|
+
`[mrrlin-mcp director-bridge] token refresh failed (will retry next tick): ${describeError(error51)}
|
|
39221
39456
|
`
|
|
39222
39457
|
);
|
|
39223
39458
|
opts.onError?.(error51);
|
|
@@ -39256,29 +39491,117 @@ function createServiceHealth() {
|
|
|
39256
39491
|
}
|
|
39257
39492
|
|
|
39258
39493
|
// src/turn-lock.ts
|
|
39494
|
+
var CHAT_LIMIT = 5;
|
|
39495
|
+
var AUTONOMOUS_LIMIT = 10;
|
|
39259
39496
|
function createTurnLock() {
|
|
39260
|
-
let
|
|
39261
|
-
let
|
|
39262
|
-
|
|
39263
|
-
|
|
39264
|
-
|
|
39265
|
-
|
|
39266
|
-
|
|
39267
|
-
|
|
39268
|
-
|
|
39269
|
-
|
|
39270
|
-
|
|
39271
|
-
|
|
39272
|
-
|
|
39273
|
-
|
|
39274
|
-
|
|
39275
|
-
|
|
39497
|
+
let chatTotalActive = 0;
|
|
39498
|
+
let autonomousTotalActive = 0;
|
|
39499
|
+
let chatRunning = 0;
|
|
39500
|
+
let autonomousRunning = 0;
|
|
39501
|
+
const chatQueue = [];
|
|
39502
|
+
const execQueue = [];
|
|
39503
|
+
const asyncQueue = [];
|
|
39504
|
+
let lastServed = null;
|
|
39505
|
+
let autonomousBound = false;
|
|
39506
|
+
const logBound = (queued) => {
|
|
39507
|
+
console.warn(
|
|
39508
|
+
`[mrrlin-mcp director-bridge] warn autonomous_ceiling_bound {"limit":${AUTONOMOUS_LIMIT},"queued":${queued}}`
|
|
39509
|
+
);
|
|
39510
|
+
};
|
|
39511
|
+
const maybeEnterBound = () => {
|
|
39512
|
+
if (!autonomousBound && autonomousRunning === AUTONOMOUS_LIMIT && execQueue.length + asyncQueue.length > 0) {
|
|
39513
|
+
autonomousBound = true;
|
|
39514
|
+
logBound(execQueue.length + asyncQueue.length);
|
|
39515
|
+
}
|
|
39516
|
+
};
|
|
39517
|
+
const maybeLeaveBound = () => {
|
|
39518
|
+
if (autonomousBound && (autonomousRunning < AUTONOMOUS_LIMIT || execQueue.length + asyncQueue.length === 0)) {
|
|
39519
|
+
autonomousBound = false;
|
|
39520
|
+
logBound(execQueue.length + asyncQueue.length);
|
|
39521
|
+
}
|
|
39522
|
+
};
|
|
39523
|
+
const acquire = (lane, startRunner) => {
|
|
39524
|
+
if (lane === "chat") {
|
|
39525
|
+
if (chatRunning < CHAT_LIMIT) {
|
|
39526
|
+
chatRunning += 1;
|
|
39527
|
+
startRunner();
|
|
39528
|
+
} else {
|
|
39529
|
+
chatQueue.push(startRunner);
|
|
39530
|
+
}
|
|
39531
|
+
} else {
|
|
39532
|
+
if (autonomousRunning < AUTONOMOUS_LIMIT) {
|
|
39533
|
+
autonomousRunning += 1;
|
|
39534
|
+
startRunner();
|
|
39535
|
+
} else {
|
|
39536
|
+
if (lane === "exec") {
|
|
39537
|
+
execQueue.push(startRunner);
|
|
39538
|
+
} else {
|
|
39539
|
+
asyncQueue.push(startRunner);
|
|
39276
39540
|
}
|
|
39277
|
-
|
|
39278
|
-
|
|
39279
|
-
}
|
|
39280
|
-
|
|
39541
|
+
maybeEnterBound();
|
|
39542
|
+
}
|
|
39543
|
+
}
|
|
39544
|
+
};
|
|
39545
|
+
const release = (lane) => {
|
|
39546
|
+
if (lane === "chat") {
|
|
39547
|
+
chatRunning -= 1;
|
|
39548
|
+
const next = chatQueue.shift();
|
|
39549
|
+
if (next) {
|
|
39550
|
+
chatRunning += 1;
|
|
39551
|
+
next();
|
|
39552
|
+
}
|
|
39553
|
+
} else {
|
|
39554
|
+
autonomousRunning -= 1;
|
|
39555
|
+
const execWait = execQueue.length > 0;
|
|
39556
|
+
const asyncWait = asyncQueue.length > 0;
|
|
39557
|
+
if (execWait || asyncWait) {
|
|
39558
|
+
let serveLane;
|
|
39559
|
+
if (execWait && !asyncWait) {
|
|
39560
|
+
serveLane = "exec";
|
|
39561
|
+
} else if (!execWait && asyncWait) {
|
|
39562
|
+
serveLane = "async";
|
|
39563
|
+
} else {
|
|
39564
|
+
serveLane = lastServed === "exec" ? "async" : "exec";
|
|
39565
|
+
}
|
|
39566
|
+
const queue = serveLane === "exec" ? execQueue : asyncQueue;
|
|
39567
|
+
const next = queue.shift();
|
|
39568
|
+
if (next) {
|
|
39569
|
+
autonomousRunning += 1;
|
|
39570
|
+
lastServed = serveLane;
|
|
39571
|
+
next();
|
|
39572
|
+
}
|
|
39573
|
+
}
|
|
39574
|
+
maybeLeaveBound();
|
|
39575
|
+
}
|
|
39576
|
+
};
|
|
39577
|
+
const run3 = async (lane, fn) => {
|
|
39578
|
+
if (lane === "chat") {
|
|
39579
|
+
chatTotalActive += 1;
|
|
39580
|
+
} else {
|
|
39581
|
+
autonomousTotalActive += 1;
|
|
39582
|
+
}
|
|
39583
|
+
return new Promise((resolve, reject) => {
|
|
39584
|
+
const start = () => {
|
|
39585
|
+
let bodyPromise;
|
|
39586
|
+
try {
|
|
39587
|
+
bodyPromise = fn();
|
|
39588
|
+
} catch (e) {
|
|
39589
|
+
bodyPromise = Promise.reject(e);
|
|
39590
|
+
}
|
|
39591
|
+
bodyPromise.then((v) => resolve(v)).catch((e) => reject(e)).finally(() => {
|
|
39592
|
+
if (lane === "chat") {
|
|
39593
|
+
chatTotalActive -= 1;
|
|
39594
|
+
} else {
|
|
39595
|
+
autonomousTotalActive -= 1;
|
|
39596
|
+
}
|
|
39597
|
+
release(lane);
|
|
39598
|
+
});
|
|
39599
|
+
};
|
|
39600
|
+
acquire(lane, start);
|
|
39601
|
+
});
|
|
39281
39602
|
};
|
|
39603
|
+
const activeCount = () => chatTotalActive + autonomousTotalActive;
|
|
39604
|
+
return { run: run3, activeCount };
|
|
39282
39605
|
}
|
|
39283
39606
|
|
|
39284
39607
|
// src/self-update.ts
|
|
@@ -39642,7 +39965,7 @@ function startCodexProbe(deps = {}) {
|
|
|
39642
39965
|
}
|
|
39643
39966
|
|
|
39644
39967
|
// src/_generated/version.ts
|
|
39645
|
-
var PKG_VERSION = "0.3.
|
|
39968
|
+
var PKG_VERSION = "0.3.64";
|
|
39646
39969
|
|
|
39647
39970
|
// src/api-base-url-migrate.ts
|
|
39648
39971
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
@@ -40142,7 +40465,7 @@ async function attemptSchedulerTick(deps) {
|
|
|
40142
40465
|
return { nextWakeAt: typeof data.nextWakeAt === "string" ? data.nextWakeAt : null, failed: false };
|
|
40143
40466
|
} catch (error51) {
|
|
40144
40467
|
process.stderr.write(
|
|
40145
|
-
`[mrrlin-mcp director-bridge] scheduler tick failed: ${
|
|
40468
|
+
`[mrrlin-mcp director-bridge] scheduler tick failed: ${describeError(error51)}
|
|
40146
40469
|
`
|
|
40147
40470
|
);
|
|
40148
40471
|
bridgeHealth?.recordFailure("schedulerTick", error51);
|
|
@@ -41831,6 +42154,11 @@ function renderMilestone(m, ctx) {
|
|
|
41831
42154
|
return "Autodeploy is enabled, but this task has nothing deployable \u2014 skipping";
|
|
41832
42155
|
case "daily_limit_reached":
|
|
41833
42156
|
return "Daily autonomous operations limit reached \u2014 I will continue tomorrow";
|
|
42157
|
+
case "thread_retired": {
|
|
42158
|
+
if (ctx.reason === "bounded_no_state")
|
|
42159
|
+
return "Started a fresh conversation for this task \u2014 the previous one grew too long and no progress notes were saved, so some earlier reasoning may be lost";
|
|
42160
|
+
return "Started a fresh conversation for this task \u2014 the previous one grew too long, so I carried the saved progress notes over";
|
|
42161
|
+
}
|
|
41834
42162
|
}
|
|
41835
42163
|
}
|
|
41836
42164
|
|
|
@@ -41854,6 +42182,82 @@ async function postRunFeed(client, projectSlug, runId, taskId, milestone, ctx =
|
|
|
41854
42182
|
}
|
|
41855
42183
|
}
|
|
41856
42184
|
|
|
42185
|
+
// src/output-projection.ts
|
|
42186
|
+
var FAILURE_PATTERNS = [
|
|
42187
|
+
/^\s*(?:E|FAIL|ERROR|ERR!)\b/i,
|
|
42188
|
+
/\bTraceback \(most recent call last\)/,
|
|
42189
|
+
/^\s*at\s+.+:\d+:\d+/,
|
|
42190
|
+
// stack frames
|
|
42191
|
+
/\bAssertionError\b|\bexpected\b.*\breceived\b|\bexpected:\b/i,
|
|
42192
|
+
/\berror\b[:\s]|\bfailed\b[:\s]|\bfailure\b[:\s]/i,
|
|
42193
|
+
/^\s*not ok\b/,
|
|
42194
|
+
// TAP
|
|
42195
|
+
/\b(?:SyntaxError|TypeError|ReferenceError|RangeError)\b/,
|
|
42196
|
+
/^\s*✕|^\s*✗|^\s*×/,
|
|
42197
|
+
/\bexit(?:ed)? (?:code|status)\s*[:=]?\s*[1-9]/i
|
|
42198
|
+
];
|
|
42199
|
+
var DEFAULT_OUTPUT_PROJECTION_MAX_CHARS = 2e3;
|
|
42200
|
+
function projectCommandOutput(output3, options = {}) {
|
|
42201
|
+
const maxChars = options.maxChars ?? DEFAULT_OUTPUT_PROJECTION_MAX_CHARS;
|
|
42202
|
+
const exitStatus = options.exitStatus ?? null;
|
|
42203
|
+
const fullOutputRef = options.fullOutputRef ?? null;
|
|
42204
|
+
const label = options.label ?? "command output";
|
|
42205
|
+
const lines2 = output3.split(/\r?\n/);
|
|
42206
|
+
const matched = [];
|
|
42207
|
+
for (let index = 0; index < lines2.length; index += 1) {
|
|
42208
|
+
const line = lines2[index] ?? "";
|
|
42209
|
+
if (FAILURE_PATTERNS.some((pattern) => pattern.test(line))) matched.push(index);
|
|
42210
|
+
}
|
|
42211
|
+
const candidates = matched.length > 0 ? matched : lines2.map((_, index) => index);
|
|
42212
|
+
const kept = [];
|
|
42213
|
+
let used = 0;
|
|
42214
|
+
for (let cursor = candidates.length - 1; cursor >= 0; cursor -= 1) {
|
|
42215
|
+
const index = candidates[cursor];
|
|
42216
|
+
if (index === void 0) continue;
|
|
42217
|
+
let text = lines2[index] ?? "";
|
|
42218
|
+
if (text.trim().length === 0) continue;
|
|
42219
|
+
if (text.length > maxChars) {
|
|
42220
|
+
text = `${text.slice(0, Math.max(0, maxChars - 14))}\u2026[line cut]`;
|
|
42221
|
+
}
|
|
42222
|
+
const cost = text.length + 1;
|
|
42223
|
+
if (used + cost > maxChars) break;
|
|
42224
|
+
kept.push({ index, text });
|
|
42225
|
+
used += cost;
|
|
42226
|
+
}
|
|
42227
|
+
kept.sort((a, b) => a.index - b.index);
|
|
42228
|
+
const failureLines = kept.map((entry) => entry.text);
|
|
42229
|
+
const carried = failureLines.reduce((total, line) => total + line.length + 1, 0);
|
|
42230
|
+
const omittedChars = Math.max(0, output3.length - carried);
|
|
42231
|
+
const parts = [`${label}:`];
|
|
42232
|
+
parts.push(exitStatus === null ? "exit status unknown" : `exit status ${exitStatus}`);
|
|
42233
|
+
parts.push(
|
|
42234
|
+
matched.length > 0 ? `${failureLines.length} of ${matched.length} failure-bearing line(s) shown` : `no failure markers found; showing the tail (${failureLines.length} line(s))`
|
|
42235
|
+
);
|
|
42236
|
+
if (omittedChars > 0) {
|
|
42237
|
+
parts.push(
|
|
42238
|
+
fullOutputRef ? `${omittedChars} chars omitted \u2014 retrieve ${fullOutputRef} for the full output` : `${omittedChars} chars omitted`
|
|
42239
|
+
);
|
|
42240
|
+
}
|
|
42241
|
+
return {
|
|
42242
|
+
exitStatus,
|
|
42243
|
+
failureLines,
|
|
42244
|
+
summary: parts.join(" "),
|
|
42245
|
+
omittedChars,
|
|
42246
|
+
fullOutputRef,
|
|
42247
|
+
usedTailFallback: matched.length === 0
|
|
42248
|
+
};
|
|
42249
|
+
}
|
|
42250
|
+
function renderOutputProjection(projection) {
|
|
42251
|
+
const lines2 = [projection.summary];
|
|
42252
|
+
if (projection.failureLines.length > 0) {
|
|
42253
|
+
lines2.push(...projection.failureLines);
|
|
42254
|
+
}
|
|
42255
|
+
return lines2.join("\n");
|
|
42256
|
+
}
|
|
42257
|
+
function outputProjectionDisabled(env = process.env) {
|
|
42258
|
+
return (env.MRRLIN_EXEC_OUTPUT_PROJECTION_DISABLE ?? "").trim() === "1";
|
|
42259
|
+
}
|
|
42260
|
+
|
|
41857
42261
|
// src/deploy-gate.ts
|
|
41858
42262
|
var DEFAULT_DEPLOY_POLL_BUDGET_SECONDS = 1200;
|
|
41859
42263
|
var DEFAULT_PROD_GRACE_SECONDS = 900;
|
|
@@ -42209,10 +42613,48 @@ async function runDeployGateTick(deps, projectSlug, run3, task) {
|
|
|
42209
42613
|
task,
|
|
42210
42614
|
run: run3
|
|
42211
42615
|
});
|
|
42616
|
+
const verificationAttempt = attemptIndex(artifacts, env, "verification") + 1;
|
|
42617
|
+
let outputRef = null;
|
|
42618
|
+
if (verdict.fullOutput && !outputProjectionDisabled()) {
|
|
42619
|
+
outputRef = `EA-veroutput-${env}-${verificationAttempt}`;
|
|
42620
|
+
const stored = await deps.client.appendExecutionArtifact(projectSlug, run3.id, {
|
|
42621
|
+
id: outputRef,
|
|
42622
|
+
kind: "command_output",
|
|
42623
|
+
payload: { env, source: "verification-turn", output: verdict.fullOutput }
|
|
42624
|
+
}).then(() => true).catch(() => false);
|
|
42625
|
+
if (!stored) outputRef = null;
|
|
42626
|
+
}
|
|
42627
|
+
const projection = verdict.fullOutput && !outputProjectionDisabled() ? projectCommandOutput(verdict.fullOutput, {
|
|
42628
|
+
// A model turn reports no exit code; claiming 0 would assert a success never observed.
|
|
42629
|
+
exitStatus: null,
|
|
42630
|
+
fullOutputRef: outputRef,
|
|
42631
|
+
label: `verification (${env})`
|
|
42632
|
+
}) : null;
|
|
42633
|
+
if (projection?.usedTailFallback) {
|
|
42634
|
+
console.warn(
|
|
42635
|
+
JSON.stringify({
|
|
42636
|
+
event: "output_projection_tail_fallback",
|
|
42637
|
+
projectSlug,
|
|
42638
|
+
runId: run3.id,
|
|
42639
|
+
env,
|
|
42640
|
+
detail: "no failure marker matched; the projection carries the tail instead"
|
|
42641
|
+
})
|
|
42642
|
+
);
|
|
42643
|
+
}
|
|
42212
42644
|
await deps.client.appendExecutionArtifact(projectSlug, run3.id, {
|
|
42213
|
-
id: `EA-verification-${env}-${
|
|
42645
|
+
id: `EA-verification-${env}-${verificationAttempt}`,
|
|
42214
42646
|
kind: "verification",
|
|
42215
|
-
payload: {
|
|
42647
|
+
payload: {
|
|
42648
|
+
env,
|
|
42649
|
+
verdict: verdict.verdict,
|
|
42650
|
+
summary: verdict.summary,
|
|
42651
|
+
turnRef: verdict.turnRef,
|
|
42652
|
+
...projection ? {
|
|
42653
|
+
projection: renderOutputProjection(projection),
|
|
42654
|
+
fullOutputArtifactId: projection.fullOutputRef,
|
|
42655
|
+
omittedChars: projection.omittedChars
|
|
42656
|
+
} : {}
|
|
42657
|
+
}
|
|
42216
42658
|
});
|
|
42217
42659
|
if (verdict.verdict === "fail") {
|
|
42218
42660
|
if (env === "prod" && task.autonomyLevel === "auto") {
|
|
@@ -42524,6 +42966,112 @@ function buildPullRequestBody(input) {
|
|
|
42524
42966
|
${lines2.join("\n")}`;
|
|
42525
42967
|
}
|
|
42526
42968
|
|
|
42969
|
+
// src/context-compact/policy.ts
|
|
42970
|
+
var DEFAULT_THRESHOLD_CHARS = 12e3;
|
|
42971
|
+
var DEFAULT_MAX_ITEMS = 25;
|
|
42972
|
+
function readPositiveInt(value, fallback) {
|
|
42973
|
+
const raw = (value ?? "").trim();
|
|
42974
|
+
if (!raw) return fallback;
|
|
42975
|
+
const parsed = Number.parseInt(raw, 10);
|
|
42976
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
42977
|
+
}
|
|
42978
|
+
function readCompactPolicy(env = process.env) {
|
|
42979
|
+
const disabled = (env.MRRLIN_MCP_COMPACT_DISABLE ?? "").trim() === "1";
|
|
42980
|
+
return {
|
|
42981
|
+
enabled: !disabled,
|
|
42982
|
+
thresholdChars: readPositiveInt(env.MRRLIN_MCP_COMPACT_THRESHOLD_CHARS, DEFAULT_THRESHOLD_CHARS),
|
|
42983
|
+
maxItems: readPositiveInt(env.MRRLIN_MCP_COMPACT_MAX_ITEMS, DEFAULT_MAX_ITEMS),
|
|
42984
|
+
debug: (env.MRRLIN_MCP_COMPACT_DEBUG ?? "").trim() === "1",
|
|
42985
|
+
retrievalBudgetDisable: (env.MRRLIN_RETRIEVAL_BUDGET_DISABLE ?? "").trim() === "1"
|
|
42986
|
+
};
|
|
42987
|
+
}
|
|
42988
|
+
function warmDeltaDisabled(env = process.env) {
|
|
42989
|
+
return (env.MRRLIN_EXEC_WARM_DELTA_DISABLE ?? "").trim() === "1";
|
|
42990
|
+
}
|
|
42991
|
+
var DEFAULT_THREAD_MAX_RUNS = 12;
|
|
42992
|
+
var THREAD_MAX_RUNS_MIN = 4;
|
|
42993
|
+
var THREAD_MAX_RUNS_MAX = 50;
|
|
42994
|
+
var DEFAULT_THREAD_RETIRE_GRACE_RUNS = 6;
|
|
42995
|
+
var warnedOutOfRange = /* @__PURE__ */ new Set();
|
|
42996
|
+
function readBoundedInt(name, raw, fallback, min, max) {
|
|
42997
|
+
const trimmed = (raw ?? "").trim();
|
|
42998
|
+
if (!trimmed) return fallback;
|
|
42999
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
43000
|
+
if (Number.isInteger(parsed) && parsed >= min && parsed <= max) return parsed;
|
|
43001
|
+
if (!warnedOutOfRange.has(name)) {
|
|
43002
|
+
warnedOutOfRange.add(name);
|
|
43003
|
+
console.warn(
|
|
43004
|
+
JSON.stringify({
|
|
43005
|
+
event: "thread_retirement_bound_out_of_range",
|
|
43006
|
+
variable: name,
|
|
43007
|
+
provided: trimmed,
|
|
43008
|
+
accepted: `${min}-${max}`,
|
|
43009
|
+
usingDefault: fallback
|
|
43010
|
+
})
|
|
43011
|
+
);
|
|
43012
|
+
}
|
|
43013
|
+
return fallback;
|
|
43014
|
+
}
|
|
43015
|
+
function threadMaxRuns(env = process.env) {
|
|
43016
|
+
return readBoundedInt(
|
|
43017
|
+
"MRRLIN_EXEC_THREAD_MAX_RUNS",
|
|
43018
|
+
env.MRRLIN_EXEC_THREAD_MAX_RUNS,
|
|
43019
|
+
DEFAULT_THREAD_MAX_RUNS,
|
|
43020
|
+
THREAD_MAX_RUNS_MIN,
|
|
43021
|
+
THREAD_MAX_RUNS_MAX
|
|
43022
|
+
);
|
|
43023
|
+
}
|
|
43024
|
+
function threadRetireGraceRuns(env = process.env) {
|
|
43025
|
+
return readBoundedInt(
|
|
43026
|
+
"MRRLIN_EXEC_THREAD_RETIRE_GRACE_RUNS",
|
|
43027
|
+
env.MRRLIN_EXEC_THREAD_RETIRE_GRACE_RUNS,
|
|
43028
|
+
DEFAULT_THREAD_RETIRE_GRACE_RUNS,
|
|
43029
|
+
0,
|
|
43030
|
+
THREAD_MAX_RUNS_MAX
|
|
43031
|
+
);
|
|
43032
|
+
}
|
|
43033
|
+
function threadRetireDisabled(env = process.env) {
|
|
43034
|
+
return (env.MRRLIN_EXEC_THREAD_RETIRE_DISABLE ?? "").trim() === "1";
|
|
43035
|
+
}
|
|
43036
|
+
|
|
43037
|
+
// src/thread-retirement.ts
|
|
43038
|
+
function decideThreadRetirement(input) {
|
|
43039
|
+
if (input.disabled) return { retire: false };
|
|
43040
|
+
if (input.threadRunCount < input.maxRuns) return { retire: false };
|
|
43041
|
+
if (input.hasNarrative) return { retire: true, reason: "bounded_run_count" };
|
|
43042
|
+
if (input.threadRunCount >= input.maxRuns + input.graceRuns) {
|
|
43043
|
+
return { retire: true, reason: "bounded_no_state" };
|
|
43044
|
+
}
|
|
43045
|
+
return { retire: false };
|
|
43046
|
+
}
|
|
43047
|
+
function hasNarrativeContent(state) {
|
|
43048
|
+
if (!state) return false;
|
|
43049
|
+
return [state.goal, state.acceptedRequirements, state.decisions, state.openProblems, state.nextActions].some(
|
|
43050
|
+
(field) => typeof field === "string" && field.trim().length > 0
|
|
43051
|
+
);
|
|
43052
|
+
}
|
|
43053
|
+
|
|
43054
|
+
// src/effort-tier.ts
|
|
43055
|
+
var DEFAULT_EFFORT_LEVEL = "medium";
|
|
43056
|
+
var MECHANICAL_EFFORT_LEVEL = "low";
|
|
43057
|
+
var ESCALATED_EFFORT_LEVEL = "high";
|
|
43058
|
+
function deriveEffort(input) {
|
|
43059
|
+
if (input.disabled) return null;
|
|
43060
|
+
if (input.impact === "high") {
|
|
43061
|
+
return {
|
|
43062
|
+
level: ESCALATED_EFFORT_LEVEL,
|
|
43063
|
+
escalationReason: "task impact is high (operator-set)"
|
|
43064
|
+
};
|
|
43065
|
+
}
|
|
43066
|
+
if (input.impact === "low") {
|
|
43067
|
+
return { level: MECHANICAL_EFFORT_LEVEL, escalationReason: null };
|
|
43068
|
+
}
|
|
43069
|
+
return { level: DEFAULT_EFFORT_LEVEL, escalationReason: null };
|
|
43070
|
+
}
|
|
43071
|
+
function effortDisabled(env = process.env) {
|
|
43072
|
+
return (env.MRRLIN_EXEC_EFFORT_DISABLE ?? "").trim() === "1";
|
|
43073
|
+
}
|
|
43074
|
+
|
|
42527
43075
|
// src/execution-consumer.ts
|
|
42528
43076
|
var DEFAULT_STALE_SECONDS = 300;
|
|
42529
43077
|
var DEFAULT_MAX_LEASE_RECLAIMS = 3;
|
|
@@ -42654,6 +43202,9 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
42654
43202
|
" if you are blocked and a human must decide before the work can continue, leave it at `handoff_needed`.",
|
|
42655
43203
|
"",
|
|
42656
43204
|
"File artifacts:",
|
|
43205
|
+
"- Decide BEFORE writing your result whether the operator will scan it, compare across it,",
|
|
43206
|
+
" re-read it later, keep it, or share it. If so it is a file, not inline text \u2014 judge by use,",
|
|
43207
|
+
" not by topic, domain, or data type.",
|
|
42657
43208
|
"- When your output is a file (markdown report, HTML, screenshot), upload it with",
|
|
42658
43209
|
" `upload_artifact` and embed the returned markdown link \u2014 never inline large blobs",
|
|
42659
43210
|
" into chat, spec, or note text.",
|
|
@@ -42661,8 +43212,28 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
42661
43212
|
" or evidence page will reference (180 days). Always pass taskId/runId when known.",
|
|
42662
43213
|
"- Anything user-facing delivered as a file MUST be uploaded. Files are capped at 5MB \u2014",
|
|
42663
43214
|
" split or summarize larger outputs.",
|
|
43215
|
+
"- `upload_artifact` needs a real local file path, so it presupposes you can write one. When this",
|
|
43216
|
+
" run path CAN write files, produce the artifact normally \u2014 that is the default. ONLY when it",
|
|
43217
|
+
" cannot write, do NOT attempt the file: put the summary in your result text and state what a",
|
|
43218
|
+
" file would have held. Never fake a path you did not write.",
|
|
43219
|
+
"",
|
|
43220
|
+
"Produced URLs \u2014 the operator's per-page links come only from you:",
|
|
43221
|
+
"- Call `record_produced_url` after publishing EACH web page this task produced, so the task UI",
|
|
43222
|
+
" links the page itself instead of the deployed site root. One call per page, not per run.",
|
|
43223
|
+
"- Pass either a full external https URL, or a path starting with `/` to resolve against the",
|
|
43224
|
+
" run's deployed env URL. An optional `label` (<=40 chars) replaces the default page title.",
|
|
43225
|
+
"- Never record the site root or a bare domain \u2014 the point is a link that opens the page itself.",
|
|
43226
|
+
" If a published page is not serving yet, record it once the deploy completes; never skip it.",
|
|
43227
|
+
" Nothing else populates these links, so a page you do not record has none at all.",
|
|
42664
43228
|
"",
|
|
42665
43229
|
"Browser tools (when the run needs to drive a browser):",
|
|
43230
|
+
"- Choose the browser FIRST when the target is a rendered UI, the question is inherently visual,",
|
|
43231
|
+
" the content sits behind a logged-in session, or reconstructing the answer would take many",
|
|
43232
|
+
" chained fetches. A single cheap fetch is still right for a plain one-shot HTTP/HTML check.",
|
|
43233
|
+
"- Escalate from a fetch to the browser in the same run on 401/403, a bot/consent/CAPTCHA wall,",
|
|
43234
|
+
" an empty JS-only shell, or a login gate. A 429 is rate limiting, not a wall \u2014 back off and",
|
|
43235
|
+
" retry once, escalating only if it persists. A failed fetch is a signal to change tool, not",
|
|
43236
|
+
" a finding \u2014 never conclude a public target is unverifiable without having tried the browser.",
|
|
42666
43237
|
"- Use tools from the `mrrlin-browser` MCP server ONLY \u2014 browser_navigate, browser_click,",
|
|
42667
43238
|
" browser_snapshot, browser_type, etc. Never invoke the same-named tools from any other",
|
|
42668
43239
|
" browser MCP (`playwright`, `chrome-devtools`, `puppeteer`, ...) that may also be connected.",
|
|
@@ -42688,6 +43259,18 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
42688
43259
|
" ## Result \u2014 what was accomplished, key findings or metrics, follow-up tasks created.",
|
|
42689
43260
|
" Do NOT remove earlier sections \u2014 they remain useful for context.",
|
|
42690
43261
|
" Adapt depth to the task: a one-line fix needs one sentence, not four headings.",
|
|
43262
|
+
"",
|
|
43263
|
+
// Slice 4 of token-efficient-execution-runs (D8). This is the ONLY way the reasoning behind a
|
|
43264
|
+
// decision survives past this conversation: a thread is retired once it has accumulated too many
|
|
43265
|
+
// runs, and the next run then starts from this record plus the repository. Everything else the
|
|
43266
|
+
// next run receives (spec, run history, artifacts) is reconstructed from the database anyway —
|
|
43267
|
+
// what only the model holds is why it chose what it chose, and what it intended to do next.
|
|
43268
|
+
"BEFORE you end your turn, call `update_task_state` to record where this task now stands: the",
|
|
43269
|
+
"goal as you understand it, decisions you made AND WHY, problems still open, and the next",
|
|
43270
|
+
"concrete actions. A later run may start from a FRESH conversation that has none of this turn's",
|
|
43271
|
+
"context \u2014 that record is what it will read. A decision without its reason is the thing most",
|
|
43272
|
+
"often lost, so state the reason. Omit any field you learned nothing new about; an omitted field",
|
|
43273
|
+
"keeps its previous value. Do not restate the spec or the file list \u2014 the next run gets those.",
|
|
42691
43274
|
""
|
|
42692
43275
|
];
|
|
42693
43276
|
var CONSUMER_BASE_INSTRUCTIONS = [
|
|
@@ -42923,13 +43506,15 @@ async function runHasLease(deps, projectSlug, runId, leaseId) {
|
|
|
42923
43506
|
return false;
|
|
42924
43507
|
}
|
|
42925
43508
|
}
|
|
42926
|
-
async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inputText, overallMs, phase) {
|
|
43509
|
+
async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inputText, overallMs, phase, effort) {
|
|
42927
43510
|
const mapper = new CodexBridgeEventMapper();
|
|
42928
43511
|
mapper.reset();
|
|
42929
43512
|
let turnError = null;
|
|
42930
43513
|
let interruptFailed = false;
|
|
42931
43514
|
let touch = () => {
|
|
42932
43515
|
};
|
|
43516
|
+
let turnUsage = null;
|
|
43517
|
+
const promptBytes = Buffer.byteLength(inputText, "utf8");
|
|
42933
43518
|
const turnStartedAt = Date.now();
|
|
42934
43519
|
let lastActivityAt = turnStartedAt;
|
|
42935
43520
|
const persist = async (event) => {
|
|
@@ -42945,7 +43530,7 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
42945
43530
|
} catch {
|
|
42946
43531
|
}
|
|
42947
43532
|
};
|
|
42948
|
-
return deps.
|
|
43533
|
+
return deps.run("exec", async () => {
|
|
42949
43534
|
const pending = [];
|
|
42950
43535
|
let resolveTurnEnded = null;
|
|
42951
43536
|
const turnEnded = new Promise((resolve) => {
|
|
@@ -42958,6 +43543,8 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
42958
43543
|
lastActivityAt = Date.now();
|
|
42959
43544
|
touch();
|
|
42960
43545
|
}
|
|
43546
|
+
const usage = !notifThreadId || notifThreadId === threadId ? extractTurnTokenUsage(method, params) : null;
|
|
43547
|
+
if (usage) turnUsage = usage;
|
|
42961
43548
|
const events = mapper.mapNotification(
|
|
42962
43549
|
{ method, params },
|
|
42963
43550
|
threadId
|
|
@@ -42983,7 +43570,8 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
42983
43570
|
touch = activity.touch;
|
|
42984
43571
|
const accepted = await codex.turn.start({
|
|
42985
43572
|
threadId,
|
|
42986
|
-
input: [{ type: "text", text: inputText }]
|
|
43573
|
+
input: [{ type: "text", text: inputText }],
|
|
43574
|
+
...effort === void 0 ? {} : { effort }
|
|
42987
43575
|
});
|
|
42988
43576
|
acceptedTurnId = accepted?.turn?.id ?? null;
|
|
42989
43577
|
await turnEnded;
|
|
@@ -43018,7 +43606,8 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
43018
43606
|
clearInterval(touchTimer);
|
|
43019
43607
|
unsub();
|
|
43020
43608
|
}
|
|
43021
|
-
|
|
43609
|
+
const telemetry = { promptBytes, usage: turnUsage };
|
|
43610
|
+
return turnError ? { ok: false, error: turnError, interruptFailed, telemetry } : { ok: true, telemetry };
|
|
43022
43611
|
});
|
|
43023
43612
|
}
|
|
43024
43613
|
async function driveRun(deps, projectSlug, runId) {
|
|
@@ -43088,6 +43677,47 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
43088
43677
|
} : context
|
|
43089
43678
|
);
|
|
43090
43679
|
bundle += await deployWorkflowReverifyNotice(deps.client, projectSlug);
|
|
43680
|
+
const runTelemetry = {
|
|
43681
|
+
promptBytes: 0,
|
|
43682
|
+
turnCount: 0,
|
|
43683
|
+
usage: null
|
|
43684
|
+
};
|
|
43685
|
+
const recordTurn = (outcome) => {
|
|
43686
|
+
runTelemetry.promptBytes += outcome.telemetry.promptBytes;
|
|
43687
|
+
runTelemetry.turnCount += 1;
|
|
43688
|
+
runTelemetry.usage = addTurnTokenUsage(runTelemetry.usage, outcome.telemetry.usage);
|
|
43689
|
+
return outcome;
|
|
43690
|
+
};
|
|
43691
|
+
const effortDecision = deriveEffort({
|
|
43692
|
+
impact: task.impact,
|
|
43693
|
+
type: task.type,
|
|
43694
|
+
disabled: effortDisabled()
|
|
43695
|
+
});
|
|
43696
|
+
const taskState = await deps.client.getTaskState(projectSlug, task.id).catch(() => null);
|
|
43697
|
+
const retirement = decideThreadRetirement({
|
|
43698
|
+
threadRunCount: taskState?.threadRunCount ?? 0,
|
|
43699
|
+
hasNarrative: hasNarrativeContent(taskState),
|
|
43700
|
+
disabled: threadRetireDisabled(),
|
|
43701
|
+
maxRuns: threadMaxRuns(),
|
|
43702
|
+
graceRuns: threadRetireGraceRuns()
|
|
43703
|
+
});
|
|
43704
|
+
const threadIdForAcquire = retirement.retire ? null : task.directorThreadId;
|
|
43705
|
+
if (retirement.retire) {
|
|
43706
|
+
console.warn(
|
|
43707
|
+
JSON.stringify({
|
|
43708
|
+
event: "thread_retirement_triggered",
|
|
43709
|
+
projectSlug,
|
|
43710
|
+
runId,
|
|
43711
|
+
taskId: task.id,
|
|
43712
|
+
reason: retirement.reason,
|
|
43713
|
+
threadRunCount: taskState?.threadRunCount ?? 0,
|
|
43714
|
+
retiringThreadId: task.directorThreadId
|
|
43715
|
+
})
|
|
43716
|
+
);
|
|
43717
|
+
}
|
|
43718
|
+
if (taskState) bundle += `
|
|
43719
|
+
|
|
43720
|
+
${renderTaskStateSection(taskState)}`;
|
|
43091
43721
|
const checkout = deps.checkoutRegistry?.get(projectSlug) ?? null;
|
|
43092
43722
|
let sandbox = "read-only";
|
|
43093
43723
|
let turnCwd = deps.codexCwd ?? null;
|
|
@@ -43163,7 +43793,7 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
43163
43793
|
const pushFn = deps.pushRunBranch ?? pushRunBranch;
|
|
43164
43794
|
try {
|
|
43165
43795
|
const { threadId, resumed } = await acquireThread(codex, {
|
|
43166
|
-
threadId:
|
|
43796
|
+
threadId: threadIdForAcquire,
|
|
43167
43797
|
startParams: {
|
|
43168
43798
|
cwd: turnCwd,
|
|
43169
43799
|
approvalPolicy: "never",
|
|
@@ -43179,9 +43809,36 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
43179
43809
|
},
|
|
43180
43810
|
onThreadIdChanged: async (newId) => {
|
|
43181
43811
|
if (!await runHasLease(deps, projectSlug, runId, leaseId)) return;
|
|
43812
|
+
if (retirement.retire) return;
|
|
43182
43813
|
await deps.client.updateTask(projectSlug, task.id, { directorThreadId: newId });
|
|
43183
43814
|
}
|
|
43184
43815
|
});
|
|
43816
|
+
if (retirement.retire && await runHasLease(deps, projectSlug, runId, leaseId)) {
|
|
43817
|
+
let persisted = true;
|
|
43818
|
+
const retiredThreadId = await deps.client.retireThread(projectSlug, task.id, threadId).catch(() => {
|
|
43819
|
+
persisted = false;
|
|
43820
|
+
return null;
|
|
43821
|
+
});
|
|
43822
|
+
if (persisted) {
|
|
43823
|
+
await postRunFeed(deps.client, projectSlug, runId, task.id, "thread_retired", {
|
|
43824
|
+
reason: retirement.reason
|
|
43825
|
+
});
|
|
43826
|
+
}
|
|
43827
|
+
console.warn(
|
|
43828
|
+
JSON.stringify({
|
|
43829
|
+
event: persisted ? "thread_retired" : "thread_retirement_write_failed",
|
|
43830
|
+
projectSlug,
|
|
43831
|
+
runId,
|
|
43832
|
+
taskId: task.id,
|
|
43833
|
+
reason: retirement.reason,
|
|
43834
|
+
retiredThreadId,
|
|
43835
|
+
newThreadId: threadId,
|
|
43836
|
+
// Says out loud what an unpersisted retirement costs, so the log line is actionable
|
|
43837
|
+
// rather than merely present.
|
|
43838
|
+
...persisted ? {} : { impact: "counter not reset and task still on the old thread; later runs will re-retire until this write succeeds" }
|
|
43839
|
+
})
|
|
43840
|
+
);
|
|
43841
|
+
}
|
|
43185
43842
|
if (!resumed) {
|
|
43186
43843
|
const primingBudgetMs = remainingTurnBudgetMs();
|
|
43187
43844
|
if (primingBudgetMs <= 0) {
|
|
@@ -43196,14 +43853,52 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
43196
43853
|
);
|
|
43197
43854
|
return;
|
|
43198
43855
|
}
|
|
43199
|
-
const primed =
|
|
43856
|
+
const primed = recordTurn(
|
|
43857
|
+
await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, bundle, primingBudgetMs, "priming", effortDecision?.level)
|
|
43858
|
+
);
|
|
43200
43859
|
if (!primed.ok) {
|
|
43860
|
+
if (retirement.retire && task.directorThreadId) {
|
|
43861
|
+
await deps.client.updateTask(projectSlug, task.id, { directorThreadId: task.directorThreadId }).catch(() => {
|
|
43862
|
+
});
|
|
43863
|
+
console.warn(
|
|
43864
|
+
JSON.stringify({
|
|
43865
|
+
event: "thread_retirement_reverted",
|
|
43866
|
+
projectSlug,
|
|
43867
|
+
runId,
|
|
43868
|
+
taskId: task.id,
|
|
43869
|
+
reason: retirement.reason,
|
|
43870
|
+
restoredThreadId: task.directorThreadId,
|
|
43871
|
+
detail: "re-prime failed; predecessor thread restored"
|
|
43872
|
+
})
|
|
43873
|
+
);
|
|
43874
|
+
}
|
|
43201
43875
|
await resetWedgedThread(deps, projectSlug, task.id, primed.interruptFailed);
|
|
43202
43876
|
await failRun(deps, projectSlug, task, runId, leaseId, primed.error);
|
|
43203
43877
|
return;
|
|
43204
43878
|
}
|
|
43205
43879
|
}
|
|
43206
|
-
const
|
|
43880
|
+
const priorRuns = context.runs.filter((r) => r.id !== runId);
|
|
43881
|
+
const priorRun = priorRuns[priorRuns.length - 1] ?? null;
|
|
43882
|
+
const useDelta = resumed && !warmDeltaDisabled() && priorRun !== null;
|
|
43883
|
+
let resumeContent;
|
|
43884
|
+
if (useDelta) {
|
|
43885
|
+
const cutoffTime = priorRun.finishedAt ?? priorRun.createdAt;
|
|
43886
|
+
const newArtifacts = context.artifacts.filter((a) => a.createdAt > cutoffTime);
|
|
43887
|
+
resumeContent = renderRunDelta({
|
|
43888
|
+
task: {
|
|
43889
|
+
id: task.id,
|
|
43890
|
+
status: task.status,
|
|
43891
|
+
latestJudgement: task.latestJudgement,
|
|
43892
|
+
latestJudgementKind: task.latestJudgementKind
|
|
43893
|
+
},
|
|
43894
|
+
newlyFinishedRuns: [priorRun],
|
|
43895
|
+
newArtifacts,
|
|
43896
|
+
omittedArtifactCount: context.omittedArtifactCount
|
|
43897
|
+
});
|
|
43898
|
+
} else {
|
|
43899
|
+
resumeContent = bundle;
|
|
43900
|
+
}
|
|
43901
|
+
const executionPrompt = resumed ? `${resumeContent}
|
|
43207
43902
|
|
|
43208
43903
|
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.`;
|
|
43209
43904
|
const workBudgetMs = remainingTurnBudgetMs();
|
|
@@ -43219,7 +43914,9 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
43219
43914
|
);
|
|
43220
43915
|
return;
|
|
43221
43916
|
}
|
|
43222
|
-
const outcome =
|
|
43917
|
+
const outcome = recordTurn(
|
|
43918
|
+
await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, executionPrompt, workBudgetMs, "work", effortDecision?.level)
|
|
43919
|
+
);
|
|
43223
43920
|
if (!outcome.ok) {
|
|
43224
43921
|
await resetWedgedThread(deps, projectSlug, task.id, outcome.interruptFailed);
|
|
43225
43922
|
await failRun(deps, projectSlug, task, runId, leaseId, outcome.error);
|
|
@@ -43243,16 +43940,19 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
43243
43940
|
})
|
|
43244
43941
|
);
|
|
43245
43942
|
const completionPrompt = buildCompletionTurnPrompt(task.id, readiness.task.status);
|
|
43246
|
-
const completionOutcome =
|
|
43247
|
-
|
|
43248
|
-
|
|
43249
|
-
|
|
43250
|
-
|
|
43251
|
-
|
|
43252
|
-
|
|
43253
|
-
|
|
43254
|
-
|
|
43255
|
-
|
|
43943
|
+
const completionOutcome = recordTurn(
|
|
43944
|
+
await driveTurn(
|
|
43945
|
+
deps,
|
|
43946
|
+
codex,
|
|
43947
|
+
projectSlug,
|
|
43948
|
+
runId,
|
|
43949
|
+
leaseId,
|
|
43950
|
+
threadId,
|
|
43951
|
+
completionPrompt,
|
|
43952
|
+
completionBudget,
|
|
43953
|
+
"completion",
|
|
43954
|
+
effortDecision?.level
|
|
43955
|
+
)
|
|
43256
43956
|
);
|
|
43257
43957
|
if (!completionOutcome.ok) {
|
|
43258
43958
|
await resetWedgedThread(deps, projectSlug, task.id, completionOutcome.interruptFailed);
|
|
@@ -43431,9 +44131,97 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
43431
44131
|
error51 instanceof Error ? error51.message : String(error51)
|
|
43432
44132
|
);
|
|
43433
44133
|
} finally {
|
|
44134
|
+
await deps.client.updateExecutionRun(projectSlug, runId, {
|
|
44135
|
+
promptBytes: runTelemetry.promptBytes,
|
|
44136
|
+
turnCount: runTelemetry.turnCount,
|
|
44137
|
+
inputTokens: runTelemetry.usage?.inputTokens ?? null,
|
|
44138
|
+
cachedInputTokens: runTelemetry.usage?.cachedInputTokens ?? null,
|
|
44139
|
+
outputTokens: runTelemetry.usage?.outputTokens ?? null,
|
|
44140
|
+
// Slice 7 (7.4): record the level actually used and, when it was an escalation, what caused
|
|
44141
|
+
// it — so "why did this run cost that much?" is answerable after the fact. NULL when the
|
|
44142
|
+
// slice is disabled, which per D6 reads as "not set" rather than a level we chose.
|
|
44143
|
+
effort: effortDecision?.level ?? null,
|
|
44144
|
+
effortEscalationReason: effortDecision?.escalationReason ?? null
|
|
44145
|
+
}).catch((error51) => {
|
|
44146
|
+
console.warn(
|
|
44147
|
+
JSON.stringify({
|
|
44148
|
+
event: "run_telemetry_write_failed",
|
|
44149
|
+
projectSlug,
|
|
44150
|
+
runId,
|
|
44151
|
+
detail: error51 instanceof Error ? error51.message : String(error51)
|
|
44152
|
+
})
|
|
44153
|
+
);
|
|
44154
|
+
});
|
|
44155
|
+
await writeServerOwnedTaskState(deps, projectSlug, task.id, runId, leaseId).catch((error51) => {
|
|
44156
|
+
console.warn(
|
|
44157
|
+
JSON.stringify({
|
|
44158
|
+
event: "task_state_server_write_failed",
|
|
44159
|
+
projectSlug,
|
|
44160
|
+
runId,
|
|
44161
|
+
taskId: task.id,
|
|
44162
|
+
detail: error51 instanceof Error ? error51.message : String(error51)
|
|
44163
|
+
})
|
|
44164
|
+
);
|
|
44165
|
+
});
|
|
43434
44166
|
disposeCodex();
|
|
43435
44167
|
}
|
|
43436
44168
|
}
|
|
44169
|
+
async function writeServerOwnedTaskState(deps, projectSlug, taskId, runId, leaseId) {
|
|
44170
|
+
const run3 = await deps.client.getExecutionRun(projectSlug, runId).catch(() => null);
|
|
44171
|
+
if (!run3) {
|
|
44172
|
+
console.warn(
|
|
44173
|
+
JSON.stringify({
|
|
44174
|
+
event: "task_state_server_write_skipped",
|
|
44175
|
+
projectSlug,
|
|
44176
|
+
runId,
|
|
44177
|
+
taskId,
|
|
44178
|
+
detail: "could not read the run's state_seq; skipped rather than writing a defaulted ordering value"
|
|
44179
|
+
})
|
|
44180
|
+
);
|
|
44181
|
+
return;
|
|
44182
|
+
}
|
|
44183
|
+
if (run3.leaseId !== null && run3.leaseId !== leaseId) {
|
|
44184
|
+
console.warn(
|
|
44185
|
+
JSON.stringify({
|
|
44186
|
+
event: "task_state_server_write_skipped",
|
|
44187
|
+
projectSlug,
|
|
44188
|
+
runId,
|
|
44189
|
+
taskId,
|
|
44190
|
+
detail: "the run is held by another lease (reclaimed); refusing to write state for a run we no longer own"
|
|
44191
|
+
})
|
|
44192
|
+
);
|
|
44193
|
+
return;
|
|
44194
|
+
}
|
|
44195
|
+
let artifacts = null;
|
|
44196
|
+
try {
|
|
44197
|
+
artifacts = await deps.client.listExecutionArtifacts(projectSlug, runId);
|
|
44198
|
+
} catch {
|
|
44199
|
+
artifacts = null;
|
|
44200
|
+
}
|
|
44201
|
+
let changed = null;
|
|
44202
|
+
if (artifacts !== null) {
|
|
44203
|
+
const summaries = artifacts.filter((artifact) => artifact.kind === "diff_summary").map((artifact) => JSON.stringify(artifact.payload));
|
|
44204
|
+
changed = summaries.length > 0 ? summaries.join("\n") : NO_FILE_CHANGES_THIS_RUN;
|
|
44205
|
+
}
|
|
44206
|
+
let latestVerification = null;
|
|
44207
|
+
if (artifacts !== null) {
|
|
44208
|
+
const verifications = artifacts.filter((artifact) => artifact.kind === "verification").map((artifact) => JSON.stringify(artifact.payload));
|
|
44209
|
+
latestVerification = verifications[verifications.length - 1] ?? NO_VERIFICATION_THIS_RUN;
|
|
44210
|
+
}
|
|
44211
|
+
await deps.client.upsertTaskState(projectSlug, taskId, {
|
|
44212
|
+
goal: null,
|
|
44213
|
+
acceptedRequirements: null,
|
|
44214
|
+
decisions: null,
|
|
44215
|
+
openProblems: null,
|
|
44216
|
+
nextActions: null,
|
|
44217
|
+
relevantFiles: null,
|
|
44218
|
+
filesChanged: changed,
|
|
44219
|
+
latestVerification,
|
|
44220
|
+
lastRunSeq: run3.stateSeq
|
|
44221
|
+
});
|
|
44222
|
+
}
|
|
44223
|
+
var NO_FILE_CHANGES_THIS_RUN = "no file changes recorded in this run";
|
|
44224
|
+
var NO_VERIFICATION_THIS_RUN = "not run in this run";
|
|
43437
44225
|
function buildDeliverableAuthorPrompt(task, specMarkdown) {
|
|
43438
44226
|
return [
|
|
43439
44227
|
`Produce the deliverable for the task below. Write the FULL deliverable content as markdown.`,
|
|
@@ -43963,7 +44751,8 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
43963
44751
|
break;
|
|
43964
44752
|
}
|
|
43965
44753
|
try {
|
|
43966
|
-
outcome = await deps.
|
|
44754
|
+
outcome = await deps.run(
|
|
44755
|
+
"async",
|
|
43967
44756
|
() => descriptor2.run(
|
|
43968
44757
|
{ client: deps.client, cwd: deps.cwd, codexExecutable: deps.codexExecutable },
|
|
43969
44758
|
input
|
|
@@ -44038,7 +44827,7 @@ async function runAsyncBackstopSweep(deps, drive = (slug, jobId) => driveAsyncJo
|
|
|
44038
44827
|
} catch {
|
|
44039
44828
|
queued = [];
|
|
44040
44829
|
}
|
|
44041
|
-
|
|
44830
|
+
await Promise.all(queued.map((job) => drive(slug, job.id)));
|
|
44042
44831
|
}
|
|
44043
44832
|
}
|
|
44044
44833
|
|
|
@@ -44506,13 +45295,16 @@ function createInactivityWatchdog(opts) {
|
|
|
44506
45295
|
const clearT = opts.clearTimeoutFn ?? clearTimeout;
|
|
44507
45296
|
const now = opts.now ?? Date.now;
|
|
44508
45297
|
const suspended = opts.suspendedMs ?? (() => 0);
|
|
45298
|
+
const excluded = opts.idleExcludedMs ?? (() => 0);
|
|
44509
45299
|
let idleTimer = null;
|
|
44510
45300
|
let maxTimer = null;
|
|
44511
45301
|
let done = false;
|
|
44512
45302
|
const startAt = now();
|
|
44513
45303
|
const suspendedAtStart = suspended();
|
|
45304
|
+
const excludedAtStart = excluded();
|
|
44514
45305
|
let lastPokeAt = startAt;
|
|
44515
45306
|
let suspendedAtLastPoke = suspendedAtStart;
|
|
45307
|
+
let excludedAtLastPoke = excludedAtStart;
|
|
44516
45308
|
const expire = (reason) => {
|
|
44517
45309
|
if (done) return;
|
|
44518
45310
|
done = true;
|
|
@@ -44530,7 +45322,7 @@ function createInactivityWatchdog(opts) {
|
|
|
44530
45322
|
};
|
|
44531
45323
|
const onIdleDeadline = () => {
|
|
44532
45324
|
if (done) return;
|
|
44533
|
-
const awakeSilence = now() - lastPokeAt - (suspended() - suspendedAtLastPoke);
|
|
45325
|
+
const awakeSilence = now() - lastPokeAt - (suspended() - suspendedAtLastPoke) - (excluded() - excludedAtLastPoke);
|
|
44534
45326
|
if (awakeSilence < opts.idleMs) {
|
|
44535
45327
|
armIdle(opts.idleMs - awakeSilence);
|
|
44536
45328
|
return;
|
|
@@ -44554,6 +45346,7 @@ function createInactivityWatchdog(opts) {
|
|
|
44554
45346
|
if (idleTimer) clearT(idleTimer);
|
|
44555
45347
|
lastPokeAt = now();
|
|
44556
45348
|
suspendedAtLastPoke = suspended();
|
|
45349
|
+
excludedAtLastPoke = excluded();
|
|
44557
45350
|
armIdle(opts.idleMs);
|
|
44558
45351
|
},
|
|
44559
45352
|
cancel: () => {
|
|
@@ -44563,8 +45356,8 @@ function createInactivityWatchdog(opts) {
|
|
|
44563
45356
|
}
|
|
44564
45357
|
};
|
|
44565
45358
|
}
|
|
44566
|
-
var DEFAULT_CHAT_TURN_IDLE_SECONDS =
|
|
44567
|
-
var DEFAULT_CHAT_TURN_MAX_SECONDS =
|
|
45359
|
+
var DEFAULT_CHAT_TURN_IDLE_SECONDS = 300;
|
|
45360
|
+
var DEFAULT_CHAT_TURN_MAX_SECONDS = 2700;
|
|
44568
45361
|
function readPositiveSeconds(name, fallback) {
|
|
44569
45362
|
const raw = (process.env[name] ?? "").trim();
|
|
44570
45363
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -44577,6 +45370,64 @@ function chatTurnMaxMs() {
|
|
|
44577
45370
|
return readPositiveSeconds("MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_MAX_SECONDS", DEFAULT_CHAT_TURN_MAX_SECONDS) * 1e3;
|
|
44578
45371
|
}
|
|
44579
45372
|
|
|
45373
|
+
// src/tool-busy.ts
|
|
45374
|
+
function createToolBusyClock(opts) {
|
|
45375
|
+
const now = opts?.now ?? Date.now;
|
|
45376
|
+
const open = /* @__PURE__ */ new Set();
|
|
45377
|
+
let total = 0;
|
|
45378
|
+
let segmentStart = null;
|
|
45379
|
+
function start(itemId) {
|
|
45380
|
+
if (!open.has(itemId)) {
|
|
45381
|
+
open.add(itemId);
|
|
45382
|
+
if (open.size === 1) {
|
|
45383
|
+
segmentStart = now();
|
|
45384
|
+
}
|
|
45385
|
+
}
|
|
45386
|
+
}
|
|
45387
|
+
function end(itemId) {
|
|
45388
|
+
if (open.has(itemId)) {
|
|
45389
|
+
open.delete(itemId);
|
|
45390
|
+
if (open.size === 0) {
|
|
45391
|
+
const nowVal = now();
|
|
45392
|
+
if (segmentStart !== null) {
|
|
45393
|
+
total += nowVal - segmentStart;
|
|
45394
|
+
segmentStart = null;
|
|
45395
|
+
}
|
|
45396
|
+
}
|
|
45397
|
+
}
|
|
45398
|
+
}
|
|
45399
|
+
function endAll() {
|
|
45400
|
+
if (open.size > 0) {
|
|
45401
|
+
const nowVal = now();
|
|
45402
|
+
if (segmentStart !== null) {
|
|
45403
|
+
total += nowVal - segmentStart;
|
|
45404
|
+
segmentStart = null;
|
|
45405
|
+
}
|
|
45406
|
+
open.clear();
|
|
45407
|
+
}
|
|
45408
|
+
}
|
|
45409
|
+
function ms() {
|
|
45410
|
+
const nowVal = now();
|
|
45411
|
+
if (open.size > 0 && segmentStart !== null) {
|
|
45412
|
+
return total + (nowVal - segmentStart);
|
|
45413
|
+
}
|
|
45414
|
+
return total;
|
|
45415
|
+
}
|
|
45416
|
+
return { start, end, endAll, ms };
|
|
45417
|
+
}
|
|
45418
|
+
var TOOL_ITEM_TYPES = /* @__PURE__ */ new Set(["commandExecution", "mcpToolCall", "fileChange"]);
|
|
45419
|
+
function applyToolBusyNotification(clock, method, params) {
|
|
45420
|
+
if (method !== "item/started" && method !== "item/completed") return;
|
|
45421
|
+
if (typeof params !== "object" || params === null) return;
|
|
45422
|
+
const p = params;
|
|
45423
|
+
const item = typeof p.item === "object" && p.item !== null ? p.item : void 0;
|
|
45424
|
+
if (typeof item?.type !== "string" || !TOOL_ITEM_TYPES.has(item.type)) return;
|
|
45425
|
+
const id = typeof item.id === "string" ? item.id : typeof p.itemId === "string" ? p.itemId : void 0;
|
|
45426
|
+
if (id === void 0) return;
|
|
45427
|
+
if (method === "item/started") clock.start(id);
|
|
45428
|
+
else clock.end(id);
|
|
45429
|
+
}
|
|
45430
|
+
|
|
44580
45431
|
// src/worker-token.ts
|
|
44581
45432
|
var import_node_crypto7 = require("node:crypto");
|
|
44582
45433
|
var import_node_fs9 = require("node:fs");
|
|
@@ -48581,11 +49432,16 @@ function parseVerdict2(text) {
|
|
|
48581
49432
|
let last = null;
|
|
48582
49433
|
while ((match = re.exec(text)) !== null) last = match;
|
|
48583
49434
|
if (!last) {
|
|
48584
|
-
return {
|
|
49435
|
+
return {
|
|
49436
|
+
verdict: "fail",
|
|
49437
|
+
summary: "no parseable VERDICT line in verification output",
|
|
49438
|
+
turnRef: null,
|
|
49439
|
+
fullOutput: text
|
|
49440
|
+
};
|
|
48585
49441
|
}
|
|
48586
49442
|
const verdict = (last[1] ?? "").toLowerCase() === "pass" ? "pass" : "fail";
|
|
48587
49443
|
const summary = (last[2] ?? "").trim() || `verification ${verdict}`;
|
|
48588
|
-
return { verdict, summary, turnRef: null };
|
|
49444
|
+
return { verdict, summary, turnRef: null, fullOutput: text };
|
|
48589
49445
|
}
|
|
48590
49446
|
function isBlockedVerificationHost(hostname3) {
|
|
48591
49447
|
const h = hostname3.toLowerCase().replace(/^\[|\]$/g, "");
|
|
@@ -49482,7 +50338,9 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
49482
50338
|
"- For public/third-party HTTP/HTML verification through `commandExecution`, prefer built-in",
|
|
49483
50339
|
" tooling such as `curl`, `python3` stdlib `urllib.request`, and `html.parser`; do not assume",
|
|
49484
50340
|
" third-party Python packages such as `requests` or `bs4` are installed. This permission covers",
|
|
49485
|
-
" third-party sites ONLY \u2014 never call the Mrrlin API from `commandExecution`.",
|
|
50341
|
+
" third-party sites ONLY \u2014 never call the Mrrlin API from `commandExecution`. A cheap fetch is",
|
|
50342
|
+
" the default, not the only option: see the browser-vs-fetch contract below for when a fetch is",
|
|
50343
|
+
" the wrong tool outright and when a failed fetch must be escalated to `mrrlin-browser`.",
|
|
49486
50344
|
"",
|
|
49487
50345
|
"Handoff contract \u2014 durable operator action MUST NOT live only in chat:",
|
|
49488
50346
|
"- If you conclude that a specific task needs human input, account access, manual evidence,",
|
|
@@ -49515,11 +50373,49 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
49515
50373
|
" if it did not (that state REQUIRES a linked Evidence wiki page); or, if the effect still",
|
|
49516
50374
|
" is not measurable, re-set `verificationDueAt` to a new date with a reason.",
|
|
49517
50375
|
"",
|
|
49518
|
-
"File artifacts
|
|
49519
|
-
"
|
|
49520
|
-
"
|
|
49521
|
-
"
|
|
49522
|
-
"it
|
|
50376
|
+
"File artifacts contract \u2014 decide the delivery medium BEFORE you compose the reply:",
|
|
50377
|
+
"- First ask what the operator will DO with this output. If they will scan it, compare across",
|
|
50378
|
+
" it, re-read it later, keep it, or share it, it belongs in a FILE. If they need to know one",
|
|
50379
|
+
" thing right now, chat alone is right. Judge by use, not by topic, domain, or data type.",
|
|
50380
|
+
"- When it is a file: write it (`.md` for linear prose, `.html` when structure or layout",
|
|
50381
|
+
" carries meaning), upload with `upload_artifact` (class=temp for one-off downloads,",
|
|
50382
|
+
" class=durable when a spec/evidence page references it; pass taskId when known), and paste",
|
|
50383
|
+
" the returned markdown link into your reply.",
|
|
50384
|
+
"- The chat reply must still stand alone as a complete answer even if the file is never opened:",
|
|
50385
|
+
" the headline finding, the few points that matter, then the link. Never reduce it to",
|
|
50386
|
+
' "see attached" or "details in the report".',
|
|
50387
|
+
"- For images, embed as an image (``) so the chat previews it inline.",
|
|
50388
|
+
"- Never inline file contents into chat. 5MB max per file.",
|
|
50389
|
+
"",
|
|
50390
|
+
"Browser-vs-fetch contract \u2014 pick whatever reaches ground truth in the fewest steps:",
|
|
50391
|
+
"- Reach for `mrrlin-browser` FIRST, without trying a fetch at all, when the target is a",
|
|
50392
|
+
" rendered UI, the question is inherently visual, the content sits behind a logged-in session,",
|
|
50393
|
+
" or reconstructing the answer would take many chained fetches.",
|
|
50394
|
+
"- A single cheap `curl`/`python3 urllib` fetch stays the right default for a plain one-shot",
|
|
50395
|
+
" HTTP/HTML check. Do not launch a browser for something one fetch already answers.",
|
|
50396
|
+
"- Escalate to `mrrlin-browser` in the SAME turn whenever a fetch cannot establish ground",
|
|
50397
|
+
" truth: 401/403, a bot/consent/CAPTCHA wall, an empty JS-only shell, or a login gate. A 429",
|
|
50398
|
+
" is rate limiting, not a wall \u2014 back off and retry the fetch once, and escalate only if it",
|
|
50399
|
+
" persists (bot detection does sometimes answer 429 to non-browser clients).",
|
|
50400
|
+
"- A failed fetch is a signal to change tool, never a finding. NEVER report that you could not",
|
|
50401
|
+
" check, verify, or reach a publicly reachable target until you have actually tried the",
|
|
50402
|
+
" browser on it in this turn.",
|
|
50403
|
+
"",
|
|
50404
|
+
"Initiative contract \u2014 do the obvious next step instead of offering it:",
|
|
50405
|
+
"- When a next step is cheap, clearly wanted, and finishable in this turn, just do it and",
|
|
50406
|
+
' report the result. Do not end a reply with "if you want, I can also ..." for work you',
|
|
50407
|
+
" could have finished in the time it took to offer.",
|
|
50408
|
+
"- This never overrides the turn-lifecycle rules above: still split a large batch UP FRONT and",
|
|
50409
|
+
" still end at a clean boundary with an explicit request to continue. Ask when the work is",
|
|
50410
|
+
" genuinely big, needs an operator decision, or would change scope \u2014 never to dodge cheap work.",
|
|
50411
|
+
"",
|
|
50412
|
+
"Context-handle contract \u2014 a `mrrlin://` handle is a pointer, not an answer:",
|
|
50413
|
+
"- When tool output carries a `mrrlin://` handle and your answer depends on detail the compact",
|
|
50414
|
+
" envelope does not contain, call `retrieve_context` on that handle BEFORE answering.",
|
|
50415
|
+
"- Before any irreversible action on a handled record \u2014 closing a task, dispatching a deploy,",
|
|
50416
|
+
" writing a spec \u2014 call `retrieve_context` first. Never act on a compact envelope alone.",
|
|
50417
|
+
"- Never guess the missing detail, and never report a field as absent when it is merely",
|
|
50418
|
+
" un-rehydrated. Do not echo a raw handle to the operator as if it were data.",
|
|
49523
50419
|
"",
|
|
49524
50420
|
"Clarifying-question contract \u2014 ask early, never lose the question:",
|
|
49525
50421
|
"- During a live chat turn, when a SPECIFIC task is ambiguous and the ambiguity affects",
|
|
@@ -50685,7 +51581,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
50685
51581
|
}
|
|
50686
51582
|
};
|
|
50687
51583
|
try {
|
|
50688
|
-
await deps.
|
|
51584
|
+
await deps.run("chat", async () => {
|
|
50689
51585
|
if (currentReplayBuf.state === "interrupted" || currentReplayBuf.state === "completed") {
|
|
50690
51586
|
logCtx?.logger.logOut({
|
|
50691
51587
|
spanId,
|
|
@@ -50714,7 +51610,9 @@ function createBridgeMessageHandler(deps) {
|
|
|
50714
51610
|
outputEvents += 1;
|
|
50715
51611
|
sendInTurn({ type: "event", directorSessionId, event });
|
|
50716
51612
|
};
|
|
51613
|
+
const toolBusy = createToolBusyClock();
|
|
50717
51614
|
const unsub = codexClient.onNotification((method, params) => {
|
|
51615
|
+
applyToolBusyNotification(toolBusy, method, params);
|
|
50718
51616
|
pokeActivity();
|
|
50719
51617
|
if (method === "account/rateLimits/updated") {
|
|
50720
51618
|
const snapshot = params?.rateLimits ?? null;
|
|
@@ -50742,6 +51640,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
50742
51640
|
idleMs,
|
|
50743
51641
|
maxMs,
|
|
50744
51642
|
...deps.suspendedMs ? { suspendedMs: deps.suspendedMs } : {},
|
|
51643
|
+
idleExcludedMs: () => toolBusy.ms(),
|
|
50745
51644
|
onExpire: (reason) => {
|
|
50746
51645
|
rejectWatchdog?.(
|
|
50747
51646
|
new Error(
|
|
@@ -50845,6 +51744,7 @@ ${effectiveText}` : effectiveText;
|
|
|
50845
51744
|
}
|
|
50846
51745
|
watchdog.cancel();
|
|
50847
51746
|
unsub();
|
|
51747
|
+
toolBusy.endAll();
|
|
50848
51748
|
thread.cancel = null;
|
|
50849
51749
|
}
|
|
50850
51750
|
});
|
|
@@ -50964,8 +51864,8 @@ async function startDirectorBridge() {
|
|
|
50964
51864
|
const turnLock = createTurnLock();
|
|
50965
51865
|
let lastTurnEndedAt = Date.now();
|
|
50966
51866
|
const wrappedTurnLock = {
|
|
50967
|
-
|
|
50968
|
-
return turnLock.
|
|
51867
|
+
run(lane, fn) {
|
|
51868
|
+
return turnLock.run(lane, async () => {
|
|
50969
51869
|
try {
|
|
50970
51870
|
return await fn();
|
|
50971
51871
|
} finally {
|
|
@@ -51008,7 +51908,11 @@ async function startDirectorBridge() {
|
|
|
51008
51908
|
}
|
|
51009
51909
|
});
|
|
51010
51910
|
const codexProbeHandle = startCodexProbe();
|
|
51011
|
-
const consumerState = {
|
|
51911
|
+
const consumerState = {
|
|
51912
|
+
inFlightRunIds: /* @__PURE__ */ new Set(),
|
|
51913
|
+
lastClaimedRunId: null,
|
|
51914
|
+
lastClaimAt: null
|
|
51915
|
+
};
|
|
51012
51916
|
const health = createServiceHealth();
|
|
51013
51917
|
try {
|
|
51014
51918
|
migrateRetiredApiBaseUrls();
|
|
@@ -51087,7 +51991,7 @@ async function startDirectorBridge() {
|
|
|
51087
51991
|
// handle returned by acquireExecutorCodex (Fix A + G), so there is no shared
|
|
51088
51992
|
// mutable `activeExecutorClient` slot to race a concurrent chat/bootstrap turn.
|
|
51089
51993
|
releaseCodex: () => codexKeepalive.release(),
|
|
51090
|
-
|
|
51994
|
+
run: wrappedTurnLock.run,
|
|
51091
51995
|
staleSeconds,
|
|
51092
51996
|
touchIntervalMs: Math.min(DEFAULT_TOUCH_INTERVAL_MS, Math.floor(staleSeconds * 1e3 / 2)),
|
|
51093
51997
|
codexCwd: config2.codexCwd,
|
|
@@ -51114,7 +52018,7 @@ async function startDirectorBridge() {
|
|
|
51114
52018
|
// ADR 0007 self-review: critique the committed diff on the executor's own client before
|
|
51115
52019
|
// push/PR. Reuses the verification turn-runner (acquireThread + collect assistant text).
|
|
51116
52020
|
selfReviewTurn: createSelfReviewTurn({
|
|
51117
|
-
runTurn: (codexClient, cwd, prompt) => runCollectedCodexTurn(codexClient, prompt, cwd, SELF_REVIEW_BASE_INSTRUCTIONS)
|
|
52021
|
+
runTurn: (codexClient, cwd, prompt) => wrappedTurnLock.run("chat", () => runCollectedCodexTurn(codexClient, prompt, cwd, SELF_REVIEW_BASE_INSTRUCTIONS))
|
|
51118
52022
|
}),
|
|
51119
52023
|
// ADR 0015 deploy gate: after a pushed run reaches dev_deploy/prod_deploy, the backstop sweep
|
|
51120
52024
|
// dispatches the deploy + polls + runs a verification turn against the deployed env URL in a
|
|
@@ -51153,7 +52057,7 @@ async function startDirectorBridge() {
|
|
|
51153
52057
|
}
|
|
51154
52058
|
};
|
|
51155
52059
|
},
|
|
51156
|
-
runTurn: (codexClient, prompt, cwd) => runCollectedCodexTurn(codexClient, prompt, cwd, VERIFICATION_BASE_INSTRUCTIONS)
|
|
52060
|
+
runTurn: (codexClient, prompt, cwd) => wrappedTurnLock.run("chat", () => runCollectedCodexTurn(codexClient, prompt, cwd, VERIFICATION_BASE_INSTRUCTIONS))
|
|
51157
52061
|
}),
|
|
51158
52062
|
budgetSeconds: deployPollBudgetSeconds()
|
|
51159
52063
|
},
|
|
@@ -51178,7 +52082,7 @@ async function startDirectorBridge() {
|
|
|
51178
52082
|
const bridgeInstanceId = (0, import_node_crypto8.randomUUID)();
|
|
51179
52083
|
const asyncConsumerDeps = {
|
|
51180
52084
|
client,
|
|
51181
|
-
|
|
52085
|
+
run: wrappedTurnLock.run,
|
|
51182
52086
|
cwd: config2.codexCwd ?? process.cwd(),
|
|
51183
52087
|
codexExecutable: config2.codexExecutable,
|
|
51184
52088
|
staleSeconds,
|
|
@@ -51194,40 +52098,34 @@ async function startDirectorBridge() {
|
|
|
51194
52098
|
sink({ type: "async-result", directorSessionId, projectSlug: slug, deliveryKind, body });
|
|
51195
52099
|
}
|
|
51196
52100
|
};
|
|
51197
|
-
|
|
51198
|
-
|
|
51199
|
-
|
|
51200
|
-
};
|
|
51201
|
-
const driveTracked = (slug, runId) => enqueueRun(async () => {
|
|
51202
|
-
consumerState.inFlightRunId = runId;
|
|
52101
|
+
const driveTracked = (slug, runId) => {
|
|
52102
|
+
consumerState.inFlightRunIds.add(runId);
|
|
52103
|
+
consumerState.lastClaimedRunId = runId;
|
|
51203
52104
|
consumerState.lastClaimAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
51204
|
-
|
|
51205
|
-
await driveRun(consumerDeps, slug, runId);
|
|
51206
|
-
} catch (error51) {
|
|
52105
|
+
void driveRun(consumerDeps, slug, runId).catch((error51) => {
|
|
51207
52106
|
process.stderr.write(`[mrrlin-mcp director-bridge] drive failed for ${slug}/${runId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
51208
52107
|
`);
|
|
51209
|
-
}
|
|
51210
|
-
consumerState.
|
|
51211
|
-
}
|
|
51212
|
-
}
|
|
52108
|
+
}).finally(() => {
|
|
52109
|
+
consumerState.inFlightRunIds.delete(runId);
|
|
52110
|
+
});
|
|
52111
|
+
};
|
|
51213
52112
|
const driveRunNow = (slug, runId) => {
|
|
51214
|
-
consumerState.
|
|
52113
|
+
consumerState.inFlightRunIds.add(runId);
|
|
52114
|
+
consumerState.lastClaimedRunId = runId;
|
|
51215
52115
|
consumerState.lastClaimAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
51216
52116
|
void driveRun(consumerDeps, slug, runId).catch((error51) => {
|
|
51217
52117
|
process.stderr.write(`[mrrlin-mcp director-bridge] drive failed for ${slug}/${runId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
51218
52118
|
`);
|
|
51219
52119
|
}).finally(() => {
|
|
51220
|
-
consumerState.
|
|
52120
|
+
consumerState.inFlightRunIds.delete(runId);
|
|
51221
52121
|
});
|
|
51222
52122
|
};
|
|
51223
|
-
const driveAsyncTracked = (slug, jobId) =>
|
|
51224
|
-
|
|
51225
|
-
await driveAsyncJob(asyncConsumerDeps, slug, jobId);
|
|
51226
|
-
} catch (error51) {
|
|
52123
|
+
const driveAsyncTracked = (slug, jobId) => {
|
|
52124
|
+
void driveAsyncJob(asyncConsumerDeps, slug, jobId).catch((error51) => {
|
|
51227
52125
|
process.stderr.write(`[mrrlin-mcp director-bridge] async drive failed for ${slug}/${jobId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
51228
52126
|
`);
|
|
51229
|
-
}
|
|
51230
|
-
}
|
|
52127
|
+
});
|
|
52128
|
+
};
|
|
51231
52129
|
const driveAsyncNow = (slug, jobId) => {
|
|
51232
52130
|
void driveAsyncJob(asyncConsumerDeps, slug, jobId).catch((error51) => {
|
|
51233
52131
|
process.stderr.write(`[mrrlin-mcp director-bridge] async drive failed for ${slug}/${jobId}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
@@ -51240,7 +52138,15 @@ async function startDirectorBridge() {
|
|
|
51240
52138
|
port: config2.port,
|
|
51241
52139
|
token: config2.token,
|
|
51242
52140
|
allowedOrigins: config2.allowedOrigins,
|
|
51243
|
-
getHealth: () => ({
|
|
52141
|
+
getHealth: () => ({
|
|
52142
|
+
ok: true,
|
|
52143
|
+
// Most recently claimed run, or null once nothing is in flight — kept for existing consumers.
|
|
52144
|
+
inFlightRunId: consumerState.inFlightRunIds.size > 0 ? consumerState.lastClaimedRunId : null,
|
|
52145
|
+
inFlightRunIds: Array.from(consumerState.inFlightRunIds),
|
|
52146
|
+
inFlightCount: consumerState.inFlightRunIds.size,
|
|
52147
|
+
lastClaimAt: consumerState.lastClaimAt,
|
|
52148
|
+
service: health.snapshot()
|
|
52149
|
+
})
|
|
51244
52150
|
})
|
|
51245
52151
|
);
|
|
51246
52152
|
const threads = /* @__PURE__ */ new Map();
|
|
@@ -51303,7 +52209,7 @@ async function startDirectorBridge() {
|
|
|
51303
52209
|
const sharedHandlerDeps = {
|
|
51304
52210
|
acquireCodex: () => codexKeepalive.acquire(),
|
|
51305
52211
|
releaseCodex: () => codexKeepalive.release(),
|
|
51306
|
-
|
|
52212
|
+
run: wrappedTurnLock.run,
|
|
51307
52213
|
suspendedMs: () => suspensionDetector.suspendedMs(),
|
|
51308
52214
|
buildStartParams: (msg, desiredModel) => buildThreadStartParams(msg, config2, desiredModel),
|
|
51309
52215
|
turnInactivity: () => ({ idleMs: chatTurnIdleMs(), maxMs: chatTurnMaxMs() }),
|
|
@@ -51449,7 +52355,7 @@ async function startDirectorBridge() {
|
|
|
51449
52355
|
await bridgeHealth.recordSuccess();
|
|
51450
52356
|
} catch (error51) {
|
|
51451
52357
|
process.stderr.write(
|
|
51452
|
-
`[mrrlin-mcp director-bridge] ${label} failed: ${
|
|
52358
|
+
`[mrrlin-mcp director-bridge] ${label} failed: ${describeError(error51)}
|
|
51453
52359
|
`
|
|
51454
52360
|
);
|
|
51455
52361
|
bridgeHealth.recordFailure(id, error51);
|
|
@@ -51461,34 +52367,28 @@ async function startDirectorBridge() {
|
|
|
51461
52367
|
const scheduleClaimLoop = () => {
|
|
51462
52368
|
if (claimLoopStopped) return;
|
|
51463
52369
|
claimTimer = setTimeout(() => {
|
|
51464
|
-
|
|
51465
|
-
|
|
51466
|
-
|
|
51467
|
-
|
|
51468
|
-
(
|
|
51469
|
-
|
|
51470
|
-
|
|
51471
|
-
})
|
|
51472
|
-
)
|
|
51473
|
-
);
|
|
51474
|
-
enqueueRun(
|
|
51475
|
-
() => runTrackedSweep(
|
|
51476
|
-
"asyncSweep",
|
|
51477
|
-
"async sweep",
|
|
51478
|
-
() => runAsyncBackstopSweep(asyncConsumerDeps, (slug, jobId) => {
|
|
51479
|
-
driveAsyncTracked(slug, jobId);
|
|
51480
|
-
return Promise.resolve();
|
|
51481
|
-
})
|
|
51482
|
-
)
|
|
52370
|
+
void runTrackedSweep(
|
|
52371
|
+
"claimSweep",
|
|
52372
|
+
"claim sweep",
|
|
52373
|
+
() => runBackstopSweep(consumerDeps, (slug, runId) => {
|
|
52374
|
+
driveTracked(slug, runId);
|
|
52375
|
+
return Promise.resolve();
|
|
52376
|
+
})
|
|
51483
52377
|
);
|
|
51484
|
-
|
|
51485
|
-
|
|
51486
|
-
|
|
51487
|
-
|
|
51488
|
-
|
|
52378
|
+
void runTrackedSweep(
|
|
52379
|
+
"asyncSweep",
|
|
52380
|
+
"async sweep",
|
|
52381
|
+
() => runAsyncBackstopSweep(asyncConsumerDeps, (slug, jobId) => {
|
|
52382
|
+
driveAsyncTracked(slug, jobId);
|
|
52383
|
+
return Promise.resolve();
|
|
51489
52384
|
})
|
|
51490
52385
|
);
|
|
51491
|
-
|
|
52386
|
+
void runOrphanWorktreeSweep(client, {
|
|
52387
|
+
checkoutRegistry,
|
|
52388
|
+
worktreeRoot: import_node_path18.default.join(config2.stateDir, "worktrees"),
|
|
52389
|
+
locksDir: import_node_path18.default.join(config2.stateDir, "locks")
|
|
52390
|
+
});
|
|
52391
|
+
{
|
|
51492
52392
|
const { retainedBuffers, retainedBytes, tombstonesRemoved } = sweepReplayBuffers(threads, tombstones, {
|
|
51493
52393
|
now: Date.now(),
|
|
51494
52394
|
suspendedMs: () => suspensionDetector.suspendedMs(),
|
|
@@ -51526,7 +52426,7 @@ async function startDirectorBridge() {
|
|
|
51526
52426
|
payload: { removed: tombstonesRemoved }
|
|
51527
52427
|
});
|
|
51528
52428
|
}
|
|
51529
|
-
}
|
|
52429
|
+
}
|
|
51530
52430
|
scheduleClaimLoop();
|
|
51531
52431
|
}, claimIntervalMs());
|
|
51532
52432
|
claimTimer.unref?.();
|
|
@@ -51564,7 +52464,7 @@ async function startDirectorBridge() {
|
|
|
51564
52464
|
agentPoolLoopHandle = startAgentPoolLoop({
|
|
51565
52465
|
push: (snapshot) => client.putOperatorAgentPool(snapshot),
|
|
51566
52466
|
onError: (err) => process.stderr.write(
|
|
51567
|
-
`[mrrlin-mcp director-bridge] agent-pool snapshot push failed: ${
|
|
52467
|
+
`[mrrlin-mcp director-bridge] agent-pool snapshot push failed: ${describeError(err)}
|
|
51568
52468
|
`
|
|
51569
52469
|
)
|
|
51570
52470
|
});
|
|
@@ -56500,31 +57400,12 @@ var mcpToolNames = {
|
|
|
56500
57400
|
restartExecutionRun: "restart_execution_run",
|
|
56501
57401
|
resolveHandoff: "resolve_handoff",
|
|
56502
57402
|
recordProducedUrl: "record_produced_url",
|
|
57403
|
+
updateTaskState: "update_task_state",
|
|
56503
57404
|
registerLocalCheckout: "register_local_checkout",
|
|
56504
57405
|
/** Compact-context retrieval: rehydrate a mrrlin:// handle into the canonical full record. */
|
|
56505
57406
|
retrieveContext: "retrieve_context"
|
|
56506
57407
|
};
|
|
56507
57408
|
|
|
56508
|
-
// src/context-compact/policy.ts
|
|
56509
|
-
var DEFAULT_THRESHOLD_CHARS = 12e3;
|
|
56510
|
-
var DEFAULT_MAX_ITEMS = 25;
|
|
56511
|
-
function readPositiveInt(value, fallback) {
|
|
56512
|
-
const raw = (value ?? "").trim();
|
|
56513
|
-
if (!raw) return fallback;
|
|
56514
|
-
const parsed = Number.parseInt(raw, 10);
|
|
56515
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
56516
|
-
}
|
|
56517
|
-
function readCompactPolicy(env = process.env) {
|
|
56518
|
-
const disabled = (env.MRRLIN_MCP_COMPACT_DISABLE ?? "").trim() === "1";
|
|
56519
|
-
return {
|
|
56520
|
-
enabled: !disabled,
|
|
56521
|
-
thresholdChars: readPositiveInt(env.MRRLIN_MCP_COMPACT_THRESHOLD_CHARS, DEFAULT_THRESHOLD_CHARS),
|
|
56522
|
-
maxItems: readPositiveInt(env.MRRLIN_MCP_COMPACT_MAX_ITEMS, DEFAULT_MAX_ITEMS),
|
|
56523
|
-
debug: (env.MRRLIN_MCP_COMPACT_DEBUG ?? "").trim() === "1",
|
|
56524
|
-
retrievalBudgetDisable: (env.MRRLIN_RETRIEVAL_BUDGET_DISABLE ?? "").trim() === "1"
|
|
56525
|
-
};
|
|
56526
|
-
}
|
|
56527
|
-
|
|
56528
57409
|
// src/context-compact/handles.ts
|
|
56529
57410
|
var MRRLIN_HANDLE_SCHEME = "mrrlin://";
|
|
56530
57411
|
var SLUG_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
@@ -56599,7 +57480,7 @@ function parseMrrlinHandle(value) {
|
|
|
56599
57480
|
var NOTES_EXCERPT_CHARS = 200;
|
|
56600
57481
|
var TITLE_TRUNCATE_CHARS = 160;
|
|
56601
57482
|
var EXCERPT_TRUNCATE_CHARS = 240;
|
|
56602
|
-
function
|
|
57483
|
+
function truncate2(value, maxChars) {
|
|
56603
57484
|
if (value.length <= maxChars) return value;
|
|
56604
57485
|
return `${value.slice(0, Math.max(0, maxChars - 1))}\u2026`;
|
|
56605
57486
|
}
|
|
@@ -56615,7 +57496,7 @@ function compactTaskList(projectSlug, tasks, maxItems) {
|
|
|
56615
57496
|
const slice = tasks.slice(0, included);
|
|
56616
57497
|
const items = slice.map((t) => ({
|
|
56617
57498
|
id: t.id,
|
|
56618
|
-
title:
|
|
57499
|
+
title: truncate2(t.title, TITLE_TRUNCATE_CHARS),
|
|
56619
57500
|
status: t.status,
|
|
56620
57501
|
type: t.type,
|
|
56621
57502
|
category: t.category,
|
|
@@ -56626,8 +57507,8 @@ function compactTaskList(projectSlug, tasks, maxItems) {
|
|
|
56626
57507
|
assignee: t.assignee,
|
|
56627
57508
|
planId: t.planId,
|
|
56628
57509
|
specWikiPageId: t.specWikiPageId,
|
|
56629
|
-
notesExcerpt: t.notes ?
|
|
56630
|
-
latestJudgementExcerpt: t.latestJudgement ?
|
|
57510
|
+
notesExcerpt: t.notes ? truncate2(t.notes, NOTES_EXCERPT_CHARS) : null,
|
|
57511
|
+
latestJudgementExcerpt: t.latestJudgement ? truncate2(t.latestJudgement, NOTES_EXCERPT_CHARS) : null,
|
|
56631
57512
|
handle: formatTaskHandle(projectSlug, t.id)
|
|
56632
57513
|
}));
|
|
56633
57514
|
const handles = slice.map((t) => ({
|
|
@@ -56648,7 +57529,7 @@ function compactWikiPageList(projectSlug, pages, maxItems) {
|
|
|
56648
57529
|
const slice = pages.slice(0, included);
|
|
56649
57530
|
const items = slice.map((p) => ({
|
|
56650
57531
|
id: p.id,
|
|
56651
|
-
title:
|
|
57532
|
+
title: truncate2(p.title, TITLE_TRUNCATE_CHARS),
|
|
56652
57533
|
folder: p.folder,
|
|
56653
57534
|
path: p.path,
|
|
56654
57535
|
updatedAt: p.updatedAt,
|
|
@@ -56675,7 +57556,7 @@ function compactWikiSearch(projectSlug, results, maxItems) {
|
|
|
56675
57556
|
const slice = results.slice(0, included);
|
|
56676
57557
|
const items = slice.map((r) => ({
|
|
56677
57558
|
pageId: r.id,
|
|
56678
|
-
title:
|
|
57559
|
+
title: truncate2(r.title, TITLE_TRUNCATE_CHARS),
|
|
56679
57560
|
folder: r.folder,
|
|
56680
57561
|
path: r.path,
|
|
56681
57562
|
rank: r.rank,
|
|
@@ -56683,7 +57564,7 @@ function compactWikiSearch(projectSlug, results, maxItems) {
|
|
|
56683
57564
|
ageDays: r.ageDays,
|
|
56684
57565
|
memoryClass: r.memoryClass,
|
|
56685
57566
|
precedence: r.precedence,
|
|
56686
|
-
snippet:
|
|
57567
|
+
snippet: truncate2(r.snippet, EXCERPT_TRUNCATE_CHARS),
|
|
56687
57568
|
handle: formatWikiHandle(projectSlug, r.id)
|
|
56688
57569
|
}));
|
|
56689
57570
|
const handles = slice.map((r) => ({
|
|
@@ -56712,7 +57593,7 @@ function compactProjectSnapshot(projectSlug, snapshot, maxItems) {
|
|
|
56712
57593
|
}
|
|
56713
57594
|
const compactPlans = snapshot.plans.map((p) => ({
|
|
56714
57595
|
id: p.id,
|
|
56715
|
-
name:
|
|
57596
|
+
name: truncate2(p.name, TITLE_TRUNCATE_CHARS),
|
|
56716
57597
|
status: p.status,
|
|
56717
57598
|
taskCount: p.taskCount,
|
|
56718
57599
|
closedTaskCount: p.closedTaskCount,
|
|
@@ -56784,7 +57665,7 @@ function compactArtifactPayload(artifact) {
|
|
|
56784
57665
|
cwd,
|
|
56785
57666
|
exitCode,
|
|
56786
57667
|
firstError,
|
|
56787
|
-
tail:
|
|
57668
|
+
tail: truncate2(tail2, ARTIFACT_PAYLOAD_BUDGET),
|
|
56788
57669
|
totalLines: lines2.length
|
|
56789
57670
|
};
|
|
56790
57671
|
}
|
|
@@ -56792,7 +57673,7 @@ function compactArtifactPayload(artifact) {
|
|
|
56792
57673
|
return {
|
|
56793
57674
|
status: payload.status ?? null,
|
|
56794
57675
|
command: payload.command ?? null,
|
|
56795
|
-
summary: typeof payload.summary === "string" ?
|
|
57676
|
+
summary: typeof payload.summary === "string" ? truncate2(payload.summary, ARTIFACT_PAYLOAD_BUDGET) : null,
|
|
56796
57677
|
failed: Array.isArray(payload.failedTests) ? payload.failedTests.slice(0, 25) : null
|
|
56797
57678
|
};
|
|
56798
57679
|
}
|
|
@@ -56810,7 +57691,7 @@ function compactArtifactPayload(artifact) {
|
|
|
56810
57691
|
case "checkpoint":
|
|
56811
57692
|
default: {
|
|
56812
57693
|
const flattened = JSON.stringify(payload);
|
|
56813
|
-
return { excerpt:
|
|
57694
|
+
return { excerpt: truncate2(flattened, ARTIFACT_PAYLOAD_BUDGET) };
|
|
56814
57695
|
}
|
|
56815
57696
|
}
|
|
56816
57697
|
}
|
|
@@ -56849,7 +57730,7 @@ function compactTaskEvents(projectSlug, taskId, events, maxItems) {
|
|
|
56849
57730
|
createdAt: e.createdAt,
|
|
56850
57731
|
autonomyLevel: e.autonomyLevel,
|
|
56851
57732
|
autonomyReason: e.autonomyReason,
|
|
56852
|
-
bodyExcerpt: e.body ?
|
|
57733
|
+
bodyExcerpt: e.body ? truncate2(e.body, NOTES_EXCERPT_CHARS) : null
|
|
56853
57734
|
}));
|
|
56854
57735
|
const handles = [
|
|
56855
57736
|
{
|
|
@@ -56871,7 +57752,7 @@ function compactGeneric(value, maxItems, projectSlug) {
|
|
|
56871
57752
|
const included = Math.min(total, maxItems);
|
|
56872
57753
|
const slice = value.slice(0, included).map((entry) => {
|
|
56873
57754
|
const text2 = typeof entry === "string" ? entry : JSON.stringify(entry);
|
|
56874
|
-
return
|
|
57755
|
+
return truncate2(text2, ARTIFACT_PAYLOAD_BUDGET);
|
|
56875
57756
|
});
|
|
56876
57757
|
return {
|
|
56877
57758
|
summary: `Compact array view: ${total} entries, ${included} included.`,
|
|
@@ -56902,7 +57783,7 @@ function compactGeneric(value, maxItems, projectSlug) {
|
|
|
56902
57783
|
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
56903
57784
|
return {
|
|
56904
57785
|
summary: "Compact scalar view.",
|
|
56905
|
-
items: [
|
|
57786
|
+
items: [truncate2(text, ARTIFACT_PAYLOAD_BUDGET)],
|
|
56906
57787
|
handles: [],
|
|
56907
57788
|
counts: { total: 1, included: 1, omitted: 0 }
|
|
56908
57789
|
};
|
|
@@ -57072,6 +57953,7 @@ var updateExecutionRunInputSchema = runScopedSchema.merge(mrrlinExecutionRunUpda
|
|
|
57072
57953
|
var listExecutionArtifactsInputSchema = runScopedSchema;
|
|
57073
57954
|
var appendExecutionArtifactInputSchema = runScopedSchema.merge(mrrlinExecutionArtifactCreateSchema);
|
|
57074
57955
|
var recordProducedUrlInputSchema = runScopedSchema.merge(mrrlinProducedUrlPayloadSchema);
|
|
57956
|
+
var updateTaskStateInputSchema = runScopedSchema.merge(mrrlinTaskStateReportSchema);
|
|
57075
57957
|
var getProjectSchedulerPolicyInputSchema = projectScopedSchema;
|
|
57076
57958
|
var upsertProjectSchedulerPolicyInputSchema = projectScopedSchema.merge(mrrlinProjectSchedulerPolicyUpsertSchema);
|
|
57077
57959
|
var recordDeployWorkflowInputSchema = projectScopedSchema.extend({
|
|
@@ -57184,6 +58066,7 @@ var mcpToolInputSchemas = {
|
|
|
57184
58066
|
[mcpToolNames.restartExecutionRun]: restartExecutionRunInputSchema,
|
|
57185
58067
|
[mcpToolNames.resolveHandoff]: resolveHandoffInputSchema,
|
|
57186
58068
|
[mcpToolNames.recordProducedUrl]: recordProducedUrlInputSchema,
|
|
58069
|
+
[mcpToolNames.updateTaskState]: updateTaskStateInputSchema,
|
|
57187
58070
|
[mcpToolNames.registerLocalCheckout]: registerLocalCheckoutInputSchema,
|
|
57188
58071
|
[mcpToolNames.retrieveContext]: retrieveContextInputSchema
|
|
57189
58072
|
};
|
|
@@ -57235,6 +58118,7 @@ var mcpToolOperationIds = {
|
|
|
57235
58118
|
[mcpToolNames.recordDeployWorkflow]: openApiOperationIds.upsertProjectDeployWorkflow,
|
|
57236
58119
|
[mcpToolNames.updatePlan]: openApiOperationIds.updatePlan,
|
|
57237
58120
|
[mcpToolNames.updateTask]: openApiOperationIds.updateTask,
|
|
58121
|
+
[mcpToolNames.updateTaskState]: openApiOperationIds.reportTaskState,
|
|
57238
58122
|
[mcpToolNames.resetDirectorThread]: openApiOperationIds.resetDirectorThread,
|
|
57239
58123
|
[mcpToolNames.archiveTask]: openApiOperationIds.archiveTask,
|
|
57240
58124
|
[mcpToolNames.unarchiveTask]: openApiOperationIds.unarchiveTask,
|
|
@@ -57295,6 +58179,7 @@ var mcpToolDescriptions = {
|
|
|
57295
58179
|
[mcpToolNames.runCodeReviewerGate]: `Review the current branch's code diff (vs baseRef) with a Code Reviewer gate \u2014 independent reviewers iterate review\u2192fix\u2192re-check (checkpoint, scope-limited reviser edits, typecheck/build fast-gate, rollback) until the diff converges or is blocked. Run before opening a PR. Pass extraHeads: ["security-analyst"] for sensitive diffs (auth, secrets, input handling, new endpoints).`,
|
|
57296
58180
|
[mcpToolNames.appendExecutionArtifact]: "Append a durable artifact (plan, checkpoint, command output, test result, diff summary, self-review, error, deploy attempt, or handoff judgement) to an execution run.",
|
|
57297
58181
|
[mcpToolNames.recordProducedUrl]: 'Call this after publishing each web page produced by the task, so the UI link opens the page itself instead of the deployed site root. Pass the page\'s full https URL (for external pages, e.g. "https://dev.to/<slug>") OR a path starting with "/" for the deploy environment (e.g. "/blog/<slug>") \u2014 paths are resolved against the run\'s deployed env URL at render time. Optional `label` (\u226440 chars) is shown next to the link; omit and the UI defaults to the page title. Call once per page; multiple calls dedupe by resolved URL in the UI.',
|
|
58182
|
+
[mcpToolNames.updateTaskState]: "Record where this task actually stands, in your own words, BEFORE you end your turn. This is the handoff the next run reads: it survives even when this conversation does not, because a thread is retired once it has accumulated too many runs and the next run then starts from this record plus the repository. Report what only you know \u2014 the goal as you now understand it, decisions you made AND WHY (a decision without its reason is the thing most often lost), problems still open, and the next concrete actions. Omit any field you learned nothing new about; an omitted field keeps its previous value rather than being cleared. Use one entry per line for decisions/next actions. Do not restate the spec or the file list \u2014 the next run gets those anyway.",
|
|
57298
58183
|
[mcpToolNames.createExecutionRun]: 'Create a durable Director execution run for a task. Persists status, branch/SHA, workflow ids, and checkpoint cursor outside the chat transcript. For tasks that require a browser (login flows, UI verification, scraping), set prerequisites:[{mcp:"mrrlin-browser",reason:"<why a browser is needed>"}] so any resulting handoff offers a one-click "Launch browser & resume". When actually driving the browser inside the run, invoke tools from the `mrrlin-browser` MCP server ONLY (browser_navigate/browser_click/browser_snapshot/browser_type/etc.) \u2014 never from `playwright`, `chrome-devtools`, `puppeteer`, or any other browser MCP that may also be connected on the operator\'s box. Only `mrrlin-browser` uses the dedicated Chromium + persistent operator profile.',
|
|
57299
58184
|
[mcpToolNames.getExecutionRun]: "Fetch a single execution run by id.",
|
|
57300
58185
|
[mcpToolNames.listExecutionArtifacts]: "List artifacts captured during an execution run, in chronological order.",
|
|
@@ -57892,6 +58777,13 @@ function createMrrlinTools(options) {
|
|
|
57892
58777
|
"Unable to append execution artifact.",
|
|
57893
58778
|
async (c, { projectSlug, runId, ...input }) => await c.appendExecutionArtifact(projectSlug, runId, input)
|
|
57894
58779
|
),
|
|
58780
|
+
[mcpToolNames.updateTaskState]: makeTool(
|
|
58781
|
+
client,
|
|
58782
|
+
mcpToolNames.updateTaskState,
|
|
58783
|
+
"TASK_STATE_REPORT_FAILED",
|
|
58784
|
+
"Unable to record task state.",
|
|
58785
|
+
async (c, { projectSlug, runId, ...report }) => await c.reportTaskState(projectSlug, runId, report)
|
|
58786
|
+
),
|
|
57895
58787
|
[mcpToolNames.recordProducedUrl]: makeTool(
|
|
57896
58788
|
client,
|
|
57897
58789
|
mcpToolNames.recordProducedUrl,
|
|
@@ -58553,11 +59445,13 @@ Usage:
|
|
|
58553
59445
|
- MRRLIN_WORKER_PORT (dispatch listener, default 8792)
|
|
58554
59446
|
- MRRLIN_DIRECTOR_BRIDGE_CLAIM_INTERVAL_SECONDS (default 30)
|
|
58555
59447
|
- MRRLIN_DIRECTOR_BRIDGE_STALE_SECONDS (default 300)
|
|
58556
|
-
- MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_IDLE_SECONDS (default
|
|
59448
|
+
- MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_IDLE_SECONDS (default 300;
|
|
58557
59449
|
a chat turn is cut only after this long with NO codex
|
|
58558
|
-
signal at all \u2014 long-but-active
|
|
58559
|
-
|
|
58560
|
-
|
|
59450
|
+
signal at all AND no tool call in flight \u2014 long-but-active
|
|
59451
|
+
turns, and long single tool calls, keep running)
|
|
59452
|
+
- MRRLIN_DIRECTOR_BRIDGE_CHAT_TURN_MAX_SECONDS (default 2700;
|
|
59453
|
+
absolute ceiling for one chat turn regardless of activity;
|
|
59454
|
+
the only guard against a genuinely hung tool call)
|
|
58561
59455
|
- MRRLIN_DIRECTOR_BRIDGE_CODEX_IDLE_SECONDS (default 300;
|
|
58562
59456
|
how long the warm codex app-server is kept alive
|
|
58563
59457
|
between turns before it is released)
|