@adhdev/daemon-standalone 0.8.28 → 0.8.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9495,6 +9495,9 @@ var require_dist = __commonJS({
9495
9495
  state.record.lastActivityAt = Date.now();
9496
9496
  return this.cloneRecord(state.record);
9497
9497
  }
9498
+ deleteSession(sessionId) {
9499
+ return this.sessions.delete(sessionId);
9500
+ }
9498
9501
  requireSession(sessionId) {
9499
9502
  const state = this.sessions.get(sessionId);
9500
9503
  if (!state) throw new Error(`Unknown session: ${sessionId}`);
@@ -9676,10 +9679,12 @@ var require_dist = __commonJS({
9676
9679
  env[key] = value;
9677
9680
  }
9678
9681
  for (const key of Object.keys(env)) {
9679
- if (key === "INIT_CWD" || key === "npm_command" || key === "npm_execpath" || key === "npm_node_execpath" || key.startsWith("npm_") || key.startsWith("npm_config_") || key.startsWith("npm_package_") || key.startsWith("npm_lifecycle_") || key.startsWith("PNPM_") || key.startsWith("YARN_") || key.startsWith("BUN_")) {
9682
+ if (key === "INIT_CWD" || key === "npm_command" || key === "npm_execpath" || key === "npm_node_execpath" || key.startsWith("npm_") || key.startsWith("npm_config_") || key.startsWith("npm_package_") || key.startsWith("npm_lifecycle_") || key.startsWith("PNPM_") || key.startsWith("YARN_") || key.startsWith("BUN_") || key.startsWith("VSCODE_") || key.startsWith("ELECTRON_")) {
9680
9683
  delete env[key];
9681
9684
  }
9682
9685
  }
9686
+ delete env.CODEX_THREAD_ID;
9687
+ delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;
9683
9688
  applyTerminalColorEnv(env);
9684
9689
  return env;
9685
9690
  }
@@ -29440,6 +29445,9 @@ var require_dist2 = __commonJS({
29440
29445
  looksLikeVisibleIdlePrompt(screenText) {
29441
29446
  const text = String(screenText || "");
29442
29447
  if (!text.trim()) return false;
29448
+ 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)) {
29449
+ return true;
29450
+ }
29443
29451
  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);
29444
29452
  }
29445
29453
  findLastMatchingLineIndex(lines, predicate) {
@@ -32739,11 +32747,72 @@ ${data.message || ""}`.trim();
32739
32747
  var os52 = __toESM2(require("os"));
32740
32748
  var HISTORY_DIR = path7.join(os52.homedir(), ".adhdev", "history");
32741
32749
  var RETAIN_DAYS = 30;
32750
+ 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;
32751
+ function normalizeHistoryComparable(text) {
32752
+ return String(text || "").replace(/\s+/g, " ").trim();
32753
+ }
32754
+ function cleanupHistoryContent(agentType, role, content) {
32755
+ let value = String(content || "").replace(/\r\n/g, "\n").trim();
32756
+ if (!value) return "";
32757
+ if (agentType === "codex-cli" && role === "assistant") {
32758
+ const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
32759
+ value = filtered;
32760
+ }
32761
+ return value;
32762
+ }
32763
+ function buildHistoryMessageHash(agentType, message) {
32764
+ if (message.historyDedupKey) return message.historyDedupKey;
32765
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
32766
+ return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
32767
+ }
32768
+ function buildHistoryMessageSignature(agentType, message) {
32769
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
32770
+ return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
32771
+ }
32772
+ function isAdjacentHistoryDuplicate(agentType, previous, next) {
32773
+ if (!previous || !next) return false;
32774
+ return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
32775
+ }
32776
+ function collapseReplayAssistantTurns(agentType, messages) {
32777
+ if (agentType !== "codex-cli") return messages;
32778
+ const collapsed = [];
32779
+ let sawAssistantSinceLastUser = false;
32780
+ for (const message of messages) {
32781
+ if (message.role === "user") {
32782
+ sawAssistantSinceLastUser = false;
32783
+ collapsed.push(message);
32784
+ continue;
32785
+ }
32786
+ if (message.role === "assistant") {
32787
+ if (sawAssistantSinceLastUser) continue;
32788
+ sawAssistantSinceLastUser = true;
32789
+ collapsed.push(message);
32790
+ continue;
32791
+ }
32792
+ collapsed.push(message);
32793
+ }
32794
+ return collapsed;
32795
+ }
32796
+ function sanitizeHistoryMessage(agentType, message) {
32797
+ if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
32798
+ return null;
32799
+ }
32800
+ const content = cleanupHistoryContent(agentType, message.role, message.content);
32801
+ if (!content) return null;
32802
+ return {
32803
+ ...message,
32804
+ content
32805
+ };
32806
+ }
32742
32807
  var ChatHistoryWriter = class {
32743
32808
  /** Last seen message count per agent (deduplication) */
32744
32809
  lastSeenCounts = /* @__PURE__ */ new Map();
32745
32810
  /** Last seen message hash per agent (deduplication) */
32746
32811
  lastSeenHashes = /* @__PURE__ */ new Map();
32812
+ /** Last appended normalized message signature per agent/session */
32813
+ lastSeenSignatures = /* @__PURE__ */ new Map();
32814
+ /** Last appended normalized non-system turn signature per agent/session */
32815
+ lastSeenTurnSignatures = /* @__PURE__ */ new Map();
32747
32816
  rotated = false;
32748
32817
  /**
32749
32818
  * Append new messages to history
@@ -32765,14 +32834,36 @@ ${data.message || ""}`.trim();
32765
32834
  }
32766
32835
  const newMessages = [];
32767
32836
  for (const msg of messages) {
32768
- const hash2 = msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`;
32837
+ const role = msg.role;
32838
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
32839
+ const content = cleanupHistoryContent(agentType, role, msg.content || "");
32840
+ if (!content) continue;
32841
+ const receivedAt = msg.receivedAt || Date.now();
32842
+ const hash2 = buildHistoryMessageHash(agentType, {
32843
+ role,
32844
+ content,
32845
+ receivedAt,
32846
+ kind: typeof msg.kind === "string" ? msg.kind : void 0,
32847
+ historyDedupKey: msg.historyDedupKey
32848
+ });
32849
+ const signature = buildHistoryMessageSignature(agentType, {
32850
+ role,
32851
+ content,
32852
+ kind: typeof msg.kind === "string" ? msg.kind : void 0
32853
+ });
32769
32854
  if (seenHashes.has(hash2)) continue;
32855
+ if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
32856
+ if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
32770
32857
  seenHashes.add(hash2);
32858
+ this.lastSeenSignatures.set(dedupKey, signature);
32859
+ if (role !== "system") {
32860
+ this.lastSeenTurnSignatures.set(dedupKey, signature);
32861
+ }
32771
32862
  newMessages.push({
32772
- ts: new Date(msg.receivedAt || Date.now()).toISOString(),
32773
- receivedAt: msg.receivedAt || Date.now(),
32774
- role: msg.role,
32775
- content: msg.content || "",
32863
+ ts: new Date(receivedAt).toISOString(),
32864
+ receivedAt,
32865
+ role,
32866
+ content,
32776
32867
  kind: typeof msg.kind === "string" ? msg.kind : void 0,
32777
32868
  senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
32778
32869
  agent: agentType,
@@ -32792,6 +32883,8 @@ ${data.message || ""}`.trim();
32792
32883
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
32793
32884
  if (messages.length < prevCount * 0.5 && prevCount > 3) {
32794
32885
  seenHashes.clear();
32886
+ this.lastSeenSignatures.delete(dedupKey);
32887
+ this.lastSeenTurnSignatures.delete(dedupKey);
32795
32888
  for (const msg of messages) {
32796
32889
  seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
32797
32890
  }
@@ -32805,6 +32898,54 @@ ${data.message || ""}`.trim();
32805
32898
  } catch {
32806
32899
  }
32807
32900
  }
32901
+ seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
32902
+ const effectiveHistoryKey = historySessionId || instanceId;
32903
+ const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
32904
+ const seenHashes = /* @__PURE__ */ new Set();
32905
+ for (const raw of messages) {
32906
+ const role = raw?.role;
32907
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
32908
+ const content = cleanupHistoryContent(agentType, role, raw?.content || "");
32909
+ if (!content) continue;
32910
+ seenHashes.add(buildHistoryMessageHash(agentType, {
32911
+ role,
32912
+ content,
32913
+ receivedAt: raw?.receivedAt || 0,
32914
+ kind: typeof raw?.kind === "string" ? raw.kind : void 0,
32915
+ historyDedupKey: raw?.historyDedupKey
32916
+ }));
32917
+ }
32918
+ this.lastSeenHashes.set(dedupKey, seenHashes);
32919
+ this.lastSeenCounts.set(dedupKey, messages.length);
32920
+ const lastMessage = [...messages].reverse().find((raw) => {
32921
+ const role = raw?.role;
32922
+ if (role !== "user" && role !== "assistant" && role !== "system") return false;
32923
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
32924
+ });
32925
+ const lastTurnMessage = [...messages].reverse().find((raw) => {
32926
+ const role = raw?.role;
32927
+ if (role !== "user" && role !== "assistant") return false;
32928
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
32929
+ });
32930
+ if (lastMessage) {
32931
+ this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
32932
+ role: lastMessage.role,
32933
+ content: lastMessage.content,
32934
+ kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
32935
+ }));
32936
+ } else {
32937
+ this.lastSeenSignatures.delete(dedupKey);
32938
+ }
32939
+ if (lastTurnMessage) {
32940
+ this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
32941
+ role: lastTurnMessage.role,
32942
+ content: lastTurnMessage.content,
32943
+ kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
32944
+ }));
32945
+ } else {
32946
+ this.lastSeenTurnSignatures.delete(dedupKey);
32947
+ }
32948
+ }
32808
32949
  appendSystemMarker(agentType, content, options = {}) {
32809
32950
  this.appendNewMessages(
32810
32951
  agentType,
@@ -32835,6 +32976,16 @@ ${data.message || ""}`.trim();
32835
32976
  this.lastSeenHashes.set(toDedupKey, nextHashes);
32836
32977
  this.lastSeenHashes.delete(fromDedupKey);
32837
32978
  }
32979
+ const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
32980
+ if (fromSignature) {
32981
+ this.lastSeenSignatures.set(toDedupKey, fromSignature);
32982
+ this.lastSeenSignatures.delete(fromDedupKey);
32983
+ }
32984
+ const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
32985
+ if (fromTurnSignature) {
32986
+ this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
32987
+ this.lastSeenTurnSignatures.delete(fromDedupKey);
32988
+ }
32838
32989
  const fromCount = this.lastSeenCounts.get(fromDedupKey);
32839
32990
  if (typeof fromCount === "number") {
32840
32991
  this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
@@ -32876,10 +33027,61 @@ ${data.message || ""}`.trim();
32876
33027
  } catch {
32877
33028
  }
32878
33029
  }
33030
+ compactHistorySession(agentType, historySessionId) {
33031
+ const sessionId = String(historySessionId || "").trim();
33032
+ if (!sessionId) return;
33033
+ try {
33034
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
33035
+ if (!fs32.existsSync(dir)) return;
33036
+ const prefix = `${this.sanitize(sessionId)}_`;
33037
+ const files = fs32.readdirSync(dir).filter((file2) => file2.startsWith(prefix) && file2.endsWith(".jsonl")).sort();
33038
+ const seen = /* @__PURE__ */ new Set();
33039
+ for (const file2 of files) {
33040
+ const filePath = path7.join(dir, file2);
33041
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
33042
+ const next = [];
33043
+ for (const line of lines) {
33044
+ let parsed = null;
33045
+ try {
33046
+ parsed = JSON.parse(line);
33047
+ } catch {
33048
+ parsed = null;
33049
+ }
33050
+ if (!parsed || parsed.historySessionId !== sessionId) continue;
33051
+ const sanitized = sanitizeHistoryMessage(agentType, parsed);
33052
+ if (!sanitized) continue;
33053
+ const hash2 = buildHistoryMessageHash(agentType, sanitized);
33054
+ if (seen.has(hash2)) continue;
33055
+ seen.add(hash2);
33056
+ next.push(sanitized);
33057
+ }
33058
+ next.sort((a, b2) => a.receivedAt - b2.receivedAt);
33059
+ const dedupedAdjacent = [];
33060
+ let lastTurn = null;
33061
+ for (const entry of next) {
33062
+ const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
33063
+ if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
33064
+ if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
33065
+ dedupedAdjacent.push(entry);
33066
+ if (entry.role !== "system") lastTurn = entry;
33067
+ }
33068
+ const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
33069
+ if (collapsed.length === 0) {
33070
+ fs32.unlinkSync(filePath);
33071
+ continue;
33072
+ }
33073
+ fs32.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
33074
+ `, "utf-8");
33075
+ }
33076
+ } catch {
33077
+ }
33078
+ }
32879
33079
  /** Called when agent session is explicitly changed */
