feat(FN-4401): complete Step 2 — memoize soul-word extraction
Fusion-Task-Id: FN-4401 Fusion-Task-Lineage: c1b6c497-b22c-48d5-b1c8-299877bf09ac
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
HEARTBEAT_NO_TASK_SYSTEM_PROMPT,
|
||||
HEARTBEAT_PROCEDURE,
|
||||
HEARTBEAT_NO_TASK_PROCEDURE,
|
||||
getAgentSoulWords,
|
||||
} from "../agent-heartbeat.js";
|
||||
import { AgentLogger } from "../agent-logger.js";
|
||||
import type { AgentStore, AgentHeartbeatRun, TaskStore, TaskDetail, Agent, MessageStore, Message } from "@fusion/core";
|
||||
@@ -35,6 +36,28 @@ vi.mock("../worktree-acquisition.js", () => ({
|
||||
acquireTaskWorktree: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("getAgentSoulWords", () => {
|
||||
it("memoizes soul words for repeated calls", () => {
|
||||
const agent = { id: "agent-memo-1", soul: "Focus reliability automation and clarity" } as Agent;
|
||||
|
||||
const first = getAgentSoulWords(agent);
|
||||
const second = getAgentSoulWords(agent);
|
||||
|
||||
expect(second).toBe(first);
|
||||
});
|
||||
|
||||
it("recomputes when soul changes", () => {
|
||||
const agent = { id: "agent-memo-2", soul: "Focus reliability automation" } as Agent;
|
||||
|
||||
const first = getAgentSoulWords(agent);
|
||||
agent.soul = "Focus performance profiling";
|
||||
const second = getAgentSoulWords(agent);
|
||||
|
||||
expect(second).not.toBe(first);
|
||||
expect(second).toContain("performance");
|
||||
});
|
||||
});
|
||||
|
||||
describe("executeHeartbeat", () => {
|
||||
let mockTaskStore: TaskStore;
|
||||
let mockAgent: Agent;
|
||||
|
||||
@@ -232,7 +232,28 @@ function isAutoClaimRelevantTasksEnabled(agent: Agent): boolean {
|
||||
return runtimeConfig.autoClaimRelevantTasks !== false;
|
||||
}
|
||||
|
||||
function taskRelevanceScore(agent: Agent, task: TaskDetail): number {
|
||||
type RelevanceScorableTask = Pick<TaskDetail, "title" | "description">;
|
||||
|
||||
const agentSoulWordsCache = new Map<string, { soulSnapshot: string; words: readonly string[] }>();
|
||||
|
||||
export function getAgentSoulWords(agent: Pick<Agent, "id" | "soul">): readonly string[] {
|
||||
const soulSnapshot = agent.soul ?? "";
|
||||
const existing = agentSoulWordsCache.get(agent.id);
|
||||
if (existing && existing.soulSnapshot === soulSnapshot) {
|
||||
return existing.words;
|
||||
}
|
||||
|
||||
const words = soulSnapshot
|
||||
.toLowerCase()
|
||||
.split(/[^a-z0-9]+/)
|
||||
.filter((word) => word.length >= 4)
|
||||
.slice(0, 8);
|
||||
|
||||
agentSoulWordsCache.set(agent.id, { soulSnapshot, words });
|
||||
return words;
|
||||
}
|
||||
|
||||
export function taskRelevanceScore(agent: Agent, task: RelevanceScorableTask): number {
|
||||
const haystack = `${task.title ?? ""} ${task.description}`.toLowerCase();
|
||||
let score = 0;
|
||||
|
||||
@@ -241,11 +262,7 @@ function taskRelevanceScore(agent: Agent, task: TaskDetail): number {
|
||||
score += 3;
|
||||
}
|
||||
|
||||
const soulWords = (agent.soul ?? "")
|
||||
.toLowerCase()
|
||||
.split(/[^a-z0-9]+/)
|
||||
.filter((word) => word.length >= 4)
|
||||
.slice(0, 8);
|
||||
const soulWords = getAgentSoulWords(agent);
|
||||
|
||||
for (const word of soulWords) {
|
||||
if (haystack.includes(word)) {
|
||||
|
||||
Reference in New Issue
Block a user