@adhdev/daemon-core 0.8.81 → 0.8.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -0
  2. package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
  3. package/dist/config/recent-activity.d.ts +14 -0
  4. package/dist/config/state-store.d.ts +4 -0
  5. package/dist/index.js +363 -117
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +363 -117
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/providers/acp-provider-instance.d.ts +0 -2
  10. package/dist/providers/cli-provider-instance.d.ts +2 -0
  11. package/dist/providers/provider-instance.d.ts +1 -1
  12. package/dist/shared-types.d.ts +3 -1
  13. package/dist/status/chat-tail-hot-sessions.d.ts +2 -0
  14. package/dist/status/snapshot.d.ts +1 -0
  15. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  16. package/package.json +1 -1
  17. package/src/cli-adapter-types.d.ts +2 -0
  18. package/src/cli-adapters/provider-cli-adapter.ts +107 -30
  19. package/src/cli-adapters/provider-cli-shared.d.ts +3 -4
  20. package/src/cli-adapters/provider-cli-shared.ts +2 -0
  21. package/src/cli-adapters/terminal-screen.ts +6 -4
  22. package/src/commands/chat-commands.ts +8 -3
  23. package/src/commands/router.ts +59 -1
  24. package/src/config/recent-activity.ts +122 -0
  25. package/src/config/state-store.ts +16 -0
  26. package/src/logging/command-log.ts +2 -0
  27. package/src/providers/acp-provider-instance.ts +2 -17
  28. package/src/providers/cli-provider-instance.ts +33 -10
  29. package/src/providers/extension-provider-instance.ts +0 -2
  30. package/src/providers/ide-provider-instance.ts +0 -2
  31. package/src/providers/provider-instance.d.ts +1 -1
  32. package/src/providers/provider-instance.ts +1 -0
  33. package/src/shared-types.d.ts +3 -0
  34. package/src/shared-types.ts +5 -1
  35. package/src/status/chat-tail-hot-sessions.ts +15 -1
  36. package/src/status/snapshot.ts +20 -3
package/dist/index.js CHANGED
@@ -956,6 +956,58 @@ var init_read_chat_contract = __esm({
956
956
  }
957
957
  });
958
958
 
959
+ // src/logging/debug-config.ts
960
+ function normalizeCategories(categories) {
961
+ if (!Array.isArray(categories)) return [];
962
+ return categories.map((category) => String(category || "").trim()).filter(Boolean);
963
+ }
964
+ function resolveDebugRuntimeConfig(options = {}) {
965
+ const dev = options.dev === true;
966
+ return {
967
+ logLevel: options.logLevel || (dev ? "debug" : DEFAULT_CONFIG2.logLevel),
968
+ collectDebugTrace: typeof options.trace === "boolean" ? options.trace : dev,
969
+ traceContent: options.traceContent === true,
970
+ traceBufferSize: Number.isFinite(options.traceBufferSize) ? Math.max(10, Math.floor(options.traceBufferSize)) : dev ? DEV_TRACE_BUFFER_SIZE : DEFAULT_CONFIG2.traceBufferSize,
971
+ traceCategories: normalizeCategories(options.traceCategories)
972
+ };
973
+ }
974
+ function setDebugRuntimeConfig(config) {
975
+ currentConfig = {
976
+ ...config,
977
+ traceCategories: normalizeCategories(config.traceCategories),
978
+ traceBufferSize: Math.max(10, Math.floor(config.traceBufferSize || DEFAULT_CONFIG2.traceBufferSize))
979
+ };
980
+ }
981
+ function getDebugRuntimeConfig() {
982
+ return { ...currentConfig, traceCategories: [...currentConfig.traceCategories] };
983
+ }
984
+ function resetDebugRuntimeConfig() {
985
+ currentConfig = { ...DEFAULT_CONFIG2 };
986
+ }
987
+ function shouldCollectTraceCategory(category) {
988
+ const config = currentConfig;
989
+ if (!config.collectDebugTrace) return false;
990
+ if (!category) return true;
991
+ if (config.traceCategories.length === 0) return true;
992
+ return config.traceCategories.includes(category);
993
+ }
994
+ var NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig;
995
+ var init_debug_config = __esm({
996
+ "src/logging/debug-config.ts"() {
997
+ "use strict";
998
+ NORMAL_TRACE_BUFFER_SIZE = 200;
999
+ DEV_TRACE_BUFFER_SIZE = 1e3;
1000
+ DEFAULT_CONFIG2 = {
1001
+ logLevel: "info",
1002
+ collectDebugTrace: false,
1003
+ traceContent: false,
1004
+ traceBufferSize: NORMAL_TRACE_BUFFER_SIZE,
1005
+ traceCategories: []
1006
+ };
1007
+ currentConfig = { ...DEFAULT_CONFIG2 };
1008
+ }
1009
+ });
1010
+
959
1011
  // src/cli-adapters/terminal-backends/ghostty-vt-backend.ts
