Skip to content

Commit 00a2368

Browse files
V48 Gate 3: Universal source-safe content-withholding filter for pipeline telemetry
sourceSafeStreamEvent withholds llm:{prompt,input,output,reasoningOutput,judgmentOutput,parsedOutput} content from every persisted/streamed pipeline event, leaving only a content-withheld summary. Applied at the execution_events persistence chokepoint so the formal agent-generics LLM substeps — which store full prompt/response content — stream lawfully (rawPromptVisible/rawProviderResponseVisible stay false) for ALL pipelines. Prerequisite for AssetPacksSynthesis on the real primitives. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a9a157f commit 00a2368

1 file changed

Lines changed: 58 additions & 2 deletions

File tree

packages/pipelines-generics/src/streaming/pipeline-stream-integration.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,65 @@ export interface PipelineStreamConfig {
2626
structuredToDatabase?: boolean;
2727
}
2828

29+
// Source-safety law (V48): pipeline telemetry must never serialize raw prompts
30+
// or provider responses (rawPromptVisible/rawProviderResponseVisible=false). The
31+
// formal LLM substeps store full prompt/response content under the `llm`
32+
// namespace, which auto-streams via Execution.store. This withholds that content
33+
// from any persisted/streamed event, leaving only a content-withheld summary —
34+
// applied universally to every pipeline stream event (not per-pipeline).
35+
const SOURCE_SAFE_LLM_CONTENT_KEYS = new Set([
36+
'prompt',
37+
'input',
38+
'output',
39+
'reasoningOutput',
40+
'judgmentOutput',
41+
'parsedOutput',
42+
]);
43+
44+
export function sourceSafeStreamEvent(event: any): any {
45+
if (!event || typeof event !== 'object') return event;
46+
const namespace = (event as any).namespace;
47+
const key = (event as any).key;
48+
if (namespace !== 'llm' || !SOURCE_SAFE_LLM_CONTENT_KEYS.has(String(key))) {
49+
return event;
50+
}
51+
const data =
52+
(event as any).data && typeof (event as any).data === 'object'
53+
? ((event as any).data as Record<string, any>)
54+
: {};
55+
const contentChars =
56+
typeof (event as any).data === 'string'
57+
? (event as any).data.length
58+
: typeof data.content === 'string'
59+
? data.content.length
60+
: typeof data.prompt === 'string'
61+
? data.prompt.length
62+
: null;
63+
const state =
64+
(event as any).executionState && typeof (event as any).executionState === 'object'
65+
? (event as any).executionState
66+
: {};
67+
return {
68+
...event,
69+
message: '[content withheld — source-safe]',
70+
data: {
71+
contentWithheld: true,
72+
sourceSafetyClass: 'source_safe',
73+
stage: key,
74+
generation: data.generation ?? state.generation ?? null,
75+
provider: data.provider ?? null,
76+
model: data.model ?? null,
77+
contentChars,
78+
phase: data.phase ?? state.phase ?? null,
79+
agent: data.agent ?? state.agent ?? null,
80+
step: data.step ?? state.step ?? null,
81+
},
82+
};
83+
}
84+
2985
/**
3086
* Wire up a pipeline execution with streaming
31-
*
87+
*
3288
* This registers a stream manager with the execution so that
3389
* all storage operations emit stream events automatically.
3490
*/
@@ -87,7 +143,7 @@ export function enablePipelineStreaming(
87143
await eventsModel.create({
88144
run_id: config.runId,
89145
event_type: event.type,
90-
event_data: event,
146+
event_data: sourceSafeStreamEvent(event),
91147
created_at: new Date().toISOString(),
92148
});
93149
} catch (error) {

0 commit comments

Comments
 (0)