feat(FN-2105): merge fusion/fn-2105

This commit is contained in:
gsxdsm
2026-04-19 02:36:43 -07:00
parent cace54db0b
commit 7a920249f8
2 changed files with 28 additions and 20 deletions

View File

@@ -6,7 +6,7 @@ import {
type AgentSession,
type HeartbeatExecutionOptions,
HEARTBEAT_SYSTEM_PROMPT,
HEARTBEAT_NO_TASK_SYSTEM_PROMPT,
HEARTBEAT_SYSTEM_PROMPT_NO_TASK,
} from "./agent-heartbeat.js";
import { AgentLogger } from "./agent-logger.js";
import type { AgentStore, AgentHeartbeatRun, TaskStore, TaskDetail, Agent, MessageStore, Message, AgentBudgetStatus } from "@fusion/core";
@@ -1465,7 +1465,7 @@ describe("HeartbeatMonitor", () => {
expect(toolNames).not.toContain("task_document_read");
});
it("no-task run system prompt uses HEARTBEAT_NO_TASK_SYSTEM_PROMPT and does not reference task-scoped tools", async () => {
it("no-task run receives HEARTBEAT_SYSTEM_PROMPT_NO_TASK as system prompt", async () => {
const store = createStoreWithAgentForExec({ taskId: undefined, soul: "I am a coordinator" });
const mockSession = createMockAgentSession();
mockedCreateKbAgent.mockResolvedValue({ session: mockSession as any });
@@ -1478,7 +1478,7 @@ describe("HeartbeatMonitor", () => {
const callArgs = mockedCreateKbAgent.mock.calls[0]![0]!;
const systemPrompt = callArgs.systemPrompt;
expect(systemPrompt).toContain(HEARTBEAT_NO_TASK_SYSTEM_PROMPT);
expect(systemPrompt).toContain(HEARTBEAT_SYSTEM_PROMPT_NO_TASK);
expect(systemPrompt).not.toContain("task_log");
expect(systemPrompt).not.toContain("task_document_write");
expect(systemPrompt).not.toContain("task_document_read");
@@ -1504,7 +1504,7 @@ describe("HeartbeatMonitor", () => {
expect(mockedCreateKbAgent).toHaveBeenCalledOnce();
const callArgs = mockedCreateKbAgent.mock.calls[0]![0]!;
const systemPrompt = callArgs.systemPrompt;
expect(systemPrompt).toContain(HEARTBEAT_NO_TASK_SYSTEM_PROMPT);
expect(systemPrompt).toContain(HEARTBEAT_SYSTEM_PROMPT_NO_TASK);
expect(systemPrompt).not.toContain("task_log");
expect(systemPrompt).not.toContain("task_document_write");
expect(systemPrompt).not.toContain("task_document_read");
@@ -1530,7 +1530,7 @@ describe("HeartbeatMonitor", () => {
expect(executionPrompt).not.toContain("Task description:");
});
it("task-scoped run system prompt uses original HEARTBEAT_SYSTEM_PROMPT", async () => {
it("task-scoped run receives HEARTBEAT_SYSTEM_PROMPT as system prompt", async () => {
const store = createStoreWithAgentForExec({ taskId: "FN-001" });
const mockSession = createMockAgentSession();
mockedCreateKbAgent.mockResolvedValue({ session: mockSession as any });
@@ -2353,19 +2353,24 @@ describe("HeartbeatMonitor", () => {
});
describe("execution", () => {
it("HEARTBEAT_NO_TASK_SYSTEM_PROMPT does not mention task_log or task_document tools", () => {
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_log");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_document_write");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_document_read");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_document");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("task_create");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("list_agents");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("delegate_task");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("read_messages");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("send_message");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("memory_search");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("memory_append");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("heartbeat_done");
it("HEARTBEAT_SYSTEM_PROMPT_NO_TASK does not mention task-scoped tools", () => {
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).not.toContain("task_log");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).not.toContain("task_document_write");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).not.toContain("task_document_read");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).not.toContain("task_document");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("task_create");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("list_agents");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("delegate_task");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("read_messages");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("send_message");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("memory_search");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("memory_append");
expect(HEARTBEAT_SYSTEM_PROMPT_NO_TASK).toContain("heartbeat_done");
});
it("HEARTBEAT_SYSTEM_PROMPT mentions task_log and task_document_write", () => {
expect(HEARTBEAT_SYSTEM_PROMPT).toContain("task_log");
expect(HEARTBEAT_SYSTEM_PROMPT).toContain("task_document_write");
});
it("creates session with enriched system prompt and expected tools", async () => {

View File

@@ -166,7 +166,7 @@ When sending messages:
* System prompt for no-task heartbeat agent sessions.
* Instructs the agent to perform ambient work only with tools that do not require task context.
*/
export const HEARTBEAT_NO_TASK_SYSTEM_PROMPT = `You are a heartbeat agent running in a short execution window.
export const HEARTBEAT_SYSTEM_PROMPT_NO_TASK = `You are a heartbeat agent running in a short execution window.
Your job:
1. Review your context — check messages, memory, and project state.
@@ -201,6 +201,9 @@ When sending messages:
- Include relevant context (task IDs, file paths) in metadata when applicable.
- Use agent-to-agent for inter-agent communication.`;
// Backward-compatible alias; prefer HEARTBEAT_SYSTEM_PROMPT_NO_TASK.
export const HEARTBEAT_NO_TASK_SYSTEM_PROMPT = HEARTBEAT_SYSTEM_PROMPT_NO_TASK;
/** Parameter schema for the heartbeat_done tool */
const heartbeatDoneParams = Type.Object({
summary: Type.Optional(Type.String({ description: "Summary of what was accomplished this heartbeat" })),
@@ -1109,7 +1112,7 @@ export class HeartbeatMonitor {
const skillContext = buildSessionSkillContextSync(agent, "heartbeat", rootDir);
const baseHeartbeatSystemPrompt = isNoTaskRun
? HEARTBEAT_NO_TASK_SYSTEM_PROMPT
? HEARTBEAT_SYSTEM_PROMPT_NO_TASK
: HEARTBEAT_SYSTEM_PROMPT;
let systemPrompt = baseHeartbeatSystemPrompt;
try {