@yeaft/webchat-agent 0.0.58 → 0.0.60
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/connection.js +6 -1
- package/crew.js +74 -9
- package/package.json +1 -1
package/connection.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
createCrewSession, handleCrewHumanInput, handleCrewControl,
|
|
25
25
|
addRoleToSession, removeRoleFromSession,
|
|
26
|
-
handleListCrewSessions, resumeCrewSession
|
|
26
|
+
handleListCrewSessions, resumeCrewSession, removeFromCrewIndex
|
|
27
27
|
} from './crew.js';
|
|
28
28
|
|
|
29
29
|
// 需要在断连期间缓冲的消息类型(Claude 输出相关的关键消息)
|
|
@@ -295,6 +295,11 @@ async function handleMessage(msg) {
|
|
|
295
295
|
await resumeCrewSession(msg);
|
|
296
296
|
break;
|
|
297
297
|
|
|
298
|
+
case 'delete_crew_session':
|
|
299
|
+
await removeFromCrewIndex(msg.sessionId);
|
|
300
|
+
(await import('./conversation.js')).sendConversationList();
|
|
301
|
+
break;
|
|
302
|
+
|
|
298
303
|
// Port proxy
|
|
299
304
|
case 'proxy_request':
|
|
300
305
|
handleProxyHttpRequest(msg);
|
package/crew.js
CHANGED
|
@@ -50,6 +50,9 @@ function sessionToIndexEntry(session) {
|
|
|
50
50
|
projectDir: session.projectDir,
|
|
51
51
|
sharedDir: session.sharedDir,
|
|
52
52
|
status: session.status,
|
|
53
|
+
goal: session.goal,
|
|
54
|
+
userId: session.userId,
|
|
55
|
+
username: session.username,
|
|
53
56
|
createdAt: session.createdAt,
|
|
54
57
|
updatedAt: Date.now()
|
|
55
58
|
};
|
|
@@ -63,6 +66,15 @@ async function upsertCrewIndex(session) {
|
|
|
63
66
|
await saveCrewIndex(index);
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
export async function removeFromCrewIndex(sessionId) {
|
|
70
|
+
const index = await loadCrewIndex();
|
|
71
|
+
const filtered = index.filter(e => e.sessionId !== sessionId);
|
|
72
|
+
if (filtered.length !== index.length) {
|
|
73
|
+
await saveCrewIndex(filtered);
|
|
74
|
+
console.log(`[Crew] Removed session ${sessionId} from index`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
// =====================================================================
|
|
67
79
|
// Session Metadata (.crew/session.json)
|
|
68
80
|
// =====================================================================
|
|
@@ -167,6 +179,7 @@ export async function resumeCrewSession(msg) {
|
|
|
167
179
|
messageHistory: [],
|
|
168
180
|
humanMessageQueue: [],
|
|
169
181
|
waitingHumanContext: null,
|
|
182
|
+
pendingRoute: null,
|
|
170
183
|
userId: userId || meta.userId,
|
|
171
184
|
username: username || meta.username,
|
|
172
185
|
createdAt: meta.createdAt || Date.now()
|
|
@@ -244,6 +257,7 @@ export async function createCrewSession(msg) {
|
|
|
244
257
|
messageHistory: [], // 群聊消息历史
|
|
245
258
|
humanMessageQueue: [], // 人的消息排队
|
|
246
259
|
waitingHumanContext: null, // { fromRole, reason, message }
|
|
260
|
+
pendingRoute: null, // { fromRole, route } — 暂停时未完成的路由
|
|
247
261
|
userId,
|
|
248
262
|
username,
|
|
249
263
|
createdAt: Date.now()
|
|
@@ -684,8 +698,8 @@ ${allRoles.map(r => `- ${r.icon} ${r.name}: ${r.displayName} - ${r.description}`
|
|
|
684
698
|
async function processRoleOutput(session, roleName, roleQuery, roleState) {
|
|
685
699
|
try {
|
|
686
700
|
for await (const message of roleQuery) {
|
|
687
|
-
// 检查 session
|
|
688
|
-
if (session.status === 'stopped') break;
|
|
701
|
+
// 检查 session 是否已停止或暂停
|
|
702
|
+
if (session.status === 'stopped' || session.status === 'paused') break;
|
|
689
703
|
|
|
690
704
|
if (message.type === 'system' && message.subtype === 'init') {
|
|
691
705
|
roleState.claudeSessionId = message.session_id;
|
|
@@ -707,14 +721,16 @@ async function processRoleOutput(session, roleName, roleQuery, roleState) {
|
|
|
707
721
|
if (block.type === 'text') {
|
|
708
722
|
roleState.accumulatedText += block.text;
|
|
709
723
|
} else if (block.type === 'tool_use') {
|
|
710
|
-
// 转发 tool use
|
|
724
|
+
// 转发 tool use + 记录当前工具
|
|
725
|
+
roleState.currentTool = block.name;
|
|
711
726
|
sendCrewOutput(session, roleName, 'tool_use', message);
|
|
712
727
|
}
|
|
713
728
|
}
|
|
714
729
|
}
|
|
715
730
|
}
|
|
716
731
|
} else if (message.type === 'user') {
|
|
717
|
-
// tool results
|
|
732
|
+
// tool results — clear currentTool
|
|
733
|
+
roleState.currentTool = null;
|
|
718
734
|
sendCrewOutput(session, roleName, 'tool_result', message);
|
|
719
735
|
} else if (message.type === 'result') {
|
|
720
736
|
// ★ Turn 完成!
|
|
@@ -759,6 +775,15 @@ async function processRoleOutput(session, roleName, roleQuery, roleState) {
|
|
|
759
775
|
} catch (error) {
|
|
760
776
|
if (error.name === 'AbortError') {
|
|
761
777
|
console.log(`[Crew] ${roleName} aborted`);
|
|
778
|
+
// 暂停时:检查已累积的文本中是否有 route,保存为 pendingRoute
|
|
779
|
+
if (session.status === 'paused' && roleState.accumulatedText) {
|
|
780
|
+
const route = parseRoute(roleState.accumulatedText);
|
|
781
|
+
if (route && !session.pendingRoute) {
|
|
782
|
+
session.pendingRoute = { fromRole: roleName, route };
|
|
783
|
+
console.log(`[Crew] Saved pending route from aborted ${roleName}: -> ${route.to}`);
|
|
784
|
+
}
|
|
785
|
+
roleState.accumulatedText = '';
|
|
786
|
+
}
|
|
762
787
|
} else {
|
|
763
788
|
console.error(`[Crew] ${roleName} error:`, error.message);
|
|
764
789
|
// 通知决策者
|
|
@@ -839,6 +864,13 @@ async function executeRoute(session, fromRole, route) {
|
|
|
839
864
|
return;
|
|
840
865
|
}
|
|
841
866
|
|
|
867
|
+
// 如果 session 已暂停或停止,保存 pendingRoute 等恢复时重放
|
|
868
|
+
if (session.status === 'paused' || session.status === 'stopped') {
|
|
869
|
+
session.pendingRoute = { fromRole, route };
|
|
870
|
+
console.log(`[Crew] Session ${session.status}, route saved as pending: ${fromRole} -> ${to}`);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
|
|
842
874
|
// 发送路由消息(UI 显示 → @xxx)
|
|
843
875
|
sendCrewOutput(session, fromRole, 'route', null, { routeTo: to, routeSummary: summary });
|
|
844
876
|
|
|
@@ -1033,7 +1065,7 @@ export async function handleCrewControl(msg) {
|
|
|
1033
1065
|
|
|
1034
1066
|
switch (action) {
|
|
1035
1067
|
case 'pause':
|
|
1036
|
-
pauseAll(session);
|
|
1068
|
+
await pauseAll(session);
|
|
1037
1069
|
break;
|
|
1038
1070
|
case 'resume':
|
|
1039
1071
|
await resumeSession(session);
|
|
@@ -1051,10 +1083,28 @@ export async function handleCrewControl(msg) {
|
|
|
1051
1083
|
|
|
1052
1084
|
/**
|
|
1053
1085
|
* 暂停所有角色
|
|
1086
|
+
* abort 运行中的 query 并保存 sessionId,恢复时 resume
|
|
1054
1087
|
*/
|
|
1055
|
-
function pauseAll(session) {
|
|
1088
|
+
async function pauseAll(session) {
|
|
1056
1089
|
session.status = 'paused';
|
|
1057
|
-
|
|
1090
|
+
|
|
1091
|
+
// abort 所有运行中的角色,保存 sessionId 以便 resume
|
|
1092
|
+
for (const [roleName, roleState] of session.roleStates) {
|
|
1093
|
+
if (roleState.claudeSessionId) {
|
|
1094
|
+
await saveRoleSessionId(session.sharedDir, roleName, roleState.claudeSessionId)
|
|
1095
|
+
.catch(e => console.warn(`[Crew] Failed to save sessionId for ${roleName}:`, e.message));
|
|
1096
|
+
}
|
|
1097
|
+
if (roleState.abortController) {
|
|
1098
|
+
roleState.abortController.abort();
|
|
1099
|
+
}
|
|
1100
|
+
// 记录哪些角色在暂停时正在工作
|
|
1101
|
+
roleState.wasActive = roleState.turnActive;
|
|
1102
|
+
roleState.turnActive = false;
|
|
1103
|
+
roleState.query = null;
|
|
1104
|
+
roleState.inputStream = null;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
console.log(`[Crew] Session ${session.id} paused, all active queries aborted`);
|
|
1058
1108
|
|
|
1059
1109
|
sendCrewOutput(session, 'system', 'system', {
|
|
1060
1110
|
type: 'assistant',
|
|
@@ -1065,6 +1115,7 @@ function pauseAll(session) {
|
|
|
1065
1115
|
|
|
1066
1116
|
/**
|
|
1067
1117
|
* 恢复 session
|
|
1118
|
+
* 重新执行被暂停时保存的 pendingRoute
|
|
1068
1119
|
*/
|
|
1069
1120
|
async function resumeSession(session) {
|
|
1070
1121
|
if (session.status !== 'paused') return;
|
|
@@ -1078,7 +1129,16 @@ async function resumeSession(session) {
|
|
|
1078
1129
|
});
|
|
1079
1130
|
sendStatusUpdate(session);
|
|
1080
1131
|
|
|
1081
|
-
//
|
|
1132
|
+
// 恢复被中断的路由
|
|
1133
|
+
if (session.pendingRoute) {
|
|
1134
|
+
const { fromRole, route } = session.pendingRoute;
|
|
1135
|
+
session.pendingRoute = null;
|
|
1136
|
+
console.log(`[Crew] Replaying pending route: ${fromRole} -> ${route.to}`);
|
|
1137
|
+
await executeRoute(session, fromRole, route);
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// 没有 pendingRoute,检查排队的人的消息
|
|
1082
1142
|
await processHumanQueue(session);
|
|
1083
1143
|
}
|
|
1084
1144
|
|
|
@@ -1195,7 +1255,12 @@ function sendStatusUpdate(session) {
|
|
|
1195
1255
|
})),
|
|
1196
1256
|
activeRoles: Array.from(session.roleStates.entries())
|
|
1197
1257
|
.filter(([, s]) => s.turnActive)
|
|
1198
|
-
.map(([name]) => name)
|
|
1258
|
+
.map(([name]) => name),
|
|
1259
|
+
currentToolByRole: Object.fromEntries(
|
|
1260
|
+
Array.from(session.roleStates.entries())
|
|
1261
|
+
.filter(([, s]) => s.turnActive && s.currentTool)
|
|
1262
|
+
.map(([name, s]) => [name, s.currentTool])
|
|
1263
|
+
)
|
|
1199
1264
|
});
|
|
1200
1265
|
|
|
1201
1266
|
// 异步更新持久化
|