@mrrlin-dev/mcp 0.4.31 → 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 +159 -28
- package/package.json +3 -3
package/dist/bin.cjs
CHANGED
|
@@ -37388,6 +37388,22 @@ function isRunnableProposal(item) {
|
|
|
37388
37388
|
return true;
|
|
37389
37389
|
}
|
|
37390
37390
|
|
|
37391
|
+
// ../../packages/schemas/dist/task-notes.js
|
|
37392
|
+
var TASK_NOTES_BRIEF_SECTIONS = [
|
|
37393
|
+
{ heading: "Context", description: "why this task exists; link to parent/trigger task if follow-up." },
|
|
37394
|
+
{ heading: "Scope", description: "concrete actions and acceptance criteria." },
|
|
37395
|
+
{ heading: "References", description: "links, data, relevant findings (omit if nothing to reference)." }
|
|
37396
|
+
];
|
|
37397
|
+
var TASK_NOTES_JOURNAL_SECTIONS = [
|
|
37398
|
+
{ heading: "Result", description: "what was accomplished, key findings or metrics, follow-up tasks created." },
|
|
37399
|
+
{
|
|
37400
|
+
heading: "Recheck",
|
|
37401
|
+
dated: true,
|
|
37402
|
+
description: "what you checked, what you found, and what happens next."
|
|
37403
|
+
}
|
|
37404
|
+
];
|
|
37405
|
+
var JOURNAL_HEADING_RE = new RegExp(`^##[ \\t]+(${TASK_NOTES_JOURNAL_SECTIONS.map((s) => s.heading).join("|")})\\b[ \\t]*(?:[\u2014\u2013-][ \\t]*(\\S.*?))?[ \\t]*$`);
|
|
37406
|
+
|
|
37391
37407
|
// ../../packages/schemas/dist/index.js
|
|
37392
37408
|
var mrrlinTaskStatusSchema = external_exports.enum([
|
|
37393
37409
|
"backlog",
|
|
@@ -37504,6 +37520,12 @@ var mrrlinRunBlockingReasonCodeSchema = external_exports.enum([
|
|
|
37504
37520
|
// start (verifyProviderReadiness returned ready:false) — an environment/infrastructure problem,
|
|
37505
37521
|
// not a task-content one, so it gets its own code rather than reusing task_blocked.
|
|
37506
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",
|
|
37507
37529
|
"unknown"
|
|
37508
37530
|
]);
|
|
37509
37531
|
var mrrlinExecutionArtifactKindSchema = external_exports.enum([
|
|
@@ -39323,6 +39345,36 @@ function detectExhaustedProviderLimit(snapshot, nowMs) {
|
|
|
39323
39345
|
const resetAt = candidates.length > 0 ? candidates.reduce((a, b) => a > b ? a : b) : null;
|
|
39324
39346
|
return { exhausted: true, resetAt };
|
|
39325
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
|
+
}
|
|
39326
39378
|
|
|
39327
39379
|
// ../../packages/agent-client/dist/providers/codex/adapter.js
|
|
39328
39380
|
var CodexProviderAdapter = class {
|
|
@@ -41837,7 +41889,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41837
41889
|
}
|
|
41838
41890
|
|
|
41839
41891
|
// src/_generated/version.ts
|
|
41840
|
-
var PKG_VERSION = "0.4.
|
|
41892
|
+
var PKG_VERSION = "0.4.33";
|
|
41841
41893
|
|
|
41842
41894
|
// src/api-base-url-migrate.ts
|
|
41843
41895
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -46039,6 +46091,30 @@ function isDeployGateRunStatus(status) {
|
|
|
46039
46091
|
return status === "dev_deploy" || status === "prod_deploy";
|
|
46040
46092
|
}
|
|
46041
46093
|
|
|
46094
|
+
// src/prompts/task-notes-contract.ts
|
|
46095
|
+
function renderTaskNotesContractLines() {
|
|
46096
|
+
const brief = TASK_NOTES_BRIEF_SECTIONS.map((s) => ` ## ${s.heading} \u2014 ${s.description}`);
|
|
46097
|
+
const [result, recheck] = TASK_NOTES_JOURNAL_SECTIONS;
|
|
46098
|
+
return [
|
|
46099
|
+
"- Notes (markdown): write for a human operator scanning a task list.",
|
|
46100
|
+
" First line: one plain-text sentence summarizing the task (no heading).",
|
|
46101
|
+
" Then scale the structure to complexity:",
|
|
46102
|
+
" For simple tasks \u2014 the one-line summary may be enough. No headings required.",
|
|
46103
|
+
" For non-trivial tasks, use these sections as needed:",
|
|
46104
|
+
...brief,
|
|
46105
|
+
" When closing a task (update_task status=closed_done), APPEND a Result section:",
|
|
46106
|
+
` ## ${result?.heading} \u2014 ${result?.description}`,
|
|
46107
|
+
" When you revisit a task that already has a Result \u2014 a later verification, a retry, a status",
|
|
46108
|
+
" re-check \u2014 APPEND a dated entry instead of rewriting Result:",
|
|
46109
|
+
` ## ${recheck?.heading} \u2014 YYYY-MM-DD \u2014 ${recheck?.description}`,
|
|
46110
|
+
" Do NOT remove earlier sections \u2014 they remain useful for context.",
|
|
46111
|
+
" The sections above the first Result/Recheck are the brief (what the task IS); Result and",
|
|
46112
|
+
" Recheck are the execution journal (what HAPPENED). The task page renders them separately and",
|
|
46113
|
+
" collapses the journal, so keep each kind of content under the right heading.",
|
|
46114
|
+
" Adapt depth to the task: a one-line fix needs one sentence, not four headings."
|
|
46115
|
+
];
|
|
46116
|
+
}
|
|
46117
|
+
|
|
46042
46118
|
// src/turn-deadline.ts
|
|
46043
46119
|
var DEFAULT_TURN_INTERRUPT_TIMEOUT_MS = 5e3;
|
|
46044
46120
|
function withTimeout2(promise2, ms, message) {
|
|
@@ -46475,10 +46551,31 @@ function executionTurnBudgetMessage(budgetMs) {
|
|
|
46475
46551
|
function isExecutionTurnTimeoutReason(reason) {
|
|
46476
46552
|
return reason.startsWith(EXECUTION_TURN_TIMEOUT_PREFIX) || reason.startsWith(EXECUTION_TURN_STALL_PREFIX);
|
|
46477
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
|
+
}
|
|
46478
46567
|
function reportAutopilotLimitPause(client, reason, resetAt) {
|
|
46479
46568
|
void client.pauseAutopilotForLimit({ source: "provider_limit", reason, resetAt }).catch(() => {
|
|
46480
46569
|
});
|
|
46481
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
|
+
}
|
|
46482
46579
|
function isDeliverableGateRetryableReason(reason) {
|
|
46483
46580
|
return DELIVERABLE_GATE_RETRYABLE_REASON_PREFIXES.some((prefix) => reason.startsWith(prefix));
|
|
46484
46581
|
}
|
|
@@ -46612,18 +46709,7 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
46612
46709
|
"- Title: short, actionable, human-readable. Describe what needs to happen, not internal jargon.",
|
|
46613
46710
|
' Good: "Audit crawled-not-indexed pages and fix 404 errors"',
|
|
46614
46711
|
' Bad: "GSC coverage triage crawled-not-indexed + 404"',
|
|
46615
|
-
|
|
46616
|
-
" First line: one plain-text sentence summarizing the task (no heading).",
|
|
46617
|
-
" Then scale the structure to complexity:",
|
|
46618
|
-
" For simple tasks \u2014 the one-line summary may be enough. No headings required.",
|
|
46619
|
-
" For non-trivial tasks, use these sections as needed:",
|
|
46620
|
-
" ## Context \u2014 why this task exists; link to parent/trigger task if follow-up.",
|
|
46621
|
-
" ## Scope \u2014 concrete actions and acceptance criteria.",
|
|
46622
|
-
" ## References \u2014 links, data, relevant findings (omit if nothing to reference).",
|
|
46623
|
-
" When closing a task (update_task status=closed_done), APPEND a Result section:",
|
|
46624
|
-
" ## Result \u2014 what was accomplished, key findings or metrics, follow-up tasks created.",
|
|
46625
|
-
" Do NOT remove earlier sections \u2014 they remain useful for context.",
|
|
46626
|
-
" Adapt depth to the task: a one-line fix needs one sentence, not four headings.",
|
|
46712
|
+
...renderTaskNotesContractLines(),
|
|
46627
46713
|
"",
|
|
46628
46714
|
// Slice 4 of token-efficient-execution-runs (D8). This is the ONLY way the reasoning behind a
|
|
46629
46715
|
// decision survives past this conversation: a thread is retired once it has accumulated too many
|
|
@@ -47998,6 +48084,62 @@ async function failRun(deps, projectSlug, task, runId, leaseId, reason, reasonCo
|
|
|
47998
48084
|
return;
|
|
47999
48085
|
}
|
|
48000
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
|
+
}
|
|
48001
48143
|
if (isExecutionTurnTimeoutReason(reason) && currentRun.checkpointCursor !== EXECUTION_TURN_TIMEOUT_RETRY_CURSOR) {
|
|
48002
48144
|
try {
|
|
48003
48145
|
await deps.client.updateExecutionRun(projectSlug, runId, {
|
|
@@ -48050,11 +48192,11 @@ async function failRun(deps, projectSlug, task, runId, leaseId, reason, reasonCo
|
|
|
48050
48192
|
}
|
|
48051
48193
|
try {
|
|
48052
48194
|
await deps.client.createExecutionRunHandoff(projectSlug, runId, {
|
|
48053
|
-
reason,
|
|
48054
|
-
...
|
|
48195
|
+
reason: handoffReason,
|
|
48196
|
+
...handoffReasonCode ? { reasonCode: handoffReasonCode } : {}
|
|
48055
48197
|
});
|
|
48056
48198
|
} catch {
|
|
48057
|
-
await notifyOperatorHandoff(deps, projectSlug, task, runId,
|
|
48199
|
+
await notifyOperatorHandoff(deps, projectSlug, task, runId, handoffReason);
|
|
48058
48200
|
}
|
|
48059
48201
|
}
|
|
48060
48202
|
async function reconcileTaskAwaitingMerge(deps, projectSlug, task, runId, reason) {
|
|
@@ -54218,18 +54360,7 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
54218
54360
|
"- Title: short, actionable, human-readable. Describe what needs to happen, not internal jargon.",
|
|
54219
54361
|
' Good: "Audit crawled-not-indexed pages and fix 404 errors"',
|
|
54220
54362
|
' Bad: "GSC coverage triage crawled-not-indexed + 404"',
|
|
54221
|
-
|
|
54222
|
-
" First line: one plain-text sentence summarizing the task (no heading).",
|
|
54223
|
-
" Then scale the structure to complexity:",
|
|
54224
|
-
" For simple tasks \u2014 the one-line summary may be enough. No headings required.",
|
|
54225
|
-
" For non-trivial tasks, use these sections as needed:",
|
|
54226
|
-
" ## Context \u2014 why this task exists; link to parent/trigger task if follow-up.",
|
|
54227
|
-
" ## Scope \u2014 concrete actions and acceptance criteria.",
|
|
54228
|
-
" ## References \u2014 links, data, relevant findings (omit if nothing to reference).",
|
|
54229
|
-
" When closing a task (update_task status=closed_done), APPEND a Result section:",
|
|
54230
|
-
" ## Result \u2014 what was accomplished, key findings or metrics, follow-up tasks created.",
|
|
54231
|
-
" Do NOT remove earlier sections \u2014 they remain useful for context.",
|
|
54232
|
-
" Adapt depth to the task: a one-line fix needs one sentence, not four headings.",
|
|
54363
|
+
...renderTaskNotesContractLines(),
|
|
54233
54364
|
// early-intent-confirmation (design.md, "The chat contract"): the core block below is copied
|
|
54234
54365
|
// VERBATIM from design.md, per tasks.md 10.1. It replaces an older sentence that also carried
|
|
54235
54366
|
// "omit operatorSeedRequest only when there is genuinely no operator request behind the task
|
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"
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
"esbuild": "^0.24.0",
|
|
23
23
|
"tsx": "^4.22.3",
|
|
24
24
|
"@mrrlin/client": "0.0.0",
|
|
25
|
-
"@mrrlin/agent-client": "0.0.0",
|
|
26
25
|
"@mrrlin/director-e2e": "0.0.0",
|
|
27
26
|
"@mrrlin/schemas": "0.0.0",
|
|
28
27
|
"@mrrlin/tsconfig": "0.0.0",
|
|
29
|
-
"@mrrlin/wiki": "0.0.0"
|
|
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",
|