@cfio/cohort-sync 0.31.11 → 0.31.13

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,8 @@ 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");
12003
+ var promptAgentsFromPluginRef = makeFunctionReference("rooms:promptAgentsFromPlugin");
12002
12004
  var transitionFromPluginRef = makeFunctionReference("tasks:transitionFromPlugin");
12003
12005
  async function pushTelemetry(apiKey2, data) {
12004
12006
  if (authCircuitOpen) return;
@@ -12102,6 +12104,55 @@ async function callAddCommentFromPlugin(apiKey2, args) {
12102
12104
  throw err;
12103
12105
  }
12104
12106
  }
12107
+ async function callAddRoomMessageFromPlugin(apiKey2, args) {
12108
+ if (authCircuitOpen) {
12109
+ throw new Error(
12110
+ '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_..."'
12111
+ );
12112
+ }
12113
+ const c = getClient();
12114
+ if (!c) {
12115
+ throw new Error("Convex client not initialized \u2014 subscription may not be active");
12116
+ }
12117
+ try {
12118
+ return await c.mutation(addRoomMessageFromPluginRef, {
12119
+ apiKeyHash: hashApiKey(apiKey2),
12120
+ roomId: args.roomId,
12121
+ agentName: args.agentName,
12122
+ body: args.content
12123
+ });
12124
+ } catch (err) {
12125
+ if (isUnauthorizedError(err)) {
12126
+ tripAuthCircuit();
12127
+ }
12128
+ throw err;
12129
+ }
12130
+ }
12131
+ async function callPromptRoomAgentsFromPlugin(apiKey2, args) {
12132
+ if (authCircuitOpen) {
12133
+ throw new Error(
12134
+ '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_..."'
12135
+ );
12136
+ }
12137
+ const c = getClient();
12138
+ if (!c) {
12139
+ throw new Error("Convex client not initialized \u2014 subscription may not be active");
12140
+ }
12141
+ try {
12142
+ return await c.mutation(promptAgentsFromPluginRef, {
12143
+ apiKeyHash: hashApiKey(apiKey2),
12144
+ roomId: args.roomId,
12145
+ moderatorAgentName: args.moderatorAgentName,
12146
+ targetAgentNames: args.targetAgentNames,
12147
+ prompt: args.prompt
12148
+ });
12149
+ } catch (err) {
12150
+ if (isUnauthorizedError(err)) {
12151
+ tripAuthCircuit();
12152
+ }
12153
+ throw err;
12154
+ }
12155
+ }
12105
12156
  async function callTransitionFromPlugin(apiKey2, args) {
12106
12157
  if (authCircuitOpen) {
12107
12158
  throw new Error(
@@ -12142,6 +12193,7 @@ var ATMENTION_RESPONSE_PROMPT = `YOU WERE DIRECTLY @-MENTIONED. RESPOND.
12142
12193
  var TOOLS_REFERENCE = `
12143
12194
  TOOLS: Use these \u2014 do NOT call the REST API directly.
12144
12195
  - cohort_comment(task_number, comment) \u2014 post a comment
12196
+ - cohort_room_message(room_id, message) \u2014 post a message in a Cohort Room
12145
12197
  - cohort_task(task_number) \u2014 fetch full task details + comments
12146
12198
  - cohort_transition(task_number, status) \u2014 change status
12147
12199
  - cohort_assign(task_number, assignee) \u2014 assign/unassign
@@ -12153,6 +12205,11 @@ function buildNotificationMessage(n) {
12153
12205
  let header;
12154
12206
  let cta;
12155
12207
  switch (n.type) {
12208
+ case "room_message":
12209
+ header = `New message in Room "${n.roomName ?? n.roomId ?? "Room"}"
12210
+ From: ${n.actorName}`;
12211
+ cta = `Reply in the Room if you can help. Use the cohort_room_message tool (roomId: ${n.roomId ?? "unknown"}).`;
12212
+ break;
12156
12213
  case "comment":
12157
12214
  if (n.isMentioned) {
12158
12215
  header = `You were @mentioned on task #${n.taskNumber} "${n.taskTitle}"
@@ -12181,7 +12238,7 @@ From: ${n.actorName}`;
12181
12238
  }
12182
12239
  let body = "";
12183
12240
  if (n.preview) {
12184
- if (n.type === "comment") {
12241
+ if (n.type === "comment" || n.type === "room_message") {
12185
12242
  const safe = sanitizePreview(n.preview);
12186
12243
  body = `
12187
12244
  <user_comment>
@@ -12200,7 +12257,7 @@ Comment: "${n.preview}"`;
12200
12257
  Scope: ${truncated}`;
12201
12258
  }
12202
12259
  let prompt;
12203
- if (n.type === "comment" && n.isMentioned) {
12260
+ if (n.type === "comment" && n.isMentioned || n.type === "room_message") {
12204
12261
  prompt = n.behavioralPrompt ? `${n.behavioralPrompt}
12205
12262
 
12206
12263
  ${ATMENTION_RESPONSE_PROMPT}` : ATMENTION_RESPONSE_PROMPT;
@@ -12209,7 +12266,7 @@ ${ATMENTION_RESPONSE_PROMPT}` : ATMENTION_RESPONSE_PROMPT;
12209
12266
 
12210
12267
  ${DEFAULT_BEHAVIORAL_PROMPT}` : DEFAULT_BEHAVIORAL_PROMPT;
12211
12268
  }
12212
- const promptBlock = n.type === "comment" ? `
12269
+ const promptBlock = n.type === "comment" || n.type === "room_message" ? `
12213
12270
 
12214
12271
  ---
12215
12272
  ${prompt}` : "";
@@ -12218,6 +12275,7 @@ ${prompt}` : "";
12218
12275
  ${cta}${promptBlock}${TOOLS_REFERENCE}`;
12219
12276
  }
12220
12277
  async function injectNotification(port, hooksToken, n, agentId = "main") {
12278
+ const sessionKey = n.type === "room_message" && n.roomId ? `hook:cohort:room-${n.roomId}` : `hook:cohort:task-${n.taskNumber}`;
12221
12279
  const response = await fetch(`http://localhost:${port}/hooks/agent`, {
12222
12280
  method: "POST",
12223
12281
  headers: {
@@ -12229,7 +12287,7 @@ async function injectNotification(port, hooksToken, n, agentId = "main") {
12229
12287
  name: "Cohort",
12230
12288
  agentId,
12231
12289
  deliver: false,
12232
- sessionKey: `hook:cohort:task-${n.taskNumber}`
12290
+ sessionKey
12233
12291
  }),
12234
12292
  signal: AbortSignal.timeout(1e4)
12235
12293
  });
@@ -12317,7 +12375,8 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12317
12375
  try {
12318
12376
  if (hooksToken) {
12319
12377
  await injectNotification(port, hooksToken, n, openclawAgentId);
12320
- logger.info(`cohort-sync: injected notification for task #${n.taskNumber} (${agentName})`);
12378
+ const targetLabel = n.type === "room_message" ? `room ${n.roomId ?? n.roomName ?? "unknown"}` : `task #${n.taskNumber}`;
12379
+ logger.info(`cohort-sync: injected notification for ${targetLabel} (${agentName})`);
12321
12380
  } else {
12322
12381
  throw new Error(
12323
12382
  `no transport available for notification ${n._id} (gwClient alive: ${gwClient?.isAlive() ?? "null"}, hooksToken: ${!!hooksToken})`
@@ -12332,8 +12391,9 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12332
12391
  const newFailCount = failCount + 1;
12333
12392
  deliveryFailures.set(n._id, newFailCount);
12334
12393
  if (newFailCount >= MAX_DELIVERY_ATTEMPTS) {
12394
+ const targetLabel = n.type === "room_message" ? `room ${n.roomId ?? n.roomName ?? "unknown"}` : `task #${n.taskNumber}`;
12335
12395
  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)}`
12396
+ `cohort-sync: dead-letter notification ${n._id} for ${targetLabel} (${n.type} from ${n.actorName}) after ${MAX_DELIVERY_ATTEMPTS} failed delivery attempts: ${String(err)}`
12337
12397
  );
12338
12398
  try {
12339
12399
  await c.mutation(markDeliveredByPlugin, {
@@ -13741,7 +13801,7 @@ function dumpEvent(event) {
13741
13801
  function positiveNumber(value) {
13742
13802
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
13743
13803
  }
13744
- var PLUGIN_VERSION = true ? "0.31.11" : "unknown";
13804
+ var PLUGIN_VERSION = true ? "0.31.13" : "unknown";
13745
13805
  function resolveGatewayToken(api) {
13746
13806
  const token2 = api.config?.gateway?.auth?.token;
13747
13807
  return typeof token2 === "string" ? token2 : null;
@@ -14871,6 +14931,87 @@ Do not attempt more comments until tomorrow.`);
14871
14931
  }
14872
14932
  };
14873
14933
  });
14934
+ api.registerTool((toolCtx) => {
14935
+ const agentId = toolCtx.agentId ?? "main";
14936
+ return {
14937
+ name: "cohort_room_message",
14938
+ label: "cohort_room_message",
14939
+ description: "Post a message in a Cohort Room. Use this to reply when you are participating in a Room chat.",
14940
+ parameters: Type.Object({
14941
+ room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
14942
+ message: Type.String({ description: "Message text to post in the Room" })
14943
+ }),
14944
+ async execute(_toolCallId, params) {
14945
+ const rt = getToolRuntime();
14946
+ if (!rt.isReady) {
14947
+ return textResult("cohort_room_message is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
14948
+ }
14949
+ const agentName = rt.resolveAgentName(agentId);
14950
+ const result = await callAddRoomMessageFromPlugin(rt.apiKey, {
14951
+ roomId: params.room_id,
14952
+ agentName,
14953
+ content: params.message
14954
+ });
14955
+ return textResult(`Message posted to Room ${params.room_id}.`, result);
14956
+ }
14957
+ };
14958
+ });
14959
+ api.registerTool((toolCtx) => {
14960
+ const agentId = toolCtx.agentId ?? "main";
14961
+ return {
14962
+ name: "cohort_room_prompt_agent",
14963
+ label: "cohort_room_prompt_agent",
14964
+ description: "Ask one agent in a Cohort Room to respond. Use this when you are the Room moderator and need controlled turn-taking.",
14965
+ parameters: Type.Object({
14966
+ room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
14967
+ agent_name: Type.String({ description: "Cohort agent name to prompt, e.g. iris" }),
14968
+ prompt: Type.String({ description: "Prompt to send to the target agent" })
14969
+ }),
14970
+ async execute(_toolCallId, params) {
14971
+ const rt = getToolRuntime();
14972
+ if (!rt.isReady) {
14973
+ return textResult("cohort_room_prompt_agent is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
14974
+ }
14975
+ const moderatorAgentName = rt.resolveAgentName(agentId);
14976
+ const result = await callPromptRoomAgentsFromPlugin(rt.apiKey, {
14977
+ roomId: params.room_id,
14978
+ moderatorAgentName,
14979
+ targetAgentNames: [params.agent_name],
14980
+ prompt: params.prompt
14981
+ });
14982
+ const noun = result.notificationCount === 1 ? "agent" : "agents";
14983
+ return textResult(`Prompt sent to ${result.notificationCount} Room ${noun}.`, result);
14984
+ }
14985
+ };
14986
+ });
14987
+ api.registerTool((toolCtx) => {
14988
+ const agentId = toolCtx.agentId ?? "main";
14989
+ return {
14990
+ name: "cohort_room_prompt_agents",
14991
+ label: "cohort_room_prompt_agents",
14992
+ description: "Ask multiple agents in a Cohort Room to respond in one controlled moderator action.",
14993
+ parameters: Type.Object({
14994
+ room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
14995
+ agent_names: Type.Array(Type.String({ description: "Cohort agent name to prompt" })),
14996
+ prompt: Type.String({ description: "Prompt to send to the target agents" })
14997
+ }),
14998
+ async execute(_toolCallId, params) {
14999
+ const rt = getToolRuntime();
15000
+ if (!rt.isReady) {
15001
+ return textResult("cohort_room_prompt_agents is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
15002
+ }
15003
+ const moderatorAgentName = rt.resolveAgentName(agentId);
15004
+ const result = await callPromptRoomAgentsFromPlugin(rt.apiKey, {
15005
+ roomId: params.room_id,
15006
+ moderatorAgentName,
15007
+ targetAgentNames: params.agent_names,
15008
+ prompt: params.prompt
15009
+ });
15010
+ const noun = result.notificationCount === 1 ? "agent" : "agents";
15011
+ return textResult(`Prompt sent to ${result.notificationCount} Room ${noun}.`, result);
15012
+ }
15013
+ };
15014
+ });
14874
15015
  api.registerTool(() => {
14875
15016
  return {
14876
15017
  name: "cohort_context",
@@ -11,6 +11,9 @@
11
11
  "contracts": {
12
12
  "tools": [
13
13
  "cohort_comment",
14
+ "cohort_room_message",
15
+ "cohort_room_prompt_agent",
16
+ "cohort_room_prompt_agents",
14
17
  "cohort_context",
15
18
  "cohort_briefing_context",
16
19
  "cohort_briefing",
@@ -74,5 +77,5 @@
74
77
  }
75
78
  }
76
79
  },
77
- "version": "0.31.11"
80
+ "version": "0.31.13"
78
81
  }
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.13",
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,9 @@
9
9
  "contracts": {
10
10
  "tools": [
11
11
  "cohort_comment",
12
+ "cohort_room_message",
13
+ "cohort_room_prompt_agent",
14
+ "cohort_room_prompt_agents",
12
15
  "cohort_context",
13
16
  "cohort_briefing_context",
14
17
  "cohort_briefing",
@@ -72,5 +75,5 @@
72
75
  }
73
76
  }
74
77
  },
75
- "version": "0.9.2"
78
+ "version": "0.9.3"
76
79
  }
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.13",
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"