mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 06:14:34 +00:00
feat(agents): use structured internal completion events
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
buildAnnounceIdempotencyKey,
|
||||
resolveQueueAnnounceId,
|
||||
} from "./announce-idempotency.js";
|
||||
import { formatAgentInternalEventsForPrompt, type AgentInternalEvent } from "./internal-events.js";
|
||||
import {
|
||||
isEmbeddedPiRunActive,
|
||||
queueEmbeddedPiMessage,
|
||||
@@ -601,6 +602,7 @@ async function sendAnnounce(item: AnnounceQueueItem) {
|
||||
to: requesterIsSubagent ? undefined : origin?.to,
|
||||
threadId: requesterIsSubagent ? undefined : threadId,
|
||||
deliver: !requesterIsSubagent,
|
||||
internalEvents: item.internalEvents,
|
||||
idempotencyKey,
|
||||
},
|
||||
timeoutMs: announceTimeoutMs,
|
||||
@@ -651,8 +653,10 @@ async function maybeQueueSubagentAnnounce(params: {
|
||||
requesterSessionKey: string;
|
||||
announceId?: string;
|
||||
triggerMessage: string;
|
||||
steerMessage: string;
|
||||
summaryLine?: string;
|
||||
requesterOrigin?: DeliveryContext;
|
||||
internalEvents?: AgentInternalEvent[];
|
||||
signal?: AbortSignal;
|
||||
}): Promise<"steered" | "queued" | "none"> {
|
||||
if (params.signal?.aborted) {
|
||||
@@ -674,7 +678,7 @@ async function maybeQueueSubagentAnnounce(params: {
|
||||
|
||||
const shouldSteer = queueSettings.mode === "steer" || queueSettings.mode === "steer-backlog";
|
||||
if (shouldSteer) {
|
||||
const steered = queueEmbeddedPiMessage(sessionId, params.triggerMessage);
|
||||
const steered = queueEmbeddedPiMessage(sessionId, params.steerMessage);
|
||||
if (steered) {
|
||||
return "steered";
|
||||
}
|
||||
@@ -693,6 +697,7 @@ async function maybeQueueSubagentAnnounce(params: {
|
||||
announceId: params.announceId,
|
||||
prompt: params.triggerMessage,
|
||||
summaryLine: params.summaryLine,
|
||||
internalEvents: params.internalEvents,
|
||||
enqueuedAt: Date.now(),
|
||||
sessionKey: canonicalKey,
|
||||
origin,
|
||||
@@ -710,6 +715,7 @@ async function sendSubagentAnnounceDirectly(params: {
|
||||
targetRequesterSessionKey: string;
|
||||
triggerMessage: string;
|
||||
completionMessage?: string;
|
||||
internalEvents?: AgentInternalEvent[];
|
||||
expectsCompletionMessage: boolean;
|
||||
bestEffortDeliver?: boolean;
|
||||
completionRouteMode?: "bound" | "fallback" | "hook";
|
||||
@@ -843,6 +849,7 @@ async function sendSubagentAnnounceDirectly(params: {
|
||||
message: params.triggerMessage,
|
||||
deliver: shouldDeliverExternally,
|
||||
bestEffortDeliver: params.bestEffortDeliver,
|
||||
internalEvents: params.internalEvents,
|
||||
channel: shouldDeliverExternally ? directChannel : undefined,
|
||||
accountId: shouldDeliverExternally ? directOrigin?.accountId : undefined,
|
||||
to: shouldDeliverExternally ? directTo : undefined,
|
||||
@@ -871,7 +878,9 @@ async function deliverSubagentAnnouncement(params: {
|
||||
requesterSessionKey: string;
|
||||
announceId?: string;
|
||||
triggerMessage: string;
|
||||
steerMessage: string;
|
||||
completionMessage?: string;
|
||||
internalEvents?: AgentInternalEvent[];
|
||||
summaryLine?: string;
|
||||
requesterOrigin?: DeliveryContext;
|
||||
completionDirectOrigin?: DeliveryContext;
|
||||
@@ -893,8 +902,10 @@ async function deliverSubagentAnnouncement(params: {
|
||||
requesterSessionKey: params.requesterSessionKey,
|
||||
announceId: params.announceId,
|
||||
triggerMessage: params.triggerMessage,
|
||||
steerMessage: params.steerMessage,
|
||||
summaryLine: params.summaryLine,
|
||||
requesterOrigin: params.requesterOrigin,
|
||||
internalEvents: params.internalEvents,
|
||||
signal: params.signal,
|
||||
}),
|
||||
direct: async () =>
|
||||
@@ -902,6 +913,7 @@ async function deliverSubagentAnnouncement(params: {
|
||||
targetRequesterSessionKey: params.targetRequesterSessionKey,
|
||||
triggerMessage: params.triggerMessage,
|
||||
completionMessage: params.completionMessage,
|
||||
internalEvents: params.internalEvents,
|
||||
directIdempotencyKey: params.directIdempotencyKey,
|
||||
completionDirectOrigin: params.completionDirectOrigin,
|
||||
completionRouteMode: params.completionRouteMode,
|
||||
@@ -1052,7 +1064,15 @@ function buildAnnounceReplyInstruction(params: {
|
||||
if (params.expectsCompletionMessage) {
|
||||
return `A completed ${params.announceType} is ready for user delivery. Convert the result above into your normal assistant voice and send that user-facing update now. Keep this internal context private (don't mention system/log/stats/session details or announce type).`;
|
||||
}
|
||||
return `A completed ${params.announceType} is ready for user delivery. Convert the result above into your normal assistant voice and send that user-facing update now. Keep this internal context private (don't mention system/log/stats/session details or announce type), and do not copy the system message verbatim. Reply ONLY: ${SILENT_REPLY_TOKEN} if this exact result was already delivered to the user in this same turn.`;
|
||||
return `A completed ${params.announceType} is ready for user delivery. Convert the result above into your normal assistant voice and send that user-facing update now. Keep this internal context private (don't mention system/log/stats/session details or announce type), and do not copy the internal event text verbatim. Reply ONLY: ${SILENT_REPLY_TOKEN} if this exact result was already delivered to the user in this same turn.`;
|
||||
}
|
||||
|
||||
function buildAnnounceSteerMessage(events: AgentInternalEvent[]): string {
|
||||
const rendered = formatAgentInternalEventsForPrompt(events);
|
||||
if (!rendered) {
|
||||
return "A background task finished. Process the completion update now.";
|
||||
}
|
||||
return rendered;
|
||||
}
|
||||
|
||||
export async function runSubagentAnnounceFlow(params: {
|
||||
@@ -1217,6 +1237,8 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
const findings = reply || "(no output)";
|
||||
let completionMessage = "";
|
||||
let triggerMessage = "";
|
||||
let steerMessage = "";
|
||||
let internalEvents: AgentInternalEvent[] = [];
|
||||
|
||||
let requesterIsSubagent = requesterDepth >= 1;
|
||||
// If the requester subagent has already finished, bubble the announce to its
|
||||
@@ -1285,15 +1307,23 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
outcome,
|
||||
announceType,
|
||||
});
|
||||
const internalSummaryMessage = [
|
||||
`[System Message] [sessionId: ${announceSessionId}] A ${announceType} "${taskLabel}" just ${statusLabel}.`,
|
||||
"",
|
||||
"Result:",
|
||||
findings,
|
||||
"",
|
||||
statsLine,
|
||||
].join("\n");
|
||||
triggerMessage = [internalSummaryMessage, "", replyInstruction].join("\n");
|
||||
internalEvents = [
|
||||
{
|
||||
type: "task_completion",
|
||||
source: announceType === "cron job" ? "cron" : "subagent",
|
||||
childSessionKey: params.childSessionKey,
|
||||
childSessionId: announceSessionId,
|
||||
announceType,
|
||||
taskLabel,
|
||||
status: outcome.status,
|
||||
statusLabel,
|
||||
result: findings,
|
||||
statsLine,
|
||||
replyInstruction,
|
||||
},
|
||||
];
|
||||
triggerMessage = buildAnnounceSteerMessage(internalEvents);
|
||||
steerMessage = triggerMessage;
|
||||
|
||||
const announceId = buildAnnounceIdFromChildRun({
|
||||
childSessionKey: params.childSessionKey,
|
||||
@@ -1329,7 +1359,9 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
requesterSessionKey: targetRequesterSessionKey,
|
||||
announceId,
|
||||
triggerMessage,
|
||||
steerMessage,
|
||||
completionMessage,
|
||||
internalEvents,
|
||||
summaryLine: taskLabel,
|
||||
requesterOrigin:
|
||||
expectsCompletionMessage && !requesterIsSubagent
|
||||
|
||||
Reference in New Issue
Block a user