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/cli/index.js CHANGED
@@ -3015,6 +3015,231 @@ var init_devtools = __esm({
3015
3015
  }
3016
3016
  });
3017
3017
 
3018
+ // ../../oss/packages/daemon-core/src/providers/io-contracts.ts
3019
+ function normalizeInputEnvelope(input) {
3020
+ const normalized = normalizeInputEnvelopePayload(input);
3021
+ const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
3022
+ return {
3023
+ parts: normalized.parts,
3024
+ textFallback,
3025
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
3026
+ };
3027
+ }
3028
+ function normalizeMessageParts(content) {
3029
+ if (typeof content === "string") return [{ type: "text", text: content }];
3030
+ if (!Array.isArray(content)) {
3031
+ if (content && typeof content === "object" && typeof content.text === "string") {
3032
+ return [{ type: "text", text: String(content.text) }];
3033
+ }
3034
+ return [];
3035
+ }
3036
+ const parts = [];
3037
+ for (const raw of content) {
3038
+ if (typeof raw === "string") {
3039
+ parts.push({ type: "text", text: raw });
3040
+ continue;
3041
+ }
3042
+ if (!raw || typeof raw !== "object") continue;
3043
+ const part = normalizeMessagePartObject(raw);
3044
+ if (part) parts.push(part);
3045
+ }
3046
+ return parts;
3047
+ }
3048
+ function flattenMessageParts(parts) {
3049
+ return parts.map((part) => {
3050
+ if (part.type === "text") return part.text;
3051
+ if (part.type === "resource") return part.resource.text || "";
3052
+ return "";
3053
+ }).filter((value) => value.length > 0).join("\n");
3054
+ }
3055
+ function normalizeInputEnvelopePayload(input) {
3056
+ if (typeof input === "string") {
3057
+ return { parts: [{ type: "text", text: input }], textFallback: input };
3058
+ }
3059
+ if (!input || typeof input !== "object") {
3060
+ return { parts: [], textFallback: "" };
3061
+ }
3062
+ const record2 = input;
3063
+ const nestedInput = record2.input;
3064
+ if (nestedInput && typeof nestedInput === "object") {
3065
+ const nested = nestedInput;
3066
+ return {
3067
+ parts: normalizeInputParts(nested.parts ?? nested.prompt),
3068
+ textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
3069
+ metadata: normalizeInputMetadata(nested.metadata)
3070
+ };
3071
+ }
3072
+ const directText = typeof record2.text === "string" ? record2.text : typeof record2.message === "string" ? record2.message : void 0;
3073
+ if (directText !== void 0) {
3074
+ return { parts: [{ type: "text", text: directText }], textFallback: directText };
3075
+ }
3076
+ const directParts = normalizeInputParts(record2.parts ?? record2.prompt);
3077
+ return {
3078
+ parts: directParts,
3079
+ textFallback: typeof record2.textFallback === "string" ? record2.textFallback : void 0,
3080
+ metadata: normalizeInputMetadata(record2.metadata)
3081
+ };
3082
+ }
3083
+ function normalizeInputMetadata(value) {
3084
+ if (!value || typeof value !== "object") return void 0;
3085
+ const record2 = value;
3086
+ const metadata = {};
3087
+ if (record2.source === "dashboard" || record2.source === "shortcut_api" || record2.source === "provider_script" || record2.source === "session_replay") {
3088
+ metadata.source = record2.source;
3089
+ }
3090
+ if (typeof record2.clientTimestamp === "number" && Number.isFinite(record2.clientTimestamp)) {
3091
+ metadata.clientTimestamp = record2.clientTimestamp;
3092
+ }
3093
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
3094
+ }
3095
+ function normalizeInputParts(value) {
3096
+ if (!Array.isArray(value)) return [];
3097
+ const parts = [];
3098
+ for (const raw of value) {
3099
+ if (typeof raw === "string") {
3100
+ parts.push({ type: "text", text: raw });
3101
+ continue;
3102
+ }
3103
+ if (!raw || typeof raw !== "object") continue;
3104
+ const part = normalizeInputPartObject(raw);
3105
+ if (part) parts.push(part);
3106
+ }
3107
+ return parts;
3108
+ }
3109
+ function normalizeInputPartObject(raw) {
3110
+ const type = raw.type;
3111
+ if (type === "text" && typeof raw.text === "string") {
3112
+ return { type, text: raw.text };
3113
+ }
3114
+ if (type === "image" && typeof raw.mimeType === "string") {
3115
+ return {
3116
+ type,
3117
+ mimeType: raw.mimeType,
3118
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
3119
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
3120
+ ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
3121
+ };
3122
+ }
3123
+ if (type === "audio" && typeof raw.mimeType === "string") {
3124
+ return {
3125
+ type,
3126
+ mimeType: raw.mimeType,
3127
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
3128
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
3129
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
3130
+ };
3131
+ }
3132
+ if (type === "video" && typeof raw.mimeType === "string") {
3133
+ return {
3134
+ type,
3135
+ mimeType: raw.mimeType,
3136
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
3137
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
3138
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
3139
+ };
3140
+ }
3141
+ if (type === "resource" && typeof raw.uri === "string") {
3142
+ return {
3143
+ type,
3144
+ uri: raw.uri,
3145
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
3146
+ ...typeof raw.name === "string" ? { name: raw.name } : {},
3147
+ ...typeof raw.text === "string" ? { text: raw.text } : {},
3148
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
3149
+ };
3150
+ }
3151
+ if (type === "resource_link" && typeof raw.uri === "string") {
3152
+ return {
3153
+ type: "resource",
3154
+ uri: raw.uri,
3155
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
3156
+ ...typeof raw.name === "string" ? { name: raw.name } : {}
3157
+ };
3158
+ }
3159
+ return null;
3160
+ }
3161
+ function normalizeMessagePartObject(raw) {
3162
+ const type = raw.type;
3163
+ if (type === "text" && typeof raw.text === "string") {
3164
+ return { type, text: raw.text };
3165
+ }
3166
+ if (type === "image" && typeof raw.mimeType === "string") {
3167
+ return {
3168
+ type,
3169
+ mimeType: raw.mimeType,
3170
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
3171
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
3172
+ };
3173
+ }
3174
+ if (type === "audio" && typeof raw.mimeType === "string") {
3175
+ return {
3176
+ type,
3177
+ mimeType: raw.mimeType,
3178
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
3179
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
3180
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
3181
+ };
3182
+ }
3183
+ if (type === "video" && typeof raw.mimeType === "string") {
3184
+ return {
3185
+ type,
3186
+ mimeType: raw.mimeType,
3187
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
3188
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
3189
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
3190
+ };
3191
+ }
3192
+ if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
3193
+ return {
3194
+ type,
3195
+ uri: raw.uri,
3196
+ name: raw.name,
3197
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
3198
+ ...typeof raw.size === "number" ? { size: raw.size } : {}
3199
+ };
3200
+ }
3201
+ if (type === "resource" && raw.resource && typeof raw.resource === "object") {
3202
+ const resource = raw.resource;
3203
+ if (typeof resource.uri !== "string") return null;
3204
+ return {
3205
+ type,
3206
+ resource: {
3207
+ uri: resource.uri,
3208
+ ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
3209
+ ...typeof resource.text === "string" ? { text: resource.text } : {},
3210
+ ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
3211
+ }
3212
+ };
3213
+ }
3214
+ return null;
3215
+ }
3216
+ function flattenInputParts(parts) {
3217
+ return parts.map((part) => {
3218
+ if (part.type === "text") return part.text;
3219
+ if (part.type === "audio") return part.transcript || "";
3220
+ if (part.type === "resource") return part.text || "";
3221
+ return "";
3222
+ }).filter((value) => value.length > 0).join("\n");
3223
+ }
3224
+ var init_io_contracts = __esm({
3225
+ "../../oss/packages/daemon-core/src/providers/io-contracts.ts"() {
3226
+ "use strict";
3227
+ }
3228
+ });
3229
+
3230
+ // ../../oss/packages/daemon-core/src/providers/contracts.ts
3231
+ function flattenContent(content) {
3232
+ if (typeof content === "string") return content;
3233
+ return flattenMessageParts(normalizeMessageParts(content));
3234
+ }
3235
+ var init_contracts = __esm({
3236
+ "../../oss/packages/daemon-core/src/providers/contracts.ts"() {
3237
+ "use strict";
3238
+ init_io_contracts();
3239
+ init_io_contracts();
3240
+ }
3241
+ });
3242
+
3018
3243
  // ../../oss/packages/daemon-core/src/providers/status-monitor.ts
