@adhdev/daemon-core 0.8.49 → 0.8.50
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.d.ts +5 -0
- package/dist/index.js +283 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +269 -22
- package/dist/index.mjs.map +1 -1
- package/dist/logging/command-log.d.ts +1 -0
- package/dist/logging/debug-config.d.ts +21 -0
- package/dist/logging/debug-trace.d.ts +35 -0
- package/dist/session-host/app-name.d.ts +6 -0
- package/dist/shared-types.d.ts +2 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/commands/chat-commands.ts +76 -13
- package/src/commands/router.ts +55 -5
- package/src/index.ts +22 -0
- package/src/logging/command-log.ts +3 -0
- package/src/logging/debug-config.ts +75 -0
- package/src/logging/debug-trace.ts +130 -0
- package/src/session-host/app-name.ts +15 -0
- package/src/shared-types.ts +2 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LogLevel } from './logger.js';
|
|
2
|
+
export interface DebugRuntimeOptions {
|
|
3
|
+
dev?: boolean;
|
|
4
|
+
logLevel?: LogLevel;
|
|
5
|
+
trace?: boolean;
|
|
6
|
+
traceContent?: boolean;
|
|
7
|
+
traceBufferSize?: number;
|
|
8
|
+
traceCategories?: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface DebugRuntimeConfig {
|
|
11
|
+
logLevel: LogLevel;
|
|
12
|
+
collectDebugTrace: boolean;
|
|
13
|
+
traceContent: boolean;
|
|
14
|
+
traceBufferSize: number;
|
|
15
|
+
traceCategories: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveDebugRuntimeConfig(options?: DebugRuntimeOptions): DebugRuntimeConfig;
|
|
18
|
+
export declare function setDebugRuntimeConfig(config: DebugRuntimeConfig): void;
|
|
19
|
+
export declare function getDebugRuntimeConfig(): DebugRuntimeConfig;
|
|
20
|
+
export declare function resetDebugRuntimeConfig(): void;
|
|
21
|
+
export declare function shouldCollectTraceCategory(category?: string | null): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type DebugTraceLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
+
export interface DebugTraceEvent {
|
|
3
|
+
interactionId?: string;
|
|
4
|
+
category: string;
|
|
5
|
+
stage: string;
|
|
6
|
+
level: DebugTraceLevel;
|
|
7
|
+
sessionId?: string;
|
|
8
|
+
providerType?: string;
|
|
9
|
+
payload?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface DebugTraceEntry extends DebugTraceEvent {
|
|
12
|
+
id: string;
|
|
13
|
+
ts: number;
|
|
14
|
+
}
|
|
15
|
+
export interface DebugTraceStoreOptions {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
capacity: number;
|
|
18
|
+
}
|
|
19
|
+
export interface DebugTraceQuery {
|
|
20
|
+
interactionId?: string;
|
|
21
|
+
category?: string;
|
|
22
|
+
limit?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface DebugTraceStore {
|
|
25
|
+
record(event: DebugTraceEvent): DebugTraceEntry | null;
|
|
26
|
+
list(query?: DebugTraceQuery): DebugTraceEntry[];
|
|
27
|
+
clear(): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function sanitizeTracePayload(payload?: Record<string, unknown>): Record<string, unknown>;
|
|
30
|
+
export declare function createDebugTraceStore(options: DebugTraceStoreOptions): DebugTraceStore;
|
|
31
|
+
export declare function configureDebugTraceStore(): void;
|
|
32
|
+
export declare function recordDebugTrace(event: DebugTraceEvent): DebugTraceEntry | null;
|
|
33
|
+
export declare function getRecentDebugTrace(query?: DebugTraceQuery): DebugTraceEntry[];
|
|
34
|
+
export declare function clearDebugTrace(): void;
|
|
35
|
+
export declare function createInteractionId(prefix?: string): string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const DEFAULT_SESSION_HOST_APP_NAME = "adhdev";
|
|
2
|
+
export declare const DEFAULT_STANDALONE_SESSION_HOST_APP_NAME = "adhdev-standalone";
|
|
3
|
+
export declare function resolveSessionHostAppName(options?: {
|
|
4
|
+
standalone?: boolean;
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
}): string;
|
package/dist/shared-types.d.ts
CHANGED
|
@@ -138,6 +138,7 @@ export interface SessionChatTailUpdate extends ReadChatSyncResult {
|
|
|
138
138
|
key: string;
|
|
139
139
|
sessionId: string;
|
|
140
140
|
historySessionId?: string;
|
|
141
|
+
interactionId?: string;
|
|
141
142
|
seq: number;
|
|
142
143
|
timestamp: number;
|
|
143
144
|
}
|
|
@@ -163,6 +164,7 @@ export interface SessionModalUpdate {
|
|
|
163
164
|
title?: string;
|
|
164
165
|
modalMessage?: string;
|
|
165
166
|
modalButtons?: string[];
|
|
167
|
+
interactionId?: string;
|
|
166
168
|
seq: number;
|
|
167
169
|
timestamp: number;
|
|
168
170
|
}
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import { flattenContent, type ProviderModule, type ProviderScripts } from '../pr
|
|
|
9
9
|
import type { ProviderInstance } from '../providers/provider-instance.js';
|
|
10
10
|
import { readChatHistory } from '../config/chat-history.js';
|
|
11
11
|
import { LOG } from '../logging/logger.js';
|
|
12
|
+
import { recordDebugTrace } from '../logging/debug-trace.js';
|
|
12
13
|
import type { ChatMessage } from '../types.js';
|
|
13
14
|
import type { ReadChatCursor, ReadChatSyncMode, SessionTransport } from '../shared-types.js';
|
|
14
15
|
|
|
@@ -103,6 +104,34 @@ function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
|
|
|
103
104
|
return providerSessionId || targetSessionId;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
function getInteractionId(args: any): string | undefined {
|
|
108
|
+
return typeof args?._interactionId === 'string' && args._interactionId.trim()
|
|
109
|
+
? args._interactionId.trim()
|
|
110
|
+
: undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function traceProviderEvent(
|
|
114
|
+
args: any,
|
|
115
|
+
category: 'provider' | 'parser',
|
|
116
|
+
stage: string,
|
|
117
|
+
options: {
|
|
118
|
+
h: CommandHelpers;
|
|
119
|
+
provider?: ProviderModule;
|
|
120
|
+
payload?: Record<string, unknown>;
|
|
121
|
+
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
122
|
+
},
|
|
123
|
+
): void {
|
|
124
|
+
recordDebugTrace({
|
|
125
|
+
interactionId: getInteractionId(args),
|
|
126
|
+
category,
|
|
127
|
+
stage,
|
|
128
|
+
level: options.level || 'info',
|
|
129
|
+
sessionId: typeof args?.targetSessionId === 'string' ? args.targetSessionId : options.h.currentSession?.sessionId,
|
|
130
|
+
providerType: options.provider?.type || options.h.currentProviderType || options.h.currentSession?.providerType,
|
|
131
|
+
payload: options.payload,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
106
135
|
function callLegacyTextScript(script: ProviderScripts[keyof ProviderScripts] | undefined, text: string): string | null {
|
|
107
136
|
if (typeof script !== 'function') return null;
|
|
108
137
|
return (script as LegacyStringScript)(text);
|
|
@@ -392,6 +421,16 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
392
421
|
if (typeof parsed === 'string') { try { parsed = JSON.parse(parsed); } catch { } }
|
|
393
422
|
if (parsed && typeof parsed === 'object') {
|
|
394
423
|
_log(`Extension OK: ${parsed.messages?.length || 0} msgs`);
|
|
424
|
+
traceProviderEvent(args, 'provider', 'extension.read_chat.success', {
|
|
425
|
+
h,
|
|
426
|
+
provider,
|
|
427
|
+
payload: {
|
|
428
|
+
method: 'evaluateProviderScript',
|
|
429
|
+
result: evalResult.result,
|
|
430
|
+
parsed,
|
|
431
|
+
messageCount: Array.isArray(parsed.messages) ? parsed.messages.length : 0,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
395
434
|
h.historyWriter.appendNewMessages(
|
|
396
435
|
provider?.type || 'unknown_extension',
|
|
397
436
|
toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
|
|
@@ -404,6 +443,12 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
404
443
|
}
|
|
405
444
|
} catch (e: any) {
|
|
406
445
|
_log(`Extension error: ${e.message}`);
|
|
446
|
+
traceProviderEvent(args, 'provider', 'extension.read_chat.error', {
|
|
447
|
+
h,
|
|
448
|
+
provider,
|
|
449
|
+
level: 'warn',
|
|
450
|
+
payload: { method: 'evaluateProviderScript', error: e.message },
|
|
451
|
+
});
|
|
407
452
|
}
|
|
408
453
|
// Alternative: AgentStreamManager (script fail when)
|
|
409
454
|
if (h.agentStream) {
|
|
@@ -471,22 +516,40 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
471
516
|
const script = h.getProviderScript('readChat') || h.getProviderScript('read_chat');
|
|
472
517
|
if (script) {
|
|
473
518
|
try {
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
519
|
+
const evalResult = await h.evaluateProviderScript('readChat', undefined, 50000);
|
|
520
|
+
if (evalResult?.result) {
|
|
521
|
+
let parsed: any = evalResult.result;
|
|
522
|
+
if (typeof parsed === 'string') { try { parsed = JSON.parse(parsed); } catch { } }
|
|
523
|
+
if (parsed && typeof parsed === 'object' && parsed.messages?.length > 0) {
|
|
524
|
+
_log(`OK: ${parsed.messages?.length} msgs`);
|
|
525
|
+
traceProviderEvent(args, 'provider', 'ide.read_chat.success', {
|
|
526
|
+
h,
|
|
527
|
+
provider,
|
|
528
|
+
payload: {
|
|
529
|
+
method: 'evaluate',
|
|
530
|
+
result: evalResult.result,
|
|
531
|
+
parsed,
|
|
532
|
+
messageCount: Array.isArray(parsed.messages) ? parsed.messages.length : 0,
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
h.historyWriter.appendNewMessages(
|
|
536
|
+
provider?.type || getCurrentProviderType(h, 'unknown_ide'),
|
|
537
|
+
toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
|
|
538
|
+
parsed.title,
|
|
539
|
+
args?.targetSessionId,
|
|
540
|
+
historySessionId,
|
|
541
|
+
);
|
|
542
|
+
return buildReadChatCommandResult(parsed, args);
|
|
543
|
+
}
|
|
487
544
|
}
|
|
488
545
|
} catch (e: any) {
|
|
489
546
|
LOG.info('Command', `[read_chat] Script error: ${e.message}`);
|
|
547
|
+
traceProviderEvent(args, 'provider', 'ide.read_chat.error', {
|
|
548
|
+
h,
|
|
549
|
+
provider,
|
|
550
|
+
level: 'warn',
|
|
551
|
+
payload: { method: 'evaluate', error: e.message },
|
|
552
|
+
});
|
|
490
553
|
}
|
|
491
554
|
}
|
|
492
555
|
|
package/src/commands/router.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { LOG } from '../logging/logger.js';
|
|
|
30
30
|
import { logCommand } from '../logging/command-log.js';
|
|
31
31
|
import type { CommandLogEntry } from '../logging/command-log.js';
|
|
32
32
|
import { getRecentLogs, LOG_PATH } from '../logging/logger.js';
|
|
33
|
+
import { createInteractionId, getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
|
|
33
34
|
import { buildSessionEntries } from '../status/builders.js';
|
|
34
35
|
import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
|
|
35
36
|
import { getSessionCompletionMarker } from '../status/snapshot.js';
|
|
@@ -104,6 +105,14 @@ function normalizeCommandSource(source: string): CommandLogEntry['source'] {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
function normalizeCommandArgsWithInteractionId(args: any): Record<string, unknown> {
|
|
109
|
+
const base = args && typeof args === 'object' ? { ...args } : {};
|
|
110
|
+
if (typeof base._interactionId !== 'string' || !String(base._interactionId).trim()) {
|
|
111
|
+
base._interactionId = createInteractionId();
|
|
112
|
+
}
|
|
113
|
+
return base;
|
|
114
|
+
}
|
|
115
|
+
|
|
107
116
|
function toHostedCliRuntimeDescriptor(record: any): HostedCliRuntimeDescriptor | null {
|
|
108
117
|
if (!record || typeof record !== 'object') return null;
|
|
109
118
|
const runtimeId = typeof record.sessionId === 'string' ? record.sessionId : '';
|
|
@@ -149,18 +158,42 @@ export class DaemonCommandRouter {
|
|
|
149
158
|
async execute(cmd: string, args: any, source: string = 'unknown'): Promise<CommandRouterResult> {
|
|
150
159
|
const cmdStart = Date.now();
|
|
151
160
|
const logSource = normalizeCommandSource(source);
|
|
161
|
+
const normalizedArgs = normalizeCommandArgsWithInteractionId(args);
|
|
162
|
+
const interactionId = typeof normalizedArgs._interactionId === 'string' ? normalizedArgs._interactionId : undefined;
|
|
163
|
+
|
|
164
|
+
recordDebugTrace({
|
|
165
|
+
interactionId,
|
|
166
|
+
category: 'command',
|
|
167
|
+
stage: 'received',
|
|
168
|
+
level: 'info',
|
|
169
|
+
payload: { cmd, source: logSource },
|
|
170
|
+
});
|
|
152
171
|
|
|
153
172
|
try {
|
|
154
173
|
// 1. Try daemon-level command
|
|
155
|
-
const daemonResult = await this.executeDaemonCommand(cmd,
|
|
174
|
+
const daemonResult = await this.executeDaemonCommand(cmd, normalizedArgs);
|
|
156
175
|
if (daemonResult) {
|
|
157
|
-
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, args, success: daemonResult.success, durationMs: Date.now() - cmdStart });
|
|
176
|
+
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, interactionId, args: normalizedArgs, success: daemonResult.success, durationMs: Date.now() - cmdStart });
|
|
177
|
+
recordDebugTrace({
|
|
178
|
+
interactionId,
|
|
179
|
+
category: 'command',
|
|
180
|
+
stage: 'completed',
|
|
181
|
+
level: daemonResult.success ? 'info' : 'warn',
|
|
182
|
+
payload: { cmd, source: logSource, success: daemonResult.success, durationMs: Date.now() - cmdStart },
|
|
183
|
+
});
|
|
158
184
|
return daemonResult;
|
|
159
185
|
}
|
|
160
186
|
|
|
161
187
|
// 2. Delegate to DaemonCommandHandler
|
|
162
|
-
const handlerResult = await this.deps.commandHandler.handle(cmd,
|
|
163
|
-
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, args, success: handlerResult.success, durationMs: Date.now() - cmdStart });
|
|
188
|
+
const handlerResult = await this.deps.commandHandler.handle(cmd, normalizedArgs);
|
|
189
|
+
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, interactionId, args: normalizedArgs, success: handlerResult.success, durationMs: Date.now() - cmdStart });
|
|
190
|
+
recordDebugTrace({
|
|
191
|
+
interactionId,
|
|
192
|
+
category: 'command',
|
|
193
|
+
stage: 'completed',
|
|
194
|
+
level: handlerResult.success ? 'info' : 'warn',
|
|
195
|
+
payload: { cmd, source: logSource, success: handlerResult.success, durationMs: Date.now() - cmdStart },
|
|
196
|
+
});
|
|
164
197
|
|
|
165
198
|
// 3. Post-chat command callback
|
|
166
199
|
if (CHAT_COMMANDS.includes(cmd) && this.deps.onPostChatCommand) {
|
|
@@ -169,7 +202,14 @@ export class DaemonCommandRouter {
|
|
|
169
202
|
|
|
170
203
|
return handlerResult;
|
|
171
204
|
} catch (e: any) {
|
|
172
|
-
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, args, success: false, error: e.message, durationMs: Date.now() - cmdStart });
|
|
205
|
+
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, interactionId, args: normalizedArgs, success: false, error: e.message, durationMs: Date.now() - cmdStart });
|
|
206
|
+
recordDebugTrace({
|
|
207
|
+
interactionId,
|
|
208
|
+
category: 'command',
|
|
209
|
+
stage: 'failed',
|
|
210
|
+
level: 'error',
|
|
211
|
+
payload: { cmd, source: logSource, error: e?.message || String(e), durationMs: Date.now() - cmdStart },
|
|
212
|
+
});
|
|
173
213
|
throw e;
|
|
174
214
|
}
|
|
175
215
|
}
|
|
@@ -218,6 +258,16 @@ export class DaemonCommandRouter {
|
|
|
218
258
|
}
|
|
219
259
|
}
|
|
220
260
|
|
|
261
|
+
case 'get_debug_trace': {
|
|
262
|
+
const count = parseInt(args?.count) || parseInt(args?.limit) || 100;
|
|
263
|
+
const sinceTs = Number(args?.since) || 0;
|
|
264
|
+
const interactionId = typeof args?.interactionId === 'string' ? args.interactionId : undefined;
|
|
265
|
+
const category = typeof args?.category === 'string' ? args.category : undefined;
|
|
266
|
+
const trace = getRecentDebugTrace({ interactionId, category, limit: count })
|
|
267
|
+
.filter((entry) => !sinceTs || entry.ts > sinceTs);
|
|
268
|
+
return { success: true, trace, count: trace.length };
|
|
269
|
+
}
|
|
270
|
+
|
|
221
271
|
case 'session_host_get_diagnostics': {
|
|
222
272
|
if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
|
|
223
273
|
const diagnostics = await this.deps.sessionHostControl.getDiagnostics({
|
package/src/index.ts
CHANGED
|
@@ -151,6 +151,23 @@ export {
|
|
|
151
151
|
getCurrentDaemonLogPath,
|
|
152
152
|
} from './logging/logger.js';
|
|
153
153
|
export type { ScopedLogger, LogLevel, LogEntry } from './logging/logger.js';
|
|
154
|
+
export {
|
|
155
|
+
resolveDebugRuntimeConfig,
|
|
156
|
+
setDebugRuntimeConfig,
|
|
157
|
+
getDebugRuntimeConfig,
|
|
158
|
+
resetDebugRuntimeConfig,
|
|
159
|
+
shouldCollectTraceCategory,
|
|
160
|
+
} from './logging/debug-config.js';
|
|
161
|
+
export type { DebugRuntimeOptions, DebugRuntimeConfig } from './logging/debug-config.js';
|
|
162
|
+
export {
|
|
163
|
+
createDebugTraceStore,
|
|
164
|
+
configureDebugTraceStore,
|
|
165
|
+
recordDebugTrace,
|
|
166
|
+
getRecentDebugTrace,
|
|
167
|
+
clearDebugTrace,
|
|
168
|
+
createInteractionId,
|
|
169
|
+
} from './logging/debug-trace.js';
|
|
170
|
+
export type { DebugTraceEvent, DebugTraceEntry, DebugTraceQuery, DebugTraceStore, DebugTraceLevel } from './logging/debug-trace.js';
|
|
154
171
|
export { logCommand, getRecentCommands } from './logging/command-log.js';
|
|
155
172
|
|
|
156
173
|
// ── CLI Management ──
|
|
@@ -191,6 +208,11 @@ export { NodePtyTransportFactory } from './cli-adapters/pty-transport.js';
|
|
|
191
208
|
export type { PtyRuntimeTransport, PtyTransportFactory, PtySpawnOptions } from './cli-adapters/pty-transport.js';
|
|
192
209
|
export { SessionHostPtyTransportFactory } from './cli-adapters/session-host-transport.js';
|
|
193
210
|
export type { HostedCliRuntimeDescriptor, CliTransportFactoryParams } from './commands/cli-manager.js';
|
|
211
|
+
export {
|
|
212
|
+
DEFAULT_SESSION_HOST_APP_NAME,
|
|
213
|
+
DEFAULT_STANDALONE_SESSION_HOST_APP_NAME,
|
|
214
|
+
resolveSessionHostAppName,
|
|
215
|
+
} from './session-host/app-name.js';
|
|
194
216
|
export { ensureSessionHostReady, listHostedCliRuntimes } from './session-host/runtime-support.js';
|
|
195
217
|
export type { SessionHostEndpoint } from '@adhdev/session-host-core';
|
|
196
218
|
|
|
@@ -31,6 +31,7 @@ export interface CommandLogEntry {
|
|
|
31
31
|
ts: string; // ISO timestamp
|
|
32
32
|
cmd: string; // command name
|
|
33
33
|
source: 'ws' | 'p2p' | 'ext' | 'api' | 'standalone' | 'unknown'; // where it came from
|
|
34
|
+
interactionId?: string;
|
|
34
35
|
args?: Record<string, unknown>; // command arguments (sensitive values masked)
|
|
35
36
|
success?: boolean; // result
|
|
36
37
|
error?: string; // error message if failed
|
|
@@ -136,6 +137,7 @@ export function logCommand(entry: CommandLogEntry): void {
|
|
|
136
137
|
ts: entry.ts,
|
|
137
138
|
cmd: entry.cmd,
|
|
138
139
|
src: entry.source,
|
|
140
|
+
...(entry.interactionId ? { interactionId: entry.interactionId } : {}),
|
|
139
141
|
...(entry.args ? { args: maskArgs(entry.args) } : {}),
|
|
140
142
|
...(entry.success !== undefined ? { ok: entry.success } : {}),
|
|
141
143
|
...(entry.error ? { err: entry.error } : {}),
|
|
@@ -161,6 +163,7 @@ export function getRecentCommands(count = 50): CommandLogEntry[] {
|
|
|
161
163
|
ts: parsed.ts,
|
|
162
164
|
cmd: parsed.cmd,
|
|
163
165
|
source: parsed.src,
|
|
166
|
+
interactionId: parsed.interactionId,
|
|
164
167
|
args: parsed.args,
|
|
165
168
|
success: parsed.ok,
|
|
166
169
|
error: parsed.err,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { LogLevel } from './logger.js'
|
|
2
|
+
|
|
3
|
+
export interface DebugRuntimeOptions {
|
|
4
|
+
dev?: boolean
|
|
5
|
+
logLevel?: LogLevel
|
|
6
|
+
trace?: boolean
|
|
7
|
+
traceContent?: boolean
|
|
8
|
+
traceBufferSize?: number
|
|
9
|
+
traceCategories?: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DebugRuntimeConfig {
|
|
13
|
+
logLevel: LogLevel
|
|
14
|
+
collectDebugTrace: boolean
|
|
15
|
+
traceContent: boolean
|
|
16
|
+
traceBufferSize: number
|
|
17
|
+
traceCategories: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const NORMAL_TRACE_BUFFER_SIZE = 200
|
|
21
|
+
const DEV_TRACE_BUFFER_SIZE = 1000
|
|
22
|
+
|
|
23
|
+
const DEFAULT_CONFIG: DebugRuntimeConfig = {
|
|
24
|
+
logLevel: 'info',
|
|
25
|
+
collectDebugTrace: false,
|
|
26
|
+
traceContent: false,
|
|
27
|
+
traceBufferSize: NORMAL_TRACE_BUFFER_SIZE,
|
|
28
|
+
traceCategories: [],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let currentConfig: DebugRuntimeConfig = { ...DEFAULT_CONFIG }
|
|
32
|
+
|
|
33
|
+
function normalizeCategories(categories?: string[]): string[] {
|
|
34
|
+
if (!Array.isArray(categories)) return []
|
|
35
|
+
return categories
|
|
36
|
+
.map((category) => String(category || '').trim())
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolveDebugRuntimeConfig(options: DebugRuntimeOptions = {}): DebugRuntimeConfig {
|
|
41
|
+
const dev = options.dev === true
|
|
42
|
+
return {
|
|
43
|
+
logLevel: options.logLevel || (dev ? 'debug' : DEFAULT_CONFIG.logLevel),
|
|
44
|
+
collectDebugTrace: typeof options.trace === 'boolean' ? options.trace : dev,
|
|
45
|
+
traceContent: options.traceContent === true,
|
|
46
|
+
traceBufferSize: Number.isFinite(options.traceBufferSize)
|
|
47
|
+
? Math.max(10, Math.floor(options.traceBufferSize as number))
|
|
48
|
+
: (dev ? DEV_TRACE_BUFFER_SIZE : DEFAULT_CONFIG.traceBufferSize),
|
|
49
|
+
traceCategories: normalizeCategories(options.traceCategories),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function setDebugRuntimeConfig(config: DebugRuntimeConfig): void {
|
|
54
|
+
currentConfig = {
|
|
55
|
+
...config,
|
|
56
|
+
traceCategories: normalizeCategories(config.traceCategories),
|
|
57
|
+
traceBufferSize: Math.max(10, Math.floor(config.traceBufferSize || DEFAULT_CONFIG.traceBufferSize)),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getDebugRuntimeConfig(): DebugRuntimeConfig {
|
|
62
|
+
return { ...currentConfig, traceCategories: [...currentConfig.traceCategories] }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function resetDebugRuntimeConfig(): void {
|
|
66
|
+
currentConfig = { ...DEFAULT_CONFIG }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function shouldCollectTraceCategory(category?: string | null): boolean {
|
|
70
|
+
const config = currentConfig
|
|
71
|
+
if (!config.collectDebugTrace) return false
|
|
72
|
+
if (!category) return true
|
|
73
|
+
if (config.traceCategories.length === 0) return true
|
|
74
|
+
return config.traceCategories.includes(category)
|
|
75
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { getDebugRuntimeConfig, shouldCollectTraceCategory } from './debug-config.js'
|
|
2
|
+
|
|
3
|
+
export type DebugTraceLevel = 'debug' | 'info' | 'warn' | 'error'
|
|
4
|
+
|
|
5
|
+
export interface DebugTraceEvent {
|
|
6
|
+
interactionId?: string
|
|
7
|
+
category: string
|
|
8
|
+
stage: string
|
|
9
|
+
level: DebugTraceLevel
|
|
10
|
+
sessionId?: string
|
|
11
|
+
providerType?: string
|
|
12
|
+
payload?: Record<string, unknown>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DebugTraceEntry extends DebugTraceEvent {
|
|
16
|
+
id: string
|
|
17
|
+
ts: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DebugTraceStoreOptions {
|
|
21
|
+
enabled: boolean
|
|
22
|
+
capacity: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DebugTraceQuery {
|
|
26
|
+
interactionId?: string
|
|
27
|
+
category?: string
|
|
28
|
+
limit?: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DebugTraceStore {
|
|
32
|
+
record(event: DebugTraceEvent): DebugTraceEntry | null
|
|
33
|
+
list(query?: DebugTraceQuery): DebugTraceEntry[]
|
|
34
|
+
clear(): void
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function summarizeString(value: string): string {
|
|
38
|
+
return `[${value.length} chars]`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sanitizeTraceValue(value: unknown, traceContent: boolean): unknown {
|
|
42
|
+
if (traceContent) {
|
|
43
|
+
if (Array.isArray(value)) return value.map((entry) => sanitizeTraceValue(entry, traceContent))
|
|
44
|
+
if (value && typeof value === 'object') {
|
|
45
|
+
return Object.fromEntries(
|
|
46
|
+
Object.entries(value as Record<string, unknown>).map(([key, nested]) => [key, sanitizeTraceValue(nested, traceContent)]),
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
return value
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof value === 'string') return summarizeString(value)
|
|
53
|
+
if (Array.isArray(value)) return value.map((entry) => sanitizeTraceValue(entry, traceContent))
|
|
54
|
+
if (value && typeof value === 'object') {
|
|
55
|
+
return Object.fromEntries(
|
|
56
|
+
Object.entries(value as Record<string, unknown>).map(([key, nested]) => [key, sanitizeTraceValue(nested, traceContent)]),
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
return value
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function sanitizeTracePayload(payload?: Record<string, unknown>): Record<string, unknown> {
|
|
63
|
+
if (!payload) return {}
|
|
64
|
+
const { traceContent } = getDebugRuntimeConfig()
|
|
65
|
+
return sanitizeTraceValue(payload, traceContent) as Record<string, unknown>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function createEntry(event: DebugTraceEvent): DebugTraceEntry {
|
|
69
|
+
return {
|
|
70
|
+
id: `trace_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
|
|
71
|
+
ts: Date.now(),
|
|
72
|
+
...event,
|
|
73
|
+
payload: sanitizeTracePayload(event.payload),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createDebugTraceStore(options: DebugTraceStoreOptions): DebugTraceStore {
|
|
78
|
+
const entries: DebugTraceEntry[] = []
|
|
79
|
+
const capacity = Math.max(1, Math.floor(options.capacity || 100))
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
record(event: DebugTraceEvent): DebugTraceEntry | null {
|
|
83
|
+
if (!options.enabled) return null
|
|
84
|
+
const entry = createEntry(event)
|
|
85
|
+
entries.push(entry)
|
|
86
|
+
if (entries.length > capacity) {
|
|
87
|
+
entries.splice(0, entries.length - capacity)
|
|
88
|
+
}
|
|
89
|
+
return entry
|
|
90
|
+
},
|
|
91
|
+
list(query: DebugTraceQuery = {}): DebugTraceEntry[] {
|
|
92
|
+
const limit = Math.max(1, Math.floor(query.limit || 100))
|
|
93
|
+
return entries
|
|
94
|
+
.filter((entry) => !query.interactionId || entry.interactionId === query.interactionId)
|
|
95
|
+
.filter((entry) => !query.category || entry.category === query.category)
|
|
96
|
+
.slice(-limit)
|
|
97
|
+
.map((entry) => ({ ...entry, payload: entry.payload ? { ...entry.payload } : {} }))
|
|
98
|
+
},
|
|
99
|
+
clear(): void {
|
|
100
|
+
entries.splice(0, entries.length)
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let globalStore = createDebugTraceStore({ enabled: false, capacity: getDebugRuntimeConfig().traceBufferSize })
|
|
106
|
+
|
|
107
|
+
export function configureDebugTraceStore(): void {
|
|
108
|
+
const config = getDebugRuntimeConfig()
|
|
109
|
+
globalStore = createDebugTraceStore({
|
|
110
|
+
enabled: config.collectDebugTrace,
|
|
111
|
+
capacity: config.traceBufferSize,
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function recordDebugTrace(event: DebugTraceEvent): DebugTraceEntry | null {
|
|
116
|
+
if (!shouldCollectTraceCategory(event.category)) return null
|
|
117
|
+
return globalStore.record(event)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function getRecentDebugTrace(query: DebugTraceQuery = {}): DebugTraceEntry[] {
|
|
121
|
+
return globalStore.list(query)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function clearDebugTrace(): void {
|
|
125
|
+
globalStore.clear()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function createInteractionId(prefix = 'ix'): string {
|
|
129
|
+
return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`
|
|
130
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const DEFAULT_SESSION_HOST_APP_NAME = 'adhdev'
|
|
2
|
+
export const DEFAULT_STANDALONE_SESSION_HOST_APP_NAME = 'adhdev-standalone'
|
|
3
|
+
|
|
4
|
+
export function resolveSessionHostAppName(options: {
|
|
5
|
+
standalone?: boolean
|
|
6
|
+
env?: NodeJS.ProcessEnv
|
|
7
|
+
} = {}): string {
|
|
8
|
+
const env = options.env || process.env
|
|
9
|
+
const explicit = typeof env.ADHDEV_SESSION_HOST_NAME === 'string'
|
|
10
|
+
? env.ADHDEV_SESSION_HOST_NAME.trim()
|
|
11
|
+
: ''
|
|
12
|
+
|
|
13
|
+
if (explicit) return explicit
|
|
14
|
+
return options.standalone ? DEFAULT_STANDALONE_SESSION_HOST_APP_NAME : DEFAULT_SESSION_HOST_APP_NAME
|
|
15
|
+
}
|
package/src/shared-types.ts
CHANGED
|
@@ -187,6 +187,7 @@ export interface SessionChatTailUpdate extends ReadChatSyncResult {
|
|
|
187
187
|
key: string;
|
|
188
188
|
sessionId: string;
|
|
189
189
|
historySessionId?: string;
|
|
190
|
+
interactionId?: string;
|
|
190
191
|
seq: number;
|
|
191
192
|
timestamp: number;
|
|
192
193
|
}
|
|
@@ -215,6 +216,7 @@ export interface SessionModalUpdate {
|
|
|
215
216
|
title?: string;
|
|
216
217
|
modalMessage?: string;
|
|
217
218
|
modalButtons?: string[];
|
|
219
|
+
interactionId?: string;
|
|
218
220
|
seq: number;
|
|
219
221
|
timestamp: number;
|
|
220
222
|
}
|