@codeforbreakfast/commy-mcp 0.18.0 → 0.19.1

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 +227 -107
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeforbreakfast/commy-mcp",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
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
@@ -3048,13 +3048,14 @@ var require_data = __commonJS((exports, module) => {
3048
3048
  };
3049
3049
  });
3050
3050
 
3051
- // node_modules/.bun/fast-uri@4.0.0/node_modules/fast-uri/lib/utils.js
3051
+ // node_modules/.bun/fast-uri@4.1.0/node_modules/fast-uri/lib/utils.js
3052
3052
  var require_utils = __commonJS((exports, module) => {
3053
3053
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3054
3054
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3055
3055
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3056
3056
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3057
3057
  var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3058
+ var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/?]$/iu);
3058
3059
  function stringArrayToHexStripped(input) {
3059
3060
  let acc = "";
3060
3061
  let code = 0;
@@ -3198,7 +3199,7 @@ var require_utils = __commonJS((exports, module) => {
3198
3199
  continue;
3199
3200
  }
3200
3201
  } else if (input[0] === "/") {
3201
- if (input[1] === "." || input[1] === "/") {
3202
+ if (input[1] === ".") {
3202
3203
  output.push("/");
3203
3204
  break;
3204
3205
  }
@@ -3340,6 +3341,47 @@ var require_utils = __commonJS((exports, module) => {
3340
3341
  }
3341
3342
  return output;
3342
3343
  }
3344
+ function normalizeQueryFragmentEncoding(input) {
3345
+ let output = "";
3346
+ for (let i = 0;i < input.length; i++) {
3347
+ const ch = input[i];
3348
+ if (ch === "%" && i + 2 < input.length) {
3349
+ const hex = input.slice(i + 1, i + 3);
3350
+ if (isHexPair(hex)) {
3351
+ const normalizedHex = hex.toUpperCase();
3352
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3353
+ if (isUnreserved(decoded)) {
3354
+ output += decoded;
3355
+ } else {
3356
+ output += "%" + normalizedHex;
3357
+ }
3358
+ i += 2;
3359
+ continue;
3360
+ }
3361
+ }
3362
+ if (isQueryFragmentCharacter(ch)) {
3363
+ output += ch;
3364
+ } else {
3365
+ const code = input.charCodeAt(i);
3366
+ if (code < 128) {
3367
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3368
+ } else if (code < 55296 || code > 57343) {
3369
+ output += percentEncodeNonAscii(code);
3370
+ } else if (code <= 56319 && i + 1 < input.length) {
3371
+ const low = input.charCodeAt(i + 1);
3372
+ if (low >= 56320 && low <= 57343) {
3373
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3374
+ i++;
3375
+ } else {
3376
+ output += percentEncodeNonAscii(65533);
3377
+ }
3378
+ } else {
3379
+ output += percentEncodeNonAscii(65533);
3380
+ }
3381
+ }
3382
+ }
3383
+ return output;
3384
+ }
3343
3385
  function escapePreservingEscapes(input) {
3344
3386
  let output = "";
3345
3387
  for (let i = 0;i < input.length; i++) {
@@ -3401,6 +3443,7 @@ var require_utils = __commonJS((exports, module) => {
3401
3443
  reescapeHostDelimiters,
3402
3444
  normalizePercentEncoding,
3403
3445
  normalizePathEncoding,
3446
+ normalizeQueryFragmentEncoding,
3404
3447
  escapePreservingEscapes,
3405
3448
  removeDotSegments,
3406
3449
  isIPv4,
@@ -3410,7 +3453,7 @@ var require_utils = __commonJS((exports, module) => {
3410
3453
  };
3411
3454
  });
3412
3455
 
3413
- // node_modules/.bun/fast-uri@4.0.0/node_modules/fast-uri/lib/schemes.js
3456
+ // node_modules/.bun/fast-uri@4.1.0/node_modules/fast-uri/lib/schemes.js
3414
3457
  var require_schemes = __commonJS((exports, module) => {
3415
3458
  var { isUUID } = require_utils();
3416
3459
  var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
@@ -3584,9 +3627,9 @@ var require_schemes = __commonJS((exports, module) => {
3584
3627
  };
3585
3628
  });
3586
3629
 
3587
- // node_modules/.bun/fast-uri@4.0.0/node_modules/fast-uri/index.js
3630
+ // node_modules/.bun/fast-uri@4.1.0/node_modules/fast-uri/index.js
3588
3631
  var require_fast_uri = __commonJS((exports, module) => {
3589
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3632
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, normalizeQueryFragmentEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3590
3633
  var { SCHEMES, getSchemeHandler } = require_schemes();
3591
3634
  function normalize3(uri, options5) {
3592
3635
  if (typeof uri === "string") {
@@ -3755,7 +3798,7 @@ var require_fast_uri = __commonJS((exports, module) => {
3755
3798
  }
3756
3799
  const matches = uri.match(URI_PARSE);
3757
3800
  if (matches) {
3758
- parsed.scheme = matches[1];
3801
+ parsed.scheme = matches[1] === undefined ? undefined : matches[1].toLowerCase();
3759
3802
  parsed.userinfo = matches[3];
3760
3803
  parsed.host = matches[4];
3761
3804
  parsed.port = parseInt(matches[5], 10);
@@ -3796,7 +3839,7 @@ var require_fast_uri = __commonJS((exports, module) => {
3796
3839
  if (!options5.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3797
3840
  if (parsed.host && (options5.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3798
3841
  try {
3799
- parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
3842
+ parsed.host = new URL("http://" + parsed.host).hostname;
3800
3843
  } catch (e) {
3801
3844
  parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3802
3845
  }
@@ -3814,12 +3857,11 @@ var require_fast_uri = __commonJS((exports, module) => {
3814
3857
  if (parsed.path) {
3815
3858
  parsed.path = normalizePathEncoding(parsed.path);
3816
3859
  }
3860
+ if (parsed.query) {
3861
+ parsed.query = normalizeQueryFragmentEncoding(parsed.query);
3862
+ }
3817
3863
  if (parsed.fragment) {
3818
- try {
3819
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3820
- } catch {
3821
- parsed.error = parsed.error || "URI malformed";
3822
- }
3864
+ parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
3823
3865
  }
3824
3866
  }
3825
3867
  if (schemeHandler && schemeHandler.parse) {
@@ -47638,9 +47680,11 @@ var reactionToInboundEvent = (reaction, directory, target) => exports_Effect.gen
47638
47680
  by
47639
47681
  };
47640
47682
  });
47641
- var registerQueue = (http, mode) => {
47683
+ var MAX_QUEUE_TIMEOUT_SECS = 604800;
47684
+ var registerQueue = (http, mode, idleTimeoutSecs) => {
47642
47685
  const baseParams = { event_types: JSON.stringify(["message", "reaction"]) };
47643
- const params = mode === "mentions" ? { ...baseParams, narrow: JSON.stringify([["is", "mentioned"]]) } : baseParams;
47686
+ const modeParams = mode === "mentions" ? { ...baseParams, narrow: JSON.stringify([["is", "mentioned"]]) } : baseParams;
47687
+ const params = idleTimeoutSecs === undefined ? modeParams : { ...modeParams, idle_queue_timeout: idleTimeoutSecs };
47644
47688
  return http.post("/register", registerResponseSchema, params).pipe(exports_Effect.map((res) => ({ queueId: res.queue_id, lastEventId: res.last_event_id })));
47645
47689
  };
47646
47690
  var populateCacheFromInboundEvents = (cache, events) => {
@@ -47711,6 +47755,13 @@ var inboxEvents = (config2) => exports_Stream.unwrap(exports_Effect.gen(function
47711
47755
  consecutiveFailures: 0,
47712
47756
  needsBreadcrumb: false
47713
47757
  });
47758
+ const resumeReported = yield* exports_Ref.make(false);
47759
+ const reportResume = (queueReplayed) => {
47760
+ const onResumeOutcome = config2.onResumeOutcome;
47761
+ if (onResumeOutcome === undefined)
47762
+ return exports_Effect.void;
47763
+ return exports_Ref.getAndSet(resumeReported, true).pipe(exports_Effect.flatMap((already) => already ? exports_Effect.void : onResumeOutcome(queueReplayed)));
47764
+ };
47714
47765
  const recordTransientFailure = (cause3) => exports_Ref.modify(breadcrumb, (s) => {
47715
47766
  const consecutiveFailures = s.consecutiveFailures + 1;
47716
47767
  return [consecutiveFailures, { consecutiveFailures, needsBreadcrumb: true }];
@@ -47723,6 +47774,7 @@ var inboxEvents = (config2) => exports_Stream.unwrap(exports_Effect.gen(function
47723
47774
  }
47724
47775
  })));
47725
47776
  const handleBadQueue = exports_Effect.gen(function* () {
47777
+ yield* reportResume(false);
47726
47778
  const since = yield* watermark.get();
47727
47779
  if (config2.replay === undefined || exports_Option.isNone(since)) {
47728
47780
  return [[], undefined];
@@ -47736,11 +47788,12 @@ var inboxEvents = (config2) => exports_Stream.unwrap(exports_Effect.gen(function
47736
47788
  return [markReplayed(gapEither.right), undefined];
47737
47789
  });
47738
47790
  const step4 = (queue) => exports_Effect.gen(function* () {
47739
- const currentQueue = queue ?? (yield* registerQueue(config2.http, config2.mode));
47791
+ const currentQueue = queue ?? (yield* registerQueue(config2.http, config2.mode, config2.queueIdleTimeoutSecs).pipe(exports_Effect.tap((q) => config2.onQueueRegister?.(q) ?? exports_Effect.void)));
47740
47792
  const res = yield* config2.http.get("/events", eventsResponseSchema, {
47741
47793
  queue_id: currentQueue.queueId,
47742
47794
  last_event_id: currentQueue.lastEventId
47743
47795
  });
47796
+ yield* reportResume(true);
47744
47797
  const directory = res.events.length > 0 ? yield* config2.resolveDirectory() : undefined;
47745
47798
  let maxId = currentQueue.lastEventId;
47746
47799
  const mapped = [];
@@ -47763,6 +47816,9 @@ var inboxEvents = (config2) => exports_Stream.unwrap(exports_Effect.gen(function
47763
47816
  yield* watermark.advance(ev.message.ts);
47764
47817
  }
47765
47818
  }
47819
+ if (config2.onQueueAdvance !== undefined && maxId > currentQueue.lastEventId) {
47820
+ yield* config2.onQueueAdvance(maxId);
47821
+ }
47766
47822
  return [mapped, { queueId: currentQueue.queueId, lastEventId: maxId }];
47767
47823
  }).pipe(exports_Effect.catchTag("ZulipApiError", (e) => {
47768
47824
  if (e.code === "BAD_EVENT_QUEUE_ID")
@@ -47940,6 +47996,7 @@ var sanitiseShortName = (name) => {
47940
47996
  const collapsed = name.toLowerCase().replace(SHORT_NAME_PATTERN, "-");
47941
47997
  return collapsed.replace(/^-+|-+$/g, "") || "bot";
47942
47998
  };
47999
+ var ACQUIRE_MINT_TIMEOUT = exports_Duration.seconds(30);
47943
48000
  var userSchema = exports_Schema.Struct({
47944
48001
  user_id: exports_Schema.Int,
47945
48002
  email: exports_Schema.String,
@@ -48212,7 +48269,7 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48212
48269
  }
48213
48270
  return exports_Effect.die(new Error(`zulipAdapter: already bound to ${existing.acquiredName} — release() before acquiring ${name}`));
48214
48271
  },
48215
- onNone: () => provideMintedFor(name).pipe(exports_Effect.flatMap((minted) => exports_Effect.gen(function* () {
48272
+ onNone: () => provideMintedFor(name).pipe(exports_Effect.timeout(ACQUIRE_MINT_TIMEOUT), exports_Effect.flatMap((minted) => exports_Effect.gen(function* () {
48216
48273
  const innerHttp = yield* makeZulipHttp(buildHttpConfig(config2.realmUrl, minted.email, minted.apiKey, config2.hostHeader)).pipe(exports_Effect.provideService(exports_HttpClient.HttpClient, httpClient));
48217
48274
  const http = wrapBotHttp(innerHttp, recipientDirectory, minted.userId);
48218
48275
  const ident = {
@@ -48406,7 +48463,7 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48406
48463
  if (exports_Option.exists(state.registration, (r) => r.mode === mode)) {
48407
48464
  return exports_Effect.succeed([undefined, state]);
48408
48465
  }
48409
- return registerQueue(minterHttp, mode).pipe(exports_Effect.map((q) => [
48466
+ return registerQueue(minterHttp, mode, config2.queueIdleTimeoutSecs).pipe(exports_Effect.tap((q) => config2.onQueueRegister?.(q) ?? exports_Effect.void), exports_Effect.map((q) => [
48410
48467
  undefined,
48411
48468
  { ...state, registration: exports_Option.some(exports_Data.struct({ queue: q, mode })) }
48412
48469
  ]));
@@ -48463,23 +48520,35 @@ var zulipAdapter = (config2) => exports_Effect.gen(function* () {
48463
48520
  subscriptions: JSON.stringify([channel])
48464
48521
  }).pipe(exports_Effect.asVoid)));
48465
48522
  }).pipe(exports_Effect.mapError((cause3) => new InboxError({ operation: "unsubscribe", cause: cause3 }))),
48466
- events: () => exports_Stream.unwrap(exports_Effect.all([exports_SynchronizedRef.get(boundRef), exports_SynchronizedRef.get(inboxRef)]).pipe(exports_Effect.map(([current2, state]) => inboxEvents({
48467
- http: minterHttp,
48468
- permalinkBase: base,
48469
- resolveDirectory: buildDirectoryLookup,
48470
- mode: currentMode(state),
48471
- messageRefCache,
48472
- watermarkStore,
48473
- replay: (since) => inbox.replay(since),
48474
- ...exports_Option.match(current2, {
48475
- onNone: () => ({}),
48476
- onSome: (b) => ({ boundIdentity: b.identity })
48477
- }),
48478
- ...exports_Option.match(state.registration, {
48479
- onNone: () => ({}),
48480
- onSome: (r) => ({ initialQueue: r.queue })
48481
- })
48482
- })))).pipe(exports_Stream.filterEffect(shouldDeliver)),
48523
+ events: () => exports_Stream.unwrap(exports_Effect.all([
48524
+ exports_SynchronizedRef.get(boundRef),
48525
+ exports_SynchronizedRef.get(inboxRef),
48526
+ config2.resumeQueue?.() ?? exports_Effect.succeedNone
48527
+ ]).pipe(exports_Effect.flatMap(([current2, state, resumed]) => {
48528
+ const initialQueue = exports_Option.orElse(resumed, () => exports_Option.map(state.registration, (r) => r.queue));
48529
+ const reportAbsentResume = exports_Option.isNone(resumed) ? config2.onResumeOutcome?.(false) ?? exports_Effect.void : exports_Effect.void;
48530
+ return reportAbsentResume.pipe(exports_Effect.as(inboxEvents({
48531
+ http: minterHttp,
48532
+ permalinkBase: base,
48533
+ resolveDirectory: buildDirectoryLookup,
48534
+ mode: currentMode(state),
48535
+ messageRefCache,
48536
+ watermarkStore,
48537
+ ...config2.queueIdleTimeoutSecs === undefined ? {} : { queueIdleTimeoutSecs: config2.queueIdleTimeoutSecs },
48538
+ ...config2.onQueueRegister === undefined ? {} : { onQueueRegister: config2.onQueueRegister },
48539
+ ...config2.onQueueAdvance === undefined ? {} : { onQueueAdvance: config2.onQueueAdvance },
48540
+ replay: (since) => inbox.replay(since),
48541
+ ...exports_Option.match(current2, {
48542
+ onNone: () => ({}),
48543
+ onSome: (b) => ({ boundIdentity: b.identity })
48544
+ }),
48545
+ ...exports_Option.match(initialQueue, {
48546
+ onNone: () => ({}),
48547
+ onSome: (q) => ({ initialQueue: q })
48548
+ }),
48549
+ ...exports_Option.isSome(resumed) && config2.onResumeOutcome !== undefined ? { onResumeOutcome: config2.onResumeOutcome } : {}
48550
+ })));
48551
+ }))).pipe(exports_Stream.filterEffect(shouldDeliver)),
48483
48552
  replay: (since) => {
48484
48553
  const replayNarrow = JSON.stringify([{ negated: true, operator: "is", operand: "dm" }]);
48485
48554
  return exports_Effect.all([
@@ -56742,6 +56811,70 @@ class StdioServerTransport {
56742
56811
  }
56743
56812
  }
56744
56813
 
56814
+ // packages/mcp/queue-state-hooks.ts
56815
+ var buildQueueStateHooks = (deps) => {
56816
+ const { store, session, idleTimeoutSecs, resumeOutcome } = deps;
56817
+ const withSession = (use) => exports_Deferred.poll(session).pipe(exports_Effect.flatMap(exports_Option.match({
56818
+ onNone: () => exports_Effect.void,
56819
+ onSome: (awaitId) => exports_Effect.flatMap(awaitId, use)
56820
+ })));
56821
+ return {
56822
+ queueIdleTimeoutSecs: idleTimeoutSecs,
56823
+ onQueueRegister: (queue) => withSession((id3) => exports_Deferred.poll(resumeOutcome).pipe(exports_Effect.flatMap(exports_Option.match({
56824
+ onSome: () => store.write(id3, queue),
56825
+ onNone: () => store.read(id3).pipe(exports_Effect.map(exports_Option.isNone), exports_Effect.catchAll(() => exports_Effect.succeed(true)), exports_Effect.flatMap((isFresh3) => isFresh3 ? store.write(id3, queue) : exports_Effect.void))
56826
+ })), exports_Effect.catchAllCause(() => exports_Effect.void))),
56827
+ onQueueAdvance: (lastEventId) => withSession((id3) => store.advance(id3, lastEventId).pipe(exports_Effect.catchAllCause(() => exports_Effect.void))),
56828
+ resumeQueue: () => exports_Deferred.poll(session).pipe(exports_Effect.flatMap(exports_Option.match({
56829
+ onNone: () => exports_Effect.succeedNone,
56830
+ onSome: (awaitId) => exports_Effect.flatMap(awaitId, (id3) => store.read(id3).pipe(exports_Effect.catchAllCause(() => exports_Effect.succeedNone)))
56831
+ }))),
56832
+ onResumeOutcome: (queueReplayed) => exports_Deferred.succeed(resumeOutcome, queueReplayed).pipe(exports_Effect.asVoid)
56833
+ };
56834
+ };
56835
+
56836
+ // packages/mcp/queue-state-store.ts
56837
+ import { homedir } from "node:os";
56838
+ import { join as join7 } from "node:path";
56839
+ class QueueStateStoreTag extends exports_Context.Tag("QueueStateStore")() {
56840
+ }
56841
+ var FILENAME_SAFE = /[^a-zA-Z0-9._-]/g;
56842
+ var stateFilename = (id3) => `${id3.replace(FILENAME_SAFE, "_")}.json`;
56843
+ var QueueStateFileSchema = exports_Schema.parseJson(exports_Schema.Struct({ queueId: exports_Schema.String, lastEventId: exports_Schema.Int }));
56844
+ var decodeQueueStateFile = exports_Schema.decodeUnknown(QueueStateFileSchema);
56845
+ var encodeQueueStateFile = exports_Schema.encode(QueueStateFileSchema);
56846
+ var isNotFound = (error2) => error2._tag === "SystemError" && error2.reason === "NotFound";
56847
+ var readState = (fs, path) => fs.readFileString(path).pipe(exports_Effect.flatMap(decodeQueueStateFile), exports_Effect.map(exports_Option.some), exports_Effect.catchIf(isNotFound, () => exports_Effect.succeed(exports_Option.none())));
56848
+ var createFileQueueStateStore = (deps) => {
56849
+ const { dir, fs } = deps;
56850
+ const pathFor = (id3) => join7(dir, stateFilename(id3));
56851
+ const persist = (path, state) => encodeQueueStateFile(state).pipe(exports_Effect.orDie, exports_Effect.flatMap((json4) => fs.makeDirectory(dir, { recursive: true }).pipe(exports_Effect.zipRight(fs.writeFileString(path, json4)))));
56852
+ const read3 = (id3) => readState(fs, pathFor(id3));
56853
+ const write4 = (id3, state) => persist(pathFor(id3), state);
56854
+ const advance = (id3, lastEventId) => {
56855
+ const path = pathFor(id3);
56856
+ return readState(fs, path).pipe(exports_Effect.catchTag("ParseError", () => exports_Effect.succeed(exports_Option.none())), exports_Effect.flatMap(exports_Option.match({
56857
+ onNone: () => exports_Effect.void,
56858
+ onSome: (prior) => lastEventId > prior.lastEventId ? persist(path, { queueId: prior.queueId, lastEventId }) : exports_Effect.void
56859
+ })));
56860
+ };
56861
+ return { read: read3, write: write4, advance };
56862
+ };
56863
+ var STATE_SEGMENT = "commy";
56864
+ var stateBaseConfig = exports_Config.nonEmptyString("XDG_STATE_HOME").pipe(exports_Config.withDefault(join7(homedir(), ".local", "state")));
56865
+ var queueStateDirConfig = stateBaseConfig.pipe(exports_Config.map((base) => join7(base, STATE_SEGMENT, "queue-state")));
56866
+ var FileQueueStateStoreLive = exports_Layer.effect(QueueStateStoreTag, exports_Effect.all([exports_FileSystem.FileSystem, exports_Effect.orDie(queueStateDirConfig)]).pipe(exports_Effect.map(([fs, dir]) => createFileQueueStateStore({ dir, fs }))));
56867
+
56868
+ // packages/mcp/resume-outcome.ts
56869
+ class ResumeOutcome extends exports_Context.Tag("commy/mcp/ResumeOutcome")() {
56870
+ }
56871
+ var ResumeOutcomeLive = exports_Layer.effect(ResumeOutcome, exports_Deferred.make());
56872
+
56873
+ // packages/mcp/session-id.ts
56874
+ class SessionId extends exports_Context.Tag("commy/mcp/SessionId")() {
56875
+ }
56876
+ var SessionIdLive = exports_Layer.effect(SessionId, exports_Deferred.make());
56877
+
56745
56878
  // packages/mcp/subscribe-parser.ts
56746
56879
  class SubscribeTokenError extends exports_Data.TaggedError("SubscribeTokenError") {
56747
56880
  get message() {
@@ -56867,6 +57000,14 @@ var userConfigString = (key) => exports_Config.string(key).pipe(exports_Config.m
56867
57000
  return exports_Either.right(value5);
56868
57001
  }));
56869
57002
  var optionalUserConfig = (key) => exports_Config.option(userConfigString(key));
57003
+ var QUEUE_IDLE_TIMEOUT_DEFAULT_SECS = 86400;
57004
+ var queueIdleTimeoutSecs = (key) => optionalUserConfig(key).pipe(exports_Config.mapOrFail((option7) => exports_Option.match(option7, {
57005
+ onNone: () => exports_Either.right(QUEUE_IDLE_TIMEOUT_DEFAULT_SECS),
57006
+ onSome: (raw3) => {
57007
+ const parsed = Number(raw3);
57008
+ return Number.isInteger(parsed) && parsed > 0 ? exports_Either.right(Math.min(parsed, MAX_QUEUE_TIMEOUT_SECS)) : exports_Either.left(exports_ConfigError.InvalidData([key], `${key} must be a positive integer — received: ${raw3}`));
57009
+ }
57010
+ })));
56870
57011
  var optionalNonNegativeInt = (key) => optionalUserConfig(key).pipe(exports_Config.mapOrFail((option7) => exports_Option.match(option7, {
56871
57012
  onNone: () => exports_Either.right(exports_Option.none()),
56872
57013
  onSome: (raw3) => {
@@ -56897,6 +57038,7 @@ var envConfig = exports_Config.all({
56897
57038
  subscribe: optionalUserConfig("COMMY_SUBSCRIBE"),
56898
57039
  project: optionalNonEmpty("COMMY_PROJECT"),
56899
57040
  catchupWindowSeconds: optionalNonNegativeInt("COMMY_CATCHUP_WINDOW_SECONDS"),
57041
+ queueIdleTimeoutSecs: queueIdleTimeoutSecs("COMMY_QUEUE_IDLE_TIMEOUT_SECS"),
56900
57042
  downloadDir: optionalNonEmpty("COMMY_DOWNLOAD_DIR")
56901
57043
  }).pipe(exports_Config.mapOrFail((raw3) => {
56902
57044
  if (exports_Option.isSome(raw3.botApiKey) && exports_Option.isNone(raw3.botName)) {
@@ -56907,6 +57049,7 @@ var envConfig = exports_Config.all({
56907
57049
  realmUrl: raw3.realmUrl,
56908
57050
  minterEmail: raw3.minterEmail,
56909
57051
  minterApiKey: raw3.minterApiKey,
57052
+ queueIdleTimeoutSecs: raw3.queueIdleTimeoutSecs,
56910
57053
  ...exports_Option.match(raw3.botName, { onNone: () => ({}), onSome: (botName) => ({ botName }) }),
56911
57054
  ...exports_Option.isSome(raw3.botName) && exports_Option.isSome(raw3.botApiKey) ? { attachIdentity: { name: raw3.botName.value, apiKey: raw3.botApiKey.value } } : {},
56912
57055
  ...exports_Option.match(raw3.subscribe, {
@@ -56980,11 +57123,24 @@ class SubstrateAdapter extends exports_Context.Tag("SubstrateAdapter")() {
56980
57123
  var substrateAdapterLayer = (make78) => exports_Layer.scoped(SubstrateAdapter, exports_Effect.tap(make78, (adapter4) => exports_Effect.addFinalizer(() => exports_Effect.promise(() => adapter4.close()))));
56981
57124
  var ZulipAdapterLive = substrateAdapterLayer(exports_Effect.gen(function* () {
56982
57125
  const parsed = yield* parseEnv;
57126
+ const queueHooks = parsed.botName === undefined ? buildQueueStateHooks({
57127
+ store: yield* QueueStateStoreTag,
57128
+ session: yield* SessionId,
57129
+ idleTimeoutSecs: parsed.queueIdleTimeoutSecs,
57130
+ resumeOutcome: yield* ResumeOutcome
57131
+ }) : undefined;
56983
57132
  return yield* zulipAdapter({
56984
57133
  realmUrl: parsed.realmUrl,
56985
57134
  minterEmail: parsed.minterEmail,
56986
57135
  minterApiKey: parsed.minterApiKey,
56987
- ...parsed.attachIdentity === undefined ? {} : { attachIdentity: parsed.attachIdentity }
57136
+ ...parsed.attachIdentity === undefined ? {} : { attachIdentity: parsed.attachIdentity },
57137
+ ...queueHooks === undefined ? {} : {
57138
+ queueIdleTimeoutSecs: queueHooks.queueIdleTimeoutSecs,
57139
+ onQueueRegister: queueHooks.onQueueRegister,
57140
+ onQueueAdvance: queueHooks.onQueueAdvance,
57141
+ resumeQueue: queueHooks.resumeQueue,
57142
+ onResumeOutcome: queueHooks.onResumeOutcome
57143
+ }
56988
57144
  });
56989
57145
  }));
56990
57146
  var subscribeFromEnv = (inbox, narrowSet, parsed) => {
@@ -57111,29 +57267,29 @@ var catchUpChannels = (deps) => exports_Effect.gen(function* () {
57111
57267
  });
57112
57268
 
57113
57269
  // packages/mcp/cursor-store.ts
57114
- import { homedir } from "node:os";
57115
- import { join as join7 } from "node:path";
57270
+ import { homedir as homedir2 } from "node:os";
57271
+ import { join as join8 } from "node:path";
57116
57272
  class CursorStoreTag extends exports_Context.Tag("CursorStore")() {
57117
57273
  }
57118
- var FILENAME_SAFE = /[^a-zA-Z0-9._-]/g;
57274
+ var FILENAME_SAFE2 = /[^a-zA-Z0-9._-]/g;
57119
57275
  var cursorFilename = (id3) => {
57120
- const safe = id3.replace(FILENAME_SAFE, "_");
57276
+ const safe = id3.replace(FILENAME_SAFE2, "_");
57121
57277
  return `${safe}.json`;
57122
57278
  };
57123
57279
  var CursorFileSchema = exports_Schema.parseJson(exports_Schema.Struct({ lastSeenTs: TimestampSchema }));
57124
57280
  var decodeCursorFile = exports_Schema.decodeUnknown(CursorFileSchema);
57125
- var isNotFound = (error2) => error2._tag === "SystemError" && error2.reason === "NotFound";
57126
- var readCursor = (fs, path) => fs.readFileString(path).pipe(exports_Effect.flatMap(decodeCursorFile), exports_Effect.map((file3) => exports_Option.some(file3.lastSeenTs)), exports_Effect.catchIf(isNotFound, () => exports_Effect.succeed(exports_Option.none())));
57281
+ var isNotFound2 = (error2) => error2._tag === "SystemError" && error2.reason === "NotFound";
57282
+ var readCursor = (fs, path) => fs.readFileString(path).pipe(exports_Effect.flatMap(decodeCursorFile), exports_Effect.map((file3) => exports_Option.some(file3.lastSeenTs)), exports_Effect.catchIf(isNotFound2, () => exports_Effect.succeed(exports_Option.none())));
57127
57283
  var createFileCursorStore = (deps) => {
57128
57284
  const { dir, fs } = deps;
57129
- const pathFor = (id3) => join7(dir, cursorFilename(id3));
57285
+ const pathFor = (id3) => join8(dir, cursorFilename(id3));
57130
57286
  const read3 = (id3) => readCursor(fs, pathFor(id3));
57131
57287
  const write4 = (id3, ts) => readCursor(fs, pathFor(id3)).pipe(exports_Effect.catchTag("ParseError", () => exports_Effect.succeed(exports_Option.none())), exports_Effect.flatMap((prior) => exports_Option.isSome(prior) && prior.value >= ts ? exports_Effect.void : fs.makeDirectory(dir, { recursive: true }).pipe(exports_Effect.zipRight(fs.writeFileString(pathFor(id3), JSON.stringify({ lastSeenTs: ts }))))));
57132
57288
  return { read: read3, write: write4 };
57133
57289
  };
57134
- var STATE_SEGMENT = "commy";
57135
- var stateBaseConfig = exports_Config.nonEmptyString("XDG_STATE_HOME").pipe(exports_Config.withDefault(join7(homedir(), ".local", "state")));
57136
- var cursorDirConfig = stateBaseConfig.pipe(exports_Config.map((base) => join7(base, STATE_SEGMENT, "cursors")));
57290
+ var STATE_SEGMENT2 = "commy";
57291
+ var stateBaseConfig2 = exports_Config.nonEmptyString("XDG_STATE_HOME").pipe(exports_Config.withDefault(join8(homedir2(), ".local", "state")));
57292
+ var cursorDirConfig = stateBaseConfig2.pipe(exports_Config.map((base) => join8(base, STATE_SEGMENT2, "cursors")));
57137
57293
  var FileCursorStoreLive = exports_Layer.effect(CursorStoreTag, exports_Effect.all([exports_FileSystem.FileSystem, exports_Effect.orDie(cursorDirConfig)]).pipe(exports_Effect.map(([fs, dir]) => createFileCursorStore({ dir, fs }))));
57138
57294
 
57139
57295
  // packages/mcp/ensure-bound.ts
@@ -57169,35 +57325,6 @@ var createEnsureBound = (deps) => exports_Effect.map(exports_Ref.make({ kind: "i
57169
57325
  return ensureBound;
57170
57326
  });
57171
57327
 
57172
- // packages/mcp/target-cursor.ts
57173
- import { homedir as homedir2 } from "node:os";
57174
- import { join as join8 } from "node:path";
57175
- var deliveryTargetOf = (message) => exports_Data.struct({
57176
- channel: message.ref.channel.name,
57177
- thread: exports_Option.map(message.ref.thread, (observed) => observed.name)
57178
- });
57179
- var DeliveryTargetSchema = exports_Schema.Data(exports_Schema.Struct({
57180
- channel: ChannelNameSchema,
57181
- thread: exports_Schema.OptionFromNullOr(ThreadNameSchema)
57182
- }));
57183
- var TargetCursorsFileSchema = exports_Schema.parseJson(exports_Schema.HashMap({ key: DeliveryTargetSchema, value: TimestampSchema }));
57184
- var decodeTargetCursorsFile = exports_Schema.decodeUnknown(TargetCursorsFileSchema);
57185
- var encodeTargetCursorsFile = exports_Schema.encode(TargetCursorsFileSchema);
57186
- var FILENAME_SAFE2 = /[^a-zA-Z0-9._-]/g;
57187
- var cursorsFilename = (id3) => `${id3.replace(FILENAME_SAFE2, "_")}.json`;
57188
- var isNotFound2 = (error2) => error2._tag === "SystemError" && error2.reason === "NotFound";
57189
- var readCursors = (fs, path) => fs.readFileString(path).pipe(exports_Effect.flatMap(decodeTargetCursorsFile), exports_Effect.map(exports_Option.some), exports_Effect.catchIf(isNotFound2, () => exports_Effect.succeed(exports_Option.none())));
57190
- var advanceTargetCursor = (fs, dir, id3, target, ts) => {
57191
- const path = join8(dir, cursorsFilename(id3));
57192
- return readCursors(fs, path).pipe(exports_Effect.catchTag("ParseError", () => exports_Effect.succeed(exports_Option.none())), exports_Effect.map(exports_Option.getOrElse(() => exports_HashMap.empty())), exports_Effect.flatMap((cursors) => {
57193
- const prior = exports_HashMap.get(cursors, target);
57194
- return exports_Option.isSome(prior) && prior.value >= ts ? exports_Effect.void : encodeTargetCursorsFile(exports_HashMap.set(cursors, target, ts)).pipe(exports_Effect.orDie, exports_Effect.flatMap((json4) => fs.makeDirectory(dir, { recursive: true }).pipe(exports_Effect.zipRight(fs.writeFileString(path, json4)))));
57195
- }));
57196
- };
57197
- var STATE_SEGMENT2 = "commy";
57198
- var stateBaseConfig2 = exports_Config.nonEmptyString("XDG_STATE_HOME").pipe(exports_Config.withDefault(join8(homedir2(), ".local", "state")));
57199
- var targetCursorDirConfig = stateBaseConfig2.pipe(exports_Config.map((base) => join8(base, STATE_SEGMENT2, "target-cursors")));
57200
-
57201
57328
  // packages/mcp/event-pump.ts
57202
57329
  var originatorId = (event) => exports_Match.value(event).pipe(exports_Match.discriminatorsExhaustive("kind")({
57203
57330
  "message-posted": (message) => message.message.sender.id,
@@ -57252,9 +57379,7 @@ var startEventPump = (deps) => exports_Effect.gen(function* () {
57252
57379
  for (const identity6 of identitiesIn(event))
57253
57380
  deps.rememberIdentity(identity6);
57254
57381
  }
57255
- const mentionAdvance = deps.onMention !== undefined && event.kind === "mention-received" ? deps.onMention(event.message.ts) : exports_Effect.void;
57256
- const deliveryAdvance = deps.onDelivery !== undefined && (event.kind === "message-posted" || event.kind === "mention-received") ? deps.onDelivery(deliveryTargetOf(event.message), event.message.ts) : exports_Effect.void;
57257
- const advance = exports_Effect.zipRight(mentionAdvance, deliveryAdvance);
57382
+ const advance = deps.onMention !== undefined && event.kind === "mention-received" ? deps.onMention(event.message.ts) : exports_Effect.void;
57258
57383
  if (recordMessageDelivery(event, seenDelivered) === "duplicate")
57259
57384
  return advance;
57260
57385
  return advance.pipe(exports_Effect.zipRight(renderEvent(event, botIdentityId)), exports_Effect.flatMap((payload) => exports_Effect.tryPromise({
@@ -58767,7 +58892,7 @@ class Server extends Protocol {
58767
58892
  }
58768
58893
 
58769
58894
  // packages/mcp/mcp-server.ts
58770
- var PLUGIN_VERSION = "0.18.0";
58895
+ var PLUGIN_VERSION = "0.19.1";
58771
58896
  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.
58772
58897
 
58773
58898
  **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.
@@ -58884,11 +59009,6 @@ var createNarrowSet = () => {
58884
59009
  var DEFAULT_TIMEOUT_MS = 5000;
58885
59010
  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));
58886
59011
 
58887
- // packages/mcp/session-id.ts
58888
- class SessionId extends exports_Context.Tag("commy/mcp/SessionId")() {
58889
- }
58890
- var SessionIdLive = exports_Layer.effect(SessionId, exports_Deferred.make());
58891
-
58892
59012
  // packages/mcp/subscription-restore.ts
58893
59013
  var applyRestored = (deps, intents) => exports_Effect.sync(() => deps.narrowSet.load(exports_Option.some(intents))).pipe(exports_Effect.zipRight(exports_Effect.forEach(intents, (intent) => deps.inbox.subscribe(intentToTarget(intent)), {
58894
59014
  discard: true
@@ -58923,25 +59043,16 @@ var subscriptionFilename = (id3) => {
58923
59043
  var isNotFound3 = (error2) => error2._tag === "SystemError" && error2.reason === "NotFound";
58924
59044
  var readSubscriptions = (fs, path) => fs.readFileString(path).pipe(exports_Effect.flatMap(decodeSubscriptionsFile), exports_Effect.map((intents) => exports_Option.some(intents)), exports_Effect.catchIf(isNotFound3, () => exports_Effect.succeed(exports_Option.none())));
58925
59045
  var createFileSubscriptionStore = (deps) => {
58926
- const { dir, fs, cursorDir, session } = deps;
59046
+ const { dir, fs, session } = deps;
58927
59047
  const pathFor = (id3) => join9(dir, subscriptionFilename(id3));
58928
59048
  const read3 = () => exports_Effect.flatMap(exports_Deferred.await(session), (id3) => readSubscriptions(fs, pathFor(id3)));
58929
59049
  const write4 = (intents) => exports_Effect.flatMap(exports_Deferred.await(session), (id3) => fs.makeDirectory(dir, { recursive: true }).pipe(exports_Effect.zipRight(fs.writeFileString(pathFor(id3), JSON.stringify(intents)))));
58930
- const advanceCursor = (target, ts) => exports_Effect.flatMap(exports_Deferred.poll(session), exports_Option.match({
58931
- onNone: () => exports_Effect.void,
58932
- onSome: (awaitId) => exports_Effect.flatMap(awaitId, (id3) => advanceTargetCursor(fs, cursorDir, id3, target, ts))
58933
- }));
58934
- return { read: read3, write: write4, advanceCursor };
59050
+ return { read: read3, write: write4 };
58935
59051
  };
58936
59052
  var STATE_SEGMENT3 = "commy";
58937
59053
  var stateBaseConfig3 = exports_Config.nonEmptyString("XDG_STATE_HOME").pipe(exports_Config.withDefault(join9(homedir3(), ".local", "state")));
58938
59054
  var subscriptionDirConfig = stateBaseConfig3.pipe(exports_Config.map((base) => join9(base, STATE_SEGMENT3, "subscriptions")));
58939
- var FileSubscriptionStoreLive = exports_Layer.effect(SubscriptionStoreTag, exports_Effect.all([
58940
- exports_FileSystem.FileSystem,
58941
- exports_Effect.orDie(subscriptionDirConfig),
58942
- exports_Effect.orDie(targetCursorDirConfig),
58943
- SessionId
58944
- ]).pipe(exports_Effect.map(([fs, dir, cursorDir, session]) => createFileSubscriptionStore({ dir, fs, cursorDir, session }))));
59055
+ var FileSubscriptionStoreLive = exports_Layer.effect(SubscriptionStoreTag, exports_Effect.all([exports_FileSystem.FileSystem, exports_Effect.orDie(subscriptionDirConfig), SessionId]).pipe(exports_Effect.map(([fs, dir, session]) => createFileSubscriptionStore({ dir, fs, session }))));
58945
59056
 
58946
59057
  // packages/mcp/tools.ts
58947
59058
  var emptyObjectSchema = {
@@ -59744,6 +59855,7 @@ var makeProgram = (params = {}) => exports_Effect.scoped(exports_Effect.gen(func
59744
59855
  const cursorStore = yield* CursorStoreTag;
59745
59856
  const subscriptionStore = yield* SubscriptionStoreTag;
59746
59857
  const sessionIdDeferred = yield* SessionId;
59858
+ const resumeOutcome = yield* ResumeOutcome;
59747
59859
  yield* exports_Effect.flatMap(readBootSessionId, exports_Option.match({
59748
59860
  onNone: () => exports_Effect.void,
59749
59861
  onSome: (sessionId) => exports_Deferred.succeed(sessionIdDeferred, sessionId).pipe(exports_Effect.asVoid)
@@ -59791,12 +59903,25 @@ var makeProgram = (params = {}) => exports_Effect.scoped(exports_Effect.gen(func
59791
59903
  onNone: () => exports_Effect.void,
59792
59904
  onSome: () => persistSubscriptions(subscriptionStore, narrowSet).pipe(exports_Effect.catchAll((err) => exports_Effect.logError(`commy plugin: subscription persist failed: ${exports_Predicate.isError(err) ? err.message : String(err)}`)), exports_Effect.provide(loggerLayer))
59793
59905
  })));
59794
- const ephemeralOnAcquire = parsed.botName === undefined ? (acquired, project3, sessionId) => ensureSessionSubscriptions(sessionId, project3).pipe(exports_Effect.zipRight(catchUpMentions({
59795
- cursorStore,
59796
- inbox: adapter4.inbox,
59797
- identityId: acquired.identity.id,
59798
- notifier
59799
- }).pipe(exports_Effect.catchAllCause(logCatchUpFailure("ephemeral mentions")))), exports_Effect.provide(loggerLayer)) : undefined;
59906
+ const ephemeralOnAcquire = parsed.botName === undefined ? (acquired, project3, sessionId) => ensureSessionSubscriptions(sessionId, project3).pipe(exports_Effect.zipRight(exports_Deferred.await(resumeOutcome)), exports_Effect.flatMap((queueReplayed) => {
59907
+ if (queueReplayed)
59908
+ return exports_Effect.void;
59909
+ const identityId = acquired.identity.id;
59910
+ const windowSeconds = parsed.catchupWindowSeconds ?? DEFAULT_CATCHUP_WINDOW_SECONDS;
59911
+ const catchUpIntents = narrowSet.intents();
59912
+ return catchUpMentions({
59913
+ cursorStore,
59914
+ inbox: adapter4.inbox,
59915
+ identityId,
59916
+ notifier
59917
+ }).pipe(exports_Effect.catchAllCause(logCatchUpFailure("ephemeral mentions")), exports_Effect.zipRight(windowSeconds > 0 && catchUpIntents.length > 0 ? catchUpChannels({
59918
+ intents: catchUpIntents,
59919
+ history: adapter4.history,
59920
+ notifier,
59921
+ botIdentityId: identityId,
59922
+ windowSeconds
59923
+ }).pipe(exports_Effect.catchAllCause(logCatchUpFailure("ephemeral channels"))) : exports_Effect.void));
59924
+ }), exports_Effect.provide(loggerLayer)) : undefined;
59800
59925
  const identityCache = yield* buildIdentityCache(adapter4, parsed.botName, ephemeralOnAcquire);
59801
59926
  let type1Intents = [];
59802
59927
  if (parsed.botName !== undefined) {
@@ -59881,11 +60006,6 @@ var makeProgram = (params = {}) => exports_Effect.scoped(exports_Effect.gen(func
59881
60006
  if (id3 === undefined)
59882
60007
  return exports_Effect.void;
59883
60008
  return cursorStore.write(id3, ts).pipe(exports_Effect.catchAllCause(() => exports_Effect.void));
59884
- },
59885
- onDelivery: (target, ts) => {
59886
- if (parsed.botName !== undefined)
59887
- return exports_Effect.void;
59888
- return subscriptionStore.advanceCursor(target, ts).pipe(exports_Effect.catchAllCause(() => exports_Effect.void));
59889
60009
  }
59890
60010
  });
59891
60011
  yield* exports_Effect.addFinalizer(() => pump.cancel);
@@ -59895,7 +60015,7 @@ var makeProgram = (params = {}) => exports_Effect.scoped(exports_Effect.gen(func
59895
60015
  yield* exports_Effect.forkScoped(restoreOnResume);
59896
60016
  yield* exports_Effect.raceFirst(pump.done, params.shutdownSignal ?? exports_Effect.never);
59897
60017
  }));
59898
- var AppLayer = exports_Layer.mergeAll(ZulipAdapterLive, FileCursorStoreLive, exports_Layer.provideMerge(FileSubscriptionStoreLive, SessionIdLive), stderrLoggerLayer);
60018
+ var AppLayer = exports_Layer.mergeAll(ZulipAdapterLive, FileCursorStoreLive, FileSubscriptionStoreLive, stderrLoggerLayer).pipe(exports_Layer.provideMerge(FileQueueStateStoreLive), exports_Layer.provideMerge(SessionIdLive), exports_Layer.provideMerge(ResumeOutcomeLive));
59899
60019
  var PlatformLive = exports_Layer.mergeAll(exports_Layer.setConfigProvider(exports_ConfigProvider.fromEnv()), exports_FetchHttpClient.layer, exports_NodeContext.layer);
59900
60020
  var MainLive = exports_Layer.provideMerge(AppLayer, PlatformLive);
59901
60021
  var clientDisconnect = (stdin3) => exports_Effect.async((resume2) => {