fix: keep suppressed notification names readable (#73)

Co-authored-by: Codex <codex@pikas.tech>
This commit is contained in:
Lyon
2026-06-02 11:16:50 +08:00
committed by GitHub
parent aa0bd64714
commit 0092f55249
3 changed files with 32 additions and 5 deletions
+6 -4
View File
@@ -653,15 +653,17 @@ function suppressedNotificationEvents(summary: SuppressedNotificationSummary): B
payload: {
phase: "codex-app-server-notifications-suppressed",
total: summary.total,
byMethod: sortCountRecord(summary.byMethod),
byItemType: sortCountRecord(summary.byItemType),
methods: countRecordEntries(summary.byMethod, "method"),
itemTypes: countRecordEntries(summary.byItemType, "itemType"),
valuesPrinted: false,
},
}];
}
function sortCountRecord(input: Record<string, number>): JsonRecord {
return Object.fromEntries(Object.entries(input).sort(([left], [right]) => left.localeCompare(right))) as JsonRecord;
function countRecordEntries(input: Record<string, number>, keyName: "method" | "itemType"): JsonRecord[] {
return Object.entries(input)
.sort(([left], [right]) => left.localeCompare(right))
.map(([name, count]) => ({ [keyName]: name, count }) as JsonRecord);
}
function isSuppressedCodexStatusNotification(method: string): boolean {
+25
View File
@@ -153,6 +153,20 @@ const selfTest: SelfTestCase = async (context) => {
const suppression = noisyItems.find((event) => event.type === "backend_status" && eventPayload(event).phase === "codex-app-server-notifications-suppressed");
assert.ok(suppression, "suppression summary must be emitted when noisy notifications are filtered");
assert.equal(eventPayload(suppression ?? { payload: {} }).total, 8);
assert.deepEqual(countEntriesByName(eventPayload(suppression ?? { payload: {} }).methods, "method"), {
"account/rateLimits/updated": 1,
"configWarning": 1,
"item/completed": 1,
"item/reasoning/textDelta": 2,
"item/started": 1,
"thread/tokenUsage/updated": 1,
"warning": 1,
});
assert.deepEqual(countEntriesByName(eventPayload(suppression ?? { payload: {} }).itemTypes, "itemType"), {
reasoning: 4,
});
assert.equal(eventPayload(suppression ?? { payload: {} }).byMethod, undefined, "suppression summary must not use method names as JSON keys because redaction treats token-like key names as sensitive");
assert.equal(JSON.stringify(suppression).includes("thread/tokenUsage/updated"), true, "suppressed tokenUsage method name should remain visible as metadata, not redacted as a key");
assert.equal(JSON.stringify(noisyEvents).includes("internal reasoning must not become durable trace text"), false, "suppression summary must not leak reasoning text");
assertNoSecretLeak(noisyEvents);
@@ -204,6 +218,17 @@ function eventPayload(event: { payload: unknown }): JsonRecord {
return typeof event.payload === "object" && event.payload !== null && !Array.isArray(event.payload) ? event.payload as JsonRecord : {};
}
function countEntriesByName(value: unknown, keyName: "method" | "itemType"): Record<string, number> {
const output: Record<string, number> = {};
if (!Array.isArray(value)) return output;
for (const entry of value) {
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
const record = entry as JsonRecord;
if (typeof record[keyName] === "string" && typeof record.count === "number") output[record[keyName]] = record.count;
}
return output;
}
function eventPayloadItem(event: { payload: unknown }): JsonRecord {
const item = eventPayload(event).item;
return typeof item === "object" && item !== null && !Array.isArray(item) ? item as JsonRecord : {};