@axhub/genie 0.2.5 → 0.2.7

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 (139) hide show
  1. package/dist/assets/App-BWSqiXAT.js +220 -0
  2. package/dist/assets/App-DrlLKa8f.css +1 -0
  3. package/dist/assets/ReviewApp-nz3mbArg.js +1 -0
  4. package/dist/assets/{_basePickBy-CFRQvihx.js → _basePickBy-C19AekOu.js} +1 -1
  5. package/dist/assets/{_baseUniq-Dhh8nCvs.js → _baseUniq-JsnevLw_.js} +1 -1
  6. package/dist/assets/{arc-DQ0v3dU4.js → arc-BLpcuBlf.js} +1 -1
  7. package/dist/assets/architectureDiagram-2XIMDMQ5-CarjBOOv.js +36 -0
  8. package/dist/assets/{blockDiagram-WCTKOSBZ-Bbxhj5KC.js → blockDiagram-WCTKOSBZ-DQBLwsUS.js} +3 -3
  9. package/dist/assets/c4Diagram-IC4MRINW-CGobwBIj.js +10 -0
  10. package/dist/assets/channel-DkFNxV_H.js +1 -0
  11. package/dist/assets/{chunk-4BX2VUAB-DlvtrM0q.js → chunk-4BX2VUAB-De63kbgc.js} +1 -1
  12. package/dist/assets/{chunk-55IACEB6-DJUSHyTa.js → chunk-55IACEB6-DtTDDdM9.js} +1 -1
  13. package/dist/assets/{chunk-FMBD7UC4-C6Ch-htf.js → chunk-FMBD7UC4-DHuwd8tw.js} +1 -1
  14. package/dist/assets/{chunk-JSJVCQXG-DzQIht58.js → chunk-JSJVCQXG-BgytFtmO.js} +1 -1
  15. package/dist/assets/{chunk-KX2RTZJC-C05jARMH.js → chunk-KX2RTZJC-nZdp86aN.js} +1 -1
  16. package/dist/assets/chunk-NQ4KR5QH-CMH6EDP2.js +220 -0
  17. package/dist/assets/{chunk-QZHKN3VN-jxti9HTX.js → chunk-QZHKN3VN-DvUQ3mnO.js} +1 -1
  18. package/dist/assets/chunk-WL4C6EOR-Dn7db_6t.js +189 -0
  19. package/dist/assets/classDiagram-VBA2DB6C-DtwCEe8S.js +1 -0
  20. package/dist/assets/classDiagram-v2-RAHNMMFH-DtwCEe8S.js +1 -0
  21. package/dist/assets/clone-C0lCEIEO.js +1 -0
  22. package/dist/assets/cose-bilkent-S5V4N54A-DD_nzqsz.js +1 -0
  23. package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
  24. package/dist/assets/{dagre-KLK3FWXG-DJ3dNSYk.js → dagre-KLK3FWXG-CHYIvW47.js} +1 -1
  25. package/dist/assets/diagram-E7M64L7V-TVdvHtGc.js +24 -0
  26. package/dist/assets/{diagram-IFDJBPK2-Da6K4aP-.js → diagram-IFDJBPK2-Dzsiln_C.js} +1 -1
  27. package/dist/assets/{diagram-P4PSJMXO-vZZKB92A.js → diagram-P4PSJMXO-DKnGbUpE.js} +1 -1
  28. package/dist/assets/erDiagram-INFDFZHY-5Kw0bByo.js +70 -0
  29. package/dist/assets/{flowDiagram-PKNHOUZH-DUV13pHi.js → flowDiagram-PKNHOUZH-BAZ2-jKp.js} +4 -4
  30. package/dist/assets/ganttDiagram-A5KZAMGK-CsADFkcq.js +292 -0
  31. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-BZ5gW69I.js → gitGraphDiagram-K3NZZRJ6-BflpyjGy.js} +1 -1
  32. package/dist/assets/{graph-BbvHswRd.js → graph-suelaXFh.js} +1 -1
  33. package/dist/assets/highlighted-body-OFNGDK62-CZrBMazC.js +1 -0
  34. package/dist/assets/index-B01NxbUv.css +1 -0
  35. package/dist/assets/index-DW5pGgQ_.js +2 -0
  36. package/dist/assets/{infoDiagram-LFFYTUFH-8auUIPKW.js → infoDiagram-LFFYTUFH-pfD1FA3p.js} +1 -1
  37. package/dist/assets/ishikawaDiagram-PHBUUO56-ndm9snwO.js +70 -0
  38. package/dist/assets/journeyDiagram-4ABVD52K-HgF2t7z5.js +139 -0
  39. package/dist/assets/{kanban-definition-K7BYSVSG-Bappd2YO.js → kanban-definition-K7BYSVSG-FWinmur1.js} +5 -5
  40. package/dist/assets/{layout-BmbfFZKy.js → layout-vcz43XvZ.js} +1 -1
  41. package/dist/assets/{linear-WZnF-PT6.js → linear-le4gc0vx.js} +1 -1
  42. package/dist/assets/mermaid-GHXKKRXX-CK8m3lad.js +870 -0
  43. package/dist/assets/mindmap-definition-YRQLILUH-CNq9SKj4.js +68 -0
  44. package/dist/assets/{pieDiagram-SKSYHLDU-uxjlAy1t.js → pieDiagram-SKSYHLDU-C7PKDh3b.js} +2 -2
  45. package/dist/assets/quadrantDiagram-337W2JSQ-B7FnztNO.js +7 -0
  46. package/dist/assets/requirementDiagram-Z7DCOOCP-Bl_BM2Th.js +73 -0
  47. package/dist/assets/{sankeyDiagram-WA2Y5GQK-2-FHHM-R.js → sankeyDiagram-WA2Y5GQK-4gulcOP4.js} +3 -3
  48. package/dist/assets/sequenceDiagram-2WXFIKYE-VEuJDwyJ.js +145 -0
  49. package/dist/assets/{stateDiagram-RAJIS63D-DoW8U53H.js → stateDiagram-RAJIS63D-CB4Vl7qM.js} +1 -1
  50. package/dist/assets/stateDiagram-v2-FVOUBMTO-C85ucl39.js +1 -0
  51. package/dist/assets/timeline-definition-YZTLITO2-BPGKhi7f.js +61 -0
  52. package/dist/assets/{treemap-KZPCXAKY-ajdAP-72.js → treemap-KZPCXAKY-DZSEE6Hz.js} +58 -58
  53. package/dist/assets/vendor-codemirror-CyOKkaQZ.js +31 -0
  54. package/dist/assets/vendor-react-CP4yFTs7.js +8 -0
  55. package/dist/assets/vendor-xterm-DfcmCpbH.js +66 -0
  56. package/dist/assets/{vennDiagram-LZ73GAT5-C9If0AT0.js → vennDiagram-LZ73GAT5-8E_G06fI.js} +4 -4
  57. package/dist/assets/xychartDiagram-JWTSCODW-CbBk50-O.js +7 -0
  58. package/dist/favicon.png +0 -0
  59. package/dist/icons/icon-128x128.png +0 -0
  60. package/dist/icons/icon-144x144.png +0 -0
  61. package/dist/icons/icon-152x152.png +0 -0
  62. package/dist/icons/icon-192x192.png +0 -0
  63. package/dist/icons/icon-384x384.png +0 -0
  64. package/dist/icons/icon-512x512.png +0 -0
  65. package/dist/icons/icon-72x72.png +0 -0
  66. package/dist/icons/icon-96x96.png +0 -0
  67. package/dist/index.html +4 -5
  68. package/dist/logo-128.png +0 -0
  69. package/dist/logo-256.png +0 -0
  70. package/dist/logo-32.png +0 -0
  71. package/dist/logo-512.png +0 -0
  72. package/dist/logo-64.png +0 -0
  73. package/package.json +2 -1
  74. package/server/_legacy-providers/README.md +30 -0
  75. package/server/_legacy-providers/claude-sdk.js +956 -0
  76. package/server/_legacy-providers/gemini-cli.js +368 -0
  77. package/server/_legacy-providers/openai-codex.js +705 -0
  78. package/server/_legacy-providers/opencode-cli.js +674 -0
  79. package/server/acp-runtime/client.js +1805 -0
  80. package/server/acp-runtime/client.test.js +688 -0
  81. package/server/acp-runtime/index.js +419 -0
  82. package/server/acp-runtime/registry.js +45 -0
  83. package/server/acp-runtime/session-store.js +254 -0
  84. package/server/acp-runtime/session-store.test.js +89 -0
  85. package/server/channels/runtime/AgentRuntimeAdapter.js +21 -70
  86. package/server/claude-sdk.js +24 -944
  87. package/server/cli.js +11 -5
  88. package/server/external-agent/service.js +77 -63
  89. package/server/gemini-cli.js +23 -360
  90. package/server/index.js +54 -46
  91. package/server/openai-codex.js +24 -698
  92. package/server/opencode-cli.js +70 -640
  93. package/server/routes/agent.js +2 -0
  94. package/server/routes/codex.js +5 -5
  95. package/server/routes/git.js +3 -20
  96. package/server/routes/mcp.js +18 -34
  97. package/server/routes/session-core.js +44 -10
  98. package/server/session-core/abortSession.js +2 -18
  99. package/server/session-core/eventStore.js +5 -1
  100. package/server/session-core/providerAdapters.js +98 -10
  101. package/server/session-core/providerDiscovery.js +2 -2
  102. package/server/session-core/runtimeState.js +16 -17
  103. package/server/session-core/runtimeWriter.js +19 -12
  104. package/server/utils/codexPath.js +3 -1
  105. package/server/utils/spawnCommand.js +7 -0
  106. package/shared/conversationEvents.js +347 -10
  107. package/shared/conversationEvents.test.js +403 -0
  108. package/dist/assets/App-BxazfNJn.js +0 -484
  109. package/dist/assets/App-qxJ8_QYu.css +0 -32
  110. package/dist/assets/ReviewApp-CsqTAlGU.js +0 -1
  111. package/dist/assets/architectureDiagram-2XIMDMQ5-DmUHdvQH.js +0 -36
  112. package/dist/assets/c4Diagram-IC4MRINW-BOivDlQU.js +0 -10
  113. package/dist/assets/channel-Cj8xVD0X.js +0 -1
  114. package/dist/assets/chunk-NQ4KR5QH-Ci-n7jfu.js +0 -220
  115. package/dist/assets/chunk-WL4C6EOR-C559Mk71.js +0 -189
  116. package/dist/assets/classDiagram-VBA2DB6C-CI2zklxw.js +0 -1
  117. package/dist/assets/classDiagram-v2-RAHNMMFH-CI2zklxw.js +0 -1
  118. package/dist/assets/clone-BEVqubrI.js +0 -1
  119. package/dist/assets/cose-bilkent-S5V4N54A-DNO9ncXL.js +0 -1
  120. package/dist/assets/cytoscape.esm-2ZfV8NB5.js +0 -331
  121. package/dist/assets/diagram-E7M64L7V-Ba_LGLun.js +0 -24
  122. package/dist/assets/erDiagram-INFDFZHY-Csb8dFdP.js +0 -70
  123. package/dist/assets/ganttDiagram-A5KZAMGK-B5Kv9Wfz.js +0 -292
  124. package/dist/assets/highlighted-body-TPN3WLV5-DZJajMGm.js +0 -1
  125. package/dist/assets/index-BFX9lxRB.css +0 -1
  126. package/dist/assets/index-BiErUGrv.js +0 -2
  127. package/dist/assets/ishikawaDiagram-PHBUUO56-JmsNlo2I.js +0 -70
  128. package/dist/assets/journeyDiagram-4ABVD52K-Cuudv7Vv.js +0 -139
  129. package/dist/assets/mermaid-O7DHMXV3-D-2fQRvw.js +0 -988
  130. package/dist/assets/mindmap-definition-YRQLILUH-BQHnzzud.js +0 -68
  131. package/dist/assets/quadrantDiagram-337W2JSQ-DpwZU-f_.js +0 -7
  132. package/dist/assets/requirementDiagram-Z7DCOOCP-C_9ClOWm.js +0 -73
  133. package/dist/assets/sequenceDiagram-2WXFIKYE-egns-0XI.js +0 -145
  134. package/dist/assets/stateDiagram-v2-FVOUBMTO-BoFZZ4Ds.js +0 -1
  135. package/dist/assets/timeline-definition-YZTLITO2-chPa8ppH.js +0 -61
  136. package/dist/assets/vendor-codemirror-Dz7_EqNA.js +0 -39
  137. package/dist/assets/vendor-react-Cpt6D04s.js +0 -59
  138. package/dist/assets/vendor-xterm-DfaPXD3y.js +0 -66
  139. package/dist/assets/xychartDiagram-JWTSCODW-DD42U6Or.js +0 -7
