adhdev 0.8.59 → 0.8.61

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
@@ -2495,6 +2495,231 @@ var init_devtools = __esm({
2495
2495
  }
2496
2496
  });
2497
2497
 
2498
+ // ../../oss/packages/daemon-core/src/providers/io-contracts.ts
2499
+ function normalizeInputEnvelope(input) {
2500
+ const normalized = normalizeInputEnvelopePayload(input);
2501
+ const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
2502
+ return {
2503
+ parts: normalized.parts,
2504
+ textFallback,
2505
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
2506
+ };
2507
+ }
2508
+ function normalizeMessageParts(content) {
2509
+ if (typeof content === "string") return [{ type: "text", text: content }];
2510
+ if (!Array.isArray(content)) {
2511
+ if (content && typeof content === "object" && typeof content.text === "string") {
2512
+ return [{ type: "text", text: String(content.text) }];
2513
+ }
2514
+ return [];
2515
+ }
2516
+ const parts = [];
2517
+ for (const raw of content) {
2518
+ if (typeof raw === "string") {
2519
+ parts.push({ type: "text", text: raw });
2520
+ continue;
2521
+ }
2522
+ if (!raw || typeof raw !== "object") continue;
2523
+ const part = normalizeMessagePartObject(raw);
2524
+ if (part) parts.push(part);
2525
+ }
2526
+ return parts;
2527
+ }
2528
+ function flattenMessageParts(parts) {
2529
+ return parts.map((part) => {
2530
+ if (part.type === "text") return part.text;
2531
+ if (part.type === "resource") return part.resource.text || "";
2532
+ return "";
2533
+ }).filter((value) => value.length > 0).join("\n");
2534
+ }
2535
+ function normalizeInputEnvelopePayload(input) {
2536
+ if (typeof input === "string") {
2537
+ return { parts: [{ type: "text", text: input }], textFallback: input };
2538
+ }
2539
+ if (!input || typeof input !== "object") {
2540
+ return { parts: [], textFallback: "" };
2541
+ }
2542
+ const record2 = input;
2543
+ const nestedInput = record2.input;
2544
+ if (nestedInput && typeof nestedInput === "object") {
2545
+ const nested = nestedInput;
2546
+ return {
2547
+ parts: normalizeInputParts(nested.parts ?? nested.prompt),
2548
+ textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
2549
+ metadata: normalizeInputMetadata(nested.metadata)
2550
+ };
2551
+ }
2552
+ const directText = typeof record2.text === "string" ? record2.text : typeof record2.message === "string" ? record2.message : void 0;
2553
+ if (directText !== void 0) {
2554
+ return { parts: [{ type: "text", text: directText }], textFallback: directText };
2555
+ }
2556
+ const directParts = normalizeInputParts(record2.parts ?? record2.prompt);
2557
+ return {
2558
+ parts: directParts,
2559
+ textFallback: typeof record2.textFallback === "string" ? record2.textFallback : void 0,
2560
+ metadata: normalizeInputMetadata(record2.metadata)
2561
+ };
2562
+ }
2563
+ function normalizeInputMetadata(value) {
2564
+ if (!value || typeof value !== "object") return void 0;
2565
+ const record2 = value;
2566
+ const metadata = {};
2567
+ if (record2.source === "dashboard" || record2.source === "shortcut_api" || record2.source === "provider_script" || record2.source === "session_replay") {
2568
+ metadata.source = record2.source;
2569
+ }
2570
+ if (typeof record2.clientTimestamp === "number" && Number.isFinite(record2.clientTimestamp)) {
2571
+ metadata.clientTimestamp = record2.clientTimestamp;
2572
+ }
2573
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
2574
+ }
2575
+ function normalizeInputParts(value) {
2576
+ if (!Array.isArray(value)) return [];
2577
+ const parts = [];
2578
+ for (const raw of value) {
2579
+ if (typeof raw === "string") {
2580
+ parts.push({ type: "text", text: raw });
2581
+ continue;
2582
+ }
2583
+ if (!raw || typeof raw !== "object") continue;
2584
+ const part = normalizeInputPartObject(raw);
2585
+ if (part) parts.push(part);
2586
+ }
2587
+ return parts;
2588
+ }
2589
+ function normalizeInputPartObject(raw) {
2590
+ const type = raw.type;
2591
+ if (type === "text" && typeof raw.text === "string") {
2592
+ return { type, text: raw.text };
2593
+ }
2594
+ if (type === "image" && typeof raw.mimeType === "string") {
2595
+ return {
2596
+ type,
2597
+ mimeType: raw.mimeType,
2598
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
2599
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
2600
+ ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
2601
+ };
2602
+ }
2603
+ if (type === "audio" && typeof raw.mimeType === "string") {
2604
+ return {
2605
+ type,
2606
+ mimeType: raw.mimeType,
2607
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
2608
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
2609
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
2610
+ };
2611
+ }
2612
+ if (type === "video" && typeof raw.mimeType === "string") {
2613
+ return {
2614
+ type,
2615
+ mimeType: raw.mimeType,
2616
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
2617
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
2618
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
2619
+ };
2620
+ }
2621
+ if (type === "resource" && typeof raw.uri === "string") {
2622
+ return {
2623
+ type,
2624
+ uri: raw.uri,
2625
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
2626
+ ...typeof raw.name === "string" ? { name: raw.name } : {},
2627
+ ...typeof raw.text === "string" ? { text: raw.text } : {},
2628
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
2629
+ };
2630
+ }
2631
+ if (type === "resource_link" && typeof raw.uri === "string") {
2632
+ return {
2633
+ type: "resource",
2634
+ uri: raw.uri,
2635
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
2636
+ ...typeof raw.name === "string" ? { name: raw.name } : {}
2637
+ };
2638
+ }
2639
+ return null;
2640
+ }
2641
+ function normalizeMessagePartObject(raw) {
2642
+ const type = raw.type;
2643
+ if (type === "text" && typeof raw.text === "string") {
2644
+ return { type, text: raw.text };
2645
+ }
2646
+ if (type === "image" && typeof raw.mimeType === "string") {
2647
+ return {
2648
+ type,
2649
+ mimeType: raw.mimeType,
2650
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
2651
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
2652
+ };
2653
+ }
2654
+ if (type === "audio" && typeof raw.mimeType === "string") {
2655
+ return {
2656
+ type,
2657
+ mimeType: raw.mimeType,
2658
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
2659
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
2660
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
2661
+ };
2662
+ }
2663
+ if (type === "video" && typeof raw.mimeType === "string") {
2664
+ return {
2665
+ type,
2666
+ mimeType: raw.mimeType,
2667
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
2668
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
2669
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
2670
+ };
2671
+ }
2672
+ if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
2673
+ return {
2674
+ type,
2675
+ uri: raw.uri,
2676
+ name: raw.name,
2677
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
2678
+ ...typeof raw.size === "number" ? { size: raw.size } : {}
2679
+ };
2680
+ }
2681
+ if (type === "resource" && raw.resource && typeof raw.resource === "object") {
2682
+ const resource = raw.resource;
2683
+ if (typeof resource.uri !== "string") return null;
2684
+ return {
2685
+ type,
2686
+ resource: {
2687
+ uri: resource.uri,
2688
+ ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
2689
+ ...typeof resource.text === "string" ? { text: resource.text } : {},
2690
+ ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
2691
+ }
2692
+ };
2693
+ }
2694
+ return null;
2695
+ }
2696
+ function flattenInputParts(parts) {
2697
+ return parts.map((part) => {
2698
+ if (part.type === "text") return part.text;
2699
+ if (part.type === "audio") return part.transcript || "";
2700
+ if (part.type === "resource") return part.text || "";
2701
+ return "";
2702
+ }).filter((value) => value.length > 0).join("\n");
2703
+ }
2704
+ var init_io_contracts = __esm({
2705
+ "../../oss/packages/daemon-core/src/providers/io-contracts.ts"() {
2706
+ "use strict";
2707
+ }
2708
+ });
2709
+
2710
+ // ../../oss/packages/daemon-core/src/providers/contracts.ts
2711
+ function flattenContent(content) {
2712
+ if (typeof content === "string") return content;
2713
+ return flattenMessageParts(normalizeMessageParts(content));
2714
+ }
2715
+ var init_contracts = __esm({
2716
+ "../../oss/packages/daemon-core/src/providers/contracts.ts"() {
2717
+ "use strict";
2718
+ init_io_contracts();
2719
+ init_io_contracts();
2720
+ }
2721
+ });
2722
+
2498
2723
  // ../../oss/packages/daemon-core/src/providers/status-monitor.ts
