oh-my-opencode 3.12.1 → 3.12.2

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
  });
@@ -20394,15 +20436,18 @@ async function handleSessionIdle(args) {
20394
20436
  shouldSkipContinuation
20395
20437
  } = args;
20396
20438
  log(`[${HOOK_NAME}] session.idle`, { sessionID });
20439
+ console.error(`[TODO-DIAG] session.idle fired for ${sessionID}`);
20397
20440
  const state2 = sessionStateStore.getState(sessionID);
20398
20441
  if (state2.isRecovering) {
20399
20442
  log(`[${HOOK_NAME}] Skipped: in recovery`, { sessionID });
20443
+ console.error(`[TODO-DIAG] BLOCKED: isRecovering=true`);
20400
20444
  return;
20401
20445
  }
20402
20446
  if (state2.abortDetectedAt) {
20403
20447
  const timeSinceAbort = Date.now() - state2.abortDetectedAt;
20404
20448
  if (timeSinceAbort < ABORT_WINDOW_MS) {
20405
20449
  log(`[${HOOK_NAME}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
20450
+ console.error(`[TODO-DIAG] BLOCKED: abort detected ${timeSinceAbort}ms ago`);
20406
20451
  state2.abortDetectedAt = undefined;
20407
20452
  return;
20408
20453
  }
@@ -20411,6 +20456,7 @@ async function handleSessionIdle(args) {
20411
20456
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
20412
20457
  if (hasRunningBgTasks) {
20413
20458
  log(`[${HOOK_NAME}] Skipped: background tasks running`, { sessionID });
20459
+ console.error(`[TODO-DIAG] BLOCKED: background tasks running`, backgroundManager?.getTasksByParentSession(sessionID).filter((t) => t.status === "running").map((t) => t.id));
20414
20460
  return;
20415
20461
  }
20416
20462
  try {
@@ -20421,10 +20467,12 @@ async function handleSessionIdle(args) {
20421
20467
  const messages = normalizeSDKResponse(messagesResp, []);
20422
20468
  if (isLastAssistantMessageAborted(messages)) {
20423
20469
  log(`[${HOOK_NAME}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
20470
+ console.error(`[TODO-DIAG] BLOCKED: last assistant message aborted`);
20424
20471
  return;
20425
20472
  }
20426
20473
  if (hasUnansweredQuestion(messages)) {
20427
20474
  log(`[${HOOK_NAME}] Skipped: pending question awaiting user response`, { sessionID });
20475
+ console.error(`[TODO-DIAG] BLOCKED: hasUnansweredQuestion=true`);
20428
20476
  return;
20429
20477
  }
20430
20478
  } catch (error) {
@@ -20436,21 +20484,27 @@ async function handleSessionIdle(args) {
20436
20484
  todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
20437
20485
  } catch (error) {
20438
20486
  log(`[${HOOK_NAME}] Todo fetch failed`, { sessionID, error: String(error) });
20487
+ console.error(`[TODO-DIAG] BLOCKED: todo fetch failed`, String(error));
20439
20488
  return;
20440
20489
  }
20441
20490
  if (!todos || todos.length === 0) {
20491
+ sessionStateStore.resetContinuationProgress(sessionID);
20442
20492
  sessionStateStore.resetContinuationProgress(sessionID);
20443
20493
  log(`[${HOOK_NAME}] No todos`, { sessionID });
20494
+ console.error(`[TODO-DIAG] BLOCKED: no todos`);
20444
20495
  return;
20445
20496
  }
20446
20497
  const incompleteCount = getIncompleteCount(todos);
20447
20498
  if (incompleteCount === 0) {
20499
+ sessionStateStore.resetContinuationProgress(sessionID);
20448
20500
  sessionStateStore.resetContinuationProgress(sessionID);
20449
20501
  log(`[${HOOK_NAME}] All todos complete`, { sessionID, total: todos.length });
20502
+ console.error(`[TODO-DIAG] BLOCKED: all todos complete (${todos.length})`);
20450
20503
  return;
20451
20504
  }
20452
20505
  if (state2.inFlight) {
20453
20506
  log(`[${HOOK_NAME}] Skipped: injection in flight`, { sessionID });
20507
+ console.error(`[TODO-DIAG] BLOCKED: inFlight=true`);
20454
20508
  return;
20455
20509
  }
20456
20510
  if (state2.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES && state2.lastInjectedAt && Date.now() - state2.lastInjectedAt >= FAILURE_RESET_WINDOW_MS) {
@@ -20458,20 +20512,14 @@ async function handleSessionIdle(args) {
20458
20512
  log(`[${HOOK_NAME}] Reset consecutive failures after recovery window`, { sessionID, failureResetWindowMs: FAILURE_RESET_WINDOW_MS });
20459
20513
  }
20460
20514
  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
- });
20515
+ log(`[${HOOK_NAME}] Skipped: max consecutive failures reached`, { sessionID, consecutiveFailures: state2.consecutiveFailures });
20516
+ console.error(`[TODO-DIAG] BLOCKED: consecutiveFailures=${state2.consecutiveFailures} >= ${MAX_CONSECUTIVE_FAILURES}`);
20466
20517
  return;
20467
20518
  }
20468
20519
  const effectiveCooldown = CONTINUATION_COOLDOWN_MS * Math.pow(2, Math.min(state2.consecutiveFailures, 5));
20469
20520
  if (state2.lastInjectedAt && Date.now() - state2.lastInjectedAt < effectiveCooldown) {
20470
- log(`[${HOOK_NAME}] Skipped: cooldown active`, {
20471
- sessionID,
20472
- effectiveCooldown,
20473
- consecutiveFailures: state2.consecutiveFailures
20474
- });
20521
+ log(`[${HOOK_NAME}] Skipped: cooldown active`, { sessionID, effectiveCooldown, consecutiveFailures: state2.consecutiveFailures });
20522
+ console.error(`[TODO-DIAG] BLOCKED: cooldown active (${effectiveCooldown}ms, failures=${state2.consecutiveFailures})`);
20475
20523
  return;
20476
20524
  }
20477
20525
  let resolvedInfo;
@@ -20492,10 +20540,12 @@ async function handleSessionIdle(args) {
20492
20540
  const resolvedAgentName = resolvedInfo?.agent;
20493
20541
  if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
20494
20542
  log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
20543
+ console.error(`[TODO-DIAG] BLOCKED: agent '${resolvedAgentName}' in skipAgents`);
20495
20544
  return;
20496
20545
  }
20497
20546
  if ((compactionGuardActive || encounteredCompaction) && !resolvedInfo?.agent) {
20498
20547
  log(`[${HOOK_NAME}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
20548
+ console.error(`[TODO-DIAG] BLOCKED: compaction guard + no agent`);
20499
20549
  return;
20500
20550
  }
20501
20551
  if (state2.recentCompactionAt && resolvedInfo?.agent) {
@@ -20503,16 +20553,20 @@ async function handleSessionIdle(args) {
20503
20553
  }
20504
20554
  if (isContinuationStopped?.(sessionID)) {
20505
20555
  log(`[${HOOK_NAME}] Skipped: continuation stopped for session`, { sessionID });
20556
+ console.error(`[TODO-DIAG] BLOCKED: isContinuationStopped=true`);
20506
20557
  return;
20507
20558
  }
20508
20559
  if (shouldSkipContinuation?.(sessionID)) {
20509
20560
  log(`[${HOOK_NAME}] Skipped: another continuation hook already injected`, { sessionID });
20561
+ console.error(`[TODO-DIAG] BLOCKED: shouldSkipContinuation=true (gptPermissionContinuation recently injected)`);
20510
20562
  return;
20511
20563
  }
20512
20564
  const progressUpdate = sessionStateStore.trackContinuationProgress(sessionID, incompleteCount, todos);
20513
20565
  if (shouldStopForStagnation({ sessionID, incompleteCount, progressUpdate })) {
20566
+ console.error(`[TODO-DIAG] BLOCKED: stagnation detected (count=${progressUpdate.stagnationCount})`);
20514
20567
  return;
20515
20568
  }
20569
+ console.error(`[TODO-DIAG] PASSED all gates! Starting countdown (${incompleteCount}/${todos.length} incomplete)`);
20516
20570
  startCountdown({
20517
20571
  ctx,
20518
20572
  sessionID,
@@ -20603,6 +20657,9 @@ function createTodoContinuationHandler(args) {
20603
20657
  } = args;
20604
20658
  return async ({ event }) => {
20605
20659
  const props = event.properties;
20660
+ if (event.type === "session.idle") {
20661
+ console.error(`[TODO-DIAG] handler received session.idle event`, { sessionID: props?.sessionID });
20662
+ }
20606
20663
  if (event.type === "session.error") {
20607
20664
  const sessionID = props?.sessionID;
20608
20665
  if (!sessionID)
@@ -36324,6 +36381,15 @@ function takePendingCall(callID) {
36324
36381
  import * as fs6 from "fs";
36325
36382
  import { tmpdir as tmpdir4 } from "os";
36326
36383
  import { join as join30 } from "path";
36384
+ var ApplyPatchMetadataSchema = zod_default.object({
36385
+ files: zod_default.array(zod_default.object({
36386
+ filePath: zod_default.string(),
36387
+ movePath: zod_default.string().optional(),
36388
+ before: zod_default.string(),
36389
+ after: zod_default.string(),
36390
+ type: zod_default.string().optional()
36391
+ }))
36392
+ });
36327
36393
  var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
36328
36394
  var DEBUG_FILE3 = join30(tmpdir4(), "comment-checker-debug.log");
36329
36395
  function debugLog3(...args) {
@@ -36384,15 +36450,6 @@ function createCommentCheckerHooks(config2) {
36384
36450
  debugLog3("skipping due to tool failure in output");
36385
36451
  return;
36386
36452
  }
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
36453
  if (toolLower === "apply_patch") {
36397
36454
  const parsed = ApplyPatchMetadataSchema.safeParse(output.metadata);
36398
36455
  if (!parsed.success) {
@@ -36863,7 +36920,7 @@ function isTokenLimitError(text) {
36863
36920
  return false;
36864
36921
  }
36865
36922
  const lower = text.toLowerCase();
36866
- return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw.toLowerCase()));
36923
+ return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw));
36867
36924
  }
36868
36925
  function parseAnthropicTokenLimitError(err) {
36869
36926
  try {
@@ -38281,7 +38338,6 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
38281
38338
  };
38282
38339
  }
38283
38340
  // src/hooks/think-mode/detector.ts
38284
- var ENGLISH_PATTERNS = [/\bultrathink\b/i, /\bthink\b/i];
38285
38341
  var MULTILINGUAL_KEYWORDS = [
38286
38342
  "\uC0DD\uAC01",
38287
38343
  "\uAC80\uD1A0",
@@ -38367,8 +38423,7 @@ var MULTILINGUAL_KEYWORDS = [
38367
38423
  "fikir",
38368
38424
  "berfikir"
38369
38425
  ];
38370
- var MULTILINGUAL_PATTERNS = MULTILINGUAL_KEYWORDS.map((kw) => new RegExp(kw, "i"));
38371
- var THINK_PATTERNS = [...ENGLISH_PATTERNS, ...MULTILINGUAL_PATTERNS];
38426
+ var COMBINED_THINK_PATTERN = new RegExp(`\\b(?:ultrathink|think)\\b|${MULTILINGUAL_KEYWORDS.join("|")}`, "i");
38372
38427
  var CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
38373
38428
  var INLINE_CODE_PATTERN = /`[^`]+`/g;
38374
38429
  function removeCodeBlocks(text) {
@@ -38376,7 +38431,7 @@ function removeCodeBlocks(text) {
38376
38431
  }
38377
38432
  function detectThinkKeyword(text) {
38378
38433
  const textWithoutCode = removeCodeBlocks(text);
38379
- return THINK_PATTERNS.some((pattern) => pattern.test(textWithoutCode));
38434
+ return COMBINED_THINK_PATTERN.test(textWithoutCode);
38380
38435
  }
38381
38436
  function extractPromptText(parts) {
38382
38437
  return parts.filter((p) => p.type === "text").map((p) => p.text || "").join("");
@@ -39033,16 +39088,16 @@ async function loadPluginExtendedConfig() {
39033
39088
  }
39034
39089
  return merged;
39035
39090
  }
39036
- var regexCache = new Map;
39091
+ var regexCache2 = new Map;
39037
39092
  function getRegex(pattern) {
39038
- let regex = regexCache.get(pattern);
39093
+ let regex = regexCache2.get(pattern);
39039
39094
  if (!regex) {
39040
39095
  try {
39041
39096
  regex = new RegExp(pattern);
39042
- regexCache.set(pattern, regex);
39097
+ regexCache2.set(pattern, regex);
39043
39098
  } catch {
39044
39099
  regex = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
39045
- regexCache.set(pattern, regex);
39100
+ regexCache2.set(pattern, regex);
39046
39101
  }
39047
39102
  }
39048
39103
  return regex;
@@ -39908,8 +39963,6 @@ function createToolExecuteAfterHandler(ctx, config2) {
39908
39963
  if (!output) {
39909
39964
  return;
39910
39965
  }
39911
- const claudeConfig = await loadClaudeHooksConfig();
39912
- const extendedConfig = await loadPluginExtendedConfig();
39913
39966
  const cachedInput = getToolInput(input.sessionID, input.tool, input.callID) || {};
39914
39967
  appendTranscriptEntry(input.sessionID, {
39915
39968
  type: "tool_result",
@@ -39921,6 +39974,8 @@ function createToolExecuteAfterHandler(ctx, config2) {
39921
39974
  if (isHookDisabled(config2, "PostToolUse")) {
39922
39975
  return;
39923
39976
  }
39977
+ const claudeConfig = await loadClaudeHooksConfig();
39978
+ const extendedConfig = await loadPluginExtendedConfig();
39924
39979
  const postClient = {
39925
39980
  session: {
39926
39981
  messages: (opts) => ctx.client.session.messages(opts)
@@ -40101,8 +40156,6 @@ function createToolExecuteBeforeHandler(ctx, config2) {
40101
40156
  output.args.todos = parsed;
40102
40157
  log("todowrite: parsed todos string to array", { sessionID: input.sessionID });
40103
40158
  }
40104
- const claudeConfig = await loadClaudeHooksConfig();
40105
- const extendedConfig = await loadPluginExtendedConfig();
40106
40159
  appendTranscriptEntry(input.sessionID, {
40107
40160
  type: "tool_use",
40108
40161
  timestamp: new Date().toISOString(),
@@ -40113,6 +40166,8 @@ function createToolExecuteBeforeHandler(ctx, config2) {
40113
40166
  if (isHookDisabled(config2, "PreToolUse")) {
40114
40167
  return;
40115
40168
  }
40169
+ const claudeConfig = await loadClaudeHooksConfig();
40170
+ const extendedConfig = await loadPluginExtendedConfig();
40116
40171
  const preCtx = {
40117
40172
  sessionId: input.sessionID,
40118
40173
  toolName: input.tool,
@@ -47544,8 +47599,7 @@ var BabysittingConfigSchema = exports_external.object({
47544
47599
  var CircuitBreakerConfigSchema = exports_external.object({
47545
47600
  enabled: exports_external.boolean().optional(),
47546
47601
  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()
47602
+ consecutiveThreshold: exports_external.number().int().min(5).optional()
47549
47603
  });
47550
47604
  var BackgroundTaskConfigSchema = exports_external.object({
47551
47605
  defaultConcurrency: exports_external.number().min(1).optional(),
@@ -49640,9 +49694,6 @@ function skillToCommandInfo(skill) {
49640
49694
  lazyContentLoader: skill.lazyContent
49641
49695
  };
49642
49696
  }
49643
- function filterDiscoveredCommandsByScope(commands3, scope) {
49644
- return commands3.filter((command) => command.scope === scope);
49645
- }
49646
49697
  async function discoverAllCommands(options) {
49647
49698
  const discoveredCommands = discoverCommandsSync(process.cwd(), {
49648
49699
  pluginsEnabled: options?.pluginsEnabled,
@@ -49650,14 +49701,17 @@ async function discoverAllCommands(options) {
49650
49701
  });
49651
49702
  const skills2 = options?.skills ?? await discoverAllSkills();
49652
49703
  const skillCommands = skills2.map(skillToCommandInfo);
49704
+ const scopeOrder = ["project", "user", "opencode-project", "opencode", "builtin", "plugin"];
49705
+ const grouped = new Map;
49706
+ for (const cmd of discoveredCommands) {
49707
+ const list = grouped.get(cmd.scope) ?? [];
49708
+ list.push(cmd);
49709
+ grouped.set(cmd.scope, list);
49710
+ }
49711
+ const orderedCommands = scopeOrder.flatMap((scope) => grouped.get(scope) ?? []);
49653
49712
  return [
49654
49713
  ...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")
49714
+ ...orderedCommands
49661
49715
  ];
49662
49716
  }
49663
49717
  async function findCommand2(commandName, options) {
@@ -53418,6 +53472,7 @@ function getErrorMessage2(error48) {
53418
53472
  return "";
53419
53473
  }
53420
53474
  }
53475
+ var DEFAULT_RETRY_PATTERN = new RegExp(`\\b(${DEFAULT_CONFIG2.retry_on_errors.join("|")})\\b`);
53421
53476
  function extractStatusCode(error48, retryOnErrors) {
53422
53477
  if (!error48)
53423
53478
  return;
@@ -53432,8 +53487,7 @@ function extractStatusCode(error48, retryOnErrors) {
53432
53487
  if (statusCode !== undefined) {
53433
53488
  return statusCode;
53434
53489
  }
53435
- const codes = retryOnErrors ?? DEFAULT_CONFIG2.retry_on_errors;
53436
- const pattern = new RegExp(`\\b(${codes.join("|")})\\b`);
53490
+ const pattern = retryOnErrors ? new RegExp(`\\b(${retryOnErrors.join("|")})\\b`) : DEFAULT_RETRY_PATTERN;
53437
53491
  const message = getErrorMessage2(error48);
53438
53492
  const statusMatch = message.match(pattern);
53439
53493
  if (statusMatch) {
@@ -54714,97 +54768,97 @@ function toImageDimensions(width, height) {
54714
54768
  }
54715
54769
  return { width, height };
54716
54770
  }
54717
- function parsePngDimensions(buffer) {
54718
- if (buffer.length < 24) {
54771
+ function parsePngDimensions(buffer2) {
54772
+ if (buffer2.length < 24) {
54719
54773
  return null;
54720
54774
  }
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") {
54775
+ 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;
54776
+ if (!isPngSignature || buffer2.toString("ascii", 12, 16) !== "IHDR") {
54723
54777
  return null;
54724
54778
  }
54725
- const width = buffer.readUInt32BE(16);
54726
- const height = buffer.readUInt32BE(20);
54779
+ const width = buffer2.readUInt32BE(16);
54780
+ const height = buffer2.readUInt32BE(20);
54727
54781
  return toImageDimensions(width, height);
54728
54782
  }
54729
- function parseGifDimensions(buffer) {
54730
- if (buffer.length < 10) {
54783
+ function parseGifDimensions(buffer2) {
54784
+ if (buffer2.length < 10) {
54731
54785
  return null;
54732
54786
  }
54733
- if (buffer.toString("ascii", 0, 4) !== "GIF8") {
54787
+ if (buffer2.toString("ascii", 0, 4) !== "GIF8") {
54734
54788
  return null;
54735
54789
  }
54736
- const width = buffer.readUInt16LE(6);
54737
- const height = buffer.readUInt16LE(8);
54790
+ const width = buffer2.readUInt16LE(6);
54791
+ const height = buffer2.readUInt16LE(8);
54738
54792
  return toImageDimensions(width, height);
54739
54793
  }
54740
- function parseJpegDimensions(buffer) {
54741
- if (buffer.length < 4 || buffer[0] !== 255 || buffer[1] !== 216) {
54794
+ function parseJpegDimensions(buffer2) {
54795
+ if (buffer2.length < 4 || buffer2[0] !== 255 || buffer2[1] !== 216) {
54742
54796
  return null;
54743
54797
  }
54744
54798
  let offset = 2;
54745
- while (offset < buffer.length) {
54746
- if (buffer[offset] !== 255) {
54799
+ while (offset < buffer2.length) {
54800
+ if (buffer2[offset] !== 255) {
54747
54801
  offset += 1;
54748
54802
  continue;
54749
54803
  }
54750
- while (offset < buffer.length && buffer[offset] === 255) {
54804
+ while (offset < buffer2.length && buffer2[offset] === 255) {
54751
54805
  offset += 1;
54752
54806
  }
54753
- if (offset >= buffer.length) {
54807
+ if (offset >= buffer2.length) {
54754
54808
  return null;
54755
54809
  }
54756
- const marker = buffer[offset];
54810
+ const marker = buffer2[offset];
54757
54811
  offset += 1;
54758
54812
  if (marker === 217 || marker === 218) {
54759
54813
  break;
54760
54814
  }
54761
- if (offset + 1 >= buffer.length) {
54815
+ if (offset + 1 >= buffer2.length) {
54762
54816
  return null;
54763
54817
  }
54764
- const segmentLength = buffer.readUInt16BE(offset);
54818
+ const segmentLength = buffer2.readUInt16BE(offset);
54765
54819
  if (segmentLength < 2) {
54766
54820
  return null;
54767
54821
  }
54768
- if ((marker === 192 || marker === 194) && offset + 7 < buffer.length) {
54769
- const height = buffer.readUInt16BE(offset + 3);
54770
- const width = buffer.readUInt16BE(offset + 5);
54822
+ if ((marker === 192 || marker === 194) && offset + 7 < buffer2.length) {
54823
+ const height = buffer2.readUInt16BE(offset + 3);
54824
+ const width = buffer2.readUInt16BE(offset + 5);
54771
54825
  return toImageDimensions(width, height);
54772
54826
  }
54773
54827
  offset += segmentLength;
54774
54828
  }
54775
54829
  return null;
54776
54830
  }
54777
- function readUInt24LE(buffer, offset) {
54778
- return buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16;
54831
+ function readUInt24LE(buffer2, offset) {
54832
+ return buffer2[offset] | buffer2[offset + 1] << 8 | buffer2[offset + 2] << 16;
54779
54833
  }
54780
- function parseWebpDimensions(buffer) {
54781
- if (buffer.length < 16) {
54834
+ function parseWebpDimensions(buffer2) {
54835
+ if (buffer2.length < 16) {
54782
54836
  return null;
54783
54837
  }
54784
- if (buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WEBP") {
54838
+ if (buffer2.toString("ascii", 0, 4) !== "RIFF" || buffer2.toString("ascii", 8, 12) !== "WEBP") {
54785
54839
  return null;
54786
54840
  }
54787
- const chunkType = buffer.toString("ascii", 12, 16);
54841
+ const chunkType = buffer2.toString("ascii", 12, 16);
54788
54842
  if (chunkType === "VP8 ") {
54789
- if (buffer[23] !== 157 || buffer[24] !== 1 || buffer[25] !== 42) {
54843
+ if (buffer2[23] !== 157 || buffer2[24] !== 1 || buffer2[25] !== 42) {
54790
54844
  return null;
54791
54845
  }
54792
- const width = buffer.readUInt16LE(26) & 16383;
54793
- const height = buffer.readUInt16LE(28) & 16383;
54846
+ const width = buffer2.readUInt16LE(26) & 16383;
54847
+ const height = buffer2.readUInt16LE(28) & 16383;
54794
54848
  return toImageDimensions(width, height);
54795
54849
  }
54796
54850
  if (chunkType === "VP8L") {
54797
- if (buffer.length < 25 || buffer[20] !== 47) {
54851
+ if (buffer2.length < 25 || buffer2[20] !== 47) {
54798
54852
  return null;
54799
54853
  }
54800
- const bits = buffer.readUInt32LE(21);
54854
+ const bits = buffer2.readUInt32LE(21);
54801
54855
  const width = (bits & 16383) + 1;
54802
54856
  const height = (bits >>> 14 & 16383) + 1;
54803
54857
  return toImageDimensions(width, height);
54804
54858
  }
54805
54859
  if (chunkType === "VP8X") {
54806
- const width = readUInt24LE(buffer, 24) + 1;
54807
- const height = readUInt24LE(buffer, 27) + 1;
54860
+ const width = readUInt24LE(buffer2, 24) + 1;
54861
+ const height = readUInt24LE(buffer2, 27) + 1;
54808
54862
  return toImageDimensions(width, height);
54809
54863
  }
54810
54864
  return null;
@@ -54819,22 +54873,22 @@ function parseImageDimensions(base64DataUrl, mimeType) {
54819
54873
  return null;
54820
54874
  }
54821
54875
  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) {
54876
+ const buffer2 = Buffer.from(headerBase64, "base64");
54877
+ if (buffer2.length === 0) {
54824
54878
  return null;
54825
54879
  }
54826
54880
  const normalizedMime = mimeType.toLowerCase();
54827
54881
  if (normalizedMime === "image/png") {
54828
- return parsePngDimensions(buffer);
54882
+ return parsePngDimensions(buffer2);
54829
54883
  }
54830
54884
  if (normalizedMime === "image/gif") {
54831
- return parseGifDimensions(buffer);
54885
+ return parseGifDimensions(buffer2);
54832
54886
  }
54833
54887
  if (normalizedMime === "image/jpeg" || normalizedMime === "image/jpg") {
54834
- return parseJpegDimensions(buffer);
54888
+ return parseJpegDimensions(buffer2);
54835
54889
  }
54836
54890
  if (normalizedMime === "image/webp") {
54837
- return parseWebpDimensions(buffer);
54891
+ return parseWebpDimensions(buffer2);
54838
54892
  }
54839
54893
  return null;
54840
54894
  } catch {
@@ -73268,8 +73322,8 @@ function convertBase64ImageToJpeg(base64Data, mimeType) {
73268
73322
  const tempFiles = [inputPath];
73269
73323
  try {
73270
73324
  const cleanBase64 = base64Data.replace(/^data:[^;]+;base64,/, "");
73271
- const buffer = Buffer.from(cleanBase64, "base64");
73272
- writeFileSync19(inputPath, buffer);
73325
+ const buffer2 = Buffer.from(cleanBase64, "base64");
73326
+ writeFileSync19(inputPath, buffer2);
73273
73327
  log(`[image-converter] Converting Base64 ${mimeType} to JPEG`);
73274
73328
  const outputPath = convertImageToJpeg(inputPath, mimeType);
73275
73329
  tempFiles.push(outputPath);
@@ -75738,10 +75792,11 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
75738
75792
  allTasks.push(task);
75739
75793
  }
75740
75794
  }
75795
+ const taskMap = new Map(allTasks.map((t) => [t.id, t]));
75741
75796
  const activeTasks = allTasks.filter((task) => task.status !== "completed" && task.status !== "deleted");
75742
75797
  const summaries = activeTasks.map((task) => {
75743
75798
  const unresolvedBlockers = task.blockedBy.filter((blockerId) => {
75744
- const blockerTask = allTasks.find((t) => t.id === blockerId);
75799
+ const blockerTask = taskMap.get(blockerId);
75745
75800
  return !blockerTask || blockerTask.status !== "completed";
75746
75801
  });
75747
75802
  return {
@@ -76430,6 +76485,15 @@ function applyPrepend(lines, text) {
76430
76485
  }
76431
76486
 
76432
76487
  // src/tools/hashline-edit/edit-operations.ts
76488
+ function arraysEqual(a, b) {
76489
+ if (a.length !== b.length)
76490
+ return false;
76491
+ for (let i2 = 0;i2 < a.length; i2++) {
76492
+ if (a[i2] !== b[i2])
76493
+ return false;
76494
+ }
76495
+ return true;
76496
+ }
76433
76497
  function applyHashlineEditsWithReport(content, edits) {
76434
76498
  if (edits.length === 0) {
76435
76499
  return {
@@ -76459,9 +76523,7 @@ function applyHashlineEditsWithReport(content, edits) {
76459
76523
  switch (edit.op) {
76460
76524
  case "replace": {
76461
76525
  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
- `)) {
76526
+ if (arraysEqual(next, lines)) {
76465
76527
  noopEdits += 1;
76466
76528
  break;
76467
76529
  }
@@ -76470,9 +76532,7 @@ function applyHashlineEditsWithReport(content, edits) {
76470
76532
  }
76471
76533
  case "append": {
76472
76534
  const next = edit.pos ? applyInsertAfter(lines, edit.pos, edit.lines, { skipValidation: true }) : applyAppend(lines, edit.lines);
76473
- if (next.join(`
76474
- `) === lines.join(`
76475
- `)) {
76535
+ if (arraysEqual(next, lines)) {
76476
76536
  noopEdits += 1;
76477
76537
  break;
76478
76538
  }
@@ -76481,9 +76541,7 @@ function applyHashlineEditsWithReport(content, edits) {
76481
76541
  }
76482
76542
  case "prepend": {
76483
76543
  const next = edit.pos ? applyInsertBefore(lines, edit.pos, edit.lines, { skipValidation: true }) : applyPrepend(lines, edit.lines);
76484
- if (next.join(`
76485
- `) === lines.join(`
76486
- `)) {
76544
+ if (arraysEqual(next, lines)) {
76487
76545
  noopEdits += 1;
76488
76546
  break;
76489
76547
  }
@@ -78048,8 +78106,7 @@ var MIN_STABILITY_TIME_MS2 = 10 * 1000;
78048
78106
  var DEFAULT_STALE_TIMEOUT_MS = 1200000;
78049
78107
  var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
78050
78108
  var DEFAULT_MAX_TOOL_CALLS = 200;
78051
- var DEFAULT_CIRCUIT_BREAKER_WINDOW_SIZE = 20;
78052
- var DEFAULT_CIRCUIT_BREAKER_REPETITION_THRESHOLD_PERCENT = 80;
78109
+ var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
78053
78110
  var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
78054
78111
  var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
78055
78112
  var MIN_IDLE_TIME_MS = 5000;
@@ -78471,6 +78528,22 @@ function removeTaskToastTracking(taskId) {
78471
78528
  }
78472
78529
  }
78473
78530
 
78531
+ // src/features/background-agent/session-status-classifier.ts
78532
+ var ACTIVE_SESSION_STATUSES = new Set(["busy", "retry", "running"]);
78533
+ var KNOWN_TERMINAL_STATUSES = new Set(["idle", "interrupted"]);
78534
+ function isActiveSessionStatus(type2) {
78535
+ if (ACTIVE_SESSION_STATUSES.has(type2)) {
78536
+ return true;
78537
+ }
78538
+ if (!KNOWN_TERMINAL_STATUSES.has(type2)) {
78539
+ log("[background-agent] Unknown session status type encountered:", type2);
78540
+ }
78541
+ return false;
78542
+ }
78543
+ function isTerminalSessionStatus(type2) {
78544
+ return KNOWN_TERMINAL_STATUSES.has(type2) && type2 !== "idle";
78545
+ }
78546
+
78474
78547
  // src/features/background-agent/task-poller.ts
78475
78548
  var TERMINAL_TASK_STATUSES = new Set([
78476
78549
  "completed",
@@ -78549,7 +78622,7 @@ async function checkAndInterruptStaleTasks(args) {
78549
78622
  if (!startedAt || !sessionID)
78550
78623
  continue;
78551
78624
  const sessionStatus = sessionStatuses?.[sessionID]?.type;
78552
- const sessionIsRunning = sessionStatus !== undefined && sessionStatus !== "idle";
78625
+ const sessionIsRunning = sessionStatus !== undefined && isActiveSessionStatus(sessionStatus);
78553
78626
  const runtime = now - startedAt.getTime();
78554
78627
  if (!task.progress?.lastUpdate) {
78555
78628
  if (sessionIsRunning)
@@ -78607,18 +78680,22 @@ function resolveCircuitBreakerSettings(config4) {
78607
78680
  return {
78608
78681
  enabled: config4?.circuitBreaker?.enabled ?? DEFAULT_CIRCUIT_BREAKER_ENABLED,
78609
78682
  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
78683
+ consecutiveThreshold: config4?.circuitBreaker?.consecutiveThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD
78612
78684
  };
78613
78685
  }
78614
78686
  function recordToolCall(window, toolName, settings, toolInput) {
78615
- const previous = window?.toolSignatures ?? [];
78616
78687
  const signature = createToolCallSignature(toolName, toolInput);
78617
- const toolSignatures = [...previous, signature].slice(-settings.windowSize);
78688
+ if (window && window.lastSignature === signature) {
78689
+ return {
78690
+ lastSignature: signature,
78691
+ consecutiveCount: window.consecutiveCount + 1,
78692
+ threshold: settings.consecutiveThreshold
78693
+ };
78694
+ }
78618
78695
  return {
78619
- toolSignatures,
78620
- windowSize: settings.windowSize,
78621
- thresholdPercent: settings.repetitionThresholdPercent
78696
+ lastSignature: signature,
78697
+ consecutiveCount: 1,
78698
+ threshold: settings.consecutiveThreshold
78622
78699
  };
78623
78700
  }
78624
78701
  function sortObject2(obj) {
@@ -78645,36 +78722,13 @@ function createToolCallSignature(toolName, toolInput) {
78645
78722
  return `${toolName}::${JSON.stringify(sortObject2(toolInput))}`;
78646
78723
  }
78647
78724
  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) {
78725
+ if (!window || window.consecutiveCount < window.threshold) {
78670
78726
  return { triggered: false };
78671
78727
  }
78672
78728
  return {
78673
78729
  triggered: true,
78674
- toolName: repeatedTool.split("::")[0],
78675
- repeatedCount,
78676
- sampleSize,
78677
- thresholdPercent: window.thresholdPercent
78730
+ toolName: window.lastSignature.split("::")[0],
78731
+ repeatedCount: window.consecutiveCount
78678
78732
  };
78679
78733
  }
78680
78734
 
@@ -78773,6 +78827,7 @@ class BackgroundManager {
78773
78827
  preStartDescendantReservations;
78774
78828
  enableParentSessionNotifications;
78775
78829
  taskHistory = new TaskHistory;
78830
+ cachedCircuitBreakerSettings;
78776
78831
  constructor(ctx, config4, options) {
78777
78832
  this.tasks = new Map;
78778
78833
  this.notifications = new Map;
@@ -79365,34 +79420,33 @@ class BackgroundManager {
79365
79420
  }
79366
79421
  task.progress.lastUpdate = new Date;
79367
79422
  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);
79423
+ const countedToolPartIDs = task.progress.countedToolPartIDs ?? new Set;
79424
+ const shouldCountToolCall = !partInfo.id || partInfo.state?.status !== "running" || !countedToolPartIDs.has(partInfo.id);
79370
79425
  if (!shouldCountToolCall) {
79371
79426
  return;
79372
79427
  }
79373
79428
  if (partInfo.id && partInfo.state?.status === "running") {
79374
- task.progress.countedToolPartIDs = [...countedToolPartIDs, partInfo.id];
79429
+ countedToolPartIDs.add(partInfo.id);
79430
+ task.progress.countedToolPartIDs = countedToolPartIDs;
79375
79431
  }
79376
79432
  task.progress.toolCalls += 1;
79377
79433
  task.progress.lastTool = partInfo.tool;
79378
- const circuitBreaker = resolveCircuitBreakerSettings(this.config);
79434
+ const circuitBreaker = this.cachedCircuitBreakerSettings ?? (this.cachedCircuitBreakerSettings = resolveCircuitBreakerSettings(this.config));
79379
79435
  if (partInfo.tool) {
79380
79436
  task.progress.toolCallWindow = recordToolCall(task.progress.toolCallWindow, partInfo.tool, circuitBreaker, partInfo.state?.input);
79381
79437
  if (circuitBreaker.enabled) {
79382
79438
  const loopDetection = detectRepetitiveToolUse(task.progress.toolCallWindow);
79383
79439
  if (loopDetection.triggered) {
79384
- log("[background-agent] Circuit breaker: repetitive tool usage detected", {
79440
+ log("[background-agent] Circuit breaker: consecutive tool usage detected", {
79385
79441
  taskId: task.id,
79386
79442
  agent: task.agent,
79387
79443
  sessionID,
79388
79444
  toolName: loopDetection.toolName,
79389
- repeatedCount: loopDetection.repeatedCount,
79390
- sampleSize: loopDetection.sampleSize,
79391
- thresholdPercent: loopDetection.thresholdPercent
79445
+ repeatedCount: loopDetection.repeatedCount
79392
79446
  });
79393
79447
  this.cancelTask(task.id, {
79394
79448
  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.`
79449
+ 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
79450
  });
79397
79451
  return;
79398
79452
  }
@@ -80042,7 +80096,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
80042
80096
  continue;
80043
80097
  }
80044
80098
  }
80045
- if (sessionStatus && sessionStatus.type !== "idle") {
80099
+ if (sessionStatus && isActiveSessionStatus(sessionStatus.type)) {
80046
80100
  log("[background-agent] Session still running, relying on event-based progress:", {
80047
80101
  taskId: task.id,
80048
80102
  sessionID,
@@ -80051,6 +80105,17 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
80051
80105
  });
80052
80106
  continue;
80053
80107
  }
80108
+ if (sessionStatus && isTerminalSessionStatus(sessionStatus.type)) {
80109
+ await this.tryCompleteTask(task, `polling (terminal session status: ${sessionStatus.type})`);
80110
+ continue;
80111
+ }
80112
+ if (sessionStatus && sessionStatus.type !== "idle") {
80113
+ log("[background-agent] Unknown session status, treating as potentially idle:", {
80114
+ taskId: task.id,
80115
+ sessionID,
80116
+ sessionStatus: sessionStatus.type
80117
+ });
80118
+ }
80054
80119
  const completionSource = sessionStatus?.type === "idle" ? "polling (idle status)" : "polling (session gone from status)";
80055
80120
  const hasValidOutput = await this.validateSessionHasOutput(sessionID);
80056
80121
  if (!hasValidOutput) {
@@ -82707,8 +82772,8 @@ async function generateVerifier(length) {
82707
82772
  return await random(length);
82708
82773
  }
82709
82774
  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, "");
82775
+ const buffer2 = await (await crypto3).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
82776
+ return btoa(String.fromCharCode(...new Uint8Array(buffer2))).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
82712
82777
  }
82713
82778
  async function pkceChallenge(length) {
82714
82779
  if (!length)
@@ -97809,10 +97874,7 @@ function createPluginInterface(args) {
97809
97874
  const { ctx, pluginConfig, firstMessageVariantGate, managers, hooks: hooks2, tools } = args;
97810
97875
  return {
97811
97876
  tool: tools,
97812
- "chat.params": async (input, output) => {
97813
- const handler = createChatParamsHandler({ anthropicEffort: hooks2.anthropicEffort });
97814
- await handler(input, output);
97815
- },
97877
+ "chat.params": createChatParamsHandler({ anthropicEffort: hooks2.anthropicEffort }),
97816
97878
  "chat.headers": createChatHeadersHandler({ ctx }),
97817
97879
  "chat.message": createChatMessageHandler3({
97818
97880
  ctx,