@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.js CHANGED
@@ -458,6 +458,64 @@ var init_logger = __esm({
458
458
  }
459
459
  });
460
460
 
461
+ // src/providers/chat-message-normalization.ts
462
+ function isBuiltinChatMessageKind(kind) {
463
+ return typeof kind === "string" && KNOWN_CHAT_MESSAGE_KINDS.has(kind.trim().toLowerCase());
464
+ }
465
+ function normalizeChatMessageKind(kind, role) {
466
+ const normalizedKind = typeof kind === "string" ? kind.trim().toLowerCase() : "";
467
+ if (normalizedKind && KNOWN_CHAT_MESSAGE_KINDS.has(normalizedKind)) return normalizedKind;
468
+ const normalizedRole = typeof role === "string" ? role.trim().toLowerCase() : "";
469
+ return normalizedRole === "system" ? "system" : "standard";
470
+ }
471
+ function buildChatMessage(message) {
472
+ return {
473
+ ...message,
474
+ kind: normalizeChatMessageKind(message?.kind, message?.role)
475
+ };
476
+ }
477
+ function buildSystemChatMessage(message) {
478
+ return buildChatMessage({
479
+ ...message,
480
+ role: "system",
481
+ kind: message?.kind || "system"
482
+ });
483
+ }
484
+ function buildRuntimeSystemChatMessage(message) {
485
+ return buildSystemChatMessage({
486
+ ...message,
487
+ senderName: typeof message?.senderName === "string" && message.senderName.trim() ? message.senderName : "System"
488
+ });
489
+ }
490
+ function buildAssistantChatMessage(message) {
491
+ return buildChatMessage({
492
+ ...message,
493
+ role: "assistant",
494
+ kind: message?.kind || "standard"
495
+ });
496
+ }
497
+ function buildUserChatMessage(message) {
498
+ return buildChatMessage({
499
+ ...message,
500
+ role: "user",
501
+ kind: message?.kind || "standard"
502
+ });
503
+ }
504
+ function normalizeChatMessage(message) {
505
+ return buildChatMessage(message);
506
+ }
507
+ function normalizeChatMessages(messages) {
508
+ return (Array.isArray(messages) ? messages : []).map((message) => normalizeChatMessage(message));
509
+ }
510
+ var BUILTIN_CHAT_MESSAGE_KINDS, KNOWN_CHAT_MESSAGE_KINDS;
511
+ var init_chat_message_normalization = __esm({
512
+ "src/providers/chat-message-normalization.ts"() {
513
+ "use strict";
514
+ BUILTIN_CHAT_MESSAGE_KINDS = ["standard", "thought", "tool", "terminal", "system"];
515
+ KNOWN_CHAT_MESSAGE_KINDS = new Set(BUILTIN_CHAT_MESSAGE_KINDS);
516
+ }
517
+ });
518
+
461
519
  // src/cli-adapters/terminal-backends/ghostty-vt-backend.ts
