@agent-link/agent 0.1.328 → 0.1.329

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.
@@ -29,12 +29,13 @@ 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, 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';
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, isYeaftBackend, 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
33
  import { addPiSendFn, cancelPiExecution, cleanupAllPiConversations, createPiPlaceholder, deletePiSession, evictByPiSessionId, getPiConversation, getPiConversations, getPiSessionWorkDir, handlePiChat, listAllPiSessionsPage, listPiSessionsPage, movePiSession, piCapabilities, readPiSessionMessages, rebindPiConversation, renamePiSession, restartPiConversation, } from './pi.js';
34
+ import { addYeaftSendFn, cancelYeaftExecution, cleanupAllYeaftConversations, createYeaftPlaceholder, deleteYeaftSession, evictByYeaftSessionId, getYeaftConversation, getYeaftConversations, handleYeaftChat, handleYeaftUserAnswer, listAllYeaftSessionsPage, listYeaftSessionsPage, moveYeaftSession, readYeaftSessionMessages, rebindYeaftConversation, renameYeaftSession, restartYeaftConversation, } from './yeaft.js';
34
35
  import { DEFAULT_SESSION_LIST_LIMIT, LEGACY_SESSION_LIST_CAP, listSessions, listSessionsPageAsync, readSessionMessages, deleteSession, renameSession, moveSession, listAllRecentSessionsPage, getSessionCacheStats } from './history.js';
35
36
  import { discoverProviderCommands, providerCommandDiscoverers, } from './provider-command-discovery.js';
36
37
  import { providerCommandsMessage } from './provider-command-types.js';
37
- import { claudeCapabilities } from './backends/types.js';
38
+ import { claudeCapabilities, yeaftCapabilities } from './backends/types.js';
38
39
  import { searchSessions } from './search-sessions.js';
39
40
  import { formatSessionExport, isSessionExportFormat } from './session-export.js';
40
41
  import { listMemoryFiles, updateMemoryFile, deleteMemoryFile } from './memory.js';
@@ -73,6 +74,7 @@ let unsubscribeSend = null;
73
74
  let unsubscribeCopilotSend = null;
74
75
  let unsubscribeCodexSend = null;
75
76
  let unsubscribePiSend = null;
77
+ let unsubscribeYeaftSend = null;
76
78
  let unsubscribeSessionStarted = null;
77
79
  let heartbeatInterval = null;
78
80
  let heartbeatTimeout = null;
@@ -91,6 +93,8 @@ function capabilitiesForBackend(backend, providerId) {
91
93
  return codexCapabilities();
92
94
  case 'pi':
93
95
  return piCapabilities();
96
+ case 'yeaft':
97
+ return yeaftCapabilities();
94
98
  case 'claude':
95
99
  default:
96
100
  return claudeCapabilities();
@@ -136,6 +140,16 @@ function hasActiveSessionConversation(backend, sessionId, providerId, workDir) {
136
140
  }
137
141
  }
138
142
  }
