@adhdev/daemon-core 0.8.29 → 0.8.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.
Files changed (63) hide show
  1. package/dist/agent-stream/manager.d.ts +1 -0
  2. package/dist/agent-stream/provider-adapter.d.ts +1 -0
  3. package/dist/agent-stream/types.d.ts +3 -0
  4. package/dist/boot/daemon-lifecycle.d.ts +2 -1
  5. package/dist/cdp/manager.d.ts +2 -0
  6. package/dist/cli-adapter-types.d.ts +34 -5
  7. package/dist/cli-adapters/provider-cli-adapter.d.ts +4 -158
  8. package/dist/cli-adapters/provider-cli-config.d.ts +30 -0
  9. package/dist/cli-adapters/provider-cli-parse.d.ts +42 -0
  10. package/dist/cli-adapters/provider-cli-runtime.d.ts +29 -0
  11. package/dist/cli-adapters/provider-cli-shared.d.ts +158 -0
  12. package/dist/commands/handler.d.ts +4 -3
  13. package/dist/config/config.d.ts +4 -3
  14. package/dist/index.js +1033 -621
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +1035 -624
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/providers/acp-provider-instance.d.ts +1 -0
  19. package/dist/providers/approval-utils.d.ts +7 -0
  20. package/dist/providers/cli-provider-instance.d.ts +2 -0
  21. package/dist/providers/contracts.d.ts +12 -1
  22. package/dist/providers/ide-provider-instance.d.ts +1 -0
  23. package/dist/providers/provider-loader.d.ts +3 -0
  24. package/dist/status/reporter.d.ts +2 -3
  25. package/dist/status/snapshot.d.ts +2 -1
  26. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  27. package/package.json +3 -1
  28. package/src/agent-stream/manager.ts +8 -2
  29. package/src/agent-stream/poller.ts +57 -6
  30. package/src/agent-stream/provider-adapter.ts +11 -7
  31. package/src/agent-stream/types.ts +3 -0
  32. package/src/boot/daemon-lifecycle.ts +7 -6
  33. package/src/cdp/initializer.ts +2 -2
  34. package/src/cdp/manager.ts +5 -0
  35. package/src/cdp/setup.ts +1 -1
  36. package/src/cli-adapter-types.ts +37 -5
  37. package/src/cli-adapters/provider-cli-adapter.ts +212 -795
  38. package/src/cli-adapters/provider-cli-config.ts +66 -0
  39. package/src/cli-adapters/provider-cli-parse.ts +202 -0
  40. package/src/cli-adapters/provider-cli-runtime.ts +142 -0
  41. package/src/cli-adapters/provider-cli-shared.ts +439 -0
  42. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +1 -1
  43. package/src/commands/cdp-commands.ts +6 -1
  44. package/src/commands/chat-commands.ts +45 -29
  45. package/src/commands/cli-manager.ts +28 -9
  46. package/src/commands/handler.ts +14 -10
  47. package/src/commands/router.ts +23 -10
  48. package/src/commands/stream-commands.ts +11 -5
  49. package/src/config/config.ts +4 -10
  50. package/src/daemon/dev-auto-implement.ts +22 -18
  51. package/src/daemon/dev-cli-debug.ts +59 -16
  52. package/src/daemon/dev-server.ts +67 -43
  53. package/src/providers/acp-provider-instance.ts +18 -3
  54. package/src/providers/approval-utils.ts +66 -0
  55. package/src/providers/cli-provider-instance.ts +32 -6
  56. package/src/providers/contracts.d.ts +1 -0
  57. package/src/providers/contracts.ts +15 -2
  58. package/src/providers/extension-provider-instance.ts +1 -1
  59. package/src/providers/ide-provider-instance.ts +67 -41
  60. package/src/providers/provider-loader.ts +110 -55
  61. package/src/providers/version-archive.ts +23 -5
  62. package/src/status/reporter.ts +18 -14
  63. package/src/status/snapshot.ts +5 -4
