fix(runner): preserve safe result authorization fields

Keep the schema-enumerated authorization boundary in canonical semantic result receipts while continuing to redact arbitrary authorization-shaped payload keys.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-26 20:06:41 +00:00
1 parent fe3264fb49
commit a47093892c
1 file changed
+30 -1
@@ -368,6 +368,7 @@ impl DurableState {
let source_seq = self.next_source_seq;
let emitted_at = current_timestamp()?;
let sanitized_payload = sanitize_event_payload(&event_type, &payload);
let envelope = json!({
"protocol": PROTOCOL,
"version": PROTOCOL_VERSION,
@@ -392,7 +393,7 @@ impl DurableState {
"schemaVersion": 1,
"priority": priority.number(),
"emittedAt": emitted_at,
"payload": sanitize_value(&payload),
"payload": sanitized_payload,
},
});
let byte_size = serde_json::to_vec(&envelope)
@@ -1101,6 +1102,34 @@ fn sanitize_value(value: &Value) -> Value {
}
}
fn sanitize_event_payload(event_type: &str, payload: &Value) -> Value {
let mut sanitized = sanitize_value(payload);
if event_type == "semantic_tool.result" {
let boundary = payload
.pointer("/semantic_tool/authorizationBoundary")
.and_then(Value::as_str)
.filter(|value| {
matches!(
*value,
"company"
| "actor"
| "active_task"
| "grant"
| "governed_action"
| "lock"
| "revision"
)
});
if let (Some(boundary), Some(target)) = (
boundary,
sanitized.pointer_mut("/semantic_tool/authorizationBoundary"),
) {
*target = Value::String(boundary.to_owned());
}
}
sanitized
}
pub(crate) fn redact_text(input: &str) -> String {
let (bounded, truncated) = if input.len() > 4096 {
let boundary = input