fix: trust semantic diagnostic progress

This commit is contained in:
Josh Avant
2026-08-06 04:47:01 -05:00
parent 4ea4596617
commit b731aea7e1
2 changed files with 50 additions and 4 deletions
@@ -3,6 +3,7 @@ import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures";
import { afterEach, describe, expect, it, vi } from "vitest";
import { hasInternalDiagnosticEventListeners } from "../infra/diagnostic-event-listener-presence.js";
import {
emitDiagnosticEvent,
emitTrustedDiagnosticEvent,
resetDiagnosticEventsForTest,
waitForDiagnosticEventsDrained,
@@ -630,6 +631,51 @@ describe("repeated request liveness", () => {
});
});
it("keeps active-owner evidence across untrusted semantic progress", async () => {
const ref = { sessionId: "untrusted-session", sessionKey: "agent:main:untrusted" };
const runId = "untrusted-run";
startDiagnosticRunActivityTracking();
markDiagnosticEmbeddedRunStarted({ ...ref, runId });
for (let attempt = 0; attempt < 2; attempt += 1) {
markDiagnosticModelStartedForTest({
...ref,
runId,
provider: "mock",
model: "request-model",
observationUnit: "request",
});
}
emitDiagnosticEvent({
type: "run.progress",
...ref,
runId,
reason: "plugin:semantic",
progressKind: "semantic",
});
await waitForDiagnosticEventsDrained();
expect(getDiagnosticSessionActivitySnapshot(ref)).toMatchObject({
lastProgressReason: "plugin:semantic",
repeatedRequestNoProgressAgeMs: expect.any(Number),
});
emitTrustedDiagnosticEvent({
type: "run.progress",
...ref,
runId,
reason: "model_result:semantic",
progressKind: "semantic",
});
await waitForDiagnosticEventsDrained();
expect(getDiagnosticSessionActivitySnapshot(ref)).toMatchObject({
lastProgressReason: "model_result:semantic",
repeatedRequestNoProgressAgeMs: undefined,
});
});
it("requires an owned semantic event across merged session aliases", () => {
vi.useFakeTimers();
const startedAt = Date.parse("2026-08-04T02:00:00Z");
+4 -4
View File
@@ -324,8 +324,8 @@ function recordModelEnded(
touchSessionActivity(activity, "model_call:ended");
}
function recordRunProgress(event: DiagnosticRunProgressActivityEvent): void {
markDiagnosticRunProgress(event);
function recordRunProgress(event: DiagnosticRunProgressActivityEvent, trusted: boolean): void {
markDiagnosticRunProgress(trusted ? event : { ...event, progressKind: "liveness" });
}
export function markDiagnosticArgumentChurnObservation(
@@ -733,7 +733,7 @@ export function startDiagnosticRunActivityTracking(): void {
return;
}
const startAfterEventSequence = getInternalDiagnosticEventSequence();
unregisterDiagnosticRunActivityListener = onInternalDiagnosticEvent((event) => {
unregisterDiagnosticRunActivityListener = onInternalDiagnosticEvent((event, metadata) => {
// A prior lifecycle can leave already-sequenced events in the async queue.
// Ignore them so a restart cannot recreate activity that stop cleared.
if (event.seq <= startAfterEventSequence) {
@@ -756,7 +756,7 @@ export function startDiagnosticRunActivityTracking(): void {
recordModelEnded(event);
return;
case "run.progress":
recordRunProgress(event);
recordRunProgress(event, metadata.trusted);
return;
case "run.completed":
recordRunCompleted(event);