@adhdev/daemon-core 0.9.76-rc.54 → 0.9.76-rc.55

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.76-rc.54",
3
+ "version": "0.9.76-rc.55",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,8 +18,8 @@ import { LOG, getRecentLogs } from '../logging/logger.js';
18
18
  import { getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
19
19
  import { buildChatMessageSignature } from '../chat/chat-signatures.js';
20
20
  import type { ChatMessage } from '../types.js';
21
- import { filterUserFacingChatMessages } from '../providers/chat-message-normalization.js';
22
21
  import type { SessionTransport } from '../shared-types.js';
22
+ import { filterUserFacingChatMessages, normalizeChatMessages } from '../providers/chat-message-normalization.js';
23
23
 
24
24
  const RECENT_SEND_WINDOW_MS = 1200;
25
25
  export const READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25_000;
@@ -196,7 +196,7 @@ function normalizeReadChatTailLimit(args: any): number {
196
196
 
197
197
  function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[] {
198
198
  const messages = Array.isArray(payload.messages) ? payload.messages as ChatMessage[] : [];
199
- return messages;
199
+ return normalizeChatMessages(messages);
200
200
  }
201
201
 
202
202
 
@@ -320,23 +320,19 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
320
320
  const visibleMessages = filterUserFacingChatMessages(messages);
321
321
  const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
322
322
  const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
323
- const returnedDebugReadChat = debugReadChat
324
- ? {
325
- ...debugReadChat,
326
- fullMsgCount: typeof debugReadChat.fullMsgCount === 'number'
327
- ? debugReadChat.fullMsgCount
328
- : messages.length,
329
- visibleMsgCount: visibleMessages.length,
330
- hiddenMsgCount,
331
- returnedMsgCount: sync.messages.length,
332
- }
333
- : undefined;
323
+ const nextDebugReadChat = {
324
+ ...(debugReadChat || {}),
325
+ fullMsgCount: messages.length,
326
+ visibleMsgCount: visibleMessages.length,
327
+ hiddenMsgCount,
328
+ returnedMsgCount: sync.messages.length,
329
+ };
334
330
  return {
335
331
  success: true,
336
332
  ...validatedPayload,
337
333
  messages: sync.messages,
338
334
  totalMessages: sync.totalMessages,
339
- ...(returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}),
335
+ debugReadChat: nextDebugReadChat,
340
336
  };
341
337
  }
342
338
 
@@ -179,44 +179,25 @@ function readMessageMeta(message: ChatMessage): Record<string, unknown> | null {
179
179
  : null;
180
180
  }
181
181
 
