openclaw-scheduler 0.2.3 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.4] -- 2026-04-18
6
+
7
+ ### Fixed
8
+ - fix(package): include dispatch/completion.mjs in the published npm tarball so dispatch CLI and watcher startup no longer crash with ERR_MODULE_NOT_FOUND in installed deployments
9
+
5
10
  ## [0.2.3] -- 2026-04-16
6
11
 
7
12
  ### Fixed
@@ -0,0 +1,135 @@
1
+ const GENERIC_COMPLETION_TEXT_RE = /^(?:completed(?:\s*\([^\n)]*\))?|done|ok|okay|success|successful|complete|all set|none|n\/?a)$/i;
2
+ const TRIVIAL_CHATTER_RE = /^(?:hi|hello|hey|yo|sup|thanks|thank you|cool|nice|sure|yep|yeah|k|kk|roger|copy that)[.!?]*$/i;
3
+
4
+ export function normalizeCompletionText(value) {
5
+ if (typeof value !== 'string') return null;
6
+ const trimmed = value.trim();
7
+ return trimmed ? trimmed : null;
8
+ }
9
+
10
+ export function isMeaningfulCompletionText(value) {
11
+ const text = normalizeCompletionText(value);
12
+ if (!text) return false;
13
+
14
+ const normalized = text.toLowerCase().replace(/\s+/g, ' ').trim();
15
+ if (!normalized) return false;
16
+ if (GENERIC_COMPLETION_TEXT_RE.test(normalized)) return false;
17
+ if (TRIVIAL_CHATTER_RE.test(normalized)) return false;
18
+
19
+ const words = normalized.split(/\s+/).filter(Boolean);
20
+ if (words.length === 1) return false;
21
+
22
+ return true;
23
+ }
24
+
25
+ function shortSha(sha) {
26
+ const text = normalizeCompletionText(sha);
27
+ if (!text) return null;
28
+ return /^[0-9a-f]{7,40}$/i.test(text) ? text.slice(0, 7) : text;
29
+ }
30
+
31
+ function cloneChecklist(checklist) {
32
+ if (!checklist || typeof checklist !== 'object' || Array.isArray(checklist)) return null;
33
+ try {
34
+ return JSON.parse(JSON.stringify(checklist));
35
+ } catch {
36
+ return { ...checklist };
37
+ }
38
+ }
39
+
40
+ export function synthesizeCompletionReply({ checklist, sha } = {}) {
41
+ const normalizedChecklist = cloneChecklist(checklist);
42
+ const short = shortSha(sha);
43
+ const sentences = [];
44
+
45
+ if (normalizedChecklist?.tests_passed === true) {
46
+ sentences.push('Tests passed.');
47
+ }
48
+
49
+ if (normalizedChecklist?.pushed === true && short) {
50
+ sentences.push(`Pushed ${short}.`);
51
+ } else if (normalizedChecklist?.pushed === true) {
52
+ sentences.push('Changes pushed.');
53
+ } else if (short) {
54
+ sentences.push(`Commit ${short}.`);
55
+ }
56
+
57
+ const extraTrueFlags = normalizedChecklist
58
+ ? Object.entries(normalizedChecklist)
59
+ .filter(([key, value]) => value === true && !['work_complete', 'tests_passed', 'pushed'].includes(key))
60
+ .map(([key]) => key.replace(/_/g, ' '))
61
+ : [];
62
+
63
+ if (extraTrueFlags.length > 0) {
64
+ sentences.push(`Checks: ${extraTrueFlags.slice(0, 3).join(', ')}.`);
65
+ }
66
+
67
+ if (sentences.length === 0) return null;
68
+ return `Work complete. ${sentences.join(' ')}`.trim();
69
+ }
70
+
71
+ export function buildTerminalCompletionPayload({ summary, checklist, sha } = {}) {
72
+ const rawSummary = normalizeCompletionText(summary);
73
+ const normalizedChecklist = cloneChecklist(checklist);
74
+ const normalizedSha = normalizeCompletionText(sha);
75
+ const prose = isMeaningfulCompletionText(rawSummary) ? rawSummary : null;
76
+ const synthesizedReply = prose
77
+ ? null
78
+ : synthesizeCompletionReply({ checklist: normalizedChecklist, sha: normalizedSha });
79
+ const effectiveSummary = prose || synthesizedReply || rawSummary || null;
80
+ const deliveryText = prose || synthesizedReply || null;
81
+
82
+ return {
83
+ version: 1,
84
+ recordedAt: new Date().toISOString(),
85
+ summary: effectiveSummary,
86
+ deliveryText,
87
+ prose,
88
+ checklist: normalizedChecklist,
89
+ sha: normalizedSha,
90
+ debug: {
91
+ rawSummary,
92
+ synthesizedReply,
93
+ deliverySource: prose ? 'summary' : synthesizedReply ? 'synthesized' : 'none',
94
+ },
95
+ };
96
+ }
97
+
98
+ export function resolveCompletionDelivery({ lastReply, completion, fallbackSummary } = {}) {
99
+ const reply = normalizeCompletionText(lastReply);
100
+ const completionSummary = normalizeCompletionText(completion?.summary);
101
+ const completionDelivery = normalizeCompletionText(completion?.deliveryText);
102
+ const fallback = normalizeCompletionText(fallbackSummary);
103
+ const preferredSummary = completionSummary || fallback;
104
+ const meaningfulSummary = [completionSummary, fallback].find(isMeaningfulCompletionText) || null;
105
+
106
+ if (isMeaningfulCompletionText(reply)) {
107
+ return {
108
+ deliveryText: reply,
109
+ summary: preferredSummary || reply.slice(0, 500),
110
+ source: 'lastReply',
111
+ };
112
+ }
113
+
114
+ if (isMeaningfulCompletionText(completionDelivery)) {
115
+ return {
116
+ deliveryText: completionDelivery,
117
+ summary: completionSummary || completionDelivery,
118
+ source: completion?.debug?.deliverySource || 'completion',
119
+ };
120
+ }
121
+
122
+ if (meaningfulSummary) {
123
+ return {
124
+ deliveryText: meaningfulSummary,
125
+ summary: meaningfulSummary,
126
+ source: completionSummary && meaningfulSummary === completionSummary ? 'completion-summary' : 'summary',
127
+ };
128
+ }
129
+
130
+ return {
131
+ deliveryText: null,
132
+ summary: preferredSummary || null,
133
+ source: 'none',
134
+ };
135
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-scheduler",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "SQLite-backed job scheduler and workflow engine for OpenClaw agents",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -37,6 +37,7 @@
37
37
  "files": [
38
38
  "bin",
39
39
  "dispatch/529-recovery.mjs",
40
+ "dispatch/completion.mjs",
40
41
  "dispatch/config.example.json",
41
42
  "dispatch/deliver-watcher.sh",
42
43
  "dispatch/hooks.mjs",