sentinelayer-cli 0.8.10 → 0.8.11
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/package.json
CHANGED
|
@@ -180,6 +180,62 @@ export function generateAgentId(modelName) {
|
|
|
180
180
|
return `${prefix}-${suffix}`;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
// In-process registry of agents registered by *this* CLI process. The
|
|
184
|
+
// dashboard treats any participant without a terminal agent_leave /
|
|
185
|
+
// agent_killed / session_killed event as "active". When a CLI exits via
|
|
186
|
+
// SIGINT/SIGTERM/crash without explicitly leaving, the dashboard shows
|
|
187
|
+
// "Last activity: 15h ago — active" indefinitely. This registry lets a
|
|
188
|
+
// single process-wide exit hook flush leave events for every agent it
|
|
189
|
+
// owns so the participant roster stays honest.
|
|
190
|
+
const _localAgents = new Map(); // key: `${sessionId}::${agentId}` -> { sessionId, agentId, targetPath }
|
|
191
|
+
let _exitHooksInstalled = false;
|
|
192
|
+
|
|
193
|
+
function _agentKey(sessionId, agentId) {
|
|
194
|
+
return `${sessionId}::${agentId}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function _trackLocalAgent(sessionId, agentId, targetPath) {
|
|
198
|
+
_localAgents.set(_agentKey(sessionId, agentId), { sessionId, agentId, targetPath });
|
|
199
|
+
_ensureExitHooksInstalled();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function _untrackLocalAgent(sessionId, agentId) {
|
|
203
|
+
_localAgents.delete(_agentKey(sessionId, agentId));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function _emitLeaveForAllLocalAgents(reason) {
|
|
207
|
+
const entries = [..._localAgents.values()];
|
|
208
|
+
_localAgents.clear();
|
|
209
|
+
for (const entry of entries) {
|
|
210
|
+
try {
|
|
211
|
+
await emitAgentEvent(
|
|
212
|
+
entry.sessionId,
|
|
213
|
+
"agent_leave",
|
|
214
|
+
{ agentId: entry.agentId, reason, model: "unknown", role: "participant" },
|
|
215
|
+
{ targetPath: entry.targetPath },
|
|
216
|
+
);
|
|
217
|
+
} catch {
|
|
218
|
+
// Best-effort: a stuck filesystem or network shouldn't block exit.
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function _ensureExitHooksInstalled() {
|
|
224
|
+
if (_exitHooksInstalled) return;
|
|
225
|
+
_exitHooksInstalled = true;
|
|
226
|
+
const onSignal = (signal) => {
|
|
227
|
+
void _emitLeaveForAllLocalAgents("manual").finally(() => {
|
|
228
|
+
process.removeListener(signal, onSignal);
|
|
229
|
+
process.kill(process.pid, signal);
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
process.on("SIGINT", onSignal);
|
|
233
|
+
process.on("SIGTERM", onSignal);
|
|
234
|
+
process.on("beforeExit", () => {
|
|
235
|
+
void _emitLeaveForAllLocalAgents("manual");
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
183
239
|
export async function registerAgent(
|
|
184
240
|
sessionId,
|
|
185
241
|
{ agentId = "", model = "", role = "observer", targetPath = process.cwd() } = {}
|
|
@@ -234,6 +290,7 @@ export async function registerAgent(
|
|
|
234
290
|
role: snapshot.role,
|
|
235
291
|
status: snapshot.status,
|
|
236
292
|
}, { targetPath });
|
|
293
|
+
_trackLocalAgent(paths.sessionId, snapshot.agentId, targetPath);
|
|
237
294
|
|
|
238
295
|
if (renamedFrom) {
|
|
239
296
|
const welcome = buildSentiWelcome({
|
|
@@ -347,6 +404,8 @@ export async function unregisterAgent(
|
|
|
347
404
|
role: snapshot.role,
|
|
348
405
|
model: snapshot.model,
|
|
349
406
|
}, { targetPath });
|
|
407
|
+
// Already left explicitly — don't double-emit on process exit.
|
|
408
|
+
_untrackLocalAgent(paths.sessionId, snapshot.agentId);
|
|
350
409
|
|
|
351
410
|
return {
|
|
352
411
|
...snapshot,
|