@agent-link/agent 0.1.323 → 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 +322 -30
- package/dist/connection.js.map +1 -1
- package/dist/copilot.d.ts +1 -0
- package/dist/copilot.js +16 -0
- package/dist/copilot.js.map +1 -1
- package/dist/directory-handlers.d.ts +1 -0
- package/dist/directory-handlers.js +2 -0
- package/dist/directory-handlers.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 +3 -1
- package/dist/runtime.js +6 -1
- 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, 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);
|
|
@@ -1113,6 +1257,12 @@ function handleServerMessage(msg) {
|
|
|
1113
1257
|
}
|
|
1114
1258
|
// Route to copilot backend
|
|
1115
1259
|
if (resumeBackend === 'copilot') {
|
|
1260
|
+
// Copilot rewrites workspace.yaml using the cwd of each resume process.
|
|
1261
|
+
// Recover the persisted cwd before creating a placeholder so a reconnect
|
|
1262
|
+
// that omits workDir cannot silently move the session to the agent cwd.
|
|
1263
|
+
const resolvedWorkDir = explicitWorkDir
|
|
1264
|
+
|| getCopilotSessionWorkDir(m.claudeSessionId)
|
|
1265
|
+
|| state.workDir;
|
|
1116
1266
|
// Rebind running copilot process to new conversationId (handles page refresh)
|
|
1117
1267
|
if (convId) {
|
|
1118
1268
|
rebindCopilotConversation(m.claudeSessionId, convId);
|
|
@@ -1123,10 +1273,9 @@ function handleServerMessage(msg) {
|
|
|
1123
1273
|
const placeholder = getCopilotConversation(convId);
|
|
1124
1274
|
if (placeholder) {
|
|
1125
1275
|
placeholder.copilotSessionId = m.claudeSessionId;
|
|
1126
|
-
placeholder.workDir =
|
|
1276
|
+
placeholder.workDir = resolvedWorkDir;
|
|
1127
1277
|
}
|
|
1128
1278
|
}
|
|
1129
|
-
const resolvedWorkDir = explicitWorkDir || state.workDir;
|
|
1130
1279
|
const history = readCopilotSessionMessages(resolvedWorkDir, m.claudeSessionId);
|
|
1131
1280
|
// Check if this session has an active copilot process running
|
|
1132
1281
|
const copilotState = getCopilotConversation(convId || '');
|
|
@@ -1141,6 +1290,7 @@ function handleServerMessage(msg) {
|
|
|
1141
1290
|
isProcessing: isCopilotProcessing,
|
|
1142
1291
|
planMode: false,
|
|
1143
1292
|
permissionMode: 'normal',
|
|
1293
|
+
workDir: resolvedWorkDir,
|
|
1144
1294
|
...(typeof m.scrollToMessageIdx === 'number' ? { scrollToMessageIdx: m.scrollToMessageIdx } : {}),
|
|
1145
1295
|
...(m.searchQuery ? { searchQuery: m.searchQuery } : {}),
|
|
1146
1296
|
});
|
|
@@ -1253,7 +1403,27 @@ function handleServerMessage(msg) {
|
|
|
1253
1403
|
const dir = m.workDir?.trim()
|
|
1254
1404
|
? path.resolve(m.workDir)
|
|
1255
1405
|
: (m.providerId === 'brain' ? BRAIN_DATA_DIR : state.workDir);
|
|
1256
|
-
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') {
|
|
1257
1427
|
if (evictByCodexSessionId(m.sessionId)) {
|
|
1258
1428
|
send({ type: 'error', message: 'Cannot delete a session while it is processing.' });
|
|
1259
1429
|
break;
|
|
@@ -1284,7 +1454,28 @@ function handleServerMessage(msg) {
|
|
|
1284
1454
|
const dir = m.workDir?.trim()
|
|
1285
1455
|
? path.resolve(m.workDir)
|
|
1286
1456
|
: (m.providerId === 'brain' ? BRAIN_DATA_DIR : state.workDir);
|
|
1287
|
-
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') {
|
|
1288
1479
|
const ok = renameCodexSession(dir, m.sessionId, m.newTitle);
|
|
1289
1480
|
console.log(`[AgentLink] rename codex session ${m.sessionId}: ${ok}`);
|
|
1290
1481
|
if (ok)
|
|
@@ -1322,12 +1513,15 @@ function handleServerMessage(msg) {
|
|
|
1322
1513
|
const sessionBackend = m.providerId
|
|
1323
1514
|
? getBackendTypeForProvider(m.providerId)
|
|
1324
1515
|
: getBackendType();
|
|
1325
|
-
if (hasActiveSessionConversation(sessionBackend, m.sessionId)) {
|
|
1516
|
+
if (hasActiveSessionConversation(sessionBackend, m.sessionId, m.providerId, fromDir)) {
|
|
1326
1517
|
send({ type: 'error', message: 'Cannot move a session while it is processing.' });
|
|
1327
1518
|
break;
|
|
1328
1519
|
}
|
|
1329
1520
|
let ok = false;
|
|
1330
|
-
if (sessionBackend === '
|
|
1521
|
+
if (sessionBackend === 'pi') {
|
|
1522
|
+
ok = movePiSession(fromDir, toDir, m.sessionId, m.providerId || 'pi');
|
|
1523
|
+
}
|
|
1524
|
+
else if (sessionBackend === 'codex') {
|
|
1331
1525
|
ok = moveCodexSession(fromDir, toDir, m.sessionId);
|
|
1332
1526
|
}
|
|
1333
1527
|
else if (sessionBackend === 'copilot') {
|
|
@@ -1337,7 +1531,7 @@ function handleServerMessage(msg) {
|
|
|
1337
1531
|
ok = moveSession(fromDir, toDir, m.sessionId);
|
|
1338
1532
|
}
|
|
1339
1533
|
if (ok) {
|
|
1340
|
-
syncMovedConversationWorkDir(sessionBackend, m.sessionId, toDir);
|
|
1534
|
+
syncMovedConversationWorkDir(sessionBackend, m.sessionId, toDir, m.providerId, fromDir);
|
|
1341
1535
|
if (sessionBackend === 'claude') {
|
|
1342
1536
|
evictByClaudeSessionId(m.sessionId);
|
|
1343
1537
|
}
|
|
@@ -1347,9 +1541,15 @@ function handleServerMessage(msg) {
|
|
|
1347
1541
|
sessionId: m.sessionId,
|
|
1348
1542
|
fromWorkDir: fromDir,
|
|
1349
1543
|
toWorkDir: toDir,
|
|
1350
|
-
...(
|
|
1544
|
+
...(sessionBackend === 'pi'
|
|
1545
|
+
? { providerId: m.providerId || 'pi', workDir: toDir }
|
|
1546
|
+
: (m.providerId ? { providerId: m.providerId } : {})),
|
|
1351
1547
|
});
|
|
1352
|
-
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') {
|
|
1353
1553
|
handleListCodexSessionsFn(fromDir, { providerId: m.providerId });
|
|
1354
1554
|
handleListCodexSessionsFn(toDir, { providerId: m.providerId });
|
|
1355
1555
|
}
|
|
@@ -1379,7 +1579,13 @@ function handleServerMessage(msg) {
|
|
|
1379
1579
|
break;
|
|
1380
1580
|
}
|
|
1381
1581
|
let readResult;
|
|
1382
|
-
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') {
|
|
1383
1589
|
readResult = { history: readCodexSessionMessages(dir, m.sessionId), workDir: dir };
|
|
1384
1590
|
}
|
|
1385
1591
|
else if (exportBackend === 'copilot') {
|
|
@@ -1419,14 +1625,17 @@ function handleServerMessage(msg) {
|
|
|
1419
1625
|
});
|
|
1420
1626
|
break;
|
|
1421
1627
|
}
|
|
1422
|
-
sendSessionExportResult(requestId, m.sessionId, result
|
|
1628
|
+
sendSessionExportResult(requestId, m.sessionId, result, {
|
|
1629
|
+
providerId: m.providerId || (exportBackend === 'pi' ? 'pi' : undefined),
|
|
1630
|
+
workDir: readResult.workDir,
|
|
1631
|
+
});
|
|
1423
1632
|
break;
|
|
1424
1633
|
}
|
|
1425
1634
|
case 'query_active_conversations': {
|
|
1426
1635
|
const request = msg;
|
|
1427
1636
|
const active = [];
|
|
1428
1637
|
const pushActive = (convId, conv) => {
|
|
1429
|
-
const sessionId = conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? null;
|
|
1638
|
+
const sessionId = conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.piSessionId ?? null;
|
|
1430
1639
|
if (conv.turnActive) {
|
|
1431
1640
|
active.push({
|
|
1432
1641
|
conversationId: convId,
|
|
@@ -1459,6 +1668,9 @@ function handleServerMessage(msg) {
|
|
|
1459
1668
|
for (const [convId, conv] of getCodexConversations()) {
|
|
1460
1669
|
pushActive(convId, conv);
|
|
1461
1670
|
}
|
|
1671
|
+
for (const [convId, conv] of getPiConversations()) {
|
|
1672
|
+
pushActive(convId, conv);
|
|
1673
|
+
}
|
|
1462
1674
|
const activeTeamState = getActiveTeam();
|
|
1463
1675
|
// Collect running loop executions for status recovery
|
|
1464
1676
|
const runningLoops = [];
|
|
@@ -1485,7 +1697,7 @@ function handleServerMessage(msg) {
|
|
|
1485
1697
|
return {
|
|
1486
1698
|
type: 'turn_status',
|
|
1487
1699
|
conversationId: convId,
|
|
1488
|
-
claudeSessionId: conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? null,
|
|
1700
|
+
claudeSessionId: conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.piSessionId ?? null,
|
|
1489
1701
|
turnId: conv.turnId,
|
|
1490
1702
|
status,
|
|
1491
1703
|
isProcessing: isProc,
|
|
@@ -1520,6 +1732,15 @@ function handleServerMessage(msg) {
|
|
|
1520
1732
|
}
|
|
1521
1733
|
}
|
|
1522
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
|
+
}
|
|
1523
1744
|
if (!found) {
|
|
1524
1745
|
send({ type: 'turn_status', turnId: m.turnId, status: 'unknown', isProcessing: false, isCompacting: false, stateVersion: 0 });
|
|
1525
1746
|
}
|
|
@@ -1540,7 +1761,13 @@ function handleServerMessage(msg) {
|
|
|
1540
1761
|
send(buildTurnStatus(m.conversationId, codexConv));
|
|
1541
1762
|
}
|
|
1542
1763
|
else {
|
|
1543
|
-
|
|
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
|
+
}
|
|
1544
1771
|
}
|
|
1545
1772
|
}
|
|
1546
1773
|
}
|
|
@@ -1740,6 +1967,21 @@ function handleServerMessage(msg) {
|
|
|
1740
1967
|
setBackendType(newBackendType);
|
|
1741
1968
|
console.log(`[AgentLink] Backend switched: ${oldBackendType} → ${newBackendType}`);
|
|
1742
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
|
+
}
|
|
1743
1985
|
else if (oldBackendType === 'codex') {
|
|
1744
1986
|
const existingCodexConv = conversationId ? getCodexConversation(conversationId) : undefined;
|
|
1745
1987
|
if (existingCodexConv?.turnActive) {
|
|
@@ -2428,7 +2670,53 @@ function handleListCodexSessionsFn(dir, options = {}) {
|
|
|
2428
2670
|
});
|
|
2429
2671
|
}
|
|
2430
2672
|
}
|
|
2431
|
-
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) {
|
|
2432
2720
|
const totalChunks = Math.max(1, Math.ceil(result.content.length / SESSION_EXPORT_CHUNK_SIZE));
|
|
2433
2721
|
console.log(`[AgentLink] → session_exported (${result.format}, ${Buffer.byteLength(result.content, 'utf8')} bytes, ${totalChunks} chunks)`);
|
|
2434
2722
|
if (totalChunks === 1) {
|
|
@@ -2437,6 +2725,8 @@ function sendSessionExportResult(requestId, sessionId, result) {
|
|
|
2437
2725
|
requestId,
|
|
2438
2726
|
sessionId,
|
|
2439
2727
|
...result,
|
|
2728
|
+
...(scope?.providerId ? { providerId: scope.providerId } : {}),
|
|
2729
|
+
...(scope?.workDir ? { workDir: scope.workDir } : {}),
|
|
2440
2730
|
});
|
|
2441
2731
|
return;
|
|
2442
2732
|
}
|
|
@@ -2452,6 +2742,8 @@ function sendSessionExportResult(requestId, sessionId, result) {
|
|
|
2452
2742
|
chunkIndex,
|
|
2453
2743
|
totalChunks,
|
|
2454
2744
|
content: result.content.slice(start, start + SESSION_EXPORT_CHUNK_SIZE),
|
|
2745
|
+
...(scope?.providerId ? { providerId: scope.providerId } : {}),
|
|
2746
|
+
...(scope?.workDir ? { workDir: scope.workDir } : {}),
|
|
2455
2747
|
});
|
|
2456
2748
|
}
|
|
2457
2749
|
}
|