@mrrlin-dev/mcp 0.4.32 → 0.4.36
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 +546 -8
- package/package.json +2 -2
package/dist/bin.cjs
CHANGED
|
@@ -36799,6 +36799,24 @@ function date4(params) {
|
|
|
36799
36799
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
36800
36800
|
config(en_default());
|
|
36801
36801
|
|
|
36802
|
+
// ../../packages/schemas/dist/artifact-checks.js
|
|
36803
|
+
var artifactKindSchema = external_exports.enum([
|
|
36804
|
+
"document",
|
|
36805
|
+
"page",
|
|
36806
|
+
"file_set",
|
|
36807
|
+
"code_change",
|
|
36808
|
+
"structured_data",
|
|
36809
|
+
"message"
|
|
36810
|
+
]);
|
|
36811
|
+
var ARTIFACT_CHECKS = {
|
|
36812
|
+
document: null,
|
|
36813
|
+
page: { verifier: "url" },
|
|
36814
|
+
file_set: null,
|
|
36815
|
+
code_change: { verifier: "build" },
|
|
36816
|
+
structured_data: null,
|
|
36817
|
+
message: { verifier: "status" }
|
|
36818
|
+
};
|
|
36819
|
+
|
|
36802
36820
|
// ../../packages/schemas/dist/operationIds.js
|
|
36803
36821
|
var openApiOperationIds = {
|
|
36804
36822
|
appendExecutionArtifact: "appendExecutionArtifact",
|
|
@@ -36889,6 +36907,7 @@ var openApiOperationIds = {
|
|
|
36889
36907
|
restartExecutionRun: "restartExecutionRun",
|
|
36890
36908
|
resolveHandoff: "resolveHandoff",
|
|
36891
36909
|
touchExecutionRun: "touchExecutionRun",
|
|
36910
|
+
stopExecutionRun: "stopExecutionRun",
|
|
36892
36911
|
getTaskState: "getTaskState",
|
|
36893
36912
|
upsertTaskState: "upsertTaskState",
|
|
36894
36913
|
retireThread: "retireThread",
|
|
@@ -36929,7 +36948,15 @@ var openApiOperationIds = {
|
|
|
36929
36948
|
getSpecConsensusReview: "getSpecConsensusReview",
|
|
36930
36949
|
onboardingKickoffTransition: "onboardingKickoffTransition",
|
|
36931
36950
|
ingestTelemetry: "ingestTelemetry",
|
|
36932
|
-
getTelemetry: "getTelemetry"
|
|
36951
|
+
getTelemetry: "getTelemetry",
|
|
36952
|
+
listCapabilityGrants: "listCapabilityGrants",
|
|
36953
|
+
createCapabilityGrant: "createCapabilityGrant",
|
|
36954
|
+
revokeCapabilityGrant: "revokeCapabilityGrant",
|
|
36955
|
+
listCapabilityRoutes: "listCapabilityRoutes",
|
|
36956
|
+
upsertCapabilityRoute: "upsertCapabilityRoute",
|
|
36957
|
+
resolveCapabilityRoute: "resolveCapabilityRoute",
|
|
36958
|
+
overrideCapabilityToolClass: "overrideCapabilityToolClass",
|
|
36959
|
+
listCapabilityUses: "listCapabilityUses"
|
|
36933
36960
|
};
|
|
36934
36961
|
|
|
36935
36962
|
// ../../packages/schemas/dist/session.js
|
|
@@ -37437,6 +37464,58 @@ function domainScopedReviewHash(rawHash, domain2) {
|
|
|
37437
37464
|
const d = normalizeTaskDomain(domain2);
|
|
37438
37465
|
return d === "code" ? rawHash : `${rawHash}@${d}`;
|
|
37439
37466
|
}
|
|
37467
|
+
var mrrlinDeclaredOutcomeShapeSchema = external_exports.object({
|
|
37468
|
+
sentence: external_exports.string().trim().min(1),
|
|
37469
|
+
requires: external_exports.array(artifactKindSchema).min(1),
|
|
37470
|
+
verification: external_exports.enum(["machine", "human_tap"])
|
|
37471
|
+
});
|
|
37472
|
+
var mrrlinDeclaredOutcomeSchema = mrrlinDeclaredOutcomeShapeSchema.superRefine((value, ctx) => {
|
|
37473
|
+
const expectedVerifiers = new Set(value.requires.map((kind) => ARTIFACT_CHECKS[kind]?.verifier ?? "human_tap"));
|
|
37474
|
+
if (expectedVerifiers.size > 1) {
|
|
37475
|
+
const spread = value.requires.map((kind) => {
|
|
37476
|
+
const check2 = ARTIFACT_CHECKS[kind];
|
|
37477
|
+
return check2 === null ? `${kind} has no machine check` : `${kind} needs ${check2.verifier}`;
|
|
37478
|
+
}).join(", ");
|
|
37479
|
+
ctx.addIssue({
|
|
37480
|
+
code: external_exports.ZodIssueCode.custom,
|
|
37481
|
+
path: ["requires"],
|
|
37482
|
+
message: `requires spans more than one verifier, so no receipt can ever satisfy it (${spread}). Declare the outcome rather than the steps that produced it; if two things must genuinely exist and neither is a means to the other, split them into linked tasks.`
|
|
37483
|
+
});
|
|
37484
|
+
}
|
|
37485
|
+
const hasVerifiableKind = value.requires.some((kind) => ARTIFACT_CHECKS[kind] !== null);
|
|
37486
|
+
const hasUnverifiableKind = value.requires.some((kind) => ARTIFACT_CHECKS[kind] === null);
|
|
37487
|
+
if (value.verification === "human_tap" && hasVerifiableKind) {
|
|
37488
|
+
ctx.addIssue({
|
|
37489
|
+
code: external_exports.ZodIssueCode.custom,
|
|
37490
|
+
path: ["verification"],
|
|
37491
|
+
message: "human_tap is not permitted: a required kind has a machine verifier"
|
|
37492
|
+
});
|
|
37493
|
+
}
|
|
37494
|
+
if (value.verification === "machine" && hasUnverifiableKind) {
|
|
37495
|
+
ctx.addIssue({
|
|
37496
|
+
code: external_exports.ZodIssueCode.custom,
|
|
37497
|
+
path: ["verification"],
|
|
37498
|
+
message: "machine is not permitted: a required kind has no machine verifier"
|
|
37499
|
+
});
|
|
37500
|
+
}
|
|
37501
|
+
});
|
|
37502
|
+
var mrrlinReceiptSchema = external_exports.object({
|
|
37503
|
+
kind: external_exports.enum(["outcome", "attempt"]),
|
|
37504
|
+
evidence: external_exports.object({
|
|
37505
|
+
type: external_exports.enum(["url", "build", "status", "human_tap"]),
|
|
37506
|
+
value: external_exports.string().trim().min(1)
|
|
37507
|
+
}),
|
|
37508
|
+
observedAt: external_exports.string().datetime(),
|
|
37509
|
+
requiresCovered: external_exports.array(artifactKindSchema)
|
|
37510
|
+
});
|
|
37511
|
+
function buildReceipt(input) {
|
|
37512
|
+
return {
|
|
37513
|
+
kind: input.coveredKinds.length === 0 ? "attempt" : "outcome",
|
|
37514
|
+
evidence: { type: input.evidenceType, value: input.evidenceValue },
|
|
37515
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37516
|
+
requiresCovered: input.coveredKinds
|
|
37517
|
+
};
|
|
37518
|
+
}
|
|
37440
37519
|
var mrrlinSpecApprovedSchema = external_exports.enum(["pending", "approved", "skipped", "skipped_legacy"]);
|
|
37441
37520
|
var mrrlinTaskSourceSchema = external_exports.enum(["fixture-import", "api"]);
|
|
37442
37521
|
var mrrlinPlanStatusSchema = external_exports.enum(["active", "paused", "done", "archived"]);
|
|
@@ -37520,6 +37599,16 @@ var mrrlinRunBlockingReasonCodeSchema = external_exports.enum([
|
|
|
37520
37599
|
// start (verifyProviderReadiness returned ready:false) — an environment/infrastructure problem,
|
|
37521
37600
|
// not a task-content one, so it gets its own code rather than reusing task_blocked.
|
|
37522
37601
|
"provider_not_ready",
|
|
37602
|
+
// agnostic-delivery: an operator-initiated stop. Parks the run at handoff_requested rather
|
|
37603
|
+
// than cancelling it, so restartParkedExecutionRun can resume it — a zod-only enum extension,
|
|
37604
|
+
// since blocking_reason is a plain text column with no DB CHECK.
|
|
37605
|
+
"operator_stopped",
|
|
37606
|
+
// The agent provider refused work MID-RUN because its usage quota is exhausted — distinct from
|
|
37607
|
+
// `provider_not_ready`, which is the pre-flight readiness gate refusing to start. A quota is
|
|
37608
|
+
// transient and the run is retriable once the limit resets, so this code must never be conflated
|
|
37609
|
+
// with a task-content block; before it existed such a failure fell through to `unknown` and was
|
|
37610
|
+
// handed off to the operator as though the Director had judged the task.
|
|
37611
|
+
"provider_limit_exhausted",
|
|
37523
37612
|
"unknown"
|
|
37524
37613
|
]);
|
|
37525
37614
|
var mrrlinExecutionArtifactKindSchema = external_exports.enum([
|
|
@@ -37632,6 +37721,14 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
37632
37721
|
// resolve it via taskRowToTask. Null reads as the pre-existing default provider and is never
|
|
37633
37722
|
// backfilled -- it is NOT a mismatch against whatever provider is currently selected.
|
|
37634
37723
|
directorThreadProvider: external_exports.enum(["codex", "claude"]).nullable().optional(),
|
|
37724
|
+
// agnostic-delivery: asserted before the work, not the deliverable-gate's differently-meaning
|
|
37725
|
+
// `attemptOutcome` asserted after it (design.md). Required at spec approval (task 3.2's API
|
|
37726
|
+
// gate), never at creation -- null on every task until then, forever on legacy tasks. Shape-only
|
|
37727
|
+
// here deliberately (not the cross-check-enforcing mrrlinDeclaredOutcomeSchema): a read must
|
|
37728
|
+
// never crash on a persisted value that fails the manifest cross-check (a raw SQL row, or one
|
|
37729
|
+
// written before task 1.5's superRefine existed) -- only the write path (mrrlinTaskUpdateSchema
|
|
37730
|
+
// below, and task 3.2's approval-time re-check) enforces that stronger rule.
|
|
37731
|
+
declaredOutcome: mrrlinDeclaredOutcomeShapeSchema.nullable().optional(),
|
|
37635
37732
|
dueDate: external_exports.string().date().nullable(),
|
|
37636
37733
|
followUpAt: external_exports.string().datetime().nullable(),
|
|
37637
37734
|
id: mrrlinTaskIdSchema,
|
|
@@ -37968,6 +38065,7 @@ var mrrlinTaskUpdateSchema = external_exports.object({
|
|
|
37968
38065
|
directorSessionId: external_exports.string().min(1).optional(),
|
|
37969
38066
|
directorThreadId: external_exports.string().min(1).nullable().optional(),
|
|
37970
38067
|
directorThreadProvider: external_exports.enum(["codex", "claude"]).nullable().optional(),
|
|
38068
|
+
declaredOutcome: mrrlinDeclaredOutcomeSchema.nullable().optional(),
|
|
37971
38069
|
dueDate: external_exports.string().date().nullable().optional(),
|
|
37972
38070
|
impact: mrrlinTaskImpactSchema.optional(),
|
|
37973
38071
|
notes: external_exports.string().nullable().optional(),
|
|
@@ -38475,7 +38573,11 @@ var mrrlinSettleExecutionRunOutcomeSchema = external_exports.object({
|
|
|
38475
38573
|
resultSummary: external_exports.string().trim().min(1).max(300),
|
|
38476
38574
|
deliverablePageId: mrrlinWikiPageIdSchema,
|
|
38477
38575
|
contentHash: external_exports.string().min(1),
|
|
38478
|
-
leaseId: external_exports.string().min(1)
|
|
38576
|
+
leaseId: external_exports.string().min(1),
|
|
38577
|
+
// Optional: a legacy caller sends none. Absent counts as unsatisfied whenever the task has a
|
|
38578
|
+
// non-null declared_outcome (task 2.7b) — losing dedup must not lose the write, but losing a
|
|
38579
|
+
// receipt must not silently close a task that declared one.
|
|
38580
|
+
receipt: mrrlinReceiptSchema.optional()
|
|
38479
38581
|
});
|
|
38480
38582
|
var mrrlinExecutionArtifactSchema = external_exports.object({
|
|
38481
38583
|
createdAt: external_exports.string().datetime(),
|
|
@@ -39041,6 +39143,74 @@ var inboxItemsMarkReadResponseSchema = external_exports.object({
|
|
|
39041
39143
|
});
|
|
39042
39144
|
var inboxItemResponseSchema = external_exports.object({ data: mrrlinInboxItemSchema });
|
|
39043
39145
|
var inboxItemListResponseSchema = external_exports.object({ data: external_exports.array(mrrlinInboxItemSchema) });
|
|
39146
|
+
var capabilityServerIdSchema = external_exports.string().trim().min(1).max(200);
|
|
39147
|
+
var capabilityNameSchema = external_exports.string().trim().min(1).max(200);
|
|
39148
|
+
var capabilityToolNameSchema = external_exports.string().trim().min(1).max(200);
|
|
39149
|
+
var CapabilityReversibilitySchema = external_exports.enum(["reversible", "irreversible"]);
|
|
39150
|
+
var CapabilityGateVerdictSchema = external_exports.enum([
|
|
39151
|
+
"silent",
|
|
39152
|
+
"ask_once",
|
|
39153
|
+
"window",
|
|
39154
|
+
"approval",
|
|
39155
|
+
"refused"
|
|
39156
|
+
]);
|
|
39157
|
+
var capabilityToolClassSourceSchema = external_exports.enum(["annotation", "operator"]);
|
|
39158
|
+
var capabilityUseOutcomeSchema = external_exports.enum(["ok", "error", "refused"]);
|
|
39159
|
+
var CapabilityGrantSchema = external_exports.object({
|
|
39160
|
+
projectSlug: mrrlinProjectSlugSchema,
|
|
39161
|
+
serverId: capabilityServerIdSchema,
|
|
39162
|
+
grantedAt: external_exports.string().datetime(),
|
|
39163
|
+
// A revoke is a soft revoke: the row stays so `resolveCapabilityRoute` can answer `no_grant`
|
|
39164
|
+
// rather than the indistinguishable `no_route`. A live grant is `revokedAt === null`.
|
|
39165
|
+
revokedAt: external_exports.string().datetime().nullable(),
|
|
39166
|
+
// "First write asks once" is bounded by (project, server) -- exactly this row's key -- so the
|
|
39167
|
+
// confirmation lives here rather than in a second table. `revokeCapability` clears it, which is
|
|
39168
|
+
// what makes a re-grant ask again.
|
|
39169
|
+
firstWriteConfirmedAt: external_exports.string().datetime().nullable()
|
|
39170
|
+
});
|
|
39171
|
+
var CapabilityRouteSchema = external_exports.object({
|
|
39172
|
+
projectSlug: mrrlinProjectSlugSchema,
|
|
39173
|
+
capability: capabilityNameSchema,
|
|
39174
|
+
// The artifact kind this route delivers. NOT the key -- a project can have two sites, and a key
|
|
39175
|
+
// of `page` would let the second overwrite the first. It is here so a declared outcome, which
|
|
39176
|
+
// carries only artifact kinds, can find its route: without it there is no chain from what a task
|
|
39177
|
+
// promises to where the delivery goes.
|
|
39178
|
+
artifactKind: artifactKindSchema,
|
|
39179
|
+
serverId: capabilityServerIdSchema,
|
|
39180
|
+
toolName: capabilityToolNameSchema,
|
|
39181
|
+
createdAt: external_exports.string().datetime(),
|
|
39182
|
+
updatedAt: external_exports.string().datetime()
|
|
39183
|
+
});
|
|
39184
|
+
var CapabilityToolClassSchema = external_exports.object({
|
|
39185
|
+
projectSlug: mrrlinProjectSlugSchema,
|
|
39186
|
+
serverId: capabilityServerIdSchema,
|
|
39187
|
+
toolName: capabilityToolNameSchema,
|
|
39188
|
+
toolClass: CapabilityReversibilitySchema,
|
|
39189
|
+
source: capabilityToolClassSourceSchema,
|
|
39190
|
+
setAt: external_exports.string().datetime()
|
|
39191
|
+
});
|
|
39192
|
+
var CapabilityUseSchema = external_exports.object({
|
|
39193
|
+
id: external_exports.number().int().nonnegative(),
|
|
39194
|
+
projectSlug: mrrlinProjectSlugSchema,
|
|
39195
|
+
serverId: capabilityServerIdSchema,
|
|
39196
|
+
toolName: capabilityToolNameSchema,
|
|
39197
|
+
taskId: mrrlinTaskIdSchema.nullable(),
|
|
39198
|
+
outcome: capabilityUseOutcomeSchema,
|
|
39199
|
+
at: external_exports.string().datetime()
|
|
39200
|
+
});
|
|
39201
|
+
var capabilityGrantCreateSchema = external_exports.object({ serverId: capabilityServerIdSchema });
|
|
39202
|
+
var capabilityRouteUpsertSchema = external_exports.object({
|
|
39203
|
+
artifactKind: artifactKindSchema,
|
|
39204
|
+
serverId: capabilityServerIdSchema,
|
|
39205
|
+
toolName: capabilityToolNameSchema
|
|
39206
|
+
});
|
|
39207
|
+
var capabilityToolClassOverrideSchema = external_exports.object({ toolClass: CapabilityReversibilitySchema });
|
|
39208
|
+
var capabilityGrantResponseSchema = external_exports.object({ data: CapabilityGrantSchema });
|
|
39209
|
+
var capabilityGrantListResponseSchema = external_exports.object({ data: external_exports.array(CapabilityGrantSchema) });
|
|
39210
|
+
var capabilityRouteResponseSchema = external_exports.object({ data: CapabilityRouteSchema });
|
|
39211
|
+
var capabilityRouteListResponseSchema = external_exports.object({ data: external_exports.array(CapabilityRouteSchema) });
|
|
39212
|
+
var capabilityToolClassResponseSchema = external_exports.object({ data: CapabilityToolClassSchema });
|
|
39213
|
+
var capabilityUseListResponseSchema = external_exports.object({ data: external_exports.array(CapabilityUseSchema) });
|
|
39044
39214
|
|
|
39045
39215
|
// ../../packages/agent-client/dist/providers/codex/render-context-bundle.js
|
|
39046
39216
|
var DEFAULT_SPEC_BUDGET_CHARS = 6e3;
|
|
@@ -39339,6 +39509,36 @@ function detectExhaustedProviderLimit(snapshot, nowMs) {
|
|
|
39339
39509
|
const resetAt = candidates.length > 0 ? candidates.reduce((a, b) => a > b ? a : b) : null;
|
|
39340
39510
|
return { exhausted: true, resetAt };
|
|
39341
39511
|
}
|
|
39512
|
+
var PROVIDER_LIMIT_ERROR_PATTERNS = [
|
|
39513
|
+
// OpenAI / Codex, verbatim from the observed event.
|
|
39514
|
+
/you'?ve hit your usage limit/i,
|
|
39515
|
+
// Anthropic phrasing for the same condition.
|
|
39516
|
+
/you have exceeded your (?:rate limit|usage limit)/i,
|
|
39517
|
+
/credit balance is too low/i,
|
|
39518
|
+
// START-ANCHORED quota wording. Round 2 split on this: the unanchored version was removed in
|
|
39519
|
+
// round 1 for matching task prose ("the quota is being exceeded by another worker"), and a round-2
|
|
39520
|
+
// reviewer then objected that removing it also drops real provider messages that LEAD with the
|
|
39521
|
+
// condition ("Monthly quota exceeded."). Anchoring resolves both — a provider error states the
|
|
39522
|
+
// condition first, prose embeds it — so the pattern is kept, but only at the head of the message.
|
|
39523
|
+
// Note this is a second line of defence either way: a provider that emits a structured
|
|
39524
|
+
// `rate_limits` snapshot is caught by detectExhaustedProviderLimit regardless of wording.
|
|
39525
|
+
// NOTE an unanchored `/usage limit reached/i` used to sit above, and it was the one real leak a
|
|
39526
|
+
// round-4 reviewer's finding pointed at (though not the example they gave): it matched
|
|
39527
|
+
// "[ERROR] memory usage limit reached", where the limit belongs to memory, not the provider. The
|
|
39528
|
+
// anchored pattern below already covers "Usage limit reached …", so the unanchored one was pure
|
|
39529
|
+
// false-positive surface and is gone.
|
|
39530
|
+
//
|
|
39531
|
+
// The optional prefix covers the log decorations providers actually ship with — "Error: ",
|
|
39532
|
+
// "[ERROR] ", "(fatal) " — without letting the condition sit anywhere in a sentence. The noun set
|
|
39533
|
+
// covers "rate limit"/"api limit" forms that carry no "quota" or "credits" at all.
|
|
39534
|
+
/^\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
|
|
39535
|
+
];
|
|
39536
|
+
function detectProviderLimitFromErrorText(text) {
|
|
39537
|
+
if (typeof text !== "string" || text.trim().length === 0)
|
|
39538
|
+
return null;
|
|
39539
|
+
const matched = PROVIDER_LIMIT_ERROR_PATTERNS.some((pattern) => pattern.test(text));
|
|
39540
|
+
return matched ? { exhausted: true, resetAt: null } : null;
|
|
39541
|
+
}
|
|
39342
39542
|
|
|
39343
39543
|
// ../../packages/agent-client/dist/providers/codex/adapter.js
|
|
39344
39544
|
var CodexProviderAdapter = class {
|
|
@@ -40855,6 +41055,18 @@ function createMrrlinClient(config2) {
|
|
|
40855
41055
|
throw error51;
|
|
40856
41056
|
}
|
|
40857
41057
|
},
|
|
41058
|
+
async listCapabilityGrants(projectSlug) {
|
|
41059
|
+
const body = await request(`${projectPath(projectSlug)}/capability-grants`);
|
|
41060
|
+
return capabilityGrantListResponseSchema.parse(body).data;
|
|
41061
|
+
},
|
|
41062
|
+
async listCapabilityUses(projectSlug) {
|
|
41063
|
+
const body = await request(`${projectPath(projectSlug)}/capability-uses`);
|
|
41064
|
+
return capabilityUseListResponseSchema.parse(body).data;
|
|
41065
|
+
},
|
|
41066
|
+
async revokeCapabilityGrant(projectSlug, serverId) {
|
|
41067
|
+
const body = await request(`${projectPath(projectSlug)}/capability-grants/${encodeURIComponent(serverId)}`, { method: "DELETE" });
|
|
41068
|
+
return capabilityGrantResponseSchema.parse(body).data;
|
|
41069
|
+
},
|
|
40858
41070
|
async listInboxItems(projectSlug, filters = {}) {
|
|
40859
41071
|
const url2 = appendQuery(`${projectPath(projectSlug)}/inbox`, {
|
|
40860
41072
|
groupId: filters.groupId,
|
|
@@ -40924,6 +41136,12 @@ function createMrrlinClient(config2) {
|
|
|
40924
41136
|
throw error51;
|
|
40925
41137
|
}
|
|
40926
41138
|
},
|
|
41139
|
+
async stopExecutionRun(projectSlug, runId) {
|
|
41140
|
+
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/stop`, {
|
|
41141
|
+
method: "POST"
|
|
41142
|
+
});
|
|
41143
|
+
return executionRunResponseSchema.parse(body).data;
|
|
41144
|
+
},
|
|
40927
41145
|
async requeueExecutionRun(projectSlug, runId, staleSeconds) {
|
|
40928
41146
|
try {
|
|
40929
41147
|
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/requeue`, {
|
|
@@ -41853,7 +42071,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41853
42071
|
}
|
|
41854
42072
|
|
|
41855
42073
|
// src/_generated/version.ts
|
|
41856
|
-
var PKG_VERSION = "0.4.
|
|
42074
|
+
var PKG_VERSION = "0.4.36";
|
|
41857
42075
|
|
|
41858
42076
|
// src/api-base-url-migrate.ts
|
|
41859
42077
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -45657,6 +45875,19 @@ async function findInboxQuestion(client, projectSlug, itemId) {
|
|
|
45657
45875
|
const items = await client.listInboxItems(projectSlug, { kind: "question" });
|
|
45658
45876
|
return items.find((item) => item.id === itemId) ?? null;
|
|
45659
45877
|
}
|
|
45878
|
+
async function cancelDeployGraceWindow(client, projectSlug, runId) {
|
|
45879
|
+
try {
|
|
45880
|
+
await client.decideInboxItem(projectSlug, graceCancelItemId(runId), {
|
|
45881
|
+
verdict: "rejected",
|
|
45882
|
+
decidedBy: "deploy-gate",
|
|
45883
|
+
reason: "operator_stopped"
|
|
45884
|
+
});
|
|
45885
|
+
return true;
|
|
45886
|
+
} catch (error51) {
|
|
45887
|
+
if (error51 instanceof MrrlinApiError && (error51.status === 404 || error51.status === 409)) return false;
|
|
45888
|
+
throw error51;
|
|
45889
|
+
}
|
|
45890
|
+
}
|
|
45660
45891
|
async function autonomyPaused(client) {
|
|
45661
45892
|
try {
|
|
45662
45893
|
const settings = await client.getOperatorSettings();
|
|
@@ -46515,10 +46746,31 @@ function executionTurnBudgetMessage(budgetMs) {
|
|
|
46515
46746
|
function isExecutionTurnTimeoutReason(reason) {
|
|
46516
46747
|
return reason.startsWith(EXECUTION_TURN_TIMEOUT_PREFIX) || reason.startsWith(EXECUTION_TURN_STALL_PREFIX);
|
|
46517
46748
|
}
|
|
46749
|
+
var PROVIDER_LIMIT_RETRY_CURSOR_PREFIX = "provider_limit_retry:";
|
|
46750
|
+
var PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS = 1;
|
|
46751
|
+
var PROVIDER_LIMIT_RETRY_CODE = "provider_limit_retry";
|
|
46752
|
+
var PROVIDER_LIMIT_PAUSE_REASON = "Agent provider usage limit exhausted during an autonomous execution run.";
|
|
46753
|
+
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.";
|
|
46754
|
+
function providerLimitRetryCursor(attempt) {
|
|
46755
|
+
return `${PROVIDER_LIMIT_RETRY_CURSOR_PREFIX}${attempt}`;
|
|
46756
|
+
}
|
|
46757
|
+
function providerLimitRetryAttempt(checkpointCursor) {
|
|
46758
|
+
if (!checkpointCursor || !checkpointCursor.startsWith(PROVIDER_LIMIT_RETRY_CURSOR_PREFIX)) return 0;
|
|
46759
|
+
const parsed = Number.parseInt(checkpointCursor.slice(PROVIDER_LIMIT_RETRY_CURSOR_PREFIX.length), 10);
|
|
46760
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
|
|
46761
|
+
}
|
|
46518
46762
|
function reportAutopilotLimitPause(client, reason, resetAt) {
|
|
46519
46763
|
void client.pauseAutopilotForLimit({ source: "provider_limit", reason, resetAt }).catch(() => {
|
|
46520
46764
|
});
|
|
46521
46765
|
}
|
|
46766
|
+
async function applyAutopilotLimitPause(client, reason, resetAt) {
|
|
46767
|
+
try {
|
|
46768
|
+
await client.pauseAutopilotForLimit({ source: "provider_limit", reason, resetAt });
|
|
46769
|
+
return true;
|
|
46770
|
+
} catch {
|
|
46771
|
+
return false;
|
|
46772
|
+
}
|
|
46773
|
+
}
|
|
46522
46774
|
function isDeliverableGateRetryableReason(reason) {
|
|
46523
46775
|
return DELIVERABLE_GATE_RETRYABLE_REASON_PREFIXES.some((prefix) => reason.startsWith(prefix));
|
|
46524
46776
|
}
|
|
@@ -46574,6 +46826,33 @@ var BASE_INSTRUCTIONS_PREFIX = [
|
|
|
46574
46826
|
"concrete progress via MCP tools rather than only narrating it.",
|
|
46575
46827
|
"",
|
|
46576
46828
|
"Completion contract (the run is gated on this):",
|
|
46829
|
+
"- Act now, in this turn, whenever the tools to do the work are in reach, the work does not",
|
|
46830
|
+
" depend on waiting for external state, and any irreversible outward action it takes is already",
|
|
46831
|
+
" covered by this turn's own recording obligation. The conveyor -- a scheduled follow-up task or",
|
|
46832
|
+
" plan -- is the exception, entered only for a build-and-test cycle, work still unfinished when",
|
|
46833
|
+
" the turn ends, or a decision only the operator can make. A task or plan exists to keep",
|
|
46834
|
+
" unfinished work from being lost, not to authorize doing it later when it could be done now.",
|
|
46835
|
+
"- Never file a clarifying question about anything the operator already answered in this thread.",
|
|
46836
|
+
" A complete instruction whose work is reachable in this turn gets done, then recorded -- it does",
|
|
46837
|
+
" not become a question.",
|
|
46838
|
+
// Final-gate finding (round 2, corrected after repo verification -- round 1's comment said
|
|
46839
|
+
// "unconditionally", which overstates it): this line tells the Director not to invoke
|
|
46840
|
+
// run_spec_consensus for such a task. Its OWN mark_spec_ready MCP tool (tools.ts) refuses to
|
|
46841
|
+
// flip spec_ready for the Director-LLM without a CONVERGED review UNLESS the project has
|
|
46842
|
+
// consensus disabled, in which case a DIFFERENT server-side path (the scheduler tick's
|
|
46843
|
+
// auto-promote under the consensus-off policy, tools.ts's own mark_spec_ready comment) can
|
|
46844
|
+
// promote it instead -- that is not a Director-invoked path either. The plain markSpecReady
|
|
46845
|
+
// API/client method (packages/client) has no CONVERGED-review requirement at all and IS directly
|
|
46846
|
+
// reachable (this is what seedApprovedTask in scripts/persona-smoke-agnostic-delivery.ts uses),
|
|
46847
|
+
// but it is not exposed to the Director as an MCP tool. So this instruction does not mean "the
|
|
46848
|
+
// Director marks it ready some other way" -- it means the Director stops short of spec_ready for
|
|
46849
|
+
// a pure form-filling task and leaves that promotion to whichever of the above actually applies
|
|
46850
|
+
// (an operator via the plain route, or the scheduler tick if consensus is disabled for the
|
|
46851
|
+
// project), consistent with such a task being too trivial to need Director-driven review in the
|
|
46852
|
+
// first place. declaredOutcome + the receipt gate (Group 2/3) are unaffected either way -- they
|
|
46853
|
+
// gate on specApproved, which is reachable once spec_ready is set by any caller.
|
|
46854
|
+
"- When a task's entire declared outcome is a submitted form, spec review is unreachable for it --",
|
|
46855
|
+
" do not route a pure form-filling task through spec review.",
|
|
46577
46856
|
"- Record evidence with `append_execution_artifact` against the run id shown in the",
|
|
46578
46857
|
' "## Open run" section of the context bundle.',
|
|
46579
46858
|
"- Artifact kinds that count as completion evidence are exactly: checkpoint, test_result, diff_summary, self_review, handoff_judgement.",
|
|
@@ -47985,13 +48264,20 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47985
48264
|
return;
|
|
47986
48265
|
}
|
|
47987
48266
|
}
|
|
48267
|
+
const receipt = parsedResult.outcome === "completed" && task.declaredOutcome != null ? buildReceipt({
|
|
48268
|
+
declaredOutcome: task.declaredOutcome,
|
|
48269
|
+
evidenceType: "url",
|
|
48270
|
+
evidenceValue: pageId,
|
|
48271
|
+
coveredKinds: ["page"]
|
|
48272
|
+
}) : void 0;
|
|
47988
48273
|
try {
|
|
47989
48274
|
const settled = await deps.client.settleExecutionRunOutcome(projectSlug, runId, {
|
|
47990
48275
|
leaseId,
|
|
47991
48276
|
outcome: parsedResult.outcome,
|
|
47992
48277
|
resultSummary: parsedResult.resultSummary,
|
|
47993
48278
|
deliverablePageId: pageId,
|
|
47994
|
-
contentHash
|
|
48279
|
+
contentHash,
|
|
48280
|
+
...receipt ? { receipt } : {}
|
|
47995
48281
|
});
|
|
47996
48282
|
if (!settled && await runHasLease(deps, projectSlug, runId, leaseId)) {
|
|
47997
48283
|
await failRun(
|
|
@@ -48027,6 +48313,62 @@ async function failRun(deps, projectSlug, task, runId, leaseId, reason, reasonCo
|
|
|
48027
48313
|
return;
|
|
48028
48314
|
}
|
|
48029
48315
|
if (currentRun.status !== "running" || currentRun.leaseId !== leaseId) return;
|
|
48316
|
+
let handoffReason = reason;
|
|
48317
|
+
let handoffReasonCode = reasonCode;
|
|
48318
|
+
const providerLimit = detectProviderLimitFromErrorText(reason);
|
|
48319
|
+
if (providerLimit) {
|
|
48320
|
+
const autopilotPaused = await applyAutopilotLimitPause(
|
|
48321
|
+
deps.client,
|
|
48322
|
+
PROVIDER_LIMIT_PAUSE_REASON,
|
|
48323
|
+
providerLimit.resetAt
|
|
48324
|
+
);
|
|
48325
|
+
const attempt = providerLimitRetryAttempt(currentRun.checkpointCursor) + 1;
|
|
48326
|
+
if (attempt <= PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS) {
|
|
48327
|
+
try {
|
|
48328
|
+
await deps.client.updateExecutionRun(projectSlug, runId, {
|
|
48329
|
+
status: "pending",
|
|
48330
|
+
leaseId: null,
|
|
48331
|
+
checkpointCursor: providerLimitRetryCursor(attempt)
|
|
48332
|
+
});
|
|
48333
|
+
await deps.client.appendExecutionArtifact(projectSlug, runId, {
|
|
48334
|
+
id: newArtifactId(),
|
|
48335
|
+
kind: "error",
|
|
48336
|
+
payload: {
|
|
48337
|
+
code: PROVIDER_LIMIT_RETRY_CODE,
|
|
48338
|
+
source: "execution-consumer",
|
|
48339
|
+
attempt,
|
|
48340
|
+
maxAttempts: PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS,
|
|
48341
|
+
// The provider's raw sentence belongs HERE — diagnostic detail on an error artifact —
|
|
48342
|
+
// and never in a handoff `reason`, which the DB trigger caches as the task's judgement.
|
|
48343
|
+
providerMessage: reason,
|
|
48344
|
+
// Whether the brake actually engaged. If it did not, the requeued run is only held
|
|
48345
|
+
// back by its own attempt budget, so this is the difference between "waiting for the
|
|
48346
|
+
// quota" and "about to fail once more"; it must be visible, not swallowed.
|
|
48347
|
+
autopilotPaused,
|
|
48348
|
+
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.`
|
|
48349
|
+
}
|
|
48350
|
+
}).catch(() => {
|
|
48351
|
+
});
|
|
48352
|
+
return;
|
|
48353
|
+
} catch {
|
|
48354
|
+
}
|
|
48355
|
+
}
|
|
48356
|
+
handoffReason = PROVIDER_LIMIT_HANDOFF_REASON;
|
|
48357
|
+
handoffReasonCode = "provider_limit_exhausted";
|
|
48358
|
+
await deps.client.appendExecutionArtifact(projectSlug, runId, {
|
|
48359
|
+
id: newArtifactId(),
|
|
48360
|
+
kind: "error",
|
|
48361
|
+
payload: {
|
|
48362
|
+
code: PROVIDER_LIMIT_RETRY_CODE,
|
|
48363
|
+
source: "execution-consumer",
|
|
48364
|
+
attempt: PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS,
|
|
48365
|
+
maxAttempts: PROVIDER_LIMIT_RETRY_MAX_ATTEMPTS,
|
|
48366
|
+
providerMessage: reason,
|
|
48367
|
+
summary: "Agent provider usage limit still exhausted after every retry; handing off to the operator."
|
|
48368
|
+
}
|
|
48369
|
+
}).catch(() => {
|
|
48370
|
+
});
|
|
48371
|
+
}
|
|
48030
48372
|
if (isExecutionTurnTimeoutReason(reason) && currentRun.checkpointCursor !== EXECUTION_TURN_TIMEOUT_RETRY_CURSOR) {
|
|
48031
48373
|
try {
|
|
48032
48374
|
await deps.client.updateExecutionRun(projectSlug, runId, {
|
|
@@ -48079,11 +48421,11 @@ async function failRun(deps, projectSlug, task, runId, leaseId, reason, reasonCo
|
|
|
48079
48421
|
}
|
|
48080
48422
|
try {
|
|
48081
48423
|
await deps.client.createExecutionRunHandoff(projectSlug, runId, {
|
|
48082
|
-
reason,
|
|
48083
|
-
...
|
|
48424
|
+
reason: handoffReason,
|
|
48425
|
+
...handoffReasonCode ? { reasonCode: handoffReasonCode } : {}
|
|
48084
48426
|
});
|
|
48085
48427
|
} catch {
|
|
48086
|
-
await notifyOperatorHandoff(deps, projectSlug, task, runId,
|
|
48428
|
+
await notifyOperatorHandoff(deps, projectSlug, task, runId, handoffReason);
|
|
48087
48429
|
}
|
|
48088
48430
|
}
|
|
48089
48431
|
async function reconcileTaskAwaitingMerge(deps, projectSlug, task, runId, reason) {
|
|
@@ -48218,6 +48560,12 @@ async function runBackstopSweep(deps, drive = (slug, id) => driveRun(deps, slug,
|
|
|
48218
48560
|
await drive(slug, run3.id);
|
|
48219
48561
|
}
|
|
48220
48562
|
if (deps.deployGate) {
|
|
48563
|
+
let autonomyPaused2 = false;
|
|
48564
|
+
try {
|
|
48565
|
+
autonomyPaused2 = (await deps.client.getOperatorSettings()).autonomyPaused === true;
|
|
48566
|
+
} catch {
|
|
48567
|
+
autonomyPaused2 = false;
|
|
48568
|
+
}
|
|
48221
48569
|
for (const status of ["dev_deploy", "prod_deploy"]) {
|
|
48222
48570
|
let inFlight = [];
|
|
48223
48571
|
try {
|
|
@@ -48229,6 +48577,10 @@ async function runBackstopSweep(deps, drive = (slug, id) => driveRun(deps, slug,
|
|
|
48229
48577
|
if (!isDeployGateRunStatus(run3.status)) continue;
|
|
48230
48578
|
try {
|
|
48231
48579
|
const task = await deps.client.getTask(slug, run3.taskId);
|
|
48580
|
+
if (autonomyPaused2 || mrrlinTerminalTaskStatuses.includes(task.status)) {
|
|
48581
|
+
await cancelDeployGraceWindow(deps.client, slug, run3.id).catch(() => {
|
|
48582
|
+
});
|
|
48583
|
+
}
|
|
48232
48584
|
const gateDeps = {
|
|
48233
48585
|
client: deps.client,
|
|
48234
48586
|
runVerificationTurn: deps.deployGate.runVerificationTurn,
|
|
@@ -48272,6 +48624,17 @@ async function runBackstopSweep(deps, drive = (slug, id) => driveRun(deps, slug,
|
|
|
48272
48624
|
}
|
|
48273
48625
|
}
|
|
48274
48626
|
}
|
|
48627
|
+
let stoppedRuns = [];
|
|
48628
|
+
try {
|
|
48629
|
+
stoppedRuns = await deps.client.listExecutionRuns(slug, { status: "handoff_requested" });
|
|
48630
|
+
} catch {
|
|
48631
|
+
stoppedRuns = [];
|
|
48632
|
+
}
|
|
48633
|
+
for (const run3 of stoppedRuns) {
|
|
48634
|
+
if (run3.blockingReason !== "operator_stopped") continue;
|
|
48635
|
+
await cancelDeployGraceWindow(deps.client, slug, run3.id).catch(() => {
|
|
48636
|
+
});
|
|
48637
|
+
}
|
|
48275
48638
|
}
|
|
48276
48639
|
}
|
|
48277
48640
|
}
|
|
@@ -48506,6 +48869,100 @@ function redactMessage(payload, opts) {
|
|
|
48506
48869
|
}
|
|
48507
48870
|
return { payload: redactValue(payload, 0, counter), redactedCount: counter.count };
|
|
48508
48871
|
}
|
|
48872
|
+
var OUTWARD_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
48873
|
+
"browser_click",
|
|
48874
|
+
"browser_type",
|
|
48875
|
+
"browser_fill_form",
|
|
48876
|
+
"browser_press_key",
|
|
48877
|
+
"browser_file_upload",
|
|
48878
|
+
"browser_select_option",
|
|
48879
|
+
"create_github_pull_request",
|
|
48880
|
+
"create_github_branch",
|
|
48881
|
+
"dispatch_github_workflow",
|
|
48882
|
+
"dispatch_task_deploy",
|
|
48883
|
+
"deliver_file",
|
|
48884
|
+
"record_produced_url"
|
|
48885
|
+
]);
|
|
48886
|
+
var READ_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
48887
|
+
"browser_navigate",
|
|
48888
|
+
"browser_snapshot",
|
|
48889
|
+
"get_execution_run",
|
|
48890
|
+
"get_github_installation_repositories",
|
|
48891
|
+
"get_project",
|
|
48892
|
+
"get_project_scheduler_policy",
|
|
48893
|
+
"get_project_snapshot",
|
|
48894
|
+
"get_scheduler_pending_tasks",
|
|
48895
|
+
"get_task",
|
|
48896
|
+
"get_wiki_page",
|
|
48897
|
+
"list_artifact_files",
|
|
48898
|
+
"list_execution_artifacts",
|
|
48899
|
+
"list_execution_runs",
|
|
48900
|
+
"list_github_installations",
|
|
48901
|
+
"list_github_workflows",
|
|
48902
|
+
"list_inbox_items",
|
|
48903
|
+
"list_plans",
|
|
48904
|
+
"list_project_members",
|
|
48905
|
+
"list_projects",
|
|
48906
|
+
"list_task_dependencies",
|
|
48907
|
+
"list_task_events",
|
|
48908
|
+
"list_tasks",
|
|
48909
|
+
"list_taxonomy",
|
|
48910
|
+
"list_wiki_pages",
|
|
48911
|
+
"retrieve_context",
|
|
48912
|
+
"search_knowledge",
|
|
48913
|
+
"search_wiki_pages",
|
|
48914
|
+
"create_task",
|
|
48915
|
+
"update_task",
|
|
48916
|
+
"create_wiki_page",
|
|
48917
|
+
"update_wiki_page",
|
|
48918
|
+
"create_inbox_item",
|
|
48919
|
+
// Group 4 checkpoint recommendation (claude-opus-5): stop_execution_run (added in Group 3, task
|
|
48920
|
+
// 3.3) is unclassified here otherwise, so it fail-closes to outward -- but its effect (parking a
|
|
48921
|
+
// run's status) is exactly as in-product/already-audited as the 5 writes above, and nothing would
|
|
48922
|
+
// ever resolve it (a stop naturally has no reason to be followed by a create_task/update_task
|
|
48923
|
+
// call), making every turn that stops a run permanently, unfixably "unresolved".
|
|
48924
|
+
"stop_execution_run"
|
|
48925
|
+
]);
|
|
48926
|
+
function normalizeToolName(toolName) {
|
|
48927
|
+
return toolName.replace(/^mcp__.+?__/, "");
|
|
48928
|
+
}
|
|
48929
|
+
var MRRLIN_MCP_NAMESPACE_PREFIXES = ["mcp__mrrlin__", "mcp__mrrlin-browser__"];
|
|
48930
|
+
function classifyToolEffect(rawToolName) {
|
|
48931
|
+
const toolName = normalizeToolName(rawToolName);
|
|
48932
|
+
if (READ_TOOL_NAMES.has(toolName)) return "read";
|
|
48933
|
+
if (OUTWARD_TOOL_NAMES.has(toolName)) return "outward";
|
|
48934
|
+
if (MRRLIN_MCP_NAMESPACE_PREFIXES.some((prefix) => rawToolName.startsWith(prefix))) return "outward";
|
|
48935
|
+
return "read";
|
|
48936
|
+
}
|
|
48937
|
+
var BROWSER_OUTWARD_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
48938
|
+
"browser_click",
|
|
48939
|
+
"browser_type",
|
|
48940
|
+
"browser_fill_form",
|
|
48941
|
+
"browser_press_key",
|
|
48942
|
+
"browser_file_upload",
|
|
48943
|
+
"browser_select_option"
|
|
48944
|
+
]);
|
|
48945
|
+
var IN_PRODUCT_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set(["create_task", "update_task", "create_wiki_page", "update_wiki_page", "create_inbox_item"]);
|
|
48946
|
+
function resolveTurnRecordingObligationEnabled(env = process.env) {
|
|
48947
|
+
const raw = (env.MRRLIN_TURN_RECORDING_OBLIGATION ?? "").trim().toLowerCase();
|
|
48948
|
+
return !(raw === "0" || raw === "false");
|
|
48949
|
+
}
|
|
48950
|
+
function buildBrowserAttemptReceipt(toolName) {
|
|
48951
|
+
return {
|
|
48952
|
+
kind: "attempt",
|
|
48953
|
+
evidence: { type: "human_tap", value: toolName },
|
|
48954
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48955
|
+
requiresCovered: []
|
|
48956
|
+
};
|
|
48957
|
+
}
|
|
48958
|
+
function extractSeqAndTurnId(payload) {
|
|
48959
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return { seq: void 0, turnId: void 0 };
|
|
48960
|
+
const p = payload;
|
|
48961
|
+
return {
|
|
48962
|
+
seq: typeof p.seq === "number" ? p.seq : void 0,
|
|
48963
|
+
turnId: typeof p.turnId === "string" ? p.turnId : void 0
|
|
48964
|
+
};
|
|
48965
|
+
}
|
|
48509
48966
|
function resolveBridgeLogEnabled(env = process.env) {
|
|
48510
48967
|
const raw = (env.MRRLIN_DIRECTOR_BRIDGE_LOG ?? "").trim().toLowerCase();
|
|
48511
48968
|
return !(raw === "0" || raw === "false");
|
|
@@ -48540,6 +48997,29 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48540
48997
|
if (!oldest.done) contextSlugBySession.delete(oldest.value);
|
|
48541
48998
|
}
|
|
48542
48999
|
};
|
|
49000
|
+
const turnRecordingObligationEnabled = resolveTurnRecordingObligationEnabled(env);
|
|
49001
|
+
const obligationBySession = /* @__PURE__ */ new Map();
|
|
49002
|
+
const getObligationState = (sessionId) => {
|
|
49003
|
+
let state = obligationBySession.get(sessionId);
|
|
49004
|
+
if (!state) {
|
|
49005
|
+
state = { pending: [], lastSeq: null, lastTurnId: null };
|
|
49006
|
+
} else {
|
|
49007
|
+
obligationBySession.delete(sessionId);
|
|
49008
|
+
}
|
|
49009
|
+
obligationBySession.set(sessionId, state);
|
|
49010
|
+
if (obligationBySession.size > MAX_TRACKED_SESSIONS) {
|
|
49011
|
+
const oldest = obligationBySession.keys().next();
|
|
49012
|
+
if (!oldest.done) obligationBySession.delete(oldest.value);
|
|
49013
|
+
}
|
|
49014
|
+
return state;
|
|
49015
|
+
};
|
|
49016
|
+
const checkSequenceGap = (state, turnId, seq) => {
|
|
49017
|
+
const isNewTurn = turnId === void 0 || turnId !== state.lastTurnId;
|
|
49018
|
+
const isGap = !isNewTurn && state.lastSeq !== null && seq !== state.lastSeq + 1;
|
|
49019
|
+
state.lastSeq = seq;
|
|
49020
|
+
state.lastTurnId = turnId ?? state.lastTurnId;
|
|
49021
|
+
return { isGap, isNewTurn };
|
|
49022
|
+
};
|
|
48543
49023
|
const gainDay = (sessionId) => {
|
|
48544
49024
|
const sessionsPath = (0, import_node_path16.join)(logsDir, ".sessions.json");
|
|
48545
49025
|
let sessionsObj = {};
|
|
@@ -48586,6 +49066,48 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48586
49066
|
);
|
|
48587
49067
|
}
|
|
48588
49068
|
}
|
|
49069
|
+
let sequenceGap;
|
|
49070
|
+
let syntheticReceipt;
|
|
49071
|
+
let recordingObligationUnresolved;
|
|
49072
|
+
let recordingObligationMultiResolve;
|
|
49073
|
+
if (turnRecordingObligationEnabled && rec.sessionId !== null && dir === "out") {
|
|
49074
|
+
const state = getObligationState(rec.sessionId);
|
|
49075
|
+
const { seq, turnId } = extractSeqAndTurnId(rec.payload);
|
|
49076
|
+
if (seq !== void 0) {
|
|
49077
|
+
const { isGap, isNewTurn } = checkSequenceGap(state, turnId, seq);
|
|
49078
|
+
if (isGap) sequenceGap = true;
|
|
49079
|
+
if (isNewTurn && state.pending.length > 0) recordingObligationUnresolved = state.pending;
|
|
49080
|
+
if (isNewTurn) state.pending = [];
|
|
49081
|
+
}
|
|
49082
|
+
if (projection?.kind === "tool_use" && typeof projection.toolName === "string" && typeof projection.toolId === "string") {
|
|
49083
|
+
const toolName = normalizeToolName(projection.toolName);
|
|
49084
|
+
if (classifyToolEffect(projection.toolName) === "outward") {
|
|
49085
|
+
if (BROWSER_OUTWARD_TOOL_NAMES.has(toolName)) {
|
|
49086
|
+
syntheticReceipt = buildBrowserAttemptReceipt(toolName);
|
|
49087
|
+
} else {
|
|
49088
|
+
state.pending.push({
|
|
49089
|
+
turnSpanId: projection.turnId,
|
|
49090
|
+
toolName,
|
|
49091
|
+
toolId: projection.toolId,
|
|
49092
|
+
seq: projection.seq,
|
|
49093
|
+
armedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49094
|
+
});
|
|
49095
|
+
}
|
|
49096
|
+
} else if (IN_PRODUCT_WRITE_TOOL_NAMES.has(toolName) && state.pending.length > 0) {
|
|
49097
|
+
if (state.pending.length > 1) {
|
|
49098
|
+
recordingObligationMultiResolve = {
|
|
49099
|
+
resolvedCount: state.pending.length,
|
|
49100
|
+
toolNames: state.pending.map((p) => p.toolName)
|
|
49101
|
+
};
|
|
49102
|
+
}
|
|
49103
|
+
state.pending = [];
|
|
49104
|
+
}
|
|
49105
|
+
}
|
|
49106
|
+
if (projection?.kind === "turn_lifecycle") {
|
|
49107
|
+
if (state.pending.length > 0) recordingObligationUnresolved = state.pending;
|
|
49108
|
+
state.pending = [];
|
|
49109
|
+
}
|
|
49110
|
+
}
|
|
48589
49111
|
const { payload: redactedPayload, redactedCount } = redactMessage(rec.payload, { includePrompts });
|
|
48590
49112
|
const line = JSON.stringify({
|
|
48591
49113
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -48598,7 +49120,11 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
48598
49120
|
redactedCount,
|
|
48599
49121
|
projection,
|
|
48600
49122
|
...projectionFailed ? { projectionFailed: true } : {},
|
|
48601
|
-
...projectScopeMismatch ? { projectScopeMismatch } : {}
|
|
49123
|
+
...projectScopeMismatch ? { projectScopeMismatch } : {},
|
|
49124
|
+
...sequenceGap ? { sequenceGap } : {},
|
|
49125
|
+
...syntheticReceipt ? { syntheticReceipt } : {},
|
|
49126
|
+
...recordingObligationUnresolved ? { recordingObligationUnresolved } : {},
|
|
49127
|
+
...recordingObligationMultiResolve ? { recordingObligationMultiResolve } : {}
|
|
48602
49128
|
}) + "\n";
|
|
48603
49129
|
(0, import_node_fs8.appendFileSync)(fileFor(), line);
|
|
48604
49130
|
if (rec.sessionId !== null) gainDay(rec.sessionId);
|
|
@@ -62415,6 +62941,7 @@ var mcpToolNames = {
|
|
|
62415
62941
|
listArtifactFiles: "list_artifact_files",
|
|
62416
62942
|
reclaimExecutionRunLease: "reclaim_execution_run_lease",
|
|
62417
62943
|
restartExecutionRun: "restart_execution_run",
|
|
62944
|
+
stopExecutionRun: "stop_execution_run",
|
|
62418
62945
|
resolveHandoff: "resolve_handoff",
|
|
62419
62946
|
recordProducedUrl: "record_produced_url",
|
|
62420
62947
|
updateTaskState: "update_task_state",
|
|
@@ -63046,6 +63573,7 @@ var listArtifactFilesInputSchema = projectScopedSchema.merge(
|
|
|
63046
63573
|
);
|
|
63047
63574
|
var reclaimExecutionRunLeaseInputSchema = projectScopedSchema.extend({ runId: mrrlinExecutionRunIdSchema }).merge(mrrlinExecutionRunRequeueSchema);
|
|
63048
63575
|
var restartExecutionRunInputSchema = projectScopedSchema.extend({ runId: mrrlinExecutionRunIdSchema }).merge(mrrlinRestartExecutionRunSchema);
|
|
63576
|
+
var stopExecutionRunInputSchema = projectScopedSchema.extend({ runId: mrrlinExecutionRunIdSchema });
|
|
63049
63577
|
var listInboxItemsInputSchema = projectScopedSchema.merge(mrrlinInboxItemListFiltersSchema);
|
|
63050
63578
|
var createInboxItemInputSchema = projectScopedSchema.merge(mrrlinInboxItemCreateSchema);
|
|
63051
63579
|
var decideInboxItemInputSchema = projectScopedSchema.extend({ itemId: mrrlinInboxItemIdSchema }).merge(mrrlinInboxItemDecideSchema);
|
|
@@ -63128,6 +63656,7 @@ var mcpToolInputSchemas = {
|
|
|
63128
63656
|
[mcpToolNames.listArtifactFiles]: listArtifactFilesInputSchema,
|
|
63129
63657
|
[mcpToolNames.reclaimExecutionRunLease]: reclaimExecutionRunLeaseInputSchema,
|
|
63130
63658
|
[mcpToolNames.restartExecutionRun]: restartExecutionRunInputSchema,
|
|
63659
|
+
[mcpToolNames.stopExecutionRun]: stopExecutionRunInputSchema,
|
|
63131
63660
|
[mcpToolNames.resolveHandoff]: resolveHandoffInputSchema,
|
|
63132
63661
|
[mcpToolNames.recordProducedUrl]: recordProducedUrlInputSchema,
|
|
63133
63662
|
[mcpToolNames.updateTaskState]: updateTaskStateInputSchema,
|
|
@@ -63197,6 +63726,7 @@ var mcpToolOperationIds = {
|
|
|
63197
63726
|
[mcpToolNames.listArtifactFiles]: openApiOperationIds.listArtifactFiles,
|
|
63198
63727
|
[mcpToolNames.reclaimExecutionRunLease]: openApiOperationIds.reclaimExecutionRunLease,
|
|
63199
63728
|
[mcpToolNames.restartExecutionRun]: openApiOperationIds.restartExecutionRun,
|
|
63729
|
+
[mcpToolNames.stopExecutionRun]: openApiOperationIds.stopExecutionRun,
|
|
63200
63730
|
[mcpToolNames.resolveHandoff]: openApiOperationIds.resolveHandoff
|
|
63201
63731
|
};
|
|
63202
63732
|
var mcpToolDescriptions = {
|
|
@@ -63267,6 +63797,7 @@ var mcpToolDescriptions = {
|
|
|
63267
63797
|
[mcpToolNames.listArtifactFiles]: "List uploaded artifact files for the project (optionally filtered by taskId/runId), newest first.",
|
|
63268
63798
|
[mcpToolNames.reclaimExecutionRunLease]: "Clear a stale pending execution-run lease when the lease has aged past staleSeconds. Returns the reclaimed run on success, null when the lease is not stale yet, and surfaces a structured conflict when reclaim retries are exhausted.",
|
|
63269
63799
|
[mcpToolNames.restartExecutionRun]: "Restart a parked execution run (handoff_requested or handoff_queued) back to pending so the queue can resume it.",
|
|
63800
|
+
[mcpToolNames.stopExecutionRun]: "Operator stop: park an in-flight execution run at handoff_requested with blockingReason operator_stopped. Parks rather than cancels, so restart_execution_run can resume it later. Refuses (409) a run already at a terminal status.",
|
|
63270
63801
|
[mcpToolNames.resolveHandoff]: "Resolve a tool_assisted handoff for an execution run (outcome success|failure). Success returns the task to the board; failure opens a new handoff cycle. Idempotent.",
|
|
63271
63802
|
[mcpToolNames.registerLocalCheckout]: "Register the operator-local git checkout path for `projectSlug` so execution-run workers run code on this machine instead of the cloud. Validates the path is a git repo whose `origin` remote matches `repo`, then writes the (slug -> absolute path) entry to the operator-local checkout registry. Call this after locating the checkout on disk (e.g. via filesystem search) and confirming the choice with the user in chat \u2014 they should NOT have to paste the path into Settings. Returns `{ projectSlug, path, confirmedAt }`. Idempotent: re-registering overwrites the prior entry.",
|
|
63272
63803
|
[mcpToolNames.retrieveContext]: "Rehydrate a mrrlin:// handle emitted by the compact-context layer into the full canonical Mrrlin record (task/wiki page/project snapshot/execution run/artifacts). Use whenever you need the full source \u2014 irreversible decisions (closing tasks, dispatching deploys, writing specs) MUST call this first; never act on a compact envelope alone. Returns `{ kind, data }`. Invalid or unsupported handle \u2192 structured `HANDLE_INVALID` error."
|
|
@@ -64166,6 +64697,13 @@ function createMrrlinTools(options) {
|
|
|
64166
64697
|
"Unable to restart the execution run.",
|
|
64167
64698
|
async (c, { projectSlug, runId, ...input }) => await c.restartExecutionRun(projectSlug, runId, input)
|
|
64168
64699
|
),
|
|
64700
|
+
[mcpToolNames.stopExecutionRun]: makeTool(
|
|
64701
|
+
client,
|
|
64702
|
+
mcpToolNames.stopExecutionRun,
|
|
64703
|
+
"EXECUTION_RUN_STOP_FAILED",
|
|
64704
|
+
"Unable to stop the execution run.",
|
|
64705
|
+
async (c, { projectSlug, runId }) => await c.stopExecutionRun(projectSlug, runId)
|
|
64706
|
+
),
|
|
64169
64707
|
[mcpToolNames.resolveHandoff]: makeTool(
|
|
64170
64708
|
client,
|
|
64171
64709
|
mcpToolNames.resolveHandoff,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.36",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -22,10 +22,10 @@
|
|
|
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",
|
|
25
26
|
"@mrrlin/director-e2e": "0.0.0",
|
|
26
27
|
"@mrrlin/schemas": "0.0.0",
|
|
27
28
|
"@mrrlin/tsconfig": "0.0.0",
|
|
28
|
-
"@mrrlin/agent-client": "0.0.0",
|
|
29
29
|
"@mrrlin/wiki": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|