@adhdev/daemon-core 0.8.60 → 0.8.62

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.
Files changed (34) hide show
  1. package/dist/cli-adapters/provider-cli-adapter.d.ts +1 -0
  2. package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
  3. package/dist/cli-adapters/provider-cli-shared.d.ts +5 -2
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +521 -343
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +511 -343
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/providers/chat-message-normalization.d.ts +43 -0
  10. package/dist/providers/cli-provider-instance.d.ts +1 -0
  11. package/dist/providers/contracts.d.ts +5 -1
  12. package/dist/providers/control-effects.d.ts +2 -0
  13. package/dist/providers/extension-provider-instance.d.ts +2 -1
  14. package/dist/providers/ide-provider-instance.d.ts +2 -1
  15. package/dist/types.d.ts +2 -1
  16. package/node_modules/@adhdev/session-host-core/package.json +2 -2
  17. package/package.json +2 -2
  18. package/src/agent-stream/poller.ts +3 -5
  19. package/src/cli-adapters/provider-cli-adapter.ts +32 -10
  20. package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
  21. package/src/cli-adapters/provider-cli-parse.ts +3 -0
  22. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  23. package/src/cli-adapters/provider-cli-shared.ts +5 -2
  24. package/src/commands/chat-commands.ts +2 -1
  25. package/src/config/chat-history.ts +6 -5
  26. package/src/index.ts +13 -0
  27. package/src/providers/acp-provider-instance.ts +19 -24
  28. package/src/providers/chat-message-normalization.ts +68 -0
  29. package/src/providers/cli-provider-instance.ts +42 -29
  30. package/src/providers/contracts.ts +5 -1
  31. package/src/providers/control-effects.ts +72 -0
  32. package/src/providers/extension-provider-instance.ts +43 -31
  33. package/src/providers/ide-provider-instance.ts +42 -30
  34. package/src/types.ts +2 -1
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;
@@ -1108,6 +1166,7 @@ function buildCliParseInput(options) {
1108
1166
  terminalScreenText,
1109
1167
  baseMessages,
1110
1168
  partialResponse,
1169
+ isWaitingForResponse,
1111
1170
  scope,
1112
1171
  runtimeSettings
1113
1172
  } = options;
@@ -1125,6 +1184,7 @@ function buildCliParseInput(options) {
1125
1184
  recentScreen: buildCliScreenSnapshot(recentBuffer),
1126
1185
  messages: [...baseMessages],
1127
1186
  partialResponse,
1187
+ isWaitingForResponse,
1128
1188
  promptText: scope?.prompt || "",
1129
1189
  settings: { ...runtimeSettings }
1130
1190
  };
