pullfrog 0.0.200 → 0.0.202
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/agents/index.d.ts +1 -1
- package/dist/agents/opencode.d.ts +1 -0
- package/dist/agents/shared.d.ts +65 -1
- package/dist/cli.mjs +2093 -888
- package/dist/external.d.ts +1 -1
- package/dist/index.js +2078 -878
- package/dist/internal.js +10 -8
- package/dist/lifecycle.d.ts +1 -1
- package/dist/mcp/checkout.d.ts +16 -2
- package/dist/mcp/comment.d.ts +1 -0
- package/dist/mcp/geminiSanitizer.d.ts +17 -0
- package/dist/mcp/git.d.ts +8 -2
- package/dist/mcp/review.d.ts +104 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/shared.d.ts +1 -1
- package/dist/modes.d.ts +1 -1
- package/dist/utils/activity.d.ts +4 -0
- package/dist/utils/agent.d.ts +3 -1
- package/dist/utils/diffCoverage.d.ts +62 -0
- package/dist/utils/lifecycle.d.ts +14 -2
- package/dist/utils/log.d.ts +13 -2
- package/dist/utils/patchWorkflowRunFields.d.ts +27 -4
- package/dist/utils/runContext.d.ts +1 -0
- package/dist/utils/secrets.d.ts +9 -2
- package/dist/utils/setup.d.ts +13 -0
- package/dist/utils/subprocess.d.ts +7 -0
- package/dist/utils/time.d.ts +1 -0
- package/dist/utils/todoTracking.d.ts +3 -1
- package/package.json +3 -2
- package/dist/agents/opentoad.d.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -4076,8 +4076,8 @@ var require_util2 = __commonJS({
|
|
|
4076
4076
|
function createDeferredPromise() {
|
|
4077
4077
|
let res;
|
|
4078
4078
|
let rej;
|
|
4079
|
-
const promise2 = new Promise((
|
|
4080
|
-
res =
|
|
4079
|
+
const promise2 = new Promise((resolve3, reject) => {
|
|
4080
|
+
res = resolve3;
|
|
4081
4081
|
rej = reject;
|
|
4082
4082
|
});
|
|
4083
4083
|
return { promise: promise2, resolve: res, reject: rej };
|
|
@@ -5581,8 +5581,8 @@ Content-Type: ${value2.type || "application/octet-stream"}\r
|
|
|
5581
5581
|
});
|
|
5582
5582
|
}
|
|
5583
5583
|
});
|
|
5584
|
-
const busboyResolve = new Promise((
|
|
5585
|
-
busboy.on("finish",
|
|
5584
|
+
const busboyResolve = new Promise((resolve3, reject) => {
|
|
5585
|
+
busboy.on("finish", resolve3);
|
|
5586
5586
|
busboy.on("error", (err) => reject(new TypeError(err)));
|
|
5587
5587
|
});
|
|
5588
5588
|
if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
|
|
@@ -6116,9 +6116,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
6116
6116
|
}
|
|
6117
6117
|
close(callback) {
|
|
6118
6118
|
if (callback === void 0) {
|
|
6119
|
-
return new Promise((
|
|
6119
|
+
return new Promise((resolve3, reject) => {
|
|
6120
6120
|
this.close((err, data) => {
|
|
6121
|
-
return err ? reject(err) :
|
|
6121
|
+
return err ? reject(err) : resolve3(data);
|
|
6122
6122
|
});
|
|
6123
6123
|
});
|
|
6124
6124
|
}
|
|
@@ -6156,12 +6156,12 @@ var require_dispatcher_base = __commonJS({
|
|
|
6156
6156
|
err = null;
|
|
6157
6157
|
}
|
|
6158
6158
|
if (callback === void 0) {
|
|
6159
|
-
return new Promise((
|
|
6159
|
+
return new Promise((resolve3, reject) => {
|
|
6160
6160
|
this.destroy(err, (err2, data) => {
|
|
6161
6161
|
return err2 ? (
|
|
6162
6162
|
/* istanbul ignore next: should never error */
|
|
6163
6163
|
reject(err2)
|
|
6164
|
-
) :
|
|
6164
|
+
) : resolve3(data);
|
|
6165
6165
|
});
|
|
6166
6166
|
});
|
|
6167
6167
|
}
|
|
@@ -7221,16 +7221,16 @@ var require_client = __commonJS({
|
|
|
7221
7221
|
return this[kNeedDrain] < 2;
|
|
7222
7222
|
}
|
|
7223
7223
|
async [kClose]() {
|
|
7224
|
-
return new Promise((
|
|
7224
|
+
return new Promise((resolve3) => {
|
|
7225
7225
|
if (!this[kSize]) {
|
|
7226
|
-
|
|
7226
|
+
resolve3(null);
|
|
7227
7227
|
} else {
|
|
7228
|
-
this[kClosedResolve] =
|
|
7228
|
+
this[kClosedResolve] = resolve3;
|
|
7229
7229
|
}
|
|
7230
7230
|
});
|
|
7231
7231
|
}
|
|
7232
7232
|
async [kDestroy](err) {
|
|
7233
|
-
return new Promise((
|
|
7233
|
+
return new Promise((resolve3) => {
|
|
7234
7234
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
7235
7235
|
for (let i = 0; i < requests.length; i++) {
|
|
7236
7236
|
const request2 = requests[i];
|
|
@@ -7241,7 +7241,7 @@ var require_client = __commonJS({
|
|
|
7241
7241
|
this[kClosedResolve]();
|
|
7242
7242
|
this[kClosedResolve] = null;
|
|
7243
7243
|
}
|
|
7244
|
-
|
|
7244
|
+
resolve3();
|
|
7245
7245
|
};
|
|
7246
7246
|
if (this[kHTTP2Session] != null) {
|
|
7247
7247
|
util2.destroy(this[kHTTP2Session], err);
|
|
@@ -7821,7 +7821,7 @@ var require_client = __commonJS({
|
|
|
7821
7821
|
});
|
|
7822
7822
|
}
|
|
7823
7823
|
try {
|
|
7824
|
-
const socket = await new Promise((
|
|
7824
|
+
const socket = await new Promise((resolve3, reject) => {
|
|
7825
7825
|
client[kConnector]({
|
|
7826
7826
|
host,
|
|
7827
7827
|
hostname: hostname4,
|
|
@@ -7833,7 +7833,7 @@ var require_client = __commonJS({
|
|
|
7833
7833
|
if (err) {
|
|
7834
7834
|
reject(err);
|
|
7835
7835
|
} else {
|
|
7836
|
-
|
|
7836
|
+
resolve3(socket2);
|
|
7837
7837
|
}
|
|
7838
7838
|
});
|
|
7839
7839
|
});
|
|
@@ -8457,12 +8457,12 @@ upgrade: ${upgrade}\r
|
|
|
8457
8457
|
cb();
|
|
8458
8458
|
}
|
|
8459
8459
|
}
|
|
8460
|
-
const waitForDrain = () => new Promise((
|
|
8460
|
+
const waitForDrain = () => new Promise((resolve3, reject) => {
|
|
8461
8461
|
assert3(callback === null);
|
|
8462
8462
|
if (socket[kError]) {
|
|
8463
8463
|
reject(socket[kError]);
|
|
8464
8464
|
} else {
|
|
8465
|
-
callback =
|
|
8465
|
+
callback = resolve3;
|
|
8466
8466
|
}
|
|
8467
8467
|
});
|
|
8468
8468
|
if (client[kHTTPConnVersion] === "h2") {
|
|
@@ -8807,8 +8807,8 @@ var require_pool_base = __commonJS({
|
|
|
8807
8807
|
if (this[kQueue].isEmpty()) {
|
|
8808
8808
|
return Promise.all(this[kClients].map((c) => c.close()));
|
|
8809
8809
|
} else {
|
|
8810
|
-
return new Promise((
|
|
8811
|
-
this[kClosedResolve] =
|
|
8810
|
+
return new Promise((resolve3) => {
|
|
8811
|
+
this[kClosedResolve] = resolve3;
|
|
8812
8812
|
});
|
|
8813
8813
|
}
|
|
8814
8814
|
}
|
|
@@ -9386,7 +9386,7 @@ var require_readable = __commonJS({
|
|
|
9386
9386
|
if (this.closed) {
|
|
9387
9387
|
return Promise.resolve(null);
|
|
9388
9388
|
}
|
|
9389
|
-
return new Promise((
|
|
9389
|
+
return new Promise((resolve3, reject) => {
|
|
9390
9390
|
const signalListenerCleanup = signal ? util2.addAbortListener(signal, () => {
|
|
9391
9391
|
this.destroy();
|
|
9392
9392
|
}) : noop4;
|
|
@@ -9395,7 +9395,7 @@ var require_readable = __commonJS({
|
|
|
9395
9395
|
if (signal && signal.aborted) {
|
|
9396
9396
|
reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
|
|
9397
9397
|
} else {
|
|
9398
|
-
|
|
9398
|
+
resolve3(null);
|
|
9399
9399
|
}
|
|
9400
9400
|
}).on("error", noop4).on("data", function(chunk) {
|
|
9401
9401
|
limit -= chunk.length;
|
|
@@ -9417,11 +9417,11 @@ var require_readable = __commonJS({
|
|
|
9417
9417
|
throw new TypeError("unusable");
|
|
9418
9418
|
}
|
|
9419
9419
|
assert3(!stream[kConsume]);
|
|
9420
|
-
return new Promise((
|
|
9420
|
+
return new Promise((resolve3, reject) => {
|
|
9421
9421
|
stream[kConsume] = {
|
|
9422
9422
|
type: type2,
|
|
9423
9423
|
stream,
|
|
9424
|
-
resolve:
|
|
9424
|
+
resolve: resolve3,
|
|
9425
9425
|
reject,
|
|
9426
9426
|
length: 0,
|
|
9427
9427
|
body: []
|
|
@@ -9456,12 +9456,12 @@ var require_readable = __commonJS({
|
|
|
9456
9456
|
}
|
|
9457
9457
|
}
|
|
9458
9458
|
function consumeEnd(consume2) {
|
|
9459
|
-
const { type: type2, body, resolve:
|
|
9459
|
+
const { type: type2, body, resolve: resolve3, stream, length } = consume2;
|
|
9460
9460
|
try {
|
|
9461
9461
|
if (type2 === "text") {
|
|
9462
|
-
|
|
9462
|
+
resolve3(toUSVString(Buffer.concat(body)));
|
|
9463
9463
|
} else if (type2 === "json") {
|
|
9464
|
-
|
|
9464
|
+
resolve3(JSON.parse(Buffer.concat(body)));
|
|
9465
9465
|
} else if (type2 === "arrayBuffer") {
|
|
9466
9466
|
const dst = new Uint8Array(length);
|
|
9467
9467
|
let pos = 0;
|
|
@@ -9469,12 +9469,12 @@ var require_readable = __commonJS({
|
|
|
9469
9469
|
dst.set(buf, pos);
|
|
9470
9470
|
pos += buf.byteLength;
|
|
9471
9471
|
}
|
|
9472
|
-
|
|
9472
|
+
resolve3(dst.buffer);
|
|
9473
9473
|
} else if (type2 === "blob") {
|
|
9474
9474
|
if (!Blob2) {
|
|
9475
9475
|
Blob2 = __require("buffer").Blob;
|
|
9476
9476
|
}
|
|
9477
|
-
|
|
9477
|
+
resolve3(new Blob2(body, { type: stream[kContentType] }));
|
|
9478
9478
|
}
|
|
9479
9479
|
consumeFinish(consume2);
|
|
9480
9480
|
} catch (err) {
|
|
@@ -9729,9 +9729,9 @@ var require_api_request = __commonJS({
|
|
|
9729
9729
|
};
|
|
9730
9730
|
function request2(opts, callback) {
|
|
9731
9731
|
if (callback === void 0) {
|
|
9732
|
-
return new Promise((
|
|
9732
|
+
return new Promise((resolve3, reject) => {
|
|
9733
9733
|
request2.call(this, opts, (err, data) => {
|
|
9734
|
-
return err ? reject(err) :
|
|
9734
|
+
return err ? reject(err) : resolve3(data);
|
|
9735
9735
|
});
|
|
9736
9736
|
});
|
|
9737
9737
|
}
|
|
@@ -9904,9 +9904,9 @@ var require_api_stream = __commonJS({
|
|
|
9904
9904
|
};
|
|
9905
9905
|
function stream(opts, factory, callback) {
|
|
9906
9906
|
if (callback === void 0) {
|
|
9907
|
-
return new Promise((
|
|
9907
|
+
return new Promise((resolve3, reject) => {
|
|
9908
9908
|
stream.call(this, opts, factory, (err, data) => {
|
|
9909
|
-
return err ? reject(err) :
|
|
9909
|
+
return err ? reject(err) : resolve3(data);
|
|
9910
9910
|
});
|
|
9911
9911
|
});
|
|
9912
9912
|
}
|
|
@@ -10187,9 +10187,9 @@ var require_api_upgrade = __commonJS({
|
|
|
10187
10187
|
};
|
|
10188
10188
|
function upgrade(opts, callback) {
|
|
10189
10189
|
if (callback === void 0) {
|
|
10190
|
-
return new Promise((
|
|
10190
|
+
return new Promise((resolve3, reject) => {
|
|
10191
10191
|
upgrade.call(this, opts, (err, data) => {
|
|
10192
|
-
return err ? reject(err) :
|
|
10192
|
+
return err ? reject(err) : resolve3(data);
|
|
10193
10193
|
});
|
|
10194
10194
|
});
|
|
10195
10195
|
}
|
|
@@ -10278,9 +10278,9 @@ var require_api_connect = __commonJS({
|
|
|
10278
10278
|
};
|
|
10279
10279
|
function connect(opts, callback) {
|
|
10280
10280
|
if (callback === void 0) {
|
|
10281
|
-
return new Promise((
|
|
10281
|
+
return new Promise((resolve3, reject) => {
|
|
10282
10282
|
connect.call(this, opts, (err, data) => {
|
|
10283
|
-
return err ? reject(err) :
|
|
10283
|
+
return err ? reject(err) : resolve3(data);
|
|
10284
10284
|
});
|
|
10285
10285
|
});
|
|
10286
10286
|
}
|
|
@@ -13902,7 +13902,7 @@ var require_fetch = __commonJS({
|
|
|
13902
13902
|
async function dispatch({ body }) {
|
|
13903
13903
|
const url4 = requestCurrentURL(request2);
|
|
13904
13904
|
const agent2 = fetchParams.controller.dispatcher;
|
|
13905
|
-
return new Promise((
|
|
13905
|
+
return new Promise((resolve3, reject) => agent2.dispatch(
|
|
13906
13906
|
{
|
|
13907
13907
|
path: url4.pathname + url4.search,
|
|
13908
13908
|
origin: url4.origin,
|
|
@@ -13978,7 +13978,7 @@ var require_fetch = __commonJS({
|
|
|
13978
13978
|
}
|
|
13979
13979
|
}
|
|
13980
13980
|
}
|
|
13981
|
-
|
|
13981
|
+
resolve3({
|
|
13982
13982
|
status,
|
|
13983
13983
|
statusText,
|
|
13984
13984
|
headersList: headers[kHeadersList],
|
|
@@ -14021,7 +14021,7 @@ var require_fetch = __commonJS({
|
|
|
14021
14021
|
const val = headersList[n + 1].toString("latin1");
|
|
14022
14022
|
headers[kHeadersList].append(key, val);
|
|
14023
14023
|
}
|
|
14024
|
-
|
|
14024
|
+
resolve3({
|
|
14025
14025
|
status,
|
|
14026
14026
|
statusText: STATUS_CODES[status],
|
|
14027
14027
|
headersList: headers[kHeadersList],
|
|
@@ -17375,11 +17375,11 @@ var require_lib = __commonJS({
|
|
|
17375
17375
|
};
|
|
17376
17376
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
17377
17377
|
function adopt(value2) {
|
|
17378
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
17379
|
-
|
|
17378
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
17379
|
+
resolve3(value2);
|
|
17380
17380
|
});
|
|
17381
17381
|
}
|
|
17382
|
-
return new (P || (P = Promise))(function(
|
|
17382
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
17383
17383
|
function fulfilled(value2) {
|
|
17384
17384
|
try {
|
|
17385
17385
|
step(generator.next(value2));
|
|
@@ -17395,7 +17395,7 @@ var require_lib = __commonJS({
|
|
|
17395
17395
|
}
|
|
17396
17396
|
}
|
|
17397
17397
|
function step(result) {
|
|
17398
|
-
result.done ?
|
|
17398
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
17399
17399
|
}
|
|
17400
17400
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17401
17401
|
});
|
|
@@ -17481,26 +17481,26 @@ var require_lib = __commonJS({
|
|
|
17481
17481
|
}
|
|
17482
17482
|
readBody() {
|
|
17483
17483
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17484
|
-
return new Promise((
|
|
17484
|
+
return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
|
|
17485
17485
|
let output = Buffer.alloc(0);
|
|
17486
17486
|
this.message.on("data", (chunk) => {
|
|
17487
17487
|
output = Buffer.concat([output, chunk]);
|
|
17488
17488
|
});
|
|
17489
17489
|
this.message.on("end", () => {
|
|
17490
|
-
|
|
17490
|
+
resolve3(output.toString());
|
|
17491
17491
|
});
|
|
17492
17492
|
}));
|
|
17493
17493
|
});
|
|
17494
17494
|
}
|
|
17495
17495
|
readBodyBuffer() {
|
|
17496
17496
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17497
|
-
return new Promise((
|
|
17497
|
+
return new Promise((resolve3) => __awaiter(this, void 0, void 0, function* () {
|
|
17498
17498
|
const chunks = [];
|
|
17499
17499
|
this.message.on("data", (chunk) => {
|
|
17500
17500
|
chunks.push(chunk);
|
|
17501
17501
|
});
|
|
17502
17502
|
this.message.on("end", () => {
|
|
17503
|
-
|
|
17503
|
+
resolve3(Buffer.concat(chunks));
|
|
17504
17504
|
});
|
|
17505
17505
|
}));
|
|
17506
17506
|
});
|
|
@@ -17709,14 +17709,14 @@ var require_lib = __commonJS({
|
|
|
17709
17709
|
*/
|
|
17710
17710
|
requestRaw(info2, data) {
|
|
17711
17711
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17712
|
-
return new Promise((
|
|
17712
|
+
return new Promise((resolve3, reject) => {
|
|
17713
17713
|
function callbackForResult(err, res) {
|
|
17714
17714
|
if (err) {
|
|
17715
17715
|
reject(err);
|
|
17716
17716
|
} else if (!res) {
|
|
17717
17717
|
reject(new Error("Unknown error"));
|
|
17718
17718
|
} else {
|
|
17719
|
-
|
|
17719
|
+
resolve3(res);
|
|
17720
17720
|
}
|
|
17721
17721
|
}
|
|
17722
17722
|
this.requestRawWithCallback(info2, data, callbackForResult);
|
|
@@ -17898,12 +17898,12 @@ var require_lib = __commonJS({
|
|
|
17898
17898
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17899
17899
|
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
|
|
17900
17900
|
const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
|
|
17901
|
-
return new Promise((
|
|
17901
|
+
return new Promise((resolve3) => setTimeout(() => resolve3(), ms));
|
|
17902
17902
|
});
|
|
17903
17903
|
}
|
|
17904
17904
|
_processResponse(res, options) {
|
|
17905
17905
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17906
|
-
return new Promise((
|
|
17906
|
+
return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
17907
17907
|
const statusCode = res.message.statusCode || 0;
|
|
17908
17908
|
const response = {
|
|
17909
17909
|
statusCode,
|
|
@@ -17911,7 +17911,7 @@ var require_lib = __commonJS({
|
|
|
17911
17911
|
headers: {}
|
|
17912
17912
|
};
|
|
17913
17913
|
if (statusCode === HttpCodes.NotFound) {
|
|
17914
|
-
|
|
17914
|
+
resolve3(response);
|
|
17915
17915
|
}
|
|
17916
17916
|
function dateTimeDeserializer(key, value2) {
|
|
17917
17917
|
if (typeof value2 === "string") {
|
|
@@ -17950,7 +17950,7 @@ var require_lib = __commonJS({
|
|
|
17950
17950
|
err.result = response.result;
|
|
17951
17951
|
reject(err);
|
|
17952
17952
|
} else {
|
|
17953
|
-
|
|
17953
|
+
resolve3(response);
|
|
17954
17954
|
}
|
|
17955
17955
|
}));
|
|
17956
17956
|
});
|
|
@@ -17967,11 +17967,11 @@ var require_auth = __commonJS({
|
|
|
17967
17967
|
"use strict";
|
|
17968
17968
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
17969
17969
|
function adopt(value2) {
|
|
17970
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
17971
|
-
|
|
17970
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
17971
|
+
resolve3(value2);
|
|
17972
17972
|
});
|
|
17973
17973
|
}
|
|
17974
|
-
return new (P || (P = Promise))(function(
|
|
17974
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
17975
17975
|
function fulfilled(value2) {
|
|
17976
17976
|
try {
|
|
17977
17977
|
step(generator.next(value2));
|
|
@@ -17987,7 +17987,7 @@ var require_auth = __commonJS({
|
|
|
17987
17987
|
}
|
|
17988
17988
|
}
|
|
17989
17989
|
function step(result) {
|
|
17990
|
-
result.done ?
|
|
17990
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
17991
17991
|
}
|
|
17992
17992
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17993
17993
|
});
|
|
@@ -18071,11 +18071,11 @@ var require_oidc_utils = __commonJS({
|
|
|
18071
18071
|
"use strict";
|
|
18072
18072
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
18073
18073
|
function adopt(value2) {
|
|
18074
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
18075
|
-
|
|
18074
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
18075
|
+
resolve3(value2);
|
|
18076
18076
|
});
|
|
18077
18077
|
}
|
|
18078
|
-
return new (P || (P = Promise))(function(
|
|
18078
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
18079
18079
|
function fulfilled(value2) {
|
|
18080
18080
|
try {
|
|
18081
18081
|
step(generator.next(value2));
|
|
@@ -18091,7 +18091,7 @@ var require_oidc_utils = __commonJS({
|
|
|
18091
18091
|
}
|
|
18092
18092
|
}
|
|
18093
18093
|
function step(result) {
|
|
18094
|
-
result.done ?
|
|
18094
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18095
18095
|
}
|
|
18096
18096
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18097
18097
|
});
|
|
@@ -18169,11 +18169,11 @@ var require_summary = __commonJS({
|
|
|
18169
18169
|
"use strict";
|
|
18170
18170
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
18171
18171
|
function adopt(value2) {
|
|
18172
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
18173
|
-
|
|
18172
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
18173
|
+
resolve3(value2);
|
|
18174
18174
|
});
|
|
18175
18175
|
}
|
|
18176
|
-
return new (P || (P = Promise))(function(
|
|
18176
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
18177
18177
|
function fulfilled(value2) {
|
|
18178
18178
|
try {
|
|
18179
18179
|
step(generator.next(value2));
|
|
@@ -18189,7 +18189,7 @@ var require_summary = __commonJS({
|
|
|
18189
18189
|
}
|
|
18190
18190
|
}
|
|
18191
18191
|
function step(result) {
|
|
18192
|
-
result.done ?
|
|
18192
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18193
18193
|
}
|
|
18194
18194
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18195
18195
|
});
|
|
@@ -18535,11 +18535,11 @@ var require_io_util = __commonJS({
|
|
|
18535
18535
|
};
|
|
18536
18536
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
18537
18537
|
function adopt(value2) {
|
|
18538
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
18539
|
-
|
|
18538
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
18539
|
+
resolve3(value2);
|
|
18540
18540
|
});
|
|
18541
18541
|
}
|
|
18542
|
-
return new (P || (P = Promise))(function(
|
|
18542
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
18543
18543
|
function fulfilled(value2) {
|
|
18544
18544
|
try {
|
|
18545
18545
|
step(generator.next(value2));
|
|
@@ -18555,7 +18555,7 @@ var require_io_util = __commonJS({
|
|
|
18555
18555
|
}
|
|
18556
18556
|
}
|
|
18557
18557
|
function step(result) {
|
|
18558
|
-
result.done ?
|
|
18558
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18559
18559
|
}
|
|
18560
18560
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18561
18561
|
});
|
|
@@ -18708,11 +18708,11 @@ var require_io = __commonJS({
|
|
|
18708
18708
|
};
|
|
18709
18709
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
18710
18710
|
function adopt(value2) {
|
|
18711
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
18712
|
-
|
|
18711
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
18712
|
+
resolve3(value2);
|
|
18713
18713
|
});
|
|
18714
18714
|
}
|
|
18715
|
-
return new (P || (P = Promise))(function(
|
|
18715
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
18716
18716
|
function fulfilled(value2) {
|
|
18717
18717
|
try {
|
|
18718
18718
|
step(generator.next(value2));
|
|
@@ -18728,7 +18728,7 @@ var require_io = __commonJS({
|
|
|
18728
18728
|
}
|
|
18729
18729
|
}
|
|
18730
18730
|
function step(result) {
|
|
18731
|
-
result.done ?
|
|
18731
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18732
18732
|
}
|
|
18733
18733
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18734
18734
|
});
|
|
@@ -18956,11 +18956,11 @@ var require_toolrunner = __commonJS({
|
|
|
18956
18956
|
};
|
|
18957
18957
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
18958
18958
|
function adopt(value2) {
|
|
18959
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
18960
|
-
|
|
18959
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
18960
|
+
resolve3(value2);
|
|
18961
18961
|
});
|
|
18962
18962
|
}
|
|
18963
|
-
return new (P || (P = Promise))(function(
|
|
18963
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
18964
18964
|
function fulfilled(value2) {
|
|
18965
18965
|
try {
|
|
18966
18966
|
step(generator.next(value2));
|
|
@@ -18976,7 +18976,7 @@ var require_toolrunner = __commonJS({
|
|
|
18976
18976
|
}
|
|
18977
18977
|
}
|
|
18978
18978
|
function step(result) {
|
|
18979
|
-
result.done ?
|
|
18979
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
18980
18980
|
}
|
|
18981
18981
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18982
18982
|
});
|
|
@@ -19204,7 +19204,7 @@ var require_toolrunner = __commonJS({
|
|
|
19204
19204
|
this.toolPath = path3.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
|
|
19205
19205
|
}
|
|
19206
19206
|
this.toolPath = yield io.which(this.toolPath, true);
|
|
19207
|
-
return new Promise((
|
|
19207
|
+
return new Promise((resolve3, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
19208
19208
|
this._debug(`exec tool: ${this.toolPath}`);
|
|
19209
19209
|
this._debug("arguments:");
|
|
19210
19210
|
for (const arg of this.args) {
|
|
@@ -19287,7 +19287,7 @@ var require_toolrunner = __commonJS({
|
|
|
19287
19287
|
if (error49) {
|
|
19288
19288
|
reject(error49);
|
|
19289
19289
|
} else {
|
|
19290
|
-
|
|
19290
|
+
resolve3(exitCode);
|
|
19291
19291
|
}
|
|
19292
19292
|
});
|
|
19293
19293
|
if (this.options.input) {
|
|
@@ -19440,11 +19440,11 @@ var require_exec = __commonJS({
|
|
|
19440
19440
|
};
|
|
19441
19441
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
19442
19442
|
function adopt(value2) {
|
|
19443
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
19444
|
-
|
|
19443
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
19444
|
+
resolve3(value2);
|
|
19445
19445
|
});
|
|
19446
19446
|
}
|
|
19447
|
-
return new (P || (P = Promise))(function(
|
|
19447
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
19448
19448
|
function fulfilled(value2) {
|
|
19449
19449
|
try {
|
|
19450
19450
|
step(generator.next(value2));
|
|
@@ -19460,7 +19460,7 @@ var require_exec = __commonJS({
|
|
|
19460
19460
|
}
|
|
19461
19461
|
}
|
|
19462
19462
|
function step(result) {
|
|
19463
|
-
result.done ?
|
|
19463
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19464
19464
|
}
|
|
19465
19465
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19466
19466
|
});
|
|
@@ -19551,11 +19551,11 @@ var require_platform = __commonJS({
|
|
|
19551
19551
|
};
|
|
19552
19552
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
19553
19553
|
function adopt(value2) {
|
|
19554
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
19555
|
-
|
|
19554
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
19555
|
+
resolve3(value2);
|
|
19556
19556
|
});
|
|
19557
19557
|
}
|
|
19558
|
-
return new (P || (P = Promise))(function(
|
|
19558
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
19559
19559
|
function fulfilled(value2) {
|
|
19560
19560
|
try {
|
|
19561
19561
|
step(generator.next(value2));
|
|
@@ -19571,7 +19571,7 @@ var require_platform = __commonJS({
|
|
|
19571
19571
|
}
|
|
19572
19572
|
}
|
|
19573
19573
|
function step(result) {
|
|
19574
|
-
result.done ?
|
|
19574
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19575
19575
|
}
|
|
19576
19576
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19577
19577
|
});
|
|
@@ -19670,11 +19670,11 @@ var require_core = __commonJS({
|
|
|
19670
19670
|
};
|
|
19671
19671
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
19672
19672
|
function adopt(value2) {
|
|
19673
|
-
return value2 instanceof P ? value2 : new P(function(
|
|
19674
|
-
|
|
19673
|
+
return value2 instanceof P ? value2 : new P(function(resolve3) {
|
|
19674
|
+
resolve3(value2);
|
|
19675
19675
|
});
|
|
19676
19676
|
}
|
|
19677
|
-
return new (P || (P = Promise))(function(
|
|
19677
|
+
return new (P || (P = Promise))(function(resolve3, reject) {
|
|
19678
19678
|
function fulfilled(value2) {
|
|
19679
19679
|
try {
|
|
19680
19680
|
step(generator.next(value2));
|
|
@@ -19690,7 +19690,7 @@ var require_core = __commonJS({
|
|
|
19690
19690
|
}
|
|
19691
19691
|
}
|
|
19692
19692
|
function step(result) {
|
|
19693
|
-
result.done ?
|
|
19693
|
+
result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19694
19694
|
}
|
|
19695
19695
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19696
19696
|
});
|
|
@@ -45381,8 +45381,8 @@ var require_resolve = __commonJS({
|
|
|
45381
45381
|
}
|
|
45382
45382
|
return count;
|
|
45383
45383
|
}
|
|
45384
|
-
function getFullPath(resolver, id = "",
|
|
45385
|
-
if (
|
|
45384
|
+
function getFullPath(resolver, id = "", normalize3) {
|
|
45385
|
+
if (normalize3 !== false)
|
|
45386
45386
|
id = normalizeId(id);
|
|
45387
45387
|
const p = resolver.parse(id);
|
|
45388
45388
|
return _getFullPath(resolver, p);
|
|
@@ -46130,7 +46130,7 @@ var require_compile = __commonJS({
|
|
|
46130
46130
|
const schOrFunc = root.refs[ref];
|
|
46131
46131
|
if (schOrFunc)
|
|
46132
46132
|
return schOrFunc;
|
|
46133
|
-
let _sch =
|
|
46133
|
+
let _sch = resolve3.call(this, root, ref);
|
|
46134
46134
|
if (_sch === void 0) {
|
|
46135
46135
|
const schema2 = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
46136
46136
|
const { schemaId } = this.opts;
|
|
@@ -46157,7 +46157,7 @@ var require_compile = __commonJS({
|
|
|
46157
46157
|
function sameSchemaEnv(s1, s2) {
|
|
46158
46158
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
46159
46159
|
}
|
|
46160
|
-
function
|
|
46160
|
+
function resolve3(root, ref) {
|
|
46161
46161
|
let sch;
|
|
46162
46162
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
46163
46163
|
ref = sch;
|
|
@@ -46722,7 +46722,7 @@ var require_fast_uri = __commonJS({
|
|
|
46722
46722
|
"use strict";
|
|
46723
46723
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils4();
|
|
46724
46724
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
46725
|
-
function
|
|
46725
|
+
function normalize3(uri, options) {
|
|
46726
46726
|
if (typeof uri === "string") {
|
|
46727
46727
|
uri = /** @type {T} */
|
|
46728
46728
|
serialize(parse5(uri, options), options);
|
|
@@ -46732,7 +46732,7 @@ var require_fast_uri = __commonJS({
|
|
|
46732
46732
|
}
|
|
46733
46733
|
return uri;
|
|
46734
46734
|
}
|
|
46735
|
-
function
|
|
46735
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
46736
46736
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
46737
46737
|
const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
46738
46738
|
schemelessOptions.skipEscape = true;
|
|
@@ -46958,8 +46958,8 @@ var require_fast_uri = __commonJS({
|
|
|
46958
46958
|
}
|
|
46959
46959
|
var fastUri = {
|
|
46960
46960
|
SCHEMES,
|
|
46961
|
-
normalize:
|
|
46962
|
-
resolve:
|
|
46961
|
+
normalize: normalize3,
|
|
46962
|
+
resolve: resolve3,
|
|
46963
46963
|
resolveComponent,
|
|
46964
46964
|
equal,
|
|
46965
46965
|
serialize,
|
|
@@ -52535,9 +52535,9 @@ var require_dispatcher_base2 = __commonJS({
|
|
|
52535
52535
|
}
|
|
52536
52536
|
close(callback) {
|
|
52537
52537
|
if (callback === void 0) {
|
|
52538
|
-
return new Promise((
|
|
52538
|
+
return new Promise((resolve3, reject) => {
|
|
52539
52539
|
this.close((err, data) => {
|
|
52540
|
-
return err ? reject(err) :
|
|
52540
|
+
return err ? reject(err) : resolve3(data);
|
|
52541
52541
|
});
|
|
52542
52542
|
});
|
|
52543
52543
|
}
|
|
@@ -52575,9 +52575,9 @@ var require_dispatcher_base2 = __commonJS({
|
|
|
52575
52575
|
err = null;
|
|
52576
52576
|
}
|
|
52577
52577
|
if (callback === void 0) {
|
|
52578
|
-
return new Promise((
|
|
52578
|
+
return new Promise((resolve3, reject) => {
|
|
52579
52579
|
this.destroy(err, (err2, data) => {
|
|
52580
|
-
return err2 ? reject(err2) :
|
|
52580
|
+
return err2 ? reject(err2) : resolve3(data);
|
|
52581
52581
|
});
|
|
52582
52582
|
});
|
|
52583
52583
|
}
|
|
@@ -56053,8 +56053,8 @@ var require_promise = __commonJS({
|
|
|
56053
56053
|
function createDeferredPromise() {
|
|
56054
56054
|
let res;
|
|
56055
56055
|
let rej;
|
|
56056
|
-
const promise2 = new Promise((
|
|
56057
|
-
res =
|
|
56056
|
+
const promise2 = new Promise((resolve3, reject) => {
|
|
56057
|
+
res = resolve3;
|
|
56058
56058
|
rej = reject;
|
|
56059
56059
|
});
|
|
56060
56060
|
return { promise: promise2, resolve: res, reject: rej };
|
|
@@ -57352,12 +57352,12 @@ upgrade: ${upgrade}\r
|
|
|
57352
57352
|
cb();
|
|
57353
57353
|
}
|
|
57354
57354
|
}
|
|
57355
|
-
const waitForDrain = () => new Promise((
|
|
57355
|
+
const waitForDrain = () => new Promise((resolve3, reject) => {
|
|
57356
57356
|
assert3(callback === null);
|
|
57357
57357
|
if (socket[kError]) {
|
|
57358
57358
|
reject(socket[kError]);
|
|
57359
57359
|
} else {
|
|
57360
|
-
callback =
|
|
57360
|
+
callback = resolve3;
|
|
57361
57361
|
}
|
|
57362
57362
|
});
|
|
57363
57363
|
socket.on("close", onDrain).on("drain", onDrain);
|
|
@@ -58198,12 +58198,12 @@ var require_client_h2 = __commonJS({
|
|
|
58198
58198
|
cb();
|
|
58199
58199
|
}
|
|
58200
58200
|
}
|
|
58201
|
-
const waitForDrain = () => new Promise((
|
|
58201
|
+
const waitForDrain = () => new Promise((resolve3, reject) => {
|
|
58202
58202
|
assert3(callback === null);
|
|
58203
58203
|
if (socket[kError]) {
|
|
58204
58204
|
reject(socket[kError]);
|
|
58205
58205
|
} else {
|
|
58206
|
-
callback =
|
|
58206
|
+
callback = resolve3;
|
|
58207
58207
|
}
|
|
58208
58208
|
});
|
|
58209
58209
|
h2stream.on("close", onDrain).on("drain", onDrain);
|
|
@@ -58511,16 +58511,16 @@ var require_client2 = __commonJS({
|
|
|
58511
58511
|
return this[kNeedDrain] < 2;
|
|
58512
58512
|
}
|
|
58513
58513
|
[kClose]() {
|
|
58514
|
-
return new Promise((
|
|
58514
|
+
return new Promise((resolve3) => {
|
|
58515
58515
|
if (this[kSize]) {
|
|
58516
|
-
this[kClosedResolve] =
|
|
58516
|
+
this[kClosedResolve] = resolve3;
|
|
58517
58517
|
} else {
|
|
58518
|
-
|
|
58518
|
+
resolve3(null);
|
|
58519
58519
|
}
|
|
58520
58520
|
});
|
|
58521
58521
|
}
|
|
58522
58522
|
[kDestroy](err) {
|
|
58523
|
-
return new Promise((
|
|
58523
|
+
return new Promise((resolve3) => {
|
|
58524
58524
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
58525
58525
|
for (let i = 0; i < requests.length; i++) {
|
|
58526
58526
|
const request2 = requests[i];
|
|
@@ -58531,7 +58531,7 @@ var require_client2 = __commonJS({
|
|
|
58531
58531
|
this[kClosedResolve]();
|
|
58532
58532
|
this[kClosedResolve] = null;
|
|
58533
58533
|
}
|
|
58534
|
-
|
|
58534
|
+
resolve3(null);
|
|
58535
58535
|
};
|
|
58536
58536
|
if (this[kHTTPContext]) {
|
|
58537
58537
|
this[kHTTPContext].destroy(err, callback);
|
|
@@ -58928,8 +58928,8 @@ var require_pool_base2 = __commonJS({
|
|
|
58928
58928
|
}
|
|
58929
58929
|
return Promise.all(closeAll);
|
|
58930
58930
|
} else {
|
|
58931
|
-
return new Promise((
|
|
58932
|
-
this[kClosedResolve] =
|
|
58931
|
+
return new Promise((resolve3) => {
|
|
58932
|
+
this[kClosedResolve] = resolve3;
|
|
58933
58933
|
});
|
|
58934
58934
|
}
|
|
58935
58935
|
}
|
|
@@ -60458,7 +60458,7 @@ var require_readable2 = __commonJS({
|
|
|
60458
60458
|
if (this._readableState.closeEmitted) {
|
|
60459
60459
|
return Promise.resolve(null);
|
|
60460
60460
|
}
|
|
60461
|
-
return new Promise((
|
|
60461
|
+
return new Promise((resolve3, reject) => {
|
|
60462
60462
|
if (this[kContentLength] && this[kContentLength] > limit || this[kBytesRead] > limit) {
|
|
60463
60463
|
this.destroy(new AbortError2());
|
|
60464
60464
|
}
|
|
@@ -60472,11 +60472,11 @@ var require_readable2 = __commonJS({
|
|
|
60472
60472
|
if (signal.aborted) {
|
|
60473
60473
|
reject(signal.reason ?? new AbortError2());
|
|
60474
60474
|
} else {
|
|
60475
|
-
|
|
60475
|
+
resolve3(null);
|
|
60476
60476
|
}
|
|
60477
60477
|
});
|
|
60478
60478
|
} else {
|
|
60479
|
-
this.on("close",
|
|
60479
|
+
this.on("close", resolve3);
|
|
60480
60480
|
}
|
|
60481
60481
|
this.on("error", noop4).on("data", () => {
|
|
60482
60482
|
if (this[kBytesRead] > limit) {
|
|
@@ -60504,7 +60504,7 @@ var require_readable2 = __commonJS({
|
|
|
60504
60504
|
}
|
|
60505
60505
|
function consume(stream, type2) {
|
|
60506
60506
|
assert3(!stream[kConsume]);
|
|
60507
|
-
return new Promise((
|
|
60507
|
+
return new Promise((resolve3, reject) => {
|
|
60508
60508
|
if (isUnusable(stream)) {
|
|
60509
60509
|
const rState = stream._readableState;
|
|
60510
60510
|
if (rState.destroyed && rState.closeEmitted === false) {
|
|
@@ -60519,7 +60519,7 @@ var require_readable2 = __commonJS({
|
|
|
60519
60519
|
stream[kConsume] = {
|
|
60520
60520
|
type: type2,
|
|
60521
60521
|
stream,
|
|
60522
|
-
resolve:
|
|
60522
|
+
resolve: resolve3,
|
|
60523
60523
|
reject,
|
|
60524
60524
|
length: 0,
|
|
60525
60525
|
body: []
|
|
@@ -60593,18 +60593,18 @@ var require_readable2 = __commonJS({
|
|
|
60593
60593
|
return buffer;
|
|
60594
60594
|
}
|
|
60595
60595
|
function consumeEnd(consume2, encoding) {
|
|
60596
|
-
const { type: type2, body, resolve:
|
|
60596
|
+
const { type: type2, body, resolve: resolve3, stream, length } = consume2;
|
|
60597
60597
|
try {
|
|
60598
60598
|
if (type2 === "text") {
|
|
60599
|
-
|
|
60599
|
+
resolve3(chunksDecode(body, length, encoding));
|
|
60600
60600
|
} else if (type2 === "json") {
|
|
60601
|
-
|
|
60601
|
+
resolve3(JSON.parse(chunksDecode(body, length, encoding)));
|
|
60602
60602
|
} else if (type2 === "arrayBuffer") {
|
|
60603
|
-
|
|
60603
|
+
resolve3(chunksConcat(body, length).buffer);
|
|
60604
60604
|
} else if (type2 === "blob") {
|
|
60605
|
-
|
|
60605
|
+
resolve3(new Blob(body, { type: stream[kContentType] }));
|
|
60606
60606
|
} else if (type2 === "bytes") {
|
|
60607
|
-
|
|
60607
|
+
resolve3(chunksConcat(body, length));
|
|
60608
60608
|
}
|
|
60609
60609
|
consumeFinish(consume2);
|
|
60610
60610
|
} catch (err) {
|
|
@@ -60794,9 +60794,9 @@ var require_api_request2 = __commonJS({
|
|
|
60794
60794
|
};
|
|
60795
60795
|
function request2(opts, callback) {
|
|
60796
60796
|
if (callback === void 0) {
|
|
60797
|
-
return new Promise((
|
|
60797
|
+
return new Promise((resolve3, reject) => {
|
|
60798
60798
|
request2.call(this, opts, (err, data) => {
|
|
60799
|
-
return err ? reject(err) :
|
|
60799
|
+
return err ? reject(err) : resolve3(data);
|
|
60800
60800
|
});
|
|
60801
60801
|
});
|
|
60802
60802
|
}
|
|
@@ -61008,9 +61008,9 @@ var require_api_stream2 = __commonJS({
|
|
|
61008
61008
|
};
|
|
61009
61009
|
function stream(opts, factory, callback) {
|
|
61010
61010
|
if (callback === void 0) {
|
|
61011
|
-
return new Promise((
|
|
61011
|
+
return new Promise((resolve3, reject) => {
|
|
61012
61012
|
stream.call(this, opts, factory, (err, data) => {
|
|
61013
|
-
return err ? reject(err) :
|
|
61013
|
+
return err ? reject(err) : resolve3(data);
|
|
61014
61014
|
});
|
|
61015
61015
|
});
|
|
61016
61016
|
}
|
|
@@ -61298,9 +61298,9 @@ var require_api_upgrade2 = __commonJS({
|
|
|
61298
61298
|
};
|
|
61299
61299
|
function upgrade(opts, callback) {
|
|
61300
61300
|
if (callback === void 0) {
|
|
61301
|
-
return new Promise((
|
|
61301
|
+
return new Promise((resolve3, reject) => {
|
|
61302
61302
|
upgrade.call(this, opts, (err, data) => {
|
|
61303
|
-
return err ? reject(err) :
|
|
61303
|
+
return err ? reject(err) : resolve3(data);
|
|
61304
61304
|
});
|
|
61305
61305
|
});
|
|
61306
61306
|
}
|
|
@@ -61393,9 +61393,9 @@ var require_api_connect2 = __commonJS({
|
|
|
61393
61393
|
};
|
|
61394
61394
|
function connect(opts, callback) {
|
|
61395
61395
|
if (callback === void 0) {
|
|
61396
|
-
return new Promise((
|
|
61396
|
+
return new Promise((resolve3, reject) => {
|
|
61397
61397
|
connect.call(this, opts, (err, data) => {
|
|
61398
|
-
return err ? reject(err) :
|
|
61398
|
+
return err ? reject(err) : resolve3(data);
|
|
61399
61399
|
});
|
|
61400
61400
|
});
|
|
61401
61401
|
}
|
|
@@ -62663,7 +62663,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
62663
62663
|
"node_modules/.pnpm/undici@7.22.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
62664
62664
|
"use strict";
|
|
62665
62665
|
var { writeFile: writeFile2, readFile, mkdir } = __require("node:fs/promises");
|
|
62666
|
-
var { dirname: dirname3, resolve:
|
|
62666
|
+
var { dirname: dirname3, resolve: resolve3 } = __require("node:path");
|
|
62667
62667
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
|
|
62668
62668
|
var { InvalidArgumentError, UndiciError } = require_errors4();
|
|
62669
62669
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -62864,7 +62864,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
62864
62864
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
62865
62865
|
}
|
|
62866
62866
|
try {
|
|
62867
|
-
const data = await readFile(
|
|
62867
|
+
const data = await readFile(resolve3(path3), "utf8");
|
|
62868
62868
|
const parsed2 = JSON.parse(data);
|
|
62869
62869
|
if (Array.isArray(parsed2)) {
|
|
62870
62870
|
this.#snapshots.clear();
|
|
@@ -62893,7 +62893,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
62893
62893
|
if (!path3) {
|
|
62894
62894
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
62895
62895
|
}
|
|
62896
|
-
const resolvedPath =
|
|
62896
|
+
const resolvedPath = resolve3(path3);
|
|
62897
62897
|
await mkdir(dirname3(resolvedPath), { recursive: true });
|
|
62898
62898
|
const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot2]) => ({
|
|
62899
62899
|
hash: hash2,
|
|
@@ -69470,7 +69470,7 @@ var require_fetch2 = __commonJS({
|
|
|
69470
69470
|
function dispatch({ body }) {
|
|
69471
69471
|
const url4 = requestCurrentURL(request2);
|
|
69472
69472
|
const agent2 = fetchParams.controller.dispatcher;
|
|
69473
|
-
return new Promise((
|
|
69473
|
+
return new Promise((resolve3, reject) => agent2.dispatch(
|
|
69474
69474
|
{
|
|
69475
69475
|
path: url4.pathname + url4.search,
|
|
69476
69476
|
origin: url4.origin,
|
|
@@ -69550,7 +69550,7 @@ var require_fetch2 = __commonJS({
|
|
|
69550
69550
|
}
|
|
69551
69551
|
}
|
|
69552
69552
|
const onError = this.onError.bind(this);
|
|
69553
|
-
|
|
69553
|
+
resolve3({
|
|
69554
69554
|
status,
|
|
69555
69555
|
statusText,
|
|
69556
69556
|
headersList,
|
|
@@ -69603,7 +69603,7 @@ var require_fetch2 = __commonJS({
|
|
|
69603
69603
|
headersList.append(headerName, String(value2), true);
|
|
69604
69604
|
}
|
|
69605
69605
|
}
|
|
69606
|
-
|
|
69606
|
+
resolve3({
|
|
69607
69607
|
status,
|
|
69608
69608
|
statusText: STATUS_CODES[status],
|
|
69609
69609
|
headersList,
|
|
@@ -69619,7 +69619,7 @@ var require_fetch2 = __commonJS({
|
|
|
69619
69619
|
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
69620
69620
|
headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
|
|
69621
69621
|
}
|
|
69622
|
-
|
|
69622
|
+
resolve3({
|
|
69623
69623
|
status,
|
|
69624
69624
|
statusText: STATUS_CODES[status],
|
|
69625
69625
|
headersList,
|
|
@@ -97475,14 +97475,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
97475
97475
|
} else if (node2.nodeType === 1) {
|
|
97476
97476
|
replacement = replacementForNode.call(self2, node2);
|
|
97477
97477
|
}
|
|
97478
|
-
return
|
|
97478
|
+
return join15(output, replacement);
|
|
97479
97479
|
}, "");
|
|
97480
97480
|
}
|
|
97481
97481
|
function postProcess(output) {
|
|
97482
97482
|
var self2 = this;
|
|
97483
97483
|
this.rules.forEach(function(rule) {
|
|
97484
97484
|
if (typeof rule.append === "function") {
|
|
97485
|
-
output =
|
|
97485
|
+
output = join15(output, rule.append(self2.options));
|
|
97486
97486
|
}
|
|
97487
97487
|
});
|
|
97488
97488
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -97494,7 +97494,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
97494
97494
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
97495
97495
|
return whitespace.leading + rule.replacement(content, node2, this.options) + whitespace.trailing;
|
|
97496
97496
|
}
|
|
97497
|
-
function
|
|
97497
|
+
function join15(output, replacement) {
|
|
97498
97498
|
var s1 = trimTrailingNewlines(output);
|
|
97499
97499
|
var s2 = trimLeadingNewlines(replacement);
|
|
97500
97500
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -97961,8 +97961,8 @@ var require_light = __commonJS({
|
|
|
97961
97961
|
return this.Promise.resolve();
|
|
97962
97962
|
}
|
|
97963
97963
|
yieldLoop(t = 0) {
|
|
97964
|
-
return new this.Promise(function(
|
|
97965
|
-
return setTimeout(
|
|
97964
|
+
return new this.Promise(function(resolve3, reject) {
|
|
97965
|
+
return setTimeout(resolve3, t);
|
|
97966
97966
|
});
|
|
97967
97967
|
}
|
|
97968
97968
|
computePenalty() {
|
|
@@ -98173,15 +98173,15 @@ var require_light = __commonJS({
|
|
|
98173
98173
|
return this._queue.length === 0;
|
|
98174
98174
|
}
|
|
98175
98175
|
async _tryToRun() {
|
|
98176
|
-
var args2, cb, error49, reject,
|
|
98176
|
+
var args2, cb, error49, reject, resolve3, returned, task;
|
|
98177
98177
|
if (this._running < 1 && this._queue.length > 0) {
|
|
98178
98178
|
this._running++;
|
|
98179
|
-
({ task, args: args2, resolve:
|
|
98179
|
+
({ task, args: args2, resolve: resolve3, reject } = this._queue.shift());
|
|
98180
98180
|
cb = await (async function() {
|
|
98181
98181
|
try {
|
|
98182
98182
|
returned = await task(...args2);
|
|
98183
98183
|
return function() {
|
|
98184
|
-
return
|
|
98184
|
+
return resolve3(returned);
|
|
98185
98185
|
};
|
|
98186
98186
|
} catch (error1) {
|
|
98187
98187
|
error49 = error1;
|
|
@@ -98196,13 +98196,13 @@ var require_light = __commonJS({
|
|
|
98196
98196
|
}
|
|
98197
98197
|
}
|
|
98198
98198
|
schedule(task, ...args2) {
|
|
98199
|
-
var promise2, reject,
|
|
98200
|
-
|
|
98199
|
+
var promise2, reject, resolve3;
|
|
98200
|
+
resolve3 = reject = null;
|
|
98201
98201
|
promise2 = new this.Promise(function(_resolve, _reject) {
|
|
98202
|
-
|
|
98202
|
+
resolve3 = _resolve;
|
|
98203
98203
|
return reject = _reject;
|
|
98204
98204
|
});
|
|
98205
|
-
this._queue.push({ task, args: args2, resolve:
|
|
98205
|
+
this._queue.push({ task, args: args2, resolve: resolve3, reject });
|
|
98206
98206
|
this._tryToRun();
|
|
98207
98207
|
return promise2;
|
|
98208
98208
|
}
|
|
@@ -98603,14 +98603,14 @@ var require_light = __commonJS({
|
|
|
98603
98603
|
counts = this._states.counts;
|
|
98604
98604
|
return counts[0] + counts[1] + counts[2] + counts[3] === at;
|
|
98605
98605
|
};
|
|
98606
|
-
return new this.Promise((
|
|
98606
|
+
return new this.Promise((resolve3, reject) => {
|
|
98607
98607
|
if (finished()) {
|
|
98608
|
-
return
|
|
98608
|
+
return resolve3();
|
|
98609
98609
|
} else {
|
|
98610
98610
|
return this.on("done", () => {
|
|
98611
98611
|
if (finished()) {
|
|
98612
98612
|
this.removeAllListeners("done");
|
|
98613
|
-
return
|
|
98613
|
+
return resolve3();
|
|
98614
98614
|
}
|
|
98615
98615
|
});
|
|
98616
98616
|
}
|
|
@@ -98703,9 +98703,9 @@ var require_light = __commonJS({
|
|
|
98703
98703
|
options = parser$5.load(options, this.jobDefaults);
|
|
98704
98704
|
}
|
|
98705
98705
|
task = (...args3) => {
|
|
98706
|
-
return new this.Promise(function(
|
|
98706
|
+
return new this.Promise(function(resolve3, reject) {
|
|
98707
98707
|
return fn2(...args3, function(...args4) {
|
|
98708
|
-
return (args4[0] != null ? reject :
|
|
98708
|
+
return (args4[0] != null ? reject : resolve3)(args4);
|
|
98709
98709
|
});
|
|
98710
98710
|
});
|
|
98711
98711
|
};
|
|
@@ -98925,6 +98925,8 @@ var require_fast_content_type_parse = __commonJS({
|
|
|
98925
98925
|
|
|
98926
98926
|
// main.ts
|
|
98927
98927
|
var core6 = __toESM(require_core(), 1);
|
|
98928
|
+
import { existsSync as existsSync6, readdirSync } from "node:fs";
|
|
98929
|
+
import { join as join14 } from "node:path";
|
|
98928
98930
|
|
|
98929
98931
|
// node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
|
|
98930
98932
|
var liftArray = (data) => Array.isArray(data) ? data : [data];
|
|
@@ -105243,10 +105245,10 @@ var BaseScope = class {
|
|
|
105243
105245
|
});
|
|
105244
105246
|
};
|
|
105245
105247
|
lazyResolutions = [];
|
|
105246
|
-
lazilyResolve(
|
|
105248
|
+
lazilyResolve(resolve3, syntheticAlias) {
|
|
105247
105249
|
const node2 = this.node("alias", {
|
|
105248
105250
|
reference: syntheticAlias ?? "synthetic",
|
|
105249
|
-
resolve:
|
|
105251
|
+
resolve: resolve3
|
|
105250
105252
|
}, { prereduced: true });
|
|
105251
105253
|
if (!this.resolved)
|
|
105252
105254
|
this.lazyResolutions.push(node2);
|
|
@@ -107396,6 +107398,81 @@ var core = __toESM(require_core(), 1);
|
|
|
107396
107398
|
var import_table = __toESM(require_src(), 1);
|
|
107397
107399
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
107398
107400
|
|
|
107401
|
+
// agents/shared.ts
|
|
107402
|
+
import { execFileSync } from "node:child_process";
|
|
107403
|
+
var MAX_STDERR_LINES = 20;
|
|
107404
|
+
var MAX_COMMIT_RETRIES = 3;
|
|
107405
|
+
function getGitStatus() {
|
|
107406
|
+
try {
|
|
107407
|
+
return execFileSync("git", ["status", "--porcelain"], {
|
|
107408
|
+
encoding: "utf-8",
|
|
107409
|
+
timeout: 1e4
|
|
107410
|
+
}).trim();
|
|
107411
|
+
} catch {
|
|
107412
|
+
return "";
|
|
107413
|
+
}
|
|
107414
|
+
}
|
|
107415
|
+
function buildCommitPrompt(_agentId, status) {
|
|
107416
|
+
return [
|
|
107417
|
+
`UNCOMMITTED CHANGES \u2014 the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`,
|
|
107418
|
+
"",
|
|
107419
|
+
"```",
|
|
107420
|
+
status,
|
|
107421
|
+
"```"
|
|
107422
|
+
].join("\n");
|
|
107423
|
+
}
|
|
107424
|
+
var agent = (input) => {
|
|
107425
|
+
return {
|
|
107426
|
+
...input,
|
|
107427
|
+
run: async (ctx) => {
|
|
107428
|
+
log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
|
|
107429
|
+
return input.run(ctx);
|
|
107430
|
+
}
|
|
107431
|
+
};
|
|
107432
|
+
};
|
|
107433
|
+
function formatCostUsd(costUsd) {
|
|
107434
|
+
return costUsd.toFixed(4);
|
|
107435
|
+
}
|
|
107436
|
+
function mergeAgentUsage(a, b) {
|
|
107437
|
+
if (!a && !b) return void 0;
|
|
107438
|
+
if (!a) return { ...b };
|
|
107439
|
+
if (!b) return { ...a };
|
|
107440
|
+
const cacheRead = (a.cacheReadTokens ?? 0) + (b.cacheReadTokens ?? 0);
|
|
107441
|
+
const cacheWrite = (a.cacheWriteTokens ?? 0) + (b.cacheWriteTokens ?? 0);
|
|
107442
|
+
const cost = (a.costUsd ?? 0) + (b.costUsd ?? 0);
|
|
107443
|
+
return {
|
|
107444
|
+
agent: a.agent,
|
|
107445
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
107446
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
107447
|
+
cacheReadTokens: cacheRead > 0 ? cacheRead : void 0,
|
|
107448
|
+
cacheWriteTokens: cacheWrite > 0 ? cacheWrite : void 0,
|
|
107449
|
+
costUsd: cost > 0 ? cost : void 0
|
|
107450
|
+
};
|
|
107451
|
+
}
|
|
107452
|
+
function logTokenTable(t) {
|
|
107453
|
+
const total = t.input + t.cacheRead + t.cacheWrite + t.output;
|
|
107454
|
+
const costUsd = typeof t.costUsd === "number" && t.costUsd > 0 ? t.costUsd : void 0;
|
|
107455
|
+
const headerRow = [
|
|
107456
|
+
{ data: "Input", header: true },
|
|
107457
|
+
{ data: "Cache Read", header: true },
|
|
107458
|
+
{ data: "Cache Write", header: true },
|
|
107459
|
+
{ data: "Output", header: true },
|
|
107460
|
+
{ data: "Total", header: true }
|
|
107461
|
+
];
|
|
107462
|
+
const dataRow = [
|
|
107463
|
+
String(t.input),
|
|
107464
|
+
String(t.cacheRead),
|
|
107465
|
+
String(t.cacheWrite),
|
|
107466
|
+
String(t.output),
|
|
107467
|
+
String(total)
|
|
107468
|
+
];
|
|
107469
|
+
if (costUsd !== void 0) {
|
|
107470
|
+
headerRow.push({ data: "Cost ($)", header: true });
|
|
107471
|
+
dataRow.push(formatCostUsd(costUsd));
|
|
107472
|
+
}
|
|
107473
|
+
log.table([headerRow, dataRow]);
|
|
107474
|
+
}
|
|
107475
|
+
|
|
107399
107476
|
// utils/globals.ts
|
|
107400
107477
|
import { existsSync } from "node:fs";
|
|
107401
107478
|
var isCloudflareSandbox = !!process.env.CLOUDFLARE_APPLICATION_ID && !!process.env.SANDBOX_VERSION;
|
|
@@ -107592,20 +107669,26 @@ function formatJsonValue(value2) {
|
|
|
107592
107669
|
}
|
|
107593
107670
|
function formatUsageSummary(entries) {
|
|
107594
107671
|
if (entries.length === 0) return "";
|
|
107595
|
-
const header = "| Agent | Input |
|
|
107596
|
-
const separatorRow = "| --- | ---: | ---: | ---: | ---: |";
|
|
107672
|
+
const header = "| Agent | Input | Cache Read | Cache Write | Output | Total | Cost ($) |";
|
|
107673
|
+
const separatorRow = "| --- | ---: | ---: | ---: | ---: | ---: | ---: |";
|
|
107597
107674
|
const fmt = (n) => n.toLocaleString("en-US");
|
|
107675
|
+
const nonCachedInput = (e) => Math.max(0, e.inputTokens - (e.cacheReadTokens ?? 0) - (e.cacheWriteTokens ?? 0));
|
|
107676
|
+
const totalFor = (e) => nonCachedInput(e) + (e.cacheReadTokens ?? 0) + (e.cacheWriteTokens ?? 0) + e.outputTokens;
|
|
107677
|
+
const costCell = (e) => typeof e.costUsd === "number" && e.costUsd > 0 ? formatCostUsd(e.costUsd) : "\u2014";
|
|
107598
107678
|
const rows = entries.map(
|
|
107599
|
-
(e) => `| ${e.agent} | ${fmt(e
|
|
107679
|
+
(e) => `| ${e.agent} | ${fmt(nonCachedInput(e))} | ${fmt(e.cacheReadTokens ?? 0)} | ${fmt(e.cacheWriteTokens ?? 0)} | ${fmt(e.outputTokens)} | ${fmt(totalFor(e))} | ${costCell(e)} |`
|
|
107600
107680
|
);
|
|
107601
107681
|
const totalsRows = [];
|
|
107602
107682
|
if (entries.length > 1) {
|
|
107603
|
-
const totalInput = entries.reduce((sum, e) => sum + e
|
|
107683
|
+
const totalInput = entries.reduce((sum, e) => sum + nonCachedInput(e), 0);
|
|
107604
107684
|
const totalOutput = entries.reduce((sum, e) => sum + e.outputTokens, 0);
|
|
107605
107685
|
const totalCacheRead = entries.reduce((sum, e) => sum + (e.cacheReadTokens ?? 0), 0);
|
|
107606
107686
|
const totalCacheWrite = entries.reduce((sum, e) => sum + (e.cacheWriteTokens ?? 0), 0);
|
|
107687
|
+
const grandTotal = totalInput + totalCacheRead + totalCacheWrite + totalOutput;
|
|
107688
|
+
const totalCostUsd = entries.reduce((sum, e) => sum + (e.costUsd ?? 0), 0);
|
|
107689
|
+
const totalCostCell = totalCostUsd > 0 ? `**${formatCostUsd(totalCostUsd)}**` : "\u2014";
|
|
107607
107690
|
totalsRows.push(
|
|
107608
|
-
`| **Total** | **${fmt(totalInput)}** | **${fmt(
|
|
107691
|
+
`| **Total** | **${fmt(totalInput)}** | **${fmt(totalCacheRead)}** | **${fmt(totalCacheWrite)}** | **${fmt(totalOutput)}** | **${fmt(grandTotal)}** | ${totalCostCell} |`
|
|
107609
107692
|
);
|
|
107610
107693
|
}
|
|
107611
107694
|
return [
|
|
@@ -107648,7 +107731,7 @@ var providers = {
|
|
|
107648
107731
|
models: {
|
|
107649
107732
|
"claude-opus": {
|
|
107650
107733
|
displayName: "Claude Opus",
|
|
107651
|
-
resolve: "anthropic/claude-opus-4-
|
|
107734
|
+
resolve: "anthropic/claude-opus-4-7",
|
|
107652
107735
|
openRouterResolve: "openrouter/anthropic/claude-opus-4.6",
|
|
107653
107736
|
preferred: true
|
|
107654
107737
|
},
|
|
@@ -107676,7 +107759,7 @@ var providers = {
|
|
|
107676
107759
|
},
|
|
107677
107760
|
"gpt-codex-mini": {
|
|
107678
107761
|
displayName: "GPT Codex Mini",
|
|
107679
|
-
resolve: "openai/codex-mini
|
|
107762
|
+
resolve: "openai/gpt-5.1-codex-mini",
|
|
107680
107763
|
openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
|
|
107681
107764
|
},
|
|
107682
107765
|
o3: {
|
|
@@ -107766,7 +107849,7 @@ var providers = {
|
|
|
107766
107849
|
},
|
|
107767
107850
|
"claude-opus": {
|
|
107768
107851
|
displayName: "Claude Opus",
|
|
107769
|
-
resolve: "opencode/claude-opus-4-
|
|
107852
|
+
resolve: "opencode/claude-opus-4-7",
|
|
107770
107853
|
openRouterResolve: "openrouter/anthropic/claude-opus-4.6"
|
|
107771
107854
|
},
|
|
107772
107855
|
"claude-sonnet": {
|
|
@@ -108044,22 +108127,35 @@ async function retry(fn2, options = {}) {
|
|
|
108044
108127
|
}
|
|
108045
108128
|
|
|
108046
108129
|
// utils/patchWorkflowRunFields.ts
|
|
108047
|
-
var
|
|
108130
|
+
var STRING_KEYS = [
|
|
108048
108131
|
"prNodeId",
|
|
108049
108132
|
"issueNodeId",
|
|
108050
108133
|
"reviewNodeId",
|
|
108051
108134
|
"planCommentNodeId",
|
|
108052
108135
|
"summaryCommentNodeId"
|
|
108053
108136
|
];
|
|
108137
|
+
var NUMBER_KEYS = [
|
|
108138
|
+
"inputTokens",
|
|
108139
|
+
"outputTokens",
|
|
108140
|
+
"cacheReadTokens",
|
|
108141
|
+
"cacheWriteTokens",
|
|
108142
|
+
"costUsd"
|
|
108143
|
+
];
|
|
108054
108144
|
async function patchWorkflowRunFields(ctx, fields) {
|
|
108055
108145
|
if (ctx.runId === void 0 || !ctx.apiToken) return;
|
|
108056
108146
|
const body = {};
|
|
108057
|
-
for (const key of
|
|
108147
|
+
for (const key of STRING_KEYS) {
|
|
108058
108148
|
const value2 = fields[key];
|
|
108059
108149
|
if (typeof value2 === "string" && value2.length > 0) {
|
|
108060
108150
|
body[key] = value2;
|
|
108061
108151
|
}
|
|
108062
108152
|
}
|
|
108153
|
+
for (const key of NUMBER_KEYS) {
|
|
108154
|
+
const value2 = fields[key];
|
|
108155
|
+
if (typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0) {
|
|
108156
|
+
body[key] = value2;
|
|
108157
|
+
}
|
|
108158
|
+
}
|
|
108063
108159
|
if (Object.keys(body).length === 0) return;
|
|
108064
108160
|
try {
|
|
108065
108161
|
await retry(
|
|
@@ -108086,6 +108182,38 @@ async function patchWorkflowRunFields(ctx, fields) {
|
|
|
108086
108182
|
log.warning(`patchWorkflowRunFields exhausted retries: ${error49}`);
|
|
108087
108183
|
}
|
|
108088
108184
|
}
|
|
108185
|
+
var INT4_MAX = 2147483647;
|
|
108186
|
+
function clampInt(value2, field) {
|
|
108187
|
+
if (value2 > INT4_MAX) {
|
|
108188
|
+
log.warning(
|
|
108189
|
+
`aggregateUsage: ${field}=${value2} exceeds INT4_MAX (${INT4_MAX}) \u2014 clamping so the rest of the usage row still persists.`
|
|
108190
|
+
);
|
|
108191
|
+
return INT4_MAX;
|
|
108192
|
+
}
|
|
108193
|
+
return value2;
|
|
108194
|
+
}
|
|
108195
|
+
function aggregateUsage(entries) {
|
|
108196
|
+
if (entries.length === 0) return {};
|
|
108197
|
+
const sum = entries.reduce(
|
|
108198
|
+
(acc, e) => ({
|
|
108199
|
+
inputTokens: acc.inputTokens + e.inputTokens,
|
|
108200
|
+
outputTokens: acc.outputTokens + e.outputTokens,
|
|
108201
|
+
cacheReadTokens: acc.cacheReadTokens + (e.cacheReadTokens ?? 0),
|
|
108202
|
+
cacheWriteTokens: acc.cacheWriteTokens + (e.cacheWriteTokens ?? 0),
|
|
108203
|
+
costUsd: acc.costUsd + (e.costUsd ?? 0)
|
|
108204
|
+
}),
|
|
108205
|
+
{ inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: 0 }
|
|
108206
|
+
);
|
|
108207
|
+
const out = {};
|
|
108208
|
+
if (sum.inputTokens > 0) out.inputTokens = clampInt(sum.inputTokens, "inputTokens");
|
|
108209
|
+
if (sum.outputTokens > 0) out.outputTokens = clampInt(sum.outputTokens, "outputTokens");
|
|
108210
|
+
if (sum.cacheReadTokens > 0)
|
|
108211
|
+
out.cacheReadTokens = clampInt(sum.cacheReadTokens, "cacheReadTokens");
|
|
108212
|
+
if (sum.cacheWriteTokens > 0)
|
|
108213
|
+
out.cacheWriteTokens = clampInt(sum.cacheWriteTokens, "cacheWriteTokens");
|
|
108214
|
+
if (sum.costUsd > 0) out.costUsd = sum.costUsd;
|
|
108215
|
+
return out;
|
|
108216
|
+
}
|
|
108089
108217
|
|
|
108090
108218
|
// node_modules/.pnpm/@toon-format+toon@1.4.0/node_modules/@toon-format/toon/dist/index.mjs
|
|
108091
108219
|
var LIST_ITEM_MARKER = "-";
|
|
@@ -108442,6 +108570,126 @@ function resolveOptions(options) {
|
|
|
108442
108570
|
};
|
|
108443
108571
|
}
|
|
108444
108572
|
|
|
108573
|
+
// mcp/geminiSanitizer.ts
|
|
108574
|
+
function parseStringEnumBranch(item) {
|
|
108575
|
+
if (!item || typeof item !== "object") return null;
|
|
108576
|
+
const record3 = item;
|
|
108577
|
+
if (Array.isArray(record3.enum)) {
|
|
108578
|
+
const strings = record3.enum.filter((v) => typeof v === "string");
|
|
108579
|
+
return strings.length === record3.enum.length && strings.length > 0 ? { values: strings } : null;
|
|
108580
|
+
}
|
|
108581
|
+
if (typeof record3.const === "string") {
|
|
108582
|
+
return { values: [record3.const] };
|
|
108583
|
+
}
|
|
108584
|
+
return null;
|
|
108585
|
+
}
|
|
108586
|
+
function collapseStringUnion(branches) {
|
|
108587
|
+
const values = [];
|
|
108588
|
+
for (const item of branches) {
|
|
108589
|
+
const parsed2 = parseStringEnumBranch(item);
|
|
108590
|
+
if (!parsed2) return null;
|
|
108591
|
+
values.push(...parsed2.values);
|
|
108592
|
+
}
|
|
108593
|
+
if (values.length === 0) return null;
|
|
108594
|
+
return { type: "string", enum: [...new Set(values)] };
|
|
108595
|
+
}
|
|
108596
|
+
function sanitizeForGemini(schema2) {
|
|
108597
|
+
if (!schema2 || typeof schema2 !== "object") return schema2;
|
|
108598
|
+
if (Array.isArray(schema2)) return schema2.map(sanitizeForGemini);
|
|
108599
|
+
const source = schema2;
|
|
108600
|
+
if (Array.isArray(source.enum) && typeof source.type !== "string") {
|
|
108601
|
+
const allStrings = source.enum.every((v) => typeof v === "string");
|
|
108602
|
+
if (allStrings) {
|
|
108603
|
+
const result = { type: "string", enum: source.enum };
|
|
108604
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
108605
|
+
return result;
|
|
108606
|
+
}
|
|
108607
|
+
}
|
|
108608
|
+
for (const unionKey of ["anyOf", "oneOf"]) {
|
|
108609
|
+
const branches = source[unionKey];
|
|
108610
|
+
if (Array.isArray(branches) && branches.length > 0) {
|
|
108611
|
+
const collapsed = collapseStringUnion(branches);
|
|
108612
|
+
if (collapsed) {
|
|
108613
|
+
const result = { ...collapsed };
|
|
108614
|
+
if (typeof source.description === "string") result.description = source.description;
|
|
108615
|
+
return result;
|
|
108616
|
+
}
|
|
108617
|
+
}
|
|
108618
|
+
}
|
|
108619
|
+
if (Array.isArray(source.anyOf) || Array.isArray(source.oneOf)) {
|
|
108620
|
+
const result = {};
|
|
108621
|
+
if (Array.isArray(source.anyOf)) result.anyOf = source.anyOf.map(sanitizeForGemini);
|
|
108622
|
+
if (Array.isArray(source.oneOf)) result.oneOf = source.oneOf.map(sanitizeForGemini);
|
|
108623
|
+
return result;
|
|
108624
|
+
}
|
|
108625
|
+
const sanitized = {};
|
|
108626
|
+
for (const [key, value2] of Object.entries(source)) {
|
|
108627
|
+
if (key === "$schema") continue;
|
|
108628
|
+
if (key === "$defs") {
|
|
108629
|
+
sanitized.definitions = sanitizeForGemini(value2);
|
|
108630
|
+
continue;
|
|
108631
|
+
}
|
|
108632
|
+
sanitized[key] = sanitizeForGemini(value2);
|
|
108633
|
+
}
|
|
108634
|
+
return sanitized;
|
|
108635
|
+
}
|
|
108636
|
+
function wrapJsonSchemaProducer(producer) {
|
|
108637
|
+
return new Proxy(producer, {
|
|
108638
|
+
get(target, prop, receiver) {
|
|
108639
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
108640
|
+
if ((prop === "input" || prop === "output") && typeof value2 === "function") {
|
|
108641
|
+
const fn2 = value2;
|
|
108642
|
+
return (...args2) => sanitizeForGemini(fn2.apply(target, args2));
|
|
108643
|
+
}
|
|
108644
|
+
return value2;
|
|
108645
|
+
}
|
|
108646
|
+
});
|
|
108647
|
+
}
|
|
108648
|
+
function wrapStandard(standard) {
|
|
108649
|
+
return new Proxy(standard, {
|
|
108650
|
+
get(target, prop, receiver) {
|
|
108651
|
+
if (prop === "jsonSchema") {
|
|
108652
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
108653
|
+
if (value2 && typeof value2 === "object") {
|
|
108654
|
+
return wrapJsonSchemaProducer(value2);
|
|
108655
|
+
}
|
|
108656
|
+
return value2;
|
|
108657
|
+
}
|
|
108658
|
+
return Reflect.get(target, prop, receiver);
|
|
108659
|
+
}
|
|
108660
|
+
});
|
|
108661
|
+
}
|
|
108662
|
+
function wrapSchemaForGemini(schema2) {
|
|
108663
|
+
return new Proxy(schema2, {
|
|
108664
|
+
get(target, prop, receiver) {
|
|
108665
|
+
if (prop === "~standard") {
|
|
108666
|
+
const value2 = Reflect.get(target, prop, receiver);
|
|
108667
|
+
if (value2 && typeof value2 === "object") {
|
|
108668
|
+
return wrapStandard(value2);
|
|
108669
|
+
}
|
|
108670
|
+
return value2;
|
|
108671
|
+
}
|
|
108672
|
+
if (prop === "toJsonSchema") {
|
|
108673
|
+
const method = Reflect.get(target, prop, receiver);
|
|
108674
|
+
if (typeof method === "function") {
|
|
108675
|
+
return () => sanitizeForGemini(method.call(target));
|
|
108676
|
+
}
|
|
108677
|
+
return method;
|
|
108678
|
+
}
|
|
108679
|
+
return Reflect.get(target, prop, receiver);
|
|
108680
|
+
}
|
|
108681
|
+
});
|
|
108682
|
+
}
|
|
108683
|
+
function sanitizeToolForGemini(tool2) {
|
|
108684
|
+
if (!tool2.parameters) return tool2;
|
|
108685
|
+
return { ...tool2, parameters: wrapSchemaForGemini(tool2.parameters) };
|
|
108686
|
+
}
|
|
108687
|
+
function isGeminiRouted(ctx) {
|
|
108688
|
+
const effective = ctx.payload.proxyModel ?? ctx.resolvedModel ?? ctx.payload.model;
|
|
108689
|
+
if (!effective) return false;
|
|
108690
|
+
return effective.toLowerCase().includes("gemini");
|
|
108691
|
+
}
|
|
108692
|
+
|
|
108445
108693
|
// mcp/shared.ts
|
|
108446
108694
|
var tool = (toolDef) => toolDef;
|
|
108447
108695
|
var handleToolSuccess = (data) => {
|
|
@@ -108477,9 +108725,10 @@ var execute = (fn2, toolName) => {
|
|
|
108477
108725
|
};
|
|
108478
108726
|
return _fn;
|
|
108479
108727
|
};
|
|
108480
|
-
var addTools = (
|
|
108728
|
+
var addTools = (ctx, server, tools) => {
|
|
108729
|
+
const shouldSanitize = isGeminiRouted(ctx);
|
|
108481
108730
|
for (const tool2 of tools) {
|
|
108482
|
-
server.addTool(tool2);
|
|
108731
|
+
server.addTool(shouldSanitize ? sanitizeToolForGemini(tool2) : tool2);
|
|
108483
108732
|
}
|
|
108484
108733
|
return server;
|
|
108485
108734
|
};
|
|
@@ -108728,8 +108977,9 @@ function ReportProgressTool(ctx) {
|
|
|
108728
108977
|
if (!params.target_plan_comment && ctx.toolState.todoTracker) {
|
|
108729
108978
|
ctx.toolState.todoTracker.cancel();
|
|
108730
108979
|
await ctx.toolState.todoTracker.settled();
|
|
108731
|
-
ctx.toolState.todoTracker.
|
|
108732
|
-
|
|
108980
|
+
const collapsible = ctx.toolState.todoTracker.renderCollapsible({
|
|
108981
|
+
completeInProgress: true
|
|
108982
|
+
});
|
|
108733
108983
|
if (collapsible) {
|
|
108734
108984
|
body = `${body}
|
|
108735
108985
|
|
|
@@ -109116,8 +109366,27 @@ import { performance as performance3 } from "node:perf_hooks";
|
|
|
109116
109366
|
|
|
109117
109367
|
// utils/activity.ts
|
|
109118
109368
|
import { performance as performance2 } from "node:perf_hooks";
|
|
109369
|
+
function isMonitorDebugEnabled() {
|
|
109370
|
+
return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
|
|
109371
|
+
}
|
|
109119
109372
|
var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
|
|
109120
109373
|
var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
|
|
109374
|
+
var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
|
|
109375
|
+
var ACTIVITY_NOISE_PATTERNS = [
|
|
109376
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
|
|
109377
|
+
new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
|
|
109378
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
|
|
109379
|
+
/^::debug::(?:spawn|process) activity /
|
|
109380
|
+
];
|
|
109381
|
+
function isActivityNoise(chunk) {
|
|
109382
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
109383
|
+
if (!text.trim()) return true;
|
|
109384
|
+
return text.split("\n").every((line) => {
|
|
109385
|
+
const trimmed = line.trim();
|
|
109386
|
+
if (!trimmed) return true;
|
|
109387
|
+
return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
109388
|
+
});
|
|
109389
|
+
}
|
|
109121
109390
|
var _lastActivity = performance2.now();
|
|
109122
109391
|
function markActivity() {
|
|
109123
109392
|
_lastActivity = performance2.now();
|
|
@@ -109127,7 +109396,9 @@ function getIdleMs() {
|
|
|
109127
109396
|
}
|
|
109128
109397
|
function wrapWrite(original, onActivity) {
|
|
109129
109398
|
const wrapped = (chunk, encodingOrCb, cb) => {
|
|
109130
|
-
|
|
109399
|
+
if (!isActivityNoise(chunk)) {
|
|
109400
|
+
onActivity();
|
|
109401
|
+
}
|
|
109131
109402
|
if (typeof encodingOrCb === "function") {
|
|
109132
109403
|
return original(chunk, encodingOrCb);
|
|
109133
109404
|
}
|
|
@@ -109141,10 +109412,15 @@ function startProcessOutputMonitor(ctx) {
|
|
|
109141
109412
|
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
109142
109413
|
process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
|
|
109143
109414
|
process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
|
|
109144
|
-
|
|
109415
|
+
const debugBypass = (msg) => {
|
|
109416
|
+
if (!isMonitorDebugEnabled()) return;
|
|
109417
|
+
originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
|
|
109418
|
+
`);
|
|
109419
|
+
};
|
|
109420
|
+
debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
|
|
109145
109421
|
const intervalId = setInterval(() => {
|
|
109146
109422
|
const idleMs = getIdleMs();
|
|
109147
|
-
|
|
109423
|
+
debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
|
|
109148
109424
|
if (timedOut || idleMs <= ctx.timeoutMs) return;
|
|
109149
109425
|
timedOut = true;
|
|
109150
109426
|
ctx.onTimeout(idleMs);
|
|
@@ -109172,12 +109448,26 @@ function createProcessOutputActivityTimeout(ctx) {
|
|
|
109172
109448
|
if (monitor) {
|
|
109173
109449
|
monitor.stop();
|
|
109174
109450
|
}
|
|
109175
|
-
|
|
109451
|
+
const reject = rejectFn;
|
|
109452
|
+
rejectFn = null;
|
|
109453
|
+
reject(new Error(`activity timeout: no output for ${idleSec}s`));
|
|
109176
109454
|
}
|
|
109177
109455
|
});
|
|
109178
109456
|
return {
|
|
109179
109457
|
promise: promise2,
|
|
109180
|
-
stop
|
|
109458
|
+
// stop() also disarms forceReject so a late safety-net fire can't reject
|
|
109459
|
+
// the promise after the run has already succeeded.
|
|
109460
|
+
stop: () => {
|
|
109461
|
+
monitor?.stop();
|
|
109462
|
+
rejectFn = null;
|
|
109463
|
+
},
|
|
109464
|
+
forceReject: (reason) => {
|
|
109465
|
+
if (!rejectFn) return;
|
|
109466
|
+
monitor?.stop();
|
|
109467
|
+
const reject = rejectFn;
|
|
109468
|
+
rejectFn = null;
|
|
109469
|
+
reject(new Error(reason));
|
|
109470
|
+
}
|
|
109181
109471
|
};
|
|
109182
109472
|
}
|
|
109183
109473
|
|
|
@@ -109207,6 +109497,16 @@ function exitWithSignal(signal) {
|
|
|
109207
109497
|
}
|
|
109208
109498
|
|
|
109209
109499
|
// utils/subprocess.ts
|
|
109500
|
+
var SPAWN_TIMEOUT_CODE = "E_SPAWN_TIMEOUT";
|
|
109501
|
+
var SPAWN_ACTIVITY_TIMEOUT_CODE = "E_SPAWN_ACTIVITY_TIMEOUT";
|
|
109502
|
+
var SpawnTimeoutError = class extends Error {
|
|
109503
|
+
code;
|
|
109504
|
+
constructor(message, code) {
|
|
109505
|
+
super(message);
|
|
109506
|
+
this.name = "SpawnTimeoutError";
|
|
109507
|
+
this.code = code;
|
|
109508
|
+
}
|
|
109509
|
+
};
|
|
109210
109510
|
var activeChildren = /* @__PURE__ */ new Map();
|
|
109211
109511
|
var externalSignalHandler = null;
|
|
109212
109512
|
function trackChild(options) {
|
|
@@ -109252,7 +109552,7 @@ async function spawn(options) {
|
|
|
109252
109552
|
const startTime = performance3.now();
|
|
109253
109553
|
let stdoutBuffer = "";
|
|
109254
109554
|
let stderrBuffer = "";
|
|
109255
|
-
return new Promise((
|
|
109555
|
+
return new Promise((resolve3, reject) => {
|
|
109256
109556
|
const child = nodeSpawn(options.cmd, options.args, {
|
|
109257
109557
|
env: options.env || {
|
|
109258
109558
|
PATH: process.env.PATH || "",
|
|
@@ -109263,15 +109563,17 @@ async function spawn(options) {
|
|
|
109263
109563
|
});
|
|
109264
109564
|
trackChild({ child });
|
|
109265
109565
|
let timeoutId;
|
|
109566
|
+
let sigkillEscalatorId;
|
|
109266
109567
|
let activityCheckIntervalId;
|
|
109267
109568
|
let isTimedOut = false;
|
|
109268
109569
|
let isActivityTimedOut = false;
|
|
109269
109570
|
let lastActivityTime = performance3.now();
|
|
109571
|
+
let killedAtIdleMs;
|
|
109270
109572
|
if (options.timeout) {
|
|
109271
109573
|
timeoutId = setTimeout(() => {
|
|
109272
109574
|
isTimedOut = true;
|
|
109273
109575
|
child.kill("SIGTERM");
|
|
109274
|
-
setTimeout(() => {
|
|
109576
|
+
sigkillEscalatorId = setTimeout(() => {
|
|
109275
109577
|
if (!child.killed) {
|
|
109276
109578
|
child.kill("SIGKILL");
|
|
109277
109579
|
}
|
|
@@ -109289,12 +109591,20 @@ async function spawn(options) {
|
|
|
109289
109591
|
);
|
|
109290
109592
|
if (idleMs > activityTimeoutMs) {
|
|
109291
109593
|
isActivityTimedOut = true;
|
|
109594
|
+
killedAtIdleMs = idleMs;
|
|
109292
109595
|
const idleSec = Math.round(idleMs / 1e3);
|
|
109293
109596
|
log.info(
|
|
109294
109597
|
`no output for ${idleSec}s from pid=${child.pid} (${options.cmd}), killing process`
|
|
109295
109598
|
);
|
|
109296
109599
|
child.kill("SIGKILL");
|
|
109297
109600
|
clearInterval(activityCheckIntervalId);
|
|
109601
|
+
try {
|
|
109602
|
+
options.onActivityTimeout?.();
|
|
109603
|
+
} catch (err) {
|
|
109604
|
+
log.debug(
|
|
109605
|
+
`spawn onActivityTimeout handler threw: ${err instanceof Error ? err.message : String(err)}`
|
|
109606
|
+
);
|
|
109607
|
+
}
|
|
109298
109608
|
}
|
|
109299
109609
|
}, DEFAULT_ACTIVITY_CHECK_INTERVAL_MS);
|
|
109300
109610
|
}
|
|
@@ -109316,24 +109626,41 @@ async function spawn(options) {
|
|
|
109316
109626
|
options.onStderr?.(chunk);
|
|
109317
109627
|
});
|
|
109318
109628
|
}
|
|
109319
|
-
child.on("close", (exitCode) => {
|
|
109629
|
+
child.on("close", (exitCode, signal) => {
|
|
109320
109630
|
const durationMs = performance3.now() - startTime;
|
|
109321
109631
|
untrackChild(child);
|
|
109322
109632
|
if (timeoutId) clearTimeout(timeoutId);
|
|
109633
|
+
if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
|
|
109323
109634
|
if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
|
|
109324
109635
|
if (isTimedOut) {
|
|
109325
|
-
reject(
|
|
109636
|
+
reject(
|
|
109637
|
+
new SpawnTimeoutError(`process timed out after ${options.timeout}ms`, SPAWN_TIMEOUT_CODE)
|
|
109638
|
+
);
|
|
109326
109639
|
return;
|
|
109327
109640
|
}
|
|
109328
109641
|
if (isActivityTimedOut) {
|
|
109329
|
-
const
|
|
109330
|
-
|
|
109642
|
+
const idleMs = killedAtIdleMs ?? performance3.now() - lastActivityTime;
|
|
109643
|
+
const idleSec = Math.round(idleMs / 1e3);
|
|
109644
|
+
reject(
|
|
109645
|
+
new SpawnTimeoutError(
|
|
109646
|
+
`activity timeout: no output for ${idleSec}s`,
|
|
109647
|
+
SPAWN_ACTIVITY_TIMEOUT_CODE
|
|
109648
|
+
)
|
|
109649
|
+
);
|
|
109331
109650
|
return;
|
|
109332
109651
|
}
|
|
109333
|
-
|
|
109652
|
+
let resolvedExitCode = exitCode ?? 0;
|
|
109653
|
+
let resolvedStderr = stderrBuffer;
|
|
109654
|
+
if (exitCode === null && signal) {
|
|
109655
|
+
const killMsg = `[spawn] ${options.cmd}: killed by signal ${signal}`;
|
|
109656
|
+
resolvedStderr = resolvedStderr ? `${resolvedStderr}
|
|
109657
|
+
${killMsg}` : killMsg;
|
|
109658
|
+
resolvedExitCode = 1;
|
|
109659
|
+
}
|
|
109660
|
+
resolve3({
|
|
109334
109661
|
stdout: stdoutBuffer,
|
|
109335
|
-
stderr:
|
|
109336
|
-
exitCode:
|
|
109662
|
+
stderr: resolvedStderr,
|
|
109663
|
+
exitCode: resolvedExitCode,
|
|
109337
109664
|
durationMs
|
|
109338
109665
|
});
|
|
109339
109666
|
});
|
|
@@ -109341,9 +109668,13 @@ async function spawn(options) {
|
|
|
109341
109668
|
const durationMs = performance3.now() - startTime;
|
|
109342
109669
|
untrackChild(child);
|
|
109343
109670
|
if (timeoutId) clearTimeout(timeoutId);
|
|
109671
|
+
if (sigkillEscalatorId) clearTimeout(sigkillEscalatorId);
|
|
109344
109672
|
if (activityCheckIntervalId) clearInterval(activityCheckIntervalId);
|
|
109345
|
-
|
|
109346
|
-
|
|
109673
|
+
const errMsg = `[spawn] ${options.cmd}: ${error49.message}`;
|
|
109674
|
+
console.error(errMsg);
|
|
109675
|
+
stderrBuffer = stderrBuffer ? `${stderrBuffer}
|
|
109676
|
+
${errMsg}` : errMsg;
|
|
109677
|
+
resolve3({
|
|
109347
109678
|
stdout: stdoutBuffer,
|
|
109348
109679
|
stderr: stderrBuffer,
|
|
109349
109680
|
exitCode: 1,
|
|
@@ -114110,7 +114441,7 @@ var Protocol = class {
|
|
|
114110
114441
|
return;
|
|
114111
114442
|
}
|
|
114112
114443
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
114113
|
-
await new Promise((
|
|
114444
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
114114
114445
|
options?.signal?.throwIfAborted();
|
|
114115
114446
|
}
|
|
114116
114447
|
} catch (error49) {
|
|
@@ -114127,7 +114458,7 @@ var Protocol = class {
|
|
|
114127
114458
|
*/
|
|
114128
114459
|
request(request2, resultSchema, options) {
|
|
114129
114460
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
114130
|
-
return new Promise((
|
|
114461
|
+
return new Promise((resolve3, reject) => {
|
|
114131
114462
|
const earlyReject = (error49) => {
|
|
114132
114463
|
reject(error49);
|
|
114133
114464
|
};
|
|
@@ -114205,7 +114536,7 @@ var Protocol = class {
|
|
|
114205
114536
|
if (!parseResult.success) {
|
|
114206
114537
|
reject(parseResult.error);
|
|
114207
114538
|
} else {
|
|
114208
|
-
|
|
114539
|
+
resolve3(parseResult.data);
|
|
114209
114540
|
}
|
|
114210
114541
|
} catch (error49) {
|
|
114211
114542
|
reject(error49);
|
|
@@ -114466,12 +114797,12 @@ var Protocol = class {
|
|
|
114466
114797
|
}
|
|
114467
114798
|
} catch {
|
|
114468
114799
|
}
|
|
114469
|
-
return new Promise((
|
|
114800
|
+
return new Promise((resolve3, reject) => {
|
|
114470
114801
|
if (signal.aborted) {
|
|
114471
114802
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
114472
114803
|
return;
|
|
114473
114804
|
}
|
|
114474
|
-
const timeoutId = setTimeout(
|
|
114805
|
+
const timeoutId = setTimeout(resolve3, interval);
|
|
114475
114806
|
signal.addEventListener("abort", () => {
|
|
114476
114807
|
clearTimeout(timeoutId);
|
|
114477
114808
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -115341,12 +115672,12 @@ var StdioServerTransport = class {
|
|
|
115341
115672
|
this.onclose?.();
|
|
115342
115673
|
}
|
|
115343
115674
|
send(message) {
|
|
115344
|
-
return new Promise((
|
|
115675
|
+
return new Promise((resolve3) => {
|
|
115345
115676
|
const json4 = serializeMessage(message);
|
|
115346
115677
|
if (this._stdout.write(json4)) {
|
|
115347
|
-
|
|
115678
|
+
resolve3();
|
|
115348
115679
|
} else {
|
|
115349
|
-
this._stdout.once("drain",
|
|
115680
|
+
this._stdout.once("drain", resolve3);
|
|
115350
115681
|
}
|
|
115351
115682
|
});
|
|
115352
115683
|
}
|
|
@@ -136374,12 +136705,12 @@ var require_schemes2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
136374
136705
|
var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
136375
136706
|
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils5();
|
|
136376
136707
|
const { SCHEMES, getSchemeHandler } = require_schemes2();
|
|
136377
|
-
function
|
|
136708
|
+
function normalize3(uri$2, options) {
|
|
136378
136709
|
if (typeof uri$2 === "string") uri$2 = serialize(parse5(uri$2, options), options);
|
|
136379
136710
|
else if (typeof uri$2 === "object") uri$2 = parse5(serialize(uri$2, options), options);
|
|
136380
136711
|
return uri$2;
|
|
136381
136712
|
}
|
|
136382
|
-
function
|
|
136713
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
136383
136714
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
136384
136715
|
const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
136385
136716
|
schemelessOptions.skipEscape = true;
|
|
@@ -136552,8 +136883,8 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
136552
136883
|
}
|
|
136553
136884
|
const fastUri = {
|
|
136554
136885
|
SCHEMES,
|
|
136555
|
-
normalize:
|
|
136556
|
-
resolve:
|
|
136886
|
+
normalize: normalize3,
|
|
136887
|
+
resolve: resolve3,
|
|
136557
136888
|
resolveComponent,
|
|
136558
136889
|
equal: equal$1,
|
|
136559
136890
|
serialize,
|
|
@@ -140141,7 +140472,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140141
140472
|
new Error(`Connection is in ${this.#connectionState} state`)
|
|
140142
140473
|
);
|
|
140143
140474
|
}
|
|
140144
|
-
return new Promise((
|
|
140475
|
+
return new Promise((resolve3, reject) => {
|
|
140145
140476
|
const timeout = setTimeout(() => {
|
|
140146
140477
|
reject(
|
|
140147
140478
|
new Error(
|
|
@@ -140151,7 +140482,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140151
140482
|
}, 5e3);
|
|
140152
140483
|
this.once("ready", () => {
|
|
140153
140484
|
clearTimeout(timeout);
|
|
140154
|
-
|
|
140485
|
+
resolve3();
|
|
140155
140486
|
});
|
|
140156
140487
|
this.once("error", (event) => {
|
|
140157
140488
|
clearTimeout(timeout);
|
|
@@ -140598,7 +140929,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140598
140929
|
}
|
|
140599
140930
|
});
|
|
140600
140931
|
if (this.#needsEventLoopFlush) {
|
|
140601
|
-
await new Promise((
|
|
140932
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
140602
140933
|
}
|
|
140603
140934
|
} catch (progressError) {
|
|
140604
140935
|
this.#logger.warn(
|
|
@@ -140656,7 +140987,7 @@ ${error49 instanceof Error ? error49.stack : JSON.stringify(error49)}`
|
|
|
140656
140987
|
}
|
|
140657
140988
|
});
|
|
140658
140989
|
if (this.#needsEventLoopFlush) {
|
|
140659
|
-
await new Promise((
|
|
140990
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
140660
140991
|
}
|
|
140661
140992
|
} catch (streamError) {
|
|
140662
140993
|
this.#logger.warn(
|
|
@@ -141661,7 +141992,7 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
141661
141992
|
switch (agentId) {
|
|
141662
141993
|
case "claude":
|
|
141663
141994
|
return `mcp__${pullfrogMcpName}__${toolName}`;
|
|
141664
|
-
case "
|
|
141995
|
+
case "opencode":
|
|
141665
141996
|
return `${pullfrogMcpName}_${toolName}`;
|
|
141666
141997
|
default:
|
|
141667
141998
|
return agentId;
|
|
@@ -141669,7 +142000,7 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
141669
142000
|
}
|
|
141670
142001
|
|
|
141671
142002
|
// utils/browser.ts
|
|
141672
|
-
import { execFileSync, spawnSync } from "node:child_process";
|
|
142003
|
+
import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
|
|
141673
142004
|
import { existsSync as existsSync4 } from "node:fs";
|
|
141674
142005
|
import { dirname } from "node:path";
|
|
141675
142006
|
|
|
@@ -141684,12 +142015,77 @@ var SENSITIVE_PATTERNS = [
|
|
|
141684
142015
|
function isSensitiveEnvName(key) {
|
|
141685
142016
|
return SENSITIVE_PATTERNS.some((p) => p.test(key));
|
|
141686
142017
|
}
|
|
142018
|
+
var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
|
|
142019
|
+
var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
142020
|
+
// system
|
|
142021
|
+
"CI",
|
|
142022
|
+
"HOME",
|
|
142023
|
+
"LANG",
|
|
142024
|
+
"LOGNAME",
|
|
142025
|
+
"PATH",
|
|
142026
|
+
"SHELL",
|
|
142027
|
+
"SHLVL",
|
|
142028
|
+
"TERM",
|
|
142029
|
+
"TMPDIR",
|
|
142030
|
+
"TZ",
|
|
142031
|
+
"USER",
|
|
142032
|
+
"XDG_CONFIG_HOME",
|
|
142033
|
+
"XDG_RUNTIME_DIR",
|
|
142034
|
+
"DEBIAN_FRONTEND",
|
|
142035
|
+
// runner image toolchain
|
|
142036
|
+
"ACCEPT_EULA",
|
|
142037
|
+
"AGENT_TOOLSDIRECTORY",
|
|
142038
|
+
"ANDROID_HOME",
|
|
142039
|
+
"ANDROID_NDK",
|
|
142040
|
+
"ANDROID_NDK_HOME",
|
|
142041
|
+
"ANDROID_NDK_LATEST_HOME",
|
|
142042
|
+
"ANDROID_NDK_ROOT",
|
|
142043
|
+
"ANDROID_SDK_ROOT",
|
|
142044
|
+
"ANT_HOME",
|
|
142045
|
+
"AZURE_EXTENSION_DIR",
|
|
142046
|
+
"BOOTSTRAP_HASKELL_NONINTERACTIVE",
|
|
142047
|
+
"CHROME_BIN",
|
|
142048
|
+
"CHROMEWEBDRIVER",
|
|
142049
|
+
"CONDA",
|
|
142050
|
+
"DOTNET_MULTILEVEL_LOOKUP",
|
|
142051
|
+
"DOTNET_NOLOGO",
|
|
142052
|
+
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
|
|
142053
|
+
"EDGEWEBDRIVER",
|
|
142054
|
+
"GECKOWEBDRIVER",
|
|
142055
|
+
"GHCUP_INSTALL_BASE_PREFIX",
|
|
142056
|
+
"GRADLE_HOME",
|
|
142057
|
+
"JAVA_HOME",
|
|
142058
|
+
"HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
|
|
142059
|
+
"HOMEBREW_NO_AUTO_UPDATE",
|
|
142060
|
+
"ImageOS",
|
|
142061
|
+
"ImageVersion",
|
|
142062
|
+
"NVM_DIR",
|
|
142063
|
+
"PIPX_BIN_DIR",
|
|
142064
|
+
"PIPX_HOME",
|
|
142065
|
+
"PSModulePath",
|
|
142066
|
+
"SELENIUM_JAR_PATH",
|
|
142067
|
+
"SGX_AESM_ADDR",
|
|
142068
|
+
"SWIFT_PATH",
|
|
142069
|
+
"VCPKG_INSTALLATION_ROOT"
|
|
142070
|
+
]);
|
|
142071
|
+
var _userAllowlist = null;
|
|
142072
|
+
function setEnvAllowlist(raw2) {
|
|
142073
|
+
const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
142074
|
+
_userAllowlist = new Set(names);
|
|
142075
|
+
}
|
|
142076
|
+
function isSafeEnvVar(key) {
|
|
142077
|
+
if (SAFE_ENV_NAMES.has(key)) return true;
|
|
142078
|
+
return SAFE_ENV_PREFIXES.some((p) => key.startsWith(p));
|
|
142079
|
+
}
|
|
141687
142080
|
function filterEnv() {
|
|
141688
142081
|
const filtered = {};
|
|
141689
142082
|
for (const [key, value2] of Object.entries(process.env)) {
|
|
141690
142083
|
if (value2 === void 0) continue;
|
|
141691
|
-
|
|
141692
|
-
|
|
142084
|
+
const userAllowed = _userAllowlist?.has(key) ?? false;
|
|
142085
|
+
if (isSensitiveEnvName(key) && !userAllowed) continue;
|
|
142086
|
+
if (isSafeEnvVar(key) || userAllowed) {
|
|
142087
|
+
filtered[key] = value2;
|
|
142088
|
+
}
|
|
141693
142089
|
}
|
|
141694
142090
|
return filtered;
|
|
141695
142091
|
}
|
|
@@ -141709,7 +142105,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
141709
142105
|
// package.json
|
|
141710
142106
|
var package_default = {
|
|
141711
142107
|
name: "pullfrog",
|
|
141712
|
-
version: "0.0.
|
|
142108
|
+
version: "0.0.202",
|
|
141713
142109
|
type: "module",
|
|
141714
142110
|
bin: {
|
|
141715
142111
|
pullfrog: "dist/cli.mjs",
|
|
@@ -141721,6 +142117,7 @@ var package_default = {
|
|
|
141721
142117
|
],
|
|
141722
142118
|
scripts: {
|
|
141723
142119
|
test: "vitest",
|
|
142120
|
+
"test:catalog": "vitest run --config vitest.main.config.ts",
|
|
141724
142121
|
typecheck: "tsc --noEmit",
|
|
141725
142122
|
build: "node esbuild.config.js && tsc -p tsconfig.exports.json",
|
|
141726
142123
|
"check:entrypoints": "node scripts/check-entrypoint-imports.ts",
|
|
@@ -141733,7 +142130,7 @@ var package_default = {
|
|
|
141733
142130
|
},
|
|
141734
142131
|
devDependencies: {
|
|
141735
142132
|
"@actions/core": "^1.11.1",
|
|
141736
|
-
"@anthropic-ai/claude-code": "2.1.
|
|
142133
|
+
"@anthropic-ai/claude-code": "2.1.112",
|
|
141737
142134
|
"@ark/fs": "0.56.0",
|
|
141738
142135
|
"@ark/util": "0.56.0",
|
|
141739
142136
|
"@clack/prompts": "^1.2.0",
|
|
@@ -141857,7 +142254,7 @@ function ensureBrowserDaemon(toolState) {
|
|
|
141857
142254
|
log.info("agent-browser installed");
|
|
141858
142255
|
let binDir;
|
|
141859
142256
|
try {
|
|
141860
|
-
const binPath =
|
|
142257
|
+
const binPath = execFileSync2("which", ["agent-browser"], { encoding: "utf-8" }).trim();
|
|
141861
142258
|
binDir = dirname(binPath);
|
|
141862
142259
|
log.info(`agent-browser binary: ${binPath}`);
|
|
141863
142260
|
} catch {
|
|
@@ -141904,9 +142301,271 @@ function closeBrowserDaemon(toolState) {
|
|
|
141904
142301
|
}
|
|
141905
142302
|
|
|
141906
142303
|
// mcp/checkout.ts
|
|
142304
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
141907
142305
|
import { writeFileSync } from "node:fs";
|
|
141908
142306
|
import { join as join3 } from "node:path";
|
|
141909
142307
|
|
|
142308
|
+
// utils/diffCoverage.ts
|
|
142309
|
+
import { isAbsolute, normalize as normalize2, resolve } from "node:path";
|
|
142310
|
+
function countLines(params) {
|
|
142311
|
+
const content = params.content;
|
|
142312
|
+
if (content.length === 0) return 0;
|
|
142313
|
+
return content.split("\n").length;
|
|
142314
|
+
}
|
|
142315
|
+
function parseDiffTocEntries(params) {
|
|
142316
|
+
const lines = params.toc.split("\n");
|
|
142317
|
+
const entries = [];
|
|
142318
|
+
for (const line of lines) {
|
|
142319
|
+
const match3 = line.match(/^- (.+) (?:→|->) lines (\d+)-(\d+)(?: · diff-[0-9a-f]+)?$/);
|
|
142320
|
+
if (!match3) continue;
|
|
142321
|
+
const startLine = Number.parseInt(match3[2], 10);
|
|
142322
|
+
const endLine = Number.parseInt(match3[3], 10);
|
|
142323
|
+
if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) continue;
|
|
142324
|
+
entries.push({ filename: match3[1], startLine, endLine });
|
|
142325
|
+
}
|
|
142326
|
+
return entries;
|
|
142327
|
+
}
|
|
142328
|
+
function createDiffCoverageState(params) {
|
|
142329
|
+
return {
|
|
142330
|
+
diffPath: params.diffPath,
|
|
142331
|
+
totalLines: params.totalLines,
|
|
142332
|
+
tocEntries: parseDiffTocEntries({ toc: params.toc }),
|
|
142333
|
+
coveredRanges: [],
|
|
142334
|
+
coveragePreflightRan: false
|
|
142335
|
+
};
|
|
142336
|
+
}
|
|
142337
|
+
function recordDiffReadFromToolUse(params) {
|
|
142338
|
+
const state = params.state;
|
|
142339
|
+
if (!state) return false;
|
|
142340
|
+
if (!isReadTool(params.toolName)) return false;
|
|
142341
|
+
const readTarget = extractReadTarget({ input: params.input });
|
|
142342
|
+
if (!readTarget) return false;
|
|
142343
|
+
const normalizedReadPath = normalizePath({ path: readTarget.path, cwd: params.cwd });
|
|
142344
|
+
const normalizedDiffPath = normalize2(state.diffPath);
|
|
142345
|
+
if (normalizedReadPath !== normalizedDiffPath) return false;
|
|
142346
|
+
const range2 = resolveReadRange({
|
|
142347
|
+
totalLines: state.totalLines,
|
|
142348
|
+
offset: readTarget.offset,
|
|
142349
|
+
limit: readTarget.limit,
|
|
142350
|
+
startLine: readTarget.startLine,
|
|
142351
|
+
endLine: readTarget.endLine,
|
|
142352
|
+
offsetBase: resolveOffsetBase({ toolName: params.toolName })
|
|
142353
|
+
});
|
|
142354
|
+
if (!range2) return false;
|
|
142355
|
+
state.coveredRanges = mergeRanges({ ranges: state.coveredRanges, nextRange: range2 });
|
|
142356
|
+
return true;
|
|
142357
|
+
}
|
|
142358
|
+
function getDiffCoverageBreakdown(params) {
|
|
142359
|
+
const state = params.state;
|
|
142360
|
+
const coveredRanges = mergeRangesList({ ranges: state.coveredRanges });
|
|
142361
|
+
const unreadRanges = invertRanges({ totalLines: state.totalLines, coveredRanges });
|
|
142362
|
+
const coveredLines = countLinesInRanges({ ranges: coveredRanges });
|
|
142363
|
+
const unreadLines = Math.max(0, state.totalLines - coveredLines);
|
|
142364
|
+
const coveragePercent = state.totalLines ? Number((coveredLines / state.totalLines * 100).toFixed(2)) : 100;
|
|
142365
|
+
const files = [];
|
|
142366
|
+
for (const entry of state.tocEntries) {
|
|
142367
|
+
const fileRange = { startLine: entry.startLine, endLine: entry.endLine };
|
|
142368
|
+
const coveredInFile = intersectRangesWithRange({ ranges: coveredRanges, target: fileRange });
|
|
142369
|
+
const unreadInFile = intersectRangesWithRange({ ranges: unreadRanges, target: fileRange });
|
|
142370
|
+
const totalFileLines = Math.max(0, entry.endLine - entry.startLine + 1);
|
|
142371
|
+
const fileCoveredLines = countLinesInRanges({ ranges: coveredInFile });
|
|
142372
|
+
files.push({
|
|
142373
|
+
filename: entry.filename,
|
|
142374
|
+
startLine: entry.startLine,
|
|
142375
|
+
endLine: entry.endLine,
|
|
142376
|
+
totalLines: totalFileLines,
|
|
142377
|
+
coveredLines: fileCoveredLines,
|
|
142378
|
+
coveredRanges: coveredInFile,
|
|
142379
|
+
unreadRanges: unreadInFile
|
|
142380
|
+
});
|
|
142381
|
+
}
|
|
142382
|
+
return {
|
|
142383
|
+
totalLines: state.totalLines,
|
|
142384
|
+
coveredLines,
|
|
142385
|
+
unreadLines,
|
|
142386
|
+
coveragePercent,
|
|
142387
|
+
coveredRanges,
|
|
142388
|
+
unreadRanges,
|
|
142389
|
+
files
|
|
142390
|
+
};
|
|
142391
|
+
}
|
|
142392
|
+
function renderDiffCoverageBreakdown(params) {
|
|
142393
|
+
const breakdown = params.breakdown;
|
|
142394
|
+
const lines = [];
|
|
142395
|
+
lines.push(`diff coverage report for \`${params.diffPath}\``);
|
|
142396
|
+
lines.push(
|
|
142397
|
+
`overall: ${breakdown.coveredLines}/${breakdown.totalLines} lines read (${breakdown.coveragePercent}%), unread: ${breakdown.unreadLines}`
|
|
142398
|
+
);
|
|
142399
|
+
lines.push(`covered ranges: ${formatRanges({ ranges: breakdown.coveredRanges })}`);
|
|
142400
|
+
lines.push(`unread ranges: ${formatRanges({ ranges: breakdown.unreadRanges })}`);
|
|
142401
|
+
lines.push("");
|
|
142402
|
+
lines.push("per-file TOC coverage:");
|
|
142403
|
+
for (const file2 of breakdown.files) {
|
|
142404
|
+
const filePercent = file2.totalLines ? Number((file2.coveredLines / file2.totalLines * 100).toFixed(2)) : 100;
|
|
142405
|
+
lines.push(
|
|
142406
|
+
`- ${file2.filename} (toc lines ${file2.startLine}-${file2.endLine}): ${file2.coveredLines}/${file2.totalLines} lines read (${filePercent}%)`
|
|
142407
|
+
);
|
|
142408
|
+
lines.push(` read: ${formatRanges({ ranges: file2.coveredRanges })}`);
|
|
142409
|
+
lines.push(` unread: ${formatRanges({ ranges: file2.unreadRanges })}`);
|
|
142410
|
+
}
|
|
142411
|
+
return lines.join("\n");
|
|
142412
|
+
}
|
|
142413
|
+
function resolveOffsetBase(params) {
|
|
142414
|
+
const lower2 = params.toolName.toLowerCase();
|
|
142415
|
+
if (lower2 === "readfile" || lower2.endsWith(".readfile")) {
|
|
142416
|
+
return "one";
|
|
142417
|
+
}
|
|
142418
|
+
return "zero";
|
|
142419
|
+
}
|
|
142420
|
+
function isReadTool(toolName) {
|
|
142421
|
+
const lower2 = toolName.toLowerCase();
|
|
142422
|
+
if (lower2 === "read" || lower2 === "readfile") return true;
|
|
142423
|
+
if (lower2.endsWith(".read") || lower2.endsWith(".readfile")) return true;
|
|
142424
|
+
return false;
|
|
142425
|
+
}
|
|
142426
|
+
function extractReadTarget(params) {
|
|
142427
|
+
const inputRecord = asRecord(params.input);
|
|
142428
|
+
if (!inputRecord) return null;
|
|
142429
|
+
const direct = extractReadTargetFromRecord({ record: inputRecord });
|
|
142430
|
+
if (direct) return direct;
|
|
142431
|
+
const nestedCandidates = [inputRecord.args, inputRecord.params, inputRecord.input];
|
|
142432
|
+
for (const candidate of nestedCandidates) {
|
|
142433
|
+
const nestedRecord = asRecord(candidate);
|
|
142434
|
+
if (!nestedRecord) continue;
|
|
142435
|
+
const nested = extractReadTargetFromRecord({ record: nestedRecord });
|
|
142436
|
+
if (nested) return nested;
|
|
142437
|
+
}
|
|
142438
|
+
return null;
|
|
142439
|
+
}
|
|
142440
|
+
function extractReadTargetFromRecord(params) {
|
|
142441
|
+
const record3 = params.record;
|
|
142442
|
+
const pathValue = readString({ value: record3.path }) ?? readString({ value: record3.file_path }) ?? readString({ value: record3.filePath }) ?? readString({ value: record3.filepath }) ?? readString({ value: record3.file }) ?? readString({ value: record3.target_file });
|
|
142443
|
+
if (!pathValue) return null;
|
|
142444
|
+
const offset = readNumber({ value: record3.offset });
|
|
142445
|
+
const limit = readNumber({ value: record3.limit });
|
|
142446
|
+
const startLine = readNumber({ value: record3.start_line }) ?? readNumber({ value: record3.startLine }) ?? readNumber({ value: record3.line_start });
|
|
142447
|
+
const endLine = readNumber({ value: record3.end_line }) ?? readNumber({ value: record3.endLine }) ?? readNumber({ value: record3.line_end });
|
|
142448
|
+
return { path: pathValue, offset, limit, startLine, endLine };
|
|
142449
|
+
}
|
|
142450
|
+
function resolveReadRange(params) {
|
|
142451
|
+
const totalLines = params.totalLines;
|
|
142452
|
+
if (totalLines <= 0) return null;
|
|
142453
|
+
if (params.startLine !== void 0 || params.endLine !== void 0) {
|
|
142454
|
+
const rawStart = params.startLine ?? 1;
|
|
142455
|
+
const rawEnd = params.endLine ?? totalLines;
|
|
142456
|
+
const startLine2 = clampLine({ value: rawStart, totalLines });
|
|
142457
|
+
const endLine2 = clampLine({ value: rawEnd, totalLines });
|
|
142458
|
+
if (endLine2 < startLine2) return null;
|
|
142459
|
+
return { startLine: startLine2, endLine: endLine2 };
|
|
142460
|
+
}
|
|
142461
|
+
let startLine = 1;
|
|
142462
|
+
if (params.offset !== void 0) {
|
|
142463
|
+
if (params.offset >= 0) {
|
|
142464
|
+
const normalizedOffset = params.offsetBase === "zero" ? params.offset + 1 : params.offset === 0 ? 1 : params.offset;
|
|
142465
|
+
startLine = clampLine({ value: normalizedOffset, totalLines });
|
|
142466
|
+
} else {
|
|
142467
|
+
startLine = clampLine({ value: totalLines + params.offset + 1, totalLines });
|
|
142468
|
+
}
|
|
142469
|
+
}
|
|
142470
|
+
let endLine = totalLines;
|
|
142471
|
+
if (params.limit !== void 0) {
|
|
142472
|
+
if (params.limit <= 0) return null;
|
|
142473
|
+
endLine = clampLine({ value: startLine + params.limit - 1, totalLines });
|
|
142474
|
+
}
|
|
142475
|
+
if (endLine < startLine) return null;
|
|
142476
|
+
return { startLine, endLine };
|
|
142477
|
+
}
|
|
142478
|
+
function normalizePath(params) {
|
|
142479
|
+
if (isAbsolute(params.path)) return normalize2(params.path);
|
|
142480
|
+
return normalize2(resolve(params.cwd, params.path));
|
|
142481
|
+
}
|
|
142482
|
+
function mergeRanges(params) {
|
|
142483
|
+
return mergeRangesList({ ranges: [...params.ranges, params.nextRange] });
|
|
142484
|
+
}
|
|
142485
|
+
function mergeRangesList(params) {
|
|
142486
|
+
if (params.ranges.length === 0) return [];
|
|
142487
|
+
const sorted = [...params.ranges].sort((a, b) => a.startLine - b.startLine);
|
|
142488
|
+
const merged = [];
|
|
142489
|
+
for (const range2 of sorted) {
|
|
142490
|
+
const last = merged[merged.length - 1];
|
|
142491
|
+
if (!last) {
|
|
142492
|
+
merged.push({ startLine: range2.startLine, endLine: range2.endLine });
|
|
142493
|
+
continue;
|
|
142494
|
+
}
|
|
142495
|
+
if (range2.startLine <= last.endLine + 1) {
|
|
142496
|
+
if (range2.endLine > last.endLine) {
|
|
142497
|
+
last.endLine = range2.endLine;
|
|
142498
|
+
}
|
|
142499
|
+
continue;
|
|
142500
|
+
}
|
|
142501
|
+
merged.push({ startLine: range2.startLine, endLine: range2.endLine });
|
|
142502
|
+
}
|
|
142503
|
+
return merged;
|
|
142504
|
+
}
|
|
142505
|
+
function invertRanges(params) {
|
|
142506
|
+
if (params.totalLines <= 0) return [];
|
|
142507
|
+
if (params.coveredRanges.length === 0) {
|
|
142508
|
+
return [{ startLine: 1, endLine: params.totalLines }];
|
|
142509
|
+
}
|
|
142510
|
+
const unread = [];
|
|
142511
|
+
let cursor = 1;
|
|
142512
|
+
for (const range2 of params.coveredRanges) {
|
|
142513
|
+
if (cursor < range2.startLine) {
|
|
142514
|
+
unread.push({ startLine: cursor, endLine: range2.startLine - 1 });
|
|
142515
|
+
}
|
|
142516
|
+
cursor = Math.max(cursor, range2.endLine + 1);
|
|
142517
|
+
}
|
|
142518
|
+
if (cursor <= params.totalLines) {
|
|
142519
|
+
unread.push({ startLine: cursor, endLine: params.totalLines });
|
|
142520
|
+
}
|
|
142521
|
+
return unread;
|
|
142522
|
+
}
|
|
142523
|
+
function intersectRangesWithRange(params) {
|
|
142524
|
+
const intersections = [];
|
|
142525
|
+
for (const range2 of params.ranges) {
|
|
142526
|
+
if (range2.endLine < params.target.startLine) continue;
|
|
142527
|
+
if (range2.startLine > params.target.endLine) continue;
|
|
142528
|
+
const startLine = Math.max(range2.startLine, params.target.startLine);
|
|
142529
|
+
const endLine = Math.min(range2.endLine, params.target.endLine);
|
|
142530
|
+
if (endLine >= startLine) {
|
|
142531
|
+
intersections.push({ startLine, endLine });
|
|
142532
|
+
}
|
|
142533
|
+
}
|
|
142534
|
+
return intersections;
|
|
142535
|
+
}
|
|
142536
|
+
function countLinesInRanges(params) {
|
|
142537
|
+
let total = 0;
|
|
142538
|
+
for (const range2 of params.ranges) {
|
|
142539
|
+
total += range2.endLine - range2.startLine + 1;
|
|
142540
|
+
}
|
|
142541
|
+
return total;
|
|
142542
|
+
}
|
|
142543
|
+
function formatRanges(params) {
|
|
142544
|
+
if (params.ranges.length === 0) return "none";
|
|
142545
|
+
return params.ranges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
|
|
142546
|
+
}
|
|
142547
|
+
function clampLine(params) {
|
|
142548
|
+
if (params.value < 1) return 1;
|
|
142549
|
+
if (params.value > params.totalLines) return params.totalLines;
|
|
142550
|
+
return params.value;
|
|
142551
|
+
}
|
|
142552
|
+
function asRecord(value2) {
|
|
142553
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
142554
|
+
return Object.fromEntries(Object.entries(value2));
|
|
142555
|
+
}
|
|
142556
|
+
function readString(params) {
|
|
142557
|
+
if (typeof params.value === "string") return params.value;
|
|
142558
|
+
return void 0;
|
|
142559
|
+
}
|
|
142560
|
+
function readNumber(params) {
|
|
142561
|
+
if (typeof params.value === "number" && Number.isFinite(params.value)) return params.value;
|
|
142562
|
+
if (typeof params.value === "string") {
|
|
142563
|
+
const parsed2 = Number.parseInt(params.value, 10);
|
|
142564
|
+
if (Number.isFinite(parsed2)) return parsed2;
|
|
142565
|
+
}
|
|
142566
|
+
return void 0;
|
|
142567
|
+
}
|
|
142568
|
+
|
|
141910
142569
|
// utils/gitAuth.ts
|
|
141911
142570
|
import { execSync } from "node:child_process";
|
|
141912
142571
|
import { createHash } from "node:crypto";
|
|
@@ -141920,7 +142579,7 @@ function resolveGit() {
|
|
|
141920
142579
|
const resolvedPath = realpathSync(whichPath);
|
|
141921
142580
|
const sha256 = hashFile(resolvedPath);
|
|
141922
142581
|
gitBinary = { path: resolvedPath, sha256 };
|
|
141923
|
-
log.
|
|
142582
|
+
log.debug(`\xBB git binary: ${resolvedPath} (sha256: ${sha256.slice(0, 12)}...)`);
|
|
141924
142583
|
}
|
|
141925
142584
|
function verifyGitBinary() {
|
|
141926
142585
|
if (!gitBinary) {
|
|
@@ -141999,29 +142658,43 @@ async function $git(subcommand, args2, options) {
|
|
|
141999
142658
|
}
|
|
142000
142659
|
|
|
142001
142660
|
// lifecycle.ts
|
|
142002
|
-
var LIFECYCLE_HOOK_TIMEOUT_MS =
|
|
142661
|
+
var LIFECYCLE_HOOK_TIMEOUT_MS = 6e5;
|
|
142003
142662
|
|
|
142004
142663
|
// utils/lifecycle.ts
|
|
142005
142664
|
async function executeLifecycleHook(params) {
|
|
142006
|
-
if (!params.script) return;
|
|
142665
|
+
if (!params.script) return {};
|
|
142007
142666
|
log.info(`\xBB executing ${params.event} lifecycle hook...`);
|
|
142008
|
-
|
|
142009
|
-
|
|
142010
|
-
|
|
142011
|
-
|
|
142012
|
-
|
|
142013
|
-
|
|
142014
|
-
|
|
142015
|
-
|
|
142016
|
-
|
|
142017
|
-
|
|
142018
|
-
|
|
142019
|
-
|
|
142020
|
-
|
|
142021
|
-
${output}
|
|
142022
|
-
|
|
142667
|
+
try {
|
|
142668
|
+
const result = await spawn({
|
|
142669
|
+
cmd: "bash",
|
|
142670
|
+
args: ["-c", params.script],
|
|
142671
|
+
env: process.env,
|
|
142672
|
+
timeout: LIFECYCLE_HOOK_TIMEOUT_MS,
|
|
142673
|
+
activityTimeout: 0,
|
|
142674
|
+
onStdout: (chunk) => process.stdout.write(chunk),
|
|
142675
|
+
onStderr: (chunk) => process.stderr.write(chunk)
|
|
142676
|
+
});
|
|
142677
|
+
if (result.exitCode !== 0) {
|
|
142678
|
+
const output = (result.stderr || result.stdout).trim();
|
|
142679
|
+
return {
|
|
142680
|
+
warning: `lifecycle hook '${params.event}' failed with exit code ${result.exitCode}. output: ${output || "(empty)"}. retry the operation if the failure looks flaky (network blips, transient rate limits). do NOT retry if the script is broken (missing commands, syntax errors) or the error is persistent.`
|
|
142681
|
+
};
|
|
142682
|
+
}
|
|
142683
|
+
log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
142684
|
+
return {};
|
|
142685
|
+
} catch (err) {
|
|
142686
|
+
const isTimeout = err instanceof SpawnTimeoutError && (err.code === SPAWN_TIMEOUT_CODE || err.code === SPAWN_ACTIVITY_TIMEOUT_CODE);
|
|
142687
|
+
if (isTimeout) {
|
|
142688
|
+
const minutes = Math.round(LIFECYCLE_HOOK_TIMEOUT_MS / 6e4);
|
|
142689
|
+
return {
|
|
142690
|
+
warning: `lifecycle hook '${params.event}' timed out after ${minutes}min. do NOT retry \u2014 the script is likely hung or doing too much work. ask the repo owner to simplify the hook (e.g. move long-running work out of the hook, add caching, or split it).`
|
|
142691
|
+
};
|
|
142692
|
+
}
|
|
142693
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
142694
|
+
return {
|
|
142695
|
+
warning: `lifecycle hook '${params.event}' failed to spawn: ${msg}. this is likely a transient failure \u2014 retry the operation.`
|
|
142696
|
+
};
|
|
142023
142697
|
}
|
|
142024
|
-
log.info(`\xBB ${params.event} lifecycle hook completed successfully`);
|
|
142025
142698
|
}
|
|
142026
142699
|
|
|
142027
142700
|
// utils/shell.ts
|
|
@@ -142171,6 +142844,787 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
|
|
|
142171
142844
|
return hasChanges ? out : null;
|
|
142172
142845
|
}
|
|
142173
142846
|
|
|
142847
|
+
// mcp/git.ts
|
|
142848
|
+
function getPushDestination(branch, storedDest) {
|
|
142849
|
+
if (storedDest && storedDest.localBranch === branch) {
|
|
142850
|
+
log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
|
|
142851
|
+
const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
|
|
142852
|
+
log: false
|
|
142853
|
+
}).trim();
|
|
142854
|
+
return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
|
|
142855
|
+
}
|
|
142856
|
+
try {
|
|
142857
|
+
const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
|
|
142858
|
+
const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
|
|
142859
|
+
const remoteBranch = merge4.replace(/^refs\/heads\//, "");
|
|
142860
|
+
const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
|
|
142861
|
+
return { remoteName: pushRemote, remoteBranch, url: url4 };
|
|
142862
|
+
} catch {
|
|
142863
|
+
log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
|
|
142864
|
+
const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
|
|
142865
|
+
return { remoteName: "origin", remoteBranch: branch, url: url4 };
|
|
142866
|
+
}
|
|
142867
|
+
}
|
|
142868
|
+
function normalizeUrl(url4) {
|
|
142869
|
+
return url4.replace(/\.git$/, "").toLowerCase();
|
|
142870
|
+
}
|
|
142871
|
+
function rejectIfLeadingDash(value2, kind) {
|
|
142872
|
+
if (value2.startsWith("-")) {
|
|
142873
|
+
throw new Error(`Blocked: ${kind} '${value2}' starts with '-' \u2014 git could parse it as a flag.`);
|
|
142874
|
+
}
|
|
142875
|
+
}
|
|
142876
|
+
var SYMBOLIC_REFS = /* @__PURE__ */ new Set(["HEAD", "FETCH_HEAD", "ORIG_HEAD", "MERGE_HEAD"]);
|
|
142877
|
+
function rejectSpecialRef(value2, kind) {
|
|
142878
|
+
rejectIfLeadingDash(value2, kind);
|
|
142879
|
+
if (value2.startsWith("refs/")) {
|
|
142880
|
+
throw new Error(
|
|
142881
|
+
`Blocked: ${kind} '${value2}' is a fully-qualified ref path. Use a bare branch name (e.g. 'feature/foo' or 'main'), not a 'refs/heads/...' form.`
|
|
142882
|
+
);
|
|
142883
|
+
}
|
|
142884
|
+
if (SYMBOLIC_REFS.has(value2)) {
|
|
142885
|
+
throw new Error(
|
|
142886
|
+
`Blocked: ${kind} '${value2}' is a git symbolic ref, not a branch name. Pass the resolved branch name (e.g. 'main'), or omit branchName to push the current branch.`
|
|
142887
|
+
);
|
|
142888
|
+
}
|
|
142889
|
+
const BAD = /[:+^~?*[\\\s]/;
|
|
142890
|
+
const badMatch = value2.match(BAD);
|
|
142891
|
+
if (badMatch) {
|
|
142892
|
+
throw new Error(
|
|
142893
|
+
`Blocked: ${kind} '${value2}' contains '${badMatch[0]}', which git interprets as refspec/revision syntax, not as part of a branch name.`
|
|
142894
|
+
);
|
|
142895
|
+
}
|
|
142896
|
+
}
|
|
142897
|
+
function validateTagName(tag) {
|
|
142898
|
+
rejectIfLeadingDash(tag, "tag");
|
|
142899
|
+
if (!/^[A-Za-z0-9._/-]+$/.test(tag)) {
|
|
142900
|
+
throw new Error(
|
|
142901
|
+
`Blocked: tag '${tag}' contains characters that could be parsed as a refspec or flag. Tags must match [A-Za-z0-9._/-]+.`
|
|
142902
|
+
);
|
|
142903
|
+
}
|
|
142904
|
+
}
|
|
142905
|
+
function validatePushDestination(ctx, branch) {
|
|
142906
|
+
const pushUrl = ctx.toolState.pushUrl;
|
|
142907
|
+
if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
|
|
142908
|
+
const dest = getPushDestination(branch, ctx.toolState.pushDest);
|
|
142909
|
+
if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
|
|
142910
|
+
throw new Error(
|
|
142911
|
+
`Push blocked: destination does not match expected repository.
|
|
142912
|
+
Expected: ${pushUrl}
|
|
142913
|
+
Actual: ${dest.url}
|
|
142914
|
+
Git configuration may have been tampered with.`
|
|
142915
|
+
);
|
|
142916
|
+
}
|
|
142917
|
+
return dest;
|
|
142918
|
+
}
|
|
142919
|
+
var PushBranch = type({
|
|
142920
|
+
branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
|
|
142921
|
+
force: type.boolean.describe("Force push (use with caution)").default(false)
|
|
142922
|
+
});
|
|
142923
|
+
function PushBranchTool(ctx) {
|
|
142924
|
+
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
142925
|
+
const pushPermission = ctx.payload.push;
|
|
142926
|
+
return tool({
|
|
142927
|
+
name: "push_branch",
|
|
142928
|
+
description: "Push the current branch to the remote repository. Omit branchName to push the current branch (recommended). If specifying branchName, use the LOCAL branch name (e.g., 'pr-1'), not the remote branch name. The correct remote and remote branch are determined automatically from branch config set by checkout_pr. Requires a clean working tree. Runs the repository prepush hook (if configured) before the network push \u2014 hook failure means tests/lint or similar in that script failed, not necessarily a Pullfrog timeout. Never force push unless explicitly requested. Pushes to the default branch are blocked in restricted mode.",
|
|
142929
|
+
parameters: PushBranch,
|
|
142930
|
+
execute: execute(async ({ branchName, force }) => {
|
|
142931
|
+
if (pushPermission === "disabled") {
|
|
142932
|
+
throw new Error("Push is disabled. This repository is configured for read-only access.");
|
|
142933
|
+
}
|
|
142934
|
+
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
|
142935
|
+
rejectSpecialRef(branch, "branch");
|
|
142936
|
+
const status = $("git", ["status", "--porcelain"], { log: false });
|
|
142937
|
+
if (status) {
|
|
142938
|
+
throw new Error(
|
|
142939
|
+
`push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
|
|
142940
|
+
|
|
142941
|
+
git status:
|
|
142942
|
+
${status}`
|
|
142943
|
+
);
|
|
142944
|
+
}
|
|
142945
|
+
const pushDest = validatePushDestination(ctx, branch);
|
|
142946
|
+
if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
|
|
142947
|
+
throw new Error(
|
|
142948
|
+
`Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
|
|
142949
|
+
);
|
|
142950
|
+
}
|
|
142951
|
+
const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
|
|
142952
|
+
const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
|
|
142953
|
+
const prepushHook = await executeLifecycleHook({
|
|
142954
|
+
event: "prepush",
|
|
142955
|
+
script: ctx.prepushScript
|
|
142956
|
+
});
|
|
142957
|
+
if (prepushHook.warning) {
|
|
142958
|
+
throw new Error(prepushHook.warning);
|
|
142959
|
+
}
|
|
142960
|
+
const postHookStatus = $("git", ["status", "--porcelain"], { log: false });
|
|
142961
|
+
if (postHookStatus) {
|
|
142962
|
+
throw new Error(
|
|
142963
|
+
`push blocked: the prepush hook modified the working tree. those changes are not included in the push. commit or discard them (or change the hook to not mutate tracked files) before retrying.
|
|
142964
|
+
|
|
142965
|
+
git status:
|
|
142966
|
+
${postHookStatus}`
|
|
142967
|
+
);
|
|
142968
|
+
}
|
|
142969
|
+
log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
|
|
142970
|
+
if (force) {
|
|
142971
|
+
log.warning(`force pushing - this will overwrite remote history`);
|
|
142972
|
+
}
|
|
142973
|
+
try {
|
|
142974
|
+
await $git("push", pushArgs, {
|
|
142975
|
+
token: ctx.gitToken
|
|
142976
|
+
});
|
|
142977
|
+
} catch (err) {
|
|
142978
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
142979
|
+
if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
|
|
142980
|
+
const integrateStep = ctx.payload.shell === "disabled" ? `2. use the git tool to merge the remote branch into yours: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] })` : `2. use the git tool to rebase or merge your changes on top: git({ command: "merge", args: ["origin/${pushDest.remoteBranch}"] }) (or 'rebase')`;
|
|
142981
|
+
throw new Error(
|
|
142982
|
+
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
|
|
142983
|
+
|
|
142984
|
+
to resolve this:
|
|
142985
|
+
1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
|
|
142986
|
+
${integrateStep}
|
|
142987
|
+
3. resolve any merge conflicts if needed
|
|
142988
|
+
4. retry push_branch`
|
|
142989
|
+
);
|
|
142990
|
+
}
|
|
142991
|
+
throw err;
|
|
142992
|
+
}
|
|
142993
|
+
return {
|
|
142994
|
+
success: true,
|
|
142995
|
+
branch,
|
|
142996
|
+
remoteBranch: pushDest.remoteBranch,
|
|
142997
|
+
remote: pushDest.remoteName,
|
|
142998
|
+
force,
|
|
142999
|
+
message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
|
|
143000
|
+
};
|
|
143001
|
+
})
|
|
143002
|
+
});
|
|
143003
|
+
}
|
|
143004
|
+
var AUTH_REQUIRED_REDIRECT = {
|
|
143005
|
+
push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
|
|
143006
|
+
fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
|
|
143007
|
+
pull: "use git_fetch to fetch the remote ref, then call this git tool with command 'merge' locally.",
|
|
143008
|
+
clone: "the repository is already cloned. use checkout_pr for PR branches."
|
|
143009
|
+
};
|
|
143010
|
+
var NOSHELL_BLOCKED_SUBCOMMANDS = {
|
|
143011
|
+
config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
|
|
143012
|
+
submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
|
|
143013
|
+
"update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
|
|
143014
|
+
"filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
|
|
143015
|
+
replace: "Blocked: git replace can redirect object lookups.",
|
|
143016
|
+
// subcommands that accept --exec or similar flags for arbitrary code execution
|
|
143017
|
+
rebase: "Blocked: git rebase --exec can execute arbitrary shell commands. Use 'merge' instead to integrate remote changes.",
|
|
143018
|
+
bisect: "Blocked: git bisect run can execute arbitrary shell commands. Bisect by hand (bisect start/good/bad/reset) is not available through this tool either \u2014 ask the user to run the bisect if needed.",
|
|
143019
|
+
// difftool/mergetool exist to shell out to external diff/merge programs.
|
|
143020
|
+
// both accept `--extcmd` / `-x` (difftool) or configured tool commands
|
|
143021
|
+
// (mergetool) that run arbitrary code. NOSHELL_BLOCKED_ARGS catches the
|
|
143022
|
+
// long `--extcmd` form, but not the `-x` short form — and globally blocking
|
|
143023
|
+
// `-x` would false-positive on `git cherry-pick -x`. block the subcommands
|
|
143024
|
+
// wholesale instead; neither has a meaningful use in an automated agent
|
|
143025
|
+
// workflow (agents use `git diff` / `git show` for diffs and resolve
|
|
143026
|
+
// conflicts via file edits, not a TUI merge tool).
|
|
143027
|
+
difftool: "Blocked: git difftool runs an external diff program via --extcmd/-x or configured tool and can execute arbitrary shell commands. Use 'diff' (or 'show' for single commits) to inspect changes \u2014 those output directly and don't invoke an external tool.",
|
|
143028
|
+
mergetool: "Blocked: git mergetool runs an external merge program configured via mergetool.<name>.cmd and can execute arbitrary shell commands. Resolve conflicts by editing the files directly (conflict markers are written into the working tree) and then commit."
|
|
143029
|
+
};
|
|
143030
|
+
var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
|
|
143031
|
+
var COLLAPSE_THRESHOLD = 200;
|
|
143032
|
+
var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
|
|
143033
|
+
var Git = type({
|
|
143034
|
+
command: type(subcommandPattern).describe("Git command (e.g., 'status', 'log', 'diff')"),
|
|
143035
|
+
args: type.string.array().describe("Additional arguments for the git command").optional()
|
|
143036
|
+
});
|
|
143037
|
+
function GitTool(ctx) {
|
|
143038
|
+
return tool({
|
|
143039
|
+
name: "git",
|
|
143040
|
+
description: "Run git commands. For push/fetch, use the dedicated MCP tools (push_branch, git_fetch). git pull is not available \u2014 use git_fetch then this tool with command 'merge'.",
|
|
143041
|
+
parameters: Git,
|
|
143042
|
+
execute: execute(async (params) => {
|
|
143043
|
+
const command = params.command;
|
|
143044
|
+
const args2 = params.args ?? [];
|
|
143045
|
+
const redirect = AUTH_REQUIRED_REDIRECT[command];
|
|
143046
|
+
if (redirect) {
|
|
143047
|
+
throw new Error(`git ${command} is not available through this tool \u2014 ${redirect}`);
|
|
143048
|
+
}
|
|
143049
|
+
if (ctx.payload.shell === "disabled") {
|
|
143050
|
+
const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[command];
|
|
143051
|
+
if (blocked) {
|
|
143052
|
+
throw new Error(blocked);
|
|
143053
|
+
}
|
|
143054
|
+
for (const arg of args2) {
|
|
143055
|
+
const isBlocked = NOSHELL_BLOCKED_ARGS.some(
|
|
143056
|
+
(flag) => arg === flag || arg.startsWith(flag + "=")
|
|
143057
|
+
);
|
|
143058
|
+
if (isBlocked) {
|
|
143059
|
+
throw new Error(
|
|
143060
|
+
`Blocked: '${arg}' flag can execute arbitrary code and is not allowed.`
|
|
143061
|
+
);
|
|
143062
|
+
}
|
|
143063
|
+
}
|
|
143064
|
+
}
|
|
143065
|
+
const output = $("git", [command, ...args2], { log: false });
|
|
143066
|
+
const lineCount = output.split("\n").length;
|
|
143067
|
+
if (lineCount > COLLAPSE_THRESHOLD) {
|
|
143068
|
+
log.group(`git ${command} output (${lineCount} lines)`, () => {
|
|
143069
|
+
log.info(output);
|
|
143070
|
+
});
|
|
143071
|
+
} else if (output) {
|
|
143072
|
+
log.info(output);
|
|
143073
|
+
}
|
|
143074
|
+
return { success: true, output };
|
|
143075
|
+
})
|
|
143076
|
+
});
|
|
143077
|
+
}
|
|
143078
|
+
var GitFetch = type({
|
|
143079
|
+
ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
|
|
143080
|
+
depth: type.number.describe("Fetch depth (for shallow clones)").optional()
|
|
143081
|
+
});
|
|
143082
|
+
function GitFetchTool(ctx) {
|
|
143083
|
+
return tool({
|
|
143084
|
+
name: "git_fetch",
|
|
143085
|
+
description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
|
|
143086
|
+
parameters: GitFetch,
|
|
143087
|
+
execute: execute(async (params) => {
|
|
143088
|
+
rejectIfLeadingDash(params.ref, "ref");
|
|
143089
|
+
const fetchArgs = ["--no-tags", "origin", params.ref];
|
|
143090
|
+
if (params.depth !== void 0) {
|
|
143091
|
+
fetchArgs.push(`--depth=${params.depth}`);
|
|
143092
|
+
}
|
|
143093
|
+
await $git("fetch", fetchArgs, {
|
|
143094
|
+
token: ctx.gitToken
|
|
143095
|
+
});
|
|
143096
|
+
return { success: true, ref: params.ref };
|
|
143097
|
+
})
|
|
143098
|
+
});
|
|
143099
|
+
}
|
|
143100
|
+
var DeleteBranch = type({
|
|
143101
|
+
branchName: type.string.describe("Remote branch to delete")
|
|
143102
|
+
});
|
|
143103
|
+
function DeleteBranchTool(ctx) {
|
|
143104
|
+
const pushPermission = ctx.payload.push;
|
|
143105
|
+
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
143106
|
+
return tool({
|
|
143107
|
+
name: "delete_branch",
|
|
143108
|
+
description: "Delete a remote branch. Requires push: enabled permission. Deletion of the repository's default branch is always blocked regardless of permission mode.",
|
|
143109
|
+
parameters: DeleteBranch,
|
|
143110
|
+
execute: execute(async (params) => {
|
|
143111
|
+
if (pushPermission !== "enabled") {
|
|
143112
|
+
throw new Error(
|
|
143113
|
+
"Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
|
|
143114
|
+
);
|
|
143115
|
+
}
|
|
143116
|
+
rejectSpecialRef(params.branchName, "branchName");
|
|
143117
|
+
if (params.branchName === defaultBranch) {
|
|
143118
|
+
throw new Error(
|
|
143119
|
+
`Blocked: cannot delete the default branch '${defaultBranch}'. If you really need to delete or rename it, do it manually via the repository settings.`
|
|
143120
|
+
);
|
|
143121
|
+
}
|
|
143122
|
+
await $git("push", ["origin", "--delete", `refs/heads/${params.branchName}`], {
|
|
143123
|
+
token: ctx.gitToken
|
|
143124
|
+
});
|
|
143125
|
+
return { success: true, deleted: params.branchName };
|
|
143126
|
+
})
|
|
143127
|
+
});
|
|
143128
|
+
}
|
|
143129
|
+
var PushTags = type({
|
|
143130
|
+
tag: type.string.describe("Tag name to push"),
|
|
143131
|
+
force: type.boolean.describe("Force push the tag").default(false)
|
|
143132
|
+
});
|
|
143133
|
+
function PushTagsTool(ctx) {
|
|
143134
|
+
const pushPermission = ctx.payload.push;
|
|
143135
|
+
return tool({
|
|
143136
|
+
name: "push_tags",
|
|
143137
|
+
description: "Push a tag to remote. Requires push: enabled permission.",
|
|
143138
|
+
parameters: PushTags,
|
|
143139
|
+
execute: execute(async (params) => {
|
|
143140
|
+
if (pushPermission !== "enabled") {
|
|
143141
|
+
throw new Error(
|
|
143142
|
+
"Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
|
|
143143
|
+
);
|
|
143144
|
+
}
|
|
143145
|
+
validateTagName(params.tag);
|
|
143146
|
+
const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
|
|
143147
|
+
await $git("push", pushArgs, {
|
|
143148
|
+
token: ctx.gitToken
|
|
143149
|
+
});
|
|
143150
|
+
return { success: true, tag: params.tag };
|
|
143151
|
+
})
|
|
143152
|
+
});
|
|
143153
|
+
}
|
|
143154
|
+
|
|
143155
|
+
// mcp/review.ts
|
|
143156
|
+
function getHttpStatus(err) {
|
|
143157
|
+
if (typeof err !== "object" || err === null) return void 0;
|
|
143158
|
+
const status = err.status;
|
|
143159
|
+
return typeof status === "number" ? status : void 0;
|
|
143160
|
+
}
|
|
143161
|
+
function commentableLinesForFile(patch) {
|
|
143162
|
+
const right = /* @__PURE__ */ new Set();
|
|
143163
|
+
const left = /* @__PURE__ */ new Set();
|
|
143164
|
+
if (!patch) return { RIGHT: right, LEFT: left };
|
|
143165
|
+
let oldLine = 0;
|
|
143166
|
+
let newLine = 0;
|
|
143167
|
+
for (const line of patch.split("\n")) {
|
|
143168
|
+
const hunk = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
143169
|
+
if (hunk) {
|
|
143170
|
+
oldLine = parseInt(hunk[1], 10);
|
|
143171
|
+
newLine = parseInt(hunk[2], 10);
|
|
143172
|
+
continue;
|
|
143173
|
+
}
|
|
143174
|
+
const changeType = line[0];
|
|
143175
|
+
if (changeType === "+") {
|
|
143176
|
+
right.add(newLine);
|
|
143177
|
+
newLine++;
|
|
143178
|
+
} else if (changeType === "-") {
|
|
143179
|
+
left.add(oldLine);
|
|
143180
|
+
oldLine++;
|
|
143181
|
+
} else if (changeType === " ") {
|
|
143182
|
+
right.add(newLine);
|
|
143183
|
+
left.add(oldLine);
|
|
143184
|
+
newLine++;
|
|
143185
|
+
oldLine++;
|
|
143186
|
+
}
|
|
143187
|
+
}
|
|
143188
|
+
return { RIGHT: right, LEFT: left };
|
|
143189
|
+
}
|
|
143190
|
+
async function buildCommentableMap(ctx, pullNumber) {
|
|
143191
|
+
const cached4 = ctx.toolState.commentableLinesByFile;
|
|
143192
|
+
const cachedFor = ctx.toolState.commentableLinesPullNumber;
|
|
143193
|
+
const cachedSha = ctx.toolState.commentableLinesCheckoutSha;
|
|
143194
|
+
const currentSha = ctx.toolState.checkoutSha;
|
|
143195
|
+
if (cached4 && cachedFor === pullNumber && cachedSha && cachedSha === currentSha) return cached4;
|
|
143196
|
+
const files = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listFiles, {
|
|
143197
|
+
owner: ctx.repo.owner,
|
|
143198
|
+
repo: ctx.repo.name,
|
|
143199
|
+
pull_number: pullNumber,
|
|
143200
|
+
per_page: 100
|
|
143201
|
+
});
|
|
143202
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
143203
|
+
for (const file2 of files) {
|
|
143204
|
+
map2.set(file2.filename, commentableLinesForFile(file2.patch));
|
|
143205
|
+
}
|
|
143206
|
+
return map2;
|
|
143207
|
+
}
|
|
143208
|
+
function validateInlineComments(comments, map2) {
|
|
143209
|
+
const valid = [];
|
|
143210
|
+
const dropped = [];
|
|
143211
|
+
for (const c of comments) {
|
|
143212
|
+
const side = c.side === "LEFT" ? "LEFT" : "RIGHT";
|
|
143213
|
+
const line = c.line ?? 0;
|
|
143214
|
+
const startLine = c.start_line ?? line;
|
|
143215
|
+
const lines = map2.get(c.path);
|
|
143216
|
+
const record3 = (reason) => {
|
|
143217
|
+
const entry = { path: c.path, line, side, reason };
|
|
143218
|
+
if (c.start_line != null) entry.startLine = c.start_line;
|
|
143219
|
+
dropped.push(entry);
|
|
143220
|
+
};
|
|
143221
|
+
if (!lines) {
|
|
143222
|
+
record3(`file not in PR diff`);
|
|
143223
|
+
continue;
|
|
143224
|
+
}
|
|
143225
|
+
if (lines.LEFT.size === 0 && lines.RIGHT.size === 0) {
|
|
143226
|
+
record3(`file has no textual diff (binary, pure rename, or mode change)`);
|
|
143227
|
+
continue;
|
|
143228
|
+
}
|
|
143229
|
+
const anchors = lines[side];
|
|
143230
|
+
if (!anchors.has(line)) {
|
|
143231
|
+
record3(`line ${line} (${side}) is not inside a diff hunk`);
|
|
143232
|
+
continue;
|
|
143233
|
+
}
|
|
143234
|
+
if (c.start_line != null && c.start_line > line) {
|
|
143235
|
+
record3(
|
|
143236
|
+
`start_line ${c.start_line} is after line ${line} \u2014 ranges must satisfy start_line <= line`
|
|
143237
|
+
);
|
|
143238
|
+
continue;
|
|
143239
|
+
}
|
|
143240
|
+
if (startLine !== line && !anchors.has(startLine)) {
|
|
143241
|
+
record3(`start_line ${startLine} (${side}) is not inside a diff hunk`);
|
|
143242
|
+
continue;
|
|
143243
|
+
}
|
|
143244
|
+
valid.push(c);
|
|
143245
|
+
}
|
|
143246
|
+
return { valid, dropped };
|
|
143247
|
+
}
|
|
143248
|
+
var MAX_DROPPED_COMMENT_LINES = 50;
|
|
143249
|
+
function reviewSkipDecision(params) {
|
|
143250
|
+
if (params.body || params.hasComments) return null;
|
|
143251
|
+
if (!params.approved) {
|
|
143252
|
+
return {
|
|
143253
|
+
kind: "no-issues",
|
|
143254
|
+
reason: "no issues found \u2014 nothing to post"
|
|
143255
|
+
};
|
|
143256
|
+
}
|
|
143257
|
+
if (!params.prApproveEnabled) {
|
|
143258
|
+
return {
|
|
143259
|
+
kind: "empty-downgraded-approve",
|
|
143260
|
+
reason: "approve requested but prApproveEnabled is disabled; no feedback body or comments to post as a COMMENT review instead"
|
|
143261
|
+
};
|
|
143262
|
+
}
|
|
143263
|
+
return null;
|
|
143264
|
+
}
|
|
143265
|
+
function formatDroppedCommentsNote(dropped) {
|
|
143266
|
+
const renderEntry = (d) => {
|
|
143267
|
+
const range2 = d.startLine != null && d.startLine !== d.line ? `${d.startLine}-${d.line}` : `${d.line}`;
|
|
143268
|
+
return `- \`${d.path}:${range2}\` (${d.side}) \u2014 ${d.reason}`;
|
|
143269
|
+
};
|
|
143270
|
+
const shown = dropped.slice(0, MAX_DROPPED_COMMENT_LINES).map(renderEntry);
|
|
143271
|
+
const remainder = dropped.length - shown.length;
|
|
143272
|
+
if (remainder > 0) shown.push(`- \u2026and ${remainder} more dropped comment(s) not shown`);
|
|
143273
|
+
return `
|
|
143274
|
+
|
|
143275
|
+
---
|
|
143276
|
+
|
|
143277
|
+
**Note:** ${dropped.length} inline comment(s) dropped because they did not anchor to lines inside the PR diff:
|
|
143278
|
+
` + shown.join("\n");
|
|
143279
|
+
}
|
|
143280
|
+
var CreatePullRequestReview = type({
|
|
143281
|
+
pull_number: type.number.describe("The pull request number to review"),
|
|
143282
|
+
body: type.string.describe(
|
|
143283
|
+
"1-2 sentence high-level summary with urgency level, critical callouts, and feedback about code outside the diff. Specific feedback on diff lines goes in 'comments' array."
|
|
143284
|
+
).optional(),
|
|
143285
|
+
approved: type.boolean.describe(
|
|
143286
|
+
"Set to true to submit as an approval. ONLY when the review contains no actionable feedback \u2014 neither inline comments nor actionable content in the body. Defaults to false (comment-only review). Rejections are not supported."
|
|
143287
|
+
).optional(),
|
|
143288
|
+
commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
|
|
143289
|
+
comments: type({
|
|
143290
|
+
path: type.string.describe(
|
|
143291
|
+
"The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
|
|
143292
|
+
),
|
|
143293
|
+
line: type.number.describe(
|
|
143294
|
+
"Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
|
|
143295
|
+
),
|
|
143296
|
+
side: type.enumerated("LEFT", "RIGHT").describe(
|
|
143297
|
+
"Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
|
|
143298
|
+
).optional(),
|
|
143299
|
+
body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
|
|
143300
|
+
suggestion: type.string.describe(
|
|
143301
|
+
"Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
|
|
143302
|
+
).optional(),
|
|
143303
|
+
start_line: type.number.describe(
|
|
143304
|
+
"Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
|
|
143305
|
+
).optional()
|
|
143306
|
+
}).array().describe(
|
|
143307
|
+
"Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
|
|
143308
|
+
).optional()
|
|
143309
|
+
});
|
|
143310
|
+
function CreatePullRequestReviewTool(ctx) {
|
|
143311
|
+
return tool({
|
|
143312
|
+
name: "create_pull_request_review",
|
|
143313
|
+
description: `Submit a review for an existing pull request. Each call creates a permanent, visible review on the PR \u2014 NEVER submit test or diagnostic reviews. Reviews with no body AND no comments are silently skipped (nothing to post). IMPORTANT: 95%+ of feedback should be in 'comments' array with file paths and line numbers. Only use 'body' for a 1-2 sentence summary with urgency and critical callouts. Use 'suggestion' to propose replacement code - MUST preserve exact indentation of original code. The first submission may error once with a one-time diff-coverage nudge listing unread TOC regions \u2014 retry with the same arguments and the pre-flight will not block again. Example replacing lines 42-44 (3 lines) with 5 lines: { path: 'src/api.ts', start_line: 42, line: 44, suggestion: ' const result = await fetch(url);\\n if (!result.ok) {\\n log.error(result.status);\\n throw new Error("request failed");\\n }' } CONSTRAINT: Inline comments can ONLY target files and lines that appear in the PR diff. Comments anchored outside a diff hunk are dropped automatically (with a note appended to the review body) \u2014 the rest of the review still posts.`,
|
|
143314
|
+
parameters: CreatePullRequestReview,
|
|
143315
|
+
execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
|
|
143316
|
+
if (body) body = fixDoubleEscapedString(body);
|
|
143317
|
+
ctx.toolState.issueNumber = pull_number;
|
|
143318
|
+
const skip = reviewSkipDecision({
|
|
143319
|
+
approved: approved ?? false,
|
|
143320
|
+
body,
|
|
143321
|
+
hasComments: comments.length > 0,
|
|
143322
|
+
prApproveEnabled: ctx.prApproveEnabled
|
|
143323
|
+
});
|
|
143324
|
+
if (skip) {
|
|
143325
|
+
log.info(`skipping review submission: ${skip.reason}`);
|
|
143326
|
+
return { success: true, skipped: true, reason: skip.reason };
|
|
143327
|
+
}
|
|
143328
|
+
let event = approved ? "APPROVE" : "COMMENT";
|
|
143329
|
+
if (event === "APPROVE" && !ctx.prApproveEnabled) {
|
|
143330
|
+
log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
|
|
143331
|
+
event = "COMMENT";
|
|
143332
|
+
}
|
|
143333
|
+
const params = {
|
|
143334
|
+
owner: ctx.repo.owner,
|
|
143335
|
+
repo: ctx.repo.name,
|
|
143336
|
+
pull_number,
|
|
143337
|
+
event
|
|
143338
|
+
};
|
|
143339
|
+
let latestHeadSha;
|
|
143340
|
+
if (commit_id) {
|
|
143341
|
+
params.commit_id = commit_id;
|
|
143342
|
+
} else {
|
|
143343
|
+
const pr = await ctx.octokit.rest.pulls.get({
|
|
143344
|
+
owner: ctx.repo.owner,
|
|
143345
|
+
repo: ctx.repo.name,
|
|
143346
|
+
pull_number
|
|
143347
|
+
});
|
|
143348
|
+
latestHeadSha = pr.data.head.sha;
|
|
143349
|
+
params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
|
|
143350
|
+
if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143351
|
+
log.info(
|
|
143352
|
+
`anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
|
|
143353
|
+
);
|
|
143354
|
+
}
|
|
143355
|
+
}
|
|
143356
|
+
runDiffCoveragePreflight({ ctx });
|
|
143357
|
+
const reviewComments = comments.map((comment) => {
|
|
143358
|
+
let commentBody = fixDoubleEscapedString(comment.body || "");
|
|
143359
|
+
if (comment.suggestion !== void 0) {
|
|
143360
|
+
const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
|
|
143361
|
+
commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
|
|
143362
|
+
}
|
|
143363
|
+
const side = comment.side || "RIGHT";
|
|
143364
|
+
const reviewComment = {
|
|
143365
|
+
path: comment.path,
|
|
143366
|
+
line: comment.line,
|
|
143367
|
+
body: commentBody,
|
|
143368
|
+
side
|
|
143369
|
+
};
|
|
143370
|
+
if (comment.start_line != null && comment.start_line !== comment.line) {
|
|
143371
|
+
reviewComment.start_line = comment.start_line;
|
|
143372
|
+
reviewComment.start_side = side;
|
|
143373
|
+
}
|
|
143374
|
+
return reviewComment;
|
|
143375
|
+
});
|
|
143376
|
+
let droppedComments = [];
|
|
143377
|
+
if (reviewComments.length > 0) {
|
|
143378
|
+
const commentableMap = await buildCommentableMap(ctx, pull_number);
|
|
143379
|
+
const validation = validateInlineComments(reviewComments, commentableMap);
|
|
143380
|
+
droppedComments = validation.dropped;
|
|
143381
|
+
if (droppedComments.length > 0) {
|
|
143382
|
+
log.info(
|
|
143383
|
+
`dropping ${droppedComments.length}/${reviewComments.length} inline comment(s) that do not anchor to PR diff lines`
|
|
143384
|
+
);
|
|
143385
|
+
}
|
|
143386
|
+
params.comments = validation.valid;
|
|
143387
|
+
}
|
|
143388
|
+
if (droppedComments.length > 0) {
|
|
143389
|
+
const note = formatDroppedCommentsNote(droppedComments);
|
|
143390
|
+
body = body ? body + note : note.replace(/^\n\n/, "");
|
|
143391
|
+
}
|
|
143392
|
+
if (!approved && !body && !params.comments?.length) {
|
|
143393
|
+
log.info("review has no body and all inline comments were dropped \u2014 skipping submission");
|
|
143394
|
+
return {
|
|
143395
|
+
success: true,
|
|
143396
|
+
skipped: true,
|
|
143397
|
+
reason: "all inline comments were invalid \u2014 nothing to post",
|
|
143398
|
+
droppedComments
|
|
143399
|
+
};
|
|
143400
|
+
}
|
|
143401
|
+
let result;
|
|
143402
|
+
try {
|
|
143403
|
+
result = body ? await createAndSubmitWithFooter(ctx, params, {
|
|
143404
|
+
body,
|
|
143405
|
+
approved: approved ?? false,
|
|
143406
|
+
hasComments: (params.comments?.length ?? 0) > 0
|
|
143407
|
+
}) : await createReviewWithStrandedRecovery(ctx, params);
|
|
143408
|
+
} catch (err) {
|
|
143409
|
+
if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
|
|
143410
|
+
const details = params.comments.map((c) => {
|
|
143411
|
+
const line = c.line ?? 0;
|
|
143412
|
+
const startLine = c.start_line ?? line;
|
|
143413
|
+
const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
|
|
143414
|
+
return `${c.path}:${range2} (${c.side ?? "RIGHT"})`;
|
|
143415
|
+
});
|
|
143416
|
+
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
143417
|
+
const checkoutRef = formatMcpToolRef(ctx.agentId, "checkout_pr");
|
|
143418
|
+
throw new Error(
|
|
143419
|
+
`GitHub rejected the review with 422 even after pre-validation. Likely causes (check "GitHub said" below to narrow down): (1) new commits pushed after pre-validation \u2014 call \`${checkoutRef}\` again to refresh the diff snapshot, then resubmit; (2) the review body exceeded GitHub's ~65KB limit \u2014 shorten it and retry; (3) a \`suggestion\` block is malformed (missing backticks, extra backticks, or wrong indentation) \u2014 inspect the affected comments below. If none apply, move the failing comments into the review body as text so the rest still posts. Affected comments: ${details.join(", ")}. GitHub said: ${rawMsg}`,
|
|
143420
|
+
{ cause: err }
|
|
143421
|
+
);
|
|
143422
|
+
}
|
|
143423
|
+
log.debug(`createReview response: ${JSON.stringify(result.data)}`);
|
|
143424
|
+
if (!result.data.id) {
|
|
143425
|
+
throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
|
|
143426
|
+
}
|
|
143427
|
+
const reviewId = result.data.id;
|
|
143428
|
+
const reviewNodeId = result.data.node_id;
|
|
143429
|
+
const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
|
|
143430
|
+
ctx.toolState.review = {
|
|
143431
|
+
id: reviewId,
|
|
143432
|
+
nodeId: reviewNodeId,
|
|
143433
|
+
reviewedSha: actuallyReviewedSha
|
|
143434
|
+
};
|
|
143435
|
+
if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143436
|
+
const fromSha = ctx.toolState.checkoutSha;
|
|
143437
|
+
const toSha = latestHeadSha;
|
|
143438
|
+
ctx.toolState.beforeSha = fromSha;
|
|
143439
|
+
ctx.toolState.checkoutSha = toSha;
|
|
143440
|
+
log.info(
|
|
143441
|
+
`new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
|
|
143442
|
+
);
|
|
143443
|
+
return {
|
|
143444
|
+
success: true,
|
|
143445
|
+
reviewId,
|
|
143446
|
+
html_url: result.data.html_url,
|
|
143447
|
+
state: result.data.state,
|
|
143448
|
+
user: result.data.user?.login,
|
|
143449
|
+
submitted_at: result.data.submitted_at,
|
|
143450
|
+
droppedComments: droppedComments.length > 0 ? droppedComments : void 0,
|
|
143451
|
+
newCommits: {
|
|
143452
|
+
from: fromSha,
|
|
143453
|
+
to: toSha,
|
|
143454
|
+
instructions: `new commits were pushed while you were reviewing. call \`${formatMcpToolRef(ctx.agentId, "checkout_pr")}\` again to fetch the latest version \u2014 it will compute the incremental diff automatically. submit another review covering only the new changes. do not repeat feedback from your previous review.`
|
|
143455
|
+
}
|
|
143456
|
+
};
|
|
143457
|
+
}
|
|
143458
|
+
return {
|
|
143459
|
+
success: true,
|
|
143460
|
+
reviewId,
|
|
143461
|
+
html_url: result.data.html_url,
|
|
143462
|
+
state: result.data.state,
|
|
143463
|
+
user: result.data.user?.login,
|
|
143464
|
+
submitted_at: result.data.submitted_at,
|
|
143465
|
+
droppedComments: droppedComments.length > 0 ? droppedComments : void 0
|
|
143466
|
+
};
|
|
143467
|
+
})
|
|
143468
|
+
});
|
|
143469
|
+
}
|
|
143470
|
+
function runDiffCoveragePreflight(params) {
|
|
143471
|
+
const coverageState = params.ctx.toolState.diffCoverage;
|
|
143472
|
+
if (!coverageState) {
|
|
143473
|
+
log.debug("diff coverage pre-flight skipped: no diffCoverage state present in toolState");
|
|
143474
|
+
return;
|
|
143475
|
+
}
|
|
143476
|
+
if (coverageState.coveragePreflightRan) {
|
|
143477
|
+
log.debug("diff coverage pre-flight skipped: already ran in this session");
|
|
143478
|
+
return;
|
|
143479
|
+
}
|
|
143480
|
+
coverageState.coveragePreflightRan = true;
|
|
143481
|
+
log.debug(
|
|
143482
|
+
`diff coverage pre-flight start: diffPath=${coverageState.diffPath}, totalLines=${coverageState.totalLines}, tocEntries=${coverageState.tocEntries.length}, coveredRanges=${coverageState.coveredRanges.length}`
|
|
143483
|
+
);
|
|
143484
|
+
const breakdown = getDiffCoverageBreakdown({ state: coverageState });
|
|
143485
|
+
const unread = [];
|
|
143486
|
+
let unreadLines = 0;
|
|
143487
|
+
for (const file2 of breakdown.files) {
|
|
143488
|
+
if (file2.unreadRanges.length === 0) continue;
|
|
143489
|
+
const rangesText = file2.unreadRanges.map((range2) => `${range2.startLine}-${range2.endLine}`).join(", ");
|
|
143490
|
+
const fileUnreadLines = countLinesInRanges({ ranges: file2.unreadRanges });
|
|
143491
|
+
unread.push({ path: file2.filename, ranges: rangesText, unreadLines: fileUnreadLines });
|
|
143492
|
+
unreadLines += fileUnreadLines;
|
|
143493
|
+
}
|
|
143494
|
+
coverageState.lastBreakdown = renderDiffCoverageBreakdown({
|
|
143495
|
+
diffPath: coverageState.diffPath,
|
|
143496
|
+
breakdown
|
|
143497
|
+
});
|
|
143498
|
+
log.debug(
|
|
143499
|
+
`diff coverage pre-flight breakdown: coveredLines=${breakdown.coveredLines}, unreadLines=${unreadLines}`
|
|
143500
|
+
);
|
|
143501
|
+
if (unreadLines === 0) {
|
|
143502
|
+
log.debug("diff coverage pre-flight passed: no unread regions");
|
|
143503
|
+
return;
|
|
143504
|
+
}
|
|
143505
|
+
log.info(
|
|
143506
|
+
`diff coverage pre-flight nudge: unread lines=${unreadLines}, unread files=${unread.length}`
|
|
143507
|
+
);
|
|
143508
|
+
const unreadText = unread.map((entry) => `- ${entry.path} (${entry.unreadLines} lines, ${entry.ranges})`).join("\n");
|
|
143509
|
+
throw new Error(
|
|
143510
|
+
`diff coverage pre-flight: some TOC regions were not read before review submission. this is a one-time nudge \u2014 optionally read the ranges below from ${coverageState.diffPath}, then call create_pull_request_review again with the same arguments. this pre-flight will not block again in this review session.
|
|
143511
|
+
|
|
143512
|
+
unread TOC regions:
|
|
143513
|
+
${unreadText}
|
|
143514
|
+
|
|
143515
|
+
${coverageState.lastBreakdown}`
|
|
143516
|
+
);
|
|
143517
|
+
}
|
|
143518
|
+
async function clearStrandedPendingReview(ctx, params) {
|
|
143519
|
+
const originalErr = params.originalErr;
|
|
143520
|
+
const msg = originalErr instanceof Error ? originalErr.message.toLowerCase() : "";
|
|
143521
|
+
if (getHttpStatus(originalErr) !== 422 || !msg.includes("pending review")) throw originalErr;
|
|
143522
|
+
const reviews = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listReviews, {
|
|
143523
|
+
owner: params.owner,
|
|
143524
|
+
repo: params.repo,
|
|
143525
|
+
pull_number: params.pull_number,
|
|
143526
|
+
per_page: 100
|
|
143527
|
+
}).catch((listErr) => {
|
|
143528
|
+
log.info(
|
|
143529
|
+
`\xBB listReviews failed during pending-review cleanup, surfacing original 422: ${listErr instanceof Error ? listErr.message : String(listErr)}`
|
|
143530
|
+
);
|
|
143531
|
+
throw originalErr;
|
|
143532
|
+
});
|
|
143533
|
+
const leftover = reviews.find((r) => r.state === "PENDING");
|
|
143534
|
+
if (!leftover?.id) throw originalErr;
|
|
143535
|
+
log.info(
|
|
143536
|
+
`\xBB clearing leftover pending review ${leftover.id} (likely stranded by a killed prior run)`
|
|
143537
|
+
);
|
|
143538
|
+
try {
|
|
143539
|
+
await ctx.octokit.rest.pulls.deletePendingReview({
|
|
143540
|
+
owner: params.owner,
|
|
143541
|
+
repo: params.repo,
|
|
143542
|
+
pull_number: params.pull_number,
|
|
143543
|
+
review_id: leftover.id
|
|
143544
|
+
});
|
|
143545
|
+
} catch (cleanupErr) {
|
|
143546
|
+
const cleanupStatus = getHttpStatus(cleanupErr);
|
|
143547
|
+
if (cleanupStatus !== 404 && cleanupStatus !== 422) throw cleanupErr;
|
|
143548
|
+
log.debug(`\xBB delete of leftover pending ${leftover.id} no-op (status ${cleanupStatus})`);
|
|
143549
|
+
}
|
|
143550
|
+
}
|
|
143551
|
+
async function createReviewWithStrandedRecovery(ctx, params) {
|
|
143552
|
+
try {
|
|
143553
|
+
return await ctx.octokit.rest.pulls.createReview(params);
|
|
143554
|
+
} catch (err) {
|
|
143555
|
+
await clearStrandedPendingReview(ctx, {
|
|
143556
|
+
owner: params.owner,
|
|
143557
|
+
repo: params.repo,
|
|
143558
|
+
pull_number: params.pull_number,
|
|
143559
|
+
originalErr: err
|
|
143560
|
+
});
|
|
143561
|
+
return await ctx.octokit.rest.pulls.createReview(params);
|
|
143562
|
+
}
|
|
143563
|
+
}
|
|
143564
|
+
async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
143565
|
+
const { event: _, ...pendingParams } = params;
|
|
143566
|
+
let pending;
|
|
143567
|
+
try {
|
|
143568
|
+
pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
|
|
143569
|
+
} catch (err) {
|
|
143570
|
+
await clearStrandedPendingReview(ctx, {
|
|
143571
|
+
owner: params.owner,
|
|
143572
|
+
repo: params.repo,
|
|
143573
|
+
pull_number: params.pull_number,
|
|
143574
|
+
originalErr: err
|
|
143575
|
+
});
|
|
143576
|
+
pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
|
|
143577
|
+
}
|
|
143578
|
+
if (!pending.data.id) {
|
|
143579
|
+
throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
|
|
143580
|
+
}
|
|
143581
|
+
try {
|
|
143582
|
+
const customParts = [];
|
|
143583
|
+
if (!opts.approved) {
|
|
143584
|
+
const apiUrl = getApiUrl();
|
|
143585
|
+
if (opts.hasComments) {
|
|
143586
|
+
const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143587
|
+
const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
|
|
143588
|
+
customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
|
|
143589
|
+
} else {
|
|
143590
|
+
const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143591
|
+
customParts.push(`[Fix it \u2794](${fixUrl})`);
|
|
143592
|
+
}
|
|
143593
|
+
}
|
|
143594
|
+
const footer = buildPullfrogFooter({
|
|
143595
|
+
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
143596
|
+
customParts,
|
|
143597
|
+
model: ctx.toolState.model
|
|
143598
|
+
});
|
|
143599
|
+
return await ctx.octokit.rest.pulls.submitReview({
|
|
143600
|
+
owner: params.owner,
|
|
143601
|
+
repo: params.repo,
|
|
143602
|
+
pull_number: params.pull_number,
|
|
143603
|
+
review_id: pending.data.id,
|
|
143604
|
+
event: params.event,
|
|
143605
|
+
body: opts.body + footer
|
|
143606
|
+
});
|
|
143607
|
+
} catch (err) {
|
|
143608
|
+
try {
|
|
143609
|
+
await ctx.octokit.rest.pulls.deletePendingReview({
|
|
143610
|
+
owner: params.owner,
|
|
143611
|
+
repo: params.repo,
|
|
143612
|
+
pull_number: params.pull_number,
|
|
143613
|
+
review_id: pending.data.id
|
|
143614
|
+
});
|
|
143615
|
+
log.debug(`\xBB deleted leftover pending review ${pending.data.id} after failure`);
|
|
143616
|
+
} catch (cleanupErr) {
|
|
143617
|
+
log.debug(
|
|
143618
|
+
`\xBB failed to delete pending review ${pending.data.id}: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
|
|
143619
|
+
);
|
|
143620
|
+
}
|
|
143621
|
+
throw err;
|
|
143622
|
+
}
|
|
143623
|
+
}
|
|
143624
|
+
async function reportReviewNodeId(ctx, params) {
|
|
143625
|
+
await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
|
|
143626
|
+
}
|
|
143627
|
+
|
|
142174
143628
|
// mcp/checkout.ts
|
|
142175
143629
|
function formatFilesWithLineNumbers(files) {
|
|
142176
143630
|
const output = [];
|
|
@@ -142237,7 +143691,10 @@ function formatFilesWithLineNumbers(files) {
|
|
|
142237
143691
|
}
|
|
142238
143692
|
const tocLines = [`## Files (${files.length})`];
|
|
142239
143693
|
for (const entry of tocEntries) {
|
|
142240
|
-
|
|
143694
|
+
const anchor = createHash2("sha256").update(entry.filename).digest("hex");
|
|
143695
|
+
tocLines.push(
|
|
143696
|
+
`- ${entry.filename} \u2192 lines ${entry.startLine}-${entry.endLine} \xB7 diff-${anchor}`
|
|
143697
|
+
);
|
|
142241
143698
|
}
|
|
142242
143699
|
tocLines.push("");
|
|
142243
143700
|
tocLines.push("---");
|
|
@@ -142259,7 +143716,7 @@ async function fetchAndFormatPrDiff(ctx, pullNumber) {
|
|
|
142259
143716
|
pull_number: pullNumber,
|
|
142260
143717
|
per_page: 100
|
|
142261
143718
|
});
|
|
142262
|
-
return formatFilesWithLineNumbers(files);
|
|
143719
|
+
return { ...formatFilesWithLineNumbers(files), files };
|
|
142263
143720
|
}
|
|
142264
143721
|
async function createTempBranch(params) {
|
|
142265
143722
|
const response = await params.octokit.rest.git.createRef({
|
|
@@ -142326,6 +143783,8 @@ async function ensureBeforeShaReachable(params) {
|
|
|
142326
143783
|
async function checkoutPrBranch(pr, params) {
|
|
142327
143784
|
const { octokit, owner, name, gitToken, toolState, beforeSha } = params;
|
|
142328
143785
|
log.info(`\xBB checking out PR #${pr.number}...`);
|
|
143786
|
+
rejectIfLeadingDash(pr.baseRef, "PR base ref");
|
|
143787
|
+
rejectIfLeadingDash(pr.headRef, "PR head ref");
|
|
142329
143788
|
const isFork = pr.headRepoFullName !== pr.baseRepoFullName;
|
|
142330
143789
|
const localBranch = `pr-${pr.number}`;
|
|
142331
143790
|
const isShallow = $("git", ["rev-parse", "--is-shallow-repository"], { log: false }).trim() === "true";
|
|
@@ -142336,7 +143795,7 @@ async function checkoutPrBranch(pr, params) {
|
|
|
142336
143795
|
if (!alreadyOnBranch) {
|
|
142337
143796
|
$("git", ["checkout", "-B", pr.baseRef, `origin/${pr.baseRef}`], { log: false });
|
|
142338
143797
|
log.debug(`\xBB fetching PR #${pr.number} (${localBranch})...`);
|
|
142339
|
-
await $git("fetch", ["--no-tags", "origin",
|
|
143798
|
+
await $git("fetch", ["--no-tags", "origin", `+pull/${pr.number}/head:${localBranch}`], {
|
|
142340
143799
|
token: gitToken
|
|
142341
143800
|
});
|
|
142342
143801
|
$("git", ["checkout", localBranch], { log: false });
|
|
@@ -142419,10 +143878,11 @@ async function checkoutPrBranch(pr, params) {
|
|
|
142419
143878
|
remoteBranch: pr.headRef,
|
|
142420
143879
|
localBranch
|
|
142421
143880
|
};
|
|
142422
|
-
await executeLifecycleHook({
|
|
143881
|
+
const postCheckoutHook = await executeLifecycleHook({
|
|
142423
143882
|
event: "post-checkout",
|
|
142424
143883
|
script: params.postCheckoutScript
|
|
142425
143884
|
});
|
|
143885
|
+
return { hookWarning: postCheckoutHook.warning };
|
|
142426
143886
|
}
|
|
142427
143887
|
function CheckoutPrTool(ctx) {
|
|
142428
143888
|
return tool({
|
|
@@ -142448,7 +143908,7 @@ function CheckoutPrTool(ctx) {
|
|
|
142448
143908
|
baseRepoFullName: prResponse.data.base.repo.full_name,
|
|
142449
143909
|
maintainerCanModify: prResponse.data.maintainer_can_modify
|
|
142450
143910
|
};
|
|
142451
|
-
await checkoutPrBranch(pr, {
|
|
143911
|
+
const checkoutResult = await checkoutPrBranch(pr, {
|
|
142452
143912
|
octokit: ctx.octokit,
|
|
142453
143913
|
owner: ctx.repo.owner,
|
|
142454
143914
|
name: ctx.repo.name,
|
|
@@ -142491,11 +143951,49 @@ ${diffPreview}`);
|
|
|
142491
143951
|
const diffPath = join3(tempDir, `pr-${pull_number}-${headShort}.diff`);
|
|
142492
143952
|
writeFileSync(diffPath, formatResult.content);
|
|
142493
143953
|
log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
|
|
143954
|
+
ctx.toolState.diffCoverage = createDiffCoverageState({
|
|
143955
|
+
diffPath,
|
|
143956
|
+
totalLines: countLines({ content: formatResult.content }),
|
|
143957
|
+
toc: formatResult.toc
|
|
143958
|
+
});
|
|
143959
|
+
log.debug(
|
|
143960
|
+
`\xBB diff coverage initialized: diffPath=${diffPath}, totalLines=${ctx.toolState.diffCoverage.totalLines}, tocEntries=${ctx.toolState.diffCoverage.tocEntries.length}`
|
|
143961
|
+
);
|
|
143962
|
+
const cached4 = /* @__PURE__ */ new Map();
|
|
143963
|
+
for (const file2 of formatResult.files) {
|
|
143964
|
+
cached4.set(file2.filename, commentableLinesForFile(file2.patch));
|
|
143965
|
+
}
|
|
143966
|
+
ctx.toolState.commentableLinesByFile = cached4;
|
|
143967
|
+
ctx.toolState.commentableLinesPullNumber = pull_number;
|
|
143968
|
+
ctx.toolState.commentableLinesCheckoutSha = ctx.toolState.checkoutSha;
|
|
142494
143969
|
const incrementalInstructions = incrementalDiffPath ? ` IMPORTANT: incrementalDiffPath contains ONLY the changes since the last reviewed version (computed via range-diff). you MUST read incrementalDiffPath FIRST to understand what changed, then use diffPath for full PR context. do NOT skip the incremental diff.` : "";
|
|
143970
|
+
const COMMIT_LOG_MAX = 200;
|
|
143971
|
+
const baseRange = `origin/${pr.baseRef}..HEAD`;
|
|
143972
|
+
let commitCount = 0;
|
|
143973
|
+
let commitLog = "";
|
|
143974
|
+
let commitLogUnavailable = false;
|
|
143975
|
+
try {
|
|
143976
|
+
commitCount = parseInt(
|
|
143977
|
+
$("git", ["rev-list", "--count", baseRange], { log: false }).trim() || "0",
|
|
143978
|
+
10
|
|
143979
|
+
);
|
|
143980
|
+
commitLog = $("git", ["log", "--oneline", `--max-count=${COMMIT_LOG_MAX}`, baseRange], {
|
|
143981
|
+
log: false
|
|
143982
|
+
});
|
|
143983
|
+
} catch (err) {
|
|
143984
|
+
commitLogUnavailable = true;
|
|
143985
|
+
log.debug(
|
|
143986
|
+
`\xBB unable to compute commit metadata for ${baseRange}: ${err instanceof Error ? err.message : String(err)}`
|
|
143987
|
+
);
|
|
143988
|
+
}
|
|
143989
|
+
const commitLogTruncated = commitCount > COMMIT_LOG_MAX;
|
|
143990
|
+
const hookWarningInstructions = checkoutResult.hookWarning ? ` HOOK WARNING: the post-checkout lifecycle hook reported a non-fatal failure (see hookWarning). decide whether to retry based on the guidance in that field before proceeding.` : "";
|
|
143991
|
+
const commitLogInstructions = commitLogUnavailable ? ` NOTE: commit metadata is partial (base ref unreachable, likely a shallow fetch). commitCount/commitLog may be 0/empty or incomplete; treat them as "unknown" rather than "no commits", and use \`git log\` directly if you need the full history.` : commitLogTruncated ? ` NOTE: commitLog was capped at ${COMMIT_LOG_MAX} entries out of ${commitCount} commits; use \`git log\` directly if you need the full history.` : "";
|
|
142495
143992
|
return {
|
|
142496
143993
|
success: true,
|
|
142497
143994
|
number: prResponse.data.number,
|
|
142498
143995
|
title: prResponse.data.title,
|
|
143996
|
+
body: prResponse.data.body,
|
|
142499
143997
|
base: pr.baseRef,
|
|
142500
143998
|
localBranch: `pr-${pull_number}`,
|
|
142501
143999
|
remoteBranch: `refs/heads/${pr.headRef}`,
|
|
@@ -142506,7 +144004,12 @@ ${diffPreview}`);
|
|
|
142506
144004
|
diffPath,
|
|
142507
144005
|
incrementalDiffPath,
|
|
142508
144006
|
toc: formatResult.toc,
|
|
142509
|
-
|
|
144007
|
+
commitCount,
|
|
144008
|
+
commitLog,
|
|
144009
|
+
commitLogTruncated,
|
|
144010
|
+
commitLogUnavailable,
|
|
144011
|
+
hookWarning: checkoutResult.hookWarning,
|
|
144012
|
+
instructions: `the diff file at diffPath contains a table of contents (TOC) at the top listing every changed file with its line range. use the TOC line ranges as your checklist and read specific files from the diff instead of reading the entire file. for example, if the TOC says "src/foo.ts \u2192 lines 5-42", read lines 5-42 from diffPath to see that file's changes. review files selectively based on relevance rather than reading everything sequentially. to inspect the PR's changed files, use diffPath \u2014 do NOT run \`git diff <base>..<head>\` to re-derive what's already in diffPath. the formatted diff with line numbers is authoritative. \`git log\` and \`git diff --stat\` are fine for commit-range overview, and \`git diff\` / \`git diff --cached\` are fine for inspecting *your own* uncommitted changes \u2014 but PR review content MUST come from diffPath. before your review is submitted, a one-time coverage pre-flight may error listing unread TOC regions. retry the same create_pull_request_review call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session. the local branch is 'localBranch' (pr-{number}), not the remote branch name. when pushing, omit branchName to use the current branch. do not use remoteBranch as a local branch name.` + incrementalInstructions + hookWarningInstructions + commitLogInstructions
|
|
142510
144013
|
};
|
|
142511
144014
|
})
|
|
142512
144015
|
});
|
|
@@ -142734,244 +144237,6 @@ function CommitInfoTool(ctx) {
|
|
|
142734
144237
|
});
|
|
142735
144238
|
}
|
|
142736
144239
|
|
|
142737
|
-
// mcp/git.ts
|
|
142738
|
-
function getPushDestination(branch, storedDest) {
|
|
142739
|
-
if (storedDest && storedDest.localBranch === branch) {
|
|
142740
|
-
log.debug(`using stored push destination: ${storedDest.remoteName}/${storedDest.remoteBranch}`);
|
|
142741
|
-
const url4 = $("git", ["remote", "get-url", "--push", storedDest.remoteName], {
|
|
142742
|
-
log: false
|
|
142743
|
-
}).trim();
|
|
142744
|
-
return { remoteName: storedDest.remoteName, remoteBranch: storedDest.remoteBranch, url: url4 };
|
|
142745
|
-
}
|
|
142746
|
-
try {
|
|
142747
|
-
const pushRemote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
|
|
142748
|
-
const merge4 = $("git", ["config", `branch.${branch}.merge`], { log: false }).trim();
|
|
142749
|
-
const remoteBranch = merge4.replace(/^refs\/heads\//, "");
|
|
142750
|
-
const url4 = $("git", ["remote", "get-url", "--push", pushRemote], { log: false }).trim();
|
|
142751
|
-
return { remoteName: pushRemote, remoteBranch, url: url4 };
|
|
142752
|
-
} catch {
|
|
142753
|
-
log.debug(`no push config for ${branch}, falling back to origin/${branch}`);
|
|
142754
|
-
const url4 = $("git", ["remote", "get-url", "--push", "origin"], { log: false }).trim();
|
|
142755
|
-
return { remoteName: "origin", remoteBranch: branch, url: url4 };
|
|
142756
|
-
}
|
|
142757
|
-
}
|
|
142758
|
-
function normalizeUrl(url4) {
|
|
142759
|
-
return url4.replace(/\.git$/, "").toLowerCase();
|
|
142760
|
-
}
|
|
142761
|
-
function validatePushDestination(ctx, branch) {
|
|
142762
|
-
const pushUrl = ctx.toolState.pushUrl;
|
|
142763
|
-
if (!pushUrl) throw new Error("pushUrl not set - setupGit must run before push_branch");
|
|
142764
|
-
const dest = getPushDestination(branch, ctx.toolState.pushDest);
|
|
142765
|
-
if (normalizeUrl(dest.url) !== normalizeUrl(pushUrl)) {
|
|
142766
|
-
throw new Error(
|
|
142767
|
-
`Push blocked: destination does not match expected repository.
|
|
142768
|
-
Expected: ${pushUrl}
|
|
142769
|
-
Actual: ${dest.url}
|
|
142770
|
-
Git configuration may have been tampered with.`
|
|
142771
|
-
);
|
|
142772
|
-
}
|
|
142773
|
-
return dest;
|
|
142774
|
-
}
|
|
142775
|
-
var PushBranch = type({
|
|
142776
|
-
branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
|
|
142777
|
-
force: type.boolean.describe("Force push (use with caution)").default(false)
|
|
142778
|
-
});
|
|
142779
|
-
function PushBranchTool(ctx) {
|
|
142780
|
-
const defaultBranch = ctx.repo.data.default_branch || "main";
|
|
142781
|
-
const pushPermission = ctx.payload.push;
|
|
142782
|
-
return tool({
|
|
142783
|
-
name: "push_branch",
|
|
142784
|
-
description: "Push the current branch to the remote repository. Omit branchName to push the current branch (recommended). If specifying branchName, use the LOCAL branch name (e.g., 'pr-1'), not the remote branch name. The correct remote and remote branch are determined automatically from branch config set by checkout_pr. Requires a clean working tree. Runs the repository prepush hook (if configured) before the network push \u2014 hook failure means tests/lint or similar in that script failed, not necessarily a Pullfrog timeout. Never force push unless explicitly requested. Pushes to the default branch are blocked in restricted mode.",
|
|
142785
|
-
parameters: PushBranch,
|
|
142786
|
-
execute: execute(async ({ branchName, force }) => {
|
|
142787
|
-
if (pushPermission === "disabled") {
|
|
142788
|
-
throw new Error("Push is disabled. This repository is configured for read-only access.");
|
|
142789
|
-
}
|
|
142790
|
-
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
|
142791
|
-
const status = $("git", ["status", "--porcelain"], { log: false });
|
|
142792
|
-
if (status) {
|
|
142793
|
-
throw new Error(
|
|
142794
|
-
`push blocked: working tree is not clean (tracked changes and/or untracked files). commit, discard, or remove stray artifacts before pushing.
|
|
142795
|
-
|
|
142796
|
-
git status:
|
|
142797
|
-
${status}`
|
|
142798
|
-
);
|
|
142799
|
-
}
|
|
142800
|
-
const pushDest = validatePushDestination(ctx, branch);
|
|
142801
|
-
if (pushPermission === "restricted" && pushDest.remoteBranch === defaultBranch) {
|
|
142802
|
-
throw new Error(
|
|
142803
|
-
`Push blocked: cannot push directly to default branch '${pushDest.remoteBranch}'. Create a feature branch and open a PR instead.`
|
|
142804
|
-
);
|
|
142805
|
-
}
|
|
142806
|
-
const refspec = branch === pushDest.remoteBranch ? branch : `${branch}:${pushDest.remoteBranch}`;
|
|
142807
|
-
const pushArgs = force ? ["--force", "-u", pushDest.remoteName, refspec] : ["-u", pushDest.remoteName, refspec];
|
|
142808
|
-
await executeLifecycleHook({ event: "prepush", script: ctx.prepushScript });
|
|
142809
|
-
log.debug(`pushing ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`);
|
|
142810
|
-
if (force) {
|
|
142811
|
-
log.warning(`force pushing - this will overwrite remote history`);
|
|
142812
|
-
}
|
|
142813
|
-
try {
|
|
142814
|
-
await $git("push", pushArgs, {
|
|
142815
|
-
token: ctx.gitToken
|
|
142816
|
-
});
|
|
142817
|
-
} catch (err) {
|
|
142818
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
142819
|
-
if (msg.includes("fetch first") || msg.includes("non-fast-forward")) {
|
|
142820
|
-
throw new Error(
|
|
142821
|
-
`push rejected: the remote branch '${pushDest.remoteBranch}' has new commits you don't have locally.
|
|
142822
|
-
|
|
142823
|
-
to resolve this:
|
|
142824
|
-
1. use git_fetch to fetch the remote branch: git_fetch({ ref: "${pushDest.remoteBranch}" })
|
|
142825
|
-
2. use the git tool to rebase your changes: git({ subcommand: "rebase", args: ["origin/${pushDest.remoteBranch}"] })
|
|
142826
|
-
3. resolve any merge conflicts if needed
|
|
142827
|
-
4. retry push_branch`
|
|
142828
|
-
);
|
|
142829
|
-
}
|
|
142830
|
-
throw err;
|
|
142831
|
-
}
|
|
142832
|
-
return {
|
|
142833
|
-
success: true,
|
|
142834
|
-
branch,
|
|
142835
|
-
remoteBranch: pushDest.remoteBranch,
|
|
142836
|
-
remote: pushDest.remoteName,
|
|
142837
|
-
force,
|
|
142838
|
-
message: `successfully pushed ${branch} to ${pushDest.remoteName}/${pushDest.remoteBranch}`
|
|
142839
|
-
};
|
|
142840
|
-
})
|
|
142841
|
-
});
|
|
142842
|
-
}
|
|
142843
|
-
var AUTH_REQUIRED_REDIRECT = {
|
|
142844
|
-
push: "use the push_branch tool instead \u2014 it handles authentication and permission checks.",
|
|
142845
|
-
fetch: "use the git_fetch tool instead \u2014 it handles authentication.",
|
|
142846
|
-
pull: "use git_fetch to fetch the remote ref, then use this git tool with subcommand 'merge' or 'rebase' locally.",
|
|
142847
|
-
clone: "the repository is already cloned. use checkout_pr for PR branches."
|
|
142848
|
-
};
|
|
142849
|
-
var NOSHELL_BLOCKED_SUBCOMMANDS = {
|
|
142850
|
-
config: "Blocked: git config can set up filter drivers or hooks that execute arbitrary code.",
|
|
142851
|
-
submodule: "Blocked: git submodule can reference malicious repositories and execute code on update.",
|
|
142852
|
-
"update-index": "Blocked: git update-index can modify index entries in ways that bypass file protections.",
|
|
142853
|
-
"filter-branch": "Blocked: git filter-branch executes arbitrary code on repository history.",
|
|
142854
|
-
replace: "Blocked: git replace can redirect object lookups.",
|
|
142855
|
-
// subcommands that accept --exec or similar flags for arbitrary code execution
|
|
142856
|
-
rebase: "Blocked: git rebase --exec can execute arbitrary shell commands.",
|
|
142857
|
-
bisect: "Blocked: git bisect run can execute arbitrary shell commands."
|
|
142858
|
-
};
|
|
142859
|
-
var NOSHELL_BLOCKED_ARGS = ["--exec", "--extcmd", "--upload-pack", "--receive-pack"];
|
|
142860
|
-
var COLLAPSE_THRESHOLD = 200;
|
|
142861
|
-
var subcommandPattern = regex("^[a-z][a-z0-9-]*$");
|
|
142862
|
-
var Git = type({
|
|
142863
|
-
subcommand: type(subcommandPattern).describe("Git subcommand (e.g., 'status', 'log', 'diff')"),
|
|
142864
|
-
args: type.string.array().describe("Additional arguments for the git command").optional()
|
|
142865
|
-
});
|
|
142866
|
-
function GitTool(ctx) {
|
|
142867
|
-
return tool({
|
|
142868
|
-
name: "git",
|
|
142869
|
-
description: "Run git commands. For push/fetch/pull, use the dedicated MCP tools instead (push_branch, git_fetch).",
|
|
142870
|
-
parameters: Git,
|
|
142871
|
-
execute: execute(async (params) => {
|
|
142872
|
-
const subcommand = params.subcommand;
|
|
142873
|
-
const args2 = params.args ?? [];
|
|
142874
|
-
const redirect = AUTH_REQUIRED_REDIRECT[subcommand];
|
|
142875
|
-
if (redirect) {
|
|
142876
|
-
throw new Error(`git ${subcommand} is not available through this tool \u2014 ${redirect}`);
|
|
142877
|
-
}
|
|
142878
|
-
if (ctx.payload.shell === "disabled") {
|
|
142879
|
-
const blocked = NOSHELL_BLOCKED_SUBCOMMANDS[subcommand];
|
|
142880
|
-
if (blocked) {
|
|
142881
|
-
throw new Error(blocked);
|
|
142882
|
-
}
|
|
142883
|
-
for (const arg of args2) {
|
|
142884
|
-
const isBlocked = NOSHELL_BLOCKED_ARGS.some(
|
|
142885
|
-
(flag) => arg === flag || arg.startsWith(flag + "=")
|
|
142886
|
-
);
|
|
142887
|
-
if (isBlocked) {
|
|
142888
|
-
throw new Error(
|
|
142889
|
-
`Blocked: '${arg}' flag can execute arbitrary code and is not allowed.`
|
|
142890
|
-
);
|
|
142891
|
-
}
|
|
142892
|
-
}
|
|
142893
|
-
}
|
|
142894
|
-
const output = $("git", [subcommand, ...args2], { log: false });
|
|
142895
|
-
const lineCount = output.split("\n").length;
|
|
142896
|
-
if (lineCount > COLLAPSE_THRESHOLD) {
|
|
142897
|
-
log.group(`git ${subcommand} output (${lineCount} lines)`, () => {
|
|
142898
|
-
log.info(output);
|
|
142899
|
-
});
|
|
142900
|
-
} else if (output) {
|
|
142901
|
-
log.info(output);
|
|
142902
|
-
}
|
|
142903
|
-
return { success: true, output };
|
|
142904
|
-
})
|
|
142905
|
-
});
|
|
142906
|
-
}
|
|
142907
|
-
var GitFetch = type({
|
|
142908
|
-
ref: type.string.describe("Ref to fetch: branch name, tag, or 'pull/N/head' for PRs"),
|
|
142909
|
-
depth: type.number.describe("Fetch depth (for shallow clones)").optional()
|
|
142910
|
-
});
|
|
142911
|
-
function GitFetchTool(ctx) {
|
|
142912
|
-
return tool({
|
|
142913
|
-
name: "git_fetch",
|
|
142914
|
-
description: "Fetch refs from remote repository. Use this instead of git fetch directly.",
|
|
142915
|
-
parameters: GitFetch,
|
|
142916
|
-
execute: execute(async (params) => {
|
|
142917
|
-
const fetchArgs = ["--no-tags", "origin", params.ref];
|
|
142918
|
-
if (params.depth !== void 0) {
|
|
142919
|
-
fetchArgs.push(`--depth=${params.depth}`);
|
|
142920
|
-
}
|
|
142921
|
-
await $git("fetch", fetchArgs, {
|
|
142922
|
-
token: ctx.gitToken
|
|
142923
|
-
});
|
|
142924
|
-
return { success: true, ref: params.ref };
|
|
142925
|
-
})
|
|
142926
|
-
});
|
|
142927
|
-
}
|
|
142928
|
-
var DeleteBranch = type({
|
|
142929
|
-
branchName: type.string.describe("Remote branch to delete")
|
|
142930
|
-
});
|
|
142931
|
-
function DeleteBranchTool(ctx) {
|
|
142932
|
-
const pushPermission = ctx.payload.push;
|
|
142933
|
-
return tool({
|
|
142934
|
-
name: "delete_branch",
|
|
142935
|
-
description: "Delete a remote branch. Requires push: enabled permission.",
|
|
142936
|
-
parameters: DeleteBranch,
|
|
142937
|
-
execute: execute(async (params) => {
|
|
142938
|
-
if (pushPermission !== "enabled") {
|
|
142939
|
-
throw new Error(
|
|
142940
|
-
"Branch deletion requires push: enabled permission. Current mode only allows pushing to non-protected branches."
|
|
142941
|
-
);
|
|
142942
|
-
}
|
|
142943
|
-
await $git("push", ["origin", "--delete", params.branchName], {
|
|
142944
|
-
token: ctx.gitToken
|
|
142945
|
-
});
|
|
142946
|
-
return { success: true, deleted: params.branchName };
|
|
142947
|
-
})
|
|
142948
|
-
});
|
|
142949
|
-
}
|
|
142950
|
-
var PushTags = type({
|
|
142951
|
-
tag: type.string.describe("Tag name to push"),
|
|
142952
|
-
force: type.boolean.describe("Force push the tag").default(false)
|
|
142953
|
-
});
|
|
142954
|
-
function PushTagsTool(ctx) {
|
|
142955
|
-
const pushPermission = ctx.payload.push;
|
|
142956
|
-
return tool({
|
|
142957
|
-
name: "push_tags",
|
|
142958
|
-
description: "Push a tag to remote. Requires push: enabled permission.",
|
|
142959
|
-
parameters: PushTags,
|
|
142960
|
-
execute: execute(async (params) => {
|
|
142961
|
-
if (pushPermission !== "enabled") {
|
|
142962
|
-
throw new Error(
|
|
142963
|
-
"Tag pushing requires push: enabled permission. Current mode only allows pushing branches."
|
|
142964
|
-
);
|
|
142965
|
-
}
|
|
142966
|
-
const pushArgs = [...params.force ? ["-f"] : [], "origin", `refs/tags/${params.tag}`];
|
|
142967
|
-
await $git("push", pushArgs, {
|
|
142968
|
-
token: ctx.gitToken
|
|
142969
|
-
});
|
|
142970
|
-
return { success: true, tag: params.tag };
|
|
142971
|
-
})
|
|
142972
|
-
});
|
|
142973
|
-
}
|
|
142974
|
-
|
|
142975
144240
|
// mcp/issue.ts
|
|
142976
144241
|
var Issue = type({
|
|
142977
144242
|
title: type.string.describe("the title of the issue"),
|
|
@@ -143434,221 +144699,6 @@ function PullRequestInfoTool(ctx) {
|
|
|
143434
144699
|
});
|
|
143435
144700
|
}
|
|
143436
144701
|
|
|
143437
|
-
// mcp/review.ts
|
|
143438
|
-
function getHttpStatus(err) {
|
|
143439
|
-
if (typeof err !== "object" || err === null) return void 0;
|
|
143440
|
-
const status = err.status;
|
|
143441
|
-
return typeof status === "number" ? status : void 0;
|
|
143442
|
-
}
|
|
143443
|
-
var CreatePullRequestReview = type({
|
|
143444
|
-
pull_number: type.number.describe("The pull request number to review"),
|
|
143445
|
-
body: type.string.describe(
|
|
143446
|
-
"1-2 sentence high-level summary with urgency level, critical callouts, and feedback about code outside the diff. Specific feedback on diff lines goes in 'comments' array."
|
|
143447
|
-
).optional(),
|
|
143448
|
-
approved: type.boolean.describe(
|
|
143449
|
-
"Set to true to submit as an approval. ONLY when the review contains no actionable feedback \u2014 neither inline comments nor actionable content in the body. Defaults to false (comment-only review). Rejections are not supported."
|
|
143450
|
-
).optional(),
|
|
143451
|
-
commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
|
|
143452
|
-
comments: type({
|
|
143453
|
-
path: type.string.describe(
|
|
143454
|
-
"The file path to comment on (relative to repo root). Must be a file that appears in the PR diff."
|
|
143455
|
-
),
|
|
143456
|
-
line: type.number.describe(
|
|
143457
|
-
"Line number to comment on. For multi-line ranges, this is the end line. Use NEW column from diff format."
|
|
143458
|
-
),
|
|
143459
|
-
side: type.enumerated("LEFT", "RIGHT").describe(
|
|
143460
|
-
"Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT."
|
|
143461
|
-
).optional(),
|
|
143462
|
-
body: type.string.describe("Explanatory comment text (optional if suggestion is provided)").optional(),
|
|
143463
|
-
suggestion: type.string.describe(
|
|
143464
|
-
"Full replacement code for the line range [start_line, line]. MUST preserve the exact indentation of the original code."
|
|
143465
|
-
).optional(),
|
|
143466
|
-
start_line: type.number.describe(
|
|
143467
|
-
"Start line for multi-line comment ranges. Omit for single-line comments. The range [start_line, line] defines which lines a suggestion replaces."
|
|
143468
|
-
).optional()
|
|
143469
|
-
}).array().describe(
|
|
143470
|
-
"Inline comments on lines within diff hunks. Feedback about code outside the diff goes in 'body' instead."
|
|
143471
|
-
).optional()
|
|
143472
|
-
});
|
|
143473
|
-
function CreatePullRequestReviewTool(ctx) {
|
|
143474
|
-
return tool({
|
|
143475
|
-
name: "create_pull_request_review",
|
|
143476
|
-
description: `Submit a review for an existing pull request. Each call creates a permanent, visible review on the PR \u2014 NEVER submit test or diagnostic reviews. Reviews with no body AND no comments are silently skipped (nothing to post). IMPORTANT: 95%+ of feedback should be in 'comments' array with file paths and line numbers. Only use 'body' for a 1-2 sentence summary with urgency and critical callouts. Use 'suggestion' to propose replacement code - MUST preserve exact indentation of original code. Example replacing lines 42-44 (3 lines) with 5 lines: { path: 'src/api.ts', start_line: 42, line: 44, suggestion: ' const result = await fetch(url);\\n if (!result.ok) {\\n log.error(result.status);\\n throw new Error("request failed");\\n }' } CONSTRAINT: Inline comments can ONLY target files and lines that appear in the PR diff. If GitHub rejects comments due to incorrect line numbers, re-read the diff and retry.`,
|
|
143477
|
-
parameters: CreatePullRequestReview,
|
|
143478
|
-
execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => {
|
|
143479
|
-
if (body) body = fixDoubleEscapedString(body);
|
|
143480
|
-
if (body && ctx.toolState.selectedMode === "Review" && ctx.toolState.todoTracker) {
|
|
143481
|
-
ctx.toolState.todoTracker.cancel();
|
|
143482
|
-
await ctx.toolState.todoTracker.settled();
|
|
143483
|
-
ctx.toolState.todoTracker.completeInProgress();
|
|
143484
|
-
const collapsible = ctx.toolState.todoTracker.renderCollapsible();
|
|
143485
|
-
if (collapsible) {
|
|
143486
|
-
body = `${body}
|
|
143487
|
-
|
|
143488
|
-
${collapsible}`;
|
|
143489
|
-
}
|
|
143490
|
-
}
|
|
143491
|
-
ctx.toolState.issueNumber = pull_number;
|
|
143492
|
-
if (!approved && !body && comments.length === 0) {
|
|
143493
|
-
log.info(
|
|
143494
|
-
"review has no body and no inline comments \u2014 skipping submission (no issues found)"
|
|
143495
|
-
);
|
|
143496
|
-
return {
|
|
143497
|
-
success: true,
|
|
143498
|
-
skipped: true,
|
|
143499
|
-
reason: "no issues found \u2014 nothing to post"
|
|
143500
|
-
};
|
|
143501
|
-
}
|
|
143502
|
-
let event = approved ? "APPROVE" : "COMMENT";
|
|
143503
|
-
if (event === "APPROVE" && !ctx.prApproveEnabled) {
|
|
143504
|
-
log.info("prApproveEnabled is disabled \u2014 downgrading APPROVE to COMMENT");
|
|
143505
|
-
event = "COMMENT";
|
|
143506
|
-
}
|
|
143507
|
-
const params = {
|
|
143508
|
-
owner: ctx.repo.owner,
|
|
143509
|
-
repo: ctx.repo.name,
|
|
143510
|
-
pull_number,
|
|
143511
|
-
event
|
|
143512
|
-
};
|
|
143513
|
-
let latestHeadSha;
|
|
143514
|
-
if (commit_id) {
|
|
143515
|
-
params.commit_id = commit_id;
|
|
143516
|
-
} else {
|
|
143517
|
-
const pr = await ctx.octokit.rest.pulls.get({
|
|
143518
|
-
owner: ctx.repo.owner,
|
|
143519
|
-
repo: ctx.repo.name,
|
|
143520
|
-
pull_number
|
|
143521
|
-
});
|
|
143522
|
-
latestHeadSha = pr.data.head.sha;
|
|
143523
|
-
params.commit_id = ctx.toolState.checkoutSha ?? latestHeadSha;
|
|
143524
|
-
if (ctx.toolState.checkoutSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143525
|
-
log.info(
|
|
143526
|
-
`anchoring review to checkout ${ctx.toolState.checkoutSha.slice(0, 7)} (HEAD is now ${latestHeadSha.slice(0, 7)})`
|
|
143527
|
-
);
|
|
143528
|
-
}
|
|
143529
|
-
}
|
|
143530
|
-
const reviewComments = comments.map((comment) => {
|
|
143531
|
-
let commentBody = fixDoubleEscapedString(comment.body || "");
|
|
143532
|
-
if (comment.suggestion !== void 0) {
|
|
143533
|
-
const suggestionBlock = "```suggestion\n" + comment.suggestion + "\n```";
|
|
143534
|
-
commentBody = commentBody ? commentBody + "\n\n" + suggestionBlock : suggestionBlock;
|
|
143535
|
-
}
|
|
143536
|
-
const side = comment.side || "RIGHT";
|
|
143537
|
-
const reviewComment = {
|
|
143538
|
-
path: comment.path,
|
|
143539
|
-
line: comment.line,
|
|
143540
|
-
body: commentBody,
|
|
143541
|
-
side
|
|
143542
|
-
};
|
|
143543
|
-
if (comment.start_line != null && comment.start_line !== comment.line) {
|
|
143544
|
-
reviewComment.start_line = comment.start_line;
|
|
143545
|
-
reviewComment.start_side = side;
|
|
143546
|
-
}
|
|
143547
|
-
return reviewComment;
|
|
143548
|
-
});
|
|
143549
|
-
if (reviewComments.length > 0) {
|
|
143550
|
-
params.comments = reviewComments;
|
|
143551
|
-
}
|
|
143552
|
-
let result;
|
|
143553
|
-
try {
|
|
143554
|
-
result = body ? await createAndSubmitWithFooter(ctx, params, {
|
|
143555
|
-
body,
|
|
143556
|
-
approved: approved ?? false,
|
|
143557
|
-
hasComments: reviewComments.length > 0
|
|
143558
|
-
}) : await ctx.octokit.rest.pulls.createReview(params);
|
|
143559
|
-
} catch (err) {
|
|
143560
|
-
if (getHttpStatus(err) !== 422 || !params.comments?.length) throw err;
|
|
143561
|
-
const details = params.comments.map((c) => {
|
|
143562
|
-
const line = c.line ?? 0;
|
|
143563
|
-
const startLine = c.start_line ?? line;
|
|
143564
|
-
const range2 = startLine !== line ? `${startLine}-${line}` : `${line}`;
|
|
143565
|
-
return `${c.path}:${range2} (${c.side ?? "RIGHT"})`;
|
|
143566
|
-
});
|
|
143567
|
-
throw new Error(
|
|
143568
|
-
`GitHub rejected inline comment(s) with "Line could not be resolved". This usually means the diff changed since you last read it (new commits pushed). Re-read the diff to get current line numbers, or move failing comments to the review body. Affected: ${details.join(", ")}`
|
|
143569
|
-
);
|
|
143570
|
-
}
|
|
143571
|
-
log.debug(`createReview response: ${JSON.stringify(result.data)}`);
|
|
143572
|
-
if (!result.data.id) {
|
|
143573
|
-
throw new Error(`createReview returned invalid data: ${JSON.stringify(result.data)}`);
|
|
143574
|
-
}
|
|
143575
|
-
const reviewId = result.data.id;
|
|
143576
|
-
const reviewNodeId = result.data.node_id;
|
|
143577
|
-
const actuallyReviewedSha = ctx.toolState.checkoutSha ?? params.commit_id;
|
|
143578
|
-
ctx.toolState.review = {
|
|
143579
|
-
id: reviewId,
|
|
143580
|
-
nodeId: reviewNodeId,
|
|
143581
|
-
reviewedSha: actuallyReviewedSha
|
|
143582
|
-
};
|
|
143583
|
-
if (ctx.toolState.checkoutSha && latestHeadSha && latestHeadSha !== ctx.toolState.checkoutSha) {
|
|
143584
|
-
const fromSha = ctx.toolState.checkoutSha;
|
|
143585
|
-
const toSha = latestHeadSha;
|
|
143586
|
-
ctx.toolState.beforeSha = fromSha;
|
|
143587
|
-
ctx.toolState.checkoutSha = toSha;
|
|
143588
|
-
log.info(
|
|
143589
|
-
`new commits detected during review: ${fromSha.slice(0, 7)}..${toSha.slice(0, 7)}`
|
|
143590
|
-
);
|
|
143591
|
-
return {
|
|
143592
|
-
success: true,
|
|
143593
|
-
reviewId,
|
|
143594
|
-
html_url: result.data.html_url,
|
|
143595
|
-
state: result.data.state,
|
|
143596
|
-
user: result.data.user?.login,
|
|
143597
|
-
submitted_at: result.data.submitted_at,
|
|
143598
|
-
newCommits: {
|
|
143599
|
-
from: fromSha,
|
|
143600
|
-
to: toSha,
|
|
143601
|
-
instructions: `new commits were pushed while you were reviewing. call \`${formatMcpToolRef(ctx.agentId, "checkout_pr")}\` again to fetch the latest version \u2014 it will compute the incremental diff automatically. submit another review covering only the new changes. do not repeat feedback from your previous review.`
|
|
143602
|
-
}
|
|
143603
|
-
};
|
|
143604
|
-
}
|
|
143605
|
-
return {
|
|
143606
|
-
success: true,
|
|
143607
|
-
reviewId,
|
|
143608
|
-
html_url: result.data.html_url,
|
|
143609
|
-
state: result.data.state,
|
|
143610
|
-
user: result.data.user?.login,
|
|
143611
|
-
submitted_at: result.data.submitted_at
|
|
143612
|
-
};
|
|
143613
|
-
})
|
|
143614
|
-
});
|
|
143615
|
-
}
|
|
143616
|
-
async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
143617
|
-
const { event: _, ...pendingParams } = params;
|
|
143618
|
-
const pending = await ctx.octokit.rest.pulls.createReview(pendingParams);
|
|
143619
|
-
if (!pending.data.id) {
|
|
143620
|
-
throw new Error(`createReview returned invalid data: ${JSON.stringify(pending.data)}`);
|
|
143621
|
-
}
|
|
143622
|
-
const customParts = [];
|
|
143623
|
-
if (!opts.approved) {
|
|
143624
|
-
const apiUrl = getApiUrl();
|
|
143625
|
-
if (opts.hasComments) {
|
|
143626
|
-
const fixAllUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143627
|
-
const fixApprovedUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix-approved&review_id=${pending.data.id}`;
|
|
143628
|
-
customParts.push(`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`);
|
|
143629
|
-
} else {
|
|
143630
|
-
const fixUrl = `${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${params.pull_number}?action=fix&review_id=${pending.data.id}`;
|
|
143631
|
-
customParts.push(`[Fix it \u2794](${fixUrl})`);
|
|
143632
|
-
}
|
|
143633
|
-
}
|
|
143634
|
-
const footer = buildPullfrogFooter({
|
|
143635
|
-
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
143636
|
-
customParts,
|
|
143637
|
-
model: ctx.toolState.model
|
|
143638
|
-
});
|
|
143639
|
-
return ctx.octokit.rest.pulls.submitReview({
|
|
143640
|
-
owner: params.owner,
|
|
143641
|
-
repo: params.repo,
|
|
143642
|
-
pull_number: params.pull_number,
|
|
143643
|
-
review_id: pending.data.id,
|
|
143644
|
-
event: params.event,
|
|
143645
|
-
body: opts.body + footer
|
|
143646
|
-
});
|
|
143647
|
-
}
|
|
143648
|
-
async function reportReviewNodeId(ctx, params) {
|
|
143649
|
-
await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
|
|
143650
|
-
}
|
|
143651
|
-
|
|
143652
144702
|
// mcp/reviewComments.ts
|
|
143653
144703
|
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
143654
144704
|
import { join as join6 } from "node:path";
|
|
@@ -143696,7 +144746,7 @@ query ($owner: String!, $name: String!, $prNumber: Int!) {
|
|
|
143696
144746
|
}
|
|
143697
144747
|
}
|
|
143698
144748
|
`;
|
|
143699
|
-
function
|
|
144749
|
+
function countLines2(str) {
|
|
143700
144750
|
let count = 1;
|
|
143701
144751
|
let index = -1;
|
|
143702
144752
|
while ((index = str.indexOf("\n", index + 1)) !== -1) {
|
|
@@ -143838,13 +144888,13 @@ function formatReviewThreads(threadBlocks, header) {
|
|
|
143838
144888
|
const reviewBodyLines = [];
|
|
143839
144889
|
if (header.reviewBody) {
|
|
143840
144890
|
reviewBodyLines.push("## Review Body", "", header.reviewBody, "");
|
|
143841
|
-
currentLine += reviewBodyLines.reduce((sum, line) => sum +
|
|
144891
|
+
currentLine += reviewBodyLines.reduce((sum, line) => sum + countLines2(line), 0);
|
|
143842
144892
|
}
|
|
143843
144893
|
const tocEntries = [];
|
|
143844
144894
|
const threadLines = [];
|
|
143845
144895
|
for (const block of threadBlocks) {
|
|
143846
144896
|
const startLine = currentLine;
|
|
143847
|
-
const actualLineCount = block.content.reduce((sum, line) => sum +
|
|
144897
|
+
const actualLineCount = block.content.reduce((sum, line) => sum + countLines2(line), 0);
|
|
143848
144898
|
const endLine = currentLine + actualLineCount - 1;
|
|
143849
144899
|
tocEntries.push(`- ${block.path}:${block.lineRange} \u2192 lines ${startLine}-${endLine}`);
|
|
143850
144900
|
threadLines.push(...block.content);
|
|
@@ -144167,7 +145217,7 @@ GitHub's markdown parser requires a blank line between ALL block-level elements.
|
|
|
144167
145217
|
Rules:
|
|
144168
145218
|
- \`##\` titles and key-change bullet lead-ins are plain-language summaries; backtick only actual code tokens (files, types, functions) where they appear in the title
|
|
144169
145219
|
- ALL variable names, identifiers, and file names in body text must be in backticks
|
|
144170
|
-
- ALL file references MUST link to the PR Files Changed view.
|
|
145220
|
+
- ALL file references MUST link to the PR Files Changed view. Use the \`diff-<hex>\` anchor precomputed next to each filename in the \`checkout_pr\` TOC \u2014 do NOT run \`sha256sum\` or any other shell command to compute anchors. NEVER fabricate hex strings. If a file is not in the TOC, omit the \`#diff-\` anchor rather than guessing.
|
|
144171
145221
|
- Add <br/> before each ## heading for visual spacing. Do NOT use horizontal rules (---)
|
|
144172
145222
|
- Do NOT include raw diff stats like '+123 / -45' or line counts
|
|
144173
145223
|
- Do NOT include code blocks or repeat diff contents
|
|
@@ -144242,7 +145292,7 @@ ${learningsStep(t, 6)}`
|
|
|
144242
145292
|
description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
|
|
144243
145293
|
prompt: `### Checklist
|
|
144244
145294
|
|
|
144245
|
-
1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`.
|
|
145295
|
+
1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. read the diff TOC first and treat its file line ranges as your coverage checklist.
|
|
144246
145296
|
|
|
144247
145297
|
2. For each area of change:
|
|
144248
145298
|
- read the diff and trace data flow, check boundaries, and verify assumptions
|
|
@@ -144259,6 +145309,7 @@ ${learningsStep(t, 6)}`
|
|
|
144259
145309
|
4. Submit \u2014 ALWAYS submit exactly one review via \`${t("create_pull_request_review")}\`.
|
|
144260
145310
|
Do NOT call \`report_progress\` \u2014 the review is the final record and the progress
|
|
144261
145311
|
comment will be cleaned up automatically.
|
|
145312
|
+
note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
|
|
144262
145313
|
|
|
144263
145314
|
- **critical issues** (blocks merge \u2014 bugs, security, data loss):
|
|
144264
145315
|
\`approved: false\`. Body begins with a GitHub alert blockquote, e.g.:
|
|
@@ -144276,7 +145327,7 @@ ${learningsStep(t, 6)}`
|
|
|
144276
145327
|
description: "Re-review a PR after new commits are pushed; focus on new changes since the last review",
|
|
144277
145328
|
prompt: `### Checklist
|
|
144278
145329
|
|
|
144279
|
-
1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available).
|
|
145330
|
+
1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available). read the diff TOC first and use its line ranges as your coverage checklist.
|
|
144280
145331
|
|
|
144281
145332
|
2. If \`incrementalDiffPath\` is present, read it to see what changed since the last review. This is a range-diff that isolates the net changes, filtering out base branch noise. If not present, fall back to reviewing the full PR diff.
|
|
144282
145333
|
|
|
@@ -144300,6 +145351,7 @@ ${learningsStep(t, 6)}`
|
|
|
144300
145351
|
- in some cases you may receive a complete diff for the whole pull request instead of an incremental one. when this happens, you will need to determine what changes have happened since Pullfrog's most recent review.
|
|
144301
145352
|
|
|
144302
145353
|
7. Submit \u2014 Do NOT call \`report_progress\` or \`create_issue_comment\` \u2014 the review is the final record and the progress comment will be cleaned up automatically. the review body always includes the reviewed changes from step 6a. append \`Prior review feedback:\\n\` with the checklist from step 6b only if any prior comments were addressed. Follow these rules:
|
|
145354
|
+
- note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
|
|
144303
145355
|
- IF NO NEW ISSUES, NON-SUBSTANTIVE CHANGES ONLY (trivial formatting, import reordering, comment tweaks): do NOT submit a review. Do NOT call \`report_progress\`. Exit \u2014 the progress comment will be cleaned up automatically.
|
|
144304
145356
|
- ELSE IF NEW CRITICAL ISSUES (blocks merge): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with a GitHub alert blockquote (e.g. \`> [!CAUTION]\\n> This PR introduces ...\`), then the reviewed changes summary and prior feedback (if any).
|
|
144305
145357
|
- ELSE IF NEW RECOMMENDED CHANGES (non-critical): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!IMPORTANT]\\n> ...\` alert, then the reviewed changes summary and prior feedback (if any).
|
|
@@ -144404,7 +145456,7 @@ ${PR_SUMMARY_FORMAT}`
|
|
|
144404
145456
|
}
|
|
144405
145457
|
];
|
|
144406
145458
|
}
|
|
144407
|
-
var modes = computeModes("
|
|
145459
|
+
var modes = computeModes("opencode");
|
|
144408
145460
|
|
|
144409
145461
|
// mcp/selectMode.ts
|
|
144410
145462
|
var SelectModeParams = type({
|
|
@@ -144607,13 +145659,19 @@ function detectSandboxMethod() {
|
|
|
144607
145659
|
} catch {
|
|
144608
145660
|
}
|
|
144609
145661
|
detectedSandboxMethod = "none";
|
|
144610
|
-
log.info("PID namespace isolation not available
|
|
145662
|
+
log.info("PID namespace isolation not available");
|
|
144611
145663
|
return "none";
|
|
144612
145664
|
}
|
|
144613
145665
|
var PROC_CLEANUP = "umount /proc 2>/dev/null; umount /proc 2>/dev/null; mount -t proc proc /proc 2>/dev/null;";
|
|
144614
145666
|
function spawnShell(params) {
|
|
144615
145667
|
const spawnOpts = { env: params.env, cwd: params.cwd, stdio: params.stdio, detached: true };
|
|
144616
145668
|
const sandboxMethod = detectSandboxMethod();
|
|
145669
|
+
const ci = process.env.CI === "true";
|
|
145670
|
+
if (ci && sandboxMethod === "none") {
|
|
145671
|
+
throw new Error(
|
|
145672
|
+
"pid namespace isolation is required in CI but unavailable (both unshare and sudo unshare failed)"
|
|
145673
|
+
);
|
|
145674
|
+
}
|
|
144617
145675
|
if (sandboxMethod === "unshare") {
|
|
144618
145676
|
return spawn2(
|
|
144619
145677
|
"unshare",
|
|
@@ -144761,11 +145819,11 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
144761
145819
|
await killProcessGroup(proc);
|
|
144762
145820
|
}
|
|
144763
145821
|
}, timeout);
|
|
144764
|
-
const exitCode = await new Promise((
|
|
145822
|
+
const exitCode = await new Promise((resolve3) => {
|
|
144765
145823
|
const done = (code) => {
|
|
144766
145824
|
exited = true;
|
|
144767
145825
|
clearTimeout(timeoutId);
|
|
144768
|
-
|
|
145826
|
+
resolve3(code);
|
|
144769
145827
|
};
|
|
144770
145828
|
proc.on("exit", done);
|
|
144771
145829
|
proc.on("error", () => done(null));
|
|
@@ -144904,12 +145962,12 @@ function readEnvPort() {
|
|
|
144904
145962
|
return parsed2;
|
|
144905
145963
|
}
|
|
144906
145964
|
function isPortAvailable(port) {
|
|
144907
|
-
return new Promise((
|
|
145965
|
+
return new Promise((resolve3) => {
|
|
144908
145966
|
const server = createServer();
|
|
144909
145967
|
server.unref();
|
|
144910
|
-
server.once("error", () =>
|
|
145968
|
+
server.once("error", () => resolve3(false));
|
|
144911
145969
|
server.once("listening", () => {
|
|
144912
|
-
server.close(() =>
|
|
145970
|
+
server.close(() => resolve3(true));
|
|
144913
145971
|
});
|
|
144914
145972
|
server.listen(port, mcpHost);
|
|
144915
145973
|
});
|
|
@@ -145049,9 +146107,12 @@ async function killBackgroundProcesses(toolState) {
|
|
|
145049
146107
|
async function startMcpHttpServer(ctx, options) {
|
|
145050
146108
|
const tools = buildOrchestratorTools(ctx, options?.outputSchema);
|
|
145051
146109
|
const startResult = await selectMcpPort(ctx, tools);
|
|
146110
|
+
let disposed = false;
|
|
145052
146111
|
return {
|
|
145053
146112
|
url: startResult.url,
|
|
145054
146113
|
[Symbol.asyncDispose]: async () => {
|
|
146114
|
+
if (disposed) return;
|
|
146115
|
+
disposed = true;
|
|
145055
146116
|
closeBrowserDaemon(ctx.toolState);
|
|
145056
146117
|
await killBackgroundProcesses(ctx.toolState);
|
|
145057
146118
|
await startResult.server.stop();
|
|
@@ -145246,39 +146307,6 @@ var ThinkingTimer = class {
|
|
|
145246
146307
|
}
|
|
145247
146308
|
};
|
|
145248
146309
|
|
|
145249
|
-
// agents/shared.ts
|
|
145250
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
145251
|
-
var MAX_STDERR_LINES = 20;
|
|
145252
|
-
var MAX_COMMIT_RETRIES = 3;
|
|
145253
|
-
function getGitStatus() {
|
|
145254
|
-
try {
|
|
145255
|
-
return execFileSync2("git", ["status", "--porcelain"], {
|
|
145256
|
-
encoding: "utf-8",
|
|
145257
|
-
timeout: 1e4
|
|
145258
|
-
}).trim();
|
|
145259
|
-
} catch {
|
|
145260
|
-
return "";
|
|
145261
|
-
}
|
|
145262
|
-
}
|
|
145263
|
-
function buildCommitPrompt(_agentId, status) {
|
|
145264
|
-
return [
|
|
145265
|
-
`UNCOMMITTED CHANGES \u2014 the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`,
|
|
145266
|
-
"",
|
|
145267
|
-
"```",
|
|
145268
|
-
status,
|
|
145269
|
-
"```"
|
|
145270
|
-
].join("\n");
|
|
145271
|
-
}
|
|
145272
|
-
var agent = (input) => {
|
|
145273
|
-
return {
|
|
145274
|
-
...input,
|
|
145275
|
-
run: async (ctx) => {
|
|
145276
|
-
log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
|
|
145277
|
-
return input.run(ctx);
|
|
145278
|
-
}
|
|
145279
|
-
};
|
|
145280
|
-
};
|
|
145281
|
-
|
|
145282
146310
|
// agents/claude.ts
|
|
145283
146311
|
async function installClaudeCli() {
|
|
145284
146312
|
return await installFromNpmTarball({
|
|
@@ -145317,6 +146345,7 @@ async function runClaude(params) {
|
|
|
145317
146345
|
let finalOutput = "";
|
|
145318
146346
|
let sessionId;
|
|
145319
146347
|
let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
146348
|
+
let accumulatedCostUsd = 0;
|
|
145320
146349
|
let tokensLogged = false;
|
|
145321
146350
|
function buildUsage() {
|
|
145322
146351
|
const totalInput = accumulatedTokens.input + accumulatedTokens.cacheRead + accumulatedTokens.cacheWrite;
|
|
@@ -145325,7 +146354,8 @@ async function runClaude(params) {
|
|
|
145325
146354
|
inputTokens: totalInput,
|
|
145326
146355
|
outputTokens: accumulatedTokens.output,
|
|
145327
146356
|
cacheReadTokens: accumulatedTokens.cacheRead || void 0,
|
|
145328
|
-
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
|
|
146357
|
+
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
|
|
146358
|
+
costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
|
|
145329
146359
|
} : void 0;
|
|
145330
146360
|
}
|
|
145331
146361
|
const handlers2 = {
|
|
@@ -145342,6 +146372,12 @@ async function runClaude(params) {
|
|
|
145342
146372
|
finalOutput = message;
|
|
145343
146373
|
} else if (block.type === "tool_use") {
|
|
145344
146374
|
const toolName = block.name || "unknown";
|
|
146375
|
+
if (params.onToolUse) {
|
|
146376
|
+
params.onToolUse({
|
|
146377
|
+
toolName,
|
|
146378
|
+
input: block.input
|
|
146379
|
+
});
|
|
146380
|
+
}
|
|
145345
146381
|
thinkingTimer.markToolCall();
|
|
145346
146382
|
log.toolCall({ toolName, input: block.input || {} });
|
|
145347
146383
|
if (toolName.includes("report_progress") && params.todoTracker) {
|
|
@@ -145357,6 +146393,8 @@ async function runClaude(params) {
|
|
|
145357
146393
|
if (msgUsage) {
|
|
145358
146394
|
accumulatedTokens.input += msgUsage.input_tokens || 0;
|
|
145359
146395
|
accumulatedTokens.output += msgUsage.output_tokens || 0;
|
|
146396
|
+
accumulatedTokens.cacheRead += msgUsage.cache_read_input_tokens || 0;
|
|
146397
|
+
accumulatedTokens.cacheWrite += msgUsage.cache_creation_input_tokens || 0;
|
|
145360
146398
|
}
|
|
145361
146399
|
},
|
|
145362
146400
|
user: (event) => {
|
|
@@ -145387,19 +146425,18 @@ async function runClaude(params) {
|
|
|
145387
146425
|
const cacheRead = usage?.cache_read_input_tokens || 0;
|
|
145388
146426
|
const cacheWrite = usage?.cache_creation_input_tokens || 0;
|
|
145389
146427
|
const outputTokens = usage?.output_tokens || 0;
|
|
145390
|
-
const
|
|
146428
|
+
const costUsd = typeof event.total_cost_usd === "number" && Number.isFinite(event.total_cost_usd) ? event.total_cost_usd : 0;
|
|
145391
146429
|
accumulatedTokens = { input: inputTokens, output: outputTokens, cacheRead, cacheWrite };
|
|
146430
|
+
accumulatedCostUsd = costUsd;
|
|
145392
146431
|
log.info(`\xBB ${params.label} result: subtype=${subtype}, turns=${numTurns}`);
|
|
145393
146432
|
if (!tokensLogged) {
|
|
145394
|
-
|
|
145395
|
-
|
|
145396
|
-
|
|
145397
|
-
|
|
145398
|
-
|
|
145399
|
-
|
|
145400
|
-
|
|
145401
|
-
[String(totalInput), String(cacheRead), String(cacheWrite), String(outputTokens)]
|
|
145402
|
-
]);
|
|
146433
|
+
logTokenTable({
|
|
146434
|
+
input: inputTokens,
|
|
146435
|
+
cacheRead,
|
|
146436
|
+
cacheWrite,
|
|
146437
|
+
output: outputTokens,
|
|
146438
|
+
costUsd
|
|
146439
|
+
});
|
|
145403
146440
|
tokensLogged = true;
|
|
145404
146441
|
}
|
|
145405
146442
|
} else if (subtype === "error_max_turns") {
|
|
@@ -145434,6 +146471,7 @@ async function runClaude(params) {
|
|
|
145434
146471
|
cwd: params.cwd,
|
|
145435
146472
|
env: params.env,
|
|
145436
146473
|
activityTimeout: 3e5,
|
|
146474
|
+
onActivityTimeout: params.onActivityTimeout,
|
|
145437
146475
|
stdio: ["ignore", "pipe", "pipe"],
|
|
145438
146476
|
onStdout: async (chunk) => {
|
|
145439
146477
|
const text = chunk.toString();
|
|
@@ -145445,25 +146483,33 @@ async function runClaude(params) {
|
|
|
145445
146483
|
for (const line of lines) {
|
|
145446
146484
|
const trimmed = line.trim();
|
|
145447
146485
|
if (!trimmed) continue;
|
|
146486
|
+
let event;
|
|
145448
146487
|
try {
|
|
145449
|
-
|
|
145450
|
-
eventCount++;
|
|
145451
|
-
log.debug(JSON.stringify(event, null, 2));
|
|
145452
|
-
const timeSinceLastActivity = getIdleMs();
|
|
145453
|
-
if (timeSinceLastActivity > 1e4) {
|
|
145454
|
-
log.info(
|
|
145455
|
-
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
|
|
145456
|
-
);
|
|
145457
|
-
}
|
|
145458
|
-
markActivity();
|
|
145459
|
-
const handler2 = handlers2[event.type];
|
|
145460
|
-
if (handler2) {
|
|
145461
|
-
handler2(event);
|
|
145462
|
-
} else {
|
|
145463
|
-
log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
|
|
145464
|
-
}
|
|
146488
|
+
event = JSON.parse(trimmed);
|
|
145465
146489
|
} catch {
|
|
145466
146490
|
log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
|
|
146491
|
+
continue;
|
|
146492
|
+
}
|
|
146493
|
+
eventCount++;
|
|
146494
|
+
log.debug(JSON.stringify(event, null, 2));
|
|
146495
|
+
const timeSinceLastActivity = getIdleMs();
|
|
146496
|
+
if (timeSinceLastActivity > 1e4) {
|
|
146497
|
+
log.info(
|
|
146498
|
+
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s (${params.label} may be processing internally) (${eventCount} events processed so far)`
|
|
146499
|
+
);
|
|
146500
|
+
}
|
|
146501
|
+
markActivity();
|
|
146502
|
+
const handler2 = handlers2[event.type];
|
|
146503
|
+
if (!handler2) {
|
|
146504
|
+
log.debug(`\xBB ${params.label} event (unhandled): type=${event.type}`);
|
|
146505
|
+
continue;
|
|
146506
|
+
}
|
|
146507
|
+
try {
|
|
146508
|
+
handler2(event);
|
|
146509
|
+
} catch (err) {
|
|
146510
|
+
log.info(
|
|
146511
|
+
`\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
|
|
146512
|
+
);
|
|
145467
146513
|
}
|
|
145468
146514
|
}
|
|
145469
146515
|
},
|
|
@@ -145497,16 +146543,9 @@ async function runClaude(params) {
|
|
|
145497
146543
|
if (stderrContext) log.info(`\xBB last stderr output:
|
|
145498
146544
|
${stderrContext}`);
|
|
145499
146545
|
}
|
|
145500
|
-
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
|
|
145501
|
-
|
|
145502
|
-
|
|
145503
|
-
[
|
|
145504
|
-
{ data: "Input Tokens", header: true },
|
|
145505
|
-
{ data: "Output Tokens", header: true },
|
|
145506
|
-
{ data: "Total Tokens", header: true }
|
|
145507
|
-
],
|
|
145508
|
-
[String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
|
|
145509
|
-
]);
|
|
146546
|
+
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
|
|
146547
|
+
logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
|
|
146548
|
+
tokensLogged = true;
|
|
145510
146549
|
}
|
|
145511
146550
|
const usage = buildUsage();
|
|
145512
146551
|
if (result.exitCode !== 0) {
|
|
@@ -145539,7 +146578,7 @@ ${stderrContext}`);
|
|
|
145539
146578
|
params.todoTracker?.cancel();
|
|
145540
146579
|
const duration4 = performance6.now() - startTime;
|
|
145541
146580
|
const errorMessage = error49 instanceof Error ? error49.message : String(error49);
|
|
145542
|
-
const isActivityTimeout =
|
|
146581
|
+
const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
|
|
145543
146582
|
const stderrContext = recentStderr.slice(-10).join("\n");
|
|
145544
146583
|
const diagnosis = lastProviderError ? `likely cause: ${lastProviderError}` : eventCount === 0 ? "Claude produced 0 stdout events - check if the API is reachable" : `${eventCount} events were processed before the hang`;
|
|
145545
146584
|
log.info(
|
|
@@ -145630,8 +146669,7 @@ var claude = agent({
|
|
|
145630
146669
|
"--effort",
|
|
145631
146670
|
effort,
|
|
145632
146671
|
"--disallowedTools",
|
|
145633
|
-
"Bash"
|
|
145634
|
-
"Agent(Bash)"
|
|
146672
|
+
"Bash,Agent(Bash)"
|
|
145635
146673
|
];
|
|
145636
146674
|
if (model) {
|
|
145637
146675
|
baseArgs.push("--model", model);
|
|
@@ -145644,11 +146682,19 @@ var claude = agent({
|
|
|
145644
146682
|
log.info(`\xBB effort: ${effort}`);
|
|
145645
146683
|
log.debug(`\xBB starting Pullfrog (Claude Code): node ${baseArgs.join(" ")}`);
|
|
145646
146684
|
log.debug(`\xBB working directory: ${repoDir}`);
|
|
145647
|
-
const runParams = {
|
|
146685
|
+
const runParams = {
|
|
146686
|
+
label: "Pullfrog",
|
|
146687
|
+
cwd: repoDir,
|
|
146688
|
+
env: env2,
|
|
146689
|
+
todoTracker: ctx.todoTracker,
|
|
146690
|
+
onActivityTimeout: ctx.onActivityTimeout,
|
|
146691
|
+
onToolUse: ctx.onToolUse
|
|
146692
|
+
};
|
|
145648
146693
|
let result = await runClaude({
|
|
145649
146694
|
...runParams,
|
|
145650
146695
|
args: [...baseArgs, "-p", ctx.instructions.full]
|
|
145651
146696
|
});
|
|
146697
|
+
let aggregatedUsage = result.usage;
|
|
145652
146698
|
for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
|
|
145653
146699
|
if (!result.success || !result.sessionId) break;
|
|
145654
146700
|
const status = getGitStatus();
|
|
@@ -145665,12 +146711,13 @@ ${status}`);
|
|
|
145665
146711
|
result.sessionId
|
|
145666
146712
|
]
|
|
145667
146713
|
});
|
|
146714
|
+
aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
|
|
145668
146715
|
}
|
|
145669
|
-
return result;
|
|
146716
|
+
return { ...result, usage: aggregatedUsage };
|
|
145670
146717
|
}
|
|
145671
146718
|
});
|
|
145672
146719
|
|
|
145673
|
-
// agents/
|
|
146720
|
+
// agents/opencode.ts
|
|
145674
146721
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
145675
146722
|
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
145676
146723
|
import { join as join10 } from "node:path";
|
|
@@ -145748,6 +146795,7 @@ async function runOpenCode(params) {
|
|
|
145748
146795
|
const thinkingTimer = new ThinkingTimer();
|
|
145749
146796
|
let finalOutput = "";
|
|
145750
146797
|
let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
146798
|
+
let accumulatedCostUsd = 0;
|
|
145751
146799
|
let tokensLogged = false;
|
|
145752
146800
|
const toolCallTimings = /* @__PURE__ */ new Map();
|
|
145753
146801
|
let currentStepId = null;
|
|
@@ -145760,7 +146808,8 @@ async function runOpenCode(params) {
|
|
|
145760
146808
|
inputTokens: totalInput,
|
|
145761
146809
|
outputTokens: accumulatedTokens.output,
|
|
145762
146810
|
cacheReadTokens: accumulatedTokens.cacheRead || void 0,
|
|
145763
|
-
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0
|
|
146811
|
+
cacheWriteTokens: accumulatedTokens.cacheWrite || void 0,
|
|
146812
|
+
costUsd: accumulatedCostUsd > 0 ? accumulatedCostUsd : void 0
|
|
145764
146813
|
} : void 0;
|
|
145765
146814
|
}
|
|
145766
146815
|
const handlers2 = {
|
|
@@ -145771,6 +146820,7 @@ async function runOpenCode(params) {
|
|
|
145771
146820
|
log.debug(`\xBB ${params.label} init event (full): ${JSON.stringify(event)}`);
|
|
145772
146821
|
finalOutput = "";
|
|
145773
146822
|
accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
146823
|
+
accumulatedCostUsd = 0;
|
|
145774
146824
|
tokensLogged = false;
|
|
145775
146825
|
},
|
|
145776
146826
|
message: (event) => {
|
|
@@ -145815,6 +146865,9 @@ async function runOpenCode(params) {
|
|
|
145815
146865
|
accumulatedTokens.cacheRead += eventTokens.cache?.read || 0;
|
|
145816
146866
|
accumulatedTokens.cacheWrite += eventTokens.cache?.write || 0;
|
|
145817
146867
|
}
|
|
146868
|
+
if (typeof event.part?.cost === "number" && Number.isFinite(event.part.cost)) {
|
|
146869
|
+
accumulatedCostUsd += event.part.cost;
|
|
146870
|
+
}
|
|
145818
146871
|
if (currentStepId === stepId) {
|
|
145819
146872
|
currentStepId = null;
|
|
145820
146873
|
currentStepType = null;
|
|
@@ -145832,6 +146885,12 @@ async function runOpenCode(params) {
|
|
|
145832
146885
|
if (stepHistory.length > 0) {
|
|
145833
146886
|
stepHistory[stepHistory.length - 1].toolCalls.push(toolName);
|
|
145834
146887
|
}
|
|
146888
|
+
if (params.onToolUse) {
|
|
146889
|
+
params.onToolUse({
|
|
146890
|
+
toolName,
|
|
146891
|
+
input: event.part?.state?.input
|
|
146892
|
+
});
|
|
146893
|
+
}
|
|
145835
146894
|
thinkingTimer.markToolCall();
|
|
145836
146895
|
log.toolCall({ toolName, input: event.part?.state?.input || {} });
|
|
145837
146896
|
if (event.part?.state?.status === "completed" && event.part.state.output) {
|
|
@@ -145887,19 +146946,9 @@ async function runOpenCode(params) {
|
|
|
145887
146946
|
if (event.status === "error") {
|
|
145888
146947
|
log.info(`\xBB ${params.label} failed: ${JSON.stringify(event)}`);
|
|
145889
146948
|
} else {
|
|
145890
|
-
const inputTokens = event.stats?.input_tokens || accumulatedTokens.input || 0;
|
|
145891
|
-
const outputTokens = event.stats?.output_tokens || accumulatedTokens.output || 0;
|
|
145892
|
-
const totalTokens = event.stats?.total_tokens || inputTokens + outputTokens;
|
|
145893
146949
|
log.info(`\xBB run complete: tool_calls=${toolCalls}, duration=${duration4}ms`);
|
|
145894
|
-
if ((
|
|
145895
|
-
|
|
145896
|
-
[
|
|
145897
|
-
{ data: "Input Tokens", header: true },
|
|
145898
|
-
{ data: "Output Tokens", header: true },
|
|
145899
|
-
{ data: "Total Tokens", header: true }
|
|
145900
|
-
],
|
|
145901
|
-
[String(inputTokens), String(outputTokens), String(totalTokens)]
|
|
145902
|
-
]);
|
|
146950
|
+
if ((accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0) && !tokensLogged) {
|
|
146951
|
+
logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
|
|
145903
146952
|
tokensLogged = true;
|
|
145904
146953
|
}
|
|
145905
146954
|
}
|
|
@@ -145916,6 +146965,7 @@ async function runOpenCode(params) {
|
|
|
145916
146965
|
cwd: params.cwd,
|
|
145917
146966
|
env: params.env,
|
|
145918
146967
|
activityTimeout: 3e5,
|
|
146968
|
+
onActivityTimeout: params.onActivityTimeout,
|
|
145919
146969
|
stdio: ["ignore", "pipe", "pipe"],
|
|
145920
146970
|
onStdout: async (chunk) => {
|
|
145921
146971
|
const text = chunk.toString();
|
|
@@ -145927,29 +146977,37 @@ async function runOpenCode(params) {
|
|
|
145927
146977
|
for (const line of lines) {
|
|
145928
146978
|
const trimmed = line.trim();
|
|
145929
146979
|
if (!trimmed) continue;
|
|
146980
|
+
let event;
|
|
145930
146981
|
try {
|
|
145931
|
-
|
|
145932
|
-
eventCount++;
|
|
145933
|
-
log.debug(JSON.stringify(event, null, 2));
|
|
145934
|
-
const timeSinceLastActivity = getIdleMs();
|
|
145935
|
-
if (timeSinceLastActivity > 1e4) {
|
|
145936
|
-
const activeToolCalls = toolCallTimings.size;
|
|
145937
|
-
const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
|
|
145938
|
-
log.info(
|
|
145939
|
-
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
|
|
145940
|
-
);
|
|
145941
|
-
}
|
|
145942
|
-
markActivity();
|
|
145943
|
-
const handler2 = handlers2[event.type];
|
|
145944
|
-
if (handler2) {
|
|
145945
|
-
await handler2(event);
|
|
145946
|
-
} else {
|
|
145947
|
-
log.info(
|
|
145948
|
-
`\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
|
|
145949
|
-
);
|
|
145950
|
-
}
|
|
146982
|
+
event = JSON.parse(trimmed);
|
|
145951
146983
|
} catch {
|
|
145952
146984
|
log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
|
|
146985
|
+
continue;
|
|
146986
|
+
}
|
|
146987
|
+
eventCount++;
|
|
146988
|
+
log.debug(JSON.stringify(event, null, 2));
|
|
146989
|
+
const timeSinceLastActivity = getIdleMs();
|
|
146990
|
+
if (timeSinceLastActivity > 1e4) {
|
|
146991
|
+
const activeToolCalls = toolCallTimings.size;
|
|
146992
|
+
const toolCallInfo = activeToolCalls > 0 ? ` (waiting for ${activeToolCalls} tool call${activeToolCalls > 1 ? "s" : ""})` : ` (${params.label} may be processing internally - LLM calls, planning, etc.)`;
|
|
146993
|
+
log.info(
|
|
146994
|
+
`\xBB no activity for ${(timeSinceLastActivity / 1e3).toFixed(1)}s${toolCallInfo} (${eventCount} events processed so far)`
|
|
146995
|
+
);
|
|
146996
|
+
}
|
|
146997
|
+
markActivity();
|
|
146998
|
+
const handler2 = handlers2[event.type];
|
|
146999
|
+
if (!handler2) {
|
|
147000
|
+
log.info(
|
|
147001
|
+
`\xBB ${params.label} event (unhandled): type=${event.type}, data=${JSON.stringify(event).substring(0, 500)}`
|
|
147002
|
+
);
|
|
147003
|
+
continue;
|
|
147004
|
+
}
|
|
147005
|
+
try {
|
|
147006
|
+
await handler2(event);
|
|
147007
|
+
} catch (err) {
|
|
147008
|
+
log.info(
|
|
147009
|
+
`\xBB ${params.label} handler for type=${event.type} threw: ${err instanceof Error ? err.message : String(err)}`
|
|
147010
|
+
);
|
|
145953
147011
|
}
|
|
145954
147012
|
}
|
|
145955
147013
|
},
|
|
@@ -145983,16 +147041,9 @@ async function runOpenCode(params) {
|
|
|
145983
147041
|
if (stderrContext) log.info(`\xBB last stderr output:
|
|
145984
147042
|
${stderrContext}`);
|
|
145985
147043
|
}
|
|
145986
|
-
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0)) {
|
|
145987
|
-
|
|
145988
|
-
|
|
145989
|
-
[
|
|
145990
|
-
{ data: "Input Tokens", header: true },
|
|
145991
|
-
{ data: "Output Tokens", header: true },
|
|
145992
|
-
{ data: "Total Tokens", header: true }
|
|
145993
|
-
],
|
|
145994
|
-
[String(accumulatedTokens.input), String(accumulatedTokens.output), String(totalTokens)]
|
|
145995
|
-
]);
|
|
147044
|
+
if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
|
|
147045
|
+
logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
|
|
147046
|
+
tokensLogged = true;
|
|
145996
147047
|
}
|
|
145997
147048
|
const usage = buildUsage();
|
|
145998
147049
|
if (result.exitCode !== 0) {
|
|
@@ -146018,7 +147069,7 @@ ${stderrContext}`);
|
|
|
146018
147069
|
params.todoTracker?.cancel();
|
|
146019
147070
|
const duration4 = performance7.now() - startTime;
|
|
146020
147071
|
const errorMessage = error49 instanceof Error ? error49.message : String(error49);
|
|
146021
|
-
const isActivityTimeout =
|
|
147072
|
+
const isActivityTimeout = error49 instanceof SpawnTimeoutError && error49.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
|
|
146022
147073
|
const stderrContext = recentStderr.slice(-10).join("\n");
|
|
146023
147074
|
const diagnosis = lastProviderError ? `likely cause: ${lastProviderError}` : eventCount === 0 ? "OpenCode produced 0 stdout events - check if the model provider is reachable" : `${eventCount} events were processed before the hang`;
|
|
146024
147075
|
log.info(
|
|
@@ -146038,8 +147089,8 @@ ${stderrContext}`
|
|
|
146038
147089
|
};
|
|
146039
147090
|
}
|
|
146040
147091
|
}
|
|
146041
|
-
var
|
|
146042
|
-
name: "
|
|
147092
|
+
var opencode = agent({
|
|
147093
|
+
name: "opencode",
|
|
146043
147094
|
install: installOpencodeCli,
|
|
146044
147095
|
run: async (ctx) => {
|
|
146045
147096
|
const cliPath = await installOpencodeCli();
|
|
@@ -146075,12 +147126,15 @@ var opentoad = agent({
|
|
|
146075
147126
|
cliPath,
|
|
146076
147127
|
cwd: repoDir,
|
|
146077
147128
|
env: env2,
|
|
146078
|
-
todoTracker: ctx.todoTracker
|
|
147129
|
+
todoTracker: ctx.todoTracker,
|
|
147130
|
+
onActivityTimeout: ctx.onActivityTimeout,
|
|
147131
|
+
onToolUse: ctx.onToolUse
|
|
146079
147132
|
};
|
|
146080
147133
|
let result = await runOpenCode({
|
|
146081
147134
|
...runParams,
|
|
146082
147135
|
args: [...baseArgs, ctx.instructions.full]
|
|
146083
147136
|
});
|
|
147137
|
+
let aggregatedUsage = result.usage;
|
|
146084
147138
|
for (let attempt = 0; attempt < MAX_COMMIT_RETRIES; attempt++) {
|
|
146085
147139
|
if (!result.success) break;
|
|
146086
147140
|
const status = getGitStatus();
|
|
@@ -146089,15 +147143,16 @@ var opentoad = agent({
|
|
|
146089
147143
|
${status}`);
|
|
146090
147144
|
result = await runOpenCode({
|
|
146091
147145
|
...runParams,
|
|
146092
|
-
args: [...baseArgs, "--continue", buildCommitPrompt("
|
|
147146
|
+
args: [...baseArgs, "--continue", buildCommitPrompt("opencode", status)]
|
|
146093
147147
|
});
|
|
147148
|
+
aggregatedUsage = mergeAgentUsage(aggregatedUsage, result.usage);
|
|
146094
147149
|
}
|
|
146095
|
-
return result;
|
|
147150
|
+
return { ...result, usage: aggregatedUsage };
|
|
146096
147151
|
}
|
|
146097
147152
|
});
|
|
146098
147153
|
|
|
146099
147154
|
// agents/index.ts
|
|
146100
|
-
var agents = { claude,
|
|
147155
|
+
var agents = { claude, opencode };
|
|
146101
147156
|
|
|
146102
147157
|
// utils/agent.ts
|
|
146103
147158
|
function hasEnvVar(name) {
|
|
@@ -146110,13 +147165,11 @@ function hasClaudeCodeAuth() {
|
|
|
146110
147165
|
function resolveModel(ctx) {
|
|
146111
147166
|
const envModel = process.env.PULLFROG_MODEL?.trim();
|
|
146112
147167
|
if (envModel) {
|
|
146113
|
-
|
|
146114
|
-
return envModel;
|
|
147168
|
+
return resolveCliModel(envModel) ?? envModel;
|
|
146115
147169
|
}
|
|
146116
147170
|
if (ctx.slug) {
|
|
146117
147171
|
const resolved = resolveCliModel(ctx.slug);
|
|
146118
147172
|
if (resolved) {
|
|
146119
|
-
log.info(`\xBB model: ${resolved} (resolved from ${ctx.slug})`);
|
|
146120
147173
|
return resolved;
|
|
146121
147174
|
}
|
|
146122
147175
|
log.warning(`\xBB unknown model slug "${ctx.slug}" \u2014 agent will auto-select`);
|
|
@@ -146127,7 +147180,6 @@ function resolveAgent(ctx) {
|
|
|
146127
147180
|
const envAgent = process.env.PULLFROG_AGENT?.trim();
|
|
146128
147181
|
if (envAgent) {
|
|
146129
147182
|
if (envAgent in agents) {
|
|
146130
|
-
log.info(`\xBB agent: ${envAgent} (override via PULLFROG_AGENT)`);
|
|
146131
147183
|
return agents[envAgent];
|
|
146132
147184
|
}
|
|
146133
147185
|
log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
|
|
@@ -146136,13 +147188,12 @@ function resolveAgent(ctx) {
|
|
|
146136
147188
|
try {
|
|
146137
147189
|
const provider2 = getModelProvider(ctx.model);
|
|
146138
147190
|
if (provider2 === "anthropic" && hasClaudeCodeAuth()) {
|
|
146139
|
-
log.info(`\xBB agent: claude (auto-selected for ${ctx.model})`);
|
|
146140
147191
|
return agents.claude;
|
|
146141
147192
|
}
|
|
146142
147193
|
} catch {
|
|
146143
147194
|
}
|
|
146144
147195
|
}
|
|
146145
|
-
return agents.
|
|
147196
|
+
return agents.opencode;
|
|
146146
147197
|
}
|
|
146147
147198
|
|
|
146148
147199
|
// utils/apiKeys.ts
|
|
@@ -150356,9 +151407,9 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
150356
151407
|
res.writeHead(409, { "Content-Type": "text/plain" });
|
|
150357
151408
|
res.end("compromised");
|
|
150358
151409
|
});
|
|
150359
|
-
await new Promise((
|
|
151410
|
+
await new Promise((resolve3, reject) => {
|
|
150360
151411
|
server.on("error", reject);
|
|
150361
|
-
server.listen(0, "127.0.0.1", () =>
|
|
151412
|
+
server.listen(0, "127.0.0.1", () => resolve3());
|
|
150362
151413
|
});
|
|
150363
151414
|
const rawAddr = server.address();
|
|
150364
151415
|
if (!rawAddr || typeof rawAddr === "string") {
|
|
@@ -150402,7 +151453,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
150402
151453
|
clearTimeout(entry.timeout);
|
|
150403
151454
|
}
|
|
150404
151455
|
codes.clear();
|
|
150405
|
-
await new Promise((
|
|
151456
|
+
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
150406
151457
|
log.debug("git auth server closed");
|
|
150407
151458
|
}
|
|
150408
151459
|
return {
|
|
@@ -150589,7 +151640,7 @@ MCP servers provide tools you can call. Inspect your available MCP servers at st
|
|
|
150589
151640
|
|
|
150590
151641
|
### Git
|
|
150591
151642
|
|
|
150592
|
-
Use \`${t("git")}\` for local git commands (status, log,
|
|
151643
|
+
Use \`${t("git")}\` for local git commands (status, log, add, commit, checkout, branch, merge, etc.). When reviewing a PR, do NOT re-derive the PR diff via \`git diff <base>..<head>\` \u2014 the diffPath returned by \`${t("checkout_pr")}\` is authoritative. \`git log\` and \`git diff --stat\` are fine for commit-range overview; \`git diff\` / \`git diff --cached\` are fine for inspecting your *own* uncommitted changes. For operations requiring remote authentication, use the dedicated MCP tools:
|
|
150593
151644
|
- \`${t("push_branch")}\` - push current or specified branch
|
|
150594
151645
|
- \`${t("git_fetch")}\` - fetch refs from remote
|
|
150595
151646
|
- \`${t("checkout_pr")}\` - checkout a PR branch (fetches and configures push for forks)
|
|
@@ -150630,6 +151681,8 @@ Never use \`sleep\` to wait for commands to complete. Commands run synchronously
|
|
|
150630
151681
|
|
|
150631
151682
|
When posting comments via ${pullfrogMcpName}, write as a professional team member would. Your final comments should be polished and actionable \u2014 do not include intermediate reasoning like "I'll now look at the code" or "Let me respond to the question."
|
|
150632
151683
|
|
|
151684
|
+
When embedding images (e.g. uploaded screenshots) in comments or PR bodies, always use markdown image syntax: \`\`. Never paste a naked URL \u2014 it will not render as an image.
|
|
151685
|
+
|
|
150633
151686
|
### Progress reporting
|
|
150634
151687
|
|
|
150635
151688
|
**Task list**: at the start of every run, create an internal task list based on the steps in your current mode. Update it as you complete each step. The system automatically renders this list to the progress comment \u2014 you do not need to call \`report_progress\` for this.
|
|
@@ -150765,7 +151818,7 @@ function normalizeEnv() {
|
|
|
150765
151818
|
|
|
150766
151819
|
// utils/payload.ts
|
|
150767
151820
|
var core4 = __toESM(require_core(), 1);
|
|
150768
|
-
import { isAbsolute, resolve } from "node:path";
|
|
151821
|
+
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
150769
151822
|
|
|
150770
151823
|
// utils/versioning.ts
|
|
150771
151824
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
@@ -150819,8 +151872,8 @@ function isPayloadEvent(value2) {
|
|
|
150819
151872
|
function resolveCwd(cwd) {
|
|
150820
151873
|
const workspace = process.env.GITHUB_WORKSPACE;
|
|
150821
151874
|
if (!cwd) return workspace;
|
|
150822
|
-
if (
|
|
150823
|
-
return workspace ?
|
|
151875
|
+
if (isAbsolute2(cwd)) return cwd;
|
|
151876
|
+
return workspace ? resolve2(workspace, cwd) : cwd;
|
|
150824
151877
|
}
|
|
150825
151878
|
function resolvePromptInput() {
|
|
150826
151879
|
const prompt = core4.getInput("prompt", { required: true });
|
|
@@ -150999,7 +152052,8 @@ var defaultSettings = {
|
|
|
150999
152052
|
shell: "restricted",
|
|
151000
152053
|
prApproveEnabled: false,
|
|
151001
152054
|
modeInstructions: {},
|
|
151002
|
-
learnings: null
|
|
152055
|
+
learnings: null,
|
|
152056
|
+
envAllowlist: null
|
|
151003
152057
|
};
|
|
151004
152058
|
var defaultRunContext = {
|
|
151005
152059
|
settings: defaultSettings,
|
|
@@ -151079,7 +152133,7 @@ async function resolveRunContextData(params) {
|
|
|
151079
152133
|
}
|
|
151080
152134
|
|
|
151081
152135
|
// utils/setup.ts
|
|
151082
|
-
import { execSync as execSync3 } from "node:child_process";
|
|
152136
|
+
import { execFileSync as execFileSync5, execSync as execSync3 } from "node:child_process";
|
|
151083
152137
|
import { mkdtempSync } from "node:fs";
|
|
151084
152138
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
151085
152139
|
import { join as join13 } from "node:path";
|
|
@@ -151089,6 +152143,51 @@ function createTempDirectory() {
|
|
|
151089
152143
|
log.info(`\xBB created temp dir at ${sharedTempDir}`);
|
|
151090
152144
|
return sharedTempDir;
|
|
151091
152145
|
}
|
|
152146
|
+
function envScopedToRepo() {
|
|
152147
|
+
const scoped = { ...process.env };
|
|
152148
|
+
for (const key of Object.keys(scoped)) {
|
|
152149
|
+
if (key.startsWith("GIT_")) delete scoped[key];
|
|
152150
|
+
}
|
|
152151
|
+
return scoped;
|
|
152152
|
+
}
|
|
152153
|
+
function removeIncludeIfEntries(repoDir) {
|
|
152154
|
+
const env2 = envScopedToRepo();
|
|
152155
|
+
let configOutput;
|
|
152156
|
+
try {
|
|
152157
|
+
configOutput = execSync3("git config --local --get-regexp -z ^includeif\\.", {
|
|
152158
|
+
cwd: repoDir,
|
|
152159
|
+
encoding: "utf-8",
|
|
152160
|
+
stdio: "pipe",
|
|
152161
|
+
env: env2
|
|
152162
|
+
});
|
|
152163
|
+
} catch {
|
|
152164
|
+
log.debug("\xBB no includeIf credential entries to remove");
|
|
152165
|
+
return;
|
|
152166
|
+
}
|
|
152167
|
+
const seen = /* @__PURE__ */ new Set();
|
|
152168
|
+
for (const entry of configOutput.split("\0")) {
|
|
152169
|
+
if (!entry) continue;
|
|
152170
|
+
const nl = entry.indexOf("\n");
|
|
152171
|
+
const key = nl === -1 ? entry : entry.slice(0, nl);
|
|
152172
|
+
if (!key || seen.has(key)) continue;
|
|
152173
|
+
seen.add(key);
|
|
152174
|
+
try {
|
|
152175
|
+
execFileSync5("git", ["config", "--local", "--unset-all", key], {
|
|
152176
|
+
cwd: repoDir,
|
|
152177
|
+
stdio: "pipe",
|
|
152178
|
+
env: env2
|
|
152179
|
+
});
|
|
152180
|
+
} catch (error49) {
|
|
152181
|
+
log.debug(
|
|
152182
|
+
`\xBB failed to unset ${key}: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
152183
|
+
);
|
|
152184
|
+
}
|
|
152185
|
+
}
|
|
152186
|
+
if (seen.size > 0)
|
|
152187
|
+
log.info(
|
|
152188
|
+
`\xBB removed ${seen.size} includeIf credential ${seen.size === 1 ? "entry" : "entries"}`
|
|
152189
|
+
);
|
|
152190
|
+
}
|
|
151092
152191
|
async function setupGit(params) {
|
|
151093
152192
|
const repoDir = process.cwd();
|
|
151094
152193
|
log.info("\xBB setting up git configuration...");
|
|
@@ -151135,24 +152234,7 @@ async function setupGit(params) {
|
|
|
151135
152234
|
} catch {
|
|
151136
152235
|
log.debug("\xBB no existing authentication headers to remove");
|
|
151137
152236
|
}
|
|
151138
|
-
|
|
151139
|
-
const configOutput = execSync3("git config --local --get-regexp ^includeif\\.", {
|
|
151140
|
-
cwd: repoDir,
|
|
151141
|
-
encoding: "utf-8",
|
|
151142
|
-
stdio: "pipe"
|
|
151143
|
-
});
|
|
151144
|
-
for (const line of configOutput.trim().split("\n")) {
|
|
151145
|
-
const key = line.split(" ")[0];
|
|
151146
|
-
if (!key) continue;
|
|
151147
|
-
execSync3(`git config --local --unset "${key}"`, {
|
|
151148
|
-
cwd: repoDir,
|
|
151149
|
-
stdio: "pipe"
|
|
151150
|
-
});
|
|
151151
|
-
}
|
|
151152
|
-
log.info("\xBB removed includeIf credential entries");
|
|
151153
|
-
} catch {
|
|
151154
|
-
log.debug("\xBB no includeIf credential entries to remove");
|
|
151155
|
-
}
|
|
152237
|
+
removeIncludeIfEntries(repoDir);
|
|
151156
152238
|
const originUrl = `https://github.com/${params.owner}/${params.name}.git`;
|
|
151157
152239
|
$("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
|
|
151158
152240
|
params.toolState.pushUrl = originUrl;
|
|
@@ -151171,6 +152253,13 @@ function parseTimeString(input) {
|
|
|
151171
152253
|
const seconds = parseInt(match3[3] || "0", 10);
|
|
151172
152254
|
return (hours * 3600 + minutes * 60 + seconds) * 1e3;
|
|
151173
152255
|
}
|
|
152256
|
+
var TIMEOUT_MAX_MS = 2147483647;
|
|
152257
|
+
function resolveTimeoutMs(input) {
|
|
152258
|
+
if (!input) return null;
|
|
152259
|
+
const parsed2 = parseTimeString(input);
|
|
152260
|
+
if (parsed2 === null || parsed2 <= 0 || parsed2 > TIMEOUT_MAX_MS) return null;
|
|
152261
|
+
return parsed2;
|
|
152262
|
+
}
|
|
151174
152263
|
|
|
151175
152264
|
// utils/todoTracking.ts
|
|
151176
152265
|
function isValidTodoStatus(value2) {
|
|
@@ -151273,9 +152362,12 @@ function createTodoTracker(onUpdate) {
|
|
|
151273
152362
|
if (item.status === "in_progress") item.status = "completed";
|
|
151274
152363
|
}
|
|
151275
152364
|
},
|
|
151276
|
-
renderCollapsible() {
|
|
152365
|
+
renderCollapsible(options) {
|
|
151277
152366
|
if (state.size === 0) return "";
|
|
151278
|
-
const
|
|
152367
|
+
const shouldCompleteInProgress = options?.completeInProgress === true;
|
|
152368
|
+
const todos = Array.from(state.values()).map(
|
|
152369
|
+
(item) => shouldCompleteInProgress && item.status === "in_progress" ? { ...item, status: "completed" } : item
|
|
152370
|
+
);
|
|
151279
152371
|
const completed = todos.filter((t) => t.status === "completed").length;
|
|
151280
152372
|
const markdown = renderTodoMarkdown(todos);
|
|
151281
152373
|
return `<details>
|
|
@@ -151335,6 +152427,32 @@ function resolveOutputSchema() {
|
|
|
151335
152427
|
log.info("\xBB structured output schema provided \u2014 output will be required");
|
|
151336
152428
|
return parsed2;
|
|
151337
152429
|
}
|
|
152430
|
+
function resolveTimeoutForLog(timeout) {
|
|
152431
|
+
if (!timeout) return "1h (default)";
|
|
152432
|
+
if (timeout === TIMEOUT_DISABLED) return "none (disabled)";
|
|
152433
|
+
return timeout;
|
|
152434
|
+
}
|
|
152435
|
+
function resolveModelForLog(ctx) {
|
|
152436
|
+
const envModel = process.env.PULLFROG_MODEL?.trim();
|
|
152437
|
+
if (envModel) return `${envModel} (override via PULLFROG_MODEL)`;
|
|
152438
|
+
if (ctx.payload.proxyModel) return `${ctx.payload.proxyModel} (proxy)`;
|
|
152439
|
+
if (ctx.resolvedModel && ctx.payload.model && ctx.payload.model !== ctx.resolvedModel) {
|
|
152440
|
+
return `${ctx.resolvedModel} (resolved from ${ctx.payload.model})`;
|
|
152441
|
+
}
|
|
152442
|
+
if (ctx.resolvedModel) return ctx.resolvedModel;
|
|
152443
|
+
if (ctx.payload.model) return `${ctx.payload.model} (unresolved)`;
|
|
152444
|
+
return "auto";
|
|
152445
|
+
}
|
|
152446
|
+
function resolveAgentForLog(ctx) {
|
|
152447
|
+
const envAgent = process.env.PULLFROG_AGENT?.trim();
|
|
152448
|
+
if (envAgent && envAgent === ctx.agentName) {
|
|
152449
|
+
return `${ctx.agentName} (override via PULLFROG_AGENT)`;
|
|
152450
|
+
}
|
|
152451
|
+
if (ctx.agentName === "claude" && ctx.resolvedModel) {
|
|
152452
|
+
return `${ctx.agentName} (auto-selected for ${ctx.resolvedModel})`;
|
|
152453
|
+
}
|
|
152454
|
+
return ctx.agentName;
|
|
152455
|
+
}
|
|
151338
152456
|
async function mintProxyKey(ctx) {
|
|
151339
152457
|
try {
|
|
151340
152458
|
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = ctx.oidcCredentials.requestUrl;
|
|
@@ -151394,6 +152512,7 @@ async function main() {
|
|
|
151394
152512
|
}
|
|
151395
152513
|
const timer = new Timer();
|
|
151396
152514
|
let activityTimeout = null;
|
|
152515
|
+
let safetyNetTimer;
|
|
151397
152516
|
const resolvedPromptInput = resolvePromptInput();
|
|
151398
152517
|
const toolState = initToolState({
|
|
151399
152518
|
progressCommentId: typeof resolvedPromptInput !== "string" ? resolvedPromptInput.progressCommentId : void 0
|
|
@@ -151413,6 +152532,9 @@ async function main() {
|
|
|
151413
152532
|
const count = Object.keys(runContext.dbSecrets).length;
|
|
151414
152533
|
if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
|
|
151415
152534
|
}
|
|
152535
|
+
if (runContext.repoSettings.envAllowlist) {
|
|
152536
|
+
setEnvAllowlist(runContext.repoSettings.envAllowlist);
|
|
152537
|
+
}
|
|
151416
152538
|
const payload = resolvePayload(resolvedPromptInput, runContext.repoSettings);
|
|
151417
152539
|
toolState.model = payload.model;
|
|
151418
152540
|
if (payload.event.trigger === "pull_request_synchronize") {
|
|
@@ -151477,10 +152599,13 @@ async function main() {
|
|
|
151477
152599
|
postCheckoutScript: runContext.repoSettings.postCheckoutScript
|
|
151478
152600
|
});
|
|
151479
152601
|
timer.checkpoint("git");
|
|
151480
|
-
await executeLifecycleHook({
|
|
152602
|
+
const setupHook = await executeLifecycleHook({
|
|
151481
152603
|
event: "setup",
|
|
151482
152604
|
script: runContext.repoSettings.setupScript
|
|
151483
152605
|
});
|
|
152606
|
+
if (setupHook.warning) {
|
|
152607
|
+
throw new Error(setupHook.warning);
|
|
152608
|
+
}
|
|
151484
152609
|
timer.checkpoint("lifecycleHooks::setup");
|
|
151485
152610
|
const agentId = agent2.name;
|
|
151486
152611
|
const modes2 = [...computeModes(agentId), ...runContext.repoSettings.modes];
|
|
@@ -151502,17 +152627,22 @@ async function main() {
|
|
|
151502
152627
|
runId: runInfo.runId,
|
|
151503
152628
|
jobId: runInfo.jobId,
|
|
151504
152629
|
mcpServerUrl: "",
|
|
151505
|
-
tmpdir: tmpdir3
|
|
152630
|
+
tmpdir: tmpdir3,
|
|
152631
|
+
resolvedModel
|
|
151506
152632
|
};
|
|
151507
152633
|
const mcpHttpServer = __using(_stack, await startMcpHttpServer(toolContext, { outputSchema }), true);
|
|
151508
152634
|
toolContext.mcpServerUrl = mcpHttpServer.url;
|
|
151509
152635
|
log.info(`\xBB MCP server started at ${mcpHttpServer.url}`);
|
|
151510
152636
|
timer.checkpoint("mcpServer");
|
|
151511
152637
|
startInstallation(toolContext);
|
|
151512
|
-
|
|
151513
|
-
|
|
152638
|
+
const modelForLog = resolveModelForLog({ payload, resolvedModel });
|
|
152639
|
+
const agentForLog = resolveAgentForLog({ agentName: agent2.name, resolvedModel });
|
|
152640
|
+
const timeoutForLog = resolveTimeoutForLog(payload.timeout);
|
|
152641
|
+
log.info(`\xBB model: ${modelForLog}`);
|
|
152642
|
+
log.info(`\xBB agent: ${agentForLog}`);
|
|
151514
152643
|
log.info(`\xBB push: ${payload.push}`);
|
|
151515
152644
|
log.info(`\xBB shell: ${payload.shell}`);
|
|
152645
|
+
log.info(`\xBB timeout: ${timeoutForLog}`);
|
|
151516
152646
|
const instructions = resolveInstructions({
|
|
151517
152647
|
payload,
|
|
151518
152648
|
repo: runContext.repo,
|
|
@@ -151534,6 +152664,18 @@ ${instructions.user}` : null,
|
|
|
151534
152664
|
log.group("View full prompt", () => {
|
|
151535
152665
|
log.info(instructions.full);
|
|
151536
152666
|
});
|
|
152667
|
+
if (agentId === "opencode") {
|
|
152668
|
+
const pluginDir = join14(process.cwd(), ".opencode", "plugin");
|
|
152669
|
+
const hasPlugins = existsSync6(pluginDir) && readdirSync(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
|
|
152670
|
+
if (hasPlugins && toolState.dependencyInstallation?.promise) {
|
|
152671
|
+
log.info(
|
|
152672
|
+
"\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
|
|
152673
|
+
);
|
|
152674
|
+
await toolState.dependencyInstallation.promise.catch(() => {
|
|
152675
|
+
});
|
|
152676
|
+
timer.checkpoint("awaitDepsForPlugins");
|
|
152677
|
+
}
|
|
152678
|
+
}
|
|
151537
152679
|
activityTimeout = createProcessOutputActivityTimeout({
|
|
151538
152680
|
timeoutMs: DEFAULT_ACTIVITY_TIMEOUT_MS,
|
|
151539
152681
|
checkIntervalMs: DEFAULT_ACTIVITY_CHECK_INTERVAL_MS
|
|
@@ -151549,24 +152691,62 @@ ${instructions.user}` : null,
|
|
|
151549
152691
|
}
|
|
151550
152692
|
});
|
|
151551
152693
|
toolState.todoTracker = todoTracker;
|
|
152694
|
+
let innerTimeoutFired = false;
|
|
152695
|
+
const onInnerActivityTimeout = () => {
|
|
152696
|
+
if (innerTimeoutFired) return;
|
|
152697
|
+
innerTimeoutFired = true;
|
|
152698
|
+
log.info(
|
|
152699
|
+
"\xBB inner activity timeout fired \u2014 stopping MCP server and starting 5min safety-net timer"
|
|
152700
|
+
);
|
|
152701
|
+
mcpHttpServer[Symbol.asyncDispose]().catch((err) => {
|
|
152702
|
+
log.debug(
|
|
152703
|
+
`mcp server stop after inner kill failed: ${err instanceof Error ? err.message : String(err)}`
|
|
152704
|
+
);
|
|
152705
|
+
});
|
|
152706
|
+
safetyNetTimer = setTimeout(
|
|
152707
|
+
() => {
|
|
152708
|
+
activityTimeout?.forceReject(
|
|
152709
|
+
"agent still pending 5min after inner activity kill \u2014 forcing exit"
|
|
152710
|
+
);
|
|
152711
|
+
},
|
|
152712
|
+
5 * 60 * 1e3
|
|
152713
|
+
);
|
|
152714
|
+
safetyNetTimer.unref?.();
|
|
152715
|
+
};
|
|
151552
152716
|
const agentPromise = agent2.run({
|
|
151553
152717
|
payload,
|
|
151554
152718
|
resolvedModel,
|
|
151555
152719
|
mcpServerUrl: mcpHttpServer.url,
|
|
151556
152720
|
tmpdir: tmpdir3,
|
|
151557
152721
|
instructions,
|
|
151558
|
-
todoTracker
|
|
152722
|
+
todoTracker,
|
|
152723
|
+
onActivityTimeout: onInnerActivityTimeout,
|
|
152724
|
+
onToolUse: (event) => {
|
|
152725
|
+
const wasTracked = recordDiffReadFromToolUse({
|
|
152726
|
+
state: toolState.diffCoverage,
|
|
152727
|
+
toolName: event.toolName,
|
|
152728
|
+
input: event.input,
|
|
152729
|
+
cwd: process.cwd()
|
|
152730
|
+
});
|
|
152731
|
+
if (!wasTracked) return;
|
|
152732
|
+
const trackedRanges = toolState.diffCoverage?.coveredRanges ?? [];
|
|
152733
|
+
log.debug(
|
|
152734
|
+
`\xBB diff coverage tracked from tool ${event.toolName} (${trackedRanges.length} merged range${trackedRanges.length === 1 ? "" : "s"})`
|
|
152735
|
+
);
|
|
152736
|
+
}
|
|
152737
|
+
});
|
|
152738
|
+
agentPromise.catch(() => {
|
|
151559
152739
|
});
|
|
151560
152740
|
let result;
|
|
151561
152741
|
if (payload.timeout === TIMEOUT_DISABLED) {
|
|
151562
152742
|
result = await Promise.race([agentPromise, activityTimeout.promise]);
|
|
151563
152743
|
} else {
|
|
151564
|
-
const
|
|
151565
|
-
if (payload.timeout &&
|
|
151566
|
-
log.warning(`invalid timeout
|
|
152744
|
+
const usable = resolveTimeoutMs(payload.timeout);
|
|
152745
|
+
if (payload.timeout && usable === null) {
|
|
152746
|
+
log.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
|
|
151567
152747
|
}
|
|
151568
|
-
const timeoutMs =
|
|
151569
|
-
const actualTimeout =
|
|
152748
|
+
const timeoutMs = usable ?? 36e5;
|
|
152749
|
+
const actualTimeout = usable !== null ? payload.timeout : "1h";
|
|
151570
152750
|
let timeoutId;
|
|
151571
152751
|
const timeoutPromise = new Promise((_3, reject) => {
|
|
151572
152752
|
timeoutId = setTimeout(() => {
|
|
@@ -151628,7 +152808,14 @@ ${instructions.user}` : null,
|
|
|
151628
152808
|
killTrackedChildren();
|
|
151629
152809
|
log.error(errorMessage);
|
|
151630
152810
|
try {
|
|
151631
|
-
|
|
152811
|
+
const errorSummary = `### \u274C Pullfrog failed
|
|
152812
|
+
|
|
152813
|
+
\`\`\`
|
|
152814
|
+
${errorMessage}
|
|
152815
|
+
\`\`\``;
|
|
152816
|
+
const usageSummary = formatUsageSummary(toolState.usageEntries);
|
|
152817
|
+
const parts = [errorSummary, toolState.lastProgressBody, usageSummary].filter(Boolean);
|
|
152818
|
+
await writeSummary(parts.join("\n\n"));
|
|
151632
152819
|
} catch {
|
|
151633
152820
|
}
|
|
151634
152821
|
try {
|
|
@@ -151646,8 +152833,21 @@ ${instructions.user}` : null,
|
|
|
151646
152833
|
};
|
|
151647
152834
|
} finally {
|
|
151648
152835
|
activityTimeout?.stop();
|
|
152836
|
+
if (safetyNetTimer) clearTimeout(safetyNetTimer);
|
|
151649
152837
|
if (usageSummaryPath) {
|
|
151650
|
-
|
|
152838
|
+
try {
|
|
152839
|
+
await writeGitHubUsageSummaryToFile(usageSummaryPath);
|
|
152840
|
+
} catch (err) {
|
|
152841
|
+
log.debug(
|
|
152842
|
+
`failed to write usage summary to ${usageSummaryPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
152843
|
+
);
|
|
152844
|
+
}
|
|
152845
|
+
}
|
|
152846
|
+
if (toolContext) {
|
|
152847
|
+
const patch = aggregateUsage(toolState.usageEntries);
|
|
152848
|
+
if (Object.keys(patch).length > 0) {
|
|
152849
|
+
await patchWorkflowRunFields(toolContext, patch);
|
|
152850
|
+
}
|
|
151651
152851
|
}
|
|
151652
152852
|
}
|
|
151653
152853
|
} catch (_2) {
|