@adhdev/daemon-core 0.8.28 → 0.8.29
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/agent-stream/manager.d.ts +1 -1
- package/dist/agent-stream/provider-adapter.d.ts +5 -0
- package/dist/commands/router.d.ts +5 -0
- package/dist/config/chat-history.d.ts +12 -0
- package/dist/index.js +385 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +385 -23
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/node_modules/@adhdev/session-host-core/dist/index.d.mts +24 -1
- package/node_modules/@adhdev/session-host-core/dist/index.d.ts +24 -1
- package/node_modules/@adhdev/session-host-core/dist/index.js +6 -1
- package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
- package/node_modules/@adhdev/session-host-core/dist/index.mjs +6 -1
- package/node_modules/@adhdev/session-host-core/dist/index.mjs.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/agent-stream/manager.ts +2 -2
- package/src/agent-stream/provider-adapter.ts +97 -3
- package/src/cli-adapters/provider-cli-adapter.ts +3 -0
- package/src/commands/chat-commands.ts +53 -3
- package/src/commands/cli-manager.ts +14 -0
- package/src/commands/router.ts +11 -0
- package/src/config/chat-history.ts +269 -18
- package/src/providers/cli-provider-instance.ts +17 -2
package/dist/index.mjs
CHANGED
|
@@ -1640,6 +1640,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1640
1640
|
looksLikeVisibleIdlePrompt(screenText) {
|
|
1641
1641
|
const text = String(screenText || "");
|
|
1642
1642
|
if (!text.trim()) return false;
|
|
1643
|
+
if (this.cliType === "codex-cli" && /(^|\n)\s*[❯›>]\s+(?:Find and fix a bug in @filename|Improve documentation in @filename|Use \/skills|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Run \/review on my current changes)(?:\n|$)/im.test(text)) {
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1643
1646
|
return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text) || /⏎\s+send/i.test(text) || /\?\s*for\s*shortcuts/i.test(text) || /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text) || /workspace\s*\(\/directory\)/i.test(text) || /for\s*shortcuts/i.test(text);
|
|
1644
1647
|
}
|
|
1645
1648
|
findLastMatchingLineIndex(lines, predicate) {
|
|
@@ -4884,11 +4887,72 @@ import * as path7 from "path";
|
|
|
4884
4887
|
import * as os5 from "os";
|
|
4885
4888
|
var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
|
|
4886
4889
|
var RETAIN_DAYS = 30;
|
|
4890
|
+
var CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
|
|
4891
|
+
function normalizeHistoryComparable(text) {
|
|
4892
|
+
return String(text || "").replace(/\s+/g, " ").trim();
|
|
4893
|
+
}
|
|
4894
|
+
function cleanupHistoryContent(agentType, role, content) {
|
|
4895
|
+
let value = String(content || "").replace(/\r\n/g, "\n").trim();
|
|
4896
|
+
if (!value) return "";
|
|
4897
|
+
if (agentType === "codex-cli" && role === "assistant") {
|
|
4898
|
+
const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4899
|
+
value = filtered;
|
|
4900
|
+
}
|
|
4901
|
+
return value;
|
|
4902
|
+
}
|
|
4903
|
+
function buildHistoryMessageHash(agentType, message) {
|
|
4904
|
+
if (message.historyDedupKey) return message.historyDedupKey;
|
|
4905
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4906
|
+
return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
|
|
4907
|
+
}
|
|
4908
|
+
function buildHistoryMessageSignature(agentType, message) {
|
|
4909
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4910
|
+
return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
|
|
4911
|
+
}
|
|
4912
|
+
function isAdjacentHistoryDuplicate(agentType, previous, next) {
|
|
4913
|
+
if (!previous || !next) return false;
|
|
4914
|
+
return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
|
|
4915
|
+
}
|
|
4916
|
+
function collapseReplayAssistantTurns(agentType, messages) {
|
|
4917
|
+
if (agentType !== "codex-cli") return messages;
|
|
4918
|
+
const collapsed = [];
|
|
4919
|
+
let sawAssistantSinceLastUser = false;
|
|
4920
|
+
for (const message of messages) {
|
|
4921
|
+
if (message.role === "user") {
|
|
4922
|
+
sawAssistantSinceLastUser = false;
|
|
4923
|
+
collapsed.push(message);
|
|
4924
|
+
continue;
|
|
4925
|
+
}
|
|
4926
|
+
if (message.role === "assistant") {
|
|
4927
|
+
if (sawAssistantSinceLastUser) continue;
|
|
4928
|
+
sawAssistantSinceLastUser = true;
|
|
4929
|
+
collapsed.push(message);
|
|
4930
|
+
continue;
|
|
4931
|
+
}
|
|
4932
|
+
collapsed.push(message);
|
|
4933
|
+
}
|
|
4934
|
+
return collapsed;
|
|
4935
|
+
}
|
|
4936
|
+
function sanitizeHistoryMessage(agentType, message) {
|
|
4937
|
+
if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
|
|
4938
|
+
return null;
|
|
4939
|
+
}
|
|
4940
|
+
const content = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4941
|
+
if (!content) return null;
|
|
4942
|
+
return {
|
|
4943
|
+
...message,
|
|
4944
|
+
content
|
|
4945
|
+
};
|
|
4946
|
+
}
|
|
4887
4947
|
var ChatHistoryWriter = class {
|
|
4888
4948
|
/** Last seen message count per agent (deduplication) */
|
|
4889
4949
|
lastSeenCounts = /* @__PURE__ */ new Map();
|
|
4890
4950
|
/** Last seen message hash per agent (deduplication) */
|
|
4891
4951
|
lastSeenHashes = /* @__PURE__ */ new Map();
|
|
4952
|
+
/** Last appended normalized message signature per agent/session */
|
|
4953
|
+
lastSeenSignatures = /* @__PURE__ */ new Map();
|
|
4954
|
+
/** Last appended normalized non-system turn signature per agent/session */
|
|
4955
|
+
lastSeenTurnSignatures = /* @__PURE__ */ new Map();
|
|
4892
4956
|
rotated = false;
|
|
4893
4957
|
/**
|
|
4894
4958
|
* Append new messages to history
|
|
@@ -4910,14 +4974,36 @@ var ChatHistoryWriter = class {
|
|
|
4910
4974
|
}
|
|
4911
4975
|
const newMessages = [];
|
|
4912
4976
|
for (const msg of messages) {
|
|
4913
|
-
const
|
|
4977
|
+
const role = msg.role;
|
|
4978
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
4979
|
+
const content = cleanupHistoryContent(agentType, role, msg.content || "");
|
|
4980
|
+
if (!content) continue;
|
|
4981
|
+
const receivedAt = msg.receivedAt || Date.now();
|
|
4982
|
+
const hash = buildHistoryMessageHash(agentType, {
|
|
4983
|
+
role,
|
|
4984
|
+
content,
|
|
4985
|
+
receivedAt,
|
|
4986
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
4987
|
+
historyDedupKey: msg.historyDedupKey
|
|
4988
|
+
});
|
|
4989
|
+
const signature = buildHistoryMessageSignature(agentType, {
|
|
4990
|
+
role,
|
|
4991
|
+
content,
|
|
4992
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0
|
|
4993
|
+
});
|
|
4914
4994
|
if (seenHashes.has(hash)) continue;
|
|
4995
|
+
if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
|
|
4996
|
+
if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
|
|
4915
4997
|
seenHashes.add(hash);
|
|
4998
|
+
this.lastSeenSignatures.set(dedupKey, signature);
|
|
4999
|
+
if (role !== "system") {
|
|
5000
|
+
this.lastSeenTurnSignatures.set(dedupKey, signature);
|
|
5001
|
+
}
|
|
4916
5002
|
newMessages.push({
|
|
4917
|
-
ts: new Date(
|
|
4918
|
-
receivedAt
|
|
4919
|
-
role
|
|
4920
|
-
content
|
|
5003
|
+
ts: new Date(receivedAt).toISOString(),
|
|
5004
|
+
receivedAt,
|
|
5005
|
+
role,
|
|
5006
|
+
content,
|
|
4921
5007
|
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
4922
5008
|
senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
|
|
4923
5009
|
agent: agentType,
|
|
@@ -4937,6 +5023,8 @@ var ChatHistoryWriter = class {
|
|
|
4937
5023
|
const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
|
|
4938
5024
|
if (messages.length < prevCount * 0.5 && prevCount > 3) {
|
|
4939
5025
|
seenHashes.clear();
|
|
5026
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5027
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
4940
5028
|
for (const msg of messages) {
|
|
4941
5029
|
seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
|
|
4942
5030
|
}
|
|
@@ -4950,6 +5038,54 @@ var ChatHistoryWriter = class {
|
|
|
4950
5038
|
} catch {
|
|
4951
5039
|
}
|
|
4952
5040
|
}
|
|
5041
|
+
seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
|
|
5042
|
+
const effectiveHistoryKey = historySessionId || instanceId;
|
|
5043
|
+
const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
|
|
5044
|
+
const seenHashes = /* @__PURE__ */ new Set();
|
|
5045
|
+
for (const raw of messages) {
|
|
5046
|
+
const role = raw?.role;
|
|
5047
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
5048
|
+
const content = cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5049
|
+
if (!content) continue;
|
|
5050
|
+
seenHashes.add(buildHistoryMessageHash(agentType, {
|
|
5051
|
+
role,
|
|
5052
|
+
content,
|
|
5053
|
+
receivedAt: raw?.receivedAt || 0,
|
|
5054
|
+
kind: typeof raw?.kind === "string" ? raw.kind : void 0,
|
|
5055
|
+
historyDedupKey: raw?.historyDedupKey
|
|
5056
|
+
}));
|
|
5057
|
+
}
|
|
5058
|
+
this.lastSeenHashes.set(dedupKey, seenHashes);
|
|
5059
|
+
this.lastSeenCounts.set(dedupKey, messages.length);
|
|
5060
|
+
const lastMessage = [...messages].reverse().find((raw) => {
|
|
5061
|
+
const role = raw?.role;
|
|
5062
|
+
if (role !== "user" && role !== "assistant" && role !== "system") return false;
|
|
5063
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5064
|
+
});
|
|
5065
|
+
const lastTurnMessage = [...messages].reverse().find((raw) => {
|
|
5066
|
+
const role = raw?.role;
|
|
5067
|
+
if (role !== "user" && role !== "assistant") return false;
|
|
5068
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5069
|
+
});
|
|
5070
|
+
if (lastMessage) {
|
|
5071
|
+
this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5072
|
+
role: lastMessage.role,
|
|
5073
|
+
content: lastMessage.content,
|
|
5074
|
+
kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
|
|
5075
|
+
}));
|
|
5076
|
+
} else {
|
|
5077
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5078
|
+
}
|
|
5079
|
+
if (lastTurnMessage) {
|
|
5080
|
+
this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5081
|
+
role: lastTurnMessage.role,
|
|
5082
|
+
content: lastTurnMessage.content,
|
|
5083
|
+
kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
|
|
5084
|
+
}));
|
|
5085
|
+
} else {
|
|
5086
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
4953
5089
|
appendSystemMarker(agentType, content, options = {}) {
|
|
4954
5090
|
this.appendNewMessages(
|
|
4955
5091
|
agentType,
|
|
@@ -4980,6 +5116,16 @@ var ChatHistoryWriter = class {
|
|
|
4980
5116
|
this.lastSeenHashes.set(toDedupKey, nextHashes);
|
|
4981
5117
|
this.lastSeenHashes.delete(fromDedupKey);
|
|
4982
5118
|
}
|
|
5119
|
+
const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
|
|
5120
|
+
if (fromSignature) {
|
|
5121
|
+
this.lastSeenSignatures.set(toDedupKey, fromSignature);
|
|
5122
|
+
this.lastSeenSignatures.delete(fromDedupKey);
|
|
5123
|
+
}
|
|
5124
|
+
const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
|
|
5125
|
+
if (fromTurnSignature) {
|
|
5126
|
+
this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
|
|
5127
|
+
this.lastSeenTurnSignatures.delete(fromDedupKey);
|
|
5128
|
+
}
|
|
4983
5129
|
const fromCount = this.lastSeenCounts.get(fromDedupKey);
|
|
4984
5130
|
if (typeof fromCount === "number") {
|
|
4985
5131
|
this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
|
|
@@ -5021,10 +5167,61 @@ var ChatHistoryWriter = class {
|
|
|
5021
5167
|
} catch {
|
|
5022
5168
|
}
|
|
5023
5169
|
}
|
|
5170
|
+
compactHistorySession(agentType, historySessionId) {
|
|
5171
|
+
const sessionId = String(historySessionId || "").trim();
|
|
5172
|
+
if (!sessionId) return;
|
|
5173
|
+
try {
|
|
5174
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
5175
|
+
if (!fs3.existsSync(dir)) return;
|
|
5176
|
+
const prefix = `${this.sanitize(sessionId)}_`;
|
|
5177
|
+
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(prefix) && file.endsWith(".jsonl")).sort();
|
|
5178
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5179
|
+
for (const file of files) {
|
|
5180
|
+
const filePath = path7.join(dir, file);
|
|
5181
|
+
const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
5182
|
+
const next = [];
|
|
5183
|
+
for (const line of lines) {
|
|
5184
|
+
let parsed = null;
|
|
5185
|
+
try {
|
|
5186
|
+
parsed = JSON.parse(line);
|
|
5187
|
+
} catch {
|
|
5188
|
+
parsed = null;
|
|
5189
|
+
}
|
|
5190
|
+
if (!parsed || parsed.historySessionId !== sessionId) continue;
|
|
5191
|
+
const sanitized = sanitizeHistoryMessage(agentType, parsed);
|
|
5192
|
+
if (!sanitized) continue;
|
|
5193
|
+
const hash = buildHistoryMessageHash(agentType, sanitized);
|
|
5194
|
+
if (seen.has(hash)) continue;
|
|
5195
|
+
seen.add(hash);
|
|
5196
|
+
next.push(sanitized);
|
|
5197
|
+
}
|
|
5198
|
+
next.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5199
|
+
const dedupedAdjacent = [];
|
|
5200
|
+
let lastTurn = null;
|
|
5201
|
+
for (const entry of next) {
|
|
5202
|
+
const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
|
|
5203
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
|
|
5204
|
+
if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
|
|
5205
|
+
dedupedAdjacent.push(entry);
|
|
5206
|
+
if (entry.role !== "system") lastTurn = entry;
|
|
5207
|
+
}
|
|
5208
|
+
const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
|
|
5209
|
+
if (collapsed.length === 0) {
|
|
5210
|
+
fs3.unlinkSync(filePath);
|
|
5211
|
+
continue;
|
|
5212
|
+
}
|
|
5213
|
+
fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
5214
|
+
`, "utf-8");
|
|
5215
|
+
}
|
|
5216
|
+
} catch {
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5024
5219
|
/** Called when agent session is explicitly changed */
|
|
5025
5220
|
onSessionChange(agentType) {
|
|
5026
5221
|
this.lastSeenHashes.delete(agentType);
|
|
5027
5222
|
this.lastSeenCounts.delete(agentType);
|
|
5223
|
+
this.lastSeenSignatures.delete(agentType);
|
|
5224
|
+
this.lastSeenTurnSignatures.delete(agentType);
|
|
5028
5225
|
}
|
|
5029
5226
|
/** Delete history files older than 30 days */
|
|
5030
5227
|
async rotateOldFiles() {
|
|
@@ -5065,23 +5262,37 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
|
5065
5262
|
return true;
|
|
5066
5263
|
}).sort().reverse();
|
|
5067
5264
|
const allMessages = [];
|
|
5068
|
-
const
|
|
5265
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5069
5266
|
for (const file of files) {
|
|
5070
|
-
if (allMessages.length >= needed) break;
|
|
5071
5267
|
const filePath = path7.join(dir, file);
|
|
5072
5268
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5073
5269
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
5074
|
-
for (let i =
|
|
5075
|
-
if (allMessages.length >= needed) break;
|
|
5270
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5076
5271
|
try {
|
|
5077
|
-
|
|
5272
|
+
const parsed = JSON.parse(lines[i]);
|
|
5273
|
+
const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
|
|
5274
|
+
if (!sanitizedMessage) continue;
|
|
5275
|
+
const hash = buildHistoryMessageHash(agentType, sanitizedMessage);
|
|
5276
|
+
if (seen.has(hash)) continue;
|
|
5277
|
+
seen.add(hash);
|
|
5278
|
+
allMessages.push(sanitizedMessage);
|
|
5078
5279
|
} catch {
|
|
5079
5280
|
}
|
|
5080
5281
|
}
|
|
5081
5282
|
}
|
|
5082
|
-
|
|
5083
|
-
const
|
|
5084
|
-
|
|
5283
|
+
allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5284
|
+
const chronological = [];
|
|
5285
|
+
let lastTurn = null;
|
|
5286
|
+
for (const message of allMessages) {
|
|
5287
|
+
const previous = chronological[chronological.length - 1];
|
|
5288
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
5289
|
+
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
5290
|
+
chronological.push(message);
|
|
5291
|
+
if (message.role !== "system") lastTurn = message;
|
|
5292
|
+
}
|
|
5293
|
+
const collapsed = collapseReplayAssistantTurns(agentType, chronological);
|
|
5294
|
+
const sliced = collapsed.slice(offset, offset + limit);
|
|
5295
|
+
const hasMore = collapsed.length > offset + limit;
|
|
5085
5296
|
return { messages: sliced, hasMore };
|
|
5086
5297
|
} catch {
|
|
5087
5298
|
return { messages: [], hasMore: false };
|
|
@@ -6935,6 +7146,46 @@ function didProviderConfirmSend(result) {
|
|
|
6935
7146
|
if (!parsed || typeof parsed !== "object") return false;
|
|
6936
7147
|
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
6937
7148
|
}
|
|
7149
|
+
async function readExtensionChatState(h) {
|
|
7150
|
+
try {
|
|
7151
|
+
const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
|
|
7152
|
+
if (!evalResult?.result) return null;
|
|
7153
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
7154
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
7155
|
+
} catch {
|
|
7156
|
+
return null;
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
function getStateMessageCount(state) {
|
|
7160
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
7161
|
+
}
|
|
7162
|
+
function getStateLastSignature(state) {
|
|
7163
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
7164
|
+
const last = messages[messages.length - 1];
|
|
7165
|
+
if (!last) return "";
|
|
7166
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
7167
|
+
}
|
|
7168
|
+
async function getStableExtensionBaseline(h) {
|
|
7169
|
+
const first = await readExtensionChatState(h);
|
|
7170
|
+
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
7171
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
7172
|
+
const second = await readExtensionChatState(h);
|
|
7173
|
+
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
7174
|
+
}
|
|
7175
|
+
async function verifyExtensionSendObserved(h, before) {
|
|
7176
|
+
const beforeCount = getStateMessageCount(before);
|
|
7177
|
+
const beforeSignature = getStateLastSignature(before);
|
|
7178
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
7179
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
7180
|
+
const state = await readExtensionChatState(h);
|
|
7181
|
+
if (state?.status === "waiting_approval") return true;
|
|
7182
|
+
const afterCount = getStateMessageCount(state);
|
|
7183
|
+
const afterSignature = getStateLastSignature(state);
|
|
7184
|
+
if (afterCount > beforeCount) return true;
|
|
7185
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
7186
|
+
}
|
|
7187
|
+
return false;
|
|
7188
|
+
}
|
|
6938
7189
|
async function handleChatHistory(h, args) {
|
|
6939
7190
|
const { agentType, offset, limit } = args;
|
|
6940
7191
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -7115,12 +7366,17 @@ async function handleSendChat(h, args) {
|
|
|
7115
7366
|
if (isExtensionTransport(transport)) {
|
|
7116
7367
|
_log(`Extension: ${provider?.type || "unknown_extension"}`);
|
|
7117
7368
|
try {
|
|
7369
|
+
const beforeState = await getStableExtensionBaseline(h);
|
|
7118
7370
|
const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
|
|
7119
7371
|
if (evalResult?.result) {
|
|
7120
7372
|
const parsed = parseMaybeJson(evalResult.result);
|
|
7121
7373
|
if (didProviderConfirmSend(parsed)) {
|
|
7122
|
-
|
|
7123
|
-
|
|
7374
|
+
const observed = await verifyExtensionSendObserved(h, beforeState);
|
|
7375
|
+
if (observed) {
|
|
7376
|
+
_log(`Extension script sent OK`);
|
|
7377
|
+
return _logSendSuccess("extension-script");
|
|
7378
|
+
}
|
|
7379
|
+
_log(`Extension script reported send but no chat-state change was observed`);
|
|
7124
7380
|
}
|
|
7125
7381
|
if (parsed?.needsTypeAndSend) {
|
|
7126
7382
|
_log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
|
|
@@ -7625,7 +7881,7 @@ async function handleResolveAction(h, args) {
|
|
|
7625
7881
|
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
7626
7882
|
}
|
|
7627
7883
|
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
7628
|
-
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
|
|
7884
|
+
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
|
|
7629
7885
|
return { success: ok };
|
|
7630
7886
|
}
|
|
7631
7887
|
if (transport === "acp") {
|
|
@@ -9032,6 +9288,7 @@ var CliProviderInstance = class {
|
|
|
9032
9288
|
historyWriter;
|
|
9033
9289
|
runtimeMessages = [];
|
|
9034
9290
|
instanceId;
|
|
9291
|
+
suppressIdleHistoryReplay = false;
|
|
9035
9292
|
presentationMode;
|
|
9036
9293
|
providerSessionId;
|
|
9037
9294
|
launchMode;
|
|
@@ -9059,7 +9316,15 @@ var CliProviderInstance = class {
|
|
|
9059
9316
|
await this.adapter.spawn();
|
|
9060
9317
|
this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
|
|
9061
9318
|
if (this.providerSessionId) {
|
|
9319
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
9062
9320
|
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
9321
|
+
this.historyWriter.seedSessionHistory(
|
|
9322
|
+
this.type,
|
|
9323
|
+
restoredHistory.messages,
|
|
9324
|
+
this.providerSessionId,
|
|
9325
|
+
this.instanceId
|
|
9326
|
+
);
|
|
9327
|
+
this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
|
|
9063
9328
|
if (restoredHistory.messages.length > 0) {
|
|
9064
9329
|
this.adapter.seedCommittedMessages(
|
|
9065
9330
|
restoredHistory.messages.map((message) => ({
|
|
@@ -9103,7 +9368,7 @@ var CliProviderInstance = class {
|
|
|
9103
9368
|
} else if (this.type === "codex-cli") {
|
|
9104
9369
|
probedSessionId = this.probeSessionIdFromConfig({
|
|
9105
9370
|
dbPath: "~/.codex/state_5.sqlite",
|
|
9106
|
-
query: "select id from threads where cwd in ({dirs}) and
|
|
9371
|
+
query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
|
|
9107
9372
|
timestampFormat: "unix_s"
|
|
9108
9373
|
});
|
|
9109
9374
|
} else if (this.type === "goose-cli") {
|
|
@@ -9163,6 +9428,7 @@ var CliProviderInstance = class {
|
|
|
9163
9428
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
9164
9429
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9165
9430
|
if (parsedMessages.length > 0) {
|
|
9431
|
+
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
9166
9432
|
let messagesToSave = parsedMessages;
|
|
9167
9433
|
if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
|
|
9168
9434
|
const lastIdx = messagesToSave.length - 1;
|
|
@@ -9170,7 +9436,7 @@ var CliProviderInstance = class {
|
|
|
9170
9436
|
messagesToSave = messagesToSave.slice(0, lastIdx);
|
|
9171
9437
|
}
|
|
9172
9438
|
}
|
|
9173
|
-
if (messagesToSave.length > 0) {
|
|
9439
|
+
if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
|
|
9174
9440
|
this.historyWriter.appendNewMessages(
|
|
9175
9441
|
this.type,
|
|
9176
9442
|
messagesToSave,
|
|
@@ -9265,6 +9531,7 @@ var CliProviderInstance = class {
|
|
|
9265
9531
|
if (newStatus !== this.lastStatus) {
|
|
9266
9532
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
9267
9533
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
9534
|
+
this.suppressIdleHistoryReplay = false;
|
|
9268
9535
|
if (this.completedDebouncePending) {
|
|
9269
9536
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
|
|
9270
9537
|
if (this.completedDebounceTimer) {
|
|
@@ -9284,6 +9551,7 @@ var CliProviderInstance = class {
|
|
|
9284
9551
|
this.generatingDebounceTimer = null;
|
|
9285
9552
|
}, 1e3);
|
|
9286
9553
|
} else if (newStatus === "waiting_approval") {
|
|
9554
|
+
this.suppressIdleHistoryReplay = false;
|
|
9287
9555
|
if (this.generatingDebouncePending) {
|
|
9288
9556
|
if (this.generatingDebounceTimer) {
|
|
9289
9557
|
clearTimeout(this.generatingDebounceTimer);
|
|
@@ -11001,6 +11269,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11001
11269
|
if (!instanceManager) return 0;
|
|
11002
11270
|
const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
|
|
11003
11271
|
let restored = 0;
|
|
11272
|
+
const restoredBindings = /* @__PURE__ */ new Set();
|
|
11004
11273
|
for (const record of sessions) {
|
|
11005
11274
|
if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
|
|
11006
11275
|
if (this.adapters.has(record.runtimeId) || instanceManager.getInstance(record.runtimeId)) continue;
|
|
@@ -11014,6 +11283,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11014
11283
|
record.cliArgs,
|
|
11015
11284
|
record.providerSessionId
|
|
11016
11285
|
);
|
|
11286
|
+
const bindingKey = [
|
|
11287
|
+
normalizedType,
|
|
11288
|
+
record.workspace,
|
|
11289
|
+
sessionBinding.providerSessionId || record.runtimeId
|
|
11290
|
+
].join("::");
|
|
11291
|
+
if (restoredBindings.has(bindingKey)) {
|
|
11292
|
+
LOG.info(
|
|
11293
|
+
"CLI",
|
|
11294
|
+
`\u21B7 Skipping duplicate hosted runtime restore: ${record.runtimeKey || record.runtimeId} (${normalizedType} @ ${record.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
|
|
11295
|
+
);
|
|
11296
|
+
continue;
|
|
11297
|
+
}
|
|
11017
11298
|
try {
|
|
11018
11299
|
await this.registerCliInstance(
|
|
11019
11300
|
record.runtimeId,
|
|
@@ -11029,6 +11310,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11029
11310
|
launchMode: "manual"
|
|
11030
11311
|
}
|
|
11031
11312
|
);
|
|
11313
|
+
restoredBindings.add(bindingKey);
|
|
11032
11314
|
restored += 1;
|
|
11033
11315
|
LOG.info("CLI", `\u267B Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
|
|
11034
11316
|
} catch (error) {
|
|
@@ -13380,6 +13662,15 @@ var DaemonCommandRouter = class {
|
|
|
13380
13662
|
const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
|
|
13381
13663
|
return { success: true, record };
|
|
13382
13664
|
}
|
|
13665
|
+
case "session_host_prune_duplicate_sessions": {
|
|
13666
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13667
|
+
const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
13668
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
13669
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
13670
|
+
dryRun: args?.dryRun === true
|
|
13671
|
+
});
|
|
13672
|
+
return { success: true, result };
|
|
13673
|
+
}
|
|
13383
13674
|
case "session_host_acquire_write": {
|
|
13384
13675
|
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13385
13676
|
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
@@ -13965,6 +14256,51 @@ var ProviderStreamAdapter = class {
|
|
|
13965
14256
|
isTransportError(reason) {
|
|
13966
14257
|
return /Session with given id not found/i.test(reason) || /CDP not connected/i.test(reason) || /Target closed/i.test(reason) || /WebSocket not open/i.test(reason) || /not connected/i.test(reason) || /execution context/i.test(reason) || /Cannot find context with specified id/i.test(reason);
|
|
13967
14258
|
}
|
|
14259
|
+
titlesMatch(actual, expected) {
|
|
14260
|
+
const lhs = actual.trim().toLowerCase();
|
|
14261
|
+
const rhs = expected.trim().toLowerCase();
|
|
14262
|
+
if (!lhs || !rhs) return false;
|
|
14263
|
+
return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
|
|
14264
|
+
}
|
|
14265
|
+
messageCount(state) {
|
|
14266
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
14267
|
+
}
|
|
14268
|
+
lastMessageSignature(state) {
|
|
14269
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
14270
|
+
const last = messages[messages.length - 1];
|
|
14271
|
+
if (!last) return "";
|
|
14272
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
14273
|
+
}
|
|
14274
|
+
async verifySendOutcome(evaluate, before) {
|
|
14275
|
+
const beforeCount = this.messageCount(before);
|
|
14276
|
+
const beforeSignature = this.lastMessageSignature(before);
|
|
14277
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
14278
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14279
|
+
let state;
|
|
14280
|
+
try {
|
|
14281
|
+
state = await this.readChat(evaluate);
|
|
14282
|
+
} catch {
|
|
14283
|
+
continue;
|
|
14284
|
+
}
|
|
14285
|
+
if (state.status === "waiting_approval") {
|
|
14286
|
+
return true;
|
|
14287
|
+
}
|
|
14288
|
+
const afterCount = this.messageCount(state);
|
|
14289
|
+
const afterSignature = this.lastMessageSignature(state);
|
|
14290
|
+
if (afterCount > beforeCount) return true;
|
|
14291
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
14292
|
+
}
|
|
14293
|
+
return false;
|
|
14294
|
+
}
|
|
14295
|
+
async readStableBaselineState(evaluate) {
|
|
14296
|
+
const first = await this.readChat(evaluate);
|
|
14297
|
+
if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
|
|
14298
|
+
return first;
|
|
14299
|
+
}
|
|
14300
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
14301
|
+
const second = await this.readChat(evaluate);
|
|
14302
|
+
return this.messageCount(second) >= this.messageCount(first) ? second : first;
|
|
14303
|
+
}
|
|
13968
14304
|
async readChat(evaluate) {
|
|
13969
14305
|
const script = this.callScript("readChat");
|
|
13970
14306
|
if (!script) return this.errorState("readChat script not available");
|
|
@@ -13990,6 +14326,9 @@ var ProviderStreamAdapter = class {
|
|
|
13990
14326
|
mode: data.mode,
|
|
13991
14327
|
activeModal: data.activeModal
|
|
13992
14328
|
};
|
|
14329
|
+
if (typeof data.title === "string" && data.title.trim()) {
|
|
14330
|
+
state.title = data.title.trim();
|
|
14331
|
+
}
|
|
13993
14332
|
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
13994
14333
|
if (controlValues) state.controlValues = controlValues;
|
|
13995
14334
|
const effects = normalizeProviderEffects(data);
|
|
@@ -14013,6 +14352,12 @@ var ProviderStreamAdapter = class {
|
|
|
14013
14352
|
}
|
|
14014
14353
|
}
|
|
14015
14354
|
async sendMessage(evaluate, text) {
|
|
14355
|
+
let beforeState = null;
|
|
14356
|
+
try {
|
|
14357
|
+
beforeState = await this.readStableBaselineState(evaluate);
|
|
14358
|
+
} catch {
|
|
14359
|
+
beforeState = null;
|
|
14360
|
+
}
|
|
14016
14361
|
const params = { message: text };
|
|
14017
14362
|
const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
|
|
14018
14363
|
if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
|
|
@@ -14030,7 +14375,9 @@ var ProviderStreamAdapter = class {
|
|
|
14030
14375
|
}
|
|
14031
14376
|
if (parsed && typeof parsed === "object") {
|
|
14032
14377
|
if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
|
|
14033
|
-
|
|
14378
|
+
const verified = await this.verifySendOutcome(evaluate, beforeState);
|
|
14379
|
+
if (verified) return;
|
|
14380
|
+
throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
|
|
14034
14381
|
}
|
|
14035
14382
|
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
14036
14383
|
throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
|
|
@@ -14041,7 +14388,15 @@ var ProviderStreamAdapter = class {
|
|
|
14041
14388
|
async resolveAction(evaluate, action, button) {
|
|
14042
14389
|
const script = this.callScript("resolveAction", { action, button });
|
|
14043
14390
|
if (!script) return false;
|
|
14044
|
-
|
|
14391
|
+
const result = await evaluate(script);
|
|
14392
|
+
const parsed = this.parseMaybeJson(result);
|
|
14393
|
+
if (parsed === true) return true;
|
|
14394
|
+
if (typeof parsed === "string") {
|
|
14395
|
+
const normalized = parsed.trim().toLowerCase();
|
|
14396
|
+
return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
|
|
14397
|
+
}
|
|
14398
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
14399
|
+
return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
|
|
14045
14400
|
}
|
|
14046
14401
|
async newSession(evaluate) {
|
|
14047
14402
|
const script = this.callScript("newSession");
|
|
@@ -14078,7 +14433,14 @@ var ProviderStreamAdapter = class {
|
|
|
14078
14433
|
return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
|
|
14079
14434
|
}
|
|
14080
14435
|
if (data && typeof data === "object") {
|
|
14081
|
-
|
|
14436
|
+
if (data.switched === true || data.success === true || data.ok === true) return true;
|
|
14437
|
+
if (typeof data.error === "string" && data.error.trim()) return false;
|
|
14438
|
+
}
|
|
14439
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
14440
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14441
|
+
const state = await this.readChat(evaluate);
|
|
14442
|
+
const title = typeof state.title === "string" ? state.title : "";
|
|
14443
|
+
if (this.titlesMatch(title, sessionId)) return true;
|
|
14082
14444
|
}
|
|
14083
14445
|
return false;
|
|
14084
14446
|
}
|
|
@@ -14286,7 +14648,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14286
14648
|
return false;
|
|
14287
14649
|
}
|
|
14288
14650
|
}
|
|
14289
|
-
async resolveSessionAction(cdp, sessionId, action) {
|
|
14651
|
+
async resolveSessionAction(cdp, sessionId, action, button) {
|
|
14290
14652
|
await this.ensureSessionPanelOpen(sessionId);
|
|
14291
14653
|
const target = this.getSessionTarget(sessionId);
|
|
14292
14654
|
if (!target?.parentSessionId) return false;
|
|
@@ -14296,7 +14658,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14296
14658
|
if (!agent) return false;
|
|
14297
14659
|
try {
|
|
14298
14660
|
const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
14299
|
-
return await agent.adapter.resolveAction(evaluate, action);
|
|
14661
|
+
return await agent.adapter.resolveAction(evaluate, action, button);
|
|
14300
14662
|
} catch (e) {
|
|
14301
14663
|
this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
|
|
14302
14664
|
return false;
|