182
- function readStringField(value: unknown): string {
183
- return typeof value === 'string' ? value.trim().toLowerCase() : '';
184
- }
185
-
186
- function isExplicitlyHiddenFromTranscript(message: ChatMessage, meta: Record<string, unknown> | null): boolean {
187
- const record = message as ChatMessage & Record<string, unknown>;
188
- const visibility = readStringField(record.visibility || meta?.visibility || meta?.transcriptVisibility);
189
- const audience = readStringField(record.audience || meta?.audience);
190
- const source = readStringField(record.source || meta?.source);
191
-
182
+ function isExplicitlyHiddenFromTranscript(meta: Record<string, unknown> | null): boolean {
183
+ if (!meta) return false;
184
+ const visibility = typeof meta.transcriptVisibility === 'string'
185
+ ? meta.transcriptVisibility.trim().toLowerCase()
186
+ : '';
192
187
  return visibility === 'hidden'
193
188
  || visibility === 'debug'
194
- || visibility === 'internal'
195
- || audience === 'debug'
196
- || audience === 'trace'
197
- || audience === 'internal'
198
- || source === 'runtime_status'
199
- || source === 'provider_chrome'
200
- || source === 'control'
201
- || record.internal === true
202
- || record.isInternal === true
203
- || record.debug === true
204
- || meta?.internal === true
205
- || meta?.isInternal === true
206
- || meta?.debug === true
207
- || meta?.statusOnly === true
208
- || meta?.controlOnly === true;
209
- }
210
-
211
- function isExplicitlyVisibleInTranscript(message: ChatMessage, meta: Record<string, unknown> | null): boolean {
212
- const record = message as ChatMessage & Record<string, unknown>;
213
- const visibility = readStringField(record.visibility || meta?.visibility || meta?.transcriptVisibility);
214
- const audience = readStringField(record.audience || meta?.audience);
215
- return visibility === 'visible'
216
- || visibility === 'user'
217
- || audience === 'chat'
218
- || record.userFacing === true
219
- || meta?.userFacing === true;
189
+ || meta.internal === true
190
+ || meta.debug === true
191
+ || meta.statusOnly === true
192
+ || meta.controlOnly === true;
193
+ }
194
+
195
+ function isExplicitlyVisibleInTranscript(meta: Record<string, unknown> | null): boolean {
196
+ if (!meta) return false;
197
+ const visibility = typeof meta.transcriptVisibility === 'string'
198
+ ? meta.transcriptVisibility.trim().toLowerCase()
199
+ : '';
200
+ return visibility === 'visible' || meta.userFacing === true;
220
201
  }
221
202
 
222
203
  /**
@@ -231,8 +212,8 @@ function isExplicitlyVisibleInTranscript(message: ChatMessage, meta: Record<stri
231
212
  export function isUserFacingChatMessage(message: ChatMessage | null | undefined): boolean {
232
213
  if (!message) return false;
233
214
  const meta = readMessageMeta(message);
234
- if (isExplicitlyHiddenFromTranscript(message, meta)) return false;
235
- if (isExplicitlyVisibleInTranscript(message, meta)) return true;
215
+ if (isExplicitlyHiddenFromTranscript(meta)) return false;
216
+ if (isExplicitlyVisibleInTranscript(meta)) return true;
236
217
 
237
218
  const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
238
219
  const kind = resolveChatMessageKind(message);
@@ -25,7 +25,7 @@ import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.
25
25
  import { getCliScriptCommand, parseCliScriptResult } from './cli-script-results.js';
26
26
  import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
27
27
  import { normalizeProviderSessionId } from './provider-session-id.js';
28
- import { buildChatMessage, buildRuntimeSystemChatMessage, normalizeChatMessages } from './chat-message-normalization.js';
28
+ import { buildChatMessage, buildRuntimeSystemChatMessage, normalizeChatMessages, resolveChatMessageKind } from './chat-message-normalization.js';
29
29
 
30
30
  type PersistableCliHistoryMessage = {
31
31
  role: string;
@@ -1007,7 +1007,7 @@ export class CliProviderInstance implements ProviderInstance {
1007
1007
  private mergeConversationMessages(parsedMessages: any[]): ChatMessage[] {
1008
1008
  if (this.runtimeMessages.length === 0) return normalizeChatMessages(parsedMessages);
1009
1009
 
1010
- type MergeEntry = { message: ChatMessage; index: number; source: 'parsed' | 'runtime' };
1010
+ type MergeEntry = { message: ChatMessage; index: number; source: 'parsed' | 'runtime'; runtimeKey?: string };
1011
1011
  const parsedEntries: MergeEntry[] = parsedMessages.map((message, index) => ({
1012
1012
  message,
1013
1013
  index,
@@ -1017,6 +1017,7 @@ export class CliProviderInstance implements ProviderInstance {
1017
1017
  message: entry.message,
1018
1018
  index: parsedMessages.length + index,
1019
1019
  source: 'runtime',
1020
+ runtimeKey: entry.key,
1020
1021
  }));
1021
1022
  const getTime = (message: ChatMessage): number => {
1022
1023
  const value = typeof message.receivedAt === 'number'
@@ -1027,16 +1028,52 @@ export class CliProviderInstance implements ProviderInstance {
1027
1028
  return Number.isFinite(value) && value > 0 ? value : 0;
1028
1029
  };
1029
1030
 
1031
+ const getRole = (message: ChatMessage): string => typeof message.role === 'string'
1032
+ ? message.role.trim().toLowerCase()
1033
+ : '';
1034
+ const isAutoApprovalRuntimeOverlay = (entry: MergeEntry): boolean => {
1035
+ if (entry.source !== 'runtime') return false;
1036
+ const key = typeof entry.runtimeKey === 'string' ? entry.runtimeKey.trim().toLowerCase() : '';
1037
+ if (key.startsWith('auto_approval:')) return true;
1038
+ const content = typeof entry.message.content === 'string'
1039
+ ? entry.message.content.trim().toLowerCase()
1040
+ : flattenContent(entry.message.content).trim().toLowerCase();
1041
+ return content.startsWith('auto-approved:');
1042
+ };
1043
+ const shouldKeepParsedBeforeUntimedRuntime = (message: ChatMessage): boolean => {
1044
+ const role = getRole(message);
1045
+ return role === 'user' || role === 'human';
1046
+ };
1047
+ const shouldKeepParsedAfterUntimedRuntime = (message: ChatMessage): boolean => {
1048
+ const role = getRole(message);
1049
+ if (role !== 'assistant') return false;
1050
+ const kind = resolveChatMessageKind(message);
1051
+ return kind === 'standard' || kind === 'terminal';
1052
+ };
1053
+
1030
1054
  return normalizeChatMessages([...parsedEntries, ...runtimeEntries]
1031
1055
  .sort((a, b) => {
1032
1056
  const aTime = getTime(a.message);
1033
1057
  const bTime = getTime(b.message);
1034
1058
  if (aTime && bTime && aTime !== bTime) return aTime - bTime;
1059
+ if (a.source !== b.source && aTime !== bTime) {
1060
+ const parsedEntry = a.source === 'parsed' ? a : b.source === 'parsed' ? b : null;
1061
+ const runtimeEntry = a.source === 'runtime' ? a : b.source === 'runtime' ? b : null;
1062
+ if (parsedEntry && runtimeEntry && isAutoApprovalRuntimeOverlay(runtimeEntry) && getTime(parsedEntry.message) === 0 && getTime(runtimeEntry.message) > 0) {
1063
+ if (shouldKeepParsedBeforeUntimedRuntime(parsedEntry.message)) {
1064
+ return a.source === 'parsed' ? -1 : 1;
1065
+ }
1066
+ if (shouldKeepParsedAfterUntimedRuntime(parsedEntry.message)) {
1067
+ return a.source === 'parsed' ? 1 : -1;
1068
+ }
1069
+ }
1070
+ }
1035
1071
  // Many provider-owned CLI transcripts (including Hermes CLI in debug bundles)
1036
1072
  // do not carry timestamps on parsed messages. In that case there is no safe
1037
1073
  // clock basis for interleaving timestamped runtime/system messages into the
1038
- // provider transcript, so preserve parsed order and append runtime entries by
1039
- // their insertion index instead of moving them ahead of the whole transcript.
1074
+ // provider transcript. Keep user prompts before runtime overlays, but do not
1075
+ // let timed runtime/system overlays become the final chat turns after an
1076
+ // untimed parsed assistant transcript.
1040
1077
  return a.index - b.index;
1041
1078
  })
1042
1079
  .map((entry) => entry.message));