mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 10:00:42 +00:00
Summary: - The PR adds a `before_agent_run` plugin hook with pass/block decisions, redacted blocked-turn persistence, diagnostics/docs/changelog updates, and focused runner, gateway, session, and plugin tests. - Reproducibility: not applicable. as a feature PR rather than a current-main bug report. Current main lacks ` ... un`, while the PR head adds source coverage and copied live Gateway/WebChat log proof for the new behavior. Automerge notes: - PR branch already contained follow-up commit before automerge: fix: trim before agent hook PR scope - PR branch already contained follow-up commit before automerge: fix: keep before-agent blocks redacted - PR branch already contained follow-up commit before automerge: fix: keep runtime context out of model prompt - PR branch already contained follow-up commit before automerge: docs: refresh config baseline after rebase - PR branch already contained follow-up commit before automerge: fix: align blocked turn clients with redacted content - PR branch already contained follow-up commit before automerge: fix: remove out-of-scope client block UI changes Validation: - ClawSweeper review passed for head767e46fde8. - Required merge gates passed before the squash merge. Prepared head SHA:767e46fde8Review: https://github.com/openclaw/openclaw/pull/75035#issuecomment-4351843275 Co-authored-by: Jesse Merhi <jessejmerhi@gmail.com> Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
87 lines
3.2 KiB
TypeScript
87 lines
3.2 KiB
TypeScript
import type {
|
|
InteractiveReply,
|
|
MessagePresentation,
|
|
ReplyPayloadDelivery,
|
|
} from "../interactive/payload.js";
|
|
|
|
export type ReplyPayload = {
|
|
text?: string;
|
|
mediaUrl?: string;
|
|
mediaUrls?: string[];
|
|
/** Internal-only trust signal for gateway webchat local media embedding. */
|
|
trustedLocalMedia?: boolean;
|
|
/** Treat media as live-only content and avoid persisting the underlying media reference. */
|
|
sensitiveMedia?: boolean;
|
|
/** Channel-agnostic rich presentation. Core degrades or asks the channel renderer to map it. */
|
|
presentation?: MessagePresentation;
|
|
/** Channel-agnostic delivery preferences, e.g. pin the sent message when supported. */
|
|
delivery?: ReplyPayloadDelivery;
|
|
/**
|
|
* @deprecated Use presentation.
|
|
*
|
|
* Internal legacy representation used by existing approval/reply helpers during migration.
|
|
*/
|
|
interactive?: InteractiveReply;
|
|
btw?: {
|
|
question: string;
|
|
};
|
|
replyToId?: string;
|
|
replyToTag?: boolean;
|
|
/** True when [[reply_to_current]] was present but not yet mapped to a message id. */
|
|
replyToCurrent?: boolean;
|
|
/** Send audio as voice message (bubble) instead of audio file. Defaults to false. */
|
|
audioAsVoice?: boolean;
|
|
/**
|
|
* Text synthesized into an audio-only TTS payload. Exposed to hooks for
|
|
* archival/search use when no visible channel text is sent.
|
|
*/
|
|
spokenText?: string;
|
|
isError?: boolean;
|
|
/** Marks this payload as a reasoning/thinking block. Channels that do not
|
|
* have a dedicated reasoning lane (e.g. WhatsApp, web) should suppress it. */
|
|
isReasoning?: boolean;
|
|
/** Marks this payload as a compaction status notice (start/end).
|
|
* Should be excluded from TTS transcript accumulation so compaction
|
|
* status lines are not synthesised into the spoken assistant reply. */
|
|
isCompactionNotice?: boolean;
|
|
/** Channel-specific payload data (per-channel envelope). */
|
|
channelData?: Record<string, unknown>;
|
|
};
|
|
|
|
export type ReplyPayloadMetadata = {
|
|
assistantMessageIndex?: number;
|
|
/**
|
|
* Internal OpenClaw notices generated after a runtime/provider failure are
|
|
* not assistant source replies. Dispatch may deliver them even when normal
|
|
* assistant source replies are message-tool-only; sendPolicy deny still wins.
|
|
*/
|
|
deliverDespiteSourceReplySuppression?: boolean;
|
|
beforeAgentRunBlocked?: boolean;
|
|
};
|
|
|
|
const replyPayloadMetadata = new WeakMap<object, ReplyPayloadMetadata>();
|
|
|
|
export function setReplyPayloadMetadata<T extends object>(
|
|
payload: T,
|
|
metadata: ReplyPayloadMetadata,
|
|
): T {
|
|
const previous = replyPayloadMetadata.get(payload);
|
|
replyPayloadMetadata.set(payload, { ...previous, ...metadata });
|
|
return payload;
|
|
}
|
|
|
|
export function getReplyPayloadMetadata(payload: object): ReplyPayloadMetadata | undefined {
|
|
return replyPayloadMetadata.get(payload);
|
|
}
|
|
|
|
export function copyReplyPayloadMetadata<T extends object>(source: object, payload: T): T {
|
|
const metadata = getReplyPayloadMetadata(source);
|
|
return metadata ? setReplyPayloadMetadata(payload, metadata) : payload;
|
|
}
|
|
|
|
export function markReplyPayloadForSourceSuppressionDelivery<T extends object>(payload: T): T {
|
|
return setReplyPayloadMetadata(payload, {
|
|
deliverDespiteSourceReplySuppression: true,
|
|
});
|
|
}
|