@@ -1310,6 +1370,7 @@ var init_provider_cli_adapter = __esm({
1310
1370
  init_terminal_screen();
1311
1371
  init_pty_transport();
1312
1372
  init_provider_cli_shared();
1373
+ init_chat_message_normalization();
1313
1374
  init_provider_cli_parse();
1314
1375
  init_provider_cli_config();
1315
1376
  init_provider_cli_runtime();
@@ -1862,6 +1923,18 @@ var init_provider_cli_adapter = __esm({
1862
1923
  const holdMs = this.getStatusActivityHoldMs();
1863
1924
  return quietForMs < holdMs || screenStableMs < holdMs;
1864
1925
  }
1926
+ shouldDeferIdleTimeoutFinish() {
1927
+ if (!this.isWaitingForResponse || this.currentStatus === "waiting_approval") {
1928
+ return false;
1929
+ }
1930
+ const latestStatus = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
1931
+ if (latestStatus === "generating") {
1932
+ this.settledBuffer = this.recentOutputBuffer;
1933
+ this.evaluateSettled();
1934
+ return true;
1935
+ }
1936
+ return false;
1937
+ }
1865
1938
  getStartupConfirmationModal(screenText) {
1866
1939
  const text = sanitizeTerminalText(String(screenText || ""));
1867
1940
  if (!text.trim()) return null;
@@ -2020,6 +2093,7 @@ var init_provider_cli_adapter = __esm({
2020
2093
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
2021
2094
  this.idleTimeout = setTimeout(() => {
2022
2095
  if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
2096
+ if (this.shouldDeferIdleTimeoutFinish()) return;
2023
2097
  this.finishResponse();
2024
2098
  }
2025
2099
  }, this.timeouts.generatingIdle);
@@ -2055,6 +2129,7 @@ var init_provider_cli_adapter = __esm({
2055
2129
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
2056
2130
  this.idleTimeout = setTimeout(() => {
2057
2131
  if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
2132
+ if (this.shouldDeferIdleTimeoutFinish()) return;
2058
2133
  this.finishResponse();
2059
2134
  }
2060
2135
  }, this.timeouts.generatingIdle);
@@ -2097,7 +2172,10 @@ var init_provider_cli_adapter = __esm({
2097
2172
  this.setStatus("generating", "script_detect");
2098
2173
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
2099
2174
  this.idleTimeout = setTimeout(() => {
2100
- if (this.isWaitingForResponse) this.finishResponse();
2175
+ if (this.isWaitingForResponse) {
2176
+ if (this.shouldDeferIdleTimeoutFinish()) return;
2177
+ this.finishResponse();
2178
+ }
2101
2179
  }, this.timeouts.generatingIdle);
2102
2180
  this.onStatusChange?.();
2103
2181
  return;
@@ -2165,6 +2243,7 @@ var init_provider_cli_adapter = __esm({
2165
2243
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
2166
2244
  this.idleTimeout = setTimeout(() => {
2167
2245
  if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
2246
+ if (this.shouldDeferIdleTimeoutFinish()) return;
2168
2247
  this.clearIdleFinishCandidate("idle_timeout_finish");
2169
2248
  this.finishResponse();
2170
2249
  }
@@ -2303,6 +2382,7 @@ var init_provider_cli_adapter = __esm({
2303
2382
  tail: text.slice(-500),
2304
2383
  screenText,
2305
2384
  rawBuffer: this.accumulatedRawBuffer,
2385
+ isWaitingForResponse: this.isWaitingForResponse,
2306
2386
  screen: buildCliScreenSnapshot(screenText),
2307
2387
  tailScreen: buildCliScreenSnapshot(text.slice(-500))
2308
2388
  });
@@ -2367,11 +2447,10 @@ var init_provider_cli_adapter = __esm({
2367
2447
  );
2368
2448
  const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
2369
2449
  if (parsed && Array.isArray(parsed.messages)) {
2370
- const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
2450
+ const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => buildChatMessage({
2371
2451
  ...message,
2372
2452
  id: message.id || `msg_${index}`,
2373
2453
  index: typeof message.index === "number" ? message.index : index,
2374
- kind: message.kind || "standard",
2375
2454
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2376
2455
  })) : hydrateCliParsedMessages(parsed.messages, {
2377
2456
  committedMessages: this.committedMessages,
@@ -2392,13 +2471,11 @@ var init_provider_cli_adapter = __esm({
2392
2471
  id: "cli_session",
2393
2472
  status: this.currentStatus,
2394
2473
  title: this.cliName,
2395
- messages: messages.slice(-50).map((message, index) => ({
2396
- id: `msg_${index}`,
2397
- role: message.role,
2398
- content: message.content,
2399
- timestamp: message.timestamp,
2400
- index,
2401
- kind: "standard"
2474
+ messages: messages.slice(-50).map((message, index) => buildChatMessage({
2475
+ ...message,
2476
+ id: message.id || `msg_${index}`,
2477
+ index: typeof message.index === "number" ? message.index : index,
2478
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2402
2479
  })),
2403
2480
  activeModal: this.activeModal
2404
2481
  };
@@ -2415,6 +2492,7 @@ var init_provider_cli_adapter = __esm({
2415
2492
  terminalScreenText: this.terminalScreen.getText(),
2416
2493
  baseMessages: this.committedMessages,
2417
2494
  partialResponse: this.responseBuffer,
2495
+ isWaitingForResponse: this.isWaitingForResponse,
2418
2496
  scope: this.currentTurnScope,
2419
2497
  runtimeSettings: this.runtimeSettings
2420
2498
  });
@@ -2433,6 +2511,7 @@ var init_provider_cli_adapter = __esm({
2433
2511
  terminalScreenText: this.terminalScreen.getText(),
2434
2512
  baseMessages,
2435
2513
  partialResponse,
2514
+ isWaitingForResponse: this.isWaitingForResponse,
2436
2515
  scope,
2437
2516
  runtimeSettings: this.runtimeSettings
2438
2517
  });
@@ -3023,6 +3102,7 @@ var index_exports = {};
3023
3102
  __export(index_exports, {
3024
3103
  AcpProviderInstance: () => AcpProviderInstance,
3025
3104
  AgentStreamPoller: () => AgentStreamPoller,
3105
+ BUILTIN_CHAT_MESSAGE_KINDS: () => BUILTIN_CHAT_MESSAGE_KINDS,
3026
3106
  CdpDomHandlers: () => CdpDomHandlers,
3027
3107
  CliProviderInstance: () => CliProviderInstance,
3028
3108
  DAEMON_WS_PATH: () => DAEMON_WS_PATH,
@@ -3047,9 +3127,14 @@ __export(index_exports, {
3047
3127
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
3048
3128
  VersionArchive: () => VersionArchive,
3049
3129
  appendRecentActivity: () => appendRecentActivity,
3130
+ buildAssistantChatMessage: () => buildAssistantChatMessage,
3131
+ buildChatMessage: () => buildChatMessage,
3050
3132
  buildMachineInfo: () => buildMachineInfo,
3133
+ buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
3051
3134
  buildSessionEntries: () => buildSessionEntries,
3052
3135
  buildStatusSnapshot: () => buildStatusSnapshot,
3136
+ buildSystemChatMessage: () => buildSystemChatMessage,
3137
+ buildUserChatMessage: () => buildUserChatMessage,
3053
3138
  clearDebugTrace: () => clearDebugTrace,
3054
3139
  configureDebugTraceStore: () => configureDebugTraceStore,
3055
3140
  connectCdpManager: () => connectCdpManager,
@@ -3081,6 +3166,7 @@ __export(index_exports, {
3081
3166
  initDaemonComponents: () => initDaemonComponents,
3082
3167
  installExtensions: () => installExtensions,
3083
3168
  installGlobalInterceptor: () => installGlobalInterceptor,
3169
+ isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
3084
3170
  isCdpConnected: () => isCdpConnected,
3085
3171
  isExtensionInstalled: () => isExtensionInstalled,
3086
3172
  isIdeRunning: () => isIdeRunning,
@@ -3099,6 +3185,9 @@ __export(index_exports, {
3099
3185
  markSetupComplete: () => markSetupComplete,
3100
3186
  maybeRunDaemonUpgradeHelperFromEnv: () => maybeRunDaemonUpgradeHelperFromEnv,
3101
3187
  normalizeActiveChatData: () => normalizeActiveChatData,
3188
+ normalizeChatMessage: () => normalizeChatMessage,
3189
+ normalizeChatMessageKind: () => normalizeChatMessageKind,
3190
+ normalizeChatMessages: () => normalizeChatMessages,
3102
3191
  normalizeInputEnvelope: () => normalizeInputEnvelope,
3103
3192
  normalizeManagedStatus: () => normalizeManagedStatus,
3104
3193
  normalizeMessageParts: () => normalizeMessageParts,
@@ -5105,6 +5194,219 @@ var CdpDomHandlers = class {
5105
5194
  // src/providers/ide-provider-instance.ts
5106
5195
  var crypto2 = __toESM(require("crypto"));
5107
5196
 
5197
+ // src/providers/io-contracts.ts
5198
+ function normalizeInputEnvelope(input) {
5199
+ const normalized = normalizeInputEnvelopePayload(input);
5200
+ const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
5201
+ return {
5202
+ parts: normalized.parts,
5203
+ textFallback,
5204
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
5205
+ };
5206
+ }
5207
+ function normalizeMessageParts(content) {
5208
+ if (typeof content === "string") return [{ type: "text", text: content }];
5209
+ if (!Array.isArray(content)) {
5210
+ if (content && typeof content === "object" && typeof content.text === "string") {
5211
+ return [{ type: "text", text: String(content.text) }];
5212
+ }
5213
+ return [];
5214
+ }
5215
+ const parts = [];
5216
+ for (const raw of content) {
5217
+ if (typeof raw === "string") {
5218
+ parts.push({ type: "text", text: raw });
5219
+ continue;
5220
+ }
5221
+ if (!raw || typeof raw !== "object") continue;
5222
+ const part = normalizeMessagePartObject(raw);
5223
+ if (part) parts.push(part);
5224
+ }
5225
+ return parts;
5226
+ }
5227
+ function flattenMessageParts(parts) {
5228
+ return parts.map((part) => {
5229
+ if (part.type === "text") return part.text;
5230
+ if (part.type === "resource") return part.resource.text || "";
5231
+ return "";
5232
+ }).filter((value) => value.length > 0).join("\n");
5233
+ }
5234
+ function normalizeInputEnvelopePayload(input) {
5235
+ if (typeof input === "string") {
5236
+ return { parts: [{ type: "text", text: input }], textFallback: input };
5237
+ }
5238
+ if (!input || typeof input !== "object") {
5239
+ return { parts: [], textFallback: "" };
5240
+ }
5241
+ const record = input;
5242
+ const nestedInput = record.input;
5243
+ if (nestedInput && typeof nestedInput === "object") {
5244
+ const nested = nestedInput;
5245
+ return {
5246
+ parts: normalizeInputParts(nested.parts ?? nested.prompt),
5247
+ textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
5248
+ metadata: normalizeInputMetadata(nested.metadata)
5249
+ };
5250
+ }
5251
+ const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
5252
+ if (directText !== void 0) {
5253
+ return { parts: [{ type: "text", text: directText }], textFallback: directText };
5254
+ }
5255
+ const directParts = normalizeInputParts(record.parts ?? record.prompt);
5256
+ return {
5257
+ parts: directParts,
5258
+ textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
5259
+ metadata: normalizeInputMetadata(record.metadata)
5260
+ };
5261
+ }
5262
+ function normalizeInputMetadata(value) {
5263
+ if (!value || typeof value !== "object") return void 0;
5264
+ const record = value;
5265
+ const metadata = {};
5266
+ if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
5267
+ metadata.source = record.source;
5268
+ }
5269
+ if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
5270
+ metadata.clientTimestamp = record.clientTimestamp;
5271
+ }
5272
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
5273
+ }
5274
+ function normalizeInputParts(value) {
5275
+ if (!Array.isArray(value)) return [];
5276
+ const parts = [];
5277
+ for (const raw of value) {
5278
+ if (typeof raw === "string") {
5279
+ parts.push({ type: "text", text: raw });
5280
+ continue;
5281
+ }
5282
+ if (!raw || typeof raw !== "object") continue;
5283
+ const part = normalizeInputPartObject(raw);
5284
+ if (part) parts.push(part);
5285
+ }
5286
+ return parts;
5287
+ }
5288
+ function normalizeInputPartObject(raw) {
5289
+ const type = raw.type;
5290
+ if (type === "text" && typeof raw.text === "string") {
5291
+ return { type, text: raw.text };
5292
+ }
5293
+ if (type === "image" && typeof raw.mimeType === "string") {
5294
+ return {
5295
+ type,
5296
+ mimeType: raw.mimeType,
5297
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5298
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5299
+ ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
5300
+ };
5301
+ }
5302
+ if (type === "audio" && typeof raw.mimeType === "string") {
5303
+ return {
5304
+ type,
5305
+ mimeType: raw.mimeType,
5306
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5307
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5308
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5309
+ };
5310
+ }
5311
+ if (type === "video" && typeof raw.mimeType === "string") {
5312
+ return {
5313
+ type,
5314
+ mimeType: raw.mimeType,
5315
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5316
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5317
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5318
+ };
5319
+ }
5320
+ if (type === "resource" && typeof raw.uri === "string") {
5321
+ return {
5322
+ type,
5323
+ uri: raw.uri,
5324
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5325
+ ...typeof raw.name === "string" ? { name: raw.name } : {},
5326
+ ...typeof raw.text === "string" ? { text: raw.text } : {},
5327
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
5328
+ };
5329
+ }
5330
+ if (type === "resource_link" && typeof raw.uri === "string") {
5331
+ return {
5332
+ type: "resource",
5333
+ uri: raw.uri,
5334
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5335
+ ...typeof raw.name === "string" ? { name: raw.name } : {}
5336
+ };
5337
+ }
5338
+ return null;
5339
+ }
5340
+ function normalizeMessagePartObject(raw) {
5341
+ const type = raw.type;
5342
+ if (type === "text" && typeof raw.text === "string") {
5343
+ return { type, text: raw.text };
5344
+ }
5345
+ if (type === "image" && typeof raw.mimeType === "string") {
5346
+ return {
5347
+ type,
5348
+ mimeType: raw.mimeType,
5349
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5350
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
5351
+ };
5352
+ }
5353
+ if (type === "audio" && typeof raw.mimeType === "string") {
5354
+ return {
5355
+ type,
5356
+ mimeType: raw.mimeType,
5357
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5358
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5359
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5360
+ };
5361
+ }
5362
+ if (type === "video" && typeof raw.mimeType === "string") {
5363
+ return {
5364
+ type,
5365
+ mimeType: raw.mimeType,
5366
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5367
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
5368
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5369
+ };
5370
+ }
5371
+ if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
5372
+ return {
5373
+ type,
5374
+ uri: raw.uri,
5375
+ name: raw.name,
5376
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5377
+ ...typeof raw.size === "number" ? { size: raw.size } : {}
5378
+ };
5379
+ }
5380
+ if (type === "resource" && raw.resource && typeof raw.resource === "object") {
5381
+ const resource = raw.resource;
5382
+ if (typeof resource.uri !== "string") return null;
5383
+ return {
5384
+ type,
5385
+ resource: {
5386
+ uri: resource.uri,
5387
+ ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
5388
+ ...typeof resource.text === "string" ? { text: resource.text } : {},
5389
+ ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
5390
+ }
5391
+ };
5392
+ }
5393
+ return null;
5394
+ }
5395
+ function flattenInputParts(parts) {
5396
+ return parts.map((part) => {
5397
+ if (part.type === "text") return part.text;
5398
+ if (part.type === "audio") return part.transcript || "";
5399
+ if (part.type === "resource") return part.text || "";
5400
+ return "";
5401
+ }).filter((value) => value.length > 0).join("\n");
5402
+ }
5403
+
5404
+ // src/providers/contracts.ts
5405
+ function flattenContent(content) {
5406
+ if (typeof content === "string") return content;
5407
+ return flattenMessageParts(normalizeMessageParts(content));
5408
+ }
5409
+
5108
5410
  // src/providers/status-monitor.ts
5109
5411
  var DEFAULT_MONITOR_CONFIG = {
5110
5412
  approvalAlert: true,
@@ -5218,6 +5520,7 @@ var StatusMonitor = class {
5218
5520
  };
5219
5521
 
5220
5522
  // src/providers/control-effects.ts
5523
+ init_chat_message_normalization();
5221
5524
  function extractProviderControlValues(controls, data) {
5222
5525
  if (!data || typeof data !== "object") return void 0;
5223
5526
  const values = {};
@@ -5285,13 +5588,58 @@ function normalizeProviderEffects(data) {
5285
5588
  level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
5286
5589
  channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
5287
5590
  preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
5288
- bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
5591
+ bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0,
5592
+ bubbleKind: typeof raw.notification.bubbleKind === "string" ? raw.notification.bubbleKind : void 0,
5593
+ bubbleRole: raw.notification.bubbleRole === "assistant" || raw.notification.bubbleRole === "user" ? raw.notification.bubbleRole : raw.notification.bubbleRole === "system" ? "system" : void 0,
5594
+ bubbleSenderName: typeof raw.notification.bubbleSenderName === "string" ? raw.notification.bubbleSenderName : void 0
5289
5595
  }
5290
5596
  });
5291
5597
  }
5292
5598
  }
5293
5599
  return effects;
5294
5600
  }
5601
+ function buildPersistedProviderEffectMessage(effect) {
5602
+ if (!effect) return null;
5603
+ if (effect.type === "message" && effect.message) {
5604
+ const role = effect.message.role === "assistant" || effect.message.role === "user" ? effect.message.role : "system";
5605
+ if (role === "system") {
5606
+ return buildRuntimeSystemChatMessage({
5607
+ content: effect.message.content,
5608
+ kind: effect.message.kind,
5609
+ senderName: effect.message.senderName
5610
+ });
5611
+ }
5612
+ return buildChatMessage({
5613
+ role,
5614
+ content: effect.message.content,
5615
+ kind: effect.message.kind,
5616
+ senderName: effect.message.senderName
5617
+ });
5618
+ }
5619
+ if (effect.type === "notification" && effect.notification) {
5620
+ const bubbleContent = effect.notification.bubbleContent ?? formatNotificationBubbleFallback(effect.notification.title, effect.notification.body);
5621
+ const flattened = typeof bubbleContent === "string" ? bubbleContent.trim() : flattenContent(bubbleContent).trim();
5622
+ if (!flattened && (!Array.isArray(bubbleContent) || bubbleContent.length === 0)) return null;
5623
+ const role = effect.notification.bubbleRole === "assistant" || effect.notification.bubbleRole === "user" ? effect.notification.bubbleRole : "system";
5624
+ if (role === "system") {
5625
+ return buildRuntimeSystemChatMessage({
5626
+ content: bubbleContent,
5627
+ kind: effect.notification.bubbleKind,
5628
+ senderName: effect.notification.bubbleSenderName
5629
+ });
5630
+ }
5631
+ return buildChatMessage({
5632
+ role,
5633
+ content: bubbleContent,
5634
+ kind: effect.notification.bubbleKind,
5635
+ senderName: effect.notification.bubbleSenderName
5636
+ });
5637
+ }
5638
+ if (effect.type === "toast" && effect.toast?.message) {
5639
+ return buildRuntimeSystemChatMessage({ content: effect.toast.message });
5640
+ }
5641
+ return null;
5642
+ }
5295
5643
  function normalizeControlListResult(data) {
5296
5644
  if (data && typeof data === "object" && Array.isArray(data.options)) {
5297
5645
  return {
@@ -5358,11 +5706,19 @@ function normalizeControlValue(value) {
5358
5706
  }
5359
5707
  return String(value);
5360
5708
  }
5709
+ function formatNotificationBubbleFallback(title, body) {
5710
+ const cleanTitle = typeof title === "string" ? title.trim() : "";
5711
+ const cleanBody = String(body || "").trim();
5712
+ if (cleanTitle && cleanBody) return `${cleanTitle}
5713
+ ${cleanBody}`;
5714
+ return cleanTitle || cleanBody;
5715
+ }
5361
5716
 
5362
5717
  // src/config/chat-history.ts
5363
5718
  var fs3 = __toESM(require("fs"));
5364
5719
  var path7 = __toESM(require("path"));
5365
5720
  var os5 = __toESM(require("os"));
5721
+ init_chat_message_normalization();
5366
5722
  var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
5367
5723
  var RETAIN_DAYS = 30;
5368
5724
  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;
@@ -5568,11 +5924,11 @@ var ChatHistoryWriter = class {
5568
5924
  this.appendNewMessages(
5569
5925
  agentType,
5570
5926
  [{
5571
- role: "system",
5572
- kind: "system",
5573
- content,
5574
- receivedAt: options.receivedAt,
5575
- senderName: options.senderName,
5927
+ ...buildRuntimeSystemChatMessage({
5928
+ content,
5929
+ receivedAt: options.receivedAt,
5930
+ senderName: options.senderName
5931
+ }),
5576
5932
  historyDedupKey: options.dedupKey
5577
5933
  }],
5578
5934
  options.sessionTitle,
@@ -5927,6 +6283,7 @@ function resolveProviderStateSurface(params) {
5927
6283
  }
5928
6284
 
5929
6285
  // src/providers/extension-provider-instance.ts
6286
+ init_chat_message_normalization();
5930
6287
  var ExtensionProviderInstance = class {
5931
6288
  type;
5932
6289
  category = "extension";
@@ -6133,8 +6490,8 @@ var ExtensionProviderInstance = class {
6133
6490
  if (this.appliedEffectKeys.has(effectKey)) continue;
6134
6491
  this.appliedEffectKeys.add(effectKey);
6135
6492
  if (effect.persist !== false) {
6136
- const persisted = this.getPersistedEffectContent(effect);
6137
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
6493
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
6494
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
6138
6495
  }
6139
6496
  if (effect.type === "message" && effect.message) {
6140
6497
  this.pushEvent({
@@ -6169,34 +6526,42 @@ var ExtensionProviderInstance = class {
6169
6526
  }
6170
6527
  }
6171
6528
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
6172
- const normalizedContent = String(content || "").trim();
6173
- if (!normalizedContent) return;
6529
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
6530
+ content,
6531
+ receivedAt,
6532
+ timestamp: receivedAt
6533
+ }), dedupKey);
6534
+ }
6535
+ appendRuntimeMessage(message, dedupKey) {
6536
+ const normalizedMessage = buildChatMessage({
6537
+ ...message,
6538
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
6539
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
6540
+ });
6541
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
6542
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
6174
6543
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
6175
6544
  this.runtimeMessages.push({
6176
6545
  key: dedupKey,
6177
- message: {
6178
- role: "system",
6179
- senderName: "System",
6180
- content: normalizedContent,
6181
- receivedAt,
6182
- timestamp: receivedAt
6183
- }
6546
+ message: normalizedMessage
6184
6547
  });
6185
6548
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
6186
- this.historyWriter.appendNewMessages(
6187
- this.type,
6188
- [{
6189
- role: "system",
6190
- senderName: "System",
6191
- content: normalizedContent,
6192
- kind: "system",
6193
- receivedAt,
6194
- historyDedupKey: dedupKey
6195
- }],
6196
- this.chatTitle || this.agentName || this.provider.name,
6197
- this.instanceId,
6198
- this.chatId || this.instanceId
6199
- );
6549
+ if (normalizedContent) {
6550
+ this.historyWriter.appendNewMessages(
6551
+ this.type,
6552
+ [{
6553
+ role: normalizedMessage.role,
6554
+ senderName: normalizedMessage.senderName,
6555
+ kind: normalizedMessage.kind,
6556
+ content: normalizedContent,
6557
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
6558
+ historyDedupKey: dedupKey
6559
+ }],
6560
+ this.chatTitle || this.agentName || this.provider.name,
6561
+ this.instanceId,
6562
+ this.chatId || this.instanceId
6563
+ );
6564
+ }
6200
6565
  }
6201
6566
  /**
6202
6567
  * Assign stable receivedAt to extension messages.
@@ -6213,16 +6578,16 @@ var ExtensionProviderInstance = class {
6213
6578
  nextHashes.set(hash, msg.receivedAt);
6214
6579
  }
6215
6580
  this.prevMessageHashes = nextHashes;
6216
- return messages;
6581
+ return normalizeChatMessages(messages);
6217
6582
  }
6218
6583
  mergeConversationMessages(messages) {
6219
- if (this.runtimeMessages.length === 0) return messages;
6220
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6584
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
6585
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6221
6586
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
6222
6587
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
6223
6588
  if (aTime !== bTime) return aTime - bTime;
6224
6589
  return a.index - b.index;
6225
- }).map((entry) => entry.message);
6590
+ }).map((entry) => entry.message));
6226
6591
  }
6227
6592
  getPersistedEffectContent(effect) {
6228
6593
  if (effect.type === "message") {
@@ -6336,6 +6701,7 @@ function formatAutoApprovalMessage(modalMessage, buttonLabel) {
6336
6701
  }
6337
6702
 
6338
6703
  // src/providers/ide-provider-instance.ts
6704
+ init_chat_message_normalization();
6339
6705
  var IdeProviderInstance = class {
6340
6706
  type;
6341
6707
  category = "ide";
@@ -6716,8 +7082,8 @@ var IdeProviderInstance = class {
6716
7082
  if (this.appliedEffectKeys.has(effectKey)) continue;
6717
7083
  this.appliedEffectKeys.add(effectKey);
6718
7084
  if (effect.persist !== false) {
6719
- const persisted = this.getPersistedEffectContent(effect);
6720
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
7085
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
7086
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
6721
7087
  }
6722
7088
  if (effect.type === "message" && effect.message) {
6723
7089
  this.pushEvent({
@@ -6752,8 +7118,20 @@ var IdeProviderInstance = class {
6752
7118
  }
6753
7119
  }
6754
7120
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
6755
- const normalizedContent = String(content || "").trim();
6756
- if (!normalizedContent) return;
7121
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
7122
+ content,
7123
+ receivedAt,
7124
+ timestamp: receivedAt
7125
+ }), dedupKey);
7126
+ }
7127
+ appendRuntimeMessage(message, dedupKey) {
7128
+ const normalizedMessage = buildChatMessage({
7129
+ ...message,
7130
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
7131
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
7132
+ });
7133
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
7134
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
6757
7135
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
6758
7136
  if (!this.cachedChat) {
6759
7137
  this.cachedChat = {
@@ -6766,39 +7144,35 @@ var IdeProviderInstance = class {
6766
7144
  };
6767
7145
  }
6768
7146
  this.runtimeMessages.push({
6769
- key: dedupKey,
6770
- message: {
6771
- role: "system",
6772
- senderName: "System",
6773
- content: normalizedContent,
6774
- receivedAt,
6775
- timestamp: receivedAt
6776
- }
7147
+ key: dedupKey,
7148
+ message: normalizedMessage
6777
7149
  });
6778
7150
  if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
6779
- this.historyWriter.appendNewMessages(
6780
- this.type,
6781
- [{
6782
- role: "system",
6783
- senderName: "System",
6784
- content: normalizedContent,
6785
- kind: "system",
6786
- receivedAt,
6787
- historyDedupKey: dedupKey
6788
- }],
6789
- this.cachedChat?.title || this.provider.name,
6790
- this.instanceId,
6791
- this.cachedChat?.id || this.instanceId
6792
- );
7151
+ if (normalizedContent) {
7152
+ this.historyWriter.appendNewMessages(
7153
+ this.type,
7154
+ [{
7155
+ role: normalizedMessage.role,
7156
+ senderName: normalizedMessage.senderName,
7157
+ kind: normalizedMessage.kind,
7158
+ content: normalizedContent,
7159
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
7160
+ historyDedupKey: dedupKey
7161
+ }],
7162
+ this.cachedChat?.title || this.provider.name,
7163
+ this.instanceId,
7164
+ this.cachedChat?.id || this.instanceId
7165
+ );
7166
+ }
6793
7167
  }
6794
7168
  mergeConversationMessages(messages) {
6795
- if (this.runtimeMessages.length === 0) return messages;
6796
- return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
7169
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(messages);
7170
+ return normalizeChatMessages([...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
6797
7171
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
6798
7172
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
6799
7173
  if (aTime !== bTime) return aTime - bTime;
6800
7174
  return a.index - b.index;
6801
- }).map((entry) => entry.message);
7175
+ }).map((entry) => entry.message));
6802
7176
  }
6803
7177
  getPersistedEffectContent(effect) {
6804
7178
  if (effect.type === "message") {
@@ -7701,219 +8075,6 @@ function reconcileIdeRuntimeSessions(instanceManager, sessionRegistry) {
7701
8075
  // src/commands/handler.ts
7702
8076
  init_logger();
7703
8077
 
7704
- // src/providers/io-contracts.ts
7705
- function normalizeInputEnvelope(input) {
7706
- const normalized = normalizeInputEnvelopePayload(input);
7707
- const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
7708
- return {
7709
- parts: normalized.parts,
7710
- textFallback,
7711
- ...normalized.metadata ? { metadata: normalized.metadata } : {}
7712
- };
7713
- }
7714
- function normalizeMessageParts(content) {
7715
- if (typeof content === "string") return [{ type: "text", text: content }];
7716
- if (!Array.isArray(content)) {
7717
- if (content && typeof content === "object" && typeof content.text === "string") {
7718
- return [{ type: "text", text: String(content.text) }];
7719
- }
7720
- return [];
7721
- }
7722
- const parts = [];
7723
- for (const raw of content) {
7724
- if (typeof raw === "string") {
7725
- parts.push({ type: "text", text: raw });
7726
- continue;
7727
- }
7728
- if (!raw || typeof raw !== "object") continue;
7729
- const part = normalizeMessagePartObject(raw);
7730
- if (part) parts.push(part);
7731
- }
7732
- return parts;
7733
- }
7734
- function flattenMessageParts(parts) {
7735
- return parts.map((part) => {
7736
- if (part.type === "text") return part.text;
7737
- if (part.type === "resource") return part.resource.text || "";
7738
- return "";
7739
- }).filter((value) => value.length > 0).join("\n");
7740
- }
7741
- function normalizeInputEnvelopePayload(input) {
7742
- if (typeof input === "string") {
7743
- return { parts: [{ type: "text", text: input }], textFallback: input };
7744
- }
7745
- if (!input || typeof input !== "object") {
7746
- return { parts: [], textFallback: "" };
7747
- }
7748
- const record = input;
7749
- const nestedInput = record.input;
7750
- if (nestedInput && typeof nestedInput === "object") {
7751
- const nested = nestedInput;
7752
- return {
7753
- parts: normalizeInputParts(nested.parts ?? nested.prompt),
7754
- textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
7755
- metadata: normalizeInputMetadata(nested.metadata)
7756
- };
7757
- }
7758
- const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
7759
- if (directText !== void 0) {
7760
- return { parts: [{ type: "text", text: directText }], textFallback: directText };
7761
- }
7762
- const directParts = normalizeInputParts(record.parts ?? record.prompt);
7763
- return {
7764
- parts: directParts,
7765
- textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
7766
- metadata: normalizeInputMetadata(record.metadata)
7767
- };
7768
- }
7769
- function normalizeInputMetadata(value) {
7770
- if (!value || typeof value !== "object") return void 0;
7771
- const record = value;
7772
- const metadata = {};
7773
- if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
7774
- metadata.source = record.source;
7775
- }
7776
- if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
7777
- metadata.clientTimestamp = record.clientTimestamp;
7778
- }
7779
- return Object.keys(metadata).length > 0 ? metadata : void 0;
7780
- }
7781
- function normalizeInputParts(value) {
7782
- if (!Array.isArray(value)) return [];
7783
- const parts = [];
7784
- for (const raw of value) {
7785
- if (typeof raw === "string") {
7786
- parts.push({ type: "text", text: raw });
7787
- continue;
7788
- }
7789
- if (!raw || typeof raw !== "object") continue;
7790
- const part = normalizeInputPartObject(raw);
7791
- if (part) parts.push(part);
7792
- }
7793
- return parts;
7794
- }
7795
- function normalizeInputPartObject(raw) {
7796
- const type = raw.type;
7797
- if (type === "text" && typeof raw.text === "string") {
7798
- return { type, text: raw.text };
7799
- }
7800
- if (type === "image" && typeof raw.mimeType === "string") {
7801
- return {
7802
- type,
7803
- mimeType: raw.mimeType,
7804
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7805
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7806
- ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
7807
- };
7808
- }
7809
- if (type === "audio" && typeof raw.mimeType === "string") {
7810
- return {
7811
- type,
7812
- mimeType: raw.mimeType,
7813
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7814
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7815
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
7816
- };
7817
- }
7818
- if (type === "video" && typeof raw.mimeType === "string") {
7819
- return {
7820
- type,
7821
- mimeType: raw.mimeType,
7822
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7823
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7824
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
7825
- };
7826
- }
7827
- if (type === "resource" && typeof raw.uri === "string") {
7828
- return {
7829
- type,
7830
- uri: raw.uri,
7831
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7832
- ...typeof raw.name === "string" ? { name: raw.name } : {},
7833
- ...typeof raw.text === "string" ? { text: raw.text } : {},
7834
- ...typeof raw.data === "string" ? { data: raw.data } : {}
7835
- };
7836
- }
7837
- if (type === "resource_link" && typeof raw.uri === "string") {
7838
- return {
7839
- type: "resource",
7840
- uri: raw.uri,
7841
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7842
- ...typeof raw.name === "string" ? { name: raw.name } : {}
7843
- };
7844
- }
7845
- return null;
7846
- }
7847
- function normalizeMessagePartObject(raw) {
7848
- const type = raw.type;
7849
- if (type === "text" && typeof raw.text === "string") {
7850
- return { type, text: raw.text };
7851
- }
7852
- if (type === "image" && typeof raw.mimeType === "string") {
7853
- return {
7854
- type,
7855
- mimeType: raw.mimeType,
7856
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7857
- ...typeof raw.data === "string" ? { data: raw.data } : {}
7858
- };
7859
- }
7860
- if (type === "audio" && typeof raw.mimeType === "string") {
7861
- return {
7862
- type,
7863
- mimeType: raw.mimeType,
7864
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7865
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7866
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
7867
- };
7868
- }
7869
- if (type === "video" && typeof raw.mimeType === "string") {
7870
- return {
7871
- type,
7872
- mimeType: raw.mimeType,
7873
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
7874
- ...typeof raw.data === "string" ? { data: raw.data } : {},
7875
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
7876
- };
7877
- }
7878
- if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
7879
- return {
7880
- type,
7881
- uri: raw.uri,
7882
- name: raw.name,
7883
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
7884
- ...typeof raw.size === "number" ? { size: raw.size } : {}
7885
- };
7886
- }
7887
- if (type === "resource" && raw.resource && typeof raw.resource === "object") {
7888
- const resource = raw.resource;
7889
- if (typeof resource.uri !== "string") return null;
7890
- return {
7891
- type,
7892
- resource: {
7893
- uri: resource.uri,
7894
- ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
7895
- ...typeof resource.text === "string" ? { text: resource.text } : {},
7896
- ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
7897
- }
7898
- };
7899
- }
7900
- return null;
7901
- }
7902
- function flattenInputParts(parts) {
7903
- return parts.map((part) => {
7904
- if (part.type === "text") return part.text;
7905
- if (part.type === "audio") return part.transcript || "";
7906
- if (part.type === "resource") return part.text || "";
7907
- return "";
7908
- }).filter((value) => value.length > 0).join("\n");
7909
- }
7910
-
7911
- // src/providers/contracts.ts
7912
- function flattenContent(content) {
7913
- if (typeof content === "string") return content;
7914
- return flattenMessageParts(normalizeMessageParts(content));
7915
- }
7916
-
7917
8078
  // src/commands/chat-commands.ts
7918
8079
  init_logger();
7919
8080
 
@@ -8044,6 +8205,7 @@ function createInteractionId(prefix = "ix") {
8044
8205
  }
8045
8206
 
8046
8207
  // src/commands/chat-commands.ts
8208
+ init_chat_message_normalization();
8047
8209
  var RECENT_SEND_WINDOW_MS = 1200;
8048
8210
  var recentSendByTarget = /* @__PURE__ */ new Map();
8049
8211
  function hashSignatureParts(parts) {
@@ -8173,7 +8335,7 @@ function normalizeReadChatCursor(args) {
8173
8335
  }
8174
8336
  function normalizeReadChatMessages(payload) {
8175
8337
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
8176
- return messages;
8338
+ return normalizeChatMessages(messages);
8177
8339
  }
8178
8340
  function deriveHistoryDedupKey(message) {
8179
8341
  const unitKey = typeof message._unitKey === "string" ? message._unitKey.trim() : "";
@@ -10521,6 +10683,7 @@ var fs5 = __toESM(require("fs"));
10521
10683
  var import_node_module = require("module");
10522
10684
  init_provider_cli_adapter();
10523
10685
  init_logger();
10686
+ init_chat_message_normalization();
10524
10687
  var CachedDatabaseSync = null;
10525
10688
  function getDatabaseSync() {
10526
10689
  if (CachedDatabaseSync) return CachedDatabaseSync;
@@ -11015,8 +11178,8 @@ var CliProviderInstance = class {
11015
11178
  if (this.appliedEffectKeys.has(effectKey)) continue;
11016
11179
  this.appliedEffectKeys.add(effectKey);
11017
11180
  if (effect.persist !== false) {
11018
- const persisted = this.getPersistedEffectContent(effect);
11019
- if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
11181
+ const persistedMessage = buildPersistedProviderEffectMessage(effect);
11182
+ if (persistedMessage) this.appendRuntimeMessage(persistedMessage, effectKey);
11020
11183
  }
11021
11184
  if (effect.type === "message" && effect.message) {
11022
11185
  const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
@@ -11140,44 +11303,53 @@ ${effect.notification.body || ""}`.trim();
11140
11303
  );
11141
11304
  }
11142
11305
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
11143
- const normalizedContent = String(content || "").trim();
11144
- if (!normalizedContent) return;
11306
+ this.appendRuntimeMessage(buildRuntimeSystemChatMessage({
11307
+ content,
11308
+ receivedAt,
11309
+ timestamp: receivedAt
11310
+ }), dedupKey);
11311
+ }
11312
+ appendRuntimeMessage(message, dedupKey) {
11313
+ const normalizedMessage = buildChatMessage({
11314
+ ...message,
11315
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp || Date.now(),
11316
+ timestamp: typeof message.timestamp === "number" ? message.timestamp : message.receivedAt || Date.now()
11317
+ });
11318
+ const normalizedContent = typeof normalizedMessage.content === "string" ? normalizedMessage.content.trim() : flattenContent(normalizedMessage.content).trim();
11319
+ if (!normalizedContent && (!Array.isArray(normalizedMessage.content) || normalizedMessage.content.length === 0)) return;
11145
11320
  if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
11146
11321
  this.runtimeMessages.push({
11147
11322
  key: dedupKey,
11148
- message: {
11149
- role: "system",
11150
- senderName: "System",
11151
- content: normalizedContent,
11152
- receivedAt,
11153
- timestamp: receivedAt
11154
- }
11323
+ message: normalizedMessage
11155
11324
  });
11156
11325
  if (this.runtimeMessages.length > 50) {
11157
11326
  this.runtimeMessages = this.runtimeMessages.slice(-50);
11158
11327
  }
11159
- this.historyWriter.appendNewMessages(
11160
- this.type,
11161
- [{
11162
- role: "system",
11163
- senderName: "System",
11164
- content: normalizedContent,
11165
- receivedAt,
11166
- historyDedupKey: dedupKey
11167
- }],
11168
- this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
11169
- this.instanceId,
11170
- this.providerSessionId
11171
- );
11328
+ if (normalizedContent) {
11329
+ this.historyWriter.appendNewMessages(
11330
+ this.type,
11331
+ [{
11332
+ role: normalizedMessage.role,
11333
+ senderName: normalizedMessage.senderName,
11334
+ kind: normalizedMessage.kind,
11335
+ content: normalizedContent,
11336
+ receivedAt: normalizedMessage.receivedAt || normalizedMessage.timestamp,
11337
+ historyDedupKey: dedupKey
11338
+ }],
11339
+ this.adapter.getScriptParsedStatus?.()?.title || this.workingDir.split("/").filter(Boolean).pop() || "session",
11340
+ this.instanceId,
11341
+ this.providerSessionId
11342
+ );
11343
+ }
11172
11344
  }
11173
11345
  mergeConversationMessages(parsedMessages) {
11174
- if (this.runtimeMessages.length === 0) return parsedMessages;
11175
- return [...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
11346
+ if (this.runtimeMessages.length === 0) return normalizeChatMessages(parsedMessages);
11347
+ return normalizeChatMessages([...parsedMessages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
11176
11348
  const aTime = a.message.receivedAt || a.message.timestamp || 0;
11177
11349
  const bTime = b.message.receivedAt || b.message.timestamp || 0;
11178
11350
  if (aTime !== bTime) return aTime - bTime;
11179
11351
  return a.index - b.index;
11180
- }).map((entry) => entry.message);
11352
+ }).map((entry) => entry.message));
11181
11353
  }
11182
11354
  formatApprovalRequestMessage(modalMessage, buttons) {
11183
11355
  const lines = ["Approval requested"];
@@ -11248,6 +11420,7 @@ var path12 = __toESM(require("path"));
11248
11420
  var import_stream = require("stream");
11249
11421
  var import_child_process5 = require("child_process");
11250
11422
  var import_sdk = require("@agentclientprotocol/sdk");
11423
+ init_chat_message_normalization();
11251
11424
  init_logger();
11252
11425
  function getPromptCapabilityFlags(agentCapabilities) {
11253
11426
  const prompt = agentCapabilities?.promptCapabilities || {};
@@ -11413,24 +11586,21 @@ var AcpProviderInstance = class {
11413
11586
  }
11414
11587
  getState() {
11415
11588
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
11416
- const recentMessages = this.messages.slice(-50).map((m) => {
11589
+ const recentMessages = normalizeChatMessages(this.messages.slice(-50).map((m) => {
11417
11590
  const content = this.truncateContent(m.content);
11418
- return {
11419
- role: m.role,
11420
- content,
11421
- timestamp: m.timestamp,
11422
- toolCalls: m.toolCalls
11423
- };
11424
- });
11591
+ return buildChatMessage({
11592
+ ...m,
11593
+ content
11594
+ });
11595
+ }));
11425
11596
  if (this.currentStatus === "generating" && (this.partialContent || this.partialBlocks.length > 0)) {
11426
11597
  const blocks = this.buildPartialBlocks();
11427
11598
  if (blocks.length > 0) {
11428
- recentMessages.push({
11429
- role: "assistant",
11599
+ recentMessages.push(buildAssistantChatMessage({
11430
11600
  content: blocks,
11431
11601
  timestamp: Date.now(),
11432
11602
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
11433
- });
11603
+ }));
11434
11604
  }
11435
11605
  }
11436
11606
  return {
@@ -11443,7 +11613,7 @@ var AcpProviderInstance = class {
11443
11613
  id: this.sessionId || `${this.type}_${this.workingDir}`,
11444
11614
  title: `${this.provider.name} \xB7 ${dirName}`,
11445
11615
  status: this.currentStatus,
11446
- messages: recentMessages,
11616
+ messages: normalizeChatMessages(recentMessages),
11447
11617
  activeModal: this.currentStatus === "waiting_approval" ? {
11448
11618
  message: this.activeToolCalls.find((t) => t.status === "running")?.name || "Permission requested",
11449
11619
  buttons: ["Approve", "Reject"]
@@ -11975,11 +12145,10 @@ var AcpProviderInstance = class {
11975
12145
  if (b.type === "resource") return { type: "resource", resource: b.resource };
11976
12146
  return { type: "text", text: flattenContent([b]) };
11977
12147
  }) : [{ type: "text", text }];
11978
- this.messages.push({
11979
- role: "user",
12148
+ this.messages.push(buildUserChatMessage({
11980
12149
  content: contentBlocks && contentBlocks.length > 0 ? contentBlocks : text,
11981
12150
  timestamp: Date.now()
11982
- });
12151
+ }));
11983
12152
  this.currentStatus = "generating";
11984
12153
  this.partialContent = "";
11985
12154
  this.partialBlocks = [];
@@ -12149,11 +12318,11 @@ var AcpProviderInstance = class {
12149
12318
  content = m.content.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
12150
12319
  }
12151
12320
  if (content.trim()) {
12152
- this.messages.push({
12321
+ this.messages.push(buildChatMessage({
12153
12322
  role: m.role || "assistant",
12154
12323
  content: content.trim(),
12155
12324
  timestamp: Date.now()
12156
- });
12325
+ }));
12157
12326
  this.partialContent = "";
12158
12327
  }
12159
12328
  }
@@ -12229,12 +12398,11 @@ var AcpProviderInstance = class {
12229
12398
  return b;
12230
12399
  }).filter((b) => b.type !== "text" || b.type === "text" && b.text.trim());
12231
12400
  if (finalBlocks.length > 0) {
12232
- this.messages.push({
12233
- role: "assistant",
12401
+ this.messages.push(buildAssistantChatMessage({
12234
12402
  content: finalBlocks.length === 1 && finalBlocks[0].type === "text" ? finalBlocks[0].text : finalBlocks,
12235
12403
  timestamp: Date.now(),
12236
12404
  toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : void 0
12237
- });
12405
+ }));
12238
12406
  }
12239
12407
  this.partialContent = "";
12240
12408
  this.partialBlocks = [];
@@ -12294,11 +12462,10 @@ var AcpProviderInstance = class {
12294
12462
  appendSystemMessage(content, timestamp = Date.now()) {
12295
12463
  const normalizedContent = String(content || "").trim();
12296
12464
  if (!normalizedContent) return;
12297
- this.messages.push({
12298
- role: "system",
12465
+ this.messages.push(buildRuntimeSystemChatMessage({
12299
12466
  content: normalizedContent,
12300
12467
  timestamp
12301
- });
12468
+ }));
12302
12469
  if (this.messages.length > 200) {
12303
12470
  this.messages = this.messages.slice(-100);
12304
12471
  }
@@ -16969,6 +17136,7 @@ var DaemonAgentStreamManager = class {
16969
17136
 
16970
17137
  // src/agent-stream/poller.ts
16971
17138
  init_logger();
17139
+ init_chat_message_normalization();
16972
17140
  var AgentStreamPoller = class {
16973
17141
  deps;
16974
17142
  timer = null;
@@ -17124,12 +17292,9 @@ var AgentStreamPoller = class {
17124
17292
  type: "message",
17125
17293
  id: effectId,
17126
17294
  persist: true,
17127
- message: {
17128
- role: "system",
17129
- senderName: "System",
17130
- kind: "system",
17295
+ message: buildRuntimeSystemChatMessage({
17131
17296
  content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel)
17132
- }
17297
+ })
17133
17298
  }
17134
17299
  ]
17135
17300
  };
@@ -17376,6 +17541,9 @@ var ProviderInstanceManager = class {
17376
17541
  }
17377
17542
  };
17378
17543
 
17544
+ // src/index.ts
17545
+ init_chat_message_normalization();
17546
+
17379
17547
  // src/providers/version-archive.ts
17380
17548
  var fs10 = __toESM(require("fs"));
17381
17549
  var path18 = __toESM(require("path"));
@@ -23651,6 +23819,7 @@ async function shutdownDaemonComponents(components) {
23651
23819
  0 && (module.exports = {
23652
23820
  AcpProviderInstance,
23653
23821
  AgentStreamPoller,
23822
+ BUILTIN_CHAT_MESSAGE_KINDS,
23654
23823
  CdpDomHandlers,
23655
23824
  CliProviderInstance,
23656
23825
  DAEMON_WS_PATH,
@@ -23675,9 +23844,14 @@ async function shutdownDaemonComponents(components) {
23675
23844
  SessionHostPtyTransportFactory,
23676
23845
  VersionArchive,
23677
23846
  appendRecentActivity,
23847
+ buildAssistantChatMessage,
23848
+ buildChatMessage,
23678
23849
  buildMachineInfo,
23850
+ buildRuntimeSystemChatMessage,
23679
23851
  buildSessionEntries,
23680
23852
  buildStatusSnapshot,
23853
+ buildSystemChatMessage,
23854
+ buildUserChatMessage,
23681
23855
  clearDebugTrace,
23682
23856
  configureDebugTraceStore,
23683
23857
  connectCdpManager,
@@ -23709,6 +23883,7 @@ async function shutdownDaemonComponents(components) {
23709
23883
  initDaemonComponents,
23710
23884
  installExtensions,
23711
23885
  installGlobalInterceptor,
23886
+ isBuiltinChatMessageKind,
23712
23887
  isCdpConnected,
23713
23888
  isExtensionInstalled,
23714
23889
  isIdeRunning,
@@ -23727,6 +23902,9 @@ async function shutdownDaemonComponents(components) {
23727
23902
  markSetupComplete,
23728
23903
  maybeRunDaemonUpgradeHelperFromEnv,
23729
23904
  normalizeActiveChatData,
23905
+ normalizeChatMessage,
23906
+ normalizeChatMessageKind,
23907
+ normalizeChatMessages,
23730
23908
  normalizeInputEnvelope,
23731
23909
  normalizeManagedStatus,
23732
23910
  normalizeMessageParts,