test(agents): consolidate run wait reply scenarios (#118469)

This commit is contained in:
Peter Steinberger
2026-08-02 21:56:57 -07:00
committed by GitHub
parent e163c3c621
commit 2bb2943e93
+184 -424
View File
@@ -460,464 +460,224 @@ describe("waitForAgentRunAndReadUpdatedAssistantReply", () => {
callGatewayMock.mockReset();
});
it("returns undefined when the latest assistant fingerprint matches the baseline", async () => {
const assistantMessage = {
role: "assistant",
content: [{ type: "text", text: "same reply" }],
timestamp: 42,
};
callGatewayMock
.mockResolvedValueOnce({
status: "ok",
})
.mockResolvedValueOnce({
messages: [assistantMessage],
});
type TranscriptMessage = Record<string, unknown>;
type WaitedReplyCase = {
name: string;
runId: string;
messages: TranscriptMessage[];
expected: Record<string, unknown>;
baseline?: { text?: string; fingerprint?: string };
sessionKey?: string;
wait?: Record<string, unknown>;
};
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
const assistant = (text: string, metadata: TranscriptMessage = {}): TranscriptMessage => ({
role: "assistant",
content: [{ type: "text", text }],
...metadata,
});
const interSession = (text: string, metadata: TranscriptMessage = {}): TranscriptMessage =>
assistant(text, {
provenance: {
kind: "inter_session",
sourceSessionKey: "agent:main:source",
sourceTool: "sessions_send",
},
...metadata,
});
const messageToolMirror = (
text: string,
mirror: TranscriptMessage,
metadata: TranscriptMessage = {},
): TranscriptMessage =>
assistant(text, {
openclawMessageToolMirror: { toolName: "message", ...mirror },
...metadata,
});
const sameReply = assistant("same reply", { timestamp: 42 });
const previousReply = assistant("previous real reply", { timestamp: 41 });
const forwardedRequest = interSession("forwarded request", {
__openclaw: { seq: 41 },
timestamp: 41,
});
const pendingSourceReply = messageToolMirror(
"source reply awaiting delivery",
{
toolCallId: "call-message-send",
sourceReplySink: "internal-ui",
sourceMessageSeq: 42,
},
{ timestamp: 42 },
);
const olderBaseline = { text: "older reply", fingerprint: "old-fingerprint" };
const cases: WaitedReplyCase[] = [
{
name: "returns undefined when the latest assistant fingerprint matches the baseline",
runId: "run-1",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
baseline: {
text: "same reply",
fingerprint: JSON.stringify(assistantMessage),
},
});
expect(result).toEqual({
status: "ok",
replyText: undefined,
});
});
it("returns undefined when a text-only baseline matches the latest assistant reply", async () => {
callGatewayMock
.mockResolvedValueOnce({
status: "ok",
})
.mockResolvedValueOnce({
messages: [
{
role: "assistant",
content: [{ type: "text", text: "same reply" }],
timestamp: 42,
},
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
messages: [sameReply],
baseline: { text: "same reply", fingerprint: JSON.stringify(sameReply) },
expected: { status: "ok", replyText: undefined },
},
{
name: "returns undefined when a text-only baseline matches the latest assistant reply",
runId: "run-text-baseline",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
baseline: {
text: "same reply",
},
});
expect(result).toEqual({
status: "ok",
replyText: undefined,
});
});
it("does not treat a message-tool delivery mirror as a new waited reply", async () => {
const baselineMessage = {
role: "assistant",
content: [{ type: "text", text: "previous real reply" }],
timestamp: 41,
};
callGatewayMock
.mockResolvedValueOnce({
status: "ok",
})
.mockResolvedValueOnce({
messages: [
baselineMessage,
{
role: "assistant",
provider: "openclaw",
model: "delivery-mirror",
content: [{ type: "text", text: "already delivered source reply" }],
timestamp: 42,
},
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
messages: [sameReply],
baseline: { text: "same reply" },
expected: { status: "ok", replyText: undefined },
},
{
name: "does not treat a message-tool delivery mirror as a new waited reply",
runId: "run-source-reply",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
baseline: {
text: "previous real reply",
fingerprint: JSON.stringify(baselineMessage),
},
});
expect(result).toEqual({
status: "ok",
replyText: undefined,
});
});
it("does not treat a projected message-tool mirror as a new waited reply", async () => {
const baselineMessage = {
role: "assistant",
content: [{ type: "text", text: "previous real reply" }],
timestamp: 41,
};
callGatewayMock
.mockResolvedValueOnce({
status: "ok",
})
.mockResolvedValueOnce({
messages: [
baselineMessage,
{
role: "assistant",
content: [{ type: "text", text: "already delivered source reply" }],
openclawMessageToolMirror: {
toolName: "message",
toolCallId: "call-message-send",
},
timestamp: 42,
},
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
runId: "run-projected-source-reply",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
baseline: {
text: "previous real reply",
fingerprint: JSON.stringify(baselineMessage),
},
});
expect(result).toEqual({
status: "ok",
replyText: undefined,
});
});
it("returns a projected message-tool reply held for outer A2A delivery", async () => {
callGatewayMock.mockResolvedValueOnce({ status: "ok" }).mockResolvedValueOnce({
messages: [
{
role: "assistant",
provenance: {
kind: "inter_session",
sourceSessionKey: "agent:main:source",
sourceTool: "sessions_send",
},
content: [{ type: "text", text: "forwarded request" }],
__openclaw: { seq: 41 },
timestamp: 41,
},
{
role: "assistant",
content: [{ type: "text", text: "source reply awaiting delivery" }],
openclawMessageToolMirror: {
toolName: "message",
toolCallId: "call-message-send",
sourceReplySink: "internal-ui",
sourceMessageSeq: 42,
},
previousReply,
assistant("already delivered source reply", {
provider: "openclaw",
model: "delivery-mirror",
timestamp: 42,
},
}),
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
baseline: { text: "previous real reply", fingerprint: JSON.stringify(previousReply) },
expected: { status: "ok", replyText: undefined },
},
{
name: "does not treat a projected message-tool mirror as a new waited reply",
runId: "run-projected-source-reply",
messages: [
previousReply,
messageToolMirror(
"already delivered source reply",
{ toolCallId: "call-message-send" },
{ timestamp: 42 },
),
],
baseline: { text: "previous real reply", fingerprint: JSON.stringify(previousReply) },
expected: { status: "ok", replyText: undefined },
},
{
name: "returns a projected message-tool reply held for outer A2A delivery",
runId: "run-internal-source-reply",
sessionKey: "agent:worker:main",
timeoutMs: 1_000,
});
expect(result).toEqual({
status: "ok",
replyText: "source reply awaiting delivery",
});
});
it("prefers an internal source reply over a later private final", async () => {
callGatewayMock.mockResolvedValueOnce({ status: "ok" }).mockResolvedValueOnce({
messages: [
{
role: "assistant",
provenance: {
kind: "inter_session",
sourceSessionKey: "agent:main:source",
sourceTool: "sessions_send",
},
content: [{ type: "text", text: "forwarded request" }],
__openclaw: { seq: 41 },
timestamp: 41,
},
{
role: "assistant",
content: [{ type: "text", text: "source reply awaiting delivery" }],
openclawMessageToolMirror: {
toolName: "message",
toolCallId: "call-message-send",
sourceReplySink: "internal-ui",
sourceMessageSeq: 42,
},
timestamp: 42,
},
{
role: "assistant",
content: [{ type: "text", text: "Done" }],
timestamp: 43,
},
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
messages: [forwardedRequest, pendingSourceReply],
expected: { status: "ok", replyText: "source reply awaiting delivery" },
},
{
name: "prefers an internal source reply over a later private final",
runId: "run-internal-source-reply-with-private-final",
sessionKey: "agent:worker:main",
timeoutMs: 1_000,
});
expect(result).toEqual({
status: "ok",
replyText: "source reply awaiting delivery",
});
});
it("does not let a late internal result cross an inter-session turn boundary", async () => {
callGatewayMock.mockResolvedValueOnce({ status: "ok" }).mockResolvedValueOnce({
messages: [forwardedRequest, pendingSourceReply, assistant("Done", { timestamp: 43 })],
expected: { status: "ok", replyText: "source reply awaiting delivery" },
},
{
name: "does not let a late internal result cross an inter-session turn boundary",
runId: "run-after-late-internal-source-reply",
sessionKey: "agent:worker:main",
messages: [
{
role: "assistant",
provenance: {
kind: "inter_session",
sourceSessionKey: "agent:main:source",
sourceTool: "sessions_send",
},
content: [{ type: "text", text: "new forwarded request" }],
__openclaw: { seq: 42 },
timestamp: 42,
},
{
role: "assistant",
content: [{ type: "text", text: "stale source reply" }],
openclawMessageToolMirror: {
toolName: "message",
interSession("new forwarded request", { __openclaw: { seq: 42 }, timestamp: 42 }),
messageToolMirror(
"stale source reply",
{
toolCallId: "call-message-before-request",
sourceReplySink: "internal-ui",
sourceMessageSeq: 41,
},
timestamp: 41,
},
{
role: "assistant",
content: [{ type: "text", text: "fresh reply" }],
timestamp: 43,
},
{ timestamp: 41 },
),
assistant("fresh reply", { timestamp: 43 }),
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
runId: "run-after-late-internal-source-reply",
sessionKey: "agent:worker:main",
timeoutMs: 1_000,
});
expect(result).toEqual({
status: "ok",
replyText: "fresh reply",
});
});
it("does not return a private final written after a message-tool delivery mirror", async () => {
callGatewayMock.mockResolvedValueOnce({ status: "ok" }).mockResolvedValueOnce({
messages: [
{
role: "assistant",
provenance: {
kind: "inter_session",
sourceSessionKey: "agent:main:source",
sourceTool: "sessions_send",
},
content: [{ type: "text", text: "forwarded request" }],
timestamp: 41,
},
{
role: "assistant",
content: [{ type: "text", text: "already delivered source reply" }],
openclawMessageToolMirror: {
toolName: "message",
toolCallId: "call-message-send",
},
timestamp: 42,
},
{
role: "assistant",
content: [{ type: "text", text: "Done" }],
timestamp: 43,
},
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
expected: { status: "ok", replyText: "fresh reply" },
},
{
name: "does not return a private final written after a message-tool delivery mirror",
runId: "run-source-reply-with-private-final",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
});
expect(result).toEqual({
status: "ok",
replyText: undefined,
});
});
it("does not let an older turn's message-tool mirror suppress a fresh reply", async () => {
callGatewayMock.mockResolvedValueOnce({ status: "ok" }).mockResolvedValueOnce({
messages: [
{
role: "assistant",
content: [{ type: "text", text: "older delivered reply" }],
openclawMessageToolMirror: {
toolName: "message",
toolCallId: "call-older-message-send",
},
timestamp: 40,
},
{
role: "assistant",
provenance: {
kind: "inter_session",
sourceSessionKey: "agent:main:source",
sourceTool: "sessions_send",
},
content: [{ type: "text", text: "new forwarded request" }],
timestamp: 41,
},
{
role: "assistant",
content: [{ type: "text", text: "fresh reply" }],
timestamp: 42,
},
interSession("forwarded request", { timestamp: 41 }),
messageToolMirror(
"already delivered source reply",
{ toolCallId: "call-message-send" },
{ timestamp: 42 },
),
assistant("Done", { timestamp: 43 }),
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
expected: { status: "ok", replyText: undefined },
},
{
name: "does not let an older turn's message-tool mirror suppress a fresh reply",
runId: "run-after-older-source-reply",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
});
expect(result).toEqual({
status: "ok",
replyText: "fresh reply",
});
});
it("does not resurrect an older reply when only a delivery mirror is newer", async () => {
callGatewayMock
.mockResolvedValueOnce({
status: "ok",
})
.mockResolvedValueOnce({
messages: [
{
role: "assistant",
content: [{ type: "text", text: "stale previous reply" }],
timestamp: 41,
},
{
role: "assistant",
provider: "openclaw",
model: "delivery-mirror",
content: [{ type: "text", text: "already delivered source reply" }],
timestamp: 42,
},
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
messages: [
messageToolMirror(
"older delivered reply",
{ toolCallId: "call-older-message-send" },
{ timestamp: 40 },
),
interSession("new forwarded request", { timestamp: 41 }),
assistant("fresh reply", { timestamp: 42 }),
],
expected: { status: "ok", replyText: "fresh reply" },
},
{
name: "does not resurrect an older reply when only a delivery mirror is newer",
runId: "run-source-reply-without-baseline",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
});
expect(result).toEqual({
status: "ok",
replyText: undefined,
});
});
it("returns the new assistant text when the fingerprint changes", async () => {
callGatewayMock
.mockResolvedValueOnce({
status: "ok",
})
.mockResolvedValueOnce({
messages: [
{
role: "assistant",
content: [{ type: "text", text: "fresh reply" }],
timestamp: 99,
},
],
});
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
messages: [
assistant("stale previous reply", { timestamp: 41 }),
assistant("already delivered source reply", {
provider: "openclaw",
model: "delivery-mirror",
timestamp: 42,
}),
],
expected: { status: "ok", replyText: undefined },
},
{
name: "returns the new assistant text when the fingerprint changes",
runId: "run-2",
sessionKey: "agent:main:child",
timeoutMs: 1_000,
baseline: {
text: "older reply",
fingerprint: "old-fingerprint",
},
});
expect(result).toEqual({
status: "ok",
replyText: "fresh reply",
});
});
it("preserves successful wait metadata when returning an updated reply", async () => {
callGatewayMock
.mockResolvedValueOnce({
messages: [assistant("fresh reply", { timestamp: 99 })],
baseline: olderBaseline,
expected: { status: "ok", replyText: "fresh reply" },
},
{
name: "preserves successful wait metadata when returning an updated reply",
runId: "run-with-metadata",
messages: [assistant("fresh reply", { timestamp: 99 })],
baseline: olderBaseline,
wait: {
status: "ok",
startedAt: 100,
endedAt: 200,
stopReason: "completed",
yielded: true,
providerStarted: true,
})
.mockResolvedValueOnce({
messages: [
{
role: "assistant",
content: [{ type: "text", text: "fresh reply" }],
timestamp: 99,
},
],
});
},
expected: {
status: "ok",
startedAt: 100,
endedAt: 200,
stopReason: "completed",
yielded: true,
providerStarted: true,
replyText: "fresh reply",
},
},
];
it.each(cases)("$name", async ({ runId, messages, expected, baseline, sessionKey, wait }) => {
callGatewayMock
.mockResolvedValueOnce(wait ?? { status: "ok" })
.mockResolvedValueOnce({ messages });
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
runId: "run-with-metadata",
sessionKey: "agent:main:child",
runId,
sessionKey: sessionKey ?? "agent:main:child",
timeoutMs: 1_000,
baseline: {
text: "older reply",
fingerprint: "old-fingerprint",
},
baseline,
});
expect(result).toEqual({
status: "ok",
startedAt: 100,
endedAt: 200,
stopReason: "completed",
yielded: true,
providerStarted: true,
replyText: "fresh reply",
});
expect(result).toEqual(expected);
expect(callGatewayMock.mock.calls.map(([request]) => request.method)).toEqual([
"agent.wait",
"chat.history",
]);
});
});