0xkobold 0.0.1
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/.agents/skills/nextjs-best-practices/SKILL.md +208 -0
- package/.agents/skills/sql-optimization-patterns/SKILL.md +509 -0
- package/HEARTBEAT.md +45 -0
- package/README.md +197 -0
- package/USAGE.md +191 -0
- package/dist/package.json +77 -0
- package/dist/src/agent/pi-adapter.js +307 -0
- package/dist/src/agent/pi-adapter.js.map +1 -0
- package/dist/src/agent/tool-adapter.js +86 -0
- package/dist/src/agent/tool-adapter.js.map +1 -0
- package/dist/src/approval/queue.js +114 -0
- package/dist/src/approval/queue.js.map +1 -0
- package/dist/src/ascii-kobold.js +76 -0
- package/dist/src/ascii-kobold.js.map +1 -0
- package/dist/src/cli/client.js +217 -0
- package/dist/src/cli/client.js.map +1 -0
- package/dist/src/cli/commands/agent.js +272 -0
- package/dist/src/cli/commands/agent.js.map +1 -0
- package/dist/src/cli/commands/chat.js +234 -0
- package/dist/src/cli/commands/chat.js.map +1 -0
- package/dist/src/cli/commands/config.js +202 -0
- package/dist/src/cli/commands/config.js.map +1 -0
- package/dist/src/cli/commands/daemon.js +203 -0
- package/dist/src/cli/commands/daemon.js.map +1 -0
- package/dist/src/cli/commands/gateway.js +184 -0
- package/dist/src/cli/commands/gateway.js.map +1 -0
- package/dist/src/cli/commands/init.js +175 -0
- package/dist/src/cli/commands/init.js.map +1 -0
- package/dist/src/cli/commands/kobold.js +21 -0
- package/dist/src/cli/commands/kobold.js.map +1 -0
- package/dist/src/cli/commands/logs.js +27 -0
- package/dist/src/cli/commands/logs.js.map +1 -0
- package/dist/src/cli/commands/mode.js +121 -0
- package/dist/src/cli/commands/mode.js.map +1 -0
- package/dist/src/cli/commands/persona.js +261 -0
- package/dist/src/cli/commands/persona.js.map +1 -0
- package/dist/src/cli/commands/start.js +66 -0
- package/dist/src/cli/commands/start.js.map +1 -0
- package/dist/src/cli/commands/status.js +117 -0
- package/dist/src/cli/commands/status.js.map +1 -0
- package/dist/src/cli/commands/stop.js +27 -0
- package/dist/src/cli/commands/stop.js.map +1 -0
- package/dist/src/cli/commands/system.js +128 -0
- package/dist/src/cli/commands/system.js.map +1 -0
- package/dist/src/cli/commands/tui.js +103 -0
- package/dist/src/cli/commands/tui.js.map +1 -0
- package/dist/src/cli/commands/update.js +133 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/extensions/discord.js +113 -0
- package/dist/src/cli/extensions/discord.js.map +1 -0
- package/dist/src/cli/extensions/env.js +91 -0
- package/dist/src/cli/extensions/env.js.map +1 -0
- package/dist/src/cli/extensions/heartbeat.js +78 -0
- package/dist/src/cli/extensions/heartbeat.js.map +1 -0
- package/dist/src/cli/index.js +24 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/program.js +70 -0
- package/dist/src/cli/program.js.map +1 -0
- package/dist/src/cli/repl.js +184 -0
- package/dist/src/cli/repl.js.map +1 -0
- package/dist/src/cli/shared/discord-service.js +102 -0
- package/dist/src/cli/shared/discord-service.js.map +1 -0
- package/dist/src/config/index.js +10 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/config/loader.js +401 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/config/paths.js +84 -0
- package/dist/src/config/paths.js.map +1 -0
- package/dist/src/config/types.js +8 -0
- package/dist/src/config/types.js.map +1 -0
- package/dist/src/context-detector.js +60 -0
- package/dist/src/context-detector.js.map +1 -0
- package/dist/src/discord/index.js +376 -0
- package/dist/src/discord/index.js.map +1 -0
- package/dist/src/event-bus/index.js +97 -0
- package/dist/src/event-bus/index.js.map +1 -0
- package/dist/src/extensions/command-args.js +68 -0
- package/dist/src/extensions/command-args.js.map +1 -0
- package/dist/src/extensions/core/agent-registry-extension.js +541 -0
- package/dist/src/extensions/core/agent-registry-extension.js.map +1 -0
- package/dist/src/extensions/core/agent-worker.js +148 -0
- package/dist/src/extensions/core/agent-worker.js.map +1 -0
- package/dist/src/extensions/core/compaction-safeguard.js +154 -0
- package/dist/src/extensions/core/compaction-safeguard.js.map +1 -0
- package/dist/src/extensions/core/confirm-destructive.js +43 -0
- package/dist/src/extensions/core/confirm-destructive.js.map +1 -0
- package/dist/src/extensions/core/context-aware-extension.js +124 -0
- package/dist/src/extensions/core/context-aware-extension.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/extension.js +124 -0
- package/dist/src/extensions/core/context-pruning/extension.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/pruner.js +312 -0
- package/dist/src/extensions/core/context-pruning/pruner.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/runtime.js +48 -0
- package/dist/src/extensions/core/context-pruning/runtime.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/settings.js +105 -0
- package/dist/src/extensions/core/context-pruning/settings.js.map +1 -0
- package/dist/src/extensions/core/dirty-repo-guard.js +42 -0
- package/dist/src/extensions/core/dirty-repo-guard.js.map +1 -0
- package/dist/src/extensions/core/discord-channel-extension.js +205 -0
- package/dist/src/extensions/core/discord-channel-extension.js.map +1 -0
- package/dist/src/extensions/core/discord-extension.js +142 -0
- package/dist/src/extensions/core/discord-extension.js.map +1 -0
- package/dist/src/extensions/core/env-loader-extension.js +157 -0
- package/dist/src/extensions/core/env-loader-extension.js.map +1 -0
- package/dist/src/extensions/core/fileops-extension.js +699 -0
- package/dist/src/extensions/core/fileops-extension.js.map +1 -0
- package/dist/src/extensions/core/gateway-extension.js +730 -0
- package/dist/src/extensions/core/gateway-extension.js.map +1 -0
- package/dist/src/extensions/core/git-checkpoint.js +46 -0
- package/dist/src/extensions/core/git-checkpoint.js.map +1 -0
- package/dist/src/extensions/core/handoff-extension.js +206 -0
- package/dist/src/extensions/core/handoff-extension.js.map +1 -0
- package/dist/src/extensions/core/heartbeat-extension.js +373 -0
- package/dist/src/extensions/core/heartbeat-extension.js.map +1 -0
- package/dist/src/extensions/core/mcp-extension.js +413 -0
- package/dist/src/extensions/core/mcp-extension.js.map +1 -0
- package/dist/src/extensions/core/mode-manager-extension.js +562 -0
- package/dist/src/extensions/core/mode-manager-extension.js.map +1 -0
- package/dist/src/extensions/core/multi-channel-extension.js +435 -0
- package/dist/src/extensions/core/multi-channel-extension.js.map +1 -0
- package/dist/src/extensions/core/ollama-provider-extension.js +66 -0
- package/dist/src/extensions/core/ollama-provider-extension.js.map +1 -0
- package/dist/src/extensions/core/onboarding-extension.js +122 -0
- package/dist/src/extensions/core/onboarding-extension.js.map +1 -0
- package/dist/src/extensions/core/persona-loader-extension.js +139 -0
- package/dist/src/extensions/core/persona-loader-extension.js.map +1 -0
- package/dist/src/extensions/core/pi-notify-extension.js +70 -0
- package/dist/src/extensions/core/pi-notify-extension.js.map +1 -0
- package/dist/src/extensions/core/protected-paths.js +24 -0
- package/dist/src/extensions/core/protected-paths.js.map +1 -0
- package/dist/src/extensions/core/questionnaire-extension.js +242 -0
- package/dist/src/extensions/core/questionnaire-extension.js.map +1 -0
- package/dist/src/extensions/core/self-update-extension.js +181 -0
- package/dist/src/extensions/core/self-update-extension.js.map +1 -0
- package/dist/src/extensions/core/session-bridge-extension.js +78 -0
- package/dist/src/extensions/core/session-bridge-extension.js.map +1 -0
- package/dist/src/extensions/core/session-manager-extension.js +319 -0
- package/dist/src/extensions/core/session-manager-extension.js.map +1 -0
- package/dist/src/extensions/core/session-name-extension.js +88 -0
- package/dist/src/extensions/core/session-name-extension.js.map +1 -0
- package/dist/src/extensions/core/session-pruning-extension.js +480 -0
- package/dist/src/extensions/core/session-pruning-extension.js.map +1 -0
- package/dist/src/extensions/core/task-manager-extension.js +661 -0
- package/dist/src/extensions/core/task-manager-extension.js.map +1 -0
- package/dist/src/extensions/core/update-extension.js +438 -0
- package/dist/src/extensions/core/update-extension.js.map +1 -0
- package/dist/src/extensions/core/websearch-extension.js +463 -0
- package/dist/src/extensions/core/websearch-extension.js.map +1 -0
- package/dist/src/extensions/index.js +5 -0
- package/dist/src/extensions/index.js.map +1 -0
- package/dist/src/extensions/loader.js +80 -0
- package/dist/src/extensions/loader.js.map +1 -0
- package/dist/src/gateway/index.js +353 -0
- package/dist/src/gateway/index.js.map +1 -0
- package/dist/src/index.js +150 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/llm/anthropic.js +86 -0
- package/dist/src/llm/anthropic.js.map +1 -0
- package/dist/src/llm/index.js +9 -0
- package/dist/src/llm/index.js.map +1 -0
- package/dist/src/llm/ollama.js +113 -0
- package/dist/src/llm/ollama.js.map +1 -0
- package/dist/src/llm/router.js +145 -0
- package/dist/src/llm/router.js.map +1 -0
- package/dist/src/llm/types.js +7 -0
- package/dist/src/llm/types.js.map +1 -0
- package/dist/src/memory/index.js +5 -0
- package/dist/src/memory/index.js.map +1 -0
- package/dist/src/memory/store.js +91 -0
- package/dist/src/memory/store.js.map +1 -0
- package/dist/src/pi-config.js +80 -0
- package/dist/src/pi-config.js.map +1 -0
- package/dist/src/skills/builtin/file.js +184 -0
- package/dist/src/skills/builtin/file.js.map +1 -0
- package/dist/src/skills/builtin/shell.js +100 -0
- package/dist/src/skills/builtin/shell.js.map +1 -0
- package/dist/src/skills/builtin/subagent.js +62 -0
- package/dist/src/skills/builtin/subagent.js.map +1 -0
- package/dist/src/skills/hello.js +42 -0
- package/dist/src/skills/hello.js.map +1 -0
- package/dist/src/skills/index.js +11 -0
- package/dist/src/skills/index.js.map +1 -0
- package/dist/src/skills/loader.js +382 -0
- package/dist/src/skills/loader.js.map +1 -0
- package/dist/src/skills/types.js +8 -0
- package/dist/src/skills/types.js.map +1 -0
- package/dist/src/utils/working-dir.js +71 -0
- package/dist/src/utils/working-dir.js.map +1 -0
- package/package.json +77 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +118 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +198 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +284 -0
- package/skills/discord/SKILL.md +197 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/food-order/SKILL.md +48 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/gh-issues/SKILL.md +865 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +163 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +122 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +310 -0
- package/skills/nano-banana-pro/SKILL.md +58 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +184 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/notion/SKILL.md +172 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-image-gen/SKILL.md +89 -0
- package/skills/openai-image-gen/scripts/gen.py +240 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +52 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
- package/skills/openhue/SKILL.md +112 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/session-logs/SKILL.md +115 -0
- package/skills/sherpa-onnx-tts/SKILL.md +103 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +370 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +111 -0
- package/skills/skill-creator/scripts/quick_validate.py +101 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +46 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +153 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +95 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- package/skills/weather/SKILL.md +112 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat Extension - Koclaw-style Periodic Check-ins
|
|
3
|
+
*
|
|
4
|
+
* Monitors agent health via periodic LLM check-ins using HEARTBEAT.md.
|
|
5
|
+
* This is a simplified implementation inspired by koclaw/openclaw.
|
|
6
|
+
*
|
|
7
|
+
* How it works:
|
|
8
|
+
* 1. Config from kobold.json (agents.defaults.heartbeat)
|
|
9
|
+
* 2. Agent reads HEARTBEAT.md and decides if action is needed
|
|
10
|
+
* 3. If response is HEARTBEAT_OK (with optional short text), it's suppressed
|
|
11
|
+
*
|
|
12
|
+
* Configuration in kobold.json:
|
|
13
|
+
* {
|
|
14
|
+
* "agents": {
|
|
15
|
+
* "defaults": {
|
|
16
|
+
* "heartbeat": {
|
|
17
|
+
* "enabled": true,
|
|
18
|
+
* "every": "30m",
|
|
19
|
+
* "prompt": "Read HEARTBEAT.md...",
|
|
20
|
+
* "ackMaxChars": 300,
|
|
21
|
+
* "activeHours": { "start": "09:00", "end": "22:00" }
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* For users: Edit HEARTBEAT.md in your workspace to customize checks.
|
|
28
|
+
*/
|
|
29
|
+
import * as fs from "node:fs/promises";
|
|
30
|
+
import * as path from "node:path";
|
|
31
|
+
import { loadConfig } from "../../config/loader.js";
|
|
32
|
+
// Constants matching koclaw conventions
|
|
33
|
+
const HEARTBEAT_TOKEN = "HEARTBEAT_OK";
|
|
34
|
+
const DEFAULT_EVERY = "30m";
|
|
35
|
+
const DEFAULT_ACK_MAX_CHARS = 300;
|
|
36
|
+
const HEARTBEAT_FILENAME = "HEARTBEAT.md";
|
|
37
|
+
function getDefaultConfig() {
|
|
38
|
+
return {
|
|
39
|
+
enabled: true,
|
|
40
|
+
every: DEFAULT_EVERY,
|
|
41
|
+
prompt: `Read HEARTBEAT.md if it exists. Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply ${HEARTBEAT_TOKEN}.`,
|
|
42
|
+
ackMaxChars: DEFAULT_ACK_MAX_CHARS,
|
|
43
|
+
activeHours: null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Parse duration string to milliseconds
|
|
47
|
+
function parseDurationMs(duration) {
|
|
48
|
+
const regex = /^(?:(\d+)h)?\s*(?:(\d+)m)?\s?(?:(\d+)s?)?$/i;
|
|
49
|
+
const match = duration.trim().match(regex);
|
|
50
|
+
if (!match) {
|
|
51
|
+
console.warn(`[Heartbeat] Invalid duration "${duration}", using default`);
|
|
52
|
+
return 30 * 60 * 1000;
|
|
53
|
+
}
|
|
54
|
+
const hours = parseInt(match[1] || "0", 10);
|
|
55
|
+
const minutes = parseInt(match[2] || "0", 10);
|
|
56
|
+
const seconds = parseInt(match[3] || "0", 10);
|
|
57
|
+
return ((hours * 60 + minutes) * 60 + seconds) * 1000;
|
|
58
|
+
}
|
|
59
|
+
// Read HEARTBEAT.md from workspace
|
|
60
|
+
async function readHeartbeatFile(workspacePath) {
|
|
61
|
+
try {
|
|
62
|
+
const filePath = path.join(workspacePath, HEARTBEAT_FILENAME);
|
|
63
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
64
|
+
return content;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Check if content is effectively empty
|
|
71
|
+
function isEffectivelyEmpty(content) {
|
|
72
|
+
const lines = content.split("\n");
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
const trimmed = line.trim();
|
|
75
|
+
if (!trimmed)
|
|
76
|
+
continue;
|
|
77
|
+
if (/^#+\s/.test(trimmed))
|
|
78
|
+
continue;
|
|
79
|
+
if (/^[-*+]\s*(\[[\sXx]\]\s*)?$/.test(trimmed))
|
|
80
|
+
continue;
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
// Check if currently within active hours
|
|
86
|
+
function isWithinActiveHours(config) {
|
|
87
|
+
if (!config.activeHours)
|
|
88
|
+
return true;
|
|
89
|
+
const now = new Date();
|
|
90
|
+
const timeStr = now.toLocaleTimeString("en-US", {
|
|
91
|
+
hour12: false,
|
|
92
|
+
hour: "2-digit",
|
|
93
|
+
minute: "2-digit",
|
|
94
|
+
});
|
|
95
|
+
const [currentHour, currentMin] = timeStr.split(":").map(Number);
|
|
96
|
+
const [startHour, startMin] = config.activeHours.start.split(":").map(Number);
|
|
97
|
+
const [endHour, endMin] = config.activeHours.end.split(":").map(Number);
|
|
98
|
+
const currentMinutes = currentHour * 60 + currentMin;
|
|
99
|
+
const startMinutes = startHour * 60 + startMin;
|
|
100
|
+
const endMinutes = endHour * 60 + endMin;
|
|
101
|
+
if (startMinutes === endMinutes)
|
|
102
|
+
return true;
|
|
103
|
+
if (startMinutes > endMinutes) {
|
|
104
|
+
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
|
|
105
|
+
}
|
|
106
|
+
return currentMinutes >= startMinutes && currentMinutes < endMinutes;
|
|
107
|
+
}
|
|
108
|
+
// Find workspace directory
|
|
109
|
+
async function findWorkspaceDir() {
|
|
110
|
+
try {
|
|
111
|
+
await fs.access(HEARTBEAT_FILENAME);
|
|
112
|
+
return process.cwd();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// not found
|
|
116
|
+
}
|
|
117
|
+
let current = process.cwd();
|
|
118
|
+
for (let i = 0; i < 5; i++) {
|
|
119
|
+
const parent = path.dirname(current);
|
|
120
|
+
if (parent === current)
|
|
121
|
+
break;
|
|
122
|
+
current = parent;
|
|
123
|
+
try {
|
|
124
|
+
await fs.access(path.join(current, HEARTBEAT_FILENAME));
|
|
125
|
+
return current;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return process.cwd();
|
|
132
|
+
}
|
|
133
|
+
async function fileExists(filepath) {
|
|
134
|
+
try {
|
|
135
|
+
await fs.access(filepath);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export default function heartbeatExtension(pi) {
|
|
143
|
+
const startTime = Date.now();
|
|
144
|
+
let checkInterval = null;
|
|
145
|
+
let lastHeartbeat = 0;
|
|
146
|
+
// Load config from kobold.json
|
|
147
|
+
let config = getDefaultConfig();
|
|
148
|
+
let configLoaded = false;
|
|
149
|
+
async function loadHeartbeatConfig() {
|
|
150
|
+
try {
|
|
151
|
+
const snapshot = await loadConfig();
|
|
152
|
+
const hbConfig = snapshot.config.agents?.defaults?.heartbeat;
|
|
153
|
+
if (hbConfig) {
|
|
154
|
+
config = {
|
|
155
|
+
enabled: hbConfig.enabled ?? config.enabled,
|
|
156
|
+
every: hbConfig.every ?? config.every,
|
|
157
|
+
prompt: hbConfig.prompt ?? config.prompt,
|
|
158
|
+
ackMaxChars: hbConfig.ackMaxChars ?? config.ackMaxChars,
|
|
159
|
+
activeHours: hbConfig.activeHours ?? config.activeHours,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
configLoaded = true;
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
console.warn('[Heartbeat] Failed to load config, using defaults:', err);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Load config on startup
|
|
169
|
+
loadHeartbeatConfig().then(() => {
|
|
170
|
+
console.log(`[Heartbeat] Config loaded (enabled: ${config.enabled}, every: ${config.every})`);
|
|
171
|
+
});
|
|
172
|
+
// Reload config on environment changes
|
|
173
|
+
pi.on("session_start", async () => {
|
|
174
|
+
await loadHeartbeatConfig();
|
|
175
|
+
console.log(`[Heartbeat] Extension loaded (enabled: ${config.enabled}, every: ${config.every})`);
|
|
176
|
+
});
|
|
177
|
+
// ═══════════════════════════════════════════════════════════════
|
|
178
|
+
// REGISTER HEARTBEAT TOOL
|
|
179
|
+
// ═══════════════════════════════════════════════════════════════
|
|
180
|
+
pi.registerTool({
|
|
181
|
+
name: "heartbeat_check",
|
|
182
|
+
label: "Heartbeat Check",
|
|
183
|
+
description: "Perform a heartbeat check by reading HEARTBEAT.md. Returns the content for review. Reply with HEARTBEAT_OK if nothing needs attention, or describe the issue.",
|
|
184
|
+
// @ts-ignore TSchema mismatch
|
|
185
|
+
parameters: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: {
|
|
188
|
+
force: {
|
|
189
|
+
type: "boolean",
|
|
190
|
+
description: "Force a check even if recently performed",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
195
|
+
const { force } = params;
|
|
196
|
+
const now = Date.now();
|
|
197
|
+
const everyMs = parseDurationMs(config.every);
|
|
198
|
+
// Check active hours
|
|
199
|
+
if (!isWithinActiveHours(config)) {
|
|
200
|
+
return {
|
|
201
|
+
content: [{
|
|
202
|
+
type: "text",
|
|
203
|
+
text: "Heartbeat skipped - outside active hours. Reply HEARTBEAT_OK to acknowledge.",
|
|
204
|
+
}],
|
|
205
|
+
details: { skipped: true, reason: "outside_active_hours" },
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (!force && now - lastHeartbeat < everyMs) {
|
|
209
|
+
return {
|
|
210
|
+
content: [{
|
|
211
|
+
type: "text",
|
|
212
|
+
text: "Heartbeat skipped - too soon since last check. Reply HEARTBEAT_OK to acknowledge.",
|
|
213
|
+
}],
|
|
214
|
+
details: { skipped: true, lastHeartbeat },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const workspaceDir = await findWorkspaceDir();
|
|
218
|
+
const heartbeatContent = await readHeartbeatFile(workspaceDir);
|
|
219
|
+
if (heartbeatContent === null) {
|
|
220
|
+
return {
|
|
221
|
+
content: [{
|
|
222
|
+
type: "text",
|
|
223
|
+
text: `No HEARTBEAT.md found in workspace. Reply ${HEARTBEAT_TOKEN} to acknowledge, or create the file to enable heartbeat checks.`,
|
|
224
|
+
}],
|
|
225
|
+
details: { fileExists: false },
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (isEffectivelyEmpty(heartbeatContent)) {
|
|
229
|
+
return {
|
|
230
|
+
content: [{
|
|
231
|
+
type: "text",
|
|
232
|
+
text: "HEARTBEAT.md is effectively empty (no actionable items). Reply HEARTBEAT_OK to acknowledge.",
|
|
233
|
+
}],
|
|
234
|
+
details: { fileExists: true, isEmpty: true },
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
lastHeartbeat = now;
|
|
238
|
+
return {
|
|
239
|
+
content: [{
|
|
240
|
+
type: "text",
|
|
241
|
+
text: `HEARTBEAT.md content:\n\n${heartbeatContent}\n\nReview the checklist above. If nothing needs attention, reply with "${HEARTBEAT_TOKEN}". Otherwise, describe what needs attention.`,
|
|
242
|
+
}],
|
|
243
|
+
details: { fileExists: true, isEmpty: false },
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
// ═══════════════════════════════════════════════════════════════
|
|
248
|
+
// COMMANDS
|
|
249
|
+
// ═══════════════════════════════════════════════════════════════
|
|
250
|
+
pi.registerCommand("heartbeat", {
|
|
251
|
+
description: "Show heartbeat status or trigger manual check",
|
|
252
|
+
handler: async (args, ctx) => {
|
|
253
|
+
await loadHeartbeatConfig();
|
|
254
|
+
if (args.trim() === "now" || args.trim() === "check") {
|
|
255
|
+
if (!isWithinActiveHours(config)) {
|
|
256
|
+
ctx.ui.notify("❤️ Heartbeat skipped - outside active hours.", "info");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
ctx.ui.notify("Running heartbeat check...", "info");
|
|
260
|
+
const workspaceDir = await findWorkspaceDir();
|
|
261
|
+
const content = await readHeartbeatFile(workspaceDir);
|
|
262
|
+
if (content === null) {
|
|
263
|
+
ctx.ui.notify("❤️ No HEARTBEAT.md found. Run /heartbeat-init to create one.", "warning");
|
|
264
|
+
}
|
|
265
|
+
else if (isEffectivelyEmpty(content)) {
|
|
266
|
+
ctx.ui.notify("❤️ HEARTBEAT.md is empty - no actionable items.", "info");
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
ctx.ui.notify(`❤️ HEARTBEAT.md found:\n${content.substring(0, 200)}...`, "info");
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const lines = [
|
|
274
|
+
"❤️ Heartbeat Configuration",
|
|
275
|
+
`Enabled: ${config.enabled ? "✅" : "❌"}`,
|
|
276
|
+
`Interval: ${config.every}`,
|
|
277
|
+
`Ack Max Chars: ${config.ackMaxChars}`,
|
|
278
|
+
];
|
|
279
|
+
if (config.activeHours) {
|
|
280
|
+
lines.push(`Active Hours: ${config.activeHours.start} - ${config.activeHours.end}`);
|
|
281
|
+
}
|
|
282
|
+
const workspaceDir = await findWorkspaceDir();
|
|
283
|
+
const hbExists = await fileExists(path.join(workspaceDir, HEARTBEAT_FILENAME));
|
|
284
|
+
lines.push(`HEARTBEAT.md: ${hbExists ? "✅ Found" : "⚠️ Not found"}`);
|
|
285
|
+
lines.push("", "Usage:", " /heartbeat now - Run check immediately", " /heartbeat-init - Create HEARTBEAT.md");
|
|
286
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
pi.registerCommand("heartbeat-init", {
|
|
290
|
+
description: "Create HEARTBEAT.md template in workspace",
|
|
291
|
+
handler: async (_args, ctx) => {
|
|
292
|
+
const workspaceDir = await findWorkspaceDir();
|
|
293
|
+
const filePath = path.join(workspaceDir, HEARTBEAT_FILENAME);
|
|
294
|
+
if (await fileExists(filePath)) {
|
|
295
|
+
ctx.ui.notify(`HEARTBEAT.md already exists at ${filePath}`, "warning");
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const template = `# Heartbeat Checklist
|
|
299
|
+
|
|
300
|
+
<!--
|
|
301
|
+
This file controls what the agent checks during periodic heartbeats.
|
|
302
|
+
Keep it short and actionable. If this file is empty or only contains headers,
|
|
303
|
+
heartbeats will be skipped to save tokens.
|
|
304
|
+
|
|
305
|
+
The agent reads this file every heartbeat interval (default: 30m).
|
|
306
|
+
If nothing needs attention, it replies with HEARTBEAT_OK.
|
|
307
|
+
If something needs attention, it describes the issue without the token.
|
|
308
|
+
-->
|
|
309
|
+
|
|
310
|
+
## Regular Checks
|
|
311
|
+
|
|
312
|
+
- [ ] Review any pending tasks in your workspace
|
|
313
|
+
- [ ] Check for blocked items needing human input
|
|
314
|
+
- [ ] Verify system status (if monitoring anything)
|
|
315
|
+
|
|
316
|
+
## Context-Aware
|
|
317
|
+
|
|
318
|
+
Only check these when relevant:
|
|
319
|
+
- [ ] Active sessions that haven't been updated recently
|
|
320
|
+
- [ ] Scheduled tasks or reminders
|
|
321
|
+
- [ ] Follow-ups from previous conversations
|
|
322
|
+
|
|
323
|
+
## Response Protocol
|
|
324
|
+
|
|
325
|
+
If nothing needs attention → reply: \`HEARTBEAT_OK\`
|
|
326
|
+
If something needs attention → brief alert message (no HEARTBEAT_OK token)
|
|
327
|
+
`;
|
|
328
|
+
try {
|
|
329
|
+
await fs.writeFile(filePath, template, "utf-8");
|
|
330
|
+
ctx.ui.notify(`✅ Created ${filePath}`, "info");
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
334
|
+
ctx.ui.notify(`❌ Failed to create file: ${errMsg}`, "error");
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
// ═══════════════════════════════════════════════════════════════
|
|
339
|
+
// AUTO-HEARTBEAT SCHEDULER
|
|
340
|
+
// ═══════════════════════════════════════════════════════════════
|
|
341
|
+
pi.on("session_start", async () => {
|
|
342
|
+
await loadHeartbeatConfig();
|
|
343
|
+
if (!config.enabled) {
|
|
344
|
+
console.log("[Heartbeat] Disabled in config, not starting scheduler");
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const everyMs = parseDurationMs(config.every);
|
|
348
|
+
// Schedule periodic checks
|
|
349
|
+
checkInterval = setInterval(async () => {
|
|
350
|
+
if (!isWithinActiveHours(config)) {
|
|
351
|
+
console.log("[Heartbeat] Skipped - outside active hours");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const workspaceDir = await findWorkspaceDir();
|
|
355
|
+
const content = await readHeartbeatFile(workspaceDir);
|
|
356
|
+
// Skip if no file or empty file
|
|
357
|
+
if (content === null || isEffectivelyEmpty(content)) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
// Use the tool to trigger heartbeat
|
|
361
|
+
pi.sendUserMessage("Perform a heartbeat check using the heartbeat_check tool.", { deliverAs: "followUp" });
|
|
362
|
+
}, everyMs);
|
|
363
|
+
console.log(`[Heartbeat] Scheduler started (every ${config.every})`);
|
|
364
|
+
});
|
|
365
|
+
pi.on("session_shutdown", async () => {
|
|
366
|
+
if (checkInterval) {
|
|
367
|
+
clearInterval(checkInterval);
|
|
368
|
+
console.log("[Heartbeat] Scheduler stopped");
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
console.log("[Heartbeat] Extension loaded");
|
|
372
|
+
}
|
|
373
|
+
//# sourceMappingURL=heartbeat-extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat-extension.js","sourceRoot":"","sources":["../../../../src/extensions/core/heartbeat-extension.ts"],"names":[],"mappings":"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"}
|