3019
3244
  var DEFAULT_MONITOR_CONFIG, StatusMonitor;
3020
3245
  var init_status_monitor = __esm({
@@ -3133,6 +3358,64 @@ var init_status_monitor = __esm({
3133
3358
  }
3134
3359
  });
3135
3360
 
3361
+ // ../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts
3362
+ function isBuiltinChatMessageKind(kind) {
3363
+ return typeof kind === "string" && KNOWN_CHAT_MESSAGE_KINDS.has(kind.trim().toLowerCase());
3364
+ }
3365
+ function normalizeChatMessageKind(kind, role) {
3366
+ const normalizedKind = typeof kind === "string" ? kind.trim().toLowerCase() : "";
3367
+ if (normalizedKind && KNOWN_CHAT_MESSAGE_KINDS.has(normalizedKind)) return normalizedKind;
3368
+ const normalizedRole = typeof role === "string" ? role.trim().toLowerCase() : "";
3369
+ return normalizedRole === "system" ? "system" : "standard";
3370
+ }
3371
+ function buildChatMessage(message) {
3372
+ return {
3373
+ ...message,
3374
+ kind: normalizeChatMessageKind(message?.kind, message?.role)
3375
+ };
3376
+ }
3377
+ function buildSystemChatMessage(message) {
3378
+ return buildChatMessage({
3379
+ ...message,
3380
+ role: "system",
3381
+ kind: message?.kind || "system"
3382
+ });
3383
+ }
3384
+ function buildRuntimeSystemChatMessage(message) {
3385
+ return buildSystemChatMessage({
3386
+ ...message,
3387
+ senderName: typeof message?.senderName === "string" && message.senderName.trim() ? message.senderName : "System"
3388
+ });
3389
+ }
3390
+ function buildAssistantChatMessage(message) {
3391
+ return buildChatMessage({
3392
+ ...message,
3393
+ role: "assistant",
3394
+ kind: message?.kind || "standard"
3395
+ });
3396
+ }
3397
+ function buildUserChatMessage(message) {
3398
+ return buildChatMessage({
3399
+ ...message,
3400
+ role: "user",
3401
+ kind: message?.kind || "standard"
3402
+ });
3403
+ }
3404
+ function normalizeChatMessage(message) {
3405
+ return buildChatMessage(message);
3406
+ }
3407
+ function normalizeChatMessages(messages) {
3408
+ return (Array.isArray(messages) ? messages : []).map((message) => normalizeChatMessage(message));
3409
+ }
3410
+ var BUILTIN_CHAT_MESSAGE_KINDS, KNOWN_CHAT_MESSAGE_KINDS;
3411
+ var init_chat_message_normalization = __esm({
3412
+ "../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts"() {
3413
+ "use strict";
3414
+ BUILTIN_CHAT_MESSAGE_KINDS = ["standard", "thought", "tool", "terminal", "system"];
3415
+ KNOWN_CHAT_MESSAGE_KINDS = new Set(BUILTIN_CHAT_MESSAGE_KINDS);
3416
+ }
3417
+ });
3418
+
3136
3419
  // ../../oss/packages/daemon-core/src/providers/control-effects.ts
3137
3420
  function extractProviderControlValues(controls, data) {
3138
3421
  if (!data || typeof data !== "object") return void 0;
@@ -3201,13 +3484,58 @@ function normalizeProviderEffects(data) {
3201
3484
  level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
3202
3485
  channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
3203
3486
  preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
3204
- bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
3487
+ bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0,
3488
+ bubbleKind: typeof raw.notification.bubbleKind === "string" ? raw.notification.bubbleKind : void 0,
3489
+ bubbleRole: raw.notification.bubbleRole === "assistant" || raw.notification.bubbleRole === "user" ? raw.notification.bubbleRole : raw.notification.bubbleRole === "system" ? "system" : void 0,
3490
+ bubbleSenderName: typeof raw.notification.bubbleSenderName === "string" ? raw.notification.bubbleSenderName : void 0
3205
3491
  }
3206
3492
  });
3207
3493
  }
3208
3494
  }
3209
3495
  return effects;
3210
3496
  }
