fix: avoid blocking terminal trace reads
This commit is contained in:
@@ -2510,6 +2510,143 @@ test("cloud api trace skips AgentRun refresh when terminal snapshot is already c
|
||||
}
|
||||
});
|
||||
|
||||
test("cloud api trace schedules terminal side effects without blocking reads (#1422)", async () => {
|
||||
const ownerCalls = [];
|
||||
const factCalls = [];
|
||||
let releaseOwnerWrite;
|
||||
let ownerWriteReleased = false;
|
||||
const ownerWrite = new Promise((resolve) => {
|
||||
releaseOwnerWrite = () => {
|
||||
if (ownerWriteReleased) return;
|
||||
ownerWriteReleased = true;
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
const traceId = "trc_issue1422_trace_effects_async";
|
||||
const runId = "run_issue1422_trace_effects_async";
|
||||
const commandId = "cmd_issue1422_trace_effects_async";
|
||||
const finalText = "trace reads should not wait for terminal side effects.";
|
||||
const traceStore = createCodeAgentTraceStore();
|
||||
traceStore.append(traceId, {
|
||||
type: "backend",
|
||||
status: "running",
|
||||
label: "agentrun:backend:run-created",
|
||||
source: "agentrun",
|
||||
sourceSeq: 1,
|
||||
runId,
|
||||
commandId,
|
||||
valuesPrinted: false
|
||||
});
|
||||
traceStore.append(traceId, {
|
||||
type: "result",
|
||||
status: "completed",
|
||||
label: "agentrun:terminal:completed",
|
||||
source: "agentrun",
|
||||
sourceSeq: 3,
|
||||
runId,
|
||||
commandId,
|
||||
terminal: true,
|
||||
valuesPrinted: false
|
||||
});
|
||||
const codeAgentChatResults = new Map([[traceId, {
|
||||
accepted: true,
|
||||
status: "completed",
|
||||
shortConnection: true,
|
||||
traceId,
|
||||
conversationId: "cnv_issue1422_trace_effects_async",
|
||||
sessionId: "ses_issue1422_trace_effects_async",
|
||||
threadId: "thread-issue1422-trace-effects-async",
|
||||
ownerUserId: TEST_AGENT_ACTOR.id,
|
||||
ownerRole: TEST_AGENT_ACTOR.role,
|
||||
finalResponse: { text: finalText, textChars: finalText.length, role: "assistant", status: "completed", traceId, valuesPrinted: false },
|
||||
traceSummary: {
|
||||
traceId,
|
||||
source: "agentrun-command-result",
|
||||
sourceEventCount: 3,
|
||||
terminalStatus: "completed",
|
||||
finalAssistantRow: { role: "assistant", status: "completed", textChars: finalText.length, textPreview: finalText, messageId: `msg_${traceId.slice(4)}`, valuesPrinted: false },
|
||||
agentRun: { runId, commandId, lastSeq: 3, valuesPrinted: false },
|
||||
valuesPrinted: false
|
||||
},
|
||||
agentRun: {
|
||||
adapter: "agentrun-v01",
|
||||
managerUrl: "http://127.0.0.1:1",
|
||||
runId,
|
||||
commandId,
|
||||
traceId,
|
||||
lastSeq: 3,
|
||||
status: "completed",
|
||||
commandState: "completed",
|
||||
terminalStatus: "completed",
|
||||
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
|
||||
valuesPrinted: false
|
||||
},
|
||||
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
|
||||
valuesPrinted: false
|
||||
}]]);
|
||||
const server = createCloudApiServer({
|
||||
traceStore,
|
||||
codeAgentChatResults,
|
||||
env: {
|
||||
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601",
|
||||
HWLAB_ENVIRONMENT: "v02",
|
||||
HWLAB_GITOPS_PROFILE: "v02"
|
||||
},
|
||||
sessionRegistry: {
|
||||
recordFact(conversationId, fact) {
|
||||
factCalls.push({ conversationId, fact });
|
||||
return { ok: true };
|
||||
}
|
||||
},
|
||||
accessController: {
|
||||
required: false,
|
||||
async authenticate() {
|
||||
return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION };
|
||||
},
|
||||
async recordAgentSessionOwner(input) {
|
||||
ownerCalls.push(input);
|
||||
await ownerWrite;
|
||||
return { ok: true, sessionId: input.sessionId };
|
||||
}
|
||||
}
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
|
||||
try {
|
||||
const { port } = server.address();
|
||||
const fetchTrace = async () => {
|
||||
const response = await Promise.race([
|
||||
fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}?limit=1`, {
|
||||
headers: { cookie: "hwlab_session=test-stub-session" }
|
||||
}),
|
||||
delay(250).then(() => null)
|
||||
]);
|
||||
assert.ok(response, "trace read should not wait for terminal side effects to finish");
|
||||
assert.equal(response.status, 200);
|
||||
const body = await response.json();
|
||||
assert.equal(body.status, "completed");
|
||||
assert.equal(body.eventCount, 2);
|
||||
assert.equal(body.finalResponse.text, finalText);
|
||||
};
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
await fetchTrace();
|
||||
}
|
||||
for (let i = 0; i < 20 && ownerCalls.length < 1; i += 1) await delay(10);
|
||||
assert.equal(ownerCalls.length, 1);
|
||||
assert.equal(factCalls.length, 1);
|
||||
assert.equal(codeAgentChatResults.get(traceId).turnStatusTerminalEffects.pending, true);
|
||||
releaseOwnerWrite();
|
||||
for (let i = 0; i < 20 && codeAgentChatResults.get(traceId).turnStatusTerminalEffects?.recorded !== true; i += 1) await delay(10);
|
||||
assert.equal(codeAgentChatResults.get(traceId).turnStatusTerminalEffects.recorded, true);
|
||||
} finally {
|
||||
releaseOwnerWrite?.();
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close((error) => (error ? reject(error) : resolve()));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("cloud api turn status skips AgentRun refresh for complete terminal evidence (#1422)", async () => {
|
||||
const calls = [];
|
||||
const traceId = "trc_issue1422_terminal_fast_path";
|
||||
|
||||
@@ -2564,9 +2564,7 @@ export async function handleCodeAgentTraceHttp(request, response, url, options)
|
||||
agentRunResult = synced.result ?? agentRunResult;
|
||||
}
|
||||
if (isTraceCommandTerminalStatus(agentRunResult?.status)) {
|
||||
await finalizeCodeAgentBillingUsage({ payload: agentRunResult, params: agentRunResult, options: requestOptions });
|
||||
recordCodeAgentConversationFact(agentRunResult, requestOptions);
|
||||
await recordCodeAgentSessionOwner({ payload: agentRunResult, params: agentRunResult, options: requestOptions, status: codeAgentOwnerStatusForResult(agentRunResult), preserveLastTraceId: true });
|
||||
scheduleCodeAgentTerminalTurnStatusEffects({ payload: agentRunResult, params: agentRunResult, options: requestOptions, preserveLastTraceId: true });
|
||||
}
|
||||
} catch (error) {
|
||||
refreshError = error;
|
||||
|
||||
Reference in New Issue
Block a user