tabminal 3.0.16 → 3.0.18
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 +342 -110
- package/src/acp-manager.mjs +189 -47
- 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,7 +5800,25 @@ 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';
|
|
5690
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
|
+
}
|
|
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
5823
|
const shouldPinToBottom = wasNearBottom || (
|
|
5693
5824
|
agentTab.scrollToBottomOnNextRender
|
|
@@ -5782,20 +5913,14 @@ class EditorManager {
|
|
|
5782
5913
|
}
|
|
5783
5914
|
this.updateAgentScrollBottomButton();
|
|
5784
5915
|
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
5916
|
this.scheduleAgentTranscriptViewportUpdate(shouldPinToBottom);
|
|
5797
5917
|
}
|
|
5798
5918
|
|
|
5919
|
+
renderAgentPanel(agentTab, options = {}) {
|
|
5920
|
+
this.renderAgentPanelChrome(agentTab);
|
|
5921
|
+
this.renderAgentTranscript(agentTab, options);
|
|
5922
|
+
}
|
|
5923
|
+
|
|
5799
5924
|
buildAgentTimelineNode(agentTab, entry, timelineIndex) {
|
|
5800
5925
|
if (!entry) {
|
|
5801
5926
|
return null;
|
|
@@ -5939,20 +6064,6 @@ class EditorManager {
|
|
|
5939
6064
|
}
|
|
5940
6065
|
}
|
|
5941
6066
|
|
|
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
6067
|
refreshAgentUsageHud() {
|
|
5957
6068
|
const activeTab = getActiveAgentTab();
|
|
5958
6069
|
if (!activeTab || !this.agentUsageHud) return;
|
|
@@ -6192,9 +6303,7 @@ class EditorManager {
|
|
|
6192
6303
|
const item = document.createElement('div');
|
|
6193
6304
|
item.className = 'agent-message agent-plan-history';
|
|
6194
6305
|
item.appendChild(buildAgentTimelineHeader(
|
|
6195
|
-
buildAgentTimelineRoleLabel(agentTab, 'plan')
|
|
6196
|
-
getAgentMessageTimeLabel(planEntry),
|
|
6197
|
-
planEntry.createdAt || ''
|
|
6306
|
+
buildAgentTimelineRoleLabel(agentTab, 'plan')
|
|
6198
6307
|
));
|
|
6199
6308
|
const body = document.createElement('div');
|
|
6200
6309
|
body.className = 'agent-plan-history-body';
|
|
@@ -6222,9 +6331,7 @@ class EditorManager {
|
|
|
6222
6331
|
item.className = `agent-message ${message.role} ${message.kind}`;
|
|
6223
6332
|
|
|
6224
6333
|
item.appendChild(buildAgentTimelineHeader(
|
|
6225
|
-
getAgentMessageRoleLabel(agentTab, message)
|
|
6226
|
-
getAgentMessageTimeLabel(message),
|
|
6227
|
-
message.createdAt || ''
|
|
6334
|
+
getAgentMessageRoleLabel(agentTab, message)
|
|
6228
6335
|
));
|
|
6229
6336
|
const attachments = buildAgentMessageAttachmentsNode(
|
|
6230
6337
|
message.attachments
|
|
@@ -6241,11 +6348,7 @@ class EditorManager {
|
|
|
6241
6348
|
&& message.kind === 'message'
|
|
6242
6349
|
) {
|
|
6243
6350
|
const cachedMarkdown = getAgentMessageMarkdownCache(message);
|
|
6244
|
-
if (
|
|
6245
|
-
!agentTab?.busy
|
|
6246
|
-
&& !isAgentMessageStreaming(agentTab, message)
|
|
6247
|
-
&& cachedMarkdown
|
|
6248
|
-
) {
|
|
6351
|
+
if (cachedMarkdown) {
|
|
6249
6352
|
body.classList.add('markdown');
|
|
6250
6353
|
body.innerHTML = cachedMarkdown;
|
|
6251
6354
|
} else {
|
|
@@ -6275,9 +6378,7 @@ class EditorManager {
|
|
|
6275
6378
|
node.className = `agent-tool-call state-${toolStatusClass}`;
|
|
6276
6379
|
|
|
6277
6380
|
node.appendChild(buildAgentTimelineHeader(
|
|
6278
|
-
buildAgentTimelineRoleLabel(agentTab, 'tool')
|
|
6279
|
-
getAgentMessageTimeLabel(toolCall),
|
|
6280
|
-
toolCall.createdAt || ''
|
|
6381
|
+
buildAgentTimelineRoleLabel(agentTab, 'tool')
|
|
6281
6382
|
));
|
|
6282
6383
|
|
|
6283
6384
|
const header = document.createElement('div');
|
|
@@ -6384,9 +6485,7 @@ class EditorManager {
|
|
|
6384
6485
|
permission.status === 'pending'
|
|
6385
6486
|
? 'permission request'
|
|
6386
6487
|
: 'permission'
|
|
6387
|
-
)
|
|
6388
|
-
getAgentMessageTimeLabel(permission),
|
|
6389
|
-
permission.createdAt || ''
|
|
6488
|
+
)
|
|
6390
6489
|
));
|
|
6391
6490
|
|
|
6392
6491
|
const titleRow = document.createElement('div');
|
|
@@ -9270,11 +9369,32 @@ class AgentTab {
|
|
|
9270
9369
|
) || null;
|
|
9271
9370
|
}
|
|
9272
9371
|
|
|
9273
|
-
notifyUi() {
|
|
9372
|
+
notifyUi(options = {}) {
|
|
9274
9373
|
const session = this.getLinkedSession();
|
|
9275
9374
|
if (!session) return;
|
|
9276
|
-
|
|
9277
|
-
|
|
9375
|
+
const shouldUpdateTabs = options.updateTabs !== false;
|
|
9376
|
+
if (shouldUpdateTabs) {
|
|
9377
|
+
session.updateTabUI();
|
|
9378
|
+
}
|
|
9379
|
+
if (state.activeSessionKey !== session.key) {
|
|
9380
|
+
return;
|
|
9381
|
+
}
|
|
9382
|
+
if (editorManager.currentSession?.key !== session.key) {
|
|
9383
|
+
editorManager.switchTo(session);
|
|
9384
|
+
return;
|
|
9385
|
+
}
|
|
9386
|
+
if (shouldUpdateTabs) {
|
|
9387
|
+
editorManager.renderEditorTabs();
|
|
9388
|
+
}
|
|
9389
|
+
if (editorManager.getActiveWorkspaceTabKey(session) !== this.key) {
|
|
9390
|
+
return;
|
|
9391
|
+
}
|
|
9392
|
+
editorManager.scheduleAgentPanelRender(this, {
|
|
9393
|
+
full: options.full !== false,
|
|
9394
|
+
delayMs: options.delayMs,
|
|
9395
|
+
dirtyKey: options.dirtyKey || '',
|
|
9396
|
+
authoritativeSync: !!options.authoritativeSync
|
|
9397
|
+
});
|
|
9278
9398
|
}
|
|
9279
9399
|
|
|
9280
9400
|
update(data) {
|
|
@@ -9456,10 +9576,14 @@ class AgentTab {
|
|
|
9456
9576
|
|
|
9457
9577
|
handleMessage(message) {
|
|
9458
9578
|
const wasBusy = this.busy;
|
|
9579
|
+
let notifyOptions = { full: true };
|
|
9459
9580
|
switch (message.type) {
|
|
9460
9581
|
case 'snapshot':
|
|
9461
9582
|
this.update(message.tab || {});
|
|
9462
9583
|
this.scrollToBottomOnNextRender = true;
|
|
9584
|
+
notifyOptions = {
|
|
9585
|
+
full: true
|
|
9586
|
+
};
|
|
9463
9587
|
break;
|
|
9464
9588
|
case 'message_open':
|
|
9465
9589
|
this.#upsertMessage(message.message);
|
|
@@ -9470,6 +9594,12 @@ class AgentTab {
|
|
|
9470
9594
|
) {
|
|
9471
9595
|
this.streamingAssistantStreamKey = message.message.streamKey;
|
|
9472
9596
|
}
|
|
9597
|
+
notifyOptions = {
|
|
9598
|
+
full: false,
|
|
9599
|
+
delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
|
|
9600
|
+
dirtyKey: this.#getMessageRenderKey(message.message),
|
|
9601
|
+
updateTabs: false
|
|
9602
|
+
};
|
|
9473
9603
|
break;
|
|
9474
9604
|
case 'message_chunk':
|
|
9475
9605
|
this.#appendChunk(message);
|
|
@@ -9480,9 +9610,15 @@ class AgentTab {
|
|
|
9480
9610
|
) {
|
|
9481
9611
|
this.streamingAssistantStreamKey = message.streamKey;
|
|
9482
9612
|
}
|
|
9613
|
+
notifyOptions = {
|
|
9614
|
+
full: false,
|
|
9615
|
+
delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
|
|
9616
|
+
dirtyKey: this.#getMessageRenderKey(message),
|
|
9617
|
+
updateTabs: false
|
|
9618
|
+
};
|
|
9483
9619
|
break;
|
|
9484
9620
|
case 'session_update':
|
|
9485
|
-
this.#applySessionUpdate(message.update || {});
|
|
9621
|
+
notifyOptions = this.#applySessionUpdate(message.update || {});
|
|
9486
9622
|
if (message.tab?.currentModeId || message.tab?.modeId) {
|
|
9487
9623
|
this.currentModeId = message.tab.currentModeId
|
|
9488
9624
|
|| message.tab.modeId;
|
|
@@ -9511,6 +9647,14 @@ class AgentTab {
|
|
|
9511
9647
|
)
|
|
9512
9648
|
});
|
|
9513
9649
|
}
|
|
9650
|
+
notifyOptions = {
|
|
9651
|
+
full: false,
|
|
9652
|
+
delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
|
|
9653
|
+
dirtyKey: this.#getPermissionRenderKey(
|
|
9654
|
+
message.permission?.id
|
|
9655
|
+
),
|
|
9656
|
+
updateTabs: false
|
|
9657
|
+
};
|
|
9514
9658
|
break;
|
|
9515
9659
|
case 'permission_resolved': {
|
|
9516
9660
|
const permission = this.permissions.get(message.permissionId);
|
|
@@ -9520,6 +9664,14 @@ class AgentTab {
|
|
|
9520
9664
|
|| permission.selectedOptionId
|
|
9521
9665
|
|| '';
|
|
9522
9666
|
}
|
|
9667
|
+
notifyOptions = {
|
|
9668
|
+
full: false,
|
|
9669
|
+
delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
|
|
9670
|
+
dirtyKey: this.#getPermissionRenderKey(
|
|
9671
|
+
message.permissionId
|
|
9672
|
+
),
|
|
9673
|
+
updateTabs: false
|
|
9674
|
+
};
|
|
9523
9675
|
break;
|
|
9524
9676
|
}
|
|
9525
9677
|
case 'terminal_update':
|
|
@@ -9559,18 +9711,22 @@ class AgentTab {
|
|
|
9559
9711
|
return;
|
|
9560
9712
|
}
|
|
9561
9713
|
}
|
|
9714
|
+
notifyOptions = { full: true };
|
|
9562
9715
|
break;
|
|
9563
9716
|
case 'usage_state':
|
|
9564
9717
|
this.usage = this.#normalizeUsageState(message.usage);
|
|
9718
|
+
notifyOptions = { full: true };
|
|
9565
9719
|
break;
|
|
9566
9720
|
case 'status':
|
|
9567
9721
|
this.status = message.status || this.status;
|
|
9568
9722
|
this.busy = !!message.busy;
|
|
9569
9723
|
this.errorMessage = message.errorMessage || '';
|
|
9724
|
+
notifyOptions = { full: true };
|
|
9570
9725
|
break;
|
|
9571
9726
|
case 'complete':
|
|
9572
9727
|
this.status = message.status || 'ready';
|
|
9573
9728
|
this.busy = !!message.busy;
|
|
9729
|
+
notifyOptions = { full: true };
|
|
9574
9730
|
break;
|
|
9575
9731
|
default:
|
|
9576
9732
|
break;
|
|
@@ -9611,7 +9767,15 @@ class AgentTab {
|
|
|
9611
9767
|
this.needsAttention = false;
|
|
9612
9768
|
}
|
|
9613
9769
|
this.#syncBusyWatchdog();
|
|
9614
|
-
this.
|
|
9770
|
+
if (wasBusy && !this.busy) {
|
|
9771
|
+
notifyOptions = {
|
|
9772
|
+
...notifyOptions,
|
|
9773
|
+
full: true,
|
|
9774
|
+
authoritativeSync: true,
|
|
9775
|
+
delayMs: AGENT_TRANSCRIPT_AUTH_SYNC_DEBOUNCE_MS
|
|
9776
|
+
};
|
|
9777
|
+
}
|
|
9778
|
+
this.notifyUi(notifyOptions);
|
|
9615
9779
|
if (shouldAutostartQueuedPrompt) {
|
|
9616
9780
|
this.lastCompletedRunCounter = this.runCounter;
|
|
9617
9781
|
void drainQueuedAgentPrompt(this);
|
|
@@ -9825,13 +9989,39 @@ class AgentTab {
|
|
|
9825
9989
|
return this.timelineCounter;
|
|
9826
9990
|
}
|
|
9827
9991
|
|
|
9992
|
+
#getMessageRenderKey(message = {}) {
|
|
9993
|
+
const role = String(message?.role || 'assistant');
|
|
9994
|
+
const kind = String(message?.kind || 'message');
|
|
9995
|
+
const identity = String(
|
|
9996
|
+
message?.id
|
|
9997
|
+
|| message?.streamKey
|
|
9998
|
+
|| ''
|
|
9999
|
+
).trim();
|
|
10000
|
+
if (!identity) {
|
|
10001
|
+
return '';
|
|
10002
|
+
}
|
|
10003
|
+
return `message:${role}:${kind}:${identity}`;
|
|
10004
|
+
}
|
|
10005
|
+
|
|
10006
|
+
#getToolRenderKey(toolCallId = '') {
|
|
10007
|
+
const identity = String(toolCallId || '').trim();
|
|
10008
|
+
return identity ? `tool:${identity}` : '';
|
|
10009
|
+
}
|
|
10010
|
+
|
|
10011
|
+
#getPermissionRenderKey(permissionId = '') {
|
|
10012
|
+
const identity = String(permissionId || '').trim();
|
|
10013
|
+
return identity ? `permission:${identity}` : '';
|
|
10014
|
+
}
|
|
10015
|
+
|
|
9828
10016
|
#findMessageIndex(candidate) {
|
|
9829
10017
|
if (!candidate) return -1;
|
|
9830
10018
|
if (candidate.id) {
|
|
9831
10019
|
const byId = this.messages.findIndex(
|
|
9832
10020
|
(message) => message.id === candidate.id
|
|
9833
10021
|
);
|
|
9834
|
-
|
|
10022
|
+
if (byId !== -1) {
|
|
10023
|
+
return byId;
|
|
10024
|
+
}
|
|
9835
10025
|
}
|
|
9836
10026
|
if (!candidate.streamKey) return -1;
|
|
9837
10027
|
for (let index = this.messages.length - 1; index >= 0; index -= 1) {
|
|
@@ -9859,7 +10049,12 @@ class AgentTab {
|
|
|
9859
10049
|
|
|
9860
10050
|
const previous = this.messages[index];
|
|
9861
10051
|
const nextMessage = this.#normalizeMessage(message, previous.order);
|
|
9862
|
-
const mergedText =
|
|
10052
|
+
const mergedText = (
|
|
10053
|
+
!previous.id
|
|
10054
|
+
&& nextMessage.id
|
|
10055
|
+
? (nextMessage.text || '')
|
|
10056
|
+
: selectAgentMessageText(previous.text, nextMessage.text)
|
|
10057
|
+
);
|
|
9863
10058
|
this.messages[index] = {
|
|
9864
10059
|
...previous,
|
|
9865
10060
|
...nextMessage,
|
|
@@ -9883,16 +10078,22 @@ class AgentTab {
|
|
|
9883
10078
|
existing.text = nextText;
|
|
9884
10079
|
clearAgentMessageMarkdownCache(existing);
|
|
9885
10080
|
}
|
|
10081
|
+
if (Number.isFinite(message?.order)) {
|
|
10082
|
+
existing.order = message.order;
|
|
10083
|
+
}
|
|
9886
10084
|
return;
|
|
9887
10085
|
}
|
|
9888
10086
|
|
|
9889
10087
|
const nextMessage = this.#normalizeMessage({
|
|
9890
|
-
id:
|
|
10088
|
+
id: typeof message.id === 'string'
|
|
10089
|
+
? message.id
|
|
10090
|
+
: '',
|
|
9891
10091
|
streamKey: message.streamKey,
|
|
9892
10092
|
role: message.role || 'assistant',
|
|
9893
10093
|
kind: message.kind || 'message',
|
|
9894
10094
|
text: message.text || '',
|
|
9895
|
-
createdAt: new Date().toISOString()
|
|
10095
|
+
createdAt: new Date().toISOString(),
|
|
10096
|
+
order: message.order
|
|
9896
10097
|
});
|
|
9897
10098
|
clearAgentMessageMarkdownCache(nextMessage);
|
|
9898
10099
|
this.messages.push(nextMessage);
|
|
@@ -9908,28 +10109,38 @@ class AgentTab {
|
|
|
9908
10109
|
this.#normalizeTimelineEntry(update, previous?.order)
|
|
9909
10110
|
);
|
|
9910
10111
|
}
|
|
9911
|
-
|
|
10112
|
+
return {
|
|
10113
|
+
full: false,
|
|
10114
|
+
delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
|
|
10115
|
+
dirtyKey: this.#getToolRenderKey(update.toolCallId),
|
|
10116
|
+
updateTabs: false
|
|
10117
|
+
};
|
|
9912
10118
|
case 'tool_call_update': {
|
|
9913
10119
|
const previous = this.toolCalls.get(update.toolCallId) || {};
|
|
9914
10120
|
this.toolCalls.set(update.toolCallId, {
|
|
9915
10121
|
...previous,
|
|
9916
10122
|
...this.#normalizeTimelineEntry(update, previous.order)
|
|
9917
10123
|
});
|
|
9918
|
-
|
|
10124
|
+
return {
|
|
10125
|
+
full: false,
|
|
10126
|
+
delayMs: AGENT_TRANSCRIPT_RENDER_DEBOUNCE_MS,
|
|
10127
|
+
dirtyKey: this.#getToolRenderKey(update.toolCallId),
|
|
10128
|
+
updateTabs: false
|
|
10129
|
+
};
|
|
9919
10130
|
}
|
|
9920
10131
|
case 'current_mode_update':
|
|
9921
10132
|
this.currentModeId = update.currentModeId || update.modeId || '';
|
|
9922
|
-
|
|
10133
|
+
return { full: true };
|
|
9923
10134
|
case 'available_commands_update':
|
|
9924
10135
|
this.availableCommands = Array.isArray(update.availableCommands)
|
|
9925
10136
|
? update.availableCommands
|
|
9926
10137
|
: [];
|
|
9927
|
-
|
|
10138
|
+
return { full: true };
|
|
9928
10139
|
case 'config_option_update':
|
|
9929
10140
|
this.configOptions = Array.isArray(update.configOptions)
|
|
9930
10141
|
? update.configOptions
|
|
9931
10142
|
: [];
|
|
9932
|
-
|
|
10143
|
+
return { full: true };
|
|
9933
10144
|
case 'plan':
|
|
9934
10145
|
this.#applyPlanState(
|
|
9935
10146
|
Array.isArray(update.entries)
|
|
@@ -9938,22 +10149,22 @@ class AgentTab {
|
|
|
9938
10149
|
)
|
|
9939
10150
|
: []
|
|
9940
10151
|
);
|
|
9941
|
-
|
|
10152
|
+
return { full: true };
|
|
9942
10153
|
case 'usage_update':
|
|
9943
10154
|
this.usage = this.#normalizeUsageState({
|
|
9944
10155
|
...(this.usage || {}),
|
|
9945
10156
|
...update
|
|
9946
10157
|
});
|
|
9947
|
-
|
|
10158
|
+
return { full: true };
|
|
9948
10159
|
case 'session_info_update':
|
|
9949
10160
|
if (typeof update.title === 'string') {
|
|
9950
10161
|
this.title = update.title;
|
|
9951
10162
|
} else if (update.title === null) {
|
|
9952
10163
|
this.title = '';
|
|
9953
10164
|
}
|
|
9954
|
-
|
|
10165
|
+
return { full: true };
|
|
9955
10166
|
default:
|
|
9956
|
-
|
|
10167
|
+
return { full: true };
|
|
9957
10168
|
}
|
|
9958
10169
|
}
|
|
9959
10170
|
|
|
@@ -11366,9 +11577,7 @@ function getAgentMessageTimeLabel(message) {
|
|
|
11366
11577
|
}
|
|
11367
11578
|
|
|
11368
11579
|
function buildAgentTimelineHeader(
|
|
11369
|
-
roleLabel
|
|
11370
|
-
timeLabel = '',
|
|
11371
|
-
createdAt = ''
|
|
11580
|
+
roleLabel
|
|
11372
11581
|
) {
|
|
11373
11582
|
const header = document.createElement('div');
|
|
11374
11583
|
header.className = 'agent-message-header';
|
|
@@ -11378,16 +11587,6 @@ function buildAgentTimelineHeader(
|
|
|
11378
11587
|
role.textContent = roleLabel;
|
|
11379
11588
|
header.appendChild(role);
|
|
11380
11589
|
|
|
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);
|
|
11389
|
-
}
|
|
11390
|
-
|
|
11391
11590
|
return header;
|
|
11392
11591
|
}
|
|
11393
11592
|
|
|
@@ -13588,18 +13787,51 @@ function upsertAgentTab(server, data) {
|
|
|
13588
13787
|
return agentTab;
|
|
13589
13788
|
}
|
|
13590
13789
|
|
|
13591
|
-
function
|
|
13592
|
-
|
|
13593
|
-
|
|
13790
|
+
function buildComparableAgentTimelineTail(source, limit = 6) {
|
|
13791
|
+
const items = [];
|
|
13792
|
+
const messages = Array.isArray(source?.messages) ? source.messages : [];
|
|
13793
|
+
const toolCalls = Array.isArray(source?.toolCalls)
|
|
13794
|
+
? source.toolCalls
|
|
13795
|
+
: Array.from(source?.toolCalls?.values?.() || []);
|
|
13796
|
+
const permissions = Array.isArray(source?.permissions)
|
|
13797
|
+
? source.permissions
|
|
13798
|
+
: Array.from(source?.permissions?.values?.() || []);
|
|
13799
|
+
for (const message of messages) {
|
|
13800
|
+
items.push([
|
|
13801
|
+
'message',
|
|
13802
|
+
Number.isFinite(message?.order) ? message.order : 0,
|
|
13803
|
+
String(message?.id || ''),
|
|
13804
|
+
String(message?.streamKey || ''),
|
|
13805
|
+
String(message?.role || ''),
|
|
13806
|
+
String(message?.kind || ''),
|
|
13807
|
+
hashUiText(message?.text || '')
|
|
13808
|
+
]);
|
|
13594
13809
|
}
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13601
|
-
|
|
13602
|
-
|
|
13810
|
+
for (const toolCall of toolCalls) {
|
|
13811
|
+
items.push([
|
|
13812
|
+
'tool',
|
|
13813
|
+
Number.isFinite(toolCall?.order) ? toolCall.order : 0,
|
|
13814
|
+
String(toolCall?.toolCallId || ''),
|
|
13815
|
+
String(toolCall?.status || ''),
|
|
13816
|
+
hashUiText(JSON.stringify(toolCall || null))
|
|
13817
|
+
]);
|
|
13818
|
+
}
|
|
13819
|
+
for (const permission of permissions) {
|
|
13820
|
+
items.push([
|
|
13821
|
+
'permission',
|
|
13822
|
+
Number.isFinite(permission?.order) ? permission.order : 0,
|
|
13823
|
+
String(permission?.id || ''),
|
|
13824
|
+
String(permission?.status || ''),
|
|
13825
|
+
String(permission?.selectedOptionId || '')
|
|
13826
|
+
]);
|
|
13827
|
+
}
|
|
13828
|
+
items.sort((left, right) => {
|
|
13829
|
+
if (left[1] !== right[1]) {
|
|
13830
|
+
return left[1] - right[1];
|
|
13831
|
+
}
|
|
13832
|
+
return String(left[0]).localeCompare(String(right[0]));
|
|
13833
|
+
});
|
|
13834
|
+
return JSON.stringify(items.slice(-limit));
|
|
13603
13835
|
}
|
|
13604
13836
|
|
|
13605
13837
|
function shouldApplyAuthoritativeAgentSnapshot(existing, data) {
|
|
@@ -13613,16 +13845,15 @@ function shouldApplyAuthoritativeAgentSnapshot(existing, data) {
|
|
|
13613
13845
|
if (!Array.isArray(data.messages)) {
|
|
13614
13846
|
return true;
|
|
13615
13847
|
}
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
13848
|
+
if (
|
|
13849
|
+
Array.isArray(data.messages)
|
|
13850
|
+
&& Array.isArray(existing.messages)
|
|
13851
|
+
&& data.messages.length !== existing.messages.length
|
|
13852
|
+
) {
|
|
13620
13853
|
return true;
|
|
13621
13854
|
}
|
|
13622
|
-
|
|
13623
|
-
|
|
13624
|
-
return getAgentMessageComparableSignature(incomingLast)
|
|
13625
|
-
!== getAgentMessageComparableSignature(currentLast);
|
|
13855
|
+
return buildComparableAgentTimelineTail(data)
|
|
13856
|
+
!== buildComparableAgentTimelineTail(existing);
|
|
13626
13857
|
}
|
|
13627
13858
|
|
|
13628
13859
|
function upsertAgentInventoryTab(server, data) {
|
|
@@ -13719,6 +13950,7 @@ function removeAgentTab(agentTabKey) {
|
|
|
13719
13950
|
const agentTab = state.agentTabs.get(agentTabKey);
|
|
13720
13951
|
if (!agentTab) return;
|
|
13721
13952
|
const session = agentTab.getLinkedSession();
|
|
13953
|
+
editorManager?.clearScheduledAgentPanelRender?.(agentTabKey);
|
|
13722
13954
|
agentTab.dispose();
|
|
13723
13955
|
state.agentTabs.delete(agentTabKey);
|
|
13724
13956
|
|
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
|
|
@@ -1529,20 +1621,38 @@ export function buildRestoredToolCall(
|
|
|
1529
1621
|
previous = null,
|
|
1530
1622
|
baseline = null,
|
|
1531
1623
|
update = {},
|
|
1532
|
-
nextTimelineOrder = null
|
|
1624
|
+
nextTimelineOrder = null,
|
|
1625
|
+
options = {}
|
|
1533
1626
|
) {
|
|
1534
1627
|
const persisted = cloneSerializable(baseline, {}) || {};
|
|
1535
1628
|
const current = cloneSerializable(previous, {}) || {};
|
|
1536
|
-
const
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1629
|
+
const allowEmptyCreatedAt = options.allowEmptyCreatedAt === true;
|
|
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)
|
|
1542
1644
|
|| 1;
|
|
1543
|
-
const
|
|
1645
|
+
const inheritedCreatedAt = String(
|
|
1544
1646
|
current.createdAt || persisted.createdAt || ''
|
|
1545
|
-
).trim()
|
|
1647
|
+
).trim();
|
|
1648
|
+
const createdAt = inheritedCreatedAt
|
|
1649
|
+
|| (
|
|
1650
|
+
allowEmptyCreatedAt
|
|
1651
|
+
|| hasCurrentCreatedAt
|
|
1652
|
+
|| hasPersistedCreatedAt
|
|
1653
|
+
? ''
|
|
1654
|
+
: new Date().toISOString()
|
|
1655
|
+
);
|
|
1546
1656
|
const nextToolCall = {
|
|
1547
1657
|
...persisted,
|
|
1548
1658
|
...current,
|
|
@@ -1642,6 +1752,13 @@ function normalizePersistedTerminalSummary(summary = {}) {
|
|
|
1642
1752
|
};
|
|
1643
1753
|
}
|
|
1644
1754
|
|
|
1755
|
+
function compareTimelineOrder(left = {}, right = {}) {
|
|
1756
|
+
return (
|
|
1757
|
+
normalizePersistedTimelineOrder(left?.order, 0)
|
|
1758
|
+
- normalizePersistedTimelineOrder(right?.order, 0)
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1645
1762
|
export function getNextSyntheticStreamTurn(messages = []) {
|
|
1646
1763
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
1647
1764
|
return 0;
|
|
@@ -2655,6 +2772,27 @@ class AcpRuntime extends EventEmitter {
|
|
|
2655
2772
|
|
|
2656
2773
|
serializeTab(tab) {
|
|
2657
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
|
+
: [];
|
|
2658
2796
|
return {
|
|
2659
2797
|
id: tab.id,
|
|
2660
2798
|
runtimeId: tab.runtimeId,
|
|
@@ -2675,19 +2813,10 @@ class AcpRuntime extends EventEmitter {
|
|
|
2675
2813
|
availableCommands: tab.availableCommands,
|
|
2676
2814
|
sessionCapabilities: this.#getSessionCapabilities(),
|
|
2677
2815
|
configOptions: tab.configOptions,
|
|
2678
|
-
messages
|
|
2679
|
-
toolCalls
|
|
2680
|
-
permissions
|
|
2681
|
-
|
|
2682
|
-
sessionId: item.sessionId,
|
|
2683
|
-
toolCall: item.toolCall,
|
|
2684
|
-
options: item.options,
|
|
2685
|
-
status: item.status,
|
|
2686
|
-
createdAt: item.createdAt || '',
|
|
2687
|
-
order: item.order,
|
|
2688
|
-
selectedOptionId: item.selectedOptionId || ''
|
|
2689
|
-
})),
|
|
2690
|
-
plan: Array.isArray(tab.plan) ? tab.plan : [],
|
|
2816
|
+
messages,
|
|
2817
|
+
toolCalls,
|
|
2818
|
+
permissions,
|
|
2819
|
+
plan,
|
|
2691
2820
|
usage: serializeUsageState(tab.usage),
|
|
2692
2821
|
terminals: Array.from(tab.terminals.values())
|
|
2693
2822
|
};
|
|
@@ -3148,7 +3277,10 @@ class AcpRuntime extends EventEmitter {
|
|
|
3148
3277
|
previous,
|
|
3149
3278
|
baseline,
|
|
3150
3279
|
update,
|
|
3151
|
-
() => this.#nextTimelineOrder(tab)
|
|
3280
|
+
() => this.#nextTimelineOrder(tab),
|
|
3281
|
+
{
|
|
3282
|
+
allowEmptyCreatedAt: !!tab.restoreCapture
|
|
3283
|
+
}
|
|
3152
3284
|
);
|
|
3153
3285
|
tab.toolCalls.set(update.toolCallId, nextToolCall);
|
|
3154
3286
|
broadcastUpdate = nextToolCall;
|
|
@@ -3165,7 +3297,10 @@ class AcpRuntime extends EventEmitter {
|
|
|
3165
3297
|
previous,
|
|
3166
3298
|
baseline,
|
|
3167
3299
|
update,
|
|
3168
|
-
() => this.#nextTimelineOrder(tab)
|
|
3300
|
+
() => this.#nextTimelineOrder(tab),
|
|
3301
|
+
{
|
|
3302
|
+
allowEmptyCreatedAt: !!tab.restoreCapture
|
|
3303
|
+
}
|
|
3169
3304
|
);
|
|
3170
3305
|
tab.toolCalls.set(update.toolCallId, nextToolCall);
|
|
3171
3306
|
broadcastUpdate = nextToolCall;
|
|
@@ -3247,23 +3382,30 @@ class AcpRuntime extends EventEmitter {
|
|
|
3247
3382
|
};
|
|
3248
3383
|
}
|
|
3249
3384
|
const streamKey = this.#getStreamKey(tab, update, role, kind);
|
|
3250
|
-
const
|
|
3385
|
+
const existingIndex = findMessageIndexByStreamKey(
|
|
3386
|
+
tab.messages,
|
|
3387
|
+
streamKey,
|
|
3388
|
+
role,
|
|
3389
|
+
kind,
|
|
3390
|
+
{
|
|
3391
|
+
searchWholeHistory: !!update?.messageId
|
|
3392
|
+
}
|
|
3393
|
+
);
|
|
3251
3394
|
|
|
3252
|
-
if (
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
const appendedText = nextText.slice(last.text.length);
|
|
3260
|
-
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;
|
|
3261
3402
|
this.#broadcast(tab, {
|
|
3262
3403
|
type: 'message_chunk',
|
|
3263
3404
|
streamKey,
|
|
3264
3405
|
role,
|
|
3265
3406
|
kind,
|
|
3266
|
-
text: appendedText
|
|
3407
|
+
text: appendedText,
|
|
3408
|
+
order: nextOrder
|
|
3267
3409
|
});
|
|
3268
3410
|
return {
|
|
3269
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'
|