oh-my-opencode 3.12.1 → 3.12.3
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/cli/index.js +69 -26
- package/dist/config/schema/background-task.d.ts +1 -2
- package/dist/config/schema/oh-my-opencode-config.d.ts +1 -2
- package/dist/features/background-agent/constants.d.ts +1 -2
- package/dist/features/background-agent/loop-detector.d.ts +1 -4
- package/dist/features/background-agent/manager.d.ts +1 -0
- package/dist/features/background-agent/session-status-classifier.d.ts +2 -0
- package/dist/features/background-agent/types.d.ts +4 -4
- package/dist/index.js +243 -202
- package/dist/oh-my-opencode.schema.json +1 -6
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -55,20 +55,43 @@ __export(exports_logger, {
|
|
|
55
55
|
import * as fs from "fs";
|
|
56
56
|
import * as os from "os";
|
|
57
57
|
import * as path from "path";
|
|
58
|
+
function flush() {
|
|
59
|
+
if (buffer.length === 0)
|
|
60
|
+
return;
|
|
61
|
+
const data = buffer.join("");
|
|
62
|
+
buffer = [];
|
|
63
|
+
try {
|
|
64
|
+
fs.appendFileSync(logFile, data);
|
|
65
|
+
} catch {}
|
|
66
|
+
}
|
|
67
|
+
function scheduleFlush() {
|
|
68
|
+
if (flushTimer)
|
|
69
|
+
return;
|
|
70
|
+
flushTimer = setTimeout(() => {
|
|
71
|
+
flushTimer = null;
|
|
72
|
+
flush();
|
|
73
|
+
}, FLUSH_INTERVAL_MS);
|
|
74
|
+
}
|
|
58
75
|
function log(message, data) {
|
|
59
76
|
try {
|
|
60
77
|
const timestamp2 = new Date().toISOString();
|
|
61
78
|
const logEntry = `[${timestamp2}] ${message} ${data ? JSON.stringify(data) : ""}
|
|
62
79
|
`;
|
|
63
|
-
|
|
80
|
+
buffer.push(logEntry);
|
|
81
|
+
if (buffer.length >= BUFFER_SIZE_LIMIT) {
|
|
82
|
+
flush();
|
|
83
|
+
} else {
|
|
84
|
+
scheduleFlush();
|
|
85
|
+
}
|
|
64
86
|
} catch {}
|
|
65
87
|
}
|
|
66
88
|
function getLogFilePath() {
|
|
67
89
|
return logFile;
|
|
68
90
|
}
|
|
69
|
-
var logFile;
|
|
91
|
+
var logFile, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50;
|
|
70
92
|
var init_logger = __esm(() => {
|
|
71
93
|
logFile = path.join(os.tmpdir(), "oh-my-opencode.log");
|
|
94
|
+
buffer = [];
|
|
72
95
|
});
|
|
73
96
|
|
|
74
97
|
// src/shared/truncate-description.ts
|
|
@@ -2454,18 +2477,18 @@ var require_sharedArrayCancellation = __commonJS((exports) => {
|
|
|
2454
2477
|
if (request.id === null) {
|
|
2455
2478
|
return;
|
|
2456
2479
|
}
|
|
2457
|
-
const
|
|
2458
|
-
const data = new Int32Array(
|
|
2480
|
+
const buffer2 = new SharedArrayBuffer(4);
|
|
2481
|
+
const data = new Int32Array(buffer2, 0, 1);
|
|
2459
2482
|
data[0] = CancellationState.Continue;
|
|
2460
|
-
this.buffers.set(request.id,
|
|
2461
|
-
request.$cancellationData =
|
|
2483
|
+
this.buffers.set(request.id, buffer2);
|
|
2484
|
+
request.$cancellationData = buffer2;
|
|
2462
2485
|
}
|
|
2463
2486
|
async sendCancellation(_conn, id) {
|
|
2464
|
-
const
|
|
2465
|
-
if (
|
|
2487
|
+
const buffer2 = this.buffers.get(id);
|
|
2488
|
+
if (buffer2 === undefined) {
|
|
2466
2489
|
return;
|
|
2467
2490
|
}
|
|
2468
|
-
const data = new Int32Array(
|
|
2491
|
+
const data = new Int32Array(buffer2, 0, 1);
|
|
2469
2492
|
Atomics.store(data, 0, CancellationState.Cancelled);
|
|
2470
2493
|
}
|
|
2471
2494
|
cleanup(id) {
|
|
@@ -2478,8 +2501,8 @@ var require_sharedArrayCancellation = __commonJS((exports) => {
|
|
|
2478
2501
|
exports.SharedArraySenderStrategy = SharedArraySenderStrategy;
|
|
2479
2502
|
|
|
2480
2503
|
class SharedArrayBufferCancellationToken {
|
|
2481
|
-
constructor(
|
|
2482
|
-
this.data = new Int32Array(
|
|
2504
|
+
constructor(buffer2) {
|
|
2505
|
+
this.data = new Int32Array(buffer2, 0, 1);
|
|
2483
2506
|
}
|
|
2484
2507
|
get isCancellationRequested() {
|
|
2485
2508
|
return Atomics.load(this.data, 0) === CancellationState.Cancelled;
|
|
@@ -2490,8 +2513,8 @@ var require_sharedArrayCancellation = __commonJS((exports) => {
|
|
|
2490
2513
|
}
|
|
2491
2514
|
|
|
2492
2515
|
class SharedArrayBufferCancellationTokenSource {
|
|
2493
|
-
constructor(
|
|
2494
|
-
this.token = new SharedArrayBufferCancellationToken(
|
|
2516
|
+
constructor(buffer2) {
|
|
2517
|
+
this.token = new SharedArrayBufferCancellationToken(buffer2);
|
|
2495
2518
|
}
|
|
2496
2519
|
cancel() {}
|
|
2497
2520
|
dispose() {}
|
|
@@ -2502,11 +2525,11 @@ var require_sharedArrayCancellation = __commonJS((exports) => {
|
|
|
2502
2525
|
this.kind = "request";
|
|
2503
2526
|
}
|
|
2504
2527
|
createCancellationTokenSource(request) {
|
|
2505
|
-
const
|
|
2506
|
-
if (
|
|
2528
|
+
const buffer2 = request.$cancellationData;
|
|
2529
|
+
if (buffer2 === undefined) {
|
|
2507
2530
|
return new cancellation_1.CancellationTokenSource;
|
|
2508
2531
|
}
|
|
2509
|
-
return new SharedArrayBufferCancellationTokenSource(
|
|
2532
|
+
return new SharedArrayBufferCancellationTokenSource(buffer2);
|
|
2510
2533
|
}
|
|
2511
2534
|
}
|
|
2512
2535
|
exports.SharedArrayReceiverStrategy = SharedArrayReceiverStrategy;
|
|
@@ -2840,18 +2863,18 @@ var require_messageWriter = __commonJS((exports) => {
|
|
|
2840
2863
|
}
|
|
2841
2864
|
async write(msg) {
|
|
2842
2865
|
return this.writeSemaphore.lock(async () => {
|
|
2843
|
-
const payload = this.options.contentTypeEncoder.encode(msg, this.options).then((
|
|
2866
|
+
const payload = this.options.contentTypeEncoder.encode(msg, this.options).then((buffer2) => {
|
|
2844
2867
|
if (this.options.contentEncoder !== undefined) {
|
|
2845
|
-
return this.options.contentEncoder.encode(
|
|
2868
|
+
return this.options.contentEncoder.encode(buffer2);
|
|
2846
2869
|
} else {
|
|
2847
|
-
return
|
|
2870
|
+
return buffer2;
|
|
2848
2871
|
}
|
|
2849
2872
|
});
|
|
2850
|
-
return payload.then((
|
|
2873
|
+
return payload.then((buffer2) => {
|
|
2851
2874
|
const headers = [];
|
|
2852
|
-
headers.push(ContentLength,
|
|
2875
|
+
headers.push(ContentLength, buffer2.byteLength.toString(), CRLF);
|
|
2853
2876
|
headers.push(CRLF);
|
|
2854
|
-
return this.doWrite(msg, headers,
|
|
2877
|
+
return this.doWrite(msg, headers, buffer2);
|
|
2855
2878
|
}, (error48) => {
|
|
2856
2879
|
this.fireError(error48);
|
|
2857
2880
|
throw error48;
|
|
@@ -2953,9 +2976,9 @@ var require_messageBuffer = __commonJS((exports) => {
|
|
|
2953
2976
|
if (state3 !== 4) {
|
|
2954
2977
|
return;
|
|
2955
2978
|
}
|
|
2956
|
-
const
|
|
2979
|
+
const buffer2 = this._read(chunkBytesRead + offset);
|
|
2957
2980
|
const result = new Map;
|
|
2958
|
-
const headers = this.toString(
|
|
2981
|
+
const headers = this.toString(buffer2, "ascii").split(CRLF);
|
|
2959
2982
|
if (headers.length < 2) {
|
|
2960
2983
|
return result;
|
|
2961
2984
|
}
|
|
@@ -4379,11 +4402,11 @@ var require_ril = __commonJS((exports) => {
|
|
|
4379
4402
|
return new util_1.TextDecoder(encoding).decode(value);
|
|
4380
4403
|
}
|
|
4381
4404
|
}
|
|
4382
|
-
asNative(
|
|
4405
|
+
asNative(buffer2, length) {
|
|
4383
4406
|
if (length === undefined) {
|
|
4384
|
-
return
|
|
4407
|
+
return buffer2 instanceof Buffer ? buffer2 : Buffer.from(buffer2);
|
|
4385
4408
|
} else {
|
|
4386
|
-
return
|
|
4409
|
+
return buffer2 instanceof Buffer ? buffer2.slice(0, length) : Buffer.from(buffer2, 0, length);
|
|
4387
4410
|
}
|
|
4388
4411
|
}
|
|
4389
4412
|
allocNative(length) {
|
|
@@ -4467,12 +4490,12 @@ var require_ril = __commonJS((exports) => {
|
|
|
4467
4490
|
}),
|
|
4468
4491
|
decoder: Object.freeze({
|
|
4469
4492
|
name: "application/json",
|
|
4470
|
-
decode: (
|
|
4493
|
+
decode: (buffer2, options) => {
|
|
4471
4494
|
try {
|
|
4472
|
-
if (
|
|
4473
|
-
return Promise.resolve(JSON.parse(
|
|
4495
|
+
if (buffer2 instanceof Buffer) {
|
|
4496
|
+
return Promise.resolve(JSON.parse(buffer2.toString(options.charset)));
|
|
4474
4497
|
} else {
|
|
4475
|
-
return Promise.resolve(JSON.parse(new util_1.TextDecoder(options.charset).decode(
|
|
4498
|
+
return Promise.resolve(JSON.parse(new util_1.TextDecoder(options.charset).decode(buffer2)));
|
|
4476
4499
|
}
|
|
4477
4500
|
} catch (err) {
|
|
4478
4501
|
return Promise.reject(err);
|
|
@@ -8405,20 +8428,20 @@ var require_utils2 = __commonJS((exports, module) => {
|
|
|
8405
8428
|
return acc;
|
|
8406
8429
|
}
|
|
8407
8430
|
var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
8408
|
-
function consumeIsZone(
|
|
8409
|
-
|
|
8431
|
+
function consumeIsZone(buffer2) {
|
|
8432
|
+
buffer2.length = 0;
|
|
8410
8433
|
return true;
|
|
8411
8434
|
}
|
|
8412
|
-
function consumeHextets(
|
|
8413
|
-
if (
|
|
8414
|
-
const hex5 = stringArrayToHexStripped(
|
|
8435
|
+
function consumeHextets(buffer2, address, output) {
|
|
8436
|
+
if (buffer2.length) {
|
|
8437
|
+
const hex5 = stringArrayToHexStripped(buffer2);
|
|
8415
8438
|
if (hex5 !== "") {
|
|
8416
8439
|
address.push(hex5);
|
|
8417
8440
|
} else {
|
|
8418
8441
|
output.error = true;
|
|
8419
8442
|
return false;
|
|
8420
8443
|
}
|
|
8421
|
-
|
|
8444
|
+
buffer2.length = 0;
|
|
8422
8445
|
}
|
|
8423
8446
|
return true;
|
|
8424
8447
|
}
|
|
@@ -8426,7 +8449,7 @@ var require_utils2 = __commonJS((exports, module) => {
|
|
|
8426
8449
|
let tokenCount = 0;
|
|
8427
8450
|
const output = { error: false, address: "", zone: "" };
|
|
8428
8451
|
const address = [];
|
|
8429
|
-
const
|
|
8452
|
+
const buffer2 = [];
|
|
8430
8453
|
let endipv6Encountered = false;
|
|
8431
8454
|
let endIpv6 = false;
|
|
8432
8455
|
let consume = consumeHextets;
|
|
@@ -8439,7 +8462,7 @@ var require_utils2 = __commonJS((exports, module) => {
|
|
|
8439
8462
|
if (endipv6Encountered === true) {
|
|
8440
8463
|
endIpv6 = true;
|
|
8441
8464
|
}
|
|
8442
|
-
if (!consume(
|
|
8465
|
+
if (!consume(buffer2, address, output)) {
|
|
8443
8466
|
break;
|
|
8444
8467
|
}
|
|
8445
8468
|
if (++tokenCount > 7) {
|
|
@@ -8452,22 +8475,22 @@ var require_utils2 = __commonJS((exports, module) => {
|
|
|
8452
8475
|
address.push(":");
|
|
8453
8476
|
continue;
|
|
8454
8477
|
} else if (cursor === "%") {
|
|
8455
|
-
if (!consume(
|
|
8478
|
+
if (!consume(buffer2, address, output)) {
|
|
8456
8479
|
break;
|
|
8457
8480
|
}
|
|
8458
8481
|
consume = consumeIsZone;
|
|
8459
8482
|
} else {
|
|
8460
|
-
|
|
8483
|
+
buffer2.push(cursor);
|
|
8461
8484
|
continue;
|
|
8462
8485
|
}
|
|
8463
8486
|
}
|
|
8464
|
-
if (
|
|
8487
|
+
if (buffer2.length) {
|
|
8465
8488
|
if (consume === consumeIsZone) {
|
|
8466
|
-
output.zone =
|
|
8489
|
+
output.zone = buffer2.join("");
|
|
8467
8490
|
} else if (endIpv6) {
|
|
8468
|
-
address.push(
|
|
8491
|
+
address.push(buffer2.join(""));
|
|
8469
8492
|
} else {
|
|
8470
|
-
address.push(stringArrayToHexStripped(
|
|
8493
|
+
address.push(stringArrayToHexStripped(buffer2));
|
|
8471
8494
|
}
|
|
8472
8495
|
}
|
|
8473
8496
|
output.address = address.join("");
|
|
@@ -12160,14 +12183,14 @@ var require_readShebang = __commonJS((exports, module) => {
|
|
|
12160
12183
|
var shebangCommand = require_shebang_command();
|
|
12161
12184
|
function readShebang(command) {
|
|
12162
12185
|
const size = 150;
|
|
12163
|
-
const
|
|
12186
|
+
const buffer2 = Buffer.alloc(size);
|
|
12164
12187
|
let fd;
|
|
12165
12188
|
try {
|
|
12166
12189
|
fd = fs19.openSync(command, "r");
|
|
12167
|
-
fs19.readSync(fd,
|
|
12190
|
+
fs19.readSync(fd, buffer2, 0, size, 0);
|
|
12168
12191
|
fs19.closeSync(fd);
|
|
12169
12192
|
} catch (e) {}
|
|
12170
|
-
return shebangCommand(
|
|
12193
|
+
return shebangCommand(buffer2.toString());
|
|
12171
12194
|
}
|
|
12172
12195
|
module.exports = readShebang;
|
|
12173
12196
|
});
|
|
@@ -15246,7 +15269,7 @@ async function resolveFileReferencesInText(text, cwd = process.cwd(), depth = 0,
|
|
|
15246
15269
|
}
|
|
15247
15270
|
let resolved = text;
|
|
15248
15271
|
for (const [pattern, replacement] of replacements.entries()) {
|
|
15249
|
-
resolved = resolved.
|
|
15272
|
+
resolved = resolved.replaceAll(pattern, replacement);
|
|
15250
15273
|
}
|
|
15251
15274
|
if (findFileReferences(resolved).length > 0 && depth + 1 < maxDepth) {
|
|
15252
15275
|
return resolveFileReferencesInText(resolved, cwd, depth + 1, maxDepth);
|
|
@@ -15345,6 +15368,7 @@ function transformToolName(toolName) {
|
|
|
15345
15368
|
function escapeRegexExceptAsterisk(str2) {
|
|
15346
15369
|
return str2.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
15347
15370
|
}
|
|
15371
|
+
var regexCache = new Map;
|
|
15348
15372
|
function matchesToolMatcher(toolName, matcher) {
|
|
15349
15373
|
if (!matcher) {
|
|
15350
15374
|
return true;
|
|
@@ -15352,8 +15376,12 @@ function matchesToolMatcher(toolName, matcher) {
|
|
|
15352
15376
|
const patterns = matcher.split("|").map((p) => p.trim());
|
|
15353
15377
|
return patterns.some((p) => {
|
|
15354
15378
|
if (p.includes("*")) {
|
|
15355
|
-
|
|
15356
|
-
|
|
15379
|
+
let regex = regexCache.get(p);
|
|
15380
|
+
if (!regex) {
|
|
15381
|
+
const escaped = escapeRegexExceptAsterisk(p);
|
|
15382
|
+
regex = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "i");
|
|
15383
|
+
regexCache.set(p, regex);
|
|
15384
|
+
}
|
|
15357
15385
|
return regex.test(toolName);
|
|
15358
15386
|
}
|
|
15359
15387
|
return p.toLowerCase() === toolName.toLowerCase();
|
|
@@ -17588,6 +17616,8 @@ function createConnectedProvidersCacheStore(getCacheDir2 = getOmoOpenCodeCacheDi
|
|
|
17588
17616
|
function getCacheFilePath(filename) {
|
|
17589
17617
|
return join9(getCacheDir2(), filename);
|
|
17590
17618
|
}
|
|
17619
|
+
let memConnected;
|
|
17620
|
+
let memProviderModels;
|
|
17591
17621
|
function ensureCacheDir2() {
|
|
17592
17622
|
const cacheDir = getCacheDir2();
|
|
17593
17623
|
if (!existsSync8(cacheDir)) {
|
|
@@ -17595,18 +17625,23 @@ function createConnectedProvidersCacheStore(getCacheDir2 = getOmoOpenCodeCacheDi
|
|
|
17595
17625
|
}
|
|
17596
17626
|
}
|
|
17597
17627
|
function readConnectedProvidersCache() {
|
|
17628
|
+
if (memConnected !== undefined)
|
|
17629
|
+
return memConnected;
|
|
17598
17630
|
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
|
|
17599
17631
|
if (!existsSync8(cacheFile)) {
|
|
17600
17632
|
log("[connected-providers-cache] Cache file not found", { cacheFile });
|
|
17633
|
+
memConnected = null;
|
|
17601
17634
|
return null;
|
|
17602
17635
|
}
|
|
17603
17636
|
try {
|
|
17604
17637
|
const content = readFileSync4(cacheFile, "utf-8");
|
|
17605
17638
|
const data = JSON.parse(content);
|
|
17606
17639
|
log("[connected-providers-cache] Read cache", { count: data.connected.length, updatedAt: data.updatedAt });
|
|
17640
|
+
memConnected = data.connected;
|
|
17607
17641
|
return data.connected;
|
|
17608
17642
|
} catch (err) {
|
|
17609
17643
|
log("[connected-providers-cache] Error reading cache", { error: String(err) });
|
|
17644
|
+
memConnected = null;
|
|
17610
17645
|
return null;
|
|
17611
17646
|
}
|
|
17612
17647
|
}
|
|
@@ -17623,15 +17658,19 @@ function createConnectedProvidersCacheStore(getCacheDir2 = getOmoOpenCodeCacheDi
|
|
|
17623
17658
|
};
|
|
17624
17659
|
try {
|
|
17625
17660
|
writeFileSync2(cacheFile, JSON.stringify(data, null, 2));
|
|
17661
|
+
memConnected = connected;
|
|
17626
17662
|
log("[connected-providers-cache] Cache written", { count: connected.length });
|
|
17627
17663
|
} catch (err) {
|
|
17628
17664
|
log("[connected-providers-cache] Error writing cache", { error: String(err) });
|
|
17629
17665
|
}
|
|
17630
17666
|
}
|
|
17631
17667
|
function readProviderModelsCache() {
|
|
17668
|
+
if (memProviderModels !== undefined)
|
|
17669
|
+
return memProviderModels;
|
|
17632
17670
|
const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE);
|
|
17633
17671
|
if (!existsSync8(cacheFile)) {
|
|
17634
17672
|
log("[connected-providers-cache] Provider-models cache file not found", { cacheFile });
|
|
17673
|
+
memProviderModels = null;
|
|
17635
17674
|
return null;
|
|
17636
17675
|
}
|
|
17637
17676
|
try {
|
|
@@ -17641,9 +17680,11 @@ function createConnectedProvidersCacheStore(getCacheDir2 = getOmoOpenCodeCacheDi
|
|
|
17641
17680
|
providerCount: Object.keys(data.models).length,
|
|
17642
17681
|
updatedAt: data.updatedAt
|
|
17643
17682
|
});
|
|
17683
|
+
memProviderModels = data;
|
|
17644
17684
|
return data;
|
|
17645
17685
|
} catch (err) {
|
|
17646
17686
|
log("[connected-providers-cache] Error reading provider-models cache", { error: String(err) });
|
|
17687
|
+
memProviderModels = null;
|
|
17647
17688
|
return null;
|
|
17648
17689
|
}
|
|
17649
17690
|
}
|
|
@@ -17660,6 +17701,7 @@ function createConnectedProvidersCacheStore(getCacheDir2 = getOmoOpenCodeCacheDi
|
|
|
17660
17701
|
};
|
|
17661
17702
|
try {
|
|
17662
17703
|
writeFileSync2(cacheFile, JSON.stringify(cacheData, null, 2));
|
|
17704
|
+
memProviderModels = cacheData;
|
|
17663
17705
|
log("[connected-providers-cache] Provider-models cache written", {
|
|
17664
17706
|
providerCount: Object.keys(data.models).length
|
|
17665
17707
|
});
|
|
@@ -20439,12 +20481,14 @@ async function handleSessionIdle(args) {
|
|
|
20439
20481
|
return;
|
|
20440
20482
|
}
|
|
20441
20483
|
if (!todos || todos.length === 0) {
|
|
20484
|
+
sessionStateStore.resetContinuationProgress(sessionID);
|
|
20442
20485
|
sessionStateStore.resetContinuationProgress(sessionID);
|
|
20443
20486
|
log(`[${HOOK_NAME}] No todos`, { sessionID });
|
|
20444
20487
|
return;
|
|
20445
20488
|
}
|
|
20446
20489
|
const incompleteCount = getIncompleteCount(todos);
|
|
20447
20490
|
if (incompleteCount === 0) {
|
|
20491
|
+
sessionStateStore.resetContinuationProgress(sessionID);
|
|
20448
20492
|
sessionStateStore.resetContinuationProgress(sessionID);
|
|
20449
20493
|
log(`[${HOOK_NAME}] All todos complete`, { sessionID, total: todos.length });
|
|
20450
20494
|
return;
|
|
@@ -20458,20 +20502,12 @@ async function handleSessionIdle(args) {
|
|
|
20458
20502
|
log(`[${HOOK_NAME}] Reset consecutive failures after recovery window`, { sessionID, failureResetWindowMs: FAILURE_RESET_WINDOW_MS });
|
|
20459
20503
|
}
|
|
20460
20504
|
if (state2.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
20461
|
-
log(`[${HOOK_NAME}] Skipped: max consecutive failures reached`, {
|
|
20462
|
-
sessionID,
|
|
20463
|
-
consecutiveFailures: state2.consecutiveFailures,
|
|
20464
|
-
maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES
|
|
20465
|
-
});
|
|
20505
|
+
log(`[${HOOK_NAME}] Skipped: max consecutive failures reached`, { sessionID, consecutiveFailures: state2.consecutiveFailures });
|
|
20466
20506
|
return;
|
|
20467
20507
|
}
|
|
20468
20508
|
const effectiveCooldown = CONTINUATION_COOLDOWN_MS * Math.pow(2, Math.min(state2.consecutiveFailures, 5));
|
|
20469
20509
|
if (state2.lastInjectedAt && Date.now() - state2.lastInjectedAt < effectiveCooldown) {
|
|
20470
|
-
log(`[${HOOK_NAME}] Skipped: cooldown active`, {
|
|
20471
|
-
sessionID,
|
|
20472
|
-
effectiveCooldown,
|
|
20473
|
-
consecutiveFailures: state2.consecutiveFailures
|
|
20474
|
-
});
|
|
20510
|
+
log(`[${HOOK_NAME}] Skipped: cooldown active`, { sessionID, effectiveCooldown, consecutiveFailures: state2.consecutiveFailures });
|
|
20475
20511
|
return;
|
|
20476
20512
|
}
|
|
20477
20513
|
let resolvedInfo;
|
|
@@ -36324,6 +36360,15 @@ function takePendingCall(callID) {
|
|
|
36324
36360
|
import * as fs6 from "fs";
|
|
36325
36361
|
import { tmpdir as tmpdir4 } from "os";
|
|
36326
36362
|
import { join as join30 } from "path";
|
|
36363
|
+
var ApplyPatchMetadataSchema = zod_default.object({
|
|
36364
|
+
files: zod_default.array(zod_default.object({
|
|
36365
|
+
filePath: zod_default.string(),
|
|
36366
|
+
movePath: zod_default.string().optional(),
|
|
36367
|
+
before: zod_default.string(),
|
|
36368
|
+
after: zod_default.string(),
|
|
36369
|
+
type: zod_default.string().optional()
|
|
36370
|
+
}))
|
|
36371
|
+
});
|
|
36327
36372
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
36328
36373
|
var DEBUG_FILE3 = join30(tmpdir4(), "comment-checker-debug.log");
|
|
36329
36374
|
function debugLog3(...args) {
|
|
@@ -36384,15 +36429,6 @@ function createCommentCheckerHooks(config2) {
|
|
|
36384
36429
|
debugLog3("skipping due to tool failure in output");
|
|
36385
36430
|
return;
|
|
36386
36431
|
}
|
|
36387
|
-
const ApplyPatchMetadataSchema = zod_default.object({
|
|
36388
|
-
files: zod_default.array(zod_default.object({
|
|
36389
|
-
filePath: zod_default.string(),
|
|
36390
|
-
movePath: zod_default.string().optional(),
|
|
36391
|
-
before: zod_default.string(),
|
|
36392
|
-
after: zod_default.string(),
|
|
36393
|
-
type: zod_default.string().optional()
|
|
36394
|
-
}))
|
|
36395
|
-
});
|
|
36396
36432
|
if (toolLower === "apply_patch") {
|
|
36397
36433
|
const parsed = ApplyPatchMetadataSchema.safeParse(output.metadata);
|
|
36398
36434
|
if (!parsed.success) {
|
|
@@ -36863,7 +36899,7 @@ function isTokenLimitError(text) {
|
|
|
36863
36899
|
return false;
|
|
36864
36900
|
}
|
|
36865
36901
|
const lower = text.toLowerCase();
|
|
36866
|
-
return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw
|
|
36902
|
+
return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw));
|
|
36867
36903
|
}
|
|
36868
36904
|
function parseAnthropicTokenLimitError(err) {
|
|
36869
36905
|
try {
|
|
@@ -38281,7 +38317,6 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
|
|
|
38281
38317
|
};
|
|
38282
38318
|
}
|
|
38283
38319
|
// src/hooks/think-mode/detector.ts
|
|
38284
|
-
var ENGLISH_PATTERNS = [/\bultrathink\b/i, /\bthink\b/i];
|
|
38285
38320
|
var MULTILINGUAL_KEYWORDS = [
|
|
38286
38321
|
"\uC0DD\uAC01",
|
|
38287
38322
|
"\uAC80\uD1A0",
|
|
@@ -38367,8 +38402,7 @@ var MULTILINGUAL_KEYWORDS = [
|
|
|
38367
38402
|
"fikir",
|
|
38368
38403
|
"berfikir"
|
|
38369
38404
|
];
|
|
38370
|
-
var
|
|
38371
|
-
var THINK_PATTERNS = [...ENGLISH_PATTERNS, ...MULTILINGUAL_PATTERNS];
|
|
38405
|
+
var COMBINED_THINK_PATTERN = new RegExp(`\\b(?:ultrathink|think)\\b|${MULTILINGUAL_KEYWORDS.join("|")}`, "i");
|
|
38372
38406
|
var CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
|
|
38373
38407
|
var INLINE_CODE_PATTERN = /`[^`]+`/g;
|
|
38374
38408
|
function removeCodeBlocks(text) {
|
|
@@ -38376,7 +38410,7 @@ function removeCodeBlocks(text) {
|
|
|
38376
38410
|
}
|
|
38377
38411
|
function detectThinkKeyword(text) {
|
|
38378
38412
|
const textWithoutCode = removeCodeBlocks(text);
|
|
38379
|
-
return
|
|
38413
|
+
return COMBINED_THINK_PATTERN.test(textWithoutCode);
|
|
38380
38414
|
}
|
|
38381
38415
|
function extractPromptText(parts) {
|
|
38382
38416
|
return parts.filter((p) => p.type === "text").map((p) => p.text || "").join("");
|
|
@@ -39033,16 +39067,16 @@ async function loadPluginExtendedConfig() {
|
|
|
39033
39067
|
}
|
|
39034
39068
|
return merged;
|
|
39035
39069
|
}
|
|
39036
|
-
var
|
|
39070
|
+
var regexCache2 = new Map;
|
|
39037
39071
|
function getRegex(pattern) {
|
|
39038
|
-
let regex =
|
|
39072
|
+
let regex = regexCache2.get(pattern);
|
|
39039
39073
|
if (!regex) {
|
|
39040
39074
|
try {
|
|
39041
39075
|
regex = new RegExp(pattern);
|
|
39042
|
-
|
|
39076
|
+
regexCache2.set(pattern, regex);
|
|
39043
39077
|
} catch {
|
|
39044
39078
|
regex = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
39045
|
-
|
|
39079
|
+
regexCache2.set(pattern, regex);
|
|
39046
39080
|
}
|
|
39047
39081
|
}
|
|
39048
39082
|
return regex;
|
|
@@ -39908,8 +39942,6 @@ function createToolExecuteAfterHandler(ctx, config2) {
|
|
|
39908
39942
|
if (!output) {
|
|
39909
39943
|
return;
|
|
39910
39944
|
}
|
|
39911
|
-
const claudeConfig = await loadClaudeHooksConfig();
|
|
39912
|
-
const extendedConfig = await loadPluginExtendedConfig();
|
|
39913
39945
|
const cachedInput = getToolInput(input.sessionID, input.tool, input.callID) || {};
|
|
39914
39946
|
appendTranscriptEntry(input.sessionID, {
|
|
39915
39947
|
type: "tool_result",
|
|
@@ -39921,6 +39953,8 @@ function createToolExecuteAfterHandler(ctx, config2) {
|
|
|
39921
39953
|
if (isHookDisabled(config2, "PostToolUse")) {
|
|
39922
39954
|
return;
|
|
39923
39955
|
}
|
|
39956
|
+
const claudeConfig = await loadClaudeHooksConfig();
|
|
39957
|
+
const extendedConfig = await loadPluginExtendedConfig();
|
|
39924
39958
|
const postClient = {
|
|
39925
39959
|
session: {
|
|
39926
39960
|
messages: (opts) => ctx.client.session.messages(opts)
|
|
@@ -40101,8 +40135,6 @@ function createToolExecuteBeforeHandler(ctx, config2) {
|
|
|
40101
40135
|
output.args.todos = parsed;
|
|
40102
40136
|
log("todowrite: parsed todos string to array", { sessionID: input.sessionID });
|
|
40103
40137
|
}
|
|
40104
|
-
const claudeConfig = await loadClaudeHooksConfig();
|
|
40105
|
-
const extendedConfig = await loadPluginExtendedConfig();
|
|
40106
40138
|
appendTranscriptEntry(input.sessionID, {
|
|
40107
40139
|
type: "tool_use",
|
|
40108
40140
|
timestamp: new Date().toISOString(),
|
|
@@ -40113,6 +40145,8 @@ function createToolExecuteBeforeHandler(ctx, config2) {
|
|
|
40113
40145
|
if (isHookDisabled(config2, "PreToolUse")) {
|
|
40114
40146
|
return;
|
|
40115
40147
|
}
|
|
40148
|
+
const claudeConfig = await loadClaudeHooksConfig();
|
|
40149
|
+
const extendedConfig = await loadPluginExtendedConfig();
|
|
40116
40150
|
const preCtx = {
|
|
40117
40151
|
sessionId: input.sessionID,
|
|
40118
40152
|
toolName: input.tool,
|
|
@@ -47544,8 +47578,7 @@ var BabysittingConfigSchema = exports_external.object({
|
|
|
47544
47578
|
var CircuitBreakerConfigSchema = exports_external.object({
|
|
47545
47579
|
enabled: exports_external.boolean().optional(),
|
|
47546
47580
|
maxToolCalls: exports_external.number().int().min(10).optional(),
|
|
47547
|
-
|
|
47548
|
-
repetitionThresholdPercent: exports_external.number().gt(0).max(100).optional()
|
|
47581
|
+
consecutiveThreshold: exports_external.number().int().min(5).optional()
|
|
47549
47582
|
});
|
|
47550
47583
|
var BackgroundTaskConfigSchema = exports_external.object({
|
|
47551
47584
|
defaultConcurrency: exports_external.number().min(1).optional(),
|
|
@@ -49640,9 +49673,6 @@ function skillToCommandInfo(skill) {
|
|
|
49640
49673
|
lazyContentLoader: skill.lazyContent
|
|
49641
49674
|
};
|
|
49642
49675
|
}
|
|
49643
|
-
function filterDiscoveredCommandsByScope(commands3, scope) {
|
|
49644
|
-
return commands3.filter((command) => command.scope === scope);
|
|
49645
|
-
}
|
|
49646
49676
|
async function discoverAllCommands(options) {
|
|
49647
49677
|
const discoveredCommands = discoverCommandsSync(process.cwd(), {
|
|
49648
49678
|
pluginsEnabled: options?.pluginsEnabled,
|
|
@@ -49650,14 +49680,17 @@ async function discoverAllCommands(options) {
|
|
|
49650
49680
|
});
|
|
49651
49681
|
const skills2 = options?.skills ?? await discoverAllSkills();
|
|
49652
49682
|
const skillCommands = skills2.map(skillToCommandInfo);
|
|
49683
|
+
const scopeOrder = ["project", "user", "opencode-project", "opencode", "builtin", "plugin"];
|
|
49684
|
+
const grouped = new Map;
|
|
49685
|
+
for (const cmd of discoveredCommands) {
|
|
49686
|
+
const list = grouped.get(cmd.scope) ?? [];
|
|
49687
|
+
list.push(cmd);
|
|
49688
|
+
grouped.set(cmd.scope, list);
|
|
49689
|
+
}
|
|
49690
|
+
const orderedCommands = scopeOrder.flatMap((scope) => grouped.get(scope) ?? []);
|
|
49653
49691
|
return [
|
|
49654
49692
|
...skillCommands,
|
|
49655
|
-
...
|
|
49656
|
-
...filterDiscoveredCommandsByScope(discoveredCommands, "user"),
|
|
49657
|
-
...filterDiscoveredCommandsByScope(discoveredCommands, "opencode-project"),
|
|
49658
|
-
...filterDiscoveredCommandsByScope(discoveredCommands, "opencode"),
|
|
49659
|
-
...filterDiscoveredCommandsByScope(discoveredCommands, "builtin"),
|
|
49660
|
-
...filterDiscoveredCommandsByScope(discoveredCommands, "plugin")
|
|
49693
|
+
...orderedCommands
|
|
49661
49694
|
];
|
|
49662
49695
|
}
|
|
49663
49696
|
async function findCommand2(commandName, options) {
|
|
@@ -53418,6 +53451,7 @@ function getErrorMessage2(error48) {
|
|
|
53418
53451
|
return "";
|
|
53419
53452
|
}
|
|
53420
53453
|
}
|
|
53454
|
+
var DEFAULT_RETRY_PATTERN = new RegExp(`\\b(${DEFAULT_CONFIG2.retry_on_errors.join("|")})\\b`);
|
|
53421
53455
|
function extractStatusCode(error48, retryOnErrors) {
|
|
53422
53456
|
if (!error48)
|
|
53423
53457
|
return;
|
|
@@ -53432,8 +53466,7 @@ function extractStatusCode(error48, retryOnErrors) {
|
|
|
53432
53466
|
if (statusCode !== undefined) {
|
|
53433
53467
|
return statusCode;
|
|
53434
53468
|
}
|
|
53435
|
-
const
|
|
53436
|
-
const pattern = new RegExp(`\\b(${codes.join("|")})\\b`);
|
|
53469
|
+
const pattern = retryOnErrors ? new RegExp(`\\b(${retryOnErrors.join("|")})\\b`) : DEFAULT_RETRY_PATTERN;
|
|
53437
53470
|
const message = getErrorMessage2(error48);
|
|
53438
53471
|
const statusMatch = message.match(pattern);
|
|
53439
53472
|
if (statusMatch) {
|
|
@@ -54714,97 +54747,97 @@ function toImageDimensions(width, height) {
|
|
|
54714
54747
|
}
|
|
54715
54748
|
return { width, height };
|
|
54716
54749
|
}
|
|
54717
|
-
function parsePngDimensions(
|
|
54718
|
-
if (
|
|
54750
|
+
function parsePngDimensions(buffer2) {
|
|
54751
|
+
if (buffer2.length < 24) {
|
|
54719
54752
|
return null;
|
|
54720
54753
|
}
|
|
54721
|
-
const isPngSignature =
|
|
54722
|
-
if (!isPngSignature ||
|
|
54754
|
+
const isPngSignature = buffer2[0] === 137 && buffer2[1] === 80 && buffer2[2] === 78 && buffer2[3] === 71 && buffer2[4] === 13 && buffer2[5] === 10 && buffer2[6] === 26 && buffer2[7] === 10;
|
|
54755
|
+
if (!isPngSignature || buffer2.toString("ascii", 12, 16) !== "IHDR") {
|
|
54723
54756
|
return null;
|
|
54724
54757
|
}
|
|
54725
|
-
const width =
|
|
54726
|
-
const height =
|
|
54758
|
+
const width = buffer2.readUInt32BE(16);
|
|
54759
|
+
const height = buffer2.readUInt32BE(20);
|
|
54727
54760
|
return toImageDimensions(width, height);
|
|
54728
54761
|
}
|
|
54729
|
-
function parseGifDimensions(
|
|
54730
|
-
if (
|
|
54762
|
+
function parseGifDimensions(buffer2) {
|
|
54763
|
+
if (buffer2.length < 10) {
|
|
54731
54764
|
return null;
|
|
54732
54765
|
}
|
|
54733
|
-
if (
|
|
54766
|
+
if (buffer2.toString("ascii", 0, 4) !== "GIF8") {
|
|
54734
54767
|
return null;
|
|
54735
54768
|
}
|
|
54736
|
-
const width =
|
|
54737
|
-
const height =
|
|
54769
|
+
const width = buffer2.readUInt16LE(6);
|
|
54770
|
+
const height = buffer2.readUInt16LE(8);
|
|
54738
54771
|
return toImageDimensions(width, height);
|
|
54739
54772
|
}
|
|
54740
|
-
function parseJpegDimensions(
|
|
54741
|
-
if (
|
|
54773
|
+
function parseJpegDimensions(buffer2) {
|
|
54774
|
+
if (buffer2.length < 4 || buffer2[0] !== 255 || buffer2[1] !== 216) {
|
|
54742
54775
|
return null;
|
|
54743
54776
|
}
|
|
54744
54777
|
let offset = 2;
|
|
54745
|
-
while (offset <
|
|
54746
|
-
if (
|
|
54778
|
+
while (offset < buffer2.length) {
|
|
54779
|
+
if (buffer2[offset] !== 255) {
|
|
54747
54780
|
offset += 1;
|
|
54748
54781
|
continue;
|
|
54749
54782
|
}
|
|
54750
|
-
while (offset <
|
|
54783
|
+
while (offset < buffer2.length && buffer2[offset] === 255) {
|
|
54751
54784
|
offset += 1;
|
|
54752
54785
|
}
|
|
54753
|
-
if (offset >=
|
|
54786
|
+
if (offset >= buffer2.length) {
|
|
54754
54787
|
return null;
|
|
54755
54788
|
}
|
|
54756
|
-
const marker =
|
|
54789
|
+
const marker = buffer2[offset];
|
|
54757
54790
|
offset += 1;
|
|
54758
54791
|
if (marker === 217 || marker === 218) {
|
|
54759
54792
|
break;
|
|
54760
54793
|
}
|
|
54761
|
-
if (offset + 1 >=
|
|
54794
|
+
if (offset + 1 >= buffer2.length) {
|
|
54762
54795
|
return null;
|
|
54763
54796
|
}
|
|
54764
|
-
const segmentLength =
|
|
54797
|
+
const segmentLength = buffer2.readUInt16BE(offset);
|
|
54765
54798
|
if (segmentLength < 2) {
|
|
54766
54799
|
return null;
|
|
54767
54800
|
}
|
|
54768
|
-
if ((marker === 192 || marker === 194) && offset + 7 <
|
|
54769
|
-
const height =
|
|
54770
|
-
const width =
|
|
54801
|
+
if ((marker === 192 || marker === 194) && offset + 7 < buffer2.length) {
|
|
54802
|
+
const height = buffer2.readUInt16BE(offset + 3);
|
|
54803
|
+
const width = buffer2.readUInt16BE(offset + 5);
|
|
54771
54804
|
return toImageDimensions(width, height);
|
|
54772
54805
|
}
|
|
54773
54806
|
offset += segmentLength;
|
|
54774
54807
|
}
|
|
54775
54808
|
return null;
|
|
54776
54809
|
}
|
|
54777
|
-
function readUInt24LE(
|
|
54778
|
-
return
|
|
54810
|
+
function readUInt24LE(buffer2, offset) {
|
|
54811
|
+
return buffer2[offset] | buffer2[offset + 1] << 8 | buffer2[offset + 2] << 16;
|
|
54779
54812
|
}
|
|
54780
|
-
function parseWebpDimensions(
|
|
54781
|
-
if (
|
|
54813
|
+
function parseWebpDimensions(buffer2) {
|
|
54814
|
+
if (buffer2.length < 16) {
|
|
54782
54815
|
return null;
|
|
54783
54816
|
}
|
|
54784
|
-
if (
|
|
54817
|
+
if (buffer2.toString("ascii", 0, 4) !== "RIFF" || buffer2.toString("ascii", 8, 12) !== "WEBP") {
|
|
54785
54818
|
return null;
|
|
54786
54819
|
}
|
|
54787
|
-
const chunkType =
|
|
54820
|
+
const chunkType = buffer2.toString("ascii", 12, 16);
|
|
54788
54821
|
if (chunkType === "VP8 ") {
|
|
54789
|
-
if (
|
|
54822
|
+
if (buffer2[23] !== 157 || buffer2[24] !== 1 || buffer2[25] !== 42) {
|
|
54790
54823
|
return null;
|
|
54791
54824
|
}
|
|
54792
|
-
const width =
|
|
54793
|
-
const height =
|
|
54825
|
+
const width = buffer2.readUInt16LE(26) & 16383;
|
|
54826
|
+
const height = buffer2.readUInt16LE(28) & 16383;
|
|
54794
54827
|
return toImageDimensions(width, height);
|
|
54795
54828
|
}
|
|
54796
54829
|
if (chunkType === "VP8L") {
|
|
54797
|
-
if (
|
|
54830
|
+
if (buffer2.length < 25 || buffer2[20] !== 47) {
|
|
54798
54831
|
return null;
|
|
54799
54832
|
}
|
|
54800
|
-
const bits =
|
|
54833
|
+
const bits = buffer2.readUInt32LE(21);
|
|
54801
54834
|
const width = (bits & 16383) + 1;
|
|
54802
54835
|
const height = (bits >>> 14 & 16383) + 1;
|
|
54803
54836
|
return toImageDimensions(width, height);
|
|
54804
54837
|
}
|
|
54805
54838
|
if (chunkType === "VP8X") {
|
|
54806
|
-
const width = readUInt24LE(
|
|
54807
|
-
const height = readUInt24LE(
|
|
54839
|
+
const width = readUInt24LE(buffer2, 24) + 1;
|
|
54840
|
+
const height = readUInt24LE(buffer2, 27) + 1;
|
|
54808
54841
|
return toImageDimensions(width, height);
|
|
54809
54842
|
}
|
|
54810
54843
|
return null;
|
|
@@ -54819,22 +54852,22 @@ function parseImageDimensions(base64DataUrl, mimeType) {
|
|
|
54819
54852
|
return null;
|
|
54820
54853
|
}
|
|
54821
54854
|
const headerBase64 = rawBase64.length > HEADER_BASE64_CHARS ? rawBase64.slice(0, HEADER_BASE64_CHARS) : rawBase64;
|
|
54822
|
-
const
|
|
54823
|
-
if (
|
|
54855
|
+
const buffer2 = Buffer.from(headerBase64, "base64");
|
|
54856
|
+
if (buffer2.length === 0) {
|
|
54824
54857
|
return null;
|
|
54825
54858
|
}
|
|
54826
54859
|
const normalizedMime = mimeType.toLowerCase();
|
|
54827
54860
|
if (normalizedMime === "image/png") {
|
|
54828
|
-
return parsePngDimensions(
|
|
54861
|
+
return parsePngDimensions(buffer2);
|
|
54829
54862
|
}
|
|
54830
54863
|
if (normalizedMime === "image/gif") {
|
|
54831
|
-
return parseGifDimensions(
|
|
54864
|
+
return parseGifDimensions(buffer2);
|
|
54832
54865
|
}
|
|
54833
54866
|
if (normalizedMime === "image/jpeg" || normalizedMime === "image/jpg") {
|
|
54834
|
-
return parseJpegDimensions(
|
|
54867
|
+
return parseJpegDimensions(buffer2);
|
|
54835
54868
|
}
|
|
54836
54869
|
if (normalizedMime === "image/webp") {
|
|
54837
|
-
return parseWebpDimensions(
|
|
54870
|
+
return parseWebpDimensions(buffer2);
|
|
54838
54871
|
}
|
|
54839
54872
|
return null;
|
|
54840
54873
|
} catch {
|
|
@@ -73268,8 +73301,8 @@ function convertBase64ImageToJpeg(base64Data, mimeType) {
|
|
|
73268
73301
|
const tempFiles = [inputPath];
|
|
73269
73302
|
try {
|
|
73270
73303
|
const cleanBase64 = base64Data.replace(/^data:[^;]+;base64,/, "");
|
|
73271
|
-
const
|
|
73272
|
-
writeFileSync19(inputPath,
|
|
73304
|
+
const buffer2 = Buffer.from(cleanBase64, "base64");
|
|
73305
|
+
writeFileSync19(inputPath, buffer2);
|
|
73273
73306
|
log(`[image-converter] Converting Base64 ${mimeType} to JPEG`);
|
|
73274
73307
|
const outputPath = convertImageToJpeg(inputPath, mimeType);
|
|
73275
73308
|
tempFiles.push(outputPath);
|
|
@@ -75738,10 +75771,11 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
75738
75771
|
allTasks.push(task);
|
|
75739
75772
|
}
|
|
75740
75773
|
}
|
|
75774
|
+
const taskMap = new Map(allTasks.map((t) => [t.id, t]));
|
|
75741
75775
|
const activeTasks = allTasks.filter((task) => task.status !== "completed" && task.status !== "deleted");
|
|
75742
75776
|
const summaries = activeTasks.map((task) => {
|
|
75743
75777
|
const unresolvedBlockers = task.blockedBy.filter((blockerId) => {
|
|
75744
|
-
const blockerTask =
|
|
75778
|
+
const blockerTask = taskMap.get(blockerId);
|
|
75745
75779
|
return !blockerTask || blockerTask.status !== "completed";
|
|
75746
75780
|
});
|
|
75747
75781
|
return {
|
|
@@ -76430,6 +76464,15 @@ function applyPrepend(lines, text) {
|
|
|
76430
76464
|
}
|
|
76431
76465
|
|
|
76432
76466
|
// src/tools/hashline-edit/edit-operations.ts
|
|
76467
|
+
function arraysEqual(a, b) {
|
|
76468
|
+
if (a.length !== b.length)
|
|
76469
|
+
return false;
|
|
76470
|
+
for (let i2 = 0;i2 < a.length; i2++) {
|
|
76471
|
+
if (a[i2] !== b[i2])
|
|
76472
|
+
return false;
|
|
76473
|
+
}
|
|
76474
|
+
return true;
|
|
76475
|
+
}
|
|
76433
76476
|
function applyHashlineEditsWithReport(content, edits) {
|
|
76434
76477
|
if (edits.length === 0) {
|
|
76435
76478
|
return {
|
|
@@ -76459,9 +76502,7 @@ function applyHashlineEditsWithReport(content, edits) {
|
|
|
76459
76502
|
switch (edit.op) {
|
|
76460
76503
|
case "replace": {
|
|
76461
76504
|
const next = edit.end ? applyReplaceLines(lines, edit.pos, edit.end, edit.lines, { skipValidation: true }) : applySetLine(lines, edit.pos, edit.lines, { skipValidation: true });
|
|
76462
|
-
if (next
|
|
76463
|
-
`) === lines.join(`
|
|
76464
|
-
`)) {
|
|
76505
|
+
if (arraysEqual(next, lines)) {
|
|
76465
76506
|
noopEdits += 1;
|
|
76466
76507
|
break;
|
|
76467
76508
|
}
|
|
@@ -76470,9 +76511,7 @@ function applyHashlineEditsWithReport(content, edits) {
|
|
|
76470
76511
|
}
|
|
76471
76512
|
case "append": {
|
|
76472
76513
|
const next = edit.pos ? applyInsertAfter(lines, edit.pos, edit.lines, { skipValidation: true }) : applyAppend(lines, edit.lines);
|
|
76473
|
-
if (next
|
|
76474
|
-
`) === lines.join(`
|
|
76475
|
-
`)) {
|
|
76514
|
+
if (arraysEqual(next, lines)) {
|
|
76476
76515
|
noopEdits += 1;
|
|
76477
76516
|
break;
|
|
76478
76517
|
}
|
|
@@ -76481,9 +76520,7 @@ function applyHashlineEditsWithReport(content, edits) {
|
|
|
76481
76520
|
}
|
|
76482
76521
|
case "prepend": {
|
|
76483
76522
|
const next = edit.pos ? applyInsertBefore(lines, edit.pos, edit.lines, { skipValidation: true }) : applyPrepend(lines, edit.lines);
|
|
76484
|
-
if (next
|
|
76485
|
-
`) === lines.join(`
|
|
76486
|
-
`)) {
|
|
76523
|
+
if (arraysEqual(next, lines)) {
|
|
76487
76524
|
noopEdits += 1;
|
|
76488
76525
|
break;
|
|
76489
76526
|
}
|
|
@@ -78048,8 +78085,7 @@ var MIN_STABILITY_TIME_MS2 = 10 * 1000;
|
|
|
78048
78085
|
var DEFAULT_STALE_TIMEOUT_MS = 1200000;
|
|
78049
78086
|
var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
|
|
78050
78087
|
var DEFAULT_MAX_TOOL_CALLS = 200;
|
|
78051
|
-
var
|
|
78052
|
-
var DEFAULT_CIRCUIT_BREAKER_REPETITION_THRESHOLD_PERCENT = 80;
|
|
78088
|
+
var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
|
|
78053
78089
|
var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
|
|
78054
78090
|
var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
|
|
78055
78091
|
var MIN_IDLE_TIME_MS = 5000;
|
|
@@ -78471,6 +78507,22 @@ function removeTaskToastTracking(taskId) {
|
|
|
78471
78507
|
}
|
|
78472
78508
|
}
|
|
78473
78509
|
|
|
78510
|
+
// src/features/background-agent/session-status-classifier.ts
|
|
78511
|
+
var ACTIVE_SESSION_STATUSES = new Set(["busy", "retry", "running"]);
|
|
78512
|
+
var KNOWN_TERMINAL_STATUSES = new Set(["idle", "interrupted"]);
|
|
78513
|
+
function isActiveSessionStatus(type2) {
|
|
78514
|
+
if (ACTIVE_SESSION_STATUSES.has(type2)) {
|
|
78515
|
+
return true;
|
|
78516
|
+
}
|
|
78517
|
+
if (!KNOWN_TERMINAL_STATUSES.has(type2)) {
|
|
78518
|
+
log("[background-agent] Unknown session status type encountered:", type2);
|
|
78519
|
+
}
|
|
78520
|
+
return false;
|
|
78521
|
+
}
|
|
78522
|
+
function isTerminalSessionStatus(type2) {
|
|
78523
|
+
return KNOWN_TERMINAL_STATUSES.has(type2) && type2 !== "idle";
|
|
78524
|
+
}
|
|
78525
|
+
|
|
78474
78526
|
// src/features/background-agent/task-poller.ts
|
|
78475
78527
|
var TERMINAL_TASK_STATUSES = new Set([
|
|
78476
78528
|
"completed",
|
|
@@ -78549,7 +78601,7 @@ async function checkAndInterruptStaleTasks(args) {
|
|
|
78549
78601
|
if (!startedAt || !sessionID)
|
|
78550
78602
|
continue;
|
|
78551
78603
|
const sessionStatus = sessionStatuses?.[sessionID]?.type;
|
|
78552
|
-
const sessionIsRunning = sessionStatus !== undefined && sessionStatus
|
|
78604
|
+
const sessionIsRunning = sessionStatus !== undefined && isActiveSessionStatus(sessionStatus);
|
|
78553
78605
|
const runtime = now - startedAt.getTime();
|
|
78554
78606
|
if (!task.progress?.lastUpdate) {
|
|
78555
78607
|
if (sessionIsRunning)
|
|
@@ -78607,18 +78659,22 @@ function resolveCircuitBreakerSettings(config4) {
|
|
|
78607
78659
|
return {
|
|
78608
78660
|
enabled: config4?.circuitBreaker?.enabled ?? DEFAULT_CIRCUIT_BREAKER_ENABLED,
|
|
78609
78661
|
maxToolCalls: config4?.circuitBreaker?.maxToolCalls ?? config4?.maxToolCalls ?? DEFAULT_MAX_TOOL_CALLS,
|
|
78610
|
-
|
|
78611
|
-
repetitionThresholdPercent: config4?.circuitBreaker?.repetitionThresholdPercent ?? DEFAULT_CIRCUIT_BREAKER_REPETITION_THRESHOLD_PERCENT
|
|
78662
|
+
consecutiveThreshold: config4?.circuitBreaker?.consecutiveThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD
|
|
78612
78663
|
};
|
|
78613
78664
|
}
|
|
78614
78665
|
function recordToolCall(window, toolName, settings, toolInput) {
|
|
78615
|
-
const previous = window?.toolSignatures ?? [];
|
|
78616
78666
|
const signature = createToolCallSignature(toolName, toolInput);
|
|
78617
|
-
|
|
78667
|
+
if (window && window.lastSignature === signature) {
|
|
78668
|
+
return {
|
|
78669
|
+
lastSignature: signature,
|
|
78670
|
+
consecutiveCount: window.consecutiveCount + 1,
|
|
78671
|
+
threshold: settings.consecutiveThreshold
|
|
78672
|
+
};
|
|
78673
|
+
}
|
|
78618
78674
|
return {
|
|
78619
|
-
|
|
78620
|
-
|
|
78621
|
-
|
|
78675
|
+
lastSignature: signature,
|
|
78676
|
+
consecutiveCount: 1,
|
|
78677
|
+
threshold: settings.consecutiveThreshold
|
|
78622
78678
|
};
|
|
78623
78679
|
}
|
|
78624
78680
|
function sortObject2(obj) {
|
|
@@ -78645,36 +78701,13 @@ function createToolCallSignature(toolName, toolInput) {
|
|
|
78645
78701
|
return `${toolName}::${JSON.stringify(sortObject2(toolInput))}`;
|
|
78646
78702
|
}
|
|
78647
78703
|
function detectRepetitiveToolUse(window) {
|
|
78648
|
-
if (!window || window.
|
|
78649
|
-
return { triggered: false };
|
|
78650
|
-
}
|
|
78651
|
-
const counts = new Map;
|
|
78652
|
-
for (const signature of window.toolSignatures) {
|
|
78653
|
-
counts.set(signature, (counts.get(signature) ?? 0) + 1);
|
|
78654
|
-
}
|
|
78655
|
-
let repeatedTool;
|
|
78656
|
-
let repeatedCount = 0;
|
|
78657
|
-
for (const [toolName, count] of counts.entries()) {
|
|
78658
|
-
if (count > repeatedCount) {
|
|
78659
|
-
repeatedTool = toolName;
|
|
78660
|
-
repeatedCount = count;
|
|
78661
|
-
}
|
|
78662
|
-
}
|
|
78663
|
-
const sampleSize = window.toolSignatures.length;
|
|
78664
|
-
const minimumSampleSize = Math.min(window.windowSize, Math.ceil(window.windowSize * window.thresholdPercent / 100));
|
|
78665
|
-
if (sampleSize < minimumSampleSize) {
|
|
78666
|
-
return { triggered: false };
|
|
78667
|
-
}
|
|
78668
|
-
const thresholdCount = Math.ceil(sampleSize * window.thresholdPercent / 100);
|
|
78669
|
-
if (!repeatedTool || repeatedCount < thresholdCount) {
|
|
78704
|
+
if (!window || window.consecutiveCount < window.threshold) {
|
|
78670
78705
|
return { triggered: false };
|
|
78671
78706
|
}
|
|
78672
78707
|
return {
|
|
78673
78708
|
triggered: true,
|
|
78674
|
-
toolName:
|
|
78675
|
-
repeatedCount
|
|
78676
|
-
sampleSize,
|
|
78677
|
-
thresholdPercent: window.thresholdPercent
|
|
78709
|
+
toolName: window.lastSignature.split("::")[0],
|
|
78710
|
+
repeatedCount: window.consecutiveCount
|
|
78678
78711
|
};
|
|
78679
78712
|
}
|
|
78680
78713
|
|
|
@@ -78773,6 +78806,7 @@ class BackgroundManager {
|
|
|
78773
78806
|
preStartDescendantReservations;
|
|
78774
78807
|
enableParentSessionNotifications;
|
|
78775
78808
|
taskHistory = new TaskHistory;
|
|
78809
|
+
cachedCircuitBreakerSettings;
|
|
78776
78810
|
constructor(ctx, config4, options) {
|
|
78777
78811
|
this.tasks = new Map;
|
|
78778
78812
|
this.notifications = new Map;
|
|
@@ -79365,34 +79399,33 @@ class BackgroundManager {
|
|
|
79365
79399
|
}
|
|
79366
79400
|
task.progress.lastUpdate = new Date;
|
|
79367
79401
|
if (partInfo?.type === "tool" || partInfo?.tool) {
|
|
79368
|
-
const countedToolPartIDs = task.progress.countedToolPartIDs ??
|
|
79369
|
-
const shouldCountToolCall = !partInfo.id || partInfo.state?.status !== "running" || !countedToolPartIDs.
|
|
79402
|
+
const countedToolPartIDs = task.progress.countedToolPartIDs ?? new Set;
|
|
79403
|
+
const shouldCountToolCall = !partInfo.id || partInfo.state?.status !== "running" || !countedToolPartIDs.has(partInfo.id);
|
|
79370
79404
|
if (!shouldCountToolCall) {
|
|
79371
79405
|
return;
|
|
79372
79406
|
}
|
|
79373
79407
|
if (partInfo.id && partInfo.state?.status === "running") {
|
|
79374
|
-
|
|
79408
|
+
countedToolPartIDs.add(partInfo.id);
|
|
79409
|
+
task.progress.countedToolPartIDs = countedToolPartIDs;
|
|
79375
79410
|
}
|
|
79376
79411
|
task.progress.toolCalls += 1;
|
|
79377
79412
|
task.progress.lastTool = partInfo.tool;
|
|
79378
|
-
const circuitBreaker = resolveCircuitBreakerSettings(this.config);
|
|
79413
|
+
const circuitBreaker = this.cachedCircuitBreakerSettings ?? (this.cachedCircuitBreakerSettings = resolveCircuitBreakerSettings(this.config));
|
|
79379
79414
|
if (partInfo.tool) {
|
|
79380
79415
|
task.progress.toolCallWindow = recordToolCall(task.progress.toolCallWindow, partInfo.tool, circuitBreaker, partInfo.state?.input);
|
|
79381
79416
|
if (circuitBreaker.enabled) {
|
|
79382
79417
|
const loopDetection = detectRepetitiveToolUse(task.progress.toolCallWindow);
|
|
79383
79418
|
if (loopDetection.triggered) {
|
|
79384
|
-
log("[background-agent] Circuit breaker:
|
|
79419
|
+
log("[background-agent] Circuit breaker: consecutive tool usage detected", {
|
|
79385
79420
|
taskId: task.id,
|
|
79386
79421
|
agent: task.agent,
|
|
79387
79422
|
sessionID,
|
|
79388
79423
|
toolName: loopDetection.toolName,
|
|
79389
|
-
repeatedCount: loopDetection.repeatedCount
|
|
79390
|
-
sampleSize: loopDetection.sampleSize,
|
|
79391
|
-
thresholdPercent: loopDetection.thresholdPercent
|
|
79424
|
+
repeatedCount: loopDetection.repeatedCount
|
|
79392
79425
|
});
|
|
79393
79426
|
this.cancelTask(task.id, {
|
|
79394
79427
|
source: "circuit-breaker",
|
|
79395
|
-
reason: `Subagent
|
|
79428
|
+
reason: `Subagent called ${loopDetection.toolName} ${loopDetection.repeatedCount} consecutive times (threshold: ${circuitBreaker.consecutiveThreshold}). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`
|
|
79396
79429
|
});
|
|
79397
79430
|
return;
|
|
79398
79431
|
}
|
|
@@ -80042,7 +80075,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80042
80075
|
continue;
|
|
80043
80076
|
}
|
|
80044
80077
|
}
|
|
80045
|
-
if (sessionStatus && sessionStatus.type
|
|
80078
|
+
if (sessionStatus && isActiveSessionStatus(sessionStatus.type)) {
|
|
80046
80079
|
log("[background-agent] Session still running, relying on event-based progress:", {
|
|
80047
80080
|
taskId: task.id,
|
|
80048
80081
|
sessionID,
|
|
@@ -80051,6 +80084,17 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80051
80084
|
});
|
|
80052
80085
|
continue;
|
|
80053
80086
|
}
|
|
80087
|
+
if (sessionStatus && isTerminalSessionStatus(sessionStatus.type)) {
|
|
80088
|
+
await this.tryCompleteTask(task, `polling (terminal session status: ${sessionStatus.type})`);
|
|
80089
|
+
continue;
|
|
80090
|
+
}
|
|
80091
|
+
if (sessionStatus && sessionStatus.type !== "idle") {
|
|
80092
|
+
log("[background-agent] Unknown session status, treating as potentially idle:", {
|
|
80093
|
+
taskId: task.id,
|
|
80094
|
+
sessionID,
|
|
80095
|
+
sessionStatus: sessionStatus.type
|
|
80096
|
+
});
|
|
80097
|
+
}
|
|
80054
80098
|
const completionSource = sessionStatus?.type === "idle" ? "polling (idle status)" : "polling (session gone from status)";
|
|
80055
80099
|
const hasValidOutput = await this.validateSessionHasOutput(sessionID);
|
|
80056
80100
|
if (!hasValidOutput) {
|
|
@@ -82707,8 +82751,8 @@ async function generateVerifier(length) {
|
|
|
82707
82751
|
return await random(length);
|
|
82708
82752
|
}
|
|
82709
82753
|
async function generateChallenge(code_verifier) {
|
|
82710
|
-
const
|
|
82711
|
-
return btoa(String.fromCharCode(...new Uint8Array(
|
|
82754
|
+
const buffer2 = await (await crypto3).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
|
|
82755
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer2))).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
|
|
82712
82756
|
}
|
|
82713
82757
|
async function pkceChallenge(length) {
|
|
82714
82758
|
if (!length)
|
|
@@ -97809,10 +97853,7 @@ function createPluginInterface(args) {
|
|
|
97809
97853
|
const { ctx, pluginConfig, firstMessageVariantGate, managers, hooks: hooks2, tools } = args;
|
|
97810
97854
|
return {
|
|
97811
97855
|
tool: tools,
|
|
97812
|
-
"chat.params":
|
|
97813
|
-
const handler = createChatParamsHandler({ anthropicEffort: hooks2.anthropicEffort });
|
|
97814
|
-
await handler(input, output);
|
|
97815
|
-
},
|
|
97856
|
+
"chat.params": createChatParamsHandler({ anthropicEffort: hooks2.anthropicEffort }),
|
|
97816
97857
|
"chat.headers": createChatHeadersHandler({ ctx }),
|
|
97817
97858
|
"chat.message": createChatMessageHandler3({
|
|
97818
97859
|
ctx,
|