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:
Fusion
2026-05-14 00:30:34 -07:00
committed by gsxdsm
parent 422e598a84
commit b8f5a237bd
2 changed files with 46 additions and 6 deletions

View File

@@ -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;

View File

@@ -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)) {