dsh-fish-memory 1.5.2 → 1.5.4
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/lib/client.js +366 -71
- package/lib/index.js +245 -41
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -193,10 +193,13 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
193
193
|
const [confirm, setConfirm] = React.useState(null) // 正在等确认的删除(二次确认,手滑不了)
|
|
194
194
|
const [并入, set并入] = React.useState(null) // ⭐ 1.4.5:切成按工作区前的「合并预览」
|
|
195
195
|
const [showDeleted, setShowDeleted] = React.useState(false)
|
|
196
|
-
const [selfDraft, setSelfDraft] = React.useState({}) //
|
|
196
|
+
const [selfDraft, setSelfDraft] = React.useState({}) // 每个会话「它自己叫什么」的草稿
|
|
197
|
+
const [称草稿, set称草稿] = React.useState({}) // 每个会话「记忆里指对方用哪个字」
|
|
198
|
+
const [自草稿, set自草稿] = React.useState({}) // 每个会话「它自称什么」
|
|
197
199
|
const [ownerDraft, setOwnerDraft] = React.useState({}) // 每个会话「怎么称呼对方」的草稿
|
|
198
|
-
const [pronounDraft, setPronounDraft] = React.useState(null) // 用户的称呼草稿
|
|
199
200
|
const [edit, setEdit] = React.useState(null) // 正在改的那条:{id, text, importance}
|
|
201
|
+
// ⭐ 检索词当场改:{记忆id: 草稿}(模型写的词常常不靠谱,改一次比重新扩一遍快)
|
|
202
|
+
const [改词, set改词] = React.useState({})
|
|
200
203
|
const [pronounAsk, setPronounAsk] = React.useState(null) // 改了称呼,问要不要连旧记忆一起改
|
|
201
204
|
const [imp, setImp] = React.useState(null) // 档案导入:扫描结果 + 勾选状态
|
|
202
205
|
const [impBusy, setImpBusy] = React.useState(false)
|
|
@@ -260,6 +263,8 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
260
263
|
// 用户导入自己的会话 → 模型读一遍 → 出一份档案 → 再走「从旧档案导入」变成条目
|
|
261
264
|
// ⚠ 四步分开,因为**第三步调模型、花钱**,得一批一批停下来看 ✓
|
|
262
265
|
const [生成开, set生成开] = React.useState(false) // 面板展开没有
|
|
266
|
+
// ⭐ 2026-11-04:停用后面板不开了,改成一个**贴在按钮下面**的小提示(不进 notes,免得看不见)
|
|
267
|
+
const [星汐提示, set星汐提示] = React.useState(false)
|
|
263
268
|
const [生成会话, set生成会话] = React.useState(null) // 这个工作区有哪些会话
|
|
264
269
|
const [生成选, set生成选] = React.useState({}) // 勾了哪几个
|
|
265
270
|
const [生成计划, set生成计划] = React.useState(null) // 算出来的:几批、多少字、多少 token
|
|
@@ -548,6 +553,26 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
548
553
|
)
|
|
549
554
|
|
|
550
555
|
// 改一条:文本框 + 重要度 + 存 / 算了
|
|
556
|
+
// 检索词:空格 / 逗号 / 顿号分开,最多 12 个
|
|
557
|
+
const 拆词 = (文) =>
|
|
558
|
+
String(文 || '')
|
|
559
|
+
.split(/[\s,,、]+/)
|
|
560
|
+
.map((x) => x.trim())
|
|
561
|
+
.filter(Boolean)
|
|
562
|
+
.slice(0, 12)
|
|
563
|
+
|
|
564
|
+
// 小按钮长相:跟文字分开,别让人以为它是检索词里的一个字
|
|
565
|
+
const 词按钮 = {
|
|
566
|
+
flex: 'none',
|
|
567
|
+
fontSize: 11,
|
|
568
|
+
padding: '1px 8px',
|
|
569
|
+
borderRadius: 10,
|
|
570
|
+
border: '1px solid rgba(122,170,220,.55)',
|
|
571
|
+
color: '#7aaadc',
|
|
572
|
+
cursor: 'pointer',
|
|
573
|
+
whiteSpace: 'nowrap',
|
|
574
|
+
}
|
|
575
|
+
|
|
551
576
|
const 改一条 = (m) =>
|
|
552
577
|
React.createElement(
|
|
553
578
|
'div',
|
|
@@ -580,7 +605,13 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
580
605
|
style: S.btn,
|
|
581
606
|
disabled: !String(edit.text || '').trim(),
|
|
582
607
|
onClick: () =>
|
|
583
|
-
post('/edit', {
|
|
608
|
+
post('/edit', {
|
|
609
|
+
session: open,
|
|
610
|
+
id: m.id,
|
|
611
|
+
text: edit.text,
|
|
612
|
+
importance: edit.importance,
|
|
613
|
+
related: 拆词(edit.related !== undefined ? edit.related : (Array.isArray(m.related) ? m.related.join(' ') : '')),
|
|
614
|
+
}).then(() => {
|
|
584
615
|
setEdit(null)
|
|
585
616
|
load()
|
|
586
617
|
}),
|
|
@@ -589,7 +620,84 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
589
620
|
),
|
|
590
621
|
React.createElement('button', { style: S.btn, onClick: () => setEdit(null) }, '算了'),
|
|
591
622
|
),
|
|
623
|
+
// ⭐ 1.5.4:改正文这个框里也一起给了检索词 —— 以前只能改正文,
|
|
624
|
+
// 想改检索词得再去点下面那行,容易找不到
|
|
625
|
+
React.createElement(
|
|
626
|
+
'div',
|
|
627
|
+
{ style: { display: 'flex', gap: 8, alignItems: 'center', marginTop: 6 } },
|
|
628
|
+
React.createElement('span', { style: { flex: 'none', ...S.dim } }, '检索词'),
|
|
629
|
+
React.createElement('input', {
|
|
630
|
+
style: Object.assign({}, S.inp, { flex: 1 }),
|
|
631
|
+
placeholder: '空格或逗号分开,最多 12 个',
|
|
632
|
+
value: edit.related !== undefined ? edit.related : (Array.isArray(m.related) ? m.related.join(' ') : ''),
|
|
633
|
+
onChange: (e) => setEdit(Object.assign({}, edit, { related: e.target.value })),
|
|
634
|
+
}),
|
|
635
|
+
React.createElement('span', { style: Object.assign({}, S.dim, { fontSize: 12, flex: 'none' }) }, '以后她说这些词就能捞到这条'),
|
|
636
|
+
),
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
// ⭐ 检索词能当场改:模型写的词经常不靠谱,改一次比重新扩一遍快,也不动正文
|
|
640
|
+
const 存检索词 = (m, 文本) => {
|
|
641
|
+
const 词 = 拆词(文本)
|
|
642
|
+
post('/edit', {
|
|
643
|
+
session: open,
|
|
644
|
+
id: m.id,
|
|
645
|
+
text: m.text,
|
|
646
|
+
importance: typeof m.importance === 'number' ? m.importance : 0.5,
|
|
647
|
+
related: 词,
|
|
648
|
+
}).then(() => {
|
|
649
|
+
const 新 = Object.assign({}, 改词)
|
|
650
|
+
delete 新[m.id]
|
|
651
|
+
set改词(新)
|
|
652
|
+
load()
|
|
653
|
+
})
|
|
654
|
+
}
|
|
655
|
+
const 关掉改词 = (id) => {
|
|
656
|
+
const 新 = Object.assign({}, 改词)
|
|
657
|
+
delete 新[id]
|
|
658
|
+
set改词(新)
|
|
659
|
+
}
|
|
660
|
+
const 检索词行 = (m) => {
|
|
661
|
+
const 在改 = 改词[m.id] !== undefined
|
|
662
|
+
const 有 = Array.isArray(m.related) && m.related.length
|
|
663
|
+
if (在改) {
|
|
664
|
+
return React.createElement(
|
|
665
|
+
'div',
|
|
666
|
+
{ style: Object.assign({}, S.dim, { margin: '0 0 6px 104px', fontSize: 11, display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }) },
|
|
667
|
+
React.createElement('span', null, '能搜到它的词:'),
|
|
668
|
+
React.createElement('input', {
|
|
669
|
+
style: Object.assign({}, S.inp, { width: 340, fontSize: 11 }),
|
|
670
|
+
value: 改词[m.id],
|
|
671
|
+
placeholder: '空格或逗号分开,最多 12 个',
|
|
672
|
+
onChange: (e) => set改词(Object.assign({}, 改词, { [m.id]: e.target.value })),
|
|
673
|
+
}),
|
|
674
|
+
React.createElement('span', { style: Object.assign({}, S.link, { fontSize: 11 }), onClick: () => 存检索词(m, 改词[m.id]) }, '存'),
|
|
675
|
+
React.createElement('span', { style: Object.assign({}, S.link, { fontSize: 11 }), onClick: () => 关掉改词(m.id) }, '算了'),
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
if (!有) {
|
|
679
|
+
return React.createElement(
|
|
680
|
+
'div',
|
|
681
|
+
{ style: Object.assign({}, S.dim, { margin: '0 0 6px 104px', fontSize: 11, display: 'flex', gap: 6, alignItems: 'center' }) },
|
|
682
|
+
React.createElement('span', null, '还没有检索词'),
|
|
683
|
+
React.createElement(
|
|
684
|
+
'span',
|
|
685
|
+
{ style: 词按钮, onClick: () => set改词(Object.assign({}, 改词, { [m.id]: '' })) },
|
|
686
|
+
'+ 加检索词',
|
|
687
|
+
),
|
|
688
|
+
)
|
|
689
|
+
}
|
|
690
|
+
return React.createElement(
|
|
691
|
+
'div',
|
|
692
|
+
{ style: Object.assign({}, S.dim, { margin: '0 0 6px 104px', fontSize: 11, overflowWrap: 'anywhere', wordBreak: 'break-word', display: 'flex', gap: 8, alignItems: 'center' }) },
|
|
693
|
+
React.createElement('span', { style: { flex: 1 } }, '能搜到它的词:' + m.related.join(' ')),
|
|
694
|
+
React.createElement(
|
|
695
|
+
'span',
|
|
696
|
+
{ style: 词按钮, onClick: () => set改词(Object.assign({}, 改词, { [m.id]: m.related.join(' ') })) },
|
|
697
|
+
'改检索词',
|
|
698
|
+
),
|
|
592
699
|
)
|
|
700
|
+
}
|
|
593
701
|
|
|
594
702
|
const 一条记忆 = (m) =>
|
|
595
703
|
React.createElement(
|
|
@@ -721,13 +829,7 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
721
829
|
'提炼前:' + m.textBefore,
|
|
722
830
|
)
|
|
723
831
|
: null,
|
|
724
|
-
m
|
|
725
|
-
? React.createElement(
|
|
726
|
-
'div',
|
|
727
|
-
{ style: Object.assign({}, S.dim, { margin: '0 0 6px 104px', fontSize: 11, overflowWrap: 'anywhere', wordBreak: 'break-word' }) },
|
|
728
|
-
'能搜到它的词:' + m.related.join(' '),
|
|
729
|
-
)
|
|
730
|
-
: null,
|
|
832
|
+
检索词行(m),
|
|
731
833
|
// ⭐⭐ 2026-09-23:**「这条讲的是哪件事」** —— 挂在这儿(第二行小字),不占行里的宽度 ✓
|
|
732
834
|
// ⚠⚠ 别用「事实键」这种我自己造的词 ——
|
|
733
835
|
// 她说了「让人看不懂,写通俗一点的」✗
|
|
@@ -1296,7 +1398,19 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
1296
1398
|
// ④ 存成档案文件 → 再走已有的「从旧档案导入」变成记忆条目
|
|
1297
1399
|
// ═══════════════════════════════════════════════════════════════
|
|
1298
1400
|
const 开生成 = () => {
|
|
1299
|
-
|
|
1401
|
+
// ⭐ 2026-11-04:这个自带流程**停用**了。
|
|
1402
|
+
// 它一直有 bug(会话列表筛不准、批次落盘不稳),而且她几乎没用过。
|
|
1403
|
+
// 生成档案这件事已经由独立插件「星汐」(dsh-xingxi)接管 ——
|
|
1404
|
+
// 那边能绑文件夹、按天分批、原文留底,比这里靠谱。
|
|
1405
|
+
// 点按钮不再进旧流程,只弹一句提示(旧面板代码留着,但这里不再打开)。
|
|
1406
|
+
set生成开(false)
|
|
1407
|
+
setNotes([
|
|
1408
|
+
'「从会话生成档案」已经停用 —— 请改用它:**星汐(dsh-xingxi)**。',
|
|
1409
|
+
'在 DSH 里装:dsh plugin add dsh-xingxi;装完侧栏会多一个「星汐」。',
|
|
1410
|
+
'旧流程有 bug(会话列表筛不准、批次落盘不稳),这里不再提供入口。',
|
|
1411
|
+
])
|
|
1412
|
+
return
|
|
1413
|
+
/* eslint-disable no-unreachable */
|
|
1300
1414
|
set生成会话(null)
|
|
1301
1415
|
set生成计划(null)
|
|
1302
1416
|
set生成条目([])
|
|
@@ -2353,43 +2467,69 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
2353
2467
|
return 小件
|
|
2354
2468
|
}
|
|
2355
2469
|
|
|
2470
|
+
// ⭐ 四格排成同一个样子:左边标签等宽、中间输入框、右边说明(字一样大)
|
|
2471
|
+
const 一格 = (标签, 节点, 说明) =>
|
|
2472
|
+
React.createElement(
|
|
2473
|
+
'div',
|
|
2474
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 } },
|
|
2475
|
+
React.createElement('span', { style: { flex: 'none', width: 110, fontSize: 13 } }, 标签),
|
|
2476
|
+
节点,
|
|
2477
|
+
React.createElement('span', { style: Object.assign({}, S.dim, { fontSize: 12, flex: 1, lineHeight: 1.5 }) }, 说明),
|
|
2478
|
+
)
|
|
2479
|
+
|
|
2356
2480
|
return React.createElement(
|
|
2357
2481
|
'div',
|
|
2358
2482
|
{ style: S.wrap },
|
|
2359
2483
|
React.createElement('div', { style: S.link, onClick: () => setOpen(null) }, '← 所有会话'),
|
|
2360
2484
|
React.createElement('div', { style: S.h }, s.title || s.id),
|
|
2361
|
-
React.createElement('div', { style: S.dim }, s.id + ' ' + s.memories.length + ' 条记忆' + (已删.length ? ' (另外还收着 ' + 已删.length + ' 条)' : '')),
|
|
2485
|
+
React.createElement('div', { style: S.dim }, s.id + ' ' + s.memories.length + ' 条记忆' + (已删.length ? ' (另外还收着 ' + 已删.length + ' 条)' : '') + (d.模式 === 'workspace' && s.工作区 ? ' · 按工作区共用一份(' + (s.工作区名 || s.工作区) + ')' : '')),
|
|
2362
2486
|
React.createElement(
|
|
2363
2487
|
'div',
|
|
2364
2488
|
{ style: { padding: '10px 12px', borderRadius: 8, border: '1px solid rgba(128,128,128,.22)', background: 'rgba(127,127,127,.05)' } },
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
React.createElement('span', { style: { flex: 'none', width: 96, fontSize: 13 } }, '她自己叫什么'),
|
|
2489
|
+
// ⭐ 2026-11-04:称呼和自称都并进这张卡(以前吊在外面的「哪些会话用记忆」下面,两边说法还不一样)
|
|
2490
|
+
一格(
|
|
2491
|
+
'它自己叫什么',
|
|
2369
2492
|
React.createElement('input', {
|
|
2370
2493
|
style: Object.assign({}, S.inp, { width: 160 }),
|
|
2371
|
-
placeholder: '
|
|
2494
|
+
placeholder: '它在这儿的名字',
|
|
2372
2495
|
value: selfDraft[open] !== undefined ? selfDraft[open] : s.selfName || s.titleName || '',
|
|
2373
2496
|
onChange: (e) => setSelfDraft(Object.assign({}, selfDraft, { [open]: e.target.value })),
|
|
2374
2497
|
}),
|
|
2375
|
-
s.selfName
|
|
2376
|
-
? null
|
|
2377
|
-
: (s.titleName ? React.createElement('span', { style: Object.assign({}, S.dim, { fontSize: 12 }) }, '(先填的是会话标题,确认没问题再点「存」)') : null),
|
|
2498
|
+
s.selfName ? '记忆里,它的名字会换成「我」' : s.titleName ? '先填的是会话标题,确认没问题再点「存」' : '填了才会自动换人称',
|
|
2378
2499
|
),
|
|
2379
|
-
|
|
2380
|
-
'
|
|
2381
|
-
{ style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 } },
|
|
2382
|
-
React.createElement('span', { style: { flex: 'none', width: 96, fontSize: 13 } }, '她怎么称呼你'),
|
|
2500
|
+
一格(
|
|
2501
|
+
'它怎么称呼你',
|
|
2383
2502
|
React.createElement('input', {
|
|
2384
2503
|
style: Object.assign({}, S.inp, { width: 160 }),
|
|
2385
|
-
placeholder: '
|
|
2504
|
+
placeholder: '它平时怎么叫你(小名、昵称都行)',
|
|
2386
2505
|
value: ownerDraft[open] !== undefined ? ownerDraft[open] : s.ownerName || '',
|
|
2387
2506
|
onChange: (e) => setOwnerDraft(Object.assign({}, ownerDraft, { [open]: e.target.value })),
|
|
2388
2507
|
}),
|
|
2508
|
+
'记忆里,这个叫法会换成「你」',
|
|
2509
|
+
),
|
|
2510
|
+
一格(
|
|
2511
|
+
'记忆里指对方用',
|
|
2512
|
+
React.createElement('input', {
|
|
2513
|
+
style: Object.assign({}, S.inp, { width: 90 }),
|
|
2514
|
+
placeholder: 'ta',
|
|
2515
|
+
value: 称草稿[open] !== undefined ? 称草稿[open] : s.pronoun || 'ta',
|
|
2516
|
+
onChange: (e) => set称草稿(Object.assign({}, 称草稿, { [open]: e.target.value })),
|
|
2517
|
+
}),
|
|
2518
|
+
'ta / 她 / 他 —— 默认 ta,设成哪个就按哪个记',
|
|
2519
|
+
),
|
|
2520
|
+
一格(
|
|
2521
|
+
'它自称什么',
|
|
2522
|
+
React.createElement('input', {
|
|
2523
|
+
style: Object.assign({}, S.inp, { width: 90 }),
|
|
2524
|
+
placeholder: '我',
|
|
2525
|
+
value: 自草稿[open] !== undefined ? 自草稿[open] : s.selfPronoun || '我',
|
|
2526
|
+
onChange: (e) => set自草稿(Object.assign({}, 自草稿, { [open]: e.target.value })),
|
|
2527
|
+
}),
|
|
2528
|
+
'它平时自称什么(本小姐、在下…),默认「我」',
|
|
2389
2529
|
),
|
|
2390
2530
|
React.createElement(
|
|
2391
2531
|
'div',
|
|
2392
|
-
{ style: { display: 'flex', alignItems: 'center', gap: 10 } },
|
|
2532
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 10, marginTop: 4 } },
|
|
2393
2533
|
React.createElement(
|
|
2394
2534
|
'button',
|
|
2395
2535
|
{
|
|
@@ -2399,9 +2539,19 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
2399
2539
|
session: open,
|
|
2400
2540
|
selfName: (selfDraft[open] !== undefined ? selfDraft[open] : s.selfName) || '',
|
|
2401
2541
|
ownerName: (ownerDraft[open] !== undefined ? ownerDraft[open] : s.ownerName) || '',
|
|
2402
|
-
|
|
2542
|
+
pronoun: (称草稿[open] !== undefined ? 称草稿[open] : s.pronoun) || 'ta',
|
|
2543
|
+
selfPronoun: (自草稿[open] !== undefined ? 自草稿[open] : s.selfPronoun) || '我',
|
|
2544
|
+
}).then((r) => {
|
|
2403
2545
|
setSelfDraft({})
|
|
2404
2546
|
setOwnerDraft({})
|
|
2547
|
+
set称草稿({})
|
|
2548
|
+
set自草稿({})
|
|
2549
|
+
// 影响到旧记忆就先问一句,不偷偷改 ✓
|
|
2550
|
+
if (r && r.称呼影响几条 > 0 && !r.称呼已改) {
|
|
2551
|
+
setPronounAsk({ n: r.称呼影响几条, to: r.称呼 || '', 例子: r.称呼例子 || [], session: open })
|
|
2552
|
+
} else {
|
|
2553
|
+
setPronounAsk(null)
|
|
2554
|
+
}
|
|
2405
2555
|
load()
|
|
2406
2556
|
}),
|
|
2407
2557
|
},
|
|
@@ -2410,10 +2560,38 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
2410
2560
|
React.createElement(
|
|
2411
2561
|
'span',
|
|
2412
2562
|
{ style: Object.assign({}, S.dim, { fontSize: 12, flex: 1, lineHeight: 1.5 }) },
|
|
2413
|
-
'
|
|
2563
|
+
'只改这个会话,别的会话各算各的;已有的记忆也会顺手洗一遍。',
|
|
2414
2564
|
),
|
|
2415
2565
|
),
|
|
2416
2566
|
),
|
|
2567
|
+
// ⭐ 改称呼影响到旧记忆时,就地在这个会话里问一句(不偷偷改)
|
|
2568
|
+
pronounAsk && pronounAsk.session === open
|
|
2569
|
+
? React.createElement(
|
|
2570
|
+
'div',
|
|
2571
|
+
{ style: Object.assign({}, S.askBox, { marginTop: 10 }) },
|
|
2572
|
+
React.createElement(
|
|
2573
|
+
'div',
|
|
2574
|
+
{ style: { flex: 1 } },
|
|
2575
|
+
React.createElement('div', null, '这个会话里有 ' + pronounAsk.n + ' 条旧记忆还写着旧的字。要一起改成「' + pronounAsk.to + '」吗?'),
|
|
2576
|
+
(pronounAsk.例子 || []).slice(0, 2).map((e, i) =>
|
|
2577
|
+
React.createElement('div', { key: i, style: Object.assign({}, S.dim, { fontSize: 12, marginTop: 2 }) }, ' 「' + e.旧 + '」→「' + e.新 + '」'),
|
|
2578
|
+
),
|
|
2579
|
+
),
|
|
2580
|
+
React.createElement(
|
|
2581
|
+
'button',
|
|
2582
|
+
{
|
|
2583
|
+
style: S.btn,
|
|
2584
|
+
onClick: () =>
|
|
2585
|
+
post('/names', { session: open, pronoun: pronounAsk.to, applyToExisting: true }).then(() => {
|
|
2586
|
+
setPronounAsk(null)
|
|
2587
|
+
load()
|
|
2588
|
+
}),
|
|
2589
|
+
},
|
|
2590
|
+
'一起改',
|
|
2591
|
+
),
|
|
2592
|
+
React.createElement('button', { style: S.btn, onClick: () => setPronounAsk(null) }, '以后再说'),
|
|
2593
|
+
)
|
|
2594
|
+
: null,
|
|
2417
2595
|
// ---- 2026-09-21 新加:她自己后台手动加一条 ----
|
|
2418
2596
|
// 跟模型自己记是两回事:它记受上面那个开关管,**手动加不受管** ✓
|
|
2419
2597
|
// (开关管的是「他自己记不记、注不注入」,管不到她手动写)
|
|
@@ -2539,19 +2717,44 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
2539
2717
|
'查重',
|
|
2540
2718
|
),
|
|
2541
2719
|
// ⭐⭐ 2026-09-22(她的第十件):**从会话生成档案**
|
|
2542
|
-
//
|
|
2543
|
-
//
|
|
2720
|
+
// ⚠ 2026-11-04 停用:这条流程一直有 bug,已经由独立插件「星汐」(dsh-xingxi) 接管。
|
|
2721
|
+
// 点按钮**就在它下面**展开一小段提示 —— 不再写进 notes
|
|
2722
|
+
// (那玩意儿在列表底下,列表没展开根本看不见,橙色字也太淡)。
|
|
2544
2723
|
React.createElement(
|
|
2545
|
-
'
|
|
2546
|
-
{
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2724
|
+
'div',
|
|
2725
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: 6, maxWidth: 320 } },
|
|
2726
|
+
React.createElement(
|
|
2727
|
+
'button',
|
|
2728
|
+
{
|
|
2729
|
+
style: S.btn,
|
|
2730
|
+
title: '生成档案:点一下看怎么做',
|
|
2731
|
+
onClick: () => set星汐提示((v) => !v),
|
|
2732
|
+
},
|
|
2733
|
+
'从会话生成档案',
|
|
2734
|
+
),
|
|
2735
|
+
星汐提示
|
|
2736
|
+
? React.createElement(
|
|
2737
|
+
'div',
|
|
2738
|
+
{
|
|
2739
|
+
style: {
|
|
2740
|
+
fontSize: 11.5,
|
|
2741
|
+
lineHeight: 1.7,
|
|
2742
|
+
color: 'rgba(196,210,236,0.92)',
|
|
2743
|
+
background: 'rgba(28,38,58,0.55)',
|
|
2744
|
+
border: '1px solid rgba(120,150,205,0.25)',
|
|
2745
|
+
borderRadius: 8,
|
|
2746
|
+
padding: '7px 9px',
|
|
2747
|
+
},
|
|
2748
|
+
},
|
|
2749
|
+
React.createElement('div', null, '改用独立插件「星汐」——它专门干这件事。'),
|
|
2750
|
+
React.createElement(
|
|
2751
|
+
'div',
|
|
2752
|
+
{ style: { marginTop: 4, fontFamily: 'monospace', fontSize: 11, color: '#9fd8ff' } },
|
|
2753
|
+
'dsh plugin add dsh-xingxi',
|
|
2754
|
+
),
|
|
2755
|
+
React.createElement('div', { style: { marginTop: 4, opacity: 0.75 } }, '装完侧栏会多一个「星汐」。'),
|
|
2756
|
+
)
|
|
2757
|
+
: null,
|
|
2555
2758
|
),
|
|
2556
2759
|
// ⭐⭐ 2026-09-22(她要的):**批量删除** —— 勾了几条就显示几条
|
|
2557
2760
|
// ⚠ 二次确认:点这里**不删**,只出确认条;确认条上再点「确定删」才真删 ✓
|
|
@@ -3881,6 +4084,123 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
3881
4084
|
})
|
|
3882
4085
|
}
|
|
3883
4086
|
|
|
4087
|
+
// ⭐ 2026-11-04:工作区模式下,称呼/自称按**工作区**改(同一个工作区共用一份记忆)
|
|
4088
|
+
// 打开工作区模式就整块展开,不再折在「改」后面
|
|
4089
|
+
const 工作区一格 = (标签, 节点, 说明) =>
|
|
4090
|
+
React.createElement(
|
|
4091
|
+
'div',
|
|
4092
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 } },
|
|
4093
|
+
React.createElement('span', { style: { flex: 'none', width: 110, fontSize: 13 } }, 标签),
|
|
4094
|
+
节点,
|
|
4095
|
+
React.createElement('span', { style: Object.assign({}, S.dim, { fontSize: 12, flex: 1, lineHeight: 1.5 }) }, 说明),
|
|
4096
|
+
)
|
|
4097
|
+
const 工作区块 =
|
|
4098
|
+
d.模式 === 'workspace' && (d.工作区们 || []).length
|
|
4099
|
+
? [
|
|
4100
|
+
React.createElement('div', { key: 'wsh', style: S.h }, '按工作区改'),
|
|
4101
|
+
React.createElement(
|
|
4102
|
+
'div',
|
|
4103
|
+
{ key: 'wst', style: S.dim },
|
|
4104
|
+
'工作区模式下,同一个工作区的会话共用一份记忆 —— 昵称、称呼、人称也跟着共用一份,在这里改一次就够。',
|
|
4105
|
+
),
|
|
4106
|
+
...(d.工作区们 || []).map((g) => {
|
|
4107
|
+
const 取名 = () => (selfDraft[g.键] !== undefined ? selfDraft[g.键] : g.selfName || '')
|
|
4108
|
+
const 取称呼 = () => (ownerDraft[g.键] !== undefined ? ownerDraft[g.键] : g.ownerName || '')
|
|
4109
|
+
const 取人称 = () => (称草稿[g.键] !== undefined ? 称草稿[g.键] : g.pronoun || 'ta')
|
|
4110
|
+
const 取自称 = () => (自草稿[g.键] !== undefined ? 自草稿[g.键] : g.selfPronoun || '我')
|
|
4111
|
+
return React.createElement(
|
|
4112
|
+
'div',
|
|
4113
|
+
{ key: 'ws-' + g.键, style: Object.assign({}, S.条, { display: 'block', padding: '8px 0' }) },
|
|
4114
|
+
React.createElement(
|
|
4115
|
+
'div',
|
|
4116
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 } },
|
|
4117
|
+
React.createElement('span', { style: { fontSize: 13, fontWeight: 600 } }, g.名 || g.工作区),
|
|
4118
|
+
React.createElement('span', { style: S.dim, title: g.路径 || '' }, (g.会话 || []).length + ' 间会话 · ' + (g.条数 || 0) + ' 条记忆'),
|
|
4119
|
+
),
|
|
4120
|
+
React.createElement(
|
|
4121
|
+
'div',
|
|
4122
|
+
{ style: { padding: '10px 12px', borderRadius: 8, border: '1px solid rgba(128,128,128,.22)', background: 'rgba(127,127,127,.05)' } },
|
|
4123
|
+
工作区一格(
|
|
4124
|
+
'它自己叫什么',
|
|
4125
|
+
React.createElement('input', {
|
|
4126
|
+
style: Object.assign({}, S.inp, { width: 160 }),
|
|
4127
|
+
placeholder: '它在这儿的名字',
|
|
4128
|
+
value: 取名(),
|
|
4129
|
+
onChange: (e) => setSelfDraft(Object.assign({}, selfDraft, { [g.键]: e.target.value })),
|
|
4130
|
+
}),
|
|
4131
|
+
'记忆里,它的名字会换成「我」',
|
|
4132
|
+
),
|
|
4133
|
+
工作区一格(
|
|
4134
|
+
'它怎么称呼你',
|
|
4135
|
+
React.createElement('input', {
|
|
4136
|
+
style: Object.assign({}, S.inp, { width: 160 }),
|
|
4137
|
+
placeholder: '它平时怎么叫你(小名、昵称都行)',
|
|
4138
|
+
value: 取称呼(),
|
|
4139
|
+
onChange: (e) => setOwnerDraft(Object.assign({}, ownerDraft, { [g.键]: e.target.value })),
|
|
4140
|
+
}),
|
|
4141
|
+
'记忆里,这个叫法会换成「你」',
|
|
4142
|
+
),
|
|
4143
|
+
工作区一格(
|
|
4144
|
+
'记忆里指对方用',
|
|
4145
|
+
React.createElement('input', {
|
|
4146
|
+
style: Object.assign({}, S.inp, { width: 90 }),
|
|
4147
|
+
placeholder: 'ta',
|
|
4148
|
+
value: 取人称(),
|
|
4149
|
+
onChange: (e) => set称草稿(Object.assign({}, 称草稿, { [g.键]: e.target.value })),
|
|
4150
|
+
}),
|
|
4151
|
+
'ta / 她 / 他 —— 默认 ta',
|
|
4152
|
+
),
|
|
4153
|
+
工作区一格(
|
|
4154
|
+
'它自称什么',
|
|
4155
|
+
React.createElement('input', {
|
|
4156
|
+
style: Object.assign({}, S.inp, { width: 90 }),
|
|
4157
|
+
placeholder: '我',
|
|
4158
|
+
value: 取自称(),
|
|
4159
|
+
onChange: (e) => set自草稿(Object.assign({}, 自草稿, { [g.键]: e.target.value })),
|
|
4160
|
+
}),
|
|
4161
|
+
'它平时自称什么(本小姐、在下…),默认「我」',
|
|
4162
|
+
),
|
|
4163
|
+
React.createElement(
|
|
4164
|
+
'div',
|
|
4165
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 10, marginTop: 4 } },
|
|
4166
|
+
React.createElement(
|
|
4167
|
+
'button',
|
|
4168
|
+
{
|
|
4169
|
+
style: S.btn,
|
|
4170
|
+
onClick: () =>
|
|
4171
|
+
post('/names', {
|
|
4172
|
+
session: g.键,
|
|
4173
|
+
selfName: 取名(),
|
|
4174
|
+
ownerName: 取称呼(),
|
|
4175
|
+
pronoun: 取人称() || 'ta',
|
|
4176
|
+
selfPronoun: 取自称() || '我',
|
|
4177
|
+
}).then((r) => {
|
|
4178
|
+
setSelfDraft({})
|
|
4179
|
+
setOwnerDraft({})
|
|
4180
|
+
set称草稿({})
|
|
4181
|
+
set自草稿({})
|
|
4182
|
+
if (r && r.称呼影响几条 > 0 && !r.称呼已改) {
|
|
4183
|
+
setPronounAsk({ n: r.称呼影响几条, to: r.称呼 || '', 例子: r.称呼例子 || [] })
|
|
4184
|
+
} else {
|
|
4185
|
+
setPronounAsk(null)
|
|
4186
|
+
}
|
|
4187
|
+
load()
|
|
4188
|
+
}),
|
|
4189
|
+
},
|
|
4190
|
+
'存',
|
|
4191
|
+
),
|
|
4192
|
+
React.createElement(
|
|
4193
|
+
'span',
|
|
4194
|
+
{ style: Object.assign({}, S.dim, { fontSize: 12, flex: 1, lineHeight: 1.5 }) },
|
|
4195
|
+
'这个工作区里所有会话一起生效;已有的记忆也会顺手洗一遍。',
|
|
4196
|
+
),
|
|
4197
|
+
),
|
|
4198
|
+
),
|
|
4199
|
+
)
|
|
4200
|
+
}),
|
|
4201
|
+
]
|
|
4202
|
+
: []
|
|
4203
|
+
|
|
3884
4204
|
const blocks = []
|
|
3885
4205
|
for (const [gid, gname, gtip] of GROUPS) {
|
|
3886
4206
|
const rows = spec.filter((s) => s.group === gid)
|
|
@@ -3918,36 +4238,11 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
3918
4238
|
{ style: S.dim },
|
|
3919
4239
|
'装好就是开着的 —— 每个会话都能记、也能读。想给某个会话单独关掉,点它右边那个「关」。每个会话一份记忆,淡了只是不容易被想起来,不会删。',
|
|
3920
4240
|
),
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
style: Object.assign({}, S.inp, { width: 90 }),
|
|
3927
|
-
placeholder: 'ta / 她 / 他',
|
|
3928
|
-
value: pronounDraft !== null ? pronounDraft : d.userPronoun || 'ta',
|
|
3929
|
-
onChange: (e) => setPronounDraft(e.target.value),
|
|
3930
|
-
}),
|
|
3931
|
-
React.createElement(
|
|
3932
|
-
'button',
|
|
3933
|
-
{
|
|
3934
|
-
style: S.btn,
|
|
3935
|
-
onClick: () => {
|
|
3936
|
-
const p = pronounDraft !== null ? pronounDraft : d.userPronoun || 'ta'
|
|
3937
|
-
post('/names', { userPronoun: p }).then((r) => {
|
|
3938
|
-
setPronounDraft(null)
|
|
3939
|
-
// 影响到了旧记忆就先问一句,不偷偷改 ✓
|
|
3940
|
-
if (r && r.称呼影响几条 > 0 && !r.称呼已改) setPronounAsk({ n: r.称呼影响几条, to: p, 例子: r.称呼例子 || [] })
|
|
3941
|
-
else setPronounAsk(null)
|
|
3942
|
-
load()
|
|
3943
|
-
})
|
|
3944
|
-
},
|
|
3945
|
-
},
|
|
3946
|
-
'存',
|
|
3947
|
-
),
|
|
3948
|
-
React.createElement('span', { style: Object.assign({}, S.dim, { fontSize: 12, flex: 1 }) }, '记忆里提到你时用哪个字。默认「ta」;设成「她」或「他」就按你设的记。'),
|
|
3949
|
-
),
|
|
3950
|
-
pronounAsk
|
|
4241
|
+
// ⭐ 工作区模式:按工作区改称呼(同一个工作区共用一份记忆)
|
|
4242
|
+
...工作区块,
|
|
4243
|
+
// ⭐ 2026-11-04:这两格搬到「每个会话」那张卡里去了 ——
|
|
4244
|
+
// 一般人都是一间会话一个对象,吊在外面既不好找,也跟会话卡里的说法对不上。
|
|
4245
|
+
pronounAsk && !open
|
|
3951
4246
|
? React.createElement(
|
|
3952
4247
|
'div',
|
|
3953
4248
|
{ style: S.askBox },
|
|
@@ -3964,7 +4259,7 @@ const 洗键进度 = { 已: 0, 总: 0, 在跑: false, 干跑: false }
|
|
|
3964
4259
|
{
|
|
3965
4260
|
style: S.btn,
|
|
3966
4261
|
onClick: () =>
|
|
3967
|
-
post('/names', {
|
|
4262
|
+
post('/names', { session: pronounAsk.session || '', pronoun: pronounAsk.to, applyToExisting: true }).then(() => {
|
|
3968
4263
|
setPronounAsk(null)
|
|
3969
4264
|
load()
|
|
3970
4265
|
}),
|
package/lib/index.js
CHANGED
|
@@ -78,9 +78,31 @@ export function 工作区目录名(p) {
|
|
|
78
78
|
return '--' + 可读.slice(0, 251) + '--'
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
/**
|
|
81
|
+
/** 工作区的可读名:DSH 自己存着「工作区 id → path / title」,拿它换个看得懂的名字 */
|
|
82
|
+
function 工作区信息(键) {
|
|
83
|
+
const k = String(键 || '')
|
|
84
|
+
if (!k) return { 名: '(未分组)', 路径: '' }
|
|
85
|
+
try {
|
|
86
|
+
const p = join(CFG_DIR, 'storages', 'workspace.json')
|
|
87
|
+
const d = JSON.parse(readFileSync(p, 'utf8'))
|
|
88
|
+
const 表 = (d && d.tables && d.tables.workspaces) || {}
|
|
89
|
+
const 本区会话 = 扫会话目录().byWorkspace.get(k) || []
|
|
90
|
+
for (const w of Object.values(表)) {
|
|
91
|
+
const ids = w && Array.isArray(w.sessionIds) ? w.sessionIds : []
|
|
92
|
+
if (!ids.length) continue
|
|
93
|
+
if (!本区会话.some((s) => ids.includes(s))) continue
|
|
94
|
+
const 路径 = String(w.path || '')
|
|
95
|
+
const 段 = 路径.split(/[\\/]/).filter(Boolean)
|
|
96
|
+
// 名字取最后两段(「上一级\当前目录」这种),只有一段就那一段;完整路径放在 title 里
|
|
97
|
+
const 名 = 段.length >= 2 ? 段.slice(-2).join('\\') : 段[0] || String(w.title || '')
|
|
98
|
+
if (名) return { 名, 路径 }
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {}
|
|
101
|
+
return { 名: String(k).replace(/^--/, '').replace(/--$/, '') || '(未分组)', 路径: '' }
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
function 工作区可读名(键) {
|
|
83
|
-
return
|
|
105
|
+
return 工作区信息(键).名
|
|
84
106
|
}
|
|
85
107
|
|
|
86
108
|
const DAY = 86400000
|
|
@@ -834,9 +856,12 @@ function migrateSwitch() {
|
|
|
834
856
|
// 记的时候、注入的时候,都把「她自己的名字」换成「我」✓
|
|
835
857
|
|
|
836
858
|
// 她自己叫什么(每个会话一个)。没设 = 不替换。
|
|
859
|
+
// ⭐ 1.5.4:工作区模式下按工作区取(同一个工作区的会话共用一份),老数据仍按会话 id 存着,两种都认。
|
|
837
860
|
function selfNameOf(sid) {
|
|
838
861
|
const c = loadCfg()
|
|
839
|
-
const
|
|
862
|
+
const 表 = c.selfNames && typeof c.selfNames === 'object' ? c.selfNames : {}
|
|
863
|
+
const k = 人称键(sid)
|
|
864
|
+
const n = (k && 表[k]) || 表[String(sid)]
|
|
840
865
|
return typeof n === 'string' ? n.trim() : ''
|
|
841
866
|
}
|
|
842
867
|
|
|
@@ -855,7 +880,9 @@ function titleNameOf(ctx, sid) {
|
|
|
855
880
|
// 没设就退回全局的 userPronoun(默认 ta)。
|
|
856
881
|
function ownerNameOf(sid) {
|
|
857
882
|
const c = loadCfg()
|
|
858
|
-
const
|
|
883
|
+
const 表 = c.ownerNames && typeof c.ownerNames === 'object' ? c.ownerNames : {}
|
|
884
|
+
const k = 人称键(sid)
|
|
885
|
+
const n = (k && 表[k]) || 表[String(sid)]
|
|
859
886
|
return typeof n === 'string' ? n.trim() : ''
|
|
860
887
|
}
|
|
861
888
|
|
|
@@ -866,6 +893,60 @@ function userPronoun() {
|
|
|
866
893
|
return p || 'ta'
|
|
867
894
|
}
|
|
868
895
|
|
|
896
|
+
// ⭐ 它自己自称什么:默认「我」。
|
|
897
|
+
// 跑角色设定比较重的模型时,它会用「本小姐」「在下」这类自称 ——
|
|
898
|
+
// 记忆里跟着它自己的说法写,比一律写「我」更像它自己。
|
|
899
|
+
function selfPronoun() {
|
|
900
|
+
const c = loadCfg()
|
|
901
|
+
const p = typeof c.selfPronoun === 'string' ? c.selfPronoun.trim() : ''
|
|
902
|
+
return p || '我'
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// ⭐ 2026-11-04:这两样也能按会话设(工作区模式按工作区设)。
|
|
906
|
+
// 一般人都是一间会话一个对象,所以默认就跟着会话走;没设过才落回全局那个值。
|
|
907
|
+
function 人称键(sid) {
|
|
908
|
+
const s = String(sid || '')
|
|
909
|
+
if (!s) return ''
|
|
910
|
+
try {
|
|
911
|
+
return String(库名(s, { 模式: 读模式() }) || s)
|
|
912
|
+
} catch (error) {
|
|
913
|
+
return s
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// 记忆里指对方用哪个字(ta / 她 / 他)
|
|
918
|
+
function pronounOf(sid) {
|
|
919
|
+
const c = loadCfg()
|
|
920
|
+
const k = 人称键(sid)
|
|
921
|
+
const v = k && c.pronouns && typeof c.pronouns === 'object' ? c.pronouns[k] : ''
|
|
922
|
+
return (typeof v === 'string' && v.trim()) || userPronoun()
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// 记忆里它自己自称什么(我 / 本小姐 / 在下……)
|
|
926
|
+
function selfPronounOf(sid) {
|
|
927
|
+
const c = loadCfg()
|
|
928
|
+
const k = 人称键(sid)
|
|
929
|
+
const v = k && c.selfPronouns && typeof c.selfPronouns === 'object' ? c.selfPronouns[k] : ''
|
|
930
|
+
return (typeof v === 'string' && v.trim()) || selfPronoun()
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// 提示词里凡是拿「我」写它自己的地方,换成它自己的自称(「我们」不算)
|
|
934
|
+
function 换自称成(文, 自称) {
|
|
935
|
+
const s = String(自称 || '').trim()
|
|
936
|
+
if (!s || s === '我') return 文
|
|
937
|
+
return String(文 || '').replace(/我(?!们)/g, s)
|
|
938
|
+
}
|
|
939
|
+
function 换自称(文) {
|
|
940
|
+
return 换自称成(文, selfPronoun())
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// 提示词里拿「她」写对方的地方,换成这个会话设的那个字(ta / 她 / 他)
|
|
944
|
+
function 换称呼(文, 称呼) {
|
|
945
|
+
const s = String(称呼 || '').trim()
|
|
946
|
+
if (!s || s === '她') return 文
|
|
947
|
+
return String(文 || '').split('她').join(s)
|
|
948
|
+
}
|
|
949
|
+
|
|
869
950
|
// 这些词不能当名字换(不然代词会被换坏)
|
|
870
951
|
const NOT_A_NAME = new Set(['她', '他', '我', '你', 'ta', 'TA', 'Ta', 'tA', '用户', '助手', 'AI', 'ai', '人', '自己'])
|
|
871
952
|
|
|
@@ -889,7 +970,7 @@ function normalizeOwner(text, sid) {
|
|
|
889
970
|
}
|
|
890
971
|
|
|
891
972
|
function normalizePerson(text, name) {
|
|
892
|
-
return normalizePersonAs(text, name,
|
|
973
|
+
return normalizePersonAs(text, name, selfPronoun())
|
|
893
974
|
}
|
|
894
975
|
|
|
895
976
|
// 同一套「认边界」的替换:换成「我」(她自己)还是「你」(用户),由调用方定
|
|
@@ -920,14 +1001,23 @@ function normalizePersonAs(text, name, 换成) {
|
|
|
920
1001
|
|
|
921
1002
|
// 用户的称呼改了,**已有记忆里那个字也要跟着变** ✓
|
|
922
1003
|
// ⚠ 两种不能动:「其他」「其它」里的「他」不是称呼;英文单词里的 ta 也别切 ✗
|
|
923
|
-
|
|
1004
|
+
// ⚠ 只换「旧称呼」本身(config 里那个值,默认 ta)。
|
|
1005
|
+
// 「他 / 她」**不是称呼**,是代词 —— 书名、小说人物、引用里的他/她都不能动。
|
|
1006
|
+
// (2026-11-04 修:以前把它们也当称呼,改一次称呼就把《没有人给他写信的上校》改成
|
|
1007
|
+
// 《没有人给老猫写信的上校》,把小说人物也换成用户的名字,而且是直接写盘的。)
|
|
924
1008
|
const ASCII_LETTER = /[a-zA-Z]/
|
|
925
1009
|
|
|
926
|
-
|
|
1010
|
+
const __旧称呼表 = (旧称呼) => {
|
|
1011
|
+
const 旧 = String(旧称呼 || 'ta')
|
|
1012
|
+
return [...new Set([旧, 旧.toLowerCase(), 旧.toUpperCase()])]
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function changeUserPronoun(text, to, 旧称呼 = 'ta') {
|
|
927
1016
|
let t = String(text || '')
|
|
928
1017
|
if (!to) return t
|
|
929
|
-
for (const from of
|
|
930
|
-
if (from === to || !t.includes(from)) continue
|
|
1018
|
+
for (const from of __旧称呼表(旧称呼)) {
|
|
1019
|
+
if (!from || from === to || !t.includes(from)) continue
|
|
1020
|
+
const 是英文 = /^[a-zA-Z]+$/.test(from)
|
|
931
1021
|
let out = ''
|
|
932
1022
|
let i = 0
|
|
933
1023
|
for (;;) {
|
|
@@ -938,9 +1028,8 @@ function changeUserPronoun(text, to) {
|
|
|
938
1028
|
}
|
|
939
1029
|
const 前 = j > 0 ? t[j - 1] : ''
|
|
940
1030
|
const 后 = t[j + from.length] || ''
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
if (/^[a-zA-Z]+$/.test(from) && (ASCII_LETTER.test(前) || ASCII_LETTER.test(后))) 别动 = true // 别切英文单词
|
|
1031
|
+
// 英文/拼音的 ta 别切单词内部;中文称呼直接换
|
|
1032
|
+
const 别动 = 是英文 && (ASCII_LETTER.test(前) || ASCII_LETTER.test(后))
|
|
944
1033
|
out += t.slice(i, j) + (别动 ? from : to)
|
|
945
1034
|
i = j + from.length
|
|
946
1035
|
}
|
|
@@ -949,6 +1038,7 @@ function changeUserPronoun(text, to) {
|
|
|
949
1038
|
return t
|
|
950
1039
|
}
|
|
951
1040
|
|
|
1041
|
+
|
|
952
1042
|
// ---------- 旧档案导入 ----------
|
|
953
1043
|
// 设计参考 AAAAGENT 的「导入旧聊天」,几条原则照抄:
|
|
954
1044
|
// ① 原日期保留(猜不到就不写,绝不编)② 助手的话只作语境、不当事实
|
|
@@ -1308,7 +1398,7 @@ function 换用户名字(text, 名字, to) {
|
|
|
1308
1398
|
// 正文过一遍人称:她自己 →「我」,用户的名字 → 称呼(跟记的时候同一套规矩)
|
|
1309
1399
|
function 导入时改人称(text, sid, 用户名字们) {
|
|
1310
1400
|
let t = normalizePerson(text, selfNameOf(sid))
|
|
1311
|
-
const p =
|
|
1401
|
+
const p = pronounOf(sid)
|
|
1312
1402
|
for (const n of 用户名字们) {
|
|
1313
1403
|
if (!n || n.length < 2) continue
|
|
1314
1404
|
t = 换用户名字(t, n, p)
|
|
@@ -1671,7 +1761,7 @@ async function 自动扩词(ctx, sid, 上限 = 2) {
|
|
|
1671
1761
|
if (!agent) { diag('自动扩词:' + sid + ' 这会儿没有活着的 agent,跳过'); return 0 }
|
|
1672
1762
|
const 输入 = 要扩.map((m, k) => k + 1 + '|' + String(m.text || '')).join('\n')
|
|
1673
1763
|
const t0 = Date.now()
|
|
1674
|
-
const r = await 问模型(ctx, agent, 扩词系统_v2, 输入, 6000)
|
|
1764
|
+
const r = await 问模型(ctx, agent, 换自称成(换称呼(扩词系统_v2, pronounOf(sid)), selfPronounOf(sid)), 输入, 6000)
|
|
1675
1765
|
if (!r || !r.ok) { diag('自动扩词:模型没回(' + ((r && r.message) || '?') + ')'); return 0 }
|
|
1676
1766
|
const 表 = 解析扩词(r.text, 要扩.length)
|
|
1677
1767
|
// ⚠⚠ 2026-10-24 修(用户报「记忆静默丢失」的**真凶**就在这几行):
|
|
@@ -1803,12 +1893,16 @@ function 提炼系统(称呼) {
|
|
|
1803
1893
|
// ⭐⭐ 2026-09-22(反馈):这份清单**漏了「重要的日子」** ——
|
|
1804
1894
|
// 实测 135 条该「永远不淡」的被标成会淡,重要的日子就在里面。
|
|
1805
1895
|
// 原因就是这里没写「生日/纪念日/结婚」,模型不知道那也算「要一直记得的」✗
|
|
1806
|
-
//
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1896
|
+
// ⚠ 措辞尽量短(「提示词、关键词得写得好一点」,token 也是钱)
|
|
1897
|
+
// ⭐ 2026-11-04 修(反馈):这一格说的是「要不要每轮都带上」,不是「重不重要」。
|
|
1898
|
+
// 以前写成「重要 = 1」,模型就把「爱读什么书」「玩什么游戏」也打 1,
|
|
1899
|
+
// 常驻位只有 2~3 个,被日常喜好占满,真正该一直挂着的(忌讳、约定)反而进不来。
|
|
1900
|
+
' 1 = **每轮都要挂在她眼前、不主动提就会出事**的(称呼和忌讳、明确的约定和承诺、' +
|
|
1901
|
+
'身体禁忌与过敏、重要的日子:生日、纪念日、结婚、第一次)\n' +
|
|
1902
|
+
' 0.5 = 其余全部(性格、喜好、书单、游戏、发生过的事、当时的想法)—— **这些不需要每轮在场**,' +
|
|
1903
|
+
'聊到了自然会被搜回来\n' +
|
|
1810
1904
|
' 拿不准就填 0.5。这一格**必须填**,不许留空、不许写别的数。\n' +
|
|
1811
|
-
' ⚠ 填 1
|
|
1905
|
+
' ⚠ 填 1 的会**每轮固定注入**(位置很少,只有两三个),填 0.5 的按需检索 —— 别把喜好占进去。\n' +
|
|
1812
1906
|
// ⭐⭐ 2026-09-23(反馈第二点):**事实键** —— 「同一个 key 只留最新一条」✓
|
|
1813
1907
|
// 根子:句子像不像**本来就不该拿来判「是不是同一件事」**。
|
|
1814
1908
|
// 「她身高一米七」和「她长胖到 55 公斤」几乎是两件事,词面上却很像;
|
|
@@ -2184,8 +2278,12 @@ export function 补线索(query, 上文们, 最短 = 6, 上限 = 240) {
|
|
|
2184
2278
|
export function 选查询词(会话里那句, 监听那句) {
|
|
2185
2279
|
const 会话 = String(会话里那句 == null ? '' : 会话里那句).trim()
|
|
2186
2280
|
const 监听 = String(监听那句 == null ? '' : 监听那句).trim()
|
|
2187
|
-
|
|
2188
|
-
|
|
2281
|
+
// ⚠ 2026-11-04 修(反馈:检索永远慢一轮):
|
|
2282
|
+
// systemPrompt.context 的 text() 是在**请求装配时**求值的,而这一轮的 user 消息
|
|
2283
|
+
// 要等装配完才落进 session.snapshotEvents()。所以「会话里最后一句」拿到的是**上一轮**。
|
|
2284
|
+
// 监听 agent/inbox/inserted 拿到的 lastText 才是这一轮真话,必须优先。
|
|
2285
|
+
if (监听) return 监听
|
|
2286
|
+
return 会话
|
|
2189
2287
|
}
|
|
2190
2288
|
|
|
2191
2289
|
/**
|
|
@@ -3552,7 +3650,9 @@ function makeRememberTool(ctx) {
|
|
|
3552
3650
|
},
|
|
3553
3651
|
importance: {
|
|
3554
3652
|
type: 'number',
|
|
3555
|
-
description:
|
|
3653
|
+
description:
|
|
3654
|
+
'**要不要每轮都挂在她眼前**(不是「重不重要」):1 = 不主动提就会出事的(称呼忌讳、约定承诺、' +
|
|
3655
|
+
'身体禁忌、生日纪念日);0.5 = 其余全部(性格、喜好、书单、游戏、发生过的事)。拿不准写 0.5。',
|
|
3556
3656
|
},
|
|
3557
3657
|
tags: {
|
|
3558
3658
|
type: 'array',
|
|
@@ -3931,9 +4031,16 @@ function buildMemoryList(ctx) {
|
|
|
3931
4031
|
out.sessions.push({
|
|
3932
4032
|
id: sid,
|
|
3933
4033
|
title: titleOfSession(ctx, sid),
|
|
4034
|
+
// ⭐ 1.5.4:工作区模式下,设置页按「工作区」分组改 —— 这两个字段给它用
|
|
4035
|
+
工作区: 会话的工作区(sid) || '',
|
|
4036
|
+
工作区名: 会话的工作区(sid) ? 工作区可读名(会话的工作区(sid)) : '',
|
|
4037
|
+
工作区路径: 会话的工作区(sid) ? 工作区信息(会话的工作区(sid)).路径 : '',
|
|
3934
4038
|
// ⭐ 1.2.9:这个会话里「怎么称呼对方」+「她自己叫什么」,设置页两个输入框要回显
|
|
3935
4039
|
ownerName: ownerNameOf(sid),
|
|
3936
4040
|
selfName: selfNameOf(sid),
|
|
4041
|
+
// ⭐ 1.5.5:这个会话(工作区模式按工作区)记忆里用哪个人称
|
|
4042
|
+
pronoun: pronounOf(sid),
|
|
4043
|
+
selfPronoun: selfPronounOf(sid),
|
|
3937
4044
|
enabled: isEnabled(sid),
|
|
3938
4045
|
live: liveIds.has(sid),
|
|
3939
4046
|
parent: 分支来处Of(ctx, sid),
|
|
@@ -4018,6 +4125,30 @@ function buildMemoryList(ctx) {
|
|
|
4018
4125
|
out.defaults = { ...DEFAULT_POLICY }
|
|
4019
4126
|
out.version = VERSION
|
|
4020
4127
|
out.userPronoun = userPronoun()
|
|
4128
|
+
out.selfPronoun = selfPronoun()
|
|
4129
|
+
// ⭐ 1.5.4:工作区模式下,设置页要按「工作区」单独改 —— 给它一份工作区清单
|
|
4130
|
+
try {
|
|
4131
|
+
if (out.模式 === 'workspace') {
|
|
4132
|
+
const 组 = new Map()
|
|
4133
|
+
for (const s of out.sessions) {
|
|
4134
|
+
const k = s.工作区 || ''
|
|
4135
|
+
if (!k) continue
|
|
4136
|
+
if (!组.has(k)) 组.set(k, { 键: 'ws_' + k, 工作区: k, 名: s.工作区名 || k, 路径: s.工作区路径 || '', 会话: [], 条数: 0 })
|
|
4137
|
+
const g = 组.get(k)
|
|
4138
|
+
g.会话.push(s.id)
|
|
4139
|
+
g.条数 = Math.max(g.条数, Number(s.count) || 0)
|
|
4140
|
+
}
|
|
4141
|
+
out.工作区们 = [...组.values()].map((g) => ({
|
|
4142
|
+
...g,
|
|
4143
|
+
selfName: selfNameOf('ws_' + g.工作区),
|
|
4144
|
+
ownerName: ownerNameOf('ws_' + g.工作区),
|
|
4145
|
+
pronoun: pronounOf('ws_' + g.工作区),
|
|
4146
|
+
selfPronoun: selfPronounOf('ws_' + g.工作区),
|
|
4147
|
+
}))
|
|
4148
|
+
}
|
|
4149
|
+
} catch (error) {
|
|
4150
|
+
diag('工作区清点失败:' + ((error && error.message) || error))
|
|
4151
|
+
}
|
|
4021
4152
|
return out
|
|
4022
4153
|
}
|
|
4023
4154
|
|
|
@@ -4241,13 +4372,13 @@ export function apply(ctx) {
|
|
|
4241
4372
|
// (这里会把用户设的「称呼」拼进去,称呼里带 {{ }} 一样会炸)
|
|
4242
4373
|
interpolate: false,
|
|
4243
4374
|
text: () => {
|
|
4244
|
-
const p = ownerNameOf(sid) ||
|
|
4375
|
+
const p = ownerNameOf(sid) || pronounOf(sid)
|
|
4245
4376
|
const self = selfNameOf(sid)
|
|
4246
4377
|
return (
|
|
4247
4378
|
'【记忆】这一轮如果有值得长期记住的事(' + p + '重要的话、约定、' + p + '的喜好、' + p + '的状态),' +
|
|
4248
4379
|
'直接调用 remember 工具记下来 —— **不用等' + p + '提醒**。' +
|
|
4249
4380
|
'日常琐事(吃了什么、今天天气)不用记。' +
|
|
4250
|
-
'
|
|
4381
|
+
'**人称**:你自己是「' + selfPronounOf(sid) + '」,用户是「' + p + '」,别人和别的 AI 直接写名字' +
|
|
4251
4382
|
(self ? '(你写成「' + self + '」也会自动换成「我」)' : '') +
|
|
4252
4383
|
';也不要写「今天/昨天」,时间交给系统。'
|
|
4253
4384
|
)
|
|
@@ -4287,8 +4418,11 @@ export function apply(ctx) {
|
|
|
4287
4418
|
text: () => {
|
|
4288
4419
|
try {
|
|
4289
4420
|
const now = Date.now()
|
|
4290
|
-
//
|
|
4291
|
-
//
|
|
4421
|
+
// ⚠ 2026-11-04 修:**监听那句(这一轮刚收到的)优先**。
|
|
4422
|
+
// 原来的注释假设「会话里最后一条 user 消息就是这一轮」,在 DSH 这个时序下不成立:
|
|
4423
|
+
// systemPrompt.context 在请求装配时求值,而 user 消息要等装配完才落进 session,
|
|
4424
|
+
// 于是永远拿到上一轮 —— 实测问「莱拉是谁」,拿去检索的是前几轮那句 49 字的闲聊。
|
|
4425
|
+
// 会话那句只在监听为空时兜底(插件中途挂上、重启后第一轮还没收到消息)。
|
|
4292
4426
|
let query = 清掉注入上下文(选查询词(lastUserTextOf(agent.session), lastText))
|
|
4293
4427
|
// ⭐ 1.4.2:一两个字的句子(「推」「在吗」)单独去搜什么都搜不到 —— 借上文一起搜
|
|
4294
4428
|
try {
|
|
@@ -5076,10 +5210,11 @@ export function apply(ctx) {
|
|
|
5076
5210
|
'你是记忆整理助手。下面每条是一条记忆,格式是「编号|内容」。\n' +
|
|
5077
5211
|
'请判断每条**该不该一直记得**(永远不淡),只回两档。\n' +
|
|
5078
5212
|
'\n' +
|
|
5079
|
-
'
|
|
5080
|
-
'1 =
|
|
5081
|
-
'
|
|
5082
|
-
'0.5 =
|
|
5213
|
+
'判据只有一条:**不主动提,会不会出事?** 常驻位只有两三个。\n' +
|
|
5214
|
+
'1 = **每轮都得在场**:称呼和忌讳(不能叫错、不能碰的)、明确的约定和承诺、\n' +
|
|
5215
|
+
' 身体禁忌与过敏、一直成立的硬事实(名字、家里的结构)。\n' +
|
|
5216
|
+
'0.5 = **其余全部**:性格、喜好、书单、游戏、长期的住处或工作、某天发生的任何事。\n' +
|
|
5217
|
+
' (这些聊到了自然会被搜回来,不需要每轮挂着。)\n' +
|
|
5083
5218
|
'\n' +
|
|
5084
5219
|
'⚠⚠ **以日期开头的(09-17 / 8月26日 / 2026-09-04…),默认一律 0.5。**\n' +
|
|
5085
5220
|
' 哪怕那件事很重要、很感人、是第一次、是求婚、是婚礼 —— 都算「某天发生的」。\n' +
|
|
@@ -5203,7 +5338,7 @@ export function apply(ctx) {
|
|
|
5203
5338
|
const c = loadCfg()
|
|
5204
5339
|
const 改了几条 = []
|
|
5205
5340
|
const 换了称呼的 = []
|
|
5206
|
-
const 称呼旧值 = userPronoun()
|
|
5341
|
+
const 称呼旧值 = b.session ? pronounOf(String(b.session)) : userPronoun()
|
|
5207
5342
|
let 称呼新值 = null
|
|
5208
5343
|
if (typeof b.userPronoun === 'string') {
|
|
5209
5344
|
const p = b.userPronoun.trim()
|
|
@@ -5218,6 +5353,34 @@ export function apply(ctx) {
|
|
|
5218
5353
|
else delete c.archiveDir
|
|
5219
5354
|
diag('档案目录设成:' + (c.archiveDir || '(还没选)'))
|
|
5220
5355
|
}
|
|
5356
|
+
// ⭐ 它自己自称什么(默认「我」)。
|
|
5357
|
+
// 跟会话一起传过来 = 这个会话(工作区模式是这个工作区)的;不带会话 = 全局默认。
|
|
5358
|
+
if (typeof b.selfPronoun === 'string') {
|
|
5359
|
+
const p = b.selfPronoun.trim()
|
|
5360
|
+
const k0 = b.session ? 人称键(String(b.session)) : ''
|
|
5361
|
+
if (k0) {
|
|
5362
|
+
if (!c.selfPronouns || typeof c.selfPronouns !== 'object') c.selfPronouns = {}
|
|
5363
|
+
if (p && p !== '我') c.selfPronouns[k0] = p
|
|
5364
|
+
else delete c.selfPronouns[k0]
|
|
5365
|
+
diag('这个会话的它自称:' + k0 + ' → 「' + (p || '(默认 我)') + '」')
|
|
5366
|
+
} else if (p && p !== '我') {
|
|
5367
|
+
c.selfPronoun = p
|
|
5368
|
+
diag('它的自称设成:' + c.selfPronoun)
|
|
5369
|
+
} else {
|
|
5370
|
+
delete c.selfPronoun
|
|
5371
|
+
diag('它的自称设成:' + '(默认 我)')
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
// ⭐ 记忆里指对方用哪个字,也能按会话设
|
|
5375
|
+
if (b.session && typeof b.pronoun === 'string') {
|
|
5376
|
+
const k0 = 人称键(String(b.session))
|
|
5377
|
+
if (!c.pronouns || typeof c.pronouns !== 'object') c.pronouns = {}
|
|
5378
|
+
const p = b.pronoun.trim()
|
|
5379
|
+
if (p && p !== 'ta') c.pronouns[k0] = p
|
|
5380
|
+
else delete c.pronouns[k0]
|
|
5381
|
+
称呼新值 = p || 'ta'
|
|
5382
|
+
diag('这个会话记忆里指对方用:' + k0 + ' → 「' + (p || '(默认 ta)') + '」')
|
|
5383
|
+
}
|
|
5221
5384
|
if (Array.isArray(b.notNames)) {
|
|
5222
5385
|
c.notNames = b.notNames
|
|
5223
5386
|
.filter((x) => typeof x === 'string' && x.trim())
|
|
@@ -5283,17 +5446,31 @@ export function apply(ctx) {
|
|
|
5283
5446
|
let 称呼影响几条 = 0
|
|
5284
5447
|
let 称呼例子 = []
|
|
5285
5448
|
const 要改 = b.applyToExisting === true
|
|
5286
|
-
const
|
|
5449
|
+
const 会话号 = String(b.session || '')
|
|
5450
|
+
const 目标称呼 = 要改
|
|
5451
|
+
? 会话号
|
|
5452
|
+
? pronounOf(会话号)
|
|
5453
|
+
: userPronoun()
|
|
5454
|
+
: 称呼新值 !== null && 称呼新值 !== 称呼旧值
|
|
5455
|
+
? 称呼新值
|
|
5456
|
+
: null
|
|
5287
5457
|
if (目标称呼 !== null) {
|
|
5288
5458
|
try {
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5459
|
+
// ⭐ 1.5.5:改成**只洗这个会话(工作区模式是这个工作区)的库**。
|
|
5460
|
+
// 以前扫全部库 —— A 会话改个叫法,B 会话的记忆也被一起洗了。
|
|
5461
|
+
const 只洗 = 会话号 ? String(库名(会话号, { 模式: 读模式() }) || 会话号) : ''
|
|
5462
|
+
const 名单 = 只洗
|
|
5463
|
+
? [只洗]
|
|
5464
|
+
: readdirSync(STORE_DIR)
|
|
5465
|
+
.filter((fn) => fn.endsWith('.json'))
|
|
5466
|
+
.map((fn) => fn.replace(/\.json$/, ''))
|
|
5467
|
+
for (const sid of 名单) {
|
|
5468
|
+
const fn = sid + '.json'
|
|
5292
5469
|
const data = loadStore(sid)
|
|
5293
5470
|
let 动 = false
|
|
5294
5471
|
for (const 区 of ['memories', 'deleted']) {
|
|
5295
5472
|
for (const m of data[区] || []) {
|
|
5296
|
-
const 新 = changeUserPronoun(m.text,
|
|
5473
|
+
const 新 = changeUserPronoun(m.text, 目标称呼, 称呼旧值 || 'ta')
|
|
5297
5474
|
if (新 !== m.text) {
|
|
5298
5475
|
称呼影响几条++
|
|
5299
5476
|
if (称呼例子.length < 3) 称呼例子.push({ id: m.id, 旧: m.text, 新: 新 })
|
|
@@ -5304,7 +5481,17 @@ export function apply(ctx) {
|
|
|
5304
5481
|
}
|
|
5305
5482
|
}
|
|
5306
5483
|
}
|
|
5307
|
-
if (要改 && 动)
|
|
5484
|
+
if (要改 && 动) {
|
|
5485
|
+
// ⚠ 改称呼是**永久写盘**,先留一份快照,出问题能一键回退
|
|
5486
|
+
try {
|
|
5487
|
+
const 原文件 = join(STORE_DIR, sid + '.json')
|
|
5488
|
+
if (existsSync(原文件)) {
|
|
5489
|
+
const 时 = new Date().toISOString().replace(/[:.]/g, '-')
|
|
5490
|
+
copyFileSync(原文件, 原文件 + '.bak-person-' + 时)
|
|
5491
|
+
}
|
|
5492
|
+
} catch {}
|
|
5493
|
+
saveStore(sid, data)
|
|
5494
|
+
}
|
|
5308
5495
|
}
|
|
5309
5496
|
} catch (error) {}
|
|
5310
5497
|
if (要改) diag('称呼改成「' + 目标称呼 + '」,顺手改了 ' + 称呼影响几条 + ' 条记忆')
|
|
@@ -5320,6 +5507,7 @@ export function apply(ctx) {
|
|
|
5320
5507
|
userNames: 用户名字列表(),
|
|
5321
5508
|
llmModel: 选定模型(),
|
|
5322
5509
|
洗了: 改了几条.length,
|
|
5510
|
+
称呼: b.session ? pronounOf(String(b.session)) : userPronoun(),
|
|
5323
5511
|
称呼影响几条,
|
|
5324
5512
|
称呼例子,
|
|
5325
5513
|
称呼已改: b.applyToExisting === true && 称呼影响几条 > 0,
|
|
@@ -5714,9 +5902,17 @@ export function apply(ctx) {
|
|
|
5714
5902
|
m.importance = imp
|
|
5715
5903
|
m.category = imp >= 1 ? 'stable_profile' : 'event'
|
|
5716
5904
|
}
|
|
5905
|
+
// ⭐ 检索词能当场改:模型给的词经常不靠谱,人直接改比重新扩一遍快,也不动正文
|
|
5906
|
+
if (Array.isArray(b.related)) {
|
|
5907
|
+
const 词 = b.related.map((x) => String(x || '').trim()).filter(Boolean).slice(0, 12)
|
|
5908
|
+
m.related = 词
|
|
5909
|
+
m.relatedAt = new Date().toISOString()
|
|
5910
|
+
m.relatedBy = '手动'
|
|
5911
|
+
diag('改检索词:' + sid + ' / ' + id + ' → ' + 词.join(' '))
|
|
5912
|
+
}
|
|
5717
5913
|
saveStore(sid, data)
|
|
5718
5914
|
diag('改了一条:' + sid + ' / ' + id + ' 「' + 旧.slice(0, 30) + '」→「' + m.text.slice(0, 30) + '」')
|
|
5719
|
-
out = { ok: true, message: '改好了', text: m.text, importance: m.importance, category: m.category }
|
|
5915
|
+
out = { ok: true, message: '改好了', text: m.text, importance: m.importance, category: m.category, related: Array.isArray(m.related) ? m.related : null }
|
|
5720
5916
|
}
|
|
5721
5917
|
} catch (error) {
|
|
5722
5918
|
out = { ok: false, message: String((error && error.message) || error) }
|
|
@@ -6368,7 +6564,7 @@ export function apply(ctx) {
|
|
|
6368
6564
|
const t0 = Date.now()
|
|
6369
6565
|
// ⚠ 预算要给够:模型会先「思考」,思考的 token 也算在里面。
|
|
6370
6566
|
// 给 2400 的时候实测被思考吃光、正文长度 0、整批被误判成「跳过」✗
|
|
6371
|
-
const r = await 问模型(ctx, agent, 提炼系统(
|
|
6567
|
+
const r = await 问模型(ctx, agent, 换自称成(提炼系统(pronounOf(sid)), selfPronounOf(sid)), 输入, 6000)
|
|
6372
6568
|
const ms = Date.now() - t0
|
|
6373
6569
|
if (!r.ok) {
|
|
6374
6570
|
out = { ok: false, message: r.message, 剩: 要提.length, provider: r.provider, model: r.model, ms: ms }
|
|
@@ -6555,7 +6751,7 @@ mroute('/dsh-fish-memory/wash-keys', async (req, res) => {
|
|
|
6555
6751
|
已有键.length = Math.min(已有键.length, 400)
|
|
6556
6752
|
}
|
|
6557
6753
|
const t0 = Date.now()
|
|
6558
|
-
const r = await 问模型(ctx, agent, 洗键系统(
|
|
6754
|
+
const r = await 问模型(ctx, agent, 换自称成(洗键系统(pronounOf(sid), 已有键), selfPronounOf(sid)), 输入, 6000)
|
|
6559
6755
|
const ms = Date.now() - t0
|
|
6560
6756
|
if (!r.ok) {
|
|
6561
6757
|
out = { ok: false, message: r.message, 剩: 要洗.length, provider: r.provider, model: r.model, ms: ms }
|
|
@@ -6880,7 +7076,7 @@ mroute('/dsh-fish-memory/wash-keys/undo', async (req, res) => {
|
|
|
6880
7076
|
// 实测 6 条的时候:**正文 167 字,思考 2912 字**(思考是答案的 17 倍)——
|
|
6881
7077
|
// 原来只给 900,连思考都不够,正文永远是空的,
|
|
6882
7078
|
// 表现就是「连着几批没回可用的词,自己停了」✗(2026-09-20 踩到)
|
|
6883
|
-
const r = await 问模型(ctx, agent, 扩词系统_v2, 输入, 6000)
|
|
7079
|
+
const r = await 问模型(ctx, agent, 换自称成(换称呼(扩词系统_v2, pronounOf(sid)), selfPronounOf(sid)), 输入, 6000)
|
|
6884
7080
|
const ms = Date.now() - t0
|
|
6885
7081
|
if (!r.ok) {
|
|
6886
7082
|
out = { ok: false, message: r.message, 剩: 要扩.length, provider: r.provider, model: r.model, ms: ms }
|
|
@@ -7041,4 +7237,12 @@ export const __测试 = {
|
|
|
7041
7237
|
库名,
|
|
7042
7238
|
读模式,
|
|
7043
7239
|
合并工作区记忆,
|
|
7240
|
+
// ⭐ 1.5.4:「它自称什么」是纯字符串替换,单独露出来好单测
|
|
7241
|
+
换自称成,
|
|
7242
|
+
换称呼,
|
|
7243
|
+
// ⭐ 1.5.4:人称现在跟着会话走,这两条是「会话 → 库」的取值口
|
|
7244
|
+
人称键,
|
|
7245
|
+
pronounOf,
|
|
7246
|
+
selfPronounOf,
|
|
7247
|
+
自称: () => selfPronoun(),
|
|
7044
7248
|
}
|
package/package.json
CHANGED