960
1012
  function isModuleNotFoundError(error, ref) {
961
1013
  if (!(error instanceof Error)) return false;
@@ -1154,10 +1206,12 @@ function logTerminalBackendSelection(preference, ghosttyAvailable, backendKind)
1154
1206
  if (loggedTerminalBackends.has(key)) return;
1155
1207
  loggedTerminalBackends.add(key);
1156
1208
  if (backendKind === "xterm" && preference !== "xterm" && !ghosttyAvailable) {
1157
- LOG.warn(
1158
- "Terminal",
1159
- `[terminal-screen] ghostty-vt unavailable; using xterm fallback (preference=${preference})`
1160
- );
1209
+ const message = `[terminal-screen] ghostty-vt unavailable; using xterm fallback (preference=${preference})`;
1210
+ if (preference === "auto") {
1211
+ LOG.info("Terminal", message);
1212
+ } else {
1213
+ LOG.warn("Terminal", message);
1214
+ }
1161
1215
  return;
1162
1216
  }
1163
1217
  LOG.info(
@@ -1809,6 +1863,7 @@ var init_provider_cli_adapter = __esm({
1809
1863
  "use strict";
1810
1864
  os10 = __toESM(require("os"));
1811
1865
  init_logger();
1866
+ init_debug_config();
1812
1867
  init_terminal_screen();
1813
1868
  init_pty_transport();
1814
1869
  init_provider_cli_shared();
@@ -1842,7 +1897,15 @@ var init_provider_cli_adapter = __esm({
1842
1897
  `[${this.cliType}] Provider resolution: providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"} source=${this.providerResolutionMeta.scriptsSource || "-"} version=${this.providerResolutionMeta.resolvedVersion || "-"}`
1843
1898
  );
1844
1899
  } else {
1845
- LOG.warn("CLI", `[${this.cliType}] \u26A0 No CLI scripts loaded! Provider needs scripts/{version}/scripts.js`);
1900
+ const resolutionSummary = `providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"} source=${this.providerResolutionMeta.scriptsSource || "-"} version=${this.providerResolutionMeta.resolvedVersion || "-"}`;
1901
+ const hasResolvedProviderScripts = Boolean(
1902
+ this.providerResolutionMeta.providerDir || this.providerResolutionMeta.scriptDir || this.providerResolutionMeta.scriptsPath || this.providerResolutionMeta.scriptsSource || this.providerResolutionMeta.resolvedVersion
1903
+ );
1904
+ if (hasResolvedProviderScripts) {
1905
+ LOG.warn("CLI", `[${this.cliType}] \u26A0 No CLI scripts loaded! Provider needs scripts/{version}/scripts.js (${resolutionSummary})`);
1906
+ } else {
1907
+ LOG.info("CLI", `[${this.cliType}] CLI scripts not yet resolved (${resolutionSummary})`);
1908
+ }
1846
1909
  }
1847
1910
  }
1848
1911
  cliType;
@@ -1860,6 +1923,7 @@ var init_provider_cli_adapter = __esm({
1860
1923
  recentOutputBuffer = "";
1861
1924
  isWaitingForResponse = false;
1862
1925
  activeModal = null;
1926
+ parseErrorMessage = null;
1863
1927
  responseTimeout = null;
1864
1928
  idleTimeout = null;
1865
1929
  ready = false;
@@ -1920,7 +1984,8 @@ var init_provider_cli_adapter = __esm({
1920
1984
  currentTurnScope = null;
1921
1985
  traceEntries = [];
1922
1986
  traceSeq = 0;
1923
- traceSessionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1987
+ traceSessionId = "";
1988
+ parsedStatusCache = null;
1924
1989
  static MAX_TRACE_ENTRIES = 250;
1925
1990
  providerResolutionMeta;
1926
1991
  static FINISH_RETRY_DELAY_MS = 300;
@@ -2161,7 +2226,8 @@ var init_provider_cli_adapter = __esm({
2161
2226
  this.terminalScreen.write(rawData);
2162
2227
  const cleanData = sanitizeTerminalText(rawData);
2163
2228
  const now = Date.now();
2164
- const normalizedScreenSnapshot = normalizeScreenSnapshot(this.terminalScreen.getText());
2229
+ const screenText = this.terminalScreen.getText();
2230
+ const normalizedScreenSnapshot = normalizeScreenSnapshot(screenText);
2165
2231
  this.lastOutputAt = now;
2166
2232
  if (cleanData.trim()) this.lastNonEmptyOutputAt = now;
2167
2233
  if (normalizedScreenSnapshot !== this.lastScreenSnapshot) {
@@ -2174,13 +2240,14 @@ var init_provider_cli_adapter = __esm({
2174
2240
  if (this.idleFinishCandidate && (rawData.length > 0 || cleanData.length > 0)) {
2175
2241
  this.clearIdleFinishCandidate("new_output");
2176
2242
  }
2177
- this.recordTrace("output", {
2178
- rawLength: rawData.length,
2179
- cleanLength: cleanData.length,
2180
- rawPreview: summarizeCliTraceText(rawData, 300),
2181
- cleanPreview: summarizeCliTraceText(cleanData, 300),
2182
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 1200)
2183
- });
2243
+ if (getDebugRuntimeConfig().collectDebugTrace) {
2244
+ this.recordTrace("output", {
2245
+ rawLength: rawData.length,
2246
+ cleanLength: cleanData.length,
2247
+ rawPreview: summarizeCliTraceText(rawData, 300),
2248
+ cleanPreview: summarizeCliTraceText(cleanData, 300)
2249
+ });
2250
+ }
2184
2251
  if (this.startupParseGate) {
2185
2252
  this.scheduleStartupSettleCheck();
2186
2253
  }
@@ -2854,10 +2921,12 @@ var init_provider_cli_adapter = __esm({
2854
2921
  // ─── Public API (CliAdapter) ───────────────────
2855
2922
  getStatus() {
2856
2923
  return {
2857
- status: this.currentStatus,
2924
+ status: this.parseErrorMessage ? "error" : this.currentStatus,
2858
2925
  messages: [...this.committedMessages],
2859
2926
  workingDir: this.workingDir,
2860
- activeModal: this.activeModal
2927
+ activeModal: this.activeModal,
2928
+ errorMessage: this.parseErrorMessage || void 0,
2929
+ errorReason: this.parseErrorMessage ? "parse_error" : void 0
2861
2930
  };
2862
2931
  }
2863
2932
  seedCommittedMessages(messages) {
@@ -2880,12 +2949,19 @@ var init_provider_cli_adapter = __esm({
2880
2949
  * Called by command handler / dashboard for rich content rendering.
2881
2950
  */
2882
2951
  getScriptParsedStatus() {
2952
+ const screenText = this.terminalScreen.getText();
2953
+ const cached = this.parsedStatusCache;
2954
+ if (cached && cached.committedMessagesRef === this.committedMessages && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.accumulatedRawBuffer === this.accumulatedRawBuffer && cached.screenText === screenText && cached.currentStatus === this.currentStatus && cached.activeModal === this.activeModal && cached.cliName === this.cliName && cached.lastOutputAt === this.lastOutputAt) {
2955
+ return cached.result;
2956
+ }
2883
2957
  const parsed = this.parseCurrentTranscript(
2884
2958
  this.committedMessages,
2885
2959
  this.responseBuffer,
2886
- this.currentTurnScope
2960
+ this.currentTurnScope,
2961
+ screenText
2887
2962
  );
2888
2963
  const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
2964
+ let result;
2889
2965
  if (parsed && Array.isArray(parsed.messages)) {
2890
2966
  const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => buildChatMessage({
2891
2967
  ...message,
@@ -2897,7 +2973,7 @@ var init_provider_cli_adapter = __esm({
2897
2973
  scope: this.currentTurnScope,
2898
2974
  lastOutputAt: this.lastOutputAt
2899
2975
  });
2900
- return {
2976
+ result = {
2901
2977
  id: parsed.id || "cli_session",
2902
2978
  status: parsed.status || this.currentStatus,
2903
2979
  title: parsed.title || this.cliName,
@@ -2905,20 +2981,36 @@ var init_provider_cli_adapter = __esm({
2905
2981
  activeModal: parsed.activeModal ?? this.activeModal,
2906
2982
  providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0
2907
2983
  };
2984
+ } else {
2985
+ const messages = [...this.committedMessages];
2986
+ result = {
2987
+ id: "cli_session",
2988
+ status: this.currentStatus,
2989
+ title: this.cliName,
2990
+ messages: messages.map((message, index) => buildChatMessage({
2991
+ ...message,
2992
+ id: message.id || `msg_${index}`,
2993
+ index: typeof message.index === "number" ? message.index : index,
2994
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2995
+ })),
2996
+ activeModal: this.activeModal
2997
+ };
2908
2998
  }
2909
- const messages = [...this.committedMessages];
2910
- return {
2911
- id: "cli_session",
2912
- status: this.currentStatus,
2913
- title: this.cliName,
2914
- messages: messages.slice(-50).map((message, index) => buildChatMessage({
2915
- ...message,
2916
- id: message.id || `msg_${index}`,
2917
- index: typeof message.index === "number" ? message.index : index,
2918
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2919
- })),
2920
- activeModal: this.activeModal
2999
+ this.parsedStatusCache = {
3000
+ committedMessagesRef: this.committedMessages,
3001
+ responseBuffer: this.responseBuffer,
3002
+ currentTurnScope: this.currentTurnScope,
3003
+ recentOutputBuffer: this.recentOutputBuffer,
3004
+ accumulatedBuffer: this.accumulatedBuffer,
3005
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
3006
+ screenText,
3007
+ currentStatus: this.currentStatus,
3008
+ activeModal: this.activeModal,
3009
+ cliName: this.cliName,
3010
+ lastOutputAt: this.lastOutputAt,
3011
+ result
2921
3012
  };
3013
+ return result;
2922
3014
  }
2923
3015
  async invokeScript(scriptName, args) {
2924
3016
  const fn = this.cliScripts?.[scriptName];
@@ -2941,14 +3033,18 @@ var init_provider_cli_adapter = __esm({
2941
3033
  args: args && typeof args === "object" ? { ...args } : {}
2942
3034
  }));
2943
3035
  }
2944
- parseCurrentTranscript(baseMessages, partialResponse, scope) {
2945
- if (!this.cliScripts?.parseOutput) return null;
3036
+ parseCurrentTranscript(baseMessages, partialResponse, scope, screenTextOverride) {
3037
+ if (!this.cliScripts?.parseOutput) {
3038
+ this.parseErrorMessage = null;
3039
+ return null;
3040
+ }
2946
3041
  try {
3042
+ const screenText = typeof screenTextOverride === "string" ? screenTextOverride : this.terminalScreen.getText();
2947
3043
  const input = buildCliParseInput({
2948
3044
  accumulatedBuffer: this.accumulatedBuffer,
2949
3045
  accumulatedRawBuffer: this.accumulatedRawBuffer,
2950
3046
  recentOutputBuffer: this.recentOutputBuffer,
2951
- terminalScreenText: this.terminalScreen.getText(),
3047
+ terminalScreenText: screenText,
2952
3048
  baseMessages,
2953
3049
  partialResponse,
2954
3050
  isWaitingForResponse: this.isWaitingForResponse,
@@ -2970,10 +3066,13 @@ var init_provider_cli_adapter = __esm({
2970
3066
  lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
2971
3067
  }
2972
3068
  }
3069
+ this.parseErrorMessage = null;
2973
3070
  return parsed;
2974
3071
  } catch (e) {
2975
- LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
2976
- return null;
3072
+ const message = e?.message || String(e);
3073
+ this.parseErrorMessage = message;
3074
+ LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${message}`);
3075
+ throw e;
2977
3076
  }
2978
3077
  }
2979
3078
  /** Whether this adapter has CLI scripts loaded */
@@ -3973,6 +4072,96 @@ function getSessionSeenMarker(state, sessionId, providerSessionId) {
3973
4072
  const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
3974
4073
  return state.sessionReadMarkers?.[providerKey] || state.sessionReadMarkers?.[sessionId] || "";
3975
4074
  }
4075
+ function getSessionNotificationDismissal(state, sessionId, providerSessionId) {
4076
+ const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
4077
+ return state.sessionNotificationDismissals?.[providerKey] || state.sessionNotificationDismissals?.[sessionId] || "";
4078
+ }
4079
+ function getSessionNotificationUnreadOverride(state, sessionId, providerSessionId) {
4080
+ const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
4081
+ return state.sessionNotificationUnreadOverrides?.[providerKey] || state.sessionNotificationUnreadOverrides?.[sessionId] || "";
4082
+ }
4083
+ function dismissSessionNotification(state, sessionId, notificationId, providerSessionId) {
4084
+ const dismissalId = String(notificationId || "").trim();
4085
+ if (!dismissalId) return state;
4086
+ const dismissalKeys = Array.from(new Set([
4087
+ sessionId,
4088
+ buildSessionReadStateKey(sessionId, providerSessionId)
4089
+ ].filter(Boolean)));
4090
+ const nextSessionNotificationDismissals = { ...state.sessionNotificationDismissals || {} };
4091
+ const nextSessionNotificationUnreadOverrides = { ...state.sessionNotificationUnreadOverrides || {} };
4092
+ for (const key of dismissalKeys) {
4093
+ nextSessionNotificationDismissals[key] = dismissalId;
4094
+ delete nextSessionNotificationUnreadOverrides[key];
4095
+ }
4096
+ return {
4097
+ ...state,
4098
+ sessionNotificationDismissals: nextSessionNotificationDismissals,
4099
+ sessionNotificationUnreadOverrides: nextSessionNotificationUnreadOverrides
4100
+ };
4101
+ }
4102
+ function markSessionNotificationUnread(state, sessionId, notificationId, providerSessionId) {
4103
+ const unreadId = String(notificationId || "").trim();
4104
+ if (!unreadId) return state;
4105
+ const unreadKeys = Array.from(new Set([
4106
+ sessionId,
4107
+ buildSessionReadStateKey(sessionId, providerSessionId)
4108
+ ].filter(Boolean)));
4109
+ const nextSessionNotificationDismissals = { ...state.sessionNotificationDismissals || {} };
4110
+ const nextSessionNotificationUnreadOverrides = { ...state.sessionNotificationUnreadOverrides || {} };
4111
+ for (const key of unreadKeys) {
4112
+ nextSessionNotificationUnreadOverrides[key] = unreadId;
4113
+ delete nextSessionNotificationDismissals[key];
4114
+ }
4115
+ return {
4116
+ ...state,
4117
+ sessionNotificationDismissals: nextSessionNotificationDismissals,
4118
+ sessionNotificationUnreadOverrides: nextSessionNotificationUnreadOverrides
4119
+ };
4120
+ }
4121
+ function getSessionNotificationTargetValue(session) {
4122
+ const providerSessionId = typeof session.providerSessionId === "string" ? session.providerSessionId.trim() : "";
4123
+ return providerSessionId || session.id;
4124
+ }
4125
+ function getSessionCurrentNotificationId(session) {
4126
+ const inboxBucket = session.inboxBucket || "idle";
4127
+ const isNeedsAttention = inboxBucket === "needs_attention" || session.status === "waiting_approval";
4128
+ const isTaskComplete = inboxBucket === "task_complete" && !!session.unread;
4129
+ const type = isNeedsAttention ? "needs_attention" : isTaskComplete ? "task_complete" : "";
4130
+ if (!type) return "";
4131
+ const target = getSessionNotificationTargetValue(session);
4132
+ const lastMessageHash = typeof session.lastMessageHash === "string" ? session.lastMessageHash : "";
4133
+ const timestamp = Number(session.lastMessageAt || session.lastUpdated || 0);
4134
+ return [type, target, lastMessageHash, String(timestamp)].join("|");
4135
+ }
4136
+ function applySessionNotificationOverlay(session, overlay) {
4137
+ const currentNotificationId = getSessionCurrentNotificationId(session);
4138
+ const taskCompleteNotificationId = (() => {
4139
+ const target = getSessionNotificationTargetValue(session);
4140
+ const lastMessageHash = typeof session.lastMessageHash === "string" ? session.lastMessageHash : "";
4141
+ const timestamp = Number(session.lastMessageAt || session.lastUpdated || 0);
4142
+ if (!target || !lastMessageHash || !timestamp) return "";
4143
+ return ["task_complete", target, lastMessageHash, String(timestamp)].join("|");
4144
+ })();
4145
+ const dismissedNotificationId = typeof overlay.dismissedNotificationId === "string" ? overlay.dismissedNotificationId.trim() : "";
4146
+ const unreadNotificationId = typeof overlay.unreadNotificationId === "string" ? overlay.unreadNotificationId.trim() : "";
4147
+ if (unreadNotificationId && (currentNotificationId === unreadNotificationId || taskCompleteNotificationId === unreadNotificationId)) {
4148
+ const forcedInboxBucket = session.inboxBucket === "needs_attention" || session.status === "waiting_approval" ? "needs_attention" : "task_complete";
4149
+ return {
4150
+ unread: true,
4151
+ inboxBucket: forcedInboxBucket
4152
+ };
4153
+ }
4154
+ if (!currentNotificationId || !dismissedNotificationId || currentNotificationId !== dismissedNotificationId) {
4155
+ return {
4156
+ unread: !!session.unread,
4157
+ inboxBucket: session.inboxBucket || "idle"
4158
+ };
4159
+ }
4160
+ return {
4161
+ unread: false,
4162
+ inboxBucket: "idle"
4163
+ };
4164
+ }
3976
4165
  function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker, providerSessionId) {
3977
4166
  const prev = state.sessionReads || {};
3978
4167
  const prevMarkers = state.sessionReadMarkers || {};
@@ -3983,14 +4172,20 @@ function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker
3983
4172
  ].filter(Boolean)));
3984
4173
  const nextSessionReads = { ...prev };
3985
4174
  const nextSessionReadMarkers = { ...prevMarkers };
4175
+ const nextSessionNotificationDismissals = { ...state.sessionNotificationDismissals || {} };
4176
+ const nextSessionNotificationUnreadOverrides = { ...state.sessionNotificationUnreadOverrides || {} };
3986
4177
  for (const key of readKeys) {
3987
4178
  nextSessionReads[key] = Math.max(prev[key] || 0, seenAt);
3988
4179
  if (nextMarker) nextSessionReadMarkers[key] = nextMarker;
4180
+ delete nextSessionNotificationDismissals[key];
4181
+ delete nextSessionNotificationUnreadOverrides[key];
3989
4182
  }
3990
4183
  return {
3991
4184
  ...state,
3992
4185
  sessionReads: nextSessionReads,
3993
- sessionReadMarkers: nextMarker ? nextSessionReadMarkers : prevMarkers
4186
+ sessionReadMarkers: nextMarker ? nextSessionReadMarkers : prevMarkers,
4187
+ sessionNotificationDismissals: nextSessionNotificationDismissals,
4188
+ sessionNotificationUnreadOverrides: nextSessionNotificationUnreadOverrides
3994
4189
  };
3995
4190
  }
3996
4191
 
@@ -4075,7 +4270,9 @@ var DEFAULT_STATE = {
4075
4270
  recentActivity: [],
4076
4271
  savedProviderSessions: [],
4077
4272
  sessionReads: {},
4078
- sessionReadMarkers: {}
4273
+ sessionReadMarkers: {},
4274
+ sessionNotificationDismissals: {},
4275
+ sessionNotificationUnreadOverrides: {}
4079
4276
  };
4080
4277
  function isPlainObject2(value) {
4081
4278
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -4107,11 +4304,19 @@ function normalizeState(raw) {
4107
4304
  const sessionReadMarkers = Object.fromEntries(
4108
4305
  Object.entries(isPlainObject2(parsed.sessionReadMarkers) ? parsed.sessionReadMarkers : {}).filter(([key, value]) => !isLegacyVolatileSessionReadKey(key) && typeof value === "string")
4109
4306
  );
4307
+ const sessionNotificationDismissals = Object.fromEntries(
4308
+ Object.entries(isPlainObject2(parsed.sessionNotificationDismissals) ? parsed.sessionNotificationDismissals : {}).filter(([key, value]) => !isLegacyVolatileSessionReadKey(key) && typeof value === "string" && value.length > 0)
4309
+ );
4310
+ const sessionNotificationUnreadOverrides = Object.fromEntries(
4311
+ Object.entries(isPlainObject2(parsed.sessionNotificationUnreadOverrides) ? parsed.sessionNotificationUnreadOverrides : {}).filter(([key, value]) => !isLegacyVolatileSessionReadKey(key) && typeof value === "string" && value.length > 0)
4312
+ );
4110
4313
  return {
4111
4314
  recentActivity,
4112
4315
  savedProviderSessions,
4113
4316
  sessionReads,
4114
- sessionReadMarkers
4317
+ sessionReadMarkers,
4318
+ sessionNotificationDismissals,
4319
+ sessionNotificationUnreadOverrides
4115
4320
  };
4116
4321
  }
4117
4322
  function loadState() {
@@ -4515,9 +4720,15 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
4515
4720
  continue;
4516
4721
  }
4517
4722
  const status = String(session?.status || "").toLowerCase();
4723
+ const unread = session?.unread === true;
4724
+ const inboxBucket = String(session?.inboxBucket || "").toLowerCase();
4725
+ const runtimeSurfaceKind = String(session?.runtimeSurfaceKind || "").toLowerCase();
4726
+ const runtimeLifecycle = String(session?.runtimeLifecycle || "").toLowerCase();
4727
+ const isLiveRuntime = runtimeSurfaceKind === "live_runtime" || LIVE_RUNTIME_LIFECYCLES.has(runtimeLifecycle);
4518
4728
  const lastMessageAt = parseMessageTimestamp(session?.lastMessageAt);
4519
4729
  const recentlyUpdated = lastMessageAt > 0 && now - lastMessageAt <= recentMessageGraceMs;
4520
- if (activeStatuses.has(status) || recentlyUpdated) {
4730
+ const shouldKeepRecentTailHot = recentlyUpdated && (unread || inboxBucket === "task_complete" || inboxBucket === "needs_attention" || isLiveRuntime || activeStatuses.has(status));
4731
+ if (activeStatuses.has(status) || shouldKeepRecentTailHot) {
4521
4732
  active.add(sessionId);
4522
4733
  }
4523
4734
  }
@@ -7434,7 +7645,6 @@ var ExtensionProviderInstance = class {
7434
7645
  }
7435
7646
  pushEvent(event) {
7436
7647
  this.events.push(event);
7437
- if (this.events.length > 50) this.events = this.events.slice(-50);
7438
7648
  }
7439
7649
  applyProviderResponse(data, options) {
7440
7650
  if (!data || typeof data !== "object") return;
@@ -7510,7 +7720,6 @@ var ExtensionProviderInstance = class {
7510
7720
  key: dedupKey,
7511
7721
  message: normalizedMessage
7512
7722
  });
7513
- if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
7514
7723
  if (normalizedContent) {
7515
7724
  this.historyWriter.appendNewMessages(
7516
7725
  this.type,
@@ -8045,7 +8254,6 @@ var IdeProviderInstance = class {
8045
8254
  }
8046
8255
  pushEvent(event) {
8047
8256
  this.events.push(event);
8048
- if (this.events.length > 50) this.events = this.events.slice(-50);
8049
8257
  }
8050
8258
  applyProviderResponse(data, options) {
8051
8259
  if (!data || typeof data !== "object") return;
@@ -8135,7 +8343,6 @@ var IdeProviderInstance = class {
8135
8343
  key: dedupKey,
8136
8344
  message: normalizedMessage
8137
8345
  });
8138
- if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
8139
8346
  if (normalizedContent) {
8140
8347
  this.historyWriter.appendNewMessages(
8141
8348
  this.type,
@@ -9153,53 +9360,8 @@ function assertProviderSupportsDeclaredInput(provider, input) {
9153
9360
  init_read_chat_contract();
9154
9361
  init_logger();
9155
9362
 
9156
- // src/logging/debug-config.ts
9157
- var NORMAL_TRACE_BUFFER_SIZE = 200;
9158
- var DEV_TRACE_BUFFER_SIZE = 1e3;
9159
- var DEFAULT_CONFIG2 = {
9160
- logLevel: "info",
9161
- collectDebugTrace: false,
9162
- traceContent: false,
9163
- traceBufferSize: NORMAL_TRACE_BUFFER_SIZE,
9164
- traceCategories: []
9165
- };
9166
- var currentConfig = { ...DEFAULT_CONFIG2 };
9167
- function normalizeCategories(categories) {
9168
- if (!Array.isArray(categories)) return [];
9169
- return categories.map((category) => String(category || "").trim()).filter(Boolean);
9170
- }
9171
- function resolveDebugRuntimeConfig(options = {}) {
9172
- const dev = options.dev === true;
9173
- return {
9174
- logLevel: options.logLevel || (dev ? "debug" : DEFAULT_CONFIG2.logLevel),
9175
- collectDebugTrace: typeof options.trace === "boolean" ? options.trace : dev,
9176
- traceContent: options.traceContent === true,
9177
- traceBufferSize: Number.isFinite(options.traceBufferSize) ? Math.max(10, Math.floor(options.traceBufferSize)) : dev ? DEV_TRACE_BUFFER_SIZE : DEFAULT_CONFIG2.traceBufferSize,
9178
- traceCategories: normalizeCategories(options.traceCategories)
9179
- };
9180
- }
9181
- function setDebugRuntimeConfig(config) {
9182
- currentConfig = {
9183
- ...config,
9184
- traceCategories: normalizeCategories(config.traceCategories),
9185
- traceBufferSize: Math.max(10, Math.floor(config.traceBufferSize || DEFAULT_CONFIG2.traceBufferSize))
9186
- };
9187
- }
9188
- function getDebugRuntimeConfig() {
9189
- return { ...currentConfig, traceCategories: [...currentConfig.traceCategories] };
9190
- }
9191
- function resetDebugRuntimeConfig() {
9192
- currentConfig = { ...DEFAULT_CONFIG2 };
9193
- }
9194
- function shouldCollectTraceCategory(category) {
9195
- const config = currentConfig;
9196
- if (!config.collectDebugTrace) return false;
9197
- if (!category) return true;
9198
- if (config.traceCategories.length === 0) return true;
9199
- return config.traceCategories.includes(category);
9200
- }
9201
-
9202
9363
  // src/logging/debug-trace.ts
9364
+ init_debug_config();
9203
9365
  function summarizeString(value) {
9204
9366
  return `[${value.length} chars]`;
9205
9367
  }
@@ -9699,7 +9861,14 @@ async function handleReadChat(h, args) {
9699
9861
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
9700
9862
  if (adapter) {
9701
9863
  _log(`${transport} adapter: ${adapter.cliType}`);
9702
- const parsedStatus = typeof adapter.getScriptParsedStatus === "function" ? parseMaybeJson(adapter.getScriptParsedStatus()) : null;
9864
+ let parsedStatus = null;
9865
+ if (typeof adapter.getScriptParsedStatus === "function") {
9866
+ try {
9867
+ parsedStatus = parseMaybeJson(adapter.getScriptParsedStatus());
9868
+ } catch (error) {
9869
+ return { success: false, error: error?.message || String(error) };
9870
+ }
9871
+ }
9703
9872
  const parsedRecord = parsedStatus && typeof parsedStatus === "object" ? parsedStatus : null;
9704
9873
  const status = parsedRecord || adapter.getStatus();
9705
9874
  const title = typeof parsedRecord?.title === "string" ? parsedRecord.title : void 0;
@@ -12033,6 +12202,8 @@ var CliProviderInstance = class {
12033
12202
  runtimeMessages = [];
12034
12203
  instanceId;
12035
12204
  suppressIdleHistoryReplay = false;
12205
+ errorMessage = void 0;
12206
+ errorReason = void 0;
12036
12207
  presentationMode;
12037
12208
  providerSessionId;
12038
12209
  launchMode;
@@ -12099,6 +12270,7 @@ var CliProviderInstance = class {
12099
12270
  }
12100
12271
  async onTick() {
12101
12272
  if (this.providerSessionId) return;
12273
+ if (this.type === "hermes-cli" && this.launchMode === "new") return;
12102
12274
  let probedSessionId = null;
12103
12275
  const probeConfig = this.provider.sessionProbe;
12104
12276
  if (probeConfig) {
@@ -12156,9 +12328,24 @@ var CliProviderInstance = class {
12156
12328
  }
12157
12329
  getState() {
12158
12330
  const adapterStatus = this.adapter.getStatus();
12159
- const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
12331
+ let parsedStatus = null;
12332
+ let parseErrorMessage;
12333
+ if (typeof this.adapter.getScriptParsedStatus === "function") {
12334
+ try {
12335
+ parsedStatus = this.adapter.getScriptParsedStatus() || null;
12336
+ this.errorMessage = void 0;
12337
+ this.errorReason = void 0;
12338
+ } catch (error) {
12339
+ parseErrorMessage = error?.message || String(error);
12340
+ this.errorMessage = parseErrorMessage;
12341
+ this.errorReason = "parse_error";
12342
+ }
12343
+ } else {
12344
+ this.errorMessage = void 0;
12345
+ this.errorReason = void 0;
12346
+ }
12160
12347
  const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
12161
- const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
12348
+ const visibleStatus = parseErrorMessage ? "error" : autoApproveActive ? "generating" : adapterStatus.status;
12162
12349
  const parsedProviderSessionId = normalizeProviderSessionId(
12163
12350
  this.type,
12164
12351
  typeof parsedStatus?.providerSessionId === "string" ? parsedStatus.providerSessionId : ""
@@ -12168,7 +12355,7 @@ var CliProviderInstance = class {
12168
12355
  }
12169
12356
  const runtime = this.adapter.getRuntimeMetadata();
12170
12357
  this.maybeAppendRuntimeRecoveryMessage(runtime);
12171
- let parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
12358
+ let parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : parseErrorMessage ? normalizeChatMessages(Array.isArray(adapterStatus.messages) ? adapterStatus.messages : []) : [];
12172
12359
  const historyMessageCount = Number.isFinite(parsedStatus?.historyMessageCount) ? Math.max(0, Number(parsedStatus.historyMessageCount)) : null;
12173
12360
  if (historyMessageCount !== null) {
12174
12361
  parsedMessages = historyMessageCount > 0 ? parsedMessages.slice(-historyMessageCount) : [];
@@ -12208,7 +12395,7 @@ var CliProviderInstance = class {
12208
12395
  activeChat: {
12209
12396
  id: `${this.type}_${this.workingDir}`,
12210
12397
  title: parsedStatus?.title || dirName,
12211
- status: autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
12398
+ status: parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
12212
12399
  messages: mergedMessages,
12213
12400
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
12214
12401
  inputContent: ""
@@ -12234,7 +12421,9 @@ var CliProviderInstance = class {
12234
12421
  resume: this.provider.resume,
12235
12422
  controlValues: surface.controlValues,
12236
12423
  providerControls: this.provider.controls,
12237
- summaryMetadata: surface.summaryMetadata
12424
+ summaryMetadata: surface.summaryMetadata,
12425
+ errorMessage: this.errorMessage,
12426
+ errorReason: this.errorReason
12238
12427
  };
12239
12428
  }
12240
12429
  setPresentationMode(mode) {
@@ -12421,7 +12610,6 @@ var CliProviderInstance = class {
12421
12610
  }
12422
12611
  pushEvent(event) {
12423
12612
  this.events.push(event);
12424
- if (this.events.length > 50) this.events = this.events.slice(-50);
12425
12613
  }
12426
12614
  flushEvents() {
12427
12615
  const events = [...this.events];
@@ -12603,9 +12791,6 @@ ${effect.notification.body || ""}`.trim();
12603
12791
  key: dedupKey,
12604
12792
  message: normalizedMessage
12605
12793
  });
12606
- if (this.runtimeMessages.length > 50) {
12607
- this.runtimeMessages = this.runtimeMessages.slice(-50);
12608
- }
12609
12794
  if (normalizedContent) {
12610
12795
  this.historyWriter.appendNewMessages(
12611
12796
  this.type,
@@ -12854,8 +13039,8 @@ var AcpProviderInstance = class {
12854
13039
  }
12855
13040
  getState() {
12856
13041
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
12857
- const recentMessages = normalizeChatMessages(this.messages.slice(-50).map((m) => {
12858
- const content = this.truncateContent(m.content);
13042
+ const recentMessages = normalizeChatMessages(this.messages.map((m) => {
13043
+ const content = m.content;
12859
13044
  return buildChatMessage({
12860
13045
  ...m,
12861
13046
  content
@@ -13648,18 +13833,6 @@ var AcpProviderInstance = class {
13648
13833
  }
13649
13834
  }
13650
13835
  // ─── Rich Content Helpers ────────────────────────────
13651
- /** Truncate content for transport (text: 2000 chars, images preserved) */
13652
- truncateContent(content) {
13653
- if (typeof content === "string") {
13654
- return content.length > 2e3 ? content.slice(0, 2e3) + "\n... (truncated)" : content;
13655
- }
13656
- return content.map((b) => {
13657
- if (b.type === "text" && b.text.length > 2e3) {
13658
- return { ...b, text: b.text.slice(0, 2e3) + "\n... (truncated)" };
13659
- }
13660
- return b;
13661
- });
13662
- }
13663
13836
  /** Build ContentBlock[] from current partial state */
13664
13837
  buildPartialBlocks() {
13665
13838
  const blocks = [];
@@ -13813,7 +13986,6 @@ ${rawInput}` : rawInput;
13813
13986
  }
13814
13987
  pushEvent(event) {
13815
13988
  this.events.push(event);
13816
- if (this.events.length > 50) this.events = this.events.slice(-50);
13817
13989
  }
13818
13990
  appendSystemMessage(content, timestamp = Date.now()) {
13819
13991
  const normalizedContent = String(content || "").trim();
@@ -16484,7 +16656,9 @@ var SKIP_COMMANDS = /* @__PURE__ */ new Set([
16484
16656
  "heartbeat",
16485
16657
  "status_report",
16486
16658
  "read_chat",
16487
- "mark_session_seen"
16659
+ "mark_session_seen",
16660
+ "delete_notification",
16661
+ "mark_notification_unread"
16488
16662
  ]);
16489
16663
  function shouldLogCommand(cmd) {
16490
16664
  return !SKIP_COMMANDS.has(cmd);
@@ -16761,9 +16935,24 @@ function buildStatusSnapshot(options) {
16761
16935
  completionMarker,
16762
16936
  seenCompletionMarker
16763
16937
  );
16938
+ const { unread: overlayUnread, inboxBucket: overlayInboxBucket } = applySessionNotificationOverlay({
16939
+ id: sourceSession.id,
16940
+ providerSessionId: sourceSession.providerSessionId,
16941
+ status: sourceSession.status,
16942
+ unread,
16943
+ inboxBucket,
16944
+ lastMessageHash: sourceSession.lastMessageHash,
16945
+ lastMessageAt: sourceSession.lastMessageAt,
16946
+ lastUpdated: sourceSession.lastUpdated
16947
+ }, {
16948
+ dismissedNotificationId: getSessionNotificationDismissal(state, sourceSession.id, sourceSession.providerSessionId),
16949
+ unreadNotificationId: getSessionNotificationUnreadOverride(state, sourceSession.id, sourceSession.providerSessionId)
16950
+ });
16764
16951
  session.lastSeenAt = lastSeenAt;
16765
- session.unread = unread;
16766
- session.inboxBucket = inboxBucket;
16952
+ session.unread = overlayUnread;
16953
+ session.inboxBucket = overlayInboxBucket;
16954
+ session.completionMarker = completionMarker;
16955
+ session.seenCompletionMarker = seenCompletionMarker;
16767
16956
  if (READ_DEBUG_ENABLED && (session.unread || session.inboxBucket !== "idle" || session.providerType.includes("codex"))) {
16768
16957
  const recentReadSnapshot = {
16769
16958
  sessionId: session.id,
@@ -17580,6 +17769,62 @@ var DaemonCommandRouter = class {
17580
17769
  completionMarker
17581
17770
  };
17582
17771
  }
17772
+ case "delete_notification": {
17773
+ const sessionId = args?.sessionId;
17774
+ const notificationId = typeof args?.notificationId === "string" ? args.notificationId.trim() : "";
17775
+ if (!sessionId || typeof sessionId !== "string") {
17776
+ return { success: false, error: "sessionId is required" };
17777
+ }
17778
+ if (!notificationId) {
17779
+ return { success: false, error: "notificationId is required" };
17780
+ }
17781
+ const sessionEntries = buildSessionEntries(
17782
+ this.deps.instanceManager.collectAllStates(),
17783
+ this.deps.cdpManagers
17784
+ );
17785
+ const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
17786
+ const next = dismissSessionNotification(
17787
+ loadState(),
17788
+ sessionId,
17789
+ notificationId,
17790
+ targetSession?.providerSessionId
17791
+ );
17792
+ saveState(next);
17793
+ this.deps.onStatusChange?.();
17794
+ return {
17795
+ success: true,
17796
+ sessionId,
17797
+ notificationId
17798
+ };
17799
+ }
17800
+ case "mark_notification_unread": {
17801
+ const sessionId = args?.sessionId;
17802
+ const notificationId = typeof args?.notificationId === "string" ? args.notificationId.trim() : "";
17803
+ if (!sessionId || typeof sessionId !== "string") {
17804
+ return { success: false, error: "sessionId is required" };
17805
+ }
17806
+ if (!notificationId) {
17807
+ return { success: false, error: "notificationId is required" };
17808
+ }
17809
+ const sessionEntries = buildSessionEntries(
17810
+ this.deps.instanceManager.collectAllStates(),
17811
+ this.deps.cdpManagers
17812
+ );
17813
+ const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
17814
+ const next = markSessionNotificationUnread(
17815
+ loadState(),
17816
+ sessionId,
17817
+ notificationId,
17818
+ targetSession?.providerSessionId
17819
+ );
17820
+ saveState(next);
17821
+ this.deps.onStatusChange?.();
17822
+ return {
17823
+ success: true,
17824
+ sessionId,
17825
+ notificationId
17826
+ };
17827
+ }
17583
17828
  // ─── Daemon Self-Upgrade ───
17584
17829
  case "daemon_upgrade": {
17585
17830
  LOG.info("Upgrade", "Remote upgrade requested from dashboard");
@@ -17958,6 +18203,7 @@ var DaemonStatusReporter = class {
17958
18203
 
17959
18204
  // src/index.ts
17960
18205
  init_logger();
18206
+ init_debug_config();
17961
18207
 
17962
18208
  // src/ipc-protocol.ts
17963
18209
  var DEFAULT_DAEMON_PORT = 19222;