Merge pull request #2333 from pikasTech/issue-1373-architecture-cleanup
fix: seal Workbench status authority merges
This commit is contained in:
@@ -6,7 +6,7 @@ import test from "node:test";
|
||||
|
||||
import type { WorkbenchSessionRecord } from "../src/types/index.ts";
|
||||
import { initialWorkbenchSessionIdFromLocation } from "../src/stores/workbench-projection.ts";
|
||||
import { createWorkbenchServerState, reduceWorkbenchServerState, selectActiveMessages, selectActiveSession, selectSessionStatusAuthority, selectTraceAuthorityById } from "../src/stores/workbench-server-state.ts";
|
||||
import { createWorkbenchServerState, reduceWorkbenchServerState, selectActiveMessages, selectActiveSession, selectSessionStatusAuthority, selectTraceAuthorityById, selectTurnStatusAuthority } from "../src/stores/workbench-server-state.ts";
|
||||
import { sessionToSessionTab, stableSessionList } from "../src/stores/workbench-session.ts";
|
||||
|
||||
test("Workbench route parsing accepts only session routes", () => {
|
||||
@@ -104,6 +104,57 @@ test("Workbench server-state session detail updates session status authority", (
|
||||
assert.equal(authority?.lastTraceId, "trc_backfill");
|
||||
});
|
||||
|
||||
test("Workbench session status action preserves sealed completed authority for the same trace", () => {
|
||||
let state = createWorkbenchServerState();
|
||||
state = reduceWorkbenchServerState(state, {
|
||||
type: "session.messages",
|
||||
sessionId: "ses_status_seal",
|
||||
messages: [
|
||||
{ id: "msg_user", messageId: "msg_user", role: "user", title: "用户", text: "ping", status: "sent", createdAt: "2026-06-24T00:00:00.000Z", sessionId: "ses_status_seal", traceId: "trc_status_seal" },
|
||||
{ id: "msg_agent", messageId: "msg_agent", role: "agent", title: "Code Agent", text: "OK", status: "completed", createdAt: "2026-06-24T00:00:01.000Z", sessionId: "ses_status_seal", traceId: "trc_status_seal" }
|
||||
]
|
||||
});
|
||||
state = reduceWorkbenchServerState(state, {
|
||||
type: "session.status",
|
||||
session: { sessionId: "ses_status_seal", status: "running", lastTraceId: "trc_status_seal" }
|
||||
});
|
||||
|
||||
let authority = selectSessionStatusAuthority(state).ses_status_seal;
|
||||
assert.equal(authority?.status, "completed");
|
||||
assert.equal(authority?.lastTraceId, "trc_status_seal");
|
||||
|
||||
state = reduceWorkbenchServerState(state, {
|
||||
type: "session.status",
|
||||
session: { sessionId: "ses_status_seal", status: "running", lastTraceId: "trc_status_next" }
|
||||
});
|
||||
|
||||
authority = selectSessionStatusAuthority(state).ses_status_seal;
|
||||
assert.equal(authority?.status, "running");
|
||||
assert.equal(authority?.lastTraceId, "trc_status_next");
|
||||
});
|
||||
|
||||
test("Workbench turn status action preserves terminal authority over stale running refresh", () => {
|
||||
let state = createWorkbenchServerState();
|
||||
state = reduceWorkbenchServerState(state, {
|
||||
type: "turn.status",
|
||||
turn: { traceId: "trc_turn_seal", status: "running", running: true, terminal: false, sessionId: "ses_turn_seal" }
|
||||
});
|
||||
state = reduceWorkbenchServerState(state, {
|
||||
type: "turn.status",
|
||||
turn: { traceId: "trc_turn_seal", status: "completed", running: false, terminal: true, sessionId: "ses_turn_seal", loadedAt: "2026-06-24T00:00:02.000Z" }
|
||||
});
|
||||
state = reduceWorkbenchServerState(state, {
|
||||
type: "turn.status",
|
||||
turn: { traceId: "trc_turn_seal", status: "running", running: true, terminal: false, sessionId: "ses_turn_seal", loadedAt: "2026-06-24T00:00:01.000Z" }
|
||||
});
|
||||
|
||||
const turn = selectTurnStatusAuthority(state).trc_turn_seal;
|
||||
assert.equal(turn?.status, "completed");
|
||||
assert.equal(turn?.running, false);
|
||||
assert.equal(turn?.terminal, true);
|
||||
assert.equal(turn?.loadedAt, "2026-06-24T00:00:02.000Z");
|
||||
});
|
||||
|
||||
test("Workbench realtime agent snapshots cannot create duplicate assistant messages", () => {
|
||||
let state = createWorkbenchServerState();
|
||||
state = reduceWorkbenchServerState(state, {
|
||||
|
||||
@@ -47,13 +47,13 @@ export function reduceWorkbenchServerState(state: WorkbenchServerState, action:
|
||||
case "message.snapshot":
|
||||
return reduceMessageSnapshot(state, action.sessionId, action.message);
|
||||
case "session.status":
|
||||
return { ...state, sessionStatusById: { ...state.sessionStatusById, [action.session.sessionId]: action.session } };
|
||||
return { ...state, sessionStatusById: { ...state.sessionStatusById, [action.session.sessionId]: mergeSessionStatusAuthority(state.sessionStatusById[action.session.sessionId], action.session) } };
|
||||
case "session.forget":
|
||||
return cleanupWorkbenchServerStateSessions(state, [action.sessionId]);
|
||||
case "session.cleanupDropped":
|
||||
return cleanupWorkbenchServerStateSessions(state, droppedSessionIds(state, new Set(action.keepSessionIds)));
|
||||
case "turn.status":
|
||||
return { ...state, turnStatusByTraceId: { ...state.turnStatusByTraceId, [action.turn.traceId]: action.turn } };
|
||||
return { ...state, turnStatusByTraceId: { ...state.turnStatusByTraceId, [action.turn.traceId]: mergeTurnStatusAuthority(state.turnStatusByTraceId[action.turn.traceId], action.turn) } };
|
||||
case "turn.forget":
|
||||
return { ...state, turnStatusByTraceId: withoutKey(state.turnStatusByTraceId, action.traceId) };
|
||||
case "trace.snapshot":
|
||||
@@ -397,6 +397,20 @@ function mergeSessionStatusAuthority(existing: SessionStatusAuthority | undefine
|
||||
};
|
||||
}
|
||||
|
||||
function mergeTurnStatusAuthority(existing: TurnStatusAuthority | undefined, incoming: TurnStatusAuthority): TurnStatusAuthority {
|
||||
if (existing && isTerminalTurnStatusAuthority(existing) && !isTerminalTurnStatusAuthority(incoming) && isRunningSessionStatus(incoming.status)) return existing;
|
||||
return {
|
||||
...(existing ?? {}),
|
||||
...incoming,
|
||||
projection: incoming.projection ?? existing?.projection ?? null,
|
||||
loadedAt: incoming.loadedAt ?? existing?.loadedAt ?? null
|
||||
};
|
||||
}
|
||||
|
||||
function isTerminalTurnStatusAuthority(turn: TurnStatusAuthority): boolean {
|
||||
return turn.terminal === true || (turn.running !== true && isTerminalMessageStatus(turn.status));
|
||||
}
|
||||
|
||||
function isSameTraceAuthority(left: SessionStatusAuthority, right: SessionStatusAuthority): boolean {
|
||||
const leftTrace = textValue(left.lastTraceId);
|
||||
const rightTrace = textValue(right.lastTraceId);
|
||||
|
||||
Reference in New Issue
Block a user