@cfio/cohort-sync 0.31.11 → 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
@@ -11999,6 +11999,7 @@ var acknowledgeCommandRef = makeFunctionReference("gatewayCommands:acknowledgeCo
11999
11999
  var failCommandRef = makeFunctionReference("gatewayCommands:failCommand");
12000
12000
  var getChannelsForPlugin = makeFunctionReference("cloudGatewayChannels:listForPlugin");
12001
12001
  var addCommentFromPluginRef = makeFunctionReference("comments:addCommentFromPlugin");
12002
+ var addRoomMessageFromPluginRef = makeFunctionReference("rooms:addMessageFromPlugin");
12002
12003
  var transitionFromPluginRef = makeFunctionReference("tasks:transitionFromPlugin");
12003
12004
  async function pushTelemetry(apiKey2, data) {
12004
12005
  if (authCircuitOpen) return;
@@ -12102,6 +12103,30 @@ async function callAddCommentFromPlugin(apiKey2, args) {
12102
12103
  throw err;
12103
12104
  }
12104
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
+ }
12105
12130
  async function callTransitionFromPlugin(apiKey2, args) {
12106
12131
  if (authCircuitOpen) {
12107
12132
  throw new Error(
@@ -12142,6 +12167,7 @@ var ATMENTION_RESPONSE_PROMPT = `YOU WERE DIRECTLY @-MENTIONED. RESPOND.
12142
12167
  var TOOLS_REFERENCE = `
12143
12168
  TOOLS: Use these \u2014 do NOT call the REST API directly.
12144
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
12145
12171
  - cohort_task(task_number) \u2014 fetch full task details + comments
12146
12172
  - cohort_transition(task_number, status) \u2014 change status
12147
12173
  - cohort_assign(task_number, assignee) \u2014 assign/unassign
@@ -12153,6 +12179,11 @@ function buildNotificationMessage(n) {
12153
12179
  let header;
12154
12180
  let cta;
12155
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;
12156
12187
  case "comment":
12157
12188
  if (n.isMentioned) {
12158
12189
  header = `You were @mentioned on task #${n.taskNumber} "${n.taskTitle}"
@@ -12181,7 +12212,7 @@ From: ${n.actorName}`;
12181
12212
  }
12182
12213
  let body = "";
12183
12214
  if (n.preview) {
12184
- if (n.type === "comment") {
12215
+ if (n.type === "comment" || n.type === "room_message") {
12185
12216
  const safe = sanitizePreview(n.preview);
12186
12217
  body = `
12187
12218
  <user_comment>
@@ -12200,7 +12231,7 @@ Comment: "${n.preview}"`;
12200
12231
  Scope: ${truncated}`;
12201
12232
  }
12202
12233
  let prompt;
12203
- if (n.type === "comment" && n.isMentioned) {
12234
+ if (n.type === "comment" && n.isMentioned || n.type === "room_message") {
12204
12235
  prompt = n.behavioralPrompt ? `${n.behavioralPrompt}
12205
12236
 
12206
12237
  ${ATMENTION_RESPONSE_PROMPT}` : ATMENTION_RESPONSE_PROMPT;
@@ -12209,7 +12240,7 @@ ${ATMENTION_RESPONSE_PROMPT}` : ATMENTION_RESPONSE_PROMPT;
12209
12240
 
12210
12241
  ${DEFAULT_BEHAVIORAL_PROMPT}` : DEFAULT_BEHAVIORAL_PROMPT;
12211
12242
  }
12212
- const promptBlock = n.type === "comment" ? `
12243
+ const promptBlock = n.type === "comment" || n.type === "room_message" ? `
12213
12244
 
12214
12245
  ---
12215
12246
  ${prompt}` : "";
@@ -12218,6 +12249,7 @@ ${prompt}` : "";
12218
12249
  ${cta}${promptBlock}${TOOLS_REFERENCE}`;
12219
12250
  }
12220
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}`;
12221
12253
  const response = await fetch(`http://localhost:${port}/hooks/agent`, {
12222
12254
  method: "POST",
12223
12255
  headers: {
@@ -12229,7 +12261,7 @@ async function injectNotification(port, hooksToken, n, agentId = "main") {
12229
12261
  name: "Cohort",
12230
12262
  agentId,
12231
12263
  deliver: false,
12232
- sessionKey: `hook:cohort:task-${n.taskNumber}`
12264
+ sessionKey
12233
12265
  }),
12234
12266
  signal: AbortSignal.timeout(1e4)
12235
12267
  });
@@ -12317,7 +12349,8 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12317
12349
  try {
12318
12350
  if (hooksToken) {
12319
12351
  await injectNotification(port, hooksToken, n, openclawAgentId);
12320
- 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})`);
12321
12354
  } else {
12322
12355
  throw new Error(
12323
12356
  `no transport available for notification ${n._id} (gwClient alive: ${gwClient?.isAlive() ?? "null"}, hooksToken: ${!!hooksToken})`
@@ -12332,8 +12365,9 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12332
12365
  const newFailCount = failCount + 1;
12333
12366
  deliveryFailures.set(n._id, newFailCount);
12334
12367
  if (newFailCount >= MAX_DELIVERY_ATTEMPTS) {
12368
+ const targetLabel = n.type === "room_message" ? `room ${n.roomId ?? n.roomName ?? "unknown"}` : `task #${n.taskNumber}`;
12335
12369
  logger.error(
12336
- `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)}`
12337
12371
  );
12338
12372
  try {
12339
12373
  await c.mutation(markDeliveredByPlugin, {
@@ -13741,7 +13775,7 @@ function dumpEvent(event) {
13741
13775
  function positiveNumber(value) {
13742
13776
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
13743
13777
  }
13744
- var PLUGIN_VERSION = true ? "0.31.11" : "unknown";
13778
+ var PLUGIN_VERSION = true ? "0.31.12" : "unknown";
13745
13779
  function resolveGatewayToken(api) {
13746
13780
  const token2 = api.config?.gateway?.auth?.token;
13747
13781
  return typeof token2 === "string" ? token2 : null;
@@ -14871,6 +14905,31 @@ Do not attempt more comments until tomorrow.`);
14871
14905
  }
14872
14906
  };
14873
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
+ });
14874
14933
  api.registerTool(() => {
14875
14934
  return {
14876
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.11"
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.11",
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.11",
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"