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/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
- fs.appendFileSync(logFile, logEntry);
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 buffer = new SharedArrayBuffer(4);
2458
- const data = new Int32Array(buffer, 0, 1);
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, buffer);
2461
- request.$cancellationData = buffer;
2483
+ this.buffers.set(request.id, buffer2);
2484
+ request.$cancellationData = buffer2;
2462
2485
  }
2463
2486
  async sendCancellation(_conn, id) {
2464
- const buffer = this.buffers.get(id);
2465
- if (buffer === undefined) {
2487
+ const buffer2 = this.buffers.get(id);
2488
+ if (buffer2 === undefined) {
2466
2489
  return;
2467
2490
  }
2468
- const data = new Int32Array(buffer, 0, 1);
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(buffer) {
2482
- this.data = new Int32Array(buffer, 0, 1);
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(buffer) {
2494
- this.token = new SharedArrayBufferCancellationToken(buffer);
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 buffer = request.$cancellationData;
2506
- if (buffer === undefined) {
2528
+ const buffer2 = request.$cancellationData;
2529
+ if (buffer2 === undefined) {
2507
2530
  return new cancellation_1.CancellationTokenSource;
2508
2531
  }
2509
- return new SharedArrayBufferCancellationTokenSource(buffer);
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((buffer) => {
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(buffer);
2868
+ return this.options.contentEncoder.encode(buffer2);
2846
2869
  } else {
2847
- return buffer;
2870
+ return buffer2;
2848
2871
  }
2849
2872
  });
2850
- return payload.then((buffer) => {
2873
+ return payload.then((buffer2) => {
2851
2874
  const headers = [];
2852
- headers.push(ContentLength, buffer.byteLength.toString(), CRLF);
2875
+ headers.push(ContentLength, buffer2.byteLength.toString(), CRLF);
2853
2876
  headers.push(CRLF);
2854
- return this.doWrite(msg, headers, buffer);
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 buffer = this._read(chunkBytesRead + offset);
2979
+ const buffer2 = this._read(chunkBytesRead + offset);
2957
2980
  const result = new Map;
2958
- const headers = this.toString(buffer, "ascii").split(CRLF);
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(buffer, length) {
4405
+ asNative(buffer2, length) {
4383
4406
  if (length === undefined) {
4384
- return buffer instanceof Buffer ? buffer : Buffer.from(buffer);
4407
+ return buffer2 instanceof Buffer ? buffer2 : Buffer.from(buffer2);
4385
4408
  } else {
4386
- return buffer instanceof Buffer ? buffer.slice(0, length) : Buffer.from(buffer, 0, length);
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: (buffer, options) => {
4493
+ decode: (buffer2, options) => {
4471
4494
  try {
4472
- if (buffer instanceof Buffer) {
4473
- return Promise.resolve(JSON.parse(buffer.toString(options.charset)));
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(buffer)));
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(buffer) {
8409
- buffer.length = 0;
8431
+ function consumeIsZone(buffer2) {
8432
+ buffer2.length = 0;
8410
8433
  return true;
8411
8434
  }
8412
- function consumeHextets(buffer, address, output) {
8413
- if (buffer.length) {
8414
- const hex5 = stringArrayToHexStripped(buffer);
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
- buffer.length = 0;
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 buffer = [];
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(buffer, address, output)) {
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(buffer, address, output)) {
8478
+ if (!consume(buffer2, address, output)) {
8456
8479
  break;
8457
8480
  }
8458
8481
  consume = consumeIsZone;
8459
8482
  } else {
8460
- buffer.push(cursor);
8483
+ buffer2.push(cursor);
8461
8484
  continue;
8462
8485
  }
8463
8486
  }
8464
- if (buffer.length) {
8487
+ if (buffer2.length) {
8465
8488
  if (consume === consumeIsZone) {
8466
- output.zone = buffer.join("");
8489
+ output.zone = buffer2.join("");
8467
8490
  } else if (endIpv6) {
8468
- address.push(buffer.join(""));
8491
+ address.push(buffer2.join(""));
8469
8492
  } else {
8470
- address.push(stringArrayToHexStripped(buffer));
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 buffer = Buffer.alloc(size);
12186
+ const buffer2 = Buffer.alloc(size);
12164
12187
  let fd;
12165
12188
  try {
12166
12189
  fd = fs19.openSync(command, "r");
12167
- fs19.readSync(fd, buffer, 0, size, 0);
12190
+ fs19.readSync(fd, buffer2, 0, size, 0);
12168
12191
  fs19.closeSync(fd);
12169
12192
  } catch (e) {}
12170
- return shebangCommand(buffer.toString());
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.split(pattern).join(replacement);
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
- const escaped = escapeRegexExceptAsterisk(p);
15356
- const regex = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "i");
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.toLowerCase()));
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 MULTILINGUAL_PATTERNS = MULTILINGUAL_KEYWORDS.map((kw) => new RegExp(kw, "i"));
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 THINK_PATTERNS.some((pattern) => pattern.test(textWithoutCode));
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 regexCache = new Map;
39070
+ var regexCache2 = new Map;
39037
39071
  function getRegex(pattern) {
39038
- let regex = regexCache.get(pattern);
39072
+ let regex = regexCache2.get(pattern);
39039
39073
  if (!regex) {
39040
39074
  try {
39041
39075
  regex = new RegExp(pattern);
39042
- regexCache.set(pattern, regex);
39076
+ regexCache2.set(pattern, regex);
39043
39077
  } catch {
39044
39078
  regex = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
39045
- regexCache.set(pattern, regex);
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
- windowSize: exports_external.number().int().min(5).optional(),
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
- ...filterDiscoveredCommandsByScope(discoveredCommands, "project"),
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 codes = retryOnErrors ?? DEFAULT_CONFIG2.retry_on_errors;
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(buffer) {
54718
- if (buffer.length < 24) {
54750
+ function parsePngDimensions(buffer2) {
54751
+ if (buffer2.length < 24) {
54719
54752
  return null;
54720
54753
  }
54721
- const isPngSignature = buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10;
54722
- if (!isPngSignature || buffer.toString("ascii", 12, 16) !== "IHDR") {
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 = buffer.readUInt32BE(16);
54726
- const height = buffer.readUInt32BE(20);
54758
+ const width = buffer2.readUInt32BE(16);
54759
+ const height = buffer2.readUInt32BE(20);
54727
54760
  return toImageDimensions(width, height);
54728
54761
  }
54729
- function parseGifDimensions(buffer) {
54730
- if (buffer.length < 10) {
54762
+ function parseGifDimensions(buffer2) {
54763
+ if (buffer2.length < 10) {
54731
54764
  return null;
54732
54765
  }
54733
- if (buffer.toString("ascii", 0, 4) !== "GIF8") {
54766
+ if (buffer2.toString("ascii", 0, 4) !== "GIF8") {
54734
54767
  return null;
54735
54768
  }
54736
- const width = buffer.readUInt16LE(6);
54737
- const height = buffer.readUInt16LE(8);
54769
+ const width = buffer2.readUInt16LE(6);
54770
+ const height = buffer2.readUInt16LE(8);
54738
54771
  return toImageDimensions(width, height);
54739
54772
  }
54740
- function parseJpegDimensions(buffer) {
54741
- if (buffer.length < 4 || buffer[0] !== 255 || buffer[1] !== 216) {
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 < buffer.length) {
54746
- if (buffer[offset] !== 255) {
54778
+ while (offset < buffer2.length) {
54779
+ if (buffer2[offset] !== 255) {
54747
54780
  offset += 1;
54748
54781
  continue;
54749
54782
  }
54750
- while (offset < buffer.length && buffer[offset] === 255) {
54783
+ while (offset < buffer2.length && buffer2[offset] === 255) {
54751
54784
  offset += 1;
54752
54785
  }
54753
- if (offset >= buffer.length) {
54786
+ if (offset >= buffer2.length) {
54754
54787
  return null;
54755
54788
  }
54756
- const marker = buffer[offset];
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 >= buffer.length) {
54794
+ if (offset + 1 >= buffer2.length) {
54762
54795
  return null;
54763
54796
  }
54764
- const segmentLength = buffer.readUInt16BE(offset);
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 < buffer.length) {
54769
- const height = buffer.readUInt16BE(offset + 3);
54770
- const width = buffer.readUInt16BE(offset + 5);
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(buffer, offset) {
54778
- return buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16;
54810
+ function readUInt24LE(buffer2, offset) {
54811
+ return buffer2[offset] | buffer2[offset + 1] << 8 | buffer2[offset + 2] << 16;
54779
54812
  }
54780
- function parseWebpDimensions(buffer) {
54781
- if (buffer.length < 16) {
54813
+ function parseWebpDimensions(buffer2) {
54814
+ if (buffer2.length < 16) {
54782
54815
  return null;
54783
54816
  }
54784
- if (buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WEBP") {
54817
+ if (buffer2.toString("ascii", 0, 4) !== "RIFF" || buffer2.toString("ascii", 8, 12) !== "WEBP") {
54785
54818
  return null;
54786
54819
  }
54787
- const chunkType = buffer.toString("ascii", 12, 16);
54820
+ const chunkType = buffer2.toString("ascii", 12, 16);
54788
54821
  if (chunkType === "VP8 ") {
54789
- if (buffer[23] !== 157 || buffer[24] !== 1 || buffer[25] !== 42) {
54822
+ if (buffer2[23] !== 157 || buffer2[24] !== 1 || buffer2[25] !== 42) {
54790
54823
  return null;
54791
54824
  }
54792
- const width = buffer.readUInt16LE(26) & 16383;
54793
- const height = buffer.readUInt16LE(28) & 16383;
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 (buffer.length < 25 || buffer[20] !== 47) {
54830
+ if (buffer2.length < 25 || buffer2[20] !== 47) {
54798
54831
  return null;
54799
54832
  }
54800
- const bits = buffer.readUInt32LE(21);
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(buffer, 24) + 1;
54807
- const height = readUInt24LE(buffer, 27) + 1;
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 buffer = Buffer.from(headerBase64, "base64");
54823
- if (buffer.length === 0) {
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(buffer);
54861
+ return parsePngDimensions(buffer2);
54829
54862
  }
54830
54863
  if (normalizedMime === "image/gif") {
54831
- return parseGifDimensions(buffer);
54864
+ return parseGifDimensions(buffer2);
54832
54865
  }
54833
54866
  if (normalizedMime === "image/jpeg" || normalizedMime === "image/jpg") {
54834
- return parseJpegDimensions(buffer);
54867
+ return parseJpegDimensions(buffer2);
54835
54868
  }
54836
54869
  if (normalizedMime === "image/webp") {
54837
- return parseWebpDimensions(buffer);
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 buffer = Buffer.from(cleanBase64, "base64");
73272
- writeFileSync19(inputPath, buffer);
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 = allTasks.find((t) => t.id === blockerId);
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.join(`
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.join(`
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.join(`
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 DEFAULT_CIRCUIT_BREAKER_WINDOW_SIZE = 20;
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 !== "idle";
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
- windowSize: config4?.circuitBreaker?.windowSize ?? DEFAULT_CIRCUIT_BREAKER_WINDOW_SIZE,
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
- const toolSignatures = [...previous, signature].slice(-settings.windowSize);
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
- toolSignatures,
78620
- windowSize: settings.windowSize,
78621
- thresholdPercent: settings.repetitionThresholdPercent
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.toolSignatures.length === 0) {
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: repeatedTool.split("::")[0],
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.includes(partInfo.id);
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
- task.progress.countedToolPartIDs = [...countedToolPartIDs, partInfo.id];
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: repetitive tool usage detected", {
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 repeatedly called ${loopDetection.toolName} ${loopDetection.repeatedCount}/${loopDetection.sampleSize} times in the recent tool-call window (${loopDetection.thresholdPercent}% threshold). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`
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 !== "idle") {
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 buffer = await (await crypto3).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
82711
- return btoa(String.fromCharCode(...new Uint8Array(buffer))).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
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": async (input, output) => {
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,