3497
+ function buildPersistedProviderEffectMessage(effect) {
3498
+ if (!effect) return null;
3499
+ if (effect.type === "message" && effect.message) {
3500
+ const role = effect.message.role === "assistant" || effect.message.role === "user" ? effect.message.role : "system";
3501
+ if (role === "system") {
3502
+ return buildRuntimeSystemChatMessage({
3503
+ content: effect.message.content,
3504
+ kind: effect.message.kind,
3505
+ senderName: effect.message.senderName
3506
+ });
3507
+ }
3508
+ return buildChatMessage({
3509
+ role,
3510
+ content: effect.message.content,
3511
+ kind: effect.message.kind,
3512
+ senderName: effect.message.senderName
3513
+ });
3514
+ }
3515
+ if (effect.type === "notification" && effect.notification) {
3516
+ const bubbleContent = effect.notification.bubbleContent ?? formatNotificationBubbleFallback(effect.notification.title, effect.notification.body);
3517
+ const flattened = typeof bubbleContent === "string" ? bubbleContent.trim() : flattenContent(bubbleContent).trim();
3518
+ if (!flattened && (!Array.isArray(bubbleContent) || bubbleContent.length === 0)) return null;
3519
+ const role = effect.notification.bubbleRole === "assistant" || effect.notification.bubbleRole === "user" ? effect.notification.bubbleRole : "system";
3520
+ if (role === "system") {
3521
+ return buildRuntimeSystemChatMessage({
3522
+ content: bubbleContent,
3523
+ kind: effect.notification.bubbleKind,
3524
+ senderName: effect.notification.bubbleSenderName
3525
+ });
3526
+ }
3527
+ return buildChatMessage({
3528
+ role,
3529
+ content: bubbleContent,
3530
+ kind: effect.notification.bubbleKind,
3531
+ senderName: effect.notification.bubbleSenderName
3532
+ });
3533
+ }
3534
+ if (effect.type === "toast" && effect.toast?.message) {
3535
+ return buildRuntimeSystemChatMessage({ content: effect.toast.message });
3536
+ }
3537
+ return null;
3538
+ }
3211
3539
  function normalizeControlListResult(data) {
3212
3540
  if (data && typeof data === "object" && Array.isArray(data.options)) {
3213
3541
  return {
@@ -3274,9 +3602,18 @@ function normalizeControlValue(value) {
3274
3602
  }
3275
3603
  return String(value);
3276
3604
  }
3605
+ function formatNotificationBubbleFallback(title, body) {
3606
+ const cleanTitle = typeof title === "string" ? title.trim() : "";
3607
+ const cleanBody = String(body || "").trim();
3608
+ if (cleanTitle && cleanBody) return `${cleanTitle}
3609
+ ${cleanBody}`;
3610
+ return cleanTitle || cleanBody;
3611
+ }
3277
3612
  var init_control_effects = __esm({
3278
3613
  "../../oss/packages/daemon-core/src/providers/control-effects.ts"() {
3279
3614
  "use strict";
3615
+ init_contracts();
3616
+ init_chat_message_normalization();
3280
3617
  }
3281
3618
  });
3282
3619
 
@@ -3464,6 +3801,7 @@ var init_chat_history = __esm({
3464
3801
  fs3 = __toESM(require("fs"));
3465
3802
  path7 = __toESM(require("path"));
3466
3803
  os6 = __toESM(require("os"));
3804
+ init_chat_message_normalization();
3467
3805
  HISTORY_DIR = path7.join(os6.homedir(), ".adhdev", "history");
3468
3806
  RETAIN_DAYS = 30;
3469
3807
  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;
@@ -3613,11 +3951,11 @@ var init_chat_history = __esm({
3613
3951
  this.appendNewMessages(
3614
3952
  agentType,
3615
3953
  [{
3616
- role: "system",
3617
- kind: "system",
3618
- content,
3619
- receivedAt: options.receivedAt,
3620
- senderName: options.senderName,
3954
+ ...buildRuntimeSystemChatMessage({
3955
+ content,
3956
+ receivedAt: options.receivedAt,
3957
+ senderName: options.senderName
3958
+ }),
3621
3959
  historyDedupKey: options.dedupKey
3622
3960
  }],
3623
3961
  options.sessionTitle,
@@ -3865,10 +4203,12 @@ var ExtensionProviderInstance;
3865
4203
  var init_extension_provider_instance = __esm({
3866
4204
  "../../oss/packages/daemon-core/src/providers/extension-provider-instance.ts"() {
3867
4205
  "use strict";
4206
+ init_contracts();
3868
4207
  init_status_monitor();
3869
4208
  init_control_effects();
3870
4209
  init_chat_history();
3871
4210
  init_provider_patch_state();
4211
+ init_chat_message_normalization();
3872
4212
  ExtensionProviderInstance = class {
3873
4213
  type;
3874
4214
  category = "extension";
@@ -4075,8 +4415,8 @@ var init_extension_provider_instance = __esm({
4075
4415
  if (this.appliedEffectKeys.has(effectKey)) continue;
4076
4416
  this.appliedEffectKeys.add(effectKey);
4077
4417
  if (effect.persist !== false) {
4078
- const persisted = this.getPersistedEffectContent(effect);
4079
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
4418
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
4419
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
4080
4420
  }
4081
4421
  if (effect.type === "message" && effect.message) {
4082
4422
  this.pushEvent({
@@ -4111,34 +4451,42 @@ var init_extension_provider_instance = __esm({
4111
4451
  }
4112
4452
  }
4113
4453
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
4114
- const normalizedContent = String(content || "").trim();
4115
- if (!normalizedContent) return;
4454
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
4455
+ content,
4456
+ receivedAt,
4457
+ timestamp: receivedAt
4458
+ }), dedupKey);
4459
+ }
4460
+ appendRuntimeMessage(message, dedupKey) {
4461
+ const normalizedMessage = buildChatMessage({
4462
+ ...message,
4463
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
4464
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
4465
+ });
4466
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
4467
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
4116
4468
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
4117
4469
  this.runtimeMessages.push({
4118
4470
  key: dedupKey,
4119
- message: {
4120
- role: "system",
4121
- senderName: "System",
4122
- content: normalizedContent,
4123
- receivedAt,
4124
- timestamp: receivedAt
4125
- }
4471
+ message: normalizedMessage
4126
4472
  });
4127
4473
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
4128
- this.historyWriter.appendNewMessages(
4129
- this.type,
4130
- [{
4131
- role: "system",
4132
- senderName: "System",
4133
- content: normalizedContent,
4134
- kind: "system",
4135
- receivedAt,
4136
- historyDedupKey: dedupKey
4137
- }],
4138
- this.chatTitle || this.agentName || this.provider.name,
4139
- this.instanceId,
4140
- this.chatId || this.instanceId
4141
- );
4474
+ if (normalizedContent) {
4475
+ this.historyWriter.appendNewMessages(
4476
+ this.type,
4477
+ [{
4478
+ role: normalizedMessage.role,
4479
+ senderName: normalizedMessage.senderName,
4480
+ kind: normalizedMessage.kind,
4481
+ content: normalizedContent,
4482
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
4483
+ historyDedupKey: dedupKey
4484
+ }],
4485
+ this.chatTitle || this.agentName || this.provider.name,
4486
+ this.instanceId,
4487
+ this.chatId || this.instanceId
4488
+ );
4489
+ }
4142
4490
  }
4143
4491
  /**
4144
4492
  * Assign stable receivedAt to extension messages.
@@ -4155,16 +4503,16 @@ var init_extension_provider_instance = __esm({
4155
4503
  nextHashes.set(hash2, msg.receivedAt);
4156
4504
  }
4157
4505
  this.prevMessageHashes = nextHashes;
4158
- return messages;
4506
+ return normalizeChatMessages(messages);
4159
4507
  }
4160
4508
  mergeConversationMessages(messages) {
4161
- if (this.runtimeMessages.length === 0) return messages;
4162
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
4509
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
4510
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
4163
4511
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
4164
4512
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
4165
4513
  if (aTime !== bTime) return aTime - bTime;
4166
4514
  return a.index - b.index;
4167
- }).map((entry) => entry.message);
4515
+ }).map((entry) => entry.message));
4168
4516
  }
4169
4517
  getPersistedEffectContent(effect) {
4170
4518
  if (effect.type === "message") {
@@ -4288,6 +4636,7 @@ var init_ide_provider_instance = __esm({
4288
4636
  "../../oss/packages/daemon-core/src/providers/ide-provider-instance.ts"() {
4289
4637
  "use strict";
4290
4638
  crypto2 = __toESM(require("crypto"));
4639
+ init_contracts();
4291
4640
  init_extension_provider_instance();
4292
4641
  init_status_monitor();
4293
4642
  init_chat_history();
@@ -4295,6 +4644,7 @@ var init_ide_provider_instance = __esm({
4295
4644
  init_control_effects();
4296
4645
  init_approval_utils();
4297
4646
  init_provider_patch_state();
4647
+ init_chat_message_normalization();
4298
4648
  IdeProviderInstance = class {
4299
4649
  type;
4300
4650
  category = "ide";
@@ -4675,8 +5025,8 @@ var init_ide_provider_instance = __esm({
4675
5025
  if (this.appliedEffectKeys.has(effectKey)) continue;
4676
5026
  this.appliedEffectKeys.add(effectKey);
4677
5027
  if (effect.persist !== false) {
4678
- const persisted = this.getPersistedEffectContent(effect);
4679
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
5028
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
5029
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
4680
5030
  }
4681
5031
  if (effect.type === "message" && effect.message) {
4682
5032
  this.pushEvent({
@@ -4711,8 +5061,20 @@ var init_ide_provider_instance = __esm({
4711
5061
  }
4712
5062
  }
4713
5063
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
4714
- const normalizedContent = String(content || "").trim();
4715
- if (!normalizedContent) return;
5064
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
5065
+ content,
5066
+ receivedAt,
5067
+ timestamp: receivedAt
5068
+ }), dedupKey);
5069
+ }
5070
+ appendRuntimeMessage(message, dedupKey) {
5071
+ const normalizedMessage = buildChatMessage({
5072
+ ...message,
5073
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
5074
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
5075
+ });
5076
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
5077
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
4716
5078
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
4717
5079
  if (!this.cachedChat) {
4718
5080
  this.cachedChat = {
@@ -4726,38 +5088,34 @@ var init_ide_provider_instance = __esm({
4726
5088
  }
4727
5089
  this.runtimeMessages.push({
4728
5090
  key: dedupKey,
4729
- message: {
4730
- role: "system",
4731
- senderName: "System",
4732
- content: normalizedContent,
4733
- receivedAt,
4734
- timestamp: receivedAt
4735
- }
5091
+ message: normalizedMessage
4736
5092
  });
4737
5093
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
4738
- this.historyWriter.appendNewMessages(
4739
- this.type,
4740
- [{
4741
- role: "system",
4742
- senderName: "System",
4743
- content: normalizedContent,
4744
- kind: "system",
4745
- receivedAt,
4746
- historyDedupKey: dedupKey
4747
- }],
4748
- this.cachedChat?.title || this.provider.name,
4749
- this.instanceId,
4750
- this.cachedChat?.id || this.instanceId
4751
- );
5094
+ if (normalizedContent) {
5095
+ this.historyWriter.appendNewMessages(
5096
+ this.type,
5097
+ [{
5098
+ role: normalizedMessage.role,
5099
+ senderName: normalizedMessage.senderName,
5100
+ kind: normalizedMessage.kind,
5101
+ content: normalizedContent,
5102
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
5103
+ historyDedupKey: dedupKey
5104
+ }],
5105
+ this.cachedChat?.title || this.provider.name,
5106
+ this.instanceId,
5107
+ this.cachedChat?.id || this.instanceId
5108
+ );
5109
+ }
4752
5110
  }
4753
5111
  mergeConversationMessages(messages) {
4754
- if (this.runtimeMessages.length === 0) return messages;
4755
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
5112
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
5113
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
4756
5114
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
4757
5115
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
4758
5116
  if (aTime !== bTime) return aTime - bTime;
4759
5117
  return a.index - b.index;
4760
- }).map((entry) => entry.message);
5118
+ }).map((entry) => entry.message));
4761
5119
  }
4762
5120
  getPersistedEffectContent(effect) {
4763
5121
  if (effect.type === "message") {
@@ -5703,231 +6061,6 @@ var init_reconcile = __esm({
5703
6061
  }
5704
6062
  });
5705
6063
 
5706
- // ../../oss/packages/daemon-core/src/providers/io-contracts.ts
5707
- function normalizeInputEnvelope(input) {
5708
- const normalized = normalizeInputEnvelopePayload(input);
5709
- const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
5710
- return {
5711
- parts: normalized.parts,
5712
- textFallback,
5713
- ...normalized.metadata ? { metadata: normalized.metadata } : {}
5714
- };
5715
- }
5716
- function normalizeMessageParts(content) {
5717
- if (typeof content === "string") return [{ type: "text", text: content }];
5718
- if (!Array.isArray(content)) {
5719
- if (content && typeof content === "object" && typeof content.text === "string") {
5720
- return [{ type: "text", text: String(content.text) }];
5721
- }
5722
- return [];
5723
- }
5724
- const parts = [];
5725
- for (const raw of content) {
5726
- if (typeof raw === "string") {
5727
- parts.push({ type: "text", text: raw });
5728
- continue;
5729
- }
5730
- if (!raw || typeof raw !== "object") continue;
5731
- const part = normalizeMessagePartObject(raw);
5732
- if (part) parts.push(part);
5733
- }
5734
- return parts;
5735
- }
5736
- function flattenMessageParts(parts) {
5737
- return parts.map((part) => {
5738
- if (part.type === "text") return part.text;
5739
- if (part.type === "resource") return part.resource.text || "";
5740
- return "";
5741
- }).filter((value) => value.length > 0).join("\n");
5742
- }
5743
- function normalizeInputEnvelopePayload(input) {
5744
- if (typeof input === "string") {
5745
- return { parts: [{ type: "text", text: input }], textFallback: input };
5746
- }
5747
- if (!input || typeof input !== "object") {
5748
- return { parts: [], textFallback: "" };
5749
- }
5750
- const record2 = input;
5751
- const nestedInput = record2.input;
5752
- if (nestedInput && typeof nestedInput === "object") {
5753
- const nested = nestedInput;
5754
- return {
5755
- parts: normalizeInputParts(nested.parts ?? nested.prompt),
5756
- textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
5757
- metadata: normalizeInputMetadata(nested.metadata)
5758
- };
5759
- }
5760
- const directText = typeof record2.text === "string" ? record2.text : typeof record2.message === "string" ? record2.message : void 0;
5761
- if (directText !== void 0) {
5762
- return { parts: [{ type: "text", text: directText }], textFallback: directText };
5763
- }
5764
- const directParts = normalizeInputParts(record2.parts ?? record2.prompt);
5765
- return {
5766
- parts: directParts,
5767
- textFallback: typeof record2.textFallback === "string" ? record2.textFallback : void 0,
5768
- metadata: normalizeInputMetadata(record2.metadata)
5769
- };
5770
- }
5771
- function normalizeInputMetadata(value) {
5772
- if (!value || typeof value !== "object") return void 0;
5773
- const record2 = value;
5774
- const metadata = {};
5775
- if (record2.source === "dashboard" || record2.source === "shortcut_api" || record2.source === "provider_script" || record2.source === "session_replay") {
5776
- metadata.source = record2.source;
5777
- }
5778
- if (typeof record2.clientTimestamp === "number" && Number.isFinite(record2.clientTimestamp)) {
5779
- metadata.clientTimestamp = record2.clientTimestamp;
5780
- }
5781
- return Object.keys(metadata).length > 0 ? metadata : void 0;
5782
- }
5783
- function normalizeInputParts(value) {
5784
- if (!Array.isArray(value)) return [];
5785
- const parts = [];
5786
- for (const raw of value) {
5787
- if (typeof raw === "string") {
5788
- parts.push({ type: "text", text: raw });
5789
- continue;
5790
- }
5791
- if (!raw || typeof raw !== "object") continue;
5792
- const part = normalizeInputPartObject(raw);
5793
- if (part) parts.push(part);
5794
- }
5795
- return parts;
5796
- }
5797
- function normalizeInputPartObject(raw) {
5798
- const type = raw.type;
5799
- if (type === "text" && typeof raw.text === "string") {
5800
- return { type, text: raw.text };
5801
- }
5802
- if (type === "image" && typeof raw.mimeType === "string") {
5803
- return {
5804
- type,
5805
- mimeType: raw.mimeType,
5806
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5807
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5808
- ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
5809
- };
5810
- }
5811
- if (type === "audio" && typeof raw.mimeType === "string") {
5812
- return {
5813
- type,
5814
- mimeType: raw.mimeType,
5815
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5816
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5817
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5818
- };
5819
- }
5820
- if (type === "video" && typeof raw.mimeType === "string") {
5821
- return {
5822
- type,
5823
- mimeType: raw.mimeType,
5824
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5825
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5826
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5827
- };
5828
- }
5829
- if (type === "resource" && typeof raw.uri === "string") {
5830
- return {
5831
- type,
5832
- uri: raw.uri,
5833
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5834
- ...typeof raw.name === "string" ? { name: raw.name } : {},
5835
- ...typeof raw.text === "string" ? { text: raw.text } : {},
5836
- ...typeof raw.data === "string" ? { data: raw.data } : {}
5837
- };
5838
- }
5839
- if (type === "resource_link" && typeof raw.uri === "string") {
5840
- return {
5841
- type: "resource",
5842
- uri: raw.uri,
5843
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5844
- ...typeof raw.name === "string" ? { name: raw.name } : {}
5845
- };
5846
- }
5847
- return null;
5848
- }
5849
- function normalizeMessagePartObject(raw) {
5850
- const type = raw.type;
5851
- if (type === "text" && typeof raw.text === "string") {
5852
- return { type, text: raw.text };
5853
- }
5854
- if (type === "image" && typeof raw.mimeType === "string") {
5855
- return {
5856
- type,
5857
- mimeType: raw.mimeType,
5858
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5859
- ...typeof raw.data === "string" ? { data: raw.data } : {}
5860
- };
5861
- }
5862
- if (type === "audio" && typeof raw.mimeType === "string") {
5863
- return {
5864
- type,
5865
- mimeType: raw.mimeType,
5866
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5867
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5868
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5869
- };
5870
- }
5871
- if (type === "video" && typeof raw.mimeType === "string") {
5872
- return {
5873
- type,
5874
- mimeType: raw.mimeType,
5875
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5876
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5877
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5878
- };
5879
- }
5880
- if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
5881
- return {
5882
- type,
5883
- uri: raw.uri,
5884
- name: raw.name,
5885
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5886
- ...typeof raw.size === "number" ? { size: raw.size } : {}
5887
- };
5888
- }
5889
- if (type === "resource" && raw.resource && typeof raw.resource === "object") {
5890
- const resource = raw.resource;
5891
- if (typeof resource.uri !== "string") return null;
5892
- return {
5893
- type,
5894
- resource: {
5895
- uri: resource.uri,
5896
- ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
5897
- ...typeof resource.text === "string" ? { text: resource.text } : {},
5898
- ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
5899
- }
5900
- };
5901
- }
5902
- return null;
5903
- }
5904
- function flattenInputParts(parts) {
5905
- return parts.map((part) => {
5906
- if (part.type === "text") return part.text;
5907
- if (part.type === "audio") return part.transcript || "";
5908
- if (part.type === "resource") return part.text || "";
5909
- return "";
5910
- }).filter((value) => value.length > 0).join("\n");
5911
- }
5912
- var init_io_contracts = __esm({
5913
- "../../oss/packages/daemon-core/src/providers/io-contracts.ts"() {
5914
- "use strict";
5915
- }
5916
- });
5917
-
5918
- // ../../oss/packages/daemon-core/src/providers/contracts.ts
5919
- function flattenContent(content) {
5920
- if (typeof content === "string") return content;
5921
- return flattenMessageParts(normalizeMessageParts(content));
5922
- }
5923
- var init_contracts = __esm({
5924
- "../../oss/packages/daemon-core/src/providers/contracts.ts"() {
5925
- "use strict";
5926
- init_io_contracts();
5927
- init_io_contracts();
5928
- }
5929
- });
5930
-
5931
6064
  // ../../oss/packages/daemon-core/src/logging/debug-config.ts
5932
6065
  function normalizeCategories(categories) {
5933
6066
  if (!Array.isArray(categories)) return [];
@@ -6195,7 +6328,7 @@ function normalizeReadChatCursor(args) {
6195
6328
  }
6196
6329
  function normalizeReadChatMessages(payload) {
6197
6330
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
6198
- return messages;
6331
+ return normalizeChatMessages(messages);
6199
6332
  }
6200
6333
  function deriveHistoryDedupKey(message) {
6201
6334
  const unitKey = typeof message._unitKey === "string" ? message._unitKey.trim() : "";
@@ -7173,6 +7306,7 @@ var init_chat_commands = __esm({
7173
7306
  init_chat_history();
7174
7307
  init_logger();
7175
7308
  init_debug_trace();
7309
+ init_chat_message_normalization();
7176
7310
  RECENT_SEND_WINDOW_MS = 1200;
7177
7311
  recentSendByTarget = /* @__PURE__ */ new Map();
7178
7312
  }
@@ -10576,6 +10710,7 @@ var init_provider_cli_adapter = __esm({
10576
10710
  init_terminal_screen();
10577
10711
  init_pty_transport();
10578
10712
  init_provider_cli_shared();
10713
+ init_chat_message_normalization();
10579
10714
  init_provider_cli_parse();
10580
10715
  init_provider_cli_config();
10581
10716
  init_provider_cli_runtime();
@@ -10686,6 +10821,7 @@ var init_provider_cli_adapter = __esm({
10686
10821
  static MAX_TRACE_ENTRIES = 250;
10687
10822
  providerResolutionMeta;
10688
10823
  static IDLE_FINISH_CONFIRM_MS = 2e3;
10824
+ static HERMES_IDLE_FINISH_CONFIRM_MS = 5e3;
10689
10825
  static STATUS_ACTIVITY_HOLD_MS = 2e3;
10690
10826
  static FINISH_RETRY_DELAY_MS = 300;
10691
10827
  static MAX_FINISH_RETRIES = 2;
@@ -10693,6 +10829,12 @@ var init_provider_cli_adapter = __esm({
10693
10829
  this.messages = [...this.committedMessages];
10694
10830
  this.structuredMessages = [...this.committedMessages];
10695
10831
  }
10832
+ getIdleFinishConfirmMs() {
10833
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
10834
+ }
10835
+ getStatusActivityHoldMs() {
10836
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
10837
+ }
10696
10838
  setStatus(status, trigger) {
10697
10839
  const prev = this.currentStatus;
10698
10840
  if (prev === status) return;
@@ -10715,6 +10857,7 @@ var init_provider_cli_adapter = __esm({
10715
10857
  }
10716
10858
  armIdleFinishCandidate(assistantLength) {
10717
10859
  const now = Date.now();
10860
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
10718
10861
  this.idleFinishCandidate = {
10719
10862
  armedAt: now,
10720
10863
  lastOutputAt: this.lastOutputAt,
@@ -10723,7 +10866,7 @@ var init_provider_cli_adapter = __esm({
10723
10866
  assistantLength
10724
10867
  };
10725
10868
  this.recordTrace("idle_candidate_armed", {
10726
- confirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
10869
+ confirmMs: idleFinishConfirmMs,
10727
10870
  candidate: this.idleFinishCandidate,
10728
10871
  ...buildCliTraceParseSnapshot({
10729
10872
  accumulatedBuffer: this.accumulatedBuffer,
@@ -10738,7 +10881,7 @@ var init_provider_cli_adapter = __esm({
10738
10881
  this.settleTimer = null;
10739
10882
  this.settledBuffer = this.recentOutputBuffer;
10740
10883
  this.evaluateSettled();
10741
- }, _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS);
10884
+ }, idleFinishConfirmMs);
10742
10885
  }
10743
10886
  recordTrace(type, payload = {}) {
10744
10887
  const entry = {
@@ -11117,7 +11260,8 @@ var init_provider_cli_adapter = __esm({
11117
11260
  hasRecentInteractiveActivity(now) {
11118
11261
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
11119
11262
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : Number.MAX_SAFE_INTEGER;
11120
- return quietForMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS || screenStableMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
11263
+ const holdMs = this.getStatusActivityHoldMs();
11264
+ return quietForMs < holdMs || screenStableMs < holdMs;
11121
11265
  }
11122
11266
  getStartupConfirmationModal(screenText) {
11123
11267
  const text = sanitizeTerminalText(String(screenText || ""));
@@ -11269,6 +11413,7 @@ var init_provider_cli_adapter = __esm({
11269
11413
  clearPendingScriptStatus();
11270
11414
  }
11271
11415
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
11416
+ const statusActivityHoldMs = this.getStatusActivityHoldMs();
11272
11417
  const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
11273
11418
  if (shouldHoldGenerating) {
11274
11419
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
@@ -11284,7 +11429,7 @@ var init_provider_cli_adapter = __esm({
11284
11429
  recentInteractiveActivity,
11285
11430
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
11286
11431
  lastScreenChangeAt: this.lastScreenChangeAt,
11287
- holdMs: _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS,
11432
+ holdMs: statusActivityHoldMs,
11288
11433
  ...buildCliTraceParseSnapshot({
11289
11434
  accumulatedBuffer: this.accumulatedBuffer,
11290
11435
  accumulatedRawBuffer: this.accumulatedRawBuffer,
@@ -11373,11 +11518,12 @@ var init_provider_cli_adapter = __esm({
11373
11518
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
11374
11519
  const hasAssistantTurn = !!lastParsedAssistant;
11375
11520
  const assistantLength = lastParsedAssistant?.content?.length || 0;
11376
- const idleQuietThresholdMs = Math.max(2e3, this.timeouts.outputSettle);
11377
- const idleStableThresholdMs = 2e3;
11521
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
11522
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
11523
+ const idleStableThresholdMs = idleFinishConfirmMs;
11378
11524
  const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
11379
11525
  const candidate = this.idleFinishCandidate;
11380
- 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;
11526
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
11381
11527
  const canFinishImmediately = idleReady && candidateQuiet;
11382
11528
  this.recordTrace("idle_decision", {
11383
11529
  visibleIdlePrompt,
@@ -11389,7 +11535,7 @@ var init_provider_cli_adapter = __esm({
11389
11535
  idleQuietThresholdMs,
11390
11536
  idleStableThresholdMs,
11391
11537
  idleReady,
11392
- idleFinishConfirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
11538
+ idleFinishConfirmMs,
11393
11539
  idleFinishCandidate: candidate,
11394
11540
  candidateQuiet,
11395
11541
  canFinishImmediately,
@@ -11622,11 +11768,10 @@ var init_provider_cli_adapter = __esm({
11622
11768
  );
11623
11769
  const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
11624
11770
  if (parsed && Array.isArray(parsed.messages)) {
11625
- const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
11771
+ const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => buildChatMessage({
11626
11772
  ...message,
11627
11773
  id: message.id || `msg_${index}`,
11628
11774
  index: typeof message.index === "number" ? message.index : index,
11629
- kind: message.kind || "standard",
11630
11775
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
11631
11776
  })) : hydrateCliParsedMessages(parsed.messages, {
11632
11777
  committedMessages: this.committedMessages,
@@ -11647,13 +11792,11 @@ var init_provider_cli_adapter = __esm({
11647
11792
  id: "cli_session",
11648
11793
  status: this.currentStatus,
11649
11794
  title: this.cliName,
11650
- messages: messages.slice(-50).map((message, index) => ({
11651
- id: `msg_${index}`,
11652
- role: message.role,
11653
- content: message.content,
11654
- timestamp: message.timestamp,
11655
- index,
11656
- kind: "standard"
11795
+ messages: messages.slice(-50).map((message, index) => buildChatMessage({
11796
+ ...message,
11797
+ id: message.id || `msg_${index}`,
11798
+ index: typeof message.index === "number" ? message.index : index,
11799
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
11657
11800
  })),
11658
11801
  activeModal: this.activeModal
11659
11802
  };
@@ -12336,6 +12479,7 @@ var init_cli_provider_instance = __esm({
12336
12479
  init_approval_utils();
12337
12480
  init_cli_script_results();
12338
12481
  init_provider_patch_state();
12482
+ init_chat_message_normalization();
12339
12483
  CachedDatabaseSync = null;
12340
12484
  CliProviderInstance = class {
12341
12485
  constructor(provider, workingDir, cliArgs = [], instanceId, transportFactory, options) {
@@ -12786,8 +12930,8 @@ var init_cli_provider_instance = __esm({
12786
12930
  if (this.appliedEffectKeys.has(effectKey)) continue;
12787
12931
  this.appliedEffectKeys.add(effectKey);
12788
12932
  if (effect.persist !== false) {
12789
- const persisted = this.getPersistedEffectContent(effect);
12790
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
12933
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
12934
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
12791
12935
  }
12792
12936
  if (effect.type === "message" && effect.message) {
12793
12937
  const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
@@ -12911,44 +13055,53 @@ ${effect.notification.body || ""}`.trim();
12911
13055
  );
12912
13056
  }
12913
13057
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
12914
- const normalizedContent = String(content || "").trim();
12915
- if (!normalizedContent) return;
13058
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
13059
+ content,
13060
+ receivedAt,
13061
+ timestamp: receivedAt
13062
+ }), dedupKey);
13063
+ }
13064
+ appendRuntimeMessage(message, dedupKey) {
13065
+ const normalizedMessage = buildChatMessage({
13066
+ ...message,
13067
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
13068
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
13069
+ });
13070
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
13071
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
12916
13072
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
12917
13073
  this.runtimeMessages.push({
12918
13074
  key: dedupKey,
12919
- message: {
12920
- role: "system",
12921
- senderName: "System",
12922
- content: normalizedContent,
12923
- receivedAt,
12924
- timestamp: receivedAt
12925
- }
13075
+ message: normalizedMessage
12926
13076
  });
12927
13077
  if (this.runtimeMessages.length > 50) {
12928
13078
  this.runtimeMessages = this.runtimeMessages.slice(-50);
12929
13079
  }
12930
- this.historyWriter.appendNewMessages(
12931
- this.type,
12932
- [{
12933
- role: "system",
12934
- senderName: "System",
12935
- content: normalizedContent,
12936
- receivedAt,
12937
- historyDedupKey: dedupKey
12938
- }],
12939
- this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
12940
- this.instanceId,
12941
- this.providerSessionId
12942
- );
13080
+ if (normalizedContent) {
13081
+ this.historyWriter.appendNewMessages(
13082
+ this.type,
13083
+ [{
13084
+ role: normalizedMessage.role,
13085
+ senderName: normalizedMessage.senderName,
13086
+ kind: normalizedMessage.kind,
13087
+ content: normalizedContent,
13088
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
13089
+ historyDedupKey: dedupKey
13090
+ }],
13091
+ this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
13092
+ this.instanceId,
13093
+ this.providerSessionId
13094
+ );
13095
+ }
12943
13096
  }
12944
13097
  mergeConversationMessages(parsedMessages) {
12945
- if (this.runtimeMessages.length === 0) return parsedMessages;
12946
- return [...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
13098
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(parsedMessages);
13099
+ return normalizeChatMessages([...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
12947
13100
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
12948
13101
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
12949
13102
  if (aTime !== bTime) return aTime - bTime;
12950
13103
  return a.index - b.index;
12951
- }).map((entry) => entry.message);
13104
+ }).map((entry) => entry.message));
12952
13105
  }
12953
13106
  formatApprovalRequestMessage(modalMessage, buttons) {
12954
13107
  const lines = ["Approval requested"];
@@ -29424,6 +29577,7 @@ var init_acp_provider_instance = __esm({
29424
29577
  init_contracts();
29425
29578
  init_status_monitor();
29426
29579
  init_summary_metadata();
29580
+ init_chat_message_normalization();
29427
29581
  init_logger();
29428
29582
  AcpProviderInstance = class {
29429
29583
  constructor(provider, workingDir, cliArgs = []) {
@@ -29494,24 +29648,21 @@ var init_acp_provider_instance = __esm({
29494
29648
  }
29495
29649
  getState() {
29496
29650
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
29497
- const recentMessages = this.messages.slice(-50).map((m) => {
29651
+ const recentMessages = normalizeChatMessages(this.messages.slice(-50).map((m) => {
29498
29652
  const content = this.truncateContent(m.content);
29499
- return {
29500
- role: m.role,
29501
- content,
29502
- timestamp: m.timestamp,
29503
- toolCalls: m.toolCalls
29504
- };
29505
- });
29653
+ return buildChatMessage({
29654
+ ...m,
29655
+ content
29656
+ });
29657
+ }));
29506
29658
  if (this.currentStatus === "generating" && (this.partialContent || this.partialBlocks.length > 0)) {
29507
29659
  const blocks = this.buildPartialBlocks();
29508
29660
  if (blocks.length > 0) {
29509
- recentMessages.push({
29510
- role: "assistant",
29661
+ recentMessages.push(buildAssistantChatMessage({
29511
29662
  content: blocks,
29512
29663
  timestamp: Date.now(),
29513
29664
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
29514
- });
29665
+ }));
29515
29666
  }
29516
29667
  }
29517
29668
  return {
@@ -29524,7 +29675,7 @@ var init_acp_provider_instance = __esm({
29524
29675
  id: this.sessionId || `${this.type}_${this.workingDir}`,
29525
29676
  title: `${this.provider.name} \xB7 ${dirName}`,
29526
29677
  status: this.currentStatus,
29527
- messages: recentMessages,
29678
+ messages: normalizeChatMessages(recentMessages),
29528
29679
  activeModal: this.currentStatus === "waiting_approval" ? {
29529
29680
  message: this.activeToolCalls.find((t) => t.status === "running")?.name || "Permission requested",
29530
29681
  buttons: ["Approve", "Reject"]
@@ -30056,11 +30207,10 @@ var init_acp_provider_instance = __esm({
30056
30207
  if (b.type === "resource") return { type: "resource", resource: b.resource };
30057
30208
  return { type: "text", text: flattenContent([b]) };
30058
30209
  }) : [{ type: "text", text }];
30059
- this.messages.push({
30060
- role: "user",
30210
+ this.messages.push(buildUserChatMessage({
30061
30211
  content: contentBlocks && contentBlocks.length > 0 ? contentBlocks : text,
30062
30212
  timestamp: Date.now()
30063
- });
30213
+ }));
30064
30214
  this.currentStatus = "generating";
30065
30215
  this.partialContent = "";
30066
30216
  this.partialBlocks = [];
@@ -30230,11 +30380,11 @@ var init_acp_provider_instance = __esm({
30230
30380
  content = m.content.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
30231
30381
  }
30232
30382
  if (content.trim()) {
30233
- this.messages.push({
30383
+ this.messages.push(buildChatMessage({
30234
30384
  role: m.role || "assistant",
30235
30385
  content: content.trim(),
30236
30386
  timestamp: Date.now()
30237
- });
30387
+ }));
30238
30388
  this.partialContent = "";
30239
30389
  }
30240
30390
  }
@@ -30310,12 +30460,11 @@ var init_acp_provider_instance = __esm({
30310
30460
  return b;
30311
30461
  }).filter((b) => b.type !== "text" || b.type === "text" && b.text.trim());
30312
30462
  if (finalBlocks.length > 0) {
30313
- this.messages.push({
30314
- role: "assistant",
30463
+ this.messages.push(buildAssistantChatMessage({
30315
30464
  content: finalBlocks.length === 1 && finalBlocks[0].type === "text" ? finalBlocks[0].text : finalBlocks,
30316
30465
  timestamp: Date.now(),
30317
30466
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
30318
- });
30467
+ }));
30319
30468
  }
30320
30469
  this.partialContent = "";
30321
30470
  this.partialBlocks = [];
@@ -30375,11 +30524,10 @@ var init_acp_provider_instance = __esm({
30375
30524
  appendSystemMessage(content, timestamp = Date.now()) {
30376
30525
  const normalizedContent = String(content || "").trim();
30377
30526
  if (!normalizedContent) return;
30378
- this.messages.push({
30379
- role: "system",
30527
+ this.messages.push(buildRuntimeSystemChatMessage({
30380
30528
  content: normalizedContent,
30381
30529
  timestamp
30382
- });
30530
+ }));
30383
30531
  if (this.messages.length > 200) {
30384
30532
  this.messages = this.messages.slice(-100);
30385
30533
  }
@@ -36932,6 +37080,7 @@ var init_poller = __esm({
36932
37080
  init_reconcile();
36933
37081
  init_logger();
36934
37082
  init_approval_utils();
37083
+ init_chat_message_normalization();
36935
37084
  AgentStreamPoller = class {
36936
37085
  deps;
36937
37086
  timer = null;
@@ -37087,12 +37236,9 @@ var init_poller = __esm({
37087
37236
  type: "message",
37088
37237
  id: effectId,
37089
37238
  persist: true,
37090
- message: {
37091
- role: "system",
37092
- senderName: "System",
37093
- kind: "system",
37239
+ message: buildRuntimeSystemChatMessage({
37094
37240
  content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
37095
- }
37241
+ })
37096
37242
  }
37097
37243
  ]
37098
37244
  };
@@ -43731,6 +43877,7 @@ var src_exports = {};
43731
43877
  __export(src_exports, {
43732
43878
  AcpProviderInstance: () => AcpProviderInstance,
43733
43879
  AgentStreamPoller: () => AgentStreamPoller,
43880
+ BUILTIN_CHAT_MESSAGE_KINDS: () => BUILTIN_CHAT_MESSAGE_KINDS,
43734
43881
  CdpDomHandlers: () => CdpDomHandlers,
43735
43882
  CliProviderInstance: () => CliProviderInstance,
43736
43883
  DAEMON_WS_PATH: () => DAEMON_WS_PATH,
@@ -43755,9 +43902,14 @@ __export(src_exports, {
43755
43902
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
43756
43903
  VersionArchive: () => VersionArchive,
43757
43904
  appendRecentActivity: () => appendRecentActivity,
43905
+ buildAssistantChatMessage: () => buildAssistantChatMessage,
43906
+ buildChatMessage: () => buildChatMessage,
43758
43907
  buildMachineInfo: () => buildMachineInfo,
43908
+ buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
43759
43909
  buildSessionEntries: () => buildSessionEntries,
43760
43910
  buildStatusSnapshot: () => buildStatusSnapshot,
43911
+ buildSystemChatMessage: () => buildSystemChatMessage,
43912
+ buildUserChatMessage: () => buildUserChatMessage,
43761
43913
  clearDebugTrace: () => clearDebugTrace,
43762
43914
  configureDebugTraceStore: () => configureDebugTraceStore,
43763
43915
  connectCdpManager: () => connectCdpManager,
@@ -43789,6 +43941,7 @@ __export(src_exports, {
43789
43941
  initDaemonComponents: () => initDaemonComponents,
43790
43942
  installExtensions: () => installExtensions,
43791
43943
  installGlobalInterceptor: () => installGlobalInterceptor,
43944
+ isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
43792
43945
  isCdpConnected: () => isCdpConnected,
43793
43946
  isExtensionInstalled: () => isExtensionInstalled,
43794
43947
  isIdeRunning: () => isIdeRunning,
@@ -43807,6 +43960,9 @@ __export(src_exports, {
43807
43960
  markSetupComplete: () => markSetupComplete,
43808
43961
  maybeRunDaemonUpgradeHelperFromEnv: () => maybeRunDaemonUpgradeHelperFromEnv,
43809
43962
  normalizeActiveChatData: () => normalizeActiveChatData,
43963
+ normalizeChatMessage: () => normalizeChatMessage,
43964
+ normalizeChatMessageKind: () => normalizeChatMessageKind,
43965
+ normalizeChatMessages: () => normalizeChatMessages,
43810
43966
  normalizeInputEnvelope: () => normalizeInputEnvelope,
43811
43967
  normalizeManagedStatus: () => normalizeManagedStatus,
43812
43968
  normalizeMessageParts: () => normalizeMessageParts,
@@ -43876,6 +44032,7 @@ var init_src = __esm({
43876
44032
  init_acp_provider_instance();
43877
44033
  init_provider_source_config();
43878
44034
  init_io_contracts();
44035
+ init_chat_message_normalization();
43879
44036
  init_version_archive();
43880
44037
  init_dev_server();
43881
44038
  init_provider_cli_adapter();
@@ -83946,7 +84103,7 @@ var init_adhdev_daemon = __esm({
83946
84103
  import_ws3 = require("ws");
83947
84104
  init_source();
83948
84105
  init_version();
83949
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.59" });
84106
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.61" });
83950
84107
  ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
83951
84108
  "generating",
83952
84109
  "waiting_approval",