@@ -0,0 +1,7 @@
1
+ import { spawn as nodeSpawn } from 'child_process';
2
+ import crossSpawn from 'cross-spawn';
3
+
4
+ export function spawnCommand(command, args = [], options = {}) {
5
+ const spawnImpl = process.platform === 'win32' ? crossSpawn : nodeSpawn;
6
+ return spawnImpl(command, args, options);
7
+ }
@@ -16,6 +16,7 @@ export const CONVERSATION_EVENT_KINDS = {
16
16
  ASSISTANT_TEXT_START: 'assistant_text_start',
17
17
  ASSISTANT_TEXT_DELTA: 'assistant_text_delta',
18
18
  ASSISTANT_TEXT_END: 'assistant_text_end',
19
+ ASSISTANT_CONTENT_BLOCK: 'assistant_content_block',
19
20
  REASONING_START: 'reasoning_start',
20
21
  REASONING_DELTA: 'reasoning_delta',
21
22
  REASONING_END: 'reasoning_end',
@@ -23,6 +24,9 @@ export const CONVERSATION_EVENT_KINDS = {
23
24
  TOOL_CALL_INPUT: 'tool_call_input',
24
25
  TOOL_CALL_END: 'tool_call_end',
25
26
  TOOL_RESULT: 'tool_result',
27
+ PLAN_UPDATE: 'plan_update',
28
+ MODE_UPDATE: 'mode_update',
29
+ AVAILABLE_COMMANDS_UPDATE: 'available_commands_update',
26
30
  APPROVAL_REQUEST: 'approval_request',
27
31
  APPROVAL_RESOLVED: 'approval_resolved',
28
32
  ARTIFACT_CREATED: 'artifact_created',
@@ -236,6 +240,167 @@ function stringifyToolContent(value) {
236
240
  }
237
241
  }
238
242
 
243
+ function cloneJsonValue(value) {
244
+ if (value == null) {
245
+ return value;
246
+ }
247
+
248
+ try {
249
+ return JSON.parse(JSON.stringify(value));
250
+ } catch {
251
+ return value;
252
+ }
253
+ }
254
+
255
+ function normalizeMessageContentBlocks(value) {
256
+ if (!Array.isArray(value)) {
257
+ return [];
258
+ }
259
+
260
+ return value
261
+ .filter((item) => item && typeof item === 'object')
262
+ .map((item) => cloneJsonValue(item));
263
+ }
264
+
265
+ function getConversationEventClientRequestId(event) {
266
+ const rawValue = event?.extensions?.clientRequestId || event?.payload?.clientRequestId || null;
267
+ return typeof rawValue === 'string' && rawValue.trim()
268
+ ? rawValue.trim()
269
+ : null;
270
+ }
271
+
272
+ function getTimelineMessageClientRequestId(message) {
273
+ const rawValue = message?.clientRequestId || message?.extensions?.clientRequestId || null;
274
+ return typeof rawValue === 'string' && rawValue.trim()
275
+ ? rawValue.trim()
276
+ : null;
277
+ }
278
+
279
+ function buildComparableUserMessageContentBlocks(message = {}) {
280
+ const imageBlocks = Array.isArray(message?.images)
281
+ ? message.images.map((image) => ({
282
+ type: 'image',
283
+ data: image?.data || image?.image || '',
284
+ mimeType: image?.mimeType || 'application/octet-stream',
285
+ name: image?.name || image?.filename || ''
286
+ }))
287
+ : [];
288
+
289
+ return [
290
+ ...imageBlocks,
291
+ ...normalizeMessageContentBlocks(message?.contentBlocks)
292
+ ];
293
+ }
294
+
295
+ function findMatchingOptimisticUserMessageIndex(messages, event, payload = {}) {
296
+ const clientRequestId = getConversationEventClientRequestId(event);
297
+ if (!clientRequestId) {
298
+ return -1;
299
+ }
300
+
301
+ const expectedContentBlocks = JSON.stringify(normalizeMessageContentBlocks(payload.contentBlocks));
302
+
303
+ return findLatestTimelineMessageIndex(messages, (message) => {
304
+ if (String(message?.type || '').trim().toLowerCase() !== 'user') {
305
+ return false;
306
+ }
307
+
308
+ if (getTimelineMessageClientRequestId(message) !== clientRequestId) {
309
+ return false;
310
+ }
311
+
312
+ if (String(message?.content || '') !== String(payload.text || '')) {
313
+ return false;
314
+ }
315
+
316
+ const actualContentBlocks = JSON.stringify(buildComparableUserMessageContentBlocks(message));
317
+ return actualContentBlocks === expectedContentBlocks;
318
+ });
319
+ }
320
+
321
+ function normalizeAcpModeState(value) {
322
+ if (!value || typeof value !== 'object') {
323
+ return null;
324
+ }
325
+
326
+ const availableModes = Array.isArray(value.availableModes)
327
+ ? value.availableModes
328
+ .filter((mode) => mode && typeof mode === 'object' && String(mode.id || '').trim())
329
+ .map((mode) => ({
330
+ id: String(mode.id).trim(),
331
+ name: String(mode.name || mode.id || '').trim() || String(mode.id).trim(),
332
+ description: mode.description == null ? null : String(mode.description)
333
+ }))
334
+ : [];
335
+ const currentModeId = typeof value.currentModeId === 'string' && value.currentModeId.trim()
336
+ ? value.currentModeId.trim()
337
+ : null;
338
+
339
+ if (!availableModes.length && !currentModeId) {
340
+ return null;
341
+ }
342
+
343
+ return {
344
+ availableModes,
345
+ currentModeId: currentModeId || availableModes[0]?.id || null
346
+ };
347
+ }
348
+
349
+ function normalizeAcpAvailableCommands(value) {
350
+ const commands = Array.isArray(value?.availableCommands)
351
+ ? value.availableCommands
352
+ : Array.isArray(value)
353
+ ? value
354
+ : [];
355
+
356
+ return commands
357
+ .filter((command) => command && typeof command === 'object' && String(command.name || '').trim())
358
+ .map((command) => ({
359
+ name: String(command.name).trim(),
360
+ description: command.description == null ? '' : String(command.description),
361
+ input: command.input && typeof command.input === 'object' ? cloneJsonValue(command.input) : null,
362
+ source: 'acp',
363
+ namespace: 'acp',
364
+ type: 'acp'
365
+ }));
366
+ }
367
+
368
+ function mergeToolCallSnapshot(existingSnapshot = null, payload = {}) {
369
+ const nextSnapshot = existingSnapshot && typeof existingSnapshot === 'object'
370
+ ? { ...existingSnapshot }
371
+ : {};
372
+
373
+ if (payload.title != null) {
374
+ nextSnapshot.title = String(payload.title);
375
+ }
376
+
377
+ if (payload.kind != null) {
378
+ nextSnapshot.kind = String(payload.kind);
379
+ }
380
+
381
+ if (payload.status != null) {
382
+ nextSnapshot.status = String(payload.status);
383
+ }
384
+
385
+ if (payload.rawInput !== undefined) {
386
+ nextSnapshot.rawInput = cloneJsonValue(payload.rawInput);
387
+ }
388
+
389
+ if (payload.rawOutput !== undefined) {
390
+ nextSnapshot.rawOutput = cloneJsonValue(payload.rawOutput);
391
+ }
392
+
393
+ if (Array.isArray(payload.contentBlocks)) {
394
+ nextSnapshot.content = cloneJsonValue(payload.contentBlocks);
395
+ }
396
+
397
+ if (Array.isArray(payload.locations)) {
398
+ nextSnapshot.locations = cloneJsonValue(payload.locations);
399
+ }
400
+
401
+ return Object.keys(nextSnapshot).length > 0 ? nextSnapshot : null;
402
+ }
403
+
239
404
  function collectClaudeToolResults(rawMessages) {
240
405
  const toolResults = new Map();
241
406
  for (const msg of rawMessages || []) {
@@ -279,7 +444,6 @@ function ensureStreamingTextMessage(messages, {
279
444
  }) {
280
445
  const index = findLatestTimelineMessageIndex(messages, (message) => (
281
446
  message?.eventMessageId === messageId &&
282
- !!message?.isStreaming &&
283
447
  !message.isToolUse &&
284
448
  !!message.isThinking === !!isThinking
285
449
  ));
@@ -310,6 +474,40 @@ function ensureStreamingTextMessage(messages, {
310
474
  return { messages, index: messages.length - 1 };
311
475
  }
312
476
 
477
+ function ensureConversationMessage(messages, {
478
+ messageId,
479
+ timestamp,
480
+ provider,
481
+ role = 'assistant'
482
+ }) {
483
+ const index = findLatestTimelineMessageIndex(messages, (message) => (
484
+ message?.eventMessageId === messageId &&
485
+ !message.isToolUse &&
486
+ !message.isThinking &&
487
+ String(message?.type || '').trim().toLowerCase() === String(role || 'assistant').trim().toLowerCase()
488
+ ));
489
+
490
+ if (index >= 0) {
491
+ const existing = { ...messages[index] };
492
+ if (provider) existing.provider = provider;
493
+ if (timestamp && !existing.timestamp) existing.timestamp = timestamp;
494
+ messages[index] = existing;
495
+ return { messages, index };
496
+ }
497
+
498
+ messages.push({
499
+ type: role,
500
+ content: '',
501
+ timestamp,
502
+ provider,
503
+ isStreaming: false,
504
+ eventMessageId: messageId,
505
+ contentBlocks: []
506
+ });
507
+
508
+ return { messages, index: messages.length - 1 };
509
+ }
510
+
313
511
  function ensureToolMessage(messages, {
314
512
  toolCallId,
315
513
  toolName,
@@ -458,14 +656,41 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
458
656
  const payload = event.payload || {};
459
657
 
460
658
  switch (event.kind) {
461
- case CONVERSATION_EVENT_KINDS.USER_MESSAGE:
659
+ case CONVERSATION_EVENT_KINDS.USER_MESSAGE: {
660
+ const clientRequestId = getConversationEventClientRequestId(event);
661
+ const optimisticIndex = findMatchingOptimisticUserMessageIndex(nextMessages, event, payload);
662
+ const normalizedContentBlocks = normalizeMessageContentBlocks(payload.contentBlocks);
663
+
664
+ if (optimisticIndex >= 0) {
665
+ const existingMessage = nextMessages[optimisticIndex] || {};
666
+ const nextContentBlocks = Array.isArray(existingMessage.images) && existingMessage.images.length > 0
667
+ ? normalizedContentBlocks.filter((block) => block?.type !== 'image')
668
+ : normalizedContentBlocks;
669
+
670
+ nextMessages[optimisticIndex] = {
671
+ ...existingMessage,
672
+ type: 'user',
673
+ content: payload.text || existingMessage.content || '',
674
+ timestamp: existingMessage.timestamp || timestamp,
675
+ provider,
676
+ contentBlocks: nextContentBlocks,
677
+ clientRequestId: clientRequestId || existingMessage.clientRequestId || null,
678
+ eventId: event.eventId || existingMessage.eventId,
679
+ id: existingMessage.id || event.eventId || existingMessage.id
680
+ };
681
+ break;
682
+ }
683
+
462
684
  nextMessages.push({
463
685
  type: 'user',
464
686
  content: payload.text || '',
465
687
  timestamp,
466
- provider
688
+ provider,
689
+ contentBlocks: normalizedContentBlocks,
690
+ clientRequestId
467
691
  });
468
692
  break;
693
+ }
469
694
 
470
695
  case CONVERSATION_EVENT_KINDS.ASSISTANT_TEXT_START: {
471
696
  const messageId = payload.messageId || event.extensions?.messageId || createEventId('assistant_text');
@@ -496,6 +721,26 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
496
721
  break;
497
722
  }
498
723
 
724
+ case CONVERSATION_EVENT_KINDS.ASSISTANT_CONTENT_BLOCK: {
725
+ const messageId = payload.messageId || event.extensions?.messageId || createEventId('assistant_content');
726
+ const { index } = ensureConversationMessage(nextMessages, {
727
+ messageId,
728
+ timestamp,
729
+ provider,
730
+ role: 'assistant'
731
+ });
732
+ const target = { ...nextMessages[index] };
733
+ const nextBlocks = Array.isArray(target.contentBlocks) ? [...target.contentBlocks] : [];
734
+ if (payload.contentBlock && typeof payload.contentBlock === 'object') {
735
+ nextBlocks.push(cloneJsonValue(payload.contentBlock));
736
+ }
737
+ target.contentBlocks = nextBlocks;
738
+ target.provider = target.provider || provider;
739
+ target.timestamp = target.timestamp || timestamp;
740
+ nextMessages[index] = target;
741
+ break;
742
+ }
743
+
499
744
  case CONVERSATION_EVENT_KINDS.REASONING_START: {
500
745
  const messageId = payload.messageId || event.extensions?.messageId || createEventId('reasoning');
501
746
  ensureStreamingTextMessage(nextMessages, { messageId, timestamp, provider, isThinking: true });
@@ -534,7 +779,11 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
534
779
  timestamp,
535
780
  provider
536
781
  });
537
- nextMessages[index] = { ...nextMessages[index], toolName: payload.toolName || nextMessages[index].toolName };
782
+ nextMessages[index] = {
783
+ ...nextMessages[index],
784
+ toolName: payload.toolName || nextMessages[index].toolName,
785
+ acpToolCall: mergeToolCallSnapshot(nextMessages[index].acpToolCall, payload)
786
+ };
538
787
  break;
539
788
  }
540
789
 
@@ -548,8 +797,13 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
548
797
  });
549
798
  nextMessages[index] = {
550
799
  ...nextMessages[index],
551
- toolInput: stringifyToolContent(payload.input || ''),
552
- provider: nextMessages[index].provider || provider
800
+ toolInput: payload.input !== undefined
801
+ ? stringifyToolContent(payload.input)
802
+ : payload.rawInput !== undefined
803
+ ? stringifyToolContent(payload.rawInput)
804
+ : nextMessages[index].toolInput,
805
+ provider: nextMessages[index].provider || provider,
806
+ acpToolCall: mergeToolCallSnapshot(nextMessages[index].acpToolCall, payload)
553
807
  };
554
808
  break;
555
809
  }
@@ -563,7 +817,12 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
563
817
  timestamp,
564
818
  provider
565
819
  });
566
- nextMessages[index] = { ...nextMessages[index], toolName: payload.toolName || nextMessages[index].toolName };
820
+ nextMessages[index] = {
821
+ ...nextMessages[index],
822
+ toolName: payload.toolName || nextMessages[index].toolName,
823
+ toolEndedAt: timestamp,
824
+ acpToolCall: mergeToolCallSnapshot(nextMessages[index].acpToolCall, payload)
825
+ };
567
826
  break;
568
827
  }
