adhdev 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/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() {
@@ -3232,6 +3440,59 @@ ${effect.notification.body || ""}`.trim();
3232
3440
  }
3233
3441
  });
3234
3442
 
3443
+ // ../../oss/packages/daemon-core/src/providers/approval-utils.ts
3444
+ function normalizeApprovalLabel(value) {
3445
+ return String(value || "").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
3446
+ }
3447
+ function getApprovalPositiveHints(provider) {
3448
+ const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
3449
+ return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
3450
+ }
3451
+ function pickApprovalButton(buttons, provider) {
3452
+ const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
3453
+ if (labels.length === 0) {
3454
+ return { index: 0, label: "Approve" };
3455
+ }
3456
+ const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
3457
+ const hints = getApprovalPositiveHints(provider);
3458
+ for (const hint of hints) {
3459
+ const exactIndex = normalizedButtons.findIndex((label) => label === hint);
3460
+ if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
3461
+ const prefixIndex = normalizedButtons.findIndex((label) => label.startsWith(hint));
3462
+ if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
3463
+ const includeIndex = normalizedButtons.findIndex((label) => label.includes(hint));
3464
+ if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
3465
+ }
3466
+ return { index: 0, label: labels[0] };
3467
+ }
3468
+ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
3469
+ const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
3470
+ const cleanMessage = String(modalMessage || "").trim();
3471
+ if (cleanMessage) lines.push(cleanMessage);
3472
+ return lines.join("\n");
3473
+ }
3474
+ var DEFAULT_APPROVAL_POSITIVE_HINTS;
3475
+ var init_approval_utils = __esm({
3476
+ "../../oss/packages/daemon-core/src/providers/approval-utils.ts"() {
3477
+ "use strict";
3478
+ DEFAULT_APPROVAL_POSITIVE_HINTS = [
3479
+ "run",
3480
+ "approve",
3481
+ "accept",
3482
+ "allow once",
3483
+ "always allow",
3484
+ "allow",
3485
+ "yes",
3486
+ "proceed",
3487
+ "continue",
3488
+ "confirm",
3489
+ "save",
3490
+ "ok",
3491
+ "trust"
3492
+ ];
3493
+ }
3494
+ });
3495
+
3235
3496
  // ../../oss/packages/daemon-core/src/providers/ide-provider-instance.ts
3236
3497
  var crypto2, IdeProviderInstance;
3237
3498
  var init_ide_provider_instance = __esm({
@@ -3243,6 +3504,7 @@ var init_ide_provider_instance = __esm({
3243
3504
  init_chat_history();
3244
3505
  init_logger();
3245
3506
  init_control_effects();
3507
+ init_approval_utils();
3246
3508
  IdeProviderInstance = class {
3247
3509
  type;
3248
3510
  category = "ide";
@@ -3309,6 +3571,8 @@ var init_ide_provider_instance = __esm({
3309
3571
  }
3310
3572
  getState() {
3311
3573
  const cdp = this.context?.cdp;
3574
+ const autoApproveActive = (this.currentStatus === "waiting_approval" || this.cachedChat?.status === "waiting_approval") && this.canAutoApprove();
3575
+ const visibleStatus = autoApproveActive ? "generating" : this.currentStatus;
3312
3576
  const extensionStates = [];
3313
3577
  for (const ext of this.extensions.values()) {
3314
3578
  extensionStates.push(ext.getState());
@@ -3317,13 +3581,13 @@ var init_ide_provider_instance = __esm({
3317
3581
  type: this.type,
3318
3582
  name: this.provider.name,
3319
3583
  category: "ide",
3320
- status: this.currentStatus,
3584
+ status: visibleStatus,
3321
3585
  activeChat: this.cachedChat ? {
3322
3586
  id: this.cachedChat.id || "active_session",
3323
3587
  title: this.cachedChat.title || this.type,
3324
- status: this.cachedChat.status || this.currentStatus,
3588
+ status: autoApproveActive && this.cachedChat.status === "waiting_approval" ? "generating" : this.cachedChat.status || visibleStatus,
3325
3589
  messages: this.mergeConversationMessages(this.cachedChat.messages || []),
3326
- activeModal: this.cachedChat.activeModal || null,
3590
+ activeModal: autoApproveActive ? null : this.cachedChat.activeModal || null,
3327
3591
  inputContent: this.cachedChat.inputContent || ""
3328
3592
  } : null,
3329
3593
  workspace: this.workspace || null,
@@ -3542,7 +3806,9 @@ var init_ide_provider_instance = __esm({
3542
3806
  const chatStatus = chatData?.status;
3543
3807
  if (!chatStatus) return;
3544
3808
  const agentKey = `${this.type}:native`;
3545
- const agentStatus = chatStatus === "streaming" || chatStatus === "generating" ? "generating" : chatStatus === "waiting_approval" ? "waiting_approval" : "idle";
3809
+ const rawAgentStatus = chatStatus === "streaming" || chatStatus === "generating" ? "generating" : chatStatus === "waiting_approval" ? "waiting_approval" : "idle";
3810
+ const autoApproveActive = rawAgentStatus === "waiting_approval" && this.canAutoApprove();
3811
+ const agentStatus = autoApproveActive ? "generating" : rawAgentStatus;
3546
3812
  const lastMsg = Array.isArray(chatData?.messages) && chatData.messages.length > 0 ? chatData.messages[chatData.messages.length - 1] : null;
3547
3813
  const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
3548
3814
  this.currentStatus = agentStatus;
@@ -3574,7 +3840,7 @@ var init_ide_provider_instance = __esm({
3574
3840
  this.applyProviderResponse(chatData, {
3575
3841
  phase: agentStatus === "idle" && (lastStatus === "generating" || lastStatus === "waiting_approval") ? "turn_completed" : "immediate"
3576
3842
  });
3577
- if (agentStatus === "waiting_approval" && this.settings.autoApprove && !this.autoApproveBusy) {
3843
+ if (rawAgentStatus === "waiting_approval" && autoApproveActive && !this.autoApproveBusy) {
3578
3844
  this.autoApproveViaScript(chatData);
3579
3845
  }
3580
3846
  const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
@@ -3725,6 +3991,9 @@ ${effect.notification.body || ""}`.trim();
3725
3991
  updateCdp(cdp) {
3726
3992
  if (this.context) this.context.cdp = cdp;
3727
3993
  }
3994
+ canAutoApprove() {
3995
+ return this.settings.autoApprove !== false && typeof this.provider.scripts?.resolveAction === "function" && !!this.context?.cdp?.isConnected;
3996
+ }
3728
3997
  // ─── Auto-approve via CDP script ────────────────────
3729
3998
  async autoApproveViaScript(_chatData) {
3730
3999
  const cdp = this.context?.cdp;
@@ -3736,17 +4005,15 @@ ${effect.notification.body || ""}`.trim();
3736
4005
  }
3737
4006
  this.autoApproveBusy = true;
3738
4007
  try {
3739
- let targetButton = _chatData?.activeModal?.buttons?.[0] || "Run";
3740
- const buttons = _chatData?.activeModal?.buttons || [];
3741
- for (const b of buttons) {
3742
- const lower = String(b).toLowerCase().replace(/[^\w]/g, "");
3743
- if (/^(run|approve|accept|yes|allow|always|proceed|save)/.test(lower)) {
3744
- targetButton = b;
3745
- break;
3746
- }
3747
- }
4008
+ const { label: targetButton } = pickApprovalButton(_chatData?.activeModal?.buttons, this.provider);
3748
4009
  const script = scriptFn({ action: "approve", button: targetButton, buttonText: targetButton });
3749
4010
  if (!script) return;
4011
+ const now = Date.now();
4012
+ this.appendRuntimeSystemMessage(
4013
+ formatAutoApprovalMessage(_chatData?.activeModal?.message, targetButton),
4014
+ `auto_approval:${now}:${targetButton}`,
4015
+ now
4016
+ );
3750
4017
  LOG.info("IdeInstance", `[IdeInstance:${this.type}] autoApprove: executing resolveAction for "${targetButton}"`);
3751
4018
  let rawResult = await cdp.evaluate(script, 1e4);
3752
4019
  if (typeof rawResult === "string") {
@@ -3768,12 +4035,6 @@ ${effect.notification.body || ""}`.trim();
3768
4035
  LOG.warn("IdeInstance", `[IdeInstance:${this.type}] autoApprove: cdp.send() not available for coordinate click`);
3769
4036
  }
3770
4037
  }
