@mrrlin-dev/mcp 0.4.95 → 0.4.98
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 +2071 -424
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__
|
|
|
32
32
|
isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
|
|
33
33
|
mod2
|
|
34
34
|
));
|
|
35
|
+
var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
|
|
35
36
|
|
|
36
37
|
// ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/parser.js
|
|
37
38
|
var require_parser = __commonJS({
|
|
@@ -1646,20 +1647,20 @@ var require_parse_async = __commonJS({
|
|
|
1646
1647
|
const index = 0;
|
|
1647
1648
|
const blocksize = opts.blocksize || 40960;
|
|
1648
1649
|
const parser = new TOMLParser();
|
|
1649
|
-
return new Promise((
|
|
1650
|
-
setImmediate(parseAsyncNext, index, blocksize,
|
|
1650
|
+
return new Promise((resolve2, reject) => {
|
|
1651
|
+
setImmediate(parseAsyncNext, index, blocksize, resolve2, reject);
|
|
1651
1652
|
});
|
|
1652
|
-
function parseAsyncNext(index2, blocksize2,
|
|
1653
|
+
function parseAsyncNext(index2, blocksize2, resolve2, reject) {
|
|
1653
1654
|
if (index2 >= str.length) {
|
|
1654
1655
|
try {
|
|
1655
|
-
return
|
|
1656
|
+
return resolve2(parser.finish());
|
|
1656
1657
|
} catch (err) {
|
|
1657
1658
|
return reject(prettyError(err, str));
|
|
1658
1659
|
}
|
|
1659
1660
|
}
|
|
1660
1661
|
try {
|
|
1661
1662
|
parser.parse(str.slice(index2, index2 + blocksize2));
|
|
1662
|
-
setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2,
|
|
1663
|
+
setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve2, reject);
|
|
1663
1664
|
} catch (err) {
|
|
1664
1665
|
reject(prettyError(err, str));
|
|
1665
1666
|
}
|
|
@@ -1685,7 +1686,7 @@ var require_parse_stream = __commonJS({
|
|
|
1685
1686
|
function parseReadable(stm) {
|
|
1686
1687
|
const parser = new TOMLParser();
|
|
1687
1688
|
stm.setEncoding("utf8");
|
|
1688
|
-
return new Promise((
|
|
1689
|
+
return new Promise((resolve2, reject) => {
|
|
1689
1690
|
let readable;
|
|
1690
1691
|
let ended = false;
|
|
1691
1692
|
let errored = false;
|
|
@@ -1693,7 +1694,7 @@ var require_parse_stream = __commonJS({
|
|
|
1693
1694
|
ended = true;
|
|
1694
1695
|
if (readable) return;
|
|
1695
1696
|
try {
|
|
1696
|
-
|
|
1697
|
+
resolve2(parser.finish());
|
|
1697
1698
|
} catch (err) {
|
|
1698
1699
|
reject(err);
|
|
1699
1700
|
}
|
|
@@ -4979,7 +4980,7 @@ var require_compile = __commonJS({
|
|
|
4979
4980
|
const schOrFunc = root.refs[ref];
|
|
4980
4981
|
if (schOrFunc)
|
|
4981
4982
|
return schOrFunc;
|
|
4982
|
-
let _sch =
|
|
4983
|
+
let _sch = resolve2.call(this, root, ref);
|
|
4983
4984
|
if (_sch === void 0) {
|
|
4984
4985
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
4985
4986
|
const { schemaId } = this.opts;
|
|
@@ -5006,7 +5007,7 @@ var require_compile = __commonJS({
|
|
|
5006
5007
|
function sameSchemaEnv(s1, s2) {
|
|
5007
5008
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
5008
5009
|
}
|
|
5009
|
-
function
|
|
5010
|
+
function resolve2(root, ref) {
|
|
5010
5011
|
let sch;
|
|
5011
5012
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
5012
5013
|
ref = sch;
|
|
@@ -5836,7 +5837,7 @@ var require_fast_uri = __commonJS({
|
|
|
5836
5837
|
}
|
|
5837
5838
|
return uri;
|
|
5838
5839
|
}
|
|
5839
|
-
function
|
|
5840
|
+
function resolve2(baseURI, relativeURI, options) {
|
|
5840
5841
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
5841
5842
|
const {
|
|
5842
5843
|
parsed: baseParsed,
|
|
@@ -6205,7 +6206,7 @@ var require_fast_uri = __commonJS({
|
|
|
6205
6206
|
var fastUri = {
|
|
6206
6207
|
SCHEMES,
|
|
6207
6208
|
normalize: normalize2,
|
|
6208
|
-
resolve,
|
|
6209
|
+
resolve: resolve2,
|
|
6209
6210
|
resolveComponent,
|
|
6210
6211
|
equal,
|
|
6211
6212
|
serialize,
|
|
@@ -9392,12 +9393,12 @@ var require_isexe = __commonJS({
|
|
|
9392
9393
|
if (typeof Promise !== "function") {
|
|
9393
9394
|
throw new TypeError("callback not provided");
|
|
9394
9395
|
}
|
|
9395
|
-
return new Promise(function(
|
|
9396
|
+
return new Promise(function(resolve2, reject) {
|
|
9396
9397
|
isexe(path29, options || {}, function(er, is) {
|
|
9397
9398
|
if (er) {
|
|
9398
9399
|
reject(er);
|
|
9399
9400
|
} else {
|
|
9400
|
-
|
|
9401
|
+
resolve2(is);
|
|
9401
9402
|
}
|
|
9402
9403
|
});
|
|
9403
9404
|
});
|
|
@@ -9463,27 +9464,27 @@ var require_which = __commonJS({
|
|
|
9463
9464
|
opt = {};
|
|
9464
9465
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
9465
9466
|
const found = [];
|
|
9466
|
-
const step = (i) => new Promise((
|
|
9467
|
+
const step = (i) => new Promise((resolve2, reject) => {
|
|
9467
9468
|
if (i === pathEnv.length)
|
|
9468
|
-
return opt.all && found.length ?
|
|
9469
|
+
return opt.all && found.length ? resolve2(found) : reject(getNotFoundError(cmd));
|
|
9469
9470
|
const ppRaw = pathEnv[i];
|
|
9470
9471
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
9471
9472
|
const pCmd = path29.join(pathPart, cmd);
|
|
9472
9473
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
9473
|
-
|
|
9474
|
+
resolve2(subStep(p, i, 0));
|
|
9474
9475
|
});
|
|
9475
|
-
const subStep = (p, i, ii) => new Promise((
|
|
9476
|
+
const subStep = (p, i, ii) => new Promise((resolve2, reject) => {
|
|
9476
9477
|
if (ii === pathExt.length)
|
|
9477
|
-
return
|
|
9478
|
+
return resolve2(step(i + 1));
|
|
9478
9479
|
const ext = pathExt[ii];
|
|
9479
9480
|
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
9480
9481
|
if (!er && is) {
|
|
9481
9482
|
if (opt.all)
|
|
9482
9483
|
found.push(p + ext);
|
|
9483
9484
|
else
|
|
9484
|
-
return
|
|
9485
|
+
return resolve2(p + ext);
|
|
9485
9486
|
}
|
|
9486
|
-
return
|
|
9487
|
+
return resolve2(subStep(p, i, ii + 1));
|
|
9487
9488
|
});
|
|
9488
9489
|
});
|
|
9489
9490
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -14016,7 +14017,7 @@ var require_websocket = __commonJS({
|
|
|
14016
14017
|
var http2 = require("http");
|
|
14017
14018
|
var net = require("net");
|
|
14018
14019
|
var tls = require("tls");
|
|
14019
|
-
var { randomBytes: randomBytes5, createHash:
|
|
14020
|
+
var { randomBytes: randomBytes5, createHash: createHash6 } = require("crypto");
|
|
14020
14021
|
var { Duplex, Readable } = require("stream");
|
|
14021
14022
|
var { URL: URL2 } = require("url");
|
|
14022
14023
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -14684,7 +14685,7 @@ var require_websocket = __commonJS({
|
|
|
14684
14685
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
14685
14686
|
return;
|
|
14686
14687
|
}
|
|
14687
|
-
const digest =
|
|
14688
|
+
const digest = createHash6("sha1").update(key + GUID).digest("base64");
|
|
14688
14689
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
14689
14690
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
14690
14691
|
return;
|
|
@@ -15053,7 +15054,7 @@ var require_websocket_server = __commonJS({
|
|
|
15053
15054
|
var EventEmitter3 = require("events");
|
|
15054
15055
|
var http2 = require("http");
|
|
15055
15056
|
var { Duplex } = require("stream");
|
|
15056
|
-
var { createHash:
|
|
15057
|
+
var { createHash: createHash6 } = require("crypto");
|
|
15057
15058
|
var extension2 = require_extension();
|
|
15058
15059
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
15059
15060
|
var subprotocol2 = require_subprotocol();
|
|
@@ -15360,7 +15361,7 @@ var require_websocket_server = __commonJS({
|
|
|
15360
15361
|
);
|
|
15361
15362
|
}
|
|
15362
15363
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
15363
|
-
const digest =
|
|
15364
|
+
const digest = createHash6("sha1").update(key + GUID).digest("base64");
|
|
15364
15365
|
const headers = [
|
|
15365
15366
|
"HTTP/1.1 101 Switching Protocols",
|
|
15366
15367
|
"Upgrade: websocket",
|
|
@@ -16946,12 +16947,12 @@ var require_adapter = __commonJS({
|
|
|
16946
16947
|
return newFs;
|
|
16947
16948
|
}
|
|
16948
16949
|
function toPromise(method) {
|
|
16949
|
-
return (...args) => new Promise((
|
|
16950
|
+
return (...args) => new Promise((resolve2, reject) => {
|
|
16950
16951
|
args.push((err, result) => {
|
|
16951
16952
|
if (err) {
|
|
16952
16953
|
reject(err);
|
|
16953
16954
|
} else {
|
|
16954
|
-
|
|
16955
|
+
resolve2(result);
|
|
16955
16956
|
}
|
|
16956
16957
|
});
|
|
16957
16958
|
method(...args);
|
|
@@ -21405,10 +21406,10 @@ var require_browser = __commonJS({
|
|
|
21405
21406
|
text = canvas;
|
|
21406
21407
|
canvas = void 0;
|
|
21407
21408
|
}
|
|
21408
|
-
return new Promise(function(
|
|
21409
|
+
return new Promise(function(resolve2, reject) {
|
|
21409
21410
|
try {
|
|
21410
21411
|
const data = QRCode2.create(text, opts);
|
|
21411
|
-
|
|
21412
|
+
resolve2(renderFunc(data, canvas, opts));
|
|
21412
21413
|
} catch (e) {
|
|
21413
21414
|
reject(e);
|
|
21414
21415
|
}
|
|
@@ -21489,11 +21490,11 @@ var require_server = __commonJS({
|
|
|
21489
21490
|
}
|
|
21490
21491
|
function render(renderFunc, text, params) {
|
|
21491
21492
|
if (!params.cb) {
|
|
21492
|
-
return new Promise(function(
|
|
21493
|
+
return new Promise(function(resolve2, reject) {
|
|
21493
21494
|
try {
|
|
21494
21495
|
const data = QRCode2.create(text, params.opts);
|
|
21495
21496
|
return renderFunc(data, params.opts, function(err, data2) {
|
|
21496
|
-
return err ? reject(err) :
|
|
21497
|
+
return err ? reject(err) : resolve2(data2);
|
|
21497
21498
|
});
|
|
21498
21499
|
} catch (e) {
|
|
21499
21500
|
reject(e);
|
|
@@ -21655,6 +21656,11 @@ var init_install_public_mcp = __esm({
|
|
|
21655
21656
|
});
|
|
21656
21657
|
|
|
21657
21658
|
// src/bin.ts
|
|
21659
|
+
var bin_exports = {};
|
|
21660
|
+
__export(bin_exports, {
|
|
21661
|
+
augmentProcessPathWithCommonDirs: () => augmentProcessPathWithCommonDirs
|
|
21662
|
+
});
|
|
21663
|
+
module.exports = __toCommonJS(bin_exports);
|
|
21658
21664
|
var import_node_path42 = __toESM(require("node:path"), 1);
|
|
21659
21665
|
var import_node_child_process22 = require("node:child_process");
|
|
21660
21666
|
var import_node_fs25 = require("node:fs");
|
|
@@ -37372,6 +37378,7 @@ var openApiOperationIds = {
|
|
|
37372
37378
|
promoteSpecReadyCas: "promoteSpecReadyCas",
|
|
37373
37379
|
recordSpecConsensusReview: "recordSpecConsensusReview",
|
|
37374
37380
|
getSpecConsensusReview: "getSpecConsensusReview",
|
|
37381
|
+
finalizeSpecReviewCas: "finalizeSpecReviewCas",
|
|
37375
37382
|
onboardingKickoffTransition: "onboardingKickoffTransition",
|
|
37376
37383
|
ingestTelemetry: "ingestTelemetry",
|
|
37377
37384
|
ingestOperatorTelemetry: "ingestOperatorTelemetry",
|
|
@@ -37642,6 +37649,63 @@ var PERSONA_ROLE_MAP = {
|
|
|
37642
37649
|
"spec-author": "spec"
|
|
37643
37650
|
};
|
|
37644
37651
|
var UNNAMED_PERSONAS = ["plan-reviewer", "scope-analyst"];
|
|
37652
|
+
var mrrlinSpecOwnershipSnapshotSchema = external_exports.discriminatedUnion("kind", [
|
|
37653
|
+
external_exports.object({
|
|
37654
|
+
kind: external_exports.literal("task"),
|
|
37655
|
+
taskId: external_exports.string().min(1),
|
|
37656
|
+
domains: external_exports.array(external_exports.string()).default(["code"])
|
|
37657
|
+
}),
|
|
37658
|
+
external_exports.object({
|
|
37659
|
+
kind: external_exports.literal("plan"),
|
|
37660
|
+
planId: external_exports.string().min(1),
|
|
37661
|
+
domains: external_exports.array(external_exports.string()).default(["code"])
|
|
37662
|
+
}),
|
|
37663
|
+
external_exports.object({
|
|
37664
|
+
kind: external_exports.literal("project"),
|
|
37665
|
+
domains: external_exports.array(external_exports.string()).default(["code"])
|
|
37666
|
+
})
|
|
37667
|
+
]);
|
|
37668
|
+
var SPEC_CONSENSUS_OUTCOMES = [
|
|
37669
|
+
"CONVERGED",
|
|
37670
|
+
"approved",
|
|
37671
|
+
"CONSENSUS_BLOCKED",
|
|
37672
|
+
"CONSENSUS_BLOCKED_CLARIFICATION",
|
|
37673
|
+
"SKIPPED"
|
|
37674
|
+
];
|
|
37675
|
+
var specConsensusOutcomeSchema = external_exports.enum(SPEC_CONSENSUS_OUTCOMES);
|
|
37676
|
+
var mrrlinFinalizeSpecReviewCasBodySchema = external_exports.object({
|
|
37677
|
+
expectedContentHash: external_exports.string().min(1),
|
|
37678
|
+
reviewHash: external_exports.string().min(1).optional(),
|
|
37679
|
+
ownership: mrrlinSpecOwnershipSnapshotSchema,
|
|
37680
|
+
review: external_exports.object({
|
|
37681
|
+
outcome: specConsensusOutcomeSchema,
|
|
37682
|
+
rounds: external_exports.number().int().nonnegative().default(0),
|
|
37683
|
+
suppressionEvidence: mrrlinSpecConsensusSuppressionEvidenceSchema.nullish(),
|
|
37684
|
+
history: mrrlinSpecConsensusHistorySchema.nullish(),
|
|
37685
|
+
highlights: mrrlinSpecConsensusHighlightsSchema.nullish()
|
|
37686
|
+
}),
|
|
37687
|
+
specReady: external_exports.boolean().default(false)
|
|
37688
|
+
}).refine((data) => {
|
|
37689
|
+
if (data.specReady && (data.review.outcome === "CONSENSUS_BLOCKED" || data.review.outcome === "CONSENSUS_BLOCKED_CLARIFICATION")) {
|
|
37690
|
+
return false;
|
|
37691
|
+
}
|
|
37692
|
+
return true;
|
|
37693
|
+
}, {
|
|
37694
|
+
message: "specReady cannot be true when review outcome is blocked",
|
|
37695
|
+
path: ["specReady"]
|
|
37696
|
+
});
|
|
37697
|
+
var mrrlinFinalizeSpecReviewCasResponseSchema = external_exports.object({
|
|
37698
|
+
data: external_exports.discriminatedUnion("status", [
|
|
37699
|
+
external_exports.object({
|
|
37700
|
+
status: external_exports.literal("applied"),
|
|
37701
|
+
promoted: external_exports.boolean()
|
|
37702
|
+
}),
|
|
37703
|
+
external_exports.object({
|
|
37704
|
+
status: external_exports.literal("superseded"),
|
|
37705
|
+
reason: external_exports.string()
|
|
37706
|
+
})
|
|
37707
|
+
])
|
|
37708
|
+
});
|
|
37645
37709
|
|
|
37646
37710
|
// ../../packages/schemas/dist/asyncJob.js
|
|
37647
37711
|
var ASYNC_JOB_STATUSES = ["queued", "running", "done", "failed", "superseded"];
|
|
@@ -37713,7 +37777,7 @@ var mrrlinAsyncJobBeginAttemptSchema = external_exports.object({
|
|
|
37713
37777
|
});
|
|
37714
37778
|
var mrrlinAsyncJobTerminalizeSchema = external_exports.object({
|
|
37715
37779
|
leaseId: external_exports.string().min(1),
|
|
37716
|
-
status: external_exports.enum(["done", "failed"]),
|
|
37780
|
+
status: external_exports.enum(["done", "failed", "superseded"]),
|
|
37717
37781
|
resultJson: external_exports.string().nullable().optional(),
|
|
37718
37782
|
error: external_exports.string().nullable().optional(),
|
|
37719
37783
|
failureReason: mrrlinAsyncJobFailureReasonSchema.nullable().optional(),
|
|
@@ -38016,13 +38080,36 @@ var DEFAULT_INTEGRATION_ROUTES = {
|
|
|
38016
38080
|
operations: {
|
|
38017
38081
|
dispatch: "slack_post_message"
|
|
38018
38082
|
}
|
|
38083
|
+
},
|
|
38084
|
+
publish: {
|
|
38085
|
+
capability: "publish",
|
|
38086
|
+
artifactKind: "page",
|
|
38087
|
+
operations: {
|
|
38088
|
+
preview: "publish_preview",
|
|
38089
|
+
release: "publish_release",
|
|
38090
|
+
rollback: "publish_rollback",
|
|
38091
|
+
observe: "publish_observe"
|
|
38092
|
+
}
|
|
38093
|
+
},
|
|
38094
|
+
"local-preview": {
|
|
38095
|
+
capability: "publish",
|
|
38096
|
+
artifactKind: "page",
|
|
38097
|
+
operations: {
|
|
38098
|
+
preview: "publish_preview",
|
|
38099
|
+
release: "publish_release",
|
|
38100
|
+
rollback: "publish_rollback",
|
|
38101
|
+
observe: "publish_observe"
|
|
38102
|
+
}
|
|
38019
38103
|
}
|
|
38020
38104
|
};
|
|
38021
38105
|
var SERVER_ALIASES = {
|
|
38022
38106
|
"github-mcp": "github",
|
|
38023
38107
|
gh: "github",
|
|
38024
38108
|
"github-server": "github",
|
|
38025
|
-
"slack-mcp": "slack"
|
|
38109
|
+
"slack-mcp": "slack",
|
|
38110
|
+
"static-publisher": "publish",
|
|
38111
|
+
"local-publisher": "publish",
|
|
38112
|
+
preview: "local-preview"
|
|
38026
38113
|
};
|
|
38027
38114
|
function getDefaultCapabilityRouteForServer(serverId) {
|
|
38028
38115
|
const normalizedId = (SERVER_ALIASES[serverId.toLowerCase()] ?? serverId).toLowerCase();
|
|
@@ -38052,10 +38139,14 @@ var mrrlinTaskEventAutonomyLevelSchema = external_exports.enum([
|
|
|
38052
38139
|
]);
|
|
38053
38140
|
var mrrlinAutoDeploySchema = external_exports.enum(["off", "dev", "prod"]);
|
|
38054
38141
|
var mrrlinTaskDomainsSchema = external_exports.array(external_exports.string()).min(1);
|
|
38055
|
-
function
|
|
38056
|
-
const d = Array.isArray(
|
|
38142
|
+
function normalizeDomainArray(domains) {
|
|
38143
|
+
const d = Array.isArray(domains) ? domains.map(String) : [String(domains || "code")];
|
|
38057
38144
|
const normalized = d.map((v) => v.trim().toLowerCase()).filter(Boolean);
|
|
38058
38145
|
normalized.sort();
|
|
38146
|
+
return normalized.length === 0 ? ["code"] : Array.from(new Set(normalized));
|
|
38147
|
+
}
|
|
38148
|
+
function domainScopedReviewHash(rawHash, domain2) {
|
|
38149
|
+
const normalized = normalizeDomainArray(domain2);
|
|
38059
38150
|
return normalized.length === 1 && normalized[0] === "code" ? rawHash : `${rawHash}@${normalized.join(",")}`;
|
|
38060
38151
|
}
|
|
38061
38152
|
var mrrlinDeclaredOutcomeShapeSchema = external_exports.object({
|
|
@@ -38355,7 +38446,8 @@ var mrrlinTaskSchema = external_exports.object({
|
|
|
38355
38446
|
// ADR 0020. OUTPUT-optional (not defaulted-required) so hardcoded task fixtures/mocks across the
|
|
38356
38447
|
// codebase need no churn; the DB column is NOT NULL default 'code', so real rows always carry it,
|
|
38357
38448
|
// and resolveHeads() treats an absent domain as `code`. Persisted create/update use their own forms.
|
|
38358
|
-
domains
|
|
38449
|
+
// Preprocesses null to undefined so tasks with null domains deserialize gracefully without widening MrrlinTask['domains'] to null.
|
|
38450
|
+
domains: external_exports.preprocess((val) => val === null ? void 0 : val, mrrlinTaskDomainsSchema.optional()),
|
|
38359
38451
|
autonomyLevel: mrrlinAutonomyLevelSchema,
|
|
38360
38452
|
autonomyReason: external_exports.string().min(1).max(240),
|
|
38361
38453
|
blockedBy: external_exports.array(mrrlinTaskIdSchema),
|
|
@@ -38470,14 +38562,17 @@ var mrrlinWikiPageSchema = external_exports.object({
|
|
|
38470
38562
|
createdAt: external_exports.string().datetime().nullable().default(null),
|
|
38471
38563
|
folder: external_exports.string().min(1),
|
|
38472
38564
|
id: mrrlinWikiPageIdSchema,
|
|
38473
|
-
markdownBody: external_exports.string().
|
|
38565
|
+
markdownBody: external_exports.string().default(""),
|
|
38474
38566
|
path: mrrlinWikiPagePathSchema,
|
|
38475
38567
|
preview: external_exports.string(),
|
|
38476
38568
|
specReady: external_exports.boolean().default(false),
|
|
38477
38569
|
taskId: mrrlinTaskIdSchema.nullable().default(null),
|
|
38478
|
-
title: external_exports.string().
|
|
38570
|
+
title: external_exports.string().default("Untitled"),
|
|
38479
38571
|
updatedAt: external_exports.string().datetime().nullable().default(null)
|
|
38480
38572
|
});
|
|
38573
|
+
var mrrlinWikiPageSummarySchema = mrrlinWikiPageSchema.omit({
|
|
38574
|
+
markdownBody: true
|
|
38575
|
+
});
|
|
38481
38576
|
var mrrlinMarkSpecReadyRequestSchema = external_exports.object({
|
|
38482
38577
|
ready: external_exports.boolean()
|
|
38483
38578
|
});
|
|
@@ -38628,6 +38723,7 @@ var mrrlinTaskCreateShapeSchema = external_exports.object({
|
|
|
38628
38723
|
// "caller omitted this" apart from "caller explicitly chose off" -- omitted falls through to
|
|
38629
38724
|
// the preview-only constant "dev"; the tasks column itself still defaults to 'off' at the
|
|
38630
38725
|
// SQL level for any direct-insert path that bypasses createTask entirely.
|
|
38726
|
+
assignee: mrrlinTaskAssigneeSchema.optional(),
|
|
38631
38727
|
autoDeploy: mrrlinAutoDeploySchema.optional(),
|
|
38632
38728
|
domains: mrrlinTaskDomainsSchema.optional(),
|
|
38633
38729
|
autonomyLevel: mrrlinAutonomyLevelSchema.default("human_review"),
|
|
@@ -38772,6 +38868,7 @@ var mrrlinTaskListFiltersSchema = external_exports.object({
|
|
|
38772
38868
|
category: taxonomyCategorySchema.optional(),
|
|
38773
38869
|
dueDate: external_exports.string().date().optional(),
|
|
38774
38870
|
impact: mrrlinTaskImpactSchema.optional(),
|
|
38871
|
+
limit: external_exports.coerce.number().int().positive().optional(),
|
|
38775
38872
|
q: external_exports.string().trim().min(1).optional(),
|
|
38776
38873
|
status: mrrlinTaskStatusSchema.optional(),
|
|
38777
38874
|
subcategory: taxonomySubcategorySchema.optional(),
|
|
@@ -38837,11 +38934,14 @@ var mrrlinWikiPageUpdateSchema = external_exports.object({
|
|
|
38837
38934
|
taskId: mrrlinTaskIdSchema.nullable().optional(),
|
|
38838
38935
|
title: external_exports.string().min(1).optional()
|
|
38839
38936
|
}).refine((value) => Object.keys(value).length > 0, { message: "WikiPageUpdate requires at least one field" });
|
|
38937
|
+
var mrrlinWikiPageViewSchema = external_exports.enum(["summary"]);
|
|
38840
38938
|
var mrrlinWikiPageListFiltersSchema = external_exports.object({
|
|
38841
38939
|
folder: external_exports.string().min(1).optional(),
|
|
38842
38940
|
id: mrrlinWikiPageIdSchema.optional(),
|
|
38941
|
+
limit: external_exports.coerce.number().int().positive().optional(),
|
|
38843
38942
|
q: external_exports.string().min(1).optional(),
|
|
38844
|
-
taskId: mrrlinTaskIdSchema.optional()
|
|
38943
|
+
taskId: mrrlinTaskIdSchema.optional(),
|
|
38944
|
+
view: mrrlinWikiPageViewSchema.optional()
|
|
38845
38945
|
});
|
|
38846
38946
|
var mrrlinWikiPageSearchQuerySchema = external_exports.object({
|
|
38847
38947
|
folder: external_exports.string().min(1).optional(),
|
|
@@ -38858,10 +38958,10 @@ var mrrlinWikiPageSearchResultSchema = external_exports.object({
|
|
|
38858
38958
|
memoryClass: external_exports.enum(["normative", "historical"]),
|
|
38859
38959
|
path: external_exports.string().min(1),
|
|
38860
38960
|
precedence: external_exports.union([external_exports.literal(0), external_exports.literal(1), external_exports.literal(2), external_exports.literal(3)]).nullable(),
|
|
38861
|
-
preview: external_exports.string(),
|
|
38961
|
+
preview: external_exports.string().default(""),
|
|
38862
38962
|
rank: external_exports.number(),
|
|
38863
|
-
snippet: external_exports.string(),
|
|
38864
|
-
title: external_exports.string().
|
|
38963
|
+
snippet: external_exports.string().default(""),
|
|
38964
|
+
title: external_exports.string().default(""),
|
|
38865
38965
|
updatedAt: external_exports.string().datetime().nullable()
|
|
38866
38966
|
});
|
|
38867
38967
|
var wikiKnowledgeChunkSchema = external_exports.object({
|
|
@@ -39037,6 +39137,9 @@ var wikiPageResponseSchema = external_exports.object({
|
|
|
39037
39137
|
var wikiPageListResponseSchema = external_exports.object({
|
|
39038
39138
|
data: external_exports.array(mrrlinWikiPageSchema)
|
|
39039
39139
|
});
|
|
39140
|
+
var wikiPageSummaryListResponseSchema = external_exports.object({
|
|
39141
|
+
data: external_exports.array(mrrlinWikiPageSummarySchema)
|
|
39142
|
+
});
|
|
39040
39143
|
var wikiPageSearchResponseSchema = external_exports.object({
|
|
39041
39144
|
data: external_exports.array(mrrlinWikiPageSearchResultSchema)
|
|
39042
39145
|
});
|
|
@@ -39508,6 +39611,7 @@ var mrrlinSchedulerParkedReasonSchema = external_exports.enum([
|
|
|
39508
39611
|
"running",
|
|
39509
39612
|
"awaiting_handoff",
|
|
39510
39613
|
"awaiting_clarification",
|
|
39614
|
+
"consensus_blocked",
|
|
39511
39615
|
"spec_awaiting_approval",
|
|
39512
39616
|
"blocked_by_active",
|
|
39513
39617
|
// Derived, not stored (design.md, "Parking is derived"): an open `question` inbox item with
|
|
@@ -39541,7 +39645,8 @@ var mrrlinInFlightSchedulerEntrySchema = external_exports.discriminatedUnion("ki
|
|
|
39541
39645
|
toolName: external_exports.enum(["draft_task_spec", "run_spec_consensus"]),
|
|
39542
39646
|
// Additive: same resolution as the `run` variant above. null when `taskId` is null (a job
|
|
39543
39647
|
// genuinely untied to a visible task) or the task could not be found.
|
|
39544
|
-
taskTitle: external_exports.string().nullable()
|
|
39648
|
+
taskTitle: external_exports.string().nullable(),
|
|
39649
|
+
specPageId: external_exports.string().nullable().optional()
|
|
39545
39650
|
})
|
|
39546
39651
|
]);
|
|
39547
39652
|
var mrrlinIneligibleTaskSchema = external_exports.object({
|
|
@@ -39735,12 +39840,35 @@ var mrrlinInboxItemDecideSchema = external_exports.object({
|
|
|
39735
39840
|
});
|
|
39736
39841
|
}
|
|
39737
39842
|
});
|
|
39843
|
+
var mrrlinInboxItemSummarySchema = external_exports.object({
|
|
39844
|
+
answer: external_exports.string().nullable(),
|
|
39845
|
+
createdAt: external_exports.string().datetime(),
|
|
39846
|
+
createdBy: external_exports.string().min(1),
|
|
39847
|
+
decidedAt: external_exports.string().datetime().nullable(),
|
|
39848
|
+
decidedBy: external_exports.string().min(1).nullable(),
|
|
39849
|
+
dedupKey: external_exports.string().nullable(),
|
|
39850
|
+
directorSessionId: external_exports.string().min(1).nullable(),
|
|
39851
|
+
groupId: external_exports.string().nullable().default(null),
|
|
39852
|
+
id: mrrlinInboxItemIdSchema,
|
|
39853
|
+
kind: mrrlinInboxItemKindSchema,
|
|
39854
|
+
origin: external_exports.string().nullable().optional(),
|
|
39855
|
+
planId: mrrlinPlanIdSchema.nullable().default(null),
|
|
39856
|
+
projectSlug: mrrlinProjectSlugSchema,
|
|
39857
|
+
readAt: external_exports.string().datetime().nullable(),
|
|
39858
|
+
reason: external_exports.string().nullable().optional(),
|
|
39859
|
+
status: mrrlinInboxItemStatusSchema,
|
|
39860
|
+
taskId: mrrlinTaskIdSchema.nullable(),
|
|
39861
|
+
taskTitle: external_exports.string().nullable()
|
|
39862
|
+
});
|
|
39863
|
+
var mrrlinInboxItemViewSchema = external_exports.enum(["summary"]);
|
|
39738
39864
|
var mrrlinInboxItemListFiltersSchema = external_exports.object({
|
|
39739
39865
|
groupId: external_exports.string().optional(),
|
|
39740
39866
|
kind: mrrlinInboxItemKindSchema.optional(),
|
|
39867
|
+
limit: external_exports.coerce.number().int().positive().optional(),
|
|
39741
39868
|
readState: external_exports.enum(["read", "unread"]).optional(),
|
|
39742
39869
|
status: mrrlinInboxItemStatusSchema.optional(),
|
|
39743
|
-
taskId: mrrlinTaskIdSchema.optional()
|
|
39870
|
+
taskId: mrrlinTaskIdSchema.optional(),
|
|
39871
|
+
view: mrrlinInboxItemViewSchema.optional()
|
|
39744
39872
|
});
|
|
39745
39873
|
var mrrlinIntentVerdictSchema = external_exports.object({
|
|
39746
39874
|
verdict: external_exports.enum(["CLEAR", "UNCLEAR"]),
|
|
@@ -39784,6 +39912,7 @@ var inboxItemsMarkReadResponseSchema = external_exports.object({
|
|
|
39784
39912
|
});
|
|
39785
39913
|
var inboxItemResponseSchema = external_exports.object({ data: mrrlinInboxItemSchema });
|
|
39786
39914
|
var inboxItemListResponseSchema = external_exports.object({ data: external_exports.array(mrrlinInboxItemSchema) });
|
|
39915
|
+
var inboxItemSummaryListResponseSchema = external_exports.object({ data: external_exports.array(mrrlinInboxItemSummarySchema) });
|
|
39787
39916
|
var capabilityServerIdSchema = external_exports.string().trim().min(1).max(200);
|
|
39788
39917
|
var capabilityNameSchema = external_exports.string().trim().min(1).max(200);
|
|
39789
39918
|
var capabilityOperationNameSchema = external_exports.string().min(1).max(100).regex(/^[a-z][a-z0-9_]*$/, "an operation name must be lower_snake_case, starting with a letter");
|
|
@@ -39984,11 +40113,12 @@ function buildTaskListPath(projectSlug, filters, view) {
|
|
|
39984
40113
|
category: filters.category,
|
|
39985
40114
|
dueDate: filters.dueDate,
|
|
39986
40115
|
impact: filters.impact,
|
|
40116
|
+
limit: filters.limit === void 0 ? void 0 : String(filters.limit),
|
|
39987
40117
|
q: filters.q,
|
|
39988
40118
|
status: filters.status,
|
|
39989
40119
|
subcategory: filters.subcategory,
|
|
39990
40120
|
type: filters.type,
|
|
39991
|
-
view
|
|
40121
|
+
view: view ?? filters.view
|
|
39992
40122
|
});
|
|
39993
40123
|
}
|
|
39994
40124
|
var MrrlinApiError = class extends Error {
|
|
@@ -40253,28 +40383,35 @@ function createMrrlinClient(config2) {
|
|
|
40253
40383
|
// this one implementation signature; MrrlinClient's declared overloads keep callers precise.
|
|
40254
40384
|
async listTasks(projectSlug, filters = {}, options) {
|
|
40255
40385
|
const path29 = buildTaskListPath(projectSlug, filters, options?.view);
|
|
40256
|
-
const body = await request(path29);
|
|
40386
|
+
const body = await request(path29, options?.signal ? { signal: options.signal } : void 0);
|
|
40257
40387
|
if (options?.view === "summary") {
|
|
40258
40388
|
return taskSummaryListResponseSchema.parse(body).data;
|
|
40259
40389
|
}
|
|
40260
40390
|
return taskListResponseSchema.parse(body).data;
|
|
40261
40391
|
},
|
|
40262
|
-
async listTaskSummaries(projectSlug, filters = {}) {
|
|
40392
|
+
async listTaskSummaries(projectSlug, filters = {}, options) {
|
|
40263
40393
|
const path29 = buildTaskListPath(projectSlug, filters, "summary");
|
|
40264
|
-
const body = await request(path29);
|
|
40394
|
+
const body = await request(path29, options?.signal ? { signal: options.signal } : void 0);
|
|
40265
40395
|
return taskSummaryListResponseSchema.parse(body).data;
|
|
40266
40396
|
},
|
|
40267
40397
|
async listTaxonomy(projectSlug) {
|
|
40268
40398
|
const body = await request(`${projectPath(projectSlug)}/taxonomy`);
|
|
40269
40399
|
return taxonomyListResponseSchema.parse(body).data;
|
|
40270
40400
|
},
|
|
40271
|
-
async listWikiPages(projectSlug, filters = {}) {
|
|
40401
|
+
async listWikiPages(projectSlug, filters = {}, options) {
|
|
40402
|
+
const view = options?.view ?? filters.view;
|
|
40272
40403
|
const path29 = appendQuery(`${projectPath(projectSlug)}/wiki`, {
|
|
40273
40404
|
folder: filters.folder,
|
|
40405
|
+
id: filters.id,
|
|
40406
|
+
limit: filters.limit === void 0 ? void 0 : String(filters.limit),
|
|
40274
40407
|
q: filters.q,
|
|
40275
|
-
taskId: filters.taskId
|
|
40408
|
+
taskId: filters.taskId,
|
|
40409
|
+
view
|
|
40276
40410
|
});
|
|
40277
|
-
const body = await request(path29);
|
|
40411
|
+
const body = await request(path29, options?.signal ? { signal: options.signal } : void 0);
|
|
40412
|
+
if (view === "summary") {
|
|
40413
|
+
return wikiPageSummaryListResponseSchema.parse(body).data;
|
|
40414
|
+
}
|
|
40278
40415
|
return wikiPageListResponseSchema.parse(body).data;
|
|
40279
40416
|
},
|
|
40280
40417
|
async searchWikiPages(projectSlug, query) {
|
|
@@ -40561,15 +40698,21 @@ function createMrrlinClient(config2) {
|
|
|
40561
40698
|
});
|
|
40562
40699
|
return capabilityUseResponseSchema.parse(body).data;
|
|
40563
40700
|
},
|
|
40564
|
-
async listInboxItems(projectSlug, filters = {}) {
|
|
40701
|
+
async listInboxItems(projectSlug, filters = {}, options) {
|
|
40702
|
+
const view = options?.view ?? filters.view;
|
|
40565
40703
|
const url2 = appendQuery(`${projectPath(projectSlug)}/inbox`, {
|
|
40566
40704
|
groupId: filters.groupId,
|
|
40567
40705
|
kind: filters.kind,
|
|
40706
|
+
limit: filters.limit === void 0 ? void 0 : String(filters.limit),
|
|
40568
40707
|
readState: filters.readState,
|
|
40569
40708
|
status: filters.status,
|
|
40570
|
-
taskId: filters.taskId
|
|
40709
|
+
taskId: filters.taskId,
|
|
40710
|
+
view
|
|
40571
40711
|
});
|
|
40572
|
-
const body = await request(url2);
|
|
40712
|
+
const body = await request(url2, options?.signal ? { signal: options.signal } : void 0);
|
|
40713
|
+
if (view === "summary") {
|
|
40714
|
+
return inboxItemSummaryListResponseSchema.parse(body).data;
|
|
40715
|
+
}
|
|
40573
40716
|
return inboxItemListResponseSchema.parse(body).data;
|
|
40574
40717
|
},
|
|
40575
40718
|
async createInboxItem(projectSlug, input) {
|
|
@@ -40952,6 +41095,14 @@ function createMrrlinClient(config2) {
|
|
|
40952
41095
|
const body = await request(url2);
|
|
40953
41096
|
return specConsensusReviewMaybeResponseSchema.parse(body).data;
|
|
40954
41097
|
},
|
|
41098
|
+
async finalizeSpecReviewCas(projectSlug, pageId, input) {
|
|
41099
|
+
const body = await request(`${projectPath(projectSlug)}/wiki/${encodeURIComponent(pageId)}/consensus-finalize`, {
|
|
41100
|
+
body: JSON.stringify(input),
|
|
41101
|
+
headers: { "content-type": "application/json" },
|
|
41102
|
+
method: "POST"
|
|
41103
|
+
});
|
|
41104
|
+
return mrrlinFinalizeSpecReviewCasResponseSchema.parse(body).data;
|
|
41105
|
+
},
|
|
40955
41106
|
async issueAgentCredential(projectSlug, input) {
|
|
40956
41107
|
const body = await request(`${projectPath(projectSlug)}/agent-credentials`, {
|
|
40957
41108
|
body: JSON.stringify(input),
|
|
@@ -41154,19 +41305,19 @@ function searchKnowledgeIndex(index, query, limit = 5) {
|
|
|
41154
41305
|
if (queryTokens.length === 0)
|
|
41155
41306
|
return [];
|
|
41156
41307
|
const documents = index.map((chunk) => tokenize(`${chunk.heading_path.join(" ")} ${chunk.chunk_text}`));
|
|
41157
|
-
const averageLength = documents.reduce((sum,
|
|
41308
|
+
const averageLength = documents.reduce((sum, tokens2) => sum + tokens2.length, 0) / Math.max(documents.length, 1);
|
|
41158
41309
|
const documentFrequency = /* @__PURE__ */ new Map();
|
|
41159
|
-
for (const
|
|
41160
|
-
for (const token of new Set(
|
|
41310
|
+
for (const tokens2 of documents) {
|
|
41311
|
+
for (const token of new Set(tokens2)) {
|
|
41161
41312
|
documentFrequency.set(token, (documentFrequency.get(token) ?? 0) + 1);
|
|
41162
41313
|
}
|
|
41163
41314
|
}
|
|
41164
41315
|
const k1 = 1.2;
|
|
41165
41316
|
const b = 0.75;
|
|
41166
41317
|
const scored = index.map((chunk, chunkIndex) => {
|
|
41167
|
-
const
|
|
41318
|
+
const tokens2 = documents[chunkIndex] ?? [];
|
|
41168
41319
|
const frequencies = /* @__PURE__ */ new Map();
|
|
41169
|
-
for (const token of
|
|
41320
|
+
for (const token of tokens2) {
|
|
41170
41321
|
frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
41171
41322
|
}
|
|
41172
41323
|
const score = queryTokens.reduce((sum, token) => {
|
|
@@ -41175,7 +41326,7 @@ function searchKnowledgeIndex(index, query, limit = 5) {
|
|
|
41175
41326
|
return sum;
|
|
41176
41327
|
const df = documentFrequency.get(token) ?? 0;
|
|
41177
41328
|
const idf = Math.log(1 + (documents.length - df + 0.5) / (df + 0.5));
|
|
41178
|
-
const denominator = tf + k1 * (1 - b + b * (
|
|
41329
|
+
const denominator = tf + k1 * (1 - b + b * (tokens2.length / Math.max(averageLength, 1)));
|
|
41179
41330
|
return sum + idf * (tf * (k1 + 1) / denominator);
|
|
41180
41331
|
}, 0);
|
|
41181
41332
|
return { ...chunk, score };
|
|
@@ -41342,7 +41493,7 @@ function createBoundedSemaphore(slots) {
|
|
|
41342
41493
|
let available = slots;
|
|
41343
41494
|
const queue = [];
|
|
41344
41495
|
return {
|
|
41345
|
-
acquire: () => available > 0 ? (available -= 1, Promise.resolve()) : new Promise((
|
|
41496
|
+
acquire: () => available > 0 ? (available -= 1, Promise.resolve()) : new Promise((resolve2) => queue.push(resolve2)),
|
|
41346
41497
|
release: () => {
|
|
41347
41498
|
const next = queue.shift();
|
|
41348
41499
|
if (next) next();
|
|
@@ -41723,18 +41874,87 @@ function resolveProducerPersona(domain2) {
|
|
|
41723
41874
|
}
|
|
41724
41875
|
|
|
41725
41876
|
// src/consensus/acceptance-criteria.ts
|
|
41726
|
-
var HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
|
|
41727
|
-
var
|
|
41728
|
-
|
|
41729
|
-
|
|
41877
|
+
var HEADING_RE = /^[ ]{0,3}(#{1,6})\s+(.+?)\s*$/;
|
|
41878
|
+
var TOP_LEVEL_CHECKLIST_RE = /^[ ]{0,1}[-*]\s+\[[ xX]\]\s+(.+\S)\s*$/;
|
|
41879
|
+
var TOP_LEVEL_ITEM_RE = /^[ ]{0,1}(?:\d+[.)]|[-*])\s+(.+?\S)\s*$/;
|
|
41880
|
+
var INDENTED_CODE_RE = /^[ ]{4,}\S/;
|
|
41881
|
+
function createCommonMarkFenceScanner() {
|
|
41882
|
+
let active = null;
|
|
41883
|
+
return {
|
|
41884
|
+
isFenced(line) {
|
|
41885
|
+
const trimmed = line.trimStart();
|
|
41886
|
+
const indent = line.length - trimmed.length;
|
|
41887
|
+
if (indent <= 3) {
|
|
41888
|
+
if (!active) {
|
|
41889
|
+
const m = /^(`{3,}|~{3,})/.exec(trimmed);
|
|
41890
|
+
if (m && m[1]) {
|
|
41891
|
+
const fenceStr = m[1];
|
|
41892
|
+
const char = fenceStr[0];
|
|
41893
|
+
const length = fenceStr.length;
|
|
41894
|
+
const remainder = trimmed.slice(length);
|
|
41895
|
+
if (char === "~" || !remainder.includes("`")) {
|
|
41896
|
+
active = { char, length };
|
|
41897
|
+
return true;
|
|
41898
|
+
}
|
|
41899
|
+
}
|
|
41900
|
+
} else {
|
|
41901
|
+
const m = /^(`{3,}|~{3,})\s*$/.exec(trimmed);
|
|
41902
|
+
if (m && m[1]) {
|
|
41903
|
+
const fenceStr = m[1];
|
|
41904
|
+
const char = fenceStr[0];
|
|
41905
|
+
const length = fenceStr.length;
|
|
41906
|
+
if (char === active.char && length >= active.length) {
|
|
41907
|
+
active = null;
|
|
41908
|
+
return true;
|
|
41909
|
+
}
|
|
41910
|
+
}
|
|
41911
|
+
}
|
|
41912
|
+
}
|
|
41913
|
+
return active !== null;
|
|
41914
|
+
},
|
|
41915
|
+
isInCodeBlock() {
|
|
41916
|
+
return active !== null;
|
|
41917
|
+
}
|
|
41918
|
+
};
|
|
41919
|
+
}
|
|
41920
|
+
var ACCEPTANCE_CRITERIA_ALIASES = /* @__PURE__ */ new Set([
|
|
41921
|
+
"acceptance criteria",
|
|
41922
|
+
"acceptance criteria (checklist)",
|
|
41923
|
+
"acceptance criteria (dod)",
|
|
41924
|
+
"acceptance criteria (definition of done)",
|
|
41925
|
+
"criteria for acceptance",
|
|
41926
|
+
"success criteria",
|
|
41927
|
+
"definition of done",
|
|
41928
|
+
"dod",
|
|
41929
|
+
"dod (definition of done)",
|
|
41930
|
+
"\u043A\u0440\u0438\u0442\u0435\u0440\u0456\u0457 \u043F\u0440\u0438\u0439\u043D\u044F\u0442\u043D\u043E\u0441\u0442\u0456",
|
|
41931
|
+
"\u043A\u0440\u0438\u0442\u0435\u0440\u0456\u0457 \u043F\u0440\u0438\u0439\u043E\u043C\u043A\u0438",
|
|
41932
|
+
"\u043A\u0440\u0438\u0442\u0435\u0440\u0456\u0457 \u043F\u0440\u0438\u0439\u043C\u0430\u043D\u043D\u044F",
|
|
41933
|
+
"\u043A\u0440\u0438\u0442\u0435\u0440\u0456\u0457 \u0443\u0441\u043F\u0456\u0445\u0443",
|
|
41934
|
+
"\u043A\u0440\u0438\u0442\u0435\u0440\u0456\u0457 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0456",
|
|
41935
|
+
"kryteria akceptacji",
|
|
41936
|
+
"kryteria sukcesu",
|
|
41937
|
+
"akzeptanzkriterien",
|
|
41938
|
+
"erfolgskriterien"
|
|
41939
|
+
]);
|
|
41940
|
+
function isAcceptanceCriteriaHeading(title) {
|
|
41941
|
+
const strippedNumber = title.trim().toLowerCase().replace(/^\d+[.)]\s*/, "");
|
|
41942
|
+
const cleaned = strippedNumber.replace(/[:\-?.]+$/, "").replace(/\s+/g, " ").trim();
|
|
41943
|
+
if (ACCEPTANCE_CRITERIA_ALIASES.has(cleaned)) return true;
|
|
41944
|
+
if (cleaned.startsWith("acceptance criteria") || cleaned.startsWith("\u043A\u0440\u0438\u0442\u0435\u0440\u0456\u0457 \u043F\u0440\u0438\u0439\u043D\u044F\u0442\u043D\u043E\u0441\u0442\u0456")) return true;
|
|
41945
|
+
return false;
|
|
41730
41946
|
}
|
|
41731
41947
|
function parseAcceptanceCriteria(markdown) {
|
|
41732
41948
|
const lines2 = markdown.split(/\r?\n/);
|
|
41949
|
+
const fenceScanner = createCommonMarkFenceScanner();
|
|
41733
41950
|
let sectionStart = -1;
|
|
41734
41951
|
let sectionLevel = 0;
|
|
41735
41952
|
for (let i = 0; i < lines2.length; i += 1) {
|
|
41736
|
-
const
|
|
41737
|
-
|
|
41953
|
+
const rawLine = lines2[i] ?? "";
|
|
41954
|
+
const isFenced = fenceScanner.isFenced(rawLine);
|
|
41955
|
+
if (isFenced) continue;
|
|
41956
|
+
const m = HEADING_RE.exec(rawLine);
|
|
41957
|
+
if (m && isAcceptanceCriteriaHeading(m[2] ?? "")) {
|
|
41738
41958
|
sectionStart = i + 1;
|
|
41739
41959
|
sectionLevel = (m[1] ?? "").length;
|
|
41740
41960
|
break;
|
|
@@ -41743,14 +41963,65 @@ function parseAcceptanceCriteria(markdown) {
|
|
|
41743
41963
|
if (sectionStart === -1) return null;
|
|
41744
41964
|
const items = [];
|
|
41745
41965
|
for (let i = sectionStart; i < lines2.length; i += 1) {
|
|
41746
|
-
const
|
|
41747
|
-
const
|
|
41966
|
+
const rawLine = lines2[i] ?? "";
|
|
41967
|
+
const isFenced = fenceScanner.isFenced(rawLine);
|
|
41968
|
+
if (isFenced) continue;
|
|
41969
|
+
if (INDENTED_CODE_RE.test(rawLine)) continue;
|
|
41970
|
+
const heading = HEADING_RE.exec(rawLine);
|
|
41748
41971
|
if (heading && (heading[1] ?? "").length <= sectionLevel) break;
|
|
41749
|
-
const item =
|
|
41972
|
+
const item = TOP_LEVEL_CHECKLIST_RE.exec(rawLine);
|
|
41750
41973
|
if (item && item[1]) items.push(item[1].trim());
|
|
41751
41974
|
}
|
|
41752
41975
|
return items.length > 0 ? { items } : null;
|
|
41753
41976
|
}
|
|
41977
|
+
function normalizeAcceptanceCriteria(markdown) {
|
|
41978
|
+
const isCrlf = markdown.includes("\r\n");
|
|
41979
|
+
const newline = isCrlf ? "\r\n" : "\n";
|
|
41980
|
+
const lines2 = markdown.split(/\r?\n/);
|
|
41981
|
+
const fenceScanner = createCommonMarkFenceScanner();
|
|
41982
|
+
let sectionStart = -1;
|
|
41983
|
+
let sectionLevel = 0;
|
|
41984
|
+
let headingIndex = -1;
|
|
41985
|
+
for (let i = 0; i < lines2.length; i += 1) {
|
|
41986
|
+
const rawLine = lines2[i] ?? "";
|
|
41987
|
+
const isFenced = fenceScanner.isFenced(rawLine);
|
|
41988
|
+
if (isFenced) continue;
|
|
41989
|
+
const m = HEADING_RE.exec(rawLine);
|
|
41990
|
+
if (m && isAcceptanceCriteriaHeading(m[2] ?? "")) {
|
|
41991
|
+
headingIndex = i;
|
|
41992
|
+
sectionStart = i + 1;
|
|
41993
|
+
sectionLevel = (m[1] ?? "").length;
|
|
41994
|
+
break;
|
|
41995
|
+
}
|
|
41996
|
+
}
|
|
41997
|
+
if (sectionStart === -1) return markdown;
|
|
41998
|
+
let changed = false;
|
|
41999
|
+
const newLines = [...lines2];
|
|
42000
|
+
const hashes = "#".repeat(sectionLevel || 2);
|
|
42001
|
+
if (newLines[headingIndex]?.trim() !== `${hashes} Acceptance criteria`) {
|
|
42002
|
+
newLines[headingIndex] = `${hashes} Acceptance criteria`;
|
|
42003
|
+
changed = true;
|
|
42004
|
+
}
|
|
42005
|
+
const bodyFenceScanner = createCommonMarkFenceScanner();
|
|
42006
|
+
for (let i = 0; i < sectionStart; i += 1) {
|
|
42007
|
+
bodyFenceScanner.isFenced(lines2[i] ?? "");
|
|
42008
|
+
}
|
|
42009
|
+
for (let i = sectionStart; i < newLines.length; i += 1) {
|
|
42010
|
+
const rawLine = newLines[i] ?? "";
|
|
42011
|
+
const isFenced = bodyFenceScanner.isFenced(rawLine);
|
|
42012
|
+
if (isFenced) continue;
|
|
42013
|
+
if (INDENTED_CODE_RE.test(rawLine)) continue;
|
|
42014
|
+
const heading = HEADING_RE.exec(rawLine);
|
|
42015
|
+
if (heading && (heading[1] ?? "").length <= sectionLevel) break;
|
|
42016
|
+
if (TOP_LEVEL_CHECKLIST_RE.test(rawLine)) continue;
|
|
42017
|
+
const match = TOP_LEVEL_ITEM_RE.exec(rawLine);
|
|
42018
|
+
if (match && match[1]) {
|
|
42019
|
+
newLines[i] = `- [ ] ${match[1]}`;
|
|
42020
|
+
changed = true;
|
|
42021
|
+
}
|
|
42022
|
+
}
|
|
42023
|
+
return changed ? newLines.join(newline) : markdown;
|
|
42024
|
+
}
|
|
41754
42025
|
function resolveSpecBudget(impact) {
|
|
41755
42026
|
switch (impact?.toLowerCase()) {
|
|
41756
42027
|
case "high":
|
|
@@ -41822,12 +42093,21 @@ function applyClarificationCap(issues, impact) {
|
|
|
41822
42093
|
};
|
|
41823
42094
|
}
|
|
41824
42095
|
async function runSpecGate(_target, deps, opts) {
|
|
41825
|
-
|
|
42096
|
+
const spec = await deps.readSpec();
|
|
42097
|
+
let currentHash = spec.contentHash;
|
|
42098
|
+
let currentMarkdown = spec.markdownBody;
|
|
42099
|
+
if (!await deps.consensusEnabled()) {
|
|
42100
|
+
return {
|
|
42101
|
+
outcome: "SKIPPED",
|
|
42102
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown }
|
|
42103
|
+
};
|
|
42104
|
+
}
|
|
41826
42105
|
const intent = await deps.readIntent();
|
|
41827
42106
|
const legacyExempt = intent.specApproved === "skipped_legacy";
|
|
41828
42107
|
if (!legacyExempt && intent.taskId !== null && (intent.seed === null || intent.seed.trim() === "")) {
|
|
41829
42108
|
return {
|
|
41830
42109
|
outcome: "CONSENSUS_BLOCKED",
|
|
42110
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown },
|
|
41831
42111
|
operatorClarifications: [
|
|
41832
42112
|
{
|
|
41833
42113
|
kind: "missing_requirement",
|
|
@@ -41845,10 +42125,17 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41845
42125
|
const baseHeads = resolveHeads(intent.domains, intent.impact);
|
|
41846
42126
|
const extraHeads = opts?.extraHeads ?? [];
|
|
41847
42127
|
const heads = [...baseHeads, ...extraHeads.filter((h) => !baseHeads.includes(h))];
|
|
41848
|
-
const spec = await deps.readSpec();
|
|
41849
|
-
let currentHash = spec.contentHash;
|
|
41850
|
-
let currentMarkdown = spec.markdownBody;
|
|
41851
42128
|
let conflicted = false;
|
|
42129
|
+
if (parseAcceptanceCriteria(currentMarkdown) === null) {
|
|
42130
|
+
const normalized = normalizeAcceptanceCriteria(currentMarkdown);
|
|
42131
|
+
if (parseAcceptanceCriteria(normalized) !== null) {
|
|
42132
|
+
const cas = await deps.casUpdateSpec(normalized, currentHash);
|
|
42133
|
+
if (cas.ok) {
|
|
42134
|
+
currentHash = cas.contentHash;
|
|
42135
|
+
currentMarkdown = normalized;
|
|
42136
|
+
}
|
|
42137
|
+
}
|
|
42138
|
+
}
|
|
41852
42139
|
if (parseAcceptanceCriteria(currentMarkdown) === null) {
|
|
41853
42140
|
const bootstrapIssue = {
|
|
41854
42141
|
id: "acceptance-criteria-bootstrap",
|
|
@@ -41861,6 +42148,7 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41861
42148
|
`);
|
|
41862
42149
|
return {
|
|
41863
42150
|
outcome: "CONSENSUS_BLOCKED",
|
|
42151
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown },
|
|
41864
42152
|
diagnostics: [
|
|
41865
42153
|
{
|
|
41866
42154
|
kind: "acceptance_criteria_unauthorable",
|
|
@@ -41881,8 +42169,11 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41881
42169
|
}
|
|
41882
42170
|
const cas = await deps.casUpdateSpec(revised.text, currentHash);
|
|
41883
42171
|
if (!cas.ok) {
|
|
41884
|
-
await deps.writeEvidence({ specPageId: spec.pageId, outcome: "SPEC_CHANGED_DURING_REVIEW" });
|
|
41885
|
-
return {
|
|
42172
|
+
await deps.writeEvidence?.({ specPageId: spec.pageId, outcome: "SPEC_CHANGED_DURING_REVIEW" });
|
|
42173
|
+
return {
|
|
42174
|
+
outcome: "SPEC_CHANGED_DURING_REVIEW",
|
|
42175
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown }
|
|
42176
|
+
};
|
|
41886
42177
|
}
|
|
41887
42178
|
currentHash = cas.contentHash;
|
|
41888
42179
|
currentMarkdown = revised.text;
|
|
@@ -41918,8 +42209,11 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41918
42209
|
// error and must propagate so it is not silently masked as a conflict.
|
|
41919
42210
|
}).catch((e) => conflicted ? null : Promise.reject(e));
|
|
41920
42211
|
if (conflicted || result === null) {
|
|
41921
|
-
await deps.writeEvidence({ specPageId: spec.pageId, outcome: "SPEC_CHANGED_DURING_REVIEW" });
|
|
41922
|
-
return {
|
|
42212
|
+
await deps.writeEvidence?.({ specPageId: spec.pageId, outcome: "SPEC_CHANGED_DURING_REVIEW" });
|
|
42213
|
+
return {
|
|
42214
|
+
outcome: "SPEC_CHANGED_DURING_REVIEW",
|
|
42215
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown }
|
|
42216
|
+
};
|
|
41923
42217
|
}
|
|
41924
42218
|
if (result.outcome === "CONVERGED" && intent.taskId !== null && !validateSpecIdentity(currentMarkdown, intent.taskId)) {
|
|
41925
42219
|
process.stderr.write(
|
|
@@ -41928,6 +42222,7 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41928
42222
|
);
|
|
41929
42223
|
return {
|
|
41930
42224
|
outcome: "CONSENSUS_BLOCKED",
|
|
42225
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown },
|
|
41931
42226
|
diagnostics: [
|
|
41932
42227
|
{
|
|
41933
42228
|
kind: "spec_identity_mismatch",
|
|
@@ -41951,19 +42246,50 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41951
42246
|
keptClarifications = capped.kept;
|
|
41952
42247
|
droppedByCap = capped.dropped;
|
|
41953
42248
|
}
|
|
41954
|
-
|
|
41955
|
-
|
|
41956
|
-
outcome
|
|
41957
|
-
|
|
41958
|
-
|
|
41959
|
-
|
|
41960
|
-
|
|
41961
|
-
|
|
42249
|
+
const ownership = intent.taskId !== null ? { kind: "task", taskId: intent.taskId, domains: intent.domains ?? ["code"] } : intent.planId != null ? { kind: "plan", planId: intent.planId, domains: intent.domains ?? ["code"] } : { kind: "project", domains: intent.domains ?? ["code"] };
|
|
42250
|
+
if (deps.finalizeSpecReviewCas) {
|
|
42251
|
+
const shouldFinalizeCas = result.outcome === "CONVERGED" || result.outcome === "CONSENSUS_BLOCKED" && keptClarifications.length === 0;
|
|
42252
|
+
if (shouldFinalizeCas) {
|
|
42253
|
+
const finalizeRes = await deps.finalizeSpecReviewCas({
|
|
42254
|
+
expectedContentHash: currentHash,
|
|
42255
|
+
ownership,
|
|
42256
|
+
review: {
|
|
42257
|
+
outcome: result.outcome,
|
|
42258
|
+
rounds: result.rounds,
|
|
42259
|
+
history: result.history,
|
|
42260
|
+
...droppedByCap.length > 0 ? { suppressionEvidence: { droppedByCap } } : {}
|
|
42261
|
+
},
|
|
42262
|
+
specReady: result.outcome === "CONVERGED"
|
|
42263
|
+
});
|
|
42264
|
+
if (finalizeRes.status === "superseded") {
|
|
42265
|
+
return {
|
|
42266
|
+
outcome: "SUPERSEDED",
|
|
42267
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown },
|
|
42268
|
+
evaluatedOwnership: ownership
|
|
42269
|
+
};
|
|
42270
|
+
}
|
|
42271
|
+
}
|
|
42272
|
+
} else {
|
|
42273
|
+
await deps.writeEvidence?.({
|
|
42274
|
+
specPageId: spec.pageId,
|
|
42275
|
+
outcome: result.outcome,
|
|
42276
|
+
rounds: result.rounds,
|
|
42277
|
+
history: result.history,
|
|
42278
|
+
contentHash: currentHash,
|
|
42279
|
+
...droppedByCap.length > 0 ? { droppedByCap } : {}
|
|
42280
|
+
});
|
|
42281
|
+
if (result.outcome === "CONVERGED") {
|
|
42282
|
+
await deps.promoteSpecReady?.(currentHash);
|
|
42283
|
+
}
|
|
42284
|
+
}
|
|
41962
42285
|
if (result.outcome === "CONVERGED") {
|
|
41963
|
-
await deps.promoteSpecReady(currentHash);
|
|
41964
42286
|
return {
|
|
41965
42287
|
outcome: "CONVERGED",
|
|
42288
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown },
|
|
42289
|
+
evaluatedOwnership: ownership,
|
|
41966
42290
|
headOutcomes: result.history.at(-1)?.headOutcomes ?? [],
|
|
42291
|
+
rounds: result.rounds,
|
|
42292
|
+
history: result.history,
|
|
41967
42293
|
arbiterInfo: result.arbiterInfo,
|
|
41968
42294
|
panelComposition: result.panelComposition,
|
|
41969
42295
|
sufficiencyVerdict: result.sufficiencyVerdict
|
|
@@ -41972,6 +42298,10 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41972
42298
|
if (result.outcome === "CONSENSUS_BLOCKED") {
|
|
41973
42299
|
return {
|
|
41974
42300
|
outcome: "CONSENSUS_BLOCKED",
|
|
42301
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown },
|
|
42302
|
+
evaluatedOwnership: ownership,
|
|
42303
|
+
rounds: result.rounds,
|
|
42304
|
+
history: result.history,
|
|
41975
42305
|
...keptClarifications.length > 0 ? { operatorClarifications: keptClarifications } : {},
|
|
41976
42306
|
...droppedByCap.length > 0 ? { droppedByCap } : {},
|
|
41977
42307
|
...result.escalation ? { escalation: result.escalation } : {},
|
|
@@ -41983,6 +42313,8 @@ async function runSpecGate(_target, deps, opts) {
|
|
|
41983
42313
|
}
|
|
41984
42314
|
return {
|
|
41985
42315
|
outcome: result.outcome,
|
|
42316
|
+
evaluatedSnapshot: { contentHash: currentHash, markdownBody: currentMarkdown },
|
|
42317
|
+
evaluatedOwnership: ownership,
|
|
41986
42318
|
arbiterInfo: result.arbiterInfo,
|
|
41987
42319
|
panelComposition: result.panelComposition,
|
|
41988
42320
|
sufficiencyVerdict: result.sufficiencyVerdict
|
|
@@ -42192,22 +42524,22 @@ var CodexJsonRpcClient = class {
|
|
|
42192
42524
|
const payload = { jsonrpc: "2.0", id, method, params };
|
|
42193
42525
|
const line = `${JSON.stringify(payload)}
|
|
42194
42526
|
`;
|
|
42195
|
-
const resultPromise = new Promise((
|
|
42527
|
+
const resultPromise = new Promise((resolve2, reject) => {
|
|
42196
42528
|
const onResponse = (response) => {
|
|
42197
42529
|
if (response.error) {
|
|
42198
42530
|
reject(new Error(response.error.message ?? "Codex JSON-RPC error"));
|
|
42199
42531
|
return;
|
|
42200
42532
|
}
|
|
42201
|
-
|
|
42533
|
+
resolve2(response.result);
|
|
42202
42534
|
};
|
|
42203
42535
|
this.emitter.once(`response:${id}`, onResponse);
|
|
42204
42536
|
});
|
|
42205
|
-
await new Promise((
|
|
42537
|
+
await new Promise((resolve2, reject) => {
|
|
42206
42538
|
this.writeStream.write(line, (error51) => {
|
|
42207
42539
|
if (error51)
|
|
42208
42540
|
reject(error51);
|
|
42209
42541
|
else
|
|
42210
|
-
|
|
42542
|
+
resolve2();
|
|
42211
42543
|
});
|
|
42212
42544
|
});
|
|
42213
42545
|
return await resultPromise;
|
|
@@ -42370,11 +42702,11 @@ var CodexServerManager = class {
|
|
|
42370
42702
|
env: config2?.env ?? process.env,
|
|
42371
42703
|
shell: true
|
|
42372
42704
|
});
|
|
42373
|
-
await new Promise((
|
|
42705
|
+
await new Promise((resolve2, reject) => {
|
|
42374
42706
|
child.on("error", (err) => {
|
|
42375
42707
|
reject(new Error(`Failed to spawn codex app-server: ${err.message}`));
|
|
42376
42708
|
});
|
|
42377
|
-
child.on("spawn", () =>
|
|
42709
|
+
child.on("spawn", () => resolve2());
|
|
42378
42710
|
});
|
|
42379
42711
|
if (!child.stdin || !child.stdout) {
|
|
42380
42712
|
child.kill();
|
|
@@ -42401,6 +42733,8 @@ var CodexServerManager = class {
|
|
|
42401
42733
|
rpcClient.respond(id, { decision: "accept" });
|
|
42402
42734
|
} else if (method === "mcpServer/elicitation/request") {
|
|
42403
42735
|
rpcClient.respond(id, { action: "accept" });
|
|
42736
|
+
} else if (method.includes("approval") || method.includes("Approval") || method.includes("permission") || method.includes("elicitation")) {
|
|
42737
|
+
rpcClient.respond(id, { action: "accept", decision: "accept", approved: true });
|
|
42404
42738
|
}
|
|
42405
42739
|
});
|
|
42406
42740
|
const notificationHandlers = /* @__PURE__ */ new Set();
|
|
@@ -43151,7 +43485,7 @@ var ClaudeProcess = class {
|
|
|
43151
43485
|
cwd: options.cwd?.trim() || void 0,
|
|
43152
43486
|
// Inherit the operator's env so the CLI picks up their own ~/.claude.json tool registry —
|
|
43153
43487
|
// unless an explicit mcpConfig was provided in options.
|
|
43154
|
-
env: process.env,
|
|
43488
|
+
env: options.env ?? process.env,
|
|
43155
43489
|
stdio: ["pipe", "pipe", "pipe"]
|
|
43156
43490
|
});
|
|
43157
43491
|
this.child.stdout.setEncoding("utf8");
|
|
@@ -43198,6 +43532,9 @@ var ClaudeProcess = class {
|
|
|
43198
43532
|
if (options.mcpConfig) {
|
|
43199
43533
|
args.push("--mcp-config", options.mcpConfig);
|
|
43200
43534
|
}
|
|
43535
|
+
if (options.appendSystemPrompt && options.appendSystemPrompt.trim().length > 0) {
|
|
43536
|
+
args.push("--append-system-prompt", options.appendSystemPrompt.trim());
|
|
43537
|
+
}
|
|
43201
43538
|
args.push("--permission-mode", options.permissionMode);
|
|
43202
43539
|
if (options.disallowedTools && options.disallowedTools.length > 0) {
|
|
43203
43540
|
args.push("--disallowedTools", options.disallowedTools.join(","));
|
|
@@ -43243,15 +43580,15 @@ var ClaudeProcess = class {
|
|
|
43243
43580
|
return Promise.resolve();
|
|
43244
43581
|
}
|
|
43245
43582
|
if (this.disposed) {
|
|
43246
|
-
return new Promise((
|
|
43583
|
+
return new Promise((resolve2) => {
|
|
43247
43584
|
const off = this.onExit(() => {
|
|
43248
43585
|
off();
|
|
43249
|
-
|
|
43586
|
+
resolve2();
|
|
43250
43587
|
});
|
|
43251
43588
|
});
|
|
43252
43589
|
}
|
|
43253
43590
|
this.disposed = true;
|
|
43254
|
-
return new Promise((
|
|
43591
|
+
return new Promise((resolve2) => {
|
|
43255
43592
|
const forceKill = setTimeout(() => {
|
|
43256
43593
|
if (!this.exited)
|
|
43257
43594
|
this.child.kill("SIGKILL");
|
|
@@ -43260,7 +43597,7 @@ var ClaudeProcess = class {
|
|
|
43260
43597
|
const off = this.onExit(() => {
|
|
43261
43598
|
clearTimeout(forceKill);
|
|
43262
43599
|
off();
|
|
43263
|
-
|
|
43600
|
+
resolve2();
|
|
43264
43601
|
});
|
|
43265
43602
|
this.child.kill("SIGTERM");
|
|
43266
43603
|
});
|
|
@@ -43460,11 +43797,16 @@ function permissionModeFor(sandbox) {
|
|
|
43460
43797
|
return { permissionMode: "bypassPermissions" };
|
|
43461
43798
|
}
|
|
43462
43799
|
var ClaudeAgentHandle = class {
|
|
43800
|
+
childEnv;
|
|
43463
43801
|
// One resident child + one mapper per thread — mirrors ClaudeProcess's own per-thread residency
|
|
43464
43802
|
// and ClaudeBridgeEventMapper's documented "fresh instance per thread" contract (its tool-use
|
|
43465
43803
|
// dedup sets must not leak across threads).
|
|
43466
43804
|
processes = /* @__PURE__ */ new Map();
|
|
43467
43805
|
mappers = /* @__PURE__ */ new Map();
|
|
43806
|
+
/** Exact complete environment passed unchanged to every process this handle spawns. */
|
|
43807
|
+
constructor(childEnv) {
|
|
43808
|
+
this.childEnv = childEnv;
|
|
43809
|
+
}
|
|
43468
43810
|
async startThread(params) {
|
|
43469
43811
|
const { permissionMode, disallowedTools } = permissionModeFor(params.sandbox);
|
|
43470
43812
|
const child = new ClaudeProcess({
|
|
@@ -43473,7 +43815,9 @@ var ClaudeAgentHandle = class {
|
|
|
43473
43815
|
disallowedTools,
|
|
43474
43816
|
model: params.model,
|
|
43475
43817
|
cwd: params.cwd,
|
|
43476
|
-
mcpConfig: params.mcpConfig
|
|
43818
|
+
mcpConfig: params.mcpConfig,
|
|
43819
|
+
appendSystemPrompt: params.baseInstructions,
|
|
43820
|
+
...this.childEnv === void 0 ? {} : { env: this.childEnv }
|
|
43477
43821
|
});
|
|
43478
43822
|
this.processes.set(child.sessionId, child);
|
|
43479
43823
|
this.mappers.set(child.sessionId, new ClaudeBridgeEventMapper());
|
|
@@ -43487,7 +43831,9 @@ var ClaudeAgentHandle = class {
|
|
|
43487
43831
|
disallowedTools,
|
|
43488
43832
|
model: params.model,
|
|
43489
43833
|
cwd: params.cwd,
|
|
43490
|
-
mcpConfig: params.mcpConfig
|
|
43834
|
+
mcpConfig: params.mcpConfig,
|
|
43835
|
+
appendSystemPrompt: params.baseInstructions,
|
|
43836
|
+
...this.childEnv === void 0 ? {} : { env: this.childEnv }
|
|
43491
43837
|
});
|
|
43492
43838
|
this.processes.set(threadId, child);
|
|
43493
43839
|
this.mappers.set(threadId, new ClaudeBridgeEventMapper());
|
|
@@ -43631,8 +43977,14 @@ var AgyProcess = class _AgyProcess {
|
|
|
43631
43977
|
args.push("--mode", "accept-edits");
|
|
43632
43978
|
break;
|
|
43633
43979
|
}
|
|
43634
|
-
|
|
43635
|
-
|
|
43980
|
+
let modelToUse = options.model;
|
|
43981
|
+
if (modelToUse === "gemini-3.1-pro-high" || modelToUse === "gemini-3.7-flash-high") {
|
|
43982
|
+
modelToUse = "gemini-3.8-flash-high";
|
|
43983
|
+
} else if (modelToUse === "gemini-3.1-pro-low" || modelToUse === "gemini-3.7-flash-low") {
|
|
43984
|
+
modelToUse = "gemini-3.8-flash-low";
|
|
43985
|
+
}
|
|
43986
|
+
if (modelToUse) {
|
|
43987
|
+
args.push("--model", modelToUse);
|
|
43636
43988
|
}
|
|
43637
43989
|
if (mode.kind === "resume") {
|
|
43638
43990
|
args.push("--conversation", mode.resumeId);
|
|
@@ -43641,7 +43993,7 @@ var AgyProcess = class _AgyProcess {
|
|
|
43641
43993
|
const cwd = options.cwd?.trim() || void 0;
|
|
43642
43994
|
this.child = (0, import_node_child_process4.spawn)(executable, args, {
|
|
43643
43995
|
cwd,
|
|
43644
|
-
env: process.env,
|
|
43996
|
+
env: options.env ?? process.env,
|
|
43645
43997
|
stdio: ["pipe", "pipe", "pipe"]
|
|
43646
43998
|
});
|
|
43647
43999
|
activeProcesses.add(this.child);
|
|
@@ -43861,7 +44213,7 @@ function agyModelCatalog() {
|
|
|
43861
44213
|
id: entry.id,
|
|
43862
44214
|
displayName: entry.displayName,
|
|
43863
44215
|
description: entry.description,
|
|
43864
|
-
isDefault: entry.id === "gemini-3.
|
|
44216
|
+
isDefault: entry.id === "gemini-3.8-flash-high"
|
|
43865
44217
|
}));
|
|
43866
44218
|
}
|
|
43867
44219
|
var MODEL_ID_REGEX2 = /^[a-zA-Z0-9][a-zA-Z0-9._-]{1,63}$/;
|
|
@@ -43886,7 +44238,7 @@ function parseAgyModelsOutput(output3) {
|
|
|
43886
44238
|
id,
|
|
43887
44239
|
displayName,
|
|
43888
44240
|
description,
|
|
43889
|
-
isDefault: id === "gemini-3.
|
|
44241
|
+
isDefault: id === "gemini-3.8-flash-high"
|
|
43890
44242
|
});
|
|
43891
44243
|
}
|
|
43892
44244
|
return models;
|
|
@@ -43953,18 +44305,28 @@ function permissionModeFor2(sandbox, approvalPolicy) {
|
|
|
43953
44305
|
return { permissionMode: "acceptEdits" };
|
|
43954
44306
|
}
|
|
43955
44307
|
var AgyAgentHandle = class {
|
|
44308
|
+
childEnv;
|
|
43956
44309
|
processes = /* @__PURE__ */ new Map();
|
|
43957
44310
|
mappers = /* @__PURE__ */ new Map();
|
|
44311
|
+
baseInstructions = /* @__PURE__ */ new Map();
|
|
44312
|
+
/** Exact complete environment passed unchanged to every process this handle spawns. */
|
|
44313
|
+
constructor(childEnv) {
|
|
44314
|
+
this.childEnv = childEnv;
|
|
44315
|
+
}
|
|
43958
44316
|
async startThread(params) {
|
|
43959
44317
|
const { permissionMode } = permissionModeFor2(params.sandbox, params.approvalPolicy);
|
|
43960
44318
|
const child = new AgyProcess({
|
|
43961
44319
|
mode: { kind: "new" },
|
|
43962
44320
|
permissionMode,
|
|
43963
44321
|
model: params.model,
|
|
43964
|
-
cwd: params.cwd
|
|
44322
|
+
cwd: params.cwd,
|
|
44323
|
+
...this.childEnv === void 0 ? {} : { env: this.childEnv }
|
|
43965
44324
|
});
|
|
43966
44325
|
this.processes.set(child.sessionId, child);
|
|
43967
44326
|
this.mappers.set(child.sessionId, new AgyBridgeEventMapper());
|
|
44327
|
+
if (params.baseInstructions && params.baseInstructions.trim().length > 0) {
|
|
44328
|
+
this.baseInstructions.set(child.sessionId, params.baseInstructions.trim());
|
|
44329
|
+
}
|
|
43968
44330
|
return { threadId: child.sessionId };
|
|
43969
44331
|
}
|
|
43970
44332
|
async resumeThread(threadId, params) {
|
|
@@ -43978,16 +44340,31 @@ var AgyAgentHandle = class {
|
|
|
43978
44340
|
mode: { kind: "resume", resumeId: threadId },
|
|
43979
44341
|
permissionMode,
|
|
43980
44342
|
model: params.model,
|
|
43981
|
-
cwd: params.cwd
|
|
44343
|
+
cwd: params.cwd,
|
|
44344
|
+
...this.childEnv === void 0 ? {} : { env: this.childEnv }
|
|
43982
44345
|
});
|
|
43983
44346
|
this.processes.set(threadId, child);
|
|
43984
44347
|
this.mappers.set(threadId, new AgyBridgeEventMapper());
|
|
44348
|
+
if (params.baseInstructions && params.baseInstructions.trim().length > 0) {
|
|
44349
|
+
this.baseInstructions.set(threadId, params.baseInstructions.trim());
|
|
44350
|
+
}
|
|
43985
44351
|
return { threadId: child.sessionId };
|
|
43986
44352
|
}
|
|
43987
44353
|
async startTurn(threadId, input) {
|
|
43988
44354
|
const child = this.requireProcess(threadId);
|
|
43989
44355
|
const text = input.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
43990
|
-
|
|
44356
|
+
const inst = this.baseInstructions.get(threadId);
|
|
44357
|
+
if (inst) {
|
|
44358
|
+
const composed = `[SYSTEM INSTRUCTIONS - MRRLIN DIRECTOR]
|
|
44359
|
+
${inst}
|
|
44360
|
+
|
|
44361
|
+
---
|
|
44362
|
+
[OPERATOR REQUEST]
|
|
44363
|
+
${text}`;
|
|
44364
|
+
child.sendTurn(composed);
|
|
44365
|
+
} else {
|
|
44366
|
+
child.sendTurn(text);
|
|
44367
|
+
}
|
|
43991
44368
|
return { turnId: void 0 };
|
|
43992
44369
|
}
|
|
43993
44370
|
async interruptTurn(threadId) {
|
|
@@ -43997,6 +44374,7 @@ var AgyAgentHandle = class {
|
|
|
43997
44374
|
});
|
|
43998
44375
|
this.processes.delete(threadId);
|
|
43999
44376
|
this.mappers.delete(threadId);
|
|
44377
|
+
this.baseInstructions.delete(threadId);
|
|
44000
44378
|
}
|
|
44001
44379
|
}
|
|
44002
44380
|
onEvent(threadId, handler) {
|
|
@@ -44019,6 +44397,14 @@ var AgyAgentHandle = class {
|
|
|
44019
44397
|
async readRateLimits() {
|
|
44020
44398
|
return null;
|
|
44021
44399
|
}
|
|
44400
|
+
disposeAll() {
|
|
44401
|
+
for (const child of this.processes.values())
|
|
44402
|
+
void child.dispose().catch(() => {
|
|
44403
|
+
});
|
|
44404
|
+
this.processes.clear();
|
|
44405
|
+
this.mappers.clear();
|
|
44406
|
+
this.baseInstructions.clear();
|
|
44407
|
+
}
|
|
44022
44408
|
requireProcess(threadId) {
|
|
44023
44409
|
const child = this.processes.get(threadId);
|
|
44024
44410
|
if (!child)
|
|
@@ -44191,7 +44577,7 @@ ${input.prompt}` : input.prompt;
|
|
|
44191
44577
|
args.push("-m", input.model);
|
|
44192
44578
|
}
|
|
44193
44579
|
args.push(fullPrompt);
|
|
44194
|
-
return new Promise((
|
|
44580
|
+
return new Promise((resolve2) => {
|
|
44195
44581
|
let settled = false;
|
|
44196
44582
|
const timerRef = { id: void 0 };
|
|
44197
44583
|
const finish = (result) => {
|
|
@@ -44200,7 +44586,7 @@ ${input.prompt}` : input.prompt;
|
|
|
44200
44586
|
settled = true;
|
|
44201
44587
|
if (timerRef.id)
|
|
44202
44588
|
clearTimeout(timerRef.id);
|
|
44203
|
-
|
|
44589
|
+
resolve2(result);
|
|
44204
44590
|
};
|
|
44205
44591
|
const options = {
|
|
44206
44592
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -44278,7 +44664,7 @@ function runClaudeExec(input, deps) {
|
|
|
44278
44664
|
args.push("--model", input.model);
|
|
44279
44665
|
}
|
|
44280
44666
|
args.push(input.prompt);
|
|
44281
|
-
return new Promise((
|
|
44667
|
+
return new Promise((resolve2) => {
|
|
44282
44668
|
let settled = false;
|
|
44283
44669
|
const timerRef = { id: void 0 };
|
|
44284
44670
|
const finish = (result) => {
|
|
@@ -44287,7 +44673,7 @@ function runClaudeExec(input, deps) {
|
|
|
44287
44673
|
settled = true;
|
|
44288
44674
|
if (timerRef.id)
|
|
44289
44675
|
clearTimeout(timerRef.id);
|
|
44290
|
-
|
|
44676
|
+
resolve2(result);
|
|
44291
44677
|
};
|
|
44292
44678
|
const options = {
|
|
44293
44679
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -44385,10 +44771,21 @@ function runAgyExec(input, deps) {
|
|
|
44385
44771
|
prompt = trimmedInstructions + "\n\n" + prompt;
|
|
44386
44772
|
}
|
|
44387
44773
|
if (input.model) {
|
|
44388
|
-
|
|
44774
|
+
let targetModel = input.model;
|
|
44775
|
+
if (targetModel === "gemini-3.1-pro-high" || targetModel === "gemini-3.7-flash-high") {
|
|
44776
|
+
targetModel = "gemini-3.8-flash-high";
|
|
44777
|
+
} else if (targetModel === "gemini-3.1-pro-low" || targetModel === "gemini-3.7-flash-low") {
|
|
44778
|
+
targetModel = "gemini-3.8-flash-low";
|
|
44779
|
+
}
|
|
44780
|
+
const known = AGY_MODEL_CATALOG.find((m) => m.id === targetModel || m.displayName.toLowerCase() === targetModel.toLowerCase());
|
|
44781
|
+
if (known && !known.id.startsWith("gemini-3.1")) {
|
|
44782
|
+
args.push("--model", known.id);
|
|
44783
|
+
} else {
|
|
44784
|
+
args.push("--model", "gemini-3.8-flash-high");
|
|
44785
|
+
}
|
|
44389
44786
|
}
|
|
44390
44787
|
args.push(`--print=${prompt}`);
|
|
44391
|
-
return new Promise((
|
|
44788
|
+
return new Promise((resolve2) => {
|
|
44392
44789
|
let settled = false;
|
|
44393
44790
|
const timerRef = { id: void 0 };
|
|
44394
44791
|
const finish = (result) => {
|
|
@@ -44397,7 +44794,7 @@ function runAgyExec(input, deps) {
|
|
|
44397
44794
|
settled = true;
|
|
44398
44795
|
if (timerRef.id)
|
|
44399
44796
|
clearTimeout(timerRef.id);
|
|
44400
|
-
|
|
44797
|
+
resolve2(result);
|
|
44401
44798
|
};
|
|
44402
44799
|
const options = {
|
|
44403
44800
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -44468,7 +44865,9 @@ function extractOneShotResult2(stdout) {
|
|
|
44468
44865
|
|
|
44469
44866
|
// ../../packages/agent-client/dist/run-agent-exec.js
|
|
44470
44867
|
function runAgentExec(input, deps, persistedProvider) {
|
|
44471
|
-
const
|
|
44868
|
+
const explicitProvider = persistedProvider ?? process.env.MRRLIN_CONSENSUS_PROVIDER?.trim().toLowerCase();
|
|
44869
|
+
const envToUse = explicitProvider ? { ...process.env, MRRLIN_AGENT_PROVIDER: explicitProvider } : process.env;
|
|
44870
|
+
const provider = resolveProvider(envToUse, explicitProvider);
|
|
44472
44871
|
if (provider.id === "codex") {
|
|
44473
44872
|
return runCodexExec(input, deps);
|
|
44474
44873
|
}
|
|
@@ -44484,7 +44883,7 @@ function runAgentExec(input, deps, persistedProvider) {
|
|
|
44484
44883
|
// ../../packages/agent-client/dist/resolved-seat.js
|
|
44485
44884
|
var CODEX_DEFAULT_MODEL_CONSTANT = "gpt-5.6-terra";
|
|
44486
44885
|
var CLAUDE_DEFAULT_MODEL_CONSTANT = "sonnet";
|
|
44487
|
-
var AGY_DEFAULT_MODEL_CONSTANT = "gemini-3.
|
|
44886
|
+
var AGY_DEFAULT_MODEL_CONSTANT = "gemini-3.8-flash-high";
|
|
44488
44887
|
var CODEX_DEFAULT_MODEL_ENV = "MRRLIN_CODEX_DEFAULT_MODEL";
|
|
44489
44888
|
var CODEX_STRONG_MODEL_ENV = "MRRLIN_CODEX_STRONG_MODEL";
|
|
44490
44889
|
var CLAUDE_DEFAULT_MODEL_ENV = "MRRLIN_CLAUDE_DEFAULT_MODEL";
|
|
@@ -44635,23 +45034,32 @@ async function resolveGatedTaskId(client, projectSlug, page) {
|
|
|
44635
45034
|
function domainScopedReviewHash2(rawHash, domain2) {
|
|
44636
45035
|
return domainScopedReviewHash(rawHash, domain2);
|
|
44637
45036
|
}
|
|
44638
|
-
async function
|
|
45037
|
+
async function resolveSpecDomains(client, projectSlug, page) {
|
|
44639
45038
|
try {
|
|
44640
45039
|
const taskId = await resolveGatedTaskId(client, projectSlug, page);
|
|
44641
45040
|
if (taskId !== null) {
|
|
44642
45041
|
const task = await client.getTask(projectSlug, taskId);
|
|
44643
|
-
return
|
|
45042
|
+
return normalizeDomainArray(task?.domains ?? task?.domain);
|
|
44644
45043
|
}
|
|
44645
45044
|
const plans = await client.listPlans(projectSlug);
|
|
44646
45045
|
const plan = plans.find((p) => p.specWikiPageId === page.id);
|
|
44647
|
-
|
|
44648
|
-
|
|
45046
|
+
if (plan) {
|
|
45047
|
+
const rawPlanDomains = plan?.domains ?? plan?.domain;
|
|
45048
|
+
const substantivePlanDomains = (Array.isArray(rawPlanDomains) ? rawPlanDomains : rawPlanDomains != null ? [rawPlanDomains] : []).map(String).map((d) => d.trim().toLowerCase()).filter(Boolean);
|
|
45049
|
+
if (substantivePlanDomains.length > 0) {
|
|
45050
|
+
return normalizeDomainArray(substantivePlanDomains);
|
|
45051
|
+
}
|
|
45052
|
+
}
|
|
44649
45053
|
const project = await client.getProject(projectSlug);
|
|
44650
|
-
return
|
|
45054
|
+
return normalizeDomainArray(project?.defaultDomains ?? project?.defaultDomain);
|
|
44651
45055
|
} catch {
|
|
44652
|
-
return "code";
|
|
45056
|
+
return ["code"];
|
|
44653
45057
|
}
|
|
44654
45058
|
}
|
|
45059
|
+
async function resolveSpecDomain(client, projectSlug, page) {
|
|
45060
|
+
const domains = await resolveSpecDomains(client, projectSlug, page);
|
|
45061
|
+
return normalizeDomain(domains);
|
|
45062
|
+
}
|
|
44655
45063
|
|
|
44656
45064
|
// src/consensus/findings.ts
|
|
44657
45065
|
function shouldCurateFindings(history) {
|
|
@@ -44726,14 +45134,14 @@ var DIFF_MAX_BYTES = 2e5;
|
|
|
44726
45134
|
var FASTGATE_OUTPUT_MAX_BYTES = 16e3;
|
|
44727
45135
|
var GIT_TIMEOUT_MS = 6e4;
|
|
44728
45136
|
var FASTGATE_TIMEOUT_MS = 3e5;
|
|
44729
|
-
var defaultRunCmd = (cmd, args, opts) => new Promise((
|
|
45137
|
+
var defaultRunCmd = (cmd, args, opts) => new Promise((resolve2) => {
|
|
44730
45138
|
let settled = false;
|
|
44731
45139
|
const timerRef = { id: void 0 };
|
|
44732
45140
|
const finish = (result) => {
|
|
44733
45141
|
if (settled) return;
|
|
44734
45142
|
settled = true;
|
|
44735
45143
|
if (timerRef.id) clearTimeout(timerRef.id);
|
|
44736
|
-
|
|
45144
|
+
resolve2(result);
|
|
44737
45145
|
};
|
|
44738
45146
|
let child;
|
|
44739
45147
|
try {
|
|
@@ -45035,7 +45443,13 @@ function gateCallRecorder(args) {
|
|
|
45035
45443
|
}
|
|
45036
45444
|
function buildSpecGateDeps(args) {
|
|
45037
45445
|
const { client, projectSlug, specPageId, codexExecutable, extraHeadCount = 0, seat, stateDir } = args;
|
|
45038
|
-
const
|
|
45446
|
+
const consensusProvider = process.env.MRRLIN_CONSENSUS_PROVIDER?.trim().toLowerCase() || void 0;
|
|
45447
|
+
const recordCost = gateCallRecorder({
|
|
45448
|
+
stateDir,
|
|
45449
|
+
projectSlug,
|
|
45450
|
+
gate: "spec",
|
|
45451
|
+
provider: consensusProvider ?? seat?.id ?? null
|
|
45452
|
+
});
|
|
45039
45453
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
45040
45454
|
const maxHeadCalls = 2 * rounds * (MAX_DOMAIN_HEADS + extraHeadCount + 2);
|
|
45041
45455
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
@@ -45049,8 +45463,24 @@ function buildSpecGateDeps(args) {
|
|
|
45049
45463
|
}
|
|
45050
45464
|
const taskId = await resolveGatedTaskId(client, projectSlug, { id: page.id, taskId: page.taskId ?? null });
|
|
45051
45465
|
if (taskId === null) {
|
|
45466
|
+
const plans = await client.listPlans(projectSlug);
|
|
45467
|
+
const plan = plans.find((p) => p.specWikiPageId === specPageId);
|
|
45468
|
+
if (plan) {
|
|
45469
|
+
const project2 = await client.getProject(projectSlug);
|
|
45470
|
+
const rawPlanDomains = plan?.domains ?? plan?.domain;
|
|
45471
|
+
const substantivePlanDomains = (Array.isArray(rawPlanDomains) ? rawPlanDomains : rawPlanDomains != null ? [rawPlanDomains] : []).map(String).map((d) => d.trim().toLowerCase()).filter(Boolean);
|
|
45472
|
+
const planDomains = substantivePlanDomains.length > 0 ? substantivePlanDomains : project2?.defaultDomains ?? (project2?.defaultDomain ? [project2.defaultDomain] : void 0);
|
|
45473
|
+
return {
|
|
45474
|
+
taskId: null,
|
|
45475
|
+
planId: plan.id,
|
|
45476
|
+
seed: null,
|
|
45477
|
+
title: plan.title ?? plan.name ?? null,
|
|
45478
|
+
domains: planDomains,
|
|
45479
|
+
notes: null
|
|
45480
|
+
};
|
|
45481
|
+
}
|
|
45052
45482
|
const project = await client.getProject(projectSlug);
|
|
45053
|
-
return { taskId: null, seed: null, title: null, domains: project.defaultDomains, notes: null };
|
|
45483
|
+
return { taskId: null, planId: null, seed: null, title: null, domains: project.defaultDomains, notes: null };
|
|
45054
45484
|
}
|
|
45055
45485
|
const task = await client.getTask(projectSlug, taskId);
|
|
45056
45486
|
if (!task) {
|
|
@@ -45067,10 +45497,7 @@ function buildSpecGateDeps(args) {
|
|
|
45067
45497
|
if (answered && answered.answer) {
|
|
45068
45498
|
resolvedSeed = "(No operator statement was recorded when this task was created. Derived from the operator's answer to the gate's requirements question:)\n\n" + answered.answer;
|
|
45069
45499
|
} else {
|
|
45070
|
-
|
|
45071
|
-
if (parsed !== null && parsed.items.length > 0) {
|
|
45072
|
-
resolvedSeed = "(No operator statement on record for this task. Derived from the task's own notes \u2014 the following acceptance criteria were extracted verbatim:)\n\n" + parsed.items.map((c) => `- ${c}`).join("\n");
|
|
45073
|
-
}
|
|
45500
|
+
resolvedSeed = null;
|
|
45074
45501
|
}
|
|
45075
45502
|
}
|
|
45076
45503
|
const adjudicated = selectAdjudicated(taskInboxItems);
|
|
@@ -45132,7 +45559,9 @@ ${artifact}`,
|
|
|
45132
45559
|
timeoutMs,
|
|
45133
45560
|
sandbox: "read-only",
|
|
45134
45561
|
codexExecutable
|
|
45135
|
-
}
|
|
45562
|
+
},
|
|
45563
|
+
void 0,
|
|
45564
|
+
consensusProvider
|
|
45136
45565
|
);
|
|
45137
45566
|
recordCost("head", personaName, null, startedAt, result);
|
|
45138
45567
|
return result;
|
|
@@ -45149,8 +45578,10 @@ ${artifact}`,
|
|
|
45149
45578
|
codexExecutable,
|
|
45150
45579
|
// model-tier-planning-and-arbiter task 2.5 (D1): the arbiter is pinned to the seat's
|
|
45151
45580
|
// default model.
|
|
45152
|
-
model: seat?.model ?? seat?.models?.strong
|
|
45153
|
-
}
|
|
45581
|
+
model: consensusProvider ? void 0 : seat?.model ?? seat?.models?.strong
|
|
45582
|
+
},
|
|
45583
|
+
void 0,
|
|
45584
|
+
consensusProvider
|
|
45154
45585
|
);
|
|
45155
45586
|
recordCost("arbiter", "arbiter", seat?.model ?? seat?.models?.strong ?? null, startedAt, result);
|
|
45156
45587
|
return result;
|
|
@@ -45173,7 +45604,9 @@ ${artifact}`,
|
|
|
45173
45604
|
timeoutMs,
|
|
45174
45605
|
sandbox: "read-only",
|
|
45175
45606
|
codexExecutable
|
|
45176
|
-
}
|
|
45607
|
+
},
|
|
45608
|
+
void 0,
|
|
45609
|
+
consensusProvider
|
|
45177
45610
|
);
|
|
45178
45611
|
recordCost("reviser", null, null, startedAt, result);
|
|
45179
45612
|
return result;
|
|
@@ -45206,6 +45639,28 @@ ${artifact}`,
|
|
|
45206
45639
|
return false;
|
|
45207
45640
|
}
|
|
45208
45641
|
},
|
|
45642
|
+
async finalizeSpecReviewCas(input) {
|
|
45643
|
+
const history = input.review.history;
|
|
45644
|
+
const highlights = await curateFindings(Array.isArray(history) ? history : [], {
|
|
45645
|
+
runCodexExec: async (execInput) => {
|
|
45646
|
+
const startedAt = Date.now();
|
|
45647
|
+
const result = await runAgentExec(execInput, void 0, consensusProvider);
|
|
45648
|
+
recordCost("findings", "findings-writer", null, startedAt, result);
|
|
45649
|
+
return result;
|
|
45650
|
+
},
|
|
45651
|
+
loadPersona,
|
|
45652
|
+
cwd: sterileCwd(),
|
|
45653
|
+
timeoutMs,
|
|
45654
|
+
codexExecutable
|
|
45655
|
+
});
|
|
45656
|
+
return client.finalizeSpecReviewCas(projectSlug, specPageId, {
|
|
45657
|
+
...input,
|
|
45658
|
+
review: {
|
|
45659
|
+
...input.review,
|
|
45660
|
+
...highlights ? { highlights } : {}
|
|
45661
|
+
}
|
|
45662
|
+
});
|
|
45663
|
+
},
|
|
45209
45664
|
// Record the consensus-review outcome keyed by content hash via the D2b
|
|
45210
45665
|
// endpoint. `record` is typed `unknown` (it crosses the SpecGateDeps
|
|
45211
45666
|
// boundary) so we narrow it defensively:
|
|
@@ -45228,7 +45683,7 @@ ${artifact}`,
|
|
|
45228
45683
|
// call the cost record silently omits.
|
|
45229
45684
|
runCodexExec: async (input) => {
|
|
45230
45685
|
const startedAt = Date.now();
|
|
45231
|
-
const result = await runAgentExec(input);
|
|
45686
|
+
const result = await runAgentExec(input, void 0, consensusProvider);
|
|
45232
45687
|
recordCost("findings", "findings-writer", null, startedAt, result);
|
|
45233
45688
|
return result;
|
|
45234
45689
|
},
|
|
@@ -45257,7 +45712,13 @@ ${artifact}`,
|
|
|
45257
45712
|
}
|
|
45258
45713
|
function buildCodeGateDeps(args) {
|
|
45259
45714
|
const { client, projectSlug, baseRef, cwd, codexExecutable, extraHeads, seat, stateDir } = args;
|
|
45260
|
-
const
|
|
45715
|
+
const consensusProvider = process.env.MRRLIN_CONSENSUS_PROVIDER?.trim().toLowerCase() || void 0;
|
|
45716
|
+
const recordCost = gateCallRecorder({
|
|
45717
|
+
stateDir,
|
|
45718
|
+
projectSlug,
|
|
45719
|
+
gate: "code",
|
|
45720
|
+
provider: consensusProvider ?? seat?.id ?? null
|
|
45721
|
+
});
|
|
45261
45722
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
45262
45723
|
const heads = ["code-reviewer", ...extraHeads ?? []];
|
|
45263
45724
|
const maxHeadCalls = 2 * rounds * (heads.length + 2);
|
|
@@ -45285,60 +45746,72 @@ function buildCodeGateDeps(args) {
|
|
|
45285
45746
|
async runHead(personaName, artifact, dismissed) {
|
|
45286
45747
|
const persona = loadPersona(personaName);
|
|
45287
45748
|
const startedAt = Date.now();
|
|
45288
|
-
const result = await runAgentExec(
|
|
45289
|
-
|
|
45749
|
+
const result = await runAgentExec(
|
|
45750
|
+
{
|
|
45751
|
+
prompt: `Review this code diff.
|
|
45290
45752
|
|
|
45291
45753
|
${dismissed}
|
|
45292
45754
|
|
|
45293
45755
|
--- DIFF ---
|
|
45294
45756
|
${artifact}`,
|
|
45295
|
-
|
|
45296
|
-
|
|
45297
|
-
|
|
45298
|
-
|
|
45299
|
-
|
|
45300
|
-
|
|
45301
|
-
|
|
45302
|
-
|
|
45303
|
-
|
|
45757
|
+
developerInstructions: persona,
|
|
45758
|
+
// Read-only review of a diff that is entirely in the prompt — sterile, per sterile-cwd.ts.
|
|
45759
|
+
// This factory is the ONE that stays mixed: runReviser below keeps the real `cwd` because
|
|
45760
|
+
// it edits the branch, and the git helpers keep it because they are not provider calls.
|
|
45761
|
+
cwd: sterileCwd(),
|
|
45762
|
+
timeoutMs: headTimeoutMs,
|
|
45763
|
+
sandbox: "read-only",
|
|
45764
|
+
codexExecutable
|
|
45765
|
+
},
|
|
45766
|
+
void 0,
|
|
45767
|
+
consensusProvider
|
|
45768
|
+
);
|
|
45304
45769
|
recordCost("head", personaName, null, startedAt, result);
|
|
45305
45770
|
return result;
|
|
45306
45771
|
},
|
|
45307
45772
|
async runArbiter(prompt) {
|
|
45308
45773
|
const startedAt = Date.now();
|
|
45309
|
-
const result = await runAgentExec(
|
|
45310
|
-
|
|
45311
|
-
|
|
45312
|
-
|
|
45313
|
-
|
|
45314
|
-
|
|
45315
|
-
|
|
45316
|
-
|
|
45317
|
-
|
|
45318
|
-
|
|
45319
|
-
|
|
45774
|
+
const result = await runAgentExec(
|
|
45775
|
+
{
|
|
45776
|
+
prompt,
|
|
45777
|
+
developerInstructions: loadPersona("arbiter"),
|
|
45778
|
+
cwd: sterileCwd(),
|
|
45779
|
+
timeoutMs: headTimeoutMs,
|
|
45780
|
+
sandbox: "read-only",
|
|
45781
|
+
codexExecutable,
|
|
45782
|
+
// model-tier-planning-and-arbiter task 2.5 (D1): the arbiter is pinned to the seat's
|
|
45783
|
+
// default model.
|
|
45784
|
+
model: consensusProvider ? void 0 : seat?.model ?? seat?.models?.strong
|
|
45785
|
+
},
|
|
45786
|
+
void 0,
|
|
45787
|
+
consensusProvider
|
|
45788
|
+
);
|
|
45320
45789
|
recordCost("arbiter", "arbiter", seat?.model ?? seat?.models?.strong ?? null, startedAt, result);
|
|
45321
45790
|
return result;
|
|
45322
45791
|
},
|
|
45323
45792
|
async runReviser(artifact, accepted) {
|
|
45324
45793
|
const acceptedList = accepted.map((a) => `- (${a.category}) ${a.text}`).join("\n");
|
|
45325
45794
|
const startedAt = Date.now();
|
|
45326
|
-
const result = await runAgentExec(
|
|
45327
|
-
|
|
45795
|
+
const result = await runAgentExec(
|
|
45796
|
+
{
|
|
45797
|
+
prompt: `Accepted issues to fix:
|
|
45328
45798
|
${acceptedList}
|
|
45329
45799
|
|
|
45330
45800
|
--- DIFF ---
|
|
45331
45801
|
${artifact}`,
|
|
45332
|
-
|
|
45333
|
-
|
|
45334
|
-
|
|
45335
|
-
|
|
45336
|
-
|
|
45337
|
-
|
|
45338
|
-
|
|
45339
|
-
|
|
45340
|
-
|
|
45341
|
-
|
|
45802
|
+
developerInstructions: "You are fixing code review issues. Edit ONLY files already changed in this branch's diff; make the minimal change to resolve each accepted issue. Do not refactor unrelated code. Do not edit files outside the diff.",
|
|
45803
|
+
// The real repository root, NOT sterileCwd(): this is the one gate call that runs
|
|
45804
|
+
// `workspace-write` and edits the branch, so a sterile cwd would point the sandbox at an
|
|
45805
|
+
// empty directory and the revision would silently apply to nothing — the gate would keep
|
|
45806
|
+
// reporting rounds while fixing no code at all.
|
|
45807
|
+
cwd,
|
|
45808
|
+
timeoutMs: reviserTimeoutMs,
|
|
45809
|
+
sandbox: "workspace-write",
|
|
45810
|
+
codexExecutable
|
|
45811
|
+
},
|
|
45812
|
+
void 0,
|
|
45813
|
+
consensusProvider
|
|
45814
|
+
);
|
|
45342
45815
|
recordCost("reviser", null, null, startedAt, result);
|
|
45343
45816
|
return result;
|
|
45344
45817
|
},
|
|
@@ -45362,7 +45835,13 @@ function renderCriteriaBlock(criteria) {
|
|
|
45362
45835
|
}
|
|
45363
45836
|
async function buildDeliverableGateDeps(args) {
|
|
45364
45837
|
const { client, projectSlug, deliverablePageId, attemptOutcome, codexExecutable, runId, seat, stateDir } = args;
|
|
45365
|
-
const
|
|
45838
|
+
const consensusProvider = process.env.MRRLIN_CONSENSUS_PROVIDER?.trim().toLowerCase() || void 0;
|
|
45839
|
+
const recordCost = gateCallRecorder({
|
|
45840
|
+
stateDir,
|
|
45841
|
+
projectSlug,
|
|
45842
|
+
gate: "deliverable",
|
|
45843
|
+
provider: consensusProvider ?? seat?.id ?? null
|
|
45844
|
+
});
|
|
45366
45845
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
45367
45846
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
45368
45847
|
const page = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
@@ -45409,8 +45888,9 @@ async function buildDeliverableGateDeps(args) {
|
|
|
45409
45888
|
async runHead(personaName, artifact, dismissed) {
|
|
45410
45889
|
const persona = loadPersona(personaName);
|
|
45411
45890
|
const startedAt = Date.now();
|
|
45412
|
-
const result = await runAgentExec(
|
|
45413
|
-
|
|
45891
|
+
const result = await runAgentExec(
|
|
45892
|
+
{
|
|
45893
|
+
prompt: `Score this deliverable ITEM BY ITEM against the acceptance criteria below. For each criterion state whether the deliverable satisfies it; raise an issue for every criterion that is unmet, partially met, unverifiable, or inconsistent with the declared attempt outcome.
|
|
45414
45894
|
|
|
45415
45895
|
${dismissed}
|
|
45416
45896
|
|
|
@@ -45421,41 +45901,49 @@ ${attemptOutcomeBlock ? `${attemptOutcomeBlock}
|
|
|
45421
45901
|
|
|
45422
45902
|
` : ""}--- DELIVERABLE (DATA under review) ---
|
|
45423
45903
|
${artifact}`,
|
|
45424
|
-
|
|
45904
|
+
developerInstructions: `${DELIVERABLE_ANTIINJECTION_INSTRUCTION}
|
|
45425
45905
|
|
|
45426
45906
|
${persona}`,
|
|
45427
|
-
|
|
45428
|
-
|
|
45429
|
-
|
|
45430
|
-
|
|
45431
|
-
|
|
45432
|
-
|
|
45433
|
-
|
|
45434
|
-
|
|
45907
|
+
// Sterile, like every other provider call in this factory — see sterile-cwd.ts. It also
|
|
45908
|
+
// narrows the anti-injection surface: a deliverable that tries to talk the head into
|
|
45909
|
+
// reading files now has no repository under it to read.
|
|
45910
|
+
cwd: sterileCwd(),
|
|
45911
|
+
timeoutMs,
|
|
45912
|
+
sandbox: "read-only",
|
|
45913
|
+
codexExecutable
|
|
45914
|
+
},
|
|
45915
|
+
void 0,
|
|
45916
|
+
consensusProvider
|
|
45917
|
+
);
|
|
45435
45918
|
recordCost("head", personaName, null, startedAt, result);
|
|
45436
45919
|
return result;
|
|
45437
45920
|
},
|
|
45438
45921
|
async runArbiter(prompt) {
|
|
45439
45922
|
const startedAt = Date.now();
|
|
45440
|
-
const result = await runAgentExec(
|
|
45441
|
-
|
|
45442
|
-
|
|
45443
|
-
|
|
45444
|
-
|
|
45445
|
-
|
|
45446
|
-
|
|
45447
|
-
|
|
45448
|
-
|
|
45449
|
-
|
|
45450
|
-
|
|
45923
|
+
const result = await runAgentExec(
|
|
45924
|
+
{
|
|
45925
|
+
prompt,
|
|
45926
|
+
developerInstructions: loadPersona("arbiter"),
|
|
45927
|
+
cwd: sterileCwd(),
|
|
45928
|
+
timeoutMs,
|
|
45929
|
+
sandbox: "read-only",
|
|
45930
|
+
codexExecutable,
|
|
45931
|
+
// model-tier-planning-and-arbiter task 2.5 (D1): the arbiter is pinned to the seat's
|
|
45932
|
+
// default model.
|
|
45933
|
+
model: consensusProvider ? void 0 : seat?.model ?? seat?.models?.strong
|
|
45934
|
+
},
|
|
45935
|
+
void 0,
|
|
45936
|
+
consensusProvider
|
|
45937
|
+
);
|
|
45451
45938
|
recordCost("arbiter", "arbiter", seat?.model ?? seat?.models?.strong ?? null, startedAt, result);
|
|
45452
45939
|
return result;
|
|
45453
45940
|
},
|
|
45454
45941
|
async runReviser(artifact, accepted) {
|
|
45455
45942
|
const acceptedList = accepted.map((a) => `- (${a.category}) ${a.text}`).join("\n");
|
|
45456
45943
|
const startedAt = Date.now();
|
|
45457
|
-
const result = await runAgentExec(
|
|
45458
|
-
|
|
45944
|
+
const result = await runAgentExec(
|
|
45945
|
+
{
|
|
45946
|
+
prompt: `Accepted issues to fix (each maps to an unmet acceptance criterion):
|
|
45459
45947
|
${acceptedList}
|
|
45460
45948
|
|
|
45461
45949
|
--- ACCEPTANCE CRITERIA ---
|
|
@@ -45463,14 +45951,17 @@ ${criteriaBlock}
|
|
|
45463
45951
|
|
|
45464
45952
|
--- DELIVERABLE ---
|
|
45465
45953
|
${artifact}`,
|
|
45466
|
-
|
|
45954
|
+
developerInstructions: `${DELIVERABLE_ANTIINJECTION_INSTRUCTION}
|
|
45467
45955
|
|
|
45468
45956
|
You revise a deliverable to address the accepted issues so it satisfies the acceptance criteria and stays consistent with the declared outcome${attemptOutcome ? ` (${attemptOutcome})` : ""}. Return ONLY the full revised markdown, no commentary. Do not invent facts; keep the deliverable's format and do not change the declared outcome.`,
|
|
45469
|
-
|
|
45470
|
-
|
|
45471
|
-
|
|
45472
|
-
|
|
45473
|
-
|
|
45957
|
+
cwd: sterileCwd(),
|
|
45958
|
+
timeoutMs,
|
|
45959
|
+
sandbox: "read-only",
|
|
45960
|
+
codexExecutable
|
|
45961
|
+
},
|
|
45962
|
+
void 0,
|
|
45963
|
+
consensusProvider
|
|
45964
|
+
);
|
|
45474
45965
|
recordCost("reviser", null, null, startedAt, result);
|
|
45475
45966
|
return result;
|
|
45476
45967
|
},
|
|
@@ -45501,7 +45992,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
45501
45992
|
const rounds2 = typeof r["rounds"] === "number" ? r["rounds"] : 0;
|
|
45502
45993
|
const history = r["history"];
|
|
45503
45994
|
const highlights = await curateFindings(Array.isArray(history) ? history : [], {
|
|
45504
|
-
runCodexExec: runAgentExec,
|
|
45995
|
+
runCodexExec: (execInput) => runAgentExec(execInput, void 0, consensusProvider),
|
|
45505
45996
|
loadPersona,
|
|
45506
45997
|
cwd: sterileCwd(),
|
|
45507
45998
|
timeoutMs,
|
|
@@ -45568,14 +46059,14 @@ function errMessage3(err) {
|
|
|
45568
46059
|
return err instanceof Error ? err.message : String(err);
|
|
45569
46060
|
}
|
|
45570
46061
|
function runExternalAgentsStatus(bin, args, timeoutMs, spawn6) {
|
|
45571
|
-
return new Promise((
|
|
46062
|
+
return new Promise((resolve2) => {
|
|
45572
46063
|
let settled = false;
|
|
45573
46064
|
const timerRef = { id: void 0 };
|
|
45574
46065
|
const finish = (r) => {
|
|
45575
46066
|
if (settled) return;
|
|
45576
46067
|
settled = true;
|
|
45577
46068
|
if (timerRef.id) clearTimeout(timerRef.id);
|
|
45578
|
-
|
|
46069
|
+
resolve2(r);
|
|
45579
46070
|
};
|
|
45580
46071
|
const options = { stdio: ["ignore", "pipe", "pipe"], env: process.env, shell: false };
|
|
45581
46072
|
let child;
|
|
@@ -45655,14 +46146,14 @@ function errMessage4(err) {
|
|
|
45655
46146
|
return err instanceof Error ? err.message : String(err);
|
|
45656
46147
|
}
|
|
45657
46148
|
function runExternalAgents(bin, args, label, timeoutMs, spawn6) {
|
|
45658
|
-
return new Promise((
|
|
46149
|
+
return new Promise((resolve2) => {
|
|
45659
46150
|
let settled = false;
|
|
45660
46151
|
const timerRef = { id: void 0 };
|
|
45661
46152
|
const finish = (r) => {
|
|
45662
46153
|
if (settled) return;
|
|
45663
46154
|
settled = true;
|
|
45664
46155
|
if (timerRef.id) clearTimeout(timerRef.id);
|
|
45665
|
-
|
|
46156
|
+
resolve2(r);
|
|
45666
46157
|
};
|
|
45667
46158
|
const options = {
|
|
45668
46159
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -48161,7 +48652,7 @@ var Protocol = class {
|
|
|
48161
48652
|
return;
|
|
48162
48653
|
}
|
|
48163
48654
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
48164
|
-
await new Promise((
|
|
48655
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
48165
48656
|
options?.signal?.throwIfAborted();
|
|
48166
48657
|
}
|
|
48167
48658
|
} catch (error51) {
|
|
@@ -48178,7 +48669,7 @@ var Protocol = class {
|
|
|
48178
48669
|
*/
|
|
48179
48670
|
request(request, resultSchema, options) {
|
|
48180
48671
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
48181
|
-
return new Promise((
|
|
48672
|
+
return new Promise((resolve2, reject) => {
|
|
48182
48673
|
const earlyReject = (error51) => {
|
|
48183
48674
|
reject(error51);
|
|
48184
48675
|
};
|
|
@@ -48256,7 +48747,7 @@ var Protocol = class {
|
|
|
48256
48747
|
if (!parseResult.success) {
|
|
48257
48748
|
reject(parseResult.error);
|
|
48258
48749
|
} else {
|
|
48259
|
-
|
|
48750
|
+
resolve2(parseResult.data);
|
|
48260
48751
|
}
|
|
48261
48752
|
} catch (error51) {
|
|
48262
48753
|
reject(error51);
|
|
@@ -48517,12 +49008,12 @@ var Protocol = class {
|
|
|
48517
49008
|
}
|
|
48518
49009
|
} catch {
|
|
48519
49010
|
}
|
|
48520
|
-
return new Promise((
|
|
49011
|
+
return new Promise((resolve2, reject) => {
|
|
48521
49012
|
if (signal.aborted) {
|
|
48522
49013
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
48523
49014
|
return;
|
|
48524
49015
|
}
|
|
48525
|
-
const timeoutId = setTimeout(
|
|
49016
|
+
const timeoutId = setTimeout(resolve2, interval);
|
|
48526
49017
|
signal.addEventListener("abort", () => {
|
|
48527
49018
|
clearTimeout(timeoutId);
|
|
48528
49019
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -49891,22 +50382,22 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
|
|
49891
50382
|
}
|
|
49892
50383
|
const nonInteractiveFlow = !provider.redirectUrl;
|
|
49893
50384
|
if (authorizationCode !== void 0 || nonInteractiveFlow) {
|
|
49894
|
-
const
|
|
50385
|
+
const tokens3 = await fetchToken(provider, authorizationServerUrl, {
|
|
49895
50386
|
metadata,
|
|
49896
50387
|
resource,
|
|
49897
50388
|
authorizationCode,
|
|
49898
50389
|
fetchFn
|
|
49899
50390
|
});
|
|
49900
|
-
await provider.saveTokens(
|
|
50391
|
+
await provider.saveTokens(tokens3);
|
|
49901
50392
|
return "AUTHORIZED";
|
|
49902
50393
|
}
|
|
49903
|
-
const
|
|
49904
|
-
if (
|
|
50394
|
+
const tokens2 = await provider.tokens();
|
|
50395
|
+
if (tokens2?.refresh_token) {
|
|
49905
50396
|
try {
|
|
49906
50397
|
const newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
49907
50398
|
metadata,
|
|
49908
50399
|
clientInformation,
|
|
49909
|
-
refreshToken:
|
|
50400
|
+
refreshToken: tokens2.refresh_token,
|
|
49910
50401
|
resource,
|
|
49911
50402
|
addClientAuthentication: provider.addClientAuthentication,
|
|
49912
50403
|
fetchFn
|
|
@@ -50208,7 +50699,7 @@ async function refreshAuthorization(authorizationServerUrl, { metadata, clientIn
|
|
|
50208
50699
|
grant_type: "refresh_token",
|
|
50209
50700
|
refresh_token: refreshToken
|
|
50210
50701
|
});
|
|
50211
|
-
const
|
|
50702
|
+
const tokens2 = await executeTokenRequest(authorizationServerUrl, {
|
|
50212
50703
|
metadata,
|
|
50213
50704
|
tokenRequestParams,
|
|
50214
50705
|
clientInformation,
|
|
@@ -50216,7 +50707,7 @@ async function refreshAuthorization(authorizationServerUrl, { metadata, clientIn
|
|
|
50216
50707
|
resource,
|
|
50217
50708
|
fetchFn
|
|
50218
50709
|
});
|
|
50219
|
-
return { refresh_token: refreshToken, ...
|
|
50710
|
+
return { refresh_token: refreshToken, ...tokens2 };
|
|
50220
50711
|
}
|
|
50221
50712
|
async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
|
|
50222
50713
|
const scope = provider.clientMetadata.scope;
|
|
@@ -50521,9 +51012,9 @@ var StreamableHTTPClientTransport = class {
|
|
|
50521
51012
|
async _commonHeaders() {
|
|
50522
51013
|
const headers = {};
|
|
50523
51014
|
if (this._authProvider) {
|
|
50524
|
-
const
|
|
50525
|
-
if (
|
|
50526
|
-
headers["Authorization"] = `Bearer ${
|
|
51015
|
+
const tokens2 = await this._authProvider.tokens();
|
|
51016
|
+
if (tokens2) {
|
|
51017
|
+
headers["Authorization"] = `Bearer ${tokens2.access_token}`;
|
|
50527
51018
|
}
|
|
50528
51019
|
}
|
|
50529
51020
|
if (this._sessionId) {
|
|
@@ -50958,7 +51449,7 @@ var StdioClientTransport = class {
|
|
|
50958
51449
|
if (this._process) {
|
|
50959
51450
|
throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
50960
51451
|
}
|
|
50961
|
-
return new Promise((
|
|
51452
|
+
return new Promise((resolve2, reject) => {
|
|
50962
51453
|
this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
|
50963
51454
|
// merge default env with server env because mcp server needs some env vars
|
|
50964
51455
|
env: {
|
|
@@ -50975,7 +51466,7 @@ var StdioClientTransport = class {
|
|
|
50975
51466
|
this.onerror?.(error51);
|
|
50976
51467
|
});
|
|
50977
51468
|
this._process.on("spawn", () => {
|
|
50978
|
-
|
|
51469
|
+
resolve2();
|
|
50979
51470
|
});
|
|
50980
51471
|
this._process.on("close", (_code) => {
|
|
50981
51472
|
this._process = void 0;
|
|
@@ -51040,22 +51531,22 @@ var StdioClientTransport = class {
|
|
|
51040
51531
|
if (this._process) {
|
|
51041
51532
|
const processToClose = this._process;
|
|
51042
51533
|
this._process = void 0;
|
|
51043
|
-
const closePromise = new Promise((
|
|
51534
|
+
const closePromise = new Promise((resolve2) => {
|
|
51044
51535
|
processToClose.once("close", () => {
|
|
51045
|
-
|
|
51536
|
+
resolve2();
|
|
51046
51537
|
});
|
|
51047
51538
|
});
|
|
51048
51539
|
try {
|
|
51049
51540
|
processToClose.stdin?.end();
|
|
51050
51541
|
} catch {
|
|
51051
51542
|
}
|
|
51052
|
-
await Promise.race([closePromise, new Promise((
|
|
51543
|
+
await Promise.race([closePromise, new Promise((resolve2) => setTimeout(resolve2, 2e3).unref())]);
|
|
51053
51544
|
if (processToClose.exitCode === null) {
|
|
51054
51545
|
try {
|
|
51055
51546
|
processToClose.kill("SIGTERM");
|
|
51056
51547
|
} catch {
|
|
51057
51548
|
}
|
|
51058
|
-
await Promise.race([closePromise, new Promise((
|
|
51549
|
+
await Promise.race([closePromise, new Promise((resolve2) => setTimeout(resolve2, 2e3).unref())]);
|
|
51059
51550
|
}
|
|
51060
51551
|
if (processToClose.exitCode === null) {
|
|
51061
51552
|
try {
|
|
@@ -51067,15 +51558,15 @@ var StdioClientTransport = class {
|
|
|
51067
51558
|
this._readBuffer.clear();
|
|
51068
51559
|
}
|
|
51069
51560
|
send(message) {
|
|
51070
|
-
return new Promise((
|
|
51561
|
+
return new Promise((resolve2) => {
|
|
51071
51562
|
if (!this._process?.stdin) {
|
|
51072
51563
|
throw new Error("Not connected");
|
|
51073
51564
|
}
|
|
51074
51565
|
const json2 = serializeMessage(message);
|
|
51075
51566
|
if (this._process.stdin.write(json2)) {
|
|
51076
|
-
|
|
51567
|
+
resolve2();
|
|
51077
51568
|
} else {
|
|
51078
|
-
this._process.stdin.once("drain",
|
|
51569
|
+
this._process.stdin.once("drain", resolve2);
|
|
51079
51570
|
}
|
|
51080
51571
|
});
|
|
51081
51572
|
}
|
|
@@ -52284,13 +52775,87 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
52284
52775
|
suppressedAsAdjudicated.push({ rawQuestion: clarification.rawQuestion, coveredByAnswer: covering.answer });
|
|
52285
52776
|
return false;
|
|
52286
52777
|
});
|
|
52778
|
+
let planId = null;
|
|
52779
|
+
if (!taskId) {
|
|
52780
|
+
const plans = await ctx.client.listPlans(input.projectSlug);
|
|
52781
|
+
planId = plans.find((p) => p.specWikiPageId === input.pageId)?.id ?? null;
|
|
52782
|
+
}
|
|
52783
|
+
const evaluatedContentHash = result.evaluatedSnapshot?.contentHash ?? page?.contentHash;
|
|
52784
|
+
const isSeedAbsence = operatorClarifications.some(
|
|
52785
|
+
(c) => c.rawQuestion === SEED_ABSENCE_QUESTION || c.rawQuestion.includes(SEED_ABSENCE_MARKER)
|
|
52786
|
+
);
|
|
52787
|
+
if (!isSeedAbsence && operatorClarifications.length > 0 && evaluatedContentHash) {
|
|
52788
|
+
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
52789
|
+
id: input.pageId,
|
|
52790
|
+
taskId
|
|
52791
|
+
});
|
|
52792
|
+
const suppressionEvidence = (result.droppedByCap?.length ?? 0) > 0 || suppressedAsAdjudicated.length > 0 ? {
|
|
52793
|
+
...result.droppedByCap?.length ? { droppedByCap: result.droppedByCap } : {},
|
|
52794
|
+
...suppressedAsAdjudicated.length ? { suppressedAsAdjudicated } : {}
|
|
52795
|
+
} : void 0;
|
|
52796
|
+
let ownership;
|
|
52797
|
+
if (result.evaluatedOwnership) {
|
|
52798
|
+
ownership = result.evaluatedOwnership;
|
|
52799
|
+
} else if (taskId !== null) {
|
|
52800
|
+
const task = await ctx.client.getTask(input.projectSlug, taskId).catch(() => null);
|
|
52801
|
+
ownership = { kind: "task", taskId, domains: task?.domains ?? [domain2] };
|
|
52802
|
+
} else if (planId !== null) {
|
|
52803
|
+
const plans = await ctx.client.listPlans(input.projectSlug).catch(() => []);
|
|
52804
|
+
const plan = plans.find((p) => p.id === planId);
|
|
52805
|
+
ownership = { kind: "plan", planId, domains: plan?.domains ?? ["code"] };
|
|
52806
|
+
} else {
|
|
52807
|
+
ownership = { kind: "project", domains: ["code"] };
|
|
52808
|
+
}
|
|
52809
|
+
if (ctx.client.finalizeSpecReviewCas) {
|
|
52810
|
+
const finalizeRes = await ctx.client.finalizeSpecReviewCas(input.projectSlug, input.pageId, {
|
|
52811
|
+
expectedContentHash: evaluatedContentHash,
|
|
52812
|
+
ownership,
|
|
52813
|
+
review: {
|
|
52814
|
+
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
52815
|
+
rounds: result.rounds !== void 0 ? result.rounds : result.history?.length ? result.history.length : 0,
|
|
52816
|
+
...result.history !== void 0 ? { history: result.history } : {},
|
|
52817
|
+
...result.highlights !== void 0 ? { highlights: Array.isArray(result.highlights) ? { findings: result.highlights } : result.highlights } : {},
|
|
52818
|
+
...suppressionEvidence ? { suppressionEvidence } : {}
|
|
52819
|
+
},
|
|
52820
|
+
specReady: false
|
|
52821
|
+
});
|
|
52822
|
+
if (finalizeRes.status === "superseded") {
|
|
52823
|
+
return {
|
|
52824
|
+
kind: "superseded",
|
|
52825
|
+
finalReviewedHash: evaluatedContentHash,
|
|
52826
|
+
error: "Spec content changed during review",
|
|
52827
|
+
...resolvedTaskId ? { taskId: resolvedTaskId } : {}
|
|
52828
|
+
};
|
|
52829
|
+
}
|
|
52830
|
+
if (finalizeRes.status !== "applied") {
|
|
52831
|
+
return {
|
|
52832
|
+
kind: "failed",
|
|
52833
|
+
failureReason: "consensus_blocked",
|
|
52834
|
+
error: finalizeRes.reason ?? "cas_rejected",
|
|
52835
|
+
finalReviewedHash: evaluatedContentHash,
|
|
52836
|
+
...resolvedTaskId ? { taskId: resolvedTaskId } : {}
|
|
52837
|
+
};
|
|
52838
|
+
}
|
|
52839
|
+
} else if (ctx.client.recordSpecConsensusReview) {
|
|
52840
|
+
try {
|
|
52841
|
+
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
52842
|
+
contentHash: domainScopedReviewHash2(evaluatedContentHash, domain2),
|
|
52843
|
+
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
52844
|
+
rounds: result.rounds !== void 0 ? result.rounds : result.history?.length ? result.history.length : 0,
|
|
52845
|
+
...result.history !== void 0 ? { history: result.history } : {},
|
|
52846
|
+
...result.highlights !== void 0 ? { highlights: Array.isArray(result.highlights) ? { findings: result.highlights } : result.highlights } : {},
|
|
52847
|
+
...suppressionEvidence ? { suppressionEvidence } : {}
|
|
52848
|
+
});
|
|
52849
|
+
} catch (error52) {
|
|
52850
|
+
process.stderr.write(
|
|
52851
|
+
`[mrrlin-mcp consensus] legacy clarification write failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
52852
|
+
`
|
|
52853
|
+
);
|
|
52854
|
+
}
|
|
52855
|
+
}
|
|
52856
|
+
}
|
|
52287
52857
|
let filedQuestionCount = 0;
|
|
52288
52858
|
if (toFile.length > 0) {
|
|
52289
|
-
let planId = null;
|
|
52290
|
-
if (!taskId) {
|
|
52291
|
-
const plans = await ctx.client.listPlans(input.projectSlug);
|
|
52292
|
-
planId = plans.find((p) => p.specWikiPageId === input.pageId)?.id ?? null;
|
|
52293
|
-
}
|
|
52294
52859
|
if (taskId) {
|
|
52295
52860
|
const scopeId = taskId;
|
|
52296
52861
|
const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto4.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
|
|
@@ -52307,7 +52872,11 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
52307
52872
|
questionOptions,
|
|
52308
52873
|
...groupId ? { groupId } : {}
|
|
52309
52874
|
});
|
|
52310
|
-
} catch {
|
|
52875
|
+
} catch (err) {
|
|
52876
|
+
const isConflict = err?.status === 409 || err?.statusCode === 409 || err?.code === "23505" || String(err?.message ?? "").includes("already exists") || String(err?.message ?? "").includes("duplicate key");
|
|
52877
|
+
if (!isConflict) {
|
|
52878
|
+
throw err;
|
|
52879
|
+
}
|
|
52311
52880
|
}
|
|
52312
52881
|
}
|
|
52313
52882
|
filedQuestionCount = toFile.length;
|
|
@@ -52327,7 +52896,11 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
52327
52896
|
questionOptions,
|
|
52328
52897
|
...groupId ? { groupId } : {}
|
|
52329
52898
|
});
|
|
52330
|
-
} catch {
|
|
52899
|
+
} catch (err) {
|
|
52900
|
+
const isConflict = err?.status === 409 || err?.statusCode === 409 || err?.code === "23505" || String(err?.message ?? "").includes("already exists") || String(err?.message ?? "").includes("duplicate key");
|
|
52901
|
+
if (!isConflict) {
|
|
52902
|
+
throw err;
|
|
52903
|
+
}
|
|
52331
52904
|
}
|
|
52332
52905
|
}
|
|
52333
52906
|
filedQuestionCount = toFile.length;
|
|
@@ -52356,40 +52929,15 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
52356
52929
|
} catch {
|
|
52357
52930
|
}
|
|
52358
52931
|
}
|
|
52359
|
-
if (operatorClarifications.length > 0) {
|
|
52360
|
-
try {
|
|
52361
|
-
if (page) {
|
|
52362
|
-
const domain2 = await resolveSpecDomain(ctx.client, input.projectSlug, {
|
|
52363
|
-
id: page.id,
|
|
52364
|
-
taskId: page.taskId ?? null
|
|
52365
|
-
});
|
|
52366
|
-
const suppressionEvidence = (result.droppedByCap?.length ?? 0) > 0 || suppressedAsAdjudicated.length > 0 ? {
|
|
52367
|
-
...result.droppedByCap?.length ? { droppedByCap: result.droppedByCap } : {},
|
|
52368
|
-
...suppressedAsAdjudicated.length ? { suppressedAsAdjudicated } : {}
|
|
52369
|
-
} : void 0;
|
|
52370
|
-
await ctx.client.recordSpecConsensusReview(input.projectSlug, input.pageId, {
|
|
52371
|
-
contentHash: domainScopedReviewHash2(page.contentHash, domain2),
|
|
52372
|
-
outcome: "CONSENSUS_BLOCKED_CLARIFICATION",
|
|
52373
|
-
rounds: 0,
|
|
52374
|
-
...suppressionEvidence ? { suppressionEvidence } : {}
|
|
52375
|
-
});
|
|
52376
|
-
}
|
|
52377
|
-
} catch (error52) {
|
|
52378
|
-
process.stderr.write(
|
|
52379
|
-
`[mrrlin-mcp consensus] clarification verdict write failed for ${input.projectSlug}/${input.pageId}: ${error52 instanceof Error ? error52.message : String(error52)}
|
|
52380
|
-
`
|
|
52381
|
-
);
|
|
52382
|
-
}
|
|
52383
|
-
}
|
|
52384
52932
|
let error51;
|
|
52385
52933
|
if (filedQuestionCount > 0) {
|
|
52386
52934
|
error51 = `Consensus blocked on ${filedQuestionCount} clarification(s); filed as operator question(s).`;
|
|
52387
52935
|
} else if (diagnostics.length > 0 && operatorClarifications.length === 0) {
|
|
52388
52936
|
const diagnosticKinds = new Set(diagnostics.map((d) => d.kind));
|
|
52389
52937
|
if (diagnosticKinds.has("acceptance_criteria_unauthorable")) {
|
|
52390
|
-
error51 = "Consensus
|
|
52938
|
+
error51 = "Consensus paused: the spec needs a clear `## Acceptance criteria` checklist (`- [ ]`) before review can complete. Please add or update the checklist in the spec.";
|
|
52391
52939
|
} else {
|
|
52392
|
-
error51 = "Consensus
|
|
52940
|
+
error51 = "Consensus paused: the spec content does not match its linked task. Please update the spec or task relationship directly, not an inbox answer.";
|
|
52393
52941
|
}
|
|
52394
52942
|
} else if (operatorClarifications.length > 0 && toFile.length === 0 && suppressedAsAdjudicated.length > 0) {
|
|
52395
52943
|
error51 = `The reviewers raised ${suppressedAsAdjudicated.length} clarification(s), but each was a reworded repeat of one already answered \u2014 nothing new to answer.`;
|
|
@@ -52401,11 +52949,13 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
|
|
|
52401
52949
|
if (result.escalation) {
|
|
52402
52950
|
error51 += ` (reason: ${result.escalation.diagnosis})`;
|
|
52403
52951
|
}
|
|
52952
|
+
const isRelationshipDiagnostic = isSeedAbsence || result.diagnostics?.some((d) => d.kind === "spec_identity_mismatch") || result.escalation?.diagnosis === "spec_identity_mismatch";
|
|
52404
52953
|
return {
|
|
52405
52954
|
kind: "failed",
|
|
52406
52955
|
failureReason: "consensus_blocked",
|
|
52407
52956
|
error: error51,
|
|
52408
52957
|
filedQuestionCount,
|
|
52958
|
+
finalReviewedHash: isRelationshipDiagnostic ? null : result.evaluatedSnapshot?.contentHash ?? null,
|
|
52409
52959
|
...resolvedTaskId ? { taskId: resolvedTaskId } : {}
|
|
52410
52960
|
};
|
|
52411
52961
|
}
|
|
@@ -52419,18 +52969,30 @@ async function retireGateQuestionsOnConverge(ctx, input, resolvedTaskId) {
|
|
|
52419
52969
|
const contentHash = page?.contentHash ?? null;
|
|
52420
52970
|
const inboxItems = await ctx.client.listInboxItems(input.projectSlug, { taskId: resolvedTaskId });
|
|
52421
52971
|
for (const item of inboxItems) {
|
|
52422
|
-
if (item.
|
|
52972
|
+
if (item.status !== "pending") {
|
|
52973
|
+
continue;
|
|
52974
|
+
}
|
|
52975
|
+
if (item.taskId != null && item.taskId !== resolvedTaskId) {
|
|
52976
|
+
continue;
|
|
52977
|
+
}
|
|
52978
|
+
const isGateQuestion = (item.origin === "gate" || item.origin === "gate_block") && item.kind === "question";
|
|
52979
|
+
const specRefPattern = new RegExp(
|
|
52980
|
+
`Specs/${input.pageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![0-9a-zA-Z_-])`
|
|
52981
|
+
);
|
|
52982
|
+
const isSpecHandoff = item.kind === "handoff" && item.id.startsWith(`II-SPEC-STOPPED-${input.pageId}-`) && specRefPattern.test(item.body);
|
|
52983
|
+
if (!isGateQuestion && !isSpecHandoff) {
|
|
52423
52984
|
continue;
|
|
52424
52985
|
}
|
|
52425
52986
|
try {
|
|
52987
|
+
const verdict = isGateQuestion ? "rejected" : "acknowledged";
|
|
52426
52988
|
await ctx.client.decideInboxItem(input.projectSlug, item.id, {
|
|
52427
|
-
verdict
|
|
52989
|
+
verdict,
|
|
52428
52990
|
decidedBy: "gate",
|
|
52429
52991
|
reason: `Superseded by converged spec Specs/${input.pageId} (contentHash: ${contentHash ?? "unknown"})`
|
|
52430
52992
|
});
|
|
52431
52993
|
} catch (itemError) {
|
|
52432
52994
|
process.stderr.write(
|
|
52433
|
-
`[mrrlin-mcp consensus] gate
|
|
52995
|
+
`[mrrlin-mcp consensus] gate retirement failed for ${input.projectSlug}/${input.pageId} item ${item.id}: ${itemError instanceof Error ? itemError.message : String(itemError)}
|
|
52434
52996
|
`
|
|
52435
52997
|
);
|
|
52436
52998
|
}
|
|
@@ -52520,6 +53082,13 @@ async function run(ctx, input, builders = defaultNativeSpecGateBuilders) {
|
|
|
52520
53082
|
return { kind: "retry", failureReason: "provider_unavailable", error: "Consensus unavailable (provider/quota).", ...resolvedTaskId ? { taskId: resolvedTaskId } : {} };
|
|
52521
53083
|
case "SPEC_CHANGED_DURING_REVIEW":
|
|
52522
53084
|
return { kind: "retry", failureReason: "spec_changed", error: "Spec changed during review.", ...resolvedTaskId ? { taskId: resolvedTaskId } : {} };
|
|
53085
|
+
case "SUPERSEDED":
|
|
53086
|
+
return {
|
|
53087
|
+
kind: "superseded",
|
|
53088
|
+
reason: "Spec changed or ownership unlinked during review",
|
|
53089
|
+
error: "Spec changed or ownership unlinked during review",
|
|
53090
|
+
...resolvedTaskId ? { taskId: resolvedTaskId } : {}
|
|
53091
|
+
};
|
|
52523
53092
|
default:
|
|
52524
53093
|
return {
|
|
52525
53094
|
kind: "failed",
|
|
@@ -52530,6 +53099,14 @@ async function run(ctx, input, builders = defaultNativeSpecGateBuilders) {
|
|
|
52530
53099
|
}
|
|
52531
53100
|
}
|
|
52532
53101
|
function onComplete(outcome, input) {
|
|
53102
|
+
if (outcome.kind === "superseded") {
|
|
53103
|
+
return {
|
|
53104
|
+
kind: "info",
|
|
53105
|
+
suppressInbox: true,
|
|
53106
|
+
body: outcome.reason ?? "Superseded",
|
|
53107
|
+
...outcome.taskId ? { taskId: outcome.taskId } : {}
|
|
53108
|
+
};
|
|
53109
|
+
}
|
|
52533
53110
|
const taskId = "taskId" in outcome ? outcome.taskId : void 0;
|
|
52534
53111
|
const taskRef = taskId ? outcome.kind === "done" && outcome.taskTitle ? `${taskId} \u2014 ${outcome.taskTitle}` : taskId : null;
|
|
52535
53112
|
if (outcome.kind === "done") {
|
|
@@ -52579,10 +53156,12 @@ ${findings.map((f) => `\u2022 ${f}`).join("\n")}` : "";
|
|
|
52579
53156
|
};
|
|
52580
53157
|
}
|
|
52581
53158
|
const handoffTaskRef = taskRef ? ` (${taskRef})` : "";
|
|
53159
|
+
const revisionSuffix = "finalReviewedHash" in outcome && outcome.finalReviewedHash ? outcome.finalReviewedHash.slice(0, 8) : "init";
|
|
52582
53160
|
return {
|
|
52583
53161
|
kind: "handoff",
|
|
53162
|
+
id: `II-SPEC-STOPPED-${input.pageId}-${revisionSuffix}`,
|
|
52584
53163
|
...taskId ? { taskId } : {},
|
|
52585
|
-
body: `The spec review stopped for Specs/${input.pageId}${handoffTaskRef}. ${outcome.error} Review the
|
|
53164
|
+
body: `The spec review stopped for Specs/${input.pageId}${handoffTaskRef}. ${outcome.error} Review the spec or consult the Director to update it.`
|
|
52586
53165
|
};
|
|
52587
53166
|
}
|
|
52588
53167
|
var consensusDescriptor = {
|
|
@@ -52761,6 +53340,14 @@ async function run2(ctx, input) {
|
|
|
52761
53340
|
};
|
|
52762
53341
|
}
|
|
52763
53342
|
function onComplete2(outcome, input) {
|
|
53343
|
+
if (outcome.kind === "superseded") {
|
|
53344
|
+
return {
|
|
53345
|
+
kind: "info",
|
|
53346
|
+
suppressInbox: true,
|
|
53347
|
+
body: outcome.reason ?? outcome.error ?? "Superseded",
|
|
53348
|
+
...outcome.taskId ? { taskId: outcome.taskId } : {}
|
|
53349
|
+
};
|
|
53350
|
+
}
|
|
52764
53351
|
const taskId = outcome.kind === "failed" || outcome.kind === "done" ? outcome.taskId : void 0;
|
|
52765
53352
|
const taskRef = taskId ? outcome.kind === "done" && outcome.taskTitle ? `${taskId} \u2014 ${outcome.taskTitle}` : taskId : null;
|
|
52766
53353
|
if (outcome.kind === "done") {
|
|
@@ -52815,10 +53402,10 @@ var DRAFT_SPEC_TLDR_MAX_CHARS = 300;
|
|
|
52815
53402
|
var DRAFT_SPEC_PERSONA = "spec-author";
|
|
52816
53403
|
var PAGE_ID_MAX_ATTEMPTS = 5;
|
|
52817
53404
|
function defaultSleep(ms) {
|
|
52818
|
-
return new Promise((
|
|
53405
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
52819
53406
|
}
|
|
52820
53407
|
function enforceSpecOutputContract(markdown, taskId, title) {
|
|
52821
|
-
let body = markdown.trim();
|
|
53408
|
+
let body = normalizeAcceptanceCriteria(markdown.trim());
|
|
52822
53409
|
const headingRe = new RegExp(`^#\\s+${taskId}\\b`, "m");
|
|
52823
53410
|
if (!headingRe.test(body)) {
|
|
52824
53411
|
body = `# ${taskId} \u2014 ${title}
|
|
@@ -53275,6 +53862,14 @@ async function runDraftSpec(ctx, input, deps) {
|
|
|
53275
53862
|
};
|
|
53276
53863
|
}
|
|
53277
53864
|
function onComplete3(outcome, input) {
|
|
53865
|
+
if (outcome.kind === "superseded") {
|
|
53866
|
+
return {
|
|
53867
|
+
kind: "info",
|
|
53868
|
+
suppressInbox: true,
|
|
53869
|
+
body: outcome.reason ?? outcome.error ?? "Superseded",
|
|
53870
|
+
taskId: input.taskId
|
|
53871
|
+
};
|
|
53872
|
+
}
|
|
53278
53873
|
if (outcome.kind === "done") {
|
|
53279
53874
|
let parsed = {};
|
|
53280
53875
|
try {
|
|
@@ -54167,6 +54762,9 @@ async function runReportEvidence(ctx, input) {
|
|
|
54167
54762
|
return { kind: "done", resultJson: JSON.stringify(artifact), finalReviewedHash: null };
|
|
54168
54763
|
}
|
|
54169
54764
|
function onComplete5(outcome, input) {
|
|
54765
|
+
if (outcome.kind === "superseded") {
|
|
54766
|
+
return { kind: "info", body: `Evidence assembly superseded for report ${input.reportId}.`, suppressInbox: true };
|
|
54767
|
+
}
|
|
54170
54768
|
if (outcome.kind === "done") {
|
|
54171
54769
|
return { kind: "info", body: `Evidence assembled for report ${input.reportId}.`, suppressInbox: true };
|
|
54172
54770
|
}
|
|
@@ -55350,7 +55948,7 @@ var mcpToolDescriptions = {
|
|
|
55350
55948
|
[mcpToolNames.appendTaskEvent]: "Append an operator note to a task's activity timeline. Also use it as the task-linked receipt path for material immediate work that has a parent task; summarize scope, outcome/no-result, evidence or artifacts, blockers, and next action in the note body.",
|
|
55351
55949
|
[mcpToolNames.createWikiPage]: "Create a markdown Wiki page.",
|
|
55352
55950
|
[mcpToolNames.deleteTaskDependency]: "Delete a blocked-by relationship.",
|
|
55353
|
-
[mcpToolNames.createTask]: "Create a new task in a project. Defaults to status='
|
|
55951
|
+
[mcpToolNames.createTask]: "Create a new task in a project. Defaults to status='todo' to authorize immediate autopilot work; pass status='backlog' only if explicitly parking the task for later. 'backlog' and 'todo' are the only creatable statuses. Pass parentTaskId when spawning a follow-up task to continue work from an existing task \u2014 its operator_seed_request is inherited automatically if you don't supply your own. Domain is required UNLESS parentTaskId is supplied (a follow-up inherits its parent's domain automatically). Domain selects which reviewer roster judges the task's spec. Valid values: code, seo, marketing, content. When creating tasks for work that produces a published web page, author the work as a [code_change] task and a [page] task (with domain: 'content') linked by blocked_by. Set autoDeploy according to the publish policy: 'prod' if only meaningful once live (robots.txt, sitemap), 'dev' to stop at preview (landing page, layout), or 'off' if the repo is not the operator's own.",
|
|
55354
55952
|
[mcpToolNames.getProject]: "Fetch a single project summary by slug.",
|
|
55355
55953
|
[mcpToolNames.getProjectSnapshot]: "Fetch the full project snapshot: GitHub binding, tasks, plans, and wiki pages.",
|
|
55356
55954
|
[mcpToolNames.getTask]: "Fetch a single task by project slug and task id.",
|
|
@@ -55591,20 +56189,18 @@ async function getAgentProviderForPanel(client, opts = {}) {
|
|
|
55591
56189
|
}
|
|
55592
56190
|
function createMrrlinTools(options) {
|
|
55593
56191
|
const client = createMrrlinClient(withAuth(options));
|
|
55594
|
-
let boundSlug = options.activeProjectSlug ?? (process.env.MRRLIN_PROJECT_SLUG?.trim() || void 0);
|
|
55595
|
-
let sessionResolved = false;
|
|
55596
56192
|
async function resolveBoundSlug() {
|
|
55597
|
-
if (
|
|
55598
|
-
|
|
56193
|
+
if (options.activeProjectSlug !== void 0) return options.activeProjectSlug;
|
|
56194
|
+
const envSlug = process.env.MRRLIN_PROJECT_SLUG?.trim();
|
|
56195
|
+
if (envSlug) return envSlug;
|
|
55599
56196
|
try {
|
|
55600
56197
|
const settings = await client.getOperatorSettings();
|
|
55601
56198
|
if (settings?.activeProjectSlug) {
|
|
55602
|
-
|
|
56199
|
+
return settings.activeProjectSlug;
|
|
55603
56200
|
}
|
|
55604
56201
|
} catch {
|
|
55605
56202
|
}
|
|
55606
|
-
|
|
55607
|
-
return boundSlug;
|
|
56203
|
+
return void 0;
|
|
55608
56204
|
}
|
|
55609
56205
|
async function checkTenantBoundary(targetSlug, resourceName) {
|
|
55610
56206
|
const active = await resolveBoundSlug();
|
|
@@ -55734,6 +56330,13 @@ function createMrrlinTools(options) {
|
|
|
55734
56330
|
...input,
|
|
55735
56331
|
folder: input.folder?.trim().toLowerCase() === "specs" ? "Specs" : input.folder
|
|
55736
56332
|
};
|
|
56333
|
+
if (pageInput.folder === "Specs") {
|
|
56334
|
+
if (!parseAcceptanceCriteria(pageInput.markdownBody)) {
|
|
56335
|
+
throw new Error(
|
|
56336
|
+
"Error: Specifications in folder 'Specs' MUST include an '## Acceptance criteria' section with at least one top-level markdown checklist item ('- [ ] <condition>'). Please re-author the page body with verifiable checklist criteria."
|
|
56337
|
+
);
|
|
56338
|
+
}
|
|
56339
|
+
}
|
|
55737
56340
|
return await deliverBuiltIn(
|
|
55738
56341
|
c,
|
|
55739
56342
|
projectSlug,
|
|
@@ -55760,7 +56363,8 @@ function createMrrlinTools(options) {
|
|
|
55760
56363
|
"Unable to create task.",
|
|
55761
56364
|
async (c, { projectSlug, ...input }) => {
|
|
55762
56365
|
await checkTenantBoundary(projectSlug, "task");
|
|
55763
|
-
|
|
56366
|
+
const status = input.status ?? "todo";
|
|
56367
|
+
return await c.createTask(projectSlug, { ...input, status });
|
|
55764
56368
|
}
|
|
55765
56369
|
),
|
|
55766
56370
|
[mcpToolNames.getProject]: makeTool(
|
|
@@ -55979,6 +56583,20 @@ function createMrrlinTools(options) {
|
|
|
55979
56583
|
...patch,
|
|
55980
56584
|
...patch.folder !== void 0 ? { folder: patch.folder.trim().toLowerCase() === "specs" ? "Specs" : patch.folder } : {}
|
|
55981
56585
|
};
|
|
56586
|
+
let targetFolder = patchInput.folder;
|
|
56587
|
+
let targetBody = patch.markdownBody;
|
|
56588
|
+
if ((targetFolder === void 0 || targetBody === void 0) && (targetFolder === "Specs" || targetBody !== void 0)) {
|
|
56589
|
+
const existing = await c.getWikiPage(projectSlug, pageId);
|
|
56590
|
+
if (targetFolder === void 0) targetFolder = existing.folder;
|
|
56591
|
+
if (targetBody === void 0) targetBody = existing.markdownBody;
|
|
56592
|
+
}
|
|
56593
|
+
if (targetFolder === "Specs" && targetBody !== void 0) {
|
|
56594
|
+
if (!parseAcceptanceCriteria(targetBody)) {
|
|
56595
|
+
throw new Error(
|
|
56596
|
+
"Error: Specifications in folder 'Specs' MUST include an '## Acceptance criteria' section with at least one top-level markdown checklist item ('- [ ] <condition>'). Please re-author the page body with verifiable checklist criteria."
|
|
56597
|
+
);
|
|
56598
|
+
}
|
|
56599
|
+
}
|
|
55982
56600
|
return await deliverBuiltIn(
|
|
55983
56601
|
c,
|
|
55984
56602
|
projectSlug,
|
|
@@ -56948,7 +57566,7 @@ var import_node_http = __toESM(require("node:http"), 1);
|
|
|
56948
57566
|
var import_node_os17 = __toESM(require("node:os"), 1);
|
|
56949
57567
|
var import_node_path38 = __toESM(require("node:path"), 1);
|
|
56950
57568
|
var import_node_child_process21 = require("node:child_process");
|
|
56951
|
-
var
|
|
57569
|
+
var import_node_crypto16 = __toESM(require("node:crypto"), 1);
|
|
56952
57570
|
|
|
56953
57571
|
// src/turn-replay-buffer.ts
|
|
56954
57572
|
var DEFAULT_MAX_EVENTS = 2e3;
|
|
@@ -57467,7 +58085,7 @@ function createTurnLock() {
|
|
|
57467
58085
|
} else {
|
|
57468
58086
|
autonomousTotalActive += 1;
|
|
57469
58087
|
}
|
|
57470
|
-
return new Promise((
|
|
58088
|
+
return new Promise((resolve2, reject) => {
|
|
57471
58089
|
const start = () => {
|
|
57472
58090
|
let bodyPromise;
|
|
57473
58091
|
try {
|
|
@@ -57475,7 +58093,7 @@ function createTurnLock() {
|
|
|
57475
58093
|
} catch (e) {
|
|
57476
58094
|
bodyPromise = Promise.reject(e);
|
|
57477
58095
|
}
|
|
57478
|
-
bodyPromise.then((v) =>
|
|
58096
|
+
bodyPromise.then((v) => resolve2(v)).catch((e) => reject(e)).finally(() => {
|
|
57479
58097
|
if (lane === "chat") {
|
|
57480
58098
|
chatTotalActive -= 1;
|
|
57481
58099
|
} else {
|
|
@@ -58077,7 +58695,717 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
58077
58695
|
}
|
|
58078
58696
|
|
|
58079
58697
|
// src/_generated/version.ts
|
|
58080
|
-
var PKG_VERSION = "0.4.
|
|
58698
|
+
var PKG_VERSION = "0.4.98";
|
|
58699
|
+
|
|
58700
|
+
// src/director-closure.ts
|
|
58701
|
+
var CLOSURE_SNAPSHOT_TIMEOUT_MS = 2e3;
|
|
58702
|
+
var CLOSURE_MAX_SNAPSHOT_PAGES = 500;
|
|
58703
|
+
var CLOSURE_MAX_SNAPSHOT_TASKS = 2e3;
|
|
58704
|
+
var CLOSURE_MAX_SNAPSHOT_INBOX = 500;
|
|
58705
|
+
var CLOSURE_MIN_PROSE_CHARS = 25;
|
|
58706
|
+
var CLOSURE_MIN_PROSE_WORDS = 4;
|
|
58707
|
+
var ID_RE = /\b(?:GT|WK)-\d+\b/g;
|
|
58708
|
+
function diffSnapshots(before, after) {
|
|
58709
|
+
const receipts = [];
|
|
58710
|
+
const diffMap = (kind, prev, next) => {
|
|
58711
|
+
for (const [id, entry] of next) {
|
|
58712
|
+
const old = prev.get(id);
|
|
58713
|
+
if (!old) {
|
|
58714
|
+
receipts.push({ kind, action: "created", id, title: entry.title });
|
|
58715
|
+
} else if (entry.updatedAt !== null && entry.updatedAt !== old.updatedAt) {
|
|
58716
|
+
receipts.push({ kind, action: "updated", id, title: entry.title });
|
|
58717
|
+
}
|
|
58718
|
+
}
|
|
58719
|
+
};
|
|
58720
|
+
diffMap("task", before.tasks, after.tasks);
|
|
58721
|
+
if (before.pages && after.pages) diffMap("page", before.pages, after.pages);
|
|
58722
|
+
if (before.inbox && after.inbox) {
|
|
58723
|
+
for (const id of after.inbox) {
|
|
58724
|
+
if (!before.inbox.has(id)) receipts.push({ kind: "inbox", action: "created", id, title: "" });
|
|
58725
|
+
}
|
|
58726
|
+
}
|
|
58727
|
+
return receipts;
|
|
58728
|
+
}
|
|
58729
|
+
function finalAssistantText(events) {
|
|
58730
|
+
let lastToolResult = -1;
|
|
58731
|
+
events.forEach((event, index) => {
|
|
58732
|
+
if (event.type === "tool_result") lastToolResult = index;
|
|
58733
|
+
});
|
|
58734
|
+
return events.slice(lastToolResult + 1).filter((event) => event.type === "assistant" && typeof event.content === "string").map((event) => event.content).join("");
|
|
58735
|
+
}
|
|
58736
|
+
var MUTATION_TOOLS = /* @__PURE__ */ new Set([
|
|
58737
|
+
"create_task",
|
|
58738
|
+
"update_task",
|
|
58739
|
+
"create_wiki_page",
|
|
58740
|
+
"update_wiki_page",
|
|
58741
|
+
"create_inbox_item"
|
|
58742
|
+
]);
|
|
58743
|
+
function extractStructuredResultId(content) {
|
|
58744
|
+
if (!content) return null;
|
|
58745
|
+
let parsed = content;
|
|
58746
|
+
if (typeof content === "string") {
|
|
58747
|
+
try {
|
|
58748
|
+
parsed = JSON.parse(content);
|
|
58749
|
+
} catch {
|
|
58750
|
+
const trimmed = content.trim();
|
|
58751
|
+
if (/^(?:GT|WK)-\d+$/.test(trimmed) || /^II-[0-9a-zA-Z-]+$/.test(trimmed)) {
|
|
58752
|
+
return trimmed;
|
|
58753
|
+
}
|
|
58754
|
+
return null;
|
|
58755
|
+
}
|
|
58756
|
+
}
|
|
58757
|
+
if (Array.isArray(parsed)) {
|
|
58758
|
+
for (const item of parsed) {
|
|
58759
|
+
const id = extractStructuredResultId(item);
|
|
58760
|
+
if (id) return id;
|
|
58761
|
+
}
|
|
58762
|
+
return null;
|
|
58763
|
+
}
|
|
58764
|
+
if (parsed && typeof parsed === "object") {
|
|
58765
|
+
const obj = parsed;
|
|
58766
|
+
if (typeof obj.text === "string") {
|
|
58767
|
+
const id = extractStructuredResultId(obj.text);
|
|
58768
|
+
if (id) return id;
|
|
58769
|
+
}
|
|
58770
|
+
if (obj.structuredContent && typeof obj.structuredContent === "object") {
|
|
58771
|
+
const sc = obj.structuredContent;
|
|
58772
|
+
if (sc.data && typeof sc.data === "object" && typeof sc.data.id === "string") {
|
|
58773
|
+
return sc.data.id;
|
|
58774
|
+
}
|
|
58775
|
+
if (typeof sc.id === "string") return sc.id;
|
|
58776
|
+
}
|
|
58777
|
+
if (obj.data && typeof obj.data === "object" && typeof obj.data.id === "string") {
|
|
58778
|
+
return obj.data.id;
|
|
58779
|
+
}
|
|
58780
|
+
if (typeof obj.id === "string") {
|
|
58781
|
+
return obj.id;
|
|
58782
|
+
}
|
|
58783
|
+
if (Array.isArray(obj.content)) {
|
|
58784
|
+
for (const item of obj.content) {
|
|
58785
|
+
const id = extractStructuredResultId(item);
|
|
58786
|
+
if (id) return id;
|
|
58787
|
+
}
|
|
58788
|
+
}
|
|
58789
|
+
}
|
|
58790
|
+
return null;
|
|
58791
|
+
}
|
|
58792
|
+
function isErrorResult(event) {
|
|
58793
|
+
if (event.is_error === true || event.is_error === "true") return true;
|
|
58794
|
+
if (event.tool && typeof event.tool === "object") {
|
|
58795
|
+
if (typeof event.tool.exitCode === "number" && event.tool.exitCode !== 0) return true;
|
|
58796
|
+
if (typeof event.tool.status === "string" && event.tool.status === "error") return true;
|
|
58797
|
+
}
|
|
58798
|
+
const checkError = (val) => {
|
|
58799
|
+
if (!val) return false;
|
|
58800
|
+
if (Array.isArray(val)) {
|
|
58801
|
+
return val.some(checkError);
|
|
58802
|
+
}
|
|
58803
|
+
if (typeof val === "object") {
|
|
58804
|
+
const obj = val;
|
|
58805
|
+
if (obj.isError === true || obj.isError === "true" || obj.error !== void 0) return true;
|
|
58806
|
+
if (Array.isArray(obj.content) && obj.content.some(checkError)) return true;
|
|
58807
|
+
if (typeof obj.text === "string") {
|
|
58808
|
+
try {
|
|
58809
|
+
return checkError(JSON.parse(obj.text));
|
|
58810
|
+
} catch {
|
|
58811
|
+
}
|
|
58812
|
+
}
|
|
58813
|
+
} else if (typeof val === "string") {
|
|
58814
|
+
try {
|
|
58815
|
+
return checkError(JSON.parse(val));
|
|
58816
|
+
} catch {
|
|
58817
|
+
}
|
|
58818
|
+
}
|
|
58819
|
+
return false;
|
|
58820
|
+
};
|
|
58821
|
+
return checkError(event.content);
|
|
58822
|
+
}
|
|
58823
|
+
function turnReferences(events) {
|
|
58824
|
+
const mutatedIds = /* @__PURE__ */ new Set();
|
|
58825
|
+
const mutatedInboxIds = /* @__PURE__ */ new Set();
|
|
58826
|
+
const textIds = /* @__PURE__ */ new Set();
|
|
58827
|
+
let hasToolEvents = false;
|
|
58828
|
+
const keyedPending = /* @__PURE__ */ new Map();
|
|
58829
|
+
const unkeyedCalls = [];
|
|
58830
|
+
for (const event of events) {
|
|
58831
|
+
if (event.type === "tool_use") {
|
|
58832
|
+
hasToolEvents = true;
|
|
58833
|
+
const rawName = event.tool?.name ?? event.name ?? "";
|
|
58834
|
+
const toolName = rawName.replace(/^.*__/, "");
|
|
58835
|
+
const isMutation = MUTATION_TOOLS.has(toolName);
|
|
58836
|
+
const inputObj = event.tool?.input && typeof event.tool.input === "object" ? event.tool.input : event.input && typeof event.input === "object" ? event.input : {};
|
|
58837
|
+
const rawTargetId = typeof inputObj.taskId === "string" ? inputObj.taskId : typeof inputObj.pageId === "string" ? inputObj.pageId : typeof inputObj.id === "string" ? inputObj.id : null;
|
|
58838
|
+
let targetId = null;
|
|
58839
|
+
if (rawTargetId) {
|
|
58840
|
+
if ((toolName === "create_task" || toolName === "update_task") && /^GT-\d+$/.test(rawTargetId)) {
|
|
58841
|
+
targetId = rawTargetId;
|
|
58842
|
+
} else if ((toolName === "create_wiki_page" || toolName === "update_wiki_page") && /^WK-\d+$/.test(rawTargetId)) {
|
|
58843
|
+
targetId = rawTargetId;
|
|
58844
|
+
}
|
|
58845
|
+
}
|
|
58846
|
+
const callId = event.tool?.id ?? event.tool_use_id ?? event.id;
|
|
58847
|
+
if (callId && typeof callId === "string") {
|
|
58848
|
+
if (isMutation) {
|
|
58849
|
+
keyedPending.set(callId, { toolName, targetId });
|
|
58850
|
+
}
|
|
58851
|
+
} else {
|
|
58852
|
+
if (isMutation) {
|
|
58853
|
+
unkeyedCalls.push({ isMutation: true, toolName, targetId });
|
|
58854
|
+
} else {
|
|
58855
|
+
unkeyedCalls.push({ isMutation: false });
|
|
58856
|
+
}
|
|
58857
|
+
}
|
|
58858
|
+
} else if (event.type === "tool_result") {
|
|
58859
|
+
hasToolEvents = true;
|
|
58860
|
+
const callId = event.tool?.id ?? event.tool_use_id ?? event.id;
|
|
58861
|
+
let mutation;
|
|
58862
|
+
if (callId && typeof callId === "string") {
|
|
58863
|
+
mutation = keyedPending.get(callId);
|
|
58864
|
+
keyedPending.delete(callId);
|
|
58865
|
+
} else if (unkeyedCalls.length > 0) {
|
|
58866
|
+
const unkeyed = unkeyedCalls.shift();
|
|
58867
|
+
if (unkeyed && unkeyed.isMutation) {
|
|
58868
|
+
mutation = { toolName: unkeyed.toolName, targetId: unkeyed.targetId };
|
|
58869
|
+
}
|
|
58870
|
+
}
|
|
58871
|
+
if (mutation && !isErrorResult(event)) {
|
|
58872
|
+
const returnedId = extractStructuredResultId(event.content);
|
|
58873
|
+
if (mutation.toolName === "create_inbox_item") {
|
|
58874
|
+
if (returnedId && /^II-[0-9a-zA-Z-]+$/.test(returnedId)) {
|
|
58875
|
+
mutatedInboxIds.add(returnedId);
|
|
58876
|
+
}
|
|
58877
|
+
} else {
|
|
58878
|
+
if (mutation.targetId) {
|
|
58879
|
+
mutatedIds.add(mutation.targetId);
|
|
58880
|
+
}
|
|
58881
|
+
if (returnedId) {
|
|
58882
|
+
if ((mutation.toolName === "create_task" || mutation.toolName === "update_task") && /^GT-\d+$/.test(returnedId)) {
|
|
58883
|
+
mutatedIds.add(returnedId);
|
|
58884
|
+
} else if ((mutation.toolName === "create_wiki_page" || mutation.toolName === "update_wiki_page") && /^WK-\d+$/.test(returnedId)) {
|
|
58885
|
+
mutatedIds.add(returnedId);
|
|
58886
|
+
}
|
|
58887
|
+
}
|
|
58888
|
+
}
|
|
58889
|
+
}
|
|
58890
|
+
} else if (event.type === "assistant" && typeof event.content === "string") {
|
|
58891
|
+
for (const id of event.content.match(ID_RE) ?? []) textIds.add(id);
|
|
58892
|
+
}
|
|
58893
|
+
}
|
|
58894
|
+
return { mutatedIds, mutatedInboxIds, textIds, hasToolEvents };
|
|
58895
|
+
}
|
|
58896
|
+
function attributeReceipts(receipts, refs) {
|
|
58897
|
+
if (refs.hasToolEvents) {
|
|
58898
|
+
const board2 = receipts.filter((r) => r.kind !== "inbox" && refs.mutatedIds.has(r.id));
|
|
58899
|
+
const inbox = receipts.filter((r) => r.kind === "inbox" && refs.mutatedInboxIds.has(r.id));
|
|
58900
|
+
return [...board2, ...inbox];
|
|
58901
|
+
}
|
|
58902
|
+
const board = receipts.filter((r) => r.kind !== "inbox" && refs.textIds.has(r.id));
|
|
58903
|
+
return [...board];
|
|
58904
|
+
}
|
|
58905
|
+
function closurePresent(finalText, receipts) {
|
|
58906
|
+
const mentioned = new Set(finalText.match(ID_RE) ?? []);
|
|
58907
|
+
if (!receipts.every((receipt) => receipt.kind === "inbox" || mentioned.has(receipt.id))) return false;
|
|
58908
|
+
if (receipts.some((receipt) => receipt.kind === "inbox") && !/(?<![\p{L}\p{N}])(?:inbox|інбокс|вхідні)(?![\p{L}\p{N}])/iu.test(finalText)) return false;
|
|
58909
|
+
const nonIdText = finalText.replace(ID_RE, "");
|
|
58910
|
+
const letters = nonIdText.match(new RegExp("\\p{L}", "gu")) ?? [];
|
|
58911
|
+
if (letters.length < CLOSURE_MIN_PROSE_CHARS) return false;
|
|
58912
|
+
const words = nonIdText.match(new RegExp("\\p{L}{2,}", "gu")) ?? [];
|
|
58913
|
+
const uniqueWords = new Set(words.map((w) => w.toLowerCase()));
|
|
58914
|
+
if (uniqueWords.size < CLOSURE_MIN_PROSE_WORDS) return false;
|
|
58915
|
+
return true;
|
|
58916
|
+
}
|
|
58917
|
+
function describe3(receipt, lang = "en") {
|
|
58918
|
+
const title = receipt.title.trim();
|
|
58919
|
+
const noun = lang === "uk" ? receipt.kind === "task" ? "\u0437\u0430\u0434\u0430\u0447\u0430" : "\u0441\u043F\u0435\u0446\u0438\u0444\u0456\u043A\u0430\u0446\u0456\u044F" : receipt.kind === "task" ? "task" : "page";
|
|
58920
|
+
return title ? `${noun} ${receipt.id} "${title}"` : `${noun} ${receipt.id}`;
|
|
58921
|
+
}
|
|
58922
|
+
function renderClosure(receipts, lang = "en") {
|
|
58923
|
+
const board = receipts.filter((r) => r.kind !== "inbox");
|
|
58924
|
+
const created = board.filter((r) => r.action === "created");
|
|
58925
|
+
const updated = board.filter((r) => r.action === "updated");
|
|
58926
|
+
const inboxCount = receipts.filter((r) => r.kind === "inbox").length;
|
|
58927
|
+
const parts = [];
|
|
58928
|
+
if (lang === "uk") {
|
|
58929
|
+
if (created.length) parts.push(`\u041F\u0456\u0434 \u0447\u0430\u0441 \u0446\u044C\u043E\u0433\u043E \u0445\u043E\u0434\u0443 \u0432 \u043F\u0440\u043E\u0454\u043A\u0442\u0456 \u0434\u043E\u0434\u0430\u043D\u043E: ${created.map((r) => describe3(r, "uk")).join("; ")}.`);
|
|
58930
|
+
if (updated.length) parts.push(`\u041E\u043D\u043E\u0432\u043B\u0435\u043D\u043E: ${updated.map((r) => describe3(r, "uk")).join("; ")}.`);
|
|
58931
|
+
if (inboxCount) parts.push(`\u041D\u043E\u0432\u0456 \u0435\u043B\u0435\u043C\u0435\u043D\u0442\u0438 Inbox: ${inboxCount}.`);
|
|
58932
|
+
if (created.some((r) => r.kind === "task")) parts.push("\u041D\u0430\u0441\u0442\u0443\u043F\u043D\u0438\u0439 \u043A\u0440\u043E\u043A: \u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0430 \u0434\u043E\u0448\u0446\u0456 \u0437\u0430\u0434\u0430\u0447.");
|
|
58933
|
+
return `
|
|
58934
|
+
|
|
58935
|
+
**\u041F\u0456\u0434\u0441\u0443\u043C\u043E\u043A:** ${parts.join(" ")}`;
|
|
58936
|
+
}
|
|
58937
|
+
if (created.length) parts.push(`During this turn the project gained: ${created.map((r) => describe3(r, "en")).join("; ")}.`);
|
|
58938
|
+
if (updated.length) parts.push(`Updated: ${updated.map((r) => describe3(r, "en")).join("; ")}.`);
|
|
58939
|
+
if (inboxCount) parts.push(`New Inbox items: ${inboxCount}.`);
|
|
58940
|
+
if (created.some((r) => r.kind === "task")) parts.push("Next: the task is on the task board.");
|
|
58941
|
+
return `
|
|
58942
|
+
|
|
58943
|
+
**Summary:** ${parts.join(" ")}`;
|
|
58944
|
+
}
|
|
58945
|
+
function closureFor(input) {
|
|
58946
|
+
const receipts = attributeReceipts(diffSnapshots(input.before, input.after), turnReferences(input.events));
|
|
58947
|
+
if (receipts.length === 0) return null;
|
|
58948
|
+
if (closurePresent(finalAssistantText(input.events), receipts)) return null;
|
|
58949
|
+
const isUk = Boolean(input.operatorText && /[Ѐ-ӿ]/.test(input.operatorText));
|
|
58950
|
+
return renderClosure(receipts, isUk ? "uk" : "en");
|
|
58951
|
+
}
|
|
58952
|
+
|
|
58953
|
+
// src/clarification-ledger.ts
|
|
58954
|
+
var LEDGER_MAX_ITEMS = 20;
|
|
58955
|
+
var LEDGER_MAX_QUESTIONS_PER_TURN = 5;
|
|
58956
|
+
var LEDGER_MAX_EXTRACTED_QUESTIONS = 50;
|
|
58957
|
+
var LEDGER_MAX_QUESTION_CHARS = 300;
|
|
58958
|
+
function emptyLedger() {
|
|
58959
|
+
return { turn: 0, items: [] };
|
|
58960
|
+
}
|
|
58961
|
+
var NEGATION_RE = /\b(?:no\s+default|not\s+(?:\w+\s+)*(?:answered|assumed|resolved|decided|selected|chosen)|unanswered|unresolved|undecided|remains?\s+(?:\w+\s+)*(?:open|unanswered|unresolved|undecided)|still\s+open|without\s+(?:a\s+)?default|no\s+assumption|has\s+not\s+(?:been\s+)?(?:answered|assumed|resolved|decided|selected|chosen)|is\s+not\s+(?:answered|assumed|resolved|decided|selected|chosen)|was\s+not\s+(?:answered|assumed|resolved|decided|selected|chosen)|cannot\s+be\s+(?:answered|assumed|resolved|decided)|(?:default|assumed|assuming|assumption|decided|resolved|answered|choice|decision|option|selection)\s+(?:is\s+|was\s+|are\s+|were\s+|will\s+be\s+|to\s+|:\s*|\s*=\s*)?(?:not|never|no))\b|не\s+(?:\w+\s+)*(?:вирішено|відповіли|прийнято|обрано|визначено|узгоджено|припускаю)|без\s+(?:відповіді|замовчування)|залишається\s+відкритим|невирішено|(?:дефолт|за\s+замовчуванням|вирішено|відповіли|прийнято|припускаємо|припущення|вибір|рішення|варіант)\s*(?::\s*)?(?:не|ні|ніколи)/iu;
|
|
58962
|
+
var DEFERRED_DECISION_RE = /\b(?:will\s+(?:be\s+)?(?:decided|determined|resolved|chosen|selected|answered|settled|clarified|discussed|picked)|to\s+be\s+(?:decided|determined|resolved|chosen|selected|answered|settled|clarified|discussed|picked)|pending\s+(?:decision|resolution|clarification|answer|choice)|tbd|to\s+be\s+determined|deferred|postponed|will\s+(?:decide|determine|choose|resolve|answer)|decide\s+later|determine\s+later|deciding\s+later|at\s+a\s+later\s+time|tomorrow|next\s+(?:sprint|week|turn)|depends\s+on|contingent\s+on|conditional\s+on|subject\s+to|if\s+and\s+when)\b|буде\s+(?:вирішено|визначено|обрано|узгоджено|встановлено|з'ясовано)|вирішимо\s+пізніше|визначимо\s+пізніше|відкладено|залежить\s+від|потребує\s+уточнення|ще\s+не\s+(?:вирішено|визначено|обрано)/iu;
|
|
58963
|
+
var RESOLUTION_MARKER_RE = /\b(?:answered|assumed|assuming|assumption|blocker|default|resolved|decided)\b|відпові|прийнят|приймаю|беру|за замовчуванням|блокер|припуска|вирішено|узгоджено/iu;
|
|
58964
|
+
var LEDGER_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
58965
|
+
"which",
|
|
58966
|
+
"what",
|
|
58967
|
+
"where",
|
|
58968
|
+
"when",
|
|
58969
|
+
"who",
|
|
58970
|
+
"whom",
|
|
58971
|
+
"how",
|
|
58972
|
+
"why",
|
|
58973
|
+
"should",
|
|
58974
|
+
"would",
|
|
58975
|
+
"could",
|
|
58976
|
+
"will",
|
|
58977
|
+
"can",
|
|
58978
|
+
"may",
|
|
58979
|
+
"might",
|
|
58980
|
+
"must",
|
|
58981
|
+
"need",
|
|
58982
|
+
"needed",
|
|
58983
|
+
"use",
|
|
58984
|
+
"used",
|
|
58985
|
+
"have",
|
|
58986
|
+
"been",
|
|
58987
|
+
"there",
|
|
58988
|
+
"this",
|
|
58989
|
+
"that",
|
|
58990
|
+
"question",
|
|
58991
|
+
"questions",
|
|
58992
|
+
"item",
|
|
58993
|
+
"items",
|
|
58994
|
+
"task",
|
|
58995
|
+
"tasks",
|
|
58996
|
+
"about",
|
|
58997
|
+
"\u044F\u043A\u0456",
|
|
58998
|
+
"\u044F\u043A\u0438\u0439",
|
|
58999
|
+
"\u044F\u043A\u0430",
|
|
59000
|
+
"\u044F\u043A\u0435",
|
|
59001
|
+
"\u044F\u043A\u0438\u0445",
|
|
59002
|
+
"\u044F\u043A\u043E\u043C\u0443",
|
|
59003
|
+
"\u044F\u043A\u0456\u0439",
|
|
59004
|
+
"\u0445\u0442\u043E",
|
|
59005
|
+
"\u043A\u043E\u0433\u043E",
|
|
59006
|
+
"\u043A\u043E\u043C\u0443",
|
|
59007
|
+
"\u043A\u0438\u043C",
|
|
59008
|
+
"\u0449\u043E",
|
|
59009
|
+
"\u0447\u043E\u0433\u043E",
|
|
59010
|
+
"\u0447\u043E\u043C\u0443",
|
|
59011
|
+
"\u0447\u0438\u043C",
|
|
59012
|
+
"\u0434\u0435",
|
|
59013
|
+
"\u043A\u0443\u0434\u0438",
|
|
59014
|
+
"\u0437\u0432\u0456\u0434\u043A\u0438",
|
|
59015
|
+
"\u043A\u043E\u043B\u0438",
|
|
59016
|
+
"\u0447\u043E\u043C\u0443",
|
|
59017
|
+
"\u043D\u0430\u0432\u0456\u0449\u043E",
|
|
59018
|
+
"\u044F\u043A",
|
|
59019
|
+
"\u0447\u0438",
|
|
59020
|
+
"\u0430\u0431\u043E",
|
|
59021
|
+
"\u0434\u043B\u044F",
|
|
59022
|
+
"\u043F\u0440\u043E",
|
|
59023
|
+
"\u043F\u0440\u0438",
|
|
59024
|
+
"\u043D\u0430\u0434",
|
|
59025
|
+
"\u043F\u0456\u0434",
|
|
59026
|
+
"\u0432\u0456\u0434",
|
|
59027
|
+
"\u0434\u043E",
|
|
59028
|
+
"\u043F\u0438\u0442\u0430\u043D\u043D\u044F",
|
|
59029
|
+
"\u0437\u0430\u0434\u0430\u0447\u0430",
|
|
59030
|
+
"\u0437\u0430\u0434\u0430\u0447\u0456",
|
|
59031
|
+
"\u043F\u043E\u0442\u0440\u0456\u0431\u043D\u043E",
|
|
59032
|
+
"\u043F\u043E\u0442\u0440\u0456\u0431\u043D\u0456",
|
|
59033
|
+
"\u0442\u0440\u0435\u0431\u0430"
|
|
59034
|
+
]);
|
|
59035
|
+
function tokens(text) {
|
|
59036
|
+
return new Set(
|
|
59037
|
+
text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 3)
|
|
59038
|
+
);
|
|
59039
|
+
}
|
|
59040
|
+
function tokenMatches(a, b) {
|
|
59041
|
+
if (a === b) return true;
|
|
59042
|
+
if (a.length >= 4 && b.length >= 4 && (a.startsWith(b.slice(0, 4)) || b.startsWith(a.slice(0, 4)))) {
|
|
59043
|
+
return true;
|
|
59044
|
+
}
|
|
59045
|
+
return false;
|
|
59046
|
+
}
|
|
59047
|
+
function normalizeQuestionIdentity(value) {
|
|
59048
|
+
return value.normalize("NFKC").toLowerCase().replace(/\s+/gu, " ").trim();
|
|
59049
|
+
}
|
|
59050
|
+
function isSameQuestion(a, b) {
|
|
59051
|
+
const left = normalizeQuestionIdentity(a);
|
|
59052
|
+
return /[\p{L}\p{N}]/u.test(left) && left === normalizeQuestionIdentity(b);
|
|
59053
|
+
}
|
|
59054
|
+
function isAffirmativeResolutionForQuestion(question, text, otherQuestions) {
|
|
59055
|
+
if (!text || !RESOLUTION_MARKER_RE.test(text)) return false;
|
|
59056
|
+
if (NEGATION_RE.test(text)) return false;
|
|
59057
|
+
if (DEFERRED_DECISION_RE.test(text)) return false;
|
|
59058
|
+
const substantiveQuestionToks = new Set(
|
|
59059
|
+
Array.from(tokens(question)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3)
|
|
59060
|
+
);
|
|
59061
|
+
if (otherQuestions && otherQuestions.length > 0) {
|
|
59062
|
+
const textToks2 = tokens(text);
|
|
59063
|
+
for (const otherQ of otherQuestions) {
|
|
59064
|
+
if (otherQ === question || isSameQuestion(otherQ, question)) continue;
|
|
59065
|
+
const otherToks = new Set(
|
|
59066
|
+
Array.from(tokens(otherQ)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3)
|
|
59067
|
+
);
|
|
59068
|
+
let hasSharedDomain = false;
|
|
59069
|
+
for (const t of substantiveQuestionToks) {
|
|
59070
|
+
for (const ot of otherToks) {
|
|
59071
|
+
if (tokenMatches(t, ot)) {
|
|
59072
|
+
hasSharedDomain = true;
|
|
59073
|
+
break;
|
|
59074
|
+
}
|
|
59075
|
+
}
|
|
59076
|
+
if (hasSharedDomain) break;
|
|
59077
|
+
}
|
|
59078
|
+
if (hasSharedDomain) {
|
|
59079
|
+
const otherDistinguishingToks = [];
|
|
59080
|
+
for (const ot of otherToks) {
|
|
59081
|
+
let inThis = false;
|
|
59082
|
+
for (const t of substantiveQuestionToks) {
|
|
59083
|
+
if (tokenMatches(t, ot)) {
|
|
59084
|
+
inThis = true;
|
|
59085
|
+
break;
|
|
59086
|
+
}
|
|
59087
|
+
}
|
|
59088
|
+
if (!inThis) otherDistinguishingToks.push(ot);
|
|
59089
|
+
}
|
|
59090
|
+
const thisDistinguishingToks = [];
|
|
59091
|
+
for (const t of substantiveQuestionToks) {
|
|
59092
|
+
let inOther = false;
|
|
59093
|
+
for (const ot of otherToks) {
|
|
59094
|
+
if (tokenMatches(t, ot)) {
|
|
59095
|
+
inOther = true;
|
|
59096
|
+
break;
|
|
59097
|
+
}
|
|
59098
|
+
}
|
|
59099
|
+
if (!inOther) thisDistinguishingToks.push(t);
|
|
59100
|
+
}
|
|
59101
|
+
if (otherDistinguishingToks.length > 0 && thisDistinguishingToks.length > 0) {
|
|
59102
|
+
let textMatchesOtherDistinguishing = false;
|
|
59103
|
+
for (const ot of otherDistinguishingToks) {
|
|
59104
|
+
for (const lt of textToks2) {
|
|
59105
|
+
if (tokenMatches(ot, lt)) {
|
|
59106
|
+
textMatchesOtherDistinguishing = true;
|
|
59107
|
+
break;
|
|
59108
|
+
}
|
|
59109
|
+
}
|
|
59110
|
+
if (textMatchesOtherDistinguishing) break;
|
|
59111
|
+
}
|
|
59112
|
+
let textMatchesThisDistinguishing = false;
|
|
59113
|
+
for (const t of thisDistinguishingToks) {
|
|
59114
|
+
for (const lt of textToks2) {
|
|
59115
|
+
if (tokenMatches(t, lt)) {
|
|
59116
|
+
textMatchesThisDistinguishing = true;
|
|
59117
|
+
break;
|
|
59118
|
+
}
|
|
59119
|
+
}
|
|
59120
|
+
if (textMatchesThisDistinguishing) break;
|
|
59121
|
+
}
|
|
59122
|
+
if (textMatchesOtherDistinguishing && !textMatchesThisDistinguishing) {
|
|
59123
|
+
return false;
|
|
59124
|
+
}
|
|
59125
|
+
}
|
|
59126
|
+
}
|
|
59127
|
+
}
|
|
59128
|
+
}
|
|
59129
|
+
if (!isTopicConsistentWithQuestion(question, text)) {
|
|
59130
|
+
return false;
|
|
59131
|
+
}
|
|
59132
|
+
const textToks = tokens(text);
|
|
59133
|
+
let shared = 0;
|
|
59134
|
+
for (const it of substantiveQuestionToks) {
|
|
59135
|
+
for (const lt of textToks) {
|
|
59136
|
+
if (tokenMatches(it, lt)) {
|
|
59137
|
+
shared += 1;
|
|
59138
|
+
break;
|
|
59139
|
+
}
|
|
59140
|
+
}
|
|
59141
|
+
}
|
|
59142
|
+
return shared >= 1 && (shared >= 2 || substantiveQuestionToks.size <= 2 || shared / substantiveQuestionToks.size >= 0.25);
|
|
59143
|
+
}
|
|
59144
|
+
function isTopicConsistentWithQuestion(question, text) {
|
|
59145
|
+
const substantiveQuestionToks = Array.from(tokens(question)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3);
|
|
59146
|
+
const genericQualifiers = /* @__PURE__ */ new Set([
|
|
59147
|
+
"the",
|
|
59148
|
+
"a",
|
|
59149
|
+
"an",
|
|
59150
|
+
"this",
|
|
59151
|
+
"that",
|
|
59152
|
+
"our",
|
|
59153
|
+
"its",
|
|
59154
|
+
"their",
|
|
59155
|
+
"your",
|
|
59156
|
+
"\u0446\u0435\u0439",
|
|
59157
|
+
"\u0446\u044F",
|
|
59158
|
+
"\u0446\u0435",
|
|
59159
|
+
"\u043D\u0430\u0448",
|
|
59160
|
+
"\u043D\u0430\u0448\u0430",
|
|
59161
|
+
"\u0439\u043E\u0433\u043E",
|
|
59162
|
+
"\u0457\u0457",
|
|
59163
|
+
"is",
|
|
59164
|
+
"as",
|
|
59165
|
+
"by",
|
|
59166
|
+
"take",
|
|
59167
|
+
"taking",
|
|
59168
|
+
"taken",
|
|
59169
|
+
"set",
|
|
59170
|
+
"use",
|
|
59171
|
+
"default",
|
|
59172
|
+
"assumed",
|
|
59173
|
+
"assuming",
|
|
59174
|
+
"decision",
|
|
59175
|
+
"outcome",
|
|
59176
|
+
"choice",
|
|
59177
|
+
"\u0434\u0435\u0444\u043E\u043B\u0442",
|
|
59178
|
+
"\u0437\u0430\u043C\u043E\u0432\u0447\u0443\u0432\u0430\u043D\u043D\u044F",
|
|
59179
|
+
"\u0432\u0438\u0431\u0456\u0440",
|
|
59180
|
+
"\u0431\u0435\u0440\u0443",
|
|
59181
|
+
"\u0432\u0432\u0430\u0436\u0430\u044E",
|
|
59182
|
+
"\u043F\u0440\u0438\u0439\u043D\u044F\u0442\u043E",
|
|
59183
|
+
"\u043E\u0431\u0440\u0430\u043D\u043E"
|
|
59184
|
+
]);
|
|
59185
|
+
const beforeTopicMatch = text.match(/([a-zA-Z\u0400-\u04FF_-]+)\s+(?:default|дефолт|за\s+замовчуванням|is|are|was|were|will\s+be|є|це|буде|будуть|обрано|було|=|:|—|-)\b/i);
|
|
59186
|
+
if (beforeTopicMatch && beforeTopicMatch[1]) {
|
|
59187
|
+
const qualifier = beforeTopicMatch[1].toLowerCase();
|
|
59188
|
+
if (!genericQualifiers.has(qualifier)) {
|
|
59189
|
+
let qualifierMatches = false;
|
|
59190
|
+
for (const it of substantiveQuestionToks) {
|
|
59191
|
+
if (tokenMatches(it, qualifier)) {
|
|
59192
|
+
qualifierMatches = true;
|
|
59193
|
+
break;
|
|
59194
|
+
}
|
|
59195
|
+
}
|
|
59196
|
+
if (!qualifierMatches) {
|
|
59197
|
+
return false;
|
|
59198
|
+
}
|
|
59199
|
+
}
|
|
59200
|
+
}
|
|
59201
|
+
const resolutionKeywords = /* @__PURE__ */ new Set([
|
|
59202
|
+
"answered",
|
|
59203
|
+
"assumed",
|
|
59204
|
+
"assuming",
|
|
59205
|
+
"default",
|
|
59206
|
+
"blocker",
|
|
59207
|
+
"decision",
|
|
59208
|
+
"outcome",
|
|
59209
|
+
"choice",
|
|
59210
|
+
"\u0432\u0456\u0434\u043F\u043E\u0432\u0456",
|
|
59211
|
+
"\u043F\u0440\u0438\u0439\u043D\u044F\u0442",
|
|
59212
|
+
"\u043F\u0440\u0438\u0439\u043C\u0430\u044E",
|
|
59213
|
+
"\u0431\u0435\u0440\u0443",
|
|
59214
|
+
"\u0437\u0430\u043C\u043E\u0432\u0447\u0443\u0432\u0430\u043D\u043D\u044F\u043C",
|
|
59215
|
+
"\u0431\u043B\u043E\u043A\u0435\u0440",
|
|
59216
|
+
"\u043F\u0440\u0438\u043F\u0443\u0441\u043A\u0430",
|
|
59217
|
+
"\u0432\u0438\u0440\u0456\u0448\u0435\u043D\u043E",
|
|
59218
|
+
"\u0443\u0437\u0433\u043E\u0434\u0436\u0435\u043D\u043E"
|
|
59219
|
+
]);
|
|
59220
|
+
const textToks = Array.from(tokens(text)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3);
|
|
59221
|
+
const contentToks = textToks.filter((t) => !resolutionKeywords.has(t) && !genericQualifiers.has(t));
|
|
59222
|
+
for (const ct of contentToks) {
|
|
59223
|
+
const topicMatch = new RegExp(`\\b${ct}\\s+(?:default|\u0434\u0435\u0444\u043E\u043B\u0442|is|are|was|were|will\\s+be|\u0454|\u0446\u0435|\u0431\u0443\u0434\u0435|\u0431\u0443\u0434\u0443\u0442\u044C|=|:|\u2014|-)\\b`, "i").test(text);
|
|
59224
|
+
if (topicMatch) {
|
|
59225
|
+
let matchesQ = false;
|
|
59226
|
+
for (const qt of substantiveQuestionToks) {
|
|
59227
|
+
if (tokenMatches(qt, ct)) {
|
|
59228
|
+
matchesQ = true;
|
|
59229
|
+
break;
|
|
59230
|
+
}
|
|
59231
|
+
}
|
|
59232
|
+
if (!matchesQ) {
|
|
59233
|
+
return false;
|
|
59234
|
+
}
|
|
59235
|
+
}
|
|
59236
|
+
}
|
|
59237
|
+
return true;
|
|
59238
|
+
}
|
|
59239
|
+
function isDirectInlineResolutionForQuestion(question, afterText) {
|
|
59240
|
+
if (!afterText) return false;
|
|
59241
|
+
const isDirect = /^\s*(?:[-—–:]|\()\s*(?:answered|assumed|assuming|default|blocker|відпові|прийнят|приймаю|беру|за\s+замовчуванням|блокер|припуска|вирішено|узгоджено)/i.test(afterText);
|
|
59242
|
+
if (!isDirect) return false;
|
|
59243
|
+
if (NEGATION_RE.test(afterText)) return false;
|
|
59244
|
+
if (DEFERRED_DECISION_RE.test(afterText)) return false;
|
|
59245
|
+
return isTopicConsistentWithQuestion(question, afterText);
|
|
59246
|
+
}
|
|
59247
|
+
function extractQuestions(text) {
|
|
59248
|
+
const withoutCode = text.replace(/```[\s\S]*?```|~~~[\s\S]*?~~~/g, " ").replace(/`[^`\n]*`/g, " ");
|
|
59249
|
+
const found = [];
|
|
59250
|
+
for (const rawLine of withoutCode.split(/\n+/)) {
|
|
59251
|
+
const line = rawLine.replace(/^\s*(?:[-*•]|\d+[.)])\s+/, "").replace(/(\*\*|__|[*_~"«»“”])/g, "").trim();
|
|
59252
|
+
if (!line) continue;
|
|
59253
|
+
for (const match of line.matchAll(/[^.!?]*\?(?=\s|$)/g)) {
|
|
59254
|
+
const sentence = match[0].trim();
|
|
59255
|
+
const after = line.slice((match.index ?? 0) + match[0].length).split("?")[0] ?? "";
|
|
59256
|
+
const hasInlineMarker = /^\s*(?:[-—–:]|\()\s*(?:answered|assumed|assuming|default|blocker|відпові|прийнят|приймаю|беру|за\s+замовчуванням|блокер|припуска|вирішено|узгоджено)/i.test(after);
|
|
59257
|
+
if (hasInlineMarker) {
|
|
59258
|
+
if (isDirectInlineResolutionForQuestion(sentence, after)) continue;
|
|
59259
|
+
} else {
|
|
59260
|
+
if (isAffirmativeResolutionForQuestion(sentence, after)) continue;
|
|
59261
|
+
}
|
|
59262
|
+
if (/https?:\/\/\S*$/.test(sentence)) continue;
|
|
59263
|
+
if (sentence.replace(/[^\p{L}]/gu, "").length < 6) continue;
|
|
59264
|
+
const question = sentence.slice(0, LEDGER_MAX_QUESTION_CHARS);
|
|
59265
|
+
if (found.some((seen) => isSameQuestion(seen, question))) continue;
|
|
59266
|
+
found.push(question);
|
|
59267
|
+
if (found.length >= LEDGER_MAX_EXTRACTED_QUESTIONS) return found;
|
|
59268
|
+
}
|
|
59269
|
+
}
|
|
59270
|
+
return found;
|
|
59271
|
+
}
|
|
59272
|
+
function presentLedger(ledger) {
|
|
59273
|
+
const turn = ledger.turn + 1;
|
|
59274
|
+
const candidates = ledger.items.map((item, index) => ({ item, index })).filter(({ item }) => item.status === "open" || item.status === "reasked" || item.status === "addressed");
|
|
59275
|
+
const shownIndexes = new Set(candidates.slice(-LEDGER_MAX_ITEMS).map(({ index }) => index));
|
|
59276
|
+
const items = ledger.items.map((item, index) => shownIndexes.has(index) ? { ...item, presentedTurn: turn } : item);
|
|
59277
|
+
const shown = items.filter((_, index) => shownIndexes.has(index)).map(({ question, status, askedTurn }) => ({ question, status, askedTurn }));
|
|
59278
|
+
return { ledger: { turn, items }, shown };
|
|
59279
|
+
}
|
|
59280
|
+
function isQuestionExplicitlyResolved(item, text, otherOpenQuestions) {
|
|
59281
|
+
if (!text || !RESOLUTION_MARKER_RE.test(text)) return false;
|
|
59282
|
+
const withoutCode = text.replace(/```[\s\S]*?```|~~~[\s\S]*?~~~/g, " ").replace(/`[^`\n]*`/g, " ");
|
|
59283
|
+
for (const rawLine of withoutCode.split(/\n+/)) {
|
|
59284
|
+
const line = rawLine.trim();
|
|
59285
|
+
if (!line) continue;
|
|
59286
|
+
if (line.includes(item.question)) {
|
|
59287
|
+
const qIndex = line.indexOf(item.question);
|
|
59288
|
+
const after = line.slice(qIndex + item.question.length).trim();
|
|
59289
|
+
const isDirectAttachment = /^[:—–\->]\s*|[^\w\s]\s*(?:assumed|default|дефолт|outcome|decision|обрано|прийнято)\b/i.test(after);
|
|
59290
|
+
if (isDirectAttachment && isDirectInlineResolutionForQuestion(item.question, after)) {
|
|
59291
|
+
const hasOtherTokens = otherOpenQuestions?.some((oq) => {
|
|
59292
|
+
if (oq === item.question || isSameQuestion(oq, item.question)) return false;
|
|
59293
|
+
const otherToks = Array.from(tokens(oq)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3);
|
|
59294
|
+
const thisToks = Array.from(tokens(item.question)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3);
|
|
59295
|
+
const otherUnique = otherToks.filter((ot) => !thisToks.some((tt) => tokenMatches(ot, tt)));
|
|
59296
|
+
const afterToks = tokens(after);
|
|
59297
|
+
return otherUnique.length > 0 && otherUnique.some((ot) => Array.from(afterToks).some((at) => tokenMatches(ot, at)));
|
|
59298
|
+
});
|
|
59299
|
+
if (!hasOtherTokens) {
|
|
59300
|
+
return true;
|
|
59301
|
+
}
|
|
59302
|
+
}
|
|
59303
|
+
}
|
|
59304
|
+
}
|
|
59305
|
+
const segments = withoutCode.split(/\n+|(?<=[.!?])\s+/).map((l) => l.trim()).filter(Boolean);
|
|
59306
|
+
for (const segment of segments) {
|
|
59307
|
+
if (!RESOLUTION_MARKER_RE.test(segment)) continue;
|
|
59308
|
+
if (NEGATION_RE.test(segment)) continue;
|
|
59309
|
+
if (DEFERRED_DECISION_RE.test(segment)) continue;
|
|
59310
|
+
if (segment.includes(item.question) || isSameQuestion(segment, item.question)) {
|
|
59311
|
+
const hasOtherTokens = otherOpenQuestions?.some((oq) => {
|
|
59312
|
+
if (oq === item.question || isSameQuestion(oq, item.question)) return false;
|
|
59313
|
+
const otherToks = Array.from(tokens(oq)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3);
|
|
59314
|
+
const thisToks = Array.from(tokens(item.question)).filter((t) => !LEDGER_STOP_WORDS.has(t) && t.length >= 3);
|
|
59315
|
+
const otherUnique = otherToks.filter((ot) => !thisToks.some((tt) => tokenMatches(ot, tt)));
|
|
59316
|
+
const segToks = tokens(segment);
|
|
59317
|
+
return otherUnique.length > 0 && otherUnique.some((ot) => Array.from(segToks).some((st) => tokenMatches(ot, st)));
|
|
59318
|
+
});
|
|
59319
|
+
if (!hasOtherTokens) {
|
|
59320
|
+
return true;
|
|
59321
|
+
}
|
|
59322
|
+
}
|
|
59323
|
+
if (isAffirmativeResolutionForQuestion(item.question, segment, otherOpenQuestions)) {
|
|
59324
|
+
return true;
|
|
59325
|
+
}
|
|
59326
|
+
}
|
|
59327
|
+
return false;
|
|
59328
|
+
}
|
|
59329
|
+
function recordTurn(ledger, finalText) {
|
|
59330
|
+
const turn = ledger.turn;
|
|
59331
|
+
const newQuestions = extractQuestions(finalText);
|
|
59332
|
+
const unmatched = [...newQuestions];
|
|
59333
|
+
const reasked = [];
|
|
59334
|
+
const openQuestions = ledger.items.filter((it) => it.status === "open" || it.status === "reasked").map((it) => it.question);
|
|
59335
|
+
const items = ledger.items.map((item) => {
|
|
59336
|
+
if (item.status === "archived") return item;
|
|
59337
|
+
const matchIndex = unmatched.findIndex((question) => isSameQuestion(question, item.question));
|
|
59338
|
+
if (matchIndex >= 0) {
|
|
59339
|
+
unmatched.splice(matchIndex, 1);
|
|
59340
|
+
if (item.askedTurn === turn) return item;
|
|
59341
|
+
const again = { ...item, status: "reasked", reasked: item.reasked + 1 };
|
|
59342
|
+
delete again.addressedTurn;
|
|
59343
|
+
reasked.push(again);
|
|
59344
|
+
return again;
|
|
59345
|
+
}
|
|
59346
|
+
if (item.status === "addressed") {
|
|
59347
|
+
return item.addressedTurn !== void 0 && item.addressedTurn < turn ? { ...item, status: "archived" } : item;
|
|
59348
|
+
}
|
|
59349
|
+
const otherQuestions = openQuestions.filter((q) => q !== item.question);
|
|
59350
|
+
if (isQuestionExplicitlyResolved(item, finalText, otherQuestions)) {
|
|
59351
|
+
return { ...item, status: "addressed", addressedTurn: turn };
|
|
59352
|
+
}
|
|
59353
|
+
return item;
|
|
59354
|
+
});
|
|
59355
|
+
const overflow = [];
|
|
59356
|
+
let unresolved = items.filter((item) => item.status === "open" || item.status === "reasked").length;
|
|
59357
|
+
let recordedNew = 0;
|
|
59358
|
+
for (const question of unmatched) {
|
|
59359
|
+
if (recordedNew >= LEDGER_MAX_QUESTIONS_PER_TURN) {
|
|
59360
|
+
overflow.push(question);
|
|
59361
|
+
continue;
|
|
59362
|
+
}
|
|
59363
|
+
if (unresolved >= LEDGER_MAX_ITEMS) {
|
|
59364
|
+
overflow.push(question);
|
|
59365
|
+
continue;
|
|
59366
|
+
}
|
|
59367
|
+
items.push({ question, askedTurn: turn, status: "open", reasked: 0 });
|
|
59368
|
+
unresolved += 1;
|
|
59369
|
+
recordedNew += 1;
|
|
59370
|
+
}
|
|
59371
|
+
return { ...capLedger({ turn, items }), reasked, overflow };
|
|
59372
|
+
}
|
|
59373
|
+
var DROP_ORDER = ["archived", "addressed"];
|
|
59374
|
+
function capLedger(ledger) {
|
|
59375
|
+
const items = [...ledger.items];
|
|
59376
|
+
const dropped = [];
|
|
59377
|
+
for (const status of DROP_ORDER) {
|
|
59378
|
+
while (items.length > LEDGER_MAX_ITEMS) {
|
|
59379
|
+
const index = items.findIndex((item) => item.status === status);
|
|
59380
|
+
if (index < 0) break;
|
|
59381
|
+
dropped.push(...items.splice(index, 1));
|
|
59382
|
+
}
|
|
59383
|
+
}
|
|
59384
|
+
return { ledger: { turn: ledger.turn, items }, dropped };
|
|
59385
|
+
}
|
|
59386
|
+
function parseLedger(value) {
|
|
59387
|
+
if (!value || typeof value !== "object") return void 0;
|
|
59388
|
+
const raw = value;
|
|
59389
|
+
if (typeof raw.turn !== "number" || !Array.isArray(raw.items)) return void 0;
|
|
59390
|
+
if (raw.items.length > LEDGER_MAX_ITEMS) return void 0;
|
|
59391
|
+
const statuses = /* @__PURE__ */ new Set(["open", "reasked", "addressed", "archived"]);
|
|
59392
|
+
const items = [];
|
|
59393
|
+
for (const entry of raw.items) {
|
|
59394
|
+
if (!entry || typeof entry !== "object") continue;
|
|
59395
|
+
const item = entry;
|
|
59396
|
+
if (typeof item.question !== "string" || typeof item.askedTurn !== "number") continue;
|
|
59397
|
+
if (typeof item.status !== "string" || !statuses.has(item.status)) continue;
|
|
59398
|
+
items.push({
|
|
59399
|
+
question: item.question.slice(0, LEDGER_MAX_QUESTION_CHARS),
|
|
59400
|
+
askedTurn: item.askedTurn,
|
|
59401
|
+
status: item.status,
|
|
59402
|
+
reasked: typeof item.reasked === "number" ? item.reasked : 0,
|
|
59403
|
+
...typeof item.presentedTurn === "number" ? { presentedTurn: item.presentedTurn } : {},
|
|
59404
|
+
...typeof item.addressedTurn === "number" ? { addressedTurn: item.addressedTurn } : {}
|
|
59405
|
+
});
|
|
59406
|
+
}
|
|
59407
|
+
return { turn: raw.turn, items };
|
|
59408
|
+
}
|
|
58081
59409
|
|
|
58082
59410
|
// src/api-base-url-migrate.ts
|
|
58083
59411
|
var import_node_fs13 = __toESM(require("node:fs"), 1);
|
|
@@ -58556,7 +59884,7 @@ async function writeCrashSentinel(stateDir, sentinel) {
|
|
|
58556
59884
|
}
|
|
58557
59885
|
function withTimeout(fn, ms = DEFAULT_OPERATION_TIMEOUT_MS) {
|
|
58558
59886
|
const controller = new AbortController();
|
|
58559
|
-
return new Promise((
|
|
59887
|
+
return new Promise((resolve2, reject) => {
|
|
58560
59888
|
const timer = setTimeout(() => {
|
|
58561
59889
|
controller.abort();
|
|
58562
59890
|
const timeoutError = new Error(`operation timed out after ${ms}ms`);
|
|
@@ -58566,7 +59894,7 @@ function withTimeout(fn, ms = DEFAULT_OPERATION_TIMEOUT_MS) {
|
|
|
58566
59894
|
fn(controller.signal).then(
|
|
58567
59895
|
(value) => {
|
|
58568
59896
|
clearTimeout(timer);
|
|
58569
|
-
|
|
59897
|
+
resolve2(value);
|
|
58570
59898
|
},
|
|
58571
59899
|
(error51) => {
|
|
58572
59900
|
clearTimeout(timer);
|
|
@@ -59512,7 +60840,7 @@ function runExternalAgentsCli(args, opts) {
|
|
|
59512
60840
|
}
|
|
59513
60841
|
const useStdin = opts.stdinValue !== void 0;
|
|
59514
60842
|
const stdioConfig = useStdin ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
59515
|
-
return new Promise((
|
|
60843
|
+
return new Promise((resolve2) => {
|
|
59516
60844
|
let stdout = "";
|
|
59517
60845
|
let stderr = "";
|
|
59518
60846
|
let settled = false;
|
|
@@ -59520,7 +60848,7 @@ function runExternalAgentsCli(args, opts) {
|
|
|
59520
60848
|
const done = (code) => {
|
|
59521
60849
|
if (!settled) {
|
|
59522
60850
|
settled = true;
|
|
59523
|
-
|
|
60851
|
+
resolve2({ stdout, stderr, code, timedOut, source, triedPath });
|
|
59524
60852
|
}
|
|
59525
60853
|
};
|
|
59526
60854
|
let spawned;
|
|
@@ -59688,7 +61016,7 @@ function startProviderReadinessLoop(deps) {
|
|
|
59688
61016
|
|
|
59689
61017
|
// src/execution-consumer.ts
|
|
59690
61018
|
var import_node_child_process19 = require("node:child_process");
|
|
59691
|
-
var
|
|
61019
|
+
var import_node_crypto14 = __toESM(require("node:crypto"), 1);
|
|
59692
61020
|
var import_node_path31 = __toESM(require("node:path"), 1);
|
|
59693
61021
|
var import_node_util2 = require("node:util");
|
|
59694
61022
|
|
|
@@ -60544,6 +61872,7 @@ async function awaitTurnWithDeadline(opts) {
|
|
|
60544
61872
|
|
|
60545
61873
|
// src/worktree.ts
|
|
60546
61874
|
var import_node_child_process18 = require("node:child_process");
|
|
61875
|
+
var import_node_crypto13 = require("node:crypto");
|
|
60547
61876
|
var import_node_fs17 = require("node:fs");
|
|
60548
61877
|
var import_node_path30 = require("node:path");
|
|
60549
61878
|
|
|
@@ -60571,8 +61900,43 @@ function worktreeBranch(runId) {
|
|
|
60571
61900
|
return `mrrlin/run-${runId}`;
|
|
60572
61901
|
}
|
|
60573
61902
|
var defaultGitExec = (args, env) => (0, import_node_child_process18.execFileSync)("git", args, { encoding: "utf8", timeout: 3e4, env });
|
|
60574
|
-
function
|
|
60575
|
-
|
|
61903
|
+
function disabledGitHooksPath(locksDir, scope) {
|
|
61904
|
+
const scopeKey = (0, import_node_crypto13.createHash)("sha256").update(scope).digest("hex").slice(0, 32);
|
|
61905
|
+
return (0, import_node_path30.resolve)(locksDir, "..", "disabled-git-hooks", scopeKey);
|
|
61906
|
+
}
|
|
61907
|
+
function prepareDisabledGitHooksDir(locksDir, scope) {
|
|
61908
|
+
const hooksDir = disabledGitHooksPath(locksDir, scope);
|
|
61909
|
+
const hooksRoot = (0, import_node_path30.resolve)(hooksDir, "..");
|
|
61910
|
+
(0, import_node_fs17.mkdirSync)(hooksRoot, { recursive: true, mode: 448 });
|
|
61911
|
+
(0, import_node_fs17.mkdirSync)(hooksDir, { recursive: true, mode: 320 });
|
|
61912
|
+
if ((0, import_node_fs17.readdirSync)(hooksDir).length > 0) {
|
|
61913
|
+
throw new Error(`disabled_git_hooks_dir_not_empty: ${hooksDir}`);
|
|
61914
|
+
}
|
|
61915
|
+
try {
|
|
61916
|
+
(0, import_node_fs17.chmodSync)(hooksDir, 320);
|
|
61917
|
+
} catch {
|
|
61918
|
+
}
|
|
61919
|
+
return hooksDir;
|
|
61920
|
+
}
|
|
61921
|
+
function removeDisabledGitHooksDir(locksDir, scope) {
|
|
61922
|
+
try {
|
|
61923
|
+
(0, import_node_fs17.rmdirSync)(disabledGitHooksPath(locksDir, scope));
|
|
61924
|
+
} catch {
|
|
61925
|
+
}
|
|
61926
|
+
}
|
|
61927
|
+
function withDisabledGitHooksEnv(env, hooksDir) {
|
|
61928
|
+
const rawCount = env.GIT_CONFIG_COUNT;
|
|
61929
|
+
const parsedCount = rawCount === void 0 ? 0 : Number(rawCount);
|
|
61930
|
+
const count2 = Number.isSafeInteger(parsedCount) && parsedCount >= 0 ? parsedCount : 0;
|
|
61931
|
+
return {
|
|
61932
|
+
...env,
|
|
61933
|
+
GIT_CONFIG_COUNT: String(count2 + 1),
|
|
61934
|
+
[`GIT_CONFIG_KEY_${count2}`]: "core.hooksPath",
|
|
61935
|
+
[`GIT_CONFIG_VALUE_${count2}`]: hooksDir
|
|
61936
|
+
};
|
|
61937
|
+
}
|
|
61938
|
+
function git(checkout, args, hooksDir) {
|
|
61939
|
+
return (0, import_node_child_process18.execFileSync)("git", ["-C", checkout, "-c", `core.hooksPath=${hooksDir}`, ...args], {
|
|
60576
61940
|
encoding: "utf8",
|
|
60577
61941
|
timeout: 3e4,
|
|
60578
61942
|
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
@@ -60580,20 +61944,21 @@ function git(checkout, args) {
|
|
|
60580
61944
|
}
|
|
60581
61945
|
async function createRunWorktree(opts) {
|
|
60582
61946
|
const worktreePath = (0, import_node_path30.join)(opts.worktreeRoot, opts.slug, opts.runId);
|
|
61947
|
+
const hooksDir = prepareDisabledGitHooksDir(opts.locksDir, opts.runId);
|
|
60583
61948
|
await withCheckoutLock(opts.locksDir, opts.slug, async () => {
|
|
60584
61949
|
(0, import_node_fs17.mkdirSync)(opts.worktreeRoot, { recursive: true });
|
|
60585
61950
|
try {
|
|
60586
|
-
git(opts.checkout, ["fetch", "origin", opts.defaultBranch]);
|
|
61951
|
+
git(opts.checkout, ["fetch", "origin", opts.defaultBranch], hooksDir);
|
|
60587
61952
|
if (!(0, import_node_fs17.existsSync)(worktreePath)) {
|
|
60588
61953
|
git(opts.checkout, [
|
|
60589
61954
|
"worktree",
|
|
60590
61955
|
"add",
|
|
61956
|
+
"--no-track",
|
|
60591
61957
|
"-b",
|
|
60592
61958
|
worktreeBranch(opts.runId),
|
|
60593
61959
|
worktreePath,
|
|
60594
61960
|
`origin/${opts.defaultBranch}`
|
|
60595
|
-
]);
|
|
60596
|
-
git(worktreePath, ["config", "core.hooksPath", "/dev/null"]);
|
|
61961
|
+
], hooksDir);
|
|
60597
61962
|
}
|
|
60598
61963
|
} catch (error51) {
|
|
60599
61964
|
const detail = error51 instanceof Error ? error51.message : String(error51);
|
|
@@ -60605,12 +61970,14 @@ async function createRunWorktree(opts) {
|
|
|
60605
61970
|
return worktreePath;
|
|
60606
61971
|
}
|
|
60607
61972
|
async function removeRunWorktree(opts) {
|
|
61973
|
+
const hooksDir = prepareDisabledGitHooksDir(opts.locksDir, (0, import_node_path30.basename)(opts.worktreePath));
|
|
60608
61974
|
await withCheckoutLock(opts.locksDir, opts.slug, async () => {
|
|
60609
61975
|
try {
|
|
60610
|
-
git(opts.checkout, ["worktree", "remove", "--force", opts.worktreePath]);
|
|
61976
|
+
git(opts.checkout, ["worktree", "remove", "--force", opts.worktreePath], hooksDir);
|
|
60611
61977
|
} catch {
|
|
60612
61978
|
}
|
|
60613
|
-
git(opts.checkout, ["worktree", "prune"]);
|
|
61979
|
+
git(opts.checkout, ["worktree", "prune"], hooksDir);
|
|
61980
|
+
removeDisabledGitHooksDir(opts.locksDir, (0, import_node_path30.basename)(opts.worktreePath));
|
|
60614
61981
|
});
|
|
60615
61982
|
}
|
|
60616
61983
|
async function pushRunBranch(opts, execGit = defaultGitExec) {
|
|
@@ -60633,6 +62000,7 @@ async function hasCommitsToPush(opts, execGit = defaultGitExec) {
|
|
|
60633
62000
|
}
|
|
60634
62001
|
async function doPushRunBranch(opts, execGit) {
|
|
60635
62002
|
const branch = worktreeBranch(opts.runId);
|
|
62003
|
+
const hooksDir = prepareDisabledGitHooksDir(opts.locksDir, opts.runId);
|
|
60636
62004
|
const pushEnv = {
|
|
60637
62005
|
PATH: process.env.PATH ?? "",
|
|
60638
62006
|
...process.env.HOME !== void 0 ? { HOME: process.env.HOME } : {},
|
|
@@ -60678,7 +62046,7 @@ async function doPushRunBranch(opts, execGit) {
|
|
|
60678
62046
|
"-C",
|
|
60679
62047
|
opts.worktreePath,
|
|
60680
62048
|
"-c",
|
|
60681
|
-
|
|
62049
|
+
`core.hooksPath=${hooksDir}`,
|
|
60682
62050
|
"push",
|
|
60683
62051
|
"--no-verify",
|
|
60684
62052
|
"origin",
|
|
@@ -60690,6 +62058,7 @@ async function doPushRunBranch(opts, execGit) {
|
|
|
60690
62058
|
}
|
|
60691
62059
|
async function sweepOrphanWorktrees(opts) {
|
|
60692
62060
|
const reaped = [];
|
|
62061
|
+
const hooksDir = prepareDisabledGitHooksDir(opts.locksDir, `sweep:${opts.slug}`);
|
|
60693
62062
|
const ownedPrefix = (() => {
|
|
60694
62063
|
try {
|
|
60695
62064
|
return (import_node_fs17.realpathSync.native ?? import_node_fs17.realpathSync)(opts.worktreeRoot);
|
|
@@ -60697,32 +62066,37 @@ async function sweepOrphanWorktrees(opts) {
|
|
|
60697
62066
|
return opts.worktreeRoot;
|
|
60698
62067
|
}
|
|
60699
62068
|
})() + import_node_path30.sep + opts.slug + import_node_path30.sep;
|
|
60700
|
-
|
|
60701
|
-
|
|
60702
|
-
|
|
60703
|
-
const
|
|
60704
|
-
|
|
60705
|
-
|
|
60706
|
-
|
|
60707
|
-
|
|
60708
|
-
|
|
60709
|
-
|
|
60710
|
-
|
|
60711
|
-
|
|
60712
|
-
|
|
60713
|
-
|
|
60714
|
-
|
|
60715
|
-
|
|
60716
|
-
|
|
60717
|
-
|
|
60718
|
-
|
|
60719
|
-
|
|
60720
|
-
|
|
62069
|
+
try {
|
|
62070
|
+
await withCheckoutLock(opts.locksDir, opts.slug, async () => {
|
|
62071
|
+
const listing = git(opts.checkout, ["worktree", "list", "--porcelain"], hooksDir);
|
|
62072
|
+
for (const block of listing.split("\n\n")) {
|
|
62073
|
+
const m = /^worktree (.+)$/m.exec(block);
|
|
62074
|
+
const wt = m?.[1]?.trim();
|
|
62075
|
+
if (!wt) continue;
|
|
62076
|
+
const canonicalWt = (() => {
|
|
62077
|
+
try {
|
|
62078
|
+
return (import_node_fs17.realpathSync.native ?? import_node_fs17.realpathSync)(wt);
|
|
62079
|
+
} catch {
|
|
62080
|
+
return wt;
|
|
62081
|
+
}
|
|
62082
|
+
})();
|
|
62083
|
+
const normWt = (0, import_node_path30.normalize)(canonicalWt);
|
|
62084
|
+
const normPrefix = (0, import_node_path30.normalize)(ownedPrefix);
|
|
62085
|
+
const isMatch = process.platform === "win32" ? normWt.toLowerCase().startsWith(normPrefix.toLowerCase()) : normWt.startsWith(normPrefix);
|
|
62086
|
+
if (isMatch && opts.isReapable(wt)) {
|
|
62087
|
+
try {
|
|
62088
|
+
git(opts.checkout, ["worktree", "remove", "--force", wt], hooksDir);
|
|
62089
|
+
removeDisabledGitHooksDir(opts.locksDir, (0, import_node_path30.basename)(wt));
|
|
62090
|
+
reaped.push(wt);
|
|
62091
|
+
} catch {
|
|
62092
|
+
}
|
|
60721
62093
|
}
|
|
60722
62094
|
}
|
|
60723
|
-
|
|
60724
|
-
|
|
60725
|
-
}
|
|
62095
|
+
git(opts.checkout, ["worktree", "prune"], hooksDir);
|
|
62096
|
+
});
|
|
62097
|
+
} finally {
|
|
62098
|
+
removeDisabledGitHooksDir(opts.locksDir, `sweep:${opts.slug}`);
|
|
62099
|
+
}
|
|
60726
62100
|
return reaped;
|
|
60727
62101
|
}
|
|
60728
62102
|
|
|
@@ -60916,7 +62290,7 @@ function readPositiveIntEnv(name, fallback) {
|
|
|
60916
62290
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
60917
62291
|
}
|
|
60918
62292
|
function delayMs(ms) {
|
|
60919
|
-
return new Promise((
|
|
62293
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
60920
62294
|
}
|
|
60921
62295
|
function executionBundleBudget() {
|
|
60922
62296
|
if ((process.env.MRRLIN_RETRIEVAL_BUDGET_DISABLE ?? "").trim() === "1") return void 0;
|
|
@@ -61244,7 +62618,7 @@ function isBridgeGeneratedArtifact(artifact) {
|
|
|
61244
62618
|
return typeof payload === "object" && payload !== null && "event" in payload;
|
|
61245
62619
|
}
|
|
61246
62620
|
function newArtifactId() {
|
|
61247
|
-
return `EA-${
|
|
62621
|
+
return `EA-${import_node_crypto14.default.randomUUID()}`;
|
|
61248
62622
|
}
|
|
61249
62623
|
var BLAST_RADIUS_FLOOR_PATTERNS = [/^supabase\/migrations\//, /^packages\/db\/src\/schema\.ts$/];
|
|
61250
62624
|
function isBlastRadiusFloorPath(path29) {
|
|
@@ -61413,8 +62787,8 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
|
|
|
61413
62787
|
return deps.run("exec", async () => {
|
|
61414
62788
|
const pending = [];
|
|
61415
62789
|
let resolveTurnEnded = null;
|
|
61416
|
-
const turnEnded = new Promise((
|
|
61417
|
-
resolveTurnEnded =
|
|
62790
|
+
const turnEnded = new Promise((resolve2) => {
|
|
62791
|
+
resolveTurnEnded = resolve2;
|
|
61418
62792
|
});
|
|
61419
62793
|
let acceptedTurnId = null;
|
|
61420
62794
|
const unsubActivity = codex.onActivity(threadId, () => {
|
|
@@ -61495,7 +62869,7 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
61495
62869
|
if (!claimed) return;
|
|
61496
62870
|
if (!claimed.leaseId) {
|
|
61497
62871
|
await deps.client.createInboxItem(projectSlug, {
|
|
61498
|
-
id: `II-${
|
|
62872
|
+
id: `II-${import_node_crypto14.default.randomUUID()}`,
|
|
61499
62873
|
kind: "handoff",
|
|
61500
62874
|
taskId: claimed.taskId,
|
|
61501
62875
|
body: `Execution run ${runId} was claimed but returned no leaseId. This indicates DB/API corruption and requires operator attention.`
|
|
@@ -61546,6 +62920,22 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
61546
62920
|
}
|
|
61547
62921
|
}
|
|
61548
62922
|
}
|
|
62923
|
+
const needsBrowser = claimed.prerequisites?.some((p) => p.mcp === "mrrlin-browser") || task.domains?.includes("browser");
|
|
62924
|
+
if (needsBrowser) {
|
|
62925
|
+
const checkBusy = deps.checkBrowserBusy ?? checkBrowserBusy;
|
|
62926
|
+
const busy = checkBusy();
|
|
62927
|
+
if (busy.busy) {
|
|
62928
|
+
await failRun(
|
|
62929
|
+
deps,
|
|
62930
|
+
projectSlug,
|
|
62931
|
+
task,
|
|
62932
|
+
runId,
|
|
62933
|
+
leaseId,
|
|
62934
|
+
`browser_busy: run ${runId} needs the browser, but it is currently in use by another task (holder pid: ${busy.holderPid}). Please retry once it finishes.`
|
|
62935
|
+
);
|
|
62936
|
+
return;
|
|
62937
|
+
}
|
|
62938
|
+
}
|
|
61549
62939
|
const blockerTasks = task.blockedBy.length ? await Promise.all(task.blockedBy.map((blockerTaskId) => deps.client.getTask(projectSlug, blockerTaskId))) : [];
|
|
61550
62940
|
const missingBlockers = task.blockedBy.filter((_, index) => !blockerTasks[index]);
|
|
61551
62941
|
if (missingBlockers.length > 0) {
|
|
@@ -61724,12 +63114,20 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
61724
63114
|
turnCount: 0,
|
|
61725
63115
|
usage: null
|
|
61726
63116
|
};
|
|
61727
|
-
const
|
|
63117
|
+
const recordTurn2 = (outcome) => {
|
|
61728
63118
|
runTelemetry.promptBytes += outcome.telemetry.promptBytes;
|
|
61729
63119
|
runTelemetry.turnCount += 1;
|
|
61730
63120
|
runTelemetry.usage = addTurnTokenUsage(runTelemetry.usage, outcome.telemetry.usage);
|
|
61731
63121
|
return outcome;
|
|
61732
63122
|
};
|
|
63123
|
+
const isContextBudgetExhausted = (usage) => {
|
|
63124
|
+
if (!usage) return false;
|
|
63125
|
+
const contextTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
|
|
63126
|
+
if (contextTokens <= 0) return false;
|
|
63127
|
+
const maxTokens = 2e5;
|
|
63128
|
+
const remainingTokens = maxTokens - contextTokens;
|
|
63129
|
+
return remainingTokens / maxTokens < 0.03 || remainingTokens < 4e3;
|
|
63130
|
+
};
|
|
61733
63131
|
const effortDecision = deriveEffort({
|
|
61734
63132
|
impact: task.impact,
|
|
61735
63133
|
type: task.type,
|
|
@@ -61952,7 +63350,7 @@ ${renderTaskStateSection(taskState)}`;
|
|
|
61952
63350
|
);
|
|
61953
63351
|
return;
|
|
61954
63352
|
}
|
|
61955
|
-
const primed =
|
|
63353
|
+
const primed = recordTurn2(
|
|
61956
63354
|
await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, bundle, primingBudgetMs, "priming", effortDecision?.level)
|
|
61957
63355
|
);
|
|
61958
63356
|
if (!primed.ok) {
|
|
@@ -62016,7 +63414,7 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
62016
63414
|
);
|
|
62017
63415
|
return;
|
|
62018
63416
|
}
|
|
62019
|
-
const outcome =
|
|
63417
|
+
const outcome = recordTurn2(
|
|
62020
63418
|
await driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, executionPrompt, workBudgetMs, "work", effortDecision?.level)
|
|
62021
63419
|
);
|
|
62022
63420
|
if (!outcome.ok) {
|
|
@@ -62024,6 +63422,26 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
62024
63422
|
await failRun(deps, projectSlug, task, runId, leaseId, outcome.error);
|
|
62025
63423
|
return;
|
|
62026
63424
|
}
|
|
63425
|
+
if (isContextBudgetExhausted(outcome.telemetry.usage)) {
|
|
63426
|
+
console.warn(
|
|
63427
|
+
JSON.stringify({
|
|
63428
|
+
event: "context_budget_gate_triggered",
|
|
63429
|
+
projectSlug,
|
|
63430
|
+
runId,
|
|
63431
|
+
taskId: task.id,
|
|
63432
|
+
usage: outcome.telemetry.usage
|
|
63433
|
+
})
|
|
63434
|
+
);
|
|
63435
|
+
await failRun(
|
|
63436
|
+
deps,
|
|
63437
|
+
projectSlug,
|
|
63438
|
+
task,
|
|
63439
|
+
runId,
|
|
63440
|
+
leaseId,
|
|
63441
|
+
`context_budget_exhausted: context window is near capacity (<3% remaining). Execution run halted safely to preserve progress and prevent lost output.`
|
|
63442
|
+
);
|
|
63443
|
+
return;
|
|
63444
|
+
}
|
|
62027
63445
|
const prReadyStatuses = useExecutor ? CODE_PR_READY_TASK_STATUSES : COMPLETED_TASK_STATUSES;
|
|
62028
63446
|
let readiness = await assertRunCompletionReady(deps, projectSlug, task.id, runId, prReadyStatuses);
|
|
62029
63447
|
if (!readiness.ok) {
|
|
@@ -62042,7 +63460,7 @@ Continue executing task ${task.id}. Make the next concrete increment of progress
|
|
|
62042
63460
|
})
|
|
62043
63461
|
);
|
|
62044
63462
|
const completionPrompt = buildCompletionTurnPrompt(task.id, readiness.task.status);
|
|
62045
|
-
const completionOutcome =
|
|
63463
|
+
const completionOutcome = recordTurn2(
|
|
62046
63464
|
await driveTurn(
|
|
62047
63465
|
deps,
|
|
62048
63466
|
codex,
|
|
@@ -62902,7 +64320,7 @@ async function reconcileTaskAwaitingMerge(deps, projectSlug, task, runId, reason
|
|
|
62902
64320
|
}
|
|
62903
64321
|
try {
|
|
62904
64322
|
await deps.client.createInboxItem(projectSlug, {
|
|
62905
|
-
id: `II-${
|
|
64323
|
+
id: `II-${import_node_crypto14.default.randomUUID()}`,
|
|
62906
64324
|
kind: "handoff",
|
|
62907
64325
|
taskId: task.id,
|
|
62908
64326
|
body: `Task ${task.id} (${task.title}) still needs operator follow-up: ${reason}. Mrrlin could not move the task back to Handoff needed automatically.`
|
|
@@ -62937,7 +64355,7 @@ async function notifyOperatorHandoff(deps, projectSlug, task, runId, reason) {
|
|
|
62937
64355
|
}
|
|
62938
64356
|
try {
|
|
62939
64357
|
await deps.client.createInboxItem(projectSlug, {
|
|
62940
|
-
id: `II-${
|
|
64358
|
+
id: `II-${import_node_crypto14.default.randomUUID()}`,
|
|
62941
64359
|
kind: "handoff",
|
|
62942
64360
|
taskId: task.id,
|
|
62943
64361
|
body: `Execution run ${runId} for task ${task.id} (${task.title}) needs operator attention: ${reason}`
|
|
@@ -63098,7 +64516,7 @@ async function deployGateHandoff(deps, projectSlug, task, runId, reason, leaseId
|
|
|
63098
64516
|
}).catch(() => {
|
|
63099
64517
|
});
|
|
63100
64518
|
await deps.client.createInboxItem(projectSlug, {
|
|
63101
|
-
id: `II-${
|
|
64519
|
+
id: `II-${import_node_crypto14.default.randomUUID()}`,
|
|
63102
64520
|
kind: "handoff",
|
|
63103
64521
|
taskId: task.id,
|
|
63104
64522
|
body: `Deploy gate for run ${runId} (task ${task.id} \u2014 ${task.title}) needs operator attention: ${reason}`
|
|
@@ -63117,7 +64535,7 @@ function maxLeaseReclaims() {
|
|
|
63117
64535
|
}
|
|
63118
64536
|
|
|
63119
64537
|
// src/async-job-consumer.ts
|
|
63120
|
-
var
|
|
64538
|
+
var import_node_crypto15 = __toESM(require("node:crypto"), 1);
|
|
63121
64539
|
var ASYNC_JOB_MAX_ATTEMPTS = 2;
|
|
63122
64540
|
function ASYNC_UNOWNED_TTL_DAYS() {
|
|
63123
64541
|
const raw = Number(process.env.MRRLIN_ASYNC_UNOWNED_TTL_DAYS);
|
|
@@ -63207,12 +64625,21 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
63207
64625
|
} finally {
|
|
63208
64626
|
clearInterval(touchTimer);
|
|
63209
64627
|
}
|
|
64628
|
+
if (outcome.kind === "superseded") {
|
|
64629
|
+
await deps.client.terminalizeAsyncJob(projectSlug, jobId, {
|
|
64630
|
+
leaseId,
|
|
64631
|
+
status: "superseded",
|
|
64632
|
+
error: outcome.reason ?? outcome.error ?? "Superseded"
|
|
64633
|
+
}).catch(() => {
|
|
64634
|
+
});
|
|
64635
|
+
return;
|
|
64636
|
+
}
|
|
63210
64637
|
const terminalStatus = outcome.kind === "done" ? "done" : "failed";
|
|
63211
64638
|
await deps.client.terminalizeAsyncJob(projectSlug, jobId, {
|
|
63212
64639
|
leaseId,
|
|
63213
64640
|
status: terminalStatus,
|
|
63214
64641
|
resultJson: outcome.kind === "done" ? outcome.resultJson : null,
|
|
63215
|
-
finalReviewedHash: outcome.kind === "done" ? outcome.finalReviewedHash : null,
|
|
64642
|
+
finalReviewedHash: outcome.kind === "done" ? outcome.finalReviewedHash : outcome.kind === "failed" ? outcome.finalReviewedHash ?? null : null,
|
|
63216
64643
|
error: outcome.kind === "done" ? null : outcome.error,
|
|
63217
64644
|
failureReason: outcome.kind === "done" ? null : outcome.failureReason
|
|
63218
64645
|
}).catch(() => {
|
|
@@ -63233,7 +64660,7 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
|
|
|
63233
64660
|
const delivery = descriptor2.onComplete(outcome, input);
|
|
63234
64661
|
if (!delivery.suppressInbox) {
|
|
63235
64662
|
await deps.client.createInboxItem(projectSlug, {
|
|
63236
|
-
id: delivery.id ?? `II-${
|
|
64663
|
+
id: delivery.id ?? `II-${import_node_crypto15.default.randomUUID()}`,
|
|
63237
64664
|
kind: delivery.kind,
|
|
63238
64665
|
body: delivery.body,
|
|
63239
64666
|
...delivery.taskId ? { taskId: delivery.taskId } : {},
|
|
@@ -63251,13 +64678,13 @@ async function deriveRequestedHash(client, toolName, input) {
|
|
|
63251
64678
|
if (input.requestedHash) return input.requestedHash;
|
|
63252
64679
|
if (toolName === "run_spec_consensus" && input.pageId) {
|
|
63253
64680
|
const page = await client.getWikiPage(input.projectSlug, input.pageId);
|
|
63254
|
-
return page.contentHash ??
|
|
64681
|
+
return page.contentHash ?? import_node_crypto15.default.createHash("sha256").update(page.markdownBody).digest("hex");
|
|
63255
64682
|
}
|
|
63256
64683
|
if (toolName === "draft_task_spec" && input.taskId) {
|
|
63257
64684
|
const task = await client.getTask(input.projectSlug, input.taskId);
|
|
63258
|
-
return task.updatedAt ??
|
|
64685
|
+
return task.updatedAt ?? import_node_crypto15.default.createHash("sha256").update(JSON.stringify(task)).digest("hex");
|
|
63259
64686
|
}
|
|
63260
|
-
return
|
|
64687
|
+
return import_node_crypto15.default.createHash("sha256").update(JSON.stringify(input)).digest("hex");
|
|
63261
64688
|
}
|
|
63262
64689
|
async function runAsyncBackstopSweep(deps, drive = (slug, jobId) => driveAsyncJob(deps, slug, jobId)) {
|
|
63263
64690
|
const projects = await deps.client.listProjects();
|
|
@@ -63599,7 +65026,9 @@ function loadThreadIndex(stateDir, ttlMs, now) {
|
|
|
63599
65026
|
lastUsedAt,
|
|
63600
65027
|
model: typeof entry.model === "string" ? entry.model : null,
|
|
63601
65028
|
reasoningEffort: typeof entry.reasoningEffort === "string" ? entry.reasoningEffort : null,
|
|
63602
|
-
provider: typeof entry.provider === "string" ? entry.provider : null
|
|
65029
|
+
provider: typeof entry.provider === "string" ? entry.provider : null,
|
|
65030
|
+
systemInstructionHash: typeof entry.systemInstructionHash === "string" ? entry.systemInstructionHash : null,
|
|
65031
|
+
...parseLedger(entry.clarificationLedger) ? { clarificationLedger: parseLedger(entry.clarificationLedger) } : {}
|
|
63603
65032
|
});
|
|
63604
65033
|
}
|
|
63605
65034
|
return result;
|
|
@@ -67359,7 +68788,7 @@ function saveAgentPairing(record2) {
|
|
|
67359
68788
|
}
|
|
67360
68789
|
}
|
|
67361
68790
|
function runPhoneTurn(handler, directorSessionId, text, emit) {
|
|
67362
|
-
return new Promise((
|
|
68791
|
+
return new Promise((resolve2) => {
|
|
67363
68792
|
const emitter = new import_node_events3.EventEmitter();
|
|
67364
68793
|
const fakeSocket = {
|
|
67365
68794
|
on: (event, cb) => {
|
|
@@ -67374,7 +68803,7 @@ function runPhoneTurn(handler, directorSessionId, text, emit) {
|
|
|
67374
68803
|
settled = true;
|
|
67375
68804
|
if (timer) clearTimeout(timer);
|
|
67376
68805
|
emitter.removeAllListeners();
|
|
67377
|
-
|
|
68806
|
+
resolve2();
|
|
67378
68807
|
};
|
|
67379
68808
|
const send = (msg) => {
|
|
67380
68809
|
if (msg.type === "error") {
|
|
@@ -67534,13 +68963,19 @@ async function renderPairingQrSvg(payload) {
|
|
|
67534
68963
|
// src/executor-server.ts
|
|
67535
68964
|
var import_node_child_process20 = require("node:child_process");
|
|
67536
68965
|
function defaultReadinessRunner(provider) {
|
|
67537
|
-
|
|
68966
|
+
if (provider !== "codex" && provider !== "claude" && provider !== "agy") {
|
|
68967
|
+
throw new Error(`unsupported executor provider: ${String(provider)}`);
|
|
68968
|
+
}
|
|
68969
|
+
const command = provider === "claude" ? "claude" : provider === "agy" ? "agy" : "codex";
|
|
67538
68970
|
return (args) => {
|
|
67539
68971
|
const r = (0, import_node_child_process20.spawnSync)(command, args, { encoding: "utf8", timeout: 8e3, shell: true });
|
|
67540
68972
|
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
67541
68973
|
};
|
|
67542
68974
|
}
|
|
67543
68975
|
async function startExecutorServer(opts) {
|
|
68976
|
+
if (opts.provider !== "codex" && opts.provider !== "claude" && opts.provider !== "agy") {
|
|
68977
|
+
throw new Error(`unsupported executor provider: ${String(opts.provider)}`);
|
|
68978
|
+
}
|
|
67544
68979
|
const verify = opts.verifyReadiness ?? verifyProviderReadiness;
|
|
67545
68980
|
const runner = opts.readinessRunner ?? defaultReadinessRunner(opts.provider);
|
|
67546
68981
|
const readiness = await verify(opts.provider, runner);
|
|
@@ -67553,8 +68988,37 @@ async function startExecutorServer(opts) {
|
|
|
67553
68988
|
error51.details = readiness.details;
|
|
67554
68989
|
throw error51;
|
|
67555
68990
|
}
|
|
68991
|
+
const baseEnv = { ...opts.env ?? process.env };
|
|
68992
|
+
const childEnv = withDisabledGitHooksEnv(baseEnv, opts.disabledGitHooksDir);
|
|
68993
|
+
if (opts.provider === "codex") {
|
|
68994
|
+
const manager = new CodexServerManager();
|
|
68995
|
+
try {
|
|
68996
|
+
const client = await manager.acquire({
|
|
68997
|
+
cwd: opts.worktree,
|
|
68998
|
+
approvalPolicy: "never",
|
|
68999
|
+
env: childEnv,
|
|
69000
|
+
...opts.spawn ? { spawn: opts.spawn } : {}
|
|
69001
|
+
});
|
|
69002
|
+
let disposed = false;
|
|
69003
|
+
const dispose = () => {
|
|
69004
|
+
if (disposed) return;
|
|
69005
|
+
disposed = true;
|
|
69006
|
+
try {
|
|
69007
|
+
manager.shutdown();
|
|
69008
|
+
} catch {
|
|
69009
|
+
}
|
|
69010
|
+
};
|
|
69011
|
+
return { handle: createCodexAgentHandle(client), dispose };
|
|
69012
|
+
} catch (error51) {
|
|
69013
|
+
try {
|
|
69014
|
+
manager.shutdown();
|
|
69015
|
+
} catch {
|
|
69016
|
+
}
|
|
69017
|
+
throw error51;
|
|
69018
|
+
}
|
|
69019
|
+
}
|
|
67556
69020
|
if (opts.provider === "claude") {
|
|
67557
|
-
const handle = new ClaudeAgentHandle();
|
|
69021
|
+
const handle = new ClaudeAgentHandle(childEnv);
|
|
67558
69022
|
let disposed = false;
|
|
67559
69023
|
const dispose = () => {
|
|
67560
69024
|
if (disposed) return;
|
|
@@ -67563,30 +69027,17 @@ async function startExecutorServer(opts) {
|
|
|
67563
69027
|
};
|
|
67564
69028
|
return { handle, dispose };
|
|
67565
69029
|
}
|
|
67566
|
-
|
|
67567
|
-
|
|
67568
|
-
const client = await manager.acquire({
|
|
67569
|
-
cwd: opts.worktree,
|
|
67570
|
-
approvalPolicy: "never",
|
|
67571
|
-
...opts.spawn ? { spawn: opts.spawn } : {}
|
|
67572
|
-
});
|
|
69030
|
+
if (opts.provider === "agy") {
|
|
69031
|
+
const handle = new AgyAgentHandle(childEnv);
|
|
67573
69032
|
let disposed = false;
|
|
67574
69033
|
const dispose = () => {
|
|
67575
69034
|
if (disposed) return;
|
|
67576
69035
|
disposed = true;
|
|
67577
|
-
|
|
67578
|
-
manager.shutdown();
|
|
67579
|
-
} catch {
|
|
67580
|
-
}
|
|
69036
|
+
handle.disposeAll();
|
|
67581
69037
|
};
|
|
67582
|
-
return { handle
|
|
67583
|
-
} catch (error51) {
|
|
67584
|
-
try {
|
|
67585
|
-
manager.shutdown();
|
|
67586
|
-
} catch {
|
|
67587
|
-
}
|
|
67588
|
-
throw error51;
|
|
69038
|
+
return { handle, dispose };
|
|
67589
69039
|
}
|
|
69040
|
+
throw new Error(`unsupported executor provider: ${String(opts.provider)}`);
|
|
67590
69041
|
}
|
|
67591
69042
|
|
|
67592
69043
|
// src/self-review.ts
|
|
@@ -68375,9 +69826,9 @@ function readCodexFallbackModels() {
|
|
|
68375
69826
|
return [...models.values()];
|
|
68376
69827
|
}
|
|
68377
69828
|
function selfUpdateInboxItemId(offeredVersion) {
|
|
68378
|
-
if (!offeredVersion) return `II-MCPUPD-${(0,
|
|
69829
|
+
if (!offeredVersion) return `II-MCPUPD-${(0, import_node_crypto16.randomUUID)()}`;
|
|
68379
69830
|
if (/^[A-Za-z0-9._-]+$/.test(offeredVersion)) return `II-MCPUPD-${offeredVersion}`;
|
|
68380
|
-
const hash3 =
|
|
69831
|
+
const hash3 = import_node_crypto16.default.createHash("sha256").update(offeredVersion).digest("hex").slice(0, 10);
|
|
68381
69832
|
return `II-MCPUPD-${offeredVersion.replace(/[^A-Za-z0-9._-]/g, "-")}-${hash3}`;
|
|
68382
69833
|
}
|
|
68383
69834
|
function neutralCheckoutCwd(stateDir) {
|
|
@@ -68399,6 +69850,25 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
68399
69850
|
"You are the Mrrlin Director assistant.",
|
|
68400
69851
|
"You can call MCP tools to read and write project state.",
|
|
68401
69852
|
"Prefer calling tools over guessing. Be concise.",
|
|
69853
|
+
// O-DIRECTOR-HARD-GATES: a compact behaviour contract replaced ~30 accumulated mandates, several of
|
|
69854
|
+
// which were fitted to single benchmark personas. Closure and localization are enforced in code
|
|
69855
|
+
// (director-closure.ts, linkifyMrrlinIds.ts); the clarification ledger arrives in the turn context.
|
|
69856
|
+
"Behaviour contract (three rules):",
|
|
69857
|
+
'- DEFAULT-FIRST: never open an open-ended question round. When the intent-confirmation contract below says you must ask before creating a task, ask only about what would change the architecture or the deliverable \u2014 never about anything the operator already stated in this conversation \u2014 and once the operator has replied, take a reasonable default for whatever is still open and say so plainly ("unless you object, I take X") instead of asking again. Never ask permission to create tasks or specs \u2014 creating them is your job.',
|
|
69858
|
+
"- USER-FACING CLOSURE: end every turn with plain text in the operator's language. If the turn created or changed anything, name each item (ID with a one-line scope), where the result goes, and what happens next; if it changed nothing, say plainly what you need from the operator or what happens next. Never invent an ID or a write to have something to report. A UI card or a bare list of IDs never replaces that text.",
|
|
69859
|
+
"- QUESTION LIFECYCLE: every question you asked must end in exactly one state \u2014 answered by the operator, assumed (you state the default you took), or blocker (you state what it blocks). The open-question list in the turn context is authoritative: resolve each item explicitly and never drop one silently.",
|
|
69860
|
+
"Core guards:",
|
|
69861
|
+
"- Never claim a deliverable, file, test result, or running execution exists unless a tool actually produced it. Never claim autopilot is running unless an execution run is active; for a human_review task say it waits in To Do for your approval.",
|
|
69862
|
+
"- Mention a GT- or WK- id only if a tool returned it in this conversation (server-authoritative receipts).",
|
|
69863
|
+
"- Reply in the operator's language for the whole turn (questions, task scopes, summaries).",
|
|
69864
|
+
"- Keep chat short and human. No code blocks, DDL, terminal output, local filesystem paths, internal job/inbox UUIDs or alert markers in chat \u2014 put long content into a wiki page and link it.",
|
|
69865
|
+
"- Spec pages in folder Specs must contain `## Acceptance criteria` followed by `- [ ]` items.",
|
|
69866
|
+
"- AGNOSTIC DELIVERY MANDATE: Mrrlin is an Agnostic Delivery Platform decoupled from mandatory GitHub. Delivery is routed strictly by artifact kind: (1) document -> wiki (specs, policies, legal compliance playbooks, project documentation); (2) page -> publish (web pages, landing pages, interactive HTML previews); (3) file_set -> artifacts (file uploads, bundles, build zips); (4) message -> send (outbound webhooks, Slack, notifications); (5) code_change -> repo_host (git / GitHub ONLY when modifying source repository code). NEVER stall or refuse non-code tasks (such as marketing landing pages, GDPR checklists, or legal compliance playbooks) by demanding a Git repository. Non-code tasks deliver directly via page (Publish) or document (Wiki).",
|
|
69867
|
+
"- 3-TIER COMMERCIAL SCOPE GOVERNANCE: Treat tasks by domain governance tier. (1) Tier 1 Core Tech Delivery (backend, frontend, SRE, databases): decompose autonomously into repository code changes (when a repo is bound) or code artifacts. (2) Tier 2 Planning & Content (landing pages, SEO, marketing): create actionable drafts; when details such as a phone number, brand name or copy are missing, do not loop on questions \u2014 create the task, record `## Placeholders: [NEED_INPUT: <item>]` in the Wiki spec, and set `autonomyLevel: 'human_review'`. (3) Tier 3 Guardrailed Advisory (high risk: privacy/GDPR, security/SOC 2, billing, production database migrations, infrastructure): always set `autonomyLevel: 'human_review'`, put the domain's standard safety controls into the spec, and state that irreversible actions need human sign-off.",
|
|
69868
|
+
"- INBOX QUESTION RETIREMENT MANDATE \u2014 Retiring answered questions: Before creating a task/plan, retire only pending Inbox questions the operator actually answered. Call `decide_inbox_item(itemId: <exact id>, decidedBy: 'director', verdict: 'acknowledged', answer: <actual answer>)`. Use `verdict: 'rejected', reason: ...` only if moot. Never close an unrelated item.",
|
|
69869
|
+
"- CHAT CLARIFICATIONS ARE IN CHAT ONLY: During synchronous interactive Director chat, ask clarifying questions directly in your chat response. DO NOT call `create_inbox_item(kind: 'question')` for routine conversational clarifications in Turn 1 \u2014 Inbox questions are reserved for autonomous background workers or asynchronous gate pauses that occur while the operator is away.",
|
|
69870
|
+
"- AUTOPILOT PRESENCE FENCE TRANSPARENCY: Autopilot requires operator web app presence to trigger execution ticks (`operator_absent`). When the operator asks about autopilot starting, remind them that keeping the web tab open allows the scheduler to advance the queue automatically.",
|
|
69871
|
+
"",
|
|
68402
69872
|
"Autonomy level is NOT a reason to skip clarification. `auto` means only that no human gate is",
|
|
68403
69873
|
"required before the result LANDS (merge/deploy); it does not authorize building on unvalidated",
|
|
68404
69874
|
"assumptions. Judge clarification on requirement clarity, independently of autonomy_level.",
|
|
@@ -68492,17 +69962,22 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
68492
69962
|
"turn with one sentence restating your understanding, then at most 3 scope-level questions, and",
|
|
68493
69963
|
"do NOT create the task yet. Create it after the operator replies.",
|
|
68494
69964
|
"",
|
|
68495
|
-
"Once the operator replies and clarifies or confirms the scope (or if scope was already clear),",
|
|
68496
|
-
"you MUST materialize the work onto the board: create the plan (if needed) and call `create_task`",
|
|
68497
|
-
"in the same turn so actionable task cards appear on `/tasks` immediately. Never leave the board",
|
|
68498
|
-
"empty with 0 tasks after agreeing on scope. Never claim tasks are created, queued, or ready unless",
|
|
68499
|
-
"`create_task` was actually invoked and returned the task ID.",
|
|
68500
|
-
"",
|
|
68501
69965
|
"Set operatorSeedRequest to the operator's OWN WORDS, verbatim. Select every message in this",
|
|
68502
69966
|
"conversation that forms part of the request \u2014 not only the last one \u2014 and concatenate them in",
|
|
68503
69967
|
"order, separated by a blank line. Do NOT paraphrase, summarise, or rewrite any of it, and do",
|
|
68504
69968
|
"NOT add words of your own.",
|
|
68505
69969
|
"",
|
|
69970
|
+
"Once the operator replies and clarifies or confirms the scope (or if scope was already clear),",
|
|
69971
|
+
"you MUST create the tasks: create the plan (if needed) and call `create_task` in the same turn so",
|
|
69972
|
+
"actionable task cards appear on the Tasks board immediately. Never leave the board empty with 0",
|
|
69973
|
+
"tasks after agreeing on scope. Never claim tasks are created, queued, or ready unless `create_task`",
|
|
69974
|
+
"was actually invoked and returned the task ID. Set `status: 'todo'` on newly created actionable",
|
|
69975
|
+
"tasks unless explicitly asked for backlog, so they appear in active Kanban columns immediately.",
|
|
69976
|
+
"When referring to created tasks or where to view them, use natural, human-friendly language with a markdown link",
|
|
69977
|
+
"to the Tasks page (e.g. 'on the [Tasks page](/tasks)', '[Tasks board](/tasks)', or in Ukrainian '\u043D\u0430 [\u0441\u0442\u043E\u0440\u0456\u043D\u0446\u0456 \u0437\u0430\u0434\u0430\u0447](/tasks)').",
|
|
69978
|
+
"Never output raw technical URL fragments like '\u043D\u0430 /tasks' or 'on /tasks', and never use developer database",
|
|
69979
|
+
"jargon like 'materialized tasks' or '\u043C\u0430\u0442\u0435\u0440\u0456\u0430\u043B\u0456\u0437\u0443\u0432\u0430\u0432 \u0437\u0430\u0434\u0430\u0447\u0456' \u2014 say 'created' / '\u0441\u0442\u0432\u043E\u0440\u0438\u0432' or 'added' / '\u0434\u043E\u0434\u0430\u0432' instead.",
|
|
69980
|
+
"",
|
|
68506
69981
|
"Set resolvedIntent to the accepted task intent in your own concise words only after the outcome",
|
|
68507
69982
|
"is resolved enough to create the task. Use it to capture the actionable understanding that came",
|
|
68508
69983
|
"from the conversation and, when needed, retrieved wiki context. Do NOT use resolvedIntent as a",
|
|
@@ -68541,6 +70016,9 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
68541
70016
|
" SHALL sentence stating the externally visible behavior.",
|
|
68542
70017
|
"- Give every requirement at least one `#### Scenario: <name>` with `- WHEN ...` / `- THEN ...`",
|
|
68543
70018
|
" bullets (`- AND ...` as needed). Scenarios make the spec verifiable.",
|
|
70019
|
+
"- MUST include `## Acceptance criteria`: a markdown checklist (`- [ ] ...`) of concrete, verifiable",
|
|
70020
|
+
" conditions \u2014 this is the machine-usable yardstick the consensus review scores against. NEVER format criteria as numbered lists (`1. ...`) or plain bullets.",
|
|
70021
|
+
"- MUST include `## Verification`: the exact tests, commands, or checks that prove each criterion holds.",
|
|
68544
70022
|
"- Keep behavior contracts in the spec; implementation details belong in `## Implementation Plan`.",
|
|
68545
70023
|
"- Apply this format ONLY to Specs/{id} pages; do not retro-fit it onto other wiki page types.",
|
|
68546
70024
|
'- Spec consensus runs ASYNCHRONOUSLY. Call `run_spec_consensus` for a `Specs/{id}` page; it returns IMMEDIATELY. On `already_converged`, the spec is already marked ready \u2014 proceed straight to `create_execution_run`; do NOT call `mark_spec_ready` and do NOT re-run consensus. On `queued` or `already_running`, tell the user it was launched and END your turn \u2014 do NOT poll or call it again; the result arrives in the inbox (an `info` item on CONVERGED, a `handoff` item if it was blocked/failed). When a `handoff` says it was blocked, address the recorded issues in the spec (any edit re-arms the gate) and call `run_spec_consensus` once more. For a NON-code task (domain seo/marketing/content, ADR 0020): the low-risk lanes `seo`, `marketing`, and `content` proceed to `create_execution_run` after consensus converges, using the same flow as code tasks. When the review agrees, tell the operator "the reviewers agreed on the spec" and proceed to create_execution_run. When it does not, tell them the reviewers could not agree and that there is a decision question in the inbox. Never say "consensus", "converged", or "CONVERGED" to the operator; those are internal words.',
|
|
@@ -68660,15 +70138,20 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
68660
70138
|
`Product-surface contract \u2014 never answer "I don't know where that is" about Mrrlin's own UI:`,
|
|
68661
70139
|
"- You are talking to an operator inside this product. When they ask where something is, name",
|
|
68662
70140
|
" the route and the control by the label actually printed on it, then let them click it.",
|
|
68663
|
-
"-
|
|
68664
|
-
"
|
|
68665
|
-
|
|
68666
|
-
"
|
|
68667
|
-
|
|
68668
|
-
"
|
|
68669
|
-
"
|
|
68670
|
-
"
|
|
68671
|
-
"
|
|
70141
|
+
"- Autopilot view lives on `/autopilot`. It displays autonomous execution runs, active background",
|
|
70142
|
+
" jobs, and tasks currently in handoff. To inspect why tasks are in handoff or diagnose autonomous",
|
|
70143
|
+
' progress, use the canonical typed tools: `list_inbox_items` (filtering by kind="handoff"),',
|
|
70144
|
+
" `list_tasks` (filtering by status), and `list_execution_runs`.",
|
|
70145
|
+
"- Tasks and board backlog live on `/tasks` (and individual task details on `/items/{taskId}`).",
|
|
70146
|
+
" Always use `list_tasks`, `get_task`, and `list_task_events` to inspect them.",
|
|
70147
|
+
"- Project knowledge, deliverable reports, and specifications live under `/wiki` and `/deliverables/*`.",
|
|
70148
|
+
" These are stored as project wiki pages \u2014 read them directly via `get_wiki_page` or search with",
|
|
70149
|
+
" `search_wiki_pages`/`list_wiki_pages`. Never open the browser for `/deliverables/*` or `/wiki/*`.",
|
|
70150
|
+
"- Workspace settings, scheduler configurations, and integrations live on `/settings`.",
|
|
70151
|
+
"- Universal Tool Availability: All canonical typed tools (`list_tasks`, `get_task`, `list_inbox_items`,",
|
|
70152
|
+
" `list_execution_runs`, `get_wiki_page`, `search_wiki_pages`, `list_wiki_pages`) are authoritative",
|
|
70153
|
+
" and fully available across ALL routes and session tabs. Never tell an operator that a typed tool",
|
|
70154
|
+
" is missing or unavailable because of the current page or route.",
|
|
68672
70155
|
"- If a surface is genuinely not in this list, say what you do know and check with a typed tool",
|
|
68673
70156
|
' rather than guessing a route \u2014 a wrong path costs the operator more than an honest "let me',
|
|
68674
70157
|
' check". Never send them to the browser to hunt for a page in their own app.',
|
|
@@ -68722,22 +70205,12 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
68722
70205
|
"- During a live chat turn, when a SPECIFIC task is ambiguous and the ambiguity affects",
|
|
68723
70206
|
" the work, ASK the operator a simple, concrete question inline in your reply (default to",
|
|
68724
70207
|
" asking rather than silently assuming). Low-impact ambiguity may still be recorded as an",
|
|
68725
|
-
" assumption on the spec page.",
|
|
68726
|
-
"
|
|
68727
|
-
'
|
|
68728
|
-
|
|
68729
|
-
"
|
|
68730
|
-
"
|
|
68731
|
-
" you explicitly prefer a path. The operator always has a custom-answer path in the UI, so",
|
|
68732
|
-
" your options should guide, not trap. (The server dedups identical open questions; do not",
|
|
68733
|
-
" supply a dedup key.)",
|
|
68734
|
-
"- When the operator answers in chat, write the answer into the Specs/{taskId}",
|
|
68735
|
-
' Clarifications section, then `decide_inbox_item` the question with `verdict="acknowledged"`',
|
|
68736
|
-
" and `answer` = the resolved answer.",
|
|
68737
|
-
"- Project-level ambiguity (scope or direction not yet tied to a specific task) MAY be banked",
|
|
68738
|
-
" the same way but with `taskId` OMITTED. There is no spec page yet, so incorporate the answer",
|
|
68739
|
-
" into your current scoping/conversation. Reserve taskless questions for genuine project-level",
|
|
68740
|
-
" scope \u2014 task-affecting questions still set `taskId`.",
|
|
70208
|
+
" assumption on the spec page. NEVER ask clarifying questions after Turn 1 when the operator",
|
|
70209
|
+
" has provided core project scope; adopt standard conventions for secondary edge cases and create tasks.",
|
|
70210
|
+
'- In synchronous live Director chat (Turn 1 scoping), ask your questions directly in your chat reply without creating Inbox items. Only bank questions in Inbox (`create_inbox_item` with `kind="question"`, `taskId` set, `directorSessionId` from Context JSON, `questionOptions` = 2-3 concrete answer options) when an autonomous background task pauses, when a task requires operator decision while the operator is away, or when explicitly requested. Each banked option MUST include a short `label`, a concise `description`, and a self-contained durable `answer`; you MAY mark at most one option as `recommended` when you explicitly prefer a path.',
|
|
70211
|
+
'- When the operator answers in chat, write the answer into the Specs/{taskId} Clarifications section, and if an Inbox question was previously open, `decide_inbox_item` with `verdict="acknowledged"` and `answer` = the resolved answer.',
|
|
70212
|
+
"- Project-level ambiguity (scope or direction not yet tied to a specific task) in live chat stays in chat; for genuine asynchronous pauses it MAY be banked with `taskId` OMITTED. Reserve taskless Inbox questions for genuine project-level scope.",
|
|
70213
|
+
"- When the operator answers a banked question, immediately call `decide_inbox_item` with its exact `itemId`, `decidedBy='director'`, `verdict='acknowledged'`, and the actual `answer` before any `create_task`. Never close another pending question.",
|
|
68741
70214
|
"- Binding taskless answers to a task: WHEN you create a task out of project-level scoping,",
|
|
68742
70215
|
" call `list_inbox_items` (kind=question), fold any relevant `acknowledged` TASKLESS answer",
|
|
68743
70216
|
" into the new task's Specs/{id} Clarifications, and `reject` (with a reason) any taskless",
|
|
@@ -68843,8 +70316,8 @@ async function runCollectedAgentTurn(handle, prompt, cwd, baseInstructions) {
|
|
|
68843
70316
|
let acceptedTurnId;
|
|
68844
70317
|
let turnError = null;
|
|
68845
70318
|
let resolveTurnEnded = null;
|
|
68846
|
-
const turnEnded = new Promise((
|
|
68847
|
-
resolveTurnEnded =
|
|
70319
|
+
const turnEnded = new Promise((resolve2) => {
|
|
70320
|
+
resolveTurnEnded = resolve2;
|
|
68848
70321
|
});
|
|
68849
70322
|
const unsub = handle.onEvent(threadId, (event) => {
|
|
68850
70323
|
if (event.type === "assistant") chunks.push(event.content);
|
|
@@ -69063,9 +70536,10 @@ function createBridgeRequestListener(opts) {
|
|
|
69063
70536
|
res.end(JSON.stringify({ ok: false, error: "Origin not allowed." }));
|
|
69064
70537
|
return;
|
|
69065
70538
|
}
|
|
69066
|
-
const nonce =
|
|
70539
|
+
const nonce = import_node_crypto16.default.randomBytes(16).toString("base64url");
|
|
69067
70540
|
res.writeHead(200, { "content-type": "application/json" });
|
|
69068
|
-
|
|
70541
|
+
const publicHost = host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
70542
|
+
res.end(JSON.stringify({ ok: true, wsUrl: `ws://${publicHost}:${port}/ws`, token, nonce }));
|
|
69069
70543
|
return;
|
|
69070
70544
|
}
|
|
69071
70545
|
res.writeHead(404).end();
|
|
@@ -69366,7 +70840,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
69366
70840
|
}
|
|
69367
70841
|
}
|
|
69368
70842
|
socket.on("message", async (data) => {
|
|
69369
|
-
const spanId =
|
|
70843
|
+
const spanId = import_node_crypto16.default.randomUUID();
|
|
69370
70844
|
if (logCtx) {
|
|
69371
70845
|
logCtx.span.id = spanId;
|
|
69372
70846
|
logCtx.span.at = Date.now();
|
|
@@ -69970,8 +71444,10 @@ function createBridgeMessageHandler(deps) {
|
|
|
69970
71444
|
const desiredEffort = normalizeReasoningEffort(msg.reasoningEffort);
|
|
69971
71445
|
const resolvedProviderId = turnSeat.id;
|
|
69972
71446
|
let startedFreshThread = false;
|
|
71447
|
+
let desiredInstructionHash = null;
|
|
69973
71448
|
try {
|
|
69974
71449
|
const startParams = deps.buildStartParams(msg, desiredModel);
|
|
71450
|
+
desiredInstructionHash = import_node_crypto16.default.createHash("sha256").update(startParams.baseInstructions ?? "").digest("hex");
|
|
69975
71451
|
if (!thread) {
|
|
69976
71452
|
const newThread = {
|
|
69977
71453
|
threadId: "",
|
|
@@ -69982,7 +71458,8 @@ function createBridgeMessageHandler(deps) {
|
|
|
69982
71458
|
reasoningEffort: desiredEffort,
|
|
69983
71459
|
replay: null,
|
|
69984
71460
|
supersededReplay: null,
|
|
69985
|
-
provider: resolvedProviderId
|
|
71461
|
+
provider: resolvedProviderId,
|
|
71462
|
+
systemInstructionHash: null
|
|
69986
71463
|
};
|
|
69987
71464
|
const started = await codexClient.startThread(startParams);
|
|
69988
71465
|
newThread.threadId = started.threadId;
|
|
@@ -69993,17 +71470,27 @@ function createBridgeMessageHandler(deps) {
|
|
|
69993
71470
|
} else {
|
|
69994
71471
|
const existingThread = thread;
|
|
69995
71472
|
const providerMismatch = existingThread.provider !== null && existingThread.provider !== resolvedProviderId;
|
|
71473
|
+
const instructionMismatch = (resolvedProviderId === "agy" || resolvedProviderId === "claude") && (!existingThread.systemInstructionHash || existingThread.systemInstructionHash !== desiredInstructionHash);
|
|
69996
71474
|
let resumed = true;
|
|
69997
71475
|
let result;
|
|
69998
|
-
if (!existingThread.threadId || providerMismatch) {
|
|
71476
|
+
if (!existingThread.threadId || providerMismatch || instructionMismatch) {
|
|
71477
|
+
if (existingThread.threadId) {
|
|
71478
|
+
await codexClient.interruptTurn(existingThread.threadId).catch(() => {
|
|
71479
|
+
});
|
|
71480
|
+
}
|
|
69999
71481
|
resumed = false;
|
|
70000
71482
|
result = await codexClient.startThread(startParams);
|
|
71483
|
+
existingThread.systemInstructionHash = null;
|
|
70001
71484
|
} else {
|
|
70002
71485
|
try {
|
|
70003
|
-
result = await codexClient.resumeThread(existingThread.threadId,
|
|
71486
|
+
result = await codexClient.resumeThread(existingThread.threadId, {
|
|
71487
|
+
...startParams,
|
|
71488
|
+
alreadySeeded: true
|
|
71489
|
+
});
|
|
70004
71490
|
} catch {
|
|
70005
71491
|
resumed = false;
|
|
70006
71492
|
result = await codexClient.startThread(startParams);
|
|
71493
|
+
existingThread.systemInstructionHash = null;
|
|
70007
71494
|
}
|
|
70008
71495
|
}
|
|
70009
71496
|
existingThread.threadId = result.threadId;
|
|
@@ -70037,7 +71524,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
70037
71524
|
thread.inFlight = true;
|
|
70038
71525
|
pendingTurnSessions.delete(directorSessionId);
|
|
70039
71526
|
thread.replay = createReplayBuffer({
|
|
70040
|
-
turnId: (0,
|
|
71527
|
+
turnId: (0, import_node_crypto16.randomUUID)(),
|
|
70041
71528
|
spanId,
|
|
70042
71529
|
...deps.retentionMs !== void 0 ? { retentionMs: deps.retentionMs } : {},
|
|
70043
71530
|
...deps.ackLingerMs !== void 0 ? { ackLingerMs: deps.ackLingerMs } : {}
|
|
@@ -70137,10 +71624,13 @@ function createBridgeMessageHandler(deps) {
|
|
|
70137
71624
|
deps.releaseCodex(turnSeat.id);
|
|
70138
71625
|
return;
|
|
70139
71626
|
}
|
|
71627
|
+
const presentedLedger = presentLedger(thread.clarificationLedger ?? emptyLedger());
|
|
71628
|
+
thread.clarificationLedger = presentedLedger.ledger;
|
|
70140
71629
|
const contextPrefix = renderContextPrefix({
|
|
70141
71630
|
...msg.context,
|
|
70142
71631
|
project: projectContext,
|
|
70143
71632
|
answeredQuestions,
|
|
71633
|
+
openQuestions: presentedLedger.shown.length ? presentedLedger.shown : void 0,
|
|
70144
71634
|
directorSessionId
|
|
70145
71635
|
});
|
|
70146
71636
|
const sendInTurn = (msg2) => {
|
|
@@ -70187,15 +71677,38 @@ function createBridgeMessageHandler(deps) {
|
|
|
70187
71677
|
}
|
|
70188
71678
|
let resolveTurn = null;
|
|
70189
71679
|
let rejectTurn = null;
|
|
70190
|
-
const turnFinished = new Promise((
|
|
70191
|
-
resolveTurn =
|
|
71680
|
+
const turnFinished = new Promise((resolve2, reject) => {
|
|
71681
|
+
resolveTurn = resolve2;
|
|
70192
71682
|
rejectTurn = (error51) => reject(error51);
|
|
70193
71683
|
});
|
|
70194
71684
|
let outputEvents = 0;
|
|
70195
71685
|
let exhaustedLimitSignal = null;
|
|
70196
71686
|
let turnTokenUsage = null;
|
|
70197
71687
|
let promptBytes = 0;
|
|
71688
|
+
let contextBudgetGateTriggered = false;
|
|
70198
71689
|
const deferredResults = [];
|
|
71690
|
+
const closureEvents = [];
|
|
71691
|
+
let closureBefore = null;
|
|
71692
|
+
const takeClosureSnapshot = async () => {
|
|
71693
|
+
const snapshot = deps.snapshotProjectObjects;
|
|
71694
|
+
if (!snapshot || !contextProjectSlug) return null;
|
|
71695
|
+
const controller = new AbortController();
|
|
71696
|
+
let timer;
|
|
71697
|
+
try {
|
|
71698
|
+
timer = setTimeout(() => {
|
|
71699
|
+
controller.abort(new Error("snapshot timed out"));
|
|
71700
|
+
}, CLOSURE_SNAPSHOT_TIMEOUT_MS);
|
|
71701
|
+
return await snapshot(contextProjectSlug, { signal: controller.signal });
|
|
71702
|
+
} catch (error51) {
|
|
71703
|
+
process.stderr.write(
|
|
71704
|
+
`[mrrlin-mcp director-bridge] closure_snapshot_failed for ${contextProjectSlug}: ${error51 instanceof Error ? error51.message : String(error51)}
|
|
71705
|
+
`
|
|
71706
|
+
);
|
|
71707
|
+
return null;
|
|
71708
|
+
} finally {
|
|
71709
|
+
if (timer) clearTimeout(timer);
|
|
71710
|
+
}
|
|
71711
|
+
};
|
|
70199
71712
|
let pokeActivity = () => {
|
|
70200
71713
|
};
|
|
70201
71714
|
const forward = (event) => {
|
|
@@ -70207,11 +71720,24 @@ function createBridgeMessageHandler(deps) {
|
|
|
70207
71720
|
if (event.type === "token_usage") {
|
|
70208
71721
|
const usage = event.usage;
|
|
70209
71722
|
turnTokenUsage = addTurnTokenUsage(turnTokenUsage, usage);
|
|
71723
|
+
if (turnTokenUsage) {
|
|
71724
|
+
const totalTokens = (turnTokenUsage.inputTokens ?? 0) + (turnTokenUsage.outputTokens ?? 0);
|
|
71725
|
+
if (totalTokens > 0) {
|
|
71726
|
+
const maxTokens = 2e5;
|
|
71727
|
+
const remainingTokens = maxTokens - totalTokens;
|
|
71728
|
+
if (remainingTokens / maxTokens < 0.03 || remainingTokens < 4e3) {
|
|
71729
|
+
contextBudgetGateTriggered = true;
|
|
71730
|
+
}
|
|
71731
|
+
}
|
|
71732
|
+
}
|
|
70210
71733
|
return;
|
|
70211
71734
|
}
|
|
70212
71735
|
if (event.type === "turn_completed" || event.type === "rate_limits") {
|
|
70213
71736
|
return;
|
|
70214
71737
|
}
|
|
71738
|
+
if (event.type === "assistant" || event.type === "tool_result" || event.type === "tool_use") {
|
|
71739
|
+
closureEvents.push(event);
|
|
71740
|
+
}
|
|
70215
71741
|
outputEvents += 1;
|
|
70216
71742
|
sendInTurn({ type: "event", directorSessionId, event });
|
|
70217
71743
|
};
|
|
@@ -70220,7 +71746,21 @@ function createBridgeMessageHandler(deps) {
|
|
|
70220
71746
|
pokeActivity();
|
|
70221
71747
|
});
|
|
70222
71748
|
const currentTurnBrowserToolIds = /* @__PURE__ */ new Set();
|
|
71749
|
+
let hadErrorOnTurn = false;
|
|
71750
|
+
let confirmedProductiveEvent = false;
|
|
70223
71751
|
const unsubEvent = codexClient.onEvent(threadId, (event) => {
|
|
71752
|
+
if (event.type === "error") {
|
|
71753
|
+
hadErrorOnTurn = true;
|
|
71754
|
+
} else if (!confirmedProductiveEvent && !hadErrorOnTurn) {
|
|
71755
|
+
const isProductive = event.type === "assistant" && typeof event.content === "string" && event.content.trim().length > 0 || event.type === "tool_use" || event.type === "tool_result" || event.type === "result" && event.subtype === "success";
|
|
71756
|
+
if (isProductive) {
|
|
71757
|
+
confirmedProductiveEvent = true;
|
|
71758
|
+
if (desiredInstructionHash && thread.systemInstructionHash !== desiredInstructionHash) {
|
|
71759
|
+
thread.systemInstructionHash = desiredInstructionHash;
|
|
71760
|
+
deps.persistThreads();
|
|
71761
|
+
}
|
|
71762
|
+
}
|
|
71763
|
+
}
|
|
70224
71764
|
if (event.type === "tool_use") {
|
|
70225
71765
|
toolBusy.start(event.tool.id);
|
|
70226
71766
|
if (event.tool.name.startsWith("mcp__mrrlin-browser__browser_")) {
|
|
@@ -70354,6 +71894,7 @@ ${effectiveText}` : effectiveText;
|
|
|
70354
71894
|
turnInput.push({ type: "image", url: imageDataUri });
|
|
70355
71895
|
}
|
|
70356
71896
|
sendInTurn({ type: "turn-executing", directorSessionId, turnId: currentReplayBuf.turnId });
|
|
71897
|
+
closureBefore = await takeClosureSnapshot();
|
|
70357
71898
|
await codexClient.startTurn(thread.threadId, turnInput, thread.reasoningEffort ?? void 0);
|
|
70358
71899
|
await turnFinished;
|
|
70359
71900
|
})();
|
|
@@ -70371,7 +71912,47 @@ ${effectiveText}` : effectiveText;
|
|
|
70371
71912
|
error: zeroOutputLimit.message
|
|
70372
71913
|
});
|
|
70373
71914
|
}
|
|
70374
|
-
|
|
71915
|
+
if (!hadErrorOnTurn && outputEvents > 0) {
|
|
71916
|
+
const recorded = recordTurn(thread.clarificationLedger ?? emptyLedger(), finalAssistantText(closureEvents));
|
|
71917
|
+
thread.clarificationLedger = recorded.ledger;
|
|
71918
|
+
for (const item of recorded.reasked) {
|
|
71919
|
+
process.stderr.write(
|
|
71920
|
+
`[mrrlin-mcp director-bridge] clarification_reasked (x${item.reasked}) for ${directorSessionId}: ${item.question}
|
|
71921
|
+
`
|
|
71922
|
+
);
|
|
71923
|
+
}
|
|
71924
|
+
for (const question of recorded.overflow) {
|
|
71925
|
+
process.stderr.write(
|
|
71926
|
+
`[mrrlin-mcp director-bridge] clarification_overflow for ${directorSessionId}: ${question}
|
|
71927
|
+
`
|
|
71928
|
+
);
|
|
71929
|
+
}
|
|
71930
|
+
for (const item of recorded.dropped) {
|
|
71931
|
+
process.stderr.write(
|
|
71932
|
+
`[mrrlin-mcp director-bridge] clarification_dropped (${item.status}) for ${directorSessionId}: ${item.question}
|
|
71933
|
+
`
|
|
71934
|
+
);
|
|
71935
|
+
}
|
|
71936
|
+
deps.persistThreads();
|
|
71937
|
+
}
|
|
71938
|
+
if (closureBefore && !hadErrorOnTurn && outputEvents > 0) {
|
|
71939
|
+
const closureAfter = await takeClosureSnapshot();
|
|
71940
|
+
const closureText = closureAfter ? closureFor({ before: closureBefore, after: closureAfter, events: closureEvents, operatorText: msg.message ?? "" }) : null;
|
|
71941
|
+
if (closureText) {
|
|
71942
|
+
sendInTurn({ type: "event", directorSessionId, event: { type: "assistant", content: closureText } });
|
|
71943
|
+
}
|
|
71944
|
+
}
|
|
71945
|
+
if (contextBudgetGateTriggered) {
|
|
71946
|
+
sendInTurn({
|
|
71947
|
+
type: "event",
|
|
71948
|
+
directorSessionId,
|
|
71949
|
+
event: {
|
|
71950
|
+
type: "assistant",
|
|
71951
|
+
content: "\n\n\u26A0\uFE0F **Context Budget Gate**: Model context window is near capacity (<3% remaining). Further tool dispatch disabled to preserve conversational integrity and results."
|
|
71952
|
+
}
|
|
71953
|
+
});
|
|
71954
|
+
}
|
|
71955
|
+
const telemetry = turnTokenUsage !== null ? { promptBytes, usage: turnTokenUsage, contextBudgetGateTriggered } : void 0;
|
|
70375
71956
|
for (const event of deferredResults) {
|
|
70376
71957
|
sendInTurn({ type: "event", directorSessionId, event, telemetry });
|
|
70377
71958
|
}
|
|
@@ -70655,7 +72236,7 @@ async function startDirectorBridge() {
|
|
|
70655
72236
|
await tokenRefresher.start();
|
|
70656
72237
|
process.stderr.write("[mrrlin-mcp director-bridge] agent credential active; operator token auto-refresh enabled.\n");
|
|
70657
72238
|
} else {
|
|
70658
|
-
|
|
72239
|
+
let apiToken = envToken ?? readOperatorToken() ?? void 0;
|
|
70659
72240
|
if (envToken) {
|
|
70660
72241
|
try {
|
|
70661
72242
|
persistOperatorToken(envToken);
|
|
@@ -70726,6 +72307,7 @@ async function startDirectorBridge() {
|
|
|
70726
72307
|
acquireExecutorAgent: async (worktreePath, providerId) => {
|
|
70727
72308
|
const executor = await startExecutorServer({
|
|
70728
72309
|
worktree: worktreePath,
|
|
72310
|
+
disabledGitHooksDir: prepareDisabledGitHooksDir(import_node_path38.default.join(config2.stateDir, "locks"), import_node_path38.default.basename(worktreePath)),
|
|
70729
72311
|
provider: providerId
|
|
70730
72312
|
});
|
|
70731
72313
|
return { handle: executor.handle, dispose: executor.dispose };
|
|
@@ -70764,7 +72346,7 @@ async function startDirectorBridge() {
|
|
|
70764
72346
|
};
|
|
70765
72347
|
const sessionSinks = createSessionSinkRegistry();
|
|
70766
72348
|
const tombstones = /* @__PURE__ */ new Map();
|
|
70767
|
-
const bridgeInstanceId = (0,
|
|
72349
|
+
const bridgeInstanceId = (0, import_node_crypto16.randomUUID)();
|
|
70768
72350
|
const asyncConsumerDeps = {
|
|
70769
72351
|
client,
|
|
70770
72352
|
run: wrappedTurnLock.run,
|
|
@@ -70858,7 +72440,9 @@ async function startDirectorBridge() {
|
|
|
70858
72440
|
reasoningEffort: normalizeReasoningEffort(entry.reasoningEffort),
|
|
70859
72441
|
replay: null,
|
|
70860
72442
|
supersededReplay: null,
|
|
70861
|
-
provider: entry.provider
|
|
72443
|
+
provider: entry.provider,
|
|
72444
|
+
systemInstructionHash: entry.systemInstructionHash ?? null,
|
|
72445
|
+
...entry.clarificationLedger ? { clarificationLedger: entry.clarificationLedger } : {}
|
|
70862
72446
|
});
|
|
70863
72447
|
}
|
|
70864
72448
|
const persistThreads = () => {
|
|
@@ -70870,7 +72454,9 @@ async function startDirectorBridge() {
|
|
|
70870
72454
|
lastUsedAt: thread.lastUsedAt,
|
|
70871
72455
|
model: thread.model,
|
|
70872
72456
|
reasoningEffort: thread.reasoningEffort,
|
|
70873
|
-
provider: thread.provider
|
|
72457
|
+
provider: thread.provider,
|
|
72458
|
+
systemInstructionHash: thread.systemInstructionHash ?? null,
|
|
72459
|
+
...thread.clarificationLedger ? { clarificationLedger: thread.clarificationLedger } : {}
|
|
70874
72460
|
}
|
|
70875
72461
|
])
|
|
70876
72462
|
);
|
|
@@ -71037,6 +72623,35 @@ async function startDirectorBridge() {
|
|
|
71037
72623
|
}
|
|
71038
72624
|
},
|
|
71039
72625
|
listAnsweredQuestions: (projectSlug, taskId) => client.listInboxItems(projectSlug, { status: "acknowledged", kind: "question", taskId }),
|
|
72626
|
+
snapshotProjectObjects: async (projectSlug, opts) => {
|
|
72627
|
+
const signal = opts?.signal;
|
|
72628
|
+
if (signal?.aborted) throw new Error("snapshot aborted");
|
|
72629
|
+
const tasks = await client.listTasks(
|
|
72630
|
+
projectSlug,
|
|
72631
|
+
{ archived: "all", limit: CLOSURE_MAX_SNAPSHOT_TASKS + 1 },
|
|
72632
|
+
{ view: "summary", signal }
|
|
72633
|
+
);
|
|
72634
|
+
if (tasks.length > CLOSURE_MAX_SNAPSHOT_TASKS) {
|
|
72635
|
+
throw new Error(`${tasks.length} tasks exceed the closure snapshot cap (${CLOSURE_MAX_SNAPSHOT_TASKS})`);
|
|
72636
|
+
}
|
|
72637
|
+
const [pages, inbox] = await Promise.all([
|
|
72638
|
+
client.listWikiPages(
|
|
72639
|
+
projectSlug,
|
|
72640
|
+
{ folder: "Specs", limit: CLOSURE_MAX_SNAPSHOT_PAGES + 1 },
|
|
72641
|
+
{ view: "summary", signal }
|
|
72642
|
+
),
|
|
72643
|
+
client.listInboxItems(
|
|
72644
|
+
projectSlug,
|
|
72645
|
+
{ status: "pending", limit: CLOSURE_MAX_SNAPSHOT_INBOX + 1 },
|
|
72646
|
+
{ view: "summary", signal }
|
|
72647
|
+
)
|
|
72648
|
+
]);
|
|
72649
|
+
return {
|
|
72650
|
+
inbox: inbox.length > CLOSURE_MAX_SNAPSHOT_INBOX ? null : new Set(inbox.map((item) => item.id)),
|
|
72651
|
+
tasks: new Map(tasks.map((task) => [task.id, { title: task.title, updatedAt: task.updatedAt ?? null }])),
|
|
72652
|
+
pages: pages.length > CLOSURE_MAX_SNAPSHOT_PAGES ? null : new Map(pages.map((page) => [page.id, { title: page.title, updatedAt: page.updatedAt }]))
|
|
72653
|
+
};
|
|
72654
|
+
},
|
|
71040
72655
|
getProject: (projectSlug) => client.getProject(projectSlug),
|
|
71041
72656
|
// External-agents control plane: the real spawn runner, with the EXTERNAL_AGENTS_CLI operator
|
|
71042
72657
|
// escape hatch bound here (the handlers stay spawn-free and this is the only production seam).
|
|
@@ -71058,7 +72673,7 @@ async function startDirectorBridge() {
|
|
|
71058
72673
|
const bridgeLogger = createBridgeLogger(config2.stateDir, process.env);
|
|
71059
72674
|
let bridgeBinShaShort = "unknown00000";
|
|
71060
72675
|
try {
|
|
71061
|
-
bridgeBinShaShort =
|
|
72676
|
+
bridgeBinShaShort = import_node_crypto16.default.createHash("sha256").update(import_node_fs23.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
|
|
71062
72677
|
} catch {
|
|
71063
72678
|
}
|
|
71064
72679
|
attachBridgeWss(server, {
|
|
@@ -71149,9 +72764,9 @@ async function startDirectorBridge() {
|
|
|
71149
72764
|
res.writeHead(400).end();
|
|
71150
72765
|
});
|
|
71151
72766
|
});
|
|
71152
|
-
await new Promise((
|
|
72767
|
+
await new Promise((resolve2, reject) => {
|
|
71153
72768
|
dispatchServer.on("error", reject);
|
|
71154
|
-
dispatchServer.listen(workerPort(), "127.0.0.1", () =>
|
|
72769
|
+
dispatchServer.listen(workerPort(), "127.0.0.1", () => resolve2());
|
|
71155
72770
|
});
|
|
71156
72771
|
const bridgeHealth = new BridgeHealthTracker({
|
|
71157
72772
|
stateDir: config2.stateDir,
|
|
@@ -71342,7 +72957,7 @@ async function startDirectorBridge() {
|
|
|
71342
72957
|
)
|
|
71343
72958
|
});
|
|
71344
72959
|
}
|
|
71345
|
-
await new Promise((
|
|
72960
|
+
await new Promise((resolve2, reject) => {
|
|
71346
72961
|
server.on("error", (err) => {
|
|
71347
72962
|
if (err.code === "EADDRINUSE") {
|
|
71348
72963
|
const conflictingPids = scanForOtherBridges();
|
|
@@ -71358,7 +72973,7 @@ async function startDirectorBridge() {
|
|
|
71358
72973
|
}
|
|
71359
72974
|
reject(err);
|
|
71360
72975
|
});
|
|
71361
|
-
server.listen(config2.port, config2.host, () =>
|
|
72976
|
+
server.listen(config2.port, config2.host, () => resolve2());
|
|
71362
72977
|
});
|
|
71363
72978
|
const orphans = scanForOtherBridges();
|
|
71364
72979
|
if (orphans.length > 0) {
|
|
@@ -71499,10 +73114,10 @@ function timingSafeTokenEqual(provided, expected) {
|
|
|
71499
73114
|
const a = Buffer.from(provided);
|
|
71500
73115
|
const b = Buffer.from(expected);
|
|
71501
73116
|
if (a.length !== b.length) return false;
|
|
71502
|
-
return
|
|
73117
|
+
return import_node_crypto16.default.timingSafeEqual(a, b);
|
|
71503
73118
|
}
|
|
71504
73119
|
function readDispatchBody(req) {
|
|
71505
|
-
return new Promise((
|
|
73120
|
+
return new Promise((resolve2, reject) => {
|
|
71506
73121
|
const chunks = [];
|
|
71507
73122
|
let size = 0;
|
|
71508
73123
|
req.on("data", (chunk) => {
|
|
@@ -71514,7 +73129,7 @@ function readDispatchBody(req) {
|
|
|
71514
73129
|
}
|
|
71515
73130
|
chunks.push(chunk);
|
|
71516
73131
|
});
|
|
71517
|
-
req.on("end", () =>
|
|
73132
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
|
|
71518
73133
|
req.on("error", reject);
|
|
71519
73134
|
});
|
|
71520
73135
|
}
|
|
@@ -71528,7 +73143,7 @@ function readOrCreateBridgeToken(stateDir) {
|
|
|
71528
73143
|
} catch {
|
|
71529
73144
|
}
|
|
71530
73145
|
import_node_fs23.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
71531
|
-
const token =
|
|
73146
|
+
const token = import_node_crypto16.default.randomBytes(32).toString("base64url");
|
|
71532
73147
|
import_node_fs23.default.writeFileSync(tokenPath, `${token}
|
|
71533
73148
|
`, { mode: 384 });
|
|
71534
73149
|
return token;
|
|
@@ -71571,10 +73186,17 @@ function renderContextPrefix(context) {
|
|
|
71571
73186
|
if (directorSessionId) payload.directorSessionId = directorSessionId;
|
|
71572
73187
|
if (contextMessages?.length) payload.contextMessages = contextMessages;
|
|
71573
73188
|
if (answeredQuestions) payload.answeredQuestions = answeredQuestions;
|
|
73189
|
+
const openQuestions = context?.openQuestions?.length ? context.openQuestions : void 0;
|
|
73190
|
+
if (openQuestions) payload.openQuestions = openQuestions;
|
|
71574
73191
|
const keys = Object.keys(payload);
|
|
71575
73192
|
if (keys.length > 0) {
|
|
71576
73193
|
parts.push(`Context (JSON): ${JSON.stringify(payload)}`);
|
|
71577
73194
|
}
|
|
73195
|
+
if (openQuestions) {
|
|
73196
|
+
parts.push(
|
|
73197
|
+
"[OPEN QUESTIONS] `openQuestions` lists questions you asked earlier in this chat. For each one, state in this reply whether it is answered (by the operator), assumed (say the default you took) or a blocker (say what it blocks). Do not ask it again unless the answer would change the architecture or the deliverable."
|
|
73198
|
+
);
|
|
73199
|
+
}
|
|
71578
73200
|
return parts.join("\n\n");
|
|
71579
73201
|
}
|
|
71580
73202
|
|
|
@@ -72219,12 +73841,12 @@ var StdioServerTransport = class {
|
|
|
72219
73841
|
this.onclose?.();
|
|
72220
73842
|
}
|
|
72221
73843
|
send(message) {
|
|
72222
|
-
return new Promise((
|
|
73844
|
+
return new Promise((resolve2) => {
|
|
72223
73845
|
const json2 = serializeMessage(message);
|
|
72224
73846
|
if (this._stdout.write(json2)) {
|
|
72225
|
-
|
|
73847
|
+
resolve2();
|
|
72226
73848
|
} else {
|
|
72227
|
-
this._stdout.once("drain",
|
|
73849
|
+
this._stdout.once("drain", resolve2);
|
|
72228
73850
|
}
|
|
72229
73851
|
});
|
|
72230
73852
|
}
|
|
@@ -72299,7 +73921,9 @@ function jsonSchemaProperty(schema) {
|
|
|
72299
73921
|
property.enum = jsonSchema.enum;
|
|
72300
73922
|
}
|
|
72301
73923
|
if (typeof jsonSchema.pattern === "string") {
|
|
72302
|
-
|
|
73924
|
+
if (!/\(\?<?[!=]/.test(jsonSchema.pattern)) {
|
|
73925
|
+
property.pattern = jsonSchema.pattern;
|
|
73926
|
+
}
|
|
72303
73927
|
}
|
|
72304
73928
|
return property;
|
|
72305
73929
|
}
|
|
@@ -72909,7 +74533,8 @@ function installService(deps) {
|
|
|
72909
74533
|
return { ok: false };
|
|
72910
74534
|
}
|
|
72911
74535
|
const claudeBin = deps.which("claude");
|
|
72912
|
-
const
|
|
74536
|
+
const agyBin = deps.which("agy");
|
|
74537
|
+
const resolvedBins = [...bins, claudeBin, agyBin].filter((b) => b !== null);
|
|
72913
74538
|
const isWin = import_node_os18.default.platform() === "win32";
|
|
72914
74539
|
const systemDirs = isWin ? (deps.env.PATH || "").split(import_node_path39.default.delimiter).filter(Boolean) : SYSTEM_PATH_DIRS;
|
|
72915
74540
|
const pathEnv = Array.from(
|
|
@@ -73184,6 +74809,24 @@ Missing required flag(s): ${missing.join(
|
|
|
73184
74809
|
}
|
|
73185
74810
|
|
|
73186
74811
|
// src/bin.ts
|
|
74812
|
+
function augmentProcessPathWithCommonDirs() {
|
|
74813
|
+
const isWin = process.platform === "win32";
|
|
74814
|
+
if (isWin) return;
|
|
74815
|
+
const home = process.env.HOME || (0, import_node_os20.homedir)();
|
|
74816
|
+
const candidates = [
|
|
74817
|
+
import_node_path43.default.join(home, ".local", "bin"),
|
|
74818
|
+
import_node_path43.default.join(home, ".cargo", "bin"),
|
|
74819
|
+
"/usr/local/bin",
|
|
74820
|
+
"/opt/homebrew/bin"
|
|
74821
|
+
];
|
|
74822
|
+
const existing = (process.env.PATH || "").split(import_node_path43.default.delimiter).filter(Boolean);
|
|
74823
|
+
const existingSet = new Set(existing);
|
|
74824
|
+
const toAdd = candidates.filter((d) => !existingSet.has(d) && (0, import_node_fs25.existsSync)(d));
|
|
74825
|
+
if (toAdd.length > 0) {
|
|
74826
|
+
process.env.PATH = [...existing, ...toAdd].join(import_node_path43.default.delimiter);
|
|
74827
|
+
}
|
|
74828
|
+
}
|
|
74829
|
+
augmentProcessPathWithCommonDirs();
|
|
73187
74830
|
function pm2Runner(args) {
|
|
73188
74831
|
const r = (0, import_node_child_process22.spawnSync)("pm2", args, { encoding: "utf8" });
|
|
73189
74832
|
process.stderr.write(r.stdout ?? "");
|
|
@@ -73690,6 +75333,10 @@ main().catch((error51) => {
|
|
|
73690
75333
|
`);
|
|
73691
75334
|
process.exit(1);
|
|
73692
75335
|
});
|
|
75336
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
75337
|
+
0 && (module.exports = {
|
|
75338
|
+
augmentProcessPathWithCommonDirs
|
|
75339
|
+
});
|
|
73693
75340
|
/*! Bundled license information:
|
|
73694
75341
|
|
|
73695
75342
|
content-type/index.js:
|