@adhdev/daemon-core 0.9.43 → 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 +4 -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 +724 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +724 -39
- 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 +105 -6
- 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/dist/index.js
CHANGED
|
@@ -985,6 +985,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
|
|
|
985
985
|
if (raw.summaryMetadata !== void 0) normalized.summaryMetadata = raw.summaryMetadata;
|
|
986
986
|
if (Array.isArray(raw.effects)) normalized.effects = raw.effects;
|
|
987
987
|
if (typeof raw.providerSessionId === "string") normalized.providerSessionId = raw.providerSessionId;
|
|
988
|
+
if (raw.transcriptAuthority === "provider" || raw.transcriptAuthority === "daemon") normalized.transcriptAuthority = raw.transcriptAuthority;
|
|
989
|
+
if (raw.coverage === "full" || raw.coverage === "tail" || raw.coverage === "current-turn") normalized.coverage = raw.coverage;
|
|
988
990
|
return normalized;
|
|
989
991
|
}
|
|
990
992
|
var VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES;
|
|
@@ -2058,7 +2060,9 @@ var provider_cli_adapter_exports = {};
|
|
|
2058
2060
|
__export(provider_cli_adapter_exports, {
|
|
2059
2061
|
ProviderCliAdapter: () => ProviderCliAdapter,
|
|
2060
2062
|
appendBoundedText: () => appendBoundedText,
|
|
2061
|
-
normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
|
|
2063
|
+
normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime,
|
|
2064
|
+
sanitizeCliStandardMessageContent: () => sanitizeCliStandardMessageContent,
|
|
2065
|
+
trimLastAssistantEchoForCliMessages: () => trimLastAssistantEchoForCliMessages
|
|
2062
2066
|
});
|
|
2063
2067
|
function normalizeComparableTranscriptText(value) {
|
|
2064
2068
|
return sanitizeTerminalText(String(value || "")).replace(/\s+/g, " ").trim();
|
|
@@ -2098,7 +2102,76 @@ function appendBoundedText(current, chunk, maxChars) {
|
|
|
2098
2102
|
if (current.length <= keepFromCurrent) return current + chunk;
|
|
2099
2103
|
return current.slice(-keepFromCurrent) + chunk;
|
|
2100
2104
|
}
|
|
2101
|
-
|
|
2105
|
+
function isLikelyCommittedActivityPrefixContinuation(line) {
|
|
2106
|
+
const trimmed = String(line || "").trim();
|
|
2107
|
+
if (!trimmed) return false;
|
|
2108
|
+
if (COMMITTED_ACTIVITY_PREFIX_BLOCK_RE.test(trimmed)) return false;
|
|
2109
|
+
if (/\s/.test(trimmed)) return false;
|
|
2110
|
+
if (/[가-힣]/.test(trimmed)) return false;
|
|
2111
|
+
if (trimmed.length > 96) return false;
|
|
2112
|
+
return /^[A-Za-z0-9_./:@+%=-]+$/.test(trimmed);
|
|
2113
|
+
}
|
|
2114
|
+
function parseCommittedActivityPrefixBlock(lines, index) {
|
|
2115
|
+
const first = String(lines[index] || "").trim();
|
|
2116
|
+
if (!COMMITTED_ACTIVITY_PREFIX_BLOCK_RE.test(first)) return null;
|
|
2117
|
+
const parts = [first];
|
|
2118
|
+
let nextIndex = index + 1;
|
|
2119
|
+
while (nextIndex < lines.length && isLikelyCommittedActivityPrefixContinuation(lines[nextIndex])) {
|
|
2120
|
+
parts.push(String(lines[nextIndex] || "").trim());
|
|
2121
|
+
nextIndex += 1;
|
|
2122
|
+
}
|
|
2123
|
+
return { label: parts.join(""), nextIndex };
|
|
2124
|
+
}
|
|
2125
|
+
function sanitizeCliStandardMessageContent(content) {
|
|
2126
|
+
const source = String(content || "").trim();
|
|
2127
|
+
if (!source) return "";
|
|
2128
|
+
const lines = source.split(/\r?\n/);
|
|
2129
|
+
if (lines.length < 4) return source;
|
|
2130
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2131
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2132
|
+
const block = parseCommittedActivityPrefixBlock(lines, index);
|
|
2133
|
+
if (!block) continue;
|
|
2134
|
+
counts.set(block.label, (counts.get(block.label) || 0) + 1);
|
|
2135
|
+
index = block.nextIndex - 1;
|
|
2136
|
+
}
|
|
2137
|
+
const repeatedLabels = new Set(
|
|
2138
|
+
Array.from(counts.entries()).filter(([, count]) => count >= 3).map(([label]) => label)
|
|
2139
|
+
);
|
|
2140
|
+
if (repeatedLabels.size === 0) return source;
|
|
2141
|
+
const stripped = [];
|
|
2142
|
+
let removed = 0;
|
|
2143
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2144
|
+
const block = parseCommittedActivityPrefixBlock(lines, index);
|
|
2145
|
+
if (block && repeatedLabels.has(block.label)) {
|
|
2146
|
+
removed += 1;
|
|
2147
|
+
index = block.nextIndex - 1;
|
|
2148
|
+
continue;
|
|
2149
|
+
}
|
|
2150
|
+
stripped.push(lines[index]);
|
|
2151
|
+
}
|
|
2152
|
+
const next = stripped.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
2153
|
+
return removed >= 3 && next.length >= 80 ? next : source;
|
|
2154
|
+
}
|
|
2155
|
+
function sanitizeCommittedMessageForDisplay(message) {
|
|
2156
|
+
if (!message || message.role !== "assistant" || (message.kind || "standard") !== "standard") return message;
|
|
2157
|
+
const content = sanitizeCliStandardMessageContent(message.content);
|
|
2158
|
+
if (content === message.content) return message;
|
|
2159
|
+
return { ...message, content };
|
|
2160
|
+
}
|
|
2161
|
+
function trimLastAssistantEchoForCliMessages(messages, prompt) {
|
|
2162
|
+
if (!prompt) return;
|
|
2163
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2164
|
+
const message = messages[index];
|
|
2165
|
+
if (!message || message.role !== "assistant" || typeof message.content !== "string") continue;
|
|
2166
|
+
if ((message.kind || "standard") !== "standard") continue;
|
|
2167
|
+
message.content = trimPromptEchoPrefix(message.content, prompt);
|
|
2168
|
+
if (!message.content.trim()) {
|
|
2169
|
+
messages.splice(index, 1);
|
|
2170
|
+
}
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
var os10, COMMITTED_ACTIVITY_PREFIX_BLOCK_RE, ProviderCliAdapter;
|
|
2102
2175
|
var init_provider_cli_adapter = __esm({
|
|
2103
2176
|
"src/cli-adapters/provider-cli-adapter.ts"() {
|
|
2104
2177
|
"use strict";
|
|
@@ -2114,6 +2187,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2114
2187
|
init_provider_cli_config();
|
|
2115
2188
|
init_provider_cli_runtime();
|
|
2116
2189
|
init_provider_cli_shared();
|
|
2190
|
+
COMMITTED_ACTIVITY_PREFIX_BLOCK_RE = /^(?:📖|💻|🔎|📚|📋|✏️|📝|🔧|🛠️|⚙️)\s+(.+)$/;
|
|
2117
2191
|
ProviderCliAdapter = class _ProviderCliAdapter {
|
|
2118
2192
|
constructor(provider, workingDir, extraArgs = [], transportFactory = new NodePtyTransportFactory()) {
|
|
2119
2193
|
this.extraArgs = extraArgs;
|
|
@@ -2294,7 +2368,14 @@ var init_provider_cli_adapter = __esm({
|
|
|
2294
2368
|
}
|
|
2295
2369
|
return null;
|
|
2296
2370
|
}
|
|
2371
|
+
providerOwnsTranscript() {
|
|
2372
|
+
return this.provider.transcriptAuthority === "provider";
|
|
2373
|
+
}
|
|
2374
|
+
shouldUseFullProviderTranscriptContext() {
|
|
2375
|
+
return this.providerOwnsTranscript() && this.provider.transcriptContext === "full";
|
|
2376
|
+
}
|
|
2297
2377
|
selectParseBaseMessages(baseMessages) {
|
|
2378
|
+
if (this.shouldUseFullProviderTranscriptContext()) return baseMessages;
|
|
2298
2379
|
if (baseMessages.length <= _ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT) return baseMessages;
|
|
2299
2380
|
return baseMessages.slice(-_ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT);
|
|
2300
2381
|
}
|
|
@@ -2325,7 +2406,10 @@ var init_provider_cli_adapter = __esm({
|
|
|
2325
2406
|
const tailFirst = parseBaseMessages[0];
|
|
2326
2407
|
if (tailFirst && this.messagesComparable(parsedFirst, tailFirst)) {
|
|
2327
2408
|
const prefixLength = fullBaseMessages.length - parseBaseMessages.length;
|
|
2328
|
-
|
|
2409
|
+
const prefix = fullBaseMessages.slice(0, prefixLength);
|
|
2410
|
+
const shouldSanitizePrefix = !!this.currentTurnScope || this.currentStatus !== "idle" || !!this.activeModal;
|
|
2411
|
+
const nextPrefix = shouldSanitizePrefix ? prefix.map((message) => sanitizeCommittedMessageForDisplay(message)) : prefix;
|
|
2412
|
+
return [...nextPrefix, ...parsedMessages];
|
|
2329
2413
|
}
|
|
2330
2414
|
return [...fullBaseMessages, ...parsedMessages];
|
|
2331
2415
|
}
|
|
@@ -2774,9 +2858,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2774
2858
|
);
|
|
2775
2859
|
}
|
|
2776
2860
|
trimLastAssistantEcho(messages, prompt) {
|
|
2777
|
-
|
|
2778
|
-
const last = [...messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
|
|
2779
|
-
if (last) last.content = trimPromptEchoPrefix(last.content, prompt);
|
|
2861
|
+
trimLastAssistantEchoForCliMessages(messages, prompt);
|
|
2780
2862
|
}
|
|
2781
2863
|
clearAllTimers() {
|
|
2782
2864
|
if (this.responseTimeout) {
|
|
@@ -3524,10 +3606,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
3524
3606
|
}
|
|
3525
3607
|
buildCommittedChatMessages() {
|
|
3526
3608
|
return this.committedMessages.map((message, index) => {
|
|
3527
|
-
const
|
|
3609
|
+
const rawContentValue = message.content;
|
|
3610
|
+
const rawContent = typeof rawContentValue === "string" ? rawContentValue : String(rawContentValue || "");
|
|
3611
|
+
const content = message.role === "assistant" && (message.kind || "standard") === "standard" ? sanitizeCliStandardMessageContent(rawContent) : rawContent;
|
|
3528
3612
|
return buildChatMessage({
|
|
3529
3613
|
role: message.role,
|
|
3530
|
-
content
|
|
3614
|
+
content,
|
|
3531
3615
|
timestamp: message.timestamp,
|
|
3532
3616
|
kind: message.kind,
|
|
3533
3617
|
meta: message.meta,
|
|
@@ -3628,7 +3712,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
3628
3712
|
title: parsed.title || this.cliName,
|
|
3629
3713
|
messages: hydratedMessages,
|
|
3630
3714
|
activeModal: parsed.activeModal ?? this.activeModal,
|
|
3631
|
-
providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0
|
|
3715
|
+
providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0,
|
|
3716
|
+
...this.providerOwnsTranscript() ? { transcriptAuthority: "provider", coverage: this.shouldUseFullProviderTranscriptContext() ? "full" : "tail" } : {}
|
|
3632
3717
|
};
|
|
3633
3718
|
} else {
|
|
3634
3719
|
const messages = [...this.committedMessages];
|
|
@@ -7962,6 +8047,27 @@ var ChatHistoryWriter = class {
|
|
|
7962
8047
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
7963
8048
|
}
|
|
7964
8049
|
};
|
|
8050
|
+
function pageHistoryRecords(agentType, records, offset = 0, limit = 30, excludeRecentCount = 0, historyBehavior) {
|
|
8051
|
+
const allMessages = records.map((message) => sanitizeHistoryMessage(agentType, message)).filter(Boolean);
|
|
8052
|
+
allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
8053
|
+
const chronological = [];
|
|
8054
|
+
let lastTurn = null;
|
|
8055
|
+
for (const message of allMessages) {
|
|
8056
|
+
const previous = chronological[chronological.length - 1];
|
|
8057
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
8058
|
+
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
8059
|
+
chronological.push(message);
|
|
8060
|
+
if (message.role !== "system") lastTurn = message;
|
|
8061
|
+
}
|
|
8062
|
+
const collapsed = collapseReplayAssistantTurns(chronological, historyBehavior);
|
|
8063
|
+
const boundedLimit = Math.max(1, limit);
|
|
8064
|
+
const boundedOffset = Math.max(0, offset);
|
|
8065
|
+
const boundedExclude = Math.max(0, Math.min(excludeRecentCount, collapsed.length));
|
|
8066
|
+
const endExclusive = Math.max(0, collapsed.length - boundedExclude - boundedOffset);
|
|
8067
|
+
const startInclusive = Math.max(0, endExclusive - boundedLimit);
|
|
8068
|
+
const sliced = collapsed.slice(startInclusive, endExclusive);
|
|
8069
|
+
return { messages: sliced, hasMore: startInclusive > 0 };
|
|
8070
|
+
}
|
|
7965
8071
|
function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, excludeRecentCount = 0, historyBehavior) {
|
|
7966
8072
|
try {
|
|
7967
8073
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -7987,25 +8093,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, ex
|
|
|
7987
8093
|
}
|
|
7988
8094
|
}
|
|
7989
8095
|
}
|
|
7990
|
-
|
|
7991
|
-
const chronological = [];
|
|
7992
|
-
let lastTurn = null;
|
|
7993
|
-
for (const message of allMessages) {
|
|
7994
|
-
const previous = chronological[chronological.length - 1];
|
|
7995
|
-
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
7996
|
-
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
7997
|
-
chronological.push(message);
|
|
7998
|
-
if (message.role !== "system") lastTurn = message;
|
|
7999
|
-
}
|
|
8000
|
-
const collapsed = collapseReplayAssistantTurns(chronological, historyBehavior);
|
|
8001
|
-
const boundedLimit = Math.max(1, limit);
|
|
8002
|
-
const boundedOffset = Math.max(0, offset);
|
|
8003
|
-
const boundedExclude = Math.max(0, Math.min(excludeRecentCount, collapsed.length));
|
|
8004
|
-
const endExclusive = Math.max(0, collapsed.length - boundedExclude - boundedOffset);
|
|
8005
|
-
const startInclusive = Math.max(0, endExclusive - boundedLimit);
|
|
8006
|
-
const sliced = collapsed.slice(startInclusive, endExclusive);
|
|
8007
|
-
const hasMore = startInclusive > 0;
|
|
8008
|
-
return { messages: sliced, hasMore };
|
|
8096
|
+
return pageHistoryRecords(agentType, allMessages, offset, limit, excludeRecentCount, historyBehavior);
|
|
8009
8097
|
} catch {
|
|
8010
8098
|
return { messages: [], hasMore: false };
|
|
8011
8099
|
}
|
|
@@ -8140,6 +8228,52 @@ function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
|
|
|
8140
8228
|
return false;
|
|
8141
8229
|
}
|
|
8142
8230
|
}
|
|
8231
|
+
function buildHermesNativeHistoryRecords(historySessionId) {
|
|
8232
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8233
|
+
if (!normalizedSessionId) return null;
|
|
8234
|
+
try {
|
|
8235
|
+
const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
|
|
8236
|
+
if (!fs3.existsSync(sessionFilePath)) return null;
|
|
8237
|
+
const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
|
|
8238
|
+
const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
|
|
8239
|
+
const records = [];
|
|
8240
|
+
let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
|
|
8241
|
+
for (const message of canonicalMessages) {
|
|
8242
|
+
const role = String(message.role || "").trim();
|
|
8243
|
+
const content = normalizeCanonicalHermesMessageContent(message.content);
|
|
8244
|
+
if (!content) continue;
|
|
8245
|
+
const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
|
|
8246
|
+
fallbackTs = receivedAt + 1;
|
|
8247
|
+
if (role === "user" || role === "assistant") {
|
|
8248
|
+
records.push({
|
|
8249
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8250
|
+
receivedAt,
|
|
8251
|
+
role,
|
|
8252
|
+
content,
|
|
8253
|
+
kind: "standard",
|
|
8254
|
+
agent: "hermes-cli",
|
|
8255
|
+
historySessionId: normalizedSessionId
|
|
8256
|
+
});
|
|
8257
|
+
continue;
|
|
8258
|
+
}
|
|
8259
|
+
if (role === "tool") {
|
|
8260
|
+
records.push({
|
|
8261
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8262
|
+
receivedAt,
|
|
8263
|
+
role: "assistant",
|
|
8264
|
+
content,
|
|
8265
|
+
kind: "tool",
|
|
8266
|
+
senderName: "Tool",
|
|
8267
|
+
agent: "hermes-cli",
|
|
8268
|
+
historySessionId: normalizedSessionId
|
|
8269
|
+
});
|
|
8270
|
+
}
|
|
8271
|
+
}
|
|
8272
|
+
return records;
|
|
8273
|
+
} catch {
|
|
8274
|
+
return null;
|
|
8275
|
+
}
|
|
8276
|
+
}
|
|
8143
8277
|
function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
8144
8278
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8145
8279
|
if (!normalizedSessionId) return false;
|
|
@@ -8289,6 +8423,77 @@ function extractClaudeUserContentParts(content) {
|
|
|
8289
8423
|
}
|
|
8290
8424
|
return parts;
|
|
8291
8425
|
}
|
|
8426
|
+
function buildClaudeNativeHistoryRecords(historySessionId, workspace) {
|
|
8427
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8428
|
+
if (!normalizedSessionId) return null;
|
|
8429
|
+
try {
|
|
8430
|
+
const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
|
|
8431
|
+
if (!transcriptPath) return null;
|
|
8432
|
+
const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
|
|
8433
|
+
const records = [];
|
|
8434
|
+
let fallbackTs = Date.now();
|
|
8435
|
+
for (const line of lines) {
|
|
8436
|
+
let parsed = null;
|
|
8437
|
+
try {
|
|
8438
|
+
parsed = JSON.parse(line);
|
|
8439
|
+
} catch {
|
|
8440
|
+
parsed = null;
|
|
8441
|
+
}
|
|
8442
|
+
if (!parsed) continue;
|
|
8443
|
+
const parsedSessionId = String(parsed.sessionId || "").trim();
|
|
8444
|
+
if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
|
|
8445
|
+
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
8446
|
+
fallbackTs = receivedAt + 1;
|
|
8447
|
+
const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
|
|
8448
|
+
if (records.length === 0 && parsedWorkspace) {
|
|
8449
|
+
records.push({
|
|
8450
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8451
|
+
receivedAt,
|
|
8452
|
+
role: "system",
|
|
8453
|
+
kind: "session_start",
|
|
8454
|
+
content: parsedWorkspace,
|
|
8455
|
+
agent: "claude-cli",
|
|
8456
|
+
historySessionId: normalizedSessionId,
|
|
8457
|
+
workspace: parsedWorkspace
|
|
8458
|
+
});
|
|
8459
|
+
}
|
|
8460
|
+
const type = String(parsed.type || "").trim();
|
|
8461
|
+
const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
|
|
8462
|
+
if (type === "user" && message) {
|
|
8463
|
+
for (const part of extractClaudeUserContentParts(message.content)) {
|
|
8464
|
+
records.push({
|
|
8465
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8466
|
+
receivedAt,
|
|
8467
|
+
role: part.role,
|
|
8468
|
+
content: part.content,
|
|
8469
|
+
kind: part.kind,
|
|
8470
|
+
senderName: part.senderName,
|
|
8471
|
+
agent: "claude-cli",
|
|
8472
|
+
historySessionId: normalizedSessionId
|
|
8473
|
+
});
|
|
8474
|
+
}
|
|
8475
|
+
continue;
|
|
8476
|
+
}
|
|
8477
|
+
if (type === "assistant" && message) {
|
|
8478
|
+
for (const part of extractClaudeAssistantContentParts(message.content)) {
|
|
8479
|
+
records.push({
|
|
8480
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8481
|
+
receivedAt,
|
|
8482
|
+
role: "assistant",
|
|
8483
|
+
content: part.content,
|
|
8484
|
+
kind: part.kind,
|
|
8485
|
+
senderName: part.senderName,
|
|
8486
|
+
agent: "claude-cli",
|
|
8487
|
+
historySessionId: normalizedSessionId
|
|
8488
|
+
});
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8491
|
+
}
|
|
8492
|
+
return records;
|
|
8493
|
+
} catch {
|
|
8494
|
+
return null;
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8292
8497
|
function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace) {
|
|
8293
8498
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8294
8499
|
if (!normalizedSessionId) return false;
|
|
@@ -8367,6 +8572,352 @@ function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace)
|
|
|
8367
8572
|
return false;
|
|
8368
8573
|
}
|
|
8369
8574
|
}
|
|
8575
|
+
function isUuidLikeSessionId(sessionId) {
|
|
8576
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
|
|
8577
|
+
}
|
|
8578
|
+
function readCodexSessionMeta(filePath) {
|
|
8579
|
+
try {
|
|
8580
|
+
const firstLine = fs3.readFileSync(filePath, "utf-8").split("\n").find(Boolean);
|
|
8581
|
+
if (!firstLine) return null;
|
|
8582
|
+
const parsed = JSON.parse(firstLine);
|
|
8583
|
+
if (String(parsed.type || "") !== "session_meta") return null;
|
|
8584
|
+
const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
|
|
8585
|
+
return payload;
|
|
8586
|
+
} catch {
|
|
8587
|
+
return null;
|
|
8588
|
+
}
|
|
8589
|
+
}
|
|
8590
|
+
function resolveCodexSessionTranscriptPath(historySessionId, workspace) {
|
|
8591
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8592
|
+
if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
|
|
8593
|
+
const sessionsDir = path7.join(os5.homedir(), ".codex", "sessions");
|
|
8594
|
+
if (!fs3.existsSync(sessionsDir)) return null;
|
|
8595
|
+
const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
|
|
8596
|
+
const candidates = [];
|
|
8597
|
+
const stack = [sessionsDir];
|
|
8598
|
+
while (stack.length > 0) {
|
|
8599
|
+
const current = stack.pop();
|
|
8600
|
+
if (!current) continue;
|
|
8601
|
+
let entries = [];
|
|
8602
|
+
try {
|
|
8603
|
+
entries = fs3.readdirSync(current, { withFileTypes: true });
|
|
8604
|
+
} catch {
|
|
8605
|
+
continue;
|
|
8606
|
+
}
|
|
8607
|
+
for (const entry of entries) {
|
|
8608
|
+
const entryPath = path7.join(current, entry.name);
|
|
8609
|
+
if (entry.isDirectory()) {
|
|
8610
|
+
stack.push(entryPath);
|
|
8611
|
+
continue;
|
|
8612
|
+
}
|
|
8613
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl") || !entry.name.includes(normalizedSessionId)) continue;
|
|
8614
|
+
const meta = readCodexSessionMeta(entryPath);
|
|
8615
|
+
const metaSessionId = String(meta?.id || "").trim();
|
|
8616
|
+
if (metaSessionId && metaSessionId !== normalizedSessionId) continue;
|
|
8617
|
+
const metaWorkspace = String(meta?.cwd || "").trim();
|
|
8618
|
+
let mtimeMs = 0;
|
|
8619
|
+
try {
|
|
8620
|
+
mtimeMs = fs3.statSync(entryPath).mtimeMs;
|
|
8621
|
+
} catch {
|
|
8622
|
+
}
|
|
8623
|
+
candidates.push({
|
|
8624
|
+
path: entryPath,
|
|
8625
|
+
mtimeMs,
|
|
8626
|
+
workspaceMatches: !!normalizedWorkspace && metaWorkspace === normalizedWorkspace,
|
|
8627
|
+
metaMatches: metaSessionId === normalizedSessionId
|
|
8628
|
+
});
|
|
8629
|
+
}
|
|
8630
|
+
}
|
|
8631
|
+
candidates.sort((a, b) => Number(b.workspaceMatches) - Number(a.workspaceMatches) || Number(b.metaMatches) - Number(a.metaMatches) || b.mtimeMs - a.mtimeMs);
|
|
8632
|
+
return candidates[0]?.path || null;
|
|
8633
|
+
}
|
|
8634
|
+
function flattenCodexContent(content) {
|
|
8635
|
+
if (typeof content === "string") return content.trim();
|
|
8636
|
+
if (content == null) return "";
|
|
8637
|
+
if (Array.isArray(content)) {
|
|
8638
|
+
return content.map((entry) => flattenCodexContent(entry)).filter(Boolean).join("\n").trim();
|
|
8639
|
+
}
|
|
8640
|
+
if (typeof content === "object") {
|
|
8641
|
+
const record = content;
|
|
8642
|
+
if (typeof record.text === "string") return record.text.trim();
|
|
8643
|
+
if (typeof record.content === "string" || Array.isArray(record.content)) return flattenCodexContent(record.content);
|
|
8644
|
+
if (typeof record.output === "string") return record.output.trim();
|
|
8645
|
+
if (typeof record.message === "string") return record.message.trim();
|
|
8646
|
+
}
|
|
8647
|
+
return "";
|
|
8648
|
+
}
|
|
8649
|
+
function summarizeCodexToolCall(payload) {
|
|
8650
|
+
const name = String(payload.name || payload.type || "tool").trim() || "tool";
|
|
8651
|
+
const rawArguments = payload.arguments ?? payload.input;
|
|
8652
|
+
let argumentValue = "";
|
|
8653
|
+
if (typeof rawArguments === "string") {
|
|
8654
|
+
const trimmed = rawArguments.trim();
|
|
8655
|
+
try {
|
|
8656
|
+
const parsed = JSON.parse(trimmed);
|
|
8657
|
+
argumentValue = summarizeCodexToolArguments(parsed);
|
|
8658
|
+
} catch {
|
|
8659
|
+
argumentValue = trimmed;
|
|
8660
|
+
}
|
|
8661
|
+
} else {
|
|
8662
|
+
argumentValue = summarizeCodexToolArguments(rawArguments);
|
|
8663
|
+
}
|
|
8664
|
+
return argumentValue ? `${name}: ${argumentValue}` : name;
|
|
8665
|
+
}
|
|
8666
|
+
function summarizeCodexToolArguments(value) {
|
|
8667
|
+
if (typeof value === "string") return value.trim();
|
|
8668
|
+
if (Array.isArray(value)) return value.map((entry) => String(entry)).join(" ").trim();
|
|
8669
|
+
if (!value || typeof value !== "object") return "";
|
|
8670
|
+
const record = value;
|
|
8671
|
+
const direct = record.command || record.cmd || record.query || record.path || record.prompt;
|
|
8672
|
+
if (typeof direct === "string") return direct.trim();
|
|
8673
|
+
if (Array.isArray(direct)) return direct.map((entry) => String(entry)).join(" ").trim();
|
|
8674
|
+
try {
|
|
8675
|
+
return JSON.stringify(record).trim();
|
|
8676
|
+
} catch {
|
|
8677
|
+
return "";
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
function codexToolOutputContent(payload) {
|
|
8681
|
+
const output = payload.output ?? payload.result ?? payload.content;
|
|
8682
|
+
const text = flattenCodexContent(output);
|
|
8683
|
+
if (text) return text;
|
|
8684
|
+
if (output && typeof output === "object") {
|
|
8685
|
+
try {
|
|
8686
|
+
return JSON.stringify(output).trim();
|
|
8687
|
+
} catch {
|
|
8688
|
+
return "";
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
return "";
|
|
8692
|
+
}
|
|
8693
|
+
function buildCodexNativeHistoryRecords(historySessionId, workspace) {
|
|
8694
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8695
|
+
if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
|
|
8696
|
+
try {
|
|
8697
|
+
const transcriptPath = resolveCodexSessionTranscriptPath(normalizedSessionId, workspace);
|
|
8698
|
+
if (!transcriptPath) return null;
|
|
8699
|
+
const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
|
|
8700
|
+
const records = [];
|
|
8701
|
+
let fallbackTs = Date.now();
|
|
8702
|
+
for (const line of lines) {
|
|
8703
|
+
let parsed = null;
|
|
8704
|
+
try {
|
|
8705
|
+
parsed = JSON.parse(line);
|
|
8706
|
+
} catch {
|
|
8707
|
+
parsed = null;
|
|
8708
|
+
}
|
|
8709
|
+
if (!parsed) continue;
|
|
8710
|
+
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
8711
|
+
fallbackTs = receivedAt + 1;
|
|
8712
|
+
const type = String(parsed.type || "").trim();
|
|
8713
|
+
const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
|
|
8714
|
+
if (!payload) continue;
|
|
8715
|
+
if (type === "session_meta") {
|
|
8716
|
+
const parsedSessionId = String(payload.id || "").trim();
|
|
8717
|
+
if (parsedSessionId && parsedSessionId !== normalizedSessionId) return null;
|
|
8718
|
+
const parsedWorkspace = String(payload.cwd || workspace || "").trim();
|
|
8719
|
+
if (records.length === 0 && parsedWorkspace) {
|
|
8720
|
+
records.push({
|
|
8721
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8722
|
+
receivedAt,
|
|
8723
|
+
role: "system",
|
|
8724
|
+
kind: "session_start",
|
|
8725
|
+
content: parsedWorkspace,
|
|
8726
|
+
agent: "codex-cli",
|
|
8727
|
+
historySessionId: normalizedSessionId,
|
|
8728
|
+
workspace: parsedWorkspace
|
|
8729
|
+
});
|
|
8730
|
+
}
|
|
8731
|
+
continue;
|
|
8732
|
+
}
|
|
8733
|
+
if (type !== "response_item") continue;
|
|
8734
|
+
const payloadType = String(payload.type || "").trim();
|
|
8735
|
+
if (payloadType === "message") {
|
|
8736
|
+
const role = String(payload.role || "").trim();
|
|
8737
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
8738
|
+
const content = flattenCodexContent(payload.content);
|
|
8739
|
+
if (!content) continue;
|
|
8740
|
+
records.push({
|
|
8741
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8742
|
+
receivedAt,
|
|
8743
|
+
role,
|
|
8744
|
+
content,
|
|
8745
|
+
kind: "standard",
|
|
8746
|
+
agent: "codex-cli",
|
|
8747
|
+
historySessionId: normalizedSessionId
|
|
8748
|
+
});
|
|
8749
|
+
continue;
|
|
8750
|
+
}
|
|
8751
|
+
if (payloadType === "function_call" || payloadType === "custom_tool_call") {
|
|
8752
|
+
const content = summarizeCodexToolCall(payload);
|
|
8753
|
+
if (!content) continue;
|
|
8754
|
+
records.push({
|
|
8755
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8756
|
+
receivedAt,
|
|
8757
|
+
role: "assistant",
|
|
8758
|
+
content,
|
|
8759
|
+
kind: "tool",
|
|
8760
|
+
senderName: "Tool",
|
|
8761
|
+
agent: "codex-cli",
|
|
8762
|
+
historySessionId: normalizedSessionId
|
|
8763
|
+
});
|
|
8764
|
+
continue;
|
|
8765
|
+
}
|
|
8766
|
+
if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
|
|
8767
|
+
const content = codexToolOutputContent(payload);
|
|
8768
|
+
if (!content) continue;
|
|
8769
|
+
records.push({
|
|
8770
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8771
|
+
receivedAt,
|
|
8772
|
+
role: "assistant",
|
|
8773
|
+
content,
|
|
8774
|
+
kind: "tool",
|
|
8775
|
+
senderName: "Tool",
|
|
8776
|
+
agent: "codex-cli",
|
|
8777
|
+
historySessionId: normalizedSessionId
|
|
8778
|
+
});
|
|
8779
|
+
}
|
|
8780
|
+
}
|
|
8781
|
+
return records;
|
|
8782
|
+
} catch {
|
|
8783
|
+
return null;
|
|
8784
|
+
}
|
|
8785
|
+
}
|
|
8786
|
+
function rebuildCodexSavedHistoryFromNativeSession(historySessionId, workspace) {
|
|
8787
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8788
|
+
if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return false;
|
|
8789
|
+
const records = buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
|
|
8790
|
+
if (!records || records.length === 0) return false;
|
|
8791
|
+
const existingSessionStart = readExistingSessionStartRecord("codex-cli", normalizedSessionId);
|
|
8792
|
+
const recordsToWrite = existingSessionStart && records[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId }, ...records] : records;
|
|
8793
|
+
return rewriteCanonicalSavedHistory("codex-cli", normalizedSessionId, recordsToWrite);
|
|
8794
|
+
}
|
|
8795
|
+
function isNativeSourceCanonicalHistory(canonicalHistory) {
|
|
8796
|
+
if (!canonicalHistory) return false;
|
|
8797
|
+
if (canonicalHistory.mode === "disabled") return false;
|
|
8798
|
+
if (canonicalHistory.mode === "materialized-mirror") return false;
|
|
8799
|
+
return true;
|
|
8800
|
+
}
|
|
8801
|
+
function buildNativeHistoryRecords(canonicalHistory, historySessionId, workspace) {
|
|
8802
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
|
|
8803
|
+
if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
|
|
8804
|
+
if (canonicalHistory.format === "hermes-json") return buildHermesNativeHistoryRecords(normalizedSessionId);
|
|
8805
|
+
if (canonicalHistory.format === "claude-jsonl") return buildClaudeNativeHistoryRecords(normalizedSessionId, workspace);
|
|
8806
|
+
if (canonicalHistory.format === "codex-jsonl") return buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
|
|
8807
|
+
return null;
|
|
8808
|
+
}
|
|
8809
|
+
function readProviderChatHistory(agentType, options = {}) {
|
|
8810
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
|
|
8811
|
+
const records = buildNativeHistoryRecords(options.canonicalHistory, options.historySessionId, options.workspace);
|
|
8812
|
+
if (!records) return { messages: [], hasMore: false, source: "native-unavailable" };
|
|
8813
|
+
return {
|
|
8814
|
+
...pageHistoryRecords(agentType, records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
|
|
8815
|
+
source: "provider-native"
|
|
8816
|
+
};
|
|
8817
|
+
}
|
|
8818
|
+
return {
|
|
8819
|
+
...readChatHistory(agentType, options.offset || 0, options.limit || 30, options.historySessionId, options.excludeRecentCount || 0, options.historyBehavior),
|
|
8820
|
+
source: "adhdev-mirror"
|
|
8821
|
+
};
|
|
8822
|
+
}
|
|
8823
|
+
function buildNativeSessionSummary(agentType, historySessionId, records, sourcePath) {
|
|
8824
|
+
const visible = pageHistoryRecords(agentType, records, 0, Number.MAX_SAFE_INTEGER).messages;
|
|
8825
|
+
if (visible.length === 0) return null;
|
|
8826
|
+
let sourceMtimeMs = 0;
|
|
8827
|
+
try {
|
|
8828
|
+
sourceMtimeMs = fs3.statSync(sourcePath).mtimeMs;
|
|
8829
|
+
} catch {
|
|
8830
|
+
}
|
|
8831
|
+
const firstMessageAt = visible[0]?.receivedAt || sourceMtimeMs || Date.now();
|
|
8832
|
+
const lastMessageAt = visible[visible.length - 1]?.receivedAt || firstMessageAt;
|
|
8833
|
+
const lastNonSystem = [...visible].reverse().find((message) => message.role !== "system") || visible[visible.length - 1];
|
|
8834
|
+
const firstSystem = visible.find((message) => message.kind === "session_start");
|
|
8835
|
+
return {
|
|
8836
|
+
historySessionId,
|
|
8837
|
+
sessionTitle: lastNonSystem?.content,
|
|
8838
|
+
messageCount: visible.length,
|
|
8839
|
+
firstMessageAt,
|
|
8840
|
+
lastMessageAt,
|
|
8841
|
+
preview: lastNonSystem?.content,
|
|
8842
|
+
workspace: firstSystem?.workspace || (firstSystem?.kind === "session_start" ? firstSystem.content : void 0),
|
|
8843
|
+
source: "provider-native",
|
|
8844
|
+
sourcePath,
|
|
8845
|
+
sourceMtimeMs
|
|
8846
|
+
};
|
|
8847
|
+
}
|
|
8848
|
+
function listFilesRecursive(root, predicate) {
|
|
8849
|
+
if (!fs3.existsSync(root)) return [];
|
|
8850
|
+
const results = [];
|
|
8851
|
+
const stack = [root];
|
|
8852
|
+
while (stack.length > 0) {
|
|
8853
|
+
const current = stack.pop();
|
|
8854
|
+
if (!current) continue;
|
|
8855
|
+
let entries = [];
|
|
8856
|
+
try {
|
|
8857
|
+
entries = fs3.readdirSync(current, { withFileTypes: true });
|
|
8858
|
+
} catch {
|
|
8859
|
+
continue;
|
|
8860
|
+
}
|
|
8861
|
+
for (const entry of entries) {
|
|
8862
|
+
const entryPath = path7.join(current, entry.name);
|
|
8863
|
+
if (entry.isDirectory()) {
|
|
8864
|
+
stack.push(entryPath);
|
|
8865
|
+
continue;
|
|
8866
|
+
}
|
|
8867
|
+
if (predicate(entryPath, entry)) results.push(entryPath);
|
|
8868
|
+
}
|
|
8869
|
+
}
|
|
8870
|
+
return results;
|
|
8871
|
+
}
|
|
8872
|
+
function collectNativeHistorySessionSummaries(agentType, canonicalHistory) {
|
|
8873
|
+
const summaries = [];
|
|
8874
|
+
if (canonicalHistory.format === "hermes-json") {
|
|
8875
|
+
const root = path7.join(os5.homedir(), ".hermes", "sessions");
|
|
8876
|
+
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && /^session_.+\.json$/.test(entry.name))) {
|
|
8877
|
+
const fileName = path7.basename(filePath);
|
|
8878
|
+
const historySessionId = fileName.replace(/^session_/, "").replace(/\.json$/, "");
|
|
8879
|
+
const records = buildHermesNativeHistoryRecords(historySessionId);
|
|
8880
|
+
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
8881
|
+
if (summary) summaries.push(summary);
|
|
8882
|
+
}
|
|
8883
|
+
} else if (canonicalHistory.format === "claude-jsonl") {
|
|
8884
|
+
const root = path7.join(os5.homedir(), ".claude", "projects");
|
|
8885
|
+
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
|
|
8886
|
+
const historySessionId = path7.basename(filePath, ".jsonl");
|
|
8887
|
+
const records = buildClaudeNativeHistoryRecords(historySessionId);
|
|
8888
|
+
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
8889
|
+
if (summary) summaries.push(summary);
|
|
8890
|
+
}
|
|
8891
|
+
} else if (canonicalHistory.format === "codex-jsonl") {
|
|
8892
|
+
const root = path7.join(os5.homedir(), ".codex", "sessions");
|
|
8893
|
+
const uuidPattern = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
|
|
8894
|
+
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
|
|
8895
|
+
const meta = readCodexSessionMeta(filePath);
|
|
8896
|
+
const historySessionId = String(meta?.id || path7.basename(filePath).match(uuidPattern)?.[1] || "").trim();
|
|
8897
|
+
if (!historySessionId) continue;
|
|
8898
|
+
const records = buildCodexNativeHistoryRecords(historySessionId, String(meta?.cwd || "").trim() || void 0);
|
|
8899
|
+
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
8900
|
+
if (summary) summaries.push(summary);
|
|
8901
|
+
}
|
|
8902
|
+
}
|
|
8903
|
+
return sortSavedHistorySessionSummaries(summaries);
|
|
8904
|
+
}
|
|
8905
|
+
function listProviderHistorySessions(agentType, options = {}) {
|
|
8906
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory)) {
|
|
8907
|
+
const offset = Math.max(0, options.offset || 0);
|
|
8908
|
+
const limit = Math.max(1, options.limit || 30);
|
|
8909
|
+
const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory);
|
|
8910
|
+
return {
|
|
8911
|
+
sessions: summaries.slice(offset, offset + limit),
|
|
8912
|
+
hasMore: offset + limit < summaries.length,
|
|
8913
|
+
source: "provider-native"
|
|
8914
|
+
};
|
|
8915
|
+
}
|
|
8916
|
+
return {
|
|
8917
|
+
...listSavedHistorySessions(agentType, { offset: options.offset, limit: options.limit }, options.historyBehavior),
|
|
8918
|
+
source: "adhdev-mirror"
|
|
8919
|
+
};
|
|
8920
|
+
}
|
|
8370
8921
|
|
|
8371
8922
|
// src/providers/provider-patch-state.ts
|
|
8372
8923
|
function isControlValue(value) {
|
|
@@ -10980,7 +11531,16 @@ async function handleChatHistory(h, args) {
|
|
|
10980
11531
|
const visibleCount = Array.isArray(status?.messages) ? status.messages.length : 0;
|
|
10981
11532
|
if (visibleCount > excludeRecentCount) excludeRecentCount = visibleCount;
|
|
10982
11533
|
}
|
|
10983
|
-
const
|
|
11534
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : typeof h.currentSession?.workspace === "string" ? h.currentSession.workspace : void 0;
|
|
11535
|
+
const result = readProviderChatHistory(agentStr, {
|
|
11536
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
11537
|
+
historySessionId,
|
|
11538
|
+
workspace,
|
|
11539
|
+
offset: offset || 0,
|
|
11540
|
+
limit: limit || 30,
|
|
11541
|
+
excludeRecentCount,
|
|
11542
|
+
historyBehavior: provider?.historyBehavior
|
|
11543
|
+
});
|
|
10984
11544
|
return { success: true, ...result, agent: agentStr };
|
|
10985
11545
|
} catch (e) {
|
|
10986
11546
|
return { success: false, error: e.message };
|
|
@@ -11005,7 +11565,8 @@ async function handleReadChat(h, args) {
|
|
|
11005
11565
|
}
|
|
11006
11566
|
const parsedRecord = parsedStatus && typeof parsedStatus === "object" ? parsedStatus : null;
|
|
11007
11567
|
const adapterStatus = adapter.getStatus();
|
|
11008
|
-
const
|
|
11568
|
+
const parsedIsProviderAuthoritative = parsedRecord?.transcriptAuthority === "provider" || parsedRecord?.coverage === "full";
|
|
11569
|
+
const shouldPreferAdapterMessages = !parsedIsProviderAuthoritative && Array.isArray(adapterStatus.messages) && adapterStatus.messages.length > 0 && Array.isArray(parsedRecord?.messages) && adapterStatus.messages.length > parsedRecord.messages.length;
|
|
11009
11570
|
const parsedShowsApproval = hasNonEmptyModalButtons(parsedRecord?.activeModal) && parsedRecord?.status === "waiting_approval";
|
|
11010
11571
|
const status = parsedRecord ? {
|
|
11011
11572
|
...parsedRecord,
|
|
@@ -11015,6 +11576,8 @@ async function handleReadChat(h, args) {
|
|
|
11015
11576
|
} : adapterStatus;
|
|
11016
11577
|
const title = typeof parsedRecord?.title === "string" ? parsedRecord.title : void 0;
|
|
11017
11578
|
const providerSessionId = typeof parsedRecord?.providerSessionId === "string" ? parsedRecord.providerSessionId : void 0;
|
|
11579
|
+
const transcriptAuthority = parsedRecord?.transcriptAuthority === "provider" || parsedRecord?.transcriptAuthority === "daemon" ? parsedRecord.transcriptAuthority : void 0;
|
|
11580
|
+
const coverage = parsedRecord?.coverage === "full" || parsedRecord?.coverage === "tail" || parsedRecord?.coverage === "current-turn" ? parsedRecord.coverage : void 0;
|
|
11018
11581
|
if (status) {
|
|
11019
11582
|
LOG.debug("Command", `[read_chat] cli-like resolved provider=${adapter.cliType} target=${String(args?.targetSessionId || "")} adapterStatus=${String(adapterStatus.status || "")} parsedStatus=${String(parsedRecord?.status || "")} shouldPreferAdapterMessages=${String(shouldPreferAdapterMessages)} adapterMsgCount=${Array.isArray(adapterStatus.messages) ? adapterStatus.messages.length : 0} parsedMsgCount=${Array.isArray(parsedRecord?.messages) ? parsedRecord.messages.length : 0} returnedMsgCount=${Array.isArray(status.messages) ? status.messages.length : 0}`);
|
|
11020
11583
|
return buildReadChatCommandResult({
|
|
@@ -11033,7 +11596,9 @@ async function handleReadChat(h, args) {
|
|
|
11033
11596
|
returnedMsgCount: Array.isArray(status.messages) ? status.messages.length : 0
|
|
11034
11597
|
},
|
|
11035
11598
|
...title ? { title } : {},
|
|
11036
|
-
...providerSessionId ? { providerSessionId } : {}
|
|
11599
|
+
...providerSessionId ? { providerSessionId } : {},
|
|
11600
|
+
...transcriptAuthority ? { transcriptAuthority } : {},
|
|
11601
|
+
...coverage ? { coverage } : {}
|
|
11037
11602
|
}, args);
|
|
11038
11603
|
}
|
|
11039
11604
|
}
|
|
@@ -13453,6 +14018,8 @@ var CliProviderInstance = class {
|
|
|
13453
14018
|
lastCanonicalHermesWatchPath = void 0;
|
|
13454
14019
|
lastCanonicalClaudeRebuildMtimeMs = 0;
|
|
13455
14020
|
lastCanonicalClaudeCheckAt = 0;
|
|
14021
|
+
lastCanonicalCodexRebuildMtimeMs = 0;
|
|
14022
|
+
lastCanonicalCodexCheckAt = 0;
|
|
13456
14023
|
cachedSqliteDb = null;
|
|
13457
14024
|
cachedSqliteDbPath = null;
|
|
13458
14025
|
cachedSqliteDbMissingUntil = 0;
|
|
@@ -14152,6 +14719,25 @@ ${effect.notification.body || ""}`.trim();
|
|
|
14152
14719
|
if (!this.providerSessionId) return false;
|
|
14153
14720
|
const canonicalHistory = this.provider.canonicalHistory;
|
|
14154
14721
|
if (!canonicalHistory) return false;
|
|
14722
|
+
if (isNativeSourceCanonicalHistory(canonicalHistory)) {
|
|
14723
|
+
const restoredHistory = readProviderChatHistory(this.type, {
|
|
14724
|
+
canonicalHistory,
|
|
14725
|
+
historySessionId: this.providerSessionId,
|
|
14726
|
+
workspace: this.workingDir,
|
|
14727
|
+
offset: 0,
|
|
14728
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
14729
|
+
historyBehavior: this.provider.historyBehavior
|
|
14730
|
+
});
|
|
14731
|
+
if (restoredHistory.source !== "provider-native") return false;
|
|
14732
|
+
this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
|
|
14733
|
+
role: message.role,
|
|
14734
|
+
content: message.content,
|
|
14735
|
+
kind: message.kind,
|
|
14736
|
+
senderName: message.senderName,
|
|
14737
|
+
receivedAt: message.receivedAt
|
|
14738
|
+
}));
|
|
14739
|
+
return true;
|
|
14740
|
+
}
|
|
14155
14741
|
try {
|
|
14156
14742
|
let rebuilt = false;
|
|
14157
14743
|
if (canonicalHistory.format === "hermes-json") {
|
|
@@ -14185,6 +14771,23 @@ ${effect.notification.body || ""}`.trim();
|
|
|
14185
14771
|
if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalClaudeRebuildMtimeMs) return true;
|
|
14186
14772
|
rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
|
|
14187
14773
|
if (rebuilt) this.lastCanonicalClaudeRebuildMtimeMs = transcriptMtime || Date.now();
|
|
14774
|
+
} else if (canonicalHistory.format === "codex-jsonl") {
|
|
14775
|
+
const now = Date.now();
|
|
14776
|
+
if (now - this.lastCanonicalCodexCheckAt < 2e3 && this.lastCanonicalCodexRebuildMtimeMs !== 0) {
|
|
14777
|
+
return true;
|
|
14778
|
+
}
|
|
14779
|
+
this.lastCanonicalCodexCheckAt = now;
|
|
14780
|
+
const transcriptFile = resolveCodexSessionTranscriptPath(this.providerSessionId, this.workingDir);
|
|
14781
|
+
let transcriptMtime = 0;
|
|
14782
|
+
if (transcriptFile) {
|
|
14783
|
+
try {
|
|
14784
|
+
transcriptMtime = fs5.statSync(transcriptFile).mtimeMs;
|
|
14785
|
+
} catch {
|
|
14786
|
+
}
|
|
14787
|
+
}
|
|
14788
|
+
if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalCodexRebuildMtimeMs) return true;
|
|
14789
|
+
rebuilt = rebuildCodexSavedHistoryFromNativeSession(this.providerSessionId, this.workingDir);
|
|
14790
|
+
if (rebuilt) this.lastCanonicalCodexRebuildMtimeMs = transcriptMtime || Date.now();
|
|
14188
14791
|
}
|
|
14189
14792
|
if (!rebuilt) return false;
|
|
14190
14793
|
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
|
|
@@ -14203,8 +14806,17 @@ ${effect.notification.body || ""}`.trim();
|
|
|
14203
14806
|
restorePersistedHistoryFromCurrentSession() {
|
|
14204
14807
|
if (!this.providerSessionId) return;
|
|
14205
14808
|
this.syncCanonicalSavedHistoryIfNeeded();
|
|
14206
|
-
this.
|
|
14207
|
-
|
|
14809
|
+
const restoredHistory = isNativeSourceCanonicalHistory(this.provider.canonicalHistory) ? readProviderChatHistory(this.type, {
|
|
14810
|
+
canonicalHistory: this.provider.canonicalHistory,
|
|
14811
|
+
historySessionId: this.providerSessionId,
|
|
14812
|
+
workspace: this.workingDir,
|
|
14813
|
+
offset: 0,
|
|
14814
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
14815
|
+
historyBehavior: this.provider.historyBehavior
|
|
14816
|
+
}) : (() => {
|
|
14817
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId, this.provider.historyBehavior);
|
|
14818
|
+
return readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
|
|
14819
|
+
})();
|
|
14208
14820
|
this.historyWriter.seedSessionHistory(
|
|
14209
14821
|
this.type,
|
|
14210
14822
|
restoredHistory.messages,
|
|
@@ -17934,6 +18546,38 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17934
18546
|
}
|
|
17935
18547
|
};
|
|
17936
18548
|
|
|
18549
|
+
// src/launch/macos-app-process.ts
|
|
18550
|
+
function normalizeMacAppPath(appPath) {
|
|
18551
|
+
const trimmed = String(appPath || "").trim();
|
|
18552
|
+
if (!trimmed) return null;
|
|
18553
|
+
return trimmed.replace(/\/+$/, "");
|
|
18554
|
+
}
|
|
18555
|
+
function parsePsLine(line) {
|
|
18556
|
+
const match = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
18557
|
+
if (!match) return null;
|
|
18558
|
+
const pid = Number.parseInt(match[1], 10);
|
|
18559
|
+
if (!Number.isFinite(pid)) return null;
|
|
18560
|
+
return { pid, args: match[2] };
|
|
18561
|
+
}
|
|
18562
|
+
function isMacAppProcessArgs(args, appPath) {
|
|
18563
|
+
const normalized = normalizeMacAppPath(appPath);
|
|
18564
|
+
if (!normalized) return false;
|
|
18565
|
+
return String(args || "").startsWith(`${normalized}/`);
|
|
18566
|
+
}
|
|
18567
|
+
function findMacAppProcessPids(psOutput, appPaths) {
|
|
18568
|
+
const normalizedPaths = appPaths.map(normalizeMacAppPath).filter((value) => !!value);
|
|
18569
|
+
if (normalizedPaths.length === 0) return [];
|
|
18570
|
+
const pids = [];
|
|
18571
|
+
for (const line of String(psOutput || "").split(/\r?\n/)) {
|
|
18572
|
+
const parsed = parsePsLine(line);
|
|
18573
|
+
if (!parsed) continue;
|
|
18574
|
+
if (normalizedPaths.some((appPath) => isMacAppProcessArgs(parsed.args, appPath))) {
|
|
18575
|
+
pids.push(parsed.pid);
|
|
18576
|
+
}
|
|
18577
|
+
}
|
|
18578
|
+
return pids;
|
|
18579
|
+
}
|
|
18580
|
+
|
|
17937
18581
|
// src/launch.ts
|
|
17938
18582
|
var _providerLoader = null;
|
|
17939
18583
|
function getProviderLoader() {
|
|
@@ -17970,6 +18614,35 @@ function getCdpStartupTimeoutMs(ideId) {
|
|
|
17970
18614
|
function escapeForAppleScript(value) {
|
|
17971
18615
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
17972
18616
|
}
|
|
18617
|
+
function getIdePathCandidates(ideId) {
|
|
18618
|
+
return getProviderLoader().getIdePathCandidates(ideId);
|
|
18619
|
+
}
|
|
18620
|
+
function getMacAppProcessPids(ideId) {
|
|
18621
|
+
const appPaths = getIdePathCandidates(ideId);
|
|
18622
|
+
if (appPaths.length === 0) return [];
|
|
18623
|
+
try {
|
|
18624
|
+
const output = (0, import_child_process7.execSync)("ps axww -o pid=,args=", {
|
|
18625
|
+
encoding: "utf-8",
|
|
18626
|
+
timeout: 3e3,
|
|
18627
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
18628
|
+
});
|
|
18629
|
+
return findMacAppProcessPids(output, appPaths);
|
|
18630
|
+
} catch {
|
|
18631
|
+
return [];
|
|
18632
|
+
}
|
|
18633
|
+
}
|
|
18634
|
+
function killMacAppPathProcesses(ideId, signal) {
|
|
18635
|
+
const pids = getMacAppProcessPids(ideId);
|
|
18636
|
+
let signalled = false;
|
|
18637
|
+
for (const pid of pids) {
|
|
18638
|
+
try {
|
|
18639
|
+
process.kill(pid, signal);
|
|
18640
|
+
signalled = true;
|
|
18641
|
+
} catch {
|
|
18642
|
+
}
|
|
18643
|
+
}
|
|
18644
|
+
return signalled;
|
|
18645
|
+
}
|
|
17973
18646
|
async function findFreePort(ports) {
|
|
17974
18647
|
for (const port2 of ports) {
|
|
17975
18648
|
const free = await checkPortFree(port2);
|
|
@@ -18031,6 +18704,7 @@ async function killIdeProcess(ideId) {
|
|
|
18031
18704
|
} catch {
|
|
18032
18705
|
}
|
|
18033
18706
|
}
|
|
18707
|
+
killMacAppPathProcesses(ideId, "SIGTERM");
|
|
18034
18708
|
} else if (plat === "win32" && winProcesses) {
|
|
18035
18709
|
for (const proc of winProcesses) {
|
|
18036
18710
|
try {
|
|
@@ -18060,6 +18734,7 @@ async function killIdeProcess(ideId) {
|
|
|
18060
18734
|
(0, import_child_process7.execSync)(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
18061
18735
|
} catch {
|
|
18062
18736
|
}
|
|
18737
|
+
killMacAppPathProcesses(ideId, "SIGKILL");
|
|
18063
18738
|
} else if (plat === "win32" && winProcesses) {
|
|
18064
18739
|
for (const proc of winProcesses) {
|
|
18065
18740
|
try {
|
|
@@ -18079,14 +18754,16 @@ function isIdeRunning(ideId) {
|
|
|
18079
18754
|
try {
|
|
18080
18755
|
if (plat === "darwin") {
|
|
18081
18756
|
const appName = getMacAppIdentifiers()[ideId];
|
|
18082
|
-
if (!appName) return
|
|
18757
|
+
if (!appName) return getMacAppProcessPids(ideId).length > 0;
|
|
18083
18758
|
try {
|
|
18084
18759
|
const result = (0, import_child_process7.execSync)(`pgrep -x "${appName}" 2>/dev/null`, {
|
|
18085
18760
|
encoding: "utf-8",
|
|
18086
18761
|
timeout: 3e3
|
|
18087
18762
|
});
|
|
18088
|
-
|
|
18763
|
+
if (result.trim().length > 0) return true;
|
|
18089
18764
|
} catch {
|
|
18765
|
+
}
|
|
18766
|
+
try {
|
|
18090
18767
|
const result = (0, import_child_process7.execSync)(
|
|
18091
18768
|
`osascript -e 'tell application "System Events" to count (every process whose name is "${escapeForAppleScript(appName)}")'`,
|
|
18092
18769
|
{
|
|
@@ -18095,8 +18772,10 @@ function isIdeRunning(ideId) {
|
|
|
18095
18772
|
stdio: ["pipe", "pipe", "pipe"]
|
|
18096
18773
|
}
|
|
18097
18774
|
);
|
|
18098
|
-
|
|
18775
|
+
if (Number.parseInt(result.trim() || "0", 10) > 0) return true;
|
|
18776
|
+
} catch {
|
|
18099
18777
|
}
|
|
18778
|
+
return getMacAppProcessPids(ideId).length > 0;
|
|
18100
18779
|
} else if (plat === "win32") {
|
|
18101
18780
|
const winProcesses = getWinProcessNames()[ideId];
|
|
18102
18781
|
if (!winProcesses) return false;
|
|
@@ -19463,13 +20142,18 @@ var DaemonCommandRouter = class {
|
|
|
19463
20142
|
const wantsAll = args?.all === true;
|
|
19464
20143
|
const offset = wantsAll ? 0 : Math.max(0, Number(args?.offset) || 0);
|
|
19465
20144
|
const limit = wantsAll ? Number.MAX_SAFE_INTEGER : Math.max(1, Math.min(100, Number(args?.limit) || 30));
|
|
19466
|
-
const
|
|
20145
|
+
const providerMeta = this.deps.providerLoader.getMeta(providerType);
|
|
20146
|
+
const { sessions: historySessions, hasMore, source } = listProviderHistorySessions(providerType, {
|
|
20147
|
+
canonicalHistory: providerMeta?.canonicalHistory,
|
|
20148
|
+
offset,
|
|
20149
|
+
limit,
|
|
20150
|
+
historyBehavior: providerMeta?.historyBehavior
|
|
20151
|
+
});
|
|
19467
20152
|
const state = loadState();
|
|
19468
20153
|
const savedSessions = getSavedProviderSessions(state, { providerType, kind });
|
|
19469
20154
|
const recentSessions = getRecentActivity(state, 200).filter((entry) => entry.providerType === providerType && entry.kind === kind && entry.providerSessionId);
|
|
19470
20155
|
const savedSessionById = new Map(savedSessions.map((entry) => [entry.providerSessionId, entry]));
|
|
19471
20156
|
const recentSessionById = new Map(recentSessions.map((entry) => [entry.providerSessionId, entry]));
|
|
19472
|
-
const providerMeta = this.deps.providerLoader.getMeta(providerType);
|
|
19473
20157
|
const canResumeById = supportsExplicitSessionResume(providerMeta?.resume);
|
|
19474
20158
|
return {
|
|
19475
20159
|
success: true,
|
|
@@ -19492,7 +20176,8 @@ var DaemonCommandRouter = class {
|
|
|
19492
20176
|
canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById
|
|
19493
20177
|
};
|
|
19494
20178
|
}),
|
|
19495
|
-
hasMore
|
|
20179
|
+
hasMore,
|
|
20180
|
+
source
|
|
19496
20181
|
};
|
|
19497
20182
|
}
|
|
19498
20183
|
// ─── restart_session: IDE / CLI / ACP unified ───
|