@adhdev/daemon-core 0.9.47 → 0.9.49

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.
@@ -5,4 +5,5 @@ export declare function parseCliScriptResult(result: unknown): {
5
5
  export declare function getCliScriptCommand(payload: any): {
6
6
  type: string;
7
7
  text?: string;
8
+ enterCount?: number;
8
9
  } | null;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.9.47",
3
+ "version": "0.9.49",
4
4
  "description": "ADHDev local session host core \u2014 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.9.47",
3
+ "version": "0.9.49",
4
4
  "description": "ADHDev daemon core \u2014 CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -41,6 +41,7 @@ export interface CliAdapter {
41
41
  sendMessage(text: string): Promise<void>;
42
42
  getStatus(): CliAdapterStatus;
43
43
  getScriptParsedStatus?(): unknown;
44
+ getDebugSnapshot?(): unknown;
44
45
  invokeScript?(scriptName: string, args?: Record<string, unknown>): Promise<unknown>;
45
46
  getPartialResponse(): string;
46
47
  saveAndStop?(): Promise<void>;
@@ -2545,6 +2545,88 @@ export class ProviderCliAdapter implements CliAdapter {
2545
2545
  return this.responseBuffer;
2546
2546
  }
2547
2547
 
2548
+ getDebugSnapshot(): Record<string, unknown> {
2549
+ const screenText = this.readTerminalScreenText();
2550
+ const parsedResult = this.parsedStatusCache?.result && typeof this.parsedStatusCache.result === 'object'
2551
+ ? this.parsedStatusCache.result as Record<string, any>
2552
+ : null;
2553
+ return {
2554
+ cliType: this.cliType,
2555
+ cliName: this.cliName,
2556
+ workingDir: this.workingDir,
2557
+ currentStatus: this.currentStatus,
2558
+ ready: this.ready,
2559
+ isWaitingForResponse: this.isWaitingForResponse,
2560
+ activeModal: this.activeModal,
2561
+ parseErrorMessage: this.parseErrorMessage,
2562
+ messageCounts: {
2563
+ committed: this.committedMessages.length,
2564
+ structured: this.structuredMessages.length,
2565
+ visible: this.messages.length,
2566
+ parsedCache: Array.isArray(parsedResult?.messages) ? parsedResult.messages.length : undefined,
2567
+ },
2568
+ buffers: {
2569
+ accumulatedLength: this.accumulatedBuffer.length,
2570
+ accumulatedRawLength: this.accumulatedRawBuffer.length,
2571
+ recentOutputLength: this.recentOutputBuffer.length,
2572
+ responseLength: this.responseBuffer.length,
2573
+ startupLength: this.startupBuffer.length,
2574
+ accumulatedTail: this.accumulatedBuffer.slice(-24_000),
2575
+ accumulatedRawTail: this.accumulatedRawBuffer.slice(-24_000),
2576
+ recentOutputTail: this.recentOutputBuffer.slice(-12_000),
2577
+ responseTail: this.responseBuffer.slice(-12_000),
2578
+ },
2579
+ terminal: {
2580
+ screenText,
2581
+ lastScreenSnapshot: this.lastScreenSnapshot,
2582
+ lastScreenText: this.lastScreenText,
2583
+ lastOutputAt: this.lastOutputAt,
2584
+ lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
2585
+ lastScreenChangeAt: this.lastScreenChangeAt,
2586
+ lastScreenSnapshotReadAt: this.lastScreenSnapshotReadAt,
2587
+ },
2588
+ parser: {
2589
+ scriptNames: listCliScriptNames(this.cliScripts),
2590
+ traceSessionId: this.traceSessionId,
2591
+ traceSeq: this.traceSeq,
2592
+ currentTurnScope: this.currentTurnScope,
2593
+ parsedStatusCache: parsedResult
2594
+ ? {
2595
+ id: parsedResult.id,
2596
+ status: parsedResult.status,
2597
+ title: parsedResult.title,
2598
+ providerSessionId: parsedResult.providerSessionId,
2599
+ transcriptAuthority: parsedResult.transcriptAuthority,
2600
+ coverage: parsedResult.coverage,
2601
+ messageCount: Array.isArray(parsedResult.messages) ? parsedResult.messages.length : undefined,
2602
+ activeModal: parsedResult.activeModal,
2603
+ }
2604
+ : null,
2605
+ pendingScriptStatus: this.pendingScriptStatus,
2606
+ pendingScriptStatusSince: this.pendingScriptStatusSince,
2607
+ },
2608
+ runtimeMetadata: this.getRuntimeMetadata(),
2609
+ statusHistory: this.statusHistory.slice(-80),
2610
+ traceEntries: this.traceEntries.slice(-120),
2611
+ timing: {
2612
+ spawnAt: this.spawnAt,
2613
+ startupFirstOutputAt: this.startupFirstOutputAt,
2614
+ submitPendingUntil: this.submitPendingUntil,
2615
+ responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
2616
+ responseEpoch: this.responseEpoch,
2617
+ resizeSuppressUntil: this.resizeSuppressUntil,
2618
+ lastApprovalResolvedAt: this.lastApprovalResolvedAt,
2619
+ committedMessagesChangedAt: this.committedMessagesChangedAt,
2620
+ },
2621
+ finish: {
2622
+ idleFinishCandidate: this.idleFinishCandidate,
2623
+ finishRetryCount: this.finishRetryCount,
2624
+ submitRetryUsed: this.submitRetryUsed,
2625
+ submitRetryPromptSnippet: this.submitRetryPromptSnippet,
2626
+ },
2627
+ };
2628
+ }
2629
+
2548
2630
  getRuntimeMetadata(): PtyRuntimeMetadata | null {
2549
2631
  if (!this.ptyProcess || typeof this.ptyProcess.getMetadata !== 'function') return null;
2550
2632
  return this.ptyProcess.getMetadata();
@@ -277,7 +277,12 @@ export function findBinary(name: string): string {
277
277
  const isWin = os.platform() === 'win32';
278
278
  try {
279
279
  const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
280
- return execSync(cmd, { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim().split('\n')[0].trim();
280
+ return execSync(cmd, {
281
+ encoding: 'utf-8',
282
+ timeout: 5000,
283
+ stdio: ['pipe', 'pipe', 'pipe'],
284
+ ...(isWin ? { windowsHide: true } : {}),
285
+ }).trim().split('\n')[0].trim();
281
286
  } catch {
282
287
  return isWin ? `${trimmed}.cmd` : trimmed;
283
288
  }
@@ -10,8 +10,8 @@ import { assertProviderSupportsDeclaredInput, assertTextOnlyInput } from '../pro
10
10
  import { validateReadChatResultPayload } from '../providers/read-chat-contract.js';
11
11
  import type { ProviderInstance } from '../providers/provider-instance.js';
12
12
  import { readProviderChatHistory } from '../config/chat-history.js';
13
- import { LOG } from '../logging/logger.js';
14
- import { recordDebugTrace } from '../logging/debug-trace.js';
13
+ import { LOG, getRecentLogs } from '../logging/logger.js';
14
+ import { getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
15
15
  import { buildChatMessageSignature } from '../chat/chat-signatures.js';
16
16
  import type { ChatMessage } from '../types.js';
17
17
  import type { ReadChatCursor, ReadChatSyncMode, SessionTransport } from '../shared-types.js';
@@ -489,6 +489,271 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
489
489
  };
490
490
  }
491
491
 
492
+
493
+ interface DebugSanitizeOptions {
494
+ maxDepth?: number;
495
+ maxArrayLength?: number;
496
+ maxObjectKeys?: number;
497
+ maxStringLength?: number;
498
+ }
499
+
500
+ const DEFAULT_DEBUG_SANITIZE_OPTIONS: Required<DebugSanitizeOptions> = {
501
+ maxDepth: 8,
502
+ maxArrayLength: 80,
503
+ maxObjectKeys: 120,
504
+ maxStringLength: 16_000,
505
+ };
506
+
507
+ const SECRET_KEY_PATTERN = /(?:token|secret|password|passwd|authorization|cookie|api[_-]?key|access[_-]?key|refresh[_-]?token|client[_-]?secret|private[_-]?key)/i;
508
+
509
+ function truncateDebugString(value: string, maxLength: number): string {
510
+ if (value.length <= maxLength) return value;
511
+ return `${value.slice(0, maxLength)}…[truncated ${value.length - maxLength} chars]`;
512
+ }
513
+
514
+ function redactDebugSecrets(value: string): string {
515
+ return value
516
+ .replace(/(Authorization\s*:\s*Bearer\s+)[^\s'"`]+/gi, '$1[REDACTED:bearer]')
517
+ .replace(/(Bearer\s+)[A-Za-z0-9._~+\/-]{16,}=*/gi, '$1[REDACTED:bearer]')
518
+ .replace(/\b(?:gh[pousr]|github_pat)_[A-Za-z0-9_]{20,}\b/g, '[REDACTED:github-token]')
519
+ .replace(/\bsk-[A-Za-z0-9_-]{16,}\b/g, '[REDACTED:api-key]')
520
+ .replace(/\bxox[baprs]-[A-Za-z0-9-]{12,}\b/g, '[REDACTED:slack-token]')
521
+ .replace(/\b(?:adk|adm)_[A-Za-z0-9_-]{16,}\b/g, '[REDACTED:adhdev-token]')
522
+ .replace(/((?:api[_-]?key|token|secret|password|passwd|client[_-]?secret)\s*[:=]\s*)[^\s,'"`}&]+/gi, '$1[REDACTED:secret]')
523
+ .replace(/([?&](?:api[_-]?key|token|secret|password|client_secret)=)[^&#\s]+/gi, '$1[REDACTED:secret]');
524
+ }
525
+
526
+ export function sanitizeDebugBundleValue(
527
+ value: unknown,
528
+ options: DebugSanitizeOptions = {},
529
+ depth = 0,
530
+ keyHint = '',
531
+ ): unknown {
532
+ const normalizedOptions = { ...DEFAULT_DEBUG_SANITIZE_OPTIONS, ...options };
533
+ if (value === null || value === undefined) return value;
534
+ if (typeof value === 'number' || typeof value === 'boolean') return value;
535
+ if (typeof value === 'bigint') return String(value);
536
+ if (typeof value === 'string') {
537
+ if (SECRET_KEY_PATTERN.test(keyHint) && value.trim()) return '[REDACTED:secret-field]';
538
+ return truncateDebugString(redactDebugSecrets(value), normalizedOptions.maxStringLength);
539
+ }
540
+ if (typeof value === 'function') return `[Function ${value.name || 'anonymous'}]`;
541
+ if (typeof value !== 'object') return String(value);
542
+ if (depth >= normalizedOptions.maxDepth) return '[MaxDepth]';
543
+
544
+ if (Array.isArray(value)) {
545
+ const items = value
546
+ .slice(0, normalizedOptions.maxArrayLength)
547
+ .map((item) => sanitizeDebugBundleValue(item, normalizedOptions, depth + 1, keyHint));
548
+ if (value.length > normalizedOptions.maxArrayLength) {
549
+ items.push(`[truncated ${value.length - normalizedOptions.maxArrayLength} items]`);
550
+ }
551
+ return items;
552
+ }
553
+
554
+ const record = value as Record<string, unknown>;
555
+ const result: Record<string, unknown> = {};
556
+ const entries = Object.entries(record).slice(0, normalizedOptions.maxObjectKeys);
557
+ for (const [key, item] of entries) {
558
+ result[key] = sanitizeDebugBundleValue(item, normalizedOptions, depth + 1, key);
559
+ }
560
+ const remaining = Object.keys(record).length - entries.length;
561
+ if (remaining > 0) result.__truncatedKeys = remaining;
562
+ return result;
563
+ }
564
+
565
+ function summarizeProviderForDebug(provider: ProviderModule | undefined): Record<string, unknown> | null {
566
+ if (!provider) return null;
567
+ const scripts = provider.scripts && typeof provider.scripts === 'object'
568
+ ? Object.keys(provider.scripts)
569
+ : [];
570
+ const controls = Array.isArray((provider as any).controls)
571
+ ? (provider as any).controls.map((control: any) => ({
572
+ id: control?.id,
573
+ label: control?.label,
574
+ type: control?.type,
575
+ settingKey: control?.settingKey,
576
+ invokeScript: control?.invokeScript,
577
+ listScript: control?.listScript,
578
+ location: control?.location,
579
+ }))
580
+ : [];
581
+ return {
582
+ type: provider.type,
583
+ name: provider.name,
584
+ category: provider.category,
585
+ version: (provider as any).version,
586
+ canonicalHistory: provider.canonicalHistory,
587
+ historyBehavior: provider.historyBehavior,
588
+ webviewMatchText: provider.webviewMatchText,
589
+ scriptNames: scripts,
590
+ controls,
591
+ resume: provider.resume,
592
+ };
593
+ }
594
+
595
+ function summarizeSessionForDebug(session: any): Record<string, unknown> | null {
596
+ if (!session || typeof session !== 'object') return null;
597
+ return {
598
+ sessionId: session.sessionId,
599
+ instanceKey: session.instanceKey,
600
+ adapterKey: session.adapterKey,
601
+ providerType: session.providerType,
602
+ providerName: session.providerName,
603
+ transport: session.transport,
604
+ kind: session.kind,
605
+ cdpManagerKey: session.cdpManagerKey,
606
+ parentSessionId: session.parentSessionId,
607
+ providerSessionId: session.providerSessionId,
608
+ workspace: session.workspace,
609
+ title: session.title,
610
+ status: session.status,
611
+ mode: session.mode,
612
+ capabilities: session.capabilities,
613
+ };
614
+ }
615
+
616
+ function summarizeStateForDebug(state: any): Record<string, unknown> | null {
617
+ if (!state || typeof state !== 'object') return null;
618
+ const activeChat = state.activeChat && typeof state.activeChat === 'object' ? state.activeChat : null;
619
+ return {
620
+ type: state.type,
621
+ name: state.name,
622
+ category: state.category,
623
+ status: state.status,
624
+ instanceId: state.instanceId,
625
+ providerSessionId: state.providerSessionId,
626
+ title: state.title,
627
+ transport: state.transport,
628
+ mode: state.mode,
629
+ workspace: state.workspace,
630
+ runtime: state.runtime,
631
+ errorMessage: state.errorMessage,
632
+ errorReason: state.errorReason,
633
+ activeChat: activeChat ? {
634
+ status: activeChat.status,
635
+ title: activeChat.title,
636
+ messageCount: Array.isArray(activeChat.messages) ? activeChat.messages.length : undefined,
637
+ activeModal: activeChat.activeModal,
638
+ messagesTail: Array.isArray(activeChat.messages) ? activeChat.messages.slice(-10) : undefined,
639
+ } : null,
640
+ controlValues: state.controlValues,
641
+ summaryMetadata: state.summaryMetadata,
642
+ };
643
+ }
644
+
645
+ function buildDebugBundleText(bundle: Record<string, unknown>): string {
646
+ return [
647
+ '# ADHDev Chat Debug Bundle',
648
+ '',
649
+ '```json',
650
+ JSON.stringify(bundle, null, 2),
651
+ '```',
652
+ ].join('\n');
653
+ }
654
+
655
+ export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Promise<CommandResult> {
656
+ const provider = h.getProvider(args?.agentType);
657
+ const transport = getTargetTransport(h, provider);
658
+ const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
659
+ const providerType = provider?.type || getCurrentProviderType(h, args?.agentType || '');
660
+ const adapter = isCliLikeTransport(transport) ? getTargetedCliAdapter(h, args, provider?.type) : null;
661
+ const targetInstance = getTargetInstance(h, args);
662
+
663
+ let adapterStatus: unknown = null;
664
+ let parsedStatus: unknown = null;
665
+ let adapterDebugSnapshot: unknown = null;
666
+ let partialResponse = '';
667
+ if (adapter) {
668
+ try { adapterStatus = adapter.getStatus?.(); } catch (error: any) { adapterStatus = { error: error?.message || String(error) }; }
669
+ try { parsedStatus = typeof adapter.getScriptParsedStatus === 'function' ? parseMaybeJson(adapter.getScriptParsedStatus()) : null; } catch (error: any) { parsedStatus = { error: error?.message || String(error) }; }
670
+ try { adapterDebugSnapshot = typeof adapter.getDebugSnapshot === 'function' ? adapter.getDebugSnapshot() : null; } catch (error: any) { adapterDebugSnapshot = { error: error?.message || String(error) }; }
671
+ try { partialResponse = adapter.getPartialResponse?.() || ''; } catch { partialResponse = ''; }
672
+ }
673
+
674
+ let instanceState: unknown = null;
675
+ if (targetInstance?.getState) {
676
+ try { instanceState = summarizeStateForDebug(targetInstance.getState()); } catch (error: any) { instanceState = { error: error?.message || String(error) }; }
677
+ }
678
+
679
+ let readChat: unknown = null;
680
+ try {
681
+ const readResult = await handleReadChat(h, { ...args, tailLimit: Math.max(1, Math.min(40, Number(args?.tailLimit || 40))) });
682
+ readChat = readResult.success
683
+ ? {
684
+ success: true,
685
+ status: readResult.status,
686
+ title: readResult.title,
687
+ totalMessages: readResult.totalMessages,
688
+ returnedMessages: Array.isArray(readResult.messages) ? readResult.messages.length : undefined,
689
+ syncMode: readResult.syncMode,
690
+ replaceFrom: readResult.replaceFrom,
691
+ lastMessageSignature: readResult.lastMessageSignature,
692
+ providerSessionId: readResult.providerSessionId,
693
+ transcriptAuthority: readResult.transcriptAuthority,
694
+ coverage: readResult.coverage,
695
+ activeModal: readResult.activeModal,
696
+ messagesTail: Array.isArray(readResult.messages) ? readResult.messages.slice(-20) : [],
697
+ debugReadChat: readResult.debugReadChat,
698
+ }
699
+ : { success: false, error: readResult.error };
700
+ } catch (error: any) {
701
+ readChat = { success: false, error: error?.message || String(error) };
702
+ }
703
+
704
+ const cdp = h.getCdp();
705
+ const rawBundle: Record<string, unknown> = {
706
+ version: 1,
707
+ createdAt: new Date().toISOString(),
708
+ target: {
709
+ targetSessionId,
710
+ providerType,
711
+ transport,
712
+ routeManagerKey: h.currentManagerKey,
713
+ currentIdeType: h.currentIdeType,
714
+ },
715
+ session: summarizeSessionForDebug(h.currentSession),
716
+ provider: summarizeProviderForDebug(provider),
717
+ daemon: {
718
+ pid: process.pid,
719
+ platform: process.platform,
720
+ nodeVersion: process.version,
721
+ cwd: process.cwd(),
722
+ },
723
+ cdp: {
724
+ requested: !!cdp,
725
+ connected: !!cdp?.isConnected,
726
+ managerKey: getCurrentManagerKey(h),
727
+ },
728
+ instanceState,
729
+ cli: adapter ? {
730
+ cliType: adapter.cliType,
731
+ cliName: adapter.cliName,
732
+ workingDir: adapter.workingDir,
733
+ status: (adapterStatus as any)?.status,
734
+ activeModal: (adapterStatus as any)?.activeModal,
735
+ messageCount: Array.isArray((adapterStatus as any)?.messages) ? (adapterStatus as any).messages.length : undefined,
736
+ messagesTail: Array.isArray((adapterStatus as any)?.messages) ? (adapterStatus as any).messages.slice(-20) : undefined,
737
+ parsedStatus,
738
+ partialResponse,
739
+ ready: typeof adapter.isReady === 'function' ? adapter.isReady() : undefined,
740
+ processing: typeof adapter.isProcessing === 'function' ? adapter.isProcessing() : undefined,
741
+ debugSnapshot: adapterDebugSnapshot,
742
+ } : null,
743
+ readChat,
744
+ frontend: args?.frontendSnapshot && typeof args.frontendSnapshot === 'object' ? args.frontendSnapshot : null,
745
+ recentLogs: getRecentLogs(80, 'debug'),
746
+ recentDebugTrace: getRecentDebugTrace({ limit: 120 }),
747
+ };
748
+
749
+ const bundle = sanitizeDebugBundleValue(rawBundle) as Record<string, unknown>;
750
+ return {
751
+ success: true,
752
+ bundle,
753
+ text: buildDebugBundleText(bundle),
754
+ };
755
+ }
756
+
492
757
  function didProviderConfirmSend(result: any): boolean {
493
758
  const parsed = parseMaybeJson(result);
494
759
  if (parsed === true) return true;
@@ -52,7 +52,10 @@ function commandExists(command: string): boolean {
52
52
  return existsSync(expandExecutable(trimmed));
53
53
  }
54
54
  try {
55
- execFileSync(process.platform === 'win32' ? 'where' : 'which', [trimmed], { stdio: 'ignore' });
55
+ execFileSync(process.platform === 'win32' ? 'where' : 'which', [trimmed], {
56
+ stdio: 'ignore',
57
+ ...(process.platform === 'win32' ? { windowsHide: true } : {}),
58
+ });
56
59
  return true;
57
60
  } catch {
58
61
  return false;
@@ -431,6 +431,7 @@ export class DaemonCommandHandler implements CommandHelpers {
431
431
  switch (cmd) {
432
432
  // ─── Chat commands (chat-commands.ts) ───────────────
433
433
  case 'read_chat': return Chat.handleReadChat(this, args);
434
+ case 'get_chat_debug_bundle': return Chat.handleGetChatDebugBundle(this, args);
434
435
  case 'chat_history': return Chat.handleChatHistory(this, args);
435
436
  case 'send_chat': return Chat.handleSendChat(this, args);
436
437
  case 'list_chats': return Chat.handleListChats(this, args);
@@ -36,7 +36,7 @@ import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../sessi
36
36
  import { buildSessionEntries } from '../status/builders.js';
37
37
  import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
38
38
  import { getSessionCompletionMarker } from '../status/snapshot.js';
39
- import { spawnDetachedDaemonUpgradeHelper } from './upgrade-helper.js';
39
+ import { execNpmCommandSync, resolveCurrentGlobalInstallSurface, spawnDetachedDaemonUpgradeHelper } from './upgrade-helper.js';
40
40
  import * as fs from 'fs';
41
41
 
42
42
  // ─── Types ───
@@ -847,23 +847,22 @@ export class DaemonCommandRouter {
847
847
  case 'daemon_upgrade': {
848
848
  LOG.info('Upgrade', 'Remote upgrade requested from dashboard');
849
849
  try {
850
- const { execSync } = await import('child_process');
851
-
852
850
  // Detect package name for upgrade
853
851
  const isStandalone = this.deps.packageName === '@adhdev/daemon-standalone'
854
852
  || process.argv[1]?.includes('daemon-standalone');
855
853
  const pkgName = isStandalone ? '@adhdev/daemon-standalone' : 'adhdev';
854
+ const npmSurface = resolveCurrentGlobalInstallSurface({ packageName: pkgName });
856
855
 
857
856
  // Check latest version
858
- const latest = execSync(`npm view ${pkgName} version`, { encoding: 'utf-8', timeout: 10000 }).trim();
857
+ const latest = String(execNpmCommandSync(['view', pkgName, 'version'], { encoding: 'utf-8', timeout: 10000 }, npmSurface)).trim();
859
858
  LOG.info('Upgrade', `Latest ${pkgName}: v${latest}`);
860
859
  let currentInstalled: string | null = null;
861
860
  try {
862
- const currentJson = execSync(`npm ls -g ${pkgName} --depth=0 --json`, {
861
+ const currentJson = String(execNpmCommandSync(['ls', '-g', pkgName, '--depth=0', '--json'], {
863
862
  encoding: 'utf-8',
864
863
  timeout: 10000,
865
864
  stdio: ['pipe', 'pipe', 'pipe'],
866
- }).trim();
865
+ }, npmSurface)).trim();
867
866
  const parsed = JSON.parse(currentJson);
868
867
  currentInstalled = parsed?.dependencies?.[pkgName]?.version || null;
869
868
  } catch {
@@ -343,7 +343,12 @@ async function executeProviderScript(h: CommandHelpers, args: any, scriptName: s
343
343
  if (cliCommand?.type === 'send_message' && cliCommand.text) {
344
344
  await adapter.sendMessage(cliCommand.text);
345
345
  } else if (cliCommand?.type === 'pty_write' && cliCommand.text && adapter.writeRaw) {
346
+ const enterCount = cliCommand.enterCount || 1;
346
347
  await adapter.writeRaw(cliCommand.text + '\r');
348
+ for (let i = 1; i < enterCount; i += 1) {
349
+ await new Promise(resolve => setTimeout(resolve, 50));
350
+ await adapter.writeRaw('\r');
351
+ }
347
352
  }
348
353
  applyProviderPatch(h, args, parsed.payload);
349
354
  return {
@@ -6,5 +6,31 @@ export interface DaemonUpgradeHelperPayload {
6
6
  cwd?: string;
7
7
  sessionHostAppName?: string;
8
8
  }
9
+ export interface CurrentGlobalInstallSurface {
10
+ npmExecutable: string;
11
+ npmArgsPrefix?: string[];
12
+ packageRoot: string | null;
13
+ installPrefix: string | null;
14
+ execOptions?: { shell: boolean };
15
+ }
16
+ export interface PinnedGlobalInstallCommand {
17
+ command: string;
18
+ args: string[];
19
+ surface: CurrentGlobalInstallSurface;
20
+ execOptions: { shell: boolean };
21
+ }
22
+ export declare function resolveCurrentGlobalInstallSurface(options: {
23
+ packageName: string;
24
+ currentCliPath?: string;
25
+ nodeExecutable?: string;
26
+ platform?: NodeJS.Platform;
27
+ }): CurrentGlobalInstallSurface;
28
+ export declare function buildPinnedGlobalInstallCommand(options: {
29
+ packageName: string;
30
+ targetVersion: string;
31
+ currentCliPath?: string;
32
+ nodeExecutable?: string;
33
+ platform?: NodeJS.Platform;
34
+ }): PinnedGlobalInstallCommand;
9
35
  export declare function spawnDetachedDaemonUpgradeHelper(payload: DaemonUpgradeHelperPayload): void;
10
36
  export declare function maybeRunDaemonUpgradeHelperFromEnv(): Promise<boolean>;