fix: preserve latest trace during historical repair
This commit is contained in:
@@ -1847,12 +1847,43 @@ function publicSession(session) { return { id: session.id, userId: session.userI
|
||||
function publicAccessTuple(tuple) { return tuple ? { userId: tuple.userId, relation: tuple.relation, object: tuple.object, createdByAdminId: tuple.createdByAdminId, createdAt: tuple.createdAt } : null; }
|
||||
function toolsFromTuples(tuples = []) { return Object.fromEntries(HWLAB_TOOL_IDS.map((toolId) => [toolId, tuples.some((tuple) => tuple.object === openFgaObject("tool", toolId) && tuple.relation === "can_use")])); }
|
||||
function defaultDeniedToolCapabilities() { return { contractVersion: "admin-access-v1", actor: null, tools: Object.fromEntries(HWLAB_TOOL_IDS.map((toolId) => [toolId, { allowed: false, authorization: { contractVersion: "openfga-authorization-v1", mode: "enforce", allowed: false, decisionSource: "missing-owner", degraded: false } }])), valuesRedacted: true }; }
|
||||
function normalizeAgentSessionOwnerRecord(input, existing, now) { return { id: input.sessionId, projectId: input.projectId ?? existing?.projectId ?? "prj_v02_code_agent", agentId: input.agentId ?? existing?.agentId ?? "hwlab-code-agent", status: input.status ?? existing?.status ?? "active", startedAt: existing?.startedAt ?? input.startedAt ?? now, endedAt: input.endedAt ?? existing?.endedAt ?? null, ownerUserId: input.ownerUserId, conversationId: input.conversationId ?? existing?.conversationId ?? null, threadId: input.threadId ?? existing?.threadId ?? null, lastTraceId: input.traceId ?? input.lastTraceId ?? existing?.lastTraceId ?? null, session: mergeAgentSessionOwnerEvidence(input.session, existing?.session), updatedAt: now }; }
|
||||
function mergeAgentSessionOwnerEvidence(nextValue, existingValue) {
|
||||
function normalizeAgentSessionOwnerRecord(input, existing, now) {
|
||||
const nextTraceId = safeTraceIdLocal(input.traceId ?? input.lastTraceId);
|
||||
const existingLastTraceId = safeTraceIdLocal(existing?.lastTraceId);
|
||||
const historicalTraceUpdate = Boolean(nextTraceId && existingLastTraceId && nextTraceId !== existingLastTraceId && isHistoricalTraceBackfill(input.session, nextTraceId));
|
||||
return {
|
||||
id: input.sessionId,
|
||||
projectId: input.projectId ?? existing?.projectId ?? "prj_v02_code_agent",
|
||||
agentId: input.agentId ?? existing?.agentId ?? "hwlab-code-agent",
|
||||
status: input.status ?? existing?.status ?? "active",
|
||||
startedAt: existing?.startedAt ?? input.startedAt ?? now,
|
||||
endedAt: input.endedAt ?? existing?.endedAt ?? null,
|
||||
ownerUserId: input.ownerUserId,
|
||||
conversationId: input.conversationId ?? existing?.conversationId ?? null,
|
||||
threadId: input.threadId ?? existing?.threadId ?? null,
|
||||
lastTraceId: historicalTraceUpdate ? existingLastTraceId : nextTraceId || existingLastTraceId || null,
|
||||
session: mergeAgentSessionOwnerEvidence(input.session, existing?.session, { historicalTraceUpdate }),
|
||||
updatedAt: now
|
||||
};
|
||||
}
|
||||
function isHistoricalTraceBackfill(sessionValue, traceId) {
|
||||
const snapshot = normalizeObject(sessionValue);
|
||||
if (snapshot.historicalTraceBackfill === true || snapshot.source === "agent-session-trace-repair") return true;
|
||||
if (Array.isArray(snapshot.messages) && snapshot.messages.length > 0) return false;
|
||||
if (agentSessionTraceEvidence({ session: snapshot }, traceId)) return true;
|
||||
return Boolean(snapshot.finalResponse || snapshot.traceSummary || snapshot.agentRun);
|
||||
}
|
||||
function mergeAgentSessionOwnerEvidence(nextValue, existingValue, options = {}) {
|
||||
const existing = normalizeObject(existingValue);
|
||||
const next = normalizeObject(nextValue);
|
||||
const merged = { ...existing, ...next };
|
||||
merged.traceResults = mergeAgentSessionTraceResults(existing.traceResults, next.traceResults ?? next.traceResult ?? next);
|
||||
merged.traceResults = mergeAgentSessionTraceResults(mergeAgentSessionTraceResults(existing.traceResults, existing), next.traceResults ?? next.traceResult ?? next);
|
||||
if (options.historicalTraceUpdate === true) {
|
||||
for (const field of ["agentRun", "finalResponse", "traceSummary", "traceId", "sessionStatus", "sessionLifecycleStatus"]) {
|
||||
if (existing[field] !== undefined) merged[field] = existing[field];
|
||||
else delete merged[field];
|
||||
}
|
||||
}
|
||||
const existingMessages = Array.isArray(existing.messages) ? existing.messages : null;
|
||||
const nextMessages = Array.isArray(next.messages) ? next.messages : null;
|
||||
if (nextMessages?.length) {
|
||||
|
||||
@@ -613,7 +613,9 @@ export async function loadPersistedAgentRunResult(traceId, options = {}) {
|
||||
const latestTraceId = safeTraceId(session?.lastTraceId ?? session?.session?.lastTraceId ?? session?.session?.traceId);
|
||||
const topLevelAgentRun = session?.session?.agentRun && typeof session.session.agentRun === "object" ? session.session.agentRun : null;
|
||||
const agentRunTraceId = safeTraceId(topLevelAgentRun?.traceId);
|
||||
const mayUseTopLevelAgentRun = latestTraceId === safeId || agentRunTraceId === safeId || (!latestTraceId && !agentRunTraceId);
|
||||
const topLevelFinalTraceId = safeTraceId(session?.session?.finalResponse?.traceId ?? session?.session?.traceSummary?.traceId);
|
||||
const topLevelConflictsTrace = (agentRunTraceId && agentRunTraceId !== safeId) || (topLevelFinalTraceId && topLevelFinalTraceId !== safeId);
|
||||
const mayUseTopLevelAgentRun = !topLevelConflictsTrace && (latestTraceId === safeId || agentRunTraceId === safeId || topLevelFinalTraceId === safeId || (!latestTraceId && !agentRunTraceId && !topLevelFinalTraceId));
|
||||
const agentRun = traceEvidence?.agentRun ?? (mayUseTopLevelAgentRun ? topLevelAgentRun : null);
|
||||
const restored = persistedAgentRunResultFromSession({ traceId: safeId, session, agentRun, traceEvidence, options });
|
||||
if (restored) return restored;
|
||||
|
||||
@@ -1880,6 +1880,23 @@ test("cloud api repairs historical same-session AgentRun trace after lastTraceId
|
||||
assert.ok(repair);
|
||||
assert.equal(repair.session.traceResults[firstTraceId].agentRun.commandId, firstCommandId);
|
||||
assert.equal(ownerSessions.get("ses_issue955_historical").session.traceResults[firstTraceId].finalResponse.text, firstFinalText);
|
||||
const repairedSession = ownerSessions.get("ses_issue955_historical");
|
||||
assert.equal(repairedSession.lastTraceId, secondTraceId);
|
||||
assert.equal(repairedSession.session.agentRun.commandId, secondCommandId);
|
||||
assert.equal(repairedSession.session.finalResponse.text, secondFinalText);
|
||||
|
||||
const secondResultResponse = await fetch(`http://127.0.0.1:${port}/v1/agent/chat/result/${secondTraceId}`, {
|
||||
headers: { cookie: "hwlab_session=test-stub-session" }
|
||||
});
|
||||
assert.equal(secondResultResponse.status, 200);
|
||||
const secondResult = await secondResultResponse.json();
|
||||
const secondText = JSON.stringify(secondResult);
|
||||
assert.equal(secondResult.traceId, secondTraceId);
|
||||
assert.equal(secondResult.status, "completed");
|
||||
assert.equal(secondResult.agentRun.commandId, secondCommandId);
|
||||
assert.match(secondText, /编译成功/u);
|
||||
assert.doesNotMatch(secondText, /目前只有一个 HWPOD 可用/u);
|
||||
assert.ok(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${secondCommandId}/result`));
|
||||
} finally {
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close((error) => (error ? reject(error) : resolve()));
|
||||
|
||||
Reference in New Issue
Block a user