Skip to content

Commit eb04aad

Browse files
fix(clownfish): address review for ghcrawl-143801-autonomous-smoke (1)
Co-authored-by: Merkava <[email protected]>
1 parent be3e3eb commit eb04aad

2 files changed

Lines changed: 70 additions & 16 deletions

File tree

src/infra/heartbeat-runner.isolated-session-mirror.test.ts

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,28 @@ function latestDeliveryRequest(): DeliveryRequest {
4040
return request as DeliveryRequest;
4141
}
4242

43+
function makeIsolatedLastTargetConfig(tmpDir: string, storePath: string): OpenClawConfig {
44+
return {
45+
agents: {
46+
list: [{ id: "main", default: true }],
47+
defaults: {
48+
workspace: tmpDir,
49+
heartbeat: {
50+
every: "5m",
51+
target: "last",
52+
isolatedSession: true,
53+
},
54+
},
55+
},
56+
channels: { whatsapp: { allowFrom: ["*"] } },
57+
session: { store: storePath },
58+
};
59+
}
60+
4361
describe("runHeartbeatOnce - isolated heartbeat outbound session mirror", () => {
4462
it("uses the isolated run key for outbound delivery while base session owns target and state", async () => {
4563
await withTempHeartbeatSandbox(async ({ tmpDir, storePath, replySpy }) => {
46-
const cfg: OpenClawConfig = {
47-
agents: {
48-
list: [{ id: "main", default: true }],
49-
defaults: {
50-
workspace: tmpDir,
51-
heartbeat: {
52-
every: "5m",
53-
target: "last",
54-
isolatedSession: true,
55-
},
56-
},
57-
},
58-
channels: { whatsapp: { allowFrom: ["*"] } },
59-
session: { store: storePath },
60-
};
64+
const cfg = makeIsolatedLastTargetConfig(tmpDir, storePath);
6165
const baseSessionKey = resolveMainSessionKey(cfg);
6266
const isolatedSessionKey = `${baseSessionKey}:heartbeat`;
6367
const nowMs = Date.now();
@@ -123,4 +127,52 @@ describe("runHeartbeatOnce - isolated heartbeat outbound session mirror", () =>
123127
expect(store[isolatedSessionKey]?.lastHeartbeatText).toBeUndefined();
124128
});
125129
});
130+
131+
it("keeps the base policy key when wake re-entry starts from the isolated key", async () => {
132+
await withTempHeartbeatSandbox(async ({ tmpDir, storePath, replySpy }) => {
133+
const cfg = makeIsolatedLastTargetConfig(tmpDir, storePath);
134+
const baseSessionKey = resolveMainSessionKey(cfg);
135+
const isolatedSessionKey = `${baseSessionKey}:heartbeat`;
136+
const nowMs = Date.now();
137+
138+
await fs.writeFile(
139+
storePath,
140+
JSON.stringify({
141+
[isolatedSessionKey]: {
142+
sessionId: "isolated-session",
143+
updatedAt: nowMs - 1_000,
144+
lastChannel: "whatsapp",
145+
lastProvider: "whatsapp",
146+
lastTo: "+15551234567",
147+
heartbeatIsolatedBaseSessionKey: baseSessionKey,
148+
},
149+
}),
150+
"utf-8",
151+
);
152+
replySpy.mockResolvedValueOnce({ text: "Wake result needs attention." });
153+
154+
const result = await runHeartbeatOnce({
155+
cfg,
156+
sessionKey: isolatedSessionKey,
157+
deps: {
158+
getReplyFromConfig: replySpy,
159+
getQueueSize: () => 0,
160+
nowMs: () => nowMs,
161+
},
162+
});
163+
164+
expect(result.status).toBe("ran");
165+
expect(replySpy.mock.calls[0]?.[0]).toMatchObject({
166+
SessionKey: isolatedSessionKey,
167+
});
168+
expect(latestDeliveryRequest()).toMatchObject({
169+
channel: "whatsapp",
170+
to: "+15551234567",
171+
session: {
172+
key: isolatedSessionKey,
173+
policyKey: baseSessionKey,
174+
},
175+
});
176+
});
177+
});
126178
});

src/infra/heartbeat-runner.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,7 @@ export async function runHeartbeatOnce(opts: {
15591559
}
15601560

15611561
let runSessionKey = sessionKey;
1562+
let outboundPolicySessionKey: string | undefined;
15621563
if (useIsolatedSession) {
15631564
const configuredSession = resolveHeartbeatSession(cfg, agentId, heartbeat);
15641565
// Collapse only the repeated `:heartbeat` suffixes introduced by wake-triggered
@@ -1628,6 +1629,7 @@ export async function runHeartbeatOnce(opts: {
16281629
}
16291630
}
16301631
runSessionKey = isolatedSessionKey;
1632+
outboundPolicySessionKey = isolatedBaseSessionKey;
16311633
}
16321634
// Update task last run times AFTER successful heartbeat completion
16331635
const updateTaskTimestamps = async () => {
@@ -1708,7 +1710,7 @@ export async function runHeartbeatOnce(opts: {
17081710
cfg,
17091711
agentId,
17101712
sessionKey: runSessionKey,
1711-
policySessionKey: runSessionKey === sessionKey ? undefined : sessionKey,
1713+
policySessionKey: outboundPolicySessionKey,
17121714
});
17131715
const canAttemptHeartbeatOk = Boolean(
17141716
!hasDueCommitments && visibility.showOk && delivery.channel !== "none" && delivery.to,

0 commit comments

Comments
 (0)