@chrysb/alphaclaw 0.6.2-beta.3 → 0.6.2-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -276,10 +276,70 @@ export const getCronJobHealthClassName = (health = "") => {
276
276
 
277
277
  export const formatTokenCount = (value) => formatInteger(Number(value || 0));
278
278
  export const formatCost = (value) => formatUsd(Number(value || 0));
279
+ const hasHeartbeatOnlySummary = (job = {}) => {
280
+ const state = job?.state || {};
281
+ try {
282
+ return String(JSON.stringify(state) || "")
283
+ .toUpperCase()
284
+ .includes("HEARTBEAT_OK");
285
+ } catch {
286
+ return false;
287
+ }
288
+ };
289
+ const hasHeartbeatSummaryInLatestRun = ({
290
+ jobId = "",
291
+ bulkRunsByJobId = {},
292
+ } = {}) => {
293
+ const safeJobId = String(jobId || "").trim();
294
+ if (!safeJobId) return false;
295
+ const entries = Array.isArray(bulkRunsByJobId?.[safeJobId]?.entries)
296
+ ? bulkRunsByJobId[safeJobId].entries
297
+ : [];
298
+ if (entries.length === 0) return false;
299
+ const latestEntry = entries.reduce((latestValue, candidate) =>
300
+ Number(candidate?.ts || 0) > Number(latestValue?.ts || 0)
301
+ ? candidate
302
+ : latestValue);
303
+ const summaryCandidates = [
304
+ latestEntry?.summary,
305
+ latestEntry?.result?.summary,
306
+ latestEntry?.payload?.summary,
307
+ ];
308
+ return summaryCandidates.some((value) =>
309
+ String(value || "")
310
+ .toUpperCase()
311
+ .includes("HEARTBEAT_OK"));
312
+ };
313
+ const getLatestRunStatus = ({
314
+ job = {},
315
+ jobId = "",
316
+ bulkRunsByJobId = {},
317
+ } = {}) => {
318
+ const safeJobId = String(jobId || "").trim();
319
+ const entries = Array.isArray(bulkRunsByJobId?.[safeJobId]?.entries)
320
+ ? bulkRunsByJobId[safeJobId].entries
321
+ : [];
322
+ const latestEntry = entries.length > 0
323
+ ? entries.reduce((latestValue, candidate) =>
324
+ Number(candidate?.ts || 0) > Number(latestValue?.ts || 0)
325
+ ? candidate
326
+ : latestValue)
327
+ : null;
328
+ const status = String(
329
+ latestEntry?.status ||
330
+ job?.state?.lastStatus ||
331
+ job?.state?.lastRunStatus ||
332
+ "",
333
+ )
334
+ .trim()
335
+ .toLowerCase();
336
+ return status;
337
+ };
279
338
 
280
- export const buildCronOptimizationWarnings = (jobs = []) => {
339
+ export const buildCronOptimizationWarnings = (jobs = [], bulkRunsByJobId = {}) => {
281
340
  const warnings = [];
282
341
  jobs.forEach((job) => {
342
+ const jobId = String(job?.id || "");
283
343
  const prompt = String(job?.payload?.message || "").toLowerCase();
284
344
  const deliveryMode = String(job?.delivery?.mode || "").toLowerCase();
285
345
  if (
@@ -301,13 +361,21 @@ export const buildCronOptimizationWarnings = (jobs = []) => {
301
361
  body: `Consecutive errors: ${Number(job?.state?.consecutiveErrors || 0)}.`,
302
362
  });
303
363
  }
364
+ const latestStatus = getLatestRunStatus({
365
+ job,
366
+ jobId,
367
+ bulkRunsByJobId,
368
+ });
304
369
  if (
305
370
  job?.state?.lastDelivered === false &&
306
- String(job?.state?.lastDeliveryStatus || "").trim().toLowerCase() === "not-delivered"
371
+ String(job?.state?.lastDeliveryStatus || "").trim().toLowerCase() === "not-delivered" &&
372
+ latestStatus !== "ok" &&
373
+ !hasHeartbeatOnlySummary(job) &&
374
+ !hasHeartbeatSummaryInLatestRun({ jobId, bulkRunsByJobId })
307
375
  ) {
308
376
  warnings.push({
309
377
  tone: "warning",
310
- jobId: String(job?.id || ""),
378
+ jobId,
311
379
  title: `${job.name || job.id}: not delivered`,
312
380
  body: "Latest run completed but was not delivered.",
313
381
  });
@@ -186,7 +186,7 @@ export const CronOverview = ({
186
186
  const enabledCount = jobs.filter((job) => job.enabled !== false).length;
187
187
  const disabledCount = jobs.length - enabledCount;
188
188
  const nextRunMs = getNextScheduledRunAcrossJobs(jobs);
189
- const warnings = buildCronOptimizationWarnings(jobs);
189
+ const warnings = buildCronOptimizationWarnings(jobs, bulkRunsByJobId);
190
190
  const recentRuns = useMemo(
191
191
  () => flattenRecentRuns({ bulkRunsByJobId, jobs }),
192
192
  [bulkRunsByJobId, jobs],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.6.2-beta.3",
3
+ "version": "0.6.2-beta.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -31,7 +31,7 @@
31
31
  "dependencies": {
32
32
  "express": "^4.21.0",
33
33
  "http-proxy": "^1.18.1",
34
- "openclaw": "^2026.3.8"
34
+ "openclaw": "2026.3.11"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@vitest/coverage-v8": "^4.0.18",