@adhdev/daemon-core 0.9.44 → 0.9.45
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/dist/cli-adapters/provider-cli-adapter.d.ts +3 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +6 -0
- package/dist/config/chat-history.d.ts +30 -9
- package/dist/index.js +657 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +657 -35
- package/dist/index.mjs.map +1 -1
- package/dist/launch/macos-app-process.d.ts +2 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -0
- package/dist/providers/contracts.d.ts +14 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +25 -3
- package/src/cli-adapters/provider-cli-shared.ts +6 -0
- package/src/commands/chat-commands.ts +27 -3
- package/src/commands/router.ts +9 -3
- package/src/config/chat-history.ts +541 -24
- package/src/launch/macos-app-process.ts +37 -0
- package/src/launch.ts +43 -5
- package/src/providers/cli-provider-instance.ts +52 -3
- package/src/providers/contracts.ts +15 -2
- package/src/providers/read-chat-contract.ts +2 -0
package/src/launch.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { detectIDEs } from './detection/ide-detector.js';
|
|
|
24
24
|
import { IDEInfo } from './detection/ide-detector.js';
|
|
25
25
|
import { ProviderLoader } from './providers/provider-loader.js';
|
|
26
26
|
import type { ProviderModule } from './providers/contracts.js';
|
|
27
|
+
import { findMacAppProcessPids } from './launch/macos-app-process.js';
|
|
27
28
|
|
|
28
29
|
// ─── Provider-based dynamic IDE infrastructure ────────────────
|
|
29
30
|
// Reads cdpPorts, processNames from provider.js — only create provider.js to add new IDE
|
|
@@ -71,6 +72,37 @@ function escapeForAppleScript(value: string): string {
|
|
|
71
72
|
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
function getIdePathCandidates(ideId: string): string[] {
|
|
76
|
+
return getProviderLoader().getIdePathCandidates(ideId);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getMacAppProcessPids(ideId: string): number[] {
|
|
80
|
+
const appPaths = getIdePathCandidates(ideId);
|
|
81
|
+
if (appPaths.length === 0) return [];
|
|
82
|
+
try {
|
|
83
|
+
const output = execSync('ps axww -o pid=,args=', {
|
|
84
|
+
encoding: 'utf-8',
|
|
85
|
+
timeout: 3000,
|
|
86
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
87
|
+
});
|
|
88
|
+
return findMacAppProcessPids(output, appPaths);
|
|
89
|
+
} catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function killMacAppPathProcesses(ideId: string, signal: NodeJS.Signals): boolean {
|
|
95
|
+
const pids = getMacAppProcessPids(ideId);
|
|
96
|
+
let signalled = false;
|
|
97
|
+
for (const pid of pids) {
|
|
98
|
+
try {
|
|
99
|
+
process.kill(pid, signal);
|
|
100
|
+
signalled = true;
|
|
101
|
+
} catch { }
|
|
102
|
+
}
|
|
103
|
+
return signalled;
|
|
104
|
+
}
|
|
105
|
+
|
|
74
106
|
// ─── Helpers ────────────────────────────────────
|
|
75
107
|
|
|
76
108
|
/** Find available port (primary → secondary → sequential after) */
|
|
@@ -137,6 +169,7 @@ export async function killIdeProcess(ideId: string): Promise<boolean> {
|
|
|
137
169
|
} catch {
|
|
138
170
|
try { execSync(`pkill -x "${appName}" 2>/dev/null`, { timeout: 5000 }); } catch { }
|
|
139
171
|
}
|
|
172
|
+
killMacAppPathProcesses(ideId, 'SIGTERM');
|
|
140
173
|
} else if (plat === 'win32' && winProcesses) {
|
|
141
174
|
// Windows: taskkill for each process name
|
|
142
175
|
for (const proc of winProcesses) {
|
|
@@ -164,6 +197,7 @@ export async function killIdeProcess(ideId: string): Promise<boolean> {
|
|
|
164
197
|
// Force terminate retry
|
|
165
198
|
if (plat === 'darwin' && appName) {
|
|
166
199
|
try { execSync(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5000 }); } catch { }
|
|
200
|
+
killMacAppPathProcesses(ideId, 'SIGKILL');
|
|
167
201
|
} else if (plat === 'win32' && winProcesses) {
|
|
168
202
|
for (const proc of winProcesses) {
|
|
169
203
|
try { execSync(`taskkill /IM "${proc}" /F 2>nul`); } catch { }
|
|
@@ -185,14 +219,16 @@ export function isIdeRunning(ideId: string): boolean {
|
|
|
185
219
|
try {
|
|
186
220
|
if (plat === 'darwin') {
|
|
187
221
|
const appName = getMacAppIdentifiers()[ideId];
|
|
188
|
-
if (!appName) return
|
|
222
|
+
if (!appName) return getMacAppProcessPids(ideId).length > 0;
|
|
189
223
|
try {
|
|
190
224
|
const result = execSync(`pgrep -x "${appName}" 2>/dev/null`, {
|
|
191
225
|
encoding: 'utf-8',
|
|
192
226
|
timeout: 3000,
|
|
193
227
|
});
|
|
194
|
-
|
|
195
|
-
} catch {
|
|
228
|
+
if (result.trim().length > 0) return true;
|
|
229
|
+
} catch { }
|
|
230
|
+
|
|
231
|
+
try {
|
|
196
232
|
const result = execSync(
|
|
197
233
|
`osascript -e 'tell application "System Events" to count (every process whose name is "${escapeForAppleScript(appName)}")'`,
|
|
198
234
|
{
|
|
@@ -201,8 +237,10 @@ export function isIdeRunning(ideId: string): boolean {
|
|
|
201
237
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
202
238
|
},
|
|
203
239
|
);
|
|
204
|
-
|
|
205
|
-
}
|
|
240
|
+
if (Number.parseInt(result.trim() || '0', 10) > 0) return true;
|
|
241
|
+
} catch { }
|
|
242
|
+
|
|
243
|
+
return getMacAppProcessPids(ideId).length > 0;
|
|
206
244
|
} else if (plat === 'win32') {
|
|
207
245
|
const winProcesses = getWinProcessNames()[ideId];
|
|
208
246
|
if (!winProcesses) return false;
|
|
@@ -17,7 +17,7 @@ import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
|
17
17
|
import type { CliProviderModule } from '../cli-adapters/provider-cli-adapter.js';
|
|
18
18
|
import type { PtyRuntimeMetadata, PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
19
19
|
import { StatusMonitor } from './status-monitor.js';
|
|
20
|
-
import { ChatHistoryWriter, readChatHistory, rebuildClaudeSavedHistoryFromNativeProject, rebuildHermesSavedHistoryFromCanonicalSession } from '../config/chat-history.js';
|
|
20
|
+
import { ChatHistoryWriter, isNativeSourceCanonicalHistory, readChatHistory, readProviderChatHistory, rebuildClaudeSavedHistoryFromNativeProject, rebuildCodexSavedHistoryFromNativeSession, rebuildHermesSavedHistoryFromCanonicalSession, resolveCodexSessionTranscriptPath } from '../config/chat-history.js';
|
|
21
21
|
import { LOG } from '../logging/logger.js';
|
|
22
22
|
import type { ChatMessage } from '../types.js';
|
|
23
23
|
import { buildPersistedProviderEffectMessage, normalizeProviderEffects } from './control-effects.js';
|
|
@@ -184,6 +184,8 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
184
184
|
private lastCanonicalHermesWatchPath: string | undefined = undefined;
|
|
185
185
|
private lastCanonicalClaudeRebuildMtimeMs = 0;
|
|
186
186
|
private lastCanonicalClaudeCheckAt = 0;
|
|
187
|
+
private lastCanonicalCodexRebuildMtimeMs = 0;
|
|
188
|
+
private lastCanonicalCodexCheckAt = 0;
|
|
187
189
|
private cachedSqliteDb: {
|
|
188
190
|
prepare(sql: string): { get(...values: Array<string | number>): unknown };
|
|
189
191
|
close(): void;
|
|
@@ -1047,6 +1049,26 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1047
1049
|
const canonicalHistory = this.provider.canonicalHistory;
|
|
1048
1050
|
if (!canonicalHistory) return false;
|
|
1049
1051
|
|
|
1052
|
+
if (isNativeSourceCanonicalHistory(canonicalHistory)) {
|
|
1053
|
+
const restoredHistory = readProviderChatHistory(this.type, {
|
|
1054
|
+
canonicalHistory,
|
|
1055
|
+
historySessionId: this.providerSessionId,
|
|
1056
|
+
workspace: this.workingDir,
|
|
1057
|
+
offset: 0,
|
|
1058
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
1059
|
+
historyBehavior: this.provider.historyBehavior,
|
|
1060
|
+
});
|
|
1061
|
+
if (restoredHistory.source !== 'provider-native') return false;
|
|
1062
|
+
this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
|
|
1063
|
+
role: message.role,
|
|
1064
|
+
content: message.content,
|
|
1065
|
+
kind: message.kind,
|
|
1066
|
+
senderName: message.senderName,
|
|
1067
|
+
receivedAt: message.receivedAt,
|
|
1068
|
+
}));
|
|
1069
|
+
return true;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1050
1072
|
try {
|
|
1051
1073
|
let rebuilt = false;
|
|
1052
1074
|
if (canonicalHistory.format === 'hermes-json') {
|
|
@@ -1085,6 +1107,22 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1085
1107
|
if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalClaudeRebuildMtimeMs) return true;
|
|
1086
1108
|
rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
|
|
1087
1109
|
if (rebuilt) this.lastCanonicalClaudeRebuildMtimeMs = transcriptMtime || Date.now();
|
|
1110
|
+
} else if (canonicalHistory.format === 'codex-jsonl') {
|
|
1111
|
+
// Codex stores rollout transcripts under ~/.codex/sessions/YYYY/MM/DD/.
|
|
1112
|
+
// Resolving requires a recursive lookup, so throttle the probe like Claude.
|
|
1113
|
+
const now = Date.now();
|
|
1114
|
+
if (now - this.lastCanonicalCodexCheckAt < 2_000 && this.lastCanonicalCodexRebuildMtimeMs !== 0) {
|
|
1115
|
+
return true;
|
|
1116
|
+
}
|
|
1117
|
+
this.lastCanonicalCodexCheckAt = now;
|
|
1118
|
+
const transcriptFile = resolveCodexSessionTranscriptPath(this.providerSessionId, this.workingDir);
|
|
1119
|
+
let transcriptMtime = 0;
|
|
1120
|
+
if (transcriptFile) {
|
|
1121
|
+
try { transcriptMtime = fs.statSync(transcriptFile).mtimeMs; } catch { /* not found yet */ }
|
|
1122
|
+
}
|
|
1123
|
+
if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalCodexRebuildMtimeMs) return true;
|
|
1124
|
+
rebuilt = rebuildCodexSavedHistoryFromNativeSession(this.providerSessionId, this.workingDir);
|
|
1125
|
+
if (rebuilt) this.lastCanonicalCodexRebuildMtimeMs = transcriptMtime || Date.now();
|
|
1088
1126
|
}
|
|
1089
1127
|
if (!rebuilt) return false;
|
|
1090
1128
|
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
|
|
@@ -1104,8 +1142,19 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1104
1142
|
private restorePersistedHistoryFromCurrentSession(): void {
|
|
1105
1143
|
if (!this.providerSessionId) return;
|
|
1106
1144
|
this.syncCanonicalSavedHistoryIfNeeded();
|
|
1107
|
-
|
|
1108
|
-
|
|
1145
|
+
const restoredHistory = isNativeSourceCanonicalHistory(this.provider.canonicalHistory)
|
|
1146
|
+
? readProviderChatHistory(this.type, {
|
|
1147
|
+
canonicalHistory: this.provider.canonicalHistory,
|
|
1148
|
+
historySessionId: this.providerSessionId,
|
|
1149
|
+
workspace: this.workingDir,
|
|
1150
|
+
offset: 0,
|
|
1151
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
1152
|
+
historyBehavior: this.provider.historyBehavior,
|
|
1153
|
+
})
|
|
1154
|
+
: (() => {
|
|
1155
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId!, this.provider.historyBehavior);
|
|
1156
|
+
return readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
|
|
1157
|
+
})();
|
|
1109
1158
|
this.historyWriter.seedSessionHistory(
|
|
1110
1159
|
this.type,
|
|
1111
1160
|
restoredHistory.messages,
|
|
@@ -36,7 +36,10 @@ export interface ReadChatResult {
|
|
|
36
36
|
controlValues?: Record<string, string | number | boolean>;
|
|
37
37
|
/** Flexible always-visible metadata for compact/live surfaces. */
|
|
38
38
|
summaryMetadata?: ProviderSummaryMetadata;
|
|
39
|
-
/** Provider-
|
|
39
|
+
/** Provider-owned transcript authority/coverage hints for daemon/dashboard sync. */
|
|
40
|
+
transcriptAuthority?: 'provider' | 'daemon';
|
|
41
|
+
coverage?: 'full' | 'tail' | 'current-turn';
|
|
42
|
+
/** Provider-driven UI effects derived from chat state */
|
|
40
43
|
effects?: ProviderEffect[];
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -609,13 +612,23 @@ export interface ProviderCanonicalHistoryConfig {
|
|
|
609
612
|
* Native history format.
|
|
610
613
|
* - 'hermes-json': single JSON file per session (~/.hermes/sessions/session_{{sessionId}}.json)
|
|
611
614
|
* - 'claude-jsonl': JSONL transcript under ~/.claude/projects/
|
|
615
|
+
* - 'codex-jsonl': rollout JSONL transcript under ~/.codex/sessions/YYYY/MM/DD/
|
|
612
616
|
*/
|
|
613
|
-
format: 'hermes-json' | 'claude-jsonl';
|
|
617
|
+
format: 'hermes-json' | 'claude-jsonl' | 'codex-jsonl';
|
|
614
618
|
/**
|
|
615
619
|
* Path to the native history file. Supports ~ and {{sessionId}} placeholder.
|
|
616
620
|
* e.g. "~/.hermes/sessions/session_{{sessionId}}.json"
|
|
617
621
|
*/
|
|
618
622
|
watchPath: string;
|
|
623
|
+
/**
|
|
624
|
+
* How ADHDev should use native history.
|
|
625
|
+
* - 'native-source': provider-native files are canonical; ADHDev reads them directly and keeps only in-memory/thin projections.
|
|
626
|
+
* - 'materialized-mirror': transitional compatibility mode; native files are rewritten into ~/.adhdev/history before read/list.
|
|
627
|
+
* - 'disabled': ignore native history and use ADHDev mirror only.
|
|
628
|
+
*
|
|
629
|
+
* Omitted mode defaults to 'native-source'.
|
|
630
|
+
*/
|
|
631
|
+
mode?: 'native-source' | 'materialized-mirror' | 'disabled';
|
|
619
632
|
}
|
|
620
633
|
|
|
621
634
|
/**
|
|
@@ -154,6 +154,8 @@ export function validateReadChatResultPayload(raw: unknown, source = 'read_chat'
|
|
|
154
154
|
if (raw.summaryMetadata !== undefined) normalized.summaryMetadata = raw.summaryMetadata as any
|
|
155
155
|
if (Array.isArray(raw.effects)) normalized.effects = raw.effects as any
|
|
156
156
|
if (typeof raw.providerSessionId === 'string') normalized.providerSessionId = raw.providerSessionId
|
|
157
|
+
if (raw.transcriptAuthority === 'provider' || raw.transcriptAuthority === 'daemon') normalized.transcriptAuthority = raw.transcriptAuthority
|
|
158
|
+
if (raw.coverage === 'full' || raw.coverage === 'tail' || raw.coverage === 'current-turn') normalized.coverage = raw.coverage
|
|
157
159
|
|
|
158
160
|
return normalized
|
|
159
161
|
}
|