- notification-store: schema v2 — repeatCount/lastTs merge for non-blocking notices; NOTICE_KIND enum (SYSTEM_NOTICE, TOOL_NOTICE, BLOCKING_NOTICE, USER_VISIBLE) for renderer classification without message parsing - sf-db: remove gate_runs and audit_events tables (replaced by uok audit.js and trace-writer); schema reduced by ~370 lines - notify-interceptor: tag auto-mode system notices with NOTICE_KIND.SYSTEM_NOTICE - auto-prompts, guided-flow, system-context: use NOTICE_KIND on emit calls - cli-status: expanded headless status surface + test coverage - headless-types: new status fields - Makefile/justfile: dev workflow improvements - record-promoter, requirement-promoter: minor cleanup - sf-db-migration tests: updated for dropped tables - uok-gate-runner, uok-metrics, uok-outcome, uok-status tests: updated Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
41 lines
1.8 KiB
JavaScript
41 lines
1.8 KiB
JavaScript
// SF Extension — Notify Interceptor
|
|
// Wraps ctx.ui.notify() in-place to persist every notification through the
|
|
// notification store. Uses a WeakSet to prevent double-wrapping and handle
|
|
// UI context replacement on /reload gracefully.
|
|
|
|
import { appendNotification } from "../notification-store.js";
|
|
import { logWarning } from "../workflow-logger.js";
|
|
|
|
// Track which ui context objects have been wrapped to prevent double-install.
|
|
// WeakSet allows GC to collect replaced uiContext instances after /reload.
|
|
const _wrappedContexts = new WeakSet();
|
|
/**
|
|
* Install the notify interceptor on a context's UI object.
|
|
* Mutates ctx.ui.notify in place — the original is called after persistence.
|
|
* Safe to call multiple times; no-ops if already installed on the same ui object.
|
|
*
|
|
* Optional third-arg metadata for durable hygiene:
|
|
* - dedupe_key — stable merge identity across wording/timer drift
|
|
* - noticeKind — NOTICE_KIND.* (system_notice, tool_notice, …)
|
|
* - merge — false to force a new JSONL row even when duplicate
|
|
*/
|
|
export function installNotifyInterceptor(ctx) {
|
|
if (_wrappedContexts.has(ctx.ui)) return;
|
|
const originalNotify = ctx.ui.notify.bind(ctx.ui);
|
|
ctx.ui.notify = (message, type, metadata) => {
|
|
try {
|
|
appendNotification(message, type ?? "info", "notify", metadata);
|
|
} catch (err) {
|
|
// Non-fatal — never let persistence break the UI.
|
|
// Include a correlation ID (timestamp + truncated message) so the
|
|
// failure can be matched against the notification that was dropped.
|
|
const correlationId = `${Date.now()}-${message.slice(0, 40).replace(/\s+/g, "_")}`;
|
|
logWarning(
|
|
"scaffold",
|
|
`notification persistence failed (non-fatal) [corr:${correlationId}]: ${err.message}`,
|
|
);
|
|
}
|
|
originalNotify(message, type, metadata);
|
|
};
|
|
_wrappedContexts.add(ctx.ui);
|
|
}
|