Skip to content

Commit 9e2b0a1

Browse files
ersinkocclaude
andcommitted
🔧 fix: autonomous agent scheduling, provider fallback, and duration bugs
- Background agent manager: use lastCycleToolCalls instead of lifetime totalToolCalls for adaptive scheduling — agents now properly accelerate when active and slow down when idle on a per-cycle basis - Autonomy engine + background agent runner: replace silent hardcoded fallback to openai/gpt-4o-mini with clear error when no provider is configured, preventing confusing auth errors - Subagent manager: use startedAt (when available) instead of spawnedAt for durationMs calculation on cancel/error, excluding queue wait time - Fix edge-mqtt-client tests: add vi.useRealTimers() in afterEach to prevent fake timer leak across tests (3 pre-existing failures fixed) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4de06c9 commit 9e2b0a1

5 files changed

Lines changed: 26 additions & 8 deletions

File tree

‎packages/gateway/src/autonomy/engine.ts‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,13 @@ export class AutonomyEngine implements IPulseService {
471471
const { getOrCreateChatAgent } = await import('../routes/agent-service.js');
472472
const { resolveForProcess } = await import('../services/model-routing.js');
473473
const resolved = await resolveForProcess('pulse');
474-
const provider = resolved.provider ?? 'openai';
475-
const model = resolved.model ?? 'gpt-4o-mini';
474+
if (!resolved.provider || !resolved.model) {
475+
throw new Error(
476+
'No AI provider configured for autonomous pulse. Set a default provider in Settings → AI Models.'
477+
);
478+
}
479+
const provider = resolved.provider;
480+
const model = resolved.model;
476481
const fallback =
477482
resolved.fallbackProvider && resolved.fallbackModel
478483
? { provider: resolved.fallbackProvider, model: resolved.fallbackModel }

‎packages/gateway/src/services/background-agent-manager.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ interface ManagedAgent {
6060
cyclesThisHour: number;
6161
hourWindow: number; // hour timestamp for rate limiting
6262
persistTimer: ReturnType<typeof setInterval> | null;
63+
lastCycleToolCalls: number; // tool calls in the most recent cycle (for scheduling)
6364
}
6465

6566
// ============================================================================
@@ -205,6 +206,7 @@ export class BackgroundAgentManager {
205206
cyclesThisHour: 0,
206207
hourWindow: this.getCurrentHour(),
207208
persistTimer: null,
209+
lastCycleToolCalls: 0,
208210
};
209211

210212
this.agents.set(config.id, managed);
@@ -427,8 +429,8 @@ export class BackgroundAgentManager {
427429
if (lastResult) {
428430
// Backoff on error
429431
delay = CONTINUOUS_MAX_DELAY_MS;
430-
} else if (managed.session.totalToolCalls === 0) {
431-
// No activity yet — use idle delay
432+
} else if (managed.lastCycleToolCalls === 0) {
433+
// Last cycle was idle (no tool calls) — slow down
432434
delay = CONTINUOUS_IDLE_DELAY_MS;
433435
} else {
434436
// Active — fast delay
@@ -571,6 +573,7 @@ export class BackgroundAgentManager {
571573
session.lastCycleAt = new Date();
572574
session.lastCycleDurationMs = result.durationMs;
573575
session.lastCycleError = result.error ?? null;
576+
managed.lastCycleToolCalls = result.toolCalls.length;
574577

575578
// Clear inbox after cycle (messages were included in the cycle prompt)
576579
session.inbox = [];

‎packages/gateway/src/services/background-agent-runner.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,15 @@ export class BackgroundAgentRunner {
8888
model = this.config.model;
8989
} else {
9090
const resolved = await resolveForProcess('pulse');
91-
provider = this.config.provider ?? resolved.provider ?? 'openai';
92-
model = this.config.model ?? resolved.model ?? 'gpt-4o-mini';
91+
const resolvedProvider = this.config.provider ?? resolved.provider;
92+
const resolvedModel = this.config.model ?? resolved.model;
93+
if (!resolvedProvider || !resolvedModel) {
94+
throw new Error(
95+
'No AI provider configured. Set provider/model on the agent or configure a default in Settings.'
96+
);
97+
}
98+
provider = resolvedProvider;
99+
model = resolvedModel;
93100
}
94101

95102
// 2. Create agent with full tool access

‎packages/gateway/src/services/edge-mqtt-client.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ describe('EdgeMqttClient', () => {
130130
});
131131

132132
afterEach(() => {
133+
vi.useRealTimers();
133134
delete process.env.MQTT_BROKER_URL;
134135
});
135136

‎packages/gateway/src/services/subagent-manager.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ export class SubagentManager {
197197
managed.session.state = 'cancelled';
198198
managed.session.completedAt = new Date();
199199
managed.session.durationMs =
200-
managed.session.completedAt.getTime() - managed.session.spawnedAt.getTime();
200+
managed.session.completedAt.getTime() -
201+
(managed.session.startedAt ?? managed.session.spawnedAt).getTime();
201202

202203
log.info(`Cancelled subagent "${managed.session.name}" [${subagentId}]`);
203204

@@ -348,7 +349,8 @@ export class SubagentManager {
348349
managed.session.error = getErrorMessage(err);
349350
managed.session.completedAt = new Date();
350351
managed.session.durationMs =
351-
managed.session.completedAt.getTime() - managed.session.spawnedAt.getTime();
352+
managed.session.completedAt.getTime() -
353+
(managed.session.startedAt ?? managed.session.spawnedAt).getTime();
352354

353355
log.error('Subagent execution error', {
354356
subagentId: id,

0 commit comments

Comments
 (0)