fix: --no-auth override, workflow revision in-place fix, state-driven heartbeats
Three orthogonal fixes bundled together so they re-land as a unit after earlier worktree-based reverts kept wiping them individually. 1. `--no-auth` flag now actually disables auth. Previously a stale FUSION_DAEMON_TOKEN in .env silently re-armed bearer-token auth despite the CLI flag. Added a `noAuth` option to ServerOptions; auth-middleware's isDaemonAuthActive/getDaemonToken short-circuit to false/undefined when set; CLI plumbs opts.noAuth through both createServer call sites. 2. Workflow review failures no longer reset every completed step. Previously a single CSS nit from a workflow reviewer could drag 5+ already-approved steps back through plan review, code review, and re-execution because determineRevisionResetStart fuzzy-matched feedback tokens against step names. handleWorkflowRevisionRequest, handleWorkflowStepFailure, and sendTaskBackForFix now call a new reopenLastStepForRevision helper that flips only the last non-pending step back to pending (with currentStep rewind via a newly-accepted updateTask field) — all earlier done steps stay done, and the agent applies the feedback as an in-place patch per the updated PROMPT.md instructions. determineRevisionResetStart stays exported as @deprecated so existing unit tests still link. 3. Heartbeat scheduling is now state-driven. Previously a non-ephemeral agent with a stale runtimeConfig.enabled=false on disk would never tick and the Pause/Resume button couldn't arm the timer without also flipping that hidden flag. HeartbeatTriggerScheduler's watchAgentLifecycle now registers on transitions into active/running and clears on transitions out; the tick and assignment-trigger guards key off state + ephemeral classification. InProcessRuntime's created/updated listeners and startup scan mirror the same semantics. runtimeConfig.enabled is only retained for ephemeral (task-worker) opt-out. Tests updated: agent-heartbeat.test.ts — one test renamed from "skips registration when enabled is false" (obsolete behavior) to "registers regardless of the legacy enabled flag"; 4 assignment-watching tests now pass a realistic `state: "active"` on mock agents. 207 heartbeat tests + 330 executor tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -492,14 +492,18 @@ export class InProcessRuntime
|
||||
);
|
||||
this.triggerScheduler.start();
|
||||
|
||||
// Set up dynamic registration for agents created or updated after startup
|
||||
// Dynamic registration follows state, not runtimeConfig.enabled.
|
||||
// Active/running → timer armed; anything else → not. Ephemeral
|
||||
// (task-worker) agents are never armed.
|
||||
const isTickable = (agent: import("@fusion/core").Agent) =>
|
||||
!isEphemeralAgent(agent) && (agent.state === "active" || agent.state === "running");
|
||||
|
||||
this.agentCreatedListener = (agent) => {
|
||||
if (!this.triggerScheduler) return;
|
||||
if (!isTickable(agent)) return;
|
||||
const rc = agent.runtimeConfig;
|
||||
if (rc?.enabled === false) return;
|
||||
this.triggerScheduler.registerAgent(agent.id, {
|
||||
heartbeatIntervalMs: rc?.heartbeatIntervalMs as number | undefined,
|
||||
enabled: rc?.enabled as boolean | undefined,
|
||||
maxConcurrentRuns: rc?.maxConcurrentRuns as number | undefined,
|
||||
});
|
||||
runtimeLog.log(`Registered new agent ${agent.id} for heartbeat triggers`);
|
||||
@@ -508,18 +512,17 @@ export class InProcessRuntime
|
||||
|
||||
this.agentUpdatedListener = (agent) => {
|
||||
if (!this.triggerScheduler) return;
|
||||
const rc = agent.runtimeConfig;
|
||||
if (rc?.enabled === false) {
|
||||
if (!isTickable(agent)) {
|
||||
this.triggerScheduler.unregisterAgent(agent.id);
|
||||
runtimeLog.log(`Unregistered agent ${agent.id} from heartbeat triggers (disabled)`);
|
||||
} else {
|
||||
this.triggerScheduler.registerAgent(agent.id, {
|
||||
heartbeatIntervalMs: rc?.heartbeatIntervalMs as number | undefined,
|
||||
enabled: rc?.enabled as boolean | undefined,
|
||||
maxConcurrentRuns: rc?.maxConcurrentRuns as number | undefined,
|
||||
});
|
||||
runtimeLog.log(`Re-registered agent ${agent.id} for heartbeat triggers`);
|
||||
runtimeLog.log(`Unregistered agent ${agent.id} from heartbeat triggers (state=${agent.state})`);
|
||||
return;
|
||||
}
|
||||
const rc = agent.runtimeConfig;
|
||||
this.triggerScheduler.registerAgent(agent.id, {
|
||||
heartbeatIntervalMs: rc?.heartbeatIntervalMs as number | undefined,
|
||||
maxConcurrentRuns: rc?.maxConcurrentRuns as number | undefined,
|
||||
});
|
||||
runtimeLog.log(`Re-registered agent ${agent.id} for heartbeat triggers (state=${agent.state})`);
|
||||
};
|
||||
this.agentStore.on("agent:updated", this.agentUpdatedListener);
|
||||
|
||||
@@ -563,21 +566,21 @@ export class InProcessRuntime
|
||||
};
|
||||
this.agentStore.on("agent:stateChanged", this.ephemeralTerminationListener);
|
||||
|
||||
// Register existing agents with heartbeat monitoring not explicitly disabled
|
||||
// Agents without explicit heartbeat config will use the default 3600-second interval (1 hour)
|
||||
// Register existing agents whose current state is tickable. Agents
|
||||
// in paused/idle/error/terminated stay unregistered until the user
|
||||
// re-activates them; the agentUpdatedListener arms the timer the
|
||||
// moment state transitions to active.
|
||||
try {
|
||||
const agents = await this.agentStore.listAgents();
|
||||
let registeredCount = 0;
|
||||
for (const agent of agents) {
|
||||
if (!isTickable(agent)) continue;
|
||||
const rc = agent.runtimeConfig;
|
||||
if (rc?.enabled !== false) {
|
||||
this.triggerScheduler.registerAgent(agent.id, {
|
||||
heartbeatIntervalMs: rc?.heartbeatIntervalMs as number | undefined,
|
||||
enabled: rc?.enabled as boolean | undefined,
|
||||
maxConcurrentRuns: rc?.maxConcurrentRuns as number | undefined,
|
||||
});
|
||||
registeredCount++;
|
||||
}
|
||||
this.triggerScheduler.registerAgent(agent.id, {
|
||||
heartbeatIntervalMs: rc?.heartbeatIntervalMs as number | undefined,
|
||||
maxConcurrentRuns: rc?.maxConcurrentRuns as number | undefined,
|
||||
});
|
||||
registeredCount++;
|
||||
}
|
||||
if (agents.length > 0) {
|
||||
runtimeLog.log(`Registered ${registeredCount} of ${agents.length} agents for heartbeat triggers`);
|
||||
|
||||
Reference in New Issue
Block a user