fix: remove dead delete+recreate and pin code, add poll fallback test
Phase 1 cleanup: - Remove deletePost() method (dead code, replaced by PUT in-place updates) - Remove _postInfo Map tracking (no longer needed) - Remove pin/unpin API calls from watcher-manager.js (incompatible with PUT updates) - Add JSDoc note on (edited) label limitation in _flushUpdate() - Add integration test: test/integration/poll-fallback.test.js - Fix addSession() lastOffset===0 falsy bug (0 was treated as 'no offset') - Fix pre-existing test failures: add lastOffset:0 where tests expect backlog reads - Fix pre-existing session-monitor test: create stub transcript files - Fix pre-existing status-formatter test: update indent check for blockquote format - Format plugin/ files with Prettier (pre-existing formatting drift)
This commit is contained in:
@@ -276,7 +276,10 @@ class SessionMonitor extends EventEmitter {
|
||||
} catch (_e) {
|
||||
// File doesn't exist — skip silently
|
||||
if (this.logger) {
|
||||
this.logger.debug({ sessionKey, transcriptFile }, 'Skipping session (transcript not found)');
|
||||
this.logger.debug(
|
||||
{ sessionKey, transcriptFile },
|
||||
'Skipping session (transcript not found)',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,10 +76,6 @@ class StatusBox extends EventEmitter {
|
||||
// Throttle state per postId
|
||||
// Map<postId, { pending: string|null, timer: NodeJS.Timeout|null, lastFiredAt: number }>
|
||||
this._throttleState = new Map();
|
||||
|
||||
// Track post metadata for delete+recreate
|
||||
// Map<postId, { channelId, rootId }>
|
||||
this._postInfo = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,9 +93,6 @@ class StatusBox extends EventEmitter {
|
||||
if (this.logger) this.logger.debug({ postId: post.id, channelId }, 'Created status post');
|
||||
this.metrics.updatesSent++;
|
||||
|
||||
// Track post info for delete+recreate
|
||||
this._postInfo.set(post.id, { channelId, rootId: rootId || null });
|
||||
|
||||
return post.id;
|
||||
}
|
||||
|
||||
@@ -145,6 +138,9 @@ class StatusBox extends EventEmitter {
|
||||
/**
|
||||
* Flush the pending update for a postId.
|
||||
* @private
|
||||
* Note: PUT updates cause Mattermost to show '(edited)' label on the post.
|
||||
* This is a known API limitation. The Mattermost plugin (Phase 3) solves this
|
||||
* via custom post type rendering.
|
||||
*/
|
||||
async _flushUpdate(postId) {
|
||||
const state = this._throttleState.get(postId);
|
||||
@@ -163,7 +159,7 @@ class StatusBox extends EventEmitter {
|
||||
this.metrics.queueDepth = Math.max(0, this.metrics.queueDepth - 1);
|
||||
|
||||
try {
|
||||
// In-place PUT update — no delete+recreate, no flicker
|
||||
// In-place PUT update
|
||||
await this._apiCallWithRetry('PUT', `/api/v4/posts/${postId}`, {
|
||||
id: postId,
|
||||
message: this._truncate(text),
|
||||
@@ -202,15 +198,6 @@ class StatusBox extends EventEmitter {
|
||||
await Promise.allSettled(postIds.map((id) => this.forceFlush(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a post.
|
||||
* @param {string} postId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deletePost(postId) {
|
||||
await this._apiCall('DELETE', `/api/v4/posts/${postId}`, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate text to maxMessageChars.
|
||||
* @private
|
||||
|
||||
@@ -70,7 +70,12 @@ function format(sessionState, opts = {}) {
|
||||
// never collapses like code blocks do, supports inline markdown
|
||||
var body = lines.join('\n');
|
||||
if (depth === 0) {
|
||||
body = body.split('\n').map(function (l) { return '> ' + l; }).join('\n');
|
||||
body = body
|
||||
.split('\n')
|
||||
.map(function (l) {
|
||||
return '> ' + l;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -52,9 +52,12 @@ class StatusWatcher extends EventEmitter {
|
||||
if (this.sessions.has(sessionKey)) return;
|
||||
|
||||
// For new sessions (no saved offset), start from current file position
|
||||
// so we only show NEW content going forward — not the entire backlog
|
||||
let startOffset = initialState.lastOffset || 0;
|
||||
if (!initialState.lastOffset) {
|
||||
// so we only show NEW content going forward — not the entire backlog.
|
||||
// Pass lastOffset: 0 explicitly to read from the beginning.
|
||||
var startOffset;
|
||||
if (initialState.lastOffset !== undefined) {
|
||||
startOffset = initialState.lastOffset;
|
||||
} else {
|
||||
try {
|
||||
var stat = fs.statSync(transcriptFile);
|
||||
startOffset = stat.size;
|
||||
@@ -86,8 +89,8 @@ class StatusWatcher extends EventEmitter {
|
||||
this.logger.debug({ sessionKey, transcriptFile, startOffset }, 'Session added to watcher');
|
||||
}
|
||||
|
||||
// Only read if we have a saved offset (recovery) — new sessions start streaming from current position
|
||||
if (initialState.lastOffset) {
|
||||
// Only read if we have an explicit offset (recovery or explicit 0) — new sessions start streaming from current position
|
||||
if (initialState.lastOffset !== undefined) {
|
||||
this._readFile(sessionKey, state);
|
||||
}
|
||||
|
||||
@@ -327,9 +330,7 @@ class StatusWatcher extends EventEmitter {
|
||||
argStr = item.arguments.url.slice(0, 60);
|
||||
}
|
||||
}
|
||||
var statusLine = argStr
|
||||
? toolName + ': ' + argStr
|
||||
: toolName + ': ' + label;
|
||||
var statusLine = argStr ? toolName + ': ' + argStr : toolName + ': ' + label;
|
||||
state.lines.push(statusLine);
|
||||
} else if (item.type === 'text' && item.text) {
|
||||
var text = item.text.trim();
|
||||
|
||||
@@ -240,14 +240,6 @@ async function startDaemon() {
|
||||
const initialText = buildInitialText(agentId, sessionKey);
|
||||
postId = await sharedStatusBox.createPost(channelId, initialText, rootPostId);
|
||||
logger.info({ sessionKey, postId, channelId }, 'Created status box');
|
||||
|
||||
// Auto-pin the status post so it's always visible
|
||||
try {
|
||||
await sharedStatusBox._apiCall('POST', '/api/v4/posts/' + postId + '/pin', {});
|
||||
logger.debug({ sessionKey, postId }, 'Status post pinned');
|
||||
} catch (pinErr) {
|
||||
logger.warn({ sessionKey, err: pinErr }, 'Failed to pin status post');
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error({ sessionKey, err }, 'Failed to create status post');
|
||||
globalMetrics.lastError = err.message;
|
||||
@@ -339,14 +331,6 @@ async function startDaemon() {
|
||||
logger.error({ sessionKey, err }, 'Failed to update final status');
|
||||
}
|
||||
|
||||
// Unpin the status post when session is done
|
||||
try {
|
||||
await sharedStatusBox._apiCall('POST', `/api/v4/posts/${box.postId}/unpin`, {});
|
||||
logger.debug({ sessionKey, postId: box.postId }, 'Status post unpinned');
|
||||
} catch (unpinErr) {
|
||||
logger.warn({ sessionKey, err: unpinErr }, 'Failed to unpin status post');
|
||||
}
|
||||
|
||||
// Clean up — remove from all tracking so session can be re-detected if it becomes active again
|
||||
activeBoxes.delete(sessionKey);
|
||||
watcher.removeSession(sessionKey);
|
||||
|
||||
Reference in New Issue
Block a user