feat(FN-940): allow terminated agents to restart as active or running
- Remove blanket block on terminated→any state transitions in agent-store - Add active and running as valid transitions from terminated state in types - Update tests to verify terminated→active and terminated→running succeed - Add tests for invalid terminated transitions (idle, paused) still throwing
This commit is contained in:
@@ -405,17 +405,35 @@ describe("AgentStore", () => {
|
||||
).rejects.toThrow("Invalid state transition: idle -> terminated");
|
||||
});
|
||||
|
||||
it("transition from terminated to any state throws", async () => {
|
||||
it("transition from terminated to invalid states throws", async () => {
|
||||
const agent = await createReadyAgent(store, "Terminated");
|
||||
await store.updateAgentState(agent.id, "active");
|
||||
await store.updateAgentState(agent.id, "terminated");
|
||||
|
||||
await expect(
|
||||
store.updateAgentState(agent.id, "active")
|
||||
).rejects.toThrow("Cannot transition from terminated");
|
||||
await expect(
|
||||
store.updateAgentState(agent.id, "idle")
|
||||
).rejects.toThrow("Cannot transition from terminated");
|
||||
).rejects.toThrow("Invalid state transition: terminated -> idle");
|
||||
await expect(
|
||||
store.updateAgentState(agent.id, "paused")
|
||||
).rejects.toThrow("Invalid state transition: terminated -> paused");
|
||||
});
|
||||
|
||||
it("terminated → active transition succeeds", async () => {
|
||||
const agent = await createReadyAgent(store, "RestartActive");
|
||||
await store.updateAgentState(agent.id, "active");
|
||||
await store.updateAgentState(agent.id, "terminated");
|
||||
|
||||
const updated = await store.updateAgentState(agent.id, "active");
|
||||
expect(updated.state).toBe("active");
|
||||
});
|
||||
|
||||
it("terminated → running transition succeeds", async () => {
|
||||
const agent = await createReadyAgent(store, "RestartRunning");
|
||||
await store.updateAgentState(agent.id, "active");
|
||||
await store.updateAgentState(agent.id, "terminated");
|
||||
|
||||
const updated = await store.updateAgentState(agent.id, "running");
|
||||
expect(updated.state).toBe("running");
|
||||
});
|
||||
|
||||
it("emits both 'agent:stateChanged' and 'agent:updated' events", async () => {
|
||||
|
||||
@@ -244,10 +244,6 @@ export class AgentStore extends EventEmitter {
|
||||
return agent; // No change needed
|
||||
}
|
||||
|
||||
if (currentState === "terminated") {
|
||||
throw new Error(`Cannot transition from terminated state to ${newState}`);
|
||||
}
|
||||
|
||||
const validTransitions = AGENT_VALID_TRANSITIONS[currentState];
|
||||
if (!validTransitions.includes(newState)) {
|
||||
throw new Error(
|
||||
|
||||
@@ -1292,7 +1292,7 @@ export const AGENT_VALID_TRANSITIONS: Record<AgentState, AgentState[]> = {
|
||||
running: ["active", "paused", "error", "terminated"],
|
||||
paused: ["active", "terminated"],
|
||||
error: ["active", "terminated"],
|
||||
terminated: [], // Terminal state - no exits
|
||||
terminated: ["active", "running"], // Can be restarted
|
||||
};
|
||||
|
||||
/** Single heartbeat event recorded for an agent */
|
||||
|
||||
Reference in New Issue
Block a user