@cfio/cohort-sync 0.31.10 → 0.31.12

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
@@ -11880,6 +11880,17 @@ function deriveConvexUrl(apiUrl2) {
11880
11880
  }
11881
11881
  return normalized;
11882
11882
  }
11883
+ async function buildHookResponseError(response) {
11884
+ let detail = "";
11885
+ try {
11886
+ const body = await response.text();
11887
+ if (body.trim()) {
11888
+ detail = `: ${body.trim().slice(0, 500)}`;
11889
+ }
11890
+ } catch {
11891
+ }
11892
+ return new Error(`/hooks/agent returned ${response.status} ${response.statusText}${detail}`);
11893
+ }
11883
11894
  var savedLogger = null;
11884
11895
  function setLogger(logger) {
11885
11896
  savedLogger = logger;
@@ -11988,6 +11999,7 @@ var acknowledgeCommandRef = makeFunctionReference("gatewayCommands:acknowledgeCo
11988
11999
  var failCommandRef = makeFunctionReference("gatewayCommands:failCommand");
11989
12000
  var getChannelsForPlugin = makeFunctionReference("cloudGatewayChannels:listForPlugin");
11990
12001
  var addCommentFromPluginRef = makeFunctionReference("comments:addCommentFromPlugin");
12002
+ var addRoomMessageFromPluginRef = makeFunctionReference("rooms:addMessageFromPlugin");
11991
12003
  var transitionFromPluginRef = makeFunctionReference("tasks:transitionFromPlugin");
11992
12004
  async function pushTelemetry(apiKey2, data) {
11993
12005
  if (authCircuitOpen) return;
@@ -12091,6 +12103,30 @@ async function callAddCommentFromPlugin(apiKey2, args) {
12091
12103
  throw err;
12092
12104
  }
12093
12105
  }
12106
+ async function callAddRoomMessageFromPlugin(apiKey2, args) {
12107
+ if (authCircuitOpen) {
12108
+ throw new Error(
12109
+ 'cohort-sync: API key rejected \u2014 all outbound mutations disabled until gateway restart.\n 1. Create a new key at https://my.cohort.bot/settings/api-keys\n 2. Run: openclaw config set plugins.entries.cohort-sync.config.apiKey "ch_live_..."'
12110
+ );
12111
+ }
12112
+ const c = getClient();
12113
+ if (!c) {
12114
+ throw new Error("Convex client not initialized \u2014 subscription may not be active");
12115
+ }
12116
+ try {
12117
+ return await c.mutation(addRoomMessageFromPluginRef, {
12118
+ apiKeyHash: hashApiKey(apiKey2),
12119
+ roomId: args.roomId,
12120
+ agentName: args.agentName,
12121
+ body: args.content
12122
+ });
12123
+ } catch (err) {
12124
+ if (isUnauthorizedError(err)) {
12125
+ tripAuthCircuit();
12126
+ }
12127
+ throw err;
12128
+ }
12129
+ }
12094
12130
  async function callTransitionFromPlugin(apiKey2, args) {
12095
12131
  if (authCircuitOpen) {
12096
12132
  throw new Error(
@@ -12131,6 +12167,7 @@ var ATMENTION_RESPONSE_PROMPT = `YOU WERE DIRECTLY @-MENTIONED. RESPOND.
12131
12167
  var TOOLS_REFERENCE = `
12132
12168
  TOOLS: Use these \u2014 do NOT call the REST API directly.
12133
12169
  - cohort_comment(task_number, comment) \u2014 post a comment
12170
+ - cohort_room_message(room_id, message) \u2014 post a message in a Cohort Room
12134
12171
  - cohort_task(task_number) \u2014 fetch full task details + comments
12135
12172
  - cohort_transition(task_number, status) \u2014 change status
12136
12173
  - cohort_assign(task_number, assignee) \u2014 assign/unassign
@@ -12142,6 +12179,11 @@ function buildNotificationMessage(n) {
12142
12179
  let header;
12143
12180
  let cta;
12144
12181
  switch (n.type) {
12182
+ case "room_message":
12183
+ header = `New message in Room "${n.roomName ?? n.roomId ?? "Room"}"
12184
+ From: ${n.actorName}`;
12185
+ cta = `Reply in the Room if you can help. Use the cohort_room_message tool (roomId: ${n.roomId ?? "unknown"}).`;
12186
+ break;
12145
12187
  case "comment":
12146
12188
  if (n.isMentioned) {
12147
12189
  header = `You were @mentioned on task #${n.taskNumber} "${n.taskTitle}"
@@ -12170,7 +12212,7 @@ From: ${n.actorName}`;
12170
12212
  }
12171
12213
  let body = "";
12172
12214
  if (n.preview) {
12173
- if (n.type === "comment") {
12215
+ if (n.type === "comment" || n.type === "room_message") {
12174
12216
  const safe = sanitizePreview(n.preview);
12175
12217
  body = `
12176
12218
  <user_comment>
@@ -12189,7 +12231,7 @@ Comment: "${n.preview}"`;
12189
12231
  Scope: ${truncated}`;
12190
12232
  }
12191
12233
  let prompt;
12192
- if (n.type === "comment" && n.isMentioned) {
12234
+ if (n.type === "comment" && n.isMentioned || n.type === "room_message") {
12193
12235
  prompt = n.behavioralPrompt ? `${n.behavioralPrompt}
12194
12236
 
12195
12237
  ${ATMENTION_RESPONSE_PROMPT}` : ATMENTION_RESPONSE_PROMPT;
@@ -12198,7 +12240,7 @@ ${ATMENTION_RESPONSE_PROMPT}` : ATMENTION_RESPONSE_PROMPT;
12198
12240
 
12199
12241
  ${DEFAULT_BEHAVIORAL_PROMPT}` : DEFAULT_BEHAVIORAL_PROMPT;
12200
12242
  }
12201
- const promptBlock = n.type === "comment" ? `
12243
+ const promptBlock = n.type === "comment" || n.type === "room_message" ? `
12202
12244
 
12203
12245
  ---
12204
12246
  ${prompt}` : "";
@@ -12207,6 +12249,7 @@ ${prompt}` : "";
12207
12249
  ${cta}${promptBlock}${TOOLS_REFERENCE}`;
12208
12250
  }
12209
12251
  async function injectNotification(port, hooksToken, n, agentId = "main") {
12252
+ const sessionKey = n.type === "room_message" && n.roomId ? `hook:cohort:room-${n.roomId}` : `hook:cohort:task-${n.taskNumber}`;
12210
12253
  const response = await fetch(`http://localhost:${port}/hooks/agent`, {
12211
12254
  method: "POST",
12212
12255
  headers: {
@@ -12218,12 +12261,12 @@ async function injectNotification(port, hooksToken, n, agentId = "main") {
12218
12261
  name: "Cohort",
12219
12262
  agentId,
12220
12263
  deliver: false,
12221
- sessionKey: `hook:cohort:task-${n.taskNumber}`
12264
+ sessionKey
12222
12265
  }),
12223
12266
  signal: AbortSignal.timeout(1e4)
12224
12267
  });
12225
12268
  if (!response.ok) {
12226
- throw new Error(`/hooks/agent returned ${response.status} ${response.statusText}`);
12269
+ throw await buildHookResponseError(response);
12227
12270
  }
12228
12271
  }
12229
12272
  function buildBriefingGenerationMessage(args) {
@@ -12258,12 +12301,12 @@ async function injectBriefingGeneration(port, hooksToken, args) {
12258
12301
  name: "Cohort",
12259
12302
  agentId: args.agentId,
12260
12303
  deliver: false,
12261
- sessionKey: args.agentId === "main" ? `hook:cohort:briefing-${args.requestId}` : `agent:${args.agentId}:hook:cohort:briefing-${args.requestId}`
12304
+ sessionKey: `hook:cohort:briefing-${args.requestId}`
12262
12305
  }),
12263
12306
  signal: AbortSignal.timeout(1e4)
12264
12307
  });
12265
12308
  if (!response.ok) {
12266
- throw new Error(`/hooks/agent returned ${response.status} ${response.statusText}`);
12309
+ throw await buildHookResponseError(response);
12267
12310
  }
12268
12311
  }
12269
12312
  var deliveryFailures = /* @__PURE__ */ new Map();
@@ -12306,7 +12349,8 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12306
12349
  try {
12307
12350
  if (hooksToken) {
12308
12351
  await injectNotification(port, hooksToken, n, openclawAgentId);
12309
- logger.info(`cohort-sync: injected notification for task #${n.taskNumber} (${agentName})`);
12352
+ const targetLabel = n.type === "room_message" ? `room ${n.roomId ?? n.roomName ?? "unknown"}` : `task #${n.taskNumber}`;
12353
+ logger.info(`cohort-sync: injected notification for ${targetLabel} (${agentName})`);
12310
12354
  } else {
12311
12355
  throw new Error(
12312
12356
  `no transport available for notification ${n._id} (gwClient alive: ${gwClient?.isAlive() ?? "null"}, hooksToken: ${!!hooksToken})`
@@ -12321,8 +12365,9 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12321
12365
  const newFailCount = failCount + 1;
12322
12366
  deliveryFailures.set(n._id, newFailCount);
12323
12367
  if (newFailCount >= MAX_DELIVERY_ATTEMPTS) {
12368
+ const targetLabel = n.type === "room_message" ? `room ${n.roomId ?? n.roomName ?? "unknown"}` : `task #${n.taskNumber}`;
12324
12369
  logger.error(
12325
- `cohort-sync: dead-letter notification ${n._id} for task #${n.taskNumber} (${n.type} from ${n.actorName}) after ${MAX_DELIVERY_ATTEMPTS} failed delivery attempts: ${String(err)}`
12370
+ `cohort-sync: dead-letter notification ${n._id} for ${targetLabel} (${n.type} from ${n.actorName}) after ${MAX_DELIVERY_ATTEMPTS} failed delivery attempts: ${String(err)}`
12326
12371
  );
12327
12372
  try {
12328
12373
  await c.mutation(markDeliveredByPlugin, {
@@ -13730,7 +13775,7 @@ function dumpEvent(event) {
13730
13775
  function positiveNumber(value) {
13731
13776
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
13732
13777
  }
13733
- var PLUGIN_VERSION = true ? "0.31.10" : "unknown";
13778
+ var PLUGIN_VERSION = true ? "0.31.12" : "unknown";
13734
13779
  function resolveGatewayToken(api) {
13735
13780
  const token2 = api.config?.gateway?.auth?.token;
13736
13781
  return typeof token2 === "string" ? token2 : null;
@@ -14860,6 +14905,31 @@ Do not attempt more comments until tomorrow.`);
14860
14905
  }
14861
14906
  };
14862
14907
  });
14908
+ api.registerTool((toolCtx) => {
14909
+ const agentId = toolCtx.agentId ?? "main";
14910
+ return {
14911
+ name: "cohort_room_message",
14912
+ label: "cohort_room_message",
14913
+ description: "Post a message in a Cohort Room. Use this to reply when you are participating in a Room chat.",
14914
+ parameters: Type.Object({
14915
+ room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
14916
+ message: Type.String({ description: "Message text to post in the Room" })
14917
+ }),
14918
+ async execute(_toolCallId, params) {
14919
+ const rt = getToolRuntime();
14920
+ if (!rt.isReady) {
14921
+ return textResult("cohort_room_message is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
14922
+ }
14923
+ const agentName = rt.resolveAgentName(agentId);
14924
+ const result = await callAddRoomMessageFromPlugin(rt.apiKey, {
14925
+ roomId: params.room_id,
14926
+ agentName,
14927
+ content: params.message
14928
+ });
14929
+ return textResult(`Message posted to Room ${params.room_id}.`, result);
14930
+ }
14931
+ };
14932
+ });
14863
14933
  api.registerTool(() => {
14864
14934
  return {
14865
14935
  name: "cohort_context",
@@ -11,6 +11,7 @@
11
11
  "contracts": {
12
12
  "tools": [
13
13
  "cohort_comment",
14
+ "cohort_room_message",
14
15
  "cohort_context",
15
16
  "cohort_briefing_context",
16
17
  "cohort_briefing",
@@ -74,5 +75,5 @@
74
75
  }
75
76
  }
76
77
  },
77
- "version": "0.31.10"
78
+ "version": "0.31.12"
78
79
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.31.10",
3
+ "version": "0.31.12",
4
4
  "description": "OpenClaw plugin — syncs agent telemetry, sessions, and activity to the Cohort dashboard",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -9,6 +9,7 @@
9
9
  "contracts": {
10
10
  "tools": [
11
11
  "cohort_comment",
12
+ "cohort_room_message",
12
13
  "cohort_context",
13
14
  "cohort_briefing_context",
14
15
  "cohort_briefing",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.31.10",
3
+ "version": "0.31.12",
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",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "esbuild": "^0.25.11",
40
- "vitest": "^2.1.9"
40
+ "vitest": "^3.2.6"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "openclaw": ">=0.1.0"