adhdev 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
@@ -2595,6 +2595,62 @@ var init_control_effects = __esm({
2595
2595
  });
2596
2596
 
2597
2597
  // ../../oss/packages/daemon-core/src/config/chat-history.ts
2598
+ function normalizeHistoryComparable(text) {
2599
+ return String(text || "").replace(/\s+/g, " ").trim();
2600
+ }
2601
+ function cleanupHistoryContent(agentType, role, content) {
2602
+ let value = String(content || "").replace(/\r\n/g, "\n").trim();
2603
+ if (!value) return "";
2604
+ if (agentType === "codex-cli" && role === "assistant") {
2605
+ const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
2606
+ value = filtered;
2607
+ }
2608
+ return value;
2609
+ }
2610
+ function buildHistoryMessageHash(agentType, message) {
2611
+ if (message.historyDedupKey) return message.historyDedupKey;
2612
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
2613
+ return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
2614
+ }
2615
+ function buildHistoryMessageSignature(agentType, message) {
2616
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
2617
+ return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
2618
+ }
2619
+ function isAdjacentHistoryDuplicate(agentType, previous, next) {
2620
+ if (!previous || !next) return false;
2621
+ return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
2622
+ }
2623
+ function collapseReplayAssistantTurns(agentType, messages) {
2624
+ if (agentType !== "codex-cli") return messages;
2625
+ const collapsed = [];
2626
+ let sawAssistantSinceLastUser = false;
2627
+ for (const message of messages) {
2628
+ if (message.role === "user") {
2629
+ sawAssistantSinceLastUser = false;
2630
+ collapsed.push(message);
2631
+ continue;
2632
+ }
2633
+ if (message.role === "assistant") {
2634
+ if (sawAssistantSinceLastUser) continue;
2635
+ sawAssistantSinceLastUser = true;
2636
+ collapsed.push(message);
2637
+ continue;
2638
+ }
2639
+ collapsed.push(message);
2640
+ }
2641
+ return collapsed;
2642
+ }
2643
+ function sanitizeHistoryMessage(agentType, message) {
2644
+ if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
2645
+ return null;
2646
+ }
2647
+ const content = cleanupHistoryContent(agentType, message.role, message.content);
2648
+ if (!content) return null;
2649
+ return {
2650
+ ...message,
2651
+ content
2652
+ };
2653
+ }
2598
2654
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
2599
2655
  try {
2600
2656
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -2609,23 +2665,37 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
2609
2665
  return true;
2610
2666
  }).sort().reverse();
2611
2667
  const allMessages = [];
2612
- const needed = offset + limit + 1;
2668
+ const seen = /* @__PURE__ */ new Set();
2613
2669
  for (const file2 of files) {
2614
- if (allMessages.length >= needed) break;
2615
2670
  const filePath = path7.join(dir, file2);
2616
2671
  const content = fs3.readFileSync(filePath, "utf-8");
2617
2672
  const lines = content.trim().split("\n").filter(Boolean);
2618
- for (let i = lines.length - 1; i >= 0; i--) {
2619
- if (allMessages.length >= needed) break;
2673
+ for (let i = 0; i < lines.length; i++) {
2620
2674
  try {
2621
- allMessages.push(JSON.parse(lines[i]));
2675
+ const parsed = JSON.parse(lines[i]);
2676
+ const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
2677
+ if (!sanitizedMessage) continue;
2678
+ const hash2 = buildHistoryMessageHash(agentType, sanitizedMessage);
2679
+ if (seen.has(hash2)) continue;
2680
+ seen.add(hash2);
2681
+ allMessages.push(sanitizedMessage);
2622
2682
  } catch {
2623
2683
  }
2624
2684
  }
2625
2685
  }
2626
- const sliced = allMessages.slice(offset, offset + limit);
2627
- const hasMore = allMessages.length > offset + limit;
2628
- sliced.reverse();
2686
+ allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
2687
+ const chronological = [];
2688
+ let lastTurn = null;
2689
+ for (const message of allMessages) {
2690
+ const previous = chronological[chronological.length - 1];
2691
+ if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
2692
+ if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
2693
+ chronological.push(message);
2694
+ if (message.role !== "system") lastTurn = message;
2695
+ }
2696
+ const collapsed = collapseReplayAssistantTurns(agentType, chronological);
2697
+ const sliced = collapsed.slice(offset, offset + limit);
2698
+ const hasMore = collapsed.length > offset + limit;
2629
2699
  return { messages: sliced, hasMore };
2630
2700
  } catch {
2631
2701
  return { messages: [], hasMore: false };
@@ -2695,7 +2765,7 @@ function listSavedHistorySessions(agentType, options = {}) {
2695
2765
  return { sessions: [], hasMore: false };
2696
2766
  }
2697
2767
  }
