@mrrlin-dev/mcp 0.4.5 → 0.4.6
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 +961 -187
- package/package.json +2 -2
package/dist/bin.cjs
CHANGED
|
@@ -20645,7 +20645,7 @@ var require_dist = __commonJS({
|
|
|
20645
20645
|
|
|
20646
20646
|
// src/bin.ts
|
|
20647
20647
|
var import_node_path25 = __toESM(require("node:path"), 1);
|
|
20648
|
-
var
|
|
20648
|
+
var import_node_child_process18 = require("node:child_process");
|
|
20649
20649
|
var import_node_fs17 = require("node:fs");
|
|
20650
20650
|
var import_node_os15 = require("node:os");
|
|
20651
20651
|
var import_node_path26 = __toESM(require("node:path"), 1);
|
|
@@ -36801,6 +36801,8 @@ var openApiOperationIds = {
|
|
|
36801
36801
|
getTaskState: "getTaskState",
|
|
36802
36802
|
upsertTaskState: "upsertTaskState",
|
|
36803
36803
|
retireThread: "retireThread",
|
|
36804
|
+
applyIntentVerdict: "applyIntentVerdict",
|
|
36805
|
+
armIntentCheck: "armIntentCheck",
|
|
36804
36806
|
// Run-scoped, model-facing half of the resume contract (D8). Distinct from upsertTaskState:
|
|
36805
36807
|
// that one is the server's own write (whole record, explicit lastRunSeq), this one is the
|
|
36806
36808
|
// executor reporting narrative only, with the ordering value derived server-side from the run.
|
|
@@ -37035,7 +37037,10 @@ var mrrlinAsyncJobSchema = external_exports.object({
|
|
|
37035
37037
|
directorSessionId: external_exports.string().nullable().default(null),
|
|
37036
37038
|
createdAt: external_exports.string(),
|
|
37037
37039
|
finishedAt: external_exports.string().nullable().default(null),
|
|
37038
|
-
updatedAt: external_exports.string()
|
|
37040
|
+
updatedAt: external_exports.string(),
|
|
37041
|
+
// model-tier-planning-and-arbiter task 1.8: the seat minted at claim (or inherited from a
|
|
37042
|
+
// prior attempt on this same lease); NULL only before the first beginAttempt call pins it.
|
|
37043
|
+
agentSeat: external_exports.enum(["codex", "claude"]).nullable().default(null)
|
|
37039
37044
|
}).strict();
|
|
37040
37045
|
var asyncJobResponseSchema = external_exports.object({ data: mrrlinAsyncJobSchema });
|
|
37041
37046
|
var asyncJobMaybeResponseSchema = external_exports.object({ data: mrrlinAsyncJobSchema.nullable() });
|
|
@@ -37052,7 +37057,10 @@ var mrrlinAsyncJobTouchSchema = external_exports.object({
|
|
|
37052
37057
|
});
|
|
37053
37058
|
var mrrlinAsyncJobBeginAttemptSchema = external_exports.object({
|
|
37054
37059
|
leaseId: external_exports.string().min(1),
|
|
37055
|
-
requestedHash: external_exports.string().min(1)
|
|
37060
|
+
requestedHash: external_exports.string().min(1),
|
|
37061
|
+
// task 1.8: the caller has already resolved (inherited or minted) a seat for this job by the
|
|
37062
|
+
// time it begins an attempt — required, not optional, so persistence can never be skipped.
|
|
37063
|
+
agentSeat: external_exports.enum(["codex", "claude"])
|
|
37056
37064
|
});
|
|
37057
37065
|
var mrrlinAsyncJobTerminalizeSchema = external_exports.object({
|
|
37058
37066
|
leaseId: external_exports.string().min(1),
|
|
@@ -37338,6 +37346,7 @@ var mrrlinStopReasonSchema = external_exports.enum([
|
|
|
37338
37346
|
"spec_draft_failed",
|
|
37339
37347
|
"spec_not_specs_folder",
|
|
37340
37348
|
"awaiting_clarification",
|
|
37349
|
+
"intent_unconfirmed",
|
|
37341
37350
|
"consensus_blocked",
|
|
37342
37351
|
"consensus_run_failed",
|
|
37343
37352
|
"consensus_disabled_spec_not_ready",
|
|
@@ -37379,6 +37388,11 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
37379
37388
|
followUpAt: external_exports.string().datetime().nullable(),
|
|
37380
37389
|
id: mrrlinTaskIdSchema,
|
|
37381
37390
|
impact: mrrlinTaskImpactSchema,
|
|
37391
|
+
// early-intent-confirmation: written by the intent-verdict route on either verdict; the
|
|
37392
|
+
// restatement the judge showed the operator, and when the task was last checked. Output-optional
|
|
37393
|
+
// so existing task fixtures need no churn; DB rows always resolve both to `string | null`.
|
|
37394
|
+
intentCheckedAt: external_exports.string().datetime().nullable().optional(),
|
|
37395
|
+
intentRestatement: external_exports.string().nullable().optional(),
|
|
37382
37396
|
latestJudgement: external_exports.string().nullable().default(null),
|
|
37383
37397
|
latestJudgementAt: external_exports.string().datetime().nullable().default(null),
|
|
37384
37398
|
latestJudgementKind: external_exports.enum(["autonomy", "handoff"]).nullable().default(null),
|
|
@@ -37413,6 +37427,12 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
37413
37427
|
updatedAt: external_exports.string().datetime().optional(),
|
|
37414
37428
|
verificationDueAt: external_exports.string().datetime().nullable()
|
|
37415
37429
|
});
|
|
37430
|
+
var mrrlinTaskSummarySchema = mrrlinTaskSchema.omit({
|
|
37431
|
+
notes: true,
|
|
37432
|
+
operatorSeedRequest: true,
|
|
37433
|
+
intentRestatement: true,
|
|
37434
|
+
latestJudgement: true
|
|
37435
|
+
});
|
|
37416
37436
|
var mrrlinPlanSchema = external_exports.object({
|
|
37417
37437
|
closedTaskCount: external_exports.number().int().nonnegative(),
|
|
37418
37438
|
// ADR 0020 Phase 2 (decision 7): plan-scoped specs resolve their consensus domain through
|
|
@@ -37539,14 +37559,19 @@ var mrrlinProjectSummarySchema = external_exports.object({
|
|
|
37539
37559
|
slug: mrrlinProjectSlugSchema,
|
|
37540
37560
|
// ADR 0020: default consensus domain for this project's plan/project-scoped specs. Output-optional
|
|
37541
37561
|
// (DB column is NOT NULL default 'code') so existing project fixtures/upsert callers need no churn.
|
|
37542
|
-
defaultDomain: mrrlinTaskDomainSchema.optional()
|
|
37562
|
+
defaultDomain: mrrlinTaskDomainSchema.optional(),
|
|
37563
|
+
// model-tier-planning-and-arbiter D9: per-project judge switch. NULL/true means the judge runs
|
|
37564
|
+
// as today; false means intent-check.ts skips it entirely. Output-optional for the same reason
|
|
37565
|
+
// defaultDomain is: existing project fixtures/upsert callers need no churn.
|
|
37566
|
+
intentJudgeEnabled: external_exports.boolean().optional()
|
|
37543
37567
|
});
|
|
37544
37568
|
var mrrlinProjectUpsertSchema = mrrlinProjectSummarySchema.omit({
|
|
37545
37569
|
onboardingKickoffState: true,
|
|
37546
37570
|
slug: true
|
|
37547
37571
|
});
|
|
37548
37572
|
var mrrlinProjectRenameSchema = external_exports.object({
|
|
37549
|
-
displayName: external_exports.string().trim().min(1).max(120)
|
|
37573
|
+
displayName: external_exports.string().trim().min(1).max(120).optional(),
|
|
37574
|
+
intentJudgeEnabled: external_exports.boolean().optional()
|
|
37550
37575
|
});
|
|
37551
37576
|
var ONBOARDING_KICKOFF_TRANSITIONS = [
|
|
37552
37577
|
{ from: null, to: "pending" },
|
|
@@ -37601,7 +37626,7 @@ var mrrlinTaskCreateShapeSchema = external_exports.object({
|
|
|
37601
37626
|
category: taxonomyCategorySchema,
|
|
37602
37627
|
dueDate: external_exports.string().date().nullable().optional(),
|
|
37603
37628
|
followUpAt: external_exports.string().datetime().nullable().optional(),
|
|
37604
|
-
id: mrrlinTaskIdSchema,
|
|
37629
|
+
id: mrrlinTaskIdSchema.optional(),
|
|
37605
37630
|
impact: mrrlinTaskImpactSchema.default("medium"),
|
|
37606
37631
|
notes: external_exports.string().nullable().optional(),
|
|
37607
37632
|
parentTaskId: mrrlinTaskIdSchema.nullable().optional(),
|
|
@@ -37721,6 +37746,7 @@ var mrrlinTaskListFiltersSchema = external_exports.object({
|
|
|
37721
37746
|
subcategory: taxonomySubcategorySchema.optional(),
|
|
37722
37747
|
type: mrrlinTaskTypeSchema.optional()
|
|
37723
37748
|
});
|
|
37749
|
+
var mrrlinTaskListViewSchema = external_exports.enum(["summary"]);
|
|
37724
37750
|
var apiErrorSchema = external_exports.object({
|
|
37725
37751
|
code: external_exports.string().min(1),
|
|
37726
37752
|
message: external_exports.string().min(1)
|
|
@@ -37950,6 +37976,9 @@ var taskResponseSchema = external_exports.object({
|
|
|
37950
37976
|
var taskListResponseSchema = external_exports.object({
|
|
37951
37977
|
data: external_exports.array(mrrlinTaskSchema)
|
|
37952
37978
|
});
|
|
37979
|
+
var taskSummaryListResponseSchema = external_exports.object({
|
|
37980
|
+
data: external_exports.array(mrrlinTaskSummarySchema)
|
|
37981
|
+
});
|
|
37953
37982
|
var planResponseSchema = external_exports.object({
|
|
37954
37983
|
data: mrrlinPlanSchema
|
|
37955
37984
|
});
|
|
@@ -37995,6 +38024,7 @@ var mrrlinRunPrerequisiteSchema = external_exports.object({
|
|
|
37995
38024
|
});
|
|
37996
38025
|
var mrrlinExecutionRunSchema = external_exports.object({
|
|
37997
38026
|
actor: external_exports.string().min(1),
|
|
38027
|
+
agentSeat: external_exports.enum(["codex", "claude"]).nullable(),
|
|
37998
38028
|
baseSha: external_exports.string().min(1).nullable(),
|
|
37999
38029
|
branch: external_exports.string().min(1).nullable(),
|
|
38000
38030
|
blockingReason: mrrlinRunBlockingReasonCodeSchema.nullable(),
|
|
@@ -38092,6 +38122,7 @@ var mrrlinExecutionRunCreateSchema = external_exports.object({
|
|
|
38092
38122
|
worktreeId: external_exports.string().min(1).optional()
|
|
38093
38123
|
});
|
|
38094
38124
|
var mrrlinExecutionRunUpdateSchema = external_exports.object({
|
|
38125
|
+
agentSeat: external_exports.enum(["codex", "claude"]).nullable().optional(),
|
|
38095
38126
|
baseSha: external_exports.string().min(1).nullable().optional(),
|
|
38096
38127
|
branch: external_exports.string().min(1).nullable().optional(),
|
|
38097
38128
|
blockingReason: mrrlinRunBlockingReasonCodeSchema.nullable().optional(),
|
|
@@ -38635,7 +38666,8 @@ var mrrlinInboxItemSchema = external_exports.object({
|
|
|
38635
38666
|
readAt: external_exports.string().datetime().nullable(),
|
|
38636
38667
|
reason: external_exports.string().nullable().optional(),
|
|
38637
38668
|
status: mrrlinInboxItemStatusSchema,
|
|
38638
|
-
taskId: mrrlinTaskIdSchema.nullable()
|
|
38669
|
+
taskId: mrrlinTaskIdSchema.nullable(),
|
|
38670
|
+
taskTitle: external_exports.string().nullable()
|
|
38639
38671
|
}).superRefine((value, context) => {
|
|
38640
38672
|
if (value.kind !== "question" && value.questionOptions !== null) {
|
|
38641
38673
|
context.addIssue({
|
|
@@ -38686,6 +38718,26 @@ var mrrlinInboxItemListFiltersSchema = external_exports.object({
|
|
|
38686
38718
|
status: mrrlinInboxItemStatusSchema.optional(),
|
|
38687
38719
|
taskId: mrrlinTaskIdSchema.optional()
|
|
38688
38720
|
});
|
|
38721
|
+
var mrrlinIntentVerdictSchema = external_exports.object({
|
|
38722
|
+
verdict: external_exports.enum(["CLEAR", "UNCLEAR"]),
|
|
38723
|
+
restatement: external_exports.string(),
|
|
38724
|
+
questions: external_exports.array(external_exports.string()).max(3)
|
|
38725
|
+
}).superRefine((value, context) => {
|
|
38726
|
+
if (value.verdict === "CLEAR" && value.questions.length !== 0) {
|
|
38727
|
+
context.addIssue({
|
|
38728
|
+
code: external_exports.ZodIssueCode.custom,
|
|
38729
|
+
message: "a CLEAR verdict must carry zero questions",
|
|
38730
|
+
path: ["questions"]
|
|
38731
|
+
});
|
|
38732
|
+
}
|
|
38733
|
+
if (value.verdict === "UNCLEAR" && value.questions.length === 0) {
|
|
38734
|
+
context.addIssue({
|
|
38735
|
+
code: external_exports.ZodIssueCode.custom,
|
|
38736
|
+
message: "an UNCLEAR verdict must carry at least one question",
|
|
38737
|
+
path: ["questions"]
|
|
38738
|
+
});
|
|
38739
|
+
}
|
|
38740
|
+
});
|
|
38689
38741
|
var inboxItemReadResponseSchema = external_exports.object({
|
|
38690
38742
|
data: external_exports.object({
|
|
38691
38743
|
readAt: external_exports.string().datetime()
|
|
@@ -39559,9 +39611,12 @@ ${input.prompt}` : input.prompt;
|
|
|
39559
39611
|
"--sandbox",
|
|
39560
39612
|
sandbox,
|
|
39561
39613
|
"--skip-git-repo-check",
|
|
39562
|
-
"--ignore-user-config"
|
|
39563
|
-
fullPrompt
|
|
39614
|
+
"--ignore-user-config"
|
|
39564
39615
|
];
|
|
39616
|
+
if (input.model) {
|
|
39617
|
+
args.push("-m", input.model);
|
|
39618
|
+
}
|
|
39619
|
+
args.push(fullPrompt);
|
|
39565
39620
|
return new Promise((resolve) => {
|
|
39566
39621
|
let settled = false;
|
|
39567
39622
|
const timerRef = { id: void 0 };
|
|
@@ -39636,6 +39691,9 @@ function runClaudeExec(input, deps) {
|
|
|
39636
39691
|
if (trimmedInstructions) {
|
|
39637
39692
|
args.push("--append-system-prompt", trimmedInstructions);
|
|
39638
39693
|
}
|
|
39694
|
+
if (input.model) {
|
|
39695
|
+
args.push("--model", input.model);
|
|
39696
|
+
}
|
|
39639
39697
|
args.push(input.prompt);
|
|
39640
39698
|
return new Promise((resolve) => {
|
|
39641
39699
|
let settled = false;
|
|
@@ -39733,6 +39791,98 @@ function runAgentExec(input, deps, persistedProvider) {
|
|
|
39733
39791
|
throw new Error(`not-yet-wired: ${provider.id}`);
|
|
39734
39792
|
}
|
|
39735
39793
|
|
|
39794
|
+
// ../../packages/agent-client/dist/resolved-seat.js
|
|
39795
|
+
var CODEX_STRONG_MODEL_CONSTANT = "gpt-5.5";
|
|
39796
|
+
var CLAUDE_STRONG_MODEL_CONSTANT = "opus";
|
|
39797
|
+
var CODEX_WEAK_MODEL_CONSTANT = "codex-gpt-5.4-mini";
|
|
39798
|
+
var CLAUDE_WEAK_MODEL_CONSTANT = "haiku";
|
|
39799
|
+
var CODEX_STRONG_MODEL_ENV = "MRRLIN_CODEX_STRONG_MODEL";
|
|
39800
|
+
var CLAUDE_STRONG_MODEL_ENV = "MRRLIN_CLAUDE_STRONG_MODEL";
|
|
39801
|
+
function logFallback(providerId, tier, source, value) {
|
|
39802
|
+
console.error(`[resolved-seat] provider=${providerId} ${tier}=${value} (source: ${source})`);
|
|
39803
|
+
}
|
|
39804
|
+
function pickStrongModelFromList(models) {
|
|
39805
|
+
const defaultModel = models.find((m) => m.isDefault === true);
|
|
39806
|
+
if (defaultModel)
|
|
39807
|
+
return defaultModel.id;
|
|
39808
|
+
const nonHidden = models.find((m) => m.hidden !== true);
|
|
39809
|
+
if (nonHidden)
|
|
39810
|
+
return nonHidden.id;
|
|
39811
|
+
return models[0]?.id ?? null;
|
|
39812
|
+
}
|
|
39813
|
+
function pickWeakModelFromList(models, strongModel) {
|
|
39814
|
+
const miniModel = models.find((m) => {
|
|
39815
|
+
const id = m.id.toLowerCase();
|
|
39816
|
+
return id.includes("mini") || id.includes("weak") || id.includes("small");
|
|
39817
|
+
});
|
|
39818
|
+
if (miniModel && miniModel.id !== strongModel)
|
|
39819
|
+
return miniModel.id;
|
|
39820
|
+
return null;
|
|
39821
|
+
}
|
|
39822
|
+
async function mintResolvedSeat(provider, opts) {
|
|
39823
|
+
const capabilities = provider.capabilities();
|
|
39824
|
+
const providerId = provider.id;
|
|
39825
|
+
let strongModel;
|
|
39826
|
+
let weakModel;
|
|
39827
|
+
if (opts?.handle && capabilities.listsModels) {
|
|
39828
|
+
try {
|
|
39829
|
+
const models = await opts.handle.listModels();
|
|
39830
|
+
const picked = pickStrongModelFromList(models);
|
|
39831
|
+
if (picked) {
|
|
39832
|
+
strongModel = picked;
|
|
39833
|
+
} else {
|
|
39834
|
+
throw new Error("empty model list");
|
|
39835
|
+
}
|
|
39836
|
+
} catch {
|
|
39837
|
+
const envValue = process.env[providerId === "codex" ? CODEX_STRONG_MODEL_ENV : CLAUDE_STRONG_MODEL_ENV]?.trim();
|
|
39838
|
+
if (envValue) {
|
|
39839
|
+
strongModel = envValue;
|
|
39840
|
+
logFallback(providerId, "strong", "env", strongModel);
|
|
39841
|
+
} else {
|
|
39842
|
+
strongModel = providerId === "codex" ? CODEX_STRONG_MODEL_CONSTANT : CLAUDE_STRONG_MODEL_CONSTANT;
|
|
39843
|
+
logFallback(providerId, "strong", "constant", strongModel);
|
|
39844
|
+
}
|
|
39845
|
+
}
|
|
39846
|
+
} else {
|
|
39847
|
+
const envValue = process.env[providerId === "codex" ? CODEX_STRONG_MODEL_ENV : CLAUDE_STRONG_MODEL_ENV]?.trim();
|
|
39848
|
+
if (envValue) {
|
|
39849
|
+
strongModel = envValue;
|
|
39850
|
+
logFallback(providerId, "strong", "env", strongModel);
|
|
39851
|
+
} else {
|
|
39852
|
+
strongModel = providerId === "codex" ? CODEX_STRONG_MODEL_CONSTANT : CLAUDE_STRONG_MODEL_CONSTANT;
|
|
39853
|
+
logFallback(providerId, "strong", "constant", strongModel);
|
|
39854
|
+
}
|
|
39855
|
+
}
|
|
39856
|
+
if (providerId === "claude") {
|
|
39857
|
+
weakModel = CLAUDE_WEAK_MODEL_CONSTANT;
|
|
39858
|
+
logFallback(providerId, "weak", "constant", weakModel);
|
|
39859
|
+
} else {
|
|
39860
|
+
if (opts?.handle && capabilities.listsModels) {
|
|
39861
|
+
try {
|
|
39862
|
+
const models = await opts.handle.listModels();
|
|
39863
|
+
const picked = pickWeakModelFromList(models, strongModel);
|
|
39864
|
+
if (picked) {
|
|
39865
|
+
weakModel = picked;
|
|
39866
|
+
} else {
|
|
39867
|
+
weakModel = strongModel;
|
|
39868
|
+
logFallback(providerId, "weak", "degenerate (weak=strong)", weakModel);
|
|
39869
|
+
}
|
|
39870
|
+
} catch {
|
|
39871
|
+
weakModel = CODEX_WEAK_MODEL_CONSTANT;
|
|
39872
|
+
logFallback(providerId, "weak", "constant", weakModel);
|
|
39873
|
+
}
|
|
39874
|
+
} else {
|
|
39875
|
+
weakModel = CODEX_WEAK_MODEL_CONSTANT;
|
|
39876
|
+
logFallback(providerId, "weak", "constant", weakModel);
|
|
39877
|
+
}
|
|
39878
|
+
}
|
|
39879
|
+
return {
|
|
39880
|
+
id: providerId,
|
|
39881
|
+
capabilities,
|
|
39882
|
+
models: { strong: strongModel, weak: weakModel }
|
|
39883
|
+
};
|
|
39884
|
+
}
|
|
39885
|
+
|
|
39736
39886
|
// ../../packages/client/dist/agent-token-provider.js
|
|
39737
39887
|
var AgentTokenProviderError = class extends Error {
|
|
39738
39888
|
code;
|
|
@@ -39806,6 +39956,25 @@ function appendQuery(url2, params) {
|
|
|
39806
39956
|
const qs = search.toString();
|
|
39807
39957
|
return qs.length > 0 ? `${url2}?${qs}` : url2;
|
|
39808
39958
|
}
|
|
39959
|
+
function projectPath(slug) {
|
|
39960
|
+
return `/projects/${encodeURIComponent(slug)}`;
|
|
39961
|
+
}
|
|
39962
|
+
function buildTaskListPath(projectSlug, filters, view) {
|
|
39963
|
+
return appendQuery(`${projectPath(projectSlug)}/tasks`, {
|
|
39964
|
+
archived: filters.archived,
|
|
39965
|
+
assignee: filters.assignee,
|
|
39966
|
+
autonomyLevel: filters.autonomyLevel,
|
|
39967
|
+
blockedBy: filters.blockedBy,
|
|
39968
|
+
category: filters.category,
|
|
39969
|
+
dueDate: filters.dueDate,
|
|
39970
|
+
impact: filters.impact,
|
|
39971
|
+
q: filters.q,
|
|
39972
|
+
status: filters.status,
|
|
39973
|
+
subcategory: filters.subcategory,
|
|
39974
|
+
type: filters.type,
|
|
39975
|
+
view
|
|
39976
|
+
});
|
|
39977
|
+
}
|
|
39809
39978
|
var MrrlinApiError = class extends Error {
|
|
39810
39979
|
code;
|
|
39811
39980
|
method;
|
|
@@ -39868,9 +40037,6 @@ function createMrrlinClient(config2) {
|
|
|
39868
40037
|
}
|
|
39869
40038
|
return response.json();
|
|
39870
40039
|
}
|
|
39871
|
-
function projectPath(slug) {
|
|
39872
|
-
return `/projects/${encodeURIComponent(slug)}`;
|
|
39873
|
-
}
|
|
39874
40040
|
return {
|
|
39875
40041
|
async listGithubInstallations() {
|
|
39876
40042
|
const body = await request(`/github/installations`);
|
|
@@ -40117,23 +40283,23 @@ function createMrrlinClient(config2) {
|
|
|
40117
40283
|
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/events`);
|
|
40118
40284
|
return taskEventListResponseSchema.parse(body).data;
|
|
40119
40285
|
},
|
|
40120
|
-
|
|
40121
|
-
|
|
40122
|
-
|
|
40123
|
-
|
|
40124
|
-
|
|
40125
|
-
|
|
40126
|
-
category: filters.category,
|
|
40127
|
-
dueDate: filters.dueDate,
|
|
40128
|
-
impact: filters.impact,
|
|
40129
|
-
q: filters.q,
|
|
40130
|
-
status: filters.status,
|
|
40131
|
-
subcategory: filters.subcategory,
|
|
40132
|
-
type: filters.type
|
|
40133
|
-
});
|
|
40286
|
+
// An object-literal method can't satisfy two overloads with distinct return types the way a
|
|
40287
|
+
// real function overload's implementation signature can (verified: a union return type is
|
|
40288
|
+
// rejected against the first, MrrlinTask[]-only overload) -- the `any` here is contained to
|
|
40289
|
+
// this one implementation signature; MrrlinClient's declared overloads keep callers precise.
|
|
40290
|
+
async listTasks(projectSlug, filters = {}, options) {
|
|
40291
|
+
const path21 = buildTaskListPath(projectSlug, filters, options?.view);
|
|
40134
40292
|
const body = await request(path21);
|
|
40293
|
+
if (options?.view === "summary") {
|
|
40294
|
+
return taskSummaryListResponseSchema.parse(body).data;
|
|
40295
|
+
}
|
|
40135
40296
|
return taskListResponseSchema.parse(body).data;
|
|
40136
40297
|
},
|
|
40298
|
+
async listTaskSummaries(projectSlug, filters = {}) {
|
|
40299
|
+
const path21 = buildTaskListPath(projectSlug, filters, "summary");
|
|
40300
|
+
const body = await request(path21);
|
|
40301
|
+
return taskSummaryListResponseSchema.parse(body).data;
|
|
40302
|
+
},
|
|
40137
40303
|
async listTaxonomy(projectSlug) {
|
|
40138
40304
|
const body = await request(`${projectPath(projectSlug)}/taxonomy`);
|
|
40139
40305
|
return taxonomyListResponseSchema.parse(body).data;
|
|
@@ -40311,10 +40477,10 @@ function createMrrlinClient(config2) {
|
|
|
40311
40477
|
throw error51;
|
|
40312
40478
|
}
|
|
40313
40479
|
},
|
|
40314
|
-
async beginAsyncJobAttempt(projectSlug, jobId, leaseId, requestedHash) {
|
|
40480
|
+
async beginAsyncJobAttempt(projectSlug, jobId, leaseId, requestedHash, agentSeat) {
|
|
40315
40481
|
try {
|
|
40316
40482
|
const body = await request(`${projectPath(projectSlug)}/async-jobs/${encodeURIComponent(jobId)}/begin-attempt`, {
|
|
40317
|
-
body: JSON.stringify({ leaseId, requestedHash }),
|
|
40483
|
+
body: JSON.stringify({ leaseId, requestedHash, agentSeat }),
|
|
40318
40484
|
headers: { "content-type": "application/json" },
|
|
40319
40485
|
method: "POST"
|
|
40320
40486
|
});
|
|
@@ -40492,6 +40658,18 @@ function createMrrlinClient(config2) {
|
|
|
40492
40658
|
});
|
|
40493
40659
|
return retireThreadResponseSchema.parse(body).data.retiredThreadId;
|
|
40494
40660
|
},
|
|
40661
|
+
async armIntentCheck(projectSlug, taskId) {
|
|
40662
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/intent-check-arm`, { method: "POST" });
|
|
40663
|
+
return taskResponseSchema.parse(body).data;
|
|
40664
|
+
},
|
|
40665
|
+
async applyIntentVerdict(projectSlug, taskId, verdict) {
|
|
40666
|
+
const body = await request(`${projectPath(projectSlug)}/tasks/${encodeURIComponent(taskId)}/intent-verdict`, {
|
|
40667
|
+
body: JSON.stringify(mrrlinIntentVerdictSchema.parse(verdict)),
|
|
40668
|
+
headers: { "content-type": "application/json" },
|
|
40669
|
+
method: "POST"
|
|
40670
|
+
});
|
|
40671
|
+
return taskResponseSchema.parse(body).data;
|
|
40672
|
+
},
|
|
40495
40673
|
async reportTaskState(projectSlug, runId, report) {
|
|
40496
40674
|
const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/task-state`, {
|
|
40497
40675
|
body: JSON.stringify(mrrlinTaskStateReportSchema.parse(report)),
|
|
@@ -41222,7 +41400,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41222
41400
|
}
|
|
41223
41401
|
|
|
41224
41402
|
// src/_generated/version.ts
|
|
41225
|
-
var PKG_VERSION = "0.4.
|
|
41403
|
+
var PKG_VERSION = "0.4.6";
|
|
41226
41404
|
|
|
41227
41405
|
// src/api-base-url-migrate.ts
|
|
41228
41406
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -42188,18 +42366,40 @@ function parseArbiterDecisions(raw) {
|
|
|
42188
42366
|
}
|
|
42189
42367
|
|
|
42190
42368
|
// src/consensus/arbiter.ts
|
|
42191
|
-
function buildArbiterPrompt(issues) {
|
|
42192
|
-
const
|
|
42193
|
-
|
|
42194
|
-
|
|
42195
|
-
|
|
42196
|
-
|
|
42369
|
+
function buildArbiterPrompt(issues, artifact, panelComposition) {
|
|
42370
|
+
const compositionLine = `Panel composition \u2013 requested: ${panelComposition.requested}, filled: ${panelComposition.filled}, underpowered: ${panelComposition.underpowered}`;
|
|
42371
|
+
const issueList = issues.map((i) => `- [${i.id}] (${i.category}) ${i.text}`).join("\n");
|
|
42372
|
+
const instruction = `You may add a line starting with "PANEL_UNDERPOWERED:" followed by a one\u2011sentence citation of what the panel likely missed given its size. If you add the line, provide a non\u2011empty citation; otherwise omit the line entirely.`;
|
|
42373
|
+
return [
|
|
42374
|
+
instruction,
|
|
42375
|
+
"",
|
|
42376
|
+
"Artifact:",
|
|
42377
|
+
artifact,
|
|
42378
|
+
"",
|
|
42379
|
+
compositionLine,
|
|
42380
|
+
"",
|
|
42381
|
+
"ISSUES:",
|
|
42382
|
+
issueList
|
|
42383
|
+
].join("\n");
|
|
42384
|
+
}
|
|
42385
|
+
function parsePanelDeclaration(raw) {
|
|
42386
|
+
const lines2 = raw.split(/\r?\n/);
|
|
42387
|
+
for (const line of lines2) {
|
|
42388
|
+
const m = line.match(/^\s*[-*]?\s*PANEL_UNDERPOWERED:\s*(.*)$/i);
|
|
42389
|
+
if (m) {
|
|
42390
|
+
const citation = (m[1] ?? "").trim();
|
|
42391
|
+
if (citation.length === 0) {
|
|
42392
|
+
return { kind: "underpowered_rejected_uncited" };
|
|
42393
|
+
}
|
|
42394
|
+
return { kind: "underpowered", citation };
|
|
42395
|
+
}
|
|
42396
|
+
}
|
|
42397
|
+
return { kind: "not_declared" };
|
|
42197
42398
|
}
|
|
42198
|
-
async function adjudicate(issues, deps) {
|
|
42199
|
-
if (issues.length === 0) return { adjudicatedVerdict: "APPROVE", accepted: [], dismissed: [] };
|
|
42399
|
+
async function adjudicate(issues, artifact, panelComposition, deps) {
|
|
42200
42400
|
const byId = new Map(issues.map((i) => [i.id, i]));
|
|
42201
42401
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
42202
|
-
const res = await deps.runArbiter(buildArbiterPrompt(issues));
|
|
42402
|
+
const res = await deps.runArbiter(buildArbiterPrompt(issues, artifact, panelComposition));
|
|
42203
42403
|
if (!res.ok) continue;
|
|
42204
42404
|
const { decisions } = parseArbiterDecisions(res.text);
|
|
42205
42405
|
const seen = new Set(decisions.map((d) => d.id));
|
|
@@ -42214,12 +42414,28 @@ async function adjudicate(issues, deps) {
|
|
|
42214
42414
|
accepted.push(i);
|
|
42215
42415
|
continue;
|
|
42216
42416
|
}
|
|
42217
|
-
if (d.decision === "accept")
|
|
42218
|
-
|
|
42417
|
+
if (d.decision === "accept") {
|
|
42418
|
+
accepted.push(i);
|
|
42419
|
+
} else {
|
|
42420
|
+
dismissed.push({ id: i.id, decision: d.decision, reason: d.reason });
|
|
42421
|
+
}
|
|
42219
42422
|
}
|
|
42220
|
-
|
|
42423
|
+
const panelDeclaration = parsePanelDeclaration(res.text);
|
|
42424
|
+
return {
|
|
42425
|
+
adjudicatedVerdict: accepted.length === 0 ? "APPROVE" : "REQUEST_CHANGES",
|
|
42426
|
+
accepted,
|
|
42427
|
+
dismissed,
|
|
42428
|
+
arbiterInfo: { unavailable: null },
|
|
42429
|
+
panelDeclaration
|
|
42430
|
+
};
|
|
42221
42431
|
}
|
|
42222
|
-
return {
|
|
42432
|
+
return {
|
|
42433
|
+
adjudicatedVerdict: "REQUEST_CHANGES",
|
|
42434
|
+
accepted: [...byId.values()],
|
|
42435
|
+
dismissed: [],
|
|
42436
|
+
arbiterInfo: { unavailable: { reason: "retry-exhausted" } },
|
|
42437
|
+
panelDeclaration: { kind: "not_declared" }
|
|
42438
|
+
};
|
|
42223
42439
|
}
|
|
42224
42440
|
|
|
42225
42441
|
// src/consensus/semaphore.ts
|
|
@@ -42277,6 +42493,11 @@ function diagnoseStall(history, configuredRounds) {
|
|
|
42277
42493
|
}
|
|
42278
42494
|
|
|
42279
42495
|
// src/consensus/loop.ts
|
|
42496
|
+
function sufficiencyRank(v) {
|
|
42497
|
+
if (v.verdict === "proceeded_underpowered") return 2;
|
|
42498
|
+
if (v.verdict === "retried") return 1;
|
|
42499
|
+
return 0;
|
|
42500
|
+
}
|
|
42280
42501
|
async function runConsensus(input) {
|
|
42281
42502
|
const sem = createBoundedSemaphore(input.concurrency ?? input.heads.length);
|
|
42282
42503
|
const history = [];
|
|
@@ -42286,9 +42507,24 @@ async function runConsensus(input) {
|
|
|
42286
42507
|
let effectiveRounds = input.rounds;
|
|
42287
42508
|
let escalated = false;
|
|
42288
42509
|
let decision;
|
|
42510
|
+
let composition = input.panelComposition;
|
|
42511
|
+
let currentRunHead = input.runHead;
|
|
42512
|
+
let repickAttempted = false;
|
|
42513
|
+
let latestArbiterInfo = null;
|
|
42514
|
+
let worstSufficiency = { verdict: "sufficient" };
|
|
42289
42515
|
for (let round = 1; round <= effectiveRounds; round++) {
|
|
42290
42516
|
if (calls + input.heads.length > input.maxHeadCalls) {
|
|
42291
|
-
return {
|
|
42517
|
+
return {
|
|
42518
|
+
outcome: "CONSENSUS_BLOCKED",
|
|
42519
|
+
rounds: round - 1,
|
|
42520
|
+
finalArtifact: artifact,
|
|
42521
|
+
history,
|
|
42522
|
+
unresolved: history[history.length - 1]?.accepted ?? [],
|
|
42523
|
+
arbiterInfo: null,
|
|
42524
|
+
panelComposition: composition,
|
|
42525
|
+
sufficiencyVerdict: worstSufficiency,
|
|
42526
|
+
...decision ? { escalation: decision } : {}
|
|
42527
|
+
};
|
|
42292
42528
|
}
|
|
42293
42529
|
const dismissedBlock = dismissedFwd.length ? `PREVIOUSLY DISMISSED (do not re-raise unless new information):
|
|
42294
42530
|
${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
@@ -42297,7 +42533,7 @@ ${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
|
42297
42533
|
await sem.acquire();
|
|
42298
42534
|
try {
|
|
42299
42535
|
calls++;
|
|
42300
|
-
const res = await
|
|
42536
|
+
const res = await currentRunHead(persona, artifact, dismissedBlock);
|
|
42301
42537
|
return { idx, res };
|
|
42302
42538
|
} catch (e) {
|
|
42303
42539
|
return { idx, res: { ok: false, error: String(e) } };
|
|
@@ -42310,7 +42546,16 @@ ${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
|
42310
42546
|
(r) => r.res.ok
|
|
42311
42547
|
);
|
|
42312
42548
|
if (responding.length === 0) {
|
|
42313
|
-
return {
|
|
42549
|
+
return {
|
|
42550
|
+
outcome: "CONSENSUS_UNAVAILABLE",
|
|
42551
|
+
rounds: round,
|
|
42552
|
+
finalArtifact: artifact,
|
|
42553
|
+
history,
|
|
42554
|
+
unresolved: [],
|
|
42555
|
+
arbiterInfo: null,
|
|
42556
|
+
panelComposition: composition,
|
|
42557
|
+
sufficiencyVerdict: worstSufficiency
|
|
42558
|
+
};
|
|
42314
42559
|
}
|
|
42315
42560
|
const headOutcomes = input.heads.map((persona) => ({
|
|
42316
42561
|
persona,
|
|
@@ -42333,24 +42578,68 @@ ${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
|
42333
42578
|
});
|
|
42334
42579
|
}
|
|
42335
42580
|
}
|
|
42581
|
+
if (calls + 1 > input.maxHeadCalls) {
|
|
42582
|
+
return {
|
|
42583
|
+
outcome: "CONSENSUS_BLOCKED",
|
|
42584
|
+
rounds: round,
|
|
42585
|
+
finalArtifact: artifact,
|
|
42586
|
+
history,
|
|
42587
|
+
unresolved: history[history.length - 1]?.accepted ?? [],
|
|
42588
|
+
arbiterInfo: { unavailable: { reason: "budget-exhausted" } },
|
|
42589
|
+
panelComposition: composition,
|
|
42590
|
+
sufficiencyVerdict: worstSufficiency,
|
|
42591
|
+
...decision ? { escalation: decision } : {}
|
|
42592
|
+
};
|
|
42593
|
+
}
|
|
42594
|
+
calls++;
|
|
42336
42595
|
let adj;
|
|
42337
|
-
|
|
42338
|
-
|
|
42339
|
-
|
|
42340
|
-
|
|
42341
|
-
|
|
42342
|
-
|
|
42343
|
-
|
|
42344
|
-
|
|
42345
|
-
|
|
42596
|
+
try {
|
|
42597
|
+
adj = await adjudicate(issues, artifact, composition, { runArbiter: input.runArbiter });
|
|
42598
|
+
} catch (e) {
|
|
42599
|
+
adj = {
|
|
42600
|
+
adjudicatedVerdict: "REQUEST_CHANGES",
|
|
42601
|
+
accepted: issues,
|
|
42602
|
+
dismissed: [],
|
|
42603
|
+
arbiterInfo: { unavailable: { reason: "thrown-exception", detail: String(e) } },
|
|
42604
|
+
panelDeclaration: { kind: "not_declared" }
|
|
42605
|
+
};
|
|
42606
|
+
}
|
|
42607
|
+
latestArbiterInfo = adj.arbiterInfo;
|
|
42608
|
+
let thisRoundSufficiency;
|
|
42609
|
+
if (adj.panelDeclaration.kind === "underpowered") {
|
|
42610
|
+
if (!repickAttempted && input.repickPanel) {
|
|
42611
|
+
repickAttempted = true;
|
|
42612
|
+
const rp = await input.repickPanel();
|
|
42613
|
+
if (rp.ok && rp.composition.filled > composition.filled) {
|
|
42614
|
+
composition = rp.composition;
|
|
42615
|
+
currentRunHead = rp.runHead;
|
|
42616
|
+
thisRoundSufficiency = { verdict: "retried", citation: adj.panelDeclaration.citation };
|
|
42617
|
+
} else {
|
|
42618
|
+
thisRoundSufficiency = { verdict: "proceeded_underpowered", citation: adj.panelDeclaration.citation };
|
|
42619
|
+
}
|
|
42620
|
+
} else {
|
|
42621
|
+
thisRoundSufficiency = { verdict: "proceeded_underpowered", citation: adj.panelDeclaration.citation };
|
|
42346
42622
|
}
|
|
42347
42623
|
} else {
|
|
42348
|
-
|
|
42624
|
+
thisRoundSufficiency = { verdict: "sufficient" };
|
|
42625
|
+
}
|
|
42626
|
+
if (sufficiencyRank(thisRoundSufficiency) >= sufficiencyRank(worstSufficiency)) {
|
|
42627
|
+
worstSufficiency = thisRoundSufficiency;
|
|
42349
42628
|
}
|
|
42350
42629
|
history.push({ round, accepted: adj.accepted, dismissed: adj.dismissed, headOutcomes });
|
|
42351
42630
|
adj.dismissed.forEach((d) => dismissedFwd.push(`- ${d.id}: ${d.reason}`));
|
|
42352
42631
|
if (adj.accepted.length === 0) {
|
|
42353
|
-
return {
|
|
42632
|
+
return {
|
|
42633
|
+
outcome: "CONVERGED",
|
|
42634
|
+
rounds: round,
|
|
42635
|
+
finalArtifact: artifact,
|
|
42636
|
+
history,
|
|
42637
|
+
unresolved: [],
|
|
42638
|
+
arbiterInfo: latestArbiterInfo,
|
|
42639
|
+
panelComposition: composition,
|
|
42640
|
+
sufficiencyVerdict: worstSufficiency,
|
|
42641
|
+
...decision ? { escalation: decision } : {}
|
|
42642
|
+
};
|
|
42354
42643
|
}
|
|
42355
42644
|
if (round === effectiveRounds) {
|
|
42356
42645
|
if (!decision) {
|
|
@@ -42360,12 +42649,31 @@ ${dismissedFwd.slice(-20).join("\n")}` : "";
|
|
|
42360
42649
|
escalated = true;
|
|
42361
42650
|
effectiveRounds = Math.min(effectiveRounds + decision.grantedRounds, input.rounds * 2);
|
|
42362
42651
|
} else {
|
|
42363
|
-
return {
|
|
42652
|
+
return {
|
|
42653
|
+
outcome: "CONSENSUS_BLOCKED",
|
|
42654
|
+
rounds: round,
|
|
42655
|
+
finalArtifact: artifact,
|
|
42656
|
+
history,
|
|
42657
|
+
unresolved: history[history.length - 1]?.accepted ?? [],
|
|
42658
|
+
arbiterInfo: latestArbiterInfo,
|
|
42659
|
+
panelComposition: composition,
|
|
42660
|
+
sufficiencyVerdict: worstSufficiency,
|
|
42661
|
+
...decision ? { escalation: decision } : {}
|
|
42662
|
+
};
|
|
42364
42663
|
}
|
|
42365
42664
|
}
|
|
42366
42665
|
artifact = await input.revise(artifact, adj.accepted);
|
|
42367
42666
|
}
|
|
42368
|
-
return {
|
|
42667
|
+
return {
|
|
42668
|
+
outcome: "CONSENSUS_BLOCKED",
|
|
42669
|
+
rounds: effectiveRounds,
|
|
42670
|
+
finalArtifact: artifact,
|
|
42671
|
+
history,
|
|
42672
|
+
unresolved: history[history.length - 1]?.accepted ?? [],
|
|
42673
|
+
arbiterInfo: latestArbiterInfo,
|
|
42674
|
+
panelComposition: composition,
|
|
42675
|
+
sufficiencyVerdict: worstSufficiency
|
|
42676
|
+
};
|
|
42369
42677
|
}
|
|
42370
42678
|
|
|
42371
42679
|
// src/consensus/spec-identity.ts
|
|
@@ -42525,7 +42833,8 @@ function applyClarificationCap(issues, impact) {
|
|
|
42525
42833
|
async function runSpecGate(_target, deps, opts) {
|
|
42526
42834
|
if (!await deps.consensusEnabled()) return { outcome: "SKIPPED" };
|
|
42527
42835
|
const intent = await deps.readIntent();
|
|
42528
|
-
|
|
42836
|
+
const legacyExempt = intent.specApproved === "skipped_legacy";
|
|
42837
|
+
if (!legacyExempt && intent.taskId !== null && (intent.seed === null || intent.seed.trim() === "")) {
|
|
42529
42838
|
return {
|
|
42530
42839
|
outcome: "CONSENSUS_BLOCKED",
|
|
42531
42840
|
operatorClarifications: [
|
|
@@ -42592,6 +42901,7 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
42592
42901
|
heads,
|
|
42593
42902
|
rounds: deps.rounds,
|
|
42594
42903
|
maxHeadCalls: deps.maxHeadCalls,
|
|
42904
|
+
panelComposition: deps.panelComposition ?? { requested: heads.length, filled: 1, underpowered: heads.length > 1 },
|
|
42595
42905
|
runHead: deps.runHead,
|
|
42596
42906
|
runArbiter: deps.runArbiter,
|
|
42597
42907
|
revise: async (artifact, accepted) => {
|
|
@@ -42635,7 +42945,10 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
42635
42945
|
whyBlocking: "converged spec content belongs to a different task than the one it gates",
|
|
42636
42946
|
category: "scope"
|
|
42637
42947
|
}
|
|
42638
|
-
]
|
|
42948
|
+
],
|
|
42949
|
+
arbiterInfo: result.arbiterInfo,
|
|
42950
|
+
panelComposition: result.panelComposition,
|
|
42951
|
+
sufficiencyVerdict: result.sufficiencyVerdict
|
|
42639
42952
|
};
|
|
42640
42953
|
}
|
|
42641
42954
|
let keptClarifications = [];
|
|
@@ -42657,7 +42970,13 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
42657
42970
|
});
|
|
42658
42971
|
if (result.outcome === "CONVERGED") {
|
|
42659
42972
|
await deps.promoteSpecReady(currentHash);
|
|
42660
|
-
return {
|
|
42973
|
+
return {
|
|
42974
|
+
outcome: "CONVERGED",
|
|
42975
|
+
headOutcomes: result.history.at(-1)?.headOutcomes ?? [],
|
|
42976
|
+
arbiterInfo: result.arbiterInfo,
|
|
42977
|
+
panelComposition: result.panelComposition,
|
|
42978
|
+
sufficiencyVerdict: result.sufficiencyVerdict
|
|
42979
|
+
};
|
|
42661
42980
|
}
|
|
42662
42981
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
42663
42982
|
return {
|
|
@@ -42665,10 +42984,18 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
42665
42984
|
...keptClarifications.length > 0 ? { operatorClarifications: keptClarifications } : {},
|
|
42666
42985
|
...droppedByCap.length > 0 ? { droppedByCap } : {},
|
|
42667
42986
|
...result.escalation ? { escalation: result.escalation } : {},
|
|
42668
|
-
unresolved: result.unresolved
|
|
42987
|
+
unresolved: result.unresolved,
|
|
42988
|
+
arbiterInfo: result.arbiterInfo,
|
|
42989
|
+
panelComposition: result.panelComposition,
|
|
42990
|
+
sufficiencyVerdict: result.sufficiencyVerdict
|
|
42669
42991
|
};
|
|
42670
42992
|
}
|
|
42671
|
-
return {
|
|
42993
|
+
return {
|
|
42994
|
+
outcome: result.outcome,
|
|
42995
|
+
arbiterInfo: result.arbiterInfo,
|
|
42996
|
+
panelComposition: result.panelComposition,
|
|
42997
|
+
sufficiencyVerdict: result.sufficiencyVerdict
|
|
42998
|
+
};
|
|
42672
42999
|
}
|
|
42673
43000
|
|
|
42674
43001
|
// src/consensus/resolve-gated-task.ts
|
|
@@ -43027,6 +43354,15 @@ ${adjudicated.map((q) => `- Q: ${q.body}
|
|
|
43027
43354
|
` : "";
|
|
43028
43355
|
return `${identity}${intent}${impactLine}${adjudicatedBlock}`;
|
|
43029
43356
|
}
|
|
43357
|
+
function selectAdjudicated(items) {
|
|
43358
|
+
return items.filter(
|
|
43359
|
+
(item) => item.origin === "gate" && (item.status === "acknowledged" || item.status === "rejected") && item.decidedBy !== "gate"
|
|
43360
|
+
).map((item) => ({
|
|
43361
|
+
body: item.body,
|
|
43362
|
+
answer: item.answer,
|
|
43363
|
+
status: item.status
|
|
43364
|
+
}));
|
|
43365
|
+
}
|
|
43030
43366
|
function reviserIdentityInstructions(taskId, title) {
|
|
43031
43367
|
const lock = taskId ? `This spec gates task ${taskId}${title ? ` ("${title}")` : ""}. You MUST NOT change the spec's task identity or heading, retarget it to a different task id, or invert its direction (e.g. live-submission vs preparation-only). Preserve the leading \`task: ${taskId}\` frontmatter EXACTLY; never change it to another task id. Address ONLY the accepted issues.
|
|
43032
43368
|
|
|
@@ -43034,7 +43370,7 @@ function reviserIdentityInstructions(taskId, title) {
|
|
|
43034
43370
|
return `${lock}You revise a spec to address accepted issues. Return ONLY the full revised markdown, no commentary.`;
|
|
43035
43371
|
}
|
|
43036
43372
|
function buildSpecGateDeps(args) {
|
|
43037
|
-
const { client, projectSlug, specPageId, cwd, codexExecutable, extraHeadCount = 0 } = args;
|
|
43373
|
+
const { client, projectSlug, specPageId, cwd, codexExecutable, extraHeadCount = 0, seat } = args;
|
|
43038
43374
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
43039
43375
|
const maxHeadCalls = 2 * rounds * (4 + extraHeadCount + 1);
|
|
43040
43376
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
@@ -43072,13 +43408,7 @@ function buildSpecGateDeps(args) {
|
|
|
43072
43408
|
}
|
|
43073
43409
|
}
|
|
43074
43410
|
}
|
|
43075
|
-
const adjudicated = taskInboxItems
|
|
43076
|
-
(item) => item.origin === "gate" && (item.status === "acknowledged" || item.status === "rejected") && item.decidedBy !== "gate"
|
|
43077
|
-
).map((item) => ({
|
|
43078
|
-
body: item.body,
|
|
43079
|
-
answer: item.answer,
|
|
43080
|
-
status: item.status
|
|
43081
|
-
}));
|
|
43411
|
+
const adjudicated = selectAdjudicated(taskInboxItems);
|
|
43082
43412
|
return {
|
|
43083
43413
|
taskId,
|
|
43084
43414
|
seed: resolvedSeed,
|
|
@@ -43086,7 +43416,8 @@ function buildSpecGateDeps(args) {
|
|
|
43086
43416
|
domain: task.domain,
|
|
43087
43417
|
notes,
|
|
43088
43418
|
impact: task.impact,
|
|
43089
|
-
adjudicated
|
|
43419
|
+
adjudicated,
|
|
43420
|
+
specApproved: task.specApproved ?? null
|
|
43090
43421
|
};
|
|
43091
43422
|
})();
|
|
43092
43423
|
}
|
|
@@ -43138,7 +43469,10 @@ ${artifact}`,
|
|
|
43138
43469
|
cwd,
|
|
43139
43470
|
timeoutMs,
|
|
43140
43471
|
sandbox: "read-only",
|
|
43141
|
-
codexExecutable
|
|
43472
|
+
codexExecutable,
|
|
43473
|
+
// model-tier-planning-and-arbiter task 2.5 (D1): the arbiter is pinned to the seat's
|
|
43474
|
+
// strongest model.
|
|
43475
|
+
model: seat?.models.strong
|
|
43142
43476
|
}
|
|
43143
43477
|
);
|
|
43144
43478
|
},
|
|
@@ -43230,7 +43564,7 @@ ${artifact}`,
|
|
|
43230
43564
|
};
|
|
43231
43565
|
}
|
|
43232
43566
|
function buildCodeGateDeps(args) {
|
|
43233
|
-
const { client, projectSlug, baseRef, cwd, codexExecutable, extraHeads } = args;
|
|
43567
|
+
const { client, projectSlug, baseRef, cwd, codexExecutable, extraHeads, seat } = args;
|
|
43234
43568
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
43235
43569
|
const heads = ["code-reviewer", ...extraHeads ?? []];
|
|
43236
43570
|
const maxHeadCalls = 2 * rounds * (heads.length + 1);
|
|
@@ -43274,7 +43608,10 @@ ${artifact}`,
|
|
|
43274
43608
|
cwd,
|
|
43275
43609
|
timeoutMs: headTimeoutMs,
|
|
43276
43610
|
sandbox: "read-only",
|
|
43277
|
-
codexExecutable
|
|
43611
|
+
codexExecutable,
|
|
43612
|
+
// model-tier-planning-and-arbiter task 2.5 (D1): the arbiter is pinned to the seat's
|
|
43613
|
+
// strongest model.
|
|
43614
|
+
model: seat?.models.strong
|
|
43278
43615
|
});
|
|
43279
43616
|
},
|
|
43280
43617
|
async runReviser(artifact, accepted) {
|
|
@@ -43311,7 +43648,7 @@ function renderCriteriaBlock(criteria) {
|
|
|
43311
43648
|
return criteria.map((c, i) => `${i + 1}. [ ] ${c}`).join("\n");
|
|
43312
43649
|
}
|
|
43313
43650
|
async function buildDeliverableGateDeps(args) {
|
|
43314
|
-
const { client, projectSlug, deliverablePageId, cwd, codexExecutable, runId } = args;
|
|
43651
|
+
const { client, projectSlug, deliverablePageId, cwd, codexExecutable, runId, seat } = args;
|
|
43315
43652
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
43316
43653
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
43317
43654
|
const page = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
@@ -43378,7 +43715,10 @@ ${persona}`,
|
|
|
43378
43715
|
cwd,
|
|
43379
43716
|
timeoutMs,
|
|
43380
43717
|
sandbox: "read-only",
|
|
43381
|
-
codexExecutable
|
|
43718
|
+
codexExecutable,
|
|
43719
|
+
// model-tier-planning-and-arbiter task 2.5 (D1): the arbiter is pinned to the seat's
|
|
43720
|
+
// strongest model.
|
|
43721
|
+
model: seat?.models.strong
|
|
43382
43722
|
});
|
|
43383
43723
|
},
|
|
43384
43724
|
async runReviser(artifact, accepted) {
|
|
@@ -43831,7 +44171,11 @@ async function runDraftSpec(ctx, input, deps) {
|
|
|
43831
44171
|
cwd: ctx.cwd,
|
|
43832
44172
|
timeoutMs: draftSpecTimeoutMs(),
|
|
43833
44173
|
sandbox: "read-only",
|
|
43834
|
-
codexExecutable: ctx.codexExecutable
|
|
44174
|
+
codexExecutable: ctx.codexExecutable,
|
|
44175
|
+
// model-tier-planning-and-arbiter task 2.4 (D1): planning is pinned to the seat's strongest
|
|
44176
|
+
// model. Absent when ctx carries no seat (e.g. a hand-built test ctx) — runExec reproduces
|
|
44177
|
+
// today's argv byte-for-byte in that case (task 2.3).
|
|
44178
|
+
model: ctx.seat?.models.strong
|
|
43835
44179
|
});
|
|
43836
44180
|
if (!exec.ok) {
|
|
43837
44181
|
return { kind: "retry", failureReason: "provider_unavailable", error: `spec authoring failed: ${exec.error}` };
|
|
@@ -44544,12 +44888,22 @@ async function runDeployGateTick(deps, projectSlug, run3, task) {
|
|
|
44544
44888
|
await deps.handoff(projectSlug, task, run3, "verification_steps_missing_at_verify");
|
|
44545
44889
|
return { kind: "handoff", reason: "verification_steps_missing_at_verify" };
|
|
44546
44890
|
}
|
|
44891
|
+
let verificationSeat = run3.agentSeat;
|
|
44892
|
+
if (verificationSeat == null) {
|
|
44893
|
+
const seat = await mintResolvedSeat(resolveProvider(process.env, getSelectedProvider()));
|
|
44894
|
+
verificationSeat = seat.id;
|
|
44895
|
+
await deps.client.updateExecutionRun(projectSlug, run3.id, { agentSeat: seat.id }).catch((err) => {
|
|
44896
|
+
console.warn(
|
|
44897
|
+
JSON.stringify({ event: "agent_seat_persist_failed", projectSlug, runId: run3.id, error: String(err) })
|
|
44898
|
+
);
|
|
44899
|
+
});
|
|
44900
|
+
}
|
|
44547
44901
|
const verdict = await deps.runVerificationTurn({
|
|
44548
44902
|
env,
|
|
44549
44903
|
environmentUrl: deployment.environmentUrl,
|
|
44550
44904
|
verificationSection: section,
|
|
44551
44905
|
task,
|
|
44552
|
-
run: run3
|
|
44906
|
+
run: { ...run3, agentSeat: verificationSeat }
|
|
44553
44907
|
});
|
|
44554
44908
|
const verificationAttempt = attemptIndex(artifacts, env, "verification") + 1;
|
|
44555
44909
|
let outputRef = null;
|
|
@@ -45648,7 +46002,18 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
45648
46002
|
maxRuns: threadMaxRuns(),
|
|
45649
46003
|
graceRuns: threadRetireGraceRuns()
|
|
45650
46004
|
});
|
|
45651
|
-
|
|
46005
|
+
let resolvedProviderId;
|
|
46006
|
+
if (claimed.agentSeat != null) {
|
|
46007
|
+
resolvedProviderId = claimed.agentSeat;
|
|
46008
|
+
} else {
|
|
46009
|
+
const seat = await mintResolvedSeat(resolveProvider(process.env, getSelectedProvider()));
|
|
46010
|
+
resolvedProviderId = seat.id;
|
|
46011
|
+
await deps.client.updateExecutionRun(projectSlug, runId, { agentSeat: seat.id }).catch((err) => {
|
|
46012
|
+
console.warn(
|
|
46013
|
+
JSON.stringify({ event: "agent_seat_persist_failed", projectSlug, runId, error: String(err) })
|
|
46014
|
+
);
|
|
46015
|
+
});
|
|
46016
|
+
}
|
|
45652
46017
|
const providerMismatch = task.directorThreadProvider != null && task.directorThreadProvider !== resolvedProviderId;
|
|
45653
46018
|
const threadIdForAcquire = retirement.retire || providerMismatch ? null : task.directorThreadId;
|
|
45654
46019
|
if (retirement.retire) {
|
|
@@ -45707,12 +46072,12 @@ ${renderTaskStateSection(taskState)}`;
|
|
|
45707
46072
|
turnCwd = worktreePath;
|
|
45708
46073
|
useExecutor = true;
|
|
45709
46074
|
if (deps.acquireExecutorAgent) {
|
|
45710
|
-
const executorAgent = await deps.acquireExecutorAgent(worktreePath);
|
|
46075
|
+
const executorAgent = await deps.acquireExecutorAgent(worktreePath, resolvedProviderId);
|
|
45711
46076
|
codex = executorAgent.handle;
|
|
45712
46077
|
disposeCodex = executorAgent.dispose;
|
|
45713
46078
|
} else {
|
|
45714
|
-
codex = await deps.acquireCodex();
|
|
45715
|
-
disposeCodex = deps.releaseCodex;
|
|
46079
|
+
codex = await deps.acquireCodex(resolvedProviderId);
|
|
46080
|
+
disposeCodex = () => deps.releaseCodex(resolvedProviderId);
|
|
45716
46081
|
}
|
|
45717
46082
|
} catch (error51) {
|
|
45718
46083
|
const isProviderNotReady = error51 instanceof Error && error51.code === "PROVIDER_NOT_READY";
|
|
@@ -45750,8 +46115,8 @@ ${renderTaskStateSection(taskState)}`;
|
|
|
45750
46115
|
}
|
|
45751
46116
|
} else {
|
|
45752
46117
|
try {
|
|
45753
|
-
codex = await deps.acquireCodex();
|
|
45754
|
-
disposeCodex = deps.releaseCodex;
|
|
46118
|
+
codex = await deps.acquireCodex(resolvedProviderId);
|
|
46119
|
+
disposeCodex = () => deps.releaseCodex(resolvedProviderId);
|
|
45755
46120
|
} catch (error51) {
|
|
45756
46121
|
const detail = error51 instanceof Error ? error51.message : String(error51);
|
|
45757
46122
|
await failRun(deps, projectSlug, task, runId, leaseId, `codex_acquire_failed: ${detail}`);
|
|
@@ -46731,6 +47096,8 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
46731
47096
|
const claimed = await deps.client.claimAsyncJob(projectSlug, jobId);
|
|
46732
47097
|
if (!claimed || !claimed.leaseId) return;
|
|
46733
47098
|
const leaseId = claimed.leaseId;
|
|
47099
|
+
const seat = claimed.agentSeat != null ? await mintResolvedSeat(resolveProvider(process.env, claimed.agentSeat)) : await mintResolvedSeat(resolveProvider(process.env, getSelectedProvider()));
|
|
47100
|
+
const resolvedProviderId = seat.id;
|
|
46734
47101
|
const descriptor2 = getAsyncToolDescriptor(claimed.toolName);
|
|
46735
47102
|
if (!descriptor2) {
|
|
46736
47103
|
await deps.client.terminalizeAsyncJob(projectSlug, jobId, {
|
|
@@ -46755,7 +47122,8 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
46755
47122
|
projectSlug,
|
|
46756
47123
|
jobId,
|
|
46757
47124
|
leaseId,
|
|
46758
|
-
await deriveRequestedHash(deps.client, claimed.toolName, input)
|
|
47125
|
+
await deriveRequestedHash(deps.client, claimed.toolName, input),
|
|
47126
|
+
resolvedProviderId
|
|
46759
47127
|
);
|
|
46760
47128
|
if (!begun) return;
|
|
46761
47129
|
if (begun.attemptCount > ASYNC_JOB_MAX_ATTEMPTS) {
|
|
@@ -46766,7 +47134,7 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
46766
47134
|
outcome = await deps.run(
|
|
46767
47135
|
"async",
|
|
46768
47136
|
() => descriptor2.run(
|
|
46769
|
-
{ client: deps.client, cwd: deps.cwd, codexExecutable: deps.codexExecutable },
|
|
47137
|
+
{ client: deps.client, cwd: deps.cwd, codexExecutable: deps.codexExecutable, seat, projectSlug },
|
|
46770
47138
|
input
|
|
46771
47139
|
)
|
|
46772
47140
|
);
|
|
@@ -46796,13 +47164,31 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
46796
47164
|
failureReason: outcome.kind === "done" ? null : outcome.failureReason
|
|
46797
47165
|
}).catch(() => {
|
|
46798
47166
|
});
|
|
47167
|
+
if (descriptor2.appliesIntentVerdict && outcome.kind === "done" && outcome.taskId) {
|
|
47168
|
+
let rawResult;
|
|
47169
|
+
try {
|
|
47170
|
+
rawResult = JSON.parse(outcome.resultJson);
|
|
47171
|
+
} catch {
|
|
47172
|
+
rawResult = void 0;
|
|
47173
|
+
}
|
|
47174
|
+
const parsedVerdict = mrrlinIntentVerdictSchema.safeParse(rawResult);
|
|
47175
|
+
if (parsedVerdict.success) {
|
|
47176
|
+
await deps.client.applyIntentVerdict(projectSlug, outcome.taskId, parsedVerdict.data).catch(() => {
|
|
47177
|
+
});
|
|
47178
|
+
}
|
|
47179
|
+
}
|
|
46799
47180
|
const delivery = descriptor2.onComplete(outcome, input);
|
|
46800
47181
|
if (!delivery.suppressInbox) {
|
|
46801
47182
|
await deps.client.createInboxItem(projectSlug, {
|
|
46802
47183
|
id: delivery.id ?? `II-${import_node_crypto6.default.randomUUID()}`,
|
|
46803
47184
|
kind: delivery.kind,
|
|
46804
47185
|
body: delivery.body,
|
|
46805
|
-
...delivery.taskId ? { taskId: delivery.taskId } : {}
|
|
47186
|
+
...delivery.taskId ? { taskId: delivery.taskId } : {},
|
|
47187
|
+
// early-intent-confirmation: without forwarding these, an intent-check question lands
|
|
47188
|
+
// with origin unset, and the adjudicated-set filter (consensus/wiring.ts) that excludes
|
|
47189
|
+
// gate-authored auto-closures by origin silently stops suppressing duplicates for it.
|
|
47190
|
+
...delivery.origin !== void 0 ? { origin: delivery.origin } : {},
|
|
47191
|
+
...delivery.questionOptions !== void 0 ? { questionOptions: delivery.questionOptions } : {}
|
|
46806
47192
|
}).catch(() => {
|
|
46807
47193
|
});
|
|
46808
47194
|
}
|
|
@@ -46858,6 +47244,10 @@ async function runDeliverableGate(_target, deps) {
|
|
|
46858
47244
|
maxHeadCalls: deps.maxHeadCalls,
|
|
46859
47245
|
runHead: deps.runHead,
|
|
46860
47246
|
runArbiter: deps.runArbiter,
|
|
47247
|
+
// model-tier-planning-and-arbiter task 2.20: no real multi-head panel reaches this gate yet
|
|
47248
|
+
// (that wiring is a documented, separate gap — see spec-gate.ts/code-gate.ts), so this always
|
|
47249
|
+
// reports the honest single-seat composition rather than a fabricated multi-agent one.
|
|
47250
|
+
panelComposition: { requested: deps.heads.length, filled: 1, underpowered: deps.heads.length > 1 },
|
|
46861
47251
|
revise: async (artifact, accepted) => {
|
|
46862
47252
|
const revised = await deps.runReviser(artifact, accepted);
|
|
46863
47253
|
if (!revised.ok) return artifact;
|
|
@@ -46936,48 +47326,58 @@ function createSessionSinkRegistry(maxEntries = DEFAULT_MAX_SESSION_SINKS) {
|
|
|
46936
47326
|
|
|
46937
47327
|
// src/agent-keepalive.ts
|
|
46938
47328
|
function createAgentKeepalive(opts) {
|
|
46939
|
-
|
|
46940
|
-
|
|
46941
|
-
|
|
46942
|
-
|
|
46943
|
-
|
|
46944
|
-
|
|
46945
|
-
|
|
47329
|
+
const slots = /* @__PURE__ */ new Map();
|
|
47330
|
+
function getSlot(providerId) {
|
|
47331
|
+
let slot = slots.get(providerId);
|
|
47332
|
+
if (!slot) {
|
|
47333
|
+
slot = { idleTimer: null, holdCount: 0, managerHeld: false };
|
|
47334
|
+
slots.set(providerId, slot);
|
|
47335
|
+
}
|
|
47336
|
+
return slot;
|
|
47337
|
+
}
|
|
47338
|
+
const cancelTimer = (slot) => {
|
|
47339
|
+
if (slot.idleTimer) {
|
|
47340
|
+
clearTimeout(slot.idleTimer);
|
|
47341
|
+
slot.idleTimer = null;
|
|
46946
47342
|
}
|
|
46947
47343
|
};
|
|
46948
47344
|
return {
|
|
46949
|
-
acquire: async () => {
|
|
46950
|
-
|
|
46951
|
-
|
|
46952
|
-
|
|
46953
|
-
|
|
47345
|
+
acquire: async (providerId) => {
|
|
47346
|
+
const slot = getSlot(providerId);
|
|
47347
|
+
cancelTimer(slot);
|
|
47348
|
+
const client = await opts.acquire(providerId);
|
|
47349
|
+
if (slot.managerHeld) {
|
|
47350
|
+
opts.release(providerId);
|
|
46954
47351
|
} else {
|
|
46955
|
-
managerHeld = true;
|
|
47352
|
+
slot.managerHeld = true;
|
|
46956
47353
|
}
|
|
46957
|
-
holdCount += 1;
|
|
47354
|
+
slot.holdCount += 1;
|
|
46958
47355
|
return client;
|
|
46959
47356
|
},
|
|
46960
|
-
release: () => {
|
|
46961
|
-
|
|
46962
|
-
|
|
47357
|
+
release: (providerId) => {
|
|
47358
|
+
const slot = getSlot(providerId);
|
|
47359
|
+
slot.holdCount = Math.max(0, slot.holdCount - 1);
|
|
47360
|
+
if (slot.holdCount > 0) {
|
|
46963
47361
|
return;
|
|
46964
47362
|
}
|
|
46965
|
-
cancelTimer();
|
|
46966
|
-
idleTimer = setTimeout(() => {
|
|
46967
|
-
idleTimer = null;
|
|
46968
|
-
if (managerHeld) {
|
|
46969
|
-
managerHeld = false;
|
|
46970
|
-
opts.release();
|
|
47363
|
+
cancelTimer(slot);
|
|
47364
|
+
slot.idleTimer = setTimeout(() => {
|
|
47365
|
+
slot.idleTimer = null;
|
|
47366
|
+
if (slot.managerHeld) {
|
|
47367
|
+
slot.managerHeld = false;
|
|
47368
|
+
opts.release(providerId);
|
|
46971
47369
|
}
|
|
46972
|
-
}, opts.idleMs);
|
|
46973
|
-
idleTimer.unref?.();
|
|
47370
|
+
}, opts.idleMs(providerId));
|
|
47371
|
+
slot.idleTimer.unref?.();
|
|
46974
47372
|
},
|
|
46975
47373
|
dropNow: () => {
|
|
46976
|
-
|
|
46977
|
-
|
|
46978
|
-
|
|
46979
|
-
managerHeld
|
|
46980
|
-
|
|
47374
|
+
for (const [providerId, slot] of slots) {
|
|
47375
|
+
cancelTimer(slot);
|
|
47376
|
+
slot.holdCount = 0;
|
|
47377
|
+
if (slot.managerHeld) {
|
|
47378
|
+
slot.managerHeld = false;
|
|
47379
|
+
opts.release(providerId);
|
|
47380
|
+
}
|
|
46981
47381
|
}
|
|
46982
47382
|
}
|
|
46983
47383
|
};
|
|
@@ -51286,7 +51686,11 @@ function createRunVerificationTurn(deps) {
|
|
|
51286
51686
|
if (isBlockedVerificationHost(hostname3)) {
|
|
51287
51687
|
return { verdict: "fail", summary: `refusing to verify against non-public host: ${hostname3}`, turnRef: null };
|
|
51288
51688
|
}
|
|
51289
|
-
const
|
|
51689
|
+
const providerId = input.run.agentSeat;
|
|
51690
|
+
if (providerId == null) {
|
|
51691
|
+
return { verdict: "fail", summary: "run has no resolved agent_seat", turnRef: null };
|
|
51692
|
+
}
|
|
51693
|
+
const { handle, dispose, cwd } = await deps.startSandbox([hostname3], providerId);
|
|
51290
51694
|
try {
|
|
51291
51695
|
let text;
|
|
51292
51696
|
try {
|
|
@@ -52105,10 +52509,48 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
52105
52509
|
" ## Result \u2014 what was accomplished, key findings or metrics, follow-up tasks created.",
|
|
52106
52510
|
" Do NOT remove earlier sections \u2014 they remain useful for context.",
|
|
52107
52511
|
" Adapt depth to the task: a one-line fix needs one sentence, not four headings.",
|
|
52108
|
-
|
|
52109
|
-
|
|
52110
|
-
"
|
|
52111
|
-
|
|
52512
|
+
// early-intent-confirmation (design.md, "The chat contract"): the core block below is copied
|
|
52513
|
+
// VERBATIM from design.md, per tasks.md 10.1. It replaces an older sentence that also carried
|
|
52514
|
+
// "omit operatorSeedRequest only when there is genuinely no operator request behind the task
|
|
52515
|
+
// (pure system bookkeeping) — omitting it makes the gate fail-closed". design.md's own
|
|
52516
|
+
// replacement text does not restate that exception, so it was initially left out per the
|
|
52517
|
+
// "verbatim" instruction and flagged for the consensus round instead of silently deciding
|
|
52518
|
+
// either way. Round 1 (two of three reviewers, independently) confirmed a real gap: without an
|
|
52519
|
+
// explicit "do not invent one" instruction, the Director could fabricate operator wording for
|
|
52520
|
+
// a task with no real request behind it. The paragraph a few lines below ("If a task has no
|
|
52521
|
+
// real operator request...") closes that gap — it is a NEW clarifying addition, not a
|
|
52522
|
+
// verbatim restoration of the old sentence, so it is deliberately kept separate from the
|
|
52523
|
+
// pinned verbatim block in director-bridge.test.ts.
|
|
52524
|
+
"Before you call create_task out of an operator request, enumerate the aspects needed to state",
|
|
52525
|
+
"the task's outcome. For each aspect that is not yet resolved, resolve it in THIS order:",
|
|
52526
|
+
" 1. the conversation you are already in \u2014 this resolves most of them, and costs nothing;",
|
|
52527
|
+
" 2. search_wiki_pages / retrieve_context.",
|
|
52528
|
+
"Ask the operator ONLY about aspects that neither resolved. When you must ask, reply in the SAME",
|
|
52529
|
+
"turn with one sentence restating your understanding, then at most 3 scope-level questions, and",
|
|
52530
|
+
"do NOT create the task yet. Create it after the operator replies.",
|
|
52531
|
+
"",
|
|
52532
|
+
"Set operatorSeedRequest to the operator's OWN WORDS, verbatim. Select every message in this",
|
|
52533
|
+
"conversation that forms part of the request \u2014 not only the last one \u2014 and concatenate them in",
|
|
52534
|
+
"order, separated by a blank line. Do NOT paraphrase, summarise, or rewrite any of it, and do",
|
|
52535
|
+
"NOT add words of your own.",
|
|
52536
|
+
"",
|
|
52537
|
+
"If a task has no real operator request behind it at all (pure system bookkeeping nobody asked",
|
|
52538
|
+
"for), do NOT invent one to fill this field \u2014 leave operatorSeedRequest unset. Never fabricate",
|
|
52539
|
+
"operator wording; an unset seed is handled downstream, a fabricated one is not.",
|
|
52540
|
+
"",
|
|
52541
|
+
'A bare last message ("ok, do it", "\u0442\u0430\u043A \u0433\u0443\u0434", "finish it") is useless on its own, so carry the',
|
|
52542
|
+
"earlier messages that give it meaning. If the operator's request spans three turns, the seed",
|
|
52543
|
+
"holds all three. If they truly said only one substantive thing, the seed holds only that.",
|
|
52544
|
+
"",
|
|
52545
|
+
"Three worked examples for the resolution order above:",
|
|
52546
|
+
'- "\u0434\u043E\u0440\u043E\u0431\u0438\u0448" right after discussing a specific task \u2014 resolved from THE CONVERSATION, so do not',
|
|
52547
|
+
" ask. This is the common case: 15 of every 19 apparent gaps close here, for free.",
|
|
52548
|
+
'- "\u0434\u043E\u0434\u0430\u0439 \u043D\u0430 \u043B\u0435\u043D\u0434\u0456\u043D\u0433 \u0431\u043B\u043E\u043A \u044F\u043A \u0443 Native Vibe" when that campaign is documented on the wiki and the',
|
|
52549
|
+
" conversation itself never named it \u2014 resolved from THE WIKI, after the conversation fails to",
|
|
52550
|
+
" name it. Do not ask.",
|
|
52551
|
+
'- "\u0437\u0440\u043E\u0431\u0438 \u044F\u043A \u043C\u0438 \u0434\u043E\u043C\u043E\u0432\u043B\u044F\u043B\u0438\u0441\u044C \u043F\u043E \u0446\u0456\u043D\u0430\u0445" with no pricing decision anywhere in this conversation and',
|
|
52552
|
+
" nothing on the wiki either \u2014 resolved by NEITHER. Ask; this is a decision only the operator",
|
|
52553
|
+
" holds.",
|
|
52112
52554
|
"",
|
|
52113
52555
|
"Spec authoring contract \u2014 when creating or updating a `Specs/{id}` wiki page:",
|
|
52114
52556
|
"- BEGIN the page with YAML frontmatter naming the gated task: a first line `---`, then",
|
|
@@ -53066,7 +53508,8 @@ function createBridgeMessageHandler(deps) {
|
|
|
53066
53508
|
let bootstrapError;
|
|
53067
53509
|
const fallbackModels = readCodexFallbackModels();
|
|
53068
53510
|
try {
|
|
53069
|
-
const
|
|
53511
|
+
const bootstrapProviderId = resolveProvider(process.env, getSelectedProvider()).id;
|
|
53512
|
+
const codexClient2 = await deps.acquireCodex(bootstrapProviderId);
|
|
53070
53513
|
try {
|
|
53071
53514
|
try {
|
|
53072
53515
|
const raw = await codexClient2.listModels();
|
|
@@ -53100,7 +53543,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
53100
53543
|
bootstrapError ??= error51 instanceof Error ? error51.message : String(error51);
|
|
53101
53544
|
}
|
|
53102
53545
|
} finally {
|
|
53103
|
-
deps.releaseCodex();
|
|
53546
|
+
deps.releaseCodex(bootstrapProviderId);
|
|
53104
53547
|
}
|
|
53105
53548
|
} catch (error51) {
|
|
53106
53549
|
bootstrapError = error51 instanceof Error ? error51.message : String(error51);
|
|
@@ -53150,7 +53593,8 @@ function createBridgeMessageHandler(deps) {
|
|
|
53150
53593
|
pendingTurnSessions.delete(directorSessionId2);
|
|
53151
53594
|
}
|
|
53152
53595
|
}
|
|
53153
|
-
const
|
|
53596
|
+
const interruptProviderId = existing.provider ?? resolveProvider(process.env, getSelectedProvider()).id;
|
|
53597
|
+
const codexClient2 = await deps.acquireCodex(interruptProviderId);
|
|
53154
53598
|
try {
|
|
53155
53599
|
await codexClient2.interruptTurn(existing.threadId);
|
|
53156
53600
|
existing.cancel?.();
|
|
@@ -53158,7 +53602,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
53158
53602
|
} catch (error51) {
|
|
53159
53603
|
sendForSpan({ type: "error", directorSessionId: directorSessionId2, error: error51 instanceof Error ? error51.message : String(error51) });
|
|
53160
53604
|
} finally {
|
|
53161
|
-
deps.releaseCodex();
|
|
53605
|
+
deps.releaseCodex(interruptProviderId);
|
|
53162
53606
|
}
|
|
53163
53607
|
return;
|
|
53164
53608
|
}
|
|
@@ -53219,8 +53663,9 @@ function createBridgeMessageHandler(deps) {
|
|
|
53219
53663
|
return;
|
|
53220
53664
|
}
|
|
53221
53665
|
pendingTurnSessions.add(directorSessionId);
|
|
53222
|
-
const
|
|
53223
|
-
const
|
|
53666
|
+
const turnSeat = await mintResolvedSeat(resolveProvider(process.env, getSelectedProvider()));
|
|
53667
|
+
const turnProviderCapabilities = turnSeat.capabilities;
|
|
53668
|
+
const codexClient = await deps.acquireCodex(turnSeat.id);
|
|
53224
53669
|
let thread = deps.threads.get(directorSessionId);
|
|
53225
53670
|
if (thread?.inFlight && msg.takeover === true) {
|
|
53226
53671
|
const takenOver = thread;
|
|
@@ -53264,7 +53709,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
53264
53709
|
}
|
|
53265
53710
|
const desiredModel = normalizeModel(msg.model);
|
|
53266
53711
|
const desiredEffort = normalizeReasoningEffort(msg.reasoningEffort);
|
|
53267
|
-
const resolvedProviderId =
|
|
53712
|
+
const resolvedProviderId = turnSeat.id;
|
|
53268
53713
|
try {
|
|
53269
53714
|
const startParams = deps.buildStartParams(msg, desiredModel);
|
|
53270
53715
|
if (!thread) {
|
|
@@ -53322,7 +53767,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
53322
53767
|
deps.setCodexAuth?.("expired");
|
|
53323
53768
|
}
|
|
53324
53769
|
pendingTurnSessions.delete(directorSessionId);
|
|
53325
|
-
deps.releaseCodex();
|
|
53770
|
+
deps.releaseCodex(turnSeat.id);
|
|
53326
53771
|
return;
|
|
53327
53772
|
}
|
|
53328
53773
|
if (thread.inFlight) {
|
|
@@ -53334,7 +53779,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
53334
53779
|
error: "Director session is busy."
|
|
53335
53780
|
});
|
|
53336
53781
|
pendingTurnSessions.delete(directorSessionId);
|
|
53337
|
-
deps.releaseCodex();
|
|
53782
|
+
deps.releaseCodex(turnSeat.id);
|
|
53338
53783
|
return;
|
|
53339
53784
|
}
|
|
53340
53785
|
thread.inFlight = true;
|
|
@@ -53587,7 +54032,7 @@ ${effectiveText}` : effectiveText;
|
|
|
53587
54032
|
thread.lastUsedAt = Date.now();
|
|
53588
54033
|
thread.lastUsedAtSuspendedMs = deps.suspendedMs?.() ?? 0;
|
|
53589
54034
|
deps.persistThreads();
|
|
53590
|
-
deps.releaseCodex();
|
|
54035
|
+
deps.releaseCodex(turnSeat.id);
|
|
53591
54036
|
}
|
|
53592
54037
|
});
|
|
53593
54038
|
};
|
|
@@ -53801,20 +54246,20 @@ async function startDirectorBridge() {
|
|
|
53801
54246
|
const hasStaticToken = !agentSecret && Boolean(envToken ?? readOperatorToken());
|
|
53802
54247
|
const client = createMrrlinClient({ baseUrl: apiBaseUrl, fetch: authedFetch });
|
|
53803
54248
|
const agentKeepalive = createAgentKeepalive({
|
|
53804
|
-
acquire: () => resolveProvider(process.env,
|
|
53805
|
-
release: () => resolveProvider(process.env,
|
|
53806
|
-
idleMs:
|
|
54249
|
+
acquire: (providerId) => resolveProvider(process.env, providerId).acquireWarm(),
|
|
54250
|
+
release: (providerId) => resolveProvider(process.env, providerId).releaseWarm(),
|
|
54251
|
+
idleMs: (providerId) => agentIdleMs(providerId)
|
|
53807
54252
|
});
|
|
53808
54253
|
const checkoutRegistry = new CheckoutRegistry(config2.stateDir);
|
|
53809
54254
|
const staleSeconds = readEnvInt("MRRLIN_DIRECTOR_BRIDGE_STALE_SECONDS", DEFAULT_STALE_SECONDS);
|
|
53810
54255
|
const consumerDeps = {
|
|
53811
54256
|
client,
|
|
53812
|
-
acquireCodex: () => agentKeepalive.acquire(),
|
|
54257
|
+
acquireCodex: (providerId) => agentKeepalive.acquire(providerId),
|
|
53813
54258
|
// Read-only / chat path release: the shared keepalive. The executor path no
|
|
53814
54259
|
// longer routes through here — it disposes its OWN per-run server via the
|
|
53815
54260
|
// handle returned by acquireExecutorCodex (Fix A + G), so there is no shared
|
|
53816
54261
|
// mutable `activeExecutorClient` slot to race a concurrent chat/bootstrap turn.
|
|
53817
|
-
releaseCodex: () => agentKeepalive.release(),
|
|
54262
|
+
releaseCodex: (providerId) => agentKeepalive.release(providerId),
|
|
53818
54263
|
run: wrappedTurnLock.run,
|
|
53819
54264
|
staleSeconds,
|
|
53820
54265
|
touchIntervalMs: Math.min(DEFAULT_TOUCH_INTERVAL_MS, Math.floor(staleSeconds * 1e3 / 2)),
|
|
@@ -53830,10 +54275,13 @@ async function startDirectorBridge() {
|
|
|
53830
54275
|
// The returned handle's dispose() shuts down the spawned process (task 4.1: startExecutorServer
|
|
53831
54276
|
// itself owns which provider-specific shutdown that is) — releasing the executor therefore
|
|
53832
54277
|
// kills the process. driveRun owns this dispose per-run (Fix A + G).
|
|
53833
|
-
|
|
54278
|
+
// Task 1.11: providerId is the caller's own pinned/minted seat (execution-consumer.ts's
|
|
54279
|
+
// resolvedProviderId), not resolved ambiently here — this run-scoped spawn has no business
|
|
54280
|
+
// re-reading a setting that could have moved since the run's own seat was decided.
|
|
54281
|
+
acquireExecutorAgent: async (worktreePath, providerId) => {
|
|
53834
54282
|
const executor = await startExecutorServer({
|
|
53835
54283
|
worktree: worktreePath,
|
|
53836
|
-
provider:
|
|
54284
|
+
provider: providerId
|
|
53837
54285
|
});
|
|
53838
54286
|
return { handle: executor.handle, dispose: executor.dispose };
|
|
53839
54287
|
},
|
|
@@ -53862,11 +54310,11 @@ async function startDirectorBridge() {
|
|
|
53862
54310
|
runVerificationTurn: createRunVerificationTurn({
|
|
53863
54311
|
// egressDomains is retained in the seam signature (verification-turn.ts still computes the
|
|
53864
54312
|
// env host) but no longer constrains anything — the executor is unrestricted.
|
|
53865
|
-
startSandbox: async (_egressDomains) => {
|
|
54313
|
+
startSandbox: async (_egressDomains, providerId) => {
|
|
53866
54314
|
const cwd = import_node_fs16.default.mkdtempSync(import_node_path20.default.join(import_node_os11.default.tmpdir(), "mrrlin-verify-"));
|
|
53867
54315
|
let executor;
|
|
53868
54316
|
try {
|
|
53869
|
-
executor = await startExecutorServer({ worktree: cwd, provider:
|
|
54317
|
+
executor = await startExecutorServer({ worktree: cwd, provider: providerId });
|
|
53870
54318
|
} catch (error51) {
|
|
53871
54319
|
try {
|
|
53872
54320
|
import_node_fs16.default.rmSync(cwd, { recursive: true, force: true });
|
|
@@ -54037,8 +54485,8 @@ async function startDirectorBridge() {
|
|
|
54037
54485
|
const relayUrl = process.env.MRRLIN_RELAY_URL?.trim();
|
|
54038
54486
|
const relayConfigured = Boolean(agentSecret && relayUrl);
|
|
54039
54487
|
const sharedHandlerDeps = {
|
|
54040
|
-
acquireCodex: () => agentKeepalive.acquire(),
|
|
54041
|
-
releaseCodex: () => agentKeepalive.release(),
|
|
54488
|
+
acquireCodex: (providerId) => agentKeepalive.acquire(providerId),
|
|
54489
|
+
releaseCodex: (providerId) => agentKeepalive.release(providerId),
|
|
54042
54490
|
run: wrappedTurnLock.run,
|
|
54043
54491
|
suspendedMs: () => suspensionDetector.suspendedMs(),
|
|
54044
54492
|
buildStartParams: (msg, desiredModel) => buildThreadStartParams(msg, config2, desiredModel),
|
|
@@ -58117,6 +58565,7 @@ async function runCodeGate(_target, deps) {
|
|
|
58117
58565
|
heads,
|
|
58118
58566
|
rounds: deps.rounds,
|
|
58119
58567
|
maxHeadCalls: deps.maxHeadCalls,
|
|
58568
|
+
panelComposition: deps.panelComposition ?? { requested: heads.length, filled: 1, underpowered: heads.length > 1 },
|
|
58120
58569
|
runHead: deps.runHead,
|
|
58121
58570
|
runArbiter: deps.runArbiter,
|
|
58122
58571
|
revise: async (prev, accepted) => {
|
|
@@ -58152,7 +58601,13 @@ async function runCodeGate(_target, deps) {
|
|
|
58152
58601
|
return await deps.readReviewArtifact();
|
|
58153
58602
|
}
|
|
58154
58603
|
});
|
|
58155
|
-
return {
|
|
58604
|
+
return {
|
|
58605
|
+
outcome: result.outcome,
|
|
58606
|
+
rounds: result.rounds,
|
|
58607
|
+
arbiterInfo: result.arbiterInfo,
|
|
58608
|
+
panelComposition: result.panelComposition,
|
|
58609
|
+
sufficiencyVerdict: result.sufficiencyVerdict
|
|
58610
|
+
};
|
|
58156
58611
|
} catch (err) {
|
|
58157
58612
|
if (err instanceof CodeGateUnfixableError) {
|
|
58158
58613
|
return { outcome: "CONSENSUS_BLOCKED" };
|
|
@@ -58162,10 +58617,101 @@ async function runCodeGate(_target, deps) {
|
|
|
58162
58617
|
}
|
|
58163
58618
|
|
|
58164
58619
|
// src/consensus/multi-llm-head.ts
|
|
58620
|
+
var import_node_child_process17 = require("node:child_process");
|
|
58621
|
+
|
|
58622
|
+
// src/consensus/provider-limits.ts
|
|
58165
58623
|
var import_node_child_process16 = require("node:child_process");
|
|
58624
|
+
var DEFAULT_STATUS_TIMEOUT_MS = 1e4;
|
|
58166
58625
|
function errMessage4(err) {
|
|
58167
58626
|
return err instanceof Error ? err.message : String(err);
|
|
58168
58627
|
}
|
|
58628
|
+
function runExternalAgentsStatus(bin, args, timeoutMs, spawn4) {
|
|
58629
|
+
return new Promise((resolve) => {
|
|
58630
|
+
let settled = false;
|
|
58631
|
+
const timerRef = { id: void 0 };
|
|
58632
|
+
const finish = (r) => {
|
|
58633
|
+
if (settled) return;
|
|
58634
|
+
settled = true;
|
|
58635
|
+
if (timerRef.id) clearTimeout(timerRef.id);
|
|
58636
|
+
resolve(r);
|
|
58637
|
+
};
|
|
58638
|
+
const options = { stdio: ["ignore", "pipe", "pipe"], env: process.env, shell: false };
|
|
58639
|
+
let child;
|
|
58640
|
+
try {
|
|
58641
|
+
child = spawn4(bin, args, options);
|
|
58642
|
+
} catch (err) {
|
|
58643
|
+
finish({ stdout: "", error: `spawn ${bin} failed: ${errMessage4(err)}` });
|
|
58644
|
+
return;
|
|
58645
|
+
}
|
|
58646
|
+
let stdout = "";
|
|
58647
|
+
timerRef.id = setTimeout(() => {
|
|
58648
|
+
child.kill("SIGKILL");
|
|
58649
|
+
finish({ stdout, error: `external-agents status timed out after ${timeoutMs}ms` });
|
|
58650
|
+
}, timeoutMs);
|
|
58651
|
+
timerRef.id.unref?.();
|
|
58652
|
+
child.stdout?.on("data", (chunk) => {
|
|
58653
|
+
stdout += chunk.toString();
|
|
58654
|
+
});
|
|
58655
|
+
child.stderr?.on("data", () => {
|
|
58656
|
+
});
|
|
58657
|
+
child.on("error", (err) => finish({ stdout, error: `external-agents status error: ${errMessage4(err)}` }));
|
|
58658
|
+
child.on("close", (code) => {
|
|
58659
|
+
if (code !== 0 && !stdout.trim()) finish({ stdout, error: `external-agents status exited ${code}` });
|
|
58660
|
+
else finish({ stdout });
|
|
58661
|
+
});
|
|
58662
|
+
});
|
|
58663
|
+
}
|
|
58664
|
+
async function fetchRegistrySnapshot(deps) {
|
|
58665
|
+
const spawn4 = deps?.spawn ?? import_node_child_process16.spawn;
|
|
58666
|
+
const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
|
|
58667
|
+
const { stdout, error: error51 } = await runExternalAgentsStatus(
|
|
58668
|
+
command,
|
|
58669
|
+
[...prefixArgs, "status", "--json"],
|
|
58670
|
+
deps?.statusTimeoutMs ?? DEFAULT_STATUS_TIMEOUT_MS,
|
|
58671
|
+
spawn4
|
|
58672
|
+
);
|
|
58673
|
+
if (error51) return { ok: false, error: error51 };
|
|
58674
|
+
try {
|
|
58675
|
+
const parsed = JSON.parse(stdout);
|
|
58676
|
+
return { ok: true, entries: Array.isArray(parsed) ? parsed : [] };
|
|
58677
|
+
} catch (err) {
|
|
58678
|
+
return { ok: false, error: `unparseable status --json output: ${errMessage4(err)}` };
|
|
58679
|
+
}
|
|
58680
|
+
}
|
|
58681
|
+
function tightestPublishedContextWindow(entries) {
|
|
58682
|
+
const values = entries.map((e) => e.token_limits?.context_window).filter((v) => typeof v === "number" && v > 0);
|
|
58683
|
+
return values.length > 0 ? Math.min(...values) : null;
|
|
58684
|
+
}
|
|
58685
|
+
async function enforceProviderLimits(seat, agentId, tokenCount, deps) {
|
|
58686
|
+
const snapshot = await fetchRegistrySnapshot(deps);
|
|
58687
|
+
if (!snapshot.ok) {
|
|
58688
|
+
console.warn(
|
|
58689
|
+
JSON.stringify({ event: "provider_limits_check_skipped_fetch_failed", agentId, error: snapshot.error })
|
|
58690
|
+
);
|
|
58691
|
+
return { ok: true };
|
|
58692
|
+
}
|
|
58693
|
+
const entries = snapshot.entries;
|
|
58694
|
+
const entry = entries.find((e) => e.id === agentId);
|
|
58695
|
+
const ownContextWindow = entry?.token_limits?.context_window;
|
|
58696
|
+
const borrowedContextWindow = ownContextWindow == null ? tightestPublishedContextWindow(entries) : null;
|
|
58697
|
+
const effectiveContextWindow = ownContextWindow ?? borrowedContextWindow;
|
|
58698
|
+
if (effectiveContextWindow != null && tokenCount > effectiveContextWindow) {
|
|
58699
|
+
return { ok: false, reason: "over_context", borrowed: ownContextWindow == null };
|
|
58700
|
+
}
|
|
58701
|
+
const tpm = entry?.token_limits?.tpm;
|
|
58702
|
+
if (tpm != null && tokenCount > tpm) {
|
|
58703
|
+
return { ok: false, reason: "over_rate" };
|
|
58704
|
+
}
|
|
58705
|
+
return { ok: true };
|
|
58706
|
+
}
|
|
58707
|
+
|
|
58708
|
+
// src/consensus/multi-llm-head.ts
|
|
58709
|
+
function estimateTokenCount(text) {
|
|
58710
|
+
return Math.ceil(text.length / 4);
|
|
58711
|
+
}
|
|
58712
|
+
function errMessage5(err) {
|
|
58713
|
+
return err instanceof Error ? err.message : String(err);
|
|
58714
|
+
}
|
|
58169
58715
|
function runExternalAgents(bin, args, label, timeoutMs, spawn4) {
|
|
58170
58716
|
return new Promise((resolve) => {
|
|
58171
58717
|
let settled = false;
|
|
@@ -58186,7 +58732,7 @@ function runExternalAgents(bin, args, label, timeoutMs, spawn4) {
|
|
|
58186
58732
|
try {
|
|
58187
58733
|
child = spawn4(bin, args, options);
|
|
58188
58734
|
} catch (err) {
|
|
58189
|
-
finish({ code: null, stdout: "", error: `spawn ${bin} failed: ${
|
|
58735
|
+
finish({ code: null, stdout: "", error: `spawn ${bin} failed: ${errMessage5(err)}` });
|
|
58190
58736
|
return;
|
|
58191
58737
|
}
|
|
58192
58738
|
let stdout = "";
|
|
@@ -58199,12 +58745,12 @@ function runExternalAgents(bin, args, label, timeoutMs, spawn4) {
|
|
|
58199
58745
|
});
|
|
58200
58746
|
child.stderr?.on("data", () => {
|
|
58201
58747
|
});
|
|
58202
|
-
child.on("error", (err) => finish({ code: null, stdout, error: `external-agents ${label} error: ${
|
|
58748
|
+
child.on("error", (err) => finish({ code: null, stdout, error: `external-agents ${label} error: ${errMessage5(err)}` }));
|
|
58203
58749
|
child.on("close", (code) => finish({ code, stdout }));
|
|
58204
58750
|
});
|
|
58205
58751
|
}
|
|
58206
58752
|
async function pickPanel(input, deps) {
|
|
58207
|
-
const spawn4 = deps?.spawn ??
|
|
58753
|
+
const spawn4 = deps?.spawn ?? import_node_child_process17.spawn;
|
|
58208
58754
|
const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
|
|
58209
58755
|
const args = [
|
|
58210
58756
|
"pick",
|
|
@@ -58225,7 +58771,7 @@ async function pickPanel(input, deps) {
|
|
|
58225
58771
|
return { ok: true, agents };
|
|
58226
58772
|
}
|
|
58227
58773
|
async function dispatchHead(agentId, prompt, timeoutMs, deps) {
|
|
58228
|
-
const spawn4 = deps?.spawn ??
|
|
58774
|
+
const spawn4 = deps?.spawn ?? import_node_child_process17.spawn;
|
|
58229
58775
|
const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
|
|
58230
58776
|
const args = ["dispatch", agentId, "--json", prompt];
|
|
58231
58777
|
const r = await runExternalAgents(command, [...prefixArgs, ...args], "dispatch", timeoutMs, spawn4);
|
|
@@ -58237,7 +58783,7 @@ async function dispatchHead(agentId, prompt, timeoutMs, deps) {
|
|
|
58237
58783
|
try {
|
|
58238
58784
|
parsed = JSON.parse(jsonLine);
|
|
58239
58785
|
} catch (err) {
|
|
58240
|
-
return { ok: false, error: `dispatch ${agentId}: unparseable JSON (${
|
|
58786
|
+
return { ok: false, error: `dispatch ${agentId}: unparseable JSON (${errMessage5(err)})` };
|
|
58241
58787
|
}
|
|
58242
58788
|
if (parsed.outcome !== "success") {
|
|
58243
58789
|
return { ok: false, error: `dispatch ${agentId}: outcome=${parsed.outcome} (exit ${parsed.exit_code})` };
|
|
@@ -58248,31 +58794,66 @@ async function dispatchHead(agentId, prompt, timeoutMs, deps) {
|
|
|
58248
58794
|
return { ok: true, text: parsed.text };
|
|
58249
58795
|
}
|
|
58250
58796
|
async function buildMultiHeadRunner(input, deps) {
|
|
58251
|
-
const
|
|
58797
|
+
const pickOnce = () => pickPanel(
|
|
58252
58798
|
{ n: input.personas.length, excludeProvider: input.excludeProvider, tierPrefer: input.tierPrefer, timeoutMs: input.pickTimeoutMs },
|
|
58253
58799
|
deps
|
|
58254
58800
|
);
|
|
58801
|
+
const picked = await pickOnce();
|
|
58255
58802
|
if (!picked.ok) return { ok: false, error: picked.error };
|
|
58256
58803
|
if (picked.agents.length === 0) return { ok: false, error: "pick returned no agents" };
|
|
58257
|
-
|
|
58258
|
-
|
|
58259
|
-
|
|
58260
|
-
|
|
58261
|
-
|
|
58262
|
-
|
|
58263
|
-
|
|
58264
|
-
|
|
58265
|
-
|
|
58266
|
-
|
|
58267
|
-
|
|
58804
|
+
function buildFromAgents(agents) {
|
|
58805
|
+
const assignment = {};
|
|
58806
|
+
input.personas.forEach((persona, i) => {
|
|
58807
|
+
const agent = agents[i % agents.length];
|
|
58808
|
+
if (agent) assignment[persona] = agent;
|
|
58809
|
+
});
|
|
58810
|
+
const composition = {
|
|
58811
|
+
requested: input.personas.length,
|
|
58812
|
+
filled: agents.length,
|
|
58813
|
+
agentIds: agents,
|
|
58814
|
+
underpowered: agents.length < input.personas.length
|
|
58815
|
+
};
|
|
58816
|
+
const runHead = async (persona, artifact, dismissed) => {
|
|
58817
|
+
const agentId = assignment[persona];
|
|
58818
|
+
if (!agentId) return { ok: false, error: `no agent assigned for persona ${persona}` };
|
|
58819
|
+
const instructions = input.personaInstructions[persona] ?? "";
|
|
58820
|
+
const dismissedBlock = dismissed.trim() ? `
|
|
58268
58821
|
|
|
58269
58822
|
${dismissed.trim()}` : "";
|
|
58270
|
-
|
|
58823
|
+
const prompt = instructions.trim() ? `${instructions.trim()}
|
|
58271
58824
|
|
|
58272
58825
|
${artifact}${dismissedBlock}` : `${artifact}${dismissedBlock}`;
|
|
58273
|
-
|
|
58826
|
+
if (input.seat) {
|
|
58827
|
+
const limits = await enforceProviderLimits(input.seat, agentId, estimateTokenCount(prompt), deps);
|
|
58828
|
+
if (!limits.ok) {
|
|
58829
|
+
const borrowed = limits.reason === "over_context" && limits.borrowed === true;
|
|
58830
|
+
console.warn(
|
|
58831
|
+
JSON.stringify({
|
|
58832
|
+
event: borrowed ? "head_skipped_borrowed_ceiling" : "head_skipped_over_budget",
|
|
58833
|
+
persona,
|
|
58834
|
+
agentId,
|
|
58835
|
+
reason: limits.reason
|
|
58836
|
+
})
|
|
58837
|
+
);
|
|
58838
|
+
return {
|
|
58839
|
+
ok: false,
|
|
58840
|
+
error: `${agentId} skipped (${limits.reason}${borrowed ? ", borrowed ceiling" : ""})`
|
|
58841
|
+
};
|
|
58842
|
+
}
|
|
58843
|
+
}
|
|
58844
|
+
console.log(JSON.stringify({ event: "pool_egress", persona, agentId, promptBytes: prompt.length }));
|
|
58845
|
+
return dispatchHead(agentId, prompt, input.dispatchTimeoutMs, deps);
|
|
58846
|
+
};
|
|
58847
|
+
return { agents, assignment, composition, runHead };
|
|
58848
|
+
}
|
|
58849
|
+
const built = buildFromAgents(picked.agents);
|
|
58850
|
+
const repick = async () => {
|
|
58851
|
+
const rePicked = await pickOnce();
|
|
58852
|
+
if (!rePicked.ok || rePicked.agents.length <= built.agents.length) return { ok: false };
|
|
58853
|
+
const rebuilt = buildFromAgents(rePicked.agents);
|
|
58854
|
+
return { ok: true, composition: rebuilt.composition, runHead: rebuilt.runHead };
|
|
58274
58855
|
};
|
|
58275
|
-
return { ok: true, runner: {
|
|
58856
|
+
return { ok: true, runner: { ...built, repick } };
|
|
58276
58857
|
}
|
|
58277
58858
|
|
|
58278
58859
|
// src/consensus/multi-llm-shadow.ts
|
|
@@ -58283,7 +58864,7 @@ function rollUpVerdict(verdicts) {
|
|
|
58283
58864
|
return "APPROVE";
|
|
58284
58865
|
}
|
|
58285
58866
|
async function runShadowPanel(input, deps) {
|
|
58286
|
-
const orchestratorSeat =
|
|
58867
|
+
const orchestratorSeat = input.orchestratorSeat;
|
|
58287
58868
|
const base = {
|
|
58288
58869
|
ran: false,
|
|
58289
58870
|
correlationId: input.correlationId,
|
|
@@ -58297,7 +58878,8 @@ async function runShadowPanel(input, deps) {
|
|
|
58297
58878
|
excludeProvider: input.excludeProvider,
|
|
58298
58879
|
tierPrefer: "strong",
|
|
58299
58880
|
pickTimeoutMs: input.pickTimeoutMs,
|
|
58300
|
-
dispatchTimeoutMs: input.dispatchTimeoutMs
|
|
58881
|
+
dispatchTimeoutMs: input.dispatchTimeoutMs,
|
|
58882
|
+
seat: input.seat
|
|
58301
58883
|
},
|
|
58302
58884
|
deps
|
|
58303
58885
|
);
|
|
@@ -58345,7 +58927,7 @@ async function runShadowPanelSafe(input, deps, logger = (line) => console.error(
|
|
|
58345
58927
|
correlationId: input.correlationId,
|
|
58346
58928
|
productionVerdict: input.productionVerdict,
|
|
58347
58929
|
reason: `shadow panel threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
58348
|
-
orchestratorSeat:
|
|
58930
|
+
orchestratorSeat: input.orchestratorSeat
|
|
58349
58931
|
};
|
|
58350
58932
|
}
|
|
58351
58933
|
try {
|
|
@@ -58379,13 +58961,19 @@ async function maybeRunShadow(input, deps, logger) {
|
|
|
58379
58961
|
personaInstructions[name] = "";
|
|
58380
58962
|
}
|
|
58381
58963
|
}
|
|
58964
|
+
const seat = await mintResolvedSeat(resolveProvider(process.env, input.persistedProvider));
|
|
58382
58965
|
await runShadowPanelSafe(
|
|
58383
58966
|
{
|
|
58384
58967
|
heads,
|
|
58385
58968
|
personaInstructions,
|
|
58386
58969
|
artifact: input.artifact,
|
|
58387
58970
|
productionVerdict,
|
|
58388
|
-
excludeProvider:
|
|
58971
|
+
excludeProvider: seat.capabilities.poolProviderLabels.join(","),
|
|
58972
|
+
orchestratorSeat: seat.id,
|
|
58973
|
+
// task 1.21: the same seat, forwarded so buildMultiHeadRunner's per-head dispatch path
|
|
58974
|
+
// has one in scope too (see ShadowInput.seat's own comment for why this is separate
|
|
58975
|
+
// from orchestratorSeat above).
|
|
58976
|
+
seat,
|
|
58389
58977
|
pickTimeoutMs: PICK_TIMEOUT_MS,
|
|
58390
58978
|
dispatchTimeoutMs: DISPATCH_TIMEOUT_MS,
|
|
58391
58979
|
correlationId: input.correlationId
|
|
@@ -58399,14 +58987,10 @@ async function maybeRunShadow(input, deps, logger) {
|
|
|
58399
58987
|
}
|
|
58400
58988
|
|
|
58401
58989
|
// src/consensus/multi-llm-activate.ts
|
|
58402
|
-
var MULTI_LLM_ENV_FLAG = "MRRLIN_CONSENSUS_MULTI_LLM";
|
|
58403
58990
|
var PICK_TIMEOUT_MS2 = 15e3;
|
|
58404
58991
|
var DISPATCH_TIMEOUT_MS2 = 6e4;
|
|
58405
|
-
function multiLlmActive(
|
|
58406
|
-
|
|
58407
|
-
const flag = env[MULTI_LLM_ENV_FLAG];
|
|
58408
|
-
if (flag === "0" || flag === "false" || flag === "off") return false;
|
|
58409
|
-
return Boolean(flag) || env["MRRLIN_STAGING"] === "1";
|
|
58992
|
+
function multiLlmActive(_env = process.env) {
|
|
58993
|
+
return true;
|
|
58410
58994
|
}
|
|
58411
58995
|
async function resolveActiveRunHead(input, deps) {
|
|
58412
58996
|
if (!multiLlmActive(input.env ?? process.env)) return null;
|
|
@@ -58421,6 +59005,7 @@ async function resolveActiveRunHead(input, deps) {
|
|
|
58421
59005
|
personaInstructions[name] = "";
|
|
58422
59006
|
}
|
|
58423
59007
|
}
|
|
59008
|
+
const seat = await mintResolvedSeat(resolveProvider(process.env, input.persistedProvider));
|
|
58424
59009
|
const built = await buildMultiHeadRunner(
|
|
58425
59010
|
{
|
|
58426
59011
|
personas: heads,
|
|
@@ -58429,10 +59014,12 @@ async function resolveActiveRunHead(input, deps) {
|
|
|
58429
59014
|
// Codex (["openai"]) and Claude (["anthropic"]) are single-element, so this is a
|
|
58430
59015
|
// behavior-neutral join; excludeProvider itself is a single (possibly comma-joined,
|
|
58431
59016
|
// per --exclude-providers) string, not an array.
|
|
58432
|
-
excludeProvider:
|
|
59017
|
+
excludeProvider: seat.capabilities.poolProviderLabels.join(","),
|
|
58433
59018
|
tierPrefer: "strong",
|
|
58434
59019
|
pickTimeoutMs: PICK_TIMEOUT_MS2,
|
|
58435
|
-
dispatchTimeoutMs: DISPATCH_TIMEOUT_MS2
|
|
59020
|
+
dispatchTimeoutMs: DISPATCH_TIMEOUT_MS2,
|
|
59021
|
+
// task 1.21: same seat that produced excludeProvider above, not a second mint.
|
|
59022
|
+
seat
|
|
58436
59023
|
},
|
|
58437
59024
|
deps
|
|
58438
59025
|
);
|
|
@@ -58640,7 +59227,9 @@ async function runNativeSpecGate(ctx, input, builders = defaultNativeSpecGateBui
|
|
|
58640
59227
|
specPageId: input.pageId,
|
|
58641
59228
|
cwd: ctx.cwd,
|
|
58642
59229
|
codexExecutable: ctx.codexExecutable,
|
|
58643
|
-
extraHeadCount: input.extraHeads?.length ?? 0
|
|
59230
|
+
extraHeadCount: input.extraHeads?.length ?? 0,
|
|
59231
|
+
// model-tier-planning-and-arbiter task 1.21: the seat minted at the async-job claim.
|
|
59232
|
+
seat: ctx.seat
|
|
58644
59233
|
});
|
|
58645
59234
|
return builders.runSpecGate(
|
|
58646
59235
|
{ projectSlug: input.projectSlug, specPageId: input.pageId },
|
|
@@ -58883,7 +59472,9 @@ async function run(ctx, input, builders = defaultNativeSpecGateBuilders) {
|
|
|
58883
59472
|
domain: shadowDomain,
|
|
58884
59473
|
extraHeads: input.extraHeads,
|
|
58885
59474
|
correlationId: `Specs/${input.pageId}`,
|
|
58886
|
-
|
|
59475
|
+
// task 1.21: ctx's own minted seat, not an ambient read — ctx already carries it by
|
|
59476
|
+
// the time `run` executes (async-job-consumer.ts mints before calling descriptor.run).
|
|
59477
|
+
persistedProvider: ctx.seat?.id ?? null
|
|
58887
59478
|
});
|
|
58888
59479
|
}
|
|
58889
59480
|
} catch {
|
|
@@ -59079,7 +59670,9 @@ async function run2(ctx, input) {
|
|
|
59079
59670
|
projectSlug: input.projectSlug,
|
|
59080
59671
|
deliverablePageId: input.pageId,
|
|
59081
59672
|
cwd: ctx.cwd,
|
|
59082
|
-
codexExecutable: ctx.codexExecutable
|
|
59673
|
+
codexExecutable: ctx.codexExecutable,
|
|
59674
|
+
// model-tier-planning-and-arbiter task 1.21: the seat minted at the async-job claim.
|
|
59675
|
+
seat: ctx.seat
|
|
59083
59676
|
});
|
|
59084
59677
|
const result = await runDeliverableGate(
|
|
59085
59678
|
{ projectSlug: input.projectSlug, deliverablePageId: input.pageId },
|
|
@@ -59159,6 +59752,177 @@ var deliverableConsensusDescriptor = {
|
|
|
59159
59752
|
wakeDirector: true
|
|
59160
59753
|
};
|
|
59161
59754
|
|
|
59755
|
+
// src/intent-rubric.ts
|
|
59756
|
+
var INTENT_CRITERIA = `Fire only if at least one applies. There is deliberately NO criterion for "I would like more detail" \u2014 that is what produces the flood.
|
|
59757
|
+
|
|
59758
|
+
1. Unresolved referent \u2014 names an object (site, page, product, metric, element) that does not resolve to exactly one thing.
|
|
59759
|
+
2. No observable outcome \u2014 no acceptance criterion is derivable; it is not stated what will change in the world.
|
|
59760
|
+
3. Two incompatible readings \u2014 two materially different end states both fit the text.
|
|
59761
|
+
4. Missing decisive input \u2014 a value only the operator holds (a URL, access, a choice between vendors, a target number).`;
|
|
59762
|
+
var INTENT_MANUAL_PATH_SCOPE_NOTE = `Scope of criterion 1 on the manual path: the judge receives only the title and notes \u2014 no taxonomy, no existing task titles, no wiki. Criterion 1 is therefore evaluated at face value only: it fires when a referent is unresolvable on its own terms \u2014 a demonstrative with no antecedent ("here", "this page", "\u0442\u0443\u0442"), or an explicitly deferred one ("add foobar (ask Vlad)"). It does NOT fire merely because the judge cannot personally confirm that a named thing exists.`;
|
|
59763
|
+
var INTENT_DECISION_RULES_INTRO = `Each decision rule came from an observed misfire. Each is a required non-firing test case, named rule-1 through rule-5.`;
|
|
59764
|
+
var INTENT_DECISION_RULES = {
|
|
59765
|
+
"rule-1": `A delegated pick is not an unresolved referent. "Zapier" or equivalent, "another low-friction directory" hand the choice to the doer. Criterion 1 does not fire.`,
|
|
59766
|
+
"rule-2": `A decision is a valid outcome. "Define pause-or-monitor-only policy", "decide whether to ship one public repo" \u2014 the binary is the task's output, not a missing precondition. Criterion 2 does not fire.`,
|
|
59767
|
+
"rule-3": `Interchangeable implementation methods are not two readings. Three routes to one stated goal (CloudFront Function / S3 rule / Hugo frontmatter for one 301) do not fire criterion 3. Criterion 3 needs two different end states.`,
|
|
59768
|
+
"rule-4": `A shared execution resource is not a missing decisive input. Needing an account or credentials is common to every task of its kind; firing criterion 4 there fires on all of them.`,
|
|
59769
|
+
"rule-5": `A diagnosable symptom is not an unresolved referent. For a defect report, an unnamed element on a named route is a diagnostic step, not a blocking ambiguity. Criterion 1 does not fire.`
|
|
59770
|
+
};
|
|
59771
|
+
function capQuestions(questions) {
|
|
59772
|
+
return questions.slice(0, 3);
|
|
59773
|
+
}
|
|
59774
|
+
|
|
59775
|
+
// src/async-tools/intent-check.ts
|
|
59776
|
+
var INTENT_CHECK_TOOL_NAME = "intent_check";
|
|
59777
|
+
function intentCheckTimeoutMs() {
|
|
59778
|
+
const parsed = Number(process.env["MRRLIN_INTENT_CHECK_TIMEOUT_MS"] ?? 6e4);
|
|
59779
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 6e4;
|
|
59780
|
+
}
|
|
59781
|
+
function assembleJudgeInstruction() {
|
|
59782
|
+
const rulesBlock = [
|
|
59783
|
+
INTENT_DECISION_RULES_INTRO,
|
|
59784
|
+
INTENT_DECISION_RULES["rule-1"],
|
|
59785
|
+
INTENT_DECISION_RULES["rule-2"],
|
|
59786
|
+
INTENT_DECISION_RULES["rule-3"],
|
|
59787
|
+
INTENT_DECISION_RULES["rule-4"],
|
|
59788
|
+
INTENT_DECISION_RULES["rule-5"]
|
|
59789
|
+
].join("\n");
|
|
59790
|
+
return [
|
|
59791
|
+
`Judge whether this task's intent is understood, using ONLY the four criteria and the five`,
|
|
59792
|
+
`decision rules below. Apply them at face value. Do not add tolerance rules of your own and do`,
|
|
59793
|
+
`not bias toward either verdict.`,
|
|
59794
|
+
``,
|
|
59795
|
+
INTENT_CRITERIA,
|
|
59796
|
+
``,
|
|
59797
|
+
INTENT_MANUAL_PATH_SCOPE_NOTE,
|
|
59798
|
+
``,
|
|
59799
|
+
rulesBlock,
|
|
59800
|
+
``,
|
|
59801
|
+
`Output exactly:`,
|
|
59802
|
+
`VERDICT: CLEAR | UNCLEAR`,
|
|
59803
|
+
`RESTATEMENT: <one sentence, English. Preserve every specific whose error would change execution`,
|
|
59804
|
+
` (names, URLs, numbers, scope boundaries); drop everything that would not.>`,
|
|
59805
|
+
`QUESTIONS: <empty if CLEAR. Otherwise 1-3 lines, each prefixed "- ", scope-level only: what the`,
|
|
59806
|
+
` work is, where it applies, or what counts as done. Never spec-level detail.>`
|
|
59807
|
+
].join("\n");
|
|
59808
|
+
}
|
|
59809
|
+
function buildJudgePrompt(title, notes) {
|
|
59810
|
+
return `TITLE: ${title}
|
|
59811
|
+
NOTES: ${notes ?? "(none)"}`;
|
|
59812
|
+
}
|
|
59813
|
+
function parseJudgeOutput(text) {
|
|
59814
|
+
const verdictMatch = /VERDICT:\s*\b(CLEAR|UNCLEAR)\b/i.exec(text);
|
|
59815
|
+
if (!verdictMatch) return null;
|
|
59816
|
+
const verdict = (verdictMatch[1] ?? "").toUpperCase();
|
|
59817
|
+
const restatementMatch = /RESTATEMENT:\s*([\s\S]*?)(?=\n\s*(?:VERDICT|QUESTIONS):|$)/i.exec(text);
|
|
59818
|
+
const restatement = restatementMatch?.[1]?.trim();
|
|
59819
|
+
if (!restatement) return null;
|
|
59820
|
+
const questionsMatch = /QUESTIONS:\s*([\s\S]*)/i.exec(text);
|
|
59821
|
+
const questionsBlock = questionsMatch?.[1] ?? "";
|
|
59822
|
+
const questions = capQuestions(
|
|
59823
|
+
questionsBlock.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim()).filter((line) => line.length > 0)
|
|
59824
|
+
);
|
|
59825
|
+
const result = mrrlinIntentVerdictSchema.safeParse({ verdict, restatement, questions });
|
|
59826
|
+
return result.success ? result.data : null;
|
|
59827
|
+
}
|
|
59828
|
+
async function runIntentCheck(ctx, input) {
|
|
59829
|
+
if (ctx.projectSlug) {
|
|
59830
|
+
let project;
|
|
59831
|
+
try {
|
|
59832
|
+
project = await ctx.client.getProject(ctx.projectSlug);
|
|
59833
|
+
} catch (err) {
|
|
59834
|
+
return {
|
|
59835
|
+
kind: "retry",
|
|
59836
|
+
failureReason: "provider_unavailable",
|
|
59837
|
+
error: `intent check: failed to read project settings: ${err instanceof Error ? err.message : String(err)}`,
|
|
59838
|
+
taskId: input.taskId
|
|
59839
|
+
};
|
|
59840
|
+
}
|
|
59841
|
+
if (project.intentJudgeEnabled === false) {
|
|
59842
|
+
const verdict2 = {
|
|
59843
|
+
verdict: "CLEAR",
|
|
59844
|
+
restatement: "(intent judge disabled for this project)",
|
|
59845
|
+
questions: []
|
|
59846
|
+
};
|
|
59847
|
+
return {
|
|
59848
|
+
kind: "done",
|
|
59849
|
+
resultJson: JSON.stringify(verdict2),
|
|
59850
|
+
finalReviewedHash: null,
|
|
59851
|
+
taskId: input.taskId,
|
|
59852
|
+
taskTitle: input.title
|
|
59853
|
+
};
|
|
59854
|
+
}
|
|
59855
|
+
}
|
|
59856
|
+
const exec = await runAgentExec({
|
|
59857
|
+
prompt: buildJudgePrompt(input.title, input.notes),
|
|
59858
|
+
developerInstructions: assembleJudgeInstruction(),
|
|
59859
|
+
cwd: ctx.cwd,
|
|
59860
|
+
timeoutMs: intentCheckTimeoutMs(),
|
|
59861
|
+
sandbox: "read-only",
|
|
59862
|
+
codexExecutable: ctx.codexExecutable,
|
|
59863
|
+
// model-tier-planning-and-arbiter task 2.6 (D1): the judge is pinned to the seat's strongest
|
|
59864
|
+
// model.
|
|
59865
|
+
model: ctx.seat?.models.strong
|
|
59866
|
+
});
|
|
59867
|
+
if (!exec.ok) {
|
|
59868
|
+
return {
|
|
59869
|
+
kind: "retry",
|
|
59870
|
+
failureReason: "provider_unavailable",
|
|
59871
|
+
error: `intent check failed: ${exec.error}`,
|
|
59872
|
+
taskId: input.taskId
|
|
59873
|
+
};
|
|
59874
|
+
}
|
|
59875
|
+
const verdict = parseJudgeOutput(exec.text);
|
|
59876
|
+
if (!verdict) {
|
|
59877
|
+
return {
|
|
59878
|
+
kind: "failed",
|
|
59879
|
+
failureReason: "zero_output",
|
|
59880
|
+
error: "intent judge produced unparseable output",
|
|
59881
|
+
taskId: input.taskId
|
|
59882
|
+
};
|
|
59883
|
+
}
|
|
59884
|
+
return {
|
|
59885
|
+
kind: "done",
|
|
59886
|
+
resultJson: JSON.stringify(verdict),
|
|
59887
|
+
finalReviewedHash: null,
|
|
59888
|
+
taskId: input.taskId,
|
|
59889
|
+
taskTitle: input.title
|
|
59890
|
+
};
|
|
59891
|
+
}
|
|
59892
|
+
function onComplete4(outcome, input) {
|
|
59893
|
+
if (outcome.kind !== "done") {
|
|
59894
|
+
return { kind: "info", taskId: input.taskId, suppressInbox: true, body: "" };
|
|
59895
|
+
}
|
|
59896
|
+
let verdict = null;
|
|
59897
|
+
try {
|
|
59898
|
+
verdict = mrrlinIntentVerdictSchema.parse(JSON.parse(outcome.resultJson));
|
|
59899
|
+
} catch {
|
|
59900
|
+
return { kind: "info", taskId: input.taskId, suppressInbox: true, body: "" };
|
|
59901
|
+
}
|
|
59902
|
+
if (verdict.verdict === "CLEAR") {
|
|
59903
|
+
return { kind: "info", taskId: input.taskId, suppressInbox: true, body: "" };
|
|
59904
|
+
}
|
|
59905
|
+
const body = [verdict.restatement, "", ...verdict.questions.map((q, i) => `${i + 1}. ${q}`)].join("\n");
|
|
59906
|
+
return {
|
|
59907
|
+
kind: "question",
|
|
59908
|
+
taskId: input.taskId,
|
|
59909
|
+
origin: "gate",
|
|
59910
|
+
questionOptions: null,
|
|
59911
|
+
body
|
|
59912
|
+
};
|
|
59913
|
+
}
|
|
59914
|
+
function createIntentCheckDescriptor() {
|
|
59915
|
+
return {
|
|
59916
|
+
toolName: INTENT_CHECK_TOOL_NAME,
|
|
59917
|
+
dedupKey: (input) => `intent-check:${input.taskId}`,
|
|
59918
|
+
run: runIntentCheck,
|
|
59919
|
+
onComplete: onComplete4,
|
|
59920
|
+
wakeDirector: true,
|
|
59921
|
+
appliesIntentVerdict: true
|
|
59922
|
+
};
|
|
59923
|
+
}
|
|
59924
|
+
var intentCheckDescriptor = createIntentCheckDescriptor();
|
|
59925
|
+
|
|
59162
59926
|
// src/async-tools/enqueue.ts
|
|
59163
59927
|
var import_node_crypto10 = __toESM(require("node:crypto"), 1);
|
|
59164
59928
|
async function enqueueAsyncTool(client, descriptor2, input) {
|
|
@@ -59741,6 +60505,7 @@ var COMPACT_OPT_IN = {
|
|
|
59741
60505
|
registerAsyncTool(consensusDescriptor);
|
|
59742
60506
|
registerAsyncTool(deliverableConsensusDescriptor);
|
|
59743
60507
|
registerAsyncTool(draftSpecDescriptor);
|
|
60508
|
+
registerAsyncTool(intentCheckDescriptor);
|
|
59744
60509
|
var projectScopedSchema = external_exports.object({ projectSlug: mrrlinProjectSlugSchema });
|
|
59745
60510
|
var taskScopedSchema = projectScopedSchema.extend({ taskId: mrrlinTaskIdSchema });
|
|
59746
60511
|
var planScopedSchema = projectScopedSchema.extend({ planId: mrrlinPlanIdSchema });
|
|
@@ -61037,7 +61802,7 @@ function readZodObjectShape(schema) {
|
|
|
61037
61802
|
if (candidate && typeof candidate === "object") return candidate;
|
|
61038
61803
|
return null;
|
|
61039
61804
|
}
|
|
61040
|
-
function
|
|
61805
|
+
function unwrapZodSchema(schema) {
|
|
61041
61806
|
let current2 = schema;
|
|
61042
61807
|
for (; ; ) {
|
|
61043
61808
|
const def = current2._def;
|
|
@@ -61045,18 +61810,27 @@ function jsonSchemaPrimitiveType(schema) {
|
|
|
61045
61810
|
current2 = def.innerType;
|
|
61046
61811
|
continue;
|
|
61047
61812
|
}
|
|
61813
|
+
if (def?.in) {
|
|
61814
|
+
current2 = def.in;
|
|
61815
|
+
continue;
|
|
61816
|
+
}
|
|
61048
61817
|
if (def?.schema) {
|
|
61049
61818
|
current2 = def.schema;
|
|
61050
61819
|
continue;
|
|
61051
61820
|
}
|
|
61052
61821
|
break;
|
|
61053
61822
|
}
|
|
61823
|
+
return current2;
|
|
61824
|
+
}
|
|
61825
|
+
function jsonSchemaPrimitiveType(schema) {
|
|
61826
|
+
const current2 = unwrapZodSchema(schema);
|
|
61054
61827
|
if (current2 instanceof external_exports.ZodNumber) return "number";
|
|
61055
61828
|
if (current2 instanceof external_exports.ZodBoolean) return "boolean";
|
|
61056
61829
|
return "string";
|
|
61057
61830
|
}
|
|
61058
61831
|
function jsonSchemaProperty(schema) {
|
|
61059
|
-
const
|
|
61832
|
+
const unwrapped = unwrapZodSchema(schema);
|
|
61833
|
+
const property = unwrapped instanceof external_exports.ZodArray ? { items: { type: jsonSchemaPrimitiveType(unwrapped.element) }, type: "array" } : { type: jsonSchemaPrimitiveType(schema) };
|
|
61060
61834
|
let jsonSchema = {};
|
|
61061
61835
|
try {
|
|
61062
61836
|
jsonSchema = external_exports.toJSONSchema(schema);
|
|
@@ -61308,13 +62082,13 @@ async function uninstallPrompts(codexHome, names) {
|
|
|
61308
62082
|
|
|
61309
62083
|
// src/bin.ts
|
|
61310
62084
|
function pm2Runner(args) {
|
|
61311
|
-
const r = (0,
|
|
62085
|
+
const r = (0, import_node_child_process18.spawnSync)("pm2", args, { encoding: "utf8" });
|
|
61312
62086
|
process.stderr.write(r.stdout ?? "");
|
|
61313
62087
|
process.stderr.write(r.stderr ?? "");
|
|
61314
62088
|
return { code: r.status ?? 1, stdout: r.stdout ?? "" };
|
|
61315
62089
|
}
|
|
61316
62090
|
function whichBin(bin) {
|
|
61317
|
-
const r = (0,
|
|
62091
|
+
const r = (0, import_node_child_process18.spawnSync)("which", [bin], { encoding: "utf8" });
|
|
61318
62092
|
const out = (r.stdout ?? "").trim();
|
|
61319
62093
|
return r.status === 0 && out ? out.split("\n")[0] ?? null : null;
|
|
61320
62094
|
}
|
|
@@ -61499,13 +62273,13 @@ async function main() {
|
|
|
61499
62273
|
}
|
|
61500
62274
|
case "browser-serve": {
|
|
61501
62275
|
const { command: command2, args, env } = browserServeArgs();
|
|
61502
|
-
const r = (0,
|
|
62276
|
+
const r = (0, import_node_child_process18.spawnSync)(command2, args, { stdio: "inherit", env: { ...process.env, ...env } });
|
|
61503
62277
|
process.exitCode = r.status ?? 1;
|
|
61504
62278
|
return;
|
|
61505
62279
|
}
|
|
61506
62280
|
case "external-agents-serve": {
|
|
61507
62281
|
const { command: command2, args } = externalAgentsServeArgs();
|
|
61508
|
-
const r = (0,
|
|
62282
|
+
const r = (0, import_node_child_process18.spawnSync)(command2, args, { stdio: "inherit", env: process.env });
|
|
61509
62283
|
process.exitCode = r.status ?? 1;
|
|
61510
62284
|
return;
|
|
61511
62285
|
}
|
|
@@ -61573,7 +62347,7 @@ async function main() {
|
|
|
61573
62347
|
);
|
|
61574
62348
|
}
|
|
61575
62349
|
const realProviderRunner = (command2) => (args) => {
|
|
61576
|
-
const r = (0,
|
|
62350
|
+
const r = (0, import_node_child_process18.spawnSync)(command2, args, { encoding: "utf8", timeout: 8e3 });
|
|
61577
62351
|
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
61578
62352
|
};
|
|
61579
62353
|
for (const provider of ["codex", "claude"]) {
|