@adhdev/daemon-core 0.8.23 → 0.8.25

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.
@@ -0,0 +1,22 @@
1
+ import type { SessionRegistry } from './registry.js';
2
+ interface IdeLikeInstance {
3
+ category?: string;
4
+ type?: string;
5
+ getInstanceId?: () => string;
6
+ getExtensionInstances?: () => Array<{
7
+ type?: string;
8
+ getInstanceId?: () => string;
9
+ }>;
10
+ }
11
+ interface InstanceManagerLike {
12
+ listInstanceIds(): string[];
13
+ getInstance(id: string): IdeLikeInstance | undefined;
14
+ }
15
+ /**
16
+ * Rebuild missing IDE/runtime session registry entries from live ProviderInstance objects.
17
+ *
18
+ * This is a defensive repair path for cases where an IDE extension instance still exists
19
+ * in InstanceManager/status, but its runtime session entry has been dropped from SessionRegistry.
20
+ */
21
+ export declare function reconcileIdeRuntimeSessions(instanceManager: InstanceManagerLike | undefined, sessionRegistry: SessionRegistry | undefined): void;
22
+ export {};
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.8.23",
3
+ "version": "0.8.25",
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.8.23",
3
+ "version": "0.8.25",
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",
@@ -16,6 +16,7 @@ import type { ProviderLoader } from '../providers/provider-loader.js';
16
16
  import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
17
17
  import { registerExtensionProviders } from '../cdp/setup.js';
18
18
  import type { SessionRegistry } from '../sessions/registry.js';
19
+ import { reconcileIdeRuntimeSessions } from '../sessions/reconcile.js';
19
20
  import { LOG } from '../logging/logger.js';
20
21
  import type { AgentStreamState } from './types.js';
21
22
 
