adhdev 0.8.22 → 0.8.24

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
@@ -84,7 +84,8 @@ function normalizeConfig(raw) {
84
84
  providerSettings: isPlainObject(parsed.providerSettings) ? parsed.providerSettings : {},
85
85
  ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
86
86
  disableUpstream: asBoolean(parsed.disableUpstream, DEFAULT_CONFIG.disableUpstream ?? false),
87
- providerDir: asOptionalString(parsed.providerDir)
87
+ providerDir: asOptionalString(parsed.providerDir),
88
+ terminalSizingMode: parsed.terminalSizingMode === "fit" ? "fit" : "measured"
88
89
  };
89
90
  }
90
91
  function generateMachineId() {
@@ -232,7 +233,8 @@ var init_config = __esm({
232
233
  registeredMachineId: void 0,
233
234
  providerSettings: {},
234
235
  ideSettings: {},
235
- disableUpstream: false
236
+ disableUpstream: false,
237
+ terminalSizingMode: "measured"
236
238
  };
237
239
  MACHINE_ID_PREFIX = "mach_";
238
240
  }
@@ -2457,204 +2459,97 @@ var init_status_monitor = __esm({
2457
2459
  }
2458
2460
  });
2459
2461
 
2460
- // ../../oss/packages/daemon-core/src/providers/extension-provider-instance.ts
2461
- var ExtensionProviderInstance;
2462
- var init_extension_provider_instance = __esm({
2463
- "../../oss/packages/daemon-core/src/providers/extension-provider-instance.ts"() {
2464
- "use strict";
2465
- init_status_monitor();
2466
- ExtensionProviderInstance = class {
2467
- type;
2468
- category = "extension";
2469
- provider;
2470
- context = null;
2471
- settings = {};
2472
- events = [];
2473
- // status
2474
- currentStatus = "idle";
2475
- agentStreams = [];
2476
- messages = [];
2477
- activeModal = null;
2478
- currentModel = "";
2479
- currentMode = "";
2480
- controlValues = {};
2481
- lastAgentStatus = "idle";
2482
- generatingStartedAt = 0;
2483
- monitor;
2484
- // meta
2485
- instanceId;
2486
- ideType = "";
2487
- chatId = null;
2488
- chatTitle = null;
2489
- agentName = "";
2490
- extensionId = "";
2491
- constructor(provider) {
2492
- this.type = provider.type;
2493
- this.provider = provider;
2494
- this.instanceId = crypto.randomUUID();
2495
- this.monitor = new StatusMonitor();
2496
- }
2497
- // ─── Lifecycle ──────────────────────────────────
2498
- async init(context) {
2499
- this.context = context;
2500
- this.settings = context.settings || {};
2501
- this.monitor.updateConfig({
2502
- approvalAlert: this.settings.approvalAlert !== false,
2503
- longGeneratingAlert: this.settings.longGeneratingAlert !== false,
2504
- longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
2505
- });
2506
- }
2507
- async onTick() {
2508
- if (!this.context?.cdp?.isConnected) return;
2509
- }
2510
- getState() {
2511
- return {
2512
- type: this.type,
2513
- name: this.provider.name,
2514
- category: "extension",
2515
- status: this.currentStatus,
2516
- activeChat: this.messages.length > 0 ? {
2517
- id: this.chatId || this.instanceId,
2518
- title: this.chatTitle || this.agentName || this.provider.name,
2519
- status: this.currentStatus,
2520
- messages: this.messages,
2521
- activeModal: this.activeModal,
2522
- inputContent: ""
2523
- } : null,
2524
- currentModel: this.currentModel || void 0,
2525
- currentPlan: this.currentMode || void 0,
2526
- controlValues: this.controlValues,
2527
- providerControls: this.provider.controls,
2528
- agentStreams: this.agentStreams,
2529
- instanceId: this.instanceId,
2530
- lastUpdated: Date.now(),
2531
- settings: this.settings,
2532
- pendingEvents: this.flushEvents()
2533
- };
2534
- }
2535
- onEvent(event, data) {
2536
- if (event === "stream_update") {
2537
- if (data?.streams) this.agentStreams = data.streams;
2538
- if (data?.messages) this.messages = data.messages;
2539
- if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
2540
- if (data?.model) this.currentModel = data.model;
2541
- if (data?.mode) this.currentMode = data.mode;
2542
- if (data?.controlValues) this.controlValues = data.controlValues;
2543
- if (typeof data?.sessionId === "string" && data.sessionId.trim()) this.chatId = data.sessionId;
2544
- if (typeof data?.title === "string" && data.title.trim()) this.chatTitle = data.title;
2545
- if (typeof data?.agentName === "string" && data.agentName.trim()) this.agentName = data.agentName;
2546
- if (typeof data?.extensionId === "string" && data.extensionId.trim()) this.extensionId = data.extensionId;
2547
- if (data?.status) {
2548
- const newStatus = data.status;
2549
- this.detectTransition(newStatus, data);
2550
- this.currentStatus = newStatus;
2551
- }
2552
- } else if (event === "stream_reset") {
2553
- this.resetStreamState();
2554
- } else if (event === "extension_connected") {
2555
- this.ideType = data?.ideType || "";
2556
- }
2557
- }
2558
- dispose() {
2559
- this.agentStreams = [];
2560
- this.messages = [];
2561
- this.monitor.reset();
2562
- }
2563
- /** Query UUID instanceId */
2564
- getInstanceId() {
2565
- return this.instanceId;
2566
- }
2567
- // ─── status transition detect ──────────────────────────────
2568
- detectTransition(newStatus, data) {
2569
- const now = Date.now();
2570
- const agentStatus = newStatus === "streaming" || newStatus === "generating" ? "generating" : newStatus === "waiting_approval" ? "waiting_approval" : "idle";
2571
- const lastMsg = Array.isArray(data?.messages) && data.messages.length > 0 ? data.messages[data.messages.length - 1] : null;
2572
- const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
2573
- if (agentStatus !== this.lastAgentStatus) {
2574
- if (this.lastAgentStatus === "idle" && agentStatus === "generating") {
2575
- this.generatingStartedAt = now;
2576
- this.pushEvent({
2577
- event: "agent:generating_started",
2578
- chatTitle: this.resolveChatTitle(data),
2579
- timestamp: now,
2580
- ideType: this.ideType || this.type,
2581
- agentType: this.type,
2582
- agentName: this.agentName || this.provider.name,
2583
- extensionId: this.extensionId || this.type
2584
- });
2585
- } else if (agentStatus === "waiting_approval") {
2586
- if (!this.generatingStartedAt) this.generatingStartedAt = now;
2587
- this.pushEvent({
2588
- event: "agent:waiting_approval",
2589
- chatTitle: this.resolveChatTitle(data),
2590
- timestamp: now,
2591
- ideType: this.ideType || this.type,
2592
- agentType: this.type,
2593
- agentName: this.agentName || this.provider.name,
2594
- extensionId: this.extensionId || this.type,
2595
- modalMessage: data?.activeModal?.message,
2596
- modalButtons: data?.activeModal?.buttons
2597
- });
2598
- } else if (agentStatus === "idle" && (this.lastAgentStatus === "generating" || this.lastAgentStatus === "waiting_approval")) {
2599
- const duration3 = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
2600
- this.pushEvent({
2601
- event: "agent:generating_completed",
2602
- chatTitle: this.resolveChatTitle(data),
2603
- duration: duration3,
2604
- timestamp: now,
2605
- ideType: this.ideType || this.type,
2606
- agentType: this.type,
2607
- agentName: this.agentName || this.provider.name,
2608
- extensionId: this.extensionId || this.type
2609
- });
2610
- this.generatingStartedAt = 0;
2611
- }
2612
- this.lastAgentStatus = agentStatus;
2462
+ // ../../oss/packages/daemon-core/src/providers/control-effects.ts
2463
+ function extractProviderControlValues(controls, data) {
2464
+ if (!data || typeof data !== "object") return void 0;
2465
+ const values = {};
2466
+ const explicit = data.controlValues;
2467
+ if (explicit && typeof explicit === "object") {
2468
+ for (const [key, value] of Object.entries(explicit)) {
2469
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2470
+ values[key] = value;
2471
+ }
2472
+ }
2473
+ }
2474
+ for (const ctrl of controls || []) {
2475
+ if (!ctrl.readFrom) continue;
2476
+ const rawValue = data[ctrl.readFrom];
2477
+ if (rawValue === void 0 || rawValue === null) continue;
2478
+ values[ctrl.id] = normalizeControlValue(rawValue);
2479
+ }
2480
+ if (data.model !== void 0 && values.model === void 0) values.model = normalizeControlValue(data.model);
2481
+ if (data.mode !== void 0 && values.mode === void 0) values.mode = normalizeControlValue(data.mode);
2482
+ return Object.keys(values).length > 0 ? values : void 0;
2483
+ }
2484
+ function normalizeProviderEffects(data) {
2485
+ const rawEffects = Array.isArray(data?.effects) ? data.effects : [];
2486
+ const effects = [];
2487
+ for (const raw of rawEffects) {
2488
+ if (!raw || typeof raw !== "object") continue;
2489
+ const type = raw.type;
2490
+ if (type === "message" && raw.message && typeof raw.message === "object") {
2491
+ const content = raw.message.content;
2492
+ if (typeof content !== "string" && !Array.isArray(content)) continue;
2493
+ effects.push({
2494
+ type: "message",
2495
+ id: typeof raw.id === "string" ? raw.id : void 0,
2496
+ when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
2497
+ persist: raw.persist !== false,
2498
+ message: {
2499
+ role: raw.message.role === "assistant" || raw.message.role === "user" ? raw.message.role : "system",
2500
+ content,
2501
+ kind: typeof raw.message.kind === "string" ? raw.message.kind : void 0,
2502
+ senderName: typeof raw.message.senderName === "string" ? raw.message.senderName : void 0
2613
2503
  }
2614
- const agentKey = `${this.type}:ext`;
2615
- const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
2616
- for (const me of monitorEvents) {
2617
- this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
2504
+ });
2505
+ continue;
2506
+ }
2507
+ if (type === "toast" && raw.toast && typeof raw.toast.message === "string") {
2508
+ effects.push({
2509
+ type: "toast",
2510
+ id: typeof raw.id === "string" ? raw.id : void 0,
2511
+ when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
2512
+ persist: raw.persist !== false,
2513
+ toast: {
2514
+ level: raw.toast.level === "success" || raw.toast.level === "warning" ? raw.toast.level : "info",
2515
+ message: raw.toast.message
2618
2516
  }
2619
- }
2620
- pushEvent(event) {
2621
- this.events.push(event);
2622
- if (this.events.length > 50) this.events = this.events.slice(-50);
2623
- }
2624
- flushEvents() {
2625
- const events = [...this.events];
2626
- this.events = [];
2627
- return events;
2628
- }
2629
- resolveChatTitle(data) {
2630
- const title = typeof data?.title === "string" && data.title.trim() ? data.title.trim() : this.chatTitle;
2631
- return title || this.agentName || this.provider.name;
2632
- }
2633
- resetStreamState() {
2634
- if (this.currentStatus !== "idle") {
2635
- this.detectTransition("idle", {
2636
- title: this.chatTitle,
2637
- agentName: this.agentName,
2638
- extensionId: this.extensionId,
2639
- messages: this.messages
2640
- });
2517
+ });
2518
+ continue;
2519
+ }
2520
+ if (type === "notification" && raw.notification && typeof raw.notification.body === "string") {
2521
+ effects.push({
2522
+ type: "notification",
2523
+ id: typeof raw.id === "string" ? raw.id : void 0,
2524
+ when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
2525
+ persist: raw.persist !== false,
2526
+ notification: {
2527
+ title: typeof raw.notification.title === "string" ? raw.notification.title : void 0,
2528
+ body: raw.notification.body,
2529
+ level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
2530
+ channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
2531
+ preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
2532
+ bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
2641
2533
  }
2642
- this.agentStreams = [];
2643
- this.messages = [];
2644
- this.activeModal = null;
2645
- this.currentModel = "";
2646
- this.currentMode = "";
2647
- this.controlValues = {};
2648
- this.currentStatus = "idle";
2649
- this.chatId = null;
2650
- this.chatTitle = null;
2651
- this.agentName = "";
2652
- this.extensionId = "";
2653
- this.lastAgentStatus = "idle";
2654
- this.generatingStartedAt = 0;
2655
- this.monitor.reset();
2656
- }
2657
- };
2534
+ });
2535
+ }
2536
+ }
2537
+ return effects;
2538
+ }
2539
+ function normalizeControlValue(value) {
2540
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2541
+ return value;
2542
+ }
2543
+ if (value && typeof value === "object") {
2544
+ if (typeof value.label === "string") return value.label;
2545
+ if (typeof value.name === "string") return value.name;
2546
+ if (typeof value.id === "string") return value.id;
2547
+ }
2548
+ return String(value);
2549
+ }
2550
+ var init_control_effects = __esm({
2551
+ "../../oss/packages/daemon-core/src/providers/control-effects.ts"() {
2552
+ "use strict";
2658
2553
  }
2659
2554
  });
2660
2555
 
@@ -2938,6 +2833,344 @@ var init_chat_history = __esm({
2938
2833
  }
2939
2834
  });
2940
2835
 
2836
+ // ../../oss/packages/daemon-core/src/providers/extension-provider-instance.ts
2837
+ var ExtensionProviderInstance;
2838
+ var init_extension_provider_instance = __esm({
2839
+ "../../oss/packages/daemon-core/src/providers/extension-provider-instance.ts"() {
2840
+ "use strict";
2841
+ init_status_monitor();
2842
+ init_control_effects();
2843
+ init_chat_history();
2844
+ ExtensionProviderInstance = class {
2845
+ type;
2846
+ category = "extension";
2847
+ provider;
2848
+ context = null;
2849
+ settings = {};
2850
+ events = [];
2851
+ // status
2852
+ currentStatus = "idle";
2853
+ agentStreams = [];
2854
+ messages = [];
2855
+ activeModal = null;
2856
+ currentModel = "";
2857
+ currentMode = "";
2858
+ controlValues = {};
2859
+ appliedEffectKeys = /* @__PURE__ */ new Set();
2860
+ runtimeMessages = [];
2861
+ lastAgentStatus = "idle";
2862
+ generatingStartedAt = 0;
2863
+ monitor;
2864
+ historyWriter;
2865
+ // meta
2866
+ instanceId;
2867
+ ideType = "";
2868
+ chatId = null;
2869
+ chatTitle = null;
2870
+ agentName = "";
2871
+ extensionId = "";
2872
+ constructor(provider) {
2873
+ this.type = provider.type;
2874
+ this.provider = provider;
2875
+ this.instanceId = crypto.randomUUID();
2876
+ this.monitor = new StatusMonitor();
2877
+ this.historyWriter = new ChatHistoryWriter();
2878
+ }
2879
+ // ─── Lifecycle ──────────────────────────────────
2880
+ async init(context) {
2881
+ this.context = context;
2882
+ this.settings = context.settings || {};
2883
+ this.monitor.updateConfig({
2884
+ approvalAlert: this.settings.approvalAlert !== false,
2885
+ longGeneratingAlert: this.settings.longGeneratingAlert !== false,
2886
+ longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
2887
+ });
2888
+ }
2889
+ async onTick() {
2890
+ if (!this.context?.cdp?.isConnected) return;
2891
+ }
2892
+ getState() {
2893
+ return {
2894
+ type: this.type,
2895
+ name: this.provider.name,
2896
+ category: "extension",
2897
+ status: this.currentStatus,
2898
+ activeChat: this.messages.length > 0 || this.runtimeMessages.length > 0 ? {
2899
+ id: this.chatId || this.instanceId,
2900
+ title: this.chatTitle || this.agentName || this.provider.name,
2901
+ status: this.currentStatus,
2902
+ messages: this.mergeConversationMessages(this.messages),
2903
+ activeModal: this.activeModal,
2904
+ inputContent: ""
2905
+ } : null,
2906
+ currentModel: this.currentModel || void 0,
2907
+ currentPlan: this.currentMode || void 0,
2908
+ controlValues: this.controlValues,
2909
+ providerControls: this.provider.controls,
2910
+ agentStreams: this.agentStreams,
2911
+ instanceId: this.instanceId,
2912
+ lastUpdated: Date.now(),
2913
+ settings: this.settings,
2914
+ pendingEvents: this.flushEvents()
2915
+ };
2916
+ }
2917
+ onEvent(event, data) {
2918
+ if (event === "stream_update") {
2919
+ if (data?.streams) this.agentStreams = data.streams;
2920
+ if (data?.messages) this.messages = data.messages;
2921
+ if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
2922
+ if (data?.model) this.currentModel = data.model;
2923
+ if (data?.mode) this.currentMode = data.mode;
2924
+ const controlValues = extractProviderControlValues(this.provider.controls, data) || data?.controlValues;
2925
+ if (controlValues) this.controlValues = controlValues;
2926
+ if (typeof data?.sessionId === "string" && data.sessionId.trim()) this.chatId = data.sessionId;
2927
+ if (typeof data?.title === "string" && data.title.trim()) this.chatTitle = data.title;
2928
+ if (typeof data?.agentName === "string" && data.agentName.trim()) this.agentName = data.agentName;
2929
+ if (typeof data?.extensionId === "string" && data.extensionId.trim()) this.extensionId = data.extensionId;
2930
+ if (data?.status) {
2931
+ const newStatus = data.status;
2932
+ this.detectTransition(newStatus, data);
2933
+ this.currentStatus = newStatus;
2934
+ }
2935
+ } else if (event === "stream_reset") {
2936
+ this.resetStreamState();
2937
+ } else if (event === "extension_connected") {
2938
+ this.ideType = data?.ideType || "";
2939
+ } else if (event === "provider_state_patch" && data && typeof data === "object") {
2940
+ this.applyProviderResponse(data, { phase: "immediate" });
2941
+ }
2942
+ }
2943
+ dispose() {
2944
+ this.agentStreams = [];
2945
+ this.messages = [];
2946
+ this.monitor.reset();
2947
+ this.appliedEffectKeys.clear();
2948
+ this.runtimeMessages = [];
2949
+ }
2950
+ updateSettings(newSettings) {
2951
+ this.settings = { ...newSettings };
2952
+ this.monitor.updateConfig({
2953
+ approvalAlert: this.settings.approvalAlert !== false,
2954
+ longGeneratingAlert: this.settings.longGeneratingAlert !== false,
2955
+ longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
2956
+ });
2957
+ }
2958
+ /** Query UUID instanceId */
2959
+ getInstanceId() {
2960
+ return this.instanceId;
2961
+ }
2962
+ // ─── status transition detect ──────────────────────────────
2963
+ detectTransition(newStatus, data) {
2964
+ const now = Date.now();
2965
+ const agentStatus = newStatus === "streaming" || newStatus === "generating" ? "generating" : newStatus === "waiting_approval" ? "waiting_approval" : "idle";
2966
+ const lastMsg = Array.isArray(data?.messages) && data.messages.length > 0 ? data.messages[data.messages.length - 1] : null;
2967
+ const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
2968
+ const previousStatus = this.lastAgentStatus;
2969
+ if (agentStatus !== this.lastAgentStatus) {
2970
+ if (this.lastAgentStatus === "idle" && agentStatus === "generating") {
2971
+ this.generatingStartedAt = now;
2972
+ this.pushEvent({
2973
+ event: "agent:generating_started",
2974
+ chatTitle: this.resolveChatTitle(data),
2975
+ timestamp: now,
2976
+ ideType: this.ideType || this.type,
2977
+ agentType: this.type,
2978
+ agentName: this.agentName || this.provider.name,
2979
+ extensionId: this.extensionId || this.type
2980
+ });
2981
+ } else if (agentStatus === "waiting_approval") {
2982
+ if (!this.generatingStartedAt) this.generatingStartedAt = now;
2983
+ this.pushEvent({
2984
+ event: "agent:waiting_approval",
2985
+ chatTitle: this.resolveChatTitle(data),
2986
+ timestamp: now,
2987
+ ideType: this.ideType || this.type,
2988
+ agentType: this.type,
2989
+ agentName: this.agentName || this.provider.name,
2990
+ extensionId: this.extensionId || this.type,
2991
+ modalMessage: data?.activeModal?.message,
2992
+ modalButtons: data?.activeModal?.buttons
2993
+ });
2994
+ } else if (agentStatus === "idle" && (this.lastAgentStatus === "generating" || this.lastAgentStatus === "waiting_approval")) {
2995
+ const duration3 = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
2996
+ this.pushEvent({
2997
+ event: "agent:generating_completed",
2998
+ chatTitle: this.resolveChatTitle(data),
2999
+ duration: duration3,
3000
+ timestamp: now,
3001
+ ideType: this.ideType || this.type,
3002
+ agentType: this.type,
3003
+ agentName: this.agentName || this.provider.name,
3004
+ extensionId: this.extensionId || this.type
3005
+ });
3006
+ this.generatingStartedAt = 0;
3007
+ }
3008
+ this.lastAgentStatus = agentStatus;
3009
+ }
3010
+ this.applyProviderResponse(data, {
3011
+ phase: agentStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
3012
+ });
3013
+ const agentKey = `${this.type}:ext`;
3014
+ const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
3015
+ for (const me of monitorEvents) {
3016
+ this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
3017
+ }
3018
+ }
3019
+ pushEvent(event) {
3020
+ this.events.push(event);
3021
+ if (this.events.length > 50) this.events = this.events.slice(-50);
3022
+ }
3023
+ applyProviderResponse(data, options) {
3024
+ if (!data || typeof data !== "object") return;
3025
+ const controlValues = extractProviderControlValues(this.provider.controls, data);
3026
+ if (controlValues) this.controlValues = { ...this.controlValues, ...controlValues };
3027
+ const effects = normalizeProviderEffects(data);
3028
+ for (const effect of effects) {
3029
+ const effectWhen = effect.when || "immediate";
3030
+ if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
3031
+ if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
3032
+ const effectKey = this.getEffectDedupKey(effect);
3033
+ if (this.appliedEffectKeys.has(effectKey)) continue;
3034
+ this.appliedEffectKeys.add(effectKey);
3035
+ if (effect.persist !== false) {
3036
+ const persisted = this.getPersistedEffectContent(effect);
3037
+ if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
3038
+ }
3039
+ if (effect.type === "message" && effect.message) {
3040
+ this.pushEvent({
3041
+ event: "provider:message",
3042
+ timestamp: Date.now(),
3043
+ content: typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content),
3044
+ role: effect.message.role || "system",
3045
+ kind: effect.message.kind,
3046
+ senderName: effect.message.senderName
3047
+ });
3048
+ } else if (effect.type === "toast" && effect.toast) {
3049
+ this.pushEvent({
3050
+ event: "provider:toast",
3051
+ effectId: effect.id || effectKey,
3052
+ timestamp: Date.now(),
3053
+ message: effect.toast.message,
3054
+ level: effect.toast.level || "info"
3055
+ });
3056
+ } else if (effect.type === "notification" && effect.notification) {
3057
+ this.pushEvent({
3058
+ event: "provider:notification",
3059
+ effectId: effect.id || effectKey,
3060
+ timestamp: Date.now(),
3061
+ title: effect.notification.title,
3062
+ message: effect.notification.body,
3063
+ content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
3064
+ level: effect.notification.level || "info",
3065
+ channels: effect.notification.channels || ["toast"],
3066
+ preferenceKey: effect.notification.preferenceKey
3067
+ });
3068
+ }
3069
+ }
3070
+ }
3071
+ appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
3072
+ const normalizedContent = String(content || "").trim();
3073
+ if (!normalizedContent) return;
3074
+ if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
3075
+ this.runtimeMessages.push({
3076
+ key: dedupKey,
3077
+ message: {
3078
+ role: "system",
3079
+ senderName: "System",
3080
+ content: normalizedContent,
3081
+ receivedAt,
3082
+ timestamp: receivedAt
3083
+ }
3084
+ });
3085
+ if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
3086
+ this.historyWriter.appendNewMessages(
3087
+ this.type,
3088
+ [{
3089
+ role: "system",
3090
+ senderName: "System",
3091
+ content: normalizedContent,
3092
+ kind: "system",
3093
+ receivedAt,
3094
+ historyDedupKey: dedupKey
3095
+ }],
3096
+ this.chatTitle || this.agentName || this.provider.name,
3097
+ this.instanceId,
3098
+ this.chatId || this.instanceId
3099
+ );
3100
+ }
3101
+ mergeConversationMessages(messages) {
3102
+ if (this.runtimeMessages.length === 0) return messages;
3103
+ return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
3104
+ const aTime = a.message.receivedAt || a.message.timestamp || 0;
3105
+ const bTime = b.message.receivedAt || b.message.timestamp || 0;
3106
+ if (aTime !== bTime) return aTime - bTime;
3107
+ return a.index - b.index;
3108
+ }).map((entry) => entry.message);
3109
+ }
3110
+ getPersistedEffectContent(effect) {
3111
+ if (effect.type === "message") {
3112
+ return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
3113
+ }
3114
+ if (effect.type === "toast") {
3115
+ return effect.toast?.message || null;
3116
+ }
3117
+ if (effect.type === "notification") {
3118
+ if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
3119
+ if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
3120
+ return `${effect.notification.title}
3121
+ ${effect.notification.body || ""}`.trim();
3122
+ }
3123
+ return effect.notification?.body || null;
3124
+ }
3125
+ return null;
3126
+ }
3127
+ getEffectDedupKey(effect) {
3128
+ if (effect.id) return `provider_effect:${effect.id}`;
3129
+ if (effect.type === "message") {
3130
+ return `provider_effect:message:${typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "")}`;
3131
+ }
3132
+ if (effect.type === "notification") {
3133
+ return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
3134
+ }
3135
+ return `provider_effect:toast:${effect.toast?.message || ""}`;
3136
+ }
3137
+ flushEvents() {
3138
+ const events = [...this.events];
3139
+ this.events = [];
3140
+ return events;
3141
+ }
3142
+ resolveChatTitle(data) {
3143
+ const title = typeof data?.title === "string" && data.title.trim() ? data.title.trim() : this.chatTitle;
3144
+ return title || this.agentName || this.provider.name;
3145
+ }
3146
+ resetStreamState() {
3147
+ if (this.currentStatus !== "idle") {
3148
+ this.detectTransition("idle", {
3149
+ title: this.chatTitle,
3150
+ agentName: this.agentName,
3151
+ extensionId: this.extensionId,
3152
+ messages: this.messages
3153
+ });
3154
+ }
3155
+ this.agentStreams = [];
3156
+ this.messages = [];
3157
+ this.activeModal = null;
3158
+ this.currentModel = "";
3159
+ this.currentMode = "";
3160
+ this.controlValues = {};
3161
+ this.currentStatus = "idle";
3162
+ this.chatId = null;
3163
+ this.chatTitle = null;
3164
+ this.agentName = "";
3165
+ this.extensionId = "";
3166
+ this.lastAgentStatus = "idle";
3167
+ this.generatingStartedAt = 0;
3168
+ this.monitor.reset();
3169
+ }
3170
+ };
3171
+ }
3172
+ });
3173
+
2941
3174
  // ../../oss/packages/daemon-core/src/providers/ide-provider-instance.ts
