@yottameta/yotta-memory 0.19.0 → 0.20.1
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/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/bin/install.js +246 -73
- package/bin/provider.js +1 -0
- package/bin/yotta-memory.js +328 -59
- package/install.sh +216 -40
- package/package.json +1 -1
- package/references/provider-protocol.md +75 -4
- package/skill-manifest.json +1 -1
package/bin/yotta-memory.js
CHANGED
|
@@ -25,7 +25,7 @@ const net = require('net');
|
|
|
25
25
|
const child_process = require('child_process');
|
|
26
26
|
const { AsyncLocalStorage } = require('async_hooks');
|
|
27
27
|
|
|
28
|
-
const VERSION = '0.
|
|
28
|
+
const VERSION = '0.20.1';
|
|
29
29
|
const CLI_VALUE_OPTS = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--agent-id', '--agent-key', '--agent-key-file', '--plugin-data', '--threshold', '--scope', '--host', '--port', '--dir', '--name', '--user', '--relationship', '--source', '--weight', '--budget', '--password', '--new-password', '--recovery-key', '--recovery-key-out', '--reason', '--merge', '--model', '--subject', '--embedding', '--focus', '--embedding-timeout', '--min-age', '--min-idle', '--max-utility', '--min-group', '--period', '--to', '--id', '--time', '--tools', '--mcp-config', '--skill-dir', '--year', '--evalset', '--k', '--seed', '--bootstrap', '--gate', '--against', '--template', '--path', '--from']);
|
|
30
30
|
const CLI_FLAG_OPTS = new Set(['--project', '--all', '--unsafe', '--no-auth', '--stdio', '--onstart', '--from-current', '--restart', '--force', '--attach', '--allow-same-volume', '--verify', '--no-hint', '--encrypt', '--no-encrypt', '--password-stdin', '--json', '--manual', '--skip-schedule', '--useful', '--useless', '--undo', '--dry-run', '--apply', '--purge', '--dedup', '--batches', '--propose', '--audit', '--capacity', '--rules', '--explain', '--semantic', '--runtime', '--ablate', '--timing', '--baseline', '--probe', '--quarantine', '--restore', '--no-usage', '--yes', '--keep-memories', '--keep-identity']);
|
|
31
31
|
|
|
@@ -7460,30 +7460,55 @@ function cmdWhoami(opts) {
|
|
|
7460
7460
|
|
|
7461
7461
|
// ---- 用户画像聚合(v0.6.0,零推断:只归组呈现原文,结论由承载 AI 依据「记忆守则」内部形成)----
|
|
7462
7462
|
function profileGroups(root, owner) {
|
|
7463
|
-
const
|
|
7464
|
-
const map = {};
|
|
7463
|
+
const entries = [];
|
|
7465
7464
|
for (const t of PRIVATE_LEAF) {
|
|
7466
7465
|
const dir = path.join(root, PRIVATE_DIR, owner, t);
|
|
7467
7466
|
if (!fs.existsSync(dir)) continue;
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
if (!fs.statSync(fp).isFile()) continue;
|
|
7467
|
+
const files = [];
|
|
7468
|
+
walkEntryFiles(dir, files);
|
|
7469
|
+
for (const fp of files) {
|
|
7472
7470
|
let e;
|
|
7473
7471
|
try { e = readEntry(fp, root); } catch (err) { continue; }
|
|
7474
|
-
|
|
7475
|
-
if (!map[key]) {
|
|
7476
|
-
map[key] = { type: e.type, subject: e.subject || '', tags: e.tags || [], items: [] };
|
|
7477
|
-
groups.push(map[key]);
|
|
7478
|
-
}
|
|
7479
|
-
map[key].items.push({
|
|
7480
|
-
file: e.file, statement: e.statement, confidence: e.confidence,
|
|
7481
|
-
last_accessed: e.last_accessed, updated: e.updated,
|
|
7482
|
-
});
|
|
7472
|
+
entries.push(e);
|
|
7483
7473
|
}
|
|
7484
7474
|
}
|
|
7475
|
+
return profileGroupsFromEntries(entries);
|
|
7476
|
+
}
|
|
7477
|
+
function profileGroupsFromEntries(entries) {
|
|
7478
|
+
const groups = [];
|
|
7479
|
+
const map = {};
|
|
7480
|
+
for (const e of (entries || [])) {
|
|
7481
|
+
const key = e.type + '\u0000' + (e.subject || '') + '\u0000' + (e.tags || []).join(',');
|
|
7482
|
+
if (!map[key]) {
|
|
7483
|
+
map[key] = { type: e.type, subject: e.subject || '', tags: e.tags || [], items: [] };
|
|
7484
|
+
groups.push(map[key]);
|
|
7485
|
+
}
|
|
7486
|
+
map[key].items.push({
|
|
7487
|
+
file: e.file, statement: e.statement, confidence: e.confidence,
|
|
7488
|
+
last_accessed: e.last_accessed, updated: e.updated,
|
|
7489
|
+
});
|
|
7490
|
+
}
|
|
7485
7491
|
return groups;
|
|
7486
7492
|
}
|
|
7493
|
+
function renderProfileText(owner, groups) {
|
|
7494
|
+
const lines = [];
|
|
7495
|
+
lines.push('# 用户画像(' + owner + ')');
|
|
7496
|
+
lines.push('');
|
|
7497
|
+
lines.push('> 生成: yotta-memory profile | 引擎零推断:以下为私密记忆原文的结构化归组,画像结论由承载 AI 依据「记忆守则」在内部形成,不当面贴标签。');
|
|
7498
|
+
lines.push('> 刷新: ' + today());
|
|
7499
|
+
lines.push('');
|
|
7500
|
+
for (const g of (groups || [])) {
|
|
7501
|
+
lines.push('## ' + g.type + ' · ' + (g.subject || '(无主题)') + (g.tags.length ? ' [tags: ' + g.tags.join(', ') + ']' : ''));
|
|
7502
|
+
lines.push('');
|
|
7503
|
+
for (const it of g.items) {
|
|
7504
|
+
const acc = it.last_accessed ? '最近访问 ' + it.last_accessed : '未访问';
|
|
7505
|
+
lines.push('- ' + it.statement + '(confidence ' + it.confidence + ' · ' + acc + ')');
|
|
7506
|
+
lines.push(' - ' + it.file);
|
|
7507
|
+
}
|
|
7508
|
+
lines.push('');
|
|
7509
|
+
}
|
|
7510
|
+
return lines.join('\n');
|
|
7511
|
+
}
|
|
7487
7512
|
function profileCore(opts) {
|
|
7488
7513
|
opts = opts || {};
|
|
7489
7514
|
const root = userRoot();
|
|
@@ -7511,23 +7536,7 @@ function profileCore(opts) {
|
|
|
7511
7536
|
}
|
|
7512
7537
|
const groups = profileGroups(root, owner);
|
|
7513
7538
|
if (!groups.length) return { error: false, text: '该智能体(' + owner + ')暂无画像:private/' + owner + '/ 下无 PREF / BOUND / COMMIT 条目。' };
|
|
7514
|
-
const
|
|
7515
|
-
lines.push('# 用户画像(' + owner + ')');
|
|
7516
|
-
lines.push('');
|
|
7517
|
-
lines.push('> 生成: yotta-memory profile | 引擎零推断:以下为私密记忆原文的结构化归组,画像结论由承载 AI 依据「记忆守则」在内部形成,不当面贴标签。');
|
|
7518
|
-
lines.push('> 刷新: ' + today());
|
|
7519
|
-
lines.push('');
|
|
7520
|
-
for (const g of groups) {
|
|
7521
|
-
lines.push('## ' + g.type + ' · ' + (g.subject || '(无主题)') + (g.tags.length ? ' [tags: ' + g.tags.join(', ') + ']' : ''));
|
|
7522
|
-
lines.push('');
|
|
7523
|
-
for (const it of g.items) {
|
|
7524
|
-
const acc = it.last_accessed ? '最近访问 ' + it.last_accessed : '未访问';
|
|
7525
|
-
lines.push('- ' + it.statement + '(confidence ' + it.confidence + ' · ' + acc + ')');
|
|
7526
|
-
lines.push(' - ' + it.file);
|
|
7527
|
-
}
|
|
7528
|
-
lines.push('');
|
|
7529
|
-
}
|
|
7530
|
-
const text = lines.join('\n');
|
|
7539
|
+
const text = renderProfileText(owner, groups);
|
|
7531
7540
|
const outFile = writeProfileText(root, owner, text + '\n');
|
|
7532
7541
|
return { error: false, text: text + '\n\n[已写入] ' + outFile };
|
|
7533
7542
|
}
|
|
@@ -7566,6 +7575,25 @@ function compareContextRecent(a, b) {
|
|
|
7566
7575
|
const MEMORY_HOOK_MAX_CANDIDATES = 500;
|
|
7567
7576
|
const MEMORY_HOOK_STATEMENT_LIMIT = 2000;
|
|
7568
7577
|
|
|
7578
|
+
function contextEntryLine(entry) {
|
|
7579
|
+
return '- [' + entry.type + '] ' + entry.subject + ': ' + entry.statement;
|
|
7580
|
+
}
|
|
7581
|
+
|
|
7582
|
+
function isSelfProfileEntry(entry, owner) {
|
|
7583
|
+
if (!entry || entry.type !== 'PREF' || entry.subject !== SELF_PROFILE_SUBJECT) return false;
|
|
7584
|
+
if (owner && entry.owner && entry.owner !== owner) return false;
|
|
7585
|
+
const file = String(entry.file || '').replace(/\\/g, '/');
|
|
7586
|
+
return owner ? file.indexOf('private/' + owner + '/prefs/') === 0 : false;
|
|
7587
|
+
}
|
|
7588
|
+
|
|
7589
|
+
function contextPageableEntries(readableEntries, owner) {
|
|
7590
|
+
return (readableEntries || [])
|
|
7591
|
+
.filter(function (entry) {
|
|
7592
|
+
return entry.type !== 'BOUND' && entry.type !== 'COMMIT' && !isSelfProfileEntry(entry, owner);
|
|
7593
|
+
})
|
|
7594
|
+
.sort(compareContextRecent);
|
|
7595
|
+
}
|
|
7596
|
+
|
|
7569
7597
|
function hookBlock(status, providerId, note) {
|
|
7570
7598
|
return {
|
|
7571
7599
|
status: status || 'not_installed',
|
|
@@ -7590,6 +7618,130 @@ function hookStatusText(hook) {
|
|
|
7590
7618
|
return hook.status;
|
|
7591
7619
|
}
|
|
7592
7620
|
|
|
7621
|
+
function pagingBlock(status, providerId, note) {
|
|
7622
|
+
return {
|
|
7623
|
+
status: status || 'not_installed',
|
|
7624
|
+
provider_id: providerId || '',
|
|
7625
|
+
applied: false,
|
|
7626
|
+
dropped: [],
|
|
7627
|
+
ordered: [],
|
|
7628
|
+
truncated: false,
|
|
7629
|
+
budget: 0,
|
|
7630
|
+
used: 0,
|
|
7631
|
+
note: note || '',
|
|
7632
|
+
};
|
|
7633
|
+
}
|
|
7634
|
+
|
|
7635
|
+
function pagingStatusText(paging) {
|
|
7636
|
+
if (!paging) return '';
|
|
7637
|
+
if (paging.status === 'active') {
|
|
7638
|
+
const who = paging.provider_id ? '提供方 ' + paging.provider_id : '提供方';
|
|
7639
|
+
return '已应用(' + who + ';丢弃 ' + paging.dropped.length + ' / 排序 ' + paging.ordered.length + ')';
|
|
7640
|
+
}
|
|
7641
|
+
if (paging.status === 'license_required') return '需授权(该能力需要授权后使用;普通记忆不受影响)';
|
|
7642
|
+
if (paging.status === 'timeout') return '未生效(提供方超时,已回落普通记忆)';
|
|
7643
|
+
if (paging.status === 'invalid_output') return '未生效(提供方输出无效,已回落普通记忆)';
|
|
7644
|
+
if (paging.status === 'error') return '未生效(提供方异常,已回落普通记忆)';
|
|
7645
|
+
return '';
|
|
7646
|
+
}
|
|
7647
|
+
|
|
7648
|
+
function pagingCandidatePayload(entry, baselineSection) {
|
|
7649
|
+
return {
|
|
7650
|
+
file: entry.file,
|
|
7651
|
+
type: entry.type,
|
|
7652
|
+
subject: entry.subject,
|
|
7653
|
+
statement: String(entry.statement || '').slice(0, MEMORY_HOOK_STATEMENT_LIMIT),
|
|
7654
|
+
created: entry.created || '',
|
|
7655
|
+
updated: entry.updated || '',
|
|
7656
|
+
chars: contextEntryLine(entry).length,
|
|
7657
|
+
baseline_section: baselineSection || 'high_value',
|
|
7658
|
+
};
|
|
7659
|
+
}
|
|
7660
|
+
|
|
7661
|
+
function applyContextPaging(pageable, opts, meta) {
|
|
7662
|
+
opts = opts || {};
|
|
7663
|
+
meta = meta || {};
|
|
7664
|
+
const budget = parseInt(opts.budget || 0, 10) || 0;
|
|
7665
|
+
if (budget <= 0) {
|
|
7666
|
+
return {
|
|
7667
|
+
block: pagingBlock('not_requested', '', '未传 --budget,未调用 context.paging'),
|
|
7668
|
+
drop: new Set(),
|
|
7669
|
+
order: null,
|
|
7670
|
+
applied: false,
|
|
7671
|
+
};
|
|
7672
|
+
}
|
|
7673
|
+
const block = pagingBlock('not_installed', '', '');
|
|
7674
|
+
block.budget = budget;
|
|
7675
|
+
const truncated = pageable.length > MEMORY_HOOK_MAX_CANDIDATES;
|
|
7676
|
+
block.truncated = truncated;
|
|
7677
|
+
const candidates = pageable.slice(0, MEMORY_HOOK_MAX_CANDIDATES);
|
|
7678
|
+
const payload = {
|
|
7679
|
+
agent: opts.owner || '',
|
|
7680
|
+
budget: budget,
|
|
7681
|
+
focus: opts.focus || '',
|
|
7682
|
+
truncated: truncated,
|
|
7683
|
+
protected: Array.isArray(meta.protected) ? meta.protected.slice() : [],
|
|
7684
|
+
sections: meta.sections || {},
|
|
7685
|
+
candidates: candidates.map(function (entry) {
|
|
7686
|
+
return pagingCandidatePayload(entry, (meta.baselineSections || {})[entry.file]);
|
|
7687
|
+
}),
|
|
7688
|
+
};
|
|
7689
|
+
let run;
|
|
7690
|
+
try {
|
|
7691
|
+
run = require('./provider').runCapability('context.paging', payload);
|
|
7692
|
+
} catch (e) {
|
|
7693
|
+
block.status = 'error';
|
|
7694
|
+
block.note = '预算分页失败:' + e.message;
|
|
7695
|
+
return { block: block, drop: new Set(), order: null, applied: false };
|
|
7696
|
+
}
|
|
7697
|
+
block.status = run.status;
|
|
7698
|
+
block.provider_id = run.provider_id || '';
|
|
7699
|
+
block.note = run.note || run.message || '';
|
|
7700
|
+
if (run.status !== 'active' || !run.data || typeof run.data !== 'object') {
|
|
7701
|
+
return { block: block, drop: new Set(), order: null, applied: false };
|
|
7702
|
+
}
|
|
7703
|
+
const sent = new Set(payload.candidates.map(function (item) { return item.file; }));
|
|
7704
|
+
const rejected = [];
|
|
7705
|
+
const dropped = [];
|
|
7706
|
+
for (const item of (Array.isArray(run.data.drop) ? run.data.drop : [])) {
|
|
7707
|
+
const file = String(item || '');
|
|
7708
|
+
if (!file) continue;
|
|
7709
|
+
if (!sent.has(file)) {
|
|
7710
|
+
if (rejected.indexOf(file) === -1) rejected.push(file);
|
|
7711
|
+
continue;
|
|
7712
|
+
}
|
|
7713
|
+
if (dropped.indexOf(file) === -1) dropped.push(file);
|
|
7714
|
+
}
|
|
7715
|
+
let order = null;
|
|
7716
|
+
if (Array.isArray(run.data.order)) {
|
|
7717
|
+
if (run.data.complete === true && !truncated) {
|
|
7718
|
+
const picked = [];
|
|
7719
|
+
for (const item of run.data.order) {
|
|
7720
|
+
const file = String(item || '');
|
|
7721
|
+
if (!file) continue;
|
|
7722
|
+
if (!sent.has(file)) {
|
|
7723
|
+
if (rejected.indexOf(file) === -1) rejected.push(file);
|
|
7724
|
+
continue;
|
|
7725
|
+
}
|
|
7726
|
+
if (picked.indexOf(file) === -1) picked.push(file);
|
|
7727
|
+
}
|
|
7728
|
+
order = picked;
|
|
7729
|
+
} else if (truncated) {
|
|
7730
|
+
block.note = block.note || '候选超过 ' + MEMORY_HOOK_MAX_CANDIDATES + ',order 未应用(保护)';
|
|
7731
|
+
} else {
|
|
7732
|
+
block.note = block.note || 'provider 未声明 complete=true,order 未应用(保护)';
|
|
7733
|
+
}
|
|
7734
|
+
}
|
|
7735
|
+
if (rejected.length) {
|
|
7736
|
+
const text = '候选集外文件已忽略:' + rejected.join(', ');
|
|
7737
|
+
block.note = block.note ? block.note + ';' + text : text;
|
|
7738
|
+
}
|
|
7739
|
+
block.dropped = dropped;
|
|
7740
|
+
block.ordered = order || [];
|
|
7741
|
+
block.applied = dropped.length > 0 || (order !== null && order.length > 0);
|
|
7742
|
+
return { block: block, drop: new Set(dropped), order: order, applied: block.applied };
|
|
7743
|
+
}
|
|
7744
|
+
|
|
7593
7745
|
/**
|
|
7594
7746
|
* memory.hook 装载点:只把「可驱逐」条目(非 BOUND / COMMIT)交给 provider。
|
|
7595
7747
|
* provider 只能返回本次候选集内的 evict(驱逐清单);complete=true 时可选 selected(白名单)。
|
|
@@ -7598,9 +7750,7 @@ function hookStatusText(hook) {
|
|
|
7598
7750
|
*/
|
|
7599
7751
|
function applyMemoryHook(readableEntries, opts) {
|
|
7600
7752
|
const block = hookBlock('not_installed', '', '');
|
|
7601
|
-
const pageable = readableEntries
|
|
7602
|
-
.filter(function (entry) { return entry.type !== 'BOUND' && entry.type !== 'COMMIT'; })
|
|
7603
|
-
.sort(compareContextRecent);
|
|
7753
|
+
const pageable = contextPageableEntries(readableEntries, opts.owner || '');
|
|
7604
7754
|
const truncated = pageable.length > MEMORY_HOOK_MAX_CANDIDATES;
|
|
7605
7755
|
const payload = {
|
|
7606
7756
|
agent: opts.owner || '',
|
|
@@ -7665,6 +7815,25 @@ function applyMemoryHook(readableEntries, opts) {
|
|
|
7665
7815
|
return { block: block, evict: new Set(evicted), selected: selected };
|
|
7666
7816
|
}
|
|
7667
7817
|
|
|
7818
|
+
function replaceContextProfileSection(lines, profileText, fence) {
|
|
7819
|
+
const start = lines.indexOf('## 2. 用户画像摘要');
|
|
7820
|
+
if (start === -1) return false;
|
|
7821
|
+
const marker = lines.indexOf('## 2.5 长期理解摘要');
|
|
7822
|
+
const end = marker === -1 ? lines.length : marker;
|
|
7823
|
+
if (end < start) return false;
|
|
7824
|
+
const block = ['## 2. 用户画像摘要', ''];
|
|
7825
|
+
if (profileText) {
|
|
7826
|
+
block.push(fence + 'markdown');
|
|
7827
|
+
block.push(String(profileText).trim());
|
|
7828
|
+
block.push(fence);
|
|
7829
|
+
} else {
|
|
7830
|
+
block.push('(该身份暂无画像;可运行 yotta-memory profile 生成)');
|
|
7831
|
+
}
|
|
7832
|
+
block.push('');
|
|
7833
|
+
lines.splice.apply(lines, [start, end - start].concat(block));
|
|
7834
|
+
return true;
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7668
7837
|
function contextCore(opts) {
|
|
7669
7838
|
opts = opts || {};
|
|
7670
7839
|
const roots = memoryRoots();
|
|
@@ -7698,6 +7867,7 @@ function contextCore(opts) {
|
|
|
7698
7867
|
const lines = [];
|
|
7699
7868
|
const shownFiles = new Set();
|
|
7700
7869
|
const usedFiles = new Set();
|
|
7870
|
+
let usedDynamicChars = 0;
|
|
7701
7871
|
const FENCE = String.fromCharCode(96, 96, 96);
|
|
7702
7872
|
function usedChars() { return lines.reduce(function (s, x) { return s + String(x).length + 1; }, 0); }
|
|
7703
7873
|
function appendContextEntry(entry, reason, options) {
|
|
@@ -7707,7 +7877,7 @@ function contextCore(opts) {
|
|
|
7707
7877
|
trace.push('[dropped] ' + entry.file + ' reason: duplicate');
|
|
7708
7878
|
return false;
|
|
7709
7879
|
}
|
|
7710
|
-
const line =
|
|
7880
|
+
const line = contextEntryLine(entry);
|
|
7711
7881
|
if (options.budget !== false && budget > 0 && usedChars() + line.length > budget) {
|
|
7712
7882
|
trace.push('[dropped] ' + entry.file + ' reason: budget_exceeded');
|
|
7713
7883
|
return false;
|
|
@@ -7715,6 +7885,7 @@ function contextCore(opts) {
|
|
|
7715
7885
|
lines.push(line);
|
|
7716
7886
|
shownFiles.add(entry.file);
|
|
7717
7887
|
usedFiles.add(entry.file);
|
|
7888
|
+
usedDynamicChars += line.length;
|
|
7718
7889
|
trace.push('[included] ' + entry.file + ' reason: ' + reason);
|
|
7719
7890
|
return true;
|
|
7720
7891
|
}
|
|
@@ -7784,19 +7955,124 @@ function contextCore(opts) {
|
|
|
7784
7955
|
if (memHook.evict.has(entry.file)) return false;
|
|
7785
7956
|
return true;
|
|
7786
7957
|
}
|
|
7958
|
+
let focusedEntries = [];
|
|
7959
|
+
if (focus) {
|
|
7960
|
+
const focused = recallCore(focus, {
|
|
7961
|
+
limit: limit,
|
|
7962
|
+
owner: owner,
|
|
7963
|
+
unsafe: unsafe,
|
|
7964
|
+
selfAgent: selfAgent,
|
|
7965
|
+
years: years,
|
|
7966
|
+
embedding: embeddingCommand,
|
|
7967
|
+
embeddingTimeout: embeddingTimeout,
|
|
7968
|
+
noUsage: true
|
|
7969
|
+
});
|
|
7970
|
+
focusedEntries = focused.entries || [];
|
|
7971
|
+
}
|
|
7972
|
+
const pageable = contextPageableEntries(readableEntries, owner).filter(hookAllows);
|
|
7973
|
+
const summaryLimit = Math.min(3, Math.max(0, limit));
|
|
7974
|
+
const summaryCandidates = pageable.filter(isConsolidatedSummary).slice(0, summaryLimit);
|
|
7975
|
+
const summaryFiles = new Set(summaryCandidates.map(function (entry) { return entry.file; }));
|
|
7976
|
+
const pageableFiles = new Set(pageable.map(function (entry) { return entry.file; }));
|
|
7977
|
+
const focusCandidates = focusedEntries.filter(function (entry) {
|
|
7978
|
+
return hookAllows(entry) && pageableFiles.has(entry.file);
|
|
7979
|
+
});
|
|
7980
|
+
const focusFiles = new Set(focusCandidates.map(function (entry) { return entry.file; }));
|
|
7981
|
+
const corridorCandidates = pageable
|
|
7982
|
+
.filter(function (entry) {
|
|
7983
|
+
return !isConsolidatedSummary(entry) && !summaryFiles.has(entry.file) && !focusFiles.has(entry.file);
|
|
7984
|
+
})
|
|
7985
|
+
.slice(0, limit);
|
|
7986
|
+
const corridorFiles = new Set(corridorCandidates.map(function (entry) { return entry.file; }));
|
|
7987
|
+
const highValueCandidates = pageable
|
|
7988
|
+
.filter(function (entry) {
|
|
7989
|
+
return !isConsolidatedSummary(entry) && !summaryFiles.has(entry.file) && !focusFiles.has(entry.file) && !corridorFiles.has(entry.file);
|
|
7990
|
+
})
|
|
7991
|
+
.map(function (entry) { return { e: entry, s: 0.5 * importanceScore(entry) + 0.5 * utilityScore(entry) }; })
|
|
7992
|
+
.sort(function (a, b) { return b.s - a.s; })
|
|
7993
|
+
.slice(0, limit)
|
|
7994
|
+
.map(function (item) { return item.e; });
|
|
7995
|
+
const baselineSections = {};
|
|
7996
|
+
function markBaseline(entries, section) {
|
|
7997
|
+
for (const entry of entries) baselineSections[entry.file] = section;
|
|
7998
|
+
}
|
|
7999
|
+
markBaseline(summaryCandidates, 'summary');
|
|
8000
|
+
markBaseline(focusCandidates, 'focus');
|
|
8001
|
+
markBaseline(corridorCandidates, 'corridor');
|
|
8002
|
+
markBaseline(highValueCandidates, 'high_value');
|
|
8003
|
+
const paging = applyContextPaging(pageable, {
|
|
8004
|
+
owner: owner,
|
|
8005
|
+
budget: budget,
|
|
8006
|
+
focus: focus,
|
|
8007
|
+
limit: limit,
|
|
8008
|
+
}, {
|
|
8009
|
+
protected: readableEntries
|
|
8010
|
+
.filter(function (entry) { return isSelfProfileEntry(entry, owner); })
|
|
8011
|
+
.map(function (entry) { return entry.file; }),
|
|
8012
|
+
sections: {
|
|
8013
|
+
summary: summaryCandidates.map(function (entry) { return entry.file; }),
|
|
8014
|
+
focus: focusCandidates.map(function (entry) { return entry.file; }),
|
|
8015
|
+
corridor: corridorCandidates.map(function (entry) { return entry.file; }),
|
|
8016
|
+
high_value: highValueCandidates.map(function (entry) { return entry.file; }),
|
|
8017
|
+
},
|
|
8018
|
+
baselineSections: baselineSections,
|
|
8019
|
+
});
|
|
8020
|
+
const pagingState = paging.block;
|
|
8021
|
+
const pagingOrder = new Map();
|
|
8022
|
+
for (let i = 0; i < pagingState.ordered.length; i++) pagingOrder.set(pagingState.ordered[i], i);
|
|
8023
|
+
function pagingAllows(entry) {
|
|
8024
|
+
return !entry || !paging.drop.has(entry.file);
|
|
8025
|
+
}
|
|
8026
|
+
function decisionAllows(entry) {
|
|
8027
|
+
return hookAllows(entry) && pagingAllows(entry);
|
|
8028
|
+
}
|
|
8029
|
+
function pagingOrderIndex(entry) {
|
|
8030
|
+
if (!entry || !pagingOrder.has(entry.file)) return Number.MAX_SAFE_INTEGER;
|
|
8031
|
+
return pagingOrder.get(entry.file);
|
|
8032
|
+
}
|
|
8033
|
+
function compareWithPaging(a, b, fallback) {
|
|
8034
|
+
const byOrder = pagingOrderIndex(a) - pagingOrderIndex(b);
|
|
8035
|
+
if (byOrder !== 0) return byOrder;
|
|
8036
|
+
return fallback(a, b);
|
|
8037
|
+
}
|
|
8038
|
+
if ((hookState.applied || pagingState.applied) && owner) {
|
|
8039
|
+
const liveGroups = profileGroupsFromEntries(
|
|
8040
|
+
readableEntries
|
|
8041
|
+
.filter(function (entry) { return entry.type === 'PREF' || entry.type === 'BOUND' || entry.type === 'COMMIT'; })
|
|
8042
|
+
.filter(decisionAllows)
|
|
8043
|
+
);
|
|
8044
|
+
replaceContextProfileSection(lines, renderProfileText(owner, liveGroups), FENCE);
|
|
8045
|
+
trace.push('[profile] source: live reason: ' + (hookState.applied ? 'memory.hook' : 'context.paging'));
|
|
8046
|
+
}
|
|
8047
|
+
const statusLines = [];
|
|
7787
8048
|
if (hookState.status !== 'not_installed') {
|
|
7788
|
-
|
|
7789
|
-
lines.splice(anchor === -1 ? 2 : anchor, 0, '- 扩展装载(memory.hook): ' + hookStatusText(hookState), '');
|
|
8049
|
+
statusLines.push('- 扩展装载(memory.hook): ' + hookStatusText(hookState));
|
|
7790
8050
|
trace.push('[hook] status: ' + hookState.status
|
|
7791
8051
|
+ ' provider: ' + (hookState.provider_id || '-')
|
|
7792
8052
|
+ ' evicted: ' + hookState.evicted.length
|
|
7793
8053
|
+ ' dropped: ' + hookState.dropped.length);
|
|
7794
8054
|
}
|
|
8055
|
+
const pagingText = pagingStatusText(pagingState);
|
|
8056
|
+
if (pagingText) {
|
|
8057
|
+
statusLines.push('- 预算分页(context.paging): ' + pagingText);
|
|
8058
|
+
trace.push('[paging] status: ' + pagingState.status
|
|
8059
|
+
+ ' provider: ' + (pagingState.provider_id || '-')
|
|
8060
|
+
+ ' dropped: ' + pagingState.dropped.length
|
|
8061
|
+
+ ' ordered: ' + pagingState.ordered.length
|
|
8062
|
+
+ ' truncated: ' + (pagingState.truncated ? 'true' : 'false'));
|
|
8063
|
+
}
|
|
8064
|
+
if (statusLines.length) {
|
|
8065
|
+
const anchor = lines.indexOf('## 1. 身份');
|
|
8066
|
+
statusLines.push('');
|
|
8067
|
+
lines.splice.apply(lines, [anchor === -1 ? 2 : anchor, 0].concat(statusLines));
|
|
8068
|
+
}
|
|
7795
8069
|
|
|
7796
8070
|
lines.push('## 2.5 长期理解摘要');
|
|
7797
8071
|
lines.push('');
|
|
7798
|
-
const summaries = readableEntries
|
|
7799
|
-
|
|
8072
|
+
const summaries = readableEntries
|
|
8073
|
+
.filter(isConsolidatedSummary)
|
|
8074
|
+
.filter(decisionAllows)
|
|
8075
|
+
.sort(function (a, b) { return compareWithPaging(a, b, compareContextRecent); });
|
|
7800
8076
|
if (!summaries.length) lines.push('(暂无周期摘要;可用 yotta-memory consolidate --apply 生成)');
|
|
7801
8077
|
for (const e of summaries.slice(0, summaryLimit)) {
|
|
7802
8078
|
appendContextEntry(e, 'summary_priority', { budget: false });
|
|
@@ -7806,19 +8082,11 @@ function contextCore(opts) {
|
|
|
7806
8082
|
if (focus) {
|
|
7807
8083
|
lines.push('## 2.6 任务相关记忆(--focus)');
|
|
7808
8084
|
lines.push('');
|
|
7809
|
-
const focused = recallCore(focus, {
|
|
7810
|
-
limit: limit,
|
|
7811
|
-
owner: owner,
|
|
7812
|
-
unsafe: unsafe,
|
|
7813
|
-
selfAgent: selfAgent,
|
|
7814
|
-
years: years,
|
|
7815
|
-
embedding: embeddingCommand,
|
|
7816
|
-
embeddingTimeout: embeddingTimeout,
|
|
7817
|
-
noUsage: true
|
|
7818
|
-
});
|
|
7819
|
-
const focusedEntries = focused.entries || [];
|
|
7820
8085
|
if (!focusedEntries.length) lines.push('(无匹配记忆)');
|
|
7821
|
-
|
|
8086
|
+
const focusedVisible = focusedEntries
|
|
8087
|
+
.filter(decisionAllows)
|
|
8088
|
+
.sort(function (a, b) { return compareWithPaging(a, b, function () { return 0; }); });
|
|
8089
|
+
for (const e of focusedVisible) {
|
|
7822
8090
|
appendContextEntry(e, 'focus_match score: ' + round3(e.score));
|
|
7823
8091
|
}
|
|
7824
8092
|
lines.push('');
|
|
@@ -7828,8 +8096,8 @@ function contextCore(opts) {
|
|
|
7828
8096
|
lines.push('');
|
|
7829
8097
|
const corridor = readableEntries
|
|
7830
8098
|
.filter(function (e) { return !isConsolidatedSummary(e) && e.type !== 'BOUND' && e.type !== 'COMMIT'; })
|
|
7831
|
-
.
|
|
7832
|
-
.
|
|
8099
|
+
.filter(decisionAllows)
|
|
8100
|
+
.sort(function (a, b) { return compareWithPaging(a, b, compareContextRecent); })
|
|
7833
8101
|
.filter(function (e) { return !shownFiles.has(e.file); })
|
|
7834
8102
|
.slice(0, limit);
|
|
7835
8103
|
if (!corridor.length) lines.push('(暂无近期记忆)');
|
|
@@ -7839,9 +8107,9 @@ function contextCore(opts) {
|
|
|
7839
8107
|
lines.push('## 4. 近期高价值记忆(补位)');
|
|
7840
8108
|
lines.push('');
|
|
7841
8109
|
const highValue = readableEntries
|
|
7842
|
-
.filter(function (e) { return !isConsolidatedSummary(e) && e.type !== 'BOUND' && e.type !== 'COMMIT' && !shownFiles.has(e.file) &&
|
|
8110
|
+
.filter(function (e) { return !isConsolidatedSummary(e) && e.type !== 'BOUND' && e.type !== 'COMMIT' && !shownFiles.has(e.file) && decisionAllows(e); })
|
|
7843
8111
|
.map(function (e) { return { e: e, s: 0.5 * importanceScore(e) + 0.5 * utilityScore(e) }; })
|
|
7844
|
-
.sort(function (a, b) { return b.s -
|
|
8112
|
+
.sort(function (a, b) { return compareWithPaging(a.e, b.e, function (x, y) { return y.s - x.s; }); })
|
|
7845
8113
|
.slice(0, limit);
|
|
7846
8114
|
if (!highValue.length) lines.push('(暂无补位条目)');
|
|
7847
8115
|
for (const h of highValue) appendContextEntry(h.e, 'high_value_backfill');
|
|
@@ -7899,13 +8167,14 @@ function contextCore(opts) {
|
|
|
7899
8167
|
for (const message of reliability.warnings) lines.push('- [警告] ' + message);
|
|
7900
8168
|
if (!reliability.ok) lines.push('- 破坏性写入已锁定:先运行 yotta-memory doctor 修复严重问题。');
|
|
7901
8169
|
}
|
|
7902
|
-
|
|
8170
|
+
pagingState.used = usedDynamicChars;
|
|
8171
|
+
return { error: false, exitCode: 0, text: lines.join('\n'), trace: trace, hook: hookState, paging: pagingState };
|
|
7903
8172
|
}
|
|
7904
8173
|
function cmdContext(opts) {
|
|
7905
8174
|
const r = contextCore(opts);
|
|
7906
8175
|
if (opts.json) {
|
|
7907
8176
|
if (r.report) console.log(JSON.stringify(r.report, null, 2));
|
|
7908
|
-
else console.log(JSON.stringify({ schema: 1, hook: r.hook || null, text: r.text }, null, 2));
|
|
8177
|
+
else console.log(JSON.stringify({ schema: 1, hook: r.hook || null, paging: r.paging || null, text: r.text }, null, 2));
|
|
7909
8178
|
} else {
|
|
7910
8179
|
console.log(r.text);
|
|
7911
8180
|
}
|