569
828
 
@@ -578,17 +837,44 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
578
837
  nextMessages[index] = {
579
838
  ...nextMessages[index],
580
839
  toolResult: {
581
- content: stringifyToolContent(payload.content || ''),
840
+ content: stringifyToolContent(payload.content ?? ''),
582
841
  isError: !!payload.isError,
583
842
  toolUseResult: payload.toolUseResult || null,
843
+ rawOutput: payload.rawOutput,
844
+ contentBlocks: Array.isArray(payload.contentBlocks) ? cloneJsonValue(payload.contentBlocks) : null,
845
+ status: payload.status || null,
584
846
  timestamp
585
847
  },
586
848
  toolError: !!payload.isError,
587
- provider: nextMessages[index].provider || provider
849
+ toolEndedAt: nextMessages[index].toolEndedAt || timestamp,
850
+ provider: nextMessages[index].provider || provider,
851
+ acpToolCall: mergeToolCallSnapshot(nextMessages[index].acpToolCall, payload)
588
852
  };
589
853
  break;
590
854
  }
591
855
 
856
+ case CONVERSATION_EVENT_KINDS.PLAN_UPDATE: {
857
+ const planEntries = Array.isArray(payload.entries) ? cloneJsonValue(payload.entries) : [];
858
+ const index = findLatestTimelineMessageIndex(nextMessages, (message) => !!message?.isPlanUpdate);
859
+ const nextPlanMessage = {
860
+ ...(index >= 0 ? nextMessages[index] : {}),
861
+ id: (index >= 0 ? nextMessages[index]?.id : null) || `plan:${event.sessionId || provider}`,
862
+ type: 'assistant',
863
+ content: String(payload.message || 'Plan updated'),
864
+ timestamp,
865
+ provider,
866
+ isPlanUpdate: true,
867
+ planEntries
868
+ };
869
+
870
+ if (index >= 0) {
871
+ nextMessages[index] = nextPlanMessage;
872
+ } else {
873
+ nextMessages.push(nextPlanMessage);
874
+ }
875
+ break;
876
+ }
877
+
592
878
  case CONVERSATION_EVENT_KINDS.APPROVAL_REQUEST:
