tabminal 3.0.17 → 3.0.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabminal",
3
- "version": "3.0.17",
3
+ "version": "3.0.19",
4
4
  "description": "Tab(ter)minal, a Cloud-Native terminal and ACP agent workspace for desktop, tablet, and phone.",
5
5
  "type": "module",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -108,6 +108,8 @@ const FILE_VERSION_CHECK_INTERVAL_MS = 3000;
108
108
  const AGENT_TRANSCRIPT_INITIAL_VISIBLE_BLOCKS = 100;
109
109
  const AGENT_TRANSCRIPT_WINDOW_STEP = 50;
110
110
  const AGENT_TRANSCRIPT_FOLLOW_LATEST_TOLERANCE = 5;
111
+ const AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS = 300;
112
+ const AGENT_TRANSCRIPT_AUTH_SYNC_DEBOUNCE_MS = 300;
111
113
  const WORKSPACE_TAB_TITLE_MAX_LENGTH = 20;
112
114
  const MAIN_SERVER_ID = 'main';
113
115
  const RUNTIME_BOOT_ID_STORAGE_KEY = 'tabminal_runtime_boot_id';
@@ -1028,6 +1030,7 @@ class EditorManager {
1028
1030
  this.agentEmbeddedEditors = [];
1029
1031
  this.agentEmbeddedTerminals = new Map();
1030
1032
  this.agentTranscriptLayout = null;
1033
+ this.agentRenderQueue = new Map();
1031
1034
  this.pdfPreviewState = {
1032
1035
  path: '',
1033
1036
  sessionKey: '',
@@ -1063,7 +1066,6 @@ class EditorManager {
1063
1066
  this.initMonaco();
1064
1067
  this.loadIconMap();
1065
1068
  this.agentTimestampTimer = window.setInterval(() => {
1066
- this.refreshAgentTimelineTimestamps();
1067
1069
  this.refreshAgentUsageHud();
1068
1070
  }, 1000);
1069
1071
  this.fileVersionCheckTimer = window.setInterval(() => {
@@ -4264,12 +4266,6 @@ class EditorManager {
4264
4266
  if (!(body instanceof HTMLElement) || !message?.text) {
4265
4267
  return;
4266
4268
  }
4267
- if (agentTab?.busy) {
4268
- return;
4269
- }
4270
- if (isAgentMessageStreaming(agentTab, message)) {
4271
- return;
4272
- }
4273
4269
  const session = this.currentSession;
4274
4270
  if (!session) {
4275
4271
  return;
@@ -4289,10 +4285,7 @@ class EditorManager {
4289
4285
 
4290
4286
  try {
4291
4287
  const { renderer } = await loadMarkdownPreviewBundle();
4292
- if (
4293
- String(message.text || '') !== sourceText
4294
- || isAgentMessageStreaming(agentTab, message)
4295
- ) {
4288
+ if (String(message.text || '') !== sourceText) {
4296
4289
  return;
4297
4290
  }
4298
4291
  const rendered = renderer.render(sourceText);
@@ -5624,13 +5617,133 @@ class EditorManager {
5624
5617
  removeAgentTab(agentTabKey);
5625
5618
  }
5626
5619
 
5627
- renderAgentPanel(agentTab, options = {}) {
5628
- const previousLayout = this.captureAgentTranscriptLayout();
5629
- const previousScrollTop = previousLayout?.scrollTop || 0;
5630
- const wasNearBottom = this.isAgentTranscriptLayoutNearBottom(
5631
- previousLayout,
5632
- 36
5633
- );
5620
+ getOrCreateAgentRenderState(agentTabKey) {
5621
+ let renderState = this.agentRenderQueue.get(agentTabKey);
5622
+ if (!renderState) {
5623
+ renderState = {
5624
+ timer: 0,
5625
+ inFlight: false,
5626
+ rerenderRequested: false,
5627
+ full: false,
5628
+ authoritativeSync: false,
5629
+ delayMs: 0,
5630
+ dirtyKeys: new Set()
5631
+ };
5632
+ this.agentRenderQueue.set(agentTabKey, renderState);
5633
+ }
5634
+ return renderState;
5635
+ }
5636
+
5637
+ clearScheduledAgentPanelRender(agentTabKey) {
5638
+ const renderState = this.agentRenderQueue.get(agentTabKey);
5639
+ if (!renderState) {
5640
+ return;
5641
+ }
5642
+ if (renderState.timer) {
5643
+ clearTimeout(renderState.timer);
5644
+ }
5645
+ this.agentRenderQueue.delete(agentTabKey);
5646
+ }
5647
+
5648
+ scheduleQueuedAgentPanelRender(agentTabKey, delayMs = 0) {
5649
+ const renderState = this.agentRenderQueue.get(agentTabKey);
5650
+ if (!renderState) {
5651
+ return;
5652
+ }
5653
+ if (renderState.timer) {
5654
+ clearTimeout(renderState.timer);
5655
+ }
5656
+ renderState.delayMs = Math.max(0, Math.floor(delayMs));
5657
+ renderState.timer = window.setTimeout(() => {
5658
+ renderState.timer = 0;
5659
+ void this.flushQueuedAgentPanelRender(agentTabKey);
5660
+ }, renderState.delayMs);
5661
+ }
5662
+
5663
+ scheduleAgentPanelRender(agentTab, options = {}) {
5664
+ if (!agentTab?.key) {
5665
+ return;
5666
+ }
5667
+ const renderState = this.getOrCreateAgentRenderState(agentTab.key);
5668
+ if (options.full) {
5669
+ renderState.full = true;
5670
+ }
5671
+ if (options.authoritativeSync) {
5672
+ renderState.authoritativeSync = true;
5673
+ }
5674
+ if (options.dirtyKey) {
5675
+ renderState.dirtyKeys.add(String(options.dirtyKey));
5676
+ }
5677
+ if (renderState.inFlight) {
5678
+ renderState.rerenderRequested = true;
5679
+ }
5680
+ const delayMs = renderState.full
5681
+ ? 0
5682
+ : (
5683
+ Number.isFinite(options.delayMs)
5684
+ ? options.delayMs
5685
+ : AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS
5686
+ );
5687
+ this.scheduleQueuedAgentPanelRender(agentTab.key, delayMs);
5688
+ }
5689
+
5690
+ async flushQueuedAgentPanelRender(agentTabKey) {
5691
+ const renderState = this.agentRenderQueue.get(agentTabKey);
5692
+ if (!renderState) {
5693
+ return;
5694
+ }
5695
+ if (renderState.inFlight) {
5696
+ renderState.rerenderRequested = true;
5697
+ return;
5698
+ }
5699
+ renderState.inFlight = true;
5700
+ const pendingFull = renderState.full;
5701
+ const pendingAuthoritativeSync = renderState.authoritativeSync;
5702
+ renderState.full = false;
5703
+ renderState.authoritativeSync = false;
5704
+ renderState.rerenderRequested = false;
5705
+ renderState.dirtyKeys.clear();
5706
+ try {
5707
+ const agentTab = state.agentTabs.get(agentTabKey);
5708
+ if (!agentTab) {
5709
+ return;
5710
+ }
5711
+ if (isAgentTabVisible(agentTab)) {
5712
+ if (pendingFull) {
5713
+ this.renderAgentPanel(agentTab, {
5714
+ reason: 'queued-full'
5715
+ });
5716
+ } else {
5717
+ this.renderAgentTranscript(agentTab, {
5718
+ reason: 'queued-transcript'
5719
+ });
5720
+ }
5721
+ }
5722
+ if (pendingAuthoritativeSync && agentTab.server?.isAuthenticated) {
5723
+ try {
5724
+ await syncAgentsForServer(agentTab.server, { force: true });
5725
+ } catch {
5726
+ // Ignore transient authority sync failures. The next
5727
+ // heartbeat or state refresh will reconcile.
5728
+ }
5729
+ }
5730
+ } finally {
5731
+ renderState.inFlight = false;
5732
+ if (
5733
+ renderState.full
5734
+ || renderState.authoritativeSync
5735
+ || renderState.dirtyKeys.size > 0
5736
+ || renderState.rerenderRequested
5737
+ ) {
5738
+ const delayMs = renderState.full
5739
+ ? 0
5740
+ : AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS;
5741
+ this.scheduleQueuedAgentPanelRender(agentTabKey, delayMs);
5742
+ }
5743
+ }
5744
+ }
5745
+
5746
+ renderAgentPanelChrome(agentTab) {
5634
5747
  this.agentHeader.textContent = '';
5635
5748
  this.agentMeta.textContent = '';
5636
5749
  this.renderAgentUsageHud(agentTab);
@@ -5687,14 +5800,33 @@ class EditorManager {
5687
5800
  }
5688
5801
 
5689
5802
  this.renderAgentComposerAttachments(agentTab);
5803
+ this.agentTools.innerHTML = '';
5804
+ this.agentTools.style.display = 'none';
5805
+ this.agentPermissions.innerHTML = '';
5806
+ this.agentPermissions.style.display = 'none';
5807
+
5808
+ this.agentPrompt.disabled = false;
5809
+ this.setAgentPromptValue(agentTab.promptDraft || '', agentTab);
5810
+ this.agentPrompt.placeholder = buildAgentPromptPlaceholder(agentTab);
5811
+ this.updateAgentComposerActions(agentTab);
5812
+ this.refreshAgentUsageHud();
5813
+ }
5690
5814
 
5815
+ renderAgentTranscript(agentTab, options = {}) {
5816
+ const previousLayout = this.captureAgentTranscriptLayout();
5817
+ const previousScrollTop = previousLayout?.scrollTop || 0;
5818
+ const wasNearBottom = this.isAgentTranscriptLayoutNearBottom(
5819
+ previousLayout,
5820
+ 36
5821
+ );
5691
5822
  const timeline = getAgentTimelineItems(agentTab);
5692
- const shouldPinToBottom = wasNearBottom || (
5823
+ const isNearLatestWindow = isAgentTranscriptWindowNearLatest(
5824
+ agentTab,
5825
+ timeline.length
5826
+ );
5827
+ const shouldPinToBottom = !options.preserveTranscriptAnchor && (
5693
5828
  agentTab.scrollToBottomOnNextRender
5694
- && isAgentTranscriptWindowNearLatest(
5695
- agentTab,
5696
- timeline.length
5697
- )
5829
+ || (wasNearBottom && isNearLatestWindow)
5698
5830
  );
5699
5831
  const transcriptWindow = getAgentTranscriptWindow(
5700
5832
  agentTab,
@@ -5782,20 +5914,14 @@ class EditorManager {
5782
5914
  }
5783
5915
  this.updateAgentScrollBottomButton();
5784
5916
  this.rememberAgentTranscriptLayout();
5785
- this.agentTools.innerHTML = '';
5786
- this.agentTools.style.display = 'none';
5787
- this.agentPermissions.innerHTML = '';
5788
- this.agentPermissions.style.display = 'none';
5789
-
5790
- this.agentPrompt.disabled = false;
5791
- this.setAgentPromptValue(agentTab.promptDraft || '', agentTab);
5792
- this.agentPrompt.placeholder = buildAgentPromptPlaceholder(agentTab);
5793
- this.updateAgentComposerActions(agentTab);
5794
- this.refreshAgentTimelineTimestamps();
5795
- this.refreshAgentUsageHud();
5796
5917
  this.scheduleAgentTranscriptViewportUpdate(shouldPinToBottom);
5797
5918
  }
5798
5919
 
5920
+ renderAgentPanel(agentTab, options = {}) {
5921
+ this.renderAgentPanelChrome(agentTab);
5922
+ this.renderAgentTranscript(agentTab, options);
5923
+ }
5924
+
5799
5925
  buildAgentTimelineNode(agentTab, entry, timelineIndex) {
5800
5926
  if (!entry) {
5801
5927
  return null;
@@ -5939,20 +6065,6 @@ class EditorManager {
5939
6065
  }
5940
6066
  }
5941
6067
 
5942
- refreshAgentTimelineTimestamps() {
5943
- if (!this.agentContainer || this.agentContainer.style.display === 'none') {
5944
- return;
5945
- }
5946
- const timestamps = this.agentContainer.querySelectorAll(
5947
- '.agent-message-time[data-created-at]'
5948
- );
5949
- for (const node of timestamps) {
5950
- const createdAt = String(node.dataset.createdAt || '').trim();
5951
- if (!createdAt) continue;
5952
- node.textContent = getAgentMessageTimeLabel({ createdAt });
5953
- }
5954
- }
5955
-
5956
6068
  refreshAgentUsageHud() {
5957
6069
  const activeTab = getActiveAgentTab();
5958
6070
  if (!activeTab || !this.agentUsageHud) return;
@@ -6192,9 +6304,7 @@ class EditorManager {
6192
6304
  const item = document.createElement('div');
6193
6305
  item.className = 'agent-message agent-plan-history';
6194
6306
  item.appendChild(buildAgentTimelineHeader(
6195
- buildAgentTimelineRoleLabel(agentTab, 'plan'),
6196
- getAgentMessageTimeLabel(planEntry),
6197
- planEntry.createdAt || ''
6307
+ buildAgentTimelineRoleLabel(agentTab, 'plan')
6198
6308
  ));
6199
6309
  const body = document.createElement('div');
6200
6310
  body.className = 'agent-plan-history-body';
@@ -6222,9 +6332,7 @@ class EditorManager {
6222
6332
  item.className = `agent-message ${message.role} ${message.kind}`;
6223
6333
 
6224
6334
  item.appendChild(buildAgentTimelineHeader(
6225
- getAgentMessageRoleLabel(agentTab, message),
6226
- getAgentMessageTimeLabel(message),
6227
- message.createdAt || ''
6335
+ getAgentMessageRoleLabel(agentTab, message)
6228
6336
  ));
6229
6337
  const attachments = buildAgentMessageAttachmentsNode(
6230
6338
  message.attachments
@@ -6241,11 +6349,7 @@ class EditorManager {
6241
6349
  && message.kind === 'message'
6242
6350
  ) {
6243
6351
  const cachedMarkdown = getAgentMessageMarkdownCache(message);
6244
- if (
6245
- !agentTab?.busy
6246
- && !isAgentMessageStreaming(agentTab, message)
6247
- && cachedMarkdown
6248
- ) {
6352
+ if (cachedMarkdown) {
6249
6353
  body.classList.add('markdown');
6250
6354
  body.innerHTML = cachedMarkdown;
6251
6355
  } else {
@@ -6274,10 +6378,13 @@ class EditorManager {
6274
6378
  );
6275
6379
  node.className = `agent-tool-call state-${toolStatusClass}`;
6276
6380
 
6381
+ const status = document.createElement('span');
6382
+ status.className = `agent-status-pill ${toolStatusClass}`;
6383
+ status.textContent = getAgentStatusLabel(toolStatusClass);
6384
+
6277
6385
  node.appendChild(buildAgentTimelineHeader(
6278
6386
  buildAgentTimelineRoleLabel(agentTab, 'tool'),
6279
- getAgentMessageTimeLabel(toolCall),
6280
- toolCall.createdAt || ''
6387
+ status
6281
6388
  ));
6282
6389
 
6283
6390
  const header = document.createElement('div');
@@ -6287,12 +6394,7 @@ class EditorManager {
6287
6394
  title.className = 'agent-tool-call-title';
6288
6395
  title.textContent = getAgentToolTitle(toolCall);
6289
6396
 
6290
- const status = document.createElement('span');
6291
- status.className = `agent-status-pill ${toolStatusClass}`;
6292
- status.textContent = getAgentStatusLabel(toolStatusClass);
6293
-
6294
6397
  header.appendChild(title);
6295
- header.appendChild(status);
6296
6398
  node.appendChild(header);
6297
6399
 
6298
6400
  const meta = document.createElement('div');
@@ -6378,6 +6480,10 @@ class EditorManager {
6378
6480
  );
6379
6481
  card.className = `agent-permission-card state-${permissionStatusClass}`;
6380
6482
 
6483
+ const status = document.createElement('span');
6484
+ status.className = `agent-status-pill ${permissionStatusClass}`;
6485
+ status.textContent = getAgentPermissionStatusLabel(permission);
6486
+
6381
6487
  card.appendChild(buildAgentTimelineHeader(
6382
6488
  buildAgentTimelineRoleLabel(
6383
6489
  agentTab,
@@ -6385,8 +6491,7 @@ class EditorManager {
6385
6491
  ? 'permission request'
6386
6492
  : 'permission'
6387
6493
  ),
6388
- getAgentMessageTimeLabel(permission),
6389
- permission.createdAt || ''
6494
+ status
6390
6495
  ));
6391
6496
 
6392
6497
  const titleRow = document.createElement('div');
@@ -6396,12 +6501,7 @@ class EditorManager {
6396
6501
  title.className = 'agent-permission-title';
6397
6502
  title.textContent = getAgentPermissionTitle(permission);
6398
6503
 
6399
- const status = document.createElement('span');
6400
- status.className = `agent-status-pill ${permissionStatusClass}`;
6401
- status.textContent = getAgentPermissionStatusLabel(permission);
6402
-
6403
6504
  titleRow.appendChild(title);
6404
- titleRow.appendChild(status);
6405
6505
  card.appendChild(titleRow);
6406
6506
 
6407
6507
  const meta = document.createElement('div');
@@ -9270,11 +9370,32 @@ class AgentTab {
9270
9370
  ) || null;
9271
9371
  }
9272
9372
 
9273
- notifyUi() {
9373
+ notifyUi(options = {}) {
9274
9374
  const session = this.getLinkedSession();
9275
9375
  if (!session) return;
9276
- session.updateTabUI();
9277
- refreshWorkspaceIfSessionActive(session);
9376
+ const shouldUpdateTabs = options.updateTabs !== false;
9377
+ if (shouldUpdateTabs) {
9378
+ session.updateTabUI();
9379
+ }
9380
+ if (state.activeSessionKey !== session.key) {
9381
+ return;
9382
+ }
9383
+ if (editorManager.currentSession?.key !== session.key) {
9384
+ editorManager.switchTo(session);
9385
+ return;
9386
+ }
9387
+ if (shouldUpdateTabs) {
9388
+ editorManager.renderEditorTabs();
9389
+ }
9390
+ if (editorManager.getActiveWorkspaceTabKey(session) !== this.key) {
9391
+ return;
9392
+ }
9393
+ editorManager.scheduleAgentPanelRender(this, {
9394
+ full: options.full !== false,
9395
+ delayMs: options.delayMs,
9396
+ dirtyKey: options.dirtyKey || '',
9397
+ authoritativeSync: !!options.authoritativeSync
9398
+ });
9278
9399
  }
9279
9400
 
9280
9401
  update(data) {
@@ -9456,10 +9577,14 @@ class AgentTab {
9456
9577
 
9457
9578
  handleMessage(message) {
9458
9579
  const wasBusy = this.busy;
9580
+ let notifyOptions = { full: true };
9459
9581
  switch (message.type) {
9460
9582
  case 'snapshot':
9461
9583
  this.update(message.tab || {});
9462
9584
  this.scrollToBottomOnNextRender = true;
9585
+ notifyOptions = {
9586
+ full: true
9587
+ };
9463
9588
  break;
9464
9589
  case 'message_open':
9465
9590
  this.#upsertMessage(message.message);
@@ -9470,6 +9595,12 @@ class AgentTab {
9470
9595
  ) {
9471
9596
  this.streamingAssistantStreamKey = message.message.streamKey;
9472
9597
  }
9598
+ notifyOptions = {
9599
+ full: false,
9600
+ delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
9601
+ dirtyKey: this.#getMessageRenderKey(message.message),
9602
+ updateTabs: false
9603
+ };
9473
9604
  break;
9474
9605
  case 'message_chunk':
9475
9606
  this.#appendChunk(message);
@@ -9480,9 +9611,15 @@ class AgentTab {
9480
9611
  ) {
9481
9612
  this.streamingAssistantStreamKey = message.streamKey;
9482
9613
  }
9614
+ notifyOptions = {
9615
+ full: false,
9616
+ delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
9617
+ dirtyKey: this.#getMessageRenderKey(message),
9618
+ updateTabs: false
9619
+ };
9483
9620
  break;
9484
9621
  case 'session_update':
9485
- this.#applySessionUpdate(message.update || {});
9622
+ notifyOptions = this.#applySessionUpdate(message.update || {});
9486
9623
  if (message.tab?.currentModeId || message.tab?.modeId) {
9487
9624
  this.currentModeId = message.tab.currentModeId
9488
9625
  || message.tab.modeId;
@@ -9511,6 +9648,14 @@ class AgentTab {
9511
9648
  )
9512
9649
  });
9513
9650
  }
9651
+ notifyOptions = {
9652
+ full: false,
9653
+ delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
9654
+ dirtyKey: this.#getPermissionRenderKey(
9655
+ message.permission?.id
9656
+ ),
9657
+ updateTabs: false
9658
+ };
9514
9659
  break;
9515
9660
  case 'permission_resolved': {
9516
9661
  const permission = this.permissions.get(message.permissionId);
@@ -9520,6 +9665,14 @@ class AgentTab {
9520
9665
  || permission.selectedOptionId
9521
9666
  || '';
9522
9667
  }
9668
+ notifyOptions = {
9669
+ full: false,
9670
+ delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
9671
+ dirtyKey: this.#getPermissionRenderKey(
9672
+ message.permissionId
9673
+ ),
9674
+ updateTabs: false
9675
+ };
9523
9676
  break;
9524
9677
  }
9525
9678
  case 'terminal_update':
@@ -9559,18 +9712,22 @@ class AgentTab {
9559
9712
  return;
9560
9713
  }
9561
9714
  }
9715
+ notifyOptions = { full: true };
9562
9716
  break;
9563
9717
  case 'usage_state':
9564
9718
  this.usage = this.#normalizeUsageState(message.usage);
9719
+ notifyOptions = { full: true };
9565
9720
  break;
9566
9721
  case 'status':
9567
9722
  this.status = message.status || this.status;
9568
9723
  this.busy = !!message.busy;
9569
9724
  this.errorMessage = message.errorMessage || '';
9725
+ notifyOptions = { full: true };
9570
9726
  break;
9571
9727
  case 'complete':
9572
9728
  this.status = message.status || 'ready';
9573
9729
  this.busy = !!message.busy;
9730
+ notifyOptions = { full: true };
9574
9731
  break;
9575
9732
  default:
9576
9733
  break;
@@ -9611,7 +9768,15 @@ class AgentTab {
9611
9768
  this.needsAttention = false;
9612
9769
  }
9613
9770
  this.#syncBusyWatchdog();
9614
- this.notifyUi();
9771
+ if (wasBusy && !this.busy) {
9772
+ notifyOptions = {
9773
+ ...notifyOptions,
9774
+ full: true,
9775
+ authoritativeSync: true,
9776
+ delayMs: AGENT_TRANSCRIPT_AUTH_SYNC_DEBOUNCE_MS
9777
+ };
9778
+ }
9779
+ this.notifyUi(notifyOptions);
9615
9780
  if (shouldAutostartQueuedPrompt) {
9616
9781
  this.lastCompletedRunCounter = this.runCounter;
9617
9782
  void drainQueuedAgentPrompt(this);
@@ -9825,13 +9990,39 @@ class AgentTab {
9825
9990
  return this.timelineCounter;
9826
9991
  }
9827
9992
 
9993
+ #getMessageRenderKey(message = {}) {
9994
+ const role = String(message?.role || 'assistant');
9995
+ const kind = String(message?.kind || 'message');
9996
+ const identity = String(
9997
+ message?.id
9998
+ || message?.streamKey
9999
+ || ''
10000
+ ).trim();
10001
+ if (!identity) {
10002
+ return '';
10003
+ }
10004
+ return `message:${role}:${kind}:${identity}`;
10005
+ }
10006
+
10007
+ #getToolRenderKey(toolCallId = '') {
10008
+ const identity = String(toolCallId || '').trim();
10009
+ return identity ? `tool:${identity}` : '';
10010
+ }
10011
+
10012
+ #getPermissionRenderKey(permissionId = '') {
10013
+ const identity = String(permissionId || '').trim();
10014
+ return identity ? `permission:${identity}` : '';
10015
+ }
10016
+
9828
10017
  #findMessageIndex(candidate) {
9829
10018
  if (!candidate) return -1;
9830
10019
  if (candidate.id) {
9831
10020
  const byId = this.messages.findIndex(
9832
10021
  (message) => message.id === candidate.id
9833
10022
  );
9834
- return byId;
10023
+ if (byId !== -1) {
10024
+ return byId;
10025
+ }
9835
10026
  }
9836
10027
  if (!candidate.streamKey) return -1;
9837
10028
  for (let index = this.messages.length - 1; index >= 0; index -= 1) {
@@ -9859,7 +10050,12 @@ class AgentTab {
9859
10050
 
9860
10051
  const previous = this.messages[index];
9861
10052
  const nextMessage = this.#normalizeMessage(message, previous.order);
9862
- const mergedText = selectAgentMessageText(previous.text, nextMessage.text);
10053
+ const mergedText = (
10054
+ !previous.id
10055
+ && nextMessage.id
10056
+ ? (nextMessage.text || '')
10057
+ : selectAgentMessageText(previous.text, nextMessage.text)
10058
+ );
9863
10059
  this.messages[index] = {
9864
10060
  ...previous,
9865
10061
  ...nextMessage,
@@ -9883,16 +10079,22 @@ class AgentTab {
9883
10079
  existing.text = nextText;
9884
10080
  clearAgentMessageMarkdownCache(existing);
9885
10081
  }
10082
+ if (Number.isFinite(message?.order)) {
10083
+ existing.order = message.order;
10084
+ }
9886
10085
  return;
9887
10086
  }
9888
10087
 
9889
10088
  const nextMessage = this.#normalizeMessage({
9890
- id: crypto.randomUUID(),
10089
+ id: typeof message.id === 'string'
10090
+ ? message.id
10091
+ : '',
9891
10092
  streamKey: message.streamKey,
9892
10093
  role: message.role || 'assistant',
9893
10094
  kind: message.kind || 'message',
9894
10095
  text: message.text || '',
9895
- createdAt: new Date().toISOString()
10096
+ createdAt: new Date().toISOString(),
10097
+ order: message.order
9896
10098
  });
9897
10099
  clearAgentMessageMarkdownCache(nextMessage);
9898
10100
  this.messages.push(nextMessage);
@@ -9908,28 +10110,38 @@ class AgentTab {
9908
10110
  this.#normalizeTimelineEntry(update, previous?.order)
9909
10111
  );
9910
10112
  }
9911
- break;
10113
+ return {
10114
+ full: false,
10115
+ delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
10116
+ dirtyKey: this.#getToolRenderKey(update.toolCallId),
10117
+ updateTabs: false
10118
+ };
9912
10119
  case 'tool_call_update': {
9913
10120
  const previous = this.toolCalls.get(update.toolCallId) || {};
9914
10121
  this.toolCalls.set(update.toolCallId, {
9915
10122
  ...previous,
9916
10123
  ...this.#normalizeTimelineEntry(update, previous.order)
9917
10124
  });
9918
- break;
10125
+ return {
10126
+ full: false,
10127
+ delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
10128
+ dirtyKey: this.#getToolRenderKey(update.toolCallId),
10129
+ updateTabs: false
10130
+ };
9919
10131
  }
9920
10132
  case 'current_mode_update':
9921
10133
  this.currentModeId = update.currentModeId || update.modeId || '';
9922
- break;
10134
+ return { full: true };
9923
10135
  case 'available_commands_update':
9924
10136
  this.availableCommands = Array.isArray(update.availableCommands)
9925
10137
  ? update.availableCommands
9926
10138
  : [];
9927
- break;
10139
+ return { full: true };
9928
10140
  case 'config_option_update':
9929
10141
  this.configOptions = Array.isArray(update.configOptions)
9930
10142
  ? update.configOptions
9931
10143
  : [];
9932
- break;
10144
+ return { full: true };
9933
10145
  case 'plan':
9934
10146
  this.#applyPlanState(
9935
10147
  Array.isArray(update.entries)
@@ -9938,22 +10150,22 @@ class AgentTab {
9938
10150
  )
9939
10151
  : []
9940
10152
  );
9941
- break;
10153
+ return { full: true };
9942
10154
  case 'usage_update':
9943
10155
  this.usage = this.#normalizeUsageState({
9944
10156
  ...(this.usage || {}),
9945
10157
  ...update
9946
10158
  });
9947
- break;
10159
+ return { full: true };
9948
10160
  case 'session_info_update':
9949
10161
  if (typeof update.title === 'string') {
9950
10162
  this.title = update.title;
9951
10163
  } else if (update.title === null) {
9952
10164
  this.title = '';
9953
10165
  }
9954
- break;
10166
+ return { full: true };
9955
10167
  default:
9956
- break;
10168
+ return { full: true };
9957
10169
  }
9958
10170
  }
9959
10171
 
@@ -11365,11 +11577,7 @@ function getAgentMessageTimeLabel(message) {
11365
11577
  return timestamp.toLocaleString();
11366
11578
  }
11367
11579
 
11368
- function buildAgentTimelineHeader(
11369
- roleLabel,
11370
- timeLabel = '',
11371
- createdAt = ''
11372
- ) {
11580
+ function buildAgentTimelineHeader(roleLabel, trailingNode = null) {
11373
11581
  const header = document.createElement('div');
11374
11582
  header.className = 'agent-message-header';
11375
11583
 
@@ -11378,14 +11586,8 @@ function buildAgentTimelineHeader(
11378
11586
  role.textContent = roleLabel;
11379
11587
  header.appendChild(role);
11380
11588
 
11381
- if (timeLabel) {
11382
- const time = document.createElement('div');
11383
- time.className = 'agent-message-time';
11384
- time.textContent = timeLabel;
11385
- if (createdAt) {
11386
- time.dataset.createdAt = createdAt;
11387
- }
11388
- header.appendChild(time);
11589
+ if (trailingNode) {
11590
+ header.appendChild(trailingNode);
11389
11591
  }
11390
11592
 
11391
11593
  return header;
@@ -13588,18 +13790,51 @@ function upsertAgentTab(server, data) {
13588
13790
  return agentTab;
13589
13791
  }
13590
13792
 
13591
- function getAgentMessageComparableSignature(message) {
13592
- if (!message || typeof message !== 'object') {
13593
- return '';
13793
+ function buildComparableAgentTimelineTail(source, limit = 6) {
13794
+ const items = [];
13795
+ const messages = Array.isArray(source?.messages) ? source.messages : [];
13796
+ const toolCalls = Array.isArray(source?.toolCalls)
13797
+ ? source.toolCalls
13798
+ : Array.from(source?.toolCalls?.values?.() || []);
13799
+ const permissions = Array.isArray(source?.permissions)
13800
+ ? source.permissions
13801
+ : Array.from(source?.permissions?.values?.() || []);
13802
+ for (const message of messages) {
13803
+ items.push([
13804
+ 'message',
13805
+ Number.isFinite(message?.order) ? message.order : 0,
13806
+ String(message?.id || ''),
13807
+ String(message?.streamKey || ''),
13808
+ String(message?.role || ''),
13809
+ String(message?.kind || ''),
13810
+ hashUiText(message?.text || '')
13811
+ ]);
13812
+ }
13813
+ for (const toolCall of toolCalls) {
13814
+ items.push([
13815
+ 'tool',
13816
+ Number.isFinite(toolCall?.order) ? toolCall.order : 0,
13817
+ String(toolCall?.toolCallId || ''),
13818
+ String(toolCall?.status || ''),
13819
+ hashUiText(JSON.stringify(toolCall || null))
13820
+ ]);
13594
13821
  }
13595
- return JSON.stringify([
13596
- message.id || '',
13597
- message.order || 0,
13598
- message.role || '',
13599
- message.kind || '',
13600
- message.streamKey || '',
13601
- hashUiText(message.text || '')
13602
- ]);
13822
+ for (const permission of permissions) {
13823
+ items.push([
13824
+ 'permission',
13825
+ Number.isFinite(permission?.order) ? permission.order : 0,
13826
+ String(permission?.id || ''),
13827
+ String(permission?.status || ''),
13828
+ String(permission?.selectedOptionId || '')
13829
+ ]);
13830
+ }
13831
+ items.sort((left, right) => {
13832
+ if (left[1] !== right[1]) {
13833
+ return left[1] - right[1];
13834
+ }
13835
+ return String(left[0]).localeCompare(String(right[0]));
13836
+ });
13837
+ return JSON.stringify(items.slice(-limit));
13603
13838
  }
13604
13839
 
13605
13840
  function shouldApplyAuthoritativeAgentSnapshot(existing, data) {
@@ -13613,16 +13848,15 @@ function shouldApplyAuthoritativeAgentSnapshot(existing, data) {
13613
13848
  if (!Array.isArray(data.messages)) {
13614
13849
  return true;
13615
13850
  }
13616
- const currentMessages = Array.isArray(existing.messages)
13617
- ? existing.messages
13618
- : [];
13619
- if (data.messages.length !== currentMessages.length) {
13851
+ if (
13852
+ Array.isArray(data.messages)
13853
+ && Array.isArray(existing.messages)
13854
+ && data.messages.length !== existing.messages.length
13855
+ ) {
13620
13856
  return true;
13621
13857
  }
13622
- const incomingLast = data.messages[data.messages.length - 1] || null;
13623
- const currentLast = currentMessages[currentMessages.length - 1] || null;
13624
- return getAgentMessageComparableSignature(incomingLast)
13625
- !== getAgentMessageComparableSignature(currentLast);
13858
+ return buildComparableAgentTimelineTail(data)
13859
+ !== buildComparableAgentTimelineTail(existing);
13626
13860
  }
13627
13861
 
13628
13862
  function upsertAgentInventoryTab(server, data) {
@@ -13719,6 +13953,7 @@ function removeAgentTab(agentTabKey) {
13719
13953
  const agentTab = state.agentTabs.get(agentTabKey);
13720
13954
  if (!agentTab) return;
13721
13955
  const session = agentTab.getLinkedSession();
13956
+ editorManager?.clearScheduledAgentPanelRender?.(agentTabKey);
13722
13957
  agentTab.dispose();
13723
13958
  state.agentTabs.delete(agentTabKey);
13724
13959
 
@@ -319,6 +319,28 @@ function hasGhCopilotCliInstalled() {
319
319
  return ghCopilotCliInstalledCache;
320
320
  }
321
321
 
322
+ function getCodexAcpPlatformPackage() {
323
+ const suffixByPlatform = {
324
+ darwin: {
325
+ arm64: 'darwin-arm64',
326
+ x64: 'darwin-x64'
327
+ },
328
+ linux: {
329
+ arm64: 'linux-arm64',
330
+ x64: 'linux-x64'
331
+ },
332
+ win32: {
333
+ arm64: 'win32-arm64',
334
+ x64: 'win32-x64'
335
+ }
336
+ };
337
+ const platformSuffix = suffixByPlatform[process.platform]?.[process.arch];
338
+ if (!platformSuffix) {
339
+ return '';
340
+ }
341
+ return `@zed-industries/codex-acp-${platformSuffix}`;
342
+ }
343
+
322
344
  function readGhAuthToken() {
323
345
  if (typeof ghAuthTokenCache === 'string') {
324
346
  return ghAuthTokenCache;
@@ -479,6 +501,10 @@ function makeBuiltInDefinitions() {
479
501
  const hasGeminiBinary = commandExists('gemini');
480
502
  const hasCopilotBinary = commandExists('copilot');
481
503
  const hasGhCopilot = hasGhCopilotWrapper();
504
+ const codexAcpPlatformPackage = getCodexAcpPlatformPackage();
505
+ const codexAcpPlatformBinary = codexAcpPlatformPackage
506
+ ? codexAcpPlatformPackage.split('/').pop()
507
+ : '';
482
508
  const definitions = [
483
509
  {
484
510
  id: 'gemini',
@@ -499,8 +525,17 @@ function makeBuiltInDefinitions() {
499
525
  description: 'Codex ACP adapter',
500
526
  websiteUrl: 'https://openai.com/codex/',
501
527
  command: NPX_COMMAND,
502
- args: ['@zed-industries/codex-acp@latest'],
503
- commandLabel: 'npx @zed-industries/codex-acp@latest'
528
+ args: codexAcpPlatformPackage
529
+ ? [
530
+ '-p',
531
+ `${codexAcpPlatformPackage}@latest`,
532
+ codexAcpPlatformBinary
533
+ ]
534
+ : ['@zed-industries/codex-acp@latest'],
535
+ commandLabel: codexAcpPlatformPackage
536
+ ? `npx -p ${codexAcpPlatformPackage}@latest `
537
+ + `${codexAcpPlatformBinary}`
538
+ : 'npx @zed-industries/codex-acp@latest'
504
539
  },
505
540
  {
506
541
  id: 'claude',
@@ -1312,8 +1347,18 @@ export function createRestoreCaptureState(messages = [], options = {}) {
1312
1347
  const toolCalls = Array.isArray(options.toolCalls)
1313
1348
  ? options.toolCalls
1314
1349
  : [];
1350
+ const baselineMessages = normalizeAgentTranscriptMessages(messages);
1351
+ const baselineMessagesByStreamKey = new Map();
1352
+ for (const message of baselineMessages) {
1353
+ const streamKey = String(message?.streamKey || '').trim();
1354
+ if (!streamKey) {
1355
+ continue;
1356
+ }
1357
+ baselineMessagesByStreamKey.set(streamKey, message);
1358
+ }
1315
1359
  return {
1316
- baselineMessages: normalizeAgentTranscriptMessages(messages),
1360
+ baselineMessages,
1361
+ baselineMessagesByStreamKey,
1317
1362
  baselineToolCalls: new Map(
1318
1363
  toolCalls
1319
1364
  .map((entry) => normalizePersistedTimelineEntry(entry, 0))
@@ -1419,6 +1464,44 @@ function getRestoreCaptureStreamKey(capture, update, role, kind, text = '') {
1419
1464
  return streamKey;
1420
1465
  }
1421
1466
 
1467
+ function findMessageIndexByStreamKey(
1468
+ messages = [],
1469
+ streamKey = '',
1470
+ role = '',
1471
+ kind = '',
1472
+ options = {}
1473
+ ) {
1474
+ const normalizedStreamKey = String(streamKey || '').trim();
1475
+ if (!normalizedStreamKey) {
1476
+ return -1;
1477
+ }
1478
+ if (!options.searchWholeHistory) {
1479
+ const lastIndex = messages.length - 1;
1480
+ if (lastIndex < 0) {
1481
+ return -1;
1482
+ }
1483
+ const lastMessage = messages[lastIndex];
1484
+ return (
1485
+ String(lastMessage?.streamKey || '') === normalizedStreamKey
1486
+ && String(lastMessage?.role || '') === String(role || '')
1487
+ && String(lastMessage?.kind || '') === String(kind || '')
1488
+ )
1489
+ ? lastIndex
1490
+ : -1;
1491
+ }
1492
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1493
+ const message = messages[index];
1494
+ if (
1495
+ String(message?.streamKey || '') === normalizedStreamKey
1496
+ && String(message?.role || '') === String(role || '')
1497
+ && String(message?.kind || '') === String(kind || '')
1498
+ ) {
1499
+ return index;
1500
+ }
1501
+ }
1502
+ return -1;
1503
+ }
1504
+
1422
1505
  export function captureRestoreReplayChunk(capture, update, role, kind, text) {
1423
1506
  if (!capture) return false;
1424
1507
  const chunk = String(text || '');
@@ -1430,20 +1513,29 @@ export function captureRestoreReplayChunk(capture, update, role, kind, text) {
1430
1513
  kind,
1431
1514
  chunk
1432
1515
  );
1433
- const last = capture.messages[capture.messages.length - 1] || null;
1434
- if (
1435
- last
1436
- && last.streamKey === streamKey
1437
- && last.role === role
1438
- && last.kind === kind
1439
- ) {
1440
- last.text = mergeAgentMessageText(last.text, chunk);
1516
+ const existingIndex = findMessageIndexByStreamKey(
1517
+ capture.messages,
1518
+ streamKey,
1519
+ role,
1520
+ kind,
1521
+ {
1522
+ searchWholeHistory: !!update?.messageId
1523
+ }
1524
+ );
1525
+ if (existingIndex !== -1) {
1526
+ const existing = capture.messages[existingIndex];
1527
+ existing.text = mergeAgentMessageText(existing.text, chunk);
1528
+ if (typeof capture.nextTimelineOrder === 'function') {
1529
+ existing.order = capture.nextTimelineOrder();
1530
+ }
1441
1531
  return true;
1442
1532
  }
1443
1533
  if (!update?.messageId) {
1444
1534
  capture.messageCounter += 1;
1445
1535
  }
1446
- const baselineMessage = capture.baselineMessages[capture.messages.length] || null;
1536
+ const baselineMessage = capture.baselineMessagesByStreamKey.get(streamKey)
1537
+ || capture.baselineMessages[capture.messages.length]
1538
+ || null;
1447
1539
  const canReuseBaseline = !!(
1448
1540
  baselineMessage
1449
1541
  && baselineMessage.role === role
@@ -1535,18 +1627,32 @@ export function buildRestoredToolCall(
1535
1627
  const persisted = cloneSerializable(baseline, {}) || {};
1536
1628
  const current = cloneSerializable(previous, {}) || {};
1537
1629
  const allowEmptyCreatedAt = options.allowEmptyCreatedAt === true;
1538
- const nextOrder = normalizePersistedTimelineOrder(current.order, 0)
1539
- || (
1540
- typeof nextTimelineOrder === 'function'
1541
- ? nextTimelineOrder()
1542
- : normalizePersistedTimelineOrder(persisted.order, 0)
1543
- )
1630
+ const hasCurrentCreatedAt = Object.prototype.hasOwnProperty.call(
1631
+ current,
1632
+ 'createdAt'
1633
+ );
1634
+ const hasPersistedCreatedAt = Object.prototype.hasOwnProperty.call(
1635
+ persisted,
1636
+ 'createdAt'
1637
+ );
1638
+ const nextOrder = (
1639
+ typeof nextTimelineOrder === 'function'
1640
+ ? nextTimelineOrder()
1641
+ : normalizePersistedTimelineOrder(current.order, 0)
1642
+ )
1643
+ || normalizePersistedTimelineOrder(persisted.order, 0)
1544
1644
  || 1;
1545
1645
  const inheritedCreatedAt = String(
1546
1646
  current.createdAt || persisted.createdAt || ''
1547
1647
  ).trim();
1548
1648
  const createdAt = inheritedCreatedAt
1549
- || (allowEmptyCreatedAt ? '' : new Date().toISOString());
1649
+ || (
1650
+ allowEmptyCreatedAt
1651
+ || hasCurrentCreatedAt
1652
+ || hasPersistedCreatedAt
1653
+ ? ''
1654
+ : new Date().toISOString()
1655
+ );
1550
1656
  const nextToolCall = {
1551
1657
  ...persisted,
1552
1658
  ...current,
@@ -1646,6 +1752,13 @@ function normalizePersistedTerminalSummary(summary = {}) {
1646
1752
  };
1647
1753
  }
1648
1754
 
1755
+ function compareTimelineOrder(left = {}, right = {}) {
1756
+ return (
1757
+ normalizePersistedTimelineOrder(left?.order, 0)
1758
+ - normalizePersistedTimelineOrder(right?.order, 0)
1759
+ );
1760
+ }
1761
+
1649
1762
  export function getNextSyntheticStreamTurn(messages = []) {
1650
1763
  if (!Array.isArray(messages) || messages.length === 0) {
1651
1764
  return 0;
@@ -2659,6 +2772,27 @@ class AcpRuntime extends EventEmitter {
2659
2772
 
2660
2773
  serializeTab(tab) {
2661
2774
  tab.messages = normalizeAgentTranscriptMessages(tab.messages);
2775
+ const messages = cloneSerializable(tab.messages, []).sort(
2776
+ compareTimelineOrder
2777
+ );
2778
+ const toolCalls = Array.from(tab.toolCalls.values())
2779
+ .map((item) => cloneSerializable(item, {}))
2780
+ .sort(compareTimelineOrder);
2781
+ const permissions = Array.from(tab.permissions.values())
2782
+ .map((item) => ({
2783
+ id: item.id,
2784
+ sessionId: item.sessionId,
2785
+ toolCall: item.toolCall,
2786
+ options: item.options,
2787
+ status: item.status,
2788
+ createdAt: item.createdAt || '',
2789
+ order: item.order,
2790
+ selectedOptionId: item.selectedOptionId || ''
2791
+ }))
2792
+ .sort(compareTimelineOrder);
2793
+ const plan = Array.isArray(tab.plan)
2794
+ ? cloneSerializable(tab.plan, []).sort(compareTimelineOrder)
2795
+ : [];
2662
2796
  return {
2663
2797
  id: tab.id,
2664
2798
  runtimeId: tab.runtimeId,
@@ -2679,19 +2813,10 @@ class AcpRuntime extends EventEmitter {
2679
2813
  availableCommands: tab.availableCommands,
2680
2814
  sessionCapabilities: this.#getSessionCapabilities(),
2681
2815
  configOptions: tab.configOptions,
2682
- messages: tab.messages,
2683
- toolCalls: Array.from(tab.toolCalls.values()),
2684
- permissions: Array.from(tab.permissions.values()).map((item) => ({
2685
- id: item.id,
2686
- sessionId: item.sessionId,
2687
- toolCall: item.toolCall,
2688
- options: item.options,
2689
- status: item.status,
2690
- createdAt: item.createdAt || '',
2691
- order: item.order,
2692
- selectedOptionId: item.selectedOptionId || ''
2693
- })),
2694
- plan: Array.isArray(tab.plan) ? tab.plan : [],
2816
+ messages,
2817
+ toolCalls,
2818
+ permissions,
2819
+ plan,
2695
2820
  usage: serializeUsageState(tab.usage),
2696
2821
  terminals: Array.from(tab.terminals.values())
2697
2822
  };
@@ -3257,23 +3382,30 @@ class AcpRuntime extends EventEmitter {
3257
3382
  };
3258
3383
  }
3259
3384
  const streamKey = this.#getStreamKey(tab, update, role, kind);
3260
- const last = tab.messages[tab.messages.length - 1] || null;
3385
+ const existingIndex = findMessageIndexByStreamKey(
3386
+ tab.messages,
3387
+ streamKey,
3388
+ role,
3389
+ kind,
3390
+ {
3391
+ searchWholeHistory: !!update?.messageId
3392
+ }
3393
+ );
3261
3394
 
3262
- if (
3263
- last
3264
- && last.streamKey === streamKey
3265
- && last.role === role
3266
- && last.kind === kind
3267
- ) {
3268
- const nextText = mergeAgentMessageText(last.text, text);
3269
- const appendedText = nextText.slice(last.text.length);
3270
- last.text = nextText;
3395
+ if (existingIndex !== -1) {
3396
+ const existing = tab.messages[existingIndex];
3397
+ const nextText = mergeAgentMessageText(existing.text, text);
3398
+ const appendedText = nextText.slice(existing.text.length);
3399
+ const nextOrder = this.#nextTimelineOrder(tab);
3400
+ existing.text = nextText;
3401
+ existing.order = nextOrder;
3271
3402
  this.#broadcast(tab, {
3272
3403
  type: 'message_chunk',
3273
3404
  streamKey,
3274
3405
  role,
3275
3406
  kind,
3276
- text: appendedText
3407
+ text: appendedText,
3408
+ order: nextOrder
3277
3409
  });
3278
3410
  return {
3279
3411
  didChange: true,
@@ -581,6 +581,53 @@ class TabminalTestAgent {
581
581
  return { stopReason: 'end_turn' };
582
582
  }
583
583
 
584
+ if (
585
+ commandName === 'inline-order'
586
+ || /synthetic-inline-order/i.test(promptText)
587
+ ) {
588
+ await this.connection.sessionUpdate({
589
+ sessionId: params.sessionId,
590
+ update: {
591
+ sessionUpdate: 'agent_message_chunk',
592
+ messageId: 'inline-order-message',
593
+ content: {
594
+ type: 'text',
595
+ text: 'Before tool. '
596
+ }
597
+ }
598
+ });
599
+ await this.connection.sessionUpdate({
600
+ sessionId: params.sessionId,
601
+ update: {
602
+ sessionUpdate: 'tool_call',
603
+ toolCallId: 'inline-order-tool',
604
+ title: 'Inline order tool',
605
+ kind: 'execute',
606
+ status: 'pending'
607
+ }
608
+ });
609
+ await this.connection.sessionUpdate({
610
+ sessionId: params.sessionId,
611
+ update: {
612
+ sessionUpdate: 'tool_call_update',
613
+ toolCallId: 'inline-order-tool',
614
+ status: 'completed'
615
+ }
616
+ });
617
+ await this.connection.sessionUpdate({
618
+ sessionId: params.sessionId,
619
+ update: {
620
+ sessionUpdate: 'agent_message_chunk',
621
+ messageId: 'inline-order-message',
622
+ content: {
623
+ type: 'text',
624
+ text: 'After tool.'
625
+ }
626
+ }
627
+ });
628
+ return { stopReason: 'end_turn' };
629
+ }
630
+
584
631
  if (
585
632
  commandName === 'demo'
586
633
  || commandName === 'diff'