feat(FN-4296): complete Step 5 — finalize sync wiring and verification fixes
Fusion-Task-Id: FN-4296 Fusion-Task-Lineage: 9d061861-547a-4905-8fdd-4b66dd7bff97
This commit is contained in:
5
.changeset/fn-4296-agent-link-drift.md
Normal file
5
.changeset/fn-4296-agent-link-drift.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Engine reliability: durable agents no longer retain a stale Current Task pointer after the task moves to done/archived/todo/triage. A new task-move listener clears agent.taskId in real time, and the self-healing sweep recovers already-drifted records on startup and periodically.
|
||||||
@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest";
|
|||||||
|
|
||||||
import { isEphemeralAgent, type Agent, type AgentStore, type Task } from "@fusion/core";
|
import { isEphemeralAgent, type Agent, type AgentStore, type Task } from "@fusion/core";
|
||||||
|
|
||||||
import { SelfHealingManager } from "../self-healing";
|
import { SelfHealingManager } from "../self-healing.js";
|
||||||
|
|
||||||
function makeAgent(id: string, taskId: string, state: Agent["state"] = "active"): Agent {
|
function makeAgent(id: string, taskId: string, state: Agent["state"] = "active"): Agent {
|
||||||
return { id, state, taskId, updatedAt: new Date(Date.now() - 120_000).toISOString() } as Agent;
|
return { id, state, taskId, updatedAt: new Date(Date.now() - 120_000).toISOString() } as Agent;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { join } from "node:path";
|
|||||||
import { AgentStore, type AgentCreateInput, type Task } from "@fusion/core";
|
import { AgentStore, type AgentCreateInput, type Task } from "@fusion/core";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { attachAgentLinkSync } from "../task-agent-sync";
|
import { attachAgentLinkSync } from "../task-agent-sync.js";
|
||||||
|
|
||||||
class EventedStore extends EventEmitter {
|
class EventedStore extends EventEmitter {
|
||||||
on(event: "task:moved", listener: (data: { task: Task; from: string; to: string }) => void): this {
|
on(event: "task:moved", listener: (data: { task: Task; from: string; to: string }) => void): this {
|
||||||
|
|||||||
@@ -484,10 +484,10 @@ describe("InProcessRuntime", () => {
|
|||||||
const moveData = { task: mockTask, from: "todo", to: "in-progress" };
|
const moveData = { task: mockTask, from: "todo", to: "in-progress" };
|
||||||
|
|
||||||
const onCalls = (taskStore.on as ReturnType<typeof vi.fn>).mock.calls;
|
const onCalls = (taskStore.on as ReturnType<typeof vi.fn>).mock.calls;
|
||||||
const taskMovedHandler = onCalls.find((call: unknown[]) => call[0] === "task:moved");
|
const taskMovedHandlers = onCalls.filter((call: unknown[]) => call[0] === "task:moved");
|
||||||
|
|
||||||
if (taskMovedHandler) {
|
for (const handler of taskMovedHandlers) {
|
||||||
(taskMovedHandler[1] as (data: { task: Task; from: string; to: string }) => void)(moveData);
|
(handler[1] as (data: { task: Task; from: string; to: string }) => void)(moveData);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(taskMovedSpy).toHaveBeenCalledWith(moveData);
|
expect(taskMovedSpy).toHaveBeenCalledWith(moveData);
|
||||||
|
|||||||
@@ -676,7 +676,7 @@ export class InProcessRuntime
|
|||||||
this.stuckTaskDetector.start();
|
this.stuckTaskDetector.start();
|
||||||
this.detachAgentLinkSync = attachAgentLinkSync({
|
this.detachAgentLinkSync = attachAgentLinkSync({
|
||||||
store: this.taskStore,
|
store: this.taskStore,
|
||||||
agentStore: this.agentStore,
|
agentStore: this.agentStore!,
|
||||||
hasActiveAgentExecution: (agentId: string) => this.heartbeatMonitor?.getTrackedAgents().includes(agentId) ?? false,
|
hasActiveAgentExecution: (agentId: string) => this.heartbeatMonitor?.getTrackedAgents().includes(agentId) ?? false,
|
||||||
logger: runtimeLog,
|
logger: runtimeLog,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user