feat(FN-2931): merge fusion/fn-2931
- **New node routing policy engine** (`packages/engine/src/node-routing-policy.ts`, `scheduler.ts`): introduces configurable routing rules for assigning tasks to specific nodes, with a new test suite covering policy matching, node priority, and capacity constraints - **Agent run route decoupling** (`register-agent-runtime-routes.ts`): routes now respond before heartbeat side effects complete, improving latency and reducing 503s under load; tests updated to handle async state transitions - **Scheduler node routing** (`scheduler-node-routing.test.ts`): 131-line test expansion covering task-to-node assignment with routing policies - **Dashboard UI polish** (AgentsView, QuickChatFAB, SettingsModal): CSS refinements and component adjustments from FN-2903 - **Reviewer and triage refinements** (`reviewer.ts`, `triage.ts`): logic improvements for async execution paths - **Remote access adapter** (`provider-adapters.ts`): extended from FN-2950 - **Settings memory routes** (`register-settings-memory-routes.ts`): from FN-2925 - **Docs** (`architecture.md`, `settings-reference.md`): updated for new routing policy and settings features Commits merged: - test(FN-2931): complete Step 2 — update async state route assertions - feat(FN-2931): complete Step 1 — respond before heartbeat side effects - feat(FN-2925): merge fusion/fn-2925 - feat(FN-2903): merge fusion/fn-2903 - feat(FN-2950): merge fusion/fn-2950 Files changed: docs/architecture.md | 19 +-- docs/settings-reference.md | 2 +- packages/dashboard/app/components/AgentsView.css | 20 +++- packages/dashboard/app/components/AgentsView.tsx | 11 +- packages/dashboard/app/components/QuickChatFAB.css | 32 ++++- .../dashboard/app/components/SettingsModal.css | 21 ++++ .../dashboard/app/components/SettingsModal.tsx | 58 ++++++++- .../app/components/__tests__/AgentsView.test.tsx | 37 ++++++ .../app/components/__tests__/QuickChatFAB.test.tsx | 18 +-- .../components/__tests__/SettingsModal.test.tsx | 2 +- packages/dashboard/package.json | 2 + .../src/__tests__/routes-agent-runs.test.ts | 13 +- .../src/routes/register-agent-runtime-routes.ts | 49 ++++---- .../src/routes/register-settings-memory-routes.ts | 8 +- .../src/__tests__/node-routing-policy.test.ts | 78 ++++++++++++ packages/engine/src/__tests__/reviewer.test.ts | 19 ++- .../src/__tests__/scheduler-node-routing.test.ts | 131 ++++++++++++++++++++- packages/engine/src/__tests__/triage.test.ts | 16 +-- packages/engine/src/node-routing-policy.ts | 45 +++++++ .../engine/src/remote-access/provider-adapters.ts | 14 ++- packages/engine/src/reviewer.ts | 33 ++++-- packages/engine/src/scheduler.ts | 44 ++++++- packages/engine/src/triage.ts | 48 ++++---- pnpm-lock.yaml | 113 ++++++++++++++++++ 24 files changed, 721 insertions(+), 112 deletions(-) Fusion-Task-Id: FN-2931
This commit is contained in:
@@ -416,29 +416,38 @@ export function registerAgentRuntimeRoutes(ctx: ApiRoutesContext, deps: AgentRun
|
||||
? heartbeatMonitor
|
||||
: null;
|
||||
|
||||
if (nextState === "paused" && projectHeartbeatMonitor) {
|
||||
const activeRun = await agentStore.getActiveHeartbeatRun(agentId);
|
||||
if (activeRun) {
|
||||
await projectHeartbeatMonitor.stopRun(agentId);
|
||||
}
|
||||
}
|
||||
|
||||
const updatedAgent = await agentStore.updateAgentState(agentId, nextState);
|
||||
|
||||
if (nextState === "active" && projectHeartbeatMonitor) {
|
||||
await projectHeartbeatMonitor.executeHeartbeat({
|
||||
agentId,
|
||||
source: "on_demand",
|
||||
triggerDetail: "Triggered from state resume",
|
||||
contextSnapshot: {
|
||||
wakeReason: "on_demand",
|
||||
triggerDetail: "Triggered from state resume",
|
||||
triggerSource: "state-resume",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.json(updatedAgent);
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
if (nextState === "paused" && projectHeartbeatMonitor) {
|
||||
const activeRun = await agentStore.getActiveHeartbeatRun(agentId);
|
||||
if (activeRun) {
|
||||
await projectHeartbeatMonitor.stopRun(agentId);
|
||||
const agentAfterStop = await agentStore.getAgent(agentId);
|
||||
if (agentAfterStop && agentAfterStop.state !== "paused") {
|
||||
await agentStore.updateAgentState(agentId, "paused");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextState === "active" && projectHeartbeatMonitor) {
|
||||
await projectHeartbeatMonitor.executeHeartbeat({
|
||||
agentId,
|
||||
source: "on_demand",
|
||||
triggerDetail: "Triggered from state resume",
|
||||
contextSnapshot: {
|
||||
wakeReason: "on_demand",
|
||||
triggerDetail: "Triggered from state resume",
|
||||
triggerSource: "state-resume",
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[agent-state] async heartbeat work failed for ${agentId}:`, err);
|
||||
}
|
||||
})();
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof ApiError) {
|
||||
throw err;
|
||||
|
||||
Reference in New Issue
Block a user