adhdev 0.8.35 → 0.8.37

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/cli/index.js CHANGED
@@ -67,6 +67,7 @@ function normalizeConfig(raw) {
67
67
  const parsed = isPlainObject(raw) ? raw : {};
68
68
  return {
69
69
  serverUrl: typeof parsed.serverUrl === "string" && parsed.serverUrl.trim() ? parsed.serverUrl : DEFAULT_CONFIG.serverUrl,
70
+ allowServerApiProxy: asBoolean(parsed.allowServerApiProxy, DEFAULT_CONFIG.allowServerApiProxy ?? false),
70
71
  selectedIde: asNullableString(parsed.selectedIde),
71
72
  configuredIdes: asStringArray(parsed.configuredIdes),
72
73
  installedExtensions: asStringArray(parsed.installedExtensions),
@@ -215,6 +216,7 @@ var init_config = __esm({
215
216
  import_crypto = require("crypto");
216
217
  DEFAULT_CONFIG = {
217
218
  serverUrl: "https://api.adhf.dev",
219
+ allowServerApiProxy: false,
218
220
  selectedIde: null,
219
221
  configuredIdes: [],
220
222
  installedExtensions: [],
@@ -4500,19 +4502,20 @@ function normalizeMessageTime(message) {
4500
4502
  }
4501
4503
  return msg;
4502
4504
  }
4503
- function trimMessagesForStatus(messages) {
4505
+ function trimMessagesForStatus(messages, options) {
4506
+ if (!options.includeMessages || options.messageLimit <= 0 || options.totalBytesLimit <= 0) return [];
4504
4507
  if (!Array.isArray(messages) || messages.length === 0) return [];
4505
- const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
4508
+ const recent = messages.slice(-options.messageLimit);
4506
4509
  const kept = [];
4507
4510
  let totalBytes = 0;
4508
4511
  for (let i = recent.length - 1; i >= 0; i -= 1) {
4509
- let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT));
4512
+ let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.stringLimit));
4510
4513
  let size = estimateBytes(normalized);
4511
- if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
4512
- normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT));
4514
+ if (size > options.totalBytesLimit) {
4515
+ normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.fallbackStringLimit));
4513
4516
  size = estimateBytes(normalized);
4514
4517
  }
4515
- if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
4518
+ if (kept.length > 0 && totalBytes + size > options.totalBytesLimit) {
4516
4519
  continue;
4517
4520
  }
4518
4521
  kept.push(normalized);
