fix: production deployment issues

1. session-monitor: handle timestamp-prefixed transcript filenames
   OpenClaw uses {ISO}_{sessionId}.jsonl — glob for *_{sessionId}.jsonl
   when direct path doesn't exist.

2. session-monitor: skip stale sessions (>5min since last transcript write)
   Prevents creating status boxes for every old session in sessions.json.

3. status-watcher: parse actual OpenClaw JSONL transcript format
   Records are {type:'message', message:{role,content:[{type,name,...}]}}
   not {type:'tool_call', name}. Now shows live tool calls with arguments
   and assistant thinking text.

4. handler.js: fix module.exports for OpenClaw hook loader
   Expects default export (function), not {handle: function}.

5. HOOK.md: add YAML frontmatter metadata for hook discovery.
This commit is contained in:
sol
2026-03-07 18:31:43 +00:00
parent 387998812c
commit 7c6c8a4432
4 changed files with 142 additions and 33 deletions

View File

@@ -113,7 +113,30 @@ class SessionMonitor extends EventEmitter {
* @returns {string}
*/
_transcriptPath(agentId, sessionId) {
return path.join(this.transcriptDir, agentId, 'sessions', `${sessionId}.jsonl`);
const sessionsDir = path.join(this.transcriptDir, agentId, 'sessions');
const directPath = path.join(sessionsDir, `${sessionId}.jsonl`);
// OpenClaw may use timestamp-prefixed filenames: {ISO}_{sessionId}.jsonl
// Check direct path first, then glob for *_{sessionId}.jsonl
if (fs.existsSync(directPath)) {
return directPath;
}
try {
const files = fs.readdirSync(sessionsDir);
const suffix = `${sessionId}.jsonl`;
const match = files.find(
(f) => f.endsWith(suffix) && f !== suffix && !f.endsWith('.deleted'),
);
if (match) {
return path.join(sessionsDir, match);
}
} catch (_e) {
// Directory doesn't exist or unreadable
}
// Fallback to direct path (will fail with ENOENT, which is handled upstream)
return directPath;
}
/**
@@ -226,6 +249,29 @@ class SessionMonitor extends EventEmitter {
const transcriptFile = this._transcriptPath(agentId, sessionId);
// Skip stale sessions — only track if transcript was modified in last 5 minutes
// This prevents creating status boxes for every old session in sessions.json
try {
const stat = fs.statSync(transcriptFile);
const ageMs = Date.now() - stat.mtimeMs;
const STALE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
if (ageMs > STALE_THRESHOLD_MS) {
if (this.logger) {
this.logger.debug(
{ sessionKey, ageS: Math.floor(ageMs / 1000) },
'Skipping stale session (transcript not recently modified)',
);
}
return;
}
} catch (_e) {
// File doesn't exist — skip silently
if (this.logger) {
this.logger.debug({ sessionKey, transcriptFile }, 'Skipping session (transcript not found)');
}
return;
}
// Sub-agents always pass through — they inherit parent channel via watcher-manager
const isSubAgent = !!spawnedBy;