@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.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 };
|
|
@@ -5503,6 +5714,55 @@ ${effect.notification.body || ""}`.trim();
|
|
|
5503
5714
|
|
|
5504
5715
|
// src/providers/ide-provider-instance.ts
|
|
5505
5716
|
init_logger();
|
|
5717
|
+
|
|
5718
|
+
// src/providers/approval-utils.ts
|
|
5719
|
+
var DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
5720
|
+
"run",
|
|
5721
|
+
"approve",
|
|
5722
|
+
"accept",
|
|
5723
|
+
"allow once",
|
|
5724
|
+
"always allow",
|
|
5725
|
+
"allow",
|
|
5726
|
+
"yes",
|
|
5727
|
+
"proceed",
|
|
5728
|
+
"continue",
|
|
5729
|
+
"confirm",
|
|
5730
|
+
"save",
|
|
5731
|
+
"ok",
|
|
5732
|
+
"trust"
|
|
5733
|
+
];
|
|
5734
|
+
function normalizeApprovalLabel(value) {
|
|
5735
|
+
return String(value || "").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
5736
|
+
}
|
|
5737
|
+
function getApprovalPositiveHints(provider) {
|
|
5738
|
+
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
5739
|
+
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
5740
|
+
}
|
|
5741
|
+
function pickApprovalButton(buttons, provider) {
|
|
5742
|
+
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
5743
|
+
if (labels.length === 0) {
|
|
5744
|
+
return { index: 0, label: "Approve" };
|
|
5745
|
+
}
|
|
5746
|
+
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
5747
|
+
const hints = getApprovalPositiveHints(provider);
|
|
5748
|
+
for (const hint of hints) {
|
|
5749
|
+
const exactIndex = normalizedButtons.findIndex((label) => label === hint);
|
|
5750
|
+
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
5751
|
+
const prefixIndex = normalizedButtons.findIndex((label) => label.startsWith(hint));
|
|
5752
|
+
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
5753
|
+
const includeIndex = normalizedButtons.findIndex((label) => label.includes(hint));
|
|
5754
|
+
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
5755
|
+
}
|
|
5756
|
+
return { index: 0, label: labels[0] };
|
|
5757
|
+
}
|
|
5758
|
+
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
5759
|
+
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
5760
|
+
const cleanMessage = String(modalMessage || "").trim();
|
|
5761
|
+
if (cleanMessage) lines.push(cleanMessage);
|
|
5762
|
+
return lines.join("\n");
|
|
5763
|
+
}
|
|
5764
|
+
|
|
5765
|
+
// src/providers/ide-provider-instance.ts
|
|
5506
5766
|
var IdeProviderInstance = class {
|
|
5507
5767
|
type;
|
|
5508
5768
|
category = "ide";
|
|
@@ -5569,6 +5829,8 @@ var IdeProviderInstance = class {
|
|
|
5569
5829
|
}
|
|
5570
5830
|
getState() {
|
|
5571
5831
|
const cdp = this.context?.cdp;
|
|
5832
|
+
const autoApproveActive = (this.currentStatus === "waiting_approval" || this.cachedChat?.status === "waiting_approval") && this.canAutoApprove();
|
|
5833
|
+
const visibleStatus = autoApproveActive ? "generating" : this.currentStatus;
|
|
5572
5834
|
const extensionStates = [];
|
|
5573
5835
|
for (const ext of this.extensions.values()) {
|
|
5574
5836
|
extensionStates.push(ext.getState());
|
|
@@ -5577,13 +5839,13 @@ var IdeProviderInstance = class {
|
|
|
5577
5839
|
type: this.type,
|
|
5578
5840
|
name: this.provider.name,
|
|
5579
5841
|
category: "ide",
|
|
5580
|
-
status:
|
|
5842
|
+
status: visibleStatus,
|
|
5581
5843
|
activeChat: this.cachedChat ? {
|
|
5582
5844
|
id: this.cachedChat.id || "active_session",
|
|
5583
5845
|
title: this.cachedChat.title || this.type,
|
|
5584
|
-
status: this.cachedChat.status
|
|
5846
|
+
status: autoApproveActive && this.cachedChat.status === "waiting_approval" ? "generating" : this.cachedChat.status || visibleStatus,
|
|
5585
5847
|
messages: this.mergeConversationMessages(this.cachedChat.messages || []),
|
|
5586
|
-
activeModal: this.cachedChat.activeModal || null,
|
|
5848
|
+
activeModal: autoApproveActive ? null : this.cachedChat.activeModal || null,
|
|
5587
5849
|
inputContent: this.cachedChat.inputContent || ""
|
|
5588
5850
|
} : null,
|
|
5589
5851
|
workspace: this.workspace || null,
|
|
@@ -5802,7 +6064,9 @@ var IdeProviderInstance = class {
|
|
|
5802
6064
|
const chatStatus = chatData?.status;
|
|
5803
6065
|
if (!chatStatus) return;
|
|
5804
6066
|
const agentKey = `${this.type}:native`;
|
|
5805
|
-
const
|
|
6067
|
+
const rawAgentStatus = chatStatus === "streaming" || chatStatus === "generating" ? "generating" : chatStatus === "waiting_approval" ? "waiting_approval" : "idle";
|
|
6068
|
+
const autoApproveActive = rawAgentStatus === "waiting_approval" && this.canAutoApprove();
|
|
6069
|
+
const agentStatus = autoApproveActive ? "generating" : rawAgentStatus;
|
|
5806
6070
|
const lastMsg = Array.isArray(chatData?.messages) && chatData.messages.length > 0 ? chatData.messages[chatData.messages.length - 1] : null;
|
|
5807
6071
|
const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
|
|
5808
6072
|
this.currentStatus = agentStatus;
|
|
@@ -5834,7 +6098,7 @@ var IdeProviderInstance = class {
|
|
|
5834
6098
|
this.applyProviderResponse(chatData, {
|
|
5835
6099
|
phase: agentStatus === "idle" && (lastStatus === "generating" || lastStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
5836
6100
|
});
|
|
5837
|
-
if (
|
|
6101
|
+
if (rawAgentStatus === "waiting_approval" && autoApproveActive && !this.autoApproveBusy) {
|
|
5838
6102
|
this.autoApproveViaScript(chatData);
|
|
5839
6103
|
}
|
|
5840
6104
|
const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
|
|
@@ -5985,6 +6249,9 @@ ${effect.notification.body || ""}`.trim();
|
|
|
5985
6249
|
updateCdp(cdp) {
|
|
5986
6250
|
if (this.context) this.context.cdp = cdp;
|
|
5987
6251
|
}
|
|
6252
|
+
canAutoApprove() {
|
|
6253
|
+
return this.settings.autoApprove !== false && typeof this.provider.scripts?.resolveAction === "function" && !!this.context?.cdp?.isConnected;
|
|
6254
|
+
}
|
|
5988
6255
|
// ─── Auto-approve via CDP script ────────────────────
|
|
5989
6256
|
async autoApproveViaScript(_chatData) {
|
|
5990
6257
|
const cdp = this.context?.cdp;
|
|
@@ -5996,17 +6263,15 @@ ${effect.notification.body || ""}`.trim();
|
|
|
5996
6263
|
}
|
|
5997
6264
|
this.autoApproveBusy = true;
|
|
5998
6265
|
try {
|
|
5999
|
-
|
|
6000
|
-
const buttons = _chatData?.activeModal?.buttons || [];
|
|
6001
|
-
for (const b of buttons) {
|
|
6002
|
-
const lower = String(b).toLowerCase().replace(/[^\w]/g, "");
|
|
6003
|
-
if (/^(run|approve|accept|yes|allow|always|proceed|save)/.test(lower)) {
|
|
6004
|
-
targetButton = b;
|
|
6005
|
-
break;
|
|
6006
|
-
}
|
|
6007
|
-
}
|
|
6266
|
+
const { label: targetButton } = pickApprovalButton(_chatData?.activeModal?.buttons, this.provider);
|
|
6008
6267
|
const script = scriptFn({ action: "approve", button: targetButton, buttonText: targetButton });
|
|
6009
6268
|
if (!script) return;
|
|
6269
|
+
const now = Date.now();
|
|
6270
|
+
this.appendRuntimeSystemMessage(
|
|
6271
|
+
formatAutoApprovalMessage(_chatData?.activeModal?.message, targetButton),
|
|
6272
|
+
`auto_approval:${now}:${targetButton}`,
|
|
6273
|
+
now
|
|
6274
|
+
);
|
|
6010
6275
|
LOG.info("IdeInstance", `[IdeInstance:${this.type}] autoApprove: executing resolveAction for "${targetButton}"`);
|
|
6011
6276
|
let rawResult = await cdp.evaluate(script, 1e4);
|
|
6012
6277
|
if (typeof rawResult === "string") {
|
|
@@ -6028,12 +6293,6 @@ ${effect.notification.body || ""}`.trim();
|
|
|
6028
6293
|
LOG.warn("IdeInstance", `[IdeInstance:${this.type}] autoApprove: cdp.send() not available for coordinate click`);
|
|
6029
6294
|
}
|
|
6030
6295
|
}
|
|
6031
|
-
this.pushEvent({
|
|
6032
|
-
event: "agent:auto_approved",
|
|
6033
|
-
chatTitle: _chatData?.title || this.provider.name,
|
|
6034
|
-
timestamp: Date.now(),
|
|
6035
|
-
ideType: this.type
|
|
6036
|
-
});
|
|
6037
6296
|
} catch (e) {
|
|
6038
6297
|
LOG.warn("IdeInstance", `[IdeInstance:${this.type}] autoApprove error: ${e?.message}`);
|
|
6039
6298
|
} finally {
|
|
@@ -6935,6 +7194,46 @@ function didProviderConfirmSend(result) {
|
|
|
6935
7194
|
if (!parsed || typeof parsed !== "object") return false;
|
|
6936
7195
|
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
6937
7196
|
}
|
|
7197
|
+
async function readExtensionChatState(h) {
|
|
7198
|
+
try {
|
|
7199
|
+
const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
|
|
7200
|
+
if (!evalResult?.result) return null;
|
|
7201
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
7202
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
7203
|
+
} catch {
|
|
7204
|
+
return null;
|
|
7205
|
+
}
|
|
7206
|
+
}
|
|
7207
|
+
function getStateMessageCount(state) {
|
|
7208
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
7209
|
+
}
|
|
7210
|
+
function getStateLastSignature(state) {
|
|
7211
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
7212
|
+
const last = messages[messages.length - 1];
|
|
7213
|
+
if (!last) return "";
|
|
7214
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
7215
|
+
}
|
|
7216
|
+
async function getStableExtensionBaseline(h) {
|
|
7217
|
+
const first = await readExtensionChatState(h);
|
|
7218
|
+
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
7219
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
7220
|
+
const second = await readExtensionChatState(h);
|
|
7221
|
+
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
7222
|
+
}
|
|
7223
|
+
async function verifyExtensionSendObserved(h, before) {
|
|
7224
|
+
const beforeCount = getStateMessageCount(before);
|
|
7225
|
+
const beforeSignature = getStateLastSignature(before);
|
|
7226
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
7227
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
7228
|
+
const state = await readExtensionChatState(h);
|
|
7229
|
+
if (state?.status === "waiting_approval") return true;
|
|
7230
|
+
const afterCount = getStateMessageCount(state);
|
|
7231
|
+
const afterSignature = getStateLastSignature(state);
|
|
7232
|
+
if (afterCount > beforeCount) return true;
|
|
7233
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
7234
|
+
}
|
|
7235
|
+
return false;
|
|
7236
|
+
}
|
|
6938
7237
|
async function handleChatHistory(h, args) {
|
|
6939
7238
|
const { agentType, offset, limit } = args;
|
|
6940
7239
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -7115,12 +7414,17 @@ async function handleSendChat(h, args) {
|
|
|
7115
7414
|
if (isExtensionTransport(transport)) {
|
|
7116
7415
|
_log(`Extension: ${provider?.type || "unknown_extension"}`);
|
|
7117
7416
|
try {
|
|
7417
|
+
const beforeState = await getStableExtensionBaseline(h);
|
|
7118
7418
|
const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
|
|
7119
7419
|
if (evalResult?.result) {
|
|
7120
7420
|
const parsed = parseMaybeJson(evalResult.result);
|
|
7121
7421
|
if (didProviderConfirmSend(parsed)) {
|
|
7122
|
-
|
|
7123
|
-
|
|
7422
|
+
const observed = await verifyExtensionSendObserved(h, beforeState);
|
|
7423
|
+
if (observed) {
|
|
7424
|
+
_log(`Extension script sent OK`);
|
|
7425
|
+
return _logSendSuccess("extension-script");
|
|
7426
|
+
}
|
|
7427
|
+
_log(`Extension script reported send but no chat-state change was observed`);
|
|
7124
7428
|
}
|
|
7125
7429
|
if (parsed?.needsTypeAndSend) {
|
|
7126
7430
|
_log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
|
|
@@ -7625,7 +7929,7 @@ async function handleResolveAction(h, args) {
|
|
|
7625
7929
|
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
7626
7930
|
}
|
|
7627
7931
|
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
7628
|
-
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
|
|
7932
|
+
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
|
|
7629
7933
|
return { success: ok };
|
|
7630
7934
|
}
|
|
7631
7935
|
if (transport === "acp") {
|
|
@@ -9032,6 +9336,7 @@ var CliProviderInstance = class {
|
|
|
9032
9336
|
historyWriter;
|
|
9033
9337
|
runtimeMessages = [];
|
|
9034
9338
|
instanceId;
|
|
9339
|
+
suppressIdleHistoryReplay = false;
|
|
9035
9340
|
presentationMode;
|
|
9036
9341
|
providerSessionId;
|
|
9037
9342
|
launchMode;
|
|
@@ -9059,7 +9364,15 @@ var CliProviderInstance = class {
|
|
|
9059
9364
|
await this.adapter.spawn();
|
|
9060
9365
|
this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
|
|
9061
9366
|
if (this.providerSessionId) {
|
|
9367
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
9062
9368
|
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
9369
|
+
this.historyWriter.seedSessionHistory(
|
|
9370
|
+
this.type,
|
|
9371
|
+
restoredHistory.messages,
|
|
9372
|
+
this.providerSessionId,
|
|
9373
|
+
this.instanceId
|
|
9374
|
+
);
|
|
9375
|
+
this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
|
|
9063
9376
|
if (restoredHistory.messages.length > 0) {
|
|
9064
9377
|
this.adapter.seedCommittedMessages(
|
|
9065
9378
|
restoredHistory.messages.map((message) => ({
|
|
@@ -9103,7 +9416,7 @@ var CliProviderInstance = class {
|
|
|
9103
9416
|
} else if (this.type === "codex-cli") {
|
|
9104
9417
|
probedSessionId = this.probeSessionIdFromConfig({
|
|
9105
9418
|
dbPath: "~/.codex/state_5.sqlite",
|
|
9106
|
-
query: "select id from threads where cwd in ({dirs}) and
|
|
9419
|
+
query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
|
|
9107
9420
|
timestampFormat: "unix_s"
|
|
9108
9421
|
});
|
|
9109
9422
|
} else if (this.type === "goose-cli") {
|
|
@@ -9147,6 +9460,8 @@ var CliProviderInstance = class {
|
|
|
9147
9460
|
getState() {
|
|
9148
9461
|
const adapterStatus = this.adapter.getStatus();
|
|
9149
9462
|
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
9463
|
+
const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
|
|
9464
|
+
const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
|
|
9150
9465
|
const parsedProviderSessionId = typeof parsedStatus?.providerSessionId === "string" ? parsedStatus.providerSessionId.trim() : "";
|
|
9151
9466
|
if (parsedProviderSessionId) {
|
|
9152
9467
|
this.promoteProviderSessionId(parsedProviderSessionId);
|
|
@@ -9163,6 +9478,7 @@ var CliProviderInstance = class {
|
|
|
9163
9478
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
9164
9479
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9165
9480
|
if (parsedMessages.length > 0) {
|
|
9481
|
+
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
9166
9482
|
let messagesToSave = parsedMessages;
|
|
9167
9483
|
if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
|
|
9168
9484
|
const lastIdx = messagesToSave.length - 1;
|
|
@@ -9170,7 +9486,7 @@ var CliProviderInstance = class {
|
|
|
9170
9486
|
messagesToSave = messagesToSave.slice(0, lastIdx);
|
|
9171
9487
|
}
|
|
9172
9488
|
}
|
|
9173
|
-
if (messagesToSave.length > 0) {
|
|
9489
|
+
if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
|
|
9174
9490
|
this.historyWriter.appendNewMessages(
|
|
9175
9491
|
this.type,
|
|
9176
9492
|
messagesToSave,
|
|
@@ -9185,14 +9501,14 @@ var CliProviderInstance = class {
|
|
|
9185
9501
|
type: this.type,
|
|
9186
9502
|
name: this.provider.name,
|
|
9187
9503
|
category: "cli",
|
|
9188
|
-
status:
|
|
9504
|
+
status: visibleStatus,
|
|
9189
9505
|
mode: this.presentationMode,
|
|
9190
9506
|
activeChat: {
|
|
9191
9507
|
id: `${this.type}_${this.workingDir}`,
|
|
9192
9508
|
title: parsedStatus?.title || dirName,
|
|
9193
|
-
status: parsedStatus?.status ||
|
|
9509
|
+
status: autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
|
|
9194
9510
|
messages: mergedMessages,
|
|
9195
|
-
activeModal: parsedStatus?.activeModal ?? adapterStatus.activeModal,
|
|
9511
|
+
activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
|
|
9196
9512
|
inputContent: ""
|
|
9197
9513
|
},
|
|
9198
9514
|
workspace: this.workingDir,
|
|
@@ -9256,7 +9572,16 @@ var CliProviderInstance = class {
|
|
|
9256
9572
|
const now = Date.now();
|
|
9257
9573
|
const adapterStatus = this.adapter.getStatus();
|
|
9258
9574
|
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
9259
|
-
const
|
|
9575
|
+
const rawStatus = adapterStatus.status;
|
|
9576
|
+
const autoApproveActive = rawStatus === "waiting_approval" && this.shouldAutoApprove();
|
|
9577
|
+
if (autoApproveActive) {
|
|
9578
|
+
const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(adapterStatus.activeModal?.buttons, this.provider);
|
|
9579
|
+
this.recordAutoApproval(adapterStatus.activeModal?.message, buttonLabel, now);
|
|
9580
|
+
setTimeout(() => {
|
|
9581
|
+
this.adapter.resolveModal(buttonIndex);
|
|
9582
|
+
}, 0);
|
|
9583
|
+
}
|
|
9584
|
+
const newStatus = autoApproveActive ? "generating" : rawStatus;
|
|
9260
9585
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9261
9586
|
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
9262
9587
|
const partial = this.adapter.getPartialResponse();
|
|
@@ -9265,6 +9590,7 @@ var CliProviderInstance = class {
|
|
|
9265
9590
|
if (newStatus !== this.lastStatus) {
|
|
9266
9591
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
9267
9592
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
9593
|
+
this.suppressIdleHistoryReplay = false;
|
|
9268
9594
|
if (this.completedDebouncePending) {
|
|
9269
9595
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
|
|
9270
9596
|
if (this.completedDebounceTimer) {
|
|
@@ -9284,6 +9610,7 @@ var CliProviderInstance = class {
|
|
|
9284
9610
|
this.generatingDebounceTimer = null;
|
|
9285
9611
|
}, 1e3);
|
|
9286
9612
|
} else if (newStatus === "waiting_approval") {
|
|
9613
|
+
this.suppressIdleHistoryReplay = false;
|
|
9287
9614
|
if (this.generatingDebouncePending) {
|
|
9288
9615
|
if (this.generatingDebounceTimer) {
|
|
9289
9616
|
clearTimeout(this.generatingDebounceTimer);
|
|
@@ -9464,6 +9791,16 @@ ${effect.notification.body || ""}`.trim();
|
|
|
9464
9791
|
get cliName() {
|
|
9465
9792
|
return this.provider.name;
|
|
9466
9793
|
}
|
|
9794
|
+
shouldAutoApprove() {
|
|
9795
|
+
return this.settings.autoApprove !== false;
|
|
9796
|
+
}
|
|
9797
|
+
recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
|
|
9798
|
+
this.appendRuntimeSystemMessage(
|
|
9799
|
+
formatAutoApprovalMessage(modalMessage, buttonLabel),
|
|
9800
|
+
`auto_approval:${now}:${buttonLabel || "approve"}`,
|
|
9801
|
+
now
|
|
9802
|
+
);
|
|
9803
|
+
}
|
|
9467
9804
|
recordApprovalSelection(buttonText) {
|
|
9468
9805
|
const cleanButton = String(buttonText || "").trim();
|
|
9469
9806
|
if (!cleanButton) return;
|
|
@@ -10042,8 +10379,10 @@ var AcpProviderInstance = class {
|
|
|
10042
10379
|
input: tc.rawInput ? typeof tc.rawInput === "string" ? tc.rawInput : JSON.stringify(tc.rawInput) : void 0
|
|
10043
10380
|
});
|
|
10044
10381
|
}
|
|
10045
|
-
if (this.settings.autoApprove) {
|
|
10046
|
-
|
|
10382
|
+
if (this.settings.autoApprove !== false) {
|
|
10383
|
+
const toolTitle = tc.title || tc.toolCallId || "tool call";
|
|
10384
|
+
this.log.info(`[${this.type}] Auto-approving: ${toolTitle}`);
|
|
10385
|
+
this.appendSystemMessage(`Auto-approved: ${toolTitle}`);
|
|
10047
10386
|
const allowOption = params.options.find((o) => o.kind === "allow_once") || params.options.find((o) => o.kind === "allow_always");
|
|
10048
10387
|
if (allowOption) {
|
|
10049
10388
|
return { outcome: { outcome: "selected", optionId: allowOption.optionId } };
|
|
@@ -10495,6 +10834,18 @@ var AcpProviderInstance = class {
|
|
|
10495
10834
|
this.events.push(event);
|
|
10496
10835
|
if (this.events.length > 50) this.events = this.events.slice(-50);
|
|
10497
10836
|
}
|
|
10837
|
+
appendSystemMessage(content, timestamp = Date.now()) {
|
|
10838
|
+
const normalizedContent = String(content || "").trim();
|
|
10839
|
+
if (!normalizedContent) return;
|
|
10840
|
+
this.messages.push({
|
|
10841
|
+
role: "system",
|
|
10842
|
+
content: normalizedContent,
|
|
10843
|
+
timestamp
|
|
10844
|
+
});
|
|
10845
|
+
if (this.messages.length > 200) {
|
|
10846
|
+
this.messages = this.messages.slice(-100);
|
|
10847
|
+
}
|
|
10848
|
+
}
|
|
10498
10849
|
flushEvents() {
|
|
10499
10850
|
const events = [...this.events];
|
|
10500
10851
|
this.events = [];
|
|
@@ -11001,6 +11352,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11001
11352
|
if (!instanceManager) return 0;
|
|
11002
11353
|
const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
|
|
11003
11354
|
let restored = 0;
|
|
11355
|
+
const restoredBindings = /* @__PURE__ */ new Set();
|
|
11004
11356
|
for (const record of sessions) {
|
|
11005
11357
|
if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
|
|
11006
11358
|
if (this.adapters.has(record.runtimeId) || instanceManager.getInstance(record.runtimeId)) continue;
|
|
@@ -11014,6 +11366,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11014
11366
|
record.cliArgs,
|
|
11015
11367
|
record.providerSessionId
|
|
11016
11368
|
);
|
|
11369
|
+
const bindingKey = [
|
|
11370
|
+
normalizedType,
|
|
11371
|
+
record.workspace,
|
|
11372
|
+
sessionBinding.providerSessionId || record.runtimeId
|
|
11373
|
+
].join("::");
|
|
11374
|
+
if (restoredBindings.has(bindingKey)) {
|
|
11375
|
+
LOG.info(
|
|
11376
|
+
"CLI",
|
|
11377
|
+
`\u21B7 Skipping duplicate hosted runtime restore: ${record.runtimeKey || record.runtimeId} (${normalizedType} @ ${record.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
|
|
11378
|
+
);
|
|
11379
|
+
continue;
|
|
11380
|
+
}
|
|
11017
11381
|
try {
|
|
11018
11382
|
await this.registerCliInstance(
|
|
11019
11383
|
record.runtimeId,
|
|
@@ -11029,6 +11393,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11029
11393
|
launchMode: "manual"
|
|
11030
11394
|
}
|
|
11031
11395
|
);
|
|
11396
|
+
restoredBindings.add(bindingKey);
|
|
11032
11397
|
restored += 1;
|
|
11033
11398
|
LOG.info("CLI", `\u267B Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
|
|
11034
11399
|
} catch (error) {
|
|
@@ -12055,7 +12420,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12055
12420
|
*/
|
|
12056
12421
|
getSettingValue(type, key) {
|
|
12057
12422
|
const schemaDef = this.getSettingsSchema(type)[key];
|
|
12058
|
-
const defaultVal = schemaDef ? schemaDef.default : void 0;
|
|
12423
|
+
const defaultVal = schemaDef ? key === "autoApprove" && schemaDef.type === "boolean" ? true : schemaDef.default : void 0;
|
|
12059
12424
|
try {
|
|
12060
12425
|
const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
12061
12426
|
const config = loadConfig2();
|
|
@@ -12114,13 +12479,32 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12114
12479
|
getSettingsSchema(type) {
|
|
12115
12480
|
const provider = this.providers.get(type);
|
|
12116
12481
|
if (!provider) return {};
|
|
12117
|
-
|
|
12482
|
+
const result = {
|
|
12118
12483
|
...this.getSyntheticSettings(type, provider),
|
|
12119
12484
|
...provider.settings || {}
|
|
12120
12485
|
};
|
|
12486
|
+
if (result.autoApprove?.type === "boolean") {
|
|
12487
|
+
result.autoApprove = {
|
|
12488
|
+
...result.autoApprove,
|
|
12489
|
+
default: true,
|
|
12490
|
+
public: true,
|
|
12491
|
+
label: result.autoApprove.label || "Auto Approve",
|
|
12492
|
+
description: result.autoApprove.description || "Automatically approve actionable prompts without sending approval alerts."
|
|
12493
|
+
};
|
|
12494
|
+
}
|
|
12495
|
+
return result;
|
|
12121
12496
|
}
|
|
12122
12497
|
getSyntheticSettings(type, provider) {
|
|
12123
12498
|
const result = {};
|
|
12499
|
+
if (!provider.settings?.autoApprove) {
|
|
12500
|
+
result.autoApprove = {
|
|
12501
|
+
type: "boolean",
|
|
12502
|
+
default: true,
|
|
12503
|
+
public: true,
|
|
12504
|
+
label: "Auto Approve",
|
|
12505
|
+
description: "Automatically approve actionable prompts without sending approval alerts."
|
|
12506
|
+
};
|
|
12507
|
+
}
|
|
12124
12508
|
if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
|
|
12125
12509
|
result.executablePath = {
|
|
12126
12510
|
type: "string",
|
|
@@ -13380,6 +13764,15 @@ var DaemonCommandRouter = class {
|
|
|
13380
13764
|
const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
|
|
13381
13765
|
return { success: true, record };
|
|
13382
13766
|
}
|
|
13767
|
+
case "session_host_prune_duplicate_sessions": {
|
|
13768
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13769
|
+
const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
13770
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
13771
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
13772
|
+
dryRun: args?.dryRun === true
|
|
13773
|
+
});
|
|
13774
|
+
return { success: true, result };
|
|
13775
|
+
}
|
|
13383
13776
|
case "session_host_acquire_write": {
|
|
13384
13777
|
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13385
13778
|
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
@@ -13965,6 +14358,51 @@ var ProviderStreamAdapter = class {
|
|
|
13965
14358
|
isTransportError(reason) {
|
|
13966
14359
|
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
14360
|
}
|
|
14361
|
+
titlesMatch(actual, expected) {
|
|
14362
|
+
const lhs = actual.trim().toLowerCase();
|
|
14363
|
+
const rhs = expected.trim().toLowerCase();
|
|
14364
|
+
if (!lhs || !rhs) return false;
|
|
14365
|
+
return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
|
|
14366
|
+
}
|
|
14367
|
+
messageCount(state) {
|
|
14368
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
14369
|
+
}
|
|
14370
|
+
lastMessageSignature(state) {
|
|
14371
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
14372
|
+
const last = messages[messages.length - 1];
|
|
14373
|
+
if (!last) return "";
|
|
14374
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
14375
|
+
}
|
|
14376
|
+
async verifySendOutcome(evaluate, before) {
|
|
14377
|
+
const beforeCount = this.messageCount(before);
|
|
14378
|
+
const beforeSignature = this.lastMessageSignature(before);
|
|
14379
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
14380
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14381
|
+
let state;
|
|
14382
|
+
try {
|
|
14383
|
+
state = await this.readChat(evaluate);
|
|
14384
|
+
} catch {
|
|
14385
|
+
continue;
|
|
14386
|
+
}
|
|
14387
|
+
if (state.status === "waiting_approval") {
|
|
14388
|
+
return true;
|
|
14389
|
+
}
|
|
14390
|
+
const afterCount = this.messageCount(state);
|
|
14391
|
+
const afterSignature = this.lastMessageSignature(state);
|
|
14392
|
+
if (afterCount > beforeCount) return true;
|
|
14393
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
14394
|
+
}
|
|
14395
|
+
return false;
|
|
14396
|
+
}
|
|
14397
|
+
async readStableBaselineState(evaluate) {
|
|
14398
|
+
const first = await this.readChat(evaluate);
|
|
14399
|
+
if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
|
|
14400
|
+
return first;
|
|
14401
|
+
}
|
|
14402
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
14403
|
+
const second = await this.readChat(evaluate);
|
|
14404
|
+
return this.messageCount(second) >= this.messageCount(first) ? second : first;
|
|
14405
|
+
}
|
|
13968
14406
|
async readChat(evaluate) {
|
|
13969
14407
|
const script = this.callScript("readChat");
|
|
13970
14408
|
if (!script) return this.errorState("readChat script not available");
|
|
@@ -13990,6 +14428,9 @@ var ProviderStreamAdapter = class {
|
|
|
13990
14428
|
mode: data.mode,
|
|
13991
14429
|
activeModal: data.activeModal
|
|
13992
14430
|
};
|
|
14431
|
+
if (typeof data.title === "string" && data.title.trim()) {
|
|
14432
|
+
state.title = data.title.trim();
|
|
14433
|
+
}
|
|
13993
14434
|
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
13994
14435
|
if (controlValues) state.controlValues = controlValues;
|
|
13995
14436
|
const effects = normalizeProviderEffects(data);
|
|
@@ -14013,6 +14454,12 @@ var ProviderStreamAdapter = class {
|
|
|
14013
14454
|
}
|
|
14014
14455
|
}
|
|
14015
14456
|
async sendMessage(evaluate, text) {
|
|
14457
|
+
let beforeState = null;
|
|
14458
|
+
try {
|
|
14459
|
+
beforeState = await this.readStableBaselineState(evaluate);
|
|
14460
|
+
} catch {
|
|
14461
|
+
beforeState = null;
|
|
14462
|
+
}
|
|
14016
14463
|
const params = { message: text };
|
|
14017
14464
|
const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
|
|
14018
14465
|
if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
|
|
@@ -14030,7 +14477,9 @@ var ProviderStreamAdapter = class {
|
|
|
14030
14477
|
}
|
|
14031
14478
|
if (parsed && typeof parsed === "object") {
|
|
14032
14479
|
if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
|
|
14033
|
-
|
|
14480
|
+
const verified = await this.verifySendOutcome(evaluate, beforeState);
|
|
14481
|
+
if (verified) return;
|
|
14482
|
+
throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
|
|
14034
14483
|
}
|
|
14035
14484
|
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
14036
14485
|
throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
|
|
@@ -14041,7 +14490,15 @@ var ProviderStreamAdapter = class {
|
|
|
14041
14490
|
async resolveAction(evaluate, action, button) {
|
|
14042
14491
|
const script = this.callScript("resolveAction", { action, button });
|
|
14043
14492
|
if (!script) return false;
|
|
14044
|
-
|
|
14493
|
+
const result = await evaluate(script);
|
|
14494
|
+
const parsed = this.parseMaybeJson(result);
|
|
14495
|
+
if (parsed === true) return true;
|
|
14496
|
+
if (typeof parsed === "string") {
|
|
14497
|
+
const normalized = parsed.trim().toLowerCase();
|
|
14498
|
+
return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
|
|
14499
|
+
}
|
|
14500
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
14501
|
+
return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
|
|
14045
14502
|
}
|
|
14046
14503
|
async newSession(evaluate) {
|
|
14047
14504
|
const script = this.callScript("newSession");
|
|
@@ -14078,7 +14535,14 @@ var ProviderStreamAdapter = class {
|
|
|
14078
14535
|
return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
|
|
14079
14536
|
}
|
|
14080
14537
|
if (data && typeof data === "object") {
|
|
14081
|
-
|
|
14538
|
+
if (data.switched === true || data.success === true || data.ok === true) return true;
|
|
14539
|
+
if (typeof data.error === "string" && data.error.trim()) return false;
|
|
14540
|
+
}
|
|
14541
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
14542
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14543
|
+
const state = await this.readChat(evaluate);
|
|
14544
|
+
const title = typeof state.title === "string" ? state.title : "";
|
|
14545
|
+
if (this.titlesMatch(title, sessionId)) return true;
|
|
14082
14546
|
}
|
|
14083
14547
|
return false;
|
|
14084
14548
|
}
|
|
@@ -14286,7 +14750,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14286
14750
|
return false;
|
|
14287
14751
|
}
|
|
14288
14752
|
}
|
|
14289
|
-
async resolveSessionAction(cdp, sessionId, action) {
|
|
14753
|
+
async resolveSessionAction(cdp, sessionId, action, button) {
|
|
14290
14754
|
await this.ensureSessionPanelOpen(sessionId);
|
|
14291
14755
|
const target = this.getSessionTarget(sessionId);
|
|
14292
14756
|
if (!target?.parentSessionId) return false;
|
|
@@ -14296,7 +14760,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14296
14760
|
if (!agent) return false;
|
|
14297
14761
|
try {
|
|
14298
14762
|
const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
14299
|
-
return await agent.adapter.resolveAction(evaluate, action);
|
|
14763
|
+
return await agent.adapter.resolveAction(evaluate, action, button);
|
|
14300
14764
|
} catch (e) {
|
|
14301
14765
|
this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
|
|
14302
14766
|
return false;
|
|
@@ -14526,7 +14990,43 @@ var AgentStreamPoller = class {
|
|
|
14526
14990
|
}
|
|
14527
14991
|
try {
|
|
14528
14992
|
await agentStreamManager.syncActiveSession(cdp, parentSessionId);
|
|
14529
|
-
|
|
14993
|
+
let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
|
|
14994
|
+
if (stream?.status === "waiting_approval") {
|
|
14995
|
+
const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
|
|
14996
|
+
if (autoApprove && resolvedActiveSessionId) {
|
|
14997
|
+
const provider = providerLoader.getMeta(stream.agentType);
|
|
14998
|
+
const { label: buttonLabel } = pickApprovalButton(stream.activeModal?.buttons, provider);
|
|
14999
|
+
const approved = await agentStreamManager.resolveSessionAction(cdp, resolvedActiveSessionId, "approve", buttonLabel);
|
|
15000
|
+
if (approved) {
|
|
15001
|
+
const effectId = [
|
|
15002
|
+
"auto_approval",
|
|
15003
|
+
resolvedActiveSessionId,
|
|
15004
|
+
String(stream.messages?.length || 0),
|
|
15005
|
+
buttonLabel,
|
|
15006
|
+
String(stream.activeModal?.message || "").trim()
|
|
15007
|
+
].join(":");
|
|
15008
|
+
stream = {
|
|
15009
|
+
...stream,
|
|
15010
|
+
status: "streaming",
|
|
15011
|
+
activeModal: void 0,
|
|
15012
|
+
effects: [
|
|
15013
|
+
...stream.effects || [],
|
|
15014
|
+
{
|
|
15015
|
+
type: "message",
|
|
15016
|
+
id: effectId,
|
|
15017
|
+
persist: true,
|
|
15018
|
+
message: {
|
|
15019
|
+
role: "system",
|
|
15020
|
+
senderName: "System",
|
|
15021
|
+
kind: "system",
|
|
15022
|
+
content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
|
|
15023
|
+
}
|
|
15024
|
+
}
|
|
15025
|
+
]
|
|
15026
|
+
};
|
|
15027
|
+
}
|
|
15028
|
+
}
|
|
15029
|
+
}
|
|
14530
15030
|
this.deps.onStreamsUpdated?.(ideType, stream ? [stream] : []);
|
|
14531
15031
|
} catch {
|
|
14532
15032
|
}
|