@@ -4542,22 +4545,26 @@ function isManagedStatusWorking(status) {
4542
4545
  function isManagedStatusWaiting(status, opts) {
4543
4546
  return normalizeManagedStatus(status, opts) === "waiting_approval";
4544
4547
  }
4545
- function normalizeActiveChatData(activeChat) {
4548
+ function normalizeActiveChatData(activeChat, options = FULL_STATUS_ACTIVE_CHAT_OPTIONS) {
4546
4549
  if (!activeChat) return activeChat;
4550
+ const resolvedOptions = {
4551
+ ...FULL_STATUS_ACTIVE_CHAT_OPTIONS,
4552
+ ...options
4553
+ };
4547
4554
  return {
4548
4555
  ...activeChat,
4549
4556
  status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),
4550
- messages: trimMessagesForStatus(activeChat.messages),
4551
- activeModal: activeChat.activeModal ? {
4557
+ messages: trimMessagesForStatus(activeChat.messages, resolvedOptions),
4558
+ activeModal: resolvedOptions.includeActiveModal && activeChat.activeModal ? {
4552
4559
  message: truncateString(activeChat.activeModal.message || "", STATUS_MODAL_MESSAGE_LIMIT),
4553
4560
  buttons: (activeChat.activeModal.buttons || []).map(
4554
4561
  (button) => truncateString(String(button || ""), STATUS_MODAL_BUTTON_LIMIT)
4555
4562
  )
4556
- } : activeChat.activeModal,
4557
- inputContent: activeChat.inputContent ? truncateString(activeChat.inputContent, STATUS_INPUT_CONTENT_LIMIT) : activeChat.inputContent
4563
+ } : null,
4564
+ inputContent: resolvedOptions.includeInputContent && activeChat.inputContent ? truncateString(activeChat.inputContent, 2 * 1024) : void 0
4558
4565
  };
4559
4566
  }
4560
- var WORKING_STATUSES, STATUS_ACTIVE_CHAT_MESSAGE_LIMIT, STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT, STATUS_ACTIVE_CHAT_STRING_LIMIT, STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT, STATUS_INPUT_CONTENT_LIMIT, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT;
4567
+ var WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT;
4561
4568
  var init_normalize = __esm({
4562
4569
  "../../oss/packages/daemon-core/src/status/normalize.ts"() {
4563
4570
  "use strict";
@@ -4569,17 +4576,43 @@ var init_normalize = __esm({
4569
4576
  "thinking",
4570
4577
  "active"
4571
4578
  ]);
4572
- STATUS_ACTIVE_CHAT_MESSAGE_LIMIT = 60;
4573
- STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT = 96 * 1024;
4574
- STATUS_ACTIVE_CHAT_STRING_LIMIT = 4 * 1024;
4575
- STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT = 1024;
4576
- STATUS_INPUT_CONTENT_LIMIT = 2 * 1024;
4579
+ FULL_STATUS_ACTIVE_CHAT_OPTIONS = {
4580
+ includeMessages: true,
4581
+ includeInputContent: true,
4582
+ includeActiveModal: true,
4583
+ messageLimit: 60,
4584
+ totalBytesLimit: 96 * 1024,
4585
+ stringLimit: 4 * 1024,
4586
+ fallbackStringLimit: 1024
4587
+ };
4588
+ LIVE_STATUS_ACTIVE_CHAT_OPTIONS = {
4589
+ includeMessages: false,
4590
+ includeInputContent: false,
4591
+ includeActiveModal: false,
4592
+ messageLimit: 0,
4593
+ totalBytesLimit: 0,
4594
+ stringLimit: 512,
4595
+ fallbackStringLimit: 256
4596
+ };
4577
4597
  STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;
4578
4598
  STATUS_MODAL_BUTTON_LIMIT = 120;
4579
4599
  }
4580
4600
  });
4581
4601
 
4582
4602
  // ../../oss/packages/daemon-core/src/status/builders.ts
4603
+ function getActiveChatOptions(profile) {
4604
+ if (profile === "full") return {};
4605
+ return LIVE_STATUS_ACTIVE_CHAT_OPTIONS;
4606
+ }
4607
+ function shouldIncludeSessionControls(profile) {
4608
+ return profile !== "live";
4609
+ }
4610
+ function shouldIncludeSessionMetadata(profile) {
4611
+ return profile !== "live";
4612
+ }
4613
+ function shouldIncludeRuntimeMetadata(profile) {
4614
+ return profile !== "live";
4615
+ }
4583
4616
  function findCdpManager(cdpManagers, key) {
4584
4617
  const exact = cdpManagers.get(key);
4585
4618
  if (exact) return exact;
@@ -4645,74 +4678,88 @@ function buildFallbackControls(providerControls, serverModel, serverMode, acpCon
4645
4678
  }
4646
4679
  return controls;
4647
4680
  }
4648
- function buildIdeWorkspaceSession(state, cdpManagers) {
4649
- const activeChat = normalizeActiveChatData(state.activeChat);
4681
+ function buildIdeWorkspaceSession(state, cdpManagers, options) {
4682
+ const profile = options.profile || "full";
4683
+ const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
4684
+ const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
4685
+ const includeSessionControls = shouldIncludeSessionControls(profile);
4650
4686
  const title = activeChat?.title || state.name;
4651
4687
  return {
4652
4688
  id: state.instanceId || state.type,
4653
4689
  parentId: null,
4654
4690
  providerType: state.type,
4655
- providerName: state.name,
4691
+ ...includeSessionMetadata && { providerName: state.name },
4656
4692
  kind: "workspace",
4657
4693
  transport: "cdp-page",
4658
4694
  status: normalizeManagedStatus(activeChat?.status || state.status, {
4659
4695
  activeModal: activeChat?.activeModal || null
4660
4696
  }),
4661
4697
  title,
4662
- workspace: state.workspace || null,
4698
+ ...includeSessionMetadata && { workspace: state.workspace || null },
4663
4699
  activeChat,
4664
- capabilities: IDE_SESSION_CAPABILITIES,
4700
+ ...includeSessionMetadata && { capabilities: IDE_SESSION_CAPABILITIES },
4665
4701
  cdpConnected: state.cdpConnected ?? isCdpConnected(cdpManagers, state.type),
4666
4702
  currentModel: state.currentModel,
4667
4703
  currentPlan: state.currentPlan,
4668
4704
  currentAutoApprove: state.currentAutoApprove,
4669
- controlValues: state.controlValues,
4670
- providerControls: buildFallbackControls(
4671
- state.providerControls,
4672
- state.currentModel,
4673
- state.currentPlan
4674
- ),
4705
+ ...includeSessionControls && {
4706
+ controlValues: state.controlValues,
4707
+ providerControls: buildFallbackControls(
4708
+ state.providerControls,
4709
+ state.currentModel,
4710
+ state.currentPlan
4711
+ )
4712
+ },
4675
4713
  errorMessage: state.errorMessage,
4676
4714
  errorReason: state.errorReason,
4677
4715
  lastUpdated: state.lastUpdated
4678
4716
  };
4679
4717
  }
4680
- function buildExtensionAgentSession(parent, ext) {
4681
- const activeChat = normalizeActiveChatData(ext.activeChat);
4718
+ function buildExtensionAgentSession(parent, ext, options) {
4719
+ const profile = options.profile || "full";
4720
+ const activeChat = normalizeActiveChatData(ext.activeChat, getActiveChatOptions(profile));
4721
+ const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
4722
+ const includeSessionControls = shouldIncludeSessionControls(profile);
4682
4723
  return {
4683
4724
  id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
4684
4725
  parentId: parent.instanceId || parent.type,
4685
4726
  providerType: ext.type,
4686
- providerName: ext.name,
4727
+ ...includeSessionMetadata && { providerName: ext.name },
4687
4728
  kind: "agent",
4688
4729
  transport: "cdp-webview",
4689
4730
  status: normalizeManagedStatus(activeChat?.status || ext.status, {
4690
4731
  activeModal: activeChat?.activeModal || null
4691
4732
  }),
4692
4733
  title: activeChat?.title || ext.name,
4693
- workspace: parent.workspace || null,
4734
+ ...includeSessionMetadata && { workspace: parent.workspace || null },
4694
4735
  activeChat,
4695
- capabilities: EXTENSION_SESSION_CAPABILITIES,
4736
+ ...includeSessionMetadata && { capabilities: EXTENSION_SESSION_CAPABILITIES },
4696
4737
  currentModel: ext.currentModel,
4697
4738
  currentPlan: ext.currentPlan,
4698
- controlValues: ext.controlValues,
4699
- providerControls: buildFallbackControls(
4700
- ext.providerControls,
4701
- ext.currentModel,
4702
- ext.currentPlan
4703
- ),
4739
+ ...includeSessionControls && {
4740
+ controlValues: ext.controlValues,
4741
+ providerControls: buildFallbackControls(
4742
+ ext.providerControls,
4743
+ ext.currentModel,
4744
+ ext.currentPlan
4745
+ )
4746
+ },
4704
4747
  errorMessage: ext.errorMessage,
4705
4748
  errorReason: ext.errorReason,
4706
4749
  lastUpdated: ext.lastUpdated
4707
4750
  };
4708
4751
  }
4709
- function buildCliSession(state) {
4710
- const activeChat = normalizeActiveChatData(state.activeChat);
4752
+ function buildCliSession(state, options) {
4753
+ const profile = options.profile || "full";
4754
+ const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
4755
+ const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
4756
+ const includeRuntimeMetadata = shouldIncludeRuntimeMetadata(profile);
4757
+ const includeSessionControls = shouldIncludeSessionControls(profile);
4711
4758
  return {
4712
4759
  id: state.instanceId,
4713
4760
  parentId: null,
4714
4761
  providerType: state.type,
4715
- providerName: state.name,
4762
+ ...includeSessionMetadata && { providerName: state.name },
4716
4763
  providerSessionId: state.providerSessionId,
4717
4764
  kind: "agent",
4718
4765
  transport: "pty",
@@ -4720,74 +4767,85 @@ function buildCliSession(state) {
4720
4767
  activeModal: activeChat?.activeModal || null
4721
4768
  }),
4722
4769
  title: activeChat?.title || state.name,
4723
- workspace: state.workspace || null,
4724
- runtimeKey: state.runtime?.runtimeKey,
4725
- runtimeDisplayName: state.runtime?.displayName,
4726
- runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
4727
- runtimeWriteOwner: state.runtime?.writeOwner || null,
4728
- runtimeAttachedClients: state.runtime?.attachedClients || [],
4770
+ ...includeSessionMetadata && { workspace: state.workspace || null },
4771
+ ...includeRuntimeMetadata && {
4772
+ runtimeKey: state.runtime?.runtimeKey,
4773
+ runtimeDisplayName: state.runtime?.displayName,
4774
+ runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
4775
+ runtimeWriteOwner: state.runtime?.writeOwner || null,
4776
+ runtimeAttachedClients: state.runtime?.attachedClients || []
4777
+ },
4729
4778
  mode: state.mode,
4730
4779
  resume: state.resume,
4731
4780
  activeChat,
4732
- capabilities: state.mode === "terminal" ? PTY_SESSION_CAPABILITIES : CLI_CHAT_SESSION_CAPABILITIES,
4733
- controlValues: state.controlValues,
4734
- providerControls: buildFallbackControls(
4735
- state.providerControls
4736
- ),
4781
+ ...includeSessionMetadata && {
4782
+ capabilities: state.mode === "terminal" ? PTY_SESSION_CAPABILITIES : CLI_CHAT_SESSION_CAPABILITIES
4783
+ },
4784
+ ...includeSessionControls && {
4785
+ controlValues: state.controlValues,
4786
+ providerControls: buildFallbackControls(
4787
+ state.providerControls
4788
+ )
4789
+ },
4737
4790
  errorMessage: state.errorMessage,
4738
4791
  errorReason: state.errorReason,
4739
4792
  lastUpdated: state.lastUpdated
4740
4793
  };
4741
4794
  }
4742
- function buildAcpSession(state) {
4743
- const activeChat = normalizeActiveChatData(state.activeChat);
4795
+ function buildAcpSession(state, options) {
4796
+ const profile = options.profile || "full";
4797
+ const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
4798
+ const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
4799
+ const includeSessionControls = shouldIncludeSessionControls(profile);
4744
4800
  return {
4745
4801
  id: state.instanceId,
4746
4802
  parentId: null,
4747
4803
  providerType: state.type,
4748
- providerName: state.name,
4804
+ ...includeSessionMetadata && { providerName: state.name },
4749
4805
  kind: "agent",
4750
4806
  transport: "acp",
4751
4807
  status: normalizeManagedStatus(activeChat?.status || state.status, {
4752
4808
  activeModal: activeChat?.activeModal || null
4753
4809
  }),
4754
4810
  title: activeChat?.title || state.name,
4755
- workspace: state.workspace || null,
4811
+ ...includeSessionMetadata && { workspace: state.workspace || null },
4756
4812
  activeChat,
4757
- capabilities: ACP_SESSION_CAPABILITIES,
4813
+ ...includeSessionMetadata && { capabilities: ACP_SESSION_CAPABILITIES },
4758
4814
  currentModel: state.currentModel,
4759
4815
  currentPlan: state.currentPlan,
4760
- acpConfigOptions: state.acpConfigOptions,
4761
- acpModes: state.acpModes,
4762
- controlValues: state.controlValues,
4763
- providerControls: buildFallbackControls(
4764
- state.providerControls,
4765
- state.currentModel,
4766
- state.currentPlan,
4767
- state.acpConfigOptions,
4768
- state.acpModes
4769
- ),
4816
+ ...includeSessionControls && {
4817
+ acpConfigOptions: state.acpConfigOptions,
4818
+ acpModes: state.acpModes,
4819
+ controlValues: state.controlValues,
4820
+ providerControls: buildFallbackControls(
4821
+ state.providerControls,
4822
+ state.currentModel,
4823
+ state.currentPlan,
4824
+ state.acpConfigOptions,
4825
+ state.acpModes
4826
+ )
4827
+ },
4770
4828
  errorMessage: state.errorMessage,
4771
4829
  errorReason: state.errorReason,
4772
4830
  lastUpdated: state.lastUpdated
4773
4831
  };
4774
4832
  }
4775
- function buildSessionEntries(allStates, cdpManagers) {
4833
+ function buildSessionEntries(allStates, cdpManagers, options = {}) {
4776
4834
  const sessions = [];
4777
4835
  const ideStates = allStates.filter((s) => s.category === "ide");
4778
4836
  const cliStates = allStates.filter((s) => s.category === "cli");
4779
4837
  const acpStates = allStates.filter((s) => s.category === "acp");
4780
4838
  for (const state of ideStates) {
4781
- sessions.push(buildIdeWorkspaceSession(state, cdpManagers));
4839
+ sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
4782
4840
  for (const ext of state.extensions) {
4783
- sessions.push(buildExtensionAgentSession(state, ext));
4841
+ sessions.push(buildExtensionAgentSession(state, ext, options));
4784
4842
  }
4785
4843
  }
4786
4844
  for (const state of cliStates) {
4787
- sessions.push(buildCliSession(state));
4845
+ sessions.push(buildCliSession(state, options));
4788
4846
  }
4789
4847
  for (const state of acpStates) {
4790
- sessions.push(buildAcpSession(state));
4848
+ sessions.push(buildAcpSession(state, options));
4791
4849
  }
4792
4850
  const extensionParentIds = new Set(
4793
4851
  sessions.filter((session) => session.transport === "cdp-webview" && !!session.parentId).map((session) => session.parentId)
@@ -4899,7 +4957,31 @@ var init_reconcile = __esm({
4899
4957
  }
4900
4958
  });
4901
4959
 
4960
+ // ../../oss/packages/daemon-core/src/providers/contracts.ts
4961
+ function flattenContent(content) {
4962
+ if (typeof content === "string") return content;
4963
+ return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
4964
+ }
4965
+ var init_contracts = __esm({
4966
+ "../../oss/packages/daemon-core/src/providers/contracts.ts"() {
4967
+ "use strict";
4968
+ }
4969
+ });
4970
+
4902
4971
  // ../../oss/packages/daemon-core/src/commands/chat-commands.ts
4972
+ function hashSignatureParts(parts) {
4973
+ let hash2 = 2166136261;
4974
+ for (const part of parts) {
4975
+ const text = String(part || "");
4976
+ for (let i = 0; i < text.length; i += 1) {
4977
+ hash2 ^= text.charCodeAt(i);
4978
+ hash2 = Math.imul(hash2, 16777619) >>> 0;
4979
+ }
4980
+ hash2 ^= 255;
4981
+ hash2 = Math.imul(hash2, 16777619) >>> 0;
4982
+ }
4983
+ return hash2.toString(16).padStart(8, "0");
4984
+ }
4903
4985
  function getCurrentProviderType(h, fallback = "") {
4904
4986
  return h.currentSession?.providerType || h.currentProviderType || fallback;
4905
4987
  }
@@ -4973,6 +5055,134 @@ function parseMaybeJson(value) {
4973
5055
  return value;
4974
5056
  }
4975
5057
  }
5058
+ function getChatMessageSignature(message) {
5059
+ if (!message) return "";
5060
+ let content = "";
5061
+ try {
5062
+ content = JSON.stringify(message.content ?? "");
5063
+ } catch {
5064
+ content = String(message.content ?? "");
5065
+ }
5066
+ return hashSignatureParts([
5067
+ String(message.id || ""),
5068
+ String(message.index ?? ""),
5069
+ String(message.role || ""),
5070
+ String(message.receivedAt ?? message.timestamp ?? ""),
5071
+ content
5072
+ ]);
5073
+ }
5074
+ function normalizeReadChatCursor(args) {
5075
+ const knownMessageCount = Math.max(0, Number(args?.knownMessageCount || 0));
5076
+ const lastMessageSignature = typeof args?.lastMessageSignature === "string" ? args.lastMessageSignature : "";
5077
+ const tailLimit = Math.max(0, Number(args?.tailLimit || 0));
5078
+ return { knownMessageCount, lastMessageSignature, tailLimit };
5079
+ }
5080
+ function normalizeReadChatMessages(payload) {
5081
+ const messages = Array.isArray(payload.messages) ? payload.messages : [];
5082
+ return messages;
5083
+ }
5084
+ function deriveHistoryDedupKey(message) {
5085
+ const unitKey = typeof message._unitKey === "string" ? message._unitKey.trim() : "";
5086
+ if (unitKey) return `read_chat:${unitKey}`;
5087
+ const turnKey = typeof message._turnKey === "string" ? message._turnKey.trim() : "";
5088
+ if (!turnKey) return void 0;
5089
+ let content = "";
5090
+ try {
5091
+ content = JSON.stringify(message.content ?? "");
5092
+ } catch {
5093
+ content = String(message.content ?? "");
5094
+ }
5095
+ return `read_chat:${turnKey}:${String(message.role || "").toLowerCase()}:${content}`;
5096
+ }
5097
+ function toHistoryPersistedMessages(messages) {
5098
+ return messages.map((message) => ({
5099
+ role: message.role,
5100
+ content: flattenContent(message.content),
5101
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : void 0,
5102
+ kind: typeof message.kind === "string" ? message.kind : void 0,
5103
+ senderName: typeof message.senderName === "string" ? message.senderName : void 0,
5104
+ historyDedupKey: deriveHistoryDedupKey(message)
5105
+ }));
5106
+ }
5107
+ function computeReadChatSync(messages, cursor) {
5108
+ const totalMessages = messages.length;
5109
+ const lastMessageSignature = getChatMessageSignature(messages[totalMessages - 1]);
5110
+ const { knownMessageCount, lastMessageSignature: knownSignature } = cursor;
5111
+ if (!knownMessageCount || !knownSignature) {
5112
+ return {
5113
+ syncMode: "full",
5114
+ replaceFrom: 0,
5115
+ messages,
5116
+ totalMessages,
5117
+ lastMessageSignature
5118
+ };
5119
+ }
5120
+ if (knownMessageCount > totalMessages) {
5121
+ return {
5122
+ syncMode: "full",
5123
+ replaceFrom: 0,
5124
+ messages,
5125
+ totalMessages,
5126
+ lastMessageSignature
5127
+ };
5128
+ }
5129
+ if (knownMessageCount === totalMessages && knownSignature === lastMessageSignature) {
5130
+ return {
5131
+ syncMode: "noop",
5132
+ replaceFrom: totalMessages,
5133
+ messages: [],
5134
+ totalMessages,
5135
+ lastMessageSignature
5136
+ };
5137
+ }
5138
+ if (knownMessageCount < totalMessages) {
5139
+ const anchorSignature = getChatMessageSignature(messages[knownMessageCount - 1]);
5140
+ if (anchorSignature === knownSignature) {
5141
+ return {
5142
+ syncMode: "append",
5143
+ replaceFrom: knownMessageCount,
5144
+ messages: messages.slice(knownMessageCount),
5145
+ totalMessages,
5146
+ lastMessageSignature
5147
+ };
5148
+ }
5149
+ }
5150
+ const replaceFrom = Math.max(0, Math.min(knownMessageCount - 1, totalMessages));
5151
+ return {
5152
+ syncMode: replaceFrom === 0 ? "full" : "replace_tail",
5153
+ replaceFrom,
5154
+ messages: replaceFrom === 0 ? messages : messages.slice(replaceFrom),
5155
+ totalMessages,
5156
+ lastMessageSignature
5157
+ };
5158
+ }
5159
+ function buildReadChatCommandResult(payload, args) {
5160
+ const messages = normalizeReadChatMessages(payload);
5161
+ const cursor = normalizeReadChatCursor(args);
5162
+ if (!cursor.knownMessageCount && !cursor.lastMessageSignature && cursor.tailLimit > 0 && messages.length > cursor.tailLimit) {
5163
+ const tailMessages = messages.slice(-cursor.tailLimit);
5164
+ const lastMessageSignature = getChatMessageSignature(tailMessages[tailMessages.length - 1]);
5165
+ return {
5166
+ success: true,
5167
+ ...payload,
5168
+ messages: tailMessages,
5169
+ syncMode: "full",
5170
+ replaceFrom: 0,
5171
+ totalMessages: messages.length,
5172
+ lastMessageSignature
5173
+ };
5174
+ }
5175
+ const sync = computeReadChatSync(messages, cursor);
5176
+ return {
5177
+ success: true,
5178
+ ...payload,
5179
+ messages: sync.messages,
5180
+ syncMode: sync.syncMode,
5181
+ replaceFrom: sync.replaceFrom,
5182
+ totalMessages: sync.totalMessages,
5183
+ lastMessageSignature: sync.lastMessageSignature
5184
+ };
5185
+ }
4976
5186
  function didProviderConfirmSend(result) {
4977
5187
  const parsed = parseMaybeJson(result);
4978
5188
  if (parsed === true) return true;
@@ -5046,12 +5256,11 @@ async function handleReadChat(h, args) {
5046
5256
  _log(`${transport} adapter: ${adapter.cliType}`);
5047
5257
  const status = adapter.getStatus();
5048
5258
  if (status) {
5049
- return {
5050
- success: true,
5259
+ return buildReadChatCommandResult({
5051
5260
  messages: status.messages || [],
5052
5261
  status: status.status,
5053
5262
  activeModal: status.activeModal
5054
- };
5263
+ }, args);
5055
5264
  }
5056
5265
  }
5057
5266
  return { success: false, error: `${transport} adapter not found` };
@@ -5071,12 +5280,12 @@ async function handleReadChat(h, args) {
5071
5280
  _log(`Extension OK: ${parsed.messages?.length || 0} msgs`);
5072
5281
  h.historyWriter.appendNewMessages(
5073
5282
  provider?.type || "unknown_extension",
5074
- parsed.messages || [],
5283
+ toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
5075
5284
  parsed.title,
5076
5285
  args?.targetSessionId,
5077
5286
  historySessionId
5078
5287
  );
5079
- return { success: true, ...parsed };
5288
+ return buildReadChatCommandResult(parsed, args);
5080
5289
  }
5081
5290
  }
5082
5291
  } catch (e) {
@@ -5088,21 +5297,25 @@ async function handleReadChat(h, args) {
5088
5297
  if (cdp2 && parentSessionId) {
5089
5298
  const stream = await h.agentStream.collectActiveSession(cdp2, parentSessionId);
5090
5299
  if (stream?.agentType !== provider?.type) {
5091
- return { success: true, messages: [], status: "idle" };
5300
+ return buildReadChatCommandResult({ messages: [], status: "idle" }, args);
5092
5301
  }
5093
5302
  if (stream) {
5094
5303
  h.historyWriter.appendNewMessages(
5095
5304
  stream.agentType,
5096
- stream.messages || [],
5305
+ toHistoryPersistedMessages(stream.messages || []),
5097
5306
  void 0,
5098
5307
  args?.targetSessionId,
5099
5308
  historySessionId
5100
5309
  );
5101
- return { success: true, messages: stream.messages || [], status: stream.status, agentType: stream.agentType };
5310
+ return buildReadChatCommandResult({
5311
+ messages: stream.messages || [],
5312
+ status: stream.status,
5313
+ agentType: stream.agentType
5314
+ }, args);
5102
5315
  }
5103
5316
  }
5104
5317
  }
5105
- return { success: true, messages: [], status: "idle" };
5318
+ return buildReadChatCommandResult({ messages: [], status: "idle" }, args);
5106
5319
  }
5107
5320
  const cdp = h.getCdp();
5108
5321
  if (!cdp?.isConnected) return { success: false, error: "CDP not connected" };
@@ -5124,18 +5337,18 @@ async function handleReadChat(h, args) {
5124
5337
  _log(`Webview OK: ${parsed.messages?.length || 0} msgs`);
5125
5338
  h.historyWriter.appendNewMessages(
5126
5339
  provider?.type || getCurrentProviderType(h, "unknown_webview"),
5127
- parsed.messages || [],
5340
+ toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
5128
5341
  parsed.title,
5129
5342
  args?.targetSessionId,
5130
5343
  historySessionId
5131
5344
  );
5132
- return { success: true, ...parsed };
5345
+ return buildReadChatCommandResult(parsed, args);
5133
5346
  }
5134
5347
  }
5135
5348
  } catch (e) {
5136
5349
  _log(`Webview readChat error: ${e.message}`);
5137
5350
  }
5138
- return { success: true, messages: [], status: "idle" };
5351
+ return buildReadChatCommandResult({ messages: [], status: "idle" }, args);
5139
5352
  }
5140
5353
  const script = h.getProviderScript("readChat") || h.getProviderScript("read_chat");
5141
5354
  if (script) {
@@ -5152,18 +5365,18 @@ async function handleReadChat(h, args) {
5152
5365
  _log(`OK: ${parsed.messages?.length} msgs`);
5153
5366
  h.historyWriter.appendNewMessages(
5154
5367
  provider?.type || getCurrentProviderType(h, "unknown_ide"),
5155
- parsed.messages || [],
5368
+ toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
5156
5369
  parsed.title,
5157
5370
  args?.targetSessionId,
5158
5371
  historySessionId
5159
5372
  );
5160
- return { success: true, ...parsed };
5373
+ return buildReadChatCommandResult(parsed, args);
5161
5374
  }
5162
5375
  } catch (e) {
5163
5376
  LOG.info("Command", `[read_chat] Script error: ${e.message}`);
5164
5377
  }
5165
5378
  }
