@cfio/cohort-sync 0.12.1 → 0.14.0

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 CHANGED
@@ -11700,14 +11700,16 @@ async function executeCommand(cmd, gwClient, cfg, resolveAgentName, logger) {
11700
11700
  throw new Error(`Cron message exceeds maximum length of ${MAX_CRON_MESSAGE_LENGTH} characters`);
11701
11701
  }
11702
11702
  if (cmd.type === "restart") {
11703
- if (gwClient && gwClient.isAlive()) {
11704
- logger.warn(`cohort-sync: RESTART command received (id=${cmd._id}), issuing graceful gateway restart`);
11705
- await gwClient.request("gateway.restart", { reason: "Cohort restart command" });
11706
- } else {
11707
- logger.warn(`cohort-sync: RESTART command received (id=${cmd._id}), no gateway client \u2014 falling back to SIGTERM`);
11708
- await new Promise((r) => setTimeout(r, 500));
11709
- process.kill(process.pid, "SIGTERM");
11703
+ if (cfg.commandsRestartEnabled !== true) {
11704
+ throw new Error(
11705
+ "Gateway restart is not authorized. Set `commands.restart: true` in your OpenClaw config and restart the gateway once. See https://docs.cohort.bot/gateway/restart"
11706
+ );
11710
11707
  }
11708
+ logger.warn(
11709
+ `cohort-sync: RESTART command received (id=${cmd._id}), sending SIGUSR1 for graceful in-process restart`
11710
+ );
11711
+ await new Promise((r) => setTimeout(r, 500));
11712
+ process.kill(process.pid, "SIGUSR1");
11711
11713
  return;
11712
11714
  }
11713
11715
  if (cmd.type.startsWith("cron")) {
@@ -13110,6 +13112,9 @@ var AgentStateTracker = class {
13110
13112
  contextTokens: 0,
13111
13113
  contextLimit: 0,
13112
13114
  compactions: 0,
13115
+ cacheReadTokens: 0,
13116
+ cacheWriteTokens: 0,
13117
+ lastSessionKey: void 0,
13113
13118
  sessions: /* @__PURE__ */ new Map(),
13114
13119
  lastPushedTelemetry: null,
13115
13120
  lastPushedSessions: null
@@ -13170,6 +13175,8 @@ var AgentStateTracker = class {
13170
13175
  if (data.model) state.model = data.model;
13171
13176
  if (data.tokensIn !== void 0) state.tokensIn += data.tokensIn;
13172
13177
  if (data.tokensOut !== void 0) state.tokensOut += data.tokensOut;
13178
+ if (data.cacheReadTokens !== void 0) state.cacheReadTokens += data.cacheReadTokens;
13179
+ if (data.cacheWriteTokens !== void 0) state.cacheWriteTokens += data.cacheWriteTokens;
13173
13180
  if (data.contextTokens !== void 0) state.contextTokens = data.contextTokens;
13174
13181
  if (data.contextLimit !== void 0) state.contextLimit = data.contextLimit;
13175
13182
  state.status = "working";
@@ -13183,6 +13190,15 @@ var AgentStateTracker = class {
13183
13190
  }
13184
13191
  }
13185
13192
  }
13193
+ /**
13194
+ * Set the last-observed session key for cost attribution. The server extracts
13195
+ * the Cohort task number from this key. Called from the llm_output hook.
13196
+ */
13197
+ updateSessionKey(agentName, sessionKey) {
13198
+ if (!sessionKey) return;
13199
+ const state = this.getOrCreate(agentName);
13200
+ state.lastSessionKey = sessionKey;
13201
+ }
13186
13202
  updateFromCompaction(agentName, data) {
13187
13203
  const state = this.getOrCreate(agentName);
13188
13204
  state.compactions += 1;
@@ -13205,7 +13221,10 @@ var AgentStateTracker = class {
13205
13221
  tokensIn: state.tokensIn,
13206
13222
  tokensOut: state.tokensOut,
13207
13223
  compactions: state.compactions,
13208
- activeSessions: state.sessions.size
13224
+ activeSessions: state.sessions.size,
13225
+ cacheReadTokens: state.cacheReadTokens,
13226
+ cacheWriteTokens: state.cacheWriteTokens,
13227
+ sessionKey: state.lastSessionKey
13209
13228
  };
13210
13229
  }
13211
13230
  getSessionsSnapshot(agentName) {
@@ -13353,7 +13372,7 @@ function dumpCtx(ctx) {
13353
13372
  function dumpEvent(event) {
13354
13373
  return dumpCtx(event);
13355
13374
  }
13356
- var PLUGIN_VERSION = true ? "0.12.1" : "unknown";
13375
+ var PLUGIN_VERSION = true ? "0.14.0" : "unknown";
13357
13376
  function resolveGatewayToken(api) {
13358
13377
  const token = api.config?.gateway?.auth?.token;
13359
13378
  return typeof token === "string" ? token : null;
@@ -13559,7 +13578,7 @@ async function handleGatewayStart(event, state) {
13559
13578
  tracker.updateStatus(agentName, "idle");
13560
13579
  const snapshot = tracker.getTelemetrySnapshot(agentName);
13561
13580
  if (snapshot) {
13562
- await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13581
+ await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, commandsRestartEnabled: cfg.commandsRestartEnabled, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13563
13582
  tracker.markTelemetryPushed(agentName);
13564
13583
  }
13565
13584
  } catch (err) {
@@ -13573,7 +13592,7 @@ async function handleGatewayStart(event, state) {
13573
13592
  const agentName = state.resolveAgentName(agentId);
13574
13593
  const snapshot = tracker.getTelemetrySnapshot(agentName);
13575
13594
  if (snapshot) {
13576
- await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} }).catch(() => {
13595
+ await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, commandsRestartEnabled: cfg.commandsRestartEnabled, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} }).catch(() => {
13577
13596
  });
13578
13597
  }
13579
13598
  }
@@ -13651,7 +13670,7 @@ function registerHookHandlers(api, logger, getState) {
13651
13670
  if (tracker.shouldPushTelemetry(agentName)) {
13652
13671
  const snapshot = tracker.getTelemetrySnapshot(agentName);
13653
13672
  if (snapshot) {
13654
- await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13673
+ await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, commandsRestartEnabled: cfg.commandsRestartEnabled, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13655
13674
  tracker.markTelemetryPushed(agentName);
13656
13675
  }
13657
13676
  }
@@ -13697,9 +13716,12 @@ function registerHookHandlers(api, logger, getState) {
13697
13716
  model,
13698
13717
  tokensIn: usage.input ?? 0,
13699
13718
  tokensOut: usage.output ?? 0,
13719
+ cacheReadTokens: usage.cacheRead ?? 0,
13720
+ cacheWriteTokens: usage.cacheWrite ?? 0,
13700
13721
  contextTokens,
13701
13722
  contextLimit
13702
13723
  });
13724
+ tracker.updateSessionKey(agentName, sessionKey);
13703
13725
  if (sessionKey && !tracker.hasSession(agentName, sessionKey)) {
13704
13726
  tracker.addSession(agentName, sessionKey);
13705
13727
  log.info(`cohort-sync: inferred session for ${agentName} from llm_output (${sessionKey})`);
@@ -13710,7 +13732,7 @@ function registerHookHandlers(api, logger, getState) {
13710
13732
  if (tracker.shouldPushTelemetry(agentName)) {
13711
13733
  const snapshot = tracker.getTelemetrySnapshot(agentName);
13712
13734
  if (snapshot) {
13713
- await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13735
+ await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, commandsRestartEnabled: cfg.commandsRestartEnabled, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13714
13736
  tracker.markTelemetryPushed(agentName);
13715
13737
  }
13716
13738
  }
@@ -13738,7 +13760,7 @@ function registerHookHandlers(api, logger, getState) {
13738
13760
  if (tracker.shouldPushTelemetry(agentName)) {
13739
13761
  const snapshot = tracker.getTelemetrySnapshot(agentName);
13740
13762
  if (snapshot) {
13741
- await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13763
+ await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, commandsRestartEnabled: cfg.commandsRestartEnabled, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13742
13764
  tracker.markTelemetryPushed(agentName);
13743
13765
  }
13744
13766
  }
@@ -13780,7 +13802,7 @@ function registerHookHandlers(api, logger, getState) {
13780
13802
  if (tracker.shouldPushTelemetry(agentName)) {
13781
13803
  const snapshot = tracker.getTelemetrySnapshot(agentName);
13782
13804
  if (snapshot) {
13783
- await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13805
+ await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, commandsRestartEnabled: cfg.commandsRestartEnabled, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13784
13806
  tracker.markTelemetryPushed(agentName);
13785
13807
  }
13786
13808
  }
@@ -13984,7 +14006,7 @@ function registerHookHandlers(api, logger, getState) {
13984
14006
  tracker.updateStatus(agentName, "unreachable");
13985
14007
  const snapshot = tracker.getTelemetrySnapshot(agentName);
13986
14008
  if (snapshot) {
13987
- await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
14009
+ await pushTelemetry(cfg.apiKey, { ...snapshot, pluginVersion: PLUGIN_VERSION, commandsRestartEnabled: cfg.commandsRestartEnabled, ...state.latestPluginVersion ? { latestPluginVersion: state.latestPluginVersion } : {} });
13988
14010
  }
13989
14011
  } catch (err) {
13990
14012
  log.warn(`cohort-sync: final unreachable push failed for ${agentName}: ${String(err)}`);
@@ -14455,11 +14477,13 @@ Do not attempt more comments until tomorrow.`);
14455
14477
  }
14456
14478
  api.logger.info(`cohort-sync: activated (api: ${apiUrl2})`);
14457
14479
  if (!sharedHookState) {
14480
+ const commandsRestartEnabled = api.config?.commands?.restart === true;
14458
14481
  sharedHookState = initializeHookState(api, {
14459
14482
  apiUrl: apiUrl2,
14460
14483
  apiKey: apiKey2,
14461
14484
  stateDir: svcCtx.stateDir,
14462
- agentNameMap: cfg?.agentNameMap
14485
+ agentNameMap: cfg?.agentNameMap,
14486
+ commandsRestartEnabled
14463
14487
  });
14464
14488
  }
14465
14489
  },
@@ -55,5 +55,5 @@
55
55
  }
56
56
  }
57
57
  },
58
- "version": "0.12.1"
58
+ "version": "0.14.0"
59
59
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "OpenClaw plugin — syncs agent telemetry, sessions, and activity to the Cohort dashboard",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "OpenClaw plugin — syncs agent telemetry, sessions, and activity to the Cohort dashboard",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.cohort.bot/gateway",
@@ -24,6 +24,7 @@
24
24
  "postinstall": "node scripts/postinstall.mjs",
25
25
  "prepublishOnly": "node scripts/build.mjs",
26
26
  "typecheck": "echo 'Skipped: openclaw types loosely pinned, build uses esbuild not tsc'",
27
+ "pretest": "node scripts/build.mjs",
27
28
  "test": "vitest run"
28
29
  },
29
30
  "files": [