2499
2724
  var DEFAULT_MONITOR_CONFIG, StatusMonitor;
2500
2725
  var init_status_monitor = __esm({
@@ -2613,6 +2838,64 @@ var init_status_monitor = __esm({
2613
2838
  }
2614
2839
  });
2615
2840
 
2841
+ // ../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts
2842
+ function isBuiltinChatMessageKind(kind) {
2843
+ return typeof kind === "string" && KNOWN_CHAT_MESSAGE_KINDS.has(kind.trim().toLowerCase());
2844
+ }
2845
+ function normalizeChatMessageKind(kind, role) {
2846
+ const normalizedKind = typeof kind === "string" ? kind.trim().toLowerCase() : "";
2847
+ if (normalizedKind && KNOWN_CHAT_MESSAGE_KINDS.has(normalizedKind)) return normalizedKind;
2848
+ const normalizedRole = typeof role === "string" ? role.trim().toLowerCase() : "";
2849
+ return normalizedRole === "system" ? "system" : "standard";
2850
+ }
2851
+ function buildChatMessage(message) {
2852
+ return {
2853
+ ...message,
2854
+ kind: normalizeChatMessageKind(message?.kind, message?.role)
2855
+ };
2856
+ }
2857
+ function buildSystemChatMessage(message) {
2858
+ return buildChatMessage({
2859
+ ...message,
2860
+ role: "system",
2861
+ kind: message?.kind || "system"
2862
+ });
2863
+ }
2864
+ function buildRuntimeSystemChatMessage(message) {
2865
+ return buildSystemChatMessage({
2866
+ ...message,
2867
+ senderName: typeof message?.senderName === "string" && message.senderName.trim() ? message.senderName : "System"
2868
+ });
2869
+ }
2870
+ function buildAssistantChatMessage(message) {
2871
+ return buildChatMessage({
2872
+ ...message,
2873
+ role: "assistant",
2874
+ kind: message?.kind || "standard"
2875
+ });
2876
+ }
2877
+ function buildUserChatMessage(message) {
2878
+ return buildChatMessage({
2879
+ ...message,
2880
+ role: "user",
2881
+ kind: message?.kind || "standard"
2882
+ });
2883
+ }
2884
+ function normalizeChatMessage(message) {
2885
+ return buildChatMessage(message);
2886
+ }
2887
+ function normalizeChatMessages(messages) {
2888
+ return (Array.isArray(messages) ? messages : []).map((message) => normalizeChatMessage(message));
2889
+ }
2890
+ var BUILTIN_CHAT_MESSAGE_KINDS, KNOWN_CHAT_MESSAGE_KINDS;
2891
+ var init_chat_message_normalization = __esm({
2892
+ "../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts"() {
2893
+ "use strict";
2894
+ BUILTIN_CHAT_MESSAGE_KINDS = ["standard", "thought", "tool", "terminal", "system"];
2895
+ KNOWN_CHAT_MESSAGE_KINDS = new Set(BUILTIN_CHAT_MESSAGE_KINDS);
2896
+ }
2897
+ });
2898
+
2616
2899
  // ../../oss/packages/daemon-core/src/providers/control-effects.ts
2617
2900
  function extractProviderControlValues(controls, data) {
2618
2901
  if (!data || typeof data !== "object") return void 0;
@@ -2681,13 +2964,58 @@ function normalizeProviderEffects(data) {
2681
2964
  level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
2682
2965
  channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
2683
2966
  preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
2684
- bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
2967
+ bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0,
2968
+ bubbleKind: typeof raw.notification.bubbleKind === "string" ? raw.notification.bubbleKind : void 0,
2969
+ bubbleRole: raw.notification.bubbleRole === "assistant" || raw.notification.bubbleRole === "user" ? raw.notification.bubbleRole : raw.notification.bubbleRole === "system" ? "system" : void 0,
2970
+ bubbleSenderName: typeof raw.notification.bubbleSenderName === "string" ? raw.notification.bubbleSenderName : void 0
2685
2971
  }
2686
2972
  });
2687
2973
  }
2688
2974
  }
2689
2975
  return effects;
2690
2976
  }
