refactor(cron): reuse shared run outcome telemetry types

This commit is contained in:
Peter Steinberger
2026-02-17 00:29:45 +00:00
parent a6466f2576
commit 80c7d04ad2
5 changed files with 77 additions and 155 deletions

View File

@@ -1,30 +1,27 @@
import type { CronConfig } from "../../config/types.cron.js";
import type { HeartbeatRunResult } from "../../infra/heartbeat-wake.js";
import type { CronJob, CronJobCreate, CronJobPatch, CronStoreFile } from "../types.js";
import type {
CronJob,
CronJobCreate,
CronJobPatch,
CronRunOutcome,
CronRunStatus,
CronRunTelemetry,
CronStoreFile,
} from "../types.js";
export type CronEvent = {
jobId: string;
action: "added" | "updated" | "removed" | "started" | "finished";
runAtMs?: number;
durationMs?: number;
status?: "ok" | "error" | "skipped";
status?: CronRunStatus;
error?: string;
summary?: string;
sessionId?: string;
sessionKey?: string;
nextRunAtMs?: number;
// Telemetry (best-effort)
model?: string;
provider?: string;
usage?: {
input_tokens?: number;
output_tokens?: number;
total_tokens?: number;
cache_read_tokens?: number;
cache_write_tokens?: number;
};
};
} & CronRunTelemetry;
export type Logger = {
debug: (obj: unknown, msg?: string) => void;
@@ -57,32 +54,20 @@ export type CronServiceDeps = {
wakeNowHeartbeatBusyMaxWaitMs?: number;
/** WakeMode=now: delay between runHeartbeatOnce retries while busy. */
wakeNowHeartbeatBusyRetryDelayMs?: number;
runIsolatedAgentJob: (params: { job: CronJob; message: string }) => Promise<{
status: "ok" | "error" | "skipped";
summary?: string;
/** Last non-empty agent text output (not truncated). */
outputText?: string;
error?: string;
sessionId?: string;
sessionKey?: string;
/**
* `true` when the isolated run already delivered its output to the target
* channel (including matching messaging-tool sends). See:
* https://github.com/openclaw/openclaw/issues/15692
*/
delivered?: boolean;
// Telemetry (best-effort)
model?: string;
provider?: string;
usage?: {
input_tokens?: number;
output_tokens?: number;
total_tokens?: number;
cache_read_tokens?: number;
cache_write_tokens?: number;
};
}>;
runIsolatedAgentJob: (params: { job: CronJob; message: string }) => Promise<
{
summary?: string;
/** Last non-empty agent text output (not truncated). */
outputText?: string;
/**
* `true` when the isolated run already delivered its output to the target
* channel (including matching messaging-tool sends). See:
* https://github.com/openclaw/openclaw/issues/15692
*/
delivered?: boolean;
} & CronRunOutcome &
CronRunTelemetry
>;
onEvent?: (evt: CronEvent) => void;
};

View File

@@ -1,8 +1,9 @@
import type { HeartbeatRunResult } from "../../infra/heartbeat-wake.js";
import type { CronJob, CronRunOutcome, CronRunStatus, CronRunTelemetry } from "../types.js";
import type { CronEvent, CronServiceState } from "./state.js";
import { DEFAULT_AGENT_ID } from "../../routing/session-key.js";
import { resolveCronDeliveryPlan } from "../delivery.js";
import { sweepCronRunSessions } from "../session-reaper.js";
import type { CronJob } from "../types.js";
import {
computeJobNextRunAtMs,
nextWakeAtMs,
@@ -10,7 +11,6 @@ import {
resolveJobPayloadTextForMain,
} from "./jobs.js";
import { locked } from "./locked.js";
import type { CronEvent, CronServiceState } from "./state.js";
import { ensureLoaded, persist } from "./store.js";
const MAX_TIMER_DELAY_MS = 60_000;
@@ -31,6 +31,13 @@ const MIN_REFIRE_GAP_MS = 2_000;
*/
const DEFAULT_JOB_TIMEOUT_MS = 10 * 60_000; // 10 minutes
type TimedCronRunOutcome = CronRunOutcome &
CronRunTelemetry & {
jobId: string;
startedAt: number;
endedAt: number;
};
/**
* Exponential backoff delays (in ms) indexed by consecutive error count.
* After the last entry the delay stays constant.
@@ -57,7 +64,7 @@ function applyJobResult(
state: CronServiceState,
job: CronJob,
result: {
status: "ok" | "error" | "skipped";
status: CronRunStatus;
error?: string;
startedAt: number;
endedAt: number;
@@ -229,25 +236,7 @@ export async function onTimer(state: CronServiceState) {
}));
});
const results: Array<{
jobId: string;
status: "ok" | "error" | "skipped";
error?: string;
summary?: string;
sessionId?: string;
sessionKey?: string;
model?: string;
provider?: string;
usage?: {
input_tokens?: number;
output_tokens?: number;
total_tokens?: number;
cache_read_tokens?: number;
cache_write_tokens?: number;
};
startedAt: number;
endedAt: number;
}> = [];
const results: TimedCronRunOutcome[] = [];
for (const { id, job } of dueJobs) {
const startedAt = state.deps.nowMs();
@@ -449,22 +438,7 @@ export async function runDueJobs(state: CronServiceState) {
async function executeJobCore(
state: CronServiceState,
job: CronJob,
): Promise<{
status: "ok" | "error" | "skipped";
error?: string;
summary?: string;
sessionId?: string;
sessionKey?: string;
model?: string;
provider?: string;
usage?: {
input_tokens?: number;
output_tokens?: number;
total_tokens?: number;
cache_read_tokens?: number;
cache_write_tokens?: number;
};
}> {
): Promise<CronRunOutcome & CronRunTelemetry> {
if (job.sessionTarget === "main") {
const text = resolveJobPayloadTextForMain(job);
if (!text) {
@@ -578,21 +552,9 @@ export async function executeJob(
emit(state, { jobId: job.id, action: "started", runAtMs: startedAt });
let coreResult: {
status: "ok" | "error" | "skipped";
error?: string;
summary?: string;
sessionId?: string;
sessionKey?: string;
model?: string;
provider?: string;
usage?: {
input_tokens?: number;
output_tokens?: number;
total_tokens?: number;
cache_read_tokens?: number;
cache_write_tokens?: number;
};
};
status: CronRunStatus;
} & CronRunOutcome &
CronRunTelemetry;
try {
coreResult = await executeJobCore(state, job);
} catch (err) {
@@ -619,21 +581,9 @@ function emitJobFinished(
state: CronServiceState,
job: CronJob,
result: {
status: "ok" | "error" | "skipped";
error?: string;
summary?: string;
sessionId?: string;
sessionKey?: string;
model?: string;
provider?: string;
usage?: {
input_tokens?: number;
output_tokens?: number;
total_tokens?: number;
cache_read_tokens?: number;
cache_write_tokens?: number;
};
},
status: CronRunStatus;
} & CronRunOutcome &
CronRunTelemetry,
runAtMs: number,
) {
emit(state, {