5166
- return { success: true, messages: [], status: "idle" };
5379
+ return buildReadChatCommandResult({ messages: [], status: "idle" }, args);
5167
5380
  }
5168
5381
  async function handleSendChat(h, args) {
5169
5382
  const text = args?.text || args?.message;
@@ -5814,6 +6027,7 @@ var RECENT_SEND_WINDOW_MS, recentSendByTarget;
5814
6027
  var init_chat_commands = __esm({
5815
6028
  "../../oss/packages/daemon-core/src/commands/chat-commands.ts"() {
5816
6029
  "use strict";
6030
+ init_contracts();
5817
6031
  init_chat_history();
5818
6032
  init_logger();
5819
6033
  RECENT_SEND_WINDOW_MS = 1200;
@@ -6299,21 +6513,21 @@ function applyProviderPatch(h, args, payload) {
6299
6513
  });
6300
6514
  }
6301
6515
  async function executeProviderScript(h, args, scriptName) {
6302
- const { agentType, ideType } = args || {};
6303
- if (!agentType) return { success: false, error: "agentType is required" };
6516
+ const resolvedProviderType = h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
6517
+ if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
6304
6518
  const loader = h.ctx.providerLoader;
6305
6519
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
6306
- const provider = loader.resolve(agentType);
6307
- if (!provider) return { success: false, error: `Provider not found: ${agentType}` };
6520
+ const provider = loader.resolve(resolvedProviderType);
6521
+ if (!provider) return { success: false, error: `Provider not found: ${resolvedProviderType}` };
6308
6522
  const webviewScriptName = `webview${scriptName.charAt(0).toUpperCase() + scriptName.slice(1)}`;
6309
6523
  const hasWebviewScript = provider.category === "ide" && !!provider.scripts?.[webviewScriptName];
6310
6524
  const actualScriptName = hasWebviewScript ? webviewScriptName : scriptName;
6311
6525
  if (!provider.scripts?.[actualScriptName]) {
6312
- return { success: false, error: `Script '${actualScriptName}' not available for ${agentType}` };
6526
+ return { success: false, error: `Script '${actualScriptName}' not available for ${resolvedProviderType}` };
6313
6527
  }
6314
6528
  const normalizedArgs = normalizeProviderScriptArgs(args);
6315
6529
  if (provider.category === "cli") {
6316
- const adapter = h.getCliAdapter(args?.targetSessionId || agentType);
6530
+ const adapter = h.getCliAdapter(args?.targetSessionId || resolvedProviderType);
6317
6531
  if (!adapter?.invokeScript) {
6318
6532
  return { success: false, error: `CLI adapter does not support script '${actualScriptName}'` };
6319
6533
  }
@@ -6338,7 +6552,7 @@ async function executeProviderScript(h, args, scriptName) {
6338
6552
  const scriptFn = provider.scripts[actualScriptName];
6339
6553
  const scriptCode = scriptFn(normalizedArgs);
6340
6554
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
6341
- const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
6555
+ const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
6342
6556
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
6343
6557
  const cdp = h.getCdp(cdpKey);
6344
6558
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
@@ -6346,7 +6560,7 @@ async function executeProviderScript(h, args, scriptName) {
6346
6560
  let result;
6347
6561
  if (provider.category === "extension") {
6348
6562
  const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
6349
- if (!runtimeSessionId) return { success: false, error: `No target session found for ${agentType}` };
6563
+ if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
6350
6564
  const parentSessionId = h.currentSession?.parentSessionId;
6351
6565
  if (parentSessionId) {
6352
6566
  await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
@@ -6375,7 +6589,7 @@ async function executeProviderScript(h, args, scriptName) {
6375
6589
  }
6376
6590
  } else {
6377
6591
  if (!targetSessionId) {
6378
- return { success: false, error: `No active session found for ${agentType}` };
6592
+ return { success: false, error: `No active session found for ${resolvedProviderType}` };
6379
6593
  }
6380
6594
  result = await cdp.evaluateInSessionFrame(targetSessionId, scriptCode);
6381
6595
  }
@@ -27201,17 +27415,6 @@ var init_acp = __esm({
27201
27415
  }
27202
27416
  });
27203
27417
 
27204
- // ../../oss/packages/daemon-core/src/providers/contracts.ts
27205
- function flattenContent(content) {
27206
- if (typeof content === "string") return content;
27207
- return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
27208
- }
27209
- var init_contracts = __esm({
27210
- "../../oss/packages/daemon-core/src/providers/contracts.ts"() {
27211
- "use strict";
27212
- }
27213
- });
27214
-
27215
27418
  // ../../oss/packages/daemon-core/src/providers/acp-provider-instance.ts
27216
27419
  var import_stream, import_child_process5, AcpProviderInstance;
27217
27420
  var init_acp_provider_instance = __esm({
@@ -32364,6 +32567,37 @@ function buildAvailableProviders(providerLoader) {
32364
32567
  ...provider.detectedPath !== void 0 ? { detectedPath: provider.detectedPath } : {}
32365
32568
  }));
32366
32569
  }
32570
+ function buildMachineInfo(profile = "full") {
32571
+ const base = {
32572
+ hostname: os17.hostname(),
32573
+ platform: os17.platform()
32574
+ };
32575
+ if (profile === "live") {
32576
+ return base;
32577
+ }
32578
+ if (profile === "metadata") {
32579
+ const memSnap2 = getHostMemorySnapshot();
32580
+ return {
32581
+ ...base,
32582
+ arch: os17.arch(),
32583
+ cpus: os17.cpus().length,
32584
+ totalMem: memSnap2.totalMem,
32585
+ release: os17.release()
32586
+ };
32587
+ }
32588
+ const memSnap = getHostMemorySnapshot();
32589
+ return {
32590
+ ...base,
32591
+ arch: os17.arch(),
32592
+ cpus: os17.cpus().length,
32593
+ totalMem: memSnap.totalMem,
32594
+ freeMem: memSnap.freeMem,
32595
+ availableMem: memSnap.availableMem,
32596
+ loadavg: os17.loadavg(),
32597
+ uptime: os17.uptime(),
32598
+ release: os17.release()
32599
+ };
32600
+ }
32367
32601
  function parseMessageTime(value) {
32368
32602
  if (typeof value === "number" && Number.isFinite(value)) return value;
32369
32603
  if (typeof value === "string") {
@@ -32419,26 +32653,35 @@ function buildRecentLaunches(recentActivity) {
32419
32653
  })).sort((a, b) => b.lastLaunchedAt - a.lastLaunchedAt).slice(0, 12);
32420
32654
  }
32421
32655
  function buildStatusSnapshot(options) {
32656
+ const profile = options.profile || "full";
32422
32657
  const cfg = loadConfig();
32423
32658
  const state = loadState();
32424
32659
  const wsState = getWorkspaceState(cfg);
32425
- const memSnap = getHostMemorySnapshot();
32426
32660
  const recentActivity = getRecentActivity(state, 20);
32427
- const sessions = buildSessionEntries(
32661
+ const unreadSourceSessions = buildSessionEntries(
32428
32662
  options.allStates,
32429
- options.cdpManagers
32663
+ options.cdpManagers,
32664
+ { profile: "full" }
32430
32665
  );
32431
- for (const session of sessions) {
32432
- const lastSeenAt = getSessionSeenAt(state, session.id);
32433
- const seenCompletionMarker = getSessionSeenMarker(state, session.id);
32434
- const lastUsedAt = getSessionLastUsedAt(session);
32435
- const completionMarker = getSessionCompletionMarker(session);
32436
- const { unread, inboxBucket } = session.surfaceHidden ? { unread: false, inboxBucket: "idle" } : getUnreadState(
32437
- getSessionMessageUpdatedAt(session) > 0,
32438
- session.status,
32666
+ const sessions = profile === "full" ? unreadSourceSessions : buildSessionEntries(
32667
+ options.allStates,
32668
+ options.cdpManagers,
32669
+ { profile }
32670
+ );
32671
+ const sessionsById = new Map(sessions.map((session) => [session.id, session]));
32672
+ for (const sourceSession of unreadSourceSessions) {
32673
+ const session = sessionsById.get(sourceSession.id);
32674
+ if (!session) continue;
32675
+ const lastSeenAt = getSessionSeenAt(state, sourceSession.id);
32676
+ const seenCompletionMarker = getSessionSeenMarker(state, sourceSession.id);
32677
+ const lastUsedAt = getSessionLastUsedAt(sourceSession);
32678
+ const completionMarker = getSessionCompletionMarker(sourceSession);
32679
+ const { unread, inboxBucket } = sourceSession.surfaceHidden ? { unread: false, inboxBucket: "idle" } : getUnreadState(
32680
+ getSessionMessageUpdatedAt(sourceSession) > 0,
32681
+ sourceSession.status,
32439
32682
  lastUsedAt,
32440
32683
  lastSeenAt,
32441
- getLastMessageRole(session),
32684
+ getLastMessageRole(sourceSession),
32442
32685
  completionMarker,
32443
32686
  seenCompletionMarker
32444
32687
  );
@@ -32448,39 +32691,30 @@ function buildStatusSnapshot(options) {
32448
32691
  if (READ_DEBUG_ENABLED && (session.unread || session.inboxBucket !== "idle" || session.providerType.includes("codex"))) {
32449
32692
  LOG.info(
32450
32693
  "RecentRead",
32451
- `snapshot session id=${session.id} provider=${session.providerType} status=${String(session.status || "")} bucket=${inboxBucket} unread=${String(unread)} lastSeenAt=${lastSeenAt} completionMarker=${completionMarker || "-"} seenMarker=${seenCompletionMarker || "-"} lastUpdated=${String(session.lastUpdated || 0)} lastUsedAt=${lastUsedAt} lastRole=${getLastMessageRole(session)} msgUpdatedAt=${getSessionMessageUpdatedAt(session)}`
32694
+ `snapshot session id=${session.id} provider=${session.providerType} status=${String(session.status || "")} bucket=${inboxBucket} unread=${String(unread)} lastSeenAt=${lastSeenAt} completionMarker=${completionMarker || "-"} seenMarker=${seenCompletionMarker || "-"} lastUpdated=${String(session.lastUpdated || 0)} lastUsedAt=${lastUsedAt} lastRole=${getLastMessageRole(sourceSession)} msgUpdatedAt=${getSessionMessageUpdatedAt(sourceSession)}`
32452
32695
  );
32453
32696
  }
32454
32697
  }
32455
- const terminalBackend = getTerminalBackendRuntimeStatus();
32698
+ const includeMachineMetadata = profile !== "live";
32699
+ const terminalBackend = includeMachineMetadata ? getTerminalBackendRuntimeStatus() : void 0;
32456
32700
  return {
32457
32701
  instanceId: options.instanceId,
32458
- version: options.version,
32459
- daemonMode: options.daemonMode,
32460
- machine: {
32461
- hostname: os17.hostname(),
32462
- platform: os17.platform(),
32463
- arch: os17.arch(),
32464
- cpus: os17.cpus().length,
32465
- totalMem: memSnap.totalMem,
32466
- freeMem: memSnap.freeMem,
32467
- availableMem: memSnap.availableMem,
32468
- loadavg: os17.loadavg(),
32469
- uptime: os17.uptime(),
32470
- release: os17.release()
32471
- },
32472
- machineNickname: options.machineNickname ?? cfg.machineNickname ?? null,
32702
+ ...includeMachineMetadata ? { version: options.version } : {},
32703
+ machine: buildMachineInfo(profile),
32704
+ ...includeMachineMetadata ? { machineNickname: options.machineNickname ?? cfg.machineNickname ?? null } : {},
32473
32705
  timestamp: options.timestamp ?? Date.now(),
32474
- detectedIdes: buildDetectedIdeInfos(options.detectedIdes, options.cdpManagers),
32475
32706
  ...options.p2p ? { p2p: options.p2p } : {},
32476
32707
  sessions,
32477
- workspaces: wsState.workspaces,
32478
- defaultWorkspaceId: wsState.defaultWorkspaceId,
32479
- defaultWorkspacePath: wsState.defaultWorkspacePath,
32480
- terminalSizingMode: cfg.terminalSizingMode || "measured",
32481
- recentLaunches: buildRecentLaunches(recentActivity),
32482
- terminalBackend,
32483
- availableProviders: buildAvailableProviders(options.providerLoader)
32708
+ ...terminalBackend ? { terminalBackend } : {},
32709
+ ...includeMachineMetadata && {
32710
+ detectedIdes: buildDetectedIdeInfos(options.detectedIdes, options.cdpManagers),
32711
+ workspaces: wsState.workspaces,
32712
+ defaultWorkspaceId: wsState.defaultWorkspaceId,
32713
+ defaultWorkspacePath: wsState.defaultWorkspacePath,
32714
+ terminalSizingMode: cfg.terminalSizingMode || "measured",
32715
+ recentLaunches: buildRecentLaunches(recentActivity),
32716
+ availableProviders: buildAvailableProviders(options.providerLoader)
32717
+ }
32484
32718
  };
32485
32719
  }
32486
32720
  var os17, READ_DEBUG_ENABLED;
@@ -32751,6 +32985,7 @@ var init_router = __esm({
32751
32985
  init_logger();
32752
32986
  init_builders();
32753
32987
  init_snapshot();
32988
+ init_snapshot();
32754
32989
  init_upgrade_helper();
32755
32990
  fs9 = __toESM(require("fs"));
32756
32991
  CHAT_COMMANDS = [
@@ -33079,6 +33314,25 @@ var init_router = __esm({
33079
33314
  updateConfig({ userName: name });
33080
33315
  return { success: true, userName: name };
33081
33316
  }
33317
+ case "get_status_metadata": {
33318
+ const snapshot = buildStatusSnapshot({
33319
+ allStates: this.deps.instanceManager.collectAllStates(),
33320
+ cdpManagers: this.deps.cdpManagers,
33321
+ providerLoader: this.deps.providerLoader,
33322
+ detectedIdes: this.deps.detectedIdes.value,
33323
+ instanceId: this.deps.statusInstanceId || loadConfig().machineId || "daemon",
33324
+ version: this.deps.statusVersion || "unknown",
33325
+ profile: "metadata"
33326
+ });
33327
+ return { success: true, status: snapshot };
33328
+ }
33329
+ case "get_machine_runtime_stats": {
33330
+ return {
33331
+ success: true,
33332
+ machine: buildMachineInfo("full"),
33333
+ timestamp: Date.now()
33334
+ };
33335
+ }
33082
33336
  case "mark_session_seen": {
33083
33337
  const sessionId = args?.sessionId;
33084
33338
  if (!sessionId || typeof sessionId !== "string") {
@@ -33292,9 +33546,58 @@ var init_reporter = __esm({
33292
33546
  }, 5e3 - elapsed);
33293
33547
  }
33294
33548
  }
33549
+ toDaemonStatusEventName(value) {
33550
+ switch (value) {
33551
+ case "agent:generating_started":
33552
+ case "agent:waiting_approval":
33553
+ case "agent:generating_completed":
33554
+ case "agent:stopped":
33555
+ case "monitor:long_generating":
33556
+ return value;
33557
+ default:
33558
+ return null;
33559
+ }
33560
+ }
33561
+ buildServerStatusEvent(event) {
33562
+ const eventName = this.toDaemonStatusEventName(event.event);
33563
+ if (!eventName) return null;
33564
+ if (eventName.startsWith("provider:")) {
33565
+ return null;
33566
+ }
33567
+ const payload = {
33568
+ event: eventName,
33569
+ timestamp: typeof event.timestamp === "number" && Number.isFinite(event.timestamp) ? event.timestamp : Date.now()
33570
+ };
33571
+ if (typeof event.targetSessionId === "string" && event.targetSessionId.trim()) {
33572
+ payload.targetSessionId = event.targetSessionId.trim();
33573
+ }
33574
+ const providerType = typeof event.providerType === "string" && event.providerType.trim() ? event.providerType.trim() : typeof event.ideType === "string" && event.ideType.trim() ? event.ideType.trim() : "";
33575
+ if (providerType) {
33576
+ payload.providerType = providerType;
33577
+ }
33578
+ if (typeof event.duration === "number" && Number.isFinite(event.duration)) {
33579
+ payload.duration = event.duration;
33580
+ }
33581
+ if (typeof event.elapsedSec === "number" && Number.isFinite(event.elapsedSec)) {
33582
+ payload.elapsedSec = event.elapsedSec;
33583
+ }
33584
+ if (typeof event.modalMessage === "string" && event.modalMessage.trim()) {
33585
+ payload.modalMessage = event.modalMessage;
33586
+ }
33587
+ if (Array.isArray(event.modalButtons)) {
33588
+ const modalButtons = event.modalButtons.filter((button) => typeof button === "string" && button.trim().length > 0);
33589
+ if (modalButtons.length > 0) {
33590
+ payload.modalButtons = modalButtons;
33591
+ }
33592
+ }
33593
+ return payload;
33594
+ }
33295
33595
  emitStatusEvent(event) {
33296
33596
  LOG.info("StatusEvent", `${event.event} (${event.providerType || event.ideType || ""})`);
33297
- this.deps.serverConn?.sendMessage("status_event", event);
33597
+ const serverEvent = this.buildServerStatusEvent(event);
33598
+ if (!serverEvent) return;
33599
+ this.deps.p2p?.sendStatusEvent(serverEvent);
33600
+ this.deps.serverConn?.sendMessage("status_event", serverEvent);
33298
33601
  }
33299
33602
  removeAgentTracking(_key) {
33300
33603
  }
@@ -33363,17 +33666,16 @@ var init_reporter = __esm({
33363
33666
  detectedIdes: this.deps.detectedIdes || [],
33364
33667
  instanceId: this.deps.instanceId,
33365
33668
  version: this.deps.daemonVersion || "unknown",
33366
- daemonMode: true,
33367
33669
  timestamp: now,
33368
33670
  p2p: {
33369
33671
  available: p2p?.isAvailable || false,
33370
33672
  state: p2p?.connectionState || "unavailable",
33371
33673
  peers: p2p?.connectedPeerCount || 0,
33372
33674
  screenshotActive: p2p?.screenshotActive || false
33373
- }
33675
+ },
33676
+ profile: "live"
33374
33677
  }),
33375
- screenshotUsage: this.deps.getScreenshotUsage?.() || null,
33376
- connectedExtensions: []
33678
+ screenshotUsage: this.deps.getScreenshotUsage?.() || null
33377
33679
  };
33378
33680
  const payloadBytes = JSON.stringify(payload).length;
33379
33681
  const p2pSent = this.sendP2PPayload(payload);
@@ -33388,16 +33690,15 @@ var init_reporter = __esm({
33388
33690
  }
33389
33691
  if (opts?.p2pOnly) return;
33390
33692
  const wsPayload = {
33391
- daemonMode: true,
33392
33693
  sessions: sessions.map((session) => ({
33393
33694
  id: session.id,
33394
33695
  parentId: session.parentId,
33395
33696
  providerType: session.providerType,
33396
- providerName: session.providerName,
33697
+ providerName: session.providerName || session.providerType,
33397
33698
  kind: session.kind,
33398
33699
  transport: session.transport,
33399
33700
  status: session.status,
33400
- workspace: session.workspace,
33701
+ workspace: session.workspace ?? null,
33401
33702
  title: session.title,
33402
33703
  cdpConnected: session.cdpConnected,
33403
33704
  currentModel: session.currentModel,
@@ -33405,9 +33706,7 @@ var init_reporter = __esm({
33405
33706
  currentAutoApprove: session.currentAutoApprove
33406
33707
  })),
33407
33708
  p2p: payload.p2p,
33408
- timestamp: now,
33409
- detectedIdes: payload.detectedIdes,
33410
- availableProviders: payload.availableProviders
33709
+ timestamp: now
33411
33710
  };
33412
33711
  const wsHash = this.simpleHash(JSON.stringify({
33413
33712
  ...wsPayload,
@@ -33424,10 +33723,15 @@ var init_reporter = __esm({
33424
33723
  // ─── P2P ─────────────────────────────────────────
33425
33724
  sendP2PPayload(payload) {
33426
33725
  const { timestamp: _ts, system: _sys, ...hashTarget } = payload;
33726
+ const sessions = Array.isArray(hashTarget.sessions) ? hashTarget.sessions.map((session) => {
33727
+ if (!session || typeof session !== "object") return session;
33728
+ const { lastUpdated: _lu, ...stableSession } = session;
33729
+ return stableSession;
33730
+ }) : hashTarget.sessions;
33427
33731
  const hashPayload = hashTarget.machine ? (() => {
33428
33732
  const { freeMem: _f, availableMem: _a2, loadavg: _l, uptime: _u, ...stableMachine } = hashTarget.machine;
33429
- return { ...hashTarget, machine: stableMachine };
33430
- })() : hashTarget;
33733
+ return { ...hashTarget, sessions, machine: stableMachine };
33734
+ })() : { ...hashTarget, sessions };
33431
33735
  const h = this.simpleHash(JSON.stringify(hashPayload));
33432
33736
  if (h !== this.lastP2PStatusHash) {
33433
33737
  this.lastP2PStatusHash = h;
@@ -40638,6 +40942,8 @@ async function initDaemonComponents(config2) {
40638
40942
  onStatusChange: config2.onStatusChange,
40639
40943
  onPostChatCommand: config2.onPostChatCommand,
40640
40944
  sessionHostControl: config2.sessionHostControl,
40945
+ statusInstanceId: config2.statusInstanceId,
40946
+ statusVersion: config2.statusVersion,
40641
40947
  getCdpLogFn: config2.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
40642
40948
  });
40643
40949
  poller = new AgentStreamPoller({
@@ -40763,6 +41069,7 @@ __export(src_exports, {
40763
41069
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
40764
41070
  VersionArchive: () => VersionArchive,
40765
41071
  appendRecentActivity: () => appendRecentActivity,
41072
+ buildMachineInfo: () => buildMachineInfo,
40766
41073
  buildSessionEntries: () => buildSessionEntries,
40767
41074
  buildStatusSnapshot: () => buildStatusSnapshot,
40768
41075
  connectCdpManager: () => connectCdpManager,
@@ -41036,6 +41343,7 @@ var init_server_connection = __esm({
41036
41343
  this.ws = new import_ws2.default(fullUrl, {
41037
41344
  headers: {
41038
41345
  "X-ADHDev-Token": this.options.token,
41346
+ "X-ADHDev-Daemon": JSON.stringify(this.options.cliInfo),
41039
41347
  "X-ADHDev-IDE": JSON.stringify(this.options.cliInfo),
41040
41348
  "X-ADHDev-Version": this.options.daemonVersion || "unknown"
41041
41349
  }
@@ -41120,7 +41428,7 @@ var init_server_connection = __esm({
41120
41428
  type: "auth",
41121
41429
  payload: {
41122
41430
  token: this.options.token,
41123
- ide: this.options.cliInfo
41431
+ daemon: this.options.cliInfo
41124
41432
  },
41125
41433
  timestamp: Date.now()
41126
41434
  });
@@ -41360,11 +41668,19 @@ function routeDataChannelMessage(peerId, msg, peers, handlers) {
41360
41668
  if (peer) peer.lastPongAt = Date.now();
41361
41669
  return;
41362
41670
  }
41671
+ if (parsed.type === "subscribe") {
41672
+ handleSubscribe(peerId, parsed, peers, handlers);
41673
+ return;
41674
+ }
41675
+ if (parsed.type === "unsubscribe") {
41676
+ handleUnsubscribe(peerId, parsed, peers);
41677
+ return;
41678
+ }
41363
41679
  if (parsed.type === "screenshot_start") {
41364
41680
  const peer = peers.get(peerId);
41365
41681
  const permission = peer?.sharePermission;
41366
- if (!parsed.ideType) {
41367
- log(`screenshot_start: REJECTED \u2014 no ideType from peer ${peerId}`);
41682
+ if (!parsed.targetSessionId) {
41683
+ log(`screenshot_start: REJECTED \u2014 no targetSessionId from peer ${peerId}`);
41368
41684
  return;
41369
41685
  }
41370
41686
  if (!canPeerStartScreenshots(permission)) {
@@ -41373,9 +41689,9 @@ function routeDataChannelMessage(peerId, msg, peers, handlers) {
41373
41689
  }
41374
41690
  if (peer) {
41375
41691
  peer.screenshotActive = true;
41376
- peer.screenshotIdeType = parsed.ideType;
41692
+ peer.screenshotTargetSessionId = parsed.targetSessionId;
41377
41693
  peer.needsFirstFrame = true;
41378
- log(`screenshot_start: peer=${peerId}, ideType=${parsed.ideType}, channelOpen=${!!peer.dataChannel}, state=${peer.state}`);
41694
+ log(`screenshot_start: peer=${peerId}, targetSessionId=${parsed.targetSessionId}, channelOpen=${!!peer.dataChannel}, state=${peer.state}`);
41379
41695
  handlers.screenshotStartHandler?.();
41380
41696
  } else {
41381
41697
  log(`screenshot_start: peer ${peerId} NOT FOUND in peers map!`);
@@ -41427,6 +41743,87 @@ function routeDataChannelMessage(peerId, msg, peers, handlers) {
41427
41743
  log(`Parse error from peer ${peerId}: ${e?.message}`);
41428
41744
  }
41429
41745
  }
41746
+ function handleSubscribe(peerId, msg, peers, handlers) {
41747
+ const peer = peers.get(peerId);
41748
+ if (!peer) return;
41749
+ if (msg.topic === "session.chat_tail") {
41750
+ const targetSessionId = typeof msg.params?.targetSessionId === "string" ? msg.params.targetSessionId.trim() : "";
41751
+ if (!targetSessionId) return;
41752
+ if (!peer.chatSubscriptions) peer.chatSubscriptions = /* @__PURE__ */ new Map();
41753
+ peer.chatSubscriptions.set(msg.key, {
41754
+ key: msg.key,
41755
+ params: msg.params,
41756
+ seq: 0,
41757
+ cursor: {
41758
+ knownMessageCount: Math.max(0, Number(msg.params.knownMessageCount || 0)),
41759
+ lastMessageSignature: typeof msg.params.lastMessageSignature === "string" ? msg.params.lastMessageSignature : "",
41760
+ tailLimit: Math.max(0, Number(msg.params.tailLimit || 0))
41761
+ },
41762
+ lastDeliveredSignature: ""
41763
+ });
41764
+ } else if (msg.topic === "machine.runtime") {
41765
+ if (!peer.machineRuntimeSubscriptions) peer.machineRuntimeSubscriptions = /* @__PURE__ */ new Map();
41766
+ peer.machineRuntimeSubscriptions.set(msg.key, {
41767
+ key: msg.key,
41768
+ params: msg.params,
41769
+ seq: 0,
41770
+ lastSentAt: 0
41771
+ });
41772
+ } else if (msg.topic === "session_host.diagnostics") {
41773
+ if (!peer.sessionHostDiagnosticsSubscriptions) peer.sessionHostDiagnosticsSubscriptions = /* @__PURE__ */ new Map();
41774
+ peer.sessionHostDiagnosticsSubscriptions.set(msg.key, {
41775
+ key: msg.key,
41776
+ params: msg.params,
41777
+ seq: 0,
41778
+ lastSentAt: 0
41779
+ });
41780
+ } else if (msg.topic === "session.modal") {
41781
+ const targetSessionId = typeof msg.params?.targetSessionId === "string" ? msg.params.targetSessionId.trim() : "";
41782
+ if (!targetSessionId) return;
41783
+ if (!peer.sessionModalSubscriptions) peer.sessionModalSubscriptions = /* @__PURE__ */ new Map();
41784
+ peer.sessionModalSubscriptions.set(msg.key, {
41785
+ key: msg.key,
41786
+ params: msg.params,
41787
+ seq: 0,
41788
+ lastSentAt: 0,
41789
+ lastDeliveredSignature: ""
41790
+ });
41791
+ } else if (msg.topic === "daemon.metadata") {
41792
+ if (!peer.daemonMetadataSubscriptions) peer.daemonMetadataSubscriptions = /* @__PURE__ */ new Map();
41793
+ peer.daemonMetadataSubscriptions.set(msg.key, {
41794
+ key: msg.key,
41795
+ params: msg.params,
41796
+ seq: 0,
41797
+ lastSentAt: 0
41798
+ });
41799
+ } else {
41800
+ return;
41801
+ }
41802
+ handlers.subscriptionChangeHandler?.();
41803
+ }
41804
+ function handleUnsubscribe(peerId, msg, peers) {
41805
+ const peer = peers.get(peerId);
41806
+ if (!peer) return;
41807
+ if (msg.topic === "session.chat_tail") {
41808
+ peer.chatSubscriptions?.delete(msg.key);
41809
+ return;
41810
+ }
41811
+ if (msg.topic === "machine.runtime") {
41812
+ peer.machineRuntimeSubscriptions?.delete(msg.key);
41813
+ return;
41814
+ }
41815
+ if (msg.topic === "session_host.diagnostics") {
41816
+ peer.sessionHostDiagnosticsSubscriptions?.delete(msg.key);
41817
+ return;
41818
+ }
41819
+ if (msg.topic === "session.modal") {
41820
+ peer.sessionModalSubscriptions?.delete(msg.key);
41821
+ return;
41822
+ }
41823
+ if (msg.topic === "daemon.metadata") {
41824
+ peer.daemonMetadataSubscriptions?.delete(msg.key);
41825
+ }
41826
+ }
41430
41827
  async function handleP2PCommand(peerId, msg, peers, handlers) {
41431
41828
  const { id, commandType, data } = msg;
41432
41829
  const peer = peers.get(peerId);
@@ -41447,7 +41844,7 @@ async function handleP2PCommand(peerId, msg, peers, handlers) {
41447
41844
  }
41448
41845
  }
41449
41846
  async function handleInputEvent(peerId, msg, peers, handlers) {
41450
- const { id, action, params, instanceId } = msg;
41847
+ const { id, action, params, targetSessionId: explicitTargetSessionId } = msg;
41451
41848
  const peer = peers.get(peerId);
41452
41849
  const permission = peer?.sharePermission;
41453
41850
  if (!canPeerUseRemoteInput(permission)) {
@@ -41459,11 +41856,11 @@ async function handleInputEvent(peerId, msg, peers, handlers) {
41459
41856
  return;
41460
41857
  }
41461
41858
  try {
41462
- const ideType = instanceId || peer?.screenshotIdeType;
41463
- if (!ideType) {
41464
- log(`[Input] WARNING: No instanceId or screenshotIdeType for peer ${peerId}`);
41859
+ const targetSessionId = explicitTargetSessionId || peer?.screenshotTargetSessionId;
41860
+ if (!targetSessionId) {
41861
+ log(`[Input] WARNING: No targetSessionId for peer ${peerId}`);
41465
41862
  }
41466
- const result = await handlers.inputHandler({ action, params, ideType });
41863
+ const result = await handlers.inputHandler({ action, params, targetSessionId });
41467
41864
  sendToPeer(peer, { id, type: "response", success: true, result });
41468
41865
  } catch (e) {
41469
41866
  sendToPeer(peer, { id, type: "response", success: false, error: e?.message });
@@ -41522,6 +41919,35 @@ var init_screenshot_sender = __esm({
41522
41919
  }
41523
41920
  return sentAny;
41524
41921
  }
41922
+ sendStatusEvent(peers, event) {
41923
+ const payload = JSON.stringify({
41924
+ type: "status_event",
41925
+ payload: event,
41926
+ timestamp: Date.now()
41927
+ });
41928
+ let sentAny = false;
41929
+ for (const peer of peers.values()) {
41930
+ if (peer.state !== "connected" || !peer.dataChannel) continue;
41931
+ try {
41932
+ peer.dataChannel.sendMessage(payload);
41933
+ sentAny = true;
41934
+ } catch {
41935
+ }
41936
+ }
41937
+ return sentAny;
41938
+ }
41939
+ sendTopicUpdateToPeer(peer, update) {
41940
+ if (!peer?.dataChannel || peer.state !== "connected") return false;
41941
+ try {
41942
+ peer.dataChannel.sendMessage(JSON.stringify({
41943
+ type: "topic_update",
41944
+ update
41945
+ }));
41946
+ return true;
41947
+ } catch {
41948
+ return false;
41949
+ }
41950
+ }
41525
41951
  /** Broadcast runtime session output to all connected peers */
41526
41952
  broadcastSessionOutput(peers, sessionId, data) {
41527
41953
  const msg = JSON.stringify({ type: "session_output", sessionId, data });
@@ -41683,7 +42109,12 @@ async function initiateConnection(deps, peerId, sharePermission) {
41683
42109
  pendingCandidates: [],
41684
42110
  remoteDescriptionSet: false,
41685
42111
  isRelay: false,
41686
- lastPongAt: Date.now()
42112
+ lastPongAt: Date.now(),
42113
+ chatSubscriptions: /* @__PURE__ */ new Map(),
42114
+ machineRuntimeSubscriptions: /* @__PURE__ */ new Map(),
42115
+ sessionHostDiagnosticsSubscriptions: /* @__PURE__ */ new Map(),
42116
+ sessionModalSubscriptions: /* @__PURE__ */ new Map(),
42117
+ daemonMetadataSubscriptions: /* @__PURE__ */ new Map()
41687
42118
  };
41688
42119
  deps.peers.set(pid, entry);
41689
42120
  deps.notifyStateChange();
@@ -41930,7 +42361,8 @@ var init_daemon_p2p = __esm({
41930
42361
  commandHandler: null,
41931
42362
  ptyInputHandler: null,
41932
42363
  ptyResizeHandler: null,
41933
- screenshotStartHandler: null
42364
+ screenshotStartHandler: null,
42365
+ subscriptionChangeHandler: null
41934
42366
  };
41935
42367
  get screenshotActive() {
41936
42368
  for (const peer of this.peers.values()) {
@@ -41938,11 +42370,11 @@ var init_daemon_p2p = __esm({
41938
42370
  }
41939
42371
  return false;
41940
42372
  }
41941
- /** Get the ideType for the currently active screenshot request */
41942
- get screenshotIdeType() {
42373
+ /** Get the target session for the currently active screenshot request */
42374
+ get screenshotTargetSessionId() {
41943
42375
  for (const peer of this.peers.values()) {
41944
- if (peer.screenshotActive && peer.state === "connected" && peer.screenshotIdeType) {
41945
- return peer.screenshotIdeType;
42376
+ if (peer.screenshotActive && peer.state === "connected" && peer.screenshotTargetSessionId) {
42377
+ return peer.screenshotTargetSessionId;
41946
42378
  }
41947
42379
  }
41948
42380
  return void 0;
@@ -42094,6 +42526,9 @@ ${e?.stack || ""}`);
42094
42526
  sendStatus(status) {
42095
42527
  return this.screenshotSender.sendStatus(this.peers, status);
42096
42528
  }
42529
+ sendStatusEvent(event) {
42530
+ return this.screenshotSender.sendStatusEvent(this.peers, event);
42531
+ }
42097
42532
  broadcastSessionOutput(sessionId, data) {
42098
42533
  return this.screenshotSender.broadcastSessionOutput(this.peers, sessionId, data);
42099
42534
  }
@@ -42122,6 +42557,89 @@ ${e?.stack || ""}`);
42122
42557
  onScreenshotStart(handler) {
42123
42558
  this.handlers.screenshotStartHandler = handler;
42124
42559
  }
42560
+ onSubscriptionChange(handler) {
42561
+ this.handlers.subscriptionChangeHandler = handler;
42562
+ }
42563
+ hasChatSubscriptions() {
42564
+ for (const peer of this.peers.values()) {
42565
+ if (peer.chatSubscriptions && peer.chatSubscriptions.size > 0) return true;
42566
+ }
42567
+ return false;
42568
+ }
42569
+ hasMachineRuntimeSubscriptions() {
42570
+ for (const peer of this.peers.values()) {
42571
+ if (peer.machineRuntimeSubscriptions && peer.machineRuntimeSubscriptions.size > 0) return true;
42572
+ }
42573
+ return false;
42574
+ }
42575
+ hasSessionHostDiagnosticsSubscriptions() {
42576
+ for (const peer of this.peers.values()) {
42577
+ if (peer.sessionHostDiagnosticsSubscriptions && peer.sessionHostDiagnosticsSubscriptions.size > 0) return true;
42578
+ }
42579
+ return false;
42580
+ }
42581
+ hasSessionModalSubscriptions() {
42582
+ for (const peer of this.peers.values()) {
42583
+ if (peer.sessionModalSubscriptions && peer.sessionModalSubscriptions.size > 0) return true;
42584
+ }
42585
+ return false;
42586
+ }
42587
+ hasDaemonMetadataSubscriptions() {
42588
+ for (const peer of this.peers.values()) {
42589
+ if (peer.daemonMetadataSubscriptions && peer.daemonMetadataSubscriptions.size > 0) return true;
42590
+ }
42591
+ return false;
42592
+ }
42593
+ async flushChatSubscriptions(builder) {
42594
+ for (const peer of this.peers.values()) {
42595
+ if (peer.state !== "connected" || !peer.chatSubscriptions || peer.chatSubscriptions.size === 0) continue;
42596
+ for (const subscription of peer.chatSubscriptions.values()) {
42597
+ const update = await builder(subscription);
42598
+ if (!update) continue;
42599
+ this.screenshotSender.sendTopicUpdateToPeer(peer, update);
42600
+ }
42601
+ }
42602
+ }
42603
+ async flushMachineRuntimeSubscriptions(builder) {
42604
+ for (const peer of this.peers.values()) {
42605
+ if (peer.state !== "connected" || !peer.machineRuntimeSubscriptions || peer.machineRuntimeSubscriptions.size === 0) continue;
42606
+ for (const subscription of peer.machineRuntimeSubscriptions.values()) {
42607
+ const update = await builder(subscription);
42608
+ if (!update) continue;
42609
+ this.screenshotSender.sendTopicUpdateToPeer(peer, update);
42610
+ }
42611
+ }
42612
+ }
42613
+ async flushSessionHostDiagnosticsSubscriptions(builder) {
42614
+ for (const peer of this.peers.values()) {
42615
+ if (peer.state !== "connected" || !peer.sessionHostDiagnosticsSubscriptions || peer.sessionHostDiagnosticsSubscriptions.size === 0) continue;
42616
+ for (const subscription of peer.sessionHostDiagnosticsSubscriptions.values()) {
42617
+ const update = await builder(subscription);
42618
+ if (!update) continue;
42619
+ this.screenshotSender.sendTopicUpdateToPeer(peer, update);
42620
+ }
42621
+ }
42622
+ }
42623
+ async flushSessionModalSubscriptions(builder) {
42624
+ for (const peer of this.peers.values()) {
42625
+ if (peer.state !== "connected" || !peer.sessionModalSubscriptions || peer.sessionModalSubscriptions.size === 0) continue;
42626
+ for (const subscription of peer.sessionModalSubscriptions.values()) {
42627
+ const update = await builder(subscription);
42628
+ if (!update) continue;
42629
+ this.screenshotSender.sendTopicUpdateToPeer(peer, update);
42630
+ }
42631
+ }
42632
+ }
42633
+ async flushDaemonMetadataSubscriptions(builder) {
42634
+ for (const peer of this.peers.values()) {
42635
+ if (peer.state !== "connected" || !peer.daemonMetadataSubscriptions || peer.daemonMetadataSubscriptions.size === 0) continue;
42636
+ for (const subscription of peer.daemonMetadataSubscriptions.values()) {
42637
+ const update = await builder(subscription);
42638
+ if (!update) continue;
42639
+ this.screenshotSender.sendTopicUpdateToPeer(peer, update);
42640
+ }
42641
+ }
42642
+ }
42125
42643
  };
42126
42644
  }
42127
42645
  });
@@ -48782,12 +49300,12 @@ var init_screenshot_controller = __esm({
48782
49300
  async tick() {
48783
49301
  if (!this.deps.isRunning()) return;
48784
49302
  const active = this.deps.isScreenshotActive();
48785
- const ssIdeType = this.deps.getScreenshotIdeType();
48786
- if (active && !ssIdeType) {
49303
+ const targetSessionId = this.deps.getScreenshotTargetSessionId();
49304
+ if (active && !targetSessionId) {
48787
49305
  this.timer = setTimeout(() => this.tick(), 500);
48788
49306
  return;
48789
49307
  }
48790
- const cdp = ssIdeType ? this.deps.getCdp(ssIdeType) : null;
49308
+ const cdp = targetSessionId ? this.deps.getCdp(targetSessionId) : null;
48791
49309
  const isRelay = this.deps.isUsingRelay();
48792
49310
  const profile = isRelay ? this.profileRelay : this.profileDirect;
48793
49311
  this.checkBudgetReset();
@@ -49318,6 +49836,48 @@ __export(adhdev_daemon_exports, {
49318
49836
  isDaemonRunning: () => isDaemonRunning,
49319
49837
  stopDaemon: () => stopDaemon
49320
49838
  });
49839
+ function hashSignatureParts2(parts) {
49840
+ let hash2 = 2166136261;
49841
+ for (const part of parts) {
49842
+ const text = String(part || "");
49843
+ for (let i = 0; i < text.length; i += 1) {
49844
+ hash2 ^= text.charCodeAt(i);
49845
+ hash2 = Math.imul(hash2, 16777619) >>> 0;
49846
+ }
49847
+ hash2 ^= 255;
49848
+ hash2 = Math.imul(hash2, 16777619) >>> 0;
49849
+ }
49850
+ return hash2.toString(16).padStart(8, "0");
49851
+ }
49852
+ function buildChatTailDeliverySignature(payload) {
49853
+ let messages = "";
49854
+ try {
49855
+ messages = JSON.stringify(payload.messages);
49856
+ } catch {
49857
+ messages = String(payload.messages.length);
49858
+ }
49859
+ return hashSignatureParts2([
49860
+ payload.sessionId,
49861
+ payload.historySessionId || "",
49862
+ payload.status,
49863
+ payload.title || "",
49864
+ payload.syncMode,
49865
+ String(payload.replaceFrom),
49866
+ String(payload.totalMessages),
49867
+ payload.lastMessageSignature,
49868
+ payload.activeModal ? `${payload.activeModal.message}|${payload.activeModal.buttons.join("")}` : "",
49869
+ messages
49870
+ ]);
49871
+ }
49872
+ function buildSessionModalDeliverySignature(payload) {
49873
+ return hashSignatureParts2([
49874
+ payload.sessionId,
49875
+ payload.status,
49876
+ payload.title || "",
49877
+ payload.modalMessage || "",
49878
+ Array.isArray(payload.modalButtons) ? payload.modalButtons.join("") : ""
49879
+ ]);
49880
+ }
49321
49881
  function getDaemonPidFile() {
49322
49882
  const dir = path26.join(os23.homedir(), ".adhdev");
49323
49883
  if (!fs17.existsSync(dir)) fs17.mkdirSync(dir, { recursive: true });
@@ -49381,7 +49941,7 @@ function stopDaemon() {
49381
49941
  return false;
49382
49942
  }
49383
49943
  }
49384
- var os23, fs17, path26, import_http, import_ws3, import_chalk2, pkgVersion, AdhdevDaemon;
49944
+ var os23, fs17, path26, import_http, import_ws3, import_chalk2, pkgVersion, ACTIVE_CHAT_POLL_STATUSES, AdhdevDaemon;
49385
49945
  var init_adhdev_daemon = __esm({
49386
49946
  "src/adhdev-daemon.ts"() {
49387
49947
  "use strict";
@@ -49399,7 +49959,12 @@ var init_adhdev_daemon = __esm({
49399
49959
  import_ws3 = require("ws");
49400
49960
  import_chalk2 = __toESM(require("chalk"));
49401
49961
  init_version();
49402
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.35" });
49962
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.37" });
49963
+ ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
49964
+ "generating",
49965
+ "waiting_approval",
49966
+ "starting"
49967
+ ]);
49403
49968
  AdhdevDaemon = class {
49404
49969
  localHttpServer = null;
49405
49970
  localWss = null;
@@ -49408,6 +49973,11 @@ var init_adhdev_daemon = __esm({
49408
49973
  p2p = null;
49409
49974
  screenshotController = null;
49410
49975
  statusReporter = null;
49976
+ topicSubscriptionTimer = null;
49977
+ p2pChatFlushInFlight = false;
49978
+ pendingP2PChatFlush = false;
49979
+ pendingP2PChatFlushOnlyActive = true;
49980
+ hotP2PChatSessionIds = /* @__PURE__ */ new Set();
49411
49981
  components = null;
49412
49982
  sessionHostEndpoint = null;
49413
49983
  sessionHostController = null;
@@ -49431,6 +50001,252 @@ var init_adhdev_daemon = __esm({
49431
50001
  const mode = this.getCliPresentationMode(sessionId);
49432
50002
  return mode === "chat" || mode === "terminal";
49433
50003
  }
50004
+ async buildChatTailUpdateForSubscription(subscription) {
50005
+ const result = await this.components.router.execute("read_chat", {
50006
+ targetSessionId: subscription.params.targetSessionId,
50007
+ ...subscription.params.historySessionId ? { historySessionId: subscription.params.historySessionId } : {},
50008
+ knownMessageCount: subscription.cursor.knownMessageCount,
50009
+ lastMessageSignature: subscription.cursor.lastMessageSignature,
50010
+ ...subscription.cursor.tailLimit > 0 ? { tailLimit: subscription.cursor.tailLimit } : {}
50011
+ }, "p2p");
50012
+ if (!result?.success || result.syncMode === "noop") {
50013
+ if (result?.success) {
50014
+ subscription.cursor = {
50015
+ knownMessageCount: Math.max(0, Number(result.totalMessages || subscription.cursor.knownMessageCount)),
50016
+ lastMessageSignature: typeof result.lastMessageSignature === "string" ? result.lastMessageSignature : subscription.cursor.lastMessageSignature,
50017
+ tailLimit: subscription.cursor.tailLimit
50018
+ };
50019
+ }
50020
+ return null;
50021
+ }
50022
+ subscription.seq += 1;
50023
+ const syncMode = result.syncMode === "append" || result.syncMode === "replace_tail" || result.syncMode === "noop" || result.syncMode === "full" ? result.syncMode : "full";
50024
+ subscription.cursor = {
50025
+ knownMessageCount: Math.max(0, Number(result.totalMessages || 0)),
50026
+ lastMessageSignature: typeof result.lastMessageSignature === "string" ? result.lastMessageSignature : "",
50027
+ tailLimit: subscription.cursor.tailLimit
50028
+ };
50029
+ const activeModal = result.activeModal && typeof result.activeModal === "object" && typeof result.activeModal.message === "string" && Array.isArray(result.activeModal.buttons) ? {
50030
+ message: result.activeModal.message,
50031
+ buttons: result.activeModal.buttons.filter((button) => typeof button === "string")
50032
+ } : null;
50033
+ const deliverySignature = buildChatTailDeliverySignature({
50034
+ sessionId: subscription.params.targetSessionId,
50035
+ ...subscription.params.historySessionId ? { historySessionId: subscription.params.historySessionId } : {},
50036
+ messages: Array.isArray(result.messages) ? result.messages : [],
50037
+ status: typeof result.status === "string" ? result.status : "idle",
50038
+ ...typeof result.title === "string" ? { title: result.title } : {},
50039
+ ...activeModal ? { activeModal } : {},
50040
+ syncMode,
50041
+ replaceFrom: Number(result.replaceFrom || 0),
50042
+ totalMessages: Number(result.totalMessages || 0),
50043
+ lastMessageSignature: typeof result.lastMessageSignature === "string" ? result.lastMessageSignature : ""
50044
+ });
50045
+ if (deliverySignature === subscription.lastDeliveredSignature) {
50046
+ return null;
50047
+ }
50048
+ subscription.lastDeliveredSignature = deliverySignature;
50049
+ return {
50050
+ topic: "session.chat_tail",
50051
+ key: subscription.key,
50052
+ sessionId: subscription.params.targetSessionId,
50053
+ ...subscription.params.historySessionId ? { historySessionId: subscription.params.historySessionId } : {},
50054
+ seq: subscription.seq,
50055
+ timestamp: Date.now(),
50056
+ messages: Array.isArray(result.messages) ? result.messages : [],
50057
+ status: typeof result.status === "string" ? result.status : "idle",
50058
+ ...typeof result.title === "string" ? { title: result.title } : {},
50059
+ ...activeModal ? { activeModal } : {},
50060
+ syncMode,
50061
+ replaceFrom: Number(result.replaceFrom || 0),
50062
+ totalMessages: Number(result.totalMessages || 0),
50063
+ lastMessageSignature: typeof result.lastMessageSignature === "string" ? result.lastMessageSignature : ""
50064
+ };
50065
+ }
50066
+ buildLiveStatusSnapshot() {
50067
+ return buildStatusSnapshot({
50068
+ allStates: this.components.instanceManager.collectAllStates(),
50069
+ cdpManagers: this.components.cdpManagers,
50070
+ providerLoader: this.components.providerLoader,
50071
+ detectedIdes: this.components.detectedIdes.value.map((ide) => ({
50072
+ ...ide,
50073
+ path: ide.path ?? void 0
50074
+ })),
50075
+ instanceId: `daemon_${loadConfig().machineId || "daemon"}`,
50076
+ version: pkgVersion,
50077
+ profile: "live"
50078
+ });
50079
+ }
50080
+ getHotChatSessionIdsForP2PFlush() {
50081
+ const snapshot = this.buildLiveStatusSnapshot();
50082
+ const active = new Set(
50083
+ snapshot.sessions.filter((session) => ACTIVE_CHAT_POLL_STATUSES.has(String(session.status || "").toLowerCase())).map((session) => session.id)
50084
+ );
50085
+ const finalizing = new Set(
50086
+ Array.from(this.hotP2PChatSessionIds).filter((sessionId) => !active.has(sessionId))
50087
+ );
50088
+ this.hotP2PChatSessionIds = active;
50089
+ return { active, finalizing };
50090
+ }
50091
+ async flushP2PChatSubscriptions(options = {}) {
50092
+ if (!this.p2p?.isConnected || !this.p2p.hasChatSubscriptions()) return;
50093
+ const onlyActive = options.onlyActive === true;
50094
+ if (this.p2pChatFlushInFlight) {
50095
+ this.pendingP2PChatFlush = true;
50096
+ this.pendingP2PChatFlushOnlyActive = this.pendingP2PChatFlushOnlyActive && onlyActive;
50097
+ return;
50098
+ }
50099
+ this.p2pChatFlushInFlight = true;
50100
+ try {
50101
+ const hotSessionIds = onlyActive ? this.getHotChatSessionIdsForP2PFlush() : null;
50102
+ await this.p2p.flushChatSubscriptions(async (subscription) => {
50103
+ if (hotSessionIds && !hotSessionIds.active.has(subscription.params.targetSessionId) && !hotSessionIds.finalizing.has(subscription.params.targetSessionId)) {
50104
+ return null;
50105
+ }
50106
+ return this.buildChatTailUpdateForSubscription(subscription);
50107
+ });
50108
+ } finally {
50109
+ this.p2pChatFlushInFlight = false;
50110
+ if (this.pendingP2PChatFlush) {
50111
+ const pendingOnlyActive = this.pendingP2PChatFlushOnlyActive;
50112
+ this.pendingP2PChatFlush = false;
50113
+ this.pendingP2PChatFlushOnlyActive = true;
50114
+ void this.flushP2PChatSubscriptions({ onlyActive: pendingOnlyActive });
50115
+ }
50116
+ }
50117
+ }
50118
+ buildMachineRuntimeUpdateForSubscription(subscription) {
50119
+ const intervalMs = Math.max(5e3, Number(subscription.params.intervalMs || 15e3));
50120
+ const now = Date.now();
50121
+ if (subscription.lastSentAt > 0 && now - subscription.lastSentAt < intervalMs) {
50122
+ return null;
50123
+ }
50124
+ subscription.seq += 1;
50125
+ subscription.lastSentAt = now;
50126
+ return {
50127
+ topic: "machine.runtime",
50128
+ key: subscription.key,
50129
+ machine: buildMachineInfo("full"),
50130
+ seq: subscription.seq,
50131
+ timestamp: now
50132
+ };
50133
+ }
50134
+ async flushP2PMachineRuntimeSubscriptions() {
50135
+ if (!this.p2p?.isConnected || !this.p2p.hasMachineRuntimeSubscriptions()) return;
50136
+ await this.p2p.flushMachineRuntimeSubscriptions(async (subscription) => this.buildMachineRuntimeUpdateForSubscription(subscription));
50137
+ }
50138
+ async buildSessionHostDiagnosticsUpdateForSubscription(subscription) {
50139
+ if (!this.sessionHostController) return null;
50140
+ const intervalMs = Math.max(5e3, Number(subscription.params.intervalMs || 1e4));
50141
+ const now = Date.now();
50142
+ if (subscription.lastSentAt > 0 && now - subscription.lastSentAt < intervalMs) {
50143
+ return null;
50144
+ }
50145
+ const diagnostics = await this.sessionHostController.getDiagnostics({
50146
+ includeSessions: subscription.params.includeSessions !== false,
50147
+ limit: Number(subscription.params.limit) || void 0
50148
+ });
50149
+ subscription.seq += 1;
50150
+ subscription.lastSentAt = now;
50151
+ return {
50152
+ topic: "session_host.diagnostics",
50153
+ key: subscription.key,
50154
+ diagnostics,
50155
+ seq: subscription.seq,
50156
+ timestamp: now
50157
+ };
50158
+ }
50159
+ async flushP2PSessionHostDiagnosticsSubscriptions() {
50160
+ if (!this.p2p?.isConnected || !this.p2p.hasSessionHostDiagnosticsSubscriptions()) return;
50161
+ await this.p2p.flushSessionHostDiagnosticsSubscriptions(
50162
+ async (subscription) => this.buildSessionHostDiagnosticsUpdateForSubscription(subscription)
50163
+ );
50164
+ }
50165
+ findProviderStateBySessionId(sessionId) {
50166
+ if (!this.components || !sessionId) return null;
50167
+ const states = this.components.instanceManager.collectAllStates();
50168
+ for (const state of states) {
50169
+ if (state.instanceId === sessionId) return state;
50170
+ if (state.category === "ide") {
50171
+ const child = state.extensions.find((entry) => entry.instanceId === sessionId);
50172
+ if (child) return child;
50173
+ }
50174
+ }
50175
+ return null;
50176
+ }
50177
+ buildSessionModalUpdateForSubscription(subscription) {
50178
+ const state = this.findProviderStateBySessionId(subscription.params.targetSessionId);
50179
+ if (!state) return null;
50180
+ const now = Date.now();
50181
+ const activeModal = state.activeChat?.activeModal;
50182
+ const modalButtons = Array.isArray(activeModal?.buttons) ? activeModal.buttons.filter((button) => typeof button === "string") : [];
50183
+ const status = String(state.activeChat?.status || state.status || "idle");
50184
+ const title = typeof state.activeChat?.title === "string" ? state.activeChat.title : void 0;
50185
+ const modalMessage = typeof activeModal?.message === "string" ? activeModal.message : void 0;
50186
+ const deliverySignature = buildSessionModalDeliverySignature({
50187
+ sessionId: subscription.params.targetSessionId,
50188
+ status,
50189
+ ...title ? { title } : {},
50190
+ ...modalMessage ? { modalMessage } : {},
50191
+ ...modalButtons.length > 0 ? { modalButtons } : {}
50192
+ });
50193
+ if (deliverySignature === subscription.lastDeliveredSignature) {
50194
+ return null;
50195
+ }
50196
+ subscription.lastDeliveredSignature = deliverySignature;
50197
+ subscription.seq += 1;
50198
+ subscription.lastSentAt = now;
50199
+ return {
50200
+ topic: "session.modal",
50201
+ key: subscription.key,
50202
+ sessionId: subscription.params.targetSessionId,
50203
+ status,
50204
+ ...title ? { title } : {},
50205
+ ...modalMessage ? { modalMessage } : {},
50206
+ ...modalButtons.length > 0 ? { modalButtons } : {},
50207
+ seq: subscription.seq,
50208
+ timestamp: now
50209
+ };
50210
+ }
50211
+ async flushP2PSessionModalSubscriptions() {
50212
+ if (!this.p2p?.isConnected || !this.p2p.hasSessionModalSubscriptions()) return;
50213
+ await this.p2p.flushSessionModalSubscriptions(
50214
+ async (subscription) => this.buildSessionModalUpdateForSubscription(subscription)
50215
+ );
50216
+ }
50217
+ buildDaemonMetadataSnapshot() {
50218
+ return buildStatusSnapshot({
50219
+ allStates: this.components.instanceManager.collectAllStates(),
50220
+ cdpManagers: this.components.cdpManagers,
50221
+ providerLoader: this.components.providerLoader,
50222
+ detectedIdes: this.components.detectedIdes.value.map((ide) => ({
50223
+ ...ide,
50224
+ path: ide.path ?? void 0
50225
+ })),
50226
+ instanceId: `daemon_${loadConfig().machineId || "daemon"}`,
50227
+ version: pkgVersion,
50228
+ profile: "metadata"
50229
+ });
50230
+ }
50231
+ buildDaemonMetadataUpdateForSubscription(subscription) {
50232
+ const now = Date.now();
50233
+ subscription.seq += 1;
50234
+ subscription.lastSentAt = now;
50235
+ return {
50236
+ topic: "daemon.metadata",
50237
+ key: subscription.key,
50238
+ daemonId: `daemon_${loadConfig().machineId || "daemon"}`,
50239
+ status: this.buildDaemonMetadataSnapshot(),
50240
+ seq: subscription.seq,
50241
+ timestamp: now
50242
+ };
50243
+ }
50244
+ async flushP2PDaemonMetadataSubscriptions() {
50245
+ if (!this.p2p?.isConnected || !this.p2p.hasDaemonMetadataSubscriptions()) return;
50246
+ await this.p2p.flushDaemonMetadataSubscriptions(
50247
+ async (subscription) => this.buildDaemonMetadataUpdateForSubscription(subscription)
50248
+ );
50249
+ }
49434
50250
  async start(options = {}) {
49435
50251
  installGlobalInterceptor();
49436
50252
  process.on("uncaughtException", (err) => {
@@ -49463,9 +50279,14 @@ ${err?.stack || ""}`);
49463
50279
  this.sessionHostEndpoint = sessionHostEndpoint;
49464
50280
  this.sessionHostController = new SessionHostController(
49465
50281
  sessionHostEndpoint,
49466
- (event) => this.broadcastLocalIpcMessage("daemon:session_host_event", event)
50282
+ (event) => {
50283
+ this.broadcastLocalIpcMessage("daemon:session_host_event", event);
50284
+ void this.flushP2PSessionHostDiagnosticsSubscriptions();
50285
+ void this.flushP2PSessionModalSubscriptions();
50286
+ }
49467
50287
  );
49468
50288
  await this.sessionHostController.start();
50289
+ const instanceId = `daemon_${config2.machineId}`;
49469
50290
  this.components = await initDaemonComponents({
49470
50291
  providerLogFn: LOG.forComponent("Provider").asLogFn(),
49471
50292
  cliManagerDeps: {
@@ -49498,6 +50319,8 @@ ${err?.stack || ""}`);
49498
50319
  listHostedCliRuntimes: async () => listHostedCliRuntimes2(sessionHostEndpoint)
49499
50320
  },
49500
50321
  enabledIdes: config2.enabledIdes,
50322
+ statusInstanceId: instanceId,
50323
+ statusVersion: pkgVersion,
49501
50324
  onStatusChange: () => this.statusReporter?.onStatusChange(),
49502
50325
  onPostChatCommand: () => {
49503
50326
  setTimeout(() => this.statusReporter?.throttledReport(), 1e3);
@@ -49523,7 +50346,6 @@ ${err?.stack || ""}`);
49523
50346
  }
49524
50347
  }).catch(() => {
49525
50348
  });
49526
- const instanceId = `daemon_${config2.machineId}`;
49527
50349
  this.serverConn = new ServerConnection({
49528
50350
  serverUrl: options.serverUrl || config2.serverUrl,
49529
50351
  token: authToken,
@@ -49573,6 +50395,13 @@ ${err?.stack || ""}`);
49573
50395
  found.adapter.resize(cols, rows);
49574
50396
  }
49575
50397
  });
50398
+ this.p2p.onSubscriptionChange(() => {
50399
+ void this.flushP2PChatSubscriptions();
50400
+ void this.flushP2PMachineRuntimeSubscriptions();
50401
+ void this.flushP2PSessionHostDiagnosticsSubscriptions();
50402
+ void this.flushP2PSessionModalSubscriptions();
50403
+ void this.flushP2PDaemonMetadataSubscriptions();
50404
+ });
49576
50405
  this.p2p.onStateChange((state) => {
49577
50406
  if (state === "connected") {
49578
50407
  LOG.info("P2P", "Peer connected \u2192 sending immediate full status report");
@@ -49589,18 +50418,24 @@ ${err?.stack || ""}`);
49589
50418
  this.screenshotController = new ScreenshotController({
49590
50419
  isRunning: () => this.running,
49591
50420
  isScreenshotActive: () => this.p2p?.screenshotActive ?? false,
49592
- getScreenshotIdeType: () => this.p2p?.screenshotIdeType,
50421
+ getScreenshotTargetSessionId: () => this.p2p?.screenshotTargetSessionId,
49593
50422
  isUsingRelay: () => this.p2p?.isUsingRelay ?? false,
49594
50423
  hasAnyNeedingFirstFrame: () => this.p2p?.hasAnyNeedingFirstFrame() ?? false,
49595
- getCdp: (ideType) => {
49596
- if (ideType) return this.getCdpFor(ideType);
49597
- LOG.warn("P2P", "Screenshot requested without ideType \u2014 cannot determine target IDE. Skipping frame.");
50424
+ getCdp: (targetSessionId) => {
50425
+ if (targetSessionId) return this.getCdpFor(targetSessionId);
50426
+ LOG.warn("P2P", "Screenshot requested without targetSessionId \u2014 cannot determine target session. Skipping frame.");
49598
50427
  return null;
49599
50428
  },
49600
50429
  sendScreenshotBuffer: (buf) => this.p2p.sendScreenshotBuffer(buf)
49601
50430
  }, planLimits ?? void 0);
49602
50431
  this.screenshotController.start();
49603
50432
  this.p2p.onScreenshotStart(() => this.screenshotController?.triggerImmediate());
50433
+ this.topicSubscriptionTimer = setInterval(() => {
50434
+ void this.flushP2PChatSubscriptions({ onlyActive: true });
50435
+ void this.flushP2PMachineRuntimeSubscriptions();
50436
+ void this.flushP2PSessionHostDiagnosticsSubscriptions();
50437
+ void this.flushP2PSessionModalSubscriptions();
50438
+ }, 2500);
49604
50439
  } else {
49605
50440
  console.log(import_chalk2.default.gray(" \u26A0 P2P unavailable \u2014 using server relay"));
49606
50441
  }
@@ -49705,10 +50540,26 @@ ${err?.stack || ""}`);
49705
50540
  async handleCommand(msg, cmd, args) {
49706
50541
  LOG.info("Command", `${cmd}${args?.targetSessionId ? ` \u2192 session:${String(args.targetSessionId).split("_")[0]}` : ""}`);
49707
50542
  const cmdStart = Date.now();
49708
- const source = msg.ipcWs ? "ext" : "ws";
50543
+ const source = msg.ipcWs ? "ext" : typeof msg.source === "string" && msg.source.trim() ? msg.source : "ws";
49709
50544
  try {
50545
+ if (source === "api" && !loadConfig().allowServerApiProxy) {
50546
+ this.sendResult(msg, false, {
50547
+ error: "Server API relay is disabled on this daemon. Enable it explicitly with `adhdev daemon:api enable`.",
50548
+ code: "SERVER_API_DISABLED"
50549
+ });
50550
+ return;
50551
+ }
49710
50552
  const result = await this.components.router.execute(cmd, args, source);
49711
50553
  if (cmd.startsWith("workspace_")) this.statusReporter?.throttledReport();
50554
+ if (cmd === "get_status_metadata" || cmd.startsWith("workspace_") || cmd.startsWith("session_host_")) {
50555
+ void this.flushP2PDaemonMetadataSubscriptions();
50556
+ }
50557
+ if (cmd.startsWith("session_host_")) {
50558
+ void this.flushP2PSessionHostDiagnosticsSubscriptions();
50559
+ }
50560
+ if (cmd === "resolve_action" || cmd === "send_chat" || cmd === "read_chat") {
50561
+ void this.flushP2PSessionModalSubscriptions();
50562
+ }
49712
50563
  this.sendResult(msg, result.success, result);
49713
50564
  } catch (e) {
49714
50565
  console.error(import_chalk2.default.red(` \u2717 Command failed: ${e.message}`));
@@ -49748,6 +50599,7 @@ ${err?.stack || ""}`);
49748
50599
  const config2 = loadConfig();
49749
50600
  config2.machineNickname = nickname;
49750
50601
  saveConfig(config2);
50602
+ void this.flushP2PDaemonMetadataSubscriptions();
49751
50603
  logCommand({ ts: (/* @__PURE__ */ new Date()).toISOString(), cmd: cmdType, source: "p2p", args: { nickname }, success: true, durationMs: Date.now() - cmdStart });
49752
50604
  return { success: true, nickname };
49753
50605
  }
@@ -49760,6 +50612,15 @@ ${err?.stack || ""}`);
49760
50612
  }
49761
50613
  const routed = await this.components.router.execute(cmdType, data, "p2p");
49762
50614
  if (cmdType.startsWith("workspace_")) this.statusReporter?.throttledReport();
50615
+ if (cmdType === "get_status_metadata" || cmdType.startsWith("workspace_") || cmdType.startsWith("session_host_")) {
50616
+ void this.flushP2PDaemonMetadataSubscriptions();
50617
+ }
50618
+ if (cmdType.startsWith("session_host_")) {
50619
+ void this.flushP2PSessionHostDiagnosticsSubscriptions();
50620
+ }
50621
+ if (cmdType === "resolve_action" || cmdType === "send_chat" || cmdType === "read_chat") {
50622
+ void this.flushP2PSessionModalSubscriptions();
50623
+ }
49763
50624
  return routed;
49764
50625
  } catch (e) {
49765
50626
  logCommand({ ts: (/* @__PURE__ */ new Date()).toISOString(), cmd: cmdType, source: "p2p", success: false, error: e.message, durationMs: Date.now() - cmdStart });
@@ -49932,6 +50793,10 @@ ${err?.stack || ""}`);
49932
50793
  this.statusReporter.stopReporting();
49933
50794
  this.statusReporter = null;
49934
50795
  }
50796
+ if (this.topicSubscriptionTimer) {
50797
+ clearInterval(this.topicSubscriptionTimer);
50798
+ this.topicSubscriptionTimer = null;
50799
+ }
49935
50800
  if (this.components) {
49936
50801
  await shutdownDaemonComponents(this.components);
49937
50802
  }
@@ -51794,17 +52659,50 @@ function registerDaemonCommands(program2, pkgVersion3) {
51794
52659
  \u2717 Failed to start standalone server: ${err.message}`));
51795
52660
  });
51796
52661
  });
52662
+ hideCommand(program2.command("daemon:api [mode]").description("Manage server API relay access (disabled by default)").action(async (modeArg) => {
52663
+ const mode = typeof modeArg === "string" && modeArg.trim() ? modeArg.trim().toLowerCase() : "status";
52664
+ const { loadConfig: loadConfig2, saveConfig: saveConfig3 } = await Promise.resolve().then(() => (init_src(), src_exports));
52665
+ const config2 = loadConfig2();
52666
+ if (mode === "status") {
52667
+ const enabled2 = config2.allowServerApiProxy === true;
52668
+ console.log();
52669
+ console.log(import_chalk5.default.bold(" Server API Relay\n"));
52670
+ console.log(` ${import_chalk5.default.bold("Status:")} ${enabled2 ? import_chalk5.default.green("enabled") : import_chalk5.default.yellow("disabled")}`);
52671
+ console.log(import_chalk5.default.gray(" Applies only to server REST/API proxy routes."));
52672
+ console.log(import_chalk5.default.gray(" Dashboard traffic remains P2P/local IPC based.\n"));
52673
+ return;
52674
+ }
52675
+ if (mode !== "enable" && mode !== "disable") {
52676
+ console.log(import_chalk5.default.red(`
52677
+ \u2717 Unknown mode: ${mode}
52678
+ `));
52679
+ console.log(import_chalk5.default.gray(" Use: adhdev daemon:api [status|enable|disable]\n"));
52680
+ process.exitCode = 1;
52681
+ return;
52682
+ }
52683
+ const enabled = mode === "enable";
52684
+ config2.allowServerApiProxy = enabled;
52685
+ saveConfig3(config2);
52686
+ console.log();
52687
+ console.log(import_chalk5.default.bold(" Server API Relay\n"));
52688
+ console.log(` ${import_chalk5.default.bold("Status:")} ${enabled ? import_chalk5.default.green("enabled") : import_chalk5.default.yellow("disabled")}`);
52689
+ console.log(import_chalk5.default.gray(` Set with: adhdev daemon:api ${enabled ? "disable" : "enable"}
52690
+ `));
52691
+ }));
51797
52692
  hideCommand(program2.command("daemon:status").description("Check ADHDev Daemon status").option("-p, --port <port>", "Local WS server port", "19222").action(async (options) => {
51798
52693
  const { isDaemonRunning: isDaemonRunning2, getDaemonPid: getDaemonPid2 } = await Promise.resolve().then(() => (init_adhdev_daemon(), adhdev_daemon_exports));
51799
52694
  const { getCurrentDaemonLogPath: getCurrentDaemonLogPath2 } = await Promise.resolve().then(() => (init_src(), src_exports));
51800
52695
  const { probeSessionHostStatus: probeSessionHostStatus2 } = await Promise.resolve().then(() => (init_session_host(), session_host_exports));
51801
52696
  const port = parseInt(options.port, 10) || 19222;
52697
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_src(), src_exports));
52698
+ const config2 = loadConfig2();
51802
52699
  if (isDaemonRunning2()) {
51803
52700
  console.log(import_chalk5.default.green(`
51804
52701
  \u2713 ADHDev Daemon is running.
51805
52702
  `));
51806
52703
  console.log(import_chalk5.default.gray(` PID: ${getDaemonPid2() ?? "unknown"}`));
51807
52704
  console.log(import_chalk5.default.gray(` Logs: ${getCurrentDaemonLogPath2()}`));
52705
+ console.log(import_chalk5.default.gray(` API Relay: ${config2.allowServerApiProxy ? "enabled" : "disabled"}`));
51808
52706
  const health = await fetchLocalDaemonHealth(port);
51809
52707
  if (health) {
51810
52708
  console.log(import_chalk5.default.gray(` Local IPC: reachable (ws://127.0.0.1:${health.port}${health.wsPath})`));