@adhdev/daemon-core 0.7.35 → 0.7.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.
Files changed (36) hide show
  1. package/dist/index.d.mts +29 -3
  2. package/dist/index.d.ts +29 -3
  3. package/dist/index.js +637 -253
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +637 -255
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/{normalize-auJAPmKy.d.mts → normalize-DVI4Lo5I.d.mts} +63 -1
  8. package/dist/{normalize-auJAPmKy.d.ts → normalize-DVI4Lo5I.d.ts} +63 -1
  9. package/dist/status/normalize.d.mts +1 -1
  10. package/dist/status/normalize.d.ts +1 -1
  11. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  12. package/package.json +1 -1
  13. package/src/agent-stream/manager.ts +14 -1
  14. package/src/agent-stream/provider-adapter.ts +45 -3
  15. package/src/agent-stream/types.ts +4 -0
  16. package/src/cli-adapters/terminal-screen.ts +15 -0
  17. package/src/commands/cli-manager.ts +35 -0
  18. package/src/commands/router.ts +44 -1
  19. package/src/commands/workspace-commands.ts +2 -1
  20. package/src/config/config.d.ts +4 -0
  21. package/src/config/config.ts +9 -1
  22. package/src/config/recent-activity.ts +83 -0
  23. package/src/config/workspaces.d.ts +3 -1
  24. package/src/config/workspaces.ts +15 -1
  25. package/src/index.ts +16 -0
  26. package/src/providers/acp-provider-instance.ts +5 -0
  27. package/src/providers/cli-provider-instance.ts +2 -0
  28. package/src/providers/contracts.ts +94 -0
  29. package/src/providers/extension-provider-instance.ts +4 -0
  30. package/src/providers/ide-provider-instance.ts +2 -0
  31. package/src/providers/provider-instance.ts +5 -1
  32. package/src/shared-types.d.ts +35 -0
  33. package/src/shared-types.ts +70 -0
  34. package/src/status/builders.ts +90 -1
  35. package/src/status/reporter.ts +8 -0
  36. package/src/status/snapshot.ts +162 -0
@@ -8,14 +8,19 @@
8
8
 
9
9
  import * as os from 'os';
10
10
  import { loadConfig } from '../config/config.js';
11
+ import { buildRecentActivityKey, getRecentActivity, getRecentSessionSeenAt } from '../config/recent-activity.js';
11
12
  import { getWorkspaceState } from '../config/workspaces.js';
12
13
  import { getWorkspaceActivity } from '../config/workspace-activity.js';
13
14
  import { getHostMemorySnapshot } from '../system/host-memory.js';
15
+ import { getTerminalBackendRuntimeStatus } from '../cli-adapters/terminal-screen.js';
14
16
  import { buildSessionEntries, isCdpConnected } from './builders.js';
15
17
  import type { ProviderState } from '../providers/provider-instance.js';
16
18
  import type {
17
19
  AvailableProviderInfo,
18
20
  DetectedIdeInfo,
21
+ RecentSessionBucket,
22
+ RecentSessionEntry,
23
+ SessionEntry,
19
24
  StatusReportPayload,
20
25
  } from '../shared-types.js';
21
26
 
@@ -76,14 +81,169 @@ function buildAvailableProviders(
76
81
  }));
77
82
  }
78
83
 
