@pcircle/memesh 4.8.1 → 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 +13 -2
- package/README.de.md +11 -10
- package/README.md +23 -11
- package/README.zh-TW.md +23 -11
- package/dashboard/dist/index.html +9 -9
- package/dist/core/agent-message-inbox.d.ts +9 -0
- package/dist/core/agent-message-inbox.d.ts.map +1 -0
- package/dist/core/agent-message-inbox.js +31 -0
- package/dist/core/agent-message-inbox.js.map +1 -0
- 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 +22 -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/config.d.ts +4 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +12 -3
- package/dist/core/config.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 +314 -12
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +2 -0
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +7 -1
- package/dist/core/dreamer.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-engine.d.ts +2 -1
- package/dist/core/lesson-engine.d.ts.map +1 -1
- package/dist/core/lesson-engine.js +5 -2
- package/dist/core/lesson-engine.js.map +1 -1
- package/dist/core/lesson-slug.d.ts +2 -0
- package/dist/core/lesson-slug.d.ts.map +1 -0
- package/dist/core/lesson-slug.js +14 -0
- package/dist/core/lesson-slug.js.map +1 -0
- 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 +4 -1
- package/dist/core/llm-validator.d.ts.map +1 -1
- package/dist/core/llm-validator.js +62 -42
- 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/operations.d.ts +9 -3
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +18 -14
- package/dist/core/operations.js.map +1 -1
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +2 -3
- package/dist/core/paths.js.map +1 -1
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +9 -7
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/transcript-extractor.d.ts.map +1 -1
- package/dist/core/transcript-extractor.js +2 -2
- package/dist/core/transcript-extractor.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/db.d.ts.map +1 -1
- package/dist/db.js +12 -0
- package/dist/db.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 +30 -15
- package/dist/storage/graph-repairs.d.ts +12 -0
- package/dist/storage/graph-repairs.d.ts.map +1 -0
- package/dist/storage/graph-repairs.js +242 -0
- package/dist/storage/graph-repairs.js.map +1 -0
- 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/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +117 -7
- package/dist/transports/http/server.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 +17 -3
- package/scripts/hooks/_generated/agent-message-inbox.js +38 -0
- package/scripts/hooks/_generated/citation-rule.js +5 -2
- package/scripts/hooks/_generated/core-paths.js +2 -3
- package/scripts/hooks/_generated/work-topology.js +24 -2
- package/scripts/hooks/_shared.js +1 -1
- package/scripts/hooks/session-start.js +59 -18
- package/scripts/hooks/session-summary.js +40 -1
- package/scripts/upgrade-plugin.sh +531 -81
- package/skills/memesh/SKILL.md +36 -5
|
@@ -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,
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
writePrivateJson,
|
|
37
38
|
} from './_shared.js';
|
|
38
39
|
import { MemeshDatabase } from './_generated/sqlite.js';
|
|
40
|
+
import { unreadDeliveryCount, unreadInboxLines } from './_generated/agent-message-inbox.js';
|
|
39
41
|
|
|
40
42
|
const require = createRequire(import.meta.url);
|
|
41
43
|
|
|
@@ -785,9 +787,12 @@ process.stdin.on('end', async () => {
|
|
|
785
787
|
};
|
|
786
788
|
|
|
787
789
|
const projectTag = `project:${projectName}`;
|
|
790
|
+
const notGlobal = colNames.has('namespace')
|
|
791
|
+
? "AND (e.namespace IS NULL OR e.namespace <> 'global')"
|
|
792
|
+
: '';
|
|
788
793
|
const projectQuery = buildScoringQuery(
|
|
789
794
|
`JOIN tags t ON t.entity_id = e.id`,
|
|
790
|
-
`WHERE t.tag = ? ${statusFilter}`,
|
|
795
|
+
`WHERE t.tag = ? ${notGlobal} ${statusFilter}`,
|
|
791
796
|
);
|
|
792
797
|
// Over-fetch WIDE, then filter. The window used to be `sessionLimit * 3`
|
|
793
798
|
// and the trust filter ran after it — so a class of entity that ranks
|
|
@@ -801,14 +806,36 @@ process.stdin.on('end', async () => {
|
|
|
801
806
|
// Shared with the briefing surface via the leaf, so the two sides'
|
|
802
807
|
// candidate windows cannot drift apart.
|
|
803
808
|
const CANDIDATE_CAP = TOPOLOGY_CANDIDATE_CAP;
|
|
804
|
-
const
|
|
805
|
-
.filter(entity => isTrustedForAutoContext(entity.metadata))
|
|
806
|
-
|
|
809
|
+
const projectOnly = db.prepare(projectQuery).all(projectTag, CANDIDATE_CAP)
|
|
810
|
+
.filter(entity => isTrustedForAutoContext(entity.metadata));
|
|
811
|
+
|
|
812
|
+
// The `global` namespace is the documented way to store something that
|
|
813
|
+
// is not tied to one project — and the injection selected purely by
|
|
814
|
+
// `project:` tag, so a global memory with no project tag was reachable
|
|
815
|
+
// by nobody (#242): 1814 entities, 2 global, one of them structurally
|
|
816
|
+
// uninjectable. A standing behaviour rule sat in that state for months.
|
|
817
|
+
//
|
|
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.
|
|
822
|
+
let globalEntities = [];
|
|
823
|
+
if (colNames.has('namespace')) {
|
|
824
|
+
const globalQuery = buildScoringQuery('', `WHERE e.namespace = 'global' ${statusFilter}`);
|
|
825
|
+
globalEntities = db.prepare(globalQuery).all(CANDIDATE_CAP)
|
|
826
|
+
.filter(entity => isTrustedForAutoContext(entity.metadata))
|
|
827
|
+
.slice(0, GLOBAL_TOPOLOGY_LIMIT);
|
|
828
|
+
}
|
|
829
|
+
const projectEntities = projectOnly.slice(0, sessionLimit);
|
|
807
830
|
|
|
808
831
|
// recentStatusFilter is "WHERE status = 'active'" or "" — the bare-column
|
|
809
832
|
// form is fine when there's no JOIN, but we now alias the table as `e`,
|
|
810
833
|
// so rewrite to e.status for consistency.
|
|
811
|
-
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 ')}` : '';
|
|
812
839
|
const recentQuery = buildScoringQuery('', recentWhere);
|
|
813
840
|
const recentEntities = db.prepare(recentQuery).all(CANDIDATE_CAP)
|
|
814
841
|
.filter(entity => isTrustedForAutoContext(entity.metadata))
|
|
@@ -828,6 +855,7 @@ process.stdin.on('end', async () => {
|
|
|
828
855
|
JOIN tags t ON t.entity_id = e.id
|
|
829
856
|
WHERE e.type = 'lesson_learned'
|
|
830
857
|
${hasStatus ? "AND e.status = 'active'" : ''}
|
|
858
|
+
${colNames.has('namespace') ? "AND (e.namespace IS NULL OR e.namespace <> 'global')" : ''}
|
|
831
859
|
AND t.tag = ?
|
|
832
860
|
LIMIT 50
|
|
833
861
|
`).all(projectTag).filter(entity => isTrustedForAutoContext(entity.metadata));
|
|
@@ -844,9 +872,11 @@ process.stdin.on('end', async () => {
|
|
|
844
872
|
// SessionStart system message takes one row in the Claude Code
|
|
845
873
|
// log instead of four. Counts are count-only — no entity bullets.
|
|
846
874
|
const projectCount = projectEntities.length;
|
|
875
|
+
const globalCount = globalEntities.length;
|
|
847
876
|
const recentCount = recentEntities.length;
|
|
848
877
|
const memoryFragments = [];
|
|
849
878
|
if (projectCount > 0) memoryFragments.push(`${projectCount} project`);
|
|
879
|
+
if (globalCount > 0) memoryFragments.push(`${globalCount} global`);
|
|
850
880
|
if (recentCount > 0) memoryFragments.push(`${recentCount} recent`);
|
|
851
881
|
|
|
852
882
|
let summary;
|
|
@@ -893,6 +923,7 @@ process.stdin.on('end', async () => {
|
|
|
893
923
|
const rankedIds = [
|
|
894
924
|
...topLessons.map(e => e.id),
|
|
895
925
|
...projectEntities.map(e => e.id),
|
|
926
|
+
...globalEntities.map(e => e.id),
|
|
896
927
|
...recentEntities.map(e => e.id),
|
|
897
928
|
];
|
|
898
929
|
const uniqueIds = [...new Set(rankedIds)];
|
|
@@ -933,10 +964,15 @@ process.stdin.on('end', async () => {
|
|
|
933
964
|
const taskRow = db
|
|
934
965
|
.prepare('SELECT metadata FROM entities WHERE name = ?')
|
|
935
966
|
.get(taskStateName(projectName));
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
967
|
+
// SessionStart has no exact recipient identity. The shared leaf fails
|
|
968
|
+
// closed before querying, so hook and briefing cannot diverge here.
|
|
969
|
+
const stateLines = [
|
|
970
|
+
...taskStateLines(
|
|
971
|
+
parseTaskState(parseEntityMetadata(taskRow?.metadata)),
|
|
972
|
+
projectName,
|
|
973
|
+
),
|
|
974
|
+
...unreadInboxLines(unreadDeliveryCount(db, projectName), projectName),
|
|
975
|
+
];
|
|
940
976
|
|
|
941
977
|
// The pools overlap by construction (a lesson tagged to this project
|
|
942
978
|
// is in lessonEntities AND projectEntities); the shared assembler
|
|
@@ -965,6 +1001,7 @@ process.stdin.on('end', async () => {
|
|
|
965
1001
|
[
|
|
966
1002
|
{ entities: topLessons.map(toEntity), foreign: false },
|
|
967
1003
|
{ entities: projectEntities.map(toEntity), foreign: false },
|
|
1004
|
+
{ entities: globalEntities.map(toEntity), foreign: false, global: true },
|
|
968
1005
|
{ entities: recentEntities.map(toEntity), foreign: true },
|
|
969
1006
|
],
|
|
970
1007
|
projectName,
|
|
@@ -994,8 +1031,9 @@ process.stdin.on('end', async () => {
|
|
|
994
1031
|
// ever been told can end up in an observation), so it must be
|
|
995
1032
|
// delimited the same way on every injection path — not hand-rolled
|
|
996
1033
|
// per hook. The lines arrive already budgeted — assembleTopologyBlock
|
|
997
|
-
// charges
|
|
998
|
-
// 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.
|
|
999
1037
|
memoryContext = buildReferenceContext(memoryLines);
|
|
1000
1038
|
// The citation contract — OUTSIDE the fence on purpose: the fence
|
|
1001
1039
|
// declares its content "background data, not instructions", and
|
|
@@ -1027,15 +1065,18 @@ process.stdin.on('end', async () => {
|
|
|
1027
1065
|
// kept as the record of what was shown.
|
|
1028
1066
|
//
|
|
1029
1067
|
// The set below is every pool the topology block draws from — the
|
|
1030
|
-
// lessons pool included. It
|
|
1031
|
-
//
|
|
1068
|
+
// lessons pool included. It is derived from rendered citation handles,
|
|
1069
|
+
// so clipped or budgeted-away candidates cannot be credited as shown.
|
|
1032
1070
|
try {
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
seenIds.add(e.id);
|
|
1037
|
-
return true;
|
|
1071
|
+
const renderedEntityIds = memoryLines.flatMap((line) => {
|
|
1072
|
+
const match = line.match(/ \[mem:(\d{1,10})\]$/);
|
|
1073
|
+
return match ? [Number(match[1])] : [];
|
|
1038
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);
|
|
1039
1080
|
|
|
1040
1081
|
if (allInjected.length > 0) {
|
|
1041
1082
|
const sessionsDir = join(memeshDir, 'sessions');
|
|
@@ -87,6 +87,30 @@ async function runAutoUpdateAtStop() {
|
|
|
87
87
|
// Handles the current Claude Code transcript format where tool_use/tool_result
|
|
88
88
|
// are nested inside assistant/user message entries, not top-level entries.
|
|
89
89
|
// Defensive: never throws — malformed lines are silently skipped.
|
|
90
|
+
/**
|
|
91
|
+
* File paths a shell command writes in place. Recognises the shapes an
|
|
92
|
+
* agent actually uses to edit without Write/Edit: heredoc redirection
|
|
93
|
+
* (`> path <<'EOF'`, `cat > path`), `sed -i`, `tee`, and `pathlib.Path('x')
|
|
94
|
+
* .write_text(` / `fs.writeFileSync('x'` inside an inline script. Returns
|
|
95
|
+
* basenames' sources; the caller keeps basename() as the stored form.
|
|
96
|
+
*/
|
|
97
|
+
function bashEditedPaths(cmd) {
|
|
98
|
+
if (typeof cmd !== 'string') return [];
|
|
99
|
+
const found = new Set();
|
|
100
|
+
const add = (m) => { if (m && m[1] && !m[1].startsWith('/dev/') && !m[1].startsWith('/tmp/')) found.add(m[1]); };
|
|
101
|
+
for (const re of [
|
|
102
|
+
/(?:^|[^<])>\s*"?([^\s"'>|&;]+)"?\s*<<\s*['"]?\w+['"]?/g, // > file <<'EOF'
|
|
103
|
+
/\bcat\s*>\s*"?([^\s"'>|&;]+)"?/g, // cat > file
|
|
104
|
+
/\btee\s+(?:-a\s+)?"?([^\s"'>|&;]+)"?/g, // tee file
|
|
105
|
+
/\bsed\s+-i(?:\s+'')?\s+(?:'[^']*'|"[^"]*")\s+"?([^\s"'>|&;]+)"?/g, // sed -i '...' file
|
|
106
|
+
/Path\(\s*['"]([^'"]+)['"]\s*\)\s*\.write_text\(/g, // pathlib write_text
|
|
107
|
+
/writeFileSync\(\s*['"]([^'"]+)['"]/g, // fs.writeFileSync
|
|
108
|
+
]) {
|
|
109
|
+
let m; while ((m = re.exec(cmd)) !== null) add(m);
|
|
110
|
+
}
|
|
111
|
+
return [...found];
|
|
112
|
+
}
|
|
113
|
+
|
|
90
114
|
function parseTranscript(transcriptPath) {
|
|
91
115
|
const filesEdited = new Set();
|
|
92
116
|
const bashCommands = [];
|
|
@@ -118,6 +142,15 @@ function parseTranscript(transcriptPath) {
|
|
|
118
142
|
}
|
|
119
143
|
if (block.name === 'Bash') {
|
|
120
144
|
const cmd = block.input?.command ?? '';
|
|
145
|
+
// A session that edits through Bash — heredocs, sed -i, a
|
|
146
|
+
// short python script — never produces a Write/Edit block, so
|
|
147
|
+
// `filesEdited` stayed empty and the summary asserted "0 files
|
|
148
|
+
// edited" for a session that edited a dozen. The count also
|
|
149
|
+
// drives the re-capture guard below, so the same gap made the
|
|
150
|
+
// -summary entity re-append on every Stop (#240). Recognise the
|
|
151
|
+
// common in-place write shapes; anything not matched is simply
|
|
152
|
+
// uncounted, which is honest — it is not asserted as zero.
|
|
153
|
+
for (const fp of bashEditedPaths(cmd)) filesEdited.add(basename(fp));
|
|
121
154
|
if (typeof cmd === 'string' && cmd.length > 10 && !cmd.startsWith('ls') && !cmd.startsWith('cd')) {
|
|
122
155
|
// Redact BEFORE truncating. A bash command line is the single
|
|
123
156
|
// most likely place a credential appears in a transcript
|
|
@@ -347,7 +380,13 @@ process.stdin.on('end', async () => {
|
|
|
347
380
|
// A dedup bail is a SUCCESSFUL run — the loop executed and correctly
|
|
348
381
|
// decided there was nothing to do — so it stamps the heartbeat like
|
|
349
382
|
// the capture path below does. Only a throw leaves no stamp.
|
|
350
|
-
|
|
383
|
+
// Guard on ANY of this session's three entities, not only `-files`.
|
|
384
|
+
// A Bash-only session created no `-files` row, so the guard never
|
|
385
|
+
// tripped and `-summary` was re-appended on every Stop — measured: 56
|
|
386
|
+
// observations, 16 unique, three commands stored fourteen times each.
|
|
387
|
+
const alreadyCaptured = db.prepare(
|
|
388
|
+
"SELECT id FROM entities WHERE name IN (?, ?, ?) LIMIT 1",
|
|
389
|
+
).get(`session-${sessionId}-files`, `session-${sessionId}-fixes`, `session-${sessionId}-summary`);
|
|
351
390
|
if (alreadyCaptured) {
|
|
352
391
|
recordHookRun(db, 'session-summary');
|
|
353
392
|
return exit0();
|