evil-omo 3.12.2 → 3.12.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -8967,7 +8967,7 @@ var {
8967
8967
  // package.json
8968
8968
  var package_default = {
8969
8969
  name: "evil-omo",
8970
- version: "3.12.2",
8970
+ version: "3.12.4",
8971
8971
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
8972
8972
  main: "dist/index.js",
8973
8973
  types: "dist/index.d.ts",
@@ -9043,17 +9043,17 @@ var package_default = {
9043
9043
  typescript: "^5.7.3"
9044
9044
  },
9045
9045
  optionalDependencies: {
9046
- "evil-omo-darwin-arm64": "3.12.2",
9047
- "evil-omo-darwin-x64": "3.12.2",
9048
- "evil-omo-darwin-x64-baseline": "3.12.2",
9049
- "evil-omo-linux-x64": "3.12.2",
9050
- "evil-omo-linux-x64-baseline": "3.12.2",
9051
- "evil-omo-linux-arm64": "3.12.2",
9052
- "evil-omo-linux-x64-musl": "3.12.2",
9053
- "evil-omo-linux-x64-musl-baseline": "3.12.2",
9054
- "evil-omo-linux-arm64-musl": "3.12.2",
9055
- "evil-omo-windows-x64": "3.12.2",
9056
- "evil-omo-windows-x64-baseline": "3.12.2"
9046
+ "evil-omo-darwin-arm64": "3.12.4",
9047
+ "evil-omo-darwin-x64": "3.12.4",
9048
+ "evil-omo-darwin-x64-baseline": "3.12.4",
9049
+ "evil-omo-linux-x64": "3.12.4",
9050
+ "evil-omo-linux-x64-baseline": "3.12.4",
9051
+ "evil-omo-linux-arm64": "3.12.4",
9052
+ "evil-omo-linux-x64-musl": "3.12.4",
9053
+ "evil-omo-linux-x64-musl-baseline": "3.12.4",
9054
+ "evil-omo-linux-arm64-musl": "3.12.4",
9055
+ "evil-omo-windows-x64": "3.12.4",
9056
+ "evil-omo-windows-x64-baseline": "3.12.4"
9057
9057
  },
9058
9058
  overrides: {
9059
9059
  "@opencode-ai/sdk": "^1.2.24"
@@ -5,9 +5,9 @@ export declare const TERMINAL_TASK_TTL_MS: number;
5
5
  export declare const MIN_STABILITY_TIME_MS: number;
6
6
  export declare const DEFAULT_STALE_TIMEOUT_MS = 1200000;
7
7
  export declare const DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
8
- export declare const DEFAULT_MAX_TOOL_CALLS = 200;
8
+ export declare const DEFAULT_MAX_TOOL_CALLS = 4000;
9
9
  export declare const DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
10
- export declare const DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
10
+ export declare const DEFAULT_CIRCUIT_BREAKER_ENABLED = false;
11
11
  export declare const MIN_RUNTIME_BEFORE_STALE_MS = 30000;
12
12
  export declare const MIN_IDLE_TIME_MS = 5000;
13
13
  export declare const POLLING_INTERVAL_MS = 3000;
package/dist/index.js CHANGED
@@ -20487,18 +20487,15 @@ async function handleSessionIdle(args) {
20487
20487
  shouldSkipContinuation
20488
20488
  } = args;
20489
20489
  log(`[${HOOK_NAME}] session.idle`, { sessionID });
20490
- console.error(`[TODO-DIAG] session.idle fired for ${sessionID}`);
20491
20490
  const state2 = sessionStateStore.getState(sessionID);
20492
20491
  if (state2.isRecovering) {
20493
20492
  log(`[${HOOK_NAME}] Skipped: in recovery`, { sessionID });
20494
- console.error(`[TODO-DIAG] BLOCKED: isRecovering=true`);
20495
20493
  return;
20496
20494
  }
20497
20495
  if (state2.abortDetectedAt) {
20498
20496
  const timeSinceAbort = Date.now() - state2.abortDetectedAt;
20499
20497
  if (timeSinceAbort < ABORT_WINDOW_MS) {
20500
20498
  log(`[${HOOK_NAME}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
20501
- console.error(`[TODO-DIAG] BLOCKED: abort detected ${timeSinceAbort}ms ago`);
20502
20499
  state2.abortDetectedAt = undefined;
20503
20500
  return;
20504
20501
  }
@@ -20507,7 +20504,6 @@ async function handleSessionIdle(args) {
20507
20504
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
20508
20505
  if (hasRunningBgTasks) {
20509
20506
  log(`[${HOOK_NAME}] Skipped: background tasks running`, { sessionID });
20510
- console.error(`[TODO-DIAG] BLOCKED: background tasks running`, backgroundManager?.getTasksByParentSession(sessionID).filter((t) => t.status === "running").map((t) => t.id));
20511
20507
  return;
20512
20508
  }
20513
20509
  try {
@@ -20518,12 +20514,10 @@ async function handleSessionIdle(args) {
20518
20514
  const messages = normalizeSDKResponse(messagesResp, []);
20519
20515
  if (isLastAssistantMessageAborted(messages)) {
20520
20516
  log(`[${HOOK_NAME}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
20521
- console.error(`[TODO-DIAG] BLOCKED: last assistant message aborted`);
20522
20517
  return;
20523
20518
  }
20524
20519
  if (hasUnansweredQuestion(messages)) {
20525
20520
  log(`[${HOOK_NAME}] Skipped: pending question awaiting user response`, { sessionID });
20526
- console.error(`[TODO-DIAG] BLOCKED: hasUnansweredQuestion=true`);
20527
20521
  return;
20528
20522
  }
20529
20523
  } catch (error) {
@@ -20535,14 +20529,12 @@ async function handleSessionIdle(args) {
20535
20529
  todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
20536
20530
  } catch (error) {
20537
20531
  log(`[${HOOK_NAME}] Todo fetch failed`, { sessionID, error: String(error) });
20538
- console.error(`[TODO-DIAG] BLOCKED: todo fetch failed`, String(error));
20539
20532
  return;
20540
20533
  }
20541
20534
  if (!todos || todos.length === 0) {
20542
20535
  sessionStateStore.resetContinuationProgress(sessionID);
20543
20536
  sessionStateStore.resetContinuationProgress(sessionID);
20544
20537
  log(`[${HOOK_NAME}] No todos`, { sessionID });
20545
- console.error(`[TODO-DIAG] BLOCKED: no todos`);
20546
20538
  return;
20547
20539
  }
20548
20540
  const incompleteCount = getIncompleteCount(todos);
@@ -20550,12 +20542,10 @@ async function handleSessionIdle(args) {
20550
20542
  sessionStateStore.resetContinuationProgress(sessionID);
20551
20543
  sessionStateStore.resetContinuationProgress(sessionID);
20552
20544
  log(`[${HOOK_NAME}] All todos complete`, { sessionID, total: todos.length });
20553
- console.error(`[TODO-DIAG] BLOCKED: all todos complete (${todos.length})`);
20554
20545
  return;
20555
20546
  }
20556
20547
  if (state2.inFlight) {
20557
20548
  log(`[${HOOK_NAME}] Skipped: injection in flight`, { sessionID });
20558
- console.error(`[TODO-DIAG] BLOCKED: inFlight=true`);
20559
20549
  return;
20560
20550
  }
20561
20551
  if (state2.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES && state2.lastInjectedAt && Date.now() - state2.lastInjectedAt >= FAILURE_RESET_WINDOW_MS) {
@@ -20564,13 +20554,11 @@ async function handleSessionIdle(args) {
20564
20554
  }
20565
20555
  if (state2.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
20566
20556
  log(`[${HOOK_NAME}] Skipped: max consecutive failures reached`, { sessionID, consecutiveFailures: state2.consecutiveFailures });
20567
- console.error(`[TODO-DIAG] BLOCKED: consecutiveFailures=${state2.consecutiveFailures} >= ${MAX_CONSECUTIVE_FAILURES}`);
20568
20557
  return;
20569
20558
  }
20570
20559
  const effectiveCooldown = CONTINUATION_COOLDOWN_MS * Math.pow(2, Math.min(state2.consecutiveFailures, 5));
20571
20560
  if (state2.lastInjectedAt && Date.now() - state2.lastInjectedAt < effectiveCooldown) {
20572
20561
  log(`[${HOOK_NAME}] Skipped: cooldown active`, { sessionID, effectiveCooldown, consecutiveFailures: state2.consecutiveFailures });
20573
- console.error(`[TODO-DIAG] BLOCKED: cooldown active (${effectiveCooldown}ms, failures=${state2.consecutiveFailures})`);
20574
20562
  return;
20575
20563
  }
20576
20564
  let resolvedInfo;
@@ -20591,12 +20579,10 @@ async function handleSessionIdle(args) {
20591
20579
  const resolvedAgentName = resolvedInfo?.agent;
20592
20580
  if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
20593
20581
  log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
20594
- console.error(`[TODO-DIAG] BLOCKED: agent '${resolvedAgentName}' in skipAgents`);
20595
20582
  return;
20596
20583
  }
20597
20584
  if ((compactionGuardActive || encounteredCompaction) && !resolvedInfo?.agent) {
20598
20585
  log(`[${HOOK_NAME}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
20599
- console.error(`[TODO-DIAG] BLOCKED: compaction guard + no agent`);
20600
20586
  return;
20601
20587
  }
20602
20588
  if (state2.recentCompactionAt && resolvedInfo?.agent) {
@@ -20604,20 +20590,16 @@ async function handleSessionIdle(args) {
20604
20590
  }
20605
20591
  if (isContinuationStopped?.(sessionID)) {
20606
20592
  log(`[${HOOK_NAME}] Skipped: continuation stopped for session`, { sessionID });
20607
- console.error(`[TODO-DIAG] BLOCKED: isContinuationStopped=true`);
20608
20593
  return;
20609
20594
  }
20610
20595
  if (shouldSkipContinuation?.(sessionID)) {
20611
20596
  log(`[${HOOK_NAME}] Skipped: another continuation hook already injected`, { sessionID });
20612
- console.error(`[TODO-DIAG] BLOCKED: shouldSkipContinuation=true (gptPermissionContinuation recently injected)`);
20613
20597
  return;
20614
20598
  }
20615
20599
  const progressUpdate = sessionStateStore.trackContinuationProgress(sessionID, incompleteCount, todos);
20616
20600
  if (shouldStopForStagnation({ sessionID, incompleteCount, progressUpdate })) {
20617
- console.error(`[TODO-DIAG] BLOCKED: stagnation detected (count=${progressUpdate.stagnationCount})`);
20618
20601
  return;
20619
20602
  }
20620
- console.error(`[TODO-DIAG] PASSED all gates! Starting countdown (${incompleteCount}/${todos.length} incomplete)`);
20621
20603
  startCountdown({
20622
20604
  ctx,
20623
20605
  sessionID,
@@ -20708,9 +20690,6 @@ function createTodoContinuationHandler(args) {
20708
20690
  } = args;
20709
20691
  return async ({ event }) => {
20710
20692
  const props = event.properties;
20711
- if (event.type === "session.idle") {
20712
- console.error(`[TODO-DIAG] handler received session.idle event`, { sessionID: props?.sessionID });
20713
- }
20714
20693
  if (event.type === "session.error") {
20715
20694
  const sessionID = props?.sessionID;
20716
20695
  if (!sessionID)
@@ -78169,9 +78148,9 @@ var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
78169
78148
  var MIN_STABILITY_TIME_MS2 = 10 * 1000;
78170
78149
  var DEFAULT_STALE_TIMEOUT_MS = 1200000;
78171
78150
  var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
78172
- var DEFAULT_MAX_TOOL_CALLS = 200;
78151
+ var DEFAULT_MAX_TOOL_CALLS = 4000;
78173
78152
  var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
78174
- var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
78153
+ var DEFAULT_CIRCUIT_BREAKER_ENABLED = false;
78175
78154
  var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
78176
78155
  var MIN_IDLE_TIME_MS = 5000;
78177
78156
  var POLLING_INTERVAL_MS = 3000;
@@ -78638,13 +78617,15 @@ function pruneStaleTasksAndNotifications(args) {
78638
78617
  tasks.delete(taskId);
78639
78618
  continue;
78640
78619
  }
78620
+ if (task.status === "running")
78621
+ continue;
78641
78622
  const timestamp2 = task.status === "pending" ? task.queuedAt?.getTime() : task.startedAt?.getTime();
78642
78623
  if (!timestamp2)
78643
78624
  continue;
78644
78625
  const age = now - timestamp2;
78645
78626
  if (age <= TASK_TTL_MS)
78646
78627
  continue;
78647
- const errorMessage = task.status === "pending" ? "Task timed out while queued (30 minutes)" : "Task timed out after 30 minutes";
78628
+ const errorMessage = "Task timed out while queued (30 minutes)";
78648
78629
  onTaskPruned(taskId, task, errorMessage);
78649
78630
  }
78650
78631
  for (const [sessionID, queued] of notifications.entries()) {
@@ -79514,22 +79495,22 @@ class BackgroundManager {
79514
79495
  });
79515
79496
  return;
79516
79497
  }
79498
+ const maxToolCalls = circuitBreaker.maxToolCalls;
79499
+ if (task.progress.toolCalls >= maxToolCalls) {
79500
+ log("[background-agent] Circuit breaker: tool call limit reached", {
79501
+ taskId: task.id,
79502
+ toolCalls: task.progress.toolCalls,
79503
+ maxToolCalls,
79504
+ agent: task.agent,
79505
+ sessionID
79506
+ });
79507
+ this.cancelTask(task.id, {
79508
+ source: "circuit-breaker",
79509
+ reason: `Subagent exceeded maximum tool call limit (${maxToolCalls}). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`
79510
+ });
79511
+ }
79517
79512
  }
79518
79513
  }
79519
- const maxToolCalls = circuitBreaker.maxToolCalls;
79520
- if (task.progress.toolCalls >= maxToolCalls) {
79521
- log("[background-agent] Circuit breaker: tool call limit reached", {
79522
- taskId: task.id,
79523
- toolCalls: task.progress.toolCalls,
79524
- maxToolCalls,
79525
- agent: task.agent,
79526
- sessionID
79527
- });
79528
- this.cancelTask(task.id, {
79529
- source: "circuit-breaker",
79530
- reason: `Subagent exceeded maximum tool call limit (${maxToolCalls}). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`
79531
- });
79532
- }
79533
79514
  }
79534
79515
  }
79535
79516
  if (event.type === "session.idle") {
@@ -80144,7 +80125,10 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
80144
80125
  this.pruneStaleTasksAndNotifications();
80145
80126
  const statusResult = await this.client.session.status();
80146
80127
  const allStatuses = normalizeSDKResponse(statusResult, {});
80147
- await this.checkAndInterruptStaleTasks(allStatuses);
80128
+ const circuitBreakerForPolling = this.cachedCircuitBreakerSettings ?? resolveCircuitBreakerSettings(this.config);
80129
+ if (circuitBreakerForPolling.enabled) {
80130
+ await this.checkAndInterruptStaleTasks(allStatuses);
80131
+ }
80148
80132
  for (const task of this.tasks.values()) {
80149
80133
  if (task.status !== "running")
80150
80134
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evil-omo",
3
- "version": "3.12.2",
3
+ "version": "3.12.4",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -76,17 +76,17 @@
76
76
  "typescript": "^5.7.3"
77
77
  },
78
78
  "optionalDependencies": {
79
- "evil-omo-darwin-arm64": "3.12.2",
80
- "evil-omo-darwin-x64": "3.12.2",
81
- "evil-omo-darwin-x64-baseline": "3.12.2",
82
- "evil-omo-linux-x64": "3.12.2",
83
- "evil-omo-linux-x64-baseline": "3.12.2",
84
- "evil-omo-linux-arm64": "3.12.2",
85
- "evil-omo-linux-x64-musl": "3.12.2",
86
- "evil-omo-linux-x64-musl-baseline": "3.12.2",
87
- "evil-omo-linux-arm64-musl": "3.12.2",
88
- "evil-omo-windows-x64": "3.12.2",
89
- "evil-omo-windows-x64-baseline": "3.12.2"
79
+ "evil-omo-darwin-arm64": "3.12.4",
80
+ "evil-omo-darwin-x64": "3.12.4",
81
+ "evil-omo-darwin-x64-baseline": "3.12.4",
82
+ "evil-omo-linux-x64": "3.12.4",
83
+ "evil-omo-linux-x64-baseline": "3.12.4",
84
+ "evil-omo-linux-arm64": "3.12.4",
85
+ "evil-omo-linux-x64-musl": "3.12.4",
86
+ "evil-omo-linux-x64-musl-baseline": "3.12.4",
87
+ "evil-omo-linux-arm64-musl": "3.12.4",
88
+ "evil-omo-windows-x64": "3.12.4",
89
+ "evil-omo-windows-x64-baseline": "3.12.4"
90
90
  },
91
91
  "overrides": {
92
92
  "@opencode-ai/sdk": "^1.2.24"