@cfio/cohort-sync 0.1.7 → 0.2.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/dist/index.js +545 -104
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11565,6 +11565,7 @@ var getUndeliveredForPlugin = makeFunctionReference("notifications:getUndelivere
|
|
|
11565
11565
|
var markDeliveredByPlugin = makeFunctionReference("notifications:markDeliveredByPlugin");
|
|
11566
11566
|
var upsertTelemetryFromPlugin = makeFunctionReference("telemetryPlugin:upsertTelemetryFromPlugin");
|
|
11567
11567
|
var upsertSessionsFromPlugin = makeFunctionReference("telemetryPlugin:upsertSessionsFromPlugin");
|
|
11568
|
+
var pushActivityFromPluginRef = makeFunctionReference("activityFeed:pushActivityFromPlugin");
|
|
11568
11569
|
var HOT_KEY = "__cohort_sync__";
|
|
11569
11570
|
function getHotState() {
|
|
11570
11571
|
return globalThis[HOT_KEY] ?? null;
|
|
@@ -11728,6 +11729,38 @@ async function pushSessions(apiKey, agentName, sessions) {
|
|
|
11728
11729
|
getLogger().error(`cohort-sync: pushSessions failed: ${err}`);
|
|
11729
11730
|
}
|
|
11730
11731
|
}
|
|
11732
|
+
async function pushActivity(apiKey, entries) {
|
|
11733
|
+
if (entries.length === 0) return;
|
|
11734
|
+
const c = getOrCreateClient();
|
|
11735
|
+
if (!c) return;
|
|
11736
|
+
try {
|
|
11737
|
+
await c.mutation(pushActivityFromPluginRef, { apiKey, entries });
|
|
11738
|
+
} catch (err) {
|
|
11739
|
+
getLogger().error(`cohort-sync: pushActivity failed: ${err}`);
|
|
11740
|
+
}
|
|
11741
|
+
}
|
|
11742
|
+
function saveAgentStatesToHot(states) {
|
|
11743
|
+
const hot = getHotState();
|
|
11744
|
+
if (hot) {
|
|
11745
|
+
hot.agentStates = states;
|
|
11746
|
+
}
|
|
11747
|
+
}
|
|
11748
|
+
function getAgentStatesFromHot() {
|
|
11749
|
+
return getHotState()?.agentStates ?? null;
|
|
11750
|
+
}
|
|
11751
|
+
function saveIntervalsToHot(heartbeat, activityFlush) {
|
|
11752
|
+
const hot = getHotState();
|
|
11753
|
+
if (hot) {
|
|
11754
|
+
hot.intervals = { heartbeat, activityFlush };
|
|
11755
|
+
}
|
|
11756
|
+
}
|
|
11757
|
+
function clearIntervalsFromHot() {
|
|
11758
|
+
const hot = getHotState();
|
|
11759
|
+
if (!hot?.intervals) return;
|
|
11760
|
+
if (hot.intervals.heartbeat) clearInterval(hot.intervals.heartbeat);
|
|
11761
|
+
if (hot.intervals.activityFlush) clearInterval(hot.intervals.activityFlush);
|
|
11762
|
+
hot.intervals = { heartbeat: null, activityFlush: null };
|
|
11763
|
+
}
|
|
11731
11764
|
|
|
11732
11765
|
// src/sync.ts
|
|
11733
11766
|
function extractJson(raw) {
|
|
@@ -11957,78 +11990,331 @@ async function fullSync(agentName, model, cfg, logger, openClawAgents) {
|
|
|
11957
11990
|
logger.info("cohort-sync: full sync complete");
|
|
11958
11991
|
}
|
|
11959
11992
|
|
|
11960
|
-
// src/
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11993
|
+
// src/session-key.ts
|
|
11994
|
+
function parseSessionKey(key) {
|
|
11995
|
+
if (!key || !key.includes(":")) {
|
|
11996
|
+
return { kind: "cli", channel: "cli" };
|
|
11997
|
+
}
|
|
11998
|
+
const parts = key.split(":");
|
|
11999
|
+
if (parts[0] === "hook" && parts[1] === "cohort") {
|
|
12000
|
+
return { kind: "hook", channel: "cohort", identifier: parts.slice(2).join(":") };
|
|
12001
|
+
}
|
|
12002
|
+
if (parts[0] === "agent" && parts.length >= 3) {
|
|
12003
|
+
const rest = parts.slice(2);
|
|
12004
|
+
if (rest[0] === "main") {
|
|
12005
|
+
return { kind: "direct", channel: "signal" };
|
|
12006
|
+
}
|
|
12007
|
+
if (rest[0] === "signal") {
|
|
12008
|
+
if (rest[1] === "group") {
|
|
12009
|
+
return { kind: "group", channel: "signal", identifier: rest.slice(2).join(":") };
|
|
12010
|
+
}
|
|
12011
|
+
if (rest.length >= 3 && rest.some((p) => p === "dm")) {
|
|
12012
|
+
const dmIndex = rest.indexOf("dm");
|
|
12013
|
+
return { kind: "direct", channel: "signal", identifier: rest.slice(dmIndex + 1).join(":") };
|
|
12014
|
+
}
|
|
12015
|
+
return { kind: "direct", channel: "signal" };
|
|
12016
|
+
}
|
|
12017
|
+
if (rest[0] === "slack" && rest[1] === "channel") {
|
|
12018
|
+
return { kind: "group", channel: "slack", identifier: rest[2] };
|
|
12019
|
+
}
|
|
12020
|
+
}
|
|
12021
|
+
return { kind: "cli", channel: "cli" };
|
|
12022
|
+
}
|
|
12023
|
+
|
|
12024
|
+
// src/agent-state.ts
|
|
12025
|
+
var DELTA_THRESHOLD = 0.1;
|
|
12026
|
+
function shouldPushTelemetry(current, lastPushed) {
|
|
12027
|
+
if (!lastPushed) return true;
|
|
12028
|
+
if (current.status !== lastPushed.status) return true;
|
|
12029
|
+
if (current.compactions !== lastPushed.compactions) return true;
|
|
12030
|
+
if (current.activeSessions !== lastPushed.activeSessions) return true;
|
|
12031
|
+
if (current.contextLimit > 0 && lastPushed.contextLimit > 0) {
|
|
12032
|
+
const currentPct = current.contextTokens / current.contextLimit;
|
|
12033
|
+
const lastPct = lastPushed.contextTokens / lastPushed.contextLimit;
|
|
12034
|
+
if (Math.abs(currentPct - lastPct) >= DELTA_THRESHOLD) return true;
|
|
12035
|
+
} else if (current.contextLimit > 0 && lastPushed.contextLimit === 0) {
|
|
12036
|
+
return true;
|
|
12037
|
+
}
|
|
12038
|
+
const maxIn = Math.max(lastPushed.tokensIn, 1);
|
|
12039
|
+
if (Math.abs(current.tokensIn - lastPushed.tokensIn) / maxIn >= DELTA_THRESHOLD) return true;
|
|
12040
|
+
const maxOut = Math.max(lastPushed.tokensOut, 1);
|
|
12041
|
+
if (Math.abs(current.tokensOut - lastPushed.tokensOut) / maxOut >= DELTA_THRESHOLD) return true;
|
|
12042
|
+
return false;
|
|
12043
|
+
}
|
|
12044
|
+
function shouldPushSessions(current, lastPushed) {
|
|
12045
|
+
if (!lastPushed) return true;
|
|
12046
|
+
if (current.length !== lastPushed.length) return true;
|
|
12047
|
+
for (const sess of current) {
|
|
12048
|
+
const prev = lastPushed.find((s) => s.key === sess.key);
|
|
12049
|
+
if (!prev) return true;
|
|
12050
|
+
if (sess.model && sess.model !== "unknown" && prev.model !== sess.model) return true;
|
|
12051
|
+
if (sess.contextLimit && sess.contextLimit > 0 && prev.contextLimit && prev.contextLimit > 0) {
|
|
12052
|
+
const curPct = (sess.contextTokens ?? 0) / sess.contextLimit;
|
|
12053
|
+
const prevPct = (prev.contextTokens ?? 0) / prev.contextLimit;
|
|
12054
|
+
if (Math.abs(curPct - prevPct) >= DELTA_THRESHOLD) return true;
|
|
12055
|
+
}
|
|
12056
|
+
}
|
|
12057
|
+
return false;
|
|
12058
|
+
}
|
|
12059
|
+
function basename(filePath) {
|
|
12060
|
+
const parts = filePath.split("/");
|
|
12061
|
+
return parts[parts.length - 1] ?? filePath;
|
|
12062
|
+
}
|
|
12063
|
+
function buildActivitySentence(agentName, event, context) {
|
|
12064
|
+
const ts = Date.now();
|
|
12065
|
+
const name = agentName.charAt(0).toUpperCase() + agentName.slice(1);
|
|
12066
|
+
switch (event) {
|
|
12067
|
+
case "before_tool_call": {
|
|
12068
|
+
const toolName = String(context.toolName ?? "a tool");
|
|
12069
|
+
const params = context.params;
|
|
12070
|
+
if (toolName === "Edit" || toolName === "Write") {
|
|
12071
|
+
const file = params?.file_path ? basename(String(params.file_path)) : "a file";
|
|
12072
|
+
return { agentName, text: `${name} is editing ${file}`, category: "tool", timestamp: ts };
|
|
12073
|
+
}
|
|
12074
|
+
if (toolName === "Read") {
|
|
12075
|
+
const file = params?.file_path ? basename(String(params.file_path)) : "a file";
|
|
12076
|
+
return { agentName, text: `${name} is reading ${file}`, category: "tool", timestamp: ts };
|
|
12077
|
+
}
|
|
12078
|
+
if (toolName === "Bash") {
|
|
12079
|
+
return { agentName, text: `${name} is running a command`, category: "tool", timestamp: ts };
|
|
12080
|
+
}
|
|
12081
|
+
if (toolName === "cohort_comment") {
|
|
12082
|
+
const taskNum = params?.task_number ?? "?";
|
|
12083
|
+
return { agentName, text: `${name} is commenting on task #${taskNum}`, category: "tool", timestamp: ts };
|
|
12084
|
+
}
|
|
12085
|
+
return { agentName, text: `${name} used ${toolName}`, category: "tool", timestamp: ts };
|
|
12086
|
+
}
|
|
12087
|
+
case "message_received": {
|
|
12088
|
+
const ch = String(context.channel ?? context.messageProvider ?? "unknown");
|
|
12089
|
+
return { agentName, text: `${name} received a message on ${ch}`, category: "message", timestamp: ts };
|
|
12090
|
+
}
|
|
12091
|
+
case "message_sent": {
|
|
12092
|
+
const ch = String(context.channel ?? context.messageProvider ?? "unknown");
|
|
12093
|
+
return { agentName, text: `${name} sent a reply on ${ch}`, category: "message", timestamp: ts };
|
|
12094
|
+
}
|
|
12095
|
+
case "after_compaction": {
|
|
12096
|
+
const before = context.beforePercent != null ? `${context.beforePercent}%` : "?";
|
|
12097
|
+
const after = context.afterPercent != null ? `${context.afterPercent}%` : "?";
|
|
12098
|
+
return { agentName, text: `${name} context compacted \u2014 ${before} \u2192 ${after}`, category: "system", timestamp: ts };
|
|
12099
|
+
}
|
|
12100
|
+
case "session_start": {
|
|
12101
|
+
const ch = String(context.channel ?? "unknown");
|
|
12102
|
+
return { agentName, text: `${name} started a session on ${ch}`, category: "session", timestamp: ts };
|
|
12103
|
+
}
|
|
12104
|
+
case "session_end": {
|
|
12105
|
+
return { agentName, text: `${name} ended a session`, category: "session", timestamp: ts };
|
|
12106
|
+
}
|
|
12107
|
+
case "before_agent_start": {
|
|
12108
|
+
const ch = String(context.channel ?? "unknown");
|
|
12109
|
+
return { agentName, text: `${name} is working on ${ch}`, category: "session", timestamp: ts };
|
|
12110
|
+
}
|
|
12111
|
+
default:
|
|
12112
|
+
return null;
|
|
12113
|
+
}
|
|
12114
|
+
}
|
|
12115
|
+
var AgentStateTracker = class {
|
|
12116
|
+
agents = /* @__PURE__ */ new Map();
|
|
12117
|
+
activityBuffer = [];
|
|
12118
|
+
sessionKeyToAgent = /* @__PURE__ */ new Map();
|
|
12119
|
+
setSessionAgent(sessionKey, agentName) {
|
|
12120
|
+
this.sessionKeyToAgent.set(sessionKey, agentName);
|
|
12121
|
+
}
|
|
12122
|
+
getSessionAgent(sessionKey) {
|
|
12123
|
+
return this.sessionKeyToAgent.get(sessionKey) ?? null;
|
|
12124
|
+
}
|
|
12125
|
+
getOrCreate(agentName) {
|
|
12126
|
+
let state = this.agents.get(agentName);
|
|
12127
|
+
if (!state) {
|
|
12128
|
+
state = {
|
|
12129
|
+
agentName,
|
|
12130
|
+
status: "idle",
|
|
12131
|
+
model: "unknown",
|
|
12132
|
+
tokensIn: 0,
|
|
12133
|
+
tokensOut: 0,
|
|
12134
|
+
contextTokens: 0,
|
|
12135
|
+
contextLimit: 0,
|
|
12136
|
+
compactions: 0,
|
|
12137
|
+
sessions: /* @__PURE__ */ new Map(),
|
|
12138
|
+
lastPushedTelemetry: null,
|
|
12139
|
+
lastPushedSessions: null
|
|
12140
|
+
};
|
|
12141
|
+
this.agents.set(agentName, state);
|
|
12142
|
+
}
|
|
12143
|
+
return state;
|
|
12144
|
+
}
|
|
12145
|
+
// --- Session lifecycle ---
|
|
12146
|
+
addSession(agentName, sessionKey) {
|
|
12147
|
+
const state = this.getOrCreate(agentName);
|
|
12148
|
+
const parsed = parseSessionKey(sessionKey);
|
|
12149
|
+
state.sessions.set(sessionKey, {
|
|
12150
|
+
key: sessionKey,
|
|
12151
|
+
kind: parsed.kind,
|
|
12152
|
+
channel: parsed.channel,
|
|
11968
12153
|
model: "unknown",
|
|
11969
12154
|
contextTokens: 0,
|
|
11970
12155
|
contextLimit: 0,
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
12156
|
+
lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12157
|
+
startedAt: Date.now()
|
|
12158
|
+
});
|
|
12159
|
+
}
|
|
12160
|
+
removeSession(agentName, sessionKey) {
|
|
12161
|
+
const state = this.agents.get(agentName);
|
|
12162
|
+
if (state) {
|
|
12163
|
+
state.sessions.delete(sessionKey);
|
|
12164
|
+
}
|
|
12165
|
+
}
|
|
12166
|
+
hasSession(agentName, sessionKey) {
|
|
12167
|
+
const state = this.agents.get(agentName);
|
|
12168
|
+
if (!state) return false;
|
|
12169
|
+
return state.sessions.has(sessionKey);
|
|
12170
|
+
}
|
|
12171
|
+
pruneStaleSessions(agentName, maxAgeMs) {
|
|
12172
|
+
const state = this.agents.get(agentName);
|
|
12173
|
+
if (!state) return [];
|
|
12174
|
+
const now = Date.now();
|
|
12175
|
+
const pruned = [];
|
|
12176
|
+
for (const [key, session] of state.sessions) {
|
|
12177
|
+
const lastMs = new Date(session.lastActivity).getTime();
|
|
12178
|
+
if (now - lastMs > maxAgeMs) {
|
|
12179
|
+
state.sessions.delete(key);
|
|
12180
|
+
pruned.push(key);
|
|
12181
|
+
}
|
|
12182
|
+
}
|
|
12183
|
+
return pruned;
|
|
12184
|
+
}
|
|
12185
|
+
// --- Telemetry updates ---
|
|
12186
|
+
updateStatus(agentName, status) {
|
|
12187
|
+
this.getOrCreate(agentName).status = status;
|
|
12188
|
+
}
|
|
12189
|
+
updateFromLlmOutput(agentName, sessionKey, data) {
|
|
12190
|
+
const state = this.getOrCreate(agentName);
|
|
12191
|
+
if (data.model) state.model = data.model;
|
|
12192
|
+
if (data.tokensIn !== void 0) state.tokensIn += data.tokensIn;
|
|
12193
|
+
if (data.tokensOut !== void 0) state.tokensOut += data.tokensOut;
|
|
12194
|
+
if (data.contextTokens !== void 0) state.contextTokens = data.contextTokens;
|
|
12195
|
+
if (data.contextLimit !== void 0) state.contextLimit = data.contextLimit;
|
|
12196
|
+
state.status = "working";
|
|
12197
|
+
if (sessionKey) {
|
|
12198
|
+
const session = state.sessions.get(sessionKey);
|
|
12199
|
+
if (session) {
|
|
12200
|
+
if (data.model) session.model = data.model;
|
|
12201
|
+
if (data.contextTokens !== void 0) session.contextTokens = data.contextTokens;
|
|
12202
|
+
if (data.contextLimit !== void 0) session.contextLimit = data.contextLimit;
|
|
12203
|
+
session.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
12204
|
+
}
|
|
12205
|
+
}
|
|
12206
|
+
}
|
|
12207
|
+
updateFromCompaction(agentName, data) {
|
|
12208
|
+
const state = this.getOrCreate(agentName);
|
|
12209
|
+
state.compactions += 1;
|
|
12210
|
+
if (data.contextTokens !== void 0) state.contextTokens = data.contextTokens;
|
|
12211
|
+
if (data.contextLimit !== void 0) state.contextLimit = data.contextLimit;
|
|
12212
|
+
}
|
|
12213
|
+
setModel(agentName, model) {
|
|
12214
|
+
this.getOrCreate(agentName).model = model;
|
|
12215
|
+
}
|
|
12216
|
+
// --- Snapshot extraction ---
|
|
12217
|
+
getTelemetrySnapshot(agentName) {
|
|
12218
|
+
const state = this.agents.get(agentName);
|
|
12219
|
+
if (!state) return null;
|
|
12220
|
+
return {
|
|
12221
|
+
agentName: state.agentName,
|
|
12222
|
+
status: state.status,
|
|
12223
|
+
model: state.model,
|
|
12224
|
+
contextTokens: state.contextTokens,
|
|
12225
|
+
contextLimit: state.contextLimit,
|
|
12226
|
+
tokensIn: state.tokensIn,
|
|
12227
|
+
tokensOut: state.tokensOut,
|
|
12228
|
+
compactions: state.compactions,
|
|
12229
|
+
activeSessions: state.sessions.size
|
|
11975
12230
|
};
|
|
11976
|
-
if (delta.model) current.model = delta.model;
|
|
11977
|
-
if (delta.status) current.status = delta.status;
|
|
11978
|
-
if (delta.tokensIn !== void 0) current.tokensIn += delta.tokensIn;
|
|
11979
|
-
if (delta.tokensOut !== void 0) current.tokensOut += delta.tokensOut;
|
|
11980
|
-
if (delta.contextTokens !== void 0) current.contextTokens = delta.contextTokens;
|
|
11981
|
-
if (delta.contextLimit !== void 0) current.contextLimit = delta.contextLimit;
|
|
11982
|
-
if (delta.activeSessions !== void 0) current.activeSessions = delta.activeSessions;
|
|
11983
|
-
if (delta.compacted) {
|
|
11984
|
-
current.compactions += 1;
|
|
11985
|
-
}
|
|
11986
|
-
this.state.set(agentName, current);
|
|
11987
|
-
return { ...current };
|
|
11988
|
-
}
|
|
11989
|
-
/** Get current state for an agent. */
|
|
11990
|
-
get(agentName) {
|
|
11991
|
-
const s = this.state.get(agentName);
|
|
11992
|
-
return s ? { ...s } : void 0;
|
|
11993
|
-
}
|
|
11994
|
-
/** Return the last known snapshot for an agent (null if unknown). */
|
|
11995
|
-
getSnapshot(agentName) {
|
|
11996
|
-
return this.state.has(agentName) ? { ...this.state.get(agentName) } : null;
|
|
11997
|
-
}
|
|
11998
|
-
/** Clear all tracked state (e.g., on gateway_stop). */
|
|
11999
|
-
clear() {
|
|
12000
|
-
this.state.clear();
|
|
12001
12231
|
}
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12232
|
+
getSessionsSnapshot(agentName) {
|
|
12233
|
+
const state = this.agents.get(agentName);
|
|
12234
|
+
if (!state) return [];
|
|
12235
|
+
return Array.from(state.sessions.values()).map((s) => ({
|
|
12236
|
+
key: s.key,
|
|
12237
|
+
kind: s.kind,
|
|
12238
|
+
channel: s.channel,
|
|
12239
|
+
model: s.model !== "unknown" ? s.model : void 0,
|
|
12240
|
+
contextTokens: s.contextTokens > 0 ? s.contextTokens : void 0,
|
|
12241
|
+
contextLimit: s.contextLimit > 0 ? s.contextLimit : void 0,
|
|
12242
|
+
lastActivity: s.lastActivity
|
|
12243
|
+
}));
|
|
12244
|
+
}
|
|
12245
|
+
// --- Delta detection ---
|
|
12246
|
+
shouldPushTelemetry(agentName) {
|
|
12247
|
+
const snapshot = this.getTelemetrySnapshot(agentName);
|
|
12248
|
+
if (!snapshot) return false;
|
|
12249
|
+
const state = this.agents.get(agentName);
|
|
12250
|
+
return shouldPushTelemetry(snapshot, state.lastPushedTelemetry);
|
|
12011
12251
|
}
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
this.
|
|
12015
|
-
|
|
12252
|
+
shouldPushSessions(agentName) {
|
|
12253
|
+
const snapshot = this.getSessionsSnapshot(agentName);
|
|
12254
|
+
const state = this.agents.get(agentName);
|
|
12255
|
+
if (!state) return false;
|
|
12256
|
+
return shouldPushSessions(snapshot, state.lastPushedSessions);
|
|
12016
12257
|
}
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
|
|
12258
|
+
markTelemetryPushed(agentName) {
|
|
12259
|
+
const state = this.agents.get(agentName);
|
|
12260
|
+
if (state) {
|
|
12261
|
+
state.lastPushedTelemetry = this.getTelemetrySnapshot(agentName);
|
|
12262
|
+
}
|
|
12020
12263
|
}
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12264
|
+
markSessionsPushed(agentName) {
|
|
12265
|
+
const state = this.agents.get(agentName);
|
|
12266
|
+
if (state) {
|
|
12267
|
+
state.lastPushedSessions = this.getSessionsSnapshot(agentName);
|
|
12268
|
+
}
|
|
12269
|
+
}
|
|
12270
|
+
// --- Activity buffer ---
|
|
12271
|
+
addActivity(entry) {
|
|
12272
|
+
this.activityBuffer.push(entry);
|
|
12273
|
+
}
|
|
12274
|
+
flushActivity() {
|
|
12275
|
+
const entries = [...this.activityBuffer];
|
|
12276
|
+
this.activityBuffer = [];
|
|
12277
|
+
return entries;
|
|
12278
|
+
}
|
|
12279
|
+
// --- Lifecycle ---
|
|
12280
|
+
getAgentNames() {
|
|
12281
|
+
return Array.from(this.agents.keys());
|
|
12024
12282
|
}
|
|
12025
|
-
/** Clear all sessions (e.g., on gateway_stop). */
|
|
12026
12283
|
clear() {
|
|
12027
|
-
this.
|
|
12284
|
+
this.agents.clear();
|
|
12285
|
+
this.activityBuffer = [];
|
|
12286
|
+
this.sessionKeyToAgent.clear();
|
|
12287
|
+
}
|
|
12288
|
+
exportState() {
|
|
12289
|
+
return {
|
|
12290
|
+
agents: new Map(this.agents),
|
|
12291
|
+
sessionKeyToAgent: new Map(this.sessionKeyToAgent)
|
|
12292
|
+
};
|
|
12293
|
+
}
|
|
12294
|
+
importState(states) {
|
|
12295
|
+
const agentMap = states instanceof Map ? states : states.agents;
|
|
12296
|
+
for (const [name, state] of agentMap) {
|
|
12297
|
+
if (!(state.sessions instanceof Map)) {
|
|
12298
|
+
state.sessions = new Map(Object.entries(state.sessions));
|
|
12299
|
+
}
|
|
12300
|
+
this.agents.set(name, state);
|
|
12301
|
+
}
|
|
12302
|
+
if (!(states instanceof Map) && states.sessionKeyToAgent) {
|
|
12303
|
+
for (const [key, agent] of states.sessionKeyToAgent) {
|
|
12304
|
+
this.sessionKeyToAgent.set(key, agent);
|
|
12305
|
+
}
|
|
12306
|
+
}
|
|
12028
12307
|
}
|
|
12029
12308
|
};
|
|
12030
12309
|
|
|
12031
12310
|
// src/hooks.ts
|
|
12311
|
+
var PLUGIN_VERSION = "unknown";
|
|
12312
|
+
try {
|
|
12313
|
+
const pkgPath = path.join(path.dirname(new URL(import.meta.url).pathname), "package.json");
|
|
12314
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
12315
|
+
PLUGIN_VERSION = pkgJson.version ?? "unknown";
|
|
12316
|
+
} catch {
|
|
12317
|
+
}
|
|
12032
12318
|
function parseIdentityFile(workspaceDir) {
|
|
12033
12319
|
try {
|
|
12034
12320
|
const filePath = path.join(workspaceDir, "IDENTITY.md");
|
|
@@ -12064,16 +12350,45 @@ function resolveIdentity(configIdentity, workspaceDir) {
|
|
|
12064
12350
|
function registerHooks(api, cfg) {
|
|
12065
12351
|
const { logger, config } = api;
|
|
12066
12352
|
const nameMap = cfg.agentNameMap;
|
|
12067
|
-
const
|
|
12068
|
-
const sessions = new SessionTracker();
|
|
12353
|
+
const tracker = new AgentStateTracker();
|
|
12069
12354
|
let heartbeatInterval = null;
|
|
12355
|
+
let activityFlushInterval = null;
|
|
12070
12356
|
setConvexUrl(cfg);
|
|
12071
12357
|
setLogger(logger);
|
|
12072
12358
|
restoreFromHotReload(logger);
|
|
12073
12359
|
restoreRosterFromHotReload(getRosterHotState(), logger);
|
|
12360
|
+
const savedStates = getAgentStatesFromHot();
|
|
12361
|
+
if (savedStates) {
|
|
12362
|
+
tracker.importState(savedStates);
|
|
12363
|
+
logger.info("cohort-sync: recovered AgentStateTracker state after hot-reload");
|
|
12364
|
+
}
|
|
12074
12365
|
function resolveAgentName(agentId) {
|
|
12075
12366
|
return (nameMap?.[agentId] ?? agentId).toLowerCase();
|
|
12076
12367
|
}
|
|
12368
|
+
function resolveAgentFromContext(ctx) {
|
|
12369
|
+
if (ctx.agentId && typeof ctx.agentId === "string") {
|
|
12370
|
+
return resolveAgentName(ctx.agentId);
|
|
12371
|
+
}
|
|
12372
|
+
const sessionKey = ctx.sessionKey ?? ctx.sessionId;
|
|
12373
|
+
if (sessionKey && typeof sessionKey === "string") {
|
|
12374
|
+
const mapped = tracker.getSessionAgent(sessionKey);
|
|
12375
|
+
if (mapped) return mapped;
|
|
12376
|
+
}
|
|
12377
|
+
return resolveAgentName("main");
|
|
12378
|
+
}
|
|
12379
|
+
clearIntervalsFromHot();
|
|
12380
|
+
heartbeatInterval = setInterval(() => {
|
|
12381
|
+
pushHeartbeat().catch((err) => {
|
|
12382
|
+
logger.warn(`cohort-sync: heartbeat tick failed: ${String(err)}`);
|
|
12383
|
+
});
|
|
12384
|
+
}, 12e4);
|
|
12385
|
+
activityFlushInterval = setInterval(() => {
|
|
12386
|
+
flushActivityBuffer().catch((err) => {
|
|
12387
|
+
logger.warn(`cohort-sync: activity flush tick failed: ${String(err)}`);
|
|
12388
|
+
});
|
|
12389
|
+
}, 1500);
|
|
12390
|
+
saveIntervalsToHot(heartbeatInterval, activityFlushInterval);
|
|
12391
|
+
logger.info("cohort-sync: intervals created (heartbeat=2m, activityFlush=1.5s)");
|
|
12077
12392
|
api.registerTool((toolCtx) => {
|
|
12078
12393
|
const agentId = toolCtx.agentId ?? "main";
|
|
12079
12394
|
const agentName = resolveAgentName(agentId);
|
|
@@ -12120,12 +12435,19 @@ function registerHooks(api, cfg) {
|
|
|
12120
12435
|
}
|
|
12121
12436
|
async function pushHeartbeat() {
|
|
12122
12437
|
const allAgentIds = ["main", ...(config?.agents?.list ?? []).map((a) => a.id)];
|
|
12438
|
+
for (const agentId of allAgentIds) {
|
|
12439
|
+
const agentName = resolveAgentName(agentId);
|
|
12440
|
+
const pruned = tracker.pruneStaleSessions(agentName, 6e5);
|
|
12441
|
+
if (pruned.length > 0) {
|
|
12442
|
+
logger.info(`cohort-sync: pruned ${pruned.length} stale sessions for ${agentName}`);
|
|
12443
|
+
}
|
|
12444
|
+
}
|
|
12123
12445
|
for (const agentId of allAgentIds) {
|
|
12124
12446
|
const agentName = resolveAgentName(agentId);
|
|
12125
12447
|
try {
|
|
12126
|
-
const snapshot =
|
|
12448
|
+
const snapshot = tracker.getTelemetrySnapshot(agentName);
|
|
12127
12449
|
if (snapshot) {
|
|
12128
|
-
await pushTelemetry(cfg.apiKey, snapshot);
|
|
12450
|
+
await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION });
|
|
12129
12451
|
} else {
|
|
12130
12452
|
logger.info(`cohort-sync: heartbeat skipped ${agentName} \u2014 no snapshot in tracker`);
|
|
12131
12453
|
}
|
|
@@ -12133,13 +12455,32 @@ function registerHooks(api, cfg) {
|
|
|
12133
12455
|
logger.warn(`cohort-sync: heartbeat push failed for ${agentName}: ${String(err)}`);
|
|
12134
12456
|
}
|
|
12135
12457
|
}
|
|
12458
|
+
for (const agentId of allAgentIds) {
|
|
12459
|
+
const agentName = resolveAgentName(agentId);
|
|
12460
|
+
try {
|
|
12461
|
+
if (tracker.shouldPushSessions(agentName)) {
|
|
12462
|
+
const sessSnapshot = tracker.getSessionsSnapshot(agentName);
|
|
12463
|
+
await pushSessions(cfg.apiKey, agentName, sessSnapshot);
|
|
12464
|
+
tracker.markSessionsPushed(agentName);
|
|
12465
|
+
}
|
|
12466
|
+
} catch (err) {
|
|
12467
|
+
logger.warn(`cohort-sync: heartbeat session push failed for ${agentName}: ${String(err)}`);
|
|
12468
|
+
}
|
|
12469
|
+
}
|
|
12136
12470
|
logger.info(`cohort-sync: heartbeat pushed for ${allAgentIds.length} agents`);
|
|
12137
12471
|
}
|
|
12472
|
+
async function flushActivityBuffer() {
|
|
12473
|
+
const entries = tracker.flushActivity();
|
|
12474
|
+
if (entries.length === 0) return;
|
|
12475
|
+
try {
|
|
12476
|
+
await pushActivity(cfg.apiKey, entries);
|
|
12477
|
+
} catch (err) {
|
|
12478
|
+
logger.warn(`cohort-sync: activity flush failed: ${String(err)}`);
|
|
12479
|
+
}
|
|
12480
|
+
}
|
|
12138
12481
|
api.on("gateway_start", async (event) => {
|
|
12139
12482
|
try {
|
|
12140
|
-
|
|
12141
|
-
const pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
12142
|
-
checkForUpdate(pkgJson.version, logger).catch(() => {
|
|
12483
|
+
checkForUpdate(PLUGIN_VERSION, logger).catch(() => {
|
|
12143
12484
|
});
|
|
12144
12485
|
} catch {
|
|
12145
12486
|
}
|
|
@@ -12165,30 +12506,32 @@ function registerHooks(api, cfg) {
|
|
|
12165
12506
|
for (const agentId of allAgentIds) {
|
|
12166
12507
|
const agentName = resolveAgentName(agentId);
|
|
12167
12508
|
try {
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12509
|
+
tracker.setModel(agentName, resolveModel(agentId));
|
|
12510
|
+
tracker.updateStatus(agentName, "idle");
|
|
12511
|
+
const snapshot = tracker.getTelemetrySnapshot(agentName);
|
|
12512
|
+
if (snapshot) {
|
|
12513
|
+
await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION });
|
|
12514
|
+
tracker.markTelemetryPushed(agentName);
|
|
12515
|
+
}
|
|
12173
12516
|
} catch (err) {
|
|
12174
12517
|
logger.warn(`cohort-sync: initial telemetry seed failed for ${agentName}: ${String(err)}`);
|
|
12175
12518
|
}
|
|
12176
12519
|
}
|
|
12177
12520
|
logger.info(`cohort-sync: seeded telemetry for ${allAgentIds.length} agents`);
|
|
12178
|
-
heartbeatInterval = setInterval(() => {
|
|
12179
|
-
pushHeartbeat().catch((err) => {
|
|
12180
|
-
logger.warn(`cohort-sync: heartbeat tick failed: ${String(err)}`);
|
|
12181
|
-
});
|
|
12182
|
-
}, 12e4);
|
|
12183
|
-
logger.info("cohort-sync: heartbeat started (2m interval)");
|
|
12184
12521
|
});
|
|
12185
12522
|
api.on("agent_end", async (_event, ctx) => {
|
|
12186
12523
|
const agentId = ctx.agentId ?? "main";
|
|
12187
12524
|
const agentName = resolveAgentName(agentId);
|
|
12188
12525
|
try {
|
|
12526
|
+
tracker.updateStatus(agentName, "idle");
|
|
12189
12527
|
await syncAgentStatus(agentName, "idle", resolveModel(agentId), cfg, logger);
|
|
12190
|
-
|
|
12191
|
-
|
|
12528
|
+
if (tracker.shouldPushTelemetry(agentName)) {
|
|
12529
|
+
const snapshot = tracker.getTelemetrySnapshot(agentName);
|
|
12530
|
+
if (snapshot) {
|
|
12531
|
+
await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION });
|
|
12532
|
+
tracker.markTelemetryPushed(agentName);
|
|
12533
|
+
}
|
|
12534
|
+
}
|
|
12192
12535
|
} catch (err) {
|
|
12193
12536
|
logger.warn(`cohort-sync: agent_end sync failed: ${String(err)}`);
|
|
12194
12537
|
}
|
|
@@ -12199,14 +12542,33 @@ function registerHooks(api, cfg) {
|
|
|
12199
12542
|
try {
|
|
12200
12543
|
const usage = event.usage ?? {};
|
|
12201
12544
|
const ext = event;
|
|
12202
|
-
const
|
|
12545
|
+
const sessionKey = ctx.sessionKey;
|
|
12546
|
+
tracker.updateFromLlmOutput(agentName, sessionKey, {
|
|
12203
12547
|
model: event.model ?? resolveModel(agentId),
|
|
12204
12548
|
tokensIn: usage.input ?? 0,
|
|
12205
12549
|
tokensOut: usage.output ?? 0,
|
|
12206
12550
|
contextTokens: ext.contextTokens ?? usage.total ?? 0,
|
|
12207
12551
|
contextLimit: ext.contextLimit ?? 0
|
|
12208
12552
|
});
|
|
12209
|
-
|
|
12553
|
+
if (sessionKey && !tracker.hasSession(agentName, sessionKey)) {
|
|
12554
|
+
tracker.addSession(agentName, sessionKey);
|
|
12555
|
+
logger.info(`cohort-sync: inferred session for ${agentName} from llm_output (${sessionKey})`);
|
|
12556
|
+
}
|
|
12557
|
+
if (sessionKey) {
|
|
12558
|
+
tracker.setSessionAgent(sessionKey, agentName);
|
|
12559
|
+
}
|
|
12560
|
+
if (tracker.shouldPushTelemetry(agentName)) {
|
|
12561
|
+
const snapshot = tracker.getTelemetrySnapshot(agentName);
|
|
12562
|
+
if (snapshot) {
|
|
12563
|
+
await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION });
|
|
12564
|
+
tracker.markTelemetryPushed(agentName);
|
|
12565
|
+
}
|
|
12566
|
+
}
|
|
12567
|
+
if (tracker.shouldPushSessions(agentName)) {
|
|
12568
|
+
const sessionsSnapshot = tracker.getSessionsSnapshot(agentName);
|
|
12569
|
+
await pushSessions(cfg.apiKey, agentName, sessionsSnapshot);
|
|
12570
|
+
tracker.markSessionsPushed(agentName);
|
|
12571
|
+
}
|
|
12210
12572
|
} catch (err) {
|
|
12211
12573
|
logger.warn(`cohort-sync: llm_output telemetry failed: ${String(err)}`);
|
|
12212
12574
|
}
|
|
@@ -12216,12 +12578,22 @@ function registerHooks(api, cfg) {
|
|
|
12216
12578
|
const agentName = resolveAgentName(agentId);
|
|
12217
12579
|
try {
|
|
12218
12580
|
const ext = event;
|
|
12219
|
-
|
|
12220
|
-
compacted: true,
|
|
12581
|
+
tracker.updateFromCompaction(agentName, {
|
|
12221
12582
|
contextTokens: ext.contextTokens ?? event.tokenCount ?? 0,
|
|
12222
12583
|
contextLimit: ext.contextLimit ?? 0
|
|
12223
12584
|
});
|
|
12224
|
-
|
|
12585
|
+
if (tracker.shouldPushTelemetry(agentName)) {
|
|
12586
|
+
const snapshot = tracker.getTelemetrySnapshot(agentName);
|
|
12587
|
+
if (snapshot) {
|
|
12588
|
+
await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION });
|
|
12589
|
+
tracker.markTelemetryPushed(agentName);
|
|
12590
|
+
}
|
|
12591
|
+
}
|
|
12592
|
+
const entry = buildActivitySentence(agentName, "after_compaction", {
|
|
12593
|
+
beforePercent: ext.beforePercent,
|
|
12594
|
+
afterPercent: ext.afterPercent
|
|
12595
|
+
});
|
|
12596
|
+
if (entry) tracker.addActivity(entry);
|
|
12225
12597
|
} catch (err) {
|
|
12226
12598
|
logger.warn(`cohort-sync: after_compaction telemetry failed: ${String(err)}`);
|
|
12227
12599
|
}
|
|
@@ -12230,8 +12602,29 @@ function registerHooks(api, cfg) {
|
|
|
12230
12602
|
const agentId = ctx.agentId ?? "main";
|
|
12231
12603
|
const agentName = resolveAgentName(agentId);
|
|
12232
12604
|
try {
|
|
12233
|
-
|
|
12234
|
-
|
|
12605
|
+
tracker.updateStatus(agentName, "working");
|
|
12606
|
+
if (tracker.shouldPushTelemetry(agentName)) {
|
|
12607
|
+
const snapshot = tracker.getTelemetrySnapshot(agentName);
|
|
12608
|
+
if (snapshot) {
|
|
12609
|
+
await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION });
|
|
12610
|
+
tracker.markTelemetryPushed(agentName);
|
|
12611
|
+
}
|
|
12612
|
+
}
|
|
12613
|
+
const sessionKey = ctx.sessionKey;
|
|
12614
|
+
if (sessionKey && !tracker.hasSession(agentName, sessionKey)) {
|
|
12615
|
+
tracker.addSession(agentName, sessionKey);
|
|
12616
|
+
tracker.setSessionAgent(sessionKey, agentName);
|
|
12617
|
+
logger.info(`cohort-sync: inferred session for ${agentName} (${sessionKey})`);
|
|
12618
|
+
if (tracker.shouldPushSessions(agentName)) {
|
|
12619
|
+
const sessSnapshot = tracker.getSessionsSnapshot(agentName);
|
|
12620
|
+
await pushSessions(cfg.apiKey, agentName, sessSnapshot);
|
|
12621
|
+
tracker.markSessionsPushed(agentName);
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
const entry = buildActivitySentence(agentName, "before_agent_start", {
|
|
12625
|
+
channel: ctx.messageProvider ?? "unknown"
|
|
12626
|
+
});
|
|
12627
|
+
if (entry) tracker.addActivity(entry);
|
|
12235
12628
|
} catch (err) {
|
|
12236
12629
|
logger.warn(`cohort-sync: before_agent_start telemetry failed: ${String(err)}`);
|
|
12237
12630
|
}
|
|
@@ -12240,15 +12633,18 @@ function registerHooks(api, cfg) {
|
|
|
12240
12633
|
const agentId = ctx.agentId ?? "main";
|
|
12241
12634
|
const agentName = resolveAgentName(agentId);
|
|
12242
12635
|
try {
|
|
12243
|
-
const
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12636
|
+
const sessionKey = ctx.sessionKey ?? event.sessionId ?? String(Date.now());
|
|
12637
|
+
tracker.addSession(agentName, sessionKey);
|
|
12638
|
+
if (tracker.shouldPushSessions(agentName)) {
|
|
12639
|
+
const sessionsSnapshot = tracker.getSessionsSnapshot(agentName);
|
|
12640
|
+
await pushSessions(cfg.apiKey, agentName, sessionsSnapshot);
|
|
12641
|
+
tracker.markSessionsPushed(agentName);
|
|
12642
|
+
}
|
|
12643
|
+
const parsed = parseSessionKey(sessionKey);
|
|
12644
|
+
const entry = buildActivitySentence(agentName, "session_start", {
|
|
12645
|
+
channel: parsed.channel
|
|
12250
12646
|
});
|
|
12251
|
-
|
|
12647
|
+
if (entry) tracker.addActivity(entry);
|
|
12252
12648
|
} catch (err) {
|
|
12253
12649
|
logger.warn(`cohort-sync: session_start tracking failed: ${String(err)}`);
|
|
12254
12650
|
}
|
|
@@ -12257,35 +12653,80 @@ function registerHooks(api, cfg) {
|
|
|
12257
12653
|
const agentId = ctx.agentId ?? "main";
|
|
12258
12654
|
const agentName = resolveAgentName(agentId);
|
|
12259
12655
|
try {
|
|
12260
|
-
const
|
|
12261
|
-
|
|
12262
|
-
|
|
12656
|
+
const sessionKey = ctx.sessionKey ?? event.sessionId ?? "";
|
|
12657
|
+
tracker.removeSession(agentName, sessionKey);
|
|
12658
|
+
if (tracker.shouldPushSessions(agentName)) {
|
|
12659
|
+
const sessionsSnapshot = tracker.getSessionsSnapshot(agentName);
|
|
12660
|
+
await pushSessions(cfg.apiKey, agentName, sessionsSnapshot);
|
|
12661
|
+
tracker.markSessionsPushed(agentName);
|
|
12662
|
+
}
|
|
12663
|
+
const entry = buildActivitySentence(agentName, "session_end", {});
|
|
12664
|
+
if (entry) tracker.addActivity(entry);
|
|
12263
12665
|
} catch (err) {
|
|
12264
12666
|
logger.warn(`cohort-sync: session_end tracking failed: ${String(err)}`);
|
|
12265
12667
|
}
|
|
12266
12668
|
});
|
|
12267
|
-
api.on("
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12669
|
+
api.on("before_tool_call", async (event, ctx) => {
|
|
12670
|
+
const agentName = resolveAgentFromContext(ctx);
|
|
12671
|
+
try {
|
|
12672
|
+
const entry = buildActivitySentence(agentName, "before_tool_call", {
|
|
12673
|
+
toolName: event.toolName ?? event.name,
|
|
12674
|
+
params: event.params ?? event.input
|
|
12675
|
+
});
|
|
12676
|
+
if (entry) tracker.addActivity(entry);
|
|
12677
|
+
} catch (err) {
|
|
12678
|
+
logger.warn(`cohort-sync: before_tool_call activity failed: ${String(err)}`);
|
|
12271
12679
|
}
|
|
12680
|
+
});
|
|
12681
|
+
api.on("message_received", async (_event, ctx) => {
|
|
12682
|
+
const agentName = resolveAgentFromContext(ctx);
|
|
12683
|
+
try {
|
|
12684
|
+
const entry = buildActivitySentence(agentName, "message_received", {
|
|
12685
|
+
channel: ctx.messageProvider ?? "unknown"
|
|
12686
|
+
});
|
|
12687
|
+
if (entry) tracker.addActivity(entry);
|
|
12688
|
+
} catch (err) {
|
|
12689
|
+
logger.warn(`cohort-sync: message_received activity failed: ${String(err)}`);
|
|
12690
|
+
}
|
|
12691
|
+
});
|
|
12692
|
+
api.on("message_sent", async (_event, ctx) => {
|
|
12693
|
+
const agentName = resolveAgentFromContext(ctx);
|
|
12694
|
+
try {
|
|
12695
|
+
const entry = buildActivitySentence(agentName, "message_sent", {
|
|
12696
|
+
channel: ctx.messageProvider ?? "unknown"
|
|
12697
|
+
});
|
|
12698
|
+
if (entry) tracker.addActivity(entry);
|
|
12699
|
+
} catch (err) {
|
|
12700
|
+
logger.warn(`cohort-sync: message_sent activity failed: ${String(err)}`);
|
|
12701
|
+
}
|
|
12702
|
+
});
|
|
12703
|
+
api.on("gateway_stop", async () => {
|
|
12704
|
+
clearIntervalsFromHot();
|
|
12705
|
+
heartbeatInterval = null;
|
|
12706
|
+
activityFlushInterval = null;
|
|
12707
|
+
await flushActivityBuffer().catch((err) => {
|
|
12708
|
+
logger.warn(`cohort-sync: final activity flush failed: ${String(err)}`);
|
|
12709
|
+
});
|
|
12272
12710
|
const allAgentIds = ["main", ...(config?.agents?.list ?? []).map((a) => a.id)];
|
|
12273
12711
|
for (const agentId of allAgentIds) {
|
|
12274
12712
|
const agentName = resolveAgentName(agentId);
|
|
12275
12713
|
try {
|
|
12276
|
-
|
|
12277
|
-
|
|
12714
|
+
tracker.updateStatus(agentName, "unreachable");
|
|
12715
|
+
const snapshot = tracker.getTelemetrySnapshot(agentName);
|
|
12716
|
+
if (snapshot) {
|
|
12717
|
+
await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION });
|
|
12718
|
+
}
|
|
12278
12719
|
} catch (err) {
|
|
12279
12720
|
logger.warn(`cohort-sync: final unreachable push failed for ${agentName}: ${String(err)}`);
|
|
12280
12721
|
}
|
|
12281
12722
|
}
|
|
12723
|
+
saveAgentStatesToHot(tracker.exportState());
|
|
12282
12724
|
try {
|
|
12283
12725
|
await markAllUnreachable(cfg, logger);
|
|
12284
12726
|
} catch (err) {
|
|
12285
12727
|
logger.warn(`cohort-sync: markAllUnreachable failed: ${String(err)}`);
|
|
12286
12728
|
}
|
|
12287
|
-
|
|
12288
|
-
sessions.clear();
|
|
12729
|
+
tracker.clear();
|
|
12289
12730
|
closeSubscription();
|
|
12290
12731
|
logger.info("cohort-sync: subscription closed");
|
|
12291
12732
|
});
|
package/dist/package.json
CHANGED