2977
+ function buildPersistedProviderEffectMessage(effect) {
2978
+ if (!effect) return null;
2979
+ if (effect.type === "message" && effect.message) {
2980
+ const role = effect.message.role === "assistant" || effect.message.role === "user" ? effect.message.role : "system";
2981
+ if (role === "system") {
2982
+ return buildRuntimeSystemChatMessage({
2983
+ content: effect.message.content,
2984
+ kind: effect.message.kind,
2985
+ senderName: effect.message.senderName
2986
+ });
2987
+ }
2988
+ return buildChatMessage({
2989
+ role,
2990
+ content: effect.message.content,
2991
+ kind: effect.message.kind,
2992
+ senderName: effect.message.senderName
2993
+ });
2994
+ }
2995
+ if (effect.type === "notification" && effect.notification) {
2996
+ const bubbleContent = effect.notification.bubbleContent ?? formatNotificationBubbleFallback(effect.notification.title, effect.notification.body);
2997
+ const flattened = typeof bubbleContent === "string" ? bubbleContent.trim() : flattenContent(bubbleContent).trim();
2998
+ if (!flattened && (!Array.isArray(bubbleContent) || bubbleContent.length === 0)) return null;
2999
+ const role = effect.notification.bubbleRole === "assistant" || effect.notification.bubbleRole === "user" ? effect.notification.bubbleRole : "system";
3000
+ if (role === "system") {
3001
+ return buildRuntimeSystemChatMessage({
3002
+ content: bubbleContent,
3003
+ kind: effect.notification.bubbleKind,
3004
+ senderName: effect.notification.bubbleSenderName
3005
+ });
3006
+ }
3007
+ return buildChatMessage({
3008
+ role,
3009
+ content: bubbleContent,
3010
+ kind: effect.notification.bubbleKind,
3011
+ senderName: effect.notification.bubbleSenderName
3012
+ });
3013
+ }
3014
+ if (effect.type === "toast" && effect.toast?.message) {
3015
+ return buildRuntimeSystemChatMessage({ content: effect.toast.message });
3016
+ }
3017
+ return null;
3018
+ }
2691
3019
  function normalizeControlListResult(data) {
2692
3020
  if (data && typeof data === "object" && Array.isArray(data.options)) {
2693
3021
  return {
@@ -2754,9 +3082,18 @@ function normalizeControlValue(value) {
2754
3082
  }
2755
3083
  return String(value);
2756
3084
  }
3085
+ function formatNotificationBubbleFallback(title, body) {
3086
+ const cleanTitle = typeof title === "string" ? title.trim() : "";
3087
+ const cleanBody = String(body || "").trim();
3088
+ if (cleanTitle && cleanBody) return `${cleanTitle}
3089
+ ${cleanBody}`;
3090
+ return cleanTitle || cleanBody;
3091
+ }
2757
3092
  var init_control_effects = __esm({
2758
3093
  "../../oss/packages/daemon-core/src/providers/control-effects.ts"() {
2759
3094
  "use strict";
3095
+ init_contracts();
3096
+ init_chat_message_normalization();
2760
3097
  }
2761
3098
  });
2762
3099
 
@@ -2944,6 +3281,7 @@ var init_chat_history = __esm({
2944
3281
  fs3 = __toESM(require("fs"));
2945
3282
  path7 = __toESM(require("path"));
2946
3283
  os5 = __toESM(require("os"));
3284
+ init_chat_message_normalization();
2947
3285
  HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
2948
3286
  RETAIN_DAYS = 30;
2949
3287
  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;
@@ -3093,11 +3431,11 @@ var init_chat_history = __esm({
3093
3431
  this.appendNewMessages(
3094
3432
  agentType,
3095
3433
  [{
3096
- role: "system",
3097
- kind: "system",
3098
- content,
3099
- receivedAt: options.receivedAt,
3100
- senderName: options.senderName,
3434
+ ...buildRuntimeSystemChatMessage({
3435
+ content,
3436
+ receivedAt: options.receivedAt,
3437
+ senderName: options.senderName
3438
+ }),
3101
3439
  historyDedupKey: options.dedupKey
3102
3440
  }],
3103
3441
  options.sessionTitle,
@@ -3345,10 +3683,12 @@ var ExtensionProviderInstance;
3345
3683
  var init_extension_provider_instance = __esm({
3346
3684
  "../../oss/packages/daemon-core/src/providers/extension-provider-instance.ts"() {
3347
3685
  "use strict";
3686
+ init_contracts();
3348
3687
  init_status_monitor();
3349
3688
  init_control_effects();
3350
3689
  init_chat_history();
3351
3690
  init_provider_patch_state();
3691
+ init_chat_message_normalization();
3352
3692
  ExtensionProviderInstance = class {
3353
3693
  type;
3354
3694
  category = "extension";
@@ -3555,8 +3895,8 @@ var init_extension_provider_instance = __esm({
3555
3895
  if (this.appliedEffectKeys.has(effectKey)) continue;
3556
3896
  this.appliedEffectKeys.add(effectKey);
3557
3897
  if (effect.persist !== false) {
3558
- const persisted = this.getPersistedEffectContent(effect);
3559
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
3898
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
3899
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
3560
3900
  }
3561
3901
  if (effect.type === "message" && effect.message) {
3562
3902
  this.pushEvent({
@@ -3591,34 +3931,42 @@ var init_extension_provider_instance = __esm({
3591
3931
  }
3592
3932
  }
3593
3933
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
3594
- const normalizedContent = String(content || "").trim();
3595
- if (!normalizedContent) return;
3934
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
3935
+ content,
3936
+ receivedAt,
3937
+ timestamp: receivedAt
3938
+ }), dedupKey);
3939
+ }
3940
+ appendRuntimeMessage(message, dedupKey) {
3941
+ const normalizedMessage = buildChatMessage({
3942
+ ...message,
3943
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
3944
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
3945
+ });
3946
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
3947
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
3596
3948
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
3597
3949
  this.runtimeMessages.push({
3598
3950
  key: dedupKey,
3599
- message: {
3600
- role: "system",
3601
- senderName: "System",
3602
- content: normalizedContent,
3603
- receivedAt,
3604
- timestamp: receivedAt
3605
- }
3951
+ message: normalizedMessage
3606
3952
  });
3607
3953
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
3608
- this.historyWriter.appendNewMessages(
3609
- this.type,
3610
- [{
3611
- role: "system",
3612
- senderName: "System",
3613
- content: normalizedContent,
3614
- kind: "system",
3615
- receivedAt,
3616
- historyDedupKey: dedupKey
3617
- }],
3618
- this.chatTitle || this.agentName || this.provider.name,
3619
- this.instanceId,
3620
- this.chatId || this.instanceId
3621
- );
3954
+ if (normalizedContent) {
3955
+ this.historyWriter.appendNewMessages(
3956
+ this.type,
3957
+ [{
3958
+ role: normalizedMessage.role,
3959
+ senderName: normalizedMessage.senderName,
3960
+ kind: normalizedMessage.kind,
3961
+ content: normalizedContent,
3962
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
3963
+ historyDedupKey: dedupKey
3964
+ }],
3965
+ this.chatTitle || this.agentName || this.provider.name,
3966
+ this.instanceId,
3967
+ this.chatId || this.instanceId
3968
+ );
3969
+ }
3622
3970
  }
3623
3971
  /**
3624
3972
  * Assign stable receivedAt to extension messages.
@@ -3635,16 +3983,16 @@ var init_extension_provider_instance = __esm({
3635
3983
  nextHashes.set(hash2, msg.receivedAt);
3636
3984
  }
3637
3985
  this.prevMessageHashes = nextHashes;
3638
- return messages;
3986
+ return normalizeChatMessages(messages);
3639
3987
  }
3640
3988
  mergeConversationMessages(messages) {
3641
- if (this.runtimeMessages.length === 0) return messages;
3642
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
3989
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
3990
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
3643
3991
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
3644
3992
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
3645
3993
  if (aTime !== bTime) return aTime - bTime;
3646
3994
  return a.index - b.index;
3647
- }).map((entry) => entry.message);
3995
+ }).map((entry) => entry.message));
3648
3996
  }
3649
3997
  getPersistedEffectContent(effect) {
3650
3998
  if (effect.type === "message") {
@@ -3768,6 +4116,7 @@ var init_ide_provider_instance = __esm({
3768
4116
  "../../oss/packages/daemon-core/src/providers/ide-provider-instance.ts"() {
3769
4117
  "use strict";
3770
4118
  crypto2 = __toESM(require("crypto"));
4119
+ init_contracts();
3771
4120
  init_extension_provider_instance();
3772
4121
  init_status_monitor();
3773
4122
  init_chat_history();
@@ -3775,6 +4124,7 @@ var init_ide_provider_instance = __esm({
3775
4124
  init_control_effects();
3776
4125
  init_approval_utils();
3777
4126
  init_provider_patch_state();
4127
+ init_chat_message_normalization();
3778
4128
  IdeProviderInstance = class {
3779
4129
  type;
3780
4130
  category = "ide";
@@ -4155,8 +4505,8 @@ var init_ide_provider_instance = __esm({
4155
4505
  if (this.appliedEffectKeys.has(effectKey)) continue;
4156
4506
  this.appliedEffectKeys.add(effectKey);
4157
4507
  if (effect.persist !== false) {
4158
- const persisted = this.getPersistedEffectContent(effect);
4159
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
4508
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
4509
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
4160
4510
  }
4161
4511
  if (effect.type === "message" && effect.message) {
4162
4512
  this.pushEvent({
@@ -4191,8 +4541,20 @@ var init_ide_provider_instance = __esm({
4191
4541
  }
4192
4542
  }
4193
4543
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
4194
- const normalizedContent = String(content || "").trim();
4195
- if (!normalizedContent) return;
4544
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
4545
+ content,
4546
+ receivedAt,
4547
+ timestamp: receivedAt
4548
+ }), dedupKey);
4549
+ }
4550
+ appendRuntimeMessage(message, dedupKey) {
4551
+ const normalizedMessage = buildChatMessage({
4552
+ ...message,
4553
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
4554
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
4555
+ });
4556
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
4557
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
4196
4558
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
4197
4559
  if (!this.cachedChat) {
4198
4560
  this.cachedChat = {
@@ -4206,38 +4568,34 @@ var init_ide_provider_instance = __esm({
4206
4568
  }
4207
4569
  this.runtimeMessages.push({
4208
4570
  key: dedupKey,
4209
- message: {
4210
- role: "system",
4211
- senderName: "System",
4212
- content: normalizedContent,
4213
- receivedAt,
4214
- timestamp: receivedAt
4215
- }
4571
+ message: normalizedMessage
4216
4572
  });
4217
4573
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
4218
- this.historyWriter.appendNewMessages(
4219
- this.type,
4220
- [{
4221
- role: "system",
4222
- senderName: "System",
4223
- content: normalizedContent,
4224
- kind: "system",
4225
- receivedAt,
4226
- historyDedupKey: dedupKey
4227
- }],
4228
- this.cachedChat?.title || this.provider.name,
4229
- this.instanceId,
4230
- this.cachedChat?.id || this.instanceId
4231
- );
4574
+ if (normalizedContent) {
4575
+ this.historyWriter.appendNewMessages(
4576
+ this.type,
4577
+ [{
4578
+ role: normalizedMessage.role,
4579
+ senderName: normalizedMessage.senderName,
4580
+ kind: normalizedMessage.kind,
4581
+ content: normalizedContent,
4582
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
4583
+ historyDedupKey: dedupKey
4584
+ }],
4585
+ this.cachedChat?.title || this.provider.name,
4586
+ this.instanceId,
4587
+ this.cachedChat?.id || this.instanceId
4588
+ );
4589
+ }
4232
4590
  }
4233
4591
  mergeConversationMessages(messages) {
4234
- if (this.runtimeMessages.length === 0) return messages;
4235
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
4592
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
4593
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
4236
4594
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
4237
4595
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
4238
4596
  if (aTime !== bTime) return aTime - bTime;
4239
4597
  return a.index - b.index;
4240
- }).map((entry) => entry.message);
4598
+ }).map((entry) => entry.message));
4241
4599
  }
4242
4600
  getPersistedEffectContent(effect) {
4243
4601
  if (effect.type === "message") {
@@ -5183,231 +5541,6 @@ var init_reconcile = __esm({
5183
5541
  }
5184
5542
  });
5185
5543
 
5186
- // ../../oss/packages/daemon-core/src/providers/io-contracts.ts
5187
- function normalizeInputEnvelope(input) {
5188
- const normalized = normalizeInputEnvelopePayload(input);
5189
- const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
5190
- return {
5191
- parts: normalized.parts,
5192
- textFallback,
5193
- ...normalized.metadata ? { metadata: normalized.metadata } : {}
5194
- };
5195
- }
5196
- function normalizeMessageParts(content) {
5197
- if (typeof content === "string") return [{ type: "text", text: content }];
5198
- if (!Array.isArray(content)) {
5199
- if (content && typeof content === "object" && typeof content.text === "string") {
5200
- return [{ type: "text", text: String(content.text) }];
5201
- }
5202
- return [];
5203
- }
5204
- const parts = [];
5205
- for (const raw of content) {
5206
- if (typeof raw === "string") {
5207
- parts.push({ type: "text", text: raw });
5208
- continue;
5209
- }
5210
- if (!raw || typeof raw !== "object") continue;
5211
- const part = normalizeMessagePartObject(raw);
5212
- if (part) parts.push(part);
5213
- }
5214
- return parts;
5215
- }
5216
- function flattenMessageParts(parts) {
5217
- return parts.map((part) => {
5218
- if (part.type === "text") return part.text;
5219
- if (part.type === "resource") return part.resource.text || "";
5220
- return "";
5221
- }).filter((value) => value.length > 0).join("\n");
5222
- }
5223
- function normalizeInputEnvelopePayload(input) {
5224
- if (typeof input === "string") {
5225
- return { parts: [{ type: "text", text: input }], textFallback: input };
5226
- }
5227
- if (!input || typeof input !== "object") {
5228
- return { parts: [], textFallback: "" };
5229
- }
5230
- const record2 = input;
5231
- const nestedInput = record2.input;
5232
- if (nestedInput && typeof nestedInput === "object") {
5233
- const nested = nestedInput;
5234
- return {
5235
- parts: normalizeInputParts(nested.parts ?? nested.prompt),
5236
- textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
5237
- metadata: normalizeInputMetadata(nested.metadata)
5238
- };
5239
- }
5240
- const directText = typeof record2.text === "string" ? record2.text : typeof record2.message === "string" ? record2.message : void 0;
5241
- if (directText !== void 0) {
5242
- return { parts: [{ type: "text", text: directText }], textFallback: directText };
5243
- }
5244
- const directParts = normalizeInputParts(record2.parts ?? record2.prompt);
5245
- return {
5246
- parts: directParts,
5247
- textFallback: typeof record2.textFallback === "string" ? record2.textFallback : void 0,
5248
- metadata: normalizeInputMetadata(record2.metadata)
5249
- };
5250
- }
5251
- function normalizeInputMetadata(value) {
5252
- if (!value || typeof value !== "object") return void 0;
5253
- const record2 = value;
5254
- const metadata = {};
5255
- if (record2.source === "dashboard" || record2.source === "shortcut_api" || record2.source === "provider_script" || record2.source === "session_replay") {
5256
- metadata.source = record2.source;
5257
- }
5258
- if (typeof record2.clientTimestamp === "number" && Number.isFinite(record2.clientTimestamp)) {
5259
- metadata.clientTimestamp = record2.clientTimestamp;
5260
- }
5261
- return Object.keys(metadata).length > 0 ? metadata : void 0;
5262
- }
5263
- function normalizeInputParts(value) {
5264
- if (!Array.isArray(value)) return [];
5265
- const parts = [];
5266
- for (const raw of value) {
5267
- if (typeof raw === "string") {
5268
- parts.push({ type: "text", text: raw });
5269
- continue;
5270
- }
5271
- if (!raw || typeof raw !== "object") continue;
5272
- const part = normalizeInputPartObject(raw);
5273
- if (part) parts.push(part);
5274
- }
5275
- return parts;
5276
- }
5277
- function normalizeInputPartObject(raw) {
5278
- const type = raw.type;
5279
- if (type === "text" && typeof raw.text === "string") {
5280
- return { type, text: raw.text };
5281
- }
5282
- if (type === "image" && typeof raw.mimeType === "string") {
5283
- return {
5284
- type,
5285
- mimeType: raw.mimeType,
5286
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5287
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5288
- ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
5289
- };
5290
- }
5291
- if (type === "audio" && typeof raw.mimeType === "string") {
5292
- return {
5293
- type,
5294
- mimeType: raw.mimeType,
5295
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5296
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5297
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5298
- };
5299
- }
5300
- if (type === "video" && typeof raw.mimeType === "string") {
5301
- return {
5302
- type,
5303
- mimeType: raw.mimeType,
5304
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5305
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5306
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5307
- };
5308
- }
5309
- if (type === "resource" && typeof raw.uri === "string") {
5310
- return {
5311
- type,
5312
- uri: raw.uri,
5313
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5314
- ...typeof raw.name === "string" ? { name: raw.name } : {},
5315
- ...typeof raw.text === "string" ? { text: raw.text } : {},
5316
- ...typeof raw.data === "string" ? { data: raw.data } : {}
5317
- };
5318
- }
5319
- if (type === "resource_link" && typeof raw.uri === "string") {
5320
- return {
5321
- type: "resource",
5322
- uri: raw.uri,
5323
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5324
- ...typeof raw.name === "string" ? { name: raw.name } : {}
5325
- };
5326
- }
5327
- return null;
5328
- }
5329
- function normalizeMessagePartObject(raw) {
5330
- const type = raw.type;
5331
- if (type === "text" && typeof raw.text === "string") {
5332
- return { type, text: raw.text };
5333
- }
5334
- if (type === "image" && typeof raw.mimeType === "string") {
5335
- return {
5336
- type,
5337
- mimeType: raw.mimeType,
5338
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5339
- ...typeof raw.data === "string" ? { data: raw.data } : {}
5340
- };
5341
- }
5342
- if (type === "audio" && typeof raw.mimeType === "string") {
5343
- return {
5344
- type,
5345
- mimeType: raw.mimeType,
5346
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5347
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5348
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5349
- };
5350
- }
5351
- if (type === "video" && typeof raw.mimeType === "string") {
5352
- return {
5353
- type,
5354
- mimeType: raw.mimeType,
5355
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5356
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5357
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5358
- };
5359
- }
5360
- if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
5361
- return {
5362
- type,
5363
- uri: raw.uri,
5364
- name: raw.name,
5365
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5366
- ...typeof raw.size === "number" ? { size: raw.size } : {}
5367
- };
5368
- }
5369
- if (type === "resource" && raw.resource && typeof raw.resource === "object") {
5370
- const resource = raw.resource;
5371
- if (typeof resource.uri !== "string") return null;
5372
- return {
5373
- type,
5374
- resource: {
5375
- uri: resource.uri,
5376
- ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
5377
- ...typeof resource.text === "string" ? { text: resource.text } : {},
5378
- ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
5379
- }
5380
- };
5381
- }
5382
- return null;
5383
- }
5384
- function flattenInputParts(parts) {
5385
- return parts.map((part) => {
5386
- if (part.type === "text") return part.text;
5387
- if (part.type === "audio") return part.transcript || "";
5388
- if (part.type === "resource") return part.text || "";
5389
- return "";
5390
- }).filter((value) => value.length > 0).join("\n");
5391
- }
5392
- var init_io_contracts = __esm({
5393
- "../../oss/packages/daemon-core/src/providers/io-contracts.ts"() {
5394
- "use strict";
5395
- }
5396
- });
5397
-
5398
- // ../../oss/packages/daemon-core/src/providers/contracts.ts
5399
- function flattenContent(content) {
5400
- if (typeof content === "string") return content;
5401
- return flattenMessageParts(normalizeMessageParts(content));
5402
- }
5403
- var init_contracts = __esm({
5404
- "../../oss/packages/daemon-core/src/providers/contracts.ts"() {
5405
- "use strict";
5406
- init_io_contracts();
5407
- init_io_contracts();
5408
- }
5409
- });
5410
-
5411
5544
  // ../../oss/packages/daemon-core/src/logging/debug-config.ts
5412
5545
  function normalizeCategories(categories) {
5413
5546
  if (!Array.isArray(categories)) return [];
@@ -5675,7 +5808,7 @@ function normalizeReadChatCursor(args) {
5675
5808
  }
5676
5809
  function normalizeReadChatMessages(payload) {
5677
5810
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
5678
- return messages;
5811
+ return normalizeChatMessages(messages);
5679
5812
  }
5680
5813
  function deriveHistoryDedupKey(message) {
5681
5814
  const unitKey = typeof message._unitKey === "string" ? message._unitKey.trim() : "";
@@ -6653,6 +6786,7 @@ var init_chat_commands = __esm({
6653
6786
  init_chat_history();
6654
6787
  init_logger();
6655
6788
  init_debug_trace();
6789
+ init_chat_message_normalization();
6656
6790
  RECENT_SEND_WINDOW_MS = 1200;
6657
6791
  recentSendByTarget = /* @__PURE__ */ new Map();
6658
6792
  }
@@ -9635,6 +9769,7 @@ var init_provider_cli_adapter = __esm({
9635
9769
  init_terminal_screen();
9636
9770
  init_pty_transport();
9637
9771
  init_provider_cli_shared();
9772
+ init_chat_message_normalization();
9638
9773
  init_provider_cli_parse();
9639
9774
  init_provider_cli_config();
9640
9775
  init_provider_cli_runtime();
@@ -9745,6 +9880,7 @@ var init_provider_cli_adapter = __esm({
9745
9880
  static MAX_TRACE_ENTRIES = 250;
9746
9881
  providerResolutionMeta;
9747
9882
  static IDLE_FINISH_CONFIRM_MS = 2e3;
9883
+ static HERMES_IDLE_FINISH_CONFIRM_MS = 5e3;
9748
9884
  static STATUS_ACTIVITY_HOLD_MS = 2e3;
9749
9885
  static FINISH_RETRY_DELAY_MS = 300;
9750
9886
  static MAX_FINISH_RETRIES = 2;
@@ -9752,6 +9888,12 @@ var init_provider_cli_adapter = __esm({
9752
9888
  this.messages = [...this.committedMessages];
9753
9889
  this.structuredMessages = [...this.committedMessages];
9754
9890
  }
9891
+ getIdleFinishConfirmMs() {
9892
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
9893
+ }
9894
+ getStatusActivityHoldMs() {
9895
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
9896
+ }
9755
9897
  setStatus(status, trigger) {
9756
9898
  const prev = this.currentStatus;
9757
9899
  if (prev === status) return;
@@ -9774,6 +9916,7 @@ var init_provider_cli_adapter = __esm({
9774
9916
  }
9775
9917
  armIdleFinishCandidate(assistantLength) {
9776
9918
  const now = Date.now();
9919
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
9777
9920
  this.idleFinishCandidate = {
9778
9921
  armedAt: now,
9779
9922
  lastOutputAt: this.lastOutputAt,
@@ -9782,7 +9925,7 @@ var init_provider_cli_adapter = __esm({
9782
9925
  assistantLength
9783
9926
  };
9784
9927
  this.recordTrace("idle_candidate_armed", {
9785
- confirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
9928
+ confirmMs: idleFinishConfirmMs,
9786
9929
  candidate: this.idleFinishCandidate,
9787
9930
  ...buildCliTraceParseSnapshot({
9788
9931
  accumulatedBuffer: this.accumulatedBuffer,
@@ -9797,7 +9940,7 @@ var init_provider_cli_adapter = __esm({
9797
9940
  this.settleTimer = null;
9798
9941
  this.settledBuffer = this.recentOutputBuffer;
9799
9942
  this.evaluateSettled();
9800
- }, _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS);
9943
+ }, idleFinishConfirmMs);
9801
9944
  }
9802
9945
  recordTrace(type, payload = {}) {
9803
9946
  const entry = {
@@ -10176,7 +10319,8 @@ var init_provider_cli_adapter = __esm({
10176
10319
  hasRecentInteractiveActivity(now) {
10177
10320
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
10178
10321
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : Number.MAX_SAFE_INTEGER;
10179
- return quietForMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS || screenStableMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
10322
+ const holdMs = this.getStatusActivityHoldMs();
10323
+ return quietForMs < holdMs || screenStableMs < holdMs;
10180
10324
  }
10181
10325
  getStartupConfirmationModal(screenText) {
10182
10326
  const text = sanitizeTerminalText(String(screenText || ""));
@@ -10328,6 +10472,7 @@ var init_provider_cli_adapter = __esm({
10328
10472
  clearPendingScriptStatus();
10329
10473
  }
10330
10474
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
10475
+ const statusActivityHoldMs = this.getStatusActivityHoldMs();
10331
10476
  const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
10332
10477
  if (shouldHoldGenerating) {
10333
10478
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
@@ -10343,7 +10488,7 @@ var init_provider_cli_adapter = __esm({
10343
10488
  recentInteractiveActivity,
10344
10489
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
10345
10490
  lastScreenChangeAt: this.lastScreenChangeAt,
10346
- holdMs: _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS,
10491
+ holdMs: statusActivityHoldMs,
10347
10492
  ...buildCliTraceParseSnapshot({
10348
10493
  accumulatedBuffer: this.accumulatedBuffer,
10349
10494
  accumulatedRawBuffer: this.accumulatedRawBuffer,
@@ -10432,11 +10577,12 @@ var init_provider_cli_adapter = __esm({
10432
10577
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
10433
10578
  const hasAssistantTurn = !!lastParsedAssistant;
10434
10579
  const assistantLength = lastParsedAssistant?.content?.length || 0;
10435
- const idleQuietThresholdMs = Math.max(2e3, this.timeouts.outputSettle);
10436
- const idleStableThresholdMs = 2e3;
10580
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
10581
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
10582
+ const idleStableThresholdMs = idleFinishConfirmMs;
10437
10583
  const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
10438
10584
  const candidate = this.idleFinishCandidate;
10439
- 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;
10585
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
10440
10586
  const canFinishImmediately = idleReady && candidateQuiet;
10441
10587
  this.recordTrace("idle_decision", {
10442
10588
  visibleIdlePrompt,
@@ -10448,7 +10594,7 @@ var init_provider_cli_adapter = __esm({
10448
10594
  idleQuietThresholdMs,
10449
10595
  idleStableThresholdMs,
10450
10596
  idleReady,
10451
- idleFinishConfirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
10597
+ idleFinishConfirmMs,
10452
10598
  idleFinishCandidate: candidate,
10453
10599
  candidateQuiet,
10454
10600
  canFinishImmediately,
@@ -10681,11 +10827,10 @@ var init_provider_cli_adapter = __esm({
10681
10827
  );
10682
10828
  const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
10683
10829
  if (parsed && Array.isArray(parsed.messages)) {
10684
- const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
10830
+ const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => buildChatMessage({
10685
10831
  ...message,
10686
10832
  id: message.id || `msg_${index}`,
10687
10833
  index: typeof message.index === "number" ? message.index : index,
10688
- kind: message.kind || "standard",
10689
10834
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
10690
10835
  })) : hydrateCliParsedMessages(parsed.messages, {
10691
10836
  committedMessages: this.committedMessages,
@@ -10706,13 +10851,11 @@ var init_provider_cli_adapter = __esm({
10706
10851
  id: "cli_session",
10707
10852
  status: this.currentStatus,
10708
10853
  title: this.cliName,
10709
- messages: messages.slice(-50).map((message, index) => ({
10710
- id: `msg_${index}`,
10711
- role: message.role,
10712
- content: message.content,
10713
- timestamp: message.timestamp,
10714
- index,
10715
- kind: "standard"
10854
+ messages: messages.slice(-50).map((message, index) => buildChatMessage({
10855
+ ...message,
10856
+ id: message.id || `msg_${index}`,
10857
+ index: typeof message.index === "number" ? message.index : index,
10858
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
10716
10859
  })),
10717
10860
  activeModal: this.activeModal
10718
10861
  };
@@ -11395,6 +11538,7 @@ var init_cli_provider_instance = __esm({
11395
11538
  init_approval_utils();
11396
11539
  init_cli_script_results();
11397
11540
  init_provider_patch_state();
11541
+ init_chat_message_normalization();
11398
11542
  CachedDatabaseSync = null;
11399
11543
  CliProviderInstance = class {
11400
11544
  constructor(provider, workingDir, cliArgs = [], instanceId, transportFactory, options) {
@@ -11845,8 +11989,8 @@ var init_cli_provider_instance = __esm({
11845
11989
  if (this.appliedEffectKeys.has(effectKey)) continue;
11846
11990
  this.appliedEffectKeys.add(effectKey);
11847
11991
  if (effect.persist !== false) {
11848
- const persisted = this.getPersistedEffectContent(effect);
11849
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
11992
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
11993
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
11850
11994
  }
11851
11995
  if (effect.type === "message" && effect.message) {
11852
11996
  const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
@@ -11970,44 +12114,53 @@ ${effect.notification.body || ""}`.trim();
11970
12114
  );
11971
12115
  }
11972
12116
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
11973
- const normalizedContent = String(content || "").trim();
11974
- if (!normalizedContent) return;
12117
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
12118
+ content,
12119
+ receivedAt,
12120
+ timestamp: receivedAt
12121
+ }), dedupKey);
12122
+ }
12123
+ appendRuntimeMessage(message, dedupKey) {
12124
+ const normalizedMessage = buildChatMessage({
12125
+ ...message,
12126
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
12127
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
12128
+ });
12129
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
12130
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
11975
12131
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
11976
12132
  this.runtimeMessages.push({
11977
12133
  key: dedupKey,
11978
- message: {
11979
- role: "system",
11980
- senderName: "System",
11981
- content: normalizedContent,
11982
- receivedAt,
11983
- timestamp: receivedAt
11984
- }
12134
+ message: normalizedMessage
11985
12135
  });
11986
12136
  if (this.runtimeMessages.length > 50) {
11987
12137
  this.runtimeMessages = this.runtimeMessages.slice(-50);
11988
12138
  }
11989
- this.historyWriter.appendNewMessages(
11990
- this.type,
11991
- [{
11992
- role: "system",
11993
- senderName: "System",
11994
- content: normalizedContent,
11995
- receivedAt,
11996
- historyDedupKey: dedupKey
11997
- }],
11998
- this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
11999
- this.instanceId,
12000
- this.providerSessionId
12001
- );
12139
+ if (normalizedContent) {
12140
+ this.historyWriter.appendNewMessages(
12141
+ this.type,
12142
+ [{
12143
+ role: normalizedMessage.role,
12144
+ senderName: normalizedMessage.senderName,
12145
+ kind: normalizedMessage.kind,
12146
+ content: normalizedContent,
12147
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
12148
+ historyDedupKey: dedupKey
12149
+ }],
12150
+ this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
12151
+ this.instanceId,
12152
+ this.providerSessionId
12153
+ );
12154
+ }
12002
12155
  }
12003
12156
  mergeConversationMessages(parsedMessages) {
12004
- if (this.runtimeMessages.length === 0) return parsedMessages;
12005
- return [...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
12157
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(parsedMessages);
12158
+ return normalizeChatMessages([...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
12006
12159
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
12007
12160
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
12008
12161
  if (aTime !== bTime) return aTime - bTime;
12009
12162
  return a.index - b.index;
12010
- }).map((entry) => entry.message);
12163
+ }).map((entry) => entry.message));
12011
12164
  }
12012
12165
  formatApprovalRequestMessage(modalMessage, buttons) {
12013
12166
  const lines = ["Approval requested"];
@@ -28483,6 +28636,7 @@ var init_acp_provider_instance = __esm({
28483
28636
  init_contracts();
28484
28637
  init_status_monitor();
28485
28638
  init_summary_metadata();
28639
+ init_chat_message_normalization();
28486
28640
  init_logger();
28487
28641
  AcpProviderInstance = class {
28488
28642
  constructor(provider, workingDir, cliArgs = []) {
@@ -28553,24 +28707,21 @@ var init_acp_provider_instance = __esm({
28553
28707
  }
28554
28708
  getState() {
28555
28709
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
28556
- const recentMessages = this.messages.slice(-50).map((m) => {
28710
+ const recentMessages = normalizeChatMessages(this.messages.slice(-50).map((m) => {
28557
28711
  const content = this.truncateContent(m.content);
28558
- return {
28559
- role: m.role,
28560
- content,
28561
- timestamp: m.timestamp,
28562
- toolCalls: m.toolCalls
28563
- };
28564
- });
28712
+ return buildChatMessage({
28713
+ ...m,
28714
+ content
28715
+ });
28716
+ }));
28565
28717
  if (this.currentStatus === "generating" && (this.partialContent || this.partialBlocks.length > 0)) {
28566
28718
  const blocks = this.buildPartialBlocks();
28567
28719
  if (blocks.length > 0) {
28568
- recentMessages.push({
28569
- role: "assistant",
28720
+ recentMessages.push(buildAssistantChatMessage({
28570
28721
  content: blocks,
28571
28722
  timestamp: Date.now(),
28572
28723
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
28573
- });
28724
+ }));
28574
28725
  }
28575
28726
  }
28576
28727
  return {
@@ -28583,7 +28734,7 @@ var init_acp_provider_instance = __esm({
28583
28734
  id: this.sessionId || `${this.type}_${this.workingDir}`,
28584
28735
  title: `${this.provider.name} \xB7 ${dirName}`,
28585
28736
  status: this.currentStatus,
28586
- messages: recentMessages,
28737
+ messages: normalizeChatMessages(recentMessages),
28587
28738
  activeModal: this.currentStatus === "waiting_approval" ? {
28588
28739
  message: this.activeToolCalls.find((t) => t.status === "running")?.name || "Permission requested",
28589
28740
  buttons: ["Approve", "Reject"]
@@ -29115,11 +29266,10 @@ var init_acp_provider_instance = __esm({
29115
29266
  if (b.type === "resource") return { type: "resource", resource: b.resource };
29116
29267
  return { type: "text", text: flattenContent([b]) };
29117
29268
  }) : [{ type: "text", text }];
29118
- this.messages.push({
29119
- role: "user",
29269
+ this.messages.push(buildUserChatMessage({
29120
29270
  content: contentBlocks && contentBlocks.length > 0 ? contentBlocks : text,
29121
29271
  timestamp: Date.now()
29122
- });
29272
+ }));
29123
29273
  this.currentStatus = "generating";
29124
29274
  this.partialContent = "";
29125
29275
  this.partialBlocks = [];
@@ -29289,11 +29439,11 @@ var init_acp_provider_instance = __esm({
29289
29439
  content = m.content.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
29290
29440
  }
29291
29441
  if (content.trim()) {
29292
- this.messages.push({
29442
+ this.messages.push(buildChatMessage({
29293
29443
  role: m.role || "assistant",
29294
29444
  content: content.trim(),
29295
29445
  timestamp: Date.now()
29296
- });
29446
+ }));
29297
29447
  this.partialContent = "";
29298
29448
  }
29299
29449
  }
@@ -29369,12 +29519,11 @@ var init_acp_provider_instance = __esm({
29369
29519
  return b;
29370
29520
  }).filter((b) => b.type !== "text" || b.type === "text" && b.text.trim());
29371
29521
  if (finalBlocks.length > 0) {
29372
- this.messages.push({
29373
- role: "assistant",
29522
+ this.messages.push(buildAssistantChatMessage({
29374
29523
  content: finalBlocks.length === 1 && finalBlocks[0].type === "text" ? finalBlocks[0].text : finalBlocks,
29375
29524
  timestamp: Date.now(),
29376
29525
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
29377
- });
29526
+ }));
29378
29527
  }
29379
29528
  this.partialContent = "";
29380
29529
  this.partialBlocks = [];
@@ -29434,11 +29583,10 @@ var init_acp_provider_instance = __esm({
29434
29583
  appendSystemMessage(content, timestamp = Date.now()) {
29435
29584
  const normalizedContent = String(content || "").trim();
29436
29585
  if (!normalizedContent) return;
29437
- this.messages.push({
29438
- role: "system",
29586
+ this.messages.push(buildRuntimeSystemChatMessage({
29439
29587
  content: normalizedContent,
29440
29588
  timestamp
29441
- });
29589
+ }));
29442
29590
  if (this.messages.length > 200) {
29443
29591
  this.messages = this.messages.slice(-100);
29444
29592
  }
@@ -35991,6 +36139,7 @@ var init_poller = __esm({
35991
36139
  init_reconcile();
35992
36140
  init_logger();
35993
36141
  init_approval_utils();
36142
+ init_chat_message_normalization();
35994
36143
  AgentStreamPoller = class {
35995
36144
  deps;
35996
36145
  timer = null;
@@ -36146,12 +36295,9 @@ var init_poller = __esm({
36146
36295
  type: "message",
36147
36296
  id: effectId,
36148
36297
  persist: true,
36149
- message: {
36150
- role: "system",
36151
- senderName: "System",
36152
- kind: "system",
36298
+ message: buildRuntimeSystemChatMessage({
36153
36299
  content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
36154
- }
36300
+ })
36155
36301
  }
36156
36302
  ]
36157
36303
  };
@@ -42790,6 +42936,7 @@ var src_exports = {};
42790
42936
  __export(src_exports, {
42791
42937
  AcpProviderInstance: () => AcpProviderInstance,
42792
42938
  AgentStreamPoller: () => AgentStreamPoller,
42939
+ BUILTIN_CHAT_MESSAGE_KINDS: () => BUILTIN_CHAT_MESSAGE_KINDS,
42793
42940
  CdpDomHandlers: () => CdpDomHandlers,
42794
42941
  CliProviderInstance: () => CliProviderInstance,
42795
42942
  DAEMON_WS_PATH: () => DAEMON_WS_PATH,
@@ -42814,9 +42961,14 @@ __export(src_exports, {
42814
42961
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
42815
42962
  VersionArchive: () => VersionArchive,
42816
42963
  appendRecentActivity: () => appendRecentActivity,
42964
+ buildAssistantChatMessage: () => buildAssistantChatMessage,
42965
+ buildChatMessage: () => buildChatMessage,
42817
42966
  buildMachineInfo: () => buildMachineInfo,
42967
+ buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
42818
42968
  buildSessionEntries: () => buildSessionEntries,
42819
42969
  buildStatusSnapshot: () => buildStatusSnapshot,
42970
+ buildSystemChatMessage: () => buildSystemChatMessage,
42971
+ buildUserChatMessage: () => buildUserChatMessage,
42820
42972
  clearDebugTrace: () => clearDebugTrace,
42821
42973
  configureDebugTraceStore: () => configureDebugTraceStore,
42822
42974
  connectCdpManager: () => connectCdpManager,
@@ -42848,6 +43000,7 @@ __export(src_exports, {
42848
43000
  initDaemonComponents: () => initDaemonComponents,
42849
43001
  installExtensions: () => installExtensions,
42850
43002
  installGlobalInterceptor: () => installGlobalInterceptor,
43003
+ isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
42851
43004
  isCdpConnected: () => isCdpConnected,
42852
43005
  isExtensionInstalled: () => isExtensionInstalled,
42853
43006
  isIdeRunning: () => isIdeRunning,
@@ -42866,6 +43019,9 @@ __export(src_exports, {
42866
43019
  markSetupComplete: () => markSetupComplete,
42867
43020
  maybeRunDaemonUpgradeHelperFromEnv: () => maybeRunDaemonUpgradeHelperFromEnv,
42868
43021
  normalizeActiveChatData: () => normalizeActiveChatData,
43022
+ normalizeChatMessage: () => normalizeChatMessage,
43023
+ normalizeChatMessageKind: () => normalizeChatMessageKind,
43024
+ normalizeChatMessages: () => normalizeChatMessages,
42869
43025
  normalizeInputEnvelope: () => normalizeInputEnvelope,
42870
43026
  normalizeManagedStatus: () => normalizeManagedStatus,
42871
43027
  normalizeMessageParts: () => normalizeMessageParts,
@@ -42935,6 +43091,7 @@ var init_src = __esm({
42935
43091
  init_acp_provider_instance();
42936
43092
  init_provider_source_config();
42937
43093
  init_io_contracts();
43094
+ init_chat_message_normalization();
42938
43095
  init_version_archive();
42939
43096
  init_dev_server();
42940
43097
  init_provider_cli_adapter();
@@ -52242,7 +52399,7 @@ var init_adhdev_daemon = __esm({
52242
52399
  import_ws3 = require("ws");
52243
52400
  init_source2();
52244
52401
  init_version();
52245
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.59" });
52402
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.61" });
52246
52403
  ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
52247
52404
  "generating",
52248
52405
  "waiting_approval",