openkbs 0.0.67 → 0.0.70

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.
Files changed (62) hide show
  1. package/README.md +1 -0
  2. package/elastic/README.md +1 -1
  3. package/elastic/functions.md +5 -5
  4. package/elastic/pulse.md +2 -2
  5. package/package.json +2 -2
  6. package/scripts/deploy.js +68 -0
  7. package/src/actions.js +7 -0
  8. package/templates/.claude/skills/openkbs/SKILL.md +37 -8
  9. package/templates/.claude/skills/openkbs/examples/monitoring-bot/README.md +55 -0
  10. package/templates/.claude/skills/openkbs/examples/monitoring-bot/app/instructions.txt +40 -0
  11. package/templates/.claude/skills/openkbs/examples/monitoring-bot/app/settings.json +41 -0
  12. package/templates/.claude/skills/openkbs/examples/monitoring-bot/openkbs.json +3 -0
  13. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/actions.js +141 -0
  14. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/handler.js +32 -0
  15. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/memoryHelpers.js +91 -0
  16. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onCronjob.js +105 -0
  17. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onPublicAPIRequest.js +165 -0
  18. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onRequest.js +2 -0
  19. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onResponse.js +2 -0
  20. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Frontend/contentRender.js +74 -0
  21. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Frontend/contentRender.json +3 -0
  22. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/auth/index.mjs +228 -0
  23. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/auth/package.json +7 -0
  24. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/posts/index.mjs +287 -0
  25. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/posts/package.json +10 -0
  26. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/settings.json +4 -0
  27. package/templates/.claude/skills/openkbs/examples/nodejs-demo/openkbs.json +16 -0
  28. package/templates/.claude/skills/openkbs/examples/nodejs-demo/site/index.html +658 -0
  29. package/templates/.claude/skills/openkbs/examples/nodejs-demo/site/settings.json +4 -0
  30. package/templates/.claude/skills/openkbs/patterns/cronjob-batch-processing.md +278 -0
  31. package/templates/.claude/skills/openkbs/patterns/cronjob-monitoring.md +341 -0
  32. package/templates/.claude/skills/openkbs/patterns/file-upload.md +205 -0
  33. package/templates/.claude/skills/openkbs/patterns/image-generation.md +139 -0
  34. package/templates/.claude/skills/openkbs/patterns/memory-system.md +264 -0
  35. package/templates/.claude/skills/openkbs/patterns/public-api-item-proxy.md +254 -0
  36. package/templates/.claude/skills/openkbs/patterns/scheduled-tasks.md +157 -0
  37. package/templates/.claude/skills/openkbs/patterns/telegram-webhook.md +424 -0
  38. package/templates/.claude/skills/openkbs/patterns/telegram.md +222 -0
  39. package/templates/.claude/skills/openkbs/patterns/vectordb-archive.md +231 -0
  40. package/templates/.claude/skills/openkbs/patterns/video-generation.md +145 -0
  41. package/templates/.claude/skills/openkbs/patterns/web-publishing.md +257 -0
  42. package/templates/.claude/skills/openkbs/reference/backend-sdk.md +13 -2
  43. package/templates/.claude/skills/openkbs/reference/elastic-services.md +61 -29
  44. package/templates/platform/README.md +15 -50
  45. package/templates/platform/agents/assistant/app/icon.png +0 -0
  46. package/templates/platform/agents/assistant/app/instructions.txt +9 -21
  47. package/templates/platform/agents/assistant/app/settings.json +11 -15
  48. package/templates/platform/agents/assistant/src/Events/actions.js +31 -62
  49. package/templates/platform/agents/assistant/src/Events/handler.js +54 -0
  50. package/templates/platform/agents/assistant/src/Events/onRequest.js +3 -0
  51. package/templates/platform/agents/assistant/src/Events/onRequest.json +5 -0
  52. package/templates/platform/agents/assistant/src/Events/onResponse.js +2 -40
  53. package/templates/platform/agents/assistant/src/Events/onResponse.json +4 -2
  54. package/templates/platform/agents/assistant/src/Frontend/contentRender.js +17 -16
  55. package/templates/platform/agents/assistant/src/Frontend/contentRender.json +1 -1
  56. package/templates/platform/functions/api/index.mjs +17 -23
  57. package/templates/platform/functions/api/package.json +4 -0
  58. package/templates/platform/openkbs.json +7 -2
  59. package/templates/platform/site/index.html +18 -19
  60. package/version.json +3 -3
  61. package/templates/.claude/skills/openkbs/examples/ai-copywriter-agent/scripts/run_job.js +0 -26
  62. package/templates/.claude/skills/openkbs/examples/ai-copywriter-agent/scripts/utils/agent_client.js +0 -265
