@yeaft/webchat-agent 0.1.972 → 0.1.973

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.
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * 1. **Top-up missing stock VPs**. If a vpId from `DEFAULT_VPS` is not
15
15
  * on disk, `createVp()` it. This keeps product-owned defaults such as
16
- * Omni and the expanded role roster visible in group/member pickers.
16
+ * Omni and the expanded role roster visible in session/member pickers.
17
17
  *
18
18
  * 2. **Backfill the `area` frontmatter line** on existing seeded VPs whose
19
19
  * role.md predates the area field. The body is left BYTE-IDENTICAL —
@@ -25,7 +25,7 @@
25
25
  * - **Never** overwrite a VP that is on disk. The user might have edited
26
26
  * persona/role/traits; that is their truth, not ours.
27
27
  * - **Keep stock defaults available.** If a shipped stock VP is missing,
28
- * recreate it, but never overwrite an on-disk VP. The group/member picker
28
+ * recreate it, but never overwrite an on-disk VP. The session/member picker
29
29
  * depends on these product-owned defaults being present.
30
30
  * - Best-effort: any failure is logged, never thrown.
31
31
  *
@@ -209,6 +209,37 @@ export function insertNameZhLine(source, nameZh) {
209
209
  return insertFrontmatterLine(source, 'nameZh', nameZh);
210
210
  }
211
211
 
212
+ /**
213
+ * Backfill `roleZh:` into role.md frontmatter.
214
+ *
215
+ * @param {string} source
216
+ * @param {string} roleZh
217
+ * @returns {string|null}
218
+ */
219
+ export function insertRoleZhLine(source, roleZh) {
220
+ return insertFrontmatterLine(source, 'roleZh', roleZh);
221
+ }
222
+
223
+ function roleBodyOf(source) {
224
+ const match = String(source || '').match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/);
225
+ return match ? match[1] : '';
226
+ }
227
+
228
+ function replaceRoleBody(source, body) {
229
+ const match = String(source || '').match(/^(---\r?\n[\s\S]*?\r?\n---\r?\n?)([\s\S]*)$/);
230
+ if (!match) return null;
231
+ return `${match[1]}${String(body || '').trim()}\n`;
232
+ }
233
+
234
+ function backfillLocalizedPersonaBody(source, vp) {
235
+ const body = roleBodyOf(source).trim();
236
+ const nextBody = typeof vp.persona === 'string' ? vp.persona.trim() : '';
237
+ const oldBody = typeof vp.personaEn === 'string' ? vp.personaEn.trim() : '';
238
+ if (!body || !nextBody || body.includes('<!-- lang:')) return null;
239
+ if (!oldBody || body !== oldBody) return null;
240
+ return replaceRoleBody(source, nextBody);
241
+ }
242
+
212
243
  /**
213
244
  * Top-up the default VPs into an existing `libDir`.
214
245
  *
@@ -217,6 +248,8 @@ export function insertNameZhLine(source, nameZh) {
217
248
  * added: string[],
218
249
  * areaBackfilled: string[],
219
250
  * nameZhBackfilled: string[],
251
+ * roleZhBackfilled: string[],
252
+ * personaBackfilled: string[],
220
253
  * respectedDeletes: string[],
221
254
  * skippedExisting: string[],
222
255
  * errors: Array<{vpId:string, code:string, message:string}>,
@@ -226,6 +259,8 @@ export function topUpDefaultVps(libDir = DEFAULT_VP_LIB_DIR) {
226
259
  const added = [];
227
260
  const areaBackfilled = [];
228
261
  const nameZhBackfilled = [];
262
+ const roleZhBackfilled = [];
263
+ const personaBackfilled = [];
229
264
  const respectedDeletes = [];
230
265
  const skippedExisting = [];
231
266
  /** @type {Array<{vpId:string,code:string,message:string}>} */
@@ -235,7 +270,7 @@ export function topUpDefaultVps(libDir = DEFAULT_VP_LIB_DIR) {
235
270
  // that case there's nothing to top up — seedDefaultVps will populate
236
271
  // everything. We still return cleanly.
237
272
  if (!existsSync(libDir)) {
238
- return { added, areaBackfilled, nameZhBackfilled, respectedDeletes, skippedExisting, errors };
273
+ return { added, areaBackfilled, nameZhBackfilled, roleZhBackfilled, personaBackfilled, respectedDeletes, skippedExisting, errors };
239
274
  }
240
275
 
241
276
  let versions = readSeedVersions(libDir);
@@ -314,6 +349,47 @@ export function topUpDefaultVps(libDir = DEFAULT_VP_LIB_DIR) {
314
349
  });
315
350
  }
316
351
  }
352
+ if (vp.roleZh) {
353
+ try {
354
+ const src = readRole();
355
+ if (src != null) {
356
+ const patched = insertRoleZhLine(src, vp.roleZh);
357
+ if (patched != null && patched !== src) {
358
+ writeFileSync(rolePath, patched, 'utf-8');
359
+ currentSrc = patched;
360
+ roleZhBackfilled.push(vpId);
361
+ }
362
+ }
363
+ } catch (err) {
364
+ errors.push({
365
+ vpId,
366
+ code: 'role_zh_backfill_failed',
367
+ message: String(err?.message || err),
368
+ });
369
+ }
370
+ }
371
+ // v0.1.967 — stock VP soul localization. Existing first-run stock VPs
372
+ // have an English-only body. Replace only the exact old stock body;
373
+ // any user-edited body is respected byte-for-byte.
374
+ if (vp.personaZh && vp.personaEn) {
375
+ try {
376
+ const src = readRole();
377
+ if (src != null) {
378
+ const patched = backfillLocalizedPersonaBody(src, vp);
379
+ if (patched != null && patched !== src) {
380
+ writeFileSync(rolePath, patched, 'utf-8');
381
+ currentSrc = patched;
382
+ personaBackfilled.push(vpId);
383
+ }
384
+ }
385
+ } catch (err) {
386
+ errors.push({
387
+ vpId,
388
+ code: 'persona_localization_backfill_failed',
389
+ message: String(err?.message || err),
390
+ });
391
+ }
392
+ }
317
393
  // Make sure the ledger records it (e.g. user-authored VP whose id
318
394
  // collides with a default — we still want to skip future creates).
319
395
  if (!inLedger) versions.seeded[vpId] = 'legacy';
@@ -323,7 +399,7 @@ export function topUpDefaultVps(libDir = DEFAULT_VP_LIB_DIR) {
323
399
  if (inLedger && !STOCK_VP_IDS.has(vpId)) {
324
400
  // We seeded this custom/default VP before, user has since deleted it — respect that.
325
401
  // Stock/default personas are product-owned roster entries and must remain
326
- // available in group creation/member pickers after migrations. Recreate
402
+ // available in session creation/member pickers after migrations. Recreate
327
403
  // them below without overwriting anything that exists on disk.
328
404
  respectedDeletes.push(vpId);
329
405
  continue;
@@ -351,5 +427,5 @@ export function topUpDefaultVps(libDir = DEFAULT_VP_LIB_DIR) {
351
427
  }
352
428
 
353
429
  writeSeedVersions(libDir, versions);
354
- return { added, areaBackfilled, nameZhBackfilled, respectedDeletes, skippedExisting, errors };
430
+ return { added, areaBackfilled, nameZhBackfilled, roleZhBackfilled, personaBackfilled, respectedDeletes, skippedExisting, errors };
355
431
  }