@mrrlin-dev/mcp 0.4.32 → 0.4.33
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 +117 -4
- package/package.json +3 -3
package/dist/bin.cjs
CHANGED
|
@@ -37520,6 +37520,12 @@ var mrrlinRunBlockingReasonCodeSchema = external_exports.enum([
|
|
|
37520
37520
|
// start (verifyProviderReadiness returned ready:false) — an environment/infrastructure problem,
|
|
37521
37521
|
// not a task-content one, so it gets its own code rather than reusing task_blocked.
|
|
37522
37522
|
"provider_not_ready",
|
|
37523
|
+
// The agent provider refused work MID-RUN because its usage quota is exhausted — distinct from
|
|
37524
|
+
// `provider_not_ready`, which is the pre-flight readiness gate refusing to start. A quota is
|
|
37525
|
+
// transient and the run is retriable once the limit resets, so this code must never be conflated
|
|
37526
|
+
// with a task-content block; before it existed such a failure fell through to `unknown` and was
|
|
37527
|
+
// handed off to the operator as though the Director had judged the task.
|
|
37528
|
+
"provider_limit_exhausted",
|
|
37523
37529
|
"unknown"
|
|
37524
37530
|
]);
|
|
37525
37531
|
var mrrlinExecutionArtifactKindSchema = external_exports.enum([
|
|
@@ -39339,6 +39345,36 @@ function detectExhaustedProviderLimit(snapshot, nowMs) {
|
|
|
39339
39345
|
const resetAt = candidates.length > 0 ? candidates.reduce((a, b) => a > b ? a : b) : null;
|
|
39340
39346
|
return { exhausted: true, resetAt };
|
|
39341
39347
|
}
|
|
39348
|
+
var PROVIDER_LIMIT_ERROR_PATTERNS = [
|
|
39349
|
+
// OpenAI / Codex, verbatim from the observed event.
|
|
39350
|
+
/you'?ve hit your usage limit/i,
|
|
39351
|
+
// Anthropic phrasing for the same condition.
|
|
39352
|
+
/you have exceeded your (?:rate limit|usage limit)/i,
|
|
39353
|
+
/credit balance is too low/i,
|
|
39354
|
+
// START-ANCHORED quota wording. Round 2 split on this: the unanchored version was removed in
|
|
39355
|
+
// round 1 for matching task prose ("the quota is being exceeded by another worker"), and a round-2
|
|
39356
|
+
// reviewer then objected that removing it also drops real provider messages that LEAD with the
|
|
39357
|
+
// condition ("Monthly quota exceeded."). Anchoring resolves both — a provider error states the
|
|
39358
|
+
// condition first, prose embeds it — so the pattern is kept, but only at the head of the message.
|
|
39359
|
+
// Note this is a second line of defence either way: a provider that emits a structured
|
|
39360
|
+
// `rate_limits` snapshot is caught by detectExhaustedProviderLimit regardless of wording.
|
|
39361
|
+
// NOTE an unanchored `/usage limit reached/i` used to sit above, and it was the one real leak a
|
|
39362
|
+
// round-4 reviewer's finding pointed at (though not the example they gave): it matched
|
|
39363
|
+
// "[ERROR] memory usage limit reached", where the limit belongs to memory, not the provider. The
|
|
39364
|
+
// anchored pattern below already covers "Usage limit reached …", so the unanchored one was pure
|
|
39365
|
+
// false-positive surface and is gone.
|
|
39366
|
+
//
|
|
39367
|
+
// The optional prefix covers the log decorations providers actually ship with — "Error: ",
|
|
39368
|
+
// "[ERROR] ", "(fatal) " — without letting the condition sit anywhere in a sentence. The noun set
|
|
39369
|
+
// covers "rate limit"/"api limit" forms that carry no "quota" or "credits" at all.
|
|
39370
|
+
/^\s*(?:[[(]?\s*(?:error|fatal|warn(?:ing)?)\s*[\])]?\s*[:-]?\s*)?(?:monthly |daily |monthly\/daily )?(?:quota|usage limit|rate limit|api limit|api credits?|credits?) (?:has been |have been |is |are )?(?:exceeded|exhausted|used up|reached)\b/i
|
|
39371
|
+
];
|
|
39372
|
+
function detectProviderLimitFromErrorText(text) {
|
|
39373
|
+
if (typeof text !== "string" || text.trim().length === 0)
|
|
39374
|
+
return null;
|
|
39375
|
+
const matched = PROVIDER_LIMIT_ERROR_PATTERNS.some((pattern) => pattern.test(text));
|
|
39376
|
+
return matched ? { exhausted: true, resetAt: null } : null;
|
|
39377
|
+
}
|
|
39342
39378
|
|
|
39343
39379
|
// ../../packages/agent-client/dist/providers/codex/adapter.js
|
|
39344
39380
|
var CodexProviderAdapter = class {
|
|
@@ -41853,7 +41889,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41853
41889
|
}
|
|
41854
41890
|
|
|
41855
41891
|
// src/_generated/version.ts
|
|
41856
|
-
var PKG_VERSION = "0.4.
|
|
41892
|
+
var PKG_VERSION = "0.4.33";
|
|
41857
41893
|
|
|
41858
41894
|
// src/api-base-url-migrate.ts
|
|
41859
41895
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -46515,10 +46551,31 @@ function executionTurnBudgetMessage(budgetMs) {
|
|
|
46515
46551
|
function isExecutionTurnTimeoutReason(reason) {
|
|
46516
46552
|
return reason.startsWith(EXECUTION_TURN_TIMEOUT_PREFIX) || reason.startsWith(EXECUTION_TURN_STALL_PREFIX);
|
|
46517
46553
|
}
|
|
46554
|
+
var PROVIDER_LIMIT_RETRY_CURSOR_PREFIX = "provider_limit_retry:";
|
|
46555
|
+
var PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS = 1;
|
|
46556
|
+
var PROVIDER_LIMIT_RETRY_CODE = "provider_limit_retry";
|
|
46557
|
+
var PROVIDER_LIMIT_PAUSE_REASON = "Agent provider usage limit exhausted during an autonomous execution run.";
|
|
46558
|
+
var PROVIDER_LIMIT_HANDOFF_REASON = "The agent provider's usage limit stayed exhausted across every automatic retry, so this run could not proceed. This is a provider quota problem, not a problem with the task.";
|
|
46559
|
+
function providerLimitRetryCursor(attempt) {
|
|
46560
|
+
return `${PROVIDER_LIMIT_RETRY_CURSOR_PREFIX}${attempt}`;
|
|
46561
|
+
}
|
|
46562
|
+
function providerLimitRetryAttempt(checkpointCursor) {
|
|
46563
|
+
if (!checkpointCursor || !checkpointCursor.startsWith(PROVIDER_LIMIT_RETRY_CURSOR_PREFIX)) return 0;
|
|
46564
|
+
const parsed = Number.parseInt(checkpointCursor.slice(PROVIDER_LIMIT_RETRY_CURSOR_PREFIX.length), 10);
|
|
46565
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
|
|
46566
|
+
}
|
|
46518
46567
|
function reportAutopilotLimitPause(client, reason, resetAt) {
|
|
46519
46568
|
void client.pauseAutopilotForLimit({ source: "provider_limit", reason, resetAt }).catch(() => {
|
|
46520
46569
|
});
|
|
46521
46570
|
}
|
|
46571
|
+
async function applyAutopilotLimitPause(client, reason, resetAt) {
|
|
46572
|
+
try {
|
|
46573
|
+
await client.pauseAutopilotForLimit({ source: "provider_limit", reason, resetAt });
|
|
46574
|
+
return true;
|
|
46575
|
+
} catch {
|
|
46576
|
+
return false;
|
|
46577
|
+
}
|
|
46578
|
+
}
|
|
46522
46579
|
function isDeliverableGateRetryableReason(reason) {
|
|
46523
46580
|
return DELIVERABLE_GATE_RETRYABLE_REASON_PREFIXES.some((prefix) => reason.startsWith(prefix));
|
|
46524
46581
|
}
|
|
@@ -48027,6 +48084,62 @@ async function failRun(deps, projectSlug, task, runId, leaseId, reason, reasonCo
|
|
|
48027
48084
|
return;
|
|
48028
48085
|
}
|
|
48029
48086
|
if (currentRun.status !== "running" || currentRun.leaseId !== leaseId) return;
|
|
48087
|
+
let handoffReason = reason;
|
|
48088
|
+
let handoffReasonCode = reasonCode;
|
|
48089
|
+
const providerLimit = detectProviderLimitFromErrorText(reason);
|
|
48090
|
+
if (providerLimit) {
|
|
48091
|
+
const autopilotPaused = await applyAutopilotLimitPause(
|
|
48092
|
+
deps.client,
|
|
48093
|
+
PROVIDER_LIMIT_PAUSE_REASON,
|
|
48094
|
+
providerLimit.resetAt
|
|
48095
|
+
);
|
|
48096
|
+
const attempt = providerLimitRetryAttempt(currentRun.checkpointCursor) + 1;
|
|
48097
|
+
if (attempt <= PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS) {
|
|
48098
|
+
try {
|
|
48099
|
+
await deps.client.updateExecutionRun(projectSlug, runId, {
|
|
48100
|
+
status: "pending",
|
|
48101
|
+
leaseId: null,
|
|
48102
|
+
checkpointCursor: providerLimitRetryCursor(attempt)
|
|
48103
|
+
});
|
|
48104
|
+
await deps.client.appendExecutionArtifact(projectSlug, runId, {
|
|
48105
|
+
id: newArtifactId(),
|
|
48106
|
+
kind: "error",
|
|
48107
|
+
payload: {
|
|
48108
|
+
code: PROVIDER_LIMIT_RETRY_CODE,
|
|
48109
|
+
source: "execution-consumer",
|
|
48110
|
+
attempt,
|
|
48111
|
+
maxAttempts: PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS,
|
|
48112
|
+
// The provider's raw sentence belongs HERE — diagnostic detail on an error artifact —
|
|
48113
|
+
// and never in a handoff `reason`, which the DB trigger caches as the task's judgement.
|
|
48114
|
+
providerMessage: reason,
|
|
48115
|
+
// Whether the brake actually engaged. If it did not, the requeued run is only held
|
|
48116
|
+
// back by its own attempt budget, so this is the difference between "waiting for the
|
|
48117
|
+
// quota" and "about to fail once more"; it must be visible, not swallowed.
|
|
48118
|
+
autopilotPaused,
|
|
48119
|
+
summary: autopilotPaused ? `Agent provider usage limit exhausted; autopilot paused and the run requeued for retry ${attempt}/${PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS}.` : `Agent provider usage limit exhausted; the run was requeued for retry ${attempt}/${PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS}, but pausing autopilot FAILED, so nothing is holding it back.`
|
|
48120
|
+
}
|
|
48121
|
+
}).catch(() => {
|
|
48122
|
+
});
|
|
48123
|
+
return;
|
|
48124
|
+
} catch {
|
|
48125
|
+
}
|
|
48126
|
+
}
|
|
48127
|
+
handoffReason = PROVIDER_LIMIT_HANDOFF_REASON;
|
|
48128
|
+
handoffReasonCode = "provider_limit_exhausted";
|
|
48129
|
+
await deps.client.appendExecutionArtifact(projectSlug, runId, {
|
|
48130
|
+
id: newArtifactId(),
|
|
48131
|
+
kind: "error",
|
|
48132
|
+
payload: {
|
|
48133
|
+
code: PROVIDER_LIMIT_RETRY_CODE,
|
|
48134
|
+
source: "execution-consumer",
|
|
48135
|
+
attempt: PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS,
|
|
48136
|
+
maxAttempts: PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS,
|
|
48137
|
+
providerMessage: reason,
|
|
48138
|
+
summary: "Agent provider usage limit still exhausted after every retry; handing off to the operator."
|
|
48139
|
+
}
|
|
48140
|
+
}).catch(() => {
|
|
48141
|
+
});
|
|
48142
|
+
}
|
|
48030
48143
|
if (isExecutionTurnTimeoutReason(reason) && currentRun.checkpointCursor !== EXECUTION_TURN_TIMEOUT_RETRY_CURSOR) {
|
|
48031
48144
|
try {
|
|
48032
48145
|
await deps.client.updateExecutionRun(projectSlug, runId, {
|
|
@@ -48079,11 +48192,11 @@ async function failRun(deps, projectSlug, task, runId, leaseId, reason, reasonCo
|
|
|
48079
48192
|
}
|
|
48080
48193
|
try {
|
|
48081
48194
|
await deps.client.createExecutionRunHandoff(projectSlug, runId, {
|
|
48082
|
-
reason,
|
|
48083
|
-
...
|
|
48195
|
+
reason: handoffReason,
|
|
48196
|
+
...handoffReasonCode ? { reasonCode: handoffReasonCode } : {}
|
|
48084
48197
|
});
|
|
48085
48198
|
} catch {
|
|
48086
|
-
await notifyOperatorHandoff(deps, projectSlug, task, runId,
|
|
48199
|
+
await notifyOperatorHandoff(deps, projectSlug, task, runId, handoffReason);
|
|
48087
48200
|
}
|
|
48088
48201
|
}
|
|
48089
48202
|
async function reconcileTaskAwaitingMerge(deps, projectSlug, task, runId, reason) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"@mrrlin/director-e2e": "0.0.0",
|
|
26
26
|
"@mrrlin/schemas": "0.0.0",
|
|
27
27
|
"@mrrlin/tsconfig": "0.0.0",
|
|
28
|
-
"@mrrlin/
|
|
29
|
-
"@mrrlin/
|
|
28
|
+
"@mrrlin/wiki": "0.0.0",
|
|
29
|
+
"@mrrlin/agent-client": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|