tabminal 3.0.38 → 3.0.39
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 +1 -0
- package/src/acp-manager.mjs +139 -0
- package/src/server.mjs +2 -1
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -14757,6 +14757,7 @@ async function resumeAgentTabFromHistory(session, agentTab, historySession) {
|
|
|
14757
14757
|
cwd: agentTab.cwd || session.cwd || session.initialCwd || '/',
|
|
14758
14758
|
terminalSessionId: session.id,
|
|
14759
14759
|
sessionId: historySession.sessionId,
|
|
14760
|
+
targetTabId: agentTab.id,
|
|
14760
14761
|
title: historySession.title || ''
|
|
14761
14762
|
})
|
|
14762
14763
|
});
|
package/src/acp-manager.mjs
CHANGED
|
@@ -2733,6 +2733,108 @@ class AcpRuntime extends EventEmitter {
|
|
|
2733
2733
|
}
|
|
2734
2734
|
}
|
|
2735
2735
|
|
|
2736
|
+
#resetTabForSessionLoad(tab, meta) {
|
|
2737
|
+
tab.acpSessionId = String(meta.acpSessionId || '').trim();
|
|
2738
|
+
tab.terminalSessionId = meta.terminalSessionId || tab.terminalSessionId;
|
|
2739
|
+
tab.cwd = meta.cwd || tab.cwd;
|
|
2740
|
+
tab.createdAt = new Date().toISOString();
|
|
2741
|
+
tab.title = meta.title || '';
|
|
2742
|
+
tab.status = 'restoring';
|
|
2743
|
+
tab.busy = true;
|
|
2744
|
+
tab.errorMessage = '';
|
|
2745
|
+
tab.messages = [];
|
|
2746
|
+
tab.toolCalls = new Map();
|
|
2747
|
+
tab.permissions = new Map();
|
|
2748
|
+
tab.syntheticStreams = new Map();
|
|
2749
|
+
tab.syntheticStreamTurn = 0;
|
|
2750
|
+
tab.pendingUserEcho = null;
|
|
2751
|
+
tab.plan = [];
|
|
2752
|
+
tab.usage = null;
|
|
2753
|
+
tab.terminals = new Map();
|
|
2754
|
+
tab.messageCounter = 0;
|
|
2755
|
+
tab.timelineCounter = 0;
|
|
2756
|
+
tab.currentModeId = '';
|
|
2757
|
+
tab.availableModes = [];
|
|
2758
|
+
tab.availableCommands = [];
|
|
2759
|
+
tab.configOptions = [];
|
|
2760
|
+
tab.restoreCapture = createRestoreCaptureState([]);
|
|
2761
|
+
tab.restoreCapture.nextTimelineOrder = () =>
|
|
2762
|
+
this.#nextTimelineOrder(tab);
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
#restoreSerializedTab(tab, snapshot) {
|
|
2766
|
+
tab.acpSessionId = snapshot.acpSessionId;
|
|
2767
|
+
tab.terminalSessionId = snapshot.terminalSessionId || '';
|
|
2768
|
+
tab.cwd = snapshot.cwd || tab.cwd;
|
|
2769
|
+
tab.createdAt = snapshot.createdAt || tab.createdAt;
|
|
2770
|
+
tab.status = snapshot.status || 'ready';
|
|
2771
|
+
tab.busy = !!snapshot.busy;
|
|
2772
|
+
tab.errorMessage = snapshot.errorMessage || '';
|
|
2773
|
+
tab.syntheticStreams = new Map();
|
|
2774
|
+
tab.pendingUserEcho = null;
|
|
2775
|
+
tab.restoreCapture = null;
|
|
2776
|
+
restorePersistedTabSnapshot(tab, snapshot);
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
async resumeIntoTab(tabId, meta) {
|
|
2780
|
+
await this.start();
|
|
2781
|
+
this.clearIdleShutdown();
|
|
2782
|
+
|
|
2783
|
+
const sessionCapabilities = this.#getSessionCapabilities();
|
|
2784
|
+
if (!sessionCapabilities.load) {
|
|
2785
|
+
throw new Error(
|
|
2786
|
+
`${this.definition.label} does not support session restore`
|
|
2787
|
+
);
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
const tab = this.tabs.get(tabId);
|
|
2791
|
+
if (!tab) {
|
|
2792
|
+
throw new Error('Agent tab not found');
|
|
2793
|
+
}
|
|
2794
|
+
if (tab.busy) {
|
|
2795
|
+
throw new Error('Agent tab is already running');
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
const targetSessionId = String(meta.acpSessionId || '').trim();
|
|
2799
|
+
if (!targetSessionId) {
|
|
2800
|
+
throw new Error('sessionId is required');
|
|
2801
|
+
}
|
|
2802
|
+
const existingTabId = this.sessionToTabId.get(targetSessionId);
|
|
2803
|
+
if (existingTabId && existingTabId !== tab.id) {
|
|
2804
|
+
throw new Error('Session is already open');
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
const previousSnapshot = this.serializeTab(tab);
|
|
2808
|
+
this.sessionToTabId.delete(tab.acpSessionId);
|
|
2809
|
+
this.#resetTabForSessionLoad(tab, {
|
|
2810
|
+
...meta,
|
|
2811
|
+
acpSessionId: targetSessionId
|
|
2812
|
+
});
|
|
2813
|
+
this.sessionToTabId.set(tab.acpSessionId, tab.id);
|
|
2814
|
+
this.#broadcast(tab, {
|
|
2815
|
+
type: 'snapshot',
|
|
2816
|
+
tab: this.serializeTab(tab)
|
|
2817
|
+
});
|
|
2818
|
+
|
|
2819
|
+
try {
|
|
2820
|
+
await this.#loadSessionIntoTab(tab, meta);
|
|
2821
|
+
this.#broadcast(tab, {
|
|
2822
|
+
type: 'snapshot',
|
|
2823
|
+
tab: this.serializeTab(tab)
|
|
2824
|
+
});
|
|
2825
|
+
return this.serializeTab(tab);
|
|
2826
|
+
} catch (error) {
|
|
2827
|
+
this.sessionToTabId.delete(tab.acpSessionId);
|
|
2828
|
+
this.#restoreSerializedTab(tab, previousSnapshot);
|
|
2829
|
+
this.sessionToTabId.set(tab.acpSessionId, tab.id);
|
|
2830
|
+
this.#broadcast(tab, {
|
|
2831
|
+
type: 'snapshot',
|
|
2832
|
+
tab: this.serializeTab(tab)
|
|
2833
|
+
});
|
|
2834
|
+
throw error;
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2736
2838
|
async #loadSessionIntoTab(tab, meta) {
|
|
2737
2839
|
try {
|
|
2738
2840
|
const response = await this.connection.loadSession({
|
|
@@ -4580,6 +4682,43 @@ export class AcpManager {
|
|
|
4580
4682
|
}
|
|
4581
4683
|
|
|
4582
4684
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
4685
|
+
const targetTabId = String(options.targetTabId || '').trim();
|
|
4686
|
+
if (targetTabId) {
|
|
4687
|
+
const tabEntry = this.tabs.get(targetTabId);
|
|
4688
|
+
if (!tabEntry) {
|
|
4689
|
+
throw new Error('Agent tab not found');
|
|
4690
|
+
}
|
|
4691
|
+
const currentSerialized = tabEntry.serialize();
|
|
4692
|
+
if (currentSerialized.agentId !== definition.id) {
|
|
4693
|
+
throw new Error('Agent mismatch');
|
|
4694
|
+
}
|
|
4695
|
+
const resumePromise = (async () => {
|
|
4696
|
+
const rawSerialized = await tabEntry.runtime.resumeIntoTab(
|
|
4697
|
+
targetTabId,
|
|
4698
|
+
{
|
|
4699
|
+
acpSessionId: options.sessionId,
|
|
4700
|
+
cwd,
|
|
4701
|
+
terminalSessionId: options.terminalSessionId || '',
|
|
4702
|
+
title: options.title || ''
|
|
4703
|
+
}
|
|
4704
|
+
);
|
|
4705
|
+
const serialized = this.#applyRuntimeMetadataFallback(
|
|
4706
|
+
tabEntry.runtime,
|
|
4707
|
+
rawSerialized
|
|
4708
|
+
);
|
|
4709
|
+
this.#clearDefinitionAvailabilityOverride(definition.id);
|
|
4710
|
+
await this.persistTabs();
|
|
4711
|
+
return serialized;
|
|
4712
|
+
})();
|
|
4713
|
+
this.pendingResumeTabs.set(resumeKey, resumePromise);
|
|
4714
|
+
|
|
4715
|
+
try {
|
|
4716
|
+
return await resumePromise;
|
|
4717
|
+
} finally {
|
|
4718
|
+
this.pendingResumeTabs.delete(resumeKey);
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
|
|
4583
4722
|
const { runtimeEntry, createdRuntime, runtimeStoreKey } =
|
|
4584
4723
|
this.#ensureRuntimeEntry(definition, cwd);
|
|
4585
4724
|
const tabId = crypto.randomUUID();
|
package/src/server.mjs
CHANGED
|
@@ -615,7 +615,7 @@ router.post('/api/agents/tabs', async (ctx) => {
|
|
|
615
615
|
});
|
|
616
616
|
|
|
617
617
|
router.post('/api/agents/tabs/resume', async (ctx) => {
|
|
618
|
-
const { agentId, cwd, terminalSessionId, sessionId, title } =
|
|
618
|
+
const { agentId, cwd, terminalSessionId, sessionId, targetTabId, title } =
|
|
619
619
|
ctx.request.body || {};
|
|
620
620
|
if (!agentId || typeof agentId !== 'string') {
|
|
621
621
|
ctx.status = 400;
|
|
@@ -639,6 +639,7 @@ router.post('/api/agents/tabs/resume', async (ctx) => {
|
|
|
639
639
|
agentId,
|
|
640
640
|
cwd,
|
|
641
641
|
sessionId,
|
|
642
|
+
targetTabId: typeof targetTabId === 'string' ? targetTabId : '',
|
|
642
643
|
title: typeof title === 'string' ? title : '',
|
|
643
644
|
terminalSessionId: typeof terminalSessionId === 'string'
|
|
644
645
|
? terminalSessionId
|