2698
- var fs3, path7, os5, HISTORY_DIR, RETAIN_DAYS, ChatHistoryWriter;
2768
+ var fs3, path7, os5, HISTORY_DIR, RETAIN_DAYS, CODEX_STARTER_PROMPT_RE, ChatHistoryWriter;
2699
2769
  var init_chat_history = __esm({
2700
2770
  "../../oss/packages/daemon-core/src/config/chat-history.ts"() {
2701
2771
  "use strict";
@@ -2704,11 +2774,16 @@ var init_chat_history = __esm({
2704
2774
  os5 = __toESM(require("os"));
2705
2775
  HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
2706
2776
  RETAIN_DAYS = 30;
2777
+ 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;
2707
2778
  ChatHistoryWriter = class {
2708
2779
  /** Last seen message count per agent (deduplication) */
2709
2780
  lastSeenCounts = /* @__PURE__ */ new Map();
2710
2781
  /** Last seen message hash per agent (deduplication) */
2711
2782
  lastSeenHashes = /* @__PURE__ */ new Map();
2783
+ /** Last appended normalized message signature per agent/session */
2784
+ lastSeenSignatures = /* @__PURE__ */ new Map();
2785
+ /** Last appended normalized non-system turn signature per agent/session */
2786
+ lastSeenTurnSignatures = /* @__PURE__ */ new Map();
2712
2787
  rotated = false;
2713
2788
  /**
2714
2789
  * Append new messages to history
@@ -2730,14 +2805,36 @@ var init_chat_history = __esm({
2730
2805
  }
2731
2806
  const newMessages = [];
2732
2807
  for (const msg of messages) {
2733
- const hash2 = msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`;
2808
+ const role = msg.role;
2809
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
2810
+ const content = cleanupHistoryContent(agentType, role, msg.content || "");
2811
+ if (!content) continue;
2812
+ const receivedAt = msg.receivedAt || Date.now();
2813
+ const hash2 = buildHistoryMessageHash(agentType, {
2814
+ role,
2815
+ content,
2816
+ receivedAt,
2817
+ kind: typeof msg.kind === "string" ? msg.kind : void 0,
2818
+ historyDedupKey: msg.historyDedupKey
2819
+ });
2820
+ const signature = buildHistoryMessageSignature(agentType, {
2821
+ role,
2822
+ content,
2823
+ kind: typeof msg.kind === "string" ? msg.kind : void 0
2824
+ });
2734
2825
  if (seenHashes.has(hash2)) continue;
2826
+ if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
2827
+ if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
2735
2828
  seenHashes.add(hash2);
2829
+ this.lastSeenSignatures.set(dedupKey, signature);
2830
+ if (role !== "system") {
2831
+ this.lastSeenTurnSignatures.set(dedupKey, signature);
2832
+ }
2736
2833
  newMessages.push({
2737
- ts: new Date(msg.receivedAt || Date.now()).toISOString(),
2738
- receivedAt: msg.receivedAt || Date.now(),
2739
- role: msg.role,
2740
- content: msg.content || "",
2834
+ ts: new Date(receivedAt).toISOString(),
2835
+ receivedAt,
2836
+ role,
2837
+ content,
2741
2838
  kind: typeof msg.kind === "string" ? msg.kind : void 0,
2742
2839
  senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
2743
2840
  agent: agentType,
@@ -2757,6 +2854,8 @@ var init_chat_history = __esm({
2757
2854
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
2758
2855
  if (messages.length < prevCount * 0.5 && prevCount > 3) {
2759
2856
  seenHashes.clear();
2857
+ this.lastSeenSignatures.delete(dedupKey);
2858
+ this.lastSeenTurnSignatures.delete(dedupKey);
2760
2859
  for (const msg of messages) {
2761
2860
  seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
2762
2861
  }
@@ -2770,6 +2869,54 @@ var init_chat_history = __esm({
2770
2869
  } catch {
2771
2870
  }
2772
2871
  }
2872
+ seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
2873
+ const effectiveHistoryKey = historySessionId || instanceId;
2874
+ const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
2875
+ const seenHashes = /* @__PURE__ */ new Set();
2876
+ for (const raw of messages) {
2877
+ const role = raw?.role;
2878
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
2879
+ const content = cleanupHistoryContent(agentType, role, raw?.content || "");
2880
+ if (!content) continue;
2881
+ seenHashes.add(buildHistoryMessageHash(agentType, {
2882
+ role,
2883
+ content,
2884
+ receivedAt: raw?.receivedAt || 0,
2885
+ kind: typeof raw?.kind === "string" ? raw.kind : void 0,
2886
+ historyDedupKey: raw?.historyDedupKey
2887
+ }));
2888
+ }
2889
+ this.lastSeenHashes.set(dedupKey, seenHashes);
2890
+ this.lastSeenCounts.set(dedupKey, messages.length);
2891
+ const lastMessage = [...messages].reverse().find((raw) => {
2892
+ const role = raw?.role;
2893
+ if (role !== "user" && role !== "assistant" && role !== "system") return false;
2894
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
2895
+ });
2896
+ const lastTurnMessage = [...messages].reverse().find((raw) => {
2897
+ const role = raw?.role;
2898
+ if (role !== "user" && role !== "assistant") return false;
2899
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
2900
+ });
2901
+ if (lastMessage) {
2902
+ this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
2903
+ role: lastMessage.role,
2904
+ content: lastMessage.content,
2905
+ kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
2906
+ }));
2907
+ } else {
2908
+ this.lastSeenSignatures.delete(dedupKey);
2909
+ }
2910
+ if (lastTurnMessage) {
2911
+ this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
2912
+ role: lastTurnMessage.role,
2913
+ content: lastTurnMessage.content,
2914
+ kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
2915
+ }));
2916
+ } else {
2917
+ this.lastSeenTurnSignatures.delete(dedupKey);
2918
+ }
2919
+ }
2773
2920
  appendSystemMarker(agentType, content, options = {}) {
2774
2921
  this.appendNewMessages(
2775
2922
  agentType,
@@ -2800,6 +2947,16 @@ var init_chat_history = __esm({
2800
2947
  this.lastSeenHashes.set(toDedupKey, nextHashes);
2801
2948
  this.lastSeenHashes.delete(fromDedupKey);
2802
2949
  }
2950
+ const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
2951
+ if (fromSignature) {
2952
+ this.lastSeenSignatures.set(toDedupKey, fromSignature);
2953
+ this.lastSeenSignatures.delete(fromDedupKey);
2954
+ }
2955
+ const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
2956
+ if (fromTurnSignature) {
2957
+ this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
2958
+ this.lastSeenTurnSignatures.delete(fromDedupKey);
2959
+ }
2803
2960
  const fromCount = this.lastSeenCounts.get(fromDedupKey);
2804
2961
  if (typeof fromCount === "number") {
2805
2962
  this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
@@ -2841,10 +2998,61 @@ var init_chat_history = __esm({
2841
2998
  } catch {
2842
2999
  }
2843
3000
  }
3001
+ compactHistorySession(agentType, historySessionId) {
3002
+ const sessionId = String(historySessionId || "").trim();
3003
+ if (!sessionId) return;
3004
+ try {
3005
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
3006
+ if (!fs3.existsSync(dir)) return;
3007
+ const prefix = `${this.sanitize(sessionId)}_`;
3008
+ const files = fs3.readdirSync(dir).filter((file2) => file2.startsWith(prefix) && file2.endsWith(".jsonl")).sort();
3009
+ const seen = /* @__PURE__ */ new Set();
3010
+ for (const file2 of files) {
3011
+ const filePath = path7.join(dir, file2);
3012
+ const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
3013
+ const next = [];
3014
+ for (const line of lines) {
3015
+ let parsed = null;
3016
+ try {
3017
+ parsed = JSON.parse(line);
3018
+ } catch {
3019
+ parsed = null;
3020
+ }
3021
+ if (!parsed || parsed.historySessionId !== sessionId) continue;
3022
+ const sanitized = sanitizeHistoryMessage(agentType, parsed);
3023
+ if (!sanitized) continue;
3024
+ const hash2 = buildHistoryMessageHash(agentType, sanitized);
3025
+ if (seen.has(hash2)) continue;
3026
+ seen.add(hash2);
3027
+ next.push(sanitized);
3028
+ }
3029
+ next.sort((a, b) => a.receivedAt - b.receivedAt);
3030
+ const dedupedAdjacent = [];
3031
+ let lastTurn = null;
3032
+ for (const entry of next) {
3033
+ const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
3034
+ if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
3035
+ if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
3036
+ dedupedAdjacent.push(entry);
3037
+ if (entry.role !== "system") lastTurn = entry;
3038
+ }
3039
+ const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
3040
+ if (collapsed.length === 0) {
3041
+ fs3.unlinkSync(filePath);
3042
+ continue;
3043
+ }
3044
+ fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
3045
+ `, "utf-8");
3046
+ }
3047
+ } catch {
3048
+ }
3049
+ }
2844
3050
  /** Called when agent session is explicitly changed */
2845
3051
  onSessionChange(agentType) {
2846
3052
  this.lastSeenHashes.delete(agentType);
2847
3053
  this.lastSeenCounts.delete(agentType);
3054
+ this.lastSeenSignatures.delete(agentType);
3055
+ this.lastSeenTurnSignatures.delete(agentType);
2848
3056
  }
2849
3057
  /** Delete history files older than 30 days */
2850
3058
  async rotateOldFiles() {
@@ -4713,6 +4921,46 @@ function didProviderConfirmSend(result) {
4713
4921
  if (!parsed || typeof parsed !== "object") return false;
4714
4922
  return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
4715
4923
  }
4924
+ async function readExtensionChatState(h) {
4925
+ try {
4926
+ const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
4927
+ if (!evalResult?.result) return null;
4928
+ const parsed = parseMaybeJson(evalResult.result);
4929
+ return parsed && typeof parsed === "object" ? parsed : null;
4930
+ } catch {
4931
+ return null;
4932
+ }
4933
+ }
4934
+ function getStateMessageCount(state) {
4935
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
4936
+ }
4937
+ function getStateLastSignature(state) {
4938
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
4939
+ const last = messages[messages.length - 1];
4940
+ if (!last) return "";
4941
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
4942
+ }
4943
+ async function getStableExtensionBaseline(h) {
4944
+ const first = await readExtensionChatState(h);
4945
+ if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
4946
+ await new Promise((resolve16) => setTimeout(resolve16, 150));
4947
+ const second = await readExtensionChatState(h);
4948
+ return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
4949
+ }
4950
+ async function verifyExtensionSendObserved(h, before) {
4951
+ const beforeCount = getStateMessageCount(before);
4952
+ const beforeSignature = getStateLastSignature(before);
4953
+ for (let attempt = 0; attempt < 12; attempt += 1) {
4954
+ await new Promise((resolve16) => setTimeout(resolve16, 250));
4955
+ const state = await readExtensionChatState(h);
4956
+ if (state?.status === "waiting_approval") return true;
4957
+ const afterCount = getStateMessageCount(state);
4958
+ const afterSignature = getStateLastSignature(state);
4959
+ if (afterCount > beforeCount) return true;
4960
+ if (afterSignature && afterSignature !== beforeSignature) return true;
4961
+ }
4962
+ return false;
4963
+ }
4716
4964
  async function handleChatHistory(h, args) {
4717
4965
  const { agentType, offset, limit } = args;
4718
4966
  const historySessionId = getHistorySessionId(h, args);
@@ -4893,12 +5141,17 @@ async function handleSendChat(h, args) {
4893
5141
  if (isExtensionTransport(transport)) {
4894
5142
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
4895
5143
  try {
5144
+ const beforeState = await getStableExtensionBaseline(h);
4896
5145
  const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
4897
5146
  if (evalResult?.result) {
4898
5147
  const parsed = parseMaybeJson(evalResult.result);
4899
5148
  if (didProviderConfirmSend(parsed)) {
4900
- _log(`Extension script sent OK`);
4901
- return _logSendSuccess("extension-script");
5149
+ const observed = await verifyExtensionSendObserved(h, beforeState);
5150
+ if (observed) {
5151
+ _log(`Extension script sent OK`);
5152
+ return _logSendSuccess("extension-script");
5153
+ }
5154
+ _log(`Extension script reported send but no chat-state change was observed`);
4902
5155
  }
4903
5156
  if (parsed?.needsTypeAndSend) {
4904
5157
  _log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
@@ -5403,7 +5656,7 @@ async function handleResolveAction(h, args) {
5403
5656
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
5404
5657
  }
5405
5658
  if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5406
- const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
5659
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
5407
5660
  return { success: ok };
5408
5661
  }
5409
5662
  if (transport === "acp") {
@@ -7098,10 +7351,12 @@ function sanitizeSpawnEnv(baseEnv, overrides) {
7098
7351
  env[key] = value;
7099
7352
  }
7100
7353
  for (const key of Object.keys(env)) {
7101
- 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_")) {
7354
+ 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_")) {
7102
7355
  delete env[key];
7103
7356
  }
7104
7357
  }
7358
+ delete env.CODEX_THREAD_ID;
7359
+ delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;
7105
7360
  applyTerminalColorEnv(env);
7106
7361
  return env;
7107
7362
  }
@@ -8183,6 +8438,9 @@ var init_provider_cli_adapter = __esm({
8183
8438
  looksLikeVisibleIdlePrompt(screenText) {
8184
8439
  const text = String(screenText || "");
8185
8440
  if (!text.trim()) return false;
8441
+ 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)) {
8442
+ return true;
8443
+ }
8186
8444
  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);
8187
8445
  }
8188
8446
  findLastMatchingLineIndex(lines, predicate) {
@@ -9401,6 +9659,7 @@ var init_cli_provider_instance = __esm({
9401
9659
  historyWriter;
9402
9660
  runtimeMessages = [];
9403
9661
  instanceId;
9662
+ suppressIdleHistoryReplay = false;
9404
9663
  presentationMode;
9405
9664
  providerSessionId;
9406
9665
  launchMode;
@@ -9428,7 +9687,15 @@ var init_cli_provider_instance = __esm({
9428
9687
  await this.adapter.spawn();
9429
9688
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
9430
9689
  if (this.providerSessionId) {
9690
+ this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
9431
9691
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
9692
+ this.historyWriter.seedSessionHistory(
9693
+ this.type,
9694
+ restoredHistory.messages,
9695
+ this.providerSessionId,
9696
+ this.instanceId
9697
+ );
9698
+ this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
9432
9699
  if (restoredHistory.messages.length > 0) {
9433
9700
  this.adapter.seedCommittedMessages(
9434
9701
  restoredHistory.messages.map((message) => ({
@@ -9472,7 +9739,7 @@ var init_cli_provider_instance = __esm({
9472
9739
  } else if (this.type === "codex-cli") {
9473
9740
  probedSessionId = this.probeSessionIdFromConfig({
9474
9741
  dbPath: "~/.codex/state_5.sqlite",
9475
- query: "select id from threads where cwd in ({dirs}) and created_at >= ? and archived = 0 order by created_at desc limit 1",
9742
+ query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
9476
9743
  timestampFormat: "unix_s"
9477
9744
  });
9478
9745
  } else if (this.type === "goose-cli") {
@@ -9532,6 +9799,7 @@ var init_cli_provider_instance = __esm({
9532
9799
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
9533
9800
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
9534
9801
  if (parsedMessages.length > 0) {
9802
+ const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
9535
9803
  let messagesToSave = parsedMessages;
9536
9804
  if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
9537
9805
  const lastIdx = messagesToSave.length - 1;
@@ -9539,7 +9807,7 @@ var init_cli_provider_instance = __esm({
9539
9807
  messagesToSave = messagesToSave.slice(0, lastIdx);
9540
9808
  }
9541
9809
  }
9542
- if (messagesToSave.length > 0) {
9810
+ if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
9543
9811
  this.historyWriter.appendNewMessages(
9544
9812
  this.type,
9545
9813
  messagesToSave,
@@ -9634,6 +9902,7 @@ var init_cli_provider_instance = __esm({
9634
9902
  if (newStatus !== this.lastStatus) {
9635
9903
  LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
9636
9904
  if (this.lastStatus === "idle" && newStatus === "generating") {
9905
+ this.suppressIdleHistoryReplay = false;
9637
9906
  if (this.completedDebouncePending) {
9638
9907
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
9639
9908
  if (this.completedDebounceTimer) {
@@ -9653,6 +9922,7 @@ var init_cli_provider_instance = __esm({
9653
9922
  this.generatingDebounceTimer = null;
9654
9923
  }, 1e3);
9655
9924
  } else if (newStatus === "waiting_approval") {
9925
+ this.suppressIdleHistoryReplay = false;
9656
9926
  if (this.generatingDebouncePending) {
9657
9927
  if (this.generatingDebounceTimer) {
9658
9928
  clearTimeout(this.generatingDebounceTimer);
@@ -27696,6 +27966,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
27696
27966
  if (!instanceManager) return 0;
27697
27967
  const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
27698
27968
  let restored = 0;
27969
+ const restoredBindings = /* @__PURE__ */ new Set();
27699
27970
  for (const record2 of sessions) {
27700
27971
  if (!record2?.runtimeId || !record2?.cliType || !record2?.workspace) continue;
27701
27972
  if (this.adapters.has(record2.runtimeId) || instanceManager.getInstance(record2.runtimeId)) continue;
@@ -27709,6 +27980,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
27709
27980
  record2.cliArgs,
27710
27981
  record2.providerSessionId
27711
27982
  );
27983
+ const bindingKey = [
27984
+ normalizedType,
27985
+ record2.workspace,
27986
+ sessionBinding.providerSessionId || record2.runtimeId
27987
+ ].join("::");
27988
+ if (restoredBindings.has(bindingKey)) {
27989
+ LOG.info(
27990
+ "CLI",
27991
+ `\u21B7 Skipping duplicate hosted runtime restore: ${record2.runtimeKey || record2.runtimeId} (${normalizedType} @ ${record2.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
27992
+ );
27993
+ continue;
27994
+ }
27712
27995
  try {
27713
27996
  await this.registerCliInstance(
27714
27997
  record2.runtimeId,
@@ -27724,6 +28007,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
27724
28007
  launchMode: "manual"
27725
28008
  }
27726
28009
  );
28010
+ restoredBindings.add(bindingKey);
27727
28011
  restored += 1;
27728
28012
  LOG.info("CLI", `\u267B Restored hosted runtime: ${record2.runtimeKey || record2.runtimeId} (${record2.displayName || record2.workspace})`);
27729
28013
  } catch (error48) {
@@ -31876,6 +32160,15 @@ var init_router = __esm({
31876
32160
  const record2 = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
31877
32161
  return { success: true, record: record2 };
31878
32162
  }
32163
+ case "session_host_prune_duplicate_sessions": {
32164
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
32165
+ const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
32166
+ providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
32167
+ workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
32168
+ dryRun: args?.dryRun === true
32169
+ });
32170
+ return { success: true, result };
32171
+ }
31879
32172
  case "session_host_acquire_write": {
31880
32173
  if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31881
32174
  const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
@@ -32479,6 +32772,51 @@ var init_provider_adapter = __esm({
32479
32772
  isTransportError(reason) {
32480
32773
  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);
32481
32774
  }
32775
+ titlesMatch(actual, expected) {
32776
+ const lhs = actual.trim().toLowerCase();
32777
+ const rhs = expected.trim().toLowerCase();
32778
+ if (!lhs || !rhs) return false;
32779
+ return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
32780
+ }
32781
+ messageCount(state) {
32782
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
32783
+ }
32784
+ lastMessageSignature(state) {
32785
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
32786
+ const last = messages[messages.length - 1];
32787
+ if (!last) return "";
32788
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
32789
+ }
32790
+ async verifySendOutcome(evaluate, before) {
32791
+ const beforeCount = this.messageCount(before);
32792
+ const beforeSignature = this.lastMessageSignature(before);
32793
+ for (let attempt = 0; attempt < 12; attempt += 1) {
32794
+ await new Promise((resolve16) => setTimeout(resolve16, 250));
32795
+ let state;
32796
+ try {
32797
+ state = await this.readChat(evaluate);
32798
+ } catch {
32799
+ continue;
32800
+ }
32801
+ if (state.status === "waiting_approval") {
32802
+ return true;
32803
+ }
32804
+ const afterCount = this.messageCount(state);
32805
+ const afterSignature = this.lastMessageSignature(state);
32806
+ if (afterCount > beforeCount) return true;
32807
+ if (afterSignature && afterSignature !== beforeSignature) return true;
32808
+ }
32809
+ return false;
32810
+ }
32811
+ async readStableBaselineState(evaluate) {
32812
+ const first = await this.readChat(evaluate);
32813
+ if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
32814
+ return first;
32815
+ }
32816
+ await new Promise((resolve16) => setTimeout(resolve16, 150));
32817
+ const second = await this.readChat(evaluate);
32818
+ return this.messageCount(second) >= this.messageCount(first) ? second : first;
32819
+ }
32482
32820
  async readChat(evaluate) {
32483
32821
  const script = this.callScript("readChat");
32484
32822
  if (!script) return this.errorState("readChat script not available");
@@ -32504,6 +32842,9 @@ var init_provider_adapter = __esm({
32504
32842
  mode: data.mode,
32505
32843
  activeModal: data.activeModal
32506
32844
  };
32845
+ if (typeof data.title === "string" && data.title.trim()) {
32846
+ state.title = data.title.trim();
32847
+ }
32507
32848
  const controlValues = extractProviderControlValues(this.provider.controls, data);
32508
32849
  if (controlValues) state.controlValues = controlValues;
32509
32850
  const effects = normalizeProviderEffects(data);
@@ -32527,6 +32868,12 @@ var init_provider_adapter = __esm({
32527
32868
  }
32528
32869
  }
32529
32870
  async sendMessage(evaluate, text) {
32871
+ let beforeState = null;
32872
+ try {
32873
+ beforeState = await this.readStableBaselineState(evaluate);
32874
+ } catch {
32875
+ beforeState = null;
32876
+ }
32530
32877
  const params = { message: text };
32531
32878
  const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
32532
32879
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
@@ -32544,7 +32891,9 @@ var init_provider_adapter = __esm({
32544
32891
  }
32545
32892
  if (parsed && typeof parsed === "object") {
32546
32893
  if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
32547
- return;
32894
+ const verified = await this.verifySendOutcome(evaluate, beforeState);
32895
+ if (verified) return;
32896
+ throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
32548
32897
  }
32549
32898
  if (typeof parsed.error === "string" && parsed.error.trim()) {
32550
32899
  throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
@@ -32555,7 +32904,15 @@ var init_provider_adapter = __esm({
32555
32904
  async resolveAction(evaluate, action, button) {
32556
32905
  const script = this.callScript("resolveAction", { action, button });
32557
32906
  if (!script) return false;
32558
- return await evaluate(script) === true;
32907
+ const result = await evaluate(script);
32908
+ const parsed = this.parseMaybeJson(result);
32909
+ if (parsed === true) return true;
32910
+ if (typeof parsed === "string") {
32911
+ const normalized = parsed.trim().toLowerCase();
32912
+ return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
32913
+ }
32914
+ if (!parsed || typeof parsed !== "object") return false;
32915
+ return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
32559
32916
  }
32560
32917
  async newSession(evaluate) {
32561
32918
  const script = this.callScript("newSession");
@@ -32592,7 +32949,14 @@ var init_provider_adapter = __esm({
32592
32949
  return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
32593
32950
  }
32594
32951
  if (data && typeof data === "object") {
32595
- return data.switched === true || data.success === true || data.ok === true;
32952
+ if (data.switched === true || data.success === true || data.ok === true) return true;
32953
+ if (typeof data.error === "string" && data.error.trim()) return false;
32954
+ }
32955
+ for (let attempt = 0; attempt < 6; attempt += 1) {
32956
+ await new Promise((resolve16) => setTimeout(resolve16, 250));
32957
+ const state = await this.readChat(evaluate);
32958
+ const title = typeof state.title === "string" ? state.title : "";
32959
+ if (this.titlesMatch(title, sessionId)) return true;
32596
32960
  }
32597
32961
  return false;
32598
32962
  }
@@ -32807,7 +33171,7 @@ var init_manager2 = __esm({
32807
33171
  return false;
32808
33172
  }
32809
33173
  }
32810
- async resolveSessionAction(cdp, sessionId, action) {
33174
+ async resolveSessionAction(cdp, sessionId, action, button) {
32811
33175
  await this.ensureSessionPanelOpen(sessionId);
32812
33176
  const target = this.getSessionTarget(sessionId);
32813
33177
  if (!target?.parentSessionId) return false;
@@ -32817,7 +33181,7 @@ var init_manager2 = __esm({
32817
33181
  if (!agent) return false;
32818
33182
  try {
32819
33183
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
32820
- return await agent.adapter.resolveAction(evaluate, action);
33184
+ return await agent.adapter.resolveAction(evaluate, action, button);
32821
33185
  } catch (e) {
32822
33186
  this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
32823
33187
  return false;
@@ -47794,6 +48158,12 @@ var init_session_host_controller = __esm({
47794
48158
  payload: { sessionId, clientId }
47795
48159
  });
47796
48160
  }
48161
+ async pruneDuplicateSessions(payload = {}) {
48162
+ return this.request({
48163
+ type: "prune_duplicate_sessions",
48164
+ payload
48165
+ });
48166
+ }
47797
48167
  async acquireWrite(payload) {
47798
48168
  return this.request({
47799
48169
  type: "acquire_write",
@@ -47960,7 +48330,7 @@ var init_adhdev_daemon = __esm({
47960
48330
  import_ws3 = require("ws");
47961
48331
  import_chalk2 = __toESM(require("chalk"));
47962
48332
  init_version();
47963
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.28" });
48333
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.29" });
47964
48334
  DANGEROUS_PATTERNS = [
47965
48335
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
47966
48336
  /\bsudo\b/i,