fix(infra): suppress heartbeat delivery context on subagent fallback

This commit is contained in:
Peter Steinberger
2026-04-07 01:39:08 +01:00
parent eab50fe488
commit 8daf60e2d9

View File

@@ -197,11 +197,27 @@ function resolveHeartbeatSession(
const mainEntry = store[mainSessionKey];
if (scope === "global") {
return { sessionKey: mainSessionKey, storePath, store, entry: mainEntry };
return {
sessionKey: mainSessionKey,
storePath,
store,
entry: mainEntry,
suppressOriginatingContext: false,
};
}
// Guard: never route heartbeats to subagent sessions, regardless of entry path.
const forced = forcedSessionKey?.trim();
if (forced && isSubagentSessionKey(forced)) {
return {
sessionKey: mainSessionKey,
storePath,
store,
entry: mainEntry,
suppressOriginatingContext: true,
};
}
if (forced && !isSubagentSessionKey(forced)) {
const forcedCandidate = toAgentStoreSessionKey({
agentId: resolvedAgentId,
@@ -222,6 +238,7 @@ function resolveHeartbeatSession(
storePath,
store,
entry: store[forcedCanonical],
suppressOriginatingContext: false,
};
}
}
@@ -230,12 +247,24 @@ function resolveHeartbeatSession(
const trimmed = heartbeat?.session?.trim() ?? "";
if (!trimmed || isSubagentSessionKey(trimmed)) {
return { sessionKey: mainSessionKey, storePath, store, entry: mainEntry };
return {
sessionKey: mainSessionKey,
storePath,
store,
entry: mainEntry,
suppressOriginatingContext: false,
};
}
const normalized = trimmed.toLowerCase();
if (normalized === "main" || normalized === "global") {
return { sessionKey: mainSessionKey, storePath, store, entry: mainEntry };
return {
sessionKey: mainSessionKey,
storePath,
store,
entry: mainEntry,
suppressOriginatingContext: false,
};
}
const candidate = toAgentStoreSessionKey({
@@ -244,7 +273,13 @@ function resolveHeartbeatSession(
mainKey: cfg.session?.mainKey,
});
if (isSubagentSessionKey(candidate)) {
return { sessionKey: mainSessionKey, storePath, store, entry: mainEntry };
return {
sessionKey: mainSessionKey,
storePath,
store,
entry: mainEntry,
suppressOriginatingContext: false,
};
}
const canonical = canonicalizeMainSessionAlias({
cfg,
@@ -259,11 +294,18 @@ function resolveHeartbeatSession(
storePath,
store,
entry: store[canonical],
suppressOriginatingContext: false,
};
}
}
return { sessionKey: mainSessionKey, storePath, store, entry: mainEntry };
return {
sessionKey: mainSessionKey,
storePath,
store,
entry: mainEntry,
suppressOriginatingContext: false,
};
}
function resolveHeartbeatReasoningPayloads(
@@ -589,7 +631,7 @@ export async function runHeartbeatOnce(opts: {
});
return { status: "skipped", reason: preflight.skipReason };
}
const { entry, sessionKey, storePath } = preflight.session;
const { entry, sessionKey, storePath, suppressOriginatingContext } = preflight.session;
// Check the resolved session lane — if it is busy, skip to avoid interrupting
// an active streaming turn. The wake-layer retry (heartbeat-wake.ts) will
@@ -718,8 +760,9 @@ export async function runHeartbeatOnce(opts: {
Body: appendCronStyleCurrentTimeLine(prompt, cfg, startedAt),
From: sender,
To: sender,
OriginatingChannel: delivery.channel !== "none" ? delivery.channel : undefined,
OriginatingTo: delivery.to,
OriginatingChannel:
!suppressOriginatingContext && delivery.channel !== "none" ? delivery.channel : undefined,
OriginatingTo: !suppressOriginatingContext ? delivery.to : undefined,
AccountId: delivery.accountId,
MessageThreadId: delivery.threadId,
Provider: hasExecCompletion ? "exec-event" : hasCronEvents ? "cron-event" : "heartbeat",