@adhdev/daemon-core 0.8.35 → 0.8.36
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/boot/daemon-lifecycle.d.ts +4 -0
- package/dist/commands/router.d.ts +3 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +458 -154
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +457 -154
- package/dist/index.mjs.map +1 -1
- package/dist/shared-types.d.ts +248 -16
- package/dist/status/builders.d.ts +5 -1
- package/dist/status/normalize.d.ts +11 -1
- package/dist/status/normalize.js +36 -16
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +35 -16
- package/dist/status/normalize.mjs.map +1 -1
- package/dist/status/reporter.d.ts +4 -0
- package/dist/status/snapshot.d.ts +5 -5
- package/dist/types.d.ts +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/boot/daemon-lifecycle.ts +7 -0
- package/src/commands/chat-commands.ts +192 -17
- package/src/commands/router.ts +25 -0
- package/src/commands/stream-commands.ts +14 -10
- package/src/config/config.ts +8 -0
- package/src/index.d.ts +2 -2
- package/src/index.ts +32 -1
- package/src/shared-types.d.ts +125 -16
- package/src/shared-types.ts +279 -16
- package/src/status/builders.ts +110 -54
- package/src/status/normalize.ts +54 -19
- package/src/status/reporter.ts +88 -13
- package/src/status/snapshot.ts +79 -41
- package/src/types.ts +1 -1
package/src/status/builders.ts
CHANGED
|
@@ -17,7 +17,35 @@ import type {
|
|
|
17
17
|
ExtensionProviderState,
|
|
18
18
|
ProviderState,
|
|
19
19
|
} from '../providers/provider-instance.js';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
LIVE_STATUS_ACTIVE_CHAT_OPTIONS,
|
|
22
|
+
normalizeActiveChatData,
|
|
23
|
+
normalizeManagedStatus,
|
|
24
|
+
type NormalizeActiveChatOptions,
|
|
25
|
+
} from './normalize.js';
|
|
26
|
+
|
|
27
|
+
export type SessionEntryProfile = 'full' | 'live' | 'metadata';
|
|
28
|
+
|
|
29
|
+
export interface SessionEntryBuildOptions {
|
|
30
|
+
profile?: SessionEntryProfile;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getActiveChatOptions(profile: SessionEntryProfile): NormalizeActiveChatOptions {
|
|
34
|
+
if (profile === 'full') return {};
|
|
35
|
+
return LIVE_STATUS_ACTIVE_CHAT_OPTIONS;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function shouldIncludeSessionControls(profile: SessionEntryProfile): boolean {
|
|
39
|
+
return profile !== 'live';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function shouldIncludeSessionMetadata(profile: SessionEntryProfile): boolean {
|
|
43
|
+
return profile !== 'live';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function shouldIncludeRuntimeMetadata(profile: SessionEntryProfile): boolean {
|
|
47
|
+
return profile !== 'live';
|
|
48
|
+
}
|
|
21
49
|
|
|
22
50
|
// ─── CDP Manager lookup helpers ──────────────────────
|
|
23
51
|
|
|
@@ -186,33 +214,39 @@ const ACP_SESSION_CAPABILITIES: SessionCapability[] = [
|
|
|
186
214
|
function buildIdeWorkspaceSession(
|
|
187
215
|
state: IdeProviderState,
|
|
188
216
|
cdpManagers: Map<string, DaemonCdpManager>,
|
|
217
|
+
options: SessionEntryBuildOptions,
|
|
189
218
|
): SessionEntry {
|
|
190
|
-
const
|
|
219
|
+
const profile = options.profile || 'full';
|
|
220
|
+
const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
|
|
221
|
+
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
222
|
+
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
191
223
|
const title = activeChat?.title || state.name;
|
|
192
224
|
return {
|
|
193
225
|
id: state.instanceId || state.type,
|
|
194
226
|
parentId: null,
|
|
195
227
|
providerType: state.type,
|
|
196
|
-
providerName: state.name,
|
|
228
|
+
...(includeSessionMetadata && { providerName: state.name }),
|
|
197
229
|
kind: 'workspace',
|
|
198
230
|
transport: 'cdp-page',
|
|
199
231
|
status: normalizeManagedStatus(activeChat?.status || state.status, {
|
|
200
232
|
activeModal: activeChat?.activeModal || null,
|
|
201
233
|
}),
|
|
202
234
|
title,
|
|
203
|
-
workspace: state.workspace || null,
|
|
235
|
+
...(includeSessionMetadata && { workspace: state.workspace || null }),
|
|
204
236
|
activeChat,
|
|
205
|
-
capabilities: IDE_SESSION_CAPABILITIES,
|
|
237
|
+
...(includeSessionMetadata && { capabilities: IDE_SESSION_CAPABILITIES }),
|
|
206
238
|
cdpConnected: state.cdpConnected ?? isCdpConnected(cdpManagers, state.type),
|
|
207
239
|
currentModel: state.currentModel,
|
|
208
240
|
currentPlan: state.currentPlan,
|
|
209
241
|
currentAutoApprove: state.currentAutoApprove,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
242
|
+
...(includeSessionControls && {
|
|
243
|
+
controlValues: state.controlValues,
|
|
244
|
+
providerControls: buildFallbackControls(
|
|
245
|
+
state.providerControls,
|
|
246
|
+
state.currentModel,
|
|
247
|
+
state.currentPlan
|
|
248
|
+
),
|
|
249
|
+
}),
|
|
216
250
|
errorMessage: state.errorMessage,
|
|
217
251
|
errorReason: state.errorReason,
|
|
218
252
|
lastUpdated: state.lastUpdated,
|
|
@@ -222,43 +256,53 @@ function buildIdeWorkspaceSession(
|
|
|
222
256
|
function buildExtensionAgentSession(
|
|
223
257
|
parent: IdeProviderState,
|
|
224
258
|
ext: ExtensionProviderState,
|
|
259
|
+
options: SessionEntryBuildOptions,
|
|
225
260
|
): SessionEntry {
|
|
226
|
-
const
|
|
261
|
+
const profile = options.profile || 'full';
|
|
262
|
+
const activeChat = normalizeActiveChatData(ext.activeChat, getActiveChatOptions(profile));
|
|
263
|
+
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
264
|
+
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
227
265
|
return {
|
|
228
266
|
id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
|
|
229
267
|
parentId: parent.instanceId || parent.type,
|
|
230
268
|
providerType: ext.type,
|
|
231
|
-
providerName: ext.name,
|
|
269
|
+
...(includeSessionMetadata && { providerName: ext.name }),
|
|
232
270
|
kind: 'agent',
|
|
233
271
|
transport: 'cdp-webview',
|
|
234
272
|
status: normalizeManagedStatus(activeChat?.status || ext.status, {
|
|
235
273
|
activeModal: activeChat?.activeModal || null,
|
|
236
274
|
}),
|
|
237
275
|
title: activeChat?.title || ext.name,
|
|
238
|
-
workspace: parent.workspace || null,
|
|
276
|
+
...(includeSessionMetadata && { workspace: parent.workspace || null }),
|
|
239
277
|
activeChat,
|
|
240
|
-
capabilities: EXTENSION_SESSION_CAPABILITIES,
|
|
278
|
+
...(includeSessionMetadata && { capabilities: EXTENSION_SESSION_CAPABILITIES }),
|
|
241
279
|
currentModel: ext.currentModel,
|
|
242
280
|
currentPlan: ext.currentPlan,
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
281
|
+
...(includeSessionControls && {
|
|
282
|
+
controlValues: ext.controlValues,
|
|
283
|
+
providerControls: buildFallbackControls(
|
|
284
|
+
ext.providerControls,
|
|
285
|
+
ext.currentModel,
|
|
286
|
+
ext.currentPlan
|
|
287
|
+
),
|
|
288
|
+
}),
|
|
249
289
|
errorMessage: ext.errorMessage,
|
|
250
290
|
errorReason: ext.errorReason,
|
|
251
291
|
lastUpdated: ext.lastUpdated,
|
|
252
292
|
};
|
|
253
293
|
}
|
|
254
294
|
|
|
255
|
-
function buildCliSession(state: CliProviderState): SessionEntry {
|
|
256
|
-
const
|
|
295
|
+
function buildCliSession(state: CliProviderState, options: SessionEntryBuildOptions): SessionEntry {
|
|
296
|
+
const profile = options.profile || 'full';
|
|
297
|
+
const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
|
|
298
|
+
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
299
|
+
const includeRuntimeMetadata = shouldIncludeRuntimeMetadata(profile);
|
|
300
|
+
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
257
301
|
return {
|
|
258
302
|
id: state.instanceId,
|
|
259
303
|
parentId: null,
|
|
260
304
|
providerType: state.type,
|
|
261
|
-
providerName: state.name,
|
|
305
|
+
...(includeSessionMetadata && { providerName: state.name }),
|
|
262
306
|
providerSessionId: state.providerSessionId,
|
|
263
307
|
kind: 'agent',
|
|
264
308
|
transport: 'pty',
|
|
@@ -266,54 +310,65 @@ function buildCliSession(state: CliProviderState): SessionEntry {
|
|
|
266
310
|
activeModal: activeChat?.activeModal || null,
|
|
267
311
|
}),
|
|
268
312
|
title: activeChat?.title || state.name,
|
|
269
|
-
workspace: state.workspace || null,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
313
|
+
...(includeSessionMetadata && { workspace: state.workspace || null }),
|
|
314
|
+
...(includeRuntimeMetadata && {
|
|
315
|
+
runtimeKey: state.runtime?.runtimeKey,
|
|
316
|
+
runtimeDisplayName: state.runtime?.displayName,
|
|
317
|
+
runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
|
|
318
|
+
runtimeWriteOwner: state.runtime?.writeOwner || null,
|
|
319
|
+
runtimeAttachedClients: state.runtime?.attachedClients || [],
|
|
320
|
+
}),
|
|
275
321
|
mode: state.mode,
|
|
276
322
|
resume: state.resume,
|
|
277
323
|
activeChat,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
324
|
+
...(includeSessionMetadata && {
|
|
325
|
+
capabilities: state.mode === 'terminal' ? PTY_SESSION_CAPABILITIES : CLI_CHAT_SESSION_CAPABILITIES,
|
|
326
|
+
}),
|
|
327
|
+
...(includeSessionControls && {
|
|
328
|
+
controlValues: state.controlValues,
|
|
329
|
+
providerControls: buildFallbackControls(
|
|
330
|
+
state.providerControls
|
|
331
|
+
),
|
|
332
|
+
}),
|
|
283
333
|
errorMessage: state.errorMessage,
|
|
284
334
|
errorReason: state.errorReason,
|
|
285
335
|
lastUpdated: state.lastUpdated,
|
|
286
336
|
};
|
|
287
337
|
}
|
|
288
338
|
|
|
289
|
-
function buildAcpSession(state: AcpProviderState): SessionEntry {
|
|
290
|
-
const
|
|
339
|
+
function buildAcpSession(state: AcpProviderState, options: SessionEntryBuildOptions): SessionEntry {
|
|
340
|
+
const profile = options.profile || 'full';
|
|
341
|
+
const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
|
|
342
|
+
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
343
|
+
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
291
344
|
return {
|
|
292
345
|
id: state.instanceId,
|
|
293
346
|
parentId: null,
|
|
294
347
|
providerType: state.type,
|
|
295
|
-
providerName: state.name,
|
|
348
|
+
...(includeSessionMetadata && { providerName: state.name }),
|
|
296
349
|
kind: 'agent',
|
|
297
350
|
transport: 'acp',
|
|
298
351
|
status: normalizeManagedStatus(activeChat?.status || state.status, {
|
|
299
352
|
activeModal: activeChat?.activeModal || null,
|
|
300
353
|
}),
|
|
301
354
|
title: activeChat?.title || state.name,
|
|
302
|
-
workspace: state.workspace || null,
|
|
355
|
+
...(includeSessionMetadata && { workspace: state.workspace || null }),
|
|
303
356
|
activeChat,
|
|
304
|
-
capabilities: ACP_SESSION_CAPABILITIES,
|
|
357
|
+
...(includeSessionMetadata && { capabilities: ACP_SESSION_CAPABILITIES }),
|
|
305
358
|
currentModel: state.currentModel,
|
|
306
359
|
currentPlan: state.currentPlan,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
360
|
+
...(includeSessionControls && {
|
|
361
|
+
acpConfigOptions: state.acpConfigOptions,
|
|
362
|
+
acpModes: state.acpModes,
|
|
363
|
+
controlValues: state.controlValues,
|
|
364
|
+
providerControls: buildFallbackControls(
|
|
365
|
+
state.providerControls,
|
|
366
|
+
state.currentModel,
|
|
367
|
+
state.currentPlan,
|
|
368
|
+
state.acpConfigOptions,
|
|
369
|
+
state.acpModes
|
|
370
|
+
),
|
|
371
|
+
}),
|
|
317
372
|
errorMessage: state.errorMessage,
|
|
318
373
|
errorReason: state.errorReason,
|
|
319
374
|
lastUpdated: state.lastUpdated,
|
|
@@ -323,6 +378,7 @@ function buildAcpSession(state: AcpProviderState): SessionEntry {
|
|
|
323
378
|
export function buildSessionEntries(
|
|
324
379
|
allStates: ProviderState[],
|
|
325
380
|
cdpManagers: Map<string, DaemonCdpManager>,
|
|
381
|
+
options: SessionEntryBuildOptions = {},
|
|
326
382
|
): SessionEntry[] {
|
|
327
383
|
const sessions: SessionEntry[] = [];
|
|
328
384
|
|
|
@@ -331,18 +387,18 @@ export function buildSessionEntries(
|
|
|
331
387
|
const acpStates = allStates.filter((s): s is AcpProviderState => s.category === 'acp');
|
|
332
388
|
|
|
333
389
|
for (const state of ideStates) {
|
|
334
|
-
sessions.push(buildIdeWorkspaceSession(state, cdpManagers));
|
|
390
|
+
sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
|
|
335
391
|
for (const ext of state.extensions as ExtensionProviderState[]) {
|
|
336
|
-
sessions.push(buildExtensionAgentSession(state, ext));
|
|
392
|
+
sessions.push(buildExtensionAgentSession(state, ext, options));
|
|
337
393
|
}
|
|
338
394
|
}
|
|
339
395
|
|
|
340
396
|
for (const state of cliStates) {
|
|
341
|
-
sessions.push(buildCliSession(state));
|
|
397
|
+
sessions.push(buildCliSession(state, options));
|
|
342
398
|
}
|
|
343
399
|
|
|
344
400
|
for (const state of acpStates) {
|
|
345
|
-
sessions.push(buildAcpSession(state));
|
|
401
|
+
sessions.push(buildAcpSession(state, options));
|
|
346
402
|
}
|
|
347
403
|
|
|
348
404
|
// Hide native IDE parent rows from inbox/recent surfaces when extension tabs exist.
|
package/src/status/normalize.ts
CHANGED
|
@@ -20,13 +20,39 @@ const WORKING_STATUSES = new Set([
|
|
|
20
20
|
'active',
|
|
21
21
|
]);
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
export interface NormalizeActiveChatOptions {
|
|
24
|
+
includeMessages?: boolean;
|
|
25
|
+
includeInputContent?: boolean;
|
|
26
|
+
includeActiveModal?: boolean;
|
|
27
|
+
messageLimit?: number;
|
|
28
|
+
totalBytesLimit?: number;
|
|
29
|
+
stringLimit?: number;
|
|
30
|
+
fallbackStringLimit?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Full snapshots are still capped, but can carry recent chat context for API/inspection use.
|
|
34
|
+
const FULL_STATUS_ACTIVE_CHAT_OPTIONS: Required<NormalizeActiveChatOptions> = {
|
|
35
|
+
includeMessages: true,
|
|
36
|
+
includeInputContent: true,
|
|
37
|
+
includeActiveModal: true,
|
|
38
|
+
messageLimit: 60,
|
|
39
|
+
totalBytesLimit: 96 * 1024,
|
|
40
|
+
stringLimit: 4 * 1024,
|
|
41
|
+
fallbackStringLimit: 1024,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Live/metadata snapshots only need routing + UI summary. Current chat text is loaded
|
|
45
|
+
// on demand via `read_chat`, and older history via `chat_history`.
|
|
46
|
+
export const LIVE_STATUS_ACTIVE_CHAT_OPTIONS: Required<NormalizeActiveChatOptions> = {
|
|
47
|
+
includeMessages: false,
|
|
48
|
+
includeInputContent: false,
|
|
49
|
+
includeActiveModal: false,
|
|
50
|
+
messageLimit: 0,
|
|
51
|
+
totalBytesLimit: 0,
|
|
52
|
+
stringLimit: 512,
|
|
53
|
+
fallbackStringLimit: 256,
|
|
54
|
+
};
|
|
55
|
+
|
|
30
56
|
const STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;
|
|
31
57
|
const STATUS_MODAL_BUTTON_LIMIT = 120;
|
|
32
58
|
|
|
@@ -81,23 +107,27 @@ function normalizeMessageTime(message: unknown): unknown {
|
|
|
81
107
|
return msg;
|
|
82
108
|
}
|
|
83
109
|
|
|
84
|
-
function trimMessagesForStatus(
|
|
110
|
+
function trimMessagesForStatus(
|
|
111
|
+
messages: unknown[] | null | undefined,
|
|
112
|
+
options: Required<NormalizeActiveChatOptions>,
|
|
113
|
+
): unknown[] {
|
|
114
|
+
if (!options.includeMessages || options.messageLimit <= 0 || options.totalBytesLimit <= 0) return [];
|
|
85
115
|
if (!Array.isArray(messages) || messages.length === 0) return [];
|
|
86
116
|
|
|
87
|
-
const recent = messages.slice(-
|
|
117
|
+
const recent = messages.slice(-options.messageLimit);
|
|
88
118
|
const kept: unknown[] = [];
|
|
89
119
|
let totalBytes = 0;
|
|
90
120
|
|
|
91
121
|
for (let i = recent.length - 1; i >= 0; i -= 1) {
|
|
92
|
-
let normalized = normalizeMessageTime(trimMessageForStatus(recent[i],
|
|
122
|
+
let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.stringLimit));
|
|
93
123
|
let size = estimateBytes(normalized);
|
|
94
124
|
|
|
95
|
-
if (size >
|
|
96
|
-
normalized = normalizeMessageTime(trimMessageForStatus(recent[i],
|
|
125
|
+
if (size > options.totalBytesLimit) {
|
|
126
|
+
normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.fallbackStringLimit));
|
|
97
127
|
size = estimateBytes(normalized);
|
|
98
128
|
}
|
|
99
129
|
|
|
100
|
-
if (kept.length > 0 && (totalBytes + size) >
|
|
130
|
+
if (kept.length > 0 && (totalBytes + size) > options.totalBytesLimit) {
|
|
101
131
|
continue;
|
|
102
132
|
}
|
|
103
133
|
|
|
@@ -143,20 +173,25 @@ export function isManagedStatusWaiting(
|
|
|
143
173
|
|
|
144
174
|
export function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(
|
|
145
175
|
activeChat: T,
|
|
176
|
+
options: NormalizeActiveChatOptions = FULL_STATUS_ACTIVE_CHAT_OPTIONS,
|
|
146
177
|
): T {
|
|
147
178
|
if (!activeChat) return activeChat;
|
|
179
|
+
const resolvedOptions: Required<NormalizeActiveChatOptions> = {
|
|
180
|
+
...FULL_STATUS_ACTIVE_CHAT_OPTIONS,
|
|
181
|
+
...options,
|
|
182
|
+
};
|
|
148
183
|
return {
|
|
149
184
|
...activeChat,
|
|
150
185
|
status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),
|
|
151
|
-
messages: trimMessagesForStatus(activeChat.messages) as T extends { messages: infer M } ? M : never,
|
|
152
|
-
activeModal: activeChat.activeModal ? {
|
|
186
|
+
messages: trimMessagesForStatus(activeChat.messages, resolvedOptions) as T extends { messages: infer M } ? M : never,
|
|
187
|
+
activeModal: resolvedOptions.includeActiveModal && activeChat.activeModal ? {
|
|
153
188
|
message: truncateString(activeChat.activeModal.message || '', STATUS_MODAL_MESSAGE_LIMIT),
|
|
154
189
|
buttons: (activeChat.activeModal.buttons || []).map((button) =>
|
|
155
190
|
truncateString(String(button || ''), STATUS_MODAL_BUTTON_LIMIT)
|
|
156
191
|
),
|
|
157
|
-
} :
|
|
158
|
-
inputContent: activeChat.inputContent
|
|
159
|
-
? truncateString(activeChat.inputContent,
|
|
160
|
-
:
|
|
192
|
+
} : null,
|
|
193
|
+
inputContent: resolvedOptions.includeInputContent && activeChat.inputContent
|
|
194
|
+
? truncateString(activeChat.inputContent, 2 * 1024)
|
|
195
|
+
: undefined,
|
|
161
196
|
} as T;
|
|
162
197
|
}
|
package/src/status/reporter.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { LOG } from '../logging/logger.js';
|
|
9
9
|
import type { DaemonCdpManager } from '../cdp/manager.js';
|
|
10
10
|
import type { MachineInfo } from '../shared-types.js';
|
|
11
|
+
import type { CloudStatusReportPayload, DaemonStatusEventPayload } from '../shared-types.js';
|
|
11
12
|
import { buildSessionEntries } from './builders.js';
|
|
12
13
|
import { buildStatusSnapshot } from './snapshot.js';
|
|
13
14
|
import type {
|
|
@@ -22,7 +23,15 @@ import type {
|
|
|
22
23
|
export interface StatusReporterDeps {
|
|
23
24
|
serverConn: { isConnected(): boolean; sendMessage(type: string, data: any): void; getUserPlan(): string } | null;
|
|
24
25
|
cdpManagers: Map<string, DaemonCdpManager>;
|
|
25
|
-
p2p: {
|
|
26
|
+
p2p: {
|
|
27
|
+
isConnected: boolean;
|
|
28
|
+
isAvailable: boolean;
|
|
29
|
+
connectionState: string;
|
|
30
|
+
connectedPeerCount: number;
|
|
31
|
+
screenshotActive: boolean;
|
|
32
|
+
sendStatus(data: any): void;
|
|
33
|
+
sendStatusEvent(event: DaemonStatusEventPayload): void;
|
|
34
|
+
} | null;
|
|
26
35
|
providerLoader: { resolve(type: string): any; getAll(): any[] };
|
|
27
36
|
detectedIdes: any[];
|
|
28
37
|
instanceId: string;
|
|
@@ -94,10 +103,73 @@ export class DaemonStatusReporter {
|
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
|
|
106
|
+
private toDaemonStatusEventName(value: unknown): DaemonStatusEventPayload['event'] | null {
|
|
107
|
+
switch (value) {
|
|
108
|
+
case 'agent:generating_started':
|
|
109
|
+
case 'agent:waiting_approval':
|
|
110
|
+
case 'agent:generating_completed':
|
|
111
|
+
case 'agent:stopped':
|
|
112
|
+
case 'monitor:long_generating':
|
|
113
|
+
return value;
|
|
114
|
+
default:
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private buildServerStatusEvent(event: Record<string, unknown>): DaemonStatusEventPayload | null {
|
|
120
|
+
const eventName = this.toDaemonStatusEventName(event.event);
|
|
121
|
+
if (!eventName) return null;
|
|
122
|
+
|
|
123
|
+
// Provider UI effects can carry arbitrary text content and are not required
|
|
124
|
+
// for server-side routing, push, or dashboard session targeting.
|
|
125
|
+
if (eventName.startsWith('provider:')) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const payload: DaemonStatusEventPayload = {
|
|
130
|
+
event: eventName,
|
|
131
|
+
timestamp: typeof event.timestamp === 'number' && Number.isFinite(event.timestamp)
|
|
132
|
+
? event.timestamp
|
|
133
|
+
: Date.now(),
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (typeof event.targetSessionId === 'string' && event.targetSessionId.trim()) {
|
|
137
|
+
payload.targetSessionId = event.targetSessionId.trim();
|
|
138
|
+
}
|
|
139
|
+
const providerType = typeof event.providerType === 'string' && event.providerType.trim()
|
|
140
|
+
? event.providerType.trim()
|
|
141
|
+
: (typeof event.ideType === 'string' && event.ideType.trim() ? event.ideType.trim() : '');
|
|
142
|
+
if (providerType) {
|
|
143
|
+
payload.providerType = providerType;
|
|
144
|
+
}
|
|
145
|
+
if (typeof event.duration === 'number' && Number.isFinite(event.duration)) {
|
|
146
|
+
payload.duration = event.duration;
|
|
147
|
+
}
|
|
148
|
+
if (typeof event.elapsedSec === 'number' && Number.isFinite(event.elapsedSec)) {
|
|
149
|
+
payload.elapsedSec = event.elapsedSec;
|
|
150
|
+
}
|
|
151
|
+
if (typeof event.modalMessage === 'string' && event.modalMessage.trim()) {
|
|
152
|
+
payload.modalMessage = event.modalMessage;
|
|
153
|
+
}
|
|
154
|
+
if (Array.isArray(event.modalButtons)) {
|
|
155
|
+
const modalButtons = event.modalButtons
|
|
156
|
+
.filter((button): button is string => typeof button === 'string' && button.trim().length > 0);
|
|
157
|
+
if (modalButtons.length > 0) {
|
|
158
|
+
payload.modalButtons = modalButtons;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return payload;
|
|
163
|
+
}
|
|
164
|
+
|
|
97
165
|
emitStatusEvent(event: Record<string, unknown>): void {
|
|
98
166
|
LOG.info('StatusEvent', `${event.event} (${event.providerType || event.ideType || ''})`);
|
|
99
|
-
|
|
100
|
-
|
|
167
|
+
const serverEvent = this.buildServerStatusEvent(event);
|
|
168
|
+
if (!serverEvent) return;
|
|
169
|
+
// Dashboard delivery is P2P-only, but the server still receives the event
|
|
170
|
+
// for push notifications, webhook dispatch, and audit-side effects.
|
|
171
|
+
this.deps.p2p?.sendStatusEvent(serverEvent);
|
|
172
|
+
this.deps.serverConn?.sendMessage('status_event', serverEvent);
|
|
101
173
|
}
|
|
102
174
|
|
|
103
175
|
removeAgentTracking(_key: string): void { /* Managed by Instance itself */ }
|
|
@@ -189,7 +261,6 @@ export class DaemonStatusReporter {
|
|
|
189
261
|
detectedIdes: this.deps.detectedIdes || [],
|
|
190
262
|
instanceId: this.deps.instanceId,
|
|
191
263
|
version: this.deps.daemonVersion || 'unknown',
|
|
192
|
-
daemonMode: true,
|
|
193
264
|
timestamp: now,
|
|
194
265
|
p2p: {
|
|
195
266
|
available: p2p?.isAvailable || false,
|
|
@@ -197,9 +268,9 @@ export class DaemonStatusReporter {
|
|
|
197
268
|
peers: p2p?.connectedPeerCount || 0,
|
|
198
269
|
screenshotActive: p2p?.screenshotActive || false,
|
|
199
270
|
},
|
|
271
|
+
profile: 'live',
|
|
200
272
|
}),
|
|
201
273
|
screenshotUsage: this.deps.getScreenshotUsage?.() || null,
|
|
202
|
-
connectedExtensions: [],
|
|
203
274
|
};
|
|
204
275
|
|
|
205
276
|
// ═══ P2P transmit ═══
|
|
@@ -219,17 +290,16 @@ export class DaemonStatusReporter {
|
|
|
219
290
|
if (opts?.p2pOnly) return;
|
|
220
291
|
// Server relay only needs compact session metadata for routing, compact status,
|
|
221
292
|
// initial_state fallback, and lightweight API/session inspection.
|
|
222
|
-
const wsPayload = {
|
|
223
|
-
daemonMode: true,
|
|
293
|
+
const wsPayload: CloudStatusReportPayload = {
|
|
224
294
|
sessions: sessions.map((session) => ({
|
|
225
295
|
id: session.id,
|
|
226
296
|
parentId: session.parentId,
|
|
227
297
|
providerType: session.providerType,
|
|
228
|
-
providerName: session.providerName,
|
|
298
|
+
providerName: session.providerName || session.providerType,
|
|
229
299
|
kind: session.kind,
|
|
230
300
|
transport: session.transport,
|
|
231
301
|
status: session.status,
|
|
232
|
-
workspace: session.workspace,
|
|
302
|
+
workspace: session.workspace ?? null,
|
|
233
303
|
title: session.title,
|
|
234
304
|
cdpConnected: session.cdpConnected,
|
|
235
305
|
currentModel: session.currentModel,
|
|
@@ -238,8 +308,6 @@ export class DaemonStatusReporter {
|
|
|
238
308
|
})),
|
|
239
309
|
p2p: payload.p2p,
|
|
240
310
|
timestamp: now,
|
|
241
|
-
detectedIdes: payload.detectedIdes,
|
|
242
|
-
availableProviders: payload.availableProviders,
|
|
243
311
|
};
|
|
244
312
|
const wsHash = this.simpleHash(JSON.stringify({
|
|
245
313
|
...wsPayload,
|
|
@@ -258,12 +326,19 @@ export class DaemonStatusReporter {
|
|
|
258
326
|
|
|
259
327
|
private sendP2PPayload(payload: { timestamp?: number; system?: unknown; machine?: MachineInfo; [key: string]: unknown }): boolean {
|
|
260
328
|
const { timestamp: _ts, system: _sys, ...hashTarget } = payload;
|
|
329
|
+
const sessions = Array.isArray(hashTarget.sessions)
|
|
330
|
+
? hashTarget.sessions.map((session) => {
|
|
331
|
+
if (!session || typeof session !== 'object') return session;
|
|
332
|
+
const { lastUpdated: _lu, ...stableSession } = session as Record<string, unknown>;
|
|
333
|
+
return stableSession;
|
|
334
|
+
})
|
|
335
|
+
: hashTarget.sessions;
|
|
261
336
|
const hashPayload = hashTarget.machine
|
|
262
337
|
? (() => {
|
|
263
338
|
const { freeMem: _f, availableMem: _a, loadavg: _l, uptime: _u, ...stableMachine } = hashTarget.machine;
|
|
264
|
-
return { ...hashTarget, machine: stableMachine };
|
|
339
|
+
return { ...hashTarget, sessions, machine: stableMachine };
|
|
265
340
|
})()
|
|
266
|
-
: hashTarget;
|
|
341
|
+
: { ...hashTarget, sessions };
|
|
267
342
|
const h = this.simpleHash(JSON.stringify(hashPayload));
|
|
268
343
|
if (h !== this.lastP2PStatusHash) {
|
|
269
344
|
this.lastP2PStatusHash = h;
|