@adhdev/daemon-core 0.9.31 → 0.9.33
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/cli-adapters/provider-cli-adapter.d.ts +4 -0
- package/dist/index.js +113 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +113 -11
- package/dist/index.mjs.map +1 -1
- package/dist/providers/provider-loader.d.ts +2 -0
- package/dist/status/chat-tail-hot-sessions.d.ts +1 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +54 -3
- package/src/commands/chat-commands.ts +54 -0
- package/src/logging/logger.ts +5 -6
- package/src/providers/provider-loader.ts +25 -5
- package/src/status/chat-tail-hot-sessions.ts +6 -0
|
@@ -85,6 +85,7 @@ export declare class ProviderLoader {
|
|
|
85
85
|
probeStarts?: string[];
|
|
86
86
|
});
|
|
87
87
|
private log;
|
|
88
|
+
private debugLog;
|
|
88
89
|
/**
|
|
89
90
|
* User override root (~/.adhdev/providers by default).
|
|
90
91
|
*/
|
|
@@ -220,6 +221,7 @@ export declare class ProviderLoader {
|
|
|
220
221
|
getMachineProviderConfig(type: string): MachineProviderConfig;
|
|
221
222
|
setMachineProviderConfig(type: string, patch: Partial<MachineProviderConfig>): boolean;
|
|
222
223
|
setMachineProviderEnabled(type: string, enabled: boolean): boolean;
|
|
224
|
+
private getEffectiveProviderAvailability;
|
|
223
225
|
getMachineProviderStatus(type: string): ProviderMachineStatus;
|
|
224
226
|
getSpawnArgs(type: string, fallback?: string[]): string[];
|
|
225
227
|
private parseArgsSetting;
|
|
@@ -15,6 +15,7 @@ export declare function classifyHotChatSessionsForSubscriptionFlush(sessions: Ho
|
|
|
15
15
|
now?: number;
|
|
16
16
|
recentMessageGraceMs?: number;
|
|
17
17
|
activeStatuses?: ReadonlySet<string>;
|
|
18
|
+
activeSessionIds?: ReadonlySet<string>;
|
|
18
19
|
}): {
|
|
19
20
|
active: Set<string>;
|
|
20
21
|
finalizing: Set<string>;
|
package/package.json
CHANGED
|
@@ -282,6 +282,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
282
282
|
private static readonly STATUS_HOT_PATH_PARSE_MIN_INTERVAL_MS = 1000;
|
|
283
283
|
private static readonly SCREEN_SNAPSHOT_MIN_INTERVAL_MS = 250;
|
|
284
284
|
private static readonly MAX_TRACE_ENTRIES = 250;
|
|
285
|
+
private static readonly PARSE_MESSAGE_TAIL_LIMIT = 100;
|
|
285
286
|
|
|
286
287
|
private readonly providerResolutionMeta: ProviderResolutionMeta;
|
|
287
288
|
private static readonly FINISH_RETRY_DELAY_MS = 300;
|
|
@@ -333,6 +334,42 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
333
334
|
return null;
|
|
334
335
|
}
|
|
335
336
|
|
|
337
|
+
private selectParseBaseMessages(baseMessages: CliChatMessage[]): CliChatMessage[] {
|
|
338
|
+
if (baseMessages.length <= ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT) return baseMessages;
|
|
339
|
+
return baseMessages.slice(-ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private messagesComparable(left: any, right: any): boolean {
|
|
343
|
+
if (!left || !right) return false;
|
|
344
|
+
if ((left.role || '') !== (right.role || '')) return false;
|
|
345
|
+
const leftText = normalizeComparableTranscriptText(left.content);
|
|
346
|
+
const rightText = normalizeComparableTranscriptText(right.content);
|
|
347
|
+
return !!leftText && leftText === rightText;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private stitchParsedMessagesWithCommittedBase(
|
|
351
|
+
parsedMessages: any[],
|
|
352
|
+
fullBaseMessages: CliChatMessage[],
|
|
353
|
+
parseBaseMessages: CliChatMessage[],
|
|
354
|
+
): any[] {
|
|
355
|
+
if (!Array.isArray(parsedMessages) || parsedMessages.length === 0) return parsedMessages;
|
|
356
|
+
if (fullBaseMessages.length <= parseBaseMessages.length) return parsedMessages;
|
|
357
|
+
|
|
358
|
+
const parsedFirst = parsedMessages[0];
|
|
359
|
+
const fullFirst = fullBaseMessages[0];
|
|
360
|
+
if (parsedMessages.length >= fullBaseMessages.length && this.messagesComparable(parsedFirst, fullFirst)) {
|
|
361
|
+
return parsedMessages;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const tailFirst = parseBaseMessages[0];
|
|
365
|
+
if (tailFirst && this.messagesComparable(parsedFirst, tailFirst)) {
|
|
366
|
+
const prefixLength = fullBaseMessages.length - parseBaseMessages.length;
|
|
367
|
+
return [...fullBaseMessages.slice(0, prefixLength), ...parsedMessages];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return [...fullBaseMessages, ...parsedMessages];
|
|
371
|
+
}
|
|
372
|
+
|
|
336
373
|
private getIdleFinishConfirmMs(): number {
|
|
337
374
|
return this.timeouts.idleFinishConfirm;
|
|
338
375
|
}
|
|
@@ -1044,7 +1081,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1044
1081
|
if (!this.applyPendingScriptStatusDebounce(ctx)) return;
|
|
1045
1082
|
|
|
1046
1083
|
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
1047
|
-
LOG.
|
|
1084
|
+
LOG.debug(
|
|
1048
1085
|
'CLI',
|
|
1049
1086
|
`[${this.cliType}] settled diagnostics prompt=${JSON.stringify(this.currentTurnScope?.prompt || '').slice(0, 140)} status=${String(status || '')} parsedStatus=${String(parsedStatus || '')} parsedMsgCount=${parsedMessages.length} lastParsedAssistant=${JSON.stringify(summarizeCliTraceText(lastParsedAssistant?.content || '', 120)).slice(0, 160)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
|
|
1050
1087
|
);
|
|
@@ -1439,18 +1476,26 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1439
1476
|
try {
|
|
1440
1477
|
const screenText = this.terminalScreen.getText();
|
|
1441
1478
|
const tail = this.recentOutputBuffer.slice(-500);
|
|
1479
|
+
const parseBaseMessages = this.selectParseBaseMessages(this.committedMessages);
|
|
1442
1480
|
const input = buildCliParseInput({
|
|
1443
1481
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
1444
1482
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1445
1483
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1446
1484
|
terminalScreenText: screenText,
|
|
1447
|
-
baseMessages:
|
|
1485
|
+
baseMessages: parseBaseMessages,
|
|
1448
1486
|
partialResponse: this.responseBuffer,
|
|
1449
1487
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
1450
1488
|
scope: this.currentTurnScope,
|
|
1451
1489
|
runtimeSettings: this.runtimeSettings,
|
|
1452
1490
|
});
|
|
1453
1491
|
const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
|
|
1492
|
+
if (session && typeof session === 'object' && Array.isArray(session.messages)) {
|
|
1493
|
+
session.messages = this.stitchParsedMessagesWithCommittedBase(
|
|
1494
|
+
session.messages,
|
|
1495
|
+
this.committedMessages,
|
|
1496
|
+
parseBaseMessages,
|
|
1497
|
+
);
|
|
1498
|
+
}
|
|
1454
1499
|
this.parseErrorMessage = null;
|
|
1455
1500
|
return session && typeof session === 'object' ? session : null;
|
|
1456
1501
|
} catch (e: any) {
|
|
@@ -1871,12 +1916,13 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1871
1916
|
}
|
|
1872
1917
|
try {
|
|
1873
1918
|
const screenText = typeof screenTextOverride === 'string' ? screenTextOverride : this.terminalScreen.getText();
|
|
1919
|
+
const parseBaseMessages = this.selectParseBaseMessages(baseMessages);
|
|
1874
1920
|
const input = buildCliParseInput({
|
|
1875
1921
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
1876
1922
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1877
1923
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1878
1924
|
terminalScreenText: screenText,
|
|
1879
|
-
baseMessages,
|
|
1925
|
+
baseMessages: parseBaseMessages,
|
|
1880
1926
|
partialResponse,
|
|
1881
1927
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
1882
1928
|
scope,
|
|
@@ -1888,6 +1934,11 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1888
1934
|
}
|
|
1889
1935
|
const normalizedParsed = this.suppressStaleParsedApproval(parsed, input.recentBuffer, input.screenText);
|
|
1890
1936
|
if (normalizedParsed && Array.isArray(normalizedParsed.messages)) {
|
|
1937
|
+
normalizedParsed.messages = this.stitchParsedMessagesWithCommittedBase(
|
|
1938
|
+
normalizedParsed.messages,
|
|
1939
|
+
baseMessages,
|
|
1940
|
+
parseBaseMessages,
|
|
1941
|
+
);
|
|
1891
1942
|
this.trimLastAssistantEcho(normalizedParsed.messages, scope?.prompt || getLastUserPromptText(baseMessages));
|
|
1892
1943
|
}
|
|
1893
1944
|
this.parseErrorMessage = null;
|
|
@@ -297,6 +297,36 @@ function toHistoryPersistedMessages(messages: ChatMessage[]): Array<{
|
|
|
297
297
|
}));
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
function findLastMessageIndexBySignature(messages: ChatMessage[], signature: string): number {
|
|
301
|
+
if (!signature) return -1;
|
|
302
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
303
|
+
if (getChatMessageSignature(messages[index]) === signature) {
|
|
304
|
+
return index;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return -1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function buildBoundedTailSync(messages: ChatMessage[], cursor: Required<ReadChatCursor>): {
|
|
311
|
+
syncMode: ReadChatSyncMode;
|
|
312
|
+
replaceFrom: number;
|
|
313
|
+
messages: ChatMessage[];
|
|
314
|
+
totalMessages: number;
|
|
315
|
+
lastMessageSignature: string;
|
|
316
|
+
} {
|
|
317
|
+
const totalMessages = messages.length;
|
|
318
|
+
const tailMessages = cursor.tailLimit > 0 && totalMessages > cursor.tailLimit
|
|
319
|
+
? messages.slice(-cursor.tailLimit)
|
|
320
|
+
: messages;
|
|
321
|
+
return {
|
|
322
|
+
syncMode: 'full',
|
|
323
|
+
replaceFrom: 0,
|
|
324
|
+
messages: tailMessages,
|
|
325
|
+
totalMessages,
|
|
326
|
+
lastMessageSignature: getChatMessageSignature(messages[totalMessages - 1]),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
300
330
|
function computeReadChatSync(messages: ChatMessage[], cursor: Required<ReadChatCursor>): {
|
|
301
331
|
syncMode: ReadChatSyncMode;
|
|
302
332
|
replaceFrom: number;
|
|
@@ -338,6 +368,16 @@ function computeReadChatSync(messages: ChatMessage[], cursor: Required<ReadChatC
|
|
|
338
368
|
};
|
|
339
369
|
}
|
|
340
370
|
|
|
371
|
+
if (cursor.tailLimit > 0 && knownSignature === lastMessageSignature) {
|
|
372
|
+
return {
|
|
373
|
+
syncMode: 'noop',
|
|
374
|
+
replaceFrom: totalMessages,
|
|
375
|
+
messages: [],
|
|
376
|
+
totalMessages,
|
|
377
|
+
lastMessageSignature,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
341
381
|
if (knownMessageCount < totalMessages) {
|
|
342
382
|
const anchorSignature = getChatMessageSignature(messages[knownMessageCount - 1]);
|
|
343
383
|
if (anchorSignature === knownSignature) {
|
|
@@ -349,6 +389,20 @@ function computeReadChatSync(messages: ChatMessage[], cursor: Required<ReadChatC
|
|
|
349
389
|
lastMessageSignature,
|
|
350
390
|
};
|
|
351
391
|
}
|
|
392
|
+
|
|
393
|
+
if (cursor.tailLimit > 0) {
|
|
394
|
+
const signatureIndex = findLastMessageIndexBySignature(messages, knownSignature);
|
|
395
|
+
if (signatureIndex >= 0) {
|
|
396
|
+
return {
|
|
397
|
+
syncMode: 'append',
|
|
398
|
+
replaceFrom: knownMessageCount,
|
|
399
|
+
messages: messages.slice(signatureIndex + 1),
|
|
400
|
+
totalMessages,
|
|
401
|
+
lastMessageSignature,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
return buildBoundedTailSync(messages, cursor);
|
|
405
|
+
}
|
|
352
406
|
}
|
|
353
407
|
|
|
354
408
|
const replaceFrom = Math.max(0, Math.min(knownMessageCount - 1, totalMessages));
|
package/src/logging/logger.ts
CHANGED
|
@@ -175,19 +175,18 @@ export function daemonLog(category: string, msg: string, level: LogLevel = 'info
|
|
|
175
175
|
const label = LEVEL_LABEL[level];
|
|
176
176
|
const line = `[${ts()}] [${label}] [${category}] ${msg}`;
|
|
177
177
|
|
|
178
|
-
//
|
|
178
|
+
// Apply the active log level consistently to console, file, and the remote ring buffer.
|
|
179
|
+
// Debug hot paths are useful when explicitly enabled, but should not inflate normal-mode logs.
|
|
180
|
+
if (!shouldOutput) return;
|
|
181
|
+
|
|
179
182
|
writeToFile(line);
|
|
180
183
|
|
|
181
|
-
// Always save to ring buffer (for remote transmission)
|
|
182
184
|
ringBuffer.push({ ts: Date.now(), level, category, message: msg });
|
|
183
185
|
if (ringBuffer.length > RING_BUFFER_SIZE) {
|
|
184
186
|
ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
|
|
188
|
-
if (shouldOutput) {
|
|
189
|
-
origConsoleLog(line);
|
|
190
|
-
}
|
|
189
|
+
origConsoleLog(line);
|
|
191
190
|
}
|
|
192
191
|
|
|
193
192
|
// ─── Convenience API ────────────────────────────────
|
|
@@ -199,6 +199,10 @@ export class ProviderLoader {
|
|
|
199
199
|
this.logFn(`[ProviderLoader] ${msg}`);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
private debugLog(msg: string): void {
|
|
203
|
+
LOG.debug('Provider', `[ProviderLoader] ${msg}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
202
206
|
// ─── Public API ────────────────────────────────
|
|
203
207
|
|
|
204
208
|
/**
|
|
@@ -649,10 +653,26 @@ export class ProviderLoader {
|
|
|
649
653
|
return this.setMachineProviderConfig(type, { enabled });
|
|
650
654
|
}
|
|
651
655
|
|
|
656
|
+
private getEffectiveProviderAvailability(type: string): ProviderAvailabilityState | undefined {
|
|
657
|
+
const providerType = this.resolveAlias(type);
|
|
658
|
+
const availability = this.providerAvailability.get(providerType);
|
|
659
|
+
if (availability) return availability;
|
|
660
|
+
|
|
661
|
+
const machineConfig = this.getMachineProviderConfig(providerType);
|
|
662
|
+
const lastDetection = machineConfig.lastDetection;
|
|
663
|
+
if (!lastDetection) return undefined;
|
|
664
|
+
return {
|
|
665
|
+
installed: lastDetection.ok === true,
|
|
666
|
+
detectedPath: typeof lastDetection.path === 'string' && lastDetection.path.trim()
|
|
667
|
+
? lastDetection.path.trim()
|
|
668
|
+
: null,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
652
672
|
getMachineProviderStatus(type: string): ProviderMachineStatus {
|
|
653
673
|
const providerType = this.resolveAlias(type);
|
|
654
674
|
if (!this.isMachineProviderEnabled(providerType)) return 'disabled';
|
|
655
|
-
const availability = this.
|
|
675
|
+
const availability = this.getEffectiveProviderAvailability(providerType);
|
|
656
676
|
if (!availability) return 'enabled_unchecked';
|
|
657
677
|
return availability.installed ? 'detected' : 'not_detected';
|
|
658
678
|
}
|
|
@@ -788,7 +808,7 @@ export class ProviderLoader {
|
|
|
788
808
|
|
|
789
809
|
getAvailableProviderInfos(): Array<ProviderModule & { installed?: boolean; detectedPath?: string | null; enabled: boolean; machineStatus: ProviderMachineStatus; lastDetection?: MachineProviderCheckResult; lastVerification?: MachineProviderCheckResult }> {
|
|
790
810
|
return this.getAll().map((provider) => {
|
|
791
|
-
const availability = this.
|
|
811
|
+
const availability = this.getEffectiveProviderAvailability(provider.type);
|
|
792
812
|
const enabled = this.isMachineProviderEnabled(provider.type);
|
|
793
813
|
const machineConfig = this.getMachineProviderConfig(provider.type);
|
|
794
814
|
return {
|
|
@@ -892,7 +912,7 @@ export class ProviderLoader {
|
|
|
892
912
|
const loaded = this.loadScriptsFromDir(type, entry.scriptDir);
|
|
893
913
|
if (loaded) {
|
|
894
914
|
resolved.scripts = loaded;
|
|
895
|
-
this.
|
|
915
|
+
this.debugLog(` [compatibility] ${type} v${currentVersion} → ${entry.scriptDir}`);
|
|
896
916
|
resolved._resolvedScriptDir = entry.scriptDir;
|
|
897
917
|
resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
|
|
898
918
|
if (providerDir) {
|
|
@@ -912,7 +932,7 @@ export class ProviderLoader {
|
|
|
912
932
|
const loaded = this.loadScriptsFromDir(type, base.defaultScriptDir);
|
|
913
933
|
if (loaded) {
|
|
914
934
|
resolved.scripts = loaded;
|
|
915
|
-
this.
|
|
935
|
+
this.debugLog(` [compatibility] ${type} v${currentVersion} → default: ${base.defaultScriptDir}`);
|
|
916
936
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
917
937
|
resolved._resolvedScriptsSource = 'defaultScriptDir:version_miss';
|
|
918
938
|
if (providerDir) {
|
|
@@ -955,7 +975,7 @@ export class ProviderLoader {
|
|
|
955
975
|
const loaded = this.loadScriptsFromDir(type, base.defaultScriptDir);
|
|
956
976
|
if (loaded) {
|
|
957
977
|
resolved.scripts = loaded;
|
|
958
|
-
this.
|
|
978
|
+
this.debugLog(` [compatibility] ${type} no version detected → default: ${base.defaultScriptDir}`);
|
|
959
979
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
960
980
|
resolved._resolvedScriptsSource = 'defaultScriptDir:no_version';
|
|
961
981
|
if (providerDir) {
|
|
@@ -59,6 +59,7 @@ export function classifyHotChatSessionsForSubscriptionFlush(
|
|
|
59
59
|
now?: number;
|
|
60
60
|
recentMessageGraceMs?: number;
|
|
61
61
|
activeStatuses?: ReadonlySet<string>;
|
|
62
|
+
activeSessionIds?: ReadonlySet<string>;
|
|
62
63
|
} = {},
|
|
63
64
|
): { active: Set<string>; finalizing: Set<string> } {
|
|
64
65
|
const now = options.now ?? Date.now();
|
|
@@ -69,6 +70,7 @@ export function classifyHotChatSessionsForSubscriptionFlush(
|
|
|
69
70
|
: DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS,
|
|
70
71
|
);
|
|
71
72
|
const activeStatuses = options.activeStatuses ?? DEFAULT_ACTIVE_CHAT_POLL_STATUSES;
|
|
73
|
+
const activeSessionIds = options.activeSessionIds ?? new Set<string>();
|
|
72
74
|
const active = new Set<string>();
|
|
73
75
|
const excluded = new Set<string>();
|
|
74
76
|
|
|
@@ -79,6 +81,10 @@ export function classifyHotChatSessionsForSubscriptionFlush(
|
|
|
79
81
|
excluded.add(sessionId);
|
|
80
82
|
continue;
|
|
81
83
|
}
|
|
84
|
+
if (activeSessionIds.has(sessionId)) {
|
|
85
|
+
active.add(sessionId);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
82
88
|
|
|
83
89
|
const status = String(session?.status || '').toLowerCase();
|
|
84
90
|
const unread = session?.unread === true;
|