@codeforbreakfast/commy-mcp 0.15.0 → 0.16.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +289 -167
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeforbreakfast/commy-mcp",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "commy MCP server — the inter-agent communication substrate, runnable on node via npx.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
package/server.js CHANGED
@@ -45359,6 +45359,9 @@ var MessageIdSchema = exports_Schema.NonEmptyString.pipe(exports_Schema.brand("M
45359
45359
  var decodeMessageId = exports_Schema.decodeUnknown(MessageIdSchema);
45360
45360
  var ThreadNameSchema = exports_Schema.NonEmptyString.pipe(exports_Schema.brand("ThreadName"));
45361
45361
  var decodeThreadName = exports_Schema.decodeUnknown(ThreadNameSchema);
45362
+ var ChannelPermalinkSchema = exports_Schema.NonEmptyString.pipe(exports_Schema.brand("ChannelPermalink"));
45363
+ var ThreadPermalinkSchema = exports_Schema.NonEmptyString.pipe(exports_Schema.brand("ThreadPermalink"));
45364
+ var MessagePermalinkSchema = exports_Schema.NonEmptyString.pipe(exports_Schema.brand("MessagePermalink"));
45362
45365
  var TimestampSchema = exports_Schema.NonNegative.pipe(exports_Schema.brand("Timestamp"));
45363
45366
  var decodeTimestamp = exports_Schema.decodeUnknown(TimestampSchema);
45364
45367
  var MessageBodySchema = exports_Schema.NonEmptyString.pipe(exports_Schema.brand("MessageBody"));
@@ -45379,6 +45382,22 @@ var decodeMessageBodySync = exports_Schema.decodeSync(MessageBodySchema);
45379
45382
  var decodeDisplayNameSync = exports_Schema.decodeSync(DisplayNameSchema);
45380
45383
  var decodeBotNameSync = exports_Schema.decodeSync(BotNameSchema);
45381
45384
  var decodeEmojiSync = exports_Schema.decodeSync(EmojiSchema);
45385
+ var ChannelRefSchema = exports_Schema.Struct({
45386
+ id: ChannelIdSchema,
45387
+ name: ChannelNameSchema,
45388
+ permalink: ChannelPermalinkSchema
45389
+ });
45390
+ var ObservedThreadSchema = exports_Schema.Struct({
45391
+ name: ThreadNameSchema,
45392
+ resolved: exports_Schema.Boolean,
45393
+ permalink: ThreadPermalinkSchema
45394
+ });
45395
+ var MessageRefSchema = exports_Schema.Struct({
45396
+ id: MessageIdSchema,
45397
+ channel: ChannelRefSchema,
45398
+ thread: exports_Schema.OptionFromSelf(ObservedThreadSchema),
45399
+ permalink: MessagePermalinkSchema
45400
+ });
45382
45401
 
45383
45402
  class DirectoryError extends exports_Data.TaggedError("DirectoryError") {
45384
45403
  get message() {
@@ -47425,23 +47444,40 @@ var HASH_REPLACEMENTS = {
47425
47444
  var encodeHashComponent = (value5) => encodeURIComponent(value5).replace(/[%!'()*.]/g, (matched) => HASH_REPLACEMENTS[matched] ?? matched);
47426
47445
  var permalinkBase = (config2) => config2.hostHeader === undefined ? config2.realmUrl.replace(/\/+$/, "") : `https://${config2.hostHeader}`;
47427
47446
  var channelSlug = (channel) => `${channel.id}-${encodeHashComponent(channel.name.replaceAll(" ", "-"))}`;
47428
- var channelPermalink = (base, channel) => `${base}/#narrow/channel/${channelSlug(channel)}`;
47429
- var topicPermalink = (base, channel, topic) => `${channelPermalink(base, channel)}/topic/${encodeHashComponent(topic)}`;
47430
- var messagePermalink = (base, channel, id3, topic) => topic === undefined ? `${channelPermalink(base, channel)}/near/${id3}` : `${topicPermalink(base, channel, topic)}/near/${id3}`;
47447
+ var channelPermalink = (base, channel) => ChannelPermalinkSchema.make(`${base}/#narrow/channel/${channelSlug(channel)}`);
47448
+ var topicPermalink = (base, channel, topic, anchor) => ThreadPermalinkSchema.make(`${channelPermalink(base, channel)}/topic/${encodeHashComponent(topic)}/with/${anchor}`);
47449
+ var messagePermalink = (base, id3) => MessagePermalinkSchema.make(`${base}/#narrow/id/${id3}`);
47431
47450
  var withChannelPermalink = (base, channel) => ({ ...channel, permalink: channelPermalink(base, channel) });
47432
- var buildMessageRef = (base, id3, channel, threadName3) => {
47451
+ var buildMessageRef = (base, id3, channel, thread) => {
47433
47452
  const decoratedChannel = withChannelPermalink(base, channel);
47434
- return threadName3 === undefined ? { id: id3, channel: decoratedChannel, permalink: messagePermalink(base, decoratedChannel, id3) } : {
47453
+ const permalink = messagePermalink(base, id3);
47454
+ return thread === undefined ? {
47435
47455
  id: id3,
47436
47456
  channel: decoratedChannel,
47437
- thread: {
47438
- name: threadName3,
47439
- permalink: topicPermalink(base, decoratedChannel, threadName3)
47440
- },
47441
- permalink: messagePermalink(base, decoratedChannel, id3, threadName3)
47457
+ thread: exports_Option.none(),
47458
+ permalink
47459
+ } : {
47460
+ id: id3,
47461
+ channel: decoratedChannel,
47462
+ thread: exports_Option.some({
47463
+ name: thread.name,
47464
+ resolved: thread.resolved,
47465
+ permalink: topicPermalink(base, decoratedChannel, thread.name, id3)
47466
+ }),
47467
+ permalink
47442
47468
  };
47443
47469
  };
47444
47470
 
47471
+ // packages/zulip/resolved-topic.ts
47472
+ var RESOLVED_TOPIC_PREFIX = "✔ ";
47473
+ var isResolvedTopic = (topic) => topic.startsWith(RESOLVED_TOPIC_PREFIX);
47474
+ var stripResolvedPrefix = (topic) => isResolvedTopic(topic) ? topic.slice(RESOLVED_TOPIC_PREFIX.length) : topic;
47475
+ var splitTopic = (rawTopic) => {
47476
+ const stripped = stripResolvedPrefix(rawTopic);
47477
+ return isResolvedTopic(rawTopic) && stripped.length > 0 ? { name: stripped, resolved: true } : { name: rawTopic, resolved: false };
47478
+ };
47479
+ var applyResolvedPrefix = (name, resolved) => resolved ? isResolvedTopic(name) ? name : RESOLVED_TOPIC_PREFIX + name : stripResolvedPrefix(name);
47480
+
47445
47481
  // packages/zulip/events.ts
47446
47482
  var createMessageRefCache = (maxSize = 1e4) => {
47447
47483
  const map32 = new Map;
@@ -47535,8 +47571,9 @@ var decodeMessageRef = (message, base) => exports_Effect.gen(function* () {
47535
47571
  const id3 = yield* decodeMessageId(String(message.id));
47536
47572
  const channelId = yield* decodeChannelId(String(message.stream_id));
47537
47573
  const channelName = yield* decodeChannelName(message.display_recipient);
47538
- const threadName3 = yield* decodeThreadName(message.subject);
47539
- return buildMessageRef(base, id3, { id: channelId, name: channelName }, threadName3);
47574
+ const { name, resolved } = splitTopic(message.subject);
47575
+ const threadName3 = yield* decodeThreadName(name);
47576
+ return buildMessageRef(base, id3, { id: channelId, name: channelName }, { name: threadName3, resolved });
47540
47577
  });
47541
47578
  var decodeSenderIdentity = (message, directory) => {
47542
47579
  const cached4 = directory.byId.get(message.sender_id);
@@ -47934,6 +47971,8 @@ var historicalReactionSchema = exports_Schema.Struct({
47934
47971
  });
47935
47972
  var historicalMessageRawSchema = exports_Schema.Struct({
47936
47973
  id: exports_Schema.Int,
47974
+ stream_id: exports_Schema.Int,
47975
+ display_recipient: exports_Schema.String,
47937
47976
  sender_id: exports_Schema.Int,
47938
47977
  sender_full_name: exports_Schema.String,
47939
47978
  subject: exports_Schema.NonEmptyString,
@@ -47942,19 +47981,27 @@ var historicalMessageRawSchema = exports_Schema.Struct({
47942
47981
  reactions: exports_Schema.optional(exports_Schema.Array(historicalReactionSchema))
47943
47982
  });
47944
47983
  var toHistoricalReaction = (r) => ({ userId: ZulipUserRef(r.user_id), emojiName: r.emoji_name });
47945
- var toHistoricalMessage = (m) => exports_Effect.all({
47946
- id: decodeMessageId(String(m.id)),
47947
- subject: decodeThreadName(m.subject),
47948
- ts: decodeTimestamp(m.timestamp)
47949
- }).pipe(exports_Effect.map(({ id: id3, subject, ts }) => ({
47950
- id: id3,
47951
- senderId: ZulipUserRef(m.sender_id),
47952
- senderFullName: m.sender_full_name,
47953
- subject,
47954
- content: m.content,
47955
- ts,
47956
- reactions: (m.reactions ?? []).map(toHistoricalReaction)
47957
- })));
47984
+ var toHistoricalMessage = (m) => {
47985
+ const { name, resolved } = splitTopic(m.subject);
47986
+ return exports_Effect.all({
47987
+ id: decodeMessageId(String(m.id)),
47988
+ channelId: decodeChannelId(String(m.stream_id)),
47989
+ channelName: decodeChannelName(m.display_recipient),
47990
+ subject: decodeThreadName(name),
47991
+ ts: decodeTimestamp(m.timestamp)
47992
+ }).pipe(exports_Effect.map(({ id: id3, channelId, channelName, subject, ts }) => ({
47993
+ id: id3,
47994
+ channelId,
47995
+ channelName,
47996
+ senderId: ZulipUserRef(m.sender_id),
47997
+ senderFullName: m.sender_full_name,
47998
+ subject,
47999
+ resolved,
48000
+ content: m.content,
48001
+ ts,
48002
+ reactions: (m.reactions ?? []).map(toHistoricalReaction)
48003
+ })));
48004
+ };
47958
48005
  var messagesResponseSchema = exports_Schema.Struct({
47959
48006
  result: exports_Schema.Literal("success"),
47960
48007
  messages: exports_Schema.Array(historicalMessageRawSchema)
@@ -48002,17 +48049,17 @@ var toIdentity = (u) => exports_Effect.all({
48002
48049
  kind: u.is_bot ? "agent" : "human"
48003
48050
  })));
48004
48051
  var isActiveOfKind = (kind) => (u) => u.is_active && (kind === "agent" ? u.is_bot : !u.is_bot);
48005
- var streamIsListening = (state, channelId) => exports_HashSet.has(state.subscribedChannels, channelId) || exports_HashSet.has(state.newTopicsChannels, channelId);
48052
+ var streamIsListening = (state, channelName) => exports_HashSet.has(state.subscribedChannels, channelName) || exports_HashSet.has(state.newTopicsChannels, channelName);
48006
48053
  var currentMode = (state) => state.mentionsSubscribed && exports_HashSet.size(state.subscribedChannels) === 0 && exports_HashSet.size(state.newTopicsChannels) === 0 ? "mentions" : "all";
48007
- var observeNewTopic = (state, channelId, threadName3) => {
48008
- const seen = exports_HashMap.get(state.seenTopicsByChannel, channelId).pipe(exports_Option.getOrElse(() => exports_HashSet.empty()));
48054
+ var observeNewTopic = (state, channelName, threadName3) => {
48055
+ const seen = exports_HashMap.get(state.seenTopicsByChannel, channelName).pipe(exports_Option.getOrElse(() => exports_HashSet.empty()));
48009
48056
  if (exports_HashSet.has(seen, threadName3))
48010
48057
  return [false, state];
48011
48058
  return [
48012
48059
  true,
48013
48060
  {
48014
48061
  ...state,
48015
- seenTopicsByChannel: exports_HashMap.set(state.seenTopicsByChannel, channelId, exports_HashSet.add(seen, threadName3))
48062
+ seenTopicsByChannel: exports_HashMap.set(state.seenTopicsByChannel, channelName, exports_HashSet.add(seen, threadName3))
48016
48063
  }
48017
48064
  ];
48018
48065
  };
@@ -48076,7 +48123,8 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48076
48123
  }
48077
48124
  return out;
48078
48125
  });
48079
- const mapHistoricalMessage = (m, channel, directory2) => exports_Effect.gen(function* () {
48126
+ const mapHistoricalMessage = (m, directory2) => exports_Effect.gen(function* () {
48127
+ const channel = decorateChannel({ id: m.channelId, name: m.channelName });
48080
48128
  const cached4 = directory2.byId.get(m.senderId);
48081
48129
  const sender = cached4 ?? (yield* exports_Effect.all({
48082
48130
  id: decodeIdentityId(String(m.senderId)),
@@ -48085,7 +48133,7 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48085
48133
  const body = yield* decodeMessageBody(m.content);
48086
48134
  const reactions = yield* mapHistoricalReactions(m.reactions, directory2);
48087
48135
  return {
48088
- ref: decorateMessageRef(m.id, channel, m.subject),
48136
+ ref: decorateMessageRef(m.id, channel, { name: m.subject, resolved: m.resolved }),
48089
48137
  sender,
48090
48138
  body,
48091
48139
  ts: m.ts,
@@ -48093,7 +48141,7 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48093
48141
  reactions
48094
48142
  };
48095
48143
  });
48096
- const readMessages = (channel, range5, narrow) => exports_Effect.all([
48144
+ const readMessages = (range5, narrow) => exports_Effect.all([
48097
48145
  buildDirectoryLookup(),
48098
48146
  minterHttp.get("/messages", messagesResponseSchema, {
48099
48147
  anchor: "newest",
@@ -48102,11 +48150,11 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48102
48150
  narrow: JSON.stringify(narrow),
48103
48151
  apply_markdown: false
48104
48152
  })
48105
- ], { concurrency: 2 }).pipe(exports_Effect.flatMap(([directory2, res]) => exports_Effect.forEach(res.messages, toHistoricalMessage).pipe(exports_Effect.map((historical) => historical.filter(inRange(range5))), exports_Effect.flatMap((inRangeMessages) => exports_Effect.forEach(inRangeMessages, (m) => mapHistoricalMessage(m, channel, directory2))))));
48153
+ ], { concurrency: 2 }).pipe(exports_Effect.flatMap(([directory2, res]) => exports_Effect.forEach(res.messages, toHistoricalMessage).pipe(exports_Effect.map((historical) => historical.filter(inRange(range5))), exports_Effect.flatMap((inRangeMessages) => exports_Effect.forEach(inRangeMessages, (m) => mapHistoricalMessage(m, directory2))))));
48106
48154
  const realmHost = new URL(config2.realmUrl).hostname;
48107
48155
  const base = permalinkBase(config2);
48108
48156
  const decorateChannel = (channel) => withChannelPermalink(base, channel);
48109
- const decorateMessageRef = (id3, channel, threadName3) => buildMessageRef(base, id3, channel, threadName3);
48157
+ const decorateMessageRef = (id3, channel, thread) => buildMessageRef(base, id3, channel, thread);
48110
48158
  const buildBotEmail = (shortName) => BotEmail(`${shortName}-bot@${realmHost}`);
48111
48159
  const findAnyBotByName = (name) => minterHttp.get("/users", usersSchema).pipe(exports_Effect.map((res) => exports_Option.fromNullable(res.members.find((u) => u.full_name === name && u.is_bot))));
48112
48160
  const mintBot = (name) => {
@@ -48262,11 +48310,42 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48262
48310
  onNone: () => refreshKnownStreams(),
48263
48311
  onSome: exports_Effect.succeed
48264
48312
  })), exports_Effect.flatMap((map32) => map32.has(name) ? exports_Effect.succeed(map32) : refreshKnownStreams()), exports_Effect.map((map32) => exports_Option.fromNullable(map32.get(name))));
48265
- const resolvePublishChannel = (requested) => lookupChannel(requested.name).pipe(exports_Effect.flatMap(exports_Option.match({
48266
- onNone: () => exports_Effect.fail(new UnknownChannel({ channel: requested.name, substrate: "zulip" })),
48313
+ const resolvePublishChannel = (requested) => lookupChannel(requested).pipe(exports_Effect.flatMap(exports_Option.match({
48314
+ onNone: () => exports_Effect.fail(new UnknownChannel({ channel: requested, substrate: "zulip" })),
48267
48315
  onSome: exports_Effect.succeed
48268
48316
  })));
48269
48317
  const listKnownChannels = () => refreshKnownStreams().pipe(exports_Effect.map((map32) => [...map32.values()]));
48318
+ const findNewestInTopic = (channel, topic) => minterHttp.get("/messages", messagesResponseSchema, {
48319
+ anchor: "newest",
48320
+ num_before: 1,
48321
+ num_after: 0,
48322
+ narrow: JSON.stringify([
48323
+ { operator: "channel", operand: channel },
48324
+ { operator: "topic", operand: topic }
48325
+ ]),
48326
+ apply_markdown: false
48327
+ }).pipe(exports_Effect.flatMap((res) => exports_Array.last(res.messages).pipe(exports_Option.match({
48328
+ onNone: () => exports_Effect.succeed(exports_Option.none()),
48329
+ onSome: (m) => decodeMessageId(String(m.id)).pipe(exports_Effect.map(exports_Option.some))
48330
+ }))));
48331
+ const setThreadResolved = (channel, thread, resolved) => {
48332
+ const operation = resolved ? "resolveThread" : "unresolveThread";
48333
+ const sourceTopic = applyResolvedPrefix(thread, !resolved);
48334
+ const targetTopic = applyResolvedPrefix(thread, resolved);
48335
+ return boundHttp().pipe(exports_Effect.flatMap((http) => findNewestInTopic(channel, sourceTopic).pipe(exports_Effect.flatMap(exports_Option.match({
48336
+ onSome: (id3) => http.patch(`/messages/${encodeURIComponent(id3)}`, successSchema2, {
48337
+ topic: targetTopic,
48338
+ propagate_mode: "change_all"
48339
+ }).pipe(exports_Effect.asVoid),
48340
+ onNone: () => findNewestInTopic(channel, targetTopic).pipe(exports_Effect.flatMap(exports_Option.match({
48341
+ onSome: () => exports_Effect.void,
48342
+ onNone: () => exports_Effect.fail(new PublisherError({
48343
+ operation,
48344
+ cause: new Error(`no thread '${thread}' in ${channel} to ${resolved ? "resolve" : "unresolve"}`)
48345
+ }))
48346
+ })))
48347
+ })))), exports_Effect.mapError((cause3) => cause3 instanceof PublisherError ? cause3 : new PublisherError({ operation, cause: cause3 })));
48348
+ };
48270
48349
  const publisher = {
48271
48350
  post: (channel, body, opts) => {
48272
48351
  const thread = opts?.thread;
@@ -48274,8 +48353,8 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48274
48353
  type: "channel",
48275
48354
  to: effective.name,
48276
48355
  content: body,
48277
- topic: thread === undefined ? "(no topic)" : thread.name
48278
- }).pipe(exports_Effect.flatMap((sent) => decodeMessageId(String(sent.id)).pipe(exports_Effect.map((id3) => decorateMessageRef(id3, effective, thread?.name)))))))), exports_Effect.mapError((cause3) => cause3 instanceof UnknownChannel ? cause3 : new PublisherError({ operation: "post", cause: cause3 })));
48356
+ topic: thread ?? "(no topic)"
48357
+ }).pipe(exports_Effect.flatMap((sent) => decodeMessageId(String(sent.id)).pipe(exports_Effect.map((id3) => decorateMessageRef(id3, effective, thread === undefined ? undefined : { name: thread, resolved: false })))))))), exports_Effect.mapError((cause3) => cause3 instanceof UnknownChannel ? cause3 : new PublisherError({ operation: "post", cause: cause3 })));
48279
48358
  },
48280
48359
  edit: (message, body) => boundHttp().pipe(exports_Effect.flatMap((http) => http.patch(`/messages/${encodeURIComponent(message.id)}`, successSchema2, {
48281
48360
  content: body
@@ -48285,7 +48364,9 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48285
48364
  })), exports_Effect.asVoid, exports_Effect.mapError((cause3) => new PublisherError({ operation: "react", cause: cause3 }))),
48286
48365
  unreact: (message, emoji) => boundHttp().pipe(exports_Effect.flatMap((http) => http.delete(`/messages/${encodeURIComponent(message.id)}/reactions`, successSchema2, {
48287
48366
  emoji_name: emoji
48288
- })), exports_Effect.asVoid, exports_Effect.mapError((cause3) => new PublisherError({ operation: "unreact", cause: cause3 })))
48367
+ })), exports_Effect.asVoid, exports_Effect.mapError((cause3) => new PublisherError({ operation: "unreact", cause: cause3 }))),
48368
+ resolveThread: (channel, thread) => setThreadResolved(channel, thread, true),
48369
+ unresolveThread: (channel, thread) => setThreadResolved(channel, thread, false)
48289
48370
  };
48290
48371
  const inboxRef = yield* exports_SynchronizedRef.make({
48291
48372
  mentionsSubscribed: false,
@@ -48300,15 +48381,18 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48300
48381
  if (event.kind !== "message-posted")
48301
48382
  return exports_Effect.succeed(true);
48302
48383
  const message = event.message;
48303
- const cid = message.ref.channel.id;
48384
+ const cname = message.ref.channel.name;
48304
48385
  const thread = message.ref.thread;
48305
48386
  return exports_SynchronizedRef.get(boundRef).pipe(exports_Effect.flatMap((current2) => {
48306
48387
  const me = exports_Option.getOrUndefined(current2)?.identity;
48307
48388
  return exports_SynchronizedRef.modify(inboxRef, (state) => {
48308
- if (!exports_HashSet.has(state.newTopicsChannels, cid))
48389
+ if (!exports_HashSet.has(state.newTopicsChannels, cname))
48309
48390
  return [true, state];
48310
- const [isFirstOfTopic, ticked] = thread === undefined ? [false, state] : observeNewTopic(state, cid, thread.name);
48311
- if (exports_HashSet.has(state.subscribedChannels, cid))
48391
+ const [isFirstOfTopic, ticked] = exports_Option.match(thread, {
48392
+ onNone: () => [false, state],
48393
+ onSome: (t) => observeNewTopic(state, cname, t.name)
48394
+ });
48395
+ if (exports_HashSet.has(state.subscribedChannels, cname))
48312
48396
  return [true, ticked];
48313
48397
  if (state.mentionsSubscribed && me !== undefined && message.mentions.some((m) => m.id === me.id)) {
48314
48398
  return [true, ticked];
@@ -48339,18 +48423,18 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48339
48423
  if (channel === undefined)
48340
48424
  return ensureQueueRegistered();
48341
48425
  return exports_SynchronizedRef.modify(inboxRef, (state) => {
48342
- const wasListening = streamIsListening(state, channel.id);
48426
+ const wasListening = streamIsListening(state, channel);
48343
48427
  const next4 = exports_Predicate.hasProperty(target, "kind") ? {
48344
48428
  ...state,
48345
- newTopicsChannels: exports_HashSet.add(state.newTopicsChannels, channel.id)
48429
+ newTopicsChannels: exports_HashSet.add(state.newTopicsChannels, channel)
48346
48430
  } : {
48347
48431
  ...state,
48348
- subscribedChannels: exports_HashSet.add(state.subscribedChannels, channel.id)
48432
+ subscribedChannels: exports_HashSet.add(state.subscribedChannels, channel)
48349
48433
  };
48350
48434
  return [wasListening, next4];
48351
48435
  }).pipe(exports_Effect.flatMap((wasListening) => {
48352
48436
  const subscribeRemote = wasListening ? exports_Effect.void : minterHttp.post("/users/me/subscriptions", subscriptionsResponseSchema, {
48353
- subscriptions: JSON.stringify([{ name: channel.name }])
48437
+ subscriptions: JSON.stringify([{ name: channel }])
48354
48438
  }).pipe(exports_Effect.asVoid);
48355
48439
  return subscribeRemote.pipe(exports_Effect.andThen(ensureQueueRegistered()));
48356
48440
  }));
@@ -48368,15 +48452,15 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48368
48452
  return exports_SynchronizedRef.modify(inboxRef, (state) => {
48369
48453
  const next4 = exports_Predicate.hasProperty(target, "kind") ? {
48370
48454
  ...state,
48371
- newTopicsChannels: exports_HashSet.remove(state.newTopicsChannels, channel.id),
48372
- seenTopicsByChannel: exports_HashMap.remove(state.seenTopicsByChannel, channel.id)
48455
+ newTopicsChannels: exports_HashSet.remove(state.newTopicsChannels, channel),
48456
+ seenTopicsByChannel: exports_HashMap.remove(state.seenTopicsByChannel, channel)
48373
48457
  } : {
48374
48458
  ...state,
48375
- subscribedChannels: exports_HashSet.remove(state.subscribedChannels, channel.id)
48459
+ subscribedChannels: exports_HashSet.remove(state.subscribedChannels, channel)
48376
48460
  };
48377
- return [streamIsListening(next4, channel.id), next4];
48461
+ return [streamIsListening(next4, channel), next4];
48378
48462
  }).pipe(exports_Effect.flatMap((stillListening) => stillListening ? exports_Effect.void : minterHttp.delete("/users/me/subscriptions", subscriptionsResponseSchema, {
48379
- subscriptions: JSON.stringify([channel.name])
48463
+ subscriptions: JSON.stringify([channel])
48380
48464
  }).pipe(exports_Effect.asVoid)));
48381
48465
  }).pipe(exports_Effect.mapError((cause3) => new InboxError({ operation: "unsubscribe", cause: cause3 }))),
48382
48466
  events: () => exports_Stream.unwrap(exports_Effect.all([exports_SynchronizedRef.get(boundRef), exports_SynchronizedRef.get(inboxRef)]).pipe(exports_Effect.map(([current2, state]) => inboxEvents({
@@ -48426,11 +48510,14 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48426
48510
  }
48427
48511
  };
48428
48512
  const history = {
48429
- readChannel: (channel, range5) => readMessages(channel, range5, [{ operator: "channel", operand: channel.name }]).pipe(exports_Effect.mapError((cause3) => new HistoryError({ operation: "readChannel", cause: cause3 }))),
48430
- readThread: (channel, threadName3, range5) => readMessages(channel, range5 ?? {}, [
48431
- { operator: "channel", operand: channel.name },
48432
- { operator: "topic", operand: threadName3 }
48433
- ]).pipe(exports_Effect.mapError((cause3) => new HistoryError({ operation: "readThread", cause: cause3 }))),
48513
+ readChannel: (channel, range5) => readMessages(range5, [{ operator: "channel", operand: channel }]).pipe(exports_Effect.mapError((cause3) => new HistoryError({ operation: "readChannel", cause: cause3 }))),
48514
+ readThread: (channel, threadName3, range5) => {
48515
+ const readTopic = (topic) => readMessages(range5 ?? {}, [
48516
+ { operator: "channel", operand: channel },
48517
+ { operator: "topic", operand: topic }
48518
+ ]);
48519
+ return readTopic(threadName3).pipe(exports_Effect.flatMap((messages) => messages.length > 0 ? exports_Effect.succeed(messages) : readTopic(applyResolvedPrefix(threadName3, true))), exports_Effect.mapError((cause3) => new HistoryError({ operation: "readThread", cause: cause3 })));
48520
+ },
48434
48521
  recentThreads: (sender, opts) => {
48435
48522
  const limit = opts?.limit ?? RECENT_THREADS_DEFAULT_LIMIT;
48436
48523
  return buildDirectoryLookup().pipe(exports_Effect.flatMap((lookup) => {
@@ -48449,7 +48536,7 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48449
48536
  let seen = exports_HashMap.empty();
48450
48537
  for (const m of res.messages) {
48451
48538
  const channel = yield* decodeChannelName(m.display_recipient);
48452
- const thread = yield* decodeThreadName(m.subject);
48539
+ const thread = yield* decodeThreadName(splitTopic(m.subject).name);
48453
48540
  const key = threadKey(channel, thread);
48454
48541
  if (!exports_HashMap.has(seen, key)) {
48455
48542
  seen = exports_HashMap.set(seen, key, {
@@ -48467,7 +48554,7 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48467
48554
  })));
48468
48555
  }), exports_Effect.mapError((cause3) => new HistoryError({ operation: "recentThreads", cause: cause3 })));
48469
48556
  },
48470
- messagePermalink: (id3, hint) => (hint === undefined ? fetchMessageRef(minterHttp, Number(id3), base).pipe(exports_Effect.map(exports_Option.flatMap((ref) => exports_Option.fromNullable(ref.permalink)))) : lookupChannel(hint.channel).pipe(exports_Effect.map(exports_Option.flatMap((channel) => exports_Option.fromNullable(buildMessageRef(base, id3, channel, hint.thread).permalink))))).pipe(exports_Effect.mapError((cause3) => new HistoryError({ operation: "messagePermalink", cause: cause3 })))
48557
+ messagePermalink: (id3, hint) => (hint === undefined ? fetchMessageRef(minterHttp, Number(id3), base).pipe(exports_Effect.map(exports_Option.map((ref) => ref.permalink))) : lookupChannel(hint.channel).pipe(exports_Effect.map(exports_Option.map((channel) => buildMessageRef(base, id3, channel, hint.thread === undefined ? undefined : { name: hint.thread, resolved: false }).permalink)))).pipe(exports_Effect.mapError((cause3) => new HistoryError({ operation: "messagePermalink", cause: cause3 })))
48471
48558
  };
48472
48559
  const reconcileMinter = () => reconcileMinterSubscriptions({
48473
48560
  listUnsubscribedPublicStreams: () => minterHttp.get("/streams", streamsListResponseSchema, {
@@ -56706,16 +56793,16 @@ var parseSubscribeTarget = (token) => exports_Effect.gen(function* () {
56706
56793
  });
56707
56794
  });
56708
56795
  var intentToTarget = (intent) => exports_Match.value(intent).pipe(exports_Match.discriminatorsExhaustive("kind")({
56709
- mentions: () => exports_Effect.succeed("mentions"),
56710
- channel: ({ channelName }) => decodeChannelId(channelName).pipe(exports_Effect.map((id3) => ({ id: id3, name: channelName })), exports_Effect.orDie),
56711
- "new-topics-in-channel": ({ channelName }) => decodeChannelId(channelName).pipe(exports_Effect.map((id3) => ({
56796
+ mentions: () => "mentions",
56797
+ channel: ({ channelName }) => channelName,
56798
+ "new-topics-in-channel": ({ channelName }) => ({
56712
56799
  kind: "new-topics-in-channel",
56713
- channel: { id: id3, name: channelName }
56714
- })), exports_Effect.orDie),
56715
- thread: ({ channelName, threadName: threadName3 }) => decodeChannelId(channelName).pipe(exports_Effect.map((id3) => ({
56716
- channel: { id: id3, name: channelName },
56717
- thread: { name: threadName3 }
56718
- })), exports_Effect.orDie)
56800
+ channel: channelName
56801
+ }),
56802
+ thread: ({ channelName, threadName: threadName3 }) => ({
56803
+ channel: channelName,
56804
+ thread: threadName3
56805
+ })
56719
56806
  }));
56720
56807
 
56721
56808
  // packages/mcp/bootstrap.ts
@@ -56739,7 +56826,7 @@ var parseBotName = (raw3) => {
56739
56826
  };
56740
56827
  var placeholderShape = /^\$\{[^}]+\}$/;
56741
56828
  var userConfigPlaceholderShape = /^\$\{user_config\.[^}]+\}$/;
56742
- var placeholderMessage = (key, raw3) => `${key} value is an unsubstituted placeholder${raw3 === undefined ? "" : ` (${raw3})`} — ` + `the host did not expand it. ` + `Claude Code only substitutes \${user_config.X} (and \${CLAUDE_PLUGIN_ROOT} in args) ` + `in .mcp.json; host-env vars like \${CLAUDE_CODE_SESSION_ID} must be inherited from ` + `the parent environment instead. See ass-ukz6 for context.`;
56829
+ var placeholderMessage = (key, raw3) => `${key} value is an unsubstituted placeholder${raw3 === undefined ? "" : ` (${raw3})`} — ` + `the host did not expand it. ` + `Claude Code only substitutes \${user_config.X} (and \${CLAUDE_PLUGIN_ROOT} in args) ` + `in .mcp.json; host-env vars like \${CLAUDE_CODE_SESSION_ID} must be inherited from ` + `the parent environment instead.`;
56743
56830
  var rejectPlaceholder = (key) => exports_Config.validate({
56744
56831
  message: placeholderMessage(key),
56745
56832
  validation: (value5) => !placeholderShape.test(value5)
@@ -56786,15 +56873,6 @@ var optionalNonNegativeInt = (key) => optionalUserConfig(key).pipe(exports_Confi
56786
56873
  return Number.isInteger(parsed) && parsed >= 0 ? exports_Either.right(exports_Option.some(parsed)) : exports_Either.left(exports_ConfigError.InvalidData([key], `${key} must be a non-negative integer — received: ${raw3}`));
56787
56874
  }
56788
56875
  })));
56789
- var optionalSessionId = (key) => exports_Config.option(exports_Config.string(key).pipe(exports_Config.mapOrFail((value5) => {
56790
- if (value5.length === 0) {
56791
- return exports_Either.left(exports_ConfigError.MissingData([key], `${key} is unset`));
56792
- }
56793
- if (placeholderShape.test(value5)) {
56794
- return exports_Either.left(exports_ConfigError.InvalidData([key], placeholderMessage(key, value5)));
56795
- }
56796
- return exports_Either.fromOption(parseSessionId(value5), () => exports_ConfigError.InvalidData([key], `${key} must be a UUID — received: ${value5}`));
56797
- })));
56798
56876
  var optionalBotName = (key) => optionalNonEmpty(key).pipe(exports_Config.mapOrFail((option7) => exports_Option.match(option7, {
56799
56877
  onNone: () => exports_Either.right(exports_Option.none()),
56800
56878
  onSome: (raw3) => exports_Either.fromOption(parseBotName(raw3), () => exports_ConfigError.InvalidData([key], `${key} must be substrate-safe (lowercase ASCII, digits, dashes, underscores; starts with letter; max 40 chars) — received: ${raw3}`)).pipe(exports_Either.map(exports_Option.some))
@@ -56817,8 +56895,7 @@ var envConfig = exports_Config.all({
56817
56895
  botApiKey: optionalApiKey("COMMY_BOT_API_KEY"),
56818
56896
  subscribe: optionalUserConfig("COMMY_SUBSCRIBE"),
56819
56897
  project: optionalNonEmpty("COMMY_PROJECT"),
56820
- catchupWindowSeconds: optionalNonNegativeInt("COMMY_CATCHUP_WINDOW_SECONDS"),
56821
- sessionId: optionalSessionId("CLAUDE_CODE_SESSION_ID")
56898
+ catchupWindowSeconds: optionalNonNegativeInt("COMMY_CATCHUP_WINDOW_SECONDS")
56822
56899
  }).pipe(exports_Config.mapOrFail((raw3) => {
56823
56900
  if (exports_Option.isSome(raw3.botApiKey) && exports_Option.isNone(raw3.botName)) {
56824
56901
  return exports_Either.left(exports_ConfigError.InvalidData(["COMMY_BOT_API_KEY"], "COMMY_BOT_API_KEY requires COMMY_BOT_NAME — the key identifies which provisioned persona to attach to"));
@@ -56834,10 +56911,6 @@ var envConfig = exports_Config.all({
56834
56911
  onNone: () => ({}),
56835
56912
  onSome: (subscribe4) => ({ subscribe: subscribe4 })
56836
56913
  }),
56837
- ...exports_Option.match(raw3.sessionId, {
56838
- onNone: () => ({}),
56839
- onSome: (sessionId) => ({ sessionId })
56840
- }),
56841
56914
  ...exports_Option.match(project3, { onNone: () => ({}), onSome: (slug) => ({ project: slug }) }),
56842
56915
  ...exports_Option.match(raw3.catchupWindowSeconds, {
56843
56916
  onNone: () => ({}),
@@ -56911,7 +56984,7 @@ var ZulipAdapterLive = substrateAdapterLayer(exports_Effect.gen(function* () {
56911
56984
  var subscribeFromEnv = (inbox, narrowSet, parsed) => {
56912
56985
  if (parsed.subscribe === undefined)
56913
56986
  return exports_Effect.succeed([]);
56914
- return exports_Effect.forEach(parsed.subscribe.split(","), (raw3) => parseSubscribeTarget(raw3.trim()).pipe(exports_Effect.tap((intent) => exports_Effect.sync(() => narrowSet.add(intent))), exports_Effect.flatMap((intent) => intentToTarget(intent).pipe(exports_Effect.flatMap((target) => inbox.subscribe(target)), exports_Effect.as(intent)))));
56987
+ return exports_Effect.forEach(parsed.subscribe.split(","), (raw3) => parseSubscribeTarget(raw3.trim()).pipe(exports_Effect.tap((intent) => exports_Effect.sync(() => narrowSet.add(intent))), exports_Effect.flatMap((intent) => inbox.subscribe(intentToTarget(intent)).pipe(exports_Effect.as(intent)))));
56915
56988
  };
56916
56989
 
56917
56990
  // packages/mcp/events.ts
@@ -56928,11 +57001,11 @@ var formatMessage = (event, botIdentityId) => {
56928
57001
  const meta2 = buildMeta([
56929
57002
  ["channel_id", ref.channel.id],
56930
57003
  ["channel_name", ref.channel.name],
56931
- ["thread", ref.thread?.name],
57004
+ ["thread", exports_Option.getOrUndefined(exports_Option.map(ref.thread, (t) => t.name))],
56932
57005
  ["message_id", ref.id],
56933
57006
  ["permalink", ref.permalink],
56934
57007
  ["channel_permalink", ref.channel.permalink],
56935
- ["thread_permalink", ref.thread?.permalink],
57008
+ ["thread_permalink", exports_Option.getOrUndefined(exports_Option.map(ref.thread, (t) => t.permalink))],
56936
57009
  ["sender_id", sender.id],
56937
57010
  ["sender_name", sender.name],
56938
57011
  ["sender_kind", sender.kind],
@@ -56950,7 +57023,7 @@ var formatReaction = (event, observedAt) => {
56950
57023
  const meta2 = buildMeta([
56951
57024
  ["target_message_id", event.target.id],
56952
57025
  ["target_channel_name", event.target.channel.name],
56953
- ["target_thread", event.target.thread?.name],
57026
+ ["target_thread", exports_Option.getOrUndefined(exports_Option.map(event.target.thread, (t) => t.name))],
56954
57027
  ["target_permalink", event.target.permalink],
56955
57028
  ["reaction_emoji", event.emoji],
56956
57029
  ["reaction_action", action],
@@ -56994,7 +57067,6 @@ var catchUpMentions = (deps) => exports_Effect.gen(function* () {
56994
57067
  });
56995
57068
 
56996
57069
  // packages/mcp/channels-catch-up.ts
56997
- var channelRefFromName = (name) => decodeChannelId(name).pipe(exports_Effect.map((id3) => ({ id: id3, name })), exports_Effect.orDie);
56998
57070
  var clampedSince = (now2, windowSeconds) => decodeTimestamp(Math.max(0, now2 - windowSeconds)).pipe(exports_Effect.orDie);
56999
57071
  var firstMessagePerTopic = (messages) => {
57000
57072
  const seen = new Set;
@@ -57002,20 +57074,20 @@ var firstMessagePerTopic = (messages) => {
57002
57074
  const ordered = exports_Array.sort(messages, exports_Order.mapInput(exports_Order.number, (m) => m.ts));
57003
57075
  for (const m of ordered) {
57004
57076
  const thread = m.ref.thread;
57005
- if (thread === undefined)
57077
+ if (exports_Option.isNone(thread))
57006
57078
  continue;
57007
- if (seen.has(thread.name))
57079
+ if (seen.has(thread.value.name))
57008
57080
  continue;
57009
- seen.add(thread.name);
57081
+ seen.add(thread.value.name);
57010
57082
  out.push(m);
57011
57083
  }
57012
57084
  return out;
57013
57085
  };
57014
57086
  var fetchForIntent = (intent, history, defaultSince, newTopicsSince) => exports_Match.value(intent).pipe(exports_Match.discriminatorsExhaustive("kind")({
57015
57087
  mentions: () => exports_Effect.succeed([]),
57016
- channel: (i) => channelRefFromName(i.channelName).pipe(exports_Effect.flatMap((ref) => history.readChannel(ref, { since: defaultSince }))),
57017
- "new-topics-in-channel": (i) => channelRefFromName(i.channelName).pipe(exports_Effect.flatMap((ref) => history.readChannel(ref, { since: newTopicsSince })), exports_Effect.map(firstMessagePerTopic)),
57018
- thread: (i) => channelRefFromName(i.channelName).pipe(exports_Effect.flatMap((ref) => history.readThread(ref, i.threadName, { since: defaultSince })))
57088
+ channel: (i) => history.readChannel(i.channelName, { since: defaultSince }),
57089
+ "new-topics-in-channel": (i) => history.readChannel(i.channelName, { since: newTopicsSince }).pipe(exports_Effect.map(firstMessagePerTopic)),
57090
+ thread: (i) => history.readThread(i.channelName, i.threadName, { since: defaultSince })
57019
57091
  }));
57020
57092
  var catchUpChannels = (deps) => exports_Effect.gen(function* () {
57021
57093
  if (deps.intents.length === 0)
@@ -58658,12 +58730,12 @@ class Server extends Protocol {
58658
58730
  }
58659
58731
 
58660
58732
  // packages/mcp/mcp-server.ts
58661
- var PLUGIN_VERSION = "0.15.0";
58733
+ var PLUGIN_VERSION = "0.16.0";
58662
58734
  var COMMY_INSTRUCTIONS = `**Substrate.** commy is the inter-agent channel: agents and humans coordinate here. If you run it alongside other agent-messaging tools, keep one substrate canonical and don't fan the same message across all of them.
58663
58735
 
58664
58736
  **Channels.** Each project has one channel: \`#<project-slug>\` where the slug is resolved per session as \`COMMY_PROJECT\` env > git remote \`origin\` basename > git root basename. Sessions launched outside a git repo (e.g. \`/tmp\`, \`$HOME\`) have no project channel and should post to \`#general\` instead. Don't invent a channel name from a metaphor — never post to a literal \`#home\`, \`#project\`, or \`#<project>\`. Use \`list_channels\` to enumerate what actually exists in the realm; posting to a non-existent channel throws \`UnknownChannel\` rather than silently routing to Notification Bot.
58665
58737
 
58666
- **Topics.** A topic is a logical thread of conversation within a channel; the \`post\` tool's \`thread\` argument names it (Zulip calls these "topics"). Open a new topic when the work shifts — a fresh task, a new question, a different incident — and reply into an existing one when you're continuing the same line of work. Name topics by the work, not the speaker: \`payments-migration\`, \`auth-token-refresh\`, \`events-queue-expiry\` — not \`bot-debugging\` or \`graeme-asked\`. Top-level channel chatter (omit \`thread\`) is for terse status pings only; anything substantive deserves a topic.
58738
+ **Topics.** A topic is a logical thread of conversation within a channel; the \`post\` tool's \`thread\` argument names it (Zulip calls these "topics"). Open a new topic when the work shifts — a fresh task, a new question, a different incident — and reply into an existing one when you're continuing the same line of work. Name topics by the work, not the speaker: \`payments-migration\`, \`auth-token-refresh\`, \`events-queue-expiry\` — not \`bot-debugging\` or \`alice-asked\`. Top-level channel chatter (omit \`thread\`) is for terse status pings only; anything substantive deserves a topic.
58667
58739
 
58668
58740
  **Subscriptions.** Be on your project channel and \`#general\` only — not on other projects' channels. Boot-time defaults come from the plugin's \`COMMY_SUBSCRIBE\` userConfig (tokens: \`channel:<name>\`, \`thread:<channel>/<thread>\`, \`new-topics:<channel>\`, \`mentions\`); adjust the live set via \`subscribe\`/\`unsubscribe\` at runtime. Inbound matches arrive as \`<channel source="commy" ...>\` blocks. If one arrives from a channel you aren't subscribed to, treat it as background context and don't reply — only post into your project channel, into \`#general\`, or into threads you've explicitly joined. **Refer to peers by name** — the \`sender_name\` (and reaction \`by_name\`) in those blocks — never by a bare number; reserve numbers for message ids, which otherwise collide with identity in a thread.
58669
58741
 
@@ -58709,7 +58781,7 @@ var keyToIntent = (key) => exports_Match.value(key).pipe(exports_Match.discrimin
58709
58781
  }));
58710
58782
  var channelKey = (ref) => channelKeyOf(ref.channel.name);
58711
58783
  var newTopicsKey = (ref) => newTopicsKeyOf(ref.channel.name);
58712
- var threadKey = (ref) => exports_Option.fromNullable(ref.thread).pipe(exports_Option.map((thread) => threadKeyOf(ref.channel.name, thread.name)));
58784
+ var threadKey = (ref) => exports_Option.map(ref.thread, (thread) => threadKeyOf(ref.channel.name, thread.name));
58713
58785
  var refMatches = (ref, intents) => exports_HashSet.has(intents, channelKey(ref)) || exports_Option.exists(threadKey(ref), (tk) => exports_HashSet.has(intents, tk));
58714
58786
  var mentionsMatches = (intents, mentions, botIdentityId) => {
58715
58787
  if (botIdentityId === undefined)
@@ -58759,11 +58831,18 @@ var DEFAULT_TIMEOUT_MS = 5000;
58759
58831
  var raceReleaseAgainstTimeout = (release, timeoutMs = DEFAULT_TIMEOUT_MS) => release.pipe(exports_Effect.tapDefect((cause3) => exports_Effect.logError(`release failed: ${exports_Cause.pretty(cause3)}`)), exports_Effect.catchAllDefect(() => exports_Effect.void), exports_Effect.timeoutOption(exports_Duration.millis(timeoutMs)), exports_Effect.flatMap((result) => exports_Option.isNone(result) ? exports_Effect.logError(`release timed out after ${timeoutMs}ms`) : exports_Effect.void));
58760
58832
 
58761
58833
  // packages/mcp/subscription-restore.ts
58762
- var applyRestored = (deps, intents) => exports_Effect.sync(() => deps.narrowSet.replace(intents)).pipe(exports_Effect.zipRight(exports_Effect.forEach(intents, (intent) => intentToTarget(intent).pipe(exports_Effect.flatMap(deps.inbox.subscribe)), { discard: true })));
58763
- var restoreOrSeedSubscriptions = (deps, sessionId, project3) => deps.subscriptionStore.read(sessionId).pipe(exports_Effect.flatMap(exports_Option.match({
58834
+ var applyRestored = (deps, intents) => exports_Effect.sync(() => deps.narrowSet.replace(intents)).pipe(exports_Effect.zipRight(exports_Effect.forEach(intents, (intent) => deps.inbox.subscribe(intentToTarget(intent)), {
58835
+ discard: true
58836
+ })));
58837
+ var seedDefaultsIfFresh = (deps, sessionId, project3) => deps.subscriptionStore.read(sessionId).pipe(exports_Effect.flatMap(exports_Option.match({
58764
58838
  onNone: () => deps.registerDefaults(project3),
58839
+ onSome: () => exports_Effect.void
58840
+ })));
58841
+ var restoreSubscriptions = (deps, sessionId) => deps.subscriptionStore.read(sessionId).pipe(exports_Effect.flatMap(exports_Option.match({
58842
+ onNone: () => exports_Effect.void,
58765
58843
  onSome: (intents) => applyRestored(deps, intents)
58766
58844
  })));
58845
+ var makeSessionRestore = (deps) => exports_Effect.map(exports_Deferred.make(), (known) => (sessionId) => exports_Deferred.succeed(known, sessionId).pipe(exports_Effect.flatMap((won) => won ? restoreSubscriptions(deps, sessionId) : exports_Effect.void)));
58767
58846
  var persistSubscriptions = (store, narrowSet, sessionId) => store.write(sessionId, narrowSet.intents());
58768
58847
 
58769
58848
  // packages/mcp/subscription-store.ts
@@ -58815,11 +58894,6 @@ var identityShape = (identity6) => ({
58815
58894
  name: identity6.name,
58816
58895
  kind: identity6.kind
58817
58896
  });
58818
- var channelShape = (channel) => ({
58819
- id: channel.id,
58820
- name: channel.name,
58821
- permalink: channel.permalink ?? null
58822
- });
58823
58897
  var createCache = () => {
58824
58898
  const identityById = new Map;
58825
58899
  const channelByName = new Map;
@@ -58852,7 +58926,7 @@ class UnknownMessageError extends exports_Data.TaggedError("UnknownMessage") {
58852
58926
  return `no MessageRef cached for id=${this.id}; pass channel_name (and thread for thread-scoped messages) to reconstruct`;
58853
58927
  }
58854
58928
  }
58855
- var reconstructMessageRef = (cache, rawId, channelNameArg, threadNameArg) => exports_Effect.gen(function* () {
58929
+ var reconstructMessageRef = (cache, rawId, channelNameArg) => exports_Effect.gen(function* () {
58856
58930
  const id3 = yield* decodeMessageId(rawId);
58857
58931
  const cached5 = cache.messageById.get(id3);
58858
58932
  if (cached5 !== undefined)
@@ -58860,11 +58934,8 @@ var reconstructMessageRef = (cache, rawId, channelNameArg, threadNameArg) => exp
58860
58934
  if (channelNameArg === undefined) {
58861
58935
  return yield* new UnknownMessageError({ id: rawId });
58862
58936
  }
58863
- const channel = yield* resolveChannel(cache, channelNameArg);
58864
- if (threadNameArg !== undefined) {
58865
- return { id: id3, channel, thread: { name: yield* decodeThreadName(threadNameArg) } };
58866
- }
58867
- return { id: id3, channel };
58937
+ const channel = yield* addressChannel(cache, yield* decodeChannelName(channelNameArg));
58938
+ return { id: id3, channel, thread: exports_Option.none(), permalink: yield* decodeMessagePermalink(id3) };
58868
58939
  });
58869
58940
  var resolveMentions = (cache, mentionIds) => exports_Effect.forEach(mentionIds, (raw3) => exports_Effect.gen(function* () {
58870
58941
  const identity6 = cache.identityById.get(yield* decodeIdentityId(raw3));
@@ -58873,15 +58944,20 @@ var resolveMentions = (cache, mentionIds) => exports_Effect.forEach(mentionIds,
58873
58944
  }
58874
58945
  return identity6;
58875
58946
  }));
58876
- var messageShape = (m) => ({
58947
+ var encodeThreadFacet = exports_Schema.encode(exports_Schema.OptionFromNullOr(ObservedThreadSchema));
58948
+ var encodeChannel = exports_Schema.encode(ChannelRefSchema);
58949
+ var encodeMessagePermalink = exports_Schema.encode(MessagePermalinkSchema);
58950
+ var decodeChannelPermalink = exports_Schema.decode(ChannelPermalinkSchema);
58951
+ var decodeMessagePermalink = exports_Schema.decode(MessagePermalinkSchema);
58952
+ var messageShape = (m) => exports_Effect.all({
58953
+ channel: encodeChannel(m.ref.channel),
58954
+ thread: encodeThreadFacet(m.ref.thread),
58955
+ permalink: encodeMessagePermalink(m.ref.permalink)
58956
+ }).pipe(exports_Effect.map(({ channel, thread, permalink }) => ({
58877
58957
  id: m.ref.id,
58878
- channel: {
58879
- id: m.ref.channel.id,
58880
- name: m.ref.channel.name,
58881
- permalink: m.ref.channel.permalink ?? null
58882
- },
58883
- thread: m.ref.thread === undefined ? null : { name: m.ref.thread.name, permalink: m.ref.thread.permalink ?? null },
58884
- permalink: m.ref.permalink ?? null,
58958
+ channel,
58959
+ thread,
58960
+ permalink,
58885
58961
  sender: identityShape(m.sender),
58886
58962
  body: m.body,
58887
58963
  ts: m.ts,
@@ -58890,14 +58966,16 @@ var messageShape = (m) => ({
58890
58966
  emoji: r.emoji,
58891
58967
  by: r.by.map(identityShape)
58892
58968
  }))
58893
- });
58894
- var resolveChannel = (cache, rawName) => exports_Effect.gen(function* () {
58895
- const name = yield* decodeChannelName(rawName);
58969
+ })));
58970
+ var addressChannel = (cache, name) => exports_Effect.gen(function* () {
58896
58971
  const cached5 = cache.channelByName.get(name);
58897
58972
  if (cached5 !== undefined)
58898
58973
  return cached5;
58899
- const synthetic = { id: yield* decodeChannelId(rawName), name };
58900
- return synthetic;
58974
+ return {
58975
+ id: yield* decodeChannelId(name),
58976
+ name,
58977
+ permalink: yield* decodeChannelPermalink(name)
58978
+ };
58901
58979
  });
58902
58980
  var parseRange = (range5) => exports_Effect.gen(function* () {
58903
58981
  const out = {};
@@ -58946,6 +59024,10 @@ var ReadThreadArgs = exports_Schema.Struct({
58946
59024
  thread: exports_Schema.String,
58947
59025
  ...RangeArgs
58948
59026
  });
59027
+ var ThreadResolutionArgs = exports_Schema.Struct({
59028
+ channel_name: exports_Schema.String,
59029
+ thread: exports_Schema.String
59030
+ });
58949
59031
  var MessageLinkArgs = exports_Schema.Struct({
58950
59032
  message_id: exports_Schema.String,
58951
59033
  channel_name: exports_Schema.optional(exports_Schema.String),
@@ -58959,11 +59041,11 @@ var buildToolDefs = (deps, cache) => {
58959
59041
  const projectForCwd = deps.projectForCwd ?? (() => exports_Effect.succeed(undefined));
58960
59042
  const sessionIdField = {
58961
59043
  type: "string",
58962
- description: "Per-conversation identifier (UUID). In Claude Code, the plugin's PreToolUse hook injects this from the harness session id; non-CC MCP clients must supply a UUID (e.g. via crypto.randomUUID()). Anything that fails UUID validation is treated as missing — the server returns the unbound-stub error rather than minting a malformed cc-* identity (comms-uqf). Drives ephemeral identity minting (ass-2dhb)."
59044
+ description: "Per-conversation identifier (UUID). In Claude Code, the plugin's PreToolUse hook injects this from the harness session id; non-CC MCP clients must supply a UUID (e.g. via crypto.randomUUID()). Anything that fails UUID validation is treated as missing — the server returns the unbound-stub error rather than minting a malformed cc-* identity. Drives ephemeral identity minting."
58963
59045
  };
58964
59046
  const cwdField = {
58965
59047
  type: "string",
58966
- description: "Calling session's working directory. In Claude Code, the PreToolUse hook injects this from the harness cwd; non-CC MCP clients may supply their own. Drives the project component of ephemeral cc-<project>-<8> names (ass-v7b4) — without this the minted name falls back to bare cc-<8> (or the operator-forced COMMY_PROJECT slug)."
59048
+ description: "Calling session's working directory. In Claude Code, the PreToolUse hook injects this from the harness cwd; non-CC MCP clients may supply their own. Drives the project component of ephemeral cc-<project>-<8> names — without this the minted name falls back to bare cc-<8> (or the operator-forced COMMY_PROJECT slug)."
58967
59049
  };
58968
59050
  const readSessionId = (args2) => exports_Option.getOrUndefined(parseSessionId(args2["session_id"]));
58969
59051
  const readCwd = (args2) => {
@@ -58971,16 +59053,16 @@ var buildToolDefs = (deps, cache) => {
58971
59053
  return exports_Predicate.isString(raw3) ? raw3 : undefined;
58972
59054
  };
58973
59055
  const projectForArgs = (args2) => projectForCwd(readCwd(args2));
58974
- const stickyThreadEngagement = async (ref) => {
58975
- if (ref.thread === undefined)
59056
+ const stickyThreadEngagement = async (channel, threadName3) => {
59057
+ if (exports_Option.isNone(threadName3))
58976
59058
  return;
58977
59059
  const intent = {
58978
59060
  kind: "thread",
58979
- channelName: ref.channel.name,
58980
- threadName: ref.thread.name
59061
+ channelName: channel.name,
59062
+ threadName: threadName3.value
58981
59063
  };
58982
59064
  narrowSet.add(intent);
58983
- await runEdge(intentToTarget(intent).pipe(exports_Effect.flatMap((target) => adapter4.inbox.subscribe(target))));
59065
+ await runEdge(adapter4.inbox.subscribe(intentToTarget(intent)));
58984
59066
  };
58985
59067
  const coreTools = [
58986
59068
  {
@@ -59068,12 +59150,12 @@ var buildToolDefs = (deps, cache) => {
59068
59150
  const channels = await runEdge(adapter4.directory.listChannels());
59069
59151
  for (const c of channels)
59070
59152
  cache.rememberChannel(c);
59071
- return { channels: channels.map(channelShape) };
59153
+ return { channels: await runEdge(exports_Effect.forEach(channels, (c) => encodeChannel(c))) };
59072
59154
  }
59073
59155
  },
59074
59156
  {
59075
59157
  name: "post",
59076
- description: "Post a message to a channel by name. Optional thread (topic), mentions (identity ids the bot has seen), and reply_to (message id). To ping someone on Zulip, write the @**Name** markup inline in body where you want it rendered — the mentions array is notification metadata only and does NOT modify body. Returns {message_id, channel_id, channel_name, thread, permalink}. When you show a human this message afterwards, link it by the returned permalink — never a bare name or message id.",
59158
+ description: "Post a message to a channel by name. Optional thread (topic), mentions (identity ids the bot has seen), and reply_to (message id). To ping someone on Zulip, write the @**Name** markup inline in body where you want it rendered — the mentions array is notification metadata only and does not modify body. Returns {message_id, channel_id, channel_name, thread, permalink}. When you show a human this message afterwards, link it by the returned permalink — never a bare name or message id.",
59077
59159
  inputSchema: {
59078
59160
  type: "object",
59079
59161
  properties: {
@@ -59102,28 +59184,31 @@ var buildToolDefs = (deps, cache) => {
59102
59184
  await runEdge(exports_Effect.flatMap(projectForArgs(args2), (project3) => identityCache.ensureBoundFor(readSessionId(args2), project3)).pipe(exports_Effect.flatMap((ensureBound) => ensureBound())));
59103
59185
  const ref = await runEdge(exports_Effect.gen(function* () {
59104
59186
  const { channel_name, body, thread, mentions, reply_to } = yield* exports_Schema.decodeUnknown(PostArgs)(args2);
59105
- const channel = yield* resolveChannel(cache, channel_name);
59187
+ const channel = yield* decodeChannelName(channel_name);
59106
59188
  const messageBody = yield* decodeMessageBody(body);
59107
59189
  const opts = {};
59108
59190
  if (thread !== undefined) {
59109
- opts.thread = { name: yield* decodeThreadName(thread) };
59191
+ opts.thread = yield* decodeThreadName(thread);
59110
59192
  }
59111
59193
  if (mentions !== undefined) {
59112
59194
  opts.mentions = yield* resolveMentions(cache, mentions);
59113
59195
  }
59114
59196
  if (reply_to !== undefined) {
59115
- opts.replyTo = yield* reconstructMessageRef(cache, reply_to, channel_name, thread);
59197
+ opts.replyTo = yield* reconstructMessageRef(cache, reply_to, channel_name);
59116
59198
  }
59117
59199
  return yield* adapter4.publisher.post(channel, messageBody, opts);
59118
59200
  }));
59119
59201
  cache.rememberMessage(ref);
59120
- await stickyThreadEngagement(ref);
59202
+ await stickyThreadEngagement(ref.channel, exports_Option.map(ref.thread, (t) => t.name));
59121
59203
  return {
59122
59204
  message_id: ref.id,
59123
59205
  channel_id: ref.channel.id,
59124
59206
  channel_name: ref.channel.name,
59125
- thread: ref.thread === undefined ? null : { name: ref.thread.name },
59126
- permalink: ref.permalink ?? null
59207
+ thread: exports_Option.match(ref.thread, {
59208
+ onNone: () => null,
59209
+ onSome: (t) => ({ name: t.name })
59210
+ }),
59211
+ permalink: ref.permalink
59127
59212
  };
59128
59213
  }
59129
59214
  },
@@ -59152,8 +59237,8 @@ var buildToolDefs = (deps, cache) => {
59152
59237
  handler: async (args2) => {
59153
59238
  await runEdge(exports_Effect.flatMap(projectForArgs(args2), (project3) => identityCache.ensureBoundFor(readSessionId(args2), project3)).pipe(exports_Effect.flatMap((ensureBound) => ensureBound())));
59154
59239
  const ref = await runEdge(exports_Effect.gen(function* () {
59155
- const { message_id, body, channel_name, thread } = yield* exports_Schema.decodeUnknown(EditMessageArgs)(args2);
59156
- const target = yield* reconstructMessageRef(cache, message_id, channel_name, thread);
59240
+ const { message_id, body, channel_name } = yield* exports_Schema.decodeUnknown(EditMessageArgs)(args2);
59241
+ const target = yield* reconstructMessageRef(cache, message_id, channel_name);
59157
59242
  yield* adapter4.publisher.edit(target, yield* decodeMessageBody(body));
59158
59243
  return target;
59159
59244
  }));
@@ -59185,14 +59270,16 @@ var buildToolDefs = (deps, cache) => {
59185
59270
  },
59186
59271
  handler: async (args2) => {
59187
59272
  await runEdge(exports_Effect.flatMap(projectForArgs(args2), (project3) => identityCache.ensureBoundFor(readSessionId(args2), project3)).pipe(exports_Effect.flatMap((ensureBound) => ensureBound())));
59188
- const ref = await runEdge(exports_Effect.gen(function* () {
59273
+ const { ref, threadName: threadName3 } = await runEdge(exports_Effect.gen(function* () {
59189
59274
  const { message_id, emoji: emoji2, channel_name, thread } = yield* exports_Schema.decodeUnknown(ReactArgs)(args2);
59190
- const target = yield* reconstructMessageRef(cache, message_id, channel_name, thread);
59275
+ const target = yield* reconstructMessageRef(cache, message_id, channel_name);
59191
59276
  yield* adapter4.publisher.react(target, yield* decodeEmoji(emoji2));
59192
- return target;
59277
+ const argThread = yield* exports_Effect.transposeMapOption(exports_Option.fromNullable(thread), decodeThreadName);
59278
+ const threadName4 = exports_Option.orElse(exports_Option.map(target.thread, (t) => t.name), () => argThread);
59279
+ return { ref: target, threadName: threadName4 };
59193
59280
  }));
59194
59281
  cache.rememberMessage(ref);
59195
- await stickyThreadEngagement(ref);
59282
+ await stickyThreadEngagement(ref.channel, threadName3);
59196
59283
  return {};
59197
59284
  }
59198
59285
  },
@@ -59215,8 +59302,8 @@ var buildToolDefs = (deps, cache) => {
59215
59302
  handler: async (args2) => {
59216
59303
  await runEdge(exports_Effect.flatMap(projectForArgs(args2), (project3) => identityCache.ensureBoundFor(readSessionId(args2), project3)).pipe(exports_Effect.flatMap((ensureBound) => ensureBound())));
59217
59304
  await runEdge(exports_Effect.gen(function* () {
59218
- const { message_id, emoji: emoji2, channel_name, thread } = yield* exports_Schema.decodeUnknown(ReactArgs)(args2);
59219
- const ref = yield* reconstructMessageRef(cache, message_id, channel_name, thread);
59305
+ const { message_id, emoji: emoji2, channel_name } = yield* exports_Schema.decodeUnknown(ReactArgs)(args2);
59306
+ const ref = yield* reconstructMessageRef(cache, message_id, channel_name);
59220
59307
  yield* adapter4.publisher.unreact(ref, yield* decodeEmoji(emoji2));
59221
59308
  }));
59222
59309
  return {};
@@ -59247,8 +59334,7 @@ var buildToolDefs = (deps, cache) => {
59247
59334
  yield* deps.ensureSessionSubscriptions(sessionId, yield* projectForArgs(args2));
59248
59335
  }
59249
59336
  narrowSet.add(intent);
59250
- const target = yield* intentToTarget(intent);
59251
- yield* adapter4.inbox.subscribe(target);
59337
+ yield* adapter4.inbox.subscribe(intentToTarget(intent));
59252
59338
  if (sessionId !== undefined && deps.persistSessionSubscriptions !== undefined) {
59253
59339
  yield* deps.persistSessionSubscriptions(sessionId);
59254
59340
  }
@@ -59281,8 +59367,7 @@ var buildToolDefs = (deps, cache) => {
59281
59367
  yield* deps.ensureSessionSubscriptions(sessionId, yield* projectForArgs(args2));
59282
59368
  }
59283
59369
  narrowSet.remove(intent);
59284
- const target = yield* intentToTarget(intent);
59285
- yield* adapter4.inbox.unsubscribe(target);
59370
+ yield* adapter4.inbox.unsubscribe(intentToTarget(intent));
59286
59371
  if (sessionId !== undefined && deps.persistSessionSubscriptions !== undefined) {
59287
59372
  yield* deps.persistSessionSubscriptions(sessionId);
59288
59373
  }
@@ -59305,14 +59390,14 @@ var buildToolDefs = (deps, cache) => {
59305
59390
  handler: async (args2) => {
59306
59391
  const messages = await runEdge(exports_Effect.gen(function* () {
59307
59392
  const parsed = yield* exports_Schema.decodeUnknown(ReadChannelArgs)(args2);
59308
- const channelRef = yield* resolveChannel(cache, parsed.channel_name);
59309
- return yield* adapter4.history.readChannel(channelRef, yield* parseRange(parsed));
59393
+ const channel = yield* decodeChannelName(parsed.channel_name);
59394
+ return yield* adapter4.history.readChannel(channel, yield* parseRange(parsed));
59310
59395
  }));
59311
59396
  for (const m of messages) {
59312
59397
  cache.rememberChannel(m.ref.channel);
59313
59398
  cache.rememberIdentity(m.sender);
59314
59399
  }
59315
- return { messages: messages.map(messageShape) };
59400
+ return { messages: await runEdge(exports_Effect.forEach(messages, messageShape)) };
59316
59401
  }
59317
59402
  },
59318
59403
  {
@@ -59331,14 +59416,54 @@ var buildToolDefs = (deps, cache) => {
59331
59416
  handler: async (args2) => {
59332
59417
  const messages = await runEdge(exports_Effect.gen(function* () {
59333
59418
  const parsed = yield* exports_Schema.decodeUnknown(ReadThreadArgs)(args2);
59334
- const channelRef = yield* resolveChannel(cache, parsed.channel_name);
59335
- return yield* adapter4.history.readThread(channelRef, yield* decodeThreadName(parsed.thread), yield* parseRange(parsed));
59419
+ const channel = yield* decodeChannelName(parsed.channel_name);
59420
+ return yield* adapter4.history.readThread(channel, yield* decodeThreadName(parsed.thread), yield* parseRange(parsed));
59336
59421
  }));
59337
59422
  for (const m of messages) {
59338
59423
  cache.rememberChannel(m.ref.channel);
59339
59424
  cache.rememberIdentity(m.sender);
59340
59425
  }
59341
- return { messages: messages.map(messageShape) };
59426
+ return { messages: await runEdge(exports_Effect.forEach(messages, messageShape)) };
59427
+ }
59428
+ },
59429
+ {
59430
+ name: "resolve_thread",
59431
+ description: "Mark a thread resolved (channel_name + thread, like read_thread). Idempotent — resolving an already-resolved thread is a no-op. Resolution is a status kept separate from the thread name; read it back as a message's thread.resolved via read_thread / read_channel. Use unresolve_thread to clear it.",
59432
+ inputSchema: {
59433
+ type: "object",
59434
+ properties: {
59435
+ channel_name: { type: "string", description: "Channel the thread lives in" },
59436
+ thread: { type: "string", description: "Thread / topic name within the channel" }
59437
+ },
59438
+ required: ["channel_name", "thread"],
59439
+ additionalProperties: false
59440
+ },
59441
+ handler: async (args2) => {
59442
+ await runEdge(exports_Effect.gen(function* () {
59443
+ const parsed = yield* exports_Schema.decodeUnknown(ThreadResolutionArgs)(args2);
59444
+ yield* adapter4.publisher.resolveThread(yield* decodeChannelName(parsed.channel_name), yield* decodeThreadName(parsed.thread));
59445
+ }));
59446
+ return {};
59447
+ }
59448
+ },
59449
+ {
59450
+ name: "unresolve_thread",
59451
+ description: "Clear a thread's resolved status (channel_name + thread, like read_thread). Idempotent — unresolving a thread that is not resolved is a no-op. The inverse of resolve_thread.",
59452
+ inputSchema: {
59453
+ type: "object",
59454
+ properties: {
59455
+ channel_name: { type: "string", description: "Channel the thread lives in" },
59456
+ thread: { type: "string", description: "Thread / topic name within the channel" }
59457
+ },
59458
+ required: ["channel_name", "thread"],
59459
+ additionalProperties: false
59460
+ },
59461
+ handler: async (args2) => {
59462
+ await runEdge(exports_Effect.gen(function* () {
59463
+ const parsed = yield* exports_Schema.decodeUnknown(ThreadResolutionArgs)(args2);
59464
+ yield* adapter4.publisher.unresolveThread(yield* decodeChannelName(parsed.channel_name), yield* decodeThreadName(parsed.thread));
59465
+ }));
59466
+ return {};
59342
59467
  }
59343
59468
  },
59344
59469
  {
@@ -59365,7 +59490,7 @@ var buildToolDefs = (deps, cache) => {
59365
59490
  const parsed = yield* exports_Schema.decodeUnknown(MessageLinkArgs)(args2);
59366
59491
  const id3 = yield* decodeMessageId(parsed.message_id);
59367
59492
  const cached5 = cache.messageById.get(id3);
59368
- if (cached5?.permalink !== undefined)
59493
+ if (cached5 !== undefined)
59369
59494
  return cached5.permalink;
59370
59495
  const hint = parsed.channel_name === undefined ? undefined : {
59371
59496
  channel: yield* decodeChannelName(parsed.channel_name),
@@ -59515,7 +59640,7 @@ var forkIdleSweep = (cache, intervalMs) => {
59515
59640
  return exports_Effect.asVoid(exports_Effect.forkScoped(exports_Effect.repeat(sweep, exports_Schedule.spaced(exports_Duration.millis(intervalMs)))));
59516
59641
  };
59517
59642
  var createType2DefaultsOnAcquire = (narrowSet, inbox) => {
59518
- const registerIntent = (intent) => exports_Effect.sync(() => narrowSet.add(intent)).pipe(exports_Effect.zipRight(intentToTarget(intent).pipe(exports_Effect.flatMap((target) => inbox.subscribe(target)))));
59643
+ const registerIntent = (intent) => exports_Effect.sync(() => narrowSet.add(intent)).pipe(exports_Effect.zipRight(inbox.subscribe(intentToTarget(intent))));
59519
59644
  return (project3) => registerIntent({ kind: "mentions" }).pipe(exports_Effect.zipRight(project3 !== undefined ? exports_Effect.all([decodeChannelName(project3), decodeThreadName("general")]).pipe(exports_Effect.flatMap(([channelName, threadName3]) => registerIntent({ kind: "thread", channelName, threadName: threadName3 }))) : exports_Effect.void), exports_Effect.catchAll((err) => exports_Effect.logError(`commy plugin: Type-2 default narrow registration failed: ${exports_Cause.pretty(exports_Cause.fail(err))}`)));
59520
59645
  };
59521
59646
  var logType1Failure = (err) => exports_Effect.logError(`commy plugin: Type-1 default narrow registration failed: ${exports_Cause.pretty(exports_Cause.fail(err))}`);
@@ -59523,7 +59648,7 @@ var registerType1DefaultsOnBoot = (inbox, narrowSet, project3) => (project3 !==
59523
59648
  { kind: "mentions" },
59524
59649
  { kind: "new-topics-in-channel", channelName },
59525
59650
  { kind: "thread", channelName, threadName: threadName3 }
59526
- ])) : exports_Effect.succeed([{ kind: "mentions" }])).pipe(exports_Effect.flatMap((intents) => exports_Effect.forEach(intents, (intent) => exports_Effect.sync(() => narrowSet.add(intent)).pipe(exports_Effect.zipRight(intentToTarget(intent).pipe(exports_Effect.flatMap((target) => inbox.subscribe(target)))))).pipe(exports_Effect.as(intents), exports_Effect.catchAll((err) => logType1Failure(err).pipe(exports_Effect.as(intents))))), exports_Effect.catchAll((err) => logType1Failure(err).pipe(exports_Effect.as([{ kind: "mentions" }]))));
59651
+ ])) : exports_Effect.succeed([{ kind: "mentions" }])).pipe(exports_Effect.flatMap((intents) => exports_Effect.forEach(intents, (intent) => exports_Effect.sync(() => narrowSet.add(intent)).pipe(exports_Effect.zipRight(inbox.subscribe(intentToTarget(intent))))).pipe(exports_Effect.as(intents), exports_Effect.catchAll((err) => logType1Failure(err).pipe(exports_Effect.as(intents))))), exports_Effect.catchAll((err) => logType1Failure(err).pipe(exports_Effect.as([{ kind: "mentions" }]))));
59527
59652
  var DEFAULT_CATCHUP_WINDOW_SECONDS = 4 * 3600;
59528
59653
  var buildIdentityCache = (adapter4, botName, ephemeralOnAcquire) => {
59529
59654
  if (botName !== undefined) {
@@ -59569,14 +59694,11 @@ var makeProgram = (params = {}) => exports_Effect.scoped(exports_Effect.gen(func
59569
59694
  const mcp = buildMcpServer();
59570
59695
  const notifier = params.notifier ?? channelNotifier(mcp);
59571
59696
  const registerType2Defaults = parsed.botName === undefined ? createType2DefaultsOnAcquire(narrowSet, adapter4.inbox) : undefined;
59572
- const restoredSessions = yield* exports_SynchronizedRef.make(exports_HashSet.empty());
59697
+ const sessionRestoreFeed = parsed.botName === undefined ? yield* makeSessionRestore({ subscriptionStore, narrowSet, inbox: adapter4.inbox }) : undefined;
59573
59698
  const seedDefaults = (project3) => registerType2Defaults !== undefined ? registerType2Defaults(project3) : exports_Effect.void;
59574
- const ensureSessionSubscriptions = (sessionId, project3) => parsed.botName !== undefined ? exports_Effect.void : exports_SynchronizedRef.updateEffect(restoredSessions, (seen) => exports_HashSet.has(seen, sessionId) ? exports_Effect.succeed(seen) : restoreOrSeedSubscriptions({
59575
- subscriptionStore,
59576
- narrowSet,
59577
- inbox: adapter4.inbox,
59578
- registerDefaults: seedDefaults
59579
- }, sessionId, project3).pipe(exports_Effect.catchAll((err) => exports_Effect.logError(`commy plugin: subscription restore failed for ${sessionId}: ${exports_Predicate.isError(err) ? err.message : String(err)}`).pipe(exports_Effect.zipRight(seedDefaults(project3)))), exports_Effect.provide(loggerLayer), exports_Effect.as(exports_HashSet.add(seen, sessionId))));
59699
+ const feedSession = (sessionId) => sessionRestoreFeed === undefined ? exports_Effect.void : sessionRestoreFeed(sessionId).pipe(exports_Effect.catchAll((err) => exports_Effect.logError(`commy plugin: subscription restore failed for ${sessionId}: ${exports_Predicate.isError(err) ? err.message : String(err)}`)), exports_Effect.provide(loggerLayer));
59700
+ const seedIfFresh = (sessionId, project3) => parsed.botName !== undefined ? exports_Effect.void : seedDefaultsIfFresh({ subscriptionStore, registerDefaults: seedDefaults }, sessionId, project3).pipe(exports_Effect.catchAll((err) => exports_Effect.logError(`commy plugin: subscription seed check failed for ${sessionId}: ${exports_Predicate.isError(err) ? err.message : String(err)}`).pipe(exports_Effect.zipRight(seedDefaults(project3)))), exports_Effect.provide(loggerLayer));
59701
+ const ensureSessionSubscriptions = (sessionId, project3) => feedSession(sessionId).pipe(exports_Effect.zipRight(seedIfFresh(sessionId, project3)));
59580
59702
  const persistSessionSubscriptions = (sessionId) => parsed.botName !== undefined ? exports_Effect.void : persistSubscriptions(subscriptionStore, narrowSet, sessionId).pipe(exports_Effect.catchAll((err) => exports_Effect.logError(`commy plugin: subscription persist failed for ${sessionId}: ${exports_Predicate.isError(err) ? err.message : String(err)}`)), exports_Effect.provide(loggerLayer));
59581
59703
  const ephemeralOnAcquire = parsed.botName === undefined ? (acquired, project3, sessionId) => ensureSessionSubscriptions(sessionId, project3).pipe(exports_Effect.zipRight(catchUpMentions({
59582
59704
  cursorStore,