32880
33080
  onSessionChange(agentType) {
32881
33081
  this.lastSeenHashes.delete(agentType);
32882
33082
  this.lastSeenCounts.delete(agentType);
33083
+ this.lastSeenSignatures.delete(agentType);
33084
+ this.lastSeenTurnSignatures.delete(agentType);
32883
33085
  }
32884
33086
  /** Delete history files older than 30 days */
32885
33087
  async rotateOldFiles() {
@@ -32920,23 +33122,37 @@ ${data.message || ""}`.trim();
32920
33122
  return true;
32921
33123
  }).sort().reverse();
32922
33124
  const allMessages = [];
32923
- const needed = offset + limit + 1;
33125
+ const seen = /* @__PURE__ */ new Set();
32924
33126
  for (const file2 of files) {
32925
- if (allMessages.length >= needed) break;
32926
33127
  const filePath = path7.join(dir, file2);
32927
33128
  const content = fs32.readFileSync(filePath, "utf-8");
32928
33129
  const lines = content.trim().split("\n").filter(Boolean);
32929
- for (let i = lines.length - 1; i >= 0; i--) {
32930
- if (allMessages.length >= needed) break;
33130
+ for (let i = 0; i < lines.length; i++) {
32931
33131
  try {
32932
- allMessages.push(JSON.parse(lines[i]));
33132
+ const parsed = JSON.parse(lines[i]);
33133
+ const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
33134
+ if (!sanitizedMessage) continue;
33135
+ const hash2 = buildHistoryMessageHash(agentType, sanitizedMessage);
33136
+ if (seen.has(hash2)) continue;
33137
+ seen.add(hash2);
33138
+ allMessages.push(sanitizedMessage);
32933
33139
  } catch {
32934
33140
  }
32935
33141
  }
32936
33142
  }
32937
- const sliced = allMessages.slice(offset, offset + limit);
32938
- const hasMore = allMessages.length > offset + limit;
32939
- sliced.reverse();
33143
+ allMessages.sort((a, b2) => a.receivedAt - b2.receivedAt);
33144
+ const chronological = [];
33145
+ let lastTurn = null;
33146
+ for (const message of allMessages) {
33147
+ const previous = chronological[chronological.length - 1];
33148
+ if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
33149
+ if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
33150
+ chronological.push(message);
33151
+ if (message.role !== "system") lastTurn = message;
33152
+ }
33153
+ const collapsed = collapseReplayAssistantTurns(agentType, chronological);
33154
+ const sliced = collapsed.slice(offset, offset + limit);
33155
+ const hasMore = collapsed.length > offset + limit;
32940
33156
  return { messages: sliced, hasMore };
32941
33157
  } catch {
32942
33158
  return { messages: [], hasMore: false };
@@ -34770,6 +34986,46 @@ ${effect.notification.body || ""}`.trim();
34770
34986
  if (!parsed || typeof parsed !== "object") return false;
34771
34987
  return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
34772
34988
  }
