Heartbeat: normalize reply arrays for twilio/web

This commit is contained in:
Peter Steinberger
2025-12-03 00:43:28 +00:00
parent 9da5b9f4bb
commit 0f17a7d828
3 changed files with 21 additions and 13 deletions

View File

@@ -49,29 +49,33 @@ export async function runTwilioHeartbeatOnce(opts: {
To: to, To: to,
MessageSid: undefined, MessageSid: undefined,
}, },
undefined, { isHeartbeat: true },
); );
const replyPayload = Array.isArray(replyResult)
? replyResult[0]
: replyResult;
if ( if (
!replyResult || !replyPayload ||
(!replyResult.text && (!replyPayload.text &&
!replyResult.mediaUrl && !replyPayload.mediaUrl &&
!replyResult.mediaUrls?.length) !replyPayload.mediaUrls?.length)
) { ) {
logInfo("heartbeat skipped: empty reply", runtime); logInfo("heartbeat skipped: empty reply", runtime);
return; return;
} }
const hasMedia = Boolean( const hasMedia = Boolean(
replyResult.mediaUrl || (replyResult.mediaUrls?.length ?? 0) > 0, replyPayload.mediaUrl || (replyPayload.mediaUrls?.length ?? 0) > 0,
); );
const stripped = stripHeartbeatToken(replyResult.text); const stripped = stripHeartbeatToken(replyPayload.text);
if (stripped.shouldSkip && !hasMedia) { if (stripped.shouldSkip && !hasMedia) {
logInfo(success("heartbeat: ok (HEARTBEAT_OK)"), runtime); logInfo(success("heartbeat: ok (HEARTBEAT_OK)"), runtime);
return; return;
} }
const finalText = stripped.text || replyResult.text || ""; const finalText = stripped.text || replyPayload.text || "";
if (dryRun) { if (dryRun) {
logInfo( logInfo(
`[dry-run] heartbeat -> ${to}: ${finalText.slice(0, 200)}`, `[dry-run] heartbeat -> ${to}: ${finalText.slice(0, 200)}`,

View File

@@ -69,7 +69,7 @@ export async function startWebhook(
} }
const client = createClient(env); const client = createClient(env);
let replyResult: ReplyPayload | undefined = let replyResult: ReplyPayload | ReplyPayload[] | undefined =
autoReply !== undefined ? { text: autoReply } : undefined; autoReply !== undefined ? { text: autoReply } : undefined;
if (!replyResult) { if (!replyResult) {
replyResult = await getReplyFromConfig( replyResult = await getReplyFromConfig(
@@ -88,9 +88,13 @@ export async function startWebhook(
); );
} }
if (replyResult && (replyResult.text || replyResult.mediaUrl)) { const replyPayload = Array.isArray(replyResult)
? replyResult[0]
: replyResult;
if (replyPayload && (replyPayload.text || replyPayload.mediaUrl)) {
try { try {
let mediaUrl = replyResult.mediaUrl; let mediaUrl = replyPayload.mediaUrl;
if (mediaUrl && !/^https?:\/\//i.test(mediaUrl)) { if (mediaUrl && !/^https?:\/\//i.test(mediaUrl)) {
const hosted = await mediaHost.ensureMediaHosted(mediaUrl); const hosted = await mediaHost.ensureMediaHosted(mediaUrl);
mediaUrl = hosted.url; mediaUrl = hosted.url;
@@ -98,7 +102,7 @@ export async function startWebhook(
await client.messages.create({ await client.messages.create({
from: To, from: To,
to: From, to: From,
body: replyResult.text ?? "", body: replyPayload.text ?? "",
...(mediaUrl ? { mediaUrl: [mediaUrl] } : {}), ...(mediaUrl ? { mediaUrl: [mediaUrl] } : {}),
}); });
if (verbose) if (verbose)

View File

@@ -72,7 +72,7 @@ const formatDuration = (ms: number) =>
const DEFAULT_REPLY_HEARTBEAT_MINUTES = 30; const DEFAULT_REPLY_HEARTBEAT_MINUTES = 30;
export const HEARTBEAT_TOKEN = "HEARTBEAT_OK"; export const HEARTBEAT_TOKEN = "HEARTBEAT_OK";
export const HEARTBEAT_PROMPT = "HEARTBEAT ultrathink"; export const HEARTBEAT_PROMPT = "HEARTBEAT /think:high";
export function resolveReplyHeartbeatMinutes( export function resolveReplyHeartbeatMinutes(
cfg: ReturnType<typeof loadConfig>, cfg: ReturnType<typeof loadConfig>,