@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.mjs
CHANGED
|
@@ -980,6 +980,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
|
|
|
980
980
|
if (raw.summaryMetadata !== void 0) normalized.summaryMetadata = raw.summaryMetadata;
|
|
981
981
|
if (Array.isArray(raw.effects)) normalized.effects = raw.effects;
|
|
982
982
|
if (typeof raw.providerSessionId === "string") normalized.providerSessionId = raw.providerSessionId;
|
|
983
|
+
if (raw.transcriptAuthority === "provider" || raw.transcriptAuthority === "daemon") normalized.transcriptAuthority = raw.transcriptAuthority;
|
|
984
|
+
if (raw.coverage === "full" || raw.coverage === "tail" || raw.coverage === "current-turn") normalized.coverage = raw.coverage;
|
|
983
985
|
return normalized;
|
|
984
986
|
}
|
|
985
987
|
var VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES;
|
|
@@ -2055,7 +2057,9 @@ var provider_cli_adapter_exports = {};
|
|
|
2055
2057
|
__export(provider_cli_adapter_exports, {
|
|
2056
2058
|
ProviderCliAdapter: () => ProviderCliAdapter,
|
|
2057
2059
|
appendBoundedText: () => appendBoundedText,
|
|
2058
|
-
normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
|
|
2060
|
+
normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime,
|
|
2061
|
+
sanitizeCliStandardMessageContent: () => sanitizeCliStandardMessageContent,
|
|
2062
|
+
trimLastAssistantEchoForCliMessages: () => trimLastAssistantEchoForCliMessages
|
|
2059
2063
|
});
|
|
2060
2064
|
import * as os10 from "os";
|
|
2061
2065
|
function normalizeComparableTranscriptText(value) {
|
|
@@ -2096,7 +2100,76 @@ function appendBoundedText(current, chunk, maxChars) {
|
|
|
2096
2100
|
if (current.length <= keepFromCurrent) return current + chunk;
|
|
2097
2101
|
return current.slice(-keepFromCurrent) + chunk;
|
|
2098
2102
|
}
|
|
2099
|
-
|
|
2103
|
+
function isLikelyCommittedActivityPrefixContinuation(line) {
|
|
2104
|
+
const trimmed = String(line || "").trim();
|
|
2105
|
+
if (!trimmed) return false;
|
|
2106
|
+
if (COMMITTED_ACTIVITY_PREFIX_BLOCK_RE.test(trimmed)) return false;
|
|
2107
|
+
if (/\s/.test(trimmed)) return false;
|
|
2108
|
+
if (/[가-힣]/.test(trimmed)) return false;
|
|
2109
|
+
if (trimmed.length > 96) return false;
|
|
2110
|
+
return /^[A-Za-z0-9_./:@+%=-]+$/.test(trimmed);
|
|
2111
|
+
}
|
|
2112
|
+
function parseCommittedActivityPrefixBlock(lines, index) {
|
|
2113
|
+
const first = String(lines[index] || "").trim();
|
|
2114
|
+
if (!COMMITTED_ACTIVITY_PREFIX_BLOCK_RE.test(first)) return null;
|
|
2115
|
+
const parts = [first];
|
|
2116
|
+
let nextIndex = index + 1;
|
|
2117
|
+
while (nextIndex < lines.length && isLikelyCommittedActivityPrefixContinuation(lines[nextIndex])) {
|
|
2118
|
+
parts.push(String(lines[nextIndex] || "").trim());
|
|
2119
|
+
nextIndex += 1;
|
|
2120
|
+
}
|
|
2121
|
+
return { label: parts.join(""), nextIndex };
|
|
2122
|
+
}
|
|
2123
|
+
function sanitizeCliStandardMessageContent(content) {
|
|
2124
|
+
const source = String(content || "").trim();
|
|
2125
|
+
if (!source) return "";
|
|
2126
|
+
const lines = source.split(/\r?\n/);
|
|
2127
|
+
if (lines.length < 4) return source;
|
|
2128
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2129
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2130
|
+
const block = parseCommittedActivityPrefixBlock(lines, index);
|
|
2131
|
+
if (!block) continue;
|
|
2132
|
+
counts.set(block.label, (counts.get(block.label) || 0) + 1);
|
|
2133
|
+
index = block.nextIndex - 1;
|
|
2134
|
+
}
|
|
2135
|
+
const repeatedLabels = new Set(
|
|
2136
|
+
Array.from(counts.entries()).filter(([, count]) => count >= 3).map(([label]) => label)
|
|
2137
|
+
);
|
|
2138
|
+
if (repeatedLabels.size === 0) return source;
|
|
2139
|
+
const stripped = [];
|
|
2140
|
+
let removed = 0;
|
|
2141
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2142
|
+
const block = parseCommittedActivityPrefixBlock(lines, index);
|
|
2143
|
+
if (block && repeatedLabels.has(block.label)) {
|
|
2144
|
+
removed += 1;
|
|
2145
|
+
index = block.nextIndex - 1;
|
|
2146
|
+
continue;
|
|
2147
|
+
}
|
|
2148
|
+
stripped.push(lines[index]);
|
|
2149
|
+
}
|
|
2150
|
+
const next = stripped.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
2151
|
+
return removed >= 3 && next.length >= 80 ? next : source;
|
|
2152
|
+
}
|
|
2153
|
+
function sanitizeCommittedMessageForDisplay(message) {
|
|
2154
|
+
if (!message || message.role !== "assistant" || (message.kind || "standard") !== "standard") return message;
|
|
2155
|
+
const content = sanitizeCliStandardMessageContent(message.content);
|
|
2156
|
+
if (content === message.content) return message;
|
|
2157
|
+
return { ...message, content };
|
|
2158
|
+
}
|
|
2159
|
+
function trimLastAssistantEchoForCliMessages(messages, prompt) {
|
|
2160
|
+
if (!prompt) return;
|
|
2161
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2162
|
+
const message = messages[index];
|
|
2163
|
+
if (!message || message.role !== "assistant" || typeof message.content !== "string") continue;
|
|
2164
|
+
if ((message.kind || "standard") !== "standard") continue;
|
|
2165
|
+
message.content = trimPromptEchoPrefix(message.content, prompt);
|
|
2166
|
+
if (!message.content.trim()) {
|
|
2167
|
+
messages.splice(index, 1);
|
|
2168
|
+
}
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
var COMMITTED_ACTIVITY_PREFIX_BLOCK_RE, ProviderCliAdapter;
|
|
2100
2173
|
var init_provider_cli_adapter = __esm({
|
|
2101
2174
|
"src/cli-adapters/provider-cli-adapter.ts"() {
|
|
2102
2175
|
"use strict";
|
|
@@ -2111,6 +2184,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2111
2184
|
init_provider_cli_config();
|
|
2112
2185
|
init_provider_cli_runtime();
|
|
2113
2186
|
init_provider_cli_shared();
|
|
2187
|
+
COMMITTED_ACTIVITY_PREFIX_BLOCK_RE = /^(?:📖|💻|🔎|📚|📋|✏️|📝|🔧|🛠️|⚙️)\s+(.+)$/;
|
|
2114
2188
|
ProviderCliAdapter = class _ProviderCliAdapter {
|
|
2115
2189
|
constructor(provider, workingDir, extraArgs = [], transportFactory = new NodePtyTransportFactory()) {
|
|
2116
2190
|
this.extraArgs = extraArgs;
|
|
@@ -2291,7 +2365,14 @@ var init_provider_cli_adapter = __esm({
|
|
|
2291
2365
|
}
|
|
2292
2366
|
return null;
|
|
2293
2367
|
}
|
|
2368
|
+
providerOwnsTranscript() {
|
|
2369
|
+
return this.provider.transcriptAuthority === "provider";
|
|
2370
|
+
}
|
|
2371
|
+
shouldUseFullProviderTranscriptContext() {
|
|
2372
|
+
return this.providerOwnsTranscript() && this.provider.transcriptContext === "full";
|
|
2373
|
+
}
|
|
2294
2374
|
selectParseBaseMessages(baseMessages) {
|
|
2375
|
+
if (this.shouldUseFullProviderTranscriptContext()) return baseMessages;
|
|
2295
2376
|
if (baseMessages.length <= _ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT) return baseMessages;
|
|
2296
2377
|
return baseMessages.slice(-_ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT);
|
|
2297
2378
|
}
|
|
@@ -2322,7 +2403,10 @@ var init_provider_cli_adapter = __esm({
|
|
|
2322
2403
|
const tailFirst = parseBaseMessages[0];
|
|
2323
2404
|
if (tailFirst && this.messagesComparable(parsedFirst, tailFirst)) {
|
|
2324
2405
|
const prefixLength = fullBaseMessages.length - parseBaseMessages.length;
|
|
2325
|
-
|
|
2406
|
+
const prefix = fullBaseMessages.slice(0, prefixLength);
|
|
2407
|
+
const shouldSanitizePrefix = !!this.currentTurnScope || this.currentStatus !== "idle" || !!this.activeModal;
|
|
2408
|
+
const nextPrefix = shouldSanitizePrefix ? prefix.map((message) => sanitizeCommittedMessageForDisplay(message)) : prefix;
|
|
2409
|
+
return [...nextPrefix, ...parsedMessages];
|
|
2326
2410
|
}
|
|
2327
2411
|
return [...fullBaseMessages, ...parsedMessages];
|
|
2328
2412
|
}
|
|
@@ -2771,9 +2855,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2771
2855
|
);
|
|
2772
2856
|
}
|
|
2773
2857
|
trimLastAssistantEcho(messages, prompt) {
|
|
2774
|
-
|
|
2775
|
-
const last = [...messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string");
|
|
2776
|
-
if (last) last.content = trimPromptEchoPrefix(last.content, prompt);
|
|
2858
|
+
trimLastAssistantEchoForCliMessages(messages, prompt);
|
|
2777
2859
|
}
|
|
2778
2860
|
clearAllTimers() {
|
|
2779
2861
|
if (this.responseTimeout) {
|
|
@@ -3521,10 +3603,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
3521
3603
|
}
|
|
3522
3604
|
buildCommittedChatMessages() {
|
|
3523
3605
|
return this.committedMessages.map((message, index) => {
|
|
3524
|
-
const
|
|
3606
|
+
const rawContentValue = message.content;
|
|
3607
|
+
const rawContent = typeof rawContentValue === "string" ? rawContentValue : String(rawContentValue || "");
|
|
3608
|
+
const content = message.role === "assistant" && (message.kind || "standard") === "standard" ? sanitizeCliStandardMessageContent(rawContent) : rawContent;
|
|
3525
3609
|
return buildChatMessage({
|
|
3526
3610
|
role: message.role,
|
|
3527
|
-
content
|
|
3611
|
+
content,
|
|
3528
3612
|
timestamp: message.timestamp,
|
|
3529
3613
|
kind: message.kind,
|
|
3530
3614
|
meta: message.meta,
|
|
@@ -3625,7 +3709,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
3625
3709
|
title: parsed.title || this.cliName,
|
|
3626
3710
|
messages: hydratedMessages,
|
|
3627
3711
|
activeModal: parsed.activeModal ?? this.activeModal,
|
|
3628
|
-
providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0
|
|
3712
|
+
providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0,
|
|
3713
|
+
...this.providerOwnsTranscript() ? { transcriptAuthority: "provider", coverage: this.shouldUseFullProviderTranscriptContext() ? "full" : "tail" } : {}
|
|
3629
3714
|
};
|
|
3630
3715
|
} else {
|
|
3631
3716
|
const messages = [...this.committedMessages];
|
|
@@ -7809,6 +7894,27 @@ var ChatHistoryWriter = class {
|
|
|
7809
7894
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
7810
7895
|
}
|
|
7811
7896
|
};
|
|
7897
|
+
function pageHistoryRecords(agentType, records, offset = 0, limit = 30, excludeRecentCount = 0, historyBehavior) {
|
|
7898
|
+
const allMessages = records.map((message) => sanitizeHistoryMessage(agentType, message)).filter(Boolean);
|
|
7899
|
+
allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
7900
|
+
const chronological = [];
|
|
7901
|
+
let lastTurn = null;
|
|
7902
|
+
for (const message of allMessages) {
|
|
7903
|
+
const previous = chronological[chronological.length - 1];
|
|
7904
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
7905
|
+
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
7906
|
+
chronological.push(message);
|
|
7907
|
+
if (message.role !== "system") lastTurn = message;
|
|
7908
|
+
}
|
|
7909
|
+
const collapsed = collapseReplayAssistantTurns(chronological, historyBehavior);
|
|
7910
|
+
const boundedLimit = Math.max(1, limit);
|
|
7911
|
+
const boundedOffset = Math.max(0, offset);
|
|
7912
|
+
const boundedExclude = Math.max(0, Math.min(excludeRecentCount, collapsed.length));
|
|
7913
|
+
const endExclusive = Math.max(0, collapsed.length - boundedExclude - boundedOffset);
|
|
7914
|
+
const startInclusive = Math.max(0, endExclusive - boundedLimit);
|
|
7915
|
+
const sliced = collapsed.slice(startInclusive, endExclusive);
|
|
7916
|
+
return { messages: sliced, hasMore: startInclusive > 0 };
|
|
7917
|
+
}
|
|
7812
7918
|
function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, excludeRecentCount = 0, historyBehavior) {
|
|
7813
7919
|
try {
|
|
7814
7920
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -7834,25 +7940,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, ex
|
|
|
7834
7940
|
}
|
|
7835
7941
|
}
|
|
7836
7942
|
}
|
|
7837
|
-
|
|
7838
|
-
const chronological = [];
|
|
7839
|
-
let lastTurn = null;
|
|
7840
|
-
for (const message of allMessages) {
|
|
7841
|
-
const previous = chronological[chronological.length - 1];
|
|
7842
|
-
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
7843
|
-
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
7844
|
-
chronological.push(message);
|
|
7845
|
-
if (message.role !== "system") lastTurn = message;
|
|
7846
|
-
}
|
|
7847
|
-
const collapsed = collapseReplayAssistantTurns(chronological, historyBehavior);
|
|
7848
|
-
const boundedLimit = Math.max(1, limit);
|
|
7849
|
-
const boundedOffset = Math.max(0, offset);
|
|
7850
|
-
const boundedExclude = Math.max(0, Math.min(excludeRecentCount, collapsed.length));
|
|
7851
|
-
const endExclusive = Math.max(0, collapsed.length - boundedExclude - boundedOffset);
|
|
7852
|
-
const startInclusive = Math.max(0, endExclusive - boundedLimit);
|
|
7853
|
-
const sliced = collapsed.slice(startInclusive, endExclusive);
|
|
7854
|
-
const hasMore = startInclusive > 0;
|
|
7855
|
-
return { messages: sliced, hasMore };
|
|
7943
|
+
return pageHistoryRecords(agentType, allMessages, offset, limit, excludeRecentCount, historyBehavior);
|
|
7856
7944
|
} catch {
|
|
7857
7945
|
return { messages: [], hasMore: false };
|
|
7858
7946
|
}
|
|
@@ -7987,6 +8075,52 @@ function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
|
|
|
7987
8075
|
return false;
|
|
7988
8076
|
}
|
|
7989
8077
|
}
|
|
8078
|
+
function buildHermesNativeHistoryRecords(historySessionId) {
|
|
8079
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8080
|
+
if (!normalizedSessionId) return null;
|
|
8081
|
+
try {
|
|
8082
|
+
const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
|
|
8083
|
+
if (!fs3.existsSync(sessionFilePath)) return null;
|
|
8084
|
+
const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
|
|
8085
|
+
const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
|
|
8086
|
+
const records = [];
|
|
8087
|
+
let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
|
|
8088
|
+
for (const message of canonicalMessages) {
|
|
8089
|
+
const role = String(message.role || "").trim();
|
|
8090
|
+
const content = normalizeCanonicalHermesMessageContent(message.content);
|
|
8091
|
+
if (!content) continue;
|
|
8092
|
+
const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
|
|
8093
|
+
fallbackTs = receivedAt + 1;
|
|
8094
|
+
if (role === "user" || role === "assistant") {
|
|
8095
|
+
records.push({
|
|
8096
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8097
|
+
receivedAt,
|
|
8098
|
+
role,
|
|
8099
|
+
content,
|
|
8100
|
+
kind: "standard",
|
|
8101
|
+
agent: "hermes-cli",
|
|
8102
|
+
historySessionId: normalizedSessionId
|
|
8103
|
+
});
|
|
8104
|
+
continue;
|
|
8105
|
+
}
|
|
8106
|
+
if (role === "tool") {
|
|
8107
|
+
records.push({
|
|
8108
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8109
|
+
receivedAt,
|
|
8110
|
+
role: "assistant",
|
|
8111
|
+
content,
|
|
8112
|
+
kind: "tool",
|
|
8113
|
+
senderName: "Tool",
|
|
8114
|
+
agent: "hermes-cli",
|
|
8115
|
+
historySessionId: normalizedSessionId
|
|
8116
|
+
});
|
|
8117
|
+
}
|
|
8118
|
+
}
|
|
8119
|
+
return records;
|
|
8120
|
+
} catch {
|
|
8121
|
+
return null;
|
|
8122
|
+
}
|
|
8123
|
+
}
|
|
7990
8124
|
function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
7991
8125
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
7992
8126
|
if (!normalizedSessionId) return false;
|
|
@@ -8136,6 +8270,77 @@ function extractClaudeUserContentParts(content) {
|
|
|
8136
8270
|
}
|
|
8137
8271
|
return parts;
|
|
8138
8272
|
}
|
|
8273
|
+
function buildClaudeNativeHistoryRecords(historySessionId, workspace) {
|
|
8274
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8275
|
+
if (!normalizedSessionId) return null;
|
|
8276
|
+
try {
|
|
8277
|
+
const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
|
|
8278
|
+
if (!transcriptPath) return null;
|
|
8279
|
+
const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
|
|
8280
|
+
const records = [];
|
|
8281
|
+
let fallbackTs = Date.now();
|
|
8282
|
+
for (const line of lines) {
|
|
8283
|
+
let parsed = null;
|
|
8284
|
+
try {
|
|
8285
|
+
parsed = JSON.parse(line);
|
|
8286
|
+
} catch {
|
|
8287
|
+
parsed = null;
|
|
8288
|
+
}
|
|
8289
|
+
if (!parsed) continue;
|
|
8290
|
+
const parsedSessionId = String(parsed.sessionId || "").trim();
|
|
8291
|
+
if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
|
|
8292
|
+
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
8293
|
+
fallbackTs = receivedAt + 1;
|
|
8294
|
+
const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
|
|
8295
|
+
if (records.length === 0 && parsedWorkspace) {
|
|
8296
|
+
records.push({
|
|
8297
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8298
|
+
receivedAt,
|
|
8299
|
+
role: "system",
|
|
8300
|
+
kind: "session_start",
|
|
8301
|
+
content: parsedWorkspace,
|
|
8302
|
+
agent: "claude-cli",
|
|
8303
|
+
historySessionId: normalizedSessionId,
|
|
8304
|
+
workspace: parsedWorkspace
|
|
8305
|
+
});
|
|
8306
|
+
}
|
|
8307
|
+
const type = String(parsed.type || "").trim();
|
|
8308
|
+
const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
|
|
8309
|
+
if (type === "user" && message) {
|
|
8310
|
+
for (const part of extractClaudeUserContentParts(message.content)) {
|
|
8311
|
+
records.push({
|
|
8312
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8313
|
+
receivedAt,
|
|
8314
|
+
role: part.role,
|
|
8315
|
+
content: part.content,
|
|
8316
|
+
kind: part.kind,
|
|
8317
|
+
senderName: part.senderName,
|
|
8318
|
+
agent: "claude-cli",
|
|
8319
|
+
historySessionId: normalizedSessionId
|
|
8320
|
+
});
|
|
8321
|
+
}
|
|
8322
|
+
continue;
|
|
8323
|
+
}
|
|
8324
|
+
if (type === "assistant" && message) {
|
|
8325
|
+
for (const part of extractClaudeAssistantContentParts(message.content)) {
|
|
8326
|
+
records.push({
|
|
8327
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8328
|
+
receivedAt,
|
|
8329
|
+
role: "assistant",
|
|
8330
|
+
content: part.content,
|
|
8331
|
+
kind: part.kind,
|
|
8332
|
+
senderName: part.senderName,
|
|
8333
|
+
agent: "claude-cli",
|
|
8334
|
+
historySessionId: normalizedSessionId
|
|
8335
|
+
});
|
|
8336
|
+
}
|
|
8337
|
+
}
|
|
8338
|
+
}
|
|
8339
|
+
return records;
|
|
8340
|
+
} catch {
|
|
8341
|
+
return null;
|
|
8342
|
+
}
|
|
8343
|
+
}
|
|
8139
8344
|
function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace) {
|
|
8140
8345
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8141
8346
|
if (!normalizedSessionId) return false;
|
|
@@ -8214,6 +8419,352 @@ function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace)
|
|
|
8214
8419
|
return false;
|
|
8215
8420
|
}
|
|
8216
8421
|
}
|
|
8422
|
+
function isUuidLikeSessionId(sessionId) {
|
|
8423
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
|
|
8424
|
+
}
|
|
8425
|
+
function readCodexSessionMeta(filePath) {
|
|
8426
|
+
try {
|
|
8427
|
+
const firstLine = fs3.readFileSync(filePath, "utf-8").split("\n").find(Boolean);
|
|
8428
|
+
if (!firstLine) return null;
|
|
8429
|
+
const parsed = JSON.parse(firstLine);
|
|
8430
|
+
if (String(parsed.type || "") !== "session_meta") return null;
|
|
8431
|
+
const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
|
|
8432
|
+
return payload;
|
|
8433
|
+
} catch {
|
|
8434
|
+
return null;
|
|
8435
|
+
}
|
|
8436
|
+
}
|
|
8437
|
+
function resolveCodexSessionTranscriptPath(historySessionId, workspace) {
|
|
8438
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8439
|
+
if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
|
|
8440
|
+
const sessionsDir = path7.join(os5.homedir(), ".codex", "sessions");
|
|
8441
|
+
if (!fs3.existsSync(sessionsDir)) return null;
|
|
8442
|
+
const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
|
|
8443
|
+
const candidates = [];
|
|
8444
|
+
const stack = [sessionsDir];
|
|
8445
|
+
while (stack.length > 0) {
|
|
8446
|
+
const current = stack.pop();
|
|
8447
|
+
if (!current) continue;
|
|
8448
|
+
let entries = [];
|
|
8449
|
+
try {
|
|
8450
|
+
entries = fs3.readdirSync(current, { withFileTypes: true });
|
|
8451
|
+
} catch {
|
|
8452
|
+
continue;
|
|
8453
|
+
}
|
|
8454
|
+
for (const entry of entries) {
|
|
8455
|
+
const entryPath = path7.join(current, entry.name);
|
|
8456
|
+
if (entry.isDirectory()) {
|
|
8457
|
+
stack.push(entryPath);
|
|
8458
|
+
continue;
|
|
8459
|
+
}
|
|
8460
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl") || !entry.name.includes(normalizedSessionId)) continue;
|
|
8461
|
+
const meta = readCodexSessionMeta(entryPath);
|
|
8462
|
+
const metaSessionId = String(meta?.id || "").trim();
|
|
8463
|
+
if (metaSessionId && metaSessionId !== normalizedSessionId) continue;
|
|
8464
|
+
const metaWorkspace = String(meta?.cwd || "").trim();
|
|
8465
|
+
let mtimeMs = 0;
|
|
8466
|
+
try {
|
|
8467
|
+
mtimeMs = fs3.statSync(entryPath).mtimeMs;
|
|
8468
|
+
} catch {
|
|
8469
|
+
}
|
|
8470
|
+
candidates.push({
|
|
8471
|
+
path: entryPath,
|
|
8472
|
+
mtimeMs,
|
|
8473
|
+
workspaceMatches: !!normalizedWorkspace && metaWorkspace === normalizedWorkspace,
|
|
8474
|
+
metaMatches: metaSessionId === normalizedSessionId
|
|
8475
|
+
});
|
|
8476
|
+
}
|
|
8477
|
+
}
|
|
8478
|
+
candidates.sort((a, b) => Number(b.workspaceMatches) - Number(a.workspaceMatches) || Number(b.metaMatches) - Number(a.metaMatches) || b.mtimeMs - a.mtimeMs);
|
|
8479
|
+
return candidates[0]?.path || null;
|
|
8480
|
+
}
|
|
8481
|
+
function flattenCodexContent(content) {
|
|
8482
|
+
if (typeof content === "string") return content.trim();
|
|
8483
|
+
if (content == null) return "";
|
|
8484
|
+
if (Array.isArray(content)) {
|
|
8485
|
+
return content.map((entry) => flattenCodexContent(entry)).filter(Boolean).join("\n").trim();
|
|
8486
|
+
}
|
|
8487
|
+
if (typeof content === "object") {
|
|
8488
|
+
const record = content;
|
|
8489
|
+
if (typeof record.text === "string") return record.text.trim();
|
|
8490
|
+
if (typeof record.content === "string" || Array.isArray(record.content)) return flattenCodexContent(record.content);
|
|
8491
|
+
if (typeof record.output === "string") return record.output.trim();
|
|
8492
|
+
if (typeof record.message === "string") return record.message.trim();
|
|
8493
|
+
}
|
|
8494
|
+
return "";
|
|
8495
|
+
}
|
|
8496
|
+
function summarizeCodexToolCall(payload) {
|
|
8497
|
+
const name = String(payload.name || payload.type || "tool").trim() || "tool";
|
|
8498
|
+
const rawArguments = payload.arguments ?? payload.input;
|
|
8499
|
+
let argumentValue = "";
|
|
8500
|
+
if (typeof rawArguments === "string") {
|
|
8501
|
+
const trimmed = rawArguments.trim();
|
|
8502
|
+
try {
|
|
8503
|
+
const parsed = JSON.parse(trimmed);
|
|
8504
|
+
argumentValue = summarizeCodexToolArguments(parsed);
|
|
8505
|
+
} catch {
|
|
8506
|
+
argumentValue = trimmed;
|
|
8507
|
+
}
|
|
8508
|
+
} else {
|
|
8509
|
+
argumentValue = summarizeCodexToolArguments(rawArguments);
|
|
8510
|
+
}
|
|
8511
|
+
return argumentValue ? `${name}: ${argumentValue}` : name;
|
|
8512
|
+
}
|
|
8513
|
+
function summarizeCodexToolArguments(value) {
|
|
8514
|
+
if (typeof value === "string") return value.trim();
|
|
8515
|
+
if (Array.isArray(value)) return value.map((entry) => String(entry)).join(" ").trim();
|
|
8516
|
+
if (!value || typeof value !== "object") return "";
|
|
8517
|
+
const record = value;
|
|
8518
|
+
const direct = record.command || record.cmd || record.query || record.path || record.prompt;
|
|
8519
|
+
if (typeof direct === "string") return direct.trim();
|
|
8520
|
+
if (Array.isArray(direct)) return direct.map((entry) => String(entry)).join(" ").trim();
|
|
8521
|
+
try {
|
|
8522
|
+
return JSON.stringify(record).trim();
|
|
8523
|
+
} catch {
|
|
8524
|
+
return "";
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
function codexToolOutputContent(payload) {
|
|
8528
|
+
const output = payload.output ?? payload.result ?? payload.content;
|
|
8529
|
+
const text = flattenCodexContent(output);
|
|
8530
|
+
if (text) return text;
|
|
8531
|
+
if (output && typeof output === "object") {
|
|
8532
|
+
try {
|
|
8533
|
+
return JSON.stringify(output).trim();
|
|
8534
|
+
} catch {
|
|
8535
|
+
return "";
|
|
8536
|
+
}
|
|
8537
|
+
}
|
|
8538
|
+
return "";
|
|
8539
|
+
}
|
|
8540
|
+
function buildCodexNativeHistoryRecords(historySessionId, workspace) {
|
|
8541
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8542
|
+
if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
|
|
8543
|
+
try {
|
|
8544
|
+
const transcriptPath = resolveCodexSessionTranscriptPath(normalizedSessionId, workspace);
|
|
8545
|
+
if (!transcriptPath) return null;
|
|
8546
|
+
const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
|
|
8547
|
+
const records = [];
|
|
8548
|
+
let fallbackTs = Date.now();
|
|
8549
|
+
for (const line of lines) {
|
|
8550
|
+
let parsed = null;
|
|
8551
|
+
try {
|
|
8552
|
+
parsed = JSON.parse(line);
|
|
8553
|
+
} catch {
|
|
8554
|
+
parsed = null;
|
|
8555
|
+
}
|
|
8556
|
+
if (!parsed) continue;
|
|
8557
|
+
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
8558
|
+
fallbackTs = receivedAt + 1;
|
|
8559
|
+
const type = String(parsed.type || "").trim();
|
|
8560
|
+
const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
|
|
8561
|
+
if (!payload) continue;
|
|
8562
|
+
if (type === "session_meta") {
|
|
8563
|
+
const parsedSessionId = String(payload.id || "").trim();
|
|
8564
|
+
if (parsedSessionId && parsedSessionId !== normalizedSessionId) return null;
|
|
8565
|
+
const parsedWorkspace = String(payload.cwd || workspace || "").trim();
|
|
8566
|
+
if (records.length === 0 && parsedWorkspace) {
|
|
8567
|
+
records.push({
|
|
8568
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8569
|
+
receivedAt,
|
|
8570
|
+
role: "system",
|
|
8571
|
+
kind: "session_start",
|
|
8572
|
+
content: parsedWorkspace,
|
|
8573
|
+
agent: "codex-cli",
|
|
8574
|
+
historySessionId: normalizedSessionId,
|
|
8575
|
+
workspace: parsedWorkspace
|
|
8576
|
+
});
|
|
8577
|
+
}
|
|
8578
|
+
continue;
|
|
8579
|
+
}
|
|
8580
|
+
if (type !== "response_item") continue;
|
|
8581
|
+
const payloadType = String(payload.type || "").trim();
|
|
8582
|
+
if (payloadType === "message") {
|
|
8583
|
+
const role = String(payload.role || "").trim();
|
|
8584
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
8585
|
+
const content = flattenCodexContent(payload.content);
|
|
8586
|
+
if (!content) continue;
|
|
8587
|
+
records.push({
|
|
8588
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8589
|
+
receivedAt,
|
|
8590
|
+
role,
|
|
8591
|
+
content,
|
|
8592
|
+
kind: "standard",
|
|
8593
|
+
agent: "codex-cli",
|
|
8594
|
+
historySessionId: normalizedSessionId
|
|
8595
|
+
});
|
|
8596
|
+
continue;
|
|
8597
|
+
}
|
|
8598
|
+
if (payloadType === "function_call" || payloadType === "custom_tool_call") {
|
|
8599
|
+
const content = summarizeCodexToolCall(payload);
|
|
8600
|
+
if (!content) continue;
|
|
8601
|
+
records.push({
|
|
8602
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8603
|
+
receivedAt,
|
|
8604
|
+
role: "assistant",
|
|
8605
|
+
content,
|
|
8606
|
+
kind: "tool",
|
|
8607
|
+
senderName: "Tool",
|
|
8608
|
+
agent: "codex-cli",
|
|
8609
|
+
historySessionId: normalizedSessionId
|
|
8610
|
+
});
|
|
8611
|
+
continue;
|
|
8612
|
+
}
|
|
8613
|
+
if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
|
|
8614
|
+
const content = codexToolOutputContent(payload);
|
|
8615
|
+
if (!content) continue;
|
|
8616
|
+
records.push({
|
|
8617
|
+
ts: new Date(receivedAt).toISOString(),
|
|
8618
|
+
receivedAt,
|
|
8619
|
+
role: "assistant",
|
|
8620
|
+
content,
|
|
8621
|
+
kind: "tool",
|
|
8622
|
+
senderName: "Tool",
|
|
8623
|
+
agent: "codex-cli",
|
|
8624
|
+
historySessionId: normalizedSessionId
|
|
8625
|
+
});
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
return records;
|
|
8629
|
+
} catch {
|
|
8630
|
+
return null;
|
|
8631
|
+
}
|
|
8632
|
+
}
|
|
8633
|
+
function rebuildCodexSavedHistoryFromNativeSession(historySessionId, workspace) {
|
|
8634
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
8635
|
+
if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return false;
|
|
8636
|
+
const records = buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
|
|
8637
|
+
if (!records || records.length === 0) return false;
|
|
8638
|
+
const existingSessionStart = readExistingSessionStartRecord("codex-cli", normalizedSessionId);
|
|
8639
|
+
const recordsToWrite = existingSessionStart && records[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId }, ...records] : records;
|
|
8640
|
+
return rewriteCanonicalSavedHistory("codex-cli", normalizedSessionId, recordsToWrite);
|
|
8641
|
+
}
|
|
8642
|
+
function isNativeSourceCanonicalHistory(canonicalHistory) {
|
|
8643
|
+
if (!canonicalHistory) return false;
|
|
8644
|
+
if (canonicalHistory.mode === "disabled") return false;
|
|
8645
|
+
if (canonicalHistory.mode === "materialized-mirror") return false;
|
|
8646
|
+
return true;
|
|
8647
|
+
}
|
|
8648
|
+
function buildNativeHistoryRecords(canonicalHistory, historySessionId, workspace) {
|
|
8649
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
|
|
8650
|
+
if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
|
|
8651
|
+
if (canonicalHistory.format === "hermes-json") return buildHermesNativeHistoryRecords(normalizedSessionId);
|
|
8652
|
+
if (canonicalHistory.format === "claude-jsonl") return buildClaudeNativeHistoryRecords(normalizedSessionId, workspace);
|
|
8653
|
+
if (canonicalHistory.format === "codex-jsonl") return buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
|
|
8654
|
+
return null;
|
|
8655
|
+
}
|
|
8656
|
+
function readProviderChatHistory(agentType, options = {}) {
|
|
8657
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
|
|
8658
|
+
const records = buildNativeHistoryRecords(options.canonicalHistory, options.historySessionId, options.workspace);
|
|
8659
|
+
if (!records) return { messages: [], hasMore: false, source: "native-unavailable" };
|
|
8660
|
+
return {
|
|
8661
|
+
...pageHistoryRecords(agentType, records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
|
|
8662
|
+
source: "provider-native"
|
|
8663
|
+
};
|
|
8664
|
+
}
|
|
8665
|
+
return {
|
|
8666
|
+
...readChatHistory(agentType, options.offset || 0, options.limit || 30, options.historySessionId, options.excludeRecentCount || 0, options.historyBehavior),
|
|
8667
|
+
source: "adhdev-mirror"
|
|
8668
|
+
};
|
|
8669
|
+
}
|
|
8670
|
+
function buildNativeSessionSummary(agentType, historySessionId, records, sourcePath) {
|
|
8671
|
+
const visible = pageHistoryRecords(agentType, records, 0, Number.MAX_SAFE_INTEGER).messages;
|
|
8672
|
+
if (visible.length === 0) return null;
|
|
8673
|
+
let sourceMtimeMs = 0;
|
|
8674
|
+
try {
|
|
8675
|
+
sourceMtimeMs = fs3.statSync(sourcePath).mtimeMs;
|
|
8676
|
+
} catch {
|
|
8677
|
+
}
|
|
8678
|
+
const firstMessageAt = visible[0]?.receivedAt || sourceMtimeMs || Date.now();
|
|
8679
|
+
const lastMessageAt = visible[visible.length - 1]?.receivedAt || firstMessageAt;
|
|
8680
|
+
const lastNonSystem = [...visible].reverse().find((message) => message.role !== "system") || visible[visible.length - 1];
|
|
8681
|
+
const firstSystem = visible.find((message) => message.kind === "session_start");
|
|
8682
|
+
return {
|
|
8683
|
+
historySessionId,
|
|
8684
|
+
sessionTitle: lastNonSystem?.content,
|
|
8685
|
+
messageCount: visible.length,
|
|
8686
|
+
firstMessageAt,
|
|
8687
|
+
lastMessageAt,
|
|
8688
|
+
preview: lastNonSystem?.content,
|
|
8689
|
+
workspace: firstSystem?.workspace || (firstSystem?.kind === "session_start" ? firstSystem.content : void 0),
|
|
8690
|
+
source: "provider-native",
|
|
8691
|
+
sourcePath,
|
|
8692
|
+
sourceMtimeMs
|
|
8693
|
+
};
|
|
8694
|
+
}
|
|
8695
|
+
function listFilesRecursive(root, predicate) {
|
|
8696
|
+
if (!fs3.existsSync(root)) return [];
|
|
8697
|
+
const results = [];
|
|
8698
|
+
const stack = [root];
|
|
8699
|
+
while (stack.length > 0) {
|
|
8700
|
+
const current = stack.pop();
|
|
8701
|
+
if (!current) continue;
|
|
8702
|
+
let entries = [];
|
|
8703
|
+
try {
|
|
8704
|
+
entries = fs3.readdirSync(current, { withFileTypes: true });
|
|
8705
|
+
} catch {
|
|
8706
|
+
continue;
|
|
8707
|
+
}
|
|
8708
|
+
for (const entry of entries) {
|
|
8709
|
+
const entryPath = path7.join(current, entry.name);
|
|
8710
|
+
if (entry.isDirectory()) {
|
|
8711
|
+
stack.push(entryPath);
|
|
8712
|
+
continue;
|
|
8713
|
+
}
|
|
8714
|
+
if (predicate(entryPath, entry)) results.push(entryPath);
|
|
8715
|
+
}
|
|
8716
|
+
}
|
|
8717
|
+
return results;
|
|
8718
|
+
}
|
|
8719
|
+
function collectNativeHistorySessionSummaries(agentType, canonicalHistory) {
|
|
8720
|
+
const summaries = [];
|
|
8721
|
+
if (canonicalHistory.format === "hermes-json") {
|
|
8722
|
+
const root = path7.join(os5.homedir(), ".hermes", "sessions");
|
|
8723
|
+
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && /^session_.+\.json$/.test(entry.name))) {
|
|
8724
|
+
const fileName = path7.basename(filePath);
|
|
8725
|
+
const historySessionId = fileName.replace(/^session_/, "").replace(/\.json$/, "");
|
|
8726
|
+
const records = buildHermesNativeHistoryRecords(historySessionId);
|
|
8727
|
+
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
8728
|
+
if (summary) summaries.push(summary);
|
|
8729
|
+
}
|
|
8730
|
+
} else if (canonicalHistory.format === "claude-jsonl") {
|
|
8731
|
+
const root = path7.join(os5.homedir(), ".claude", "projects");
|
|
8732
|
+
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
|
|
8733
|
+
const historySessionId = path7.basename(filePath, ".jsonl");
|
|
8734
|
+
const records = buildClaudeNativeHistoryRecords(historySessionId);
|
|
8735
|
+
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
8736
|
+
if (summary) summaries.push(summary);
|
|
8737
|
+
}
|
|
8738
|
+
} else if (canonicalHistory.format === "codex-jsonl") {
|
|
8739
|
+
const root = path7.join(os5.homedir(), ".codex", "sessions");
|
|
8740
|
+
const uuidPattern = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
|
|
8741
|
+
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
|
|
8742
|
+
const meta = readCodexSessionMeta(filePath);
|
|
8743
|
+
const historySessionId = String(meta?.id || path7.basename(filePath).match(uuidPattern)?.[1] || "").trim();
|
|
8744
|
+
if (!historySessionId) continue;
|
|
8745
|
+
const records = buildCodexNativeHistoryRecords(historySessionId, String(meta?.cwd || "").trim() || void 0);
|
|
8746
|
+
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
8747
|
+
if (summary) summaries.push(summary);
|
|
8748
|
+
}
|
|
8749
|
+
}
|
|
8750
|
+
return sortSavedHistorySessionSummaries(summaries);
|
|
8751
|
+
}
|
|
8752
|
+
function listProviderHistorySessions(agentType, options = {}) {
|
|
8753
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory)) {
|
|
8754
|
+
const offset = Math.max(0, options.offset || 0);
|
|
8755
|
+
const limit = Math.max(1, options.limit || 30);
|
|
8756
|
+
const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory);
|
|
8757
|
+
return {
|
|
8758
|
+
sessions: summaries.slice(offset, offset + limit),
|
|
8759
|
+
hasMore: offset + limit < summaries.length,
|
|
8760
|
+
source: "provider-native"
|
|
8761
|
+
};
|
|
8762
|
+
}
|
|
8763
|
+
return {
|
|
8764
|
+
...listSavedHistorySessions(agentType, { offset: options.offset, limit: options.limit }, options.historyBehavior),
|
|
8765
|
+
source: "adhdev-mirror"
|
|
8766
|
+
};
|
|
8767
|
+
}
|
|
8217
8768
|
|
|
8218
8769
|
// src/providers/provider-patch-state.ts
|
|
8219
8770
|
function isControlValue(value) {
|
|
@@ -10827,7 +11378,16 @@ async function handleChatHistory(h, args) {
|
|
|
10827
11378
|
const visibleCount = Array.isArray(status?.messages) ? status.messages.length : 0;
|
|
10828
11379
|
if (visibleCount > excludeRecentCount) excludeRecentCount = visibleCount;
|
|
10829
11380
|
}
|
|
10830
|
-
const
|
|
11381
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : typeof h.currentSession?.workspace === "string" ? h.currentSession.workspace : void 0;
|
|
11382
|
+
const result = readProviderChatHistory(agentStr, {
|
|
11383
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
11384
|
+
historySessionId,
|
|
11385
|
+
workspace,
|
|
11386
|
+
offset: offset || 0,
|
|
11387
|
+
limit: limit || 30,
|
|
11388
|
+
excludeRecentCount,
|
|
11389
|
+
historyBehavior: provider?.historyBehavior
|
|
11390
|
+
});
|
|
10831
11391
|
return { success: true, ...result, agent: agentStr };
|
|
10832
11392
|
} catch (e) {
|
|
10833
11393
|
return { success: false, error: e.message };
|
|
@@ -10852,7 +11412,8 @@ async function handleReadChat(h, args) {
|
|
|
10852
11412
|
}
|
|
10853
11413
|
const parsedRecord = parsedStatus && typeof parsedStatus === "object" ? parsedStatus : null;
|
|
10854
11414
|
const adapterStatus = adapter.getStatus();
|
|
10855
|
-
const
|
|
11415
|
+
const parsedIsProviderAuthoritative = parsedRecord?.transcriptAuthority === "provider" || parsedRecord?.coverage === "full";
|
|
11416
|
+
const shouldPreferAdapterMessages = !parsedIsProviderAuthoritative && Array.isArray(adapterStatus.messages) && adapterStatus.messages.length > 0 && Array.isArray(parsedRecord?.messages) && adapterStatus.messages.length > parsedRecord.messages.length;
|
|
10856
11417
|
const parsedShowsApproval = hasNonEmptyModalButtons(parsedRecord?.activeModal) && parsedRecord?.status === "waiting_approval";
|
|
10857
11418
|
const status = parsedRecord ? {
|
|
10858
11419
|
...parsedRecord,
|
|
@@ -10862,6 +11423,8 @@ async function handleReadChat(h, args) {
|
|
|
10862
11423
|
} : adapterStatus;
|
|
10863
11424
|
const title = typeof parsedRecord?.title === "string" ? parsedRecord.title : void 0;
|
|
10864
11425
|
const providerSessionId = typeof parsedRecord?.providerSessionId === "string" ? parsedRecord.providerSessionId : void 0;
|
|
11426
|
+
const transcriptAuthority = parsedRecord?.transcriptAuthority === "provider" || parsedRecord?.transcriptAuthority === "daemon" ? parsedRecord.transcriptAuthority : void 0;
|
|
11427
|
+
const coverage = parsedRecord?.coverage === "full" || parsedRecord?.coverage === "tail" || parsedRecord?.coverage === "current-turn" ? parsedRecord.coverage : void 0;
|
|
10865
11428
|
if (status) {
|
|
10866
11429
|
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}`);
|
|
10867
11430
|
return buildReadChatCommandResult({
|
|
@@ -10880,7 +11443,9 @@ async function handleReadChat(h, args) {
|
|
|
10880
11443
|
returnedMsgCount: Array.isArray(status.messages) ? status.messages.length : 0
|
|
10881
11444
|
},
|
|
10882
11445
|
...title ? { title } : {},
|
|
10883
|
-
...providerSessionId ? { providerSessionId } : {}
|
|
11446
|
+
...providerSessionId ? { providerSessionId } : {},
|
|
11447
|
+
...transcriptAuthority ? { transcriptAuthority } : {},
|
|
11448
|
+
...coverage ? { coverage } : {}
|
|
10884
11449
|
}, args);
|
|
10885
11450
|
}
|
|
10886
11451
|
}
|
|
@@ -13300,6 +13865,8 @@ var CliProviderInstance = class {
|
|
|
13300
13865
|
lastCanonicalHermesWatchPath = void 0;
|
|
13301
13866
|
lastCanonicalClaudeRebuildMtimeMs = 0;
|
|
13302
13867
|
lastCanonicalClaudeCheckAt = 0;
|
|
13868
|
+
lastCanonicalCodexRebuildMtimeMs = 0;
|
|
13869
|
+
lastCanonicalCodexCheckAt = 0;
|
|
13303
13870
|
cachedSqliteDb = null;
|
|
13304
13871
|
cachedSqliteDbPath = null;
|
|
13305
13872
|
cachedSqliteDbMissingUntil = 0;
|
|
@@ -13999,6 +14566,25 @@ ${effect.notification.body || ""}`.trim();
|
|
|
13999
14566
|
if (!this.providerSessionId) return false;
|
|
14000
14567
|
const canonicalHistory = this.provider.canonicalHistory;
|
|
14001
14568
|
if (!canonicalHistory) return false;
|
|
14569
|
+
if (isNativeSourceCanonicalHistory(canonicalHistory)) {
|
|
14570
|
+
const restoredHistory = readProviderChatHistory(this.type, {
|
|
14571
|
+
canonicalHistory,
|
|
14572
|
+
historySessionId: this.providerSessionId,
|
|
14573
|
+
workspace: this.workingDir,
|
|
14574
|
+
offset: 0,
|
|
14575
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
14576
|
+
historyBehavior: this.provider.historyBehavior
|
|
14577
|
+
});
|
|
14578
|
+
if (restoredHistory.source !== "provider-native") return false;
|
|
14579
|
+
this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
|
|
14580
|
+
role: message.role,
|
|
14581
|
+
content: message.content,
|
|
14582
|
+
kind: message.kind,
|
|
14583
|
+
senderName: message.senderName,
|
|
14584
|
+
receivedAt: message.receivedAt
|
|
14585
|
+
}));
|
|
14586
|
+
return true;
|
|
14587
|
+
}
|
|
14002
14588
|
try {
|
|
14003
14589
|
let rebuilt = false;
|
|
14004
14590
|
if (canonicalHistory.format === "hermes-json") {
|
|
@@ -14032,6 +14618,23 @@ ${effect.notification.body || ""}`.trim();
|
|
|
14032
14618
|
if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalClaudeRebuildMtimeMs) return true;
|
|
14033
14619
|
rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
|
|
14034
14620
|
if (rebuilt) this.lastCanonicalClaudeRebuildMtimeMs = transcriptMtime || Date.now();
|
|
14621
|
+
} else if (canonicalHistory.format === "codex-jsonl") {
|
|
14622
|
+
const now = Date.now();
|
|
14623
|
+
if (now - this.lastCanonicalCodexCheckAt < 2e3 && this.lastCanonicalCodexRebuildMtimeMs !== 0) {
|
|
14624
|
+
return true;
|
|
14625
|
+
}
|
|
14626
|
+
this.lastCanonicalCodexCheckAt = now;
|
|
14627
|
+
const transcriptFile = resolveCodexSessionTranscriptPath(this.providerSessionId, this.workingDir);
|
|
14628
|
+
let transcriptMtime = 0;
|
|
14629
|
+
if (transcriptFile) {
|
|
14630
|
+
try {
|
|
14631
|
+
transcriptMtime = fs5.statSync(transcriptFile).mtimeMs;
|
|
14632
|
+
} catch {
|
|
14633
|
+
}
|
|
14634
|
+
}
|
|
14635
|
+
if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalCodexRebuildMtimeMs) return true;
|
|
14636
|
+
rebuilt = rebuildCodexSavedHistoryFromNativeSession(this.providerSessionId, this.workingDir);
|
|
14637
|
+
if (rebuilt) this.lastCanonicalCodexRebuildMtimeMs = transcriptMtime || Date.now();
|
|
14035
14638
|
}
|
|
14036
14639
|
if (!rebuilt) return false;
|
|
14037
14640
|
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
|
|
@@ -14050,8 +14653,17 @@ ${effect.notification.body || ""}`.trim();
|
|
|
14050
14653
|
restorePersistedHistoryFromCurrentSession() {
|
|
14051
14654
|
if (!this.providerSessionId) return;
|
|
14052
14655
|
this.syncCanonicalSavedHistoryIfNeeded();
|
|
14053
|
-
this.
|
|
14054
|
-
|
|
14656
|
+
const restoredHistory = isNativeSourceCanonicalHistory(this.provider.canonicalHistory) ? readProviderChatHistory(this.type, {
|
|
14657
|
+
canonicalHistory: this.provider.canonicalHistory,
|
|
14658
|
+
historySessionId: this.providerSessionId,
|
|
14659
|
+
workspace: this.workingDir,
|
|
14660
|
+
offset: 0,
|
|
14661
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
14662
|
+
historyBehavior: this.provider.historyBehavior
|
|
14663
|
+
}) : (() => {
|
|
14664
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId, this.provider.historyBehavior);
|
|
14665
|
+
return readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
|
|
14666
|
+
})();
|
|
14055
14667
|
this.historyWriter.seedSessionHistory(
|
|
14056
14668
|
this.type,
|
|
14057
14669
|
restoredHistory.messages,
|
|
@@ -17786,6 +18398,38 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17786
18398
|
}
|
|
17787
18399
|
};
|
|
17788
18400
|
|
|
18401
|
+
// src/launch/macos-app-process.ts
|
|
18402
|
+
function normalizeMacAppPath(appPath) {
|
|
18403
|
+
const trimmed = String(appPath || "").trim();
|
|
18404
|
+
if (!trimmed) return null;
|
|
18405
|
+
return trimmed.replace(/\/+$/, "");
|
|
18406
|
+
}
|
|
18407
|
+
function parsePsLine(line) {
|
|
18408
|
+
const match = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
18409
|
+
if (!match) return null;
|
|
18410
|
+
const pid = Number.parseInt(match[1], 10);
|
|
18411
|
+
if (!Number.isFinite(pid)) return null;
|
|
18412
|
+
return { pid, args: match[2] };
|
|
18413
|
+
}
|
|
18414
|
+
function isMacAppProcessArgs(args, appPath) {
|
|
18415
|
+
const normalized = normalizeMacAppPath(appPath);
|
|
18416
|
+
if (!normalized) return false;
|
|
18417
|
+
return String(args || "").startsWith(`${normalized}/`);
|
|
18418
|
+
}
|
|
18419
|
+
function findMacAppProcessPids(psOutput, appPaths) {
|
|
18420
|
+
const normalizedPaths = appPaths.map(normalizeMacAppPath).filter((value) => !!value);
|
|
18421
|
+
if (normalizedPaths.length === 0) return [];
|
|
18422
|
+
const pids = [];
|
|
18423
|
+
for (const line of String(psOutput || "").split(/\r?\n/)) {
|
|
18424
|
+
const parsed = parsePsLine(line);
|
|
18425
|
+
if (!parsed) continue;
|
|
18426
|
+
if (normalizedPaths.some((appPath) => isMacAppProcessArgs(parsed.args, appPath))) {
|
|
18427
|
+
pids.push(parsed.pid);
|
|
18428
|
+
}
|
|
18429
|
+
}
|
|
18430
|
+
return pids;
|
|
18431
|
+
}
|
|
18432
|
+
|
|
17789
18433
|
// src/launch.ts
|
|
17790
18434
|
var _providerLoader = null;
|
|
17791
18435
|
function getProviderLoader() {
|
|
@@ -17822,6 +18466,35 @@ function getCdpStartupTimeoutMs(ideId) {
|
|
|
17822
18466
|
function escapeForAppleScript(value) {
|
|
17823
18467
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
17824
18468
|
}
|
|
18469
|
+
function getIdePathCandidates(ideId) {
|
|
18470
|
+
return getProviderLoader().getIdePathCandidates(ideId);
|
|
18471
|
+
}
|
|
18472
|
+
function getMacAppProcessPids(ideId) {
|
|
18473
|
+
const appPaths = getIdePathCandidates(ideId);
|
|
18474
|
+
if (appPaths.length === 0) return [];
|
|
18475
|
+
try {
|
|
18476
|
+
const output = execSync4("ps axww -o pid=,args=", {
|
|
18477
|
+
encoding: "utf-8",
|
|
18478
|
+
timeout: 3e3,
|
|
18479
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
18480
|
+
});
|
|
18481
|
+
return findMacAppProcessPids(output, appPaths);
|
|
18482
|
+
} catch {
|
|
18483
|
+
return [];
|
|
18484
|
+
}
|
|
18485
|
+
}
|
|
18486
|
+
function killMacAppPathProcesses(ideId, signal) {
|
|
18487
|
+
const pids = getMacAppProcessPids(ideId);
|
|
18488
|
+
let signalled = false;
|
|
18489
|
+
for (const pid of pids) {
|
|
18490
|
+
try {
|
|
18491
|
+
process.kill(pid, signal);
|
|
18492
|
+
signalled = true;
|
|
18493
|
+
} catch {
|
|
18494
|
+
}
|
|
18495
|
+
}
|
|
18496
|
+
return signalled;
|
|
18497
|
+
}
|
|
17825
18498
|
async function findFreePort(ports) {
|
|
17826
18499
|
for (const port2 of ports) {
|
|
17827
18500
|
const free = await checkPortFree(port2);
|
|
@@ -17883,6 +18556,7 @@ async function killIdeProcess(ideId) {
|
|
|
17883
18556
|
} catch {
|
|
17884
18557
|
}
|
|
17885
18558
|
}
|
|
18559
|
+
killMacAppPathProcesses(ideId, "SIGTERM");
|
|
17886
18560
|
} else if (plat === "win32" && winProcesses) {
|
|
17887
18561
|
for (const proc of winProcesses) {
|
|
17888
18562
|
try {
|
|
@@ -17912,6 +18586,7 @@ async function killIdeProcess(ideId) {
|
|
|
17912
18586
|
execSync4(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
17913
18587
|
} catch {
|
|
17914
18588
|
}
|
|
18589
|
+
killMacAppPathProcesses(ideId, "SIGKILL");
|
|
17915
18590
|
} else if (plat === "win32" && winProcesses) {
|
|
17916
18591
|
for (const proc of winProcesses) {
|
|
17917
18592
|
try {
|
|
@@ -17931,14 +18606,16 @@ function isIdeRunning(ideId) {
|
|
|
17931
18606
|
try {
|
|
17932
18607
|
if (plat === "darwin") {
|
|
17933
18608
|
const appName = getMacAppIdentifiers()[ideId];
|
|
17934
|
-
if (!appName) return
|
|
18609
|
+
if (!appName) return getMacAppProcessPids(ideId).length > 0;
|
|
17935
18610
|
try {
|
|
17936
18611
|
const result = execSync4(`pgrep -x "${appName}" 2>/dev/null`, {
|
|
17937
18612
|
encoding: "utf-8",
|
|
17938
18613
|
timeout: 3e3
|
|
17939
18614
|
});
|
|
17940
|
-
|
|
18615
|
+
if (result.trim().length > 0) return true;
|
|
17941
18616
|
} catch {
|
|
18617
|
+
}
|
|
18618
|
+
try {
|
|
17942
18619
|
const result = execSync4(
|
|
17943
18620
|
`osascript -e 'tell application "System Events" to count (every process whose name is "${escapeForAppleScript(appName)}")'`,
|
|
17944
18621
|
{
|
|
@@ -17947,8 +18624,10 @@ function isIdeRunning(ideId) {
|
|
|
17947
18624
|
stdio: ["pipe", "pipe", "pipe"]
|
|
17948
18625
|
}
|
|
17949
18626
|
);
|
|
17950
|
-
|
|
18627
|
+
if (Number.parseInt(result.trim() || "0", 10) > 0) return true;
|
|
18628
|
+
} catch {
|
|
17951
18629
|
}
|
|
18630
|
+
return getMacAppProcessPids(ideId).length > 0;
|
|
17952
18631
|
} else if (plat === "win32") {
|
|
17953
18632
|
const winProcesses = getWinProcessNames()[ideId];
|
|
17954
18633
|
if (!winProcesses) return false;
|
|
@@ -19315,13 +19994,18 @@ var DaemonCommandRouter = class {
|
|
|
19315
19994
|
const wantsAll = args?.all === true;
|
|
19316
19995
|
const offset = wantsAll ? 0 : Math.max(0, Number(args?.offset) || 0);
|
|
19317
19996
|
const limit = wantsAll ? Number.MAX_SAFE_INTEGER : Math.max(1, Math.min(100, Number(args?.limit) || 30));
|
|
19318
|
-
const
|
|
19997
|
+
const providerMeta = this.deps.providerLoader.getMeta(providerType);
|
|
19998
|
+
const { sessions: historySessions, hasMore, source } = listProviderHistorySessions(providerType, {
|
|
19999
|
+
canonicalHistory: providerMeta?.canonicalHistory,
|
|
20000
|
+
offset,
|
|
20001
|
+
limit,
|
|
20002
|
+
historyBehavior: providerMeta?.historyBehavior
|
|
20003
|
+
});
|
|
19319
20004
|
const state = loadState();
|
|
19320
20005
|
const savedSessions = getSavedProviderSessions(state, { providerType, kind });
|
|
19321
20006
|
const recentSessions = getRecentActivity(state, 200).filter((entry) => entry.providerType === providerType && entry.kind === kind && entry.providerSessionId);
|
|
19322
20007
|
const savedSessionById = new Map(savedSessions.map((entry) => [entry.providerSessionId, entry]));
|
|
19323
20008
|
const recentSessionById = new Map(recentSessions.map((entry) => [entry.providerSessionId, entry]));
|
|
19324
|
-
const providerMeta = this.deps.providerLoader.getMeta(providerType);
|
|
19325
20009
|
const canResumeById = supportsExplicitSessionResume(providerMeta?.resume);
|
|
19326
20010
|
return {
|
|
19327
20011
|
success: true,
|
|
@@ -19344,7 +20028,8 @@ var DaemonCommandRouter = class {
|
|
|
19344
20028
|
canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById
|
|
19345
20029
|
};
|
|
19346
20030
|
}),
|
|
19347
|
-
hasMore
|
|
20031
|
+
hasMore,
|
|
20032
|
+
source
|
|
19348
20033
|
};
|
|
19349
20034
|
}
|
|
19350
20035
|
// ─── restart_session: IDE / CLI / ACP unified ───
|