@adhdev/daemon-core 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.mjs CHANGED
@@ -453,6 +453,64 @@ var init_logger = __esm({
453
453
  }
454
454
  });
455
455
 
456
+ // src/providers/chat-message-normalization.ts
457
+ function isBuiltinChatMessageKind(kind) {
458
+ return typeof kind === "string" && KNOWN_CHAT_MESSAGE_KINDS.has(kind.trim().toLowerCase());
459
+ }
460
+ function normalizeChatMessageKind(kind, role) {
461
+ const normalizedKind = typeof kind === "string" ? kind.trim().toLowerCase() : "";
462
+ if (normalizedKind && KNOWN_CHAT_MESSAGE_KINDS.has(normalizedKind)) return normalizedKind;
463
+ const normalizedRole = typeof role === "string" ? role.trim().toLowerCase() : "";
464
+ return normalizedRole === "system" ? "system" : "standard";
465
+ }
466
+ function buildChatMessage(message) {
467
+ return {
468
+ ...message,
469
+ kind: normalizeChatMessageKind(message?.kind, message?.role)
470
+ };
471
+ }
472
+ function buildSystemChatMessage(message) {
473
+ return buildChatMessage({
474
+ ...message,
475
+ role: "system",
476
+ kind: message?.kind || "system"
477
+ });
478
+ }
479
+ function buildRuntimeSystemChatMessage(message) {
480
+ return buildSystemChatMessage({
481
+ ...message,
482
+ senderName: typeof message?.senderName === "string" && message.senderName.trim() ? message.senderName : "System"
483
+ });
484
+ }
485
+ function buildAssistantChatMessage(message) {
486
+ return buildChatMessage({
487
+ ...message,
488
+ role: "assistant",
489
+ kind: message?.kind || "standard"
490
+ });
491
+ }
492
+ function buildUserChatMessage(message) {
493
+ return buildChatMessage({
494
+ ...message,
495
+ role: "user",
496
+ kind: message?.kind || "standard"
497
+ });
498
+ }
499
+ function normalizeChatMessage(message) {
500
+ return buildChatMessage(message);
501
+ }
502
+ function normalizeChatMessages(messages) {
503
+ return (Array.isArray(messages) ? messages : []).map((message) => normalizeChatMessage(message));
504
+ }
505
+ var BUILTIN_CHAT_MESSAGE_KINDS, KNOWN_CHAT_MESSAGE_KINDS;
506
+ var init_chat_message_normalization = __esm({
507
+ "src/providers/chat-message-normalization.ts"() {
508
+ "use strict";
509
+ BUILTIN_CHAT_MESSAGE_KINDS = ["standard", "thought", "tool", "terminal", "system"];
510
+ KNOWN_CHAT_MESSAGE_KINDS = new Set(BUILTIN_CHAT_MESSAGE_KINDS);
511
+ }
512
+ });
513
+
456
514
  // src/cli-adapters/terminal-backends/ghostty-vt-backend.ts
