@yeaft/webchat-agent 0.1.314 → 0.1.316
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/crew/control.js +2 -0
- package/crew/human-interaction.js +3 -0
- package/crew/persistence.js +21 -1
- package/crew/role-management.js +7 -0
- package/crew/role-output.js +5 -0
- package/crew/routing.js +5 -1
- package/crew/session.js +3 -1
- package/crew/ui-messages.js +2 -5
- package/package.json +1 -1
package/crew/control.js
CHANGED
|
@@ -187,6 +187,8 @@ async function resumeSession(session) {
|
|
|
187
187
|
message: { role: 'assistant', content: [{ type: 'text', text: 'Session 已恢复' }] }
|
|
188
188
|
});
|
|
189
189
|
sendStatusUpdate(session);
|
|
190
|
+
// Status changed to running — persist
|
|
191
|
+
saveSessionMeta(session).catch(e => console.warn('[Crew] Failed to save after resume:', e.message));
|
|
190
192
|
|
|
191
193
|
if (session.pendingRoutes.length > 0) {
|
|
192
194
|
const pending = session.pendingRoutes.slice();
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { dispatchToRole } from './routing.js';
|
|
6
6
|
import { sendStatusUpdate } from './ui-messages.js';
|
|
7
|
+
import { debouncedSaveSessionMeta } from './persistence.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* 处理人的输入
|
|
@@ -50,6 +51,8 @@ export async function handleCrewHumanInput(msg) {
|
|
|
50
51
|
session.status = 'running';
|
|
51
52
|
session.waitingHumanContext = null;
|
|
52
53
|
sendStatusUpdate(session);
|
|
54
|
+
// Status changed + new human message — persist (debounced, dispatch will follow)
|
|
55
|
+
debouncedSaveSessionMeta(session);
|
|
53
56
|
|
|
54
57
|
const target = targetRole || waitingContext?.fromRole || session.decisionMaker;
|
|
55
58
|
await dispatchToRole(session, target, buildHumanContent('人工回复:', content), 'human');
|
package/crew/persistence.js
CHANGED
|
@@ -119,6 +119,24 @@ export async function unhideCrewSession(sessionId) {
|
|
|
119
119
|
// =====================================================================
|
|
120
120
|
|
|
121
121
|
const MESSAGE_SHARD_SIZE = 256 * 1024; // 256KB per shard
|
|
122
|
+
const SAVE_DEBOUNCE_MS = 10_000; // 10 seconds
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Debounced version of saveSessionMeta.
|
|
126
|
+
* Coalesces rapid-fire calls (e.g. multiple role turn-ends) into a single
|
|
127
|
+
* disk write per session, at most once every SAVE_DEBOUNCE_MS.
|
|
128
|
+
*/
|
|
129
|
+
export function debouncedSaveSessionMeta(session) {
|
|
130
|
+
if (session._saveDebounceTimer) {
|
|
131
|
+
clearTimeout(session._saveDebounceTimer);
|
|
132
|
+
}
|
|
133
|
+
session._saveDebounceTimer = setTimeout(() => {
|
|
134
|
+
session._saveDebounceTimer = null;
|
|
135
|
+
saveSessionMeta(session).catch(e =>
|
|
136
|
+
console.warn('[Crew] Debounced save failed:', e.message)
|
|
137
|
+
);
|
|
138
|
+
}, SAVE_DEBOUNCE_MS);
|
|
139
|
+
}
|
|
122
140
|
|
|
123
141
|
export async function saveSessionMeta(session) {
|
|
124
142
|
const meta = {
|
|
@@ -127,9 +145,11 @@ export async function saveSessionMeta(session) {
|
|
|
127
145
|
sharedDir: session.sharedDir,
|
|
128
146
|
name: session.name || '',
|
|
129
147
|
status: session.status,
|
|
148
|
+
// claudeMd is intentionally excluded — it's large and already persisted
|
|
149
|
+
// in per-role CLAUDE.md files under .crew/roles/<name>/CLAUDE.md
|
|
130
150
|
roles: Array.from(session.roles.values()).map(r => ({
|
|
131
151
|
name: r.name, displayName: r.displayName, icon: r.icon,
|
|
132
|
-
description: r.description,
|
|
152
|
+
description: r.description,
|
|
133
153
|
isDecisionMaker: r.isDecisionMaker || false,
|
|
134
154
|
groupIndex: r.groupIndex, roleType: r.roleType, model: r.model
|
|
135
155
|
})),
|
package/crew/role-management.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { initRoleDir, updateSharedClaudeMd } from './shared-dir.js';
|
|
6
6
|
import { saveRoleSessionId } from './role-query.js';
|
|
7
7
|
import { sendCrewMessage, sendCrewOutput, sendStatusUpdate } from './ui-messages.js';
|
|
8
|
+
import { saveSessionMeta, upsertCrewIndex } from './persistence.js';
|
|
8
9
|
|
|
9
10
|
/** Format role label */
|
|
10
11
|
function roleLabel(r) {
|
|
@@ -94,6 +95,9 @@ export async function addRoleToSession(msg) {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
sendStatusUpdate(session);
|
|
98
|
+
// Roles changed — persist
|
|
99
|
+
saveSessionMeta(session).catch(e => console.warn('[Crew] Failed to save after addRole:', e.message));
|
|
100
|
+
upsertCrewIndex(session).catch(e => console.warn('[Crew] Failed to update index after addRole:', e.message));
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
/**
|
|
@@ -152,4 +156,7 @@ export async function removeRoleFromSession(msg) {
|
|
|
152
156
|
});
|
|
153
157
|
|
|
154
158
|
sendStatusUpdate(session);
|
|
159
|
+
// Roles changed — persist
|
|
160
|
+
saveSessionMeta(session).catch(e => console.warn('[Crew] Failed to save after removeRole:', e.message));
|
|
161
|
+
upsertCrewIndex(session).catch(e => console.warn('[Crew] Failed to update index after removeRole:', e.message));
|
|
155
162
|
}
|
package/crew/role-output.js
CHANGED
|
@@ -6,6 +6,7 @@ import { sendCrewMessage, sendCrewOutput, sendStatusUpdate, endRoleStreaming } f
|
|
|
6
6
|
import { saveRoleSessionId, clearRoleSessionId, classifyRoleError, createRoleQuery } from './role-query.js';
|
|
7
7
|
import { parseRoutes, executeRoute, dispatchToRole } from './routing.js';
|
|
8
8
|
import { parseCompletedTasks, updateFeatureIndex, appendChangelog, saveRoleWorkSummary, updateKanban } from './task-files.js';
|
|
9
|
+
import { debouncedSaveSessionMeta, saveSessionMeta } from './persistence.js';
|
|
9
10
|
import ctx from '../context.js';
|
|
10
11
|
|
|
11
12
|
// Context 使用率常量(运行时从 ctx.CONFIG 读取)
|
|
@@ -170,6 +171,8 @@ export async function processRoleOutput(session, roleName, roleQuery, roleState)
|
|
|
170
171
|
});
|
|
171
172
|
|
|
172
173
|
sendStatusUpdate(session);
|
|
174
|
+
// Cost/tokens/messages updated — debounced persist (coalesces rapid turn-ends)
|
|
175
|
+
debouncedSaveSessionMeta(session);
|
|
173
176
|
|
|
174
177
|
// 执行路由
|
|
175
178
|
if (routes.length > 0) {
|
|
@@ -267,6 +270,8 @@ export async function processRoleOutput(session, roleName, roleQuery, roleState)
|
|
|
267
270
|
message: errDetail
|
|
268
271
|
});
|
|
269
272
|
sendStatusUpdate(session);
|
|
273
|
+
// Status changed to waiting_human — persist
|
|
274
|
+
saveSessionMeta(session).catch(e => console.warn('[Crew] Failed to save after error→human:', e.message));
|
|
270
275
|
}
|
|
271
276
|
return;
|
|
272
277
|
}
|
package/crew/routing.js
CHANGED
|
@@ -6,6 +6,7 @@ import { join } from 'path';
|
|
|
6
6
|
import { sendCrewMessage, sendCrewOutput, sendStatusUpdate } from './ui-messages.js';
|
|
7
7
|
import { ensureTaskFile, appendTaskRecord, readTaskFile, updateKanban, readKanban, saveRoleWorkSummary } from './task-files.js';
|
|
8
8
|
import { createRoleQuery, clearRoleSessionId } from './role-query.js';
|
|
9
|
+
import { saveSessionMeta } from './persistence.js';
|
|
9
10
|
import ctx from '../context.js';
|
|
10
11
|
|
|
11
12
|
/** Format role label */
|
|
@@ -19,7 +20,8 @@ function roleLabel(r) {
|
|
|
19
20
|
*/
|
|
20
21
|
export function parseRoutes(text) {
|
|
21
22
|
const routes = [];
|
|
22
|
-
|
|
23
|
+
// ★ Tolerate both underscore and space variants: ---END_ROUTE--- or ---END ROUTE---
|
|
24
|
+
const regex = /---ROUTE---\s*\n([\s\S]*?)---END[_ ]ROUTE---/g;
|
|
23
25
|
let match;
|
|
24
26
|
|
|
25
27
|
while ((match = regex.exec(text)) !== null) {
|
|
@@ -168,6 +170,8 @@ export async function executeRoute(session, fromRole, route) {
|
|
|
168
170
|
message: summary
|
|
169
171
|
});
|
|
170
172
|
sendStatusUpdate(session);
|
|
173
|
+
// Status changed to waiting_human — persist
|
|
174
|
+
saveSessionMeta(session).catch(e => console.warn('[Crew] Failed to save after →human:', e.message));
|
|
171
175
|
return;
|
|
172
176
|
}
|
|
173
177
|
|
package/crew/session.js
CHANGED
|
@@ -488,6 +488,8 @@ export async function resumeCrewSession(msg) {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
const roles = meta.roles || [];
|
|
491
|
+
// Migration: strip claudeMd from legacy session.json (now persisted in per-role CLAUDE.md files)
|
|
492
|
+
for (const r of roles) delete r.claudeMd;
|
|
491
493
|
if (roles.length === 0) {
|
|
492
494
|
console.warn(`[Crew] resumeCrewSession: session ${sessionId} has empty roles in session.json`);
|
|
493
495
|
}
|
|
@@ -594,11 +596,11 @@ export async function handleUpdateCrewSession(msg) {
|
|
|
594
596
|
console.log(`[Crew] Role added during update: ${r.name} (${r.displayName})`);
|
|
595
597
|
} else {
|
|
596
598
|
// Existing role — update metadata (displayName, icon, etc.) but preserve roleState
|
|
599
|
+
// claudeMd is NOT updated here — it's managed via CLAUDE.md files, not through edit session
|
|
597
600
|
const existing = session.roles.get(r.name);
|
|
598
601
|
existing.displayName = r.displayName;
|
|
599
602
|
existing.icon = r.icon;
|
|
600
603
|
existing.description = r.description;
|
|
601
|
-
existing.claudeMd = r.claudeMd;
|
|
602
604
|
existing.isDecisionMaker = r.isDecisionMaker;
|
|
603
605
|
existing.roleType = r.roleType;
|
|
604
606
|
existing.groupIndex = r.groupIndex;
|
package/crew/ui-messages.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* sendCrewMessage, sendCrewOutput, sendStatusUpdate, endRoleStreaming, findActiveRole
|
|
4
4
|
*/
|
|
5
5
|
import ctx from '../context.js';
|
|
6
|
-
import { upsertCrewIndex, saveSessionMeta } from './persistence.js';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* 发送 crew 消息到 server(透传到 Web)
|
|
@@ -257,8 +256,6 @@ export function sendStatusUpdate(session) {
|
|
|
257
256
|
features: getActiveFeatures(session),
|
|
258
257
|
initProgress: session.initProgress || null
|
|
259
258
|
});
|
|
260
|
-
|
|
261
|
-
//
|
|
262
|
-
upsertCrewIndex(session).catch(e => console.warn('[Crew] Failed to update index:', e.message));
|
|
263
|
-
saveSessionMeta(session).catch(e => console.warn('[Crew] Failed to save session meta:', e.message));
|
|
259
|
+
// Persist is NOT called here — callers that change persistent state
|
|
260
|
+
// (status, roles, cost, messages) must call saveSessionMeta explicitly.
|
|
264
261
|
}
|