@@ -0,0 +1,278 @@
1
+ # Cronjob Batch Processing Pattern
2
+
3
+ Complete working code for scheduled batch processing of files/documents.
4
+
5
+ ## Concept
6
+
7
+ Process files from storage in batches with:
8
+ - Timestamp-based deduplication (prevents reprocessing)
9
+ - Configurable batch size limits
10
+ - Processing state persistence
11
+ - Early termination to avoid timeouts
12
+
13
+ ## onCronjob.js
14
+
15
+ ```javascript
16
+ export const handler = async (event) => {
17
+ try {
18
+ // Load or create agent config
19
+ let agent;
20
+ try {
21
+ agent = await openkbs.getItem('agent');
22
+ agent = agent.item.body;
23
+ } catch (e) {
24
+ // First run - create config
25
+ const now = new Date();
26
+ agent = {
27
+ processingYear: String(now.getFullYear()),
28
+ processingMonth: String(now.getMonth() + 1).padStart(2, '0'),
29
+ batchSize: 10,
30
+ processingStatus: 'RUNNING', // or 'PAUSED'
31
+ lastProcessedTimestamp: null,
32
+ currentBatchDocumentCount: 0,
33
+ totalDocumentsProcessed: 0,
34
+ lastUpdated: now.toISOString(),
35
+ processingStartedAt: null
36
+ };
37
+
38
+ await openkbs.createItem({
39
+ itemType: 'agent',
40
+ itemId: 'agent',
41
+ body: agent
42
+ });
43
+ }
44
+
45
+ // Check if paused (optional - can use KB status instead)
46
+ if (agent.processingStatus === 'PAUSED') {
47
+ return { success: true, skipped: true, reason: 'paused' };
48
+ }
49
+
50
+ // Reset batch counter
51
+ const now = new Date();
52
+ agent.currentBatchDocumentCount = 0;
53
+ agent.currentBatchStartedAt = now.toISOString();
54
+ if (!agent.processingStartedAt) {
55
+ agent.processingStartedAt = now.toISOString();
56
+ }
57
+
58
+ // Process files
59
+ const result = await processFilesFromStorage(agent);
60
+
61
+ // Update statistics
62
+ agent.currentBatchDocumentCount = result.processed;
63
+ agent.totalDocumentsProcessed += result.processed;
64
+ agent.lastUpdated = now.toISOString();
65
+ if (result.lastTimestamp) {
66
+ agent.lastProcessedTimestamp = result.lastTimestamp;
67
+ }
68
+
69
+ // Save state
70
+ await openkbs.updateItem({
71
+ itemType: 'agent',
72
+ itemId: 'agent',
73
+ body: agent
74
+ });
75
+
76
+ return {
77
+ success: true,
78
+ documentsInBatch: result.processed,
79
+ totalProcessed: agent.totalDocumentsProcessed
80
+ };
81
+
82
+ } catch (error) {
83
+ return { success: false, error: error.message };
84
+ }
85
+ };
86
+
87
+ handler.CRON_SCHEDULE = "* * * * *"; // Every minute
88
+ ```
89
+
90
+ ## File Processing Logic
91
+
92
+ ```javascript
93
+ async function processFilesFromStorage(agent) {
94
+ // List files from storage
95
+ const files = await openkbs.kb({
96
+ action: 'listObjects',
97
+ namespace: 'files',
98
+ prefix: `invoices/${agent.processingYear}/${agent.processingMonth}/`
99
+ });
100
+
101
+ if (!files?.Contents?.length) {
102
+ return { processed: 0, lastTimestamp: null };
103
+ }
104
+
105
+ // Filter by timestamp (skip already processed)
106
+ const newFiles = files.Contents.filter(file => {
107
+ if (!agent.lastProcessedTimestamp) return true;
108
+ return file.LastModified > agent.lastProcessedTimestamp;
109
+ });
110
+
111
+ // Sort by timestamp (oldest first)
112
+ newFiles.sort((a, b) => new Date(a.LastModified) - new Date(b.LastModified));
113
+
114
+ // Limit to batch size
115
+ const batch = newFiles.slice(0, agent.batchSize);
116
+
117
+ let processed = 0;
118
+ let lastTimestamp = agent.lastProcessedTimestamp;
119
+
120
+ for (const file of batch) {
121
+ try {
122
+ // Parse folder structure: invoices/CompanyName-EIK/Year/Month/Type/
123
+ const pathParts = file.Key.split('/');
124
+ const companyFolder = pathParts[1]; // "CompanyName-123456789"
125
+ const eik = companyFolder.split('-').pop();
126
+
127
+ // Get file URL
128
+ const fileUrl = await openkbs.kb({
129
+ action: 'createPresignedURL',
130
+ namespace: 'files',
131
+ fileName: file.Key,
132
+ fileType: 'image/jpeg',
133
+ presignedOperation: 'getObject'
134
+ });
135
+
136
+ // Create chat for LLM processing
137
+ await openkbs.chats({
138
+ chatTitle: `Process: ${file.Key.split('/').pop()}`,
139
+ message: JSON.stringify([
140
+ { type: "text", text: `PROCESS_DOCUMENT\nCompany EIK: ${eik}\nFile: ${file.Key}` },
141
+ { type: "image_url", image_url: { url: fileUrl } }
142
+ ])
143
+ });
144
+
145
+ processed++;
146
+ lastTimestamp = file.LastModified;
147
+
148
+ // Early termination if approaching timeout
149
+ if (processed >= agent.batchSize) {
150
+ break;
151
+ }
152
+
153
+ } catch (e) {
154
+ console.error(`Failed to process ${file.Key}:`, e.message);
155
+ // Continue with next file
156
+ }
157
+ }
158
+
159
+ return { processed, lastTimestamp };
160
+ }
161
+ ```
162
+
163
+ ## Agent State Management
164
+
165
+ ```javascript
166
+ // Pause processing
167
+ async function pauseAgent() {
168
+ const agent = await openkbs.getItem('agent');
169
+ await openkbs.updateItem({
170
+ itemType: 'agent',
171
+ itemId: 'agent',
172
+ body: {
173
+ ...agent.item.body,
174
+ processingStatus: 'PAUSED',
175
+ lastUpdated: new Date().toISOString()
176
+ }
177
+ });
178
+ }
179
+
180
+ // Resume processing
181
+ async function resumeAgent() {
182
+ const agent = await openkbs.getItem('agent');
183
+ await openkbs.updateItem({
184
+ itemType: 'agent',
185
+ itemId: 'agent',
186
+ body: {
187
+ ...agent.item.body,
188
+ processingStatus: 'RUNNING',
189
+ lastUpdated: new Date().toISOString()
190
+ }
191
+ });
192
+ }
193
+
194
+ // Change processing period
195
+ async function setProcessingPeriod(year, month) {
196
+ const agent = await openkbs.getItem('agent');
197
+ await openkbs.updateItem({
198
+ itemType: 'agent',
199
+ itemId: 'agent',
200
+ body: {
201
+ ...agent.item.body,
202
+ processingYear: String(year),
203
+ processingMonth: String(month).padStart(2, '0'),
204
+ lastProcessedTimestamp: null, // Reset for new period
205
+ lastUpdated: new Date().toISOString()
206
+ }
207
+ });
208
+ }
209
+ ```
210
+
211
+ ## Frontend Status Display
212
+
213
+ ```javascript
214
+ // In contentRender.js Header component
215
+ const AgentStatusBadge = () => {
216
+ const [status, setStatus] = React.useState(null);
217
+
218
+ React.useEffect(() => {
219
+ const fetchStatus = async () => {
220
+ try {
221
+ const agent = await openkbs.items({ action: 'getItem', itemId: 'agent' });
222
+ setStatus(agent?.item?.body);
223
+ } catch (e) {}
224
+ };
225
+ fetchStatus();
226
+ const interval = setInterval(fetchStatus, 30000);
227
+ return () => clearInterval(interval);
228
+ }, []);
229
+
230
+ if (!status) return null;
231
+
232
+ const isRunning = status.processingStatus === 'RUNNING';
233
+
234
+ return (
235
+ <div style={{
236
+ padding: '4px 8px',
237
+ borderRadius: '4px',
238
+ background: isRunning ? '#4caf50' : '#ff9800',
239
+ color: 'white',
240
+ fontSize: '12px'
241
+ }}>
242
+ {isRunning ? '🟢 RUNNING' : '🟠 PAUSED'}
243
+ <div style={{ fontSize: '10px' }}>
244
+ {status.totalDocumentsProcessed} docs processed
245
+ </div>
246
+ </div>
247
+ );
248
+ };
249
+ ```
250
+
251
+ ## instructions.txt (LLM prompt)
252
+
253
+ ```
254
+ Batch Processing Commands:
255
+
256
+ Set processing period:
257
+ <setProcessingPeriod>{"year": 2025, "month": 1}</setProcessingPeriod>
258
+
259
+ Pause/resume:
260
+ <pauseAgent/>
261
+ <resumeAgent/>
262
+
263
+ Get status:
264
+ <getAgentStatus/>
265
+
266
+ Documents are processed automatically every minute when RUNNING.
267
+ Processing period determines which folder to scan.
268
+ ```
269
+
270
+ ## Key Points
271
+
272
+ 1. **Timestamp deduplication** - `lastProcessedTimestamp` prevents reprocessing
273
+ 2. **Batch limits** - Process max N files per run to avoid timeouts
274
+ 3. **State persistence** - Agent config stored in `agent` item
275
+ 4. **Folder structure** - Parse metadata from path (company, year, month)
276
+ 5. **Early termination** - Stop when batch limit reached
277
+ 6. **Error isolation** - One file failure doesn't stop the batch
278
+ 7. **CRON_SCHEDULE** - Export handler property to set schedule
@@ -0,0 +1,341 @@
1
+ # Cronjob Monitoring Pattern
2
+
3
+ Complete working code for continuous monitoring with pulse interval control.
4
+
5
+ ## Concept
6
+
7
+ Monitor external sources (cameras, APIs, sensors) with:
8
+ - Pulse interval control (execute every N minutes)
9
+ - Sleep/hibernation mode
10
+ - Weather/sensor data injection
11
+ - Reference image comparison
12
+ - Multi-source parallel fetching
13
+
14
+ ## onCronjob.js
15
+
16
+ ```javascript
17
+ import { getAgentSetting, setAgentSetting, setMemoryValue } from './memoryHelpers.js';
18
+
19
+ // Check if agent should execute this minute
20
+ async function shouldExecute() {
21
+ try {
22
+ // Check sleep mode
23
+ const sleepUntil = await getAgentSetting('agent_sleepUntil');
24
+ if (sleepUntil) {
25
+ const sleepUntilDate = new Date(sleepUntil);
26
+ if (sleepUntilDate > new Date()) {
27
+ return { execute: false, reason: 'sleeping' };
28
+ }
29
+ }
30
+
31
+ // Check pulse interval (default: every minute)
32
+ const pulseInterval = (await getAgentSetting('agent_pulseInterval')) || 1;
33
+ const currentMinute = new Date().getMinutes();
34
+
35
+ if (currentMinute % pulseInterval !== 0) {
36
+ return { execute: false, reason: 'pulse_interval' };
37
+ }
38
+
39
+ return { execute: true, interval: pulseInterval };
40
+ } catch (e) {
41
+ return { execute: true, interval: 1 };
42
+ }
43
+ }
44
+
45
+ export const handler = async (event) => {
46
+ try {
47
+ // Check execution conditions
48
+ const status = await shouldExecute();
49
+ if (!status.execute) {
50
+ return { success: true, skipped: true, reason: status.reason };
51
+ }
52
+
53
+ // Fetch all data sources in parallel
54
+ const [cameraData, weatherData, sensorData] = await Promise.all([
55
+ fetchCameraImages(),
56
+ fetchWeatherData(),
57
+ fetchSensorReadings()
58
+ ]);
59
+
60
+ // Build message for LLM analysis
61
+ const message = [];
62
+
63
+ message.push({
64
+ type: "text",
65
+ text: "PROCESS_MONITORING_CHECK"
66
+ });
67
+
68
+ // Add camera images
69
+ for (const camera of cameraData) {
70
+ message.push({
71
+ type: "text",
72
+ text: `\nšŸ“¹ ${camera.name} - ${camera.timestamp}`
73
+ });
74
+ message.push({
75
+ type: "image_url",
76
+ image_url: { url: camera.imageUrl }
77
+ });
78
+ }
79
+
80
+ // Add reference images from memory
81
+ await injectReferenceImages(message);
82
+
83
+ // Add weather data periodically (every hour)
84
+ const currentMinute = new Date().getMinutes();
85
+ if (currentMinute === 0) {
86
+ await injectWeatherData(message, weatherData);
87
+ }
88
+
89
+ // Create analysis chat
90
+ const now = new Date();
91
+ const timeStr = now.toLocaleTimeString('en-GB', {
92
+ hour: '2-digit',
93
+ minute: '2-digit',
94
+ timeZone: 'Europe/Sofia'
95
+ });
96
+
97
+ await openkbs.chats({
98
+ chatTitle: `Monitoring - ${timeStr}`,
99
+ message: JSON.stringify(message)
100
+ });
101
+
102
+ return { success: true, sources: cameraData.length };
103
+
104
+ } catch (error) {
105
+ return { success: false, error: error.message };
106
+ }
107
+ };
108
+
109
+ handler.CRON_SCHEDULE = "* * * * *";
110
+ ```
111
+
112
+ ## Data Fetching Helpers
113
+
114
+ ```javascript
115
+ // Fetch latest images from cameras
116
+ async function fetchCameraImages() {
117
+ const cameras = [
118
+ { name: 'Camera-North', url: 'https://cdn.example.com/cam1/' },
119
+ { name: 'Camera-South', url: 'https://cdn.example.com/cam2/' },
120
+ { name: 'Camera-East', url: 'https://cdn.example.com/cam3/' },
121
+ { name: 'Camera-West', url: 'https://cdn.example.com/cam4/' }
122
+ ];
123
+
124
+ const results = await Promise.all(cameras.map(async (cam) => {
125
+ try {
126
+ // Fetch directory listing
127
+ const response = await fetch(cam.url);
128
+ const html = await response.text();
129
+
130
+ // Parse image filenames (adjust regex for your CDN format)
131
+ const matches = html.match(/href="([^"]+\.jpg)"/g) || [];
132
+ const files = matches.map(m => m.match(/href="([^"]+)"/)[1]);
133
+
134
+ // Get latest image
135
+ const latest = files.sort().pop();
136
+
137
+ return {
138
+ name: cam.name,
139
+ imageUrl: `${cam.url}${latest}`,
140
+ timestamp: extractTimestamp(latest)
141
+ };
142
+ } catch (e) {
143
+ return { name: cam.name, error: e.message };
144
+ }
145
+ }));
146
+
147
+ return results.filter(r => r.imageUrl);
148
+ }
149
+
150
+ // Extract timestamp from filename (e.g., "20250104_143022.jpg")
151
+ function extractTimestamp(filename) {
152
+ const match = filename.match(/(\d{8})_(\d{6})/);
153
+ if (match) {
154
+ const [_, date, time] = match;
155
+ return `${date.slice(0,4)}-${date.slice(4,6)}-${date.slice(6,8)} ${time.slice(0,2)}:${time.slice(2,4)}:${time.slice(4,6)}`;
156
+ }
157
+ return 'Unknown';
158
+ }
159
+ ```
160
+
161
+ ## Reference Image Injection
162
+
163
+ ```javascript
164
+ // Inject stored reference images for comparison
165
+ async function injectReferenceImages(message) {
166
+ try {
167
+ const imageMemories = await openkbs.fetchItems({
168
+ beginsWith: 'memory_with_image_',
169
+ limit: 50,
170
+ field: 'itemId'
171
+ });
172
+
173
+ if (imageMemories?.items?.length > 0) {
174
+ message.push({
175
+ type: "text",
176
+ text: `\nšŸ“ø REFERENCE IMAGES (${imageMemories.items.length})\nUse these for comparison with current state.`
177
+ });
178
+
179
+ for (const item of imageMemories.items) {
180
+ const value = item.item?.body?.value;
181
+ if (value?.imageUrl) {
182
+ const description = value.description || 'Reference';
183
+
184
+ message.push({
185
+ type: "text",
186
+ text: `\nšŸ·ļø ${description}`
187
+ });
188
+ message.push({
189
+ type: "image_url",
190
+ image_url: { url: value.imageUrl }
191
+ });
192
+ }
193
+ }
194
+ }
195
+ } catch (e) {
196
+ console.error('Failed to fetch reference images:', e.message);
197
+ }
198
+ }
199
+ ```
200
+
201
+ ## Weather Data Injection
202
+
203
+ ```javascript
204
+ async function injectWeatherData(message, weatherData) {
205
+ message.push({
206
+ type: 'text',
207
+ text: `\nšŸ“Š WEATHER DATA\nAnalyze conditions and include weatherSummary in your assessment.`
208
+ });
209
+
210
+ // Add weather graphs/images if available
211
+ if (weatherData.graphs) {
212
+ for (const graph of weatherData.graphs) {
213
+ message.push({
214
+ type: "image_url",
215
+ image_url: { url: graph.url }
216
+ });
217
+ }
218
+ }
219
+
220
+ // Fetch current weather for multiple locations
221
+ const locations = [
222
+ { name: 'Location-A', lat: 42.20, lon: 22.81 },
223
+ { name: 'Location-B', lat: 42.06, lon: 22.77 }
224
+ ];
225
+
226
+ const weatherPromises = locations.map(async (loc) => {
227
+ try {
228
+ const response = await axios.get('https://webtools.openkbs.com/weather', {
229
+ params: { lat: loc.lat, lon: loc.lon }
230
+ });
231
+ return {
232
+ name: loc.name,
233
+ temp: response.data.main?.temp,
234
+ humidity: response.data.main?.humidity,
235
+ wind: response.data.wind?.speed,
236
+ condition: response.data.weather?.[0]?.main
237
+ };
238
+ } catch (e) {
239
+ return { name: loc.name, error: e.message };
240
+ }
241
+ });
242
+
243
+ const locationWeather = await Promise.all(weatherPromises);
244
+
245
+ // Store in memory for context
246
+ await setMemoryValue('memory_locations_weather', locationWeather);
247
+ }
248
+ ```
249
+
250
+ ## Agent Control Commands (actions.js)
251
+
252
+ ```javascript
253
+ // Hibernate agent until specific time
254
+ [/<hibernateAgent>([\\s\\S]*?)<\\/hibernateAgent>/s, async (match) => {
255
+ try {
256
+ const data = JSON.parse(match[1].trim());
257
+ let sleepUntil;
258
+
259
+ if (data.hours) {
260
+ sleepUntil = new Date(Date.now() + data.hours * 60 * 60 * 1000);
261
+ } else if (data.days) {
262
+ sleepUntil = new Date(Date.now() + data.days * 24 * 60 * 60 * 1000);
263
+ } else if (data.until) {
264
+ sleepUntil = new Date(data.until);
265
+ } else {
266
+ throw new Error('Specify hours, days, or until (ISO timestamp)');
267
+ }
268
+
269
+ await setAgentSetting('agent_sleepUntil', sleepUntil.toISOString());
270
+
271
+ return {
272
+ type: "AGENT_HIBERNATING",
273
+ sleepUntil: sleepUntil.toISOString(),
274
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
275
+ };
276
+ } catch (e) {
277
+ return { type: "HIBERNATE_ERROR", error: e.message, _meta_actions: ["REQUEST_CHAT_MODEL"] };
278
+ }
279
+ }],
280
+
281
+ // Set pulse interval (how often to execute)
282
+ [/<setAgentSettings>([\\s\\S]*?)<\\/setAgentSettings>/s, async (match) => {
283
+ try {
284
+ const data = JSON.parse(match[1].trim());
285
+
286
+ if (data.pulseInterval !== undefined) {
287
+ const interval = Math.max(1, Math.min(60, parseInt(data.pulseInterval)));
288
+ await setAgentSetting('agent_pulseInterval', interval);
289
+ }
290
+
291
+ if (data.wakeUp === true) {
292
+ await setAgentSetting('agent_sleepUntil', null);
293
+ }
294
+
295
+ return {
296
+ type: "AGENT_SETTINGS_UPDATED",
297
+ settings: data,
298
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
299
+ };
300
+ } catch (e) {
301
+ return { type: "SETTINGS_ERROR", error: e.message, _meta_actions: ["REQUEST_CHAT_MODEL"] };
302
+ }
303
+ }]
304
+ ```
305
+
306
+ ## instructions.txt (LLM prompt)
307
+
308
+ ```
309
+ Monitoring Commands:
310
+
311
+ Hibernate agent:
312
+ <hibernateAgent>{"hours": 24}</hibernateAgent>
313
+ <hibernateAgent>{"days": 7}</hibernateAgent>
314
+ <hibernateAgent>{"until": "2025-01-15T08:00:00Z"}</hibernateAgent>
315
+
316
+ Change pulse interval (1-60 minutes):
317
+ <setAgentSettings>{"pulseInterval": 5}</setAgentSettings>
318
+
319
+ Wake up agent:
320
+ <setAgentSettings>{"wakeUp": true}</setAgentSettings>
321
+
322
+ Store reference image:
323
+ <setMemory>{
324
+ "itemId": "memory_with_image_camera1_sunset",
325
+ "value": {
326
+ "imageUrl": "https://...",
327
+ "description": "Normal sunset glow - not fire",
328
+ "camera": "Camera-North"
329
+ }
330
+ }</setMemory>
331
+ ```
332
+
333
+ ## Key Points
334
+
335
+ 1. **Pulse interval** - Control execution frequency (every N minutes)
336
+ 2. **Sleep mode** - Hibernate until specific time
337
+ 3. **Parallel fetching** - Fetch all sources simultaneously
338
+ 4. **Reference images** - Store `memory_with_image_*` for comparison
339
+ 5. **Weather injection** - Add contextual data periodically
340
+ 6. **Timezone handling** - Use proper timezone for display
341
+ 7. **CRON_SCHEDULE** - Runs every minute, agent controls actual execution