fix: trim stale blocked-content projection plumbing

This commit is contained in:
jesse-merhi
2026-05-06 09:59:31 +10:00
committed by clawsweeper
parent 403e33c4f0
commit 545f12c067
3 changed files with 29 additions and 63 deletions

View File

@@ -33,10 +33,6 @@ const header = `// Generated by scripts/protocol-gen-swift.ts — do not edit by
.map((c) => ` case ${camelCase(c)} = "${c}"`)
.join("\n")}\n}\n`;
const OPTIONAL_INIT_DEFAULTS = new Map<string, Set<string>>([
["ChatHistoryParams", new Set(["includeblockedoriginalcontent"])],
]);
const reserved = new Set([
"associatedtype",
"class",
@@ -194,9 +190,7 @@ function emitStruct(name: string, schema: JsonSchema): string {
.map(([key, prop]) => {
const propName = safeName(key);
const req = required.has(key);
const defaultValue =
!req && OPTIONAL_INIT_DEFAULTS.get(name)?.has(propName) ? " = nil" : "";
return ` ${propName}: ${swiftType(prop, true)}${req ? "" : "?"}${defaultValue}`;
return ` ${propName}: ${swiftType(prop, true)}${req ? "" : "?"}`;
})
.join(",\n") +
")\n" +

View File

@@ -79,7 +79,7 @@ function writeTranscript(tmpDir: string, sessionId: string, lines: unknown[]): s
function appendBlockedUserMessageWithSessionManager(params: {
sessionFile: string;
originalText: string;
originalText?: string;
redactedText: string;
pluginId: string;
reason: string;
@@ -93,7 +93,6 @@ function appendBlockedUserMessageWithSessionManager(params: {
...(params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : {}),
__openclaw: {
beforeAgentRunBlocked: {
content: params.originalText ? [{ type: "text", text: params.originalText }] : [],
blockedBy: params.pluginId,
reason: params.reason,
blockedAt: Date.now(),
@@ -1305,7 +1304,7 @@ describe("readSessionMessages", () => {
});
expect(messageId).toBeTruthy();
const out = readSessionMessages(sessionId, storePath, sessionFile, {});
const out = readSessionMessages(sessionId, storePath, sessionFile);
expect(
out.map((message) => ({
role: (message as { role?: string }).role,
@@ -1316,10 +1315,7 @@ describe("readSessionMessages", () => {
{ role: "assistant", text: "hi" },
{ role: "user", text: [{ type: "text", text: "Blocked by HITL test hook." }] },
]);
expect(
(out[2] as { __openclaw?: { beforeAgentRunBlocked?: { content?: unknown } } }).__openclaw
?.beforeAgentRunBlocked?.content,
).toEqual([{ type: "text", text: "[hitl:block] hello" }]);
expect(JSON.stringify(out)).not.toContain("[hitl:block] hello");
});
test("keeps repeated blocked hook messages together in a new session", async () => {
@@ -1352,25 +1348,23 @@ describe("readSessionMessages", () => {
appendBlockedUserMessageWithSessionManager({
sessionFile,
originalText: "[hitl:block] second",
redactedText: "Blocked by HITL test hook.",
redactedText: "Blocked again by HITL test hook.",
pluginId: "hitl-test-hooks",
reason: "blocked by test policy",
});
const out = readSessionMessages(sessionId, storePath, sessionFile, {});
const out = readSessionMessages(sessionId, storePath, sessionFile);
expect(
out.map((message) => ({
role: (message as { role?: string }).role,
original: (
message as {
__openclaw?: { beforeAgentRunBlocked?: { content?: Array<{ text?: string }> } };
}
).__openclaw?.beforeAgentRunBlocked?.content?.[0]?.text,
text: (message as { content?: Array<{ text?: string }> }).content?.[0]?.text,
})),
).toEqual([
{ role: "user", original: "[hitl:block] first" },
{ role: "user", original: "[hitl:block] second" },
{ role: "user", text: "Blocked by HITL test hook." },
{ role: "user", text: "Blocked again by HITL test hook." },
]);
expect(JSON.stringify(out)).not.toContain("[hitl:block] first");
expect(JSON.stringify(out)).not.toContain("[hitl:block] second");
});
});

View File

@@ -139,13 +139,10 @@ export function attachOpenClawTranscriptMeta(
};
}
type SessionMessageProjectionOptions = Record<never, never>;
export function readSessionMessages(
sessionId: string,
storePath: string | undefined,
sessionFile?: string,
opts?: SessionMessageProjectionOptions,
): unknown[] {
const candidates = resolveSessionTranscriptCandidates(sessionId, storePath, sessionFile);
@@ -154,20 +151,20 @@ export function readSessionMessages(
return [];
}
return transcriptRecordsToMessages(readSelectedTranscriptRecords(filePath), opts);
return transcriptRecordsToMessages(readSelectedTranscriptRecords(filePath));
}
export type ReadRecentSessionMessagesOptions = SessionMessageProjectionOptions & {
export type ReadRecentSessionMessagesOptions = {
maxMessages: number;
maxBytes?: number;
maxLines?: number;
};
export type ReadSessionMessagesAsyncOptions =
| ({
| {
mode: "full";
reason: string;
} & SessionMessageProjectionOptions)
}
| ({
mode: "recent";
} & ReadRecentSessionMessagesOptions);
@@ -233,7 +230,7 @@ export function readRecentSessionMessages(
.filter((line) => line.trim().length > 0)
.slice(-maxLines);
return parseRecentTranscriptTailMessages(lines, maxMessages, opts);
return parseRecentTranscriptTailMessages(lines, maxMessages);
}) ?? []
);
}
@@ -429,14 +426,11 @@ function readSelectedTranscriptRecords(filePath: string): TailTranscriptRecord[]
}
}
function transcriptRecordsToMessages(
records: TailTranscriptRecord[],
opts?: SessionMessageProjectionOptions,
): unknown[] {
function transcriptRecordsToMessages(records: TailTranscriptRecord[]): unknown[] {
const messages: unknown[] = [];
let messageSeq = 0;
for (const entry of records) {
const message = parsedSessionEntryToMessage(entry.record, messageSeq + 1, opts);
const message = parsedSessionEntryToMessage(entry.record, messageSeq + 1);
if (message) {
messageSeq += 1;
messages.push(message);
@@ -445,18 +439,12 @@ function transcriptRecordsToMessages(
return messages;
}
function parseRecentTranscriptTailMessages(
lines: string[],
maxMessages: number,
opts?: SessionMessageProjectionOptions,
): unknown[] {
function parseRecentTranscriptTailMessages(lines: string[], maxMessages: number): unknown[] {
const entries = lines.flatMap((line) => {
const entry = parseTailTranscriptRecord(line);
return entry ? [entry] : [];
});
return transcriptRecordsToMessages(selectActiveTranscriptRecords(entries), opts).slice(
-maxMessages,
);
return transcriptRecordsToMessages(selectActiveTranscriptRecords(entries)).slice(-maxMessages);
}
function visitTranscriptLines(filePath: string, visit: (line: string) => void): void {
@@ -576,7 +564,7 @@ export async function readSessionMessagesAsync(
return [];
}
const index = await readSessionTranscriptIndex(filePath);
return index?.entries.flatMap((entry) => indexedTranscriptEntryToMessages(entry, opts)) ?? [];
return index?.entries.flatMap((entry) => indexedTranscriptEntryToMessages(entry)) ?? [];
}
export async function visitSessionMessagesAsync(
@@ -584,7 +572,7 @@ export async function visitSessionMessagesAsync(
storePath: string | undefined,
sessionFile: string | undefined,
visit: (message: unknown, seq: number) => void,
opts: { mode: "full"; reason: string },
_opts: { mode: "full"; reason: string },
): Promise<number> {
const filePath = findExistingTranscriptPath(sessionId, storePath, sessionFile);
if (!filePath) {
@@ -595,7 +583,7 @@ export async function visitSessionMessagesAsync(
return 0;
}
for (const entry of index.entries) {
const message = indexedTranscriptEntryToMessage(entry, opts);
const message = indexedTranscriptEntryToMessage(entry);
if (message) {
visit(message, entry.seq);
}
@@ -674,7 +662,7 @@ export async function readRecentSessionMessagesAsync(
...opts,
maxMessages,
});
return parseRecentTranscriptTailMessages(lines, maxMessages, opts);
return parseRecentTranscriptTailMessages(lines, maxMessages);
}
export async function readRecentSessionMessagesWithStatsAsync(
@@ -728,11 +716,7 @@ export function readRecentSessionTranscriptLines(params: {
return { lines, totalLines };
}
function parsedSessionEntryToMessage(
parsed: unknown,
seq: number,
opts?: SessionMessageProjectionOptions,
): unknown {
function parsedSessionEntryToMessage(parsed: unknown, seq: number): unknown {
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
return null;
}
@@ -763,18 +747,12 @@ function parsedSessionEntryToMessage(
return null;
}
function indexedTranscriptEntryToMessage(
entry: IndexedTranscriptEntry,
opts?: SessionMessageProjectionOptions,
): unknown {
return parsedSessionEntryToMessage(entry.record, entry.seq, opts);
function indexedTranscriptEntryToMessage(entry: IndexedTranscriptEntry): unknown {
return parsedSessionEntryToMessage(entry.record, entry.seq);
}
function indexedTranscriptEntryToMessages(
entry: IndexedTranscriptEntry,
opts?: SessionMessageProjectionOptions,
): unknown[] {
const message = indexedTranscriptEntryToMessage(entry, opts);
function indexedTranscriptEntryToMessages(entry: IndexedTranscriptEntry): unknown[] {
const message = indexedTranscriptEntryToMessage(entry);
return message ? [message] : [];
}