462
520
  function isModuleNotFoundError(error, ref) {
463
521
  if (!(error instanceof Error)) return false;
@@ -1310,6 +1368,7 @@ var init_provider_cli_adapter = __esm({
1310
1368
  init_terminal_screen();
1311
1369
  init_pty_transport();
1312
1370
  init_provider_cli_shared();
1371
+ init_chat_message_normalization();
1313
1372
  init_provider_cli_parse();
1314
1373
  init_provider_cli_config();
1315
1374
  init_provider_cli_runtime();
@@ -1420,6 +1479,7 @@ var init_provider_cli_adapter = __esm({
1420
1479
  static MAX_TRACE_ENTRIES = 250;
1421
1480
  providerResolutionMeta;
1422
1481
  static IDLE_FINISH_CONFIRM_MS = 2e3;
1482
+ static HERMES_IDLE_FINISH_CONFIRM_MS = 5e3;
1423
1483
  static STATUS_ACTIVITY_HOLD_MS = 2e3;
1424
1484
  static FINISH_RETRY_DELAY_MS = 300;
1425
1485
  static MAX_FINISH_RETRIES = 2;
@@ -1427,6 +1487,12 @@ var init_provider_cli_adapter = __esm({
1427
1487
  this.messages = [...this.committedMessages];
1428
1488
  this.structuredMessages = [...this.committedMessages];
1429
1489
  }
1490
+ getIdleFinishConfirmMs() {
1491
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
1492
+ }
1493
+ getStatusActivityHoldMs() {
1494
+ return this.cliType === "hermes-cli" ? _ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS : _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
1495
+ }
1430
1496
  setStatus(status, trigger) {
1431
1497
  const prev = this.currentStatus;
1432
1498
  if (prev === status) return;
@@ -1449,6 +1515,7 @@ var init_provider_cli_adapter = __esm({
1449
1515
  }
1450
1516
  armIdleFinishCandidate(assistantLength) {
1451
1517
  const now = Date.now();
1518
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
1452
1519
  this.idleFinishCandidate = {
1453
1520
  armedAt: now,
1454
1521
  lastOutputAt: this.lastOutputAt,
@@ -1457,7 +1524,7 @@ var init_provider_cli_adapter = __esm({
1457
1524
  assistantLength
1458
1525
  };
1459
1526
  this.recordTrace("idle_candidate_armed", {
1460
- confirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
1527
+ confirmMs: idleFinishConfirmMs,
1461
1528
  candidate: this.idleFinishCandidate,
1462
1529
  ...buildCliTraceParseSnapshot({
1463
1530
  accumulatedBuffer: this.accumulatedBuffer,
@@ -1472,7 +1539,7 @@ var init_provider_cli_adapter = __esm({
1472
1539
  this.settleTimer = null;
1473
1540
  this.settledBuffer = this.recentOutputBuffer;
1474
1541
  this.evaluateSettled();
1475
- }, _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS);
1542
+ }, idleFinishConfirmMs);
1476
1543
  }
1477
1544
  recordTrace(type, payload = {}) {
1478
1545
  const entry = {
@@ -1851,7 +1918,8 @@ var init_provider_cli_adapter = __esm({
1851
1918
  hasRecentInteractiveActivity(now) {
1852
1919
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
1853
1920
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : Number.MAX_SAFE_INTEGER;
1854
- return quietForMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS || screenStableMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
1921
+ const holdMs = this.getStatusActivityHoldMs();
1922
+ return quietForMs < holdMs || screenStableMs < holdMs;
1855
1923
  }
1856
1924
  getStartupConfirmationModal(screenText) {
1857
1925
  const text = sanitizeTerminalText(String(screenText || ""));
@@ -2003,6 +2071,7 @@ var init_provider_cli_adapter = __esm({
2003
2071
  clearPendingScriptStatus();
2004
2072
  }
2005
2073
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
2074
+ const statusActivityHoldMs = this.getStatusActivityHoldMs();
2006
2075
  const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
2007
2076
  if (shouldHoldGenerating) {
2008
2077
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
@@ -2018,7 +2087,7 @@ var init_provider_cli_adapter = __esm({
2018
2087
  recentInteractiveActivity,
2019
2088
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
2020
2089
  lastScreenChangeAt: this.lastScreenChangeAt,
2021
- holdMs: _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS,
2090
+ holdMs: statusActivityHoldMs,
2022
2091
  ...buildCliTraceParseSnapshot({
2023
2092
  accumulatedBuffer: this.accumulatedBuffer,
2024
2093
  accumulatedRawBuffer: this.accumulatedRawBuffer,
@@ -2107,11 +2176,12 @@ var init_provider_cli_adapter = __esm({
2107
2176
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
2108
2177
  const hasAssistantTurn = !!lastParsedAssistant;
2109
2178
  const assistantLength = lastParsedAssistant?.content?.length || 0;
2110
- const idleQuietThresholdMs = Math.max(2e3, this.timeouts.outputSettle);
2111
- const idleStableThresholdMs = 2e3;
2179
+ const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
2180
+ const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
2181
+ const idleStableThresholdMs = idleFinishConfirmMs;
2112
2182
  const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
2113
2183
  const candidate = this.idleFinishCandidate;
2114
- 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;
2184
+ const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
2115
2185
  const canFinishImmediately = idleReady && candidateQuiet;
2116
2186
  this.recordTrace("idle_decision", {
2117
2187
  visibleIdlePrompt,
@@ -2123,7 +2193,7 @@ var init_provider_cli_adapter = __esm({
2123
2193
  idleQuietThresholdMs,
2124
2194
  idleStableThresholdMs,
2125
2195
  idleReady,
2126
- idleFinishConfirmMs: _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS,
2196
+ idleFinishConfirmMs,
2127
2197
  idleFinishCandidate: candidate,
2128
2198
  candidateQuiet,
2129
2199
  canFinishImmediately,
@@ -2356,11 +2426,10 @@ var init_provider_cli_adapter = __esm({
2356
2426
  );
2357
2427
  const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
2358
2428
  if (parsed && Array.isArray(parsed.messages)) {
2359
- const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
2429
+ const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => buildChatMessage({
2360
2430
  ...message,
2361
2431
  id: message.id || `msg_${index}`,
2362
2432
  index: typeof message.index === "number" ? message.index : index,
2363
- kind: message.kind || "standard",
2364
2433
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2365
2434
  })) : hydrateCliParsedMessages(parsed.messages, {
2366
2435
  committedMessages: this.committedMessages,
@@ -2381,13 +2450,11 @@ var init_provider_cli_adapter = __esm({
2381
2450
  id: "cli_session",
2382
2451
  status: this.currentStatus,
2383
2452
  title: this.cliName,
2384
- messages: messages.slice(-50).map((message, index) => ({
2385
- id: `msg_${index}`,
2386
- role: message.role,
2387
- content: message.content,
2388
- timestamp: message.timestamp,
2389
- index,
2390
- kind: "standard"
2453
+ messages: messages.slice(-50).map((message, index) => buildChatMessage({
2454
+ ...message,
2455
+ id: message.id || `msg_${index}`,
2456
+ index: typeof message.index === "number" ? message.index : index,
2457
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2391
2458
  })),
2392
2459
  activeModal: this.activeModal
2393
2460
  };
@@ -3012,6 +3079,7 @@ var index_exports = {};
3012
3079
  __export(index_exports, {
3013
3080
  AcpProviderInstance: () => AcpProviderInstance,
3014
3081
  AgentStreamPoller: () => AgentStreamPoller,
3082
+ BUILTIN_CHAT_MESSAGE_KINDS: () => BUILTIN_CHAT_MESSAGE_KINDS,
3015
3083
  CdpDomHandlers: () => CdpDomHandlers,
3016
3084
  CliProviderInstance: () => CliProviderInstance,
3017
3085
  DAEMON_WS_PATH: () => DAEMON_WS_PATH,
@@ -3036,9 +3104,14 @@ __export(index_exports, {
3036
3104
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
3037
3105
  VersionArchive: () => VersionArchive,
3038
3106
  appendRecentActivity: () => appendRecentActivity,
3107
+ buildAssistantChatMessage: () => buildAssistantChatMessage,
3108
+ buildChatMessage: () => buildChatMessage,
3039
3109
  buildMachineInfo: () => buildMachineInfo,
3110
+ buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
3040
3111
  buildSessionEntries: () => buildSessionEntries,
3041
3112
  buildStatusSnapshot: () => buildStatusSnapshot,
3113
+ buildSystemChatMessage: () => buildSystemChatMessage,
3114
+ buildUserChatMessage: () => buildUserChatMessage,
3042
3115
  clearDebugTrace: () => clearDebugTrace,
3043
3116
  configureDebugTraceStore: () => configureDebugTraceStore,
3044
3117
  connectCdpManager: () => connectCdpManager,
@@ -3070,6 +3143,7 @@ __export(index_exports, {
3070
3143
  initDaemonComponents: () => initDaemonComponents,
3071
3144
  installExtensions: () => installExtensions,
3072
3145
  installGlobalInterceptor: () => installGlobalInterceptor,
3146
+ isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
3073
3147
  isCdpConnected: () => isCdpConnected,
3074
3148
  isExtensionInstalled: () => isExtensionInstalled,
3075
3149
  isIdeRunning: () => isIdeRunning,
@@ -3088,6 +3162,9 @@ __export(index_exports, {
3088
3162
  markSetupComplete: () => markSetupComplete,
3089
3163
  maybeRunDaemonUpgradeHelperFromEnv: () => maybeRunDaemonUpgradeHelperFromEnv,
3090
3164
  normalizeActiveChatData: () => normalizeActiveChatData,
3165
+ normalizeChatMessage: () => normalizeChatMessage,
3166
+ normalizeChatMessageKind: () => normalizeChatMessageKind,
3167
+ normalizeChatMessages: () => normalizeChatMessages,
3091
3168
  normalizeInputEnvelope: () => normalizeInputEnvelope,
3092
3169
  normalizeManagedStatus: () => normalizeManagedStatus,
3093
3170
  normalizeMessageParts: () => normalizeMessageParts,
@@ -5094,6 +5171,219 @@ var CdpDomHandlers = class {
5094
5171
  // src/providers/ide-provider-instance.ts
5095
5172
  var crypto2 = __toESM(require("crypto"));
5096
5173
 
5174
+ // src/providers/io-contracts.ts
5175
+ function normalizeInputEnvelope(input) {
5176
+ const normalized = normalizeInputEnvelopePayload(input);
5177
+ const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
5178
+ return {
5179
+ parts: normalized.parts,
5180
+ textFallback,
5181
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
5182
+ };
5183
+ }
5184
+ function normalizeMessageParts(content) {
5185
+ if (typeof content === "string") return [{ type: "text", text: content }];
5186
+ if (!Array.isArray(content)) {
5187
+ if (content && typeof content === "object" && typeof content.text === "string") {
5188
+ return [{ type: "text", text: String(content.text) }];
5189
+ }
5190
+ return [];
5191
+ }
5192
+ const parts = [];
5193
+ for (const raw of content) {
5194
+ if (typeof raw === "string") {
5195
+ parts.push({ type: "text", text: raw });
5196
+ continue;
5197
+ }
5198
+ if (!raw || typeof raw !== "object") continue;
5199
+ const part = normalizeMessagePartObject(raw);
5200
+ if (part) parts.push(part);
5201
+ }
5202
+ return parts;
5203
+ }
5204
+ function flattenMessageParts(parts) {
5205
+ return parts.map((part) => {
5206
+ if (part.type === "text") return part.text;
5207
+ if (part.type === "resource") return part.resource.text || "";
5208
+ return "";
5209
+ }).filter((value) => value.length > 0).join("\n");
5210
+ }
5211
+ function normalizeInputEnvelopePayload(input) {
5212
+ if (typeof input === "string") {
5213
+ return { parts: [{ type: "text", text: input }], textFallback: input };
5214
+ }
5215
+ if (!input || typeof input !== "object") {
5216
+ return { parts: [], textFallback: "" };
5217
+ }
5218
+ const record = input;
5219
+ const nestedInput = record.input;
5220
+ if (nestedInput && typeof nestedInput === "object") {
5221
+ const nested = nestedInput;
5222
+ return {
5223
+ parts: normalizeInputParts(nested.parts ?? nested.prompt),
5224
+ textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
5225
+ metadata: normalizeInputMetadata(nested.metadata)
5226
+ };
5227
+ }
5228
+ const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
5229
+ if (directText !== void 0) {
5230
+ return { parts: [{ type: "text", text: directText }], textFallback: directText };
5231
+ }
5232
+ const directParts = normalizeInputParts(record.parts ?? record.prompt);
5233
+ return {
5234
+ parts: directParts,
5235
+ textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
5236
+ metadata: normalizeInputMetadata(record.metadata)
5237
+ };
5238
+ }
5239
+ function normalizeInputMetadata(value) {
5240
+ if (!value || typeof value !== "object") return void 0;
5241
+ const record = value;
5242
+ const metadata = {};
5243
+ if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
5244
+ metadata.source = record.source;
5245
+ }
5246
+ if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
5247
+ metadata.clientTimestamp = record.clientTimestamp;
5248
+ }
5249
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
5250
+ }
5251
+ function normalizeInputParts(value) {
5252
+ if (!Array.isArray(value)) return [];
5253
+ const parts = [];
5254
+ for (const raw of value) {
5255
+ if (typeof raw === "string") {
5256
+ parts.push({ type: "text", text: raw });
5257
+ continue;
5258
+ }
5259
+ if (!raw || typeof raw !== "object") continue;
5260
+ const part = normalizeInputPartObject(raw);
5261
+ if (part) parts.push(part);
5262
+ }
5263
+ return parts;
5264
+ }
5265
+ function normalizeInputPartObject(raw) {
5266
+ const type = raw.type;
5267
+ if (type === "text" && typeof raw.text === "string") {
5268
+ return { type, text: raw.text };
5269
+ }
5270
+ if (type === "image" && typeof raw.mimeType === "string") {
5271
+ return {
5272
+ type,
5273
+ mimeType: raw.mimeType,
5274
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5275
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5276
+ ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
5277
+ };
5278
+ }
5279
+ if (type === "audio" && typeof raw.mimeType === "string") {
5280
+ return {
5281
+ type,
5282
+ mimeType: raw.mimeType,
5283
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5284
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5285
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5286
+ };
5287
+ }
5288
+ if (type === "video" && typeof raw.mimeType === "string") {
5289
+ return {
5290
+ type,
5291
+ mimeType: raw.mimeType,
5292
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5293
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5294
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5295
+ };
5296
+ }
5297
+ if (type === "resource" && typeof raw.uri === "string") {
5298
+ return {
5299
+ type,
5300
+ uri: raw.uri,
5301
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5302
+ ...typeof raw.name === "string" ? { name: raw.name } : {},
5303
+ ...typeof raw.text === "string" ? { text: raw.text } : {},
5304
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
5305
+ };
5306
+ }
5307
+ if (type === "resource_link" && typeof raw.uri === "string") {
5308
+ return {
5309
+ type: "resource",
5310
+ uri: raw.uri,
5311
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5312
+ ...typeof raw.name === "string" ? { name: raw.name } : {}
5313
+ };
5314
+ }
5315
+ return null;
5316
+ }
5317
+ function normalizeMessagePartObject(raw) {
5318
+ const type = raw.type;
5319
+ if (type === "text" && typeof raw.text === "string") {
5320
+ return { type, text: raw.text };
5321
+ }
5322
+ if (type === "image" && typeof raw.mimeType === "string") {
5323
+ return {
5324
+ type,
5325
+ mimeType: raw.mimeType,
5326
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5327
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
5328
+ };
5329
+ }
5330
+ if (type === "audio" && typeof raw.mimeType === "string") {
5331
+ return {
5332
+ type,
5333
+ mimeType: raw.mimeType,
5334
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5335
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5336
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5337
+ };
5338
+ }
5339
+ if (type === "video" && typeof raw.mimeType === "string") {
5340
+ return {
5341
+ type,
5342
+ mimeType: raw.mimeType,
5343
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5344
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5345
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5346
+ };
5347
+ }
5348
+ if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
5349
+ return {
5350
+ type,
5351
+ uri: raw.uri,
5352
+ name: raw.name,
5353
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5354
+ ...typeof raw.size === "number" ? { size: raw.size } : {}
5355
+ };
5356
+ }
5357
+ if (type === "resource" && raw.resource && typeof raw.resource === "object") {
5358
+ const resource = raw.resource;
5359
+ if (typeof resource.uri !== "string") return null;
5360
+ return {
5361
+ type,
5362
+ resource: {
5363
+ uri: resource.uri,
5364
+ ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
5365
+ ...typeof resource.text === "string" ? { text: resource.text } : {},
5366
+ ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
5367
+ }
5368
+ };
5369
+ }
5370
+ return null;
5371
+ }
5372
+ function flattenInputParts(parts) {
5373
+ return parts.map((part) => {
5374
+ if (part.type === "text") return part.text;
5375
+ if (part.type === "audio") return part.transcript || "";
5376
+ if (part.type === "resource") return part.text || "";
5377
+ return "";
5378
+ }).filter((value) => value.length > 0).join("\n");
5379
+ }
5380
+
5381
+ // src/providers/contracts.ts
5382
+ function flattenContent(content) {
5383
+ if (typeof content === "string") return content;
5384
+ return flattenMessageParts(normalizeMessageParts(content));
5385
+ }
5386
+
5097
5387
  // src/providers/status-monitor.ts
5098
5388
  var DEFAULT_MONITOR_CONFIG = {
5099
5389
  approvalAlert: true,
@@ -5207,6 +5497,7 @@ var StatusMonitor = class {
5207
5497
  };
5208
5498
 
5209
5499
  // src/providers/control-effects.ts
5500
+ init_chat_message_normalization();
5210
5501
  function extractProviderControlValues(controls, data) {
5211
5502
  if (!data || typeof data !== "object") return void 0;
5212
5503
  const values = {};
@@ -5274,13 +5565,58 @@ function normalizeProviderEffects(data) {
5274
5565
  level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
5275
5566
  channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
5276
5567
  preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
5277
- bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
5568
+ bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0,
5569
+ bubbleKind: typeof raw.notification.bubbleKind === "string" ? raw.notification.bubbleKind : void 0,
5570
+ bubbleRole: raw.notification.bubbleRole === "assistant" || raw.notification.bubbleRole === "user" ? raw.notification.bubbleRole : raw.notification.bubbleRole === "system" ? "system" : void 0,
5571
+ bubbleSenderName: typeof raw.notification.bubbleSenderName === "string" ? raw.notification.bubbleSenderName : void 0
5278
5572
  }
5279
5573
  });
5280
5574
  }
5281
5575
  }
5282
5576
  return effects;
5283
5577
  }
5578
+ function buildPersistedProviderEffectMessage(effect) {
5579
+ if (!effect) return null;
5580
+ if (effect.type === "message" && effect.message) {
5581
+ const role = effect.message.role === "assistant" || effect.message.role === "user" ? effect.message.role : "system";
5582
+ if (role === "system") {
5583
+ return buildRuntimeSystemChatMessage({
5584
+ content: effect.message.content,
5585
+ kind: effect.message.kind,
5586
+ senderName: effect.message.senderName
5587
+ });
5588
+ }
5589
+ return buildChatMessage({
5590
+ role,
5591
+ content: effect.message.content,
5592
+ kind: effect.message.kind,
5593
+ senderName: effect.message.senderName
5594
+ });
5595
+ }
5596
+ if (effect.type === "notification" && effect.notification) {
5597
+ const bubbleContent = effect.notification.bubbleContent ?? formatNotificationBubbleFallback(effect.notification.title, effect.notification.body);
5598
+ const flattened = typeof bubbleContent === "string" ? bubbleContent.trim() : flattenContent(bubbleContent).trim();
5599
+ if (!flattened && (!Array.isArray(bubbleContent) || bubbleContent.length === 0)) return null;
5600
+ const role = effect.notification.bubbleRole === "assistant" || effect.notification.bubbleRole === "user" ? effect.notification.bubbleRole : "system";
5601
+ if (role === "system") {
5602
+ return buildRuntimeSystemChatMessage({
5603
+ content: bubbleContent,
5604
+ kind: effect.notification.bubbleKind,
5605
+ senderName: effect.notification.bubbleSenderName
5606
+ });
5607
+ }
5608
+ return buildChatMessage({
5609
+ role,
5610
+ content: bubbleContent,
5611
+ kind: effect.notification.bubbleKind,
5612
+ senderName: effect.notification.bubbleSenderName
5613
+ });
5614
+ }
5615
+ if (effect.type === "toast" && effect.toast?.message) {
5616
+ return buildRuntimeSystemChatMessage({ content: effect.toast.message });
5617
+ }
5618
+ return null;
5619
+ }
5284
5620
  function normalizeControlListResult(data) {
5285
5621
  if (data && typeof data === "object" && Array.isArray(data.options)) {
5286
5622
  return {
@@ -5347,11 +5683,19 @@ function normalizeControlValue(value) {
5347
5683
  }
5348
5684
  return String(value);
5349
5685
  }
5686
+ function formatNotificationBubbleFallback(title, body) {
5687
+ const cleanTitle = typeof title === "string" ? title.trim() : "";
5688
+ const cleanBody = String(body || "").trim();
5689
+ if (cleanTitle && cleanBody) return `${cleanTitle}
5690
+ ${cleanBody}`;
5691
+ return cleanTitle || cleanBody;
5692
+ }
5350
5693
 
5351
5694
  // src/config/chat-history.ts
5352
5695
  var fs3 = __toESM(require("fs"));
5353
5696
  var path7 = __toESM(require("path"));
5354
5697
  var os5 = __toESM(require("os"));
5698
+ init_chat_message_normalization();
5355
5699
  var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
5356
5700
  var RETAIN_DAYS = 30;
5357
5701
  var 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;
@@ -5557,11 +5901,11 @@ var ChatHistoryWriter = class {
5557
5901
  this.appendNewMessages(
5558
5902
  agentType,
5559
5903
  [{
5560
- role: "system",
5561
- kind: "system",
5562
- content,
5563
- receivedAt: options.receivedAt,
5564
- senderName: options.senderName,
5904
+ ...buildRuntimeSystemChatMessage({
5905
+ content,
5906
+ receivedAt: options.receivedAt,
5907
+ senderName: options.senderName
5908
+ }),
5565
5909
  historyDedupKey: options.dedupKey
5566
5910
  }],
5567
5911
  options.sessionTitle,
@@ -5916,6 +6260,7 @@ function resolveProviderStateSurface(params) {
5916
6260
  }
5917
6261
 
5918
6262
  // src/providers/extension-provider-instance.ts
6263
+ init_chat_message_normalization();
5919
6264
  var ExtensionProviderInstance = class {
5920
6265
  type;
5921
6266
  category = "extension";
@@ -6122,8 +6467,8 @@ var ExtensionProviderInstance = class {
6122
6467
  if (this.appliedEffectKeys.has(effectKey)) continue;
6123
6468
  this.appliedEffectKeys.add(effectKey);
6124
6469
  if (effect.persist !== false) {
6125
- const persisted = this.getPersistedEffectContent(effect);
6126
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
6470
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
6471
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
6127
6472
  }
6128
6473
  if (effect.type === "message" && effect.message) {
6129
6474
  this.pushEvent({
@@ -6158,34 +6503,42 @@ var ExtensionProviderInstance = class {
6158
6503
  }
6159
6504
  }
6160
6505
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
6161
- const normalizedContent = String(content || "").trim();
6162
- if (!normalizedContent) return;
6506
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
6507
+ content,
6508
+ receivedAt,
6509
+ timestamp: receivedAt
6510
+ }), dedupKey);
6511
+ }
6512
+ appendRuntimeMessage(message, dedupKey) {
6513
+ const normalizedMessage = buildChatMessage({
6514
+ ...message,
6515
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
6516
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
6517
+ });
6518
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
6519
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
6163
6520
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
6164
6521
  this.runtimeMessages.push({
6165
6522
  key: dedupKey,
6166
- message: {
6167
- role: "system",
6168
- senderName: "System",
6169
- content: normalizedContent,
6170
- receivedAt,
6171
- timestamp: receivedAt
6172
- }
6523
+ message: normalizedMessage
6173
6524
  });
6174
6525
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
6175
- this.historyWriter.appendNewMessages(
6176
- this.type,
6177
- [{
6178
- role: "system",
6179
- senderName: "System",
6180
- content: normalizedContent,
6181
- kind: "system",
6182
- receivedAt,
6183
- historyDedupKey: dedupKey
6184
- }],
6185
- this.chatTitle || this.agentName || this.provider.name,
6186
- this.instanceId,
6187
- this.chatId || this.instanceId
6188
- );
6526
+ if (normalizedContent) {
6527
+ this.historyWriter.appendNewMessages(
6528
+ this.type,
6529
+ [{
6530
+ role: normalizedMessage.role,
6531
+ senderName: normalizedMessage.senderName,
6532
+ kind: normalizedMessage.kind,
6533
+ content: normalizedContent,
6534
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
6535
+ historyDedupKey: dedupKey
6536
+ }],
6537
+ this.chatTitle || this.agentName || this.provider.name,
6538
+ this.instanceId,
6539
+ this.chatId || this.instanceId
6540
+ );
6541
+ }
6189
6542
  }
6190
6543
  /**
6191
6544
  * Assign stable receivedAt to extension messages.
@@ -6202,16 +6555,16 @@ var ExtensionProviderInstance = class {
6202
6555
  nextHashes.set(hash, msg.receivedAt);
6203
6556
  }
6204
6557
  this.prevMessageHashes = nextHashes;
6205
- return messages;
6558
+ return normalizeChatMessages(messages);
6206
6559
  }
6207
6560
  mergeConversationMessages(messages) {
6208
- if (this.runtimeMessages.length === 0) return messages;
6209
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6561
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
6562
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6210
6563
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
6211
6564
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
6212
6565
  if (aTime !== bTime) return aTime - bTime;
6213
6566
  return a.index - b.index;
6214
- }).map((entry) => entry.message);
6567
+ }).map((entry) => entry.message));
6215
6568
  }
6216
6569
  getPersistedEffectContent(effect) {
6217
6570
  if (effect.type === "message") {
@@ -6325,6 +6678,7 @@ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
6325
6678
  }
6326
6679
 
6327
6680
  // src/providers/ide-provider-instance.ts
6681
+ init_chat_message_normalization();
6328
6682
  var IdeProviderInstance = class {
6329
6683
  type;
6330
6684
  category = "ide";
@@ -6705,8 +7059,8 @@ var IdeProviderInstance = class {
6705
7059
  if (this.appliedEffectKeys.has(effectKey)) continue;
6706
7060
  this.appliedEffectKeys.add(effectKey);
6707
7061
  if (effect.persist !== false) {
6708
- const persisted = this.getPersistedEffectContent(effect);
6709
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
7062
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
7063
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
6710
7064
  }
6711
7065
  if (effect.type === "message" && effect.message) {
6712
7066
  this.pushEvent({
@@ -6741,8 +7095,20 @@ var IdeProviderInstance = class {
6741
7095
  }
6742
7096
  }
6743
7097
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
6744
- const normalizedContent = String(content || "").trim();
6745
- if (!normalizedContent) return;
7098
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
7099
+ content,
7100
+ receivedAt,
7101
+ timestamp: receivedAt
7102
+ }), dedupKey);
7103
+ }
7104
+ appendRuntimeMessage(message, dedupKey) {
7105
+ const normalizedMessage = buildChatMessage({
7106
+ ...message,
7107
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
7108
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
7109
+ });
7110
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
7111
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
6746
7112
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
6747
7113
  if (!this.cachedChat) {
6748
7114
  this.cachedChat = {
@@ -6755,39 +7121,35 @@ var IdeProviderInstance = class {
6755
7121
  };
6756
7122
  }
6757
7123
  this.runtimeMessages.push({
6758
- key: dedupKey,
6759
- message: {
6760
- role: "system",
6761
- senderName: "System",
6762
- content: normalizedContent,
6763
- receivedAt,
6764
- timestamp: receivedAt
6765
- }
7124
+ key: dedupKey,
7125
+ message: normalizedMessage
6766
7126
  });
6767
7127
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
6768
- this.historyWriter.appendNewMessages(
6769
- this.type,
6770
- [{
6771
- role: "system",
6772
- senderName: "System",
6773
- content: normalizedContent,
6774
- kind: "system",
6775
- receivedAt,
6776
- historyDedupKey: dedupKey
6777
- }],
6778
- this.cachedChat?.title || this.provider.name,
6779
- this.instanceId,
6780
- this.cachedChat?.id || this.instanceId
6781
- );
7128
+ if (normalizedContent) {
7129
+ this.historyWriter.appendNewMessages(
7130
+ this.type,
7131
+ [{
7132
+ role: normalizedMessage.role,
7133
+ senderName: normalizedMessage.senderName,
7134
+ kind: normalizedMessage.kind,
7135
+ content: normalizedContent,
7136
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
7137
+ historyDedupKey: dedupKey
7138
+ }],
7139
+ this.cachedChat?.title || this.provider.name,
7140
+ this.instanceId,
7141
+ this.cachedChat?.id || this.instanceId
7142
+ );
7143
+ }
6782
7144
  }
6783
7145
  mergeConversationMessages(messages) {
6784
- if (this.runtimeMessages.length === 0) return messages;
6785
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
7146
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
7147
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6786
7148
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
6787
7149
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
6788
7150
  if (aTime !== bTime) return aTime - bTime;
6789
7151
  return a.index - b.index;
6790
- }).map((entry) => entry.message);
7152
+ }).map((entry) => entry.message));
6791
7153
  }
6792
7154
  getPersistedEffectContent(effect) {
6793
7155
  if (effect.type === "message") {
@@ -7690,219 +8052,6 @@ function reconcileIdeRuntimeSessions(instanceManager, sessionRegistry) {
7690
8052
  // src/commands/handler.ts
7691
8053
  init_logger();
7692
8054
 
7693
- // src/providers/io-contracts.ts
7694
- function normalizeInputEnvelope(input) {
7695
- const normalized = normalizeInputEnvelopePayload(input);
7696
- const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
7697
- return {
7698
- parts: normalized.parts,
7699
- textFallback,
7700
- ...normalized.metadata ? { metadata: normalized.metadata } : {}
7701
- };
7702
- }
7703
- function normalizeMessageParts(content) {
7704
- if (typeof content === "string") return [{ type: "text", text: content }];
7705
- if (!Array.isArray(content)) {
7706
- if (content && typeof content === "object" && typeof content.text === "string") {
7707
- return [{ type: "text", text: String(content.text) }];
7708
- }
7709
- return [];
7710
- }
7711
- const parts = [];
7712
- for (const raw of content) {
7713
- if (typeof raw === "string") {
7714
- parts.push({ type: "text", text: raw });
7715
- continue;
7716
- }
7717
- if (!raw || typeof raw !== "object") continue;
7718
- const part = normalizeMessagePartObject(raw);
7719
- if (part) parts.push(part);
7720
- }
7721
- return parts;
7722
- }
7723
- function flattenMessageParts(parts) {
7724
- return parts.map((part) => {
7725
- if (part.type === "text") return part.text;
7726
- if (part.type === "resource") return part.resource.text || "";
7727
- return "";
7728
- }).filter((value) => value.length > 0).join("\n");
7729
- }
7730
- function normalizeInputEnvelopePayload(input) {
7731
- if (typeof input === "string") {
7732
- return { parts: [{ type: "text", text: input }], textFallback: input };
7733
- }
7734
- if (!input || typeof input !== "object") {
7735
- return { parts: [], textFallback: "" };
7736
- }
7737
- const record = input;
7738
- const nestedInput = record.input;
7739
- if (nestedInput && typeof nestedInput === "object") {
7740
- const nested = nestedInput;
7741
- return {
7742
- parts: normalizeInputParts(nested.parts ?? nested.prompt),
7743
- textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
7744
- metadata: normalizeInputMetadata(nested.metadata)
7745
- };
7746
- }
7747
- const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
7748
- if (directText !== void 0) {
7749
- return { parts: [{ type: "text", text: directText }], textFallback: directText };
7750
- }
7751
- const directParts = normalizeInputParts(record.parts ?? record.prompt);
7752
- return {
7753
- parts: directParts,
7754
- textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
7755
- metadata: normalizeInputMetadata(record.metadata)
7756
- };
7757
- }
7758
- function normalizeInputMetadata(value) {
7759
- if (!value || typeof value !== "object") return void 0;
7760
- const record = value;
7761
- const metadata = {};
7762
- if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
7763
- metadata.source = record.source;
7764
- }
7765
- if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
7766
- metadata.clientTimestamp = record.clientTimestamp;
7767
- }
7768
- return Object.keys(metadata).length > 0 ? metadata : void 0;
7769
- }
7770
- function normalizeInputParts(value) {
7771
- if (!Array.isArray(value)) return [];
7772
- const parts = [];
7773
- for (const raw of value) {
7774
- if (typeof raw === "string") {
7775
- parts.push({ type: "text", text: raw });
7776
- continue;
7777
- }
7778
- if (!raw || typeof raw !== "object") continue;
7779
- const part = normalizeInputPartObject(raw);
7780
- if (part) parts.push(part);
7781
- }
7782
- return parts;
7783
- }
7784
- function normalizeInputPartObject(raw) {
7785
- const type = raw.type;
7786
- if (type === "text" && typeof raw.text === "string") {
7787
- return { type, text: raw.text };
7788
- }
7789
- if (type === "image" && typeof raw.mimeType === "string") {
7790
- return {
7791
- type,
7792
- mimeType: raw.mimeType,
7793
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7794
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7795
- ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
7796
- };
7797
- }
7798
- if (type === "audio" && typeof raw.mimeType === "string") {
7799
- return {
7800
- type,
7801
- mimeType: raw.mimeType,
7802
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7803
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7804
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
7805
- };
7806
- }
7807
- if (type === "video" && typeof raw.mimeType === "string") {
7808
- return {
7809
- type,
7810
- mimeType: raw.mimeType,
7811
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7812
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7813
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
7814
- };
7815
- }
7816
- if (type === "resource" && typeof raw.uri === "string") {
7817
- return {
7818
- type,
7819
- uri: raw.uri,
7820
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7821
- ...typeof raw.name === "string" ? { name: raw.name } : {},
7822
- ...typeof raw.text === "string" ? { text: raw.text } : {},
7823
- ...typeof raw.data === "string" ? { data: raw.data } : {}
7824
- };
7825
- }
7826
- if (type === "resource_link" && typeof raw.uri === "string") {
7827
- return {
7828
- type: "resource",
7829
- uri: raw.uri,
7830
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7831
- ...typeof raw.name === "string" ? { name: raw.name } : {}
7832
- };
7833
- }
7834
- return null;
7835
- }
7836
- function normalizeMessagePartObject(raw) {
7837
- const type = raw.type;
7838
- if (type === "text" && typeof raw.text === "string") {
7839
- return { type, text: raw.text };
7840
- }
7841
- if (type === "image" && typeof raw.mimeType === "string") {
7842
- return {
7843
- type,
7844
- mimeType: raw.mimeType,
7845
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7846
- ...typeof raw.data === "string" ? { data: raw.data } : {}
7847
- };
7848
- }
7849
- if (type === "audio" && typeof raw.mimeType === "string") {
7850
- return {
7851
- type,
7852
- mimeType: raw.mimeType,
7853
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7854
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7855
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
7856
- };
7857
- }
7858
- if (type === "video" && typeof raw.mimeType === "string") {
7859
- return {
7860
- type,
7861
- mimeType: raw.mimeType,
7862
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7863
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7864
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
7865
- };
7866
- }
7867
- if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
7868
- return {
7869
- type,
7870
- uri: raw.uri,
7871
- name: raw.name,
7872
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7873
- ...typeof raw.size === "number" ? { size: raw.size } : {}
7874
- };
7875
- }
7876
- if (type === "resource" && raw.resource && typeof raw.resource === "object") {
7877
- const resource = raw.resource;
7878
- if (typeof resource.uri !== "string") return null;
7879
- return {
7880
- type,
7881
- resource: {
7882
- uri: resource.uri,
7883
- ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
7884
- ...typeof resource.text === "string" ? { text: resource.text } : {},
7885
- ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
7886
- }
7887
- };
7888
- }
7889
- return null;
7890
- }
7891
- function flattenInputParts(parts) {
7892
- return parts.map((part) => {
7893
- if (part.type === "text") return part.text;
7894
- if (part.type === "audio") return part.transcript || "";
7895
- if (part.type === "resource") return part.text || "";
7896
- return "";
7897
- }).filter((value) => value.length > 0).join("\n");
7898
- }
7899
-
7900
- // src/providers/contracts.ts
7901
- function flattenContent(content) {
7902
- if (typeof content === "string") return content;
7903
- return flattenMessageParts(normalizeMessageParts(content));
7904
- }
7905
-
7906
8055
  // src/commands/chat-commands.ts
7907
8056
  init_logger();
7908
8057
 
@@ -8033,6 +8182,7 @@ function createInteractionId(prefix = "ix") {
8033
8182
  }
8034
8183
 
8035
8184
  // src/commands/chat-commands.ts
8185
+ init_chat_message_normalization();
8036
8186
  var RECENT_SEND_WINDOW_MS = 1200;
8037
8187
  var recentSendByTarget = /* @__PURE__ */ new Map();
8038
8188
  function hashSignatureParts(parts) {
@@ -8162,7 +8312,7 @@ function normalizeReadChatCursor(args) {
8162
8312
  }
8163
8313
  function normalizeReadChatMessages(payload) {
8164
8314
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
8165
- return messages;
8315
+ return normalizeChatMessages(messages);
8166
8316
  }
8167
8317
  function deriveHistoryDedupKey(message) {
8168
8318
  const unitKey = typeof message._unitKey === "string" ? message._unitKey.trim() : "";
@@ -10510,6 +10660,7 @@ var fs5 = __toESM(require("fs"));
10510
10660
  var import_node_module = require("module");
10511
10661
  init_provider_cli_adapter();
10512
10662
  init_logger();
10663
+ init_chat_message_normalization();
10513
10664
  var CachedDatabaseSync = null;
10514
10665
  function getDatabaseSync() {
10515
10666
  if (CachedDatabaseSync) return CachedDatabaseSync;
@@ -11004,8 +11155,8 @@ var CliProviderInstance = class {
11004
11155
  if (this.appliedEffectKeys.has(effectKey)) continue;
11005
11156
  this.appliedEffectKeys.add(effectKey);
11006
11157
  if (effect.persist !== false) {
11007
- const persisted = this.getPersistedEffectContent(effect);
11008
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
11158
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
11159
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
11009
11160
  }
11010
11161
  if (effect.type === "message" && effect.message) {
11011
11162
  const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
@@ -11129,44 +11280,53 @@ ${effect.notification.body || ""}`.trim();
11129
11280
  );
11130
11281
  }
11131
11282
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
11132
- const normalizedContent = String(content || "").trim();
11133
- if (!normalizedContent) return;
11283
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
11284
+ content,
11285
+ receivedAt,
11286
+ timestamp: receivedAt
11287
+ }), dedupKey);
11288
+ }
11289
+ appendRuntimeMessage(message, dedupKey) {
11290
+ const normalizedMessage = buildChatMessage({
11291
+ ...message,
11292
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
11293
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
11294
+ });
11295
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
11296
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
11134
11297
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
11135
11298
  this.runtimeMessages.push({
11136
11299
  key: dedupKey,
11137
- message: {
11138
- role: "system",
11139
- senderName: "System",
11140
- content: normalizedContent,
11141
- receivedAt,
11142
- timestamp: receivedAt
11143
- }
11300
+ message: normalizedMessage
11144
11301
  });
11145
11302
  if (this.runtimeMessages.length > 50) {
11146
11303
  this.runtimeMessages = this.runtimeMessages.slice(-50);
11147
11304
  }
11148
- this.historyWriter.appendNewMessages(
11149
- this.type,
11150
- [{
11151
- role: "system",
11152
- senderName: "System",
11153
- content: normalizedContent,
11154
- receivedAt,
11155
- historyDedupKey: dedupKey
11156
- }],
11157
- this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
11158
- this.instanceId,
11159
- this.providerSessionId
11160
- );
11305
+ if (normalizedContent) {
11306
+ this.historyWriter.appendNewMessages(
11307
+ this.type,
11308
+ [{
11309
+ role: normalizedMessage.role,
11310
+ senderName: normalizedMessage.senderName,
11311
+ kind: normalizedMessage.kind,
11312
+ content: normalizedContent,
11313
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
11314
+ historyDedupKey: dedupKey
11315
+ }],
11316
+ this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
11317
+ this.instanceId,
11318
+ this.providerSessionId
11319
+ );
11320
+ }
11161
11321
  }
11162
11322
  mergeConversationMessages(parsedMessages) {
11163
- if (this.runtimeMessages.length === 0) return parsedMessages;
11164
- return [...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
11323
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(parsedMessages);
11324
+ return normalizeChatMessages([...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
11165
11325
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
11166
11326
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
11167
11327
  if (aTime !== bTime) return aTime - bTime;
11168
11328
  return a.index - b.index;
11169
- }).map((entry) => entry.message);
11329
+ }).map((entry) => entry.message));
11170
11330
  }
11171
11331
  formatApprovalRequestMessage(modalMessage, buttons) {
11172
11332
  const lines = ["Approval requested"];
@@ -11237,6 +11397,7 @@ var path12 = __toESM(require("path"));
11237
11397
  var import_stream = require("stream");
11238
11398
  var import_child_process5 = require("child_process");
11239
11399
  var import_sdk = require("@agentclientprotocol/sdk");
11400
+ init_chat_message_normalization();
11240
11401
  init_logger();
11241
11402
  function getPromptCapabilityFlags(agentCapabilities) {
11242
11403
  const prompt = agentCapabilities?.promptCapabilities || {};
@@ -11402,24 +11563,21 @@ var AcpProviderInstance = class {
11402
11563
  }
11403
11564
  getState() {
11404
11565
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
11405
- const recentMessages = this.messages.slice(-50).map((m) => {
11566
+ const recentMessages = normalizeChatMessages(this.messages.slice(-50).map((m) => {
11406
11567
  const content = this.truncateContent(m.content);
11407
- return {
11408
- role: m.role,
11409
- content,
11410
- timestamp: m.timestamp,
11411
- toolCalls: m.toolCalls
11412
- };
11413
- });
11568
+ return buildChatMessage({
11569
+ ...m,
11570
+ content
11571
+ });
11572
+ }));
11414
11573
  if (this.currentStatus === "generating" && (this.partialContent || this.partialBlocks.length > 0)) {
11415
11574
  const blocks = this.buildPartialBlocks();
11416
11575
  if (blocks.length > 0) {
11417
- recentMessages.push({
11418
- role: "assistant",
11576
+ recentMessages.push(buildAssistantChatMessage({
11419
11577
  content: blocks,
11420
11578
  timestamp: Date.now(),
11421
11579
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
11422
- });
11580
+ }));
11423
11581
  }
11424
11582
  }
11425
11583
  return {
@@ -11432,7 +11590,7 @@ var AcpProviderInstance = class {
11432
11590
  id: this.sessionId || `${this.type}_${this.workingDir}`,
11433
11591
  title: `${this.provider.name} \xB7 ${dirName}`,
11434
11592
  status: this.currentStatus,
11435
- messages: recentMessages,
11593
+ messages: normalizeChatMessages(recentMessages),
11436
11594
  activeModal: this.currentStatus === "waiting_approval" ? {
11437
11595
  message: this.activeToolCalls.find((t) => t.status === "running")?.name || "Permission requested",
11438
11596
  buttons: ["Approve", "Reject"]
@@ -11964,11 +12122,10 @@ var AcpProviderInstance = class {
11964
12122
  if (b.type === "resource") return { type: "resource", resource: b.resource };
11965
12123
  return { type: "text", text: flattenContent([b]) };
11966
12124
  }) : [{ type: "text", text }];
11967
- this.messages.push({
11968
- role: "user",
12125
+ this.messages.push(buildUserChatMessage({
11969
12126
  content: contentBlocks && contentBlocks.length > 0 ? contentBlocks : text,
11970
12127
  timestamp: Date.now()
11971
- });
12128
+ }));
11972
12129
  this.currentStatus = "generating";
11973
12130
  this.partialContent = "";
11974
12131
  this.partialBlocks = [];
@@ -12138,11 +12295,11 @@ var AcpProviderInstance = class {
12138
12295
  content = m.content.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
12139
12296
  }
12140
12297
  if (content.trim()) {
12141
- this.messages.push({
12298
+ this.messages.push(buildChatMessage({
12142
12299
  role: m.role || "assistant",
12143
12300
  content: content.trim(),
12144
12301
  timestamp: Date.now()
12145
- });
12302
+ }));
12146
12303
  this.partialContent = "";
12147
12304
  }
12148
12305
  }
@@ -12218,12 +12375,11 @@ var AcpProviderInstance = class {
12218
12375
  return b;
12219
12376
  }).filter((b) => b.type !== "text" || b.type === "text" && b.text.trim());
12220
12377
  if (finalBlocks.length > 0) {
12221
- this.messages.push({
12222
- role: "assistant",
12378
+ this.messages.push(buildAssistantChatMessage({
12223
12379
  content: finalBlocks.length === 1 && finalBlocks[0].type === "text" ? finalBlocks[0].text : finalBlocks,
12224
12380
  timestamp: Date.now(),
12225
12381
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
12226
- });
12382
+ }));
12227
12383
  }
12228
12384
  this.partialContent = "";
12229
12385
  this.partialBlocks = [];
@@ -12283,11 +12439,10 @@ var AcpProviderInstance = class {
12283
12439
  appendSystemMessage(content, timestamp = Date.now()) {
12284
12440
  const normalizedContent = String(content || "").trim();
12285
12441
  if (!normalizedContent) return;
12286
- this.messages.push({
12287
- role: "system",
12442
+ this.messages.push(buildRuntimeSystemChatMessage({
12288
12443
  content: normalizedContent,
12289
12444
  timestamp
12290
- });
12445
+ }));
12291
12446
  if (this.messages.length > 200) {
12292
12447
  this.messages = this.messages.slice(-100);
12293
12448
  }
@@ -16958,6 +17113,7 @@ var DaemonAgentStreamManager = class {
16958
17113
 
16959
17114
  // src/agent-stream/poller.ts
16960
17115
  init_logger();
17116
+ init_chat_message_normalization();
16961
17117
  var AgentStreamPoller = class {
16962
17118
  deps;
16963
17119
  timer = null;
@@ -17113,12 +17269,9 @@ var AgentStreamPoller = class {
17113
17269
  type: "message",
17114
17270
  id: effectId,
17115
17271
  persist: true,
17116
- message: {
17117
- role: "system",
17118
- senderName: "System",
17119
- kind: "system",
17272
+ message: buildRuntimeSystemChatMessage({
17120
17273
  content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
17121
- }
17274
+ })
17122
17275
  }
17123
17276
  ]
17124
17277
  };
@@ -17365,6 +17518,9 @@ var ProviderInstanceManager = class {
17365
17518
  }
17366
17519
  };
17367
17520
 
17521
+ // src/index.ts
17522
+ init_chat_message_normalization();
17523
+
17368
17524
  // src/providers/version-archive.ts
17369
17525
  var fs10 = __toESM(require("fs"));
17370
17526
  var path18 = __toESM(require("path"));
@@ -23640,6 +23796,7 @@ async function shutdownDaemonComponents(components) {
23640
23796
  0 && (module.exports = {
23641
23797
  AcpProviderInstance,
23642
23798
  AgentStreamPoller,
23799
+ BUILTIN_CHAT_MESSAGE_KINDS,
23643
23800
  CdpDomHandlers,
23644
23801
  CliProviderInstance,
23645
23802
  DAEMON_WS_PATH,
@@ -23664,9 +23821,14 @@ async function shutdownDaemonComponents(components) {
23664
23821
  SessionHostPtyTransportFactory,
23665
23822
  VersionArchive,
23666
23823
  appendRecentActivity,
23824
+ buildAssistantChatMessage,
23825
+ buildChatMessage,
23667
23826
  buildMachineInfo,
23827
+ buildRuntimeSystemChatMessage,
23668
23828
  buildSessionEntries,
23669
23829
  buildStatusSnapshot,
23830
+ buildSystemChatMessage,
23831
+ buildUserChatMessage,
23670
23832
  clearDebugTrace,
23671
23833
  configureDebugTraceStore,
23672
23834
  connectCdpManager,
@@ -23698,6 +23860,7 @@ async function shutdownDaemonComponents(components) {
23698
23860
  initDaemonComponents,
23699
23861
  installExtensions,
23700
23862
  installGlobalInterceptor,
23863
+ isBuiltinChatMessageKind,
23701
23864
  isCdpConnected,
23702
23865
  isExtensionInstalled,
23703
23866
  isIdeRunning,
@@ -23716,6 +23879,9 @@ async function shutdownDaemonComponents(components) {
23716
23879
  markSetupComplete,
23717
23880
  maybeRunDaemonUpgradeHelperFromEnv,
23718
23881
  normalizeActiveChatData,
23882
+ normalizeChatMessage,
23883
+ normalizeChatMessageKind,
23884
+ normalizeChatMessages,
23719
23885
  normalizeInputEnvelope,
23720
23886
  normalizeManagedStatus,
23721
23887
  normalizeMessageParts,