@@ -78,6 +79,9 @@ export class AgentStreamPoller {
78
79
 
79
80
  if (!agentStreamManager || cdpManagers.size === 0) return;
80
81
 
82
+ // Defensive repair: keep SessionRegistry aligned with live IDE/extension instances.
83
+ reconcileIdeRuntimeSessions(instanceManager as any, sessionRegistry);
84
+
81
85
  // ─── Phase 1: Refresh extension providers + IDE instance extensions ───
82
86
  for (const [ideType, cdp] of cdpManagers) {
83
87
  // 1a. Refresh CDP manager's extension providers from config
@@ -21,6 +21,7 @@ import type { DaemonAgentStreamManager } from '../agent-stream/index.js';
21
21
  import { loadConfig } from '../config/config.js';
22
22
  import { ChatHistoryWriter } from '../config/chat-history.js';
23
23
  import type { SessionRegistry, SessionRuntimeTarget } from '../sessions/registry.js';
24
+ import { reconcileIdeRuntimeSessions } from '../sessions/reconcile.js';
24
25
  import { LOG } from '../logging/logger.js';
25
26
 
26
27
  // Sub-module imports
@@ -165,6 +166,7 @@ export class DaemonCommandHandler implements CommandHelpers {
165
166
  session?: SessionRuntimeTarget;
166
167
  managerKey?: string;
167
168
  providerType?: string;
169
+ sessionLookupFailed?: boolean;
168
170
  } = {};
169
171
 
170
172
  constructor(ctx: CommandContext) {
@@ -284,23 +286,36 @@ export class DaemonCommandHandler implements CommandHelpers {
284
286
  return key.split('_')[0];
285
287
  }
286
288
 
287
- private resolveRoute(args: any): { session?: SessionRuntimeTarget; managerKey?: string; providerType?: string } {
288
- const session = this._ctx.sessionRegistry?.get(args?.targetSessionId);
289
- const managerKey = this.extractIdeType(args);
290
- const providerType =
291
- args?.agentType
292
- || args?.providerType
293
- || session?.providerType
294
- || this.inferProviderType(managerKey);
295
- return { session, managerKey, providerType };
289
+ private resolveRoute(args: any): { session?: SessionRuntimeTarget; managerKey?: string; providerType?: string; sessionLookupFailed?: boolean } {
290
+ const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
291
+ let session = targetSessionId ? this._ctx.sessionRegistry?.get(targetSessionId) : undefined;
292
+ if (targetSessionId && !session) {
293
+ reconcileIdeRuntimeSessions(this._ctx.instanceManager as any, this._ctx.sessionRegistry);
294
+ session = this._ctx.sessionRegistry?.get(targetSessionId);
295
+ }
296
+ const sessionLookupFailed = !!targetSessionId && !session;
297
+
298
+ const managerKey = this.extractIdeType(args, sessionLookupFailed);
299
+ let providerType: string | undefined;
300
+
301
+ if (!sessionLookupFailed) {
302
+ providerType =
303
+ session?.providerType
304
+ || args?.agentType
305
+ || args?.providerType
306
+ || this.inferProviderType(managerKey);
307
+ }
308
+
309
+ return { session, managerKey, providerType, sessionLookupFailed };
296
310
  }
297
311
 
298
312
  /** Extract CDP scope key from target session or explicit ideType */
299
- private extractIdeType(args: any): string | undefined {
313
+ private extractIdeType(args: any, sessionLookupFailed = false): string | undefined {
300
314
  if (args?.targetSessionId) {
301
315
  const target = this._ctx.sessionRegistry?.get(args.targetSessionId);
302
316
  if (target?.cdpManagerKey) return target.cdpManagerKey;
303
317
  if (this._ctx.cdpManagers.has(args.targetSessionId)) return args.targetSessionId;
318
+ if (sessionLookupFailed) return undefined;
304
319
  }
305
320
 
306
321
  // Also accept explicit ideType from args (P2P input, agentType for extensions)
@@ -360,6 +375,35 @@ export class DaemonCommandHandler implements CommandHelpers {
360
375
  const startedAt = Date.now();
361
376
  this.logCommandStart(cmd, args);
362
377
 
378
+ const sessionScopedCommands = new Set([
379
+ 'read_chat',
380
+ 'send_chat',
381
+ 'list_chats',
382
+ 'new_chat',
383
+ 'switch_chat',
384
+ 'set_mode',
385
+ 'change_model',
386
+ 'set_thought_level',
387
+ 'resolve_action',
388
+ 'focus_session',
389
+ 'pty_input',
390
+ 'pty_resize',
391
+ 'invoke_provider_script',
392
+ 'list_extension_models',
393
+ 'set_extension_model',
394
+ 'list_extension_modes',
395
+ 'set_extension_mode',
396
+ ]);
397
+
398
+ if (this._currentRoute.sessionLookupFailed && sessionScopedCommands.has(cmd)) {
399
+ const result = {
400
+ success: false,
401
+ error: `Live session not found for targetSessionId: ${String(args?.targetSessionId || '').trim() || 'unknown'}`,
402
+ };
403
+ this.logCommandEnd(cmd, result, startedAt);
404
+ return result;
405
+ }
406
+
363
407
  // Commands without ideType CDP silently fail (prevent P2P retry spam)
364
408
  let result: CommandResult;
365
409
  if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
@@ -0,0 +1,85 @@
1
+ import type { SessionRegistry, SessionRuntimeTarget } from './registry.js';
2
+
3
+ interface IdeLikeInstance {
4
+ category?: string;
5
+ type?: string;
6
+ getInstanceId?: () => string;
7
+ getExtensionInstances?: () => Array<{
8
+ type?: string;
9
+ getInstanceId?: () => string;
10
+ }>;
11
+ }
12
+
13
+ interface InstanceManagerLike {
14
+ listInstanceIds(): string[];
15
+ getInstance(id: string): IdeLikeInstance | undefined;
16
+ }
17
+
18
+ function upsertSessionTarget(sessionRegistry: SessionRegistry, target: SessionRuntimeTarget): void {
19
+ const existing = sessionRegistry.get(target.sessionId);
20
+ if (
21
+ existing
22
+ && existing.parentSessionId === target.parentSessionId
23
+ && existing.providerType === target.providerType
24
+ && existing.transport === target.transport
25
+ && existing.cdpManagerKey === target.cdpManagerKey
26
+ && existing.instanceKey === target.instanceKey
27
+ ) {
28
+ return;
29
+ }
30
+ sessionRegistry.register(target);
31
+ }
32
+
33
+ /**
34
+ * Rebuild missing IDE/runtime session registry entries from live ProviderInstance objects.
35
+ *
36
+ * This is a defensive repair path for cases where an IDE extension instance still exists
37
+ * in InstanceManager/status, but its runtime session entry has been dropped from SessionRegistry.
38
+ */
39
+ export function reconcileIdeRuntimeSessions(
40
+ instanceManager: InstanceManagerLike | undefined,
41
+ sessionRegistry: SessionRegistry | undefined,
42
+ ): void {
43
+ if (!instanceManager || !sessionRegistry) return;
44
+
45
+ for (const instanceKey of instanceManager.listInstanceIds()) {
46
+ if (!instanceKey.startsWith('ide:')) continue;
47
+
48
+ const ideInstance = instanceManager.getInstance(instanceKey);
49
+ if (!ideInstance || ideInstance.category !== 'ide' || typeof ideInstance.getInstanceId !== 'function') {
50
+ continue;
51
+ }
52
+
53
+ const managerKey = instanceKey.slice(4);
54
+ const ideType = typeof ideInstance.type === 'string' && ideInstance.type.trim()
55
+ ? ideInstance.type.trim()
56
+ : managerKey.split('_')[0];
57
+ const parentSessionId = ideInstance.getInstanceId();
58
+ if (!parentSessionId) continue;
59
+
60
+ upsertSessionTarget(sessionRegistry, {
61
+ sessionId: parentSessionId,
62
+ parentSessionId: null,
63
+ providerType: ideType,
64
+ transport: 'cdp-page',
65
+ cdpManagerKey: managerKey,
66
+ instanceKey,
67
+ });
68
+
69
+ const extensions = ideInstance.getExtensionInstances?.() || [];
70
+ for (const ext of extensions) {
71
+ const extType = typeof ext?.type === 'string' ? ext.type.trim() : '';
72
+ const extSessionId = ext?.getInstanceId?.();
73
+ if (!extType || !extSessionId) continue;
74
+
75
+ upsertSessionTarget(sessionRegistry, {
76
+ sessionId: extSessionId,
77
+ parentSessionId,
78
+ providerType: extType,
79
+ transport: 'cdp-webview',
80
+ cdpManagerKey: managerKey,
81
+ instanceKey,
82
+ });
83
+ }
84
+ }
85
+ }