openclaw/src/sessions/input-provenance.ts
Mariano e3df94365b
ACP: add optional ingress provenance receipts (#40473)
Merged via squash.

Prepared head SHA: b63e46dd94479de611dab68868340aa18bdaff2f
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
2026-03-09 04:19:03 +01:00

82 lines
2.3 KiB
TypeScript

import type { AgentMessage } from "@mariozechner/pi-agent-core";
export const INPUT_PROVENANCE_KIND_VALUES = [
"external_user",
"inter_session",
"internal_system",
] as const;
export type InputProvenanceKind = (typeof INPUT_PROVENANCE_KIND_VALUES)[number];
export type InputProvenance = {
kind: InputProvenanceKind;
originSessionId?: string;
sourceSessionKey?: string;
sourceChannel?: string;
sourceTool?: string;
};
function normalizeOptionalString(value: unknown): string | undefined {
if (typeof value !== "string") {
return undefined;
}
const trimmed = value.trim();
return trimmed ? trimmed : undefined;
}
function isInputProvenanceKind(value: unknown): value is InputProvenanceKind {
return (
typeof value === "string" && (INPUT_PROVENANCE_KIND_VALUES as readonly string[]).includes(value)
);
}
export function normalizeInputProvenance(value: unknown): InputProvenance | undefined {
if (!value || typeof value !== "object") {
return undefined;
}
const record = value as Record<string, unknown>;
if (!isInputProvenanceKind(record.kind)) {
return undefined;
}
return {
kind: record.kind,
originSessionId: normalizeOptionalString(record.originSessionId),
sourceSessionKey: normalizeOptionalString(record.sourceSessionKey),
sourceChannel: normalizeOptionalString(record.sourceChannel),
sourceTool: normalizeOptionalString(record.sourceTool),
};
}
export function applyInputProvenanceToUserMessage(
message: AgentMessage,
inputProvenance: InputProvenance | undefined,
): AgentMessage {
if (!inputProvenance) {
return message;
}
if ((message as { role?: unknown }).role !== "user") {
return message;
}
const existing = normalizeInputProvenance((message as { provenance?: unknown }).provenance);
if (existing) {
return message;
}
return {
...(message as unknown as Record<string, unknown>),
provenance: inputProvenance,
} as unknown as AgentMessage;
}
export function isInterSessionInputProvenance(value: unknown): boolean {
return normalizeInputProvenance(value)?.kind === "inter_session";
}
export function hasInterSessionUserProvenance(
message: { role?: unknown; provenance?: unknown } | undefined,
): boolean {
if (!message || message.role !== "user") {
return false;
}
return isInterSessionInputProvenance(message.provenance);
}