adhdev 0.9.55 → 0.9.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13748,6 +13748,12 @@ var init_provider_cli_adapter = __esm({
13748
13748
  accumulatedRawBuffer = "";
13749
13749
  /** Current visible terminal screen snapshot */
13750
13750
  terminalScreen = new TerminalScreen(24, 80);
13751
+ static MAX_RESPONSE_BUFFER = 8e3;
13752
+ static MAX_RECENT_OUTPUT_BUFFER = 1e3;
13753
+ responseBufferDroppedChars = 0;
13754
+ recentOutputDroppedChars = 0;
13755
+ accumulatedBufferDroppedChars = 0;
13756
+ accumulatedRawBufferDroppedChars = 0;
13751
13757
  /** Max accumulated buffer size. Sized to comfortably hold a single long
13752
13758
  * Hermes turn (tool calls + reasoning + final bubble) without the
13753
13759
  * rolling window pushing the turn's ╭─ opening line out of view. */
@@ -13765,6 +13771,23 @@ var init_provider_cli_adapter = __esm({
13765
13771
  providerResolutionMeta;
13766
13772
  static FINISH_RETRY_DELAY_MS = 300;
13767
13773
  static MAX_FINISH_RETRIES = 2;
13774
+ getBufferState() {
13775
+ const build = (droppedChars, maxChars) => droppedChars > 0 ? { truncated: true, droppedChars, maxChars } : void 0;
13776
+ const responseBuffer = build(this.responseBufferDroppedChars, _ProviderCliAdapter.MAX_RESPONSE_BUFFER);
13777
+ const recentOutputBuffer = build(this.recentOutputDroppedChars, _ProviderCliAdapter.MAX_RECENT_OUTPUT_BUFFER);
13778
+ const accumulatedBuffer = build(this.accumulatedBufferDroppedChars, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
13779
+ const accumulatedRawBuffer = build(this.accumulatedRawBufferDroppedChars, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
13780
+ if (!responseBuffer && !recentOutputBuffer && !accumulatedBuffer && !accumulatedRawBuffer) return void 0;
13781
+ return {
13782
+ ...responseBuffer ? { responseBuffer } : {},
13783
+ ...recentOutputBuffer ? { recentOutputBuffer } : {},
13784
+ ...accumulatedBuffer ? { accumulatedBuffer } : {},
13785
+ ...accumulatedRawBuffer ? { accumulatedRawBuffer } : {}
13786
+ };
13787
+ }
13788
+ recordBoundedAppendDrop(previousLength, appendedLength, nextLength) {
13789
+ return Math.max(0, previousLength + appendedLength - nextLength);
13790
+ }
13768
13791
  buildCommittedMessagesActivitySignature() {
13769
13792
  const last = this.committedMessages[this.committedMessages.length - 1];
13770
13793
  return [
@@ -14130,7 +14153,9 @@ var init_provider_cli_adapter = __esm({
14130
14153
  this.scheduleStartupSettleCheck();
14131
14154
  }
14132
14155
  if (this.isWaitingForResponse && cleanData) {
14133
- this.responseBuffer = appendBoundedText(this.responseBuffer, cleanData, 8e3);
14156
+ const previousResponseLen = this.responseBuffer.length;
14157
+ this.responseBuffer = appendBoundedText(this.responseBuffer, cleanData, _ProviderCliAdapter.MAX_RESPONSE_BUFFER);
14158
+ this.responseBufferDroppedChars += this.recordBoundedAppendDrop(previousResponseLen, cleanData.length, this.responseBuffer.length);
14134
14159
  }
14135
14160
  if (cleanData.trim()) {
14136
14161
  if (this.serverConn) {
@@ -14139,14 +14164,19 @@ var init_provider_cli_adapter = __esm({
14139
14164
  this.logBuffer.push({ message: cleanData.trim(), level: "info" });
14140
14165
  }
14141
14166
  }
14167
+ const prevRecentLen = this.recentOutputBuffer.length;
14142
14168
  const prevAccumulatedLen = this.accumulatedBuffer.length;
14143
14169
  const prevAccumulatedRawLen = this.accumulatedRawBuffer.length;
14144
- this.recentOutputBuffer = appendBoundedText(this.recentOutputBuffer, cleanData, 1e3);
14170
+ this.recentOutputBuffer = appendBoundedText(this.recentOutputBuffer, cleanData, _ProviderCliAdapter.MAX_RECENT_OUTPUT_BUFFER);
14145
14171
  this.accumulatedBuffer = appendBoundedText(this.accumulatedBuffer, cleanData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
14146
14172
  this.accumulatedRawBuffer = appendBoundedText(this.accumulatedRawBuffer, rawData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
14173
+ const droppedRecent = this.recordBoundedAppendDrop(prevRecentLen, cleanData.length, this.recentOutputBuffer.length);
14174
+ const droppedClean = this.recordBoundedAppendDrop(prevAccumulatedLen, cleanData.length, this.accumulatedBuffer.length);
14175
+ const droppedRaw = this.recordBoundedAppendDrop(prevAccumulatedRawLen, rawData.length, this.accumulatedRawBuffer.length);
14176
+ this.recentOutputDroppedChars += droppedRecent;
14177
+ this.accumulatedBufferDroppedChars += droppedClean;
14178
+ this.accumulatedRawBufferDroppedChars += droppedRaw;
14147
14179
  if (this.currentTurnScope) {
14148
- const droppedClean = prevAccumulatedLen + cleanData.length - this.accumulatedBuffer.length;
14149
- const droppedRaw = prevAccumulatedRawLen + rawData.length - this.accumulatedRawBuffer.length;
14150
14180
  if (droppedClean > 0) {
14151
14181
  this.currentTurnScope.bufferStart = Math.max(0, this.currentTurnScope.bufferStart - droppedClean);
14152
14182
  }
@@ -14976,13 +15006,15 @@ var init_provider_cli_adapter = __esm({
14976
15006
  effectiveModal = parsedModal;
14977
15007
  }
14978
15008
  }
15009
+ const bufferState = this.getBufferState();
14979
15010
  return {
14980
15011
  status: effectiveStatus,
14981
15012
  messages: [...this.committedMessages],
14982
15013
  workingDir: this.workingDir,
14983
15014
  activeModal: effectiveModal,
14984
15015
  errorMessage: this.parseErrorMessage || void 0,
14985
- errorReason: this.parseErrorMessage ? "parse_error" : void 0
15016
+ errorReason: this.parseErrorMessage ? "parse_error" : void 0,
15017
+ ...bufferState ? { bufferState } : {}
14986
15018
  };
14987
15019
  }
14988
15020
  seedCommittedMessages(messages) {
@@ -15164,10 +15196,12 @@ var init_provider_cli_adapter = __esm({
15164
15196
  messages: hydratedMessages,
15165
15197
  activeModal: parsed.activeModal ?? this.activeModal,
15166
15198
  providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0,
15199
+ ...this.getBufferState() ? { bufferState: this.getBufferState() } : {},
15167
15200
  ...this.providerOwnsTranscript() ? { transcriptAuthority: "provider", coverage: this.shouldUseFullProviderTranscriptContext() ? "full" : "tail" } : {}
15168
15201
  };
15169
15202
  } else {
15170
15203
  const messages = [...this.committedMessages];
15204
+ const bufferState = this.getBufferState();
15171
15205
  result = {
15172
15206
  id: "cli_session",
15173
15207
  status: this.currentStatus,
@@ -15178,7 +15212,8 @@ var init_provider_cli_adapter = __esm({
15178
15212
  index: typeof message.index === "number" ? message.index : index,
15179
15213
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
15180
15214
  })),
15181
- activeModal: this.activeModal
15215
+ activeModal: this.activeModal,
15216
+ ...bufferState ? { bufferState } : {}
15182
15217
  };
15183
15218
  }
15184
15219
  const hasVisibleAssistantMessage = Array.isArray(result?.messages) && result.messages.some((message) => message?.role === "assistant" && typeof message?.content === "string" && message.content.trim());
@@ -34899,7 +34934,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
34899
34934
  resolvedDir,
34900
34935
  resolvedCliArgs,
34901
34936
  resolvedProvider,
34902
- {},
34937
+ this.providerLoader.getSettings(normalizedType),
34903
34938
  false,
34904
34939
  {
34905
34940
  providerSessionId: sessionBinding.providerSessionId,
@@ -49308,6 +49343,58 @@ var init_server_connection = __esm({
49308
49343
  onStateChange(callback) {
49309
49344
  this.stateChangeCallbacks.push(callback);
49310
49345
  }
49346
+ /**
49347
+ * Send a command to another daemon owned by the same user, via server relay.
49348
+ * The server validates ownership before forwarding to the target daemon.
49349
+ *
49350
+ * Returns the command result or throws on timeout / auth failure.
49351
+ */
49352
+ sendMeshCommand(targetDaemonId, command, args = {}, timeoutMs = 3e4) {
49353
+ return new Promise((resolve19, reject) => {
49354
+ const requestId = `mesh_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
49355
+ const timer = setTimeout(() => {
49356
+ this.off("daemon_mesh_result", handler);
49357
+ reject(new Error(`Mesh command timed out after ${timeoutMs}ms`));
49358
+ }, timeoutMs);
49359
+ const handler = (msg) => {
49360
+ if (msg.payload?.requestId !== requestId) return;
49361
+ this.off("daemon_mesh_result", handler);
49362
+ clearTimeout(timer);
49363
+ if (msg.payload?.success === false) {
49364
+ reject(new Error(msg.payload?.error ?? "Mesh command failed"));
49365
+ } else {
49366
+ resolve19(msg.payload?.result);
49367
+ }
49368
+ };
49369
+ this.on("daemon_mesh_result", handler);
49370
+ const sent = this.ws?.readyState === 1 && (() => {
49371
+ try {
49372
+ this.ws.send(JSON.stringify({
49373
+ type: "daemon_mesh_command",
49374
+ requestId,
49375
+ targetDaemonId,
49376
+ command,
49377
+ args,
49378
+ timestamp: Date.now()
49379
+ }));
49380
+ return true;
49381
+ } catch {
49382
+ return false;
49383
+ }
49384
+ })();
49385
+ if (!sent) {
49386
+ clearTimeout(timer);
49387
+ this.off("daemon_mesh_result", handler);
49388
+ reject(new Error("Not connected to server"));
49389
+ }
49390
+ });
49391
+ }
49392
+ off(type, handler) {
49393
+ const handlers = this.messageHandlers.get(type);
49394
+ if (!handlers) return;
49395
+ const idx = handlers.indexOf(handler);
49396
+ if (idx !== -1) handlers.splice(idx, 1);
49397
+ }
49311
49398
  getState() {
49312
49399
  return this.state;
49313
49400
  }
@@ -49617,7 +49704,7 @@ function sendToPeer(peer, data) {
49617
49704
  return false;
49618
49705
  }
49619
49706
  const json2 = JSON.stringify(data);
49620
- if (messageType === "command_result" && json2.length > MAX_INLINE_JSON_MESSAGE_CHARS) {
49707
+ if (messageType === "command_result" && Buffer.byteLength(json2, "utf8") > MAX_INLINE_JSON_MESSAGE_CHARS) {
49621
49708
  return sendChunkedCommandResult(peer, peerId, requestId, json2);
49622
49709
  }
49623
49710
  try {
@@ -49630,21 +49717,51 @@ function sendToPeer(peer, data) {
49630
49717
  return false;
49631
49718
  }
49632
49719
  }
49720
+ function buildCommandResultChunkEnvelope(requestId, chunkId, index, total, data) {
49721
+ return JSON.stringify({
49722
+ type: "command_result_chunk",
49723
+ id: requestId,
49724
+ chunkId,
49725
+ index,
49726
+ total,
49727
+ data
49728
+ });
49729
+ }
49730
+ function splitJsonIntoCommandResultChunks(json2, requestId, chunkId) {
49731
+ const chunks = [];
49732
+ let offset = 0;
49733
+ while (offset < json2.length) {
49734
+ let end = Math.min(json2.length, offset + JSON_CHUNK_PAYLOAD_CHARS);
49735
+ while (end > offset) {
49736
+ const candidate = json2.slice(offset, end);
49737
+ const envelope = buildCommandResultChunkEnvelope(requestId, chunkId, chunks.length, MAX_JSON_CHUNKS, candidate);
49738
+ if (Buffer.byteLength(envelope, "utf8") <= MAX_INLINE_JSON_MESSAGE_BYTES) break;
49739
+ end = offset + Math.max(1, Math.floor((end - offset) * 0.8));
49740
+ }
49741
+ chunks.push(json2.slice(offset, end));
49742
+ if (chunks.length > MAX_JSON_CHUNKS) return [];
49743
+ offset = end;
49744
+ }
49745
+ return chunks;
49746
+ }
49633
49747
  function sendChunkedCommandResult(peer, peerId, requestId, json2) {
49634
49748
  const chunkId = requestId || `command_result_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
49635
- const total = Math.ceil(json2.length / JSON_CHUNK_PAYLOAD_CHARS);
49749
+ const chunks = splitJsonIntoCommandResultChunks(json2, requestId, chunkId);
49750
+ const total = chunks.length;
49751
+ if (total === 0 || total > MAX_JSON_CHUNKS) {
49752
+ log(`command_result send failed: peer=${peerId} id=${requestId || "-"} reason=too_many_chunks chunks=${total || "unknown"}`);
49753
+ return false;
49754
+ }
49636
49755
  logDebug(`command_result chunked: peer=${peerId} id=${requestId || "-"} chars=${json2.length} chunks=${total}`);
49637
49756
  for (let index = 0; index < total; index += 1) {
49638
- const chunk = json2.slice(index * JSON_CHUNK_PAYLOAD_CHARS, (index + 1) * JSON_CHUNK_PAYLOAD_CHARS);
49757
+ const chunk = chunks[index] || "";
49639
49758
  try {
49640
- peer.dataChannel?.sendMessage(JSON.stringify({
49641
- type: "command_result_chunk",
49642
- id: requestId,
49643
- chunkId,
49644
- index,
49645
- total,
49646
- data: chunk
49647
- }));
49759
+ const envelope = buildCommandResultChunkEnvelope(requestId, chunkId, index, total, chunk);
49760
+ if (Buffer.byteLength(envelope, "utf8") > MAX_INLINE_JSON_MESSAGE_BYTES) {
49761
+ log(`command_result send failed: peer=${peerId} id=${requestId || "-"} chunk=${index + 1}/${total} reason=chunk_too_large`);
49762
+ return false;
49763
+ }
49764
+ peer.dataChannel?.sendMessage(envelope);
49648
49765
  } catch (error48) {
49649
49766
  log(`command_result send failed: peer=${peerId} id=${requestId || "-"} chunk=${index + 1}/${total} error=${error48?.message || error48}`);
49650
49767
  return false;
@@ -49652,10 +49769,58 @@ function sendChunkedCommandResult(peer, peerId, requestId, json2) {
49652
49769
  }
49653
49770
  return true;
49654
49771
  }
49772
+ function parseChunkedJsonMessage(peerId, parsed) {
49773
+ const chunkType = typeof parsed?.type === "string" ? parsed.type : "";
49774
+ if (!chunkType.endsWith("_chunk")) return parsed;
49775
+ const chunkId = typeof parsed?.chunkId === "string" ? parsed.chunkId : "";
49776
+ const id = typeof parsed?.id === "string" ? parsed.id : "";
49777
+ const index = Number(parsed?.index);
49778
+ const total = Number(parsed?.total);
49779
+ const data = typeof parsed?.data === "string" ? parsed.data : "";
49780
+ if (!chunkId || !Number.isInteger(index) || !Number.isInteger(total) || index < 0 || total <= 0 || total > MAX_JSON_CHUNKS || index >= total || !data) {
49781
+ log(`chunked JSON ignored: peer=${peerId} type=${chunkType || "unknown"} reason=malformed`);
49782
+ return null;
49783
+ }
49784
+ const now = Date.now();
49785
+ incomingJsonChunks.forEach((entry2, key2) => {
49786
+ if (now - entry2.createdAt > JSON_CHUNK_TTL_MS) incomingJsonChunks.delete(key2);
49787
+ });
49788
+ const key = `${peerId}:${chunkId}`;
49789
+ let entry = incomingJsonChunks.get(key);
49790
+ if (!entry || entry.total !== total || entry.type !== chunkType) {
49791
+ entry = { total, chunks: new Array(total).fill(""), received: 0, createdAt: now, type: chunkType, id };
49792
+ incomingJsonChunks.set(key, entry);
49793
+ }
49794
+ if (!entry.chunks[index]) {
49795
+ entry.chunks[index] = data;
49796
+ entry.received += 1;
49797
+ }
49798
+ if (entry.received < entry.total) return null;
49799
+ incomingJsonChunks.delete(key);
49800
+ try {
49801
+ return JSON.parse(entry.chunks.join(""));
49802
+ } catch (error48) {
49803
+ log(`chunked JSON parse failed: peer=${peerId} type=${chunkType} id=${entry.id || "-"} error=${error48?.message || error48}`);
49804
+ return CHUNK_PARSE_FAILED;
49805
+ }
49806
+ }
49655
49807
  function routeDataChannelMessage(peerId, msg, peers, handlers) {
49656
49808
  const text = typeof msg === "string" ? msg : msg.toString("utf-8");
49657
49809
  try {
49658
- const parsed = JSON.parse(text);
49810
+ let parsed = JSON.parse(text);
49811
+ const chunked = parseChunkedJsonMessage(peerId, parsed);
49812
+ if (chunked === null) return;
49813
+ if (chunked === CHUNK_PARSE_FAILED) {
49814
+ const peer = peers.get(peerId);
49815
+ const id = typeof parsed?.id === "string" ? parsed.id : "";
49816
+ if (parsed?.type === "command_chunk") {
49817
+ sendToPeer(peer, { type: "command_result", id, success: false, error: "Failed to reassemble chunked command" });
49818
+ } else if (id) {
49819
+ sendToPeer(peer, { id, type: "response", success: false, error: "Failed to reassemble chunked request" });
49820
+ }
49821
+ return;
49822
+ }
49823
+ parsed = chunked;
49659
49824
  if (parsed.type !== "command" && parsed.type !== "pty_input" && parsed.type !== "pty_resize" && parsed.type !== "ping" && parsed.type !== "pong") {
49660
49825
  logDebug(`Files message from peer ${peerId}: type=${parsed.type}`);
49661
49826
  }
@@ -49951,7 +50116,7 @@ async function handleFileRequest(peerId, req, peers, handlers) {
49951
50116
  }
49952
50117
  sendToPeer(peer, response);
49953
50118
  }
49954
- var MAX_INLINE_JSON_MESSAGE_CHARS, JSON_CHUNK_PAYLOAD_CHARS;
50119
+ var MAX_INLINE_JSON_MESSAGE_CHARS, MAX_INLINE_JSON_MESSAGE_BYTES, JSON_CHUNK_PAYLOAD_CHARS, JSON_CHUNK_TTL_MS, MAX_JSON_CHUNKS, CHUNK_PARSE_FAILED, incomingJsonChunks;
49955
50120
  var init_data_channel_router = __esm({
49956
50121
  "src/daemon-p2p/data-channel-router.ts"() {
49957
50122
  "use strict";
@@ -49959,18 +50124,23 @@ var init_data_channel_router = __esm({
49959
50124
  init_permission();
49960
50125
  init_log();
49961
50126
  MAX_INLINE_JSON_MESSAGE_CHARS = 6e4;
49962
- JSON_CHUNK_PAYLOAD_CHARS = 32e3;
50127
+ MAX_INLINE_JSON_MESSAGE_BYTES = 6e4;
50128
+ JSON_CHUNK_PAYLOAD_CHARS = 16e3;
50129
+ JSON_CHUNK_TTL_MS = 6e4;
50130
+ MAX_JSON_CHUNKS = 1024;
50131
+ CHUNK_PARSE_FAILED = /* @__PURE__ */ Symbol("chunk_parse_failed");
50132
+ incomingJsonChunks = /* @__PURE__ */ new Map();
49963
50133
  }
49964
50134
  });
49965
50135
 
49966
50136
  // src/daemon-p2p/screenshot-sender.ts
49967
- var CHUNK_SIZE, MAX_INLINE_JSON_MESSAGE_BYTES, JSON_CHUNK_PAYLOAD_CHARS2, ScreenshotSender;
50137
+ var CHUNK_SIZE, MAX_INLINE_JSON_MESSAGE_BYTES2, JSON_CHUNK_PAYLOAD_CHARS2, ScreenshotSender;
49968
50138
  var init_screenshot_sender = __esm({
49969
50139
  "src/daemon-p2p/screenshot-sender.ts"() {
49970
50140
  "use strict";
49971
50141
  init_log();
49972
50142
  CHUNK_SIZE = 6e4;
49973
- MAX_INLINE_JSON_MESSAGE_BYTES = 6e4;
50143
+ MAX_INLINE_JSON_MESSAGE_BYTES2 = 6e4;
49974
50144
  JSON_CHUNK_PAYLOAD_CHARS2 = 16e3;
49975
50145
  ScreenshotSender = class {
49976
50146
  _ssDebugDone = false;
@@ -50014,7 +50184,7 @@ var init_screenshot_sender = __esm({
50014
50184
  type: "topic_update",
50015
50185
  update
50016
50186
  });
50017
- if (Buffer.byteLength(json2, "utf8") > MAX_INLINE_JSON_MESSAGE_BYTES) {
50187
+ if (Buffer.byteLength(json2, "utf8") > MAX_INLINE_JSON_MESSAGE_BYTES2) {
50018
50188
  return this.sendChunkedTopicUpdate(peer, update, json2);
50019
50189
  }
50020
50190
  try {
@@ -50053,6 +50223,10 @@ var init_screenshot_sender = __esm({
50053
50223
  let sentAny = false;
50054
50224
  for (const peer of peers.values()) {
50055
50225
  if (peer.state !== "connected" || !peer.dataChannel?.isOpen()) continue;
50226
+ if (Buffer.byteLength(msg, "utf8") > MAX_INLINE_JSON_MESSAGE_BYTES2) {
50227
+ if (this.sendChunkedSessionOutput(peer, sessionId, msg)) sentAny = true;
50228
+ continue;
50229
+ }
50056
50230
  try {
50057
50231
  peer.dataChannel.sendMessage(msg);
50058
50232
  sentAny = true;
@@ -50061,6 +50235,27 @@ var init_screenshot_sender = __esm({
50061
50235
  }
50062
50236
  return sentAny;
50063
50237
  }
50238
+ sendChunkedSessionOutput(peer, sessionId, json2) {
50239
+ const chunkId = `session_output:${sessionId}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
50240
+ const total = Math.ceil(json2.length / JSON_CHUNK_PAYLOAD_CHARS2);
50241
+ logDebug(`session_output chunked: peer=${peer.peerId || "unknown"} sessionId=${sessionId || "-"} chars=${json2.length} chunks=${total}`);
50242
+ for (let index = 0; index < total; index += 1) {
50243
+ const chunk = json2.slice(index * JSON_CHUNK_PAYLOAD_CHARS2, (index + 1) * JSON_CHUNK_PAYLOAD_CHARS2);
50244
+ try {
50245
+ peer.dataChannel?.sendMessage(JSON.stringify({
50246
+ type: "session_output_chunk",
50247
+ sessionId,
50248
+ chunkId,
50249
+ index,
50250
+ total,
50251
+ data: chunk
50252
+ }));
50253
+ } catch {
50254
+ return false;
50255
+ }
50256
+ }
50257
+ return true;
50258
+ }
50064
50259
  sendScreenshot(peers, base64Data, targetSessionId) {
50065
50260
  const buffer = Buffer.from(base64Data, "base64");
50066
50261
  return this.sendScreenshotBuffer(peers, buffer, targetSessionId);
@@ -58650,6 +58845,64 @@ var init_version = __esm({
58650
58845
  }
58651
58846
  });
58652
58847
 
58848
+ // src/daemon-mesh-manager.ts
58849
+ var daemon_mesh_manager_exports = {};
58850
+ __export(daemon_mesh_manager_exports, {
58851
+ DaemonMeshManager: () => DaemonMeshManager
58852
+ });
58853
+ function interpolateArgs(args, context) {
58854
+ const result = {};
58855
+ for (const [k, v] of Object.entries(args)) {
58856
+ result[k] = typeof v === "string" ? interpolateString(v, context) : v;
58857
+ }
58858
+ return result;
58859
+ }
58860
+ function interpolateString(template, ctx) {
58861
+ return template.replace(/\{\{(\w+)\}\}/g, (_2, key) => {
58862
+ const val = ctx[key];
58863
+ return val !== void 0 ? String(val) : `{{${key}}}`;
58864
+ });
58865
+ }
58866
+ var DaemonMeshManager;
58867
+ var init_daemon_mesh_manager = __esm({
58868
+ "src/daemon-mesh-manager.ts"() {
58869
+ "use strict";
58870
+ init_src();
58871
+ DaemonMeshManager = class {
58872
+ rules = [];
58873
+ serverConn;
58874
+ constructor(serverConn) {
58875
+ this.serverConn = serverConn;
58876
+ }
58877
+ setRules(rules) {
58878
+ this.rules = rules;
58879
+ LOG.info("Mesh", `[Mesh] ${rules.length} rule(s) loaded`);
58880
+ }
58881
+ async emit(event) {
58882
+ const matching = this.rules.filter((r) => r.trigger === event.trigger);
58883
+ if (matching.length === 0) return;
58884
+ await Promise.allSettled(
58885
+ matching.map((rule) => this.executeRule(rule, event.context ?? {}))
58886
+ );
58887
+ }
58888
+ async executeRule(rule, context) {
58889
+ const args = interpolateArgs(rule.args ?? {}, context);
58890
+ try {
58891
+ LOG.info("Mesh", `[Mesh] Relaying '${rule.command}' \u2192 ${rule.targetDaemonId.slice(0, 12)}\u2026`);
58892
+ await this.serverConn.sendMeshCommand(rule.targetDaemonId, rule.command, args);
58893
+ LOG.info("Mesh", `[Mesh] Relay '${rule.command}' complete`);
58894
+ } catch (err) {
58895
+ LOG.warn("Mesh", `[Mesh] Relay '${rule.command}' failed: ${err?.message ?? err}`);
58896
+ }
58897
+ }
58898
+ /** Convenience: send a one-off mesh command without a rule. */
58899
+ async sendCommand(targetDaemonId, command, args = {}) {
58900
+ return this.serverConn.sendMeshCommand(targetDaemonId, command, args);
58901
+ }
58902
+ };
58903
+ }
58904
+ });
58905
+
58653
58906
  // src/adhdev-daemon.ts
58654
58907
  var adhdev_daemon_exports = {};
58655
58908
  __export(adhdev_daemon_exports, {
@@ -58894,13 +59147,14 @@ var init_adhdev_daemon = __esm({
58894
59147
  init_version();
58895
59148
  init_src();
58896
59149
  init_runtime_defaults();
58897
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.55" });
59150
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.57" });
58898
59151
  AdhdevDaemon = class _AdhdevDaemon {
58899
59152
  localHttpServer = null;
58900
59153
  localWss = null;
58901
59154
  localClients = /* @__PURE__ */ new Set();
58902
59155
  serverConn = null;
58903
59156
  p2p = null;
59157
+ meshManager = null;
58904
59158
  screenshotController = null;
58905
59159
  statusReporter = null;
58906
59160
  topicSubscriptionTimer = null;
@@ -59484,6 +59738,12 @@ ${err?.stack || ""}`);
59484
59738
  }
59485
59739
  });
59486
59740
  this.p2p = new DaemonP2PSender(this.serverConn);
59741
+ const { DaemonMeshManager: DaemonMeshManager2 } = await Promise.resolve().then(() => (init_daemon_mesh_manager(), daemon_mesh_manager_exports));
59742
+ this.meshManager = new DaemonMeshManager2(this.serverConn);
59743
+ const meshRules = config2.meshRules;
59744
+ if (Array.isArray(meshRules) && meshRules.length > 0) {
59745
+ this.meshManager.setRules(meshRules);
59746
+ }
59487
59747
  if (this.p2p.isAvailable) {
59488
59748
  console.log(source_default2.green(" \u{1F517} P2P available (node-datachannel)"));
59489
59749
  this.p2p.onInput(async (event) => {
@@ -59822,6 +60082,16 @@ ${err?.stack || ""}`);
59822
60082
  if (cmdType.startsWith("git_")) {
59823
60083
  void this.flushP2PWorkspaceGitSubscriptions();
59824
60084
  }
60085
+ if (cmdType === "git_checkpoint" && routed.success && this.meshManager) {
60086
+ void this.meshManager.emit({
60087
+ trigger: "git_checkpoint_complete",
60088
+ context: {
60089
+ workspace: String(normalizedData.workspace ?? ""),
60090
+ checkpoint_message: routed.checkpoint?.message ?? String(normalizedData.message ?? ""),
60091
+ commit: routed.checkpoint?.commit ?? ""
60092
+ }
60093
+ });
60094
+ }
59825
60095
  return { ...routed, interactionId };
59826
60096
  } catch (e) {
59827
60097
  logCommand({ ts: (/* @__PURE__ */ new Date()).toISOString(), cmd: cmdType, source: "p2p", interactionId, success: false, error: e.message, durationMs: Date.now() - cmdStart });