diff --git a/src/index.ts b/src/index.ts index 58c185a..0d287f7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,7 +68,7 @@ async function main() { // is up, regardless of when the MCP client sends its first request. const auth = await getAuth() await registerWorker({ userId: auth.userId, tokenId: auth.tokenId }) - const { stop: stopHeartbeat } = startHeartbeat({ tokenId: auth.tokenId }) + const { stop: stopHeartbeat } = startHeartbeat({ userId: auth.userId, tokenId: auth.tokenId }) registerShutdownHandlers({ userId: auth.userId, tokenId: auth.tokenId, stopHeartbeat }) const transport = new StdioServerTransport() diff --git a/src/presence/heartbeat.ts b/src/presence/heartbeat.ts index f4cb230..abe7f74 100644 --- a/src/presence/heartbeat.ts +++ b/src/presence/heartbeat.ts @@ -1,6 +1,8 @@ import { prisma } from '../prisma.js' +import { registerWorker } from './worker.js' export function startHeartbeat(opts: { + userId: string tokenId: string intervalMs?: number }): { stop: () => void } { @@ -11,11 +13,18 @@ export function startHeartbeat(opts: { data: { last_seen_at: new Date() }, }) if (result.count === 0) { - console.error('[scrum4me-mcp] Heartbeat: worker record not found — token may be revoked. Stopping.') - clearInterval(timer) + // Record disappeared — likely deleted by prisma_workers_cleanup, + // a manual cleanup, or a race during shutdown of a parallel worker. + // Re-register so the UI's 'Agent verbonden'-indicator self-heals + // instead of going dark for the rest of the process lifetime. + try { + await registerWorker({ userId: opts.userId, tokenId: opts.tokenId }) + } catch (err) { + console.error('[scrum4me-mcp] Heartbeat: re-register failed', err) + } } } catch { - // non-fatal + // non-fatal — next tick retries } }, opts.intervalMs ?? 5_000)