@adhdev/daemon-core 0.7.39 → 0.7.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -170,7 +170,8 @@ var init_config = __esm({
170
170
  workspaces: [],
171
171
  defaultWorkspaceId: null,
172
172
  recentActivity: [],
173
- recentSessionReads: {},
173
+ sessionReads: {},
174
+ sessionReadMarkers: {},
174
175
  machineNickname: null,
175
176
  machineId: void 0,
176
177
  machineSecret: null,
@@ -2044,18 +2045,27 @@ function appendRecentActivity(config, entry) {
2044
2045
  function getRecentActivity(config, limit = 20) {
2045
2046
  return [...config.recentActivity || []].sort((a, b) => b.lastUsedAt - a.lastUsedAt).slice(0, limit);
2046
2047
  }
2047
- function getRecentSessionSeenAt(config, recentKey) {
2048
- return config.recentSessionReads?.[recentKey] || 0;
2048
+ function getSessionSeenAt(config, sessionId) {
2049
+ return config.sessionReads?.[sessionId] || 0;
2049
2050
  }
2050
- function markRecentSessionSeen(config, recentKey, seenAt = Date.now()) {
2051
- const prev = config.recentSessionReads || {};
2052
- const nextSeenAt = Math.max(prev[recentKey] || 0, seenAt);
2051
+ function getSessionSeenMarker(config, sessionId) {
2052
+ return config.sessionReadMarkers?.[sessionId] || "";
2053
+ }
2054
+ function markSessionSeen(config, sessionId, seenAt = Date.now(), completionMarker) {
2055
+ const prev = config.sessionReads || {};
2056
+ const nextSeenAt = Math.max(prev[sessionId] || 0, seenAt);
2057
+ const prevMarkers = config.sessionReadMarkers || {};
2058
+ const nextMarker = typeof completionMarker === "string" ? completionMarker : "";
2053
2059
  return {
2054
2060
  ...config,
2055
- recentSessionReads: {
2061
+ sessionReads: {
2056
2062
  ...prev,
2057
- [recentKey]: nextSeenAt
2058
- }
2063
+ [sessionId]: nextSeenAt
2064
+ },
2065
+ sessionReadMarkers: nextMarker ? {
2066
+ ...prevMarkers,
2067
+ [sessionId]: nextMarker
2068
+ } : prevMarkers
2059
2069
  };
2060
2070
  }
2061
2071
 
@@ -8385,6 +8395,149 @@ cleanOldFiles();
8385
8395
 
8386
8396
  // src/commands/router.ts
8387
8397
  init_logger();
8398
+
8399
+ // src/status/snapshot.ts
8400
+ init_config();
8401
+ import * as os10 from "os";
8402
+ init_terminal_screen();
8403
+ init_logger();
8404
+ var READ_DEBUG_ENABLED = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
8405
+ function buildDetectedIdeInfos(detectedIdes, cdpManagers) {
8406
+ return detectedIdes.filter((ide) => ide.installed !== false).map((ide) => ({
8407
+ id: ide.id,
8408
+ type: ide.id,
8409
+ name: ide.displayName || ide.name || ide.id,
8410
+ running: isCdpConnected(cdpManagers, ide.id),
8411
+ ...ide.path ? { path: ide.path } : {}
8412
+ }));
8413
+ }
8414
+ function buildAvailableProviders(providerLoader) {
8415
+ return providerLoader.getAll().map((provider) => ({
8416
+ type: provider.type,
8417
+ name: provider.displayName || provider.type,
8418
+ displayName: provider.displayName || provider.type,
8419
+ icon: provider.icon || "\u{1F4BB}",
8420
+ category: provider.category
8421
+ }));
8422
+ }
8423
+ function parseMessageTime(value) {
8424
+ if (typeof value === "number" && Number.isFinite(value)) return value;
8425
+ if (typeof value === "string") {
8426
+ const parsed = Date.parse(value);
8427
+ if (Number.isFinite(parsed)) return parsed;
8428
+ }
8429
+ return 0;
8430
+ }
8431
+ function getSessionMessageUpdatedAt(session) {
8432
+ const lastMessage = session.activeChat?.messages?.at?.(-1);
8433
+ if (!lastMessage) return 0;
8434
+ return parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt) || 0;
8435
+ }
8436
+ function getSessionCompletionMarker(session) {
8437
+ const lastMessage = session.activeChat?.messages?.at?.(-1);
8438
+ if (!lastMessage) return "";
8439
+ const role = typeof lastMessage.role === "string" ? lastMessage.role : "";
8440
+ if (role === "user" || role === "human") return "";
8441
+ if (typeof lastMessage._turnKey === "string" && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
8442
+ if (typeof lastMessage.id === "string" && lastMessage.id) return `id:${lastMessage.id}`;
8443
+ if (typeof lastMessage.index === "number" && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
8444
+ const timestamp = parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt);
8445
+ return timestamp > 0 ? `ts:${timestamp}` : "";
8446
+ }
8447
+ function getSessionLastUsedAt(session) {
8448
+ return getSessionMessageUpdatedAt(session) || session.lastUpdated || Date.now();
8449
+ }
8450
+ function getLastMessageRole(session) {
8451
+ const role = session.activeChat?.messages?.at?.(-1)?.role;
8452
+ return typeof role === "string" ? role : "";
8453
+ }
8454
+ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRole, completionMarker, seenCompletionMarker) {
8455
+ if (status === "waiting_approval") {
8456
+ return { unread: false, inboxBucket: "needs_attention" };
8457
+ }
8458
+ if (status === "generating" || status === "starting") {
8459
+ return { unread: false, inboxBucket: "working" };
8460
+ }
8461
+ const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human";
8462
+ return { unread, inboxBucket: unread ? "task_complete" : "idle" };
8463
+ }
8464
+ function buildRecentLaunches(recentActivity) {
8465
+ return recentActivity.map((item) => ({
8466
+ id: item.id,
8467
+ providerType: item.providerType,
8468
+ providerName: item.providerName,
8469
+ kind: item.kind,
8470
+ title: item.title || item.providerName,
8471
+ workspace: item.workspace,
8472
+ currentModel: item.currentModel,
8473
+ lastLaunchedAt: item.lastUsedAt
8474
+ })).sort((a, b) => b.lastLaunchedAt - a.lastLaunchedAt).slice(0, 12);
8475
+ }
8476
+ function buildStatusSnapshot(options) {
8477
+ const cfg = loadConfig();
8478
+ const wsState = getWorkspaceState(cfg);
8479
+ const memSnap = getHostMemorySnapshot();
8480
+ const recentActivity = getRecentActivity(cfg, 20);
8481
+ const sessions = buildSessionEntries(
8482
+ options.allStates,
8483
+ options.cdpManagers
8484
+ );
8485
+ for (const session of sessions) {
8486
+ const lastSeenAt = getSessionSeenAt(cfg, session.id);
8487
+ const seenCompletionMarker = getSessionSeenMarker(cfg, session.id);
8488
+ const lastUsedAt = getSessionLastUsedAt(session);
8489
+ const completionMarker = getSessionCompletionMarker(session);
8490
+ const { unread, inboxBucket } = session.surfaceHidden ? { unread: false, inboxBucket: "idle" } : getUnreadState(
8491
+ getSessionMessageUpdatedAt(session) > 0,
8492
+ session.status,
8493
+ lastUsedAt,
8494
+ lastSeenAt,
8495
+ getLastMessageRole(session),
8496
+ completionMarker,
8497
+ seenCompletionMarker
8498
+ );
8499
+ session.lastSeenAt = lastSeenAt;
8500
+ session.unread = unread;
8501
+ session.inboxBucket = inboxBucket;
8502
+ if (READ_DEBUG_ENABLED && (session.unread || session.inboxBucket !== "idle" || session.providerType.includes("codex"))) {
8503
+ LOG.info(
8504
+ "RecentRead",
8505
+ `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)}`
8506
+ );
8507
+ }
8508
+ }
8509
+ const terminalBackend = getTerminalBackendRuntimeStatus();
8510
+ return {
8511
+ instanceId: options.instanceId,
8512
+ version: options.version,
8513
+ daemonMode: options.daemonMode,
8514
+ machine: {
8515
+ hostname: os10.hostname(),
8516
+ platform: os10.platform(),
8517
+ arch: os10.arch(),
8518
+ cpus: os10.cpus().length,
8519
+ totalMem: memSnap.totalMem,
8520
+ freeMem: memSnap.freeMem,
8521
+ availableMem: memSnap.availableMem,
8522
+ loadavg: os10.loadavg(),
8523
+ uptime: os10.uptime(),
8524
+ release: os10.release()
8525
+ },
8526
+ machineNickname: options.machineNickname ?? cfg.machineNickname ?? null,
8527
+ timestamp: options.timestamp ?? Date.now(),
8528
+ detectedIdes: buildDetectedIdeInfos(options.detectedIdes, options.cdpManagers),
8529
+ ...options.p2p ? { p2p: options.p2p } : {},
8530
+ sessions,
8531
+ workspaces: wsState.workspaces,
8532
+ defaultWorkspaceId: wsState.defaultWorkspaceId,
8533
+ defaultWorkspacePath: wsState.defaultWorkspacePath,
8534
+ recentLaunches: buildRecentLaunches(recentActivity),
8535
+ terminalBackend,
8536
+ availableProviders: buildAvailableProviders(options.providerLoader)
8537
+ };
8538
+ }
8539
+
8540
+ // src/commands/router.ts
8388
8541
  import * as fs7 from "fs";
8389
8542
  var CHAT_COMMANDS = [
8390
8543
  "send_chat",
@@ -8393,6 +8546,7 @@ var CHAT_COMMANDS = [
8393
8546
  "set_mode",
8394
8547
  "change_model"
8395
8548
  ];
8549
+ var READ_DEBUG_ENABLED2 = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
8396
8550
  var DaemonCommandRouter = class {
8397
8551
  deps;
8398
8552
  constructor(deps) {
@@ -8563,28 +8717,35 @@ var DaemonCommandRouter = class {
8563
8717
  updateConfig({ userName: name });
8564
8718
  return { success: true, userName: name };
8565
8719
  }
8566
- case "mark_recent_seen": {
8567
- const kind = args?.kind;
8568
- const providerType = args?.providerType;
8569
- if (!kind || !providerType) {
8570
- return { success: false, error: "kind and providerType are required" };
8720
+ case "mark_session_seen": {
8721
+ const sessionId = args?.sessionId;
8722
+ if (!sessionId || typeof sessionId !== "string") {
8723
+ return { success: false, error: "sessionId is required" };
8571
8724
  }
8572
- const recentKey = args?.recentKey || buildRecentActivityKey({
8573
- kind,
8574
- providerType,
8575
- workspace: args?.workspace || null
8576
- });
8577
- const next = markRecentSessionSeen(
8578
- loadConfig(),
8579
- recentKey,
8580
- typeof args?.seenAt === "number" ? args.seenAt : Date.now()
8725
+ const currentConfig = loadConfig();
8726
+ const prevSeenAt = currentConfig.sessionReads?.[sessionId] || 0;
8727
+ const sessionEntries = buildSessionEntries(
8728
+ this.deps.instanceManager.collectAllStates(),
8729
+ this.deps.cdpManagers
8730
+ );
8731
+ const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
8732
+ const completionMarker = targetSession ? getSessionCompletionMarker(targetSession) : "";
8733
+ const next = markSessionSeen(
8734
+ currentConfig,
8735
+ sessionId,
8736
+ typeof args?.seenAt === "number" ? args.seenAt : Date.now(),
8737
+ completionMarker
8581
8738
  );
8739
+ if (READ_DEBUG_ENABLED2) {
8740
+ LOG.info("RecentRead", `mark_session_seen sessionId=${sessionId} seenAt=${String(args?.seenAt || "")} prevSeenAt=${String(prevSeenAt)} nextSeenAt=${String(next.sessionReads?.[sessionId] || 0)} marker=${completionMarker || "-"}`);
8741
+ }
8582
8742
  saveConfig(next);
8583
8743
  this.deps.onStatusChange?.();
8584
8744
  return {
8585
8745
  success: true,
8586
- recentKey,
8587
- seenAt: next.recentSessionReads?.[recentKey] || Date.now()
8746
+ sessionId,
8747
+ seenAt: next.sessionReads?.[sessionId] || Date.now(),
8748
+ completionMarker
8588
8749
  };
8589
8750
  }
8590
8751
  // ─── Daemon Self-Upgrade ───
@@ -8701,194 +8862,6 @@ var DaemonCommandRouter = class {
8701
8862
 
8702
8863
  // src/status/reporter.ts
8703
8864
  init_logger();
8704
-
8705
- // src/status/snapshot.ts
8706
- init_config();
8707
- import * as os10 from "os";
8708
- init_terminal_screen();
8709
- function buildDetectedIdeInfos(detectedIdes, cdpManagers) {
8710
- return detectedIdes.filter((ide) => ide.installed !== false).map((ide) => ({
8711
- id: ide.id,
8712
- type: ide.id,
8713
- name: ide.displayName || ide.name || ide.id,
8714
- running: isCdpConnected(cdpManagers, ide.id),
8715
- ...ide.path ? { path: ide.path } : {}
8716
- }));
8717
- }
8718
- function buildAvailableProviders(providerLoader) {
8719
- return providerLoader.getAll().map((provider) => ({
8720
- type: provider.type,
8721
- name: provider.displayName || provider.type,
8722
- displayName: provider.displayName || provider.type,
8723
- icon: provider.icon || "\u{1F4BB}",
8724
- category: provider.category
8725
- }));
8726
- }
8727
- function parseMessageTime(value) {
8728
- if (typeof value === "number" && Number.isFinite(value)) return value;
8729
- if (typeof value === "string") {
8730
- const parsed = Date.parse(value);
8731
- if (Number.isFinite(parsed)) return parsed;
8732
- }
8733
- return 0;
8734
- }
8735
- function getSessionMessageUpdatedAt(session) {
8736
- const lastMessage = session.activeChat?.messages?.at?.(-1);
8737
- if (!lastMessage) return 0;
8738
- return parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt) || 0;
8739
- }
8740
- function getSessionLastUsedAt(session) {
8741
- return getSessionMessageUpdatedAt(session) || session.lastUpdated || Date.now();
8742
- }
8743
- function getSessionKind(session) {
8744
- return session.transport === "cdp-page" || session.transport === "cdp-webview" ? "ide" : session.transport === "acp" ? "acp" : "cli";
8745
- }
8746
- function getLastMessageRole(session) {
8747
- const role = session.activeChat?.messages?.at?.(-1)?.role;
8748
- return typeof role === "string" ? role : "";
8749
- }
8750
- function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRole) {
8751
- if (status === "waiting_approval") {
8752
- return { unread: false, inboxBucket: "needs_attention" };
8753
- }
8754
- if (status === "generating" || status === "starting") {
8755
- return { unread: false, inboxBucket: "working" };
8756
- }
8757
- const unread = hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human";
8758
- return { unread, inboxBucket: unread ? "task_complete" : "idle" };
8759
- }
8760
- function buildRecentSessions(sessions, recentActivity, readState) {
8761
- const visibleKeys = /* @__PURE__ */ new Set();
8762
- const hiddenKeys = /* @__PURE__ */ new Set();
8763
- const live = sessions.filter((session) => !session.surfaceHidden && session.status !== "stopped").map((session) => {
8764
- const kind = getSessionKind(session);
8765
- const recentKey = buildRecentActivityKey({
8766
- kind,
8767
- providerType: session.providerType,
8768
- workspace: session.workspace
8769
- });
8770
- const lastSeenAt = readState[recentKey] || 0;
8771
- const lastUsedAt = getSessionLastUsedAt(session);
8772
- const { unread, inboxBucket } = getUnreadState(
8773
- getSessionMessageUpdatedAt(session) > 0,
8774
- session.status,
8775
- lastUsedAt,
8776
- lastSeenAt,
8777
- getLastMessageRole(session)
8778
- );
8779
- return {
8780
- id: session.id,
8781
- recentKey,
8782
- sessionId: session.id,
8783
- providerType: session.providerType,
8784
- providerName: session.providerName,
8785
- kind,
8786
- title: session.activeChat?.title || session.title || session.providerName,
8787
- workspace: session.workspace,
8788
- currentModel: session.currentModel,
8789
- status: session.status,
8790
- lastUsedAt,
8791
- unread,
8792
- lastSeenAt,
8793
- inboxBucket,
8794
- surfaceHidden: false
8795
- };
8796
- });
8797
- for (const item of live) {
8798
- visibleKeys.add(`${item.kind}:${item.providerType}:${item.workspace || ""}`);
8799
- }
8800
- for (const session of sessions) {
8801
- if (!session.surfaceHidden) continue;
8802
- hiddenKeys.add(`${getSessionKind(session)}:${session.providerType}:${session.workspace || ""}`);
8803
- }
8804
- const persisted = recentActivity.filter((item) => {
8805
- const key = `${item.kind}:${item.providerType}:${item.workspace || ""}`;
8806
- return !visibleKeys.has(key) && !hiddenKeys.has(key);
8807
- }).map((item) => {
8808
- const lastSeenAt = readState[item.id] || 0;
8809
- const unread = item.lastUsedAt > lastSeenAt;
8810
- return {
8811
- id: item.id,
8812
- recentKey: item.id,
8813
- sessionId: item.sessionId || null,
8814
- providerType: item.providerType,
8815
- providerName: item.providerName,
8816
- kind: item.kind,
8817
- title: item.title || item.providerName,
8818
- workspace: item.workspace,
8819
- currentModel: item.currentModel,
8820
- lastUsedAt: item.lastUsedAt,
8821
- unread,
8822
- lastSeenAt,
8823
- inboxBucket: unread ? "task_complete" : "idle",
8824
- surfaceHidden: false
8825
- };
8826
- });
8827
- return [...live, ...persisted].sort((a, b) => b.lastUsedAt - a.lastUsedAt).slice(0, 12);
8828
- }
8829
- function buildStatusSnapshot(options) {
8830
- const cfg = loadConfig();
8831
- const wsState = getWorkspaceState(cfg);
8832
- const memSnap = getHostMemorySnapshot();
8833
- const recentActivity = getRecentActivity(cfg, 20);
8834
- const sessions = buildSessionEntries(
8835
- options.allStates,
8836
- options.cdpManagers
8837
- );
8838
- const readState = cfg.recentSessionReads || {};
8839
- for (const session of sessions) {
8840
- const kind = getSessionKind(session);
8841
- const recentKey = buildRecentActivityKey({
8842
- kind,
8843
- providerType: session.providerType,
8844
- workspace: session.workspace
8845
- });
8846
- const lastSeenAt = getRecentSessionSeenAt(cfg, recentKey);
8847
- const lastUsedAt = getSessionLastUsedAt(session);
8848
- const { unread, inboxBucket } = session.surfaceHidden ? { unread: false, inboxBucket: "idle" } : getUnreadState(
8849
- getSessionMessageUpdatedAt(session) > 0,
8850
- session.status,
8851
- lastUsedAt,
8852
- lastSeenAt,
8853
- getLastMessageRole(session)
8854
- );
8855
- session.recentKey = recentKey;
8856
- session.lastSeenAt = lastSeenAt;
8857
- session.unread = unread;
8858
- session.inboxBucket = inboxBucket;
8859
- }
8860
- const terminalBackend = getTerminalBackendRuntimeStatus();
8861
- return {
8862
- instanceId: options.instanceId,
8863
- version: options.version,
8864
- daemonMode: options.daemonMode,
8865
- machine: {
8866
- hostname: os10.hostname(),
8867
- platform: os10.platform(),
8868
- arch: os10.arch(),
8869
- cpus: os10.cpus().length,
8870
- totalMem: memSnap.totalMem,
8871
- freeMem: memSnap.freeMem,
8872
- availableMem: memSnap.availableMem,
8873
- loadavg: os10.loadavg(),
8874
- uptime: os10.uptime(),
8875
- release: os10.release()
8876
- },
8877
- machineNickname: options.machineNickname ?? cfg.machineNickname ?? null,
8878
- timestamp: options.timestamp ?? Date.now(),
8879
- detectedIdes: buildDetectedIdeInfos(options.detectedIdes, options.cdpManagers),
8880
- ...options.p2p ? { p2p: options.p2p } : {},
8881
- sessions,
8882
- workspaces: wsState.workspaces,
8883
- defaultWorkspaceId: wsState.defaultWorkspaceId,
8884
- defaultWorkspacePath: wsState.defaultWorkspacePath,
8885
- recentSessions: buildRecentSessions(sessions, recentActivity, readState),
8886
- terminalBackend,
8887
- availableProviders: buildAvailableProviders(options.providerLoader)
8888
- };
8889
- }
8890
-
8891
- // src/status/reporter.ts
8892
8865
  var DaemonStatusReporter = class {
8893
8866
  deps;
8894
8867
  log;
@@ -9036,7 +9009,7 @@ var DaemonStatusReporter = class {
9036
9009
  currentModel: session.currentModel,
9037
9010
  currentPlan: session.currentPlan,
9038
9011
  currentAutoApprove: session.currentAutoApprove,
9039
- recentKey: session.recentKey,
9012
+ lastUpdated: session.lastUpdated,
9040
9013
  unread: session.unread,
9041
9014
  lastSeenAt: session.lastSeenAt,
9042
9015
  inboxBucket: session.inboxBucket,