593
879
  if (payload.message) {
594
880
  nextMessages.push({
@@ -641,6 +927,8 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
641
927
  break;
642
928
 
643
929
  case CONVERSATION_EVENT_KINDS.SESSION_STATE_CHANGED:
930
+ case CONVERSATION_EVENT_KINDS.MODE_UPDATE:
931
+ case CONVERSATION_EVENT_KINDS.AVAILABLE_COMMANDS_UPDATE:
644
932
  case CONVERSATION_EVENT_KINDS.ARTIFACT_CREATED:
645
933
  default:
646
934
  break;
@@ -661,6 +949,39 @@ export function conversationEventsToTimelineMessages(events = [], sessionProvide
661
949
  );
662
950
  }
663
951
 
952
+ export function extractAcpSessionMetadataFromConversationEvents(events = []) {
953
+ const metadata = {
954
+ modeState: null,
955
+ availableCommands: []
956
+ };
957
+
958
+ for (const event of Array.isArray(events) ? events : []) {
959
+ if (!isConversationEvent(event)) {
960
+ continue;
961
+ }
962
+
963
+ if (event.kind === CONVERSATION_EVENT_KINDS.MODE_UPDATE) {
964
+ const nextModeState = normalizeAcpModeState(event.payload || {});
965
+ if (nextModeState) {
966
+ metadata.modeState = metadata.modeState
967
+ ? {
968
+ availableModes: nextModeState.availableModes.length > 0
969
+ ? nextModeState.availableModes
970
+ : metadata.modeState.availableModes,
971
+ currentModeId: nextModeState.currentModeId || metadata.modeState.currentModeId || null
972
+ }
973
+ : nextModeState;
974
+ }
975
+ }
976
+
977
+ if (event.kind === CONVERSATION_EVENT_KINDS.AVAILABLE_COMMANDS_UPDATE) {
978
+ metadata.availableCommands = normalizeAcpAvailableCommands(event.payload || {});
979
+ }
980
+ }
981
+
982
+ return metadata;
983
+ }
984
+
664
985
  function inferRealtimeMessageId(payload, provider, sessionId, kindPrefix = 'assistant_text') {
665
986
  const data = payload?.data || {};
666
987
  const idCandidate =
@@ -736,7 +1057,7 @@ export function normalizeRealtimePayloadToConversationEvents(payload, fallbackPr
736
1057
  const rawRef = { type: payload.type };
737
1058
 
738
1059
  if (payload.type === 'session-created') {
739
- return [
1060
+ const events = [
740
1061
  createConversationEvent({
741
1062
  kind: CONVERSATION_EVENT_KINDS.SESSION_STATE_CHANGED,
742
1063
  provider,
@@ -746,6 +1067,22 @@ export function normalizeRealtimePayloadToConversationEvents(payload, fallbackPr
746
1067
  rawRef
747
1068
  })
748
1069
  ];
1070
+
1071
+ const initialModeState = normalizeAcpModeState(payload.modes || null);
1072
+ if (initialModeState) {
1073
+ events.push(
1074
+ createConversationEvent({
1075
+ kind: CONVERSATION_EVENT_KINDS.MODE_UPDATE,
1076
+ provider,
1077
+ sessionId,
1078
+ timestamp,
1079
+ payload: initialModeState,
1080
+ rawRef
1081
+ })
1082
+ );
1083
+ }
1084
+
1085
+ return events;
749
1086
  }
750
1087
 
751
1088
  if (payload.type === 'claude-permission-request') {