34989
+ async function readExtensionChatState(h) {
34990
+ try {
34991
+ const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
34992
+ if (!evalResult?.result) return null;
34993
+ const parsed = parseMaybeJson(evalResult.result);
34994
+ return parsed && typeof parsed === "object" ? parsed : null;
34995
+ } catch {
34996
+ return null;
34997
+ }
34998
+ }
34999
+ function getStateMessageCount(state) {
35000
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
35001
+ }
35002
+ function getStateLastSignature(state) {
35003
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
35004
+ const last = messages[messages.length - 1];
35005
+ if (!last) return "";
35006
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
35007
+ }
35008
+ async function getStableExtensionBaseline(h) {
35009
+ const first = await readExtensionChatState(h);
35010
+ if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
35011
+ await new Promise((resolve12) => setTimeout(resolve12, 150));
35012
+ const second = await readExtensionChatState(h);
35013
+ return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
35014
+ }
35015
+ async function verifyExtensionSendObserved(h, before) {
35016
+ const beforeCount = getStateMessageCount(before);
35017
+ const beforeSignature = getStateLastSignature(before);
35018
+ for (let attempt = 0; attempt < 12; attempt += 1) {
35019
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
35020
+ const state = await readExtensionChatState(h);
35021
+ if (state?.status === "waiting_approval") return true;
35022
+ const afterCount = getStateMessageCount(state);
35023
+ const afterSignature = getStateLastSignature(state);
35024
+ if (afterCount > beforeCount) return true;
35025
+ if (afterSignature && afterSignature !== beforeSignature) return true;
35026
+ }
35027
+ return false;
35028
+ }
34773
35029
  async function handleChatHistory(h, args) {
34774
35030
  const { agentType, offset, limit } = args;
34775
35031
  const historySessionId = getHistorySessionId(h, args);
@@ -34950,12 +35206,17 @@ ${effect.notification.body || ""}`.trim();
34950
35206
  if (isExtensionTransport(transport)) {
34951
35207
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
34952
35208
  try {
35209
+ const beforeState = await getStableExtensionBaseline(h);
34953
35210
  const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
34954
35211
  if (evalResult?.result) {
34955
35212
  const parsed = parseMaybeJson(evalResult.result);
34956
35213
  if (didProviderConfirmSend(parsed)) {
34957
- _log(`Extension script sent OK`);
34958
- return _logSendSuccess("extension-script");
35214
+ const observed = await verifyExtensionSendObserved(h, beforeState);
35215
+ if (observed) {
35216
+ _log(`Extension script sent OK`);
35217
+ return _logSendSuccess("extension-script");
35218
+ }
35219
+ _log(`Extension script reported send but no chat-state change was observed`);
34959
35220
  }
34960
35221
  if (parsed?.needsTypeAndSend) {
34961
35222
  _log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
@@ -35460,7 +35721,7 @@ ${effect.notification.body || ""}`.trim();
35460
35721
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
35461
35722
  }
