@adhdev/daemon-core 0.7.29 → 0.7.31

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.7.29",
3
+ "version": "0.7.31",
4
4
  "description": "ADHDev local session host core — session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.7.29",
3
+ "version": "0.7.31",
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",
@@ -108,7 +108,7 @@ export class DaemonAgentStreamManager {
108
108
 
109
109
  private resolveSessionIdForTarget(parentSessionId: string, agentType: string): string | null {
110
110
  const child = (this.sessionRegistry?.listChildren(parentSessionId) || [])
111
- .find((entry) => entry.providerCategory === 'extension' && entry.providerType === agentType);
111
+ .find((entry) => entry.transport === 'cdp-webview' && entry.providerType === agentType);
112
112
  return child?.sessionId || null;
113
113
  }
114
114
 
@@ -118,7 +118,7 @@ export class DaemonAgentStreamManager {
118
118
  runtimeSessionId: string,
119
119
  ): Promise<ManagedAgent | null> {
120
120
  const target = this.getSessionTarget(runtimeSessionId);
121
- if (!target || target.providerCategory !== 'extension') return null;
121
+ if (!target || target.transport !== 'cdp-webview') return null;
122
122
  const adapter = this.adaptersByType.get(target.providerType);
123
123
  if (!adapter) return null;
124
124
  const targets = await cdp.discoverAgentWebviews();
@@ -117,7 +117,6 @@ export class AgentStreamPoller {
117
117
  sessionId: extInstance.getInstanceId(),
118
118
  parentSessionId,
119
119
  providerType: extType,
120
- providerCategory: 'extension',
121
120
  transport: 'cdp-webview',
122
121
  cdpManagerKey: ideType,
123
122
  instanceKey: `ide:${ideType}`,
package/src/cdp/setup.ts CHANGED
@@ -95,7 +95,6 @@ export async function setupIdeInstance(
95
95
  sessionId: ideInstance.getInstanceId(),
96
96
  parentSessionId: null,
97
97
  providerType: ideType,
98
- providerCategory: 'ide',
99
98
  transport: 'cdp-page',
100
99
  cdpManagerKey: managerKey,
101
100
  instanceKey: `ide:${managerKey}`,
@@ -113,7 +112,6 @@ export async function setupIdeInstance(
113
112
  sessionId: ext.getInstanceId(),
114
113
  parentSessionId: ideInstance.getInstanceId(),
115
114
  providerType: ext.type,
116
- providerCategory: 'extension',
117
115
  transport: 'cdp-webview',
118
116
  cdpManagerKey: managerKey,
119
117
  instanceKey: `ide:${managerKey}`,
@@ -6,6 +6,7 @@
6
6
  * swap in libghostty-vt once a native Node binding is available.
7
7
  */
8
8
 
9
+ import { LOG } from '../logging/logger.js';
9
10
  import { GhosttyVtTerminalBackend, isGhosttyVtBackendAvailable, resolveTerminalBackendPreference } from './terminal-backends/ghostty-vt-backend.js';
10
11
  import type {
11
12
  TerminalViewportBackend,
@@ -16,20 +17,42 @@ import type {
16
17
  import { XtermTerminalBackend } from './terminal-backends/xterm-backend.js';
17
18
 
18
19
  const DEFAULT_SCROLLBACK = 2000;
20
+ const loggedTerminalBackends = new Set<string>();
19
21
 
20
22
  function createTerminalBackend(
21
23
  options: TerminalViewportBackendOptions,
22
24
  preference: TerminalViewportBackendPreference,
23
25
  ): TerminalViewportBackend {
26
+ const ghosttyAvailable = isGhosttyVtBackendAvailable();
24
27
  if (preference === 'ghostty-vt') {
25
- return new GhosttyVtTerminalBackend(options);
28
+ const backend = new GhosttyVtTerminalBackend(options);
29
+ logTerminalBackendSelection(preference, ghosttyAvailable, backend.kind);
30
+ return backend;
26
31
  }
27
32
 
28
- if (preference === 'auto' && isGhosttyVtBackendAvailable()) {
29
- return new GhosttyVtTerminalBackend(options);
33
+ if (preference === 'auto' && ghosttyAvailable) {
34
+ const backend = new GhosttyVtTerminalBackend(options);
35
+ logTerminalBackendSelection(preference, ghosttyAvailable, backend.kind);
36
+ return backend;
30
37
  }
31
38
 
32
- return new XtermTerminalBackend(options);
39
+ const backend = new XtermTerminalBackend(options);
40
+ logTerminalBackendSelection(preference, ghosttyAvailable, backend.kind);
41
+ return backend;
42
+ }
43
+
44
+ function logTerminalBackendSelection(
45
+ preference: TerminalViewportBackendPreference,
46
+ ghosttyAvailable: boolean,
47
+ backendKind: TerminalViewportBackendKind,
48
+ ): void {
49
+ const key = `${preference}:${ghosttyAvailable}:${backendKind}`;
50
+ if (loggedTerminalBackends.has(key)) return;
51
+ loggedTerminalBackends.add(key);
52
+ LOG.info(
53
+ 'Terminal',
54
+ `[terminal-screen] backend=${backendKind} preference=${preference} ghosttyAvailable=${ghosttyAvailable}`,
55
+ );
33
56
  }
34
57
 
35
58
  export class TerminalScreen {
@@ -6,6 +6,7 @@
6
6
  import type { CommandResult, CommandHelpers } from './handler.js';
7
7
  import { readChatHistory } from '../config/chat-history.js';
8
8
  import { LOG } from '../logging/logger.js';
9
+ import type { SessionTransport } from '../shared-types.js';
9
10
 
10
11
  const RECENT_SEND_WINDOW_MS = 1200;
11
12
  const recentSendByTarget = new Map<string, number>();
@@ -22,7 +23,32 @@ function getTargetedCliAdapter(h: CommandHelpers, args: any, providerType?: stri
22
23
  return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
23
24
  }
24
25
 
26
+ function getTargetTransport(h: CommandHelpers, provider?: any): SessionTransport | null {
27
+ if (h.currentSession?.transport) return h.currentSession.transport;
28
+ switch (provider?.category) {
29
+ case 'cli':
30
+ return 'pty';
31
+ case 'acp':
32
+ return 'acp';
33
+ case 'extension':
34
+ return 'cdp-webview';
35
+ case 'ide':
36
+ return 'cdp-page';
37
+ default:
38
+ return null;
39
+ }
40
+ }
41
+
42
+ function isCliLikeTransport(transport: SessionTransport | null): boolean {
43
+ return transport === 'pty' || transport === 'acp';
44
+ }
45
+
46
+ function isExtensionTransport(transport: SessionTransport | null): boolean {
47
+ return transport === 'cdp-webview';
48
+ }
49
+
25
50
  function buildRecentSendKey(h: CommandHelpers, args: any, provider: any, text: string): string {
51
+ const transport = getTargetTransport(h, provider) || 'unknown';
26
52
  const target =
27
53
  args?.targetSessionId
28
54
  || args?.agentType
@@ -30,7 +56,7 @@ function buildRecentSendKey(h: CommandHelpers, args: any, provider: any, text: s
30
56
  || h.currentProviderType
31
57
  || h.currentManagerKey
32
58
  || 'unknown';
33
- return `${provider?.category || 'unknown'}:${target}:${text.trim()}`;
59
+ return `${transport}:${target}:${text.trim()}`;
34
60
  }
35
61
 
36
62
  function isRecentDuplicateSend(key: string): boolean {
@@ -59,14 +85,15 @@ export async function handleChatHistory(h: CommandHelpers, args: any): Promise<C
59
85
 
60
86
  export async function handleReadChat(h: CommandHelpers, args: any): Promise<CommandResult> {
61
87
  const provider = h.getProvider(args?.agentType);
88
+ const transport = getTargetTransport(h, provider);
62
89
 
63
90
  const _log = (msg: string) => LOG.debug('Command', `[read_chat] ${msg}`);
64
91
 
65
- // CLI / ACP category: read from adapter
66
- if (provider?.category === 'cli' || provider?.category === 'acp') {
67
- const adapter = getTargetedCliAdapter(h, args, provider.type);
92
+ // PTY / ACP transport: read from adapter
93
+ if (isCliLikeTransport(transport)) {
94
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
68
95
  if (adapter) {
69
- _log(`${provider.category} adapter: ${(adapter as any).cliType}`);
96
+ _log(`${transport} adapter: ${(adapter as any).cliType}`);
70
97
  const status = (adapter as any).getStatus?.();
71
98
  if (status) {
72
99
  return {
@@ -74,15 +101,14 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
74
101
  messages: status.messages || [],
75
102
  status: status.status,
76
103
  activeModal: status.activeModal,
77
- terminalHistory: status.terminalHistory || '',
78
104
  };
79
105
  }
80
106
  }
81
- return { success: false, error: `${provider.category} adapter not found` };
107
+ return { success: false, error: `${transport} adapter not found` };
82
108
  }
83
109
 
84
- // Extension category: evaluateInSession
85
- if (provider?.category === 'extension') {
110
+ // Extension transport: evaluateInSession
111
+ if (isExtensionTransport(transport)) {
86
112
  try {
87
113
  const evalResult = await h.evaluateProviderScript('readChat', undefined, 50000);
88
114
  if (evalResult?.result) {
@@ -91,7 +117,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
91
117
  if (parsed && typeof parsed === 'object') {
92
118
  _log(`Extension OK: ${parsed.messages?.length || 0} msgs`);
93
119
  h.historyWriter.appendNewMessages(
94
- provider.type || 'unknown_extension',
120
+ provider?.type || 'unknown_extension',
95
121
  parsed.messages || [],
96
122
  parsed.title,
97
123
  args?.targetSessionId
@@ -108,7 +134,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
108
134
  const parentSessionId = h.currentSession?.parentSessionId;
109
135
  if (cdp && parentSessionId) {
110
136
  const stream = await h.agentStream.collectActiveSession(cdp, parentSessionId);
111
- if (stream?.agentType !== provider.type) {
137
+ if (stream?.agentType !== provider?.type) {
112
138
  return { success: true, messages: [], status: 'idle' };
113
139
  }
114
140
  if (stream) {
@@ -188,6 +214,7 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
188
214
  if (!text) return { success: false, error: 'text required' };
189
215
  const _log = (msg: string) => LOG.debug('Command', `[send_chat] ${msg}`);
190
216
  const provider = h.getProvider(args?.agentType);
217
+ const transport = getTargetTransport(h, provider);
191
218
  const dedupeKey = buildRecentSendKey(h, args, provider, text);
192
219
 
193
220
  const _logSendSuccess = (method: string, targetAgent?: string) => {
@@ -205,23 +232,23 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
205
232
  return { success: true, sent: false, deduplicated: true };
206
233
  }
207
234
 
208
- // CLI / ACP category: transmit via adapter
209
- if (provider?.category === 'cli' || provider?.category === 'acp') {
210
- const adapter = getTargetedCliAdapter(h, args, provider.type);
235
+ // PTY / ACP transport: transmit via adapter
236
+ if (isCliLikeTransport(transport)) {
237
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
211
238
  if (adapter) {
212
- _log(`${provider.category} adapter: ${(adapter as any).cliType}`);
239
+ _log(`${transport} adapter: ${(adapter as any).cliType}`);
213
240
  try {
214
241
  await adapter.sendMessage(text);
215
- return _logSendSuccess(`${provider.category}-adapter`, (adapter as any).cliType);
242
+ return _logSendSuccess(`${transport}-adapter`, (adapter as any).cliType);
216
243
  } catch (e: any) {
217
- return { success: false, error: `${provider.category} send failed: ${e.message}` };
244
+ return { success: false, error: `${transport} send failed: ${e.message}` };
218
245
  }
219
246
  }
220
247
  }
221
248
 
222
- // Extension category: via AgentStreamManager
223
- if (provider?.category === 'extension') {
224
- _log(`Extension: ${provider.type}`);
249
+ // Extension transport: via AgentStreamManager
250
+ if (isExtensionTransport(transport)) {
251
+ _log(`Extension: ${provider?.type || 'unknown_extension'}`);
225
252
  // Method 1: provider sendMessage script via evaluateInSession
226
253
  try {
227
254
  const evalResult = await h.evaluateProviderScript('sendMessage', { MESSAGE: text }, 30000);
@@ -248,7 +275,7 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
248
275
  return _logSendSuccess('agent-stream');
249
276
  }
250
277
  }
251
- return { success: false, error: `Extension '${provider.type}' send failed` };
278
+ return { success: false, error: `Extension '${provider?.type || 'unknown_extension'}' send failed` };
252
279
  }
253
280
 
254
281
  // IDE category (default): provider sendMessage script is authoritative when present.
@@ -366,9 +393,10 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
366
393
 
367
394
  export async function handleListChats(h: CommandHelpers, args: any): Promise<CommandResult> {
368
395
  const provider = h.getProvider(args?.agentType);
396
+ const transport = getTargetTransport(h, provider);
369
397
 
370
- // Extension: via AgentStreamManager
371
- if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
398
+ // Extension transport: via AgentStreamManager
399
+ if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
372
400
  try {
373
401
  const chats = await h.agentStream.listSessionChats(h.getCdp()!, h.currentSession.sessionId);
374
402
  LOG.info('Command', `[list_chats] Extension: ${chats.length} chats`);
@@ -416,9 +444,10 @@ export async function handleListChats(h: CommandHelpers, args: any): Promise<Com
416
444
 
417
445
  export async function handleNewChat(h: CommandHelpers, args: any): Promise<CommandResult> {
418
446
  const provider = h.getProvider(args?.agentType);
447
+ const transport = getTargetTransport(h, provider);
419
448
 
420
- if (provider?.category === 'cli') {
421
- const adapter = getTargetedCliAdapter(h, args, provider.type);
449
+ if (transport === 'pty') {
450
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
422
451
  if (!adapter) return { success: false, error: 'CLI adapter not running' };
423
452
  if (typeof (adapter as any).clearHistory === 'function') {
424
453
  (adapter as any).clearHistory();
@@ -427,7 +456,7 @@ export async function handleNewChat(h: CommandHelpers, args: any): Promise<Comma
427
456
  return { success: false, error: 'new_chat not supported by this CLI provider' };
428
457
  }
429
458
 
430
- if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
459
+ if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
431
460
  const ok = await h.agentStream.newSession(h.getCdp()!, h.currentSession.sessionId);
432
461
  return { success: ok };
433
462
  }
@@ -457,12 +486,13 @@ export async function handleNewChat(h: CommandHelpers, args: any): Promise<Comma
457
486
 
458
487
  export async function handleSwitchChat(h: CommandHelpers, args: any): Promise<CommandResult> {
459
488
  const provider = h.getProvider(args?.agentType);
489
+ const transport = getTargetTransport(h, provider);
460
490
  const managerKey = getCurrentManagerKey(h);
461
491
  const sessionId = args?.sessionId || args?.id || args?.chatId;
462
492
  if (!sessionId) return { success: false, error: 'sessionId required' };
463
493
  LOG.info('Command', `[switch_chat] sessionId=${sessionId}, manager=${managerKey}`);
464
494
 
465
- if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
495
+ if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
466
496
  const ok = await h.agentStream.switchConversation(h.getCdp()!, h.currentSession.sessionId, sessionId);
467
497
  return { success: ok, result: ok ? 'switched' : 'failed' };
468
498
  }
@@ -544,11 +574,12 @@ export async function handleSwitchChat(h: CommandHelpers, args: any): Promise<Co
544
574
 
545
575
  export async function handleSetMode(h: CommandHelpers, args: any): Promise<CommandResult> {
546
576
  const provider = h.getProvider(args?.agentType);
577
+ const transport = getTargetTransport(h, provider);
547
578
  const mode = args?.mode || 'agent';
548
579
 
549
- // ACP provider
550
- if (provider?.category === 'acp') {
551
- const adapter = getTargetedCliAdapter(h, args, provider.type);
580
+ // ACP transport
581
+ if (transport === 'acp') {
582
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
552
583
  if (adapter) {
553
584
  const acpInstance = (adapter as any)._acpInstance;
554
585
  if (acpInstance && typeof acpInstance.onEvent === 'function') {
@@ -597,13 +628,14 @@ export async function handleSetMode(h: CommandHelpers, args: any): Promise<Comma
597
628
 
598
629
  export async function handleChangeModel(h: CommandHelpers, args: any): Promise<CommandResult> {
599
630
  const provider = h.getProvider(args?.agentType);
631
+ const transport = getTargetTransport(h, provider);
600
632
  const model = args?.model;
601
633
 
602
- LOG.info('Command', `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} manager=${getCurrentManagerKey(h)} providerType=${getCurrentProviderType(h)}`);
634
+ LOG.info('Command', `[change_model] model=${model} provider=${provider?.type} transport=${transport} manager=${getCurrentManagerKey(h)} providerType=${getCurrentProviderType(h)}`);
603
635
 
604
- // ACP provider
605
- if (provider?.category === 'acp') {
606
- const adapter = getTargetedCliAdapter(h, args, provider.type);
636
+ // ACP transport
637
+ if (transport === 'acp') {
638
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
607
639
  LOG.info('Command', `[change_model] ACP adapter found: ${!!adapter}, type=${(adapter as any)?.cliType}, hasAcpInstance=${!!(adapter as any)?._acpInstance}`);
608
640
  if (adapter) {
609
641
  const acpInstance = (adapter as any)._acpInstance;
@@ -658,16 +690,17 @@ export async function handleSetThoughtLevel(h: CommandHelpers, args: any): Promi
658
690
  if (!configId || !value) return { success: false, error: 'configId and value required' };
659
691
 
660
692
  const provider = h.getProvider(args?.agentType);
661
- if (!provider || provider.category !== 'acp') {
693
+ const transport = getTargetTransport(h, provider);
694
+ if (transport !== 'acp') {
662
695
  return { success: false, error: 'set_thought_level only for ACP providers' };
663
696
  }
664
- const adapter = getTargetedCliAdapter(h, args, provider.type);
697
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
665
698
  const acpInstance = adapter?._acpInstance;
666
699
  if (!acpInstance) return { success: false, error: 'ACP instance not found' };
667
700
 
668
701
  try {
669
702
  await acpInstance.setConfigOption(configId, value);
670
- LOG.info('Command', `[set_thought_level] ${configId}=${value} for ${provider.type}`);
703
+ LOG.info('Command', `[set_thought_level] ${configId}=${value} for ${provider?.type || 'unknown_acp'}`);
671
704
  return { success: true, configId, value };
672
705
  } catch (e: any) {
673
706
  return { success: false, error: e?.message };
@@ -676,15 +709,16 @@ export async function handleSetThoughtLevel(h: CommandHelpers, args: any): Promi
676
709
 
677
710
  export async function handleResolveAction(h: CommandHelpers, args: any): Promise<CommandResult> {
678
711
  const provider = h.getProvider(args?.agentType);
712
+ const transport = getTargetTransport(h, provider);
679
713
  const action = args?.action || 'approve';
680
714
  const button = args?.button || args?.buttonText
681
715
  || (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : 'Accept');
682
716
 
683
717
  LOG.info('Command', `[resolveAction] action=${action} button="${button}" provider=${provider?.type}`);
684
718
 
685
- // 0. CLI / ACP category: navigate approval dialog via PTY arrow keys + Enter
686
- if (provider?.category === 'cli') {
687
- const adapter = getTargetedCliAdapter(h, args, provider.type);
719
+ // 0. PTY transport: navigate approval dialog via PTY arrow keys + Enter
720
+ if (transport === 'pty') {
721
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
688
722
  if (!adapter) return { success: false, error: 'CLI adapter not running' };
689
723
 
690
724
  // Handle data-driven resolve actions (like from the dashboard 'Fix' button)
@@ -730,8 +764,8 @@ export async function handleResolveAction(h: CommandHelpers, args: any): Promise
730
764
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
731
765
  }
732
766
 
733
- // 1. Extension: via AgentStreamManager
734
- if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
767
+ // 1. Extension transport: via AgentStreamManager
768
+ if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
735
769
  const ok = await h.agentStream.resolveSessionAction(h.getCdp()!, h.currentSession.sessionId, action);
736
770
  return { success: ok };
737
771
  }
@@ -199,7 +199,6 @@ export class DaemonCliManager {
199
199
  sessionId: cliInstance.instanceId,
200
200
  parentSessionId: null,
201
201
  providerType: normalizedType,
202
- providerCategory: 'cli',
203
202
  transport: 'pty',
204
203
  adapterKey: key,
205
204
  instanceKey: key,
@@ -263,7 +262,6 @@ export class DaemonCliManager {
263
262
  sessionId,
264
263
  parentSessionId: null,
265
264
  providerType: normalizedType,
266
- providerCategory: 'acp',
267
265
  transport: 'acp',
268
266
  adapterKey: key,
269
267
  instanceKey: key,
@@ -171,7 +171,6 @@ export class ProviderInstanceManager {
171
171
  providerType,
172
172
  instanceId: state.instanceId,
173
173
  targetSessionId: state.instanceId,
174
- providerCategory: state.category,
175
174
  workspaceName: state.workspace || undefined,
176
175
  ...extra,
177
176
  });
@@ -4,7 +4,6 @@ export interface SessionRuntimeTarget {
4
4
  sessionId: string;
5
5
  parentSessionId: string | null;
6
6
  providerType: string;
7
- providerCategory: 'ide' | 'extension' | 'cli' | 'acp';
8
7
  transport: SessionTransport;
9
8
  cdpManagerKey?: string;
10
9
  adapterKey?: string;