@yeaft/webchat-agent 0.1.608 → 0.1.609

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.
@@ -36,7 +36,7 @@ import { sendToServer, flushMessageBuffer } from './buffer.js';
36
36
  import { handleRestartAgent, handleUpgradeAgent } from './upgrade.js';
37
37
  import { loadMcpServers, updateMcpConfig } from '../mcp.js';
38
38
  import { getLlmConfig, updateLlmConfig, getUnifySettings, updateUnifySettings } from '../unify/config-api.js';
39
- import { handleUnifyChat, handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyMergeThread, handleUnifyForkThread, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyTaskMessage, handleUnifyUserMemoryWrite, handleUnifyUserMemoryRemove, handleUnifyMemoryScopeList, handleUnifyMemoryQuery, handleUnifyMemoryTrace, handleUnifyFetchSummaryHistory, handleUnifyTaskCrud, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyArchiveGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger } from '../unify/web-bridge.js';
39
+ import { handleUnifyChat, handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyMergeThread, handleUnifyForkThread, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyTaskMessage, handleUnifyUserMemoryWrite, handleUnifyUserMemoryRemove, handleUnifyMemoryScopeList, handleUnifyMemoryQuery, handleUnifyMemoryTrace, handleUnifyFetchSummaryHistory, handleUnifyTaskCrud, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyArchiveGroup, handleUnifyDeleteGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger } from '../unify/web-bridge.js';
40
40
 
