@cfio/cohort-sync 0.31.7 → 0.31.9

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
@@ -11741,7 +11741,7 @@ function checkRateLimit() {
11741
11741
  return true;
11742
11742
  }
11743
11743
  var MAX_CRON_MESSAGE_LENGTH = 1e3;
11744
- async function executeCommand(cmd, gwClient, cfg, resolveAgentName, logger, cronTimestampTracker) {
11744
+ async function executeCommand(cmd, gwClient, cfg, resolveAgentName, logger, cronTimestampTracker, injection) {
11745
11745
  logger.info(`cohort-sync: executing command type=${cmd.type} id=${cmd._id}`);
11746
11746
  if (!checkRateLimit()) {
11747
11747
  logger.warn(`cohort-sync: rate limit exceeded (>${RATE_LIMIT_MAX} commands/min), rejecting command ${cmd._id}`);
@@ -11764,6 +11764,23 @@ async function executeCommand(cmd, gwClient, cfg, resolveAgentName, logger, cron
11764
11764
  process.kill(process.pid, "SIGUSR1");
11765
11765
  return;
11766
11766
  }
11767
+ if (cmd.type === "briefingGenerate") {
11768
+ const requestId = cmd.payload?.briefingRequestId;
11769
+ if (!requestId) {
11770
+ throw new Error("briefingRequestId is required for briefingGenerate");
11771
+ }
11772
+ if (!injection?.port || !injection.hooksToken) {
11773
+ throw new Error("Gateway hooks transport is not available for briefing generation");
11774
+ }
11775
+ const agentName = cmd.payload?.agentId ?? "main";
11776
+ const agentId = reverseResolveAgentName(agentName, cfg.agentNameMap ?? {});
11777
+ await injectBriefingGeneration(injection.port, injection.hooksToken, {
11778
+ requestId,
11779
+ agentId,
11780
+ agentName
11781
+ });
11782
+ return;
11783
+ }
11767
11784
  if (cmd.type.startsWith("cron")) {
11768
11785
  if (!gwClient || !gwClient.isAlive()) {
11769
11786
  logger.warn(`cohort-sync: no gateway client, cannot execute ${cmd.type}`);
@@ -12209,6 +12226,46 @@ async function injectNotification(port, hooksToken, n, agentId = "main") {
12209
12226
  throw new Error(`/hooks/agent returned ${response.status} ${response.statusText}`);
12210
12227
  }
12211
12228
  }
12229
+ function buildBriefingGenerationMessage(args) {
12230
+ return `Generate the Cohort Home briefing requested for ${args.agentName}.
12231
+
12232
+ Use your own voice and persona from SOUL.md, IDENTITY.md, and the rest of your agent instructions. First call cohort_briefing_context with request_id "${args.requestId}" to read the prepared workspace context. Then write the final briefing and submit it with cohort_briefing.
12233
+
12234
+ Final briefing requirements:
12235
+ - Write exactly two short paragraphs.
12236
+ - No bullet points or numbered lists.
12237
+ - Do not mention "this briefing".
12238
+ - Avoid colon-heavy phrasing.
12239
+ - Include one concrete high-leverage thing the reader should direct attention to next.
12240
+ - Keep it useful, personal, and alive without sounding theatrical.
12241
+
12242
+ TOOLS:
12243
+ - cohort_briefing_context(request_id) \u2014 fetch the prepared briefing context.
12244
+ - cohort_briefing(request_id, content) \u2014 submit the final Home briefing.`;
12245
+ }
12246
+ async function injectBriefingGeneration(port, hooksToken, args) {
12247
+ const response = await fetch(`http://localhost:${port}/hooks/agent`, {
12248
+ method: "POST",
12249
+ headers: {
12250
+ "Content-Type": "application/json",
12251
+ "Authorization": `Bearer ${hooksToken}`
12252
+ },
12253
+ body: JSON.stringify({
12254
+ message: buildBriefingGenerationMessage({
12255
+ requestId: args.requestId,
12256
+ agentName: args.agentName
12257
+ }),
12258
+ name: "Cohort",
12259
+ agentId: args.agentId,
12260
+ deliver: false,
12261
+ sessionKey: args.agentId === "main" ? `hook:cohort:briefing-${args.requestId}` : `agent:${args.agentId}:hook:cohort:briefing-${args.requestId}`
12262
+ }),
12263
+ signal: AbortSignal.timeout(1e4)
12264
+ });
12265
+ if (!response.ok) {
12266
+ throw new Error(`/hooks/agent returned ${response.status} ${response.statusText}`);
12267
+ }
12268
+ }
12212
12269
  var deliveryFailures = /* @__PURE__ */ new Map();
12213
12270
  var MAX_DELIVERY_ATTEMPTS = 3;
12214
12271
  async function startNotificationSubscription(port, cfg, hooksToken, logger, gwClient, resolvedNameMap) {
@@ -12298,7 +12355,7 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12298
12355
  unsubscribers.push(unsubscribe);
12299
12356
  }
12300
12357
  }
12301
- function startCommandSubscription(cfg, logger, resolveAgentName, gwClient, cronTimestampTracker) {
12358
+ function startCommandSubscription(cfg, logger, resolveAgentName, gwClient, cronTimestampTracker, injection) {
12302
12359
  const c = getClient();
12303
12360
  if (!c) {
12304
12361
  logger.warn("cohort-sync: no ConvexClient \u2014 command subscription skipped");
@@ -12321,7 +12378,15 @@ function startCommandSubscription(cfg, logger, resolveAgentName, gwClient, cronT
12321
12378
  commandId: cmd._id,
12322
12379
  apiKeyHash
12323
12380
  });
12324
- await executeCommand(cmd, gwClient, cfg, resolveAgentName, logger, cronTimestampTracker);
12381
+ await executeCommand(
12382
+ cmd,
12383
+ gwClient,
12384
+ cfg,
12385
+ resolveAgentName,
12386
+ logger,
12387
+ cronTimestampTracker,
12388
+ injection
12389
+ );
12325
12390
  if (cmd.type === "restart") return;
12326
12391
  } catch (err) {
12327
12392
  if (isUnauthorizedError(err)) {
@@ -13665,11 +13730,15 @@ function dumpEvent(event) {
13665
13730
  function positiveNumber(value) {
13666
13731
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
13667
13732
  }
13668
- var PLUGIN_VERSION = true ? "0.31.7" : "unknown";
13733
+ var PLUGIN_VERSION = true ? "0.31.9" : "unknown";
13669
13734
  function resolveGatewayToken(api) {
13670
13735
  const token2 = api.config?.gateway?.auth?.token;
13671
13736
  return typeof token2 === "string" ? token2 : null;
13672
13737
  }
13738
+ function resolveHooksToken(api) {
13739
+ const token2 = api.config?.hooks?.token;
13740
+ return typeof token2 === "string" ? token2 : null;
13741
+ }
13673
13742
  function registerCronEventHandlers(client2, cfg, resolveAgentName, cronTimestampTracker, logger) {
13674
13743
  if (client2.availableEvents.has("cron")) {
13675
13744
  let debounceTimer = null;
@@ -13853,6 +13922,7 @@ async function handleGatewayStart(event, state) {
13853
13922
  logger.debug("cohort-sync: gateway start: bridge after seed", { bridge: Object.fromEntries(getChannelAgentBridge()) });
13854
13923
  state.gatewayPort = event.port;
13855
13924
  const token2 = resolveGatewayToken(api);
13925
+ const hooksToken = resolveHooksToken(api);
13856
13926
  if (token2) {
13857
13927
  state.gatewayToken = token2;
13858
13928
  logger.debug("cohort-sync: gateway client connecting", { port: event.port, hasToken: true });
@@ -13864,7 +13934,14 @@ async function handleGatewayStart(event, state) {
13864
13934
  state.commandUnsubscriber();
13865
13935
  state.commandUnsubscriber = null;
13866
13936
  }
13867
- const unsub = startCommandSubscription(cfg, logger, state.resolveAgentName, state.persistentGwClient, state.cronTimestampTracker);
13937
+ const unsub = startCommandSubscription(
13938
+ cfg,
13939
+ logger,
13940
+ state.resolveAgentName,
13941
+ state.persistentGwClient,
13942
+ state.cronTimestampTracker,
13943
+ { port: state.gatewayPort, hooksToken }
13944
+ );
13868
13945
  state.commandUnsubscriber = unsub;
13869
13946
  } catch (err) {
13870
13947
  logger.debug("cohort-sync: gateway client connect failed", { error: String(err) });
@@ -13916,7 +13993,7 @@ async function handleGatewayStart(event, state) {
13916
13993
  await startNotificationSubscription(
13917
13994
  event.port,
13918
13995
  cfg,
13919
- api.config.hooks?.token,
13996
+ hooksToken ?? void 0,
13920
13997
  logger,
13921
13998
  state.persistentGwClient,
13922
13999
  resolvedNameMap
@@ -14194,7 +14271,14 @@ function registerHookHandlers(api, logger, getState) {
14194
14271
  state.commandUnsubscriber();
14195
14272
  state.commandUnsubscriber = null;
14196
14273
  }
14197
- const unsub = startCommandSubscription(cfg, log, state.resolveAgentName, state.persistentGwClient, state.cronTimestampTracker);
14274
+ const unsub = startCommandSubscription(
14275
+ cfg,
14276
+ log,
14277
+ state.resolveAgentName,
14278
+ state.persistentGwClient,
14279
+ state.cronTimestampTracker,
14280
+ { port: state.gatewayPort, hooksToken: state.gatewayToken }
14281
+ );
14198
14282
  state.commandUnsubscriber = unsub;
14199
14283
  } catch (err) {
14200
14284
  log.debug("cohort-sync: gateway client lazy init failed", { error: String(err) });
@@ -14501,6 +14585,7 @@ function initializeHookState(api, cfg) {
14501
14585
  });
14502
14586
  const gatewayPort = api.config?.gateway?.port ?? null;
14503
14587
  const gatewayToken = resolveGatewayToken(api);
14588
+ const hooksToken = resolveHooksToken(api);
14504
14589
  const cronTimestampTracker = new CronTimestampTracker();
14505
14590
  const cronRunStarts = /* @__PURE__ */ new Map();
14506
14591
  let persistentGwClient = null;
@@ -14510,7 +14595,14 @@ function initializeHookState(api, cfg) {
14510
14595
  persistentGwClient = client2;
14511
14596
  gwClientInitialized = true;
14512
14597
  }
14513
- const commandUnsub = startCommandSubscription(cfg, logger, resolveAgentName, persistentGwClient, cronTimestampTracker);
14598
+ const commandUnsub = startCommandSubscription(
14599
+ cfg,
14600
+ logger,
14601
+ resolveAgentName,
14602
+ persistentGwClient,
14603
+ cronTimestampTracker,
14604
+ { port: gatewayPort, hooksToken }
14605
+ );
14514
14606
  setToolRuntime({
14515
14607
  apiKey: cfg.apiKey,
14516
14608
  apiUrl: cfg.apiUrl,
@@ -14794,6 +14886,86 @@ Do not attempt more comments until tomorrow.`);
14794
14886
  }
14795
14887
  };
14796
14888
  });
14889
+ api.registerTool((toolCtx) => {
14890
+ const agentId = toolCtx.agentId ?? "main";
14891
+ return {
14892
+ name: "cohort_briefing_context",
14893
+ label: "cohort_briefing_context",
14894
+ description: "Fetch the prepared workspace context for a Cohort Home briefing request.",
14895
+ parameters: Type.Object({
14896
+ request_id: Type.String({ description: "Briefing request ID supplied by Cohort." })
14897
+ }),
14898
+ async execute(_toolCallId, params) {
14899
+ const rt = getToolRuntime();
14900
+ if (!rt.isReady) {
14901
+ return textResult("cohort_briefing_context is not ready yet \u2014 the plugin is still starting up.");
14902
+ }
14903
+ try {
14904
+ const response = await fetch(
14905
+ `${rt.apiUrl}/api/v1/briefings/${encodeURIComponent(params.request_id)}/context`,
14906
+ {
14907
+ method: "GET",
14908
+ headers: { "Authorization": `Bearer ${rt.apiKey}` },
14909
+ signal: AbortSignal.timeout(1e4)
14910
+ }
14911
+ );
14912
+ const body = await response.text();
14913
+ if (!response.ok) {
14914
+ return textResult(`Failed to fetch briefing context: ${response.status} ${body.slice(0, 300)}`);
14915
+ }
14916
+ const agentName = rt.resolveAgentName(agentId);
14917
+ return textResult(
14918
+ `Briefing context for ${agentName}:
14919
+
14920
+ ${body}`,
14921
+ JSON.parse(body)
14922
+ );
14923
+ } catch (err) {
14924
+ return textResult(`Failed to fetch briefing context: ${err instanceof Error ? err.message : String(err)}`);
14925
+ }
14926
+ }
14927
+ };
14928
+ });
14929
+ api.registerTool((toolCtx) => {
14930
+ const agentId = toolCtx.agentId ?? "main";
14931
+ return {
14932
+ name: "cohort_briefing",
14933
+ label: "cohort_briefing",
14934
+ description: "Submit the final generated text for a Cohort Home briefing request.",
14935
+ parameters: Type.Object({
14936
+ request_id: Type.String({ description: "Briefing request ID supplied by Cohort." }),
14937
+ content: Type.String({ description: "Final briefing text. Use exactly two short paragraphs." })
14938
+ }),
14939
+ async execute(_toolCallId, params) {
14940
+ const rt = getToolRuntime();
14941
+ if (!rt.isReady) {
14942
+ return textResult("cohort_briefing is not ready yet \u2014 the plugin is still starting up.");
14943
+ }
14944
+ const agentName = rt.resolveAgentName(agentId);
14945
+ try {
14946
+ const response = await fetch(
14947
+ `${rt.apiUrl}/api/v1/briefings/${encodeURIComponent(params.request_id)}`,
14948
+ {
14949
+ method: "POST",
14950
+ headers: {
14951
+ "Authorization": `Bearer ${rt.apiKey}`,
14952
+ "Content-Type": "application/json"
14953
+ },
14954
+ body: JSON.stringify({ agentName, content: params.content }),
14955
+ signal: AbortSignal.timeout(1e4)
14956
+ }
14957
+ );
14958
+ const body = await response.text();
14959
+ if (!response.ok) {
14960
+ return textResult(`Failed to submit briefing: ${response.status} ${body.slice(0, 300)}`);
14961
+ }
14962
+ return textResult("Briefing submitted.", JSON.parse(body));
14963
+ } catch (err) {
14964
+ return textResult(`Failed to submit briefing: ${err instanceof Error ? err.message : String(err)}`);
14965
+ }
14966
+ }
14967
+ };
14968
+ });
14797
14969
  api.registerTool(() => {
14798
14970
  return {
14799
14971
  name: "cohort_task",
@@ -72,5 +72,5 @@
72
72
  }
73
73
  }
74
74
  },
75
- "version": "0.31.7"
75
+ "version": "0.31.9"
76
76
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfio/cohort-sync",
3
- "version": "0.31.7",
3
+ "version": "0.31.9",
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.31.7",
3
+ "version": "0.31.9",
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",