@@ -0,0 +1,66 @@
1
+ import type { CliProviderModule } from './provider-cli-shared.js';
2
+
3
+ export interface ProviderResolutionMeta {
4
+ type: string;
5
+ name: string;
6
+ resolvedVersion: string | null;
7
+ resolvedOs: string | null;
8
+ providerDir: string | null;
9
+ scriptDir: string | null;
10
+ scriptsPath: string | null;
11
+ scriptsSource: string | null;
12
+ versionWarning: string | null;
13
+ }
14
+
15
+ export interface ResolvedCliAdapterConfig {
16
+ timeouts: {
17
+ ptyFlush: number;
18
+ dialogAccept: number;
19
+ approvalCooldown: number;
20
+ generatingIdle: number;
21
+ idleFinish: number;
22
+ maxResponse: number;
23
+ shutdownGrace: number;
24
+ outputSettle: number;
25
+ };
26
+ approvalKeys: Record<number, string>;
27
+ sendDelayMs: number;
28
+ sendKey: string;
29
+ submitStrategy: 'wait_for_echo' | 'immediate';
30
+ providerResolutionMeta: ProviderResolutionMeta;
31
+ }
32
+
33
+ export function resolveCliAdapterConfig(provider: CliProviderModule): ResolvedCliAdapterConfig {
34
+ const t = provider.timeouts || {};
35
+ const rawKeys = provider.approvalKeys;
36
+
37
+ return {
38
+ timeouts: {
39
+ ptyFlush: t.ptyFlush ?? 50,
40
+ dialogAccept: t.dialogAccept ?? 300,
41
+ approvalCooldown: t.approvalCooldown ?? 3000,
42
+ generatingIdle: t.generatingIdle ?? 6000,
43
+ idleFinish: t.idleFinish ?? 5000,
44
+ maxResponse: t.maxResponse ?? 300000,
45
+ shutdownGrace: t.shutdownGrace ?? 1000,
46
+ outputSettle: t.outputSettle ?? 300,
47
+ },
48
+ approvalKeys: (rawKeys && typeof rawKeys === 'object') ? rawKeys : {},
49
+ sendDelayMs: typeof provider.sendDelayMs === 'number' ? Math.max(0, provider.sendDelayMs) : 0,
50
+ sendKey: typeof provider.sendKey === 'string' && provider.sendKey.length > 0
51
+ ? provider.sendKey
52
+ : '\r',
53
+ submitStrategy: provider.submitStrategy === 'immediate' ? 'immediate' : 'wait_for_echo',
54
+ providerResolutionMeta: {
55
+ type: provider.type,
56
+ name: provider.name,
57
+ resolvedVersion: provider._resolvedVersion || null,
58
+ resolvedOs: provider._resolvedOs || null,
59
+ providerDir: provider._resolvedProviderDir || null,
60
+ scriptDir: provider._resolvedScriptDir || null,
61
+ scriptsPath: provider._resolvedScriptsPath || null,
62
+ scriptsSource: provider._resolvedScriptsSource || null,
63
+ versionWarning: provider._versionWarning || null,
64
+ },
65
+ };
66
+ }
@@ -0,0 +1,202 @@
1
+ import {
2
+ buildCliScreenSnapshot,
3
+ normalizeComparableMessageContent,
4
+ sanitizeTerminalText,
5
+ type CliChatMessage,
6
+ type CliScriptInput,
7
+ } from './provider-cli-shared.js';
8
+
9
+ export interface TurnParseScope {
10
+ prompt: string;
11
+ startedAt: number;
12
+ bufferStart: number;
13
+ rawBufferStart: number;
14
+ }
15
+
16
+ function sliceFromOffset(text: string, start: number): string {
17
+ if (!text) return '';
18
+ if (!Number.isFinite(start) || start <= 0) return text;
19
+ if (start >= text.length) return '';
20
+ return text.slice(start);
21
+ }
22
+
23
+ export function hydrateCliParsedMessages(
24
+ parsedMessages: any[],
25
+ options: {
26
+ committedMessages: CliChatMessage[];
27
+ scope?: TurnParseScope | null;
28
+ lastOutputAt: number;
29
+ now?: number;
30
+ },
31
+ ): any[] {
32
+ const { committedMessages, scope, lastOutputAt } = options;
33
+ const referenceMessages = [...committedMessages];
34
+ const usedReferenceIndexes = new Set<number>();
35
+ const now = options.now ?? Date.now();
36
+
37
+ const findReferenceTimestamp = (role: 'user' | 'assistant', content: string, parsedIndex: number): number | undefined => {
38
+ const normalizedContent = normalizeComparableMessageContent(content);
39
+ if (!normalizedContent) return undefined;
40
+
41
+ const sameIndex = referenceMessages[parsedIndex];
42
+ if (
43
+ sameIndex
44
+ && !usedReferenceIndexes.has(parsedIndex)
45
+ && sameIndex.role === role
46
+ && normalizeComparableMessageContent(sameIndex.content) === normalizedContent
47
+ && typeof sameIndex.timestamp === 'number'
48
+ && Number.isFinite(sameIndex.timestamp)
49
+ ) {
50
+ usedReferenceIndexes.add(parsedIndex);
51
+ return sameIndex.timestamp;
52
+ }
53
+
54
+ for (let i = 0; i < referenceMessages.length; i++) {
55
+ if (usedReferenceIndexes.has(i)) continue;
56
+ const candidate = referenceMessages[i];
57
+ if (!candidate || candidate.role !== role) continue;
58
+ const candidateContent = normalizeComparableMessageContent(candidate.content);
59
+ if (!candidateContent) continue;
60
+ const exactMatch = candidateContent === normalizedContent;
61
+ const fuzzyMatch = candidateContent.includes(normalizedContent) || normalizedContent.includes(candidateContent);
62
+ if (!exactMatch && !fuzzyMatch) continue;
63
+ if (typeof candidate.timestamp === 'number' && Number.isFinite(candidate.timestamp)) {
64
+ usedReferenceIndexes.add(i);
65
+ return candidate.timestamp;
66
+ }
67
+ }
68
+
69
+ return undefined;
70
+ };
71
+
72
+ return parsedMessages
73
+ .filter((message) => message && (message.role === 'user' || message.role === 'assistant'))
74
+ .map((message, index) => {
75
+ const role = message.role as 'user' | 'assistant';
76
+ const content = typeof message.content === 'string' ? message.content : String(message.content || '');
77
+ const parsedTimestamp = typeof message.timestamp === 'number' && Number.isFinite(message.timestamp)
78
+ ? message.timestamp
79
+ : undefined;
80
+ const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
81
+ const fallbackTimestamp = role === 'user'
82
+ ? (scope?.startedAt || now)
83
+ : (lastOutputAt || scope?.startedAt || now);
84
+ const timestamp = referenceTimestamp ?? fallbackTimestamp;
85
+ return {
86
+ ...message,
87
+ role,
88
+ content,
89
+ timestamp,
90
+ receivedAt: typeof message.receivedAt === 'number' && Number.isFinite(message.receivedAt)
91
+ ? message.receivedAt
92
+ : timestamp,
93
+ };
94
+ });
95
+ }
96
+
97
+ export function normalizeCliParsedMessages(
98
+ parsedMessages: any[],
99
+ options: {
100
+ committedMessages: CliChatMessage[];
101
+ scope?: TurnParseScope | null;
102
+ lastOutputAt: number;
103
+ now?: number;
104
+ },
105
+ ): CliChatMessage[] {
106
+ return hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
107
+ role: message.role,
108
+ content: message.content,
109
+ timestamp: message.timestamp,
110
+ receivedAt: message.receivedAt,
111
+ kind: message.kind,
112
+ id: message.id,
113
+ index: message.index,
114
+ meta: message.meta,
115
+ senderName: message.senderName,
116
+ }));
117
+ }
118
+
119
+ export function buildCliParseInput(options: {
120
+ accumulatedBuffer: string;
121
+ accumulatedRawBuffer: string;
122
+ recentOutputBuffer: string;
123
+ terminalScreenText: string;
124
+ baseMessages: CliChatMessage[];
125
+ partialResponse: string;
126
+ scope?: TurnParseScope | null;
127
+ runtimeSettings: Record<string, any>;
128
+ }): CliScriptInput {
129
+ const {
130
+ accumulatedBuffer,
131
+ accumulatedRawBuffer,
132
+ recentOutputBuffer,
133
+ terminalScreenText,
134
+ baseMessages,
135
+ partialResponse,
136
+ scope,
137
+ runtimeSettings,
138
+ } = options;
139
+ const buffer = scope
140
+ ? (sliceFromOffset(accumulatedBuffer, scope.bufferStart) || accumulatedBuffer)
141
+ : accumulatedBuffer;
142
+ const rawBuffer = scope
143
+ ? (sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) || accumulatedRawBuffer)
144
+ : accumulatedRawBuffer;
145
+ const screenText = terminalScreenText;
146
+ const recentBuffer = buffer.slice(-1000) || recentOutputBuffer;
147
+
148
+ return {
149
+ buffer,
150
+ rawBuffer,
151
+ recentBuffer,
152
+ screenText,
153
+ screen: buildCliScreenSnapshot(screenText),
154
+ bufferScreen: buildCliScreenSnapshot(buffer),
155
+ recentScreen: buildCliScreenSnapshot(recentBuffer),
156
+ messages: [...baseMessages],
157
+ partialResponse,
158
+ promptText: scope?.prompt || '',
159
+ settings: { ...runtimeSettings },
160
+ };
161
+ }
162
+
163
+ export function summarizeCliTraceText(text: string, max = 800): string {
164
+ const value = sanitizeTerminalText(String(text || ''));
165
+ if (value.length <= max) return value;
166
+ return `…${value.slice(-max)}`;
167
+ }
168
+
169
+ export function summarizeCliTraceMessages(
170
+ messages: CliChatMessage[],
171
+ limit = 3,
172
+ ): { role: string; content: string; timestamp?: number }[] {
173
+ return messages.slice(-limit).map((message) => ({
174
+ role: message.role,
175
+ content: summarizeCliTraceText(message.content, 240),
176
+ timestamp: message.timestamp,
177
+ }));
178
+ }
179
+
180
+ export function buildCliTraceParseSnapshot(options: {
181
+ accumulatedBuffer: string;
182
+ accumulatedRawBuffer: string;
183
+ responseBuffer: string;
184
+ partialResponse?: string;
185
+ scope?: TurnParseScope | null;
186
+ }): Record<string, any> {
187
+ const { accumulatedBuffer, accumulatedRawBuffer, responseBuffer, partialResponse, scope } = options;
188
+ const scopedBuffer = scope
189
+ ? (sliceFromOffset(accumulatedBuffer, scope.bufferStart) || accumulatedBuffer)
190
+ : accumulatedBuffer;
191
+ const scopedRawBuffer = scope
192
+ ? (sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) || accumulatedRawBuffer)
193
+ : accumulatedRawBuffer;
194
+ return {
195
+ currentTurnScope: scope || null,
196
+ responseBuffer: summarizeCliTraceText(responseBuffer, 1200),
197
+ partialResponse: summarizeCliTraceText(partialResponse || responseBuffer, 1200),
198
+ turnBuffer: summarizeCliTraceText(scopedBuffer, 1600),
199
+ turnRawPreview: summarizeCliTraceText(scopedRawBuffer, 1600),
200
+ turnSanitizedRawPreview: summarizeCliTraceText(sanitizeTerminalText(scopedRawBuffer), 1600),
201
+ };
202
+ }
@@ -0,0 +1,142 @@
1
+ import * as os from 'os';
2
+ import * as path from 'path';
3
+ import type { PtySpawnOptions, PtyRuntimeTransport } from './pty-transport.js';
4
+ import type { TerminalScreen } from './terminal-screen.js';
5
+ import {
6
+ buildCliSpawnEnv,
7
+ findBinary,
8
+ isScriptBinary,
9
+ looksLikeMachOOrElf,
10
+ shSingleQuote,
11
+ type CliProviderModule,
12
+ } from './provider-cli-shared.js';
13
+
14
+ export interface CliSpawnPlan {
15
+ binaryPath: string;
16
+ allArgs: string[];
17
+ shellCmd: string;
18
+ shellArgs: string[];
19
+ ptyOptions: PtySpawnOptions;
20
+ isWin: boolean;
21
+ useShell: boolean;
22
+ }
23
+
24
+ export function resolveCliSpawnPlan(options: {
25
+ provider: CliProviderModule;
26
+ runtimeSettings: Record<string, any>;
27
+ workingDir: string;
28
+ extraArgs: string[];
29
+ }): CliSpawnPlan {
30
+ const { provider, runtimeSettings, workingDir, extraArgs } = options;
31
+ const { spawn: spawnConfig } = provider;
32
+ const configuredCommand = typeof runtimeSettings.executablePath === 'string' && runtimeSettings.executablePath.trim()
33
+ ? runtimeSettings.executablePath.trim()
34
+ : spawnConfig.command;
35
+ const binaryPath = findBinary(configuredCommand);
36
+ const isWin = os.platform() === 'win32';
37
+ const allArgs = [...spawnConfig.args, ...extraArgs];
38
+
39
+ let shellCmd: string;
40
+ let shellArgs: string[];
41
+ const useShellUnix = !isWin && (
42
+ !!spawnConfig.shell
43
+ || !path.isAbsolute(binaryPath)
44
+ || isScriptBinary(binaryPath)
45
+ || !looksLikeMachOOrElf(binaryPath)
46
+ );
47
+ const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
48
+ const useShellWin = !!spawnConfig.shell
49
+ || isCmdShim
50
+ || !path.isAbsolute(binaryPath)
51
+ || isScriptBinary(binaryPath);
52
+ const useShell = isWin ? useShellWin : useShellUnix;
53
+
54
+ if (useShell) {
55
+ shellCmd = isWin ? 'cmd.exe' : (process.env.SHELL || '/bin/zsh');
56
+ if (isWin) {
57
+ shellArgs = ['/c', binaryPath, ...allArgs];
58
+ } else {
59
+ const fullCmd = [binaryPath, ...allArgs].map(shSingleQuote).join(' ');
60
+ shellArgs = ['-l', '-c', fullCmd];
61
+ }
62
+ } else {
63
+ shellCmd = binaryPath;
64
+ shellArgs = allArgs;
65
+ }
66
+
67
+ return {
68
+ binaryPath,
69
+ allArgs,
70
+ shellCmd,
71
+ shellArgs,
72
+ isWin,
73
+ useShell,
74
+ ptyOptions: {
75
+ cols: 80,
76
+ rows: 24,
77
+ cwd: workingDir,
78
+ env: buildCliSpawnEnv(process.env, spawnConfig.env),
79
+ },
80
+ };
81
+ }
82
+
83
+ export function buildCliLoginShellRetry(plan: Pick<CliSpawnPlan, 'binaryPath' | 'allArgs'>): {
84
+ shellCmd: string;
85
+ shellArgs: string[];
86
+ } {
87
+ const shellCmd = process.env.SHELL || '/bin/zsh';
88
+ const fullCmd = [plan.binaryPath, ...plan.allArgs].map(shSingleQuote).join(' ');
89
+ return {
90
+ shellCmd,
91
+ shellArgs: ['-l', '-c', fullCmd],
92
+ };
93
+ }
94
+
95
+ export function getCliSpawnErrorHint(message: string, shellCmd: string, isWin: boolean): string | null {
96
+ if (!isWin) return null;
97
+ if (/error code 267|ERROR_DIRECTORY/i.test(message)) {
98
+ return ' (working directory does not exist or is not a directory)';
99
+ }
100
+ if (/error code 740|elevation/i.test(message)) {
101
+ return ' (requires administrator privileges)';
102
+ }
103
+ if (/error code 2|ENOENT|not found/i.test(message)) {
104
+ return ` (executable not found: ${shellCmd})`;
105
+ }
106
+ return null;
107
+ }
108
+
109
+ export function respondToCliTerminalQueries(options: {
110
+ ptyProcess: PtyRuntimeTransport | null;
111
+ pendingTail: string;
112
+ data: string;
113
+ terminalScreen: TerminalScreen;
114
+ }): string {
115
+ const { ptyProcess, pendingTail, data, terminalScreen } = options;
116
+ if (!ptyProcess || !data) return pendingTail;
117
+
118
+ const combined = pendingTail + data;
119
+ const regex = /\x1b\[(\?)?6n/g;
120
+ let match: RegExpExecArray | null;
121
+
122
+ while ((match = regex.exec(combined)) !== null) {
123
+ const cursor = terminalScreen.getCursorPosition();
124
+ const row = Math.max(1, (cursor.row | 0) + 1);
125
+ const col = Math.max(1, (cursor.col | 0) + 1);
126
+ const response = match[1]
127
+ ? `\x1b[?${row};${col}R`
128
+ : `\x1b[${row};${col}R`;
129
+ ptyProcess.write(response);
130
+ }
131
+
132
+ const prefixes = ['\x1b[6n', '\x1b[?6n'];
133
+ const maxLength = prefixes.reduce((n, value) => Math.max(n, value.length), 0) - 1;
134
+ const start = Math.max(0, combined.length - maxLength);
135
+ for (let i = start; i < combined.length; i++) {
136
+ const suffix = combined.slice(i);
137
+ if (prefixes.some((pattern) => suffix.length < pattern.length && pattern.startsWith(suffix))) {
138
+ return suffix;
139
+ }
140
+ }
141
+ return '';
142
+ }