2942
3175
  var crypto2, IdeProviderInstance;
2943
3176
  var init_ide_provider_instance = __esm({
@@ -2948,6 +3181,7 @@ var init_ide_provider_instance = __esm({
2948
3181
  init_status_monitor();
2949
3182
  init_chat_history();
2950
3183
  init_logger();
3184
+ init_control_effects();
2951
3185
  IdeProviderInstance = class {
2952
3186
  type;
2953
3187
  category = "ide";
@@ -2965,6 +3199,8 @@ var init_ide_provider_instance = __esm({
2965
3199
  monitor;
2966
3200
  historyWriter;
2967
3201
  autoApproveBusy = false;
3202
+ appliedEffectKeys = /* @__PURE__ */ new Set();
3203
+ runtimeMessages = [];
2968
3204
  // IDE meta
2969
3205
  ideVersion = "";
2970
3206
  instanceId;
@@ -3025,7 +3261,7 @@ var init_ide_provider_instance = __esm({
3025
3261
  id: this.cachedChat.id || "active_session",
3026
3262
  title: this.cachedChat.title || this.type,
3027
3263
  status: this.cachedChat.status || this.currentStatus,
3028
- messages: this.cachedChat.messages || [],
3264
+ messages: this.mergeConversationMessages(this.cachedChat.messages || []),
3029
3265
  activeModal: this.cachedChat.activeModal || null,
3030
3266
  inputContent: this.cachedChat.inputContent || ""
3031
3267
  } : null,
@@ -3065,6 +3301,13 @@ var init_ide_provider_instance = __esm({
3065
3301
  for (const ext of this.extensions.values()) {
3066
3302
  ext.onEvent("stream_reset");
3067
3303
  }
3304
+ } else if (event === "provider_state_patch" && data && typeof data === "object") {
3305
+ const extType = typeof data.extensionType === "string" ? data.extensionType : "";
3306
+ if (extType && this.extensions.has(extType)) {
3307
+ this.extensions.get(extType).onEvent("provider_state_patch", data);
3308
+ } else {
3309
+ this.applyProviderResponse(data, { phase: "immediate" });
3310
+ }
3068
3311
  }
3069
3312
  }
3070
3313
  dispose() {
@@ -3072,11 +3315,21 @@ var init_ide_provider_instance = __esm({
3072
3315
  this.lastAgentStatuses.clear();
3073
3316
  this.generatingStartedAt.clear();
3074
3317
  this.monitor.reset();
3318
+ this.appliedEffectKeys.clear();
3319
+ this.runtimeMessages = [];
3075
3320
  for (const ext of this.extensions.values()) {
3076
3321
  ext.dispose();
3077
3322
  }
3078
3323
  this.extensions.clear();
3079
3324
  }
3325
+ updateSettings(newSettings) {
3326
+ this.settings = { ...newSettings };
3327
+ this.monitor.updateConfig({
3328
+ approvalAlert: this.settings.approvalAlert !== false,
3329
+ longGeneratingAlert: this.settings.longGeneratingAlert !== false,
3330
+ longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
3331
+ });
3332
+ }
3080
3333
  // ─── Extension manage ─────────────────────────────
3081
3334
  /** Extension Instance add */
3082
3335
  async addExtension(provider, settings) {
@@ -3189,6 +3442,8 @@ var init_ide_provider_instance = __esm({
3189
3442
  raw.messages = raw.messages.filter((m) => !hiddenKinds.has(m.kind));
3190
3443
  }
3191
3444
  }
3445
+ const controlValues = extractProviderControlValues(this.provider.controls, raw);
3446
+ if (controlValues) raw.controlValues = controlValues;
3192
3447
  this.cachedChat = { ...raw, activeModal };
3193
3448
  this.detectAgentTransitions(raw, now);
3194
3449
  if (raw.messages?.length > 0) {
@@ -3255,6 +3510,9 @@ var init_ide_provider_instance = __esm({
3255
3510
  }
3256
3511
  this.lastAgentStatuses.set(agentKey, agentStatus);
3257
3512
  }
3513
+ this.applyProviderResponse(chatData, {
3514
+ phase: agentStatus === "idle" && (lastStatus === "generating" || lastStatus === "waiting_approval") ? "turn_completed" : "immediate"
3515
+ });
3258
3516
  if (agentStatus === "waiting_approval" && this.settings.autoApprove && !this.autoApproveBusy) {
3259
3517
  this.autoApproveViaScript(chatData);
3260
3518
  }
@@ -3267,6 +3525,136 @@ var init_ide_provider_instance = __esm({
3267
3525
  this.events.push(event);
3268
3526
  if (this.events.length > 50) this.events = this.events.slice(-50);
3269
3527
  }
3528
+ applyProviderResponse(data, options) {
3529
+ if (!data || typeof data !== "object") return;
3530
+ const controlValues = extractProviderControlValues(this.provider.controls, data);
3531
+ if (controlValues) {
3532
+ this.cachedChat = {
3533
+ ...this.cachedChat || {},
3534
+ ...data,
3535
+ controlValues: { ...this.cachedChat?.controlValues || {}, ...controlValues }
3536
+ };
3537
+ }
3538
+ const effects = normalizeProviderEffects(data);
3539
+ for (const effect of effects) {
3540
+ const effectWhen = effect.when || "immediate";
3541
+ if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
3542
+ if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
3543
+ const effectKey = this.getEffectDedupKey(effect);
3544
+ if (this.appliedEffectKeys.has(effectKey)) continue;
3545
+ this.appliedEffectKeys.add(effectKey);
3546
+ if (effect.persist !== false) {
3547
+ const persisted = this.getPersistedEffectContent(effect);
3548
+ if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
3549
+ }
3550
+ if (effect.type === "message" && effect.message) {
3551
+ this.pushEvent({
3552
+ event: "provider:message",
3553
+ timestamp: Date.now(),
3554
+ content: typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content),
3555
+ role: effect.message.role || "system",
3556
+ kind: effect.message.kind,
3557
+ senderName: effect.message.senderName
3558
+ });
3559
+ } else if (effect.type === "toast" && effect.toast) {
3560
+ this.pushEvent({
3561
+ event: "provider:toast",
3562
+ effectId: effect.id || effectKey,
3563
+ timestamp: Date.now(),
3564
+ message: effect.toast.message,
3565
+ level: effect.toast.level || "info"
3566
+ });
3567
+ } else if (effect.type === "notification" && effect.notification) {
3568
+ this.pushEvent({
3569
+ event: "provider:notification",
3570
+ effectId: effect.id || effectKey,
3571
+ timestamp: Date.now(),
3572
+ title: effect.notification.title,
3573
+ message: effect.notification.body,
3574
+ content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
3575
+ level: effect.notification.level || "info",
3576
+ channels: effect.notification.channels || ["toast"],
3577
+ preferenceKey: effect.notification.preferenceKey
3578
+ });
3579
+ }
3580
+ }
3581
+ }
3582
+ appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
3583
+ const normalizedContent = String(content || "").trim();
3584
+ if (!normalizedContent) return;
3585
+ if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
3586
+ if (!this.cachedChat) {
3587
+ this.cachedChat = {
3588
+ id: "active_session",
3589
+ title: this.provider.name,
3590
+ status: this.currentStatus,
3591
+ messages: [],
3592
+ activeModal: null,
3593
+ inputContent: ""
3594
+ };
3595
+ }
3596
+ this.runtimeMessages.push({
3597
+ key: dedupKey,
3598
+ message: {
3599
+ role: "system",
3600
+ senderName: "System",
3601
+ content: normalizedContent,
3602
+ receivedAt,
3603
+ timestamp: receivedAt
3604
+ }
3605
+ });
3606
+ if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
3607
+ this.historyWriter.appendNewMessages(
3608
+ this.type,
3609
+ [{
3610
+ role: "system",
3611
+ senderName: "System",
3612
+ content: normalizedContent,
3613
+ kind: "system",
3614
+ receivedAt,
3615
+ historyDedupKey: dedupKey
3616
+ }],
3617
+ this.cachedChat?.title || this.provider.name,
3618
+ this.instanceId,
3619
+ this.cachedChat?.id || this.instanceId
3620
+ );
3621
+ }
3622
+ mergeConversationMessages(messages) {
3623
+ if (this.runtimeMessages.length === 0) return messages;
3624
+ return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
3625
+ const aTime = a.message.receivedAt || a.message.timestamp || 0;
3626
+ const bTime = b.message.receivedAt || b.message.timestamp || 0;
3627
+ if (aTime !== bTime) return aTime - bTime;
3628
+ return a.index - b.index;
3629
+ }).map((entry) => entry.message);
3630
+ }
3631
+ getPersistedEffectContent(effect) {
3632
+ if (effect.type === "message") {
3633
+ return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
3634
+ }
3635
+ if (effect.type === "toast") {
3636
+ return effect.toast?.message || null;
3637
+ }
3638
+ if (effect.type === "notification") {
3639
+ if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
3640
+ if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
3641
+ return `${effect.notification.title}
3642
+ ${effect.notification.body || ""}`.trim();
3643
+ }
3644
+ return effect.notification?.body || null;
3645
+ }
3646
+ return null;
3647
+ }
3648
+ getEffectDedupKey(effect) {
3649
+ if (effect.id) return `provider_effect:${effect.id}`;
3650
+ if (effect.type === "message") {
3651
+ return `provider_effect:message:${typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "")}`;
3652
+ }
3653
+ if (effect.type === "notification") {
3654
+ return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
3655
+ }
3656
+ return `provider_effect:toast:${effect.toast?.message || ""}`;
3657
+ }
3270
3658
  flushEvents() {
3271
3659
  const events = [...this.events];
3272
3660
  this.events = [];
@@ -5388,7 +5776,56 @@ function handleSetProviderSetting(h, args) {
5388
5776
  }
5389
5777
  return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
5390
5778
  }
5391
- async function handleExtensionScript(h, args, scriptName) {
5779
+ function normalizeProviderScriptArgs(args) {
5780
+ const normalizedArgs = { ...args || {} };
5781
+ for (const key of ["mode", "model", "message", "action", "button", "text", "sessionId", "value"]) {
5782
+ if (key in normalizedArgs && !(key.toUpperCase() in normalizedArgs)) {
5783
+ normalizedArgs[key.toUpperCase()] = normalizedArgs[key];
5784
+ }
5785
+ }
5786
+ return normalizedArgs;
5787
+ }
5788
+ function parseScriptResult(result) {
5789
+ if (typeof result === "string") {
5790
+ try {
5791
+ const parsed = JSON.parse(result);
5792
+ if (parsed && typeof parsed === "object" && parsed.success === false) {
5793
+ return { success: false, payload: parsed };
5794
+ }
5795
+ return { success: true, payload: parsed };
5796
+ } catch {
5797
+ return { success: true, payload: { result } };
5798
+ }
5799
+ }
5800
+ if (result && typeof result === "object" && result.success === false) {
5801
+ return { success: false, payload: result };
5802
+ }
5803
+ return { success: true, payload: result };
5804
+ }
5805
+ function getCliScriptCommand(payload) {
5806
+ if (!payload || typeof payload !== "object") return null;
5807
+ if (typeof payload.sendMessage === "string" && payload.sendMessage.trim()) {
5808
+ return { type: "send_message", text: payload.sendMessage.trim() };
5809
+ }
5810
+ const command = payload.command;
5811
+ if (!command || typeof command !== "object") return null;
5812
+ if (command.type !== "send_message") return null;
5813
+ const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
5814
+ if (!text) return null;
5815
+ return { type: "send_message", text };
5816
+ }
5817
+ function applyProviderPatch(h, args, payload) {
5818
+ if (!payload || typeof payload !== "object") return;
5819
+ const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
5820
+ const targetSession = targetSessionId ? h.ctx.sessionRegistry?.get(targetSessionId) : void 0;
5821
+ const instanceKey = targetSession?.instanceKey || targetSessionId;
5822
+ if (!instanceKey) return;
5823
+ h.ctx.instanceManager?.sendEvent(instanceKey, "provider_state_patch", {
5824
+ ...payload,
5825
+ extensionType: targetSession?.transport === "cdp-webview" ? targetSession.providerType : void 0
5826
+ });
5827
+ }
5828
+ async function executeProviderScript(h, args, scriptName) {
5392
5829
  const { agentType, ideType } = args || {};
5393
5830
  if (!agentType) return { success: false, error: "agentType is required" };
5394
5831
  const loader = h.ctx.providerLoader;
@@ -5401,13 +5838,29 @@ async function handleExtensionScript(h, args, scriptName) {
5401
5838
  if (!provider.scripts?.[actualScriptName]) {
5402
5839
  return { success: false, error: `Script '${actualScriptName}' not available for ${agentType}` };
5403
5840
  }
5404
- const scriptFn = provider.scripts[actualScriptName];
5405
- const normalizedArgs = { ...args };
5406
- for (const key of ["mode", "model", "message", "action", "button", "text", "sessionId"]) {
5407
- if (key in normalizedArgs && !(key.toUpperCase() in normalizedArgs)) {
5408
- normalizedArgs[key.toUpperCase()] = normalizedArgs[key];
5841
+ const normalizedArgs = normalizeProviderScriptArgs(args);
5842
+ if (provider.category === "cli") {
5843
+ const adapter = h.getCliAdapter(args?.targetSessionId || agentType);
5844
+ if (!adapter?.invokeScript) {
5845
+ return { success: false, error: `CLI adapter does not support script '${actualScriptName}'` };
5846
+ }
5847
+ try {
5848
+ const raw = await adapter.invokeScript(actualScriptName, normalizedArgs);
5849
+ const parsed = parseScriptResult(raw);
5850
+ if (!parsed.success) {
5851
+ return { success: false, ...parsed.payload || {} };
5852
+ }
5853
+ const cliCommand = getCliScriptCommand(parsed.payload);
5854
+ if (cliCommand?.type === "send_message" && cliCommand.text) {
5855
+ await adapter.sendMessage(cliCommand.text);
5856
+ }
5857
+ applyProviderPatch(h, args, parsed.payload);
5858
+ return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
5859
+ } catch (e) {
5860
+ return { success: false, error: `Script execution failed: ${e.message}` };
5409
5861
  }
5410
5862
  }
5863
+ const scriptFn = provider.scripts[actualScriptName];
5411
5864
  const scriptCode = scriptFn(normalizedArgs);
5412
5865
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
5413
5866
  const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
@@ -5461,16 +5914,29 @@ async function handleExtensionScript(h, args, scriptName) {
5461
5914
  if (typeof result === "string") {
5462
5915
  try {
5463
5916
  const parsed = JSON.parse(result);
5917
+ applyProviderPatch(h, args, parsed);
5918
+ if (parsed && typeof parsed === "object" && parsed.success === false) {
5919
+ return { success: false, ...parsed };
5920
+ }
5464
5921
  return { success: true, ...parsed };
5465
5922
  } catch {
5466
5923
  return { success: true, result };
5467
5924
  }
5468
5925
  }
5926
+ applyProviderPatch(h, args, result);
5469
5927
  return { success: true, result };
5470
5928
  } catch (e) {
5471
5929
  return { success: false, error: `Script execution failed: ${e.message}` };
5472
5930
  }
5473
5931
  }
5932
+ async function handleExtensionScript(h, args, scriptName) {
5933
+ return executeProviderScript(h, args, scriptName);
5934
+ }
5935
+ async function handleProviderScript(h, args) {
5936
+ const scriptName = typeof args?.scriptName === "string" ? args.scriptName.trim() : "";
5937
+ if (!scriptName) return { success: false, error: "scriptName is required" };
5938
+ return executeProviderScript(h, args, scriptName);
5939
+ }
5474
5940
  function handleGetIdeExtensions(h, args) {
5475
5941
  const { ideType } = args || {};
5476
5942
  const loader = h.ctx.providerLoader;
@@ -5994,6 +6460,8 @@ var init_handler = __esm({
5994
6460
  case "set_ide_extension":
5995
6461
  return handleSetIdeExtension(this, args);
5996
6462
  // ─── Extension Model / Mode Control (stream-commands.ts) ──────────
6463
+ case "invoke_provider_script":
6464
+ return handleProviderScript(this, args);
5997
6465
  case "list_extension_models":
5998
6466
  return handleExtensionScript(this, args, "listModels");
5999
6467
  case "set_extension_model":
@@ -6678,6 +7146,53 @@ function stripTerminalNoise(str) {
6678
7146
  function sanitizeTerminalText(str) {
6679
7147
  return stripTerminalNoise(stripAnsi(str));
6680
7148
  }
7149
+ function splitCliScreenLines(text) {
7150
+ return String(text || "").replace(/\u0007/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").map((line) => line.replace(/\s+$/, ""));
7151
+ }
7152
+ function isPromptLikeCliLine(line) {
7153
+ const trimmed = String(line || "").trim();
7154
+ if (!trimmed) return false;
7155
+ return /^[❯›>]\s*(?:$|\S.*)$/.test(trimmed);
7156
+ }
7157
+ function buildCliScreenSnapshot(text) {
7158
+ const normalizedText = String(text || "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
7159
+ const rawLines = splitCliScreenLines(normalizedText);
7160
+ const lines = rawLines.map((line, index, arr) => {
7161
+ const trimmed = String(line || "").trim();
7162
+ return {
7163
+ index,
7164
+ fromTop: index,
7165
+ fromBottom: arr.length - index - 1,
7166
+ text: line,
7167
+ trimmed,
7168
+ isEmpty: trimmed.length === 0
7169
+ };
7170
+ });
7171
+ const nonEmptyLines = lines.filter((line) => !line.isEmpty);
7172
+ const firstNonEmptyLine = nonEmptyLines[0] ?? null;
7173
+ const lastNonEmptyLine = nonEmptyLines[nonEmptyLines.length - 1] ?? null;
7174
+ let promptLineIndex = -1;
7175
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
7176
+ if (isPromptLikeCliLine(lines[i].text)) {
7177
+ promptLineIndex = i;
7178
+ break;
7179
+ }
7180
+ }
7181
+ return {
7182
+ text: normalizedText,
7183
+ lineCount: lines.length,
7184
+ lines,
7185
+ nonEmptyLines,
7186
+ firstNonEmptyLineIndex: firstNonEmptyLine?.index ?? -1,
7187
+ lastNonEmptyLineIndex: lastNonEmptyLine?.index ?? -1,
7188
+ firstNonEmptyLine,
7189
+ lastNonEmptyLine,
7190
+ promptLineIndex,
7191
+ promptLine: promptLineIndex >= 0 ? lines[promptLineIndex] : null,
7192
+ linesAbovePrompt: promptLineIndex >= 0 ? lines.slice(0, promptLineIndex) : [...lines],
7193
+ linesBelowPrompt: promptLineIndex >= 0 ? lines.slice(promptLineIndex + 1) : []
7194
+ };
7195
+ }
6681
7196
  function computeTerminalQueryTail(buffer) {
6682
7197
  const prefixes = ["\x1B[6n", "\x1B[?6n"];
6683
7198
  const maxLength = prefixes.reduce((n, value) => Math.max(n, value.length), 0) - 1;
@@ -6919,7 +7434,9 @@ var init_provider_cli_adapter = __esm({
6919
7434
  ready = false;
6920
7435
  startupBuffer = "";
6921
7436
  startupParseGate = false;
7437
+ startupSettleTimer = null;
6922
7438
  spawnAt = 0;
7439
+ startupFirstOutputAt = 0;
6923
7440
  // PTY I/O
6924
7441
  onPtyDataCallback = null;
6925
7442
  pendingOutputParseBuffer = "";
@@ -6960,6 +7477,7 @@ var init_provider_cli_adapter = __esm({
6960
7477
  statusHistory = [];
6961
7478
  // ─── CLI Scripts (script-based parsing) ───
6962
7479
  cliScripts;
7480
+ runtimeSettings = {};
6963
7481
  /** Full accumulated ANSI-stripped PTY output */
6964
7482
  accumulatedBuffer = "";
6965
7483
  /** Full accumulated raw PTY output (with ANSI) */
@@ -6974,14 +7492,15 @@ var init_provider_cli_adapter = __esm({
6974
7492
  traceSessionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
6975
7493
  static MAX_TRACE_ENTRIES = 250;
6976
7494
  providerResolutionMeta;
6977
- static IDLE_FINISH_CONFIRM_MS = 900;
7495
+ static IDLE_FINISH_CONFIRM_MS = 2e3;
7496
+ static STATUS_ACTIVITY_HOLD_MS = 2e3;
6978
7497
  static FINISH_RETRY_DELAY_MS = 300;
6979
7498
  static MAX_FINISH_RETRIES = 2;
6980
7499
  syncMessageViews() {
6981
7500
  this.messages = [...this.committedMessages];
6982
7501
  this.structuredMessages = [...this.committedMessages];
6983
7502
  }
6984
- normalizeParsedMessages(parsedMessages) {
7503
+ hydrateParsedMessages(parsedMessages, scope) {
6985
7504
  const referenceMessages = [...this.committedMessages];
6986
7505
  const usedReferenceIndexes = /* @__PURE__ */ new Set();
6987
7506
  const now = Date.now();
@@ -7014,13 +7533,30 @@ var init_provider_cli_adapter = __esm({
7014
7533
  const content = typeof message.content === "string" ? message.content : String(message.content || "");
7015
7534
  const parsedTimestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0;
7016
7535
  const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
7536
+ const fallbackTimestamp = role === "user" ? scope?.startedAt || now : this.lastOutputAt || scope?.startedAt || now;
7537
+ const timestamp = referenceTimestamp ?? fallbackTimestamp;
7017
7538
  return {
7539
+ ...message,
7018
7540
  role,
7019
7541
  content,
7020
- timestamp: referenceTimestamp ?? now
7542
+ timestamp,
7543
+ receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : timestamp
7021
7544
  };
7022
7545
  });
7023
7546
  }
7547
+ normalizeParsedMessages(parsedMessages, scope) {
7548
+ return this.hydrateParsedMessages(parsedMessages, scope).map((message) => ({
7549
+ role: message.role,
7550
+ content: message.content,
7551
+ timestamp: message.timestamp,
7552
+ receivedAt: message.receivedAt,
7553
+ kind: message.kind,
7554
+ id: message.id,
7555
+ index: message.index,
7556
+ meta: message.meta,
7557
+ senderName: message.senderName
7558
+ }));
7559
+ }
7024
7560
  sliceFromOffset(text, start) {
7025
7561
  if (!text) return "";
7026
7562
  if (!Number.isFinite(start) || start <= 0) return text;
@@ -7030,14 +7566,20 @@ var init_provider_cli_adapter = __esm({
7030
7566
  buildParseInput(baseMessages, partialResponse, scope) {
7031
7567
  const buffer = scope ? this.sliceFromOffset(this.accumulatedBuffer, scope.bufferStart) || this.accumulatedBuffer : this.accumulatedBuffer;
7032
7568
  const rawBuffer = scope ? this.sliceFromOffset(this.accumulatedRawBuffer, scope.rawBufferStart) || this.accumulatedRawBuffer : this.accumulatedRawBuffer;
7569
+ const screenText = this.terminalScreen.getText();
7570
+ const recentBuffer = buffer.slice(-1e3) || this.recentOutputBuffer;
7033
7571
  return {
7034
7572
  buffer,
7035
7573
  rawBuffer,
7036
- recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
7037
- screenText: this.terminalScreen.getText(),
7574
+ recentBuffer,
7575
+ screenText,
7576
+ screen: buildCliScreenSnapshot(screenText),
7577
+ bufferScreen: buildCliScreenSnapshot(buffer),
7578
+ recentScreen: buildCliScreenSnapshot(recentBuffer),
7038
7579
  messages: [...baseMessages],
7039
7580
  partialResponse,
7040
- promptText: scope?.prompt || ""
7581
+ promptText: scope?.prompt || "",
7582
+ settings: { ...this.runtimeSettings }
7041
7583
  };
7042
7584
  }
7043
7585
  setStatus(status, trigger) {
@@ -7143,6 +7685,9 @@ var init_provider_cli_adapter = __esm({
7143
7685
  const scriptNames = Object.keys(scripts).filter((k) => typeof scripts[k] === "function");
7144
7686
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
7145
7687
  }
7688
+ updateRuntimeSettings(settings) {
7689
+ this.runtimeSettings = { ...settings };
7690
+ }
7146
7691
  // ─── Lifecycle ─────────────────────────────────
7147
7692
  setServerConn(serverConn) {
7148
7693
  this.serverConn = serverConn;
@@ -7277,6 +7822,11 @@ var init_provider_cli_adapter = __esm({
7277
7822
  this.spawnAt = Date.now();
7278
7823
  this.startupParseGate = true;
7279
7824
  this.startupBuffer = "";
7825
+ this.startupFirstOutputAt = 0;
7826
+ if (this.startupSettleTimer) {
7827
+ clearTimeout(this.startupSettleTimer);
7828
+ this.startupSettleTimer = null;
7829
+ }
7280
7830
  this.terminalScreen.reset(24, 80);
7281
7831
  this.pendingTerminalQueryTail = "";
7282
7832
  this.currentTurnScope = null;
@@ -7290,7 +7840,8 @@ var init_provider_cli_adapter = __esm({
7290
7840
  this.recordTrace("ready", {
7291
7841
  runtimeMeta: this.getRuntimeMetadata()
7292
7842
  });
7293
- this.setStatus("idle", "pty_ready");
7843
+ this.setStatus("starting", "pty_ready");
7844
+ this.scheduleStartupSettleCheck();
7294
7845
  this.onStatusChange?.();
7295
7846
  }
7296
7847
  // ─── Output Handling ────────────────────────────
@@ -7305,6 +7856,9 @@ var init_provider_cli_adapter = __esm({
7305
7856
  this.lastScreenSnapshot = normalizedScreenSnapshot;
7306
7857
  this.lastScreenChangeAt = now;
7307
7858
  }
7859
+ if (this.startupParseGate && !this.startupFirstOutputAt && (cleanData.trim() || normalizedScreenSnapshot.trim())) {
7860
+ this.startupFirstOutputAt = now;
7861
+ }
7308
7862
  if (this.idleFinishCandidate && (rawData.length > 0 || cleanData.length > 0)) {
7309
7863
  this.clearIdleFinishCandidate("new_output");
7310
7864
  }
@@ -7315,6 +7869,9 @@ var init_provider_cli_adapter = __esm({
7315
7869
  cleanPreview: this.summarizeTraceText(cleanData, 300),
7316
7870
  screenText: this.summarizeTraceText(this.terminalScreen.getText(), 1200)
7317
7871
  });
7872
+ if (this.startupParseGate) {
7873
+ this.scheduleStartupSettleCheck();
7874
+ }
7318
7875
  if (this.isWaitingForResponse && cleanData) {
7319
7876
  this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
7320
7877
  }
@@ -7328,27 +7885,51 @@ var init_provider_cli_adapter = __esm({
7328
7885
  this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
7329
7886
  this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
7330
7887
  this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
7331
- if (this.startupParseGate) {
7332
- this.startupBuffer += cleanData;
7333
- const elapsed = Date.now() - this.spawnAt;
7334
- const screenText = this.terminalScreen.getText() || "";
7335
- const startupModal = this.getStartupConfirmationModal(screenText);
7336
- const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
7337
- const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
7338
- const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7339
- const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || !!startupModal && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
7340
- if (isReady) {
7341
- this.startupParseGate = false;
7342
- this.ready = true;
7343
- LOG.info(
7344
- "CLI",
7345
- `[${this.cliType}] Startup ready (${elapsed}ms, scriptStatus=${scriptStatus}, prompt=${hasInteractivePrompt}, stableMs=${startupStableMs}) providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
7346
- );
7347
- this.onStatusChange?.();
7348
- }
7349
- }
7888
+ this.resolveStartupState("output");
7350
7889
  this.scheduleSettle();
7351
7890
  }
7891
+ resolveStartupState(trigger) {
7892
+ if (!this.startupParseGate) return;
7893
+ const now = Date.now();
7894
+ const screenText = this.terminalScreen.getText() || "";
7895
+ const normalizedScreen = normalizeScreenSnapshot(screenText);
7896
+ const hasStartupOutput = !!this.startupFirstOutputAt || !!normalizedScreen.trim();
7897
+ if (!hasStartupOutput) return;
7898
+ const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7899
+ if (stableMs < 2e3) return;
7900
+ const startupModal = this.getStartupConfirmationModal(screenText);
7901
+ this.startupParseGate = false;
7902
+ if (this.startupSettleTimer) {
7903
+ clearTimeout(this.startupSettleTimer);
7904
+ this.startupSettleTimer = null;
7905
+ }
7906
+ this.ready = true;
7907
+ if (startupModal) {
7908
+ this.activeModal = startupModal;
7909
+ this.setStatus("waiting_approval", `startup_ready:${trigger}`);
7910
+ } else {
7911
+ this.setStatus("idle", `startup_ready:${trigger}`);
7912
+ }
7913
+ LOG.info(
7914
+ "CLI",
7915
+ `[${this.cliType}] Startup settled (${trigger}, stableMs=${stableMs}, modal=${!!startupModal}) providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
7916
+ );
7917
+ this.onStatusChange?.();
7918
+ }
7919
+ scheduleStartupSettleCheck() {
7920
+ if (!this.startupParseGate) return;
7921
+ if (this.startupSettleTimer) clearTimeout(this.startupSettleTimer);
7922
+ const now = Date.now();
7923
+ const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7924
+ const delayMs = Math.max(250, 2050 - stableMs);
7925
+ this.startupSettleTimer = setTimeout(() => {
7926
+ this.startupSettleTimer = null;
7927
+ this.resolveStartupState("startup_timer");
7928
+ if (this.startupParseGate && Date.now() - this.spawnAt < 1e4) {
7929
+ this.scheduleStartupSettleCheck();
7930
+ }
7931
+ }, delayMs);
7932
+ }
7352
7933
  scheduleSettle() {
7353
7934
  if (this.settleTimer) clearTimeout(this.settleTimer);
7354
7935
  const settleEpoch = this.responseEpoch;
@@ -7415,11 +7996,14 @@ var init_provider_cli_adapter = __esm({
7415
7996
  if (allLines.length === 0) return false;
7416
7997
  const recentLines = allLines.slice(-12);
7417
7998
  const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
7418
- const activeRegion = promptIndex >= 0 ? recentLines.slice(promptIndex + 1) : recentLines;
7999
+ const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
7419
8000
  if (activeRegion.length === 0) return false;
7420
8001
  return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
7421
8002
  }
7422
8003
  refineDetectedStatus(status, tail, screenText) {
8004
+ if (this.startupParseGate) {
8005
+ return this.getStartupConfirmationModal(screenText || "") ? "waiting_approval" : "starting";
8006
+ }
7423
8007
  if (status === "waiting_approval") return status;
7424
8008
  if (this.detectClaudeGeneratingOverride(screenText || "", tail)) return "generating";
7425
8009
  return status;
@@ -7458,6 +8042,11 @@ var init_provider_cli_adapter = __esm({
7458
8042
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7459
8043
  return quietForMs < 1200 || screenStableMs < 1200 || !commitResult.hasAssistant;
7460
8044
  }
8045
+ hasRecentInteractiveActivity(now) {
8046
+ const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
8047
+ const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : Number.MAX_SAFE_INTEGER;
8048
+ return quietForMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS || screenStableMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
8049
+ }
7461
8050
  getStartupConfirmationModal(screenText) {
7462
8051
  const text = sanitizeTerminalText(String(screenText || ""));
7463
8052
  if (!text.trim()) return null;
@@ -7485,13 +8074,14 @@ var init_provider_cli_adapter = __esm({
7485
8074
  const startedAt = Date.now();
7486
8075
  let loggedWait = false;
7487
8076
  while (Date.now() - startedAt < maxWaitMs) {
8077
+ this.resolveStartupState("interactive_wait");
7488
8078
  const screenText = this.terminalScreen.getText() || "";
7489
8079
  const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
7490
8080
  const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
7491
8081
  const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
7492
8082
  const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
7493
8083
  const startupLikelyActive = /Welcome back|Tips for getting|Recent activity|Claude Code v\d/i.test(screenText);
7494
- const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "starting" && status !== "generating";
8084
+ const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "generating";
7495
8085
  if (interactiveReady) {
7496
8086
  if (loggedWait) {
7497
8087
  LOG.info(
@@ -7530,6 +8120,10 @@ var init_provider_cli_adapter = __esm({
7530
8120
  }
7531
8121
  const tail = this.settledBuffer;
7532
8122
  const screenText = this.terminalScreen.getText() || "";
8123
+ this.resolveStartupState("settled");
8124
+ if (this.startupParseGate) {
8125
+ return;
8126
+ }
7533
8127
  const startupModal = this.getStartupConfirmationModal(screenText);
7534
8128
  const modal = this.runParseApproval(tail) || startupModal;
7535
8129
  const rawScriptStatus = this.runDetectStatus(tail);
@@ -7592,6 +8186,28 @@ var init_provider_cli_adapter = __esm({
7592
8186
  } else {
7593
8187
  clearPendingScriptStatus();
7594
8188
  }
8189
+ const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
8190
+ const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
8191
+ if (shouldHoldGenerating) {
8192
+ this.clearIdleFinishCandidate("hold_generating_recent_activity");
8193
+ this.setStatus("generating", "recent_activity_hold");
8194
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
8195
+ this.idleTimeout = setTimeout(() => {
8196
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
8197
+ this.finishResponse();
8198
+ }
8199
+ }, this.timeouts.generatingIdle);
8200
+ this.recordTrace("hold_generating_recent_activity", {
8201
+ scriptStatus,
8202
+ recentInteractiveActivity,
8203
+ lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
8204
+ lastScreenChangeAt: this.lastScreenChangeAt,
8205
+ holdMs: _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS,
8206
+ ...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
8207
+ });
8208
+ this.onStatusChange?.();
8209
+ return;
8210
+ }
7595
8211
  if (scriptStatus === "waiting_approval") {
7596
8212
  this.clearIdleFinishCandidate("waiting_approval");
7597
8213
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
@@ -7669,8 +8285,8 @@ var init_provider_cli_adapter = __esm({
7669
8285
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7670
8286
  const hasAssistantTurn = !!lastParsedAssistant;
7671
8287
  const assistantLength = lastParsedAssistant?.content?.length || 0;
7672
- const idleQuietThresholdMs = Math.max(220, this.timeouts.outputSettle);
7673
- const idleStableThresholdMs = Math.max(120, Math.min(220, this.timeouts.outputSettle));
8288
+ const idleQuietThresholdMs = Math.max(2e3, this.timeouts.outputSettle);
8289
+ const idleStableThresholdMs = 2e3;
7674
8290
  const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
7675
8291
  const candidate = this.idleFinishCandidate;
7676
8292
  const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
@@ -7784,7 +8400,7 @@ var init_provider_cli_adapter = __esm({
7784
8400
  this.currentTurnScope
7785
8401
  );
7786
8402
  if (parsed && Array.isArray(parsed.messages)) {
7787
- this.committedMessages = this.normalizeParsedMessages(parsed.messages);
8403
+ this.committedMessages = this.normalizeParsedMessages(parsed.messages, this.currentTurnScope);
7788
8404
  const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
7789
8405
  if (promptForTrim) {
7790
8406
  const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
@@ -7825,7 +8441,9 @@ var init_provider_cli_adapter = __esm({
7825
8441
  const status = this.cliScripts.detectStatus({
7826
8442
  tail: text.slice(-500),
7827
8443
  screenText,
7828
- rawBuffer: this.accumulatedRawBuffer
8444
+ rawBuffer: this.accumulatedRawBuffer,
8445
+ screen: buildCliScreenSnapshot(screenText),
8446
+ tailScreen: buildCliScreenSnapshot(text.slice(-500))
7829
8447
  });
7830
8448
  return this.refineDetectedStatus(status, text, screenText || "");
7831
8449
  } catch (e) {
@@ -7836,11 +8454,16 @@ var init_provider_cli_adapter = __esm({
7836
8454
  runParseApproval(tail) {
7837
8455
  if (!this.cliScripts?.parseApproval) return null;
7838
8456
  try {
8457
+ const screenText = this.terminalScreen.getText();
8458
+ const buffer = screenText || this.accumulatedBuffer;
7839
8459
  return this.cliScripts.parseApproval({
7840
- buffer: this.terminalScreen.getText() || this.accumulatedBuffer,
7841
- screenText: this.terminalScreen.getText(),
8460
+ buffer,
8461
+ screenText,
7842
8462
  rawBuffer: this.accumulatedRawBuffer,
7843
- tail
8463
+ tail,
8464
+ screen: buildCliScreenSnapshot(screenText),
8465
+ bufferScreen: buildCliScreenSnapshot(buffer),
8466
+ tailScreen: buildCliScreenSnapshot(tail)
7844
8467
  });
7845
8468
  } catch (e) {
7846
8469
  LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e.message}`);
@@ -7856,6 +8479,21 @@ var init_provider_cli_adapter = __esm({
7856
8479
  activeModal: this.activeModal
7857
8480
  };
7858
8481
  }
8482
+ seedCommittedMessages(messages) {
8483
+ const normalized = (Array.isArray(messages) ? messages : []).filter((message) => message && (message.role === "user" || message.role === "assistant")).map((message) => ({
8484
+ role: message.role,
8485
+ content: typeof message.content === "string" ? message.content : String(message.content || ""),
8486
+ timestamp: typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0,
8487
+ receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : void 0,
8488
+ kind: typeof message.kind === "string" ? message.kind : void 0,
8489
+ id: typeof message.id === "string" ? message.id : void 0,
8490
+ index: typeof message.index === "number" ? message.index : void 0,
8491
+ meta: message.meta && typeof message.meta === "object" ? { ...message.meta } : void 0,
8492
+ senderName: typeof message.senderName === "string" ? message.senderName : void 0
8493
+ }));
8494
+ this.committedMessages = normalized;
8495
+ this.syncMessageViews();
8496
+ }
7859
8497
  /**
7860
8498
  * Script-based full parse — returns ReadChatResult.
7861
8499
  * Called by command handler / dashboard for rich content rendering.
@@ -7866,12 +8504,20 @@ var init_provider_cli_adapter = __esm({
7866
8504
  this.responseBuffer,
7867
8505
  this.currentTurnScope
7868
8506
  );
8507
+ const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
7869
8508
  if (parsed && Array.isArray(parsed.messages)) {
8509
+ const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
8510
+ ...message,
8511
+ id: message.id || `msg_${index}`,
8512
+ index: typeof message.index === "number" ? message.index : index,
8513
+ kind: message.kind || "standard",
8514
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
8515
+ })) : this.hydrateParsedMessages(parsed.messages, this.currentTurnScope);
7870
8516
  return {
7871
8517
  id: parsed.id || "cli_session",
7872
8518
  status: parsed.status || this.currentStatus,
7873
8519
  title: parsed.title || this.cliName,
7874
- messages: parsed.messages,
8520
+ messages: hydratedMessages,
7875
8521
  activeModal: parsed.activeModal ?? this.activeModal,
7876
8522
  providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0
7877
8523
  };
@@ -7892,6 +8538,21 @@ var init_provider_cli_adapter = __esm({
7892
8538
  activeModal: this.activeModal
7893
8539
  };
7894
8540
  }
8541
+ async invokeScript(scriptName, args) {
8542
+ const fn = this.cliScripts?.[scriptName];
8543
+ if (typeof fn !== "function") {
8544
+ throw new Error(`CLI script '${scriptName}' not available`);
8545
+ }
8546
+ const input = this.buildParseInput(
8547
+ this.committedMessages,
8548
+ this.responseBuffer,
8549
+ this.currentTurnScope
8550
+ );
8551
+ return await Promise.resolve(fn({
8552
+ ...input,
8553
+ args: args && typeof args === "object" ? { ...args } : {}
8554
+ }));
8555
+ }
7895
8556
  parseCurrentTranscript(baseMessages, partialResponse, scope) {
7896
8557
  if (!this.cliScripts?.parseOutput) return null;
7897
8558
  try {
@@ -7947,12 +8608,23 @@ ${data.message || ""}`.trim();
7947
8608
  if (this.startupParseGate) {
7948
8609
  const deadline = Date.now() + 1e4;
7949
8610
  while (this.startupParseGate && Date.now() < deadline) {
8611
+ this.resolveStartupState("send_wait");
7950
8612
  await new Promise((resolve13) => setTimeout(resolve13, 50));
7951
8613
  }
7952
8614
  }
8615
+ await this.waitForInteractivePrompt();
8616
+ if (!this.ready) {
8617
+ this.resolveStartupState("send_precheck");
8618
+ const screenText = this.terminalScreen.getText() || "";
8619
+ const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
8620
+ if (hasPrompt && this.currentStatus === "idle") {
8621
+ this.ready = true;
8622
+ this.startupParseGate = false;
8623
+ LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
8624
+ }
8625
+ }
7953
8626
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
7954
8627
  if (this.isWaitingForResponse) return;
7955
- await this.waitForInteractivePrompt();
7956
8628
  const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
7957
8629
  if (blockingModal || this.currentStatus === "waiting_approval") {
7958
8630
  throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
@@ -7996,8 +8668,6 @@ ${data.message || ""}`.trim();
7996
8668
  }
7997
8669
  this.responseEpoch += 1;
7998
8670
  this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
7999
- this.setStatus("generating", "sendMessage");
8000
- this.onStatusChange?.();
8001
8671
  const startResponseTimeout = () => {
8002
8672
  if (this.responseTimeout) clearTimeout(this.responseTimeout);
8003
8673
  this.responseTimeout = setTimeout(() => {
@@ -8016,7 +8686,7 @@ ${data.message || ""}`.trim();
8016
8686
  const retrySubmitIfStuck = (attempt) => {
8017
8687
  this.submitRetryTimer = null;
8018
8688
  if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
8019
- if (this.currentStatus !== "generating") return;
8689
+ if (this.currentStatus === "waiting_approval") return;
8020
8690
  if ((this.responseBuffer || "").trim()) return;
8021
8691
  const screenText = this.terminalScreen.getText();
8022
8692
  if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
@@ -8051,7 +8721,7 @@ ${data.message || ""}`.trim();
8051
8721
  this.submitRetryTimer = setTimeout(() => {
8052
8722
  this.submitRetryTimer = null;
8053
8723
  if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
8054
- if (this.currentStatus !== "generating") return;
8724
+ if (this.currentStatus === "waiting_approval") return;
8055
8725
  if ((this.responseBuffer || "").trim()) return;
8056
8726
  const screenText = this.terminalScreen.getText();
8057
8727
  if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
@@ -8483,6 +9153,7 @@ var init_cli_provider_instance = __esm({
8483
9153
  init_status_monitor();
8484
9154
  init_chat_history();
8485
9155
  init_logger();
9156
+ init_control_effects();
8486
9157
  CachedDatabaseSync = null;
8487
9158
  CliProviderInstance = class {
8488
9159
  constructor(provider, workingDir, cliArgs = [], instanceId, transportFactory, options) {
@@ -8511,6 +9182,8 @@ var init_cli_provider_instance = __esm({
8511
9182
  generatingDebounceTimer = null;
8512
9183
  generatingDebouncePending = null;
8513
9184
  lastApprovalEventAt = 0;
9185
+ controlValues = {};
9186
+ appliedEffectKeys = /* @__PURE__ */ new Set();
8514
9187
  historyWriter;
8515
9188
  runtimeMessages = [];
8516
9189
  instanceId;
@@ -8523,6 +9196,7 @@ var init_cli_provider_instance = __esm({
8523
9196
  async init(context) {
8524
9197
  this.context = context;
8525
9198
  this.settings = context.settings || {};
9199
+ this.adapter.updateRuntimeSettings?.(this.settings);
8526
9200
  this.monitor.updateConfig({
8527
9201
  approvalAlert: this.settings.approvalAlert !== false,
8528
9202
  longGeneratingAlert: this.settings.longGeneratingAlert !== false,
@@ -8538,6 +9212,21 @@ var init_cli_provider_instance = __esm({
8538
9212
  this.detectStatusTransition();
8539
9213
  });
8540
9214
  await this.adapter.spawn();
9215
+ if (this.providerSessionId) {
9216
+ const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
9217
+ if (restoredHistory.messages.length > 0) {
9218
+ this.adapter.seedCommittedMessages(
9219
+ restoredHistory.messages.map((message) => ({
9220
+ role: message.role,
9221
+ content: message.content,
9222
+ timestamp: message.receivedAt,
9223
+ receivedAt: message.receivedAt,
9224
+ kind: message.kind,
9225
+ senderName: message.senderName
9226
+ }))
9227
+ );
9228
+ }
9229
+ }
8541
9230
  if (this.providerSessionId && this.launchMode === "resume") {
8542
9231
  const resumedAt = Date.now();
8543
9232
  this.historyWriter.appendSystemMarker(
@@ -8618,6 +9307,12 @@ var init_cli_provider_instance = __esm({
8618
9307
  }
8619
9308
  const runtime = this.adapter.getRuntimeMetadata();
8620
9309
  const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
9310
+ const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
9311
+ if (controlValues) {
9312
+ this.controlValues = controlValues;
9313
+ } else if (Object.keys(this.controlValues).length > 0) {
9314
+ this.controlValues = {};
9315
+ }
8621
9316
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
8622
9317
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
8623
9318
  if (parsedMessages.length > 0) {
@@ -8638,6 +9333,7 @@ var init_cli_provider_instance = __esm({
8638
9333
  );
8639
9334
  }
8640
9335
  }
9336
+ this.applyProviderResponse(parsedStatus, { phase: "immediate" });
8641
9337
  return {
8642
9338
  type: this.type,
8643
9339
  name: this.provider.name,
@@ -8667,8 +9363,7 @@ var init_cli_provider_instance = __esm({
8667
9363
  attachedClients: runtime.attachedClients || []
8668
9364
  } : void 0,
8669
9365
  resume: this.provider.resume,
8670
- controlValues: void 0,
8671
- // CLI controls not yet wired from stream
9366
+ controlValues: this.controlValues,
8672
9367
  providerControls: this.provider.controls
8673
9368
  };
8674
9369
  }
@@ -8679,6 +9374,15 @@ var init_cli_provider_instance = __esm({
8679
9374
  getPresentationMode() {
8680
9375
  return this.presentationMode;
8681
9376
  }
9377
+ updateSettings(newSettings) {
9378
+ this.settings = { ...newSettings };
9379
+ this.adapter.updateRuntimeSettings?.(this.settings);
9380
+ this.monitor.updateConfig({
9381
+ approvalAlert: this.settings.approvalAlert !== false,
9382
+ longGeneratingAlert: this.settings.longGeneratingAlert !== false,
9383
+ longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
9384
+ });
9385
+ }
8682
9386
  onEvent(event, data) {
8683
9387
  if (event === "send_message" && data?.text) {
8684
9388
  void this.adapter.sendMessage(data.text).catch((e) => {
@@ -8690,22 +9394,27 @@ var init_cli_provider_instance = __esm({
8690
9394
  void this.adapter.resolveAction(data).catch((e) => {
8691
9395
  LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
8692
9396
  });
9397
+ } else if (event === "provider_state_patch" && data && typeof data === "object") {
9398
+ this.applyProviderResponse(data, { phase: "immediate" });
8693
9399
  }
8694
9400
  }
8695
9401
  dispose() {
8696
9402
  this.adapter.shutdown();
8697
9403
  this.monitor.reset();
9404
+ this.appliedEffectKeys.clear();
8698
9405
  }
8699
9406
  completedDebounceTimer = null;
8700
9407
  completedDebouncePending = null;
8701
9408
  detectStatusTransition() {
8702
9409
  const now = Date.now();
8703
9410
  const adapterStatus = this.adapter.getStatus();
9411
+ const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
8704
9412
  const newStatus = adapterStatus.status;
8705
9413
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
8706
9414
  const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
8707
9415
  const partial2 = this.adapter.getPartialResponse();
8708
9416
  const progressFingerprint = newStatus === "generating" ? `${partial2 || ""}::${adapterStatus.messages.at(-1)?.content || ""}`.slice(-2e3) : void 0;
9417
+ const previousStatus = this.lastStatus;
8709
9418
  if (newStatus !== this.lastStatus) {
8710
9419
  LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
8711
9420
  if (this.lastStatus === "idle" && newStatus === "generating") {
@@ -8798,6 +9507,9 @@ var init_cli_provider_instance = __esm({
8798
9507
  }
8799
9508
  this.lastStatus = newStatus;
8800
9509
  }
9510
+ this.applyProviderResponse(parsedStatus, {
9511
+ phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
9512
+ });
8801
9513
  const agentKey = `${this.type}:cli`;
8802
9514
  const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
8803
9515
  for (const me of monitorEvents) {
@@ -8813,6 +9525,88 @@ var init_cli_provider_instance = __esm({
8813
9525
  this.events = [];
8814
9526
  return events;
8815
9527
  }
9528
+ applyProviderResponse(data, options) {
9529
+ if (!data || typeof data !== "object") return;
9530
+ const controlValues = extractProviderControlValues(this.provider.controls, data);
9531
+ if (controlValues) {
9532
+ this.controlValues = { ...this.controlValues, ...controlValues };
9533
+ }
9534
+ const effects = normalizeProviderEffects(data);
9535
+ for (const effect of effects) {
9536
+ const effectWhen = effect.when || "immediate";
9537
+ if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
9538
+ if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
9539
+ const effectKey = this.getEffectDedupKey(effect);
9540
+ if (this.appliedEffectKeys.has(effectKey)) continue;
9541
+ this.appliedEffectKeys.add(effectKey);
9542
+ if (effect.persist !== false) {
9543
+ const persisted = this.getPersistedEffectContent(effect);
9544
+ if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
9545
+ }
9546
+ if (effect.type === "message" && effect.message) {
9547
+ const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
9548
+ this.pushEvent({
9549
+ event: "provider:message",
9550
+ timestamp: Date.now(),
9551
+ content,
9552
+ role: effect.message.role || "system",
9553
+ kind: effect.message.kind,
9554
+ senderName: effect.message.senderName
9555
+ });
9556
+ } else if (effect.type === "toast" && effect.toast) {
9557
+ this.pushEvent({
9558
+ event: "provider:toast",
9559
+ effectId: effect.id || effectKey,
9560
+ timestamp: Date.now(),
9561
+ message: effect.toast.message,
9562
+ level: effect.toast.level || "info"
9563
+ });
9564
+ } else if (effect.type === "notification" && effect.notification) {
9565
+ this.pushEvent({
9566
+ event: "provider:notification",
9567
+ effectId: effect.id || effectKey,
9568
+ timestamp: Date.now(),
9569
+ title: effect.notification.title,
9570
+ message: effect.notification.body,
9571
+ content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
9572
+ level: effect.notification.level || "info",
9573
+ channels: effect.notification.channels || ["toast"],
9574
+ preferenceKey: effect.notification.preferenceKey
9575
+ });
9576
+ }
9577
+ }
9578
+ if (this.appliedEffectKeys.size > 200) {
9579
+ this.appliedEffectKeys = new Set(Array.from(this.appliedEffectKeys).slice(-100));
9580
+ }
9581
+ }
9582
+ getEffectDedupKey(effect) {
9583
+ if (effect.id) return `provider_effect:${effect.id}`;
9584
+ if (effect.type === "message") {
9585
+ const content = typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
9586
+ return `provider_effect:message:${content}`;
9587
+ }
9588
+ if (effect.type === "notification") {
9589
+ return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
9590
+ }
9591
+ return `provider_effect:toast:${effect.toast?.message || ""}`;
9592
+ }
9593
+ getPersistedEffectContent(effect) {
9594
+ if (effect.type === "message") {
9595
+ return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
9596
+ }
9597
+ if (effect.type === "toast") {
9598
+ return effect.toast?.message || null;
9599
+ }
9600
+ if (effect.type === "notification") {
9601
+ if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
9602
+ if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
9603
+ return `${effect.notification.title}
9604
+ ${effect.notification.body || ""}`.trim();
9605
+ }
9606
+ return effect.notification?.body || null;
9607
+ }
9608
+ return null;
9609
+ }
8816
9610
  // ─── Adapter access (backward compat) ──────────────────
8817
9611
  getAdapter() {
8818
9612
  return this.adapter;
@@ -29667,6 +30461,22 @@ function getMacAppIdentifiers() {
29667
30461
  function getWinProcessNames() {
29668
30462
  return getProviderLoader().getWinProcessNames();
29669
30463
  }
30464
+ function getProviderMeta(ideId) {
30465
+ return getProviderLoader().getMeta(ideId);
30466
+ }
30467
+ function getPreferredLaunchMethod(ideId, platform11) {
30468
+ const prefer = getProviderMeta(ideId)?.launch?.prefer;
30469
+ const value = prefer?.[platform11];
30470
+ return value === "cli" || value === "app" || value === "auto" ? value : "auto";
30471
+ }
30472
+ function getCdpStartupTimeoutMs(ideId) {
30473
+ const value = getProviderMeta(ideId)?.launch?.cdpStartupTimeoutMs;
30474
+ if (typeof value !== "number" || !Number.isFinite(value)) return 15e3;
30475
+ return Math.max(1e3, Math.floor(value));
30476
+ }
30477
+ function escapeForAppleScript(value) {
30478
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
30479
+ }
29670
30480
  async function findFreePort(ports) {
29671
30481
  for (const port2 of ports) {
29672
30482
  const free = await checkPortFree(port2);
@@ -29719,12 +30529,12 @@ async function killIdeProcess(ideId) {
29719
30529
  try {
29720
30530
  if (plat === "darwin" && appName) {
29721
30531
  try {
29722
- (0, import_child_process6.execSync)(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
30532
+ (0, import_child_process6.execSync)(`osascript -e 'tell application "${escapeForAppleScript(appName)}" to quit' 2>/dev/null`, {
29723
30533
  timeout: 5e3
29724
30534
  });
29725
30535
  } catch {
29726
30536
  try {
29727
- (0, import_child_process6.execSync)(`pkill -f "${appName}" 2>/dev/null`);
30537
+ (0, import_child_process6.execSync)(`pkill -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
29728
30538
  } catch {
29729
30539
  }
29730
30540
  }
@@ -29754,7 +30564,7 @@ async function killIdeProcess(ideId) {
29754
30564
  }
29755
30565
  if (plat === "darwin" && appName) {
29756
30566
  try {
29757
- (0, import_child_process6.execSync)(`pkill -9 -f "${appName}" 2>/dev/null`);
30567
+ (0, import_child_process6.execSync)(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
29758
30568
  } catch {
29759
30569
  }
29760
30570
  } else if (plat === "win32" && winProcesses) {
@@ -29777,8 +30587,23 @@ function isIdeRunning(ideId) {
29777
30587
  if (plat === "darwin") {
29778
30588
  const appName = getMacAppIdentifiers()[ideId];
29779
30589
  if (!appName) return false;
29780
- const result = (0, import_child_process6.execSync)(`pgrep -f "${appName}" 2>/dev/null`, { encoding: "utf-8" });
29781
- return result.trim().length > 0;
30590
+ try {
30591
+ const result = (0, import_child_process6.execSync)(`pgrep -x "${appName}" 2>/dev/null`, {
30592
+ encoding: "utf-8",
30593
+ timeout: 3e3
30594
+ });
30595
+ return result.trim().length > 0;
30596
+ } catch {
30597
+ const result = (0, import_child_process6.execSync)(
30598
+ `osascript -e 'tell application "System Events" to count (every process whose name is "${escapeForAppleScript(appName)}")'`,
30599
+ {
30600
+ encoding: "utf-8",
30601
+ timeout: 3e3,
30602
+ stdio: ["pipe", "pipe", "pipe"]
30603
+ }
30604
+ );
30605
+ return Number.parseInt(result.trim() || "0", 10) > 0;
30606
+ }
29782
30607
  } else if (plat === "win32") {
29783
30608
  const winProcesses = getWinProcessNames()[ideId];
29784
30609
  if (!winProcesses) return false;
@@ -29927,7 +30752,8 @@ async function launchWithCdp(options = {}) {
29927
30752
  await launchLinux(targetIde, port, workspace, options.newWindow);
29928
30753
  }
29929
30754
  let cdpReady = false;
29930
- for (let i = 0; i < 30; i++) {
30755
+ const waitDeadline = Date.now() + getCdpStartupTimeoutMs(targetIde.id);
30756
+ while (Date.now() < waitDeadline) {
29931
30757
  await new Promise((r) => setTimeout(r, 500));
29932
30758
  if (await isCdpActive(port)) {
29933
30759
  cdpReady = true;
@@ -29956,14 +30782,18 @@ async function launchWithCdp(options = {}) {
29956
30782
  }
29957
30783
  async function launchMacOS(ide, port, workspace, newWindow) {
29958
30784
  const appName = getMacAppIdentifiers()[ide.id];
30785
+ const preferredMethod = getPreferredLaunchMethod(ide.id, "darwin");
29959
30786
  const args = ["--remote-debugging-port=" + port];
29960
30787
  if (newWindow) args.push("--new-window");
29961
30788
  if (workspace) args.push(workspace);
29962
- if (appName) {
30789
+ const canUseCli = !!ide.cliCommand;
30790
+ const canUseAppLauncher = !!appName;
30791
+ const useAppLauncher = preferredMethod === "app" ? canUseAppLauncher : preferredMethod === "cli" ? false : !canUseCli && canUseAppLauncher;
30792
+ if (!useAppLauncher && ide.cliCommand) {
30793
+ (0, import_child_process6.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
30794
+ } else if (appName) {
29963
30795
  const openArgs = ["-a", appName, "--args", ...args];
29964
30796
  (0, import_child_process6.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
29965
- } else if (ide.cliCommand) {
29966
- (0, import_child_process6.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
29967
30797
  } else {
29968
30798
  throw new Error(`No app identifier or CLI for ${ide.displayName}`);
29969
30799
  }
@@ -30281,6 +31111,7 @@ function buildStatusSnapshot(options) {
30281
31111
  workspaces: wsState.workspaces,
30282
31112
  defaultWorkspaceId: wsState.defaultWorkspaceId,
30283
31113
  defaultWorkspacePath: wsState.defaultWorkspacePath,
31114
+ terminalSizingMode: cfg.terminalSizingMode || "measured",
30284
31115
  recentLaunches: buildRecentLaunches(recentActivity),
30285
31116
  terminalBackend,
30286
31117
  availableProviders: buildAvailableProviders(options.providerLoader)
@@ -31129,6 +31960,7 @@ var ProviderStreamAdapter;
31129
31960
  var init_provider_adapter = __esm({
31130
31961
  "../../oss/packages/daemon-core/src/agent-stream/provider-adapter.ts"() {
31131
31962
  "use strict";
31963
+ init_control_effects();
31132
31964
  ProviderStreamAdapter = class {
31133
31965
  agentType;
31134
31966
  agentName;
@@ -31188,19 +32020,10 @@ var init_provider_adapter = __esm({
31188
32020
  mode: data.mode,
31189
32021
  activeModal: data.activeModal
31190
32022
  };
31191
- if (this.provider.controls?.length) {
31192
- const cv = {};
31193
- for (const ctrl of this.provider.controls) {
31194
- if (!ctrl.readFrom) continue;
31195
- const val = data[ctrl.readFrom];
31196
- if (val !== void 0 && val !== null) {
31197
- cv[ctrl.id] = typeof val === "object" ? val.name || val.id || String(val) : val;
31198
- }
31199
- }
31200
- if (data.model && !cv["model"]) cv["model"] = data.model;
31201
- if (data.mode && !cv["mode"]) cv["mode"] = data.mode;
31202
- if (Object.keys(cv).length > 0) state.controlValues = cv;
31203
- }
32023
+ const controlValues = extractProviderControlValues(this.provider.controls, data);
32024
+ if (controlValues) state.controlValues = controlValues;
32025
+ const effects = normalizeProviderEffects(data);
32026
+ if (effects.length > 0) state.effects = effects;
31204
32027
  if (state.messages.length > 0) {
31205
32028
  this.lastSuccessState = state;
31206
32029
  }
@@ -31760,6 +32583,8 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
31760
32583
  activeModal: stream.activeModal || null,
31761
32584
  model: stream.model || void 0,
31762
32585
  mode: stream.mode || void 0,
32586
+ controlValues: stream.controlValues || void 0,
32587
+ effects: stream.effects || void 0,
31763
32588
  sessionId: stream.sessionId || stream.instanceId || void 0,
31764
32589
  title: stream.title || stream.agentName || void 0,
31765
32590
  agentType: stream.agentType || void 0,
@@ -46443,7 +47268,7 @@ var init_adhdev_daemon = __esm({
46443
47268
  import_ws3 = require("ws");
46444
47269
  import_chalk2 = __toESM(require("chalk"));
46445
47270
  init_version();
46446
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.22" });
47271
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.24" });
46447
47272
  DANGEROUS_PATTERNS = [
46448
47273
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
46449
47274
  /\bsudo\b/i,