3771
- this.pushEvent({
3772
- event: "agent:auto_approved",
3773
- chatTitle: _chatData?.title || this.provider.name,
3774
- timestamp: Date.now(),
3775
- ideType: this.type
3776
- });
3777
4038
  } catch (e) {
3778
4039
  LOG.warn("IdeInstance", `[IdeInstance:${this.type}] autoApprove error: ${e?.message}`);
3779
4040
  } finally {
@@ -4713,6 +4974,46 @@ function didProviderConfirmSend(result) {
4713
4974
  if (!parsed || typeof parsed !== "object") return false;
4714
4975
  return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
4715
4976
  }
4977
+ async function readExtensionChatState(h) {
4978
+ try {
4979
+ const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
4980
+ if (!evalResult?.result) return null;
4981
+ const parsed = parseMaybeJson(evalResult.result);
4982
+ return parsed && typeof parsed === "object" ? parsed : null;
4983
+ } catch {
4984
+ return null;
4985
+ }
4986
+ }
4987
+ function getStateMessageCount(state) {
4988
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
4989
+ }
4990
+ function getStateLastSignature(state) {
4991
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
4992
+ const last = messages[messages.length - 1];
4993
+ if (!last) return "";
4994
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
4995
+ }
4996
+ async function getStableExtensionBaseline(h) {
4997
+ const first = await readExtensionChatState(h);
4998
+ if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
4999
+ await new Promise((resolve16) => setTimeout(resolve16, 150));
5000
+ const second = await readExtensionChatState(h);
5001
+ return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
5002
+ }
5003
+ async function verifyExtensionSendObserved(h, before) {
5004
+ const beforeCount = getStateMessageCount(before);
5005
+ const beforeSignature = getStateLastSignature(before);
5006
+ for (let attempt = 0; attempt < 12; attempt += 1) {
5007
+ await new Promise((resolve16) => setTimeout(resolve16, 250));
5008
+ const state = await readExtensionChatState(h);
5009
+ if (state?.status === "waiting_approval") return true;
5010
+ const afterCount = getStateMessageCount(state);
5011
+ const afterSignature = getStateLastSignature(state);
5012
+ if (afterCount > beforeCount) return true;
5013
+ if (afterSignature && afterSignature !== beforeSignature) return true;
5014
+ }
5015
+ return false;
5016
+ }
4716
5017
  async function handleChatHistory(h, args) {
4717
5018
  const { agentType, offset, limit } = args;
4718
5019
  const historySessionId = getHistorySessionId(h, args);
@@ -4893,12 +5194,17 @@ async function handleSendChat(h, args) {
4893
5194
  if (isExtensionTransport(transport)) {
4894
5195
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
4895
5196
  try {
5197
+ const beforeState = await getStableExtensionBaseline(h);
4896
5198
  const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
4897
5199
  if (evalResult?.result) {
4898
5200
  const parsed = parseMaybeJson(evalResult.result);
4899
5201
  if (didProviderConfirmSend(parsed)) {
4900
- _log(`Extension script sent OK`);
4901
- return _logSendSuccess("extension-script");
5202
+ const observed = await verifyExtensionSendObserved(h, beforeState);
5203
+ if (observed) {
5204
+ _log(`Extension script sent OK`);
5205
+ return _logSendSuccess("extension-script");
5206
+ }
5207
+ _log(`Extension script reported send but no chat-state change was observed`);
4902
5208
  }
4903
5209
  if (parsed?.needsTypeAndSend) {
4904
5210
  _log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
@@ -5403,7 +5709,7 @@ async function handleResolveAction(h, args) {
5403
5709
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
5404
5710
  }
5405
5711
  if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5406
- const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
5712
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
5407
5713
  return { success: ok };
5408
5714
  }
5409
5715
  if (transport === "acp") {
@@ -7098,10 +7404,12 @@ function sanitizeSpawnEnv(baseEnv, overrides) {
7098
7404
  env[key] = value;
7099
7405
  }
7100
7406
  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_")) {
7407
+ 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
7408
  delete env[key];
7103
7409
  }
7104
7410
  }
7411
+ delete env.CODEX_THREAD_ID;
7412
+ delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;
7105
7413
  applyTerminalColorEnv(env);
7106
7414
  return env;
7107
7415
  }
@@ -8183,6 +8491,9 @@ var init_provider_cli_adapter = __esm({
8183
8491
  looksLikeVisibleIdlePrompt(screenText) {
8184
8492
  const text = String(screenText || "");
8185
8493
  if (!text.trim()) return false;
8494
+ 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)) {
8495
+ return true;
8496
+ }
8186
8497
  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
8498
  }
8188
8499
  findLastMatchingLineIndex(lines, predicate) {
@@ -9368,6 +9679,7 @@ var init_cli_provider_instance = __esm({
9368
9679
  init_chat_history();
9369
9680
  init_logger();
9370
9681
  init_control_effects();
9682
+ init_approval_utils();
9371
9683
  CachedDatabaseSync = null;
9372
9684
  CliProviderInstance = class {
9373
9685
  constructor(provider, workingDir, cliArgs = [], instanceId, transportFactory, options) {
@@ -9401,6 +9713,7 @@ var init_cli_provider_instance = __esm({
9401
9713
  historyWriter;
9402
9714
  runtimeMessages = [];
9403
9715
  instanceId;
9716
+ suppressIdleHistoryReplay = false;
9404
9717
  presentationMode;
9405
9718
  providerSessionId;
9406
9719
  launchMode;
@@ -9428,7 +9741,15 @@ var init_cli_provider_instance = __esm({
9428
9741
  await this.adapter.spawn();
9429
9742
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
9430
9743
  if (this.providerSessionId) {
9744
+ this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
9431
9745
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
9746
+ this.historyWriter.seedSessionHistory(
9747
+ this.type,
9748
+ restoredHistory.messages,
9749
+ this.providerSessionId,
9750
+ this.instanceId
9751
+ );
9752
+ this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
9432
9753
  if (restoredHistory.messages.length > 0) {
9433
9754
  this.adapter.seedCommittedMessages(
9434
9755
  restoredHistory.messages.map((message) => ({
@@ -9472,7 +9793,7 @@ var init_cli_provider_instance = __esm({
9472
9793
  } else if (this.type === "codex-cli") {
9473
9794
  probedSessionId = this.probeSessionIdFromConfig({
9474
9795
  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",
9796
+ query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
9476
9797
  timestampFormat: "unix_s"
9477
9798
  });
9478
9799
  } else if (this.type === "goose-cli") {
@@ -9516,6 +9837,8 @@ var init_cli_provider_instance = __esm({
9516
9837
  getState() {
9517
9838
  const adapterStatus = this.adapter.getStatus();
9518
9839
  const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
9840
+ const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
9841
+ const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
9519
9842
  const parsedProviderSessionId = typeof parsedStatus?.providerSessionId === "string" ? parsedStatus.providerSessionId.trim() : "";
9520
9843
  if (parsedProviderSessionId) {
9521
9844
  this.promoteProviderSessionId(parsedProviderSessionId);
@@ -9532,6 +9855,7 @@ var init_cli_provider_instance = __esm({
9532
9855
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
9533
9856
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
9534
9857
  if (parsedMessages.length > 0) {
9858
+ const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
9535
9859
  let messagesToSave = parsedMessages;
9536
9860
  if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
9537
9861
  const lastIdx = messagesToSave.length - 1;
@@ -9539,7 +9863,7 @@ var init_cli_provider_instance = __esm({
9539
9863
  messagesToSave = messagesToSave.slice(0, lastIdx);
9540
9864
  }
9541
9865
  }
9542
- if (messagesToSave.length > 0) {
9866
+ if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
9543
9867
  this.historyWriter.appendNewMessages(
9544
9868
  this.type,
9545
9869
  messagesToSave,
@@ -9554,14 +9878,14 @@ var init_cli_provider_instance = __esm({
9554
9878
  type: this.type,
9555
9879
  name: this.provider.name,
9556
9880
  category: "cli",
9557
- status: adapterStatus.status,
9881
+ status: visibleStatus,
9558
9882
  mode: this.presentationMode,
9559
9883
  activeChat: {
9560
9884
  id: `${this.type}_${this.workingDir}`,
9561
9885
  title: parsedStatus?.title || dirName,
9562
- status: parsedStatus?.status || adapterStatus.status,
9886
+ status: autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
9563
9887
  messages: mergedMessages,
9564
- activeModal: parsedStatus?.activeModal ?? adapterStatus.activeModal,
9888
+ activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
9565
9889
  inputContent: ""
9566
9890
  },
9567
9891
  workspace: this.workingDir,
@@ -9625,7 +9949,16 @@ var init_cli_provider_instance = __esm({
9625
9949
  const now = Date.now();
9626
9950
  const adapterStatus = this.adapter.getStatus();
9627
9951
  const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
9628
- const newStatus = adapterStatus.status;
9952
+ const rawStatus = adapterStatus.status;
9953
+ const autoApproveActive = rawStatus === "waiting_approval" && this.shouldAutoApprove();
9954
+ if (autoApproveActive) {
9955
+ const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(adapterStatus.activeModal?.buttons, this.provider);
9956
+ this.recordAutoApproval(adapterStatus.activeModal?.message, buttonLabel, now);
9957
+ setTimeout(() => {
9958
+ this.adapter.resolveModal(buttonIndex);
9959
+ }, 0);
9960
+ }
9961
+ const newStatus = autoApproveActive ? "generating" : rawStatus;
9629
9962
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
9630
9963
  const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
9631
9964
  const partial2 = this.adapter.getPartialResponse();
@@ -9634,6 +9967,7 @@ var init_cli_provider_instance = __esm({
9634
9967
  if (newStatus !== this.lastStatus) {
9635
9968
  LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
9636
9969
  if (this.lastStatus === "idle" && newStatus === "generating") {
9970
+ this.suppressIdleHistoryReplay = false;
9637
9971
  if (this.completedDebouncePending) {
9638
9972
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
9639
9973
  if (this.completedDebounceTimer) {
@@ -9653,6 +9987,7 @@ var init_cli_provider_instance = __esm({
9653
9987
  this.generatingDebounceTimer = null;
9654
9988
  }, 1e3);
9655
9989
  } else if (newStatus === "waiting_approval") {
9990
+ this.suppressIdleHistoryReplay = false;
9656
9991
  if (this.generatingDebouncePending) {
9657
9992
  if (this.generatingDebounceTimer) {
9658
9993
  clearTimeout(this.generatingDebounceTimer);
@@ -9833,6 +10168,16 @@ ${effect.notification.body || ""}`.trim();
9833
10168
  get cliName() {
9834
10169
  return this.provider.name;
9835
10170
  }
10171
+ shouldAutoApprove() {
10172
+ return this.settings.autoApprove !== false;
10173
+ }
10174
+ recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
10175
+ this.appendRuntimeSystemMessage(
10176
+ formatAutoApprovalMessage(modalMessage, buttonLabel),
10177
+ `auto_approval:${now}:${buttonLabel || "approve"}`,
10178
+ now
10179
+ );
10180
+ }
9836
10181
  recordApprovalSelection(buttonText) {
9837
10182
  const cleanButton = String(buttonText || "").trim();
9838
10183
  if (!cleanButton) return;
@@ -26718,8 +27063,10 @@ var init_acp_provider_instance = __esm({
26718
27063
  input: tc.rawInput ? typeof tc.rawInput === "string" ? tc.rawInput : JSON.stringify(tc.rawInput) : void 0
26719
27064
  });
26720
27065
  }
26721
- if (this.settings.autoApprove) {
26722
- this.log.info(`[${this.type}] Auto-approving: ${tc.title || tc.toolCallId}`);
27066
+ if (this.settings.autoApprove !== false) {
27067
+ const toolTitle = tc.title || tc.toolCallId || "tool call";
27068
+ this.log.info(`[${this.type}] Auto-approving: ${toolTitle}`);
27069
+ this.appendSystemMessage(`Auto-approved: ${toolTitle}`);
26723
27070
  const allowOption = params.options.find((o) => o.kind === "allow_once") || params.options.find((o) => o.kind === "allow_always");
26724
27071
  if (allowOption) {
26725
27072
  return { outcome: { outcome: "selected", optionId: allowOption.optionId } };
@@ -27171,6 +27518,18 @@ var init_acp_provider_instance = __esm({
27171
27518
  this.events.push(event);
27172
27519
  if (this.events.length > 50) this.events = this.events.slice(-50);
27173
27520
  }
27521
+ appendSystemMessage(content, timestamp = Date.now()) {
27522
+ const normalizedContent = String(content || "").trim();
27523
+ if (!normalizedContent) return;
27524
+ this.messages.push({
27525
+ role: "system",
27526
+ content: normalizedContent,
27527
+ timestamp
27528
+ });
27529
+ if (this.messages.length > 200) {
27530
+ this.messages = this.messages.slice(-100);
27531
+ }
27532
+ }
27174
27533
  flushEvents() {
27175
27534
  const events = [...this.events];
27176
27535
  this.events = [];
@@ -27696,6 +28055,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
27696
28055
  if (!instanceManager) return 0;
27697
28056
  const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
27698
28057
  let restored = 0;
28058
+ const restoredBindings = /* @__PURE__ */ new Set();
27699
28059
  for (const record2 of sessions) {
27700
28060
  if (!record2?.runtimeId || !record2?.cliType || !record2?.workspace) continue;
27701
28061
  if (this.adapters.has(record2.runtimeId) || instanceManager.getInstance(record2.runtimeId)) continue;
@@ -27709,6 +28069,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
27709
28069
  record2.cliArgs,
27710
28070
  record2.providerSessionId
27711
28071
  );
28072
+ const bindingKey = [
28073
+ normalizedType,
28074
+ record2.workspace,
28075
+ sessionBinding.providerSessionId || record2.runtimeId
28076
+ ].join("::");
28077
+ if (restoredBindings.has(bindingKey)) {
28078
+ LOG.info(
28079
+ "CLI",
28080
+ `\u21B7 Skipping duplicate hosted runtime restore: ${record2.runtimeKey || record2.runtimeId} (${normalizedType} @ ${record2.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
28081
+ );
28082
+ continue;
28083
+ }
27712
28084
  try {
27713
28085
  await this.registerCliInstance(
27714
28086
  record2.runtimeId,
@@ -27724,6 +28096,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
27724
28096
  launchMode: "manual"
27725
28097
  }
27726
28098
  );
28099
+ restoredBindings.add(bindingKey);
27727
28100
  restored += 1;
27728
28101
  LOG.info("CLI", `\u267B Restored hosted runtime: ${record2.runtimeKey || record2.runtimeId} (${record2.displayName || record2.workspace})`);
27729
28102
  } catch (error48) {
@@ -30500,7 +30873,7 @@ var init_provider_loader = __esm({
30500
30873
  */
30501
30874
  getSettingValue(type, key) {
30502
30875
  const schemaDef = this.getSettingsSchema(type)[key];
30503
- const defaultVal = schemaDef ? schemaDef.default : void 0;
30876
+ const defaultVal = schemaDef ? key === "autoApprove" && schemaDef.type === "boolean" ? true : schemaDef.default : void 0;
30504
30877
  try {
30505
30878
  const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
30506
30879
  const config2 = loadConfig2();
@@ -30559,13 +30932,32 @@ var init_provider_loader = __esm({
30559
30932
  getSettingsSchema(type) {
30560
30933
  const provider = this.providers.get(type);
30561
30934
  if (!provider) return {};
30562
- return {
30935
+ const result = {
30563
30936
  ...this.getSyntheticSettings(type, provider),
30564
30937
  ...provider.settings || {}
30565
30938
  };
30939
+ if (result.autoApprove?.type === "boolean") {
30940
+ result.autoApprove = {
30941
+ ...result.autoApprove,
30942
+ default: true,
30943
+ public: true,
30944
+ label: result.autoApprove.label || "Auto Approve",
30945
+ description: result.autoApprove.description || "Automatically approve actionable prompts without sending approval alerts."
30946
+ };
30947
+ }
30948
+ return result;
30566
30949
  }
30567
30950
  getSyntheticSettings(type, provider) {
30568
30951
  const result = {};
30952
+ if (!provider.settings?.autoApprove) {
30953
+ result.autoApprove = {
30954
+ type: "boolean",
30955
+ default: true,
30956
+ public: true,
30957
+ label: "Auto Approve",
30958
+ description: "Automatically approve actionable prompts without sending approval alerts."
30959
+ };
30960
+ }
30569
30961
  if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
30570
30962
  result.executablePath = {
30571
30963
  type: "string",
@@ -31876,6 +32268,15 @@ var init_router = __esm({
31876
32268
  const record2 = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
31877
32269
  return { success: true, record: record2 };
31878
32270
  }
32271
+ case "session_host_prune_duplicate_sessions": {
32272
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
32273
+ const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
32274
+ providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
32275
+ workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
32276
+ dryRun: args?.dryRun === true
32277
+ });
32278
+ return { success: true, result };
32279
+ }
31879
32280
  case "session_host_acquire_write": {
31880
32281
  if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
31881
32282
  const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
@@ -32479,6 +32880,51 @@ var init_provider_adapter = __esm({
32479
32880
  isTransportError(reason) {
32480
32881
  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
32882
  }
32883
+ titlesMatch(actual, expected) {
32884
+ const lhs = actual.trim().toLowerCase();
32885
+ const rhs = expected.trim().toLowerCase();
32886
+ if (!lhs || !rhs) return false;
32887
+ return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
32888
+ }
32889
+ messageCount(state) {
32890
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
32891
+ }
32892
+ lastMessageSignature(state) {
32893
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
32894
+ const last = messages[messages.length - 1];
32895
+ if (!last) return "";
32896
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
32897
+ }
32898
+ async verifySendOutcome(evaluate, before) {
32899
+ const beforeCount = this.messageCount(before);
32900
+ const beforeSignature = this.lastMessageSignature(before);
32901
+ for (let attempt = 0; attempt < 12; attempt += 1) {
32902
+ await new Promise((resolve16) => setTimeout(resolve16, 250));
32903
+ let state;
32904
+ try {
32905
+ state = await this.readChat(evaluate);
32906
+ } catch {
32907
+ continue;
32908
+ }
32909
+ if (state.status === "waiting_approval") {
32910
+ return true;
32911
+ }
32912
+ const afterCount = this.messageCount(state);
32913
+ const afterSignature = this.lastMessageSignature(state);
32914
+ if (afterCount > beforeCount) return true;
32915
+ if (afterSignature && afterSignature !== beforeSignature) return true;
32916
+ }
32917
+ return false;
32918
+ }
32919
+ async readStableBaselineState(evaluate) {
32920
+ const first = await this.readChat(evaluate);
32921
+ if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
32922
+ return first;
32923
+ }
32924
+ await new Promise((resolve16) => setTimeout(resolve16, 150));
32925
+ const second = await this.readChat(evaluate);
32926
+ return this.messageCount(second) >= this.messageCount(first) ? second : first;
32927
+ }
32482
32928
  async readChat(evaluate) {
32483
32929
  const script = this.callScript("readChat");
32484
32930
  if (!script) return this.errorState("readChat script not available");
@@ -32504,6 +32950,9 @@ var init_provider_adapter = __esm({
32504
32950
  mode: data.mode,
32505
32951
  activeModal: data.activeModal
32506
32952
  };
32953
+ if (typeof data.title === "string" && data.title.trim()) {
32954
+ state.title = data.title.trim();
32955
+ }
32507
32956
  const controlValues = extractProviderControlValues(this.provider.controls, data);
32508
32957
  if (controlValues) state.controlValues = controlValues;
32509
32958
  const effects = normalizeProviderEffects(data);
@@ -32527,6 +32976,12 @@ var init_provider_adapter = __esm({
32527
32976
  }
32528
32977
  }
32529
32978
  async sendMessage(evaluate, text) {
32979
+ let beforeState = null;
32980
+ try {
32981
+ beforeState = await this.readStableBaselineState(evaluate);
32982
+ } catch {
32983
+ beforeState = null;
32984
+ }
32530
32985
  const params = { message: text };
32531
32986
  const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
32532
32987
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
@@ -32544,7 +32999,9 @@ var init_provider_adapter = __esm({
32544
32999
  }
32545
33000
  if (parsed && typeof parsed === "object") {
32546
33001
  if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
32547
- return;
33002
+ const verified = await this.verifySendOutcome(evaluate, beforeState);
33003
+ if (verified) return;
33004
+ throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
32548
33005
  }
32549
33006
  if (typeof parsed.error === "string" && parsed.error.trim()) {
32550
33007
  throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
@@ -32555,7 +33012,15 @@ var init_provider_adapter = __esm({
32555
33012
  async resolveAction(evaluate, action, button) {
32556
33013
  const script = this.callScript("resolveAction", { action, button });
32557
33014
  if (!script) return false;
32558
- return await evaluate(script) === true;
33015
+ const result = await evaluate(script);
33016
+ const parsed = this.parseMaybeJson(result);
33017
+ if (parsed === true) return true;
33018
+ if (typeof parsed === "string") {
33019
+ const normalized = parsed.trim().toLowerCase();
33020
+ return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
33021
+ }
33022
+ if (!parsed || typeof parsed !== "object") return false;
33023
+ return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
32559
33024
  }
32560
33025
  async newSession(evaluate) {
32561
33026
  const script = this.callScript("newSession");
@@ -32592,7 +33057,14 @@ var init_provider_adapter = __esm({
32592
33057
  return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
32593
33058
  }
32594
33059
  if (data && typeof data === "object") {
32595
- return data.switched === true || data.success === true || data.ok === true;
33060
+ if (data.switched === true || data.success === true || data.ok === true) return true;
33061
+ if (typeof data.error === "string" && data.error.trim()) return false;
33062
+ }
33063
+ for (let attempt = 0; attempt < 6; attempt += 1) {
33064
+ await new Promise((resolve16) => setTimeout(resolve16, 250));
33065
+ const state = await this.readChat(evaluate);
33066
+ const title = typeof state.title === "string" ? state.title : "";
33067
+ if (this.titlesMatch(title, sessionId)) return true;
32596
33068
  }
32597
33069
  return false;
32598
33070
  }
@@ -32807,7 +33279,7 @@ var init_manager2 = __esm({
32807
33279
  return false;
32808
33280
  }
32809
33281
  }
32810
- async resolveSessionAction(cdp, sessionId, action) {
33282
+ async resolveSessionAction(cdp, sessionId, action, button) {
32811
33283
  await this.ensureSessionPanelOpen(sessionId);
32812
33284
  const target = this.getSessionTarget(sessionId);
32813
33285
  if (!target?.parentSessionId) return false;
@@ -32817,7 +33289,7 @@ var init_manager2 = __esm({
32817
33289
  if (!agent) return false;
32818
33290
  try {
32819
33291
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
32820
- return await agent.adapter.resolveAction(evaluate, action);
33292
+ return await agent.adapter.resolveAction(evaluate, action, button);
32821
33293
  } catch (e) {
32822
33294
  this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
32823
33295
  return false;
@@ -32932,6 +33404,7 @@ var init_poller = __esm({
32932
33404
  init_setup();
32933
33405
  init_reconcile();
32934
33406
  init_logger();
33407
+ init_approval_utils();
32935
33408
  AgentStreamPoller = class {
32936
33409
  deps;
32937
33410
  timer = null;
@@ -33062,7 +33535,43 @@ var init_poller = __esm({
33062
33535
  }
33063
33536
  try {
33064
33537
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
33065
- const stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
33538
+ let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
33539
+ if (stream?.status === "waiting_approval") {
33540
+ const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
33541
+ if (autoApprove && resolvedActiveSessionId) {
33542
+ const provider = providerLoader.getMeta(stream.agentType);
33543
+ const { label: buttonLabel } = pickApprovalButton(stream.activeModal?.buttons, provider);
33544
+ const approved = await agentStreamManager.resolveSessionAction(cdp, resolvedActiveSessionId, "approve", buttonLabel);
33545
+ if (approved) {
33546
+ const effectId = [
33547
+ "auto_approval",
33548
+ resolvedActiveSessionId,
33549
+ String(stream.messages?.length || 0),
33550
+ buttonLabel,
33551
+ String(stream.activeModal?.message || "").trim()
33552
+ ].join(":");
33553
+ stream = {
33554
+ ...stream,
33555
+ status: "streaming",
33556
+ activeModal: void 0,
33557
+ effects: [
33558
+ ...stream.effects || [],
33559
+ {
33560
+ type: "message",
33561
+ id: effectId,
33562
+ persist: true,
33563
+ message: {
33564
+ role: "system",
33565
+ senderName: "System",
33566
+ kind: "system",
33567
+ content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
33568
+ }
33569
+ }
33570
+ ]
33571
+ };
33572
+ }
33573
+ }
33574
+ }
33066
33575
  this.deps.onStreamsUpdated?.(ideType, stream ? [stream] : []);
33067
33576
  } catch {
33068
33577
  }
@@ -47794,6 +48303,12 @@ var init_session_host_controller = __esm({
47794
48303
  payload: { sessionId, clientId }
47795
48304
  });
47796
48305
  }
48306
+ async pruneDuplicateSessions(payload = {}) {
48307
+ return this.request({
48308
+ type: "prune_duplicate_sessions",
48309
+ payload
48310
+ });
48311
+ }
47797
48312
  async acquireWrite(payload) {
47798
48313
  return this.request({
47799
48314
  type: "acquire_write",
@@ -47960,7 +48475,7 @@ var init_adhdev_daemon = __esm({
47960
48475
  import_ws3 = require("ws");
47961
48476
  import_chalk2 = __toESM(require("chalk"));
47962
48477
  init_version();
47963
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.28" });
48478
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.30" });
47964
48479
  DANGEROUS_PATTERNS = [
47965
48480
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
47966
48481
  /\bsudo\b/i,