Chain CEO runs through self-wake during team building
triggerSelfWake fired only when the completing agent had at least one actionable task (todo / in_progress). A CEO in team_building mode has zero tasks by definition — each heartbeat hired one or two agents, returned, and then waited for the next 2-minute timer tick. Operators watched bootstrap crawl: in the captured field log, six heartbeats burned 12 minutes before the first hire landed. Teach triggerSelfWake to also chain when the agent is a top-level CEO with active goals, zero active direct reports, and no already-pending hire_agent approvals — the exact state team_building.en.md is telling the model to resolve. The existing circuit breaker (MAX_CONSECUTIVE_WAKES without progress) still applies, so a CEO that keeps returning without filing hires gets paused and its manager wake — the board / human — is raised instead. Once reports activate or all intended hires are filed, the predicate flips to false and runs resume the normal heartbeat cadence.
This commit is contained in:
1 parent
6b22fa9f0e
commit
834ac913ca
1 file changed
+37
-1
@@ -295,7 +295,8 @@ export function triggerSelfWake(
|
||||
|
||||
try {
|
||||
const actionableTasks = db.listAgentTasks(run.agentId, run.companyId, ["todo", "in_progress"]);
|
||||
if (actionableTasks.length === 0) return;
|
||||
const hasWorkToContinue = actionableTasks.length > 0 || hasUnfinishedTeamBuilding(db, run.agentId, run.companyId);
|
||||
if (!hasWorkToContinue) return;
|
||||
|
||||
const allowed = cb.tryWake(run.agentId, run.companyId);
|
||||
if (!allowed) {
|
||||
@@ -322,3 +323,38 @@ export function triggerSelfWake(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when a top-level agent (CEO) still has team-building work
|
||||
* in flight: the company has an active goal, zero active direct reports,
|
||||
* and no pending hire approvals. Each wake in that window files at most a
|
||||
* handful of hire_agent POSTs, so chaining runs via self-wake instead of
|
||||
* waiting for the 2-minute heartbeat timer takes bootstrap from
|
||||
* 10-plus minutes down to the LLM's round-trip time.
|
||||
*
|
||||
* The circuit breaker on the caller side still applies, so a CEO that
|
||||
* keeps completing runs without actually creating hires gets paused
|
||||
* after MAX_CONSECUTIVE_WAKES attempts.
|
||||
*/
|
||||
function hasUnfinishedTeamBuilding(
|
||||
db: HandlerDeps["db"],
|
||||
agentId: string,
|
||||
companyId: string,
|
||||
): boolean {
|
||||
try {
|
||||
const agent = db.getAgent(agentId);
|
||||
if (agent.reportsTo !== null) return false;
|
||||
const activeReports = db
|
||||
.getDirectReports(agentId, companyId)
|
||||
.filter((r) => r.status !== "pending_approval" && r.status !== "terminated");
|
||||
if (activeReports.length > 0) return false;
|
||||
const goalCount = db.countActiveGoalsForCompany(companyId);
|
||||
if (goalCount === 0) return false;
|
||||
const pendingHires = db
|
||||
.listCompanyApprovalsByType(companyId, "hire_agent")
|
||||
.filter((a) => a.state === "pending");
|
||||
return pendingHires.length === 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user