@pcircle/memesh 4.8.2 → 4.8.3
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/mcp.json +7 -0
- package/.codex-plugin/plugin.json +6 -0
- package/AGENTS.md +10 -5
- package/README.de.md +11 -10
- package/README.md +23 -11
- package/README.zh-TW.md +23 -11
- package/dashboard/dist/index.html +3 -3
- package/dist/core/agent-message-inbox.d.ts +2 -2
- package/dist/core/agent-message-inbox.d.ts.map +1 -1
- package/dist/core/agent-message-inbox.js +10 -5
- package/dist/core/agent-message-inbox.js.map +1 -1
- package/dist/core/agent-messaging.d.ts +7 -0
- package/dist/core/agent-messaging.d.ts.map +1 -1
- package/dist/core/agent-messaging.js +28 -9
- package/dist/core/agent-messaging.js.map +1 -1
- package/dist/core/agent-router.d.ts +27 -20
- package/dist/core/agent-router.d.ts.map +1 -1
- package/dist/core/agent-router.js +114 -43
- package/dist/core/agent-router.js.map +1 -1
- package/dist/core/briefing.d.ts +1 -1
- package/dist/core/briefing.d.ts.map +1 -1
- package/dist/core/briefing.js +18 -6
- package/dist/core/briefing.js.map +1 -1
- package/dist/core/citation-rule.d.ts.map +1 -1
- package/dist/core/citation-rule.js +5 -2
- package/dist/core/citation-rule.js.map +1 -1
- package/dist/core/doctor.d.ts +8 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +309 -10
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/install-channel.d.ts +3 -0
- package/dist/core/install-channel.d.ts.map +1 -1
- package/dist/core/install-channel.js +79 -2
- package/dist/core/install-channel.js.map +1 -1
- package/dist/core/install-hooks.d.ts.map +1 -1
- package/dist/core/install-hooks.js +10 -6
- package/dist/core/install-hooks.js.map +1 -1
- package/dist/core/lesson-slug.d.ts.map +1 -1
- package/dist/core/lesson-slug.js +6 -4
- package/dist/core/lesson-slug.js.map +1 -1
- package/dist/core/llm-client.d.ts.map +1 -1
- package/dist/core/llm-client.js +4 -1
- package/dist/core/llm-client.js.map +1 -1
- package/dist/core/llm-validator.d.ts.map +1 -1
- package/dist/core/llm-validator.js +6 -15
- package/dist/core/llm-validator.js.map +1 -1
- package/dist/core/ollama-host.d.ts +6 -0
- package/dist/core/ollama-host.d.ts.map +1 -0
- package/dist/core/ollama-host.js +30 -0
- package/dist/core/ollama-host.js.map +1 -0
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +8 -6
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/work-topology.d.ts +3 -0
- package/dist/core/work-topology.d.ts.map +1 -1
- package/dist/core/work-topology.js +24 -2
- package/dist/core/work-topology.js.map +1 -1
- package/dist/host-adapters/claude-channel.d.ts +0 -58
- package/dist/host-adapters/claude-channel.d.ts.map +1 -1
- package/dist/host-adapters/claude-channel.js +0 -242
- package/dist/host-adapters/claude-channel.js.map +1 -1
- package/dist/host-adapters/codex-cli-queue.d.ts.map +1 -1
- package/dist/host-adapters/codex-cli-queue.js +15 -12
- package/dist/host-adapters/codex-cli-queue.js.map +1 -1
- package/dist/host-runtime/acp.d.ts +2 -0
- package/dist/host-runtime/acp.d.ts.map +1 -1
- package/dist/host-runtime/acp.js +4 -0
- package/dist/host-runtime/acp.js.map +1 -1
- package/dist/host-runtime/claude.d.ts +2 -0
- package/dist/host-runtime/claude.d.ts.map +1 -1
- package/dist/host-runtime/claude.js +6 -5
- package/dist/host-runtime/claude.js.map +1 -1
- package/dist/host-runtime/codex-session.d.ts +3 -1
- package/dist/host-runtime/codex-session.d.ts.map +1 -1
- package/dist/host-runtime/codex-session.js +7 -5
- package/dist/host-runtime/codex-session.js.map +1 -1
- package/dist/host-runtime/codex.d.ts +2 -0
- package/dist/host-runtime/codex.d.ts.map +1 -1
- package/dist/host-runtime/codex.js +4 -0
- package/dist/host-runtime/codex.js.map +1 -1
- package/dist/host-runtime/router-client.d.ts +2 -0
- package/dist/host-runtime/router-client.d.ts.map +1 -1
- package/dist/host-runtime/router-client.js.map +1 -1
- package/dist/skills-manifest.json +23 -13
- package/dist/storage/graph-repairs.d.ts.map +1 -1
- package/dist/storage/graph-repairs.js +70 -22
- package/dist/storage/graph-repairs.js.map +1 -1
- package/dist/transports/agent-messaging.d.ts +10 -1
- package/dist/transports/agent-messaging.d.ts.map +1 -1
- package/dist/transports/agent-messaging.js +92 -9
- package/dist/transports/agent-messaging.js.map +1 -1
- package/dist/transports/cli/cli.d.ts +4 -0
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +65 -26
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +10 -6
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +21 -9
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +5 -0
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +8 -1
- package/dist/transports/schemas.js.map +1 -1
- package/docs/platforms/README.md +1 -1
- package/docs/platforms/agent-messaging.md +89 -36
- package/llms-install.md +29 -6
- package/package.json +5 -1
- package/scripts/hooks/_generated/agent-message-inbox.js +10 -5
- package/scripts/hooks/_generated/citation-rule.js +5 -2
- package/scripts/hooks/_generated/work-topology.js +24 -2
- package/scripts/hooks/_shared.js +1 -1
- package/scripts/hooks/session-start.js +37 -23
- package/scripts/upgrade-plugin.sh +531 -81
- package/skills/memesh/SKILL.md +20 -5
|
@@ -6,18 +6,21 @@
|
|
|
6
6
|
// always-on capture path survives a missing or stale dist/ while staying
|
|
7
7
|
// byte-locked to core — eliminating the hand-mirror drift behind the P0 FTS bug.
|
|
8
8
|
// ============================================================================
|
|
9
|
-
export function unreadDeliveryCount(db, project) {
|
|
9
|
+
export function unreadDeliveryCount(db, project, recipient) {
|
|
10
|
+
if (!recipient)
|
|
11
|
+
return 0;
|
|
10
12
|
try {
|
|
11
13
|
const row = db.prepare(`SELECT COUNT(*) AS n
|
|
12
14
|
FROM agent_message_deliveries d
|
|
13
15
|
WHERE d.project = ?
|
|
16
|
+
AND d.recipient = ?
|
|
14
17
|
AND NOT EXISTS (
|
|
15
18
|
SELECT 1 FROM agent_message_receipts r
|
|
16
19
|
WHERE r.project = d.project
|
|
17
20
|
AND r.recipient = d.recipient
|
|
18
21
|
AND r.message_id = d.message_id
|
|
19
22
|
AND r.receipt_kind = 'intake'
|
|
20
|
-
)`).get(project);
|
|
23
|
+
)`).get(project, recipient);
|
|
21
24
|
const n = row?.n;
|
|
22
25
|
return typeof n === 'number' && n > 0 ? n : 0;
|
|
23
26
|
}
|
|
@@ -25,9 +28,11 @@ export function unreadDeliveryCount(db, project) {
|
|
|
25
28
|
return 0;
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
|
-
export function unreadInboxLines(count, project) {
|
|
29
|
-
if (count <= 0)
|
|
31
|
+
export function unreadInboxLines(count, project, recipient) {
|
|
32
|
+
if (count <= 0 || !recipient)
|
|
30
33
|
return [];
|
|
31
34
|
const noun = count === 1 ? 'message' : 'messages';
|
|
32
|
-
|
|
35
|
+
const displayProject = JSON.stringify(project);
|
|
36
|
+
const displayRecipient = JSON.stringify(recipient);
|
|
37
|
+
return [`${count} ${noun} waiting for ${displayRecipient} in project ${displayProject} — poll the message tool with project ${displayProject} and recipient ${displayRecipient}, then fetch each message_id; fetching does not acknowledge.`];
|
|
33
38
|
}
|
|
@@ -25,8 +25,11 @@ memory is recorded as unused: that is how MeMesh learns which memories are
|
|
|
25
25
|
worth the tokens they cost you.
|
|
26
26
|
`;
|
|
27
27
|
export function citationRuleDir(scope, home, cwd) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (scope === 'project')
|
|
29
|
+
return path.join(cwd, '.claude', 'rules');
|
|
30
|
+
const relocated = process.env.CLAUDE_CONFIG_DIR;
|
|
31
|
+
const configRoot = relocated ? path.resolve(relocated) : path.join(home, '.claude');
|
|
32
|
+
return path.join(configRoot, 'rules');
|
|
30
33
|
}
|
|
31
34
|
export function citationRulePath(scope, home, cwd) {
|
|
32
35
|
return path.join(citationRuleDir(scope, home, cwd), CITATION_RULE_FILENAME);
|
|
@@ -82,10 +82,15 @@ export function groupTopology(entities, projectName) {
|
|
|
82
82
|
const lessons = [];
|
|
83
83
|
const knowledge = [];
|
|
84
84
|
const evidence = [];
|
|
85
|
+
const global = [];
|
|
85
86
|
const foreign = [];
|
|
86
87
|
for (const e of entities) {
|
|
87
88
|
if (e.type === 'task-state')
|
|
88
89
|
continue;
|
|
90
|
+
if (e.global) {
|
|
91
|
+
global.push(e);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
89
94
|
if (e.foreign) {
|
|
90
95
|
foreign.push(e);
|
|
91
96
|
continue;
|
|
@@ -104,7 +109,7 @@ export function groupTopology(entities, projectName) {
|
|
|
104
109
|
else
|
|
105
110
|
decisions.push(e);
|
|
106
111
|
}
|
|
107
|
-
for (const list of [decisions, lessons, knowledge, evidence, foreign])
|
|
112
|
+
for (const list of [decisions, lessons, knowledge, evidence, global, foreign])
|
|
108
113
|
list.sort(bySignal);
|
|
109
114
|
const sections = [];
|
|
110
115
|
if (decisions.length)
|
|
@@ -115,6 +120,8 @@ export function groupTopology(entities, projectName) {
|
|
|
115
120
|
sections.push({ heading: `What is known about "${projectName}":`, entities: knowledge });
|
|
116
121
|
if (evidence.length)
|
|
117
122
|
sections.push({ heading: `Recent activity in "${projectName}":`, entities: evidence });
|
|
123
|
+
if (global.length)
|
|
124
|
+
sections.push({ heading: 'Global memory — applies across projects:', entities: global });
|
|
118
125
|
if (foreign.length)
|
|
119
126
|
sections.push({ heading: 'From your other projects (may or may not apply here):', entities: foreign });
|
|
120
127
|
return sections;
|
|
@@ -124,6 +131,11 @@ export const DEFAULT_TOPOLOGY_BUDGET = {
|
|
|
124
131
|
maxChars: 4000,
|
|
125
132
|
maxLineChars: 160,
|
|
126
133
|
};
|
|
134
|
+
export const GLOBAL_TOPOLOGY_LIMIT = 3;
|
|
135
|
+
const GLOBAL_TOPOLOGY_BUDGET = {
|
|
136
|
+
maxChars: 640,
|
|
137
|
+
maxLineChars: DEFAULT_TOPOLOGY_BUDGET.maxLineChars,
|
|
138
|
+
};
|
|
127
139
|
export const TOPOLOGY_CANDIDATE_CAP = 400;
|
|
128
140
|
export const SNIPPET_FETCH_CHARS = DEFAULT_TOPOLOGY_BUDGET.maxLineChars * 4;
|
|
129
141
|
export function buildTopologyLines(entities, projectName, budget) {
|
|
@@ -155,12 +167,18 @@ export function buildTopologyLines(entities, projectName, budget) {
|
|
|
155
167
|
export function assembleTopologyBlock(stateLines, pools, projectName, budget = DEFAULT_TOPOLOGY_BUDGET) {
|
|
156
168
|
const seen = new Set();
|
|
157
169
|
const candidates = [];
|
|
170
|
+
const globalCandidates = [];
|
|
158
171
|
for (const pool of pools) {
|
|
159
172
|
for (const e of pool.entities) {
|
|
160
173
|
if (seen.has(e.name))
|
|
161
174
|
continue;
|
|
162
175
|
seen.add(e.name);
|
|
163
|
-
|
|
176
|
+
if (pool.global) {
|
|
177
|
+
globalCandidates.push(e.global ? e : { ...e, global: true });
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
candidates.push(pool.foreign && !e.foreign ? { ...e, foreign: true } : e);
|
|
181
|
+
}
|
|
164
182
|
}
|
|
165
183
|
}
|
|
166
184
|
const lines = [];
|
|
@@ -173,9 +191,13 @@ export function assembleTopologyBlock(stateLines, pools, projectName, budget = D
|
|
|
173
191
|
const topologyLines = remaining > 0
|
|
174
192
|
? buildTopologyLines(candidates, projectName, { ...budget, maxChars: remaining })
|
|
175
193
|
: [];
|
|
194
|
+
const globalLines = buildTopologyLines(globalCandidates, projectName, GLOBAL_TOPOLOGY_BUDGET);
|
|
176
195
|
if (lines.length > 0 && topologyLines.length > 0)
|
|
177
196
|
lines.push('');
|
|
178
197
|
lines.push(...topologyLines);
|
|
198
|
+
if (lines.length > 0 && globalLines.length > 0)
|
|
199
|
+
lines.push('');
|
|
200
|
+
lines.push(...globalLines);
|
|
179
201
|
return lines;
|
|
180
202
|
}
|
|
181
203
|
export function buildReferenceContext(memoryLines) {
|
package/scripts/hooks/_shared.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
slugFromRemoteUrl,
|
|
35
35
|
} from './_generated/core-paths.js';
|
|
36
36
|
import { autoCaptureDecision } from './_generated/capture-flag.js';
|
|
37
|
-
export { assembleTopologyBlock, buildReferenceContext, extractCitedMemoryIds, DEFAULT_TOPOLOGY_BUDGET, SNIPPET_FETCH_CHARS, TOPOLOGY_CANDIDATE_CAP } from './_generated/work-topology.js';
|
|
37
|
+
export { assembleTopologyBlock, buildReferenceContext, extractCitedMemoryIds, DEFAULT_TOPOLOGY_BUDGET, GLOBAL_TOPOLOGY_LIMIT, SNIPPET_FETCH_CHARS, TOPOLOGY_CANDIDATE_CAP } from './_generated/work-topology.js';
|
|
38
38
|
export { readRepoState, repoStateLines } from './_generated/repo-state.js';
|
|
39
39
|
export { matchingGuards, guardFromMetadata } from './_generated/guards.js';
|
|
40
40
|
export { writeCitationRule, citationRulePath, CITATION_RULE_BODY } from './_generated/citation-rule.js';
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
importFromPluginRoot,
|
|
16
16
|
assembleTopologyBlock,
|
|
17
17
|
DEFAULT_TOPOLOGY_BUDGET,
|
|
18
|
+
GLOBAL_TOPOLOGY_LIMIT,
|
|
18
19
|
SNIPPET_FETCH_CHARS,
|
|
19
20
|
TOPOLOGY_CANDIDATE_CAP,
|
|
20
21
|
isTrustedForAutoContext,
|
|
@@ -786,9 +787,12 @@ process.stdin.on('end', async () => {
|
|
|
786
787
|
};
|
|
787
788
|
|
|
788
789
|
const projectTag = `project:${projectName}`;
|
|
790
|
+
const notGlobal = colNames.has('namespace')
|
|
791
|
+
? "AND (e.namespace IS NULL OR e.namespace <> 'global')"
|
|
792
|
+
: '';
|
|
789
793
|
const projectQuery = buildScoringQuery(
|
|
790
794
|
`JOIN tags t ON t.entity_id = e.id`,
|
|
791
|
-
`WHERE t.tag = ? ${statusFilter}`,
|
|
795
|
+
`WHERE t.tag = ? ${notGlobal} ${statusFilter}`,
|
|
792
796
|
);
|
|
793
797
|
// Over-fetch WIDE, then filter. The window used to be `sessionLimit * 3`
|
|
794
798
|
// and the trust filter ran after it — so a class of entity that ranks
|
|
@@ -811,26 +815,27 @@ process.stdin.on('end', async () => {
|
|
|
811
815
|
// by nobody (#242): 1814 entities, 2 global, one of them structurally
|
|
812
816
|
// uninjectable. A standing behaviour rule sat in that state for months.
|
|
813
817
|
//
|
|
814
|
-
// Global rows ride in a SEPARATE, small window
|
|
815
|
-
//
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
// branch is simply empty, which is the old behaviour.
|
|
819
|
-
const GLOBAL_SLOTS = 3;
|
|
818
|
+
// Global rows ride in a SEPARATE, small window and the shared assembler
|
|
819
|
+
// gives them a separate render budget. The project keeps `sessionLimit`
|
|
820
|
+
// slots and its full character budget. The column is absent on
|
|
821
|
+
// pre-namespace schemas; then this branch is simply empty.
|
|
820
822
|
let globalEntities = [];
|
|
821
823
|
if (colNames.has('namespace')) {
|
|
822
824
|
const globalQuery = buildScoringQuery('', `WHERE e.namespace = 'global' ${statusFilter}`);
|
|
823
|
-
const projectIds = new Set(projectOnly.map(e => e.id));
|
|
824
825
|
globalEntities = db.prepare(globalQuery).all(CANDIDATE_CAP)
|
|
825
|
-
.filter(entity => isTrustedForAutoContext(entity.metadata)
|
|
826
|
-
.slice(0,
|
|
826
|
+
.filter(entity => isTrustedForAutoContext(entity.metadata))
|
|
827
|
+
.slice(0, GLOBAL_TOPOLOGY_LIMIT);
|
|
827
828
|
}
|
|
828
|
-
const projectEntities =
|
|
829
|
+
const projectEntities = projectOnly.slice(0, sessionLimit);
|
|
829
830
|
|
|
830
831
|
// recentStatusFilter is "WHERE status = 'active'" or "" — the bare-column
|
|
831
832
|
// form is fine when there's no JOIN, but we now alias the table as `e`,
|
|
832
833
|
// so rewrite to e.status for consistency.
|
|
833
|
-
const
|
|
834
|
+
const recentConditions = [
|
|
835
|
+
hasStatus ? "e.status = 'active'" : '',
|
|
836
|
+
colNames.has('namespace') ? "(e.namespace IS NULL OR e.namespace <> 'global')" : '',
|
|
837
|
+
].filter(Boolean);
|
|
838
|
+
const recentWhere = recentConditions.length > 0 ? `WHERE ${recentConditions.join(' AND ')}` : '';
|
|
834
839
|
const recentQuery = buildScoringQuery('', recentWhere);
|
|
835
840
|
const recentEntities = db.prepare(recentQuery).all(CANDIDATE_CAP)
|
|
836
841
|
.filter(entity => isTrustedForAutoContext(entity.metadata))
|
|
@@ -850,6 +855,7 @@ process.stdin.on('end', async () => {
|
|
|
850
855
|
JOIN tags t ON t.entity_id = e.id
|
|
851
856
|
WHERE e.type = 'lesson_learned'
|
|
852
857
|
${hasStatus ? "AND e.status = 'active'" : ''}
|
|
858
|
+
${colNames.has('namespace') ? "AND (e.namespace IS NULL OR e.namespace <> 'global')" : ''}
|
|
853
859
|
AND t.tag = ?
|
|
854
860
|
LIMIT 50
|
|
855
861
|
`).all(projectTag).filter(entity => isTrustedForAutoContext(entity.metadata));
|
|
@@ -866,9 +872,11 @@ process.stdin.on('end', async () => {
|
|
|
866
872
|
// SessionStart system message takes one row in the Claude Code
|
|
867
873
|
// log instead of four. Counts are count-only — no entity bullets.
|
|
868
874
|
const projectCount = projectEntities.length;
|
|
875
|
+
const globalCount = globalEntities.length;
|
|
869
876
|
const recentCount = recentEntities.length;
|
|
870
877
|
const memoryFragments = [];
|
|
871
878
|
if (projectCount > 0) memoryFragments.push(`${projectCount} project`);
|
|
879
|
+
if (globalCount > 0) memoryFragments.push(`${globalCount} global`);
|
|
872
880
|
if (recentCount > 0) memoryFragments.push(`${recentCount} recent`);
|
|
873
881
|
|
|
874
882
|
let summary;
|
|
@@ -915,6 +923,7 @@ process.stdin.on('end', async () => {
|
|
|
915
923
|
const rankedIds = [
|
|
916
924
|
...topLessons.map(e => e.id),
|
|
917
925
|
...projectEntities.map(e => e.id),
|
|
926
|
+
...globalEntities.map(e => e.id),
|
|
918
927
|
...recentEntities.map(e => e.id),
|
|
919
928
|
];
|
|
920
929
|
const uniqueIds = [...new Set(rankedIds)];
|
|
@@ -955,8 +964,8 @@ process.stdin.on('end', async () => {
|
|
|
955
964
|
const taskRow = db
|
|
956
965
|
.prepare('SELECT metadata FROM entities WHERE name = ?')
|
|
957
966
|
.get(taskStateName(projectName));
|
|
958
|
-
//
|
|
959
|
-
//
|
|
967
|
+
// SessionStart has no exact recipient identity. The shared leaf fails
|
|
968
|
+
// closed before querying, so hook and briefing cannot diverge here.
|
|
960
969
|
const stateLines = [
|
|
961
970
|
...taskStateLines(
|
|
962
971
|
parseTaskState(parseEntityMetadata(taskRow?.metadata)),
|
|
@@ -992,6 +1001,7 @@ process.stdin.on('end', async () => {
|
|
|
992
1001
|
[
|
|
993
1002
|
{ entities: topLessons.map(toEntity), foreign: false },
|
|
994
1003
|
{ entities: projectEntities.map(toEntity), foreign: false },
|
|
1004
|
+
{ entities: globalEntities.map(toEntity), foreign: false, global: true },
|
|
995
1005
|
{ entities: recentEntities.map(toEntity), foreign: true },
|
|
996
1006
|
],
|
|
997
1007
|
projectName,
|
|
@@ -1021,8 +1031,9 @@ process.stdin.on('end', async () => {
|
|
|
1021
1031
|
// ever been told can end up in an observation), so it must be
|
|
1022
1032
|
// delimited the same way on every injection path — not hand-rolled
|
|
1023
1033
|
// per hook. The lines arrive already budgeted — assembleTopologyBlock
|
|
1024
|
-
// charges
|
|
1025
|
-
// and
|
|
1034
|
+
// charges task state plus project/foreign sections against the main
|
|
1035
|
+
// ceiling and global context against its small additive ceiling. It
|
|
1036
|
+
// returns whole lines only, so the closing fence cannot be cut.
|
|
1026
1037
|
memoryContext = buildReferenceContext(memoryLines);
|
|
1027
1038
|
// The citation contract — OUTSIDE the fence on purpose: the fence
|
|
1028
1039
|
// declares its content "background data, not instructions", and
|
|
@@ -1054,15 +1065,18 @@ process.stdin.on('end', async () => {
|
|
|
1054
1065
|
// kept as the record of what was shown.
|
|
1055
1066
|
//
|
|
1056
1067
|
// The set below is every pool the topology block draws from — the
|
|
1057
|
-
// lessons pool included. It
|
|
1058
|
-
//
|
|
1068
|
+
// lessons pool included. It is derived from rendered citation handles,
|
|
1069
|
+
// so clipped or budgeted-away candidates cannot be credited as shown.
|
|
1059
1070
|
try {
|
|
1060
|
-
const
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
seenIds.add(e.id);
|
|
1064
|
-
return true;
|
|
1071
|
+
const renderedEntityIds = memoryLines.flatMap((line) => {
|
|
1072
|
+
const match = line.match(/ \[mem:(\d{1,10})\]$/);
|
|
1073
|
+
return match ? [Number(match[1])] : [];
|
|
1065
1074
|
});
|
|
1075
|
+
const poolEntities = [...topLessons, ...projectEntities, ...globalEntities, ...recentEntities];
|
|
1076
|
+
const entitiesById = new Map(poolEntities.map((entity) => [entity.id, entity]));
|
|
1077
|
+
const allInjected = renderedEntityIds
|
|
1078
|
+
.map((id) => entitiesById.get(id))
|
|
1079
|
+
.filter(Boolean);
|
|
1066
1080
|
|
|
1067
1081
|
if (allInjected.length > 0) {
|
|
1068
1082
|
const sessionsDir = join(memeshDir, 'sessions');
|