84
+ function parseMessageTime(value: unknown): number {
85
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
86
+ if (typeof value === 'string') {
87
+ const parsed = Date.parse(value);
88
+ if (Number.isFinite(parsed)) return parsed;
89
+ }
90
+ return 0;
91
+ }
92
+
93
+ function getSessionMessageUpdatedAt(session: {
94
+ activeChat?: {
95
+ messages?: Array<{ timestamp?: number | string; receivedAt?: number | string; createdAt?: number | string }> | null
96
+ } | null
97
+ }) {
98
+ const lastMessage = session.activeChat?.messages?.at?.(-1);
99
+ if (!lastMessage) return 0;
100
+ return (
101
+ parseMessageTime(lastMessage.timestamp)
102
+ || parseMessageTime(lastMessage.receivedAt)
103
+ || parseMessageTime(lastMessage.createdAt)
104
+ || 0
105
+ );
106
+ }
107
+
108
+ function getSessionLastUsedAt(session: {
109
+ activeChat?: {
110
+ messages?: Array<{ timestamp?: number | string; receivedAt?: number | string; createdAt?: number | string }> | null
111
+ } | null
112
+ lastUpdated?: number
113
+ }) {
114
+ return getSessionMessageUpdatedAt(session) || session.lastUpdated || Date.now();
115
+ }
116
+
117
+ function getSessionKind(session: SessionEntry): RecentSessionEntry['kind'] {
118
+ return session.transport === 'cdp-page' || session.transport === 'cdp-webview'
119
+ ? 'ide'
120
+ : session.transport === 'acp'
121
+ ? 'acp'
122
+ : 'cli';
123
+ }
124
+
125
+ function getLastMessageRole(session: { activeChat?: { messages?: Array<{ role?: string }> | null } | null }): string {
126
+ const role = session.activeChat?.messages?.at?.(-1)?.role;
127
+ return typeof role === 'string' ? role : '';
128
+ }
129
+
130
+ function getUnreadState(
131
+ hasContentChange: boolean,
132
+ status: SessionEntry['status'] | undefined,
133
+ lastUsedAt: number,
134
+ lastSeenAt: number,
135
+ lastRole: string,
136
+ ): { unread: boolean; inboxBucket: RecentSessionBucket } {
137
+ if (status === 'waiting_approval') {
138
+ return { unread: false, inboxBucket: 'needs_attention' };
139
+ }
140
+ if (status === 'generating' || status === 'starting') {
141
+ return { unread: false, inboxBucket: 'working' };
142
+ }
143
+ const unread = hasContentChange && lastUsedAt > lastSeenAt && lastRole !== 'user' && lastRole !== 'human';
144
+ return { unread, inboxBucket: unread ? 'task_complete' : 'idle' };
145
+ }
146
+
147
+ function buildRecentSessions(
148
+ sessions: ReturnType<typeof buildSessionEntries>,
149
+ recentActivity: ReturnType<typeof getRecentActivity>,
150
+ readState: Record<string, number>,
151
+ ): RecentSessionEntry[] {
152
+ const live = sessions
153
+ .filter((session) => !session.parentId && session.status !== 'stopped')
154
+ .map((session) => {
155
+ const kind = getSessionKind(session);
156
+ const recentKey = buildRecentActivityKey({
157
+ kind,
158
+ providerType: session.providerType,
159
+ workspace: session.workspace,
160
+ });
161
+ const lastSeenAt = readState[recentKey] || 0;
162
+ const lastUsedAt = getSessionLastUsedAt(session);
163
+ const { unread, inboxBucket } = getUnreadState(
164
+ getSessionMessageUpdatedAt(session) > 0,
165
+ session.status,
166
+ lastUsedAt,
167
+ lastSeenAt,
168
+ getLastMessageRole(session),
169
+ );
170
+ return {
171
+ id: session.id,
172
+ recentKey,
173
+ sessionId: session.id,
174
+ providerType: session.providerType,
175
+ providerName: session.providerName,
176
+ kind,
177
+ title: session.activeChat?.title || session.title || session.providerName,
178
+ workspace: session.workspace,
179
+ currentModel: session.currentModel,
180
+ status: session.status,
181
+ lastUsedAt,
182
+ unread,
183
+ lastSeenAt,
184
+ inboxBucket,
185
+ };
186
+ })
187
+ const seen = new Set(live.map((item) => `${item.kind}:${item.providerType}:${item.workspace || ''}`));
188
+ const persisted = recentActivity
189
+ .filter((item) => !seen.has(`${item.kind}:${item.providerType}:${item.workspace || ''}`))
190
+ .map((item) => {
191
+ const lastSeenAt = readState[item.id] || 0;
192
+ const unread = item.lastUsedAt > lastSeenAt;
193
+ return {
194
+ id: item.id,
195
+ recentKey: item.id,
196
+ sessionId: item.sessionId || null,
197
+ providerType: item.providerType,
198
+ providerName: item.providerName,
199
+ kind: item.kind,
200
+ title: item.title || item.providerName,
201
+ workspace: item.workspace,
202
+ currentModel: item.currentModel,
203
+ lastUsedAt: item.lastUsedAt,
204
+ unread,
205
+ lastSeenAt,
206
+ inboxBucket: unread ? 'task_complete' : 'idle' as RecentSessionBucket,
207
+ };
208
+ });
209
+
210
+ return [...live, ...persisted]
211
+ .sort((a, b) => b.lastUsedAt - a.lastUsedAt)
212
+ .slice(0, 12);
213
+ }
214
+
79
215
  export function buildStatusSnapshot(options: StatusSnapshotOptions): StatusSnapshot {
80
216
  const cfg = loadConfig();
81
217
  const wsState = getWorkspaceState(cfg);
82
218
  const memSnap = getHostMemorySnapshot();
219
+ const recentActivity = getRecentActivity(cfg, 20);
83
220
  const sessions = buildSessionEntries(
84
221
  options.allStates,
85
222
  options.cdpManagers as Map<string, any>,
86
223
  );
224
+ const readState = cfg.recentSessionReads || {};
225
+ for (const session of sessions) {
226
+ const kind = getSessionKind(session);
227
+ const recentKey = buildRecentActivityKey({
228
+ kind,
229
+ providerType: session.providerType,
230
+ workspace: session.workspace,
231
+ });
232
+ const lastSeenAt = getRecentSessionSeenAt(cfg, recentKey);
233
+ const lastUsedAt = getSessionLastUsedAt(session);
234
+ const { unread, inboxBucket } = getUnreadState(
235
+ getSessionMessageUpdatedAt(session) > 0,
236
+ session.status,
237
+ lastUsedAt,
238
+ lastSeenAt,
239
+ getLastMessageRole(session),
240
+ );
241
+ session.recentKey = recentKey;
242
+ session.lastSeenAt = lastSeenAt;
243
+ session.unread = unread;
244
+ session.inboxBucket = inboxBucket;
245
+ }
246
+ const terminalBackend = getTerminalBackendRuntimeStatus();
87
247
 
88
248
  return {
89
249
  instanceId: options.instanceId,
@@ -110,6 +270,8 @@ export function buildStatusSnapshot(options: StatusSnapshotOptions): StatusSnaps
110
270
  defaultWorkspaceId: wsState.defaultWorkspaceId,
111
271
  defaultWorkspacePath: wsState.defaultWorkspacePath,
112
272
  workspaceActivity: getWorkspaceActivity(cfg, 15),
273
+ recentSessions: buildRecentSessions(sessions, recentActivity, readState),
274
+ terminalBackend,
113
275
  availableProviders: buildAvailableProviders(options.providerLoader),
114
276
  };
115
277
  }