Files
openclaw/src/cron/delivery.ts
2026-04-27 06:44:53 +01:00

178 lines
4.9 KiB
TypeScript

import type { CliDeps } from "../cli/deps.types.js";
import { createOutboundSendDeps } from "../cli/outbound-send-deps.js";
import type { OpenClawConfig } from "../config/types.js";
import { formatErrorMessage } from "../infra/errors.js";
import { deliverOutboundPayloads } from "../infra/outbound/deliver.js";
import { resolveAgentOutboundIdentity } from "../infra/outbound/identity.js";
import { buildOutboundSessionContext } from "../infra/outbound/session-context.js";
import { getChildLogger } from "../logging.js";
import {
resolveFailureDestination,
type CronFailureDeliveryPlan,
type CronFailureDestinationInput,
type CronDeliveryPlan,
resolveCronDeliveryPlan,
} from "./delivery-plan.js";
import {
resolveDeliveryTarget,
type DeliveryTargetResolution,
} from "./isolated-agent/delivery-target.js";
import { resolveCronNotificationSessionKey } from "./session-target.js";
import type { CronMessageChannel } from "./types.js";
export {
resolveCronDeliveryPlan,
resolveFailureDestination,
type CronDeliveryPlan,
type CronFailureDeliveryPlan,
type CronFailureDestinationInput,
};
const FAILURE_NOTIFICATION_TIMEOUT_MS = 30_000;
const cronDeliveryLogger = getChildLogger({ subsystem: "cron-delivery" });
export type CronAnnounceTarget = {
channel?: string;
to?: string;
accountId?: string;
sessionKey?: string;
};
type SuccessfulDeliveryTarget = Extract<DeliveryTargetResolution, { ok: true }>;
async function resolveCronAnnounceDelivery(params: {
cfg: OpenClawConfig;
agentId: string;
jobId: string;
target: CronAnnounceTarget;
}): Promise<
| {
ok: true;
resolvedTarget: SuccessfulDeliveryTarget;
session: ReturnType<typeof buildOutboundSessionContext>;
identity: ReturnType<typeof resolveAgentOutboundIdentity>;
}
| { ok: false; error: Error }
> {
const resolvedTarget = await resolveDeliveryTarget(params.cfg, params.agentId, {
channel: params.target.channel as CronMessageChannel | undefined,
to: params.target.to,
accountId: params.target.accountId,
sessionKey: params.target.sessionKey,
});
if (!resolvedTarget.ok) {
return { ok: false, error: resolvedTarget.error };
}
const identity = resolveAgentOutboundIdentity(params.cfg, params.agentId);
const session = buildOutboundSessionContext({
cfg: params.cfg,
agentId: params.agentId,
sessionKey: resolveCronNotificationSessionKey({
jobId: params.jobId,
sessionKey: params.target.sessionKey,
}),
});
return {
ok: true,
resolvedTarget,
session,
identity,
};
}
async function deliverCronAnnouncePayload(params: {
deps: CliDeps;
cfg: OpenClawConfig;
delivery: {
resolvedTarget: SuccessfulDeliveryTarget;
session: ReturnType<typeof buildOutboundSessionContext>;
identity: ReturnType<typeof resolveAgentOutboundIdentity>;
};
message: string;
abortSignal: AbortSignal;
}): Promise<void> {
await deliverOutboundPayloads({
cfg: params.cfg,
channel: params.delivery.resolvedTarget.channel,
to: params.delivery.resolvedTarget.to,
accountId: params.delivery.resolvedTarget.accountId,
threadId: params.delivery.resolvedTarget.threadId,
payloads: [{ text: params.message }],
session: params.delivery.session,
identity: params.delivery.identity,
bestEffort: false,
deps: createOutboundSendDeps(params.deps),
abortSignal: params.abortSignal,
});
}
export async function sendCronAnnouncePayloadStrict(params: {
deps: CliDeps;
cfg: OpenClawConfig;
agentId: string;
jobId: string;
target: CronAnnounceTarget;
message: string;
abortSignal: AbortSignal;
}): Promise<void> {
const delivery = await resolveCronAnnounceDelivery(params);
if (!delivery.ok) {
throw delivery.error;
}
await deliverCronAnnouncePayload({
deps: params.deps,
cfg: params.cfg,
delivery,
message: params.message,
abortSignal: params.abortSignal,
});
}
export async function sendFailureNotificationAnnounce(
deps: CliDeps,
cfg: OpenClawConfig,
agentId: string,
jobId: string,
target: CronAnnounceTarget,
message: string,
): Promise<void> {
const delivery = await resolveCronAnnounceDelivery({ cfg, agentId, jobId, target });
if (!delivery.ok) {
cronDeliveryLogger.warn(
{ error: delivery.error.message },
"cron: failed to resolve failure destination target",
);
return;
}
const abortController = new AbortController();
const timeout = setTimeout(() => {
abortController.abort();
}, FAILURE_NOTIFICATION_TIMEOUT_MS);
try {
await deliverCronAnnouncePayload({
deps,
cfg,
delivery,
message,
abortSignal: abortController.signal,
});
} catch (err) {
cronDeliveryLogger.warn(
{
err: formatErrorMessage(err),
channel: delivery.resolvedTarget.channel,
to: delivery.resolvedTarget.to,
},
"cron: failure destination announce failed",
);
} finally {
clearTimeout(timeout);
}
}