143
+ else if (backend === 'yeaft') {
144
+ for (const conv of getYeaftConversations().values()) {
145
+ if ((conv.yeaftSessionId === sessionId || conv.lastYeaftSessionId === sessionId)
146
+ && (!providerId || conv.providerId === providerId)
147
+ && (!workDir || normalizeDirForCompare(conv.workDir) === normalizeDirForCompare(workDir))
148
+ && conv.turnActive) {
149
+ return true;
150
+ }
151
+ }
152
+ }
139
153
  else {
140
154
  for (const conv of getConversations().values()) {
141
155
  if (conv.claudeSessionId === sessionId || conv.lastClaudeSessionId === sessionId) {
@@ -167,6 +181,14 @@ function syncMovedConversationWorkDir(backend, sessionId, toWorkDir, providerId,
167
181
  conv.workDir = toWorkDir;
168
182
  }
169
183
  }
184
+ else if (backend === 'yeaft') {
185
+ for (const conv of getYeaftConversations().values()) {
186
+ if ((conv.yeaftSessionId === sessionId || conv.lastYeaftSessionId === sessionId)
187
+ && (!providerId || conv.providerId === providerId)
188
+ && (!fromWorkDir || normalizeDirForCompare(conv.workDir) === normalizeDirForCompare(fromWorkDir)))
189
+ conv.workDir = toWorkDir;
190
+ }
191
+ }
170
192
  else {
171
193
  for (const conv of getConversations().values()) {
172
194
  if (conv.claudeSessionId === sessionId || conv.lastClaudeSessionId === sessionId)
@@ -236,6 +258,13 @@ export function initializeLocalConnection(config) {
236
258
  catch { /* ignore */ }
237
259
  }
238
260
  unsubscribePiSend = addPiSendFn(send);
261
+ if (unsubscribeYeaftSend) {
262
+ try {
263
+ unsubscribeYeaftSend();
264
+ }
265
+ catch { /* ignore */ }
266
+ }
267
+ unsubscribeYeaftSend = addYeaftSendFn(send);
239
268
  // Wire up action item session linkage
240
269
  if (unsubscribeSessionStarted) {
241
270
  try {
@@ -534,6 +563,7 @@ export function disconnect() {
534
563
  cleanupAllCopilotConversations();
535
564
  cleanupAllCodexConversations();
536
565
  cleanupAllPiConversations();
566
+ cleanupAllYeaftConversations();
537
567
  if (unsubscribeSend) {
538
568
  try {
539
569
  unsubscribeSend();
@@ -562,6 +592,13 @@ export function disconnect() {
562
592
  catch { /* ignore */ }
563
593
  unsubscribePiSend = null;
564
594
  }
595
+ if (unsubscribeYeaftSend) {
596
+ try {
597
+ unsubscribeYeaftSend();
598
+ }
599
+ catch { /* ignore */ }
600
+ unsubscribeYeaftSend = null;
601
+ }
565
602
  if (unsubscribeSessionStarted) {
566
603
  try {
567
604
  unsubscribeSessionStarted();
@@ -675,6 +712,7 @@ export function getConnectionStatus() {
675
712
  conversations: getConversations().size
676
713
  + getCopilotConversations().size
677
714
  + getCodexConversations().size
715
+ + getYeaftConversations().size
678
716
  + getPiConversations().size,
679
717
  };
680
718
  }
@@ -904,7 +942,15 @@ function handleServerMessage(msg) {
904
942
  }
905
943
  }
906
944
  // Route to the selected backend.
907
- if (isPiBackend()) {
945
+ if (isYeaftBackend()) {
946
+ const yeaftMsg = msg;
947
+ handleYeaftChat(chatConvId || '', yeaftMsg.prompt, chatDir, {
948
+ resumeSessionId: chatOptions.resumeSessionId,
949
+ providerId: effectiveProviderId,
950
+ yeaftOptions: yeaftMsg.yeaftOptions || yeaftMsg.copilotOptions,
951
+ }, yeaftMsg.files);
952
+ }
953
+ else if (isPiBackend()) {
908
954
  const piMsg = msg;
909
955
  const piState = chatConvId ? getPiConversation(chatConvId) : undefined;
910
956
  const piResumeDir = chatOptions.resumeSessionId
@@ -974,7 +1020,10 @@ function handleServerMessage(msg) {
974
1020
  const conversationId = msg.conversationId || '';
975
1021
  // Cancellation follows conversation ownership, not the currently selected
976
1022
  // provider. This keeps background turns cancellable after a tab switches.
977
- if (getPiConversation(conversationId)) {
1023
+ if (getYeaftConversation(conversationId)) {
1024
+ cancelYeaftExecution(conversationId);
1025
+ }
1026
+ else if (getPiConversation(conversationId)) {
978
1027
  cancelPiExecution(conversationId);
979
1028
  }
980
1029
  else if (getCodexConversation(conversationId)) {
@@ -986,6 +1035,9 @@ function handleServerMessage(msg) {
986
1035
  else if (getConversation(conversationId)) {
987
1036
  claudeCancelExecution(conversationId);
988
1037
  }
1038
+ else if (isYeaftBackend()) {
1039
+ cancelYeaftExecution(conversationId);
1040
+ }
989
1041
  else if (isPiBackend()) {
990
1042
  cancelPiExecution(conversationId);
991
1043
  }
@@ -1007,7 +1059,17 @@ function handleServerMessage(msg) {
1007
1059
  const sessionBackend = m.providerId
1008
1060
  ? getBackendTypeForProvider(m.providerId)
1009
1061
  : getBackendType();
1010
- if (sessionBackend === 'pi') {
1062
+ if (sessionBackend === 'yeaft') {
1063
+ handleListYeaftSessionsFn(dir, {
1064
+ limit: m.limit,
1065
+ cursor: m.cursor,
1066
+ mode: m.mode,
1067
+ requestId: m.requestId,
1068
+ providerId: m.providerId,
1069
+ legacy: m.limit == null && !m.cursor && !m.mode && !m.requestId,
1070
+ });
1071
+ }
1072
+ else if (sessionBackend === 'pi') {
1011
1073
  handleListPiSessionsFn(dir, {
1012
1074
  limit: m.limit,
1013
1075
  cursor: m.cursor,
@@ -1057,7 +1119,21 @@ function handleServerMessage(msg) {
1057
1119
  const recentBackend = m.providerId
1058
1120
  ? getBackendTypeForProvider(m.providerId)
1059
1121
  : getBackendType();
1060
- if (recentBackend === 'pi') {
1122
+ if (recentBackend === 'yeaft') {
1123
+ const page = listAllYeaftSessionsPage({ limit: m.limit, cursor: m.cursor });
1124
+ console.log(`[AgentLink] → recent_sessions_list (${page.sessions.length} Yeaft sessions) hasMore=${page.hasMore}`);
1125
+ send({
1126
+ type: 'recent_sessions_list',
1127
+ sessions: page.sessions,
1128
+ hasMore: page.hasMore,
1129
+ nextCursor: page.nextCursor,
1130
+ indexState: page.indexState,
1131
+ ...(m.mode ? { mode: m.mode } : {}),
1132
+ ...(m.requestId ? { requestId: m.requestId } : {}),
1133
+ providerId: m.providerId || 'yeaft',
1134
+ });
1135
+ }
1136
+ else if (recentBackend === 'pi') {
1061
1137
  const page = listAllPiSessionsPage({
1062
1138
  limit: m.limit,
1063
1139
  cursor: m.cursor,
@@ -1222,6 +1298,39 @@ function handleServerMessage(msg) {
1222
1298
  const resumeBackend = m.providerId
1223
1299
  ? getBackendTypeForProvider(m.providerId)
1224
1300
  : getBackendType();
1301
+ if (resumeBackend === 'yeaft') {
1302
+ if (convId) {
1303
+ rebindYeaftConversation(m.claudeSessionId, convId);
1304
+ if (!getYeaftConversation(convId)) {
1305
+ createYeaftPlaceholder(convId, { providerId: m.providerId || 'yeaft' });
1306
+ }
1307
+ }
1308
+ const yeaftState = getYeaftConversation(convId || '');
1309
+ const resolvedWorkDir = explicitWorkDir || yeaftState?.workDir || state.workDir;
1310
+ if (yeaftState) {
1311
+ yeaftState.yeaftSessionId = m.claudeSessionId;
1312
+ yeaftState.lastYeaftSessionId = m.claudeSessionId;
1313
+ yeaftState.workDir = resolvedWorkDir;
1314
+ }
1315
+ const history = readYeaftSessionMessages(resolvedWorkDir, m.claudeSessionId);
1316
+ const isYeaftProcessing = !!(yeaftState?.turnActive && yeaftState.child);
1317
+ console.log(`[AgentLink] → conversation_resumed (${history.length} Yeaft messages, session ${m.claudeSessionId.slice(0, 8)})`);
1318
+ send({
1319
+ type: 'conversation_resumed',
1320
+ conversationId: convId,
1321
+ claudeSessionId: m.claudeSessionId,
1322
+ history,
1323
+ isCompacting: false,
1324
+ isProcessing: isYeaftProcessing,
1325
+ planMode: false,
1326
+ permissionMode: 'normal',
1327
+ workDir: resolvedWorkDir,
1328
+ providerId: m.providerId || 'yeaft',
1329
+ ...(typeof m.scrollToMessageIdx === 'number' ? { scrollToMessageIdx: m.scrollToMessageIdx } : {}),
1330
+ ...(m.searchQuery ? { searchQuery: m.searchQuery } : {}),
1331
+ });
1332
+ break;
1333
+ }
1225
1334
  if (resumeBackend === 'pi') {
1226
1335
  const persistedWorkDir = getPiSessionWorkDir(m.claudeSessionId, m.providerId || 'pi', explicitWorkDir);
1227
1336
  if (convId) {
@@ -1435,7 +1544,10 @@ function handleServerMessage(msg) {
1435
1544
  }
1436
1545
  case 'ask_user_answer': {
1437
1546
  const m = msg;
1438
- handleUserAnswer(m.requestId, m.answers);
1547
+ if (isYeaftBackend())
1548
+ handleYeaftUserAnswer(m.requestId, m.answers, { conversationId: m.conversationId });
1549
+ else
1550
+ handleUserAnswer(m.requestId, m.answers);
1439
1551
  break;
1440
1552
  }
1441
1553
  case 'delete_session': {
@@ -1446,7 +1558,27 @@ function handleServerMessage(msg) {
1446
1558
  const dir = m.workDir?.trim()
1447
1559
  ? path.resolve(m.workDir)
1448
1560
  : (m.providerId === 'brain' ? BRAIN_DATA_DIR : state.workDir);
1449
- if (sessionBackend === 'pi') {
1561
+ if (sessionBackend === 'yeaft') {
1562
+ if (evictByYeaftSessionId(m.sessionId)) {
1563
+ send({ type: 'error', message: 'Cannot delete a session while it is processing.' });
1564
+ break;
1565
+ }
1566
+ const ok = deleteYeaftSession(dir, m.sessionId);
1567
+ console.log(`[AgentLink] delete Yeaft session ${m.sessionId}: ${ok}`);
1568
+ if (ok) {
1569
+ send({
1570
+ type: 'session_deleted',
1571
+ sessionId: m.sessionId,
1572
+ providerId: m.providerId || 'yeaft',
1573
+ workDir: dir,
1574
+ });
1575
+ }
1576
+ else {
1577
+ send({ type: 'error', message: 'Session not found or could not be deleted.' });
1578
+ }
1579
+ handleListYeaftSessionsFn(dir, { providerId: m.providerId || 'yeaft' });
1580
+ }
1581
+ else if (sessionBackend === 'pi') {
1450
1582
  if (evictByPiSessionId(m.sessionId, m.providerId || 'pi', dir)) {
1451
1583
  send({ type: 'error', message: 'Cannot delete a session while it is processing.' });
1452
1584
  break;
@@ -1497,7 +1629,28 @@ function handleServerMessage(msg) {
1497
1629
  const dir = m.workDir?.trim()
1498
1630
  ? path.resolve(m.workDir)
1499
1631
  : (m.providerId === 'brain' ? BRAIN_DATA_DIR : state.workDir);
1500
- if (sessionBackend === 'pi') {
1632
+ if (sessionBackend === 'yeaft') {
1633
+ if (hasActiveSessionConversation('yeaft', m.sessionId, m.providerId || 'yeaft', dir)) {
1634
+ send({ type: 'error', message: 'Cannot rename a session while it is processing.' });
1635
+ break;
1636
+ }
1637
+ const ok = renameYeaftSession(dir, m.sessionId, m.newTitle);
1638
+ console.log(`[AgentLink] rename Yeaft session ${m.sessionId}: ${ok}`);
1639
+ if (ok) {
1640
+ send({
1641
+ type: 'session_renamed',
1642
+ sessionId: m.sessionId,
1643
+ newTitle: m.newTitle,
1644
+ providerId: m.providerId || 'yeaft',
1645
+ workDir: dir,
1646
+ });
1647
+ }
1648
+ else {
1649
+ send({ type: 'error', message: 'Session not found or could not be renamed.' });
1650
+ }
1651
+ handleListYeaftSessionsFn(dir, { providerId: m.providerId || 'yeaft' });
1652
+ }
1653
+ else if (sessionBackend === 'pi') {
1501
1654
  if (hasActiveSessionConversation('pi', m.sessionId, m.providerId || 'pi', dir)) {
1502
1655
  send({ type: 'error', message: 'Cannot rename a session while it is processing.' });
1503
1656
  break;
@@ -1561,7 +1714,10 @@ function handleServerMessage(msg) {
1561
1714
  break;
1562
1715
  }
1563
1716
  let ok = false;
1564
- if (sessionBackend === 'pi') {
1717
+ if (sessionBackend === 'yeaft') {
1718
+ ok = moveYeaftSession(fromDir, toDir, m.sessionId);
1719
+ }
1720
+ else if (sessionBackend === 'pi') {
1565
1721
  ok = movePiSession(fromDir, toDir, m.sessionId, m.providerId || 'pi');
1566
1722
  }
1567
1723
  else if (sessionBackend === 'codex') {
@@ -1584,11 +1740,15 @@ function handleServerMessage(msg) {
1584
1740
  sessionId: m.sessionId,
1585
1741
  fromWorkDir: fromDir,
1586
1742
  toWorkDir: toDir,
1587
- ...(sessionBackend === 'pi'
1588
- ? { providerId: m.providerId || 'pi', workDir: toDir }
1743
+ ...(sessionBackend === 'pi' || sessionBackend === 'yeaft'
1744
+ ? { providerId: m.providerId || sessionBackend, workDir: toDir }
1589
1745
  : (m.providerId ? { providerId: m.providerId } : {})),
1590
1746
  });
1591
- if (sessionBackend === 'pi') {
1747
+ if (sessionBackend === 'yeaft') {
1748
+ handleListYeaftSessionsFn(fromDir, { providerId: m.providerId || 'yeaft' });
1749
+ handleListYeaftSessionsFn(toDir, { providerId: m.providerId || 'yeaft' });
1750
+ }
1751
+ else if (sessionBackend === 'pi') {
1592
1752
  handleListPiSessionsFn(fromDir, { providerId: m.providerId || 'pi' });
1593
1753
  handleListPiSessionsFn(toDir, { providerId: m.providerId || 'pi' });
1594
1754
  }
@@ -1622,7 +1782,13 @@ function handleServerMessage(msg) {
1622
1782
  break;
1623
1783
  }
1624
1784
  let readResult;
1625
- if (exportBackend === 'pi') {
1785
+ if (exportBackend === 'yeaft') {
1786
+ readResult = {
1787
+ history: readYeaftSessionMessages(dir, m.sessionId),
1788
+ workDir: dir,
1789
+ };
1790
+ }
1791
+ else if (exportBackend === 'pi') {
1626
1792
  readResult = {
1627
1793
  history: readPiSessionMessages(dir, m.sessionId, m.providerId || 'pi'),
1628
1794
  workDir: dir,
@@ -1669,7 +1835,7 @@ function handleServerMessage(msg) {
1669
1835
  break;
1670
1836
  }
1671
1837
  sendSessionExportResult(requestId, m.sessionId, result, {
1672
- providerId: m.providerId || (exportBackend === 'pi' ? 'pi' : undefined),
1838
+ providerId: m.providerId || ((exportBackend === 'pi' || exportBackend === 'yeaft') ? exportBackend : undefined),
1673
1839
  workDir: readResult.workDir,
1674
1840
  });
1675
1841
  break;
@@ -1678,7 +1844,7 @@ function handleServerMessage(msg) {
1678
1844
  const request = msg;
1679
1845
  const active = [];
1680
1846
  const pushActive = (convId, conv) => {
1681
- const sessionId = conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.piSessionId ?? null;
1847
+ const sessionId = conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.yeaftSessionId ?? conv.piSessionId ?? null;
1682
1848
  if (conv.turnActive) {
1683
1849
  active.push({
1684
1850
  conversationId: convId,
@@ -1711,6 +1877,9 @@ function handleServerMessage(msg) {
1711
1877
  for (const [convId, conv] of getCodexConversations()) {
1712
1878
  pushActive(convId, conv);
1713
1879
  }
1880
+ for (const [convId, conv] of getYeaftConversations()) {
1881
+ pushActive(convId, conv);
1882
+ }
1714
1883
  for (const [convId, conv] of getPiConversations()) {
1715
1884
  pushActive(convId, conv);
1716
1885
  }
@@ -1740,7 +1909,7 @@ function handleServerMessage(msg) {
1740
1909
  return {
1741
1910
  type: 'turn_status',
1742
1911
  conversationId: convId,
1743
- claudeSessionId: conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.piSessionId ?? null,
1912
+ claudeSessionId: conv.claudeSessionId ?? conv.copilotSessionId ?? conv.codexSessionId ?? conv.yeaftSessionId ?? conv.piSessionId ?? null,
1744
1913
  turnId: conv.turnId,
1745
1914
  status,
1746
1915
  isProcessing: isProc,
@@ -1775,6 +1944,15 @@ function handleServerMessage(msg) {
1775
1944
  }
1776
1945
  }
1777
1946
  }
1947
+ if (!found) {
1948
+ for (const [convId, conv] of getYeaftConversations()) {
1949
+ if (conv.turnId === m.turnId) {
1950
+ send(buildTurnStatus(convId, conv));
1951
+ found = true;
1952
+ break;
1953
+ }
1954
+ }
1955
+ }
1778
1956
  if (!found) {
1779
1957
  for (const [convId, conv] of getPiConversations()) {
1780
1958
  if (conv.turnId === m.turnId) {
@@ -1804,12 +1982,18 @@ function handleServerMessage(msg) {
1804
1982
  send(buildTurnStatus(m.conversationId, codexConv));
1805
1983
  }
1806
1984
  else {
1807
- const piConv = getPiConversation(m.conversationId);
1808
- if (piConv) {
1809
- send(buildTurnStatus(m.conversationId, piConv));
1985
+ const yeaftConv = getYeaftConversation(m.conversationId);
1986
+ if (yeaftConv) {
1987
+ send(buildTurnStatus(m.conversationId, yeaftConv));
1810
1988
  }
1811
1989
  else {
1812
- send({ type: 'turn_status', conversationId: m.conversationId, status: 'unknown', isProcessing: false, isCompacting: false, stateVersion: 0 });
1990
+ const piConv = getPiConversation(m.conversationId);
1991
+ if (piConv) {
1992
+ send(buildTurnStatus(m.conversationId, piConv));
1993
+ }
1994
+ else {
1995
+ send({ type: 'turn_status', conversationId: m.conversationId, status: 'unknown', isProcessing: false, isCompacting: false, stateVersion: 0 });
1996
+ }
1813
1997
  }
1814
1998
  }
1815
1999
  }
@@ -2010,6 +2194,20 @@ function handleServerMessage(msg) {
2010
2194
  setBackendType(newBackendType);
2011
2195
  console.log(`[AgentLink] Backend switched: ${oldBackendType} → ${newBackendType}`);
2012
2196
  }
2197
+ else if (oldBackendType === 'yeaft') {
2198
+ const existingYeaftConv = conversationId ? getYeaftConversation(conversationId) : undefined;
2199
+ if (existingYeaftConv?.turnActive) {
2200
+ existingYeaftConv.providerId = providerId;
2201
+ }
2202
+ else if (conversationId && existingYeaftConv) {
2203
+ const result = restartYeaftConversation(conversationId, { providerId });
2204
+ if (result.wasTurnActive)
2205
+ send({ type: 'execution_cancelled', conversationId });
2206
+ }
2207
+ else if (conversationId) {
2208
+ createYeaftPlaceholder(conversationId, { providerId });
2209
+ }
2210
+ }
2013
2211
  else if (oldBackendType === 'pi') {
2014
2212
  const existingPiConversation = conversationId ? getPiConversation(conversationId) : undefined;
2015
2213
  if (existingPiConversation?.turnActive) {
@@ -2717,6 +2915,45 @@ function handleListCodexSessionsFn(dir, options = {}) {
2717
2915
  });
2718
2916
  }
2719
2917
  }
2918
+ function handleListYeaftSessionsFn(dir, options = {}) {
2919
+ try {
2920
+ const page = options.legacy
2921
+ ? { ...listYeaftSessionsPage(dir, { limit: Number.POSITIVE_INFINITY }), hasMore: false }
2922
+ : listYeaftSessionsPage(dir, {
2923
+ limit: options.limit ?? DEFAULT_SESSION_LIST_LIMIT,
2924
+ cursor: options.cursor,
2925
+ });
2926
+ if (options.legacy && page.sessions.length > LEGACY_SESSION_LIST_CAP) {
2927
+ page.sessions = page.sessions.slice(0, LEGACY_SESSION_LIST_CAP);
2928
+ }
2929
+ console.log(`[AgentLink] → sessions_list (${page.sessions.length} Yeaft sessions for ${dir}) hasMore=${page.hasMore}`);
2930
+ send({
2931
+ type: 'sessions_list',
2932
+ sessions: page.sessions,
2933
+ workDir: dir,
2934
+ hasMore: page.hasMore,
2935
+ nextCursor: page.nextCursor,
2936
+ indexState: 'ready',
2937
+ ...(options.mode ? { mode: options.mode } : {}),
2938
+ ...(options.requestId ? { requestId: options.requestId } : {}),
2939
+ providerId: options.providerId || 'yeaft',
2940
+ });
2941
+ }
2942
+ catch (err) {
2943
+ console.error('[AgentLink] listYeaftSessions failed:', err);
2944
+ send({
2945
+ type: 'sessions_list',
2946
+ sessions: [],
2947
+ workDir: dir,
2948
+ hasMore: false,
2949
+ indexState: 'ready',
2950
+ error: err.message,
2951
+ ...(options.mode ? { mode: options.mode } : {}),
2952
+ ...(options.requestId ? { requestId: options.requestId } : {}),
2953
+ providerId: options.providerId || 'yeaft',
2954
+ });
2955
+ }
2956
+ }
2720
2957
  function handleListPiSessionsFn(dir, options = {}) {
2721
2958
  try {
2722
2959
  const page = options.legacy