41
41
  export async function handleMessage(msg) {
42
42
  switch (msg.type) {
@@ -479,6 +479,9 @@ export async function handleMessage(msg) {
479
479
  case 'unify_archive_group':
480
480
  handleUnifyArchiveGroup(msg);
481
481
  break;
482
+ case 'unify_delete_group':
483
+ handleUnifyDeleteGroup(msg);
484
+ break;
482
485
  case 'unify_add_member':
483
486
  handleUnifyAddMember(msg);
484
487
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.608",
3
+ "version": "0.1.609",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -32,7 +32,7 @@
32
32
  * 'reserved'/'invalid_vp_id'/... — bubbled from ids.js validators
33
33
  */
34
34
 
35
- import { existsSync, renameSync } from 'fs';
35
+ import { existsSync, renameSync, rmSync, readdirSync, statSync } from 'fs';
36
36
  import { randomBytes } from 'crypto';
37
37
  import { join } from 'path';
38
38
  import {
@@ -184,6 +184,74 @@ export function archiveGroup(yeaftDir, groupId) {
184
184
  return { groupId, archivedAs: dstDir };
185
185
  }
186
186
 
187
+ /**
188
+ * (A.3.b) Delete — physically remove the group directory and all its
189
+ * contents (group.json, messages/, tasks/, vps/). Irreversible.
190
+ *
191
+ * Bug 8 fix: replaces the soft-archive flow that left `.archived-*` dirs
192
+ * lying around in `~/.yeaft/groups/`. Per user request, "delete" means
193
+ * physical deletion, not rename.
194
+ *
195
+ * Also sweeps any sibling `.archived-*-<groupId>` dirs that were left
196
+ * behind by the previous soft-archive implementation, so a single
197
+ * delete cleans up legacy state too.
198
+ */
199
+ export function deleteGroup(yeaftDir, groupId) {
200
+ const root = groupsRoot(yeaftDir);
201
+ const srcDir = join(root, groupId);
202
+ const liveExists = existsSync(srcDir) && !!loadGroupMeta(srcDir);
203
+
204
+ // Collect any leftover soft-archive directories matching this groupId.
205
+ const legacyDirs = [];
206
+ if (existsSync(root)) {
207
+ for (const name of readdirSync(root)) {
208
+ if (!name.startsWith('.archived-')) continue;
209
+ // Soft-archive format: .archived-<ts>-<suffix>-<groupId>
210
+ if (!name.endsWith(`-${groupId}`)) continue;
211
+ const p = join(root, name);
212
+ try {
213
+ if (statSync(p).isDirectory()) legacyDirs.push(p);
214
+ } catch { /* skip */ }
215
+ }
216
+ }
217
+
218
+ if (!liveExists && legacyDirs.length === 0) {
219
+ throw new GroupCrudError('not_found', groupId);
220
+ }
221
+
222
+ if (liveExists) {
223
+ rmSync(srcDir, { recursive: true, force: true });
224
+ }
225
+ for (const dir of legacyDirs) {
226
+ rmSync(dir, { recursive: true, force: true });
227
+ }
228
+
229
+ return { groupId, deleted: true, legacyCleanedUp: legacyDirs.length };
230
+ }
231
+
232
+ /**
233
+ * Sweep any leftover `.archived-*` directories under groups/ that are
234
+ * orphans of the old soft-archive flow. Used at boot so users don't see
235
+ * ghost groups in subsequent loads. Returns the list of removed paths.
236
+ */
237
+ export function purgeArchivedGroups(yeaftDir) {
238
+ const root = groupsRoot(yeaftDir);
239
+ if (!existsSync(root)) return [];
240
+ const removed = [];
241
+ for (const name of readdirSync(root)) {
242
+ if (!name.startsWith('.archived-')) continue;
243
+ const p = join(root, name);
244
+ try {
245
+ if (!statSync(p).isDirectory()) continue;
246
+ } catch { continue; }
247
+ try {
248
+ rmSync(p, { recursive: true, force: true });
249
+ removed.push(p);
250
+ } catch { /* skip */ }
251
+ }
252
+ return removed;
253
+ }
254
+
187
255
  /**
188
256
  * (A.4) Add a VP to the group roster. Idempotent — no-op if already present.
189
257
  * Returns the new meta.
@@ -151,6 +151,8 @@ export function listGroups(groupsRoot) {
151
151
  if (!existsSync(groupsRoot)) return [];
152
152
  const out = [];
153
153
  for (const name of readdirSync(groupsRoot)) {
154
+ // Skip dotfiles and legacy soft-archive dirs (`.archived-*`).
155
+ if (name.startsWith('.')) continue;
154
156
  const p = join(groupsRoot, name);
155
157
  try {
156
158
  if (!statSync(p).isDirectory()) continue;
@@ -44,6 +44,8 @@ export {
44
44
  createGroupFromSpec,
45
45
  renameGroup,
46
46
  archiveGroup,
47
+ deleteGroup,
48
+ purgeArchivedGroups,
47
49
  addMember,
48
50
  removeMember,
49
51
  setGroupDefaultVp,
@@ -40,6 +40,8 @@ import {
40
40
  createGroupFromSpec,
41
41
  renameGroup,
42
42
  archiveGroup,
43
+ deleteGroup,
44
+ purgeArchivedGroups,
43
45
  addMember,
44
46
  removeMember,
45
47
  setGroupDefaultVp,
@@ -455,6 +457,23 @@ export function handleUnifyArchiveGroup(msg) {
455
457
  }
456
458
  }
457
459
 
460
+ /**
461
+ * Bug 8: physical delete — removes the group dir and any legacy
462
+ * `.archived-*-<groupId>` siblings. Replies with op:'delete'.
463
+ */
464
+ export function handleUnifyDeleteGroup(msg) {
465
+ const requestId = msg && msg.requestId;
466
+ const groupId = msg && msg.groupId;
467
+ try {
468
+ const yeaftDir = ctx.CONFIG?.yeaftDir;
469
+ const result = deleteGroup(yeaftDir, groupId);
470
+ sendGroupCrudResult({ op: 'delete', requestId, ok: true, groupId: result.groupId });
471
+ sendGroupSnapshotBroadcast();
472
+ } catch (err) {
473
+ sendGroupCrudResult({ op: 'delete', requestId, ok: false, error: groupErrorPayload(err) });
474
+ }
475
+ }
476
+
458
477
  export function handleUnifyAddMember(msg) {
459
478
  const requestId = msg && msg.requestId;
460
479
  const groupId = msg && msg.groupId;
@@ -1370,6 +1389,21 @@ export async function handleUnifyChat(msg) {
1370
1389
  runAutoArchiveSweep(session);
1371
1390
  scheduleAutoArchive(session);
1372
1391
 
1392
+ // Bug 8: clean up any legacy `.archived-*` group directories left
1393
+ // behind by the previous soft-archive flow. This is a one-shot
1394
+ // boot-time sweep — physical deletes after this point are immediate.
1395
+ try {
1396
+ const yeaftDir = ctx.CONFIG?.yeaftDir;
1397
+ if (yeaftDir) {
1398
+ const removed = purgeArchivedGroups(yeaftDir);
1399
+ if (removed && removed.length > 0) {
1400
+ console.log(`[Unify] purged ${removed.length} legacy .archived group dir(s)`);
1401
+ }
1402
+ }
1403
+ } catch (err) {
1404
+ console.warn('[Unify] purgeArchivedGroups failed:', err?.message || err);
1405
+ }
1406
+
1373
1407
  // Create a stable conversationId for the Unify session
1374
1408
  unifyConversationId = `unify-${Date.now()}`;
1375
1409