Merge pull request #2770 from pikasTech/fix/workbench-l1-l2-runtime-template
Pipelines as Code CI / hwlab-nc01-v03-ci-poll-974ffad37cef6e37ecc9aa94fe1ae5907b8d6b70 Success

透传 Workbench 会话真实创建时间
This commit is contained in:
Lyon
2026-07-23 09:58:41 +08:00
committed by GitHub
4 changed files with 50 additions and 1 deletions
@@ -0,0 +1,22 @@
import assert from "node:assert/strict";
import test from "node:test";
import { factSessionSummary } from "./server-workbench-facts.ts";
test("session summary preserves createdAt without using updatedAt as fallback", () => {
const createdAt = "2026-07-23T07:30:00.000Z";
const summary = factSessionSummary({
sessionId: "ses_created_at",
status: "idle",
createdAt,
updatedAt: "2026-07-23T08:30:00.000Z",
});
assert.equal(summary?.createdAt, createdAt);
const withoutCreatedAt = factSessionSummary({
sessionId: "ses_without_created_at",
status: "idle",
updatedAt: "2026-07-23T08:30:00.000Z",
});
assert.equal(withoutCreatedAt?.createdAt, null);
});
+5
View File
@@ -106,6 +106,7 @@ export function factSessionSummary(session, facts = {}) {
launchContext,
messageCount: messages.length,
firstUserMessagePreview: firstUserPreview(messages),
createdAt: factCreatedAt(session),
updatedAt: factUpdatedAt(session),
turnSummary: turn ? {
turnId: factTurnId(turn, traceId),
@@ -710,6 +711,10 @@ export function factUpdatedAt(record) {
return textValue(record?.updatedAt ?? record?.occurredAt ?? record?.createdAt) || null;
}
export function factCreatedAt(record) {
return textValue(record?.createdAt) || null;
}
export function factSeq(record) {
for (const value of [record?.projectedSeq, record?.sourceSeq, record?.seq]) {
const parsed = Number(value);
+1 -1
View File
@@ -2174,7 +2174,7 @@ func sessionSummary(session map[string]any, facts map[string][]any) map[string]a
turnSummary = map[string]any{"turnId": first(text(turn["turnId"]), traceID), "traceId": traceID, "status": status, "running": isRunning(status), "terminal": isTerminal(status), "eventCount": projection["lastProjectedSeq"], "projection": projection, "projectionStatus": projection["projectionStatus"], "projectionHealth": projection["projectionHealth"], "staleMs": projection["staleMs"], "blocker": projection["blocker"], "timing": timing, "startedAt": timing["startedAt"], "lastEventAt": timing["lastEventAt"], "finishedAt": timing["finishedAt"], "durationMs": timing["durationMs"], "updatedAt": updatedAt(turn)}
}
title := sessionDisplayTitle(session, firstUserMessagePreview)
return map[string]any{"sessionId": sid, "threadId": nullableText(session["threadId"]), "agentId": first(text(session["agentId"]), "hwlab-code-agent"), "title": title, "name": title, "status": status, "running": isRunning(status), "terminal": isTerminal(status), "lastTraceId": nullableText(traceID), "projection": projection, "projectionStatus": projection["projectionStatus"], "projectionHealth": projection["projectionHealth"], "staleMs": projection["staleMs"], "blocker": projection["blocker"], "providerProfile": nullableText(first(text(session["providerProfile"]), text(objectMap(session["sessionJson"])["providerProfile"]))), "messageCount": messageCount, "firstUserMessagePreview": firstUserMessagePreview, "updatedAt": updatedAt(session), "turnSummary": turnSummary, "valuesRedacted": true}
return map[string]any{"sessionId": sid, "threadId": nullableText(session["threadId"]), "agentId": first(text(session["agentId"]), "hwlab-code-agent"), "title": title, "name": title, "status": status, "running": isRunning(status), "terminal": isTerminal(status), "lastTraceId": nullableText(traceID), "projection": projection, "projectionStatus": projection["projectionStatus"], "projectionHealth": projection["projectionHealth"], "staleMs": projection["staleMs"], "blocker": projection["blocker"], "providerProfile": nullableText(first(text(session["providerProfile"]), text(objectMap(session["sessionJson"])["providerProfile"]))), "messageCount": messageCount, "firstUserMessagePreview": firstUserMessagePreview, "createdAt": nullableText(session["createdAt"]), "updatedAt": updatedAt(session), "turnSummary": turnSummary, "valuesRedacted": true}
}
type traceCandidate struct {
+22
View File
@@ -56,3 +56,25 @@ func TestSessionSummaryUsesNewestTurnFactsOverStaleSessionLastTrace(t *testing.T
t.Fatalf("turnSummary.status=%q, want running; turnSummary=%#v", got, turnSummary)
}
}
func TestSessionSummaryPreservesCreatedAtWithoutUpdatedAtFallback(t *testing.T) {
createdAt := "2026-07-23T07:30:00Z"
summary := sessionSummary(map[string]any{
"sessionId": "ses_created_at",
"status": "idle",
"createdAt": createdAt,
"updatedAt": "2026-07-23T08:30:00Z",
}, emptyFacts())
if got := text(summary["createdAt"]); got != createdAt {
t.Fatalf("createdAt=%q, want %q", got, createdAt)
}
withoutCreatedAt := sessionSummary(map[string]any{
"sessionId": "ses_without_created_at",
"status": "idle",
"updatedAt": "2026-07-23T08:30:00Z",
}, emptyFacts())
if withoutCreatedAt["createdAt"] != nil {
t.Fatalf("createdAt=%#v, want nil without source creation fact", withoutCreatedAt["createdAt"])
}
}