fix(heartbeat): default target none and internalize relay prompts

This commit is contained in:
Peter Steinberger
2026-02-25 00:33:32 +00:00
parent 4d89548e59
commit e2362d352d
9 changed files with 191 additions and 30 deletions

View File

@@ -44,6 +44,7 @@ import { escapeRegExp } from "../utils.js";
import { formatErrorMessage, hasErrnoCode } from "./errors.js";
import { isWithinActiveHours } from "./heartbeat-active-hours.js";
import {
buildExecEventPrompt,
buildCronEventPrompt,
isCronSystemEvent,
isExecCompletionEvent,
@@ -95,15 +96,7 @@ export type HeartbeatSummary = {
ackMaxChars: number;
};
const DEFAULT_HEARTBEAT_TARGET = "last";
// Prompt used when an async exec has completed and the result should be relayed to the user.
// This overrides the standard heartbeat prompt to ensure the model responds with the exec result
// instead of just "HEARTBEAT_OK".
const EXEC_EVENT_PROMPT =
"An async command you ran earlier has completed. The result is shown in the system messages above. " +
"Please relay the command output to the user in a helpful way. If the command succeeded, share the relevant output. " +
"If it failed, explain what went wrong.";
const DEFAULT_HEARTBEAT_TARGET = "none";
export { isCronSystemEvent };
type HeartbeatAgentState = {
@@ -615,12 +608,12 @@ export async function runHeartbeatOnce(opts: {
if (delivery.reason === "unknown-account") {
log.warn("heartbeat: unknown accountId", {
accountId: delivery.accountId ?? heartbeatAccountId ?? null,
target: heartbeat?.target ?? "last",
target: heartbeat?.target ?? "none",
});
} else if (heartbeatAccountId) {
log.info("heartbeat: using explicit accountId", {
accountId: delivery.accountId ?? heartbeatAccountId,
target: heartbeat?.target ?? "last",
target: heartbeat?.target ?? "none",
channel: delivery.channel,
});
}
@@ -654,10 +647,13 @@ export async function runHeartbeatOnce(opts: {
.map((event) => event.text);
const hasExecCompletion = pendingEvents.some(isExecCompletionEvent);
const hasCronEvents = cronEvents.length > 0;
const canRelayToUser = Boolean(
delivery.channel !== "none" && delivery.to && visibility.showAlerts,
);
const prompt = hasExecCompletion
? EXEC_EVENT_PROMPT
? buildExecEventPrompt({ deliverToUser: canRelayToUser })
: hasCronEvents
? buildCronEventPrompt(cronEvents)
? buildCronEventPrompt(cronEvents, { deliverToUser: canRelayToUser })
: resolveHeartbeatPrompt(cfg, heartbeat);
const ctx = {
Body: appendCronStyleCurrentTimeLine(prompt, cfg, startedAt),