@cfio/cohort-sync 0.31.12 → 0.32.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
@@ -12000,6 +12000,10 @@ var failCommandRef = makeFunctionReference("gatewayCommands:failCommand");
12000
12000
  var getChannelsForPlugin = makeFunctionReference("cloudGatewayChannels:listForPlugin");
12001
12001
  var addCommentFromPluginRef = makeFunctionReference("comments:addCommentFromPlugin");
12002
12002
  var addRoomMessageFromPluginRef = makeFunctionReference("rooms:addMessageFromPlugin");
12003
+ var startModerationSessionFromPluginRef = makeFunctionReference("moderationSessions:startFromPlugin");
12004
+ var advanceModerationSessionFromPluginRef = makeFunctionReference("moderationSessions:advanceFromPlugin");
12005
+ var cancelModerationSessionFromPluginRef = makeFunctionReference("moderationSessions:cancelFromPlugin");
12006
+ var promptAgentsFromPluginRef = makeFunctionReference("rooms:promptAgentsFromPlugin");
12003
12007
  var transitionFromPluginRef = makeFunctionReference("tasks:transitionFromPlugin");
12004
12008
  async function pushTelemetry(apiKey2, data) {
12005
12009
  if (authCircuitOpen) return;
@@ -12118,7 +12122,116 @@ async function callAddRoomMessageFromPlugin(apiKey2, args) {
12118
12122
  apiKeyHash: hashApiKey(apiKey2),
12119
12123
  roomId: args.roomId,
12120
12124
  agentName: args.agentName,
12121
- body: args.content
12125
+ body: args.content,
12126
+ // Omit (not undefined) when absent — keeps the wire args clean.
12127
+ ...args.turnId !== void 0 ? { turnId: args.turnId } : {}
12128
+ });
12129
+ } catch (err) {
12130
+ if (isUnauthorizedError(err)) {
12131
+ tripAuthCircuit();
12132
+ }
12133
+ throw err;
12134
+ }
12135
+ }
12136
+ async function callStartModerationSessionFromPlugin(apiKey2, args) {
12137
+ if (authCircuitOpen) {
12138
+ throw new Error(
12139
+ '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_..."'
12140
+ );
12141
+ }
12142
+ const c = getClient();
12143
+ if (!c) {
12144
+ throw new Error("Convex client not initialized \u2014 subscription may not be active");
12145
+ }
12146
+ try {
12147
+ return await c.mutation(startModerationSessionFromPluginRef, {
12148
+ apiKeyHash: hashApiKey(apiKey2),
12149
+ roomId: args.roomId,
12150
+ moderatorAgentName: args.moderatorAgentName,
12151
+ mode: args.mode,
12152
+ objective: args.objective,
12153
+ participantAgentNames: args.participantAgentNames,
12154
+ ...args.questionText !== void 0 ? { questionText: args.questionText } : {},
12155
+ ...args.autoAdvance !== void 0 ? { autoAdvance: args.autoAdvance } : {}
12156
+ });
12157
+ } catch (err) {
12158
+ if (isUnauthorizedError(err)) {
12159
+ tripAuthCircuit();
12160
+ }
12161
+ throw err;
12162
+ }
12163
+ }
12164
+ async function callAdvanceModerationSessionFromPlugin(apiKey2, args) {
12165
+ if (authCircuitOpen) {
12166
+ throw new Error(
12167
+ '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_..."'
12168
+ );
12169
+ }
12170
+ const c = getClient();
12171
+ if (!c) {
12172
+ throw new Error("Convex client not initialized \u2014 subscription may not be active");
12173
+ }
12174
+ try {
12175
+ return await c.mutation(advanceModerationSessionFromPluginRef, {
12176
+ apiKeyHash: hashApiKey(apiKey2),
12177
+ sessionId: args.sessionId,
12178
+ moderatorAgentName: args.moderatorAgentName,
12179
+ action: args.action,
12180
+ ...args.prompt !== void 0 ? { prompt: args.prompt } : {},
12181
+ ...args.targetAgentName !== void 0 ? { targetAgentName: args.targetAgentName } : {},
12182
+ ...args.summary !== void 0 ? { summary: args.summary } : {},
12183
+ ...args.decisions !== void 0 ? { decisions: args.decisions } : {},
12184
+ ...args.proposedTasks !== void 0 ? { proposedTasks: args.proposedTasks } : {},
12185
+ ...args.followUps !== void 0 ? { followUps: args.followUps } : {}
12186
+ });
12187
+ } catch (err) {
12188
+ if (isUnauthorizedError(err)) {
12189
+ tripAuthCircuit();
12190
+ }
12191
+ throw err;
12192
+ }
12193
+ }
12194
+ async function callCancelModerationSessionFromPlugin(apiKey2, args) {
12195
+ if (authCircuitOpen) {
12196
+ throw new Error(
12197
+ '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_..."'
12198
+ );
12199
+ }
12200
+ const c = getClient();
12201
+ if (!c) {
12202
+ throw new Error("Convex client not initialized \u2014 subscription may not be active");
12203
+ }
12204
+ try {
12205
+ return await c.mutation(cancelModerationSessionFromPluginRef, {
12206
+ apiKeyHash: hashApiKey(apiKey2),
12207
+ sessionId: args.sessionId,
12208
+ moderatorAgentName: args.moderatorAgentName,
12209
+ ...args.reason !== void 0 ? { reason: args.reason } : {}
12210
+ });
12211
+ } catch (err) {
12212
+ if (isUnauthorizedError(err)) {
12213
+ tripAuthCircuit();
12214
+ }
12215
+ throw err;
12216
+ }
12217
+ }
12218
+ async function callPromptRoomAgentsFromPlugin(apiKey2, args) {
12219
+ if (authCircuitOpen) {
12220
+ throw new Error(
12221
+ '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_..."'
12222
+ );
12223
+ }
12224
+ const c = getClient();
12225
+ if (!c) {
12226
+ throw new Error("Convex client not initialized \u2014 subscription may not be active");
12227
+ }
12228
+ try {
12229
+ return await c.mutation(promptAgentsFromPluginRef, {
12230
+ apiKeyHash: hashApiKey(apiKey2),
12231
+ roomId: args.roomId,
12232
+ moderatorAgentName: args.moderatorAgentName,
12233
+ targetAgentNames: args.targetAgentNames,
12234
+ prompt: args.prompt
12122
12235
  });
12123
12236
  } catch (err) {
12124
12237
  if (isUnauthorizedError(err)) {
@@ -12164,17 +12277,113 @@ var ATMENTION_RESPONSE_PROMPT = `YOU WERE DIRECTLY @-MENTIONED. RESPOND.
12164
12277
  - If the mention is a question, answer it. If it's a request, acknowledge what you'll do (and then do it).
12165
12278
  - For coding requests, do not stop at local implementation; source scripts/agent-dev-env.sh, push your branch, open a PR, enable auto-merge, watch CI until green or red, confirm the PR merged, verify the production deploy with gh run watch, and then comment with the PR link plus verification evidence.
12166
12279
  - Do not stop because Vercel CLI is not authenticated; Cohort deploys through GitHub Actions after merge.`;
12280
+ var ROOM_MESSAGE_RESPONSE_PROMPT = `YOU WERE DIRECTLY ASKED IN A COHORT ROOM. RESPOND.
12281
+
12282
+ - If you are the Room moderator and a human asks for a roundtable, standup, report-in, or panel response, FIRST use cohort_room_start_moderation_session(room_id, mode, objective, participant_names) to run it as a managed session, then drive it with cohort_room_advance_moderation_session; do not only name agents in prose.
12283
+ - Otherwise, use the cohort_room_message tool to post a reply in the Room. Do NOT just think silently and exit.
12284
+ - Reply in your own voice (see your persona in IDENTITY.md).
12285
+ - If the human asks for a roundtable, standup, report-in, or panel response, contribute your own concise update.
12286
+ - If the moderator asks you to answer, answer. If another agent asks for your report, provide it. If your prompt includes a turn_id, echo it in cohort_room_message.
12287
+ - A brief, honest reply is better than no reply. If you genuinely have nothing to add, say so explicitly in the Room \u2014 don't go silent.`;
12167
12288
  var TOOLS_REFERENCE = `
12168
12289
  TOOLS: Use these \u2014 do NOT call the REST API directly.
12169
12290
  - cohort_comment(task_number, comment) \u2014 post a comment
12170
- - cohort_room_message(room_id, message) \u2014 post a message in a Cohort Room
12291
+ - cohort_room_message(room_id, message, turn_id?) \u2014 post a message in a Cohort Room; pass turn_id when your prompt included one so the reply is attributed to that moderation turn
12292
+ - cohort_room_start_moderation_session(room_id, mode, objective, participant_names) \u2014 moderator only: start a managed round robin or Q&A session
12293
+ - cohort_room_advance_moderation_session(session_id, action, ...) \u2014 moderator only: advance, retry, skip, accept, followup, or complete the session
12294
+ - cohort_room_cancel_moderation_session(session_id, reason?) \u2014 moderator only: cancel an in-flight session
12295
+ - cohort_room_prompt_agent(roomId, agentName, prompt) \u2014 moderator only, low-level: ask one Room agent to respond (prefer moderation sessions for multi-step workflows)
12296
+ - cohort_room_prompt_agents(roomId, agentNames, prompt) \u2014 moderator only, low-level: ask multiple Room agents to respond (prefer moderation sessions for multi-step workflows)
12171
12297
  - cohort_task(task_number) \u2014 fetch full task details + comments
12172
12298
  - cohort_transition(task_number, status) \u2014 change status
12173
12299
  - cohort_assign(task_number, assignee) \u2014 assign/unassign
12174
12300
  - cohort_context() \u2014 get your session briefing`;
12301
+ var HANDOFF_REQUEST_TERMS = [
12302
+ "round robin",
12303
+ "round-robin",
12304
+ "go around",
12305
+ "around the table",
12306
+ "whole team",
12307
+ "everyone",
12308
+ "all hands",
12309
+ "pass the buck",
12310
+ "pass the conch"
12311
+ ];
12312
+ var HANDOFF_TOPIC_TERMS = [
12313
+ "standup",
12314
+ "stand-up",
12315
+ "report in",
12316
+ "report-in",
12317
+ "panel"
12318
+ ];
12319
+ var HANDOFF_ACTION_TERMS = [
12320
+ "moderate",
12321
+ "run",
12322
+ "gather",
12323
+ "ask"
12324
+ ];
12175
12325
  function sanitizePreview(raw) {
12176
12326
  return raw.replace(/<\/?user_comment>/gi, "");
12177
12327
  }
12328
+ function isHumanHandoffRequest(n) {
12329
+ if (n.actorType !== "human") {
12330
+ return false;
12331
+ }
12332
+ const preview = (n.preview ?? "").toLowerCase();
12333
+ if (HANDOFF_REQUEST_TERMS.some((term) => preview.includes(term))) {
12334
+ return true;
12335
+ }
12336
+ const hasTopic = HANDOFF_TOPIC_TERMS.some((term) => preview.includes(term));
12337
+ const hasAction = HANDOFF_ACTION_TERMS.some((term) => preview.includes(term));
12338
+ return hasTopic && hasAction;
12339
+ }
12340
+ function roomMessageCta(n) {
12341
+ const roomId = n.roomId ?? "unknown";
12342
+ if (n.moderationContext != null && renderModerationContext(n.moderationContext) !== "") {
12343
+ return "Follow the MODERATION SESSION block in this message \u2014 it is authoritative.";
12344
+ }
12345
+ if (isHumanHandoffRequest(n)) {
12346
+ return `You are moderating a Room round robin. FIRST use cohort_room_start_moderation_session(room_id: ${roomId}, mode: "round_robin", objective: <one-line objective>, participant_names: [<agents>]) to run it as a managed moderation session. Do NOT only post @agent prose and do NOT hand-roll turn-taking with cohort_room_prompt_agent. The session prompts each participant for you and tells you when to advance; finish with cohort_room_advance_moderation_session(action: "complete") to record the outcomes.`;
12347
+ }
12348
+ if (isAgentReplyToModerator(n)) {
12349
+ const actor = n.actorName ?? "the agent";
12350
+ return `If you are the Room moderator continuing a round robin, The <user_comment> preview is the agent's actual Room reply from ${actor}. Check whether the From: agent is the expected participant. If correct and the round robin has more participants, FIRST call cohort_room_prompt_agent for the next participant. If wrong, prompt the correct agent. Only consolidate when the requested sequence is complete. Do NOT stop after posting a commentary message.`;
12351
+ }
12352
+ return `Reply in the Room if you can help. Use the cohort_room_message tool (roomId: ${roomId}).`;
12353
+ }
12354
+ function isAgentReplyToModerator(n) {
12355
+ if (n.actorType !== "agent") {
12356
+ return false;
12357
+ }
12358
+ const actor = (n.actorName ?? "").trim().toLowerCase();
12359
+ const recipient = (n.recipientAgent ?? "").trim().toLowerCase();
12360
+ const preview = (n.preview ?? "").toLowerCase();
12361
+ if (["please ", "passing the buck to you", "pass the buck to you"].some((term) => preview.includes(term))) {
12362
+ return false;
12363
+ }
12364
+ return Boolean(actor && recipient && actor !== recipient);
12365
+ }
12366
+ function renderModerationContext(ctx) {
12367
+ if (ctx.version !== 1) return "";
12368
+ const expected = ctx.expectedAgentNames.length > 0 ? ctx.expectedAgentNames.join(", ") : "(none)";
12369
+ const lines = [
12370
+ `[MODERATION SESSION ${ctx.sessionId}]`,
12371
+ `Mode: ${ctx.mode} | Status: ${ctx.status} | Auto-advance: ${ctx.autoAdvance ? "on" : "off"} | Queued behind: ${ctx.queuedCount}`,
12372
+ `Objective: ${ctx.objective}`,
12373
+ `Sequence: ${ctx.sequenceAgentNames.join(" -> ")} (cursor ${ctx.cursor}/${ctx.sequenceAgentNames.length})`,
12374
+ `Expected responder(s): ${expected}`,
12375
+ `Last event: ${ctx.lastEvent}`,
12376
+ `NEXT ACTION: ${ctx.nextActionHint}`,
12377
+ "Do NOT post commentary instead of advancing. Advance or complete via the session tools."
12378
+ ];
12379
+ if (ctx.respondTurnId) {
12380
+ lines.push(
12381
+ `To reply for this turn, call cohort_room_message(room_id: ${ctx.roomId}, message: <your reply>, turn_id: "${ctx.respondTurnId}").`
12382
+ );
12383
+ }
12384
+ lines.push("[/MODERATION SESSION]");
12385
+ return lines.join("\n");
12386
+ }
12178
12387
  function buildNotificationMessage(n) {
12179
12388
  let header;
12180
12389
  let cta;
@@ -12182,7 +12391,7 @@ function buildNotificationMessage(n) {
12182
12391
  case "room_message":
12183
12392
  header = `New message in Room "${n.roomName ?? n.roomId ?? "Room"}"
12184
12393
  From: ${n.actorName}`;
12185
- cta = `Reply in the Room if you can help. Use the cohort_room_message tool (roomId: ${n.roomId ?? "unknown"}).`;
12394
+ cta = roomMessageCta(n);
12186
12395
  break;
12187
12396
  case "comment":
12188
12397
  if (n.isMentioned) {
@@ -12231,7 +12440,13 @@ Comment: "${n.preview}"`;
12231
12440
  Scope: ${truncated}`;
12232
12441
  }
12233
12442
  let prompt;
12234
- if (n.type === "comment" && n.isMentioned || n.type === "room_message") {
12443
+ if (n.type === "room_message") {
12444
+ const moderationBlock = n.moderationContext != null ? renderModerationContext(n.moderationContext) : "";
12445
+ const preamble = moderationBlock || n.behavioralPrompt;
12446
+ prompt = preamble ? `${preamble}
12447
+
12448
+ ${ROOM_MESSAGE_RESPONSE_PROMPT}` : ROOM_MESSAGE_RESPONSE_PROMPT;
12449
+ } else if (n.type === "comment" && n.isMentioned) {
12235
12450
  prompt = n.behavioralPrompt ? `${n.behavioralPrompt}
12236
12451
 
12237
12452
  ${ATMENTION_RESPONSE_PROMPT}` : ATMENTION_RESPONSE_PROMPT;
@@ -13775,7 +13990,7 @@ function dumpEvent(event) {
13775
13990
  function positiveNumber(value) {
13776
13991
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
13777
13992
  }
13778
- var PLUGIN_VERSION = true ? "0.31.12" : "unknown";
13993
+ var PLUGIN_VERSION = true ? "0.32.0" : "unknown";
13779
13994
  function resolveGatewayToken(api) {
13780
13995
  const token2 = api.config?.gateway?.auth?.token;
13781
13996
  return typeof token2 === "string" ? token2 : null;
@@ -14913,7 +15128,8 @@ Do not attempt more comments until tomorrow.`);
14913
15128
  description: "Post a message in a Cohort Room. Use this to reply when you are participating in a Room chat.",
14914
15129
  parameters: Type.Object({
14915
15130
  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" })
15131
+ message: Type.String({ description: "Message text to post in the Room" }),
15132
+ turn_id: Type.Optional(Type.String({ description: "Moderation turn id to attribute this reply to (echo from your prompt)" }))
14917
15133
  }),
14918
15134
  async execute(_toolCallId, params) {
14919
15135
  const rt = getToolRuntime();
@@ -14924,12 +15140,189 @@ Do not attempt more comments until tomorrow.`);
14924
15140
  const result = await callAddRoomMessageFromPlugin(rt.apiKey, {
14925
15141
  roomId: params.room_id,
14926
15142
  agentName,
14927
- content: params.message
15143
+ content: params.message,
15144
+ ...params.turn_id !== void 0 ? { turnId: params.turn_id } : {}
14928
15145
  });
14929
15146
  return textResult(`Message posted to Room ${params.room_id}.`, result);
14930
15147
  }
14931
15148
  };
14932
15149
  });
15150
+ api.registerTool((toolCtx) => {
15151
+ const agentId = toolCtx.agentId ?? "main";
15152
+ return {
15153
+ name: "cohort_room_prompt_agent",
15154
+ label: "cohort_room_prompt_agent",
15155
+ description: "Ask one agent in a Cohort Room to respond. Use this when you are the Room moderator and need controlled turn-taking.",
15156
+ parameters: Type.Object({
15157
+ room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
15158
+ agent_name: Type.String({ description: "Cohort agent name to prompt, e.g. iris" }),
15159
+ prompt: Type.String({ description: "Prompt to send to the target agent" })
15160
+ }),
15161
+ async execute(_toolCallId, params) {
15162
+ const rt = getToolRuntime();
15163
+ if (!rt.isReady) {
15164
+ return textResult("cohort_room_prompt_agent is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
15165
+ }
15166
+ const moderatorAgentName = rt.resolveAgentName(agentId);
15167
+ const result = await callPromptRoomAgentsFromPlugin(rt.apiKey, {
15168
+ roomId: params.room_id,
15169
+ moderatorAgentName,
15170
+ targetAgentNames: [params.agent_name],
15171
+ prompt: params.prompt
15172
+ });
15173
+ const noun = result.notificationCount === 1 ? "agent" : "agents";
15174
+ return textResult(`Prompt sent to ${result.notificationCount} Room ${noun}.`, result);
15175
+ }
15176
+ };
15177
+ });
15178
+ api.registerTool((toolCtx) => {
15179
+ const agentId = toolCtx.agentId ?? "main";
15180
+ return {
15181
+ name: "cohort_room_prompt_agents",
15182
+ label: "cohort_room_prompt_agents",
15183
+ description: "Ask multiple agents in a Cohort Room to respond in one controlled moderator action.",
15184
+ parameters: Type.Object({
15185
+ room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
15186
+ agent_names: Type.Array(Type.String({ description: "Cohort agent name to prompt" })),
15187
+ prompt: Type.String({ description: "Prompt to send to the target agents" })
15188
+ }),
15189
+ async execute(_toolCallId, params) {
15190
+ const rt = getToolRuntime();
15191
+ if (!rt.isReady) {
15192
+ return textResult("cohort_room_prompt_agents is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
15193
+ }
15194
+ const moderatorAgentName = rt.resolveAgentName(agentId);
15195
+ const result = await callPromptRoomAgentsFromPlugin(rt.apiKey, {
15196
+ roomId: params.room_id,
15197
+ moderatorAgentName,
15198
+ targetAgentNames: params.agent_names,
15199
+ prompt: params.prompt
15200
+ });
15201
+ const noun = result.notificationCount === 1 ? "agent" : "agents";
15202
+ return textResult(`Prompt sent to ${result.notificationCount} Room ${noun}.`, result);
15203
+ }
15204
+ };
15205
+ });
15206
+ api.registerTool((toolCtx) => {
15207
+ const agentId = toolCtx.agentId ?? "main";
15208
+ return {
15209
+ name: "cohort_room_start_moderation_session",
15210
+ label: "cohort_room_start_moderation_session",
15211
+ description: "Start a managed moderation session in a Cohort Room. This is THE way to run round robins and moderated Q&A as the Room moderator: the backend prompts each participant in turn, tracks who responded, and tells you when to advance. Returns the session state block. The returned block includes the session_id \u2014 pass it to cohort_room_advance_moderation_session for every subsequent action.",
15212
+ parameters: Type.Object({
15213
+ room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
15214
+ mode: Type.Union([
15215
+ Type.Literal("round_robin"),
15216
+ Type.Literal("moderated_qna")
15217
+ ], { description: 'Session mode: "round_robin" (each participant responds in turn) or "moderated_qna" (one participant answers a question)' }),
15218
+ objective: Type.String({ description: "One-line objective for the session, e.g. 'Daily standup: blockers and progress'" }),
15219
+ participant_names: Type.Array(Type.String(), { description: "Cohort agent names to include, in speaking order" }),
15220
+ question_text: Type.Optional(Type.String({ description: "moderated_qna only: the question to ask the participant" })),
15221
+ auto_advance: Type.Optional(Type.Boolean({ description: "round_robin only: automatically prompt the next participant after each matched response" }))
15222
+ }),
15223
+ async execute(_toolCallId, params) {
15224
+ const rt = getToolRuntime();
15225
+ if (!rt.isReady) {
15226
+ return textResult("cohort_room_start_moderation_session is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
15227
+ }
15228
+ const moderatorAgentName = rt.resolveAgentName(agentId);
15229
+ try {
15230
+ const result = await callStartModerationSessionFromPlugin(rt.apiKey, {
15231
+ roomId: params.room_id,
15232
+ moderatorAgentName,
15233
+ mode: params.mode,
15234
+ objective: params.objective,
15235
+ participantAgentNames: params.participant_names,
15236
+ ...params.question_text !== void 0 ? { questionText: params.question_text } : {},
15237
+ ...params.auto_advance !== void 0 ? { autoAdvance: params.auto_advance } : {}
15238
+ });
15239
+ return textResult(result.stateBlock, result);
15240
+ } catch (err) {
15241
+ const msg = getConvexAppErrorMessage(err) ?? (err instanceof Error ? err.message : String(err));
15242
+ return textResult(`Failed to start moderation session: ${msg}`);
15243
+ }
15244
+ }
15245
+ };
15246
+ });
15247
+ api.registerTool((toolCtx) => {
15248
+ const agentId = toolCtx.agentId ?? "main";
15249
+ return {
15250
+ name: "cohort_room_advance_moderation_session",
15251
+ label: "cohort_room_advance_moderation_session",
15252
+ description: 'Advance a moderation session you are running: "next" prompts the next participant, "retry"/"skip"/"accept" resolve a mismatched turn, "followup" asks a target a follow-up question, and "complete" ends the session. Outcomes passed with action "complete" (summary, decisions, proposed_tasks, follow_ups) become the session recap; proposed_tasks surface for human approval. Returns the updated session state block.',
15253
+ parameters: Type.Object({
15254
+ session_id: Type.String({ description: "Moderation session ID returned by cohort_room_start_moderation_session" }),
15255
+ action: Type.Union([
15256
+ Type.Literal("next"),
15257
+ Type.Literal("retry"),
15258
+ Type.Literal("skip"),
15259
+ Type.Literal("accept"),
15260
+ Type.Literal("followup"),
15261
+ Type.Literal("complete")
15262
+ ], { description: "next = prompt the next participant (from waiting); retry = re-prompt the same agent; skip = skip the expected agent and move on; accept = accept an out-of-order reply as the response; followup = route a follow-up question (Q&A only); complete = finish the session (pass summary/decisions/proposed_tasks to create the recap)." }),
15263
+ prompt: Type.Optional(Type.String({ description: "Custom prompt for the turn (next/retry/followup)" })),
15264
+ target_agent_name: Type.Optional(Type.String({ description: "followup only: the agent to ask" })),
15265
+ summary: Type.Optional(Type.String({ description: "complete only: session recap summary" })),
15266
+ decisions: Type.Optional(Type.Array(Type.String(), { description: "complete only: decisions reached" })),
15267
+ proposed_tasks: Type.Optional(Type.Array(Type.String(), { description: "complete only: proposed tasks for human approval" })),
15268
+ follow_ups: Type.Optional(Type.Array(Type.String(), { description: "complete only: follow-up items" }))
15269
+ }),
15270
+ async execute(_toolCallId, params) {
15271
+ const rt = getToolRuntime();
15272
+ if (!rt.isReady) {
15273
+ return textResult("cohort_room_advance_moderation_session is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
15274
+ }
15275
+ const moderatorAgentName = rt.resolveAgentName(agentId);
15276
+ try {
15277
+ const result = await callAdvanceModerationSessionFromPlugin(rt.apiKey, {
15278
+ sessionId: params.session_id,
15279
+ moderatorAgentName,
15280
+ action: params.action,
15281
+ ...params.prompt !== void 0 ? { prompt: params.prompt } : {},
15282
+ ...params.target_agent_name !== void 0 ? { targetAgentName: params.target_agent_name } : {},
15283
+ ...params.summary !== void 0 ? { summary: params.summary } : {},
15284
+ ...params.decisions !== void 0 ? { decisions: params.decisions } : {},
15285
+ ...params.proposed_tasks !== void 0 ? { proposedTasks: params.proposed_tasks } : {},
15286
+ ...params.follow_ups !== void 0 ? { followUps: params.follow_ups } : {}
15287
+ });
15288
+ return textResult(result.stateBlock, result);
15289
+ } catch (err) {
15290
+ const msg = getConvexAppErrorMessage(err) ?? (err instanceof Error ? err.message : String(err));
15291
+ return textResult(`Failed to advance moderation session: ${msg}`);
15292
+ }
15293
+ }
15294
+ };
15295
+ });
15296
+ api.registerTool((toolCtx) => {
15297
+ const agentId = toolCtx.agentId ?? "main";
15298
+ return {
15299
+ name: "cohort_room_cancel_moderation_session",
15300
+ label: "cohort_room_cancel_moderation_session",
15301
+ description: "Cancel a moderation session you are running (e.g. superseded, no longer needed). Only valid while the session is queued, active, or waiting \u2014 completed/failed sessions cannot be canceled. Returns the final session state block.",
15302
+ parameters: Type.Object({
15303
+ session_id: Type.String({ description: "Moderation session ID to cancel" }),
15304
+ reason: Type.Optional(Type.String({ description: "Short reason for canceling" }))
15305
+ }),
15306
+ async execute(_toolCallId, params) {
15307
+ const rt = getToolRuntime();
15308
+ if (!rt.isReady) {
15309
+ return textResult("cohort_room_cancel_moderation_session is not ready yet \u2014 the plugin is still starting up. Try again in a few seconds.");
15310
+ }
15311
+ const moderatorAgentName = rt.resolveAgentName(agentId);
15312
+ try {
15313
+ const result = await callCancelModerationSessionFromPlugin(rt.apiKey, {
15314
+ sessionId: params.session_id,
15315
+ moderatorAgentName,
15316
+ ...params.reason !== void 0 ? { reason: params.reason } : {}
15317
+ });
15318
+ return textResult(result.stateBlock, result);
15319
+ } catch (err) {
15320
+ const msg = getConvexAppErrorMessage(err) ?? (err instanceof Error ? err.message : String(err));
15321
+ return textResult(`Failed to cancel moderation session: ${msg}`);
15322
+ }
15323
+ }
15324
+ };
15325
+ });
14933
15326
  api.registerTool(() => {
14934
15327
  return {
14935
15328
  name: "cohort_context",
@@ -12,6 +12,11 @@
12
12
  "tools": [
13
13
  "cohort_comment",
14
14
  "cohort_room_message",
15
+ "cohort_room_prompt_agent",
16
+ "cohort_room_prompt_agents",
17
+ "cohort_room_start_moderation_session",
18
+ "cohort_room_advance_moderation_session",
19
+ "cohort_room_cancel_moderation_session",
15
20
  "cohort_context",
16
21
  "cohort_briefing_context",
17
22
  "cohort_briefing",
@@ -75,5 +80,5 @@
75
80
  }
76
81
  }
77
82
  },
78
- "version": "0.31.12"
83
+ "version": "0.32.0"
79
84
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.31.12",
3
+ "version": "0.32.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",
@@ -10,6 +10,11 @@
10
10
  "tools": [
11
11
  "cohort_comment",
12
12
  "cohort_room_message",
13
+ "cohort_room_prompt_agent",
14
+ "cohort_room_prompt_agents",
15
+ "cohort_room_start_moderation_session",
16
+ "cohort_room_advance_moderation_session",
17
+ "cohort_room_cancel_moderation_session",
13
18
  "cohort_context",
14
19
  "cohort_briefing_context",
15
20
  "cohort_briefing",
@@ -73,5 +78,5 @@
73
78
  }
74
79
  }
75
80
  },
76
- "version": "0.9.2"
81
+ "version": "0.9.3"
77
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.31.12",
3
+ "version": "0.32.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",