feat(FN-4400): complete Step 2 — add heartbeat prompt template settings

Fusion-Task-Id: FN-4400
Fusion-Task-Lineage: 0d4f9c48-5b8b-49eb-9cf3-d1d585ffe7fc
This commit is contained in:
Fusion
2026-05-14 03:36:31 -07:00
committed by gsxdsm
parent 8a608e1d0d
commit ae150d169d
6 changed files with 48 additions and 1 deletions

View File

@@ -95,6 +95,12 @@ describe("settings key parity", () => {
expect(isGlobalSettingsKey("heartbeatScopeDiscipline")).toBe(false);
});
it("keeps heartbeatPromptTemplate project-scoped with default default", () => {
expect(DEFAULT_PROJECT_SETTINGS.heartbeatPromptTemplate).toBe("default");
expect(isProjectSettingsKey("heartbeatPromptTemplate")).toBe(true);
expect(isGlobalSettingsKey("heartbeatPromptTemplate")).toBe(false);
});
it("documents autoClaimCandidatesInPrompt expected integer range", () => {
const inRange = [0, 1, 5, 10];
const outOfRange = [-1, 11, 100];

View File

@@ -3,6 +3,7 @@ import {
validateDirectMergeCommitStrategy,
validateGithubAuthMode,
validateGithubRepoSlug,
validateHeartbeatPromptTemplate,
validateHeartbeatScopeDisciplineMode,
validateUnavailableNodePolicy,
} from "../settings-validation.js";
@@ -62,6 +63,19 @@ describe("settings-validation", () => {
});
});
describe("validateHeartbeatPromptTemplate", () => {
it("accepts supported templates", () => {
expect(validateHeartbeatPromptTemplate("default")).toBe("default");
expect(validateHeartbeatPromptTemplate("compact")).toBe("compact");
});
it("returns undefined for invalid values", () => {
expect(validateHeartbeatPromptTemplate("tiny")).toBeUndefined();
expect(validateHeartbeatPromptTemplate(123)).toBeUndefined();
expect(validateHeartbeatPromptTemplate(undefined)).toBeUndefined();
});
});
describe("validateGithubRepoSlug", () => {
it("accepts valid owner/repo slugs", () => {
expect(validateGithubRepoSlug("owner/repo")).toBe("owner/repo");

View File

@@ -1,5 +1,5 @@
export { COLUMNS, DEFAULT_COLUMN, isColumn, normalizeColumn, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, DEFAULT_SETTINGS, DEFAULT_GLOBAL_SETTINGS, DEFAULT_PROJECT_SETTINGS, GLOBAL_SETTINGS_KEYS, PROJECT_SETTINGS_KEYS, isGlobalSettingsKey, isProjectSettingsKey, resolvePersistAgentThinkingLog, THINKING_LEVELS, THEME_MODES, COLOR_THEMES, WORKFLOW_STEP_TEMPLATES, AGENT_PERMISSIONS, PERMANENT_AGENT_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PROVISIONING_APPROVAL_MODES, AGENT_PERMISSION_POLICY_PRESET_IDS, LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES, APPROVAL_REQUEST_STATUSES, APPROVAL_REQUEST_AUDIT_EVENT_TYPES, normalizeApprovalRequestActionCategory, isValidApprovalRequestTransition, agentToConfigSnapshot, diffConfigSnapshots, isEphemeralAgent, hasAgentIdentity, CheckoutConflictError, DEFAULT_HEARTBEAT_PROCEDURE_PATH, getDefaultHeartbeatProcedurePath, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY, HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, DASHBOARD_USER_ID, normalizeMessageParticipant, validateMessageMetadata, validateDockerNodeConfig, sanitizeDockerNodeConfigForResponse, normalizeMergeConflictStrategy, normalizeMergeStrategyOverlapBehavior, normalizePostMergeAuditMode, POST_MERGE_AUDIT_MODES, normalizeMergeAuditAutoRecovery, MERGE_AUDIT_AUTO_RECOVERY_MODES, buildResearchDocumentKey, REPO_OVERRIDE_RE, SHARED_STATE_SNAPSHOT_VERSION } from "./types.js";
export type { Column, IssueInfo, IssueState, TaskSourceIssue, PrInfo, PrStatus, Task, TaskTokenUsage, TaskAttachment, TaskComment, TaskCommentInput, TaskDocument, TaskDocumentRevision, TaskDocumentCreateInput, TaskDocumentWithTask, TaskCreateInput, MeshReplicatedTaskCreatePayload, MeshReplicatedTaskApplyResult, TaskSource, SourceType, TaskDetail, InboxTask, TodoList, TodoItem, TodoListCreateInput, TodoListUpdateInput, TodoItemCreateInput, TodoItemUpdateInput, TodoListWithItems, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, DistributedTaskIdReserveInput, DistributedTaskIdReserveResult, DistributedTaskIdCommitInput, DistributedTaskIdCommitResult, DistributedTaskIdAbortInput, DistributedTaskIdAbortResult, DistributedTaskIdStateInput, DistributedTaskIdStateResult, AutostashOrphanRecord, AutostashOutcome, MergeDetails, MergeResult, MergeConflictStrategy, CanonicalMergeConflictStrategy, MergeStrategyOverlapBehavior, PostMergeAuditMode, MergeAuditAutoRecoveryMode, DirectMergeCommitStrategy, Settings, GlobalSettings, ProjectSettings, WebSearchBackend, ResearchEnabledSources, ResearchGlobalDefaults, ResearchProjectLimits, ResearchProjectSettings, EvalFollowUpPolicy, EvalProjectSettings, ResolvedEvalSettings, SettingsScope, DaemonTokenSettings, TaskStep, StepStatus, TaskLogEntry, RunMutationContext, ActivityLogEntry, ActivityEventType, ThinkingLevel, ThemeMode, ColorTheme, ExecutionMode, TaskPriority, UnavailableNodePolicy, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult, GithubIssueAction, ModelPreset, WorkflowStep, WorkflowStepMode, WorkflowStepPhase, WorkflowStepInput, WorkflowStepResult, WorkflowStepTemplate, Agent, OrgTreeNode, AgentState, AgentDetail, AgentCreateInput, AgentUpdateInput, AgentApiKey, AgentApiKeyCreateResult, AgentCapability, AgentPromptTemplate, AgentPromptsConfig, AgentPermission, PermanentAgentActionCategory, PermanentAgentSensitiveActionCategory, PermanentAgentGatingContext, AgentPermissionPolicy, AgentPermissionPolicyRules, AgentPermissionPolicyActionCategory, AgentProvisioningApprovalMode, LegacyAgentPermissionPolicyActionCategory, ApprovalRequestActionCategoryInput, ApprovalRequestActionCategory, AgentPermissionPolicyDisposition, AgentPermissionPolicyPresetId, ApprovalRequestStatus, ApprovalRequestAuditEventType, ApprovalRequestActorSnapshot, ApprovalRequestTargetAction, ApprovalRequestAuditEvent, ApprovalRequest, ApprovalRequestCreateInput, ApprovalRequestDecisionInput, ApprovalRequestCompletionInput, ApprovalRequestListInput, TaskAssignSource, AgentAccessState, AgentHeartbeatConfig, AgentBudgetConfig, AgentBudgetStatus, InstructionsBundleConfig, MessageResponseMode, AgentHeartbeatEvent, AgentHeartbeatRun, BlockedStateSnapshot, HeartbeatInvocationSource, AgentTaskSession, AgentRating, AgentRatingSummary, AgentRatingInput, AgentConfigSnapshot, RevisionFieldDiff, AgentConfigRevision, AgentStats, ReflectionTrigger, ReflectionMetrics, AgentReflection, AgentPerformanceSummary, NtfyNotificationEvent, NotificationEvent, NotificationPayload, NotificationProviderConfig, CustomProvider, SteeringComment, ParticipantType, MessageType, Message, MessageCreateInput, MessageFilter, MessageMetadata, MessageReplyReference, Mailbox, CheckoutLease, RunAuditDomain, RunAuditEvent, RunAuditEventInput, RunAuditEventFilter, AgentMemoryInclusionMode, HeartbeatScopeDisciplineMode } from "./types.js";
export type { Column, IssueInfo, IssueState, TaskSourceIssue, PrInfo, PrStatus, Task, TaskTokenUsage, TaskAttachment, TaskComment, TaskCommentInput, TaskDocument, TaskDocumentRevision, TaskDocumentCreateInput, TaskDocumentWithTask, TaskCreateInput, MeshReplicatedTaskCreatePayload, MeshReplicatedTaskApplyResult, TaskSource, SourceType, TaskDetail, InboxTask, TodoList, TodoItem, TodoListCreateInput, TodoListUpdateInput, TodoItemCreateInput, TodoItemUpdateInput, TodoListWithItems, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, DistributedTaskIdReserveInput, DistributedTaskIdReserveResult, DistributedTaskIdCommitInput, DistributedTaskIdCommitResult, DistributedTaskIdAbortInput, DistributedTaskIdAbortResult, DistributedTaskIdStateInput, DistributedTaskIdStateResult, AutostashOrphanRecord, AutostashOutcome, MergeDetails, MergeResult, MergeConflictStrategy, CanonicalMergeConflictStrategy, MergeStrategyOverlapBehavior, PostMergeAuditMode, MergeAuditAutoRecoveryMode, DirectMergeCommitStrategy, Settings, GlobalSettings, ProjectSettings, WebSearchBackend, ResearchEnabledSources, ResearchGlobalDefaults, ResearchProjectLimits, ResearchProjectSettings, EvalFollowUpPolicy, EvalProjectSettings, ResolvedEvalSettings, SettingsScope, DaemonTokenSettings, TaskStep, StepStatus, TaskLogEntry, RunMutationContext, ActivityLogEntry, ActivityEventType, ThinkingLevel, ThemeMode, ColorTheme, ExecutionMode, TaskPriority, UnavailableNodePolicy, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult, GithubIssueAction, ModelPreset, WorkflowStep, WorkflowStepMode, WorkflowStepPhase, WorkflowStepInput, WorkflowStepResult, WorkflowStepTemplate, Agent, OrgTreeNode, AgentState, AgentDetail, AgentCreateInput, AgentUpdateInput, AgentApiKey, AgentApiKeyCreateResult, AgentCapability, AgentPromptTemplate, AgentPromptsConfig, AgentPermission, PermanentAgentActionCategory, PermanentAgentSensitiveActionCategory, PermanentAgentGatingContext, AgentPermissionPolicy, AgentPermissionPolicyRules, AgentPermissionPolicyActionCategory, AgentProvisioningApprovalMode, LegacyAgentPermissionPolicyActionCategory, ApprovalRequestActionCategoryInput, ApprovalRequestActionCategory, AgentPermissionPolicyDisposition, AgentPermissionPolicyPresetId, ApprovalRequestStatus, ApprovalRequestAuditEventType, ApprovalRequestActorSnapshot, ApprovalRequestTargetAction, ApprovalRequestAuditEvent, ApprovalRequest, ApprovalRequestCreateInput, ApprovalRequestDecisionInput, ApprovalRequestCompletionInput, ApprovalRequestListInput, TaskAssignSource, AgentAccessState, AgentHeartbeatConfig, AgentBudgetConfig, AgentBudgetStatus, InstructionsBundleConfig, MessageResponseMode, AgentHeartbeatEvent, AgentHeartbeatRun, BlockedStateSnapshot, HeartbeatInvocationSource, AgentTaskSession, AgentRating, AgentRatingSummary, AgentRatingInput, AgentConfigSnapshot, RevisionFieldDiff, AgentConfigRevision, AgentStats, ReflectionTrigger, ReflectionMetrics, AgentReflection, AgentPerformanceSummary, NtfyNotificationEvent, NotificationEvent, NotificationPayload, NotificationProviderConfig, CustomProvider, SteeringComment, ParticipantType, MessageType, Message, MessageCreateInput, MessageFilter, MessageMetadata, MessageReplyReference, Mailbox, CheckoutLease, RunAuditDomain, RunAuditEvent, RunAuditEventInput, RunAuditEventFilter, AgentMemoryInclusionMode, HeartbeatPromptTemplate, HeartbeatScopeDisciplineMode } from "./types.js";
export { AGENT_VALID_TRANSITIONS } from "./types.js";
export {
resolveAgentMemoryInclusionMode,

View File

@@ -173,6 +173,7 @@ export const DEFAULT_PROJECT_SETTINGS = {
heartbeatMultiplier: 1,
autoClaimCandidatesInPrompt: 5,
heartbeatScopeDiscipline: "strict",
heartbeatPromptTemplate: "default",
groupOverlappingFiles: true,
overlapIgnorePaths: [],
autoMerge: true,

View File

@@ -1,6 +1,7 @@
import type {
DirectMergeCommitStrategy,
GithubAuthMode,
HeartbeatPromptTemplate,
HeartbeatScopeDisciplineMode,
UnavailableNodePolicy,
} from "./types.js";
@@ -14,6 +15,10 @@ const HEARTBEAT_SCOPE_DISCIPLINE_MODES: readonly HeartbeatScopeDisciplineMode[]
"lite",
"off",
] as const;
const HEARTBEAT_PROMPT_TEMPLATES: readonly HeartbeatPromptTemplate[] = [
"default",
"compact",
] as const;
/**
* Validates a project unavailable-node routing policy value.
@@ -83,3 +88,16 @@ export function validateHeartbeatScopeDisciplineMode(value: unknown): HeartbeatS
? (value as HeartbeatScopeDisciplineMode)
: undefined;
}
/** Returns a validated heartbeat prompt template for project/agent settings, otherwise undefined. */
export function validateHeartbeatPromptTemplate(value: unknown): HeartbeatPromptTemplate | undefined {
if (value === undefined) {
return undefined;
}
if (typeof value !== "string") {
return undefined;
}
return (HEARTBEAT_PROMPT_TEMPLATES as readonly string[]).includes(value)
? (value as HeartbeatPromptTemplate)
: undefined;
}

View File

@@ -1666,6 +1666,7 @@ export interface ResolvedEvalSettings {
export type AgentMemoryInclusionMode = "full" | "index" | "off";
export type HeartbeatScopeDisciplineMode = "strict" | "lite" | "off";
export type HeartbeatPromptTemplate = "default" | "compact";
export interface GlobalSettings {
/** Theme mode preference: dark, light, or system (follows OS). Default: "dark". */
@@ -2102,6 +2103,11 @@ export interface ProjectSettings {
* - "off": minimal procedure with no scope-classification step
*/
heartbeatScopeDiscipline?: HeartbeatScopeDisciplineMode;
/** Heartbeat execution prompt template mode.
* - "default": richer context with higher caps (default)
* - "compact": lower caps to reduce prompt size
*/
heartbeatPromptTemplate?: HeartbeatPromptTemplate;
groupOverlappingFiles: boolean;
/** File/directory paths to ignore when evaluating overlap serialization.
* Entries are project-relative paths (for example: `docs/README.md`, `docs/`, `generated/*`).
@@ -4700,6 +4706,8 @@ export interface AgentHeartbeatConfig {
agentMemoryInclusionMode?: AgentMemoryInclusionMode;
/** Per-agent override for heartbeat scope-discipline procedure mode. */
heartbeatScopeDiscipline?: HeartbeatScopeDisciplineMode;
/** Per-agent override for heartbeat execution prompt template mode. */
heartbeatPromptTemplate?: HeartbeatPromptTemplate;
/** Last resolved memory inclusion mode recorded by engine for transition logging. */
lastAgentMemoryInclusionMode?: AgentMemoryInclusionMode;
/**