fix: wait on agent.wait for sessions_send

This commit is contained in:
Peter Steinberger
2026-01-04 01:15:23 +01:00
parent 412e8b3aee
commit e3c543ec06
8 changed files with 362 additions and 40 deletions

View File

@@ -123,17 +123,29 @@ describe("sessions tools", () => {
it("sessions_send supports fire-and-forget and wait", async () => {
callGatewayMock.mockReset();
const calls: Array<{ method?: string; expectFinal?: boolean }> = [];
const calls: Array<{ method?: string; params?: unknown }> = [];
callGatewayMock.mockImplementation(async (opts: unknown) => {
const request = opts as { method?: string; expectFinal?: boolean };
const request = opts as { method?: string; params?: unknown };
calls.push(request);
if (request.method === "agent") {
return { runId: "run-1", status: "accepted" };
return { runId: "run-1", status: "accepted", acceptedAt: 1234 };
}
if (request.method === "agent.wait") {
return { runId: "run-1", status: "ok" };
}
if (request.method === "chat.history") {
return {
messages: [
{ role: "assistant", content: [{ type: "text", text: "done" }] },
{
role: "assistant",
content: [
{
type: "text",
text: "done",
},
],
timestamp: 20,
},
],
};
}
@@ -156,7 +168,7 @@ describe("sessions tools", () => {
const waitPromise = tool.execute("call6", {
sessionKey: "main",
message: "wait",
timeoutSeconds: 5,
timeoutSeconds: 1,
});
const waited = await waitPromise;
expect(waited.details).toMatchObject({
@@ -166,7 +178,13 @@ describe("sessions tools", () => {
});
const agentCalls = calls.filter((call) => call.method === "agent");
expect(agentCalls[0]?.expectFinal).toBeUndefined();
expect(agentCalls[1]?.expectFinal).toBe(true);
const waitCalls = calls.filter((call) => call.method === "agent.wait");
const historyOnlyCalls = calls.filter(
(call) => call.method === "chat.history",
);
expect(agentCalls).toHaveLength(2);
expect(waitCalls).toHaveLength(1);
expect(historyOnlyCalls).toHaveLength(1);
expect(waitCalls[0]?.params).toMatchObject({ afterMs: 1234 });
});
});

View File

@@ -1536,7 +1536,8 @@ function createNodesTool(): AnyAgentTool {
const node = readStringParam(params, "node", { required: true });
const nodeId = await resolveNodeId(gatewayOpts, node);
const maxAgeMs =
typeof params.maxAgeMs === "number" && Number.isFinite(params.maxAgeMs)
typeof params.maxAgeMs === "number" &&
Number.isFinite(params.maxAgeMs)
? params.maxAgeMs
: undefined;
const desiredAccuracy =
@@ -2736,7 +2737,7 @@ function createSessionsSendTool(): AnyAgentTool {
? Math.max(0, Math.floor(params.timeoutSeconds))
: 30;
const idempotencyKey = crypto.randomUUID();
let runId = idempotencyKey;
let runId: string = idempotencyKey;
const displayKey = resolveDisplaySessionKey({
key: sessionKey,
alias,
@@ -2776,39 +2777,22 @@ function createSessionsSendTool(): AnyAgentTool {
}
}
let acceptedAt: number | undefined;
try {
const response = (await callGateway({
method: "agent",
params: sendParams,
expectFinal: true,
timeoutMs: timeoutSeconds * 1000,
})) as { runId?: string; status?: string };
timeoutMs: 10_000,
})) as { runId?: string; acceptedAt?: number };
if (typeof response?.runId === "string" && response.runId) {
runId = response.runId;
}
if (typeof response?.acceptedAt === "number") {
acceptedAt = response.acceptedAt;
}
} catch (err) {
const message =
err instanceof Error ? err.message : String(err ?? "error");
if (message.includes("gateway timeout")) {
try {
const cached = (await callGateway({
method: "agent",
params: sendParams,
timeoutMs: 5_000,
})) as { runId?: string };
if (typeof cached?.runId === "string" && cached.runId) {
runId = cached.runId;
}
} catch {
/* ignore */
}
return jsonResult({
runId,
status: "timeout",
error: message,
sessionKey: displayKey,
});
}
return jsonResult({
runId,
status: "error",
@@ -2817,6 +2801,49 @@ function createSessionsSendTool(): AnyAgentTool {
});
}
const timeoutMs = timeoutSeconds * 1000;
let waitStatus: string | undefined;
let waitError: string | undefined;
try {
const wait = (await callGateway({
method: "agent.wait",
params: {
runId,
afterMs: acceptedAt,
timeoutMs,
},
timeoutMs: timeoutMs + 2000,
})) as { status?: string; error?: string };
waitStatus = typeof wait?.status === "string" ? wait.status : undefined;
waitError = typeof wait?.error === "string" ? wait.error : undefined;
} catch (err) {
const message =
err instanceof Error ? err.message : String(err ?? "error");
return jsonResult({
runId,
status: message.includes("gateway timeout") ? "timeout" : "error",
error: message,
sessionKey: displayKey,
});
}
if (waitStatus === "timeout") {
return jsonResult({
runId,
status: "timeout",
error: waitError,
sessionKey: displayKey,
});
}
if (waitStatus === "error") {
return jsonResult({
runId,
status: "error",
error: waitError ?? "agent error",
sessionKey: displayKey,
});
}
const history = (await callGateway({
method: "chat.history",
params: { sessionKey: resolvedKey, limit: 50 },