457
515
  function isModuleNotFoundError(error, ref) {
458
516
  if (!(error instanceof Error)) return false;
@@ -1307,6 +1365,7 @@ var init_provider_cli_adapter = __esm({
1307
1365
  init_terminal_screen();
1308
1366
  init_pty_transport();
1309
1367
  init_provider_cli_shared();
1368
+ init_chat_message_normalization();
1310
1369
  init_provider_cli_parse();
1311
1370
  init_provider_cli_config();
1312
1371
  init_provider_cli_runtime();
@@ -1417,6 +1476,7 @@ var init_provider_cli_adapter = __esm({
1417
1476
  static MAX_TRACE_ENTRIES = 250;
1418
1477
  providerResolutionMeta;
1419
1478
  static IDLE_FINISH_CONFIRM_MS = 2e3;
1479
+ static HERMES_IDLE_FINISH_CONFIRM_MS = 5e3;
1420
1480
  static STATUS_ACTIVITY_HOLD_MS = 2e3;
1421
1481
  static FINISH_RETRY_DELAY_MS = 300;
1422
1482
  static MAX_FINISH_RETRIES = 2;
@@ -1424,6 +1484,12 @@ var init_provider_cli_adapter = __esm({
1424
1484
  this.messages = [...this.committedMessages];
1425
1485
  this.structuredMessages = [...this.committedMessages];
1426
1486
  }
1487
+ getIdleFinishConfirmMs() {
1488
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
1489
+ }
1490
+ getStatusActivityHoldMs() {
1491
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
1492
+ }
1427
1493
  setStatus(status, trigger) {
1428
1494
  const prev = this.currentStatus;
1429
1495
  if (prev === status) return;
@@ -1446,6 +1512,7 @@ var init_provider_cli_adapter = __esm({
1446
1512
  }
1447
1513
  armIdleFinishCandidate(assistantLength) {
1448
1514
  const now = Date.now();
1515
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
1449
1516
  this.idleFinishCandidate = {
1450
1517
  armedAt: now,
1451
1518
  lastOutputAt: this.lastOutputAt,
@@ -1454,7 +1521,7 @@ var init_provider_cli_adapter = __esm({
1454
1521
  assistantLength
1455
1522
  };
1456
1523
  this.recordTrace("idle_candidate_armed", {
1457
- confirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
1524
+ confirmMs: idleFinishConfirmMs,
1458
1525
  candidate: this.idleFinishCandidate,
1459
1526
  ...buildCliTraceParseSnapshot({
1460
1527
  accumulatedBuffer: this.accumulatedBuffer,
@@ -1469,7 +1536,7 @@ var init_provider_cli_adapter = __esm({
1469
1536
  this.settleTimer = null;
1470
1537
  this.settledBuffer = this.recentOutputBuffer;
1471
1538
  this.evaluateSettled();
1472
- }, _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS);
1539
+ }, idleFinishConfirmMs);
1473
1540
  }
1474
1541
  recordTrace(type, payload = {}) {
1475
1542
  const entry = {
@@ -1848,7 +1915,8 @@ var init_provider_cli_adapter = __esm({
1848
1915
  hasRecentInteractiveActivity(now) {
1849
1916
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
1850
1917
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : Number.MAX_SAFE_INTEGER;
1851
- return quietForMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS || screenStableMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
1918
+ const holdMs = this.getStatusActivityHoldMs();
1919
+ return quietForMs < holdMs || screenStableMs < holdMs;
1852
1920
  }
1853
1921
  getStartupConfirmationModal(screenText) {
1854
1922
  const text = sanitizeTerminalText(String(screenText || ""));
@@ -2000,6 +2068,7 @@ var init_provider_cli_adapter = __esm({
2000
2068
  clearPendingScriptStatus();
2001
2069
  }
2002
2070
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
2071
+ const statusActivityHoldMs = this.getStatusActivityHoldMs();
2003
2072
  const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
2004
2073
  if (shouldHoldGenerating) {
2005
2074
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
@@ -2015,7 +2084,7 @@ var init_provider_cli_adapter = __esm({
2015
2084
  recentInteractiveActivity,
2016
2085
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
2017
2086
  lastScreenChangeAt: this.lastScreenChangeAt,
2018
- holdMs: _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS,
2087
+ holdMs: statusActivityHoldMs,
2019
2088
  ...buildCliTraceParseSnapshot({
2020
2089
  accumulatedBuffer: this.accumulatedBuffer,
2021
2090
  accumulatedRawBuffer: this.accumulatedRawBuffer,
@@ -2104,11 +2173,12 @@ var init_provider_cli_adapter = __esm({
2104
2173
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
2105
2174
  const hasAssistantTurn = !!lastParsedAssistant;
2106
2175
  const assistantLength = lastParsedAssistant?.content?.length || 0;
2107
- const idleQuietThresholdMs = Math.max(2e3, this.timeouts.outputSettle);
2108
- const idleStableThresholdMs = 2e3;
2176
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
2177
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
2178
+ const idleStableThresholdMs = idleFinishConfirmMs;
2109
2179
  const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
2110
2180
  const candidate = this.idleFinishCandidate;
2111
- 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;
2181
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
2112
2182
  const canFinishImmediately = idleReady && candidateQuiet;
2113
2183
  this.recordTrace("idle_decision", {
2114
2184
  visibleIdlePrompt,
@@ -2120,7 +2190,7 @@ var init_provider_cli_adapter = __esm({
2120
2190
  idleQuietThresholdMs,
2121
2191
  idleStableThresholdMs,
2122
2192
  idleReady,
2123
- idleFinishConfirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
2193
+ idleFinishConfirmMs,
2124
2194
  idleFinishCandidate: candidate,
2125
2195
  candidateQuiet,
2126
2196
  canFinishImmediately,
@@ -2353,11 +2423,10 @@ var init_provider_cli_adapter = __esm({
2353
2423
  );
2354
2424
  const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
2355
2425
  if (parsed && Array.isArray(parsed.messages)) {
2356
- const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
2426
+ const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => buildChatMessage({
2357
2427
  ...message,
2358
2428
  id: message.id || `msg_${index}`,
2359
2429
  index: typeof message.index === "number" ? message.index : index,
2360
- kind: message.kind || "standard",
2361
2430
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2362
2431
  })) : hydrateCliParsedMessages(parsed.messages, {
2363
2432
  committedMessages: this.committedMessages,
@@ -2378,13 +2447,11 @@ var init_provider_cli_adapter = __esm({
2378
2447
  id: "cli_session",
2379
2448
  status: this.currentStatus,
2380
2449
  title: this.cliName,
2381
- messages: messages.slice(-50).map((message, index) => ({
2382
- id: `msg_${index}`,
2383
- role: message.role,
2384
- content: message.content,
2385
- timestamp: message.timestamp,
2386
- index,
2387
- kind: "standard"
2450
+ messages: messages.slice(-50).map((message, index) => buildChatMessage({
2451
+ ...message,
2452
+ id: message.id || `msg_${index}`,
2453
+ index: typeof message.index === "number" ? message.index : index,
2454
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2388
2455
  })),
2389
2456
  activeModal: this.activeModal
2390
2457
  };
@@ -4982,6 +5049,219 @@ var CdpDomHandlers = class {
4982
5049
  // src/providers/ide-provider-instance.ts
4983
5050
  import * as crypto2 from "crypto";
4984
5051
 
5052
+ // src/providers/io-contracts.ts
5053
+ function normalizeInputEnvelope(input) {
5054
+ const normalized = normalizeInputEnvelopePayload(input);
5055
+ const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
5056
+ return {
5057
+ parts: normalized.parts,
5058
+ textFallback,
5059
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
5060
+ };
5061
+ }
5062
+ function normalizeMessageParts(content) {
5063
+ if (typeof content === "string") return [{ type: "text", text: content }];
5064
+ if (!Array.isArray(content)) {
5065
+ if (content && typeof content === "object" && typeof content.text === "string") {
5066
+ return [{ type: "text", text: String(content.text) }];
5067
+ }
5068
+ return [];
5069
+ }
5070
+ const parts = [];
5071
+ for (const raw of content) {
5072
+ if (typeof raw === "string") {
5073
+ parts.push({ type: "text", text: raw });
5074
+ continue;
5075
+ }
5076
+ if (!raw || typeof raw !== "object") continue;
5077
+ const part = normalizeMessagePartObject(raw);
5078
+ if (part) parts.push(part);
5079
+ }
5080
+ return parts;
5081
+ }
5082
+ function flattenMessageParts(parts) {
5083
+ return parts.map((part) => {
5084
+ if (part.type === "text") return part.text;
5085
+ if (part.type === "resource") return part.resource.text || "";
5086
+ return "";
5087
+ }).filter((value) => value.length > 0).join("\n");
5088
+ }
5089
+ function normalizeInputEnvelopePayload(input) {
5090
+ if (typeof input === "string") {
5091
+ return { parts: [{ type: "text", text: input }], textFallback: input };
5092
+ }
5093
+ if (!input || typeof input !== "object") {
5094
+ return { parts: [], textFallback: "" };
5095
+ }
5096
+ const record = input;
5097
+ const nestedInput = record.input;
5098
+ if (nestedInput && typeof nestedInput === "object") {
5099
+ const nested = nestedInput;
5100
+ return {
5101
+ parts: normalizeInputParts(nested.parts ?? nested.prompt),
5102
+ textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
5103
+ metadata: normalizeInputMetadata(nested.metadata)
5104
+ };
5105
+ }
5106
+ const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
5107
+ if (directText !== void 0) {
5108
+ return { parts: [{ type: "text", text: directText }], textFallback: directText };
5109
+ }
5110
+ const directParts = normalizeInputParts(record.parts ?? record.prompt);
5111
+ return {
5112
+ parts: directParts,
5113
+ textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
5114
+ metadata: normalizeInputMetadata(record.metadata)
5115
+ };
5116
+ }
5117
+ function normalizeInputMetadata(value) {
5118
+ if (!value || typeof value !== "object") return void 0;
5119
+ const record = value;
5120
+ const metadata = {};
5121
+ if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
5122
+ metadata.source = record.source;
5123
+ }
5124
+ if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
5125
+ metadata.clientTimestamp = record.clientTimestamp;
5126
+ }
5127
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
5128
+ }
5129
+ function normalizeInputParts(value) {
5130
+ if (!Array.isArray(value)) return [];
5131
+ const parts = [];
5132
+ for (const raw of value) {
5133
+ if (typeof raw === "string") {
5134
+ parts.push({ type: "text", text: raw });
5135
+ continue;
5136
+ }
5137
+ if (!raw || typeof raw !== "object") continue;
5138
+ const part = normalizeInputPartObject(raw);
5139
+ if (part) parts.push(part);
5140
+ }
5141
+ return parts;
5142
+ }
5143
+ function normalizeInputPartObject(raw) {
5144
+ const type = raw.type;
5145
+ if (type === "text" && typeof raw.text === "string") {
5146
+ return { type, text: raw.text };
5147
+ }
5148
+ if (type === "image" && typeof raw.mimeType === "string") {
5149
+ return {
5150
+ type,
5151
+ mimeType: raw.mimeType,
5152
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5153
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5154
+ ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
5155
+ };
5156
+ }
5157
+ if (type === "audio" && typeof raw.mimeType === "string") {
5158
+ return {
5159
+ type,
5160
+ mimeType: raw.mimeType,
5161
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5162
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5163
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5164
+ };
5165
+ }
5166
+ if (type === "video" && typeof raw.mimeType === "string") {
5167
+ return {
5168
+ type,
5169
+ mimeType: raw.mimeType,
5170
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5171
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5172
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5173
+ };
5174
+ }
5175
+ if (type === "resource" && typeof raw.uri === "string") {
5176
+ return {
5177
+ type,
5178
+ uri: raw.uri,
5179
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5180
+ ...typeof raw.name === "string" ? { name: raw.name } : {},
5181
+ ...typeof raw.text === "string" ? { text: raw.text } : {},
5182
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
5183
+ };
5184
+ }
5185
+ if (type === "resource_link" && typeof raw.uri === "string") {
5186
+ return {
5187
+ type: "resource",
5188
+ uri: raw.uri,
5189
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5190
+ ...typeof raw.name === "string" ? { name: raw.name } : {}
5191
+ };
5192
+ }
5193
+ return null;
5194
+ }
5195
+ function normalizeMessagePartObject(raw) {
5196
+ const type = raw.type;
5197
+ if (type === "text" && typeof raw.text === "string") {
5198
+ return { type, text: raw.text };
5199
+ }
5200
+ if (type === "image" && typeof raw.mimeType === "string") {
5201
+ return {
5202
+ type,
5203
+ mimeType: raw.mimeType,
5204
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5205
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
5206
+ };
5207
+ }
5208
+ if (type === "audio" && typeof raw.mimeType === "string") {
5209
+ return {
5210
+ type,
5211
+ mimeType: raw.mimeType,
5212
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5213
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5214
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5215
+ };
5216
+ }
5217
+ if (type === "video" && typeof raw.mimeType === "string") {
5218
+ return {
5219
+ type,
5220
+ mimeType: raw.mimeType,
5221
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5222
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5223
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5224
+ };
5225
+ }
5226
+ if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
5227
+ return {
5228
+ type,
5229
+ uri: raw.uri,
5230
+ name: raw.name,
5231
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5232
+ ...typeof raw.size === "number" ? { size: raw.size } : {}
5233
+ };
5234
+ }
5235
+ if (type === "resource" && raw.resource && typeof raw.resource === "object") {
5236
+ const resource = raw.resource;
5237
+ if (typeof resource.uri !== "string") return null;
5238
+ return {
5239
+ type,
5240
+ resource: {
5241
+ uri: resource.uri,
5242
+ ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
5243
+ ...typeof resource.text === "string" ? { text: resource.text } : {},
5244
+ ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
5245
+ }
5246
+ };
5247
+ }
5248
+ return null;
5249
+ }
5250
+ function flattenInputParts(parts) {
5251
+ return parts.map((part) => {
5252
+ if (part.type === "text") return part.text;
5253
+ if (part.type === "audio") return part.transcript || "";
5254
+ if (part.type === "resource") return part.text || "";
5255
+ return "";
5256
+ }).filter((value) => value.length > 0).join("\n");
5257
+ }
5258
+
5259
+ // src/providers/contracts.ts
5260
+ function flattenContent(content) {
5261
+ if (typeof content === "string") return content;
5262
+ return flattenMessageParts(normalizeMessageParts(content));
5263
+ }
5264
+
4985
5265
  // src/providers/status-monitor.ts
4986
5266
  var DEFAULT_MONITOR_CONFIG = {
4987
5267
  approvalAlert: true,
@@ -5095,6 +5375,7 @@ var StatusMonitor = class {
5095
5375
  };
5096
5376
 
5097
5377
  // src/providers/control-effects.ts
5378
+ init_chat_message_normalization();
5098
5379
  function extractProviderControlValues(controls, data) {
5099
5380
  if (!data || typeof data !== "object") return void 0;
5100
5381
  const values = {};
@@ -5162,13 +5443,58 @@ function normalizeProviderEffects(data) {
5162
5443
  level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
5163
5444
  channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
5164
5445
  preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
5165
- bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
5446
+ bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0,
5447
+ bubbleKind: typeof raw.notification.bubbleKind === "string" ? raw.notification.bubbleKind : void 0,
5448
+ bubbleRole: raw.notification.bubbleRole === "assistant" || raw.notification.bubbleRole === "user" ? raw.notification.bubbleRole : raw.notification.bubbleRole === "system" ? "system" : void 0,
5449
+ bubbleSenderName: typeof raw.notification.bubbleSenderName === "string" ? raw.notification.bubbleSenderName : void 0
5166
5450
  }
5167
5451
  });
5168
5452
  }
5169
5453
  }
5170
5454
  return effects;
5171
5455
  }
5456
+ function buildPersistedProviderEffectMessage(effect) {
5457
+ if (!effect) return null;
5458
+ if (effect.type === "message" && effect.message) {
5459
+ const role = effect.message.role === "assistant" || effect.message.role === "user" ? effect.message.role : "system";
5460
+ if (role === "system") {
5461
+ return buildRuntimeSystemChatMessage({
5462
+ content: effect.message.content,
5463
+ kind: effect.message.kind,
5464
+ senderName: effect.message.senderName
5465
+ });
5466
+ }
5467
+ return buildChatMessage({
5468
+ role,
5469
+ content: effect.message.content,
5470
+ kind: effect.message.kind,
5471
+ senderName: effect.message.senderName
5472
+ });
5473
+ }
5474
+ if (effect.type === "notification" && effect.notification) {
5475
+ const bubbleContent = effect.notification.bubbleContent ?? formatNotificationBubbleFallback(effect.notification.title, effect.notification.body);
5476
+ const flattened = typeof bubbleContent === "string" ? bubbleContent.trim() : flattenContent(bubbleContent).trim();
5477
+ if (!flattened && (!Array.isArray(bubbleContent) || bubbleContent.length === 0)) return null;
5478
+ const role = effect.notification.bubbleRole === "assistant" || effect.notification.bubbleRole === "user" ? effect.notification.bubbleRole : "system";
5479
+ if (role === "system") {
5480
+ return buildRuntimeSystemChatMessage({
5481
+ content: bubbleContent,
5482
+ kind: effect.notification.bubbleKind,
5483
+ senderName: effect.notification.bubbleSenderName
5484
+ });
5485
+ }
5486
+ return buildChatMessage({
5487
+ role,
5488
+ content: bubbleContent,
5489
+ kind: effect.notification.bubbleKind,
5490
+ senderName: effect.notification.bubbleSenderName
5491
+ });
5492
+ }
5493
+ if (effect.type === "toast" && effect.toast?.message) {
5494
+ return buildRuntimeSystemChatMessage({ content: effect.toast.message });
5495
+ }
5496
+ return null;
5497
+ }
5172
5498
  function normalizeControlListResult(data) {
5173
5499
  if (data && typeof data === "object" && Array.isArray(data.options)) {
5174
5500
  return {
@@ -5235,8 +5561,16 @@ function normalizeControlValue(value) {
5235
5561
  }
5236
5562
  return String(value);
5237
5563
  }
5564
+ function formatNotificationBubbleFallback(title, body) {
5565
+ const cleanTitle = typeof title === "string" ? title.trim() : "";
5566
+ const cleanBody = String(body || "").trim();
5567
+ if (cleanTitle && cleanBody) return `${cleanTitle}
5568
+ ${cleanBody}`;
5569
+ return cleanTitle || cleanBody;
5570
+ }
5238
5571
 
5239
5572
  // src/config/chat-history.ts
5573
+ init_chat_message_normalization();
5240
5574
  import * as fs3 from "fs";
5241
5575
  import * as path7 from "path";
5242
5576
  import * as os5 from "os";
@@ -5445,11 +5779,11 @@ var ChatHistoryWriter = class {
5445
5779
  this.appendNewMessages(
5446
5780
  agentType,
5447
5781
  [{
5448
- role: "system",
5449
- kind: "system",
5450
- content,
5451
- receivedAt: options.receivedAt,
5452
- senderName: options.senderName,
5782
+ ...buildRuntimeSystemChatMessage({
5783
+ content,
5784
+ receivedAt: options.receivedAt,
5785
+ senderName: options.senderName
5786
+ }),
5453
5787
  historyDedupKey: options.dedupKey
5454
5788
  }],
5455
5789
  options.sessionTitle,
@@ -5804,6 +6138,7 @@ function resolveProviderStateSurface(params) {
5804
6138
  }
5805
6139
 
5806
6140
  // src/providers/extension-provider-instance.ts
6141
+ init_chat_message_normalization();
5807
6142
  var ExtensionProviderInstance = class {
5808
6143
  type;
5809
6144
  category = "extension";
@@ -6010,8 +6345,8 @@ var ExtensionProviderInstance = class {
6010
6345
  if (this.appliedEffectKeys.has(effectKey)) continue;
6011
6346
  this.appliedEffectKeys.add(effectKey);
6012
6347
  if (effect.persist !== false) {
6013
- const persisted = this.getPersistedEffectContent(effect);
6014
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
6348
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
6349
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
6015
6350
  }
6016
6351
  if (effect.type === "message" && effect.message) {
6017
6352
  this.pushEvent({
@@ -6046,34 +6381,42 @@ var ExtensionProviderInstance = class {
6046
6381
  }
6047
6382
  }
6048
6383
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
6049
- const normalizedContent = String(content || "").trim();
6050
- if (!normalizedContent) return;
6384
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
6385
+ content,
6386
+ receivedAt,
6387
+ timestamp: receivedAt
6388
+ }), dedupKey);
6389
+ }
6390
+ appendRuntimeMessage(message, dedupKey) {
6391
+ const normalizedMessage = buildChatMessage({
6392
+ ...message,
6393
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
6394
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
6395
+ });
6396
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
6397
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
6051
6398
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
6052
6399
  this.runtimeMessages.push({
6053
6400
  key: dedupKey,
6054
- message: {
6055
- role: "system",
6056
- senderName: "System",
6057
- content: normalizedContent,
6058
- receivedAt,
6059
- timestamp: receivedAt
6060
- }
6401
+ message: normalizedMessage
6061
6402
  });
6062
6403
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
6063
- this.historyWriter.appendNewMessages(
6064
- this.type,
6065
- [{
6066
- role: "system",
6067
- senderName: "System",
6068
- content: normalizedContent,
6069
- kind: "system",
6070
- receivedAt,
6071
- historyDedupKey: dedupKey
6072
- }],
6073
- this.chatTitle || this.agentName || this.provider.name,
6074
- this.instanceId,
6075
- this.chatId || this.instanceId
6076
- );
6404
+ if (normalizedContent) {
6405
+ this.historyWriter.appendNewMessages(
6406
+ this.type,
6407
+ [{
6408
+ role: normalizedMessage.role,
6409
+ senderName: normalizedMessage.senderName,
6410
+ kind: normalizedMessage.kind,
6411
+ content: normalizedContent,
6412
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
6413
+ historyDedupKey: dedupKey
6414
+ }],
6415
+ this.chatTitle || this.agentName || this.provider.name,
6416
+ this.instanceId,
6417
+ this.chatId || this.instanceId
6418
+ );
6419
+ }
6077
6420
  }
6078
6421
  /**
6079
6422
  * Assign stable receivedAt to extension messages.
@@ -6090,16 +6433,16 @@ var ExtensionProviderInstance = class {
6090
6433
  nextHashes.set(hash, msg.receivedAt);
6091
6434
  }
6092
6435
  this.prevMessageHashes = nextHashes;
6093
- return messages;
6436
+ return normalizeChatMessages(messages);
6094
6437
  }
6095
6438
  mergeConversationMessages(messages) {
6096
- if (this.runtimeMessages.length === 0) return messages;
6097
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6439
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
6440
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6098
6441
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
6099
6442
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
6100
6443
  if (aTime !== bTime) return aTime - bTime;
6101
6444
  return a.index - b.index;
6102
- }).map((entry) => entry.message);
6445
+ }).map((entry) => entry.message));
6103
6446
  }
6104
6447
  getPersistedEffectContent(effect) {
6105
6448
  if (effect.type === "message") {
@@ -6213,6 +6556,7 @@ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
6213
6556
  }
6214
6557
 
6215
6558
  // src/providers/ide-provider-instance.ts
6559
+ init_chat_message_normalization();
6216
6560
  var IdeProviderInstance = class {
6217
6561
  type;
6218
6562
  category = "ide";
@@ -6593,8 +6937,8 @@ var IdeProviderInstance = class {
6593
6937
  if (this.appliedEffectKeys.has(effectKey)) continue;
6594
6938
  this.appliedEffectKeys.add(effectKey);
6595
6939
  if (effect.persist !== false) {
6596
- const persisted = this.getPersistedEffectContent(effect);
6597
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
6940
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
6941
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
6598
6942
  }
6599
6943
  if (effect.type === "message" && effect.message) {
6600
6944
  this.pushEvent({
@@ -6629,8 +6973,20 @@ var IdeProviderInstance = class {
6629
6973
  }
6630
6974
  }
6631
6975
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
6632
- const normalizedContent = String(content || "").trim();
6633
- if (!normalizedContent) return;
6976
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
6977
+ content,
6978
+ receivedAt,
6979
+ timestamp: receivedAt
6980
+ }), dedupKey);
6981
+ }
6982
+ appendRuntimeMessage(message, dedupKey) {
6983
+ const normalizedMessage = buildChatMessage({
6984
+ ...message,
6985
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
6986
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
6987
+ });
6988
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
6989
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
6634
6990
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
6635
6991
  if (!this.cachedChat) {
6636
6992
  this.cachedChat = {
@@ -6643,39 +6999,35 @@ var IdeProviderInstance = class {
6643
6999
  };
6644
7000
  }
6645
7001
  this.runtimeMessages.push({
6646
- key: dedupKey,
6647
- message: {
6648
- role: "system",
6649
- senderName: "System",
6650
- content: normalizedContent,
6651
- receivedAt,
6652
- timestamp: receivedAt
6653
- }
7002
+ key: dedupKey,
7003
+ message: normalizedMessage
6654
7004
  });
6655
7005
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
6656
- this.historyWriter.appendNewMessages(
6657
- this.type,
6658
- [{
6659
- role: "system",
6660
- senderName: "System",
6661
- content: normalizedContent,
6662
- kind: "system",
6663
- receivedAt,
6664
- historyDedupKey: dedupKey
6665
- }],
6666
- this.cachedChat?.title || this.provider.name,
6667
- this.instanceId,
6668
- this.cachedChat?.id || this.instanceId
6669
- );
7006
+ if (normalizedContent) {
7007
+ this.historyWriter.appendNewMessages(
7008
+ this.type,
7009
+ [{
7010
+ role: normalizedMessage.role,
7011
+ senderName: normalizedMessage.senderName,
7012
+ kind: normalizedMessage.kind,
7013
+ content: normalizedContent,
7014
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
7015
+ historyDedupKey: dedupKey
7016
+ }],
7017
+ this.cachedChat?.title || this.provider.name,
7018
+ this.instanceId,
7019
+ this.cachedChat?.id || this.instanceId
7020
+ );
7021
+ }
6670
7022
  }
6671
7023
  mergeConversationMessages(messages) {
6672
- if (this.runtimeMessages.length === 0) return messages;
6673
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
7024
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
7025
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6674
7026
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
6675
7027
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
6676
7028
  if (aTime !== bTime) return aTime - bTime;
6677
7029
  return a.index - b.index;
6678
- }).map((entry) => entry.message);
7030
+ }).map((entry) => entry.message));
6679
7031
  }
6680
7032
  getPersistedEffectContent(effect) {
6681
7033
  if (effect.type === "message") {
@@ -7578,219 +7930,6 @@ function reconcileIdeRuntimeSessions(instanceManager, sessionRegistry) {
7578
7930
  // src/commands/handler.ts
7579
7931
  init_logger();
7580
7932
 
7581
- // src/providers/io-contracts.ts
7582
- function normalizeInputEnvelope(input) {
7583
- const normalized = normalizeInputEnvelopePayload(input);
7584
- const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
7585
- return {
7586
- parts: normalized.parts,
7587
- textFallback,
7588
- ...normalized.metadata ? { metadata: normalized.metadata } : {}
7589
- };
7590
- }
7591
- function normalizeMessageParts(content) {
7592
- if (typeof content === "string") return [{ type: "text", text: content }];
7593
- if (!Array.isArray(content)) {
7594
- if (content && typeof content === "object" && typeof content.text === "string") {
7595
- return [{ type: "text", text: String(content.text) }];
7596
- }
7597
- return [];
7598
- }
7599
- const parts = [];
7600
- for (const raw of content) {
7601
- if (typeof raw === "string") {
7602
- parts.push({ type: "text", text: raw });
7603
- continue;
7604
- }
7605
- if (!raw || typeof raw !== "object") continue;
7606
- const part = normalizeMessagePartObject(raw);
7607
- if (part) parts.push(part);
7608
- }
7609
- return parts;
7610
- }
7611
- function flattenMessageParts(parts) {
7612
- return parts.map((part) => {
7613
- if (part.type === "text") return part.text;
7614
- if (part.type === "resource") return part.resource.text || "";
7615
- return "";
7616
- }).filter((value) => value.length > 0).join("\n");
7617
- }
7618
- function normalizeInputEnvelopePayload(input) {
7619
- if (typeof input === "string") {
7620
- return { parts: [{ type: "text", text: input }], textFallback: input };
7621
- }
7622
- if (!input || typeof input !== "object") {
7623
- return { parts: [], textFallback: "" };
7624
- }
7625
- const record = input;
7626
- const nestedInput = record.input;
7627
- if (nestedInput && typeof nestedInput === "object") {
7628
- const nested = nestedInput;
7629
- return {
7630
- parts: normalizeInputParts(nested.parts ?? nested.prompt),
7631
- textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
7632
- metadata: normalizeInputMetadata(nested.metadata)
7633
- };
7634
- }
7635
- const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
7636
- if (directText !== void 0) {
7637
- return { parts: [{ type: "text", text: directText }], textFallback: directText };
7638
- }
7639
- const directParts = normalizeInputParts(record.parts ?? record.prompt);
7640
- return {
7641
- parts: directParts,
7642
- textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
7643
- metadata: normalizeInputMetadata(record.metadata)
7644
- };
7645
- }
7646
- function normalizeInputMetadata(value) {
7647
- if (!value || typeof value !== "object") return void 0;
7648
- const record = value;
7649
- const metadata = {};
7650
- if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
7651
- metadata.source = record.source;
7652
- }
7653
- if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
7654
- metadata.clientTimestamp = record.clientTimestamp;
7655
- }
7656
- return Object.keys(metadata).length > 0 ? metadata : void 0;
7657
- }
7658
- function normalizeInputParts(value) {
7659
- if (!Array.isArray(value)) return [];
7660
- const parts = [];
7661
- for (const raw of value) {
7662
- if (typeof raw === "string") {
7663
- parts.push({ type: "text", text: raw });
7664
- continue;
7665
- }
7666
- if (!raw || typeof raw !== "object") continue;
7667
- const part = normalizeInputPartObject(raw);
7668
- if (part) parts.push(part);
7669
- }
7670
- return parts;
7671
- }
7672
- function normalizeInputPartObject(raw) {
7673
- const type = raw.type;
7674
- if (type === "text" && typeof raw.text === "string") {
7675
- return { type, text: raw.text };
7676
- }
7677
- if (type === "image" && typeof raw.mimeType === "string") {
7678
- return {
7679
- type,
7680
- mimeType: raw.mimeType,
7681
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7682
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7683
- ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
7684
- };
7685
- }
7686
- if (type === "audio" && typeof raw.mimeType === "string") {
7687
- return {
7688
- type,
7689
- mimeType: raw.mimeType,
7690
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7691
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7692
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
7693
- };
7694
- }
7695
- if (type === "video" && typeof raw.mimeType === "string") {
7696
- return {
7697
- type,
7698
- mimeType: raw.mimeType,
7699
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7700
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7701
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
7702
- };
7703
- }
7704
- if (type === "resource" && typeof raw.uri === "string") {
7705
- return {
7706
- type,
7707
- uri: raw.uri,
7708
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7709
- ...typeof raw.name === "string" ? { name: raw.name } : {},
7710
- ...typeof raw.text === "string" ? { text: raw.text } : {},
7711
- ...typeof raw.data === "string" ? { data: raw.data } : {}
7712
- };
7713
- }
7714
- if (type === "resource_link" && typeof raw.uri === "string") {
7715
- return {
7716
- type: "resource",
7717
- uri: raw.uri,
7718
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7719
- ...typeof raw.name === "string" ? { name: raw.name } : {}
7720
- };
7721
- }
7722
- return null;
7723
- }
7724
- function normalizeMessagePartObject(raw) {
7725
- const type = raw.type;
7726
- if (type === "text" && typeof raw.text === "string") {
7727
- return { type, text: raw.text };
7728
- }
7729
- if (type === "image" && typeof raw.mimeType === "string") {
7730
- return {
7731
- type,
7732
- mimeType: raw.mimeType,
7733
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7734
- ...typeof raw.data === "string" ? { data: raw.data } : {}
7735
- };
7736
- }
7737
- if (type === "audio" && typeof raw.mimeType === "string") {
7738
- return {
7739
- type,
7740
- mimeType: raw.mimeType,
7741
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7742
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7743
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
7744
- };
7745
- }
7746
- if (type === "video" && typeof raw.mimeType === "string") {
7747
- return {
7748
- type,
7749
- mimeType: raw.mimeType,
7750
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7751
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7752
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
7753
- };
7754
- }
7755
- if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
7756
- return {
7757
- type,
7758
- uri: raw.uri,
7759
- name: raw.name,
7760
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7761
- ...typeof raw.size === "number" ? { size: raw.size } : {}
7762
- };
7763
- }
7764
- if (type === "resource" && raw.resource && typeof raw.resource === "object") {
7765
- const resource = raw.resource;
7766
- if (typeof resource.uri !== "string") return null;
7767
- return {
7768
- type,
7769
- resource: {
7770
- uri: resource.uri,
7771
- ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
7772
- ...typeof resource.text === "string" ? { text: resource.text } : {},
7773
- ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
7774
- }
7775
- };
7776
- }
7777
- return null;
7778
- }
7779
- function flattenInputParts(parts) {
7780
- return parts.map((part) => {
7781
- if (part.type === "text") return part.text;
7782
- if (part.type === "audio") return part.transcript || "";
7783
- if (part.type === "resource") return part.text || "";
7784
- return "";
7785
- }).filter((value) => value.length > 0).join("\n");
7786
- }
7787
-
7788
- // src/providers/contracts.ts
7789
- function flattenContent(content) {
7790
- if (typeof content === "string") return content;
7791
- return flattenMessageParts(normalizeMessageParts(content));
7792
- }
7793
-
7794
7933
  // src/commands/chat-commands.ts
7795
7934
  init_logger();
7796
7935
 
@@ -7921,6 +8060,7 @@ function createInteractionId(prefix = "ix") {
7921
8060
  }
7922
8061
 
7923
8062
  // src/commands/chat-commands.ts
8063
+ init_chat_message_normalization();
7924
8064
  var RECENT_SEND_WINDOW_MS = 1200;
7925
8065
  var recentSendByTarget = /* @__PURE__ */ new Map();
7926
8066
  function hashSignatureParts(parts) {
@@ -8050,7 +8190,7 @@ function normalizeReadChatCursor(args) {
8050
8190
  }
8051
8191
  function normalizeReadChatMessages(payload) {
8052
8192
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
8053
- return messages;
8193
+ return normalizeChatMessages(messages);
8054
8194
  }
8055
8195
  function deriveHistoryDedupKey(message) {
8056
8196
  const unitKey = typeof message._unitKey === "string" ? message._unitKey.trim() : "";
@@ -10398,6 +10538,7 @@ import * as fs5 from "fs";
10398
10538
  import { createRequire } from "module";
10399
10539
  init_provider_cli_adapter();
10400
10540
  init_logger();
10541
+ init_chat_message_normalization();
10401
10542
  var CachedDatabaseSync = null;
10402
10543
  function getDatabaseSync() {
10403
10544
  if (CachedDatabaseSync) return CachedDatabaseSync;
@@ -10892,8 +11033,8 @@ var CliProviderInstance = class {
10892
11033
  if (this.appliedEffectKeys.has(effectKey)) continue;
10893
11034
  this.appliedEffectKeys.add(effectKey);
10894
11035
  if (effect.persist !== false) {
10895
- const persisted = this.getPersistedEffectContent(effect);
10896
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
11036
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
11037
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
10897
11038
  }
10898
11039
  if (effect.type === "message" && effect.message) {
10899
11040
  const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
@@ -11017,44 +11158,53 @@ ${effect.notification.body || ""}`.trim();
11017
11158
  );
11018
11159
  }
11019
11160
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
11020
- const normalizedContent = String(content || "").trim();
11021
- if (!normalizedContent) return;
11161
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
11162
+ content,
11163
+ receivedAt,
11164
+ timestamp: receivedAt
11165
+ }), dedupKey);
11166
+ }
11167
+ appendRuntimeMessage(message, dedupKey) {
11168
+ const normalizedMessage = buildChatMessage({
11169
+ ...message,
11170
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
11171
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
11172
+ });
11173
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
11174
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
11022
11175
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
11023
11176
  this.runtimeMessages.push({
11024
11177
  key: dedupKey,
11025
- message: {
11026
- role: "system",
11027
- senderName: "System",
11028
- content: normalizedContent,
11029
- receivedAt,
11030
- timestamp: receivedAt
11031
- }
11178
+ message: normalizedMessage
11032
11179
  });
11033
11180
  if (this.runtimeMessages.length > 50) {
11034
11181
  this.runtimeMessages = this.runtimeMessages.slice(-50);
11035
11182
  }
11036
- this.historyWriter.appendNewMessages(
11037
- this.type,
11038
- [{
11039
- role: "system",
11040
- senderName: "System",
11041
- content: normalizedContent,
11042
- receivedAt,
11043
- historyDedupKey: dedupKey
11044
- }],
11045
- this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
11046
- this.instanceId,
11047
- this.providerSessionId
11048
- );
11183
+ if (normalizedContent) {
11184
+ this.historyWriter.appendNewMessages(
11185
+ this.type,
11186
+ [{
11187
+ role: normalizedMessage.role,
11188
+ senderName: normalizedMessage.senderName,
11189
+ kind: normalizedMessage.kind,
11190
+ content: normalizedContent,
11191
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
11192
+ historyDedupKey: dedupKey
11193
+ }],
11194
+ this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
11195
+ this.instanceId,
11196
+ this.providerSessionId
11197
+ );
11198
+ }
11049
11199
  }
11050
11200
  mergeConversationMessages(parsedMessages) {
11051
- if (this.runtimeMessages.length === 0) return parsedMessages;
11052
- return [...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
11201
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(parsedMessages);
11202
+ return normalizeChatMessages([...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
11053
11203
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
11054
11204
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
11055
11205
  if (aTime !== bTime) return aTime - bTime;
11056
11206
  return a.index - b.index;
11057
- }).map((entry) => entry.message);
11207
+ }).map((entry) => entry.message));
11058
11208
  }
11059
11209
  formatApprovalRequestMessage(modalMessage, buttons) {
11060
11210
  const lines = ["Approval requested"];
@@ -11130,6 +11280,7 @@ import {
11130
11280
  RequestError,
11131
11281
  PROTOCOL_VERSION
11132
11282
  } from "@agentclientprotocol/sdk";
11283
+ init_chat_message_normalization();
11133
11284
  init_logger();
11134
11285
  function getPromptCapabilityFlags(agentCapabilities) {
11135
11286
  const prompt = agentCapabilities?.promptCapabilities || {};
@@ -11295,24 +11446,21 @@ var AcpProviderInstance = class {
11295
11446
  }
11296
11447
  getState() {
11297
11448
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
11298
- const recentMessages = this.messages.slice(-50).map((m) => {
11449
+ const recentMessages = normalizeChatMessages(this.messages.slice(-50).map((m) => {
11299
11450
  const content = this.truncateContent(m.content);
11300
- return {
11301
- role: m.role,
11302
- content,
11303
- timestamp: m.timestamp,
11304
- toolCalls: m.toolCalls
11305
- };
11306
- });
11451
+ return buildChatMessage({
11452
+ ...m,
11453
+ content
11454
+ });
11455
+ }));
11307
11456
  if (this.currentStatus === "generating" && (this.partialContent || this.partialBlocks.length > 0)) {
11308
11457
  const blocks = this.buildPartialBlocks();
11309
11458
  if (blocks.length > 0) {
11310
- recentMessages.push({
11311
- role: "assistant",
11459
+ recentMessages.push(buildAssistantChatMessage({
11312
11460
  content: blocks,
11313
11461
  timestamp: Date.now(),
11314
11462
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
11315
- });
11463
+ }));
11316
11464
  }
11317
11465
  }
11318
11466
  return {
@@ -11325,7 +11473,7 @@ var AcpProviderInstance = class {
11325
11473
  id: this.sessionId || `${this.type}_${this.workingDir}`,
11326
11474
  title: `${this.provider.name} \xB7 ${dirName}`,
11327
11475
  status: this.currentStatus,
11328
- messages: recentMessages,
11476
+ messages: normalizeChatMessages(recentMessages),
11329
11477
  activeModal: this.currentStatus === "waiting_approval" ? {
11330
11478
  message: this.activeToolCalls.find((t) => t.status === "running")?.name || "Permission requested",
11331
11479
  buttons: ["Approve", "Reject"]
@@ -11857,11 +12005,10 @@ var AcpProviderInstance = class {
11857
12005
  if (b.type === "resource") return { type: "resource", resource: b.resource };
11858
12006
  return { type: "text", text: flattenContent([b]) };
11859
12007
  }) : [{ type: "text", text }];
11860
- this.messages.push({
11861
- role: "user",
12008
+ this.messages.push(buildUserChatMessage({
11862
12009
  content: contentBlocks && contentBlocks.length > 0 ? contentBlocks : text,
11863
12010
  timestamp: Date.now()
11864
- });
12011
+ }));
11865
12012
  this.currentStatus = "generating";
11866
12013
  this.partialContent = "";
11867
12014
  this.partialBlocks = [];
@@ -12031,11 +12178,11 @@ var AcpProviderInstance = class {
12031
12178
  content = m.content.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
12032
12179
  }
12033
12180
  if (content.trim()) {
12034
- this.messages.push({
12181
+ this.messages.push(buildChatMessage({
12035
12182
  role: m.role || "assistant",
12036
12183
  content: content.trim(),
12037
12184
  timestamp: Date.now()
12038
- });
12185
+ }));
12039
12186
  this.partialContent = "";
12040
12187
  }
12041
12188
  }
@@ -12111,12 +12258,11 @@ var AcpProviderInstance = class {
12111
12258
  return b;
12112
12259
  }).filter((b) => b.type !== "text" || b.type === "text" && b.text.trim());
12113
12260
  if (finalBlocks.length > 0) {
12114
- this.messages.push({
12115
- role: "assistant",
12261
+ this.messages.push(buildAssistantChatMessage({
12116
12262
  content: finalBlocks.length === 1 && finalBlocks[0].type === "text" ? finalBlocks[0].text : finalBlocks,
12117
12263
  timestamp: Date.now(),
12118
12264
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
12119
- });
12265
+ }));
12120
12266
  }
12121
12267
  this.partialContent = "";
12122
12268
  this.partialBlocks = [];
@@ -12176,11 +12322,10 @@ var AcpProviderInstance = class {
12176
12322
  appendSystemMessage(content, timestamp = Date.now()) {
12177
12323
  const normalizedContent = String(content || "").trim();
12178
12324
  if (!normalizedContent) return;
12179
- this.messages.push({
12180
- role: "system",
12325
+ this.messages.push(buildRuntimeSystemChatMessage({
12181
12326
  content: normalizedContent,
12182
12327
  timestamp
12183
- });
12328
+ }));
12184
12329
  if (this.messages.length > 200) {
12185
12330
  this.messages = this.messages.slice(-100);
12186
12331
  }
@@ -16851,6 +16996,7 @@ var DaemonAgentStreamManager = class {
16851
16996
 
16852
16997
  // src/agent-stream/poller.ts
16853
16998
  init_logger();
16999
+ init_chat_message_normalization();
16854
17000
  var AgentStreamPoller = class {
16855
17001
  deps;
16856
17002
  timer = null;
@@ -17006,12 +17152,9 @@ var AgentStreamPoller = class {
17006
17152
  type: "message",
17007
17153
  id: effectId,
17008
17154
  persist: true,
17009
- message: {
17010
- role: "system",
17011
- senderName: "System",
17012
- kind: "system",
17155
+ message: buildRuntimeSystemChatMessage({
17013
17156
  content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
17014
- }
17157
+ })
17015
17158
  }
17016
17159
  ]
17017
17160
  };
@@ -17258,6 +17401,9 @@ var ProviderInstanceManager = class {
17258
17401
  }
17259
17402
  };
17260
17403
 
17404
+ // src/index.ts
17405
+ init_chat_message_normalization();
17406
+
17261
17407
  // src/providers/version-archive.ts
17262
17408
  import * as fs10 from "fs";
17263
17409
  import * as path18 from "path";
@@ -23537,6 +23683,7 @@ async function shutdownDaemonComponents(components) {
23537
23683
  export {
23538
23684
  AcpProviderInstance,
23539
23685
  AgentStreamPoller,
23686
+ BUILTIN_CHAT_MESSAGE_KINDS,
23540
23687
  CdpDomHandlers,
23541
23688
  CliProviderInstance,
23542
23689
  DAEMON_WS_PATH,
@@ -23561,9 +23708,14 @@ export {
23561
23708
  SessionHostPtyTransportFactory,
23562
23709
  VersionArchive,
23563
23710
  appendRecentActivity,
23711
+ buildAssistantChatMessage,
23712
+ buildChatMessage,
23564
23713
  buildMachineInfo,
23714
+ buildRuntimeSystemChatMessage,
23565
23715
  buildSessionEntries,
23566
23716
  buildStatusSnapshot,
23717
+ buildSystemChatMessage,
23718
+ buildUserChatMessage,
23567
23719
  clearDebugTrace,
23568
23720
  configureDebugTraceStore,
23569
23721
  connectCdpManager,
@@ -23595,6 +23747,7 @@ export {
23595
23747
  initDaemonComponents,
23596
23748
  installExtensions,
23597
23749
  installGlobalInterceptor,
23750
+ isBuiltinChatMessageKind,
23598
23751
  isCdpConnected,
23599
23752
  isExtensionInstalled,
23600
23753
  isIdeRunning,
@@ -23613,6 +23766,9 @@ export {
23613
23766
  markSetupComplete,
23614
23767
  maybeRunDaemonUpgradeHelperFromEnv,
23615
23768
  normalizeActiveChatData,
23769
+ normalizeChatMessage,
23770
+ normalizeChatMessageKind,
23771
+ normalizeChatMessages,
23616
23772
  normalizeInputEnvelope,
23617
23773
  normalizeManagedStatus,
23618
23774
  normalizeMessageParts,