@adhdev/daemon-core 0.8.28 → 0.8.30
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 +552 -52
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +552 -52
- package/dist/index.mjs.map +1 -1
- package/dist/providers/acp-provider-instance.d.ts +1 -0
- package/dist/providers/approval-utils.d.ts +7 -0
- package/dist/providers/cli-provider-instance.d.ts +3 -0
- package/dist/providers/contracts.d.ts +2 -0
- package/dist/providers/ide-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/poller.ts +38 -1
- 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/acp-provider-instance.ts +17 -2
- package/src/providers/approval-utils.ts +66 -0
- package/src/providers/cli-provider-instance.ts +47 -6
- package/src/providers/contracts.d.ts +1 -0
- package/src/providers/contracts.ts +3 -1
- package/src/providers/ide-provider-instance.ts +28 -23
- package/src/providers/provider-loader.ts +26 -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 };
|
|
@@ -5588,6 +5799,55 @@ ${effect.notification.body || ""}`.trim();
|
|
|
5588
5799
|
|
|
5589
5800
|
// src/providers/ide-provider-instance.ts
|
|
5590
5801
|
init_logger();
|
|
5802
|
+
|
|
5803
|
+
// src/providers/approval-utils.ts
|
|
5804
|
+
var DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
5805
|
+
"run",
|
|
5806
|
+
"approve",
|
|
5807
|
+
"accept",
|
|
5808
|
+
"allow once",
|
|
5809
|
+
"always allow",
|
|
5810
|
+
"allow",
|
|
5811
|
+
"yes",
|
|
5812
|
+
"proceed",
|
|
5813
|
+
"continue",
|
|
5814
|
+
"confirm",
|
|
5815
|
+
"save",
|
|
5816
|
+
"ok",
|
|
5817
|
+
"trust"
|
|
5818
|
+
];
|
|
5819
|
+
function normalizeApprovalLabel(value) {
|
|
5820
|
+
return String(value || "").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
5821
|
+
}
|
|
5822
|
+
function getApprovalPositiveHints(provider) {
|
|
5823
|
+
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
5824
|
+
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
5825
|
+
}
|
|
5826
|
+
function pickApprovalButton(buttons, provider) {
|
|
5827
|
+
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
5828
|
+
if (labels.length === 0) {
|
|
5829
|
+
return { index: 0, label: "Approve" };
|
|
5830
|
+
}
|
|
5831
|
+
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
5832
|
+
const hints = getApprovalPositiveHints(provider);
|
|
5833
|
+
for (const hint of hints) {
|
|
5834
|
+
const exactIndex = normalizedButtons.findIndex((label) => label === hint);
|
|
5835
|
+
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
5836
|
+
const prefixIndex = normalizedButtons.findIndex((label) => label.startsWith(hint));
|
|
5837
|
+
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
5838
|
+
const includeIndex = normalizedButtons.findIndex((label) => label.includes(hint));
|
|
5839
|
+
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
5840
|
+
}
|
|
5841
|
+
return { index: 0, label: labels[0] };
|
|
5842
|
+
}
|
|
5843
|
+
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
5844
|
+
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
5845
|
+
const cleanMessage = String(modalMessage || "").trim();
|
|
5846
|
+
if (cleanMessage) lines.push(cleanMessage);
|
|
5847
|
+
return lines.join("\n");
|
|
5848
|
+
}
|
|
5849
|
+
|
|
5850
|
+
// src/providers/ide-provider-instance.ts
|
|
5591
5851
|
var IdeProviderInstance = class {
|
|
5592
5852
|
type;
|
|
5593
5853
|
category = "ide";
|
|
@@ -5654,6 +5914,8 @@ var IdeProviderInstance = class {
|
|
|
5654
5914
|
}
|
|
5655
5915
|
getState() {
|
|
5656
5916
|
const cdp = this.context?.cdp;
|
|
5917
|
+
const autoApproveActive = (this.currentStatus === "waiting_approval" || this.cachedChat?.status === "waiting_approval") && this.canAutoApprove();
|
|
5918
|
+
const visibleStatus = autoApproveActive ? "generating" : this.currentStatus;
|
|
5657
5919
|
const extensionStates = [];
|
|
5658
5920
|
for (const ext of this.extensions.values()) {
|
|
5659
5921
|
extensionStates.push(ext.getState());
|
|
@@ -5662,13 +5924,13 @@ var IdeProviderInstance = class {
|
|
|
5662
5924
|
type: this.type,
|
|
5663
5925
|
name: this.provider.name,
|
|
5664
5926
|
category: "ide",
|
|
5665
|
-
status:
|
|
5927
|
+
status: visibleStatus,
|
|
5666
5928
|
activeChat: this.cachedChat ? {
|
|
5667
5929
|
id: this.cachedChat.id || "active_session",
|
|
5668
5930
|
title: this.cachedChat.title || this.type,
|
|
5669
|
-
status: this.cachedChat.status
|
|
5931
|
+
status: autoApproveActive && this.cachedChat.status === "waiting_approval" ? "generating" : this.cachedChat.status || visibleStatus,
|
|
5670
5932
|
messages: this.mergeConversationMessages(this.cachedChat.messages || []),
|
|
5671
|
-
activeModal: this.cachedChat.activeModal || null,
|
|
5933
|
+
activeModal: autoApproveActive ? null : this.cachedChat.activeModal || null,
|
|
5672
5934
|
inputContent: this.cachedChat.inputContent || ""
|
|
5673
5935
|
} : null,
|
|
5674
5936
|
workspace: this.workspace || null,
|
|
@@ -5887,7 +6149,9 @@ var IdeProviderInstance = class {
|
|
|
5887
6149
|
const chatStatus = chatData?.status;
|
|
5888
6150
|
if (!chatStatus) return;
|
|
5889
6151
|
const agentKey = `${this.type}:native`;
|
|
5890
|
-
const
|
|
6152
|
+
const rawAgentStatus = chatStatus === "streaming" || chatStatus === "generating" ? "generating" : chatStatus === "waiting_approval" ? "waiting_approval" : "idle";
|
|
6153
|
+
const autoApproveActive = rawAgentStatus === "waiting_approval" && this.canAutoApprove();
|
|
6154
|
+
const agentStatus = autoApproveActive ? "generating" : rawAgentStatus;
|
|
5891
6155
|
const lastMsg = Array.isArray(chatData?.messages) && chatData.messages.length > 0 ? chatData.messages[chatData.messages.length - 1] : null;
|
|
5892
6156
|
const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
|
|
5893
6157
|
this.currentStatus = agentStatus;
|
|
@@ -5919,7 +6183,7 @@ var IdeProviderInstance = class {
|
|
|
5919
6183
|
this.applyProviderResponse(chatData, {
|
|
5920
6184
|
phase: agentStatus === "idle" && (lastStatus === "generating" || lastStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
5921
6185
|
});
|
|
5922
|
-
if (
|
|
6186
|
+
if (rawAgentStatus === "waiting_approval" && autoApproveActive && !this.autoApproveBusy) {
|
|
5923
6187
|
this.autoApproveViaScript(chatData);
|
|
5924
6188
|
}
|
|
5925
6189
|
const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
|
|
@@ -6070,6 +6334,9 @@ ${effect.notification.body || ""}`.trim();
|
|
|
6070
6334
|
updateCdp(cdp) {
|
|
6071
6335
|
if (this.context) this.context.cdp = cdp;
|
|
6072
6336
|
}
|
|
6337
|
+
canAutoApprove() {
|
|
6338
|
+
return this.settings.autoApprove !== false && typeof this.provider.scripts?.resolveAction === "function" && !!this.context?.cdp?.isConnected;
|
|
6339
|
+
}
|
|
6073
6340
|
// ─── Auto-approve via CDP script ────────────────────
|
|
6074
6341
|
async autoApproveViaScript(_chatData) {
|
|
6075
6342
|
const cdp = this.context?.cdp;
|
|
@@ -6081,17 +6348,15 @@ ${effect.notification.body || ""}`.trim();
|
|
|
6081
6348
|
}
|
|
6082
6349
|
this.autoApproveBusy = true;
|
|
6083
6350
|
try {
|
|
6084
|
-
|
|
6085
|
-
const buttons = _chatData?.activeModal?.buttons || [];
|
|
6086
|
-
for (const b of buttons) {
|
|
6087
|
-
const lower = String(b).toLowerCase().replace(/[^\w]/g, "");
|
|
6088
|
-
if (/^(run|approve|accept|yes|allow|always|proceed|save)/.test(lower)) {
|
|
6089
|
-
targetButton = b;
|
|
6090
|
-
break;
|
|
6091
|
-
}
|
|
6092
|
-
}
|
|
6351
|
+
const { label: targetButton } = pickApprovalButton(_chatData?.activeModal?.buttons, this.provider);
|
|
6093
6352
|
const script = scriptFn({ action: "approve", button: targetButton, buttonText: targetButton });
|
|
6094
6353
|
if (!script) return;
|
|
6354
|
+
const now = Date.now();
|
|
6355
|
+
this.appendRuntimeSystemMessage(
|
|
6356
|
+
formatAutoApprovalMessage(_chatData?.activeModal?.message, targetButton),
|
|
6357
|
+
`auto_approval:${now}:${targetButton}`,
|
|
6358
|
+
now
|
|
6359
|
+
);
|
|
6095
6360
|
LOG.info("IdeInstance", `[IdeInstance:${this.type}] autoApprove: executing resolveAction for "${targetButton}"`);
|
|
6096
6361
|
let rawResult = await cdp.evaluate(script, 1e4);
|
|
6097
6362
|
if (typeof rawResult === "string") {
|
|
@@ -6113,12 +6378,6 @@ ${effect.notification.body || ""}`.trim();
|
|
|
6113
6378
|
LOG.warn("IdeInstance", `[IdeInstance:${this.type}] autoApprove: cdp.send() not available for coordinate click`);
|
|
6114
6379
|
}
|
|
6115
6380
|
}
|
|
6116
|
-
this.pushEvent({
|
|
6117
|
-
event: "agent:auto_approved",
|
|
6118
|
-
chatTitle: _chatData?.title || this.provider.name,
|
|
6119
|
-
timestamp: Date.now(),
|
|
6120
|
-
ideType: this.type
|
|
6121
|
-
});
|
|
6122
6381
|
} catch (e) {
|
|
6123
6382
|
LOG.warn("IdeInstance", `[IdeInstance:${this.type}] autoApprove error: ${e?.message}`);
|
|
6124
6383
|
} finally {
|
|
@@ -7020,6 +7279,46 @@ function didProviderConfirmSend(result) {
|
|
|
7020
7279
|
if (!parsed || typeof parsed !== "object") return false;
|
|
7021
7280
|
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
7022
7281
|
}
|
|
7282
|
+
async function readExtensionChatState(h) {
|
|
7283
|
+
try {
|
|
7284
|
+
const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
|
|
7285
|
+
if (!evalResult?.result) return null;
|
|
7286
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
7287
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
7288
|
+
} catch {
|
|
7289
|
+
return null;
|
|
7290
|
+
}
|
|
7291
|
+
}
|
|
7292
|
+
function getStateMessageCount(state) {
|
|
7293
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
7294
|
+
}
|
|
7295
|
+
function getStateLastSignature(state) {
|
|
7296
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
7297
|
+
const last = messages[messages.length - 1];
|
|
7298
|
+
if (!last) return "";
|
|
7299
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
7300
|
+
}
|
|
7301
|
+
async function getStableExtensionBaseline(h) {
|
|
7302
|
+
const first = await readExtensionChatState(h);
|
|
7303
|
+
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
7304
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
7305
|
+
const second = await readExtensionChatState(h);
|
|
7306
|
+
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
7307
|
+
}
|
|
7308
|
+
async function verifyExtensionSendObserved(h, before) {
|
|
7309
|
+
const beforeCount = getStateMessageCount(before);
|
|
7310
|
+
const beforeSignature = getStateLastSignature(before);
|
|
7311
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
7312
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
7313
|
+
const state = await readExtensionChatState(h);
|
|
7314
|
+
if (state?.status === "waiting_approval") return true;
|
|
7315
|
+
const afterCount = getStateMessageCount(state);
|
|
7316
|
+
const afterSignature = getStateLastSignature(state);
|
|
7317
|
+
if (afterCount > beforeCount) return true;
|
|
7318
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
7319
|
+
}
|
|
7320
|
+
return false;
|
|
7321
|
+
}
|
|
7023
7322
|
async function handleChatHistory(h, args) {
|
|
7024
7323
|
const { agentType, offset, limit } = args;
|
|
7025
7324
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -7200,12 +7499,17 @@ async function handleSendChat(h, args) {
|
|
|
7200
7499
|
if (isExtensionTransport(transport)) {
|
|
7201
7500
|
_log(`Extension: ${provider?.type || "unknown_extension"}`);
|
|
7202
7501
|
try {
|
|
7502
|
+
const beforeState = await getStableExtensionBaseline(h);
|
|
7203
7503
|
const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
|
|
7204
7504
|
if (evalResult?.result) {
|
|
7205
7505
|
const parsed = parseMaybeJson(evalResult.result);
|
|
7206
7506
|
if (didProviderConfirmSend(parsed)) {
|
|
7207
|
-
|
|
7208
|
-
|
|
7507
|
+
const observed = await verifyExtensionSendObserved(h, beforeState);
|
|
7508
|
+
if (observed) {
|
|
7509
|
+
_log(`Extension script sent OK`);
|
|
7510
|
+
return _logSendSuccess("extension-script");
|
|
7511
|
+
}
|
|
7512
|
+
_log(`Extension script reported send but no chat-state change was observed`);
|
|
7209
7513
|
}
|
|
7210
7514
|
if (parsed?.needsTypeAndSend) {
|
|
7211
7515
|
_log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
|
|
@@ -7710,7 +8014,7 @@ async function handleResolveAction(h, args) {
|
|
|
7710
8014
|
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
7711
8015
|
}
|
|
7712
8016
|
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
7713
|
-
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
|
|
8017
|
+
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
|
|
7714
8018
|
return { success: ok };
|
|
7715
8019
|
}
|
|
7716
8020
|
if (transport === "acp") {
|
|
@@ -9117,6 +9421,7 @@ var CliProviderInstance = class {
|
|
|
9117
9421
|
historyWriter;
|
|
9118
9422
|
runtimeMessages = [];
|
|
9119
9423
|
instanceId;
|
|
9424
|
+
suppressIdleHistoryReplay = false;
|
|
9120
9425
|
presentationMode;
|
|
9121
9426
|
providerSessionId;
|
|
9122
9427
|
launchMode;
|
|
@@ -9144,7 +9449,15 @@ var CliProviderInstance = class {
|
|
|
9144
9449
|
await this.adapter.spawn();
|
|
9145
9450
|
this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
|
|
9146
9451
|
if (this.providerSessionId) {
|
|
9452
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
9147
9453
|
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
9454
|
+
this.historyWriter.seedSessionHistory(
|
|
9455
|
+
this.type,
|
|
9456
|
+
restoredHistory.messages,
|
|
9457
|
+
this.providerSessionId,
|
|
9458
|
+
this.instanceId
|
|
9459
|
+
);
|
|
9460
|
+
this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
|
|
9148
9461
|
if (restoredHistory.messages.length > 0) {
|
|
9149
9462
|
this.adapter.seedCommittedMessages(
|
|
9150
9463
|
restoredHistory.messages.map((message) => ({
|
|
@@ -9188,7 +9501,7 @@ var CliProviderInstance = class {
|
|
|
9188
9501
|
} else if (this.type === "codex-cli") {
|
|
9189
9502
|
probedSessionId = this.probeSessionIdFromConfig({
|
|
9190
9503
|
dbPath: "~/.codex/state_5.sqlite",
|
|
9191
|
-
query: "select id from threads where cwd in ({dirs}) and
|
|
9504
|
+
query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
|
|
9192
9505
|
timestampFormat: "unix_s"
|
|
9193
9506
|
});
|
|
9194
9507
|
} else if (this.type === "goose-cli") {
|
|
@@ -9232,6 +9545,8 @@ var CliProviderInstance = class {
|
|
|
9232
9545
|
getState() {
|
|
9233
9546
|
const adapterStatus = this.adapter.getStatus();
|
|
9234
9547
|
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
9548
|
+
const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
|
|
9549
|
+
const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
|
|
9235
9550
|
const parsedProviderSessionId = typeof parsedStatus?.providerSessionId === "string" ? parsedStatus.providerSessionId.trim() : "";
|
|
9236
9551
|
if (parsedProviderSessionId) {
|
|
9237
9552
|
this.promoteProviderSessionId(parsedProviderSessionId);
|
|
@@ -9248,6 +9563,7 @@ var CliProviderInstance = class {
|
|
|
9248
9563
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
9249
9564
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9250
9565
|
if (parsedMessages.length > 0) {
|
|
9566
|
+
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
9251
9567
|
let messagesToSave = parsedMessages;
|
|
9252
9568
|
if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
|
|
9253
9569
|
const lastIdx = messagesToSave.length - 1;
|
|
@@ -9255,7 +9571,7 @@ var CliProviderInstance = class {
|
|
|
9255
9571
|
messagesToSave = messagesToSave.slice(0, lastIdx);
|
|
9256
9572
|
}
|
|
9257
9573
|
}
|
|
9258
|
-
if (messagesToSave.length > 0) {
|
|
9574
|
+
if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
|
|
9259
9575
|
this.historyWriter.appendNewMessages(
|
|
9260
9576
|
this.type,
|
|
9261
9577
|
messagesToSave,
|
|
@@ -9270,14 +9586,14 @@ var CliProviderInstance = class {
|
|
|
9270
9586
|
type: this.type,
|
|
9271
9587
|
name: this.provider.name,
|
|
9272
9588
|
category: "cli",
|
|
9273
|
-
status:
|
|
9589
|
+
status: visibleStatus,
|
|
9274
9590
|
mode: this.presentationMode,
|
|
9275
9591
|
activeChat: {
|
|
9276
9592
|
id: `${this.type}_${this.workingDir}`,
|
|
9277
9593
|
title: parsedStatus?.title || dirName,
|
|
9278
|
-
status: parsedStatus?.status ||
|
|
9594
|
+
status: autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
|
|
9279
9595
|
messages: mergedMessages,
|
|
9280
|
-
activeModal: parsedStatus?.activeModal ?? adapterStatus.activeModal,
|
|
9596
|
+
activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
|
|
9281
9597
|
inputContent: ""
|
|
9282
9598
|
},
|
|
9283
9599
|
workspace: this.workingDir,
|
|
@@ -9341,7 +9657,16 @@ var CliProviderInstance = class {
|
|
|
9341
9657
|
const now = Date.now();
|
|
9342
9658
|
const adapterStatus = this.adapter.getStatus();
|
|
9343
9659
|
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
9344
|
-
const
|
|
9660
|
+
const rawStatus = adapterStatus.status;
|
|
9661
|
+
const autoApproveActive = rawStatus === "waiting_approval" && this.shouldAutoApprove();
|
|
9662
|
+
if (autoApproveActive) {
|
|
9663
|
+
const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(adapterStatus.activeModal?.buttons, this.provider);
|
|
9664
|
+
this.recordAutoApproval(adapterStatus.activeModal?.message, buttonLabel, now);
|
|
9665
|
+
setTimeout(() => {
|
|
9666
|
+
this.adapter.resolveModal(buttonIndex);
|
|
9667
|
+
}, 0);
|
|
9668
|
+
}
|
|
9669
|
+
const newStatus = autoApproveActive ? "generating" : rawStatus;
|
|
9345
9670
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9346
9671
|
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
9347
9672
|
const partial = this.adapter.getPartialResponse();
|
|
@@ -9350,6 +9675,7 @@ var CliProviderInstance = class {
|
|
|
9350
9675
|
if (newStatus !== this.lastStatus) {
|
|
9351
9676
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
9352
9677
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
9678
|
+
this.suppressIdleHistoryReplay = false;
|
|
9353
9679
|
if (this.completedDebouncePending) {
|
|
9354
9680
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
|
|
9355
9681
|
if (this.completedDebounceTimer) {
|
|
@@ -9369,6 +9695,7 @@ var CliProviderInstance = class {
|
|
|
9369
9695
|
this.generatingDebounceTimer = null;
|
|
9370
9696
|
}, 1e3);
|
|
9371
9697
|
} else if (newStatus === "waiting_approval") {
|
|
9698
|
+
this.suppressIdleHistoryReplay = false;
|
|
9372
9699
|
if (this.generatingDebouncePending) {
|
|
9373
9700
|
if (this.generatingDebounceTimer) {
|
|
9374
9701
|
clearTimeout(this.generatingDebounceTimer);
|
|
@@ -9549,6 +9876,16 @@ ${effect.notification.body || ""}`.trim();
|
|
|
9549
9876
|
get cliName() {
|
|
9550
9877
|
return this.provider.name;
|
|
9551
9878
|
}
|
|
9879
|
+
shouldAutoApprove() {
|
|
9880
|
+
return this.settings.autoApprove !== false;
|
|
9881
|
+
}
|
|
9882
|
+
recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
|
|
9883
|
+
this.appendRuntimeSystemMessage(
|
|
9884
|
+
formatAutoApprovalMessage(modalMessage, buttonLabel),
|
|
9885
|
+
`auto_approval:${now}:${buttonLabel || "approve"}`,
|
|
9886
|
+
now
|
|
9887
|
+
);
|
|
9888
|
+
}
|
|
9552
9889
|
recordApprovalSelection(buttonText) {
|
|
9553
9890
|
const cleanButton = String(buttonText || "").trim();
|
|
9554
9891
|
if (!cleanButton) return;
|
|
@@ -10122,8 +10459,10 @@ var AcpProviderInstance = class {
|
|
|
10122
10459
|
input: tc.rawInput ? typeof tc.rawInput === "string" ? tc.rawInput : JSON.stringify(tc.rawInput) : void 0
|
|
10123
10460
|
});
|
|
10124
10461
|
}
|
|
10125
|
-
if (this.settings.autoApprove) {
|
|
10126
|
-
|
|
10462
|
+
if (this.settings.autoApprove !== false) {
|
|
10463
|
+
const toolTitle = tc.title || tc.toolCallId || "tool call";
|
|
10464
|
+
this.log.info(`[${this.type}] Auto-approving: ${toolTitle}`);
|
|
10465
|
+
this.appendSystemMessage(`Auto-approved: ${toolTitle}`);
|
|
10127
10466
|
const allowOption = params.options.find((o) => o.kind === "allow_once") || params.options.find((o) => o.kind === "allow_always");
|
|
10128
10467
|
if (allowOption) {
|
|
10129
10468
|
return { outcome: { outcome: "selected", optionId: allowOption.optionId } };
|
|
@@ -10575,6 +10914,18 @@ var AcpProviderInstance = class {
|
|
|
10575
10914
|
this.events.push(event);
|
|
10576
10915
|
if (this.events.length > 50) this.events = this.events.slice(-50);
|
|
10577
10916
|
}
|
|
10917
|
+
appendSystemMessage(content, timestamp = Date.now()) {
|
|
10918
|
+
const normalizedContent = String(content || "").trim();
|
|
10919
|
+
if (!normalizedContent) return;
|
|
10920
|
+
this.messages.push({
|
|
10921
|
+
role: "system",
|
|
10922
|
+
content: normalizedContent,
|
|
10923
|
+
timestamp
|
|
10924
|
+
});
|
|
10925
|
+
if (this.messages.length > 200) {
|
|
10926
|
+
this.messages = this.messages.slice(-100);
|
|
10927
|
+
}
|
|
10928
|
+
}
|
|
10578
10929
|
flushEvents() {
|
|
10579
10930
|
const events = [...this.events];
|
|
10580
10931
|
this.events = [];
|
|
@@ -11081,6 +11432,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11081
11432
|
if (!instanceManager) return 0;
|
|
11082
11433
|
const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
|
|
11083
11434
|
let restored = 0;
|
|
11435
|
+
const restoredBindings = /* @__PURE__ */ new Set();
|
|
11084
11436
|
for (const record of sessions) {
|
|
11085
11437
|
if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
|
|
11086
11438
|
if (this.adapters.has(record.runtimeId) || instanceManager.getInstance(record.runtimeId)) continue;
|
|
@@ -11094,6 +11446,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11094
11446
|
record.cliArgs,
|
|
11095
11447
|
record.providerSessionId
|
|
11096
11448
|
);
|
|
11449
|
+
const bindingKey = [
|
|
11450
|
+
normalizedType,
|
|
11451
|
+
record.workspace,
|
|
11452
|
+
sessionBinding.providerSessionId || record.runtimeId
|
|
11453
|
+
].join("::");
|
|
11454
|
+
if (restoredBindings.has(bindingKey)) {
|
|
11455
|
+
LOG.info(
|
|
11456
|
+
"CLI",
|
|
11457
|
+
`\u21B7 Skipping duplicate hosted runtime restore: ${record.runtimeKey || record.runtimeId} (${normalizedType} @ ${record.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
|
|
11458
|
+
);
|
|
11459
|
+
continue;
|
|
11460
|
+
}
|
|
11097
11461
|
try {
|
|
11098
11462
|
await this.registerCliInstance(
|
|
11099
11463
|
record.runtimeId,
|
|
@@ -11109,6 +11473,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11109
11473
|
launchMode: "manual"
|
|
11110
11474
|
}
|
|
11111
11475
|
);
|
|
11476
|
+
restoredBindings.add(bindingKey);
|
|
11112
11477
|
restored += 1;
|
|
11113
11478
|
LOG.info("CLI", `\u267B Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
|
|
11114
11479
|
} catch (error) {
|
|
@@ -12135,7 +12500,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12135
12500
|
*/
|
|
12136
12501
|
getSettingValue(type, key) {
|
|
12137
12502
|
const schemaDef = this.getSettingsSchema(type)[key];
|
|
12138
|
-
const defaultVal = schemaDef ? schemaDef.default : void 0;
|
|
12503
|
+
const defaultVal = schemaDef ? key === "autoApprove" && schemaDef.type === "boolean" ? true : schemaDef.default : void 0;
|
|
12139
12504
|
try {
|
|
12140
12505
|
const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
12141
12506
|
const config = loadConfig2();
|
|
@@ -12194,13 +12559,32 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12194
12559
|
getSettingsSchema(type) {
|
|
12195
12560
|
const provider = this.providers.get(type);
|
|
12196
12561
|
if (!provider) return {};
|
|
12197
|
-
|
|
12562
|
+
const result = {
|
|
12198
12563
|
...this.getSyntheticSettings(type, provider),
|
|
12199
12564
|
...provider.settings || {}
|
|
12200
12565
|
};
|
|
12566
|
+
if (result.autoApprove?.type === "boolean") {
|
|
12567
|
+
result.autoApprove = {
|
|
12568
|
+
...result.autoApprove,
|
|
12569
|
+
default: true,
|
|
12570
|
+
public: true,
|
|
12571
|
+
label: result.autoApprove.label || "Auto Approve",
|
|
12572
|
+
description: result.autoApprove.description || "Automatically approve actionable prompts without sending approval alerts."
|
|
12573
|
+
};
|
|
12574
|
+
}
|
|
12575
|
+
return result;
|
|
12201
12576
|
}
|
|
12202
12577
|
getSyntheticSettings(type, provider) {
|
|
12203
12578
|
const result = {};
|
|
12579
|
+
if (!provider.settings?.autoApprove) {
|
|
12580
|
+
result.autoApprove = {
|
|
12581
|
+
type: "boolean",
|
|
12582
|
+
default: true,
|
|
12583
|
+
public: true,
|
|
12584
|
+
label: "Auto Approve",
|
|
12585
|
+
description: "Automatically approve actionable prompts without sending approval alerts."
|
|
12586
|
+
};
|
|
12587
|
+
}
|
|
12204
12588
|
if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
|
|
12205
12589
|
result.executablePath = {
|
|
12206
12590
|
type: "string",
|
|
@@ -13460,6 +13844,15 @@ var DaemonCommandRouter = class {
|
|
|
13460
13844
|
const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
|
|
13461
13845
|
return { success: true, record };
|
|
13462
13846
|
}
|
|
13847
|
+
case "session_host_prune_duplicate_sessions": {
|
|
13848
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13849
|
+
const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
13850
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
13851
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
13852
|
+
dryRun: args?.dryRun === true
|
|
13853
|
+
});
|
|
13854
|
+
return { success: true, result };
|
|
13855
|
+
}
|
|
13463
13856
|
case "session_host_acquire_write": {
|
|
13464
13857
|
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13465
13858
|
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
@@ -14045,6 +14438,51 @@ var ProviderStreamAdapter = class {
|
|
|
14045
14438
|
isTransportError(reason) {
|
|
14046
14439
|
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
14440
|
}
|
|
14441
|
+
titlesMatch(actual, expected) {
|
|
14442
|
+
const lhs = actual.trim().toLowerCase();
|
|
14443
|
+
const rhs = expected.trim().toLowerCase();
|
|
14444
|
+
if (!lhs || !rhs) return false;
|
|
14445
|
+
return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
|
|
14446
|
+
}
|
|
14447
|
+
messageCount(state) {
|
|
14448
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
14449
|
+
}
|
|
14450
|
+
lastMessageSignature(state) {
|
|
14451
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
14452
|
+
const last = messages[messages.length - 1];
|
|
14453
|
+
if (!last) return "";
|
|
14454
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
14455
|
+
}
|
|
14456
|
+
async verifySendOutcome(evaluate, before) {
|
|
14457
|
+
const beforeCount = this.messageCount(before);
|
|
14458
|
+
const beforeSignature = this.lastMessageSignature(before);
|
|
14459
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
14460
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14461
|
+
let state;
|
|
14462
|
+
try {
|
|
14463
|
+
state = await this.readChat(evaluate);
|
|
14464
|
+
} catch {
|
|
14465
|
+
continue;
|
|
14466
|
+
}
|
|
14467
|
+
if (state.status === "waiting_approval") {
|
|
14468
|
+
return true;
|
|
14469
|
+
}
|
|
14470
|
+
const afterCount = this.messageCount(state);
|
|
14471
|
+
const afterSignature = this.lastMessageSignature(state);
|
|
14472
|
+
if (afterCount > beforeCount) return true;
|
|
14473
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
14474
|
+
}
|
|
14475
|
+
return false;
|
|
14476
|
+
}
|
|
14477
|
+
async readStableBaselineState(evaluate) {
|
|
14478
|
+
const first = await this.readChat(evaluate);
|
|
14479
|
+
if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
|
|
14480
|
+
return first;
|
|
14481
|
+
}
|
|
14482
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
14483
|
+
const second = await this.readChat(evaluate);
|
|
14484
|
+
return this.messageCount(second) >= this.messageCount(first) ? second : first;
|
|
14485
|
+
}
|
|
14048
14486
|
async readChat(evaluate) {
|
|
14049
14487
|
const script = this.callScript("readChat");
|
|
14050
14488
|
if (!script) return this.errorState("readChat script not available");
|
|
@@ -14070,6 +14508,9 @@ var ProviderStreamAdapter = class {
|
|
|
14070
14508
|
mode: data.mode,
|
|
14071
14509
|
activeModal: data.activeModal
|
|
14072
14510
|
};
|
|
14511
|
+
if (typeof data.title === "string" && data.title.trim()) {
|
|
14512
|
+
state.title = data.title.trim();
|
|
14513
|
+
}
|
|
14073
14514
|
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
14074
14515
|
if (controlValues) state.controlValues = controlValues;
|
|
14075
14516
|
const effects = normalizeProviderEffects(data);
|
|
@@ -14093,6 +14534,12 @@ var ProviderStreamAdapter = class {
|
|
|
14093
14534
|
}
|
|
14094
14535
|
}
|
|
14095
14536
|
async sendMessage(evaluate, text) {
|
|
14537
|
+
let beforeState = null;
|
|
14538
|
+
try {
|
|
14539
|
+
beforeState = await this.readStableBaselineState(evaluate);
|
|
14540
|
+
} catch {
|
|
14541
|
+
beforeState = null;
|
|
14542
|
+
}
|
|
14096
14543
|
const params = { message: text };
|
|
14097
14544
|
const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
|
|
14098
14545
|
if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
|
|
@@ -14110,7 +14557,9 @@ var ProviderStreamAdapter = class {
|
|
|
14110
14557
|
}
|
|
14111
14558
|
if (parsed && typeof parsed === "object") {
|
|
14112
14559
|
if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
|
|
14113
|
-
|
|
14560
|
+
const verified = await this.verifySendOutcome(evaluate, beforeState);
|
|
14561
|
+
if (verified) return;
|
|
14562
|
+
throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
|
|
14114
14563
|
}
|
|
14115
14564
|
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
14116
14565
|
throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
|
|
@@ -14121,7 +14570,15 @@ var ProviderStreamAdapter = class {
|
|
|
14121
14570
|
async resolveAction(evaluate, action, button) {
|
|
14122
14571
|
const script = this.callScript("resolveAction", { action, button });
|
|
14123
14572
|
if (!script) return false;
|
|
14124
|
-
|
|
14573
|
+
const result = await evaluate(script);
|
|
14574
|
+
const parsed = this.parseMaybeJson(result);
|
|
14575
|
+
if (parsed === true) return true;
|
|
14576
|
+
if (typeof parsed === "string") {
|
|
14577
|
+
const normalized = parsed.trim().toLowerCase();
|
|
14578
|
+
return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
|
|
14579
|
+
}
|
|
14580
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
14581
|
+
return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
|
|
14125
14582
|
}
|
|
14126
14583
|
async newSession(evaluate) {
|
|
14127
14584
|
const script = this.callScript("newSession");
|
|
@@ -14158,7 +14615,14 @@ var ProviderStreamAdapter = class {
|
|
|
14158
14615
|
return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
|
|
14159
14616
|
}
|
|
14160
14617
|
if (data && typeof data === "object") {
|
|
14161
|
-
|
|
14618
|
+
if (data.switched === true || data.success === true || data.ok === true) return true;
|
|
14619
|
+
if (typeof data.error === "string" && data.error.trim()) return false;
|
|
14620
|
+
}
|
|
14621
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
14622
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14623
|
+
const state = await this.readChat(evaluate);
|
|
14624
|
+
const title = typeof state.title === "string" ? state.title : "";
|
|
14625
|
+
if (this.titlesMatch(title, sessionId)) return true;
|
|
14162
14626
|
}
|
|
14163
14627
|
return false;
|
|
14164
14628
|
}
|
|
@@ -14366,7 +14830,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14366
14830
|
return false;
|
|
14367
14831
|
}
|
|
14368
14832
|
}
|
|
14369
|
-
async resolveSessionAction(cdp, sessionId, action) {
|
|
14833
|
+
async resolveSessionAction(cdp, sessionId, action, button) {
|
|
14370
14834
|
await this.ensureSessionPanelOpen(sessionId);
|
|
14371
14835
|
const target = this.getSessionTarget(sessionId);
|
|
14372
14836
|
if (!target?.parentSessionId) return false;
|
|
@@ -14376,7 +14840,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14376
14840
|
if (!agent) return false;
|
|
14377
14841
|
try {
|
|
14378
14842
|
const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
14379
|
-
return await agent.adapter.resolveAction(evaluate, action);
|
|
14843
|
+
return await agent.adapter.resolveAction(evaluate, action, button);
|
|
14380
14844
|
} catch (e) {
|
|
14381
14845
|
this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
|
|
14382
14846
|
return false;
|
|
@@ -14606,7 +15070,43 @@ var AgentStreamPoller = class {
|
|
|
14606
15070
|
}
|
|
14607
15071
|
try {
|
|
14608
15072
|
await agentStreamManager.syncActiveSession(cdp, parentSessionId);
|
|
14609
|
-
|
|
15073
|
+
let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
|
|
15074
|
+
if (stream?.status === "waiting_approval") {
|
|
15075
|
+
const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
|
|
15076
|
+
if (autoApprove && resolvedActiveSessionId) {
|
|
15077
|
+
const provider = providerLoader.getMeta(stream.agentType);
|
|
15078
|
+
const { label: buttonLabel } = pickApprovalButton(stream.activeModal?.buttons, provider);
|
|
15079
|
+
const approved = await agentStreamManager.resolveSessionAction(cdp, resolvedActiveSessionId, "approve", buttonLabel);
|
|
15080
|
+
if (approved) {
|
|
15081
|
+
const effectId = [
|
|
15082
|
+
"auto_approval",
|
|
15083
|
+
resolvedActiveSessionId,
|
|
15084
|
+
String(stream.messages?.length || 0),
|
|
15085
|
+
buttonLabel,
|
|
15086
|
+
String(stream.activeModal?.message || "").trim()
|
|
15087
|
+
].join(":");
|
|
15088
|
+
stream = {
|
|
15089
|
+
...stream,
|
|
15090
|
+
status: "streaming",
|
|
15091
|
+
activeModal: void 0,
|
|
15092
|
+
effects: [
|
|
15093
|
+
...stream.effects || [],
|
|
15094
|
+
{
|
|
15095
|
+
type: "message",
|
|
15096
|
+
id: effectId,
|
|
15097
|
+
persist: true,
|
|
15098
|
+
message: {
|
|
15099
|
+
role: "system",
|
|
15100
|
+
senderName: "System",
|
|
15101
|
+
kind: "system",
|
|
15102
|
+
content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
|
|
15103
|
+
}
|
|
15104
|
+
}
|
|
15105
|
+
]
|
|
15106
|
+
};
|
|
15107
|
+
}
|
|
15108
|
+
}
|
|
15109
|
+
}
|
|
14610
15110
|
this.deps.onStreamsUpdated?.(ideType, stream ? [stream] : []);
|
|
14611
15111
|
} catch {
|
|
14612
15112
|
}
|