@agent-link/agent 0.1.324 → 0.1.325
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/dist/backends/index.js +2 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/backends/types.d.ts +11 -1
- package/dist/backends/types.js +74 -1
- package/dist/backends/types.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/connection.js +314 -28
- package/dist/connection.js.map +1 -1
- package/dist/pi.d.ts +109 -0
- package/dist/pi.js +1742 -0
- package/dist/pi.js.map +1 -0
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +17 -1
- package/dist/providers.js.map +1 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +5 -0
- package/dist/runtime.js.map +1 -1
- package/dist/sdk.d.ts +3 -1
- package/dist/sdk.js +42 -1
- package/dist/sdk.js.map +1 -1
- package/package.json +1 -1
package/dist/connection.js
CHANGED
|
@@ -29,7 +29,8 @@ const require = createRequire(import.meta.url);
|
|
|
29
29
|
const pkg = require('../package.json');
|
|
30
30
|
const TEAM_SESSIONS_DIR = path.join(CONFIG_DIR, 'team-sessions');
|
|
31
31
|
const LOOP_SESSIONS_DIR = path.join(CONFIG_DIR, 'loop-sessions');
|
|
32
|
-
import { handleChat as claudeHandleChat, addSendFn, abort as abortClaude, abortAll as abortAllClaude, cancelExecution as claudeCancelExecution, handleUserAnswer, handleToolPermissionResponse, handleBtwQuestion, getConversation, getConversations, getIsCompacting, clearSessionId, evictByClaudeSessionId, rebindConversation, addOutputObserver, removeOutputObserver, addCloseObserver, removeCloseObserver, setOutputObserver, clearOutputObserver, setCloseObserver, clearCloseObserver, restartConversation, createPlaceholderConversation, getPendingQuestions, getPendingToolPermissions, setModel, addOnSessionStarted, getBackendType, setBackendType, isCopilotBackend, isCodexBackend, addCopilotSendFn, handleCopilotChat, cancelCopilotExecution, listCopilotSessionsPage, listAllCopilotSessionsPage, readCopilotSessionMessages, getCopilotSessionWorkDir, deleteCopilotSession, renameCopilotSession, moveCopilotSession, getCopilotConversation, getCopilotConversations, createCopilotPlaceholder, restartCopilotConversation, rebindCopilotConversation, cleanupAllCopilotConversations, copilotCapabilities, addCodexSendFn, handleCodexChat, cancelCodexExecution, listCodexSessionsPage, listAllCodexSessionsPage, readCodexSessionMessages, deleteCodexSession, renameCodexSession, moveCodexSession, getCodexConversation, getCodexConversations, createCodexPlaceholder, restartCodexConversation, rebindCodexConversation, cleanupAllCodexConversations, evictByCodexSessionId, codexCapabilities } from './runtime.js';
|
|
32
|
+
import { handleChat as claudeHandleChat, addSendFn, abort as abortClaude, abortAll as abortAllClaude, cancelExecution as claudeCancelExecution, handleUserAnswer, handleToolPermissionResponse, handleBtwQuestion, getConversation, getConversations, getIsCompacting, clearSessionId, evictByClaudeSessionId, rebindConversation, addOutputObserver, removeOutputObserver, addCloseObserver, removeCloseObserver, setOutputObserver, clearOutputObserver, setCloseObserver, clearCloseObserver, restartConversation, createPlaceholderConversation, getPendingQuestions, getPendingToolPermissions, setModel, addOnSessionStarted, getBackendType, setBackendType, isCopilotBackend, isCodexBackend, isPiBackend, addCopilotSendFn, handleCopilotChat, cancelCopilotExecution, listCopilotSessionsPage, listAllCopilotSessionsPage, readCopilotSessionMessages, getCopilotSessionWorkDir, deleteCopilotSession, renameCopilotSession, moveCopilotSession, getCopilotConversation, getCopilotConversations, createCopilotPlaceholder, restartCopilotConversation, rebindCopilotConversation, cleanupAllCopilotConversations, copilotCapabilities, addCodexSendFn, handleCodexChat, cancelCodexExecution, listCodexSessionsPage, listAllCodexSessionsPage, readCodexSessionMessages, deleteCodexSession, renameCodexSession, moveCodexSession, getCodexConversation, getCodexConversations, createCodexPlaceholder, restartCodexConversation, rebindCodexConversation, cleanupAllCodexConversations, evictByCodexSessionId, codexCapabilities } from './runtime.js';
|
|
33
|
+
import { addPiSendFn, cancelPiExecution, cleanupAllPiConversations, createPiPlaceholder, deletePiSession, evictByPiSessionId, getPiConversation, getPiConversations, getPiSessionWorkDir, handlePiChat, listAllPiSessionsPage, listPiSessionsPage, movePiSession, piCapabilities, readPiSessionMessages, rebindPiConversation, renamePiSession, restartPiConversation, } from './pi.js';
|
|
33
34
|
import { DEFAULT_SESSION_LIST_LIMIT, LEGACY_SESSION_LIST_CAP, listSessions, listSessionsPageAsync, readSessionMessages, deleteSession, renameSession, moveSession, listAllRecentSessionsPage, getSessionCacheStats } from './history.js';
|
|
34
35
|
import { claudeCapabilities } from './backends/types.js';
|
|
35
36
|
import { searchSessions } from './search-sessions.js';
|
|
@@ -69,6 +70,7 @@ const state = {
|
|
|
69
70
|
let unsubscribeSend = null;
|
|
70
71
|
let unsubscribeCopilotSend = null;
|
|
71
72
|
let unsubscribeCodexSend = null;
|
|
73
|
+
let unsubscribePiSend = null;
|
|
72
74
|
let unsubscribeSessionStarted = null;
|
|
73
75
|
let heartbeatInterval = null;
|
|
74
76
|
let heartbeatTimeout = null;
|
|
@@ -84,6 +86,8 @@ function capabilitiesForBackend(backend) {
|
|
|
84
86
|
return copilotCapabilities();
|
|
85
87
|
case 'codex':
|
|
86
88
|
return codexCapabilities();
|
|
89
|
+
case 'pi':
|
|
90
|
+
return piCapabilities();
|
|
87
91
|
case 'claude':
|
|
88
92
|
default:
|
|
89
93
|
return claudeCapabilities();
|
|
@@ -93,7 +97,7 @@ function normalizeDirForCompare(value) {
|
|
|
93
97
|
const normalized = value.replace(/[\\/]+$/, '');
|
|
94
98
|
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
|
|
95
99
|
}
|
|
96
|
-
function hasActiveSessionConversation(backend, sessionId) {
|
|
100
|
+
function hasActiveSessionConversation(backend, sessionId, providerId, workDir) {
|
|
97
101
|
if (backend === 'copilot') {
|
|
98
102
|
for (const conv of getCopilotConversations().values()) {
|
|
99
103
|
if (conv.copilotSessionId === sessionId || conv.lastCopilotSessionId === sessionId) {
|
|
@@ -110,6 +114,16 @@ function hasActiveSessionConversation(backend, sessionId) {
|
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
}
|
|
117
|
+
else if (backend === 'pi') {
|
|
118
|
+
for (const conv of getPiConversations().values()) {
|
|
119
|
+
if ((conv.piSessionId === sessionId || conv.lastPiSessionId === sessionId)
|
|
120
|
+
&& (!providerId || conv.providerId === providerId)
|
|
121
|
+
&& (!workDir || normalizeDirForCompare(conv.workDir) === normalizeDirForCompare(workDir))) {
|
|
122
|
+
if (conv.turnActive)
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
113
127
|
else {
|
|
114
128
|
for (const conv of getConversations().values()) {
|
|
115
129
|
if (conv.claudeSessionId === sessionId || conv.lastClaudeSessionId === sessionId) {
|
|
@@ -120,7 +134,7 @@ function hasActiveSessionConversation(backend, sessionId) {
|
|
|
120
134
|
}
|
|
121
135
|
return false;
|
|
122
136
|
}
|
|
123
|
-
function syncMovedConversationWorkDir(backend, sessionId, toWorkDir) {
|
|
137
|
+
function syncMovedConversationWorkDir(backend, sessionId, toWorkDir, providerId, fromWorkDir) {
|
|
124
138
|
if (backend === 'copilot') {
|
|
125
139
|
for (const conv of getCopilotConversations().values()) {
|
|
126
140
|
if (conv.copilotSessionId === sessionId || conv.lastCopilotSessionId === sessionId)
|
|
@@ -133,6 +147,14 @@ function syncMovedConversationWorkDir(backend, sessionId, toWorkDir) {
|
|
|
133
147
|
conv.workDir = toWorkDir;
|
|
134
148
|
}
|
|
135
149
|
}
|
|
150
|
+
else if (backend === 'pi') {
|
|
151
|
+
for (const conv of getPiConversations().values()) {
|
|
152
|
+
if ((conv.piSessionId === sessionId || conv.lastPiSessionId === sessionId)
|
|
153
|
+
&& (!providerId || conv.providerId === providerId)
|
|
154
|
+
&& (!fromWorkDir || normalizeDirForCompare(conv.workDir) === normalizeDirForCompare(fromWorkDir)))
|
|
155
|
+
conv.workDir = toWorkDir;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
136
158
|
else {
|
|
137
159
|
for (const conv of getConversations().values()) {
|
|
138
160
|
if (conv.claudeSessionId === sessionId || conv.lastClaudeSessionId === sessionId)
|
|
@@ -195,6 +217,13 @@ export function initializeLocalConnection(config) {
|
|
|
195
217
|
catch { /* ignore */ }
|
|
196
218
|
}
|
|
197
219
|
unsubscribeCodexSend = addCodexSendFn(send);
|
|
220
|
+
if (unsubscribePiSend) {
|
|
221
|
+
try {
|
|
222
|
+
unsubscribePiSend();
|
|
223
|
+
}
|
|
224
|
+
catch { /* ignore */ }
|
|
225
|
+
}
|
|
226
|
+
unsubscribePiSend = addPiSendFn(send);
|
|
198
227
|
// Wire up action item session linkage
|
|
199
228
|
if (unsubscribeSessionStarted) {
|
|
200
229
|
try {
|
|
@@ -492,6 +521,7 @@ export function disconnect() {
|
|
|
492
521
|
abortAllClaude();
|
|
493
522
|
cleanupAllCopilotConversations();
|
|
494
523
|
cleanupAllCodexConversations();
|
|
524
|
+
cleanupAllPiConversations();
|
|
495
525
|
if (unsubscribeSend) {
|
|
496
526
|
try {
|
|
497
527
|
unsubscribeSend();
|
|
@@ -513,6 +543,13 @@ export function disconnect() {
|
|
|
513
543
|
catch { /* ignore */ }
|
|
514
544
|
unsubscribeCodexSend = null;
|
|
515
545
|
}
|
|
546
|
+
if (unsubscribePiSend) {
|
|
547
|
+
try {
|
|
548
|
+
unsubscribePiSend();
|
|
549
|
+
}
|
|
550
|
+
catch { /* ignore */ }
|
|
551
|
+
unsubscribePiSend = null;
|
|
552
|
+
}
|
|
516
553
|
if (unsubscribeSessionStarted) {
|
|
517
554
|
try {
|
|
518
555
|
unsubscribeSessionStarted();
|
|
@@ -625,7 +662,8 @@ export function getConnectionStatus() {
|
|
|
625
662
|
sessionRegistration: state.sessionRegistration,
|
|
626
663
|
conversations: getConversations().size
|
|
627
664
|
+ getCopilotConversations().size
|
|
628
|
-
+ getCodexConversations().size
|
|
665
|
+
+ getCodexConversations().size
|
|
666
|
+
+ getPiConversations().size,
|
|
629
667
|
};
|
|
630
668
|
}
|
|
631
669
|
function buildWsUrl(config) {
|
|
@@ -854,7 +892,20 @@ function handleServerMessage(msg) {
|
|
|
854
892
|
}
|
|
855
893
|
}
|
|
856
894
|
// Route to the selected backend.
|
|
857
|
-
if (
|
|
895
|
+
if (isPiBackend()) {
|
|
896
|
+
const piMsg = msg;
|
|
897
|
+
const piState = chatConvId ? getPiConversation(chatConvId) : undefined;
|
|
898
|
+
const piResumeDir = chatOptions.resumeSessionId
|
|
899
|
+
? getPiSessionWorkDir(chatOptions.resumeSessionId, effectiveProviderId || 'pi', explicitWorkDir)
|
|
900
|
+
: undefined;
|
|
901
|
+
const piWorkDir = explicitWorkDir || piState?.workDir || piResumeDir || chatDir;
|
|
902
|
+
handlePiChat(chatConvId || '', piMsg.prompt, piWorkDir, {
|
|
903
|
+
resumeSessionId: chatOptions.resumeSessionId,
|
|
904
|
+
providerId: effectiveProviderId,
|
|
905
|
+
piOptions: piMsg.piOptions,
|
|
906
|
+
}, piMsg.files);
|
|
907
|
+
}
|
|
908
|
+
else if (isCodexBackend()) {
|
|
858
909
|
const codexMsg = msg;
|
|
859
910
|
handleCodexChat(chatConvId || '', codexMsg.prompt, chatDir, {
|
|
860
911
|
resumeSessionId: chatOptions.resumeSessionId,
|
|
@@ -876,14 +927,34 @@ function handleServerMessage(msg) {
|
|
|
876
927
|
break;
|
|
877
928
|
}
|
|
878
929
|
case 'cancel_execution':
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
930
|
+
{
|
|
931
|
+
const conversationId = msg.conversationId || '';
|
|
932
|
+
// Cancellation follows conversation ownership, not the currently selected
|
|
933
|
+
// provider. This keeps background turns cancellable after a tab switches.
|
|
934
|
+
if (getPiConversation(conversationId)) {
|
|
935
|
+
cancelPiExecution(conversationId);
|
|
936
|
+
}
|
|
937
|
+
else if (getCodexConversation(conversationId)) {
|
|
938
|
+
cancelCodexExecution(conversationId);
|
|
939
|
+
}
|
|
940
|
+
else if (getCopilotConversation(conversationId)) {
|
|
941
|
+
cancelCopilotExecution(conversationId);
|
|
942
|
+
}
|
|
943
|
+
else if (getConversation(conversationId)) {
|
|
944
|
+
claudeCancelExecution(conversationId);
|
|
945
|
+
}
|
|
946
|
+
else if (isPiBackend()) {
|
|
947
|
+
cancelPiExecution(conversationId);
|
|
948
|
+
}
|
|
949
|
+
else if (isCodexBackend()) {
|
|
950
|
+
cancelCodexExecution(conversationId);
|
|
951
|
+
}
|
|
952
|
+
else if (isCopilotBackend()) {
|
|
953
|
+
cancelCopilotExecution(conversationId);
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
claudeCancelExecution(conversationId || undefined);
|
|
957
|
+
}
|
|
887
958
|
}
|
|
888
959
|
break;
|
|
889
960
|
case 'list_sessions': {
|
|
@@ -893,7 +964,17 @@ function handleServerMessage(msg) {
|
|
|
893
964
|
const sessionBackend = m.providerId
|
|
894
965
|
? getBackendTypeForProvider(m.providerId)
|
|
895
966
|
: getBackendType();
|
|
896
|
-
if (sessionBackend === '
|
|
967
|
+
if (sessionBackend === 'pi') {
|
|
968
|
+
handleListPiSessionsFn(dir, {
|
|
969
|
+
limit: m.limit,
|
|
970
|
+
cursor: m.cursor,
|
|
971
|
+
mode: m.mode,
|
|
972
|
+
requestId: m.requestId,
|
|
973
|
+
providerId: m.providerId,
|
|
974
|
+
legacy: m.limit == null && !m.cursor && !m.mode && !m.requestId,
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
else if (sessionBackend === 'codex') {
|
|
897
978
|
handleListCodexSessionsFn(dir, {
|
|
898
979
|
limit: m.limit,
|
|
899
980
|
cursor: m.cursor,
|
|
@@ -933,7 +1014,25 @@ function handleServerMessage(msg) {
|
|
|
933
1014
|
const recentBackend = m.providerId
|
|
934
1015
|
? getBackendTypeForProvider(m.providerId)
|
|
935
1016
|
: getBackendType();
|
|
936
|
-
if (recentBackend === '
|
|
1017
|
+
if (recentBackend === 'pi') {
|
|
1018
|
+
const page = listAllPiSessionsPage({
|
|
1019
|
+
limit: m.limit,
|
|
1020
|
+
cursor: m.cursor,
|
|
1021
|
+
providerId: m.providerId || 'pi',
|
|
1022
|
+
});
|
|
1023
|
+
console.log(`[AgentLink] → recent_sessions_list (${page.sessions.length} PI sessions) hasMore=${page.hasMore}`);
|
|
1024
|
+
send({
|
|
1025
|
+
type: 'recent_sessions_list',
|
|
1026
|
+
sessions: page.sessions,
|
|
1027
|
+
hasMore: page.hasMore,
|
|
1028
|
+
nextCursor: page.nextCursor,
|
|
1029
|
+
indexState: page.indexState,
|
|
1030
|
+
...(m.mode ? { mode: m.mode } : {}),
|
|
1031
|
+
...(m.requestId ? { requestId: m.requestId } : {}),
|
|
1032
|
+
providerId: m.providerId || 'pi',
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
else if (recentBackend === 'codex') {
|
|
937
1036
|
const page = listAllCodexSessionsPage({ limit: m.limit, cursor: m.cursor });
|
|
938
1037
|
console.log(`[AgentLink] → recent_sessions_list (${page.sessions.length} codex sessions) hasMore=${page.hasMore}`);
|
|
939
1038
|
send({
|
|
@@ -1080,6 +1179,51 @@ function handleServerMessage(msg) {
|
|
|
1080
1179
|
const resumeBackend = m.providerId
|
|
1081
1180
|
? getBackendTypeForProvider(m.providerId)
|
|
1082
1181
|
: getBackendType();
|
|
1182
|
+
if (resumeBackend === 'pi') {
|
|
1183
|
+
const persistedWorkDir = getPiSessionWorkDir(m.claudeSessionId, m.providerId || 'pi', explicitWorkDir);
|
|
1184
|
+
if (convId) {
|
|
1185
|
+
rebindPiConversation(m.claudeSessionId, convId, m.providerId || 'pi', explicitWorkDir || persistedWorkDir);
|
|
1186
|
+
}
|
|
1187
|
+
const reboundState = getPiConversation(convId || '');
|
|
1188
|
+
const authoritativeWorkDir = reboundState?.workDir || persistedWorkDir;
|
|
1189
|
+
const resolvedWorkDir = explicitWorkDir || authoritativeWorkDir || state.workDir;
|
|
1190
|
+
if (!authoritativeWorkDir
|
|
1191
|
+
|| normalizeDirForCompare(authoritativeWorkDir) !== normalizeDirForCompare(resolvedWorkDir)) {
|
|
1192
|
+
send({
|
|
1193
|
+
type: 'error',
|
|
1194
|
+
message: `PI session ${m.claudeSessionId} was not found in ${resolvedWorkDir}.`,
|
|
1195
|
+
conversationId: convId,
|
|
1196
|
+
});
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1199
|
+
if (convId && !getPiConversation(convId)) {
|
|
1200
|
+
createPiPlaceholder(convId, { providerId: m.providerId || 'pi', workDir: resolvedWorkDir });
|
|
1201
|
+
}
|
|
1202
|
+
const piState = getPiConversation(convId || '');
|
|
1203
|
+
if (piState) {
|
|
1204
|
+
piState.piSessionId = m.claudeSessionId;
|
|
1205
|
+
piState.lastPiSessionId = m.claudeSessionId;
|
|
1206
|
+
piState.workDir = resolvedWorkDir;
|
|
1207
|
+
}
|
|
1208
|
+
const history = readPiSessionMessages(resolvedWorkDir, m.claudeSessionId, m.providerId || 'pi');
|
|
1209
|
+
const isPiProcessing = !!(piState?.turnActive && piState.child);
|
|
1210
|
+
console.log(`[AgentLink] → conversation_resumed (${history.length} PI messages, session ${m.claudeSessionId.slice(0, 8)})`);
|
|
1211
|
+
send({
|
|
1212
|
+
type: 'conversation_resumed',
|
|
1213
|
+
conversationId: convId,
|
|
1214
|
+
claudeSessionId: m.claudeSessionId,
|
|
1215
|
+
history,
|
|
1216
|
+
isCompacting: false,
|
|
1217
|
+
isProcessing: isPiProcessing,
|
|
1218
|
+
planMode: false,
|
|
1219
|
+
permissionMode: 'normal',
|
|
1220
|
+
workDir: resolvedWorkDir,
|
|
1221
|
+
providerId: m.providerId || 'pi',
|
|
1222
|
+
...(typeof m.scrollToMessageIdx === 'number' ? { scrollToMessageIdx: m.scrollToMessageIdx } : {}),
|
|
1223
|
+
...(m.searchQuery ? { searchQuery: m.searchQuery } : {}),
|
|
1224
|
+
});
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1083
1227
|
if (resumeBackend === 'codex') {
|
|
1084
1228
|
if (convId) {
|
|
1085
1229
|
rebindCodexConversation(m.claudeSessionId, convId);
|
|
@@ -1259,7 +1403,27 @@ function handleServerMessage(msg) {
|
|
|
1259
1403
|
const dir = m.workDir?.trim()
|
|
1260
1404
|
? path.resolve(m.workDir)
|
|
1261
1405
|
: (m.providerId === 'brain' ? BRAIN_DATA_DIR : state.workDir);
|
|
1262
|
-
if (sessionBackend === '
|
|
1406
|
+
if (sessionBackend === 'pi') {
|
|
1407
|
+
if (evictByPiSessionId(m.sessionId, m.providerId || 'pi', dir)) {
|
|
1408
|
+
send({ type: 'error', message: 'Cannot delete a session while it is processing.' });
|
|
1409
|
+
break;
|
|
1410
|
+
}
|
|
1411
|
+
const ok = deletePiSession(dir, m.sessionId, m.providerId || 'pi');
|
|
1412
|
+
console.log(`[AgentLink] delete PI session ${m.sessionId}: ${ok}`);
|
|
1413
|
+
if (ok) {
|
|
1414
|
+
send({
|
|
1415
|
+
type: 'session_deleted',
|
|
1416
|
+
sessionId: m.sessionId,
|
|
1417
|
+
providerId: m.providerId || 'pi',
|
|
1418
|
+
workDir: dir,
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
else {
|
|
1422
|
+
send({ type: 'error', message: 'Session not found or could not be deleted.' });
|
|
1423
|
+
}
|
|
1424
|
+
handleListPiSessionsFn(dir, { providerId: m.providerId || 'pi' });
|
|
1425
|
+
}
|
|
1426
|
+
else if (sessionBackend === 'codex') {
|
|
1263
1427
|
if (evictByCodexSessionId(m.sessionId)) {
|
|
1264
1428
|
send({ type: 'error', message: 'Cannot delete a session while it is processing.' });
|
|
1265
1429
|
break;
|
|
@@ -1290,7 +1454,28 @@ function handleServerMessage(msg) {
|
|
|
1290
1454
|
const dir = m.workDir?.trim()
|
|
1291
1455
|
? path.resolve(m.workDir)
|
|
1292
1456
|
: (m.providerId === 'brain' ? BRAIN_DATA_DIR : state.workDir);
|
|
1293
|
-
if (sessionBackend === '
|
|
1457
|
+
if (sessionBackend === 'pi') {
|
|
1458
|
+
if (hasActiveSessionConversation('pi', m.sessionId, m.providerId || 'pi', dir)) {
|
|
1459
|
+
send({ type: 'error', message: 'Cannot rename a session while it is processing.' });
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
const ok = renamePiSession(dir, m.sessionId, m.newTitle, m.providerId || 'pi');
|
|
1463
|
+
console.log(`[AgentLink] rename PI session ${m.sessionId}: ${ok}`);
|
|
1464
|
+
if (ok) {
|
|
1465
|
+
send({
|
|
1466
|
+
type: 'session_renamed',
|
|
1467
|
+
sessionId: m.sessionId,
|
|
1468
|
+
newTitle: m.newTitle,
|
|
1469
|
+
providerId: m.providerId || 'pi',
|
|
1470
|
+
workDir: dir,
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
send({ type: 'error', message: 'Session not found or could not be renamed.' });
|
|
1475
|
+
}
|
|
1476
|
+
handleListPiSessionsFn(dir, { providerId: m.providerId || 'pi' });
|
|
1477
|
+
}
|
|
1478
|
+
else if (sessionBackend === 'codex') {
|
|
1294
1479
|
const ok = renameCodexSession(dir, m.sessionId, m.newTitle);
|
|
1295
1480
|
console.log(`[AgentLink] rename codex session ${m.sessionId}: ${ok}`);
|
|
1296
1481
|
if (ok)
|
|
@@ -1328,12 +1513,15 @@ function handleServerMessage(msg) {
|
|
|
1328
1513
|
const sessionBackend = m.providerId
|
|
1329
1514
|
? getBackendTypeForProvider(m.providerId)
|
|
1330
1515
|
: getBackendType();
|
|
1331
|
-
if (hasActiveSessionConversation(sessionBackend, m.sessionId)) {
|
|
1516
|
+
if (hasActiveSessionConversation(sessionBackend, m.sessionId, m.providerId, fromDir)) {
|
|
1332
1517
|
send({ type: 'error', message: 'Cannot move a session while it is processing.' });
|
|
1333
1518
|
break;
|
|
1334
1519
|
}
|
|
1335
1520
|
let ok = false;
|
|
1336
|
-
if (sessionBackend === '
|
|
1521
|
+
if (sessionBackend === 'pi') {
|
|
1522
|
+
ok = movePiSession(fromDir, toDir, m.sessionId, m.providerId || 'pi');
|
|
1523
|
+
}
|
|
1524
|
+
else if (sessionBackend === 'codex') {
|
|
1337
1525
|
ok = moveCodexSession(fromDir, toDir, m.sessionId);
|
|
1338
1526
|
}
|
|
1339
1527
|
else if (sessionBackend === 'copilot') {
|
|
@@ -1343,7 +1531,7 @@ function handleServerMessage(msg) {
|
|
|
1343
1531
|
ok = moveSession(fromDir, toDir, m.sessionId);
|
|
1344
1532
|
}
|
|
1345
1533
|
if (ok) {
|
|
1346
|
-
syncMovedConversationWorkDir(sessionBackend, m.sessionId, toDir);
|
|
1534
|
+
syncMovedConversationWorkDir(sessionBackend, m.sessionId, toDir, m.providerId, fromDir);
|
|
1347
1535
|
if (sessionBackend === 'claude') {
|
|
1348
1536
|
evictByClaudeSessionId(m.sessionId);
|
|
1349
1537
|
}
|
|
@@ -1353,9 +1541,15 @@ function handleServerMessage(msg) {
|
|
|
1353
1541
|
sessionId: m.sessionId,
|
|
1354
1542
|
fromWorkDir: fromDir,
|
|
1355
1543
|
toWorkDir: toDir,
|
|
1356
|
-
...(
|
|
1544
|
+
...(sessionBackend === 'pi'
|
|
1545
|
+
? { providerId: m.providerId || 'pi', workDir: toDir }
|
|
1546
|
+
: (m.providerId ? { providerId: m.providerId } : {})),
|
|
1357
1547
|
});
|
|
1358
|
-
if (sessionBackend === '
|
|
1548
|
+
if (sessionBackend === 'pi') {
|
|
1549
|
+
handleListPiSessionsFn(fromDir, { providerId: m.providerId || 'pi' });
|
|
1550
|
+
handleListPiSessionsFn(toDir, { providerId: m.providerId || 'pi' });
|
|
1551
|
+
}
|
|
1552
|
+
else if (sessionBackend === 'codex') {
|
|
1359
1553
|
handleListCodexSessionsFn(fromDir, { providerId: m.providerId });
|
|
1360
1554
|
handleListCodexSessionsFn(toDir, { providerId: m.providerId });
|
|
1361
1555
|
}
|
|
@@ -1385,7 +1579,13 @@ function handleServerMessage(msg) {
|
|
|
1385
1579
|
break;
|
|
1386
1580
|
}
|
|
1387
1581
|
let readResult;
|
|
1388
|
-
if (exportBackend === '
|
|
1582
|
+
if (exportBackend === 'pi') {
|
|
1583
|
+
readResult = {
|
|
1584
|
+
history: readPiSessionMessages(dir, m.sessionId, m.providerId || 'pi'),
|
|
1585
|
+
workDir: dir,
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
else if (exportBackend === 'codex') {
|
|
1389
1589
|
readResult = { history: readCodexSessionMessages(dir, m.sessionId), workDir: dir };
|
|
1390
1590
|
}
|
|
1391
1591
|
else if (exportBackend === 'copilot') {
|
|
@@ -1425,14 +1625,17 @@ function handleServerMessage(msg) {
|
|
|
1425
1625
|
});
|
|
1426
1626
|
break;
|
|
1427
1627
|
}
|
|
1428
|
-
sendSessionExportResult(requestId, m.sessionId, result
|
|
1628
|
+
sendSessionExportResult(requestId, m.sessionId, result, {
|
|
1629
|
+
providerId: m.providerId || (exportBackend === 'pi' ? 'pi' : undefined),
|
|
1630
|
+
workDir: readResult.workDir,
|
|
1631
|
+
});
|
|
1429
1632
|
break;
|
|
1430
1633
|
}
|
|
1431
1634
|
case 'query_active_conversations': {
|
|
1432
1635
|
const request = msg;
|
|
1433
1636
|
const active = [];
|
|
1434
1637
|
const pushActive = (convId, conv) => {
|
|
1435
|
-
const sessionId = conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? null;
|
|
1638
|
+
const sessionId = conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.piSessionId ?? null;
|
|
1436
1639
|
if (conv.turnActive) {
|
|
1437
1640
|
active.push({
|
|
1438
1641
|
conversationId: convId,
|
|
@@ -1465,6 +1668,9 @@ function handleServerMessage(msg) {
|
|
|
1465
1668
|
for (const [convId, conv] of getCodexConversations()) {
|
|
1466
1669
|
pushActive(convId, conv);
|
|
1467
1670
|
}
|
|
1671
|
+
for (const [convId, conv] of getPiConversations()) {
|
|
1672
|
+
pushActive(convId, conv);
|
|
1673
|
+
}
|
|
1468
1674
|
const activeTeamState = getActiveTeam();
|
|
1469
1675
|
// Collect running loop executions for status recovery
|
|
1470
1676
|
const runningLoops = [];
|
|
@@ -1491,7 +1697,7 @@ function handleServerMessage(msg) {
|
|
|
1491
1697
|
return {
|
|
1492
1698
|
type: 'turn_status',
|
|
1493
1699
|
conversationId: convId,
|
|
1494
|
-
claudeSessionId: conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? null,
|
|
1700
|
+
claudeSessionId: conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.piSessionId ?? null,
|
|
1495
1701
|
turnId: conv.turnId,
|
|
1496
1702
|
status,
|
|
1497
1703
|
isProcessing: isProc,
|
|
@@ -1526,6 +1732,15 @@ function handleServerMessage(msg) {
|
|
|
1526
1732
|
}
|
|
1527
1733
|
}
|
|
1528
1734
|
}
|
|
1735
|
+
if (!found) {
|
|
1736
|
+
for (const [convId, conv] of getPiConversations()) {
|
|
1737
|
+
if (conv.turnId === m.turnId) {
|
|
1738
|
+
send(buildTurnStatus(convId, conv));
|
|
1739
|
+
found = true;
|
|
1740
|
+
break;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1529
1744
|
if (!found) {
|
|
1530
1745
|
send({ type: 'turn_status', turnId: m.turnId, status: 'unknown', isProcessing: false, isCompacting: false, stateVersion: 0 });
|
|
1531
1746
|
}
|
|
@@ -1546,7 +1761,13 @@ function handleServerMessage(msg) {
|
|
|
1546
1761
|
send(buildTurnStatus(m.conversationId, codexConv));
|
|
1547
1762
|
}
|
|
1548
1763
|
else {
|
|
1549
|
-
|
|
1764
|
+
const piConv = getPiConversation(m.conversationId);
|
|
1765
|
+
if (piConv) {
|
|
1766
|
+
send(buildTurnStatus(m.conversationId, piConv));
|
|
1767
|
+
}
|
|
1768
|
+
else {
|
|
1769
|
+
send({ type: 'turn_status', conversationId: m.conversationId, status: 'unknown', isProcessing: false, isCompacting: false, stateVersion: 0 });
|
|
1770
|
+
}
|
|
1550
1771
|
}
|
|
1551
1772
|
}
|
|
1552
1773
|
}
|
|
@@ -1746,6 +1967,21 @@ function handleServerMessage(msg) {
|
|
|
1746
1967
|
setBackendType(newBackendType);
|
|
1747
1968
|
console.log(`[AgentLink] Backend switched: ${oldBackendType} → ${newBackendType}`);
|
|
1748
1969
|
}
|
|
1970
|
+
else if (oldBackendType === 'pi') {
|
|
1971
|
+
const existingPiConversation = conversationId ? getPiConversation(conversationId) : undefined;
|
|
1972
|
+
if (existingPiConversation?.turnActive) {
|
|
1973
|
+
// Keep the live process bound to the provider that launched it.
|
|
1974
|
+
// The next chat carries the new providerId and restarts PI after settlement.
|
|
1975
|
+
}
|
|
1976
|
+
else if (conversationId && existingPiConversation) {
|
|
1977
|
+
const result = restartPiConversation(conversationId, { providerId });
|
|
1978
|
+
if (result.wasTurnActive)
|
|
1979
|
+
send({ type: 'execution_cancelled', conversationId });
|
|
1980
|
+
}
|
|
1981
|
+
else if (conversationId) {
|
|
1982
|
+
createPiPlaceholder(conversationId, { providerId });
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1749
1985
|
else if (oldBackendType === 'codex') {
|
|
1750
1986
|
const existingCodexConv = conversationId ? getCodexConversation(conversationId) : undefined;
|
|
1751
1987
|
if (existingCodexConv?.turnActive) {
|
|
@@ -2434,7 +2670,53 @@ function handleListCodexSessionsFn(dir, options = {}) {
|
|
|
2434
2670
|
});
|
|
2435
2671
|
}
|
|
2436
2672
|
}
|
|
2437
|
-
function
|
|
2673
|
+
function handleListPiSessionsFn(dir, options = {}) {
|
|
2674
|
+
try {
|
|
2675
|
+
const page = options.legacy
|
|
2676
|
+
? {
|
|
2677
|
+
...listPiSessionsPage(dir, {
|
|
2678
|
+
limit: Number.POSITIVE_INFINITY,
|
|
2679
|
+
providerId: options.providerId || 'pi',
|
|
2680
|
+
}),
|
|
2681
|
+
hasMore: false,
|
|
2682
|
+
}
|
|
2683
|
+
: listPiSessionsPage(dir, {
|
|
2684
|
+
limit: options.limit ?? DEFAULT_SESSION_LIST_LIMIT,
|
|
2685
|
+
cursor: options.cursor,
|
|
2686
|
+
providerId: options.providerId || 'pi',
|
|
2687
|
+
});
|
|
2688
|
+
if (options.legacy && page.sessions.length > LEGACY_SESSION_LIST_CAP) {
|
|
2689
|
+
page.sessions = page.sessions.slice(0, LEGACY_SESSION_LIST_CAP);
|
|
2690
|
+
}
|
|
2691
|
+
console.log(`[AgentLink] → sessions_list (${page.sessions.length} PI sessions for ${dir}) hasMore=${page.hasMore}`);
|
|
2692
|
+
send({
|
|
2693
|
+
type: 'sessions_list',
|
|
2694
|
+
sessions: page.sessions,
|
|
2695
|
+
workDir: dir,
|
|
2696
|
+
hasMore: page.hasMore,
|
|
2697
|
+
nextCursor: page.nextCursor,
|
|
2698
|
+
indexState: 'ready',
|
|
2699
|
+
...(options.mode ? { mode: options.mode } : {}),
|
|
2700
|
+
...(options.requestId ? { requestId: options.requestId } : {}),
|
|
2701
|
+
providerId: options.providerId || 'pi',
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
catch (err) {
|
|
2705
|
+
console.error('[AgentLink] listPiSessions failed:', err);
|
|
2706
|
+
send({
|
|
2707
|
+
type: 'sessions_list',
|
|
2708
|
+
sessions: [],
|
|
2709
|
+
workDir: dir,
|
|
2710
|
+
hasMore: false,
|
|
2711
|
+
indexState: 'ready',
|
|
2712
|
+
error: err.message,
|
|
2713
|
+
...(options.mode ? { mode: options.mode } : {}),
|
|
2714
|
+
...(options.requestId ? { requestId: options.requestId } : {}),
|
|
2715
|
+
providerId: options.providerId || 'pi',
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
function sendSessionExportResult(requestId, sessionId, result, scope) {
|
|
2438
2720
|
const totalChunks = Math.max(1, Math.ceil(result.content.length / SESSION_EXPORT_CHUNK_SIZE));
|
|
2439
2721
|
console.log(`[AgentLink] → session_exported (${result.format}, ${Buffer.byteLength(result.content, 'utf8')} bytes, ${totalChunks} chunks)`);
|
|
2440
2722
|
if (totalChunks === 1) {
|
|
@@ -2443,6 +2725,8 @@ function sendSessionExportResult(requestId, sessionId, result) {
|
|
|
2443
2725
|
requestId,
|
|
2444
2726
|
sessionId,
|
|
2445
2727
|
...result,
|
|
2728
|
+
...(scope?.providerId ? { providerId: scope.providerId } : {}),
|
|
2729
|
+
...(scope?.workDir ? { workDir: scope.workDir } : {}),
|
|
2446
2730
|
});
|
|
2447
2731
|
return;
|
|
2448
2732
|
}
|
|
@@ -2458,6 +2742,8 @@ function sendSessionExportResult(requestId, sessionId, result) {
|
|
|
2458
2742
|
chunkIndex,
|
|
2459
2743
|
totalChunks,
|
|
2460
2744
|
content: result.content.slice(start, start + SESSION_EXPORT_CHUNK_SIZE),
|
|
2745
|
+
...(scope?.providerId ? { providerId: scope.providerId } : {}),
|
|
2746
|
+
...(scope?.workDir ? { workDir: scope.workDir } : {}),
|
|
2461
2747
|
});
|
|
2462
2748
|
}
|
|
2463
2749
|
}
|