@adhdev/daemon-core 0.8.81 → 0.8.82

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 (32) hide show
  1. package/dist/cli-adapters/provider-cli-adapter.d.ts +1 -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 +232 -41
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +232 -41
  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 +1 -1
  13. package/dist/status/snapshot.d.ts +1 -0
  14. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  15. package/package.json +1 -1
  16. package/src/cli-adapter-types.d.ts +2 -0
  17. package/src/cli-adapters/provider-cli-adapter.ts +14 -5
  18. package/src/cli-adapters/provider-cli-shared.d.ts +3 -4
  19. package/src/cli-adapters/provider-cli-shared.ts +2 -0
  20. package/src/commands/chat-commands.ts +8 -3
  21. package/src/commands/router.ts +59 -1
  22. package/src/config/recent-activity.ts +122 -0
  23. package/src/config/state-store.ts +16 -0
  24. package/src/logging/command-log.ts +2 -0
  25. package/src/providers/acp-provider-instance.ts +2 -17
  26. package/src/providers/cli-provider-instance.ts +32 -10
  27. package/src/providers/extension-provider-instance.ts +0 -2
  28. package/src/providers/ide-provider-instance.ts +0 -2
  29. package/src/providers/provider-instance.d.ts +1 -1
  30. package/src/providers/provider-instance.ts +1 -0
  31. package/src/shared-types.ts +3 -1
  32. package/src/status/snapshot.ts +18 -3
