@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.js
CHANGED
|
@@ -1642,6 +1642,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1642
1642
|
looksLikeVisibleIdlePrompt(screenText) {
|
|
1643
1643
|
const text = String(screenText || "");
|
|
1644
1644
|
if (!text.trim()) return false;
|
|
1645
|
+
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)) {
|
|
1646
|
+
return true;
|
|
1647
|
+
}
|
|
1645
1648
|
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);
|
|
1646
1649
|
}
|
|
1647
1650
|
findLastMatchingLineIndex(lines, predicate) {
|
|
@@ -4969,11 +4972,72 @@ var path7 = __toESM(require("path"));
|
|
|
4969
4972
|
var os5 = __toESM(require("os"));
|
|
4970
4973
|
var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
|
|
4971
4974
|
var RETAIN_DAYS = 30;
|
|
4975
|
+
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;
|
|
4976
|
+
function normalizeHistoryComparable(text) {
|
|
4977
|
+
return String(text || "").replace(/\s+/g, " ").trim();
|
|
4978
|
+
}
|
|
4979
|
+
function cleanupHistoryContent(agentType, role, content) {
|
|
4980
|
+
let value = String(content || "").replace(/\r\n/g, "\n").trim();
|
|
4981
|
+
if (!value) return "";
|
|
4982
|
+
if (agentType === "codex-cli" && role === "assistant") {
|
|
4983
|
+
const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4984
|
+
value = filtered;
|
|
4985
|
+
}
|
|
4986
|
+
return value;
|
|
4987
|
+
}
|
|
4988
|
+
function buildHistoryMessageHash(agentType, message) {
|
|
4989
|
+
if (message.historyDedupKey) return message.historyDedupKey;
|
|
4990
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4991
|
+
return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
|
|
4992
|
+
}
|
|
4993
|
+
function buildHistoryMessageSignature(agentType, message) {
|
|
4994
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4995
|
+
return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
|
|
4996
|
+
}
|
|
4997
|
+
function isAdjacentHistoryDuplicate(agentType, previous, next) {
|
|
4998
|
+
if (!previous || !next) return false;
|
|
4999
|
+
return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
|
|
5000
|
+
}
|
|
5001
|
+
function collapseReplayAssistantTurns(agentType, messages) {
|
|
5002
|
+
if (agentType !== "codex-cli") return messages;
|
|
5003
|
+
const collapsed = [];
|
|
5004
|
+
let sawAssistantSinceLastUser = false;
|
|
5005
|
+
for (const message of messages) {
|
|
5006
|
+
if (message.role === "user") {
|
|
5007
|
+
sawAssistantSinceLastUser = false;
|
|
5008
|
+
collapsed.push(message);
|
|
5009
|
+
continue;
|
|
5010
|
+
}
|
|
5011
|
+
if (message.role === "assistant") {
|
|
5012
|
+
if (sawAssistantSinceLastUser) continue;
|
|
5013
|
+
sawAssistantSinceLastUser = true;
|
|
5014
|
+
collapsed.push(message);
|
|
5015
|
+
continue;
|
|
5016
|
+
}
|
|
5017
|
+
collapsed.push(message);
|
|
5018
|
+
}
|
|
5019
|
+
return collapsed;
|
|
5020
|
+
}
|
|
5021
|
+
function sanitizeHistoryMessage(agentType, message) {
|
|
5022
|
+
if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
|
|
5023
|
+
return null;
|
|
5024
|
+
}
|
|
5025
|
+
const content = cleanupHistoryContent(agentType, message.role, message.content);
|
|
5026
|
+
if (!content) return null;
|
|
5027
|
+
return {
|
|
5028
|
+
...message,
|
|
5029
|
+
content
|
|
5030
|
+
};
|
|
5031
|
+
}
|
|
4972
5032
|
var ChatHistoryWriter = class {
|
|
4973
5033
|
/** Last seen message count per agent (deduplication) */
|
|
4974
5034
|
lastSeenCounts = /* @__PURE__ */ new Map();
|
|
4975
5035
|
/** Last seen message hash per agent (deduplication) */
|
|
4976
5036
|
lastSeenHashes = /* @__PURE__ */ new Map();
|
|
5037
|
+
/** Last appended normalized message signature per agent/session */
|
|
5038
|
+
lastSeenSignatures = /* @__PURE__ */ new Map();
|
|
5039
|
+
/** Last appended normalized non-system turn signature per agent/session */
|
|
5040
|
+
lastSeenTurnSignatures = /* @__PURE__ */ new Map();
|
|
4977
5041
|
rotated = false;
|
|
4978
5042
|
/**
|
|
4979
5043
|
* Append new messages to history
|
|
@@ -4995,14 +5059,36 @@ var ChatHistoryWriter = class {
|
|
|
4995
5059
|
}
|
|
4996
5060
|
const newMessages = [];
|
|
4997
5061
|
for (const msg of messages) {
|
|
4998
|
-
const
|
|
5062
|
+
const role = msg.role;
|
|
5063
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
5064
|
+
const content = cleanupHistoryContent(agentType, role, msg.content || "");
|
|
5065
|
+
if (!content) continue;
|
|
5066
|
+
const receivedAt = msg.receivedAt || Date.now();
|
|
5067
|
+
const hash = buildHistoryMessageHash(agentType, {
|
|
5068
|
+
role,
|
|
5069
|
+
content,
|
|
5070
|
+
receivedAt,
|
|
5071
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
5072
|
+
historyDedupKey: msg.historyDedupKey
|
|
5073
|
+
});
|
|
5074
|
+
const signature = buildHistoryMessageSignature(agentType, {
|
|
5075
|
+
role,
|
|
5076
|
+
content,
|
|
5077
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0
|
|
5078
|
+
});
|
|
4999
5079
|
if (seenHashes.has(hash)) continue;
|
|
5080
|
+
if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
|
|
5081
|
+
if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
|
|
5000
5082
|
seenHashes.add(hash);
|
|
5083
|
+
this.lastSeenSignatures.set(dedupKey, signature);
|
|
5084
|
+
if (role !== "system") {
|
|
5085
|
+
this.lastSeenTurnSignatures.set(dedupKey, signature);
|
|
5086
|
+
}
|
|
5001
5087
|
newMessages.push({
|
|
5002
|
-
ts: new Date(
|
|
5003
|
-
receivedAt
|
|
5004
|
-
role
|
|
5005
|
-
content
|
|
5088
|
+
ts: new Date(receivedAt).toISOString(),
|
|
5089
|
+
receivedAt,
|
|
5090
|
+
role,
|
|
5091
|
+
content,
|
|
5006
5092
|
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
5007
5093
|
senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
|
|
5008
5094
|
agent: agentType,
|
|
@@ -5022,6 +5108,8 @@ var ChatHistoryWriter = class {
|
|
|
5022
5108
|
const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
|
|
5023
5109
|
if (messages.length < prevCount * 0.5 && prevCount > 3) {
|
|
5024
5110
|
seenHashes.clear();
|
|
5111
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5112
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
5025
5113
|
for (const msg of messages) {
|
|
5026
5114
|
seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
|
|
5027
5115
|
}
|
|
@@ -5035,6 +5123,54 @@ var ChatHistoryWriter = class {
|
|
|
5035
5123
|
} catch {
|
|
5036
5124
|
}
|
|
5037
5125
|
}
|
|
5126
|
+
seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
|
|
5127
|
+
const effectiveHistoryKey = historySessionId || instanceId;
|
|
5128
|
+
const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
|
|
5129
|
+
const seenHashes = /* @__PURE__ */ new Set();
|
|
5130
|
+
for (const raw of messages) {
|
|
5131
|
+
const role = raw?.role;
|
|
5132
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
5133
|
+
const content = cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5134
|
+
if (!content) continue;
|
|
5135
|
+
seenHashes.add(buildHistoryMessageHash(agentType, {
|
|
5136
|
+
role,
|
|
5137
|
+
content,
|
|
5138
|
+
receivedAt: raw?.receivedAt || 0,
|
|
5139
|
+
kind: typeof raw?.kind === "string" ? raw.kind : void 0,
|
|
5140
|
+
historyDedupKey: raw?.historyDedupKey
|
|
5141
|
+
}));
|
|
5142
|
+
}
|
|
5143
|
+
this.lastSeenHashes.set(dedupKey, seenHashes);
|
|
5144
|
+
this.lastSeenCounts.set(dedupKey, messages.length);
|
|
5145
|
+
const lastMessage = [...messages].reverse().find((raw) => {
|
|
5146
|
+
const role = raw?.role;
|
|
5147
|
+
if (role !== "user" && role !== "assistant" && role !== "system") return false;
|
|
5148
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5149
|
+
});
|
|
5150
|
+
const lastTurnMessage = [...messages].reverse().find((raw) => {
|
|
5151
|
+
const role = raw?.role;
|
|
5152
|
+
if (role !== "user" && role !== "assistant") return false;
|
|
5153
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5154
|
+
});
|
|
5155
|
+
if (lastMessage) {
|
|
5156
|
+
this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5157
|
+
role: lastMessage.role,
|
|
5158
|
+
content: lastMessage.content,
|
|
5159
|
+
kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
|
|
5160
|
+
}));
|
|
5161
|
+
} else {
|
|
5162
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5163
|
+
}
|
|
5164
|
+
if (lastTurnMessage) {
|
|
5165
|
+
this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5166
|
+
role: lastTurnMessage.role,
|
|
5167
|
+
content: lastTurnMessage.content,
|
|
5168
|
+
kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
|
|
5169
|
+
}));
|
|
5170
|
+
} else {
|
|
5171
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5038
5174
|
appendSystemMarker(agentType, content, options = {}) {
|
|
5039
5175
|
this.appendNewMessages(
|
|
5040
5176
|
agentType,
|
|
@@ -5065,6 +5201,16 @@ var ChatHistoryWriter = class {
|
|
|
5065
5201
|
this.lastSeenHashes.set(toDedupKey, nextHashes);
|
|
5066
5202
|
this.lastSeenHashes.delete(fromDedupKey);
|
|
5067
5203
|
}
|
|
5204
|
+
const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
|
|
5205
|
+
if (fromSignature) {
|
|
5206
|
+
this.lastSeenSignatures.set(toDedupKey, fromSignature);
|
|
5207
|
+
this.lastSeenSignatures.delete(fromDedupKey);
|
|
5208
|
+
}
|
|
5209
|
+
const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
|
|
5210
|
+
if (fromTurnSignature) {
|
|
5211
|
+
this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
|
|
5212
|
+
this.lastSeenTurnSignatures.delete(fromDedupKey);
|
|
5213
|
+
}
|
|
5068
5214
|
const fromCount = this.lastSeenCounts.get(fromDedupKey);
|
|
5069
5215
|
if (typeof fromCount === "number") {
|
|
5070
5216
|
this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
|
|
@@ -5106,10 +5252,61 @@ var ChatHistoryWriter = class {
|
|
|
5106
5252
|
} catch {
|
|
5107
5253
|
}
|
|
5108
5254
|
}
|
|
5255
|
+
compactHistorySession(agentType, historySessionId) {
|
|
5256
|
+
const sessionId = String(historySessionId || "").trim();
|
|
5257
|
+
if (!sessionId) return;
|
|
5258
|
+
try {
|
|
5259
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
5260
|
+
if (!fs3.existsSync(dir)) return;
|
|
5261
|
+
const prefix = `${this.sanitize(sessionId)}_`;
|
|
5262
|
+
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(prefix) && file.endsWith(".jsonl")).sort();
|
|
5263
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5264
|
+
for (const file of files) {
|
|
5265
|
+
const filePath = path7.join(dir, file);
|
|
5266
|
+
const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
5267
|
+
const next = [];
|
|
5268
|
+
for (const line of lines) {
|
|
5269
|
+
let parsed = null;
|
|
5270
|
+
try {
|
|
5271
|
+
parsed = JSON.parse(line);
|
|
5272
|
+
} catch {
|
|
5273
|
+
parsed = null;
|
|
5274
|
+
}
|
|
5275
|
+
if (!parsed || parsed.historySessionId !== sessionId) continue;
|
|
5276
|
+
const sanitized = sanitizeHistoryMessage(agentType, parsed);
|
|
5277
|
+
if (!sanitized) continue;
|
|
5278
|
+
const hash = buildHistoryMessageHash(agentType, sanitized);
|
|
5279
|
+
if (seen.has(hash)) continue;
|
|
5280
|
+
seen.add(hash);
|
|
5281
|
+
next.push(sanitized);
|
|
5282
|
+
}
|
|
5283
|
+
next.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5284
|
+
const dedupedAdjacent = [];
|
|
5285
|
+
let lastTurn = null;
|
|
5286
|
+
for (const entry of next) {
|
|
5287
|
+
const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
|
|
5288
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
|
|
5289
|
+
if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
|
|
5290
|
+
dedupedAdjacent.push(entry);
|
|
5291
|
+
if (entry.role !== "system") lastTurn = entry;
|
|
5292
|
+
}
|
|
5293
|
+
const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
|
|
5294
|
+
if (collapsed.length === 0) {
|
|
5295
|
+
fs3.unlinkSync(filePath);
|
|
5296
|
+
continue;
|
|
5297
|
+
}
|
|
5298
|
+
fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
5299
|
+
`, "utf-8");
|
|
5300
|
+
}
|
|
5301
|
+
} catch {
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5109
5304
|
/** Called when agent session is explicitly changed */
|
|
5110
5305
|
onSessionChange(agentType) {
|
|
5111
5306
|
this.lastSeenHashes.delete(agentType);
|
|
5112
5307
|
this.lastSeenCounts.delete(agentType);
|
|
5308
|
+
this.lastSeenSignatures.delete(agentType);
|
|
5309
|
+
this.lastSeenTurnSignatures.delete(agentType);
|
|
5113
5310
|
}
|
|
5114
5311
|
/** Delete history files older than 30 days */
|
|
5115
5312
|
async rotateOldFiles() {
|
|
@@ -5150,23 +5347,37 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
|
5150
5347
|
return true;
|
|
5151
5348
|
}).sort().reverse();
|
|
5152
5349
|
const allMessages = [];
|
|
5153
|
-
const
|
|
5350
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5154
5351
|
for (const file of files) {
|
|
5155
|
-
if (allMessages.length >= needed) break;
|
|
5156
5352
|
const filePath = path7.join(dir, file);
|
|
5157
5353
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5158
5354
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
5159
|
-
for (let i =
|
|
5160
|
-
if (allMessages.length >= needed) break;
|
|
5355
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5161
5356
|
try {
|
|
5162
|
-
|
|
5357
|
+
const parsed = JSON.parse(lines[i]);
|
|
5358
|
+
const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
|
|
5359
|
+
if (!sanitizedMessage) continue;
|
|
5360
|
+
const hash = buildHistoryMessageHash(agentType, sanitizedMessage);
|
|
5361
|
+
if (seen.has(hash)) continue;
|
|
5362
|
+
seen.add(hash);
|
|
5363
|
+
allMessages.push(sanitizedMessage);
|
|
5163
5364
|
} catch {
|
|
5164
5365
|
}
|
|
5165
5366
|
}
|
|
5166
5367
|
}
|
|
5167
|
-
|
|
5168
|
-
const
|
|
5169
|
-
|
|
5368
|
+
allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5369
|
+
const chronological = [];
|
|
5370
|
+
let lastTurn = null;
|
|
5371
|
+
for (const message of allMessages) {
|
|
5372
|
+
const previous = chronological[chronological.length - 1];
|
|
5373
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
5374
|
+
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
5375
|
+
chronological.push(message);
|
|
5376
|
+
if (message.role !== "system") lastTurn = message;
|
|
5377
|
+
}
|
|
5378
|
+
const collapsed = collapseReplayAssistantTurns(agentType, chronological);
|
|
5379
|
+
const sliced = collapsed.slice(offset, offset + limit);
|
|
5380
|
+
const hasMore = collapsed.length > offset + limit;
|
|
5170
5381
|
return { messages: sliced, hasMore };
|
|
5171
5382
|
} catch {
|
|
5172
5383
|
return { messages: [], hasMore: false };
|
|
@@ -7020,6 +7231,46 @@ function didProviderConfirmSend(result) {
|
|
|
7020
7231
|
if (!parsed || typeof parsed !== "object") return false;
|
|
7021
7232
|
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
7022
7233
|
}
|
|
7234
|
+
async function readExtensionChatState(h) {
|
|
7235
|
+
try {
|
|
7236
|
+
const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
|
|
7237
|
+
if (!evalResult?.result) return null;
|
|
7238
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
7239
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
7240
|
+
} catch {
|
|
7241
|
+
return null;
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
function getStateMessageCount(state) {
|
|
7245
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
7246
|
+
}
|
|
7247
|
+
function getStateLastSignature(state) {
|
|
7248
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
7249
|
+
const last = messages[messages.length - 1];
|
|
7250
|
+
if (!last) return "";
|
|
7251
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
7252
|
+
}
|
|
7253
|
+
async function getStableExtensionBaseline(h) {
|
|
7254
|
+
const first = await readExtensionChatState(h);
|
|
7255
|
+
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
7256
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
7257
|
+
const second = await readExtensionChatState(h);
|
|
7258
|
+
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
7259
|
+
}
|
|
7260
|
+
async function verifyExtensionSendObserved(h, before) {
|
|
7261
|
+
const beforeCount = getStateMessageCount(before);
|
|
7262
|
+
const beforeSignature = getStateLastSignature(before);
|
|
7263
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
7264
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
7265
|
+
const state = await readExtensionChatState(h);
|
|
7266
|
+
if (state?.status === "waiting_approval") return true;
|
|
7267
|
+
const afterCount = getStateMessageCount(state);
|
|
7268
|
+
const afterSignature = getStateLastSignature(state);
|
|
7269
|
+
if (afterCount > beforeCount) return true;
|
|
7270
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
7271
|
+
}
|
|
7272
|
+
return false;
|
|
7273
|
+
}
|
|
7023
7274
|
async function handleChatHistory(h, args) {
|
|
7024
7275
|
const { agentType, offset, limit } = args;
|
|
7025
7276
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -7200,12 +7451,17 @@ async function handleSendChat(h, args) {
|
|
|
7200
7451
|
if (isExtensionTransport(transport)) {
|
|
7201
7452
|
_log(`Extension: ${provider?.type || "unknown_extension"}`);
|
|
7202
7453
|
try {
|
|
7454
|
+
const beforeState = await getStableExtensionBaseline(h);
|
|
7203
7455
|
const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
|
|
7204
7456
|
if (evalResult?.result) {
|
|
7205
7457
|
const parsed = parseMaybeJson(evalResult.result);
|
|
7206
7458
|
if (didProviderConfirmSend(parsed)) {
|
|
7207
|
-
|
|
7208
|
-
|
|
7459
|
+
const observed = await verifyExtensionSendObserved(h, beforeState);
|
|
7460
|
+
if (observed) {
|
|
7461
|
+
_log(`Extension script sent OK`);
|
|
7462
|
+
return _logSendSuccess("extension-script");
|
|
7463
|
+
}
|
|
7464
|
+
_log(`Extension script reported send but no chat-state change was observed`);
|
|
7209
7465
|
}
|
|
7210
7466
|
if (parsed?.needsTypeAndSend) {
|
|
7211
7467
|
_log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
|
|
@@ -7710,7 +7966,7 @@ async function handleResolveAction(h, args) {
|
|
|
7710
7966
|
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
7711
7967
|
}
|
|
7712
7968
|
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
7713
|
-
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
|
|
7969
|
+
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
|
|
7714
7970
|
return { success: ok };
|
|
7715
7971
|
}
|
|
7716
7972
|
if (transport === "acp") {
|
|
@@ -9117,6 +9373,7 @@ var CliProviderInstance = class {
|
|
|
9117
9373
|
historyWriter;
|
|
9118
9374
|
runtimeMessages = [];
|
|
9119
9375
|
instanceId;
|
|
9376
|
+
suppressIdleHistoryReplay = false;
|
|
9120
9377
|
presentationMode;
|
|
9121
9378
|
providerSessionId;
|
|
9122
9379
|
launchMode;
|
|
@@ -9144,7 +9401,15 @@ var CliProviderInstance = class {
|
|
|
9144
9401
|
await this.adapter.spawn();
|
|
9145
9402
|
this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
|
|
9146
9403
|
if (this.providerSessionId) {
|
|
9404
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
9147
9405
|
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
9406
|
+
this.historyWriter.seedSessionHistory(
|
|
9407
|
+
this.type,
|
|
9408
|
+
restoredHistory.messages,
|
|
9409
|
+
this.providerSessionId,
|
|
9410
|
+
this.instanceId
|
|
9411
|
+
);
|
|
9412
|
+
this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
|
|
9148
9413
|
if (restoredHistory.messages.length > 0) {
|
|
9149
9414
|
this.adapter.seedCommittedMessages(
|
|
9150
9415
|
restoredHistory.messages.map((message) => ({
|
|
@@ -9188,7 +9453,7 @@ var CliProviderInstance = class {
|
|
|
9188
9453
|
} else if (this.type === "codex-cli") {
|
|
9189
9454
|
probedSessionId = this.probeSessionIdFromConfig({
|
|
9190
9455
|
dbPath: "~/.codex/state_5.sqlite",
|
|
9191
|
-
query: "select id from threads where cwd in ({dirs}) and
|
|
9456
|
+
query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
|
|
9192
9457
|
timestampFormat: "unix_s"
|
|
9193
9458
|
});
|
|
9194
9459
|
} else if (this.type === "goose-cli") {
|
|
@@ -9248,6 +9513,7 @@ var CliProviderInstance = class {
|
|
|
9248
9513
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
9249
9514
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9250
9515
|
if (parsedMessages.length > 0) {
|
|
9516
|
+
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
9251
9517
|
let messagesToSave = parsedMessages;
|
|
9252
9518
|
if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
|
|
9253
9519
|
const lastIdx = messagesToSave.length - 1;
|
|
@@ -9255,7 +9521,7 @@ var CliProviderInstance = class {
|
|
|
9255
9521
|
messagesToSave = messagesToSave.slice(0, lastIdx);
|
|
9256
9522
|
}
|
|
9257
9523
|
}
|
|
9258
|
-
if (messagesToSave.length > 0) {
|
|
9524
|
+
if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
|
|
9259
9525
|
this.historyWriter.appendNewMessages(
|
|
9260
9526
|
this.type,
|
|
9261
9527
|
messagesToSave,
|
|
@@ -9350,6 +9616,7 @@ var CliProviderInstance = class {
|
|
|
9350
9616
|
if (newStatus !== this.lastStatus) {
|
|
9351
9617
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
9352
9618
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
9619
|
+
this.suppressIdleHistoryReplay = false;
|
|
9353
9620
|
if (this.completedDebouncePending) {
|
|
9354
9621
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
|
|
9355
9622
|
if (this.completedDebounceTimer) {
|
|
@@ -9369,6 +9636,7 @@ var CliProviderInstance = class {
|
|
|
9369
9636
|
this.generatingDebounceTimer = null;
|
|
9370
9637
|
}, 1e3);
|
|
9371
9638
|
} else if (newStatus === "waiting_approval") {
|
|
9639
|
+
this.suppressIdleHistoryReplay = false;
|
|
9372
9640
|
if (this.generatingDebouncePending) {
|
|
9373
9641
|
if (this.generatingDebounceTimer) {
|
|
9374
9642
|
clearTimeout(this.generatingDebounceTimer);
|
|
@@ -11081,6 +11349,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11081
11349
|
if (!instanceManager) return 0;
|
|
11082
11350
|
const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
|
|
11083
11351
|
let restored = 0;
|
|
11352
|
+
const restoredBindings = /* @__PURE__ */ new Set();
|
|
11084
11353
|
for (const record of sessions) {
|
|
11085
11354
|
if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
|
|
11086
11355
|
if (this.adapters.has(record.runtimeId) || instanceManager.getInstance(record.runtimeId)) continue;
|
|
@@ -11094,6 +11363,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11094
11363
|
record.cliArgs,
|
|
11095
11364
|
record.providerSessionId
|
|
11096
11365
|
);
|
|
11366
|
+
const bindingKey = [
|
|
11367
|
+
normalizedType,
|
|
11368
|
+
record.workspace,
|
|
11369
|
+
sessionBinding.providerSessionId || record.runtimeId
|
|
11370
|
+
].join("::");
|
|
11371
|
+
if (restoredBindings.has(bindingKey)) {
|
|
11372
|
+
LOG.info(
|
|
11373
|
+
"CLI",
|
|
11374
|
+
`\u21B7 Skipping duplicate hosted runtime restore: ${record.runtimeKey || record.runtimeId} (${normalizedType} @ ${record.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
|
|
11375
|
+
);
|
|
11376
|
+
continue;
|
|
11377
|
+
}
|
|
11097
11378
|
try {
|
|
11098
11379
|
await this.registerCliInstance(
|
|
11099
11380
|
record.runtimeId,
|
|
@@ -11109,6 +11390,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11109
11390
|
launchMode: "manual"
|
|
11110
11391
|
}
|
|
11111
11392
|
);
|
|
11393
|
+
restoredBindings.add(bindingKey);
|
|
11112
11394
|
restored += 1;
|
|
11113
11395
|
LOG.info("CLI", `\u267B Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
|
|
11114
11396
|
} catch (error) {
|
|
@@ -13460,6 +13742,15 @@ var DaemonCommandRouter = class {
|
|
|
13460
13742
|
const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
|
|
13461
13743
|
return { success: true, record };
|
|
13462
13744
|
}
|
|
13745
|
+
case "session_host_prune_duplicate_sessions": {
|
|
13746
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13747
|
+
const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
13748
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
13749
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
13750
|
+
dryRun: args?.dryRun === true
|
|
13751
|
+
});
|
|
13752
|
+
return { success: true, result };
|
|
13753
|
+
}
|
|
13463
13754
|
case "session_host_acquire_write": {
|
|
13464
13755
|
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13465
13756
|
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
@@ -14045,6 +14336,51 @@ var ProviderStreamAdapter = class {
|
|
|
14045
14336
|
isTransportError(reason) {
|
|
14046
14337
|
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);
|
|
14047
14338
|
}
|
|
14339
|
+
titlesMatch(actual, expected) {
|
|
14340
|
+
const lhs = actual.trim().toLowerCase();
|
|
14341
|
+
const rhs = expected.trim().toLowerCase();
|
|
14342
|
+
if (!lhs || !rhs) return false;
|
|
14343
|
+
return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
|
|
14344
|
+
}
|
|
14345
|
+
messageCount(state) {
|
|
14346
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
14347
|
+
}
|
|
14348
|
+
lastMessageSignature(state) {
|
|
14349
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
14350
|
+
const last = messages[messages.length - 1];
|
|
14351
|
+
if (!last) return "";
|
|
14352
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
14353
|
+
}
|
|
14354
|
+
async verifySendOutcome(evaluate, before) {
|
|
14355
|
+
const beforeCount = this.messageCount(before);
|
|
14356
|
+
const beforeSignature = this.lastMessageSignature(before);
|
|
14357
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
14358
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14359
|
+
let state;
|
|
14360
|
+
try {
|
|
14361
|
+
state = await this.readChat(evaluate);
|
|
14362
|
+
} catch {
|
|
14363
|
+
continue;
|
|
14364
|
+
}
|
|
14365
|
+
if (state.status === "waiting_approval") {
|
|
14366
|
+
return true;
|
|
14367
|
+
}
|
|
14368
|
+
const afterCount = this.messageCount(state);
|
|
14369
|
+
const afterSignature = this.lastMessageSignature(state);
|
|
14370
|
+
if (afterCount > beforeCount) return true;
|
|
14371
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
14372
|
+
}
|
|
14373
|
+
return false;
|
|
14374
|
+
}
|
|
14375
|
+
async readStableBaselineState(evaluate) {
|
|
14376
|
+
const first = await this.readChat(evaluate);
|
|
14377
|
+
if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
|
|
14378
|
+
return first;
|
|
14379
|
+
}
|
|
14380
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
14381
|
+
const second = await this.readChat(evaluate);
|
|
14382
|
+
return this.messageCount(second) >= this.messageCount(first) ? second : first;
|
|
14383
|
+
}
|
|
14048
14384
|
async readChat(evaluate) {
|
|
14049
14385
|
const script = this.callScript("readChat");
|
|
14050
14386
|
if (!script) return this.errorState("readChat script not available");
|
|
@@ -14070,6 +14406,9 @@ var ProviderStreamAdapter = class {
|
|
|
14070
14406
|
mode: data.mode,
|
|
14071
14407
|
activeModal: data.activeModal
|
|
14072
14408
|
};
|
|
14409
|
+
if (typeof data.title === "string" && data.title.trim()) {
|
|
14410
|
+
state.title = data.title.trim();
|
|
14411
|
+
}
|
|
14073
14412
|
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
14074
14413
|
if (controlValues) state.controlValues = controlValues;
|
|
14075
14414
|
const effects = normalizeProviderEffects(data);
|
|
@@ -14093,6 +14432,12 @@ var ProviderStreamAdapter = class {
|
|
|
14093
14432
|
}
|
|
14094
14433
|
}
|
|
14095
14434
|
async sendMessage(evaluate, text) {
|
|
14435
|
+
let beforeState = null;
|
|
14436
|
+
try {
|
|
14437
|
+
beforeState = await this.readStableBaselineState(evaluate);
|
|
14438
|
+
} catch {
|
|
14439
|
+
beforeState = null;
|
|
14440
|
+
}
|
|
14096
14441
|
const params = { message: text };
|
|
14097
14442
|
const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
|
|
14098
14443
|
if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
|
|
@@ -14110,7 +14455,9 @@ var ProviderStreamAdapter = class {
|
|
|
14110
14455
|
}
|
|
14111
14456
|
if (parsed && typeof parsed === "object") {
|
|
14112
14457
|
if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
|
|
14113
|
-
|
|
14458
|
+
const verified = await this.verifySendOutcome(evaluate, beforeState);
|
|
14459
|
+
if (verified) return;
|
|
14460
|
+
throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
|
|
14114
14461
|
}
|
|
14115
14462
|
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
14116
14463
|
throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
|
|
@@ -14121,7 +14468,15 @@ var ProviderStreamAdapter = class {
|
|
|
14121
14468
|
async resolveAction(evaluate, action, button) {
|
|
14122
14469
|
const script = this.callScript("resolveAction", { action, button });
|
|
14123
14470
|
if (!script) return false;
|
|
14124
|
-
|
|
14471
|
+
const result = await evaluate(script);
|
|
14472
|
+
const parsed = this.parseMaybeJson(result);
|
|
14473
|
+
if (parsed === true) return true;
|
|
14474
|
+
if (typeof parsed === "string") {
|
|
14475
|
+
const normalized = parsed.trim().toLowerCase();
|
|
14476
|
+
return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
|
|
14477
|
+
}
|
|
14478
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
14479
|
+
return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
|
|
14125
14480
|
}
|
|
14126
14481
|
async newSession(evaluate) {
|
|
14127
14482
|
const script = this.callScript("newSession");
|
|
@@ -14158,7 +14513,14 @@ var ProviderStreamAdapter = class {
|
|
|
14158
14513
|
return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
|
|
14159
14514
|
}
|
|
14160
14515
|
if (data && typeof data === "object") {
|
|
14161
|
-
|
|
14516
|
+
if (data.switched === true || data.success === true || data.ok === true) return true;
|
|
14517
|
+
if (typeof data.error === "string" && data.error.trim()) return false;
|
|
14518
|
+
}
|
|
14519
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
14520
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14521
|
+
const state = await this.readChat(evaluate);
|
|
14522
|
+
const title = typeof state.title === "string" ? state.title : "";
|
|
14523
|
+
if (this.titlesMatch(title, sessionId)) return true;
|
|
14162
14524
|
}
|
|
14163
14525
|
return false;
|
|
14164
14526
|
}
|
|
@@ -14366,7 +14728,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14366
14728
|
return false;
|
|
14367
14729
|
}
|
|
14368
14730
|
}
|
|
14369
|
-
async resolveSessionAction(cdp, sessionId, action) {
|
|
14731
|
+
async resolveSessionAction(cdp, sessionId, action, button) {
|
|
14370
14732
|
await this.ensureSessionPanelOpen(sessionId);
|
|
14371
14733
|
const target = this.getSessionTarget(sessionId);
|
|
14372
14734
|
if (!target?.parentSessionId) return false;
|
|
@@ -14376,7 +14738,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14376
14738
|
if (!agent) return false;
|
|
14377
14739
|
try {
|
|
14378
14740
|
const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
14379
|
-
return await agent.adapter.resolveAction(evaluate, action);
|
|
14741
|
+
return await agent.adapter.resolveAction(evaluate, action, button);
|
|
14380
14742
|
} catch (e) {
|
|
14381
14743
|
this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
|
|
14382
14744
|
return false;
|