@adhdev/daemon-core 0.7.39 → 0.7.41
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/commands/cli-manager.d.ts +2 -1
- package/dist/config/config.d.ts +4 -2
- package/dist/config/recent-activity.d.ts +6 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +247 -224
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +247 -224
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +10 -1
- package/dist/providers/contracts.d.ts +79 -0
- package/dist/providers/provider-instance.d.ts +2 -0
- package/dist/shared-types-extra.d.ts +1 -1
- package/dist/shared-types.d.ts +8 -12
- package/dist/status/snapshot.d.ts +13 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/commands/cli-manager.ts +43 -5
- package/src/commands/router.ts +26 -16
- package/src/config/config.ts +6 -3
- package/src/config/recent-activity.ts +24 -12
- package/src/index.ts +2 -2
- package/src/providers/cli-provider-instance.ts +29 -2
- package/src/providers/contracts.ts +80 -0
- package/src/providers/provider-instance.ts +2 -0
- package/src/shared-types-extra.ts +1 -1
- package/src/shared-types.d.ts +4 -6
- package/src/shared-types.ts +8 -12
- package/src/status/builders.ts +2 -0
- package/src/status/reporter.ts +1 -1
- package/src/status/snapshot.ts +61 -91
|
@@ -317,6 +317,20 @@ export interface ProviderModule {
|
|
|
317
317
|
shell?: boolean;
|
|
318
318
|
env?: Record<string, string>;
|
|
319
319
|
};
|
|
320
|
+
/**
|
|
321
|
+
* Configurable options shown at session launch time (schema declaration).
|
|
322
|
+
* The frontend renders these as a launch config UI.
|
|
323
|
+
* Values are passed to launchArgBuilder to produce the final args.
|
|
324
|
+
*/
|
|
325
|
+
launchOptions?: ProviderLaunchOption[];
|
|
326
|
+
/**
|
|
327
|
+
* Builds extra spawn args from user-selected launch option values.
|
|
328
|
+
* Called with the merged defaults + mode preset + user overrides.
|
|
329
|
+
* When defined, takes precedence over launchMode.extraArgs.
|
|
330
|
+
*/
|
|
331
|
+
launchArgBuilder?: (options: Record<string, string | boolean | number>) => string[];
|
|
332
|
+
/** Named presets — shortcuts that set launchOption values in bulk */
|
|
333
|
+
launchModes?: ProviderLaunchMode[];
|
|
320
334
|
patterns?: {
|
|
321
335
|
prompt?: RegExp[];
|
|
322
336
|
generating?: RegExp[];
|
|
@@ -393,6 +407,72 @@ export interface ProviderModule {
|
|
|
393
407
|
auth?: AcpAuthMethod[];
|
|
394
408
|
}
|
|
395
409
|
|
|
410
|
+
// ─── CLI Launch Options (individual configurable flags) ────────────────
|
|
411
|
+
|
|
412
|
+
export type ProviderLaunchOptionType = 'select' | 'boolean' | 'string' | 'number';
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* A single configurable option exposed at session launch time.
|
|
416
|
+
* Providers declare these so the frontend can render a launch config UI.
|
|
417
|
+
* The final args are built by launchArgBuilder(selectedValues).
|
|
418
|
+
*
|
|
419
|
+
* Example — Claude Code:
|
|
420
|
+
* { id: 'outputFormat', type: 'select', options: [
|
|
421
|
+
* { value: 'terminal', label: 'Terminal' },
|
|
422
|
+
* { value: 'stream-json', label: 'Chat' },
|
|
423
|
+
* ], default: 'terminal' }
|
|
424
|
+
*/
|
|
425
|
+
export interface ProviderLaunchOption {
|
|
426
|
+
/** Unique identifier — key used in launchArgBuilder options map */
|
|
427
|
+
id: string;
|
|
428
|
+
/** Display label */
|
|
429
|
+
name: string;
|
|
430
|
+
description?: string;
|
|
431
|
+
type: ProviderLaunchOptionType;
|
|
432
|
+
/** Options for 'select' type */
|
|
433
|
+
options?: { value: string; label: string; description?: string }[];
|
|
434
|
+
/** Default value — applied when not explicitly set */
|
|
435
|
+
default?: string | boolean | number;
|
|
436
|
+
/**
|
|
437
|
+
* Maps specific values of this option to a frontend rendering hint.
|
|
438
|
+
* e.g. { 'stream-json': 'stream-json' } tells the UI to switch to Chat mode.
|
|
439
|
+
*/
|
|
440
|
+
outputFormatMap?: Record<string, 'terminal' | 'stream-json'>;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* A named launch preset — shorthand for a specific set of launchOption values.
|
|
445
|
+
* When selected, its `options` are merged with user-configured values before
|
|
446
|
+
* calling launchArgBuilder. Falls back to `extraArgs` if no launchArgBuilder defined.
|
|
447
|
+
*/
|
|
448
|
+
export interface ProviderLaunchMode {
|
|
449
|
+
/** Unique mode identifier (e.g. 'terminal', 'chat') */
|
|
450
|
+
id: string;
|
|
451
|
+
/** Display name */
|
|
452
|
+
name: string;
|
|
453
|
+
description?: string;
|
|
454
|
+
/**
|
|
455
|
+
* Preset option values — merged over defaults before calling launchArgBuilder.
|
|
456
|
+
* Keys correspond to ProviderLaunchOption.id.
|
|
457
|
+
*/
|
|
458
|
+
options?: Record<string, string | boolean | number>;
|
|
459
|
+
/**
|
|
460
|
+
* Fallback: raw args appended when no launchArgBuilder is defined.
|
|
461
|
+
* Use launchArgBuilder + options for anything more than trivial cases.
|
|
462
|
+
*/
|
|
463
|
+
extraArgs?: string[];
|
|
464
|
+
/** Env var overrides applied on top of spawn.env */
|
|
465
|
+
env?: Record<string, string>;
|
|
466
|
+
/**
|
|
467
|
+
* Output rendering hint (shorthand when not using launchOptions/outputFormatMap).
|
|
468
|
+
* - 'terminal' — raw PTY stream (default)
|
|
469
|
+
* - 'stream-json' — structured JSON events → chat messages
|
|
470
|
+
*/
|
|
471
|
+
outputFormat?: 'terminal' | 'stream-json';
|
|
472
|
+
/** Whether this is the default mode when none is specified */
|
|
473
|
+
default?: boolean;
|
|
474
|
+
}
|
|
475
|
+
|
|
396
476
|
export interface ProviderResumeCapability {
|
|
397
477
|
supported: boolean;
|
|
398
478
|
stopStrategy?: 'command' | 'ctrl_c';
|
|
@@ -103,6 +103,8 @@ export interface CliProviderState extends ProviderStateBase {
|
|
|
103
103
|
category: 'cli';
|
|
104
104
|
/** terminal = PTY stream, chat = parsed conversation */
|
|
105
105
|
mode: 'terminal' | 'chat';
|
|
106
|
+
/** Active launch mode id (e.g. 'chat') — undefined means default terminal */
|
|
107
|
+
launchMode?: string;
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
/** ACP provider state */
|
|
@@ -20,7 +20,7 @@ export interface RuntimeAttachedClient {
|
|
|
20
20
|
readOnly: boolean;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/** Session status union (used by SessionEntry.status,
|
|
23
|
+
/** Session status union (used by SessionEntry.status, legacy recent-launch metadata, etc.) */
|
|
24
24
|
export type SessionStatus = 'idle' | 'generating' | 'waiting_approval' | 'error' | 'stopped' | 'starting' | 'panel_hidden' | 'not_monitored' | 'disconnected';
|
|
25
25
|
|
|
26
26
|
/** Inbox bucket categories for recent sessions */
|
package/src/shared-types.d.ts
CHANGED
|
@@ -147,17 +147,15 @@ export interface WorkspaceActivity {
|
|
|
147
147
|
kind?: string;
|
|
148
148
|
agentType?: string;
|
|
149
149
|
}
|
|
150
|
-
export interface
|
|
150
|
+
export interface RecentLaunchEntry {
|
|
151
151
|
id: string;
|
|
152
|
-
sessionId?: string | null;
|
|
153
152
|
providerType: string;
|
|
154
153
|
providerName: string;
|
|
155
154
|
kind: 'ide' | 'cli' | 'acp';
|
|
156
|
-
title
|
|
155
|
+
title?: string;
|
|
157
156
|
workspace?: string | null;
|
|
158
157
|
currentModel?: string;
|
|
159
|
-
|
|
160
|
-
lastUsedAt: number;
|
|
158
|
+
lastLaunchedAt: number;
|
|
161
159
|
}
|
|
162
160
|
export interface StatusReportPayload {
|
|
163
161
|
/** Daemon instance ID */
|
|
@@ -188,5 +186,5 @@ export interface StatusReportPayload {
|
|
|
188
186
|
defaultWorkspaceId?: string | null;
|
|
189
187
|
defaultWorkspacePath?: string | null;
|
|
190
188
|
workspaceActivity?: WorkspaceActivity[];
|
|
191
|
-
|
|
189
|
+
recentLaunches?: RecentLaunchEntry[];
|
|
192
190
|
}
|
package/src/shared-types.ts
CHANGED
|
@@ -102,6 +102,10 @@ export interface SessionEntry {
|
|
|
102
102
|
runtimeKey?: string;
|
|
103
103
|
runtimeDisplayName?: string;
|
|
104
104
|
runtimeWorkspaceLabel?: string;
|
|
105
|
+
/** CLI only: active launch mode id (e.g. 'terminal', 'chat') */
|
|
106
|
+
launchMode?: string;
|
|
107
|
+
/** CLI only: output rendering mode derived from launchMode.outputFormat */
|
|
108
|
+
mode?: 'terminal' | 'chat';
|
|
105
109
|
runtimeWriteOwner?: RuntimeWriteOwner | null;
|
|
106
110
|
runtimeAttachedClients?: RuntimeAttachedClient[];
|
|
107
111
|
resume?: ProviderResumeCapability;
|
|
@@ -120,7 +124,6 @@ export interface SessionEntry {
|
|
|
120
124
|
errorMessage?: string;
|
|
121
125
|
errorReason?: _ProviderErrorReason;
|
|
122
126
|
lastUpdated?: number;
|
|
123
|
-
recentKey?: string;
|
|
124
127
|
unread?: boolean;
|
|
125
128
|
lastSeenAt?: number;
|
|
126
129
|
inboxBucket?: RecentSessionBucket;
|
|
@@ -215,22 +218,15 @@ export type { RecentSessionBucket, TerminalBackendStatus } from './shared-types-
|
|
|
215
218
|
import type { RecentSessionBucket } from './shared-types-extra.js';
|
|
216
219
|
import type { TerminalBackendStatus } from './shared-types-extra.js';
|
|
217
220
|
|
|
218
|
-
export interface
|
|
221
|
+
export interface RecentLaunchEntry {
|
|
219
222
|
id: string;
|
|
220
|
-
recentKey: string;
|
|
221
|
-
sessionId?: string | null;
|
|
222
223
|
providerType: string;
|
|
223
224
|
providerName: string;
|
|
224
225
|
kind: 'ide' | 'cli' | 'acp';
|
|
225
|
-
title
|
|
226
|
+
title?: string;
|
|
226
227
|
workspace?: string | null;
|
|
227
228
|
currentModel?: string;
|
|
228
|
-
|
|
229
|
-
lastUsedAt: number;
|
|
230
|
-
unread?: boolean;
|
|
231
|
-
lastSeenAt?: number;
|
|
232
|
-
inboxBucket?: RecentSessionBucket;
|
|
233
|
-
surfaceHidden?: boolean;
|
|
229
|
+
lastLaunchedAt: number;
|
|
234
230
|
}
|
|
235
231
|
|
|
236
232
|
// ─── Status Report Payload (daemon → server) ────────────────────────
|
|
@@ -259,7 +255,7 @@ export interface StatusReportPayload {
|
|
|
259
255
|
workspaces?: WorkspaceEntry[];
|
|
260
256
|
defaultWorkspaceId?: string | null;
|
|
261
257
|
defaultWorkspacePath?: string | null;
|
|
262
|
-
|
|
258
|
+
recentLaunches?: RecentLaunchEntry[];
|
|
263
259
|
terminalBackend?: TerminalBackendStatus;
|
|
264
260
|
/** Available providers (present in StatusSnapshot, optional in raw payload) */
|
|
265
261
|
availableProviders?: AvailableProviderInfo[];
|
package/src/status/builders.ts
CHANGED
|
@@ -265,6 +265,8 @@ function buildCliSession(state: CliProviderState): SessionEntry {
|
|
|
265
265
|
runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
|
|
266
266
|
runtimeWriteOwner: state.runtime?.writeOwner || null,
|
|
267
267
|
runtimeAttachedClients: state.runtime?.attachedClients || [],
|
|
268
|
+
launchMode: state.launchMode,
|
|
269
|
+
mode: state.mode,
|
|
268
270
|
resume: state.resume,
|
|
269
271
|
activeChat,
|
|
270
272
|
capabilities: PTY_SESSION_CAPABILITIES,
|
package/src/status/reporter.ts
CHANGED
|
@@ -203,7 +203,7 @@ export class DaemonStatusReporter {
|
|
|
203
203
|
currentModel: session.currentModel,
|
|
204
204
|
currentPlan: session.currentPlan,
|
|
205
205
|
currentAutoApprove: session.currentAutoApprove,
|
|
206
|
-
|
|
206
|
+
lastUpdated: session.lastUpdated,
|
|
207
207
|
unread: (session as any).unread,
|
|
208
208
|
lastSeenAt: (session as any).lastSeenAt,
|
|
209
209
|
inboxBucket: (session as any).inboxBucket,
|
package/src/status/snapshot.ts
CHANGED
|
@@ -8,17 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
import * as os from 'os';
|
|
10
10
|
import { loadConfig } from '../config/config.js';
|
|
11
|
-
import {
|
|
11
|
+
import { getRecentActivity, getSessionSeenAt, getSessionSeenMarker } from '../config/recent-activity.js';
|
|
12
12
|
import { getWorkspaceState } from '../config/workspaces.js';
|
|
13
13
|
import { getHostMemorySnapshot } from '../system/host-memory.js';
|
|
14
14
|
import { getTerminalBackendRuntimeStatus } from '../cli-adapters/terminal-screen.js';
|
|
15
|
+
import { LOG } from '../logging/logger.js';
|
|
15
16
|
import { buildSessionEntries, isCdpConnected } from './builders.js';
|
|
16
17
|
import type { ProviderState } from '../providers/provider-instance.js';
|
|
17
18
|
import type {
|
|
18
19
|
AvailableProviderInfo,
|
|
19
20
|
DetectedIdeInfo,
|
|
21
|
+
RecentLaunchEntry,
|
|
20
22
|
RecentSessionBucket,
|
|
21
|
-
RecentSessionEntry,
|
|
22
23
|
SessionEntry,
|
|
23
24
|
StatusReportPayload,
|
|
24
25
|
} from '../shared-types.js';
|
|
@@ -53,6 +54,8 @@ export interface StatusSnapshot extends StatusReportPayload {
|
|
|
53
54
|
availableProviders: AvailableProviderInfo[];
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
const READ_DEBUG_ENABLED = process.argv.includes('--dev') || process.env.ADHDEV_READ_DEBUG === '1';
|
|
58
|
+
|
|
56
59
|
function buildDetectedIdeInfos(
|
|
57
60
|
detectedIdes: StatusSnapshotOptions['detectedIdes'],
|
|
58
61
|
cdpManagers: StatusSnapshotOptions['cdpManagers'],
|
|
@@ -104,6 +107,30 @@ function getSessionMessageUpdatedAt(session: {
|
|
|
104
107
|
);
|
|
105
108
|
}
|
|
106
109
|
|
|
110
|
+
export function getSessionCompletionMarker(session: {
|
|
111
|
+
activeChat?: {
|
|
112
|
+
messages?: Array<{
|
|
113
|
+
role?: string;
|
|
114
|
+
id?: string;
|
|
115
|
+
index?: number;
|
|
116
|
+
timestamp?: number | string;
|
|
117
|
+
receivedAt?: number | string;
|
|
118
|
+
createdAt?: number | string;
|
|
119
|
+
_turnKey?: string;
|
|
120
|
+
}> | null
|
|
121
|
+
} | null
|
|
122
|
+
}) {
|
|
123
|
+
const lastMessage = session.activeChat?.messages?.at?.(-1) as any;
|
|
124
|
+
if (!lastMessage) return '';
|
|
125
|
+
const role = typeof lastMessage.role === 'string' ? lastMessage.role : '';
|
|
126
|
+
if (role === 'user' || role === 'human') return '';
|
|
127
|
+
if (typeof lastMessage._turnKey === 'string' && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
|
|
128
|
+
if (typeof lastMessage.id === 'string' && lastMessage.id) return `id:${lastMessage.id}`;
|
|
129
|
+
if (typeof lastMessage.index === 'number' && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
|
|
130
|
+
const timestamp = parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt);
|
|
131
|
+
return timestamp > 0 ? `ts:${timestamp}` : '';
|
|
132
|
+
}
|
|
133
|
+
|
|
107
134
|
function getSessionLastUsedAt(session: {
|
|
108
135
|
activeChat?: {
|
|
109
136
|
messages?: Array<{ timestamp?: number | string; receivedAt?: number | string; createdAt?: number | string }> | null
|
|
@@ -113,7 +140,7 @@ function getSessionLastUsedAt(session: {
|
|
|
113
140
|
return getSessionMessageUpdatedAt(session) || session.lastUpdated || Date.now();
|
|
114
141
|
}
|
|
115
142
|
|
|
116
|
-
function getSessionKind(session: SessionEntry):
|
|
143
|
+
function getSessionKind(session: SessionEntry): RecentLaunchEntry['kind'] {
|
|
117
144
|
return session.transport === 'cdp-page' || session.transport === 'cdp-webview'
|
|
118
145
|
? 'ide'
|
|
119
146
|
: session.transport === 'acp'
|
|
@@ -132,6 +159,8 @@ function getUnreadState(
|
|
|
132
159
|
lastUsedAt: number,
|
|
133
160
|
lastSeenAt: number,
|
|
134
161
|
lastRole: string,
|
|
162
|
+
completionMarker: string,
|
|
163
|
+
seenCompletionMarker: string,
|
|
135
164
|
): { unread: boolean; inboxBucket: RecentSessionBucket } {
|
|
136
165
|
if (status === 'waiting_approval') {
|
|
137
166
|
return { unread: false, inboxBucket: 'needs_attention' };
|
|
@@ -139,88 +168,27 @@ function getUnreadState(
|
|
|
139
168
|
if (status === 'generating' || status === 'starting') {
|
|
140
169
|
return { unread: false, inboxBucket: 'working' };
|
|
141
170
|
}
|
|
142
|
-
const unread =
|
|
171
|
+
const unread = completionMarker
|
|
172
|
+
? completionMarker !== seenCompletionMarker
|
|
173
|
+
: hasContentChange && lastUsedAt > lastSeenAt && lastRole !== 'user' && lastRole !== 'human';
|
|
143
174
|
return { unread, inboxBucket: unread ? 'task_complete' : 'idle' };
|
|
144
175
|
}
|
|
145
176
|
|
|
146
|
-
function
|
|
147
|
-
sessions: ReturnType<typeof buildSessionEntries>,
|
|
177
|
+
function buildRecentLaunches(
|
|
148
178
|
recentActivity: ReturnType<typeof getRecentActivity>,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const lastSeenAt = readState[recentKey] || 0;
|
|
163
|
-
const lastUsedAt = getSessionLastUsedAt(session);
|
|
164
|
-
const { unread, inboxBucket } = getUnreadState(
|
|
165
|
-
getSessionMessageUpdatedAt(session) > 0,
|
|
166
|
-
session.status,
|
|
167
|
-
lastUsedAt,
|
|
168
|
-
lastSeenAt,
|
|
169
|
-
getLastMessageRole(session),
|
|
170
|
-
);
|
|
171
|
-
return {
|
|
172
|
-
id: session.id,
|
|
173
|
-
recentKey,
|
|
174
|
-
sessionId: session.id,
|
|
175
|
-
providerType: session.providerType,
|
|
176
|
-
providerName: session.providerName,
|
|
177
|
-
kind,
|
|
178
|
-
title: session.activeChat?.title || session.title || session.providerName,
|
|
179
|
-
workspace: session.workspace,
|
|
180
|
-
currentModel: session.currentModel,
|
|
181
|
-
status: session.status,
|
|
182
|
-
lastUsedAt,
|
|
183
|
-
unread,
|
|
184
|
-
lastSeenAt,
|
|
185
|
-
inboxBucket,
|
|
186
|
-
surfaceHidden: false,
|
|
187
|
-
};
|
|
188
|
-
});
|
|
189
|
-
for (const item of live) {
|
|
190
|
-
visibleKeys.add(`${item.kind}:${item.providerType}:${item.workspace || ''}`);
|
|
191
|
-
}
|
|
192
|
-
for (const session of sessions) {
|
|
193
|
-
if (!session.surfaceHidden) continue;
|
|
194
|
-
hiddenKeys.add(`${getSessionKind(session)}:${session.providerType}:${session.workspace || ''}`);
|
|
195
|
-
}
|
|
196
|
-
const persisted = recentActivity
|
|
197
|
-
.filter((item) => {
|
|
198
|
-
const key = `${item.kind}:${item.providerType}:${item.workspace || ''}`;
|
|
199
|
-
return !visibleKeys.has(key) && !hiddenKeys.has(key);
|
|
200
|
-
})
|
|
201
|
-
.map((item) => {
|
|
202
|
-
const lastSeenAt = readState[item.id] || 0;
|
|
203
|
-
const unread = item.lastUsedAt > lastSeenAt;
|
|
204
|
-
return {
|
|
205
|
-
id: item.id,
|
|
206
|
-
recentKey: item.id,
|
|
207
|
-
sessionId: item.sessionId || null,
|
|
208
|
-
providerType: item.providerType,
|
|
209
|
-
providerName: item.providerName,
|
|
210
|
-
kind: item.kind,
|
|
211
|
-
title: item.title || item.providerName,
|
|
212
|
-
workspace: item.workspace,
|
|
213
|
-
currentModel: item.currentModel,
|
|
214
|
-
lastUsedAt: item.lastUsedAt,
|
|
215
|
-
unread,
|
|
216
|
-
lastSeenAt,
|
|
217
|
-
inboxBucket: unread ? 'task_complete' : 'idle' as RecentSessionBucket,
|
|
218
|
-
surfaceHidden: false,
|
|
219
|
-
};
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
return [...live, ...persisted]
|
|
223
|
-
.sort((a, b) => b.lastUsedAt - a.lastUsedAt)
|
|
179
|
+
): RecentLaunchEntry[] {
|
|
180
|
+
return recentActivity
|
|
181
|
+
.map((item) => ({
|
|
182
|
+
id: item.id,
|
|
183
|
+
providerType: item.providerType,
|
|
184
|
+
providerName: item.providerName,
|
|
185
|
+
kind: item.kind,
|
|
186
|
+
title: item.title || item.providerName,
|
|
187
|
+
workspace: item.workspace,
|
|
188
|
+
currentModel: item.currentModel,
|
|
189
|
+
lastLaunchedAt: item.lastUsedAt,
|
|
190
|
+
}))
|
|
191
|
+
.sort((a, b) => b.lastLaunchedAt - a.lastLaunchedAt)
|
|
224
192
|
.slice(0, 12);
|
|
225
193
|
}
|
|
226
194
|
|
|
@@ -233,16 +201,11 @@ export function buildStatusSnapshot(options: StatusSnapshotOptions): StatusSnaps
|
|
|
233
201
|
options.allStates,
|
|
234
202
|
options.cdpManagers as Map<string, any>,
|
|
235
203
|
);
|
|
236
|
-
const readState = cfg.recentSessionReads || {};
|
|
237
204
|
for (const session of sessions) {
|
|
238
|
-
const
|
|
239
|
-
const
|
|
240
|
-
kind,
|
|
241
|
-
providerType: session.providerType,
|
|
242
|
-
workspace: session.workspace,
|
|
243
|
-
});
|
|
244
|
-
const lastSeenAt = getRecentSessionSeenAt(cfg, recentKey);
|
|
205
|
+
const lastSeenAt = getSessionSeenAt(cfg, session.id);
|
|
206
|
+
const seenCompletionMarker = getSessionSeenMarker(cfg, session.id);
|
|
245
207
|
const lastUsedAt = getSessionLastUsedAt(session);
|
|
208
|
+
const completionMarker = getSessionCompletionMarker(session);
|
|
246
209
|
const { unread, inboxBucket } = session.surfaceHidden
|
|
247
210
|
? { unread: false, inboxBucket: 'idle' as RecentSessionBucket }
|
|
248
211
|
: getUnreadState(
|
|
@@ -251,11 +214,18 @@ export function buildStatusSnapshot(options: StatusSnapshotOptions): StatusSnaps
|
|
|
251
214
|
lastUsedAt,
|
|
252
215
|
lastSeenAt,
|
|
253
216
|
getLastMessageRole(session),
|
|
217
|
+
completionMarker,
|
|
218
|
+
seenCompletionMarker,
|
|
254
219
|
);
|
|
255
|
-
session.recentKey = recentKey;
|
|
256
220
|
session.lastSeenAt = lastSeenAt;
|
|
257
221
|
session.unread = unread;
|
|
258
222
|
session.inboxBucket = inboxBucket;
|
|
223
|
+
if (READ_DEBUG_ENABLED && (session.unread || session.inboxBucket !== 'idle' || session.providerType.includes('codex'))) {
|
|
224
|
+
LOG.info(
|
|
225
|
+
'RecentRead',
|
|
226
|
+
`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)}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
259
229
|
}
|
|
260
230
|
const terminalBackend = getTerminalBackendRuntimeStatus();
|
|
261
231
|
|
|
@@ -283,7 +253,7 @@ export function buildStatusSnapshot(options: StatusSnapshotOptions): StatusSnaps
|
|
|
283
253
|
workspaces: wsState.workspaces,
|
|
284
254
|
defaultWorkspaceId: wsState.defaultWorkspaceId,
|
|
285
255
|
defaultWorkspacePath: wsState.defaultWorkspacePath,
|
|
286
|
-
|
|
256
|
+
recentLaunches: buildRecentLaunches(recentActivity),
|
|
287
257
|
terminalBackend,
|
|
288
258
|
availableProviders: buildAvailableProviders(options.providerLoader),
|
|
289
259
|
};
|