package/dist/index.mjs CHANGED
@@ -1857,6 +1857,7 @@ var init_provider_cli_adapter = __esm({
1857
1857
  recentOutputBuffer = "";
1858
1858
  isWaitingForResponse = false;
1859
1859
  activeModal = null;
1860
+ parseErrorMessage = null;
1860
1861
  responseTimeout = null;
1861
1862
  idleTimeout = null;
1862
1863
  ready = false;
@@ -2851,10 +2852,12 @@ var init_provider_cli_adapter = __esm({
2851
2852
  // ─── Public API (CliAdapter) ───────────────────
2852
2853
  getStatus() {
2853
2854
  return {
2854
- status: this.currentStatus,
2855
+ status: this.parseErrorMessage ? "error" : this.currentStatus,
2855
2856
  messages: [...this.committedMessages],
2856
2857
  workingDir: this.workingDir,
2857
- activeModal: this.activeModal
2858
+ activeModal: this.activeModal,
2859
+ errorMessage: this.parseErrorMessage || void 0,
2860
+ errorReason: this.parseErrorMessage ? "parse_error" : void 0
2858
2861
  };
2859
2862
  }
2860
2863
  seedCommittedMessages(messages) {
@@ -2908,7 +2911,7 @@ var init_provider_cli_adapter = __esm({
2908
2911
  id: "cli_session",
2909
2912
  status: this.currentStatus,
2910
2913
  title: this.cliName,
2911
- messages: messages.slice(-50).map((message, index) => buildChatMessage({
2914
+ messages: messages.map((message, index) => buildChatMessage({
2912
2915
  ...message,
2913
2916
  id: message.id || `msg_${index}`,
2914
2917
  index: typeof message.index === "number" ? message.index : index,
@@ -2939,7 +2942,10 @@ var init_provider_cli_adapter = __esm({
2939
2942
  }));
2940
2943
  }
2941
2944
  parseCurrentTranscript(baseMessages, partialResponse, scope) {
2942
- if (!this.cliScripts?.parseOutput) return null;
2945
+ if (!this.cliScripts?.parseOutput) {
2946
+ this.parseErrorMessage = null;
2947
+ return null;
2948
+ }
2943
2949
  try {
2944
2950
  const input = buildCliParseInput({
2945
2951
  accumulatedBuffer: this.accumulatedBuffer,
@@ -2967,10 +2973,13 @@ var init_provider_cli_adapter = __esm({
2967
2973
  lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
2968
2974
  }
2969
2975
  }
2976
+ this.parseErrorMessage = null;
2970
2977
  return parsed;
2971
2978
  } catch (e) {
2972
- LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
2973
- return null;
2979
+ const message = e?.message || String(e);
2980
+ this.parseErrorMessage = message;
2981
+ LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${message}`);
2982
+ throw e;
2974
2983
  }
2975
2984
  }
2976
2985
  /** Whether this adapter has CLI scripts loaded */
@@ -3834,6 +3843,96 @@ function getSessionSeenMarker(state, sessionId, providerSessionId) {
3834
3843
  const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
3835
3844
  return state.sessionReadMarkers?.[providerKey] || state.sessionReadMarkers?.[sessionId] || "";
3836
3845
  }
3846
+ function getSessionNotificationDismissal(state, sessionId, providerSessionId) {
3847
+ const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
3848
+ return state.sessionNotificationDismissals?.[providerKey] || state.sessionNotificationDismissals?.[sessionId] || "";
3849
+ }
3850
+ function getSessionNotificationUnreadOverride(state, sessionId, providerSessionId) {
3851
+ const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
3852
+ return state.sessionNotificationUnreadOverrides?.[providerKey] || state.sessionNotificationUnreadOverrides?.[sessionId] || "";
3853
+ }
3854
+ function dismissSessionNotification(state, sessionId, notificationId, providerSessionId) {
3855
+ const dismissalId = String(notificationId || "").trim();
3856
+ if (!dismissalId) return state;
3857
+ const dismissalKeys = Array.from(new Set([
3858
+ sessionId,
3859
+ buildSessionReadStateKey(sessionId, providerSessionId)
3860
+ ].filter(Boolean)));
3861
+ const nextSessionNotificationDismissals = { ...state.sessionNotificationDismissals || {} };
3862
+ const nextSessionNotificationUnreadOverrides = { ...state.sessionNotificationUnreadOverrides || {} };
3863
+ for (const key of dismissalKeys) {
3864
+ nextSessionNotificationDismissals[key] = dismissalId;
3865
+ delete nextSessionNotificationUnreadOverrides[key];
3866
+ }
3867
+ return {
3868
+ ...state,
3869
+ sessionNotificationDismissals: nextSessionNotificationDismissals,
3870
+ sessionNotificationUnreadOverrides: nextSessionNotificationUnreadOverrides
3871
+ };
3872
+ }
3873
+ function markSessionNotificationUnread(state, sessionId, notificationId, providerSessionId) {
3874
+ const unreadId = String(notificationId || "").trim();
3875
+ if (!unreadId) return state;
3876
+ const unreadKeys = Array.from(new Set([
3877
+ sessionId,
3878
+ buildSessionReadStateKey(sessionId, providerSessionId)
3879
+ ].filter(Boolean)));
3880
+ const nextSessionNotificationDismissals = { ...state.sessionNotificationDismissals || {} };
3881
+ const nextSessionNotificationUnreadOverrides = { ...state.sessionNotificationUnreadOverrides || {} };
3882
+ for (const key of unreadKeys) {
3883
+ nextSessionNotificationUnreadOverrides[key] = unreadId;
3884
+ delete nextSessionNotificationDismissals[key];
3885
+ }
3886
+ return {
3887
+ ...state,
3888
+ sessionNotificationDismissals: nextSessionNotificationDismissals,
3889
+ sessionNotificationUnreadOverrides: nextSessionNotificationUnreadOverrides
3890
+ };
3891
+ }
3892
+ function getSessionNotificationTargetValue(session) {
3893
+ const providerSessionId = typeof session.providerSessionId === "string" ? session.providerSessionId.trim() : "";
3894
+ return providerSessionId || session.id;
3895
+ }
3896
+ function getSessionCurrentNotificationId(session) {
3897
+ const inboxBucket = session.inboxBucket || "idle";
3898
+ const isNeedsAttention = inboxBucket === "needs_attention" || session.status === "waiting_approval";
3899
+ const isTaskComplete = inboxBucket === "task_complete" && !!session.unread;
3900
+ const type = isNeedsAttention ? "needs_attention" : isTaskComplete ? "task_complete" : "";
3901
+ if (!type) return "";
3902
+ const target = getSessionNotificationTargetValue(session);
3903
+ const lastMessageHash = typeof session.lastMessageHash === "string" ? session.lastMessageHash : "";
3904
+ const timestamp = Number(session.lastMessageAt || session.lastUpdated || 0);
3905
+ return [type, target, lastMessageHash, String(timestamp)].join("|");
3906
+ }
3907
+ function applySessionNotificationOverlay(session, overlay) {
3908
+ const currentNotificationId = getSessionCurrentNotificationId(session);
3909
+ const taskCompleteNotificationId = (() => {
3910
+ const target = getSessionNotificationTargetValue(session);
3911
+ const lastMessageHash = typeof session.lastMessageHash === "string" ? session.lastMessageHash : "";
3912
+ const timestamp = Number(session.lastMessageAt || session.lastUpdated || 0);
3913
+ if (!target || !lastMessageHash || !timestamp) return "";
3914
+ return ["task_complete", target, lastMessageHash, String(timestamp)].join("|");
3915
+ })();
3916
+ const dismissedNotificationId = typeof overlay.dismissedNotificationId === "string" ? overlay.dismissedNotificationId.trim() : "";
3917
+ const unreadNotificationId = typeof overlay.unreadNotificationId === "string" ? overlay.unreadNotificationId.trim() : "";
3918
+ if (unreadNotificationId && (currentNotificationId === unreadNotificationId || taskCompleteNotificationId === unreadNotificationId)) {
3919
+ const forcedInboxBucket = session.inboxBucket === "needs_attention" || session.status === "waiting_approval" ? "needs_attention" : "task_complete";
3920
+ return {
3921
+ unread: true,
3922
+ inboxBucket: forcedInboxBucket
3923
+ };
3924
+ }
3925
+ if (!currentNotificationId || !dismissedNotificationId || currentNotificationId !== dismissedNotificationId) {
3926
+ return {
3927
+ unread: !!session.unread,
3928
+ inboxBucket: session.inboxBucket || "idle"
3929
+ };
3930
+ }
3931
+ return {
3932
+ unread: false,
3933
+ inboxBucket: "idle"
3934
+ };
3935
+ }
3837
3936
  function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker, providerSessionId) {
3838
3937
  const prev = state.sessionReads || {};
3839
3938
  const prevMarkers = state.sessionReadMarkers || {};
@@ -3844,14 +3943,20 @@ function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker
3844
3943
  ].filter(Boolean)));
3845
3944
  const nextSessionReads = { ...prev };
3846
3945
  const nextSessionReadMarkers = { ...prevMarkers };
3946
+ const nextSessionNotificationDismissals = { ...state.sessionNotificationDismissals || {} };
3947
+ const nextSessionNotificationUnreadOverrides = { ...state.sessionNotificationUnreadOverrides || {} };
3847
3948
  for (const key of readKeys) {
3848
3949
  nextSessionReads[key] = Math.max(prev[key] || 0, seenAt);
3849
3950
  if (nextMarker) nextSessionReadMarkers[key] = nextMarker;
3951
+ delete nextSessionNotificationDismissals[key];
3952
+ delete nextSessionNotificationUnreadOverrides[key];
3850
3953
  }
3851
3954
  return {
3852
3955
  ...state,
3853
3956
  sessionReads: nextSessionReads,
3854
- sessionReadMarkers: nextMarker ? nextSessionReadMarkers : prevMarkers
3957
+ sessionReadMarkers: nextMarker ? nextSessionReadMarkers : prevMarkers,
3958
+ sessionNotificationDismissals: nextSessionNotificationDismissals,
3959
+ sessionNotificationUnreadOverrides: nextSessionNotificationUnreadOverrides
3855
3960
  };
3856
3961
  }
3857
3962
 
@@ -3936,7 +4041,9 @@ var DEFAULT_STATE = {
3936
4041
  recentActivity: [],
3937
4042
  savedProviderSessions: [],
3938
4043
  sessionReads: {},
3939
- sessionReadMarkers: {}
4044
+ sessionReadMarkers: {},
4045
+ sessionNotificationDismissals: {},
4046
+ sessionNotificationUnreadOverrides: {}
3940
4047
  };
3941
4048
  function isPlainObject2(value) {
3942
4049
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -3968,11 +4075,19 @@ function normalizeState(raw) {
3968
4075
  const sessionReadMarkers = Object.fromEntries(
3969
4076
  Object.entries(isPlainObject2(parsed.sessionReadMarkers) ? parsed.sessionReadMarkers : {}).filter(([key, value]) => !isLegacyVolatileSessionReadKey(key) && typeof value === "string")
3970
4077
  );
4078
+ const sessionNotificationDismissals = Object.fromEntries(
4079
+ Object.entries(isPlainObject2(parsed.sessionNotificationDismissals) ? parsed.sessionNotificationDismissals : {}).filter(([key, value]) => !isLegacyVolatileSessionReadKey(key) && typeof value === "string" && value.length > 0)
4080
+ );
4081
+ const sessionNotificationUnreadOverrides = Object.fromEntries(
4082
+ Object.entries(isPlainObject2(parsed.sessionNotificationUnreadOverrides) ? parsed.sessionNotificationUnreadOverrides : {}).filter(([key, value]) => !isLegacyVolatileSessionReadKey(key) && typeof value === "string" && value.length > 0)
4083
+ );
3971
4084
  return {
3972
4085
  recentActivity,
3973
4086
  savedProviderSessions,
3974
4087
  sessionReads,
3975
- sessionReadMarkers
4088
+ sessionReadMarkers,
4089
+ sessionNotificationDismissals,
4090
+ sessionNotificationUnreadOverrides
3976
4091
  };
3977
4092
  }
3978
4093
  function loadState() {
@@ -7295,7 +7410,6 @@ var ExtensionProviderInstance = class {
7295
7410
  }
7296
7411
  pushEvent(event) {
7297
7412
  this.events.push(event);
7298
- if (this.events.length > 50) this.events = this.events.slice(-50);
7299
7413
  }
7300
7414
  applyProviderResponse(data, options) {
7301
7415
  if (!data || typeof data !== "object") return;
@@ -7371,7 +7485,6 @@ var ExtensionProviderInstance = class {
7371
7485
  key: dedupKey,
7372
7486
  message: normalizedMessage
7373
7487
  });
7374
- if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
7375
7488
  if (normalizedContent) {
7376
7489
  this.historyWriter.appendNewMessages(
7377
7490
  this.type,
@@ -7906,7 +8019,6 @@ var IdeProviderInstance = class {
7906
8019
  }
7907
8020
  pushEvent(event) {
7908
8021
  this.events.push(event);
7909
- if (this.events.length > 50) this.events = this.events.slice(-50);
7910
8022
  }
7911
8023
  applyProviderResponse(data, options) {
7912
8024
  if (!data || typeof data !== "object") return;
@@ -7996,7 +8108,6 @@ var IdeProviderInstance = class {
7996
8108
  key: dedupKey,
7997
8109
  message: normalizedMessage
7998
8110
  });
7999
- if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
8000
8111
  if (normalizedContent) {
8001
8112
  this.historyWriter.appendNewMessages(
8002
8113
  this.type,
@@ -9560,7 +9671,14 @@ async function handleReadChat(h, args) {
9560
9671
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
9561
9672
  if (adapter) {
9562
9673
  _log(`${transport} adapter: ${adapter.cliType}`);
9563
- const parsedStatus = typeof adapter.getScriptParsedStatus === "function" ? parseMaybeJson(adapter.getScriptParsedStatus()) : null;
9674
+ let parsedStatus = null;
9675
+ if (typeof adapter.getScriptParsedStatus === "function") {
9676
+ try {
9677
+ parsedStatus = parseMaybeJson(adapter.getScriptParsedStatus());
9678
+ } catch (error) {
9679
+ return { success: false, error: error?.message || String(error) };
9680
+ }
9681
+ }
9564
9682
  const parsedRecord = parsedStatus && typeof parsedStatus === "object" ? parsedStatus : null;
9565
9683
  const status = parsedRecord || adapter.getStatus();
9566
9684
  const title = typeof parsedRecord?.title === "string" ? parsedRecord.title : void 0;
@@ -11894,6 +12012,8 @@ var CliProviderInstance = class {
11894
12012
  runtimeMessages = [];
11895
12013
  instanceId;
11896
12014
  suppressIdleHistoryReplay = false;
12015
+ errorMessage = void 0;
12016
+ errorReason = void 0;
11897
12017
  presentationMode;
11898
12018
  providerSessionId;
11899
12019
  launchMode;
@@ -12017,9 +12137,24 @@ var CliProviderInstance = class {
12017
12137
  }
12018
12138
  getState() {
12019
12139
  const adapterStatus = this.adapter.getStatus();
12020
- const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
12140
+ let parsedStatus = null;
12141
+ let parseErrorMessage;
12142
+ if (typeof this.adapter.getScriptParsedStatus === "function") {
12143
+ try {
12144
+ parsedStatus = this.adapter.getScriptParsedStatus() || null;
12145
+ this.errorMessage = void 0;
12146
+ this.errorReason = void 0;
12147
+ } catch (error) {
12148
+ parseErrorMessage = error?.message || String(error);
12149
+ this.errorMessage = parseErrorMessage;
12150
+ this.errorReason = "parse_error";
12151
+ }
12152
+ } else {
12153
+ this.errorMessage = void 0;
12154
+ this.errorReason = void 0;
12155
+ }
12021
12156
  const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
12022
- const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
12157
+ const visibleStatus = parseErrorMessage ? "error" : autoApproveActive ? "generating" : adapterStatus.status;
12023
12158
  const parsedProviderSessionId = normalizeProviderSessionId(
12024
12159
  this.type,
12025
12160
  typeof parsedStatus?.providerSessionId === "string" ? parsedStatus.providerSessionId : ""
@@ -12029,7 +12164,7 @@ var CliProviderInstance = class {
12029
12164
  }
12030
12165
  const runtime = this.adapter.getRuntimeMetadata();
12031
12166
  this.maybeAppendRuntimeRecoveryMessage(runtime);
12032
- let parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
12167
+ let parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : parseErrorMessage ? normalizeChatMessages(Array.isArray(adapterStatus.messages) ? adapterStatus.messages : []) : [];
12033
12168
  const historyMessageCount = Number.isFinite(parsedStatus?.historyMessageCount) ? Math.max(0, Number(parsedStatus.historyMessageCount)) : null;
12034
12169
  if (historyMessageCount !== null) {
12035
12170
  parsedMessages = historyMessageCount > 0 ? parsedMessages.slice(-historyMessageCount) : [];
@@ -12069,7 +12204,7 @@ var CliProviderInstance = class {
12069
12204
  activeChat: {
12070
12205
  id: `${this.type}_${this.workingDir}`,
12071
12206
  title: parsedStatus?.title || dirName,
12072
- status: autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
12207
+ status: parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
12073
12208
  messages: mergedMessages,
12074
12209
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
12075
12210
  inputContent: ""
@@ -12095,7 +12230,9 @@ var CliProviderInstance = class {
12095
12230
  resume: this.provider.resume,
12096
12231
  controlValues: surface.controlValues,
12097
12232
  providerControls: this.provider.controls,
12098
- summaryMetadata: surface.summaryMetadata
12233
+ summaryMetadata: surface.summaryMetadata,
12234
+ errorMessage: this.errorMessage,
12235
+ errorReason: this.errorReason
12099
12236
  };
12100
12237
  }
12101
12238
  setPresentationMode(mode) {
@@ -12282,7 +12419,6 @@ var CliProviderInstance = class {
12282
12419
  }
12283
12420
  pushEvent(event) {
12284
12421
  this.events.push(event);
12285
- if (this.events.length > 50) this.events = this.events.slice(-50);
12286
12422
  }
12287
12423
  flushEvents() {
12288
12424
  const events = [...this.events];
@@ -12464,9 +12600,6 @@ ${effect.notification.body || ""}`.trim();
12464
12600
  key: dedupKey,
12465
12601
  message: normalizedMessage
12466
12602
  });
12467
- if (this.runtimeMessages.length > 50) {
12468
- this.runtimeMessages = this.runtimeMessages.slice(-50);
12469
- }
12470
12603
  if (normalizedContent) {
12471
12604
  this.historyWriter.appendNewMessages(
12472
12605
  this.type,
@@ -12720,8 +12853,8 @@ var AcpProviderInstance = class {
12720
12853
  }
12721
12854
  getState() {
12722
12855
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
12723
- const recentMessages = normalizeChatMessages(this.messages.slice(-50).map((m) => {
12724
- const content = this.truncateContent(m.content);
12856
+ const recentMessages = normalizeChatMessages(this.messages.map((m) => {
12857
+ const content = m.content;
12725
12858
  return buildChatMessage({
12726
12859
  ...m,
12727
12860
  content
@@ -13514,18 +13647,6 @@ var AcpProviderInstance = class {
13514
13647
  }
13515
13648
  }
13516
13649
  // ─── Rich Content Helpers ────────────────────────────
13517
- /** Truncate content for transport (text: 2000 chars, images preserved) */
13518
- truncateContent(content) {
13519
- if (typeof content === "string") {
13520
- return content.length > 2e3 ? content.slice(0, 2e3) + "\n... (truncated)" : content;
13521
- }
13522
- return content.map((b) => {
13523
- if (b.type === "text" && b.text.length > 2e3) {
13524
- return { ...b, text: b.text.slice(0, 2e3) + "\n... (truncated)" };
13525
- }
13526
- return b;
13527
- });
13528
- }
13529
13650
  /** Build ContentBlock[] from current partial state */
13530
13651
  buildPartialBlocks() {
13531
13652
  const blocks = [];
@@ -13679,7 +13800,6 @@ ${rawInput}` : rawInput;
13679
13800
  }
13680
13801
  pushEvent(event) {
13681
13802
  this.events.push(event);
13682
- if (this.events.length > 50) this.events = this.events.slice(-50);
13683
13803
  }
13684
13804
  appendSystemMessage(content, timestamp = Date.now()) {
13685
13805
  const normalizedContent = String(content || "").trim();
@@ -16350,7 +16470,9 @@ var SKIP_COMMANDS = /* @__PURE__ */ new Set([
16350
16470
  "heartbeat",
16351
16471
  "status_report",
16352
16472
  "read_chat",
16353
- "mark_session_seen"
16473
+ "mark_session_seen",
16474
+ "delete_notification",
16475
+ "mark_notification_unread"
16354
16476
  ]);
16355
16477
  function shouldLogCommand(cmd) {
16356
16478
  return !SKIP_COMMANDS.has(cmd);
@@ -16627,9 +16749,22 @@ function buildStatusSnapshot(options) {
16627
16749
  completionMarker,
16628
16750
  seenCompletionMarker
16629
16751
  );
16752
+ const { unread: overlayUnread, inboxBucket: overlayInboxBucket } = applySessionNotificationOverlay({
16753
+ id: sourceSession.id,
16754
+ providerSessionId: sourceSession.providerSessionId,
16755
+ status: sourceSession.status,
16756
+ unread,
16757
+ inboxBucket,
16758
+ lastMessageHash: sourceSession.lastMessageHash,
16759
+ lastMessageAt: sourceSession.lastMessageAt,
16760
+ lastUpdated: sourceSession.lastUpdated
16761
+ }, {
16762
+ dismissedNotificationId: getSessionNotificationDismissal(state, sourceSession.id, sourceSession.providerSessionId),
16763
+ unreadNotificationId: getSessionNotificationUnreadOverride(state, sourceSession.id, sourceSession.providerSessionId)
16764
+ });
16630
16765
  session.lastSeenAt = lastSeenAt;
16631
- session.unread = unread;
16632
- session.inboxBucket = inboxBucket;
16766
+ session.unread = overlayUnread;
16767
+ session.inboxBucket = overlayInboxBucket;
16633
16768
  if (READ_DEBUG_ENABLED && (session.unread || session.inboxBucket !== "idle" || session.providerType.includes("codex"))) {
16634
16769
  const recentReadSnapshot = {
16635
16770
  sessionId: session.id,
@@ -17446,6 +17581,62 @@ var DaemonCommandRouter = class {
17446
17581
  completionMarker
17447
17582
  };
17448
17583
  }
17584
+ case "delete_notification": {
17585
+ const sessionId = args?.sessionId;
17586
+ const notificationId = typeof args?.notificationId === "string" ? args.notificationId.trim() : "";
17587
+ if (!sessionId || typeof sessionId !== "string") {
17588
+ return { success: false, error: "sessionId is required" };
17589
+ }
17590
+ if (!notificationId) {
17591
+ return { success: false, error: "notificationId is required" };
17592
+ }
17593
+ const sessionEntries = buildSessionEntries(
17594
+ this.deps.instanceManager.collectAllStates(),
17595
+ this.deps.cdpManagers
17596
+ );
17597
+ const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
17598
+ const next = dismissSessionNotification(
17599
+ loadState(),
17600
+ sessionId,
17601
+ notificationId,
17602
+ targetSession?.providerSessionId
17603
+ );
17604
+ saveState(next);
17605
+ this.deps.onStatusChange?.();
17606
+ return {
17607
+ success: true,
17608
+ sessionId,
17609
+ notificationId
17610
+ };
17611
+ }
17612
+ case "mark_notification_unread": {
17613
+ const sessionId = args?.sessionId;
17614
+ const notificationId = typeof args?.notificationId === "string" ? args.notificationId.trim() : "";
17615
+ if (!sessionId || typeof sessionId !== "string") {
17616
+ return { success: false, error: "sessionId is required" };
17617
+ }
17618
+ if (!notificationId) {
17619
+ return { success: false, error: "notificationId is required" };
17620
+ }
17621
+ const sessionEntries = buildSessionEntries(
17622
+ this.deps.instanceManager.collectAllStates(),
17623
+ this.deps.cdpManagers
17624
+ );
17625
+ const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
17626
+ const next = markSessionNotificationUnread(
17627
+ loadState(),
17628
+ sessionId,
17629
+ notificationId,
17630
+ targetSession?.providerSessionId
17631
+ );
17632
+ saveState(next);
17633
+ this.deps.onStatusChange?.();
17634
+ return {
17635
+ success: true,
17636
+ sessionId,
17637
+ notificationId
17638
+ };
17639
+ }
17449
17640
  // ─── Daemon Self-Upgrade ───
17450
17641
  case "daemon_upgrade": {
17451
17642
  LOG.info("Upgrade", "Remote upgrade requested from dashboard");