35462
35723
  if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
35463
- const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
35724
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
35464
35725
  return { success: ok };
35465
35726
  }
35466
35727
  if (transport === "acp") {
@@ -36855,6 +37116,7 @@ ${effect.notification.body || ""}`.trim();
36855
37116
  historyWriter;
36856
37117
  runtimeMessages = [];
36857
37118
  instanceId;
37119
+ suppressIdleHistoryReplay = false;
36858
37120
  presentationMode;
36859
37121
  providerSessionId;
36860
37122
  launchMode;
@@ -36882,7 +37144,15 @@ ${effect.notification.body || ""}`.trim();
36882
37144
  await this.adapter.spawn();
36883
37145
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
36884
37146
  if (this.providerSessionId) {
37147
+ this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
36885
37148
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
37149
+ this.historyWriter.seedSessionHistory(
37150
+ this.type,
37151
+ restoredHistory.messages,
37152
+ this.providerSessionId,
37153
+ this.instanceId
37154
+ );
37155
+ this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
36886
37156
  if (restoredHistory.messages.length > 0) {
36887
37157
  this.adapter.seedCommittedMessages(
36888
37158
  restoredHistory.messages.map((message) => ({
@@ -36926,7 +37196,7 @@ ${effect.notification.body || ""}`.trim();
36926
37196
  } else if (this.type === "codex-cli") {
36927
37197
  probedSessionId = this.probeSessionIdFromConfig({
36928
37198
  dbPath: "~/.codex/state_5.sqlite",
36929
- query: "select id from threads where cwd in ({dirs}) and created_at >= ? and archived = 0 order by created_at desc limit 1",
37199
+ query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
36930
37200
  timestampFormat: "unix_s"
36931
37201
  });
36932
37202
  } else if (this.type === "goose-cli") {
@@ -36986,6 +37256,7 @@ ${effect.notification.body || ""}`.trim();
36986
37256
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
36987
37257
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
36988
37258
  if (parsedMessages.length > 0) {
37259
+ const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
36989
37260
  let messagesToSave = parsedMessages;
36990
37261
  if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
36991
37262
  const lastIdx = messagesToSave.length - 1;
@@ -36993,7 +37264,7 @@ ${effect.notification.body || ""}`.trim();
36993
37264
  messagesToSave = messagesToSave.slice(0, lastIdx);
36994
37265
  }
36995
37266
  }
36996
- if (messagesToSave.length > 0) {
37267
+ if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
36997
37268
  this.historyWriter.appendNewMessages(
36998
37269
  this.type,
36999
37270
  messagesToSave,
@@ -37088,6 +37359,7 @@ ${effect.notification.body || ""}`.trim();
37088
37359
  if (newStatus !== this.lastStatus) {
37089
37360
  LOG2.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
37090
37361
  if (this.lastStatus === "idle" && newStatus === "generating") {
37362
+ this.suppressIdleHistoryReplay = false;
37091
37363
  if (this.completedDebouncePending) {
37092
37364
  LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
37093
37365
  if (this.completedDebounceTimer) {
@@ -37107,6 +37379,7 @@ ${effect.notification.body || ""}`.trim();
37107
37379
  this.generatingDebounceTimer = null;
37108
37380
  }, 1e3);
37109
37381
  } else if (newStatus === "waiting_approval") {
37382
+ this.suppressIdleHistoryReplay = false;
37110
37383
  if (this.generatingDebouncePending) {
37111
37384
  if (this.generatingDebounceTimer) {
37112
37385
  clearTimeout(this.generatingDebounceTimer);
@@ -38811,6 +39084,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
38811
39084
  if (!instanceManager) return 0;
38812
39085
  const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
38813
39086
  let restored = 0;
39087
+ const restoredBindings = /* @__PURE__ */ new Set();
38814
39088
  for (const record2 of sessions) {
38815
39089
  if (!record2?.runtimeId || !record2?.cliType || !record2?.workspace) continue;
38816
39090
  if (this.adapters.has(record2.runtimeId) || instanceManager.getInstance(record2.runtimeId)) continue;
@@ -38824,6 +39098,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
38824
39098
  record2.cliArgs,
38825
39099
  record2.providerSessionId
38826
39100
  );
39101
+ const bindingKey = [
39102
+ normalizedType,
39103
+ record2.workspace,
39104
+ sessionBinding.providerSessionId || record2.runtimeId
39105
+ ].join("::");
39106
+ if (restoredBindings.has(bindingKey)) {
39107
+ LOG2.info(
39108
+ "CLI",
39109
+ `\u21B7 Skipping duplicate hosted runtime restore: ${record2.runtimeKey || record2.runtimeId} (${normalizedType} @ ${record2.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
39110
+ );
39111
+ continue;
39112
+ }
38827
39113
  try {
38828
39114
  await this.registerCliInstance(
38829
39115
  record2.runtimeId,
@@ -38839,6 +39125,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
38839
39125
  launchMode: "manual"
38840
39126
  }
38841
39127
  );
39128
+ restoredBindings.add(bindingKey);
38842
39129
  restored += 1;
38843
39130
  LOG2.info("CLI", `\u267B Restored hosted runtime: ${record2.runtimeKey || record2.runtimeId} (${record2.displayName || record2.workspace})`);
38844
39131
  } catch (error48) {
@@ -41172,6 +41459,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
41172
41459
  const record2 = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
41173
41460
  return { success: true, record: record2 };
41174
41461
  }
41462
+ case "session_host_prune_duplicate_sessions": {
41463
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
41464
+ const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
41465
+ providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
41466
+ workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
41467
+ dryRun: args?.dryRun === true
41468
+ });
41469
+ return { success: true, result };
41470
+ }
41175
41471
  case "session_host_acquire_write": {
41176
41472
  if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
41177
41473
  const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
@@ -41749,6 +42045,51 @@ Run 'adhdev doctor' for detailed diagnostics.`
41749
42045
  isTransportError(reason) {
41750
42046
  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);
41751
42047
  }
42048
+ titlesMatch(actual, expected) {
42049
+ const lhs = actual.trim().toLowerCase();
42050
+ const rhs = expected.trim().toLowerCase();
42051
+ if (!lhs || !rhs) return false;
42052
+ return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
42053
+ }
42054
+ messageCount(state) {
42055
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
42056
+ }
42057
+ lastMessageSignature(state) {
42058
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
42059
+ const last = messages[messages.length - 1];
42060
+ if (!last) return "";
42061
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
42062
+ }
42063
+ async verifySendOutcome(evaluate, before) {
42064
+ const beforeCount = this.messageCount(before);
42065
+ const beforeSignature = this.lastMessageSignature(before);
42066
+ for (let attempt = 0; attempt < 12; attempt += 1) {
42067
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
42068
+ let state;
42069
+ try {
42070
+ state = await this.readChat(evaluate);
42071
+ } catch {
42072
+ continue;
42073
+ }
42074
+ if (state.status === "waiting_approval") {
42075
+ return true;
42076
+ }
42077
+ const afterCount = this.messageCount(state);
42078
+ const afterSignature = this.lastMessageSignature(state);
42079
+ if (afterCount > beforeCount) return true;
42080
+ if (afterSignature && afterSignature !== beforeSignature) return true;
42081
+ }
42082
+ return false;
42083
+ }
42084
+ async readStableBaselineState(evaluate) {
42085
+ const first = await this.readChat(evaluate);
42086
+ if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
42087
+ return first;
42088
+ }
42089
+ await new Promise((resolve12) => setTimeout(resolve12, 150));
42090
+ const second = await this.readChat(evaluate);
42091
+ return this.messageCount(second) >= this.messageCount(first) ? second : first;
42092
+ }
41752
42093
  async readChat(evaluate) {
41753
42094
  const script = this.callScript("readChat");
41754
42095
  if (!script) return this.errorState("readChat script not available");
@@ -41774,6 +42115,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
41774
42115
  mode: data.mode,
41775
42116
  activeModal: data.activeModal
41776
42117
  };
42118
+ if (typeof data.title === "string" && data.title.trim()) {
42119
+ state.title = data.title.trim();
42120
+ }
41777
42121
  const controlValues = extractProviderControlValues(this.provider.controls, data);
41778
42122
  if (controlValues) state.controlValues = controlValues;
41779
42123
  const effects = normalizeProviderEffects(data);
@@ -41797,6 +42141,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
41797
42141
  }
41798
42142
  }
41799
42143
  async sendMessage(evaluate, text) {
42144
+ let beforeState = null;
42145
+ try {
42146
+ beforeState = await this.readStableBaselineState(evaluate);
42147
+ } catch {
42148
+ beforeState = null;
42149
+ }
41800
42150
  const params = { message: text };
41801
42151
  const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
41802
42152
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
@@ -41814,7 +42164,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
41814
42164
  }
41815
42165
  if (parsed && typeof parsed === "object") {
41816
42166
  if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
41817
- return;
42167
+ const verified = await this.verifySendOutcome(evaluate, beforeState);
42168
+ if (verified) return;
42169
+ throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
41818
42170
  }
41819
42171
  if (typeof parsed.error === "string" && parsed.error.trim()) {
41820
42172
  throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
@@ -41825,7 +42177,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
41825
42177
  async resolveAction(evaluate, action, button) {
41826
42178
  const script = this.callScript("resolveAction", { action, button });
41827
42179
  if (!script) return false;
41828
- return await evaluate(script) === true;
42180
+ const result = await evaluate(script);
42181
+ const parsed = this.parseMaybeJson(result);
42182
+ if (parsed === true) return true;
42183
+ if (typeof parsed === "string") {
42184
+ const normalized = parsed.trim().toLowerCase();
42185
+ return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
42186
+ }
42187
+ if (!parsed || typeof parsed !== "object") return false;
42188
+ return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
41829
42189
  }
41830
42190
  async newSession(evaluate) {
41831
42191
  const script = this.callScript("newSession");
@@ -41862,7 +42222,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
41862
42222
  return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
41863
42223
  }
41864
42224
  if (data && typeof data === "object") {
41865
- return data.switched === true || data.success === true || data.ok === true;
42225
+ if (data.switched === true || data.success === true || data.ok === true) return true;
42226
+ if (typeof data.error === "string" && data.error.trim()) return false;
42227
+ }
42228
+ for (let attempt = 0; attempt < 6; attempt += 1) {
42229
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
42230
+ const state = await this.readChat(evaluate);
42231
+ const title = typeof state.title === "string" ? state.title : "";
42232
+ if (this.titlesMatch(title, sessionId)) return true;
41866
42233
  }
41867
42234
  return false;
41868
42235
  }
@@ -42068,7 +42435,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42068
42435
  return false;
42069
42436
  }
42070
42437
  }
42071
- async resolveSessionAction(cdp, sessionId, action) {
42438
+ async resolveSessionAction(cdp, sessionId, action, button) {
42072
42439
  await this.ensureSessionPanelOpen(sessionId);
42073
42440
  const target = this.getSessionTarget(sessionId);
42074
42441
  if (!target?.parentSessionId) return false;
@@ -42078,7 +42445,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42078
42445
  if (!agent) return false;
42079
42446
  try {
42080
42447
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
42081
- return await agent.adapter.resolveAction(evaluate, action);
42448
+ return await agent.adapter.resolveAction(evaluate, action, button);
42082
42449
  } catch (e) {
42083
42450
  this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
42084
42451
  return false;
@@ -48995,6 +49362,9 @@ var StandaloneSessionHostControlPlane = class {
48995
49362
  async forceDetachClient(sessionId, clientId) {
48996
49363
  return this.request("force_detach_client", { sessionId, clientId });
48997
49364
  }
49365
+ async pruneDuplicateSessions(payload = {}) {
49366
+ return this.request("prune_duplicate_sessions", payload);
49367
+ }
48998
49368
  async acquireWrite(payload) {
48999
49369
  return this.request("acquire_write", payload);
49000
49370
  }