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 +1 -1
- package/public/app.js +358 -123
- package/src/acp-manager.mjs +175 -43
- package/src/acp-test-agent.mjs +47 -0
package/package.json
CHANGED
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
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
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
|
|
5823
|
+
const isNearLatestWindow = isAgentTranscriptWindowNearLatest(
|
|
5824
|
+
agentTab,
|
|
5825
|
+
timeline.length
|
|
5826
|
+
);
|
|
5827
|
+
const shouldPinToBottom = !options.preserveTranscriptAnchor && (
|
|
5693
5828
|
agentTab.scrollToBottomOnNextRender
|
|
5694
|
-
&&
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9277
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10166
|
+
return { full: true };
|
|
9955
10167
|
default:
|
|
9956
|
-
|
|
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 (
|
|
11382
|
-
|
|
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
|
|
13592
|
-
|
|
13593
|
-
|
|
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
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13601
|
-
|
|
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
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
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
|
-
|
|
13623
|
-
|
|
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
|
|
package/src/acp-manager.mjs
CHANGED
|
@@ -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:
|
|
503
|
-
|
|
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
|
|
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
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
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.
|
|
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
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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
|
-
|| (
|
|
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
|
|
2683
|
-
toolCalls
|
|
2684
|
-
permissions
|
|
2685
|
-
|
|
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
|
|
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
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
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,
|
package/src/acp-test-agent.mjs
CHANGED
|
@@ -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'
|